@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.
Files changed (168) hide show
  1. package/CHANGELOG.md +41 -650
  2. package/README.md +40 -50
  3. package/dist/package/main.js +38224 -35673
  4. package/docs/README.md +5 -4
  5. package/docs/{connected-services.md → connected-host.md} +18 -10
  6. package/docs/getting-started.md +43 -42
  7. package/docs/release-and-publishing.md +6 -4
  8. package/package.json +3 -3
  9. package/src/agent/behavior-discovery-summary.ts +165 -0
  10. package/src/agent/persona-discovery.ts +1 -3
  11. package/src/agent/persona-registry.ts +5 -3
  12. package/src/agent/reminder-schedule-format.ts +6 -6
  13. package/src/agent/reminder-schedule.ts +1 -1
  14. package/src/agent/routine-registry.ts +43 -4
  15. package/src/agent/routine-schedule-format.ts +6 -6
  16. package/src/agent/routine-schedule-promotion.ts +1 -1
  17. package/src/agent/routine-schedule-receipts.ts +1 -1
  18. package/src/agent/runtime-profile-starters.ts +331 -0
  19. package/src/agent/runtime-profile.ts +228 -330
  20. package/src/agent/skill-registry.ts +190 -13
  21. package/src/cli/agent-knowledge-command.ts +2 -2
  22. package/src/cli/agent-knowledge-format.ts +2 -2
  23. package/src/cli/agent-knowledge-runtime.ts +1 -1
  24. package/src/cli/bundle-command.ts +1 -1
  25. package/src/cli/entrypoint.ts +16 -7
  26. package/src/cli/help.ts +26 -19
  27. package/src/cli/local-library-command.ts +75 -21
  28. package/src/cli/management-commands.ts +7 -6
  29. package/src/cli/management.ts +6 -51
  30. package/src/cli/parser.ts +23 -2
  31. package/src/cli/profiles-command.ts +247 -1
  32. package/src/cli/routines-command.ts +139 -11
  33. package/src/cli/service-posture.ts +11 -11
  34. package/src/cli/status.ts +21 -25
  35. package/src/input/agent-workspace-access-command-editor-submission.ts +149 -0
  36. package/src/input/agent-workspace-access-command-editors.ts +170 -0
  37. package/src/input/agent-workspace-activation.ts +2 -1
  38. package/src/input/agent-workspace-basic-command-editor-submission.ts +787 -0
  39. package/src/input/agent-workspace-basic-command-editors.ts +524 -459
  40. package/src/input/agent-workspace-categories.ts +177 -14
  41. package/src/input/agent-workspace-channel-command-editor-submission.ts +49 -0
  42. package/src/input/agent-workspace-channel-command-editors.ts +47 -0
  43. package/src/input/agent-workspace-command-editor.ts +25 -1
  44. package/src/input/agent-workspace-delegation-editor-submission.ts +55 -0
  45. package/src/input/agent-workspace-editors.ts +27 -0
  46. package/src/input/agent-workspace-knowledge-command-editor-submission.ts +132 -0
  47. package/src/input/agent-workspace-knowledge-command-editors.ts +110 -0
  48. package/src/input/agent-workspace-learned-behavior.ts +67 -0
  49. package/src/input/agent-workspace-library-command-editor-submission.ts +50 -0
  50. package/src/input/agent-workspace-library-command-editors.ts +35 -0
  51. package/src/input/agent-workspace-mcp-command-editor-submission.ts +95 -0
  52. package/src/input/agent-workspace-memory-command-editor-submission.ts +232 -0
  53. package/src/input/agent-workspace-memory-command-editors.ts +180 -0
  54. package/src/input/agent-workspace-navigation.ts +45 -0
  55. package/src/input/agent-workspace-notify-editor-submission.ts +107 -0
  56. package/src/input/agent-workspace-operations-command-editor-submission.ts +146 -0
  57. package/src/input/agent-workspace-operations-command-editors.ts +119 -0
  58. package/src/input/agent-workspace-provider-command-editor-submission.ts +155 -0
  59. package/src/input/agent-workspace-provider-command-editors.ts +93 -0
  60. package/src/input/agent-workspace-requirements.ts +11 -0
  61. package/src/input/agent-workspace-secret-editor-submission.ts +153 -0
  62. package/src/input/agent-workspace-session-command-editor-submission.ts +199 -0
  63. package/src/input/agent-workspace-session-command-editors.ts +249 -0
  64. package/src/input/agent-workspace-setup.ts +49 -19
  65. package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +101 -0
  66. package/src/input/agent-workspace-skill-bundle-command-editors.ts +110 -0
  67. package/src/input/agent-workspace-snapshot.ts +56 -8
  68. package/src/input/agent-workspace-task-command-editor-submission.ts +61 -0
  69. package/src/input/agent-workspace-task-command-editors.ts +47 -0
  70. package/src/input/agent-workspace-types.ts +124 -0
  71. package/src/input/agent-workspace-voice-media.ts +1 -1
  72. package/src/input/agent-workspace-workplan-editor-submission.ts +153 -0
  73. package/src/input/agent-workspace.ts +47 -37
  74. package/src/input/command-registry.ts +1 -1
  75. package/src/input/commands/agent-runtime-profile-runtime.ts +160 -6
  76. package/src/input/commands/agent-skills-runtime.ts +76 -12
  77. package/src/input/commands/agent-workspace-runtime.ts +4 -3
  78. package/src/input/commands/brief-runtime.ts +38 -9
  79. package/src/input/commands/channels-runtime.ts +3 -3
  80. package/src/input/commands/compat-runtime.ts +32 -0
  81. package/src/input/commands/health-runtime.ts +22 -24
  82. package/src/input/commands/knowledge.ts +26 -5
  83. package/src/input/commands/local-provider-runtime.ts +7 -3
  84. package/src/input/commands/local-setup-review.ts +4 -14
  85. package/src/input/commands/mcp-runtime.ts +8 -12
  86. package/src/input/commands/operator-runtime.ts +8 -67
  87. package/src/input/commands/personas-runtime.ts +1 -1
  88. package/src/input/commands/platform-access-runtime.ts +7 -70
  89. package/src/input/commands/policy-dispatch.ts +1 -1
  90. package/src/input/commands/policy.ts +1 -5
  91. package/src/input/commands/product-runtime.ts +2 -2
  92. package/src/input/commands/qrcode-runtime.ts +6 -4
  93. package/src/input/commands/routines-runtime.ts +44 -6
  94. package/src/input/commands/runtime-services.ts +1 -14
  95. package/src/input/commands/security-runtime.ts +3 -8
  96. package/src/input/commands/session-content.ts +1 -1
  97. package/src/input/commands/session-workflow.ts +5 -14
  98. package/src/input/commands/shell-core.ts +8 -2
  99. package/src/input/commands/subscription-runtime.ts +1 -6
  100. package/src/input/commands/support-bundle-runtime.ts +53 -0
  101. package/src/input/commands/tasks-runtime.ts +2 -2
  102. package/src/input/commands.ts +4 -0
  103. package/src/input/feed-context-factory.ts +0 -1
  104. package/src/input/handler-content-actions.ts +0 -25
  105. package/src/input/handler-feed.ts +0 -2
  106. package/src/input/handler-interactions.ts +2 -31
  107. package/src/input/handler-onboarding.ts +29 -12
  108. package/src/input/handler-shortcuts.ts +1 -3
  109. package/src/input/handler.ts +4 -7
  110. package/src/input/keybindings.ts +9 -9
  111. package/src/input/mcp-workspace.ts +90 -26
  112. package/src/input/onboarding/handler-onboarding-routes.ts +3 -1
  113. package/src/input/onboarding/onboarding-wizard-apply.ts +49 -0
  114. package/src/input/onboarding/onboarding-wizard-helpers.ts +1 -1
  115. package/src/input/onboarding/onboarding-wizard-operator-steps.ts +126 -11
  116. package/src/input/onboarding/onboarding-wizard-steps.ts +15 -8
  117. package/src/input/onboarding/onboarding-wizard-types.ts +1 -0
  118. package/src/input/onboarding/onboarding-wizard.ts +19 -2
  119. package/src/input/profile-picker-modal.ts +2 -2
  120. package/src/input/settings-modal-agent-policy.ts +6 -6
  121. package/src/input/settings-modal-subscriptions.ts +3 -3
  122. package/src/input/settings-modal.ts +14 -14
  123. package/src/main.ts +0 -2
  124. package/src/panels/automation-control-panel.ts +3 -3
  125. package/src/panels/builtin/agent.ts +1 -1
  126. package/src/panels/builtin/operations.ts +2 -2
  127. package/src/panels/builtin/session.ts +26 -18
  128. package/src/panels/builtin/shared.ts +6 -6
  129. package/src/panels/project-planning-panel.ts +1 -1
  130. package/src/panels/provider-health-domains.ts +3 -3
  131. package/src/panels/qr-panel.ts +44 -20
  132. package/src/panels/subscription-panel.ts +3 -3
  133. package/src/panels/system-messages-panel.ts +2 -2
  134. package/src/panels/tasks-panel.ts +4 -4
  135. package/src/planning/project-planning-coordinator.ts +2 -2
  136. package/src/renderer/agent-workspace.ts +52 -5
  137. package/src/renderer/block-actions.ts +1 -3
  138. package/src/renderer/help-overlay.ts +13 -8
  139. package/src/renderer/mcp-workspace.ts +32 -13
  140. package/src/renderer/model-picker-overlay.ts +0 -1
  141. package/src/renderer/model-workspace.ts +0 -2
  142. package/src/renderer/onboarding/onboarding-wizard.ts +20 -4
  143. package/src/renderer/panel-picker-overlay.ts +1 -1
  144. package/src/renderer/profile-picker-modal.ts +2 -2
  145. package/src/renderer/settings-modal-helpers.ts +23 -23
  146. package/src/renderer/settings-modal.ts +18 -18
  147. package/src/renderer/system-message.ts +1 -1
  148. package/src/runtime/agent-runtime-events.ts +129 -0
  149. package/src/runtime/bootstrap-command-parts.ts +3 -2
  150. package/src/runtime/bootstrap-core.ts +4 -64
  151. package/src/runtime/bootstrap-shell.ts +42 -4
  152. package/src/runtime/bootstrap.ts +8 -27
  153. package/src/runtime/connected-host-auth.ts +42 -0
  154. package/src/runtime/diagnostics/panels/panel-resources.ts +1 -1
  155. package/src/runtime/index.ts +1 -5
  156. package/src/runtime/onboarding/apply.ts +213 -0
  157. package/src/runtime/onboarding/derivation.ts +25 -6
  158. package/src/runtime/onboarding/snapshot.ts +4 -2
  159. package/src/runtime/onboarding/types.ts +28 -1
  160. package/src/runtime/onboarding/verify.ts +81 -2
  161. package/src/runtime/operator-token-cleanup.ts +2 -2
  162. package/src/runtime/services.ts +27 -12
  163. package/src/shell/service-settings-sync.ts +7 -7
  164. package/src/shell/ui-openers.ts +27 -38
  165. package/src/tools/agent-local-registry-tool.ts +10 -4
  166. package/src/tools/agent-tool-policy-guard.ts +5 -7
  167. package/src/version.ts +1 -1
  168. 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
- export function skillStorePath(shellPaths: ShellPathService): string {
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: ShellPathService): AgentSkillRegistry {
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
- assertNoSecretLikeText([name, description, procedure, ...(input.tags ?? []), ...(input.triggers ?? [])]);
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
- assertNoSecretLikeText([name, description, procedure, ...(input.tags ?? []), ...(input.triggers ?? [])]);
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
- `### Skill Bundle: ${bundle.name}`,
596
- `Description: ${bundle.description}`,
597
- `Review state: ${bundle.reviewState}`,
598
- `Included skills: ${bundle.skillIds.join(', ')}`,
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
- daemon: {
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 services so /status matches the Agent SDK pin.']),
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 services so /status matches the Agent SDK pin.'
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 services to the SDK version required by this Agent package.'
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 service SDK version ${daemonVersion} does not match Agent SDK pin ${metadata.sdkVersion}; Agent Knowledge route is unavailable.`,
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}`,
@@ -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
- agentProfile?: string,
58
+ options: {
59
+ readonly agentProfile?: string;
60
+ readonly useSelectedProfile: boolean;
61
+ } = { useSelectedProfile: true },
59
62
  ): ShellEntrypointOwnership {
60
- const homeDirectory = agentProfile
61
- ? resolveAgentRuntimeProfileHome(roots.homeDirectory, agentProfile).homeDirectory
62
- : roots.homeDirectory;
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
- cli.command === 'profiles' ? undefined : cli.flags.agentProfile,
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
- ' /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',
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
- 'Commands:',
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 service version, and Agent Knowledge route readiness',
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 shown for this user.',
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-service state, and setup posture.',
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 services.',
162
- examples: ['profiles templates', 'profiles templates export research ./research-starter.json --yes', 'profiles templates import ./research-starter.json --yes', 'profiles create household --template household --yes', '--agent-profile household status'],
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, review local promotion receipts, reconcile receipts against live connected schedules, and explicitly promote a reviewed routine into a GoodVibes schedule. Without --yes, promote only prints the schedules.create preview.',
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-service task summaries. Agent blocks service-owned task submission; use run for one-shot work or delegate for explicit build/fix/review handoff.',
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
  '',