@xhulz/nina 0.35.0 → 0.36.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/nina.mjs +4 -1
- package/package.json +1 -1
- package/releases/0.36.0/core/GAPS.md +125 -0
- package/releases/0.36.0/core/answered.json +29 -0
- package/releases/0.36.0/core/defaults/tree/.claude/agents/architect.md +1 -0
- package/releases/0.36.0/core/defaults/tree/.claude/agents/dba.md +3 -0
- package/releases/0.36.0/core/defaults/tree/.claude/agents/devops.md +1 -0
- package/releases/0.36.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
- package/releases/0.36.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.36.0/core/defaults/tree/.claude/agents/qa.md +2 -0
- package/releases/0.36.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
- package/releases/0.36.0/core/defaults/tree/.claude/agents/secops.md +3 -0
- package/releases/0.36.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
- package/releases/0.36.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
- package/releases/0.36.0/core/tree/.claude/agents/architect.md +127 -0
- package/releases/0.36.0/core/tree/.claude/agents/dba.md +110 -0
- package/releases/0.36.0/core/tree/.claude/agents/devops.md +114 -0
- package/releases/0.36.0/core/tree/.claude/agents/implementer.md +156 -0
- package/releases/0.36.0/core/tree/.claude/agents/integration-tester.md +168 -0
- package/releases/0.36.0/core/tree/.claude/agents/planner.md +132 -0
- package/releases/0.36.0/core/tree/.claude/agents/qa.md +153 -0
- package/releases/0.36.0/core/tree/.claude/agents/reviewer.md +183 -0
- package/releases/0.36.0/core/tree/.claude/agents/secops.md +121 -0
- package/releases/0.36.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
- package/releases/0.36.0/core/tree/.claude/agents/solidity-dev.md +123 -0
- package/releases/0.36.0/core/tree/.claude/agents-overview.md +64 -0
- package/releases/0.36.0/core/tree/.claude/frontend.md +10 -0
- package/releases/0.36.0/core/tree/.claude/graph.md +75 -0
- package/releases/0.36.0/core/tree/.claude/patterns.md +213 -0
- package/releases/0.36.0/core/tree/.claude/pills/README.md +146 -0
- package/releases/0.36.0/core/tree/.claude/pipeline.md +101 -0
- package/releases/0.36.0/core/tree/.claude/retrieval.md +103 -0
- package/releases/0.36.0/core/tree/.claude/router.md +198 -0
- package/releases/0.36.0/core/tree/.claude/templates/integration.md +45 -0
- package/releases/0.36.0/core/tree/CLAUDE.md +161 -0
- package/releases/0.36.0/core/tree/scripts/cost-watch.mjs +18 -0
- package/releases/0.36.0/core/tree/scripts/edit-guard.mjs +16 -0
- package/releases/0.36.0/core/tree/scripts/harness-check.mjs +71 -0
- package/releases/0.36.0/core/tree/scripts/loop-gate.mjs +19 -0
- package/releases/0.36.0/core/vocabulary.json +13 -0
- package/releases/0.36.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
- package/releases/0.36.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
- package/releases/0.36.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
- package/releases/0.36.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
- package/releases/0.36.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
- package/releases/0.36.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
- package/releases/0.36.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
- package/releases/0.36.0/surfaces/blockchain/tree/.claude/router.md +6 -0
- package/releases/0.36.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
- package/releases/0.36.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
- package/releases/0.36.0/surfaces/db/tree/.claude/agents/dba.md +50 -0
- package/releases/0.36.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
- package/releases/0.36.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.36.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
- package/releases/0.36.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
- package/releases/0.36.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
- package/releases/0.36.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
- package/releases/0.36.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
- package/releases/0.36.0/surfaces/db/tree/.claude/graph.md +8 -0
- package/releases/0.36.0/surfaces/db/tree/.claude/patterns.md +19 -0
- package/releases/0.36.0/surfaces/db/tree/.claude/pipeline.md +13 -0
- package/releases/0.36.0/surfaces/db/tree/.claude/retrieval.md +5 -0
- package/releases/0.36.0/surfaces/db/tree/.claude/router.md +4 -0
- package/releases/0.36.0/surfaces/db/tree/CLAUDE.md +17 -0
- package/releases/0.36.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
- package/releases/0.36.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
- package/releases/0.36.0/surfaces/edge-cf/tree/.claude/agents/devops.md +32 -0
- package/releases/0.36.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
- package/releases/0.36.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
- package/releases/0.36.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
- package/releases/0.36.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.36.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
- package/releases/0.36.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
- package/releases/0.36.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
- package/releases/0.36.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
- package/releases/0.36.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
- package/releases/0.36.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
- package/releases/0.36.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
- package/releases/0.36.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
- package/releases/0.36.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
- package/releases/0.36.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
- package/releases/0.36.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
- package/releases/0.36.0/surfaces/frontend/tree/.claude/router.md +14 -0
- package/releases/0.36.0/surfaces/frontend/tree/CLAUDE.md +5 -0
- package/releases/0.36.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
- package/releases/0.36.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
- package/releases/0.36.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
- package/releases/0.36.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
- package/releases/0.36.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
- package/releases/0.36.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
- package/releases/0.36.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
- package/releases/0.36.0/surfaces/integrations/tree/.claude/graph.md +10 -0
- package/releases/0.36.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
- package/releases/0.36.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
- package/releases/0.36.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
- package/releases/0.36.0/surfaces/integrations/tree/.claude/router.md +6 -0
- package/releases/0.36.0/surfaces/integrations/tree/CLAUDE.md +17 -0
- package/releases/0.36.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
- package/releases/0.36.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
- package/releases/0.36.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
- package/releases/0.36.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.36.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
- package/releases/0.36.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
- package/releases/0.36.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
- package/releases/0.36.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
- package/releases/0.36.0/surfaces/money/tree/.claude/graph.md +2 -0
- package/releases/0.36.0/surfaces/money/tree/.claude/patterns.md +49 -0
- package/releases/0.36.0/surfaces/money/tree/.claude/pipeline.md +2 -0
- package/releases/0.36.0/surfaces/money/tree/.claude/retrieval.md +2 -0
- package/releases/0.36.0/surfaces/money/tree/CLAUDE.md +11 -0
- package/releases/0.36.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
- package/releases/0.36.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
- package/releases/0.36.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.36.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
- package/releases/0.36.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
- package/releases/0.36.0/surfaces/pii/tree/CLAUDE.md +5 -0
- package/releases/0.36.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
- package/releases/0.36.0/surfaces/prisma/tree/.claude/agents/dba.md +39 -0
- package/releases/0.36.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
- package/releases/0.36.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.36.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
- package/releases/0.36.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
- package/releases/0.36.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
- package/releases/0.36.0/surfaces/prisma/tree/CLAUDE.md +5 -0
- package/src/commands/export.mjs +8 -16
- package/src/commands/gate.mjs +19 -13
- package/src/commands/learn.mjs +5 -15
- package/src/commands/pipeline.mjs +4 -4
- package/src/commands/runs.mjs +14 -8
- package/src/commands/snapshot.mjs +63 -64
- package/src/commands/stats.mjs +46 -28
- package/src/commands/status.mjs +2 -2
- package/src/store.mjs +158 -0
- package/src/transcripts.mjs +15 -4
package/src/store.mjs
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The measurement store, read one way by every report.
|
|
3
|
+
*
|
|
4
|
+
* Five readers each parsed `~/.nina/snapshots/<project>.jsonl` their own way — `stats`, `runs`, `learn`,
|
|
5
|
+
* `export` and `status` — and chose the project four different ways, so two reports on the same history
|
|
6
|
+
* disagreed and neither said why. Every report reads it here now. Only `snapshot`, which rewrites the file,
|
|
7
|
+
* reads it raw.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync, readFileSync, readdirSync, realpathSync, statSync } from 'node:fs';
|
|
11
|
+
import { join, resolve } from 'node:path';
|
|
12
|
+
import { slugFor, snapshotsDir } from './paths.mjs';
|
|
13
|
+
import { ROLE_TOKENS } from './transcripts.mjs';
|
|
14
|
+
|
|
15
|
+
/** What a record says when no verdict was read from its report. */
|
|
16
|
+
const UNREAD = new Set(['UNCLEAR', 'NONE', null, undefined]);
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Records from a store file's text. A torn line loses one record, never the file.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} text - The file.
|
|
22
|
+
* @returns {object[]}
|
|
23
|
+
*/
|
|
24
|
+
export function parseStore(text) {
|
|
25
|
+
return String(text)
|
|
26
|
+
.split('\n')
|
|
27
|
+
.filter((l) => l.trim())
|
|
28
|
+
.flatMap((l) => {
|
|
29
|
+
try {
|
|
30
|
+
return [JSON.parse(l)];
|
|
31
|
+
} catch {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A record as the reports read it. A verdict outside the vocabulary of the role that gave it is not a
|
|
39
|
+
* verdict that role can give: records captured before the classifier knew the role read a dba's report
|
|
40
|
+
* as `PASS`, qa's token, and 21 of those made a gate that had stopped one change in fourteen look like a
|
|
41
|
+
* rubber stamp. Kept in the store as captured, and read as unreadable.
|
|
42
|
+
*
|
|
43
|
+
* @param {object} record - As captured.
|
|
44
|
+
* @returns {object}
|
|
45
|
+
*/
|
|
46
|
+
export function asRead(record) {
|
|
47
|
+
const tokens = ROLE_TOKENS[record?.role];
|
|
48
|
+
if (!tokens || UNREAD.has(record.verdict) || tokens.includes(record.verdict)) return record;
|
|
49
|
+
return { ...record, verdict: 'UNCLEAR', verdict_source: 'outside-role' };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Every record in one store file.
|
|
54
|
+
*
|
|
55
|
+
* @param {string} file - The file.
|
|
56
|
+
* @param {{raw?: boolean}} [options] - `raw`: as captured, for the one writer of the store.
|
|
57
|
+
* @returns {object[]} Empty when there is no such file.
|
|
58
|
+
*/
|
|
59
|
+
export function readStoreFile(file, { raw = false } = {}) {
|
|
60
|
+
let text;
|
|
61
|
+
try {
|
|
62
|
+
text = readFileSync(file, 'utf8');
|
|
63
|
+
} catch {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
const records = parseStore(text);
|
|
67
|
+
return raw ? records : records.map(asRead);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The store names a project's records may be under: its path as given, and with its links resolved, since
|
|
72
|
+
* either may be the one its sessions ran in.
|
|
73
|
+
*
|
|
74
|
+
* @param {string} dir - The project.
|
|
75
|
+
* @returns {string[]}
|
|
76
|
+
*/
|
|
77
|
+
export function storeNamesOf(dir) {
|
|
78
|
+
const names = new Set([slugFor(dir)]);
|
|
79
|
+
try {
|
|
80
|
+
names.add(slugFor(realpathSync(dir)));
|
|
81
|
+
} catch {
|
|
82
|
+
// A directory that cannot be resolved is looked up as given.
|
|
83
|
+
}
|
|
84
|
+
return [...names];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* A project's records.
|
|
89
|
+
*
|
|
90
|
+
* @param {string} dir - The project.
|
|
91
|
+
* @param {string} [store] - The store directory.
|
|
92
|
+
* @returns {object[]|null} Null when the store has nothing for it.
|
|
93
|
+
*/
|
|
94
|
+
export function projectRecords(dir, store = snapshotsDir()) {
|
|
95
|
+
for (const name of storeNamesOf(dir)) {
|
|
96
|
+
const file = join(store, `${name}.jsonl`);
|
|
97
|
+
if (existsSync(file)) return readStoreFile(file);
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The store names a `--project` value picks, one rule for every report. A directory is that project, by the
|
|
104
|
+
* name its sessions ran under; anything else is a store name, exactly, or else every name that contains it.
|
|
105
|
+
* `stats --project ../thing` read the directory as a piece of a name, found none, and said to run a snapshot.
|
|
106
|
+
*
|
|
107
|
+
* @param {string} value - What was given.
|
|
108
|
+
* @param {string} [store] - The store directory.
|
|
109
|
+
* @returns {string[]}
|
|
110
|
+
*/
|
|
111
|
+
export function namesFor(value, store = snapshotsDir()) {
|
|
112
|
+
const names = (() => {
|
|
113
|
+
try {
|
|
114
|
+
return readdirSync(store).filter((f) => f.endsWith('.jsonl')).map((f) => f.slice(0, -'.jsonl'.length));
|
|
115
|
+
} catch {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
})();
|
|
119
|
+
let isDir = false;
|
|
120
|
+
try {
|
|
121
|
+
isDir = statSync(resolve(value)).isDirectory();
|
|
122
|
+
} catch {
|
|
123
|
+
// Not a directory here: a name.
|
|
124
|
+
}
|
|
125
|
+
if (isDir) return storeNamesOf(resolve(value)).filter((n) => names.includes(n));
|
|
126
|
+
if (names.includes(value)) return [value];
|
|
127
|
+
return names.filter((n) => n.includes(value));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Two digits. */
|
|
131
|
+
const two = (n) => String(n).padStart(2, '0');
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The day a moment fell on where this machine is, as `YYYY-MM-DD`. The store keeps UTC; the reports read
|
|
135
|
+
* the owner's day. Cut from the UTC string, `--since` and the days a report printed moved a round from the
|
|
136
|
+
* evening it ran in to the next morning for anyone west of Greenwich — 20 of one project's 130.
|
|
137
|
+
*
|
|
138
|
+
* @param {unknown} ts - An ISO timestamp.
|
|
139
|
+
* @returns {string} The day, or the first ten characters of whatever could not be read as a moment.
|
|
140
|
+
*/
|
|
141
|
+
export function localDay(ts) {
|
|
142
|
+
const d = new Date(String(ts ?? ''));
|
|
143
|
+
return Number.isNaN(d.getTime()) ? String(ts ?? '').slice(0, 10) : `${d.getFullYear()}-${two(d.getMonth() + 1)}-${two(d.getDate())}`;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* A moment where this machine is, as `YYYY-MM-DD HH:MM`.
|
|
148
|
+
*
|
|
149
|
+
* @param {unknown} ts - An ISO timestamp.
|
|
150
|
+
* @returns {string}
|
|
151
|
+
*/
|
|
152
|
+
export function localMinute(ts) {
|
|
153
|
+
const d = new Date(String(ts ?? ''));
|
|
154
|
+
return Number.isNaN(d.getTime()) ? String(ts ?? '') : `${localDay(ts)} ${two(d.getHours())}:${two(d.getMinutes())}`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** The zone the reports read days in, named where a report prints a time. */
|
|
158
|
+
export const zone = () => Intl.DateTimeFormat().resolvedOptions().timeZone ?? 'local time';
|
package/src/transcripts.mjs
CHANGED
|
@@ -20,8 +20,15 @@ import { homedir } from 'node:os';
|
|
|
20
20
|
/** Root under which Claude Code stores one directory of transcripts per project. */
|
|
21
21
|
export const PROJECTS_ROOT = join(homedir(), '.claude', 'projects');
|
|
22
22
|
|
|
23
|
-
/**
|
|
24
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Where the transcripts are read from: Claude Code's own directory, or `NINA_TRANSCRIPTS`, which moves it for
|
|
25
|
+
* a test. Every reader asks this: listing the projects read Claude Code's own whatever the variable said, so a
|
|
26
|
+
* snapshot meant to stay in a sandbox captured the machine's real projects into it.
|
|
27
|
+
*/
|
|
28
|
+
const transcriptsRoot = () => process.env.NINA_TRANSCRIPTS ?? PROJECTS_ROOT;
|
|
29
|
+
|
|
30
|
+
/** The transcript directory of a project, by its snapshot name. */
|
|
31
|
+
export const transcriptsOf = (slug) => join(transcriptsRoot(), slug);
|
|
25
32
|
|
|
26
33
|
/**
|
|
27
34
|
* The verdict tokens the agent specs mandate on a report's first line, per role.
|
|
@@ -486,6 +493,9 @@ export async function scanProject(projectDir, prior = {}) {
|
|
|
486
493
|
model: block?.input?.model ?? row?.message?.model ?? null,
|
|
487
494
|
branch: row.gitBranch ?? null,
|
|
488
495
|
session: row.sessionId ?? null,
|
|
496
|
+
// The Claude Code that ran the dispatch: a change in how its transcripts are written first shows
|
|
497
|
+
// as a parser that stops finding something, and this says which version it stopped at.
|
|
498
|
+
claude_code: row.version ?? null,
|
|
489
499
|
verdict: null,
|
|
490
500
|
verdict_source: null,
|
|
491
501
|
issues: null,
|
|
@@ -757,6 +767,7 @@ async function attachAgentDetail(projectDir, dispatches) {
|
|
|
757
767
|
model: run.model,
|
|
758
768
|
branch: round.branch ?? run.branch,
|
|
759
769
|
session: run.session,
|
|
770
|
+
claude_code: run.claude_code ?? null,
|
|
760
771
|
verdict: null,
|
|
761
772
|
verdict_source: null,
|
|
762
773
|
issues: null,
|
|
@@ -884,10 +895,10 @@ export function tokensOf(usage) {
|
|
|
884
895
|
* @returns {Promise<{slug: string, dir: string, mtime: number}[]>} The projects found.
|
|
885
896
|
*/
|
|
886
897
|
export async function listProjects() {
|
|
887
|
-
const slugs = await readdir(
|
|
898
|
+
const slugs = await readdir(transcriptsRoot()).catch(() => []);
|
|
888
899
|
const out = [];
|
|
889
900
|
for (const slug of slugs) {
|
|
890
|
-
const dir =
|
|
901
|
+
const dir = transcriptsOf(slug);
|
|
891
902
|
const info = await stat(dir).catch(() => null);
|
|
892
903
|
if (info?.isDirectory()) out.push({ slug, dir, mtime: info.mtimeMs });
|
|
893
904
|
}
|