@pugi/cli 0.1.0-beta.98 → 1.0.0-alpha.1

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 (448) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +11 -191
  3. package/bin/pugi +8 -0
  4. package/package.json +15 -71
  5. package/postinstall.mjs +31 -0
  6. package/CHANGELOG.md +0 -132
  7. package/THIRD_PARTY_NOTICES.md +0 -40
  8. package/assets/pugi-mascot.ansi +0 -16
  9. package/assets/pugi-prozr2-mascot.ansi +0 -9
  10. package/bin/run.js +0 -34
  11. package/dist/commands/deploy.js +0 -439
  12. package/dist/commands/flatten.js +0 -191
  13. package/dist/commands/jobs-watch.js +0 -201
  14. package/dist/commands/jobs.js +0 -260
  15. package/dist/commands/retro.js +0 -210
  16. package/dist/commands/smoke.js +0 -133
  17. package/dist/core/agent-progress/cleanup.js +0 -134
  18. package/dist/core/agent-progress/schema.js +0 -144
  19. package/dist/core/agent-progress/writer.js +0 -101
  20. package/dist/core/agents/adaptive-router.js +0 -330
  21. package/dist/core/agents/loader.js +0 -104
  22. package/dist/core/agents/query-decomposer.js +0 -297
  23. package/dist/core/agents/registry.js +0 -69
  24. package/dist/core/approvals/shortcut-resolver.js +0 -98
  25. package/dist/core/artifact-chain/dispatcher.js +0 -148
  26. package/dist/core/artifact-chain/exporter.js +0 -164
  27. package/dist/core/artifact-chain/state.js +0 -243
  28. package/dist/core/artifact-chain/steps.js +0 -169
  29. package/dist/core/ask-user/question.js +0 -92
  30. package/dist/core/audit/audit-trail.js +0 -275
  31. package/dist/core/auth/ensure-authenticated.js +0 -129
  32. package/dist/core/auth/env-provider.js +0 -238
  33. package/dist/core/auto-open-browser.js +0 -128
  34. package/dist/core/auto-update/channels.js +0 -122
  35. package/dist/core/auto-update/checker.js +0 -241
  36. package/dist/core/auto-update/state.js +0 -235
  37. package/dist/core/bare-mode/index.js +0 -107
  38. package/dist/core/bash/redirect.js +0 -281
  39. package/dist/core/bash-classifier.js +0 -1397
  40. package/dist/core/checkpoint/resumer.js +0 -149
  41. package/dist/core/checkpoint/rewinder.js +0 -291
  42. package/dist/core/checkpoints/shadow-git.js +0 -670
  43. package/dist/core/citations/parser.js +0 -109
  44. package/dist/core/classifier/yolo-classifier.js +0 -88
  45. package/dist/core/clipboard.js +0 -70
  46. package/dist/core/codegraph/decision-store.js +0 -248
  47. package/dist/core/codegraph/detect-repo.js +0 -459
  48. package/dist/core/codegraph/install.js +0 -134
  49. package/dist/core/codegraph/offer-hook.js +0 -220
  50. package/dist/core/compact/auto-trigger.js +0 -96
  51. package/dist/core/compact/buffer-rewriter.js +0 -115
  52. package/dist/core/compact/summarizer.js +0 -208
  53. package/dist/core/compact/token-counter.js +0 -108
  54. package/dist/core/consensus/anvil-fanout.js +0 -276
  55. package/dist/core/consensus/diff-capture.js +0 -491
  56. package/dist/core/consensus/rubric.js +0 -233
  57. package/dist/core/context/builder.js +0 -114
  58. package/dist/core/context/compaction-events.js +0 -99
  59. package/dist/core/context/compaction.js +0 -602
  60. package/dist/core/context/index.js +0 -28
  61. package/dist/core/context/invariants.js +0 -250
  62. package/dist/core/context/markdown-loader.js +0 -288
  63. package/dist/core/context/markdown-traverse.js +0 -255
  64. package/dist/core/context/pugiignore.js +0 -316
  65. package/dist/core/context/repo-skeleton.js +0 -533
  66. package/dist/core/context/tool-eviction.js +0 -55
  67. package/dist/core/context/watcher.js +0 -342
  68. package/dist/core/context/working-set.js +0 -165
  69. package/dist/core/coordinator/agent-tools.js +0 -77
  70. package/dist/core/coordinator/agent-toolset.js +0 -65
  71. package/dist/core/coordinator/fsm.js +0 -73
  72. package/dist/core/coordinator/mode-fsm.js +0 -70
  73. package/dist/core/cost/rate-card.js +0 -129
  74. package/dist/core/cost/tracker.js +0 -221
  75. package/dist/core/credentials.js +0 -355
  76. package/dist/core/cron/scheduler.js +0 -138
  77. package/dist/core/denial-tracking/index.js +0 -8
  78. package/dist/core/denial-tracking/state.js +0 -264
  79. package/dist/core/diagnostics/probe-runner.js +0 -93
  80. package/dist/core/diagnostics/probes/api.js +0 -46
  81. package/dist/core/diagnostics/probes/auth.js +0 -93
  82. package/dist/core/diagnostics/probes/bare-mode.js +0 -42
  83. package/dist/core/diagnostics/probes/cli-version.js +0 -127
  84. package/dist/core/diagnostics/probes/config.js +0 -72
  85. package/dist/core/diagnostics/probes/denial-tracking.js +0 -57
  86. package/dist/core/diagnostics/probes/disk.js +0 -81
  87. package/dist/core/diagnostics/probes/engine-live.js +0 -46
  88. package/dist/core/diagnostics/probes/git.js +0 -65
  89. package/dist/core/diagnostics/probes/hooks.js +0 -118
  90. package/dist/core/diagnostics/probes/mcp.js +0 -75
  91. package/dist/core/diagnostics/probes/node.js +0 -59
  92. package/dist/core/diagnostics/probes/pnpm.js +0 -36
  93. package/dist/core/diagnostics/probes/pugi-md.js +0 -89
  94. package/dist/core/diagnostics/probes/sandbox.js +0 -72
  95. package/dist/core/diagnostics/probes/session.js +0 -74
  96. package/dist/core/diagnostics/probes/status-snapshot.js +0 -488
  97. package/dist/core/diagnostics/probes/workspace.js +0 -63
  98. package/dist/core/diagnostics/types.js +0 -70
  99. package/dist/core/dispatch/cache-cleanup.js +0 -197
  100. package/dist/core/dispatch/cache-handoff.js +0 -295
  101. package/dist/core/edits/apply-patch-layer-e.js +0 -189
  102. package/dist/core/edits/dispatch.js +0 -511
  103. package/dist/core/edits/format-detector.js +0 -260
  104. package/dist/core/edits/format-matrix.js +0 -26
  105. package/dist/core/edits/fuzzy-ladder.js +0 -650
  106. package/dist/core/edits/index.js +0 -19
  107. package/dist/core/edits/journal.js +0 -199
  108. package/dist/core/edits/layer-a-apply.js +0 -217
  109. package/dist/core/edits/layer-a-fuzzy-apply.js +0 -198
  110. package/dist/core/edits/layer-b-apply.js +0 -211
  111. package/dist/core/edits/layer-c-apply.js +0 -160
  112. package/dist/core/edits/layer-d-ast.js +0 -572
  113. package/dist/core/edits/marker-parser.js +0 -401
  114. package/dist/core/edits/security-gate.js +0 -223
  115. package/dist/core/edits/verify-hook.js +0 -273
  116. package/dist/core/edits/worktree.js +0 -322
  117. package/dist/core/engine/adapter-runner.js +0 -8
  118. package/dist/core/engine/anvil-client.js +0 -344
  119. package/dist/core/engine/auto-compact.js +0 -179
  120. package/dist/core/engine/budgets.js +0 -192
  121. package/dist/core/engine/context-prefix.js +0 -155
  122. package/dist/core/engine/index.js +0 -12
  123. package/dist/core/engine/intensity.js +0 -163
  124. package/dist/core/engine/intent.js +0 -260
  125. package/dist/core/engine/native-pugi.js +0 -1616
  126. package/dist/core/engine/noop.js +0 -27
  127. package/dist/core/engine/prompts.js +0 -236
  128. package/dist/core/engine/strip-internal-fields.js +0 -124
  129. package/dist/core/engine/tool-bridge.js +0 -2173
  130. package/dist/core/engine/verification-patterns.js +0 -195
  131. package/dist/core/evaluation/golden-dataset.js +0 -293
  132. package/dist/core/feedback/queue.js +0 -177
  133. package/dist/core/feedback/submitter.js +0 -145
  134. package/dist/core/file-cache.js +0 -141
  135. package/dist/core/flatten/flatten-repo.js +0 -439
  136. package/dist/core/format/osc8-link.js +0 -28
  137. package/dist/core/hook-chains.js +0 -392
  138. package/dist/core/hooks/citation-verify-hook.js +0 -138
  139. package/dist/core/hooks/citation-verify.js +0 -112
  140. package/dist/core/hooks/events.js +0 -46
  141. package/dist/core/hooks/index.js +0 -15
  142. package/dist/core/hooks/registry.js +0 -216
  143. package/dist/core/hooks/runner.js +0 -236
  144. package/dist/core/hooks/v2/event-emitter.js +0 -115
  145. package/dist/core/hooks/v2/executor.js +0 -282
  146. package/dist/core/hooks/v2/index.js +0 -25
  147. package/dist/core/hooks/v2/lifecycle.js +0 -104
  148. package/dist/core/hooks/v2/loader.js +0 -216
  149. package/dist/core/hooks/v2/matcher.js +0 -125
  150. package/dist/core/hooks/v2/trust.js +0 -143
  151. package/dist/core/hooks/v2/types.js +0 -86
  152. package/dist/core/hooks/worktree-events.js +0 -158
  153. package/dist/core/hooks.js +0 -415
  154. package/dist/core/image/renderer.js +0 -71
  155. package/dist/core/index-store.js +0 -260
  156. package/dist/core/init/detector.js +0 -582
  157. package/dist/core/init/template-renderer.js +0 -242
  158. package/dist/core/jobs/registry.js +0 -462
  159. package/dist/core/ledger/results-tsv.js +0 -142
  160. package/dist/core/log-discipline/stdout-redirect.js +0 -51
  161. package/dist/core/lsp/cache.js +0 -105
  162. package/dist/core/lsp/client.js +0 -1229
  163. package/dist/core/lsp/language-detect.js +0 -66
  164. package/dist/core/lsp/post-edit-diagnostics.js +0 -171
  165. package/dist/core/lsp/server-detect.js +0 -173
  166. package/dist/core/lsp/symbol-cache.js +0 -162
  167. package/dist/core/lsp/symbol-tools.js +0 -664
  168. package/dist/core/mcp/client.js +0 -385
  169. package/dist/core/mcp/http-server.js +0 -553
  170. package/dist/core/mcp/orchestrator-config.js +0 -192
  171. package/dist/core/mcp/orchestrator-tools.js +0 -806
  172. package/dist/core/mcp/permission.js +0 -190
  173. package/dist/core/mcp/registry.js +0 -193
  174. package/dist/core/mcp/server-tools.js +0 -219
  175. package/dist/core/mcp/server.js +0 -397
  176. package/dist/core/mcp/trust.js +0 -91
  177. package/dist/core/memory/dual-write.js +0 -416
  178. package/dist/core/memory/passive-extract.js +0 -130
  179. package/dist/core/memory/phase1-kinds.js +0 -20
  180. package/dist/core/memory/secret-scanner.js +0 -304
  181. package/dist/core/memory-sync/queue.js +0 -170
  182. package/dist/core/metrics/extract.js +0 -113
  183. package/dist/core/modes/roo-modes.js +0 -68
  184. package/dist/core/onboarding/ensure-initialized.js +0 -133
  185. package/dist/core/onboarding/marker.js +0 -111
  186. package/dist/core/onboarding/telemetry-state.js +0 -108
  187. package/dist/core/output-style/presets.js +0 -176
  188. package/dist/core/output-style/state.js +0 -185
  189. package/dist/core/path-security.js +0 -345
  190. package/dist/core/permission.js +0 -369
  191. package/dist/core/permissions/auto-classifier.js +0 -124
  192. package/dist/core/permissions/bash-parser.js +0 -371
  193. package/dist/core/permissions/circuit-breaker.js +0 -83
  194. package/dist/core/permissions/constrained-edit.js +0 -91
  195. package/dist/core/permissions/gate.js +0 -278
  196. package/dist/core/permissions/index.js +0 -20
  197. package/dist/core/permissions/mode.js +0 -174
  198. package/dist/core/permissions/network-egress.js +0 -137
  199. package/dist/core/permissions/state.js +0 -241
  200. package/dist/core/permissions/tool-class.js +0 -107
  201. package/dist/core/plan-mode/ui-state.js +0 -51
  202. package/dist/core/plans/plan-artifact.js +0 -721
  203. package/dist/core/policy-limits/etag-store.js +0 -122
  204. package/dist/core/prd-check/parser.js +0 -215
  205. package/dist/core/prd-check/reporter.js +0 -127
  206. package/dist/core/prd-check/session-review.js +0 -557
  207. package/dist/core/prd-check/verifiers.js +0 -223
  208. package/dist/core/prompt-cache/client-cache.js +0 -99
  209. package/dist/core/prompts/assembly.js +0 -29
  210. package/dist/core/prompts/registry.js +0 -364
  211. package/dist/core/pugi-gitignore.js +0 -52
  212. package/dist/core/pugi-md/cc-compat-rules.js +0 -735
  213. package/dist/core/pugi-md/context-injector.js +0 -76
  214. package/dist/core/pugi-md/walk-up.js +0 -207
  215. package/dist/core/python/uv-installer.js +0 -270
  216. package/dist/core/python/uv-resolver.js +0 -83
  217. package/dist/core/rate-limit/narrator.js +0 -146
  218. package/dist/core/recipes/cli-types.js +0 -20
  219. package/dist/core/recipes/loader.js +0 -103
  220. package/dist/core/recipes/runner.js +0 -345
  221. package/dist/core/recipes/schema.js +0 -587
  222. package/dist/core/release-notes/parser.js +0 -241
  223. package/dist/core/release-notes/state.js +0 -116
  224. package/dist/core/repl/ask.js +0 -512
  225. package/dist/core/repl/cancellation.js +0 -98
  226. package/dist/core/repl/cap-warning.js +0 -91
  227. package/dist/core/repl/clipboard-read.js +0 -174
  228. package/dist/core/repl/dispatch-fsm.js +0 -220
  229. package/dist/core/repl/engine-bridge.js +0 -303
  230. package/dist/core/repl/history-search.js +0 -175
  231. package/dist/core/repl/history.js +0 -182
  232. package/dist/core/repl/kill-ring.js +0 -138
  233. package/dist/core/repl/model-pricing.js +0 -135
  234. package/dist/core/repl/privacy-banner.js +0 -71
  235. package/dist/core/repl/session.js +0 -4962
  236. package/dist/core/repl/slash-commands.js +0 -747
  237. package/dist/core/repl/store/index.js +0 -12
  238. package/dist/core/repl/store/jsonl-log.js +0 -321
  239. package/dist/core/repl/store/lockfile.js +0 -155
  240. package/dist/core/repl/store/session-store.js +0 -821
  241. package/dist/core/repl/store/types.js +0 -44
  242. package/dist/core/repl/store/uuid-v7.js +0 -68
  243. package/dist/core/repl/tool-route.js +0 -382
  244. package/dist/core/repl/workspace-context.js +0 -206
  245. package/dist/core/repo-map/build.js +0 -125
  246. package/dist/core/repo-map/cache.js +0 -185
  247. package/dist/core/repo-map/extractor.js +0 -254
  248. package/dist/core/repo-map/formatter.js +0 -145
  249. package/dist/core/repo-map/page-rank.js +0 -105
  250. package/dist/core/repo-map/scanner.js +0 -211
  251. package/dist/core/retro/git-collector.js +0 -251
  252. package/dist/core/retro/health-card.js +0 -25
  253. package/dist/core/retro/metrics.js +0 -342
  254. package/dist/core/retro/narrative.js +0 -249
  255. package/dist/core/retro/plane-collector.js +0 -274
  256. package/dist/core/retro/pr-issue-link.js +0 -65
  257. package/dist/core/retro/types.js +0 -16
  258. package/dist/core/retry-budget/budget.js +0 -284
  259. package/dist/core/retry-budget/index.js +0 -5
  260. package/dist/core/retry-budget/retry-cap.js +0 -74
  261. package/dist/core/routing/lead-worker.js +0 -43
  262. package/dist/core/routing/pre-flight-estimator.js +0 -108
  263. package/dist/core/runs/run-tree.js +0 -103
  264. package/dist/core/sandboxing/adapter.js +0 -29
  265. package/dist/core/sandboxing/index.js +0 -49
  266. package/dist/core/sandboxing/none.js +0 -19
  267. package/dist/core/sandboxing/seatbelt.js +0 -183
  268. package/dist/core/security/injection-scanner.js +0 -367
  269. package/dist/core/security/output-filter.js +0 -418
  270. package/dist/core/session/env-file.js +0 -105
  271. package/dist/core/session/section-budgets.js +0 -140
  272. package/dist/core/session.js +0 -377
  273. package/dist/core/settings.js +0 -400
  274. package/dist/core/share/formatter.js +0 -271
  275. package/dist/core/share/redactor.js +0 -221
  276. package/dist/core/share/uploader.js +0 -267
  277. package/dist/core/skills/defaults.js +0 -457
  278. package/dist/core/skills/loader.js +0 -454
  279. package/dist/core/skills/sources.js +0 -480
  280. package/dist/core/skills/trust.js +0 -172
  281. package/dist/core/smoke/headless-driver.js +0 -174
  282. package/dist/core/smoke/orchestrator.js +0 -194
  283. package/dist/core/smoke/runner.js +0 -238
  284. package/dist/core/smoke/scenario-parser.js +0 -316
  285. package/dist/core/statusline.js +0 -99
  286. package/dist/core/subagents/dispatcher-real.js +0 -600
  287. package/dist/core/subagents/dispatcher.js +0 -352
  288. package/dist/core/subagents/index.js +0 -39
  289. package/dist/core/subagents/isolation-matrix.js +0 -213
  290. package/dist/core/subagents/spawn.js +0 -101
  291. package/dist/core/telemetry/emitter.js +0 -229
  292. package/dist/core/telemetry/queue.js +0 -251
  293. package/dist/core/theme/context.js +0 -91
  294. package/dist/core/theme/presets.js +0 -228
  295. package/dist/core/theme/state.js +0 -181
  296. package/dist/core/todos/invariant.js +0 -10
  297. package/dist/core/todos/state.js +0 -177
  298. package/dist/core/tool-schema/compressor.js +0 -89
  299. package/dist/core/transport/version-interceptor.js +0 -166
  300. package/dist/core/trust.js +0 -109
  301. package/dist/core/tui/thinking-block.js +0 -64
  302. package/dist/core/vim/keymap.js +0 -288
  303. package/dist/core/vim/state.js +0 -92
  304. package/dist/core/watch-markers/marker-watcher.js +0 -133
  305. package/dist/core/worktree/include-parser.js +0 -249
  306. package/dist/core/worktree-manager/cleanup.js +0 -123
  307. package/dist/core/worktree-manager/manager.js +0 -303
  308. package/dist/index.js +0 -44
  309. package/dist/runtime/bootstrap.js +0 -190
  310. package/dist/runtime/cli.js +0 -8121
  311. package/dist/runtime/commands/agents.js +0 -385
  312. package/dist/runtime/commands/budget.js +0 -192
  313. package/dist/runtime/commands/cancel.js +0 -231
  314. package/dist/runtime/commands/chain.js +0 -489
  315. package/dist/runtime/commands/codegraph-status.js +0 -227
  316. package/dist/runtime/commands/compact.js +0 -297
  317. package/dist/runtime/commands/config.js +0 -595
  318. package/dist/runtime/commands/cost.js +0 -199
  319. package/dist/runtime/commands/delegate.js +0 -312
  320. package/dist/runtime/commands/dispatch.js +0 -126
  321. package/dist/runtime/commands/doctor.js +0 -579
  322. package/dist/runtime/commands/feedback.js +0 -184
  323. package/dist/runtime/commands/hooks.js +0 -187
  324. package/dist/runtime/commands/init.js +0 -254
  325. package/dist/runtime/commands/lsp.js +0 -368
  326. package/dist/runtime/commands/mcp.js +0 -935
  327. package/dist/runtime/commands/memory.js +0 -582
  328. package/dist/runtime/commands/model.js +0 -237
  329. package/dist/runtime/commands/onboarding.js +0 -275
  330. package/dist/runtime/commands/patch.js +0 -128
  331. package/dist/runtime/commands/permissions.js +0 -112
  332. package/dist/runtime/commands/plan.js +0 -143
  333. package/dist/runtime/commands/prd-check.js +0 -285
  334. package/dist/runtime/commands/privacy.js +0 -107
  335. package/dist/runtime/commands/recipe.js +0 -325
  336. package/dist/runtime/commands/redo-blob-store.js +0 -92
  337. package/dist/runtime/commands/redo.js +0 -361
  338. package/dist/runtime/commands/release-notes.js +0 -229
  339. package/dist/runtime/commands/repo-map.js +0 -95
  340. package/dist/runtime/commands/report.js +0 -299
  341. package/dist/runtime/commands/resume.js +0 -118
  342. package/dist/runtime/commands/review-consensus.js +0 -414
  343. package/dist/runtime/commands/rewind.js +0 -333
  344. package/dist/runtime/commands/roster.js +0 -117
  345. package/dist/runtime/commands/sessions.js +0 -163
  346. package/dist/runtime/commands/share.js +0 -316
  347. package/dist/runtime/commands/skills.js +0 -401
  348. package/dist/runtime/commands/status.js +0 -186
  349. package/dist/runtime/commands/stickers.js +0 -82
  350. package/dist/runtime/commands/style.js +0 -194
  351. package/dist/runtime/commands/theme.js +0 -196
  352. package/dist/runtime/commands/undo.js +0 -361
  353. package/dist/runtime/commands/update.js +0 -289
  354. package/dist/runtime/commands/vim.js +0 -140
  355. package/dist/runtime/commands/worktree.js +0 -177
  356. package/dist/runtime/commands/worktrees.js +0 -155
  357. package/dist/runtime/deprecation-warning.js +0 -69
  358. package/dist/runtime/engine-exit-code.js +0 -50
  359. package/dist/runtime/headless-repl.js +0 -195
  360. package/dist/runtime/headless.js +0 -548
  361. package/dist/runtime/load-hooks-or-exit.js +0 -71
  362. package/dist/runtime/plan-decompose.js +0 -531
  363. package/dist/runtime/sigint-guard.js +0 -272
  364. package/dist/runtime/stream-renderer.js +0 -195
  365. package/dist/runtime/update-check.js +0 -294
  366. package/dist/runtime/version.js +0 -65
  367. package/dist/runtime/worktree-bootstrap.js +0 -579
  368. package/dist/skills/bundled/batch.js +0 -617
  369. package/dist/skills/bundled/index.js +0 -45
  370. package/dist/skills/bundled/loop.js +0 -358
  371. package/dist/skills/bundled/remember.js +0 -383
  372. package/dist/skills/bundled/simplify.js +0 -289
  373. package/dist/skills/bundled/skillify.js +0 -373
  374. package/dist/skills/bundled/stuck.js +0 -558
  375. package/dist/skills/bundled/verify.js +0 -439
  376. package/dist/testing/vcr.js +0 -486
  377. package/dist/tools/agent-tool.js +0 -229
  378. package/dist/tools/apply-patch.js +0 -556
  379. package/dist/tools/ask-user-question.js +0 -337
  380. package/dist/tools/ask-user.js +0 -115
  381. package/dist/tools/bash.js +0 -1238
  382. package/dist/tools/brief.js +0 -224
  383. package/dist/tools/cron.js +0 -433
  384. package/dist/tools/enter-worktree.js +0 -250
  385. package/dist/tools/exit-worktree.js +0 -147
  386. package/dist/tools/file-tools.js +0 -553
  387. package/dist/tools/http-request.js +0 -336
  388. package/dist/tools/lsp-tools.js +0 -565
  389. package/dist/tools/mcp-tool.js +0 -260
  390. package/dist/tools/multi-edit.js +0 -361
  391. package/dist/tools/powershell.js +0 -268
  392. package/dist/tools/registry.js +0 -166
  393. package/dist/tools/server-tools.js +0 -892
  394. package/dist/tools/skill-tool.js +0 -96
  395. package/dist/tools/sleep.js +0 -99
  396. package/dist/tools/synthetic-output.js +0 -133
  397. package/dist/tools/tasks.js +0 -208
  398. package/dist/tools/todo-write.js +0 -184
  399. package/dist/tools/verify-plan-execution.js +0 -295
  400. package/dist/tools/web-fetch-injection-scanner.js +0 -207
  401. package/dist/tools/web-fetch.js +0 -720
  402. package/dist/tools/web-search.js +0 -458
  403. package/dist/tui/agent-progress-card.js +0 -111
  404. package/dist/tui/agent-tree-pane.js +0 -9
  405. package/dist/tui/agent-tree.js +0 -87
  406. package/dist/tui/ask-cli.js +0 -52
  407. package/dist/tui/ask-modal.js +0 -211
  408. package/dist/tui/ask-user-question-chips.js +0 -315
  409. package/dist/tui/ask-user-question-prompt.js +0 -203
  410. package/dist/tui/compact-banner.js +0 -81
  411. package/dist/tui/conversation-pane.js +0 -164
  412. package/dist/tui/cost-table.js +0 -111
  413. package/dist/tui/device-flow.js +0 -142
  414. package/dist/tui/doctor-table.js +0 -46
  415. package/dist/tui/feedback-prompt.js +0 -156
  416. package/dist/tui/input-box.js +0 -732
  417. package/dist/tui/login-picker.js +0 -69
  418. package/dist/tui/markdown-render.js +0 -266
  419. package/dist/tui/multi-file-diff-approval.js +0 -375
  420. package/dist/tui/onboarding-wizard.js +0 -240
  421. package/dist/tui/permissions-picker.js +0 -86
  422. package/dist/tui/render.js +0 -160
  423. package/dist/tui/repl-render.js +0 -770
  424. package/dist/tui/repl-splash-art.js +0 -64
  425. package/dist/tui/repl-splash-mascot.js +0 -154
  426. package/dist/tui/repl-splash.js +0 -117
  427. package/dist/tui/repl.js +0 -378
  428. package/dist/tui/slash-palette.js +0 -106
  429. package/dist/tui/splash-data.js +0 -61
  430. package/dist/tui/splash.js +0 -31
  431. package/dist/tui/status-bar.js +0 -209
  432. package/dist/tui/status-table.js +0 -7
  433. package/dist/tui/stickers-art.js +0 -136
  434. package/dist/tui/style-table.js +0 -28
  435. package/dist/tui/theme-table.js +0 -29
  436. package/dist/tui/thinking-spinner.js +0 -123
  437. package/dist/tui/tool-stream-pane.js +0 -140
  438. package/dist/tui/update-banner.js +0 -33
  439. package/dist/tui/vim-input.js +0 -267
  440. package/dist/tui/welcome-banner.js +0 -107
  441. package/dist/tui/welcome-data.js +0 -293
  442. package/dist/tui/workspace-context.js +0 -105
  443. package/docs/examples/codegraph.mcp.json +0 -10
  444. package/test/scenarios/codegen-create-file.scenario.txt +0 -13
  445. package/test/scenarios/compact-force.scenario.txt +0 -12
  446. package/test/scenarios/identity.scenario.txt +0 -11
  447. package/test/scenarios/persona-handoff.scenario.txt +0 -12
  448. package/test/scenarios/walkback.scenario.txt +0 -12
@@ -1,732 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /**
3
- * REPL input box - Sprint (REPL UX P0 wave 1).
4
- *
5
- * Bordered, cursor-aware input box matching the upstream tool / peer CLI
6
- * aesthetics. Layered upgrade over :
7
- *
8
- * - Top divider (full-width cyan rule) anchors the input below the
9
- * main pane and gives the operator a clear visual seam.
10
- * - Rounded cyan border wraps the prompt + line + cursor.
11
- * - Cursor follows the caret position (left/right arrows + Home/End
12
- * reposition without losing the trailing text).
13
- * - Single-arrow `›` prompt in brand cyan; dim continuation prompt
14
- * `┊` when the input wraps past the available width so the operator
15
- * sees that they are still inside one logical line.
16
- * - History navigation (↑/↓), slash palette inline suggestion, Esc
17
- * cancel, Ctrl+C ×2 exit remain wired identically to .
18
- *
19
- * State that belongs in this component:
20
- * - `line` (string) - current buffer
21
- * - `cursor` (number) - caret offset into `line`
22
- * - `history` (string[]) - session-local; persistence lands in
23
- * commit 2 (per-workspace JSONL).
24
- * - `historyIndex` (number) - -1 when not navigating
25
- *
26
- * Brand voice gate: no forbidden words. ASCII-only glyphs.
27
- */
28
- import { useEffect, useMemo, useRef, useState } from 'react';
29
- import { Box, Text, useInput, useStdout } from 'ink';
30
- import { append as appendHistory, read as readHistory, } from '../core/repl/history.js';
31
- import { applyQuery, currentBrief, cycle, initialSearchState, } from '../core/repl/history-search.js';
32
- import { SlashPalette, completePalette, filterPalette, } from './slash-palette.js';
33
- import { EMPTY_KILL_RING, killToLineEnd, killToLineStart, killWordBackward, yankAtCursor, } from '../core/repl/kill-ring.js';
34
- import { readClipboard } from '../core/repl/clipboard-read.js';
35
- const CTRL_C_DOUBLE_TAP_MS = 1_000;
36
- /**
37
- * BT 8 (the upstream tool parity): Esc-Esc walks the conversation back
38
- * one turn. 500ms is tight enough that an operator clearing the buffer +
39
- * later changing their mind does NOT accidentally pop a turn, while
40
- * still feeling like one motion. Matches the upstream tool's documented
41
- * double-Esc window.
42
- */
43
- const ESCAPE_DOUBLE_TAP_MS = 500;
44
- /** Width subtracted from the terminal width so the border + padding fit. */
45
- const FRAME_OVERHEAD_COLUMNS = 4;
46
- /** Fallback width when ink cannot read stdout (e.g. test harness). */
47
- const FALLBACK_COLUMNS = 80;
48
- /** Cursor blink interval (ms). 530ms matches the GNOME / iTerm2 default. */
49
- const CURSOR_BLINK_MS = 530;
50
- /**
51
- * Strip ANSI escape sequences, bracketed-paste markers, and C0 control
52
- * bytes (except `\n` and `\t`) from clipboard text before splicing it
53
- * into the buffer. Keystroke input never reaches the input box with
54
- * escapes (ink filters them), so the buffer assumes printable text +
55
- * the two whitespace forms we preserve. Clipboard text bypasses ink's
56
- * filter, so we re-apply the same constraint here to neutralise any
57
- * escape sequence a hostile or accidental copy might carry into the
58
- * operator's terminal.
59
- */
60
- export function sanitiseClipboardText(raw) {
61
- return raw
62
- .replace(/\x1b\[200~/g, '')
63
- .replace(/\x1b\[201~/g, '')
64
- .replace(/\x1b\[[0-9;?]*[A-Za-z]/g, '')
65
- .replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, '')
66
- .replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, '');
67
- }
68
- export function InputBox(props) {
69
- // Seed history from disk on mount so the operator can ↑ to last
70
- // session's brief immediately. Memoised so we read the file once per
71
- // workspace, not on every render.
72
- const seededHistory = useMemo(() => {
73
- if (!props.workspaceSlug)
74
- return [];
75
- const entries = readHistory({
76
- home: props.historyHome,
77
- workspaceSlug: props.workspaceSlug,
78
- });
79
- return entries.map((e) => e.brief);
80
- }, [props.workspaceSlug, props.historyHome]);
81
- const [line, setLine] = useState(props.initial ?? '');
82
- const [cursor, setCursor] = useState(props.initial?.length ?? 0);
83
- const [history, setHistory] = useState(seededHistory);
84
- const [historyIndex, setHistoryIndex] = useState(-1);
85
- const [lastCtrlCAt, setLastCtrlCAt] = useState(undefined);
86
- // CEO P0 #2 : the upstream tool parity — surface а visible
87
- // "Press Ctrl+C again to exit" toast on the first Ctrl+C press so
88
- // the operator knows the second press will terminate the REPL.
89
- // Auto-clears after CTRL_C_DOUBLE_TAP_MS so it never lingers past
90
- // the double-tap window.
91
- const [ctrlCToast, setCtrlCToast] = useState(null);
92
- const ctrlCToastTimerRef = useRef(null);
93
- // BT 8: Esc-Esc walkback double-tap window. Tracks the epoch
94
- // ms of the most recent Esc press so the next Esc within
95
- // ESCAPE_DOUBLE_TAP_MS triggers the walkback handler instead of
96
- // re-clearing the buffer.
97
- const [lastEscapeAt, setLastEscapeAt] = useState(undefined);
98
- const [cursorVisible, setCursorVisible] = useState(true);
99
- // Ctrl+R / Ctrl+S reverse-search mode. Undefined when idle, a
100
- // HistorySearchState while the operator is searching.
101
- const [search, setSearch] = useState(undefined);
102
- // Draft preserved while the operator searches so Esc returns the
103
- // pre-search buffer instead of dropping it on the floor.
104
- const [draftBeforeSearch, setDraftBeforeSearch] = useState('');
105
- // Slash palette state. When the buffer starts with `/` the palette
106
- // renders, ↑/↓ selects a row, Tab autocompletes, Enter runs.
107
- const [paletteIndex, setPaletteIndex] = useState(0);
108
- // Operator-toggled palette suppression. Esc closes the palette
109
- // without clearing the buffer (useful for `/help` text dispatch).
110
- const [paletteSuppressed, setPaletteSuppressed] = useState(false);
111
- // Readline-style kill ring backing Ctrl+U / Ctrl+K / Ctrl+W / Ctrl+Y.
112
- const [killRing, setKillRing] = useState(EMPTY_KILL_RING);
113
- // Soft counter; bumping forces Ink to re-render the surrounding
114
- // panes when Ctrl+L wipes the terminal (the parent React tree is
115
- // otherwise stable and would not redraw on a stdout.write alone).
116
- const [, setRedrawTick] = useState(0);
117
- // Shift+Tab toast — flashed for 2s after a mode cycle so the
118
- // operator sees `Mode → acceptEdits` под the input divider. Cleared
119
- // by a setTimeout so a quick second Shift+Tab refreshes the toast.
120
- const [modeCycleToast, setModeCycleToast] = useState(null);
121
- const modeCycleTimerRef = useRef(null);
122
- const now = props.now ?? Date.now;
123
- const { stdout } = useStdout();
124
- const columns = stdout?.columns ?? FALLBACK_COLUMNS;
125
- const innerWidth = Math.max(20, columns - FRAME_OVERHEAD_COLUMNS);
126
- // Refs mirror the latest committed line + cursor so the async clipboard
127
- // paste handler can splice against current values without re-entering
128
- // React's setState updater (which strict mode invokes twice and would
129
- // double-insert the pasted text). The setState updater pattern used to
130
- // capture latest line/cursor (see Codex P2 below) is correct under
131
- // single-render mode but broken under React 18 strict mode + the
132
- // setLine-inside-setCursor nesting we ended up with. Refs are the
133
- // canonical escape hatch for "use the most recently committed value
134
- // inside a one-shot async callback".
135
- const lineRef = useRef(line);
136
- const cursorRef = useRef(cursor);
137
- useEffect(() => {
138
- lineRef.current = line;
139
- }, [line]);
140
- useEffect(() => {
141
- cursorRef.current = cursor;
142
- }, [cursor]);
143
- // Soft blink so the operator can spot where typing will land. Ink
144
- // re-renders only when state changes, so a 530ms toggle is the cheapest
145
- // honest cursor we can show without a custom raw-mode dance. Tests
146
- // disable the interval so node:test does not see a dangling timer.
147
- const blinkEnabled = props.blinkCursor ?? true;
148
- useEffect(() => {
149
- if (!blinkEnabled)
150
- return undefined;
151
- const interval = setInterval(() => {
152
- setCursorVisible((prev) => !prev);
153
- }, CURSOR_BLINK_MS);
154
- return () => clearInterval(interval);
155
- }, [blinkEnabled]);
156
- useInput((input, key) => {
157
- if (key.ctrl && input === 'c') {
158
- const t = now();
159
- // : the upstream tool-style double-press semantics. First Ctrl+C
160
- // ALWAYS attempts to cancel an in-flight dispatch (when the
161
- // session reports non-idle); second Ctrl+C within 1s exits the
162
- // process. If onCancel is omitted (legacy callers, tests), the
163
- // old behaviour is preserved: first Ctrl+C clears the buffer +
164
- // arms the exit timer, second Ctrl+C exits.
165
- const withinDoubleTapWindow = typeof lastCtrlCAt === 'number' && t - lastCtrlCAt <= CTRL_C_DOUBLE_TAP_MS;
166
- if (withinDoubleTapWindow) {
167
- // Second press inside the window — always exit. This matches
168
- // the upstream tool: even mid-dispatch, the second Ctrl+C wins so
169
- // the operator can always escape a stuck REPL.
170
- props.onExit();
171
- return;
172
- }
173
- // First press in a fresh window. If the host wired a cancel
174
- // surface and there is something to cancel, abort the dispatch.
175
- // The buffer is left untouched on a cancel (the operator's
176
- // current input is NOT trashed by an accidental Ctrl+C while a
177
- // tool is running).
178
- //
179
- // Three-valued onCancel return (see prop docstring):
180
- // - true → dispatch cancelled, keep buffer, arm exit timer
181
- // - false → idle, clear buffer (legacy), arm exit timer
182
- // - undefined → handler bypassed (modal owns input); NO state
183
- // change at all. Buffer stays, exit timer NOT
184
- // armed (otherwise the modal would silently
185
- // promote a Ctrl+C to "press again to exit",
186
- // which is wrong context for a modal cancel).
187
- let cancelResult;
188
- if (props.onCancel) {
189
- cancelResult = props.onCancel();
190
- }
191
- if (cancelResult === undefined && props.onCancel) {
192
- // Bypass path - modal owns the input. Drop the press silently
193
- // so the modal's own cancel surface (Esc / its own Ctrl+C
194
- // binding inside the modal component) takes effect on its own
195
- // terms. P2 fix: previously this fell through to the
196
- // legacy buffer-clear + setLastCtrlCAt path and wiped modal
197
- // draft text on first Ctrl+C.
198
- return;
199
- }
200
- setLastCtrlCAt(t);
201
- // CEO P0 #2 : surface the "Press Ctrl+C again to
202
- // exit" toast on the first press so the operator sees the
203
- // double-tap semantics in the UI, not just в the bottom hint
204
- // line. Mirrors the standard tool's exit affordance verbatim. The
205
- // toast string varies by which branch fired (cancel vs idle
206
- // clear) so the operator learns what the press just did:
207
- //
208
- // - cancelResult === true → "Aborted. Press Ctrl+C again to exit."
209
- // - cancelResult === false → "Press Ctrl+C again to exit."
210
- //
211
- // (The undefined branch already returned above — а modal owns
212
- // input и the toast is suppressed.)
213
- const toastCopy = cancelResult === true
214
- ? 'Aborted. Press Ctrl+C again to exit.'
215
- : 'Press Ctrl+C again to exit.';
216
- setCtrlCToast(toastCopy);
217
- if (ctrlCToastTimerRef.current)
218
- clearTimeout(ctrlCToastTimerRef.current);
219
- ctrlCToastTimerRef.current = setTimeout(() => {
220
- setCtrlCToast(null);
221
- ctrlCToastTimerRef.current = null;
222
- }, CTRL_C_DOUBLE_TAP_MS);
223
- // Legacy behaviour: on idle (or no onCancel wired), clear the
224
- // buffer + reset search so the operator's screen is calm before
225
- // they confirm exit. When we DID cancel a live dispatch, keep
226
- // the buffer so a half-typed brief is not lost.
227
- if (cancelResult !== true) {
228
- setLine('');
229
- setCursor(0);
230
- setSearch(undefined);
231
- }
232
- return;
233
- }
234
- // — the upstream tool parity: Shift+Tab cycles permission mode.
235
- // The host owns the cycle logic + persistence; we just intercept
236
- // the chord and surface a one-line toast on success. Place this
237
- // BEFORE the search-mode and palette branches so a Shift+Tab fires
238
- // even while reverse-search is active (operator habit-driven).
239
- if (key.shift && key.tab && props.onCyclePermissionMode) {
240
- const nextMode = props.onCyclePermissionMode();
241
- if (nextMode) {
242
- setModeCycleToast(`Mode → ${nextMode}`);
243
- if (modeCycleTimerRef.current)
244
- clearTimeout(modeCycleTimerRef.current);
245
- modeCycleTimerRef.current = setTimeout(() => {
246
- setModeCycleToast(null);
247
- modeCycleTimerRef.current = null;
248
- }, 2_000);
249
- }
250
- return;
251
- }
252
- // Search-mode key handling. Ctrl+R / Ctrl+S cycle, Enter accepts,
253
- // Esc cancels (restoring the pre-search draft), backspace shortens
254
- // the query, typed characters extend it.
255
- if (search) {
256
- if (key.ctrl && input === 'r') {
257
- setSearch((s) => (s ? cycle(s, 1) : s));
258
- return;
259
- }
260
- if (key.ctrl && input === 's') {
261
- setSearch((s) => (s ? cycle(s, -1) : s));
262
- return;
263
- }
264
- if (key.escape) {
265
- setSearch(undefined);
266
- setLine(draftBeforeSearch);
267
- setCursor(draftBeforeSearch.length);
268
- return;
269
- }
270
- // Bare LF accepts the focused match, same as CR (`key.return`).
271
- // See the post-search block below for the rationale.
272
- if (key.return || (input === '\n' && !key.meta && !key.ctrl && !key.shift)) {
273
- const picked = currentBrief(search);
274
- setSearch(undefined);
275
- if (picked !== null) {
276
- setLine(picked);
277
- setCursor(picked.length);
278
- }
279
- else {
280
- setLine(draftBeforeSearch);
281
- setCursor(draftBeforeSearch.length);
282
- }
283
- return;
284
- }
285
- if (key.backspace || key.delete) {
286
- const nextQuery = search.query.slice(0, -1);
287
- setSearch(applyQuery(search, nextQuery, history));
288
- return;
289
- }
290
- if (input && !key.meta && !key.ctrl) {
291
- // Drop a bare LF from the search query — the Enter-accept
292
- // branch above already handled it; falling through here would
293
- // splice a newline into the search string.
294
- if (input === '\n')
295
- return;
296
- const nextQuery = search.query + input;
297
- setSearch(applyQuery(search, nextQuery, history));
298
- return;
299
- }
300
- // Any other key inside search mode is ignored - the operator can
301
- // still escape with Esc or cancel via Ctrl+C handled above.
302
- return;
303
- }
304
- if (key.ctrl && input === 'r') {
305
- // Enter reverse-search mode. Stash the current buffer so Esc
306
- // restores it untouched.
307
- setDraftBeforeSearch(line);
308
- setSearch(initialSearchState(history));
309
- return;
310
- }
311
- if (key.ctrl && input === 's') {
312
- // Symmetric forward search entry. Cycle direction differs once
313
- // active; entry seeds the same browse state.
314
- setDraftBeforeSearch(line);
315
- setSearch(initialSearchState(history));
316
- return;
317
- }
318
- // P0 fix (CEO dogfood, second iteration): bare LF (`\n`)
319
- // MUST submit the brief, same as bare CR (`\r`). Ink's parseKeypress
320
- // maps `\r` to `key.return` and `\n` to `key.name === 'enter'`
321
- // WITHOUT setting `key.return`. Most real terminals deliver CR for
322
- // Enter (ICRNL on by default), so the `key.return` branch below
323
- // catches them. But when stdin is a PTY whose parent writes raw
324
- // `\n` (Python's `pty.fork` + `os.write(fd, b"\n")`, automation
325
- // harnesses, certain SSH multiplexers), the LF arrives as a
326
- // printable char.
327
- //
328
- // PR (beta.45) fixed the case where `input === '\n'` exactly.
329
- // CEO PTY smoke surfaced the REAL shape: when the parent
330
- // writes the brief AND the Enter as separate `os.write` calls (or
331
- // even when it doesn't), Node's stdin buffer COALESCES them into
332
- // ONE chunk before Ink delivers the `useInput` event. The repro
333
- // confirmed via stderr instrumentation: typing `hi\n` arrives in
334
- // input-box as `bytes=[68 69 0a] len=3 flags=-` — a SINGLE 3-char
335
- // chunk "hi\n" with no key flags. The PR branch (`input ===
336
- // '\n'`) does not match, so `hi\n` falls through to the printable-
337
- // char branch and the literal newline lands in the buffer as
338
- // `› hi\n █` (multi-line composer, brief never dispatches, status
339
- // stays `idle` forever).
340
- //
341
- // Fix: detect a TRAILING `\n` in a printable chunk with no
342
- // modifiers — type the prefix into the buffer, then submit. The
343
- // discriminator that keeps multi-line paste working: the chunk
344
- // must contain EXACTLY ONE `\n` (the trailing one) and no other
345
- // newlines. Multi-line pastes have ≥2 `\n` characters (or arrive
346
- // wrapped in bracketed-paste markers handled below), so they
347
- // still preserve interior newlines via the printable-char branch.
348
- //
349
- // Detection contract:
350
- // - `input` ends with `\n`
351
- // - no Ctrl / Meta / Shift modifiers
352
- // - exactly ONE `\n` in the chunk (the trailing one)
353
- // - chunk is not bracketed-paste wrapped (markers stripped below)
354
- //
355
- // Edge cases covered by `test/input-box-lf-submit.spec.tsx`:
356
- // - bare `\n` → submit empty (no-op on empty buf)
357
- // - `hi\n` → splice `hi` + submit
358
- // - `hi\nthere\n` (multi-line) → printable branch, preserves \n
359
- // - `\r` (CR) → key.return branch unchanged
360
- // - `hi\r\n` (CRLF) → key.return branch (CR wins first)
361
- const endsWithLf = input.length > 0 && input.charCodeAt(input.length - 1) === 0x0a;
362
- const newlineCount = (input.match(/\n/g) || []).length;
363
- if (endsWithLf
364
- && newlineCount === 1
365
- && !key.meta
366
- && !key.ctrl
367
- && !key.shift) {
368
- // Splice the prefix (everything before the trailing `\n`) into
369
- // the buffer at the cursor, then run the canonical submit path.
370
- // Refs (cursorRef / lineRef) hold the latest committed values so
371
- // the splice runs against the operator's most recent edits even
372
- // if a previous async paste / setState is still mid-flight.
373
- const prefix = input.slice(0, -1);
374
- let mergedLine = lineRef.current;
375
- let mergedCursor = cursorRef.current;
376
- if (prefix.length > 0) {
377
- // Same sanitisation as the printable-char branch below — strip
378
- // bracketed-paste markers so a stray escape sequence never
379
- // lands in the submitted brief.
380
- const stripped = prefix
381
- .replace(/\x1b\[200~/g, '')
382
- .replace(/\x1b\[201~/g, '')
383
- .replace(/\[200~/g, '')
384
- .replace(/\[201~/g, '');
385
- if (stripped.length > 0) {
386
- mergedLine =
387
- mergedLine.slice(0, mergedCursor) + stripped + mergedLine.slice(mergedCursor);
388
- mergedCursor = mergedCursor + stripped.length;
389
- }
390
- }
391
- // Synthesise the same payload-shape the `key.return` branch
392
- // below uses so palette completion + history dedup + onSubmit
393
- // dispatch all run identically.
394
- const paletteHere = !paletteSuppressed
395
- ? filterPalette(mergedLine)
396
- : { rows: [], totalBeforeLimit: 0 };
397
- const paletteOpenHere = paletteHere.rows.length > 0;
398
- const paletteFocusedIndexHere = paletteHere.rows.length === 0
399
- ? 0
400
- : Math.min(paletteIndex, paletteHere.rows.length - 1);
401
- let payload = mergedLine;
402
- if (paletteOpenHere) {
403
- const completed = completePalette(mergedLine, paletteHere.rows, paletteFocusedIndexHere);
404
- if (completed !== null)
405
- payload = completed;
406
- }
407
- const trimmed = payload.trim();
408
- if (trimmed.length > 0) {
409
- setHistory((prev) => {
410
- if (prev[prev.length - 1] === trimmed)
411
- return prev;
412
- return [...prev, trimmed];
413
- });
414
- setHistoryIndex(-1);
415
- if (props.workspaceSlug) {
416
- appendHistory({
417
- home: props.historyHome,
418
- workspaceSlug: props.workspaceSlug,
419
- brief: trimmed,
420
- });
421
- }
422
- props.onSubmit(trimmed);
423
- }
424
- setLine('');
425
- setCursor(0);
426
- setPaletteSuppressed(false);
427
- setPaletteIndex(0);
428
- return;
429
- }
430
- // Readline-style kill ring shortcuts. All four kills push the
431
- // removed slice onto the ring; Ctrl+Y yanks the most recent.
432
- if (key.ctrl && input === 'u') {
433
- const next = killToLineStart(line, cursor, killRing);
434
- setLine(next.line);
435
- setCursor(next.cursor);
436
- setKillRing(next.ring);
437
- return;
438
- }
439
- if (key.ctrl && input === 'k') {
440
- const next = killToLineEnd(line, cursor, killRing);
441
- setLine(next.line);
442
- setCursor(next.cursor);
443
- setKillRing(next.ring);
444
- return;
445
- }
446
- if (key.ctrl && input === 'w') {
447
- const next = killWordBackward(line, cursor, killRing);
448
- setLine(next.line);
449
- setCursor(next.cursor);
450
- setKillRing(next.ring);
451
- return;
452
- }
453
- if (key.ctrl && input === 'y') {
454
- const next = yankAtCursor(line, cursor, killRing);
455
- setLine(next.line);
456
- setCursor(next.cursor);
457
- return;
458
- }
459
- if (key.ctrl && input === 'l') {
460
- // Clear the terminal viewport. ANSI 2J = erase entire screen,
461
- // 0;0H = home cursor. We bump the redraw tick so Ink repaints
462
- // the surrounding REPL panes on the next frame; without this
463
- // ink keeps its diff buffer and we get a half-wiped screen.
464
- if (stdout && typeof stdout.write === 'function') {
465
- stdout.write('\x1b[2J\x1b[0;0H');
466
- }
467
- setRedrawTick((t) => t + 1);
468
- return;
469
- }
470
- if (key.ctrl && input === 'v') {
471
- // Ctrl+V: read the OS clipboard and insert at cursor. The
472
- // platform helper (pbpaste / wl-paste / xclip -o / Get-Clipboard)
473
- // is async + best-effort; on failure the buffer is left
474
- // untouched and the operator falls back to terminal paste
475
- // (Cmd+V on macOS, right-click on Linux).
476
- //
477
- // Multi-line pastes preserve interior newlines so the operator
478
- // sees the full text in the buffer; the Enter-on-buffered-LF
479
- // case is left to the operator (they press Enter when ready).
480
- //
481
- // Refs (lineRef / cursorRef) hold the latest committed values so
482
- // the splice runs against the operator's most recent edits even
483
- // if they kept typing while pbpaste was in flight. The previous
484
- // setLine-inside-setCursor nesting double-inserted under React
485
- // 18 strict mode because each setState updater runs twice in
486
- // development. Claude P1.
487
- //
488
- // sanitiseClipboardText strips ANSI escapes, bracketed-paste
489
- // markers, and C0 control bytes so a hostile or accidental copy
490
- // cannot inject terminal-control sequences. Claude P2.
491
- readClipboard()
492
- .then((res) => {
493
- if (res.text === null)
494
- return;
495
- const sanitised = sanitiseClipboardText(res.text);
496
- if (sanitised.length === 0)
497
- return;
498
- const currentLine = lineRef.current;
499
- const currentCursor = cursorRef.current;
500
- const newLine = currentLine.slice(0, currentCursor) +
501
- sanitised +
502
- currentLine.slice(currentCursor);
503
- const newCursor = currentCursor + sanitised.length;
504
- setLine(newLine);
505
- setCursor(newCursor);
506
- })
507
- .catch(() => {
508
- // Helper already swallows errors; the catch here is belt +
509
- // suspenders so a TypeError never bubbles into the render.
510
- });
511
- return;
512
- }
513
- // Compute palette visibility FIRST so Enter can run the focused
514
- // palette row instead of submitting a partial `/he` as unknown.
515
- // Palette is hidden when the operator pressed Esc on it.
516
- const palette = !paletteSuppressed ? filterPalette(line) : { rows: [], totalBeforeLimit: 0 };
517
- const paletteOpen = palette.rows.length > 0;
518
- const paletteFocusedIndex = palette.rows.length === 0
519
- ? 0
520
- : Math.min(paletteIndex, palette.rows.length - 1);
521
- if (key.return) {
522
- // When the palette is open, Enter expands the buffer to the
523
- // complete command name (preserving any args past the head)
524
- // before submission. Codex review P2.
525
- let payload = line;
526
- if (paletteOpen) {
527
- const completed = completePalette(line, palette.rows, paletteFocusedIndex);
528
- if (completed !== null)
529
- payload = completed;
530
- }
531
- const trimmed = payload.trim();
532
- if (trimmed.length > 0) {
533
- setHistory((prev) => {
534
- // In-session dedup of consecutive identical entries mirrors
535
- // the on-disk dedup so ↑ never shows duplicates.
536
- if (prev[prev.length - 1] === trimmed)
537
- return prev;
538
- return [...prev, trimmed];
539
- });
540
- setHistoryIndex(-1);
541
- if (props.workspaceSlug) {
542
- // Fire-and-forget. Helper never throws - it returns null
543
- // when storage is unavailable and history degrades to
544
- // session-local for this turn.
545
- appendHistory({
546
- home: props.historyHome,
547
- workspaceSlug: props.workspaceSlug,
548
- brief: trimmed,
549
- });
550
- }
551
- props.onSubmit(trimmed);
552
- }
553
- setLine('');
554
- setCursor(0);
555
- setPaletteSuppressed(false);
556
- setPaletteIndex(0);
557
- return;
558
- }
559
- if (key.escape) {
560
- if (paletteOpen) {
561
- // Close the palette without clearing the buffer so the operator
562
- // can still send `/help` as plain text if they want. Palette
563
- // takes precedence over walkback because the operator's mental
564
- // model is "Esc closes the visible overlay first".
565
- setPaletteSuppressed(true);
566
- setLastEscapeAt(undefined);
567
- return;
568
- }
569
- // BT 8: Esc-Esc walkback. Two presses within
570
- // ESCAPE_DOUBLE_TAP_MS step the conversation back by one turn.
571
- // First press still clears the buffer (legacy behaviour for the
572
- // single-Esc cancel UX); the second press calls the host's
573
- // walkback handler. Buffer-clear on the first press is what makes
574
- // the double-tap feel "free" - the operator did not have to
575
- // memorise a new chord; they just have to keep pressing.
576
- const tEsc = now();
577
- const withinEscapeWindow = typeof lastEscapeAt === 'number'
578
- && tEsc - lastEscapeAt <= ESCAPE_DOUBLE_TAP_MS;
579
- if (withinEscapeWindow && props.onWalkback) {
580
- // Second tap inside the window. Buffer was already cleared on
581
- // the first press, so the host sees a clean input box AND the
582
- // walkback result. We clear the window so a third tap restarts
583
- // the cycle (no run-on walkbacks from a stuck Esc key).
584
- const verdict = props.onWalkback();
585
- setLastEscapeAt(undefined);
586
- if (verdict !== 'walked-back') {
587
- // Host refused (dispatch in flight, no turns to pop). The
588
- // host owns the refusal copy via its own writeOutput path;
589
- // we do not double-message here.
590
- return;
591
- }
592
- return;
593
- }
594
- // First Esc (or no walkback wired). Arm the window + clear the
595
- // buffer per the long-standing single-Esc cancel contract.
596
- setLastEscapeAt(tEsc);
597
- setLine('');
598
- setCursor(0);
599
- setHistoryIndex(-1);
600
- return;
601
- }
602
- if (key.tab && paletteOpen) {
603
- const completed = completePalette(line, palette.rows, paletteFocusedIndex);
604
- if (completed !== null) {
605
- setLine(completed);
606
- setCursor(completed.length);
607
- }
608
- return;
609
- }
610
- if (key.upArrow) {
611
- if (paletteOpen) {
612
- setPaletteIndex((i) => (palette.rows.length === 0 ? 0 : (i - 1 + palette.rows.length) % palette.rows.length));
613
- return;
614
- }
615
- if (history.length === 0)
616
- return;
617
- const nextIndex = historyIndex === -1 ? history.length - 1 : Math.max(0, historyIndex - 1);
618
- const entry = history[nextIndex] ?? '';
619
- setHistoryIndex(nextIndex);
620
- setLine(entry);
621
- setCursor(entry.length);
622
- return;
623
- }
624
- if (key.downArrow) {
625
- if (paletteOpen) {
626
- setPaletteIndex((i) => (palette.rows.length === 0 ? 0 : (i + 1) % palette.rows.length));
627
- return;
628
- }
629
- if (history.length === 0)
630
- return;
631
- if (historyIndex === -1)
632
- return;
633
- const nextIndex = historyIndex + 1;
634
- if (nextIndex >= history.length) {
635
- setHistoryIndex(-1);
636
- setLine('');
637
- setCursor(0);
638
- return;
639
- }
640
- const entry = history[nextIndex] ?? '';
641
- setHistoryIndex(nextIndex);
642
- setLine(entry);
643
- setCursor(entry.length);
644
- return;
645
- }
646
- if (key.leftArrow) {
647
- setCursor((c) => Math.max(0, c - 1));
648
- return;
649
- }
650
- if (key.rightArrow) {
651
- setCursor((c) => Math.min(line.length, c + 1));
652
- return;
653
- }
654
- if (key.backspace || key.delete) {
655
- if (cursor === 0)
656
- return;
657
- // Read cursor via ref inside the updater so the slice indices come
658
- // from the latest committed value, not the closure-captured one.
659
- // Same React-18 strict-mode race that bit the Ctrl+V paste path
660
- // (Claude P1 on PR wave 1): updaters run twice under strict
661
- // mode, and a stale closure `cursor` produces a double-edit on the
662
- // second invocation. cursorRef is the canonical fix — do NOT
663
- // re-introduce closure `cursor` inside setLine updaters.
664
- const cursorAtPress = cursorRef.current;
665
- setLine((prev) => prev.slice(0, cursorAtPress - 1) + prev.slice(cursorAtPress));
666
- setCursor((c) => Math.max(0, c - 1));
667
- // Backspacing past `/` re-opens the palette next render.
668
- setPaletteSuppressed(false);
669
- setPaletteIndex(0);
670
- return;
671
- }
672
- if (input && !key.meta && !key.ctrl) {
673
- // Ink delivers one or more characters per event; concatenate at
674
- // the cursor without filtering so non-Latin + emoji sequences
675
- // (the operator's own brief copy) survive paste.
676
- //
677
- // Bracketed-paste mode markers (`ESC[200~ ... ESC[201~`) may
678
- // arrive when a modern terminal pastes multi-line text. Strip
679
- // them inline so the buffer stays clean; the spec calls out
680
- // disabling Enter-as-submit during the burst, but Ink's
681
- // useInput delivers the whole paste as one event so the burst
682
- // is atomic and Enter cannot fire mid-paste here.
683
- //
684
- // Ink's escape-sequence parser may consume the leading ESC for
685
- // unknown CSI sequences, so we match both forms (with + without
686
- // the leading ESC byte) to be safe across terminal emulators.
687
- const stripped = input
688
- .replace(/\x1b\[200~/g, '')
689
- .replace(/\x1b\[201~/g, '')
690
- .replace(/\[200~/g, '')
691
- .replace(/\[201~/g, '');
692
- if (stripped.length === 0)
693
- return;
694
- // Read cursor via ref inside the updater so the splice indices come
695
- // from the latest committed value, not the closure-captured one.
696
- // Same React-18 strict-mode race that bit the Ctrl+V paste path
697
- // (Claude P1 on PR wave 1): updaters run twice under strict
698
- // mode, and a stale closure `cursor` produces a duplicated insert
699
- // on the second invocation. cursorRef is the canonical fix — do
700
- // NOT re-introduce closure `cursor` inside setLine updaters.
701
- const cursorAtPress = cursorRef.current;
702
- setLine((prev) => prev.slice(0, cursorAtPress) + stripped + prev.slice(cursorAtPress));
703
- setCursor((c) => c + stripped.length);
704
- // Typing a new char un-suppresses the palette and resets focus.
705
- setPaletteSuppressed(false);
706
- setPaletteIndex(0);
707
- }
708
- });
709
- const paletteView = !search && !paletteSuppressed ? filterPalette(line) : { rows: [], totalBeforeLimit: 0 };
710
- // Clamp focus to current row count so a shrink (e.g. operator typed
711
- // a char that narrowed the list) does not point off-the-end.
712
- const clampedPaletteIndex = paletteView.rows.length === 0
713
- ? 0
714
- : Math.min(paletteIndex, paletteView.rows.length - 1);
715
- const divider = '─'.repeat(innerWidth);
716
- const focusedMatch = search ? search.matches[search.focusedIndex] : undefined;
717
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "#3da9fc", dimColor: true, children: divider }), _jsx(Box, { paddingX: 1, flexDirection: "column", children: search ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: "#3da9fc", children: '(reverse-i-search) ' }), _jsx(Text, { children: `\`${search.query}\`: ` }), _jsx(Text, { color: "yellow", children: focusedMatch ? focusedMatch.brief : '(no match)' })] }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: `Ctrl+R next · Ctrl+S prev · Enter accept · Esc cancel · ${search.matches.length} match${search.matches.length === 1 ? '' : 'es'}` }) })] })) : (_jsxs(Box, { children: [_jsx(Text, { color: "#3da9fc", children: '› ' }), _jsx(Text, { children: renderLineWithCursor(line, cursor, cursorVisible) })] })) }), _jsx(Text, { color: "#3da9fc", dimColor: true, children: divider }), modeCycleToast ? (_jsx(Box, { children: _jsx(Text, { color: "#3da9fc", bold: true, children: ` ${modeCycleToast}` }) })) : null, ctrlCToast ? (_jsx(Box, { children: _jsx(Text, { color: "yellow", bold: true, children: ` ${ctrlCToast}` }) })) : null, line.length > innerWidth - 4 ? (_jsxs(Box, { children: [_jsx(Text, { color: "gray", children: '┊ ' }), _jsx(Text, { dimColor: true, children: 'line wraps - Enter still submits' })] })) : null, _jsx(SlashPalette, { rows: paletteView.rows, focusedIndex: clampedPaletteIndex, totalBeforeLimit: paletteView.totalBeforeLimit }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: '↑/↓ history · Ctrl+R search · / commands · Shift+Tab mode · Enter brief · Esc cancel · Ctrl+C abort / ×2 exit' }) })] }));
718
- }
719
- /**
720
- * Render the line with the cursor glyph inserted at `cursor`. The cursor
721
- * glyph is an inverted block when visible, a single space when blinked
722
- * off - keeping the rendered width stable so the surrounding border
723
- * does not jitter.
724
- */
725
- function renderLineWithCursor(line, cursor, visible) {
726
- const safeCursor = Math.max(0, Math.min(line.length, cursor));
727
- const before = line.slice(0, safeCursor);
728
- const after = line.slice(safeCursor);
729
- const caret = visible ? '█' : ' ';
730
- return `${before}${caret}${after}`;
731
- }
732
- //# sourceMappingURL=input-box.js.map