@pellux/goodvibes-agent 0.1.117 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/CHANGELOG.md +13 -4
  2. package/README.md +35 -14
  3. package/bin/goodvibes-agent.ts +16 -2
  4. package/dist/package/main.js +176073 -170980
  5. package/docs/README.md +11 -5
  6. package/docs/channels-remote-and-api.md +50 -0
  7. package/docs/connected-host.md +3 -3
  8. package/docs/getting-started.md +29 -15
  9. package/docs/knowledge-artifacts-and-multimodal.md +91 -0
  10. package/docs/project-planning.md +79 -0
  11. package/docs/providers-and-routing.md +46 -0
  12. package/docs/release-and-publishing.md +44 -9
  13. package/docs/tools-and-commands.md +123 -0
  14. package/docs/voice-and-live-tts.md +51 -0
  15. package/package.json +2 -5
  16. package/src/agent/channel-delivery.ts +201 -0
  17. package/src/agent/media-generation.ts +159 -0
  18. package/src/agent/memory-prompt.ts +0 -1
  19. package/src/agent/note-registry.ts +329 -0
  20. package/src/agent/operator-actions.ts +343 -0
  21. package/src/agent/persona-registry.ts +15 -14
  22. package/src/agent/record-labels.ts +107 -0
  23. package/src/agent/reminder-schedule-format.ts +33 -24
  24. package/src/agent/reminder-schedule.ts +26 -25
  25. package/src/agent/routine-registry.ts +13 -12
  26. package/src/agent/routine-schedule-args.ts +2 -1
  27. package/src/agent/routine-schedule-format.ts +77 -53
  28. package/src/agent/routine-schedule-promotion.ts +34 -32
  29. package/src/agent/routine-schedule-receipts.ts +28 -26
  30. package/src/agent/runtime-profile-starters.ts +2 -2
  31. package/src/agent/runtime-profile.ts +18 -17
  32. package/src/agent/skill-registry.ts +25 -24
  33. package/src/cli/agent-knowledge-args.ts +5 -1
  34. package/src/cli/agent-knowledge-command.ts +39 -33
  35. package/src/cli/agent-knowledge-format.ts +80 -67
  36. package/src/cli/agent-knowledge-methods.ts +1 -1
  37. package/src/cli/agent-knowledge-runtime.ts +32 -26
  38. package/src/cli/bundle-command.ts +13 -8
  39. package/src/cli/config-overrides.ts +37 -36
  40. package/src/cli/external-runtime.ts +10 -4
  41. package/src/cli/help.ts +29 -11
  42. package/src/cli/local-library-command.ts +134 -68
  43. package/src/cli/management-commands.ts +98 -62
  44. package/src/cli/management.ts +52 -26
  45. package/src/cli/memory-command.ts +66 -32
  46. package/src/cli/parser.ts +37 -24
  47. package/src/cli/profiles-command.ts +52 -35
  48. package/src/cli/provider-auth-routes.ts +2 -1
  49. package/src/cli/routines-command.ts +44 -36
  50. package/src/cli/service-posture.ts +6 -6
  51. package/src/cli/status.ts +46 -121
  52. package/src/core/conversation-message-snapshot.ts +131 -0
  53. package/src/input/agent-workspace-activation.ts +33 -7
  54. package/src/input/agent-workspace-basic-command-editor-submission.ts +7 -3
  55. package/src/input/agent-workspace-basic-command-editors.ts +30 -10
  56. package/src/input/agent-workspace-categories.ts +276 -64
  57. package/src/input/agent-workspace-channel-command-editor-submission.ts +34 -0
  58. package/src/input/agent-workspace-channel-command-editors.ts +23 -5
  59. package/src/input/agent-workspace-channels.ts +35 -2
  60. package/src/input/agent-workspace-command-editor.ts +18 -2
  61. package/src/input/agent-workspace-config-reader.ts +16 -0
  62. package/src/input/agent-workspace-delegation-editor-submission.ts +1 -1
  63. package/src/input/agent-workspace-editors.ts +140 -2
  64. package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
  65. package/src/input/agent-workspace-learned-behavior.ts +2 -2
  66. package/src/input/agent-workspace-library-command-editor-submission.ts +1 -1
  67. package/src/input/agent-workspace-library-command-editors.ts +2 -2
  68. package/src/input/agent-workspace-local-operations.ts +218 -0
  69. package/src/input/agent-workspace-local-selection.ts +75 -0
  70. package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
  71. package/src/input/agent-workspace-media-command-editors.ts +27 -0
  72. package/src/input/agent-workspace-memory-command-editors.ts +1 -1
  73. package/src/input/agent-workspace-memory-editor.ts +2 -2
  74. package/src/input/agent-workspace-navigation.ts +38 -2
  75. package/src/input/agent-workspace-notify-editor-submission.ts +16 -2
  76. package/src/input/agent-workspace-operations-command-editor-submission.ts +63 -1
  77. package/src/input/agent-workspace-operations-command-editors.ts +80 -3
  78. package/src/input/agent-workspace-panel-route.ts +43 -0
  79. package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
  80. package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
  81. package/src/input/agent-workspace-search.ts +169 -0
  82. package/src/input/agent-workspace-setup.ts +22 -11
  83. package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +6 -6
  84. package/src/input/agent-workspace-skill-bundle-command-editors.ts +10 -10
  85. package/src/input/agent-workspace-snapshot.ts +112 -13
  86. package/src/input/agent-workspace-task-command-editors.ts +4 -4
  87. package/src/input/agent-workspace-token.ts +18 -2
  88. package/src/input/agent-workspace-types.ts +92 -4
  89. package/src/input/agent-workspace-voice-media.ts +3 -6
  90. package/src/input/agent-workspace-web-research-editor.ts +104 -0
  91. package/src/input/agent-workspace.ts +136 -208
  92. package/src/input/command-registry.ts +4 -1
  93. package/src/input/commands/agent-runtime-profile-runtime.ts +45 -41
  94. package/src/input/commands/agent-skills-runtime.ts +83 -70
  95. package/src/input/commands/agent-workspace-runtime.ts +23 -7
  96. package/src/input/commands/brief-runtime.ts +25 -24
  97. package/src/input/commands/channels-runtime.ts +145 -31
  98. package/src/input/commands/delegation-runtime.ts +29 -23
  99. package/src/input/commands/experience-runtime.ts +6 -4
  100. package/src/input/commands/guidance-runtime.ts +4 -4
  101. package/src/input/commands/health-runtime.ts +140 -115
  102. package/src/input/commands/knowledge.ts +57 -56
  103. package/src/input/commands/local-provider-runtime.ts +36 -18
  104. package/src/input/commands/local-runtime.ts +138 -16
  105. package/src/input/commands/local-setup-review.ts +7 -7
  106. package/src/input/commands/mcp-runtime.ts +56 -35
  107. package/src/input/commands/notify-runtime.ts +38 -9
  108. package/src/input/commands/operator-actions-runtime.ts +138 -0
  109. package/src/input/commands/operator-runtime.ts +6 -17
  110. package/src/input/commands/personas-runtime.ts +45 -30
  111. package/src/input/commands/planning-runtime.ts +1 -1
  112. package/src/input/commands/platform-access-runtime.ts +29 -29
  113. package/src/input/commands/provider-accounts-runtime.ts +60 -39
  114. package/src/input/commands/qrcode-runtime.ts +45 -6
  115. package/src/input/commands/recall-bundle.ts +11 -11
  116. package/src/input/commands/recall-capture.ts +13 -11
  117. package/src/input/commands/recall-query.ts +29 -21
  118. package/src/input/commands/recall-review.ts +2 -1
  119. package/src/input/commands/routines-runtime.ts +59 -43
  120. package/src/input/commands/schedule-runtime.ts +33 -20
  121. package/src/input/commands/security-runtime.ts +4 -4
  122. package/src/input/commands/session-content.ts +80 -78
  123. package/src/input/commands/session-workflow.ts +132 -93
  124. package/src/input/commands/session.ts +3 -174
  125. package/src/input/commands/shell-core.ts +32 -17
  126. package/src/input/commands/subscription-runtime.ts +53 -179
  127. package/src/input/commands/tasks-runtime.ts +20 -20
  128. package/src/input/commands/work-plan-runtime.ts +33 -8
  129. package/src/input/commands.ts +2 -2
  130. package/src/input/feed-context-factory.ts +2 -1
  131. package/src/input/file-picker.ts +3 -2
  132. package/src/input/handler-command-route.ts +4 -7
  133. package/src/input/handler-content-actions.ts +89 -1
  134. package/src/input/handler-feed-routes.ts +19 -12
  135. package/src/input/handler-feed.ts +6 -3
  136. package/src/input/handler-interactions.ts +7 -5
  137. package/src/input/handler-modal-stack.ts +3 -3
  138. package/src/input/handler-onboarding.ts +24 -80
  139. package/src/input/handler-shortcuts.ts +15 -4
  140. package/src/input/handler.ts +47 -17
  141. package/src/input/input-history.ts +5 -6
  142. package/src/input/keybindings.ts +22 -11
  143. package/src/input/onboarding/onboarding-wizard-apply.ts +13 -0
  144. package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
  145. package/src/input/onboarding/onboarding-wizard-operator-steps.ts +147 -57
  146. package/src/input/onboarding/onboarding-wizard-steps.ts +46 -29
  147. package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
  148. package/src/input/onboarding/onboarding-wizard.ts +68 -0
  149. package/src/input/profile-picker-modal.ts +31 -12
  150. package/src/input/session-picker-modal.ts +21 -4
  151. package/src/input/settings-modal-behavior.ts +0 -3
  152. package/src/input/settings-modal-subscriptions.ts +3 -3
  153. package/src/input/settings-modal-types.ts +2 -13
  154. package/src/input/settings-modal.ts +6 -52
  155. package/src/input/submission-intent.ts +0 -1
  156. package/src/input/submission-router.ts +3 -3
  157. package/src/main.ts +18 -8
  158. package/src/panels/approval-panel.ts +8 -8
  159. package/src/panels/automation-control-panel.ts +2 -2
  160. package/src/panels/base-panel.ts +1 -1
  161. package/src/panels/builtin/agent.ts +1 -1
  162. package/src/panels/builtin/operations.ts +1 -10
  163. package/src/panels/builtin/session.ts +9 -9
  164. package/src/panels/builtin/shared.ts +2 -2
  165. package/src/panels/cost-tracker-panel.ts +1 -1
  166. package/src/panels/docs-panel.ts +5 -3
  167. package/src/panels/index.ts +0 -1
  168. package/src/panels/knowledge-panel.ts +6 -5
  169. package/src/panels/memory-panel.ts +7 -6
  170. package/src/panels/panel-list-panel.ts +36 -80
  171. package/src/panels/project-planning-panel.ts +18 -11
  172. package/src/panels/provider-account-snapshot.ts +51 -25
  173. package/src/panels/provider-accounts-panel.ts +33 -18
  174. package/src/panels/provider-health-domains.ts +45 -4
  175. package/src/panels/provider-health-panel.ts +5 -4
  176. package/src/panels/qr-panel.ts +1 -1
  177. package/src/panels/schedule-panel.ts +9 -17
  178. package/src/panels/security-panel.ts +8 -8
  179. package/src/panels/session-browser-panel.ts +10 -10
  180. package/src/panels/subscription-panel.ts +3 -3
  181. package/src/panels/system-messages-panel.ts +1 -1
  182. package/src/panels/tasks-panel.ts +20 -13
  183. package/src/panels/tool-inspector-panel.ts +19 -12
  184. package/src/panels/work-plan-panel.ts +5 -5
  185. package/src/planning/project-planning-coordinator.ts +1 -1
  186. package/src/provider-auth-route-display.ts +9 -0
  187. package/src/renderer/agent-workspace-style.ts +34 -0
  188. package/src/renderer/agent-workspace.ts +228 -52
  189. package/src/renderer/autocomplete-overlay.ts +25 -6
  190. package/src/renderer/bookmark-modal.ts +19 -4
  191. package/src/renderer/buffer.ts +4 -2
  192. package/src/renderer/context-inspector.ts +50 -13
  193. package/src/renderer/diff.ts +1 -1
  194. package/src/renderer/file-picker-overlay.ts +19 -6
  195. package/src/renderer/help-overlay.ts +106 -33
  196. package/src/renderer/history-search-overlay.ts +19 -4
  197. package/src/renderer/live-tail-modal.ts +27 -5
  198. package/src/renderer/mcp-workspace.ts +164 -50
  199. package/src/renderer/model-picker-overlay.ts +58 -2
  200. package/src/renderer/model-workspace.ts +104 -20
  201. package/src/renderer/process-modal.ts +27 -6
  202. package/src/renderer/profile-picker-modal.ts +20 -4
  203. package/src/renderer/search-overlay.ts +25 -5
  204. package/src/renderer/selection-modal-overlay.ts +46 -14
  205. package/src/renderer/session-picker-modal.ts +18 -1
  206. package/src/renderer/settings-modal-helpers.ts +2 -40
  207. package/src/renderer/settings-modal.ts +83 -50
  208. package/src/renderer/tool-call.ts +20 -11
  209. package/src/runtime/agent-runtime-events.ts +6 -6
  210. package/src/runtime/bootstrap-command-context.ts +7 -1
  211. package/src/runtime/bootstrap-command-parts.ts +9 -7
  212. package/src/runtime/bootstrap-core.ts +19 -1
  213. package/src/runtime/bootstrap-hook-bridge.ts +7 -18
  214. package/src/runtime/bootstrap-shell.ts +4 -4
  215. package/src/runtime/bootstrap.ts +31 -22
  216. package/src/runtime/connected-host-auth.ts +7 -2
  217. package/src/runtime/diagnostics/panels/index.ts +2 -2
  218. package/src/runtime/diagnostics/panels/policy.ts +7 -7
  219. package/src/runtime/index.ts +2 -1
  220. package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
  221. package/src/runtime/onboarding/apply.ts +80 -79
  222. package/src/runtime/onboarding/derivation.ts +5 -8
  223. package/src/runtime/onboarding/snapshot.ts +0 -15
  224. package/src/runtime/onboarding/types.ts +8 -19
  225. package/src/runtime/onboarding/verify.ts +32 -10
  226. package/src/runtime/operator-token-cleanup.ts +1 -1
  227. package/src/runtime/services.ts +91 -22
  228. package/src/runtime/store/selectors/index.ts +3 -3
  229. package/src/runtime/store/state.ts +1 -4
  230. package/src/runtime/surface-feature-flags.ts +41 -6
  231. package/src/runtime/ui-read-models.ts +3 -4
  232. package/src/runtime/ui-services.ts +6 -6
  233. package/src/shell/blocking-input.ts +2 -1
  234. package/src/shell/ui-openers.ts +3 -13
  235. package/src/tools/agent-analysis-registry-policy.ts +0 -1
  236. package/src/tools/agent-channel-send-tool.ts +133 -0
  237. package/src/tools/agent-context-policy.ts +1 -1
  238. package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
  239. package/src/tools/agent-knowledge-tool.ts +170 -0
  240. package/src/tools/agent-local-registry-tool.ts +269 -69
  241. package/src/tools/agent-media-generate-tool.ts +133 -0
  242. package/src/tools/agent-notify-tool.ts +143 -0
  243. package/src/tools/agent-operator-action-tool.ts +137 -0
  244. package/src/tools/agent-operator-briefing-tool.ts +217 -0
  245. package/src/tools/agent-reminder-schedule-tool.ts +237 -0
  246. package/src/tools/agent-tool-policy-guard.ts +8 -8
  247. package/src/tools/agent-work-plan-tool.ts +256 -0
  248. package/src/version.ts +1 -1
  249. package/src/input/commands/policy-dispatch.ts +0 -339
  250. package/src/input/commands/policy.ts +0 -13
  251. package/src/panels/panel-picker.ts +0 -105
  252. package/src/panels/policy-panel.ts +0 -308
  253. package/src/renderer/panel-picker-overlay.ts +0 -202
@@ -1,308 +0,0 @@
1
- import type { Line } from '../types/grid.ts';
2
- import { createEmptyLine } from '../types/grid.ts';
3
- import { BasePanel } from './base-panel.ts';
4
- import type { PolicyRuntimeState } from '@/runtime/index.ts';
5
- import type { PolicyPanelSnapshot } from '../runtime/diagnostics/panels/policy.ts';
6
- import {
7
- buildPanelLine,
8
- buildPanelWorkspace,
9
- resolveScrollablePanelSection,
10
- DEFAULT_PANEL_PALETTE,
11
- } from './polish.ts';
12
-
13
- const C = {
14
- ...DEFAULT_PANEL_PALETTE,
15
- header: '#94a3b8',
16
- headerBg: '#1e293b',
17
- label: '#64748b',
18
- value: '#e2e8f0',
19
- dim: '#475569',
20
- ok: '#22c55e',
21
- warn: '#eab308',
22
- error: '#ef4444',
23
- info: '#38bdf8',
24
- empty: '#334155',
25
- } as const;
26
-
27
- function fmtTime(value: string | undefined): string {
28
- if (!value) return 'n/a';
29
- return value.replace('T', ' ').slice(0, 19);
30
- }
31
-
32
- function fmtRate(value: number | undefined): string {
33
- if (value === undefined) return 'n/a';
34
- return `${(value * 100).toFixed(1)}%`;
35
- }
36
-
37
- function gateColor(status: string | undefined): string {
38
- switch (status) {
39
- case 'allowed':
40
- return C.ok;
41
- case 'blocked':
42
- return C.error;
43
- case 'no_data':
44
- return C.warn;
45
- default:
46
- return C.dim;
47
- }
48
- }
49
-
50
- export class PolicyPanel extends BasePanel {
51
- private readonly _state: PolicyRuntimeState;
52
- private readonly _unsub: (() => void) | null;
53
- private _scrollOffset = 0;
54
-
55
- public constructor(state: PolicyRuntimeState) {
56
- super('policy', 'Policy', 'U', 'monitoring');
57
- this._state = state;
58
- this._unsub = state.subscribe(() => this.markDirty());
59
- }
60
-
61
- public override onActivate(): void {
62
- super.onActivate();
63
- this._scrollOffset = 0;
64
- }
65
-
66
- public override onDestroy(): void {
67
- this._unsub?.();
68
- }
69
-
70
- public handleInput(key: string): boolean {
71
- if (key === 'up' || key === 'k') {
72
- this._scrollOffset = Math.max(0, this._scrollOffset - 1);
73
- this.markDirty();
74
- return true;
75
- }
76
- if (key === 'down' || key === 'j') {
77
- this._scrollOffset++;
78
- this.markDirty();
79
- return true;
80
- }
81
- if (key === 'r') {
82
- this._state.recordTrendEntry();
83
- this.markDirty();
84
- return true;
85
- }
86
- return false;
87
- }
88
-
89
- public render(width: number, height: number): Line[] {
90
- this.needsRender = false;
91
- const snapshot = this._state.getSnapshot();
92
- const content = this._buildContent(width, snapshot);
93
- const governanceSection = resolveScrollablePanelSection(width, height, {
94
- footerLines: [buildPanelLine(width, [[' Up/Down scroll r record divergence trend snapshot', C.dim]])],
95
- palette: C,
96
- section: {
97
- title: 'Governance',
98
- scrollableLines: content,
99
- scrollOffset: this._scrollOffset,
100
- minRows: 4,
101
- },
102
- });
103
- this._scrollOffset = governanceSection.scrollOffset;
104
- const lines = buildPanelWorkspace(width, height, {
105
- title: 'Policy And Governance',
106
- sections: [governanceSection.section],
107
- footerLines: [buildPanelLine(width, [[' Up/Down scroll r record divergence trend snapshot', C.dim]])],
108
- palette: C,
109
- });
110
- while (lines.length < height) {
111
- lines.push(createEmptyLine(width));
112
- }
113
- return lines.slice(0, height);
114
- }
115
-
116
- private _buildContent(width: number, snapshot: PolicyPanelSnapshot): Line[] {
117
- const lines: Line[] = [];
118
- const current = snapshot.current;
119
- const candidate = snapshot.candidate;
120
- const divergence = snapshot.divergence;
121
- const permissionAudit = snapshot.recentPermissionAudit;
122
- const lintFindings = snapshot.lintFindings;
123
- const simulationSummary = snapshot.lastSimulationSummary;
124
- const preflightReview = snapshot.lastPreflightReview;
125
-
126
- if (!current && !candidate) {
127
- lines.push(buildPanelLine(width, [[' No policy bundles loaded. Use /policy load to begin.', C.empty]]));
128
- }
129
-
130
- if (current) {
131
- lines.push(buildPanelLine(width, [[' Current', C.label]]));
132
- lines.push(buildPanelLine(width, [
133
- [' Bundle: ', C.label],
134
- [current.bundle.bundleId, C.value],
135
- [' State: ', C.label],
136
- [current.state, C.info],
137
- ]));
138
- lines.push(buildPanelLine(width, [
139
- [' Loaded: ', C.label],
140
- [fmtTime(current.loadedAt), C.dim],
141
- [' Active: ', C.label],
142
- [fmtTime(current.activatedAt), C.dim],
143
- ]));
144
- }
145
-
146
- if (candidate) {
147
- lines.push(buildPanelLine(width, [[' Candidate', C.label]]));
148
- lines.push(buildPanelLine(width, [
149
- [' Bundle: ', C.label],
150
- [candidate.bundle.bundleId, C.value],
151
- [' State: ', C.label],
152
- [candidate.state, C.info],
153
- ]));
154
- lines.push(buildPanelLine(width, [
155
- [' Loaded: ', C.label],
156
- [fmtTime(candidate.loadedAt), C.dim],
157
- [' Rules: ', C.label],
158
- [String(candidate.rules.length), C.value],
159
- ]));
160
- }
161
-
162
- if (snapshot.diff) {
163
- lines.push(buildPanelLine(width, [[' Diff', C.label]]));
164
- lines.push(buildPanelLine(width, [
165
- [' Changes: ', C.label],
166
- [String(snapshot.diff.totalChanges), C.value],
167
- [' Added: ', C.label],
168
- [String(snapshot.diff.added.length), C.ok],
169
- [' Removed: ', C.label],
170
- [String(snapshot.diff.removed.length), C.error],
171
- [' Changed: ', C.label],
172
- [String(snapshot.diff.changed.length), C.warn],
173
- ]));
174
- }
175
-
176
- if (divergence) {
177
- lines.push(buildPanelLine(width, [[' Governance Gate', C.label]]));
178
- lines.push(buildPanelLine(width, [
179
- [' Mode: ', C.label],
180
- [divergence.mode, C.info],
181
- [' Gate: ', C.label],
182
- [divergence.gate.status, gateColor(divergence.gate.status)],
183
- [' Divergence: ', C.label],
184
- [fmtRate(divergence.gate.divergenceRate ?? divergence.report.overall.divergenceRate), C.value],
185
- ]));
186
- lines.push(buildPanelLine(width, [
187
- [' Evaluations: ', C.label],
188
- [String(divergence.report.overall.totalEvaluations), C.value],
189
- [' Trend points: ', C.label],
190
- [String(divergence.trend.length), C.value],
191
- ]));
192
- }
193
-
194
- if (snapshot.history.length > 0) {
195
- lines.push(buildPanelLine(width, [[' History', C.label]]));
196
- for (const version of snapshot.history.slice(0, 5)) {
197
- lines.push(buildPanelLine(width, [
198
- [' ', C.label],
199
- [version.bundle.bundleId, C.value],
200
- [' ', C.label],
201
- [version.state, C.dim],
202
- [' ', C.label],
203
- [fmtTime(version.activatedAt ?? version.loadedAt), C.dim],
204
- ]));
205
- }
206
- }
207
-
208
- if (permissionAudit.length > 0) {
209
- lines.push(buildPanelLine(width, [[' Permission Audit', C.label]]));
210
- for (const entry of permissionAudit.slice(0, 5)) {
211
- const outcome = entry.approved === undefined ? 'pending' : entry.approved ? 'approved' : 'denied';
212
- const outcomeColor = entry.approved === undefined ? C.warn : entry.approved ? C.ok : C.error;
213
- lines.push(buildPanelLine(width, [
214
- [' ', C.label],
215
- [entry.tool, C.value],
216
- [' ', C.label],
217
- [entry.riskLevel.toUpperCase(), outcomeColor],
218
- [' ', C.label],
219
- [outcome, outcomeColor],
220
- ]));
221
- lines.push(buildPanelLine(width, [
222
- [' ', C.label],
223
- [entry.summary.slice(0, Math.max(0, width - 6)), C.dim],
224
- ]));
225
- }
226
- }
227
-
228
- if (lintFindings.length > 0) {
229
- lines.push(buildPanelLine(width, [[' Policy Lint', C.label]]));
230
- for (const finding of lintFindings.slice(0, 5)) {
231
- const color = finding.severity === 'error' ? C.error : finding.severity === 'warn' ? C.warn : C.info;
232
- lines.push(buildPanelLine(width, [
233
- [' ', C.label],
234
- [finding.severity.toUpperCase(), color],
235
- [' ', C.label],
236
- [finding.message.slice(0, Math.max(0, width - 14)), color],
237
- ]));
238
- }
239
- }
240
-
241
- if (!preflightReview) {
242
- if (!current && !candidate && !divergence && snapshot.history.length === 0 && permissionAudit.length === 0 && lintFindings.length === 0 && !simulationSummary) {
243
- lines.push(buildPanelLine(width, [[' Preflight Review', C.label]]));
244
- lines.push(buildPanelLine(width, [[' No proactive preflight review recorded yet.', C.empty]]));
245
- }
246
- } else {
247
- lines.push(buildPanelLine(width, [[' Preflight Review', C.label]]));
248
- const statusColor =
249
- preflightReview.status === 'pass'
250
- ? C.ok
251
- : preflightReview.status === 'warn'
252
- ? C.warn
253
- : C.error;
254
- lines.push(buildPanelLine(width, [
255
- [' Status: ', C.label],
256
- [preflightReview.status.toUpperCase(), statusColor],
257
- [' Issues: ', C.label],
258
- [String(preflightReview.issueCount), C.value],
259
- [' Generated: ', C.label],
260
- [fmtTime(preflightReview.generatedAt), C.dim],
261
- ]));
262
- lines.push(buildPanelLine(width, [
263
- [' ', C.label],
264
- [preflightReview.summary.slice(0, Math.max(0, width - 2)), C.dim],
265
- ]));
266
- for (const issue of preflightReview.issues.slice(0, 4)) {
267
- const issueColor = issue.severity === 'error' ? C.error : issue.severity === 'warn' ? C.warn : C.info;
268
- lines.push(buildPanelLine(width, [
269
- [' ', C.label],
270
- [issue.severity.toUpperCase(), issueColor],
271
- [' ', C.label],
272
- [issue.message.slice(0, Math.max(0, width - 14)), issueColor],
273
- ]));
274
- }
275
- }
276
-
277
- if (!simulationSummary) {
278
- if (!current && !candidate && !divergence && snapshot.history.length === 0 && permissionAudit.length === 0 && lintFindings.length === 0 && !preflightReview) {
279
- lines.push(buildPanelLine(width, [[' Simulation Samples', C.label]]));
280
- lines.push(buildPanelLine(width, [[' No concrete simulation samples recorded yet.', C.empty]]));
281
- }
282
- } else {
283
- lines.push(buildPanelLine(width, [[' Simulation Samples', C.label]]));
284
- lines.push(buildPanelLine(width, [
285
- [' Mode: ', C.label],
286
- [simulationSummary.mode, C.info],
287
- [' Diverged: ', C.label],
288
- [`${simulationSummary.divergentScenarios}/${simulationSummary.totalScenarios}`, simulationSummary.divergentScenarios > 0 ? C.warn : C.ok],
289
- [' Allowed(actual/sim): ', C.label],
290
- [`${simulationSummary.allowedByActual}/${simulationSummary.allowedBySimulated}`, C.value],
291
- ]));
292
- for (const result of simulationSummary.results.slice(0, 4)) {
293
- const color = result.diverged ? C.warn : (result.authoritativeDecision.allowed ? C.ok : C.error);
294
- lines.push(buildPanelLine(width, [
295
- [' ', C.label],
296
- [result.scenario.label.slice(0, Math.max(0, width - 40)), C.value],
297
- [' ', C.label],
298
- [(result.authoritativeDecision.allowed ? 'allow' : 'deny').toUpperCase(), color],
299
- [' ', C.label],
300
- [result.diverged ? (result.divergenceType ?? 'diverged') : 'aligned', color],
301
- ]));
302
- }
303
- }
304
-
305
- lines.push(buildPanelLine(width, [[' /policy opens this panel. Press r to record a divergence trend snapshot.', C.dim]]));
306
- return lines;
307
- }
308
- }
@@ -1,202 +0,0 @@
1
- import { type Line } from '../types/grid.ts';
2
- import { fitDisplay, getDisplayWidth, truncateDisplay } from '../utils/terminal-width.ts';
3
- import type { PanelPicker } from '../panels/panel-picker.ts';
4
- import { CATEGORY_LABELS, CATEGORY_ORDER } from '../panels/panel-picker.ts';
5
- import type { PanelCategory, PanelRegistration } from '../panels/types.ts';
6
- import {
7
- createOverlayBorderLine,
8
- createOverlayBoxLayout,
9
- createOverlayContentLine,
10
- DEFAULT_OVERLAY_PALETTE,
11
- putOverlayText,
12
- } from './overlay-box.ts';
13
- import { getOverlaySurfaceMetrics } from './overlay-viewport.ts';
14
-
15
- const TITLE_FG = '#cbd5e1';
16
- const CATEGORY_FG = '#94a3b8';
17
- const SELECTED_FG = '#e2e8f0';
18
- const SELECTED_BG = '#1e293b';
19
- const BODY_FG = '252';
20
- const BORDER_FG = DEFAULT_OVERLAY_PALETTE.borderFg;
21
-
22
- /**
23
- * Render the panel picker modal as Line[] for overlay in the viewport.
24
- * Panels are grouped by category. Category headers are inserted between groups.
25
- * When a search query is active a search-bar row is shown beneath the title.
26
- */
27
- export function renderPanelPickerOverlay(
28
- picker: PanelPicker,
29
- width: number,
30
- viewportHeight = 24,
31
- ): Line[] {
32
- if (!picker.active) return [];
33
-
34
- const lines: Line[] = [];
35
- const metrics = getOverlaySurfaceMetrics(width, viewportHeight, {
36
- margin: 4,
37
- maxWidth: 72,
38
- chromeRows: picker.searchQuery.length > 0 ? 7 : 6,
39
- minContentRows: 6,
40
- maxContentRows: 10,
41
- });
42
- const layout = createOverlayBoxLayout(width, metrics.margin, metrics.boxWidth);
43
- const contentW = layout.innerWidth;
44
- const titleFg = TITLE_FG;
45
- const borderFg = BORDER_FG;
46
-
47
- // ── Title bar ──────────────────────────────────────────────────────────────
48
- const titleLine = createOverlayBorderLine(width, layout, '┌', '─', '┐', borderFg);
49
- putOverlayText(titleLine, layout.margin + 2, layout.width - 4, 'Deferred Workspace Picker', { fg: titleFg, bold: true });
50
- lines.push(titleLine);
51
-
52
- // ── Search bar (shown when query is non-empty) ──────────────────────────────
53
- if (picker.searchQuery.length > 0) {
54
- const searchLine = createOverlayContentLine(width, layout, borderFg);
55
- const searchLabel = '\u2315 ';
56
- const queryAvail = contentW - getDisplayWidth(searchLabel);
57
- const queryText = fitDisplay(picker.searchQuery, queryAvail);
58
- putOverlayText(searchLine, layout.margin + 2, getDisplayWidth(searchLabel), searchLabel, { fg: titleFg });
59
- putOverlayText(searchLine, layout.margin + 2 + getDisplayWidth(searchLabel), queryAvail, queryText, { fg: titleFg });
60
- lines.push(searchLine);
61
-
62
- lines.push(createOverlayBorderLine(width, layout, '├', '─', '┤', borderFg));
63
- } else {
64
- const placeholder = ' Browse by category or start typing to filter by panel name, purpose, or category.';
65
- const row = createOverlayContentLine(width, layout, borderFg);
66
- putOverlayText(row, layout.margin + 2, contentW, fitDisplay(placeholder, contentW), { fg: '244', dim: true });
67
- lines.push(row);
68
- }
69
-
70
- const visible = picker.getVisible();
71
-
72
- if (visible.length === 0) {
73
- const noResults = 'No panels match your search';
74
- const noRow = createOverlayContentLine(width, layout, borderFg);
75
- putOverlayText(noRow, layout.margin + 2, contentW, fitDisplay(noResults, contentW), { fg: '244', dim: true });
76
- lines.push(noRow);
77
- } else {
78
- // Build a flat render list of { type: 'header' | 'item', ... } entries
79
- // so we can compute the correct scroll window over all rows.
80
- type HeaderEntry = { type: 'header'; category: PanelCategory };
81
- type ItemEntry = { type: 'item'; reg: PanelRegistration; flatIndex: number };
82
- type RenderEntry = HeaderEntry | ItemEntry;
83
-
84
- const renderEntries: RenderEntry[] = [];
85
-
86
- // When no search query, group by category; when searching, show flat list
87
- // with a single "Results" header.
88
- if (picker.searchQuery.length === 0) {
89
- const byCategory = new Map<PanelCategory, PanelRegistration[]>();
90
- for (const reg of visible) {
91
- const group = byCategory.get(reg.category) ?? [];
92
- group.push(reg);
93
- byCategory.set(reg.category, group);
94
- }
95
- let flatIndex = 0;
96
- for (const cat of CATEGORY_ORDER) {
97
- const group = byCategory.get(cat);
98
- if (!group || group.length === 0) continue;
99
- renderEntries.push({ type: 'header', category: cat });
100
- for (const reg of group) {
101
- renderEntries.push({ type: 'item', reg, flatIndex });
102
- flatIndex++;
103
- }
104
- }
105
- } else {
106
- for (let i = 0; i < visible.length; i++) {
107
- renderEntries.push({ type: 'item', reg: visible[i], flatIndex: i });
108
- }
109
- }
110
-
111
- // Compute scroll window: ensure the selected item row stays visible.
112
- // Find the render-entry index of the selected item.
113
- const selectedEntryIdx = renderEntries.findIndex(
114
- e => e.type === 'item' && e.flatIndex === picker.selectedIndex,
115
- );
116
- const total = renderEntries.length;
117
- const maxVisible = metrics.contentRows;
118
- let startEntry = 0;
119
- if (total > maxVisible && selectedEntryIdx >= 0) {
120
- startEntry = Math.max(
121
- 0,
122
- Math.min(
123
- selectedEntryIdx - Math.floor(maxVisible / 2),
124
- total - maxVisible,
125
- ),
126
- );
127
- }
128
- const endEntry = Math.min(startEntry + maxVisible, total);
129
-
130
- for (let i = startEntry; i < endEntry; i++) {
131
- const entry = renderEntries[i];
132
-
133
- if (entry.type === 'header') {
134
- // Category header row
135
- const label = CATEGORY_LABELS[entry.category].toUpperCase();
136
- const headerRow = createOverlayContentLine(width, layout, borderFg);
137
- putOverlayText(headerRow, layout.margin + 2, contentW, fitDisplay(` ${label}`, contentW), { fg: CATEGORY_FG, dim: true });
138
- lines.push(headerRow);
139
- } else {
140
- const { reg, flatIndex } = entry;
141
- const isSelected = flatIndex === picker.selectedIndex;
142
- const indicator = isSelected ? '\u25b6 ' : ' ';
143
-
144
- // icon (1 char) + 2 spaces + name
145
- const iconStr = reg.icon + ' ';
146
- const iconW = getDisplayWidth(iconStr);
147
-
148
- // separator between name and description: ' \u2014 '
149
- const sep = ' \u2014 ';
150
- const sepW = getDisplayWidth(sep);
151
-
152
- // Allocate space: indicator(2) + icon(iconW) + name + sep + desc fills contentW
153
- const nameMaxW = Math.max(8, Math.floor((contentW - 2 - iconW - sepW) * 0.35));
154
- const descMaxW = contentW - 2 - iconW - nameMaxW - sepW;
155
-
156
- const nameRaw = reg.name;
157
- const nameStr = fitDisplay(nameRaw, nameMaxW);
158
-
159
- const descRaw = reg.description;
160
- const descStr = fitDisplay(descRaw, descMaxW);
161
-
162
- const row = createOverlayContentLine(width, layout, borderFg, isSelected ? SELECTED_BG : '');
163
- const rowText = indicator + iconStr + nameStr + sep + descStr;
164
- putOverlayText(row, layout.margin + 2, contentW, fitDisplay(truncateDisplay(rowText, contentW), contentW), {
165
- fg: isSelected ? SELECTED_FG : BODY_FG,
166
- bg: isSelected ? SELECTED_BG : '',
167
- bold: isSelected,
168
- });
169
- lines.push(row);
170
- }
171
- }
172
-
173
- const selected = picker.getSelected();
174
- if (selected) {
175
- lines.push(createOverlayBorderLine(width, layout, '├', '─', '┤', borderFg));
176
- const categoryLabel = CATEGORY_LABELS[selected.category].toUpperCase();
177
- const selectedLine = createOverlayContentLine(width, layout, borderFg);
178
- putOverlayText(selectedLine, layout.margin + 2, contentW, fitDisplay(`${selected.icon} ${selected.name} [${categoryLabel}]`, contentW), { fg: SELECTED_FG });
179
- lines.push(selectedLine);
180
- const desc = fitDisplay(truncateDisplay(selected.description, contentW), contentW);
181
- const descRow = createOverlayContentLine(width, layout, borderFg);
182
- putOverlayText(descRow, layout.margin + 2, contentW, desc, { fg: '244', dim: true });
183
- lines.push(descRow);
184
- }
185
-
186
- // ── Scroll indicator ───────────────────────────────────────────────────
187
- if (total > maxVisible) {
188
- const scrollInfo = `${picker.selectedIndex + 1}/${visible.length}`;
189
- const scrollRow = createOverlayContentLine(width, layout, borderFg);
190
- putOverlayText(scrollRow, layout.margin + 2 + Math.max(0, contentW - getDisplayWidth(scrollInfo)), getDisplayWidth(scrollInfo), scrollInfo, { fg: '240', dim: true });
191
- lines.push(scrollRow);
192
- }
193
- }
194
-
195
- // ── Bottom border with hints ───────────────────────────────────────────────
196
- const hints = '[Up/Down] Navigate [Enter] Open [/] Filter [Esc] Cancel';
197
- const bottomLine = createOverlayBorderLine(width, layout, '└', '─', '┘', borderFg);
198
- putOverlayText(bottomLine, layout.margin + 2, layout.width - 4, truncateDisplay(hints, layout.width - 4), { fg: '240', dim: true });
199
- lines.push(bottomLine);
200
-
201
- return lines;
202
- }