@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,297 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
2
+ import { dirname, join } from 'path';
3
+ import { logger } from '@pellux/goodvibes-sdk/platform/utils';
4
+ import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
5
+ import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
6
+
7
+ /**
8
+ * InputHistory — Persisted command history with arrow-key navigation.
9
+ *
10
+ * Navigation model:
11
+ * position = -1 : not browsing (at the live draft)
12
+ * position = 0 : most recent entry
13
+ * position = N-1 : oldest visible entry
14
+ *
15
+ * Up arrow goes to older entries (position increases).
16
+ * Down arrow goes to newer entries (position decreases), then back to draft.
17
+ */
18
+ export interface HistorySearchMatch {
19
+ entry: string;
20
+ matchStart: number;
21
+ matchLength: number;
22
+ }
23
+
24
+ export class HistorySearch {
25
+ active = false;
26
+ query = '';
27
+ matches: HistorySearchMatch[] = [];
28
+ matchIndex = 0;
29
+ savedDraft = '';
30
+
31
+ constructor(private getEntries: () => readonly string[]) {}
32
+
33
+ open(draft: string): void {
34
+ this.active = true;
35
+ this.savedDraft = draft;
36
+ this.query = '';
37
+ this.matches = [];
38
+ this.matchIndex = 0;
39
+ }
40
+
41
+ search(query: string): void {
42
+ this.query = query;
43
+ const q = query.toLowerCase();
44
+ this.matches = [];
45
+ if (!q) return;
46
+ for (const entry of this.getEntries()) {
47
+ const idx = entry.toLowerCase().indexOf(q);
48
+ if (idx >= 0) {
49
+ this.matches.push({ entry, matchStart: idx, matchLength: q.length });
50
+ }
51
+ }
52
+ this.matchIndex = 0;
53
+ }
54
+
55
+ appendChar(ch: string): void {
56
+ this.search(this.query + ch);
57
+ }
58
+
59
+ deleteChar(): void {
60
+ if (this.query.length > 0) {
61
+ this.search(this.query.slice(0, -1));
62
+ }
63
+ }
64
+
65
+ /** Move to next older match (higher index). */
66
+ stepOlder(): void {
67
+ if (this.matches.length > 0 && this.matchIndex < this.matches.length - 1) {
68
+ this.matchIndex++;
69
+ }
70
+ }
71
+
72
+ /** Move to next newer match (lower index). */
73
+ stepNewer(): void {
74
+ if (this.matchIndex > 0) {
75
+ this.matchIndex--;
76
+ }
77
+ }
78
+
79
+ get currentMatch(): HistorySearchMatch | null {
80
+ return this.matches[this.matchIndex] ?? null;
81
+ }
82
+
83
+ accept(): string {
84
+ const match = this.currentMatch;
85
+ this.close();
86
+ return match?.entry ?? '';
87
+ }
88
+
89
+ cancel(): string {
90
+ const draft = this.savedDraft;
91
+ this.close();
92
+ return draft;
93
+ }
94
+
95
+ private close(): void {
96
+ this.active = false;
97
+ this.query = '';
98
+ this.matches = [];
99
+ this.matchIndex = 0;
100
+ this.savedDraft = '';
101
+ }
102
+ }
103
+
104
+ export interface InputHistoryOptions {
105
+ readonly historyPath?: string;
106
+ readonly userRoot?: string;
107
+ readonly homeDirectory?: string;
108
+ readonly persist?: boolean;
109
+ }
110
+
111
+ type StoredInputHistoryEntry = string | {
112
+ readonly text: string;
113
+ readonly recallText?: string;
114
+ };
115
+
116
+ function resolveHistoryPath(options?: InputHistoryOptions): string {
117
+ if (options?.historyPath) {
118
+ return options.historyPath;
119
+ }
120
+ const userRoot = options?.userRoot ?? options?.homeDirectory;
121
+ if (!userRoot) {
122
+ throw new Error('InputHistory requires historyPath or an explicit userRoot/homeDirectory.');
123
+ }
124
+ return join(userRoot, '.goodvibes', GOODVIBES_AGENT_SURFACE_ROOT, 'input-history.json');
125
+ }
126
+
127
+ export class InputHistory {
128
+ private entries: StoredInputHistoryEntry[] = [];
129
+ private position = -1; // -1 = not browsing
130
+ private draft = ''; // Saves current input when entering history
131
+ private maxEntries = 500;
132
+ private historyPath: string;
133
+ private persist: boolean;
134
+
135
+ constructor(options: InputHistoryOptions) {
136
+ this.persist = options.persist ?? true;
137
+ this.historyPath = resolveHistoryPath(options);
138
+ if (this.persist) {
139
+ this.load();
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Add a new entry. Called on submit.
145
+ * - Ignores empty/whitespace-only strings.
146
+ * - Deduplicates consecutive identical entries.
147
+ * - Resets browsing position.
148
+ */
149
+ add(text: string, options: { readonly recallText?: string } = {}): void {
150
+ const trimmed = text.trim();
151
+ if (!trimmed) return;
152
+ const recallText = options.recallText?.trim();
153
+ const entry: StoredInputHistoryEntry = recallText && recallText !== trimmed
154
+ ? { text: trimmed, recallText }
155
+ : trimmed;
156
+
157
+ // Dedup: skip if same as most recent entry
158
+ if (this.entries.length > 0 && this.sameEntry(this.entries[0]!, entry)) {
159
+ this.resetPosition();
160
+ return;
161
+ }
162
+
163
+ this.entries.unshift(entry);
164
+ if (this.entries.length > this.maxEntries) {
165
+ this.entries.length = this.maxEntries;
166
+ }
167
+
168
+ this.resetPosition();
169
+
170
+ if (this.persist) {
171
+ this.save();
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Navigate up (older entry).
177
+ * On first call, saves currentInput as draft.
178
+ * Returns the entry to display, or null if at boundary.
179
+ */
180
+ up(currentInput: string): string | null {
181
+ if (this.entries.length === 0) return null;
182
+
183
+ // Save draft on first navigation
184
+ if (this.position === -1) {
185
+ this.draft = currentInput;
186
+ }
187
+
188
+ const next = this.position + 1;
189
+ if (next < this.entries.length) {
190
+ this.position = next;
191
+ return this.getRecallText(this.entries[this.position]!);
192
+ }
193
+
194
+ // At oldest boundary
195
+ return null;
196
+ }
197
+
198
+ /**
199
+ * Navigate down (newer entry).
200
+ * Returns newer entry, or draft when reaching the end.
201
+ * Returns null if not currently browsing.
202
+ */
203
+ down(): string | null {
204
+ if (this.position === -1) return null;
205
+
206
+ const prev = this.position - 1;
207
+ if (prev >= 0) {
208
+ this.position = prev;
209
+ return this.getRecallText(this.entries[this.position]!);
210
+ }
211
+
212
+ // Back to draft
213
+ this.position = -1;
214
+ return this.draft;
215
+ }
216
+
217
+ /**
218
+ * Reset browsing position. Called when user types new text.
219
+ */
220
+ resetPosition(): void {
221
+ this.position = -1;
222
+ this.draft = '';
223
+ }
224
+
225
+ /**
226
+ * Whether currently browsing history.
227
+ */
228
+ get isBrowsing(): boolean {
229
+ return this.position !== -1;
230
+ }
231
+
232
+ /**
233
+ * Return entries as readonly for use by HistorySearch.
234
+ */
235
+ getEntries(): readonly string[] {
236
+ return this.entries.map((entry) => this.getRecallText(entry));
237
+ }
238
+
239
+ /**
240
+ * Save history to disk.
241
+ */
242
+ save(): void {
243
+ try {
244
+ mkdirSync(dirname(this.historyPath), { recursive: true });
245
+ writeFileSync(this.historyPath, JSON.stringify(this.entries), 'utf-8');
246
+ } catch (err) {
247
+ logger.debug('InputHistory save failed (non-fatal)', { error: summarizeError(err) });
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Load history from disk.
253
+ */
254
+ load(): void {
255
+ try {
256
+ if (existsSync(this.historyPath)) {
257
+ const raw = readFileSync(this.historyPath, 'utf-8');
258
+ const parsed = JSON.parse(raw) as unknown;
259
+ if (Array.isArray(parsed)) {
260
+ this.entries = (parsed as unknown[])
261
+ .map((entry) => this.normalizeStoredEntry(entry))
262
+ .filter((entry): entry is StoredInputHistoryEntry => entry !== null)
263
+ .slice(0, this.maxEntries);
264
+ }
265
+ }
266
+ } catch (err) {
267
+ logger.debug('InputHistory load failed (non-fatal, using empty history)', { error: summarizeError(err) });
268
+ this.entries = [];
269
+ }
270
+ }
271
+
272
+ private getDisplayText(entry: StoredInputHistoryEntry): string {
273
+ return typeof entry === 'string' ? entry : entry.text;
274
+ }
275
+
276
+ private getRecallText(entry: StoredInputHistoryEntry): string {
277
+ return typeof entry === 'string' ? entry : entry.recallText ?? entry.text;
278
+ }
279
+
280
+ private sameEntry(a: StoredInputHistoryEntry, b: StoredInputHistoryEntry): boolean {
281
+ return this.getDisplayText(a) === this.getDisplayText(b)
282
+ && this.getRecallText(a) === this.getRecallText(b);
283
+ }
284
+
285
+ private normalizeStoredEntry(entry: unknown): StoredInputHistoryEntry | null {
286
+ if (typeof entry === 'string') return entry;
287
+ if (!entry || typeof entry !== 'object') return null;
288
+ const record = entry as Record<string, unknown>;
289
+ if (typeof record.text !== 'string') return null;
290
+ const text = record.text.trim();
291
+ if (!text) return null;
292
+ if (typeof record.recallText === 'string' && record.recallText.trim() && record.recallText.trim() !== text) {
293
+ return { text, recallText: record.recallText.trim() };
294
+ }
295
+ return text;
296
+ }
297
+ }
@@ -0,0 +1,292 @@
1
+ /**
2
+ * KeybindingsManager — loads and merges keyboard shortcut configuration.
3
+ *
4
+ * Default bindings are hardcoded here. Users can override any binding by
5
+ * creating ~/.goodvibes/agent/keybindings.json.
6
+ *
7
+ * Config file format example:
8
+ * {
9
+ * "search": { "key": "g", "ctrl": true },
10
+ * "block-copy": { "key": "c", "ctrl": true, "alt": true }
11
+ * }
12
+ *
13
+ * Each value is a KeyCombo or an array of KeyCombos for multi-binding support.
14
+ */
15
+
16
+ import { readFileSync, existsSync } from 'node:fs';
17
+ import { join } from 'node:path';
18
+ import { logger } from '@pellux/goodvibes-sdk/platform/utils';
19
+ import { resolveSurfaceDirectory } from '@/runtime/index.ts';
20
+ import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
21
+ import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
22
+
23
+ /** Identifies a specific key press with modifiers. */
24
+ export interface KeyCombo {
25
+ /** Logical key name (single char like 'f', or named key like 'r', 'z', 'f2', etc.) */
26
+ key: string;
27
+ ctrl?: boolean;
28
+ shift?: boolean;
29
+ alt?: boolean;
30
+ }
31
+
32
+ /** All bindable action identifiers. */
33
+ export type KeyAction =
34
+ | 'copy-selection'
35
+ | 'clear-cancel'
36
+ | 'screen-clear'
37
+ | 'panel-picker'
38
+ | 'panel-close'
39
+ | 'panel-close-all'
40
+ | 'panel-tab-next'
41
+ | 'panel-tab-prev'
42
+ | 'history-search'
43
+ | 'search'
44
+ | 'block-copy'
45
+ | 'bookmark'
46
+ | 'block-save'
47
+ | 'delete-word'
48
+ | 'apply-diff-line-start'
49
+ | 'next-error-line-end'
50
+ | 'kill-line'
51
+ | 'clear-prompt'
52
+ | 'undo'
53
+ | 'redo'
54
+ | 'paste'
55
+ | 'replay-panel';
56
+
57
+ /** Human-readable description for each action (used in /keybindings display). */
58
+ export const ACTION_DESCRIPTIONS: Record<KeyAction, string> = {
59
+ 'copy-selection': 'Copy selected text to clipboard',
60
+ 'clear-cancel': 'Clear input / cancel generation / exit (double)',
61
+ 'screen-clear': 'Repaint the screen',
62
+ 'panel-picker': 'Open, focus, or hide the panel workspace',
63
+ 'panel-close': 'Close the currently active panel',
64
+ 'panel-close-all': 'Close all open panels',
65
+ 'panel-tab-next': 'Next workspace panel tab',
66
+ 'panel-tab-prev': 'Previous workspace panel tab',
67
+ 'history-search': 'Reverse input history search',
68
+ 'search': 'Toggle conversation search',
69
+ 'block-copy': 'Copy nearest block to clipboard',
70
+ 'bookmark': 'Bookmark / unbookmark nearest block',
71
+ 'block-save': 'Save nearest block to file',
72
+ 'delete-word': 'Delete word backward',
73
+ 'apply-diff-line-start': 'Apply nearest diff / move to line start',
74
+ 'next-error-line-end': 'Navigate to next error / move to line end',
75
+ 'kill-line': 'Kill to end of line',
76
+ 'clear-prompt': 'Clear the prompt',
77
+ 'undo': 'Undo last prompt edit',
78
+ 'redo': 'Redo last undone edit',
79
+ 'paste': 'Paste from clipboard (image priority)',
80
+ 'replay-panel': 'Open / close the Replay panel',
81
+ };
82
+
83
+ /** Default key bindings for all actions. */
84
+ export const DEFAULT_KEYBINDINGS: Record<KeyAction, KeyCombo[]> = {
85
+ 'copy-selection': [{ key: 'c', ctrl: true, shift: true }],
86
+ 'clear-cancel': [{ key: 'c', ctrl: true }],
87
+ 'screen-clear': [{ key: 'l', ctrl: true }],
88
+ 'panel-picker': [{ key: 'p', ctrl: true }],
89
+ 'panel-close': [{ key: 'x', ctrl: true }],
90
+ 'panel-close-all': [{ key: 'x', ctrl: true, shift: true }],
91
+ 'panel-tab-next': [{ key: ']', ctrl: true }],
92
+ 'panel-tab-prev': [{ key: '[', ctrl: true }],
93
+ 'history-search': [{ key: 'r', ctrl: true }],
94
+ 'search': [{ key: 'f', ctrl: true }],
95
+ 'block-copy': [{ key: 'y', ctrl: true }],
96
+ 'bookmark': [{ key: 'b', ctrl: true }],
97
+ 'block-save': [{ key: 's', ctrl: true }],
98
+ 'delete-word': [{ key: 'w', ctrl: true }],
99
+ 'apply-diff-line-start': [{ key: 'a', ctrl: true }],
100
+ 'next-error-line-end': [{ key: 'e', ctrl: true }],
101
+ 'kill-line': [{ key: 'k', ctrl: true }],
102
+ 'clear-prompt': [{ key: 'u', ctrl: true }],
103
+ 'undo': [{ key: 'z', ctrl: true }],
104
+ 'redo': [{ key: 'z', ctrl: true, shift: true }],
105
+ 'paste': [{ key: 'v', ctrl: true }],
106
+ 'replay-panel': [{ key: 'r', ctrl: true, shift: true }],
107
+ };
108
+
109
+ /** Resolved overrides type: each key can be a single combo or array. */
110
+ type KeybindingsFile = Partial<Record<KeyAction, KeyCombo | KeyCombo[]>>;
111
+
112
+ export interface KeybindingsManagerOptions {
113
+ readonly configPath?: string;
114
+ readonly userRoot?: string;
115
+ readonly homeDirectory?: string;
116
+ readonly surfaceRoot?: string;
117
+ }
118
+
119
+ function resolveKeybindingsPath(options?: KeybindingsManagerOptions): string {
120
+ if (options?.configPath) {
121
+ return options.configPath;
122
+ }
123
+ const userRoot = options?.userRoot ?? options?.homeDirectory;
124
+ if (!userRoot) {
125
+ throw new Error('KeybindingsManager requires configPath or an explicit userRoot/homeDirectory.');
126
+ }
127
+ if (options?.surfaceRoot) {
128
+ return resolveSurfaceDirectory(userRoot, options.surfaceRoot, 'keybindings.json');
129
+ }
130
+ return join(userRoot, '.goodvibes', GOODVIBES_AGENT_SURFACE_ROOT, 'keybindings.json');
131
+ }
132
+
133
+ /**
134
+ * KeybindingsManager — owns the resolved keybinding table.
135
+ *
136
+ * Call loadFromDisk() once at startup (in main.ts) to merge user config.
137
+ * Then use matches() anywhere a key token is being evaluated.
138
+ */
139
+ export class KeybindingsManager {
140
+ private bindings: Record<KeyAction, KeyCombo[]>;
141
+ private configPath: string;
142
+ /** Inverted lookup map: composite key → KeyAction. Built in buildLookupMap(). */
143
+ private lookupMap = new Map<string, KeyAction>();
144
+
145
+ constructor(options: KeybindingsManagerOptions) {
146
+ this.configPath = resolveKeybindingsPath(options);
147
+ // Start with deep copy of defaults
148
+ this.bindings = this.cloneDefaults();
149
+ this.buildLookupMap();
150
+ }
151
+
152
+ private cloneDefaults(): Record<KeyAction, KeyCombo[]> {
153
+ const result = {} as Record<KeyAction, KeyCombo[]>;
154
+ for (const [action, combos] of Object.entries(DEFAULT_KEYBINDINGS) as [KeyAction, KeyCombo[]][]) {
155
+ result[action] = combos.map(c => ({ ...c }));
156
+ }
157
+ return result;
158
+ }
159
+
160
+ /**
161
+ * Load user overrides from disk and merge into the binding table.
162
+ * Unknown actions are ignored with a debug log. Malformed entries are skipped.
163
+ * Safe to call multiple times (reloads on each call).
164
+ */
165
+ loadFromDisk(): void {
166
+ if (!existsSync(this.configPath)) return;
167
+ try {
168
+ const raw = readFileSync(this.configPath, 'utf-8');
169
+ const parsed = JSON.parse(raw) as KeybindingsFile;
170
+ const validActions = new Set(Object.keys(DEFAULT_KEYBINDINGS) as KeyAction[]);
171
+
172
+ // Reset to defaults before applying overrides
173
+ this.bindings = this.cloneDefaults();
174
+
175
+ for (const [action, combo] of Object.entries(parsed)) {
176
+ if (!validActions.has(action as KeyAction)) {
177
+ logger.debug('keybindings: unknown action, skipping', { action });
178
+ continue;
179
+ }
180
+ const normalized = Array.isArray(combo) ? combo : [combo];
181
+ if (!this.validateCombos(normalized)) {
182
+ logger.debug('keybindings: invalid combo for action, skipping', { action, combo });
183
+ continue;
184
+ }
185
+ this.bindings[action as KeyAction] = normalized;
186
+ }
187
+ logger.debug('keybindings: loaded overrides from disk', { path: this.configPath });
188
+ } catch (err) {
189
+ logger.debug('keybindings: failed to load config file', { path: this.configPath, err: summarizeError(err) });
190
+ }
191
+ this.buildLookupMap();
192
+ }
193
+
194
+ /**
195
+ * buildLookupMap — Rebuild the inverted lookup map from the current bindings table.
196
+ * Called after constructor init and after loadFromDisk().
197
+ * Map key format: "logicalName:ctrl:shift:alt" (booleans as 0/1).
198
+ * Last writer wins for duplicate combos (deterministic: iterate actions in order).
199
+ */
200
+ private buildLookupMap(): void {
201
+ this.lookupMap.clear();
202
+ for (const [action, combos] of Object.entries(this.bindings) as [KeyAction, KeyCombo[]][]) {
203
+ for (const combo of combos) {
204
+ const key = `${combo.key}:${combo.ctrl ? 1 : 0}:${combo.shift ? 1 : 0}:${combo.alt ? 1 : 0}`;
205
+ this.lookupMap.set(key, action);
206
+ }
207
+ }
208
+ }
209
+
210
+ /**
211
+ * lookup — O(1) keybinding lookup by token.
212
+ * Returns the matching KeyAction, or null if no binding matches.
213
+ */
214
+ lookup(token: { logicalName?: string; ctrl?: boolean; shift?: boolean; alt?: boolean }): KeyAction | null {
215
+ if (!token.logicalName) return null;
216
+ const key = `${token.logicalName}:${token.ctrl ? 1 : 0}:${token.shift ? 1 : 0}:${token.alt ? 1 : 0}`;
217
+ return this.lookupMap.get(key) ?? null;
218
+ }
219
+
220
+ private validateCombos(combos: unknown[]): combos is KeyCombo[] {
221
+ return combos.every((c) => {
222
+ if (typeof c !== 'object' || c === null) return false;
223
+ const combo = c as Record<string, unknown>;
224
+ return typeof combo['key'] === 'string' && combo['key'].length > 0;
225
+ });
226
+ }
227
+
228
+ /**
229
+ * matches — Check whether a keyboard token matches the given action.
230
+ *
231
+ * @param action The action to test.
232
+ * @param token The parsed keyboard token from InputTokenizer.
233
+ * Expects: { logicalName: string; ctrl?: boolean; shift?: boolean; alt?: boolean }
234
+ */
235
+ matches(
236
+ action: KeyAction,
237
+ token: { logicalName?: string; ctrl?: boolean; shift?: boolean; alt?: boolean },
238
+ ): boolean {
239
+ const combos = this.bindings[action];
240
+ if (!combos) return false;
241
+ return combos.some((combo) => this.comboMatches(combo, token));
242
+ }
243
+
244
+ private comboMatches(
245
+ combo: KeyCombo,
246
+ token: { logicalName?: string; ctrl?: boolean; shift?: boolean; alt?: boolean },
247
+ ): boolean {
248
+ if (token.logicalName !== combo.key) return false;
249
+ if (!!combo.ctrl !== !!token.ctrl) return false;
250
+ if (!!combo.shift !== !!token.shift) return false;
251
+ if (!!combo.alt !== !!token.alt) return false;
252
+ return true;
253
+ }
254
+
255
+ /**
256
+ * getAll — Return the full resolved binding table for display purposes.
257
+ */
258
+ getAll(): Array<{ action: KeyAction; combos: KeyCombo[]; description: string }> {
259
+ return (Object.keys(this.bindings) as KeyAction[]).map((action) => ({
260
+ action,
261
+ combos: this.bindings[action],
262
+ description: ACTION_DESCRIPTIONS[action],
263
+ }));
264
+ }
265
+
266
+ /**
267
+ * getComboLabel — Return a human-readable label for the first combo of an action.
268
+ * Example: { key: 'f', ctrl: true } → "Ctrl+F"
269
+ */
270
+ getComboLabel(action: KeyAction): string {
271
+ const combos = this.bindings[action];
272
+ if (!combos?.length) return '(unbound)';
273
+ return this.formatCombo(combos[0]);
274
+ }
275
+
276
+ /**
277
+ * formatCombo — Format a KeyCombo as a human-readable string.
278
+ */
279
+ formatCombo(combo: KeyCombo): string {
280
+ const parts: string[] = [];
281
+ if (combo.ctrl) parts.push('Ctrl');
282
+ if (combo.alt) parts.push('Alt');
283
+ if (combo.shift) parts.push('Shift');
284
+ parts.push(combo.key.length === 1 ? combo.key.toUpperCase() : combo.key);
285
+ return parts.join('+');
286
+ }
287
+
288
+ /** Return the config file path. */
289
+ getConfigPath(): string {
290
+ return this.configPath;
291
+ }
292
+ }