@pellux/goodvibes-agent 0.1.116 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/CHANGELOG.md +48 -661
  2. package/README.md +64 -57
  3. package/bin/goodvibes-agent.ts +16 -2
  4. package/dist/package/main.js +199180 -192185
  5. package/docs/README.md +15 -8
  6. package/docs/channels-remote-and-api.md +50 -0
  7. package/docs/connected-host.md +59 -0
  8. package/docs/getting-started.md +61 -50
  9. package/docs/knowledge-artifacts-and-multimodal.md +91 -0
  10. package/docs/project-planning.md +79 -0
  11. package/docs/providers-and-routing.md +46 -0
  12. package/docs/release-and-publishing.md +48 -11
  13. package/docs/tools-and-commands.md +123 -0
  14. package/docs/voice-and-live-tts.md +51 -0
  15. package/package.json +3 -6
  16. package/src/agent/behavior-discovery-summary.ts +1 -3
  17. package/src/agent/channel-delivery.ts +201 -0
  18. package/src/agent/media-generation.ts +159 -0
  19. package/src/agent/memory-prompt.ts +0 -1
  20. package/src/agent/note-registry.ts +329 -0
  21. package/src/agent/operator-actions.ts +343 -0
  22. package/src/agent/persona-discovery.ts +1 -3
  23. package/src/agent/persona-registry.ts +19 -16
  24. package/src/agent/record-labels.ts +107 -0
  25. package/src/agent/reminder-schedule-format.ts +33 -24
  26. package/src/agent/reminder-schedule.ts +27 -26
  27. package/src/agent/routine-registry.ts +54 -14
  28. package/src/agent/routine-schedule-args.ts +2 -1
  29. package/src/agent/routine-schedule-format.ts +77 -53
  30. package/src/agent/routine-schedule-promotion.ts +35 -33
  31. package/src/agent/routine-schedule-receipts.ts +29 -27
  32. package/src/agent/runtime-profile-starters.ts +5 -5
  33. package/src/agent/runtime-profile.ts +91 -12
  34. package/src/agent/skill-registry.ts +210 -32
  35. package/src/cli/agent-knowledge-args.ts +5 -1
  36. package/src/cli/agent-knowledge-command.ts +40 -34
  37. package/src/cli/agent-knowledge-format.ts +80 -67
  38. package/src/cli/agent-knowledge-methods.ts +1 -1
  39. package/src/cli/agent-knowledge-runtime.ts +32 -26
  40. package/src/cli/bundle-command.ts +14 -9
  41. package/src/cli/config-overrides.ts +37 -36
  42. package/src/cli/entrypoint.ts +16 -7
  43. package/src/cli/external-runtime.ts +10 -4
  44. package/src/cli/help.ts +54 -29
  45. package/src/cli/local-library-command.ts +203 -83
  46. package/src/cli/management-commands.ts +105 -68
  47. package/src/cli/management.ts +55 -74
  48. package/src/cli/memory-command.ts +66 -32
  49. package/src/cli/parser.ts +37 -3
  50. package/src/cli/profiles-command.ts +166 -31
  51. package/src/cli/provider-auth-routes.ts +2 -1
  52. package/src/cli/routines-command.ts +176 -40
  53. package/src/cli/service-posture.ts +16 -16
  54. package/src/cli/status.ts +56 -135
  55. package/src/core/conversation-message-snapshot.ts +131 -0
  56. package/src/input/agent-workspace-access-command-editor-submission.ts +149 -0
  57. package/src/input/agent-workspace-access-command-editors.ts +170 -0
  58. package/src/input/agent-workspace-activation.ts +35 -8
  59. package/src/input/agent-workspace-basic-command-editor-submission.ts +351 -94
  60. package/src/input/agent-workspace-basic-command-editors.ts +502 -9
  61. package/src/input/agent-workspace-categories.ts +423 -51
  62. package/src/input/agent-workspace-channel-command-editor-submission.ts +83 -0
  63. package/src/input/agent-workspace-channel-command-editors.ts +65 -0
  64. package/src/input/agent-workspace-channels.ts +35 -2
  65. package/src/input/agent-workspace-command-editor.ts +41 -3
  66. package/src/input/agent-workspace-config-reader.ts +16 -0
  67. package/src/input/agent-workspace-delegation-editor-submission.ts +55 -0
  68. package/src/input/agent-workspace-editors.ts +166 -1
  69. package/src/input/agent-workspace-knowledge-command-editor-submission.ts +132 -0
  70. package/src/input/agent-workspace-knowledge-command-editors.ts +110 -0
  71. package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
  72. package/src/input/agent-workspace-learned-behavior.ts +67 -0
  73. package/src/input/agent-workspace-library-command-editor-submission.ts +50 -0
  74. package/src/input/agent-workspace-library-command-editors.ts +35 -0
  75. package/src/input/agent-workspace-local-operations.ts +218 -0
  76. package/src/input/agent-workspace-local-selection.ts +75 -0
  77. package/src/input/agent-workspace-mcp-command-editor-submission.ts +95 -0
  78. package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
  79. package/src/input/agent-workspace-media-command-editors.ts +27 -0
  80. package/src/input/agent-workspace-memory-command-editor-submission.ts +232 -0
  81. package/src/input/agent-workspace-memory-command-editors.ts +180 -0
  82. package/src/input/agent-workspace-memory-editor.ts +2 -2
  83. package/src/input/agent-workspace-navigation.ts +81 -0
  84. package/src/input/agent-workspace-notify-editor-submission.ts +121 -0
  85. package/src/input/agent-workspace-operations-command-editor-submission.ts +208 -0
  86. package/src/input/agent-workspace-operations-command-editors.ts +196 -0
  87. package/src/input/agent-workspace-panel-route.ts +43 -0
  88. package/src/input/agent-workspace-provider-command-editor-submission.ts +155 -0
  89. package/src/input/agent-workspace-provider-command-editors.ts +93 -0
  90. package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
  91. package/src/input/agent-workspace-requirements.ts +11 -0
  92. package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
  93. package/src/input/agent-workspace-search.ts +169 -0
  94. package/src/input/agent-workspace-secret-editor-submission.ts +153 -0
  95. package/src/input/agent-workspace-session-command-editor-submission.ts +199 -0
  96. package/src/input/agent-workspace-session-command-editors.ts +249 -0
  97. package/src/input/agent-workspace-setup.ts +37 -16
  98. package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +101 -0
  99. package/src/input/agent-workspace-skill-bundle-command-editors.ts +110 -0
  100. package/src/input/agent-workspace-snapshot.ts +162 -21
  101. package/src/input/agent-workspace-task-command-editor-submission.ts +61 -0
  102. package/src/input/agent-workspace-task-command-editors.ts +47 -0
  103. package/src/input/agent-workspace-token.ts +18 -2
  104. package/src/input/agent-workspace-types.ts +212 -4
  105. package/src/input/agent-workspace-voice-media.ts +4 -7
  106. package/src/input/agent-workspace-web-research-editor.ts +104 -0
  107. package/src/input/agent-workspace-workplan-editor-submission.ts +153 -0
  108. package/src/input/agent-workspace.ts +178 -240
  109. package/src/input/command-registry.ts +5 -2
  110. package/src/input/commands/agent-runtime-profile-runtime.ts +126 -37
  111. package/src/input/commands/agent-skills-runtime.ts +149 -72
  112. package/src/input/commands/agent-workspace-runtime.ts +23 -6
  113. package/src/input/commands/brief-runtime.ts +55 -25
  114. package/src/input/commands/channels-runtime.ts +147 -33
  115. package/src/input/commands/compat-runtime.ts +32 -0
  116. package/src/input/commands/delegation-runtime.ts +29 -23
  117. package/src/input/commands/experience-runtime.ts +6 -4
  118. package/src/input/commands/guidance-runtime.ts +4 -4
  119. package/src/input/commands/health-runtime.ts +149 -126
  120. package/src/input/commands/knowledge.ts +82 -60
  121. package/src/input/commands/local-provider-runtime.ts +43 -21
  122. package/src/input/commands/local-runtime.ts +138 -16
  123. package/src/input/commands/local-setup-review.ts +10 -20
  124. package/src/input/commands/mcp-runtime.ts +63 -46
  125. package/src/input/commands/notify-runtime.ts +38 -9
  126. package/src/input/commands/operator-actions-runtime.ts +138 -0
  127. package/src/input/commands/operator-runtime.ts +6 -76
  128. package/src/input/commands/personas-runtime.ts +46 -31
  129. package/src/input/commands/planning-runtime.ts +1 -1
  130. package/src/input/commands/platform-access-runtime.ts +35 -98
  131. package/src/input/commands/product-runtime.ts +2 -2
  132. package/src/input/commands/provider-accounts-runtime.ts +60 -39
  133. package/src/input/commands/qrcode-runtime.ts +50 -9
  134. package/src/input/commands/recall-bundle.ts +11 -11
  135. package/src/input/commands/recall-capture.ts +13 -11
  136. package/src/input/commands/recall-query.ts +29 -21
  137. package/src/input/commands/recall-review.ts +2 -1
  138. package/src/input/commands/routines-runtime.ts +99 -45
  139. package/src/input/commands/runtime-services.ts +1 -14
  140. package/src/input/commands/schedule-runtime.ts +33 -20
  141. package/src/input/commands/security-runtime.ts +6 -11
  142. package/src/input/commands/session-content.ts +80 -78
  143. package/src/input/commands/session-workflow.ts +134 -104
  144. package/src/input/commands/session.ts +3 -174
  145. package/src/input/commands/shell-core.ts +38 -17
  146. package/src/input/commands/subscription-runtime.ts +54 -185
  147. package/src/input/commands/support-bundle-runtime.ts +53 -0
  148. package/src/input/commands/tasks-runtime.ts +21 -21
  149. package/src/input/commands/work-plan-runtime.ts +33 -8
  150. package/src/input/commands.ts +6 -2
  151. package/src/input/feed-context-factory.ts +2 -2
  152. package/src/input/file-picker.ts +3 -2
  153. package/src/input/handler-command-route.ts +4 -7
  154. package/src/input/handler-content-actions.ts +89 -26
  155. package/src/input/handler-feed-routes.ts +19 -12
  156. package/src/input/handler-feed.ts +6 -5
  157. package/src/input/handler-interactions.ts +9 -36
  158. package/src/input/handler-modal-stack.ts +3 -3
  159. package/src/input/handler-onboarding.ts +52 -91
  160. package/src/input/handler-shortcuts.ts +16 -7
  161. package/src/input/handler.ts +50 -23
  162. package/src/input/input-history.ts +5 -6
  163. package/src/input/keybindings.ts +27 -16
  164. package/src/input/mcp-workspace.ts +90 -26
  165. package/src/input/onboarding/handler-onboarding-routes.ts +3 -1
  166. package/src/input/onboarding/onboarding-wizard-apply.ts +62 -0
  167. package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
  168. package/src/input/onboarding/onboarding-wizard-helpers.ts +1 -1
  169. package/src/input/onboarding/onboarding-wizard-operator-steps.ts +223 -55
  170. package/src/input/onboarding/onboarding-wizard-steps.ts +59 -35
  171. package/src/input/onboarding/onboarding-wizard-types.ts +4 -1
  172. package/src/input/onboarding/onboarding-wizard.ts +87 -2
  173. package/src/input/profile-picker-modal.ts +31 -12
  174. package/src/input/session-picker-modal.ts +21 -4
  175. package/src/input/settings-modal-agent-policy.ts +6 -6
  176. package/src/input/settings-modal-behavior.ts +0 -3
  177. package/src/input/settings-modal-subscriptions.ts +3 -3
  178. package/src/input/settings-modal-types.ts +2 -13
  179. package/src/input/settings-modal.ts +20 -66
  180. package/src/input/submission-intent.ts +0 -1
  181. package/src/input/submission-router.ts +3 -3
  182. package/src/main.ts +18 -10
  183. package/src/panels/approval-panel.ts +8 -8
  184. package/src/panels/automation-control-panel.ts +4 -4
  185. package/src/panels/base-panel.ts +1 -1
  186. package/src/panels/builtin/agent.ts +1 -1
  187. package/src/panels/builtin/operations.ts +3 -12
  188. package/src/panels/builtin/session.ts +32 -24
  189. package/src/panels/builtin/shared.ts +7 -7
  190. package/src/panels/cost-tracker-panel.ts +1 -1
  191. package/src/panels/docs-panel.ts +5 -3
  192. package/src/panels/index.ts +0 -1
  193. package/src/panels/knowledge-panel.ts +6 -5
  194. package/src/panels/memory-panel.ts +7 -6
  195. package/src/panels/panel-list-panel.ts +36 -80
  196. package/src/panels/project-planning-panel.ts +19 -12
  197. package/src/panels/provider-account-snapshot.ts +51 -25
  198. package/src/panels/provider-accounts-panel.ts +33 -18
  199. package/src/panels/provider-health-domains.ts +48 -7
  200. package/src/panels/provider-health-panel.ts +5 -4
  201. package/src/panels/qr-panel.ts +44 -20
  202. package/src/panels/schedule-panel.ts +9 -17
  203. package/src/panels/security-panel.ts +8 -8
  204. package/src/panels/session-browser-panel.ts +10 -10
  205. package/src/panels/subscription-panel.ts +6 -6
  206. package/src/panels/system-messages-panel.ts +3 -3
  207. package/src/panels/tasks-panel.ts +21 -14
  208. package/src/panels/tool-inspector-panel.ts +19 -12
  209. package/src/panels/work-plan-panel.ts +5 -5
  210. package/src/planning/project-planning-coordinator.ts +3 -3
  211. package/src/provider-auth-route-display.ts +9 -0
  212. package/src/renderer/agent-workspace-style.ts +34 -0
  213. package/src/renderer/agent-workspace.ts +254 -57
  214. package/src/renderer/autocomplete-overlay.ts +25 -6
  215. package/src/renderer/block-actions.ts +1 -3
  216. package/src/renderer/bookmark-modal.ts +19 -4
  217. package/src/renderer/buffer.ts +4 -2
  218. package/src/renderer/context-inspector.ts +50 -13
  219. package/src/renderer/diff.ts +1 -1
  220. package/src/renderer/file-picker-overlay.ts +19 -6
  221. package/src/renderer/help-overlay.ts +112 -34
  222. package/src/renderer/history-search-overlay.ts +19 -4
  223. package/src/renderer/live-tail-modal.ts +27 -5
  224. package/src/renderer/mcp-workspace.ts +176 -43
  225. package/src/renderer/model-picker-overlay.ts +58 -3
  226. package/src/renderer/model-workspace.ts +104 -22
  227. package/src/renderer/onboarding/onboarding-wizard.ts +20 -4
  228. package/src/renderer/process-modal.ts +27 -6
  229. package/src/renderer/profile-picker-modal.ts +21 -5
  230. package/src/renderer/search-overlay.ts +25 -5
  231. package/src/renderer/selection-modal-overlay.ts +46 -14
  232. package/src/renderer/session-picker-modal.ts +18 -1
  233. package/src/renderer/settings-modal-helpers.ts +2 -40
  234. package/src/renderer/settings-modal.ts +88 -55
  235. package/src/renderer/system-message.ts +1 -1
  236. package/src/renderer/tool-call.ts +20 -11
  237. package/src/runtime/agent-runtime-events.ts +129 -0
  238. package/src/runtime/bootstrap-command-context.ts +7 -1
  239. package/src/runtime/bootstrap-command-parts.ts +11 -8
  240. package/src/runtime/bootstrap-core.ts +23 -65
  241. package/src/runtime/bootstrap-hook-bridge.ts +7 -18
  242. package/src/runtime/bootstrap-shell.ts +46 -8
  243. package/src/runtime/bootstrap.ts +36 -46
  244. package/src/runtime/connected-host-auth.ts +47 -0
  245. package/src/runtime/diagnostics/panels/index.ts +2 -2
  246. package/src/runtime/diagnostics/panels/panel-resources.ts +1 -1
  247. package/src/runtime/diagnostics/panels/policy.ts +7 -7
  248. package/src/runtime/index.ts +2 -5
  249. package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
  250. package/src/runtime/onboarding/apply.ts +288 -74
  251. package/src/runtime/onboarding/derivation.ts +10 -13
  252. package/src/runtime/onboarding/snapshot.ts +2 -17
  253. package/src/runtime/onboarding/types.ts +34 -20
  254. package/src/runtime/onboarding/verify.ts +105 -4
  255. package/src/runtime/operator-token-cleanup.ts +3 -3
  256. package/src/runtime/services.ts +95 -11
  257. package/src/runtime/store/selectors/index.ts +3 -3
  258. package/src/runtime/store/state.ts +1 -4
  259. package/src/runtime/surface-feature-flags.ts +41 -6
  260. package/src/runtime/ui-read-models.ts +3 -4
  261. package/src/runtime/ui-services.ts +6 -6
  262. package/src/shell/blocking-input.ts +2 -1
  263. package/src/shell/service-settings-sync.ts +7 -7
  264. package/src/shell/ui-openers.ts +17 -38
  265. package/src/tools/agent-analysis-registry-policy.ts +0 -1
  266. package/src/tools/agent-channel-send-tool.ts +133 -0
  267. package/src/tools/agent-context-policy.ts +1 -1
  268. package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
  269. package/src/tools/agent-knowledge-tool.ts +170 -0
  270. package/src/tools/agent-local-registry-tool.ts +279 -73
  271. package/src/tools/agent-media-generate-tool.ts +133 -0
  272. package/src/tools/agent-notify-tool.ts +143 -0
  273. package/src/tools/agent-operator-action-tool.ts +137 -0
  274. package/src/tools/agent-operator-briefing-tool.ts +217 -0
  275. package/src/tools/agent-reminder-schedule-tool.ts +237 -0
  276. package/src/tools/agent-tool-policy-guard.ts +13 -15
  277. package/src/tools/agent-work-plan-tool.ts +256 -0
  278. package/src/version.ts +1 -1
  279. package/docs/connected-services.md +0 -51
  280. package/src/cli/package-verification.ts +0 -274
  281. package/src/input/commands/policy-dispatch.ts +0 -339
  282. package/src/input/commands/policy.ts +0 -17
  283. package/src/panels/panel-picker.ts +0 -105
  284. package/src/panels/policy-panel.ts +0 -308
  285. package/src/renderer/panel-picker-overlay.ts +0 -202
@@ -1,10 +1,12 @@
1
1
  import { createShellPathService } from '@/runtime/index.ts';
2
+ import { formatAgentRecordOrigin, formatAgentRecordReviewState } from '../agent/record-labels.ts';
2
3
  import { discoverRoutines, type DiscoveredRoutineRecord } from '../agent/routine-discovery.ts';
3
- import { AgentRoutineRegistry, type AgentRoutineRecord } from '../agent/routine-registry.ts';
4
+ import { AgentRoutineRegistry, evaluateAgentRoutineReadiness, type AgentRoutineRecord } from '../agent/routine-registry.ts';
5
+ import { buildAgentSkillRequirements, formatAgentSkillRequirement } from '../agent/skill-registry.ts';
4
6
  import {
5
7
  buildRoutineSchedulePreview,
6
- promoteRoutineToDaemonSchedule,
7
- resolveAgentDaemonConnection,
8
+ promoteRoutineToConnectedSchedule,
9
+ resolveAgentConnectedHostConnection,
8
10
  } from '../agent/routine-schedule-promotion.ts';
9
11
  import { parseRoutineSchedulePromotionArgs } from '../agent/routine-schedule-args.ts';
10
12
  import {
@@ -34,10 +36,26 @@ interface RoutinesCommandFailure {
34
36
  readonly error: string;
35
37
  }
36
38
 
39
+ interface ParsedRoutineOptions {
40
+ readonly positionals: readonly string[];
41
+ readonly flags: ReadonlyMap<string, string>;
42
+ readonly yes: boolean;
43
+ }
44
+
45
+ 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]';
46
+
37
47
  function jsonOrText(runtime: CliCommandRuntime, value: unknown, text: string): string {
38
48
  return runtime.cli.flags.outputFormat === 'json' ? JSON.stringify(value, null, 2) : text;
39
49
  }
40
50
 
51
+ function commandFailure(runtime: CliCommandRuntime, kind: string, error: string, exitCode: number): CliCommandOutput {
52
+ const failure: RoutinesCommandFailure = { ok: false, kind, error };
53
+ return {
54
+ output: runtime.cli.flags.outputFormat === 'json' ? JSON.stringify(failure, null, 2) : error,
55
+ exitCode,
56
+ };
57
+ }
58
+
41
59
  function routineRegistry(runtime: CliCommandRuntime): AgentRoutineRegistry {
42
60
  return AgentRoutineRegistry.fromShellPaths(createShellPathService({
43
61
  workingDirectory: runtime.workingDirectory,
@@ -64,6 +82,47 @@ function splitList(value: string | undefined): readonly string[] {
64
82
  return value.split(',').map((entry) => entry.trim()).filter(Boolean);
65
83
  }
66
84
 
85
+ function parseRoutineOptions(args: readonly string[]): ParsedRoutineOptions {
86
+ const flags = new Map<string, string>();
87
+ const positionals: string[] = [];
88
+ let yes = false;
89
+ for (let index = 0; index < args.length; index += 1) {
90
+ const token = args[index] ?? '';
91
+ if (token === '--yes') {
92
+ yes = true;
93
+ continue;
94
+ }
95
+ if (token.startsWith('--')) {
96
+ const key = token.slice(2);
97
+ const next = args[index + 1];
98
+ if (next !== undefined && !next.startsWith('--')) {
99
+ flags.set(key, next);
100
+ index += 1;
101
+ } else {
102
+ flags.set(key, 'true');
103
+ }
104
+ continue;
105
+ }
106
+ positionals.push(token);
107
+ }
108
+ return { positionals, flags, yes };
109
+ }
110
+
111
+ function optionValue(options: ParsedRoutineOptions, key: string): string | undefined {
112
+ const value = options.flags.get(key)?.trim();
113
+ return value ? value : undefined;
114
+ }
115
+
116
+ function hasFlag(options: ParsedRoutineOptions, key: string): boolean {
117
+ return options.flags.get(key) === 'true';
118
+ }
119
+
120
+ function requiredOption(options: ParsedRoutineOptions, key: string): string {
121
+ const value = optionValue(options, key);
122
+ if (!value) throw new Error(`${ROUTINE_CREATE_USAGE}\nMissing --${key}.`);
123
+ return value;
124
+ }
125
+
67
126
  function parseImportFlags(args: readonly string[]): {
68
127
  readonly name: string;
69
128
  readonly enabled: boolean;
@@ -88,15 +147,17 @@ function parseImportFlags(args: readonly string[]): {
88
147
 
89
148
  function summarizeRoutine(routine: AgentRoutineRecord): string {
90
149
  const enabled = routine.enabled ? 'enabled' : 'disabled';
91
- 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 tags = routine.tags.length > 0 ? ` tags ${routine.tags.join(', ')}` : '';
151
+ const readiness = evaluateAgentRoutineReadiness(routine);
152
+ const ready = readiness.ready ? 'ready' : `needs ${readiness.missing.map(formatAgentSkillRequirement).join(',')}`;
153
+ return ` ${routine.id} ${enabled} ${formatAgentRecordReviewState(routine.reviewState)} ${ready} starts ${routine.startCount} ${routine.name} - ${routine.description}${tags}`;
93
154
  }
94
155
 
95
156
  function summarizeDiscoveredRoutine(routine: DiscoveredRoutineRecord): string {
96
157
  const description = routine.description ? ` - ${routine.description}` : '';
97
158
  return [
98
159
  ` ${routine.name} ${routine.origin}${description}`,
99
- ` path: ${routine.path}`,
160
+ ` path ${routine.path}`,
100
161
  ].join('\n');
101
162
  }
102
163
 
@@ -136,35 +197,46 @@ function discoveredRoutineFrontmatterList(routine: DiscoveredRoutineRecord, key:
136
197
  return splitList(value);
137
198
  }
138
199
 
139
- function renderRoutineList(title: string, path: string, routines: readonly AgentRoutineRecord[]): string {
200
+ function discoveredRoutineFrontmatterAnyList(routine: DiscoveredRoutineRecord, keys: readonly string[]): readonly string[] {
201
+ for (const key of keys) {
202
+ const values = discoveredRoutineFrontmatterList(routine, key);
203
+ if (values.length > 0) return values;
204
+ }
205
+ return [];
206
+ }
207
+
208
+ function renderRoutineList(title: string, path: string, routines: readonly AgentRoutineRecord[], emptyMessage?: string): string {
140
209
  if (routines.length === 0) {
141
210
  return [
142
211
  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');
212
+ ` ${emptyMessage ?? 'No Agent-local routines yet.'}`,
213
+ emptyMessage ? '' : ' Open /agent routines in the TUI, or use goodvibes-agent routines create for scripted setup.',
214
+ ].filter(Boolean).join('\n');
146
215
  }
147
216
  return [
148
217
  `${title} (${routines.length})`,
149
- ` store: ${path}`,
218
+ ` store ${path}`,
150
219
  ...routines.map(summarizeRoutine),
151
220
  ].join('\n');
152
221
  }
153
222
 
154
223
  function renderRoutine(routine: AgentRoutineRecord): string {
224
+ const readiness = evaluateAgentRoutineReadiness(routine);
155
225
  return [
156
226
  `Routine ${routine.name}`,
157
- ` id: ${routine.id}`,
158
- ` enabled: ${routine.enabled ? 'yes' : 'no'}`,
159
- ` review: ${routine.reviewState}`,
160
- ` source: ${routine.source}`,
161
- ` provenance: ${routine.provenance}`,
162
- ` tags: ${routine.tags.join(', ') || '(none)'}`,
163
- ` triggers: ${routine.triggers.join(', ') || '(manual)'}`,
164
- ` started: ${routine.startCount}${routine.lastStartedAt ? `; last ${routine.lastStartedAt}` : ''}`,
165
- ` created: ${routine.createdAt}`,
166
- ` updated: ${routine.updatedAt}`,
167
- routine.staleReason ? ` stale reason: ${routine.staleReason}` : '',
227
+ ` id ${routine.id}`,
228
+ ` enabled ${routine.enabled ? 'yes' : 'no'}`,
229
+ ` readiness ${readiness.ready ? 'ready' : 'needs setup'}`,
230
+ ` requirements ${routine.requirements.map(formatAgentSkillRequirement).join(', ') || '(none)'}`,
231
+ readiness.missing.length > 0 ? ` missing ${readiness.missing.map(formatAgentSkillRequirement).join(', ')}` : '',
232
+ ` review ${formatAgentRecordReviewState(routine.reviewState)}`,
233
+ ` origin ${formatAgentRecordOrigin(routine.source, routine.provenance)}`,
234
+ ` tags ${routine.tags.join(', ') || '(none)'}`,
235
+ ` triggers ${routine.triggers.join(', ') || '(manual)'}`,
236
+ ` started ${routine.startCount}${routine.lastStartedAt ? `; last ${routine.lastStartedAt}` : ''}`,
237
+ ` created ${routine.createdAt}`,
238
+ ` updated ${routine.updatedAt}`,
239
+ routine.staleReason ? ` stale reason ${routine.staleReason}` : '',
168
240
  '',
169
241
  routine.description,
170
242
  '',
@@ -195,7 +267,7 @@ async function handleRoutinePromotion(runtime: CliCommandRuntime, args: readonly
195
267
  const failure: RoutinesCommandFailure = {
196
268
  ok: false,
197
269
  kind: 'routine_not_found',
198
- error: `Unknown Agent routine: ${parsed.routineId ?? ''}`,
270
+ error: `Unknown Agent routine ${parsed.routineId ?? ''}`,
199
271
  };
200
272
  return {
201
273
  output: json ? JSON.stringify(failure, null, 2) : failure.error,
@@ -214,8 +286,8 @@ async function handleRoutinePromotion(runtime: CliCommandRuntime, args: readonly
214
286
  exitCode: 0,
215
287
  };
216
288
  }
217
- const connection = resolveAgentDaemonConnection(runtime.configManager, runtime.homeDirectory);
218
- const result = await promoteRoutineToDaemonSchedule(connection, preview);
289
+ const connection = resolveAgentConnectedHostConnection(runtime.configManager, runtime.homeDirectory);
290
+ const result = await promoteRoutineToConnectedSchedule(connection, preview);
219
291
  const receipt = routineReceiptStore(runtime).append(connection, preview, result);
220
292
  if (!result.ok) {
221
293
  return {
@@ -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 Agent-local routines need setup.' : undefined,
343
+ )),
255
344
  exitCode: 0,
256
345
  };
257
346
  }
@@ -285,7 +374,7 @@ export async function handleRoutinesCommand(runtime: CliCommandRuntime): Promise
285
374
  const failure: RoutinesCommandFailure = {
286
375
  ok: false,
287
376
  kind: 'routine_discovery_not_found',
288
- error: `Unknown discovered Agent routine: ${parsed.name}\nRun goodvibes-agent routines discover to inspect available routine files.`,
377
+ error: `Unknown discovered Agent routine ${parsed.name}\nRun goodvibes-agent routines discover to inspect available routine files.`,
289
378
  };
290
379
  return {
291
380
  output: runtime.cli.flags.outputFormat === 'json' ? JSON.stringify(failure, null, 2) : failure.error,
@@ -301,12 +390,12 @@ export async function handleRoutinesCommand(runtime: CliCommandRuntime): Promise
301
390
  return {
302
391
  output: jsonOrText(runtime, value, [
303
392
  'Agent routine import preview',
304
- ` name: ${discovered.name}`,
305
- ` origin: ${discovered.origin}`,
306
- ` path: ${discovered.path}`,
307
- ` description: ${discovered.description || '(none)'}`,
308
- ` steps characters: ${discovered.steps.length}`,
309
- ' next: rerun with --yes to import into the Agent-local routine registry',
393
+ ` name ${discovered.name}`,
394
+ ` origin ${discovered.origin}`,
395
+ ` path ${discovered.path}`,
396
+ ` description ${discovered.description || '(none)'}`,
397
+ ` steps characters ${discovered.steps.length}`,
398
+ ' next rerun with --yes to import into the Agent-local routine registry',
310
399
  ].join('\n')),
311
400
  exitCode: 0,
312
401
  };
@@ -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}`,
@@ -327,10 +420,53 @@ export async function handleRoutinesCommand(runtime: CliCommandRuntime): Promise
327
420
  data: routine,
328
421
  };
329
422
  return {
330
- output: jsonOrText(runtime, value, `Imported Agent routine ${routine.id}: ${routine.name}${routine.enabled ? ' (enabled)' : ''}`),
423
+ output: jsonOrText(runtime, value, [
424
+ `Imported Agent routine ${routine.id}: ${routine.name}${routine.enabled ? ' (enabled)' : ''}`,
425
+ ` name ${routine.name}`,
426
+ ` enabled ${routine.enabled ? 'yes' : 'no'}`,
427
+ ].join('\n')),
331
428
  exitCode: 0,
332
429
  };
333
430
  }
431
+ if (normalized === 'create') {
432
+ const options = parseRoutineOptions(rest);
433
+ try {
434
+ const routine = registry.create({
435
+ name: requiredOption(options, 'name'),
436
+ description: requiredOption(options, 'description'),
437
+ steps: optionValue(options, 'steps') ?? options.positionals.join(' ').trim(),
438
+ tags: splitList(optionValue(options, 'tags')),
439
+ triggers: splitList(optionValue(options, 'triggers')),
440
+ requirements: buildAgentSkillRequirements({
441
+ env: splitList(optionValue(options, 'requires-env')),
442
+ commands: splitList(optionValue(options, 'requires-command') ?? optionValue(options, 'requires-commands')),
443
+ }),
444
+ enabled: hasFlag(options, 'enabled'),
445
+ source: 'user',
446
+ provenance: 'cli',
447
+ });
448
+ const value: RoutinesCommandSuccess<AgentRoutineRecord> = {
449
+ ok: true,
450
+ kind: 'agent.routines.create',
451
+ data: routine,
452
+ };
453
+ return {
454
+ output: jsonOrText(runtime, value, [
455
+ `Created Agent routine ${routine.id}: ${routine.name}${routine.enabled ? ' (enabled)' : ''}`,
456
+ ` name ${routine.name}`,
457
+ ` enabled ${routine.enabled ? 'yes' : 'no'}`,
458
+ ].join('\n')),
459
+ exitCode: 0,
460
+ };
461
+ } catch (error) {
462
+ return commandFailure(
463
+ runtime,
464
+ error instanceof Error && error.message.startsWith('Usage:') ? 'invalid_routine_command' : 'agent.routines.create.error',
465
+ error instanceof Error ? error.message : String(error),
466
+ error instanceof Error && error.message.startsWith('Usage:') ? 2 : 1,
467
+ );
468
+ }
469
+ }
334
470
  if (normalized === 'show') {
335
471
  const id = rest[0];
336
472
  if (!id) return { output: 'Usage: goodvibes-agent routines show <id>', exitCode: 2 };
@@ -363,7 +499,7 @@ export async function handleRoutinesCommand(runtime: CliCommandRuntime): Promise
363
499
  if (normalized === 'reconcile' || normalized === 'sync' || normalized === 'status') {
364
500
  const store = routineReceiptStore(runtime);
365
501
  const result = await reconcileRoutineScheduleReceipts(
366
- resolveAgentDaemonConnection(runtime.configManager, runtime.homeDirectory),
502
+ resolveAgentConnectedHostConnection(runtime.configManager, runtime.homeDirectory),
367
503
  store.snapshot(),
368
504
  );
369
505
  return {
@@ -376,7 +512,7 @@ export async function handleRoutinesCommand(runtime: CliCommandRuntime): Promise
376
512
  if (!id) return { output: 'Usage: goodvibes-agent routines receipt <receipt-id>', exitCode: 2 };
377
513
  const receipt = routineReceiptStore(runtime).get(id);
378
514
  if (!receipt) {
379
- const failure: RoutinesCommandFailure = { ok: false, kind: 'routine_schedule_receipt_not_found', error: `Unknown routine schedule receipt: ${id}` };
515
+ const failure: RoutinesCommandFailure = { ok: false, kind: 'routine_schedule_receipt_not_found', error: `Unknown routine schedule receipt ${id}` };
380
516
  return {
381
517
  output: runtime.cli.flags.outputFormat === 'json' ? JSON.stringify(failure, null, 2) : failure.error,
382
518
  exitCode: 1,
@@ -396,7 +532,7 @@ export async function handleRoutinesCommand(runtime: CliCommandRuntime): Promise
396
532
  return handleRoutinePromotion(runtime, rest);
397
533
  }
398
534
  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]',
535
+ 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
536
  exitCode: 2,
401
537
  };
402
538
  }
@@ -1,7 +1,7 @@
1
1
  import { closeSync, existsSync, openSync, readSync, statSync } from 'node:fs';
2
2
  import net from 'node:net';
3
3
  import { isAbsolute, join } from 'node:path';
4
- import type { ConfigManager } from '../config/index.ts';
4
+ import type { ConfigKey, ConfigManager } from '../config/index.ts';
5
5
  import { resolveRuntimeEndpointBinding } from './endpoints.ts';
6
6
  import type { RuntimeEndpointBinding, RuntimeEndpointId } from './endpoints.ts';
7
7
  import { classifyBindPosture, isNetworkFacing } from './network-posture.ts';
@@ -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,13 +54,13 @@ 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[];
61
61
  }
62
62
 
63
- const ENDPOINTS: readonly { readonly id: RuntimeEndpointId; readonly label: string; readonly enabledKey: string }[] = [
63
+ const ENDPOINTS: readonly { readonly id: RuntimeEndpointId; readonly label: string; readonly enabledKey: ConfigKey }[] = [
64
64
  { id: 'controlPlane', label: 'runtime connection', enabledKey: 'controlPlane.enabled' },
65
65
  { id: 'httpListener', label: 'inbound events endpoint', enabledKey: 'danger.httpListener' },
66
66
  { id: 'web', label: 'browser companion route', enabledKey: 'web.enabled' },
@@ -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
  }
@@ -161,7 +161,7 @@ export async function buildCliServicePosture(
161
161
  options: CliServicePostureOptions = {},
162
162
  ): Promise<CliServicePosture> {
163
163
  const endpoints = await Promise.all(ENDPOINTS.map(async (endpoint): Promise<CliServiceEndpointPosture> => {
164
- const enabled = runtime.configManager.get(endpoint.enabledKey as never) === true;
164
+ const enabled = runtime.configManager.get(endpoint.enabledKey) === true;
165
165
  const binding = resolveRuntimeEndpointBinding(runtime.configManager, endpoint.id);
166
166
  return {
167
167
  id: endpoint.id,
@@ -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,21 +223,21 @@ 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
- ` ${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)}`}`,
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
  ),
239
239
  '',
240
- posture.issues.length === 0 ? 'Readiness: ready' : 'Readiness: needs attention',
240
+ posture.issues.length === 0 ? 'Readiness ready' : 'Readiness needs attention',
241
241
  ...posture.issues.map((issue) => ` - ${issue}`),
242
242
  ].join('\n');
243
243
  }