@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
@@ -0,0 +1,189 @@
1
+ import { createHash } from "node:crypto";
2
+ import {
3
+ closeSync,
4
+ constants,
5
+ fstatSync,
6
+ lstatSync,
7
+ openSync,
8
+ readdirSync,
9
+ readFileSync,
10
+ readlinkSync,
11
+ realpathSync,
12
+ type Stats,
13
+ } from "node:fs";
14
+ import path from "node:path";
15
+
16
+ export interface ExtensionContentDigestOptions {
17
+ /** Root-relative regular files whose exact hashed bytes should be returned. */
18
+ capture?: ReadonlyArray<string>;
19
+ }
20
+
21
+ export interface ExtensionContentDigestResult {
22
+ digest: string;
23
+ captured: ReadonlyMap<string, Buffer>;
24
+ }
25
+
26
+ function contained(root: string, candidate: string): boolean {
27
+ const relative = path.relative(root, candidate);
28
+ return relative === "" || (!relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative));
29
+ }
30
+
31
+ function digestFrame(hash: ReturnType<typeof createHash>, kind: string, relativePath: string, payload?: Buffer): void {
32
+ const header = Buffer.from(
33
+ `${kind}\0${relativePath.replaceAll(path.sep, "/")}\0${payload?.byteLength ?? 0}\0`,
34
+ "utf8",
35
+ );
36
+ hash.update(header);
37
+ if (payload) hash.update(payload);
38
+ hash.update("\0");
39
+ }
40
+
41
+ function sameFileState(left: Stats, right: Stats): boolean {
42
+ return (
43
+ left.dev === right.dev &&
44
+ left.ino === right.ino &&
45
+ left.mode === right.mode &&
46
+ left.nlink === right.nlink &&
47
+ left.size === right.size &&
48
+ left.mtimeMs === right.mtimeMs &&
49
+ left.ctimeMs === right.ctimeMs
50
+ );
51
+ }
52
+
53
+ function readStableRegularFile(filePath: string, inspected: Stats): Buffer {
54
+ const fd = openSync(filePath, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
55
+ try {
56
+ const opened = fstatSync(fd);
57
+ if (!opened.isFile() || opened.nlink !== 1 || !sameFileState(inspected, opened)) {
58
+ throw new Error(`extension file changed while being opened: ${filePath}`);
59
+ }
60
+ const payload = readFileSync(fd);
61
+ if (!sameFileState(opened, fstatSync(fd))) {
62
+ throw new Error(`extension file changed while being hashed: ${filePath}`);
63
+ }
64
+ return payload;
65
+ } finally {
66
+ closeSync(fd);
67
+ }
68
+ }
69
+
70
+ function readStableContainedLink(linkPath: string, inspected: Stats, canonicalRoot: string): Buffer {
71
+ const canonicalTarget = realpathSync(linkPath);
72
+ if (!contained(canonicalRoot, canonicalTarget)) {
73
+ throw new Error(`symbolic link escapes the extension root: ${linkPath}`);
74
+ }
75
+ const target = readlinkSync(linkPath);
76
+ const after = lstatSync(linkPath);
77
+ if (!after.isSymbolicLink() || !sameFileState(inspected, after)) {
78
+ throw new Error(`symbolic link changed while being hashed: ${linkPath}`);
79
+ }
80
+ const canonicalAfter = realpathSync(linkPath);
81
+ if (canonicalAfter !== canonicalTarget || !contained(canonicalRoot, canonicalAfter)) {
82
+ throw new Error(`symbolic link changed or escapes the extension root: ${linkPath}`);
83
+ }
84
+ return Buffer.from(target, "utf8");
85
+ }
86
+
87
+ function assertStableDirectory(
88
+ directoryPath: string,
89
+ inspected: Stats,
90
+ canonicalDirectory: string,
91
+ canonicalRoot: string,
92
+ expectedEntries?: readonly string[],
93
+ ): void {
94
+ const after = lstatSync(directoryPath);
95
+ if (!after.isDirectory() || !sameFileState(inspected, after)) {
96
+ throw new Error(`extension directory changed while being hashed: ${directoryPath}`);
97
+ }
98
+ const canonicalAfter = realpathSync(directoryPath);
99
+ if (canonicalAfter !== canonicalDirectory || !contained(canonicalRoot, canonicalAfter)) {
100
+ throw new Error(`extension directory changed or escapes the extension root: ${directoryPath}`);
101
+ }
102
+ if (expectedEntries !== undefined) {
103
+ const currentEntries = readdirSync(directoryPath).sort();
104
+ if (
105
+ currentEntries.length !== expectedEntries.length ||
106
+ currentEntries.some((entry, index) => entry !== expectedEntries[index])
107
+ ) {
108
+ throw new Error(`extension directory entries changed while being hashed: ${directoryPath}`);
109
+ }
110
+ }
111
+ }
112
+
113
+ function readStableDirectory(
114
+ directoryPath: string,
115
+ inspected: Stats,
116
+ canonicalRoot: string,
117
+ ): { canonicalDirectory: string; entries: string[] } {
118
+ const canonicalDirectory = realpathSync(directoryPath);
119
+ if (!contained(canonicalRoot, canonicalDirectory)) {
120
+ throw new Error(`extension directory escapes the extension root: ${directoryPath}`);
121
+ }
122
+ const entries = readdirSync(directoryPath).sort();
123
+ assertStableDirectory(directoryPath, inspected, canonicalDirectory, canonicalRoot);
124
+ return { canonicalDirectory, entries };
125
+ }
126
+
127
+ /**
128
+ * Hash an extension tree in stable relative-path order. File names, node kinds,
129
+ * symlink targets, empty directories, and file bytes are all integrity-bound.
130
+ * Symlinks must resolve within the package so their content cannot drift outside
131
+ * the tree without changing the digest.
132
+ */
133
+ export function extensionContentDigestWithCapture(
134
+ root: string,
135
+ options: ExtensionContentDigestOptions = {},
136
+ ): ExtensionContentDigestResult {
137
+ const resolvedRoot = path.resolve(root);
138
+ const rootStat = lstatSync(resolvedRoot);
139
+ if (rootStat.isSymbolicLink()) {
140
+ throw new Error(`extension root must be a directory, not a symbolic link: ${resolvedRoot}`);
141
+ }
142
+ if (!rootStat.isDirectory()) {
143
+ throw new Error(`extension root must be a directory: ${resolvedRoot}`);
144
+ }
145
+ const canonicalRoot = realpathSync(resolvedRoot);
146
+ const canonicalizedRootStat = lstatSync(resolvedRoot);
147
+ if (!canonicalizedRootStat.isDirectory() || !sameFileState(rootStat, canonicalizedRootStat)) {
148
+ throw new Error(`extension root changed while being canonicalized: ${resolvedRoot}`);
149
+ }
150
+ const hash = createHash("sha256");
151
+ const requested = new Set((options.capture ?? []).map((entry) => entry.replaceAll(path.sep, "/")));
152
+ const captured = new Map<string, Buffer>();
153
+ const visit = (absolutePath: string, relativePath: string): void => {
154
+ const stat = lstatSync(absolutePath);
155
+ if (stat.isSymbolicLink()) {
156
+ if (relativePath === "") {
157
+ throw new Error(`extension root changed to a symbolic link while being hashed: ${absolutePath}`);
158
+ }
159
+ digestFrame(hash, "link", relativePath, readStableContainedLink(absolutePath, stat, canonicalRoot));
160
+ return;
161
+ }
162
+ if (stat.isDirectory()) {
163
+ digestFrame(hash, "directory", relativePath);
164
+ const { canonicalDirectory, entries } = readStableDirectory(absolutePath, stat, canonicalRoot);
165
+ for (const entry of entries) {
166
+ visit(path.join(absolutePath, entry), relativePath ? path.join(relativePath, entry) : entry);
167
+ }
168
+ assertStableDirectory(absolutePath, stat, canonicalDirectory, canonicalRoot, entries);
169
+ return;
170
+ }
171
+ if (stat.isFile()) {
172
+ if (stat.nlink !== 1) {
173
+ throw new Error(`hard-linked file is unsupported in an extension: ${absolutePath}`);
174
+ }
175
+ const payload = readStableRegularFile(absolutePath, stat);
176
+ digestFrame(hash, "file", relativePath, payload);
177
+ const key = relativePath.replaceAll(path.sep, "/");
178
+ if (requested.has(key)) captured.set(key, payload);
179
+ return;
180
+ }
181
+ throw new Error(`unsupported filesystem entry in extension: ${absolutePath}`);
182
+ };
183
+ visit(resolvedRoot, "");
184
+ return { digest: hash.digest("hex"), captured };
185
+ }
186
+
187
+ export function extensionContentDigest(root: string): string {
188
+ return extensionContentDigestWithCapture(root).digest;
189
+ }
@@ -4,27 +4,43 @@ export {
4
4
  findExtensionManifestPath,
5
5
  parseExtensionManifest,
6
6
  } from "./discovery.js";
7
- export { enabledExtensionResourceRoots, extensionResourcePath } from "./resources.js";
7
+ export { enabledExtensionResourceRoots, extensionResourcePath, extensionSnapshotFor } from "./resources.js";
8
+ export type { InstalledExtensionRecord } from "./state.js";
8
9
  export {
9
10
  disableExtension,
10
11
  enableExtension,
11
12
  extensionBaseDir,
12
13
  installExtension,
14
+ listInstalledExtensionRecords,
13
15
  listInstalledExtensions,
14
16
  removeExtension,
17
+ upgradeLegacyExtensionInstallState,
15
18
  } from "./state.js";
16
19
  export type {
17
20
  ClioExtensionManifest,
18
21
  ExtensionCandidate,
19
22
  ExtensionDiagnostic,
23
+ ExtensionHookSource,
20
24
  ExtensionInstallOptions,
21
25
  ExtensionInstallResult,
22
26
  ExtensionListOptions,
23
27
  ExtensionManifestResources,
24
28
  ExtensionMutationResult,
29
+ ExtensionProvenance,
30
+ ExtensionReloadCandidate,
31
+ ExtensionReloadCommitted,
32
+ ExtensionReloadPrepareResult,
33
+ ExtensionReloadRejection,
34
+ ExtensionReloadRejectionReason,
35
+ ExtensionReloadResult,
25
36
  ExtensionResourceKind,
26
37
  ExtensionResourceRoot,
27
38
  ExtensionScope,
39
+ ExtensionSnapshot,
40
+ ExtensionSnapshotDiagnostics,
28
41
  ExtensionState,
42
+ ExtensionStateUpgradeReport,
29
43
  InstalledExtension,
44
+ LoadableExtension,
30
45
  } from "./types.js";
46
+ export { isLoadableExtension } from "./types.js";
@@ -0,0 +1,27 @@
1
+ import { realpathSync, statSync } from "node:fs";
2
+ import path from "node:path";
3
+
4
+ export function extensionResourcePath(rootPath: string, resourcePath: string): string | null {
5
+ const root = path.resolve(rootPath);
6
+ const full = path.resolve(root, resourcePath);
7
+ const relative = path.relative(root, full);
8
+ if (relative === "" || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
9
+ return null;
10
+ }
11
+ try {
12
+ const rootReal = realpathSync(root);
13
+ const fullReal = realpathSync(full);
14
+ const canonicalRelative = path.relative(rootReal, fullReal);
15
+ if (
16
+ canonicalRelative === ".." ||
17
+ canonicalRelative.startsWith(`..${path.sep}`) ||
18
+ path.isAbsolute(canonicalRelative)
19
+ ) {
20
+ return null;
21
+ }
22
+ if (!statSync(fullReal).isDirectory()) return null;
23
+ return fullReal;
24
+ } catch {
25
+ return null;
26
+ }
27
+ }
@@ -1,50 +1,30 @@
1
- import { lstatSync, realpathSync } from "node:fs";
1
+ import { realpathSync } from "node:fs";
2
2
  import path from "node:path";
3
- import { listInstalledExtensions } from "./state.js";
4
- import type { ExtensionResourceKind, ExtensionResourceRoot } from "./types.js";
3
+ import { buildExtensionSnapshot } from "./snapshot.js";
4
+ import { committedExtensionSnapshot } from "./snapshot-store.js";
5
+ import type { ExtensionResourceKind, ExtensionResourceRoot, ExtensionSnapshot } from "./types.js";
5
6
 
6
- export function extensionResourcePath(rootPath: string, resourcePath: string): string | null {
7
- const root = path.resolve(rootPath);
8
- const full = path.resolve(root, resourcePath);
9
- const relative = path.relative(root, full);
10
- if (relative === "" || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative))
11
- return null;
7
+ export { extensionResourcePath } from "./resource-path.js";
8
+
9
+ /**
10
+ * The committed snapshot when a store is bound for this cwd; otherwise an
11
+ * ephemeral generation-0 build that never touches the store. The ephemeral
12
+ * branch serves the CLI, `config inspect` with an explicit cwd, and any
13
+ * process that never booted the extensions bundle.
14
+ */
15
+ export function extensionSnapshotFor(cwd = process.cwd()): ExtensionSnapshot {
16
+ const committed = committedExtensionSnapshot();
12
17
  try {
13
- const rootReal = realpathSync(root);
14
- const fullReal = realpathSync(full);
15
- const canonicalRelative = path.relative(rootReal, fullReal);
16
- if (
17
- canonicalRelative === ".." ||
18
- canonicalRelative.startsWith(`..${path.sep}`) ||
19
- path.isAbsolute(canonicalRelative)
20
- ) {
21
- return null;
22
- }
23
- if (!lstatSync(full).isDirectory()) return null;
24
- return full;
18
+ if (committed !== null && committed.cwd === realpathSync(path.resolve(cwd))) return committed;
25
19
  } catch {
26
- return null;
20
+ // Fall through to an ephemeral build, which retains the original error semantics.
27
21
  }
22
+ return buildExtensionSnapshot({ cwd, generation: 0 });
28
23
  }
29
24
 
30
25
  export function enabledExtensionResourceRoots(
31
26
  kind: ExtensionResourceKind,
32
27
  cwd = process.cwd(),
33
28
  ): ExtensionResourceRoot[] {
34
- const roots: ExtensionResourceRoot[] = [];
35
- for (const entry of listInstalledExtensions(cwd)) {
36
- if (!entry.enabled || !entry.compatible || !entry.effective) continue;
37
- const rel = entry.resources[kind];
38
- if (!rel) continue;
39
- const full = extensionResourcePath(entry.rootPath, rel);
40
- if (!full) continue;
41
- roots.push({
42
- id: entry.id,
43
- scope: entry.scope,
44
- path: full,
45
- rootPath: entry.rootPath,
46
- source: `extension:${entry.scope}:${entry.id}`,
47
- });
48
- }
49
- return roots;
29
+ return [...extensionSnapshotFor(cwd).resourceRoots[kind]];
50
30
  }
@@ -0,0 +1,39 @@
1
+ import type { ExtensionSnapshot } from "./types.js";
2
+
3
+ /**
4
+ * Holder for the committed snapshot reference plus the process-local
5
+ * generation counter. `publish` is assignment-only: validation that a
6
+ * snapshot is strictly newer belongs to the candidate that prepared it, on
7
+ * the same stack, immediately before publishing.
8
+ */
9
+ export interface ExtensionSnapshotStore {
10
+ current(): ExtensionSnapshot | null;
11
+ publish(snapshot: ExtensionSnapshot): void;
12
+ /** Reserve and return the next process-local generation. Reservations are never reused. */
13
+ nextGeneration(): number;
14
+ }
15
+
16
+ export function createExtensionSnapshotStore(): ExtensionSnapshotStore {
17
+ let committed: ExtensionSnapshot | null = null;
18
+ let reservedGeneration = 0;
19
+ return {
20
+ current: () => committed,
21
+ publish(snapshot) {
22
+ committed = snapshot;
23
+ },
24
+ nextGeneration() {
25
+ reservedGeneration += 1;
26
+ return reservedGeneration;
27
+ },
28
+ };
29
+ }
30
+
31
+ let boundStore: ExtensionSnapshotStore | null = null;
32
+
33
+ export function committedExtensionSnapshot(): ExtensionSnapshot | null {
34
+ return boundStore?.current() ?? null;
35
+ }
36
+
37
+ export function bindExtensionSnapshotStore(store: ExtensionSnapshotStore | null): void {
38
+ boundStore = store;
39
+ }
@@ -0,0 +1,180 @@
1
+ import { createHash } from "node:crypto";
2
+ import { realpathSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { parse as parseYaml } from "yaml";
5
+ import { extensionResourcePath } from "./resource-path.js";
6
+ import { type InstalledExtensionRecord, listInstalledExtensionRecords } from "./state.js";
7
+ import {
8
+ type ExtensionDiagnostic,
9
+ type ExtensionHookSource,
10
+ type ExtensionResourceKind,
11
+ type ExtensionResourceRoot,
12
+ type ExtensionSnapshot,
13
+ type ExtensionSnapshotDiagnostics,
14
+ isLoadableExtension,
15
+ } from "./types.js";
16
+
17
+ export const EXTENSION_SNAPSHOT_DIAGNOSTIC_CAP = 200;
18
+ export const EXTENSION_SNAPSHOT_DIAGNOSTIC_PER_PACKAGE_CAP = 20;
19
+ export const EXTENSION_SNAPSHOT_DIAGNOSTIC_MESSAGE_CAP = 512;
20
+
21
+ const RESOURCE_KINDS: ReadonlyArray<ExtensionResourceKind> = ["skills", "prompts", "agents", "fleets", "themes"];
22
+
23
+ export interface BuildExtensionSnapshotInput {
24
+ cwd: string;
25
+ generation: number;
26
+ now?: () => Date;
27
+ listRecords?: (cwd?: string, options?: { all?: boolean }) => InstalledExtensionRecord[];
28
+ }
29
+
30
+ function canonicalJson(value: unknown): string {
31
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
32
+ if (value !== null && typeof value === "object") {
33
+ const record = value as Record<string, unknown>;
34
+ return `{${Object.keys(record)
35
+ .sort()
36
+ .map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`)
37
+ .join(",")}}`;
38
+ }
39
+ return JSON.stringify(value) ?? "null";
40
+ }
41
+
42
+ function deepFreeze<T>(value: T, seen = new Set<object>()): T {
43
+ if (value === null || typeof value !== "object" || seen.has(value as object)) return value;
44
+ seen.add(value as object);
45
+ for (const child of Object.values(value as Record<string, unknown>)) deepFreeze(child, seen);
46
+ return Object.freeze(value);
47
+ }
48
+
49
+ function boundedDiagnostics(records: ReadonlyArray<InstalledExtensionRecord>): ExtensionSnapshotDiagnostics {
50
+ const entries: Array<ExtensionDiagnostic & { extensionId?: string }> = [];
51
+ let truncated = 0;
52
+ for (const { entry } of records) {
53
+ let admittedForPackage = 0;
54
+ for (const diagnostic of entry.diagnostics) {
55
+ if (
56
+ admittedForPackage >= EXTENSION_SNAPSHOT_DIAGNOSTIC_PER_PACKAGE_CAP ||
57
+ entries.length >= EXTENSION_SNAPSHOT_DIAGNOSTIC_CAP
58
+ ) {
59
+ truncated += 1;
60
+ continue;
61
+ }
62
+ entries.push({
63
+ type: diagnostic.type,
64
+ message: diagnostic.message.slice(0, EXTENSION_SNAPSHOT_DIAGNOSTIC_MESSAGE_CAP),
65
+ ...(diagnostic.path ? { path: diagnostic.path } : {}),
66
+ extensionId: entry.id,
67
+ });
68
+ admittedForPackage += 1;
69
+ }
70
+ }
71
+ return { entries, truncated };
72
+ }
73
+
74
+ export function buildExtensionSnapshot(input: BuildExtensionSnapshotInput): ExtensionSnapshot {
75
+ const cwd = realpathSync(path.resolve(input.cwd));
76
+ const records = (input.listRecords ?? listInstalledExtensionRecords)(cwd, { all: true });
77
+ const resourceRoots: Record<ExtensionResourceKind, ExtensionResourceRoot[]> = {
78
+ skills: [],
79
+ prompts: [],
80
+ agents: [],
81
+ fleets: [],
82
+ themes: [],
83
+ };
84
+ const hookSources: ExtensionHookSource[] = [];
85
+ const digestPackages: unknown[] = [];
86
+
87
+ for (const record of records) {
88
+ const { entry } = record;
89
+ const packageRoots: Array<{ kind: ExtensionResourceKind; path: string }> = [];
90
+ let hooksDigest: string | undefined;
91
+ if (isLoadableExtension(entry)) {
92
+ for (const kind of RESOURCE_KINDS) {
93
+ const relative = entry.resources[kind];
94
+ if (!relative) continue;
95
+ const resolved = extensionResourcePath(entry.rootPath, relative);
96
+ if (!resolved) continue;
97
+ resourceRoots[kind].push({
98
+ id: entry.id,
99
+ scope: entry.scope,
100
+ path: resolved,
101
+ rootPath: entry.rootPath,
102
+ source: `extension:${entry.scope}:${entry.id}`,
103
+ provenance: entry.provenance,
104
+ generation: input.generation,
105
+ });
106
+ packageRoots.push({ kind, path: resolved });
107
+ }
108
+ const hookBytes = record.captured?.get("hooks.yaml");
109
+ if (hookBytes !== undefined) {
110
+ hooksDigest = createHash("sha256").update(hookBytes).digest("hex");
111
+ let declarations: unknown = [];
112
+ let parseError: string | undefined;
113
+ try {
114
+ declarations = parseYaml(hookBytes.toString("utf8")) ?? [];
115
+ } catch (error) {
116
+ parseError = error instanceof Error ? error.message : String(error);
117
+ }
118
+ hookSources.push({
119
+ provenance: entry.provenance,
120
+ declarationsDigest: hooksDigest,
121
+ declarations,
122
+ ...(parseError ? { parseError } : {}),
123
+ });
124
+ }
125
+ }
126
+ digestPackages.push({
127
+ id: entry.id,
128
+ scope: entry.scope,
129
+ loadable: entry.loadable,
130
+ ...(entry.provenance ? { provenance: entry.provenance } : {}),
131
+ resourceRoots: packageRoots,
132
+ ...(hooksDigest ? { hooksDigest } : {}),
133
+ });
134
+ }
135
+
136
+ const snapshot: ExtensionSnapshot = {
137
+ version: 1,
138
+ generation: input.generation,
139
+ cwd,
140
+ builtAt: (input.now ?? (() => new Date()))().toISOString(),
141
+ digest: createHash("sha256").update(canonicalJson(digestPackages)).digest("hex"),
142
+ packages: records.map((record) => record.entry),
143
+ resourceRoots,
144
+ hookSources,
145
+ diagnostics: boundedDiagnostics(records),
146
+ };
147
+ return deepFreeze(snapshot);
148
+ }
149
+
150
+ function packageFingerprints(snapshot: ExtensionSnapshot): Map<string, string> {
151
+ const grouped = new Map<string, unknown[]>();
152
+ for (const entry of snapshot.packages) {
153
+ const list = grouped.get(entry.id) ?? [];
154
+ list.push({
155
+ scope: entry.scope,
156
+ loadable: entry.loadable,
157
+ provenance: entry.provenance ?? null,
158
+ resources: entry.resources,
159
+ });
160
+ grouped.set(entry.id, list);
161
+ }
162
+ return new Map(
163
+ [...grouped].map(([id, values]) => [id, createHash("sha256").update(canonicalJson(values)).digest("hex")]),
164
+ );
165
+ }
166
+
167
+ export function diffExtensionSnapshots(
168
+ previous: ExtensionSnapshot | null,
169
+ next: ExtensionSnapshot,
170
+ ): { changed: boolean; added: string[]; removed: string[]; modified: string[] } {
171
+ if (previous === null) {
172
+ return { changed: true, added: [...new Set(next.packages.map((entry) => entry.id))], removed: [], modified: [] };
173
+ }
174
+ const before = packageFingerprints(previous);
175
+ const after = packageFingerprints(next);
176
+ const added = [...after.keys()].filter((id) => !before.has(id));
177
+ const removed = [...before.keys()].filter((id) => !after.has(id));
178
+ const modified = [...after.keys()].filter((id) => before.has(id) && before.get(id) !== after.get(id));
179
+ return { changed: previous.digest !== next.digest, added, removed, modified };
180
+ }