@zuzuucodes/cli 1.2.0 → 1.2.1

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 (27) hide show
  1. package/README.md +5 -6
  2. package/package.json +3 -5
  3. package/zuzuu/capture/bin/capture.mjs +99 -0
  4. package/zuzuu/capture/bin/inspect-trace.mjs +13 -0
  5. package/zuzuu/capture-core.mjs +3 -3
  6. package/zuzuu/commands/capture.mjs +1 -1
  7. package/zuzuu/commands/doctor.mjs +1 -1
  8. package/zuzuu/commands/hook.mjs +1 -1
  9. package/zuzuu/commands/init.mjs +1 -1
  10. package/zuzuu/commands/status.mjs +1 -1
  11. package/zuzuu/commands/trace.mjs +1 -1
  12. package/zuzuu/knowledge/distill.mjs +1 -1
  13. package/zuzuu/live/reconcile.mjs +1 -1
  14. package/zuzuu/store.mjs +1 -1
  15. /package/{experiments/experiment-1-trace-capture → zuzuu/capture}/adapters/claude-code.mjs +0 -0
  16. /package/{experiments/experiment-1-trace-capture → zuzuu/capture}/adapters/codex.mjs +0 -0
  17. /package/{experiments/experiment-1-trace-capture → zuzuu/capture}/adapters/gemini-cli.mjs +0 -0
  18. /package/{experiments/experiment-1-trace-capture → zuzuu/capture}/adapters/host-adapter.mjs +0 -0
  19. /package/{experiments/experiment-1-trace-capture → zuzuu/capture}/adapters/opencode.mjs +0 -0
  20. /package/{experiments/experiment-1-trace-capture → zuzuu/capture}/adapters/pi.mjs +0 -0
  21. /package/{experiments/experiment-1-trace-capture → zuzuu/capture}/adapters/registry.mjs +0 -0
  22. /package/{experiments/experiment-1-trace-capture → zuzuu/capture}/adapters/signals.mjs +0 -0
  23. /package/{experiments/experiment-1-trace-capture → zuzuu/capture}/core/event.mjs +0 -0
  24. /package/{experiments/experiment-1-trace-capture → zuzuu/capture}/core/ids.mjs +0 -0
  25. /package/{experiments/experiment-1-trace-capture → zuzuu/capture}/core/otlp.mjs +0 -0
  26. /package/{experiments/experiment-1-trace-capture → zuzuu/capture}/core/render.mjs +0 -0
  27. /package/{experiments/experiment-1-trace-capture → zuzuu/capture}/core/spans.mjs +0 -0
package/README.md CHANGED
@@ -40,7 +40,7 @@ zuzuu doctor # health + lost-session reconciliation
40
40
 
41
41
  ¹ **Codex is interactive-only** — `codex exec` (headless) fires no hooks (verified, v0.138.0), so live capture + gate work when you run Codex interactively; headless Codex still gets post-hoc `zuzuu capture`.
42
42
 
43
- All five verified against **real sessions** — never fixtures; every host's live capture + gate was wired from **real captured hook payloads** and dogfooded end-to-end ([`experiments/LOG.md`](experiments/LOG.md) exp-11 Gemini/Codex, exp-12 OpenCode/pi). Gate semantics are host-honest: deny hard-blocks everywhere; `ask` maps to a native prompt on Claude, defers to the host elsewhere.
43
+ All five verified against **real sessions** — never fixtures; every host's live capture + gate was wired from **real captured hook payloads** and dogfooded end-to-end ([`docs/LOG.md`](docs/LOG.md) exp-11 Gemini/Codex, exp-12 OpenCode/pi). Gate semantics are host-honest: deny hard-blocks everywhere; `ask` maps to a native prompt on Claude, defers to the host elsewhere.
44
44
 
45
45
  **Prerequisites:** Node ≥ 22 — that's it. You need at least one supported agent you've already used, so a session exists to capture. (Hacking on zuzuu itself? `git clone https://github.com/h1902y/zuzuu && cd zuzuu && npm link`.)
46
46
 
@@ -77,15 +77,14 @@ All five verified against **real sessions** — never fixtures; every host's liv
77
77
 
78
78
  | Path | What |
79
79
  |---|---|
80
- | [`zuzuu/`](zuzuu/) + `bin/zuzuu.mjs` | the CLI — capture, live lifecycle, faculty home (product surface) |
81
- | [`experiments/`](experiments/) | spike code + [`LOG.md`](experiments/LOG.md) the build journal (hypothesis real-data proof conclusions per experiment) |
82
- | [`app/`](app/) | the durable application skeleton (be / run / evolve) — proven code harvests here |
80
+ | [`zuzuu/`](zuzuu/) + `bin/zuzuu.mjs` | the CLI — capture pipeline (`zuzuu/capture/`), live lifecycle, faculty home (product surface) |
81
+ | [`web/`](web/) | the visual workbenchnested project (daemon + React SPA), ships bundled inside the npm package |
83
82
  | [`tests/`](tests/) | hermetic unit + regression (`npm test`) + real-data smoke playgrounds (`npm run playground`) |
84
- | [`docs/`](docs/) | [`DESIGN.md`](docs/DESIGN.md) (the canon) + [`inspiration/`](docs/inspiration/) (the research shelf: 100-project survey + 5 audits) |
83
+ | [`docs/`](docs/) | [`DESIGN.md`](docs/DESIGN.md) (the canon) + [`LOG.md`](docs/LOG.md) (the build journal) + [`inspiration/`](docs/inspiration/) (the research shelf) |
85
84
 
86
85
  ## How this is built (the method)
87
86
 
88
- **Experiment → prove on real data conclude harvest.** Every capability starts as a numbered experiment with a hypothesis; it must be verified against *real* sessions/wire data (never invented fixtures) before it counts; lessons land in the experiment’s Conclusions section; proven parts graduate into `app/`. Built in public — day-by-day on X ([@h1902y](https://x.com/h1902y)).
87
+ **Prove on real data, record in the journal.** Every capability is verified against *real* sessions/wire data (never invented fixtures) before it counts; the record lives in [`docs/LOG.md`](docs/LOG.md) (append-only). Live experimentation happens in disposable project directories outside the repo, keeping the codebase clean. Built in public — day-by-day on X ([@h1902y](https://x.com/h1902y)).
89
88
 
90
89
  ## License & status
91
90
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zuzuucodes/cli",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -42,15 +42,13 @@
42
42
  "bin/",
43
43
  "zuzuu/",
44
44
  "web-app/",
45
- "experiments/experiment-1-trace-capture/core/",
46
- "experiments/experiment-1-trace-capture/adapters/",
47
45
  "LICENSE",
48
46
  "README.md"
49
47
  ],
50
48
  "scripts": {
51
49
  "zuzuu": "node bin/zuzuu.mjs",
52
- "capture": "node experiments/experiment-1-trace-capture/bin/capture.mjs",
53
- "inspect": "node experiments/experiment-1-trace-capture/bin/inspect-trace.mjs",
50
+ "capture": "node zuzuu/capture/bin/capture.mjs",
51
+ "inspect": "node zuzuu/capture/bin/inspect-trace.mjs",
54
52
  "test": "node --test 'tests/**/*.test.mjs'",
55
53
  "playground": "node tests/playground/run.mjs",
56
54
  "prepublishOnly": "npm test",
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+ // capture — read a host's session log, normalize it, write an OTLP/JSON trace.
3
+ //
4
+ // node bin/capture.mjs [--host NAME] [--project STR] [--session ID] [--file PATH] [--out PATH]
5
+ // node bin/capture.mjs --list # show detected hosts + sessions
6
+ //
7
+ // With no --host, picks the first detected host (transcript-present). Pure file
8
+ // parsing — no host needs to be running.
9
+
10
+ import { fileURLToPath } from 'node:url';
11
+ import { dirname, join } from 'node:path';
12
+ import { ADAPTERS, byName, detected } from '../adapters/registry.mjs';
13
+ import { eventsToSpans } from '../core/spans.mjs';
14
+ import { toExportRequest, writeNdjson } from '../core/otlp.mjs';
15
+ import { EventKind } from '../core/event.mjs';
16
+
17
+ const HERE = dirname(fileURLToPath(import.meta.url));
18
+ const DEFAULT_OUT_DIR = join(HERE, '..', 'out');
19
+
20
+ function parseArgs(argv) {
21
+ const a = { _: [] };
22
+ for (let i = 0; i < argv.length; i++) {
23
+ const t = argv[i];
24
+ if (t === '--list') a.list = true;
25
+ else if (t === '-h' || t === '--help') a.help = true;
26
+ else if (t.startsWith('--')) a[t.slice(2)] = argv[++i];
27
+ else a._.push(t);
28
+ }
29
+ return a;
30
+ }
31
+
32
+ function printList() {
33
+ const hosts = detected();
34
+ if (!hosts.length) {
35
+ console.log('No hosts detected on this machine.');
36
+ return;
37
+ }
38
+ for (const adapter of hosts) {
39
+ const sessions = adapter.listSessions({ cwd: process.cwd() });
40
+ console.log(`\n● ${adapter.name} — ${sessions.length} session(s)`);
41
+ for (const s of sessions.slice(0, 8)) {
42
+ console.log(` ${s.sessionId} ${s.label ?? ''}`);
43
+ }
44
+ if (sessions.length > 8) console.log(` … and ${sessions.length - 8} more`);
45
+ }
46
+ console.log();
47
+ }
48
+
49
+ function chooseRef(adapter, args) {
50
+ if (args.file) return args.file; // explicit transcript path (claude-code)
51
+ const sessions = adapter.listSessions({ cwd: process.cwd(), project: args.project });
52
+ let pool = sessions;
53
+ if (args.project) pool = pool.filter((s) => (s.label ?? '').includes(args.project));
54
+ if (args.session) pool = pool.filter((s) => s.sessionId === args.session || s.sessionId.includes(args.session));
55
+ if (!pool.length) throw new Error(`no matching session for ${adapter.name} (sessions found: ${sessions.length})`);
56
+ return pool[0].ref;
57
+ }
58
+
59
+ function summarize(trace) {
60
+ const counts = {};
61
+ for (const e of trace.events) counts[e.kind] = (counts[e.kind] || 0) + 1;
62
+ return counts;
63
+ }
64
+
65
+ function main() {
66
+ const args = parseArgs(process.argv.slice(2));
67
+ if (args.help) {
68
+ console.log('usage: capture.mjs [--host NAME] [--project STR] [--session ID] [--file PATH] [--out PATH] | --list');
69
+ console.log('hosts:', ADAPTERS.map((a) => a.name).join(', '));
70
+ return;
71
+ }
72
+ if (args.list) return printList();
73
+
74
+ const adapter = args.host ? byName(args.host) : detected()[0];
75
+ if (!adapter) {
76
+ console.error(args.host ? `unknown host: ${args.host}` : 'no host detected (try --list)');
77
+ process.exit(1);
78
+ }
79
+
80
+ const ref = chooseRef(adapter, args);
81
+ const trace = adapter.parse(ref);
82
+ const { traceId, spans } = eventsToSpans(trace);
83
+ const request = toExportRequest({ traceId, spans }, { host: trace.host, sessionId: trace.sessionId });
84
+
85
+ const outPath = args.out || join(DEFAULT_OUT_DIR, `${trace.host}-${trace.sessionId}.otlp.jsonl`);
86
+ writeNdjson(outPath, [request]);
87
+
88
+ const counts = summarize(trace);
89
+ const root = trace.events.find((e) => e.kind === EventKind.SESSION) || trace.events[0];
90
+ const durSec = root ? ((root.endMs - root.startMs) / 1000).toFixed(1) : '?';
91
+ console.log(`captured ${trace.host} session ${trace.sessionId}`);
92
+ console.log(` trace_id : ${traceId}`);
93
+ console.log(` events : ${spans.length} (${Object.entries(counts).map(([k, v]) => `${k}:${v}`).join(', ')})`);
94
+ console.log(` duration : ${durSec}s`);
95
+ console.log(` written : ${outPath}`);
96
+ console.log(` inspect : node ${join('zuzuu', 'capture', 'bin', 'inspect-trace.mjs')} "${outPath}"`);
97
+ }
98
+
99
+ main();
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ // inspect-trace — pretty-print the span tree of an OTLP/JSON NDJSON trace file.
3
+ //
4
+ // node bin/inspect-trace.mjs out/claude-code-<id>.otlp.jsonl
5
+
6
+ import { loadSpans, renderTree } from '../core/render.mjs';
7
+
8
+ const file = process.argv[2];
9
+ if (!file) {
10
+ console.error('usage: inspect-trace.mjs <trace.otlp.jsonl>');
11
+ process.exit(1);
12
+ }
13
+ console.log(renderTree(loadSpans(file)));
@@ -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 '../experiments/experiment-1-trace-capture/core/spans.mjs';
7
- import { toExportRequest } from '../experiments/experiment-1-trace-capture/core/otlp.mjs';
8
- import { EventKind, Status } from '../experiments/experiment-1-trace-capture/core/event.mjs';
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
 
@@ -1,6 +1,6 @@
1
1
  // `zuzuu capture` — post-hoc: parse a host transcript into a git-native trace + record.
2
2
 
3
- import { ADAPTERS, byName, detected } from '../../experiments/experiment-1-trace-capture/adapters/registry.mjs';
3
+ import { ADAPTERS, byName, detected } from '../capture/adapters/registry.mjs';
4
4
  import { captureTrace } from '../capture-core.mjs';
5
5
  import { paths } from '../store.mjs';
6
6
 
@@ -2,7 +2,7 @@
2
2
  // real problems (warnings don't fail). Phase 2 will also reconcile lost sessions.
3
3
 
4
4
  import { mkdirSync, accessSync, constants } from 'node:fs';
5
- import { detected } from '../../experiments/experiment-1-trace-capture/adapters/registry.mjs';
5
+ import { detected } from '../capture/adapters/registry.mjs';
6
6
  import { paths, gitInfo } from '../store.mjs';
7
7
  import { listLive } from '../live/live-store.mjs';
8
8
  import { reconcile } from '../live/reconcile.mjs';
@@ -13,7 +13,7 @@
13
13
 
14
14
  import { readFileSync, writeFileSync, appendFileSync, mkdirSync } from 'node:fs';
15
15
  import { join, dirname } from 'node:path';
16
- import { byName } from '../../experiments/experiment-1-trace-capture/adapters/registry.mjs';
16
+ import { byName } from '../capture/adapters/registry.mjs';
17
17
  import { captureTrace } from '../capture-core.mjs';
18
18
  import { SessionState } from '../session.mjs';
19
19
  import { openLive, touchLive, closeLive } from '../live/live-store.mjs';
@@ -14,7 +14,7 @@ import { join, basename } from 'node:path';
14
14
  import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
15
15
  import { applyScaffold, ensureGitignore, homeExists } from '../scaffold.mjs';
16
16
  import { injectBlock, facultiesBlock, hasBlock, BLOCK_VERSION } from '../inject.mjs';
17
- import { detected } from '../../experiments/experiment-1-trace-capture/adapters/registry.mjs';
17
+ import { detected } from '../capture/adapters/registry.mjs';
18
18
  import { repoRoot } from '../store.mjs';
19
19
  import { migrateHome } from './migrate.mjs';
20
20
 
@@ -1,7 +1,7 @@
1
1
  // `zuzuu status` — detected hosts + recorded sessions (the git-native index).
2
2
 
3
3
  import { existsSync } from 'node:fs';
4
- import { detected } from '../../experiments/experiment-1-trace-capture/adapters/registry.mjs';
4
+ import { detected } from '../capture/adapters/registry.mjs';
5
5
  import { readIndex, paths } from '../store.mjs';
6
6
  import { FACULTIES } from '../faculty/contract.mjs';
7
7
  import { listProposals } from '../faculty/proposal.mjs';
@@ -1,7 +1,7 @@
1
1
  // `zuzuu trace [--last | <file>]` — print the span tree of a captured trace.
2
2
 
3
3
  import { existsSync } from 'node:fs';
4
- import { loadSpans, renderTree } from '../../experiments/experiment-1-trace-capture/core/render.mjs';
4
+ import { loadSpans, renderTree } from '../capture/core/render.mjs';
5
5
  import { lastTrace } from '../store.mjs';
6
6
 
7
7
  export function trace(args) {
@@ -12,7 +12,7 @@
12
12
  // failures — tools failing ≥3× → `fact` candidates (worth knowing!)
13
13
 
14
14
  import { readFileSync } from 'node:fs';
15
- import * as registry from '../../experiments/experiment-1-trace-capture/adapters/registry.mjs';
15
+ import * as registry from '../capture/adapters/registry.mjs';
16
16
  import { slugify } from './items.mjs';
17
17
  import { createProposal, fileRegistryProposals } from './proposals.mjs';
18
18
 
@@ -4,7 +4,7 @@
4
4
  // correct capture of the abandoned session before closing it. Nothing is lost.
5
5
 
6
6
  import { listLive, isStale, closeLive } from './live-store.mjs';
7
- import { byName } from '../../experiments/experiment-1-trace-capture/adapters/registry.mjs';
7
+ import { byName } from '../capture/adapters/registry.mjs';
8
8
  import { captureTrace } from '../capture-core.mjs';
9
9
  import { SessionState } from '../session.mjs';
10
10
 
package/zuzuu/store.mjs CHANGED
@@ -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 '../experiments/experiment-1-trace-capture/core/otlp.mjs';
14
+ import { writeNdjson } from './capture/core/otlp.mjs';
15
15
 
16
16
  const INDEX_VERSION = 1;
17
17