@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,11 +1,32 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import { dirname } from 'node:path';
|
|
1
|
+
import { accessSync, constants, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { delimiter, dirname, join } from 'node:path';
|
|
3
3
|
import type { ShellPathService } from '@/runtime/index.ts';
|
|
4
4
|
import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
|
|
5
5
|
import { assertNoSecretLikeText } from './persona-registry.ts';
|
|
6
|
+
import { formatAgentRecordReviewState } from './record-labels.ts';
|
|
6
7
|
|
|
7
8
|
export type AgentSkillSource = 'user' | 'agent' | 'imported' | 'system';
|
|
8
9
|
export type AgentSkillReviewState = 'fresh' | 'reviewed' | 'stale';
|
|
10
|
+
export type AgentSkillRequirementKind = 'env' | 'command';
|
|
11
|
+
|
|
12
|
+
export interface AgentSkillRequirement {
|
|
13
|
+
readonly kind: AgentSkillRequirementKind;
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly description?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface AgentSkillReadiness {
|
|
19
|
+
readonly ready: boolean;
|
|
20
|
+
readonly met: readonly AgentSkillRequirement[];
|
|
21
|
+
readonly missing: readonly AgentSkillRequirement[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface AgentSkillBundleReadiness {
|
|
25
|
+
readonly ready: boolean;
|
|
26
|
+
readonly includedSkills: readonly AgentSkillRecord[];
|
|
27
|
+
readonly missingSkillIds: readonly string[];
|
|
28
|
+
readonly missingRequirements: readonly AgentSkillRequirement[];
|
|
29
|
+
}
|
|
9
30
|
|
|
10
31
|
export interface AgentSkillRecord {
|
|
11
32
|
readonly id: string;
|
|
@@ -14,6 +35,7 @@ export interface AgentSkillRecord {
|
|
|
14
35
|
readonly procedure: string;
|
|
15
36
|
readonly triggers: readonly string[];
|
|
16
37
|
readonly tags: readonly string[];
|
|
38
|
+
readonly requirements: readonly AgentSkillRequirement[];
|
|
17
39
|
readonly enabled: boolean;
|
|
18
40
|
readonly source: AgentSkillSource;
|
|
19
41
|
readonly provenance: string;
|
|
@@ -45,6 +67,7 @@ export interface AgentSkillCreateInput {
|
|
|
45
67
|
readonly procedure: string;
|
|
46
68
|
readonly triggers?: readonly string[];
|
|
47
69
|
readonly tags?: readonly string[];
|
|
70
|
+
readonly requirements?: readonly AgentSkillRequirement[];
|
|
48
71
|
readonly enabled?: boolean;
|
|
49
72
|
readonly source?: AgentSkillSource;
|
|
50
73
|
readonly provenance?: string;
|
|
@@ -65,6 +88,7 @@ export interface AgentSkillUpdateInput {
|
|
|
65
88
|
readonly procedure?: string;
|
|
66
89
|
readonly triggers?: readonly string[];
|
|
67
90
|
readonly tags?: readonly string[];
|
|
91
|
+
readonly requirements?: readonly AgentSkillRequirement[];
|
|
68
92
|
readonly provenance?: string;
|
|
69
93
|
}
|
|
70
94
|
|
|
@@ -105,6 +129,11 @@ function readStringArray(value: unknown): string[] {
|
|
|
105
129
|
return value.filter((entry): entry is string => typeof entry === 'string').map((entry) => entry.trim()).filter(Boolean);
|
|
106
130
|
}
|
|
107
131
|
|
|
132
|
+
function readRequirementKind(value: unknown): AgentSkillRequirementKind | null {
|
|
133
|
+
if (value === 'env' || value === 'command') return value;
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
108
137
|
function normalizeName(name: string): string {
|
|
109
138
|
return name.trim().replace(/\s+/g, ' ');
|
|
110
139
|
}
|
|
@@ -123,6 +152,48 @@ function normalizeList(values: readonly string[] | undefined): string[] {
|
|
|
123
152
|
return result;
|
|
124
153
|
}
|
|
125
154
|
|
|
155
|
+
function validateRequirementName(requirement: AgentSkillRequirement): void {
|
|
156
|
+
if (requirement.kind === 'env' && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(requirement.name)) {
|
|
157
|
+
throw new Error(`Invalid skill env requirement ${requirement.name}`);
|
|
158
|
+
}
|
|
159
|
+
if (requirement.kind === 'command' && !/^[A-Za-z0-9._+-]+$/.test(requirement.name)) {
|
|
160
|
+
throw new Error(`Invalid skill command requirement ${requirement.name}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function normalizeRequirements(values: readonly AgentSkillRequirement[] | undefined): AgentSkillRequirement[] {
|
|
165
|
+
const seen = new Set<string>();
|
|
166
|
+
const result: AgentSkillRequirement[] = [];
|
|
167
|
+
for (const value of values ?? []) {
|
|
168
|
+
const kind = readRequirementKind(value.kind);
|
|
169
|
+
const name = typeof value.name === 'string' ? value.name.trim() : '';
|
|
170
|
+
if (!kind || !name) continue;
|
|
171
|
+
const description = typeof value.description === 'string' ? value.description.trim() : '';
|
|
172
|
+
const requirement: AgentSkillRequirement = {
|
|
173
|
+
kind,
|
|
174
|
+
name,
|
|
175
|
+
...(description ? { description } : {}),
|
|
176
|
+
};
|
|
177
|
+
validateRequirementName(requirement);
|
|
178
|
+
const key = `${requirement.kind}:${requirement.name.toLowerCase()}`;
|
|
179
|
+
if (seen.has(key)) continue;
|
|
180
|
+
seen.add(key);
|
|
181
|
+
result.push(requirement);
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function parseRequirements(value: unknown): AgentSkillRequirement[] {
|
|
187
|
+
if (!Array.isArray(value)) return [];
|
|
188
|
+
return normalizeRequirements(value
|
|
189
|
+
.filter(isRecord)
|
|
190
|
+
.map((entry) => ({
|
|
191
|
+
kind: readRequirementKind(entry.kind) ?? 'env',
|
|
192
|
+
name: readString(entry.name).trim(),
|
|
193
|
+
description: readString(entry.description).trim(),
|
|
194
|
+
})));
|
|
195
|
+
}
|
|
196
|
+
|
|
126
197
|
function slugify(value: string): string {
|
|
127
198
|
const slug = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
128
199
|
return slug || 'skill';
|
|
@@ -132,6 +203,23 @@ function nowIso(): string {
|
|
|
132
203
|
return new Date().toISOString();
|
|
133
204
|
}
|
|
134
205
|
|
|
206
|
+
function canExecute(path: string): boolean {
|
|
207
|
+
try {
|
|
208
|
+
accessSync(path, constants.X_OK);
|
|
209
|
+
return true;
|
|
210
|
+
} catch {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function commandExists(command: string, pathValue: string | undefined): boolean {
|
|
216
|
+
if (!pathValue) return false;
|
|
217
|
+
return pathValue
|
|
218
|
+
.split(delimiter)
|
|
219
|
+
.filter(Boolean)
|
|
220
|
+
.some((pathEntry) => canExecute(join(pathEntry, command)));
|
|
221
|
+
}
|
|
222
|
+
|
|
135
223
|
function parseSkill(value: unknown): AgentSkillRecord | null {
|
|
136
224
|
if (!isRecord(value)) return null;
|
|
137
225
|
const id = readString(value.id).trim();
|
|
@@ -150,6 +238,7 @@ function parseSkill(value: unknown): AgentSkillRecord | null {
|
|
|
150
238
|
procedure,
|
|
151
239
|
triggers: readStringArray(value.triggers),
|
|
152
240
|
tags: readStringArray(value.tags),
|
|
241
|
+
requirements: parseRequirements(value.requirements),
|
|
153
242
|
enabled: value.enabled === true,
|
|
154
243
|
source,
|
|
155
244
|
provenance: readString(value.provenance, source).trim() || source,
|
|
@@ -206,14 +295,16 @@ function formatStore(store: SkillStoreFile): string {
|
|
|
206
295
|
return `${JSON.stringify(store, null, 2)}\n`;
|
|
207
296
|
}
|
|
208
297
|
|
|
209
|
-
|
|
298
|
+
type AgentLocalStorePaths = Pick<ShellPathService, 'resolveUserPath'>;
|
|
299
|
+
|
|
300
|
+
export function skillStorePath(shellPaths: AgentLocalStorePaths): string {
|
|
210
301
|
return shellPaths.resolveUserPath(GOODVIBES_AGENT_SURFACE_ROOT, 'skills', 'skills.json');
|
|
211
302
|
}
|
|
212
303
|
|
|
213
304
|
export class AgentSkillRegistry {
|
|
214
305
|
public constructor(private readonly storePath: string) {}
|
|
215
306
|
|
|
216
|
-
public static fromShellPaths(shellPaths:
|
|
307
|
+
public static fromShellPaths(shellPaths: AgentLocalStorePaths): AgentSkillRegistry {
|
|
217
308
|
return new AgentSkillRegistry(skillStorePath(shellPaths));
|
|
218
309
|
}
|
|
219
310
|
|
|
@@ -284,9 +375,10 @@ export class AgentSkillRegistry {
|
|
|
284
375
|
const description = input.description.trim();
|
|
285
376
|
const procedure = input.procedure.trim();
|
|
286
377
|
this.validateRequired(name, description, procedure);
|
|
287
|
-
|
|
378
|
+
const requirements = normalizeRequirements(input.requirements);
|
|
379
|
+
assertNoSecretLikeText([name, description, procedure, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])], 'Skills');
|
|
288
380
|
const duplicate = store.skills.find((skill) => skill.name.toLowerCase() === name.toLowerCase());
|
|
289
|
-
if (duplicate) throw new Error(`Skill already exists
|
|
381
|
+
if (duplicate) throw new Error(`Skill already exists ${duplicate.id}`);
|
|
290
382
|
const timestamp = nowIso();
|
|
291
383
|
const skill: AgentSkillRecord = {
|
|
292
384
|
id: this.nextId(name, store.skills),
|
|
@@ -295,6 +387,7 @@ export class AgentSkillRegistry {
|
|
|
295
387
|
procedure,
|
|
296
388
|
triggers: normalizeList(input.triggers),
|
|
297
389
|
tags: normalizeList(input.tags),
|
|
390
|
+
requirements,
|
|
298
391
|
enabled: input.enabled === true,
|
|
299
392
|
source: input.source ?? 'user',
|
|
300
393
|
provenance: input.provenance?.trim() || input.source || 'user',
|
|
@@ -312,9 +405,9 @@ export class AgentSkillRegistry {
|
|
|
312
405
|
const description = input.description.trim();
|
|
313
406
|
const skillIds = this.normalizeExistingSkillIds(store, input.skillIds);
|
|
314
407
|
this.validateBundleRequired(name, description, skillIds);
|
|
315
|
-
assertNoSecretLikeText([name, description, ...skillIds]);
|
|
408
|
+
assertNoSecretLikeText([name, description, ...skillIds], 'Skill bundles');
|
|
316
409
|
const duplicate = store.bundles.find((bundle) => bundle.name.toLowerCase() === name.toLowerCase());
|
|
317
|
-
if (duplicate) throw new Error(`Skill bundle already exists
|
|
410
|
+
if (duplicate) throw new Error(`Skill bundle already exists ${duplicate.id}`);
|
|
318
411
|
const timestamp = nowIso();
|
|
319
412
|
const bundle: AgentSkillBundleRecord = {
|
|
320
413
|
id: this.nextBundleId(name, store.bundles),
|
|
@@ -335,14 +428,15 @@ export class AgentSkillRegistry {
|
|
|
335
428
|
public update(idOrName: string, input: AgentSkillUpdateInput): AgentSkillRecord {
|
|
336
429
|
const store = this.readStore();
|
|
337
430
|
const existing = this.findInStore(store, idOrName);
|
|
338
|
-
if (!existing) throw new Error(`Unknown skill
|
|
431
|
+
if (!existing) throw new Error(`Unknown skill ${idOrName}`);
|
|
339
432
|
const name = input.name === undefined ? existing.name : normalizeName(input.name);
|
|
340
433
|
const description = input.description === undefined ? existing.description : input.description.trim();
|
|
341
434
|
const procedure = input.procedure === undefined ? existing.procedure : input.procedure.trim();
|
|
342
435
|
this.validateRequired(name, description, procedure);
|
|
343
|
-
|
|
436
|
+
const requirements = input.requirements === undefined ? existing.requirements : normalizeRequirements(input.requirements);
|
|
437
|
+
assertNoSecretLikeText([name, description, procedure, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])], 'Skills');
|
|
344
438
|
const duplicate = store.skills.find((skill) => skill.id !== existing.id && skill.name.toLowerCase() === name.toLowerCase());
|
|
345
|
-
if (duplicate) throw new Error(`Skill already exists
|
|
439
|
+
if (duplicate) throw new Error(`Skill already exists ${duplicate.id}`);
|
|
346
440
|
const updated: AgentSkillRecord = {
|
|
347
441
|
...existing,
|
|
348
442
|
name,
|
|
@@ -350,6 +444,7 @@ export class AgentSkillRegistry {
|
|
|
350
444
|
procedure,
|
|
351
445
|
triggers: input.triggers === undefined ? existing.triggers : normalizeList(input.triggers),
|
|
352
446
|
tags: input.tags === undefined ? existing.tags : normalizeList(input.tags),
|
|
447
|
+
requirements,
|
|
353
448
|
provenance: input.provenance === undefined ? existing.provenance : input.provenance.trim() || existing.provenance,
|
|
354
449
|
reviewState: 'fresh',
|
|
355
450
|
staleReason: undefined,
|
|
@@ -366,14 +461,14 @@ export class AgentSkillRegistry {
|
|
|
366
461
|
public updateBundle(idOrName: string, input: AgentSkillBundleUpdateInput): AgentSkillBundleRecord {
|
|
367
462
|
const store = this.readStore();
|
|
368
463
|
const existing = this.findBundleInStore(store, idOrName);
|
|
369
|
-
if (!existing) throw new Error(`Unknown skill bundle
|
|
464
|
+
if (!existing) throw new Error(`Unknown skill bundle ${idOrName}`);
|
|
370
465
|
const name = input.name === undefined ? existing.name : normalizeName(input.name);
|
|
371
466
|
const description = input.description === undefined ? existing.description : input.description.trim();
|
|
372
467
|
const skillIds = input.skillIds === undefined ? existing.skillIds : this.normalizeExistingSkillIds(store, input.skillIds);
|
|
373
468
|
this.validateBundleRequired(name, description, skillIds);
|
|
374
|
-
assertNoSecretLikeText([name, description, ...skillIds]);
|
|
469
|
+
assertNoSecretLikeText([name, description, ...skillIds], 'Skill bundles');
|
|
375
470
|
const duplicate = store.bundles.find((bundle) => bundle.id !== existing.id && bundle.name.toLowerCase() === name.toLowerCase());
|
|
376
|
-
if (duplicate) throw new Error(`Skill bundle already exists
|
|
471
|
+
if (duplicate) throw new Error(`Skill bundle already exists ${duplicate.id}`);
|
|
377
472
|
const updated: AgentSkillBundleRecord = {
|
|
378
473
|
...existing,
|
|
379
474
|
name,
|
|
@@ -395,7 +490,7 @@ export class AgentSkillRegistry {
|
|
|
395
490
|
public setEnabled(idOrName: string, enabled: boolean): AgentSkillRecord {
|
|
396
491
|
const store = this.readStore();
|
|
397
492
|
const existing = this.findInStore(store, idOrName);
|
|
398
|
-
if (!existing) throw new Error(`Unknown skill
|
|
493
|
+
if (!existing) throw new Error(`Unknown skill ${idOrName}`);
|
|
399
494
|
const updated: AgentSkillRecord = { ...existing, enabled, updatedAt: nowIso() };
|
|
400
495
|
this.writeStore({
|
|
401
496
|
...store,
|
|
@@ -407,7 +502,7 @@ export class AgentSkillRegistry {
|
|
|
407
502
|
public setBundleEnabled(idOrName: string, enabled: boolean): AgentSkillBundleRecord {
|
|
408
503
|
const store = this.readStore();
|
|
409
504
|
const existing = this.findBundleInStore(store, idOrName);
|
|
410
|
-
if (!existing) throw new Error(`Unknown skill bundle
|
|
505
|
+
if (!existing) throw new Error(`Unknown skill bundle ${idOrName}`);
|
|
411
506
|
const updated: AgentSkillBundleRecord = { ...existing, enabled, updatedAt: nowIso() };
|
|
412
507
|
this.writeStore({
|
|
413
508
|
...store,
|
|
@@ -419,7 +514,7 @@ export class AgentSkillRegistry {
|
|
|
419
514
|
public markReviewed(idOrName: string): AgentSkillRecord {
|
|
420
515
|
const store = this.readStore();
|
|
421
516
|
const existing = this.findInStore(store, idOrName);
|
|
422
|
-
if (!existing) throw new Error(`Unknown skill
|
|
517
|
+
if (!existing) throw new Error(`Unknown skill ${idOrName}`);
|
|
423
518
|
const updated: AgentSkillRecord = {
|
|
424
519
|
...existing,
|
|
425
520
|
reviewState: 'reviewed',
|
|
@@ -437,7 +532,7 @@ export class AgentSkillRegistry {
|
|
|
437
532
|
public markBundleReviewed(idOrName: string): AgentSkillBundleRecord {
|
|
438
533
|
const store = this.readStore();
|
|
439
534
|
const existing = this.findBundleInStore(store, idOrName);
|
|
440
|
-
if (!existing) throw new Error(`Unknown skill bundle
|
|
535
|
+
if (!existing) throw new Error(`Unknown skill bundle ${idOrName}`);
|
|
441
536
|
const updated: AgentSkillBundleRecord = {
|
|
442
537
|
...existing,
|
|
443
538
|
reviewState: 'reviewed',
|
|
@@ -455,7 +550,7 @@ export class AgentSkillRegistry {
|
|
|
455
550
|
public markStale(idOrName: string, reason: string): AgentSkillRecord {
|
|
456
551
|
const store = this.readStore();
|
|
457
552
|
const existing = this.findInStore(store, idOrName);
|
|
458
|
-
if (!existing) throw new Error(`Unknown skill
|
|
553
|
+
if (!existing) throw new Error(`Unknown skill ${idOrName}`);
|
|
459
554
|
const updated: AgentSkillRecord = {
|
|
460
555
|
...existing,
|
|
461
556
|
reviewState: 'stale',
|
|
@@ -472,7 +567,7 @@ export class AgentSkillRegistry {
|
|
|
472
567
|
public markBundleStale(idOrName: string, reason: string): AgentSkillBundleRecord {
|
|
473
568
|
const store = this.readStore();
|
|
474
569
|
const existing = this.findBundleInStore(store, idOrName);
|
|
475
|
-
if (!existing) throw new Error(`Unknown skill bundle
|
|
570
|
+
if (!existing) throw new Error(`Unknown skill bundle ${idOrName}`);
|
|
476
571
|
const updated: AgentSkillBundleRecord = {
|
|
477
572
|
...existing,
|
|
478
573
|
reviewState: 'stale',
|
|
@@ -489,7 +584,7 @@ export class AgentSkillRegistry {
|
|
|
489
584
|
public deleteSkill(idOrName: string): AgentSkillRecord {
|
|
490
585
|
const store = this.readStore();
|
|
491
586
|
const existing = this.findInStore(store, idOrName);
|
|
492
|
-
if (!existing) throw new Error(`Unknown skill
|
|
587
|
+
if (!existing) throw new Error(`Unknown skill ${idOrName}`);
|
|
493
588
|
this.writeStore({
|
|
494
589
|
...store,
|
|
495
590
|
skills: store.skills.filter((skill) => skill.id !== existing.id),
|
|
@@ -503,7 +598,7 @@ export class AgentSkillRegistry {
|
|
|
503
598
|
public deleteBundle(idOrName: string): AgentSkillBundleRecord {
|
|
504
599
|
const store = this.readStore();
|
|
505
600
|
const existing = this.findBundleInStore(store, idOrName);
|
|
506
|
-
if (!existing) throw new Error(`Unknown skill bundle
|
|
601
|
+
if (!existing) throw new Error(`Unknown skill bundle ${idOrName}`);
|
|
507
602
|
this.writeStore({
|
|
508
603
|
...store,
|
|
509
604
|
bundles: store.bundles.filter((bundle) => bundle.id !== existing.id),
|
|
@@ -561,7 +656,7 @@ export class AgentSkillRegistry {
|
|
|
561
656
|
const normalized = normalizeList(skillIds).map(slugify);
|
|
562
657
|
const known = new Set(store.skills.map((skill) => skill.id));
|
|
563
658
|
for (const skillId of normalized) {
|
|
564
|
-
if (!known.has(skillId)) throw new Error(`Unknown skill for bundle
|
|
659
|
+
if (!known.has(skillId)) throw new Error(`Unknown skill for bundle ${skillId}`);
|
|
565
660
|
}
|
|
566
661
|
return normalized;
|
|
567
662
|
}
|
|
@@ -571,7 +666,7 @@ export class AgentSkillRegistry {
|
|
|
571
666
|
try {
|
|
572
667
|
return parseStore(readFileSync(this.storePath, 'utf-8'));
|
|
573
668
|
} catch (error) {
|
|
574
|
-
throw new Error(`Could not read Agent skill store
|
|
669
|
+
throw new Error(`Could not read Agent skill store ${error instanceof Error ? error.message : String(error)}`);
|
|
575
670
|
}
|
|
576
671
|
}
|
|
577
672
|
|
|
@@ -583,6 +678,80 @@ export class AgentSkillRegistry {
|
|
|
583
678
|
}
|
|
584
679
|
}
|
|
585
680
|
|
|
681
|
+
export function buildAgentSkillRequirements(input: {
|
|
682
|
+
readonly env?: readonly string[];
|
|
683
|
+
readonly commands?: readonly string[];
|
|
684
|
+
}): readonly AgentSkillRequirement[] {
|
|
685
|
+
return normalizeRequirements([
|
|
686
|
+
...(input.env ?? []).map((name) => ({ kind: 'env' as const, name })),
|
|
687
|
+
...(input.commands ?? []).map((name) => ({ kind: 'command' as const, name })),
|
|
688
|
+
]);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
export function normalizeAgentSkillRequirements(values: readonly AgentSkillRequirement[] | undefined): readonly AgentSkillRequirement[] {
|
|
692
|
+
return normalizeRequirements(values);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
export function formatAgentSkillRequirement(requirement: AgentSkillRequirement): string {
|
|
696
|
+
return `${requirement.kind}:${requirement.name}`;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
export function evaluateAgentSkillReadiness(
|
|
700
|
+
skill: Pick<AgentSkillRecord, 'requirements'>,
|
|
701
|
+
options: {
|
|
702
|
+
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
703
|
+
readonly pathValue?: string;
|
|
704
|
+
} = {},
|
|
705
|
+
): AgentSkillReadiness {
|
|
706
|
+
const env = options.env ?? process.env;
|
|
707
|
+
const pathValue = options.pathValue ?? env.PATH;
|
|
708
|
+
const met: AgentSkillRequirement[] = [];
|
|
709
|
+
const missing: AgentSkillRequirement[] = [];
|
|
710
|
+
for (const requirement of skill.requirements) {
|
|
711
|
+
const present = requirement.kind === 'env'
|
|
712
|
+
? typeof env[requirement.name] === 'string' && (env[requirement.name] ?? '').length > 0
|
|
713
|
+
: commandExists(requirement.name, pathValue);
|
|
714
|
+
if (present) met.push(requirement);
|
|
715
|
+
else missing.push(requirement);
|
|
716
|
+
}
|
|
717
|
+
return { ready: missing.length === 0, met, missing };
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
export function evaluateAgentSkillBundleReadiness(
|
|
721
|
+
bundle: Pick<AgentSkillBundleRecord, 'skillIds'>,
|
|
722
|
+
skills: readonly AgentSkillRecord[],
|
|
723
|
+
options: {
|
|
724
|
+
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
725
|
+
readonly pathValue?: string;
|
|
726
|
+
} = {},
|
|
727
|
+
): AgentSkillBundleReadiness {
|
|
728
|
+
const skillById = new Map(skills.map((skill) => [skill.id, skill]));
|
|
729
|
+
const includedSkills: AgentSkillRecord[] = [];
|
|
730
|
+
const missingSkillIds: string[] = [];
|
|
731
|
+
const missingRequirements: AgentSkillRequirement[] = [];
|
|
732
|
+
const seenRequirements = new Set<string>();
|
|
733
|
+
for (const skillId of bundle.skillIds) {
|
|
734
|
+
const skill = skillById.get(skillId);
|
|
735
|
+
if (!skill) {
|
|
736
|
+
missingSkillIds.push(skillId);
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
includedSkills.push(skill);
|
|
740
|
+
for (const requirement of evaluateAgentSkillReadiness(skill, options).missing) {
|
|
741
|
+
const key = `${requirement.kind}:${requirement.name.toLowerCase()}`;
|
|
742
|
+
if (seenRequirements.has(key)) continue;
|
|
743
|
+
seenRequirements.add(key);
|
|
744
|
+
missingRequirements.push(requirement);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
return {
|
|
748
|
+
ready: missingSkillIds.length === 0 && missingRequirements.length === 0,
|
|
749
|
+
includedSkills,
|
|
750
|
+
missingSkillIds,
|
|
751
|
+
missingRequirements,
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
|
|
586
755
|
export function buildEnabledSkillsPrompt(shellPaths: ShellPathService): string | null {
|
|
587
756
|
const snapshot = AgentSkillRegistry.fromShellPaths(shellPaths).snapshot();
|
|
588
757
|
const active = snapshot.activeSkills;
|
|
@@ -591,18 +760,27 @@ export function buildEnabledSkillsPrompt(shellPaths: ShellPathService): string |
|
|
|
591
760
|
'## Enabled GoodVibes Agent Skills',
|
|
592
761
|
'Use these local reusable procedures inside the same serial assistant conversation when they fit the user request.',
|
|
593
762
|
'',
|
|
594
|
-
...snapshot.enabledBundles.slice(0, 4).flatMap((bundle) =>
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
763
|
+
...snapshot.enabledBundles.slice(0, 4).flatMap((bundle) => {
|
|
764
|
+
const readiness = evaluateAgentSkillBundleReadiness(bundle, snapshot.skills);
|
|
765
|
+
const missing = [
|
|
766
|
+
...readiness.missingRequirements.map(formatAgentSkillRequirement),
|
|
767
|
+
...readiness.missingSkillIds.map((skillId) => `skill:${skillId}`),
|
|
768
|
+
];
|
|
769
|
+
return [
|
|
770
|
+
`### Skill Bundle: ${bundle.name}`,
|
|
771
|
+
`Description: ${bundle.description}`,
|
|
772
|
+
`Review: ${formatAgentRecordReviewState(bundle.reviewState)}`,
|
|
773
|
+
`Readiness: ${readiness.ready ? 'ready' : `missing ${missing.join(', ')}`}`,
|
|
774
|
+
`Included skills: ${bundle.skillIds.join(', ')}`,
|
|
775
|
+
'',
|
|
776
|
+
];
|
|
777
|
+
}),
|
|
601
778
|
...active.slice(0, 8).flatMap((skill) => [
|
|
602
779
|
`### ${skill.name}`,
|
|
603
780
|
`Description: ${skill.description}`,
|
|
604
|
-
`Review
|
|
781
|
+
`Review: ${formatAgentRecordReviewState(skill.reviewState)}`,
|
|
605
782
|
`Triggers: ${skill.triggers.join(', ') || '(manual)'}`,
|
|
783
|
+
`Readiness: ${evaluateAgentSkillReadiness(skill).ready ? 'ready' : `missing ${evaluateAgentSkillReadiness(skill).missing.map(formatAgentSkillRequirement).join(', ')}`}`,
|
|
606
784
|
skill.procedure,
|
|
607
785
|
'',
|
|
608
786
|
]),
|
|
@@ -14,7 +14,7 @@ export function commandValues(args: readonly string[]): string[] {
|
|
|
14
14
|
export function delegationTaskValues(args: readonly string[]): string[] {
|
|
15
15
|
const values: string[] = [];
|
|
16
16
|
for (const token of args) {
|
|
17
|
-
if (token === '--wrfc') continue;
|
|
17
|
+
if (token === '--review' || token === '--wrfc') continue;
|
|
18
18
|
if (!token.startsWith('--')) values.push(token);
|
|
19
19
|
}
|
|
20
20
|
return values;
|
|
@@ -79,6 +79,10 @@ export function hasFlag(args: readonly string[], flag: string): boolean {
|
|
|
79
79
|
return args.includes(flag);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
export function hasAnyFlag(args: readonly string[], flags: readonly string[]): boolean {
|
|
83
|
+
return flags.some((flag) => args.includes(flag));
|
|
84
|
+
}
|
|
85
|
+
|
|
82
86
|
export function stripCommandFlag(args: readonly string[], flag: string): { readonly rest: readonly string[]; readonly present: boolean } {
|
|
83
87
|
const rest: string[] = [];
|
|
84
88
|
let present = false;
|
|
@@ -3,6 +3,7 @@ import type { CliCommandRuntime } from './management.ts';
|
|
|
3
3
|
import {
|
|
4
4
|
commandValues,
|
|
5
5
|
delegationTaskValues,
|
|
6
|
+
hasAnyFlag,
|
|
6
7
|
hasFlag,
|
|
7
8
|
parseConnectorInput,
|
|
8
9
|
readFirstStringList,
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
} from './agent-knowledge-args.ts';
|
|
15
16
|
import {
|
|
16
17
|
formatAsk,
|
|
18
|
+
formatAgentKnowledgeFailureKind,
|
|
17
19
|
formatBatchIngest,
|
|
18
20
|
formatConnector,
|
|
19
21
|
formatConnectorDoctor,
|
|
@@ -30,7 +32,7 @@ import {
|
|
|
30
32
|
import { AGENT_KNOWLEDGE_METHODS, DELEGATION_METHOD } from './agent-knowledge-methods.ts';
|
|
31
33
|
import {
|
|
32
34
|
createAgentSdk,
|
|
33
|
-
|
|
35
|
+
fetchConnectedHostStatus,
|
|
34
36
|
findDisallowedKnowledgeScopeFlag,
|
|
35
37
|
formatScopeFlagRejection,
|
|
36
38
|
getAgentKnowledgeJson,
|
|
@@ -38,7 +40,7 @@ import {
|
|
|
38
40
|
postAgentKnowledgeJson,
|
|
39
41
|
readPackageMetadata,
|
|
40
42
|
readString,
|
|
41
|
-
|
|
43
|
+
resolveConnectedHostConnection,
|
|
42
44
|
runKnowledgeCall,
|
|
43
45
|
} from './agent-knowledge-runtime.ts';
|
|
44
46
|
import { formatJsonOrText, yesNo } from './management.ts';
|
|
@@ -47,23 +49,23 @@ interface DelegationResult {
|
|
|
47
49
|
readonly sessionId: string;
|
|
48
50
|
readonly message: unknown;
|
|
49
51
|
readonly task: string;
|
|
50
|
-
readonly
|
|
52
|
+
readonly reviewRequested: boolean;
|
|
51
53
|
}
|
|
52
54
|
|
|
53
|
-
function buildDelegationBody(task: string,
|
|
55
|
+
function buildDelegationBody(task: string, reviewRequested: boolean): string {
|
|
54
56
|
return [
|
|
55
57
|
'GoodVibes Agent explicit build delegation.',
|
|
56
58
|
'',
|
|
57
|
-
'Original user ask
|
|
59
|
+
'Original user ask',
|
|
58
60
|
task,
|
|
59
61
|
'',
|
|
60
|
-
'Agent policy
|
|
62
|
+
'Agent policy',
|
|
61
63
|
'- GoodVibes Agent is not the coding TUI.',
|
|
62
64
|
'- Preserve the full original ask.',
|
|
63
|
-
'- GoodVibes TUI owns file edits, git/worktree flows, execution isolation UX, and any
|
|
64
|
-
|
|
65
|
-
? '-
|
|
66
|
-
: '-
|
|
65
|
+
'- GoodVibes TUI owns file edits, git/worktree flows, execution isolation UX, and any delegated review owner chain.',
|
|
66
|
+
reviewRequested
|
|
67
|
+
? '- Delegated review was explicitly requested by the Agent user for this build/fix/review handoff.'
|
|
68
|
+
: '- Delegated review was not explicitly requested; do not add review solely because this came from Agent.',
|
|
67
69
|
].join('\n');
|
|
68
70
|
}
|
|
69
71
|
|
|
@@ -479,12 +481,12 @@ export async function handleAgentKnowledgeShortcutCommand(
|
|
|
479
481
|
}
|
|
480
482
|
|
|
481
483
|
export async function handleCompatCommand(runtime: CliCommandRuntime): Promise<CliCommandOutput> {
|
|
482
|
-
const connection =
|
|
484
|
+
const connection = resolveConnectedHostConnection(runtime);
|
|
483
485
|
const metadata = readPackageMetadata();
|
|
484
|
-
const
|
|
485
|
-
const
|
|
486
|
-
const
|
|
487
|
-
const versionCompatible =
|
|
486
|
+
const connectedHost = await fetchConnectedHostStatus(connection);
|
|
487
|
+
const connectedHostRecord = isRecord(connectedHost.body) ? connectedHost.body : {};
|
|
488
|
+
const connectedHostVersion = readString(connectedHostRecord, 'version') ?? 'unknown';
|
|
489
|
+
const versionCompatible = connectedHost.ok && connectedHostVersion === metadata.sdkVersion;
|
|
488
490
|
const knowledgeRoute = await runKnowledgeCall(runtime, AGENT_KNOWLEDGE_METHODS.status, async (routeConnection) => (
|
|
489
491
|
await createAgentSdk(routeConnection).knowledge.status()
|
|
490
492
|
));
|
|
@@ -493,11 +495,11 @@ export async function handleCompatCommand(runtime: CliCommandRuntime): Promise<C
|
|
|
493
495
|
ok: versionCompatible && knowledgeRouteReady,
|
|
494
496
|
packageVersion: metadata.version,
|
|
495
497
|
sdkPin: metadata.sdkVersion,
|
|
496
|
-
|
|
498
|
+
connectedHost: {
|
|
497
499
|
baseUrl: connection.baseUrl,
|
|
498
|
-
status:
|
|
499
|
-
version:
|
|
500
|
-
reachable:
|
|
500
|
+
status: connectedHost.status,
|
|
501
|
+
version: connectedHostVersion,
|
|
502
|
+
reachable: connectedHost.ok,
|
|
501
503
|
compatible: versionCompatible,
|
|
502
504
|
},
|
|
503
505
|
auth: {
|
|
@@ -512,13 +514,13 @@ export async function handleCompatCommand(runtime: CliCommandRuntime): Promise<C
|
|
|
512
514
|
};
|
|
513
515
|
const text = [
|
|
514
516
|
'GoodVibes Agent compatibility',
|
|
515
|
-
` package
|
|
516
|
-
` SDK pin
|
|
517
|
-
`
|
|
518
|
-
` version compatible
|
|
519
|
-
` operator token
|
|
520
|
-
` Agent
|
|
521
|
-
...(versionCompatible ? [] : [' next
|
|
517
|
+
` package ${metadata.version}`,
|
|
518
|
+
` SDK pin ${metadata.sdkVersion}`,
|
|
519
|
+
` connected host ${connectedHostVersion} at ${connection.baseUrl} (${connectedHost.ok ? 'reachable' : 'unreachable'})`,
|
|
520
|
+
` version compatible ${yesNo(versionCompatible)}`,
|
|
521
|
+
` operator token ${connection.token ? 'present' : 'missing'} (${connection.tokenPath})`,
|
|
522
|
+
` Agent Knowledge route ${knowledgeRouteReady ? 'ready' : `not ready (${knowledgeRoute.ok ? 'unknown' : formatAgentKnowledgeFailureKind(knowledgeRoute.kind)})`}`,
|
|
523
|
+
...(versionCompatible ? [] : [' next update the connected GoodVibes host so /status matches the Agent SDK pin.']),
|
|
522
524
|
].join('\n');
|
|
523
525
|
return {
|
|
524
526
|
output: runtime.cli.flags.outputFormat === 'json' ? JSON.stringify(value, null, 2) : text,
|
|
@@ -527,11 +529,11 @@ export async function handleCompatCommand(runtime: CliCommandRuntime): Promise<C
|
|
|
527
529
|
}
|
|
528
530
|
|
|
529
531
|
export async function handleDelegateCommand(runtime: CliCommandRuntime): Promise<CliCommandOutput> {
|
|
530
|
-
const
|
|
532
|
+
const reviewRequested = hasAnyFlag(runtime.cli.commandArgs, ['--review', '--wrfc']);
|
|
531
533
|
const task = delegationTaskValues(runtime.cli.commandArgs).join(' ').trim();
|
|
532
534
|
if (!task) {
|
|
533
535
|
return {
|
|
534
|
-
output: 'Usage: goodvibes-agent delegate [--
|
|
536
|
+
output: 'Usage: goodvibes-agent delegate [--review] <build/fix/review task>',
|
|
535
537
|
exitCode: 2,
|
|
536
538
|
};
|
|
537
539
|
}
|
|
@@ -548,10 +550,14 @@ export async function handleDelegateCommand(runtime: CliCommandRuntime): Promise
|
|
|
548
550
|
if (!sessionId) throw new Error('sessions.create returned no session id.');
|
|
549
551
|
const message = await sdk.operator.invoke('sessions.messages.create', {
|
|
550
552
|
sessionId,
|
|
551
|
-
body: buildDelegationBody(task,
|
|
553
|
+
body: buildDelegationBody(task, reviewRequested),
|
|
552
554
|
surfaceKind: 'goodvibes-agent',
|
|
553
555
|
surfaceId: 'goodvibes-agent-cli',
|
|
554
556
|
kind: 'task',
|
|
557
|
+
metadata: {
|
|
558
|
+
reviewRequested,
|
|
559
|
+
wrfcRequested: reviewRequested,
|
|
560
|
+
},
|
|
555
561
|
routing: {
|
|
556
562
|
executionIntent: {
|
|
557
563
|
riskClass: 'elevated',
|
|
@@ -561,15 +567,15 @@ export async function handleDelegateCommand(runtime: CliCommandRuntime): Promise
|
|
|
561
567
|
},
|
|
562
568
|
},
|
|
563
569
|
});
|
|
564
|
-
return { sessionId, message, task,
|
|
570
|
+
return { sessionId, message, task, reviewRequested };
|
|
565
571
|
});
|
|
566
572
|
if (!result.ok) return { output: formatFailure(result, runtime.cli.flags.outputFormat === 'json'), exitCode: 1 };
|
|
567
573
|
const text = [
|
|
568
574
|
'Delegation submitted to GoodVibes TUI/shared-session routes.',
|
|
569
|
-
` session
|
|
570
|
-
` mode
|
|
571
|
-
` task
|
|
572
|
-
' next
|
|
575
|
+
` session ${result.data.sessionId}`,
|
|
576
|
+
` mode ${result.data.reviewRequested ? 'delegated review requested' : 'direct build delegation'}`,
|
|
577
|
+
` task ${result.data.task}`,
|
|
578
|
+
' next check GoodVibes TUI shared-session/task status for the result.',
|
|
573
579
|
].join('\n');
|
|
574
580
|
return {
|
|
575
581
|
output: formatJsonOrText(runtime.cli)(result, text),
|