@zuzuucodes/cli 1.4.0 → 1.6.0

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 (211) hide show
  1. package/bin/zuzuu.mjs +20 -4
  2. package/package.json +1 -1
  3. package/web-app/dist/auth.js +91 -0
  4. package/web-app/dist/server.js +16 -79
  5. package/web-app/dist/zuzuu-cli.js +124 -0
  6. package/web-app/dist/{zuzuu-api.js → zuzuu-routes.js} +158 -133
  7. package/web-app/web-dist/assets/CommandPalette-DhBdR7X3.js +45 -0
  8. package/web-app/web-dist/assets/DiffTab-CqxwSjI2.js +1 -0
  9. package/web-app/web-dist/assets/EditorPane-94QPFR9R.js +41 -0
  10. package/web-app/web-dist/assets/MonacoFile-D76epTrG.js +1 -0
  11. package/web-app/web-dist/assets/angular-html-BVBpGdXr.js +1 -0
  12. package/web-app/web-dist/assets/{angular-ts-CD_OonCa.js → angular-ts-BfdufMKP.js} +1 -1
  13. package/web-app/web-dist/assets/{apl-uOGC3x4e.js → apl-DWBSSoBH.js} +1 -1
  14. package/web-app/web-dist/assets/{astro-B6ybQmWG.js → astro-3LtMP0Sq.js} +1 -1
  15. package/web-app/web-dist/assets/{blade-B1QGRlVx.js → blade-llJRbbtR.js} +1 -1
  16. package/web-app/web-dist/assets/c-Wt1voDr2.js +1 -0
  17. package/web-app/web-dist/assets/{cobol-BgqgtYWn.js → cobol-x_HIyl2P.js} +1 -1
  18. package/web-app/web-dist/assets/{coffee-0wIRKYlr.js → coffee-CThvmt4R.js} +1 -1
  19. package/web-app/web-dist/assets/cpp-NtAeskI3.js +1 -0
  20. package/web-app/web-dist/assets/{crystal-CyTK3qFN.js → crystal-DNu_sX0G.js} +1 -1
  21. package/web-app/web-dist/assets/css-DJp_X0uY.js +1 -0
  22. package/web-app/web-dist/assets/{cssMode-B9jnrWOz.js → cssMode-ByQBaInt.js} +1 -1
  23. package/web-app/web-dist/assets/dist-DQqjtuhV.js +153 -0
  24. package/web-app/web-dist/assets/{edge-CvML9pwC.js → edge-ozw5tpLl.js} +1 -1
  25. package/web-app/web-dist/assets/{editor.api2-BmGoRSl4.js → editor.api2-C7skgoRB.js} +1 -1
  26. package/web-app/web-dist/assets/{elixir-CrjqTiSc.js → elixir-VhA6FeZt.js} +1 -1
  27. package/web-app/web-dist/assets/{elm-C4JtJ0Au.js → elm-dREJmIFz.js} +1 -1
  28. package/web-app/web-dist/assets/{erb-Cmeb-29V.js → erb-CIg6G69l.js} +1 -1
  29. package/web-app/web-dist/assets/{freemarker2-B5LAi19B.js → freemarker2-CBBwP9JV.js} +1 -1
  30. package/web-app/web-dist/assets/{git-rebase-CXqdToiP.js → git-rebase-B44mJPta.js} +1 -1
  31. package/web-app/web-dist/assets/{glimmer-js-Kq-kdTyV.js → glimmer-js-vH_gHG0-.js} +1 -1
  32. package/web-app/web-dist/assets/{glimmer-ts-D0RKLJNf.js → glimmer-ts--abOzSAQ.js} +1 -1
  33. package/web-app/web-dist/assets/glsl-Dv5r7kPw.js +1 -0
  34. package/web-app/web-dist/assets/graphql-CB4jsw2E.js +1 -0
  35. package/web-app/web-dist/assets/{hack-trjVF3Po.js → hack-DvEYX148.js} +1 -1
  36. package/web-app/web-dist/assets/haml-zE6W3STP.js +1 -0
  37. package/web-app/web-dist/assets/{handlebars-B8_x7Zx7.js → handlebars-CzBR2SDs.js} +1 -1
  38. package/web-app/web-dist/assets/{handlebars-g7ZhGhI_.js → handlebars-tXdfxEd6.js} +1 -1
  39. package/web-app/web-dist/assets/html-C8UlPnhE.js +1 -0
  40. package/web-app/web-dist/assets/{html-CfvRMgoC.js → html-DgPn1QYH.js} +1 -1
  41. package/web-app/web-dist/assets/{html-derivative-BYX_F_XH.js → html-derivative-CY6NRz-J.js} +1 -1
  42. package/web-app/web-dist/assets/{htmlMode-Bi8vSvwb.js → htmlMode-BtdIDgA2.js} +1 -1
  43. package/web-app/web-dist/assets/{http-BIVDpHT-.js → http-Cyd7bS_S.js} +1 -1
  44. package/web-app/web-dist/assets/{hurl-CFsshMju.js → hurl-CWPsiEpf.js} +1 -1
  45. package/web-app/web-dist/assets/index-B27_WOhS.css +2 -0
  46. package/web-app/web-dist/assets/index-De6DWTZM.js +7 -0
  47. package/web-app/web-dist/assets/java-CGc3VwQr.js +1 -0
  48. package/web-app/web-dist/assets/{javascript-Bxx2wV4w.js → javascript-5m05n-Be.js} +1 -1
  49. package/web-app/web-dist/assets/javascript-CUt1pgmJ.js +1 -0
  50. package/web-app/web-dist/assets/{jinja-_ZS5zWwe.js → jinja-CD-Z-FLd.js} +1 -1
  51. package/web-app/web-dist/assets/{jison-D8mMEpcs.js → jison-imPNup1l.js} +1 -1
  52. package/web-app/web-dist/assets/json-Bg9ijW3F.js +1 -0
  53. package/web-app/web-dist/assets/{jsonMode-C6ELX5GM.js → jsonMode-BG32YnTY.js} +1 -1
  54. package/web-app/web-dist/assets/jsx-CY6oMTks.js +1 -0
  55. package/web-app/web-dist/assets/{julia-D4h2DZrs.js → julia-Dc3O-irA.js} +1 -1
  56. package/web-app/web-dist/assets/{just-bMqQi3xg.js → just-BhOq_Kbv.js} +1 -1
  57. package/web-app/web-dist/assets/{latex-DThYi3CX.js → latex-Cu4Y1d5w.js} +1 -1
  58. package/web-app/web-dist/assets/lib-KIOQTlcs.js +1 -0
  59. package/web-app/web-dist/assets/{liquid-CUjzzP4r.js → liquid-3ZnQzTbs.js} +1 -1
  60. package/web-app/web-dist/assets/{liquid-CesB-zzl.js → liquid-CvXMrjlQ.js} +1 -1
  61. package/web-app/web-dist/assets/{lspLanguageFeatures-gTnJsses.js → lspLanguageFeatures-6KXALSrl.js} +1 -1
  62. package/web-app/web-dist/assets/lua-BjLEUjKY.js +1 -0
  63. package/web-app/web-dist/assets/{marko-yoGoLK2m.js → marko-DvhNOisQ.js} +1 -1
  64. package/web-app/web-dist/assets/{mdc-BvtXU6eH.js → mdc-Bm9TpL1X.js} +1 -1
  65. package/web-app/web-dist/assets/{mdx-DrXGQbNB.js → mdx-DffTEkNE.js} +1 -1
  66. package/web-app/web-dist/assets/{monaco-setup-CsR6EfHe.js → monaco-setup-DM3A5_VI.js} +3 -3
  67. package/web-app/web-dist/assets/{nginx-DoUz032F.js → nginx-Bhc82uuv.js} +1 -1
  68. package/web-app/web-dist/assets/{nim-B0Pl8B4R.js → nim-DXTVBFnF.js} +1 -1
  69. package/web-app/web-dist/assets/{perl-D2tfAALb.js → perl-C7veXV9z.js} +1 -1
  70. package/web-app/web-dist/assets/{php-BImCcX5X.js → php-BRiuMnnr.js} +1 -1
  71. package/web-app/web-dist/assets/{pug-BcnpC8P_.js → pug-C5hz5LQ7.js} +1 -1
  72. package/web-app/web-dist/assets/{python-ypRCBnvu.js → python-DyLAD3Wt.js} +1 -1
  73. package/web-app/web-dist/assets/{qml-DFDAunHY.js → qml-BdUV3aTS.js} +1 -1
  74. package/web-app/web-dist/assets/r-8R7vtdQc.js +1 -0
  75. package/web-app/web-dist/assets/{razor-aqrhpwqZ.js → razor-C49xQTPQ.js} +1 -1
  76. package/web-app/web-dist/assets/{razor-1_376SZM.js → razor-DRL52XO2.js} +1 -1
  77. package/web-app/web-dist/assets/react-vendor-CCIEwYL0.js +9 -0
  78. package/web-app/web-dist/assets/regexp-Omp9DhTb.js +1 -0
  79. package/web-app/web-dist/assets/{rst-2vG6f11Y.js → rst-BHX71KW9.js} +1 -1
  80. package/web-app/web-dist/assets/{ruby-Dj6bCFXR.js → ruby-B--HzjGU.js} +1 -1
  81. package/web-app/web-dist/assets/{sas-BhVZ4qL2.js → sas-DrLaYOK_.js} +1 -1
  82. package/web-app/web-dist/assets/scss-DdSxiZKl.js +1 -0
  83. package/web-app/web-dist/assets/shellscript-DwcUjJBL.js +1 -0
  84. package/web-app/web-dist/assets/{shellsession-CyO2fnhB.js → shellsession-CPZkydE6.js} +1 -1
  85. package/web-app/web-dist/assets/{soy-DIkw6E88.js → soy-Br5FhD7c.js} +1 -1
  86. package/web-app/web-dist/assets/sql-DNssxck8.js +1 -0
  87. package/web-app/web-dist/assets/{stata-DvkM932O.js → stata-DXn1tqOr.js} +1 -1
  88. package/web-app/web-dist/assets/{surrealql-B4-Q8tqV.js → surrealql-IeLNQw0f.js} +1 -1
  89. package/web-app/web-dist/assets/{svelte-p6yBy-Ki.js → svelte-DOdLCIlh.js} +1 -1
  90. package/web-app/web-dist/assets/{templ-C7EkuiZr.js → templ-CIwIngms.js} +1 -1
  91. package/web-app/web-dist/assets/{tex-DkmD8uFC.js → tex-D8QMumu5.js} +1 -1
  92. package/web-app/web-dist/assets/{ts-tags-U-hncHg4.js → ts-tags-BMVY4q-l.js} +1 -1
  93. package/web-app/web-dist/assets/{tsMode-a8OvovQd.js → tsMode-BndVBac5.js} +1 -1
  94. package/web-app/web-dist/assets/tsx-5Eka4NBX.js +1 -0
  95. package/web-app/web-dist/assets/{twig-CU0OP-IA.js → twig-C8o_5mgw.js} +1 -1
  96. package/web-app/web-dist/assets/{typescript-DnLjiKtn.js → typescript-B1w9vqKF.js} +1 -1
  97. package/web-app/web-dist/assets/typescript-DOu2WMV5.js +1 -0
  98. package/web-app/web-dist/assets/{vue-Db7nY3ba.js → vue-BU18DNDL.js} +1 -1
  99. package/web-app/web-dist/assets/{vue-html-BvAbiAw1.js → vue-html-BeluIYX0.js} +1 -1
  100. package/web-app/web-dist/assets/{vue-vine-BEaIQIlA.js → vue-vine-DGUAbOCX.js} +1 -1
  101. package/web-app/web-dist/assets/{xml-an4Nuuqq.js → xml-D8uAlVv5.js} +1 -1
  102. package/web-app/web-dist/assets/xml-DIqSwXR3.js +1 -0
  103. package/web-app/web-dist/assets/{xsl-D3NQgH22.js → xsl-Ct_-YIAy.js} +1 -1
  104. package/web-app/web-dist/assets/xterm-B1ffpRuj.js +36 -0
  105. package/web-app/web-dist/assets/xterm-addons-psDEiUMC.js +136 -0
  106. package/web-app/web-dist/assets/{yaml-Diiu6O9P.js → yaml-Bb7jXyQv.js} +1 -1
  107. package/web-app/web-dist/assets/yaml-DTtCYNlS.js +1 -0
  108. package/web-app/web-dist/index.html +6 -3
  109. package/zuzuu/actions/convert.mjs +10 -9
  110. package/zuzuu/actions/dispatch.mjs +12 -7
  111. package/zuzuu/actions/inbox.mjs +5 -5
  112. package/zuzuu/actions/manifest.mjs +48 -30
  113. package/zuzuu/actions/schema.mjs +9 -3
  114. package/zuzuu/actions/trail.mjs +1 -1
  115. package/zuzuu/commands/act-author.mjs +23 -13
  116. package/zuzuu/commands/act.mjs +4 -6
  117. package/zuzuu/commands/capture.mjs +2 -2
  118. package/zuzuu/commands/code.mjs +2 -2
  119. package/zuzuu/commands/digest.mjs +2 -2
  120. package/zuzuu/commands/distill.mjs +15 -16
  121. package/zuzuu/commands/doctor.mjs +41 -19
  122. package/zuzuu/commands/enable.mjs +1 -1
  123. package/zuzuu/commands/eval.mjs +3 -36
  124. package/zuzuu/commands/explain.mjs +4 -4
  125. package/zuzuu/commands/faculty.mjs +158 -0
  126. package/zuzuu/commands/generation.mjs +5 -8
  127. package/zuzuu/commands/hook.mjs +14 -12
  128. package/zuzuu/commands/inbox.mjs +1 -6
  129. package/zuzuu/commands/init.mjs +18 -4
  130. package/zuzuu/commands/knowledge.mjs +1 -1
  131. package/zuzuu/commands/migrations/home.mjs +96 -0
  132. package/zuzuu/commands/migrations/index.mjs +48 -0
  133. package/zuzuu/commands/migrations/items.mjs +360 -0
  134. package/zuzuu/commands/migrations/proposals.mjs +100 -0
  135. package/zuzuu/commands/proposals.mjs +131 -0
  136. package/zuzuu/commands/review.mjs +13 -227
  137. package/zuzuu/commands/session.mjs +8 -2
  138. package/zuzuu/commands/sessions.mjs +159 -0
  139. package/zuzuu/commands/status.mjs +3 -3
  140. package/zuzuu/commands/trace.mjs +1 -1
  141. package/zuzuu/{capture-core.mjs → core/capture-core.mjs} +3 -3
  142. package/zuzuu/{store.mjs → core/store.mjs} +1 -1
  143. package/zuzuu/digest/compose.mjs +96 -0
  144. package/zuzuu/eval/score.mjs +14 -1
  145. package/zuzuu/faculties/actions/index.mjs +283 -0
  146. package/zuzuu/faculties/guardrails/index.mjs +320 -0
  147. package/zuzuu/faculties/instructions/index.mjs +288 -0
  148. package/zuzuu/faculties/knowledge/index.mjs +185 -0
  149. package/zuzuu/faculties/memory/index.mjs +124 -0
  150. package/zuzuu/faculty/envelope.mjs +290 -0
  151. package/zuzuu/faculty/generation/read.mjs +206 -0
  152. package/zuzuu/faculty/generation/write.mjs +207 -0
  153. package/zuzuu/faculty/items.mjs +81 -0
  154. package/zuzuu/faculty/module.mjs +74 -0
  155. package/zuzuu/faculty/pending.mjs +63 -0
  156. package/zuzuu/faculty/registry.mjs +204 -18
  157. package/zuzuu/faculty/render.mjs +59 -0
  158. package/zuzuu/faculty/trail.mjs +1 -1
  159. package/zuzuu/guardrails/engine.mjs +137 -0
  160. package/zuzuu/{scaffold.mjs → home/scaffold.mjs} +110 -39
  161. package/zuzuu/knowledge/items.mjs +56 -91
  162. package/zuzuu/live/install.mjs +1 -1
  163. package/zuzuu/live/live-store.mjs +2 -2
  164. package/zuzuu/live/reconcile.mjs +2 -2
  165. package/zuzuu/sessions/git.mjs +47 -0
  166. package/zuzuu/{session-git.mjs → sessions/session-git.mjs} +5 -43
  167. package/web-app/web-dist/assets/DiffTab-BuWonUNJ.js +0 -1
  168. package/web-app/web-dist/assets/MonacoFile-CL3DhFKG.js +0 -1
  169. package/web-app/web-dist/assets/angular-html-CmT26mqM.js +0 -1
  170. package/web-app/web-dist/assets/c-BvoqrSVH.js +0 -1
  171. package/web-app/web-dist/assets/cpp-BXsk94m0.js +0 -1
  172. package/web-app/web-dist/assets/css-Z8oOGxII.js +0 -1
  173. package/web-app/web-dist/assets/dist-ChcDQ_7s.js +0 -153
  174. package/web-app/web-dist/assets/glsl-KwyfU2aa.js +0 -1
  175. package/web-app/web-dist/assets/graphql-DSeOUAa2.js +0 -1
  176. package/web-app/web-dist/assets/haml-azVoxQRV.js +0 -1
  177. package/web-app/web-dist/assets/html-D_7P5S4m.js +0 -1
  178. package/web-app/web-dist/assets/index--5yy8RbA.js +0 -267
  179. package/web-app/web-dist/assets/index-BVG4hgk7.css +0 -2
  180. package/web-app/web-dist/assets/java-D4RbCvBe.js +0 -1
  181. package/web-app/web-dist/assets/javascript-Cb010CKM.js +0 -1
  182. package/web-app/web-dist/assets/json-DWgqV4D1.js +0 -1
  183. package/web-app/web-dist/assets/jsx-CZjSJa1f.js +0 -1
  184. package/web-app/web-dist/assets/lua-TGj_6NzO.js +0 -1
  185. package/web-app/web-dist/assets/r-fCpuAR7u.js +0 -1
  186. package/web-app/web-dist/assets/regexp-B4yxx-Ty.js +0 -1
  187. package/web-app/web-dist/assets/scss-QdjMO_xV.js +0 -1
  188. package/web-app/web-dist/assets/shellscript-BnlgeVVx.js +0 -1
  189. package/web-app/web-dist/assets/sql-DGnQv6iD.js +0 -1
  190. package/web-app/web-dist/assets/tsx-MJ0-9sYG.js +0 -1
  191. package/web-app/web-dist/assets/typescript-C17ZkDe8.js +0 -1
  192. package/web-app/web-dist/assets/xml-CA9lHFQV.js +0 -1
  193. package/web-app/web-dist/assets/yaml-CwRYMJka.js +0 -1
  194. package/zuzuu/actions/adapter.mjs +0 -130
  195. package/zuzuu/commands/migrate.mjs +0 -225
  196. package/zuzuu/digest.mjs +0 -149
  197. package/zuzuu/faculty/generation.mjs +0 -392
  198. package/zuzuu/guardrails/adapter.mjs +0 -134
  199. package/zuzuu/guardrails.mjs +0 -89
  200. package/zuzuu/instructions/adapter.mjs +0 -93
  201. package/zuzuu/knowledge/adapter.mjs +0 -99
  202. package/zuzuu/memory/adapter.mjs +0 -121
  203. package/zuzuu/miners/actions.mjs +0 -118
  204. package/zuzuu/miners/guardrails.mjs +0 -179
  205. package/zuzuu/miners/instructions.mjs +0 -157
  206. package/zuzuu/miners/knowledge.mjs +0 -25
  207. package/zuzuu/miners/memory.mjs +0 -27
  208. package/zuzuu/miners/registry.mjs +0 -31
  209. /package/web-app/web-dist/assets/{chunk-QTnfLwEv.js → rolldown-runtime-QTnfLwEv.js} +0 -0
  210. /package/zuzuu/{session.mjs → core/session.mjs} +0 -0
  211. /package/zuzuu/{inject.mjs → home/inject.mjs} +0 -0
@@ -1 +1 @@
1
- import{h as e}from"./editor.api2-BmGoRSl4.js";var t={comments:{lineComment:`#`},brackets:[[`{`,`}`],[`[`,`]`],[`(`,`)`]],autoClosingPairs:[{open:`{`,close:`}`},{open:`[`,close:`]`},{open:`(`,close:`)`},{open:`"`,close:`"`},{open:`'`,close:`'`}],surroundingPairs:[{open:`{`,close:`}`},{open:`[`,close:`]`},{open:`(`,close:`)`},{open:`"`,close:`"`},{open:`'`,close:`'`}],folding:{offSide:!0},onEnterRules:[{beforeText:/:\s*$/,action:{indentAction:e.IndentAction.Indent}}]},n={tokenPostfix:`.yaml`,brackets:[{token:`delimiter.bracket`,open:`{`,close:`}`},{token:`delimiter.square`,open:`[`,close:`]`}],keywords:[`true`,`True`,`TRUE`,`false`,`False`,`FALSE`,`null`,`Null`,`Null`,`~`],numberInteger:/(?:0|[+-]?[0-9]+)/,numberFloat:/(?:0|[+-]?[0-9]+)(?:\.[0-9]+)?(?:e[-+][1-9][0-9]*)?/,numberOctal:/0o[0-7]+/,numberHex:/0x[0-9a-fA-F]+/,numberInfinity:/[+-]?\.(?:inf|Inf|INF)/,numberNaN:/\.(?:nan|Nan|NAN)/,numberDate:/\d{4}-\d\d-\d\d([Tt ]\d\d:\d\d:\d\d(\.\d+)?(( ?[+-]\d\d?(:\d\d)?)|Z)?)?/,escapes:/\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/,tokenizer:{root:[{include:`@whitespace`},{include:`@comment`},[/%[^ ]+.*$/,`meta.directive`],[/---/,`operators.directivesEnd`],[/\.{3}/,`operators.documentEnd`],[/[-?:](?= )/,`operators`],{include:`@anchor`},{include:`@tagHandle`},{include:`@flowCollections`},{include:`@blockStyle`},[/@numberInteger(?![ \t]*\S+)/,`number`],[/@numberFloat(?![ \t]*\S+)/,`number.float`],[/@numberOctal(?![ \t]*\S+)/,`number.octal`],[/@numberHex(?![ \t]*\S+)/,`number.hex`],[/@numberInfinity(?![ \t]*\S+)/,`number.infinity`],[/@numberNaN(?![ \t]*\S+)/,`number.nan`],[/@numberDate(?![ \t]*\S+)/,`number.date`],[/(".*?"|'.*?'|[^#'"]*?)([ \t]*)(:)( |$)/,[`type`,`white`,`operators`,`white`]],{include:`@flowScalars`},[/.+?(?=(\s+#|$))/,{cases:{"@keywords":`keyword`,"@default":`string`}}]],object:[{include:`@whitespace`},{include:`@comment`},[/\}/,`@brackets`,`@pop`],[/,/,`delimiter.comma`],[/:(?= )/,`operators`],[/(?:".*?"|'.*?'|[^,\{\[]+?)(?=: )/,`type`],{include:`@flowCollections`},{include:`@flowScalars`},{include:`@tagHandle`},{include:`@anchor`},{include:`@flowNumber`},[/[^\},]+/,{cases:{"@keywords":`keyword`,"@default":`string`}}]],array:[{include:`@whitespace`},{include:`@comment`},[/\]/,`@brackets`,`@pop`],[/,/,`delimiter.comma`],{include:`@flowCollections`},{include:`@flowScalars`},{include:`@tagHandle`},{include:`@anchor`},{include:`@flowNumber`},[/[^\],]+/,{cases:{"@keywords":`keyword`,"@default":`string`}}]],multiString:[[/^( +).+$/,`string`,`@multiStringContinued.$1`]],multiStringContinued:[[/^( *).+$/,{cases:{"$1==$S2":`string`,"@default":{token:`@rematch`,next:`@popall`}}}]],whitespace:[[/[ \t\r\n]+/,`white`]],comment:[[/#.*$/,`comment`]],flowCollections:[[/\[/,`@brackets`,`@array`],[/\{/,`@brackets`,`@object`]],flowScalars:[[/"([^"\\]|\\.)*$/,`string.invalid`],[/'([^'\\]|\\.)*$/,`string.invalid`],[/'[^']*'/,`string`],[/"/,`string`,`@doubleQuotedString`]],doubleQuotedString:[[/[^\\"]+/,`string`],[/@escapes/,`string.escape`],[/\\./,`string.escape.invalid`],[/"/,`string`,`@pop`]],blockStyle:[[/[>|][0-9]*[+-]?$/,`operators`,`@multiString`]],flowNumber:[[/@numberInteger(?=[ \t]*[,\]\}])/,`number`],[/@numberFloat(?=[ \t]*[,\]\}])/,`number.float`],[/@numberOctal(?=[ \t]*[,\]\}])/,`number.octal`],[/@numberHex(?=[ \t]*[,\]\}])/,`number.hex`],[/@numberInfinity(?=[ \t]*[,\]\}])/,`number.infinity`],[/@numberNaN(?=[ \t]*[,\]\}])/,`number.nan`],[/@numberDate(?=[ \t]*[,\]\}])/,`number.date`]],tagHandle:[[/\![^ ]*/,`tag`]],anchor:[[/[&*][^ ]+/,`namespace`]]}};export{t as conf,n as language};
1
+ import{h as e}from"./editor.api2-C7skgoRB.js";var t={comments:{lineComment:`#`},brackets:[[`{`,`}`],[`[`,`]`],[`(`,`)`]],autoClosingPairs:[{open:`{`,close:`}`},{open:`[`,close:`]`},{open:`(`,close:`)`},{open:`"`,close:`"`},{open:`'`,close:`'`}],surroundingPairs:[{open:`{`,close:`}`},{open:`[`,close:`]`},{open:`(`,close:`)`},{open:`"`,close:`"`},{open:`'`,close:`'`}],folding:{offSide:!0},onEnterRules:[{beforeText:/:\s*$/,action:{indentAction:e.IndentAction.Indent}}]},n={tokenPostfix:`.yaml`,brackets:[{token:`delimiter.bracket`,open:`{`,close:`}`},{token:`delimiter.square`,open:`[`,close:`]`}],keywords:[`true`,`True`,`TRUE`,`false`,`False`,`FALSE`,`null`,`Null`,`Null`,`~`],numberInteger:/(?:0|[+-]?[0-9]+)/,numberFloat:/(?:0|[+-]?[0-9]+)(?:\.[0-9]+)?(?:e[-+][1-9][0-9]*)?/,numberOctal:/0o[0-7]+/,numberHex:/0x[0-9a-fA-F]+/,numberInfinity:/[+-]?\.(?:inf|Inf|INF)/,numberNaN:/\.(?:nan|Nan|NAN)/,numberDate:/\d{4}-\d\d-\d\d([Tt ]\d\d:\d\d:\d\d(\.\d+)?(( ?[+-]\d\d?(:\d\d)?)|Z)?)?/,escapes:/\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/,tokenizer:{root:[{include:`@whitespace`},{include:`@comment`},[/%[^ ]+.*$/,`meta.directive`],[/---/,`operators.directivesEnd`],[/\.{3}/,`operators.documentEnd`],[/[-?:](?= )/,`operators`],{include:`@anchor`},{include:`@tagHandle`},{include:`@flowCollections`},{include:`@blockStyle`},[/@numberInteger(?![ \t]*\S+)/,`number`],[/@numberFloat(?![ \t]*\S+)/,`number.float`],[/@numberOctal(?![ \t]*\S+)/,`number.octal`],[/@numberHex(?![ \t]*\S+)/,`number.hex`],[/@numberInfinity(?![ \t]*\S+)/,`number.infinity`],[/@numberNaN(?![ \t]*\S+)/,`number.nan`],[/@numberDate(?![ \t]*\S+)/,`number.date`],[/(".*?"|'.*?'|[^#'"]*?)([ \t]*)(:)( |$)/,[`type`,`white`,`operators`,`white`]],{include:`@flowScalars`},[/.+?(?=(\s+#|$))/,{cases:{"@keywords":`keyword`,"@default":`string`}}]],object:[{include:`@whitespace`},{include:`@comment`},[/\}/,`@brackets`,`@pop`],[/,/,`delimiter.comma`],[/:(?= )/,`operators`],[/(?:".*?"|'.*?'|[^,\{\[]+?)(?=: )/,`type`],{include:`@flowCollections`},{include:`@flowScalars`},{include:`@tagHandle`},{include:`@anchor`},{include:`@flowNumber`},[/[^\},]+/,{cases:{"@keywords":`keyword`,"@default":`string`}}]],array:[{include:`@whitespace`},{include:`@comment`},[/\]/,`@brackets`,`@pop`],[/,/,`delimiter.comma`],{include:`@flowCollections`},{include:`@flowScalars`},{include:`@tagHandle`},{include:`@anchor`},{include:`@flowNumber`},[/[^\],]+/,{cases:{"@keywords":`keyword`,"@default":`string`}}]],multiString:[[/^( +).+$/,`string`,`@multiStringContinued.$1`]],multiStringContinued:[[/^( *).+$/,{cases:{"$1==$S2":`string`,"@default":{token:`@rematch`,next:`@popall`}}}]],whitespace:[[/[ \t\r\n]+/,`white`]],comment:[[/#.*$/,`comment`]],flowCollections:[[/\[/,`@brackets`,`@array`],[/\{/,`@brackets`,`@object`]],flowScalars:[[/"([^"\\]|\\.)*$/,`string.invalid`],[/'([^'\\]|\\.)*$/,`string.invalid`],[/'[^']*'/,`string`],[/"/,`string`,`@doubleQuotedString`]],doubleQuotedString:[[/[^\\"]+/,`string`],[/@escapes/,`string.escape`],[/\\./,`string.escape.invalid`],[/"/,`string`,`@pop`]],blockStyle:[[/[>|][0-9]*[+-]?$/,`operators`,`@multiString`]],flowNumber:[[/@numberInteger(?=[ \t]*[,\]\}])/,`number`],[/@numberFloat(?=[ \t]*[,\]\}])/,`number.float`],[/@numberOctal(?=[ \t]*[,\]\}])/,`number.octal`],[/@numberHex(?=[ \t]*[,\]\}])/,`number.hex`],[/@numberInfinity(?=[ \t]*[,\]\}])/,`number.infinity`],[/@numberNaN(?=[ \t]*[,\]\}])/,`number.nan`],[/@numberDate(?=[ \t]*[,\]\}])/,`number.date`]],tagHandle:[[/\![^ ]*/,`tag`]],anchor:[[/[&*][^ ]+/,`namespace`]]}};export{t as conf,n as language};
@@ -0,0 +1 @@
1
+ import{n as e}from"./rolldown-runtime-QTnfLwEv.js";var t=e({default:()=>n}),n=[Object.freeze(JSON.parse(`{"displayName":"YAML","fileTypes":["yaml","yml","rviz","reek","clang-format","yaml-tmlanguage","syntax","sublime-syntax"],"firstLineMatch":"^%YAML( ?1.\\\\d+)?","name":"yaml","patterns":[{"include":"#comment"},{"include":"#property"},{"include":"#directive"},{"match":"^---","name":"entity.other.document.begin.yaml"},{"match":"^\\\\.{3}","name":"entity.other.document.end.yaml"},{"include":"#node"}],"repository":{"block-collection":{"patterns":[{"include":"#block-sequence"},{"include":"#block-mapping"}]},"block-mapping":{"patterns":[{"include":"#block-pair"}]},"block-node":{"patterns":[{"include":"#prototype"},{"include":"#block-scalar"},{"include":"#block-collection"},{"include":"#flow-scalar-plain-out"},{"include":"#flow-node"}]},"block-pair":{"patterns":[{"begin":"\\\\?","beginCaptures":{"1":{"name":"punctuation.definition.key-value.begin.yaml"}},"end":"(?=\\\\?)|^ *(:)|(:)","endCaptures":{"1":{"name":"punctuation.separator.key-value.mapping.yaml"},"2":{"name":"invalid.illegal.expected-newline.yaml"}},"name":"meta.block-mapping.yaml","patterns":[{"include":"#block-node"}]},{"begin":"(?=(?:[^-\\\\]!\\"#%\\\\&'*,:>?@\\\\[\`{|}\\\\s]|[-:?]\\\\S)([^:\\\\s]|:\\\\S|\\\\s+(?![#\\\\s]))*\\\\s*:(\\\\s|$))","end":"(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$))","patterns":[{"include":"#flow-scalar-plain-out-implicit-type"},{"begin":"[^-\\\\]!\\"#%\\\\&'*,:>?@\\\\[\`{|}\\\\s]|[-:?]\\\\S","beginCaptures":{"0":{"name":"entity.name.tag.yaml"}},"contentName":"entity.name.tag.yaml","end":"(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$))","name":"string.unquoted.plain.out.yaml"}]},{"match":":(?=\\\\s|$)","name":"punctuation.separator.key-value.mapping.yaml"}]},"block-scalar":{"begin":"(?:(\\\\|)|(>))([1-9])?([-+])?(.*\\\\n?)","beginCaptures":{"1":{"name":"keyword.control.flow.block-scalar.literal.yaml"},"2":{"name":"keyword.control.flow.block-scalar.folded.yaml"},"3":{"name":"constant.numeric.indentation-indicator.yaml"},"4":{"name":"storage.modifier.chomping-indicator.yaml"},"5":{"patterns":[{"include":"#comment"},{"match":".+","name":"invalid.illegal.expected-comment-or-newline.yaml"}]}},"end":"^(?=\\\\S)|(?!\\\\G)","patterns":[{"begin":"^( +)(?! )","end":"^(?!\\\\1|\\\\s*$)","name":"string.unquoted.block.yaml"}]},"block-sequence":{"match":"(-)(?!\\\\S)","name":"punctuation.definition.block.sequence.item.yaml"},"comment":{"begin":"(?:^([\\\\t ]*)|[\\\\t ]+)(?=#\\\\p{print}*$)","beginCaptures":{"1":{"name":"punctuation.whitespace.comment.leading.yaml"}},"end":"(?!\\\\G)","patterns":[{"begin":"#","beginCaptures":{"0":{"name":"punctuation.definition.comment.yaml"}},"end":"\\\\n","name":"comment.line.number-sign.yaml"}]},"directive":{"begin":"^%","beginCaptures":{"0":{"name":"punctuation.definition.directive.begin.yaml"}},"end":"(?=$|[\\\\t ]+($|#))","name":"meta.directive.yaml","patterns":[{"captures":{"1":{"name":"keyword.other.directive.yaml.yaml"},"2":{"name":"constant.numeric.yaml-version.yaml"}},"match":"\\\\G(YAML)[\\\\t ]+(\\\\d+\\\\.\\\\d+)"},{"captures":{"1":{"name":"keyword.other.directive.tag.yaml"},"2":{"name":"storage.type.tag-handle.yaml"},"3":{"name":"support.type.tag-prefix.yaml"}},"match":"\\\\G(TAG)(?:[\\\\t ]+(!(?:[-0-9A-Za-z]*!)?)(?:[\\\\t ]+(!(?:%\\\\h{2}|[]!#$\\\\&-;=?-\\\\[_a-z~])*|(?![]!,\\\\[{}])(?:%\\\\h{2}|[]!#$\\\\&-;=?-\\\\[_a-z~])+))?)?"},{"captures":{"1":{"name":"support.other.directive.reserved.yaml"},"2":{"name":"string.unquoted.directive-name.yaml"},"3":{"name":"string.unquoted.directive-parameter.yaml"}},"match":"\\\\G(\\\\w+)(?:[\\\\t ]+(\\\\w+)(?:[\\\\t ]+(\\\\w+))?)?"},{"match":"\\\\S+","name":"invalid.illegal.unrecognized.yaml"}]},"flow-alias":{"captures":{"1":{"name":"keyword.control.flow.alias.yaml"},"2":{"name":"punctuation.definition.alias.yaml"},"3":{"name":"variable.other.alias.yaml"},"4":{"name":"invalid.illegal.character.anchor.yaml"}},"match":"((\\\\*))([^],/\\\\[{}\\\\s]+)([^],}\\\\s]\\\\S*)?"},"flow-collection":{"patterns":[{"include":"#flow-sequence"},{"include":"#flow-mapping"}]},"flow-mapping":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.mapping.begin.yaml"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.mapping.end.yaml"}},"name":"meta.flow-mapping.yaml","patterns":[{"include":"#prototype"},{"match":",","name":"punctuation.separator.mapping.yaml"},{"include":"#flow-pair"}]},"flow-node":{"patterns":[{"include":"#prototype"},{"include":"#flow-alias"},{"include":"#flow-collection"},{"include":"#flow-scalar"}]},"flow-pair":{"patterns":[{"begin":"\\\\?","beginCaptures":{"0":{"name":"punctuation.definition.key-value.begin.yaml"}},"end":"(?=[],}])","name":"meta.flow-pair.explicit.yaml","patterns":[{"include":"#prototype"},{"include":"#flow-pair"},{"include":"#flow-node"},{"begin":":(?=\\\\s|$|[],\\\\[{}])","beginCaptures":{"0":{"name":"punctuation.separator.key-value.mapping.yaml"}},"end":"(?=[],}])","patterns":[{"include":"#flow-value"}]}]},{"begin":"(?=(?:[^-\\\\]!\\"#%\\\\&'*,:>?@\\\\[\`{|}\\\\s]|[-:?][^],\\\\[{}\\\\s])([^],:\\\\[{}\\\\s]|:[^],\\\\[{}\\\\s]|\\\\s+(?![#\\\\s]))*\\\\s*:(\\\\s|$))","end":"(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$)|\\\\s*:[],\\\\[{}]|\\\\s*[],\\\\[{}])","name":"meta.flow-pair.key.yaml","patterns":[{"include":"#flow-scalar-plain-in-implicit-type"},{"begin":"[^-\\\\]!\\"#%\\\\&'*,:>?@\\\\[\`{|}\\\\s]|[-:?][^],\\\\[{}\\\\s]","beginCaptures":{"0":{"name":"entity.name.tag.yaml"}},"contentName":"entity.name.tag.yaml","end":"(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$)|\\\\s*:[],\\\\[{}]|\\\\s*[],\\\\[{}])","name":"string.unquoted.plain.in.yaml"}]},{"include":"#flow-node"},{"begin":":(?=\\\\s|$|[],\\\\[{}])","captures":{"0":{"name":"punctuation.separator.key-value.mapping.yaml"}},"end":"(?=[],}])","name":"meta.flow-pair.yaml","patterns":[{"include":"#flow-value"}]}]},"flow-scalar":{"patterns":[{"include":"#flow-scalar-double-quoted"},{"include":"#flow-scalar-single-quoted"},{"include":"#flow-scalar-plain-in"}]},"flow-scalar-double-quoted":{"begin":"\\"","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.yaml"}},"end":"\\"","endCaptures":{"0":{"name":"punctuation.definition.string.end.yaml"}},"name":"string.quoted.double.yaml","patterns":[{"match":"\\\\\\\\([ \\"/0LN\\\\\\\\_abefnprtv]|x\\\\d\\\\d|u\\\\d{4}|U\\\\d{8})","name":"constant.character.escape.yaml"},{"match":"\\\\\\\\\\\\n","name":"constant.character.escape.double-quoted.newline.yaml"}]},"flow-scalar-plain-in":{"patterns":[{"include":"#flow-scalar-plain-in-implicit-type"},{"begin":"[^-\\\\]!\\"#%\\\\&'*,:>?@\\\\[\`{|}\\\\s]|[-:?][^],\\\\[{}\\\\s]","end":"(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$)|\\\\s*:[],\\\\[{}]|\\\\s*[],\\\\[{}])","name":"string.unquoted.plain.in.yaml"}]},"flow-scalar-plain-in-implicit-type":{"patterns":[{"captures":{"1":{"name":"constant.language.null.yaml"},"2":{"name":"constant.language.boolean.yaml"},"3":{"name":"constant.numeric.integer.yaml"},"4":{"name":"constant.numeric.float.yaml"},"5":{"name":"constant.other.timestamp.yaml"},"6":{"name":"constant.language.value.yaml"},"7":{"name":"constant.language.merge.yaml"}},"match":"(?:(null|Null|NULL|~)|([Yy]|yes|Yes|YES|[Nn]|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)|([-+]?0b[01_]+|[-+]?0[0-7_]+|[-+]?(?:0|[1-9][0-9_]*)|[-+]?0x[_\\\\h]+|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)|([-+]?(?:[0-9][0-9_]*)?\\\\.[.0-9]*(?:[Ee][-+][0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\\\.[0-9_]*|[-+]?\\\\.(?:inf|Inf|INF)|\\\\.(?:nan|NaN|NAN))|(\\\\d{4}-\\\\d{2}-\\\\d{2}|\\\\d{4}-\\\\d{1,2}-\\\\d{1,2}(?:[Tt]|[\\\\t ]+)\\\\d{1,2}:\\\\d{2}:\\\\d{2}(?:\\\\.\\\\d*)?(?:[\\\\t ]*Z|[-+]\\\\d{1,2}(?::\\\\d{1,2})?)?)|(=)|(<<))(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$)|\\\\s*:[],\\\\[{}]|\\\\s*[],\\\\[{}])"}]},"flow-scalar-plain-out":{"patterns":[{"include":"#flow-scalar-plain-out-implicit-type"},{"begin":"[^-\\\\]!\\"#%\\\\&'*,:>?@\\\\[\`{|}\\\\s]|[-:?]\\\\S","end":"(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$))","name":"string.unquoted.plain.out.yaml"}]},"flow-scalar-plain-out-implicit-type":{"patterns":[{"captures":{"1":{"name":"constant.language.null.yaml"},"2":{"name":"constant.language.boolean.yaml"},"3":{"name":"constant.numeric.integer.yaml"},"4":{"name":"constant.numeric.float.yaml"},"5":{"name":"constant.other.timestamp.yaml"},"6":{"name":"constant.language.value.yaml"},"7":{"name":"constant.language.merge.yaml"}},"match":"(?:(null|Null|NULL|~)|([Yy]|yes|Yes|YES|[Nn]|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)|([-+]?0b[01_]+|[-+]?0[0-7_]+|[-+]?(?:0|[1-9][0-9_]*)|[-+]?0x[_\\\\h]+|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)|([-+]?(?:[0-9][0-9_]*)?\\\\.[.0-9]*(?:[Ee][-+][0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\\\.[0-9_]*|[-+]?\\\\.(?:inf|Inf|INF)|\\\\.(?:nan|NaN|NAN))|(\\\\d{4}-\\\\d{2}-\\\\d{2}|\\\\d{4}-\\\\d{1,2}-\\\\d{1,2}(?:[Tt]|[\\\\t ]+)\\\\d{1,2}:\\\\d{2}:\\\\d{2}(?:\\\\.\\\\d*)?(?:[\\\\t ]*Z|[-+]\\\\d{1,2}(?::\\\\d{1,2})?)?)|(=)|(<<))(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$))"}]},"flow-scalar-single-quoted":{"begin":"'","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.yaml"}},"end":"'(?!')","endCaptures":{"0":{"name":"punctuation.definition.string.end.yaml"}},"name":"string.quoted.single.yaml","patterns":[{"match":"''","name":"constant.character.escape.single-quoted.yaml"}]},"flow-sequence":{"begin":"\\\\[","beginCaptures":{"0":{"name":"punctuation.definition.sequence.begin.yaml"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.sequence.end.yaml"}},"name":"meta.flow-sequence.yaml","patterns":[{"include":"#prototype"},{"match":",","name":"punctuation.separator.sequence.yaml"},{"include":"#flow-pair"},{"include":"#flow-node"}]},"flow-value":{"patterns":[{"begin":"\\\\G(?![],}])","end":"(?=[],}])","name":"meta.flow-pair.value.yaml","patterns":[{"include":"#flow-node"}]}]},"node":{"patterns":[{"include":"#block-node"}]},"property":{"begin":"(?=[!\\\\&])","end":"(?!\\\\G)","name":"meta.property.yaml","patterns":[{"captures":{"1":{"name":"keyword.control.property.anchor.yaml"},"2":{"name":"punctuation.definition.anchor.yaml"},"3":{"name":"entity.name.type.anchor.yaml"},"4":{"name":"invalid.illegal.character.anchor.yaml"}},"match":"\\\\G((&))([^],/\\\\[{}\\\\s]+)(\\\\S+)?"},{"match":"\\\\G!(?:<(?:%\\\\h{2}|[]!#$\\\\&-;=?-\\\\[_a-z~])+>|(?:[-0-9A-Za-z]*!)?(?:%\\\\h{2}|[#$\\\\&-+\\\\--;=?-Z_a-z~])+|)(?=[\\\\t ]|$)","name":"storage.type.tag-handle.yaml"},{"match":"\\\\S+","name":"invalid.illegal.tag-handle.yaml"}]},"prototype":{"patterns":[{"include":"#comment"},{"include":"#property"}]}},"scopeName":"source.yaml","aliases":["yml"]}`))];export{t as n,n as t};
@@ -6,9 +6,12 @@
6
6
  <meta name="color-scheme" content="dark" />
7
7
  <title>zuzuu-web</title>
8
8
  <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='7' fill='%230d1117'/%3E%3Ctext x='6' y='22' font-family='monospace' font-size='16' fill='%2358e6c0'%3E%E2%9D%AF_%3C/text%3E%3C/svg%3E" />
9
- <script type="module" crossorigin src="/assets/index--5yy8RbA.js"></script>
10
- <link rel="modulepreload" crossorigin href="/assets/chunk-QTnfLwEv.js">
11
- <link rel="stylesheet" crossorigin href="/assets/index-BVG4hgk7.css">
9
+ <script type="module" crossorigin src="/assets/index-De6DWTZM.js"></script>
10
+ <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-QTnfLwEv.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/xterm-addons-psDEiUMC.js">
12
+ <link rel="modulepreload" crossorigin href="/assets/xterm-B1ffpRuj.js">
13
+ <link rel="modulepreload" crossorigin href="/assets/react-vendor-CCIEwYL0.js">
14
+ <link rel="stylesheet" crossorigin href="/assets/index-B27_WOhS.css">
12
15
  </head>
13
16
  <body>
14
17
  <div id="root"></div>
@@ -1,7 +1,9 @@
1
1
  // zuzuu/actions/convert.mjs
2
2
  // Pure manifest → tool-definition converters (the _labs tool-definition pattern).
3
- // The manifest's `inputs` JSON Schema is already the right shape for each format,
4
- // so conversion is a thin re-wrap.
3
+ // Manifests are ACTION.md envelopes (W24): name = the envelope id, description =
4
+ // the prompt snippet (body first line) or title. The envelope carries no
5
+ // inputs/outputs JSON-schemas (clean break) — tool definitions expose the
6
+ // permissive object schema; the runner validates the same way.
5
7
  //
6
8
  // STATUS (2026-06-11): used today only by `zuzuu act schema <slug> [--mcp|--openai|
7
9
  // --anthropic]` for inspection. There is NO runtime MCP/native-tool *serving* yet —
@@ -9,19 +11,18 @@
9
11
  // agent in the digest. Live "Actions over MCP" serving is DEFERRED (DESIGN §6 /
10
12
  // Stage 2 / OpenCode bundle); these converters are the seam for it, not the thing.
11
13
 
12
- const desc = (m) => m.description ?? m.title ?? m.slug;
13
- const inputs = (m) => m.inputs ?? { type: 'object' };
14
+ const name = (m) => m.id ?? m.slug;
15
+ const desc = (m) => m.promptSnippet ?? m.title ?? name(m);
16
+ const inputs = () => ({ type: 'object' });
14
17
 
15
18
  export function toMcpTool(m) {
16
- const t = { name: m.slug, description: desc(m), inputSchema: inputs(m) };
17
- if (m.outputs) t.outputSchema = m.outputs;
18
- return t;
19
+ return { name: name(m), description: desc(m), inputSchema: inputs() };
19
20
  }
20
21
 
21
22
  export function toOpenAITool(m) {
22
- return { type: 'function', function: { name: m.slug, description: desc(m), parameters: inputs(m) } };
23
+ return { type: 'function', function: { name: name(m), description: desc(m), parameters: inputs() } };
23
24
  }
24
25
 
25
26
  export function toAnthropicTool(m) {
26
- return { name: m.slug, description: desc(m), input_schema: inputs(m) };
27
+ return { name: name(m), description: desc(m), input_schema: inputs() };
27
28
  }
@@ -8,7 +8,7 @@ import { spawnSync } from 'node:child_process';
8
8
  import { fileURLToPath } from 'node:url';
9
9
  import { join, dirname } from 'node:path';
10
10
  import { existsSync } from 'node:fs';
11
- import { loadManifest, actionsDir } from './manifest.mjs';
11
+ import { loadManifest, execOf, actionsDir } from './manifest.mjs';
12
12
  import { MARKER } from './marker.mjs';
13
13
 
14
14
  const MAX_DEPTH = 8;
@@ -51,16 +51,21 @@ export function runAction(agentDir, slug, callerArgs = {}, { timeoutMs = ACTION_
51
51
  if (depth >= MAX_DEPTH) return { ok: false, error: 'depth_exceeded', detail: `depth ${depth} ≥ ${MAX_DEPTH}`, logs: '' };
52
52
 
53
53
  const manifest = loadManifest(agentDir, slug);
54
- if (!manifest) return { ok: false, error: 'not_found', detail: `no action '${slug}' (missing action.json)`, logs: '' };
54
+ if (!manifest) return { ok: false, error: 'not_found', detail: `no action '${slug}' (missing ACTION.md)`, logs: '' };
55
55
 
56
- const runPath = join(actionsDir(agentDir), slug, 'run.mjs');
57
- if (!existsSync(runPath)) return { ok: false, error: 'not_runnable', detail: `'${slug}' has no run.mjs`, logs: '' };
56
+ const exec = execOf(manifest);
57
+ if (!exec) return { ok: false, error: 'not_runnable', detail: `'${slug}' has an unsafe exec entry`, logs: '' };
58
+ const runPath = join(actionsDir(agentDir), slug, exec);
59
+ if (!existsSync(runPath)) return { ok: false, error: 'not_runnable', detail: `'${slug}' has no ${exec}`, logs: '' };
58
60
 
61
+ // The envelope carries no inputs/outputs JSON-schemas (clean break, W24) —
62
+ // the runner validates against the permissive object schema; payload.args
63
+ // are the default args (caller args win).
59
64
  const payload = JSON.stringify({
60
65
  runPath,
61
- inputs: manifest.inputs ?? { type: 'object' },
62
- outputs: manifest.outputs ?? { type: 'object' },
63
- default_args: manifest.default_args ?? {},
66
+ inputs: { type: 'object' },
67
+ outputs: { type: 'object' },
68
+ default_args: manifest.payload?.args ?? {},
64
69
  args: callerArgs ?? {},
65
70
  });
66
71
 
@@ -7,6 +7,7 @@
7
7
  import { join } from 'node:path';
8
8
  import { existsSync, readFileSync, renameSync, mkdirSync, rmSync } from 'node:fs';
9
9
  import { actionsDir, inboxDir, listActions, isSafeSlug } from './manifest.mjs';
10
+ import { parseEnvelope } from '../faculty/envelope.mjs';
10
11
 
11
12
  /** Archive dir for rejected action proposals: .zuzuu/actions/proposals/archive/. */
12
13
  const archiveBaseDir = (agentDir) => join(actionsDir(agentDir), 'proposals', 'archive');
@@ -26,12 +27,11 @@ export function activateAction(agentDir, slug) {
26
27
  const to = join(actionsDir(agentDir), slug);
27
28
  if (!existsSync(from)) return { ok: false, error: `no proposed action '${slug}'` };
28
29
  if (existsSync(to)) return { ok: false, error: `an active action '${slug}' already exists — reject or rename first` };
29
- const manPath = join(from, 'action.json');
30
+ const manPath = join(from, 'ACTION.md');
30
31
  if (existsSync(manPath)) {
31
- let man;
32
- try { man = JSON.parse(readFileSync(manPath, 'utf8')); }
33
- catch { return { ok: false, error: `manifest is not valid JSON` }; }
34
- if (man.slug && man.slug !== slug) return { ok: false, error: `manifest slug '${man.slug}' ≠ dir '${slug}'` };
32
+ const { ok, item } = parseEnvelope(readFileSync(manPath, 'utf8'));
33
+ if (!ok) return { ok: false, error: 'ACTION.md is not a valid envelope' };
34
+ if (item.id && item.id !== slug) return { ok: false, error: `ACTION.md id '${item.id}' dir '${slug}'` };
35
35
  }
36
36
  renameSync(from, to);
37
37
  return { ok: true };
@@ -1,10 +1,17 @@
1
1
  // zuzuu/actions/manifest.mjs
2
- // Reads the Actions faculty off disk: one action per dir under .zuzuu/actions/.
3
- // Two kinds `script` (has run.mjs + action.json) and `runbook` (SKILL.md prose).
2
+ // Reads the Actions faculty off disk: one action per dir under .zuzuu/actions/,
3
+ // described by an ACTION.md envelope (the Faculty Standard, W24 SKILL.md-shaped:
4
+ // frontmatter + instruction prose body; scripts stay siblings):
5
+ //
6
+ // actions/<slug>/ACTION.md kind: script|runbook; payload.exec (script entry,
7
+ // default run.mjs) + payload.args (default args)
8
+ // actions/<slug>/run.mjs the script (kind: script)
9
+ //
4
10
  // Pure-ish: filesystem reads only, no logging, no process control.
5
11
 
6
12
  import { join } from 'node:path';
7
13
  import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
14
+ import { parseEnvelope, deriveTitle } from '../faculty/envelope.mjs';
8
15
 
9
16
  // Action slugs: letters/digits start, then letters/digits/-/_. No dots or slashes
10
17
  // → cannot escape .zuzuu/actions/ via path traversal.
@@ -13,60 +20,71 @@ export function isSafeSlug(slug) {
13
20
  return typeof slug === 'string' && SAFE_SLUG.test(slug);
14
21
  }
15
22
 
23
+ // payload.exec must be a plain sibling filename — never a path.
24
+ const SAFE_EXEC = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
25
+
16
26
  export const actionsDir = (agentDir) => join(agentDir, 'actions');
17
27
  export const inboxDir = (agentDir) => join(actionsDir(agentDir), 'inbox');
18
28
  const actionDir = (agentDir, slug) => join(actionsDir(agentDir), slug);
19
29
 
20
- /** Read action.json for a slug object, or null if absent/unparseable. */
30
+ /** First non-empty body line the one-liner the digest shows. */
31
+ function snippetOf(item) {
32
+ const first = String(item.body ?? '').split('\n').map((l) => l.trim()).find(Boolean);
33
+ return first || item.title || item.id;
34
+ }
35
+
36
+ /**
37
+ * Read and parse ACTION.md for a slug → envelope manifest, or null if
38
+ * absent/unparseable. Shape: { id, kind, title, status, created_at,
39
+ * payload: {exec?, args?}, body } + promptSnippet derived from the body.
40
+ */
21
41
  export function loadManifest(agentDir, slug) {
22
- const path = join(actionDir(agentDir, slug), 'action.json');
42
+ const path = join(actionDir(agentDir, slug), 'ACTION.md');
23
43
  try {
24
- return JSON.parse(readFileSync(path, 'utf8'));
44
+ const { ok, item } = parseEnvelope(readFileSync(path, 'utf8'));
45
+ if (!ok || item.faculty !== 'actions') return null;
46
+ item.title = item.title ?? deriveTitle(item.body, item.id);
47
+ item.promptSnippet = snippetOf(item);
48
+ return item;
25
49
  } catch {
26
50
  return null;
27
51
  }
28
52
  }
29
53
 
30
- /** Pull `name` / `description` from a SKILL.md YAML-ish frontmatter (best-effort). */
31
- function skillFrontmatter(text) {
32
- const m = text.match(/^---\r?\n([\s\S]*?)\r?\n---/);
33
- const fm = {};
34
- if (m) {
35
- for (const line of m[1].split('\n')) {
36
- const kv = line.match(/^(\w+):\s*(.*)$/);
37
- if (kv) fm[kv[1]] = kv[2].trim();
38
- }
39
- }
40
- return fm;
54
+ /** The script entry file for a manifest (validated sibling name), or null. */
55
+ export function execOf(manifest) {
56
+ const exec = manifest?.payload?.exec ?? 'run.mjs';
57
+ return SAFE_EXEC.test(exec) ? exec : null;
41
58
  }
42
59
 
43
60
  /**
44
61
  * List actions in a base dir as {slug, kind, title, promptSnippet}.
45
- * `script` = dir has run.mjs; `runbook` = dir has SKILL.md; other entries skipped.
46
- * Reads the manifest directly from each entry dir (works for any baseDir, e.g. the inbox).
62
+ * An action is a dir carrying ACTION.md; kind comes from its envelope
63
+ * (`script` | `runbook`). Other entries are skipped (READMEs, stray files,
64
+ * unparseable envelopes — the latter surface via `zuzuu faculty items actions`).
65
+ * Reads directly from each entry dir (works for any baseDir, e.g. the inbox).
47
66
  */
48
67
  export function listActions(baseDir) {
49
68
  if (!existsSync(baseDir)) return [];
50
69
  const out = [];
51
- for (const name of readdirSync(baseDir)) {
70
+ for (const name of readdirSync(baseDir).sort()) {
52
71
  const d = join(baseDir, name);
53
72
  let isDir = false;
54
73
  try { isDir = statSync(d).isDirectory(); } catch { /* skip */ }
55
74
  if (!isDir) continue; // ignores README.md and any stray files
56
- if (existsSync(join(d, 'run.mjs'))) {
57
- let man = {};
58
- try { man = JSON.parse(readFileSync(join(d, 'action.json'), 'utf8')); } catch { /* slug fallback */ }
59
- out.push({ slug: name, kind: 'script', title: man.title ?? name, promptSnippet: man.promptSnippet ?? man.description ?? name });
60
- } else if (existsSync(join(d, 'SKILL.md'))) {
61
- let fm = {};
62
- try { fm = skillFrontmatter(readFileSync(join(d, 'SKILL.md'), 'utf8')); } catch { /* slug fallback */ }
63
- out.push({ slug: name, kind: 'runbook', title: fm.name ?? name, promptSnippet: fm.description ?? name });
64
- }
75
+ const actionMd = join(d, 'ACTION.md');
76
+ if (!existsSync(actionMd)) continue;
77
+ try {
78
+ const { ok, item } = parseEnvelope(readFileSync(actionMd, 'utf8'));
79
+ if (!ok) continue;
80
+ const kind = item.kind === 'script' ? 'script' : 'runbook';
81
+ out.push({ slug: name, kind, title: item.title ?? name, promptSnippet: snippetOf(item) });
82
+ } catch { /* unreadable skip */ }
65
83
  }
66
84
  return out;
67
85
  }
68
86
 
69
- /** Active actions under .zuzuu/actions/ (the inbox subdir is excluded). */
87
+ /** Active actions under .zuzuu/actions/ (inbox/proposals subdirs excluded). */
70
88
  export function allActions(agentDir) {
71
- return listActions(actionsDir(agentDir)).filter((a) => a.slug !== 'inbox');
89
+ return listActions(actionsDir(agentDir)).filter((a) => a.slug !== 'inbox' && a.slug !== 'proposals' && a.slug !== '_rolledback');
72
90
  }
@@ -1,9 +1,10 @@
1
1
  // zuzuu/actions/schema.mjs
2
2
  // A hand-rolled JSON-Schema *subset* validator — zero-dep (no Ajv), matching the
3
3
  // project's node-builtins-only policy. Supports: object (properties, required),
4
- // array (items), string/number/integer/boolean scalars, enum, and basic length/
5
- // range constraints. Returns an array of error strings ([] = valid). No coercion:
6
- // values are expected to already carry real JSON types.
4
+ // array (items), string/number/integer/boolean scalars, enum, pattern, and basic
5
+ // length/range constraints. Returns an array of error strings ([] = valid). No
6
+ // coercion: values are expected to already carry real JSON types. Shared by the
7
+ // actions runner AND the faculty envelope (payload validation — W24).
7
8
 
8
9
  function isPlainObject(v) {
9
10
  return v !== null && typeof v === 'object' && !Array.isArray(v);
@@ -41,6 +42,11 @@ export function validate(schema, value, path = '$') {
41
42
  if (typeof value === 'string') {
42
43
  if (schema.minLength != null && value.length < schema.minLength) errors.push(`${path}: shorter than minLength ${schema.minLength}`);
43
44
  if (schema.maxLength != null && value.length > schema.maxLength) errors.push(`${path}: longer than maxLength ${schema.maxLength}`);
45
+ if (schema.pattern != null) {
46
+ try {
47
+ if (!new RegExp(schema.pattern).test(value)) errors.push(`${path}: does not match pattern ${schema.pattern}`);
48
+ } catch { /* an unparseable authored pattern never blocks (fail-open) */ }
49
+ }
44
50
  }
45
51
  if (typeof value === 'number') {
46
52
  if (schema.minimum != null && value < schema.minimum) errors.push(`${path}: below minimum ${schema.minimum}`);
@@ -6,7 +6,7 @@
6
6
 
7
7
  import { join } from 'node:path';
8
8
  import { mkdirSync, appendFileSync } from 'node:fs';
9
- import { liveDir } from '../store.mjs';
9
+ import { liveDir } from '../core/store.mjs';
10
10
 
11
11
  /** Append a fail-soft outcome record. Never throws. */
12
12
  export function recordOutcome(agentDir, { slug, ok, error } = {}) {
@@ -1,23 +1,33 @@
1
1
  // zuzuu/commands/act-author.mjs
2
2
  // `zuzuu act new <slug>` — scaffold a script action (idempotent, no-clobber).
3
3
  // `zuzuu act schema <slug> [--mcp|--openai|--anthropic]` — convert the manifest.
4
+ // Actions are described by ACTION.md (the Faculty Standard envelope, W24).
4
5
 
5
6
  import { mkdirSync, existsSync, writeFileSync } from 'node:fs';
6
7
  import { join } from 'node:path';
7
8
  import { actionsDir, inboxDir, loadManifest, isSafeSlug } from '../actions/manifest.mjs';
8
9
  import { toMcpTool, toOpenAITool, toAnthropicTool } from '../actions/convert.mjs';
10
+ import { serializeEnvelope } from '../faculty/envelope.mjs';
9
11
 
10
- function manifestStub(slug) {
11
- return JSON.stringify({
12
- slug,
12
+ function actionMdStub(slug) {
13
+ return serializeEnvelope({
14
+ id: slug,
15
+ faculty: 'actions',
16
+ kind: 'script',
13
17
  title: slug,
14
- description: 'what this action does',
15
- promptSnippet: `one line the digest shows for ${slug}`,
16
- inputs: { type: 'object', properties: {}, required: [] },
17
- outputs: { type: 'object', properties: {} },
18
- default_args: {},
19
- requires: [],
20
- }, null, 2) + '\n';
18
+ status: 'active',
19
+ created_at: new Date().toISOString().replace(/\.\d+Z$/, 'Z'),
20
+ payload: { exec: 'run.mjs' },
21
+ body: [
22
+ `What ${slug} does — the first line is the one-liner the digest shows.`,
23
+ '',
24
+ '## Usage',
25
+ '',
26
+ `\`zuzuu act ${slug} --args '{"key":"value"}'\``,
27
+ '',
28
+ 'Describe inputs, outputs, and when to reach for this action here.',
29
+ ].join('\n'),
30
+ });
21
31
  }
22
32
 
23
33
  const RUN_TEMPLATE = `// run.mjs — implement the action. Export async main(args) → a JSON object.
@@ -26,7 +36,7 @@ const RUN_TEMPLATE = `// run.mjs — implement the action. Export async main(arg
26
36
  // export function prepareArguments(args) { return args; }
27
37
 
28
38
  export async function main(args) {
29
- // args is validated against action.json "inputs"; return must match "outputs".
39
+ // args = ACTION.md payload.args defaults merged with caller --args (caller wins).
30
40
  return { ok: true };
31
41
  }
32
42
  `;
@@ -41,7 +51,7 @@ function scaffoldInto(baseDir, slug) {
41
51
  const p = join(dir, name);
42
52
  if (!existsSync(p)) { writeFileSync(p, body); created.push(name); }
43
53
  };
44
- write('action.json', manifestStub(slug));
54
+ write('ACTION.md', actionMdStub(slug));
45
55
  write('run.mjs', RUN_TEMPLATE);
46
56
  return { created };
47
57
  }
@@ -66,7 +76,7 @@ export function newAction(agentDir, slug) {
66
76
  export function schema(agentDir, slug, args = {}) {
67
77
  if (!slug) { console.error('usage: zuzuu act schema <slug> [--mcp|--openai|--anthropic]'); process.exit(1); }
68
78
  const man = loadManifest(agentDir, slug);
69
- if (!man) { console.error(`no action '${slug}' (missing action.json)`); process.exit(1); }
79
+ if (!man) { console.error(`no action '${slug}' (missing ACTION.md)`); process.exit(1); }
70
80
  const def = args.openai ? toOpenAITool(man) : args.anthropic ? toAnthropicTool(man) : toMcpTool(man);
71
81
  console.log(JSON.stringify(def, null, 2));
72
82
  }
@@ -9,8 +9,8 @@
9
9
 
10
10
  import { readFileSync, existsSync } from 'node:fs';
11
11
  import { join } from 'node:path';
12
- import { paths } from '../store.mjs';
13
- import { allActions, loadManifest, actionsDir, isSafeSlug } from '../actions/manifest.mjs';
12
+ import { paths } from '../core/store.mjs';
13
+ import { allActions, actionsDir, isSafeSlug } from '../actions/manifest.mjs';
14
14
  import { runAction } from '../actions/dispatch.mjs';
15
15
  import { MARKER } from '../actions/marker.mjs';
16
16
  import { newAction, schema as schemaCmd, proposeAction } from './act-author.mjs';
@@ -35,10 +35,8 @@ function list(agentDir) {
35
35
 
36
36
  function show(agentDir, slug) {
37
37
  if (!slug) { console.error('usage: zuzuu act show <slug>'); process.exit(1); }
38
- const man = loadManifest(agentDir, slug);
39
- if (man) return console.log(JSON.stringify(man, null, 2));
40
- const skill = join(actionsDir(agentDir), slug, 'SKILL.md');
41
- if (existsSync(skill)) return process.stdout.write(readFileSync(skill, 'utf8'));
38
+ const actionMd = join(actionsDir(agentDir), slug, 'ACTION.md');
39
+ if (existsSync(actionMd)) return process.stdout.write(readFileSync(actionMd, 'utf8'));
42
40
  console.error(`no action '${slug}'`);
43
41
  process.exit(1);
44
42
  }
@@ -1,8 +1,8 @@
1
1
  // `zuzuu capture` — post-hoc: parse a host transcript into a git-native trace + record.
2
2
 
3
3
  import { ADAPTERS, byName, detected } from '../capture/adapters/registry.mjs';
4
- import { captureTrace } from '../capture-core.mjs';
5
- import { paths } from '../store.mjs';
4
+ import { captureTrace } from '../core/capture-core.mjs';
5
+ import { paths } from '../core/store.mjs';
6
6
 
7
7
  function chooseRef(adapter, args) {
8
8
  if (args.file) return args.file;
@@ -11,8 +11,8 @@ import { resolve } from 'node:path';
11
11
  import { spawnSync } from 'node:child_process';
12
12
  import { init } from './init.mjs';
13
13
  import { enable } from './enable.mjs';
14
- import { repoRoot } from '../store.mjs';
15
- import { homeExists } from '../scaffold.mjs';
14
+ import { repoRoot } from '../core/store.mjs';
15
+ import { homeExists } from '../home/scaffold.mjs';
16
16
 
17
17
  // Run a command inside `dir` without permanently changing the process cwd —
18
18
  // init/enable resolve their target from process.cwd(), so we chdir around them.
@@ -3,8 +3,8 @@
3
3
  // start would inject. Lets a human (or a hookless host) see exactly what the
4
4
  // agent sees.
5
5
 
6
- import { paths } from '../store.mjs';
7
- import { computeDigest } from '../digest.mjs';
6
+ import { paths } from '../core/store.mjs';
7
+ import { computeDigest } from '../digest/compose.mjs';
8
8
 
9
9
  /** Pure: the digest payload — the zuzuu-web /digest source (the daemon also reads .zuzuu/.live/digest.md directly). */
10
10
  export function digestData(agentDir, opts = {}) {
@@ -2,20 +2,15 @@
2
2
  //
3
3
  // Default: knowledge only (back-compat, via distillSessions). With
4
4
  // `--all-faculties`: mine each transcript ONCE into a superset, then run every
5
- // registered faculty miner (knowledge today; actions/guardrails/instructions/
6
- // memory land in later WS5 tasks) over the shared sessions array.
5
+ // faculty module's miner (the Faculty Module registry) over the shared
6
+ // sessions array. Miner hooks are miner-class: fail-soft + time-boxed a
7
+ // broken or hung miner degrades to 0 proposals, never sinks the others.
7
8
 
8
- import { paths } from '../store.mjs';
9
+ import { paths } from '../core/store.mjs';
9
10
  import { distillSessions, transcriptsFor, mineHostSession } from '../knowledge/distill.mjs';
10
- import * as registry from '../miners/registry.mjs';
11
- // Import miner modules so they self-register.
12
- import '../miners/knowledge.mjs';
13
- import '../miners/actions.mjs';
14
- import '../miners/guardrails.mjs';
15
- import '../miners/instructions.mjs';
16
- import '../miners/memory.mjs';
11
+ import * as registry from '../faculty/registry.mjs';
17
12
 
18
- export function distill(args) {
13
+ export async function distill(args) {
19
14
  const scope = args.all ? 'all' : args.session ? null : 'last';
20
15
  const pairs = transcriptsFor({ scope: scope ?? 'all', session: args.session || null, cwd: process.cwd() });
21
16
  if (!pairs.length) {
@@ -27,13 +22,17 @@ export function distill(args) {
27
22
  if (args['all-faculties'] || args.allFaculties) {
28
23
  const sessions = pairs.map(mineHostSession).filter(Boolean);
29
24
  const hosts = new Set(sessions.map((s) => s.host));
30
- console.log(`distilled ${sessions.length} session(s) across ${hosts.size} host(s) and ${registry.all().length} faculty miner(s):`);
25
+ const miners = registry.miners();
26
+ console.log(`distilled ${sessions.length} session(s) across ${hosts.size} host(s) and ${miners.length} faculty miner(s):`);
31
27
  let total = 0;
32
- for (const miner of registry.all()) {
33
- const cand = miner.aggregate(sessions, {});
34
- const n = miner.propose(agentDir, cand);
28
+ for (const miner of miners) {
29
+ const entry = { id: miner.faculty, module: miner };
30
+ const agg = await registry.invokeTimeboxed(entry, 'aggregate', [sessions, {}]);
31
+ const prop = agg.ok ? await registry.invokeTimeboxed(entry, 'propose', [agentDir, agg.value]) : agg;
32
+ const n = prop.ok && Number.isFinite(prop.value) ? prop.value : 0;
35
33
  total += n;
36
- console.log(` ${miner.faculty.padEnd(12)} ${n} proposal(s)`);
34
+ const note = prop.ok ? '' : ' (miner degraded — see zuzuu doctor)';
35
+ console.log(` ${miner.faculty.padEnd(12)} ${n} proposal(s)${note}`);
37
36
  }
38
37
  if (total) console.log('next: zuzuu review');
39
38
  return;
@@ -3,16 +3,17 @@
3
3
 
4
4
  import { mkdirSync, accessSync, constants } from 'node:fs';
5
5
  import { detected } from '../capture/adapters/registry.mjs';
6
- import { paths, gitInfo } from '../store.mjs';
6
+ import { paths, gitInfo } from '../core/store.mjs';
7
7
  import { listLive } from '../live/live-store.mjs';
8
8
  import { reconcile } from '../live/reconcile.mjs';
9
- import { planScaffold, homeExists } from '../scaffold.mjs';
9
+ import { planScaffold, homeExists } from '../home/scaffold.mjs';
10
+ import { facultiesOf, hookFailures } from '../faculty/registry.mjs';
10
11
  import { loadRegistry } from '../knowledge/registry.mjs';
11
12
  import { allItems } from '../knowledge/items.mjs';
12
13
  import { listProposals } from '../knowledge/proposals.mjs';
13
14
  import { detectEmbedder } from '../knowledge/embed.mjs';
14
- import { activeGeneration, readGeneration, snapshotFaculties } from '../faculty/generation.mjs';
15
- import { sessionStatus } from '../session-git.mjs';
15
+ import { activeGeneration, readGeneration, snapshotFaculties } from '../faculty/generation/read.mjs';
16
+ import { sessionStatus } from '../sessions/session-git.mjs';
16
17
  import { leftoverLine } from './session.mjs';
17
18
 
18
19
  /**
@@ -40,8 +41,8 @@ export function detectDrift(agentDir) {
40
41
  const pinned = lockfile.faculties || {};
41
42
  const drifted = [];
42
43
 
43
- // Compare per-faculty item arrays (knowledge, actions, memory).
44
- for (const faculty of ['knowledge', 'actions', 'memory']) {
44
+ // Compare per-faculty item arrays all five are envelope-item lists (W24).
45
+ for (const faculty of ['knowledge', 'actions', 'memory', 'guardrails', 'instructions']) {
45
46
  const pinnedItems = (pinned[faculty]?.items ?? []);
46
47
  const currentItems = (current[faculty]?.items ?? []);
47
48
 
@@ -65,19 +66,6 @@ export function detectDrift(agentDir) {
65
66
  }
66
67
  }
67
68
 
68
- // Compare single-file faculties (guardrails.rulesHash, instructions.projectHash)
69
- const singleFile = [
70
- { faculty: 'guardrails', field: 'rulesHash' },
71
- { faculty: 'instructions', field: 'projectHash' },
72
- ];
73
- for (const { faculty, field } of singleFile) {
74
- const pinnedHash = pinned[faculty]?.[field] ?? null;
75
- const currentHash = current[faculty]?.[field] ?? null;
76
- if (pinnedHash !== currentHash) {
77
- drifted.push({ id: field, faculty, reason: 'hash_changed', pinned: pinnedHash, current: currentHash });
78
- }
79
- }
80
-
81
69
  // Compare knowledge.registryHash
82
70
  const pinnedReg = pinned.knowledge?.registryHash ?? null;
83
71
  const currentReg = current.knowledge?.registryHash ?? null;
@@ -91,6 +79,32 @@ export function detectDrift(agentDir) {
91
79
  }
92
80
  }
93
81
 
82
+ /**
83
+ * Pure-ish: faculty-module health for doctor — broken manifests + recorded
84
+ * hook failures become warnings (the module degraded to items-only);
85
+ * declarative faculties get an informational note. Fail-open: any error
86
+ * returns empty lists rather than throwing into doctor.
87
+ * @returns {{warnings: string[], notes: string[]}}
88
+ */
89
+ export function facultyModuleHealth(agentDir) {
90
+ try {
91
+ const warnings = [];
92
+ const notes = [];
93
+ const entries = facultiesOf(agentDir);
94
+ for (const e of entries.filter((x) => x.manifestError)) {
95
+ warnings.push(`faculty '${e.id}' faculty.json unreadable (${e.manifestError}) — module degraded to items-only`);
96
+ }
97
+ const declarative = entries.filter((x) => x.declarative && !x.manifestError);
98
+ if (declarative.length) notes.push(`declarative faculties: ${declarative.map((x) => x.id).join(', ')}`);
99
+ for (const f of hookFailures()) {
100
+ warnings.push(`faculty '${f.faculty}' hook ${f.hook} failed (${f.error}) — degraded, items-only`);
101
+ }
102
+ return { warnings, notes };
103
+ } catch {
104
+ return { warnings: [], notes: [] }; // module health must never break doctor
105
+ }
106
+ }
107
+
94
108
  /** The closing line: honest about warnings, never "all good" under them. */
95
109
  export function summaryLine(problems, warnings) {
96
110
  if (problems) return `\n${problems} problem(s) found`;
@@ -146,6 +160,14 @@ export async function doctor() {
146
160
  else ok('faculty home complete (knowledge/ memory/ actions/ instructions/ guardrails/)');
147
161
  }
148
162
 
163
+ // faculty modules (the Faculty Module contract): a broken manifest or a
164
+ // failed hook degrades that faculty to items-only — surface it, never throw.
165
+ if (homeExists(root)) {
166
+ const health = facultyModuleHealth(dir);
167
+ for (const w of health.warnings) warn(w);
168
+ for (const n of health.notes) info(n);
169
+ }
170
+
149
171
  // knowledge faculty
150
172
  if (homeExists(root)) {
151
173
  const reg = loadRegistry(dir);