@pellux/goodvibes-agent 0.1.117 → 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 (253) hide show
  1. package/CHANGELOG.md +13 -4
  2. package/README.md +35 -14
  3. package/bin/goodvibes-agent.ts +16 -2
  4. package/dist/package/main.js +176073 -170980
  5. package/docs/README.md +11 -5
  6. package/docs/channels-remote-and-api.md +50 -0
  7. package/docs/connected-host.md +3 -3
  8. package/docs/getting-started.md +29 -15
  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 +44 -9
  13. package/docs/tools-and-commands.md +123 -0
  14. package/docs/voice-and-live-tts.md +51 -0
  15. package/package.json +2 -5
  16. package/src/agent/channel-delivery.ts +201 -0
  17. package/src/agent/media-generation.ts +159 -0
  18. package/src/agent/memory-prompt.ts +0 -1
  19. package/src/agent/note-registry.ts +329 -0
  20. package/src/agent/operator-actions.ts +343 -0
  21. package/src/agent/persona-registry.ts +15 -14
  22. package/src/agent/record-labels.ts +107 -0
  23. package/src/agent/reminder-schedule-format.ts +33 -24
  24. package/src/agent/reminder-schedule.ts +26 -25
  25. package/src/agent/routine-registry.ts +13 -12
  26. package/src/agent/routine-schedule-args.ts +2 -1
  27. package/src/agent/routine-schedule-format.ts +77 -53
  28. package/src/agent/routine-schedule-promotion.ts +34 -32
  29. package/src/agent/routine-schedule-receipts.ts +28 -26
  30. package/src/agent/runtime-profile-starters.ts +2 -2
  31. package/src/agent/runtime-profile.ts +18 -17
  32. package/src/agent/skill-registry.ts +25 -24
  33. package/src/cli/agent-knowledge-args.ts +5 -1
  34. package/src/cli/agent-knowledge-command.ts +39 -33
  35. package/src/cli/agent-knowledge-format.ts +80 -67
  36. package/src/cli/agent-knowledge-methods.ts +1 -1
  37. package/src/cli/agent-knowledge-runtime.ts +32 -26
  38. package/src/cli/bundle-command.ts +13 -8
  39. package/src/cli/config-overrides.ts +37 -36
  40. package/src/cli/external-runtime.ts +10 -4
  41. package/src/cli/help.ts +29 -11
  42. package/src/cli/local-library-command.ts +134 -68
  43. package/src/cli/management-commands.ts +98 -62
  44. package/src/cli/management.ts +52 -26
  45. package/src/cli/memory-command.ts +66 -32
  46. package/src/cli/parser.ts +37 -24
  47. package/src/cli/profiles-command.ts +52 -35
  48. package/src/cli/provider-auth-routes.ts +2 -1
  49. package/src/cli/routines-command.ts +44 -36
  50. package/src/cli/service-posture.ts +6 -6
  51. package/src/cli/status.ts +46 -121
  52. package/src/core/conversation-message-snapshot.ts +131 -0
  53. package/src/input/agent-workspace-activation.ts +33 -7
  54. package/src/input/agent-workspace-basic-command-editor-submission.ts +7 -3
  55. package/src/input/agent-workspace-basic-command-editors.ts +30 -10
  56. package/src/input/agent-workspace-categories.ts +276 -64
  57. package/src/input/agent-workspace-channel-command-editor-submission.ts +34 -0
  58. package/src/input/agent-workspace-channel-command-editors.ts +23 -5
  59. package/src/input/agent-workspace-channels.ts +35 -2
  60. package/src/input/agent-workspace-command-editor.ts +18 -2
  61. package/src/input/agent-workspace-config-reader.ts +16 -0
  62. package/src/input/agent-workspace-delegation-editor-submission.ts +1 -1
  63. package/src/input/agent-workspace-editors.ts +140 -2
  64. package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
  65. package/src/input/agent-workspace-learned-behavior.ts +2 -2
  66. package/src/input/agent-workspace-library-command-editor-submission.ts +1 -1
  67. package/src/input/agent-workspace-library-command-editors.ts +2 -2
  68. package/src/input/agent-workspace-local-operations.ts +218 -0
  69. package/src/input/agent-workspace-local-selection.ts +75 -0
  70. package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
  71. package/src/input/agent-workspace-media-command-editors.ts +27 -0
  72. package/src/input/agent-workspace-memory-command-editors.ts +1 -1
  73. package/src/input/agent-workspace-memory-editor.ts +2 -2
  74. package/src/input/agent-workspace-navigation.ts +38 -2
  75. package/src/input/agent-workspace-notify-editor-submission.ts +16 -2
  76. package/src/input/agent-workspace-operations-command-editor-submission.ts +63 -1
  77. package/src/input/agent-workspace-operations-command-editors.ts +80 -3
  78. package/src/input/agent-workspace-panel-route.ts +43 -0
  79. package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
  80. package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
  81. package/src/input/agent-workspace-search.ts +169 -0
  82. package/src/input/agent-workspace-setup.ts +22 -11
  83. package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +6 -6
  84. package/src/input/agent-workspace-skill-bundle-command-editors.ts +10 -10
  85. package/src/input/agent-workspace-snapshot.ts +112 -13
  86. package/src/input/agent-workspace-task-command-editors.ts +4 -4
  87. package/src/input/agent-workspace-token.ts +18 -2
  88. package/src/input/agent-workspace-types.ts +92 -4
  89. package/src/input/agent-workspace-voice-media.ts +3 -6
  90. package/src/input/agent-workspace-web-research-editor.ts +104 -0
  91. package/src/input/agent-workspace.ts +136 -208
  92. package/src/input/command-registry.ts +4 -1
  93. package/src/input/commands/agent-runtime-profile-runtime.ts +45 -41
  94. package/src/input/commands/agent-skills-runtime.ts +83 -70
  95. package/src/input/commands/agent-workspace-runtime.ts +23 -7
  96. package/src/input/commands/brief-runtime.ts +25 -24
  97. package/src/input/commands/channels-runtime.ts +145 -31
  98. package/src/input/commands/delegation-runtime.ts +29 -23
  99. package/src/input/commands/experience-runtime.ts +6 -4
  100. package/src/input/commands/guidance-runtime.ts +4 -4
  101. package/src/input/commands/health-runtime.ts +140 -115
  102. package/src/input/commands/knowledge.ts +57 -56
  103. package/src/input/commands/local-provider-runtime.ts +36 -18
  104. package/src/input/commands/local-runtime.ts +138 -16
  105. package/src/input/commands/local-setup-review.ts +7 -7
  106. package/src/input/commands/mcp-runtime.ts +56 -35
  107. package/src/input/commands/notify-runtime.ts +38 -9
  108. package/src/input/commands/operator-actions-runtime.ts +138 -0
  109. package/src/input/commands/operator-runtime.ts +6 -17
  110. package/src/input/commands/personas-runtime.ts +45 -30
  111. package/src/input/commands/planning-runtime.ts +1 -1
  112. package/src/input/commands/platform-access-runtime.ts +29 -29
  113. package/src/input/commands/provider-accounts-runtime.ts +60 -39
  114. package/src/input/commands/qrcode-runtime.ts +45 -6
  115. package/src/input/commands/recall-bundle.ts +11 -11
  116. package/src/input/commands/recall-capture.ts +13 -11
  117. package/src/input/commands/recall-query.ts +29 -21
  118. package/src/input/commands/recall-review.ts +2 -1
  119. package/src/input/commands/routines-runtime.ts +59 -43
  120. package/src/input/commands/schedule-runtime.ts +33 -20
  121. package/src/input/commands/security-runtime.ts +4 -4
  122. package/src/input/commands/session-content.ts +80 -78
  123. package/src/input/commands/session-workflow.ts +132 -93
  124. package/src/input/commands/session.ts +3 -174
  125. package/src/input/commands/shell-core.ts +32 -17
  126. package/src/input/commands/subscription-runtime.ts +53 -179
  127. package/src/input/commands/tasks-runtime.ts +20 -20
  128. package/src/input/commands/work-plan-runtime.ts +33 -8
  129. package/src/input/commands.ts +2 -2
  130. package/src/input/feed-context-factory.ts +2 -1
  131. package/src/input/file-picker.ts +3 -2
  132. package/src/input/handler-command-route.ts +4 -7
  133. package/src/input/handler-content-actions.ts +89 -1
  134. package/src/input/handler-feed-routes.ts +19 -12
  135. package/src/input/handler-feed.ts +6 -3
  136. package/src/input/handler-interactions.ts +7 -5
  137. package/src/input/handler-modal-stack.ts +3 -3
  138. package/src/input/handler-onboarding.ts +24 -80
  139. package/src/input/handler-shortcuts.ts +15 -4
  140. package/src/input/handler.ts +47 -17
  141. package/src/input/input-history.ts +5 -6
  142. package/src/input/keybindings.ts +22 -11
  143. package/src/input/onboarding/onboarding-wizard-apply.ts +13 -0
  144. package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
  145. package/src/input/onboarding/onboarding-wizard-operator-steps.ts +147 -57
  146. package/src/input/onboarding/onboarding-wizard-steps.ts +46 -29
  147. package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
  148. package/src/input/onboarding/onboarding-wizard.ts +68 -0
  149. package/src/input/profile-picker-modal.ts +31 -12
  150. package/src/input/session-picker-modal.ts +21 -4
  151. package/src/input/settings-modal-behavior.ts +0 -3
  152. package/src/input/settings-modal-subscriptions.ts +3 -3
  153. package/src/input/settings-modal-types.ts +2 -13
  154. package/src/input/settings-modal.ts +6 -52
  155. package/src/input/submission-intent.ts +0 -1
  156. package/src/input/submission-router.ts +3 -3
  157. package/src/main.ts +18 -8
  158. package/src/panels/approval-panel.ts +8 -8
  159. package/src/panels/automation-control-panel.ts +2 -2
  160. package/src/panels/base-panel.ts +1 -1
  161. package/src/panels/builtin/agent.ts +1 -1
  162. package/src/panels/builtin/operations.ts +1 -10
  163. package/src/panels/builtin/session.ts +9 -9
  164. package/src/panels/builtin/shared.ts +2 -2
  165. package/src/panels/cost-tracker-panel.ts +1 -1
  166. package/src/panels/docs-panel.ts +5 -3
  167. package/src/panels/index.ts +0 -1
  168. package/src/panels/knowledge-panel.ts +6 -5
  169. package/src/panels/memory-panel.ts +7 -6
  170. package/src/panels/panel-list-panel.ts +36 -80
  171. package/src/panels/project-planning-panel.ts +18 -11
  172. package/src/panels/provider-account-snapshot.ts +51 -25
  173. package/src/panels/provider-accounts-panel.ts +33 -18
  174. package/src/panels/provider-health-domains.ts +45 -4
  175. package/src/panels/provider-health-panel.ts +5 -4
  176. package/src/panels/qr-panel.ts +1 -1
  177. package/src/panels/schedule-panel.ts +9 -17
  178. package/src/panels/security-panel.ts +8 -8
  179. package/src/panels/session-browser-panel.ts +10 -10
  180. package/src/panels/subscription-panel.ts +3 -3
  181. package/src/panels/system-messages-panel.ts +1 -1
  182. package/src/panels/tasks-panel.ts +20 -13
  183. package/src/panels/tool-inspector-panel.ts +19 -12
  184. package/src/panels/work-plan-panel.ts +5 -5
  185. package/src/planning/project-planning-coordinator.ts +1 -1
  186. package/src/provider-auth-route-display.ts +9 -0
  187. package/src/renderer/agent-workspace-style.ts +34 -0
  188. package/src/renderer/agent-workspace.ts +228 -52
  189. package/src/renderer/autocomplete-overlay.ts +25 -6
  190. package/src/renderer/bookmark-modal.ts +19 -4
  191. package/src/renderer/buffer.ts +4 -2
  192. package/src/renderer/context-inspector.ts +50 -13
  193. package/src/renderer/diff.ts +1 -1
  194. package/src/renderer/file-picker-overlay.ts +19 -6
  195. package/src/renderer/help-overlay.ts +106 -33
  196. package/src/renderer/history-search-overlay.ts +19 -4
  197. package/src/renderer/live-tail-modal.ts +27 -5
  198. package/src/renderer/mcp-workspace.ts +164 -50
  199. package/src/renderer/model-picker-overlay.ts +58 -2
  200. package/src/renderer/model-workspace.ts +104 -20
  201. package/src/renderer/process-modal.ts +27 -6
  202. package/src/renderer/profile-picker-modal.ts +20 -4
  203. package/src/renderer/search-overlay.ts +25 -5
  204. package/src/renderer/selection-modal-overlay.ts +46 -14
  205. package/src/renderer/session-picker-modal.ts +18 -1
  206. package/src/renderer/settings-modal-helpers.ts +2 -40
  207. package/src/renderer/settings-modal.ts +83 -50
  208. package/src/renderer/tool-call.ts +20 -11
  209. package/src/runtime/agent-runtime-events.ts +6 -6
  210. package/src/runtime/bootstrap-command-context.ts +7 -1
  211. package/src/runtime/bootstrap-command-parts.ts +9 -7
  212. package/src/runtime/bootstrap-core.ts +19 -1
  213. package/src/runtime/bootstrap-hook-bridge.ts +7 -18
  214. package/src/runtime/bootstrap-shell.ts +4 -4
  215. package/src/runtime/bootstrap.ts +31 -22
  216. package/src/runtime/connected-host-auth.ts +7 -2
  217. package/src/runtime/diagnostics/panels/index.ts +2 -2
  218. package/src/runtime/diagnostics/panels/policy.ts +7 -7
  219. package/src/runtime/index.ts +2 -1
  220. package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
  221. package/src/runtime/onboarding/apply.ts +80 -79
  222. package/src/runtime/onboarding/derivation.ts +5 -8
  223. package/src/runtime/onboarding/snapshot.ts +0 -15
  224. package/src/runtime/onboarding/types.ts +8 -19
  225. package/src/runtime/onboarding/verify.ts +32 -10
  226. package/src/runtime/operator-token-cleanup.ts +1 -1
  227. package/src/runtime/services.ts +91 -22
  228. package/src/runtime/store/selectors/index.ts +3 -3
  229. package/src/runtime/store/state.ts +1 -4
  230. package/src/runtime/surface-feature-flags.ts +41 -6
  231. package/src/runtime/ui-read-models.ts +3 -4
  232. package/src/runtime/ui-services.ts +6 -6
  233. package/src/shell/blocking-input.ts +2 -1
  234. package/src/shell/ui-openers.ts +3 -13
  235. package/src/tools/agent-analysis-registry-policy.ts +0 -1
  236. package/src/tools/agent-channel-send-tool.ts +133 -0
  237. package/src/tools/agent-context-policy.ts +1 -1
  238. package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
  239. package/src/tools/agent-knowledge-tool.ts +170 -0
  240. package/src/tools/agent-local-registry-tool.ts +269 -69
  241. package/src/tools/agent-media-generate-tool.ts +133 -0
  242. package/src/tools/agent-notify-tool.ts +143 -0
  243. package/src/tools/agent-operator-action-tool.ts +137 -0
  244. package/src/tools/agent-operator-briefing-tool.ts +217 -0
  245. package/src/tools/agent-reminder-schedule-tool.ts +237 -0
  246. package/src/tools/agent-tool-policy-guard.ts +8 -8
  247. package/src/tools/agent-work-plan-tool.ts +256 -0
  248. package/src/version.ts +1 -1
  249. package/src/input/commands/policy-dispatch.ts +0 -339
  250. package/src/input/commands/policy.ts +0 -13
  251. package/src/panels/panel-picker.ts +0 -105
  252. package/src/panels/policy-panel.ts +0 -308
  253. package/src/renderer/panel-picker-overlay.ts +0 -202
@@ -12,10 +12,14 @@ type KnowledgeAskInput = Parameters<KnowledgeService['ask']>[0];
12
12
  type KnowledgeAskResult = Awaited<ReturnType<KnowledgeService['ask']>>;
13
13
  type KnowledgeAskMode = NonNullable<KnowledgeAskInput['mode']>;
14
14
 
15
+ function isRecord(value: unknown): value is Record<string, unknown> {
16
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
17
+ }
18
+
15
19
  function requireAgentKnowledgeApi(context: CommandContext) {
16
20
  const knowledgeApi = context.clients?.agentKnowledgeApi;
17
21
  if (!knowledgeApi) {
18
- context.print('[knowledge] Agent Knowledge API is not available in this runtime. Refusing to use default Knowledge/Wiki or non-Agent knowledge fallback.');
22
+ context.print('[knowledge] Agent Knowledge API is not available in this runtime. Refusing to use default knowledge or non-Agent knowledge fallback.');
19
23
  return null;
20
24
  }
21
25
  return knowledgeApi;
@@ -48,7 +52,7 @@ function findDisallowedKnowledgeScopeFlag(args: readonly string[]): string | nul
48
52
  function printScopeFlagRejection(context: CommandContext, flag: string): void {
49
53
  context.print([
50
54
  `[knowledge] Agent Knowledge is isolated; ${flag} is not accepted.`,
51
- '[knowledge] GoodVibes Agent must not use default Knowledge/Wiki or non-Agent product spaces.',
55
+ '[knowledge] GoodVibes Agent must not use default knowledge or non-Agent product spaces.',
52
56
  '[knowledge] Use only /api/goodvibes-agent/knowledge/* Agent-owned routes.',
53
57
  ].join('\n'));
54
58
  }
@@ -110,10 +114,7 @@ function readJsonObjectFlag(args: string[], name: string): Record<string, unknow
110
114
  if (!value) return undefined;
111
115
  try {
112
116
  const parsed = JSON.parse(value) as unknown;
113
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
114
- return null;
115
- }
116
- return parsed as Record<string, unknown>;
117
+ return isRecord(parsed) ? parsed : null;
117
118
  } catch {
118
119
  return null;
119
120
  }
@@ -131,7 +132,7 @@ function requireAgentKnowledgeAsk(context: CommandContext): ((input: KnowledgeAs
131
132
  const serviceAsk = context.extensions.agentKnowledgeService?.ask?.bind(context.extensions.agentKnowledgeService);
132
133
  if (serviceAsk) return serviceAsk;
133
134
 
134
- context.print('[knowledge] Agent Knowledge ask is not available in this runtime. Refusing to use default Knowledge/Wiki or non-Agent knowledge fallback.');
135
+ context.print('[knowledge] Agent Knowledge ask is not available in this runtime. Refusing to use default knowledge or non-Agent knowledge fallback.');
135
136
  return null;
136
137
  }
137
138
 
@@ -144,7 +145,7 @@ function formatKnowledgeMap(result: KnowledgeMapResult): string {
144
145
  'Agent Knowledge map',
145
146
  ` nodes: ${result.nodeCount}${result.totalNodeCount !== undefined && result.totalNodeCount !== result.nodeCount ? ` of ${result.totalNodeCount}` : ''}`,
146
147
  ` edges: ${result.edgeCount}${result.totalEdgeCount !== undefined && result.totalEdgeCount !== result.edgeCount ? ` of ${result.totalEdgeCount}` : ''}`,
147
- ' route: /api/goodvibes-agent/knowledge/map',
148
+ ' route /api/goodvibes-agent/knowledge/map',
148
149
  ].join('\n');
149
150
  }
150
151
 
@@ -152,7 +153,7 @@ function nodeLabel(node: { readonly kind?: string; readonly title?: string; read
152
153
  const kind = cleanInline(node.kind) || 'node';
153
154
  const title = cleanInline(node.title) || 'untitled';
154
155
  const summary = cleanInline(node.summary);
155
- const confidence = typeof node.confidence === 'number' ? ` confidence=${node.confidence}` : '';
156
+ const confidence = typeof node.confidence === 'number' ? ` confidence ${node.confidence}` : '';
156
157
  return summary ? `[${kind}] ${title}${confidence} - ${summary}` : `[${kind}] ${title}${confidence}`;
157
158
  }
158
159
 
@@ -179,7 +180,7 @@ function renderKnowledgeAskResult(result: KnowledgeAskResult): string {
179
180
  `[knowledge] ${result.query}`,
180
181
  answer.text,
181
182
  '',
182
- `mode: ${answer.mode} confidence: ${answer.confidence} synthesized: ${answer.synthesized ? 'yes' : 'no'}`,
183
+ `mode ${answer.mode} confidence ${answer.confidence} synthesized ${answer.synthesized ? 'yes' : 'no'}`,
183
184
  ];
184
185
 
185
186
  if (answer.sources.length > 0) {
@@ -208,7 +209,7 @@ function renderKnowledgeAskResult(result: KnowledgeAskResult): string {
208
209
  export const knowledgeCommand: SlashCommand = {
209
210
  name: 'knowledge',
210
211
  aliases: ['know', 'kb'],
211
- description: 'Agent Knowledge/Wiki: isolated Agent-owned sources, graph, review queue, and compact prompt packets.',
212
+ description: 'Agent Knowledge: isolated Agent-owned sources, graph, review queue, and compact prompt packets.',
212
213
  usage: '<subcommand> [args]',
213
214
  argsHint: 'status|ask|ingest-url --yes|ingest-file --yes|import-bookmarks --yes|list|search|get|queue|review-issue --yes',
214
215
  handler: async (args: string[], context: CommandContext): Promise<void> => {
@@ -330,9 +331,9 @@ export const knowledgeCommand: SlashCommand = {
330
331
  return;
331
332
  }
332
333
  const result = await knowledge.ingest.bookmarksFile({ path, sessionId: context.session.runtime.sessionId });
333
- context.print(`[knowledge] Imported bookmarks: ${result.imported} ok, ${result.failed} failed`);
334
+ context.print(`[knowledge] Imported bookmarks ${result.imported} ok, ${result.failed} failed`);
334
335
  if (result.errors.length > 0) {
335
- for (const error of result.errors.slice(0, 10)) context.print(` error: ${error}`);
336
+ for (const error of result.errors.slice(0, 10)) context.print(` error ${error}`);
336
337
  }
337
338
  break;
338
339
  }
@@ -348,9 +349,9 @@ export const knowledgeCommand: SlashCommand = {
348
349
  return;
349
350
  }
350
351
  const result = await knowledge.ingest.urlsFile({ path, sessionId: context.session.runtime.sessionId });
351
- context.print(`[knowledge] Imported URL list: ${result.imported} ok, ${result.failed} failed`);
352
+ context.print(`[knowledge] Imported URL list ${result.imported} ok, ${result.failed} failed`);
352
353
  if (result.errors.length > 0) {
353
- for (const error of result.errors.slice(0, 10)) context.print(` error: ${error}`);
354
+ for (const error of result.errors.slice(0, 10)) context.print(` error ${error}`);
354
355
  }
355
356
  break;
356
357
  }
@@ -371,9 +372,9 @@ export const knowledgeCommand: SlashCommand = {
371
372
  sessionId: context.session.runtime.sessionId,
372
373
  });
373
374
  context.print(`[knowledge] Imported browser knowledge: ${result.imported} ok, ${result.failed} failed`);
374
- if (result.profiles.length > 0) context.print(` profiles: ${result.profiles.length}`);
375
+ if (result.profiles.length > 0) context.print(` profiles ${result.profiles.length}`);
375
376
  if (result.errors.length > 0) {
376
- for (const error of result.errors.slice(0, 10)) context.print(` error: ${error}`);
377
+ for (const error of result.errors.slice(0, 10)) context.print(` error ${error}`);
377
378
  }
378
379
  break;
379
380
  }
@@ -405,7 +406,7 @@ export const knowledgeCommand: SlashCommand = {
405
406
  });
406
407
  context.print(`[knowledge] Imported connector input: ${result.imported} ok, ${result.failed} failed`);
407
408
  if (result.errors.length > 0) {
408
- for (const error of result.errors.slice(0, 10)) context.print(` error: ${error}`);
409
+ for (const error of result.errors.slice(0, 10)) context.print(` error ${error}`);
409
410
  }
410
411
  break;
411
412
  }
@@ -419,7 +420,7 @@ export const knowledgeCommand: SlashCommand = {
419
420
  context.print('[knowledge] No nodes.');
420
421
  return;
421
422
  }
422
- context.print(`[knowledge] ${nodes.length} node(s):`);
423
+ context.print(`[knowledge] ${nodes.length} node(s)`);
423
424
  for (const node of nodes) {
424
425
  context.print(` ${node.id} [${node.kind}] ${node.title}`);
425
426
  if (node.summary) context.print(` ${node.summary}`);
@@ -432,7 +433,7 @@ export const knowledgeCommand: SlashCommand = {
432
433
  context.print('[knowledge] No issues.');
433
434
  return;
434
435
  }
435
- context.print(`[knowledge] ${issues.length} issue(s):`);
436
+ context.print(`[knowledge] ${issues.length} issue(s)`);
436
437
  for (const issue of issues) {
437
438
  context.print(` ${issue.id} [${issue.severity}] ${issue.code}`);
438
439
  context.print(` ${issue.message}`);
@@ -444,7 +445,7 @@ export const knowledgeCommand: SlashCommand = {
444
445
  context.print('[knowledge] No sources.');
445
446
  return;
446
447
  }
447
- context.print(`[knowledge] ${sources.length} source(s):`);
448
+ context.print(`[knowledge] ${sources.length} source(s)`);
448
449
  for (const source of sources) {
449
450
  context.print(` ${source.id} [${source.sourceType}/${source.status}] ${source.title ?? source.canonicalUri ?? source.sourceUri ?? 'untitled'}`);
450
451
  if (source.summary) context.print(` ${source.summary}`);
@@ -465,10 +466,10 @@ export const knowledgeCommand: SlashCommand = {
465
466
  context.print('[knowledge] No results.');
466
467
  return;
467
468
  }
468
- context.print(`[knowledge] ${results.length} result(s):`);
469
+ context.print(`[knowledge] ${results.length} result(s)`);
469
470
  for (const result of results) {
470
471
  const title = result.source?.title ?? result.source?.canonicalUri ?? result.node?.title ?? result.id;
471
- context.print(` ${result.id} [${result.kind}] score=${result.score} ${title}`);
472
+ context.print(` ${result.id} [${result.kind}] score ${result.score} ${title}`);
472
473
  context.print(` ${result.reason}`);
473
474
  }
474
475
  break;
@@ -482,28 +483,28 @@ export const knowledgeCommand: SlashCommand = {
482
483
  }
483
484
  const item = knowledge.graph.items.get(id);
484
485
  if (!item) {
485
- context.print(`[knowledge] Unknown item: ${id}`);
486
+ context.print(`[knowledge] Unknown item ${id}`);
486
487
  return;
487
488
  }
488
489
  if (item.source) {
489
490
  context.print(`[knowledge] source ${item.source.id}`);
490
- context.print(` title: ${item.source.title ?? 'untitled'}`);
491
- context.print(` uri: ${item.source.canonicalUri ?? item.source.sourceUri ?? 'n/a'}`);
492
- context.print(` status: ${item.source.status}`);
493
- if (item.source.summary) context.print(` summary: ${item.source.summary}`);
491
+ context.print(` title ${item.source.title ?? 'untitled'}`);
492
+ context.print(` uri ${item.source.canonicalUri ?? item.source.sourceUri ?? 'n/a'}`);
493
+ context.print(` status ${item.source.status}`);
494
+ if (item.source.summary) context.print(` summary ${item.source.summary}`);
494
495
  } else if (item.node) {
495
496
  context.print(`[knowledge] node ${item.node.id}`);
496
- context.print(` kind: ${item.node.kind}`);
497
- context.print(` title: ${item.node.title}`);
498
- if (item.node.summary) context.print(` summary: ${item.node.summary}`);
497
+ context.print(` type ${item.node.kind}`);
498
+ context.print(` title ${item.node.title}`);
499
+ if (item.node.summary) context.print(` summary ${item.node.summary}`);
499
500
  } else if (item.issue) {
500
501
  context.print(`[knowledge] issue ${item.issue.id}`);
501
- context.print(` severity: ${item.issue.severity}`);
502
- context.print(` code: ${item.issue.code}`);
503
- context.print(` message: ${item.issue.message}`);
502
+ context.print(` severity ${item.issue.severity}`);
503
+ context.print(` code ${item.issue.code}`);
504
+ context.print(` message ${item.issue.message}`);
504
505
  }
505
506
  if (item.relatedEdges.length > 0) {
506
- context.print(' relations:');
507
+ context.print(' relations');
507
508
  for (const edge of item.relatedEdges.slice(0, 12)) {
508
509
  context.print(` ${edge.fromKind}:${edge.fromId} -[${edge.relation}]-> ${edge.toKind}:${edge.toId}`);
509
510
  }
@@ -528,7 +529,7 @@ export const knowledgeCommand: SlashCommand = {
528
529
  context.print('[knowledge] No lint issues.');
529
530
  return;
530
531
  }
531
- context.print(`[knowledge] ${issues.length} lint issue(s):`);
532
+ context.print(`[knowledge] ${issues.length} lint issue(s)`);
532
533
  for (const issue of issues) {
533
534
  context.print(` ${issue.id} [${issue.severity}] ${issue.code}`);
534
535
  context.print(` ${issue.message}`);
@@ -544,7 +545,7 @@ export const knowledgeCommand: SlashCommand = {
544
545
  context.print('Knowledge review queue is empty.');
545
546
  return;
546
547
  }
547
- context.print(`[knowledge] Review queue (${issues.length}):`);
548
+ context.print(`[knowledge] Review queue (${issues.length})`);
548
549
  for (const issue of issues) {
549
550
  context.print(` ${issue.id} [${issue.severity}] ${issue.code}`);
550
551
  context.print(` ${issue.message}`);
@@ -578,11 +579,11 @@ export const knowledgeCommand: SlashCommand = {
578
579
  });
579
580
  context.print([
580
581
  `[knowledge] Reviewed issue ${result.issue.id}`,
581
- ` action: ${action}`,
582
- ` status: ${result.issue.status}`,
583
- ...(result.node ? [` node: ${result.node.id} ${result.node.title}`] : []),
584
- ...(result.source ? [` source: ${result.source.id} ${result.source.title ?? result.source.canonicalUri ?? result.source.sourceUri ?? 'untitled'}`] : []),
585
- ...(result.appliedFacts ? [` applied facts: ${Object.keys(result.appliedFacts).join(', ') || 'none'}`] : []),
582
+ ` action ${action}`,
583
+ ` status ${result.issue.status}`,
584
+ ...(result.node ? [` node ${result.node.id} ${result.node.title}`] : []),
585
+ ...(result.source ? [` knowledge source ${result.source.id} ${result.source.title ?? result.source.canonicalUri ?? result.source.sourceUri ?? 'untitled'}`] : []),
586
+ ...(result.appliedFacts ? [` applied facts ${Object.keys(result.appliedFacts).join(', ') || 'none'}`] : []),
586
587
  ].join('\n'));
587
588
  } catch (error) {
588
589
  context.print(`[knowledge] ${error instanceof Error ? error.message : String(error)}`);
@@ -598,9 +599,9 @@ export const knowledgeCommand: SlashCommand = {
598
599
  context.print('[knowledge] No consolidation candidates.');
599
600
  return;
600
601
  }
601
- context.print(`[knowledge] Consolidation candidates (${candidates.length}):`);
602
+ context.print(`[knowledge] Consolidation candidates (${candidates.length})`);
602
603
  for (const candidate of candidates) {
603
- context.print(` ${candidate.id} [${candidate.status}] score=${candidate.score} ${candidate.title}`);
604
+ context.print(` ${candidate.id} [${candidate.status}] score ${candidate.score} ${candidate.title}`);
604
605
  context.print(` ${candidate.candidateType}`);
605
606
  }
606
607
  break;
@@ -615,7 +616,7 @@ export const knowledgeCommand: SlashCommand = {
615
616
  }
616
617
  const report = await knowledge.connectors.doctor(second);
617
618
  if (!report) {
618
- context.print(`[knowledge] Connector doctor report unavailable: ${second}`);
619
+ context.print(`[knowledge] Connector doctor report unavailable ${second}`);
619
620
  return;
620
621
  }
621
622
  context.print([
@@ -630,14 +631,14 @@ export const knowledgeCommand: SlashCommand = {
630
631
  if (first) {
631
632
  const connector = knowledge.connectors.get(first);
632
633
  if (!connector) {
633
- context.print(`[knowledge] Unknown connector: ${first}`);
634
+ context.print(`[knowledge] Unknown connector ${first}`);
634
635
  return;
635
636
  }
636
637
  context.print([
637
638
  `[knowledge] Connector ${connector.id}`,
638
- ` name: ${connector.displayName ?? connector.id}`,
639
- ` sourceType: ${connector.sourceType}`,
640
- ` description: ${connector.description}`,
639
+ ` name ${connector.displayName ?? connector.id}`,
640
+ ` source type ${connector.sourceType}`,
641
+ ` description ${connector.description}`,
641
642
  ` capabilities: ${connector.capabilities?.join(', ') || 'none'}`,
642
643
  ].join('\n'));
643
644
  return;
@@ -647,7 +648,7 @@ export const knowledgeCommand: SlashCommand = {
647
648
  context.print('[knowledge] No connectors.');
648
649
  return;
649
650
  }
650
- context.print(`[knowledge] Connectors (${connectors.length}):`);
651
+ context.print(`[knowledge] Connectors (${connectors.length})`);
651
652
  for (const connector of connectors) {
652
653
  context.print(` ${connector.id} [${connector.sourceType}] ${connector.displayName ?? connector.id}`);
653
654
  context.print(` ${connector.description}`);
@@ -663,7 +664,7 @@ export const knowledgeCommand: SlashCommand = {
663
664
  context.print('[knowledge] No consolidation reports.');
664
665
  return;
665
666
  }
666
- context.print(`[knowledge] Consolidation reports (${reports.length}):`);
667
+ context.print(`[knowledge] Consolidation reports (${reports.length})`);
667
668
  for (const report of reports) {
668
669
  context.print(` ${report.id} [${report.kind}] ${report.title}`);
669
670
  context.print(` ${report.summary}`);
@@ -677,7 +678,7 @@ export const knowledgeCommand: SlashCommand = {
677
678
  context.print('[knowledge] No knowledge schedules.');
678
679
  return;
679
680
  }
680
- context.print(`[knowledge] Managed schedules (${schedules.length}):`);
681
+ context.print(`[knowledge] Managed schedules (${schedules.length})`);
681
682
  for (const schedule of schedules) {
682
683
  context.print(` ${schedule.id} [${schedule.enabled ? 'enabled' : 'disabled'}] ${schedule.jobId}`);
683
684
  context.print(` ${schedule.label}`);
@@ -729,10 +730,10 @@ export const knowledgeCommand: SlashCommand = {
729
730
  const result = await knowledge.status.reindex();
730
731
  context.print([
731
732
  '[knowledge] Reindex complete',
732
- ` sources: ${result.status.sourceCount}`,
733
- ` nodes: ${result.status.nodeCount}`,
734
- ` edges: ${result.status.edgeCount}`,
735
- ` issues: ${result.status.issueCount}`,
733
+ ` sources ${result.status.sourceCount}`,
734
+ ` nodes ${result.status.nodeCount}`,
735
+ ` edges ${result.status.edgeCount}`,
736
+ ` issues ${result.status.issueCount}`,
736
737
  ].join('\n'));
737
738
  break;
738
739
  }
@@ -13,6 +13,14 @@ function isValidProviderName(name: string): boolean {
13
13
  return /^[a-zA-Z0-9_-]+$/.test(name);
14
14
  }
15
15
 
16
+ function readDiscoveredModelIds(body: unknown): string[] {
17
+ if (!body || typeof body !== 'object' || !('data' in body) || !Array.isArray(body.data)) return [];
18
+ return body.data
19
+ .filter((model): model is { readonly id: unknown } => Boolean(model) && typeof model === 'object' && 'id' in model)
20
+ .map((model) => String(model.id))
21
+ .filter(Boolean);
22
+ }
23
+
16
24
  export function registerLocalProviderRuntimeCommands(registry: CommandRegistry): void {
17
25
  registry.register({
18
26
  name: 'provider',
@@ -36,20 +44,26 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
36
44
  return;
37
45
  }
38
46
  if (!isValidProviderName(name)) {
39
- ctx.print('Error: Provider name must contain only letters, numbers, hyphens, and underscores.');
47
+ ctx.print([
48
+ 'Error',
49
+ ' message Provider name must contain only letters, numbers, hyphens, and underscores.',
50
+ ].join('\n'));
40
51
  return;
41
52
  }
42
53
  let parsedUrl: URL;
43
54
  try {
44
55
  parsedUrl = new URL(baseURL);
45
56
  } catch {
46
- ctx.print(`Error: '${baseURL}' is not a valid URL. Example: http://192.168.0.85:8001/v1`);
57
+ ctx.print([
58
+ 'Error',
59
+ ` message ${baseURL} is not a valid URL. Example http://192.168.0.85:8001/v1`,
60
+ ].join('\n'));
47
61
  return;
48
62
  }
49
63
  const providersDir = shellPaths.resolveUserPath(GOODVIBES_AGENT_SURFACE_ROOT, 'providers');
50
64
  const providerFile = join(providersDir, `${name}.json`);
51
65
  if (existsSync(providerFile)) {
52
- ctx.print(`Error: Provider '${name}' already exists at ${providerFile}\nRemove it first with: /provider remove ${name} --yes`);
66
+ ctx.print(`Provider ${name} already exists at ${providerFile}\nRemove it first with /provider remove ${name} --yes`);
53
67
  return;
54
68
  }
55
69
 
@@ -64,12 +78,7 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
64
78
  clearTimeout(timeoutId);
65
79
  if (res.ok) {
66
80
  const body = await res.json() as unknown;
67
- if (body && typeof body === 'object' && 'data' in body && Array.isArray((body as Record<string, unknown>).data)) {
68
- discoveredModelIds = ((body as { data: unknown[] }).data)
69
- .filter((m): m is Record<string, unknown> => typeof m === 'object' && m !== null && 'id' in m)
70
- .map((m) => String(m.id))
71
- .filter(Boolean);
72
- }
81
+ discoveredModelIds = readDiscoveredModelIds(body);
73
82
  }
74
83
  } catch {
75
84
  ctx.print(`Could not reach ${baseURL}/models — creating provider with a minimal starter config.`);
@@ -111,10 +120,10 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
111
120
  mkdirSync(providersDir, { recursive: true });
112
121
  await writeFile(providerFile, JSON.stringify(config, null, 2), 'utf-8');
113
122
  } catch (e) {
114
- ctx.print(`Error writing provider file: ${summarizeError(e)}`);
123
+ ctx.print(`Error writing provider file ${summarizeError(e)}`);
115
124
  return;
116
125
  }
117
- ctx.print(`Provider '${name}' added with ${models.length} model(s):\n${discoveredModelIds.length > 0 ? discoveredModelIds.map((id) => ` • ${id} (${(contextWindows[id] ?? 8192).toLocaleString()} ctx)`).join('\n') : ` • ${defaultModel} (starter entry)`}\nThe file watcher will auto-register it shortly.`);
126
+ ctx.print(`Provider ${name} added with ${models.length} model(s)\n${discoveredModelIds.length > 0 ? discoveredModelIds.map((id) => ` • ${id} (${(contextWindows[id] ?? 8192).toLocaleString()} ctx)`).join('\n') : ` • ${defaultModel} (starter entry)`}\nThe file watcher will auto-register it shortly.`);
118
127
  return;
119
128
  }
120
129
 
@@ -129,19 +138,22 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
129
138
  return;
130
139
  }
131
140
  if (!isValidProviderName(name)) {
132
- ctx.print('Error: Provider name must contain only letters, numbers, hyphens, and underscores.');
141
+ ctx.print([
142
+ 'Error',
143
+ ' message Provider name must contain only letters, numbers, hyphens, and underscores.',
144
+ ].join('\n'));
133
145
  return;
134
146
  }
135
147
  const providerFile = shellPaths.resolveUserPath(GOODVIBES_AGENT_SURFACE_ROOT, 'providers', `${name}.json`);
136
148
  if (!existsSync(providerFile)) {
137
- ctx.print(`Error: No custom provider '${name}' found at ${providerFile}`);
149
+ ctx.print(`No custom provider ${name} found at ${providerFile}`);
138
150
  return;
139
151
  }
140
152
  try {
141
153
  await unlink(providerFile);
142
- ctx.print(`Provider '${name}' removed. The file watcher will deregister it shortly.`);
154
+ ctx.print(`Provider ${name} removed. The file watcher will deregister it shortly.`);
143
155
  } catch (e) {
144
- ctx.print(`Error removing provider file: ${summarizeError(e)}`);
156
+ ctx.print(`Error removing provider file ${summarizeError(e)}`);
145
157
  }
146
158
  return;
147
159
  }
@@ -152,7 +164,7 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
152
164
  return;
153
165
  }
154
166
  const providers = requireProviderApi(ctx).listProviderIds();
155
- ctx.print(['Available providers:', ...providers.map((provider) => ` ${provider === ctx.session.runtime.provider ? '▶' : ' '} ${provider}`)].join('\n'));
167
+ ctx.print(['Available providers', ...providers.map((provider) => ` ${provider === ctx.session.runtime.provider ? '▶' : ' '} ${provider}`)].join('\n'));
156
168
  return;
157
169
  }
158
170
 
@@ -165,7 +177,10 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
165
177
  });
166
178
  const match = selectable[0];
167
179
  if (!match) {
168
- ctx.print(`Unknown provider: ${providerName}. Available: ${providerApi.listProviderIds().join(', ')}`);
180
+ ctx.print([
181
+ `Unknown provider ${providerName}`,
182
+ `available providers ${providerApi.listProviderIds().join(', ')}`,
183
+ ].join('\n'));
169
184
  return;
170
185
  }
171
186
  try {
@@ -178,7 +193,10 @@ export function registerLocalProviderRuntimeCommands(registry: CommandRegistry):
178
193
  ctx.platform.configManager.set('provider.model', selected.registryKey);
179
194
  ctx.print(`Switched to provider: ${selected.providerId} (model: ${selected.modelId})`);
180
195
  } catch (e) {
181
- ctx.print(`Error: ${summarizeError(e)}`);
196
+ ctx.print([
197
+ 'Error',
198
+ ` message ${summarizeError(e)}`,
199
+ ].join('\n'));
182
200
  }
183
201
  },
184
202
  });