@xhulz/nina 0.52.0 → 0.53.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/README.md +4 -0
- package/bin/nina.mjs +29 -5
- package/package.json +1 -1
- package/releases/0.53.0/core/GAPS.md +125 -0
- package/releases/0.53.0/core/answered.json +68 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/architect.md +1 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/dba.md +3 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/devops.md +1 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/qa.md +7 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/secops.md +3 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
- package/releases/0.53.0/core/tree/.claude/agents/architect.md +129 -0
- package/releases/0.53.0/core/tree/.claude/agents/dba.md +117 -0
- package/releases/0.53.0/core/tree/.claude/agents/devops.md +118 -0
- package/releases/0.53.0/core/tree/.claude/agents/implementer.md +163 -0
- package/releases/0.53.0/core/tree/.claude/agents/integration-tester.md +168 -0
- package/releases/0.53.0/core/tree/.claude/agents/planner.md +132 -0
- package/releases/0.53.0/core/tree/.claude/agents/qa.md +150 -0
- package/releases/0.53.0/core/tree/.claude/agents/reviewer.md +184 -0
- package/releases/0.53.0/core/tree/.claude/agents/secops.md +120 -0
- package/releases/0.53.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
- package/releases/0.53.0/core/tree/.claude/agents/solidity-dev.md +123 -0
- package/releases/0.53.0/core/tree/.claude/agents-overview.md +64 -0
- package/releases/0.53.0/core/tree/.claude/frontend.md +10 -0
- package/releases/0.53.0/core/tree/.claude/graph.md +75 -0
- package/releases/0.53.0/core/tree/.claude/patterns.md +251 -0
- package/releases/0.53.0/core/tree/.claude/pills/README.md +150 -0
- package/releases/0.53.0/core/tree/.claude/pipeline.md +101 -0
- package/releases/0.53.0/core/tree/.claude/retrieval.md +103 -0
- package/releases/0.53.0/core/tree/.claude/router.md +202 -0
- package/releases/0.53.0/core/tree/.claude/templates/integration.md +46 -0
- package/releases/0.53.0/core/tree/CLAUDE.md +164 -0
- package/releases/0.53.0/core/tree/scripts/cost-watch.mjs +25 -0
- package/releases/0.53.0/core/tree/scripts/edit-guard.mjs +24 -0
- package/releases/0.53.0/core/tree/scripts/harness-check.mjs +71 -0
- package/releases/0.53.0/core/tree/scripts/loop-gate.mjs +19 -0
- package/releases/0.53.0/core/vocabulary.json +13 -0
- package/releases/0.53.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
- package/releases/0.53.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
- package/releases/0.53.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
- package/releases/0.53.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
- package/releases/0.53.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
- package/releases/0.53.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
- package/releases/0.53.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
- package/releases/0.53.0/surfaces/blockchain/tree/.claude/router.md +6 -0
- package/releases/0.53.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
- package/releases/0.53.0/surfaces/d1/tree/.claude/agents/dba.md +15 -0
- package/releases/0.53.0/surfaces/d1/tree/.claude/agents/devops.md +2 -0
- package/releases/0.53.0/surfaces/d1/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.53.0/surfaces/d1/tree/.claude/patterns.md +8 -0
- package/releases/0.53.0/surfaces/d1/tree/CLAUDE.md +2 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/agents/dba.md +54 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/graph.md +8 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/patterns.md +22 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/pipeline.md +13 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/retrieval.md +5 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/router.md +4 -0
- package/releases/0.53.0/surfaces/db/tree/CLAUDE.md +17 -0
- package/releases/0.53.0/surfaces/drizzle/tree/.claude/agents/dba.md +9 -0
- package/releases/0.53.0/surfaces/drizzle/tree/.claude/agents/devops.md +5 -0
- package/releases/0.53.0/surfaces/drizzle/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.53.0/surfaces/drizzle/tree/.claude/patterns.md +7 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/agents/devops.md +29 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
- package/releases/0.53.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
- package/releases/0.53.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
- package/releases/0.53.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
- package/releases/0.53.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
- package/releases/0.53.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
- package/releases/0.53.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
- package/releases/0.53.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
- package/releases/0.53.0/surfaces/frontend/tree/.claude/router.md +14 -0
- package/releases/0.53.0/surfaces/frontend/tree/CLAUDE.md +5 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/graph.md +10 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/router.md +6 -0
- package/releases/0.53.0/surfaces/integrations/tree/CLAUDE.md +17 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/graph.md +2 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/patterns.md +49 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/pipeline.md +2 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/retrieval.md +2 -0
- package/releases/0.53.0/surfaces/money/tree/CLAUDE.md +11 -0
- package/releases/0.53.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
- package/releases/0.53.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
- package/releases/0.53.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.53.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
- package/releases/0.53.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
- package/releases/0.53.0/surfaces/pii/tree/CLAUDE.md +5 -0
- package/releases/0.53.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
- package/releases/0.53.0/surfaces/prisma/tree/.claude/agents/dba.md +42 -0
- package/releases/0.53.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
- package/releases/0.53.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.53.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
- package/releases/0.53.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
- package/releases/0.53.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
- package/releases/0.53.0/surfaces/prisma/tree/CLAUDE.md +5 -0
- package/src/commands/dashboard.mjs +33 -0
- package/src/commands/learn.mjs +51 -19
- package/src/commands/pills.mjs +55 -28
- package/src/commands/pipeline.mjs +62 -33
- package/src/commands/runs.mjs +14 -3
- package/src/commands/stats.mjs +199 -107
- package/src/dashboard.html +328 -0
- package/src/dashboard.mjs +185 -0
- package/src/office.mjs +48 -83
- package/src/serve.mjs +108 -0
package/src/commands/pills.mjs
CHANGED
|
@@ -336,43 +336,24 @@ export function validate(pill, text, roles, today) {
|
|
|
336
336
|
}
|
|
337
337
|
|
|
338
338
|
/**
|
|
339
|
-
* `nina pills
|
|
339
|
+
* A project's pills, checked: what `nina pills` prints and the dashboard's pills tab shows — each pill's stage, status,
|
|
340
|
+
* count and trigger, and the problems and notes over the whole corpus. Null when the project has composed no agents.
|
|
340
341
|
*
|
|
341
|
-
* @param {string
|
|
342
|
-
* @param {{root
|
|
343
|
-
* @returns {Promise<number>} Process exit code.
|
|
342
|
+
* @param {string} target - The project.
|
|
343
|
+
* @param {{root?: string}} [ctx] - CLI context: where the layers are, to say where a recurring lesson belongs.
|
|
344
344
|
*/
|
|
345
|
-
export async function
|
|
346
|
-
const target = resolve(argv.includes('--project') ? argv[argv.indexOf('--project') + 1] : '.');
|
|
347
|
-
const quiet = argv.includes('--quiet');
|
|
345
|
+
export async function pillsData(target, ctx) {
|
|
348
346
|
const pillsDir = join(target, '.claude', 'pills');
|
|
349
347
|
const agentsDir = join(target, '.claude', 'agents');
|
|
350
|
-
|
|
351
|
-
if (!existsSync(agentsDir)) {
|
|
352
|
-
console.error(` no .claude/agents under ${target} — run \`nina compose\` first.\n`);
|
|
353
|
-
return 1;
|
|
354
|
-
}
|
|
348
|
+
if (!existsSync(agentsDir)) return null;
|
|
355
349
|
const roles = (await readdir(agentsDir))
|
|
356
350
|
.filter((f) => f.endsWith('.md'))
|
|
357
351
|
.map((f) => f.replace(/\.md$/, ''))
|
|
358
352
|
.sort();
|
|
359
|
-
|
|
360
353
|
const today = new Date().toISOString().slice(0, 10);
|
|
361
|
-
if (argv.includes('--tidy')) {
|
|
362
|
-
const moved = await tidyRetired(target);
|
|
363
|
-
for (const m of moved) console.log(` moved ${m} → ${RETIRED}/${m}`);
|
|
364
|
-
console.log(moved.length ? `\npills: ${moved.length} retired pill(s) moved where no agent opens them\n` : 'pills: no retired pill where agents read\n');
|
|
365
|
-
return 0;
|
|
366
|
-
}
|
|
367
354
|
const files = existsSync(pillsDir) ? await pillFiles(pillsDir) : [];
|
|
368
355
|
|
|
369
|
-
|
|
370
|
-
// normal state of a new project and the suspicious state of an old one — say which, and
|
|
371
|
-
// leave the judgement to whoever knows how long the pipeline has been running.
|
|
372
|
-
if (files.length === 0) {
|
|
373
|
-
if (!quiet) console.log(` no pill written yet under ${target} — either nothing has been learned the expensive way, or nothing was written down.\n`);
|
|
374
|
-
return 0;
|
|
375
|
-
}
|
|
356
|
+
if (files.length === 0) return { files: 0, active: 0, retired: 0, problems: [], notes: [], pills: [] };
|
|
376
357
|
const problems = [];
|
|
377
358
|
const notes = [];
|
|
378
359
|
const active = [];
|
|
@@ -404,8 +385,21 @@ export async function pills(argv, ctx) {
|
|
|
404
385
|
let resolved = 0;
|
|
405
386
|
let cited = 0;
|
|
406
387
|
|
|
388
|
+
const each = [];
|
|
407
389
|
for (const pill of files) {
|
|
408
390
|
const result = validate(pill, await readFile(pill.path, 'utf8'), roles, today);
|
|
391
|
+
const f = result.fields ?? {};
|
|
392
|
+
each.push({
|
|
393
|
+
label: result.label,
|
|
394
|
+
status: f.status ?? null,
|
|
395
|
+
roles: list(f.applies_to),
|
|
396
|
+
occurrences: Number(f.occurrences ?? 1) || 1,
|
|
397
|
+
date: f.date ?? null,
|
|
398
|
+
last_seen: f.last_seen ?? null,
|
|
399
|
+
severity: f.severity ?? null,
|
|
400
|
+
trigger: f.trigger ?? null,
|
|
401
|
+
problems: result.problems.length,
|
|
402
|
+
});
|
|
409
403
|
problems.push(...result.problems);
|
|
410
404
|
notes.push(...result.notes);
|
|
411
405
|
if (pill.dir && pill.dir !== 'shared' && pill.dir !== RETIRED) taught.add(pill.dir);
|
|
@@ -478,14 +472,47 @@ export async function pills(argv, ctx) {
|
|
|
478
472
|
const dated = active.map((p) => p.date).filter(Boolean).sort();
|
|
479
473
|
if (dated.length > 0) notes.push(`oldest active pill is ${dated[0]}, newest ${dated[dated.length - 1]}`);
|
|
480
474
|
|
|
475
|
+
return { files: files.length, active: active.length, retired, problems, notes, pills: each };
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* `nina pills`.
|
|
480
|
+
*
|
|
481
|
+
* @param {string[]} argv - Command arguments; `--project <dir>` selects the target.
|
|
482
|
+
* @param {{root: string}} [ctx] - CLI context; `root` is the NINA install directory.
|
|
483
|
+
* @returns {Promise<number>} Process exit code.
|
|
484
|
+
*/
|
|
485
|
+
export async function pills(argv, ctx) {
|
|
486
|
+
const target = resolve(argv.includes('--project') ? argv[argv.indexOf('--project') + 1] : '.');
|
|
487
|
+
const quiet = argv.includes('--quiet');
|
|
488
|
+
const agentsDir = join(target, '.claude', 'agents');
|
|
489
|
+
|
|
490
|
+
if (!existsSync(agentsDir)) {
|
|
491
|
+
console.error(` no .claude/agents under ${target} — run \`nina compose\` first.\n`);
|
|
492
|
+
return 1;
|
|
493
|
+
}
|
|
494
|
+
if (argv.includes('--tidy')) {
|
|
495
|
+
const moved = await tidyRetired(target);
|
|
496
|
+
for (const m of moved) console.log(` moved ${m} → ${RETIRED}/${m}`);
|
|
497
|
+
console.log(moved.length ? `\npills: ${moved.length} retired pill(s) moved where no agent opens them\n` : 'pills: no retired pill where agents read\n');
|
|
498
|
+
return 0;
|
|
499
|
+
}
|
|
500
|
+
const { files, active, retired, problems, notes } = await pillsData(target, ctx);
|
|
501
|
+
// The directory itself is composed, so its presence says nothing. An empty corpus is the
|
|
502
|
+
// normal state of a new project and the suspicious state of an old one — say which, and
|
|
503
|
+
// leave the judgement to whoever knows how long the pipeline has been running.
|
|
504
|
+
if (files === 0) {
|
|
505
|
+
if (!quiet) console.log(` no pill written yet under ${target} — either nothing has been learned the expensive way, or nothing was written down.\n`);
|
|
506
|
+
return 0;
|
|
507
|
+
}
|
|
481
508
|
if (!(quiet && problems.length === 0)) {
|
|
482
|
-
console.log(` ${files
|
|
509
|
+
console.log(` ${files} pill(s): ${active} active, ${retired} retired\n`);
|
|
483
510
|
for (const p of problems) console.log(` ✗ ${p}`);
|
|
484
511
|
for (const n of notes) console.log(` · ${n}`);
|
|
485
512
|
if (problems.length === 0 && notes.length === 0) console.log(' ✓ every pill is well formed, placed where it is read, and carries evidence');
|
|
486
513
|
console.log(
|
|
487
514
|
problems.length === 0
|
|
488
|
-
? `\npills: all ${files
|
|
515
|
+
? `\npills: all ${files} well formed${notes.length > 0 ? ` (${notes.length} note(s))` : ''}\n`
|
|
489
516
|
: `\npills: ${problems.length} problem(s)\n`,
|
|
490
517
|
);
|
|
491
518
|
}
|
|
@@ -165,6 +165,54 @@ export function historyOf(records, since) {
|
|
|
165
165
|
return byRole;
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
/**
|
|
169
|
+
* A composed pipeline, as data: what `nina pipeline` draws and the dashboard's pipeline tab shows — the line and where
|
|
170
|
+
* it ends, the gates, the stages that run as several agents, who sends work back to whom and how many rounds per
|
|
171
|
+
* issue, and each stage's model, effort, skills and what it ran since `since`; the project's own agents; and which
|
|
172
|
+
* chain each task shape takes. Null when the project has no composed graph.
|
|
173
|
+
*
|
|
174
|
+
* @param {string} dir - The project.
|
|
175
|
+
* @param {{since?: string}} [options] - The first day of the history each stage is measured over.
|
|
176
|
+
*/
|
|
177
|
+
export function pipelineData(dir, { since } = {}) {
|
|
178
|
+
const graphPath = join(dir, '.claude', 'graph.md');
|
|
179
|
+
if (!existsSync(graphPath)) return null;
|
|
180
|
+
const graphText = readFileSync(graphPath, 'utf8');
|
|
181
|
+
const graph = parseGraph(graphText);
|
|
182
|
+
const words = stageWords(graphText);
|
|
183
|
+
const shape = shapeOf(graph);
|
|
184
|
+
const claude = join(dir, 'CLAUDE.md');
|
|
185
|
+
const profile = readProfile(dir).profile ?? {};
|
|
186
|
+
const agentsDir = join(dir, '.claude', 'agents');
|
|
187
|
+
const specs = new Map();
|
|
188
|
+
for (const file of existsSync(agentsDir) ? readdirSync(agentsDir).filter((f) => f.endsWith('.md')).sort() : []) {
|
|
189
|
+
specs.set(file.replace(/\.md$/, ''), readFileSync(join(agentsDir, file), 'utf8'));
|
|
190
|
+
}
|
|
191
|
+
const from = since || localDay(new Date(Date.now() - HISTORY_DAYS * 86_400_000).toISOString());
|
|
192
|
+
const records = projectRecords(dir);
|
|
193
|
+
const history = records ? historyOf(records, from) : null;
|
|
194
|
+
return {
|
|
195
|
+
project: basename(dir),
|
|
196
|
+
core: profile.core ?? null,
|
|
197
|
+
surfaces: (profile.surfaces ?? []).slice().sort(),
|
|
198
|
+
graph,
|
|
199
|
+
line: shape.line,
|
|
200
|
+
ends: shape.ends,
|
|
201
|
+
gates: shape.gates,
|
|
202
|
+
many: [...(graph.many ?? new Map())].map(([stage, { when }]) => ({ stage, when })),
|
|
203
|
+
back: shape.back,
|
|
204
|
+
since: from,
|
|
205
|
+
measured: history !== null,
|
|
206
|
+
stages: [...graph.stages].map((stage) => {
|
|
207
|
+
const spec = specs.get(stage) ?? '';
|
|
208
|
+
const front = frontmatter(spec) ?? {};
|
|
209
|
+
return { stage, model: front.model ?? null, effort: front.effort ?? null, words: words.get(stage) ?? '', history: history?.get(stage) ?? null, skills: [...required(spec).skills].sort() };
|
|
210
|
+
}),
|
|
211
|
+
own: [...specs.keys()].filter((role) => !graph.stages.has(role)).map((role) => ({ role, description: (frontmatter(specs.get(role)) ?? {}).description ?? '' })),
|
|
212
|
+
shapes: existsSync(claude) ? chainsByShape(readFileSync(claude, 'utf8')) : [],
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
168
216
|
/**
|
|
169
217
|
* Runs the command.
|
|
170
218
|
*
|
|
@@ -200,12 +248,9 @@ export async function pipeline(argv) {
|
|
|
200
248
|
}
|
|
201
249
|
return viewOffice({ target: dir, wanted: arg('--session') || undefined, replay: argv.includes('--replay'), port: port === null ? undefined : Number(port), open: argv.includes('--open') });
|
|
202
250
|
}
|
|
203
|
-
const
|
|
204
|
-
const graph =
|
|
205
|
-
const
|
|
206
|
-
const shape = shapeOf(graph);
|
|
207
|
-
const claude = join(dir, 'CLAUDE.md');
|
|
208
|
-
const shapes = existsSync(claude) ? chainsByShape(readFileSync(claude, 'utf8')) : [];
|
|
251
|
+
const data = pipelineData(dir, { since: arg('--since') || undefined });
|
|
252
|
+
const { graph, shapes } = data;
|
|
253
|
+
const shape = { line: data.line, ends: data.ends, gates: data.gates, back: data.back };
|
|
209
254
|
|
|
210
255
|
// `--for`: one shape's chain, and only the stages on it.
|
|
211
256
|
const query = arg('--for');
|
|
@@ -232,9 +277,7 @@ export async function pipeline(argv) {
|
|
|
232
277
|
const padName = (s, n) => `${name(s)}${' '.repeat(Math.max(n - s.length, 1))}`;
|
|
233
278
|
|
|
234
279
|
// A pipeline composed by hand draws all the same; only the header has less to say.
|
|
235
|
-
|
|
236
|
-
const surfaces = (profile.surfaces ?? []).slice().sort();
|
|
237
|
-
console.log(` pipeline · ${basename(dir)}${profile.core ? ` · core ${profile.core}` : ''}${surfaces.length ? ` · surfaces ${surfaces.join(', ')}` : ''}`);
|
|
280
|
+
console.log(` pipeline · ${data.project}${data.core ? ` · core ${data.core}` : ''}${data.surfaces.length ? ` · surfaces ${data.surfaces.join(', ')}` : ''}`);
|
|
238
281
|
|
|
239
282
|
// The stages this drawing covers: all of them, or those the chosen shape's chain names.
|
|
240
283
|
const onChain = chosen ? (chosen.chain.match(/[a-z][a-z-]*/g) ?? []).filter((w) => graph.stages.has(w)) : null;
|
|
@@ -300,30 +343,18 @@ export async function pipeline(argv) {
|
|
|
300
343
|
}
|
|
301
344
|
|
|
302
345
|
// Each stage: what it does, on which model and effort, with which skills, and what it did lately.
|
|
303
|
-
const
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const history = runs ? historyOf(runs, since) : null;
|
|
311
|
-
const stages = [...graph.stages].filter((s) => covered.has(s));
|
|
312
|
-
const col = Math.max(...stages.map((s) => s.length), 10) + 2;
|
|
313
|
-
console.log(`\n ${dim(history ? `stages · what each ran since ${since}, from the measured history` : 'stages · no measured history for this project yet')}`);
|
|
314
|
-
for (const stage of stages) {
|
|
315
|
-
const spec = specs.get(stage) ?? '';
|
|
316
|
-
const front = frontmatter(spec) ?? {};
|
|
317
|
-
const on = [front.model, front.effort].filter(Boolean).join(' · ') || 'no spec';
|
|
318
|
-
console.log(` ${padName(stage, col)}${on.padEnd(26)}${dim(fit(words.get(stage) ?? '', width - col - 32))}`);
|
|
319
|
-
if (history) {
|
|
320
|
-
const h = history.get(stage);
|
|
346
|
+
const stages = data.stages.filter((s) => covered.has(s.stage));
|
|
347
|
+
const col = Math.max(...stages.map((s) => s.stage.length), 10) + 2;
|
|
348
|
+
console.log(`\n ${dim(data.measured ? `stages · what each ran since ${data.since}, from the measured history` : 'stages · no measured history for this project yet')}`);
|
|
349
|
+
for (const { stage, model, effort, words, history: h, skills } of stages) {
|
|
350
|
+
const on = [model, effort].filter(Boolean).join(' · ') || 'no spec';
|
|
351
|
+
console.log(` ${padName(stage, col)}${on.padEnd(26)}${dim(fit(words, width - col - 32))}`);
|
|
352
|
+
if (data.measured) {
|
|
321
353
|
const said = !h
|
|
322
354
|
? 'no run'
|
|
323
355
|
: `${h.runs} run(s)${h.rounds > h.runs ? ` in ${h.rounds} rounds` : ''}${h.read === 0 ? ', no verdict read' : `, sent back ${h.back} of ${h.read} (${Math.round((100 * h.back) / h.read)}%)`}`;
|
|
324
356
|
console.log(` ${' '.repeat(col)}${dim('ran ')}${said}`);
|
|
325
357
|
}
|
|
326
|
-
const skills = [...required(spec).skills].sort();
|
|
327
358
|
if (skills.length > 0) {
|
|
328
359
|
const lines = [];
|
|
329
360
|
let current = '';
|
|
@@ -341,12 +372,10 @@ export async function pipeline(argv) {
|
|
|
341
372
|
|
|
342
373
|
if (!chosen) {
|
|
343
374
|
// An agent the project wrote for itself is not a stage, and the graph does not route to it.
|
|
344
|
-
|
|
345
|
-
if (own.length > 0) {
|
|
375
|
+
if (data.own.length > 0) {
|
|
346
376
|
console.log(`\n ${dim("the project's own agents, outside the pipeline")}`);
|
|
347
|
-
for (const role of own) {
|
|
348
|
-
|
|
349
|
-
console.log(` ${role}${' '.repeat(Math.max(col - role.length, 2))}${dim(fit(front.description ?? '', width - col - 6))}`);
|
|
377
|
+
for (const { role, description } of data.own) {
|
|
378
|
+
console.log(` ${role}${' '.repeat(Math.max(col - role.length, 2))}${dim(fit(description, width - col - 6))}`);
|
|
350
379
|
}
|
|
351
380
|
}
|
|
352
381
|
if (shapes.length > 0) {
|
package/src/commands/runs.mjs
CHANGED
|
@@ -66,12 +66,25 @@ function summarize(cycle) {
|
|
|
66
66
|
unpriced,
|
|
67
67
|
name: String(rounds[0]?.desc ?? '').trim() || '(no description)',
|
|
68
68
|
how: ENDS[cycle.end](rounds.at(-1)),
|
|
69
|
+
end_kind: cycle.end,
|
|
69
70
|
stages: [...stages.values()].map((s) => ({ ...s, runs: s.runs.size })),
|
|
70
71
|
files: files.length ? Math.max(...files) : null,
|
|
71
72
|
peak: peaks.length ? Math.max(...peaks) : null,
|
|
72
73
|
};
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
/**
|
|
77
|
+
* A project's pieces of work, newest first: what `nina runs` prints and the dashboard's tasks tab shows.
|
|
78
|
+
*
|
|
79
|
+
* @param {object[]} records - The project's measured rounds.
|
|
80
|
+
* @returns {ReturnType<typeof summarize>[]}
|
|
81
|
+
*/
|
|
82
|
+
export function taskList(records) {
|
|
83
|
+
return cyclesOf(records.filter((r) => r.status !== 'denied'))
|
|
84
|
+
.map(summarize)
|
|
85
|
+
.sort((a, b) => b.start.localeCompare(a.start));
|
|
86
|
+
}
|
|
87
|
+
|
|
75
88
|
/** A duration in minutes, as hours and minutes. */
|
|
76
89
|
const span = (m) => (m === null ? '—' : m >= 60 ? `${Math.floor(m / 60)}h${String(m % 60).padStart(2, '0')}` : `${m}m`);
|
|
77
90
|
|
|
@@ -103,9 +116,7 @@ export async function runs(argv) {
|
|
|
103
116
|
);
|
|
104
117
|
return 1;
|
|
105
118
|
}
|
|
106
|
-
const cycles =
|
|
107
|
-
.map(summarize)
|
|
108
|
-
.sort((a, b) => b.start.localeCompare(a.start));
|
|
119
|
+
const cycles = taskList(records);
|
|
109
120
|
const shown = cycles.slice(0, last);
|
|
110
121
|
console.log(
|
|
111
122
|
` ${bold(pink(projectName(records[0].project)))} · ${cycles.length} cycle(s)` +
|