@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.
Files changed (134) hide show
  1. package/bin/nina.mjs +4 -1
  2. package/package.json +1 -1
  3. package/releases/0.36.0/core/GAPS.md +125 -0
  4. package/releases/0.36.0/core/answered.json +29 -0
  5. package/releases/0.36.0/core/defaults/tree/.claude/agents/architect.md +1 -0
  6. package/releases/0.36.0/core/defaults/tree/.claude/agents/dba.md +3 -0
  7. package/releases/0.36.0/core/defaults/tree/.claude/agents/devops.md +1 -0
  8. package/releases/0.36.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
  9. package/releases/0.36.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
  10. package/releases/0.36.0/core/defaults/tree/.claude/agents/qa.md +2 -0
  11. package/releases/0.36.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
  12. package/releases/0.36.0/core/defaults/tree/.claude/agents/secops.md +3 -0
  13. package/releases/0.36.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
  14. package/releases/0.36.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
  15. package/releases/0.36.0/core/tree/.claude/agents/architect.md +127 -0
  16. package/releases/0.36.0/core/tree/.claude/agents/dba.md +110 -0
  17. package/releases/0.36.0/core/tree/.claude/agents/devops.md +114 -0
  18. package/releases/0.36.0/core/tree/.claude/agents/implementer.md +156 -0
  19. package/releases/0.36.0/core/tree/.claude/agents/integration-tester.md +168 -0
  20. package/releases/0.36.0/core/tree/.claude/agents/planner.md +132 -0
  21. package/releases/0.36.0/core/tree/.claude/agents/qa.md +153 -0
  22. package/releases/0.36.0/core/tree/.claude/agents/reviewer.md +183 -0
  23. package/releases/0.36.0/core/tree/.claude/agents/secops.md +121 -0
  24. package/releases/0.36.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
  25. package/releases/0.36.0/core/tree/.claude/agents/solidity-dev.md +123 -0
  26. package/releases/0.36.0/core/tree/.claude/agents-overview.md +64 -0
  27. package/releases/0.36.0/core/tree/.claude/frontend.md +10 -0
  28. package/releases/0.36.0/core/tree/.claude/graph.md +75 -0
  29. package/releases/0.36.0/core/tree/.claude/patterns.md +213 -0
  30. package/releases/0.36.0/core/tree/.claude/pills/README.md +146 -0
  31. package/releases/0.36.0/core/tree/.claude/pipeline.md +101 -0
  32. package/releases/0.36.0/core/tree/.claude/retrieval.md +103 -0
  33. package/releases/0.36.0/core/tree/.claude/router.md +198 -0
  34. package/releases/0.36.0/core/tree/.claude/templates/integration.md +45 -0
  35. package/releases/0.36.0/core/tree/CLAUDE.md +161 -0
  36. package/releases/0.36.0/core/tree/scripts/cost-watch.mjs +18 -0
  37. package/releases/0.36.0/core/tree/scripts/edit-guard.mjs +16 -0
  38. package/releases/0.36.0/core/tree/scripts/harness-check.mjs +71 -0
  39. package/releases/0.36.0/core/tree/scripts/loop-gate.mjs +19 -0
  40. package/releases/0.36.0/core/vocabulary.json +13 -0
  41. package/releases/0.36.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
  42. package/releases/0.36.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
  43. package/releases/0.36.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
  44. package/releases/0.36.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
  45. package/releases/0.36.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
  46. package/releases/0.36.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
  47. package/releases/0.36.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
  48. package/releases/0.36.0/surfaces/blockchain/tree/.claude/router.md +6 -0
  49. package/releases/0.36.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
  50. package/releases/0.36.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
  51. package/releases/0.36.0/surfaces/db/tree/.claude/agents/dba.md +50 -0
  52. package/releases/0.36.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
  53. package/releases/0.36.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
  54. package/releases/0.36.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
  55. package/releases/0.36.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
  56. package/releases/0.36.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
  57. package/releases/0.36.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
  58. package/releases/0.36.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
  59. package/releases/0.36.0/surfaces/db/tree/.claude/graph.md +8 -0
  60. package/releases/0.36.0/surfaces/db/tree/.claude/patterns.md +19 -0
  61. package/releases/0.36.0/surfaces/db/tree/.claude/pipeline.md +13 -0
  62. package/releases/0.36.0/surfaces/db/tree/.claude/retrieval.md +5 -0
  63. package/releases/0.36.0/surfaces/db/tree/.claude/router.md +4 -0
  64. package/releases/0.36.0/surfaces/db/tree/CLAUDE.md +17 -0
  65. package/releases/0.36.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
  66. package/releases/0.36.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
  67. package/releases/0.36.0/surfaces/edge-cf/tree/.claude/agents/devops.md +32 -0
  68. package/releases/0.36.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
  69. package/releases/0.36.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
  70. package/releases/0.36.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
  71. package/releases/0.36.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
  72. package/releases/0.36.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
  73. package/releases/0.36.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
  74. package/releases/0.36.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
  75. package/releases/0.36.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
  76. package/releases/0.36.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
  77. package/releases/0.36.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
  78. package/releases/0.36.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
  79. package/releases/0.36.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
  80. package/releases/0.36.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
  81. package/releases/0.36.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
  82. package/releases/0.36.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
  83. package/releases/0.36.0/surfaces/frontend/tree/.claude/router.md +14 -0
  84. package/releases/0.36.0/surfaces/frontend/tree/CLAUDE.md +5 -0
  85. package/releases/0.36.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
  86. package/releases/0.36.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
  87. package/releases/0.36.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
  88. package/releases/0.36.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
  89. package/releases/0.36.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
  90. package/releases/0.36.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
  91. package/releases/0.36.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
  92. package/releases/0.36.0/surfaces/integrations/tree/.claude/graph.md +10 -0
  93. package/releases/0.36.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
  94. package/releases/0.36.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
  95. package/releases/0.36.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
  96. package/releases/0.36.0/surfaces/integrations/tree/.claude/router.md +6 -0
  97. package/releases/0.36.0/surfaces/integrations/tree/CLAUDE.md +17 -0
  98. package/releases/0.36.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
  99. package/releases/0.36.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
  100. package/releases/0.36.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
  101. package/releases/0.36.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
  102. package/releases/0.36.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
  103. package/releases/0.36.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
  104. package/releases/0.36.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
  105. package/releases/0.36.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
  106. package/releases/0.36.0/surfaces/money/tree/.claude/graph.md +2 -0
  107. package/releases/0.36.0/surfaces/money/tree/.claude/patterns.md +49 -0
  108. package/releases/0.36.0/surfaces/money/tree/.claude/pipeline.md +2 -0
  109. package/releases/0.36.0/surfaces/money/tree/.claude/retrieval.md +2 -0
  110. package/releases/0.36.0/surfaces/money/tree/CLAUDE.md +11 -0
  111. package/releases/0.36.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
  112. package/releases/0.36.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
  113. package/releases/0.36.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
  114. package/releases/0.36.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
  115. package/releases/0.36.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
  116. package/releases/0.36.0/surfaces/pii/tree/CLAUDE.md +5 -0
  117. package/releases/0.36.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
  118. package/releases/0.36.0/surfaces/prisma/tree/.claude/agents/dba.md +39 -0
  119. package/releases/0.36.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
  120. package/releases/0.36.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
  121. package/releases/0.36.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
  122. package/releases/0.36.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
  123. package/releases/0.36.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
  124. package/releases/0.36.0/surfaces/prisma/tree/CLAUDE.md +5 -0
  125. package/src/commands/export.mjs +8 -16
  126. package/src/commands/gate.mjs +19 -13
  127. package/src/commands/learn.mjs +5 -15
  128. package/src/commands/pipeline.mjs +4 -4
  129. package/src/commands/runs.mjs +14 -8
  130. package/src/commands/snapshot.mjs +63 -64
  131. package/src/commands/stats.mjs +46 -28
  132. package/src/commands/status.mjs +2 -2
  133. package/src/store.mjs +158 -0
  134. 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';
@@ -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
- /** The transcript directory of a project, by its snapshot name; `NINA_TRANSCRIPTS` moves the root, for tests. */
24
- export const transcriptsOf = (slug) => join(process.env.NINA_TRANSCRIPTS ?? PROJECTS_ROOT, slug);
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(PROJECTS_ROOT).catch(() => []);
898
+ const slugs = await readdir(transcriptsRoot()).catch(() => []);
888
899
  const out = [];
889
900
  for (const slug of slugs) {
890
- const dir = join(PROJECTS_ROOT, slug);
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
  }