@xhulz/nina 0.51.0 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -2
- package/bin/nina.mjs +29 -5
- package/package.json +1 -1
- package/releases/0.52.0/core/GAPS.md +125 -0
- package/releases/0.52.0/core/answered.json +68 -0
- package/releases/0.52.0/core/defaults/tree/.claude/agents/architect.md +1 -0
- package/releases/0.52.0/core/defaults/tree/.claude/agents/dba.md +3 -0
- package/releases/0.52.0/core/defaults/tree/.claude/agents/devops.md +1 -0
- package/releases/0.52.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
- package/releases/0.52.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.52.0/core/defaults/tree/.claude/agents/qa.md +7 -0
- package/releases/0.52.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
- package/releases/0.52.0/core/defaults/tree/.claude/agents/secops.md +3 -0
- package/releases/0.52.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
- package/releases/0.52.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
- package/releases/0.52.0/core/tree/.claude/agents/architect.md +129 -0
- package/releases/0.52.0/core/tree/.claude/agents/dba.md +117 -0
- package/releases/0.52.0/core/tree/.claude/agents/devops.md +118 -0
- package/releases/0.52.0/core/tree/.claude/agents/implementer.md +163 -0
- package/releases/0.52.0/core/tree/.claude/agents/integration-tester.md +168 -0
- package/releases/0.52.0/core/tree/.claude/agents/planner.md +132 -0
- package/releases/0.52.0/core/tree/.claude/agents/qa.md +150 -0
- package/releases/0.52.0/core/tree/.claude/agents/reviewer.md +184 -0
- package/releases/0.52.0/core/tree/.claude/agents/secops.md +120 -0
- package/releases/0.52.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
- package/releases/0.52.0/core/tree/.claude/agents/solidity-dev.md +123 -0
- package/releases/0.52.0/core/tree/.claude/agents-overview.md +64 -0
- package/releases/0.52.0/core/tree/.claude/frontend.md +10 -0
- package/releases/0.52.0/core/tree/.claude/graph.md +75 -0
- package/releases/0.52.0/core/tree/.claude/patterns.md +251 -0
- package/releases/0.52.0/core/tree/.claude/pills/README.md +150 -0
- package/releases/0.52.0/core/tree/.claude/pipeline.md +101 -0
- package/releases/0.52.0/core/tree/.claude/retrieval.md +103 -0
- package/releases/0.52.0/core/tree/.claude/router.md +202 -0
- package/releases/0.52.0/core/tree/.claude/templates/integration.md +46 -0
- package/releases/0.52.0/core/tree/CLAUDE.md +164 -0
- package/releases/0.52.0/core/tree/scripts/cost-watch.mjs +25 -0
- package/releases/0.52.0/core/tree/scripts/edit-guard.mjs +24 -0
- package/releases/0.52.0/core/tree/scripts/harness-check.mjs +71 -0
- package/releases/0.52.0/core/tree/scripts/loop-gate.mjs +19 -0
- package/releases/0.52.0/core/vocabulary.json +13 -0
- package/releases/0.52.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
- package/releases/0.52.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
- package/releases/0.52.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
- package/releases/0.52.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
- package/releases/0.52.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
- package/releases/0.52.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
- package/releases/0.52.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
- package/releases/0.52.0/surfaces/blockchain/tree/.claude/router.md +6 -0
- package/releases/0.52.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
- package/releases/0.52.0/surfaces/d1/tree/.claude/agents/dba.md +15 -0
- package/releases/0.52.0/surfaces/d1/tree/.claude/agents/devops.md +2 -0
- package/releases/0.52.0/surfaces/d1/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.52.0/surfaces/d1/tree/.claude/patterns.md +8 -0
- package/releases/0.52.0/surfaces/d1/tree/CLAUDE.md +2 -0
- package/releases/0.52.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
- package/releases/0.52.0/surfaces/db/tree/.claude/agents/dba.md +54 -0
- package/releases/0.52.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
- package/releases/0.52.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.52.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
- package/releases/0.52.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
- package/releases/0.52.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
- package/releases/0.52.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
- package/releases/0.52.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
- package/releases/0.52.0/surfaces/db/tree/.claude/graph.md +8 -0
- package/releases/0.52.0/surfaces/db/tree/.claude/patterns.md +22 -0
- package/releases/0.52.0/surfaces/db/tree/.claude/pipeline.md +13 -0
- package/releases/0.52.0/surfaces/db/tree/.claude/retrieval.md +5 -0
- package/releases/0.52.0/surfaces/db/tree/.claude/router.md +4 -0
- package/releases/0.52.0/surfaces/db/tree/CLAUDE.md +17 -0
- package/releases/0.52.0/surfaces/drizzle/tree/.claude/agents/dba.md +9 -0
- package/releases/0.52.0/surfaces/drizzle/tree/.claude/agents/devops.md +5 -0
- package/releases/0.52.0/surfaces/drizzle/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.52.0/surfaces/drizzle/tree/.claude/patterns.md +7 -0
- package/releases/0.52.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
- package/releases/0.52.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
- package/releases/0.52.0/surfaces/edge-cf/tree/.claude/agents/devops.md +29 -0
- package/releases/0.52.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
- package/releases/0.52.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
- package/releases/0.52.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
- package/releases/0.52.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.52.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
- package/releases/0.52.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
- package/releases/0.52.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
- package/releases/0.52.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
- package/releases/0.52.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
- package/releases/0.52.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
- package/releases/0.52.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
- package/releases/0.52.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
- package/releases/0.52.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
- package/releases/0.52.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
- package/releases/0.52.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
- package/releases/0.52.0/surfaces/frontend/tree/.claude/router.md +14 -0
- package/releases/0.52.0/surfaces/frontend/tree/CLAUDE.md +5 -0
- package/releases/0.52.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
- package/releases/0.52.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
- package/releases/0.52.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
- package/releases/0.52.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
- package/releases/0.52.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
- package/releases/0.52.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
- package/releases/0.52.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
- package/releases/0.52.0/surfaces/integrations/tree/.claude/graph.md +10 -0
- package/releases/0.52.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
- package/releases/0.52.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
- package/releases/0.52.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
- package/releases/0.52.0/surfaces/integrations/tree/.claude/router.md +6 -0
- package/releases/0.52.0/surfaces/integrations/tree/CLAUDE.md +17 -0
- package/releases/0.52.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
- package/releases/0.52.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
- package/releases/0.52.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
- package/releases/0.52.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.52.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
- package/releases/0.52.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
- package/releases/0.52.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
- package/releases/0.52.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
- package/releases/0.52.0/surfaces/money/tree/.claude/graph.md +2 -0
- package/releases/0.52.0/surfaces/money/tree/.claude/patterns.md +49 -0
- package/releases/0.52.0/surfaces/money/tree/.claude/pipeline.md +2 -0
- package/releases/0.52.0/surfaces/money/tree/.claude/retrieval.md +2 -0
- package/releases/0.52.0/surfaces/money/tree/CLAUDE.md +11 -0
- package/releases/0.52.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
- package/releases/0.52.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
- package/releases/0.52.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.52.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
- package/releases/0.52.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
- package/releases/0.52.0/surfaces/pii/tree/CLAUDE.md +5 -0
- package/releases/0.52.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
- package/releases/0.52.0/surfaces/prisma/tree/.claude/agents/dba.md +42 -0
- package/releases/0.52.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
- package/releases/0.52.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.52.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
- package/releases/0.52.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
- package/releases/0.52.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
- package/releases/0.52.0/surfaces/prisma/tree/CLAUDE.md +5 -0
- package/releases/0.53.0/core/GAPS.md +125 -0
- package/releases/0.53.0/core/answered.json +68 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/architect.md +1 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/dba.md +3 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/devops.md +1 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/qa.md +7 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/secops.md +3 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
- package/releases/0.53.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
- package/releases/0.53.0/core/tree/.claude/agents/architect.md +129 -0
- package/releases/0.53.0/core/tree/.claude/agents/dba.md +117 -0
- package/releases/0.53.0/core/tree/.claude/agents/devops.md +118 -0
- package/releases/0.53.0/core/tree/.claude/agents/implementer.md +163 -0
- package/releases/0.53.0/core/tree/.claude/agents/integration-tester.md +168 -0
- package/releases/0.53.0/core/tree/.claude/agents/planner.md +132 -0
- package/releases/0.53.0/core/tree/.claude/agents/qa.md +150 -0
- package/releases/0.53.0/core/tree/.claude/agents/reviewer.md +184 -0
- package/releases/0.53.0/core/tree/.claude/agents/secops.md +120 -0
- package/releases/0.53.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
- package/releases/0.53.0/core/tree/.claude/agents/solidity-dev.md +123 -0
- package/releases/0.53.0/core/tree/.claude/agents-overview.md +64 -0
- package/releases/0.53.0/core/tree/.claude/frontend.md +10 -0
- package/releases/0.53.0/core/tree/.claude/graph.md +75 -0
- package/releases/0.53.0/core/tree/.claude/patterns.md +251 -0
- package/releases/0.53.0/core/tree/.claude/pills/README.md +150 -0
- package/releases/0.53.0/core/tree/.claude/pipeline.md +101 -0
- package/releases/0.53.0/core/tree/.claude/retrieval.md +103 -0
- package/releases/0.53.0/core/tree/.claude/router.md +202 -0
- package/releases/0.53.0/core/tree/.claude/templates/integration.md +46 -0
- package/releases/0.53.0/core/tree/CLAUDE.md +164 -0
- package/releases/0.53.0/core/tree/scripts/cost-watch.mjs +25 -0
- package/releases/0.53.0/core/tree/scripts/edit-guard.mjs +24 -0
- package/releases/0.53.0/core/tree/scripts/harness-check.mjs +71 -0
- package/releases/0.53.0/core/tree/scripts/loop-gate.mjs +19 -0
- package/releases/0.53.0/core/vocabulary.json +13 -0
- package/releases/0.53.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
- package/releases/0.53.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
- package/releases/0.53.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
- package/releases/0.53.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
- package/releases/0.53.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
- package/releases/0.53.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
- package/releases/0.53.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
- package/releases/0.53.0/surfaces/blockchain/tree/.claude/router.md +6 -0
- package/releases/0.53.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
- package/releases/0.53.0/surfaces/d1/tree/.claude/agents/dba.md +15 -0
- package/releases/0.53.0/surfaces/d1/tree/.claude/agents/devops.md +2 -0
- package/releases/0.53.0/surfaces/d1/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.53.0/surfaces/d1/tree/.claude/patterns.md +8 -0
- package/releases/0.53.0/surfaces/d1/tree/CLAUDE.md +2 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/agents/dba.md +54 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/graph.md +8 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/patterns.md +22 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/pipeline.md +13 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/retrieval.md +5 -0
- package/releases/0.53.0/surfaces/db/tree/.claude/router.md +4 -0
- package/releases/0.53.0/surfaces/db/tree/CLAUDE.md +17 -0
- package/releases/0.53.0/surfaces/drizzle/tree/.claude/agents/dba.md +9 -0
- package/releases/0.53.0/surfaces/drizzle/tree/.claude/agents/devops.md +5 -0
- package/releases/0.53.0/surfaces/drizzle/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.53.0/surfaces/drizzle/tree/.claude/patterns.md +7 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/agents/devops.md +29 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
- package/releases/0.53.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
- package/releases/0.53.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
- package/releases/0.53.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
- package/releases/0.53.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
- package/releases/0.53.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
- package/releases/0.53.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
- package/releases/0.53.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
- package/releases/0.53.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
- package/releases/0.53.0/surfaces/frontend/tree/.claude/router.md +14 -0
- package/releases/0.53.0/surfaces/frontend/tree/CLAUDE.md +5 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/graph.md +10 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
- package/releases/0.53.0/surfaces/integrations/tree/.claude/router.md +6 -0
- package/releases/0.53.0/surfaces/integrations/tree/CLAUDE.md +17 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/graph.md +2 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/patterns.md +49 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/pipeline.md +2 -0
- package/releases/0.53.0/surfaces/money/tree/.claude/retrieval.md +2 -0
- package/releases/0.53.0/surfaces/money/tree/CLAUDE.md +11 -0
- package/releases/0.53.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
- package/releases/0.53.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
- package/releases/0.53.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.53.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
- package/releases/0.53.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
- package/releases/0.53.0/surfaces/pii/tree/CLAUDE.md +5 -0
- package/releases/0.53.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
- package/releases/0.53.0/surfaces/prisma/tree/.claude/agents/dba.md +42 -0
- package/releases/0.53.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
- package/releases/0.53.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.53.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
- package/releases/0.53.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
- package/releases/0.53.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
- package/releases/0.53.0/surfaces/prisma/tree/CLAUDE.md +5 -0
- package/src/commands/dashboard.mjs +33 -0
- package/src/commands/learn.mjs +51 -19
- package/src/commands/pills.mjs +55 -28
- package/src/commands/pipeline.mjs +62 -33
- package/src/commands/runs.mjs +14 -3
- package/src/commands/stats.mjs +227 -115
- package/src/dashboard.html +328 -0
- package/src/dashboard.mjs +185 -0
- package/src/gate.mjs +14 -2
- package/src/office.html +142 -4
- package/src/office.mjs +192 -104
- package/src/serve.mjs +108 -0
- package/src/store.mjs +13 -1
package/src/commands/runs.mjs
CHANGED
|
@@ -66,12 +66,25 @@ function summarize(cycle) {
|
|
|
66
66
|
unpriced,
|
|
67
67
|
name: String(rounds[0]?.desc ?? '').trim() || '(no description)',
|
|
68
68
|
how: ENDS[cycle.end](rounds.at(-1)),
|
|
69
|
+
end_kind: cycle.end,
|
|
69
70
|
stages: [...stages.values()].map((s) => ({ ...s, runs: s.runs.size })),
|
|
70
71
|
files: files.length ? Math.max(...files) : null,
|
|
71
72
|
peak: peaks.length ? Math.max(...peaks) : null,
|
|
72
73
|
};
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
/**
|
|
77
|
+
* A project's pieces of work, newest first: what `nina runs` prints and the dashboard's tasks tab shows.
|
|
78
|
+
*
|
|
79
|
+
* @param {object[]} records - The project's measured rounds.
|
|
80
|
+
* @returns {ReturnType<typeof summarize>[]}
|
|
81
|
+
*/
|
|
82
|
+
export function taskList(records) {
|
|
83
|
+
return cyclesOf(records.filter((r) => r.status !== 'denied'))
|
|
84
|
+
.map(summarize)
|
|
85
|
+
.sort((a, b) => b.start.localeCompare(a.start));
|
|
86
|
+
}
|
|
87
|
+
|
|
75
88
|
/** A duration in minutes, as hours and minutes. */
|
|
76
89
|
const span = (m) => (m === null ? '—' : m >= 60 ? `${Math.floor(m / 60)}h${String(m % 60).padStart(2, '0')}` : `${m}m`);
|
|
77
90
|
|
|
@@ -103,9 +116,7 @@ export async function runs(argv) {
|
|
|
103
116
|
);
|
|
104
117
|
return 1;
|
|
105
118
|
}
|
|
106
|
-
const cycles =
|
|
107
|
-
.map(summarize)
|
|
108
|
-
.sort((a, b) => b.start.localeCompare(a.start));
|
|
119
|
+
const cycles = taskList(records);
|
|
109
120
|
const shown = cycles.slice(0, last);
|
|
110
121
|
console.log(
|
|
111
122
|
` ${bold(pink(projectName(records[0].project)))} · ${cycles.length} cycle(s)` +
|
package/src/commands/stats.mjs
CHANGED
|
@@ -222,9 +222,15 @@ const running = (r, now) => !r.verdict && now - Date.parse(r.ts) <= RUNNING_HOUR
|
|
|
222
222
|
*
|
|
223
223
|
* @param {object[]} records - The runs in the window.
|
|
224
224
|
*/
|
|
225
|
-
|
|
225
|
+
/**
|
|
226
|
+
* What each stage's runs cost at API list prices, as data: what `stats` prints and the dashboard's stats tab shows.
|
|
227
|
+
* Null when no run has a token record, or none has a price.
|
|
228
|
+
*
|
|
229
|
+
* @param {object[]} records - The rounds in the window.
|
|
230
|
+
*/
|
|
231
|
+
export function costData(records) {
|
|
226
232
|
const measured = records.filter((r) => r.tokens && typeof r.tokens === 'object');
|
|
227
|
-
if (measured.length === 0) return;
|
|
233
|
+
if (measured.length === 0) return null;
|
|
228
234
|
// A run's cost is its rounds', so a reviewer resumed twice is one run at the price of three rounds.
|
|
229
235
|
/** @type {Map<string, number[]>} */
|
|
230
236
|
const byRole = new Map();
|
|
@@ -240,25 +246,33 @@ function costReport(records) {
|
|
|
240
246
|
byRole.set(role, [...(byRole.get(role) ?? []), costs.reduce((a, b) => a + b, 0)]);
|
|
241
247
|
}
|
|
242
248
|
const total = [...byRole.values()].flat().reduce((a, b) => a + b, 0);
|
|
243
|
-
if (total === 0) return;
|
|
249
|
+
if (total === 0) return null;
|
|
250
|
+
const rows = [...byRole]
|
|
251
|
+
.map(([role, costs]) => ({ role, runs: costs.length, median: median(costs), total: costs.reduce((a, b) => a + b, 0) }))
|
|
252
|
+
.sort((a, b) => b.total - a.total);
|
|
253
|
+
return { pricesAsOf: PRICES_AS_OF, measuredRuns: runs.size, allRuns: byRun(records).size, unpriced, total, rows };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function costReport(records) {
|
|
257
|
+
const d = costData(records);
|
|
258
|
+
if (!d) return;
|
|
244
259
|
const money = (n) => `$${n >= 100 ? n.toFixed(0) : n.toFixed(2)}`;
|
|
245
260
|
// The same convention as the duration column beside it: the upper middle of an even count.
|
|
246
261
|
console.log(
|
|
247
262
|
heading(
|
|
248
263
|
'cost',
|
|
249
|
-
`at API list prices of ${
|
|
250
|
-
(unpriced > 0 ? `, ${unpriced} on a model the price table does not know` : ''),
|
|
264
|
+
`at API list prices of ${d.pricesAsOf}; ${d.measuredRuns} of ${d.allRuns} runs have a token record` +
|
|
265
|
+
(d.unpriced > 0 ? `, ${d.unpriced} on a model the price table does not know` : ''),
|
|
251
266
|
),
|
|
252
267
|
);
|
|
253
268
|
console.log(dim(` ${'stage'.padEnd(20)}${'runs'.padStart(6)}${'median'.padStart(10)}${'total'.padStart(10)}${'share'.padStart(8)}`));
|
|
254
|
-
for (const
|
|
255
|
-
const sum = costs.reduce((a, b) => a + b, 0);
|
|
269
|
+
for (const { role, runs, median: middle, total: sum } of d.rows) {
|
|
256
270
|
console.log(
|
|
257
|
-
` ${pink(role)}${' '.repeat(Math.max(20 - role.length, 1))}${String(
|
|
258
|
-
`${bold(money(sum).padStart(10))}${pct(sum, total).padStart(8)} ${pink(bar(sum / total, 20))}`,
|
|
271
|
+
` ${pink(role)}${' '.repeat(Math.max(20 - role.length, 1))}${String(runs).padStart(6)}${money(middle).padStart(10)}` +
|
|
272
|
+
`${bold(money(sum).padStart(10))}${pct(sum, d.total).padStart(8)} ${pink(bar(sum / d.total, 20))}`,
|
|
259
273
|
);
|
|
260
274
|
}
|
|
261
|
-
console.log(bold(` ${'all stages'.padEnd(20)}${String(
|
|
275
|
+
console.log(bold(` ${'all stages'.padEnd(20)}${String(d.rows.reduce((a, r) => a + r.runs, 0)).padStart(6)}${''.padStart(10)}${money(d.total).padStart(10)}`));
|
|
262
276
|
}
|
|
263
277
|
|
|
264
278
|
/**
|
|
@@ -270,30 +284,57 @@ function costReport(records) {
|
|
|
270
284
|
* @param {object[]} records - The rounds in the window.
|
|
271
285
|
*/
|
|
272
286
|
function contextReport(records) {
|
|
273
|
-
const
|
|
274
|
-
if (
|
|
287
|
+
const d = contextData(records);
|
|
288
|
+
if (!d) return;
|
|
275
289
|
const k = (n) => (typeof n === 'number' ? `${Math.round(n / 1000)}k` : '—');
|
|
276
|
-
const chars = (xs) => (xs.length ? median(xs).toLocaleString('en-US') : '—');
|
|
277
290
|
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'));
|
|
278
291
|
console.log(dim(` ${'stage'.padEnd(20)}${'rounds'.padEnd(8)}${'n'.padStart(4)}${'turns'.padStart(7)}${'calls/turn'.padStart(12)}${'starts at'.padStart(11)}${'peak'.padStart(8)}${'prompt'.padStart(9)}`));
|
|
292
|
+
let previous = null;
|
|
293
|
+
for (const row of d.rows) {
|
|
294
|
+
const name = row.role !== previous ? pink(row.role) + ' '.repeat(Math.max(20 - row.role.length, 1)) : ' '.repeat(20);
|
|
295
|
+
previous = row.role;
|
|
296
|
+
console.log(
|
|
297
|
+
` ${name}${dim(row.label.padEnd(8))}${String(row.n).padStart(4)}${String(row.turns).padStart(7)}` +
|
|
298
|
+
`${row.callsPerTurn.toFixed(2).padStart(12)}${k(row.start).padStart(11)}${k(row.peak).padStart(8)}` +
|
|
299
|
+
`${(row.prompt === null ? '—' : row.prompt.toLocaleString('en-US')).padStart(9)}`,
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Each stage's rounds and how they spent their context, as data (see `contextReport`): the first rounds of a run
|
|
306
|
+
* and the resumed ones apart. Null when no round recorded its turns.
|
|
307
|
+
*
|
|
308
|
+
* @param {object[]} records - The rounds in the window.
|
|
309
|
+
*/
|
|
310
|
+
export function contextData(records) {
|
|
311
|
+
const shaped = records.filter((r) => typeof r.turns === 'number' && r.turns > 0);
|
|
312
|
+
if (shaped.length === 0) return null;
|
|
279
313
|
const byRole = new Map();
|
|
280
314
|
for (const r of shaped) byRole.set(r.role, [...(byRole.get(r.role) ?? []), r]);
|
|
315
|
+
const rows = [];
|
|
281
316
|
for (const [role, rounds] of [...byRole].sort((a, b) => b[1].reduce((x, r) => x + r.turns, 0) - a[1].reduce((x, r) => x + r.turns, 0))) {
|
|
282
|
-
[
|
|
317
|
+
for (const [label, group] of [
|
|
283
318
|
['first', rounds.filter((r) => (r.round ?? 1) === 1)],
|
|
284
319
|
['resumed', rounds.filter((r) => (r.round ?? 1) > 1)],
|
|
285
|
-
]
|
|
286
|
-
if (group.length === 0)
|
|
320
|
+
]) {
|
|
321
|
+
if (group.length === 0) continue;
|
|
287
322
|
const turns = group.reduce((a, r) => a + r.turns, 0);
|
|
288
323
|
const calls = group.reduce((a, r) => a + (r.tool_calls ?? 0), 0);
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
324
|
+
const prompts = group.map((r) => r.prompt_chars).filter((n) => typeof n === 'number');
|
|
325
|
+
rows.push({
|
|
326
|
+
role,
|
|
327
|
+
label,
|
|
328
|
+
n: group.length,
|
|
329
|
+
turns: median(group.map((r) => r.turns)),
|
|
330
|
+
callsPerTurn: calls / turns,
|
|
331
|
+
start: median(group.map((r) => r.context_start ?? 0)),
|
|
332
|
+
peak: median(group.map((r) => r.context_peak ?? 0)),
|
|
333
|
+
prompt: prompts.length ? median(prompts) : null,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
296
336
|
}
|
|
337
|
+
return { rows };
|
|
297
338
|
}
|
|
298
339
|
|
|
299
340
|
/**
|
|
@@ -436,10 +477,17 @@ export const WRITERS = new Set(['implementer', 'solidity-dev']);
|
|
|
436
477
|
/** The stages whose presence makes a cycle a designed one. */
|
|
437
478
|
const DESIGNERS = new Set(['planner', 'architect']);
|
|
438
479
|
|
|
480
|
+
/**
|
|
481
|
+
* The verdict a run still at work stands in with, for a reader that cuts a session as it happens — the office's
|
|
482
|
+
* timeline: it closes nothing, being neither a closing verdict nor one that could not be read, and it lets no
|
|
483
|
+
* write through. A run with none at all would read as unreadable, and a qa still at work would close its cycle.
|
|
484
|
+
*/
|
|
485
|
+
export const IN_FLIGHT = 'IN-FLIGHT';
|
|
486
|
+
|
|
439
487
|
/**
|
|
440
488
|
* A session's records cut into cycles: each up to the verdict that closes one, or to the next design stage
|
|
441
|
-
* after code was written
|
|
442
|
-
*
|
|
489
|
+
* after code was written and let through, or to a closing stage whose verdict cannot be read (see
|
|
490
|
+
* `proportionReport`). What `stats` sizes, `nina runs` prices and the office's timeline names.
|
|
443
491
|
*
|
|
444
492
|
* @param {object[]} records - Records of any number of sessions and projects.
|
|
445
493
|
* @returns {{runs: object[], end: 'closed'|'unreadable'|'next'|'open'}[]} In the order they happened within
|
|
@@ -455,21 +503,33 @@ export function cyclesOf(records) {
|
|
|
455
503
|
/** @type {{runs: object[], end: 'closed'|'unreadable'|'next'|'open'}[]} */
|
|
456
504
|
const cycles = [];
|
|
457
505
|
const unread = (verdict) => !verdict || verdict === 'UNCLEAR' || verdict === 'NONE';
|
|
506
|
+
// Code was written, and what reviewed its latest write let it through: a verdict read from a stage that reviews —
|
|
507
|
+
// neither one that writes nor one that designs — and nothing sent back since. Read off the one verdict dispatched
|
|
508
|
+
// before the design, the gates that run side by side made it a matter of which went out last — a rejection beside
|
|
509
|
+
// three approvals ended the change its own fix belonged to.
|
|
510
|
+
const letThrough = (cycle) => {
|
|
511
|
+
const write = cycle.findLastIndex((x) => WRITERS.has(x.role));
|
|
512
|
+
if (write < 0) return false;
|
|
513
|
+
const since = cycle
|
|
514
|
+
.slice(write + 1)
|
|
515
|
+
.filter((x) => !WRITERS.has(x.role) && !DESIGNERS.has(x.role))
|
|
516
|
+
.map((x) => x.verdict)
|
|
517
|
+
.filter((v) => !unread(v) && v !== IN_FLIGHT);
|
|
518
|
+
return since.length > 0 && !since.some(isLoopBack);
|
|
519
|
+
};
|
|
458
520
|
for (const runs of bySession.values()) {
|
|
459
521
|
let cycle = [];
|
|
460
|
-
let previous = null;
|
|
461
522
|
const end = (how) => {
|
|
462
523
|
if (cycle.length > 0) cycles.push({ runs: cycle, end: how });
|
|
463
524
|
cycle = [];
|
|
464
525
|
};
|
|
465
526
|
for (const r of runs) {
|
|
466
|
-
if (DESIGNERS.has(r.role) && cycle
|
|
527
|
+
if (DESIGNERS.has(r.role) && letThrough(cycle)) end('next');
|
|
467
528
|
cycle.push(r);
|
|
468
529
|
if (r.role in CYCLE_ENDS) {
|
|
469
530
|
if (r.verdict === CYCLE_ENDS[r.role]) end('closed');
|
|
470
531
|
else if (unread(r.verdict)) end('unreadable');
|
|
471
532
|
}
|
|
472
|
-
if (!unread(r.verdict)) previous = r.verdict;
|
|
473
533
|
}
|
|
474
534
|
end('open');
|
|
475
535
|
}
|
|
@@ -482,9 +542,10 @@ export function cyclesOf(records) {
|
|
|
482
542
|
* (`CYCLE_ENDS`), so a loop-back's fix rounds stay with the design that preceded them — the owner's own
|
|
483
543
|
* prompts were tried as the boundary first, and every "pode seguir" after a spec cut a pipeline in two.
|
|
484
544
|
* Two more things end a cycle, both found in real sessions: a design stage dispatched after code was
|
|
485
|
-
* written starts the next change — the light chain has no qa to close it, and it was absorbed
|
|
486
|
-
* design that followed —
|
|
487
|
-
* designers
|
|
545
|
+
* written and let through starts the next change — the light chain has no qa to close it, and it was absorbed
|
|
546
|
+
* into the design that followed — but not while anything since the latest write sent work back, which the graph
|
|
547
|
+
* routes into both designers, or no stage that reviews has read that write yet, as when a fix and its spec's
|
|
548
|
+
* correction go out together; and a closing stage whose verdict cannot be read still closes it, counted as unreadable,
|
|
488
549
|
* because a qa run with no verdict line otherwise let one cycle swallow a dozen pipelines. A cycle is
|
|
489
550
|
* sized by the most files any one of its writers wrote, not their sum, so three fix rounds on two files
|
|
490
551
|
* stay two files; and only edits through the edit tools count, so a file written from a shell is missed
|
|
@@ -498,8 +559,44 @@ export function cyclesOf(records) {
|
|
|
498
559
|
* @param {object[]} records - The runs in the window.
|
|
499
560
|
*/
|
|
500
561
|
function proportionReport(records, stepLimit = () => null) {
|
|
562
|
+
const d = proportionData(records, stepLimit);
|
|
563
|
+
if (!d) return;
|
|
564
|
+
console.log(
|
|
565
|
+
heading(
|
|
566
|
+
'proportion',
|
|
567
|
+
`${d.built} cycle(s) that wrote code; a cycle closes at qa PASS, devops DEPLOYED or secops SECURE, or at the next design after code` +
|
|
568
|
+
(d.unreadable > 0 ? `; ${d.unreadable} closed by a run whose verdict could not be read` : '') +
|
|
569
|
+
(d.open > 0 ? `; ${d.open} still open when their session's record ends` : ''),
|
|
570
|
+
),
|
|
571
|
+
);
|
|
572
|
+
console.log(dim(` ${'largest write'.padEnd(16)}${'cycles'.padStart(8)}${'with a planner or architect'.padStart(30)}`));
|
|
573
|
+
for (const row of d.rows) {
|
|
574
|
+
const designed = `${row.designed} (${pct(row.designed, row.cycles)})`.padStart(30);
|
|
575
|
+
console.log(` ${row.label.padEnd(16)}${String(row.cycles).padStart(8)}${row.cycles ? designed : dim(designed)} ${pink(bar(row.cycles ? row.designed / row.cycles : 0, 10))}`);
|
|
576
|
+
}
|
|
577
|
+
console.log(dim(' A shape, not a verdict: a critical path is gated in full at any size, and one spec may cover sibling steps.'));
|
|
578
|
+
if (d.over) {
|
|
579
|
+
const read = d.over.readM === null ? '' : `, ${d.over.readM}M tokens read from cache`;
|
|
580
|
+
console.log(
|
|
581
|
+
note(
|
|
582
|
+
`${d.over.count} implementer round(s) wrote more files than one step may (${d.over.limit}); the largest wrote ${d.over.largest}${read}. The architect splits such a spec into steps.`,
|
|
583
|
+
'warn',
|
|
584
|
+
),
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* The size of each change against the chain that carried it, as data (see `proportionReport`): cycles that wrote code
|
|
591
|
+
* by their largest write, how many had a design stage, and the implementer rounds that wrote more files than one step
|
|
592
|
+
* may. Null when no cycle wrote code that was counted.
|
|
593
|
+
*
|
|
594
|
+
* @param {object[]} records - The rounds in the window.
|
|
595
|
+
* @param {(project: string) => number|null} [stepLimit] - The most files one implementer run may write, by project.
|
|
596
|
+
*/
|
|
597
|
+
export function proportionData(records, stepLimit = () => null) {
|
|
501
598
|
const measured = records.filter((r) => WRITERS.has(r.role) && typeof r.files_touched === 'number');
|
|
502
|
-
if (measured.length === 0) return;
|
|
599
|
+
if (measured.length === 0) return null;
|
|
503
600
|
const cycles = cyclesOf(records);
|
|
504
601
|
const rows = [
|
|
505
602
|
{ label: '1–2 files', from: 1, to: 2 },
|
|
@@ -521,22 +618,7 @@ function proportionReport(records, stepLimit = () => null) {
|
|
|
521
618
|
row.cycles += 1;
|
|
522
619
|
if (cycle.some((r) => DESIGNERS.has(r.role))) row.designed += 1;
|
|
523
620
|
}
|
|
524
|
-
if (built === 0) return;
|
|
525
|
-
console.log(
|
|
526
|
-
heading(
|
|
527
|
-
'proportion',
|
|
528
|
-
`${built} cycle(s) that wrote code; a cycle closes at qa PASS, devops DEPLOYED or secops SECURE, or at the next design after code` +
|
|
529
|
-
(unreadable > 0 ? `; ${unreadable} closed by a run whose verdict could not be read` : '') +
|
|
530
|
-
(open > 0 ? `; ${open} still open when their session's record ends` : ''),
|
|
531
|
-
),
|
|
532
|
-
);
|
|
533
|
-
console.log(dim(` ${'largest write'.padEnd(16)}${'cycles'.padStart(8)}${'with a planner or architect'.padStart(30)}`));
|
|
534
|
-
for (const row of rows) {
|
|
535
|
-
const designed = `${row.designed} (${pct(row.designed, row.cycles)})`.padStart(30);
|
|
536
|
-
console.log(` ${row.label.padEnd(16)}${String(row.cycles).padStart(8)}${row.cycles ? designed : dim(designed)} ${pink(bar(row.cycles ? row.designed / row.cycles : 0, 10))}`);
|
|
537
|
-
}
|
|
538
|
-
console.log(dim(' A shape, not a verdict: a critical path is gated in full at any size, and one spec may cover sibling steps.'));
|
|
539
|
-
|
|
621
|
+
if (built === 0) return null;
|
|
540
622
|
// The one size the harness does limit: what one implementer run writes. A run re-reads the context it
|
|
541
623
|
// has built on every turn, so its cost grows faster than its size — measured over two projects, the
|
|
542
624
|
// cache read per file held near 1M up to 19 files and was 6.9M in a 52-file run. Asked only of a project
|
|
@@ -546,16 +628,16 @@ function proportionReport(records, stepLimit = () => null) {
|
|
|
546
628
|
const limit = stepLimit(r.project);
|
|
547
629
|
return limit !== null && r.files_touched > limit;
|
|
548
630
|
});
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
),
|
|
557
|
-
|
|
558
|
-
}
|
|
631
|
+
const largest = over.length ? over.reduce((a, b) => (b.files_touched > a.files_touched ? b : a)) : null;
|
|
632
|
+
return {
|
|
633
|
+
rows: rows.map(({ label, cycles: n, designed }) => ({ label, cycles: n, designed })),
|
|
634
|
+
built,
|
|
635
|
+
unreadable,
|
|
636
|
+
open,
|
|
637
|
+
over: largest
|
|
638
|
+
? { count: over.length, limit: stepLimit(largest.project), largest: largest.files_touched, readM: typeof largest.tokens?.read === 'number' ? Math.round(largest.tokens.read / 1e6) : null }
|
|
639
|
+
: null,
|
|
640
|
+
};
|
|
559
641
|
}
|
|
560
642
|
|
|
561
643
|
/**
|
|
@@ -587,13 +669,74 @@ function stepLimitOf(ctx) {
|
|
|
587
669
|
};
|
|
588
670
|
}
|
|
589
671
|
|
|
672
|
+
/**
|
|
673
|
+
* Each stage's rounds and what came of them, as data: what `stats` prints in its stages table and the dashboard's
|
|
674
|
+
* stats tab shows. Counted in rounds — the verdicts are the rounds', and the loop gate counts rounds — with the runs
|
|
675
|
+
* they belong to; and the rounds of agents outside the pipeline apart, which have no verdict to give.
|
|
676
|
+
*
|
|
677
|
+
* @param {object[]} records - The rounds in the window.
|
|
678
|
+
* @param {number} [now] - Against which a round with no report is still running or lost.
|
|
679
|
+
*/
|
|
680
|
+
export function stageData(records, now = Date.now()) {
|
|
681
|
+
/** @type {Map<string, {n:number, runs:Set<string>, done:number, lost:number, clear:number, loop:number, unclear:number, declared:number, durations:number[]}>} */
|
|
682
|
+
const byRole = new Map();
|
|
683
|
+
// Agents outside the pipeline — Claude Code's own, a project's own — have no verdict to give. Read as
|
|
684
|
+
// stages they were 100% unreadable and were told to add a verdict token their spec never asked for.
|
|
685
|
+
const outside = new Map();
|
|
686
|
+
for (const r of records.filter((r) => r.status !== 'denied')) {
|
|
687
|
+
if (!PIPELINE_ROLES.has(r.role)) {
|
|
688
|
+
outside.set(r.role, (outside.get(r.role) ?? 0) + 1);
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
if (!byRole.has(r.role)) {
|
|
692
|
+
byRole.set(r.role, { n: 0, runs: new Set(), done: 0, lost: 0, clear: 0, loop: 0, unclear: 0, declared: 0, durations: [] });
|
|
693
|
+
}
|
|
694
|
+
const s = byRole.get(r.role);
|
|
695
|
+
s.n += 1;
|
|
696
|
+
s.runs.add(runOf(r));
|
|
697
|
+
if (!r.verdict && !running(r, now)) s.lost += 1;
|
|
698
|
+
if (r.verdict) s.done += 1;
|
|
699
|
+
if (r.verdict_source === 'declared' || r.verdict_source === 'handback') s.declared += 1;
|
|
700
|
+
if (r.verdict === 'UNCLEAR' || r.verdict === 'NONE') s.unclear += 1;
|
|
701
|
+
else if (r.verdict) {
|
|
702
|
+
s.clear += 1;
|
|
703
|
+
if (isLoopBack(r.verdict)) s.loop += 1;
|
|
704
|
+
}
|
|
705
|
+
if (typeof r.duration_s === 'number') s.durations.push(r.duration_s);
|
|
706
|
+
}
|
|
707
|
+
const rows = [...byRole.entries()]
|
|
708
|
+
.sort((a, b) => b[1].n - a[1].n)
|
|
709
|
+
.map(([role, s]) => {
|
|
710
|
+
const durations = s.durations.filter((v) => typeof v === 'number' && v >= 0);
|
|
711
|
+
return { role, runs: s.runs.size, rounds: s.n, done: s.done, lost: s.lost, clear: s.clear, loop: s.loop, unclear: s.unclear, declared: s.declared, median_s: durations.length ? median(durations) : null };
|
|
712
|
+
});
|
|
713
|
+
return { rows, outside: [...outside.entries()].sort((a, b) => b[1] - a[1]) };
|
|
714
|
+
}
|
|
715
|
+
|
|
590
716
|
/** The middle of a list of numbers — the upper one of two — or undefined for none. */
|
|
591
717
|
const median = (xs) => [...xs].sort((a, b) => a - b)[Math.floor(xs.length / 2)];
|
|
592
718
|
|
|
593
|
-
/**
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
719
|
+
/**
|
|
720
|
+
* One project's measured history, as the dashboard's stats tab shows it: the same sections `stats` prints — its
|
|
721
|
+
* stages, what they cost, how they spent their context, and the size of each change against its chain — each from
|
|
722
|
+
* the function the report prints from. Null when nothing is recorded.
|
|
723
|
+
*
|
|
724
|
+
* @param {object[]} records - The project's rounds, oldest first.
|
|
725
|
+
* @param {{root: string}} ctx - CLI context, for the step limit its pinned release states.
|
|
726
|
+
*/
|
|
727
|
+
export function statsData(records, ctx) {
|
|
728
|
+
if (records.length === 0) return null;
|
|
729
|
+
const ran = records.filter((r) => r.status !== 'denied');
|
|
730
|
+
return {
|
|
731
|
+
runs: byRun(ran).size,
|
|
732
|
+
rounds: ran.length,
|
|
733
|
+
denied: records.length - ran.length,
|
|
734
|
+
span: [records[0]?.ts, records.at(-1)?.ts].map(localDay),
|
|
735
|
+
stages: stageData(records),
|
|
736
|
+
cost: costData(ran),
|
|
737
|
+
context: contextData(ran),
|
|
738
|
+
proportion: proportionData(ran, stepLimitOf(ctx)),
|
|
739
|
+
};
|
|
597
740
|
}
|
|
598
741
|
|
|
599
742
|
/**
|
|
@@ -667,40 +810,9 @@ export async function stats(argv, ctx) {
|
|
|
667
810
|
|
|
668
811
|
const skipped = new Set(all.map((r) => r.project)).size - new Set(records.map((r) => r.project)).size;
|
|
669
812
|
|
|
670
|
-
/**
|
|
671
|
-
* Per stage, counted in rounds: the verdicts are the rounds', and the loop gate counts rounds.
|
|
672
|
-
*
|
|
673
|
-
* @type {Map<string, {n:number, runs:Set<string>, done:number, lost:number, clear:number, loop:number,
|
|
674
|
-
* unclear:number, declared:number, durations:number[]}>}
|
|
675
|
-
*/
|
|
676
|
-
const byRole = new Map();
|
|
677
813
|
// A dispatch a hook denied never ran, so it is neither a run nor a missing verdict.
|
|
678
814
|
const held = records.filter((r) => r.status === 'denied').length;
|
|
679
|
-
const
|
|
680
|
-
// Agents outside the pipeline — Claude Code's own, a project's own — have no verdict to give. Read as
|
|
681
|
-
// stages they were 100% unreadable and were told to add a verdict token their spec never asked for.
|
|
682
|
-
const outside = new Map();
|
|
683
|
-
for (const r of records.filter((r) => r.status !== 'denied')) {
|
|
684
|
-
if (!PIPELINE_ROLES.has(r.role)) {
|
|
685
|
-
outside.set(r.role, (outside.get(r.role) ?? 0) + 1);
|
|
686
|
-
continue;
|
|
687
|
-
}
|
|
688
|
-
if (!byRole.has(r.role)) {
|
|
689
|
-
byRole.set(r.role, { n: 0, runs: new Set(), done: 0, lost: 0, clear: 0, loop: 0, unclear: 0, declared: 0, durations: [] });
|
|
690
|
-
}
|
|
691
|
-
const s = byRole.get(r.role);
|
|
692
|
-
s.n += 1;
|
|
693
|
-
s.runs.add(runOf(r));
|
|
694
|
-
if (!r.verdict && !running(r, now)) s.lost += 1;
|
|
695
|
-
if (r.verdict) s.done += 1;
|
|
696
|
-
if (r.verdict_source === 'declared' || r.verdict_source === 'handback') s.declared += 1;
|
|
697
|
-
if (r.verdict === 'UNCLEAR' || r.verdict === 'NONE') s.unclear += 1;
|
|
698
|
-
else if (r.verdict) {
|
|
699
|
-
s.clear += 1;
|
|
700
|
-
if (isLoopBack(r.verdict)) s.loop += 1;
|
|
701
|
-
}
|
|
702
|
-
if (typeof r.duration_s === 'number') s.durations.push(r.duration_s);
|
|
703
|
-
}
|
|
815
|
+
const { rows, outside } = stageData(records);
|
|
704
816
|
|
|
705
817
|
const span = [records[0]?.ts, records.at(-1)?.ts].map(localDay);
|
|
706
818
|
const projectCount = new Set(records.map((r) => r.project)).size;
|
|
@@ -721,38 +833,37 @@ export async function stats(argv, ctx) {
|
|
|
721
833
|
),
|
|
722
834
|
);
|
|
723
835
|
|
|
724
|
-
const rows = [...byRole.entries()].sort((a, b) => b[1].n - a[1].n);
|
|
725
836
|
// Each stage's runs as one bar, as long as its share of the busiest stage's: what went forward, what
|
|
726
837
|
// was sent back, and what said nothing readable (or is still running).
|
|
727
|
-
const busiest = Math.max(...rows.map((
|
|
838
|
+
const busiest = Math.max(...rows.map((s) => s.rounds), 1);
|
|
728
839
|
const zero = (text, n) => (n === 0 ? dim(text) : text);
|
|
729
|
-
for (const
|
|
840
|
+
for (const s of rows) {
|
|
730
841
|
const runsBar = stacked(
|
|
731
842
|
[
|
|
732
843
|
{ n: s.clear - s.loop, char: '█', paint: pink },
|
|
733
844
|
{ n: s.loop, char: '▓', paint: amber },
|
|
734
|
-
{ n: s.
|
|
845
|
+
{ n: s.rounds - s.clear, char: '░', paint: dim },
|
|
735
846
|
],
|
|
736
|
-
Math.max(1, Math.round((16 * s.
|
|
847
|
+
Math.max(1, Math.round((16 * s.rounds) / busiest)),
|
|
737
848
|
);
|
|
738
849
|
const declared = pct(s.declared, s.done).padStart(10);
|
|
739
850
|
const unread = pct(s.unclear + s.lost, s.done + s.lost).padStart(12);
|
|
740
851
|
console.log(
|
|
741
|
-
` ${pink(role)}${' '.repeat(Math.max(20 - role.length, 1))}${String(s.runs
|
|
852
|
+
` ${pink(s.role)}${' '.repeat(Math.max(20 - s.role.length, 1))}${String(s.runs).padStart(6)}${String(s.rounds).padStart(9)}` +
|
|
742
853
|
`${zero(String(s.loop).padStart(11), s.loop)}${zero(pct(s.loop, s.clear).padStart(7), s.loop)}` +
|
|
743
854
|
`${s.declared < s.done ? amber(declared) : dim(declared)}${s.unclear + s.lost > 0 ? amber(unread) : dim(unread)}` +
|
|
744
|
-
`${
|
|
855
|
+
`${(s.median_s === null ? '—' : `${(s.median_s / 60).toFixed(1)}m`).padStart(9)} ${runsBar}`,
|
|
745
856
|
);
|
|
746
857
|
}
|
|
747
858
|
console.log(dim(` ${' '.repeat(86)}█ forward ▓ sent back ░ no verdict read`));
|
|
748
859
|
|
|
749
|
-
const unreadable = rows.reduce((a,
|
|
750
|
-
const lost = rows.reduce((a,
|
|
751
|
-
const done = rows.reduce((a,
|
|
860
|
+
const unreadable = rows.reduce((a, s) => a + s.unclear, 0);
|
|
861
|
+
const lost = rows.reduce((a, s) => a + s.lost, 0);
|
|
862
|
+
const done = rows.reduce((a, s) => a + s.done, 0);
|
|
752
863
|
console.log('');
|
|
753
|
-
if (outside.
|
|
754
|
-
const named =
|
|
755
|
-
console.log(note(`${
|
|
864
|
+
if (outside.length > 0) {
|
|
865
|
+
const named = outside.map(([role, n]) => `${role} ×${n}`).join(', ');
|
|
866
|
+
console.log(note(`${outside.reduce((a, [, n]) => a + n, 0)} round(s) by agents outside the pipeline (${named}) — not stages, so no verdict is asked of them.`, 'info'));
|
|
756
867
|
}
|
|
757
868
|
if (unreadable === 0 && lost === 0) console.log(note("every finished round's verdict could be read.", 'ok'));
|
|
758
869
|
// The same share as the table's column: rounds that finished, or should have, with no verdict read.
|
|
@@ -799,20 +910,21 @@ export async function stats(argv, ctx) {
|
|
|
799
910
|
|
|
800
911
|
// A gate that never stops anything is either upstream quality or a rubber stamp, and the
|
|
801
912
|
// rate alone cannot say which — but it can say which gate to go and look at.
|
|
802
|
-
const gates = rows.filter((
|
|
803
|
-
for (const
|
|
913
|
+
const gates = rows.filter((s) => GATES.has(s.role));
|
|
914
|
+
for (const s of gates) {
|
|
915
|
+
const { role } = s;
|
|
804
916
|
if (s.clear < GATE_SAMPLE || s.loop / s.clear >= GATE_FLOOR) continue;
|
|
805
|
-
const others = gates.filter((
|
|
917
|
+
const others = gates.filter((o) => o.role !== role);
|
|
806
918
|
const elsewhere = pct(
|
|
807
|
-
others.reduce((a,
|
|
808
|
-
others.reduce((a,
|
|
919
|
+
others.reduce((a, o) => a + o.loop, 0),
|
|
920
|
+
others.reduce((a, o) => a + o.clear, 0),
|
|
809
921
|
);
|
|
810
922
|
console.log(
|
|
811
923
|
note(
|
|
812
924
|
`${role}: ${s.loop} loop-back(s) in ${s.clear} readable verdict(s) (${pct(s.loop, s.clear)}),` +
|
|
813
925
|
` against ${elsewhere} across the other gates — check whether it still gates anything.` +
|
|
814
|
-
(s.
|
|
815
|
-
? ` ${s.
|
|
926
|
+
(s.rounds - s.clear > 0
|
|
927
|
+
? ` ${s.rounds - s.clear} further round(s) produced no readable verdict at all, so the rate may understate it.`
|
|
816
928
|
: '') +
|
|
817
929
|
// A rate built from verdicts a parser guessed at is not the same claim as one built
|
|
818
930
|
// from verdicts the stage declared, and the difference decides whether it can be acted on.
|
|
@@ -832,7 +944,7 @@ export async function stats(argv, ctx) {
|
|
|
832
944
|
// What the pipeline learned, against what it had to learn from. A loop-back is the raw
|
|
833
945
|
// material and a pill is the product, so the two numbers belong on the same screen: the
|
|
834
946
|
// rule that turns one into the other is the only rule here that nothing else can check.
|
|
835
|
-
const loopBacks = rows.reduce((a,
|
|
947
|
+
const loopBacks = rows.reduce((a, s) => a + s.loop, 0);
|
|
836
948
|
const pills = await harvest([...new Set(records.map((r) => r.project))]);
|
|
837
949
|
const newest = pills.dates.at(-1);
|
|
838
950
|
const inWindow = pills.dates.filter((d) => d >= span[0] && d <= span[1]).length;
|