@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
@@ -32,10 +32,17 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
32
32
  ctx.session.runtime.model = selected.registryKey;
33
33
  ctx.session.runtime.provider = selected.providerId;
34
34
  ctx.platform.configManager.set('provider.model', selected.registryKey);
35
- ctx.print(`Switched to model: ${selected.displayName} (${selected.providerId})`);
35
+ ctx.print([
36
+ 'Switched model',
37
+ ` model ${selected.displayName}`,
38
+ ` provider ${selected.providerId}`,
39
+ ].join('\n'));
36
40
  void providerApi.recordModelUsage(selected.registryKey).catch((err) => { logger.debug('model usage record failed', { err }); });
37
41
  } catch (e) {
38
- ctx.print(`Error: ${summarizeError(e)}`);
42
+ ctx.print([
43
+ 'Error',
44
+ ` message ${summarizeError(e)}`,
45
+ ].join('\n'));
39
46
  }
40
47
  }
41
48
  },
@@ -114,18 +121,20 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
114
121
  handler(_args, ctx) {
115
122
  if (ctx.openSelection) {
116
123
  const items: SelectionItem[] = [
117
- { id: '/agent', label: '/agent', detail: 'Open the Agent operator workspace', category: 'Agent Operator' },
118
- { id: '/agent setup', label: '/agent setup', detail: 'Open the Agent setup workspace', category: 'Agent Operator' },
124
+ { id: '/agent', label: '/agent', detail: 'Open the Agent operator workspace; press / inside it to search every action', category: 'Agent Operator' },
125
+ { id: '/agent setup', label: '/agent setup', detail: 'Open the Agent setup workspace; workspace search can find every setup action', category: 'Agent Operator' },
119
126
  { id: '/agent channels', label: '/agent channels', detail: 'Open channel readiness and delivery safety', category: 'Agent Operator' },
120
127
  { id: '/agent knowledge', label: '/agent knowledge', detail: 'Open isolated Agent Knowledge workflows', category: 'Agent Operator' },
121
128
  { id: '/agent voice-media', label: '/agent voice-media', detail: 'Open voice, TTS, image, browser, and media setup', category: 'Agent Operator' },
122
129
  { id: '/home', label: '/home', detail: 'Alias for the Agent operator workspace', category: 'Agent Operator' },
123
130
  { id: '/brief', label: '/brief', detail: 'Show a concise Agent operator briefing and next actions', category: 'Agent Operator' },
124
131
  { id: '/knowledge', label: '/knowledge', detail: 'Inspect isolated Agent Knowledge status, ask/search, and ingest flows', category: 'Agent Operator' },
125
- { id: '/memory', label: '/memory', detail: 'Review local Agent memory records', category: 'Agent Operator' },
126
- { id: '/personas', label: '/personas', detail: 'Create, review, and activate local Agent personas', category: 'Agent Operator' },
127
- { id: '/agent-skills', label: '/agent-skills', detail: 'Create, review, and enable reusable Agent skills', category: 'Agent Operator' },
132
+ { id: '/memory', label: '/memory', detail: 'Review Agent-local memory records', category: 'Agent Operator' },
133
+ { id: '/notes', label: '/notes', detail: 'Open Agent-local scratchpad notes for source triage and temporary context', category: 'Agent Operator' },
134
+ { id: '/personas', label: '/personas', detail: 'Create, review, and activate Agent-local personas', category: 'Agent Operator' },
135
+ { id: '/skills', label: '/skills', detail: 'Create, review, and enable reusable Agent skills', category: 'Agent Operator' },
128
136
  { id: '/routines', label: '/routines', detail: 'Create, review, start, and promote Agent routines explicitly', category: 'Agent Operator' },
137
+ { id: '/automation', label: '/automation', detail: 'Run confirmed connected-host automation actions', category: 'Agent Operator' },
129
138
  { id: '/delegate', label: '/delegate [task]', detail: 'Explicit build/fix/review handoff to GoodVibes TUI', category: 'Agent Operator' },
130
139
  { id: '/channels', label: '/channels', detail: 'Inspect messaging-channel readiness without sending messages', category: 'Agent Operator' },
131
140
  { id: '/pair', label: '/pair', detail: 'Pair companion clients through connected GoodVibes host', category: 'Agent Operator' },
@@ -178,7 +187,7 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
178
187
  });
179
188
  return;
180
189
  }
181
- ctx.print('Use /help to open the help modal. Commands: /agent, /brief, /knowledge, /memory, /personas, /agent-skills, /routines, /channels, /delegate, /model, /provider, /config, /compat, /bundle, /paste, /sessions, /bookmarks, /save, /load, /undo, /redo, /retry, /clear, /reset, /compact, /export, /title, /effort, /expand, /collapse, /quit');
190
+ ctx.print('Open the Agent workspace first, then press / inside it to search every product action. Slash commands remain available: /agent, /brief, /knowledge, /memory, /notes, /personas, /skills, /routines, /channels, /approval, /automation, /schedule, /delegate, /model, /provider, /config, /compat, /bundle, /paste, /sessions, /bookmarks, /save, /load, /undo, /redo, /retry, /clear, /reset, /compact, /export, /title, /effort, /expand, /collapse, /quit');
182
191
  },
183
192
  });
184
193
 
@@ -259,20 +268,23 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
259
268
  const level = result.item.id as 'instant' | 'low' | 'medium' | 'high';
260
269
  ctx.session.runtime.reasoningEffort = level;
261
270
  ctx.platform.configManager.set('provider.reasoningEffort', level);
262
- ctx.print(`Reasoning effort set to: ${level}`);
271
+ ctx.print([
272
+ 'Reasoning effort set',
273
+ ` level ${level}`,
274
+ ].join('\n'));
263
275
  ctx.renderRequest();
264
276
  });
265
277
  return;
266
278
  }
267
279
  const budget = REASONING_BUDGET_MAP[current];
268
280
  const lines = [
269
- `Reasoning effort: ${current}`,
270
- ` Mercury-2: reasoning_effort = '${current}'`,
271
- ` Claude: thinking.budget_tokens = ${budget}`,
272
- ` Gemini: thinking_config.thinking_budget = ${budget}`,
273
- ` GPT-5: (no-op)`,
281
+ `Reasoning effort ${current}`,
282
+ ` Mercury-2 reasoning_effort = '${current}'`,
283
+ ` Claude thinking.budget_tokens = ${budget}`,
284
+ ` Gemini thinking_config.thinking_budget = ${budget}`,
285
+ ` GPT-5 (no-op)`,
274
286
  '',
275
- `Levels: ${validLevels.join(', ')}`,
287
+ `Levels ${validLevels.join(', ')}`,
276
288
  ];
277
289
  ctx.print(lines.join('\n'));
278
290
  return;
@@ -280,13 +292,16 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
280
292
 
281
293
  const level = args[0] as 'instant' | 'low' | 'medium' | 'high';
282
294
  if (!validLevels.includes(level)) {
283
- ctx.print(`Invalid effort level: ${level}\nValid levels: ${validLevels.join(', ')}`);
295
+ ctx.print(`Invalid effort level ${level}\nValid levels ${validLevels.join(', ')}`);
284
296
  return;
285
297
  }
286
298
 
287
299
  ctx.session.runtime.reasoningEffort = level;
288
300
  ctx.platform.configManager.set('provider.reasoningEffort', level);
289
- ctx.print(`Reasoning effort set to: ${level}`);
301
+ ctx.print([
302
+ 'Reasoning effort set',
303
+ ` level ${level}`,
304
+ ].join('\n'));
290
305
  },
291
306
  });
292
307
 
@@ -1,14 +1,13 @@
1
1
  import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import { dirname, resolve } from 'node:path';
3
3
  import type { CommandContext, CommandRegistry } from '../command-registry.ts';
4
- import { createOAuthLocalListener } from '@pellux/goodvibes-sdk/platform/config';
5
4
  import { beginOpenAICodexLogin, exchangeOpenAICodexCode } from '@pellux/goodvibes-sdk/platform/config';
6
5
  import type { OAuthProviderConfig, ProviderSubscription } from '@pellux/goodvibes-sdk/platform/config';
7
6
  import { getSubscriptionProviderConfig, listAvailableSubscriptionProviders } from '@pellux/goodvibes-sdk/platform/config';
8
7
  import { inspectProviderAuth } from '@/runtime/index.ts';
8
+ import { formatAgentRecordSource } from '../../agent/record-labels.ts';
9
9
  import { openExternalUrl } from '@pellux/goodvibes-sdk/platform/utils';
10
10
  import { requireSecretsManager, requireServiceRegistry, requireShellPaths, requireSubscriptionManager } from './runtime-services.ts';
11
- import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
12
11
  import { requireYesFlag, stripYesFlag } from './confirmation.ts';
13
12
 
14
13
  interface SubscriptionBundle {
@@ -24,13 +23,13 @@ function buildReviewText(ctx: CommandContext): string {
24
23
  return [
25
24
  'Subscription Review',
26
25
  ' No provider subscriptions stored yet.',
27
- ...(available.length > 0 ? [` available providers: ${available.map((entry) => entry.provider).join(', ')}`] : []),
26
+ ...(available.length > 0 ? [` available providers ${available.map((entry) => entry.provider).join(', ')}`] : []),
28
27
  ].join('\n');
29
28
  }
30
29
  return [
31
30
  `Subscription Review`,
32
31
  ...subscriptions.map((subscription) => (
33
- ` ${subscription.provider} ${subscription.authMode} token=${subscription.tokenType} expires=${subscription.expiresAt ? new Date(subscription.expiresAt).toISOString() : 'n/a'}`
32
+ ` ${subscription.provider} auth mode ${subscription.authMode} token ${subscription.tokenType} expires ${subscription.expiresAt ? new Date(subscription.expiresAt).toISOString() : 'n/a'}`
34
33
  )),
35
34
  ].join('\n');
36
35
  }
@@ -39,8 +38,8 @@ function inspectBundle(path: string): string {
39
38
  const bundle = JSON.parse(readFileSync(path, 'utf-8')) as SubscriptionBundle;
40
39
  return [
41
40
  'Subscription Bundle Review',
42
- ` exportedAt: ${new Date(bundle.exportedAt).toISOString()}`,
43
- ` subscriptions: ${bundle.subscriptions.length}`,
41
+ ` exported at ${new Date(bundle.exportedAt).toISOString()}`,
42
+ ` subscriptions ${bundle.subscriptions.length}`,
44
43
  ...bundle.subscriptions.map((subscription) => ` ${subscription.provider} ${subscription.authMode}`),
45
44
  ].join('\n');
46
45
  }
@@ -57,19 +56,16 @@ function extractAuthorizationCode(input: string): string | null {
57
56
  }
58
57
  }
59
58
 
60
- function resolveLoginConfig(config: OAuthProviderConfig, useLocalCallback: boolean): OAuthProviderConfig {
61
- if (!useLocalCallback || !config.localCallback) {
62
- return config.manualRedirectUri
63
- ? { ...config, redirectUri: config.manualRedirectUri }
64
- : config;
65
- }
66
- return config;
59
+ function resolveManualLoginConfig(config: OAuthProviderConfig): OAuthProviderConfig {
60
+ return config.manualRedirectUri
61
+ ? { ...config, redirectUri: config.manualRedirectUri }
62
+ : config;
67
63
  }
68
64
 
69
65
  function describePrecedence(record: Pick<ProviderSubscription, 'overrideAmbientApiKeys'>): string {
70
66
  return record.overrideAmbientApiKeys
71
- ? ' precedence: this now overrides ambient API keys for the provider'
72
- : ' precedence: stored for subscription-backed flows only; ambient API keys are unchanged';
67
+ ? ' precedence this now overrides ambient API keys for the provider'
68
+ : ' precedence stored for subscription-backed flows only; ambient API keys are unchanged';
73
69
  }
74
70
 
75
71
  export function registerSubscriptionRuntimeCommands(registry: CommandRegistry): void {
@@ -77,7 +73,7 @@ export function registerSubscriptionRuntimeCommands(registry: CommandRegistry):
77
73
  name: 'subscription',
78
74
  aliases: ['subs'],
79
75
  description: 'Manage provider subscription sessions and, when supported, let them override ambient API keys for matching providers',
80
- usage: '[review|list|providers|inspect <provider>|login <provider> start [--no-browser] [--manual] --yes|login <provider> finish <code-or-url> --yes|logout <provider> --yes|bundle export <path> --yes|bundle inspect <path>]',
76
+ usage: '[review|list|providers|inspect <provider>|login <provider> start [--no-browser] --yes|login <provider> finish <code-or-url> --yes|logout <provider> --yes|bundle export <path> --yes|bundle inspect <path>]',
81
77
  async handler(args, ctx) {
82
78
  const parsed = stripYesFlag(args);
83
79
  const commandArgs = [...parsed.rest];
@@ -99,7 +95,7 @@ export function registerSubscriptionRuntimeCommands(registry: CommandRegistry):
99
95
  ctx.print([
100
96
  'Available Subscription Providers',
101
97
  ...available.map((provider) => (
102
- ` ${provider.provider} source=${provider.source} redirect=${provider.oauth.redirectUri}`
98
+ ` ${provider.provider} origin ${formatAgentRecordSource(provider.source)} redirect ${provider.oauth.redirectUri}`
103
99
  )),
104
100
  ].join('\n'));
105
101
  return;
@@ -123,29 +119,28 @@ export function registerSubscriptionRuntimeCommands(registry: CommandRegistry):
123
119
  });
124
120
  ctx.print([
125
121
  `Subscription ${provider}`,
126
- ` configured: ${inspection.configured ? 'yes' : 'no'}`,
127
- ` freshness: ${inspection.freshness}`,
128
- ` callbackMode: ${inspection.callbackMode}`,
122
+ ` configured ${inspection.configured ? 'yes' : 'no'}`,
123
+ ` freshness ${inspection.freshness}`,
124
+ ' finish mode explicit manual',
129
125
  ...(resolved ? [
130
- ` source: ${resolved.source}`,
131
- ` redirectUri: ${resolved.oauth.redirectUri}`,
132
- ` authUrl: ${resolved.oauth.authUrl}`,
133
- ` tokenUrl: ${resolved.oauth.tokenUrl}`,
134
- ...(inspection.localCallback ? [` localCallback: ${inspection.localCallback}`] : []),
126
+ ` origin ${formatAgentRecordSource(resolved.source)}`,
127
+ ` redirect URI ${resolved.oauth.redirectUri}`,
128
+ ` auth URL ${resolved.oauth.authUrl}`,
129
+ ` token URL ${resolved.oauth.tokenUrl}`,
135
130
  ] : []),
136
131
  ...(inspection.activeSubscription ? [
137
- ` authMode: ${manager.get(provider)?.authMode ?? 'oauth'}`,
138
- ` tokenType: ${inspection.tokenType ?? 'n/a'}`,
139
- ` createdAt: ${manager.get(provider)?.createdAt ? new Date(manager.get(provider)!.createdAt).toISOString() : 'n/a'}`,
140
- ` updatedAt: ${manager.get(provider)?.updatedAt ? new Date(manager.get(provider)!.updatedAt).toISOString() : 'n/a'}`,
141
- ` expiresAt: ${inspection.expiresAt ? new Date(inspection.expiresAt).toISOString() : 'n/a'}`,
142
- ` refreshToken: ${manager.get(provider)?.refreshToken ? 'present' : 'absent'}`,
132
+ ` auth mode ${manager.get(provider)?.authMode ?? 'oauth'}`,
133
+ ` token type ${inspection.tokenType ?? 'n/a'}`,
134
+ ` created at ${manager.get(provider)?.createdAt ? new Date(manager.get(provider)!.createdAt).toISOString() : 'n/a'}`,
135
+ ` updated at ${manager.get(provider)?.updatedAt ? new Date(manager.get(provider)!.updatedAt).toISOString() : 'n/a'}`,
136
+ ` expires at ${inspection.expiresAt ? new Date(inspection.expiresAt).toISOString() : 'n/a'}`,
137
+ ` refresh token ${manager.get(provider)?.refreshToken ? 'present' : 'absent'}`,
143
138
  describePrecedence(manager.get(provider)!),
144
139
  ] : [
145
- ` state: ${inspection.freshness === 'pending' ? 'pending login' : 'available for login'}`,
140
+ ` state ${inspection.freshness === 'pending' ? 'pending login' : 'available for login'}`,
146
141
  ]),
147
- ...inspection.issues.map((issue) => ` issue: ${issue}`),
148
- ...inspection.nextActions.map((action) => ` next: ${action}`),
142
+ ...inspection.issues.map((issue) => ` issue ${issue}`),
143
+ ...inspection.nextActions.map((action) => ` next ${action}`),
149
144
  ].join('\n'));
150
145
  return;
151
146
  }
@@ -174,7 +169,6 @@ export function registerSubscriptionRuntimeCommands(registry: CommandRegistry):
174
169
  if (mode === 'start') {
175
170
  const flags = new Set(commandArgs.slice(3));
176
171
  const openBrowser = !flags.has('--no-browser');
177
- const useManualMode = flags.has('--manual');
178
172
  if (provider === 'openai' && resolved.source === 'builtin') {
179
173
  const started = await beginOpenAICodexLogin();
180
174
  manager.savePending({
@@ -185,158 +179,38 @@ export function registerSubscriptionRuntimeCommands(registry: CommandRegistry):
185
179
  createdAt: Date.now(),
186
180
  });
187
181
 
188
- let listener: Awaited<ReturnType<typeof createOAuthLocalListener>> | null = null;
189
- if (!useManualMode) {
190
- try {
191
- listener = await createOAuthLocalListener({
192
- expectedState: started.state,
193
- host: '127.0.0.1',
194
- port: 1455,
195
- path: '/auth/callback',
196
- }).catch(() => null); // best-effort: listener creation is optional; null triggers manual flow
197
- } catch {
198
- listener = null;
199
- }
200
- }
201
-
202
182
  const browserOpened = openBrowser
203
183
  ? await openExternalUrl(started.authorizationUrl)
204
184
  : false;
205
185
 
206
- if (listener && browserOpened) {
207
- try {
208
- const callback = await listener.waitForCode();
209
- const token = await exchangeOpenAICodexCode(callback.code, started.verifier);
210
- const now = Date.now();
211
- const record = manager.saveSubscription({
212
- provider,
213
- accessToken: token.accessToken,
214
- refreshToken: token.refreshToken,
215
- tokenType: token.tokenType,
216
- expiresAt: token.expiresAt,
217
- ...(token.scopes ? { scopes: token.scopes } : {}),
218
- authMode: 'oauth',
219
- overrideAmbientApiKeys: false,
220
- createdAt: manager.get(provider)?.createdAt ?? now,
221
- updatedAt: now,
222
- });
223
- ctx.print([
224
- `Subscription OAuth Start: ${provider}`,
225
- ` source: ${resolved.source}`,
226
- ` state: ${started.state}`,
227
- ` redirectUri: ${started.redirectUri}`,
228
- ` browser: ${openBrowser ? (browserOpened ? 'opened' : 'open failed') : 'skipped'}`,
229
- ' authorizationUrl:',
230
- ` ${started.authorizationUrl}`,
231
- `Subscription OAuth Complete: ${provider}`,
232
- ` tokenType: ${record.tokenType}`,
233
- ` expiresAt: ${record.expiresAt ? new Date(record.expiresAt).toISOString() : 'n/a'}`,
234
- describePrecedence(record),
235
- ].join('\n'));
236
- return;
237
- } catch (error) {
238
- listener.close();
239
- ctx.print([
240
- `Subscription OAuth Start: ${provider}`,
241
- ` source: ${resolved.source}`,
242
- ` state: ${started.state}`,
243
- ` redirectUri: ${started.redirectUri}`,
244
- ` browser: ${openBrowser ? (browserOpened ? 'opened' : 'open failed') : 'skipped'}`,
245
- ` next: /subscription login ${provider} finish <code-or-url> --yes`,
246
- ` listener: ${summarizeError(error)}`,
247
- ' authorizationUrl:',
248
- ` ${started.authorizationUrl}`,
249
- ].join('\n'));
250
- return;
251
- }
252
- }
253
-
254
- listener?.close();
255
186
  ctx.print([
256
- `Subscription OAuth Start: ${provider}`,
257
- ` source: ${resolved.source}`,
258
- ` state: ${started.state}`,
259
- ` redirectUri: ${started.redirectUri}`,
260
- ` browser: ${openBrowser ? (browserOpened ? 'opened' : 'open failed') : 'skipped'}`,
261
- ` next: /subscription login ${provider} finish <code-or-url> --yes`,
262
- ' authorizationUrl:',
187
+ `Subscription OAuth start ${provider}`,
188
+ ` origin ${formatAgentRecordSource(resolved.source)}`,
189
+ ` state ${started.state}`,
190
+ ` redirect URI ${started.redirectUri}`,
191
+ ` browser ${openBrowser ? (browserOpened ? 'opened' : 'open failed') : 'skipped'}`,
192
+ ' completion paste the callback code or redirect URL into the finish command',
193
+ ` next /subscription login ${provider} finish <code-or-url> --yes`,
194
+ ' authorization URL',
263
195
  ` ${started.authorizationUrl}`,
264
196
  ].join('\n'));
265
197
  return;
266
198
  }
267
- const useLocalCallback = Boolean(resolved.oauth.localCallback) && !flags.has('--manual');
268
- let activeConfig = resolveLoginConfig(resolved.oauth, useLocalCallback);
269
- let listener: Awaited<ReturnType<typeof createOAuthLocalListener>> | null = null;
270
-
271
- if (useLocalCallback && resolved.oauth.localCallback) {
272
- listener = await createOAuthLocalListener({
273
- expectedState: '',
274
- host: resolved.oauth.localCallback.host,
275
- port: resolved.oauth.localCallback.port,
276
- path: resolved.oauth.localCallback.path,
277
- }).catch(() => null); // best-effort: local callback listener is optional; null falls back to manual redirect
278
- }
279
-
280
- if (listener) {
281
- activeConfig = { ...activeConfig, redirectUri: listener.redirectUri };
282
- }
283
-
199
+ const activeConfig = resolveManualLoginConfig(resolved.oauth);
284
200
  const started = await manager.beginOAuthLogin(provider, activeConfig);
285
- if (listener) {
286
- listener.setExpectedState(started.pending.state);
287
- }
288
-
289
201
  const browserOpened = openBrowser
290
- ? await openExternalUrl(started.authorizationUrl)
291
- : false;
292
-
293
- const shouldAutoComplete = Boolean(listener) && (resolved.oauth.localCallback?.autoComplete ?? true) && browserOpened;
294
-
295
- if (shouldAutoComplete && listener) {
296
- try {
297
- const callback = await listener.waitForCode();
298
- const record = await manager.completeOAuthLogin(provider, activeConfig, callback.code);
299
- ctx.print([
300
- `Subscription OAuth Start: ${provider}`,
301
- ` source: ${resolved.source}`,
302
- ` state: ${started.pending.state}`,
303
- ` redirectUri: ${activeConfig.redirectUri}`,
304
- ` browser: ${openBrowser ? (browserOpened ? 'opened' : 'open failed') : 'skipped'}`,
305
- ' authorizationUrl:',
306
- ` ${started.authorizationUrl}`,
307
- `Subscription OAuth Complete: ${provider}`,
308
- ` tokenType: ${record.tokenType}`,
309
- ` expiresAt: ${record.expiresAt ? new Date(record.expiresAt).toISOString() : 'n/a'}`,
310
- describePrecedence(record),
311
- ].join('\n'));
312
- return;
313
- } catch (error) {
314
- listener.close();
315
- ctx.print([
316
- `Subscription OAuth Start: ${provider}`,
317
- ` source: ${resolved.source}`,
318
- ` state: ${started.pending.state}`,
319
- ` redirectUri: ${activeConfig.redirectUri}`,
320
- ` browser: ${openBrowser ? (browserOpened ? 'opened' : 'open failed') : 'skipped'}`,
321
- ` next: /subscription login ${provider} finish <code-or-url> --yes`,
322
- ` listener: ${summarizeError(error)}`,
323
- ' authorizationUrl:',
324
- ` ${started.authorizationUrl}`,
325
- ].join('\n'));
326
- return;
327
- }
328
- }
329
-
330
- listener?.close();
202
+ ? await openExternalUrl(started.authorizationUrl)
203
+ : false;
331
204
 
332
205
  ctx.print([
333
- `Subscription OAuth Start: ${provider}`,
334
- ` source: ${resolved.source}`,
335
- ` state: ${started.pending.state}`,
336
- ` redirectUri: ${activeConfig.redirectUri}`,
337
- ` browser: ${openBrowser ? (browserOpened ? 'opened' : 'open failed') : 'skipped'}`,
338
- ` next: /subscription login ${provider} finish <code-or-url> --yes`,
339
- ' authorizationUrl:',
206
+ `Subscription OAuth start ${provider}`,
207
+ ` origin ${formatAgentRecordSource(resolved.source)}`,
208
+ ` state ${started.pending.state}`,
209
+ ` redirect URI ${activeConfig.redirectUri}`,
210
+ ` browser ${openBrowser ? (browserOpened ? 'opened' : 'open failed') : 'skipped'}`,
211
+ ' completion paste the callback code or redirect URL into the finish command',
212
+ ` next /subscription login ${provider} finish <code-or-url> --yes`,
213
+ ' authorization URL',
340
214
  ` ${started.authorizationUrl}`,
341
215
  ].join('\n'));
342
216
  return;
@@ -370,18 +244,18 @@ export function registerSubscriptionRuntimeCommands(registry: CommandRegistry):
370
244
  });
371
245
  ctx.print([
372
246
  `Stored subscription session for ${provider}.`,
373
- ` tokenType: ${record.tokenType}`,
374
- ` expiresAt: ${record.expiresAt ? new Date(record.expiresAt).toISOString() : 'n/a'}`,
247
+ ` token type ${record.tokenType}`,
248
+ ` expires at ${record.expiresAt ? new Date(record.expiresAt).toISOString() : 'n/a'}`,
375
249
  describePrecedence(record),
376
250
  ].join('\n'));
377
251
  return;
378
252
  }
379
- const activeConfig = resolveLoginConfig(resolved.oauth, false);
253
+ const activeConfig = resolveManualLoginConfig(resolved.oauth);
380
254
  const record = await manager.completeOAuthLogin(provider, activeConfig, code);
381
255
  ctx.print([
382
256
  `Stored subscription session for ${provider}.`,
383
- ` tokenType: ${record.tokenType}`,
384
- ` expiresAt: ${record.expiresAt ? new Date(record.expiresAt).toISOString() : 'n/a'}`,
257
+ ` token type ${record.tokenType}`,
258
+ ` expires at ${record.expiresAt ? new Date(record.expiresAt).toISOString() : 'n/a'}`,
385
259
  describePrecedence(record),
386
260
  ].join('\n'));
387
261
  return;
@@ -16,10 +16,10 @@ const BLOCKED_TASK_MUTATIONS: ReadonlySet<string> = new Set([
16
16
  function printTaskMutationBlocked(print: (text: string) => void, subcommand: string): void {
17
17
  print([
18
18
  `Task mutation "${subcommand}" is blocked in GoodVibes Agent.`,
19
- ' policy: connected-host tasks are read-only from the Agent TUI; normal work stays in the main conversation.',
20
- ' durable tasks: use /workplan for visible planning and task tracking.',
21
- ' build/fix/review: use /delegate <task> to hand explicit implementation work to GoodVibes TUI.',
22
- ' result: no local task state was changed.',
19
+ ' policy connected-host tasks are read-only from the Agent TUI; normal work stays in the main conversation.',
20
+ ' durable tasks use /workplan for visible planning and task tracking.',
21
+ ' build/fix/review use /delegate <task> to hand explicit implementation work to GoodVibes TUI.',
22
+ ' result no local task state was changed.',
23
23
  ].join('\n'));
24
24
  }
25
25
 
@@ -56,7 +56,7 @@ export function registerTasksRuntimeCommands(registry: CommandRegistry): void {
56
56
  handler(args, ctx) {
57
57
  const subcommand = args[0]?.toLowerCase() ?? 'list';
58
58
  if (subcommand === 'open' || subcommand === 'panel') {
59
- ctx.print('Task panels are not part of the Agent workspace. Use /tasks list.');
59
+ ctx.print('Open Agent Workspace -> Work -> Host tasks for the workspace view, or run /tasks list for compact command output.');
60
60
  return;
61
61
  }
62
62
 
@@ -76,7 +76,7 @@ export function registerTasksRuntimeCommands(registry: CommandRegistry): void {
76
76
  return;
77
77
  }
78
78
  ctx.print([
79
- `Runtime Tasks (${filtered.length})`,
79
+ `Connected-host Tasks (${filtered.length})`,
80
80
  ...filtered.slice(0, 20).map((task) => ` ${task.id} ${task.status.padEnd(9)} ${task.kind.padEnd(11)} ${task.owner} ${task.title}`),
81
81
  ].join('\n'));
82
82
  return;
@@ -90,23 +90,23 @@ export function registerTasksRuntimeCommands(registry: CommandRegistry): void {
90
90
  }
91
91
  const task = operatorClient.tasks.get(taskId);
92
92
  if (!task) {
93
- ctx.print(`Unknown task: ${taskId}`);
93
+ ctx.print(`Unknown task ${taskId}`);
94
94
  return;
95
95
  }
96
96
  ctx.print([
97
97
  `Task ${task.id}`,
98
- ` title: ${task.title}`,
98
+ ` title ${task.title}`,
99
99
  ` kind: ${task.kind}`,
100
- ` status: ${task.status}`,
101
- ` owner: ${task.owner}`,
102
- ` cancellable: ${task.cancellable ? 'yes' : 'no'}`,
103
- ` queuedAt: ${new Date(task.queuedAt).toISOString()}`,
104
- ` startedAt: ${task.startedAt ? new Date(task.startedAt).toISOString() : 'n/a'}`,
105
- ` endedAt: ${task.endedAt ? new Date(task.endedAt).toISOString() : 'n/a'}`,
106
- ` parent: ${task.parentTaskId ?? 'none'}`,
107
- ` children: ${task.childTaskIds.join(', ') || '(none)'}`,
108
- ` correlationId: ${task.correlationId ?? 'n/a'}`,
109
- ` summary: ${summarizeTaskResult(task)}`,
100
+ ` status ${task.status}`,
101
+ ` owner ${task.owner}`,
102
+ ` cancellable ${task.cancellable ? 'yes' : 'no'}`,
103
+ ` queued at ${new Date(task.queuedAt).toISOString()}`,
104
+ ` started at ${task.startedAt ? new Date(task.startedAt).toISOString() : 'n/a'}`,
105
+ ` ended at ${task.endedAt ? new Date(task.endedAt).toISOString() : 'n/a'}`,
106
+ ` parent ${task.parentTaskId ?? 'none'}`,
107
+ ` children ${task.childTaskIds.join(', ') || '(none)'}`,
108
+ ` correlation ID ${task.correlationId ?? 'n/a'}`,
109
+ ` summary ${summarizeTaskResult(task)}`,
110
110
  ].join('\n'));
111
111
  return;
112
112
  }
@@ -119,7 +119,7 @@ export function registerTasksRuntimeCommands(registry: CommandRegistry): void {
119
119
  }
120
120
  const task = operatorClient.tasks.get(taskId);
121
121
  if (!task) {
122
- ctx.print(`Unknown task: ${taskId}`);
122
+ ctx.print(`Unknown task ${taskId}`);
123
123
  return;
124
124
  }
125
125
  const payload = typeof task.result === 'string'
@@ -131,7 +131,7 @@ export function registerTasksRuntimeCommands(registry: CommandRegistry): void {
131
131
  return;
132
132
  }
133
133
 
134
- ctx.print(`Unknown tasks subcommand: ${subcommand}`);
134
+ ctx.print(`Unknown tasks subcommand ${subcommand}`);
135
135
  },
136
136
  });
137
137
  }
@@ -26,7 +26,12 @@ function getStore(ctx: import('../command-registry.ts').CommandContext): WorkPla
26
26
 
27
27
  function formatList(store: WorkPlanStore): string {
28
28
  const items = store.listItems();
29
- if (items.length === 0) return 'Work plan is empty. Add one with /workplan add <title>.';
29
+ if (items.length === 0) {
30
+ return [
31
+ 'Work plan is empty',
32
+ ' next /workplan add <title>',
33
+ ].join('\n');
34
+ }
30
35
  return [
31
36
  `Work Plan (${items.length})`,
32
37
  ...items.map((item) => {
@@ -83,7 +88,7 @@ export function registerWorkPlanRuntimeCommands(registry: CommandRegistry): void
83
88
  const subcommand = (commandArgs[0] ?? 'list').toLowerCase();
84
89
  try {
85
90
  if (subcommand === 'panel' || subcommand === 'open') {
86
- ctx.print('Work plan panels are not part of the Agent workspace. Use /workplan list.');
91
+ ctx.print('Open Agent Workspace -> Work -> Work plan for the workspace view, or run /workplan list for compact command output.');
87
92
  return;
88
93
  }
89
94
  if (subcommand === 'list') {
@@ -106,7 +111,12 @@ export function registerWorkPlanRuntimeCommands(registry: CommandRegistry): void
106
111
  ...(addArgs.notes ? { notes: addArgs.notes } : {}),
107
112
  };
108
113
  const item = store.addItem(addArgs.title, addOptions);
109
- ctx.print(`Added work plan item ${item.id}. Use /workplan list to review.`);
114
+ ctx.print([
115
+ 'Added work plan item',
116
+ ` id ${item.id}`,
117
+ ` title ${item.title}`,
118
+ ' next open Agent Workspace -> Work -> Work plan to review',
119
+ ].join('\n'));
110
120
  return;
111
121
  }
112
122
  if (subcommand === 'remove' || subcommand === 'delete' || subcommand === 'rm') {
@@ -120,7 +130,11 @@ export function registerWorkPlanRuntimeCommands(registry: CommandRegistry): void
120
130
  return;
121
131
  }
122
132
  const item = store.removeItem(id);
123
- ctx.print(`Removed work plan item ${item.id}: ${item.title}`);
133
+ ctx.print([
134
+ 'Removed work plan item',
135
+ ` id ${item.id}`,
136
+ ` title ${item.title}`,
137
+ ].join('\n'));
124
138
  return;
125
139
  }
126
140
  if (subcommand === 'clear-done' || subcommand === 'clear-completed') {
@@ -129,7 +143,10 @@ export function registerWorkPlanRuntimeCommands(registry: CommandRegistry): void
129
143
  return;
130
144
  }
131
145
  const count = store.clearCompleted();
132
- ctx.print(`Cleared ${count} completed/cancelled work plan item${count === 1 ? '' : 's'}.`);
146
+ ctx.print([
147
+ 'Cleared completed/cancelled work plan items',
148
+ ` count ${count}`,
149
+ ].join('\n'));
133
150
  return;
134
151
  }
135
152
  if (subcommand === 'cycle' || subcommand === 'toggle') {
@@ -139,7 +156,11 @@ export function registerWorkPlanRuntimeCommands(registry: CommandRegistry): void
139
156
  return;
140
157
  }
141
158
  const item = store.cycleItemStatus(id);
142
- ctx.print(`Updated ${item.id}: ${item.status}.`);
159
+ ctx.print([
160
+ 'Updated work plan item',
161
+ ` id ${item.id}`,
162
+ ` status ${item.status}`,
163
+ ].join('\n'));
143
164
  return;
144
165
  }
145
166
  const status = STATUS_COMMANDS[subcommand];
@@ -150,14 +171,18 @@ export function registerWorkPlanRuntimeCommands(registry: CommandRegistry): void
150
171
  return;
151
172
  }
152
173
  const item = store.setItemStatus(id, status);
153
- ctx.print(`Updated ${item.id}: ${item.status}.`);
174
+ ctx.print([
175
+ 'Updated work plan item',
176
+ ` id ${item.id}`,
177
+ ` status ${item.status}`,
178
+ ].join('\n'));
154
179
  return;
155
180
  }
156
181
  if (WORK_PLAN_STATUSES.includes(subcommand as WorkPlanItemStatus)) {
157
182
  ctx.print(`Usage: /workplan ${subcommand} <id>`);
158
183
  return;
159
184
  }
160
- ctx.print(`Unknown workplan subcommand: ${subcommand}`);
185
+ ctx.print(`Unknown workplan subcommand ${subcommand}`);
161
186
  } catch (error) {
162
187
  ctx.print(summarizeError(error));
163
188
  }