@pellux/goodvibes-agent 0.1.116 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +48 -661
- package/README.md +64 -57
- package/bin/goodvibes-agent.ts +16 -2
- package/dist/package/main.js +199180 -192185
- package/docs/README.md +15 -8
- package/docs/channels-remote-and-api.md +50 -0
- package/docs/connected-host.md +59 -0
- package/docs/getting-started.md +61 -50
- package/docs/knowledge-artifacts-and-multimodal.md +91 -0
- package/docs/project-planning.md +79 -0
- package/docs/providers-and-routing.md +46 -0
- package/docs/release-and-publishing.md +48 -11
- package/docs/tools-and-commands.md +123 -0
- package/docs/voice-and-live-tts.md +51 -0
- package/package.json +3 -6
- package/src/agent/behavior-discovery-summary.ts +1 -3
- package/src/agent/channel-delivery.ts +201 -0
- package/src/agent/media-generation.ts +159 -0
- package/src/agent/memory-prompt.ts +0 -1
- package/src/agent/note-registry.ts +329 -0
- package/src/agent/operator-actions.ts +343 -0
- package/src/agent/persona-discovery.ts +1 -3
- package/src/agent/persona-registry.ts +19 -16
- package/src/agent/record-labels.ts +107 -0
- package/src/agent/reminder-schedule-format.ts +33 -24
- package/src/agent/reminder-schedule.ts +27 -26
- package/src/agent/routine-registry.ts +54 -14
- package/src/agent/routine-schedule-args.ts +2 -1
- package/src/agent/routine-schedule-format.ts +77 -53
- package/src/agent/routine-schedule-promotion.ts +35 -33
- package/src/agent/routine-schedule-receipts.ts +29 -27
- package/src/agent/runtime-profile-starters.ts +5 -5
- package/src/agent/runtime-profile.ts +91 -12
- package/src/agent/skill-registry.ts +210 -32
- package/src/cli/agent-knowledge-args.ts +5 -1
- package/src/cli/agent-knowledge-command.ts +40 -34
- package/src/cli/agent-knowledge-format.ts +80 -67
- package/src/cli/agent-knowledge-methods.ts +1 -1
- package/src/cli/agent-knowledge-runtime.ts +32 -26
- package/src/cli/bundle-command.ts +14 -9
- package/src/cli/config-overrides.ts +37 -36
- package/src/cli/entrypoint.ts +16 -7
- package/src/cli/external-runtime.ts +10 -4
- package/src/cli/help.ts +54 -29
- package/src/cli/local-library-command.ts +203 -83
- package/src/cli/management-commands.ts +105 -68
- package/src/cli/management.ts +55 -74
- package/src/cli/memory-command.ts +66 -32
- package/src/cli/parser.ts +37 -3
- package/src/cli/profiles-command.ts +166 -31
- package/src/cli/provider-auth-routes.ts +2 -1
- package/src/cli/routines-command.ts +176 -40
- package/src/cli/service-posture.ts +16 -16
- package/src/cli/status.ts +56 -135
- package/src/core/conversation-message-snapshot.ts +131 -0
- package/src/input/agent-workspace-access-command-editor-submission.ts +149 -0
- package/src/input/agent-workspace-access-command-editors.ts +170 -0
- package/src/input/agent-workspace-activation.ts +35 -8
- package/src/input/agent-workspace-basic-command-editor-submission.ts +351 -94
- package/src/input/agent-workspace-basic-command-editors.ts +502 -9
- package/src/input/agent-workspace-categories.ts +423 -51
- package/src/input/agent-workspace-channel-command-editor-submission.ts +83 -0
- package/src/input/agent-workspace-channel-command-editors.ts +65 -0
- package/src/input/agent-workspace-channels.ts +35 -2
- package/src/input/agent-workspace-command-editor.ts +41 -3
- package/src/input/agent-workspace-config-reader.ts +16 -0
- package/src/input/agent-workspace-delegation-editor-submission.ts +55 -0
- package/src/input/agent-workspace-editors.ts +166 -1
- package/src/input/agent-workspace-knowledge-command-editor-submission.ts +132 -0
- package/src/input/agent-workspace-knowledge-command-editors.ts +110 -0
- package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
- package/src/input/agent-workspace-learned-behavior.ts +67 -0
- package/src/input/agent-workspace-library-command-editor-submission.ts +50 -0
- package/src/input/agent-workspace-library-command-editors.ts +35 -0
- package/src/input/agent-workspace-local-operations.ts +218 -0
- package/src/input/agent-workspace-local-selection.ts +75 -0
- package/src/input/agent-workspace-mcp-command-editor-submission.ts +95 -0
- package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
- package/src/input/agent-workspace-media-command-editors.ts +27 -0
- package/src/input/agent-workspace-memory-command-editor-submission.ts +232 -0
- package/src/input/agent-workspace-memory-command-editors.ts +180 -0
- package/src/input/agent-workspace-memory-editor.ts +2 -2
- package/src/input/agent-workspace-navigation.ts +81 -0
- package/src/input/agent-workspace-notify-editor-submission.ts +121 -0
- package/src/input/agent-workspace-operations-command-editor-submission.ts +208 -0
- package/src/input/agent-workspace-operations-command-editors.ts +196 -0
- package/src/input/agent-workspace-panel-route.ts +43 -0
- package/src/input/agent-workspace-provider-command-editor-submission.ts +155 -0
- package/src/input/agent-workspace-provider-command-editors.ts +93 -0
- package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
- package/src/input/agent-workspace-requirements.ts +11 -0
- package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
- package/src/input/agent-workspace-search.ts +169 -0
- package/src/input/agent-workspace-secret-editor-submission.ts +153 -0
- package/src/input/agent-workspace-session-command-editor-submission.ts +199 -0
- package/src/input/agent-workspace-session-command-editors.ts +249 -0
- package/src/input/agent-workspace-setup.ts +37 -16
- package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +101 -0
- package/src/input/agent-workspace-skill-bundle-command-editors.ts +110 -0
- package/src/input/agent-workspace-snapshot.ts +162 -21
- package/src/input/agent-workspace-task-command-editor-submission.ts +61 -0
- package/src/input/agent-workspace-task-command-editors.ts +47 -0
- package/src/input/agent-workspace-token.ts +18 -2
- package/src/input/agent-workspace-types.ts +212 -4
- package/src/input/agent-workspace-voice-media.ts +4 -7
- package/src/input/agent-workspace-web-research-editor.ts +104 -0
- package/src/input/agent-workspace-workplan-editor-submission.ts +153 -0
- package/src/input/agent-workspace.ts +178 -240
- package/src/input/command-registry.ts +5 -2
- package/src/input/commands/agent-runtime-profile-runtime.ts +126 -37
- package/src/input/commands/agent-skills-runtime.ts +149 -72
- package/src/input/commands/agent-workspace-runtime.ts +23 -6
- package/src/input/commands/brief-runtime.ts +55 -25
- package/src/input/commands/channels-runtime.ts +147 -33
- package/src/input/commands/compat-runtime.ts +32 -0
- package/src/input/commands/delegation-runtime.ts +29 -23
- package/src/input/commands/experience-runtime.ts +6 -4
- package/src/input/commands/guidance-runtime.ts +4 -4
- package/src/input/commands/health-runtime.ts +149 -126
- package/src/input/commands/knowledge.ts +82 -60
- package/src/input/commands/local-provider-runtime.ts +43 -21
- package/src/input/commands/local-runtime.ts +138 -16
- package/src/input/commands/local-setup-review.ts +10 -20
- package/src/input/commands/mcp-runtime.ts +63 -46
- package/src/input/commands/notify-runtime.ts +38 -9
- package/src/input/commands/operator-actions-runtime.ts +138 -0
- package/src/input/commands/operator-runtime.ts +6 -76
- package/src/input/commands/personas-runtime.ts +46 -31
- package/src/input/commands/planning-runtime.ts +1 -1
- package/src/input/commands/platform-access-runtime.ts +35 -98
- package/src/input/commands/product-runtime.ts +2 -2
- package/src/input/commands/provider-accounts-runtime.ts +60 -39
- package/src/input/commands/qrcode-runtime.ts +50 -9
- package/src/input/commands/recall-bundle.ts +11 -11
- package/src/input/commands/recall-capture.ts +13 -11
- package/src/input/commands/recall-query.ts +29 -21
- package/src/input/commands/recall-review.ts +2 -1
- package/src/input/commands/routines-runtime.ts +99 -45
- package/src/input/commands/runtime-services.ts +1 -14
- package/src/input/commands/schedule-runtime.ts +33 -20
- package/src/input/commands/security-runtime.ts +6 -11
- package/src/input/commands/session-content.ts +80 -78
- package/src/input/commands/session-workflow.ts +134 -104
- package/src/input/commands/session.ts +3 -174
- package/src/input/commands/shell-core.ts +38 -17
- package/src/input/commands/subscription-runtime.ts +54 -185
- package/src/input/commands/support-bundle-runtime.ts +53 -0
- package/src/input/commands/tasks-runtime.ts +21 -21
- package/src/input/commands/work-plan-runtime.ts +33 -8
- package/src/input/commands.ts +6 -2
- package/src/input/feed-context-factory.ts +2 -2
- package/src/input/file-picker.ts +3 -2
- package/src/input/handler-command-route.ts +4 -7
- package/src/input/handler-content-actions.ts +89 -26
- package/src/input/handler-feed-routes.ts +19 -12
- package/src/input/handler-feed.ts +6 -5
- package/src/input/handler-interactions.ts +9 -36
- package/src/input/handler-modal-stack.ts +3 -3
- package/src/input/handler-onboarding.ts +52 -91
- package/src/input/handler-shortcuts.ts +16 -7
- package/src/input/handler.ts +50 -23
- package/src/input/input-history.ts +5 -6
- package/src/input/keybindings.ts +27 -16
- package/src/input/mcp-workspace.ts +90 -26
- package/src/input/onboarding/handler-onboarding-routes.ts +3 -1
- package/src/input/onboarding/onboarding-wizard-apply.ts +62 -0
- package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
- package/src/input/onboarding/onboarding-wizard-helpers.ts +1 -1
- package/src/input/onboarding/onboarding-wizard-operator-steps.ts +223 -55
- package/src/input/onboarding/onboarding-wizard-steps.ts +59 -35
- package/src/input/onboarding/onboarding-wizard-types.ts +4 -1
- package/src/input/onboarding/onboarding-wizard.ts +87 -2
- package/src/input/profile-picker-modal.ts +31 -12
- package/src/input/session-picker-modal.ts +21 -4
- package/src/input/settings-modal-agent-policy.ts +6 -6
- package/src/input/settings-modal-behavior.ts +0 -3
- package/src/input/settings-modal-subscriptions.ts +3 -3
- package/src/input/settings-modal-types.ts +2 -13
- package/src/input/settings-modal.ts +20 -66
- package/src/input/submission-intent.ts +0 -1
- package/src/input/submission-router.ts +3 -3
- package/src/main.ts +18 -10
- package/src/panels/approval-panel.ts +8 -8
- package/src/panels/automation-control-panel.ts +4 -4
- package/src/panels/base-panel.ts +1 -1
- package/src/panels/builtin/agent.ts +1 -1
- package/src/panels/builtin/operations.ts +3 -12
- package/src/panels/builtin/session.ts +32 -24
- package/src/panels/builtin/shared.ts +7 -7
- package/src/panels/cost-tracker-panel.ts +1 -1
- package/src/panels/docs-panel.ts +5 -3
- package/src/panels/index.ts +0 -1
- package/src/panels/knowledge-panel.ts +6 -5
- package/src/panels/memory-panel.ts +7 -6
- package/src/panels/panel-list-panel.ts +36 -80
- package/src/panels/project-planning-panel.ts +19 -12
- package/src/panels/provider-account-snapshot.ts +51 -25
- package/src/panels/provider-accounts-panel.ts +33 -18
- package/src/panels/provider-health-domains.ts +48 -7
- package/src/panels/provider-health-panel.ts +5 -4
- package/src/panels/qr-panel.ts +44 -20
- package/src/panels/schedule-panel.ts +9 -17
- package/src/panels/security-panel.ts +8 -8
- package/src/panels/session-browser-panel.ts +10 -10
- package/src/panels/subscription-panel.ts +6 -6
- package/src/panels/system-messages-panel.ts +3 -3
- package/src/panels/tasks-panel.ts +21 -14
- package/src/panels/tool-inspector-panel.ts +19 -12
- package/src/panels/work-plan-panel.ts +5 -5
- package/src/planning/project-planning-coordinator.ts +3 -3
- package/src/provider-auth-route-display.ts +9 -0
- package/src/renderer/agent-workspace-style.ts +34 -0
- package/src/renderer/agent-workspace.ts +254 -57
- package/src/renderer/autocomplete-overlay.ts +25 -6
- package/src/renderer/block-actions.ts +1 -3
- package/src/renderer/bookmark-modal.ts +19 -4
- package/src/renderer/buffer.ts +4 -2
- package/src/renderer/context-inspector.ts +50 -13
- package/src/renderer/diff.ts +1 -1
- package/src/renderer/file-picker-overlay.ts +19 -6
- package/src/renderer/help-overlay.ts +112 -34
- package/src/renderer/history-search-overlay.ts +19 -4
- package/src/renderer/live-tail-modal.ts +27 -5
- package/src/renderer/mcp-workspace.ts +176 -43
- package/src/renderer/model-picker-overlay.ts +58 -3
- package/src/renderer/model-workspace.ts +104 -22
- package/src/renderer/onboarding/onboarding-wizard.ts +20 -4
- package/src/renderer/process-modal.ts +27 -6
- package/src/renderer/profile-picker-modal.ts +21 -5
- package/src/renderer/search-overlay.ts +25 -5
- package/src/renderer/selection-modal-overlay.ts +46 -14
- package/src/renderer/session-picker-modal.ts +18 -1
- package/src/renderer/settings-modal-helpers.ts +2 -40
- package/src/renderer/settings-modal.ts +88 -55
- package/src/renderer/system-message.ts +1 -1
- package/src/renderer/tool-call.ts +20 -11
- package/src/runtime/agent-runtime-events.ts +129 -0
- package/src/runtime/bootstrap-command-context.ts +7 -1
- package/src/runtime/bootstrap-command-parts.ts +11 -8
- package/src/runtime/bootstrap-core.ts +23 -65
- package/src/runtime/bootstrap-hook-bridge.ts +7 -18
- package/src/runtime/bootstrap-shell.ts +46 -8
- package/src/runtime/bootstrap.ts +36 -46
- package/src/runtime/connected-host-auth.ts +47 -0
- package/src/runtime/diagnostics/panels/index.ts +2 -2
- package/src/runtime/diagnostics/panels/panel-resources.ts +1 -1
- package/src/runtime/diagnostics/panels/policy.ts +7 -7
- package/src/runtime/index.ts +2 -5
- package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
- package/src/runtime/onboarding/apply.ts +288 -74
- package/src/runtime/onboarding/derivation.ts +10 -13
- package/src/runtime/onboarding/snapshot.ts +2 -17
- package/src/runtime/onboarding/types.ts +34 -20
- package/src/runtime/onboarding/verify.ts +105 -4
- package/src/runtime/operator-token-cleanup.ts +3 -3
- package/src/runtime/services.ts +95 -11
- package/src/runtime/store/selectors/index.ts +3 -3
- package/src/runtime/store/state.ts +1 -4
- package/src/runtime/surface-feature-flags.ts +41 -6
- package/src/runtime/ui-read-models.ts +3 -4
- package/src/runtime/ui-services.ts +6 -6
- package/src/shell/blocking-input.ts +2 -1
- package/src/shell/service-settings-sync.ts +7 -7
- package/src/shell/ui-openers.ts +17 -38
- package/src/tools/agent-analysis-registry-policy.ts +0 -1
- package/src/tools/agent-channel-send-tool.ts +133 -0
- package/src/tools/agent-context-policy.ts +1 -1
- package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
- package/src/tools/agent-knowledge-tool.ts +170 -0
- package/src/tools/agent-local-registry-tool.ts +279 -73
- package/src/tools/agent-media-generate-tool.ts +133 -0
- package/src/tools/agent-notify-tool.ts +143 -0
- package/src/tools/agent-operator-action-tool.ts +137 -0
- package/src/tools/agent-operator-briefing-tool.ts +217 -0
- package/src/tools/agent-reminder-schedule-tool.ts +237 -0
- package/src/tools/agent-tool-policy-guard.ts +13 -15
- package/src/tools/agent-work-plan-tool.ts +256 -0
- package/src/version.ts +1 -1
- package/docs/connected-services.md +0 -51
- package/src/cli/package-verification.ts +0 -274
- package/src/input/commands/policy-dispatch.ts +0 -339
- package/src/input/commands/policy.ts +0 -17
- package/src/panels/panel-picker.ts +0 -105
- package/src/panels/policy-panel.ts +0 -308
- package/src/renderer/panel-picker-overlay.ts +0 -202
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { createShellPathService } from '@/runtime/index.ts';
|
|
2
2
|
import { discoverPersonas, type DiscoveredPersonaRecord } from '../agent/persona-discovery.ts';
|
|
3
3
|
import { AgentPersonaRegistry, type AgentPersonaRecord } from '../agent/persona-registry.ts';
|
|
4
|
+
import { formatAgentRecordOrigin, formatAgentRecordReviewState } from '../agent/record-labels.ts';
|
|
4
5
|
import { discoverSkills, type SkillRecord } from '../agent/skill-discovery.ts';
|
|
5
6
|
import {
|
|
6
7
|
AgentSkillRegistry,
|
|
8
|
+
buildAgentSkillRequirements,
|
|
9
|
+
evaluateAgentSkillBundleReadiness,
|
|
10
|
+
evaluateAgentSkillReadiness,
|
|
11
|
+
formatAgentSkillRequirement,
|
|
7
12
|
type AgentSkillBundleRecord,
|
|
8
13
|
type AgentSkillRecord,
|
|
9
14
|
} from '../agent/skill-registry.ts';
|
|
@@ -118,8 +123,8 @@ function shellPaths(runtime: CliCommandRuntime): ReturnType<typeof createShellPa
|
|
|
118
123
|
|
|
119
124
|
function summarizePersona(persona: AgentPersonaRecord, activePersonaId: string | null): string {
|
|
120
125
|
const active = persona.id === activePersonaId ? 'active' : 'available';
|
|
121
|
-
const tags = persona.tags.length > 0 ? `
|
|
122
|
-
return ` ${persona.id} ${active} ${persona.reviewState} ${persona.name} - ${persona.description}${tags}`;
|
|
126
|
+
const tags = persona.tags.length > 0 ? ` tags ${persona.tags.join(', ')}` : '';
|
|
127
|
+
return ` ${persona.id} ${active} ${formatAgentRecordReviewState(persona.reviewState)} ${persona.name} - ${persona.description}${tags}`;
|
|
123
128
|
}
|
|
124
129
|
|
|
125
130
|
function renderPersonaList(title: string, path: string, personas: readonly AgentPersonaRecord[], activePersonaId: string | null): string {
|
|
@@ -127,12 +132,13 @@ function renderPersonaList(title: string, path: string, personas: readonly Agent
|
|
|
127
132
|
return [
|
|
128
133
|
title,
|
|
129
134
|
' No local Agent personas yet.',
|
|
135
|
+
' No Agent-local personas yet.',
|
|
130
136
|
' Create one with: goodvibes-agent personas create --name <name> --description <summary> --body <instructions>',
|
|
131
137
|
].join('\n');
|
|
132
138
|
}
|
|
133
139
|
return [
|
|
134
140
|
`${title} (${personas.length})`,
|
|
135
|
-
` store
|
|
141
|
+
` store ${path}`,
|
|
136
142
|
...personas.map((persona) => summarizePersona(persona, activePersonaId)),
|
|
137
143
|
].join('\n');
|
|
138
144
|
}
|
|
@@ -140,11 +146,10 @@ function renderPersonaList(title: string, path: string, personas: readonly Agent
|
|
|
140
146
|
function renderPersona(persona: AgentPersonaRecord, activePersonaId: string | null): string {
|
|
141
147
|
return [
|
|
142
148
|
`Persona ${persona.name}`,
|
|
143
|
-
` id
|
|
149
|
+
` id ${persona.id}`,
|
|
144
150
|
` active: ${persona.id === activePersonaId ? 'yes' : 'no'}`,
|
|
145
|
-
` review: ${persona.reviewState}`,
|
|
146
|
-
`
|
|
147
|
-
` provenance: ${persona.provenance}`,
|
|
151
|
+
` review: ${formatAgentRecordReviewState(persona.reviewState)}`,
|
|
152
|
+
` origin: ${formatAgentRecordOrigin(persona.source, persona.provenance)}`,
|
|
148
153
|
` tags: ${persona.tags.join(', ') || '(none)'}`,
|
|
149
154
|
` triggers: ${persona.triggers.join(', ') || '(manual)'}`,
|
|
150
155
|
` created: ${persona.createdAt}`,
|
|
@@ -161,7 +166,7 @@ function summarizeDiscoveredPersona(persona: DiscoveredPersonaRecord): string {
|
|
|
161
166
|
const description = persona.description ? ` - ${persona.description}` : '';
|
|
162
167
|
return [
|
|
163
168
|
` ${persona.name} ${persona.origin}${description}`,
|
|
164
|
-
` path
|
|
169
|
+
` path ${persona.path}`,
|
|
165
170
|
].join('\n');
|
|
166
171
|
}
|
|
167
172
|
|
|
@@ -170,7 +175,7 @@ function renderDiscoveredPersonaList(personas: readonly DiscoveredPersonaRecord[
|
|
|
170
175
|
return [
|
|
171
176
|
'Discovered Agent persona files',
|
|
172
177
|
' No persona markdown files found in Agent persona folders.',
|
|
173
|
-
' Search roots: .goodvibes/personas, .goodvibes/agent/personas,
|
|
178
|
+
' Search roots: .goodvibes/personas, .goodvibes/agent/personas, ~/.goodvibes/personas, ~/.goodvibes/agent/personas',
|
|
174
179
|
].join('\n');
|
|
175
180
|
}
|
|
176
181
|
return [
|
|
@@ -203,37 +208,46 @@ function discoveredPersonaFrontmatterList(persona: DiscoveredPersonaRecord, key:
|
|
|
203
208
|
|
|
204
209
|
function summarizeSkill(skill: AgentSkillRecord): string {
|
|
205
210
|
const enabled = skill.enabled ? 'enabled' : 'disabled';
|
|
206
|
-
const tags = skill.tags.length > 0 ? `
|
|
207
|
-
|
|
211
|
+
const tags = skill.tags.length > 0 ? ` tags ${skill.tags.join(', ')}` : '';
|
|
212
|
+
const readiness = evaluateAgentSkillReadiness(skill);
|
|
213
|
+
const ready = readiness.ready ? 'ready' : `needs ${readiness.missing.map(formatAgentSkillRequirement).join(',')}`;
|
|
214
|
+
return ` ${skill.id} ${enabled} ${formatAgentRecordReviewState(skill.reviewState)} ${ready} ${skill.name} - ${skill.description}${tags}`;
|
|
208
215
|
}
|
|
209
216
|
|
|
210
|
-
function summarizeBundle(bundle: AgentSkillBundleRecord): string {
|
|
217
|
+
function summarizeBundle(bundle: AgentSkillBundleRecord, skills: readonly AgentSkillRecord[]): string {
|
|
211
218
|
const enabled = bundle.enabled ? 'enabled' : 'disabled';
|
|
212
|
-
|
|
219
|
+
const readiness = evaluateAgentSkillBundleReadiness(bundle, skills);
|
|
220
|
+
const missing = [
|
|
221
|
+
...readiness.missingRequirements.map(formatAgentSkillRequirement),
|
|
222
|
+
...readiness.missingSkillIds.map((skillId) => `skill:${skillId}`),
|
|
223
|
+
];
|
|
224
|
+
const ready = readiness.ready ? 'ready' : `needs ${missing.join(',')}`;
|
|
225
|
+
return ` ${bundle.id} ${enabled} ${formatAgentRecordReviewState(bundle.reviewState)} ${ready} ${bundle.name} - ${bundle.description} skills ${bundle.skillIds.join(',')}`;
|
|
213
226
|
}
|
|
214
227
|
|
|
215
|
-
function renderSkillList(title: string, path: string, skills: readonly AgentSkillRecord[]): string {
|
|
228
|
+
function renderSkillList(title: string, path: string, skills: readonly AgentSkillRecord[], emptyMessage?: string): string {
|
|
216
229
|
if (skills.length === 0) {
|
|
217
230
|
return [
|
|
218
231
|
title,
|
|
219
|
-
' No local Agent skills yet.',
|
|
220
|
-
|
|
221
|
-
|
|
232
|
+
emptyMessage ? '' : ' No local Agent skills yet.',
|
|
233
|
+
` ${emptyMessage ?? 'No Agent-local skills yet.'}`,
|
|
234
|
+
emptyMessage ? '' : ' Create one with: goodvibes-agent skills create --name <name> --description <summary> --procedure <steps>',
|
|
235
|
+
].filter(Boolean).join('\n');
|
|
222
236
|
}
|
|
223
237
|
return [
|
|
224
238
|
`${title} (${skills.length})`,
|
|
225
|
-
` store
|
|
239
|
+
` store ${path}`,
|
|
226
240
|
...skills.map(summarizeSkill),
|
|
227
241
|
].join('\n');
|
|
228
242
|
}
|
|
229
243
|
|
|
230
244
|
function summarizeDiscoveredSkill(skill: SkillRecord): string {
|
|
231
245
|
const description = skill.description ? ` - ${skill.description}` : '';
|
|
232
|
-
const dependencies = skill.dependencies.length > 0 ? `
|
|
233
|
-
const includes = skill.includes.length > 0 ? `
|
|
246
|
+
const dependencies = skill.dependencies.length > 0 ? ` dependencies ${skill.dependencies.join(', ')}` : '';
|
|
247
|
+
const includes = skill.includes.length > 0 ? ` includes ${skill.includes.join(', ')}` : '';
|
|
234
248
|
return [
|
|
235
249
|
` ${skill.name} ${skill.origin}${description}${dependencies}${includes}`,
|
|
236
|
-
` path
|
|
250
|
+
` path ${skill.path}`,
|
|
237
251
|
].join('\n');
|
|
238
252
|
}
|
|
239
253
|
|
|
@@ -273,29 +287,40 @@ function discoveredFrontmatterList(skill: SkillRecord, key: string): readonly st
|
|
|
273
287
|
return value.split(',').map((entry) => entry.trim()).filter(Boolean);
|
|
274
288
|
}
|
|
275
289
|
|
|
276
|
-
function
|
|
290
|
+
function discoveredFrontmatterAnyList(skill: SkillRecord, keys: readonly string[]): readonly string[] {
|
|
291
|
+
for (const key of keys) {
|
|
292
|
+
const values = discoveredFrontmatterList(skill, key);
|
|
293
|
+
if (values.length > 0) return values;
|
|
294
|
+
}
|
|
295
|
+
return [];
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function renderBundleList(title: string, path: string, bundles: readonly AgentSkillBundleRecord[], skills: readonly AgentSkillRecord[], emptyMessage?: string): string {
|
|
277
299
|
if (bundles.length === 0) {
|
|
278
300
|
return [
|
|
279
301
|
title,
|
|
280
|
-
'
|
|
281
|
-
' Create one with: goodvibes-agent skills bundle create --name <name> --description <summary> --skills <id,id>',
|
|
282
|
-
].join('\n');
|
|
302
|
+
` ${emptyMessage ?? 'No Agent-local skill bundles yet.'}`,
|
|
303
|
+
emptyMessage ? '' : ' Create one with: goodvibes-agent skills bundle create --name <name> --description <summary> --skills <id,id>',
|
|
304
|
+
].filter(Boolean).join('\n');
|
|
283
305
|
}
|
|
284
306
|
return [
|
|
285
307
|
`${title} (${bundles.length})`,
|
|
286
|
-
` store
|
|
287
|
-
...bundles.map(summarizeBundle),
|
|
308
|
+
` store ${path}`,
|
|
309
|
+
...bundles.map((bundle) => summarizeBundle(bundle, skills)),
|
|
288
310
|
].join('\n');
|
|
289
311
|
}
|
|
290
312
|
|
|
291
313
|
function renderSkill(skill: AgentSkillRecord): string {
|
|
314
|
+
const readiness = evaluateAgentSkillReadiness(skill);
|
|
292
315
|
return [
|
|
293
316
|
`Skill ${skill.name}`,
|
|
294
|
-
` id
|
|
317
|
+
` id ${skill.id}`,
|
|
295
318
|
` enabled: ${skill.enabled ? 'yes' : 'no'}`,
|
|
296
|
-
`
|
|
297
|
-
`
|
|
298
|
-
`
|
|
319
|
+
` readiness: ${readiness.ready ? 'ready' : 'needs setup'}`,
|
|
320
|
+
` requirements: ${skill.requirements.map(formatAgentSkillRequirement).join(', ') || '(none)'}`,
|
|
321
|
+
readiness.missing.length > 0 ? ` missing: ${readiness.missing.map(formatAgentSkillRequirement).join(', ')}` : '',
|
|
322
|
+
` review: ${formatAgentRecordReviewState(skill.reviewState)}`,
|
|
323
|
+
` origin: ${formatAgentRecordOrigin(skill.source, skill.provenance)}`,
|
|
299
324
|
` tags: ${skill.tags.join(', ') || '(none)'}`,
|
|
300
325
|
` triggers: ${skill.triggers.join(', ') || '(manual)'}`,
|
|
301
326
|
` created: ${skill.createdAt}`,
|
|
@@ -308,14 +333,20 @@ function renderSkill(skill: AgentSkillRecord): string {
|
|
|
308
333
|
].filter((line): line is string => Boolean(line)).join('\n');
|
|
309
334
|
}
|
|
310
335
|
|
|
311
|
-
function renderBundle(bundle: AgentSkillBundleRecord): string {
|
|
336
|
+
function renderBundle(bundle: AgentSkillBundleRecord, skills: readonly AgentSkillRecord[]): string {
|
|
337
|
+
const readiness = evaluateAgentSkillBundleReadiness(bundle, skills);
|
|
338
|
+
const missing = [
|
|
339
|
+
...readiness.missingRequirements.map(formatAgentSkillRequirement),
|
|
340
|
+
...readiness.missingSkillIds.map((skillId) => `skill:${skillId}`),
|
|
341
|
+
];
|
|
312
342
|
return [
|
|
313
343
|
`Skill bundle ${bundle.name}`,
|
|
314
|
-
` id
|
|
344
|
+
` id ${bundle.id}`,
|
|
315
345
|
` enabled: ${bundle.enabled ? 'yes' : 'no'}`,
|
|
316
|
-
`
|
|
317
|
-
`
|
|
318
|
-
`
|
|
346
|
+
` readiness: ${readiness.ready ? 'ready' : 'needs setup'}`,
|
|
347
|
+
missing.length > 0 ? ` missing: ${missing.join(', ')}` : '',
|
|
348
|
+
` review: ${formatAgentRecordReviewState(bundle.reviewState)}`,
|
|
349
|
+
` origin: ${formatAgentRecordOrigin(bundle.source, bundle.provenance)}`,
|
|
319
350
|
` skills: ${bundle.skillIds.join(', ')}`,
|
|
320
351
|
` created: ${bundle.createdAt}`,
|
|
321
352
|
` updated: ${bundle.updatedAt}`,
|
|
@@ -330,11 +361,11 @@ function usagePersonas(): string {
|
|
|
330
361
|
}
|
|
331
362
|
|
|
332
363
|
function usageSkills(): string {
|
|
333
|
-
return 'Usage: goodvibes-agent skills [list|enabled|active|discover|import-discovered <name> --yes|search <query>|show <id>|create|update <id>|enable <id>|disable <id>|review <id>|stale <id> <reason>|delete <id> --yes|bundle ...]';
|
|
364
|
+
return 'Usage: goodvibes-agent skills [list|enabled|active|attention|discover|import-discovered <name> --yes|search <query>|show <id>|create [--requires-env A,B] [--requires-command gh,jq]|update <id>|enable <id>|disable <id>|review <id>|stale <id> <reason>|delete <id> --yes|bundle ...]';
|
|
334
365
|
}
|
|
335
366
|
|
|
336
367
|
function usageBundles(): string {
|
|
337
|
-
return 'Usage: goodvibes-agent skills bundle [list|enabled|search <query>|show <id>|create|update <id>|enable <id>|disable <id>|review <id>|stale <id> <reason>|delete <id> --yes]';
|
|
368
|
+
return 'Usage: goodvibes-agent skills bundle [list|enabled|attention|search <query>|show <id>|create|update <id>|enable <id>|disable <id>|review <id>|stale <id> <reason>|delete <id> --yes]';
|
|
338
369
|
}
|
|
339
370
|
|
|
340
371
|
function errorOutput(runtime: CliCommandRuntime, error: unknown, kind: string): CliCommandOutput {
|
|
@@ -367,17 +398,17 @@ export async function handlePersonasCommand(runtime: CliCommandRuntime): Promise
|
|
|
367
398
|
if (!name) return failure(runtime, 'invalid_persona_command', 'Usage: goodvibes-agent personas import-discovered <name> [--use] --yes', 2);
|
|
368
399
|
const discovered = findDiscoveredPersona(await discoverPersonas(shellPaths(runtime)), name);
|
|
369
400
|
if (!discovered) {
|
|
370
|
-
return failure(runtime, 'persona_discovery_not_found', `Unknown discovered Agent persona
|
|
401
|
+
return failure(runtime, 'persona_discovery_not_found', `Unknown discovered Agent persona ${name}\nRun goodvibes-agent personas discover to inspect available persona files.`, 1);
|
|
371
402
|
}
|
|
372
403
|
if (!hasFlag(options, 'yes')) {
|
|
373
404
|
return success(runtime, 'agent.personas.import_discovered.preview', { persona: discovered }, [
|
|
374
405
|
'Agent persona import preview',
|
|
375
|
-
` name
|
|
376
|
-
` origin
|
|
377
|
-
` path
|
|
378
|
-
` description
|
|
379
|
-
` body characters
|
|
380
|
-
' next
|
|
406
|
+
` name ${discovered.name}`,
|
|
407
|
+
` origin ${discovered.origin}`,
|
|
408
|
+
` path ${discovered.path}`,
|
|
409
|
+
` description ${discovered.description || '(none)'}`,
|
|
410
|
+
` body characters ${discovered.body.length}`,
|
|
411
|
+
' next rerun with --yes to import into the Agent-local persona registry',
|
|
381
412
|
].join('\n'));
|
|
382
413
|
}
|
|
383
414
|
const persona = registry.create({
|
|
@@ -390,7 +421,11 @@ export async function handlePersonasCommand(runtime: CliCommandRuntime): Promise
|
|
|
390
421
|
provenance: `discovered:${discovered.origin}:${discovered.path}`,
|
|
391
422
|
});
|
|
392
423
|
if (hasFlag(options, 'use')) registry.setActive(persona.id);
|
|
393
|
-
return success(runtime, 'agent.personas.import_discovered', persona,
|
|
424
|
+
return success(runtime, 'agent.personas.import_discovered', persona, [
|
|
425
|
+
`Imported Agent persona ${persona.id}: ${persona.name}${hasFlag(options, 'use') ? ' (active)' : ''}`,
|
|
426
|
+
` name ${persona.name}`,
|
|
427
|
+
` active ${hasFlag(options, 'use') ? 'yes' : 'no'}`,
|
|
428
|
+
].join('\n'));
|
|
394
429
|
}
|
|
395
430
|
if (normalized === 'search' || normalized === 'find') {
|
|
396
431
|
const query = rest.join(' ').trim();
|
|
@@ -401,7 +436,7 @@ export async function handlePersonasCommand(runtime: CliCommandRuntime): Promise
|
|
|
401
436
|
const id = rest[0];
|
|
402
437
|
if (!id) return failure(runtime, 'invalid_persona_command', 'Usage: goodvibes-agent personas show <id>', 2);
|
|
403
438
|
const persona = registry.get(id);
|
|
404
|
-
if (!persona) return failure(runtime, 'persona_not_found', `Unknown Agent persona
|
|
439
|
+
if (!persona) return failure(runtime, 'persona_not_found', `Unknown Agent persona ${id}`, 1);
|
|
405
440
|
return success(runtime, 'agent.personas.show', persona, renderPersona(persona, snapshot.activePersonaId));
|
|
406
441
|
}
|
|
407
442
|
if (normalized === 'create') {
|
|
@@ -412,10 +447,15 @@ export async function handlePersonasCommand(runtime: CliCommandRuntime): Promise
|
|
|
412
447
|
body: requiredOption(options, 'body', 'Usage: goodvibes-agent personas create --name <name> --description <summary> --body <instructions>'),
|
|
413
448
|
tags: csvOption(options, 'tags'),
|
|
414
449
|
triggers: csvOption(options, 'triggers'),
|
|
415
|
-
provenance: optionValue(options, 'provenance') ?? '
|
|
450
|
+
provenance: optionValue(options, 'provenance') ?? 'Command',
|
|
416
451
|
});
|
|
417
452
|
if (hasFlag(options, 'use')) registry.setActive(persona.id);
|
|
418
|
-
return success(runtime, 'agent.personas.create', persona,
|
|
453
|
+
return success(runtime, 'agent.personas.create', persona, [
|
|
454
|
+
'Agent persona created',
|
|
455
|
+
` id ${persona.id}`,
|
|
456
|
+
hasFlag(options, 'use') ? ' (active)' : '',
|
|
457
|
+
` active ${hasFlag(options, 'use') ? 'yes' : 'no'}`,
|
|
458
|
+
].filter(Boolean).join('\n'));
|
|
419
459
|
}
|
|
420
460
|
if (normalized === 'update') {
|
|
421
461
|
const id = rest[0];
|
|
@@ -429,13 +469,19 @@ export async function handlePersonasCommand(runtime: CliCommandRuntime): Promise
|
|
|
429
469
|
triggers: csvOption(options, 'triggers'),
|
|
430
470
|
provenance: optionValue(options, 'provenance'),
|
|
431
471
|
});
|
|
432
|
-
return success(runtime, 'agent.personas.update', persona,
|
|
472
|
+
return success(runtime, 'agent.personas.update', persona, [
|
|
473
|
+
'Agent persona updated',
|
|
474
|
+
` id ${persona.id}`,
|
|
475
|
+
].join('\n'));
|
|
433
476
|
}
|
|
434
477
|
if (normalized === 'use') {
|
|
435
478
|
const id = rest[0];
|
|
436
479
|
if (!id) return failure(runtime, 'invalid_persona_command', 'Usage: goodvibes-agent personas use <id>', 2);
|
|
437
480
|
const persona = registry.setActive(id);
|
|
438
|
-
return success(runtime, 'agent.personas.use', persona,
|
|
481
|
+
return success(runtime, 'agent.personas.use', persona, [
|
|
482
|
+
'Active Agent persona',
|
|
483
|
+
` id ${persona.id}`,
|
|
484
|
+
].join('\n'));
|
|
439
485
|
}
|
|
440
486
|
if (normalized === 'clear') {
|
|
441
487
|
registry.clearActive();
|
|
@@ -445,13 +491,19 @@ export async function handlePersonasCommand(runtime: CliCommandRuntime): Promise
|
|
|
445
491
|
const id = rest[0];
|
|
446
492
|
if (!id) return failure(runtime, 'invalid_persona_command', 'Usage: goodvibes-agent personas review <id>', 2);
|
|
447
493
|
const persona = registry.markReviewed(id);
|
|
448
|
-
return success(runtime, 'agent.personas.review', persona,
|
|
494
|
+
return success(runtime, 'agent.personas.review', persona, [
|
|
495
|
+
'Agent persona reviewed',
|
|
496
|
+
` id ${persona.id}`,
|
|
497
|
+
].join('\n'));
|
|
449
498
|
}
|
|
450
499
|
if (normalized === 'stale') {
|
|
451
500
|
const id = rest[0];
|
|
452
501
|
if (!id || rest.length < 2) return failure(runtime, 'invalid_persona_command', 'Usage: goodvibes-agent personas stale <id> <reason>', 2);
|
|
453
502
|
const persona = registry.markStale(id, rest.slice(1).join(' '));
|
|
454
|
-
return success(runtime, 'agent.personas.stale', persona,
|
|
503
|
+
return success(runtime, 'agent.personas.stale', persona, [
|
|
504
|
+
'Agent persona marked stale',
|
|
505
|
+
` id ${persona.id}`,
|
|
506
|
+
].join('\n'));
|
|
455
507
|
}
|
|
456
508
|
if (normalized === 'delete' || normalized === 'remove' || normalized === 'rm') {
|
|
457
509
|
const options = parseOptions(rest);
|
|
@@ -459,7 +511,10 @@ export async function handlePersonasCommand(runtime: CliCommandRuntime): Promise
|
|
|
459
511
|
if (!id) return failure(runtime, 'invalid_persona_command', 'Usage: goodvibes-agent personas delete <id> --yes', 2);
|
|
460
512
|
if (!hasFlag(options, 'yes')) return failure(runtime, 'confirmation_required', `Refusing to delete Agent persona ${id} without --yes.`, 2);
|
|
461
513
|
const persona = registry.deletePersona(id);
|
|
462
|
-
return success(runtime, 'agent.personas.delete', persona,
|
|
514
|
+
return success(runtime, 'agent.personas.delete', persona, [
|
|
515
|
+
`Agent persona deleted: ${id}`,
|
|
516
|
+
` id ${persona.id}`,
|
|
517
|
+
].join('\n'));
|
|
463
518
|
}
|
|
464
519
|
return failure(runtime, 'invalid_persona_command', usagePersonas(), 2);
|
|
465
520
|
} catch (error) {
|
|
@@ -473,6 +528,7 @@ function skillPayloadFromOptions(options: ParsedOptions): {
|
|
|
473
528
|
readonly procedure: string;
|
|
474
529
|
readonly tags: readonly string[] | undefined;
|
|
475
530
|
readonly triggers: readonly string[] | undefined;
|
|
531
|
+
readonly requirements: ReturnType<typeof buildAgentSkillRequirements>;
|
|
476
532
|
readonly enabled: boolean | undefined;
|
|
477
533
|
readonly provenance: string;
|
|
478
534
|
} {
|
|
@@ -483,8 +539,12 @@ function skillPayloadFromOptions(options: ParsedOptions): {
|
|
|
483
539
|
procedure: requiredOption(options, 'procedure', usage),
|
|
484
540
|
tags: csvOption(options, 'tags'),
|
|
485
541
|
triggers: csvOption(options, 'triggers'),
|
|
542
|
+
requirements: buildAgentSkillRequirements({
|
|
543
|
+
env: csvOption(options, 'requires-env'),
|
|
544
|
+
commands: csvOption(options, 'requires-command') ?? csvOption(options, 'requires-commands'),
|
|
545
|
+
}),
|
|
486
546
|
enabled: hasFlag(options, 'enabled') ? true : undefined,
|
|
487
|
-
provenance: optionValue(options, 'provenance') ?? '
|
|
547
|
+
provenance: optionValue(options, 'provenance') ?? 'Command',
|
|
488
548
|
};
|
|
489
549
|
}
|
|
490
550
|
|
|
@@ -495,22 +555,26 @@ async function handleSkillBundleCommand(runtime: CliCommandRuntime, args: readon
|
|
|
495
555
|
const registry = skillRegistry(runtime);
|
|
496
556
|
const snapshot = registry.snapshot();
|
|
497
557
|
if (normalized === 'list' || normalized === 'ls') {
|
|
498
|
-
return success(runtime, 'agent.skills.bundles.list', { path: snapshot.path, bundles: snapshot.bundles }, renderBundleList('Agent skill bundles', snapshot.path, snapshot.bundles));
|
|
558
|
+
return success(runtime, 'agent.skills.bundles.list', { path: snapshot.path, bundles: snapshot.bundles }, renderBundleList('Agent skill bundles', snapshot.path, snapshot.bundles, snapshot.skills));
|
|
499
559
|
}
|
|
500
560
|
if (normalized === 'enabled') {
|
|
501
|
-
return success(runtime, 'agent.skills.bundles.enabled', { path: snapshot.path, bundles: snapshot.enabledBundles }, renderBundleList('Enabled Agent skill bundles', snapshot.path, snapshot.enabledBundles));
|
|
561
|
+
return success(runtime, 'agent.skills.bundles.enabled', { path: snapshot.path, bundles: snapshot.enabledBundles }, renderBundleList('Enabled Agent skill bundles', snapshot.path, snapshot.enabledBundles, snapshot.skills));
|
|
562
|
+
}
|
|
563
|
+
if (normalized === 'attention' || normalized === 'needs-setup') {
|
|
564
|
+
const bundles = snapshot.bundles.filter((bundle) => !evaluateAgentSkillBundleReadiness(bundle, snapshot.skills).ready);
|
|
565
|
+
return success(runtime, 'agent.skills.bundles.attention', { path: snapshot.path, bundles }, renderBundleList('Agent skill bundles needing setup', snapshot.path, bundles, snapshot.skills, 'No Agent-local skill bundles need setup.'));
|
|
502
566
|
}
|
|
503
567
|
if (normalized === 'search' || normalized === 'find') {
|
|
504
568
|
const query = rest.join(' ').trim();
|
|
505
569
|
const results = registry.searchBundles(query);
|
|
506
|
-
return success(runtime, 'agent.skills.bundles.search', { query, results }, renderBundleList(`Agent skill bundles matching "${query}"`, snapshot.path, results));
|
|
570
|
+
return success(runtime, 'agent.skills.bundles.search', { query, results }, renderBundleList(`Agent skill bundles matching "${query}"`, snapshot.path, results, snapshot.skills));
|
|
507
571
|
}
|
|
508
572
|
if (normalized === 'show' || normalized === 'get') {
|
|
509
573
|
const id = rest[0];
|
|
510
574
|
if (!id) return failure(runtime, 'invalid_skill_bundle_command', 'Usage: goodvibes-agent skills bundle show <id>', 2);
|
|
511
575
|
const bundle = registry.getBundle(id);
|
|
512
|
-
if (!bundle) return failure(runtime, 'skill_bundle_not_found', `Unknown Agent skill bundle
|
|
513
|
-
return success(runtime, 'agent.skills.bundles.show', bundle, renderBundle(bundle));
|
|
576
|
+
if (!bundle) return failure(runtime, 'skill_bundle_not_found', `Unknown Agent skill bundle ${id}`, 1);
|
|
577
|
+
return success(runtime, 'agent.skills.bundles.show', bundle, renderBundle(bundle, snapshot.skills));
|
|
514
578
|
}
|
|
515
579
|
if (normalized === 'create') {
|
|
516
580
|
const options = parseOptions(rest);
|
|
@@ -520,9 +584,12 @@ async function handleSkillBundleCommand(runtime: CliCommandRuntime, args: readon
|
|
|
520
584
|
description: requiredOption(options, 'description', usage),
|
|
521
585
|
skillIds: requiredOption(options, 'skills', usage).split(',').map((entry) => entry.trim()).filter(Boolean),
|
|
522
586
|
enabled: hasFlag(options, 'enabled'),
|
|
523
|
-
provenance: optionValue(options, 'provenance') ?? '
|
|
587
|
+
provenance: optionValue(options, 'provenance') ?? 'Command',
|
|
524
588
|
});
|
|
525
|
-
return success(runtime, 'agent.skills.bundles.create', bundle,
|
|
589
|
+
return success(runtime, 'agent.skills.bundles.create', bundle, [
|
|
590
|
+
'Agent skill bundle created',
|
|
591
|
+
` id ${bundle.id}`,
|
|
592
|
+
].join('\n'));
|
|
526
593
|
}
|
|
527
594
|
if (normalized === 'update') {
|
|
528
595
|
const id = rest[0];
|
|
@@ -534,25 +601,37 @@ async function handleSkillBundleCommand(runtime: CliCommandRuntime, args: readon
|
|
|
534
601
|
skillIds: csvOption(options, 'skills'),
|
|
535
602
|
provenance: optionValue(options, 'provenance'),
|
|
536
603
|
});
|
|
537
|
-
return success(runtime, 'agent.skills.bundles.update', bundle,
|
|
604
|
+
return success(runtime, 'agent.skills.bundles.update', bundle, [
|
|
605
|
+
'Agent skill bundle updated',
|
|
606
|
+
` id ${bundle.id}`,
|
|
607
|
+
].join('\n'));
|
|
538
608
|
}
|
|
539
609
|
if (normalized === 'enable' || normalized === 'disable') {
|
|
540
610
|
const id = rest[0];
|
|
541
611
|
if (!id) return failure(runtime, 'invalid_skill_bundle_command', `Usage: goodvibes-agent skills bundle ${normalized} <id>`, 2);
|
|
542
612
|
const bundle = registry.setBundleEnabled(id, normalized === 'enable');
|
|
543
|
-
return success(runtime, `agent.skills.bundles.${normalized}`, bundle,
|
|
613
|
+
return success(runtime, `agent.skills.bundles.${normalized}`, bundle, [
|
|
614
|
+
`Agent skill bundle ${normalized === 'enable' ? 'enabled' : 'disabled'}`,
|
|
615
|
+
` id ${bundle.id}`,
|
|
616
|
+
].join('\n'));
|
|
544
617
|
}
|
|
545
618
|
if (normalized === 'review') {
|
|
546
619
|
const id = rest[0];
|
|
547
620
|
if (!id) return failure(runtime, 'invalid_skill_bundle_command', 'Usage: goodvibes-agent skills bundle review <id>', 2);
|
|
548
621
|
const bundle = registry.markBundleReviewed(id);
|
|
549
|
-
return success(runtime, 'agent.skills.bundles.review', bundle,
|
|
622
|
+
return success(runtime, 'agent.skills.bundles.review', bundle, [
|
|
623
|
+
'Agent skill bundle reviewed',
|
|
624
|
+
` id ${bundle.id}`,
|
|
625
|
+
].join('\n'));
|
|
550
626
|
}
|
|
551
627
|
if (normalized === 'stale') {
|
|
552
628
|
const id = rest[0];
|
|
553
629
|
if (!id || rest.length < 2) return failure(runtime, 'invalid_skill_bundle_command', 'Usage: goodvibes-agent skills bundle stale <id> <reason>', 2);
|
|
554
630
|
const bundle = registry.markBundleStale(id, rest.slice(1).join(' '));
|
|
555
|
-
return success(runtime, 'agent.skills.bundles.stale', bundle,
|
|
631
|
+
return success(runtime, 'agent.skills.bundles.stale', bundle, [
|
|
632
|
+
'Agent skill bundle marked stale',
|
|
633
|
+
` id ${bundle.id}`,
|
|
634
|
+
].join('\n'));
|
|
556
635
|
}
|
|
557
636
|
if (normalized === 'delete' || normalized === 'remove' || normalized === 'rm') {
|
|
558
637
|
const options = parseOptions(rest);
|
|
@@ -560,7 +639,10 @@ async function handleSkillBundleCommand(runtime: CliCommandRuntime, args: readon
|
|
|
560
639
|
if (!id) return failure(runtime, 'invalid_skill_bundle_command', 'Usage: goodvibes-agent skills bundle delete <id> --yes', 2);
|
|
561
640
|
if (!hasFlag(options, 'yes')) return failure(runtime, 'confirmation_required', `Refusing to delete Agent skill bundle ${id} without --yes.`, 2);
|
|
562
641
|
const bundle = registry.deleteBundle(id);
|
|
563
|
-
return success(runtime, 'agent.skills.bundles.delete', bundle,
|
|
642
|
+
return success(runtime, 'agent.skills.bundles.delete', bundle, [
|
|
643
|
+
'Agent skill bundle deleted',
|
|
644
|
+
` id ${bundle.id}`,
|
|
645
|
+
].join('\n'));
|
|
564
646
|
}
|
|
565
647
|
return failure(runtime, 'invalid_skill_bundle_command', usageBundles(), 2);
|
|
566
648
|
} catch (error) {
|
|
@@ -584,9 +666,13 @@ export async function handleSkillsCommand(runtime: CliCommandRuntime): Promise<C
|
|
|
584
666
|
if (normalized === 'active') {
|
|
585
667
|
return success(runtime, 'agent.skills.active', { path: snapshot.path, skills: snapshot.activeSkills, bundles: snapshot.enabledBundles }, [
|
|
586
668
|
renderSkillList('Active Agent skills', snapshot.path, snapshot.activeSkills),
|
|
587
|
-
snapshot.enabledBundles.length > 0 ? renderBundleList('Enabled Agent skill bundles', snapshot.path, snapshot.enabledBundles) : '',
|
|
669
|
+
snapshot.enabledBundles.length > 0 ? renderBundleList('Enabled Agent skill bundles', snapshot.path, snapshot.enabledBundles, snapshot.skills) : '',
|
|
588
670
|
].filter(Boolean).join('\n\n'));
|
|
589
671
|
}
|
|
672
|
+
if (normalized === 'attention' || normalized === 'needs-setup') {
|
|
673
|
+
const skills = snapshot.skills.filter((skill) => !evaluateAgentSkillReadiness(skill).ready);
|
|
674
|
+
return success(runtime, 'agent.skills.attention', { path: snapshot.path, skills }, renderSkillList('Agent skills needing setup', snapshot.path, skills, 'No Agent-local skills need setup.'));
|
|
675
|
+
}
|
|
590
676
|
if (normalized === 'discover') {
|
|
591
677
|
const discovered = await discoverSkills(shellPaths(runtime));
|
|
592
678
|
return success(runtime, 'agent.skills.discover', { skills: discovered }, renderDiscoveredSkillList(discovered));
|
|
@@ -597,17 +683,17 @@ export async function handleSkillsCommand(runtime: CliCommandRuntime): Promise<C
|
|
|
597
683
|
if (!name) return failure(runtime, 'invalid_skill_command', 'Usage: goodvibes-agent skills import-discovered <name> [--enabled] --yes', 2);
|
|
598
684
|
const discovered = findDiscoveredSkill(await discoverSkills(shellPaths(runtime)), name);
|
|
599
685
|
if (!discovered) {
|
|
600
|
-
return failure(runtime, 'skill_discovery_not_found', `Unknown discovered Agent skill
|
|
686
|
+
return failure(runtime, 'skill_discovery_not_found', `Unknown discovered Agent skill ${name}\nRun goodvibes-agent skills discover to inspect available skill files.`, 1);
|
|
601
687
|
}
|
|
602
688
|
if (!hasFlag(options, 'yes')) {
|
|
603
689
|
return success(runtime, 'agent.skills.import_discovered.preview', { skill: discovered }, [
|
|
604
690
|
'Agent skill import preview',
|
|
605
|
-
` name
|
|
606
|
-
` origin
|
|
607
|
-
` path
|
|
608
|
-
` description
|
|
609
|
-
` procedure characters
|
|
610
|
-
' next
|
|
691
|
+
` name ${discovered.name}`,
|
|
692
|
+
` origin ${discovered.origin}`,
|
|
693
|
+
` path ${discovered.path}`,
|
|
694
|
+
` description ${discovered.description || '(none)'}`,
|
|
695
|
+
` procedure characters ${discovered.body.length}`,
|
|
696
|
+
' next rerun with --yes to import into the Agent-local skill registry',
|
|
611
697
|
].join('\n'));
|
|
612
698
|
}
|
|
613
699
|
const skill = registry.create({
|
|
@@ -616,11 +702,19 @@ export async function handleSkillsCommand(runtime: CliCommandRuntime): Promise<C
|
|
|
616
702
|
procedure: discovered.body,
|
|
617
703
|
tags: discoveredFrontmatterList(discovered, 'tags'),
|
|
618
704
|
triggers: discoveredFrontmatterList(discovered, 'triggers'),
|
|
705
|
+
requirements: buildAgentSkillRequirements({
|
|
706
|
+
env: discoveredFrontmatterAnyList(discovered, ['requiresEnv', 'requires-env', 'requires_env']),
|
|
707
|
+
commands: discoveredFrontmatterAnyList(discovered, ['requiresCommands', 'requires-commands', 'requires_commands', 'commands']),
|
|
708
|
+
}),
|
|
619
709
|
enabled: hasFlag(options, 'enabled'),
|
|
620
710
|
source: 'imported',
|
|
621
711
|
provenance: `discovered:${discovered.origin}:${discovered.path}`,
|
|
622
712
|
});
|
|
623
|
-
return success(runtime, 'agent.skills.import_discovered', skill,
|
|
713
|
+
return success(runtime, 'agent.skills.import_discovered', skill, [
|
|
714
|
+
`Imported Agent skill ${skill.id}: ${skill.name}${skill.enabled ? ' (enabled)' : ''}`,
|
|
715
|
+
` name ${skill.name}`,
|
|
716
|
+
` enabled ${skill.enabled ? 'yes' : 'no'}`,
|
|
717
|
+
].join('\n'));
|
|
624
718
|
}
|
|
625
719
|
if (normalized === 'search' || normalized === 'find') {
|
|
626
720
|
const query = rest.join(' ').trim();
|
|
@@ -631,12 +725,17 @@ export async function handleSkillsCommand(runtime: CliCommandRuntime): Promise<C
|
|
|
631
725
|
const id = rest[0];
|
|
632
726
|
if (!id) return failure(runtime, 'invalid_skill_command', 'Usage: goodvibes-agent skills show <id>', 2);
|
|
633
727
|
const skill = registry.get(id);
|
|
634
|
-
if (!skill) return failure(runtime, 'skill_not_found', `Unknown Agent skill
|
|
728
|
+
if (!skill) return failure(runtime, 'skill_not_found', `Unknown Agent skill ${id}`, 1);
|
|
635
729
|
return success(runtime, 'agent.skills.show', skill, renderSkill(skill));
|
|
636
730
|
}
|
|
637
731
|
if (normalized === 'create') {
|
|
638
732
|
const skill = registry.create(skillPayloadFromOptions(parseOptions(rest)));
|
|
639
|
-
return success(runtime, 'agent.skills.create', skill,
|
|
733
|
+
return success(runtime, 'agent.skills.create', skill, [
|
|
734
|
+
'Agent skill created',
|
|
735
|
+
` id ${skill.id}`,
|
|
736
|
+
skill.enabled ? ' (enabled)' : '',
|
|
737
|
+
` enabled ${skill.enabled ? 'yes' : 'no'}`,
|
|
738
|
+
].filter(Boolean).join('\n'));
|
|
640
739
|
}
|
|
641
740
|
if (normalized === 'update') {
|
|
642
741
|
const id = rest[0];
|
|
@@ -648,27 +747,45 @@ export async function handleSkillsCommand(runtime: CliCommandRuntime): Promise<C
|
|
|
648
747
|
procedure: optionValue(options, 'procedure'),
|
|
649
748
|
tags: csvOption(options, 'tags'),
|
|
650
749
|
triggers: csvOption(options, 'triggers'),
|
|
750
|
+
requirements: options.values.has('requires-env') || options.values.has('requires-command') || options.values.has('requires-commands')
|
|
751
|
+
? buildAgentSkillRequirements({
|
|
752
|
+
env: csvOption(options, 'requires-env'),
|
|
753
|
+
commands: csvOption(options, 'requires-command') ?? csvOption(options, 'requires-commands'),
|
|
754
|
+
})
|
|
755
|
+
: undefined,
|
|
651
756
|
provenance: optionValue(options, 'provenance'),
|
|
652
757
|
});
|
|
653
|
-
return success(runtime, 'agent.skills.update', skill,
|
|
758
|
+
return success(runtime, 'agent.skills.update', skill, [
|
|
759
|
+
'Agent skill updated',
|
|
760
|
+
` id ${skill.id}`,
|
|
761
|
+
].join('\n'));
|
|
654
762
|
}
|
|
655
763
|
if (normalized === 'enable' || normalized === 'disable') {
|
|
656
764
|
const id = rest[0];
|
|
657
765
|
if (!id) return failure(runtime, 'invalid_skill_command', `Usage: goodvibes-agent skills ${normalized} <id>`, 2);
|
|
658
766
|
const skill = registry.setEnabled(id, normalized === 'enable');
|
|
659
|
-
return success(runtime, `agent.skills.${normalized}`, skill,
|
|
767
|
+
return success(runtime, `agent.skills.${normalized}`, skill, [
|
|
768
|
+
`Agent skill ${normalized === 'enable' ? 'enabled' : 'disabled'}`,
|
|
769
|
+
` id ${skill.id}`,
|
|
770
|
+
].join('\n'));
|
|
660
771
|
}
|
|
661
772
|
if (normalized === 'review') {
|
|
662
773
|
const id = rest[0];
|
|
663
774
|
if (!id) return failure(runtime, 'invalid_skill_command', 'Usage: goodvibes-agent skills review <id>', 2);
|
|
664
775
|
const skill = registry.markReviewed(id);
|
|
665
|
-
return success(runtime, 'agent.skills.review', skill,
|
|
776
|
+
return success(runtime, 'agent.skills.review', skill, [
|
|
777
|
+
'Agent skill reviewed',
|
|
778
|
+
` id ${skill.id}`,
|
|
779
|
+
].join('\n'));
|
|
666
780
|
}
|
|
667
781
|
if (normalized === 'stale') {
|
|
668
782
|
const id = rest[0];
|
|
669
783
|
if (!id || rest.length < 2) return failure(runtime, 'invalid_skill_command', 'Usage: goodvibes-agent skills stale <id> <reason>', 2);
|
|
670
784
|
const skill = registry.markStale(id, rest.slice(1).join(' '));
|
|
671
|
-
return success(runtime, 'agent.skills.stale', skill,
|
|
785
|
+
return success(runtime, 'agent.skills.stale', skill, [
|
|
786
|
+
'Agent skill marked stale',
|
|
787
|
+
` id ${skill.id}`,
|
|
788
|
+
].join('\n'));
|
|
672
789
|
}
|
|
673
790
|
if (normalized === 'delete' || normalized === 'remove' || normalized === 'rm') {
|
|
674
791
|
const options = parseOptions(rest);
|
|
@@ -676,7 +793,10 @@ export async function handleSkillsCommand(runtime: CliCommandRuntime): Promise<C
|
|
|
676
793
|
if (!id) return failure(runtime, 'invalid_skill_command', 'Usage: goodvibes-agent skills delete <id> --yes', 2);
|
|
677
794
|
if (!hasFlag(options, 'yes')) return failure(runtime, 'confirmation_required', `Refusing to delete Agent skill ${id} without --yes.`, 2);
|
|
678
795
|
const skill = registry.deleteSkill(id);
|
|
679
|
-
return success(runtime, 'agent.skills.delete', skill,
|
|
796
|
+
return success(runtime, 'agent.skills.delete', skill, [
|
|
797
|
+
`Agent skill deleted: ${id}`,
|
|
798
|
+
` id ${skill.id}`,
|
|
799
|
+
].join('\n'));
|
|
680
800
|
}
|
|
681
801
|
return failure(runtime, 'invalid_skill_command', usageSkills(), 2);
|
|
682
802
|
} catch (error) {
|