@iowarp/clio-coder 0.4.1 → 0.4.2

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 (437) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/CONTRIBUTING.md +59 -36
  3. package/README.md +404 -472
  4. package/SECURITY.md +2 -1
  5. package/dist/{acp-ZILU3AUO.js → acp-TMDQZDIG.js} +7 -7
  6. package/dist/{agents-HYWGBGQR.js → agents-5N5NG3XG.js} +28 -28
  7. package/dist/assets/codewiki.json +1 -1
  8. package/dist/{auth-N3QT7CBO.js → auth-Z5CCBXKQ.js} +8 -9
  9. package/dist/{builtins-UJLMOVOV.js → builtins-K6TNDT24.js} +4 -4
  10. package/dist/{chunk-GVQJ5CCZ.js → chunk-2HFQNRV3.js} +7 -7
  11. package/dist/{chunk-QMXC4JB7.js → chunk-2NHR3NAY.js} +163 -1401
  12. package/dist/chunk-2X4RYJTJ.js +39 -0
  13. package/dist/{chunk-Y45G3AXC.js → chunk-2Z2IKEXI.js} +6 -10
  14. package/dist/{chunk-EIMVLWB3.js → chunk-34BHNEE3.js} +7 -3
  15. package/dist/{chunk-GIZNH63R.js → chunk-35MSIRKH.js} +9 -4
  16. package/dist/chunk-3EBYEESD.js +314 -0
  17. package/dist/{chunk-CTJ4RNAA.js → chunk-3F7VUY77.js} +2 -2
  18. package/dist/{chunk-AP73CFDC.js → chunk-3KIPBMUA.js} +2 -2
  19. package/dist/{chunk-J5LZHVIT.js → chunk-3M6DQK6S.js} +113 -35
  20. package/dist/{chunk-VEGN6WIQ.js → chunk-462T4EGZ.js} +2 -2
  21. package/dist/{chunk-AFKWHWXF.js → chunk-4JDLP6ZS.js} +33 -16
  22. package/dist/{chunk-6FN3E6KX.js → chunk-4O6MANBS.js} +2 -2
  23. package/dist/{chunk-AKB4GYDL.js → chunk-54ODD65L.js} +5 -5
  24. package/dist/{chunk-BBTJOK6Y.js → chunk-5KW52TEP.js} +3 -3
  25. package/dist/{chunk-6CCS4G3W.js → chunk-5PFYMY2V.js} +2 -2
  26. package/dist/chunk-77QIVUZB.js +1334 -0
  27. package/dist/{chunk-7OBGU7UB.js → chunk-7BHIY2MW.js} +7 -13
  28. package/dist/{chunk-3QSOM6PA.js → chunk-AZ4WMN4W.js} +2 -2
  29. package/dist/{chunk-6NJQITNH.js → chunk-B74PXLU7.js} +6 -3
  30. package/dist/{chunk-R23Z6K6I.js → chunk-B7HM5Z7T.js} +15 -15
  31. package/dist/{chunk-R32CLGZ6.js → chunk-BO7Y52RY.js} +81 -20
  32. package/dist/{chunk-UEDMSP56.js → chunk-BYMNWQ7O.js} +123 -148
  33. package/dist/{chunk-ZJLUDYFY.js → chunk-CRFOIAX3.js} +4 -4
  34. package/dist/{chunk-2NM363SV.js → chunk-CYZW7JHJ.js} +7 -7
  35. package/dist/{chunk-6HMJX2VU.js → chunk-DYHAXKHD.js} +38 -10
  36. package/dist/{chunk-THYWACCR.js → chunk-DZAW46HP.js} +3 -3
  37. package/dist/{chunk-FYUN5KZ3.js → chunk-DZEK6CJN.js} +17 -17
  38. package/dist/{chunk-3I5NY75V.js → chunk-E7GT7O5N.js} +5 -5
  39. package/dist/{chunk-VKFQTNDV.js → chunk-F2I26BDK.js} +4 -4
  40. package/dist/{chunk-HLW2MRKE.js → chunk-F4EKGO4N.js} +3 -1
  41. package/dist/{chunk-IXJT6DCX.js → chunk-FVDGR2ZL.js} +3 -3
  42. package/dist/{chunk-TZSKNMZG.js → chunk-GTUD2WMY.js} +2 -1
  43. package/dist/{chunk-7EPLI7VL.js → chunk-HIICAHCJ.js} +2 -2
  44. package/dist/{chunk-E67WX76H.js → chunk-HKMD33FO.js} +29 -80
  45. package/dist/chunk-HLAFFSEK.js +360 -0
  46. package/dist/{chunk-UAPGZHYC.js → chunk-I64IFBLB.js} +9 -2
  47. package/dist/{chunk-XKA2ICR3.js → chunk-I66ZTYNP.js} +440 -175
  48. package/dist/{chunk-7PWAODYW.js → chunk-I7XBWTYH.js} +2 -2
  49. package/dist/{chunk-PVAMAVBB.js → chunk-IDNA72AH.js} +102 -2
  50. package/dist/{chunk-GCSMB2KY.js → chunk-IKOZFYBN.js} +1 -1
  51. package/dist/{chunk-2VG7KLYV.js → chunk-IKSLQ4XV.js} +5460 -3241
  52. package/dist/{chunk-QKIFBZKT.js → chunk-IMXMHHMQ.js} +166 -25
  53. package/dist/{chunk-74YWRRU5.js → chunk-JBCS7CRR.js} +2 -2
  54. package/dist/{chunk-BDPT6GTK.js → chunk-JWJGP5DQ.js} +2 -2
  55. package/dist/{chunk-K6BF4U2H.js → chunk-KKOJXO6R.js} +62 -14
  56. package/dist/chunk-KPXDY6QF.js +47 -0
  57. package/dist/{chunk-ABLSQ6JX.js → chunk-LJID3DYZ.js} +7 -1
  58. package/dist/{chunk-VKRH2TCS.js → chunk-M2DAX4F6.js} +2 -2
  59. package/dist/{chunk-6I5ILFOF.js → chunk-M2WXEHER.js} +2 -2
  60. package/dist/{chunk-YPI3QQCF.js → chunk-MCEPRMZW.js} +2 -4
  61. package/dist/{chunk-N5UK64DP.js → chunk-MCMZMDAC.js} +2 -2
  62. package/dist/{chunk-Y4CAGMM6.js → chunk-MNJGS2IN.js} +5 -6
  63. package/dist/{chunk-TVHHYFHE.js → chunk-NEDJ26B5.js} +2 -2
  64. package/dist/{chunk-U2WB7TZS.js → chunk-NMJXSHBJ.js} +97 -85
  65. package/dist/{chunk-HUAS7ITX.js → chunk-O3YUNJZ2.js} +13 -21
  66. package/dist/{chunk-MA3H6DM5.js → chunk-P75RZCJW.js} +25 -3
  67. package/dist/{chunk-IG7BCQBA.js → chunk-PGF63K6I.js} +2 -2
  68. package/dist/chunk-PJX3WQUQ.js +42 -0
  69. package/dist/{chunk-6DWBAZ5U.js → chunk-Q4XWMHX6.js} +4 -6
  70. package/dist/{chunk-OJTRZGR3.js → chunk-QQLGQY2A.js} +8 -8
  71. package/dist/{chunk-J4HBWF6Y.js → chunk-RLYRBIYQ.js} +115 -20
  72. package/dist/{chunk-NLFAQR7Z.js → chunk-S66XZJOF.js} +3 -23
  73. package/dist/{chunk-C537JADH.js → chunk-SSEYRH53.js} +6 -7
  74. package/dist/chunk-SZAA6XDG.js +30 -0
  75. package/dist/{chunk-MOPSG2X7.js → chunk-TPEQIQIE.js} +6 -6
  76. package/dist/{chunk-JA5QWE4Z.js → chunk-UBRFI4HS.js} +1879 -1650
  77. package/dist/{chunk-BTGG6BG2.js → chunk-UH347SHR.js} +154 -15
  78. package/dist/{chunk-5YHDIDBP.js → chunk-UH632ZYL.js} +2 -2
  79. package/dist/{chunk-BWW4HLO4.js → chunk-UXCU4E3T.js} +8 -6
  80. package/dist/{chunk-6VC4OV3Z.js → chunk-VIA6RFQZ.js} +3 -11
  81. package/dist/{chunk-ZAZB4JMW.js → chunk-VKPAQYEB.js} +27 -8
  82. package/dist/{chunk-UXN6JT4W.js → chunk-W4YEMFBX.js} +2 -2
  83. package/dist/{chunk-TD3PGPQA.js → chunk-W6NIE6OW.js} +2 -2
  84. package/dist/{chunk-TVH4ONAM.js → chunk-X7IARSHT.js} +3 -3
  85. package/dist/{chunk-PJJ6MY27.js → chunk-XE3PCIXH.js} +3 -3
  86. package/dist/{chunk-FEFIFZTL.js → chunk-XGDPUNND.js} +2 -2
  87. package/dist/{chunk-SCYB3HA4.js → chunk-XOXV5GKE.js} +51 -16
  88. package/dist/{chunk-QTFGO774.js → chunk-XQRY4DTA.js} +24 -11
  89. package/dist/{chunk-BJGUKIG4.js → chunk-YJISEZKC.js} +2 -2
  90. package/dist/{chunk-GPPB3JBE.js → chunk-ZGNYYXQ6.js} +2 -2
  91. package/dist/{chunk-SINK3QR6.js → chunk-ZNT2M6TG.js} +7 -7
  92. package/dist/{chunk-7RY5VZPH.js → chunk-ZW4HH5JJ.js} +6 -6
  93. package/dist/cli/index.js +33 -32
  94. package/dist/{clio-IT3G3VQH.js → clio-7VB377CC.js} +7 -7
  95. package/dist/{code-nav-RK6S7F6E.js → code-nav-YVLCYA7V.js} +85 -17
  96. package/dist/{config-3QZRWZJF.js → config-4HVOS65E.js} +88 -43
  97. package/dist/{configure-FL7Y3KJF.js → configure-PIWO7B24.js} +10 -10
  98. package/dist/{context-5HE7ODYK.js → context-IYEHL3WQ.js} +33 -31
  99. package/dist/{context-XNHL75JV.js → context-KQYIWPWT.js} +47 -34
  100. package/dist/{context-KYQFRVDC.js → context-N6ZE3LGJ.js} +11 -11
  101. package/dist/{context-clear-N545L53A.js → context-clear-G4OGZJDS.js} +33 -31
  102. package/dist/{context-working-set-QHKXSV2F.js → context-working-set-BWLF6LJP.js} +7 -7
  103. package/dist/{dispatch-runner-RGIE5PCT.js → dispatch-runner-2QQAITS3.js} +38 -38
  104. package/dist/{docs-5NAF6AU7.js → docs-PD3EXDKU.js} +21 -20
  105. package/dist/{doctor-ZGPEGHIP.js → doctor-LHBD36VU.js} +23 -22
  106. package/dist/{eval-GXLL44RD.js → eval-C45FYRJ6.js} +21 -20
  107. package/dist/{eval-inventory-HBWSWQOK.js → eval-inventory-6DEJPLBF.js} +2 -2
  108. package/dist/{evidence-HWLBRH3Q.js → evidence-6SHONYAF.js} +30 -28
  109. package/dist/{evolve-FTZBMNVW.js → evolve-KRKMV72X.js} +30 -28
  110. package/dist/{extensions-VHRBEID7.js → extensions-KPZ2UHBB.js} +5 -3
  111. package/dist/{fleet-CKZHJWZJ.js → fleet-IVTCKDHT.js} +62 -61
  112. package/dist/{fleet-commands-EXDXBMV6.js → fleet-commands-EDWL3IT7.js} +5 -5
  113. package/dist/{fleet-decisions-OTHB6KRL.js → fleet-decisions-YP3YEFGK.js} +4 -4
  114. package/dist/{fleet-graph-YTEZUCUT.js → fleet-graph-ZFWKHY2M.js} +16 -14
  115. package/dist/{fleet-inspect-SS6YMDCK.js → fleet-inspect-FVUNCBML.js} +31 -29
  116. package/dist/{fleet-preflight-PBY4VYOM.js → fleet-preflight-UN5XED4R.js} +2 -2
  117. package/dist/{fleet-validate-KMEM5L3S.js → fleet-validate-XOWC4HSX.js} +17 -15
  118. package/dist/{fleet-verify-QD5M7E7Q.js → fleet-verify-UN3SODEL.js} +30 -28
  119. package/dist/{fleet-view-WAMJYNDT.js → fleet-view-TWHJKCN6.js} +31 -29
  120. package/dist/{init-5XQRBOFV.js → init-T2QORQ3Y.js} +50 -49
  121. package/dist/{interop-34TVO25M.js → interop-IN5I2A66.js} +5 -5
  122. package/dist/{library-3QY6KF57.js → library-LSCATDLZ.js} +15 -13
  123. package/dist/{memory-L4UTIIIW.js → memory-HYOKAGGJ.js} +31 -29
  124. package/dist/{models-ZVX3QOWE.js → models-2GPMFYCM.js} +22 -21
  125. package/dist/{monitor-CEKVSYTS.js → monitor-E4ASVUJH.js} +34 -32
  126. package/dist/{orchestrator-77BAP6BC.js → orchestrator-DDMPR3PY.js} +984 -583
  127. package/dist/{panes-7STHOAUJ.js → panes-E3RUXOW5.js} +4 -4
  128. package/dist/{panes-SHAUIRXY.js → panes-IXKLOKA2.js} +23 -8
  129. package/dist/{reset-EOLM7GVE.js → reset-OAQP3W4O.js} +4 -4
  130. package/dist/{resources-74GKTLSF.js → resources-OTRSN34L.js} +15 -13
  131. package/dist/{run-HBAUJNNZ.js → run-5DEYH5QK.js} +60 -59
  132. package/dist/{share-G3APVLVP.js → share-IHWTLO3M.js} +19 -15
  133. package/dist/{skills-35HHUKCR.js → skills-IYMXMKW4.js} +17 -15
  134. package/dist/{skills-eval-QN4HSHDC.js → skills-eval-DROHSJAR.js} +36 -36
  135. package/dist/{skills-inventory-J357J34F.js → skills-inventory-D7X4L4ZX.js} +15 -13
  136. package/dist/{slash-commands-JZZCQA32.js → slash-commands-QBM7UZ3B.js} +21 -18
  137. package/dist/{steer-XAVHJM22.js → steer-Z5DO23FJ.js} +2 -2
  138. package/dist/{targets-DSM6CY3M.js → targets-P2FUC4IL.js} +25 -28
  139. package/dist/{terminal-lease-JOPFUVEM.js → terminal-lease-YREJ3JX2.js} +5 -5
  140. package/dist/{tools-MKNWVPBH.js → tools-5B7RO6MV.js} +4 -4
  141. package/dist/{trace-ECQ7TIYZ.js → trace-YMGMUM6A.js} +55 -7
  142. package/dist/{upgrade-H7TOM7YL.js → upgrade-PXK3S2YM.js} +11 -9
  143. package/dist/{usage-X52N3IDJ.js → usage-ME5MPXGX.js} +36 -34
  144. package/dist/{verifiers-EJTVVSMA.js → verifiers-BVZ7IWOO.js} +5 -5
  145. package/dist/{verify-YJL6XET2.js → verify-5K7ZKQFC.js} +4 -4
  146. package/dist/{web-fetch-MPIFL3LL.js → web-fetch-MPARV2K7.js} +2 -2
  147. package/dist/{wiki-generate-4NDZTQ4B.js → wiki-generate-F5W5QTYY.js} +48 -47
  148. package/dist/{with-panes-OBOBFIIR.js → with-panes-BYOJCLAM.js} +51 -255
  149. package/dist/worker/entry.js +45 -30
  150. package/docs/README.md +176 -81
  151. package/docs/{acp.md → architecture/acp.md} +36 -20
  152. package/docs/{alcf-provider.md → architecture/alcf-provider.md} +8 -5
  153. package/docs/{architecture.md → architecture/architecture.md} +43 -22
  154. package/docs/{artifact-placement.md → architecture/artifact-placement.md} +26 -23
  155. package/docs/architecture/artifact-versions.md +90 -0
  156. package/docs/{capacity-and-scheduling.md → architecture/capacity-and-scheduling.md} +26 -13
  157. package/docs/{context-engine.md → architecture/context-engine.md} +25 -25
  158. package/docs/{context-working-set.md → architecture/context-working-set.md} +13 -10
  159. package/docs/{dispatch-architecture-rationale.md → architecture/dispatch-architecture-rationale.md} +12 -9
  160. package/docs/{dispatch-typed-intent.md → architecture/dispatch-typed-intent.md} +68 -46
  161. package/docs/{evidence-and-memory.md → architecture/evidence-and-memory.md} +23 -16
  162. package/docs/{middleware-and-components.md → architecture/middleware-and-components.md} +11 -5
  163. package/docs/{model-catalog.md → architecture/model-catalog.md} +40 -17
  164. package/docs/{observability.md → architecture/observability.md} +26 -13
  165. package/docs/{pi-boundary.md → architecture/pi-boundary.md} +24 -11
  166. package/docs/{prompt-envelope-and-tools.md → architecture/prompt-envelope-and-tools.md} +55 -20
  167. package/docs/{provider-adapter-cookbook.md → architecture/provider-adapter-cookbook.md} +35 -24
  168. package/docs/{safety-model.md → architecture/safety-model.md} +20 -15
  169. package/docs/{session-lifecycle.md → architecture/session-lifecycle.md} +8 -5
  170. package/docs/architecture/time-conventions.md +125 -0
  171. package/docs/{trace-store.md → architecture/trace-store.md} +13 -5
  172. package/docs/{tui-design.md → architecture/tui-design.md} +13 -13
  173. package/docs/{worker-dispatch-mechanics.md → architecture/worker-dispatch-mechanics.md} +27 -30
  174. package/docs/{built-in-agents.md → guide/built-in-agents.md} +50 -34
  175. package/docs/{commands-and-modes.md → guide/commands-and-modes.md} +65 -60
  176. package/docs/{configuration-and-targets.md → guide/configuration-and-targets.md} +227 -289
  177. package/docs/guide/configuration-reference.md +1158 -0
  178. package/docs/{environment-variables.md → guide/environment-variables.md} +31 -28
  179. package/docs/{exit-codes-and-output.md → guide/exit-codes-and-output.md} +6 -3
  180. package/docs/{extensions-and-sharing.md → guide/extensions-and-sharing.md} +41 -14
  181. package/docs/{fleet-dispatch.md → guide/fleet-dispatch.md} +39 -43
  182. package/docs/{glossary.md → guide/glossary.md} +14 -11
  183. package/docs/{installation-and-lifecycle.md → guide/installation-and-lifecycle.md} +44 -13
  184. package/docs/guide/panes-and-files.md +290 -0
  185. package/docs/{proactive-memory.md → guide/proactive-memory.md} +79 -66
  186. package/docs/{resource-library.md → guide/resource-library.md} +13 -4
  187. package/docs/{skills-marketplace.md → guide/skills-marketplace.md} +7 -3
  188. package/docs/{tool-usage.md → guide/tool-usage.md} +87 -23
  189. package/docs/{troubleshooting.md → guide/troubleshooting.md} +9 -4
  190. package/docs/{config-knobs-audit.md → history/config-knobs-audit.md} +11 -11
  191. package/docs/{release-cut-checklist.md → history/release-cut-checklist.md} +29 -2
  192. package/docs/{development-pipeline.md → process/development-pipeline.md} +24 -26
  193. package/docs/process/documentation-coverage.md +100 -0
  194. package/docs/process/documentation-guide.md +187 -0
  195. package/docs/{eval-runner.md → process/eval-runner.md} +41 -50
  196. package/docs/{evals-internal.md → process/evals-internal.md} +10 -10
  197. package/docs/{evolution.md → process/evolution.md} +2 -2
  198. package/docs/{fleet-demo-runbook.md → process/fleet-demo-runbook.md} +11 -7
  199. package/docs/{git-commit-provenance.md → process/git-commit-provenance.md} +11 -4
  200. package/docs/{performance-methodology.md → process/performance-methodology.md} +87 -69
  201. package/docs/{scientific-validation.md → process/scientific-validation.md} +4 -4
  202. package/evals/README.md +2 -2
  203. package/package.json +9 -7
  204. package/skills/README.md +46 -37
  205. package/skills/coding/ast-grep/SKILL.md +2 -2
  206. package/skills/coding/coding-standards/SKILL.md +2 -2
  207. package/skills/coding/prototype/SKILL.md +2 -2
  208. package/skills/coding/tdd/SKILL.md +2 -2
  209. package/skills/context/context-handoff/SKILL.md +2 -2
  210. package/skills/context/context-prime/SKILL.md +2 -2
  211. package/skills/git/file-ticket/SKILL.md +2 -2
  212. package/skills/git/fix-issue/SKILL.md +3 -3
  213. package/skills/git/resolve-merge-conflicts/SKILL.md +2 -2
  214. package/skills/git/ship/SKILL.md +2 -2
  215. package/skills/git/worktree-create/SKILL.md +2 -2
  216. package/skills/git/worktree-merge/SKILL.md +2 -2
  217. package/skills/meta/clio-coder-dev/SKILL.md +9 -5
  218. package/skills/meta/clio-coder-dev/evals.md +3 -2
  219. package/skills/meta/clio-coder-test/SKILL.md +102 -95
  220. package/skills/meta/clio-coder-test/evals.md +9 -4
  221. package/skills/meta/clio-coder-test/references/harness.md +100 -124
  222. package/skills/meta/clio-coder-test/references/test-map.md +77 -50
  223. package/skills/meta/credentials/SKILL.md +2 -2
  224. package/skills/meta/find-skills/SKILL.md +2 -2
  225. package/skills/meta/herdr/SKILL.md +2 -2
  226. package/skills/meta/skill-craft/SKILL.md +22 -16
  227. package/skills/planning/architecture/SKILL.md +2 -2
  228. package/skills/planning/backlog/SKILL.md +2 -2
  229. package/skills/planning/prd/SKILL.md +2 -2
  230. package/skills/planning/product-intent/SKILL.md +2 -2
  231. package/skills/planning/tech-spec/SKILL.md +2 -2
  232. package/skills/registry.yaml +62 -62
  233. package/skills/research/arxiv-literature/SKILL.md +2 -2
  234. package/skills/research/experiment-protocol/SKILL.md +2 -2
  235. package/skills/research/scientific-debugging/SKILL.md +2 -2
  236. package/skills/research/scientific-modernization/SKILL.md +2 -2
  237. package/skills/skill-marketplace.json +62 -62
  238. package/skills/workflow/cut-it/SKILL.md +2 -2
  239. package/skills/workflow/design-council/SKILL.md +2 -2
  240. package/skills/workflow/grill-me/SKILL.md +2 -2
  241. package/skills/workflow/workflow-distiller/SKILL.md +2 -2
  242. package/src/cli/args.ts +2 -2
  243. package/src/cli/bootstrap-generate.ts +1 -1
  244. package/src/cli/config-inspect.ts +65 -12
  245. package/src/cli/configure.ts +0 -4
  246. package/src/cli/docs.ts +22 -14
  247. package/src/cli/doctor-naming.ts +5 -5
  248. package/src/cli/doctor-toolchain.ts +3 -3
  249. package/src/cli/eval.ts +1 -2
  250. package/src/cli/extensions.ts +2 -1
  251. package/src/cli/fleet.ts +1 -1
  252. package/src/cli/index.ts +2 -1
  253. package/src/cli/internal-dispatch.ts +3 -4
  254. package/src/cli/panes.ts +19 -5
  255. package/src/cli/run.ts +2 -2
  256. package/src/cli/share.ts +5 -1
  257. package/src/cli/skills-eval.ts +3 -3
  258. package/src/cli/targets.ts +2 -6
  259. package/src/cli/trace.ts +55 -4
  260. package/src/cli/wiki-generate.ts +1 -1
  261. package/src/core/artifact-paths.ts +1 -1
  262. package/src/core/bash-exec.ts +131 -86
  263. package/src/core/bus-events.ts +51 -6
  264. package/src/core/config.ts +5 -1
  265. package/src/core/defaults.ts +7 -4
  266. package/src/core/dispatch-outcome.ts +16 -0
  267. package/src/core/guardrails.ts +10 -49
  268. package/src/core/prompt-hint.ts +9 -0
  269. package/src/domains/agents/builtins/architect.md +2 -3
  270. package/src/domains/agents/builtins/coder.md +3 -2
  271. package/src/domains/agents/builtins/debugger.md +2 -2
  272. package/src/domains/agents/builtins/documenter.md +2 -2
  273. package/src/domains/agents/builtins/git-master.md +1 -1
  274. package/src/domains/agents/builtins/oracle.md +1 -1
  275. package/src/domains/agents/builtins/provenance.md +1 -1
  276. package/src/domains/agents/builtins/researcher.md +1 -1
  277. package/src/domains/agents/builtins/scout.md +1 -1
  278. package/src/domains/agents/builtins/tester.md +2 -2
  279. package/src/domains/agents/builtins/verifier.md +2 -2
  280. package/src/domains/agents/builtins/wiki-writer.md +1 -1
  281. package/src/domains/agents/catalog.ts +12 -14
  282. package/src/domains/agents/contract.ts +2 -0
  283. package/src/domains/agents/extension.ts +23 -1
  284. package/src/domains/config/keybindings.ts +8 -0
  285. package/src/domains/context/extension.ts +0 -3
  286. package/src/domains/context/working-set/path-index.ts +1 -0
  287. package/src/domains/dispatch/capability-match.ts +10 -0
  288. package/src/domains/dispatch/extension.ts +105 -22
  289. package/src/domains/dispatch/host-verification.ts +435 -39
  290. package/src/domains/dispatch/intent-requirements.ts +10 -0
  291. package/src/domains/dispatch/intent.ts +18 -1
  292. package/src/domains/dispatch/path-scope.ts +235 -24
  293. package/src/domains/dispatch/run-event-journal.ts +4 -15
  294. package/src/domains/dispatch/state.ts +2 -3
  295. package/src/domains/dispatch/transport.ts +45 -21
  296. package/src/domains/dispatch/types.ts +55 -3
  297. package/src/domains/eval/artifacts/store.ts +5 -0
  298. package/src/domains/eval/store.ts +8 -1
  299. package/src/domains/evidence/trust-status.ts +10 -1
  300. package/src/domains/extensions/contract.ts +15 -1
  301. package/src/domains/extensions/discovery.ts +238 -41
  302. package/src/domains/extensions/extension.ts +105 -6
  303. package/src/domains/extensions/index.ts +24 -0
  304. package/src/domains/extensions/integrity.ts +189 -0
  305. package/src/domains/extensions/manager.ts +17 -1
  306. package/src/domains/extensions/resource-path.ts +27 -0
  307. package/src/domains/extensions/resources.ts +18 -38
  308. package/src/domains/extensions/snapshot-store.ts +39 -0
  309. package/src/domains/extensions/snapshot.ts +180 -0
  310. package/src/domains/extensions/state.ts +385 -57
  311. package/src/domains/extensions/types.ts +118 -1
  312. package/src/domains/lifecycle/migrations/2026-09-01-extension-install-digests.ts +27 -0
  313. package/src/domains/lifecycle/migrations/index.ts +2 -0
  314. package/src/domains/lifecycle/naming-resources.ts +19 -4
  315. package/src/domains/lifecycle/naming-yazi.ts +10 -5
  316. package/src/domains/middleware/contract.ts +26 -0
  317. package/src/domains/middleware/extension.ts +24 -24
  318. package/src/domains/middleware/hook-receipts.ts +27 -4
  319. package/src/domains/middleware/hooks-io.ts +65 -32
  320. package/src/domains/middleware/hooks.ts +64 -0
  321. package/src/domains/middleware/index.ts +28 -4
  322. package/src/domains/middleware/registrations.ts +326 -0
  323. package/src/domains/middleware/runtime.ts +28 -0
  324. package/src/domains/middleware/snapshot.ts +20 -7
  325. package/src/domains/mux/contract.ts +38 -0
  326. package/src/domains/mux/detect.ts +6 -13
  327. package/src/domains/mux/index.ts +1 -1
  328. package/src/domains/mux/operations.ts +44 -5
  329. package/src/domains/mux/yazi/assets/yazi.toml +2 -2
  330. package/src/domains/mux/yazi/session.ts +53 -4
  331. package/src/domains/mux/yazi/theme.ts +117 -17
  332. package/src/domains/observability/contract.ts +10 -11
  333. package/src/domains/observability/extension.ts +11 -3
  334. package/src/domains/observability/projection.ts +14 -90
  335. package/src/domains/observability/trace-store.ts +43 -7
  336. package/src/domains/prompts/compiler.ts +73 -53
  337. package/src/domains/prompts/contract.ts +15 -3
  338. package/src/domains/prompts/extension.ts +97 -9
  339. package/src/domains/prompts/fragments/identity/clio-worker.md +1 -3
  340. package/src/domains/prompts/fragments/identity/clio.md +6 -12
  341. package/src/domains/prompts/fragments/identity/docs-routing.md +1 -2
  342. package/src/domains/prompts/fragments/identity/self-awareness.md +3 -11
  343. package/src/domains/prompts/fragments/operating/contract.md +7 -15
  344. package/src/domains/prompts/fragments/operating/delegation.md +32 -34
  345. package/src/domains/prompts/fragments/operating/skills.md +10 -24
  346. package/src/domains/prompts/fragments/operating/worker.md +1 -8
  347. package/src/domains/providers/index.ts +1 -1
  348. package/src/domains/providers/model-runtime-capabilities.ts +85 -21
  349. package/src/domains/providers/models/local-models/clio-coder-local-coding-targets.yaml +669 -104
  350. package/src/domains/providers/runtime-resolution.ts +31 -0
  351. package/src/domains/providers/runtimes/common/probe-helpers.ts +7 -2
  352. package/src/domains/providers/runtimes/local-native/llamacpp.ts +9 -1
  353. package/src/domains/providers/types/cost-provenance.ts +19 -0
  354. package/src/domains/providers/types/local-model-quirks.ts +85 -37
  355. package/src/domains/resources/skills/loader.ts +16 -19
  356. package/src/domains/safety/call-target.ts +1 -1
  357. package/src/domains/safety/loop-detector.ts +7 -4
  358. package/src/domains/session/task-board.ts +10 -9
  359. package/src/domains/share/archive.ts +164 -7
  360. package/src/engine/acp/server.ts +62 -9
  361. package/src/engine/apis/llamacpp-residency.ts +3 -4
  362. package/src/engine/apis/lmstudio.ts +3 -3
  363. package/src/engine/apis/ollama-native.ts +6 -6
  364. package/src/engine/apis/openai-completions.ts +28 -25
  365. package/src/engine/apis/output-budget.ts +8 -18
  366. package/src/engine/apis/residency.ts +8 -27
  367. package/src/engine/gemma-channel-filter.ts +19 -0
  368. package/src/engine/loop-guard.ts +92 -12
  369. package/src/engine/worker-runtime.ts +40 -11
  370. package/src/engine/worker-tools.ts +3 -1
  371. package/src/entry/extension-hook-sources.ts +28 -0
  372. package/src/entry/extension-reload.ts +309 -0
  373. package/src/entry/orchestrator.ts +59 -35
  374. package/src/interactive/application-controller.ts +2 -1
  375. package/src/interactive/bus-notices.ts +8 -1
  376. package/src/interactive/chat-loop-messages.ts +3 -13
  377. package/src/interactive/chat-loop.ts +10 -1
  378. package/src/interactive/chat-panel.ts +36 -13
  379. package/src/interactive/chat-renderer.ts +71 -7
  380. package/src/interactive/dispatch-board.ts +6 -11
  381. package/src/interactive/footer/widgets.ts +13 -0
  382. package/src/interactive/interactive-application.ts +39 -4
  383. package/src/interactive/interactive-input-runtime.ts +4 -0
  384. package/src/interactive/interactive-presentation.ts +2 -2
  385. package/src/interactive/interactive-slash-runtime.ts +2 -0
  386. package/src/interactive/overlays/extensions.ts +9 -1
  387. package/src/interactive/overlays/help-reference.ts +13 -0
  388. package/src/interactive/overlays/settings.ts +27 -16
  389. package/src/interactive/panes-runtime.ts +111 -35
  390. package/src/interactive/prompt-cache-identity.ts +88 -0
  391. package/src/interactive/slash-commands.ts +129 -14
  392. package/src/interactive/stream-pacing-policy.ts +0 -23
  393. package/src/interactive/turn-context.ts +30 -15
  394. package/src/interactive/yazi-bridge.ts +60 -6
  395. package/src/tools/agent-tools.ts +30 -1
  396. package/src/tools/artifact.ts +2 -2
  397. package/src/tools/ask-user.ts +3 -3
  398. package/src/tools/bash.ts +1 -1
  399. package/src/tools/bootstrap.ts +4 -0
  400. package/src/tools/builtin-tool-catalog.ts +52 -22
  401. package/src/tools/codewiki/code-nav-surface.ts +6 -0
  402. package/src/tools/codewiki/code-nav.ts +99 -13
  403. package/src/tools/context/docs-engine.ts +20 -7
  404. package/src/tools/context/index.ts +29 -12
  405. package/src/tools/core-bootstrap.ts +28 -6
  406. package/src/tools/credential-present.ts +1 -2
  407. package/src/tools/dispatch-arguments.ts +5 -1
  408. package/src/tools/dispatch-plan.ts +48 -4
  409. package/src/tools/dispatch-run-events.ts +1 -1
  410. package/src/tools/dispatch-schema.ts +338 -0
  411. package/src/tools/dispatch-types.ts +3 -0
  412. package/src/tools/dispatch.ts +9 -254
  413. package/src/tools/ledger.ts +3 -5
  414. package/src/tools/monitor-surface.ts +5 -13
  415. package/src/tools/observation.ts +4 -5
  416. package/src/tools/panes-surface.ts +4 -11
  417. package/src/tools/panes.ts +4 -2
  418. package/src/tools/policy.ts +15 -2
  419. package/src/tools/read.ts +5 -6
  420. package/src/tools/registry.ts +30 -7
  421. package/src/tools/result-shaping.ts +18 -14
  422. package/src/tools/steer-surface.ts +1 -1
  423. package/src/tools/tasks.ts +1 -1
  424. package/src/tools/truncate.ts +6 -5
  425. package/src/tools/verify/surface.ts +6 -12
  426. package/src/tools/web-fetch-surface.ts +1 -3
  427. package/dist/chunk-5QIAJV2D.js +0 -48
  428. package/dist/chunk-JZWT5J3Y.js +0 -814
  429. package/dist/chunk-K7VKOLQQ.js +0 -15
  430. package/dist/chunk-PMZCIOCJ.js +0 -25
  431. package/dist/chunk-SUW5DORT.js +0 -819
  432. package/dist/chunk-UOV2BYIW.js +0 -107
  433. package/dist/chunk-WR6U3OVP.js +0 -45
  434. package/docs/artifact-versions.md +0 -67
  435. package/docs/documentation-coverage.md +0 -46
  436. package/docs/documentation-guide.md +0 -167
  437. package/docs/time-conventions.md +0 -101
@@ -619,6 +619,15 @@ export function adaptRunReceiptValidationStatus(
619
619
  if (receipt.hostVerification?.status === "verified") {
620
620
  return attributed("validated", receiptSource(receipt), { kind: "validator", id: "host-verification" }, artifacts);
621
621
  }
622
+ // A batch member the failure was charged away from carries a check that
623
+ // exited non-zero, so the validator has no verdict for this run: neither the
624
+ // "failed" the exit code alone would suggest nor the "validated" a passing
625
+ // check earns. It stops here rather than falling through to receipt-quality,
626
+ // which cannot see that a declared host check failed on the tree this run
627
+ // shipped in.
628
+ if (receipt.hostVerification?.status === "not_implicated") {
629
+ return attributed("unknown", receiptSource(receipt), { kind: "validator", id: "host-verification" }, artifacts);
630
+ }
622
631
  if (qualityHasFailure(receipt.quality)) {
623
632
  return attributed("failed", receiptSource(receipt), { kind: "validator", id: "receipt-quality" }, artifacts);
624
633
  }
@@ -939,7 +948,7 @@ export interface GroundedEvidenceValidationInput {
939
948
  * Project validation that the evidence linker grounded in an executed
940
949
  * artifact. Grounding requires at least one independently observed artifact:
941
950
  * a self-report of the run under inspection cannot ground its own validation
942
- * (docs/evidence-and-memory.md, "composition rules"), and an empty reference
951
+ * (docs/architecture/evidence-and-memory.md, "composition rules"), and an empty reference
943
952
  * list grounds
944
953
  * nothing at all.
945
954
  */
@@ -5,8 +5,10 @@ import type {
5
5
  ExtensionInstallResult,
6
6
  ExtensionListOptions,
7
7
  ExtensionMutationResult,
8
+ ExtensionReloadPrepareResult,
8
9
  ExtensionResourceKind,
9
10
  ExtensionResourceRoot,
11
+ ExtensionSnapshot,
10
12
  InstalledExtension,
11
13
  } from "./manager.js";
12
14
 
@@ -17,6 +19,18 @@ export interface ExtensionsContract extends DomainContract {
17
19
  enable(id: string, options?: ExtensionListOptions): ExtensionMutationResult;
18
20
  disable(id: string, options?: ExtensionListOptions): ExtensionMutationResult;
19
21
  remove(id: string, options?: ExtensionListOptions): ExtensionMutationResult;
22
+ /** Committed projection for the bundle's cwd; ephemeral generation-0 build for any other cwd. */
20
23
  resourceRoots(kind: ExtensionResourceKind, cwd?: string): ExtensionResourceRoot[];
21
- reload(): Promise<void>;
24
+ snapshot(): ExtensionSnapshot | null;
25
+ generation(): number;
26
+ /**
27
+ * Build and validate the next generation from disk without publishing it.
28
+ * Never throws: a build failure or an in-flight candidate is reported as a
29
+ * rejection and the committed generation is untouched. A rejected build
30
+ * still burns its reserved generation number. The candidate publishes
31
+ * itself with one assignment-only call after the caller has confirmed
32
+ * `current()` on the same stack. Nothing is published at start(); the
33
+ * composition root publishes the boot generation together with its hooks.
34
+ */
35
+ prepareReload(): ExtensionReloadPrepareResult;
22
36
  }
@@ -1,4 +1,4 @@
1
- import { existsSync, lstatSync, readdirSync, readFileSync, realpathSync, statSync } from "node:fs";
1
+ import { type Dirent, existsSync, lstatSync, readdirSync, readFileSync, realpathSync, statSync } from "node:fs";
2
2
  import path from "node:path";
3
3
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
4
4
  import { evaluateClioCompatibility } from "./compatibility.js";
@@ -10,6 +10,23 @@ import type {
10
10
  } from "./types.js";
11
11
 
12
12
  const MANIFEST_NAMES = ["clio-coder-extension.yaml", "clio-coder-extension.yml", "clio-coder-extension.json"] as const;
13
+ const MANIFEST_KEYS = new Set([
14
+ "manifestVersion",
15
+ "id",
16
+ "name",
17
+ "version",
18
+ "description",
19
+ "resources",
20
+ "tools",
21
+ "settings",
22
+ "compatibility",
23
+ ]);
24
+ const RESOURCE_KEYS = new Set(["skills", "prompts", "agents", "fleets", "themes"]);
25
+ const COMPATIBILITY_KEYS = new Set(["clio"]);
26
+
27
+ function compareNames(a: { name: string }, b: { name: string }): number {
28
+ return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
29
+ }
13
30
 
14
31
  export function isRecord(value: unknown): value is Record<string, unknown> {
15
32
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -35,19 +52,45 @@ function readJsonOrYaml(filePath: string): unknown {
35
52
  return parseYaml(raw);
36
53
  }
37
54
 
38
- function normalizeResources(value: unknown): ExtensionManifestResources {
39
- if (!isRecord(value)) return {};
55
+ function rejectUnknownKeys(
56
+ value: Record<string, unknown>,
57
+ allowed: ReadonlySet<string>,
58
+ label: string,
59
+ manifestPath: string,
60
+ diagnostics: ExtensionDiagnostic[],
61
+ ): void {
62
+ for (const key of Object.keys(value).sort()) {
63
+ if (!allowed.has(key)) {
64
+ diagnostics.push({ type: "error", message: `unknown ${label} key '${key}'`, path: manifestPath });
65
+ }
66
+ }
67
+ }
68
+
69
+ function normalizeResources(
70
+ value: unknown,
71
+ manifestPath: string,
72
+ diagnostics: ExtensionDiagnostic[],
73
+ ): ExtensionManifestResources {
74
+ if (value === undefined) return {};
75
+ if (!isRecord(value)) {
76
+ diagnostics.push({ type: "error", message: "resources must be an object", path: manifestPath });
77
+ return {};
78
+ }
79
+ rejectUnknownKeys(value, RESOURCE_KEYS, "resources", manifestPath, diagnostics);
40
80
  const out: ExtensionManifestResources = {};
41
- const skills = trimString(value.skills);
42
- const prompts = trimString(value.prompts);
43
- const agents = trimString(value.agents);
44
- const fleets = trimString(value.fleets);
45
- const themes = trimString(value.themes);
46
- if (skills) out.skills = skills;
47
- if (prompts) out.prompts = prompts;
48
- if (agents) out.agents = agents;
49
- if (fleets) out.fleets = fleets;
50
- if (themes) out.themes = themes;
81
+ for (const kind of RESOURCE_KEYS) {
82
+ if (value[kind] === undefined) continue;
83
+ const resourcePath = trimString(value[kind]);
84
+ if (!resourcePath) {
85
+ diagnostics.push({
86
+ type: "error",
87
+ message: `resources.${kind} must be a non-empty string`,
88
+ path: manifestPath,
89
+ });
90
+ continue;
91
+ }
92
+ out[kind as keyof ExtensionManifestResources] = resourcePath;
93
+ }
51
94
  return out;
52
95
  }
53
96
 
@@ -77,7 +120,7 @@ function validateResourceTree(
77
120
  try {
78
121
  canonicalRoot = realpathSync(resolvedRoot);
79
122
  canonicalResource = realpathSync(resolved);
80
- if (!lstatSync(resolved).isDirectory()) {
123
+ if (!statSync(resolved).isDirectory()) {
81
124
  diagnostics.push({ type: "error", message: `resources.${kind} is not a directory`, path: resolved });
82
125
  return;
83
126
  }
@@ -98,16 +141,32 @@ function validateResourceTree(
98
141
  while (pending.length > 0) {
99
142
  const directory = pending.pop();
100
143
  if (!directory) continue;
101
- for (const entry of readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
144
+ for (const entry of readdirSync(directory, { withFileTypes: true }).sort(compareNames)) {
102
145
  const candidate = path.join(directory, entry.name);
103
146
  if (entry.isSymbolicLink()) {
104
147
  try {
105
- if (!contained(canonicalRoot, realpathSync(candidate))) {
148
+ const canonicalTarget = realpathSync(candidate);
149
+ if (!contained(canonicalRoot, canonicalTarget)) {
106
150
  diagnostics.push({
107
151
  type: "error",
108
152
  message: `resources.${kind} contains an escaping symbolic link`,
109
153
  path: candidate,
110
154
  });
155
+ continue;
156
+ }
157
+ const targetStat = statSync(candidate);
158
+ if (!targetStat.isDirectory() && !targetStat.isFile()) {
159
+ diagnostics.push({
160
+ type: "error",
161
+ message: `resources.${kind} contains a symbolic link to an unsupported filesystem entry`,
162
+ path: candidate,
163
+ });
164
+ } else if (targetStat.isFile() && targetStat.nlink !== 1) {
165
+ diagnostics.push({
166
+ type: "error",
167
+ message: `resources.${kind} contains a symbolic link to a hard-linked file`,
168
+ path: candidate,
169
+ });
111
170
  }
112
171
  } catch (error) {
113
172
  diagnostics.push({
@@ -118,6 +177,28 @@ function validateResourceTree(
118
177
  }
119
178
  } else if (entry.isDirectory()) {
120
179
  pending.push(candidate);
180
+ } else if (entry.isFile()) {
181
+ try {
182
+ if (lstatSync(candidate).nlink !== 1) {
183
+ diagnostics.push({
184
+ type: "error",
185
+ message: `resources.${kind} contains a hard-linked file`,
186
+ path: candidate,
187
+ });
188
+ }
189
+ } catch (error) {
190
+ diagnostics.push({
191
+ type: "error",
192
+ message: `resources.${kind} entry could not be inspected: ${error instanceof Error ? error.message : String(error)}`,
193
+ path: candidate,
194
+ });
195
+ }
196
+ } else {
197
+ diagnostics.push({
198
+ type: "error",
199
+ message: `resources.${kind} contains an unsupported filesystem entry`,
200
+ path: candidate,
201
+ });
121
202
  }
122
203
  }
123
204
  }
@@ -133,9 +214,32 @@ function validateResourceRoots(
133
214
  }
134
215
  }
135
216
 
136
- function stringArray(value: unknown): string[] | undefined {
137
- if (!Array.isArray(value)) return undefined;
138
- const out = value.map((entry) => trimString(entry)).filter((entry): entry is string => entry !== undefined);
217
+ function stringArray(
218
+ value: unknown,
219
+ field: "tools" | "settings",
220
+ manifestPath: string,
221
+ diagnostics: ExtensionDiagnostic[],
222
+ ): string[] | undefined {
223
+ if (value === undefined) return undefined;
224
+ if (!Array.isArray(value)) {
225
+ diagnostics.push({ type: "error", message: `${field} must be an array of non-empty strings`, path: manifestPath });
226
+ return undefined;
227
+ }
228
+ const out: string[] = [];
229
+ const seen = new Set<string>();
230
+ for (const entry of value) {
231
+ const parsed = trimString(entry);
232
+ if (!parsed) {
233
+ diagnostics.push({ type: "error", message: `${field} must contain only non-empty strings`, path: manifestPath });
234
+ continue;
235
+ }
236
+ if (seen.has(parsed)) {
237
+ diagnostics.push({ type: "error", message: `${field} contains duplicate entry '${parsed}'`, path: manifestPath });
238
+ continue;
239
+ }
240
+ seen.add(parsed);
241
+ out.push(parsed);
242
+ }
139
243
  return out.length > 0 ? out : undefined;
140
244
  }
141
245
 
@@ -150,6 +254,7 @@ export function parseExtensionManifest(
150
254
  if (!isRecord(value)) {
151
255
  return { diagnostics: [{ type: "error", message: "extension manifest must be an object", path: manifestPath }] };
152
256
  }
257
+ rejectUnknownKeys(value, MANIFEST_KEYS, "manifest", manifestPath, diagnostics);
153
258
  if (value.manifestVersion !== 1) {
154
259
  diagnostics.push({ type: "error", message: "manifestVersion must be 1", path: manifestPath });
155
260
  }
@@ -164,31 +269,36 @@ export function parseExtensionManifest(
164
269
  }
165
270
  if (!version) diagnostics.push({ type: "error", message: "version is required", path: manifestPath });
166
271
  if (!description) diagnostics.push({ type: "error", message: "description is required", path: manifestPath });
167
- const resources = normalizeResources(value.resources);
168
- const tools = stringArray(value.tools);
169
- const settings = stringArray(value.settings);
272
+ const resources = normalizeResources(value.resources, manifestPath, diagnostics);
273
+ const tools = stringArray(value.tools, "tools", manifestPath, diagnostics);
274
+ const settings = stringArray(value.settings, "settings", manifestPath, diagnostics);
170
275
  let compatibility: ClioExtensionManifest["compatibility"];
171
276
  if (value.compatibility !== undefined) {
172
277
  if (!isRecord(value.compatibility)) {
173
278
  diagnostics.push({ type: "error", message: "compatibility must be an object", path: manifestPath });
174
- } else if ("clio" in value.compatibility) {
175
- const range = trimString(value.compatibility.clio);
176
- if (range === undefined) {
177
- diagnostics.push({
178
- type: "error",
179
- message: "compatibility.clio must be a non-empty semver range",
180
- path: manifestPath,
181
- });
279
+ } else {
280
+ rejectUnknownKeys(value.compatibility, COMPATIBILITY_KEYS, "compatibility", manifestPath, diagnostics);
281
+ if (!("clio" in value.compatibility)) {
282
+ compatibility = {};
182
283
  } else {
183
- const evaluation = evaluateClioCompatibility(range);
184
- if (!evaluation.rangeValid) {
284
+ const range = trimString(value.compatibility.clio);
285
+ if (range === undefined) {
185
286
  diagnostics.push({
186
287
  type: "error",
187
- message: `extension ${id ?? "<unknown>"} declares malformed compatibility.clio range '${range}'`,
288
+ message: "compatibility.clio must be a non-empty semver range",
188
289
  path: manifestPath,
189
290
  });
190
291
  } else {
191
- compatibility = { clio: range };
292
+ const evaluation = evaluateClioCompatibility(range);
293
+ if (!evaluation.rangeValid) {
294
+ diagnostics.push({
295
+ type: "error",
296
+ message: `extension ${id ?? "<unknown>"} declares malformed compatibility.clio range '${range}'`,
297
+ path: manifestPath,
298
+ });
299
+ } else {
300
+ compatibility = { clio: range };
301
+ }
192
302
  }
193
303
  }
194
304
  }
@@ -268,7 +378,24 @@ export function discoverExtensionPackages(root: string): ExtensionCandidate[] {
268
378
  if (!existsSync(full)) {
269
379
  return [{ path: full, valid: false, diagnostics: [{ type: "error", message: "path does not exist", path: full }] }];
270
380
  }
271
- const stat = statSync(full);
381
+ let stat: ReturnType<typeof statSync>;
382
+ try {
383
+ stat = statSync(full);
384
+ } catch (error) {
385
+ return [
386
+ {
387
+ path: full,
388
+ valid: false,
389
+ diagnostics: [
390
+ {
391
+ type: "error",
392
+ message: `extension path could not be inspected: ${error instanceof Error ? error.message : String(error)}`,
393
+ path: full,
394
+ },
395
+ ],
396
+ },
397
+ ];
398
+ }
272
399
  if (!stat.isDirectory()) {
273
400
  return [
274
401
  {
@@ -278,16 +405,86 @@ export function discoverExtensionPackages(root: string): ExtensionCandidate[] {
278
405
  },
279
406
  ];
280
407
  }
281
- const direct = loadManifestFromRoot(full);
408
+ let canonicalFull: string;
409
+ try {
410
+ canonicalFull = realpathSync(full);
411
+ } catch (error) {
412
+ return [
413
+ {
414
+ path: full,
415
+ valid: false,
416
+ diagnostics: [
417
+ {
418
+ type: "error",
419
+ message: `extension path could not be canonicalized: ${error instanceof Error ? error.message : String(error)}`,
420
+ path: full,
421
+ },
422
+ ],
423
+ },
424
+ ];
425
+ }
426
+ const direct = loadManifestFromRoot(canonicalFull);
282
427
  if (direct.valid || direct.manifestPath) return [direct];
283
428
  const candidates: ExtensionCandidate[] = [];
284
- for (const entry of readdirSync(full, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
285
- if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
286
- const child = path.join(full, entry.name);
287
- const loaded = loadManifestFromRoot(child);
429
+ const discoveryFailures: ExtensionCandidate[] = [];
430
+ const spellingsByCanonicalRoot = new Map<string, string[]>();
431
+ let entries: Dirent<string>[];
432
+ try {
433
+ entries = readdirSync(canonicalFull, { withFileTypes: true, encoding: "utf8" });
434
+ } catch (error) {
435
+ return [
436
+ {
437
+ path: canonicalFull,
438
+ valid: false,
439
+ diagnostics: [
440
+ {
441
+ type: "error",
442
+ message: `extension directory could not be read: ${error instanceof Error ? error.message : String(error)}`,
443
+ path: canonicalFull,
444
+ },
445
+ ],
446
+ },
447
+ ];
448
+ }
449
+ for (const entry of entries.sort(compareNames)) {
450
+ if (entry.name.startsWith(".")) continue;
451
+ const child = path.join(canonicalFull, entry.name);
452
+ try {
453
+ if (!entry.isDirectory() && !(entry.isSymbolicLink() && statSync(child).isDirectory())) continue;
454
+ const canonicalChild = realpathSync(child);
455
+ const spellings = spellingsByCanonicalRoot.get(canonicalChild) ?? [];
456
+ spellings.push(child);
457
+ spellingsByCanonicalRoot.set(canonicalChild, spellings);
458
+ } catch (error) {
459
+ discoveryFailures.push({
460
+ path: child,
461
+ valid: false,
462
+ diagnostics: [
463
+ {
464
+ type: "error",
465
+ message: `extension child could not be canonicalized: ${error instanceof Error ? error.message : String(error)}`,
466
+ path: child,
467
+ },
468
+ ],
469
+ });
470
+ }
471
+ }
472
+ for (const canonicalChild of [...spellingsByCanonicalRoot.keys()].sort()) {
473
+ const loaded = loadManifestFromRoot(canonicalChild);
474
+ const spellings = spellingsByCanonicalRoot.get(canonicalChild) ?? [];
475
+ if (spellings.length > 1) {
476
+ loaded.diagnostics.push({
477
+ type: "warning",
478
+ message: `duplicate canonical extension root loaded once; discovered as ${spellings.sort().join(", ")}`,
479
+ path: canonicalChild,
480
+ });
481
+ }
288
482
  if (loaded.valid || loaded.manifestPath) candidates.push(loaded);
289
483
  }
290
- const discovered = candidates.length > 0 ? candidates : [direct];
484
+ const discovered = [...candidates, ...discoveryFailures].sort((a, b) =>
485
+ a.path < b.path ? -1 : a.path > b.path ? 1 : 0,
486
+ );
487
+ if (discovered.length === 0) discovered.push(direct);
291
488
  const ids = new Map<string, ExtensionCandidate[]>();
292
489
  for (const candidate of discovered) {
293
490
  const id = candidate.manifest?.id;
@@ -9,11 +9,76 @@ import {
9
9
  listInstalledExtensions,
10
10
  removeExtension,
11
11
  } from "./manager.js";
12
+ import {
13
+ type BuildExtensionSnapshotInput,
14
+ buildExtensionSnapshot,
15
+ diffExtensionSnapshots,
16
+ EXTENSION_SNAPSHOT_DIAGNOSTIC_MESSAGE_CAP,
17
+ } from "./snapshot.js";
18
+ import { bindExtensionSnapshotStore, createExtensionSnapshotStore } from "./snapshot-store.js";
19
+ import type {
20
+ ExtensionReloadCandidate,
21
+ ExtensionReloadRejection,
22
+ ExtensionReloadRejectionReason,
23
+ ExtensionSnapshotDiagnostics,
24
+ } from "./types.js";
25
+
26
+ /** Test seams only; the domain loader constructs the bundle with defaults. */
27
+ export interface ExtensionsBundleOptions {
28
+ cwd?: () => string;
29
+ now?: BuildExtensionSnapshotInput["now"];
30
+ listRecords?: BuildExtensionSnapshotInput["listRecords"];
31
+ }
32
+
33
+ function failureDiagnostics(error: unknown): ExtensionSnapshotDiagnostics {
34
+ const message = error instanceof Error ? error.message : String(error);
35
+ return {
36
+ entries: [{ type: "error", message: message.slice(0, EXTENSION_SNAPSHOT_DIAGNOSTIC_MESSAGE_CAP) }],
37
+ truncated: 0,
38
+ };
39
+ }
40
+
41
+ export function createExtensionsBundle(
42
+ _context: DomainContext,
43
+ options: ExtensionsBundleOptions = {},
44
+ ): DomainBundle<ExtensionsContract> {
45
+ const store = createExtensionSnapshotStore();
46
+ const cwd = options.cwd ?? (() => process.cwd());
47
+ const build = (generation: number) =>
48
+ buildExtensionSnapshot({
49
+ cwd: cwd(),
50
+ generation,
51
+ ...(options.now !== undefined ? { now: options.now } : {}),
52
+ ...(options.listRecords !== undefined ? { listRecords: options.listRecords } : {}),
53
+ });
54
+ /**
55
+ * The single prepared-but-unpublished candidate. Holding it here is what
56
+ * makes prepare reentrancy-safe and lets a candidate tell whether it is
57
+ * still the one in flight.
58
+ */
59
+ let inFlight: ExtensionReloadCandidate | null = null;
60
+
61
+ const rejected = (
62
+ reason: ExtensionReloadRejectionReason,
63
+ diagnostics: ExtensionSnapshotDiagnostics,
64
+ ): ExtensionReloadRejection => ({
65
+ status: "rejected",
66
+ reason,
67
+ generation: store.current()?.generation ?? 0,
68
+ diagnostics,
69
+ });
12
70
 
13
- export function createExtensionsBundle(_context: DomainContext): DomainBundle<ExtensionsContract> {
14
71
  const extension: DomainExtension = {
15
72
  start() {
16
- return undefined;
73
+ // Bind an empty store only. Until the composition root publishes the
74
+ // boot generation together with its user hooks, every reader takes
75
+ // the ephemeral generation-0 path, so no consumer can observe
76
+ // extension resources paired with hooks from another generation.
77
+ bindExtensionSnapshotStore(store);
78
+ },
79
+ stop() {
80
+ inFlight = null;
81
+ bindExtensionSnapshotStore(null);
17
82
  },
18
83
  };
19
84
  const contract: ExtensionsContract = {
@@ -35,11 +100,45 @@ export function createExtensionsBundle(_context: DomainContext): DomainBundle<Ex
35
100
  remove(id, options = {}) {
36
101
  return removeExtension(id, options);
37
102
  },
38
- resourceRoots(kind, cwd) {
39
- return enabledExtensionResourceRoots(kind, cwd);
103
+ resourceRoots(kind, requestedCwd) {
104
+ return enabledExtensionResourceRoots(kind, requestedCwd ?? cwd());
105
+ },
106
+ snapshot() {
107
+ return store.current();
108
+ },
109
+ generation() {
110
+ return store.current()?.generation ?? 0;
40
111
  },
41
- async reload() {
42
- return undefined;
112
+ prepareReload() {
113
+ if (inFlight !== null) return rejected("reentrant", { entries: [], truncated: 0 });
114
+ const previous = store.current();
115
+ // Reserve before building so a discarded candidate burns its number
116
+ // and generations stay strictly monotonic without a second counter.
117
+ const generation = store.nextGeneration();
118
+ let snapshot: ReturnType<typeof build>;
119
+ try {
120
+ snapshot = build(generation);
121
+ } catch (error) {
122
+ return rejected("build-failed", failureDiagnostics(error));
123
+ }
124
+ const candidate: ExtensionReloadCandidate = {
125
+ generation,
126
+ previousGeneration: previous?.generation ?? 0,
127
+ snapshot,
128
+ ...diffExtensionSnapshots(previous, snapshot),
129
+ // Identity on the committed reference covers every intervening
130
+ // publish; the in-flight check covers discard and double publish.
131
+ current: () => inFlight === candidate && store.current() === previous,
132
+ publish() {
133
+ store.publish(snapshot);
134
+ inFlight = null;
135
+ },
136
+ discard() {
137
+ if (inFlight === candidate) inFlight = null;
138
+ },
139
+ };
140
+ inFlight = candidate;
141
+ return { status: "prepared", candidate };
43
142
  },
44
143
  };
45
144
  return { extension, contract };
@@ -15,20 +15,44 @@ export {
15
15
  discoverExtensionPackages,
16
16
  type ExtensionCandidate,
17
17
  type ExtensionDiagnostic,
18
+ type ExtensionHookSource,
18
19
  type ExtensionInstallOptions,
19
20
  type ExtensionInstallResult,
20
21
  type ExtensionListOptions,
21
22
  type ExtensionMutationResult,
23
+ type ExtensionProvenance,
24
+ type ExtensionReloadCandidate,
25
+ type ExtensionReloadCommitted,
26
+ type ExtensionReloadPrepareResult,
27
+ type ExtensionReloadRejection,
28
+ type ExtensionReloadRejectionReason,
29
+ type ExtensionReloadResult,
22
30
  type ExtensionResourceKind,
23
31
  type ExtensionResourceRoot,
24
32
  type ExtensionScope,
33
+ type ExtensionSnapshot,
34
+ type ExtensionSnapshotDiagnostics,
35
+ type ExtensionStateUpgradeReport,
25
36
  enabledExtensionResourceRoots,
26
37
  enableExtension,
27
38
  extensionManifestYaml,
39
+ extensionSnapshotFor,
28
40
  type InstalledExtension,
41
+ type InstalledExtensionRecord,
29
42
  installExtension,
43
+ isLoadableExtension,
44
+ type LoadableExtension,
45
+ listInstalledExtensionRecords,
30
46
  listInstalledExtensions,
31
47
  parseExtensionManifest,
32
48
  removeExtension,
49
+ upgradeLegacyExtensionInstallState,
33
50
  } from "./manager.js";
34
51
  export { ExtensionsManifest } from "./manifest.js";
52
+ export {
53
+ buildExtensionSnapshot,
54
+ diffExtensionSnapshots,
55
+ EXTENSION_SNAPSHOT_DIAGNOSTIC_CAP,
56
+ EXTENSION_SNAPSHOT_DIAGNOSTIC_MESSAGE_CAP,
57
+ EXTENSION_SNAPSHOT_DIAGNOSTIC_PER_PACKAGE_CAP,
58
+ } from "./snapshot.js";