@zuzuucodes/cli 1.5.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 +12 -3
- 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} +46 -116
- 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-Dx3ub8Pk.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-DM6oHc7c.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-DflaUwqW.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-wbBeb0oN.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-DRwkDcoK.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/trail.mjs +1 -1
- package/zuzuu/commands/act.mjs +1 -1
- 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 +39 -4
- package/zuzuu/commands/enable.mjs +1 -1
- package/zuzuu/commands/eval.mjs +3 -36
- package/zuzuu/commands/faculty.mjs +102 -19
- package/zuzuu/commands/generation.mjs +3 -4
- package/zuzuu/commands/hook.mjs +7 -7
- package/zuzuu/commands/inbox.mjs +1 -6
- package/zuzuu/commands/init.mjs +5 -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/{migrate.mjs → migrations/items.mjs} +34 -246
- 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/{memory/adapter.mjs → faculties/memory/index.mjs} +37 -9
- package/zuzuu/faculty/generation/read.mjs +206 -0
- package/zuzuu/faculty/generation/write.mjs +207 -0
- package/zuzuu/faculty/items.mjs +11 -5
- 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.mjs → guardrails/engine.mjs} +1 -1
- package/zuzuu/{scaffold.mjs → home/scaffold.mjs} +12 -2
- 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-BpGp1akx.js +0 -1
- package/web-app/web-dist/assets/MonacoFile-CqbVacUZ.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-C6R6xoyX.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-DHpC851f.js +0 -268
- package/web-app/web-dist/assets/index-O-t1gyMG.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 -122
- package/zuzuu/digest.mjs +0 -154
- package/zuzuu/faculty/generation.mjs +0 -398
- package/zuzuu/guardrails/adapter.mjs +0 -103
- package/zuzuu/instructions/adapter.mjs +0 -93
- package/zuzuu/knowledge/adapter.mjs +0 -99
- package/zuzuu/miners/actions.mjs +0 -112
- package/zuzuu/miners/guardrails.mjs +0 -176
- 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
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// `zuzuu proposals` — the human gate, non-interactive: list|show|approve|reject.
|
|
2
|
+
// The same adapter-driven path `zuzuu review` walks, minus the ceremony. Pure
|
|
3
|
+
// data fns (proposalsListData/approveData/rejectData) feed the --json surface
|
|
4
|
+
// and the web workbench.
|
|
5
|
+
|
|
6
|
+
import { paths } from '../core/store.mjs';
|
|
7
|
+
import { processInbox } from '../knowledge/inbox.mjs';
|
|
8
|
+
import { getProposal } from '../knowledge/proposals.mjs';
|
|
9
|
+
import * as registry from '../faculty/registry.mjs';
|
|
10
|
+
import * as gate from '../faculty/gate.mjs';
|
|
11
|
+
import { pendingByFaculty } from '../faculty/pending.mjs';
|
|
12
|
+
import { knowledgeLine, proposalTitle } from '../faculty/render.mjs';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Resolve which faculty owns a given proposal id (used when --faculty is omitted).
|
|
16
|
+
* Defaults to 'knowledge' (the historical path) when no other faculty claims it.
|
|
17
|
+
*/
|
|
18
|
+
function facultyOf(agentDir, id, only) {
|
|
19
|
+
if (only) return only;
|
|
20
|
+
for (const { adapter, proposals } of pendingByFaculty(agentDir)) {
|
|
21
|
+
if (proposals.some((p) => p.id === id)) return adapter.name;
|
|
22
|
+
}
|
|
23
|
+
return 'knowledge';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Pure: the structured object for `proposals approve --json`.
|
|
28
|
+
* Calls gate.approve and returns the result object the branch prints.
|
|
29
|
+
* @param {string} agentDir
|
|
30
|
+
* @param {string} id
|
|
31
|
+
* @param {string} faculty
|
|
32
|
+
* @returns {object} the gate result (contains ok, action, etc.)
|
|
33
|
+
*/
|
|
34
|
+
export function approveData(agentDir, id, faculty) {
|
|
35
|
+
return gate.approve(agentDir, faculty, id);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Pure: the structured object for `proposals reject --json`.
|
|
40
|
+
* Calls gate.reject and returns the result object the branch prints.
|
|
41
|
+
* @param {string} agentDir
|
|
42
|
+
* @param {string} id
|
|
43
|
+
* @param {string} faculty
|
|
44
|
+
* @param {string} [reason]
|
|
45
|
+
* @returns {object} { ok, id, ... }
|
|
46
|
+
*/
|
|
47
|
+
export function rejectData(agentDir, id, faculty, reason = '') {
|
|
48
|
+
const r = gate.reject(agentDir, faculty, id, reason);
|
|
49
|
+
return { ...r, id };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Pure: list pending proposals as structured data — the zuzuu-web /proposals source.
|
|
54
|
+
* @param {string} agentDir
|
|
55
|
+
* @param {string} [only] optional faculty filter
|
|
56
|
+
* @returns {{ pending: Array<{id, faculty, title}> }}
|
|
57
|
+
*/
|
|
58
|
+
export function proposalsListData(agentDir, only) {
|
|
59
|
+
const groups = pendingByFaculty(agentDir).filter((g) => !only || g.adapter.name === only);
|
|
60
|
+
const pending = [];
|
|
61
|
+
for (const { adapter, proposals } of groups) {
|
|
62
|
+
for (const p of proposals) {
|
|
63
|
+
pending.push({ id: p.id, faculty: adapter.name, title: proposalTitle(adapter, p) });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return { pending };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Non-interactive: zuzuu proposals list|show <id>|approve <id>|reject <id> [--reason r] [--faculty f] */
|
|
70
|
+
export function proposals(args) {
|
|
71
|
+
const agentDir = paths().dir;
|
|
72
|
+
const sub = args._[0] || 'list';
|
|
73
|
+
const only = args.faculty; // optional filter; default = all
|
|
74
|
+
if (sub === 'list') {
|
|
75
|
+
if (args.json) {
|
|
76
|
+
processInbox(agentDir); // promote plain-text inbox candidates, same as text path
|
|
77
|
+
const d = proposalsListData(agentDir, only);
|
|
78
|
+
console.log(JSON.stringify(d));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const inbox = processInbox(agentDir);
|
|
82
|
+
if (inbox.processed) console.log(`(processed ${inbox.processed} inbox candidate(s))`);
|
|
83
|
+
const groups = pendingByFaculty(agentDir).filter((g) => !only || g.adapter.name === only);
|
|
84
|
+
const any = groups.some((g) => g.proposals.length);
|
|
85
|
+
if (!any) return console.log('no pending proposals');
|
|
86
|
+
for (const { adapter, proposals } of groups) {
|
|
87
|
+
for (const p of proposals) {
|
|
88
|
+
// knowledge keeps its historical one-liner; other faculties use adapter.render
|
|
89
|
+
if (adapter.name === 'knowledge') {
|
|
90
|
+
console.log(knowledgeLine(p));
|
|
91
|
+
} else {
|
|
92
|
+
console.log(` ${adapter.render(p).line}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const id = args._[1];
|
|
99
|
+
if (sub === 'show') {
|
|
100
|
+
const faculty = facultyOf(agentDir, id, only);
|
|
101
|
+
const a = registry.get(faculty);
|
|
102
|
+
const p = (a && typeof a.getProposal === 'function') ? a.getProposal(agentDir, id) : getProposal(agentDir, id);
|
|
103
|
+
if (!p) return console.error('not found');
|
|
104
|
+
// show always prints JSON (both with and without --json flag)
|
|
105
|
+
console.log(JSON.stringify(p, null, 2));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (sub === 'approve') {
|
|
109
|
+
const faculty = facultyOf(agentDir, id, only);
|
|
110
|
+
const r = approveData(agentDir, id, faculty);
|
|
111
|
+
if (args.json) {
|
|
112
|
+
console.log(JSON.stringify(r));
|
|
113
|
+
} else {
|
|
114
|
+
console.log(r.ok ? `✓ ${r.action}` : `✗ ${(r.errors ?? [r.action]).join('; ')}`);
|
|
115
|
+
for (const w of r.warnings ?? []) console.log(`⚠ ${w}`);
|
|
116
|
+
}
|
|
117
|
+
process.exit(r.ok ? 0 : 1);
|
|
118
|
+
}
|
|
119
|
+
if (sub === 'reject') {
|
|
120
|
+
const faculty = facultyOf(agentDir, id, only);
|
|
121
|
+
const r = rejectData(agentDir, id, faculty, args.reason || '');
|
|
122
|
+
if (args.json) {
|
|
123
|
+
console.log(JSON.stringify(r));
|
|
124
|
+
} else {
|
|
125
|
+
console.log(r.ok ? '✓ rejected' : '✗ not found');
|
|
126
|
+
}
|
|
127
|
+
process.exit(r.ok ? 0 : 1);
|
|
128
|
+
}
|
|
129
|
+
console.error('usage: zuzuu proposals list|show <id>|approve <id>|reject <id> [--reason r] [--faculty f]');
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
@@ -1,105 +1,22 @@
|
|
|
1
|
-
// `zuzuu review` — the human gate, as a daily ritual
|
|
2
|
-
// one-by-one: shows the candidate, its evidence, and
|
|
3
|
-
// matched item when enrich/duplicate) → y approve ·
|
|
4
|
-
// q quit. Works piped (answers on stdin) — that's
|
|
5
|
-
//
|
|
1
|
+
// `zuzuu review` — the human gate, as a daily ritual (the interactive ceremony).
|
|
2
|
+
// Walks pending proposals one-by-one: shows the candidate, its evidence, and
|
|
3
|
+
// the ER verdict (with the matched item when enrich/duplicate) → y approve ·
|
|
4
|
+
// n reject · e edit · s skip · q quit. Works piped (answers on stdin) — that's
|
|
5
|
+
// also how it's tested. The non-interactive surface lives in proposals.mjs.
|
|
6
6
|
|
|
7
7
|
import { spawnSync } from 'node:child_process';
|
|
8
8
|
import { join } from 'node:path';
|
|
9
9
|
import { createInterface } from 'node:readline';
|
|
10
|
-
import { paths
|
|
10
|
+
import { paths } from '../core/store.mjs';
|
|
11
11
|
import { processInbox } from '../knowledge/inbox.mjs';
|
|
12
12
|
import { getProposal, proposalsDir } from '../knowledge/proposals.mjs';
|
|
13
|
-
import { readItem } from '../knowledge/items.mjs';
|
|
14
|
-
import * as registry from '../faculty/registry.mjs';
|
|
15
13
|
import * as gate from '../faculty/gate.mjs';
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
14
|
+
import { pendingByFaculty, buildSessionMtimes } from '../faculty/pending.mjs';
|
|
15
|
+
import { knowledgeCard } from '../faculty/render.mjs';
|
|
16
|
+
import { activeGeneration } from '../faculty/generation/read.mjs';
|
|
17
|
+
import { mintGeneration } from '../faculty/generation/write.mjs';
|
|
18
|
+
import { getScorer } from '../eval/score.mjs';
|
|
20
19
|
import { evalLine } from './eval.mjs';
|
|
21
|
-
import '../knowledge/adapter.mjs'; // self-registers the 'knowledge' adapter
|
|
22
|
-
import '../actions/adapter.mjs'; // self-registers the 'actions' adapter
|
|
23
|
-
import '../guardrails/adapter.mjs'; // self-registers the 'guardrails' adapter
|
|
24
|
-
import '../instructions/adapter.mjs'; // self-registers the 'instructions' adapter
|
|
25
|
-
import '../memory/adapter.mjs'; // self-registers the 'memory' adapter
|
|
26
|
-
|
|
27
|
-
/** Build sessionMtimes map from the sessions index — best-effort, fail-open. */
|
|
28
|
-
function buildSessionMtimes() {
|
|
29
|
-
try {
|
|
30
|
-
const idx = readIndex();
|
|
31
|
-
const map = {};
|
|
32
|
-
for (const s of idx.sessions ?? []) {
|
|
33
|
-
if (!s.id) continue;
|
|
34
|
-
const ms = s.startedAt ? Date.parse(s.startedAt) : 0;
|
|
35
|
-
if (!isNaN(ms) && ms > 0) map[s.id] = ms;
|
|
36
|
-
}
|
|
37
|
-
return map;
|
|
38
|
-
} catch {
|
|
39
|
-
return {};
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Review walks faculties in a fixed order so piped sessions are deterministic
|
|
44
|
-
// (the combo smoke test feeds one stdin across the actions pass then knowledge).
|
|
45
|
-
const REVIEW_ORDER = ['actions', 'knowledge', 'guardrails', 'instructions', 'memory'];
|
|
46
|
-
|
|
47
|
-
/** Ordered list of adapters that have pending proposals to review. */
|
|
48
|
-
function pendingByFaculty(agentDir) {
|
|
49
|
-
const adapters = registry.all();
|
|
50
|
-
const seen = new Set();
|
|
51
|
-
const ordered = [];
|
|
52
|
-
for (const name of REVIEW_ORDER) {
|
|
53
|
-
const a = adapters.find((x) => x.name === name);
|
|
54
|
-
if (a) { ordered.push(a); seen.add(name); }
|
|
55
|
-
}
|
|
56
|
-
for (const a of adapters) if (!seen.has(a.name)) ordered.push(a);
|
|
57
|
-
const sessionMtimes = buildSessionMtimes();
|
|
58
|
-
const now = Date.now();
|
|
59
|
-
const scorer = getScorer();
|
|
60
|
-
const out = [];
|
|
61
|
-
for (const a of ordered) {
|
|
62
|
-
let proposals = facultyPending(agentDir, a);
|
|
63
|
-
if (!proposals.length) continue;
|
|
64
|
-
// Rank proposals highest-score-first (display only — never changes approval/mint).
|
|
65
|
-
const ranked = rank(proposals, scorer, { now, sessionMtimes });
|
|
66
|
-
proposals = ranked.map((r) => r.proposal);
|
|
67
|
-
out.push({ adapter: a, proposals });
|
|
68
|
-
}
|
|
69
|
-
return out;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** Pending proposals for one adapter (dir-shaped adapters override listProposals). */
|
|
73
|
-
function facultyPending(agentDir, a) {
|
|
74
|
-
if (typeof a.listProposals === 'function') return a.listProposals(agentDir);
|
|
75
|
-
// JSON-record faculties: read via the spine (records carry both the spine shape
|
|
76
|
-
// and the legacy candidate/er keys the knowledge card renders from).
|
|
77
|
-
return spineListProposals(agentDir, a.name);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function card(agentDir, p, i, total, scoreResult) {
|
|
81
|
-
const lines = [];
|
|
82
|
-
lines.push(`\n━━ proposal ${i + 1}/${total} ── ${p.id} ── ${p.kind} ── source: ${p.source ?? '-'} ━━`);
|
|
83
|
-
if (p.kind === 'registry') {
|
|
84
|
-
lines.push(` register ${p.registry.slice(0, -1)}: '${p.key}' (seen ${p.evidence?.occurrences}× in candidates)`);
|
|
85
|
-
} else {
|
|
86
|
-
const c = p.candidate;
|
|
87
|
-
lines.push(` ${c.type}: ${c.body?.slice(0, 100).replace(/\n/g, ' ')}`);
|
|
88
|
-
for (const [k, v] of Object.entries(c.attributes ?? {})) lines.push(` · ${k} = ${v}`);
|
|
89
|
-
for (const r of c.relations ?? []) lines.push(` → ${r.type} ${r.target}`);
|
|
90
|
-
const ev = p.evidence ?? {};
|
|
91
|
-
if (Object.keys(ev).length) lines.push(` evidence: ${Object.entries(ev).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(' ')}`);
|
|
92
|
-
const er = p.er ?? {};
|
|
93
|
-
lines.push(` er: ${er.verdict}${er.match ? ` → ${er.match}` : ''} (${(er.confidence ?? 0).toFixed(2)} · ${er.reason ?? ''})`);
|
|
94
|
-
if (er.match) {
|
|
95
|
-
const m = readItem(agentDir, er.match);
|
|
96
|
-
if (m) lines.push(` existing: ${m.body.slice(0, 80).replace(/\n/g, ' ')}`);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
// Eval line — always shown; scoreResult computed by caller from ranked array.
|
|
100
|
-
if (scoreResult) lines.push(` ${evalLine(scoreResult)}`);
|
|
101
|
-
return lines.join('\n');
|
|
102
|
-
}
|
|
103
20
|
|
|
104
21
|
/**
|
|
105
22
|
* Pure: the graduation ceremony block shown when a generation is minted.
|
|
@@ -177,7 +94,7 @@ export async function review() {
|
|
|
177
94
|
try { scoreResult = scorer(p, { now, sessionMtimes }); } catch { /* fail-open */ }
|
|
178
95
|
// Card: knowledge keeps its rich card (ER + existing-item lookup); other
|
|
179
96
|
// faculties render through the adapter contract.
|
|
180
|
-
if (adapter.name === 'knowledge') console.log(
|
|
97
|
+
if (adapter.name === 'knowledge') console.log(knowledgeCard(agentDir, p, i, proposals.length, scoreResult));
|
|
181
98
|
else {
|
|
182
99
|
const r = adapter.render(p);
|
|
183
100
|
const [head, ...rest] = r.card.split('\n');
|
|
@@ -210,7 +127,7 @@ export async function review() {
|
|
|
210
127
|
proposals[i] = fresh;
|
|
211
128
|
let freshScore = null;
|
|
212
129
|
try { freshScore = scorer(fresh, { now, sessionMtimes }); } catch { /* fail-open */ }
|
|
213
|
-
console.log(
|
|
130
|
+
console.log(knowledgeCard(agentDir, fresh, i, proposals.length, freshScore));
|
|
214
131
|
}
|
|
215
132
|
} else if (a === 's') {
|
|
216
133
|
skipped++; totalLeft--; acted = true;
|
|
@@ -233,134 +150,3 @@ export async function review() {
|
|
|
233
150
|
console.log(ceremonyBlock(gen.id, approvedIds, approvedByFaculty));
|
|
234
151
|
}
|
|
235
152
|
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Resolve which faculty owns a given proposal id (used when --faculty is omitted).
|
|
239
|
-
* Defaults to 'knowledge' (the historical path) when no other faculty claims it.
|
|
240
|
-
*/
|
|
241
|
-
function facultyOf(agentDir, id, only) {
|
|
242
|
-
if (only) return only;
|
|
243
|
-
for (const { adapter, proposals } of pendingByFaculty(agentDir)) {
|
|
244
|
-
if (proposals.some((p) => p.id === id)) return adapter.name;
|
|
245
|
-
}
|
|
246
|
-
return 'knowledge';
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Pure: the structured object for `proposals approve --json`.
|
|
251
|
-
* Calls gate.approve and returns the result object the branch prints.
|
|
252
|
-
* @param {string} agentDir
|
|
253
|
-
* @param {string} id
|
|
254
|
-
* @param {string} faculty
|
|
255
|
-
* @returns {object} the gate result (contains ok, action, etc.)
|
|
256
|
-
*/
|
|
257
|
-
export function approveData(agentDir, id, faculty) {
|
|
258
|
-
return gate.approve(agentDir, faculty, id);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Pure: the structured object for `proposals reject --json`.
|
|
263
|
-
* Calls gate.reject and returns the result object the branch prints.
|
|
264
|
-
* @param {string} agentDir
|
|
265
|
-
* @param {string} id
|
|
266
|
-
* @param {string} faculty
|
|
267
|
-
* @param {string} [reason]
|
|
268
|
-
* @returns {object} { ok, id, ... }
|
|
269
|
-
*/
|
|
270
|
-
export function rejectData(agentDir, id, faculty, reason = '') {
|
|
271
|
-
const r = gate.reject(agentDir, faculty, id, reason);
|
|
272
|
-
return { ...r, id };
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Pure: list pending proposals as structured data — the zuzuu-web /proposals source.
|
|
277
|
-
* @param {string} agentDir
|
|
278
|
-
* @param {string} [only] optional faculty filter
|
|
279
|
-
* @returns {{ pending: Array<{id, faculty, title}> }}
|
|
280
|
-
*/
|
|
281
|
-
export function proposalsListData(agentDir, only) {
|
|
282
|
-
const groups = pendingByFaculty(agentDir).filter((g) => !only || g.adapter.name === only);
|
|
283
|
-
const pending = [];
|
|
284
|
-
for (const { adapter, proposals } of groups) {
|
|
285
|
-
for (const p of proposals) {
|
|
286
|
-
// derive a human title the same way the table does
|
|
287
|
-
let title;
|
|
288
|
-
if (adapter.name === 'knowledge') {
|
|
289
|
-
title = p.kind === 'registry'
|
|
290
|
-
? `register ${p.registry?.slice(0, -1) ?? ''} '${p.key ?? ''}'`
|
|
291
|
-
: (p.candidate?.body ?? p.payload?.body ?? p.id)?.slice(0, 80);
|
|
292
|
-
} else {
|
|
293
|
-
title = p.title ?? adapter.render(p).line;
|
|
294
|
-
}
|
|
295
|
-
pending.push({ id: p.id, faculty: adapter.name, title: title ?? p.id });
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
return { pending };
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/** Non-interactive: zuzuu proposals list|show <id>|approve <id>|reject <id> [--reason r] [--faculty f] */
|
|
302
|
-
export function proposals(args) {
|
|
303
|
-
const agentDir = paths().dir;
|
|
304
|
-
const sub = args._[0] || 'list';
|
|
305
|
-
const only = args.faculty; // optional filter; default = all
|
|
306
|
-
if (sub === 'list') {
|
|
307
|
-
if (args.json) {
|
|
308
|
-
processInbox(agentDir); // promote plain-text inbox candidates, same as text path
|
|
309
|
-
const d = proposalsListData(agentDir, only);
|
|
310
|
-
console.log(JSON.stringify(d));
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
const inbox = processInbox(agentDir);
|
|
314
|
-
if (inbox.processed) console.log(`(processed ${inbox.processed} inbox candidate(s))`);
|
|
315
|
-
const groups = pendingByFaculty(agentDir).filter((g) => !only || g.adapter.name === only);
|
|
316
|
-
const any = groups.some((g) => g.proposals.length);
|
|
317
|
-
if (!any) return console.log('no pending proposals');
|
|
318
|
-
for (const { adapter, proposals } of groups) {
|
|
319
|
-
for (const p of proposals) {
|
|
320
|
-
// knowledge keeps its historical one-liner; other faculties use adapter.render
|
|
321
|
-
if (adapter.name === 'knowledge') {
|
|
322
|
-
const what = p.kind === 'registry'
|
|
323
|
-
? `register ${p.registry.slice(0, -1)} '${p.key}'`
|
|
324
|
-
: `${p.candidate.type}: ${p.candidate.body?.slice(0, 60).replace(/\n/g, ' ')}`;
|
|
325
|
-
console.log(` ${p.id} [${p.er?.verdict ?? p.kind}] ${what}`);
|
|
326
|
-
} else {
|
|
327
|
-
console.log(` ${adapter.render(p).line}`);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
return;
|
|
332
|
-
}
|
|
333
|
-
const id = args._[1];
|
|
334
|
-
if (sub === 'show') {
|
|
335
|
-
const faculty = facultyOf(agentDir, id, only);
|
|
336
|
-
const a = registry.get(faculty);
|
|
337
|
-
const p = (a && typeof a.getProposal === 'function') ? a.getProposal(agentDir, id) : getProposal(agentDir, id);
|
|
338
|
-
if (!p) return console.error('not found');
|
|
339
|
-
// show always prints JSON (both with and without --json flag)
|
|
340
|
-
console.log(JSON.stringify(p, null, 2));
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
if (sub === 'approve') {
|
|
344
|
-
const faculty = facultyOf(agentDir, id, only);
|
|
345
|
-
const r = approveData(agentDir, id, faculty);
|
|
346
|
-
if (args.json) {
|
|
347
|
-
console.log(JSON.stringify(r));
|
|
348
|
-
} else {
|
|
349
|
-
console.log(r.ok ? `✓ ${r.action}` : `✗ ${(r.errors ?? [r.action]).join('; ')}`);
|
|
350
|
-
for (const w of r.warnings ?? []) console.log(`⚠ ${w}`);
|
|
351
|
-
}
|
|
352
|
-
process.exit(r.ok ? 0 : 1);
|
|
353
|
-
}
|
|
354
|
-
if (sub === 'reject') {
|
|
355
|
-
const faculty = facultyOf(agentDir, id, only);
|
|
356
|
-
const r = rejectData(agentDir, id, faculty, args.reason || '');
|
|
357
|
-
if (args.json) {
|
|
358
|
-
console.log(JSON.stringify(r));
|
|
359
|
-
} else {
|
|
360
|
-
console.log(r.ok ? '✓ rejected' : '✗ not found');
|
|
361
|
-
}
|
|
362
|
-
process.exit(r.ok ? 0 : 1);
|
|
363
|
-
}
|
|
364
|
-
console.error('usage: zuzuu proposals list|show <id>|approve <id>|reject <id> [--reason r] [--faculty f]');
|
|
365
|
-
process.exit(1);
|
|
366
|
-
}
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
// All git mutation lives in session-git.mjs (fail-soft, never throws); this is
|
|
9
9
|
// the thin print layer (xxxData pattern — pure data fns + --json everywhere).
|
|
10
10
|
|
|
11
|
-
import { sessionStatus, closeSession, continueSession, discardSession } from '../session-git.mjs';
|
|
11
|
+
import { sessionStatus, closeSession, continueSession, discardSession } from '../sessions/session-git.mjs';
|
|
12
|
+
import { sessionInspect } from './sessions.mjs';
|
|
12
13
|
|
|
13
14
|
/** Pure: structured session-git state (the leftover detector included). */
|
|
14
15
|
export function sessionStatusData(cwd = process.cwd()) {
|
|
@@ -98,6 +99,11 @@ export function session(args = {}) {
|
|
|
98
99
|
process.exit(1);
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
|
|
102
|
+
if (sub === 'inspect') {
|
|
103
|
+
sessionInspect(args);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
console.error(`unknown: zuzuu session ${sub}\nusage: zuzuu session [status|merge [--title t]|continue|discard --yes|inspect <id>]`);
|
|
102
108
|
process.exit(1);
|
|
103
109
|
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// zuzuu/commands/sessions.mjs — the sessions observability surface (overhaul
|
|
2
|
+
// Part A, 2026-06-13).
|
|
3
|
+
//
|
|
4
|
+
// zuzuu sessions [--json] recorded sessions w/ state labels
|
|
5
|
+
// zuzuu session inspect <id> [--json] one session: trace summary +
|
|
6
|
+
// per-faculty mined signals
|
|
7
|
+
//
|
|
8
|
+
// inspect = { session, trace: {spans, tools, duration}, signals: {<faculty>:
|
|
9
|
+
// counts} } — the span count reads the stored OTLP blob; the signals re-mine
|
|
10
|
+
// the HOST transcript through the proven adapters' mineSignals and map the
|
|
11
|
+
// superset onto faculties via each module's sessionSignals hook. Fail-soft
|
|
12
|
+
// throughout: a gone blob/transcript degrades to warnings, never a throw.
|
|
13
|
+
|
|
14
|
+
import { readFileSync } from 'node:fs';
|
|
15
|
+
import { readIndex, resolveTrace, paths } from '../core/store.mjs';
|
|
16
|
+
import { transcriptsFor, mineHostSession } from '../knowledge/distill.mjs';
|
|
17
|
+
import { facultiesOf, invoke } from '../faculty/registry.mjs';
|
|
18
|
+
|
|
19
|
+
/** Pure: the sessions list with state labels — the web Sessions section source. */
|
|
20
|
+
export function sessionsListData(cwd = process.cwd()) {
|
|
21
|
+
const { sessions } = readIndex(cwd);
|
|
22
|
+
return {
|
|
23
|
+
sessions: sessions.map((s) => ({
|
|
24
|
+
id: s.id,
|
|
25
|
+
host: s.host,
|
|
26
|
+
state: s.status, // active | completed | abandoned | crashed | captured | opening
|
|
27
|
+
startedAt: s.startedAt ?? null,
|
|
28
|
+
endedAt: s.endedAt ?? null,
|
|
29
|
+
durationMs: s.durationMs ?? 0,
|
|
30
|
+
counts: s.counts ?? { turns: 0, tools: 0, errors: 0 },
|
|
31
|
+
generation: s.generation ?? null,
|
|
32
|
+
git: s.git ?? { commit: null, branch: null },
|
|
33
|
+
})),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Count spans across an OTLP/JSON NDJSON blob (one export request per line). */
|
|
38
|
+
function countTraceSpans(file) {
|
|
39
|
+
let spans = 0;
|
|
40
|
+
for (const line of readFileSync(file, 'utf8').split('\n')) {
|
|
41
|
+
if (!line.trim()) continue;
|
|
42
|
+
const req = JSON.parse(line);
|
|
43
|
+
for (const rs of req.resourceSpans ?? []) {
|
|
44
|
+
for (const ss of rs.scopeSpans ?? []) spans += (ss.spans ?? []).length;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return spans;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Pure-ish: one session's observability document, or null for an unknown id.
|
|
52
|
+
* Accepts a unique id prefix (the table shows 8-char ids).
|
|
53
|
+
* @param {string} cwd
|
|
54
|
+
* @param {string} idArg
|
|
55
|
+
* @param {{transcripts?: Array<{host,ref,sessionId}>}} [opts] injectable for hermetic tests
|
|
56
|
+
* @returns {{session, trace:{spans,tools,duration}, signals:object, warnings:string[]} | null}
|
|
57
|
+
*/
|
|
58
|
+
export function sessionInspectData(cwd, idArg, { transcripts } = {}) {
|
|
59
|
+
if (!idArg) return null;
|
|
60
|
+
const { sessions } = readIndex(cwd);
|
|
61
|
+
const matches = sessions.filter((s) => s.id === idArg);
|
|
62
|
+
const byPrefix = matches.length ? matches : sessions.filter((s) => String(s.id).startsWith(idArg));
|
|
63
|
+
if (!byPrefix.length) return null;
|
|
64
|
+
const s = byPrefix[0];
|
|
65
|
+
const warnings = [];
|
|
66
|
+
|
|
67
|
+
// trace summary — span count from the stored OTLP blob (fail-soft)
|
|
68
|
+
let spans = null;
|
|
69
|
+
try {
|
|
70
|
+
spans = countTraceSpans(resolveTrace(s.traceRef, cwd));
|
|
71
|
+
} catch {
|
|
72
|
+
warnings.push('trace blob unavailable — span count unknown');
|
|
73
|
+
}
|
|
74
|
+
const trace = { spans, tools: s.counts?.tools ?? 0, duration: s.durationMs ?? 0 };
|
|
75
|
+
|
|
76
|
+
// per-faculty mined signals — re-mine the host transcript (fail-soft when gone)
|
|
77
|
+
const signals = {};
|
|
78
|
+
try {
|
|
79
|
+
const pairs = transcripts ?? transcriptsFor({ scope: 'all', cwd });
|
|
80
|
+
const pair = pairs.find((p) => p.host === s.host && String(p.sessionId) === String(s.id));
|
|
81
|
+
const mined = pair ? mineHostSession(pair) : null;
|
|
82
|
+
if (!mined) {
|
|
83
|
+
warnings.push('host transcript unavailable — signals empty');
|
|
84
|
+
} else {
|
|
85
|
+
const agentDir = paths(cwd).dir;
|
|
86
|
+
for (const entry of facultiesOf(agentDir)) {
|
|
87
|
+
const r = invoke(entry, 'sessionSignals', mined);
|
|
88
|
+
if (r.ok && r.value && typeof r.value === 'object') signals[entry.id] = r.value;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
warnings.push('signal mining failed — signals empty');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
session: {
|
|
97
|
+
id: s.id,
|
|
98
|
+
host: s.host,
|
|
99
|
+
state: s.status,
|
|
100
|
+
startedAt: s.startedAt ?? null,
|
|
101
|
+
endedAt: s.endedAt ?? null,
|
|
102
|
+
durationMs: s.durationMs ?? 0,
|
|
103
|
+
counts: s.counts ?? { turns: 0, tools: 0, errors: 0 },
|
|
104
|
+
generation: s.generation ?? null,
|
|
105
|
+
git: s.git ?? { commit: null, branch: null },
|
|
106
|
+
traceRef: s.traceRef ?? null,
|
|
107
|
+
},
|
|
108
|
+
trace,
|
|
109
|
+
signals,
|
|
110
|
+
warnings,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const fmtDur = (ms) => (ms < 60_000 ? `${(ms / 1000).toFixed(0)}s` : `${(ms / 60_000).toFixed(1)}m`);
|
|
115
|
+
|
|
116
|
+
/** `zuzuu sessions [--json]` — the recorded-sessions list with state labels. */
|
|
117
|
+
export function sessions(args = {}) {
|
|
118
|
+
const cwd = process.cwd();
|
|
119
|
+
const d = sessionsListData(cwd);
|
|
120
|
+
if (args.json) { console.log(JSON.stringify(d)); return; }
|
|
121
|
+
if (!d.sessions.length) {
|
|
122
|
+
console.log('no recorded sessions yet — run `zuzuu capture`, or just start your agent (live capture)');
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
console.log(' STATE HOST DUR T/TOOLS/ERR STARTED SESSION');
|
|
126
|
+
for (const s of d.sessions) {
|
|
127
|
+
const dur = fmtDur(s.durationMs || 0).padStart(6);
|
|
128
|
+
const cnt = `${s.counts.turns}/${s.counts.tools}/${s.counts.errors}`.padEnd(11);
|
|
129
|
+
const started = (s.startedAt ?? '').slice(0, 19).padEnd(20);
|
|
130
|
+
console.log(` ${s.state.padEnd(10)} ${s.host.padEnd(13)} ${dur} ${cnt} ${started} ${String(s.id).slice(0, 8)}`);
|
|
131
|
+
}
|
|
132
|
+
console.log(`\n${d.sessions.length} session(s) — inspect one: zuzuu session inspect <id>`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** `zuzuu session inspect <id> [--json]` — print one session's document. */
|
|
136
|
+
export function sessionInspect(args = {}) {
|
|
137
|
+
const cwd = process.cwd();
|
|
138
|
+
const id = args._?.[1];
|
|
139
|
+
const d = sessionInspectData(cwd, id);
|
|
140
|
+
if (!d) {
|
|
141
|
+
console.error(id ? `no recorded session matching '${id}'` : 'usage: zuzuu session inspect <id> [--json]');
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
if (args.json) { console.log(JSON.stringify(d)); return; }
|
|
145
|
+
const s = d.session;
|
|
146
|
+
console.log(`${s.id} — ${s.host} · ${s.state}`);
|
|
147
|
+
console.log(` started: ${s.startedAt ?? '?'} ended: ${s.endedAt ?? '?'} dur: ${fmtDur(s.durationMs || 0)}`);
|
|
148
|
+
console.log(` git: ${s.git?.commit ? s.git.commit.slice(0, 8) : '-'} (${s.git?.branch ?? '-'}) generation: ${s.generation ?? '-'}`);
|
|
149
|
+
console.log(` trace: ${d.trace.spans ?? '?'} span(s) · ${d.trace.tools} tool(s) · ${fmtDur(d.trace.duration || 0)}`);
|
|
150
|
+
const sigs = Object.entries(d.signals);
|
|
151
|
+
if (sigs.length) {
|
|
152
|
+
console.log(' signals:');
|
|
153
|
+
for (const [faculty, counts] of sigs) {
|
|
154
|
+
const parts = Object.entries(counts).map(([k, v]) => `${k} ${v}`).join(' · ');
|
|
155
|
+
console.log(` ${faculty.padEnd(13)} ${parts}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
for (const w of d.warnings) console.log(` ⚠ ${w}`);
|
|
159
|
+
}
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
import { existsSync } from 'node:fs';
|
|
4
4
|
import { dirname } from 'node:path';
|
|
5
5
|
import { detected } from '../capture/adapters/registry.mjs';
|
|
6
|
-
import { sessionStatus } from '../session-git.mjs';
|
|
7
|
-
import { readIndex, paths } from '../store.mjs';
|
|
6
|
+
import { sessionStatus } from '../sessions/session-git.mjs';
|
|
7
|
+
import { readIndex, paths } from '../core/store.mjs';
|
|
8
8
|
import { FACULTIES } from '../faculty/contract.mjs';
|
|
9
9
|
import { listProposals } from '../faculty/proposal.mjs';
|
|
10
|
-
import { activeGeneration as activeGenerationFn } from '../faculty/generation.mjs';
|
|
10
|
+
import { activeGeneration as activeGenerationFn } from '../faculty/generation/read.mjs';
|
|
11
11
|
import { detectDrift } from './doctor.mjs';
|
|
12
12
|
|
|
13
13
|
const fmtDur = (ms) => (ms < 60_000 ? `${(ms / 1000).toFixed(0)}s` : `${(ms / 60_000).toFixed(1)}m`);
|
package/zuzuu/commands/trace.mjs
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
// (`hook`/`reconcile`, statuses active/completed/abandoned). One proven path —
|
|
4
4
|
// Design B: the hook never builds spans, it re-runs THIS.
|
|
5
5
|
|
|
6
|
-
import { eventsToSpans } from '
|
|
7
|
-
import { toExportRequest } from '
|
|
8
|
-
import { EventKind, Status } from '
|
|
6
|
+
import { eventsToSpans } from '../capture/core/spans.mjs';
|
|
7
|
+
import { toExportRequest } from '../capture/core/otlp.mjs';
|
|
8
|
+
import { EventKind, Status } from '../capture/core/event.mjs';
|
|
9
9
|
import { makeSession, SessionState } from './session.mjs';
|
|
10
10
|
import { writeTrace, upsertSession, gitInfo } from './store.mjs';
|
|
11
11
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { join, relative, resolve, isAbsolute } from 'node:path';
|
|
12
12
|
import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, writeFileSync, renameSync } from 'node:fs';
|
|
13
13
|
import { spawnSync } from 'node:child_process';
|
|
14
|
-
import { writeNdjson } from '
|
|
14
|
+
import { writeNdjson } from '../capture/core/otlp.mjs';
|
|
15
15
|
|
|
16
16
|
const INDEX_VERSION = 1;
|
|
17
17
|
|