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