@iowarp/clio-coder 0.3.1 → 0.3.3

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 (601) hide show
  1. package/CHANGELOG.md +233 -373
  2. package/CONTRIBUTING.md +23 -23
  3. package/README.md +284 -613
  4. package/dist/{acp-FPR54DGL.js → acp-P2AQILE2.js} +43 -53
  5. package/dist/{agents-OGPIHPJH.js → agents-72W3BI7I.js} +43 -26
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-IC3K6NIZ.js → auth-5TWEIYDN.js} +20 -12
  8. package/dist/chunk-2DJ2KNFG.js +2095 -0
  9. package/dist/{chunk-IS3ONKU3.js → chunk-2IR2NMPA.js} +6 -4
  10. package/dist/chunk-2SFS6XQE.js +122 -0
  11. package/dist/{chunk-PV4JUBVJ.js → chunk-2TLUCQVG.js} +40 -21
  12. package/dist/chunk-2VTFPG5O.js +48 -0
  13. package/dist/chunk-4BJ5BYCE.js +61 -0
  14. package/dist/{chunk-474KN5II.js → chunk-4BPJXDWC.js} +111 -181
  15. package/dist/chunk-4VP4KH3K.js +962 -0
  16. package/dist/chunk-4XUGQOHA.js +797 -0
  17. package/dist/chunk-4ZG3XFUR.js +77 -0
  18. package/dist/chunk-5B2AEOW5.js +5407 -0
  19. package/dist/{chunk-K2ITRMHZ.js → chunk-5TSRNF4G.js} +6 -138
  20. package/dist/{chunk-4QKXUHSR.js → chunk-5UFT4SUX.js} +70 -20
  21. package/dist/{chunk-OLBBMFRD.js → chunk-5UUP6MWO.js} +24 -62
  22. package/dist/chunk-65DEGPJ6.js +52 -0
  23. package/dist/chunk-6EJMN2Y3.js +17 -0
  24. package/dist/chunk-6N5PTWMY.js +136 -0
  25. package/dist/chunk-6SGHMWE3.js +277 -0
  26. package/dist/chunk-6XLNIQDB.js +27 -0
  27. package/dist/chunk-7CR24IG7.js +242 -0
  28. package/dist/chunk-7MNJORFF.js +22 -0
  29. package/dist/{chunk-KY56HMHH.js → chunk-A3CYT5EX.js} +125 -31
  30. package/dist/chunk-AGYYIBLL.js +1069 -0
  31. package/dist/{chunk-GB6QRBXN.js → chunk-APJ265NV.js} +54 -1187
  32. package/dist/{chunk-673JJUWJ.js → chunk-BMEMKKIT.js} +2 -2
  33. package/dist/chunk-CBCAPZAA.js +229 -0
  34. package/dist/chunk-CMZWFGD2.js +352 -0
  35. package/dist/chunk-COU2UHX6.js +400 -0
  36. package/dist/chunk-DSELYM6W.js +1077 -0
  37. package/dist/chunk-DUYJ5IO6.js +644 -0
  38. package/dist/chunk-ECH6PKUQ.js +39 -0
  39. package/dist/chunk-ED4KHGC3.js +143 -0
  40. package/dist/chunk-EKMEHE4H.js +340 -0
  41. package/dist/chunk-FCSXB6T2.js +338 -0
  42. package/dist/chunk-FJ3H4MN5.js +48 -0
  43. package/dist/{chunk-RPTR2H26.js → chunk-FNTMWMX5.js} +21 -15
  44. package/dist/chunk-FQ4SKYE4.js +29 -0
  45. package/dist/chunk-G4BMMOKF.js +182 -0
  46. package/dist/{chunk-ZPY3JZ5E.js → chunk-GGXXDWE4.js} +183 -1233
  47. package/dist/chunk-HC4CLZ2Y.js +68 -0
  48. package/dist/{chunk-LU4TK2PR.js → chunk-HFSBBKSQ.js} +5 -56
  49. package/dist/{chunk-PIUMUEMV.js → chunk-HKIYEGME.js} +10 -6
  50. package/dist/chunk-I4HZDVNP.js +73 -0
  51. package/dist/chunk-IKCO5N3L.js +162 -0
  52. package/dist/chunk-IR4CFBFN.js +56 -0
  53. package/dist/{chunk-R5KLMSBV.js → chunk-J5Q24KAG.js} +2 -2
  54. package/dist/chunk-J7CWMCQD.js +255 -0
  55. package/dist/{chunk-K5XEMXTI.js → chunk-JVCV3ICN.js} +1 -1
  56. package/dist/chunk-KZWTDYJF.js +217 -0
  57. package/dist/chunk-LBMZMYH2.js +285 -0
  58. package/dist/{chunk-G34LV2PF.js → chunk-LM5TQCJZ.js} +84 -170
  59. package/dist/chunk-LW6DSM3M.js +5135 -0
  60. package/dist/chunk-LWLEKMDQ.js +3482 -0
  61. package/dist/{chunk-H6F6BYOH.js → chunk-LZSJBIVT.js} +7003 -7434
  62. package/dist/{chunk-HQQID6OA.js → chunk-M6SHUN7Q.js} +5 -5
  63. package/dist/{chunk-FST4FYJB.js → chunk-MFFY33HR.js} +99 -140
  64. package/dist/{chunk-BSU2YIWB.js → chunk-MVVUPGPW.js} +131 -136
  65. package/dist/chunk-OAO4GE4M.js +619 -0
  66. package/dist/{chunk-Q3RUPKEJ.js → chunk-OC7FIQPC.js} +58 -189
  67. package/dist/chunk-OKGUZO2U.js +34 -0
  68. package/dist/{chunk-GAEBEQVI.js → chunk-OOJYHWRB.js} +32 -346
  69. package/dist/{chunk-Q5WJOSJ7.js → chunk-OQ33BKR3.js} +2 -1
  70. package/dist/chunk-OQE5J4C6.js +73 -0
  71. package/dist/{chunk-KKNLWXI6.js → chunk-ORBHGJC5.js} +8 -8
  72. package/dist/{chunk-MAR7Y6HW.js → chunk-PAJK6MAQ.js} +23 -16
  73. package/dist/{chunk-M5T5VO65.js → chunk-PIWWS5BL.js} +837 -635
  74. package/dist/chunk-POHLU5DW.js +1186 -0
  75. package/dist/chunk-QKMUKYO7.js +4961 -0
  76. package/dist/chunk-SRDMMSEP.js +16405 -0
  77. package/dist/chunk-SST6Z5JA.js +80 -0
  78. package/dist/chunk-STBPMHSX.js +2456 -0
  79. package/dist/chunk-T6YILFSB.js +80 -0
  80. package/dist/chunk-TZK7PACC.js +174 -0
  81. package/dist/chunk-TZTZS7QK.js +227 -0
  82. package/dist/{chunk-ASND7OZK.js → chunk-UFIIWP2H.js} +13 -13
  83. package/dist/chunk-UOV2BYIW.js +107 -0
  84. package/dist/{chunk-PFEFKVGL.js → chunk-V6RTAOC2.js} +13 -11
  85. package/dist/chunk-VAKQQHWR.js +434 -0
  86. package/dist/chunk-VG7TBQIY.js +128 -0
  87. package/dist/chunk-VJWL6YS5.js +244 -0
  88. package/dist/{chunk-EYOKLTMF.js → chunk-VPAYEGVX.js} +17 -3
  89. package/dist/chunk-WEH5XRJQ.js +32 -0
  90. package/dist/chunk-X4RCMKVQ.js +641 -0
  91. package/dist/{chunk-TEKV33Q5.js → chunk-X6IAEBZR.js} +65 -33
  92. package/dist/chunk-XBXAASKX.js +18 -0
  93. package/dist/chunk-XN3L4EYL.js +46 -0
  94. package/dist/{chunk-RDLVBZEO.js → chunk-YCWGATWI.js} +6 -4
  95. package/dist/chunk-YHZX5GEU.js +193 -0
  96. package/dist/chunk-YXLYO42X.js +91 -0
  97. package/dist/{chunk-NMOX6HFD.js → chunk-ZDOOVTXZ.js} +29 -77
  98. package/dist/chunk-ZI647VB5.js +37 -0
  99. package/dist/{chunk-C4PTHK7P.js → chunk-ZWLZP4ZT.js} +5 -5
  100. package/dist/chunk-ZWMF7253.js +1882 -0
  101. package/dist/cli/index.js +62 -54
  102. package/dist/clio-JOU4FXVA.js +25 -0
  103. package/dist/code-nav-7AX6FYE6.js +600 -0
  104. package/dist/codewiki/build-worker.js +66 -0
  105. package/dist/compile-cache-CVJMMODC.js +18 -0
  106. package/dist/{components-DMAOEKFB.js → components-KELWS457.js} +11 -6
  107. package/dist/{config-IRUQ7SE4.js → config-XCDVKR23.js} +92 -55
  108. package/dist/configure-4GAP54ZW.js +42 -0
  109. package/dist/{context-5RADCKTR.js → context-4UOGGLQ5.js} +71 -35
  110. package/dist/context-5VKGUVJJ.js +866 -0
  111. package/dist/{context-3KWFLHJG.js → context-77FM5DV5.js} +15 -13
  112. package/dist/{context-clear-7TSNPAAI.js → context-clear-XXJRLCJJ.js} +54 -28
  113. package/dist/{context-index-W4RLWOQH.js → context-index-BZ4UYMTC.js} +30 -24
  114. package/dist/dispatch-runner-QPRDDBDX.js +1997 -0
  115. package/dist/{docs-5AWSPS37.js → docs-2C2LTVT2.js} +23 -10
  116. package/dist/{doctor-UC5NAJYQ.js → doctor-HR46URBJ.js} +27 -17
  117. package/dist/{eval-U6TJHRLX.js → eval-XSSNATB4.js} +29 -16
  118. package/dist/{evidence-YEGUW4L3.js → evidence-6HG2PY2B.js} +46 -26
  119. package/dist/{evolve-TXARCTPG.js → evolve-K7YU3NCY.js} +45 -25
  120. package/dist/{extensions-OZFJ3A3G.js → extensions-QVDOHDGJ.js} +16 -7
  121. package/dist/{fleet-6G3DHNYE.js → fleet-VY3HHKN6.js} +163 -54
  122. package/dist/{fleet-preflight-DSNT37JK.js → fleet-preflight-DDN536IT.js} +7 -4
  123. package/dist/{init-KZ5QTF6M.js → init-JYGXI3FK.js} +69 -32
  124. package/dist/{memory-73ESV5YC.js → memory-WFZMGYHX.js} +48 -27
  125. package/dist/{models-A4PVNWJK.js → models-I5QWSEOM.js} +39 -25
  126. package/dist/monitor-GE4ID3IA.js +661 -0
  127. package/dist/{chunk-FCIH3BIZ.js → orchestrator-EM5MC3HM.js} +15979 -12407
  128. package/dist/{paths-C4H6IV77.js → paths-UXLN5YYZ.js} +10 -5
  129. package/dist/{preload-6WVMHX3A.js → preload-P6DGH2PZ.js} +2 -2
  130. package/dist/{reset-BGW6OGMV.js → reset-L2FQEE3E.js} +16 -10
  131. package/dist/{run-YTPEYQOH.js → run-ZU3QMZPZ.js} +101 -61
  132. package/dist/{share-YIFFV4NQ.js → share-S5BZQC5I.js} +15 -8
  133. package/dist/{skills-2V6RA3OQ.js → skills-X5VXCRNQ.js} +34 -14
  134. package/dist/{skills-eval-S2TVJO4F.js → skills-eval-WKIHWTHR.js} +70 -34
  135. package/dist/steer-GGWFUJUD.js +77 -0
  136. package/dist/{targets-TYXLPB23.js → targets-SNCPI2NR.js} +43 -27
  137. package/dist/terminal-lease-BNAHVHBS.js +395 -0
  138. package/dist/{trace-GGOJ6Q6Z.js → trace-PNCASAXC.js} +41 -16
  139. package/dist/{chunk-N6F52NLF.js → tree-sitter-HGKH6LG4.js} +28 -2306
  140. package/dist/{uninstall-LLLT4F4W.js → uninstall-FZCQCDKC.js} +10 -5
  141. package/dist/{upgrade-33G2LMM5.js → upgrade-JQHHPQ4K.js} +45 -25
  142. package/dist/{usage-ZAFSXKKG.js → usage-OR4O5SMZ.js} +62 -31
  143. package/dist/verify-375KUB3Y.js +716 -0
  144. package/dist/web-fetch-2YHJ3KTG.js +638 -0
  145. package/dist/{wiki-generate-NUQCVOQ3.js → wiki-generate-UEXP2ARI.js} +74 -34
  146. package/dist/worker/entry.js +221 -36
  147. package/dist/workspace-G4ZWUIPR.js +22 -0
  148. package/docs/README.md +22 -17
  149. package/docs/acp.md +168 -16
  150. package/docs/alcf-provider.md +1 -1
  151. package/docs/architecture.md +136 -7
  152. package/docs/artifact-versions.md +1 -1
  153. package/docs/built-in-agents.md +1 -1
  154. package/docs/capacity-and-scheduling.md +1 -1
  155. package/docs/commands-and-modes.md +114 -71
  156. package/docs/config-knobs-audit.md +1 -3
  157. package/docs/configuration-and-targets.md +174 -46
  158. package/docs/context-engine.md +29 -6
  159. package/docs/development-pipeline.md +26 -1
  160. package/docs/dispatch-architecture-rationale.md +1 -1
  161. package/docs/documentation-coverage.md +2 -2
  162. package/docs/documentation-guide.md +1 -1
  163. package/docs/environment-variables.md +13 -5
  164. package/docs/eval-runner.md +1 -1
  165. package/docs/evals-internal.md +1 -1
  166. package/docs/evidence-and-memory.md +6 -2
  167. package/docs/evolution.md +2 -2
  168. package/docs/exit-codes-and-output.md +15 -9
  169. package/docs/extensions-and-sharing.md +9 -9
  170. package/docs/fleet-dispatch.md +7 -5
  171. package/docs/git-commit-provenance.md +120 -0
  172. package/docs/glossary.md +1 -1
  173. package/docs/installation-and-lifecycle.md +34 -27
  174. package/docs/middleware-and-components.md +1 -1
  175. package/docs/model-catalog.md +45 -14
  176. package/docs/observability.md +8 -5
  177. package/docs/performance-methodology.md +491 -0
  178. package/docs/pi-boundary.md +72 -0
  179. package/docs/proactive-memory.md +3 -3
  180. package/docs/prompt-envelope-and-tools.md +24 -3
  181. package/docs/provider-adapter-cookbook.md +57 -4
  182. package/docs/release-cut-checklist.md +129 -115
  183. package/docs/safety-model.md +9 -5
  184. package/docs/scientific-validation.md +3 -3
  185. package/docs/session-lifecycle.md +55 -12
  186. package/docs/skills-marketplace.md +12 -8
  187. package/docs/time-conventions.md +1 -1
  188. package/docs/tool-usage.md +3 -3
  189. package/docs/trace-store.md +1 -1
  190. package/docs/troubleshooting.md +10 -7
  191. package/docs/tui-design.md +47 -10
  192. package/docs/worker-dispatch-mechanics.md +1 -1
  193. package/package.json +19 -22
  194. package/skills/coding/ast-grep/SKILL.md +136 -0
  195. package/skills/coding/ast-grep/evals.md +56 -0
  196. package/skills/coding/ast-grep/references/rule_reference.md +297 -0
  197. package/skills/coding/coding-standards/SKILL.md +113 -0
  198. package/skills/coding/coding-standards/evals.md +34 -0
  199. package/skills/coding/prototype/SKILL.md +86 -0
  200. package/skills/coding/prototype/evals.md +42 -0
  201. package/skills/coding/prototype/references/LOGIC.md +67 -0
  202. package/skills/coding/prototype/references/UI.md +112 -0
  203. package/skills/coding/tdd/SKILL.md +101 -0
  204. package/skills/coding/tdd/evals.md +41 -0
  205. package/skills/coding/tdd/references/mocking.md +59 -0
  206. package/skills/coding/tdd/references/tests.md +77 -0
  207. package/skills/context/context-handoff/SKILL.md +126 -0
  208. package/skills/context/context-handoff/evals.md +57 -0
  209. package/skills/context/context-handoff/scripts/new-handoff.sh +26 -0
  210. package/skills/context/context-prime/SKILL.md +95 -0
  211. package/skills/context/context-prime/evals.md +54 -0
  212. package/skills/meta/clio-dev/SKILL.md +91 -0
  213. package/skills/meta/clio-dev/evals.md +45 -0
  214. package/skills/meta/clio-test/SKILL.md +130 -0
  215. package/skills/meta/clio-test/evals.md +43 -0
  216. package/skills/meta/clio-test/references/harness.md +97 -0
  217. package/skills/meta/clio-test/references/test-map.md +59 -0
  218. package/skills/meta/credentials/SKILL.md +125 -0
  219. package/skills/meta/credentials/evals.md +104 -0
  220. package/skills/meta/find-skills/SKILL.md +72 -0
  221. package/skills/meta/find-skills/evals.md +47 -0
  222. package/skills/meta/herdr/SKILL.md +127 -0
  223. package/skills/meta/herdr/evals.md +38 -0
  224. package/skills/meta/skill-craft/SKILL.md +102 -0
  225. package/skills/meta/skill-craft/evals.md +41 -0
  226. package/skills/planning/architecture/SKILL.md +129 -0
  227. package/skills/planning/architecture/evals.md +36 -0
  228. package/skills/planning/backlog/SKILL.md +90 -0
  229. package/skills/planning/backlog/evals.md +43 -0
  230. package/skills/planning/prd/SKILL.md +82 -0
  231. package/skills/planning/prd/evals.md +49 -0
  232. package/skills/planning/product-intent/SKILL.md +112 -0
  233. package/skills/planning/product-intent/evals.md +36 -0
  234. package/skills/planning/tech-spec/SKILL.md +115 -0
  235. package/skills/planning/tech-spec/evals.md +47 -0
  236. package/skills/registry.yaml +136 -0
  237. package/skills/research/arxiv-literature/SKILL.md +104 -0
  238. package/skills/research/arxiv-literature/evals.md +58 -0
  239. package/skills/research/experiment-protocol/SKILL.md +122 -0
  240. package/skills/research/experiment-protocol/evals.md +91 -0
  241. package/skills/research/scientific-debugging/SKILL.md +119 -0
  242. package/skills/research/scientific-debugging/evals.md +138 -0
  243. package/skills/research/scientific-modernization/SKILL.md +138 -0
  244. package/skills/research/scientific-modernization/evals.md +84 -0
  245. package/skills/workflow/design-council/SKILL.md +139 -0
  246. package/skills/workflow/design-council/evals.md +97 -0
  247. package/skills/workflow/grill-me/SKILL.md +186 -0
  248. package/skills/workflow/grill-me/evals.md +78 -0
  249. package/skills/workflow/workflow-distiller/SKILL.md +136 -0
  250. package/skills/workflow/workflow-distiller/evals.md +107 -0
  251. package/src/cli/acp.ts +31 -4
  252. package/src/cli/clio.ts +68 -6
  253. package/src/cli/config-inspect.ts +28 -22
  254. package/src/cli/configure.ts +47 -9
  255. package/src/cli/context-clear.ts +2 -2
  256. package/src/cli/context-index.ts +21 -23
  257. package/src/cli/context.ts +13 -8
  258. package/src/cli/default-target.ts +9 -17
  259. package/src/cli/docs.ts +11 -5
  260. package/src/cli/evidence.ts +4 -1
  261. package/src/cli/extensions.ts +10 -1
  262. package/src/cli/fleet.ts +47 -6
  263. package/src/cli/index.ts +55 -26
  264. package/src/cli/memory.ts +3 -1
  265. package/src/cli/models.ts +1 -1
  266. package/src/cli/modes/json-stream.ts +37 -1
  267. package/src/cli/modes/print.ts +24 -9
  268. package/src/cli/run.ts +2 -2
  269. package/src/cli/skills-eval.ts +23 -8
  270. package/src/cli/skills.ts +19 -4
  271. package/src/cli/targets.ts +4 -0
  272. package/src/cli/text-layout.ts +15 -5
  273. package/src/cli/trace.ts +62 -14
  274. package/src/cli/upgrade.ts +18 -2
  275. package/src/cli/usage.ts +10 -3
  276. package/src/cli/wiki-generate.ts +2 -1
  277. package/src/core/agent-environment.ts +7 -0
  278. package/src/core/bash-exec.ts +72 -1
  279. package/src/core/boot-trace.ts +9 -4
  280. package/src/core/bus-events.ts +20 -4
  281. package/src/core/commit-attribution.ts +157 -0
  282. package/src/core/compile-cache.ts +159 -0
  283. package/src/core/config.ts +131 -2
  284. package/src/core/defaults.ts +39 -5
  285. package/src/core/domain-loader.ts +12 -5
  286. package/src/core/git-commit-attribution.ts +387 -0
  287. package/src/core/incomplete-installation.ts +45 -0
  288. package/src/core/response-schema.ts +1 -1
  289. package/src/core/safe-exec.ts +13 -1
  290. package/src/core/settings-layers.ts +155 -21
  291. package/src/core/skill-activation.ts +1 -1
  292. package/src/core/startup-timer.ts +3 -3
  293. package/src/core/state-file-lock.ts +13 -1
  294. package/src/core/termination.ts +78 -5
  295. package/src/domains/config/classify.ts +15 -3
  296. package/src/domains/config/extension.ts +19 -13
  297. package/src/domains/config/index.ts +10 -0
  298. package/src/domains/config/keybindings.ts +45 -9
  299. package/src/domains/context/bootstrap-prompt.ts +1 -1
  300. package/src/domains/context/bootstrap.ts +111 -18
  301. package/src/domains/context/clear.ts +16 -11
  302. package/src/domains/context/clio-md.ts +111 -9
  303. package/src/domains/context/codewiki/artifact.ts +400 -0
  304. package/src/domains/context/codewiki/build-worker-protocol.ts +24 -0
  305. package/src/domains/context/codewiki/build-worker.ts +54 -0
  306. package/src/domains/context/codewiki/coordinator.ts +182 -0
  307. package/src/domains/context/codewiki/indexer.ts +59 -144
  308. package/src/domains/context/codewiki/paths.ts +67 -0
  309. package/src/domains/context/codewiki/schema.ts +80 -0
  310. package/src/domains/context/codewiki/tree-sitter.ts +1 -1
  311. package/src/domains/context/contract.ts +11 -5
  312. package/src/domains/context/extension.ts +94 -143
  313. package/src/domains/context/fingerprint.ts +3 -1
  314. package/src/domains/context/index.ts +12 -22
  315. package/src/domains/context/project-metadata.ts +19 -0
  316. package/src/domains/context/prompt-context.ts +9 -10
  317. package/src/domains/context/refresh.ts +29 -21
  318. package/src/domains/context/runtime.ts +17 -0
  319. package/src/domains/context/wiki/generate.ts +39 -34
  320. package/src/domains/context/wiki/plan.ts +1 -1
  321. package/src/domains/context/wiki/prompts.ts +21 -8
  322. package/src/domains/dispatch/code-step.ts +20 -1
  323. package/src/domains/dispatch/extension.ts +158 -21
  324. package/src/domains/dispatch/failure-classification.ts +6 -0
  325. package/src/domains/dispatch/fleet-commit-attribution.ts +56 -0
  326. package/src/domains/dispatch/orphan-recovery.ts +50 -8
  327. package/src/domains/dispatch/receipt-integrity.ts +5 -0
  328. package/src/domains/dispatch/state.ts +31 -6
  329. package/src/domains/dispatch/transport.ts +2 -1
  330. package/src/domains/dispatch/types.ts +14 -0
  331. package/src/domains/dispatch/worker-spawn.ts +21 -2
  332. package/src/domains/eval/metrics/context.ts +1 -1
  333. package/src/domains/eval/types.ts +0 -1
  334. package/src/domains/evidence/build.ts +41 -1
  335. package/src/domains/lifecycle/migrations/2026-08-18-lmstudio-runtime-id.ts +52 -0
  336. package/src/domains/lifecycle/migrations/index.ts +24 -4
  337. package/src/domains/middleware/hooks-io.ts +12 -0
  338. package/src/domains/middleware/skills-reminder.ts +30 -15
  339. package/src/domains/prompts/compiler.ts +142 -84
  340. package/src/domains/prompts/contract.ts +18 -2
  341. package/src/domains/prompts/extension.ts +39 -7
  342. package/src/domains/prompts/fragment-loader.ts +0 -1
  343. package/src/domains/prompts/fragments/identity/clio.md +2 -4
  344. package/src/domains/prompts/fragments/identity/docs-routing.md +10 -0
  345. package/src/domains/prompts/fragments/identity/self-awareness.md +1 -45
  346. package/src/domains/prompts/fragments/operating/contract.md +4 -50
  347. package/src/domains/prompts/fragments/operating/delegation.md +42 -0
  348. package/src/domains/prompts/fragments/operating/skills.md +26 -0
  349. package/src/domains/prompts/fragments/operating/worker.md +16 -0
  350. package/src/domains/prompts/fragments/safety/auto-edit.md +5 -5
  351. package/src/domains/prompts/fragments/safety/full-auto.md +3 -3
  352. package/src/domains/prompts/fragments/safety/read-only.md +4 -4
  353. package/src/domains/prompts/fragments/safety/suggest.md +2 -2
  354. package/src/domains/prompts/fragments/wiki/page.md +10 -0
  355. package/src/domains/prompts/fragments/wiki/plan.md +10 -0
  356. package/src/domains/prompts/preload.ts +3 -3
  357. package/src/domains/providers/auth/api-key.ts +1 -1
  358. package/src/domains/providers/auth/backend-file.ts +20 -10
  359. package/src/domains/providers/auth/backend-memory.ts +59 -4
  360. package/src/domains/providers/auth/boot-status.ts +65 -0
  361. package/src/domains/providers/auth/oauth.ts +2 -1
  362. package/src/domains/providers/auth/storage.ts +97 -38
  363. package/src/domains/providers/capabilities.ts +12 -4
  364. package/src/domains/providers/contract.ts +15 -4
  365. package/src/domains/providers/extension.ts +18 -6
  366. package/src/domains/providers/model-runtime-capabilities.ts +15 -4
  367. package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +118 -35
  368. package/src/domains/providers/plugins.ts +5 -3
  369. package/src/domains/providers/probe/fingerprint.ts +25 -5
  370. package/src/domains/providers/registry.ts +31 -10
  371. package/src/domains/providers/runtimes/boot-manifest.ts +55 -0
  372. package/src/domains/providers/runtimes/builtins.ts +2 -2
  373. package/src/domains/providers/runtimes/common/lmstudio-http.ts +423 -0
  374. package/src/domains/providers/runtimes/common/local-synth.ts +6 -7
  375. package/src/domains/providers/runtimes/local-native/lmstudio.ts +241 -0
  376. package/src/domains/providers/support.ts +6 -3
  377. package/src/domains/providers/types/local-model-quirks.ts +7 -9
  378. package/src/domains/providers/types/runtime-descriptor.ts +12 -1
  379. package/src/domains/providers/types/target-descriptor.ts +22 -0
  380. package/src/domains/resources/contract.ts +0 -1
  381. package/src/domains/resources/extension.ts +1 -3
  382. package/src/domains/resources/loader.ts +3 -4
  383. package/src/domains/resources/prompts/loader.ts +16 -2
  384. package/src/domains/resources/prompts/substitute.ts +1 -65
  385. package/src/domains/resources/skills/content-hash.ts +2 -0
  386. package/src/domains/resources/skills/install.ts +17 -0
  387. package/src/domains/resources/skills/loader.ts +17 -10
  388. package/src/domains/resources/skills/marketplace.ts +55 -9
  389. package/src/domains/safety/action-classifier.ts +4 -2
  390. package/src/domains/safety/audit.ts +8 -2
  391. package/src/domains/safety/extension.ts +1 -1
  392. package/src/domains/session/compaction/branch-summary.ts +3 -2
  393. package/src/domains/session/compaction/cut-point.ts +2 -1
  394. package/src/domains/session/compaction/tokens.ts +2 -1
  395. package/src/domains/session/context-ledger.ts +14 -0
  396. package/src/domains/session/contract.ts +15 -0
  397. package/src/domains/session/decision-board.ts +190 -0
  398. package/src/domains/session/entries.ts +66 -3
  399. package/src/domains/session/extension.ts +93 -12
  400. package/src/domains/session/retry.ts +10 -18
  401. package/src/domains/session/session-artifacts.ts +107 -0
  402. package/src/domains/session/task-board.ts +207 -13
  403. package/src/domains/session/tree/active-path.ts +44 -5
  404. package/src/domains/session/tree/fork.ts +26 -27
  405. package/src/domains/session/tree/preview.ts +2 -2
  406. package/src/domains/session/workspace/git-probe.ts +17 -11
  407. package/src/domains/user-tasks/store.ts +297 -0
  408. package/src/engine/acp/errors.ts +96 -0
  409. package/src/engine/acp/server.ts +1728 -146
  410. package/src/engine/acp/transport.ts +135 -14
  411. package/src/engine/acp/types.ts +26 -0
  412. package/src/engine/agent.ts +3 -3
  413. package/src/engine/ai.ts +32 -27
  414. package/src/engine/alcf-oauth.ts +26 -19
  415. package/src/engine/api-registry.ts +223 -0
  416. package/src/engine/apis/index.ts +3 -7
  417. package/src/engine/apis/llamacpp-residency.ts +49 -9
  418. package/src/engine/apis/lmstudio-residency.ts +5 -21
  419. package/src/engine/apis/lmstudio.ts +243 -0
  420. package/src/engine/apis/ollama-native.ts +24 -3
  421. package/src/engine/apis/openai-completions.ts +170 -91
  422. package/src/engine/apis/residency.ts +139 -3
  423. package/src/engine/apis/types.ts +16 -0
  424. package/src/engine/env-api-keys.ts +98 -0
  425. package/src/engine/gemma-channel-filter.ts +223 -0
  426. package/src/engine/instrumented-tui.ts +192 -0
  427. package/src/engine/messages.ts +14 -0
  428. package/src/engine/models.ts +42 -0
  429. package/src/engine/oauth.ts +16 -12
  430. package/src/engine/prompt-templates.ts +1 -0
  431. package/src/engine/provider-payload.ts +16 -59
  432. package/src/engine/strip-tokenizer-sentinels.ts +1 -1
  433. package/src/engine/truncate.ts +9 -0
  434. package/src/engine/tui.ts +17 -9
  435. package/src/engine/types.ts +3 -6
  436. package/src/engine/worker-runtime-capabilities.ts +5 -0
  437. package/src/engine/worker-runtime.ts +1 -1
  438. package/src/engine/worker-tools.ts +9 -4
  439. package/src/entry/boot-options.ts +50 -0
  440. package/src/entry/orchestrator.ts +288 -150
  441. package/src/interactive/application-controller.ts +89 -2
  442. package/src/interactive/chat-loop.ts +266 -41
  443. package/src/interactive/chat-panel.ts +715 -278
  444. package/src/interactive/chat-renderer.ts +306 -85
  445. package/src/interactive/clio-editor.ts +3 -8
  446. package/src/interactive/command-fallbacks.ts +2 -2
  447. package/src/interactive/context-overlay.ts +27 -1
  448. package/src/interactive/editor-submit.ts +253 -24
  449. package/src/interactive/export-html/ansi-to-html.ts +161 -0
  450. package/src/interactive/export-html/index.ts +51 -0
  451. package/src/interactive/export-html/template.ts +45 -0
  452. package/src/interactive/export-html/tool-renderer.ts +54 -0
  453. package/src/interactive/footer/dashboard.ts +4 -0
  454. package/src/interactive/footer/notifications.ts +1 -1
  455. package/src/interactive/footer/widgets.ts +42 -22
  456. package/src/interactive/footer-panel.ts +8 -3
  457. package/src/interactive/format-time.ts +14 -2
  458. package/src/interactive/interactive-application.ts +203 -17
  459. package/src/interactive/interactive-event-projection.ts +18 -1
  460. package/src/interactive/interactive-input-runtime.ts +50 -4
  461. package/src/interactive/interactive-presentation.ts +151 -19
  462. package/src/interactive/interactive-shell.ts +268 -14
  463. package/src/interactive/interactive-slash-runtime.ts +184 -117
  464. package/src/interactive/interactive-tickers.ts +38 -7
  465. package/src/interactive/keybinding-manager.ts +1 -1
  466. package/src/interactive/layout.ts +40 -3
  467. package/src/interactive/overlay-frame.ts +1 -1
  468. package/src/interactive/overlay-general-openers.ts +58 -1
  469. package/src/interactive/overlay-key-routing.ts +3 -0
  470. package/src/interactive/overlay-lifecycle.ts +13 -0
  471. package/src/interactive/overlay-permission-lifecycle.ts +2 -1
  472. package/src/interactive/overlay-session-lifecycle.ts +69 -12
  473. package/src/interactive/overlays/ask-user.ts +146 -24
  474. package/src/interactive/overlays/decisions.ts +300 -0
  475. package/src/interactive/overlays/help-reference.ts +15 -10
  476. package/src/interactive/overlays/model-selector.ts +34 -16
  477. package/src/interactive/overlays/session-selector.ts +18 -0
  478. package/src/interactive/overlays/settings.ts +105 -17
  479. package/src/interactive/overlays/skills-hub.ts +4 -4
  480. package/src/interactive/overlays/tree-selector.ts +41 -6
  481. package/src/interactive/render-trace.ts +499 -90
  482. package/src/interactive/renderers/compaction-summary.ts +2 -2
  483. package/src/interactive/renderers/diff.ts +115 -104
  484. package/src/interactive/renderers/mermaid.ts +53 -0
  485. package/src/interactive/renderers/tool-execution.ts +516 -168
  486. package/src/interactive/renderers/worker-entry.ts +20 -4
  487. package/src/interactive/session-switch-settlement.ts +10 -0
  488. package/src/interactive/slash-autocomplete.ts +6 -114
  489. package/src/interactive/slash-commands.ts +135 -47
  490. package/src/interactive/slash-spec.ts +9 -38
  491. package/src/interactive/status/controller.ts +5 -1
  492. package/src/interactive/status/index.ts +12 -1
  493. package/src/interactive/status/reasoning.ts +87 -0
  494. package/src/interactive/status/summary.ts +13 -2
  495. package/src/interactive/stdout-backpressure.ts +99 -0
  496. package/src/interactive/stream-pacer.ts +530 -0
  497. package/src/interactive/stream-pacing-policy.ts +66 -0
  498. package/src/interactive/tasks-overlay.ts +368 -14
  499. package/src/interactive/terminal-lease.ts +485 -0
  500. package/src/interactive/theme/tokens.ts +1 -1
  501. package/src/interactive/transcript-detail.ts +120 -0
  502. package/src/interactive/turn-context.ts +4 -3
  503. package/src/interactive/turn-persistence.ts +30 -13
  504. package/src/interactive/turn-queues.ts +12 -0
  505. package/src/interactive/turn-recovery.ts +25 -8
  506. package/src/interactive/turn-runtime.ts +79 -12
  507. package/src/interactive/turn-state.ts +10 -0
  508. package/src/interactive/view/artifacts.ts +114 -4
  509. package/src/interactive/view/view-overlay.ts +3 -0
  510. package/src/interactive/welcome-dashboard.ts +17 -16
  511. package/src/interactive/worker-receipts.ts +52 -3
  512. package/src/interactive/worker-stream.ts +5 -1
  513. package/src/tools/agent-tools.ts +23 -3
  514. package/src/tools/artifact.ts +2 -2
  515. package/src/tools/ask-user.ts +23 -13
  516. package/src/tools/bash.ts +30 -2
  517. package/src/tools/bootstrap.ts +34 -431
  518. package/src/tools/builtin-tool-catalog.ts +271 -0
  519. package/src/tools/codewiki/code-nav-surface.ts +29 -0
  520. package/src/tools/codewiki/code-nav.ts +8 -22
  521. package/src/tools/codewiki/shared.ts +41 -38
  522. package/src/tools/context/docs-engine.ts +14 -3
  523. package/src/tools/context/index.ts +107 -28
  524. package/src/tools/context/surface.ts +19 -0
  525. package/src/tools/core-bootstrap.ts +168 -0
  526. package/src/tools/credential-present.ts +5 -5
  527. package/src/tools/dispatch-admission.ts +533 -0
  528. package/src/tools/dispatch-background.ts +54 -0
  529. package/src/tools/dispatch-event-text.ts +6 -0
  530. package/src/tools/dispatch-plan.ts +9 -4
  531. package/src/tools/dispatch-run-events.ts +238 -0
  532. package/src/tools/dispatch-runner.ts +2370 -0
  533. package/src/tools/dispatch-scout-admission.ts +295 -0
  534. package/src/tools/dispatch-types.ts +77 -0
  535. package/src/tools/dispatch.ts +67 -3161
  536. package/src/tools/find.ts +4 -2
  537. package/src/tools/grep.ts +2 -2
  538. package/src/tools/lazy-tool.ts +60 -0
  539. package/src/tools/ledger.ts +3 -3
  540. package/src/tools/monitor-surface.ts +36 -0
  541. package/src/tools/monitor.ts +2 -32
  542. package/src/tools/observers.ts +2 -2
  543. package/src/tools/presentation.ts +107 -0
  544. package/src/tools/registry.ts +45 -27
  545. package/src/tools/safe-exec.ts +2 -2
  546. package/src/tools/steer-surface.ts +17 -0
  547. package/src/tools/steer.ts +2 -13
  548. package/src/tools/tasks.ts +108 -11
  549. package/src/tools/truncate.ts +25 -184
  550. package/src/tools/verify/frontend.ts +3 -1
  551. package/src/tools/verify/index.ts +3 -38
  552. package/src/tools/verify/surface.ts +46 -0
  553. package/src/tools/web-fetch-surface.ts +23 -0
  554. package/src/tools/web-fetch.ts +2 -20
  555. package/src/tools/write.ts +7 -2
  556. package/src/worker/entry.ts +39 -2
  557. package/src/worker/spec-contract.ts +26 -5
  558. package/dist/chunk-7SS2CTV2.js +0 -61361
  559. package/dist/chunk-DKGKUHFA.js +0 -924
  560. package/dist/chunk-GEP36Y4X.js +0 -12796
  561. package/dist/chunk-XYWBQRDM.js +0 -137
  562. package/dist/clio-BZVGEUFJ.js +0 -58
  563. package/dist/configure-S7S6F6CL.js +0 -32
  564. package/docs/html/agents_blueprint.html +0 -936
  565. package/docs/html/alcf_blueprint.html +0 -324
  566. package/docs/html/architecture_blueprint.html +0 -850
  567. package/docs/html/commands_blueprint.html +0 -939
  568. package/docs/html/config_knobs_audit_blueprint.html +0 -178
  569. package/docs/html/configuration_blueprint.html +0 -1080
  570. package/docs/html/context_blueprint.html +0 -603
  571. package/docs/html/documentation_blueprint.html +0 -832
  572. package/docs/html/environment_blueprint.html +0 -404
  573. package/docs/html/eval_blueprint.html +0 -743
  574. package/docs/html/evals_internal_blueprint.html +0 -190
  575. package/docs/html/evolution_blueprint.html +0 -674
  576. package/docs/html/extensions_blueprint.html +0 -2065
  577. package/docs/html/fleet_dispatch_blueprint.html +0 -286
  578. package/docs/html/index.html +0 -919
  579. package/docs/html/lifecycle_blueprint.html +0 -723
  580. package/docs/html/memory_blueprint.html +0 -699
  581. package/docs/html/middleware_blueprint.html +0 -664
  582. package/docs/html/models_blueprint.html +0 -2366
  583. package/docs/html/observability_blueprint.html +0 -683
  584. package/docs/html/provider_adapter_blueprint.html +0 -245
  585. package/docs/html/safety_blueprint.html +0 -1386
  586. package/docs/html/shared.css +0 -571
  587. package/docs/html/shared.js +0 -143
  588. package/docs/html/skills_blueprint.html +0 -671
  589. package/docs/html/soak_blueprint.html +0 -182
  590. package/docs/html/tool_usage_blueprint.html +0 -350
  591. package/docs/html/tools_blueprint.html +0 -2249
  592. package/docs/html/trace_blueprint.html +0 -235
  593. package/docs/html/tui_design_blueprint.html +0 -374
  594. package/docs/html/validation_blueprint.html +0 -961
  595. package/docs/html/worker_dispatch_blueprint.html +0 -231
  596. package/src/core/release.ts +0 -2
  597. package/src/domains/providers/runtimes/common/lmstudio-logger.ts +0 -32
  598. package/src/domains/providers/runtimes/local-native/lmstudio-native.ts +0 -491
  599. package/src/engine/apis/lmstudio-native.ts +0 -1438
  600. package/src/engine/apis/thinking-replay.ts +0 -11
  601. package/src/tools/string-enum.ts +0 -15
@@ -1,4 +1,10 @@
1
- import type { SessionEntry, TaskLedgerGoal, TaskLedgerStatus, TaskLedgerValidationEvidence } from "./entries.js";
1
+ import { randomUUID } from "node:crypto";
2
+ import {
3
+ isSessionEntry,
4
+ type TaskLedgerGoal,
5
+ type TaskLedgerStatus,
6
+ type TaskLedgerValidationEvidence,
7
+ } from "./entries.js";
2
8
 
3
9
  /**
4
10
  * The session task board: the live state behind the `tasks` tool, the footer
@@ -15,11 +21,14 @@ import type { SessionEntry, TaskLedgerGoal, TaskLedgerStatus, TaskLedgerValidati
15
21
  */
16
22
 
17
23
  const TASK_BOARD_GOAL_ID = "board";
24
+ export const LEGACY_TASK_BOARD_ID = "legacy";
18
25
 
19
26
  export interface TaskBoardTask {
20
27
  id: string;
21
28
  title: string;
22
29
  status: TaskLedgerStatus;
30
+ origin?: "agent" | "user";
31
+ userTaskId?: string;
23
32
  /** Block or drop reason; empty for pending/active/completed tasks. */
24
33
  reason?: string;
25
34
  /** Evidence note recorded when the task was completed. */
@@ -27,6 +36,8 @@ export interface TaskBoardTask {
27
36
  }
28
37
 
29
38
  export interface TaskBoardSnapshot {
39
+ /** Stable identity for this plan generation; task display ids are reusable. */
40
+ boardId: string;
30
41
  title: string;
31
42
  tasks: ReadonlyArray<TaskBoardTask>;
32
43
  /**
@@ -53,6 +64,7 @@ export interface TaskBoardCounts {
53
64
  export type TaskBoardMutation =
54
65
  | { op: "plan"; title: string; tasks: ReadonlyArray<string> }
55
66
  | { op: "add"; tasks: ReadonlyArray<string> }
67
+ | { op: "pick"; title: string; userTaskId: string }
56
68
  | { op: "start"; id: string }
57
69
  | { op: "done"; id: string; evidence: string }
58
70
  | { op: "block"; id: string; reason: string }
@@ -71,6 +83,7 @@ export type TaskBoardMutationResult =
71
83
  export interface TaskLedgerEntryFields {
72
84
  kind: "taskLedger";
73
85
  parentTurnId: null;
86
+ boardId: string;
74
87
  goals: TaskLedgerGoal[];
75
88
  subgoals: TaskLedgerGoal[];
76
89
  activeRunIds: string[];
@@ -88,18 +101,48 @@ export interface TaskBoardStoreDeps {
88
101
  readEntries?: () => ReadonlyArray<unknown>;
89
102
  /** Persist one full-snapshot taskLedger entry. Absent means in-memory only. */
90
103
  appendEntry?: (entry: TaskLedgerEntryFields) => void;
104
+ /** Stable board-id source; injectable for deterministic contract tests. */
105
+ createBoardId?: () => string;
91
106
  now?: () => Date;
92
107
  }
93
108
 
109
+ export interface SessionTaskHistoryBoard {
110
+ boardId: string;
111
+ title: string;
112
+ tasks: ReadonlyArray<TaskBoardTask>;
113
+ lastSnapshotAt: string;
114
+ }
115
+
94
116
  export interface TaskBoardStore {
95
117
  /** Current board, refolded from the session ledger after a session switch. */
96
118
  snapshot(): TaskBoardSnapshot | null;
119
+ /**
120
+ * Current in-memory projection only. Unlike snapshot(), this never reads or
121
+ * folds the session ledger and returns null rather than leaking a stale board
122
+ * when the session id changed or invalidate() has marked the cache dirty.
123
+ */
124
+ cachedSnapshot(): TaskBoardSnapshot | null;
125
+ /** Durable board generations on the active session path, newest first. */
126
+ historySnapshot(): ReadonlyArray<SessionTaskHistoryBoard>;
97
127
  /** Apply one mutation, persist the resulting snapshot, and return it. */
98
128
  apply(mutation: TaskBoardMutation): TaskBoardMutationResult;
99
129
  /** Link an in-flight dispatch run to the board; a no-op without a board. */
100
130
  attachRun(runId: string): void;
101
131
  /** Unlink a finished dispatch run; a no-op when it was never attached. */
102
132
  detachRun(runId: string): void;
133
+ /**
134
+ * Force the next read to refold from the ledger even if `getSessionId()`
135
+ * reports the same id it did last time. A `/tree` switch moves the active
136
+ * append point without changing which session is open, so the id-keyed
137
+ * cache alone never noticed the branch change and kept showing the
138
+ * abandoned branch's board (issue #94). Callers invalidate on the bus
139
+ * signal that switch emits.
140
+ */
141
+ invalidate(): void;
142
+ }
143
+
144
+ function isOpenUserTask(task: TaskBoardTask): boolean {
145
+ return task.origin === "user" && (task.status === "pending" || task.status === "active");
103
146
  }
104
147
 
105
148
  export function taskBoardCounts(board: Pick<TaskBoardSnapshot, "tasks">): TaskBoardCounts {
@@ -150,12 +193,15 @@ export function toTaskLedgerEntryFields(board: TaskBoardSnapshot, now: Date): Ta
150
193
  status: task.status,
151
194
  parentGoalId: TASK_BOARD_GOAL_ID,
152
195
  };
196
+ if (task.origin) goal.origin = task.origin;
197
+ if (task.userTaskId) goal.userTaskId = task.userTaskId;
153
198
  if (task.reason) goal.description = task.reason;
154
199
  return goal;
155
200
  });
156
201
  return {
157
202
  kind: "taskLedger",
158
203
  parentTurnId: null,
204
+ boardId: board.boardId,
159
205
  goals: [{ id: TASK_BOARD_GOAL_ID, title: board.title, status: boardStatus(board.tasks) }],
160
206
  subgoals,
161
207
  activeRunIds: [...board.activeRunIds],
@@ -165,13 +211,13 @@ export function toTaskLedgerEntryFields(board: TaskBoardSnapshot, now: Date): Ta
165
211
 
166
212
  function isTaskLedgerShaped(value: unknown): value is {
167
213
  kind: "taskLedger";
214
+ boardId?: string;
215
+ timestamp?: string;
168
216
  goals: TaskLedgerGoal[];
169
217
  subgoals: TaskLedgerGoal[];
170
218
  requiredValidationEvidence: TaskLedgerValidationEvidence[];
171
219
  } {
172
- if (!value || typeof value !== "object") return false;
173
- const entry = value as Partial<SessionEntry>;
174
- return entry.kind === "taskLedger" && Array.isArray((entry as { goals?: unknown }).goals);
220
+ return isSessionEntry(value) && value.kind === "taskLedger";
175
221
  }
176
222
 
177
223
  /**
@@ -186,7 +232,60 @@ export function foldTaskBoard(entries: ReadonlyArray<unknown>): TaskBoardSnapsho
186
232
  return last;
187
233
  }
188
234
 
235
+ /**
236
+ * Fold every safely identifiable board generation in a session. New ledgers
237
+ * key generations by boardId; pre-boardId ledgers collapse to the newest
238
+ * legacy snapshot because reusable tN ids cannot safely distinguish their
239
+ * older plan generations.
240
+ */
241
+ export function foldSessionTaskHistory(entries: ReadonlyArray<unknown>): SessionTaskHistoryBoard[] {
242
+ type MutableHistoryBoard = {
243
+ boardId: string;
244
+ title: string;
245
+ tasks: Map<string, TaskBoardTask>;
246
+ lastSnapshotAt: string;
247
+ lastIndex: number;
248
+ };
249
+ const boards = new Map<string, MutableHistoryBoard>();
250
+ for (const [index, raw] of entries.entries()) {
251
+ if (!isTaskLedgerShaped(raw)) continue;
252
+ const view = toEntryView(raw);
253
+ if (!view) continue;
254
+ const boardId = typeof raw.boardId === "string" && raw.boardId.length > 0 ? raw.boardId : LEGACY_TASK_BOARD_ID;
255
+ const timestamp = typeof raw.timestamp === "string" ? raw.timestamp : "";
256
+ if (boardId === LEGACY_TASK_BOARD_ID) {
257
+ boards.set(boardId, {
258
+ boardId,
259
+ title: view.title,
260
+ tasks: new Map(view.tasks.map((task) => [task.id, task])),
261
+ lastSnapshotAt: timestamp,
262
+ lastIndex: index,
263
+ });
264
+ continue;
265
+ }
266
+ const existing = boards.get(boardId);
267
+ if (!existing) {
268
+ boards.set(boardId, {
269
+ boardId,
270
+ title: view.title,
271
+ tasks: new Map(view.tasks.map((task) => [task.id, task])),
272
+ lastSnapshotAt: timestamp,
273
+ lastIndex: index,
274
+ });
275
+ continue;
276
+ }
277
+ existing.title = view.title;
278
+ for (const task of view.tasks) existing.tasks.set(task.id, task);
279
+ existing.lastSnapshotAt = timestamp;
280
+ existing.lastIndex = index;
281
+ }
282
+ return [...boards.values()]
283
+ .sort((left, right) => right.lastIndex - left.lastIndex)
284
+ .map(({ lastIndex: _lastIndex, tasks, ...board }) => ({ ...board, tasks: [...tasks.values()] }));
285
+ }
286
+
189
287
  function toEntryView(entry: {
288
+ boardId?: string;
190
289
  goals: TaskLedgerGoal[];
191
290
  subgoals: TaskLedgerGoal[];
192
291
  requiredValidationEvidence: TaskLedgerValidationEvidence[];
@@ -199,9 +298,16 @@ function toEntryView(entry: {
199
298
  evidenceByTask.set(taskId, item.description);
200
299
  }
201
300
  return {
301
+ boardId: entry.boardId ?? LEGACY_TASK_BOARD_ID,
202
302
  title: boardGoal.title,
203
303
  tasks: entry.subgoals.map((goal) => {
204
- const task: TaskBoardTask = { id: goal.id, title: goal.title, status: goal.status };
304
+ const task: TaskBoardTask = {
305
+ id: goal.id,
306
+ title: goal.title,
307
+ status: goal.status,
308
+ origin: goal.origin ?? "agent",
309
+ };
310
+ if (goal.userTaskId) task.userTaskId = goal.userTaskId;
205
311
  if (goal.description) task.reason = goal.description;
206
312
  const evidence = evidenceByTask.get(goal.id);
207
313
  if (evidence) task.evidence = evidence;
@@ -235,14 +341,27 @@ function applyMutation(
235
341
  const titles = normalizeTitles(mutation.tasks);
236
342
  if (mutation.title.trim().length === 0) return { error: "plan requires a non-empty title" };
237
343
  if (titles.length === 0) return { error: "plan requires at least one task" };
344
+ const retained = board?.tasks.filter(isOpenUserTask) ?? [];
238
345
  const notes: string[] = [];
239
- if (board && taskBoardCounts(board).open > 0) {
240
- notes.push(`replaced board "${board.title}" with ${taskBoardCounts(board).open} task(s) still open`);
346
+ const replacedOpen = board ? taskBoardCounts(board).open - retained.length : 0;
347
+ if (board && replacedOpen > 0) {
348
+ notes.push(`replaced board "${board.title}" with ${replacedOpen} agent task(s) still open`);
241
349
  }
350
+ if (retained.length > 0) notes.push(`preserved ${retained.length} open operator task(s)`);
351
+ const start = nextTaskId(retained);
242
352
  return {
243
353
  board: {
354
+ boardId: "",
244
355
  title: mutation.title.trim(),
245
- tasks: titles.map((title, index) => ({ id: `t${index + 1}`, title, status: "pending" as const })),
356
+ tasks: [
357
+ ...retained,
358
+ ...titles.map((title, index) => ({
359
+ id: `t${start + index}`,
360
+ title,
361
+ status: "pending" as const,
362
+ origin: "agent" as const,
363
+ })),
364
+ ],
246
365
  // Runs in flight outlive a board swap: they belong to the session,
247
366
  // so the fresh board inherits the linkage until the runs finish.
248
367
  activeRunIds: board?.activeRunIds ?? [],
@@ -250,12 +369,48 @@ function applyMutation(
250
369
  notes,
251
370
  };
252
371
  }
372
+ if (mutation.op === "pick") {
373
+ const title = mutation.title.trim();
374
+ if (title.length === 0) return { error: "pick requires a non-empty operator task title" };
375
+ if (!/^u[1-9]\d*$/.test(mutation.userTaskId)) return { error: "pick requires an operator task id like u3" };
376
+ if (
377
+ board?.tasks.some(
378
+ (task) =>
379
+ task.userTaskId === mutation.userTaskId &&
380
+ (task.status === "pending" || task.status === "active" || task.status === "completed"),
381
+ )
382
+ ) {
383
+ return { error: `operator task ${mutation.userTaskId} is already linked to durable board work` };
384
+ }
385
+ const tasks = board?.tasks ?? [];
386
+ const picked: TaskBoardTask = {
387
+ id: `t${nextTaskId(tasks)}`,
388
+ title,
389
+ status: "pending",
390
+ origin: "user",
391
+ userTaskId: mutation.userTaskId,
392
+ };
393
+ return {
394
+ board: {
395
+ boardId: board?.boardId ?? "",
396
+ title: board?.title ?? "Operator tasks",
397
+ tasks: [...tasks, picked],
398
+ activeRunIds: board?.activeRunIds ?? [],
399
+ },
400
+ notes: [],
401
+ };
402
+ }
253
403
  if (board === null) return { error: 'no task board yet; declare one with action="plan" first' };
254
404
  if (mutation.op === "add") {
255
405
  const titles = normalizeTitles(mutation.tasks);
256
406
  if (titles.length === 0) return { error: "add requires at least one task" };
257
407
  const start = nextTaskId(board.tasks);
258
- const added = titles.map((title, index) => ({ id: `t${start + index}`, title, status: "pending" as const }));
408
+ const added = titles.map((title, index) => ({
409
+ id: `t${start + index}`,
410
+ title,
411
+ status: "pending" as const,
412
+ origin: "agent" as const,
413
+ }));
259
414
  return { board: { ...board, tasks: [...board.tasks, ...added] }, notes: [] };
260
415
  }
261
416
  const target = board.tasks.find((task) => task.id === mutation.id);
@@ -323,11 +478,13 @@ function applyStatusMutation(
323
478
 
324
479
  export function createTaskBoardStore(deps: TaskBoardStoreDeps = {}): TaskBoardStore {
325
480
  let cachedSessionId: string | null | undefined;
481
+ let dirty = true;
326
482
  let board: TaskBoardSnapshot | null = null;
327
483
 
328
484
  const syncToSession = (): void => {
329
485
  const sessionId = deps.getSessionId?.() ?? null;
330
- if (cachedSessionId === sessionId) return;
486
+ if (!dirty && cachedSessionId === sessionId) return;
487
+ dirty = false;
331
488
  cachedSessionId = sessionId;
332
489
  try {
333
490
  board = deps.readEntries ? foldTaskBoard(deps.readEntries()) : null;
@@ -345,18 +502,52 @@ export function createTaskBoardStore(deps: TaskBoardStoreDeps = {}): TaskBoardSt
345
502
  }
346
503
  };
347
504
 
505
+ const mutationRequiresAcknowledgement = (current: TaskBoardSnapshot | null, mutation: TaskBoardMutation): boolean => {
506
+ if (mutation.op === "pick") return true;
507
+ if (mutation.op === "plan") return current?.tasks.some(isOpenUserTask) ?? false;
508
+ if (mutation.op !== "done") return false;
509
+ return current?.tasks.some((task) => task.id === mutation.id && task.origin === "user") ?? false;
510
+ };
511
+
348
512
  return {
349
513
  snapshot(): TaskBoardSnapshot | null {
350
514
  syncToSession();
351
515
  return board;
352
516
  },
517
+ cachedSnapshot(): TaskBoardSnapshot | null {
518
+ const sessionId = deps.getSessionId?.() ?? null;
519
+ return !dirty && cachedSessionId === sessionId ? board : null;
520
+ },
521
+ historySnapshot(): ReadonlyArray<SessionTaskHistoryBoard> {
522
+ syncToSession();
523
+ try {
524
+ return deps.readEntries ? foldSessionTaskHistory(deps.readEntries()) : [];
525
+ } catch {
526
+ return [];
527
+ }
528
+ },
353
529
  apply(mutation: TaskBoardMutation): TaskBoardMutationResult {
354
530
  syncToSession();
355
531
  const result = applyMutation(board, mutation);
356
532
  if ("error" in result) return { ok: false, message: result.error };
357
- board = result.board;
358
- persist(result.board);
359
- return { ok: true, board: result.board, notes: result.notes };
533
+ const needsBoardId = mutation.op === "plan" || result.board.boardId.length === 0;
534
+ const nextBoard = needsBoardId ? { ...result.board, boardId: deps.createBoardId?.() ?? randomUUID() } : result.board;
535
+ if (mutationRequiresAcknowledgement(board, mutation)) {
536
+ if (!deps.appendEntry) {
537
+ return { ok: false, message: "durable task-ledger persistence is unavailable for operator-linked work" };
538
+ }
539
+ try {
540
+ deps.appendEntry(toTaskLedgerEntryFields(nextBoard, deps.now?.() ?? new Date()));
541
+ } catch (error) {
542
+ const message = error instanceof Error ? error.message : String(error);
543
+ return { ok: false, message: `could not persist operator-linked task board: ${message}` };
544
+ }
545
+ board = nextBoard;
546
+ return { ok: true, board: nextBoard, notes: result.notes };
547
+ }
548
+ board = nextBoard;
549
+ persist(nextBoard);
550
+ return { ok: true, board: nextBoard, notes: result.notes };
360
551
  },
361
552
  attachRun(runId: string): void {
362
553
  syncToSession();
@@ -371,5 +562,8 @@ export function createTaskBoardStore(deps: TaskBoardStoreDeps = {}): TaskBoardSt
371
562
  board = { ...board, activeRunIds: board.activeRunIds.filter((id) => id !== runId) };
372
563
  persist(board);
373
564
  },
565
+ invalidate(): void {
566
+ dirty = true;
567
+ },
374
568
  };
375
569
  }
@@ -12,8 +12,14 @@ import type { MessageEntry, SessionEntry } from "../entries.js";
12
12
  * - message entries on the active path,
13
13
  * - sidecar entries anchored to a path turn (parentTurnId or targetTurnId),
14
14
  * - compaction summaries whose firstKeptTurnId or parent lands on the path,
15
- * - unanchored sidecars (parentTurnId null), which cannot be attributed to
16
- * a branch and keep their historical always-included behavior.
15
+ * - unanchored sidecars (parentTurnId null) written at or before the leaf's
16
+ * position in the file. `fork.ts` shares this same rule (`entryBelongsToPath`
17
+ * below) for the same reason: an unanchored sidecar records state as of
18
+ * the moment it was written, and a sidecar written after the turn a
19
+ * reconstruction stands at was not visible then, on any branch. Before
20
+ * issue #94 this filter kept every unanchored sidecar regardless of
21
+ * position while fork applied the positional cut, so the same turn
22
+ * reached by /tree switch versus /fork saw two different task boards.
17
23
  *
18
24
  * A plain chain is returned unchanged unless a live caller explicitly pins an
19
25
  * earlier leaf. That preserves historical linear replay (including old parent
@@ -47,7 +53,8 @@ export function filterEntriesToActivePath(entries: ReadonlyArray<SessionEntry>,
47
53
  // as the root rather than failing the whole replay.
48
54
  current = messagesById.get(current.parentTurnId);
49
55
  }
50
- return entries.filter((entry) => entryOnActivePath(entry, pathIds));
56
+ const leafIndex = entries.findIndex((entry) => entry.kind === "message" && entry.turnId === leaf.turnId);
57
+ return entries.filter((entry, index) => entryBelongsToPath(entry, pathIds, leafIndex >= 0 && index <= leafIndex));
51
58
  }
52
59
 
53
60
  /**
@@ -87,12 +94,44 @@ function hasBranch(messages: ReadonlyArray<MessageEntry>): boolean {
87
94
  return false;
88
95
  }
89
96
 
90
- function entryOnActivePath(entry: SessionEntry, pathIds: ReadonlySet<string>): boolean {
97
+ /**
98
+ * Shared verdict for whether a session entry belongs to a reconstruction
99
+ * standing at the turn `pathIds` traces back to. Used by both live
100
+ * `/tree`-switch replay (above) and `/fork` (`fork.ts`), so the two surfaces
101
+ * agree on the one thing that was inconsistent between them: an unanchored
102
+ * sidecar (`parentTurnId === null`, e.g. `taskLedger`, a routing notice, a
103
+ * leafless `workerRun`) carries no turn pointer, so file position is the only
104
+ * signal available for it. `atOrBeforeLeaf` is that positional verdict,
105
+ * computed by the caller: was the entry written at or before the leaf's
106
+ * position in the file. A sidecar written after the leaf's position was not
107
+ * visible when a reconstruction standing at that leaf would have looked, on
108
+ * any branch, so it is excluded (issue #94; before this fix, live replay kept
109
+ * every unanchored sidecar regardless of position while fork dropped anything
110
+ * after the fork point, so the same turn reached the two ways produced two
111
+ * different task boards).
112
+ *
113
+ * `compactionSummary` is deliberately not given the same positional
114
+ * treatment here: a compaction anchored to a turn on the path is ordinarily
115
+ * appended after that turn's own message (compaction runs on top of a
116
+ * finished conversation, not before it), so gating it by leaf position would
117
+ * drop the compaction that legitimately covers the leaf itself. `fork.ts`
118
+ * layers its own, stricter compaction gate on top of this function, because
119
+ * a fork represents a snapshot frozen at one moment and a compaction written
120
+ * after that moment did not exist at the fork point even when its anchor
121
+ * turn is an ancestor. A `sessionInfo` entry with no `targetTurnId` is a
122
+ * session-wide fact (e.g. a rename), not scoped to any branch or moment, so
123
+ * it is always kept.
124
+ */
125
+ export function entryBelongsToPath(
126
+ entry: SessionEntry,
127
+ pathIds: ReadonlySet<string>,
128
+ atOrBeforeLeaf: boolean,
129
+ ): boolean {
91
130
  if (entry.kind === "message") return pathIds.has(entry.turnId);
92
131
  if (entry.kind === "label") return pathIds.has(entry.targetTurnId);
93
132
  if (entry.kind === "sessionInfo") return entry.targetTurnId === undefined || pathIds.has(entry.targetTurnId);
94
133
  if (entry.kind === "compactionSummary" && entry.firstKeptTurnId.length > 0 && pathIds.has(entry.firstKeptTurnId)) {
95
134
  return true;
96
135
  }
97
- return entry.parentTurnId === null || pathIds.has(entry.parentTurnId);
136
+ return entry.parentTurnId === null ? atOrBeforeLeaf : pathIds.has(entry.parentTurnId);
98
137
  }
@@ -4,6 +4,7 @@ import type { SessionMeta } from "../contract.js";
4
4
  import { isSessionHeader, type SessionEntry } from "../entries.js";
5
5
  import { enrichForkMeta } from "../history.js";
6
6
  import { type SessionManagerState, startSession } from "../manager.js";
7
+ import { entryBelongsToPath } from "./active-path.js";
7
8
 
8
9
  /**
9
10
  * Single-point fork-from-parent-turn orchestration. Closes the caller's
@@ -80,32 +81,21 @@ function treeFromLinearPath(path: ReadonlyArray<LinkedRecord>): SessionTreeNode[
80
81
  }
81
82
 
82
83
  /**
83
- * Unanchored sidecars (parentTurnId null) carry no turn pointer, so file
84
- * position is the only branch signal: anything written after the fork-point
85
- * turn belongs to the timeline that diverged from it, not to the fork.
86
- * `atOrBeforeForkPoint` is that positional verdict, computed by the caller.
84
+ * Fork-specific tightening of the shared `entryBelongsToPath` verdict. A fork
85
+ * is a snapshot frozen at `parentTurnId`: a compaction summary written after
86
+ * that point did not exist at the fork moment even when its `firstKeptTurnId`
87
+ * names an ancestor turn, so it must not ride along just because that turn
88
+ * happens to be on the path. Live `/tree`-switch replay does not need this
89
+ * extra gate (see the comment on the shared function for why), which is the
90
+ * one place fork and live replay still deliberately disagree.
87
91
  */
88
92
  function sessionEntryBelongsToPath(
89
93
  entry: SessionEntry,
90
94
  pathIds: ReadonlySet<string>,
91
95
  atOrBeforeForkPoint: boolean,
92
96
  ): boolean {
93
- if (entry.kind === "message") return pathIds.has(entry.turnId);
94
- if (entry.kind === "label") return pathIds.has(entry.targetTurnId);
95
- if (entry.kind === "sessionInfo") return entry.targetTurnId ? pathIds.has(entry.targetTurnId) : false;
96
- if (entry.kind === "compactionSummary") {
97
- // A summary describes history as of its write position. One written
98
- // after the fork point did not exist when the forked timeline split,
99
- // even when its firstKeptTurnId lands on the fork path; copying it
100
- // would open the fork at the summary boundary and hide the transcript.
101
- if (!atOrBeforeForkPoint) return false;
102
- if (entry.firstKeptTurnId.length > 0 && pathIds.has(entry.firstKeptTurnId)) return true;
103
- }
104
- return entry.parentTurnId === null ? atOrBeforeForkPoint : pathIds.has(entry.parentTurnId);
105
- }
106
-
107
- function entryBelongsToPath(entry: SessionEntry, pathIds: ReadonlySet<string>, atOrBeforeForkPoint: boolean): boolean {
108
- return sessionEntryBelongsToPath(entry, pathIds, atOrBeforeForkPoint);
97
+ if (entry.kind === "compactionSummary" && !atOrBeforeForkPoint) return false;
98
+ return entryBelongsToPath(entry, pathIds, atOrBeforeForkPoint);
109
99
  }
110
100
 
111
101
  function branchEntriesFromParent(
@@ -124,7 +114,7 @@ function branchEntriesFromParent(
124
114
  const pathIds = new Set(path.map((record) => record.id));
125
115
  const forkPointIndex = parsed.findIndex((entry) => linkedRecordFromEntry(entry).id === leafTurnId);
126
116
  const entries = parsed.filter((entry, index) =>
127
- entryBelongsToPath(entry, pathIds, forkPointIndex >= 0 && index <= forkPointIndex),
117
+ sessionEntryBelongsToPath(entry, pathIds, forkPointIndex >= 0 && index <= forkPointIndex),
128
118
  );
129
119
  return {
130
120
  parentCurrentPath,
@@ -134,15 +124,21 @@ function branchEntriesFromParent(
134
124
  }
135
125
 
136
126
  /**
137
- * Fork the given state into a new session. Closes the prior writer
138
- * best-effort so the on-disk endedAt marker is written; the caller is
139
- * responsible for replacing its own state pointer with `result.next`.
127
+ * Fork the given state into a new session. Closes the prior writer once the
128
+ * child session is known good, so the on-disk endedAt marker on the parent is
129
+ * never written for a fork that failed; the caller is responsible for
130
+ * replacing its own state pointer with `result.next`.
140
131
  */
141
132
  export function forkFromState(input: ForkInput): ForkResult {
142
133
  const parentMeta = input.from.meta;
143
- // close() only performs synchronous filesystem work before resolving, so
144
- // the following read sees a fully flushed parent transcript.
145
- void input.from.writer.close();
134
+ // Appends are already synchronous writes (writeSync in the engine writer),
135
+ // so the read below already sees everything appended in this process
136
+ // without closing first; flushAppends only fsyncs for durability. Reading
137
+ // (and traceAncestry inside branchEntriesFromParent, which throws on an
138
+ // unknown or broken parent chain) happens before the parent writer is
139
+ // touched, so a broken fork point leaves the parent open and un-ended
140
+ // instead of orphaning the caller (issue #93).
141
+ input.from.writer.flushAppends();
146
142
  const branch = branchEntriesFromParent(parentMeta, input.parentTurnId);
147
143
 
148
144
  const cwd = input.cwd ?? parentMeta.cwd;
@@ -156,5 +152,8 @@ export function forkFromState(input: ForkInput): ForkResult {
156
152
  parentTurnId: input.parentTurnId,
157
153
  });
158
154
  enrichForkMeta(next.meta, parentMeta.id, input.parentTurnId);
155
+ // Only now is the child known good: created, seeded, and stamped with its
156
+ // parent pointers. Close (and stamp endedAt on) the parent last.
157
+ void input.from.writer.close();
159
158
  return { next, parentMeta, nodes: branch.tree };
160
159
  }
@@ -21,10 +21,10 @@ import { stripTokenizerSentinels } from "../../../engine/strip-tokenizer-sentine
21
21
  * and optional label suffix. Callers that need a tighter budget can clamp
22
22
  * the output further.
23
23
  */
24
- export const TURN_PREVIEW_MAX_CHARS = 60;
24
+ const TURN_PREVIEW_MAX_CHARS = 60;
25
25
 
26
26
  /** Minimal turn shape buildTurnPreview accepts. */
27
- export type TurnPreviewInput = Pick<ClioTurnRecord, "kind" | "payload">;
27
+ type TurnPreviewInput = Pick<ClioTurnRecord, "kind" | "payload">;
28
28
 
29
29
  /**
30
30
  * Strip ANSI escape sequences. Tool outputs sometimes carry colour codes
@@ -104,27 +104,31 @@ function remoteFrom(raw: string | null): string | null {
104
104
  export function probeGit(cwd: string): GitProbeResult {
105
105
  const inside = gitOk(cwd, ["rev-parse", "--is-inside-work-tree"]);
106
106
  if (inside !== "true") return EMPTY_PROBE;
107
+ const ignored = gitOk(cwd, ["check-ignore", "--quiet", "--", "."]);
108
+ if (ignored !== null) return EMPTY_PROBE;
107
109
  const branch = branchFrom(gitOk(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]));
108
- const dirty = dirtyFrom(gitOk(cwd, ["status", "--porcelain"]));
110
+ const dirty = dirtyFrom(gitOk(cwd, ["status", "--porcelain", "--", "."]));
109
111
  const { ahead, behind } = aheadBehindFrom(gitOk(cwd, ["rev-list", "--left-right", "--count", "HEAD...@{u}"]));
110
- const recentCommits = recentCommitsFrom(gitOk(cwd, ["log", "-5", "--format=%H%x09%s"]));
112
+ const recentCommits = recentCommitsFrom(gitOk(cwd, ["log", "-5", "--format=%H%x09%s", "--", "."]));
111
113
  const remoteUrl = remoteFrom(gitOk(cwd, ["remote", "get-url", "origin"]));
112
114
  return { isGit: true, branch, dirty, ahead, behind, recentCommits, remoteUrl };
113
115
  }
114
116
 
115
117
  /**
116
- * The full probe without blocking the event loop. Six subprocesses cost 63-100 ms
117
- * of blocked loop on a large repository; the five that follow the work-tree gate
118
- * are independent, so they run concurrently and the caller waits on none of them.
118
+ * The full probe without blocking the event loop. Once Git confirms the exact
119
+ * workspace is both inside a work tree and not ignored, the remaining five
120
+ * subprocesses are independent and run concurrently.
119
121
  */
120
122
  export async function probeGitAsync(cwd: string): Promise<GitProbeResult> {
121
123
  const inside = await gitOkAsync(cwd, ["rev-parse", "--is-inside-work-tree"]);
122
124
  if (inside !== "true") return EMPTY_PROBE;
125
+ const ignored = await gitOkAsync(cwd, ["check-ignore", "--quiet", "--", "."]);
126
+ if (ignored !== null) return EMPTY_PROBE;
123
127
  const [branchRaw, status, aheadBehindRaw, log, remoteRaw] = await Promise.all([
124
128
  gitOkAsync(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]),
125
- gitOkAsync(cwd, ["status", "--porcelain"]),
129
+ gitOkAsync(cwd, ["status", "--porcelain", "--", "."]),
126
130
  gitOkAsync(cwd, ["rev-list", "--left-right", "--count", "HEAD...@{u}"]),
127
- gitOkAsync(cwd, ["log", "-5", "--format=%H%x09%s"]),
131
+ gitOkAsync(cwd, ["log", "-5", "--format=%H%x09%s", "--", "."]),
128
132
  gitOkAsync(cwd, ["remote", "get-url", "origin"]),
129
133
  ]);
130
134
  const { ahead, behind } = aheadBehindFrom(aheadBehindRaw);
@@ -140,16 +144,18 @@ export async function probeGitAsync(cwd: string): Promise<GitProbeResult> {
140
144
  }
141
145
 
142
146
  /**
143
- * Branch, dirty flag, and remote only: four subprocesses instead of six, run
144
- * concurrently and off the loop. This is what the interactive footer polls on
145
- * its ticker and re-reads at the end of every turn.
147
+ * Branch, dirty flag, and remote only. After the work-tree and ignored-workspace
148
+ * gates, the three fact probes run concurrently and off the loop. This is what
149
+ * the interactive footer polls and re-reads at the end of every turn.
146
150
  */
147
151
  export async function probeGitStatusAsync(cwd: string): Promise<GitStatusProbeResult> {
148
152
  const inside = await gitOkAsync(cwd, ["rev-parse", "--is-inside-work-tree"]);
149
153
  if (inside !== "true") return EMPTY_STATUS_PROBE;
154
+ const ignored = await gitOkAsync(cwd, ["check-ignore", "--quiet", "--", "."]);
155
+ if (ignored !== null) return EMPTY_STATUS_PROBE;
150
156
  const [branchRaw, status, remoteRaw] = await Promise.all([
151
157
  gitOkAsync(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]),
152
- gitOkAsync(cwd, ["status", "--porcelain"]),
158
+ gitOkAsync(cwd, ["status", "--porcelain", "--", "."]),
153
159
  gitOkAsync(cwd, ["remote", "get-url", "origin"]),
154
160
  ]);
155
161
  return {