@pellux/goodvibes-agent 0.1.116 → 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 -663
  2. package/README.md +38 -52
  3. package/dist/package/main.js +37826 -35924
  4. package/docs/README.md +5 -4
  5. package/docs/{connected-services.md → connected-host.md} +18 -10
  6. package/docs/getting-started.md +41 -44
  7. package/docs/release-and-publishing.md +6 -4
  8. package/package.json +3 -3
  9. package/src/agent/behavior-discovery-summary.ts +1 -3
  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 +3 -3
  19. package/src/agent/runtime-profile.ts +78 -0
  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 +119 -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 +345 -92
  39. package/src/input/agent-workspace-basic-command-editors.ts +480 -7
  40. package/src/input/agent-workspace-categories.ts +175 -15
  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 +23 -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 +24 -14
  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 +50 -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 +120 -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 +89 -4
  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 +81 -3
  116. package/src/input/onboarding/onboarding-wizard-steps.ts +14 -7
  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 +26 -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 +5 -5
  158. package/src/runtime/onboarding/snapshot.ts +2 -2
  159. package/src/runtime/onboarding/types.ts +26 -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
@@ -2,13 +2,16 @@ import {
2
2
  createAgentRuntimeProfile,
3
3
  createAgentRuntimeProfileFromDiscovered,
4
4
  createAgentRuntimeProfileTemplateFromDiscovered,
5
+ clearAgentRuntimeProfileSelection,
5
6
  deleteAgentRuntimeProfile,
6
7
  exportAgentRuntimeProfileTemplate,
7
8
  importAgentRuntimeProfileTemplate,
8
9
  isAgentRuntimeProfileTemplateId,
9
10
  listAgentRuntimeProfiles,
10
11
  listAgentRuntimeProfileTemplates,
12
+ readAgentRuntimeProfileSelection,
11
13
  resolveAgentRuntimeProfileHome,
14
+ setAgentRuntimeProfileSelection,
12
15
  type AgentRuntimeProfileTemplateId,
13
16
  type AgentRuntimeProfileCommandResult,
14
17
  type AgentRuntimeProfileInfo,
@@ -118,6 +121,20 @@ function renderProfilesResult(result: AgentRuntimeProfileCommandResult): string
118
121
  ` use: goodvibes-agent profiles create <name> --template ${result.data.template.id} --yes`,
119
122
  ].join('\n');
120
123
  }
124
+ if (result.kind === 'agent.profiles.default') {
125
+ const selected = result.data?.selectedProfile;
126
+ if (!selected) return 'No default Agent profile selected. Use: goodvibes-agent profiles use <name> --yes';
127
+ return [
128
+ `Default Agent profile: ${selected.id}${selected.exists ? '' : ' (missing)'}`,
129
+ ` home: ${selected.homeDirectory}`,
130
+ selected.selectedAt ? ` selectedAt: ${selected.selectedAt}` : '',
131
+ ` next launch: ${result.data?.nextCommand ?? 'goodvibes-agent'}`,
132
+ selected.exists ? '' : ' fix: create the profile again or clear it with goodvibes-agent profiles default clear --yes',
133
+ ].filter(Boolean).join('\n');
134
+ }
135
+ if (result.kind === 'agent.profiles.default.clear') {
136
+ return 'Default Agent profile cleared. Next launch uses the base Agent home unless --agent-profile is provided.';
137
+ }
121
138
  if (result.kind === 'agent.profiles.create_from_discovered' && result.data?.profile && result.data.template) {
122
139
  return [
123
140
  `Agent profile created from discovered behavior: ${result.data.profile.id}`,
@@ -296,6 +313,107 @@ export async function handleProfilesCommand(runtime: ProfilesCommandRuntime): Pr
296
313
  };
297
314
  }
298
315
 
316
+ if (sub === 'default') {
317
+ const target = values[0];
318
+ if (target === 'clear' || target === 'none' || target === 'base') {
319
+ if (!hasYes(rawRest)) {
320
+ const result: AgentRuntimeProfileCommandResult = {
321
+ ok: false,
322
+ kind: 'agent.profiles.error',
323
+ error: 'Refusing to clear the default Agent profile without --yes.',
324
+ };
325
+ return {
326
+ output: renderProfilesOutput(result, runtime.cli.flags.outputFormat),
327
+ exitCode: 2,
328
+ };
329
+ }
330
+ clearAgentRuntimeProfileSelection(runtime.homeDirectory);
331
+ const result: AgentRuntimeProfileCommandResult = {
332
+ ok: true,
333
+ kind: 'agent.profiles.default.clear',
334
+ };
335
+ return {
336
+ output: renderProfilesOutput(result, runtime.cli.flags.outputFormat),
337
+ exitCode: 0,
338
+ };
339
+ }
340
+ if (target) {
341
+ if (!hasYes(rawRest)) {
342
+ const result: AgentRuntimeProfileCommandResult = {
343
+ ok: false,
344
+ kind: 'agent.profiles.error',
345
+ error: `Refusing to set default Agent profile ${target} without --yes.`,
346
+ };
347
+ return {
348
+ output: renderProfilesOutput(result, runtime.cli.flags.outputFormat),
349
+ exitCode: 2,
350
+ };
351
+ }
352
+ const selected = setAgentRuntimeProfileSelection(runtime.homeDirectory, target);
353
+ const result: AgentRuntimeProfileCommandResult = {
354
+ ok: true,
355
+ kind: 'agent.profiles.default',
356
+ data: {
357
+ selectedProfile: selected,
358
+ nextCommand: 'goodvibes-agent',
359
+ },
360
+ };
361
+ return {
362
+ output: renderProfilesOutput(result, runtime.cli.flags.outputFormat),
363
+ exitCode: 0,
364
+ };
365
+ }
366
+ const selected = readAgentRuntimeProfileSelection(runtime.homeDirectory);
367
+ const result: AgentRuntimeProfileCommandResult = {
368
+ ok: true,
369
+ kind: 'agent.profiles.default',
370
+ data: selected ? { selectedProfile: selected, nextCommand: 'goodvibes-agent' } : undefined,
371
+ };
372
+ return {
373
+ output: renderProfilesOutput(result, runtime.cli.flags.outputFormat),
374
+ exitCode: 0,
375
+ };
376
+ }
377
+
378
+ if (sub === 'use' || sub === 'select' || sub === 'switch') {
379
+ const name = values[0];
380
+ if (!name) {
381
+ const result: AgentRuntimeProfileCommandResult = {
382
+ ok: false,
383
+ kind: 'agent.profiles.error',
384
+ error: 'Usage: goodvibes-agent profiles use <name> --yes',
385
+ };
386
+ return {
387
+ output: renderProfilesOutput(result, runtime.cli.flags.outputFormat),
388
+ exitCode: 2,
389
+ };
390
+ }
391
+ if (!hasYes(rawRest)) {
392
+ const result: AgentRuntimeProfileCommandResult = {
393
+ ok: false,
394
+ kind: 'agent.profiles.error',
395
+ error: `Refusing to set default Agent profile ${name} without --yes.`,
396
+ };
397
+ return {
398
+ output: renderProfilesOutput(result, runtime.cli.flags.outputFormat),
399
+ exitCode: 2,
400
+ };
401
+ }
402
+ const selected = setAgentRuntimeProfileSelection(runtime.homeDirectory, name);
403
+ const result: AgentRuntimeProfileCommandResult = {
404
+ ok: true,
405
+ kind: 'agent.profiles.default',
406
+ data: {
407
+ selectedProfile: selected,
408
+ nextCommand: 'goodvibes-agent',
409
+ },
410
+ };
411
+ return {
412
+ output: renderProfilesOutput(result, runtime.cli.flags.outputFormat),
413
+ exitCode: 0,
414
+ };
415
+ }
416
+
299
417
  if (sub === 'show' || sub === 'path' || sub === 'home') {
300
418
  const name = values[0];
301
419
  if (!name) {
@@ -464,7 +582,7 @@ export async function handleProfilesCommand(runtime: ProfilesCommandRuntime): Pr
464
582
  const result: AgentRuntimeProfileCommandResult = {
465
583
  ok: false,
466
584
  kind: 'agent.profiles.error',
467
- error: 'Usage: goodvibes-agent profiles [list|templates|show <name>|create <name> [--template <id>] --yes|delete <name> --yes]',
585
+ error: 'Usage: goodvibes-agent profiles [list|templates|default [<name>|clear] --yes|use <name> --yes|show <name>|create <name> [--template <id>] --yes|delete <name> --yes]',
468
586
  };
469
587
  return {
470
588
  output: renderProfilesOutput(result, runtime.cli.flags.outputFormat),
@@ -1,6 +1,7 @@
1
1
  import { createShellPathService } from '@/runtime/index.ts';
2
2
  import { discoverRoutines, type DiscoveredRoutineRecord } from '../agent/routine-discovery.ts';
3
- import { AgentRoutineRegistry, type AgentRoutineRecord } from '../agent/routine-registry.ts';
3
+ import { AgentRoutineRegistry, evaluateAgentRoutineReadiness, type AgentRoutineRecord } from '../agent/routine-registry.ts';
4
+ import { buildAgentSkillRequirements, formatAgentSkillRequirement } from '../agent/skill-registry.ts';
4
5
  import {
5
6
  buildRoutineSchedulePreview,
6
7
  promoteRoutineToDaemonSchedule,
@@ -34,10 +35,26 @@ interface RoutinesCommandFailure {
34
35
  readonly error: string;
35
36
  }
36
37
 
38
+ interface ParsedRoutineOptions {
39
+ readonly positionals: readonly string[];
40
+ readonly flags: ReadonlyMap<string, string>;
41
+ readonly yes: boolean;
42
+ }
43
+
44
+ const ROUTINE_CREATE_USAGE = 'Usage: goodvibes-agent routines create --name <name> --description <summary> --steps <steps> [--tags a,b] [--triggers a,b] [--requires-env A,B] [--requires-command gh,jq] [--enabled]';
45
+
37
46
  function jsonOrText(runtime: CliCommandRuntime, value: unknown, text: string): string {
38
47
  return runtime.cli.flags.outputFormat === 'json' ? JSON.stringify(value, null, 2) : text;
39
48
  }
40
49
 
50
+ function commandFailure(runtime: CliCommandRuntime, kind: string, error: string, exitCode: number): CliCommandOutput {
51
+ const failure: RoutinesCommandFailure = { ok: false, kind, error };
52
+ return {
53
+ output: runtime.cli.flags.outputFormat === 'json' ? JSON.stringify(failure, null, 2) : error,
54
+ exitCode,
55
+ };
56
+ }
57
+
41
58
  function routineRegistry(runtime: CliCommandRuntime): AgentRoutineRegistry {
42
59
  return AgentRoutineRegistry.fromShellPaths(createShellPathService({
43
60
  workingDirectory: runtime.workingDirectory,
@@ -64,6 +81,47 @@ function splitList(value: string | undefined): readonly string[] {
64
81
  return value.split(',').map((entry) => entry.trim()).filter(Boolean);
65
82
  }
66
83
 
84
+ function parseRoutineOptions(args: readonly string[]): ParsedRoutineOptions {
85
+ const flags = new Map<string, string>();
86
+ const positionals: string[] = [];
87
+ let yes = false;
88
+ for (let index = 0; index < args.length; index += 1) {
89
+ const token = args[index] ?? '';
90
+ if (token === '--yes') {
91
+ yes = true;
92
+ continue;
93
+ }
94
+ if (token.startsWith('--')) {
95
+ const key = token.slice(2);
96
+ const next = args[index + 1];
97
+ if (next !== undefined && !next.startsWith('--')) {
98
+ flags.set(key, next);
99
+ index += 1;
100
+ } else {
101
+ flags.set(key, 'true');
102
+ }
103
+ continue;
104
+ }
105
+ positionals.push(token);
106
+ }
107
+ return { positionals, flags, yes };
108
+ }
109
+
110
+ function optionValue(options: ParsedRoutineOptions, key: string): string | undefined {
111
+ const value = options.flags.get(key)?.trim();
112
+ return value ? value : undefined;
113
+ }
114
+
115
+ function hasFlag(options: ParsedRoutineOptions, key: string): boolean {
116
+ return options.flags.get(key) === 'true';
117
+ }
118
+
119
+ function requiredOption(options: ParsedRoutineOptions, key: string): string {
120
+ const value = optionValue(options, key);
121
+ if (!value) throw new Error(`${ROUTINE_CREATE_USAGE}\nMissing --${key}.`);
122
+ return value;
123
+ }
124
+
67
125
  function parseImportFlags(args: readonly string[]): {
68
126
  readonly name: string;
69
127
  readonly enabled: boolean;
@@ -89,7 +147,9 @@ function parseImportFlags(args: readonly string[]): {
89
147
  function summarizeRoutine(routine: AgentRoutineRecord): string {
90
148
  const enabled = routine.enabled ? 'enabled' : 'disabled';
91
149
  const tags = routine.tags.length > 0 ? ` tags=${routine.tags.join(',')}` : '';
92
- return ` ${routine.id} ${enabled} ${routine.reviewState} starts=${routine.startCount} ${routine.name} - ${routine.description}${tags}`;
150
+ const readiness = evaluateAgentRoutineReadiness(routine);
151
+ const ready = readiness.ready ? 'ready' : `needs ${readiness.missing.map(formatAgentSkillRequirement).join(',')}`;
152
+ return ` ${routine.id} ${enabled} ${routine.reviewState} ${ready} starts=${routine.startCount} ${routine.name} - ${routine.description}${tags}`;
93
153
  }
94
154
 
95
155
  function summarizeDiscoveredRoutine(routine: DiscoveredRoutineRecord): string {
@@ -136,13 +196,21 @@ function discoveredRoutineFrontmatterList(routine: DiscoveredRoutineRecord, key:
136
196
  return splitList(value);
137
197
  }
138
198
 
139
- function renderRoutineList(title: string, path: string, routines: readonly AgentRoutineRecord[]): string {
199
+ function discoveredRoutineFrontmatterAnyList(routine: DiscoveredRoutineRecord, keys: readonly string[]): readonly string[] {
200
+ for (const key of keys) {
201
+ const values = discoveredRoutineFrontmatterList(routine, key);
202
+ if (values.length > 0) return values;
203
+ }
204
+ return [];
205
+ }
206
+
207
+ function renderRoutineList(title: string, path: string, routines: readonly AgentRoutineRecord[], emptyMessage?: string): string {
140
208
  if (routines.length === 0) {
141
209
  return [
142
210
  title,
143
- ' No local Agent routines yet.',
144
- ' Create routines inside the Agent TUI with /routines create, or create an Agent profile from a starter template.',
145
- ].join('\n');
211
+ ` ${emptyMessage ?? 'No local Agent routines yet.'}`,
212
+ emptyMessage ? '' : ' Open /agent routines in the TUI, or use goodvibes-agent routines create for scripted setup.',
213
+ ].filter(Boolean).join('\n');
146
214
  }
147
215
  return [
148
216
  `${title} (${routines.length})`,
@@ -152,10 +220,14 @@ function renderRoutineList(title: string, path: string, routines: readonly Agent
152
220
  }
153
221
 
154
222
  function renderRoutine(routine: AgentRoutineRecord): string {
223
+ const readiness = evaluateAgentRoutineReadiness(routine);
155
224
  return [
156
225
  `Routine ${routine.name}`,
157
226
  ` id: ${routine.id}`,
158
227
  ` enabled: ${routine.enabled ? 'yes' : 'no'}`,
228
+ ` readiness: ${readiness.ready ? 'ready' : 'needs setup'}`,
229
+ ` requirements: ${routine.requirements.map(formatAgentSkillRequirement).join(', ') || '(none)'}`,
230
+ readiness.missing.length > 0 ? ` missing: ${readiness.missing.map(formatAgentSkillRequirement).join(', ')}` : '',
159
231
  ` review: ${routine.reviewState}`,
160
232
  ` source: ${routine.source}`,
161
233
  ` provenance: ${routine.provenance}`,
@@ -235,15 +307,23 @@ export async function handleRoutinesCommand(runtime: CliCommandRuntime): Promise
235
307
  const registry = routineRegistry(runtime);
236
308
  const snapshot = registry.snapshot();
237
309
  const normalized = sub.toLowerCase();
238
- if (normalized === 'list' || normalized === 'enabled') {
239
- const routines = normalized === 'enabled' ? snapshot.enabledRoutines : snapshot.routines;
310
+ if (normalized === 'list' || normalized === 'enabled' || normalized === 'attention' || normalized === 'needs-setup') {
311
+ const routines = normalized === 'enabled'
312
+ ? snapshot.enabledRoutines
313
+ : normalized === 'attention' || normalized === 'needs-setup'
314
+ ? snapshot.routines.filter((routine) => !evaluateAgentRoutineReadiness(routine).ready)
315
+ : snapshot.routines;
240
316
  const value: RoutinesCommandSuccess<{
241
317
  readonly path: string;
242
318
  readonly routines: readonly AgentRoutineRecord[];
243
319
  readonly enabledCount: number;
244
320
  }> = {
245
321
  ok: true,
246
- kind: normalized === 'enabled' ? 'agent.routines.enabled' : 'agent.routines.list',
322
+ kind: normalized === 'enabled'
323
+ ? 'agent.routines.enabled'
324
+ : normalized === 'attention' || normalized === 'needs-setup'
325
+ ? 'agent.routines.attention'
326
+ : 'agent.routines.list',
247
327
  data: {
248
328
  path: snapshot.path,
249
329
  routines,
@@ -251,7 +331,16 @@ export async function handleRoutinesCommand(runtime: CliCommandRuntime): Promise
251
331
  },
252
332
  };
253
333
  return {
254
- output: jsonOrText(runtime, value, renderRoutineList(normalized === 'enabled' ? 'Enabled Agent routines' : 'Agent routines', snapshot.path, routines)),
334
+ output: jsonOrText(runtime, value, renderRoutineList(
335
+ normalized === 'enabled'
336
+ ? 'Enabled Agent routines'
337
+ : normalized === 'attention' || normalized === 'needs-setup'
338
+ ? 'Agent routines needing setup'
339
+ : 'Agent routines',
340
+ snapshot.path,
341
+ routines,
342
+ normalized === 'attention' || normalized === 'needs-setup' ? 'No local Agent routines need setup.' : undefined,
343
+ )),
255
344
  exitCode: 0,
256
345
  };
257
346
  }
@@ -317,6 +406,10 @@ export async function handleRoutinesCommand(runtime: CliCommandRuntime): Promise
317
406
  steps: discovered.steps,
318
407
  tags: discoveredRoutineFrontmatterList(discovered, 'tags'),
319
408
  triggers: discoveredRoutineFrontmatterList(discovered, 'triggers'),
409
+ requirements: buildAgentSkillRequirements({
410
+ env: discoveredRoutineFrontmatterAnyList(discovered, ['requiresEnv', 'requires-env', 'requires_env']),
411
+ commands: discoveredRoutineFrontmatterAnyList(discovered, ['requiresCommands', 'requires-commands', 'requires_commands', 'commands']),
412
+ }),
320
413
  enabled: parsed.enabled,
321
414
  source: 'imported',
322
415
  provenance: `discovered:${discovered.origin}:${discovered.path}`,
@@ -331,6 +424,41 @@ export async function handleRoutinesCommand(runtime: CliCommandRuntime): Promise
331
424
  exitCode: 0,
332
425
  };
333
426
  }
427
+ if (normalized === 'create') {
428
+ const options = parseRoutineOptions(rest);
429
+ try {
430
+ const routine = registry.create({
431
+ name: requiredOption(options, 'name'),
432
+ description: requiredOption(options, 'description'),
433
+ steps: optionValue(options, 'steps') ?? options.positionals.join(' ').trim(),
434
+ tags: splitList(optionValue(options, 'tags')),
435
+ triggers: splitList(optionValue(options, 'triggers')),
436
+ requirements: buildAgentSkillRequirements({
437
+ env: splitList(optionValue(options, 'requires-env')),
438
+ commands: splitList(optionValue(options, 'requires-command') ?? optionValue(options, 'requires-commands')),
439
+ }),
440
+ enabled: hasFlag(options, 'enabled'),
441
+ source: 'user',
442
+ provenance: 'cli',
443
+ });
444
+ const value: RoutinesCommandSuccess<AgentRoutineRecord> = {
445
+ ok: true,
446
+ kind: 'agent.routines.create',
447
+ data: routine,
448
+ };
449
+ return {
450
+ output: jsonOrText(runtime, value, `Created Agent routine ${routine.id}: ${routine.name}${routine.enabled ? ' (enabled)' : ''}`),
451
+ exitCode: 0,
452
+ };
453
+ } catch (error) {
454
+ return commandFailure(
455
+ runtime,
456
+ error instanceof Error && error.message.startsWith('Usage:') ? 'invalid_routine_command' : 'agent.routines.create.error',
457
+ error instanceof Error ? error.message : String(error),
458
+ error instanceof Error && error.message.startsWith('Usage:') ? 2 : 1,
459
+ );
460
+ }
461
+ }
334
462
  if (normalized === 'show') {
335
463
  const id = rest[0];
336
464
  if (!id) return { output: 'Usage: goodvibes-agent routines show <id>', exitCode: 2 };
@@ -396,7 +524,7 @@ export async function handleRoutinesCommand(runtime: CliCommandRuntime): Promise
396
524
  return handleRoutinePromotion(runtime, rest);
397
525
  }
398
526
  return {
399
- output: 'Usage: goodvibes-agent routines [list|enabled|discover|import-discovered <name> --yes|show <id>|receipts|reconcile|receipt <id>|promote <id> (--cron <expr>|--every <interval>|--at <iso-time>) [--delivery-channel <channel>|--delivery-route <route>|--delivery-webhook <url>] --yes]',
527
+ output: `Usage: goodvibes-agent routines [list|enabled|attention|discover|import-discovered <name> --yes|create --name <name> --description <summary> --steps <steps>|show <id>|receipts|reconcile|receipt <id>|promote <id> (--cron <expr>|--every <interval>|--at <iso-time>) [--delivery-channel <channel>|--delivery-route <route>|--delivery-webhook <url>] --yes]`,
400
528
  exitCode: 2,
401
529
  };
402
530
  }
@@ -32,7 +32,7 @@ export interface CliServiceLogPosture {
32
32
  readonly readError?: string;
33
33
  }
34
34
 
35
- export interface CliExternalDaemonLifecyclePosture {
35
+ export interface CliExternalHostLifecyclePosture {
36
36
  readonly platform: 'manual';
37
37
  readonly path: string;
38
38
  readonly installed: false;
@@ -54,7 +54,7 @@ export interface CliServicePosture {
54
54
  readonly restartOnFailure: boolean;
55
55
  readonly daemonEnabled: boolean;
56
56
  };
57
- readonly managed: CliExternalDaemonLifecyclePosture;
57
+ readonly managed: CliExternalHostLifecyclePosture;
58
58
  readonly endpoints: readonly CliServiceEndpointPosture[];
59
59
  readonly log: CliServiceLogPosture;
60
60
  readonly issues: readonly string[];
@@ -140,10 +140,10 @@ function resolveConfiguredLogPath(runtime: CliServiceRuntime): string | undefine
140
140
  return isAbsolute(trimmed) ? trimmed : join(runtime.homeDirectory, trimmed);
141
141
  }
142
142
 
143
- function createExternalDaemonLifecycle(logPath: string | undefined): CliExternalDaemonLifecyclePosture {
143
+ function createExternalHostLifecycle(logPath: string | undefined): CliExternalHostLifecyclePosture {
144
144
  return {
145
145
  platform: 'manual',
146
- path: 'connected GoodVibes services',
146
+ path: 'connected GoodVibes host',
147
147
  installed: false,
148
148
  autostart: false,
149
149
  running: false,
@@ -151,7 +151,7 @@ function createExternalDaemonLifecycle(logPath: string | undefined): CliExternal
151
151
  commandPreview: 'managed outside goodvibes-agent',
152
152
  suggestedCommands: [],
153
153
  lastAction: 'status',
154
- pidPath: 'connected GoodVibes services',
154
+ pidPath: 'connected GoodVibes host',
155
155
  lastError: null,
156
156
  };
157
157
  }
@@ -184,7 +184,7 @@ export async function buildCliServicePosture(
184
184
  const issues: string[] = [];
185
185
 
186
186
  if (serverBackedEnabled && !config.enabled) {
187
- issues.push('Connected-service settings are present, but Agent service ownership is disabled by design.');
187
+ issues.push('Connected-host settings are present, but Agent host ownership is disabled by design.');
188
188
  }
189
189
  for (const endpoint of endpoints) {
190
190
  if (endpoint.enabled && options.probe && endpoint.reachable === false) {
@@ -209,7 +209,7 @@ export async function buildCliServicePosture(
209
209
 
210
210
  return {
211
211
  config,
212
- managed: createExternalDaemonLifecycle(configuredLogPath),
212
+ managed: createExternalHostLifecycle(configuredLogPath),
213
213
  endpoints,
214
214
  log,
215
215
  issues,
@@ -223,16 +223,16 @@ function yesNo(value: boolean): string {
223
223
  export function formatCliServicePosture(posture: CliServicePosture, json = false): string {
224
224
  if (json) return JSON.stringify(posture, null, 2);
225
225
  return [
226
- 'GoodVibes Agent runtime connection diagnostics',
227
- ' ownership: managed outside goodvibes-agent',
228
- ' Agent owns lifecycle: no',
226
+ 'GoodVibes Agent connected-host diagnostics',
227
+ ' lifecycle owner: outside goodvibes-agent',
228
+ ' Agent starts connected host: no',
229
229
  ` external host config present: ${yesNo(posture.config.enabled)}`,
230
230
  ' external host lifecycle config: ignored by Agent',
231
231
  ` legacy host switch present: ${yesNo(posture.config.daemonEnabled)}`,
232
232
  ` log: ${posture.log.path ?? 'n/a'} (${posture.log.exists ? 'present' : 'missing'})`,
233
233
  ...(posture.log.readError ? [` log read error: ${posture.log.readError}`] : []),
234
234
  '',
235
- 'Connected GoodVibes API checks:',
235
+ 'Connected API checks:',
236
236
  ...posture.endpoints.map((endpoint) =>
237
237
  ` ${endpoint.label}: enabled=${yesNo(endpoint.enabled)} ${endpoint.binding.hostMode} ${endpoint.binding.host}:${endpoint.binding.port} posture=${endpoint.bindPosture.label}${endpoint.reachable === undefined ? '' : ` reachable=${yesNo(endpoint.reachable)}`}`,
238
238
  ),
package/src/cli/status.ts CHANGED
@@ -124,9 +124,9 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
124
124
  id: 'external-runtime-unreachable',
125
125
  area: 'runtime',
126
126
  severity: 'warning',
127
- summary: 'Connected GoodVibes services are not reachable.',
127
+ summary: 'Connected GoodVibes host is not reachable.',
128
128
  cause: `Agent could not reach ${options.externalRuntime.baseUrl}${options.externalRuntime.error ? `: ${options.externalRuntime.error}` : '.'}`,
129
- impact: 'Companion chat, isolated Agent Knowledge, approvals, automation status, and build delegation cannot work until connected GoodVibes services are available.',
129
+ impact: 'Companion chat, isolated Agent Knowledge, approvals, automation status, and build delegation cannot work until the connected GoodVibes host is available.',
130
130
  action: 'Start or repair the owning GoodVibes host, then rerun goodvibes-agent status.',
131
131
  });
132
132
  } else if (!options.externalRuntime.compatible) {
@@ -134,8 +134,8 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
134
134
  id: 'external-runtime-version-mismatch',
135
135
  area: 'runtime',
136
136
  severity: 'warning',
137
- summary: 'Connected GoodVibes service SDK version does not match Agent.',
138
- cause: `Connected service reports SDK ${options.externalRuntime.version}; Agent expects ${options.externalRuntime.expectedVersion}.`,
137
+ summary: 'Connected GoodVibes host SDK version does not match Agent.',
138
+ cause: `Connected host reports SDK ${options.externalRuntime.version}; Agent expects ${options.externalRuntime.expectedVersion}.`,
139
139
  impact: 'Agent-only routes, especially isolated Agent Knowledge, may be missing or incompatible.',
140
140
  action: 'Update the owning GoodVibes host so /status matches this Agent package SDK pin.',
141
141
  });
@@ -146,9 +146,9 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
146
146
  id: 'external-runtime-token-missing',
147
147
  area: 'auth',
148
148
  severity: 'warning',
149
- summary: 'Connected-service operator token is missing.',
149
+ summary: 'Connected-host operator token is missing.',
150
150
  cause: `No operator token was found at ${options.externalRuntime.operatorToken.path}.`,
151
- impact: 'Agent can inspect only unauthenticated routes and cannot use protected connected-service APIs.',
151
+ impact: 'Agent can inspect only unauthenticated routes and cannot use protected connected-host APIs.',
152
152
  action: 'Pair or provision access through the owning GoodVibes host, then rerun goodvibes-agent auth.',
153
153
  });
154
154
  }
@@ -161,7 +161,7 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
161
161
  summary: 'Isolated Agent Knowledge route is not ready.',
162
162
  cause: `${options.externalRuntime.agentKnowledge.route} returned ${options.externalRuntime.agentKnowledge.kind}${options.externalRuntime.agentKnowledge.statusCode === null ? '' : ` (${options.externalRuntime.agentKnowledge.statusCode})`}.`,
163
163
  impact: 'Agent Knowledge ask/search will not use any fallback wiki or non-Agent knowledge segment; it will fail closed until the Agent route is available.',
164
- action: 'Update the connected GoodVibes services to the Agent-compatible SDK and verify goodvibes-agent compat.',
164
+ action: 'Update the connected GoodVibes host to the Agent-compatible SDK and verify goodvibes-agent compat.',
165
165
  });
166
166
  }
167
167
  }
@@ -171,10 +171,10 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
171
171
  id: 'runtime-ownership-external',
172
172
  area: 'runtime',
173
173
  severity: 'warning',
174
- summary: 'Connected-service settings are present while Agent service ownership is disabled by design.',
174
+ summary: 'Connected-host settings are present while Agent host ownership is disabled by design.',
175
175
  cause: 'One or more connected API, inbound events, or browser companion settings are enabled while service.enabled is false.',
176
176
  impact: 'The owning GoodVibes host must provide availability for those endpoints; Agent will not start or enable them.',
177
- action: 'Manage connected-service availability from GoodVibes TUI or the owning host, then use Agent for read-only diagnostics.',
177
+ action: 'Manage connected-host availability from GoodVibes TUI or the owning host, then use Agent for read-only diagnostics.',
178
178
  });
179
179
  }
180
180
 
@@ -186,9 +186,9 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
186
186
  area: 'runtime',
187
187
  severity: 'warning',
188
188
  summary: issue,
189
- cause: 'The connected-service inspection found a mismatch between configured endpoint state and observed host state.',
189
+ cause: 'The connected-host inspection found a mismatch between configured endpoint state and observed host state.',
190
190
  impact: 'Connected API, listener, or web availability may not match the configuration.',
191
- action: 'Use Agent status and doctor diagnostics here, then manage connected services outside Agent.',
191
+ action: 'Use Agent status and doctor diagnostics here, then manage the connected host outside Agent.',
192
192
  });
193
193
  }
194
194
  }
@@ -198,9 +198,9 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
198
198
  id: 'onboarding-incomplete',
199
199
  area: 'onboarding',
200
200
  severity: 'warning',
201
- summary: 'Agent setup has not been shown for this user.',
202
- cause: 'No global user setup check marker was found.',
203
- impact: 'Important connected-service, network, provider, auth, or permission choices may still be implicit defaults.',
201
+ summary: 'Agent setup has not been applied for this user.',
202
+ cause: 'No global user setup completion marker was found.',
203
+ impact: 'Important connected-host, network, provider, auth, or permission choices may still be implicit defaults.',
204
204
  action: 'Run /setup in GoodVibes Agent or goodvibes-agent setup status to review setup state.',
205
205
  });
206
206
  }
@@ -210,10 +210,10 @@ export function buildCliDoctorFindings(options: CliStatusOptions): readonly CliD
210
210
  id: 'network-endpoint-without-runtime-auth-signal',
211
211
  area: 'auth',
212
212
  severity: 'risk',
213
- summary: 'Network-facing connected-service endpoints are enabled without a visible auth signal.',
214
- cause: `${networkFacingSurfaces.map(([name]) => name).join(', ')} are LAN/custom-bound, but Agent cannot see connected-service auth state from its local compatibility files.`,
213
+ summary: 'Network-facing connected-host endpoints are enabled without a visible auth signal.',
214
+ cause: `${networkFacingSurfaces.map(([name]) => name).join(', ')} are LAN/custom-bound, but Agent cannot see connected-host auth state from its local compatibility files.`,
215
215
  impact: 'Remote access paths may be unusable or unsafe unless the owning host configured auth.',
216
- action: 'Review connected-service auth outside Agent; Agent will not create local service users.',
216
+ action: 'Review connected-host auth outside Agent; Agent will not create local service users.',
217
217
  });
218
218
  }
219
219
 
@@ -364,15 +364,11 @@ export function renderCliStatus(options: CliStatusOptions): string {
364
364
  ' live check: unavailable',
365
365
  ]),
366
366
  '',
367
- 'Connected GoodVibes Services:',
367
+ 'Connected Host:',
368
368
  ' Agent role: client/operator TUI only',
369
- ' service ownership: outside Agent',
370
- ' Agent starts services: no',
369
+ ' lifecycle owner: outside Agent',
370
+ ' Agent starts connected host: no',
371
371
  ...(options.service ? [
372
- ` platform: ${options.service.managed.platform}`,
373
- ` installed: ${yesNo(options.service.managed.installed)}`,
374
- ` running: ${yesNo(options.service.managed.running)}`,
375
- ` definition: ${options.service.managed.path}`,
376
372
  ` log: ${options.service.log.path ?? 'n/a'} (${options.service.log.exists ? 'present' : 'missing'})`,
377
373
  ] : []),
378
374
  'Onboarding:',
@@ -384,7 +380,7 @@ export function renderCliStatus(options: CliStatusOptions): string {
384
380
  if (options.doctor) {
385
381
  lines.push(
386
382
  '',
387
- 'Connected-Service Config Signals:',
383
+ 'Connected Host Config Signals:',
388
384
  ` host config present: ${yesNo(serviceEnabled)}`,
389
385
  ' lifecycle config: external to Agent',
390
386
  '',