@xhulz/nina 0.47.0 → 0.49.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 +18 -3
- package/package.json +1 -1
- package/releases/0.48.0/core/GAPS.md +125 -0
- package/releases/0.48.0/core/answered.json +44 -0
- package/releases/0.48.0/core/defaults/tree/.claude/agents/architect.md +1 -0
- package/releases/0.48.0/core/defaults/tree/.claude/agents/dba.md +3 -0
- package/releases/0.48.0/core/defaults/tree/.claude/agents/devops.md +1 -0
- package/releases/0.48.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
- package/releases/0.48.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.48.0/core/defaults/tree/.claude/agents/qa.md +7 -0
- package/releases/0.48.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
- package/releases/0.48.0/core/defaults/tree/.claude/agents/secops.md +3 -0
- package/releases/0.48.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
- package/releases/0.48.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
- package/releases/0.48.0/core/tree/.claude/agents/architect.md +127 -0
- package/releases/0.48.0/core/tree/.claude/agents/dba.md +117 -0
- package/releases/0.48.0/core/tree/.claude/agents/devops.md +118 -0
- package/releases/0.48.0/core/tree/.claude/agents/implementer.md +160 -0
- package/releases/0.48.0/core/tree/.claude/agents/integration-tester.md +168 -0
- package/releases/0.48.0/core/tree/.claude/agents/planner.md +132 -0
- package/releases/0.48.0/core/tree/.claude/agents/qa.md +150 -0
- package/releases/0.48.0/core/tree/.claude/agents/reviewer.md +183 -0
- package/releases/0.48.0/core/tree/.claude/agents/secops.md +120 -0
- package/releases/0.48.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
- package/releases/0.48.0/core/tree/.claude/agents/solidity-dev.md +123 -0
- package/releases/0.48.0/core/tree/.claude/agents-overview.md +64 -0
- package/releases/0.48.0/core/tree/.claude/frontend.md +10 -0
- package/releases/0.48.0/core/tree/.claude/graph.md +75 -0
- package/releases/0.48.0/core/tree/.claude/patterns.md +251 -0
- package/releases/0.48.0/core/tree/.claude/pills/README.md +147 -0
- package/releases/0.48.0/core/tree/.claude/pipeline.md +101 -0
- package/releases/0.48.0/core/tree/.claude/retrieval.md +103 -0
- package/releases/0.48.0/core/tree/.claude/router.md +202 -0
- package/releases/0.48.0/core/tree/.claude/templates/integration.md +45 -0
- package/releases/0.48.0/core/tree/CLAUDE.md +164 -0
- package/releases/0.48.0/core/tree/scripts/cost-watch.mjs +25 -0
- package/releases/0.48.0/core/tree/scripts/edit-guard.mjs +24 -0
- package/releases/0.48.0/core/tree/scripts/harness-check.mjs +71 -0
- package/releases/0.48.0/core/tree/scripts/loop-gate.mjs +19 -0
- package/releases/0.48.0/core/vocabulary.json +13 -0
- package/releases/0.48.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
- package/releases/0.48.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
- package/releases/0.48.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
- package/releases/0.48.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
- package/releases/0.48.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
- package/releases/0.48.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
- package/releases/0.48.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
- package/releases/0.48.0/surfaces/blockchain/tree/.claude/router.md +6 -0
- package/releases/0.48.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
- package/releases/0.48.0/surfaces/d1/tree/.claude/agents/dba.md +13 -0
- package/releases/0.48.0/surfaces/d1/tree/.claude/agents/devops.md +2 -0
- package/releases/0.48.0/surfaces/d1/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.48.0/surfaces/d1/tree/.claude/patterns.md +8 -0
- package/releases/0.48.0/surfaces/d1/tree/CLAUDE.md +2 -0
- package/releases/0.48.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
- package/releases/0.48.0/surfaces/db/tree/.claude/agents/dba.md +54 -0
- package/releases/0.48.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
- package/releases/0.48.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.48.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
- package/releases/0.48.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
- package/releases/0.48.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
- package/releases/0.48.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
- package/releases/0.48.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
- package/releases/0.48.0/surfaces/db/tree/.claude/graph.md +8 -0
- package/releases/0.48.0/surfaces/db/tree/.claude/patterns.md +22 -0
- package/releases/0.48.0/surfaces/db/tree/.claude/pipeline.md +13 -0
- package/releases/0.48.0/surfaces/db/tree/.claude/retrieval.md +5 -0
- package/releases/0.48.0/surfaces/db/tree/.claude/router.md +4 -0
- package/releases/0.48.0/surfaces/db/tree/CLAUDE.md +17 -0
- package/releases/0.48.0/surfaces/drizzle/tree/.claude/agents/dba.md +9 -0
- package/releases/0.48.0/surfaces/drizzle/tree/.claude/agents/devops.md +5 -0
- package/releases/0.48.0/surfaces/drizzle/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.48.0/surfaces/drizzle/tree/.claude/patterns.md +7 -0
- package/releases/0.48.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
- package/releases/0.48.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
- package/releases/0.48.0/surfaces/edge-cf/tree/.claude/agents/devops.md +29 -0
- package/releases/0.48.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
- package/releases/0.48.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
- package/releases/0.48.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
- package/releases/0.48.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.48.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
- package/releases/0.48.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
- package/releases/0.48.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
- package/releases/0.48.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
- package/releases/0.48.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
- package/releases/0.48.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
- package/releases/0.48.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
- package/releases/0.48.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
- package/releases/0.48.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
- package/releases/0.48.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
- package/releases/0.48.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
- package/releases/0.48.0/surfaces/frontend/tree/.claude/router.md +14 -0
- package/releases/0.48.0/surfaces/frontend/tree/CLAUDE.md +5 -0
- package/releases/0.48.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
- package/releases/0.48.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
- package/releases/0.48.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
- package/releases/0.48.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
- package/releases/0.48.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
- package/releases/0.48.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
- package/releases/0.48.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
- package/releases/0.48.0/surfaces/integrations/tree/.claude/graph.md +10 -0
- package/releases/0.48.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
- package/releases/0.48.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
- package/releases/0.48.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
- package/releases/0.48.0/surfaces/integrations/tree/.claude/router.md +6 -0
- package/releases/0.48.0/surfaces/integrations/tree/CLAUDE.md +17 -0
- package/releases/0.48.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
- package/releases/0.48.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
- package/releases/0.48.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
- package/releases/0.48.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.48.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
- package/releases/0.48.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
- package/releases/0.48.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
- package/releases/0.48.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
- package/releases/0.48.0/surfaces/money/tree/.claude/graph.md +2 -0
- package/releases/0.48.0/surfaces/money/tree/.claude/patterns.md +49 -0
- package/releases/0.48.0/surfaces/money/tree/.claude/pipeline.md +2 -0
- package/releases/0.48.0/surfaces/money/tree/.claude/retrieval.md +2 -0
- package/releases/0.48.0/surfaces/money/tree/CLAUDE.md +11 -0
- package/releases/0.48.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
- package/releases/0.48.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
- package/releases/0.48.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.48.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
- package/releases/0.48.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
- package/releases/0.48.0/surfaces/pii/tree/CLAUDE.md +5 -0
- package/releases/0.48.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
- package/releases/0.48.0/surfaces/prisma/tree/.claude/agents/dba.md +42 -0
- package/releases/0.48.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
- package/releases/0.48.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.48.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
- package/releases/0.48.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
- package/releases/0.48.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
- package/releases/0.48.0/surfaces/prisma/tree/CLAUDE.md +5 -0
- package/releases/0.49.0/core/GAPS.md +125 -0
- package/releases/0.49.0/core/answered.json +53 -0
- package/releases/0.49.0/core/defaults/tree/.claude/agents/architect.md +1 -0
- package/releases/0.49.0/core/defaults/tree/.claude/agents/dba.md +3 -0
- package/releases/0.49.0/core/defaults/tree/.claude/agents/devops.md +1 -0
- package/releases/0.49.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
- package/releases/0.49.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.49.0/core/defaults/tree/.claude/agents/qa.md +7 -0
- package/releases/0.49.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
- package/releases/0.49.0/core/defaults/tree/.claude/agents/secops.md +3 -0
- package/releases/0.49.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
- package/releases/0.49.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
- package/releases/0.49.0/core/tree/.claude/agents/architect.md +129 -0
- package/releases/0.49.0/core/tree/.claude/agents/dba.md +117 -0
- package/releases/0.49.0/core/tree/.claude/agents/devops.md +118 -0
- package/releases/0.49.0/core/tree/.claude/agents/implementer.md +162 -0
- package/releases/0.49.0/core/tree/.claude/agents/integration-tester.md +168 -0
- package/releases/0.49.0/core/tree/.claude/agents/planner.md +132 -0
- package/releases/0.49.0/core/tree/.claude/agents/qa.md +150 -0
- package/releases/0.49.0/core/tree/.claude/agents/reviewer.md +184 -0
- package/releases/0.49.0/core/tree/.claude/agents/secops.md +120 -0
- package/releases/0.49.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
- package/releases/0.49.0/core/tree/.claude/agents/solidity-dev.md +123 -0
- package/releases/0.49.0/core/tree/.claude/agents-overview.md +64 -0
- package/releases/0.49.0/core/tree/.claude/frontend.md +10 -0
- package/releases/0.49.0/core/tree/.claude/graph.md +75 -0
- package/releases/0.49.0/core/tree/.claude/patterns.md +251 -0
- package/releases/0.49.0/core/tree/.claude/pills/README.md +147 -0
- package/releases/0.49.0/core/tree/.claude/pipeline.md +101 -0
- package/releases/0.49.0/core/tree/.claude/retrieval.md +103 -0
- package/releases/0.49.0/core/tree/.claude/router.md +202 -0
- package/releases/0.49.0/core/tree/.claude/templates/integration.md +45 -0
- package/releases/0.49.0/core/tree/CLAUDE.md +164 -0
- package/releases/0.49.0/core/tree/scripts/cost-watch.mjs +25 -0
- package/releases/0.49.0/core/tree/scripts/edit-guard.mjs +24 -0
- package/releases/0.49.0/core/tree/scripts/harness-check.mjs +71 -0
- package/releases/0.49.0/core/tree/scripts/loop-gate.mjs +19 -0
- package/releases/0.49.0/core/vocabulary.json +13 -0
- package/releases/0.49.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
- package/releases/0.49.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
- package/releases/0.49.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
- package/releases/0.49.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
- package/releases/0.49.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
- package/releases/0.49.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
- package/releases/0.49.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
- package/releases/0.49.0/surfaces/blockchain/tree/.claude/router.md +6 -0
- package/releases/0.49.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
- package/releases/0.49.0/surfaces/d1/tree/.claude/agents/dba.md +13 -0
- package/releases/0.49.0/surfaces/d1/tree/.claude/agents/devops.md +2 -0
- package/releases/0.49.0/surfaces/d1/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.49.0/surfaces/d1/tree/.claude/patterns.md +8 -0
- package/releases/0.49.0/surfaces/d1/tree/CLAUDE.md +2 -0
- package/releases/0.49.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
- package/releases/0.49.0/surfaces/db/tree/.claude/agents/dba.md +54 -0
- package/releases/0.49.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
- package/releases/0.49.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.49.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
- package/releases/0.49.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
- package/releases/0.49.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
- package/releases/0.49.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
- package/releases/0.49.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
- package/releases/0.49.0/surfaces/db/tree/.claude/graph.md +8 -0
- package/releases/0.49.0/surfaces/db/tree/.claude/patterns.md +22 -0
- package/releases/0.49.0/surfaces/db/tree/.claude/pipeline.md +13 -0
- package/releases/0.49.0/surfaces/db/tree/.claude/retrieval.md +5 -0
- package/releases/0.49.0/surfaces/db/tree/.claude/router.md +4 -0
- package/releases/0.49.0/surfaces/db/tree/CLAUDE.md +17 -0
- package/releases/0.49.0/surfaces/drizzle/tree/.claude/agents/dba.md +9 -0
- package/releases/0.49.0/surfaces/drizzle/tree/.claude/agents/devops.md +5 -0
- package/releases/0.49.0/surfaces/drizzle/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.49.0/surfaces/drizzle/tree/.claude/patterns.md +7 -0
- package/releases/0.49.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
- package/releases/0.49.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
- package/releases/0.49.0/surfaces/edge-cf/tree/.claude/agents/devops.md +29 -0
- package/releases/0.49.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
- package/releases/0.49.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
- package/releases/0.49.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
- package/releases/0.49.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.49.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
- package/releases/0.49.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
- package/releases/0.49.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
- package/releases/0.49.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
- package/releases/0.49.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
- package/releases/0.49.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
- package/releases/0.49.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
- package/releases/0.49.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
- package/releases/0.49.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
- package/releases/0.49.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
- package/releases/0.49.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
- package/releases/0.49.0/surfaces/frontend/tree/.claude/router.md +14 -0
- package/releases/0.49.0/surfaces/frontend/tree/CLAUDE.md +5 -0
- package/releases/0.49.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
- package/releases/0.49.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
- package/releases/0.49.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
- package/releases/0.49.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
- package/releases/0.49.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
- package/releases/0.49.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
- package/releases/0.49.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
- package/releases/0.49.0/surfaces/integrations/tree/.claude/graph.md +10 -0
- package/releases/0.49.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
- package/releases/0.49.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
- package/releases/0.49.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
- package/releases/0.49.0/surfaces/integrations/tree/.claude/router.md +6 -0
- package/releases/0.49.0/surfaces/integrations/tree/CLAUDE.md +17 -0
- package/releases/0.49.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
- package/releases/0.49.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
- package/releases/0.49.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
- package/releases/0.49.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.49.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
- package/releases/0.49.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
- package/releases/0.49.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
- package/releases/0.49.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
- package/releases/0.49.0/surfaces/money/tree/.claude/graph.md +2 -0
- package/releases/0.49.0/surfaces/money/tree/.claude/patterns.md +49 -0
- package/releases/0.49.0/surfaces/money/tree/.claude/pipeline.md +2 -0
- package/releases/0.49.0/surfaces/money/tree/.claude/retrieval.md +2 -0
- package/releases/0.49.0/surfaces/money/tree/CLAUDE.md +11 -0
- package/releases/0.49.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
- package/releases/0.49.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
- package/releases/0.49.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.49.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
- package/releases/0.49.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
- package/releases/0.49.0/surfaces/pii/tree/CLAUDE.md +5 -0
- package/releases/0.49.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
- package/releases/0.49.0/surfaces/prisma/tree/.claude/agents/dba.md +42 -0
- package/releases/0.49.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
- package/releases/0.49.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.49.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
- package/releases/0.49.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
- package/releases/0.49.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
- package/releases/0.49.0/surfaces/prisma/tree/CLAUDE.md +5 -0
- package/src/commands/check.mjs +10 -0
- package/src/commands/gate.mjs +47 -2
- package/src/commands/init.mjs +9 -2
- package/src/commands/pipeline.mjs +22 -1
- package/src/commands/upgrade.mjs +8 -0
- package/src/gate.mjs +106 -22
- package/src/office.html +1069 -0
- package/src/office.mjs +434 -0
- package/src/wiring.mjs +11 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!-- nina:slot prisma.1 -->
|
|
2
|
+
- **Database table/column names** (Prisma `@map` / `@@map`): `snake_case` in SQL, `camelCase` in TS.
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot prisma.2 -->
|
|
5
|
+
- Where Prisma reads are cached through Accelerate, `cacheStrategy` lives in `{{DB_PKG}}`: services do not add, strip, or override cache policies.
|
|
6
|
+
|
|
7
|
+
<!-- nina:slot prisma.3 -->
|
|
8
|
+
|
|
9
|
+
### Prisma
|
|
10
|
+
|
|
11
|
+
- Schema lives in `{{DB_PKG}}/prisma/schema.prisma`. Nowhere else.
|
|
12
|
+
- **Never write raw SQL** unless justified in a code comment with perf data.
|
|
13
|
+
- N+1 is solved with `include` / `select`, chosen deliberately.
|
|
14
|
+
- Where reads are cached through Accelerate, every `findUnique` / `findMany` on a hot path defines its `cacheStrategy`, and no direct connection string is embedded in code.
|
|
15
|
+
- Tests run a real Prisma client against a real database; the client is never mocked.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!-- nina:slot prisma.1 -->
|
|
2
|
+
| Run any `prisma` CLI command (the database gate, devops, and the integration gate where there is one) | `prisma-cli` | flags differ between versions; `migrate diff --shadow-database-url` against a live DB wiped dev/staging once |
|
|
3
|
+
|
|
4
|
+
<!-- nina:slot prisma.2 -->
|
|
5
|
+
| Write or audit a Prisma query (architect, implementer, the database gate, reviewer) | `prisma-client-api` | filters, operators, `$transaction` semantics. a caching or edge layer on top of the client is **NOT covered** — its cache strategy belongs to that layer's own integration doc |
|
package/src/commands/check.mjs
CHANGED
|
@@ -297,6 +297,16 @@ export async function check(argv, ctx) {
|
|
|
297
297
|
notes.push(`${label} names boundary "${entry.boundary}", which does not exist yet`);
|
|
298
298
|
}
|
|
299
299
|
}
|
|
300
|
+
// The other direction: a doc no integration declares is retired, waits for its boundary, or lost its
|
|
301
|
+
// declaration, and nothing told a reader which. Said as a note, and as a deferral once the profile says why.
|
|
302
|
+
const declaredSlugs = new Set(integrations.map((entry) => entry?.slug));
|
|
303
|
+
const docs = await readdir(join(target, '.claude', 'integrations')).catch(() => []);
|
|
304
|
+
for (const doc of docs.filter((name) => name.endsWith('.md') && !declaredSlugs.has(name.slice(0, -'.md'.length))).sort()) {
|
|
305
|
+
const key = `.claude/integrations/${doc}`;
|
|
306
|
+
const why = deferredWhy(profile, key);
|
|
307
|
+
if (why) deferredUsed.add(key);
|
|
308
|
+
notes.push(why ? `deferred: ${key} — ${why}` : `${key} documents no declared integration — declare it, or say under "deferred" why not: retired, or waiting for its boundary`);
|
|
309
|
+
}
|
|
300
310
|
|
|
301
311
|
const slots = await projectSlots(resolved.dir, surfaces);
|
|
302
312
|
const filled = await filledSlots(target);
|
package/src/commands/gate.mjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* `nina gate` — the loop gate's own check, and a way to run it by hand.
|
|
3
3
|
*
|
|
4
4
|
* nina gate --selftest is the gate wired, has it failed, would it still hold a loop past its cap?
|
|
5
|
+
* nina gate --status the loops a session holds open, and what would close each
|
|
5
6
|
* nina gate --hook handle one hook event from stdin, the way the composed script does
|
|
6
7
|
*
|
|
7
8
|
* The gate lets every call through when anything goes wrong, which is right for a hook and makes a
|
|
@@ -20,7 +21,7 @@ import { tmpdir } from 'node:os';
|
|
|
20
21
|
import { join, resolve } from 'node:path';
|
|
21
22
|
import { readProfile, slugFor } from '../paths.mjs';
|
|
22
23
|
import { GATE, hookCommand, missingWiring, shippedScripts } from '../wiring.mjs';
|
|
23
|
-
import { LEDGER_TAIL, ledgerPath, loadProject, projectGateDir, readLedger, runGate } from '../gate.mjs';
|
|
24
|
+
import { LEDGER_TAIL, ledgerPath, loadProject, loopState, projectGateDir, readLedger, runGate, sessionLedgers } from '../gate.mjs';
|
|
24
25
|
import { projectRecords } from '../store.mjs';
|
|
25
26
|
import { layerRootFor } from './compose.mjs';
|
|
26
27
|
import { handsToModel } from '../detectors.mjs';
|
|
@@ -271,13 +272,57 @@ function dryRun(target) {
|
|
|
271
272
|
}
|
|
272
273
|
|
|
273
274
|
/**
|
|
274
|
-
*
|
|
275
|
+
* What the gate holds of one session: its loops, what would close each, and the loop-backs waiting on a fix —
|
|
276
|
+
* the same lines a compaction hands the orchestrator, at any time. The session is the one whose ledger was
|
|
277
|
+
* written last, which is the running one while it dispatches, or the one `--session` names by the start of its id.
|
|
278
|
+
*
|
|
279
|
+
* @param {string} target - The project directory.
|
|
280
|
+
* @param {string|undefined} wanted - The start of a session id, or nothing for the latest.
|
|
281
|
+
* @returns {number} Process exit code.
|
|
282
|
+
*/
|
|
283
|
+
function loopStatus(target, wanted) {
|
|
284
|
+
const project = loadProject(target);
|
|
285
|
+
if (!project) {
|
|
286
|
+
console.error(' .claude/graph.md is not composed, so the gate has no loops to hold — run `nina compose`\n');
|
|
287
|
+
return 2;
|
|
288
|
+
}
|
|
289
|
+
const { dir, ledgers, picked, why } = sessionLedgers(target, wanted);
|
|
290
|
+
if (ledgers.length === 0 && !wanted) {
|
|
291
|
+
console.log(` the gate has no ledger here yet: no session has dispatched a stage since it was wired (${dir})`);
|
|
292
|
+
console.log('gate: no open loop');
|
|
293
|
+
return 0;
|
|
294
|
+
}
|
|
295
|
+
if (!picked) {
|
|
296
|
+
console.error(` ${why} (${dir})\n`);
|
|
297
|
+
return 2;
|
|
298
|
+
}
|
|
299
|
+
const { session, written, path } = picked;
|
|
300
|
+
const state = loopState(project, readLedger(path));
|
|
301
|
+
const others = ledgers.length > 1 ? `; ${ledgers.length - 1} other session(s) kept here, --session <id> for one` : '';
|
|
302
|
+
console.log(` session ${session}, its ledger last written ${written.toISOString().slice(0, 16).replace('T', ' ')} UTC${others}`);
|
|
303
|
+
for (const line of state.lines) console.log(` ${line}`);
|
|
304
|
+
const counts = [
|
|
305
|
+
state.open ? `${state.open} open loop(s)` : 'no open loop',
|
|
306
|
+
state.closing ? `${state.closing} settled, dropped at the next dispatch` : '',
|
|
307
|
+
state.waiting ? `${state.waiting} loop-back(s) waiting on a fix` : '',
|
|
308
|
+
];
|
|
309
|
+
console.log(`gate: ${counts.filter(Boolean).join(', ')}`);
|
|
310
|
+
return 0;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* @param {string[]} argv - `[--selftest | --status [--session <id>] | --hook] [--project <dir>]`.
|
|
275
315
|
* @param {{root: string}} ctx - CLI context; `root` is the NINA install directory.
|
|
276
316
|
* @returns {Promise<number>} Process exit code.
|
|
277
317
|
*/
|
|
278
318
|
export async function gate(argv, ctx) {
|
|
279
319
|
const target = resolve(argv.includes('--project') ? argv[argv.indexOf('--project') + 1] : '.');
|
|
280
320
|
if (argv.includes('--hook')) return runGate({ root: target });
|
|
321
|
+
if (argv.includes('--status')) return loopStatus(target, argv.includes('--session') ? argv[argv.indexOf('--session') + 1] : undefined);
|
|
322
|
+
if (argv.includes('--session')) {
|
|
323
|
+
console.error(' --session names the session --status reads, and means nothing without it\n');
|
|
324
|
+
return 2;
|
|
325
|
+
}
|
|
281
326
|
|
|
282
327
|
const { profile, error } = readProfile(target);
|
|
283
328
|
if (error) {
|
package/src/commands/init.mjs
CHANGED
|
@@ -218,14 +218,21 @@ async function interview(available, matched, impacts) {
|
|
|
218
218
|
const scope = lines.join(' ');
|
|
219
219
|
|
|
220
220
|
const order = available;
|
|
221
|
-
console.log(`\n ${strong('Surfaces')} — ${order.length} yes-or-no questions, one per surface.`);
|
|
221
|
+
console.log(`\n ${strong('Surfaces')} — up to ${order.length} yes-or-no questions, one per surface; a stack comes only after a yes to what it needs.`);
|
|
222
222
|
console.log(' A surface is a part of the harness only some projects need: its rules, its checks, sometimes a');
|
|
223
223
|
console.log(' whole role. Answer for what the project does today; one can be added later in .nina/profile.json.\n');
|
|
224
224
|
const surfaces = [];
|
|
225
|
+
let asked = 0;
|
|
225
226
|
for (const [n, s] of order.entries()) {
|
|
226
227
|
// A stack is asked about only where its concern was answered yes: asked whether data it does not
|
|
227
228
|
// own goes through Prisma, a project has nothing true to answer.
|
|
228
229
|
if (missingNeeds(s, surfaces).length > 0) continue;
|
|
230
|
+
// Counted by what is asked, not by place in the list: numbered by place, a no to the database read
|
|
231
|
+
// 1/10 then 4/10, and the header promised ten questions to someone asked seven. The total is what is
|
|
232
|
+
// asked so far plus what can still be, so a no shrinks it; the last question is n/n while the order
|
|
233
|
+
// ends with a surface that needs nothing, as it does.
|
|
234
|
+
asked += 1;
|
|
235
|
+
const total = asked + order.slice(n + 1).filter((t) => missingNeeds(t, [...surfaces, ...order.slice(n)]).length === 0).length;
|
|
229
236
|
const i = impacts[s];
|
|
230
237
|
const reason = matched.find((d) => d.surface === s)?.why;
|
|
231
238
|
// What a yes brings, in what a person would recognise: roles, rules, agents. The count of
|
|
@@ -235,7 +242,7 @@ async function interview(available, matched, impacts) {
|
|
|
235
242
|
...(i.rules ? [`${i.rules} hard rule${i.rules > 1 ? 's' : ''}`] : []),
|
|
236
243
|
...(i.agents.length ? [`changes to ${i.agents.length} agent${i.agents.length > 1 ? 's' : ''}`] : []),
|
|
237
244
|
];
|
|
238
|
-
console.log(` ${faint(`${
|
|
245
|
+
console.log(` ${faint(`${asked}/${total}`)} ${strong(s)}`);
|
|
239
246
|
console.log(` ${questionFor(s)}`);
|
|
240
247
|
console.log(faint(` if yes: ${brings.length ? brings.join(' · ') : `guidance in ${i.fragments} places`}`));
|
|
241
248
|
if (reason) console.log(found(` already here: ${reason}`));
|
|
@@ -168,7 +168,8 @@ export function historyOf(records, since) {
|
|
|
168
168
|
/**
|
|
169
169
|
* Runs the command.
|
|
170
170
|
*
|
|
171
|
-
* @param {string[]} argv - `[--project <dir>] [--for <number or words>] [--since <YYYY-MM-DD>]
|
|
171
|
+
* @param {string[]} argv - `[--project <dir>] [--for <number or words>] [--since <YYYY-MM-DD>]`, or `--view [--session <id>]
|
|
172
|
+
* [--replay] [--port <n>] [--open]` for the office.
|
|
172
173
|
* @returns {Promise<number>} Process exit code.
|
|
173
174
|
*/
|
|
174
175
|
export async function pipeline(argv) {
|
|
@@ -179,6 +180,26 @@ export async function pipeline(argv) {
|
|
|
179
180
|
console.error(` no .claude/graph.md in ${dir} — this draws a composed pipeline: \`nina compose\` writes one`);
|
|
180
181
|
return 1;
|
|
181
182
|
}
|
|
183
|
+
// The office: the same graph as a room, its agents at their desks, live or replayed (src/office.mjs).
|
|
184
|
+
const viewOnly = ['--session', '--replay', '--port', '--open'].filter((flag) => argv.includes(flag));
|
|
185
|
+
if (viewOnly.length > 0 && !argv.includes('--view')) {
|
|
186
|
+
console.error(` ${viewOnly.join(', ')} ${viewOnly.length > 1 ? 'belong' : 'belongs'} to the office, and mean${viewOnly.length > 1 ? '' : 's'} nothing without --view`);
|
|
187
|
+
return 2;
|
|
188
|
+
}
|
|
189
|
+
if (argv.includes('--view')) {
|
|
190
|
+
const drawingOnly = ['--for', '--since'].filter((flag) => argv.includes(flag));
|
|
191
|
+
if (drawingOnly.length > 0) {
|
|
192
|
+
console.error(` ${drawingOnly.join(', ')} ${drawingOnly.length > 1 ? 'are' : 'is'} for the drawing, not the office`);
|
|
193
|
+
return 2;
|
|
194
|
+
}
|
|
195
|
+
const { viewOffice } = await import('../office.mjs');
|
|
196
|
+
const port = arg('--port');
|
|
197
|
+
if (port !== null && !(/^\d+$/.test(port) && Number(port) <= 65535)) {
|
|
198
|
+
console.error(` --port takes a port number, 0 to 65535, and was given ${port}`);
|
|
199
|
+
return 2;
|
|
200
|
+
}
|
|
201
|
+
return viewOffice({ target: dir, wanted: arg('--session') || undefined, replay: argv.includes('--replay'), port: port === null ? undefined : Number(port), open: argv.includes('--open') });
|
|
202
|
+
}
|
|
182
203
|
const graphText = readFileSync(graphPath, 'utf8');
|
|
183
204
|
const graph = parseGraph(graphText);
|
|
184
205
|
const words = stageWords(graphText);
|
package/src/commands/upgrade.mjs
CHANGED
|
@@ -364,6 +364,14 @@ export async function upgrade(argv, ctx) {
|
|
|
364
364
|
const scriptsBefore = await shippedScripts(from.dir, surfaces);
|
|
365
365
|
const newScripts = new Set([...(await shippedScripts(onto.dir, kept))].filter((s) => !scriptsBefore.has(s)));
|
|
366
366
|
const unwired = newScripts.size > 0 ? await missingWiring(target, newScripts) : [];
|
|
367
|
+
// A script already run whose hooks this NINA needs more of — a matcher that grew — holds what it held, so it does
|
|
368
|
+
// not stop the move; it is said, with what fixes it.
|
|
369
|
+
const stillRun = new Set([...(await shippedScripts(onto.dir, kept))].filter((s) => scriptsBefore.has(s)));
|
|
370
|
+
const widened = stillRun.size > 0 ? (await missingWiring(target, stillRun)).filter((w) => / hook running `[^`]+` does not match /.test(w)) : [];
|
|
371
|
+
if (widened.length > 0) {
|
|
372
|
+
for (const w of widened) console.log(` · ${w}`);
|
|
373
|
+
console.log('');
|
|
374
|
+
}
|
|
367
375
|
if (unwired.length > 0) {
|
|
368
376
|
console.log(` ✗ ${unwired.length} hook(s) or script(s) that ${to}'s new ${[...newScripts].join(', ')} need(s) are not wired:`);
|
|
369
377
|
for (const w of unwired) console.log(` ${w}`);
|
package/src/gate.mjs
CHANGED
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
* PreToolUse / PostToolUse on Agent, Task, SendMessage
|
|
19
19
|
* a dispatch launched, and the agent it launched
|
|
20
20
|
* UserPromptSubmit (human text only) the owner spoke, and every open loop gets one round more
|
|
21
|
+
* PreToolUse / PostToolUse on AskUserQuestion
|
|
22
|
+
* the orchestrator asked the owner, and was answered: when, for the office
|
|
21
23
|
* PreToolUse on Agent, Task, SendMessage
|
|
22
24
|
* the decision: let it through, or send the round past the cap to
|
|
23
25
|
* the owner to confirm
|
|
@@ -280,11 +282,13 @@ function append(path, entry) {
|
|
|
280
282
|
* @param {object[]} entries - The ledger, oldest first.
|
|
281
283
|
* @param {ReturnType<typeof loopEdges>} loops - The capped loop-back edges.
|
|
282
284
|
* @param {Map<string, Set<string>>} [forward] - Stage → the sources that hand passing work on to it.
|
|
283
|
-
* @returns {{effect: (to: string) => {rounds: object[]}, rounds: object[]}}
|
|
285
|
+
* @returns {{effect: (to: string) => {rounds: object[]}, rounds: object[], open: object[], waiting: object[]}}
|
|
284
286
|
* A round is `{source, target, token, round, max, edgeRound, ceiling, agents}`, plus `issue` (the id
|
|
285
287
|
* that decides it) and `issues` (every id with its count) when it was counted by issue.
|
|
286
288
|
* `effect` says what a dispatch to a stage would make now, without making it; `rounds` is every round
|
|
287
|
-
* the recorded dispatches made
|
|
289
|
+
* the recorded dispatches made; `open` each loop still counted, with whether a pass already closes it
|
|
290
|
+
* (`closing`), the stages a dispatch to which would (`targets`) and the stages whose pass would (`onward`);
|
|
291
|
+
* `waiting` each loop-back no fix has acted on yet.
|
|
288
292
|
*/
|
|
289
293
|
export function replay(entries, loops, forward = new Map()) {
|
|
290
294
|
// When each dispatch went out. A foreground dispatch's `dispatch` entry is written after the agent
|
|
@@ -329,15 +333,23 @@ export function replay(entries, loops, forward = new Map()) {
|
|
|
329
333
|
for (const map of [streak, grace]) for (const key of [...map.keys()]) if (key.startsWith(`${source}|`)) map.delete(key);
|
|
330
334
|
};
|
|
331
335
|
|
|
336
|
+
/**
|
|
337
|
+
* A source's waiting verdicts, settled: the rejections no later review of a fix cancelled, and whether a pass
|
|
338
|
+
* closes its loops — one that began after the latest fix, with no rejection running beside it.
|
|
339
|
+
*/
|
|
340
|
+
const settle = (source, batch) => {
|
|
341
|
+
const passes = batch.filter((v) => !isLoopBack(v.verdict));
|
|
342
|
+
const live = batch.filter((v) => isLoopBack(v.verdict) && !passes.some((p) => p.launch > v.at && fixBetween(source, v.at, p.launch)));
|
|
343
|
+
return { live, closes: passes.some((p) => p.launch > (lastFix.get(source) ?? -1) && !live.some((v) => v.launch < p.at)) };
|
|
344
|
+
};
|
|
345
|
+
|
|
332
346
|
const effect = (to, at) => {
|
|
333
347
|
const rounds = [];
|
|
334
348
|
const outcome = [];
|
|
335
349
|
for (const [source, batch] of pending) {
|
|
336
350
|
const byToken = loops.get(source);
|
|
337
351
|
if (!byToken || batch.length === 0 || !targetsOf(source).has(to)) continue;
|
|
338
|
-
const
|
|
339
|
-
const live = batch.filter((v) => isLoopBack(v.verdict) && !passes.some((p) => p.launch > v.at && fixBetween(source, v.at, p.launch)));
|
|
340
|
-
const closes = passes.some((p) => p.launch > (lastFix.get(source) ?? -1) && !live.some((v) => v.launch < p.at));
|
|
352
|
+
const { live, closes } = settle(source, batch);
|
|
341
353
|
const routed = live.filter((v) => byToken.get(v.verdict)?.has(to));
|
|
342
354
|
const fresh = routed.filter((v) => {
|
|
343
355
|
const last = streak.get(`${source}|${v.verdict}`);
|
|
@@ -434,13 +446,18 @@ export function replay(entries, loops, forward = new Map()) {
|
|
|
434
446
|
});
|
|
435
447
|
// What is open when the replay ends: each loop with its rounds, the rounds the owner's replies gave it and the
|
|
436
448
|
// issues it counted by, and each loop-back no fix has acted on yet — what a compaction takes from the orchestrator.
|
|
449
|
+
// A loop whose source passed after the latest fix is closed by the next dispatch it could act on, and until then
|
|
450
|
+
// is still in the count; it says so, and what would close each of the others. Listed as open, three loops
|
|
451
|
+
// approved the evening before a compaction were handed back as capped, and the orchestrator went looking for why.
|
|
437
452
|
const open = [...streak.entries()]
|
|
438
453
|
.filter(([key]) => key.split('|').length === 2)
|
|
439
454
|
.map(([key, s]) => {
|
|
440
455
|
const [source, token] = key.split('|');
|
|
441
456
|
const cap = Math.max(0, ...(loops.get(source)?.get(token)?.values() ?? []));
|
|
442
457
|
const issues = [...streak.keys()].filter((k) => k.startsWith(`${key}|`)).map((k) => k.slice(key.length + 1));
|
|
443
|
-
|
|
458
|
+
const closing = settle(source, pending.get(source) ?? []).closes;
|
|
459
|
+
const onward = [...forward].filter(([, sources]) => sources.has(source)).map(([stage]) => stage);
|
|
460
|
+
return { source, token, rounds: s.n, cap, given: grace.get(key) ?? 0, issues, closing, targets: [...targetsOf(source)], onward };
|
|
444
461
|
});
|
|
445
462
|
const waiting = [...pending.entries()].flatMap(([source, batch]) => batch.filter((v) => isLoopBack(v.verdict)).map((v) => ({ source, verdict: v.verdict, issues: v.issues ?? [] })));
|
|
446
463
|
return { effect: (to) => effect(to, entries.length), rounds: made, open, waiting };
|
|
@@ -591,6 +608,10 @@ function onPost(input, project, path, at) {
|
|
|
591
608
|
}
|
|
592
609
|
// A dispatch made inside a subagent is not the pipeline's: its stages are dispatched by the orchestrator.
|
|
593
610
|
if (input.agent_id) return null;
|
|
611
|
+
if (tool === 'AskUserQuestion') {
|
|
612
|
+
append(path, { k: 'answered', at });
|
|
613
|
+
return null;
|
|
614
|
+
}
|
|
594
615
|
if (!DISPATCH.has(tool)) return null;
|
|
595
616
|
const response = input.tool_response && typeof input.tool_response === 'object' ? input.tool_response : {};
|
|
596
617
|
let role;
|
|
@@ -644,13 +665,23 @@ function confirmation(r) {
|
|
|
644
665
|
|
|
645
666
|
/** A dispatch is about to go out: note when, and send the round past its edge's cap to the owner to confirm. */
|
|
646
667
|
function onPre(input, project, path, at) {
|
|
668
|
+
// The orchestrator asking the owner, as it asks: when it was, and nothing it said. Nothing counts it; the office
|
|
669
|
+
// (`nina pipeline --view`) draws the question over its head until the answer, which `onPost` records.
|
|
670
|
+
if (!input.agent_id && input.tool_name === 'AskUserQuestion') {
|
|
671
|
+
append(path, { k: 'question', at });
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
647
674
|
if (input.agent_id || !DISPATCH.has(input.tool_name)) return null;
|
|
648
675
|
const entries = readLedger(path);
|
|
649
|
-
const
|
|
676
|
+
const to = input.tool_name === 'SendMessage' ? recipient(entries, project, input.tool_input?.to) : null;
|
|
677
|
+
const role = to ? to.role : input.tool_input?.subagent_type;
|
|
650
678
|
if (!role || !project.graph.stages.has(role)) return null;
|
|
651
679
|
// When it went out, whatever the owner decides: joined to its `dispatch` entry by the tool_use id,
|
|
652
|
-
// it is the moment the fixer was sent — which is what a later review has to begin after.
|
|
653
|
-
|
|
680
|
+
// it is the moment the fixer was sent — which is what a later review has to begin after. A message says so, and
|
|
681
|
+
// names its agent when the ledger already knows it — never a name the model wrote that nothing launched — so the
|
|
682
|
+
// office can tell a message to a running agent from a resume before the `dispatch` entry is written.
|
|
683
|
+
const known = to?.agent && entries.some((e) => e.k === 'dispatch' && e.agent === to.agent);
|
|
684
|
+
append(path, { k: 'pre', at, role, id: input.tool_use_id ?? null, ...(to ? { via: 'SendMessage', ...(known ? { agent: to.agent } : {}) } : {}) });
|
|
654
685
|
const over = roundsFor(entries, role, project.loops, project.forward)
|
|
655
686
|
.filter((r) => excess(r) > 0)
|
|
656
687
|
.sort((a, b) => excess(b) - excess(a));
|
|
@@ -672,31 +703,84 @@ function onPre(input, project, path, at) {
|
|
|
672
703
|
}
|
|
673
704
|
|
|
674
705
|
/**
|
|
675
|
-
*
|
|
676
|
-
*
|
|
677
|
-
*
|
|
678
|
-
*
|
|
679
|
-
*
|
|
706
|
+
* A project's session ledgers, the newest first, and the one asked for: the latest, or the one whose id starts
|
|
707
|
+
* with `wanted`. `nina gate --status` and the office (`nina pipeline --view`) pick a session through this, so
|
|
708
|
+
* the two never show different ones.
|
|
709
|
+
*
|
|
710
|
+
* @param {string} root - The project directory.
|
|
711
|
+
* @param {string} [wanted] - The start of a session id; absent, the session whose ledger was written last.
|
|
712
|
+
* @returns {{dir: string, ledgers: {session: string, written: Date, path: string}[], picked: {session: string, written: Date, path: string}|null, why: string|null}}
|
|
713
|
+
* `why` says why nothing was picked, and is null when there is simply no ledger yet.
|
|
714
|
+
*/
|
|
715
|
+
export function sessionLedgers(root, wanted) {
|
|
716
|
+
const dir = projectGateDir(root);
|
|
717
|
+
const ledgers = (existsSync(dir) ? readdirSync(dir) : [])
|
|
718
|
+
.filter((f) => f.endsWith('.jsonl') && f !== 'errors.jsonl')
|
|
719
|
+
.flatMap((f) => {
|
|
720
|
+
// Pruned between the listing and here, by another session: gone, and not a failure.
|
|
721
|
+
try {
|
|
722
|
+
return [{ session: f.slice(0, -'.jsonl'.length), written: statSync(join(dir, f)).mtime, path: join(dir, f) }];
|
|
723
|
+
} catch {
|
|
724
|
+
return [];
|
|
725
|
+
}
|
|
726
|
+
})
|
|
727
|
+
.sort((a, b) => b.written - a.written);
|
|
728
|
+
const matching = wanted ? ledgers.filter((l) => l.session.startsWith(wanted)) : ledgers.slice(0, 1);
|
|
729
|
+
if (matching.length === 1) return { dir, ledgers, picked: matching[0], why: null };
|
|
730
|
+
const why = !wanted ? null : matching.length === 0 ? `no session starts with "${wanted}"` : `${matching.length} sessions start with "${wanted}" — give more of the id`;
|
|
731
|
+
return { dir, ledgers, picked: null, why };
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/** Stages as a sentence lists them: `qa`, `implementer or architect`, `a, b or c`. */
|
|
735
|
+
const either = (stages) => (stages.length < 2 ? stages.join('') : `${stages.slice(0, -1).join(', ')} or ${stages.at(-1)}`);
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* What a session's ledger says of its loops, a line each: every loop still counted, and what would close it;
|
|
739
|
+
* the loop-backs no fix has acted on yet; and the latest dispatches. It is what a compaction hands the
|
|
740
|
+
* orchestrator and what `nina gate --status` prints, so the two cannot disagree.
|
|
680
741
|
*
|
|
681
742
|
* @param {object} project - `loadProject`'s.
|
|
682
|
-
* @param {
|
|
683
|
-
* @
|
|
684
|
-
*
|
|
743
|
+
* @param {object[]} entries - The session's ledger, oldest first.
|
|
744
|
+
* @returns {{lines: string[], open: number, closing: number, waiting: number}} The lines; the loops open, the
|
|
745
|
+
* loops a pass already settled, whose count the next dispatch drops, and the loop-backs waiting on a fix.
|
|
685
746
|
*/
|
|
686
|
-
function
|
|
687
|
-
const entries = readLedger(path);
|
|
747
|
+
export function loopState(project, entries) {
|
|
688
748
|
const { open, waiting } = replay(entries, project.loops, project.forward);
|
|
689
|
-
const lines = [
|
|
749
|
+
const lines = [];
|
|
690
750
|
for (const l of open) {
|
|
751
|
+
const count =
|
|
752
|
+
`${l.source} ${l.token} — ${l.rounds} round(s), capped at ${l.cap}${l.given ? ` and ${l.given} more from the owner's replies` : ''}` +
|
|
753
|
+
(l.issues.length ? `, issues ${l.issues.join(', ')}` : '');
|
|
754
|
+
const fix = waiting.some((w) => w.source === l.source) ? 'the fix it waits on' : 'the latest fix';
|
|
691
755
|
lines.push(
|
|
692
|
-
|
|
693
|
-
|
|
756
|
+
l.closing
|
|
757
|
+
? `- passed after its latest fix: ${count}. Those rounds are settled, and the gate drops them at the next dispatch to ${either(l.targets)}`
|
|
758
|
+
: `- open loop: ${count}. It closes when a ${l.source} launched after ${fix} passes with no rejection beside it` +
|
|
759
|
+
(l.onward.length ? `, or when ${either(l.onward)} passes, sent out after the ${l.source} last reported` : ''),
|
|
694
760
|
);
|
|
695
761
|
}
|
|
696
762
|
for (const w of waiting) lines.push(`- waiting on a fix: ${w.source} ${w.verdict}${w.issues.length ? `, issues ${w.issues.join(', ')}` : ''}`);
|
|
697
763
|
if (open.length === 0 && waiting.length === 0) lines.push('- no loop is open');
|
|
698
764
|
const dispatched = entries.filter((e) => e.k === 'dispatch' && e.role).slice(-4);
|
|
699
765
|
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`);
|
|
766
|
+
const closing = open.filter((l) => l.closing).length;
|
|
767
|
+
return { lines, open: open.length - closing, closing, waiting: waiting.length };
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* What the gate hands the orchestrator after its conversation was compacted: the loops still open and the
|
|
772
|
+
* loop-backs waiting on a fix, the latest dispatches, where the checkout stands, and the two documents to read
|
|
773
|
+
* again. The graph and the router are read once, at a session's start, and a compaction drops them with every
|
|
774
|
+
* report; the router told the orchestrator to write "round 2 of max 2" in a dispatch so "you after a
|
|
775
|
+
* compaction" could see it, and after a compaction it sees only a summary.
|
|
776
|
+
*
|
|
777
|
+
* @param {object} project - `loadProject`'s.
|
|
778
|
+
* @param {string} path - The session's ledger.
|
|
779
|
+
* @param {string} root - The project.
|
|
780
|
+
* @returns {object}
|
|
781
|
+
*/
|
|
782
|
+
function afterCompaction(project, path, root) {
|
|
783
|
+
const lines = ['The conversation was compacted. What the NINA loop gate kept of this session:', ...loopState(project, readLedger(path)).lines];
|
|
700
784
|
const git = (...args) => spawnSync('git', ['-C', root, ...args], { encoding: 'utf8', timeout: 2000 }).stdout?.trim() ?? '';
|
|
701
785
|
const branch = git('rev-parse', '--abbrev-ref', 'HEAD');
|
|
702
786
|
const last = git('log', '-1', '--format=%h %s');
|