@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
@@ -6,7 +6,7 @@
6
6
  import { join, dirname } from 'node:path';
7
7
  import { fileURLToPath } from 'node:url';
8
8
  import { existsSync, readFileSync, writeFileSync, mkdirSync, rmSync } from 'node:fs';
9
- import { repoRoot } from '../store.mjs';
9
+ import { repoRoot } from '../core/store.mjs';
10
10
  import { addHooks, removeHooks, isInstalled, LIFECYCLE_EVENTS, GATE_EVENTS, addHookEntries, removeHookEntries } from '../live/install.mjs';
11
11
 
12
12
  const BIN = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'bin', 'zuzuu.mjs');
@@ -7,16 +7,11 @@
7
7
  // a one-line eval annotation per proposal card.
8
8
 
9
9
  import { join } from 'node:path';
10
- import { paths, readIndex } from '../store.mjs';
10
+ import { paths } from '../core/store.mjs';
11
11
  import * as registry from '../faculty/registry.mjs';
12
- import { listProposals as spineListProposals } from '../faculty/proposal.mjs';
12
+ import { buildSessionMtimes, facultyPending } from '../faculty/pending.mjs';
13
13
  import { rank } from '../eval/rank.mjs';
14
14
  import { getScorer } from '../eval/score.mjs';
15
- import '../knowledge/adapter.mjs'; // self-registers the 'knowledge' adapter
16
- import '../actions/adapter.mjs'; // self-registers the 'actions' adapter
17
- import '../guardrails/adapter.mjs'; // self-registers the 'guardrails' adapter
18
- import '../instructions/adapter.mjs'; // self-registers the 'instructions' adapter
19
- import '../memory/adapter.mjs'; // self-registers the 'memory' adapter
20
15
 
21
16
  /**
22
17
  * Format one eval annotation line for a proposal card in `zuzuu review`.
@@ -30,34 +25,6 @@ export function evalLine({ score, confidence, rationale }) {
30
25
  return `eval: ${score} [${confidence}] · ${rationale}${warn}`;
31
26
  }
32
27
 
33
- /**
34
- * Build sessionMtimes from the sessions index — cheap best-effort.
35
- * Falls back to {} on any error.
36
- * @param {string} [cwd]
37
- * @returns {Record<string, number>}
38
- */
39
- function buildSessionMtimes(cwd) {
40
- try {
41
- const idx = readIndex(cwd);
42
- const map = {};
43
- for (const s of idx.sessions ?? []) {
44
- if (!s.id) continue;
45
- // prefer startedAt ms; fall back to 0 (neutral recency)
46
- const ms = s.startedAt ? Date.parse(s.startedAt) : 0;
47
- if (!isNaN(ms) && ms > 0) map[s.id] = ms;
48
- }
49
- return map;
50
- } catch {
51
- return {};
52
- }
53
- }
54
-
55
- /** Collect proposals for a given adapter (mirrors review.mjs's facultyPending). */
56
- function collectProposals(agentDir, adapter) {
57
- if (typeof adapter.listProposals === 'function') return adapter.listProposals(agentDir);
58
- return spineListProposals(agentDir, adapter.name);
59
- }
60
-
61
28
  /**
62
29
  * Pure: gather + rank all pending proposals, returning structured data for JSON output.
63
30
  * The zuzuu-web /eval source.
@@ -77,7 +44,7 @@ export function evalData(agentDir, { faculty: onlyFaculty = null } = {}) {
77
44
  const allEntries = [];
78
45
  for (const adapter of adapters) {
79
46
  if (onlyFaculty && adapter.name !== onlyFaculty) continue;
80
- const proposals = collectProposals(agentDir, adapter);
47
+ const proposals = facultyPending(agentDir, adapter);
81
48
  for (const proposal of proposals) {
82
49
  allEntries.push({ proposal, faculty: adapter.name });
83
50
  }
@@ -26,12 +26,12 @@ const FACULTY_CONTRACTS = {
26
26
  'Propose one with `zuzuu act propose <slug>` (lands in actions/inbox/); on approval ' +
27
27
  'it becomes an active action you can run with `zuzuu act <slug>`.',
28
28
  instructions:
29
- 'instructions — the directive faculty: who to BE. The pinned steering artifact ' +
30
- '(instructions/project.md) that grounds every session. Empty by default — the ' +
29
+ 'instructions — the directive faculty: who to BE. The pinned steering item ' +
30
+ '(instructions/items/steering.md) that grounds every session. Empty by default — the ' +
31
31
  'digest tells the agent to interview you and draft it for your approval.',
32
32
  guardrails:
33
- 'guardrails — the protective faculty: what NOT to do, ENFORCED. Rules in ' +
34
- 'guardrails/rules.json gate tool calls (deny > ask > allow) before they run. ' +
33
+ 'guardrails — the protective faculty: what NOT to do, ENFORCED. One rule per item in ' +
34
+ 'guardrails/items/ gates tool calls (deny > ask > allow) before they run. ' +
35
35
  'A refusal here is policy, not preference. The gate fails open — never breaks the host.',
36
36
  };
37
37
 
@@ -0,0 +1,158 @@
1
+ // zuzuu/commands/faculty.mjs — `zuzuu faculty` (W24 Faculty Standard + the
2
+ // 2026-06-13 Faculty Module contract).
3
+ //
4
+ // The read surface over the one envelope format + the module contract:
5
+ // zuzuu faculty items <f> [--json|--jsonl] list a faculty's envelope items
6
+ // zuzuu faculty schema <f> [--json] print its payload schema
7
+ // zuzuu faculty manifest <f> [--json] print its faculty.json manifest
8
+ // zuzuu faculty overview [--json] ALL faculties in ONE process:
9
+ // manifest.ui + counts + top-3 item
10
+ // titles + pending counts (the
11
+ // daemon's batching endpoint)
12
+ //
13
+ // `--json` = one document; `--jsonl` = one item per line (streaming consumers).
14
+ // Declarative faculties (manifest-only folders) are first-class here: items
15
+ // list from manifest.itemsDir, schemas serve from the home, the overview and
16
+ // digest include them. Fail-soft like everything on the serve path.
17
+
18
+ import { existsSync, readFileSync } from 'node:fs';
19
+ import { join } from 'node:path';
20
+ import { paths } from '../core/store.mjs';
21
+ import { listFacultyItems } from '../faculty/items.mjs';
22
+ import { listProposals } from '../faculty/proposal.mjs';
23
+ import { PAYLOAD_SCHEMAS, FACULTY_KINDS } from '../faculty/envelope.mjs';
24
+ import { facultiesOf, facultyOf, get as getAdapter } from '../faculty/registry.mjs';
25
+
26
+ /** Pure: one faculty's envelope items + parse errors (the --json document). */
27
+ export function facultyItemsData(agentDir, faculty, manifest = null) {
28
+ const m = manifest ?? facultyOf(agentDir, faculty)?.manifest;
29
+ const { items, errors } = listFacultyItems(agentDir, faculty, { itemsDir: m?.itemsDir });
30
+ return { faculty, count: items.length, items, errors };
31
+ }
32
+
33
+ /**
34
+ * Pure: the payload schema served for a faculty — the home's seeded
35
+ * `<faculty>/schema.json` when present and parseable (humans may extend it),
36
+ * else the built-in default. Never throws.
37
+ */
38
+ export function facultySchemaData(agentDir, faculty) {
39
+ const seeded = join(agentDir, faculty, 'schema.json');
40
+ if (existsSync(seeded)) {
41
+ try { return { faculty, source: 'home', schema: JSON.parse(readFileSync(seeded, 'utf8')) }; }
42
+ catch { /* fall through to the built-in */ }
43
+ }
44
+ return { faculty, source: 'builtin', schema: PAYLOAD_SCHEMAS[faculty] ?? null };
45
+ }
46
+
47
+ /**
48
+ * Pure: one faculty's manifest document (home faculty.json when present,
49
+ * built-in fallback), or null for an unknown faculty.
50
+ */
51
+ export function facultyManifestData(agentDir, faculty) {
52
+ const entry = facultyOf(agentDir, faculty);
53
+ if (!entry) return null;
54
+ return {
55
+ faculty: entry.id,
56
+ source: entry.manifestSource,
57
+ declarative: entry.declarative,
58
+ ...(entry.manifestError ? { error: entry.manifestError } : {}),
59
+ manifest: entry.manifest,
60
+ };
61
+ }
62
+
63
+ /** Pending-proposal count for one faculty (dir-shaped adapters override). */
64
+ function pendingCount(agentDir, entry) {
65
+ try {
66
+ const a = getAdapter(entry.id);
67
+ if (a && typeof a.listProposals === 'function') return a.listProposals(agentDir).length;
68
+ return listProposals(agentDir, entry.id).length;
69
+ } catch {
70
+ return 0;
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Pure: the overview document — EVERY faculty (built-in + declarative) with
76
+ * manifest/ui, item + pending counts and the top-3 item titles, computed in
77
+ * ONE process (the web daemon's batching endpoint — kills 5-spawn cycles).
78
+ * Fail-soft per faculty: a broken one reports zeros + its manifestError.
79
+ */
80
+ export function facultyOverviewData(agentDir) {
81
+ const faculties = facultiesOf(agentDir).map((entry) => {
82
+ let items = [], errors = [];
83
+ try {
84
+ ({ items, errors } = listFacultyItems(agentDir, entry.id, { itemsDir: entry.manifest?.itemsDir }));
85
+ } catch { /* unreadable → zeros */ }
86
+ return {
87
+ id: entry.id,
88
+ title: entry.manifest?.title ?? entry.id,
89
+ tagline: entry.manifest?.tagline ?? '',
90
+ ui: entry.manifest?.ui ?? {},
91
+ kinds: entry.manifest?.kinds ?? [],
92
+ declarative: entry.declarative,
93
+ ...(entry.manifestError ? { manifestError: entry.manifestError } : {}),
94
+ counts: { items: items.length, pending: pendingCount(agentDir, entry), errors: errors.length },
95
+ top: items.slice(0, 3).map((i) => i.title ?? i.id),
96
+ };
97
+ });
98
+ return { faculties };
99
+ }
100
+
101
+ /** `zuzuu faculty <sub> [<f>]` — items | schema | manifest | overview. */
102
+ export function faculty(args = {}, log = console.log) {
103
+ const [sub, f] = args._ ?? [];
104
+ if (!sub || !['items', 'schema', 'manifest', 'overview'].includes(sub)) {
105
+ console.error('usage: zuzuu faculty items <faculty> [--json|--jsonl] · faculty schema <faculty> [--json] · faculty manifest <faculty> [--json] · faculty overview [--json]');
106
+ process.exitCode = 1;
107
+ return;
108
+ }
109
+ const agentDir = paths().dir;
110
+
111
+ if (sub === 'overview') {
112
+ const d = facultyOverviewData(agentDir);
113
+ if (args.json) { log(JSON.stringify(d, null, 2)); return; }
114
+ for (const fac of d.faculties) {
115
+ const pending = fac.counts.pending ? ` · ${fac.counts.pending} pending` : '';
116
+ const flag = fac.declarative ? ' [declarative]' : '';
117
+ log(`${fac.id.padEnd(13)} ${String(fac.counts.items).padStart(3)} item(s)${pending}${flag}${fac.manifestError ? ` ✗ ${fac.manifestError}` : ''}`);
118
+ }
119
+ return;
120
+ }
121
+
122
+ const entry = facultyOf(agentDir, f);
123
+ if (!entry) {
124
+ const known = facultiesOf(agentDir).map((x) => x.id);
125
+ console.error(`unknown faculty: ${f ?? '(none)'} — one of ${known.join(' · ')}`);
126
+ process.exitCode = 1;
127
+ return;
128
+ }
129
+
130
+ if (sub === 'manifest') {
131
+ const d = facultyManifestData(agentDir, entry.id);
132
+ log(JSON.stringify(args.json ? d : d.manifest, null, 2));
133
+ return;
134
+ }
135
+
136
+ if (sub === 'schema') {
137
+ const { schema } = facultySchemaData(agentDir, entry.id);
138
+ log(JSON.stringify(schema, null, 2));
139
+ return;
140
+ }
141
+
142
+ const data = facultyItemsData(agentDir, entry.id, entry.manifest);
143
+ if (args.jsonl) {
144
+ for (const item of data.items) log(JSON.stringify(item));
145
+ return;
146
+ }
147
+ if (args.json) {
148
+ log(JSON.stringify(data, null, 2));
149
+ return;
150
+ }
151
+ const kinds = FACULTY_KINDS[entry.id] ?? (entry.declarative && entry.manifest?.kinds?.length ? entry.manifest.kinds : null);
152
+ log(`${entry.id} — ${data.count} item(s)${kinds ? ` [${kinds.join('|')}]` : ''}`);
153
+ for (const it of data.items) {
154
+ log(` ${it.id} ${it.kind} · ${it.status ?? 'active'} — ${it.title}`);
155
+ }
156
+ for (const e of data.errors) log(` ✗ ${e.file}: ${e.error}`);
157
+ if (!data.count && !data.errors.length) log(' (none yet)');
158
+ }
@@ -4,10 +4,9 @@
4
4
  // zuzuu generation mint manually mint a generation from the current faculty state
5
5
  // zuzuu generation rollback <id> restore a past generation by content (flip active + restore)
6
6
 
7
- import { paths, repoRoot } from '../store.mjs';
8
- import {
9
- listGenerations, readGeneration, activeGeneration, mintGeneration, rollback, diffGenerations,
10
- } from '../faculty/generation.mjs';
7
+ import { paths, repoRoot } from '../core/store.mjs';
8
+ import { listGenerations, readGeneration, activeGeneration, diffGenerations } from '../faculty/generation/read.mjs';
9
+ import { mintGeneration, rollback } from '../faculty/generation/write.mjs';
11
10
 
12
11
  function agentDir() {
13
12
  return paths(repoRoot(process.cwd())).dir;
@@ -86,7 +85,8 @@ export function showLines(dir, id) {
86
85
  lines.push(` forkedFrom: ${d.forkedFrom ?? '(none — first generation)'}`);
87
86
  lines.push(` mintedFrom: ${d.mintedFrom.length} proposal(s)`);
88
87
  lines.push(' changes vs parent:');
89
- for (const f of ['knowledge', 'actions', 'memory']) {
88
+ // all five faculties are envelope-item lists under the Faculty Standard (W24)
89
+ for (const f of ['knowledge', 'actions', 'memory', 'guardrails', 'instructions']) {
90
90
  const x = d.faculties[f] || { added: [], changed: [], removed: [] };
91
91
  const parts = [];
92
92
  if (x.added.length) parts.push(`+${x.added.length} added`);
@@ -95,9 +95,6 @@ export function showLines(dir, id) {
95
95
  if (f === 'knowledge' && x.registryChanged) parts.push('registry changed');
96
96
  lines.push(` ${f}: ${parts.length ? parts.join(' · ') : 'no change'}`);
97
97
  }
98
- for (const f of ['guardrails', 'instructions']) {
99
- lines.push(` ${f}: ${d.faculties[f]?.changed ? 'changed' : 'no change'}`);
100
- }
101
98
  return lines.join('\n');
102
99
  }
103
100
 
@@ -14,14 +14,14 @@
14
14
  import { readFileSync, writeFileSync, appendFileSync, mkdirSync } from 'node:fs';
15
15
  import { join, dirname } from 'node:path';
16
16
  import { byName } from '../capture/adapters/registry.mjs';
17
- import { captureTrace } from '../capture-core.mjs';
18
- import { SessionState } from '../session.mjs';
17
+ import { captureTrace } from '../core/capture-core.mjs';
18
+ import { SessionState } from '../core/session.mjs';
19
19
  import { openLive, touchLive, closeLive, updateLive } from '../live/live-store.mjs';
20
- import { sessionGitEnabled, openSession, checkpoint, closeSession } from '../session-git.mjs';
21
- import { loadRules, evaluate, toPreToolUseDecision, toGeminiDecision } from '../guardrails.mjs';
22
- import { paths, liveDir as liveDirOf } from '../store.mjs';
23
- import { computeDigest } from '../digest.mjs';
24
- import { activeGeneration } from '../faculty/generation.mjs';
20
+ import { sessionGitEnabled, openSession, checkpoint, closeSession } from '../sessions/session-git.mjs';
21
+ import { loadRules, evaluate, toPreToolUseDecision, toGeminiDecision } from '../guardrails/engine.mjs';
22
+ import { paths, liveDir as liveDirOf } from '../core/store.mjs';
23
+ import { computeDigest } from '../digest/compose.mjs';
24
+ import { activeGeneration } from '../faculty/generation/read.mjs';
25
25
 
26
26
  // Lifecycle events, normalized across hosts (verified by observing each host):
27
27
  // open — session starts
@@ -110,8 +110,9 @@ export function handleHook({ event, payload = {}, cwd = process.cwd(), now = Dat
110
110
  }
111
111
 
112
112
  /**
113
- * The Guardrails gate (PreToolUse). Evaluates the tool call against
114
- * .zuzuu/guardrails/rules.json and prints Claude's hookSpecificOutput decision —
113
+ * The Guardrails gate (PreToolUse). Evaluates the tool call against the
114
+ * envelope rule items in .zuzuu/guardrails/items/ and prints Claude's
115
+ * hookSpecificOutput decision —
115
116
  * or NOTHING (exit 0, no JSON = defer to the host's normal permission flow).
116
117
  * That silence is the fail-open: engine errors and rule-file problems can slow
117
118
  * nothing down and block nothing. Matched decisions are logged for the trace.
@@ -127,14 +128,15 @@ function guardrailsLogName(sessionId) {
127
128
  const GATE_EVENTS = new Set(['PreToolUse', 'BeforeTool']);
128
129
 
129
130
  /**
130
- * Evaluate a tool call against rules.json and return the host's block decision
131
- * (or null = fail-open / no match → host's normal flow). Logs matched decisions.
131
+ * Evaluate a tool call against the guardrail rule items and return the host's
132
+ * block decision (or null = fail-open / no match → host's normal flow). Logs
133
+ * matched decisions.
132
134
  * codex + claude-code → hookSpecificOutput · gemini-cli → {decision,reason}
133
135
  */
134
136
  export function gateDecision({ host = 'claude-code', payload = {}, cwd = process.cwd() } = {}) {
135
137
  try {
136
138
  const { dir } = paths(cwd);
137
- const loaded = loadRules(join(dir, 'guardrails', 'rules.json'));
139
+ const loaded = loadRules(join(dir, 'guardrails'));
138
140
  if (!loaded.ok) return null;
139
141
  const verdict = evaluate(loaded.rules, { tool: payload.tool_name, input: payload.tool_input });
140
142
  if (verdict) {
@@ -5,15 +5,10 @@
5
5
  // points at `zuzuu review`. Fail-soft per faculty — a broken adapter or unreadable
6
6
  // proposal never sinks the whole view.
7
7
 
8
- import { paths } from '../store.mjs';
8
+ import { paths } from '../core/store.mjs';
9
9
  import { FACULTIES } from '../faculty/contract.mjs';
10
10
  import { listProposals } from '../faculty/proposal.mjs';
11
11
  import * as registry from '../faculty/registry.mjs';
12
- import '../knowledge/adapter.mjs'; // self-registers the 'knowledge' adapter
13
- import '../actions/adapter.mjs'; // self-registers the 'actions' adapter
14
- import '../guardrails/adapter.mjs'; // self-registers the 'guardrails' adapter
15
- import '../instructions/adapter.mjs'; // self-registers the 'instructions' adapter
16
- import '../memory/adapter.mjs'; // self-registers the 'memory' adapter
17
12
 
18
13
  /** Best-effort one-line title for a proposal (adapter.render → payload → id). */
19
14
  function titleOf(faculty, p) {
@@ -12,11 +12,12 @@
12
12
 
13
13
  import { join, basename } from 'node:path';
14
14
  import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
15
- import { applyScaffold, ensureGitignore, homeExists } from '../scaffold.mjs';
16
- import { injectBlock, facultiesBlock, hasBlock, BLOCK_VERSION } from '../inject.mjs';
15
+ import { applyScaffold, ensureGitignore, homeExists } from '../home/scaffold.mjs';
16
+ import { injectBlock, facultiesBlock, hasBlock, BLOCK_VERSION } from '../home/inject.mjs';
17
17
  import { detected } from '../capture/adapters/registry.mjs';
18
- import { repoRoot } from '../store.mjs';
19
- import { migrateHome } from './migrate.mjs';
18
+ import { repoRoot } from '../core/store.mjs';
19
+ import { migrateHome } from './migrations/home.mjs';
20
+ import { migrateItems, needsItemsMigration } from './migrations/items.mjs';
20
21
 
21
22
  const HOST_FILES = ['CLAUDE.md', 'AGENTS.md', 'GEMINI.md'];
22
23
  // dotfiles/dirs that don't make a directory "a project" for emptiness purposes
@@ -80,6 +81,19 @@ export function init(args = {}) {
80
81
  }
81
82
  } catch { /* fail-open */ }
82
83
 
84
+ // One-shot items migration (W24): pre-standard faculty shapes (rules.json /
85
+ // project.md / action.json / legacy item frontmatter) become Faculty Standard
86
+ // envelopes. Gated on detection, idempotent, fail-open — like migrateHome.
87
+ try {
88
+ const home = join(cwd, '.zuzuu');
89
+ if (existsSync(home) && needsItemsMigration(home)) {
90
+ const r = migrateItems(home);
91
+ const total = r.knowledge + r.memory + r.guardrails + r.actions + r.instructions;
92
+ if (total) console.log(`Migrated ${total} faculty item(s) to the envelope standard (knowledge ${r.knowledge} · memory ${r.memory} · guardrails ${r.guardrails} · actions ${r.actions} · instructions ${r.instructions})`);
93
+ for (const e of r.errors) console.log(` ✗ ${e.file}: ${e.error} — left in place; fix and rerun \`zuzuu migrate --items\``);
94
+ }
95
+ } catch { /* fail-open */ }
96
+
83
97
  const reinit = homeExists(cwd);
84
98
  const greenfield = !reinit && isEmptyDir(cwd);
85
99
 
@@ -1,7 +1,7 @@
1
1
  // Knowledge CLI: `zuzuu remember` (human-direct write — the human IS the gate),
2
2
  // `zuzuu recall` (one command, three search modes), `zuzuu knowledge reindex|audit`.
3
3
 
4
- import { paths } from '../store.mjs';
4
+ import { paths } from '../core/store.mjs';
5
5
  import { loadRegistry, validateItem } from '../knowledge/registry.mjs';
6
6
  import { slugify, writeItem, readItem, allItems } from '../knowledge/items.mjs';
7
7
  import { upsertItem, reindex, search, neighbors, indexPath, putVector, allVectors, unembedded } from '../knowledge/index.mjs';
@@ -0,0 +1,96 @@
1
+ // zuzuu/commands/migrations/home.mjs — the home migrator (W1, 2026-06-12):
2
+ // visible agent/ → hidden .zuzuu/ (byte-identical inner layout), plus the
3
+ // follow-ups: traceRef rewrite, gitignore + deny-rule swap, host-block re-inject.
4
+ // Pure core: migrateHome(root) → { migrated }. Idempotent + fail-soft.
5
+
6
+ import { existsSync, readFileSync, writeFileSync, renameSync, rmSync } from 'node:fs';
7
+ import { join } from 'node:path';
8
+ import { repoRoot } from '../../core/store.mjs';
9
+ import { ensureGitignore } from '../../home/scaffold.mjs';
10
+ import { injectBlock, BLOCK_VERSION } from '../../home/inject.mjs';
11
+
12
+ // The denies the old visible-agent/ home installed; scrubbed here (NOT kept in
13
+ // install.mjs — clean break) and replaced by the current narrow .zuzuu/ pair.
14
+ const LEGACY_DENY_RULES = ['Read(./agent/.traces/**)', 'Read(./agent/.live/**)'];
15
+ const NEW_DENY_RULES = ['Read(./.zuzuu/.traces/**)', 'Read(./.zuzuu/.live/**)'];
16
+
17
+ /**
18
+ * One-shot HOME migration: visible `agent/` → hidden `.zuzuu/` (byte-identical
19
+ * inner layout). Gated on `agent/agent.json` — `agent/` is a common dir name,
20
+ * so an unrelated agent/ dir in a brownfield repo must NEVER be touched (the
21
+ * one place this differs from the old `.mns→agent` precedent). Idempotent +
22
+ * fail-soft; NEVER clobbers an existing .zuzuu/. Pure FS move (renameSync).
23
+ * @returns {{migrated: boolean}}
24
+ */
25
+ export function migrateHome(root = repoRoot()) {
26
+ const legacy = join(root, 'agent');
27
+ const home = join(root, '.zuzuu');
28
+ if (existsSync(home) || !existsSync(join(legacy, 'agent.json'))) return { migrated: false };
29
+
30
+ renameSync(legacy, home); // move the whole home (atomic on same filesystem)
31
+
32
+ rewriteTraceRefs(home);
33
+ rewriteGitignore(root);
34
+ scrubLegacyDenies(root);
35
+ // derived index: drop, it rebuilds on the next recall/reindex
36
+ try { rmSync(join(home, 'knowledge', '.index.db'), { force: true }); } catch { /* fail-soft */ }
37
+ return { migrated: true };
38
+ }
39
+
40
+ /** sessions.json stores repo-relative traceRefs (`agent/.traces/…`) — re-point them. */
41
+ function rewriteTraceRefs(home) {
42
+ const index = join(home, 'sessions.json');
43
+ if (!existsSync(index)) return;
44
+ try {
45
+ const idx = JSON.parse(readFileSync(index, 'utf8'));
46
+ for (const s of idx.sessions || []) {
47
+ if (typeof s.traceRef === 'string' && s.traceRef.startsWith('agent/')) {
48
+ s.traceRef = '.zuzuu/' + s.traceRef.slice('agent/'.length);
49
+ }
50
+ }
51
+ writeFileSync(index, JSON.stringify(idx, null, 2) + '\n');
52
+ } catch { /* fail-soft: a bad index never blocks the move */ }
53
+ }
54
+
55
+ /** Drop legacy `agent/` ignore lines, then append the canonical .zuzuu/ ones. */
56
+ function rewriteGitignore(root) {
57
+ const path = join(root, '.gitignore');
58
+ if (existsSync(path)) {
59
+ const kept = readFileSync(path, 'utf8')
60
+ .split('\n')
61
+ .filter((l) => !l.trim().startsWith('agent/'))
62
+ .join('\n');
63
+ writeFileSync(path, kept.endsWith('\n') || kept === '' ? kept : kept + '\n');
64
+ }
65
+ ensureGitignore(root); // appends .zuzuu/.traces/, .zuzuu/.live/, .zuzuu/knowledge/.index.db
66
+ }
67
+
68
+ /** Swap the old agent/ deny rules for the .zuzuu/ pair in any .claude settings file. */
69
+ function scrubLegacyDenies(root) {
70
+ for (const f of ['settings.json', 'settings.local.json']) {
71
+ const path = join(root, '.claude', f);
72
+ if (!existsSync(path)) continue;
73
+ try {
74
+ const s = JSON.parse(readFileSync(path, 'utf8'));
75
+ const deny = s?.permissions?.deny;
76
+ if (!Array.isArray(deny)) continue;
77
+ const hadOurs = deny.some((r) => LEGACY_DENY_RULES.includes(r));
78
+ if (!hadOurs) continue;
79
+ s.permissions.deny = deny.filter((r) => !LEGACY_DENY_RULES.includes(r));
80
+ for (const rule of NEW_DENY_RULES) if (!s.permissions.deny.includes(rule)) s.permissions.deny.push(rule);
81
+ writeFileSync(path, JSON.stringify(s, null, 2) + '\n');
82
+ } catch { /* fail-soft: never break settings we can't parse */ }
83
+ }
84
+ }
85
+
86
+ /** Re-inject the current faculties block into any existing host instruction files. */
87
+ export function reinjectHostBlocks(root) {
88
+ for (const f of ['CLAUDE.md', 'AGENTS.md', 'GEMINI.md']) {
89
+ const p = join(root, f);
90
+ if (existsSync(p)) {
91
+ const text = readFileSync(p, 'utf8');
92
+ if (!text.includes(`zuzuu:faculties:v${BLOCK_VERSION}`)) writeFileSync(p, injectBlock(text));
93
+ }
94
+ }
95
+ }
96
+
@@ -0,0 +1,48 @@
1
+ // zuzuu/commands/migrations/index.mjs — `zuzuu migrate` dispatch.
2
+ //
3
+ // (default) proposal schema: legacy {candidate, er} → spine {payload, analysis, faculty}
4
+ // --home faculty home: visible agent/ → hidden .zuzuu/
5
+ // --items Faculty Standard: legacy faculty shapes → the envelope standard
6
+ //
7
+ // Pure cores live beside this file (proposals/home/items.mjs); this is the CLI
8
+ // surface — resolves paths, runs a core, prints the summary.
9
+
10
+ import { paths, repoRoot } from '../../core/store.mjs';
11
+ import { BLOCK_VERSION } from '../../home/inject.mjs';
12
+ import { migrateProposals } from './proposals.mjs';
13
+ import { migrateHome, reinjectHostBlocks } from './home.mjs';
14
+ import { migrateItems, needsItemsMigration } from './items.mjs';
15
+
16
+ export { migrateProposals } from './proposals.mjs';
17
+ export { migrateHome } from './home.mjs';
18
+ export { migrateItems, needsItemsMigration } from './items.mjs';
19
+
20
+ export function migrate(args = {}) {
21
+ if (args.items) {
22
+ const agentDir = paths().dir;
23
+ const r = migrateItems(agentDir);
24
+ const total = r.knowledge + r.memory + r.guardrails + r.actions + r.instructions;
25
+ console.log(`migrate --items: ${total} item(s) → the Faculty Standard envelope — knowledge ${r.knowledge} · memory ${r.memory} · guardrails ${r.guardrails} · actions ${r.actions} · instructions ${r.instructions} (${r.skipped} already standard)`);
26
+ if (r.manifests) console.log(` seeded ${r.manifests} faculty manifest(s) (faculty.json — the Faculty Module contract)`);
27
+ for (const e of r.errors) console.log(` ✗ ${e.file}: ${e.error}`);
28
+ if (!total && !r.manifests && !r.errors.length) console.log(' nothing to migrate (the home already speaks the envelope)');
29
+ return;
30
+ }
31
+ if (args.home) {
32
+ const root = repoRoot(process.cwd());
33
+ const { migrated } = migrateHome(root);
34
+ if (!migrated) { console.log('migrate --home: nothing to do (already .zuzuu/, or no zuzuu home at agent/)'); return; }
35
+ try { reinjectHostBlocks(root); } catch { /* fail-open */ }
36
+ console.log(`migrate --home: agent/ → .zuzuu/ (hidden, like .git; block v${BLOCK_VERSION}, gitignore + deny rules rewritten)`);
37
+ console.log(' transparency lives in porcelain now: zuzuu status · explain · digest');
38
+ return;
39
+ }
40
+ const agentDir = paths().dir;
41
+ const { scanned, migrated, skipped } = migrateProposals(agentDir);
42
+ console.log(`migrate: scanned ${scanned} proposal(s) — migrated ${migrated}, skipped ${skipped}`);
43
+ if (migrated > 0) {
44
+ console.log(' legacy candidate/er keys rewritten to payload/analysis.er + faculty:knowledge');
45
+ } else {
46
+ console.log(' nothing to migrate (all records already in new shape)');
47
+ }
48
+ }