@pugi/cli 0.1.0-beta.9 → 0.1.0-beta.91

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 (411) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/LICENSE +1 -1
  3. package/assets/pugi-prozr2-mascot.ansi +9 -0
  4. package/bin/run.js +33 -1
  5. package/dist/commands/deploy.js +40 -40
  6. package/dist/commands/flatten.js +191 -0
  7. package/dist/commands/jobs-watch.js +201 -0
  8. package/dist/commands/jobs.js +42 -27
  9. package/dist/commands/smoke.js +133 -0
  10. package/dist/core/agent-progress/cleanup.js +134 -0
  11. package/dist/core/agent-progress/schema.js +144 -0
  12. package/dist/core/agent-progress/writer.js +101 -0
  13. package/dist/core/agents/adaptive-router.js +330 -0
  14. package/dist/core/agents/query-decomposer.js +297 -0
  15. package/dist/core/agents/registry.js +3 -3
  16. package/dist/core/approvals/shortcut-resolver.js +98 -0
  17. package/dist/core/artifact-chain/dispatcher.js +148 -0
  18. package/dist/core/artifact-chain/exporter.js +164 -0
  19. package/dist/core/artifact-chain/state.js +243 -0
  20. package/dist/core/artifact-chain/steps.js +169 -0
  21. package/dist/core/ask-user/question.js +92 -0
  22. package/dist/core/audit/audit-trail.js +275 -0
  23. package/dist/core/auth/ensure-authenticated.js +129 -0
  24. package/dist/core/auth/env-provider.js +238 -0
  25. package/dist/core/auto-open-browser.js +4 -4
  26. package/dist/core/auto-update/channels.js +122 -0
  27. package/dist/core/auto-update/checker.js +241 -0
  28. package/dist/core/auto-update/state.js +235 -0
  29. package/dist/core/bare-mode/index.js +107 -0
  30. package/dist/core/bash/redirect.js +281 -0
  31. package/dist/core/bash-classifier.js +436 -40
  32. package/dist/core/checkpoint/resumer.js +149 -0
  33. package/dist/core/checkpoint/rewinder.js +291 -0
  34. package/dist/core/checkpoints/shadow-git.js +670 -0
  35. package/dist/core/citations/parser.js +109 -0
  36. package/dist/core/classifier/yolo-classifier.js +88 -0
  37. package/dist/core/codegraph/decision-store.js +248 -0
  38. package/dist/core/codegraph/detect-repo.js +459 -0
  39. package/dist/core/codegraph/install.js +134 -0
  40. package/dist/core/codegraph/offer-hook.js +220 -0
  41. package/dist/core/compact/auto-trigger.js +96 -0
  42. package/dist/core/compact/buffer-rewriter.js +115 -0
  43. package/dist/core/compact/summarizer.js +208 -0
  44. package/dist/core/compact/token-counter.js +108 -0
  45. package/dist/core/consensus/anvil-fanout.js +25 -25
  46. package/dist/core/consensus/diff-capture.js +121 -12
  47. package/dist/core/consensus/rubric.js +21 -21
  48. package/dist/core/context/builder.js +6 -6
  49. package/dist/core/context/compaction-events.js +8 -8
  50. package/dist/core/context/compaction.js +31 -31
  51. package/dist/core/context/index.js +15 -8
  52. package/dist/core/context/invariants.js +51 -51
  53. package/dist/core/context/markdown-loader.js +28 -10
  54. package/dist/core/context/markdown-traverse.js +255 -0
  55. package/dist/core/context/pugiignore.js +41 -41
  56. package/dist/core/context/repo-skeleton.js +37 -37
  57. package/dist/core/context/tool-eviction.js +55 -0
  58. package/dist/core/context/watcher.js +32 -32
  59. package/dist/core/context/working-set.js +23 -23
  60. package/dist/core/coordinator/agent-tools.js +77 -0
  61. package/dist/core/coordinator/agent-toolset.js +65 -0
  62. package/dist/core/coordinator/fsm.js +73 -0
  63. package/dist/core/coordinator/mode-fsm.js +70 -0
  64. package/dist/core/cost/rate-card.js +129 -0
  65. package/dist/core/cost/tracker.js +221 -0
  66. package/dist/core/credentials.js +13 -13
  67. package/dist/core/cron/scheduler.js +138 -0
  68. package/dist/core/denial-tracking/index.js +8 -0
  69. package/dist/core/denial-tracking/state.js +264 -0
  70. package/dist/core/diagnostics/probe-runner.js +93 -0
  71. package/dist/core/diagnostics/probes/api.js +46 -0
  72. package/dist/core/diagnostics/probes/auth.js +93 -0
  73. package/dist/core/diagnostics/probes/bare-mode.js +42 -0
  74. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  75. package/dist/core/diagnostics/probes/config.js +72 -0
  76. package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
  77. package/dist/core/diagnostics/probes/disk.js +81 -0
  78. package/dist/core/diagnostics/probes/engine-live.js +46 -0
  79. package/dist/core/diagnostics/probes/git.js +65 -0
  80. package/dist/core/diagnostics/probes/hooks.js +118 -0
  81. package/dist/core/diagnostics/probes/mcp.js +75 -0
  82. package/dist/core/diagnostics/probes/node.js +59 -0
  83. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  84. package/dist/core/diagnostics/probes/pugi-md.js +89 -0
  85. package/dist/core/diagnostics/probes/sandbox.js +40 -0
  86. package/dist/core/diagnostics/probes/session.js +74 -0
  87. package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
  88. package/dist/core/diagnostics/probes/workspace.js +63 -0
  89. package/dist/core/diagnostics/types.js +70 -0
  90. package/dist/core/dispatch/cache-cleanup.js +197 -0
  91. package/dist/core/dispatch/cache-handoff.js +295 -0
  92. package/dist/core/edits/apply-patch-layer-e.js +189 -0
  93. package/dist/core/edits/dispatch.js +333 -7
  94. package/dist/core/edits/format-detector.js +260 -0
  95. package/dist/core/edits/format-matrix.js +26 -0
  96. package/dist/core/edits/fuzzy-ladder.js +650 -0
  97. package/dist/core/edits/index.js +5 -1
  98. package/dist/core/edits/journal.js +199 -0
  99. package/dist/core/edits/layer-a-apply.js +15 -15
  100. package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
  101. package/dist/core/edits/layer-b-apply.js +9 -9
  102. package/dist/core/edits/layer-c-apply.js +6 -6
  103. package/dist/core/edits/layer-d-ast.js +557 -14
  104. package/dist/core/edits/marker-parser.js +12 -12
  105. package/dist/core/edits/security-gate.js +27 -27
  106. package/dist/core/edits/verify-hook.js +273 -0
  107. package/dist/core/edits/worktree.js +29 -29
  108. package/dist/core/engine/anvil-client.js +214 -26
  109. package/dist/core/engine/auto-compact.js +179 -0
  110. package/dist/core/engine/budgets.js +186 -0
  111. package/dist/core/engine/context-prefix.js +155 -0
  112. package/dist/core/engine/index.js +1 -1
  113. package/dist/core/engine/intensity.js +158 -0
  114. package/dist/core/engine/intent.js +260 -0
  115. package/dist/core/engine/native-pugi.js +1295 -227
  116. package/dist/core/engine/prompts.js +129 -19
  117. package/dist/core/engine/strip-internal-fields.js +124 -0
  118. package/dist/core/engine/tool-bridge.js +1792 -59
  119. package/dist/core/evaluation/golden-dataset.js +293 -0
  120. package/dist/core/feedback/queue.js +177 -0
  121. package/dist/core/feedback/submitter.js +145 -0
  122. package/dist/core/file-cache.js +113 -1
  123. package/dist/core/flatten/flatten-repo.js +439 -0
  124. package/dist/core/format/osc8-link.js +28 -0
  125. package/dist/core/hook-chains.js +392 -0
  126. package/dist/core/hooks/citation-verify-hook.js +138 -0
  127. package/dist/core/hooks/citation-verify.js +112 -0
  128. package/dist/core/hooks/events.js +46 -0
  129. package/dist/core/hooks/index.js +15 -0
  130. package/dist/core/hooks/registry.js +216 -0
  131. package/dist/core/hooks/runner.js +236 -0
  132. package/dist/core/hooks/v2/event-emitter.js +115 -0
  133. package/dist/core/hooks/v2/executor.js +282 -0
  134. package/dist/core/hooks/v2/index.js +25 -0
  135. package/dist/core/hooks/v2/lifecycle.js +104 -0
  136. package/dist/core/hooks/v2/loader.js +216 -0
  137. package/dist/core/hooks/v2/matcher.js +125 -0
  138. package/dist/core/hooks/v2/trust.js +143 -0
  139. package/dist/core/hooks/v2/types.js +86 -0
  140. package/dist/core/hooks/worktree-events.js +158 -0
  141. package/dist/core/image/renderer.js +71 -0
  142. package/dist/core/init/detector.js +582 -0
  143. package/dist/core/init/template-renderer.js +242 -0
  144. package/dist/core/jobs/registry.js +18 -18
  145. package/dist/core/ledger/results-tsv.js +142 -0
  146. package/dist/core/log-discipline/stdout-redirect.js +51 -0
  147. package/dist/core/lsp/cache.js +105 -0
  148. package/dist/core/lsp/client.js +551 -41
  149. package/dist/core/lsp/language-detect.js +66 -0
  150. package/dist/core/lsp/post-edit-diagnostics.js +171 -0
  151. package/dist/core/lsp/server-detect.js +173 -0
  152. package/dist/core/lsp/symbol-cache.js +162 -0
  153. package/dist/core/lsp/symbol-tools.js +664 -0
  154. package/dist/core/mcp/client.js +97 -28
  155. package/dist/core/mcp/http-server.js +553 -0
  156. package/dist/core/mcp/orchestrator-tools.js +662 -0
  157. package/dist/core/mcp/permission.js +190 -0
  158. package/dist/core/mcp/registry.js +39 -17
  159. package/dist/core/mcp/server-tools.js +219 -0
  160. package/dist/core/mcp/server.js +397 -0
  161. package/dist/core/mcp/trust.js +10 -10
  162. package/dist/core/memory/dual-write.js +416 -0
  163. package/dist/core/memory/passive-extract.js +130 -0
  164. package/dist/core/memory/phase1-kinds.js +20 -0
  165. package/dist/core/memory/secret-scanner.js +304 -0
  166. package/dist/core/memory-sync/queue.js +170 -0
  167. package/dist/core/metrics/extract.js +113 -0
  168. package/dist/core/modes/roo-modes.js +68 -0
  169. package/dist/core/onboarding/ensure-initialized.js +133 -0
  170. package/dist/core/onboarding/marker.js +111 -0
  171. package/dist/core/onboarding/telemetry-state.js +108 -0
  172. package/dist/core/output-style/presets.js +176 -0
  173. package/dist/core/output-style/state.js +185 -0
  174. package/dist/core/path-security.js +287 -5
  175. package/dist/core/permission.js +82 -22
  176. package/dist/core/permissions/auto-classifier.js +124 -0
  177. package/dist/core/permissions/bash-parser.js +371 -0
  178. package/dist/core/permissions/circuit-breaker.js +83 -0
  179. package/dist/core/permissions/constrained-edit.js +91 -0
  180. package/dist/core/permissions/gate.js +278 -0
  181. package/dist/core/permissions/index.js +20 -0
  182. package/dist/core/permissions/mode.js +174 -0
  183. package/dist/core/permissions/network-egress.js +137 -0
  184. package/dist/core/permissions/state.js +241 -0
  185. package/dist/core/permissions/tool-class.js +93 -0
  186. package/dist/core/plan-mode/ui-state.js +51 -0
  187. package/dist/core/plans/plan-artifact.js +721 -0
  188. package/dist/core/policy-limits/etag-store.js +122 -0
  189. package/dist/core/prd-check/parser.js +215 -0
  190. package/dist/core/prd-check/reporter.js +127 -0
  191. package/dist/core/prd-check/session-review.js +557 -0
  192. package/dist/core/prd-check/verifiers.js +223 -0
  193. package/dist/core/prompt-cache/client-cache.js +99 -0
  194. package/dist/core/prompts/assembly.js +29 -0
  195. package/dist/core/prompts/registry.js +364 -0
  196. package/dist/core/pugi-md/cc-compat-rules.js +735 -0
  197. package/dist/core/pugi-md/context-injector.js +76 -0
  198. package/dist/core/pugi-md/walk-up.js +207 -0
  199. package/dist/core/python/uv-installer.js +270 -0
  200. package/dist/core/python/uv-resolver.js +83 -0
  201. package/dist/core/rate-limit/narrator.js +146 -0
  202. package/dist/core/recipes/cli-types.js +20 -0
  203. package/dist/core/recipes/loader.js +103 -0
  204. package/dist/core/recipes/runner.js +345 -0
  205. package/dist/core/recipes/schema.js +587 -0
  206. package/dist/core/release-notes/parser.js +241 -0
  207. package/dist/core/release-notes/state.js +116 -0
  208. package/dist/core/repl/ask.js +37 -37
  209. package/dist/core/repl/cancellation.js +26 -26
  210. package/dist/core/repl/cap-warning.js +4 -4
  211. package/dist/core/repl/clipboard-read.js +11 -11
  212. package/dist/core/repl/dispatch-fsm.js +12 -12
  213. package/dist/core/repl/history-search.js +15 -15
  214. package/dist/core/repl/history.js +28 -18
  215. package/dist/core/repl/kill-ring.js +5 -5
  216. package/dist/core/repl/model-pricing.js +135 -0
  217. package/dist/core/repl/privacy-banner.js +22 -22
  218. package/dist/core/repl/session.js +2148 -217
  219. package/dist/core/repl/slash-commands.js +501 -41
  220. package/dist/core/repl/store/index.js +1 -1
  221. package/dist/core/repl/store/jsonl-log.js +22 -22
  222. package/dist/core/repl/store/lockfile.js +10 -10
  223. package/dist/core/repl/store/session-store.js +136 -107
  224. package/dist/core/repl/store/types.js +15 -15
  225. package/dist/core/repl/store/uuid-v7.js +12 -12
  226. package/dist/core/repl/workspace-context.js +43 -21
  227. package/dist/core/repo-map/build.js +125 -0
  228. package/dist/core/repo-map/cache.js +185 -0
  229. package/dist/core/repo-map/extractor.js +254 -0
  230. package/dist/core/repo-map/formatter.js +145 -0
  231. package/dist/core/repo-map/page-rank.js +105 -0
  232. package/dist/core/repo-map/scanner.js +211 -0
  233. package/dist/core/retry-budget/budget.js +284 -0
  234. package/dist/core/retry-budget/index.js +5 -0
  235. package/dist/core/retry-budget/retry-cap.js +74 -0
  236. package/dist/core/routing/lead-worker.js +43 -0
  237. package/dist/core/routing/pre-flight-estimator.js +108 -0
  238. package/dist/core/runs/run-tree.js +103 -0
  239. package/dist/core/security/injection-scanner.js +367 -0
  240. package/dist/core/security/output-filter.js +418 -0
  241. package/dist/core/session/env-file.js +105 -0
  242. package/dist/core/session/section-budgets.js +140 -0
  243. package/dist/core/session.js +92 -0
  244. package/dist/core/settings.js +324 -5
  245. package/dist/core/share/formatter.js +271 -0
  246. package/dist/core/share/redactor.js +221 -0
  247. package/dist/core/share/uploader.js +267 -0
  248. package/dist/core/skills/defaults.js +30 -30
  249. package/dist/core/skills/loader.js +22 -22
  250. package/dist/core/skills/sources.js +27 -27
  251. package/dist/core/smoke/headless-driver.js +174 -0
  252. package/dist/core/smoke/orchestrator.js +194 -0
  253. package/dist/core/smoke/runner.js +238 -0
  254. package/dist/core/smoke/scenario-parser.js +316 -0
  255. package/dist/core/statusline.js +99 -0
  256. package/dist/core/subagents/dispatcher-real.js +600 -0
  257. package/dist/core/subagents/dispatcher.js +132 -43
  258. package/dist/core/subagents/index.js +19 -6
  259. package/dist/core/subagents/isolation-matrix.js +213 -0
  260. package/dist/core/subagents/spawn.js +19 -4
  261. package/dist/core/telemetry/emitter.js +229 -0
  262. package/dist/core/telemetry/queue.js +251 -0
  263. package/dist/core/theme/context.js +91 -0
  264. package/dist/core/theme/presets.js +228 -0
  265. package/dist/core/theme/state.js +181 -0
  266. package/dist/core/todos/invariant.js +10 -0
  267. package/dist/core/todos/state.js +177 -0
  268. package/dist/core/tool-schema/compressor.js +89 -0
  269. package/dist/core/transport/version-interceptor.js +166 -0
  270. package/dist/core/trust.js +2 -2
  271. package/dist/core/tui/thinking-block.js +64 -0
  272. package/dist/core/vim/keymap.js +288 -0
  273. package/dist/core/vim/state.js +92 -0
  274. package/dist/core/watch-markers/marker-watcher.js +133 -0
  275. package/dist/core/worktree/include-parser.js +249 -0
  276. package/dist/core/worktree-manager/cleanup.js +123 -0
  277. package/dist/core/worktree-manager/manager.js +303 -0
  278. package/dist/index.js +36 -0
  279. package/dist/runtime/bootstrap.js +190 -0
  280. package/dist/runtime/cli.js +4185 -549
  281. package/dist/runtime/commands/agents.js +31 -31
  282. package/dist/runtime/commands/budget.js +5 -5
  283. package/dist/runtime/commands/cancel.js +231 -0
  284. package/dist/runtime/commands/chain.js +489 -0
  285. package/dist/runtime/commands/codegraph-status.js +227 -0
  286. package/dist/runtime/commands/compact.js +297 -0
  287. package/dist/runtime/commands/config.js +73 -39
  288. package/dist/runtime/commands/cost.js +199 -0
  289. package/dist/runtime/commands/delegate.js +27 -4
  290. package/dist/runtime/commands/dispatch.js +126 -0
  291. package/dist/runtime/commands/doctor.js +579 -0
  292. package/dist/runtime/commands/feedback.js +184 -0
  293. package/dist/runtime/commands/hooks.js +187 -0
  294. package/dist/runtime/commands/init.js +254 -0
  295. package/dist/runtime/commands/lsp.js +200 -38
  296. package/dist/runtime/commands/mcp.js +879 -0
  297. package/dist/runtime/commands/memory.js +582 -0
  298. package/dist/runtime/commands/model.js +237 -0
  299. package/dist/runtime/commands/onboarding.js +275 -0
  300. package/dist/runtime/commands/patch.js +12 -12
  301. package/dist/runtime/commands/permissions.js +112 -0
  302. package/dist/runtime/commands/plan.js +143 -0
  303. package/dist/runtime/commands/prd-check.js +285 -0
  304. package/dist/runtime/commands/privacy.js +17 -17
  305. package/dist/runtime/commands/recipe.js +325 -0
  306. package/dist/runtime/commands/redo-blob-store.js +92 -0
  307. package/dist/runtime/commands/redo.js +361 -0
  308. package/dist/runtime/commands/release-notes.js +229 -0
  309. package/dist/runtime/commands/repo-map.js +95 -0
  310. package/dist/runtime/commands/report.js +299 -0
  311. package/dist/runtime/commands/resume.js +118 -0
  312. package/dist/runtime/commands/review-consensus.js +68 -53
  313. package/dist/runtime/commands/rewind.js +333 -0
  314. package/dist/runtime/commands/roster.js +14 -14
  315. package/dist/runtime/commands/sessions.js +163 -0
  316. package/dist/runtime/commands/share.js +316 -0
  317. package/dist/runtime/commands/skills.js +31 -31
  318. package/dist/runtime/commands/status.js +186 -0
  319. package/dist/runtime/commands/stickers.js +82 -0
  320. package/dist/runtime/commands/style.js +194 -0
  321. package/dist/runtime/commands/theme.js +196 -0
  322. package/dist/runtime/commands/undo.js +54 -22
  323. package/dist/runtime/commands/update.js +289 -0
  324. package/dist/runtime/commands/vim.js +140 -0
  325. package/dist/runtime/commands/worktree.js +8 -8
  326. package/dist/runtime/commands/worktrees.js +155 -0
  327. package/dist/runtime/headless-repl.js +195 -0
  328. package/dist/runtime/headless.js +543 -0
  329. package/dist/runtime/load-hooks-or-exit.js +71 -0
  330. package/dist/runtime/plan-decompose.js +22 -22
  331. package/dist/runtime/sigint-guard.js +272 -0
  332. package/dist/runtime/update-check.js +28 -28
  333. package/dist/runtime/version.js +65 -0
  334. package/dist/runtime/worktree-bootstrap.js +579 -0
  335. package/dist/skills/bundled/batch.js +617 -0
  336. package/dist/skills/bundled/index.js +45 -0
  337. package/dist/skills/bundled/loop.js +358 -0
  338. package/dist/skills/bundled/remember.js +383 -0
  339. package/dist/skills/bundled/simplify.js +289 -0
  340. package/dist/skills/bundled/skillify.js +373 -0
  341. package/dist/skills/bundled/stuck.js +558 -0
  342. package/dist/skills/bundled/verify.js +439 -0
  343. package/dist/testing/vcr.js +486 -0
  344. package/dist/tools/agent-tool.js +229 -0
  345. package/dist/tools/apply-patch.js +89 -28
  346. package/dist/tools/ask-user-question.js +337 -0
  347. package/dist/tools/ask-user.js +115 -0
  348. package/dist/tools/bash.js +624 -46
  349. package/dist/tools/brief.js +224 -0
  350. package/dist/tools/cron.js +433 -0
  351. package/dist/tools/enter-worktree.js +250 -0
  352. package/dist/tools/exit-worktree.js +147 -0
  353. package/dist/tools/file-tools.js +161 -44
  354. package/dist/tools/lsp-tools.js +377 -1
  355. package/dist/tools/mcp-tool.js +260 -0
  356. package/dist/tools/multi-edit.js +361 -0
  357. package/dist/tools/powershell.js +268 -0
  358. package/dist/tools/registry.js +99 -4
  359. package/dist/tools/skill-tool.js +96 -0
  360. package/dist/tools/sleep.js +99 -0
  361. package/dist/tools/synthetic-output.js +133 -0
  362. package/dist/tools/tasks.js +208 -0
  363. package/dist/tools/todo-write.js +184 -0
  364. package/dist/tools/verify-plan-execution.js +295 -0
  365. package/dist/tools/web-fetch-injection-scanner.js +207 -0
  366. package/dist/tools/web-fetch.js +195 -10
  367. package/dist/tools/web-search.js +458 -0
  368. package/dist/tui/agent-progress-card.js +111 -0
  369. package/dist/tui/agent-tree.js +11 -1
  370. package/dist/tui/ask-modal.js +14 -14
  371. package/dist/tui/ask-user-question-chips.js +315 -0
  372. package/dist/tui/ask-user-question-prompt.js +203 -0
  373. package/dist/tui/compact-banner.js +81 -0
  374. package/dist/tui/conversation-pane.js +85 -11
  375. package/dist/tui/cost-table.js +111 -0
  376. package/dist/tui/device-flow.js +2 -2
  377. package/dist/tui/doctor-table.js +46 -0
  378. package/dist/tui/feedback-prompt.js +156 -0
  379. package/dist/tui/input-box.js +247 -32
  380. package/dist/tui/login-picker.js +3 -3
  381. package/dist/tui/markdown-render.js +6 -6
  382. package/dist/tui/multi-file-diff-approval.js +375 -0
  383. package/dist/tui/onboarding-wizard.js +240 -0
  384. package/dist/tui/permissions-picker.js +86 -0
  385. package/dist/tui/render.js +36 -1
  386. package/dist/tui/repl-render.js +176 -25
  387. package/dist/tui/repl-splash-art.js +16 -16
  388. package/dist/tui/repl-splash-mascot.js +48 -24
  389. package/dist/tui/repl-splash.js +22 -22
  390. package/dist/tui/repl.js +125 -45
  391. package/dist/tui/slash-palette.js +6 -6
  392. package/dist/tui/splash.js +2 -2
  393. package/dist/tui/status-bar.js +109 -31
  394. package/dist/tui/status-table.js +7 -0
  395. package/dist/tui/stickers-art.js +136 -0
  396. package/dist/tui/style-table.js +28 -0
  397. package/dist/tui/theme-table.js +29 -0
  398. package/dist/tui/thinking-spinner.js +123 -0
  399. package/dist/tui/tool-stream-pane.js +53 -4
  400. package/dist/tui/update-banner.js +27 -2
  401. package/dist/tui/vim-input.js +267 -0
  402. package/dist/tui/welcome-banner.js +107 -0
  403. package/dist/tui/welcome-data.js +293 -0
  404. package/dist/tui/workspace-context.js +2 -2
  405. package/package.json +31 -16
  406. package/test/scenarios/codegen-create-file.scenario.txt +13 -0
  407. package/test/scenarios/compact-force.scenario.txt +12 -0
  408. package/test/scenarios/identity.scenario.txt +12 -0
  409. package/test/scenarios/persona-handoff.scenario.txt +12 -0
  410. package/test/scenarios/walkback.scenario.txt +12 -0
  411. package/dist/core/engine/compaction-hook.js +0 -154
@@ -1,55 +1,55 @@
1
1
  /**
2
- * LSP client wrapper — α7.7 Phase 1 (LSP tool + worktree + apply_patch).
2
+ * LSP client wrapper — Phase 1 (LSP tool + worktree + apply_patch).
3
3
  *
4
4
  * Wraps a Language Server Protocol server process (started locally via
5
5
  * stdio) behind a small async surface used by the LSP tools:
6
6
  *
7
- * - hover(file, line, col)
8
- * - definition(file, line, col)
9
- * - references(file, line, col)
10
- * - diagnostics(file)
7
+ * - hover(file, line, col)
8
+ * - definition(file, line, col)
9
+ * - references(file, line, col)
10
+ * - diagnostics(file)
11
11
  *
12
12
  * Why no `vscode-languageserver-protocol` dep:
13
13
  *
14
- * The α6.6+ posture for `apps/pugi-cli` keeps the dep tree intentionally
15
- * lean (zod, ink, react, undici, tar — that's most of it). Pulling
16
- * `vscode-languageserver-protocol` + transitive `vscode-jsonrpc` for
17
- * four operations would expand the install footprint disproportionately.
18
- * The LSP framing protocol (Content-Length headers + JSON-RPC bodies)
19
- * is documented in the LSP spec §3 and stable since 2017; we implement
20
- * the framer ourselves in ~80 LOC. Pulling in the official packages
21
- * later (when we add 20+ operations) stays an open option — every
22
- * public surface here mirrors the official type names so a future swap
23
- * is mechanical.
14
+ * The + posture for `apps/pugi-cli` keeps the dep tree intentionally
15
+ * lean (zod, ink, react, undici, tar — that's most of it). Pulling
16
+ * `vscode-languageserver-protocol` + transitive `vscode-jsonrpc` for
17
+ * four operations would expand the install footprint disproportionately.
18
+ * The LSP framing protocol (Content-Length headers + JSON-RPC bodies)
19
+ * is documented in the LSP spec §3 and stable since 2017; we implement
20
+ * the framer ourselves in ~80 LOC. Pulling in the official packages
21
+ * later (when we add 20+ operations) stays an open option — every
22
+ * public surface here mirrors the official type names so a future swap
23
+ * is mechanical.
24
24
  *
25
25
  * Why we spawn `npx <server>` for stock languages:
26
26
  *
27
- * - typescript-language-server: `npx typescript-language-server --stdio`
28
- * - pyright: `pyright-langserver --stdio` (operator-installed)
29
- * - gopls: `gopls` (operator-installed via `go install`)
30
- * - rust-analyzer: `rust-analyzer` (operator-installed via `rustup`)
27
+ * - typescript-language-server: `npx typescript-language-server --stdio`
28
+ * - pyright: `pyright-langserver --stdio` (operator-installed)
29
+ * - gopls: `gopls` (operator-installed via `go install`)
30
+ * - rust-analyzer: `rust-analyzer` (operator-installed via `rustup`)
31
31
  *
32
- * `npx typescript-language-server` resolves the binary from the
33
- * workspace's `node_modules/.bin` first, then falls back to the global
34
- * npm cache. The other servers are operator-installed system binaries
35
- * (we run `which <name>` once at start and fail loud with
36
- * `lsp_unavailable` if absent — see `detectServer` below). This keeps
37
- * `pugi` install-time zero-deps for non-TS workspaces.
32
+ * `npx typescript-language-server` resolves the binary from the
33
+ * workspace's `node_modules/.bin` first, then falls back to the global
34
+ * npm cache. The other servers are operator-installed system binaries
35
+ * (we run `which <name>` once at start and fail loud with
36
+ * `lsp_unavailable` if absent — see `detectServer` below). This keeps
37
+ * `pugi` install-time zero-deps for non-TS workspaces.
38
38
  *
39
39
  * Lifecycle contract:
40
40
  *
41
- * 1. `startLspClient(lang, opts)` spawns the server, performs the LSP
42
- * `initialize` handshake, sends `initialized`, and returns a client.
43
- * 2. Each operation auto-opens the target file via `textDocument/didOpen`
44
- * on first touch (we keep a `Set<string>` of opened URIs). Files are
45
- * never closed mid-session — the server is per-CLI-invocation and
46
- * the process exit cleans up.
47
- * 3. `stop()` sends `shutdown` + `exit` (best-effort) and SIGKILLs the
48
- * child after a 1s grace window so the CLI never hangs on a
49
- * misbehaving server.
41
+ * 1. `startLspClient(lang, opts)` spawns the server, performs the LSP
42
+ * `initialize` handshake, sends `initialized`, and returns a client.
43
+ * 2. Each operation auto-opens the target file via `textDocument/didOpen`
44
+ * on first touch (we keep a `Set<string>` of opened URIs). Files are
45
+ * never closed mid-session — the server is per-CLI-invocation and
46
+ * the process exit cleans up.
47
+ * 3. `stop()` sends `shutdown` + `exit` (best-effort) and SIGKILLs the
48
+ * child after a 1s grace window so the CLI never hangs on a
49
+ * misbehaving server.
50
50
  *
51
51
  * Cancellation: every async operation accepts an optional CancellationToken
52
- * (α6.9). On abort, we send LSP `$/cancelRequest` for the in-flight ID and
52
+ * . On abort, we send LSP `$/cancelRequest` for the in-flight ID and
53
53
  * reject the promise with `OperatorAbortedError`.
54
54
  *
55
55
  * Privacy: requests stay client-side. There is no Anvil round-trip; the
@@ -130,7 +130,7 @@ export class LspClient {
130
130
  child.stderr.on('data', () => { });
131
131
  }
132
132
  child.on('exit', () => this.onExit());
133
- // R1 fix (2026-05-26, PR #413 r1, P2 #11): mirror onExit for the
133
+ // R1 fix (2026-05-26, PR r1, P2 #11): mirror onExit for the
134
134
  // 'error' event. A late-fired spawn error (EIO, ENOMEM, etc.) or
135
135
  // any unhandled child-process error would otherwise leave
136
136
  // in-flight pending requests dangling until their per-request
@@ -206,6 +206,189 @@ export class LspClient {
206
206
  return { ok: true, value: locations };
207
207
  });
208
208
  }
209
+ /**
210
+ * PUGI-78 Phase 1: document outline. Returns the LSP raw shape (either
211
+ * `DocumentSymbol[]` hierarchical or `SymbolInformation[]` flat); the
212
+ * symbol-tools layer normalizes both to the agent-facing flat form.
213
+ */
214
+ async documentSymbols(file, token) {
215
+ return this.withDocument(file, async (uri) => {
216
+ const raw = await this.sendRequest('textDocument/documentSymbol', { textDocument: { uri } }, token);
217
+ if (!Array.isArray(raw))
218
+ return { ok: true, value: [] };
219
+ return { ok: true, value: raw };
220
+ });
221
+ }
222
+ /**
223
+ * PUGI-78 Phase 1: workspace-wide symbol fuzzy search. The query is
224
+ * server-defined (substring / fuzzy / prefix); we forward verbatim.
225
+ * Files outside the workspace are kept as raw URIs — the symbol-tools
226
+ * layer surfaces them to the operator as-is.
227
+ */
228
+ async workspaceSymbols(query, token) {
229
+ // workspace/symbol does not need a document open, but we still
230
+ // honor the cancellation token. We call sendRequest directly via
231
+ // the typed internal accessor used by the handshake.
232
+ try {
233
+ const internal = this;
234
+ const raw = await internal.sendRequest('workspace/symbol', { query }, token);
235
+ if (!Array.isArray(raw))
236
+ return { ok: true, value: [] };
237
+ return { ok: true, value: raw };
238
+ }
239
+ catch (error) {
240
+ if (error instanceof OperatorAbortedError) {
241
+ return { ok: false, reason: 'operator_aborted', detail: error.message };
242
+ }
243
+ if (error instanceof Error && error.message === 'request_timeout') {
244
+ return { ok: false, reason: 'request_timeout', detail: 'workspace/symbol timed out' };
245
+ }
246
+ return {
247
+ ok: false,
248
+ reason: 'lsp_error',
249
+ detail: error instanceof Error ? error.message : String(error),
250
+ };
251
+ }
252
+ }
253
+ /**
254
+ * PUGI-78 Phase 1: implementations of an interface / abstract method.
255
+ * LSP `textDocument/implementation` returns the same `Location[]` shape
256
+ * as `definition`, so we re-use `normalizeLocations`.
257
+ */
258
+ async implementations(file, pos, token) {
259
+ return this.withDocument(file, async (uri) => {
260
+ const raw = await this.sendRequest('textDocument/implementation', { textDocument: { uri }, position: pos }, token);
261
+ const locations = normalizeLocations(raw, this.cwd);
262
+ return { ok: true, value: locations };
263
+ });
264
+ }
265
+ /**
266
+ * PUGI-78 Phase 1: type-definition (vs value-definition). Same wire
267
+ * shape as `definition` — server reports the location of the symbol's
268
+ * type declaration, not its instantiation site.
269
+ */
270
+ async typeDefinition(file, pos, token) {
271
+ return this.withDocument(file, async (uri) => {
272
+ const raw = await this.sendRequest('textDocument/typeDefinition', { textDocument: { uri }, position: pos }, token);
273
+ const locations = normalizeLocations(raw, this.cwd);
274
+ return { ok: true, value: locations };
275
+ });
276
+ }
277
+ /**
278
+ * PUGI-78 Phase 1: signature help at a call site. Returns the active
279
+ * overload's label, parameters, and active-parameter index. Null when
280
+ * the server reports no signature (cursor is not inside a call).
281
+ */
282
+ async signatureHelp(file, pos, token) {
283
+ return this.withDocument(file, async (uri) => {
284
+ const raw = await this.sendRequest('textDocument/signatureHelp', { textDocument: { uri }, position: pos }, token);
285
+ return { ok: true, value: normalizeSignatureHelp(raw) };
286
+ });
287
+ }
288
+ /**
289
+ * PUGI-78 Phase 1: prepare a rename. Returns the range LSP says the
290
+ * symbol occupies; the dispatcher uses this to confirm the cursor is
291
+ * actually on a renameable token before issuing the full rename.
292
+ */
293
+ async prepareRename(file, pos, token) {
294
+ return this.withDocument(file, async (uri) => {
295
+ try {
296
+ const raw = await this.sendRequest('textDocument/prepareRename', { textDocument: { uri }, position: pos }, token);
297
+ if (!raw)
298
+ return { ok: true, value: null };
299
+ // LSP allows { range, placeholder } OR a raw Range; surface
300
+ // either as a flat Range.
301
+ if (typeof raw === 'object' && raw && 'start' in raw && 'end' in raw) {
302
+ const range = parseRange(raw);
303
+ return { ok: true, value: range ?? null };
304
+ }
305
+ if (typeof raw === 'object' && raw && 'range' in raw) {
306
+ const range = parseRange(raw.range);
307
+ return { ok: true, value: range ?? null };
308
+ }
309
+ return { ok: true, value: null };
310
+ }
311
+ catch (error) {
312
+ // Some servers (pyright pre-1.1) lack prepareRename support and
313
+ // return a JSON-RPC error. The dispatcher should still be able
314
+ // to attempt the rename — surface null instead of bubbling the
315
+ // failure as the agent surface treats null as "skip prepare".
316
+ if (error instanceof Error && /method not (found|supported)/i.test(error.message)) {
317
+ return { ok: true, value: null };
318
+ }
319
+ throw error;
320
+ }
321
+ });
322
+ }
323
+ /**
324
+ * PUGI-78 Phase 1: atomic rename refactor. Returns the workspace edit
325
+ * the server proposes; the dispatcher previews + applies in a future
326
+ * ticket (Phase 2). For Phase 1 the agent surface lists affected files
327
+ * + line:character of each edit so the model can summarize the impact.
328
+ */
329
+ async rename(file, pos, newName, token) {
330
+ return this.withDocument(file, async (uri) => {
331
+ const raw = await this.sendRequest('textDocument/rename', { textDocument: { uri }, position: pos, newName }, token);
332
+ return { ok: true, value: normalizeWorkspaceEdit(raw, this.cwd) };
333
+ });
334
+ }
335
+ /**
336
+ * PUGI-78 Phase 1: quick-fix list at a range. The server returns either
337
+ * `Command[]` (legacy) or `CodeAction[]` (modern); we normalize to the
338
+ * union shape so the surface stays stable.
339
+ */
340
+ async codeActions(file, range, token) {
341
+ return this.withDocument(file, async (uri) => {
342
+ const raw = await this.sendRequest('textDocument/codeAction', {
343
+ textDocument: { uri },
344
+ range,
345
+ context: { diagnostics: [] },
346
+ }, token);
347
+ return { ok: true, value: normalizeCodeActions(raw) };
348
+ });
349
+ }
350
+ /**
351
+ * PUGI-78 Phase 1: formatter. Returns the text edits the server would
352
+ * apply for `textDocument/formatting`. The dispatcher composes them
353
+ * into a single rewrite in a future ticket; Phase 1 surfaces the edits
354
+ * for inspection.
355
+ */
356
+ async formatting(file, token, options) {
357
+ return this.withDocument(file, async (uri) => {
358
+ const raw = await this.sendRequest('textDocument/formatting', {
359
+ textDocument: { uri },
360
+ options: {
361
+ tabSize: options?.tabSize ?? 2,
362
+ insertSpaces: options?.insertSpaces ?? true,
363
+ },
364
+ }, token);
365
+ return { ok: true, value: normalizeTextEdits(raw) };
366
+ });
367
+ }
368
+ /**
369
+ * PUGI-78 Phase 1: call hierarchy at a symbol position. The LSP wire
370
+ * is a two-step protocol — first `prepareCallHierarchy` to get the
371
+ * item handle, then `incomingCalls` and `outgoingCalls`. We surface
372
+ * both edges in a single call.
373
+ */
374
+ async callHierarchy(file, pos, token) {
375
+ return this.withDocument(file, async (uri) => {
376
+ const prepared = await this.sendRequest('textDocument/prepareCallHierarchy', { textDocument: { uri }, position: pos }, token);
377
+ if (!Array.isArray(prepared) || prepared.length === 0) {
378
+ return { ok: true, value: { incoming: [], outgoing: [] } };
379
+ }
380
+ const item = prepared[0];
381
+ const incomingRaw = await this.sendRequest('callHierarchy/incomingCalls', { item }, token);
382
+ const outgoingRaw = await this.sendRequest('callHierarchy/outgoingCalls', { item }, token);
383
+ return {
384
+ ok: true,
385
+ value: {
386
+ incoming: normalizeCallHierarchyEdges(incomingRaw, 'from', this.cwd),
387
+ outgoing: normalizeCallHierarchyEdges(outgoingRaw, 'to', this.cwd),
388
+ },
389
+ };
390
+ });
391
+ }
209
392
  /**
210
393
  * Diagnostics in LSP arrive as PUSH (`textDocument/publishDiagnostics`)
211
394
  * not pull. We open the document, wait one short tick for the server
@@ -260,7 +443,7 @@ export class LspClient {
260
443
  detail: error instanceof Error ? error.message : String(error),
261
444
  };
262
445
  }
263
- // R1 fix (2026-05-26, PR #413 r1, Fix 8): realpath containment.
446
+ // R1 fix (2026-05-26, PR r1, Fix 8): realpath containment.
264
447
  // Without this gate, a workspace-local symlink (e.g. `alias` ->
265
448
  // `/etc/passwd`) passed the lexical `absPath.startsWith(cwd)`
266
449
  // check, then `readFileSync(absPath, 'utf8')` happily followed the
@@ -385,7 +568,7 @@ export class LspClient {
385
568
  const headerText = this.buffer.subarray(0, headerEnd).toString('ascii');
386
569
  const lengthMatch = headerText.match(/Content-Length:\s*(\d+)/i);
387
570
  if (!lengthMatch || lengthMatch[1] === undefined) {
388
- // R1 fix (2026-05-26, PR #413 r1, Fix 7): malformed header —
571
+ // R1 fix (2026-05-26, PR r1, Fix 7): malformed header —
389
572
  // instead of nuking the entire buffer (which would discard ANY
390
573
  // subsequent valid messages already queued in `this.buffer`),
391
574
  // scan forward for the next `Content-Length:` marker and resync
@@ -465,10 +648,59 @@ export class LspClient {
465
648
  }
466
649
  }
467
650
  }
651
+ /**
652
+ * Map a short LSP language slug to the settings.json key. β7 L9 — the
653
+ * settings schema spells out the full language name (`typescript`,
654
+ * `python`, ...) for human readability; the short slug (`ts`, `py`) is
655
+ * what every internal call site uses. Keep this map narrow and explicit.
656
+ */
657
+ const SETTINGS_KEY_BY_LANG = {
658
+ ts: 'typescript',
659
+ js: 'javascript',
660
+ py: 'python',
661
+ go: 'go',
662
+ rust: 'rust',
663
+ };
664
+ /**
665
+ * Report whether the operator has explicitly disabled this language via
666
+ * `.pugi/settings.json::lsp.<language> = false`. Absent section or
667
+ * absent key means "enabled by default" — backwards-compatible with the
668
+ * surface that ignored settings entirely. Returns true ONLY when
669
+ * the operator explicitly set the value to false.
670
+ */
671
+ export function isLspLanguageDisabled(lang, lspSettings) {
672
+ if (!lspSettings)
673
+ return false;
674
+ const key = SETTINGS_KEY_BY_LANG[lang];
675
+ return lspSettings[key] === false;
676
+ }
677
+ /**
678
+ * Probe every registered language server. Operator-facing helper for
679
+ * `pugi lsp servers` — returns one row per language with the binary
680
+ * name, whether it was found on PATH, and whether the settings toggle
681
+ * has explicitly disabled it.
682
+ */
683
+ export function inspectLspServers(lspSettings) {
684
+ const out = [];
685
+ for (const lang of Object.keys(LANGUAGE_SERVERS)) {
686
+ const server = LANGUAGE_SERVERS[lang];
687
+ out.push({
688
+ language: lang,
689
+ command: server.command + (server.args.length > 0 ? ` ${server.args.join(' ')}` : ''),
690
+ available: detectBinary(server.probe),
691
+ enabled: !isLspLanguageDisabled(lang, lspSettings),
692
+ });
693
+ }
694
+ return out;
695
+ }
468
696
  /**
469
697
  * Start an LSP client for the given language. Returns either an `LspClient`
470
698
  * ready to use, or a structured failure (`lsp_unavailable`,
471
699
  * `language_unsupported`).
700
+ *
701
+ * β7 L9: respects `.pugi/settings.json::lsp.<language> = false` —
702
+ * a disabled language reports `lsp_disabled` so the caller surface can
703
+ * tell the operator the binary IS available but settings says no.
472
704
  */
473
705
  export async function startLspClient(lang, opts) {
474
706
  const server = opts.serverOverride ?? LANGUAGE_SERVERS[lang];
@@ -479,6 +711,14 @@ export async function startLspClient(lang, opts) {
479
711
  detail: `no LSP server registered for language: ${lang}`,
480
712
  };
481
713
  }
714
+ if (!opts.serverOverride && isLspLanguageDisabled(lang, opts.lspSettings)) {
715
+ return {
716
+ ok: false,
717
+ reason: 'lsp_disabled',
718
+ detail: `${lang} is disabled in .pugi/settings.json::lsp.${SETTINGS_KEY_BY_LANG[lang]}. ` +
719
+ `Remove the override (or set it to true) to enable.`,
720
+ };
721
+ }
482
722
  if (!opts.serverOverride) {
483
723
  const available = detectBinary(server.probe);
484
724
  if (!available) {
@@ -575,6 +815,27 @@ async function initializeHandshake(client, cwd) {
575
815
  definition: { linkSupport: false },
576
816
  references: {},
577
817
  publishDiagnostics: { relatedInformation: false },
818
+ // PUGI-78 Phase 1: full 13-tool symbol surface. Each block
819
+ // mirrors the LSP §3.17 dynamic capability advertisement so
820
+ // the server enables the corresponding feature (e.g. pyright
821
+ // gates `workspace/symbol` on this flag). `dynamicRegistration`
822
+ // stays false for every entry — we never register a capability
823
+ // post-initialize; the static block is the only handshake.
824
+ documentSymbol: {
825
+ hierarchicalDocumentSymbolSupport: true,
826
+ symbolKind: { valueSet: Array.from({ length: 26 }, (_, i) => i + 1) },
827
+ },
828
+ signatureHelp: { signatureInformation: { documentationFormat: ['plaintext', 'markdown'] } },
829
+ implementation: { linkSupport: false },
830
+ typeDefinition: { linkSupport: false },
831
+ rename: { prepareSupport: true },
832
+ codeAction: { codeActionLiteralSupport: { codeActionKind: { valueSet: ['', 'quickfix', 'refactor', 'source'] } } },
833
+ formatting: {},
834
+ callHierarchy: {},
835
+ },
836
+ workspace: {
837
+ symbol: { symbolKind: { valueSet: Array.from({ length: 26 }, (_, i) => i + 1) } },
838
+ workspaceEdit: { documentChanges: true },
578
839
  },
579
840
  },
580
841
  workspaceFolders: [{ uri: rootUri, name: 'pugi-workspace' }],
@@ -597,9 +858,9 @@ function detectBinary(name) {
597
858
  }
598
859
  function normalizeHover(raw) {
599
860
  // LSP `Hover.contents` can be:
600
- // - string
601
- // - { kind: 'markdown' | 'plaintext', value: string }
602
- // - Array<string | { language: string, value: string }>
861
+ // - string
862
+ // - { kind: 'markdown' | 'plaintext', value: string }
863
+ // - Array<string | { language: string, value: string }>
603
864
  if (!raw || typeof raw !== 'object') {
604
865
  return { content: String(raw ?? ''), raw };
605
866
  }
@@ -705,6 +966,249 @@ function normalizeDiagnostic(raw) {
705
966
  };
706
967
  return out;
707
968
  }
969
+ /**
970
+ * PUGI-78 Phase 1: collapse a `textDocument/signatureHelp` response to
971
+ * one active overload. LSP returns `{ signatures, activeSignature,
972
+ * activeParameter }`; we surface the active overload's label, params,
973
+ * and the active-param index. Returns null when the server reports no
974
+ * signatures (cursor not at a call site).
975
+ */
976
+ function normalizeSignatureHelp(raw) {
977
+ if (!raw || typeof raw !== 'object')
978
+ return null;
979
+ const obj = raw;
980
+ if (!Array.isArray(obj.signatures) || obj.signatures.length === 0)
981
+ return null;
982
+ const idx = typeof obj.activeSignature === 'number' && obj.activeSignature >= 0
983
+ ? Math.min(obj.activeSignature, obj.signatures.length - 1)
984
+ : 0;
985
+ const sig = obj.signatures[idx];
986
+ if (!sig || typeof sig !== 'object')
987
+ return null;
988
+ const sigObj = sig;
989
+ if (typeof sigObj.label !== 'string')
990
+ return null;
991
+ const params = [];
992
+ if (Array.isArray(sigObj.parameters)) {
993
+ for (const p of sigObj.parameters) {
994
+ if (!p || typeof p !== 'object')
995
+ continue;
996
+ const po = p;
997
+ // LSP allows `label` as string OR [start,end] tuple of offsets;
998
+ // we surface the string form, and for tuple form we slice the
999
+ // signature label.
1000
+ let label = '';
1001
+ if (typeof po.label === 'string')
1002
+ label = po.label;
1003
+ else if (Array.isArray(po.label) && po.label.length === 2 && typeof po.label[0] === 'number' && typeof po.label[1] === 'number') {
1004
+ label = sigObj.label.slice(po.label[0], po.label[1]);
1005
+ }
1006
+ if (!label)
1007
+ continue;
1008
+ const doc = extractMarkupString(po.documentation);
1009
+ params.push({ label, ...(doc ? { documentation: doc } : {}) });
1010
+ }
1011
+ }
1012
+ const docTop = extractMarkupString(sigObj.documentation);
1013
+ const out = {
1014
+ label: sigObj.label,
1015
+ parameters: params,
1016
+ raw,
1017
+ ...(docTop ? { documentation: docTop } : {}),
1018
+ ...(typeof obj.activeParameter === 'number' ? { activeParameter: obj.activeParameter } : {}),
1019
+ };
1020
+ return out;
1021
+ }
1022
+ /**
1023
+ * LSP `MarkupContent | string` -> string. Used by both signature help
1024
+ * (top-level docs + per-param docs) and hover normalization.
1025
+ */
1026
+ function extractMarkupString(raw) {
1027
+ if (typeof raw === 'string')
1028
+ return raw.length > 0 ? raw : undefined;
1029
+ if (raw && typeof raw === 'object' && 'value' in raw) {
1030
+ const value = raw.value;
1031
+ if (typeof value === 'string' && value.length > 0)
1032
+ return value;
1033
+ }
1034
+ return undefined;
1035
+ }
1036
+ /**
1037
+ * PUGI-78 Phase 1: collapse a `textDocument/rename` `WorkspaceEdit` to
1038
+ * the agent-facing flat list. Handles BOTH shapes:
1039
+ * - legacy `changes: { [uri]: TextEdit[] }`
1040
+ * - modern `documentChanges: (TextDocumentEdit | FileOp)[]`
1041
+ *
1042
+ * File-op entries (`CreateFile`, `RenameFile`, `DeleteFile`) are NOT
1043
+ * surfaced in Phase 1 — the only renames Pugi enables out of the box
1044
+ * are symbol-level. Phase 2 wires the file-op variants.
1045
+ */
1046
+ function normalizeWorkspaceEdit(raw, cwd) {
1047
+ if (!raw || typeof raw !== 'object')
1048
+ return null;
1049
+ const obj = raw;
1050
+ const edits = [];
1051
+ const fileSet = new Set();
1052
+ const pushEdit = (uri, edit) => {
1053
+ if (!edit || typeof edit !== 'object')
1054
+ return;
1055
+ const editObj = edit;
1056
+ const range = parseRange(editObj.range);
1057
+ if (!range)
1058
+ return;
1059
+ const newText = typeof editObj.newText === 'string' ? editObj.newText : '';
1060
+ const file = uriToWorkspacePath(uri, cwd);
1061
+ if (!file)
1062
+ return;
1063
+ fileSet.add(file);
1064
+ edits.push({ file, line: range.start.line, character: range.start.character, newText });
1065
+ };
1066
+ if (obj.changes && typeof obj.changes === 'object') {
1067
+ for (const [uri, list] of Object.entries(obj.changes)) {
1068
+ if (!Array.isArray(list))
1069
+ continue;
1070
+ for (const edit of list)
1071
+ pushEdit(uri, edit);
1072
+ }
1073
+ }
1074
+ if (Array.isArray(obj.documentChanges)) {
1075
+ for (const docChange of obj.documentChanges) {
1076
+ if (!docChange || typeof docChange !== 'object')
1077
+ continue;
1078
+ const dc = docChange;
1079
+ // Skip file-op entries — they have a `kind` discriminator.
1080
+ if (typeof dc.kind === 'string' && dc.kind !== '')
1081
+ continue;
1082
+ const td = dc.textDocument;
1083
+ if (!td || typeof td !== 'object')
1084
+ continue;
1085
+ const uri = td.uri;
1086
+ if (typeof uri !== 'string')
1087
+ continue;
1088
+ if (Array.isArray(dc.edits)) {
1089
+ for (const edit of dc.edits)
1090
+ pushEdit(uri, edit);
1091
+ }
1092
+ }
1093
+ }
1094
+ if (fileSet.size === 0)
1095
+ return null;
1096
+ return { files: Array.from(fileSet).sort(), edits, raw };
1097
+ }
1098
+ /**
1099
+ * Convert an LSP `file://` URI to a workspace-relative path. Empty
1100
+ * string when the URI escapes the workspace; the caller decides whether
1101
+ * to drop the edit or surface the raw URI.
1102
+ */
1103
+ function uriToWorkspacePath(uri, cwd) {
1104
+ try {
1105
+ const url = new URL(uri);
1106
+ if (url.protocol !== 'file:')
1107
+ return uri;
1108
+ const abs = decodeURIComponent(url.pathname);
1109
+ if (abs === cwd)
1110
+ return '.';
1111
+ if (abs.startsWith(cwd + sep))
1112
+ return abs.slice(cwd.length + 1);
1113
+ return uri;
1114
+ }
1115
+ catch {
1116
+ return '';
1117
+ }
1118
+ }
1119
+ /**
1120
+ * PUGI-78 Phase 1: parse `textDocument/codeAction` response. Server
1121
+ * returns either `Command[]` or `CodeAction[]`. Both have `title`;
1122
+ * `CodeAction` additionally carries `kind` + `isPreferred` + `edit`.
1123
+ */
1124
+ function normalizeCodeActions(raw) {
1125
+ if (!Array.isArray(raw))
1126
+ return [];
1127
+ const out = [];
1128
+ for (const item of raw) {
1129
+ if (!item || typeof item !== 'object')
1130
+ continue;
1131
+ const obj = item;
1132
+ if (typeof obj.title !== 'string' || obj.title.length === 0)
1133
+ continue;
1134
+ out.push({
1135
+ title: obj.title,
1136
+ ...(typeof obj.kind === 'string' ? { kind: obj.kind } : {}),
1137
+ ...(typeof obj.isPreferred === 'boolean' ? { isPreferred: obj.isPreferred } : {}),
1138
+ });
1139
+ }
1140
+ return out;
1141
+ }
1142
+ /**
1143
+ * PUGI-78 Phase 1: parse a `TextEdit[]` payload returned by the
1144
+ * formatter. Skips rows with missing range or non-string newText.
1145
+ */
1146
+ function normalizeTextEdits(raw) {
1147
+ if (!Array.isArray(raw))
1148
+ return [];
1149
+ const out = [];
1150
+ for (const item of raw) {
1151
+ if (!item || typeof item !== 'object')
1152
+ continue;
1153
+ const obj = item;
1154
+ const range = parseRange(obj.range);
1155
+ if (!range)
1156
+ continue;
1157
+ const newText = typeof obj.newText === 'string' ? obj.newText : '';
1158
+ out.push({ range, newText });
1159
+ }
1160
+ return out;
1161
+ }
1162
+ /**
1163
+ * PUGI-78 Phase 1: collapse `callHierarchy/{incoming,outgoing}Calls`.
1164
+ * The two response shapes share the same `from`/`to` field for the
1165
+ * counterpart item; `fromRanges`/`toRanges` flatten to `callRanges`.
1166
+ */
1167
+ function normalizeCallHierarchyEdges(raw, itemKey, cwd) {
1168
+ if (!Array.isArray(raw))
1169
+ return [];
1170
+ const out = [];
1171
+ const rangeKey = itemKey === 'from' ? 'fromRanges' : 'toRanges';
1172
+ for (const row of raw) {
1173
+ if (!row || typeof row !== 'object')
1174
+ continue;
1175
+ const rowObj = row;
1176
+ const item = rowObj[itemKey];
1177
+ if (!item || typeof item !== 'object')
1178
+ continue;
1179
+ const itemObj = item;
1180
+ if (typeof itemObj.name !== 'string' || itemObj.name.length === 0)
1181
+ continue;
1182
+ const kind = typeof itemObj.kind === 'number' ? itemObj.kind : 0;
1183
+ if (!kind)
1184
+ continue;
1185
+ const uri = typeof itemObj.uri === 'string' ? itemObj.uri : '';
1186
+ if (!uri)
1187
+ continue;
1188
+ const file = uriToWorkspacePath(uri, cwd);
1189
+ const selRange = parseRange(itemObj.selectionRange) ?? parseRange(itemObj.range);
1190
+ if (!selRange)
1191
+ continue;
1192
+ const rangesRaw = rowObj[rangeKey];
1193
+ const callRanges = [];
1194
+ if (Array.isArray(rangesRaw)) {
1195
+ for (const r of rangesRaw) {
1196
+ const parsed = parseRange(r);
1197
+ if (parsed)
1198
+ callRanges.push(parsed);
1199
+ }
1200
+ }
1201
+ out.push({
1202
+ name: itemObj.name,
1203
+ kind,
1204
+ file,
1205
+ line: selRange.start.line,
1206
+ character: selRange.start.character,
1207
+ callRanges,
1208
+ });
1209
+ }
1210
+ return out;
1211
+ }
708
1212
  /**
709
1213
  * Test-only surface so specs can hand-craft an `LspClient` over a mock
710
1214
  * stdio pipe without paying for the real `startLspClient` spawn cost.
@@ -715,5 +1219,11 @@ export const __test__ = {
715
1219
  normalizeHover,
716
1220
  normalizeLocations,
717
1221
  normalizeDiagnostic,
1222
+ normalizeSignatureHelp,
1223
+ normalizeWorkspaceEdit,
1224
+ normalizeCodeActions,
1225
+ normalizeTextEdits,
1226
+ normalizeCallHierarchyEdges,
1227
+ uriToWorkspacePath,
718
1228
  };
719
1229
  //# sourceMappingURL=client.js.map