mirror of
https://github.com/awizemann/scarf.git
synced 2026-05-10 18:44:45 +00:00
ee3791a1b2
Hermes v0.12 added three skills surfaces Scarf can now reach: - Direct-URL install: `hermes skills install <https://...>` lets users pull a one-off skill without going through a registry. Mac SkillsView grew an "Install from URL…" toolbar button (capability-gated on HermesCapabilities.hasSkillURLInstall) opening a sheet with the URL field plus optional --category / --name overrides. - Reload: `hermes skills audit` rescans `~/.hermes/skills/` and refreshes the agent's view of available skills without restarting. Wired to a "Reload" toolbar button next to the install button on Mac. - Enabled state: skills.disabled in config.yaml is now read at scan time (SkillsViewModel.readDisabledSkillNames). Disabled skills render strikethrough + an "OFF" pill on Mac and iOS rows so users see what Hermes won't load. iOS detail view explains the state in plain text. - Curator pin badge: pinned-skill names from `~/.hermes/skills/.curator_state` (SkillsViewModel.readPinnedSkillNames) surface as a pin glyph on each row. Mac sidebar + iOS list both show it; iOS detail view explains "pinned by curator — won't auto-archive." Model + scanner: - HermesSkill gains `enabled: Bool` (default true) and `pinned: Bool` (default false). Both default to backwards-compatible values so unmodified call sites keep compiling. - SkillsScanner.scan now takes optional `disabledNames` and `pinnedNames` sets and applies them per skill at scan time. - SkillsViewModel.load auto-fetches both sets internally so Mac/iOS callers don't have to plumb curator state manually; an opt-in `pinnedNames` override is available for the Curator screen which has a fresher snapshot in hand. Tests: 215 ScarfCore tests pass; both Mac and iOS schemes build clean. Note: the disable-toggle path (writing the array back into config.yaml) is deferred to v2.7 — Hermes ships `hermes skills config` as an interactive verb only, and we'd rather read accurately than risk clobbering the user's list with a half-tested write path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
80 lines
3.6 KiB
Swift
80 lines
3.6 KiB
Swift
import SwiftUI
|
|
import ScarfCore
|
|
import ScarfDesign
|
|
|
|
/// Installed skills sub-tab. Category-grouped list; tapping a row
|
|
/// pushes `SkillDetailView` for that skill. Filtering uses the VM's
|
|
/// `filteredCategories` derivation so the search field works against
|
|
/// the same model the Mac uses.
|
|
struct InstalledSkillsListView: View {
|
|
@Bindable var vm: SkillsViewModel
|
|
|
|
var body: some View {
|
|
Group {
|
|
if vm.isLoading && vm.categories.isEmpty {
|
|
ProgressView("Scanning skills…")
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
} else if vm.categories.isEmpty {
|
|
ContentUnavailableView {
|
|
Label("No skills installed", systemImage: "lightbulb")
|
|
} description: {
|
|
Text("Browse the Hub tab to install one, or run `hermes skills install <name>` on the remote.")
|
|
.font(.caption)
|
|
}
|
|
} else {
|
|
listContent
|
|
}
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private var listContent: some View {
|
|
List {
|
|
ForEach(vm.filteredCategories) { category in
|
|
Section(category.name) {
|
|
ForEach(category.skills) { skill in
|
|
NavigationLink {
|
|
SkillDetailView(skill: skill, vm: vm)
|
|
} label: {
|
|
HStack(spacing: 8) {
|
|
VStack(alignment: .leading, spacing: 2) {
|
|
Text(skill.name)
|
|
.font(.body)
|
|
.foregroundStyle(skill.enabled ? .primary : .secondary)
|
|
.strikethrough(!skill.enabled, color: .secondary)
|
|
Text("\(skill.files.count) file\(skill.files.count == 1 ? "" : "s")")
|
|
.font(.caption)
|
|
.foregroundStyle(ScarfColor.foregroundMuted)
|
|
}
|
|
Spacer(minLength: 0)
|
|
if skill.pinned {
|
|
Image(systemName: "pin.fill")
|
|
.font(.caption2)
|
|
.foregroundStyle(ScarfColor.accent)
|
|
.accessibilityLabel("Pinned by curator")
|
|
}
|
|
if !skill.enabled {
|
|
Text("OFF")
|
|
.font(.caption2.weight(.semibold))
|
|
.padding(.horizontal, 4)
|
|
.padding(.vertical, 1)
|
|
.background(ScarfColor.backgroundTertiary)
|
|
.clipShape(RoundedRectangle(cornerRadius: 3))
|
|
.foregroundStyle(ScarfColor.foregroundMuted)
|
|
.accessibilityLabel("Disabled — Hermes won't load this skill")
|
|
}
|
|
}
|
|
}
|
|
.scarfGoCompactListRow()
|
|
.listRowBackground(ScarfColor.backgroundSecondary)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.scarfGoListDensity()
|
|
.scrollContentBackground(.hidden)
|
|
.background(ScarfColor.backgroundPrimary)
|
|
.searchable(text: $vm.searchText, placement: .navigationBarDrawer(displayMode: .always))
|
|
}
|
|
}
|