@pellux/goodvibes-agent 0.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.
Files changed (398) hide show
  1. package/.goodvibes/GOODVIBES.md +35 -0
  2. package/.goodvibes/agents/reviewer.md +48 -0
  3. package/.goodvibes/skills/add-provider/SKILL.md +199 -0
  4. package/CHANGELOG.md +25 -0
  5. package/README.md +74 -0
  6. package/bin/goodvibes-agent.ts +2 -0
  7. package/docs/README.md +23 -0
  8. package/docs/deployment-and-services.md +57 -0
  9. package/docs/getting-started.md +53 -0
  10. package/docs/release-and-publishing.md +46 -0
  11. package/package.json +134 -0
  12. package/scripts/check-bun.sh +20 -0
  13. package/src/audio/player.ts +156 -0
  14. package/src/audio/spoken-turn-controller.ts +203 -0
  15. package/src/audio/spoken-turn-model-routing.ts +117 -0
  16. package/src/audio/spoken-turn-wiring.ts +44 -0
  17. package/src/audio/text-chunker.ts +110 -0
  18. package/src/cli/bundle-command.ts +227 -0
  19. package/src/cli/completion.ts +90 -0
  20. package/src/cli/config-overrides.ts +159 -0
  21. package/src/cli/endpoints.ts +63 -0
  22. package/src/cli/entrypoint.ts +172 -0
  23. package/src/cli/help.ts +299 -0
  24. package/src/cli/index.ts +11 -0
  25. package/src/cli/management-commands.ts +426 -0
  26. package/src/cli/management.ts +744 -0
  27. package/src/cli/network-posture.ts +46 -0
  28. package/src/cli/package-verification.ts +123 -0
  29. package/src/cli/parser.ts +369 -0
  30. package/src/cli/provider-auth-routes.ts +22 -0
  31. package/src/cli/provider-classification.ts +107 -0
  32. package/src/cli/redaction.ts +105 -0
  33. package/src/cli/service-command.ts +26 -0
  34. package/src/cli/service-posture.ts +482 -0
  35. package/src/cli/status.ts +383 -0
  36. package/src/cli/surface-command.ts +247 -0
  37. package/src/cli/tui-startup.ts +32 -0
  38. package/src/cli/types.ts +69 -0
  39. package/src/cli-flags.ts +21 -0
  40. package/src/config/goodvibes-home-audit.ts +465 -0
  41. package/src/config/index.ts +57 -0
  42. package/src/config/provider-model.ts +23 -0
  43. package/src/config/secret-config.ts +119 -0
  44. package/src/config/secrets.ts +71 -0
  45. package/src/config/surface.ts +1 -0
  46. package/src/core/composer-state.ts +61 -0
  47. package/src/core/conversation-rendering.ts +359 -0
  48. package/src/core/conversation.ts +551 -0
  49. package/src/core/history.ts +45 -0
  50. package/src/core/orchestrator.ts +7 -0
  51. package/src/core/system-message-router.ts +171 -0
  52. package/src/daemon/cli.ts +55 -0
  53. package/src/daemon/safe-serve.ts +61 -0
  54. package/src/input/agent-workspace.ts +428 -0
  55. package/src/input/autocomplete.ts +96 -0
  56. package/src/input/bookmark-modal.ts +115 -0
  57. package/src/input/command-args-hint.ts +36 -0
  58. package/src/input/command-registry.ts +329 -0
  59. package/src/input/commands/agent-externalized-tui.ts +73 -0
  60. package/src/input/commands/agent-workspace-runtime.ts +17 -0
  61. package/src/input/commands/branch-runtime.ts +72 -0
  62. package/src/input/commands/cloudflare-runtime.ts +370 -0
  63. package/src/input/commands/config.ts +18 -0
  64. package/src/input/commands/control-room-runtime.ts +255 -0
  65. package/src/input/commands/conversation-runtime.ts +207 -0
  66. package/src/input/commands/discovery-runtime.ts +52 -0
  67. package/src/input/commands/eval.ts +204 -0
  68. package/src/input/commands/experience-runtime.ts +278 -0
  69. package/src/input/commands/guidance-runtime.ts +106 -0
  70. package/src/input/commands/health-runtime.ts +434 -0
  71. package/src/input/commands/hooks-runtime.ts +148 -0
  72. package/src/input/commands/incident-runtime.ts +95 -0
  73. package/src/input/commands/integration-runtime.ts +394 -0
  74. package/src/input/commands/intelligence-runtime.ts +223 -0
  75. package/src/input/commands/knowledge.ts +531 -0
  76. package/src/input/commands/local-auth-runtime.ts +105 -0
  77. package/src/input/commands/local-provider-runtime.ts +170 -0
  78. package/src/input/commands/local-runtime.ts +392 -0
  79. package/src/input/commands/local-setup-review.ts +199 -0
  80. package/src/input/commands/local-setup-transfer.ts +135 -0
  81. package/src/input/commands/local-setup.ts +282 -0
  82. package/src/input/commands/managed-runtime.ts +209 -0
  83. package/src/input/commands/marketplace-runtime.ts +290 -0
  84. package/src/input/commands/mcp-runtime.ts +432 -0
  85. package/src/input/commands/memory-product-runtime.ts +111 -0
  86. package/src/input/commands/memory.ts +151 -0
  87. package/src/input/commands/notify-runtime.ts +83 -0
  88. package/src/input/commands/onboarding-runtime.ts +14 -0
  89. package/src/input/commands/operator-panel-runtime.ts +146 -0
  90. package/src/input/commands/operator-runtime.ts +392 -0
  91. package/src/input/commands/planning-runtime.ts +205 -0
  92. package/src/input/commands/platform-access-runtime.ts +422 -0
  93. package/src/input/commands/platform-services-runtime.ts +246 -0
  94. package/src/input/commands/policy-dispatch.ts +339 -0
  95. package/src/input/commands/policy.ts +17 -0
  96. package/src/input/commands/product-runtime.ts +351 -0
  97. package/src/input/commands/profile-sync-runtime.ts +99 -0
  98. package/src/input/commands/provider-accounts-runtime.ts +113 -0
  99. package/src/input/commands/provider.ts +363 -0
  100. package/src/input/commands/qrcode-runtime.ts +20 -0
  101. package/src/input/commands/quit-shared.ts +162 -0
  102. package/src/input/commands/recall-bundle.ts +132 -0
  103. package/src/input/commands/recall-capture.ts +152 -0
  104. package/src/input/commands/recall-query.ts +229 -0
  105. package/src/input/commands/recall-review.ts +98 -0
  106. package/src/input/commands/recall-shared.ts +22 -0
  107. package/src/input/commands/remote-runtime-pool.ts +106 -0
  108. package/src/input/commands/remote-runtime-setup.ts +199 -0
  109. package/src/input/commands/remote-runtime.ts +431 -0
  110. package/src/input/commands/replay-runtime.ts +18 -0
  111. package/src/input/commands/runtime-services.ts +291 -0
  112. package/src/input/commands/schedule-runtime.ts +91 -0
  113. package/src/input/commands/services-runtime.ts +209 -0
  114. package/src/input/commands/session-content.ts +408 -0
  115. package/src/input/commands/session-workflow.ts +464 -0
  116. package/src/input/commands/session.ts +375 -0
  117. package/src/input/commands/settings-sync-runtime.ts +174 -0
  118. package/src/input/commands/share-runtime.ts +119 -0
  119. package/src/input/commands/shell-core.ts +307 -0
  120. package/src/input/commands/skills-runtime.ts +221 -0
  121. package/src/input/commands/subscription-runtime.ts +434 -0
  122. package/src/input/commands/tasks-runtime.ts +230 -0
  123. package/src/input/commands/teamwork-runtime.ts +339 -0
  124. package/src/input/commands/teleport-runtime.ts +57 -0
  125. package/src/input/commands/tts-runtime.ts +29 -0
  126. package/src/input/commands/work-plan-runtime.ts +169 -0
  127. package/src/input/commands.ts +131 -0
  128. package/src/input/feed-context-factory.ts +254 -0
  129. package/src/input/file-picker.ts +192 -0
  130. package/src/input/handler-command-route.ts +180 -0
  131. package/src/input/handler-content-actions.ts +497 -0
  132. package/src/input/handler-feed-routes.ts +648 -0
  133. package/src/input/handler-feed.ts +452 -0
  134. package/src/input/handler-interactions.ts +281 -0
  135. package/src/input/handler-modal-routes.ts +418 -0
  136. package/src/input/handler-modal-stack.ts +263 -0
  137. package/src/input/handler-modal-token-routes.ts +329 -0
  138. package/src/input/handler-onboarding-cloudflare.ts +391 -0
  139. package/src/input/handler-onboarding.ts +620 -0
  140. package/src/input/handler-picker-routes.ts +472 -0
  141. package/src/input/handler-prompt-buffer.ts +320 -0
  142. package/src/input/handler-shortcuts.ts +213 -0
  143. package/src/input/handler-ui-state.ts +372 -0
  144. package/src/input/handler.ts +729 -0
  145. package/src/input/input-history.ts +297 -0
  146. package/src/input/keybindings.ts +292 -0
  147. package/src/input/mcp-workspace.ts +554 -0
  148. package/src/input/model-picker-provider-filter.ts +28 -0
  149. package/src/input/model-picker-types.ts +137 -0
  150. package/src/input/model-picker.ts +797 -0
  151. package/src/input/onboarding/handler-onboarding-routes.ts +125 -0
  152. package/src/input/onboarding/onboarding-runtime-status.ts +87 -0
  153. package/src/input/onboarding/onboarding-wizard-apply.ts +277 -0
  154. package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +494 -0
  155. package/src/input/onboarding/onboarding-wizard-cloudflare.ts +204 -0
  156. package/src/input/onboarding/onboarding-wizard-constants.ts +158 -0
  157. package/src/input/onboarding/onboarding-wizard-external-surface-extra-specs.ts +130 -0
  158. package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +762 -0
  159. package/src/input/onboarding/onboarding-wizard-helpers.ts +167 -0
  160. package/src/input/onboarding/onboarding-wizard-rules.ts +256 -0
  161. package/src/input/onboarding/onboarding-wizard-state.ts +365 -0
  162. package/src/input/onboarding/onboarding-wizard-steps.ts +798 -0
  163. package/src/input/onboarding/onboarding-wizard-types.ts +195 -0
  164. package/src/input/onboarding/onboarding-wizard.ts +711 -0
  165. package/src/input/panel-integration-actions.ts +78 -0
  166. package/src/input/profile-picker-modal.ts +222 -0
  167. package/src/input/search.ts +100 -0
  168. package/src/input/selection-modal.ts +163 -0
  169. package/src/input/selection.ts +135 -0
  170. package/src/input/session-picker-modal.ts +136 -0
  171. package/src/input/settings-modal-behavior.ts +37 -0
  172. package/src/input/settings-modal-secrets.ts +41 -0
  173. package/src/input/settings-modal-subscriptions.ts +95 -0
  174. package/src/input/settings-modal-types.ts +91 -0
  175. package/src/input/settings-modal.ts +793 -0
  176. package/src/input/submission-intent.ts +17 -0
  177. package/src/input/submission-router.ts +59 -0
  178. package/src/input/tts-settings-actions.ts +100 -0
  179. package/src/main.ts +792 -0
  180. package/src/mcp/runtime-reload.ts +81 -0
  181. package/src/panels/agent-inspector-panel.ts +521 -0
  182. package/src/panels/agent-inspector-shared.ts +94 -0
  183. package/src/panels/agent-logs-panel.ts +559 -0
  184. package/src/panels/agent-logs-shared.ts +129 -0
  185. package/src/panels/approval-panel.ts +150 -0
  186. package/src/panels/automation-control-panel.ts +212 -0
  187. package/src/panels/base-panel.ts +254 -0
  188. package/src/panels/builtin/agent.ts +117 -0
  189. package/src/panels/builtin/development.ts +31 -0
  190. package/src/panels/builtin/knowledge.ts +26 -0
  191. package/src/panels/builtin/operations.ts +349 -0
  192. package/src/panels/builtin/session.ts +129 -0
  193. package/src/panels/builtin/shared.ts +274 -0
  194. package/src/panels/builtin-panels.ts +23 -0
  195. package/src/panels/cockpit-panel.ts +183 -0
  196. package/src/panels/communication-panel.ts +153 -0
  197. package/src/panels/confirm-state.ts +61 -0
  198. package/src/panels/context-visualizer-panel.ts +204 -0
  199. package/src/panels/control-plane-panel.ts +211 -0
  200. package/src/panels/cost-tracker-panel.ts +444 -0
  201. package/src/panels/debug-panel.ts +432 -0
  202. package/src/panels/diff-panel.ts +520 -0
  203. package/src/panels/docs-panel.ts +283 -0
  204. package/src/panels/eval-panel.ts +399 -0
  205. package/src/panels/file-explorer-panel.ts +584 -0
  206. package/src/panels/file-preview-panel.ts +434 -0
  207. package/src/panels/forensics-panel.ts +364 -0
  208. package/src/panels/git-panel.ts +638 -0
  209. package/src/panels/hooks-panel.ts +239 -0
  210. package/src/panels/incident-review-panel.ts +197 -0
  211. package/src/panels/index.ts +46 -0
  212. package/src/panels/intelligence-panel.ts +176 -0
  213. package/src/panels/knowledge-panel.ts +345 -0
  214. package/src/panels/local-auth-panel.ts +130 -0
  215. package/src/panels/marketplace-panel.ts +212 -0
  216. package/src/panels/memory-panel.ts +225 -0
  217. package/src/panels/ops-control-panel.ts +150 -0
  218. package/src/panels/ops-strategy-panel.ts +235 -0
  219. package/src/panels/orchestration-panel.ts +273 -0
  220. package/src/panels/panel-list-panel.ts +509 -0
  221. package/src/panels/panel-manager.ts +570 -0
  222. package/src/panels/panel-picker.ts +106 -0
  223. package/src/panels/plan-dashboard-panel.ts +274 -0
  224. package/src/panels/plugins-panel.ts +178 -0
  225. package/src/panels/policy-panel.ts +308 -0
  226. package/src/panels/polish.ts +717 -0
  227. package/src/panels/project-planning-panel.ts +711 -0
  228. package/src/panels/provider-account-snapshot.ts +259 -0
  229. package/src/panels/provider-accounts-panel.ts +218 -0
  230. package/src/panels/provider-health-domains.ts +215 -0
  231. package/src/panels/provider-health-panel.ts +727 -0
  232. package/src/panels/provider-health-tracker.ts +115 -0
  233. package/src/panels/provider-stats-panel.ts +366 -0
  234. package/src/panels/qr-panel.ts +182 -0
  235. package/src/panels/remote-panel.ts +449 -0
  236. package/src/panels/routes-panel.ts +178 -0
  237. package/src/panels/sandbox-panel.ts +283 -0
  238. package/src/panels/schedule-panel.ts +329 -0
  239. package/src/panels/scrollable-list-panel.ts +491 -0
  240. package/src/panels/search-focus.ts +32 -0
  241. package/src/panels/security-panel.ts +295 -0
  242. package/src/panels/services-panel.ts +231 -0
  243. package/src/panels/session-browser-panel.ts +400 -0
  244. package/src/panels/session-maintenance.ts +125 -0
  245. package/src/panels/settings-sync-panel.ts +120 -0
  246. package/src/panels/skills-panel.ts +431 -0
  247. package/src/panels/subscription-panel.ts +263 -0
  248. package/src/panels/symbol-outline-panel.ts +486 -0
  249. package/src/panels/system-messages-panel.ts +230 -0
  250. package/src/panels/tasks-panel.ts +399 -0
  251. package/src/panels/thinking-panel.ts +304 -0
  252. package/src/panels/token-budget-panel.ts +475 -0
  253. package/src/panels/tool-inspector-panel.ts +429 -0
  254. package/src/panels/types.ts +54 -0
  255. package/src/panels/watchers-panel.ts +193 -0
  256. package/src/panels/work-plan-panel.ts +175 -0
  257. package/src/panels/worktree-panel.ts +182 -0
  258. package/src/panels/wrfc-panel.ts +609 -0
  259. package/src/permissions/prompt.ts +165 -0
  260. package/src/planning/project-planning-coordinator.ts +543 -0
  261. package/src/plugins/loader.ts +15 -0
  262. package/src/renderer/agent-detail-modal.ts +331 -0
  263. package/src/renderer/agent-workspace.ts +238 -0
  264. package/src/renderer/ansi-sanitize.ts +76 -0
  265. package/src/renderer/autocomplete-overlay.ts +154 -0
  266. package/src/renderer/block-actions.ts +76 -0
  267. package/src/renderer/bookmark-modal.ts +101 -0
  268. package/src/renderer/bottom-bar.ts +58 -0
  269. package/src/renderer/buffer.ts +113 -0
  270. package/src/renderer/code-block.ts +373 -0
  271. package/src/renderer/compositor.ts +283 -0
  272. package/src/renderer/context-inspector.ts +219 -0
  273. package/src/renderer/conversation-layout.ts +67 -0
  274. package/src/renderer/conversation-overlays.ts +140 -0
  275. package/src/renderer/conversation-surface.ts +260 -0
  276. package/src/renderer/diff-view.ts +132 -0
  277. package/src/renderer/diff.ts +130 -0
  278. package/src/renderer/file-picker-overlay.ts +101 -0
  279. package/src/renderer/file-tree.ts +153 -0
  280. package/src/renderer/fullscreen-primitives.ts +130 -0
  281. package/src/renderer/fullscreen-workspace.ts +199 -0
  282. package/src/renderer/git-status.ts +89 -0
  283. package/src/renderer/help-overlay.ts +267 -0
  284. package/src/renderer/history-search-overlay.ts +73 -0
  285. package/src/renderer/layout-engine.ts +97 -0
  286. package/src/renderer/layout.ts +32 -0
  287. package/src/renderer/live-tail-modal.ts +156 -0
  288. package/src/renderer/markdown.ts +635 -0
  289. package/src/renderer/mcp-workspace.ts +237 -0
  290. package/src/renderer/modal-factory.ts +467 -0
  291. package/src/renderer/modal-utils.ts +24 -0
  292. package/src/renderer/model-picker-overlay.ts +473 -0
  293. package/src/renderer/model-workspace.ts +488 -0
  294. package/src/renderer/onboarding/onboarding-wizard.ts +615 -0
  295. package/src/renderer/overlay-box.ts +146 -0
  296. package/src/renderer/overlay-viewport.ts +104 -0
  297. package/src/renderer/panel-composite.ts +158 -0
  298. package/src/renderer/panel-picker-overlay.ts +202 -0
  299. package/src/renderer/panel-tab-bar.ts +69 -0
  300. package/src/renderer/panel-workspace-bar.ts +42 -0
  301. package/src/renderer/process-indicator.ts +96 -0
  302. package/src/renderer/process-modal.ts +656 -0
  303. package/src/renderer/process-summary.ts +67 -0
  304. package/src/renderer/profile-picker-modal.ts +129 -0
  305. package/src/renderer/progress.ts +98 -0
  306. package/src/renderer/qr-renderer.ts +120 -0
  307. package/src/renderer/search-overlay.ts +54 -0
  308. package/src/renderer/selection-modal-overlay.ts +214 -0
  309. package/src/renderer/semantic-diff.ts +369 -0
  310. package/src/renderer/session-picker-modal.ts +127 -0
  311. package/src/renderer/settings-modal-helpers.ts +193 -0
  312. package/src/renderer/settings-modal.ts +537 -0
  313. package/src/renderer/shell-surface.ts +88 -0
  314. package/src/renderer/status-glyphs.ts +21 -0
  315. package/src/renderer/status-token.ts +67 -0
  316. package/src/renderer/surface-layout.ts +101 -0
  317. package/src/renderer/syntax-highlighter.ts +542 -0
  318. package/src/renderer/system-message.ts +83 -0
  319. package/src/renderer/tab-strip.ts +108 -0
  320. package/src/renderer/text-layout.ts +31 -0
  321. package/src/renderer/thinking.ts +17 -0
  322. package/src/renderer/tool-call.ts +234 -0
  323. package/src/renderer/ui-factory.ts +524 -0
  324. package/src/renderer/ui-primitives.ts +96 -0
  325. package/src/runtime/bootstrap-command-context.ts +278 -0
  326. package/src/runtime/bootstrap-command-parts.ts +386 -0
  327. package/src/runtime/bootstrap-core.ts +540 -0
  328. package/src/runtime/bootstrap-hook-bridge.ts +112 -0
  329. package/src/runtime/bootstrap-shell.ts +283 -0
  330. package/src/runtime/bootstrap.ts +575 -0
  331. package/src/runtime/cloudflare-control-plane.ts +349 -0
  332. package/src/runtime/context.ts +142 -0
  333. package/src/runtime/diagnostics/panels/index.ts +24 -0
  334. package/src/runtime/diagnostics/panels/ops.ts +156 -0
  335. package/src/runtime/diagnostics/panels/panel-resources.ts +118 -0
  336. package/src/runtime/diagnostics/panels/policy.ts +177 -0
  337. package/src/runtime/index.ts +662 -0
  338. package/src/runtime/onboarding/apply.ts +642 -0
  339. package/src/runtime/onboarding/derivation.ts +534 -0
  340. package/src/runtime/onboarding/index.ts +7 -0
  341. package/src/runtime/onboarding/markers.ts +148 -0
  342. package/src/runtime/onboarding/snapshot.ts +406 -0
  343. package/src/runtime/onboarding/state.ts +141 -0
  344. package/src/runtime/onboarding/types.ts +404 -0
  345. package/src/runtime/onboarding/verify.ts +171 -0
  346. package/src/runtime/operator-token-cleanup.ts +27 -0
  347. package/src/runtime/perf/panel-contracts.ts +32 -0
  348. package/src/runtime/perf/panel-health-monitor.ts +18 -0
  349. package/src/runtime/sandbox-public-gaps.ts +358 -0
  350. package/src/runtime/services.ts +670 -0
  351. package/src/runtime/store/domains/domain-read-matrix.ts +15 -0
  352. package/src/runtime/store/domains/index.ts +222 -0
  353. package/src/runtime/store/domains/panels.ts +117 -0
  354. package/src/runtime/store/domains/ui-perf.ts +103 -0
  355. package/src/runtime/store/index.ts +305 -0
  356. package/src/runtime/store/selectors/index.ts +359 -0
  357. package/src/runtime/store/state.ts +145 -0
  358. package/src/runtime/surface-feature-flags.ts +65 -0
  359. package/src/runtime/terminal-output-guard.ts +228 -0
  360. package/src/runtime/ui/index.ts +39 -0
  361. package/src/runtime/ui/model-picker/data-provider.ts +182 -0
  362. package/src/runtime/ui/model-picker/health-enrichment.ts +228 -0
  363. package/src/runtime/ui/model-picker/index.ts +59 -0
  364. package/src/runtime/ui/model-picker/types.ts +149 -0
  365. package/src/runtime/ui/provider-health/data-provider.ts +244 -0
  366. package/src/runtime/ui/provider-health/fallback-visualizer.ts +71 -0
  367. package/src/runtime/ui/provider-health/index.ts +46 -0
  368. package/src/runtime/ui/provider-health/types.ts +146 -0
  369. package/src/runtime/ui-events.ts +1 -0
  370. package/src/runtime/ui-read-model-helpers.ts +1 -0
  371. package/src/runtime/ui-read-models-observability-maintenance.ts +1 -0
  372. package/src/runtime/ui-read-models-observability-options.ts +1 -0
  373. package/src/runtime/ui-read-models-observability-remote.ts +1 -0
  374. package/src/runtime/ui-read-models-observability-security.ts +1 -0
  375. package/src/runtime/ui-read-models-observability-system.ts +1 -0
  376. package/src/runtime/ui-read-models-observability.ts +1 -0
  377. package/src/runtime/ui-read-models.ts +61 -0
  378. package/src/runtime/ui-service-queries.ts +1 -0
  379. package/src/runtime/ui-services.ts +190 -0
  380. package/src/scripts/process-messages.ts +42 -0
  381. package/src/shell/blocking-input.ts +98 -0
  382. package/src/shell/service-settings-sync.ts +273 -0
  383. package/src/shell/ui-openers.ts +352 -0
  384. package/src/tools/index.ts +1 -0
  385. package/src/tools/wrfc-agent-guard.ts +49 -0
  386. package/src/types/grid.ts +48 -0
  387. package/src/types/sql-js.d.ts +15 -0
  388. package/src/utils/clipboard.ts +22 -0
  389. package/src/utils/splash-lines.ts +46 -0
  390. package/src/utils/terminal-width.ts +185 -0
  391. package/src/verification/live-verifier.ts +430 -0
  392. package/src/verification/verification-ledger.ts +242 -0
  393. package/src/version.ts +17 -0
  394. package/src/widget/index.ts +2 -0
  395. package/src/widget/types.ts +9 -0
  396. package/src/widget/widget.ts +8 -0
  397. package/src/work-plans/work-plan-store.ts +374 -0
  398. package/tsconfig.json +18 -0
@@ -0,0 +1,491 @@
1
+ import type { Line } from '../types/grid.ts';
2
+ import { createEmptyLine, createStyledCell } from '../types/grid.ts';
3
+ import { BasePanel } from './base-panel.ts';
4
+ import type { PanelCategory } from './types.ts';
5
+ import type { ComponentHealthMonitor } from '../runtime/perf/panel-health-monitor.ts';
6
+ import {
7
+ buildEmptyState,
8
+ buildPanelWorkspace,
9
+ buildSearchInputLine,
10
+ DEFAULT_PANEL_PALETTE,
11
+ resolveScrollablePanelSection,
12
+ type PanelPalette,
13
+ } from './polish.ts';
14
+ import { GLYPHS } from '../renderer/ui-primitives.ts';
15
+ import {
16
+ isPanelSearchBackspace,
17
+ isPanelSearchCancel,
18
+ isPanelSearchPrintable,
19
+ } from './search-focus.ts';
20
+
21
+ // ---------------------------------------------------------------------------
22
+ // ScrollableListPanel<T>
23
+ // ---------------------------------------------------------------------------
24
+
25
+ /**
26
+ * Base class for all list-based panels that require scroll/cursor navigation.
27
+ *
28
+ * Subclasses implement:
29
+ * - `getItems()` — the ordered list of items to display
30
+ * - `renderItem(item, index, selected, width)` — one `Line` per item
31
+ *
32
+ * Optionally override:
33
+ * - `getEmptyStateMessage()` / `getEmptyStateActions()` — empty-state copy
34
+ * - `onSelect(item)` — called when the user presses Enter
35
+ * - `onAction(item, action)` — for secondary key bindings
36
+ * - `getPalette()` — colour palette (defaults to `DEFAULT_PANEL_PALETTE`)
37
+ * - `getPageSize()` — rows per page-up/page-down (default 10)
38
+ *
39
+ * `renderList()` produces the full `Line[]` output that a trivial panel's
40
+ * `render()` can return directly:
41
+ *
42
+ * ```ts
43
+ * render(width: number, height: number): Line[] {
44
+ * return this.renderList(width, height, { header: this.buildHeader(width) });
45
+ * }
46
+ * ```
47
+ */
48
+ export abstract class ScrollableListPanel<T> extends BasePanel {
49
+ protected selectedIndex = 0;
50
+ /** Tracks the first visible row index; kept in sync with resolveScrollablePanelSection. */
51
+ protected scrollStart = 0;
52
+ /**
53
+ * When true, prepends a 2-column `▸ ` gutter on the selected row.
54
+ * Unselected rows get ` ` (two spaces) to maintain alignment.
55
+ * Opt-in; default false to avoid breaking existing panel layouts.
56
+ */
57
+ protected showSelectionGutter = false;
58
+
59
+ constructor(
60
+ id: string,
61
+ name: string,
62
+ icon: string,
63
+ category: PanelCategory,
64
+ componentHealthMonitor?: ComponentHealthMonitor,
65
+ ) {
66
+ super(id, name, icon, category, componentHealthMonitor);
67
+ }
68
+
69
+ // -------------------------------------------------------------------------
70
+ // Abstract — subclasses must implement
71
+ // -------------------------------------------------------------------------
72
+
73
+ /** Return the full ordered list of items to display. */
74
+ protected abstract getItems(): readonly T[];
75
+
76
+ /** Render a single item as one terminal `Line`. */
77
+ protected abstract renderItem(
78
+ item: T,
79
+ index: number,
80
+ selected: boolean,
81
+ width: number,
82
+ ): Line;
83
+
84
+ // -------------------------------------------------------------------------
85
+ // Optional overrides
86
+ // -------------------------------------------------------------------------
87
+
88
+ /** Short label shown in the empty-state title. */
89
+ protected getEmptyStateMessage(): string {
90
+ return 'No items';
91
+ }
92
+
93
+ /** Suggested actions shown in the empty state. */
94
+ protected getEmptyStateActions(): Array<{ command: string; summary: string }> {
95
+ return [];
96
+ }
97
+
98
+ /** Called when the user presses Enter on the selected item. */
99
+ protected onSelect(_item: T): void {}
100
+
101
+ /** Called for secondary key bindings (e.g. 'd' for delete). */
102
+ protected onAction(_item: T, _action: string): void {}
103
+
104
+ /** Colour palette used by `renderList()`. */
105
+ protected getPalette(): PanelPalette {
106
+ return DEFAULT_PANEL_PALETTE;
107
+ }
108
+
109
+ /**
110
+ * Rows to jump on pageup / pagedown.
111
+ * Override in `render()` to pass the actual visible row count:
112
+ *
113
+ * ```ts
114
+ * this._pageSize = Math.max(1, visibleRows - 2);
115
+ * ```
116
+ */
117
+ protected getPageSize(): number {
118
+ return 10;
119
+ }
120
+
121
+ // -------------------------------------------------------------------------
122
+ // Navigation — consistent across ALL panels
123
+ // -------------------------------------------------------------------------
124
+
125
+ /**
126
+ * Handle keyboard input for list navigation.
127
+ *
128
+ * **Auto-clearError contract**: At the top of this method, `lastError` is cleared if
129
+ * non-null. This means any transient error set via `setError()` is dismissed on the
130
+ * very next keystroke the user presses. Subclasses that override `handleInput()` should
131
+ * either:
132
+ * 1. Call `super.handleInput(key)` as a fallback (preferred), which will clear the
133
+ * error when navigation keys are pressed, or
134
+ * 2. Manually call `this.clearError()` at the top of their override to maintain
135
+ * the same contract for their handled keys.
136
+ *
137
+ * Returns `true` if the key was consumed, `false` to let the panel manager try another
138
+ * handler.
139
+ */
140
+ handleInput(key: string): boolean {
141
+ // I2: auto-clear transient errors on the next keystroke so stale errors don't linger.
142
+ // Subclasses that override handleInput should call super.handleInput(key) OR manually
143
+ // call this.clearError() at the start of their handler.
144
+ if (this.lastError !== null) this.clearError();
145
+
146
+ const items = this.getItems();
147
+ const total = items.length;
148
+
149
+ switch (key) {
150
+ case 'up':
151
+ case 'k':
152
+ if (total === 0) return false;
153
+ this.selectedIndex = Math.max(0, this.selectedIndex - 1);
154
+ this.needsRender = true;
155
+ return true;
156
+
157
+ case 'down':
158
+ case 'j':
159
+ if (total === 0) return false;
160
+ this.selectedIndex = Math.min(total - 1, this.selectedIndex + 1);
161
+ this.needsRender = true;
162
+ return true;
163
+
164
+ case 'pageup':
165
+ if (total === 0) return false;
166
+ this.selectedIndex = Math.max(0, this.selectedIndex - this.getPageSize());
167
+ this.needsRender = true;
168
+ return true;
169
+
170
+ case 'pagedown':
171
+ if (total === 0) return false;
172
+ this.selectedIndex = Math.min(total - 1, this.selectedIndex + this.getPageSize());
173
+ this.needsRender = true;
174
+ return true;
175
+
176
+ case 'home':
177
+ case 'g':
178
+ if (total === 0) return false;
179
+ this.selectedIndex = 0;
180
+ this.needsRender = true;
181
+ return true;
182
+
183
+ case 'end':
184
+ case 'G':
185
+ if (total === 0) return false;
186
+ this.selectedIndex = total - 1;
187
+ this.needsRender = true;
188
+ return true;
189
+
190
+ case 'return':
191
+ case 'enter': {
192
+ if (total === 0) return false;
193
+ const item = items[this.selectedIndex];
194
+ if (item !== undefined) this.onSelect(item);
195
+ return true;
196
+ }
197
+
198
+ default:
199
+ return false;
200
+ }
201
+ }
202
+
203
+ handleScroll(deltaRows: number): boolean {
204
+ if (this.lastError !== null) this.clearError();
205
+ const total = this.getItems().length;
206
+ const rows = Math.trunc(deltaRows);
207
+ if (total === 0 || rows === 0) return false;
208
+ const next = Math.max(0, Math.min(total - 1, this.selectedIndex + rows));
209
+ if (next === this.selectedIndex) return false;
210
+ this.selectedIndex = next;
211
+ this.needsRender = true;
212
+ return true;
213
+ }
214
+
215
+ // -------------------------------------------------------------------------
216
+ // Scroll state helpers
217
+ // -------------------------------------------------------------------------
218
+
219
+ /**
220
+ * Clamp `selectedIndex` to the current item count.
221
+ * Must be called after any data refresh that may shrink the list.
222
+ */
223
+ protected clampSelection(): void {
224
+ const total = this.getItems().length;
225
+ if (total === 0) {
226
+ this.selectedIndex = 0;
227
+ } else {
228
+ this.selectedIndex = Math.min(this.selectedIndex, total - 1);
229
+ }
230
+ }
231
+
232
+ // -------------------------------------------------------------------------
233
+ // Render helper — the main convenience entry point
234
+ // -------------------------------------------------------------------------
235
+
236
+ /**
237
+ * Render the full panel including optional header/footer and an empty state.
238
+ *
239
+ * Uses `resolveScrollablePanelSection` + `buildPanelWorkspace` internally,
240
+ * keeping `scrollStart` in sync after each call.
241
+ *
242
+ * @param width Panel width in columns.
243
+ * @param height Panel height in rows.
244
+ * @param options.header Lines prepended as the first workspace section.
245
+ * @param options.footer Lines appended as the last workspace section.
246
+ * @param options.emptyMessage Override for the empty-state title text.
247
+ * @param options.title Workspace title (defaults to `this.name`).
248
+ * @param options.spinnerFrame Animation frame for the loading spinner.
249
+ */
250
+ protected renderList(
251
+ width: number,
252
+ height: number,
253
+ options: {
254
+ readonly header?: readonly Line[];
255
+ readonly footer?: readonly Line[];
256
+ readonly emptyMessage?: string;
257
+ readonly title?: string;
258
+ readonly spinnerFrame?: number;
259
+ } = {},
260
+ ): Line[] {
261
+ this.needsRender = false;
262
+ const palette = this.getPalette();
263
+ const items = this.getItems();
264
+ const title = options.title ?? this.name;
265
+
266
+ // I2: inject error line into footer when present
267
+ const errorLine = this.renderErrorLine(width);
268
+ const baseFooter = options.footer ? [...options.footer as Line[]] : [];
269
+ const effectiveFooter: Line[] = errorLine ? [errorLine, ...baseFooter] : baseFooter;
270
+
271
+ // I3: if loading, show spinner in place of normal content
272
+ const spinnerLine = this.renderLoadingLine(width, options.spinnerFrame ?? 0);
273
+ if (spinnerLine) {
274
+ const loadingSection = { lines: [spinnerLine] };
275
+ const headerSection = options.header ? [{ lines: options.header as Line[] }] : [];
276
+ const lines = buildPanelWorkspace(width, height, {
277
+ title,
278
+ sections: [...headerSection, loadingSection],
279
+ palette,
280
+ });
281
+ while (lines.length < height) lines.push(createEmptyLine(width));
282
+ return lines.slice(0, height);
283
+ }
284
+
285
+ // Build all item lines (pre-render for resolveScrollablePanelSection)
286
+ const scrollableLines: Line[] = items.map((item, index) =>
287
+ this.renderItem(item, index, index === this.selectedIndex, width),
288
+ );
289
+
290
+ // I5: prepend selection gutter when opted in
291
+ if (this.showSelectionGutter) {
292
+ const infoColor = this.getPalette().info ?? DEFAULT_PANEL_PALETTE.info;
293
+ const dimColor = this.getPalette().dim;
294
+ for (let i = 0; i < scrollableLines.length; i++) {
295
+ const line = scrollableLines[i]!;
296
+ const isSelected = i === this.selectedIndex;
297
+ // Shift all cells right by 2, drop the last 2 to preserve width
298
+ const shifted = line.slice(0, width - 2);
299
+ const gutterChar = isSelected ? GLYPHS.navigation.selected : ' ';
300
+ const gutterFg = isSelected ? infoColor : dimColor;
301
+ const g0 = createStyledCell(gutterChar, { fg: gutterFg, bold: isSelected });
302
+ const g1 = createStyledCell(' ', { fg: gutterFg });
303
+ scrollableLines[i] = [g0, g1, ...shifted] as Line;
304
+ }
305
+ }
306
+
307
+ // Empty state
308
+ if (scrollableLines.length === 0) {
309
+ const emptyLines = buildEmptyState(
310
+ width,
311
+ options.emptyMessage ?? this.getEmptyStateMessage(),
312
+ '',
313
+ this.getEmptyStateActions(),
314
+ palette,
315
+ );
316
+ const lines = buildPanelWorkspace(width, height, {
317
+ title,
318
+ sections: [
319
+ ...(options.header ? [{ lines: options.header as Line[] }] : []),
320
+ { lines: emptyLines },
321
+ ...(effectiveFooter.length > 0 ? [{ lines: effectiveFooter }] : []),
322
+ ],
323
+ palette,
324
+ });
325
+ while (lines.length < height) lines.push(createEmptyLine(width));
326
+ return lines.slice(0, height);
327
+ }
328
+
329
+ // Resolve scrollable section (updates scrollStart)
330
+ const beforeSections = options.header ? [{ lines: options.header as Line[] }] : [];
331
+ const afterSections = effectiveFooter.length > 0 ? [{ lines: effectiveFooter }] : [];
332
+
333
+ const resolved = resolveScrollablePanelSection(width, height, {
334
+ palette,
335
+ beforeSections,
336
+ afterSections,
337
+ section: {
338
+ scrollableLines,
339
+ selectedIndex: this.selectedIndex,
340
+ scrollOffset: this.scrollStart,
341
+ guardRows: 1,
342
+ appendWindowSummary: scrollableLines.length > 5 ? { dimColor: palette.dim } : undefined,
343
+ },
344
+ });
345
+ this.scrollStart = resolved.scrollOffset;
346
+
347
+ const sections = [
348
+ ...beforeSections,
349
+ resolved.section,
350
+ ...afterSections,
351
+ ];
352
+
353
+ const lines = buildPanelWorkspace(width, height, {
354
+ title,
355
+ sections,
356
+ palette,
357
+ });
358
+ while (lines.length < height) lines.push(createEmptyLine(width));
359
+ return lines.slice(0, height);
360
+ }
361
+ }
362
+
363
+ // ---------------------------------------------------------------------------
364
+ // SearchableListPanel<T>
365
+ // ---------------------------------------------------------------------------
366
+
367
+ /**
368
+ * Extends `ScrollableListPanel<T>` with inline search/filter support.
369
+ *
370
+ * Subclasses implement:
371
+ * - `getAllItems()` — the full (unfiltered) item list
372
+ * - `matchesSearch(item, query)` — case-insensitive filter predicate
373
+ *
374
+ * `getItems()` is implemented here and returns filtered results. Do NOT
375
+ * override `getItems()` in subclasses — override `getAllItems()` instead.
376
+ *
377
+ * Search state:
378
+ * - Printable characters append to `searchQuery`.
379
+ * - Backspace/Delete removes the last character.
380
+ * - Escape clears the query.
381
+ * - Navigation keys (up/down/etc.) are forwarded to the parent.
382
+ *
383
+ * Render the search input line by calling `buildSearchInput(width)` from
384
+ * your panel's header builder.
385
+ */
386
+ export abstract class SearchableListPanel<T> extends ScrollableListPanel<T> {
387
+ protected searchQuery = '';
388
+
389
+ private _filteredItems: readonly T[] = [];
390
+ private _filterDirty = true;
391
+
392
+ // -------------------------------------------------------------------------
393
+ // Abstract — subclasses must implement
394
+ // -------------------------------------------------------------------------
395
+
396
+ /** Return the full unfiltered item list. */
397
+ protected abstract getAllItems(): readonly T[];
398
+
399
+ /** Return true if `item` matches the search `query`. */
400
+ protected abstract matchesSearch(item: T, query: string): boolean;
401
+
402
+ // -------------------------------------------------------------------------
403
+ // getItems — returns filtered list (do NOT override in subclasses)
404
+ // -------------------------------------------------------------------------
405
+
406
+ protected getItems(): readonly T[] {
407
+ if (this._filterDirty) {
408
+ const all = this.getAllItems();
409
+ this._filteredItems = this.searchQuery
410
+ ? all.filter((item) => this.matchesSearch(item, this.searchQuery))
411
+ : all;
412
+ this._filterDirty = false;
413
+ // Clamp after filter to keep selection in bounds
414
+ this.clampSelection();
415
+ }
416
+ return this._filteredItems;
417
+ }
418
+
419
+ /**
420
+ * Mark the filter cache as stale.
421
+ * Call this whenever `getAllItems()` returns new data.
422
+ */
423
+ protected invalidateFilter(): void {
424
+ this._filterDirty = true;
425
+ this.needsRender = true;
426
+ }
427
+
428
+ // -------------------------------------------------------------------------
429
+ // Input — search first, navigation second
430
+ // -------------------------------------------------------------------------
431
+
432
+ handleInput(key: string): boolean {
433
+ // Backspace: trim query
434
+ if (isPanelSearchBackspace(key)) {
435
+ if (this.searchQuery.length > 0) {
436
+ this.searchQuery = this.searchQuery.slice(0, -1);
437
+ this._filterDirty = true;
438
+ this.needsRender = true;
439
+ return true;
440
+ }
441
+ return false;
442
+ }
443
+
444
+ // Escape: clear query
445
+ if (isPanelSearchCancel(key)) {
446
+ if (this.searchQuery.length > 0) {
447
+ this.searchQuery = '';
448
+ this._filterDirty = true;
449
+ this.needsRender = true;
450
+ return true;
451
+ }
452
+ return false;
453
+ }
454
+
455
+ // Printable characters: append to query
456
+ if (isPanelSearchPrintable(key)) {
457
+ this.searchQuery += key;
458
+ this._filterDirty = true;
459
+ this.needsRender = true;
460
+ return true;
461
+ }
462
+
463
+ // Navigation and Enter: delegate to parent
464
+ return super.handleInput(key);
465
+ }
466
+
467
+ /**
468
+ * Build the filter input `Line` for use in a panel header section.
469
+ *
470
+ * Renders the filter label and current query with context-sensitive formatting:
471
+ *
472
+ * - **Focused** (`focused = true`): `[Filter] query_` — active, bold, cursor visible
473
+ * - **Unfocused** (`focused = false`): `Filter: query` — dim, no cursor
474
+ *
475
+ * @param width Panel width in columns.
476
+ * @param label Label text (default: `'Filter'`).
477
+ * @param focused Whether the filter input is currently active.
478
+ */
479
+ protected buildFilterInputLine(width: number, label = 'Filter', focused: boolean): Line {
480
+ const palette = this.getPalette();
481
+ const formattedLabel = focused ? `[${label}] ` : `${label}: `;
482
+ const value = focused ? `${this.searchQuery}_` : this.searchQuery;
483
+ // Pass active:false when focused to prevent buildSearchInputLine from converting the
484
+ // trailing '_' cursor to the block-glyph (GLYPHS.surface.cursor). The focused visual
485
+ // affordance is provided by the '[Label] ' bracket format and explicit inputBg/info colors.
486
+ const opts = focused
487
+ ? { active: false, bg: palette.inputBg, valueColor: palette.info }
488
+ : { active: false };
489
+ return buildSearchInputLine(width, formattedLabel, value, palette, opts);
490
+ }
491
+ }
@@ -0,0 +1,32 @@
1
+ export type PanelSearchFocusTransition = 'focus-search' | 'focus-list' | null;
2
+
3
+ export function getPanelSearchFocusTransition(
4
+ key: string,
5
+ options: { selectedIndex: number; itemCount: number; focusKeys?: ReadonlyArray<string> },
6
+ ): PanelSearchFocusTransition {
7
+ const focusKeys = options.focusKeys ?? ['/'];
8
+ if (focusKeys.includes(key)) return 'focus-search';
9
+ if ((key === 'up' || key === 'ArrowUp') && options.selectedIndex <= 0) {
10
+ return 'focus-search';
11
+ }
12
+ if ((key === 'down' || key === 'ArrowDown') && options.itemCount > 0) {
13
+ return 'focus-list';
14
+ }
15
+ return null;
16
+ }
17
+
18
+ export function isPanelSearchBackspace(key: string): boolean {
19
+ return key === 'backspace' || key === 'delete' || key === 'Backspace' || key === 'Delete';
20
+ }
21
+
22
+ export function isPanelSearchCancel(key: string): boolean {
23
+ return key === 'escape' || key === 'Escape';
24
+ }
25
+
26
+ export function isPanelSearchCommit(key: string): boolean {
27
+ return key === 'return' || key === 'enter' || key === 'Enter';
28
+ }
29
+
30
+ export function isPanelSearchPrintable(key: string): boolean {
31
+ return key.length === 1 && key >= ' ';
32
+ }