@xhulz/nina 0.38.0 → 0.40.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/package.json +1 -1
- package/releases/0.39.0/core/GAPS.md +125 -0
- package/releases/0.39.0/core/answered.json +32 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/architect.md +1 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/dba.md +3 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/devops.md +1 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/qa.md +2 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/secops.md +3 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
- package/releases/0.39.0/core/tree/.claude/agents/architect.md +127 -0
- package/releases/0.39.0/core/tree/.claude/agents/dba.md +110 -0
- package/releases/0.39.0/core/tree/.claude/agents/devops.md +114 -0
- package/releases/0.39.0/core/tree/.claude/agents/implementer.md +156 -0
- package/releases/0.39.0/core/tree/.claude/agents/integration-tester.md +168 -0
- package/releases/0.39.0/core/tree/.claude/agents/planner.md +132 -0
- package/releases/0.39.0/core/tree/.claude/agents/qa.md +152 -0
- package/releases/0.39.0/core/tree/.claude/agents/reviewer.md +183 -0
- package/releases/0.39.0/core/tree/.claude/agents/secops.md +121 -0
- package/releases/0.39.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
- package/releases/0.39.0/core/tree/.claude/agents/solidity-dev.md +123 -0
- package/releases/0.39.0/core/tree/.claude/agents-overview.md +64 -0
- package/releases/0.39.0/core/tree/.claude/frontend.md +10 -0
- package/releases/0.39.0/core/tree/.claude/graph.md +75 -0
- package/releases/0.39.0/core/tree/.claude/patterns.md +213 -0
- package/releases/0.39.0/core/tree/.claude/pills/README.md +147 -0
- package/releases/0.39.0/core/tree/.claude/pipeline.md +101 -0
- package/releases/0.39.0/core/tree/.claude/retrieval.md +103 -0
- package/releases/0.39.0/core/tree/.claude/router.md +198 -0
- package/releases/0.39.0/core/tree/.claude/templates/integration.md +45 -0
- package/releases/0.39.0/core/tree/CLAUDE.md +163 -0
- package/releases/0.39.0/core/tree/scripts/cost-watch.mjs +18 -0
- package/releases/0.39.0/core/tree/scripts/edit-guard.mjs +24 -0
- package/releases/0.39.0/core/tree/scripts/harness-check.mjs +71 -0
- package/releases/0.39.0/core/tree/scripts/loop-gate.mjs +19 -0
- package/releases/0.39.0/core/vocabulary.json +13 -0
- package/releases/0.39.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
- package/releases/0.39.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
- package/releases/0.39.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
- package/releases/0.39.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
- package/releases/0.39.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
- package/releases/0.39.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
- package/releases/0.39.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
- package/releases/0.39.0/surfaces/blockchain/tree/.claude/router.md +6 -0
- package/releases/0.39.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/agents/dba.md +50 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/graph.md +8 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/patterns.md +19 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/pipeline.md +13 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/retrieval.md +5 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/router.md +4 -0
- package/releases/0.39.0/surfaces/db/tree/CLAUDE.md +17 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/agents/devops.md +32 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
- package/releases/0.39.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
- package/releases/0.39.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
- package/releases/0.39.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
- package/releases/0.39.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
- package/releases/0.39.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
- package/releases/0.39.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
- package/releases/0.39.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
- package/releases/0.39.0/surfaces/frontend/tree/.claude/router.md +14 -0
- package/releases/0.39.0/surfaces/frontend/tree/CLAUDE.md +5 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/graph.md +10 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/router.md +6 -0
- package/releases/0.39.0/surfaces/integrations/tree/CLAUDE.md +17 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/graph.md +2 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/patterns.md +49 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/pipeline.md +2 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/retrieval.md +2 -0
- package/releases/0.39.0/surfaces/money/tree/CLAUDE.md +11 -0
- package/releases/0.39.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
- package/releases/0.39.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
- package/releases/0.39.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.39.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
- package/releases/0.39.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
- package/releases/0.39.0/surfaces/pii/tree/CLAUDE.md +5 -0
- package/releases/0.39.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
- package/releases/0.39.0/surfaces/prisma/tree/.claude/agents/dba.md +39 -0
- package/releases/0.39.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
- package/releases/0.39.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.39.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
- package/releases/0.39.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
- package/releases/0.39.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
- package/releases/0.39.0/surfaces/prisma/tree/CLAUDE.md +5 -0
- package/releases/0.40.0/core/GAPS.md +125 -0
- package/releases/0.40.0/core/answered.json +32 -0
- package/releases/0.40.0/core/defaults/tree/.claude/agents/architect.md +1 -0
- package/releases/0.40.0/core/defaults/tree/.claude/agents/dba.md +3 -0
- package/releases/0.40.0/core/defaults/tree/.claude/agents/devops.md +1 -0
- package/releases/0.40.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
- package/releases/0.40.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.40.0/core/defaults/tree/.claude/agents/qa.md +2 -0
- package/releases/0.40.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
- package/releases/0.40.0/core/defaults/tree/.claude/agents/secops.md +3 -0
- package/releases/0.40.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
- package/releases/0.40.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
- package/releases/0.40.0/core/tree/.claude/agents/architect.md +127 -0
- package/releases/0.40.0/core/tree/.claude/agents/dba.md +110 -0
- package/releases/0.40.0/core/tree/.claude/agents/devops.md +114 -0
- package/releases/0.40.0/core/tree/.claude/agents/implementer.md +156 -0
- package/releases/0.40.0/core/tree/.claude/agents/integration-tester.md +168 -0
- package/releases/0.40.0/core/tree/.claude/agents/planner.md +132 -0
- package/releases/0.40.0/core/tree/.claude/agents/qa.md +152 -0
- package/releases/0.40.0/core/tree/.claude/agents/reviewer.md +183 -0
- package/releases/0.40.0/core/tree/.claude/agents/secops.md +121 -0
- package/releases/0.40.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
- package/releases/0.40.0/core/tree/.claude/agents/solidity-dev.md +123 -0
- package/releases/0.40.0/core/tree/.claude/agents-overview.md +64 -0
- package/releases/0.40.0/core/tree/.claude/frontend.md +10 -0
- package/releases/0.40.0/core/tree/.claude/graph.md +75 -0
- package/releases/0.40.0/core/tree/.claude/patterns.md +213 -0
- package/releases/0.40.0/core/tree/.claude/pills/README.md +147 -0
- package/releases/0.40.0/core/tree/.claude/pipeline.md +101 -0
- package/releases/0.40.0/core/tree/.claude/retrieval.md +103 -0
- package/releases/0.40.0/core/tree/.claude/router.md +198 -0
- package/releases/0.40.0/core/tree/.claude/templates/integration.md +45 -0
- package/releases/0.40.0/core/tree/CLAUDE.md +163 -0
- package/releases/0.40.0/core/tree/scripts/cost-watch.mjs +25 -0
- package/releases/0.40.0/core/tree/scripts/edit-guard.mjs +24 -0
- package/releases/0.40.0/core/tree/scripts/harness-check.mjs +71 -0
- package/releases/0.40.0/core/tree/scripts/loop-gate.mjs +19 -0
- package/releases/0.40.0/core/vocabulary.json +13 -0
- package/releases/0.40.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
- package/releases/0.40.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
- package/releases/0.40.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
- package/releases/0.40.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
- package/releases/0.40.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
- package/releases/0.40.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
- package/releases/0.40.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
- package/releases/0.40.0/surfaces/blockchain/tree/.claude/router.md +6 -0
- package/releases/0.40.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
- package/releases/0.40.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
- package/releases/0.40.0/surfaces/db/tree/.claude/agents/dba.md +50 -0
- package/releases/0.40.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
- package/releases/0.40.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.40.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
- package/releases/0.40.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
- package/releases/0.40.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
- package/releases/0.40.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
- package/releases/0.40.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
- package/releases/0.40.0/surfaces/db/tree/.claude/graph.md +8 -0
- package/releases/0.40.0/surfaces/db/tree/.claude/patterns.md +19 -0
- package/releases/0.40.0/surfaces/db/tree/.claude/pipeline.md +13 -0
- package/releases/0.40.0/surfaces/db/tree/.claude/retrieval.md +5 -0
- package/releases/0.40.0/surfaces/db/tree/.claude/router.md +4 -0
- package/releases/0.40.0/surfaces/db/tree/CLAUDE.md +17 -0
- package/releases/0.40.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
- package/releases/0.40.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
- package/releases/0.40.0/surfaces/edge-cf/tree/.claude/agents/devops.md +32 -0
- package/releases/0.40.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
- package/releases/0.40.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
- package/releases/0.40.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
- package/releases/0.40.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.40.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
- package/releases/0.40.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
- package/releases/0.40.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
- package/releases/0.40.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
- package/releases/0.40.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
- package/releases/0.40.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
- package/releases/0.40.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
- package/releases/0.40.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
- package/releases/0.40.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
- package/releases/0.40.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
- package/releases/0.40.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
- package/releases/0.40.0/surfaces/frontend/tree/.claude/router.md +14 -0
- package/releases/0.40.0/surfaces/frontend/tree/CLAUDE.md +5 -0
- package/releases/0.40.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
- package/releases/0.40.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
- package/releases/0.40.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
- package/releases/0.40.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
- package/releases/0.40.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
- package/releases/0.40.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
- package/releases/0.40.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
- package/releases/0.40.0/surfaces/integrations/tree/.claude/graph.md +10 -0
- package/releases/0.40.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
- package/releases/0.40.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
- package/releases/0.40.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
- package/releases/0.40.0/surfaces/integrations/tree/.claude/router.md +6 -0
- package/releases/0.40.0/surfaces/integrations/tree/CLAUDE.md +17 -0
- package/releases/0.40.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
- package/releases/0.40.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
- package/releases/0.40.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
- package/releases/0.40.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.40.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
- package/releases/0.40.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
- package/releases/0.40.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
- package/releases/0.40.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
- package/releases/0.40.0/surfaces/money/tree/.claude/graph.md +2 -0
- package/releases/0.40.0/surfaces/money/tree/.claude/patterns.md +49 -0
- package/releases/0.40.0/surfaces/money/tree/.claude/pipeline.md +2 -0
- package/releases/0.40.0/surfaces/money/tree/.claude/retrieval.md +2 -0
- package/releases/0.40.0/surfaces/money/tree/CLAUDE.md +11 -0
- package/releases/0.40.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
- package/releases/0.40.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
- package/releases/0.40.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.40.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
- package/releases/0.40.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
- package/releases/0.40.0/surfaces/pii/tree/CLAUDE.md +5 -0
- package/releases/0.40.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
- package/releases/0.40.0/surfaces/prisma/tree/.claude/agents/dba.md +39 -0
- package/releases/0.40.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
- package/releases/0.40.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.40.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
- package/releases/0.40.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
- package/releases/0.40.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
- package/releases/0.40.0/surfaces/prisma/tree/CLAUDE.md +5 -0
- package/src/commands/check.mjs +15 -28
- package/src/commands/compose.mjs +50 -64
- package/src/commands/eval.mjs +1 -1
- package/src/commands/export.mjs +4 -34
- package/src/commands/gate.mjs +4 -11
- package/src/commands/init.mjs +22 -30
- package/src/commands/langfuse.mjs +1 -1
- package/src/commands/learn.mjs +10 -12
- package/src/commands/pills.mjs +5 -5
- package/src/commands/pipeline.mjs +4 -8
- package/src/commands/release.mjs +2 -3
- package/src/commands/runs.mjs +1 -1
- package/src/commands/snapshot.mjs +12 -50
- package/src/commands/stats.mjs +7 -8
- package/src/commands/status.mjs +4 -5
- package/src/commands/upgrade.mjs +9 -12
- package/src/commands/where.mjs +9 -18
- package/src/commands/wire.mjs +8 -19
- package/src/deep.mjs +3 -3
- package/src/gate.mjs +54 -7
- package/src/graph.mjs +1 -1
- package/src/guard.mjs +194 -7
- package/src/langfuse.mjs +9 -8
- package/src/lock.mjs +82 -0
- package/src/paths.mjs +65 -0
- package/src/shell.mjs +3 -3
- package/src/steps.mjs +5 -19
- package/src/store.mjs +2 -2
- package/src/wiring.mjs +20 -7
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
14
14
|
import { basename, join, resolve } from 'node:path';
|
|
15
15
|
import { dim, pink } from '../look.mjs';
|
|
16
|
-
import {
|
|
16
|
+
import { readProfile } from '../paths.mjs';
|
|
17
17
|
import { TERMINALS, parseGraph } from '../graph.mjs';
|
|
18
18
|
import { required } from '../tools.mjs';
|
|
19
19
|
import { isLoopBack, runOf } from '../transcripts.mjs';
|
|
@@ -33,7 +33,7 @@ const HISTORY_DAYS = 30;
|
|
|
33
33
|
* @param {string} text - The composed `.claude/graph.md`.
|
|
34
34
|
* @returns {Map<string, string>}
|
|
35
35
|
*/
|
|
36
|
-
|
|
36
|
+
function stageWords(text) {
|
|
37
37
|
const out = new Map();
|
|
38
38
|
let inStages = false;
|
|
39
39
|
for (const line of text.split('\n')) {
|
|
@@ -210,12 +210,8 @@ export async function pipeline(argv) {
|
|
|
210
210
|
const shownStage = (s) => `${name(s)}${many.has(s) ? ' ×n' : ''}`;
|
|
211
211
|
const padName = (s, n) => `${name(s)}${' '.repeat(Math.max(n - s.length, 1))}`;
|
|
212
212
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
profile = JSON.parse(readFileSync(join(dir, HARNESS, 'profile.json'), 'utf8'));
|
|
216
|
-
} catch {
|
|
217
|
-
// A pipeline composed by hand draws all the same; only the header has less to say.
|
|
218
|
-
}
|
|
213
|
+
// A pipeline composed by hand draws all the same; only the header has less to say.
|
|
214
|
+
const profile = readProfile(dir).profile ?? {};
|
|
219
215
|
const surfaces = (profile.surfaces ?? []).slice().sort();
|
|
220
216
|
console.log(` pipeline · ${basename(dir)}${profile.core ? ` · core ${profile.core}` : ''}${surfaces.length ? ` · surfaces ${surfaces.join(', ')}` : ''}`);
|
|
221
217
|
|
package/src/commands/release.mjs
CHANGED
|
@@ -13,6 +13,7 @@ import { cp, mkdir, readFile, readdir, rename, rm, writeFile } from 'node:fs/pro
|
|
|
13
13
|
import { existsSync } from 'node:fs';
|
|
14
14
|
import { join } from 'node:path';
|
|
15
15
|
import { byVersion } from './compose.mjs';
|
|
16
|
+
import { surfacesIn } from '../paths.mjs';
|
|
16
17
|
|
|
17
18
|
/** A release name: dotted numbers, so the directory sorts and reads like a version. */
|
|
18
19
|
const VERSION = /^\d+\.\d+\.\d+$/;
|
|
@@ -104,9 +105,7 @@ export async function release(argv, ctx) {
|
|
|
104
105
|
await mkdir(join(ctx.root, 'releases'), { recursive: true });
|
|
105
106
|
await rename(partial, dest);
|
|
106
107
|
|
|
107
|
-
const surfaces = (await
|
|
108
|
-
.filter((e) => e.isDirectory())
|
|
109
|
-
.map((e) => e.name);
|
|
108
|
+
const surfaces = (await surfacesIn(dest));
|
|
110
109
|
const bumped = await bumpPackage(ctx.root, version);
|
|
111
110
|
|
|
112
111
|
console.log(` released ${version} — core + ${surfaces.length} surfaces (${surfaces.join(', ')})`);
|
package/src/commands/runs.mjs
CHANGED
|
@@ -34,7 +34,7 @@ const ENDS = {
|
|
|
34
34
|
* @param {{runs: object[], end: string}} cycle
|
|
35
35
|
* @returns {{start: string, end: string, minutes: number|null, cost: number, unpriced: number, name: string, how: string, stages: {role: string, runs: number, rounds: number, back: number}[], files: number|null, peak: number|null}}
|
|
36
36
|
*/
|
|
37
|
-
|
|
37
|
+
function summarize(cycle) {
|
|
38
38
|
const rounds = cycle.runs;
|
|
39
39
|
const start = rounds.map((r) => String(r.ts)).sort()[0];
|
|
40
40
|
const end = rounds.map((r) => String(r.result_ts ?? r.ts)).sort().at(-1);
|
|
@@ -8,16 +8,14 @@
|
|
|
8
8
|
* the transcripts are hundreds of megabytes and growing.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { copyFile, mkdir,
|
|
11
|
+
import { copyFile, mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
12
12
|
import { HARNESS, snapshotsDir } from '../paths.mjs';
|
|
13
13
|
import { decodeProjectDir } from './stats.mjs';
|
|
14
14
|
import { readStoreFile } from '../store.mjs';
|
|
15
15
|
import { existsSync, readFileSync } from 'node:fs';
|
|
16
16
|
import { join, resolve } from 'node:path';
|
|
17
17
|
import { listProjects, scanProject } from '../transcripts.mjs';
|
|
18
|
-
|
|
19
|
-
/** Where per-project read cursors live, beside the snapshots they describe. */
|
|
20
|
-
const STATE_FILE = '.state.json';
|
|
18
|
+
import { takeLock } from '../lock.mjs';
|
|
21
19
|
|
|
22
20
|
/**
|
|
23
21
|
* Reads a JSON file, or an empty object when it is missing or unreadable. A state file cut short
|
|
@@ -64,40 +62,6 @@ async function writeAtomic(file, records) {
|
|
|
64
62
|
await rename(tmp, file);
|
|
65
63
|
}
|
|
66
64
|
|
|
67
|
-
/**
|
|
68
|
-
* Takes a project's lock, or says it is taken. The records and the cursors are written one after the other,
|
|
69
|
-
* and two snapshots of one project at once — a project's own detector and a global hook, both on every
|
|
70
|
-
* Stop — could leave the cursors of the scan that read further beside the records of the one that read less,
|
|
71
|
-
* and whatever lay between was never captured. One that finds the lock taken skips the project: the next
|
|
72
|
-
* snapshot reads from where this one leaves it. A lock names its process, so one left by a snapshot that
|
|
73
|
-
* died is taken over.
|
|
74
|
-
*
|
|
75
|
-
* @param {string} path - The lock file.
|
|
76
|
-
* @returns {Promise<(() => Promise<void>)|null>} Its release, or null when another snapshot holds it.
|
|
77
|
-
*/
|
|
78
|
-
async function lock(path) {
|
|
79
|
-
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
80
|
-
try {
|
|
81
|
-
const handle = await open(path, 'wx');
|
|
82
|
-
await handle.writeFile(String(process.pid));
|
|
83
|
-
await handle.close();
|
|
84
|
-
return () => rm(path, { force: true });
|
|
85
|
-
} catch (error) {
|
|
86
|
-
if (error.code !== 'EEXIST') throw error;
|
|
87
|
-
const pid = Number(await readFile(path, 'utf8').catch(() => ''));
|
|
88
|
-
let alive = false;
|
|
89
|
-
try {
|
|
90
|
-
alive = pid > 0 && process.kill(pid, 0);
|
|
91
|
-
} catch (e) {
|
|
92
|
-
alive = e.code === 'EPERM';
|
|
93
|
-
}
|
|
94
|
-
if (alive) return null;
|
|
95
|
-
await rm(path, { force: true });
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
65
|
/**
|
|
102
66
|
* Runs the snapshot.
|
|
103
67
|
*
|
|
@@ -115,14 +79,6 @@ export async function snapshot(argv, ctx) {
|
|
|
115
79
|
const quiet = argv.includes('--quiet');
|
|
116
80
|
|
|
117
81
|
await mkdir(outDir, { recursive: true });
|
|
118
|
-
// The cursors used to live in one file shared by every project, rewritten whole and in place.
|
|
119
|
-
// Two snapshots at once — the global hook and a project's own detector, or two sessions ending
|
|
120
|
-
// together — each read all of it, updated their own project, and wrote all of it back: the later
|
|
121
|
-
// writer silently put every other project's cursor back where it had been, and a cursor that
|
|
122
|
-
// goes back re-reads history. Each project now has its own, beside its records, written by
|
|
123
|
-
// rename. The shared file is still read, once, so a project's cursor is not lost in the move.
|
|
124
|
-
const legacy = rebuild ? {} : await readJson(join(outDir, STATE_FILE));
|
|
125
|
-
|
|
126
82
|
const projects = await listProjects();
|
|
127
83
|
// `--exact` for a caller that knows the slug — a project's own detector. A substring also
|
|
128
84
|
// matched every project nested under it, and snapshotted them all on every turn.
|
|
@@ -136,10 +92,13 @@ export async function snapshot(argv, ctx) {
|
|
|
136
92
|
|
|
137
93
|
let totalNew = 0;
|
|
138
94
|
for (const project of targets) {
|
|
139
|
-
|
|
95
|
+
// One at a time per project: the records and the cursors are written one after the other, and two at once could
|
|
96
|
+
// leave the cursors of the scan that read further beside the records of the one that read less. One that finds
|
|
97
|
+
// the lock held skips the project, and the next reads from where this one leaves it.
|
|
98
|
+
const release = await takeLock(join(outDir, `${project.slug}.lock`));
|
|
140
99
|
if (!release) continue;
|
|
141
100
|
try {
|
|
142
|
-
totalNew += await snapshotProject(project, { outDir, rebuild, quiet
|
|
101
|
+
totalNew += await snapshotProject(project, { outDir, rebuild, quiet });
|
|
143
102
|
} finally {
|
|
144
103
|
await release();
|
|
145
104
|
}
|
|
@@ -192,15 +151,18 @@ function pinOf(slug) {
|
|
|
192
151
|
*
|
|
193
152
|
* @returns {Promise<number>} How many rounds it captured that were not on record.
|
|
194
153
|
*/
|
|
195
|
-
async function snapshotProject(project, { outDir, rebuild, quiet
|
|
154
|
+
async function snapshotProject(project, { outDir, rebuild, quiet }) {
|
|
196
155
|
const file = join(outDir, `${project.slug}.jsonl`);
|
|
197
156
|
// The store is the only copy of history older than the transcripts, and a rebuild re-reads only what is
|
|
198
157
|
// still on disk: the record it replaces is kept beside it first.
|
|
199
158
|
if (rebuild && existsSync(file)) await copyFile(file, `${file}.before-rebuild-${new Date().toISOString().replace(/[:.]/g, '-')}`);
|
|
200
159
|
const stored = readStoreFile(file, { raw: true });
|
|
201
160
|
const prior = rebuild ? [] : stored;
|
|
161
|
+
// Each project's read cursors live beside its records, written by rename. They once shared one file, rewritten
|
|
162
|
+
// whole: two snapshots at once each wrote every other project's cursor back where it had been, and a cursor
|
|
163
|
+
// that goes back re-reads history.
|
|
202
164
|
const own = rebuild ? {} : await readJson(join(outDir, `${project.slug}.state.json`));
|
|
203
|
-
const cursors = rebuild ? {} : (own.cursors ??
|
|
165
|
+
const cursors = rebuild ? {} : (own.cursors ?? {});
|
|
204
166
|
|
|
205
167
|
const scanned = await scanProject(project.dir, { cursors, records: prior });
|
|
206
168
|
// With its cursors gone — a state file deleted, or cut short — the transcripts are read again from the start
|
package/src/commands/stats.mjs
CHANGED
|
@@ -164,7 +164,7 @@ export function projectName(encoded) {
|
|
|
164
164
|
* @param {string[]} projects - The encoded project names in the report.
|
|
165
165
|
* @returns {Promise<{total:number, retired:number, undated:number, dates:string[], unresolved:string[]}>}
|
|
166
166
|
*/
|
|
167
|
-
|
|
167
|
+
async function harvest(projects) {
|
|
168
168
|
const out = { total: 0, retired: 0, undated: 0, dates: [], unresolved: [] };
|
|
169
169
|
for (const name of projects) {
|
|
170
170
|
const dir = decodeProjectDir(name);
|
|
@@ -243,7 +243,6 @@ function costReport(records) {
|
|
|
243
243
|
if (total === 0) return;
|
|
244
244
|
const money = (n) => `$${n >= 100 ? n.toFixed(0) : n.toFixed(2)}`;
|
|
245
245
|
// The same convention as the duration column beside it: the upper middle of an even count.
|
|
246
|
-
const median = (xs) => [...xs].sort((a, b) => a - b)[Math.floor(xs.length / 2)];
|
|
247
246
|
console.log(
|
|
248
247
|
heading(
|
|
249
248
|
'cost',
|
|
@@ -273,7 +272,6 @@ function costReport(records) {
|
|
|
273
272
|
function contextReport(records) {
|
|
274
273
|
const shaped = records.filter((r) => typeof r.turns === 'number' && r.turns > 0);
|
|
275
274
|
if (shaped.length === 0) return;
|
|
276
|
-
const median = (xs) => [...xs].sort((a, b) => a - b)[Math.floor(xs.length / 2)];
|
|
277
275
|
const k = (n) => (typeof n === 'number' ? `${Math.round(n / 1000)}k` : '—');
|
|
278
276
|
const chars = (xs) => (xs.length ? median(xs).toLocaleString('en-US') : '—');
|
|
279
277
|
console.log(heading('context', 'every turn re-reads the whole context: a round costs its turns times its size, and a resumed one starts where it ended; the prompt it was handed is in characters'));
|
|
@@ -362,7 +360,6 @@ function modelReport(records) {
|
|
|
362
360
|
const changed = [...byRole].filter(([, models]) => models.size > 1).sort(([a], [b]) => a.localeCompare(b));
|
|
363
361
|
|
|
364
362
|
const money = (n) => `$${n.toFixed(2)}`;
|
|
365
|
-
const median = (xs) => [...xs].sort((a, b) => a - b)[Math.floor(xs.length / 2)];
|
|
366
363
|
const lines = [];
|
|
367
364
|
for (const [role, models] of changed) {
|
|
368
365
|
const ordered = [...models].sort(([, a], [, b]) => String(a[0].ts).localeCompare(String(b[0].ts)));
|
|
@@ -431,7 +428,7 @@ const RULE_SINCE = { issues: '0.22.0', stepFiles: '0.28.8' };
|
|
|
431
428
|
* @param {keyof RULE_SINCE} rule - The rule.
|
|
432
429
|
* @returns {boolean}
|
|
433
430
|
*/
|
|
434
|
-
|
|
431
|
+
const ranUnder = (record, rule) => typeof record.core === 'string' && (record.core === 'dev' || byVersion(record.core, RULE_SINCE[rule]) >= 0);
|
|
435
432
|
|
|
436
433
|
/** The stages that write code, whose runs' file counts size a cycle. */
|
|
437
434
|
export const WRITERS = new Set(['implementer', 'solidity-dev']);
|
|
@@ -590,11 +587,13 @@ function stepLimitOf(ctx) {
|
|
|
590
587
|
};
|
|
591
588
|
}
|
|
592
589
|
|
|
590
|
+
/** The middle of a list of numbers — the upper one of two — or undefined for none. */
|
|
591
|
+
const median = (xs) => [...xs].sort((a, b) => a - b)[Math.floor(xs.length / 2)];
|
|
592
|
+
|
|
593
593
|
/** Formats a median duration in minutes from a list of seconds. */
|
|
594
594
|
function medianMin(values) {
|
|
595
|
-
const xs = values.filter((v) => typeof v === 'number' && v >= 0)
|
|
596
|
-
|
|
597
|
-
return `${(xs[Math.floor(xs.length / 2)] / 60).toFixed(1)}m`;
|
|
595
|
+
const xs = values.filter((v) => typeof v === 'number' && v >= 0);
|
|
596
|
+
return xs.length ? `${(median(xs) / 60).toFixed(1)}m` : '—';
|
|
598
597
|
}
|
|
599
598
|
|
|
600
599
|
/**
|
package/src/commands/status.mjs
CHANGED
|
@@ -14,7 +14,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
14
14
|
import { existsSync, readFileSync } from 'node:fs';
|
|
15
15
|
import { createRequire } from 'node:module';
|
|
16
16
|
import { join, resolve } from 'node:path';
|
|
17
|
-
import {
|
|
17
|
+
import { readProfile } from '../paths.mjs';
|
|
18
18
|
import { costOf } from '../prices.mjs';
|
|
19
19
|
import { runOf } from '../transcripts.mjs';
|
|
20
20
|
import { missingWiring, shippedScripts } from '../wiring.mjs';
|
|
@@ -86,12 +86,11 @@ function ago(iso, now) {
|
|
|
86
86
|
*/
|
|
87
87
|
export async function status(argv, ctx) {
|
|
88
88
|
const target = resolve(argv.includes('--project') ? argv[argv.indexOf('--project') + 1] : '.');
|
|
89
|
-
const
|
|
90
|
-
if (
|
|
91
|
-
console.error(`
|
|
89
|
+
const { profile, error } = readProfile(target);
|
|
90
|
+
if (error) {
|
|
91
|
+
console.error(` ${error}\n`);
|
|
92
92
|
return 1;
|
|
93
93
|
}
|
|
94
|
-
const profile = JSON.parse(readFileSync(profilePath, 'utf8'));
|
|
95
94
|
const bin = join(ctx.root, 'bin', 'nina.mjs');
|
|
96
95
|
const now = Date.now();
|
|
97
96
|
const row = (label, text) => console.log(` ${pink(label.padEnd(10))} ${text}`);
|
package/src/commands/upgrade.mjs
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
* writes only when told to. Reconciling the fragments is work, and work does not get done by a flag.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { readFile
|
|
12
|
+
import { readFile } from 'node:fs/promises';
|
|
13
13
|
import { spawnSync } from 'node:child_process';
|
|
14
|
-
import {
|
|
14
|
+
import { describeMove, HARNESS, interruptedMove, movedFrom, moveJournal, readProfile, surfacesIn } from '../paths.mjs';
|
|
15
15
|
import { existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
16
16
|
import { dirname, join, resolve } from 'node:path';
|
|
17
17
|
import { REMOVED, composeProject, composedPaths, defaultedSlots, layerRootFor, walk } from './compose.mjs';
|
|
@@ -20,7 +20,7 @@ import { filledSlots, projectSlots, referencedVocabulary } from './check.mjs';
|
|
|
20
20
|
import { EXPECT_ENV } from '../expected.mjs';
|
|
21
21
|
import { closeAnswered } from './learn.mjs';
|
|
22
22
|
import { tidyRetired } from './pills.mjs';
|
|
23
|
-
import { missingFragment, missingWiring, shippedScripts } from '../wiring.mjs';
|
|
23
|
+
import { lacksPackage, missingFragment, missingWiring, shippedScripts } from '../wiring.mjs';
|
|
24
24
|
import { runSteps } from '../steps.mjs';
|
|
25
25
|
import { defaultVocabulary } from '../vocabulary.mjs';
|
|
26
26
|
|
|
@@ -239,12 +239,11 @@ export async function upgrade(argv, ctx) {
|
|
|
239
239
|
const target = resolve(arg('--project') ?? '.');
|
|
240
240
|
const apply = argv.includes('--apply');
|
|
241
241
|
|
|
242
|
-
const profilePath =
|
|
243
|
-
if (
|
|
244
|
-
console.error(`
|
|
242
|
+
const { profile, path: profilePath, error } = readProfile(target);
|
|
243
|
+
if (error) {
|
|
244
|
+
console.error(` ${error}\n`);
|
|
245
245
|
return 1;
|
|
246
246
|
}
|
|
247
|
-
const profile = JSON.parse(await readFile(profilePath, 'utf8'));
|
|
248
247
|
|
|
249
248
|
// A move stopped half-way comes before anything else asked of this command: its pin may already name the
|
|
250
249
|
// version it was moving to, and "already pins" left the tree half-moved for good.
|
|
@@ -290,15 +289,13 @@ export async function upgrade(argv, ctx) {
|
|
|
290
289
|
}
|
|
291
290
|
|
|
292
291
|
const surfaces = profile.surfaces ?? [];
|
|
293
|
-
const available = (await
|
|
294
|
-
.filter((e) => e.isDirectory())
|
|
295
|
-
.map((e) => e.name);
|
|
292
|
+
const available = (await surfacesIn(onto.dir));
|
|
296
293
|
const gone = surfaces.filter((s) => !available.includes(s));
|
|
297
294
|
const kept = surfaces.filter((s) => available.includes(s));
|
|
298
295
|
// A surface the target release adds that this project's own files show it has. When a stack is split out
|
|
299
296
|
// of a concern — `prisma` out of `db` — its rules go with it, and a project that does not declare it loses
|
|
300
297
|
// them in silence: the rules that were in its specs yesterday are not there after the move.
|
|
301
|
-
const offeredBefore = (await
|
|
298
|
+
const offeredBefore = (await surfacesIn(from.dir));
|
|
302
299
|
const offered = detected(await walk(target)).filter(
|
|
303
300
|
(d) => available.includes(d.surface) && !offeredBefore.includes(d.surface) && !surfaces.includes(d.surface) && (!NEEDS[d.surface] || kept.includes(NEEDS[d.surface])),
|
|
304
301
|
);
|
|
@@ -376,7 +373,7 @@ export async function upgrade(argv, ctx) {
|
|
|
376
373
|
for (const line of fragment.split('\n')) console.log(` ${line}`);
|
|
377
374
|
}
|
|
378
375
|
console.log(` \`nina wire --to ${to} --apply\` merges them. They stay inert until ${to} is pinned, so wiring first is safe.`);
|
|
379
|
-
if (unwired
|
|
376
|
+
if (lacksPackage(unwired)) {
|
|
380
377
|
console.log(' The package is the one thing `wire` cannot do: `pnpm add -D -E @xhulz/nina`.');
|
|
381
378
|
}
|
|
382
379
|
console.log('');
|
package/src/commands/where.mjs
CHANGED
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
import { existsSync } from 'node:fs';
|
|
22
|
-
import { readFile
|
|
22
|
+
import { readFile } from 'node:fs/promises';
|
|
23
23
|
import { isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
24
|
-
import { HARNESS,
|
|
25
|
-
import {
|
|
24
|
+
import { HARNESS, readProfile, surfacesIn } from '../paths.mjs';
|
|
25
|
+
import { SLOT, closedGate, defaultedSlots, generatedNotice, layerRootFor } from './compose.mjs';
|
|
26
26
|
import { filledSlots, owedDocuments } from './check.mjs';
|
|
27
27
|
import { roleGates } from './pills.mjs';
|
|
28
28
|
|
|
@@ -71,16 +71,9 @@ export async function where(argv, ctx) {
|
|
|
71
71
|
return 1;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
const
|
|
75
|
-
if (
|
|
76
|
-
console.error(`
|
|
77
|
-
return 1;
|
|
78
|
-
}
|
|
79
|
-
let profile;
|
|
80
|
-
try {
|
|
81
|
-
profile = JSON.parse(await readFile(profilePath, 'utf8'));
|
|
82
|
-
} catch (error) {
|
|
83
|
-
console.error(` ${profilePath} is not valid JSON — ${error.message}\n`);
|
|
74
|
+
const { profile, error } = readProfile(target);
|
|
75
|
+
if (error) {
|
|
76
|
+
console.error(` ${error}\n`);
|
|
84
77
|
return 1;
|
|
85
78
|
}
|
|
86
79
|
|
|
@@ -89,9 +82,7 @@ export async function where(argv, ctx) {
|
|
|
89
82
|
console.error(` ${resolved.error}\n`);
|
|
90
83
|
return 1;
|
|
91
84
|
}
|
|
92
|
-
const available = (await
|
|
93
|
-
.filter((e) => e.isDirectory())
|
|
94
|
-
.map((e) => e.name);
|
|
85
|
+
const available = (await surfacesIn(resolved.dir));
|
|
95
86
|
const surfaces = (profile.surfaces ?? []).filter((s) => available.includes(s));
|
|
96
87
|
|
|
97
88
|
console.log(` ${posix(rel)}\n`);
|
|
@@ -99,8 +90,8 @@ export async function where(argv, ctx) {
|
|
|
99
90
|
const corePath = join(resolved.dir, 'core', 'tree', rel);
|
|
100
91
|
if (existsSync(corePath)) {
|
|
101
92
|
const core = await readFile(corePath, 'utf8');
|
|
102
|
-
const gate =
|
|
103
|
-
if (gate
|
|
93
|
+
const gate = closedGate(core, surfaces);
|
|
94
|
+
if (gate) {
|
|
104
95
|
block(`defined in core ${profile.core}, but gated on the "${gate}" surface, which this project
|
|
105
96
|
does not declare — so it is never composed here and is not on disk.
|
|
106
97
|
to get it: add "${gate}" to ${HARNESS}/profile.json and run \`nina compose\`.`);
|
package/src/commands/wire.mjs
CHANGED
|
@@ -12,11 +12,9 @@
|
|
|
12
12
|
* hook runs its script only once that script is composed
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import { HARNESS, legacyHint } from '../paths.mjs';
|
|
19
|
-
import { applyWiring, missingFragment, missingWiring, shippedScripts, staleHooks } from '../wiring.mjs';
|
|
15
|
+
import { resolve } from 'node:path';
|
|
16
|
+
import { readProfile, surfacesIn } from '../paths.mjs';
|
|
17
|
+
import { applyWiring, lacksPackage, missingFragment, missingWiring, shippedScripts, staleHooks } from '../wiring.mjs';
|
|
20
18
|
import { layerRootFor } from './compose.mjs';
|
|
21
19
|
|
|
22
20
|
/**
|
|
@@ -27,16 +25,9 @@ import { layerRootFor } from './compose.mjs';
|
|
|
27
25
|
export async function wire(argv, ctx) {
|
|
28
26
|
const arg = (name) => (argv.includes(name) ? argv[argv.indexOf(name) + 1] : null);
|
|
29
27
|
const target = resolve(arg('--project') ?? '.');
|
|
30
|
-
const
|
|
31
|
-
if (
|
|
32
|
-
console.error(`
|
|
33
|
-
return 2;
|
|
34
|
-
}
|
|
35
|
-
let profile;
|
|
36
|
-
try {
|
|
37
|
-
profile = JSON.parse(await readFile(profilePath, 'utf8'));
|
|
38
|
-
} catch (error) {
|
|
39
|
-
console.error(` ${profilePath} is not valid JSON — ${error.message}\n`);
|
|
28
|
+
const { profile, error } = readProfile(target);
|
|
29
|
+
if (error) {
|
|
30
|
+
console.error(` ${error}\n`);
|
|
40
31
|
return 2;
|
|
41
32
|
}
|
|
42
33
|
const version = arg('--to') ?? profile.core;
|
|
@@ -45,9 +36,7 @@ export async function wire(argv, ctx) {
|
|
|
45
36
|
console.error(` ${resolved.error}\n`);
|
|
46
37
|
return 2;
|
|
47
38
|
}
|
|
48
|
-
const available = (await
|
|
49
|
-
.filter((e) => e.isDirectory())
|
|
50
|
-
.map((e) => e.name);
|
|
39
|
+
const available = (await surfacesIn(resolved.dir));
|
|
51
40
|
const shipped = await shippedScripts(resolved.dir, (profile.surfaces ?? []).filter((s) => available.includes(s)));
|
|
52
41
|
|
|
53
42
|
const missing = await missingWiring(target, shipped);
|
|
@@ -71,7 +60,7 @@ export async function wire(argv, ctx) {
|
|
|
71
60
|
for (const line of fragment.split('\n')) console.log(` ${line}`);
|
|
72
61
|
}
|
|
73
62
|
console.log(`\n \`nina wire${arg('--to') ? ` --to ${version}` : ''} --apply\` merges them, changing nothing else — a customised command is never touched.`);
|
|
74
|
-
if (missing
|
|
63
|
+
if (lacksPackage(missing)) {
|
|
75
64
|
console.log(' The package it cannot install: that one is `pnpm add -D -E @xhulz/nina`.');
|
|
76
65
|
}
|
|
77
66
|
console.log(`wire: ${missing.length} missing${stale.length ? `, ${stale.length} to update` : ''}`);
|
package/src/deep.mjs
CHANGED
|
@@ -30,7 +30,7 @@ const REPORT_CHARS = 1500;
|
|
|
30
30
|
/** The default model: grouping sentences, not reviewing code. */
|
|
31
31
|
const MODEL = 'haiku';
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
const MAP_SCHEMA = {
|
|
34
34
|
type: 'object',
|
|
35
35
|
properties: {
|
|
36
36
|
causes: {
|
|
@@ -45,7 +45,7 @@ export const MAP_SCHEMA = {
|
|
|
45
45
|
required: ['causes'],
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
const REDUCE_SCHEMA = {
|
|
49
49
|
type: 'object',
|
|
50
50
|
properties: {
|
|
51
51
|
clusters: {
|
|
@@ -134,7 +134,7 @@ export function mapPrompt(batch) {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
/** The reduce prompt: the causes grouped, set against the pills already written. */
|
|
137
|
-
|
|
137
|
+
function reducePrompt(causes, known) {
|
|
138
138
|
return [
|
|
139
139
|
'Below are root causes of work a software pipeline sent back, one per report, and the lessons ("pills") already',
|
|
140
140
|
'written for it. Group the causes that are the same mistake. For each group give the refs, the pill that already',
|
package/src/gate.mjs
CHANGED
|
@@ -54,11 +54,12 @@
|
|
|
54
54
|
* gate that could block work on its own crash would be worse than the loops it exists to stop.
|
|
55
55
|
*/
|
|
56
56
|
|
|
57
|
+
import { spawnSync } from 'node:child_process';
|
|
57
58
|
import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, realpathSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
58
59
|
import { dirname, join } from 'node:path';
|
|
59
60
|
import { fileURLToPath } from 'node:url';
|
|
60
61
|
import { TERMINALS, declaredTokens, parseGraph } from './graph.mjs';
|
|
61
|
-
import { gateDir, slugFor } from './paths.mjs';
|
|
62
|
+
import { gateDir, layerRootFor, readProfile, slugFor } from './paths.mjs';
|
|
62
63
|
import { declaredIssues, isLoopBack, saidToAgent } from './transcripts.mjs';
|
|
63
64
|
import { lessonIndex } from './lessons.mjs';
|
|
64
65
|
|
|
@@ -134,12 +135,11 @@ export function ledgerPath(root, session) {
|
|
|
134
135
|
* @param {string} [pkg] - The NINA install to look in.
|
|
135
136
|
* @returns {boolean}
|
|
136
137
|
*/
|
|
137
|
-
|
|
138
|
+
function pinnedShipsGate(root, pkg = PACKAGE) {
|
|
138
139
|
try {
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
return existsSync(join(layers, 'core', 'tree', 'scripts', 'loop-gate.mjs'));
|
|
140
|
+
const { profile } = readProfile(root);
|
|
141
|
+
const layers = profile ? layerRootFor(pkg, profile.core) : { error: 'no profile' };
|
|
142
|
+
return !layers.error && existsSync(join(layers.dir, 'core', 'tree', 'scripts', 'loop-gate.mjs'));
|
|
143
143
|
} catch {
|
|
144
144
|
return false;
|
|
145
145
|
}
|
|
@@ -432,7 +432,18 @@ export function replay(entries, loops, forward = new Map()) {
|
|
|
432
432
|
}
|
|
433
433
|
}
|
|
434
434
|
});
|
|
435
|
-
|
|
435
|
+
// What is open when the replay ends: each loop with its rounds, the rounds the owner's replies gave it and the
|
|
436
|
+
// issues it counted by, and each loop-back no fix has acted on yet — what a compaction takes from the orchestrator.
|
|
437
|
+
const open = [...streak.entries()]
|
|
438
|
+
.filter(([key]) => key.split('|').length === 2)
|
|
439
|
+
.map(([key, s]) => {
|
|
440
|
+
const [source, token] = key.split('|');
|
|
441
|
+
const cap = Math.max(0, ...(loops.get(source)?.get(token)?.values() ?? []));
|
|
442
|
+
const issues = [...streak.keys()].filter((k) => k.startsWith(`${key}|`)).map((k) => k.slice(key.length + 1));
|
|
443
|
+
return { source, token, rounds: s.n, cap, given: grace.get(key) ?? 0, issues };
|
|
444
|
+
});
|
|
445
|
+
const waiting = [...pending.entries()].flatMap(([source, batch]) => batch.filter((v) => isLoopBack(v.verdict)).map((v) => ({ source, verdict: v.verdict, issues: v.issues ?? [] })));
|
|
446
|
+
return { effect: (to) => effect(to, entries.length), rounds: made, open, waiting };
|
|
436
447
|
}
|
|
437
448
|
|
|
438
449
|
/**
|
|
@@ -660,6 +671,40 @@ function onPre(input, project, path, at) {
|
|
|
660
671
|
return confirmation(worst);
|
|
661
672
|
}
|
|
662
673
|
|
|
674
|
+
/**
|
|
675
|
+
* What the gate hands the orchestrator after its conversation was compacted: the loops still open and the
|
|
676
|
+
* loop-backs waiting on a fix, the latest dispatches, where the checkout stands, and the two documents to read
|
|
677
|
+
* again. The graph and the router are read once, at a session's start, and a compaction drops them with every
|
|
678
|
+
* report; the router told the orchestrator to write "round 2 of max 2" in a dispatch so "you after a
|
|
679
|
+
* compaction" could see it, and after a compaction it sees only a summary.
|
|
680
|
+
*
|
|
681
|
+
* @param {object} project - `loadProject`'s.
|
|
682
|
+
* @param {string} path - The session's ledger.
|
|
683
|
+
* @param {string} root - The project.
|
|
684
|
+
* @returns {object}
|
|
685
|
+
*/
|
|
686
|
+
function afterCompaction(project, path, root) {
|
|
687
|
+
const entries = readLedger(path);
|
|
688
|
+
const { open, waiting } = replay(entries, project.loops, project.forward);
|
|
689
|
+
const lines = ['The conversation was compacted. What the NINA loop gate kept of this session:'];
|
|
690
|
+
for (const l of open) {
|
|
691
|
+
lines.push(
|
|
692
|
+
`- open loop: ${l.source} ${l.token} — ${l.rounds} round(s), capped at ${l.cap}${l.given ? ` and ${l.given} more from the owner's replies` : ''}` +
|
|
693
|
+
(l.issues.length ? `, issues ${l.issues.join(', ')}` : ''),
|
|
694
|
+
);
|
|
695
|
+
}
|
|
696
|
+
for (const w of waiting) lines.push(`- waiting on a fix: ${w.source} ${w.verdict}${w.issues.length ? `, issues ${w.issues.join(', ')}` : ''}`);
|
|
697
|
+
if (open.length === 0 && waiting.length === 0) lines.push('- no loop is open');
|
|
698
|
+
const dispatched = entries.filter((e) => e.k === 'dispatch' && e.role).slice(-4);
|
|
699
|
+
if (dispatched.length) lines.push(`- the latest dispatches: ${dispatched.map((e) => e.role).join(', ')}, the newest at ${String(dispatched.at(-1).at ?? '').slice(11, 16)} UTC`);
|
|
700
|
+
const git = (...args) => spawnSync('git', ['-C', root, ...args], { encoding: 'utf8', timeout: 2000 }).stdout?.trim() ?? '';
|
|
701
|
+
const branch = git('rev-parse', '--abbrev-ref', 'HEAD');
|
|
702
|
+
const last = git('log', '-1', '--format=%h %s');
|
|
703
|
+
if (branch) lines.push(`- the checkout: branch ${branch}${last ? `, last commit ${last.slice(0, 100)}` : ''}`);
|
|
704
|
+
lines.push('Before the next dispatch, read .claude/graph.md and .claude/router.md again: they were read at the start of the session, and are no longer in your context.');
|
|
705
|
+
return { hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: lines.join('\n') } };
|
|
706
|
+
}
|
|
707
|
+
|
|
663
708
|
/** Drops a project's ledgers nobody has written to in a month, and error lines older than a week. */
|
|
664
709
|
function prune(root, now) {
|
|
665
710
|
const dir = projectGateDir(root);
|
|
@@ -719,6 +764,8 @@ export function handle(input, { root, now = new Date(), pkg = PACKAGE }) {
|
|
|
719
764
|
return null;
|
|
720
765
|
case 'SubagentStop':
|
|
721
766
|
return onStop(input, project, path, at);
|
|
767
|
+
case 'SessionStart':
|
|
768
|
+
return input.source === 'compact' ? afterCompaction(project, path, root) : null;
|
|
722
769
|
case 'SubagentStart': {
|
|
723
770
|
// Not the loop's business, but the one hook every stage passes through as it begins: its lessons. Claude
|
|
724
771
|
// Code reads `additionalContext` from this event's `hookSpecificOutput` into the subagent's context as it
|
package/src/graph.mjs
CHANGED
|
@@ -102,7 +102,7 @@ export function declaredTokens(spec) {
|
|
|
102
102
|
* @param {Set<string>} known - Every stage name that could be meant.
|
|
103
103
|
* @returns {Set<string>}
|
|
104
104
|
*/
|
|
105
|
-
|
|
105
|
+
function proseRoutes(spec, known) {
|
|
106
106
|
const routes = new Set();
|
|
107
107
|
for (const m of spec.matchAll(/(?:back to|→)\s+\*\*([A-Za-z-]+)\*\*/g)) {
|
|
108
108
|
const name = m[1].toLowerCase();
|