@pellux/goodvibes-agent 0.1.114 → 0.1.117
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 +41 -650
- package/README.md +40 -50
- package/dist/package/main.js +38224 -35673
- package/docs/README.md +5 -4
- package/docs/{connected-services.md → connected-host.md} +18 -10
- package/docs/getting-started.md +43 -42
- package/docs/release-and-publishing.md +6 -4
- package/package.json +3 -3
- package/src/agent/behavior-discovery-summary.ts +165 -0
- package/src/agent/persona-discovery.ts +1 -3
- package/src/agent/persona-registry.ts +5 -3
- package/src/agent/reminder-schedule-format.ts +6 -6
- package/src/agent/reminder-schedule.ts +1 -1
- package/src/agent/routine-registry.ts +43 -4
- package/src/agent/routine-schedule-format.ts +6 -6
- package/src/agent/routine-schedule-promotion.ts +1 -1
- package/src/agent/routine-schedule-receipts.ts +1 -1
- package/src/agent/runtime-profile-starters.ts +331 -0
- package/src/agent/runtime-profile.ts +228 -330
- package/src/agent/skill-registry.ts +190 -13
- package/src/cli/agent-knowledge-command.ts +2 -2
- package/src/cli/agent-knowledge-format.ts +2 -2
- package/src/cli/agent-knowledge-runtime.ts +1 -1
- package/src/cli/bundle-command.ts +1 -1
- package/src/cli/entrypoint.ts +16 -7
- package/src/cli/help.ts +26 -19
- package/src/cli/local-library-command.ts +75 -21
- package/src/cli/management-commands.ts +7 -6
- package/src/cli/management.ts +6 -51
- package/src/cli/parser.ts +23 -2
- package/src/cli/profiles-command.ts +247 -1
- package/src/cli/routines-command.ts +139 -11
- package/src/cli/service-posture.ts +11 -11
- package/src/cli/status.ts +21 -25
- 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 +2 -1
- package/src/input/agent-workspace-basic-command-editor-submission.ts +787 -0
- package/src/input/agent-workspace-basic-command-editors.ts +524 -459
- package/src/input/agent-workspace-categories.ts +177 -14
- package/src/input/agent-workspace-channel-command-editor-submission.ts +49 -0
- package/src/input/agent-workspace-channel-command-editors.ts +47 -0
- package/src/input/agent-workspace-command-editor.ts +25 -1
- package/src/input/agent-workspace-delegation-editor-submission.ts +55 -0
- package/src/input/agent-workspace-editors.ts +27 -0
- 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-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-mcp-command-editor-submission.ts +95 -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-navigation.ts +45 -0
- package/src/input/agent-workspace-notify-editor-submission.ts +107 -0
- package/src/input/agent-workspace-operations-command-editor-submission.ts +146 -0
- package/src/input/agent-workspace-operations-command-editors.ts +119 -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-requirements.ts +11 -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 +49 -19
- 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 +56 -8
- 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-types.ts +124 -0
- package/src/input/agent-workspace-voice-media.ts +1 -1
- package/src/input/agent-workspace-workplan-editor-submission.ts +153 -0
- package/src/input/agent-workspace.ts +47 -37
- package/src/input/command-registry.ts +1 -1
- package/src/input/commands/agent-runtime-profile-runtime.ts +160 -6
- package/src/input/commands/agent-skills-runtime.ts +76 -12
- package/src/input/commands/agent-workspace-runtime.ts +4 -3
- package/src/input/commands/brief-runtime.ts +38 -9
- package/src/input/commands/channels-runtime.ts +3 -3
- package/src/input/commands/compat-runtime.ts +32 -0
- package/src/input/commands/health-runtime.ts +22 -24
- package/src/input/commands/knowledge.ts +26 -5
- package/src/input/commands/local-provider-runtime.ts +7 -3
- package/src/input/commands/local-setup-review.ts +4 -14
- package/src/input/commands/mcp-runtime.ts +8 -12
- package/src/input/commands/operator-runtime.ts +8 -67
- package/src/input/commands/personas-runtime.ts +1 -1
- package/src/input/commands/platform-access-runtime.ts +7 -70
- package/src/input/commands/policy-dispatch.ts +1 -1
- package/src/input/commands/policy.ts +1 -5
- package/src/input/commands/product-runtime.ts +2 -2
- package/src/input/commands/qrcode-runtime.ts +6 -4
- package/src/input/commands/routines-runtime.ts +44 -6
- package/src/input/commands/runtime-services.ts +1 -14
- package/src/input/commands/security-runtime.ts +3 -8
- package/src/input/commands/session-content.ts +1 -1
- package/src/input/commands/session-workflow.ts +5 -14
- package/src/input/commands/shell-core.ts +8 -2
- package/src/input/commands/subscription-runtime.ts +1 -6
- package/src/input/commands/support-bundle-runtime.ts +53 -0
- package/src/input/commands/tasks-runtime.ts +2 -2
- package/src/input/commands.ts +4 -0
- package/src/input/feed-context-factory.ts +0 -1
- package/src/input/handler-content-actions.ts +0 -25
- package/src/input/handler-feed.ts +0 -2
- package/src/input/handler-interactions.ts +2 -31
- package/src/input/handler-onboarding.ts +29 -12
- package/src/input/handler-shortcuts.ts +1 -3
- package/src/input/handler.ts +4 -7
- package/src/input/keybindings.ts +9 -9
- 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 +49 -0
- package/src/input/onboarding/onboarding-wizard-helpers.ts +1 -1
- package/src/input/onboarding/onboarding-wizard-operator-steps.ts +126 -11
- package/src/input/onboarding/onboarding-wizard-steps.ts +15 -8
- package/src/input/onboarding/onboarding-wizard-types.ts +1 -0
- package/src/input/onboarding/onboarding-wizard.ts +19 -2
- package/src/input/profile-picker-modal.ts +2 -2
- package/src/input/settings-modal-agent-policy.ts +6 -6
- package/src/input/settings-modal-subscriptions.ts +3 -3
- package/src/input/settings-modal.ts +14 -14
- package/src/main.ts +0 -2
- package/src/panels/automation-control-panel.ts +3 -3
- package/src/panels/builtin/agent.ts +1 -1
- package/src/panels/builtin/operations.ts +2 -2
- package/src/panels/builtin/session.ts +26 -18
- package/src/panels/builtin/shared.ts +6 -6
- package/src/panels/project-planning-panel.ts +1 -1
- package/src/panels/provider-health-domains.ts +3 -3
- package/src/panels/qr-panel.ts +44 -20
- package/src/panels/subscription-panel.ts +3 -3
- package/src/panels/system-messages-panel.ts +2 -2
- package/src/panels/tasks-panel.ts +4 -4
- package/src/planning/project-planning-coordinator.ts +2 -2
- package/src/renderer/agent-workspace.ts +52 -5
- package/src/renderer/block-actions.ts +1 -3
- package/src/renderer/help-overlay.ts +13 -8
- package/src/renderer/mcp-workspace.ts +32 -13
- package/src/renderer/model-picker-overlay.ts +0 -1
- package/src/renderer/model-workspace.ts +0 -2
- package/src/renderer/onboarding/onboarding-wizard.ts +20 -4
- package/src/renderer/panel-picker-overlay.ts +1 -1
- package/src/renderer/profile-picker-modal.ts +2 -2
- package/src/renderer/settings-modal-helpers.ts +23 -23
- package/src/renderer/settings-modal.ts +18 -18
- package/src/renderer/system-message.ts +1 -1
- package/src/runtime/agent-runtime-events.ts +129 -0
- package/src/runtime/bootstrap-command-parts.ts +3 -2
- package/src/runtime/bootstrap-core.ts +4 -64
- package/src/runtime/bootstrap-shell.ts +42 -4
- package/src/runtime/bootstrap.ts +8 -27
- package/src/runtime/connected-host-auth.ts +42 -0
- package/src/runtime/diagnostics/panels/panel-resources.ts +1 -1
- package/src/runtime/index.ts +1 -5
- package/src/runtime/onboarding/apply.ts +213 -0
- package/src/runtime/onboarding/derivation.ts +25 -6
- package/src/runtime/onboarding/snapshot.ts +4 -2
- package/src/runtime/onboarding/types.ts +28 -1
- package/src/runtime/onboarding/verify.ts +81 -2
- package/src/runtime/operator-token-cleanup.ts +2 -2
- package/src/runtime/services.ts +27 -12
- package/src/shell/service-settings-sync.ts +7 -7
- package/src/shell/ui-openers.ts +27 -38
- package/src/tools/agent-local-registry-tool.ts +10 -4
- package/src/tools/agent-tool-policy-guard.ts +5 -7
- package/src/version.ts +1 -1
- package/src/cli/package-verification.ts +0 -274
|
@@ -1,11 +1,31 @@
|
|
|
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
6
|
|
|
7
7
|
export type AgentSkillSource = 'user' | 'agent' | 'imported' | 'system';
|
|
8
8
|
export type AgentSkillReviewState = 'fresh' | 'reviewed' | 'stale';
|
|
9
|
+
export type AgentSkillRequirementKind = 'env' | 'command';
|
|
10
|
+
|
|
11
|
+
export interface AgentSkillRequirement {
|
|
12
|
+
readonly kind: AgentSkillRequirementKind;
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly description?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface AgentSkillReadiness {
|
|
18
|
+
readonly ready: boolean;
|
|
19
|
+
readonly met: readonly AgentSkillRequirement[];
|
|
20
|
+
readonly missing: readonly AgentSkillRequirement[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface AgentSkillBundleReadiness {
|
|
24
|
+
readonly ready: boolean;
|
|
25
|
+
readonly includedSkills: readonly AgentSkillRecord[];
|
|
26
|
+
readonly missingSkillIds: readonly string[];
|
|
27
|
+
readonly missingRequirements: readonly AgentSkillRequirement[];
|
|
28
|
+
}
|
|
9
29
|
|
|
10
30
|
export interface AgentSkillRecord {
|
|
11
31
|
readonly id: string;
|
|
@@ -14,6 +34,7 @@ export interface AgentSkillRecord {
|
|
|
14
34
|
readonly procedure: string;
|
|
15
35
|
readonly triggers: readonly string[];
|
|
16
36
|
readonly tags: readonly string[];
|
|
37
|
+
readonly requirements: readonly AgentSkillRequirement[];
|
|
17
38
|
readonly enabled: boolean;
|
|
18
39
|
readonly source: AgentSkillSource;
|
|
19
40
|
readonly provenance: string;
|
|
@@ -45,6 +66,7 @@ export interface AgentSkillCreateInput {
|
|
|
45
66
|
readonly procedure: string;
|
|
46
67
|
readonly triggers?: readonly string[];
|
|
47
68
|
readonly tags?: readonly string[];
|
|
69
|
+
readonly requirements?: readonly AgentSkillRequirement[];
|
|
48
70
|
readonly enabled?: boolean;
|
|
49
71
|
readonly source?: AgentSkillSource;
|
|
50
72
|
readonly provenance?: string;
|
|
@@ -65,6 +87,7 @@ export interface AgentSkillUpdateInput {
|
|
|
65
87
|
readonly procedure?: string;
|
|
66
88
|
readonly triggers?: readonly string[];
|
|
67
89
|
readonly tags?: readonly string[];
|
|
90
|
+
readonly requirements?: readonly AgentSkillRequirement[];
|
|
68
91
|
readonly provenance?: string;
|
|
69
92
|
}
|
|
70
93
|
|
|
@@ -105,6 +128,11 @@ function readStringArray(value: unknown): string[] {
|
|
|
105
128
|
return value.filter((entry): entry is string => typeof entry === 'string').map((entry) => entry.trim()).filter(Boolean);
|
|
106
129
|
}
|
|
107
130
|
|
|
131
|
+
function readRequirementKind(value: unknown): AgentSkillRequirementKind | null {
|
|
132
|
+
if (value === 'env' || value === 'command') return value;
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
108
136
|
function normalizeName(name: string): string {
|
|
109
137
|
return name.trim().replace(/\s+/g, ' ');
|
|
110
138
|
}
|
|
@@ -123,6 +151,48 @@ function normalizeList(values: readonly string[] | undefined): string[] {
|
|
|
123
151
|
return result;
|
|
124
152
|
}
|
|
125
153
|
|
|
154
|
+
function validateRequirementName(requirement: AgentSkillRequirement): void {
|
|
155
|
+
if (requirement.kind === 'env' && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(requirement.name)) {
|
|
156
|
+
throw new Error(`Invalid skill env requirement: ${requirement.name}`);
|
|
157
|
+
}
|
|
158
|
+
if (requirement.kind === 'command' && !/^[A-Za-z0-9._+-]+$/.test(requirement.name)) {
|
|
159
|
+
throw new Error(`Invalid skill command requirement: ${requirement.name}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function normalizeRequirements(values: readonly AgentSkillRequirement[] | undefined): AgentSkillRequirement[] {
|
|
164
|
+
const seen = new Set<string>();
|
|
165
|
+
const result: AgentSkillRequirement[] = [];
|
|
166
|
+
for (const value of values ?? []) {
|
|
167
|
+
const kind = readRequirementKind(value.kind);
|
|
168
|
+
const name = typeof value.name === 'string' ? value.name.trim() : '';
|
|
169
|
+
if (!kind || !name) continue;
|
|
170
|
+
const description = typeof value.description === 'string' ? value.description.trim() : '';
|
|
171
|
+
const requirement: AgentSkillRequirement = {
|
|
172
|
+
kind,
|
|
173
|
+
name,
|
|
174
|
+
...(description ? { description } : {}),
|
|
175
|
+
};
|
|
176
|
+
validateRequirementName(requirement);
|
|
177
|
+
const key = `${requirement.kind}:${requirement.name.toLowerCase()}`;
|
|
178
|
+
if (seen.has(key)) continue;
|
|
179
|
+
seen.add(key);
|
|
180
|
+
result.push(requirement);
|
|
181
|
+
}
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function parseRequirements(value: unknown): AgentSkillRequirement[] {
|
|
186
|
+
if (!Array.isArray(value)) return [];
|
|
187
|
+
return normalizeRequirements(value
|
|
188
|
+
.filter(isRecord)
|
|
189
|
+
.map((entry) => ({
|
|
190
|
+
kind: readRequirementKind(entry.kind) ?? 'env',
|
|
191
|
+
name: readString(entry.name).trim(),
|
|
192
|
+
description: readString(entry.description).trim(),
|
|
193
|
+
})));
|
|
194
|
+
}
|
|
195
|
+
|
|
126
196
|
function slugify(value: string): string {
|
|
127
197
|
const slug = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
128
198
|
return slug || 'skill';
|
|
@@ -132,6 +202,23 @@ function nowIso(): string {
|
|
|
132
202
|
return new Date().toISOString();
|
|
133
203
|
}
|
|
134
204
|
|
|
205
|
+
function canExecute(path: string): boolean {
|
|
206
|
+
try {
|
|
207
|
+
accessSync(path, constants.X_OK);
|
|
208
|
+
return true;
|
|
209
|
+
} catch {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function commandExists(command: string, pathValue: string | undefined): boolean {
|
|
215
|
+
if (!pathValue) return false;
|
|
216
|
+
return pathValue
|
|
217
|
+
.split(delimiter)
|
|
218
|
+
.filter(Boolean)
|
|
219
|
+
.some((pathEntry) => canExecute(join(pathEntry, command)));
|
|
220
|
+
}
|
|
221
|
+
|
|
135
222
|
function parseSkill(value: unknown): AgentSkillRecord | null {
|
|
136
223
|
if (!isRecord(value)) return null;
|
|
137
224
|
const id = readString(value.id).trim();
|
|
@@ -150,6 +237,7 @@ function parseSkill(value: unknown): AgentSkillRecord | null {
|
|
|
150
237
|
procedure,
|
|
151
238
|
triggers: readStringArray(value.triggers),
|
|
152
239
|
tags: readStringArray(value.tags),
|
|
240
|
+
requirements: parseRequirements(value.requirements),
|
|
153
241
|
enabled: value.enabled === true,
|
|
154
242
|
source,
|
|
155
243
|
provenance: readString(value.provenance, source).trim() || source,
|
|
@@ -206,14 +294,16 @@ function formatStore(store: SkillStoreFile): string {
|
|
|
206
294
|
return `${JSON.stringify(store, null, 2)}\n`;
|
|
207
295
|
}
|
|
208
296
|
|
|
209
|
-
|
|
297
|
+
type AgentLocalStorePaths = Pick<ShellPathService, 'resolveUserPath'>;
|
|
298
|
+
|
|
299
|
+
export function skillStorePath(shellPaths: AgentLocalStorePaths): string {
|
|
210
300
|
return shellPaths.resolveUserPath(GOODVIBES_AGENT_SURFACE_ROOT, 'skills', 'skills.json');
|
|
211
301
|
}
|
|
212
302
|
|
|
213
303
|
export class AgentSkillRegistry {
|
|
214
304
|
public constructor(private readonly storePath: string) {}
|
|
215
305
|
|
|
216
|
-
public static fromShellPaths(shellPaths:
|
|
306
|
+
public static fromShellPaths(shellPaths: AgentLocalStorePaths): AgentSkillRegistry {
|
|
217
307
|
return new AgentSkillRegistry(skillStorePath(shellPaths));
|
|
218
308
|
}
|
|
219
309
|
|
|
@@ -284,7 +374,8 @@ export class AgentSkillRegistry {
|
|
|
284
374
|
const description = input.description.trim();
|
|
285
375
|
const procedure = input.procedure.trim();
|
|
286
376
|
this.validateRequired(name, description, procedure);
|
|
287
|
-
|
|
377
|
+
const requirements = normalizeRequirements(input.requirements);
|
|
378
|
+
assertNoSecretLikeText([name, description, procedure, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])]);
|
|
288
379
|
const duplicate = store.skills.find((skill) => skill.name.toLowerCase() === name.toLowerCase());
|
|
289
380
|
if (duplicate) throw new Error(`Skill already exists: ${duplicate.id}`);
|
|
290
381
|
const timestamp = nowIso();
|
|
@@ -295,6 +386,7 @@ export class AgentSkillRegistry {
|
|
|
295
386
|
procedure,
|
|
296
387
|
triggers: normalizeList(input.triggers),
|
|
297
388
|
tags: normalizeList(input.tags),
|
|
389
|
+
requirements,
|
|
298
390
|
enabled: input.enabled === true,
|
|
299
391
|
source: input.source ?? 'user',
|
|
300
392
|
provenance: input.provenance?.trim() || input.source || 'user',
|
|
@@ -340,7 +432,8 @@ export class AgentSkillRegistry {
|
|
|
340
432
|
const description = input.description === undefined ? existing.description : input.description.trim();
|
|
341
433
|
const procedure = input.procedure === undefined ? existing.procedure : input.procedure.trim();
|
|
342
434
|
this.validateRequired(name, description, procedure);
|
|
343
|
-
|
|
435
|
+
const requirements = input.requirements === undefined ? existing.requirements : normalizeRequirements(input.requirements);
|
|
436
|
+
assertNoSecretLikeText([name, description, procedure, ...(input.tags ?? []), ...(input.triggers ?? []), ...requirements.flatMap((requirement) => [requirement.name, requirement.description ?? ''])]);
|
|
344
437
|
const duplicate = store.skills.find((skill) => skill.id !== existing.id && skill.name.toLowerCase() === name.toLowerCase());
|
|
345
438
|
if (duplicate) throw new Error(`Skill already exists: ${duplicate.id}`);
|
|
346
439
|
const updated: AgentSkillRecord = {
|
|
@@ -350,6 +443,7 @@ export class AgentSkillRegistry {
|
|
|
350
443
|
procedure,
|
|
351
444
|
triggers: input.triggers === undefined ? existing.triggers : normalizeList(input.triggers),
|
|
352
445
|
tags: input.tags === undefined ? existing.tags : normalizeList(input.tags),
|
|
446
|
+
requirements,
|
|
353
447
|
provenance: input.provenance === undefined ? existing.provenance : input.provenance.trim() || existing.provenance,
|
|
354
448
|
reviewState: 'fresh',
|
|
355
449
|
staleReason: undefined,
|
|
@@ -583,6 +677,80 @@ export class AgentSkillRegistry {
|
|
|
583
677
|
}
|
|
584
678
|
}
|
|
585
679
|
|
|
680
|
+
export function buildAgentSkillRequirements(input: {
|
|
681
|
+
readonly env?: readonly string[];
|
|
682
|
+
readonly commands?: readonly string[];
|
|
683
|
+
}): readonly AgentSkillRequirement[] {
|
|
684
|
+
return normalizeRequirements([
|
|
685
|
+
...(input.env ?? []).map((name) => ({ kind: 'env' as const, name })),
|
|
686
|
+
...(input.commands ?? []).map((name) => ({ kind: 'command' as const, name })),
|
|
687
|
+
]);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
export function normalizeAgentSkillRequirements(values: readonly AgentSkillRequirement[] | undefined): readonly AgentSkillRequirement[] {
|
|
691
|
+
return normalizeRequirements(values);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
export function formatAgentSkillRequirement(requirement: AgentSkillRequirement): string {
|
|
695
|
+
return `${requirement.kind}:${requirement.name}`;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
export function evaluateAgentSkillReadiness(
|
|
699
|
+
skill: Pick<AgentSkillRecord, 'requirements'>,
|
|
700
|
+
options: {
|
|
701
|
+
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
702
|
+
readonly pathValue?: string;
|
|
703
|
+
} = {},
|
|
704
|
+
): AgentSkillReadiness {
|
|
705
|
+
const env = options.env ?? process.env;
|
|
706
|
+
const pathValue = options.pathValue ?? env.PATH;
|
|
707
|
+
const met: AgentSkillRequirement[] = [];
|
|
708
|
+
const missing: AgentSkillRequirement[] = [];
|
|
709
|
+
for (const requirement of skill.requirements) {
|
|
710
|
+
const present = requirement.kind === 'env'
|
|
711
|
+
? typeof env[requirement.name] === 'string' && (env[requirement.name] ?? '').length > 0
|
|
712
|
+
: commandExists(requirement.name, pathValue);
|
|
713
|
+
if (present) met.push(requirement);
|
|
714
|
+
else missing.push(requirement);
|
|
715
|
+
}
|
|
716
|
+
return { ready: missing.length === 0, met, missing };
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
export function evaluateAgentSkillBundleReadiness(
|
|
720
|
+
bundle: Pick<AgentSkillBundleRecord, 'skillIds'>,
|
|
721
|
+
skills: readonly AgentSkillRecord[],
|
|
722
|
+
options: {
|
|
723
|
+
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
724
|
+
readonly pathValue?: string;
|
|
725
|
+
} = {},
|
|
726
|
+
): AgentSkillBundleReadiness {
|
|
727
|
+
const skillById = new Map(skills.map((skill) => [skill.id, skill]));
|
|
728
|
+
const includedSkills: AgentSkillRecord[] = [];
|
|
729
|
+
const missingSkillIds: string[] = [];
|
|
730
|
+
const missingRequirements: AgentSkillRequirement[] = [];
|
|
731
|
+
const seenRequirements = new Set<string>();
|
|
732
|
+
for (const skillId of bundle.skillIds) {
|
|
733
|
+
const skill = skillById.get(skillId);
|
|
734
|
+
if (!skill) {
|
|
735
|
+
missingSkillIds.push(skillId);
|
|
736
|
+
continue;
|
|
737
|
+
}
|
|
738
|
+
includedSkills.push(skill);
|
|
739
|
+
for (const requirement of evaluateAgentSkillReadiness(skill, options).missing) {
|
|
740
|
+
const key = `${requirement.kind}:${requirement.name.toLowerCase()}`;
|
|
741
|
+
if (seenRequirements.has(key)) continue;
|
|
742
|
+
seenRequirements.add(key);
|
|
743
|
+
missingRequirements.push(requirement);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
return {
|
|
747
|
+
ready: missingSkillIds.length === 0 && missingRequirements.length === 0,
|
|
748
|
+
includedSkills,
|
|
749
|
+
missingSkillIds,
|
|
750
|
+
missingRequirements,
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
|
|
586
754
|
export function buildEnabledSkillsPrompt(shellPaths: ShellPathService): string | null {
|
|
587
755
|
const snapshot = AgentSkillRegistry.fromShellPaths(shellPaths).snapshot();
|
|
588
756
|
const active = snapshot.activeSkills;
|
|
@@ -591,18 +759,27 @@ export function buildEnabledSkillsPrompt(shellPaths: ShellPathService): string |
|
|
|
591
759
|
'## Enabled GoodVibes Agent Skills',
|
|
592
760
|
'Use these local reusable procedures inside the same serial assistant conversation when they fit the user request.',
|
|
593
761
|
'',
|
|
594
|
-
...snapshot.enabledBundles.slice(0, 4).flatMap((bundle) =>
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
762
|
+
...snapshot.enabledBundles.slice(0, 4).flatMap((bundle) => {
|
|
763
|
+
const readiness = evaluateAgentSkillBundleReadiness(bundle, snapshot.skills);
|
|
764
|
+
const missing = [
|
|
765
|
+
...readiness.missingRequirements.map(formatAgentSkillRequirement),
|
|
766
|
+
...readiness.missingSkillIds.map((skillId) => `skill:${skillId}`),
|
|
767
|
+
];
|
|
768
|
+
return [
|
|
769
|
+
`### Skill Bundle: ${bundle.name}`,
|
|
770
|
+
`Description: ${bundle.description}`,
|
|
771
|
+
`Review state: ${bundle.reviewState}`,
|
|
772
|
+
`Readiness: ${readiness.ready ? 'ready' : `missing ${missing.join(', ')}`}`,
|
|
773
|
+
`Included skills: ${bundle.skillIds.join(', ')}`,
|
|
774
|
+
'',
|
|
775
|
+
];
|
|
776
|
+
}),
|
|
601
777
|
...active.slice(0, 8).flatMap((skill) => [
|
|
602
778
|
`### ${skill.name}`,
|
|
603
779
|
`Description: ${skill.description}`,
|
|
604
780
|
`Review state: ${skill.reviewState}`,
|
|
605
781
|
`Triggers: ${skill.triggers.join(', ') || '(manual)'}`,
|
|
782
|
+
`Readiness: ${evaluateAgentSkillReadiness(skill).ready ? 'ready' : `missing ${evaluateAgentSkillReadiness(skill).missing.map(formatAgentSkillRequirement).join(', ')}`}`,
|
|
606
783
|
skill.procedure,
|
|
607
784
|
'',
|
|
608
785
|
]),
|
|
@@ -493,7 +493,7 @@ export async function handleCompatCommand(runtime: CliCommandRuntime): Promise<C
|
|
|
493
493
|
ok: versionCompatible && knowledgeRouteReady,
|
|
494
494
|
packageVersion: metadata.version,
|
|
495
495
|
sdkPin: metadata.sdkVersion,
|
|
496
|
-
|
|
496
|
+
connectedHost: {
|
|
497
497
|
baseUrl: connection.baseUrl,
|
|
498
498
|
status: daemon.status,
|
|
499
499
|
version: daemonVersion,
|
|
@@ -518,7 +518,7 @@ export async function handleCompatCommand(runtime: CliCommandRuntime): Promise<C
|
|
|
518
518
|
` version compatible: ${yesNo(versionCompatible)}`,
|
|
519
519
|
` operator token: ${connection.token ? 'present' : 'missing'} (${connection.tokenPath})`,
|
|
520
520
|
` Agent knowledge route: ${knowledgeRouteReady ? 'ready' : `not ready (${knowledgeRoute.ok ? 'unknown' : knowledgeRoute.kind})`}`,
|
|
521
|
-
...(versionCompatible ? [] : [' next: update connected GoodVibes
|
|
521
|
+
...(versionCompatible ? [] : [' next: update the connected GoodVibes host so /status matches the Agent SDK pin.']),
|
|
522
522
|
].join('\n');
|
|
523
523
|
return {
|
|
524
524
|
output: runtime.cli.flags.outputFormat === 'json' ? JSON.stringify(value, null, 2) : text,
|
|
@@ -367,10 +367,10 @@ export function formatFailure(failure: AgentKnowledgeFailureLike, json: boolean)
|
|
|
367
367
|
? ` versions: runtime=${failure.daemonVersion} expected=${failure.expectedSdkVersion}`
|
|
368
368
|
: null,
|
|
369
369
|
failure.kind === 'version_mismatch'
|
|
370
|
-
? ' next: update connected GoodVibes
|
|
370
|
+
? ' next: update the connected GoodVibes host so /status matches the Agent SDK pin.'
|
|
371
371
|
: null,
|
|
372
372
|
failure.kind === 'daemon_route_unavailable'
|
|
373
|
-
? ' next: update connected GoodVibes
|
|
373
|
+
? ' next: update the connected GoodVibes host to the SDK version required by this Agent package.'
|
|
374
374
|
: null,
|
|
375
375
|
].filter((line): line is string => Boolean(line)).join('\n');
|
|
376
376
|
}
|
|
@@ -94,7 +94,7 @@ export async function classifyKnowledgeError(error: unknown, connection: AgentDa
|
|
|
94
94
|
return {
|
|
95
95
|
ok: false,
|
|
96
96
|
kind: 'version_mismatch',
|
|
97
|
-
error: `Connected GoodVibes
|
|
97
|
+
error: `Connected GoodVibes host SDK version ${daemonVersion} does not match Agent SDK pin ${metadata.sdkVersion}; Agent Knowledge route is unavailable.`,
|
|
98
98
|
baseUrl: connection.baseUrl,
|
|
99
99
|
route,
|
|
100
100
|
daemonVersion,
|
|
@@ -132,7 +132,7 @@ export async function handleBundleCommand(runtime: CliCommandRuntime): Promise<C
|
|
|
132
132
|
const summary = inspectBundle(sourcePath, parsed.value);
|
|
133
133
|
return {
|
|
134
134
|
output: formatJsonOrText(runtime, summary, [
|
|
135
|
-
'GoodVibes bundle',
|
|
135
|
+
'GoodVibes Agent support bundle',
|
|
136
136
|
` type: ${summary.type}`,
|
|
137
137
|
` version: ${summary.version}`,
|
|
138
138
|
` path: ${summary.path}`,
|
package/src/cli/entrypoint.ts
CHANGED
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
import { buildCliServicePosture } from './service-posture.ts';
|
|
26
26
|
import { inspectCliExternalRuntime } from './external-runtime.ts';
|
|
27
27
|
import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
|
|
28
|
-
import { resolveAgentRuntimeProfileHome } from '../agent/runtime-profile.ts';
|
|
28
|
+
import { resolveAgentRuntimeProfileHome, resolveSelectedAgentRuntimeProfileHome } from '../agent/runtime-profile.ts';
|
|
29
29
|
|
|
30
30
|
type ShellEntrypointOwnership = {
|
|
31
31
|
readonly workingDirectory: string;
|
|
@@ -52,14 +52,20 @@ export type PreparedShellCliRuntime = {
|
|
|
52
52
|
readonly bootstrapHomeDirectory: string;
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
-
function resolveShellEntrypointOwnership(
|
|
55
|
+
export function resolveShellEntrypointOwnership(
|
|
56
56
|
roots: ShellEntrypointRoots,
|
|
57
57
|
workingDirOverride?: string,
|
|
58
|
-
|
|
58
|
+
options: {
|
|
59
|
+
readonly agentProfile?: string;
|
|
60
|
+
readonly useSelectedProfile: boolean;
|
|
61
|
+
} = { useSelectedProfile: true },
|
|
59
62
|
): ShellEntrypointOwnership {
|
|
60
|
-
const
|
|
61
|
-
? resolveAgentRuntimeProfileHome(roots.homeDirectory, agentProfile)
|
|
62
|
-
:
|
|
63
|
+
const selectedProfile = options.agentProfile
|
|
64
|
+
? resolveAgentRuntimeProfileHome(roots.homeDirectory, options.agentProfile)
|
|
65
|
+
: options.useSelectedProfile
|
|
66
|
+
? resolveSelectedAgentRuntimeProfileHome(roots.homeDirectory)
|
|
67
|
+
: null;
|
|
68
|
+
const homeDirectory = selectedProfile?.homeDirectory ?? roots.homeDirectory;
|
|
63
69
|
return {
|
|
64
70
|
workingDirectory: workingDirOverride ?? roots.defaultWorkingDirectory,
|
|
65
71
|
homeDirectory,
|
|
@@ -103,7 +109,10 @@ export async function prepareShellCliRuntime(
|
|
|
103
109
|
ownership = resolveShellEntrypointOwnership(
|
|
104
110
|
roots,
|
|
105
111
|
cli.flags.workingDir ?? (cli.command === 'tui' ? cli.commandArgs[0] : undefined),
|
|
106
|
-
|
|
112
|
+
{
|
|
113
|
+
agentProfile: cli.command === 'profiles' ? undefined : cli.flags.agentProfile,
|
|
114
|
+
useSelectedProfile: cli.command !== 'profiles',
|
|
115
|
+
},
|
|
107
116
|
);
|
|
108
117
|
} catch (error) {
|
|
109
118
|
console.error(error instanceof Error ? error.message : String(error));
|
package/src/cli/help.ts
CHANGED
|
@@ -37,12 +37,12 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
|
|
|
37
37
|
' /setup Open setup/config flows',
|
|
38
38
|
' /model Choose provider and model',
|
|
39
39
|
' /knowledge Use isolated Agent Knowledge',
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
' /personas, /skills Tune local Agent behavior',
|
|
41
|
+
' /routines Run local routines in the main conversation',
|
|
42
|
+
' /schedule remind Create confirmed reminders or inspect schedules',
|
|
43
|
+
' /delegate Explicitly hand build/fix/review work to GoodVibes TUI',
|
|
44
44
|
'',
|
|
45
|
-
'
|
|
45
|
+
'Scriptable mirrors:',
|
|
46
46
|
' run|exec [prompt] Run non-interactively with text/json/stream-json output',
|
|
47
47
|
' status Print config, provider, auth, and setup posture',
|
|
48
48
|
' doctor Print status plus setup warnings',
|
|
@@ -55,7 +55,7 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
|
|
|
55
55
|
' memory Manage Agent-owned durable memory records',
|
|
56
56
|
' routines Inspect local routines and explicitly promote one to a connected schedule',
|
|
57
57
|
' auth Inspect Agent auth posture and connection token state',
|
|
58
|
-
' compat Inspect Agent SDK pin, connected
|
|
58
|
+
' compat Inspect Agent SDK pin, connected host version, and Agent Knowledge route readiness',
|
|
59
59
|
' knowledge Use isolated Agent Knowledge/Wiki routes',
|
|
60
60
|
' ask|search Shortcuts for isolated Agent Knowledge ask/search',
|
|
61
61
|
' delegate Explicitly delegate build/fix/review work to GoodVibes TUI',
|
|
@@ -106,7 +106,7 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
|
|
|
106
106
|
` ${binary} profiles create household --template household --yes`,
|
|
107
107
|
` ${binary} --agent-profile household`,
|
|
108
108
|
` ${binary} personas create --name "Travel Planner" --description "Plan trips" --body "Compare options before booking"`,
|
|
109
|
-
` ${binary} skills create --name "Daily Brief" --description "Summarize operator state" --procedure "Review Agent Knowledge, work plans, approvals, and routines" --enabled`,
|
|
109
|
+
` ${binary} skills create --name "Daily Brief" --description "Summarize operator state" --procedure "Review Agent Knowledge, work plans, approvals, and routines" --requires-env GOODVIBES_AGENT_TOKEN --requires-command gh --enabled`,
|
|
110
110
|
` ${binary} memory add fact "Prefers concise morning briefings" --scope project --tags preference`,
|
|
111
111
|
` ${binary} memory search "morning briefings"`,
|
|
112
112
|
` ${binary} routines promote daily-operations-sweep --cron "0 9 * * *" --timezone America/Chicago --yes`,
|
|
@@ -138,12 +138,12 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
|
|
|
138
138
|
},
|
|
139
139
|
setup: {
|
|
140
140
|
usage: ['setup', 'setup status', 'onboarding', 'onboarding status'],
|
|
141
|
-
summary: 'Open Agent setup, or inspect whether setup has already been
|
|
141
|
+
summary: 'Open Agent setup, or inspect whether setup has already been applied for this user.',
|
|
142
142
|
examples: ['setup', 'setup status', 'onboarding status'],
|
|
143
143
|
},
|
|
144
144
|
status: {
|
|
145
145
|
usage: ['status', 'status --json', '--runtime-url http://127.0.0.1:3421 status'],
|
|
146
|
-
summary: 'Print Agent config, provider, auth, connected-
|
|
146
|
+
summary: 'Print Agent config, provider, auth, connected-host state, and setup posture.',
|
|
147
147
|
examples: ['status', 'status --json', '--runtime-url http://127.0.0.1:3421 status'],
|
|
148
148
|
},
|
|
149
149
|
doctor: {
|
|
@@ -157,9 +157,9 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
|
|
|
157
157
|
examples: ['providers', 'providers inspect openai-subscriber', 'providers use openai openai:gpt-5.4'],
|
|
158
158
|
},
|
|
159
159
|
profiles: {
|
|
160
|
-
usage: ['profiles list', 'profiles templates', 'profiles templates export <id> <path> --yes', 'profiles templates import <path> --yes', 'profiles show <name>', 'profiles create <name> [--template <id>] --yes', 'profiles delete <name> --yes', '--agent-profile <name>'],
|
|
161
|
-
summary: 'Create and inspect isolated Agent profile homes, with starter templates for household, research, travel, operations, personal productivity, and local imported starters. A profile changes Agent-local config, sessions, memory, personas, skills, routines, and setup paths without changing connected GoodVibes
|
|
162
|
-
examples: ['profiles templates', 'profiles
|
|
160
|
+
usage: ['profiles list', 'profiles default', 'profiles use <name> --yes', 'profiles default clear --yes', 'profiles templates', 'profiles templates from-discovered <id> --yes', 'profiles create-from-discovered <name> --yes', 'profiles templates export <id> <path> --yes', 'profiles templates import <path> --yes', 'profiles show <name>', 'profiles create <name> [--template <id>] --yes', 'profiles delete <name> --yes', '--agent-profile <name>'],
|
|
161
|
+
summary: 'Create and inspect isolated Agent profile homes, with starter templates for household, research, travel, operations, personal productivity, and local imported starters. A profile changes Agent-local config, sessions, memory, personas, skills, routines, and setup paths without changing the connected GoodVibes host.',
|
|
162
|
+
examples: ['profiles templates', 'profiles create household --template household --yes', 'profiles use household --yes', 'profiles default', 'profiles create-from-discovered research-desk --yes', 'profiles templates from-discovered research-desk --yes', 'profiles templates export research ./research-starter.json --yes', 'profiles templates import ./research-starter.json --yes', '--agent-profile household status'],
|
|
163
163
|
},
|
|
164
164
|
personas: {
|
|
165
165
|
usage: [
|
|
@@ -192,25 +192,28 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
|
|
|
192
192
|
'skills list',
|
|
193
193
|
'skills enabled',
|
|
194
194
|
'skills active',
|
|
195
|
+
'skills attention',
|
|
195
196
|
'skills discover',
|
|
196
197
|
'skills import-discovered <name> [--enabled] --yes',
|
|
197
198
|
'skills search <query>',
|
|
198
199
|
'skills show <id>',
|
|
199
|
-
'skills create --name <name> --description <summary> --procedure <steps> [--tags a,b] [--triggers a,b] [--enabled]',
|
|
200
|
-
'skills update <id> [--name <name>] [--description <summary>] [--procedure <steps>] [--tags a,b] [--triggers a,b]',
|
|
200
|
+
'skills create --name <name> --description <summary> --procedure <steps> [--tags a,b] [--triggers a,b] [--requires-env A,B] [--requires-command gh,jq] [--enabled]',
|
|
201
|
+
'skills update <id> [--name <name>] [--description <summary>] [--procedure <steps>] [--tags a,b] [--triggers a,b] [--requires-env A,B] [--requires-command gh,jq]',
|
|
201
202
|
'skills enable <id>',
|
|
202
203
|
'skills disable <id>',
|
|
203
204
|
'skills review <id>',
|
|
204
205
|
'skills stale <id> <reason>',
|
|
205
206
|
'skills delete <id> --yes',
|
|
206
|
-
'skills bundle [list|enabled|show|create|update|enable|disable|review|stale|delete]',
|
|
207
|
+
'skills bundle [list|enabled|attention|show|create|update|enable|disable|review|stale|delete]',
|
|
207
208
|
],
|
|
208
209
|
summary: 'Manage reusable Agent-local procedures and skill bundles for the main conversation.',
|
|
209
210
|
examples: [
|
|
210
211
|
'skills list',
|
|
212
|
+
'skills attention',
|
|
213
|
+
'skills bundle attention',
|
|
211
214
|
'skills discover',
|
|
212
215
|
'skills import-discovered "Daily Brief" --enabled --yes',
|
|
213
|
-
'skills create --name "Daily Brief" --description "Summarize operator state" --procedure "Review Agent Knowledge, work plans, approvals, and routines" --enabled',
|
|
216
|
+
'skills create --name "Daily Brief" --description "Summarize operator state" --procedure "Review Agent Knowledge, work plans, approvals, and routines" --requires-env GOODVIBES_AGENT_TOKEN --requires-command gh --enabled',
|
|
214
217
|
'skills bundle create --name "Ops Pack" --description "Daily operations bundle" --skills daily-brief --enabled',
|
|
215
218
|
'skills delete daily-brief --yes',
|
|
216
219
|
],
|
|
@@ -247,20 +250,24 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
|
|
|
247
250
|
usage: [
|
|
248
251
|
'routines list',
|
|
249
252
|
'routines enabled',
|
|
253
|
+
'routines attention',
|
|
250
254
|
'routines discover',
|
|
251
255
|
'routines import-discovered <name> [--enabled] --yes',
|
|
252
256
|
'routines show <id>',
|
|
257
|
+
'routines create --name <name> --description <summary> --steps <steps> [--tags a,b] [--triggers a,b] [--requires-env A,B] [--requires-command gh,jq] [--enabled]',
|
|
253
258
|
'routines receipts',
|
|
254
259
|
'routines reconcile',
|
|
255
260
|
'routines receipt <receipt-id>',
|
|
256
261
|
'routines promote <id> (--cron <expr>|--every <interval>|--at <iso-time>) [--timezone <tz>] [--name <schedule-name>] [--provider <id>] [--model <model>] [--delivery-channel <channel[:route[:label]]>|--delivery-route <route[:label]>|--delivery-webhook <url>|--delivery-link <url>] [--disabled] --yes',
|
|
257
262
|
],
|
|
258
|
-
summary: 'Inspect and import Agent-local routines
|
|
263
|
+
summary: 'Inspect, create, and import Agent-local routines with setup readiness. The same routine workflow is available in the TUI through /routines and the Agent workspace. GoodVibes schedule promotion is explicit. Without --yes, promotion only prints the preview.',
|
|
259
264
|
examples: [
|
|
260
265
|
'routines list',
|
|
266
|
+
'routines attention',
|
|
261
267
|
'routines discover',
|
|
262
268
|
'routines import-discovered "Daily Brief" --enabled --yes',
|
|
263
269
|
'routines show daily-operations-sweep',
|
|
270
|
+
'routines create --name "Daily Sweep" --description "Review operator state" --steps "Check tasks, approvals, channels, and Agent Knowledge" --requires-env GOODVIBES_AGENT_TOKEN',
|
|
264
271
|
'routines receipts',
|
|
265
272
|
'routines reconcile',
|
|
266
273
|
'routines promote daily-operations-sweep --cron "0 9 * * *" --timezone America/Chicago --delivery-channel slack --yes',
|
|
@@ -348,7 +355,7 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
|
|
|
348
355
|
},
|
|
349
356
|
tasks: {
|
|
350
357
|
usage: ['tasks list', 'tasks show <taskId>'],
|
|
351
|
-
summary: 'Inspect connected-
|
|
358
|
+
summary: 'Inspect connected-host task summaries. Agent blocks host-owned task submission; use run for one-shot work or delegate for explicit build/fix/review handoff.',
|
|
352
359
|
examples: ['tasks list', 'tasks show task-123', 'run "check provider readiness"', 'delegate "fix the failing tests"'],
|
|
353
360
|
},
|
|
354
361
|
bundle: {
|
|
@@ -402,7 +409,7 @@ export function renderGoodVibesCommandHelp(topic: string, binary = 'goodvibes-ag
|
|
|
402
409
|
].join('\n');
|
|
403
410
|
}
|
|
404
411
|
return [
|
|
405
|
-
`GoodVibes ${normalized}`,
|
|
412
|
+
`GoodVibes Agent ${normalized}`,
|
|
406
413
|
'',
|
|
407
414
|
help.summary,
|
|
408
415
|
'',
|