@xhulz/nina 0.37.0 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/releases/0.38.0/core/GAPS.md +125 -0
- package/releases/0.38.0/core/answered.json +32 -0
- package/releases/0.38.0/core/defaults/tree/.claude/agents/architect.md +1 -0
- package/releases/0.38.0/core/defaults/tree/.claude/agents/dba.md +3 -0
- package/releases/0.38.0/core/defaults/tree/.claude/agents/devops.md +1 -0
- package/releases/0.38.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
- package/releases/0.38.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.38.0/core/defaults/tree/.claude/agents/qa.md +2 -0
- package/releases/0.38.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
- package/releases/0.38.0/core/defaults/tree/.claude/agents/secops.md +3 -0
- package/releases/0.38.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
- package/releases/0.38.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
- package/releases/0.38.0/core/tree/.claude/agents/architect.md +127 -0
- package/releases/0.38.0/core/tree/.claude/agents/dba.md +110 -0
- package/releases/0.38.0/core/tree/.claude/agents/devops.md +114 -0
- package/releases/0.38.0/core/tree/.claude/agents/implementer.md +156 -0
- package/releases/0.38.0/core/tree/.claude/agents/integration-tester.md +168 -0
- package/releases/0.38.0/core/tree/.claude/agents/planner.md +132 -0
- package/releases/0.38.0/core/tree/.claude/agents/qa.md +152 -0
- package/releases/0.38.0/core/tree/.claude/agents/reviewer.md +183 -0
- package/releases/0.38.0/core/tree/.claude/agents/secops.md +121 -0
- package/releases/0.38.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
- package/releases/0.38.0/core/tree/.claude/agents/solidity-dev.md +123 -0
- package/releases/0.38.0/core/tree/.claude/agents-overview.md +64 -0
- package/releases/0.38.0/core/tree/.claude/frontend.md +10 -0
- package/releases/0.38.0/core/tree/.claude/graph.md +75 -0
- package/releases/0.38.0/core/tree/.claude/patterns.md +213 -0
- package/releases/0.38.0/core/tree/.claude/pills/README.md +147 -0
- package/releases/0.38.0/core/tree/.claude/pipeline.md +101 -0
- package/releases/0.38.0/core/tree/.claude/retrieval.md +103 -0
- package/releases/0.38.0/core/tree/.claude/router.md +198 -0
- package/releases/0.38.0/core/tree/.claude/templates/integration.md +45 -0
- package/releases/0.38.0/core/tree/CLAUDE.md +161 -0
- package/releases/0.38.0/core/tree/scripts/cost-watch.mjs +18 -0
- package/releases/0.38.0/core/tree/scripts/edit-guard.mjs +16 -0
- package/releases/0.38.0/core/tree/scripts/harness-check.mjs +71 -0
- package/releases/0.38.0/core/tree/scripts/loop-gate.mjs +19 -0
- package/releases/0.38.0/core/vocabulary.json +13 -0
- package/releases/0.38.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
- package/releases/0.38.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
- package/releases/0.38.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
- package/releases/0.38.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
- package/releases/0.38.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
- package/releases/0.38.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
- package/releases/0.38.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
- package/releases/0.38.0/surfaces/blockchain/tree/.claude/router.md +6 -0
- package/releases/0.38.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
- package/releases/0.38.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
- package/releases/0.38.0/surfaces/db/tree/.claude/agents/dba.md +50 -0
- package/releases/0.38.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
- package/releases/0.38.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.38.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
- package/releases/0.38.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
- package/releases/0.38.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
- package/releases/0.38.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
- package/releases/0.38.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
- package/releases/0.38.0/surfaces/db/tree/.claude/graph.md +8 -0
- package/releases/0.38.0/surfaces/db/tree/.claude/patterns.md +19 -0
- package/releases/0.38.0/surfaces/db/tree/.claude/pipeline.md +13 -0
- package/releases/0.38.0/surfaces/db/tree/.claude/retrieval.md +5 -0
- package/releases/0.38.0/surfaces/db/tree/.claude/router.md +4 -0
- package/releases/0.38.0/surfaces/db/tree/CLAUDE.md +17 -0
- package/releases/0.38.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
- package/releases/0.38.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
- package/releases/0.38.0/surfaces/edge-cf/tree/.claude/agents/devops.md +32 -0
- package/releases/0.38.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
- package/releases/0.38.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
- package/releases/0.38.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
- package/releases/0.38.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.38.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
- package/releases/0.38.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
- package/releases/0.38.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
- package/releases/0.38.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
- package/releases/0.38.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
- package/releases/0.38.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
- package/releases/0.38.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
- package/releases/0.38.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
- package/releases/0.38.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
- package/releases/0.38.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
- package/releases/0.38.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
- package/releases/0.38.0/surfaces/frontend/tree/.claude/router.md +14 -0
- package/releases/0.38.0/surfaces/frontend/tree/CLAUDE.md +5 -0
- package/releases/0.38.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
- package/releases/0.38.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
- package/releases/0.38.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
- package/releases/0.38.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
- package/releases/0.38.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
- package/releases/0.38.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
- package/releases/0.38.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
- package/releases/0.38.0/surfaces/integrations/tree/.claude/graph.md +10 -0
- package/releases/0.38.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
- package/releases/0.38.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
- package/releases/0.38.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
- package/releases/0.38.0/surfaces/integrations/tree/.claude/router.md +6 -0
- package/releases/0.38.0/surfaces/integrations/tree/CLAUDE.md +17 -0
- package/releases/0.38.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
- package/releases/0.38.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
- package/releases/0.38.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
- package/releases/0.38.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.38.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
- package/releases/0.38.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
- package/releases/0.38.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
- package/releases/0.38.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
- package/releases/0.38.0/surfaces/money/tree/.claude/graph.md +2 -0
- package/releases/0.38.0/surfaces/money/tree/.claude/patterns.md +49 -0
- package/releases/0.38.0/surfaces/money/tree/.claude/pipeline.md +2 -0
- package/releases/0.38.0/surfaces/money/tree/.claude/retrieval.md +2 -0
- package/releases/0.38.0/surfaces/money/tree/CLAUDE.md +11 -0
- package/releases/0.38.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
- package/releases/0.38.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
- package/releases/0.38.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.38.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
- package/releases/0.38.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
- package/releases/0.38.0/surfaces/pii/tree/CLAUDE.md +5 -0
- package/releases/0.38.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
- package/releases/0.38.0/surfaces/prisma/tree/.claude/agents/dba.md +39 -0
- package/releases/0.38.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
- package/releases/0.38.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.38.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
- package/releases/0.38.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
- package/releases/0.38.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
- package/releases/0.38.0/surfaces/prisma/tree/CLAUDE.md +5 -0
- package/releases/0.39.0/core/GAPS.md +125 -0
- package/releases/0.39.0/core/answered.json +32 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/architect.md +1 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/dba.md +3 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/devops.md +1 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/qa.md +2 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/secops.md +3 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
- package/releases/0.39.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
- package/releases/0.39.0/core/tree/.claude/agents/architect.md +127 -0
- package/releases/0.39.0/core/tree/.claude/agents/dba.md +110 -0
- package/releases/0.39.0/core/tree/.claude/agents/devops.md +114 -0
- package/releases/0.39.0/core/tree/.claude/agents/implementer.md +156 -0
- package/releases/0.39.0/core/tree/.claude/agents/integration-tester.md +168 -0
- package/releases/0.39.0/core/tree/.claude/agents/planner.md +132 -0
- package/releases/0.39.0/core/tree/.claude/agents/qa.md +152 -0
- package/releases/0.39.0/core/tree/.claude/agents/reviewer.md +183 -0
- package/releases/0.39.0/core/tree/.claude/agents/secops.md +121 -0
- package/releases/0.39.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
- package/releases/0.39.0/core/tree/.claude/agents/solidity-dev.md +123 -0
- package/releases/0.39.0/core/tree/.claude/agents-overview.md +64 -0
- package/releases/0.39.0/core/tree/.claude/frontend.md +10 -0
- package/releases/0.39.0/core/tree/.claude/graph.md +75 -0
- package/releases/0.39.0/core/tree/.claude/patterns.md +213 -0
- package/releases/0.39.0/core/tree/.claude/pills/README.md +147 -0
- package/releases/0.39.0/core/tree/.claude/pipeline.md +101 -0
- package/releases/0.39.0/core/tree/.claude/retrieval.md +103 -0
- package/releases/0.39.0/core/tree/.claude/router.md +198 -0
- package/releases/0.39.0/core/tree/.claude/templates/integration.md +45 -0
- package/releases/0.39.0/core/tree/CLAUDE.md +163 -0
- package/releases/0.39.0/core/tree/scripts/cost-watch.mjs +18 -0
- package/releases/0.39.0/core/tree/scripts/edit-guard.mjs +24 -0
- package/releases/0.39.0/core/tree/scripts/harness-check.mjs +71 -0
- package/releases/0.39.0/core/tree/scripts/loop-gate.mjs +19 -0
- package/releases/0.39.0/core/vocabulary.json +13 -0
- package/releases/0.39.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
- package/releases/0.39.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
- package/releases/0.39.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
- package/releases/0.39.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
- package/releases/0.39.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
- package/releases/0.39.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
- package/releases/0.39.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
- package/releases/0.39.0/surfaces/blockchain/tree/.claude/router.md +6 -0
- package/releases/0.39.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/agents/dba.md +50 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/graph.md +8 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/patterns.md +19 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/pipeline.md +13 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/retrieval.md +5 -0
- package/releases/0.39.0/surfaces/db/tree/.claude/router.md +4 -0
- package/releases/0.39.0/surfaces/db/tree/CLAUDE.md +17 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/agents/devops.md +32 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
- package/releases/0.39.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
- package/releases/0.39.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
- package/releases/0.39.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
- package/releases/0.39.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
- package/releases/0.39.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
- package/releases/0.39.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
- package/releases/0.39.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
- package/releases/0.39.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
- package/releases/0.39.0/surfaces/frontend/tree/.claude/router.md +14 -0
- package/releases/0.39.0/surfaces/frontend/tree/CLAUDE.md +5 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/graph.md +10 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
- package/releases/0.39.0/surfaces/integrations/tree/.claude/router.md +6 -0
- package/releases/0.39.0/surfaces/integrations/tree/CLAUDE.md +17 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/graph.md +2 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/patterns.md +49 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/pipeline.md +2 -0
- package/releases/0.39.0/surfaces/money/tree/.claude/retrieval.md +2 -0
- package/releases/0.39.0/surfaces/money/tree/CLAUDE.md +11 -0
- package/releases/0.39.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
- package/releases/0.39.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
- package/releases/0.39.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.39.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
- package/releases/0.39.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
- package/releases/0.39.0/surfaces/pii/tree/CLAUDE.md +5 -0
- package/releases/0.39.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
- package/releases/0.39.0/surfaces/prisma/tree/.claude/agents/dba.md +39 -0
- package/releases/0.39.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
- package/releases/0.39.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.39.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
- package/releases/0.39.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
- package/releases/0.39.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
- package/releases/0.39.0/surfaces/prisma/tree/CLAUDE.md +5 -0
- package/src/commands/compose.mjs +7 -3
- package/src/commands/init.mjs +1 -1
- package/src/commands/learn.mjs +226 -71
- package/src/commands/pills.mjs +32 -9
- package/src/commands/stats.mjs +4 -4
- package/src/deep.mjs +26 -4
- package/src/gate.mjs +59 -1
- package/src/guard.mjs +188 -0
- package/src/lessons.mjs +55 -0
- package/src/wiring.mjs +5 -2
package/src/gate.mjs
CHANGED
|
@@ -54,12 +54,14 @@
|
|
|
54
54
|
* gate that could block work on its own crash would be worse than the loops it exists to stop.
|
|
55
55
|
*/
|
|
56
56
|
|
|
57
|
+
import { spawnSync } from 'node:child_process';
|
|
57
58
|
import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, realpathSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
58
59
|
import { dirname, join } from 'node:path';
|
|
59
60
|
import { fileURLToPath } from 'node:url';
|
|
60
61
|
import { TERMINALS, declaredTokens, parseGraph } from './graph.mjs';
|
|
61
62
|
import { gateDir, slugFor } from './paths.mjs';
|
|
62
63
|
import { declaredIssues, isLoopBack, saidToAgent } from './transcripts.mjs';
|
|
64
|
+
import { lessonIndex } from './lessons.mjs';
|
|
63
65
|
|
|
64
66
|
/** The package this runs from: its releases say whether a pinned version ships the gate at all. */
|
|
65
67
|
const PACKAGE = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
@@ -431,7 +433,18 @@ export function replay(entries, loops, forward = new Map()) {
|
|
|
431
433
|
}
|
|
432
434
|
}
|
|
433
435
|
});
|
|
434
|
-
|
|
436
|
+
// What is open when the replay ends: each loop with its rounds, the rounds the owner's replies gave it and the
|
|
437
|
+
// issues it counted by, and each loop-back no fix has acted on yet — what a compaction takes from the orchestrator.
|
|
438
|
+
const open = [...streak.entries()]
|
|
439
|
+
.filter(([key]) => key.split('|').length === 2)
|
|
440
|
+
.map(([key, s]) => {
|
|
441
|
+
const [source, token] = key.split('|');
|
|
442
|
+
const cap = Math.max(0, ...(loops.get(source)?.get(token)?.values() ?? []));
|
|
443
|
+
const issues = [...streak.keys()].filter((k) => k.startsWith(`${key}|`)).map((k) => k.slice(key.length + 1));
|
|
444
|
+
return { source, token, rounds: s.n, cap, given: grace.get(key) ?? 0, issues };
|
|
445
|
+
});
|
|
446
|
+
const waiting = [...pending.entries()].flatMap(([source, batch]) => batch.filter((v) => isLoopBack(v.verdict)).map((v) => ({ source, verdict: v.verdict, issues: v.issues ?? [] })));
|
|
447
|
+
return { effect: (to) => effect(to, entries.length), rounds: made, open, waiting };
|
|
435
448
|
}
|
|
436
449
|
|
|
437
450
|
/**
|
|
@@ -659,6 +672,40 @@ function onPre(input, project, path, at) {
|
|
|
659
672
|
return confirmation(worst);
|
|
660
673
|
}
|
|
661
674
|
|
|
675
|
+
/**
|
|
676
|
+
* What the gate hands the orchestrator after its conversation was compacted: the loops still open and the
|
|
677
|
+
* loop-backs waiting on a fix, the latest dispatches, where the checkout stands, and the two documents to read
|
|
678
|
+
* again. The graph and the router are read once, at a session's start, and a compaction drops them with every
|
|
679
|
+
* report; the router told the orchestrator to write "round 2 of max 2" in a dispatch so "you after a
|
|
680
|
+
* compaction" could see it, and after a compaction it sees only a summary.
|
|
681
|
+
*
|
|
682
|
+
* @param {object} project - `loadProject`'s.
|
|
683
|
+
* @param {string} path - The session's ledger.
|
|
684
|
+
* @param {string} root - The project.
|
|
685
|
+
* @returns {object}
|
|
686
|
+
*/
|
|
687
|
+
function afterCompaction(project, path, root) {
|
|
688
|
+
const entries = readLedger(path);
|
|
689
|
+
const { open, waiting } = replay(entries, project.loops, project.forward);
|
|
690
|
+
const lines = ['The conversation was compacted. What the NINA loop gate kept of this session:'];
|
|
691
|
+
for (const l of open) {
|
|
692
|
+
lines.push(
|
|
693
|
+
`- open loop: ${l.source} ${l.token} — ${l.rounds} round(s), capped at ${l.cap}${l.given ? ` and ${l.given} more from the owner's replies` : ''}` +
|
|
694
|
+
(l.issues.length ? `, issues ${l.issues.join(', ')}` : ''),
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
for (const w of waiting) lines.push(`- waiting on a fix: ${w.source} ${w.verdict}${w.issues.length ? `, issues ${w.issues.join(', ')}` : ''}`);
|
|
698
|
+
if (open.length === 0 && waiting.length === 0) lines.push('- no loop is open');
|
|
699
|
+
const dispatched = entries.filter((e) => e.k === 'dispatch' && e.role).slice(-4);
|
|
700
|
+
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`);
|
|
701
|
+
const git = (...args) => spawnSync('git', ['-C', root, ...args], { encoding: 'utf8', timeout: 2000 }).stdout?.trim() ?? '';
|
|
702
|
+
const branch = git('rev-parse', '--abbrev-ref', 'HEAD');
|
|
703
|
+
const last = git('log', '-1', '--format=%h %s');
|
|
704
|
+
if (branch) lines.push(`- the checkout: branch ${branch}${last ? `, last commit ${last.slice(0, 100)}` : ''}`);
|
|
705
|
+
lines.push('Before the next dispatch, read .claude/graph.md and .claude/router.md again: they were read at the start of the session, and are no longer in your context.');
|
|
706
|
+
return { hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: lines.join('\n') } };
|
|
707
|
+
}
|
|
708
|
+
|
|
662
709
|
/** Drops a project's ledgers nobody has written to in a month, and error lines older than a week. */
|
|
663
710
|
function prune(root, now) {
|
|
664
711
|
const dir = projectGateDir(root);
|
|
@@ -718,6 +765,17 @@ export function handle(input, { root, now = new Date(), pkg = PACKAGE }) {
|
|
|
718
765
|
return null;
|
|
719
766
|
case 'SubagentStop':
|
|
720
767
|
return onStop(input, project, path, at);
|
|
768
|
+
case 'SessionStart':
|
|
769
|
+
return input.source === 'compact' ? afterCompaction(project, path, root) : null;
|
|
770
|
+
case 'SubagentStart': {
|
|
771
|
+
// Not the loop's business, but the one hook every stage passes through as it begins: its lessons. Claude
|
|
772
|
+
// Code reads `additionalContext` from this event's `hookSpecificOutput` into the subagent's context as it
|
|
773
|
+
// starts — its hook schema and handler say so in 2.1.283, and the handler in 2.1.158 — though the help text
|
|
774
|
+
// it prints for a malformed hook output does not list the event.
|
|
775
|
+
const role = input.agent_type;
|
|
776
|
+
const text = role && project.graph.stages.has(role) ? lessonIndex(root, role) : null;
|
|
777
|
+
return text ? { hookSpecificOutput: { hookEventName: 'SubagentStart', additionalContext: text } } : null;
|
|
778
|
+
}
|
|
721
779
|
case 'PostToolUse':
|
|
722
780
|
return onPost(input, project, path, at);
|
|
723
781
|
case 'PreToolUse':
|
package/src/guard.mjs
CHANGED
|
@@ -32,6 +32,190 @@ const PACKAGE = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
|
32
32
|
/** The tools that change a file, and where each names it. */
|
|
33
33
|
const EDITS = { Edit: 'file_path', Write: 'file_path', MultiEdit: 'file_path', NotebookEdit: 'notebook_path' };
|
|
34
34
|
|
|
35
|
+
/** The git subcommands that move the checkout or write its history — Hard Rule #18's. */
|
|
36
|
+
const MOVES = new Set(['stash', 'checkout', 'switch', 'reset', 'restore', 'clean', 'commit', 'merge', 'rebase', 'cherry-pick', 'revert', 'am', 'apply', 'pull', 'push']);
|
|
37
|
+
|
|
38
|
+
/** Words before the command word that leave it the command: `env X=1 git …`, `command git …`, `{ git …; }`. */
|
|
39
|
+
const RUNNERS = new Set(['env', 'command', 'exec', 'time', 'nohup', 'nice', '{', '}', '!']);
|
|
40
|
+
|
|
41
|
+
/** A `-C` or a `cd` to one of these is still the tree the stage was dispatched into. */
|
|
42
|
+
const HERE = new Set(['.', './']);
|
|
43
|
+
|
|
44
|
+
/** Where a heredoc's delimiter word ends. */
|
|
45
|
+
const DELIMITER_ENDS = new Set([' ', '\t', '\r', '\n', ';', '&', '|', '(', ')', '`', '<', '>']);
|
|
46
|
+
|
|
47
|
+
/** The characters that can end a run of plain word characters. */
|
|
48
|
+
const SPECIAL = new Set(["'", '"', '\\', '#', '\n', '<', '&', ';', '|', '(', ')', '`', ' ', '\t', '\r']);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* A shell command as the shell reads it: its simple commands, in order, each as its words with the quotes removed.
|
|
52
|
+
* `&&`, `||`, `;`, `|`, `&`, a newline, a parenthesis and a backquote end one; a quoted string is part of a word — `"git" stash`
|
|
53
|
+
* is git — and never a separator or a command of its own, across lines too; a comment and a heredoc's body are not
|
|
54
|
+
* read. One pass, no pattern: the command is a model's, and a pattern that backtracks on it is one a stage could
|
|
55
|
+
* stall the guard with.
|
|
56
|
+
*
|
|
57
|
+
* @param {string} command
|
|
58
|
+
* @returns {string[][]}
|
|
59
|
+
*/
|
|
60
|
+
export function simpleCommands(command) {
|
|
61
|
+
const text = String(command);
|
|
62
|
+
const commands = [[]];
|
|
63
|
+
const heredocs = [];
|
|
64
|
+
let word = null;
|
|
65
|
+
const end = () => {
|
|
66
|
+
if (word !== null) commands.at(-1).push(word);
|
|
67
|
+
word = null;
|
|
68
|
+
};
|
|
69
|
+
const next = () => {
|
|
70
|
+
end();
|
|
71
|
+
if (commands.at(-1).length) commands.push([]);
|
|
72
|
+
};
|
|
73
|
+
let i = 0;
|
|
74
|
+
while (i < text.length) {
|
|
75
|
+
const c = text[i];
|
|
76
|
+
if (c === "'") {
|
|
77
|
+
const close = text.indexOf("'", i + 1);
|
|
78
|
+
const stop = close === -1 ? text.length : close;
|
|
79
|
+
word = (word ?? '') + text.slice(i + 1, stop);
|
|
80
|
+
i = stop + 1;
|
|
81
|
+
} else if (c === '"') {
|
|
82
|
+
const parts = [];
|
|
83
|
+
i += 1;
|
|
84
|
+
let from = i;
|
|
85
|
+
while (i < text.length && text[i] !== '"') {
|
|
86
|
+
if (text[i] === '\\' && i + 1 < text.length) {
|
|
87
|
+
parts.push(text.slice(from, i));
|
|
88
|
+
from = i + 1;
|
|
89
|
+
i += 2;
|
|
90
|
+
} else i += 1;
|
|
91
|
+
}
|
|
92
|
+
parts.push(text.slice(from, i));
|
|
93
|
+
word = (word ?? '') + parts.join('');
|
|
94
|
+
i += 1;
|
|
95
|
+
} else if (c === '\\') {
|
|
96
|
+
if (text[i + 1] !== '\n') word = (word ?? '') + (text[i + 1] ?? '');
|
|
97
|
+
i += 2;
|
|
98
|
+
} else if (c === '#' && word === null) {
|
|
99
|
+
const line = text.indexOf('\n', i);
|
|
100
|
+
i = line === -1 ? text.length : line;
|
|
101
|
+
} else if (c === '\n') {
|
|
102
|
+
next();
|
|
103
|
+
i += 1;
|
|
104
|
+
for (const delimiter of heredocs.splice(0)) {
|
|
105
|
+
while (i < text.length) {
|
|
106
|
+
const line = text.indexOf('\n', i);
|
|
107
|
+
const stop = line === -1 ? text.length : line;
|
|
108
|
+
const body = text.slice(i, stop);
|
|
109
|
+
i = stop + 1;
|
|
110
|
+
if (body.trim() === delimiter) break;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} else if (c === '<' && text.startsWith('<<<', i)) {
|
|
114
|
+
word = (word ?? '') + '<<<';
|
|
115
|
+
i += 3;
|
|
116
|
+
} else if (c === '<' && text[i + 1] === '<') {
|
|
117
|
+
end();
|
|
118
|
+
i += text[i + 2] === '-' ? 3 : 2;
|
|
119
|
+
while (text[i] === ' ' || text[i] === '\t') i += 1;
|
|
120
|
+
const from = i;
|
|
121
|
+
while (i < text.length && !DELIMITER_ENDS.has(text[i])) i += 1;
|
|
122
|
+
const delimiter = text.slice(from, i).replaceAll("'", '').replaceAll('"', '').replaceAll('\\', '');
|
|
123
|
+
if (delimiter) heredocs.push(delimiter);
|
|
124
|
+
} else if (c === '&' && (text[i + 1] === '>' || word?.endsWith('>') || word?.endsWith('<'))) {
|
|
125
|
+
word = (word ?? '') + c;
|
|
126
|
+
i += 1;
|
|
127
|
+
} else if (c === ';' || c === '&' || c === '|' || c === '(' || c === ')' || c === '`') {
|
|
128
|
+
next();
|
|
129
|
+
i += (c === '&' || c === '|') && text[i + 1] === c ? 2 : 1;
|
|
130
|
+
} else if (c === ' ' || c === '\t' || c === '\r') {
|
|
131
|
+
end();
|
|
132
|
+
i += 1;
|
|
133
|
+
} else {
|
|
134
|
+
const from = i;
|
|
135
|
+
i += 1;
|
|
136
|
+
while (i < text.length && !SPECIAL.has(text[i])) i += 1;
|
|
137
|
+
word = (word ?? '') + text.slice(from, i);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
end();
|
|
141
|
+
return commands.filter((words) => words.length);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* The git subcommand in a shell command that would move the checkout the command runs in, or null. Read command
|
|
146
|
+
* by command, in order, as the shell runs it: a `cd` exempts only what comes after it — `git stash && cd /tmp`
|
|
147
|
+
* stashed the tree all the same, and a test of the whole string for a `cd` let it through — and `-C .` is not
|
|
148
|
+
* another directory. A git command is one whose command word is `git`, not a phrase in a quoted string or a
|
|
149
|
+
* heredoc's body; its subcommand is the word after its global options, so `merge-base` is not `merge`; and a
|
|
150
|
+
* stash's `list` or `show`, and an `apply` that only checks, read.
|
|
151
|
+
*
|
|
152
|
+
* @param {string} command - A Bash tool call's command.
|
|
153
|
+
* @returns {string|null}
|
|
154
|
+
*/
|
|
155
|
+
export function checkoutMove(command) {
|
|
156
|
+
let moved = false;
|
|
157
|
+
for (const words of simpleCommands(command)) {
|
|
158
|
+
while (words.length && (/^[A-Za-z_][A-Za-z0-9_]*=/.test(words[0]) || RUNNERS.has(words[0]))) words.shift();
|
|
159
|
+
if (words[0] === 'cd' || words[0] === 'pushd') {
|
|
160
|
+
moved ||= !HERE.has(words[1] ?? '');
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (words[0] !== 'git') continue;
|
|
164
|
+
let i = 1;
|
|
165
|
+
let elsewhere = false;
|
|
166
|
+
while (words[i]?.startsWith('-')) {
|
|
167
|
+
if (words[i] === '-C') {
|
|
168
|
+
elsewhere ||= !HERE.has(words[i + 1] ?? '.');
|
|
169
|
+
i += 2;
|
|
170
|
+
} else if (words[i] === '-c') {
|
|
171
|
+
i += 2;
|
|
172
|
+
} else {
|
|
173
|
+
if (/^--(git-dir|work-tree)\b/.test(words[i])) elsewhere = true;
|
|
174
|
+
i += 1;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const sub = words[i];
|
|
178
|
+
if (!MOVES.has(sub) || moved || elsewhere) continue;
|
|
179
|
+
const rest = words.slice(i + 1);
|
|
180
|
+
if (sub === 'stash' && ['list', 'show'].includes(rest[0])) continue;
|
|
181
|
+
if (sub === 'apply' && rest.some((w) => ['--check', '--stat', '--numstat', '--summary'].includes(w)) && !rest.includes('--apply')) continue;
|
|
182
|
+
return sub;
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* A stage's shell command that would move the checkout it was dispatched into, refused. Only the orchestrator
|
|
189
|
+
* commits, and the work in flight lives in that tree, uncommitted: one reviewer, told not to edit, stashed it to
|
|
190
|
+
* compare with the baseline, and its pop failed on a conflict.
|
|
191
|
+
*
|
|
192
|
+
* @param {object} input - The hook's stdin, parsed.
|
|
193
|
+
* @param {{layers: string}} pinned - The pinned layers, whose hard rules say whether this one is the project's.
|
|
194
|
+
* @returns {object|null}
|
|
195
|
+
*/
|
|
196
|
+
function handleShell(input, pinned) {
|
|
197
|
+
if (!input.agent_id) return null;
|
|
198
|
+
const moves = checkoutMove(input.tool_input?.command ?? '');
|
|
199
|
+
if (!moves) return null;
|
|
200
|
+
let rules = '';
|
|
201
|
+
try {
|
|
202
|
+
rules = readFileSync(join(pinned.layers, 'core', 'tree', 'CLAUDE.md'), 'utf8');
|
|
203
|
+
} catch {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
if (!rules.includes('Only the orchestrator commits')) return null;
|
|
207
|
+
return {
|
|
208
|
+
hookSpecificOutput: {
|
|
209
|
+
hookEventName: 'PreToolUse',
|
|
210
|
+
permissionDecision: 'deny',
|
|
211
|
+
permissionDecisionReason:
|
|
212
|
+
`NINA: \`git ${moves}\` would move the checkout the pipeline's work in flight lives in — Hard Rule #18: only the orchestrator ` +
|
|
213
|
+
'commits, and no stage moves the checkout. Read history with git diff, log, show, status or blame, and build or test another ' +
|
|
214
|
+
'revision in a `git worktree` you remove afterwards.',
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
35
219
|
/** How much of a file is read to find it: the head, not the whole of a large file on every edit. */
|
|
36
220
|
const HEAD_BYTES = 16 * 1024;
|
|
37
221
|
|
|
@@ -83,6 +267,10 @@ function head(path) {
|
|
|
83
267
|
export async function handleEdit(input, { root, pkg = PACKAGE }) {
|
|
84
268
|
try {
|
|
85
269
|
if (input?.hook_event_name !== 'PreToolUse') return null;
|
|
270
|
+
if (input.tool_name === 'Bash') {
|
|
271
|
+
const pinnedShell = input.agent_id ? pinnedLayers(root, pkg) : null;
|
|
272
|
+
return pinnedShell ? handleShell(input, pinnedShell) : null;
|
|
273
|
+
}
|
|
86
274
|
const key = EDITS[input.tool_name];
|
|
87
275
|
const path = key ? input.tool_input?.[key] : null;
|
|
88
276
|
const pinned = typeof path === 'string' ? pinnedLayers(root, pkg) : null;
|
package/src/lessons.mjs
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The lessons a stage is handed as it starts: the list of its active pills, each with the trigger that says
|
|
3
|
+
* when it applies, given to the subagent by the `SubagentStart` hook.
|
|
4
|
+
*
|
|
5
|
+
* Every spec told its stage to read its pills, and a stage found them by opening files: every pill in its own
|
|
6
|
+
* directory, and every shared one to see whether its `applies_to` named it. So a reviewer read one in only
|
|
7
|
+
* seven of fifteen runs while twelve applied to it, and a stage no pill named opened them in two runs of three.
|
|
8
|
+
* Handed the list, a stage opens the ones whose trigger matches its task, and nothing else.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
import { frontmatter, list } from './commands/pills.mjs';
|
|
14
|
+
|
|
15
|
+
/** How much of a trigger goes in the list: enough to say when, not the lesson. */
|
|
16
|
+
const TRIGGER_CHARS = 160;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A stage's lessons as the text handed to it, or null when it has none.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} root - The project directory.
|
|
22
|
+
* @param {string} role - The stage.
|
|
23
|
+
* @returns {string|null}
|
|
24
|
+
*/
|
|
25
|
+
export function lessonIndex(root, role) {
|
|
26
|
+
const found = [];
|
|
27
|
+
for (const sub of [role, 'shared']) {
|
|
28
|
+
let files = [];
|
|
29
|
+
try {
|
|
30
|
+
files = readdirSync(join(root, '.claude', 'pills', sub)).filter((f) => f.endsWith('.md')).sort();
|
|
31
|
+
} catch {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
for (const file of files) {
|
|
35
|
+
let fields = null;
|
|
36
|
+
try {
|
|
37
|
+
fields = frontmatter(readFileSync(join(root, '.claude', 'pills', sub, file), 'utf8'));
|
|
38
|
+
} catch {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (!fields || fields.status === 'retired') continue;
|
|
42
|
+
const applies = list(fields.applies_to);
|
|
43
|
+
// A shared pill is this stage's when it names it; one in the stage's own directory, unless it names others.
|
|
44
|
+
if (sub === 'shared' ? !applies.includes(role) : applies.length > 0 && !applies.includes(role)) continue;
|
|
45
|
+
const trigger = String(fields.trigger ?? '').replace(/\s+/g, ' ').trim();
|
|
46
|
+
const when = trigger ? `when ${trigger.length > TRIGGER_CHARS ? `${trigger.slice(0, TRIGGER_CHARS - 1)}…` : trigger}` : 'on every task';
|
|
47
|
+
found.push(`- \`.claude/pills/${sub}/${file}\` — ${when}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (found.length === 0) return null;
|
|
51
|
+
return [
|
|
52
|
+
`Your lessons in this project, ${found.length}: before acting, open each one whose trigger matches this task, and only those.`,
|
|
53
|
+
...found,
|
|
54
|
+
].join('\n');
|
|
55
|
+
}
|
package/src/wiring.mjs
CHANGED
|
@@ -109,7 +109,7 @@ const previousCommands = (h) => [command(h.script, h.mode), ...EARLIER_CANNOT_ST
|
|
|
109
109
|
* The harness check has two: `Stop` tells the person what a turn left behind, `UserPromptSubmit` puts
|
|
110
110
|
* the same findings in the model's context before it answers — the only one of the two it reads. The
|
|
111
111
|
* loop gate has four, one per fact it keeps: a verdict, a dispatch, the owner speaking, and the
|
|
112
|
-
* decision itself.
|
|
112
|
+
* decision itself; and a fifth, as each stage starts, that hands the stage its lessons (`src/lessons.mjs`).
|
|
113
113
|
*/
|
|
114
114
|
export const HOOKS = [
|
|
115
115
|
{
|
|
@@ -132,8 +132,11 @@ export const HOOKS = [
|
|
|
132
132
|
},
|
|
133
133
|
{ event: 'PostToolUse', matcher: 'Agent|Task|SendMessage|AskUserQuestion|SubagentHandback', script: GATE, timeout: 10, why: 'the gate never learns which rounds went out, or that the owner answered' },
|
|
134
134
|
{ event: 'SubagentStop', script: GATE, timeout: 10, why: 'the gate misses the verdict of a stage that wrote its report as its last message' },
|
|
135
|
+
{ event: 'SubagentStart', script: GATE, timeout: 10, why: 'a stage is not handed its lessons, and opens every shared pill to find them, or none' },
|
|
136
|
+
{ event: 'SessionStart', matcher: 'compact', script: GATE, timeout: 10, why: 'after a compaction the orchestrator has lost the graph, the router and its open loops, and nothing gives them back' },
|
|
135
137
|
{
|
|
136
|
-
event: 'PreToolUse', matcher: 'Edit|Write|MultiEdit|NotebookEdit', script: GUARD, timeout: 10,
|
|
138
|
+
event: 'PreToolUse', matcher: 'Edit|Write|MultiEdit|NotebookEdit|Bash', script: GUARD, timeout: 10,
|
|
139
|
+
why: "a hand edit to a composed file is found only after the turn, as drift, and a stage's git stash or checkout takes the work in flight",
|
|
137
140
|
crash: (cannot) => ({ systemMessage: `NINA: the edit guard could not start, so composed files can be edited in place — ${cannot}` }),
|
|
138
141
|
},
|
|
139
142
|
// Every tool, since what it watches is a subagent's every turn; a call outside a subagent returns at once.
|