@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.
- package/bin/zuzuu.mjs +20 -4
- package/package.json +1 -1
- package/web-app/dist/auth.js +91 -0
- package/web-app/dist/server.js +16 -79
- package/web-app/dist/zuzuu-cli.js +124 -0
- package/web-app/dist/{zuzuu-api.js → zuzuu-routes.js} +158 -133
- package/web-app/web-dist/assets/CommandPalette-DhBdR7X3.js +45 -0
- package/web-app/web-dist/assets/DiffTab-CqxwSjI2.js +1 -0
- package/web-app/web-dist/assets/EditorPane-94QPFR9R.js +41 -0
- package/web-app/web-dist/assets/MonacoFile-D76epTrG.js +1 -0
- package/web-app/web-dist/assets/angular-html-BVBpGdXr.js +1 -0
- package/web-app/web-dist/assets/{angular-ts-CD_OonCa.js → angular-ts-BfdufMKP.js} +1 -1
- package/web-app/web-dist/assets/{apl-uOGC3x4e.js → apl-DWBSSoBH.js} +1 -1
- package/web-app/web-dist/assets/{astro-B6ybQmWG.js → astro-3LtMP0Sq.js} +1 -1
- package/web-app/web-dist/assets/{blade-B1QGRlVx.js → blade-llJRbbtR.js} +1 -1
- package/web-app/web-dist/assets/c-Wt1voDr2.js +1 -0
- package/web-app/web-dist/assets/{cobol-BgqgtYWn.js → cobol-x_HIyl2P.js} +1 -1
- package/web-app/web-dist/assets/{coffee-0wIRKYlr.js → coffee-CThvmt4R.js} +1 -1
- package/web-app/web-dist/assets/cpp-NtAeskI3.js +1 -0
- package/web-app/web-dist/assets/{crystal-CyTK3qFN.js → crystal-DNu_sX0G.js} +1 -1
- package/web-app/web-dist/assets/css-DJp_X0uY.js +1 -0
- package/web-app/web-dist/assets/{cssMode-B9jnrWOz.js → cssMode-ByQBaInt.js} +1 -1
- package/web-app/web-dist/assets/dist-DQqjtuhV.js +153 -0
- package/web-app/web-dist/assets/{edge-CvML9pwC.js → edge-ozw5tpLl.js} +1 -1
- package/web-app/web-dist/assets/{editor.api2-BmGoRSl4.js → editor.api2-C7skgoRB.js} +1 -1
- package/web-app/web-dist/assets/{elixir-CrjqTiSc.js → elixir-VhA6FeZt.js} +1 -1
- package/web-app/web-dist/assets/{elm-C4JtJ0Au.js → elm-dREJmIFz.js} +1 -1
- package/web-app/web-dist/assets/{erb-Cmeb-29V.js → erb-CIg6G69l.js} +1 -1
- package/web-app/web-dist/assets/{freemarker2-B5LAi19B.js → freemarker2-CBBwP9JV.js} +1 -1
- package/web-app/web-dist/assets/{git-rebase-CXqdToiP.js → git-rebase-B44mJPta.js} +1 -1
- package/web-app/web-dist/assets/{glimmer-js-Kq-kdTyV.js → glimmer-js-vH_gHG0-.js} +1 -1
- package/web-app/web-dist/assets/{glimmer-ts-D0RKLJNf.js → glimmer-ts--abOzSAQ.js} +1 -1
- package/web-app/web-dist/assets/glsl-Dv5r7kPw.js +1 -0
- package/web-app/web-dist/assets/graphql-CB4jsw2E.js +1 -0
- package/web-app/web-dist/assets/{hack-trjVF3Po.js → hack-DvEYX148.js} +1 -1
- package/web-app/web-dist/assets/haml-zE6W3STP.js +1 -0
- package/web-app/web-dist/assets/{handlebars-B8_x7Zx7.js → handlebars-CzBR2SDs.js} +1 -1
- package/web-app/web-dist/assets/{handlebars-g7ZhGhI_.js → handlebars-tXdfxEd6.js} +1 -1
- package/web-app/web-dist/assets/html-C8UlPnhE.js +1 -0
- package/web-app/web-dist/assets/{html-CfvRMgoC.js → html-DgPn1QYH.js} +1 -1
- package/web-app/web-dist/assets/{html-derivative-BYX_F_XH.js → html-derivative-CY6NRz-J.js} +1 -1
- package/web-app/web-dist/assets/{htmlMode-Bi8vSvwb.js → htmlMode-BtdIDgA2.js} +1 -1
- package/web-app/web-dist/assets/{http-BIVDpHT-.js → http-Cyd7bS_S.js} +1 -1
- package/web-app/web-dist/assets/{hurl-CFsshMju.js → hurl-CWPsiEpf.js} +1 -1
- package/web-app/web-dist/assets/index-B27_WOhS.css +2 -0
- package/web-app/web-dist/assets/index-De6DWTZM.js +7 -0
- package/web-app/web-dist/assets/java-CGc3VwQr.js +1 -0
- package/web-app/web-dist/assets/{javascript-Bxx2wV4w.js → javascript-5m05n-Be.js} +1 -1
- package/web-app/web-dist/assets/javascript-CUt1pgmJ.js +1 -0
- package/web-app/web-dist/assets/{jinja-_ZS5zWwe.js → jinja-CD-Z-FLd.js} +1 -1
- package/web-app/web-dist/assets/{jison-D8mMEpcs.js → jison-imPNup1l.js} +1 -1
- package/web-app/web-dist/assets/json-Bg9ijW3F.js +1 -0
- package/web-app/web-dist/assets/{jsonMode-C6ELX5GM.js → jsonMode-BG32YnTY.js} +1 -1
- package/web-app/web-dist/assets/jsx-CY6oMTks.js +1 -0
- package/web-app/web-dist/assets/{julia-D4h2DZrs.js → julia-Dc3O-irA.js} +1 -1
- package/web-app/web-dist/assets/{just-bMqQi3xg.js → just-BhOq_Kbv.js} +1 -1
- package/web-app/web-dist/assets/{latex-DThYi3CX.js → latex-Cu4Y1d5w.js} +1 -1
- package/web-app/web-dist/assets/lib-KIOQTlcs.js +1 -0
- package/web-app/web-dist/assets/{liquid-CUjzzP4r.js → liquid-3ZnQzTbs.js} +1 -1
- package/web-app/web-dist/assets/{liquid-CesB-zzl.js → liquid-CvXMrjlQ.js} +1 -1
- package/web-app/web-dist/assets/{lspLanguageFeatures-gTnJsses.js → lspLanguageFeatures-6KXALSrl.js} +1 -1
- package/web-app/web-dist/assets/lua-BjLEUjKY.js +1 -0
- package/web-app/web-dist/assets/{marko-yoGoLK2m.js → marko-DvhNOisQ.js} +1 -1
- package/web-app/web-dist/assets/{mdc-BvtXU6eH.js → mdc-Bm9TpL1X.js} +1 -1
- package/web-app/web-dist/assets/{mdx-DrXGQbNB.js → mdx-DffTEkNE.js} +1 -1
- package/web-app/web-dist/assets/{monaco-setup-CsR6EfHe.js → monaco-setup-DM3A5_VI.js} +3 -3
- package/web-app/web-dist/assets/{nginx-DoUz032F.js → nginx-Bhc82uuv.js} +1 -1
- package/web-app/web-dist/assets/{nim-B0Pl8B4R.js → nim-DXTVBFnF.js} +1 -1
- package/web-app/web-dist/assets/{perl-D2tfAALb.js → perl-C7veXV9z.js} +1 -1
- package/web-app/web-dist/assets/{php-BImCcX5X.js → php-BRiuMnnr.js} +1 -1
- package/web-app/web-dist/assets/{pug-BcnpC8P_.js → pug-C5hz5LQ7.js} +1 -1
- package/web-app/web-dist/assets/{python-ypRCBnvu.js → python-DyLAD3Wt.js} +1 -1
- package/web-app/web-dist/assets/{qml-DFDAunHY.js → qml-BdUV3aTS.js} +1 -1
- package/web-app/web-dist/assets/r-8R7vtdQc.js +1 -0
- package/web-app/web-dist/assets/{razor-aqrhpwqZ.js → razor-C49xQTPQ.js} +1 -1
- package/web-app/web-dist/assets/{razor-1_376SZM.js → razor-DRL52XO2.js} +1 -1
- package/web-app/web-dist/assets/react-vendor-CCIEwYL0.js +9 -0
- package/web-app/web-dist/assets/regexp-Omp9DhTb.js +1 -0
- package/web-app/web-dist/assets/{rst-2vG6f11Y.js → rst-BHX71KW9.js} +1 -1
- package/web-app/web-dist/assets/{ruby-Dj6bCFXR.js → ruby-B--HzjGU.js} +1 -1
- package/web-app/web-dist/assets/{sas-BhVZ4qL2.js → sas-DrLaYOK_.js} +1 -1
- package/web-app/web-dist/assets/scss-DdSxiZKl.js +1 -0
- package/web-app/web-dist/assets/shellscript-DwcUjJBL.js +1 -0
- package/web-app/web-dist/assets/{shellsession-CyO2fnhB.js → shellsession-CPZkydE6.js} +1 -1
- package/web-app/web-dist/assets/{soy-DIkw6E88.js → soy-Br5FhD7c.js} +1 -1
- package/web-app/web-dist/assets/sql-DNssxck8.js +1 -0
- package/web-app/web-dist/assets/{stata-DvkM932O.js → stata-DXn1tqOr.js} +1 -1
- package/web-app/web-dist/assets/{surrealql-B4-Q8tqV.js → surrealql-IeLNQw0f.js} +1 -1
- package/web-app/web-dist/assets/{svelte-p6yBy-Ki.js → svelte-DOdLCIlh.js} +1 -1
- package/web-app/web-dist/assets/{templ-C7EkuiZr.js → templ-CIwIngms.js} +1 -1
- package/web-app/web-dist/assets/{tex-DkmD8uFC.js → tex-D8QMumu5.js} +1 -1
- package/web-app/web-dist/assets/{ts-tags-U-hncHg4.js → ts-tags-BMVY4q-l.js} +1 -1
- package/web-app/web-dist/assets/{tsMode-a8OvovQd.js → tsMode-BndVBac5.js} +1 -1
- package/web-app/web-dist/assets/tsx-5Eka4NBX.js +1 -0
- package/web-app/web-dist/assets/{twig-CU0OP-IA.js → twig-C8o_5mgw.js} +1 -1
- package/web-app/web-dist/assets/{typescript-DnLjiKtn.js → typescript-B1w9vqKF.js} +1 -1
- package/web-app/web-dist/assets/typescript-DOu2WMV5.js +1 -0
- package/web-app/web-dist/assets/{vue-Db7nY3ba.js → vue-BU18DNDL.js} +1 -1
- package/web-app/web-dist/assets/{vue-html-BvAbiAw1.js → vue-html-BeluIYX0.js} +1 -1
- package/web-app/web-dist/assets/{vue-vine-BEaIQIlA.js → vue-vine-DGUAbOCX.js} +1 -1
- package/web-app/web-dist/assets/{xml-an4Nuuqq.js → xml-D8uAlVv5.js} +1 -1
- package/web-app/web-dist/assets/xml-DIqSwXR3.js +1 -0
- package/web-app/web-dist/assets/{xsl-D3NQgH22.js → xsl-Ct_-YIAy.js} +1 -1
- package/web-app/web-dist/assets/xterm-B1ffpRuj.js +36 -0
- package/web-app/web-dist/assets/xterm-addons-psDEiUMC.js +136 -0
- package/web-app/web-dist/assets/{yaml-Diiu6O9P.js → yaml-Bb7jXyQv.js} +1 -1
- package/web-app/web-dist/assets/yaml-DTtCYNlS.js +1 -0
- package/web-app/web-dist/index.html +6 -3
- package/zuzuu/actions/convert.mjs +10 -9
- package/zuzuu/actions/dispatch.mjs +12 -7
- package/zuzuu/actions/inbox.mjs +5 -5
- package/zuzuu/actions/manifest.mjs +48 -30
- package/zuzuu/actions/schema.mjs +9 -3
- package/zuzuu/actions/trail.mjs +1 -1
- package/zuzuu/commands/act-author.mjs +23 -13
- package/zuzuu/commands/act.mjs +4 -6
- package/zuzuu/commands/capture.mjs +2 -2
- package/zuzuu/commands/code.mjs +2 -2
- package/zuzuu/commands/digest.mjs +2 -2
- package/zuzuu/commands/distill.mjs +15 -16
- package/zuzuu/commands/doctor.mjs +41 -19
- package/zuzuu/commands/enable.mjs +1 -1
- package/zuzuu/commands/eval.mjs +3 -36
- package/zuzuu/commands/explain.mjs +4 -4
- package/zuzuu/commands/faculty.mjs +158 -0
- package/zuzuu/commands/generation.mjs +5 -8
- package/zuzuu/commands/hook.mjs +14 -12
- package/zuzuu/commands/inbox.mjs +1 -6
- package/zuzuu/commands/init.mjs +18 -4
- package/zuzuu/commands/knowledge.mjs +1 -1
- package/zuzuu/commands/migrations/home.mjs +96 -0
- package/zuzuu/commands/migrations/index.mjs +48 -0
- package/zuzuu/commands/migrations/items.mjs +360 -0
- package/zuzuu/commands/migrations/proposals.mjs +100 -0
- package/zuzuu/commands/proposals.mjs +131 -0
- package/zuzuu/commands/review.mjs +13 -227
- package/zuzuu/commands/session.mjs +8 -2
- package/zuzuu/commands/sessions.mjs +159 -0
- package/zuzuu/commands/status.mjs +3 -3
- package/zuzuu/commands/trace.mjs +1 -1
- package/zuzuu/{capture-core.mjs → core/capture-core.mjs} +3 -3
- package/zuzuu/{store.mjs → core/store.mjs} +1 -1
- package/zuzuu/digest/compose.mjs +96 -0
- package/zuzuu/eval/score.mjs +14 -1
- package/zuzuu/faculties/actions/index.mjs +283 -0
- package/zuzuu/faculties/guardrails/index.mjs +320 -0
- package/zuzuu/faculties/instructions/index.mjs +288 -0
- package/zuzuu/faculties/knowledge/index.mjs +185 -0
- package/zuzuu/faculties/memory/index.mjs +124 -0
- package/zuzuu/faculty/envelope.mjs +290 -0
- package/zuzuu/faculty/generation/read.mjs +206 -0
- package/zuzuu/faculty/generation/write.mjs +207 -0
- package/zuzuu/faculty/items.mjs +81 -0
- package/zuzuu/faculty/module.mjs +74 -0
- package/zuzuu/faculty/pending.mjs +63 -0
- package/zuzuu/faculty/registry.mjs +204 -18
- package/zuzuu/faculty/render.mjs +59 -0
- package/zuzuu/faculty/trail.mjs +1 -1
- package/zuzuu/guardrails/engine.mjs +137 -0
- package/zuzuu/{scaffold.mjs → home/scaffold.mjs} +110 -39
- package/zuzuu/knowledge/items.mjs +56 -91
- package/zuzuu/live/install.mjs +1 -1
- package/zuzuu/live/live-store.mjs +2 -2
- package/zuzuu/live/reconcile.mjs +2 -2
- package/zuzuu/sessions/git.mjs +47 -0
- package/zuzuu/{session-git.mjs → sessions/session-git.mjs} +5 -43
- package/web-app/web-dist/assets/DiffTab-BuWonUNJ.js +0 -1
- package/web-app/web-dist/assets/MonacoFile-CL3DhFKG.js +0 -1
- package/web-app/web-dist/assets/angular-html-CmT26mqM.js +0 -1
- package/web-app/web-dist/assets/c-BvoqrSVH.js +0 -1
- package/web-app/web-dist/assets/cpp-BXsk94m0.js +0 -1
- package/web-app/web-dist/assets/css-Z8oOGxII.js +0 -1
- package/web-app/web-dist/assets/dist-ChcDQ_7s.js +0 -153
- package/web-app/web-dist/assets/glsl-KwyfU2aa.js +0 -1
- package/web-app/web-dist/assets/graphql-DSeOUAa2.js +0 -1
- package/web-app/web-dist/assets/haml-azVoxQRV.js +0 -1
- package/web-app/web-dist/assets/html-D_7P5S4m.js +0 -1
- package/web-app/web-dist/assets/index--5yy8RbA.js +0 -267
- package/web-app/web-dist/assets/index-BVG4hgk7.css +0 -2
- package/web-app/web-dist/assets/java-D4RbCvBe.js +0 -1
- package/web-app/web-dist/assets/javascript-Cb010CKM.js +0 -1
- package/web-app/web-dist/assets/json-DWgqV4D1.js +0 -1
- package/web-app/web-dist/assets/jsx-CZjSJa1f.js +0 -1
- package/web-app/web-dist/assets/lua-TGj_6NzO.js +0 -1
- package/web-app/web-dist/assets/r-fCpuAR7u.js +0 -1
- package/web-app/web-dist/assets/regexp-B4yxx-Ty.js +0 -1
- package/web-app/web-dist/assets/scss-QdjMO_xV.js +0 -1
- package/web-app/web-dist/assets/shellscript-BnlgeVVx.js +0 -1
- package/web-app/web-dist/assets/sql-DGnQv6iD.js +0 -1
- package/web-app/web-dist/assets/tsx-MJ0-9sYG.js +0 -1
- package/web-app/web-dist/assets/typescript-C17ZkDe8.js +0 -1
- package/web-app/web-dist/assets/xml-CA9lHFQV.js +0 -1
- package/web-app/web-dist/assets/yaml-CwRYMJka.js +0 -1
- package/zuzuu/actions/adapter.mjs +0 -130
- package/zuzuu/commands/migrate.mjs +0 -225
- package/zuzuu/digest.mjs +0 -149
- package/zuzuu/faculty/generation.mjs +0 -392
- package/zuzuu/guardrails/adapter.mjs +0 -134
- package/zuzuu/guardrails.mjs +0 -89
- package/zuzuu/instructions/adapter.mjs +0 -93
- package/zuzuu/knowledge/adapter.mjs +0 -99
- package/zuzuu/memory/adapter.mjs +0 -121
- package/zuzuu/miners/actions.mjs +0 -118
- package/zuzuu/miners/guardrails.mjs +0 -179
- package/zuzuu/miners/instructions.mjs +0 -157
- package/zuzuu/miners/knowledge.mjs +0 -25
- package/zuzuu/miners/memory.mjs +0 -27
- package/zuzuu/miners/registry.mjs +0 -31
- /package/web-app/web-dist/assets/{chunk-QTnfLwEv.js → rolldown-runtime-QTnfLwEv.js} +0 -0
- /package/zuzuu/{session.mjs → core/session.mjs} +0 -0
- /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');
|
package/zuzuu/commands/eval.mjs
CHANGED
|
@@ -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
|
|
10
|
+
import { paths } from '../core/store.mjs';
|
|
11
11
|
import * as registry from '../faculty/registry.mjs';
|
|
12
|
-
import {
|
|
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 =
|
|
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
|
|
30
|
-
'(instructions/
|
|
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.
|
|
34
|
-
'guardrails/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
package/zuzuu/commands/hook.mjs
CHANGED
|
@@ -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/
|
|
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
|
|
131
|
-
* (or null = fail-open / no match → host's normal flow). Logs
|
|
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'
|
|
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) {
|
package/zuzuu/commands/inbox.mjs
CHANGED
|
@@ -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) {
|
package/zuzuu/commands/init.mjs
CHANGED
|
@@ -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 './
|
|
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
|
+
}
|