@pellux/goodvibes-tui 1.0.0 → 1.1.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.
- package/CHANGELOG.md +66 -127
- package/README.md +16 -7
- package/docs/foundation-artifacts/operator-contract.json +1 -1
- package/package.json +2 -2
- package/src/cli/ensure-goodvibes-gitignore.ts +32 -0
- package/src/cli/entrypoint.ts +2 -0
- package/src/core/conversation.ts +40 -0
- package/src/core/stream-event-wiring.ts +104 -2
- package/src/core/stream-stall-watchdog.ts +41 -17
- package/src/export/cost-utils.ts +90 -8
- package/src/input/command-registry.ts +16 -0
- package/src/input/commands/diff-runtime.ts +61 -30
- package/src/input/commands/session-content.ts +12 -0
- package/src/input/commands/session-workflow.ts +3 -0
- package/src/input/commands/share-runtime.ts +9 -2
- package/src/input/handler-content-actions.ts +22 -8
- package/src/input/handler-feed-routes.ts +63 -1
- package/src/input/handler-feed.ts +12 -0
- package/src/input/handler-interactions.ts +2 -0
- package/src/input/handler-types.ts +1 -0
- package/src/input/handler.ts +4 -0
- package/src/main.ts +17 -17
- package/src/panels/agent-inspector-shared.ts +33 -10
- package/src/panels/builtin/development.ts +1 -1
- package/src/panels/cockpit-read-model.ts +16 -11
- package/src/panels/cost-tracker-panel.ts +28 -5
- package/src/panels/debug-panel.ts +11 -5
- package/src/panels/diff-panel.ts +112 -18
- package/src/panels/docs-panel.ts +9 -0
- package/src/panels/file-explorer-panel.ts +9 -0
- package/src/panels/git-panel.ts +9 -9
- package/src/panels/knowledge-graph-panel.ts +9 -0
- package/src/panels/local-auth-panel.ts +10 -0
- package/src/panels/panel-list-panel.ts +9 -0
- package/src/panels/project-planning-panel.ts +10 -0
- package/src/panels/provider-health-tracker.ts +5 -1
- package/src/panels/provider-health-views.ts +8 -1
- package/src/panels/scrollable-list-panel.ts +9 -0
- package/src/panels/session-browser-panel.ts +9 -0
- package/src/panels/token-budget-panel.ts +5 -3
- package/src/panels/types.ts +13 -0
- package/src/panels/work-plan-panel.ts +10 -0
- package/src/renderer/agent-detail-modal.ts +5 -4
- package/src/renderer/process-modal.ts +17 -2
- package/src/renderer/shell-surface.ts +8 -1
- package/src/renderer/ui-factory.ts +91 -7
- package/src/runtime/bootstrap-command-context.ts +3 -0
- package/src/runtime/bootstrap-command-parts.ts +3 -1
- package/src/runtime/bootstrap-core.ts +5 -0
- package/src/runtime/bootstrap-hook-bridge.ts +5 -0
- package/src/runtime/bootstrap-shell.ts +12 -1
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,138 +4,70 @@ All notable changes to GoodVibes TUI.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
## [1.
|
|
7
|
+
## [1.1.0] — 2026-07-03
|
|
8
8
|
|
|
9
9
|
### Changes
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
10
|
+
- e3d20840 chore(artifacts): regenerate foundation artifacts against SDK 0.36.0
|
|
11
|
+
- 2dab222b integrate wave0: trust repairs (v1.1.0 scope)
|
|
12
|
+
- d3116e0a chore(deps): bump @pellux/goodvibes-sdk to 0.36.0 (Wave-0 trust repairs)
|
|
13
|
+
- 08caa3a3 test(tools): pin the SDK 0.36 tail-preserving truncation contract
|
|
14
|
+
- e1e124b7 chore: trim stall-gate comment to keep main.ts under the 800-line cap
|
|
15
|
+
- 57a9c5bd fix(stream): suppress stall indicator false-positive during tool execution
|
|
16
|
+
- 96225275 fix(test-runner): fence git discovery at the per-file temp root
|
|
17
|
+
- efbfc8de chore(wave0): trim main.ts/git-panel.ts back to the 800-line cap after wave merges
|
|
18
|
+
- e7d166e4 integrate WO-315 (W0.10 cost-truth unpriced markers) into wave0
|
|
19
|
+
- 83d77e50 integrate WO-314 (W0.9 per-agent stats + footer usage hydration) into wave0
|
|
20
|
+
- 5801ced8 integrate WO-313 (W0.7 stream stall honesty, TUI half) into wave0
|
|
21
|
+
- 5abfed54 integrate WO-312 (W0.4/5/6/11/12 diff+slash+ctrlc cluster) into wave0
|
|
22
|
+
- 40fdee43 integrate WO-311 (W0.8 panel focus/burst/indicator) into wave0
|
|
23
|
+
- 8e61b248 fix(wo312): Wave 0 TUI trust-repair cluster (W0.4/W0.5/W0.6/W0.11/W0.12)
|
|
24
|
+
- 210b4b80 fix(W0.9): honest per-agent usage display + footer token hydration after resume
|
|
25
|
+
- b791dbd2 fix(cost): catalog-backed pricing with honest unpriced state (WO-315/W0.10)
|
|
26
|
+
- 1af7105e fix(input,renderer): panel entry points reachable + burst guard + focus indicator (W0.8)
|
|
27
|
+
- 000250a1 fix(streaming): honest stall indicator + mid-stream watchdog re-arm (W0.7)
|
|
28
|
+
- 74141d53 feat(dev): local SDK overlay for rapid iteration, with release-blocking gates
|
|
29
|
+
- 69c282a1 docs: reconcile documentation with the shipped 0.29.0 and 1.0.0 releases
|
|
30
|
+
|
|
31
|
+
## [1.0.0] — 2026-07-03
|
|
32
|
+
|
|
33
|
+
The renderer milestone: the full rendering pipeline measured, cached, gated, and visually pinned — plus a fix for a long-shipped multi-line paste defect. 1.0.0 marks the stability posture promised since early development: semver, with incompatible changes landing only in major releases.
|
|
34
|
+
|
|
35
|
+
### Features
|
|
36
|
+
- Restored conversation diff rendering: expanded diff-shaped tool results render with green add / red delete / blue hunk-header gutters through the same color tokens as the diff panel and the git panel's inline diff, so diffs look identical on every surface. This path had been unwired since v0.9.6; the new no-unused-exports gate exists so nothing goes silently dead again.
|
|
37
|
+
- Unified the renderer palette onto the `UI_TONES` token set — byte-identical color swaps; genuinely distinct palettes (agent-detail neon theme, code-block VS-Code-Dark+ fallback) are preserved exactly as named accents.
|
|
38
|
+
- Long-conversation append performance: a per-message line cache renders only the new message on append (~0.9ms median) instead of rebuilding the whole transcript (~45.5ms and ~71MB of throwaway allocations at 1,000 messages).
|
|
39
|
+
- Render coalescing: same-tick render requests collapse into one composite — streaming bursts previously triggered up to 7 redundant composites per tick.
|
|
40
|
+
- Honest profile picker: dropped the hardcoded fake "Settings" column that showed identical text for every profile; adopted proportional column widths.
|
|
41
|
+
|
|
42
|
+
### Fixes
|
|
43
|
+
- Multi-line paste works: terminals transmit the line breaks inside a bracketed paste as carriage returns, which the composer inserted verbatim — the input box never grew, ghost text overwrote the prompt marker, and edits acted on an invisible layout. Line endings are now normalized at the paste boundary, and history entries persisted before the fix are laundered on load. (A shipped defect since bracketed paste landed, not a regression from this effort.)
|
|
44
|
+
- Seven rendering-correctness fixes at narrow/short terminal sizes: real viewport height in the agent-detail modal, overlay width clamped ahead of its 20/24-column floors, consistent compact-footer height math, and wide-glyph (CJK/fullwidth) boundary fixes — each landed with a test that failed before the fix.
|
|
45
|
+
|
|
46
|
+
### Internal
|
|
47
|
+
- Golden-frame harness expanded to 35 committed byte-exact reference frames: the splash at 60/100/140 columns, transcript scenes (plain and diff tool results collapsed/expanded, code blocks on both tokenizer paths, thinking blocks, streaming partials), and every overlay at normal and hostile sizes. Splash goldens render with a pinned version fixture so release version bumps can never break them (the first v1.0.0 validate run failed on exactly that).
|
|
48
|
+
- Performance baseline harness with enforced CI budgets (`scripts/perf-baseline.json`, `perf:check`): startup load, frame-composite p95/p99, and line-production budgets for transcript build/append/resize, panel build, markdown, code blocks, and overlay open. `transcript.append_one` ratcheted 20ms → 6ms.
|
|
49
|
+
- No-unused-exports architecture gate for the renderer layer; three dead renderer modules deleted (`progress.ts`, `file-tree.ts`, `status-token.ts`).
|
|
50
|
+
- Splash art and its wide/narrow glyph typography byte-identical throughout — enforced by an empty-diff check every wave plus the splash goldens.
|
|
38
51
|
|
|
39
52
|
## [0.29.0] — 2026-07-03
|
|
40
53
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
- 782e7752 fix(panels): services test/detail act on the filtered selection
|
|
59
|
-
- e8783a89 integrate WO-154 into panel-ux-remediation
|
|
60
|
-
- cdb399b9 integrate WO-140 into panel-ux-remediation
|
|
61
|
-
- 943089ea integrate WO-139 into panel-ux-remediation
|
|
62
|
-
- d527c223 integrate WO-141 into panel-ux-remediation
|
|
63
|
-
- a65ee307 integrate WO-150 into panel-ux-remediation
|
|
64
|
-
- a73e874b feat(panels): WO-140 planning cluster polish (wrfc/project-planning/work-plan)
|
|
65
|
-
- e1921ab8 feat(panels): WO-141 keep-panel hardening + residual signpost sweep
|
|
66
|
-
- 8b018746 feat(panels): WO-139 provider-cost leftovers — subscription/local-auth/cost-tracker
|
|
67
|
-
- da7b9550 feat(panels): WO-150 unify focus ownership in PanelManager
|
|
68
|
-
- 4c569ae8 feat(shell): WO-154 main-UX shell splash, footer cost/compact, overlay posture
|
|
69
|
-
- 7218349c fix(panels): wave 3a review findings — filtered-selection desyncs, residual signposts, dead constants
|
|
70
|
-
- 1cc9f195 integrate WO-138 into panel-ux-remediation
|
|
71
|
-
- 179a9a91 integrate WO-137 into panel-ux-remediation
|
|
72
|
-
- 4df3036e integrate WO-136 into panel-ux-remediation
|
|
73
|
-
- 20533671 integrate WO-135 into panel-ux-remediation
|
|
74
|
-
- 2f116f82 integrate WO-134 into panel-ux-remediation
|
|
75
|
-
- 595df267 integrate WO-133 into panel-ux-remediation
|
|
76
|
-
- 490dc881 feat(panels): WO-137 diagnostics trio — security, debug, system-messages
|
|
77
|
-
- 066fd91a feat(panels): WO-138 session-comm quad real actions (remote/communication/routes/qr)
|
|
78
|
-
- a9a1ebe0 feat(panels): WO-134 Plugins + Hooks control rooms
|
|
79
|
-
- e4380896 feat(panels): WO-136 Docs + Intelligence live actions
|
|
80
|
-
- 8f6a3f45 feat(panels): WO-133 explorer + preview lazy loading, git decorations, diff/reload keys
|
|
81
|
-
- 744dc460 feat(panels): WO-135 marketplace + skills real actions
|
|
82
|
-
- a04c86e9 fix(panels): reserve summary row inside stacked scrollable section budgets
|
|
83
|
-
- b2a43d9b fix(panels): reserve a row for the window-summary inside the scrollable budget
|
|
84
|
-
- acb3e3b5 fix(panels): bind w to dispatch teamwork review from Tasks; stop git panel swallowing i on loaded repos
|
|
85
|
-
- 4bc04330 integrate WO-130 into panel-ux-remediation
|
|
86
|
-
- e15c010b integrate WO-128 into panel-ux-remediation
|
|
87
|
-
- 3020d2e1 integrate WO-127 into panel-ux-remediation
|
|
88
|
-
- 1a0c0d76 integrate WO-132 into panel-ux-remediation
|
|
89
|
-
- 79ac40bd integrate WO-131 into panel-ux-remediation
|
|
90
|
-
- 61092aa0 integrate WO-126 into panel-ux-remediation
|
|
91
|
-
- f83d6820 feat(panels): WO-131 agent-runtime trio — tools + tasks + orchestration
|
|
92
|
-
- 8dafc5c0 feat(panels): WO-132 Git + Diff consoles — stage/unstage/commit, explicit init confirm, self-loading diff panel
|
|
93
|
-
- 24c5127e feat(panels): WO-128 plan dashboard revival
|
|
94
|
-
- 2c8e8d1f feat(panels): WO-126 symbol outline on tree-sitter
|
|
95
|
-
- 2d424e1c feat(panels): WO-130 Cockpit operator polish
|
|
96
|
-
- 66c0deeb feat(panels): WO-127 eval console — baseline seed, live run, regressions
|
|
97
|
-
- 1a53ef2c fix(panels): drop redundant slash-command guidance from knowledge review mode
|
|
98
|
-
- 16ba104d fix(tests): operator-surfaces gate verifies forensics alias resolves to incident
|
|
99
|
-
- a5b8f4e9 integrate FOLLOWUPS into panel-ux-remediation
|
|
100
|
-
- f60988eb integrate WO-125 into panel-ux-remediation
|
|
101
|
-
- 39996bfd integrate WO-124 into panel-ux-remediation
|
|
102
|
-
- 78fb4916 integrate WO-123 into panel-ux-remediation
|
|
103
|
-
- e5294103 integrate WO-122 into panel-ux-remediation
|
|
104
|
-
- 371f4d60 integrate WO-121 into panel-ux-remediation
|
|
105
|
-
- 1ff1e0ae integrate WO-120 into panel-ux-remediation
|
|
106
|
-
- 933b09a3 feat(panels): WO-120 register ops-control panel and wire operator actions
|
|
107
|
-
- 39bfc1a1 feat(panels): WO-125 wire worktree and sandbox lifecycle verbs to their registries
|
|
108
|
-
- 4992fd03 feat(panels): WO-121 control-plane approvals console
|
|
109
|
-
- ea1c47b0 feat(panels): WO-123 knowledge console replaces static command catalogue
|
|
110
|
-
- 91273a60 feat(panels): WO-124 settings-sync console — resolve conflicts in-panel, Tab browse modes
|
|
111
|
-
- 74f5d39d feat(panels): WO-122 policy governance console actions and depth
|
|
112
|
-
- f4679a7e fix(panels): broaden hex-literal ratchet to 3/6/8-digit forms, clear remaining raw hex in fullscreen-primitives and diff-panel
|
|
113
|
-
- 6e1b814c fix(panels): forensics resolves as alias to incident, not duplicate registration
|
|
114
|
-
- 6a0f4405 fix(tests): operator-surfaces gate asserts merged provider-health id instead of retired providers id
|
|
115
|
-
- 09d81371 fix(tests): retarget legacy-turn-bus ban from deleted context-visualizer-panel to its WO-113 successor token-budget-panel
|
|
116
|
-
- e835a821 integrate WO-114 into panel-ux-remediation
|
|
117
|
-
- 433b4c25 integrate WO-113 into panel-ux-remediation
|
|
118
|
-
- 70466337 integrate WO-112 into panel-ux-remediation
|
|
119
|
-
- a1ebf5da fix(tests): contract module count 30 after WO-110+WO-111 both deleted a module
|
|
120
|
-
- fd25fb19 integrate WO-111 into panel-ux-remediation
|
|
121
|
-
- 0d72a2ef integrate WO-110 into panel-ux-remediation
|
|
122
|
-
- 0a344b60 WO-112: Provider console merge: providers + accounts into provider-health
|
|
123
|
-
- d4c7c8b4 WO-111: Automation console merge: schedule + watchers into automation
|
|
124
|
-
- 1fc22d27 WO-110: Agent console merge: agent-logs into inspector
|
|
125
|
-
- 0d4858ff WO-114: Incident console merge: forensics into incident
|
|
126
|
-
- a4cdfb3c WO-113: Context/token merge: context into tokens
|
|
127
|
-
- fd8e84ef integrate WO-002 into panel-ux-remediation
|
|
128
|
-
- 1a85ece3 WO-002: Panel palette and chrome sweep (extendPalette everywhere)
|
|
129
|
-
- 1cd556da integrate WO-006 into panel-ux-remediation
|
|
130
|
-
- d6effd51 integrate WO-005 into panel-ux-remediation
|
|
131
|
-
- d7735d20 integrate WO-004 into panel-ux-remediation
|
|
132
|
-
- 9b1de69a integrate WO-003 into panel-ux-remediation
|
|
133
|
-
- e149323f integrate WO-001 into panel-ux-remediation
|
|
134
|
-
- 45e90fa7 WO-006: Shared-test decongestion: per-panel spec modules
|
|
135
|
-
- 2d7d5777 WO-001: Theme-token backbone + architecture-gate hex enforcement
|
|
136
|
-
- 207cd46b WO-003: Action-callback plumbing pattern + ConfirmState generalization
|
|
137
|
-
- abf6771b WO-005: session-maintenance evaluator dedup
|
|
138
|
-
- 4fe16129 WO-004: Dead code and dead keybinding removal
|
|
54
|
+
The panel remediation release: a 27-finding audit closed across 38 work orders — panels became working consoles instead of slash-command signposts.
|
|
55
|
+
|
|
56
|
+
### Features
|
|
57
|
+
- Panels perform their actions directly: ops task/agent interventions, approval resolution, policy simulate/preflight/promote/rollback, knowledge review, settings-sync conflict resolution, worktree/sandbox lifecycle, and git operations run from the panel — keyed, with confirm gates on destructive actions — instead of printing "run /some-command".
|
|
58
|
+
- `Ctrl+O` opens the Ops Control panel as a real, rebindable keybinding.
|
|
59
|
+
- Five panel merges with the old ids preserved as aliases: agent-logs→inspector, context→tokens, providers+accounts→provider-health, forensics→incident.
|
|
60
|
+
|
|
61
|
+
### Fixes
|
|
62
|
+
- `/shortcuts` and `/commands` overlays actually display: the token-feed pipeline was reverting overlay flags that command handlers set mid-feed, so both commands silently did nothing. Fixed with change-detecting write-back plus a regression test.
|
|
63
|
+
- Every Alt-modifier keybinding works again after a tokenizer/matcher mismatch left them all dead at runtime (Alt+B/Alt+F word navigation, Alt+D and Alt+Y kill-ring bindings).
|
|
64
|
+
- A stale-selection bug class across the Marketplace, Services, Incident, and Settings-sync panels: after filtering, an action could silently apply to the wrong row. Fixed via a shared `getSelectedItem()` accessor plus an architecture rule banning raw selected-index access.
|
|
65
|
+
- Panel focus ownership centralized in PanelManager with a self-healing invariant, replacing scattered per-panel focus bookkeeping.
|
|
66
|
+
|
|
67
|
+
### Internal
|
|
68
|
+
- Parallel per-file test runner (`scripts/run-tests.ts`): min(8, cores−1) workers with isolated TMPDIRs — the suite dropped from ~10.5 minutes to under 2 locally; the CI test-job cap raised to 20 minutes as a hang backstop.
|
|
69
|
+
- New architecture gates: a raw-hex-literal ratchet (424 → 262 literals, exact per-file baseline), the selected-index rule, registry-time icon uniqueness, panel contract-module parity, an 800-line source-file cap, and a focus-invariant test.
|
|
70
|
+
- +278 tests across the remediation.
|
|
139
71
|
|
|
140
72
|
## [0.28.0] — 2026-06-30
|
|
141
73
|
|
|
@@ -185,6 +117,13 @@ Full deep-review audit of the TUI (33 findings fixed, each reviewed to a score o
|
|
|
185
117
|
- 97f74119 chore: upgrade @pellux/goodvibes-sdk to 0.34.0
|
|
186
118
|
- 6de2627a revert: remove daemon control-plane buildout — duplicated @pellux/goodvibes-sdk 0.34.0
|
|
187
119
|
|
|
120
|
+
## [0.25.0] — 2026-06-21
|
|
121
|
+
|
|
122
|
+
### Changes
|
|
123
|
+
- 5353a7d9 chore: release v0.25.0
|
|
124
|
+
- a8a4cd4f fix(daemon/remote): make runProcess timeout resolve promptly under orphaned children
|
|
125
|
+
- c00461be feat(daemon): implement control-plane operator surfaces for channels, email, calendar, and remote execution
|
|
126
|
+
|
|
188
127
|
## [0.24.1] — 2026-06-17
|
|
189
128
|
|
|
190
129
|
### Changes
|
package/README.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](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
|
|
|
9
|
-
>
|
|
9
|
+
> **1.0.0 shipped 2026-07-03.** From 1.0.0 the project follows the stability posture promised for this milestone: semver, with incompatible changes to CLI flags, config keys, slash commands, key bindings, daemon routes, and on-disk layouts landing only in major releases, and deprecations noted in `CHANGELOG.md` first. Documentation always describes the **current** behavior, not historical behavior.
|
|
10
10
|
|
|
11
11
|
<!-- screenshot -->
|
|
12
12
|
|
|
@@ -146,6 +146,7 @@ The TUI now consumes the extracted `@pellux/goodvibes-sdk` (`0.34.0`) platform l
|
|
|
146
146
|
- Shared panel workspace layout budgeting with renderer-owned visible-row budgets
|
|
147
147
|
- Copy/selection logic that strips decorative gutters and visual scaffolding from clipboard output
|
|
148
148
|
- Line-count indicator rendered right-aligned inside the prompt border on multi-line input (footer height stays stable regardless of prompt line count)
|
|
149
|
+
- Multi-line clipboard paste handled at the input boundary: bracketed-paste carriage-return line separators are normalized to newlines, the composer grows to the pasted line count, and pastes longer than 8 lines collapse to a compact paste marker
|
|
149
150
|
|
|
150
151
|
### Conversation And Transcript Workflow
|
|
151
152
|
- Markdown rendering, syntax highlighting, inline diffs, collapsible blocks, bookmarks, block copy, and block save
|
|
@@ -1318,7 +1319,8 @@ Those pieces cover conversation-noise routing, panel-health/performance budgets,
|
|
|
1318
1319
|
| `/diff [target]` | `/d` | Show unified diff: session, head, working, staged, or a git ref |
|
|
1319
1320
|
| `/mcp [add\|remove\|reload\|tools]` | — | Manage MCP servers at runtime and list connected tools |
|
|
1320
1321
|
| `/help [command]` | `/h`, `/?` | Show available commands and keyboard shortcuts |
|
|
1321
|
-
| `/quit` |
|
|
1322
|
+
| `/quit` | `/:q` | Exit the application |
|
|
1323
|
+
| `/wq` | `/:wq` | Commit all git changes, then exit |
|
|
1322
1324
|
|
|
1323
1325
|
> **Tip:** Use the `/add-provider` skill for interactive guided provider setup with smart defaults for popular providers.
|
|
1324
1326
|
>
|
|
@@ -1386,9 +1388,13 @@ All shortcuts are customizable via `~/.goodvibes/tui/keybindings.json`. Use `/ke
|
|
|
1386
1388
|
| Key | Action |
|
|
1387
1389
|
|-----|--------|
|
|
1388
1390
|
| `Ctrl+P` | Toggle panel sidebar |
|
|
1389
|
-
| `Ctrl+
|
|
1390
|
-
| `Ctrl
|
|
1391
|
-
|
|
|
1391
|
+
| `Ctrl+]` | Next panel tab |
|
|
1392
|
+
| `Ctrl+[` | Previous panel tab |
|
|
1393
|
+
| `Alt+1`…`Alt+9` | Jump to panel tab 1–9 |
|
|
1394
|
+
| `Ctrl+X` | Close the focused panel |
|
|
1395
|
+
| `Ctrl+Shift+X` | Close all panels |
|
|
1396
|
+
| `Ctrl+O` | Open the Ops Control panel |
|
|
1397
|
+
| `Ctrl+G` | Toggle focus between split panes |
|
|
1392
1398
|
|
|
1393
1399
|
### System
|
|
1394
1400
|
|
|
@@ -1652,6 +1658,9 @@ src/
|
|
|
1652
1658
|
- **Backend-first external surface** — the daemon/control plane exposes typed HTTP/gateway methods for knowledge, artifacts, media, search, channels, and remote peers so future clients do not have to reimplement runtime logic
|
|
1653
1659
|
- **Plugin system** — manifest.json + sandboxed API surface for commands, providers, tools, gateway methods, channels, embeddings, voice, media, and search
|
|
1654
1660
|
- **Crash recovery** — periodic JSONL snapshots with recovery prompt on next startup; an fsync-per-record append-only transcript journal between snapshots is replayed at every resume seam (command resume, Ctrl+R crash recovery, panel resume) for SIGKILL-proof durability
|
|
1661
|
+
- **Golden-frame regression harness** — 35 committed byte-exact reference frames (including the splash at three widths) pin the renderer's output; any unintended visual change fails CI
|
|
1662
|
+
- **Render coalescing** — same-tick render requests collapse into one composite via `src/runtime/render-scheduler.ts`, so a streaming burst pays for one frame instead of several
|
|
1663
|
+
- **Per-message line cache** — `src/core/conversation-line-cache.ts` renders only the appended message on transcript growth (~0.9ms) instead of rebuilding the whole conversation (~45ms at 1,000 messages)
|
|
1655
1664
|
|
|
1656
1665
|
---
|
|
1657
1666
|
|
|
@@ -1669,7 +1678,7 @@ bun run dev
|
|
|
1669
1678
|
bun test
|
|
1670
1679
|
```
|
|
1671
1680
|
|
|
1672
|
-
|
|
1681
|
+
The suite spans 623 test files (contract, security, release gate, runtime, renderer, panel, integration, and UX anti-regression), run through a parallel per-file runner (`scripts/run-tests.ts`). Performance budgets run as a CI gate — the build fails if startup load, frame-composite p95/p99, or any line-production budget (transcript build/append/resize, panel two-pane build, markdown render, code-block render on both tokenizer paths, overlay open) exceeds the ratcheted values in `scripts/perf-baseline.json`. Architecture gates run alongside: import-cycle and layer rules, an 800-line cap on non-test source files, a raw-hex-literal ratchet, icon-uniqueness and panel contract-parity checks, and a no-unused-exports rule for the renderer layer (`scripts/check-architecture.ts`, `scripts/no-unused-exports-rule.ts`).
|
|
1673
1682
|
|
|
1674
1683
|
### Build standalone binary
|
|
1675
1684
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-tui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.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",
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"@anthropic-ai/vertex-sdk": "^0.16.0",
|
|
100
100
|
"@ast-grep/napi": "^0.42.0",
|
|
101
101
|
"@aws/bedrock-token-generator": "^1.1.0",
|
|
102
|
-
"@pellux/goodvibes-sdk": "0.
|
|
102
|
+
"@pellux/goodvibes-sdk": "0.36.0",
|
|
103
103
|
"bash-language-server": "^5.6.0",
|
|
104
104
|
"fuse.js": "^7.1.0",
|
|
105
105
|
"graphql": "^16.13.2",
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { existsSync, readFileSync, appendFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The TUI's own `.goodvibes/` directory (logs, overflow buffers, exec output,
|
|
6
|
+
* cache, session state, etc.) is transient scratch space, not project source —
|
|
7
|
+
* but nothing else in the codebase excludes it from the *project's* git
|
|
8
|
+
* history. Any `git add -A` (write-quit's auto-commit, a WRFC chain's own
|
|
9
|
+
* commit, etc.) sweeps the whole tree in unless the project's .gitignore
|
|
10
|
+
* already happens to exclude it. Idempotent and append-only: never rewrites
|
|
11
|
+
* or reorders a project's existing .gitignore, only adds the rule if it is
|
|
12
|
+
* verifiably absent, and never touches a directory that isn't a git repo.
|
|
13
|
+
*/
|
|
14
|
+
const GOODVIBES_IGNORE_PATTERN = /(^|\n)[ \t]*\/?\.goodvibes\/?\*?[ \t]*(\n|$)/;
|
|
15
|
+
|
|
16
|
+
export function ensureGoodvibesGitignore(projectRoot: string): void {
|
|
17
|
+
try {
|
|
18
|
+
if (!existsSync(join(projectRoot, '.git'))) return; // only matters for git projects
|
|
19
|
+
const gitignorePath = join(projectRoot, '.gitignore');
|
|
20
|
+
const existing = existsSync(gitignorePath) ? readFileSync(gitignorePath, 'utf-8') : '';
|
|
21
|
+
if (GOODVIBES_IGNORE_PATTERN.test(existing)) return; // already excluded
|
|
22
|
+
const rule = '# goodvibes-tui transient state\n.goodvibes/\n';
|
|
23
|
+
if (existing.length === 0) {
|
|
24
|
+
writeFileSync(gitignorePath, rule);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const separator = existing.endsWith('\n') ? '\n' : '\n\n';
|
|
28
|
+
appendFileSync(gitignorePath, separator + rule);
|
|
29
|
+
} catch {
|
|
30
|
+
// Best-effort only — never block startup on a gitignore write failure.
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/cli/entrypoint.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
renderOnboardingCliStatus,
|
|
23
23
|
} from './index.ts';
|
|
24
24
|
import { buildCliServicePosture } from './service-posture.ts';
|
|
25
|
+
import { ensureGoodvibesGitignore } from './ensure-goodvibes-gitignore.ts';
|
|
25
26
|
|
|
26
27
|
type ShellEntrypointOwnership = {
|
|
27
28
|
readonly workingDirectory: string;
|
|
@@ -95,6 +96,7 @@ export async function prepareShellCliRuntime(
|
|
|
95
96
|
homeDirectory: bootstrapHomeDirectory,
|
|
96
97
|
} = resolveShellEntrypointOwnership(roots, cli.flags.workingDir ?? (cli.command === 'tui' ? cli.commandArgs[0] : undefined));
|
|
97
98
|
configureActivityLogger(join(bootstrapWorkingDir, '.goodvibes', 'logs'));
|
|
99
|
+
ensureGoodvibesGitignore(bootstrapWorkingDir);
|
|
98
100
|
const configManager = new ConfigManager({
|
|
99
101
|
workingDir: bootstrapWorkingDir,
|
|
100
102
|
homeDir: bootstrapHomeDirectory,
|
package/src/core/conversation.ts
CHANGED
|
@@ -49,6 +49,46 @@ export type { BlockMeta };
|
|
|
49
49
|
import type { ConversationMessageSnapshot } from '@pellux/goodvibes-sdk/platform/core';
|
|
50
50
|
type Message = ConversationMessageSnapshot;
|
|
51
51
|
|
|
52
|
+
/** Matches Orchestrator.usage's shape (SDK platform/core/orchestrator.ts). Not
|
|
53
|
+
* exported by the SDK as a standalone type, so mirrored here. */
|
|
54
|
+
export interface OrchestratorUsageTotals {
|
|
55
|
+
input: number;
|
|
56
|
+
output: number;
|
|
57
|
+
cacheRead: number;
|
|
58
|
+
cacheWrite: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* sumConversationUsage - Fold every assistant message's per-turn usage into
|
|
63
|
+
* running totals, plus the *last* assistant message's own input-token figure
|
|
64
|
+
* (context-window occupancy — not a running sum).
|
|
65
|
+
*
|
|
66
|
+
* TUI-side counterpart to SDK Orchestrator.usage: after a session resume
|
|
67
|
+
* replays historical messages into a freshly-constructed Orchestrator (whose
|
|
68
|
+
* `usage` starts at all zeros — bootstrap.ts always constructs a fresh
|
|
69
|
+
* instance, see W0.9), this lets the caller hydrate the footer's token
|
|
70
|
+
* counters from messages that already carry real usage data instead of
|
|
71
|
+
* waiting for the first new turn to populate them.
|
|
72
|
+
*/
|
|
73
|
+
export function sumConversationUsage(
|
|
74
|
+
messages: readonly ConversationMessageSnapshot[],
|
|
75
|
+
): { usage: OrchestratorUsageTotals; lastInputTokens: number } {
|
|
76
|
+
const usage: OrchestratorUsageTotals = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
77
|
+
let lastInputTokens = 0;
|
|
78
|
+
for (const message of messages) {
|
|
79
|
+
if (message.role !== 'assistant' || !message.usage) continue;
|
|
80
|
+
usage.input += message.usage.inputTokens;
|
|
81
|
+
usage.output += message.usage.outputTokens;
|
|
82
|
+
usage.cacheRead += message.usage.cacheReadTokens ?? 0;
|
|
83
|
+
usage.cacheWrite += message.usage.cacheWriteTokens ?? 0;
|
|
84
|
+
// Context-window occupancy tracks the most recent turn only, not a sum.
|
|
85
|
+
lastInputTokens = message.usage.inputTokens
|
|
86
|
+
+ (message.usage.cacheReadTokens ?? 0)
|
|
87
|
+
+ (message.usage.cacheWriteTokens ?? 0);
|
|
88
|
+
}
|
|
89
|
+
return { usage, lastInputTokens };
|
|
90
|
+
}
|
|
91
|
+
|
|
52
92
|
export class ConversationManager extends SdkConversationManager {
|
|
53
93
|
public history = new InfiniteBuffer();
|
|
54
94
|
private _getWidth: () => number;
|
|
@@ -23,6 +23,29 @@ export interface StreamMetrics {
|
|
|
23
23
|
activeToolStartedAtMs: number | undefined;
|
|
24
24
|
/** Name of the currently executing tool; cleared when execution completes. */
|
|
25
25
|
activeToolName: string | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Epoch ms of the most recent STREAM_START or STREAM_DELTA; undefined when
|
|
28
|
+
* idle (no turn in flight). Read every render frame — not just on the
|
|
29
|
+
* watchdog's one-shot hint — so "ms since last byte" can be computed even
|
|
30
|
+
* when no new SDK event has arrived at all (a no-delta stall watchdog for
|
|
31
|
+
* the render loop itself, independent of the low-priority system message).
|
|
32
|
+
*/
|
|
33
|
+
lastDeltaAtMs: number | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* 1-based count of stall episodes the watchdog has fired for the current
|
|
36
|
+
* turn; 0 = no stall yet. Increments each time a fresh silence (after a
|
|
37
|
+
* recovery) crosses the stall threshold.
|
|
38
|
+
*/
|
|
39
|
+
stallEpisode: number;
|
|
40
|
+
/**
|
|
41
|
+
* Populated from the SDK's STREAM_RETRY event when present. SDK 0.35.0 (the
|
|
42
|
+
* pinned dependency) has no such event on the TurnEvent union yet — these
|
|
43
|
+
* fields are consumed structurally (see looseTurnsFeed below) and stay
|
|
44
|
+
* undefined until a future SDK version emits it. Cleared on STREAM_DELTA
|
|
45
|
+
* (a byte arriving means the reconnect, if any, succeeded).
|
|
46
|
+
*/
|
|
47
|
+
reconnectAttempt: number | undefined;
|
|
48
|
+
reconnectMaxAttempts: number | undefined;
|
|
26
49
|
}
|
|
27
50
|
|
|
28
51
|
/** Minimal orchestrator surface required for stream token-speed calculation. */
|
|
@@ -60,6 +83,33 @@ interface StreamSystemMessageRouter {
|
|
|
60
83
|
low(message: string): void;
|
|
61
84
|
}
|
|
62
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Loosely-typed variant of the turns event feed, used only to subscribe to
|
|
88
|
+
* event names not yet present in the SDK's TurnEvent union (STREAM_RETRY,
|
|
89
|
+
* STREAM_STALL — see the structural-consumption comment at the subscription
|
|
90
|
+
* site below). `events.turns.on` itself stays fully typed against the real
|
|
91
|
+
* TurnEvent union everywhere else in this file.
|
|
92
|
+
*/
|
|
93
|
+
interface LooseTurnEventFeed {
|
|
94
|
+
on(type: string, listener: (payload: unknown) => void): () => void;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Payload shape expected from a future SDK STREAM_RETRY event. */
|
|
98
|
+
interface StreamRetryLikePayload {
|
|
99
|
+
readonly attempt: number;
|
|
100
|
+
readonly maxAttempts: number;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Runtime guard validating an unknown STREAM_RETRY-like payload's shape. */
|
|
104
|
+
function isStreamRetryLikePayload(payload: unknown): payload is StreamRetryLikePayload {
|
|
105
|
+
return (
|
|
106
|
+
typeof payload === 'object' &&
|
|
107
|
+
payload !== null &&
|
|
108
|
+
typeof (payload as Record<string, unknown>).attempt === 'number' &&
|
|
109
|
+
typeof (payload as Record<string, unknown>).maxAttempts === 'number'
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
63
113
|
/**
|
|
64
114
|
* Minimal cost lookup surface for attaching cost-delta information to failover notices.
|
|
65
115
|
* Returns USD-per-1M-token pricing for the given model ID.
|
|
@@ -197,10 +247,19 @@ export function wireStreamEventMetrics(
|
|
|
197
247
|
metrics.tokenSpeed = 0;
|
|
198
248
|
metrics.ttftMs = undefined;
|
|
199
249
|
metrics.ttftRecorded = false;
|
|
250
|
+
metrics.lastDeltaAtMs = Date.now();
|
|
251
|
+
metrics.stallEpisode = 0;
|
|
252
|
+
metrics.reconnectAttempt = undefined;
|
|
253
|
+
metrics.reconnectMaxAttempts = undefined;
|
|
200
254
|
}));
|
|
201
255
|
|
|
202
256
|
unsubs.push(events.turns.on('STREAM_DELTA', () => {
|
|
203
257
|
metrics.deltaCount++;
|
|
258
|
+
metrics.lastDeltaAtMs = Date.now();
|
|
259
|
+
// A byte arrived: any in-flight reconnect (if the SDK reported one)
|
|
260
|
+
// has succeeded.
|
|
261
|
+
metrics.reconnectAttempt = undefined;
|
|
262
|
+
metrics.reconnectMaxAttempts = undefined;
|
|
204
263
|
const elapsed = (Date.now() - metrics.startTime) / 1000;
|
|
205
264
|
// Record TTFT on the first delta of each turn.
|
|
206
265
|
if (!metrics.ttftRecorded) {
|
|
@@ -296,10 +355,16 @@ export function wireStreamEventMetrics(
|
|
|
296
355
|
render();
|
|
297
356
|
}));
|
|
298
357
|
|
|
299
|
-
// --- Stream stall watchdog: emit
|
|
358
|
+
// --- Stream stall watchdog: emit a low hint each time a no-delta gap (at
|
|
359
|
+
// start of turn OR mid-stream, after a re-arm) crosses 30s. The transient
|
|
360
|
+
// system message below is the initial alert; streamMetrics.stallEpisode
|
|
361
|
+
// (read every render frame) drives the persistent "stalled Ns" indicator
|
|
362
|
+
// in the thinking fragment, so the two coexist rather than conflict — the
|
|
363
|
+
// message announces the stall, the indicator tracks it ongoing.
|
|
300
364
|
const stallWatchdog = createStreamStallWatchdog({
|
|
301
365
|
events: events.turns,
|
|
302
|
-
onStall: (providerName) => {
|
|
366
|
+
onStall: (providerName, episode) => {
|
|
367
|
+
metrics.stallEpisode = episode;
|
|
303
368
|
systemMessageRouter.low(`Still waiting on ${providerName}… Ctrl+C to cancel`);
|
|
304
369
|
render();
|
|
305
370
|
},
|
|
@@ -308,22 +373,59 @@ export function wireStreamEventMetrics(
|
|
|
308
373
|
});
|
|
309
374
|
unsubs.push(() => stallWatchdog.dispose());
|
|
310
375
|
|
|
376
|
+
// --- Structural consumption of STREAM_RETRY / STREAM_STALL ---
|
|
377
|
+
// Neither event exists on the pinned SDK's (0.35.0) TurnEvent union today —
|
|
378
|
+
// both are proposed additions for the transport-level withRetry() callback
|
|
379
|
+
// (see the stall-honesty audit brief). The typed `events.turns.on` feed
|
|
380
|
+
// rejects unknown event names at compile time, so this casts the feed to a
|
|
381
|
+
// loosely-typed variant and validates each payload with a runtime guard
|
|
382
|
+
// instead of importing a type name that doesn't exist yet — same pattern
|
|
383
|
+
// used elsewhere in this codebase for settings pending SDK schema additions
|
|
384
|
+
// (see src/input/settings-modal-data.ts). Compiles today against 0.35.0;
|
|
385
|
+
// lights up automatically once the SDK adds the real event.
|
|
386
|
+
const looseTurnsFeed = events.turns as unknown as LooseTurnEventFeed;
|
|
387
|
+
unsubs.push(looseTurnsFeed.on('STREAM_RETRY', (payload) => {
|
|
388
|
+
if (!isStreamRetryLikePayload(payload)) return;
|
|
389
|
+
metrics.reconnectAttempt = payload.attempt;
|
|
390
|
+
metrics.reconnectMaxAttempts = payload.maxAttempts;
|
|
391
|
+
render();
|
|
392
|
+
}));
|
|
393
|
+
unsubs.push(looseTurnsFeed.on('STREAM_STALL', () => {
|
|
394
|
+
// Informational only: the TUI's own createStreamStallWatchdog above is
|
|
395
|
+
// the authoritative no-delta detector and already drives the visible
|
|
396
|
+
// indicator via streamMetrics.stallEpisode. This subscription exists so
|
|
397
|
+
// the SDK's own signal (once it lands) is observed rather than silently
|
|
398
|
+
// dropped, without duplicating or fighting the local watchdog.
|
|
399
|
+
render();
|
|
400
|
+
}));
|
|
401
|
+
|
|
311
402
|
unsubs.push(events.tools.on('TOOL_EXECUTING', (ev) => {
|
|
312
403
|
metrics.activeToolStartedAtMs = ev.startedAt;
|
|
313
404
|
metrics.activeToolName = ev.tool;
|
|
314
405
|
render();
|
|
315
406
|
}));
|
|
407
|
+
// On every tool-completion path, reset lastDeltaAtMs to "now" so the
|
|
408
|
+
// post-tool silence window starts fresh. Tool execution suppresses stall
|
|
409
|
+
// detection at the render call site (see main.ts), but lastDeltaAtMs itself
|
|
410
|
+
// keeps its pre-tool value the whole time the tool runs — without this
|
|
411
|
+
// reset, the instant a tool completes (potentially long after the last real
|
|
412
|
+
// delta), the very next frame would read msSinceLastDelta as the full
|
|
413
|
+
// tool-execution duration and immediately report a stall, even though the
|
|
414
|
+
// model hasn't had a chance to resume producing tokens yet.
|
|
316
415
|
unsubs.push(events.tools.on('TOOL_SUCCEEDED', () => {
|
|
317
416
|
metrics.activeToolStartedAtMs = undefined;
|
|
318
417
|
metrics.activeToolName = undefined;
|
|
418
|
+
metrics.lastDeltaAtMs = Date.now();
|
|
319
419
|
}));
|
|
320
420
|
unsubs.push(events.tools.on('TOOL_FAILED', () => {
|
|
321
421
|
metrics.activeToolStartedAtMs = undefined;
|
|
322
422
|
metrics.activeToolName = undefined;
|
|
423
|
+
metrics.lastDeltaAtMs = Date.now();
|
|
323
424
|
}));
|
|
324
425
|
unsubs.push(events.tools.on('TOOL_CANCELLED', () => {
|
|
325
426
|
metrics.activeToolStartedAtMs = undefined;
|
|
326
427
|
metrics.activeToolName = undefined;
|
|
428
|
+
metrics.lastDeltaAtMs = Date.now();
|
|
327
429
|
}));
|
|
328
430
|
|
|
329
431
|
let _errorSurfacedCb: ((exhausted: boolean) => void) | undefined;
|