@pellux/goodvibes-tui 1.1.0 → 1.9.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 (267) hide show
  1. package/CHANGELOG.md +85 -21
  2. package/README.md +28 -18
  3. package/docs/foundation-artifacts/operator-contract.json +4045 -1763
  4. package/package.json +2 -2
  5. package/src/audio/spoken-turn-controller.ts +12 -2
  6. package/src/audio/spoken-turn-wiring.ts +2 -1
  7. package/src/cli/help.ts +8 -1
  8. package/src/cli/service-posture.ts +2 -1
  9. package/src/cli/status.ts +2 -1
  10. package/src/cli/surface-command.ts +2 -2
  11. package/src/core/alert-gating.ts +67 -0
  12. package/src/core/approval-alert.ts +72 -0
  13. package/src/core/budget-breach-notifier.ts +106 -0
  14. package/src/core/composer-state.ts +11 -3
  15. package/src/core/conversation-line-cache.ts +27 -3
  16. package/src/core/conversation-rendering.ts +90 -14
  17. package/src/core/conversation.ts +18 -0
  18. package/src/core/focus-tracker.ts +41 -0
  19. package/src/core/long-task-notifier.ts +33 -6
  20. package/src/core/stream-event-wiring.ts +20 -1
  21. package/src/core/system-message-noise.ts +87 -0
  22. package/src/core/system-message-router.ts +104 -51
  23. package/src/core/turn-cancellation.ts +25 -0
  24. package/src/core/turn-event-wiring.ts +72 -3
  25. package/src/daemon/cli.ts +29 -2
  26. package/src/daemon/handlers/register.ts +8 -1
  27. package/src/daemon/service-commands.ts +329 -0
  28. package/src/export/cost-utils.ts +36 -0
  29. package/src/input/autocomplete.ts +27 -1
  30. package/src/input/command-registry.ts +84 -5
  31. package/src/input/commands/checkpoint-runtime.ts +280 -0
  32. package/src/input/commands/codebase-runtime.ts +232 -0
  33. package/src/input/commands/config.ts +43 -3
  34. package/src/input/commands/eval.ts +1 -1
  35. package/src/input/commands/health-runtime.ts +10 -2
  36. package/src/input/commands/image-runtime.ts +112 -0
  37. package/src/input/commands/intelligence-runtime.ts +11 -1
  38. package/src/input/commands/local-auth-runtime.ts +4 -1
  39. package/src/input/commands/local-runtime.ts +9 -3
  40. package/src/input/commands/marketplace-runtime.ts +2 -2
  41. package/src/input/commands/memory.ts +73 -35
  42. package/src/input/commands/operator-panel-runtime.ts +71 -31
  43. package/src/input/commands/planning-runtime.ts +116 -4
  44. package/src/input/commands/platform-sandbox-runtime.ts +1 -6
  45. package/src/input/commands/plugin-runtime.ts +2 -2
  46. package/src/input/commands/policy-dispatch.ts +21 -0
  47. package/src/input/commands/provider-accounts-runtime.ts +1 -1
  48. package/src/input/commands/qrcode-runtime.ts +26 -6
  49. package/src/input/commands/recall-review.ts +35 -0
  50. package/src/input/commands/remote-runtime-setup.ts +5 -3
  51. package/src/input/commands/remote-runtime.ts +3 -3
  52. package/src/input/commands/runtime-services.ts +54 -13
  53. package/src/input/commands/services-runtime.ts +1 -1
  54. package/src/input/commands/session-content.ts +20 -9
  55. package/src/input/commands/session-workflow.ts +16 -1
  56. package/src/input/commands/settings-sync-runtime.ts +16 -4
  57. package/src/input/commands/shell-core.ts +25 -8
  58. package/src/input/commands/skills-runtime.ts +2 -8
  59. package/src/input/commands/subscription-runtime.ts +2 -2
  60. package/src/input/commands/test-runtime.ts +277 -0
  61. package/src/input/commands/websearch-runtime.ts +101 -0
  62. package/src/input/commands/work-plan-runtime.ts +36 -10
  63. package/src/input/commands/workstream-runtime.ts +488 -0
  64. package/src/input/commands.ts +12 -0
  65. package/src/input/config-modal-types.ts +175 -0
  66. package/src/input/config-modal.ts +592 -0
  67. package/src/input/feed-context-factory.ts +10 -8
  68. package/src/input/handler-command-route.ts +37 -20
  69. package/src/input/handler-content-actions.ts +17 -2
  70. package/src/input/handler-feed-routes.ts +74 -114
  71. package/src/input/handler-feed.ts +79 -27
  72. package/src/input/handler-interactions.ts +1 -3
  73. package/src/input/handler-modal-routes.ts +136 -6
  74. package/src/input/handler-modal-stack.ts +14 -7
  75. package/src/input/handler-modal-token-routes.ts +16 -49
  76. package/src/input/handler-onboarding-daemon-adopt.ts +149 -0
  77. package/src/input/handler-onboarding.ts +71 -59
  78. package/src/input/handler-picker-routes.ts +26 -102
  79. package/src/input/handler-shortcuts.ts +74 -14
  80. package/src/input/handler-types.ts +2 -6
  81. package/src/input/handler-ui-state.ts +10 -22
  82. package/src/input/handler.ts +12 -29
  83. package/src/input/keybindings.ts +23 -8
  84. package/src/input/model-picker-types.ts +24 -6
  85. package/src/input/model-picker.ts +77 -2
  86. package/src/input/onboarding/onboarding-runtime-status.ts +10 -1
  87. package/src/input/onboarding/onboarding-wizard-apply.ts +8 -1
  88. package/src/input/onboarding/onboarding-wizard-constants.ts +4 -1
  89. package/src/input/onboarding/onboarding-wizard-network-adopt.ts +136 -0
  90. package/src/input/onboarding/onboarding-wizard-steps.ts +9 -6
  91. package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
  92. package/src/input/panel-integration-actions.ts +9 -170
  93. package/src/input/panel-mouse-geometry.ts +97 -0
  94. package/src/input/panel-paste-flood-guard.ts +86 -0
  95. package/src/input/selection-modal.ts +11 -0
  96. package/src/input/session-picker-modal.ts +44 -4
  97. package/src/input/settings-modal-data.ts +146 -2
  98. package/src/input/settings-modal-mutations.ts +6 -4
  99. package/src/input/settings-modal-types.ts +4 -2
  100. package/src/main.ts +49 -51
  101. package/src/panels/agent-inspector-shared.ts +2 -1
  102. package/src/panels/base-panel.ts +22 -1
  103. package/src/panels/builtin/agent.ts +21 -107
  104. package/src/panels/builtin/development.ts +15 -61
  105. package/src/panels/builtin/knowledge.ts +8 -32
  106. package/src/panels/builtin/operations.ts +84 -428
  107. package/src/panels/builtin/session.ts +21 -112
  108. package/src/panels/builtin/shared.ts +18 -46
  109. package/src/panels/builtin-modals.ts +218 -0
  110. package/src/panels/builtin-panels.ts +5 -0
  111. package/src/panels/cost-tracker-panel.ts +36 -10
  112. package/src/panels/diff-panel.ts +12 -43
  113. package/src/panels/eval-registry.ts +60 -0
  114. package/src/panels/fleet-deep-link.ts +31 -0
  115. package/src/panels/fleet-panel-format.ts +62 -0
  116. package/src/panels/fleet-panel-worktree-detail.ts +48 -0
  117. package/src/panels/fleet-panel.ts +758 -0
  118. package/src/panels/fleet-read-model.ts +520 -0
  119. package/src/panels/fleet-steer.ts +125 -0
  120. package/src/panels/fleet-stop.ts +153 -0
  121. package/src/panels/fleet-tabs.ts +230 -0
  122. package/src/panels/fleet-transcript.ts +356 -0
  123. package/src/panels/index.ts +7 -31
  124. package/src/panels/modals/hooks-modal.ts +187 -0
  125. package/src/panels/modals/keybindings-modal.ts +166 -0
  126. package/src/panels/modals/knowledge-modal.ts +158 -0
  127. package/src/panels/modals/local-auth-modal.ts +132 -0
  128. package/src/panels/modals/marketplace-modal.ts +218 -0
  129. package/src/panels/modals/memory-modal.ts +180 -0
  130. package/src/panels/modals/modal-surface-helpers.ts +54 -0
  131. package/src/panels/modals/modal-theme.ts +42 -0
  132. package/src/panels/modals/pairing-modal.ts +163 -0
  133. package/src/panels/modals/planning-modal.ts +304 -0
  134. package/src/panels/modals/plugins-modal.ts +174 -0
  135. package/src/panels/modals/policy-modal.ts +256 -0
  136. package/src/panels/modals/provider-health-modal.ts +136 -0
  137. package/src/panels/modals/remote-modal.ts +115 -0
  138. package/src/panels/modals/sandbox-modal.ts +150 -0
  139. package/src/panels/modals/security-modal.ts +217 -0
  140. package/src/panels/modals/services-modal.ts +179 -0
  141. package/src/panels/modals/settings-sync-modal.ts +142 -0
  142. package/src/panels/modals/skills-modal.ts +189 -0
  143. package/src/panels/modals/subscription-modal.ts +172 -0
  144. package/src/panels/modals/work-plan-modal.ts +177 -0
  145. package/src/panels/panel-confirm-overlay.ts +72 -0
  146. package/src/panels/panel-manager.ts +123 -9
  147. package/src/panels/polish-core.ts +38 -25
  148. package/src/panels/project-planning-answer-actions.ts +8 -11
  149. package/src/panels/skills-panel.ts +7 -51
  150. package/src/panels/types.ts +37 -0
  151. package/src/permissions/hunk-selection.ts +179 -0
  152. package/src/permissions/prompt.ts +216 -13
  153. package/src/renderer/autocomplete-overlay.ts +28 -2
  154. package/src/renderer/compaction-history-modal.ts +19 -3
  155. package/src/renderer/compaction-preview.ts +13 -2
  156. package/src/renderer/compaction-quality.ts +100 -0
  157. package/src/renderer/compositor.ts +2 -3
  158. package/src/renderer/config-modal.ts +95 -0
  159. package/src/renderer/conversation-overlays.ts +10 -17
  160. package/src/renderer/fleet-tab-strip.ts +56 -0
  161. package/src/renderer/footer-tips.ts +15 -3
  162. package/src/renderer/fullscreen-primitives.ts +32 -22
  163. package/src/renderer/git-status.ts +43 -1
  164. package/src/renderer/help-overlay.ts +2 -2
  165. package/src/renderer/layout.ts +0 -4
  166. package/src/renderer/markdown.ts +7 -3
  167. package/src/renderer/modal-factory.ts +25 -20
  168. package/src/renderer/model-workspace.ts +62 -4
  169. package/src/renderer/overlay-box.ts +21 -17
  170. package/src/renderer/panel-workspace-bar.ts +8 -2
  171. package/src/renderer/process-indicator.ts +14 -3
  172. package/src/renderer/selection-modal-overlay.ts +6 -1
  173. package/src/renderer/session-picker-modal.ts +196 -3
  174. package/src/renderer/settings-modal-helpers.ts +2 -0
  175. package/src/renderer/settings-modal.ts +7 -0
  176. package/src/renderer/shell-surface.ts +8 -1
  177. package/src/renderer/status-glyphs.ts +14 -15
  178. package/src/renderer/system-message.ts +15 -3
  179. package/src/renderer/terminal-bg-probe.ts +339 -0
  180. package/src/renderer/terminal-escapes.ts +20 -0
  181. package/src/renderer/theme-mode-config.ts +67 -0
  182. package/src/renderer/theme.ts +91 -1
  183. package/src/renderer/thinking.ts +11 -3
  184. package/src/renderer/tool-call.ts +15 -9
  185. package/src/renderer/tool-result-summary.ts +148 -0
  186. package/src/renderer/turn-injection.ts +133 -0
  187. package/src/renderer/ui-factory.ts +154 -85
  188. package/src/renderer/ui-primitives.ts +30 -129
  189. package/src/runtime/bootstrap-command-context.ts +27 -1
  190. package/src/runtime/bootstrap-command-parts.ts +41 -10
  191. package/src/runtime/bootstrap-core.ts +47 -15
  192. package/src/runtime/bootstrap-hook-bridge.ts +7 -0
  193. package/src/runtime/bootstrap-shell.ts +48 -17
  194. package/src/runtime/bootstrap.ts +129 -22
  195. package/src/runtime/code-index-services.ts +135 -0
  196. package/src/runtime/legacy-daemon-migration.ts +516 -0
  197. package/src/runtime/memory-fold.ts +26 -0
  198. package/src/runtime/onboarding/derivation.ts +7 -2
  199. package/src/runtime/onboarding/snapshot.ts +27 -1
  200. package/src/runtime/onboarding/types.ts +31 -1
  201. package/src/runtime/operator-token-cleanup.ts +82 -1
  202. package/src/runtime/orchestrator-core-services.ts +59 -0
  203. package/src/runtime/process-lifecycle.ts +3 -1
  204. package/src/runtime/resume-notice.ts +209 -0
  205. package/src/runtime/services.ts +98 -46
  206. package/src/runtime/session-inbound-inputs.ts +252 -0
  207. package/src/runtime/session-spine-transport.ts +64 -0
  208. package/src/runtime/terminal-output-guard.ts +15 -8
  209. package/src/runtime/ui-services.ts +27 -3
  210. package/src/runtime/workstream-services.ts +327 -0
  211. package/src/runtime/wrfc-persistence.ts +124 -17
  212. package/src/shell/blocking-input.ts +68 -4
  213. package/src/shell/recovery-input-helpers.ts +170 -1
  214. package/src/shell/ui-openers.ts +171 -26
  215. package/src/utils/format-duration.ts +8 -18
  216. package/src/utils/splash-lines.ts +1 -1
  217. package/src/utils/terminal-width.ts +52 -0
  218. package/src/version.ts +1 -1
  219. package/src/panels/agent-inspector-panel.ts +0 -786
  220. package/src/panels/approval-panel.ts +0 -252
  221. package/src/panels/automation-control-panel.ts +0 -479
  222. package/src/panels/cockpit-panel.ts +0 -481
  223. package/src/panels/cockpit-read-model.ts +0 -238
  224. package/src/panels/communication-panel.ts +0 -256
  225. package/src/panels/control-plane-panel.ts +0 -470
  226. package/src/panels/debug-panel.ts +0 -615
  227. package/src/panels/docs-panel.ts +0 -384
  228. package/src/panels/eval-panel.ts +0 -627
  229. package/src/panels/file-explorer-panel.ts +0 -673
  230. package/src/panels/file-preview-panel.ts +0 -517
  231. package/src/panels/hooks-panel.ts +0 -401
  232. package/src/panels/incident-review-panel.ts +0 -406
  233. package/src/panels/intelligence-panel.ts +0 -383
  234. package/src/panels/knowledge-graph-panel.ts +0 -515
  235. package/src/panels/marketplace-panel.ts +0 -399
  236. package/src/panels/memory-panel.ts +0 -558
  237. package/src/panels/ops-control-panel.ts +0 -329
  238. package/src/panels/ops-strategy-panel.ts +0 -321
  239. package/src/panels/orchestration-panel.ts +0 -465
  240. package/src/panels/panel-list-panel.ts +0 -566
  241. package/src/panels/plan-dashboard-panel.ts +0 -707
  242. package/src/panels/policy-panel.ts +0 -517
  243. package/src/panels/project-planning-panel.ts +0 -731
  244. package/src/panels/provider-health-panel.ts +0 -678
  245. package/src/panels/provider-health-tracker.ts +0 -310
  246. package/src/panels/provider-health-views.ts +0 -567
  247. package/src/panels/qr-panel.ts +0 -280
  248. package/src/panels/remote-panel.ts +0 -534
  249. package/src/panels/routes-panel.ts +0 -241
  250. package/src/panels/sandbox-panel.ts +0 -456
  251. package/src/panels/security-panel.ts +0 -447
  252. package/src/panels/services-panel.ts +0 -329
  253. package/src/panels/session-browser-panel.ts +0 -496
  254. package/src/panels/settings-sync-panel.ts +0 -398
  255. package/src/panels/subscription-panel.ts +0 -342
  256. package/src/panels/symbol-outline-panel.ts +0 -619
  257. package/src/panels/system-messages-panel.ts +0 -364
  258. package/src/panels/tasks-panel.ts +0 -608
  259. package/src/panels/thinking-panel.ts +0 -333
  260. package/src/panels/tool-inspector-panel.ts +0 -537
  261. package/src/panels/work-plan-panel.ts +0 -540
  262. package/src/panels/worktree-panel.ts +0 -360
  263. package/src/panels/wrfc-panel.ts +0 -790
  264. package/src/renderer/agent-detail-modal.ts +0 -466
  265. package/src/renderer/live-tail-modal.ts +0 -156
  266. package/src/renderer/process-modal.ts +0 -671
  267. package/src/renderer/qr-renderer.ts +0 -120
@@ -13,7 +13,7 @@ import {
13
13
  upsertEcosystemCatalogEntry,
14
14
  uninstallEcosystemCatalogEntry,
15
15
  } from '@/runtime/index.ts';
16
- import { requireEcosystemCatalogPaths, requirePluginPathOptions } from './runtime-services.ts';
16
+ import { openModalCommand, requireEcosystemCatalogPaths, requirePluginPathOptions } from './runtime-services.ts';
17
17
 
18
18
  export function registerPluginRuntimeCommands(registry: CommandRegistry): void {
19
19
  registry.register({
@@ -33,7 +33,7 @@ export function registerPluginRuntimeCommands(registry: CommandRegistry): void {
33
33
  const sub = args[0];
34
34
 
35
35
  if (!sub || sub === 'open' || sub === 'panel') {
36
- if (ctx.showPanel) ctx.showPanel('plugins');
36
+ openModalCommand(ctx, 'plugins-modal');
37
37
  return;
38
38
  }
39
39
 
@@ -301,6 +301,23 @@ export function renderPolicyUsage(): string {
301
301
  ].join('\n');
302
302
  }
303
303
 
304
+ // W6 command-path parity: the policy modal dropped its 'r' (record a
305
+ // divergence-dashboard trend sample) action because the panel used to call
306
+ // this._state.recordTrendEntry() directly and /policy had no equivalent verb.
307
+ // This exposes it as a thin wrapper: recordTrendEntry() forwards to the attached
308
+ // DivergencePanel, so it only captures a sample while a simulation dashboard is
309
+ // active — reported honestly here rather than silently no-op'ing.
310
+ function handleRecordTrend(_args: string[], context: CommandContext): void {
311
+ const policyState = getPolicyState(context);
312
+ if (!policyState.getDashboard()) {
313
+ context.print('[policy] No active simulation dashboard — nothing to sample. Run `/policy simulate` first, then `/policy record-trend`.');
314
+ return;
315
+ }
316
+ policyState.recordTrendEntry();
317
+ const snap = policyState.getSnapshot();
318
+ context.print(`[policy] Recorded a divergence trend sample (${snap.divergence?.trend.length ?? 0} point(s) total).`);
319
+ }
320
+
304
321
  export async function dispatchPolicyCommand(args: string[], context: CommandContext): Promise<void> {
305
322
  const [sub, ...rest] = args;
306
323
  switch (sub) {
@@ -332,6 +349,10 @@ export async function dispatchPolicyCommand(args: string[], context: CommandCont
332
349
  case 'st':
333
350
  await handleStatus(rest, context);
334
351
  break;
352
+ case 'record-trend':
353
+ case 'trend':
354
+ handleRecordTrend(rest, context);
355
+ break;
335
356
  default:
336
357
  context.print(renderPolicyUsage());
337
358
  break;
@@ -29,7 +29,7 @@ export function registerProviderAccountsRuntimeCommands(registry: CommandRegistr
29
29
  async handler(args, ctx) {
30
30
  const sub = (args[0] ?? 'review').toLowerCase();
31
31
  if (sub === 'panel' || sub === 'open') {
32
- openCommandPanel(ctx, 'accounts');
32
+ ctx.openModal?.('providers-modal'); // W6.1: accounts alias -> provider-health modal
33
33
  return;
34
34
  }
35
35
  const snapshot = await loadProviderAccountSnapshot(ctx);
@@ -1,19 +1,39 @@
1
+ import { join } from 'node:path';
1
2
  import type { CommandRegistry } from '../command-registry.ts';
2
- import { openCommandPanel } from './runtime-services.ts';
3
+ import { openModalCommand, requireShellPaths } from './runtime-services.ts';
4
+ import { regenerateCompanionToken } from '@pellux/goodvibes-sdk/platform/pairing';
3
5
 
4
6
  /**
5
7
  * Register the /qrcode command.
6
8
  *
7
- * Opens the QR Code panel which displays a scannable QR code for
8
- * companion app pairing, along with connection URL, token, and username.
9
+ * `/qrcode` (no args) opens the companion-app pairing modal (QR code + URL/
10
+ * token/username). `/qrcode regenerate` rotates the companion pairing token:
11
+ * it re-keys the shared operator-token store, so the previously-issued token is
12
+ * rejected on the next auth and a fresh QR must be scanned to reconnect.
9
13
  */
10
14
  export function registerQrcodeRuntimeCommands(registry: CommandRegistry): void {
11
15
  registry.register({
12
16
  name: 'qrcode',
13
17
  aliases: ['qr', 'pair'],
14
- description: 'Open the QR code panel for companion app pairing',
15
- handler(_args, ctx) {
16
- openCommandPanel(ctx, 'qr-code');
18
+ description: 'Open the companion-app pairing modal (QR code), or regenerate the pairing token',
19
+ usage: '[regenerate]',
20
+ argsHint: '[regenerate]',
21
+ handler(args, ctx) {
22
+ if (args[0]?.toLowerCase() === 'regenerate') {
23
+ const shellPaths = requireShellPaths(ctx);
24
+ const daemonHomeDir = join(shellPaths.homeDirectory, '.goodvibes', 'daemon');
25
+ try {
26
+ const rotated = regenerateCompanionToken({ daemonHomeDir });
27
+ ctx.print(
28
+ 'Pairing token rotated. The previous token is now rejected — ' +
29
+ `re-scan the QR (new peer ${rotated.peerId.slice(0, 8)}…) to reconnect the companion app.`,
30
+ );
31
+ } catch (error) {
32
+ ctx.print(`Could not regenerate the pairing token: ${String(error)}`);
33
+ }
34
+ return;
35
+ }
36
+ openModalCommand(ctx, 'pairing-modal');
17
37
  },
18
38
  });
19
39
  }
@@ -1,6 +1,7 @@
1
1
  import type { CommandContext } from '../command-registry.ts';
2
2
  import { VALID_REVIEW_STATES, VALID_SCOPES, isValidReviewState, isValidScope } from './recall-shared.ts';
3
3
  import { getMemoryApi } from './recall-query.ts';
4
+ import { buildMainSessionTurnInjectionsText, buildTurnInjectionsText } from '../../renderer/turn-injection.ts';
4
5
 
5
6
  export function handleRecallQueue(args: string[], context: CommandContext): void {
6
7
  const memory = getMemoryApi(context);
@@ -102,6 +103,40 @@ export function handleRecallExplain(args: string[], context: CommandContext): vo
102
103
  context.print(explanation.prompt ?? '[recall] No explainable project knowledge was selected.');
103
104
  }
104
105
 
106
+ /**
107
+ * W5.2 (wo803) — per-turn passive knowledge injection inspectability.
108
+ *
109
+ * With an explicit agent id, reads `AgentRecord.turnInjections` (Wave-5 wo801)
110
+ * via `context.ops.agentManager.exportState()` — the CommandContext-exposed
111
+ * `ShellAgentManagerService` doesn't have a `getStatus`/`list` pair, but
112
+ * `exportState()` returns the same full `AgentRecord[]` (it's what
113
+ * compactConversation() already uses for the same reason).
114
+ *
115
+ * With NO agent id (wo805 wiring), renders the MAIN interactive session's own
116
+ * per-turn injection ring — `Orchestrator.getTurnInjections()`, threaded onto
117
+ * the command context as `session.getMainSessionTurnInjections`. As of wo805 the
118
+ * main session DOES route through the passive-injection engine, so this is the
119
+ * honest main-session default (previously there was no record source, so the
120
+ * no-id path could only print a usage hint). When the accessor isn't wired
121
+ * (headless/test contexts built without an orchestrator), the main-session
122
+ * renderer's honest empty state stands in.
123
+ */
124
+ export function handleRecallInjections(args: string[], context: CommandContext): void {
125
+ const agentId = args[0];
126
+ if (!agentId) {
127
+ const mainSessionInjections = context.session.getMainSessionTurnInjections?.() ?? [];
128
+ context.print(buildMainSessionTurnInjectionsText(mainSessionInjections));
129
+ return;
130
+ }
131
+ const records = context.ops.agentManager?.exportState() ?? [];
132
+ const record = records.find((candidate) => candidate.id === agentId);
133
+ if (!record) {
134
+ context.print(`[recall] Agent not found: ${agentId}`);
135
+ return;
136
+ }
137
+ context.print(buildTurnInjectionsText(agentId, record.turnInjections ?? []));
138
+ }
139
+
105
140
  export function handleRecallPromote(args: string[], context: CommandContext): void {
106
141
  const memory = getMemoryApi(context);
107
142
  if (!memory) {
@@ -1,6 +1,7 @@
1
1
  import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import { dirname, resolve } from 'node:path';
3
3
  import { getDefaultAcpAgentCommand } from '@pellux/goodvibes-sdk/platform/acp';
4
+ import { resolveDaemonEnabled } from '@pellux/goodvibes-sdk/platform/config';
4
5
  import type { CommandContext, RemoteCommandService } from '../command-registry.ts';
5
6
  import type { RemoteSessionBundle } from '@/runtime/index.ts';
6
7
  import { requireShellPaths } from './runtime-services.ts';
@@ -37,10 +38,11 @@ export async function handleRemoteSetupCommand(
37
38
  if (subcommand === 'setup') {
38
39
  const command = getDefaultAcpAgentCommand();
39
40
  const danger = ctx.platform.configManager.getCategory('danger');
41
+ const daemonEnabled = resolveDaemonEnabled(ctx.platform.configManager);
40
42
  const lines = [
41
43
  'Remote Setup Review',
42
44
  ` acp agent command: ${command.join(' ')}`,
43
- ` daemon enabled: ${danger.daemon ? 'yes' : 'no'}`,
45
+ ` daemon enabled: ${daemonEnabled ? 'yes' : 'no'}`,
44
46
  ` http listener enabled: ${danger.httpListener ? 'yes' : 'no'}`,
45
47
  ` remote runner contracts: ${remoteRegistry.listContracts().length}`,
46
48
  ` active acp connections: ${activeConnections.length}`,
@@ -48,7 +50,7 @@ export async function handleRemoteSetupCommand(
48
50
  ' guidance:',
49
51
  ' - set ACP_AGENT_CMD to override the spawned remote agent command',
50
52
  ' - use /remote env to export a reusable shell snippet',
51
- ' - enable danger.daemon / danger.httpListener only when you actually need those remote surfaces',
53
+ ' - the daemon runs by default (daemon.enabled); enable danger.httpListener only when you actually need that remote surface',
52
54
  ];
53
55
  if (args[1]?.toLowerCase() === 'export') {
54
56
  const pathArg = args[2];
@@ -62,7 +64,7 @@ export async function handleRemoteSetupCommand(
62
64
  writeFileSync(targetPath, `${JSON.stringify({
63
65
  exportedAt: Date.now(),
64
66
  acpAgentCommand: command,
65
- daemonEnabled: Boolean(danger.daemon),
67
+ daemonEnabled,
66
68
  httpListenerEnabled: Boolean(danger.httpListener),
67
69
  remoteRunnerContracts: remoteRegistry.listContracts().length,
68
70
  }, null, 2)}\n`, 'utf-8');
@@ -47,11 +47,11 @@ export function registerRemoteRuntimeCommands(registry: CommandRegistry): void {
47
47
  usage: '[list | show [agentId] | supervisor [runnerId] | capabilities [runnerId] | recover [runnerId] | setup [export <path>] | env [export <path>] | tunnel [review|export <path>] | bootstrap [export <path>|inspect <path>] | session <export|inspect|import> <path> | pool <list|show|create|assign|unassign> ... | dispatch [template] <description> | dispatch-pool <pool> [template] <description> | contract [agentId] | cancel <agentId> | export <agentId> [path] | artifact list | artifact show <id> | artifact export <id> [path] | review <id> | rerun-local <id> | import <path>]',
48
48
  async handler(args, ctx) {
49
49
  if (args.length === 0) {
50
- if (ctx.openRemotePanel) {
51
- ctx.openRemotePanel();
50
+ if (ctx.openModal) {
51
+ ctx.openModal('remote-modal'); // W6.1: remote panel -> config modal
52
52
  return;
53
53
  }
54
- ctx.print('Remote panel is not available in this runtime.');
54
+ ctx.print('Remote surface is not available in this runtime.');
55
55
  return;
56
56
  }
57
57
 
@@ -9,6 +9,8 @@ import type {
9
9
  } from '../command-registry.ts';
10
10
  import { compactSmallWindow, getLastCompactionEvent, SMALL_WINDOW_THRESHOLD } from '@pellux/goodvibes-sdk/platform/core';
11
11
  import type { CompactionContext, CompactionEvent } from '@pellux/goodvibes-sdk/platform/core';
12
+ import { recordCompactionQualityScore, scoreCompactionRun } from '../../renderer/compaction-quality.ts';
13
+ import type { CompactionQualityScore } from '../../renderer/compaction-quality.ts';
12
14
  import type { UiReadModels } from '../../runtime/ui-read-models.ts';
13
15
  import type { ShellPathService } from '@/runtime/index.ts';
14
16
  import type { EcosystemCatalogPathOptions } from '@/runtime/index.ts';
@@ -101,6 +103,21 @@ export function openCommandPanel(
101
103
  showPanel(panelId, pane);
102
104
  }
103
105
 
106
+ /**
107
+ * W6.1 (group-B migration): open a config-modal surface by name via the
108
+ * ctx.openModal seam (ui-openers wires it; the WO-A host replaces the interim
109
+ * "not available yet" implementation with real dispatch). Front-doors for
110
+ * panels that migrated to modals call this instead of openCommandPanel — the
111
+ * modal is the surface's new home. Stubbed in tests by setting ctx.openModal.
112
+ */
113
+ export function openModalCommand(
114
+ context: Pick<CommandContext, 'openModal'>,
115
+ modalName: string,
116
+ ): void {
117
+ const openModal = requireContextValue(context.openModal, 'openModal');
118
+ openModal(modalName);
119
+ }
120
+
104
121
  export function openOnboardingWizard(
105
122
  context: Pick<CommandContext, 'openOnboardingWizard'>,
106
123
  modeOrOptions?: import('../onboarding/onboarding-wizard.ts').OnboardingWizardMode
@@ -238,12 +255,19 @@ export function requireProviderApi(context: CommandContext): ProviderApi {
238
255
  return requireContextValue(context.clients?.providerApi, 'clients.providerApi');
239
256
  }
240
257
 
258
+ /** Result of a manual /compact run: the SDK's CompactionEvent, plus an
259
+ * out-of-band quality score (W5.4/B28) when one could be computed. */
260
+ export interface CompactionRunResult {
261
+ readonly event: CompactionEvent;
262
+ readonly qualityScore: CompactionQualityScore | null;
263
+ }
264
+
241
265
  /**
242
- * Compact the conversation and return the CompactionEvent recorded by the SDK,
243
- * or null if no event was recorded (e.g. compaction was skipped or produced no
244
- * change).
266
+ * Compact the conversation and return the CompactionEvent recorded by the SDK
267
+ * (plus an out-of-band quality score), or null if no event was recorded (e.g.
268
+ * compaction was skipped or produced no change).
245
269
  */
246
- export async function compactConversation(context: CommandContext): Promise<CompactionEvent | null> {
270
+ export async function compactConversation(context: CommandContext): Promise<CompactionRunResult | null> {
247
271
  const conversationManager = context.session.conversationManager;
248
272
  const providerRegistry = context.provider.providerRegistry;
249
273
  // Resolve the live context window for the current model so manual /compact
@@ -263,20 +287,20 @@ export async function compactConversation(context: CommandContext): Promise<Comp
263
287
  }
264
288
 
265
289
  const eventBefore = getLastCompactionEvent();
290
+ const messagesBefore = conversationManager.getMessagesForLLM();
266
291
  const sessionMemories = context.session.sessionMemoryStore?.list() ?? [];
267
292
  const lineageTracker = context.session.sessionLineageTracker;
268
293
  const agentManager = context.ops.agentManager;
269
294
  const planManager = context.ops.planManager;
270
295
  const compactionCtx: CompactionContext = {
271
- messages: conversationManager.getMessagesForLLM(),
296
+ messages: messagesBefore,
272
297
  sessionMemories,
273
- // Mirror buildAutoCompactionContext: pass the running/pending agents, the
274
- // active execution plan, and the session-lineage log so the handoff summary
275
- // and lineage section are populated correctly (not "compaction #0").
276
- agents:
277
- agentManager
278
- ?.exportState()
279
- .filter((agent) => agent.status === 'running' || agent.status === 'pending') ?? [],
298
+ // Mirror buildAutoCompactionContext: pass ALL agent records (not just
299
+ // running/pending) so completed/failed subagent work reaches the
300
+ // "Completed Agent Work" / "Currently Running" compaction sections, plus
301
+ // the active execution plan and the session-lineage log so the handoff
302
+ // summary and lineage section are populated correctly (not "compaction #0").
303
+ agents: agentManager?.exportState() ?? [],
280
304
  // Mirror buildAutoCompactionContext: include the live WRFC chains (wired
281
305
  // through context.session.wrfcController) so the handoff summary's
282
306
  // orchestration section matches the SDK auto-compaction path.
@@ -284,6 +308,11 @@ export async function compactConversation(context: CommandContext): Promise<Comp
284
308
  activePlan: planManager?.getActive(context.session.runtime.sessionId) ?? null,
285
309
  lineageEntries: lineageTracker?.getEntries() ?? [],
286
310
  compactionCount: lineageTracker?.getCompactionCount() ?? 0,
311
+ // Mirror buildAutoCompactionContext: pass the recorded original task so
312
+ // the "Original task" lineage line reflects the session's actual first
313
+ // task instead of falling back to the current message on every manual
314
+ // compaction after the first.
315
+ originalTask: lineageTracker?.getOriginalTask() ?? undefined,
287
316
  contextWindow,
288
317
  trigger: 'manual',
289
318
  extractionModelId: context.session.runtime.model,
@@ -299,7 +328,19 @@ export async function compactConversation(context: CommandContext): Promise<Comp
299
328
  const eventAfter = getLastCompactionEvent();
300
329
  // Return the new event only if it differs from the one recorded before the call.
301
330
  if (eventAfter !== null && eventAfter !== eventBefore) {
302
- return eventAfter;
331
+ // W5.4/B28: score this run out-of-band (see compaction-quality.ts for why
332
+ // this can't just subscribe to the SDK's own CompactionManager pipeline)
333
+ // and keep the score keyed by this event's timestamp for /compact-history.
334
+ const qualityScore = scoreCompactionRun({
335
+ sessionId: context.session.runtime.sessionId,
336
+ contextWindow,
337
+ messagesBefore,
338
+ messagesAfter: conversationManager.getMessagesForLLM(),
339
+ tokensBefore: eventAfter.tokensBeforeEstimate,
340
+ tokensAfter: eventAfter.tokensAfterEstimate,
341
+ });
342
+ recordCompactionQualityScore(eventAfter.timestamp, qualityScore);
343
+ return { event: eventAfter, qualityScore };
303
344
  }
304
345
  return null;
305
346
  }
@@ -16,7 +16,7 @@ export function registerServicesRuntimeCommands(registry: CommandRegistry): void
16
16
  const sub = args[0] ?? 'open';
17
17
  const shellPaths = requireShellPaths(ctx);
18
18
  if (sub === 'open' || sub === 'panel') {
19
- openCommandPanel(ctx, 'services');
19
+ ctx.openModal?.('services-modal'); // W6.1: services panel -> config modal
20
20
  return;
21
21
  }
22
22
  const svcRegistry = requireServiceRegistry(ctx);
@@ -384,9 +384,20 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
384
384
  },
385
385
  });
386
386
 
387
+ // W6-C3 (Wave 6 core-verb pass, MEMORY fragmentation — worst-class
388
+ // collision #2): this command used to be named /memory, colliding with
389
+ // the agent's /memory (an alias that forwards to /recall, the durable
390
+ // cross-session MemoryStore) — same command name, two unrelated features.
391
+ // Renamed to /note (this feature manages EPHEMERAL, session-scoped sticky
392
+ // notes pinned across context compaction — an entirely different resource
393
+ // from /recall's durable records). /memory is now registered as an alias
394
+ // of /recall below (memory.ts), matching the agent, so the word means the
395
+ // same thing on both surfaces. See
396
+ // docs/decisions/2026-07-06-core-verb-spec.md (in the SDK repo) for the
397
+ // full writeup.
387
398
  registry.register({
388
- name: 'memory',
389
- description: 'Manage session memories (pinned across context compaction)',
399
+ name: 'note',
400
+ description: 'Manage session notes (pinned across context compaction)',
390
401
  usage: '[list|add <text>|remove <id>]',
391
402
  argsHint: '[list|add|remove]',
392
403
  handler(args, ctx) {
@@ -394,26 +405,26 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
394
405
  if (sub === 'list' || args.length === 0) {
395
406
  const memories = requireSessionMemoryStore(ctx).list();
396
407
  ctx.print(memories.length === 0
397
- ? 'No session memories. Use !# prefix or /memory add <text> to create one.'
398
- : [`Session Memories (${memories.length}):`, ...memories.map(m => ` [${m.id}] ${m.text}`)].join('\n'));
408
+ ? 'No session notes. Use !# prefix or /note add <text> to create one.'
409
+ : [`Session Notes (${memories.length}):`, ...memories.map(m => ` [${m.id}] ${m.text}`)].join('\n'));
399
410
  } else if (sub === 'add') {
400
411
  const text = args.slice(1).join(' ').trim();
401
412
  if (!text) {
402
- ctx.print('Usage: /memory add <text>');
413
+ ctx.print('Usage: /note add <text>');
403
414
  return;
404
415
  }
405
416
  const id = requireSessionMemoryStore(ctx).add(text);
406
- ctx.print(`Memory added: [${id}] ${text}`);
417
+ ctx.print(`Note added: [${id}] ${text}`);
407
418
  } else if (sub === 'remove') {
408
419
  const id = args[1];
409
420
  if (!id) {
410
- ctx.print('Usage: /memory remove <id>');
421
+ ctx.print('Usage: /note remove <id>');
411
422
  return;
412
423
  }
413
424
  const store = requireSessionMemoryStore(ctx);
414
- ctx.print(store.remove(id) ? `Memory removed: [${id}]` : `Memory not found: ${id}`);
425
+ ctx.print(store.remove(id) ? `Note removed: [${id}]` : `Note not found: ${id}`);
415
426
  } else {
416
- ctx.print('Usage: /memory [list|add <text>|remove <id>]\n /memory — list all session memories\n /memory list — list all session memories\n /memory add <text> — add a memory without sending a message\n /memory remove <id> — remove a specific memory');
427
+ ctx.print('Usage: /note [list|add <text>|remove <id>]\n /note — list all session notes\n /note list — list all session notes\n /note add <text> — add a note without sending a message\n /note remove <id> — remove a specific note');
417
428
  }
418
429
  },
419
430
  });
@@ -79,11 +79,23 @@ function buildTranscriptReviewLines(
79
79
  ];
80
80
  }
81
81
 
82
- function reopenPanelsFromReturnContext(ctx: CommandContext, summary: SessionReturnContextSummary | undefined): string[] {
82
+ // Exported (test-only concern) so the MIGRATE-TO-MODAL redirect-skip honesty
83
+ // (W6 review, finding 3: saved-layout restore with 'sessions' must not lie)
84
+ // can be unit-tested directly instead of through a full /resume harness.
85
+ export function reopenPanelsFromReturnContext(ctx: CommandContext, summary: SessionReturnContextSummary | undefined): string[] {
83
86
  if (!summary?.openPanels || summary.openPanels.length === 0) return [];
84
87
  const panelManager = requirePanelManager(ctx);
85
88
  const reopened: string[] = [];
89
+ const movedToModal: string[] = [];
86
90
  for (const panelId of summary.openPanels.slice(0, 4)) {
91
+ // W6.1 (the purge): a MIGRATE-TO-MODAL id has no panel to restore — a modal
92
+ // is not part of the saved panel layout. Skip it (don't pop a modal
93
+ // mid-resume) and note it once, rather than firing openModal + revealing an
94
+ // empty workspace during resume.
95
+ if (panelManager.getModalRedirect(panelId) !== undefined) {
96
+ movedToModal.push(panelId);
97
+ continue;
98
+ }
87
99
  try {
88
100
  panelManager.open(panelId);
89
101
  reopened.push(panelId);
@@ -92,6 +104,9 @@ function reopenPanelsFromReturnContext(ctx: CommandContext, summary: SessionRetu
92
104
  }
93
105
  }
94
106
  if (reopened.length > 0) panelManager.show();
107
+ if (movedToModal.length > 0) {
108
+ ctx.print(`Note: ${movedToModal.join(', ')} moved to a modal — reopen via its command instead of as a panel.`);
109
+ }
95
110
  return reopened;
96
111
  }
97
112
 
@@ -26,14 +26,26 @@ export function registerSettingsSyncRuntimeCommands(registry: CommandRegistry):
26
26
  registry.register({
27
27
  name: 'settings-sync',
28
28
  aliases: ['settingssync'],
29
- description: 'Review sync posture, export/import settings-sync bundles, and open the settings sync workspace',
30
- usage: '[review|panel|show <key>|staged|conflicts|resolve <key> <local|synced>|failures|rollback-history|export <path>|inspect <path>|pull <path>|push <path>|lock <key> <source> <reason...>|unlock <key>]',
29
+ description: 'Open the settings sync modal (bare); review posture, export/import bundles, or resolve conflicts by subcommand',
30
+ usage: '[panel|report|review|show <key>|staged|conflicts|resolve <key> <local|synced>|failures|rollback-history|export <path>|inspect <path>|pull <path>|push <path>|lock <key> <source> <reason...>|unlock <key>] — bare opens the modal',
31
31
  handler(args, ctx) {
32
32
  const shellPaths = requireShellPaths(ctx);
33
33
  const controlPlaneConfigDir = ctx.platform.configManager.getControlPlaneConfigDir();
34
- const sub = (args[0] ?? 'review').toLowerCase();
34
+ const sub = (args[0] ?? '').toLowerCase();
35
+ // DEBT-5 item 3 (report-vs-modal front doors): settings-sync has a full
36
+ // modal surface (settings-sync-modal), so the bare command now opens it
37
+ // — the old bare/`review` transcript report moved to an explicit
38
+ // `report` subcommand (scriptability preserved: /settings-sync report).
39
+ if (sub === '') {
40
+ ctx.openModal?.('settings-sync-modal');
41
+ return;
42
+ }
35
43
  if (sub === 'panel' || sub === 'open') {
36
- openCommandPanel(ctx, 'settings-sync');
44
+ ctx.openModal?.('settings-sync-modal'); // W6.1: settings-sync panel -> config modal
45
+ return;
46
+ }
47
+ if (sub === 'report' || sub === 'review') {
48
+ ctx.print(formatSettingsControlPlaneReview(ctx.platform.configManager).join('\n'));
37
49
  return;
38
50
  }
39
51
  if (sub === 'show') {
@@ -153,6 +153,8 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
153
153
  { id: '/template save', label: '/template save <name>', detail: 'Save prompt as template', category: 'Templates' },
154
154
  { id: '/template use', label: '/template use <name>', detail: 'Execute template', category: 'Templates' },
155
155
  { id: '/tools', label: '/tools', detail: 'List available tools', category: 'Tools & System' },
156
+ { id: '/search', label: '/search <query> [--limit <n>]', detail: 'Search the web and show ranked results', category: 'Tools & System' },
157
+ { id: '/imagine', label: '/imagine <prompt>', detail: 'Generate an image from a prompt', category: 'Tools & System' },
156
158
  { id: '/paste', label: '/paste', detail: 'Insert clipboard text or image into the prompt', category: 'Tools & System' },
157
159
  { id: '/shortcuts', label: '/shortcuts', detail: 'View keyboard shortcuts reference', category: 'Tools & System' },
158
160
  { id: '/commands', label: '/commands', detail: 'Browse all commands in a scrollable list', category: 'Tools & System' },
@@ -161,7 +163,10 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
161
163
  { id: '/quit', label: '/quit', detail: 'Exit', category: 'Tools & System' },
162
164
  { id: '/wq', label: '/wq', detail: 'Commit all git changes and then exit', category: 'Tools & System' },
163
165
  ];
164
- ctx.openSelection('Help — Commands', items, { allowSearch: true }, (result) => {
166
+ // UX-C: every item here is a command, and picking one RUNS it — label
167
+ // the verb "Run" (matching the slash-command palette) instead of the
168
+ // generic "Select" default.
169
+ ctx.openSelection('Help — Commands', items, { allowSearch: true, primaryVerbLabel: 'Run' }, (result) => {
165
170
  if (!result) return;
166
171
  const command = result.item.id;
167
172
  if (command.startsWith('/')) {
@@ -207,18 +212,24 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
207
212
  description: 'Summarize conversation to free context window',
208
213
  async handler(_args, ctx) {
209
214
  const messages = ctx.session.conversationManager.getMessagesForLLM();
210
- // contextWindow is not on CommandContext; preview shows message/token counts
211
- // without the capacity-% clause (still honest; no fabricated value).
212
- const contextWindow = 0;
215
+ // W5.4 (wo803): contextWindow IS reachable from CommandContext the same
216
+ // call compactConversation() (runtime-services.ts) already makes to scale
217
+ // its own behaviour by window size. Previously hardcoded to 0 here, which
218
+ // silently suppressed buildCompactionPreview()'s capacity-% clause even
219
+ // though the builder has always supported it.
220
+ const contextWindow = ctx.provider.providerRegistry.getContextWindowForModel(
221
+ ctx.provider.providerRegistry.getCurrentModel(),
222
+ );
213
223
  const memStore = ctx.session.sessionMemoryStore;
214
224
  const pinnedMemoryCount = memStore ? memStore.list().length : 0;
215
225
  // Pre-compact preview: honest estimate, clearly labelled.
216
226
  const preview = buildCompactionPreview({ messages, contextWindow, pinnedMemoryCount, trigger: 'manual' });
217
227
  ctx.print(preview);
218
- const event = await compactConversation(ctx);
219
- if (event) {
220
- // Post-compact notice: uses real CompactionEvent figures.
221
- ctx.print(buildCompactionAfterNotice({ event, pinnedMemoryCount }));
228
+ const result = await compactConversation(ctx);
229
+ if (result) {
230
+ // Post-compact notice: uses real CompactionEvent figures, plus an
231
+ // out-of-band quality-score grade line when one was computed (W5.4/B28).
232
+ ctx.print(buildCompactionAfterNotice({ event: result.event, pinnedMemoryCount, qualityScore: result.qualityScore }));
222
233
  } else {
223
234
  ctx.print('[Context] Compact complete.');
224
235
  }
@@ -286,6 +297,12 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
286
297
  handler(_args, ctx) {
287
298
  ctx.session.runtime.debugMode = !ctx.session.runtime.debugMode;
288
299
  ctx.print(`Debug mode: ${ctx.session.runtime.debugMode ? 'ON' : 'OFF'}`);
300
+ // Honest quiet counter for direct terminal writes the guard intercepted
301
+ // (routed here instead of spamming the transcript). (UX-B item 1a.)
302
+ const intercepted = ctx.session.runtime.terminalWritesIntercepted ?? 0;
303
+ if (intercepted > 0) {
304
+ ctx.print(`Terminal writes intercepted this session: ${intercepted} (details in the activity log)`);
305
+ }
289
306
  },
290
307
  });
291
308
 
@@ -11,7 +11,7 @@ import {
11
11
  updateInstalledEcosystemEntry,
12
12
  upsertEcosystemCatalogEntry,
13
13
  } from '@/runtime/index.ts';
14
- import { requireEcosystemCatalogPaths, requirePanelManager, requireShellPaths } from './runtime-services.ts';
14
+ import { openModalCommand, requireEcosystemCatalogPaths, requireShellPaths } from './runtime-services.ts';
15
15
 
16
16
  export function registerSkillsRuntimeCommands(registry: CommandRegistry): void {
17
17
  registry.register({
@@ -22,13 +22,7 @@ export function registerSkillsRuntimeCommands(registry: CommandRegistry): void {
22
22
  async handler(args, ctx) {
23
23
  const sub = args[0] ?? 'open';
24
24
  if (sub === 'open' || sub === 'panel') {
25
- if (ctx.showPanel) ctx.showPanel('skills');
26
- else {
27
- const panelManager = requirePanelManager(ctx);
28
- panelManager.open('skills');
29
- panelManager.show();
30
- ctx.renderRequest();
31
- }
25
+ openModalCommand(ctx, 'skills-modal');
32
26
  return;
33
27
  }
34
28
  const skills = await discoverSkills(requireShellPaths(ctx));
@@ -79,8 +79,8 @@ export function registerSubscriptionRuntimeCommands(registry: CommandRegistry):
79
79
  usage: '[review|list|providers|inspect <provider>|login <provider> start [--no-browser] [--manual]|finish <code-or-url>|logout <provider>|bundle export <path>|bundle inspect <path>]',
80
80
  async handler(args, ctx) {
81
81
  const shellPaths = requireShellPaths(ctx);
82
- if (args.length === 0 && ctx.openSubscriptionPanel) {
83
- ctx.openSubscriptionPanel();
82
+ if (args.length === 0 && ctx.openModal) {
83
+ ctx.openModal('subscription-modal'); // W6.1: subscription panel -> config modal
84
84
  return;
85
85
  }
86
86
  const sub = (args[0] ?? 'review').toLowerCase();