@pellux/goodvibes-tui 0.18.20 → 0.19.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 (83) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/README.md +1 -1
  3. package/docs/foundation-artifacts/operator-contract.json +1 -1
  4. package/package.json +7 -3
  5. package/src/core/conversation-rendering.ts +22 -6
  6. package/src/core/orchestrator.ts +1 -1
  7. package/src/input/commands/diff-runtime.ts +6 -5
  8. package/src/input/commands/guidance-runtime.ts +1 -1
  9. package/src/input/commands/health-runtime.ts +2 -2
  10. package/src/input/commands/local-setup-review.ts +1 -1
  11. package/src/input/commands/session-content.ts +1 -1
  12. package/src/input/commands/session.ts +0 -1
  13. package/src/input/commands/shell-core.ts +3 -2
  14. package/src/input/commands/skills-runtime.ts +2 -2
  15. package/src/input/commands/subscription-runtime.ts +4 -4
  16. package/src/input/feed-context-factory.ts +236 -0
  17. package/src/input/handler-feed.ts +44 -6
  18. package/src/input/handler-shortcuts.ts +138 -125
  19. package/src/input/handler.ts +119 -119
  20. package/src/input/keybindings.ts +30 -0
  21. package/src/input/panel-integration-actions.ts +2 -1
  22. package/src/input/settings-modal-types.ts +60 -0
  23. package/src/input/settings-modal.ts +83 -65
  24. package/src/panels/agent-inspector-panel.ts +10 -9
  25. package/src/panels/agent-logs-panel.ts +26 -6
  26. package/src/panels/approval-panel.ts +55 -82
  27. package/src/panels/automation-control-panel.ts +120 -161
  28. package/src/panels/base-panel.ts +108 -3
  29. package/src/panels/communication-panel.ts +69 -107
  30. package/src/panels/context-visualizer-panel.ts +2 -0
  31. package/src/panels/control-plane-panel.ts +117 -172
  32. package/src/panels/diff-panel.ts +2 -0
  33. package/src/panels/file-explorer-panel.ts +51 -31
  34. package/src/panels/file-preview-panel.ts +57 -35
  35. package/src/panels/git-panel.ts +12 -13
  36. package/src/panels/hooks-panel.ts +103 -138
  37. package/src/panels/incident-review-panel.ts +59 -109
  38. package/src/panels/knowledge-panel.ts +75 -107
  39. package/src/panels/local-auth-panel.ts +77 -93
  40. package/src/panels/marketplace-panel.ts +51 -69
  41. package/src/panels/mcp-panel.ts +110 -155
  42. package/src/panels/memory-panel.ts +90 -158
  43. package/src/panels/ops-control-panel.ts +51 -85
  44. package/src/panels/orchestration-panel.ts +70 -51
  45. package/src/panels/panel-list-panel.ts +5 -4
  46. package/src/panels/panel-manager.ts +25 -2
  47. package/src/panels/plan-dashboard-panel.ts +2 -0
  48. package/src/panels/plugins-panel.ts +37 -60
  49. package/src/panels/polish.ts +51 -2
  50. package/src/panels/provider-accounts-panel.ts +1 -0
  51. package/src/panels/provider-health-panel.ts +6 -8
  52. package/src/panels/routes-panel.ts +91 -141
  53. package/src/panels/schedule-panel.ts +7 -6
  54. package/src/panels/scrollable-list-panel.ts +64 -16
  55. package/src/panels/security-panel.ts +118 -152
  56. package/src/panels/services-panel.ts +63 -105
  57. package/src/panels/session-browser-panel.ts +19 -18
  58. package/src/panels/settings-sync-panel.ts +79 -123
  59. package/src/panels/skills-panel.ts +114 -230
  60. package/src/panels/subscription-panel.ts +64 -86
  61. package/src/panels/system-messages-panel.ts +147 -141
  62. package/src/panels/tasks-panel.ts +130 -179
  63. package/src/panels/token-budget-panel.ts +2 -0
  64. package/src/panels/watchers-panel.ts +89 -137
  65. package/src/panels/worktree-panel.ts +1 -0
  66. package/src/panels/wrfc-panel.ts +2 -0
  67. package/src/renderer/agent-detail-modal.ts +2 -2
  68. package/src/renderer/ansi-sanitize.ts +76 -0
  69. package/src/renderer/buffer.ts +23 -1
  70. package/src/renderer/diff.ts +8 -0
  71. package/src/renderer/help-overlay.ts +48 -28
  72. package/src/renderer/markdown.ts +3 -145
  73. package/src/renderer/settings-modal-helpers.ts +27 -0
  74. package/src/renderer/settings-modal.ts +18 -1
  75. package/src/renderer/status-glyphs.ts +21 -0
  76. package/src/renderer/status-token.ts +4 -8
  77. package/src/renderer/tool-call.ts +4 -3
  78. package/src/runtime/bootstrap-core.ts +1 -1
  79. package/src/runtime/bootstrap-hook-bridge.ts +1 -1
  80. package/src/runtime/bootstrap.ts +7 -8
  81. package/src/runtime/diagnostics/panels/policy.ts +2 -1
  82. package/src/shell/ui-openers.ts +1 -1
  83. package/src/version.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,160 @@ All notable changes to GoodVibes TUI.
4
4
 
5
5
  ---
6
6
 
7
+ ## [0.19.0] — 2026-04-18
8
+
9
+ ### Changed
10
+ - Upgraded `@pellux/goodvibes-sdk` from 0.19.6 to 0.21.1 (soak-period release).
11
+ TUI adaptation required: regenerated `docs/foundation-artifacts/operator-contract.json`
12
+ to match the updated `buildOperatorContract()` output in the new SDK version.
13
+ `peer-contract.json`, `knowledge-graphql.graphql`, and `knowledge-store.sql` were
14
+ unchanged by this SDK bump.
15
+
16
+ ### Added
17
+ - Wave B panel migration: migrated 5 panels (knowledge, marketplace, memory,
18
+ system-messages, orchestration) to `ScrollableListPanel<T>`/`SearchableListPanel<T>`
19
+ generics; added `docs/panel-authoring.md` as the canonical panel authoring guide.
20
+ - Wave C-α reliability pass: F-perf-01 (trackedRender on 5 hot panels),
21
+ F-perf-02 (async panel fs + skills-panel de-blocking), F-perf-03 (timer registry
22
+ + 5-panel zombie-timer leak prevention), F-errors-02 (observable async failures —
23
+ no silent `.catch(() => {})`), F-sec-02 (ANSI escape sanitization at
24
+ tool-call untrusted-content entry points).
25
+ - `src/input/settings-modal-types.ts`: extracted `SettingsCategory`,
26
+ `SETTINGS_CATEGORIES`, `SettingEntry`, `FlagEntry`, `McpEntry`,
27
+ `SubscriptionEntry` type definitions out of the settings-modal module.
28
+
29
+ ### Security
30
+ - Postinstall patcher from `@pellux/goodvibes-sdk@0.21.1` mitigates three
31
+ minimatch ReDoS advisories in the consumer install tree.
32
+ - Added `overrides: { minimatch: ^10.2.5 }` to TUI's own `package.json`
33
+ so `npm audit` reports clean for the TUI install tree independently.
34
+
35
+ ### Fixed
36
+ - Foundation artifacts gate now passes: `operator-contract.json` updated to
37
+ match SDK 0.21.1 `buildOperatorContract()` canonical output.
38
+
39
+ ---
40
+
41
+ ## [0.18.23] — 2026-04-16
42
+
43
+ ### Wave 4α+β performance bundle + α review follow-ups + regression fixes
44
+
45
+ Bundles Wave 4α (conversation-rendering double-parse elimination) and Wave 4β
46
+ (feed-context object reuse) into a single release, adds documentation and test
47
+ follow-ups from the 4α review, and fixes two regressions surfaced during that
48
+ review.
49
+
50
+ ### Performance (Wave 4α — conversation-rendering)
51
+
52
+ - **`src/core/conversation-rendering.ts`** — eliminates the legacy `renderMarkdown()`
53
+ duplicate call used for `'code'` mode line counting. The 'all' mode retain its
54
+ intentional measurement pass (see inline comment for rationale); the 'code' mode
55
+ now derives its gutter width from the single `renderMarkdownTracked()` call.
56
+
57
+ ### Performance (Wave 4β — feed-context object reuse)
58
+
59
+ - **`src/input/handler.ts`** — `InputHandler` now allocates a single `InputFeedContext`
60
+ at startup (`initFeedContext()`) and mutates only the 14 mutable fields before
61
+ each `feedInputTokens()` call via `syncFeedContextMutableFields()`. Stable service
62
+ handles, closures, and callbacks are wired once and never re-assigned. Eliminates
63
+ per-keystroke allocations on the hot input path.
64
+ - **`src/input/feed-context-factory.ts`** (new) — extracts `buildInitialFeedContext()`
65
+ and the `FeedContextMutableInit` / `FeedContextStableRefs` / `FeedContextClosures`
66
+ interfaces out of `handler.ts` to keep `handler.ts` under the 800-line architecture
67
+ cap.
68
+
69
+ ### Regression fixes
70
+
71
+ - **R1 — `handler.ts` architecture cap** — `handler.ts` was 830 lines after Wave 4β.
72
+ Extracted factory functions into `src/input/feed-context-factory.ts`; `handler.ts`
73
+ is now exactly 800 lines. `bun run architecture:check` passes.
74
+ - **R2 — `SearchableListPanel.buildFilterInputLine` cursor glyph** — the focused
75
+ filter line (`[Label] query_`) was rendering the trailing `_` as the block cursor
76
+ glyph `█` because `buildSearchInputLine` substitutes `_` → `█` when `active:true`.
77
+ Fixed by passing `active:false` with explicit `inputBg`/`info` colors; the
78
+ `[Label] ` bracket format provides the focused visual affordance without triggering
79
+ the substitution. Fixes the pre-existing test failure from 0.18.22.
80
+
81
+ ### Quality bumps (Wave 4α review follow-ups)
82
+
83
+ - **F1** — `src/test/input/feed-context-reuse.test.ts`: added mutable-field assertion
84
+ verifying `ctx.prompt` and `ctx.cursorPos` update between feeds (feeds 'a' → 'b',
85
+ asserts accumulated mutation).
86
+ - **F2** — `src/test/input/keybinding-lookup.test.ts`: replaced no-op reload test
87
+ with a real temp-file config override (remaps `search` → Ctrl+G, verifies Ctrl+F
88
+ returns null); added a second test for conflicting bindings (two actions mapped to
89
+ same combo resolves to one of them, not null).
90
+ - **F3** — `src/input/handler-feed.ts`: added JSDoc to `InputFeedContext` interface
91
+ documenting all mutable-per-feed fields vs. stable service handles and explaining
92
+ the reuse rationale.
93
+ - **F4** — `src/input/feed-context-factory.ts`: `syncFeedContextMutableFields()` has
94
+ full JSDoc listing every synced field and documenting intentional exclusions.
95
+ - **F5** — `src/core/conversation-rendering.ts`: added inline comment block explaining
96
+ why 'all' mode requires a double-call to `renderMarkdownTracked` (Option B) and
97
+ why single-pass is not feasible.
98
+
99
+ ---
100
+
101
+ ## [0.18.22] — 2026-04-16
102
+
103
+ ### Wave 3b / Tier 2 TUI UX Consistency — Panel Migration Batch
104
+
105
+ Migrates 7 more BasePanel-direct panels to `ScrollableListPanel<T>`, restores
106
+ section-title text lost during prior migrations, and fixes 8 pre-existing test regressions.
107
+
108
+ ### Migrated panels
109
+
110
+ - **`src/panels/hooks-panel.ts`** — `HooksPanel` → `ScrollableListPanel<HookEntry>`.
111
+ Contracts/chains/managed/file stats in `header`; selected hook detail, activity (with `Recent Activity` label), authoring (with `Authoring` label) in `footer`. Empty state shows extra context via `header` parameter.
112
+ - **`src/panels/mcp-panel.ts`** — `McpPanel` → `ScrollableListPanel<McpServerSecurityEntry>`.
113
+ Derived type via `ReturnType<McpRegistry['listServerSecurity']>[number]` since no named export exists.
114
+ `MCP posture` label + stats + guidance in `header`; selected server detail, repair actions, decision log in `footer`.
115
+ - **`src/panels/approval-panel.ts`** — `ApprovalPanel` → `ScrollableListPanel<ApprovalRow>`.
116
+ `Approval posture` label + approval counts + guidance + `Selected Lane` label + detail in `header`; nav hint in `footer`.
117
+ - **`src/panels/security-panel.ts`** — `SecurityPanel` → `ScrollableListPanel<TokenAuditResult>`.
118
+ Governance + threat lines in `header`; selected detail + attack path findings in `footer`.
119
+ - **`src/panels/services-panel.ts`** — `ServicesPanel` → `ScrollableListPanel<ServicePanelEntry>`.
120
+ `r` (refresh) and `t` (test selected) key overrides preserved; loading state handled via early return.
121
+ - **`src/panels/subscription-panel.ts`** — `SubscriptionPanel` → `ScrollableListPanel<SubscriptionRow>`.
122
+ Fully overrides `handleInput` (uses `ArrowUp`/`ArrowDown`); logout confirm state preserved; empty state uses direct `buildPanelWorkspace` path.
123
+ - **`src/panels/tasks-panel.ts`** — `TasksPanel` → `ScrollableListPanel<RuntimeTask>`.
124
+ `!readModel` early-exit preserved; `buildSummaryBlock` in `header`, `buildDetailBlock` in `footer`.
125
+ - **`src/panels/incident-review-panel.ts`** — `IncidentReviewPanel` → `ScrollableListPanel<FailureReport>`.
126
+ `Action Rail` label added before action guidance lines in `footer`.
127
+ - **`src/panels/communication-panel.ts`** — `CommunicationPanel` → `ScrollableListPanel<CommunicationRecord>`.
128
+ `Communication posture` label added to both posture line arrays (empty + populated states).
129
+
130
+ ### Panels kept as BasePanel
131
+
132
+ - `PolicyPanel`, `RemotePanel`, `ProviderHealthPanel`, `PanelListPanel`, `OrchestrationPanel`,
133
+ `MarketplacePanel`, `SchedulePanel`, `MemoryPanel`, `KnowledgePanel`, `SkillsPanel`,
134
+ `SessionBrowserPanel` — all use `resolveScrollablePanelSection`, multi-line-per-item render,
135
+ dual-mode browsing, `setInterval`, or `canRenderNow()` / `reportRenderDuration()` patterns
136
+ incompatible with `ScrollableListPanel<T>`.
137
+
138
+ ### Test fixes
139
+
140
+ Restored section-title strings (`'Approval posture'`, `'Communication posture'`, `'MCP posture'`,
141
+ `'Action Rail'`, `'Recent Activity'`, `'Selected Lane'`) dropped during migration, fixing 8
142
+ regressions across `approval-panel`, `communication-panel`, `hooks-panel`, `mcp-panel`, and
143
+ `incident-review-panel` test files.
144
+
145
+ ---
146
+
147
+ ## [0.18.21] — 2026-04-16
148
+
149
+ ### Wave 3a / Tier 2 TUI UX Consistency Infrastructure — I5
150
+
151
+ Final item from Wave 3a: selection gutter and filter input UX consistency across list panels.
152
+
153
+ ### I5 — Selection gutter + filter input label conventions
154
+
155
+ - **`src/panels/scrollable-list-panel.ts`** — `ScrollableListPanel`: added opt-in `protected showSelectionGutter = false`. When enabled, `renderList()` post-processes each item line to prepend a 2-column left gutter: `▸ ` (info color, bold) for the selected row, ` ` for all others. Line width is preserved by dropping the last 2 cells. Default off to avoid breaking panels with custom selection indicators.
156
+ - **`src/panels/scrollable-list-panel.ts`** — `SearchableListPanel`: added `protected buildFilterInputLine(width, label, focused)`. Renders the filter line with context-sensitive label formatting: `[Filter] query_` when `focused=true` (active, bold, cursor visible), `Filter: query` when `focused=false` (dim, no cursor). Delegates to `buildSearchInputLine` from `polish.ts`.
157
+ - **New test file `src/test/panels/scrollable-list-panel-i5.test.ts`**: 13 tests covering gutter on/off, column position of `▸`, line-width preservation, and filter label format in both focused/unfocused states.
158
+
159
+ ---
160
+
7
161
  ## [0.18.20] — 2026-04-16
8
162
 
9
163
  ### Wave 3a / Tier 2 TUI UX Consistency Infrastructure
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![CI](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml/badge.svg)](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
- [![Version](https://img.shields.io/badge/version-0.18.20-blue.svg)](https://github.com/mgd34msu/goodvibes-tui)
5
+ [![Version](https://img.shields.io/badge/version-0.19.0-blue.svg)](https://github.com/mgd34msu/goodvibes-tui)
6
6
 
7
7
  A terminal-native AI coding, operations, automation, knowledge, and integration console with a typed runtime, omnichannel surfaces, structured memory/knowledge, and a raw ANSI renderer.
8
8
 
@@ -3,7 +3,7 @@
3
3
  "product": {
4
4
  "id": "goodvibes",
5
5
  "surface": "operator",
6
- "version": "0.18.37"
6
+ "version": "0.21.1"
7
7
  },
8
8
  "auth": {
9
9
  "modes": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-tui",
3
- "version": "0.18.20",
3
+ "version": "0.19.0",
4
4
  "description": "Terminal-native GoodVibes product for coding, operations, automation, knowledge, channels, and daemon-backed control-plane workflows.",
5
5
  "type": "module",
6
6
  "main": "src/main.ts",
@@ -89,7 +89,7 @@
89
89
  "@anthropic-ai/vertex-sdk": "^0.16.0",
90
90
  "@ast-grep/napi": "^0.42.0",
91
91
  "@aws/bedrock-token-generator": "^1.1.0",
92
- "@pellux/goodvibes-sdk": "0.18.37",
92
+ "@pellux/goodvibes-sdk": "0.21.1",
93
93
  "bash-language-server": "^5.6.0",
94
94
  "fuse.js": "^7.1.0",
95
95
  "graphql": "^16.13.2",
@@ -115,8 +115,12 @@
115
115
  "typescript": "^5.9.3"
116
116
  },
117
117
  "trustedDependencies": [
118
+ "@pellux/goodvibes-sdk",
118
119
  "tree-sitter-css",
119
120
  "tree-sitter-javascript",
120
121
  "tree-sitter-python"
121
- ]
122
+ ],
123
+ "overrides": {
124
+ "minimatch": "^10.2.5"
125
+ }
122
126
  }
@@ -1,5 +1,5 @@
1
1
  import { UIFactory } from '../renderer/ui-factory.ts';
2
- import { renderMarkdown, renderMarkdownTracked } from '../renderer/markdown.ts';
2
+ import { renderMarkdownTracked } from '../renderer/markdown.ts';
3
3
  import { renderToolCallBlock } from '../renderer/tool-call.ts';
4
4
  import { renderThinkingBlock } from '../renderer/thinking.ts';
5
5
  import { renderSystemMessage } from '../renderer/system-message.ts';
@@ -102,10 +102,26 @@ export function renderConversationAssistantMessage(
102
102
  if (message.content) {
103
103
  const showAllLineNumbers = lineNumberMode === 'all';
104
104
  const showCodeBlockLineNumbers = lineNumberMode === 'all' ? false : lineNumberMode === 'code';
105
- const preRendered = showAllLineNumbers
106
- ? renderMarkdown(message.content, width, { codeBlockLineNumbers: false })
107
- : null;
108
- const totalLines = preRendered?.length ?? 0;
105
+ // First pass: measure totalLines for gutter sizing (only when line-numbers='all').
106
+ // When line numbers are off, skip the measurement pass entirely.
107
+ //
108
+ // NOTE: The 'all' mode intentionally calls renderMarkdownTracked twice:
109
+ // 1. Measure pass: render at full `width` to get the total line count, which
110
+ // determines `numWidth` (digit count) and thus `gutterW` (gutter column width).
111
+ // 2. Render pass: render at `width - gutterW` with the gutter factored in.
112
+ //
113
+ // Single-pass is not pursued here. It would require either a pessimistic
114
+ // `numWidth=6` (fits 999,999 lines, but wastes 3-4 gutter columns on typical
115
+ // messages) or rendering the numbered output into a scratch buffer and trimming.
116
+ // Neither is clearly better than the current two-pass measurement approach.
117
+ // The 4α commit message claim that this "eliminates double-parse when line
118
+ // numbers are enabled" was inaccurate: 4α eliminated the legacy
119
+ // `renderMarkdown()` duplicate used for code-block line-number mode ('code').
120
+ // The 'all' mode double-call is a deliberate design choice and remains unchanged.
121
+ const measureWidth = showAllLineNumbers ? width : 0;
122
+ const totalLines = showAllLineNumbers
123
+ ? renderMarkdownTracked(message.content, measureWidth, { codeBlockLineNumbers: false }).lines.length
124
+ : 0;
109
125
  const numWidth = Math.max(3, String(totalLines).length);
110
126
  const gutterW = numWidth + 3;
111
127
  const contentWidth = showAllLineNumbers ? width - gutterW : width;
@@ -239,7 +255,7 @@ export function renderConversationToolMessage(
239
255
  // Leave invalid JSON as-is.
240
256
  }
241
257
  }
242
- context.history.addLines(renderMarkdown(contentToRender, width));
258
+ context.history.addLines(renderMarkdownTracked(contentToRender, width).lines);
243
259
  }
244
260
 
245
261
  const renderedLineCount = context.history.getLineCount() - startLine;
@@ -1,3 +1,3 @@
1
1
  // Thin TUI wrapper — re-exports the SDK Orchestrator unchanged.
2
2
  // The SDK class already contains all behaviour including getSpinner().
3
- export { Orchestrator } from '@pellux/goodvibes-sdk/platform/core/orchestrator';
3
+ export { Orchestrator, type OrchestratorOptions } from '@pellux/goodvibes-sdk/platform/core/orchestrator';
@@ -1,4 +1,5 @@
1
1
  import { join } from 'path';
2
+ import { logger } from '@pellux/goodvibes-sdk/platform/utils/logger';
2
3
  import type { CommandRegistry } from '../command-registry.ts';
3
4
  import { requirePanelManager, requireSessionChangeTracker, requireShellPaths } from './runtime-services.ts';
4
5
 
@@ -86,7 +87,7 @@ export function registerDiffRuntimeCommands(registry: CommandRegistry): void {
86
87
  return (await new Response(proc.stdout).text()).trim().split('\n').filter(Boolean);
87
88
  })();
88
89
  if (workingChangedFiles.length > 0) {
89
- enrichSemanticDiff(diffPanel, workingChangedFiles, 'HEAD', () => ctx.renderRequest(), workingDirectory).catch(() => {});
90
+ enrichSemanticDiff(diffPanel, workingChangedFiles, 'HEAD', () => ctx.renderRequest(), workingDirectory).catch((err) => { logger.debug('semantic diff enrichment failed', { err }); });
90
91
  }
91
92
  break;
92
93
  }
@@ -111,7 +112,7 @@ export function registerDiffRuntimeCommands(registry: CommandRegistry): void {
111
112
  return (await new Response(stagedProc.stdout).text()).trim().split('\n').filter(Boolean);
112
113
  })();
113
114
  if (stagedChangedFiles.length > 0) {
114
- enrichSemanticDiff(diffPanel, stagedChangedFiles, 'HEAD', () => ctx.renderRequest(), workingDirectory).catch(() => {});
115
+ enrichSemanticDiff(diffPanel, stagedChangedFiles, 'HEAD', () => ctx.renderRequest(), workingDirectory).catch((err) => { logger.debug('semantic diff enrichment failed', { err }); });
115
116
  }
116
117
  }
117
118
  break;
@@ -126,7 +127,7 @@ export function registerDiffRuntimeCommands(registry: CommandRegistry): void {
126
127
  return (await new Response(proc.stdout).text()).trim().split('\n').filter(Boolean);
127
128
  })();
128
129
  if (headChangedFiles.length > 0) {
129
- enrichSemanticDiff(diffPanel, headChangedFiles, 'HEAD', () => ctx.renderRequest(), workingDirectory).catch(() => {});
130
+ enrichSemanticDiff(diffPanel, headChangedFiles, 'HEAD', () => ctx.renderRequest(), workingDirectory).catch((err) => { logger.debug('semantic diff enrichment failed', { err }); });
130
131
  }
131
132
  break;
132
133
  }
@@ -137,7 +138,7 @@ export function registerDiffRuntimeCommands(registry: CommandRegistry): void {
137
138
  ctx.print(`Loading session diff (${sessionFiles.length} file${sessionFiles.length === 1 ? '' : 's'} changed this session)...`);
138
139
  await diffPanel.showFileDiffs(sessionFiles, 'HEAD');
139
140
  ctx.print(`Diff panel updated: ${sessionFiles.length} session file${sessionFiles.length === 1 ? '' : 's'}.`);
140
- enrichSemanticDiff(diffPanel, sessionFiles, 'HEAD', () => ctx.renderRequest(), workingDirectory).catch(() => {});
141
+ enrichSemanticDiff(diffPanel, sessionFiles, 'HEAD', () => ctx.renderRequest(), workingDirectory).catch((err) => { logger.debug('semantic diff enrichment failed', { err }); });
141
142
  } else {
142
143
  ctx.print('No session changes tracked yet. Showing diff vs HEAD...');
143
144
  await diffPanel.showGitDiff('HEAD');
@@ -148,7 +149,7 @@ export function registerDiffRuntimeCommands(registry: CommandRegistry): void {
148
149
  return (await new Response(proc.stdout).text()).trim().split('\n').filter(Boolean);
149
150
  })();
150
151
  if (fallbackFiles.length > 0) {
151
- enrichSemanticDiff(diffPanel, fallbackFiles, 'HEAD', () => ctx.renderRequest(), workingDirectory).catch(() => {});
152
+ enrichSemanticDiff(diffPanel, fallbackFiles, 'HEAD', () => ctx.renderRequest(), workingDirectory).catch((err) => { logger.debug('semantic diff enrichment failed', { err }); });
152
153
  }
153
154
  }
154
155
  break;
@@ -64,7 +64,7 @@ export function registerGuidanceRuntimeCommands(registry: CommandRegistry): void
64
64
  }
65
65
 
66
66
  const providerApi = requireProviderApi(ctx);
67
- const currentModel = await providerApi.getCurrentModel().catch(() => null);
67
+ const currentModel = await providerApi.getCurrentModel().catch(() => null); // best-effort: null handled as unknown context window
68
68
  const llmMessages = ctx.session.conversationManager.getMessagesForLLM();
69
69
  const readModels = requireReadModels(ctx);
70
70
  const session = readModels.session.getSnapshot();
@@ -240,7 +240,7 @@ export function registerHealthRuntimeCommands(registry: CommandRegistry): void {
240
240
  if (sub === 'maintenance') {
241
241
  const session = readModels.session.getSnapshot();
242
242
  const providerApi = requireProviderApi(ctx);
243
- const currentModel = await providerApi.getCurrentModel().catch(() => null);
243
+ const currentModel = await providerApi.getCurrentModel().catch(() => null); // best-effort: null handled as unknown context window
244
244
  const llmMessages = typeof ctx.session.conversationManager.getMessagesForLLM === 'function'
245
245
  ? ctx.session.conversationManager.getMessagesForLLM()
246
246
  : [];
@@ -369,7 +369,7 @@ export function registerHealthRuntimeCommands(registry: CommandRegistry): void {
369
369
 
370
370
  const session = readModels.session.getSnapshot();
371
371
  const providerApi = requireProviderApi(ctx);
372
- const currentModel = await providerApi.getCurrentModel().catch(() => null);
372
+ const currentModel = await providerApi.getCurrentModel().catch(() => null); // best-effort: null handled as unknown context window
373
373
  const llmMessages = typeof ctx.session.conversationManager.getMessagesForLLM === 'function'
374
374
  ? ctx.session.conversationManager.getMessagesForLLM()
375
375
  : [];
@@ -22,7 +22,7 @@ export async function buildSetupReviewSnapshot(ctx: CommandContext): Promise<Set
22
22
  }
23
23
  }
24
24
 
25
- const skills = discoverSkills(shellPaths);
25
+ const skills = await discoverSkills(shellPaths);
26
26
  const security = requireReadModels(ctx).security.getSnapshot();
27
27
  const plugins = security.plugins;
28
28
  const mcpServers = security.mcpServers;
@@ -161,7 +161,7 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
161
161
 
162
162
  registry.register({
163
163
  name: 'undo',
164
- aliases: ['u'],
164
+ aliases: [],
165
165
  description: 'Undo last action. /undo file — revert last file write/edit. /undo — remove last conversation turn.',
166
166
  usage: '[file]',
167
167
  argsHint: '[file]',
@@ -347,7 +347,6 @@ export const sessionCommand: SlashCommand = {
347
347
  break;
348
348
 
349
349
  case 'cancel':
350
- case 'x':
351
350
  handleCancel(rest, context);
352
351
  break;
353
352
 
@@ -5,6 +5,7 @@ import { REASONING_BUDGET_MAP } from '@pellux/goodvibes-sdk/platform/providers/i
5
5
  import { executeWriteQuit } from './quit-shared.ts';
6
6
  import { compactConversation, requireKeybindingsManager, requireProviderApi } from './runtime-services.ts';
7
7
  import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils/error-display';
8
+ import { logger } from '@pellux/goodvibes-sdk/platform/utils/logger';
8
9
 
9
10
  export function registerShellCoreCommands(registry: CommandRegistry): void {
10
11
  registry.register({
@@ -34,7 +35,7 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
34
35
  ctx.platform.configManager.set('provider.model', selected.registryKey);
35
36
  ctx.platform.configManager.set('provider.provider', selected.providerId);
36
37
  ctx.print(`Switched to model: ${selected.displayName} (${selected.providerId})`);
37
- void providerApi.recordModelUsage(selected.registryKey).catch(() => undefined);
38
+ void providerApi.recordModelUsage(selected.registryKey).catch((err) => { logger.debug('model usage record failed', { err }); });
38
39
  } catch (e) {
39
40
  ctx.print(`Error: ${summarizeError(e)}`);
40
41
  }
@@ -202,7 +203,7 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
202
203
 
203
204
  registry.register({
204
205
  name: 'quit',
205
- aliases: ['q', ':q'],
206
+ aliases: [':q'],
206
207
  description: 'Exit the application',
207
208
  handler(_args, ctx) {
208
209
  ctx.exit();
@@ -19,7 +19,7 @@ export function registerSkillsRuntimeCommands(registry: CommandRegistry): void {
19
19
  aliases: ['skill'],
20
20
  description: 'Inspect installed skill packs',
21
21
  usage: '[open|list|show <name>|origins|browse [query]|installed|catalog-review <id>|publish-local <id> <path> <summary...>|unpublish <id>|install-hint <catalog-id>|install <id> [project|user]|update <id> [project|user]|uninstall <id> [project|user]]',
22
- handler(args, ctx) {
22
+ async handler(args, ctx) {
23
23
  const sub = args[0] ?? 'open';
24
24
  if (sub === 'open' || sub === 'panel') {
25
25
  if (ctx.showPanel) ctx.showPanel('skills');
@@ -31,7 +31,7 @@ export function registerSkillsRuntimeCommands(registry: CommandRegistry): void {
31
31
  }
32
32
  return;
33
33
  }
34
- const skills = discoverSkills(requireShellPaths(ctx));
34
+ const skills = await discoverSkills(requireShellPaths(ctx));
35
35
  const ecosystemPaths = requireEcosystemCatalogPaths(ctx);
36
36
  if (sub === 'list') {
37
37
  if (skills.length === 0) {
@@ -174,7 +174,7 @@ export function registerSubscriptionRuntimeCommands(registry: CommandRegistry):
174
174
  const openBrowser = !flags.has('--no-browser');
175
175
  const useManualMode = flags.has('--manual');
176
176
  if (provider === 'openai' && resolved.source === 'builtin') {
177
- const started = beginOpenAICodexLogin();
177
+ const started = await beginOpenAICodexLogin();
178
178
  manager.savePending({
179
179
  provider,
180
180
  state: started.state,
@@ -191,7 +191,7 @@ export function registerSubscriptionRuntimeCommands(registry: CommandRegistry):
191
191
  host: '127.0.0.1',
192
192
  port: 1455,
193
193
  path: '/auth/callback',
194
- }).catch(() => null);
194
+ }).catch(() => null); // best-effort: listener creation is optional; null triggers manual flow
195
195
  } catch {
196
196
  listener = null;
197
197
  }
@@ -272,14 +272,14 @@ export function registerSubscriptionRuntimeCommands(registry: CommandRegistry):
272
272
  host: resolved.oauth.localCallback.host,
273
273
  port: resolved.oauth.localCallback.port,
274
274
  path: resolved.oauth.localCallback.path,
275
- }).catch(() => null);
275
+ }).catch(() => null); // best-effort: local callback listener is optional; null falls back to manual redirect
276
276
  }
277
277
 
278
278
  if (listener) {
279
279
  activeConfig = { ...activeConfig, redirectUri: listener.redirectUri };
280
280
  }
281
281
 
282
- const started = manager.beginOAuthLogin(provider, activeConfig);
282
+ const started = await manager.beginOAuthLogin(provider, activeConfig);
283
283
  if (listener) {
284
284
  listener.setExpectedState(started.pending.state);
285
285
  }