claude-code-session-manager 0.83.0 → 0.84.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/dist/assets/{AgentLibrary-B-OM-4gk.js → AgentLibrary-CCVIpoSz.js} +1 -1
- package/dist/assets/{DataModel-BC6ppcBr.js → DataModel-BltREYde.js} +1 -1
- package/dist/assets/{History-D3Qw2BWn.js → History-BZxFkOp6.js} +2 -2
- package/dist/assets/{Hooks-OxBQ0nIc.js → Hooks-Bznlfaaa.js} +1 -1
- package/dist/assets/{HostBilko-BNFC6bNZ.js → HostBilko-DUG5YHA_.js} +1 -1
- package/dist/assets/{Library-DAMyrUtY.js → Library-Bi2Fn3w9.js} +1 -1
- package/dist/assets/{ListDetail-BwywrpvE.js → ListDetail-4VBvXKrz.js} +1 -1
- package/dist/assets/{MarkdownEditor-HEwjN1bp.js → MarkdownEditor-D2ft_v5j.js} +1 -1
- package/dist/assets/{McpServers-D1VZF7Ix.js → McpServers-FDekKkyE.js} +1 -1
- package/dist/assets/{Memory-CgYJ_QqD.js → Memory-Dkl80uj_.js} +6 -6
- package/dist/assets/{Panel-dTNWC59Q.js → Panel-LurG5VfD.js} +1 -1
- package/dist/assets/{Permissions-qI_NKi1b.js → Permissions-D2wHBCFA.js} +1 -1
- package/dist/assets/{Plugins-DZ0_CHiF.js → Plugins-CTw_wwbI.js} +2 -2
- package/dist/assets/{ProvenanceBadge-B2i_uxCu.js → ProvenanceBadge-CW6HNUv6.js} +1 -1
- package/dist/assets/{SaveBar-4tNMCjUd.js → SaveBar-alDHG6cP.js} +1 -1
- package/dist/assets/{Scheduler-CUen18Gc.js → Scheduler-DxiPcaiW.js} +7 -7
- package/dist/assets/{ScopeSwitcher-Co3tkLTT.js → ScopeSwitcher-DzFXUKLZ.js} +1 -1
- package/dist/assets/Settings-B6H3v2am.js +3 -0
- package/dist/assets/{SkillReferenceGraph-BKcHtMPc.js → SkillReferenceGraph-B8EZalpN.js} +1 -1
- package/dist/assets/{Skills-DHb4INtF.js → Skills-DwuHuA08.js} +2 -2
- package/dist/assets/SystemPrompt-CMMqpGYn.js +1 -0
- package/dist/assets/{TagLibrary-xLP5dJvh.js → TagLibrary-C2N5C1n9.js} +1 -1
- package/dist/assets/{TiptapBody-T-ULTjK4.js → TiptapBody-btlID-dQ.js} +1 -1
- package/dist/assets/{Toggle-B-5M2hGe.js → Toggle-BGOn3DZj.js} +1 -1
- package/dist/assets/{index-CzAxC432.js → index-QLRf0epp.js} +316 -314
- package/dist/assets/{index-CYhdtisq.css → index-mnjNDpb1.css} +1 -1
- package/dist/assets/{settingsSchema-C_nsemcF.js → settingsSchema-DA3N2Up3.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +4 -1
- package/scripts/hooks/guard-destructive-git.cjs +514 -0
- package/scripts/hooks/guard-inline-implementation.cjs +219 -0
- package/scripts/hooks/guard-prd-writes.cjs +200 -0
- package/src/main/__tests__/epicMintTelemetryTap.test.cjs +64 -0
- package/src/main/__tests__/health-delegation-chain.test.cjs +2 -1
- package/src/main/__tests__/health-queue-dispatch.test.cjs +84 -0
- package/src/main/__tests__/health-usage-poller.test.cjs +97 -0
- package/src/main/__tests__/health-worktree-cap-blocked.test.cjs +65 -0
- package/src/main/__tests__/opsErrorLogTelemetryTap.test.cjs +143 -0
- package/src/main/__tests__/pollLoop-dispatch-on-failure.test.cjs +120 -0
- package/src/main/__tests__/promptSessionTranscript.test.cjs +0 -0
- package/src/main/__tests__/queue-starvation-dispatch-driver.test.cjs +143 -0
- package/src/main/__tests__/rateLimitPollerStreak.test.cjs +79 -0
- package/src/main/__tests__/scheduleJobTransitionsTelemetryTap.test.cjs +72 -0
- package/src/main/__tests__/scheduler-inplace-salvage.test.cjs +74 -0
- package/src/main/__tests__/scheduler-job-overrun.test.cjs +58 -0
- package/src/main/__tests__/scheduler-notify-originating-tab-transcript.test.cjs +1 -0
- package/src/main/__tests__/scheduler-periodic-reverify-guard.test.cjs +134 -2
- package/src/main/__tests__/scheduler-reap-dead-running-jobs.test.cjs +33 -0
- package/src/main/__tests__/scheduler-stuck-failed-escalation.test.cjs +136 -0
- package/src/main/__tests__/telemetryClient.test.cjs +66 -0
- package/src/main/__tests__/telemetryContract.test.cjs +883 -0
- package/src/main/crashDiagnostics.cjs +29 -1
- package/src/main/health.cjs +197 -3
- package/src/main/index.cjs +65 -6
- package/src/main/ipcSchemas.cjs +19 -2
- package/src/main/lib/__tests__/crashTelemetry.test.cjs +97 -0
- package/src/main/lib/__tests__/delegationReadiness.test.cjs +302 -4
- package/src/main/lib/__tests__/fixtures/scheduler-machine.json.corrupt-1789147548 +34 -0
- package/src/main/lib/__tests__/gitWorktree.test.cjs +413 -1
- package/src/main/lib/__tests__/jobWorktreeBootLive.test.cjs +71 -0
- package/src/main/lib/__tests__/queueStoreAtomicWrite.test.cjs +88 -0
- package/src/main/lib/__tests__/queueStoreMachineStateRecovery.test.cjs +123 -0
- package/src/main/lib/__tests__/reaperHelpers.test.cjs +58 -0
- package/src/main/lib/__tests__/telemetryBacklog.test.cjs +620 -0
- package/src/main/lib/__tests__/telemetryBoot.test.cjs +125 -0
- package/src/main/lib/__tests__/telemetryConsent.test.cjs +130 -0
- package/src/main/lib/__tests__/telemetryCounters.test.cjs +57 -0
- package/src/main/lib/__tests__/telemetryCountersMetadataColumn.test.cjs +89 -0
- package/src/main/lib/crashTelemetry.cjs +37 -0
- package/src/main/lib/delegationReadiness.cjs +119 -1
- package/src/main/lib/epicMint.cjs +2 -0
- package/src/main/lib/gitWorktree.cjs +427 -17
- package/src/main/lib/jobWorktree.cjs +2 -1
- package/src/main/lib/jobWorktreeBootLive.cjs +51 -0
- package/src/main/lib/jobWorktreeTerminalOrphanLive.cjs +68 -0
- package/src/main/lib/opsErrorLog.cjs +78 -25
- package/src/main/lib/queueStore.cjs +233 -20
- package/src/main/lib/reaperHelpers.cjs +23 -1
- package/src/main/lib/scheduleJobSchema.cjs +7 -0
- package/src/main/lib/scheduleJobTransitions.cjs +12 -0
- package/src/main/lib/telemetryBacklog.cjs +601 -0
- package/src/main/lib/telemetryBoot.cjs +71 -0
- package/src/main/lib/telemetryClient.cjs +31 -0
- package/src/main/lib/telemetryConsent.cjs +34 -0
- package/src/main/lib/telemetryCounters.cjs +45 -0
- package/src/main/promptSessionTranscript.cjs +0 -0
- package/src/main/pty.cjs +2 -0
- package/src/main/scheduler.cjs +481 -33
- package/src/preload/api.d.ts +84 -4
- package/src/preload/index.cjs +9 -0
- package/dist/assets/Settings-DXVgKLUx.js +0 -3
- package/dist/assets/SystemPrompt-BjqFOHSk.js +0 -1
|
@@ -15,10 +15,12 @@ const {
|
|
|
15
15
|
checkDelegationReadiness,
|
|
16
16
|
installPrdWriteGuard,
|
|
17
17
|
installDestructiveGitGuard,
|
|
18
|
+
installInlineImplementationGuard,
|
|
18
19
|
probeSchedulerMcpLive,
|
|
19
20
|
clearLiveProbeCache,
|
|
20
21
|
PRD_WRITE_GUARD_SCRIPT,
|
|
21
22
|
DESTRUCTIVE_GIT_GUARD_SCRIPT,
|
|
23
|
+
INLINE_IMPLEMENTATION_GUARD_SCRIPT,
|
|
22
24
|
} = require('../delegationReadiness.cjs');
|
|
23
25
|
|
|
24
26
|
const REQUIRED_TOOLS = ['scheduler_create_prd', 'session_manager_help'];
|
|
@@ -97,7 +99,10 @@ async function makeGreenFixtures() {
|
|
|
97
99
|
PreToolUse: [
|
|
98
100
|
{
|
|
99
101
|
matcher: 'Write|Edit|NotebookEdit',
|
|
100
|
-
hooks: [
|
|
102
|
+
hooks: [
|
|
103
|
+
{ type: 'command', command: `node ${PRD_WRITE_GUARD_SCRIPT}` },
|
|
104
|
+
{ type: 'command', command: `node ${INLINE_IMPLEMENTATION_GUARD_SCRIPT}` },
|
|
105
|
+
],
|
|
101
106
|
},
|
|
102
107
|
{
|
|
103
108
|
matcher: 'Bash',
|
|
@@ -110,12 +115,12 @@ async function makeGreenFixtures() {
|
|
|
110
115
|
return { homeDir, cwd, scriptPath };
|
|
111
116
|
}
|
|
112
117
|
|
|
113
|
-
test('all
|
|
118
|
+
test('all eight checks pass on a fully-configured project', async () => {
|
|
114
119
|
const { homeDir, cwd } = await makeGreenFixtures();
|
|
115
120
|
const result = await checkDelegationReadiness({ cwd, homeDir });
|
|
116
121
|
|
|
117
122
|
expect(result.ok).toBe(true);
|
|
118
|
-
expect(result.checks).toHaveLength(
|
|
123
|
+
expect(result.checks).toHaveLength(8);
|
|
119
124
|
expect(result.checks.every((c) => c.ok)).toBe(true);
|
|
120
125
|
expect(result.checks.map((c) => c.id)).toEqual([
|
|
121
126
|
'scheduler-mcp',
|
|
@@ -125,6 +130,7 @@ test('all seven checks pass on a fully-configured project', async () => {
|
|
|
125
130
|
'agent-personas',
|
|
126
131
|
'prd-write-guard',
|
|
127
132
|
'destructive-git-guard',
|
|
133
|
+
'inline-implementation-guard',
|
|
128
134
|
]);
|
|
129
135
|
}, 15_000);
|
|
130
136
|
|
|
@@ -399,7 +405,10 @@ test('destructive-git-guard: fails independently when the hook is missing', asyn
|
|
|
399
405
|
PreToolUse: [
|
|
400
406
|
{
|
|
401
407
|
matcher: 'Write|Edit|NotebookEdit',
|
|
402
|
-
hooks: [
|
|
408
|
+
hooks: [
|
|
409
|
+
{ type: 'command', command: `node ${PRD_WRITE_GUARD_SCRIPT}` },
|
|
410
|
+
{ type: 'command', command: `node ${INLINE_IMPLEMENTATION_GUARD_SCRIPT}` },
|
|
411
|
+
],
|
|
403
412
|
},
|
|
404
413
|
],
|
|
405
414
|
},
|
|
@@ -577,3 +586,292 @@ test('the installed destructive-git command actually DENIES a shared-tree stash
|
|
|
577
586
|
const allowed = run('git stash', worktreeCwd);
|
|
578
587
|
expect(allowed.hookSpecificOutput?.permissionDecision).not.toBe('deny');
|
|
579
588
|
});
|
|
589
|
+
|
|
590
|
+
// ─────────────────────────────── inline-implementation-guard / installInlineImplementationGuard
|
|
591
|
+
|
|
592
|
+
test('inline-implementation-guard: fails independently when the hook is missing (not-installed detail)', async () => {
|
|
593
|
+
const { homeDir, cwd } = await makeGreenFixtures();
|
|
594
|
+
await writeJson(path.join(cwd, '.claude', 'settings.json'), {
|
|
595
|
+
hooks: {
|
|
596
|
+
PreToolUse: [
|
|
597
|
+
{
|
|
598
|
+
matcher: 'Write|Edit|NotebookEdit',
|
|
599
|
+
hooks: [{ type: 'command', command: `node ${PRD_WRITE_GUARD_SCRIPT}` }],
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
matcher: 'Bash',
|
|
603
|
+
hooks: [{ type: 'command', command: `node ${DESTRUCTIVE_GIT_GUARD_SCRIPT}` }],
|
|
604
|
+
},
|
|
605
|
+
],
|
|
606
|
+
},
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
const result = await checkDelegationReadiness({ cwd, homeDir });
|
|
610
|
+
const check = result.checks.find((c) => c.id === 'inline-implementation-guard');
|
|
611
|
+
expect(check.ok).toBe(false);
|
|
612
|
+
expect(check.fixAction).toBe('install-inline-implementation-guard');
|
|
613
|
+
expect(check.detail).toBe(`no guard-inline-implementation PreToolUse hook in ${cwd}/.claude/settings.json — this is a nudge, not a hard gate`);
|
|
614
|
+
expect(result.ok).toBe(false);
|
|
615
|
+
// the other checks still pass independently
|
|
616
|
+
expect(result.checks.filter((c) => c.id !== 'inline-implementation-guard').every((c) => c.ok)).toBe(true);
|
|
617
|
+
}, 15_000);
|
|
618
|
+
|
|
619
|
+
test('inline-implementation-guard: fails with the "would silently guard nothing" detail when the hook names a script that does not exist', async () => {
|
|
620
|
+
const { homeDir, cwd } = await makeGreenFixtures();
|
|
621
|
+
await writeJson(path.join(cwd, '.claude', 'settings.json'), {
|
|
622
|
+
hooks: {
|
|
623
|
+
PreToolUse: [
|
|
624
|
+
{
|
|
625
|
+
matcher: 'Write|Edit|NotebookEdit',
|
|
626
|
+
hooks: [
|
|
627
|
+
{ type: 'command', command: `node ${PRD_WRITE_GUARD_SCRIPT}` },
|
|
628
|
+
{ type: 'command', command: 'node /does/not/exist/guard-inline-implementation.cjs' },
|
|
629
|
+
],
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
matcher: 'Bash',
|
|
633
|
+
hooks: [{ type: 'command', command: `node ${DESTRUCTIVE_GIT_GUARD_SCRIPT}` }],
|
|
634
|
+
},
|
|
635
|
+
],
|
|
636
|
+
},
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
const result = await checkDelegationReadiness({ cwd, homeDir });
|
|
640
|
+
const check = result.checks.find((c) => c.id === 'inline-implementation-guard');
|
|
641
|
+
expect(check.ok).toBe(false);
|
|
642
|
+
expect(check.detail).toBe(`guard-inline-implementation PreToolUse hook in ${cwd}/.claude/settings.json names a script that does not exist — it would silently guard nothing`);
|
|
643
|
+
}, 15_000);
|
|
644
|
+
|
|
645
|
+
test('inline-implementation-guard: ok when the hook resolves to an existing script file', async () => {
|
|
646
|
+
const { homeDir, cwd } = await makeGreenFixtures();
|
|
647
|
+
const result = await checkDelegationReadiness({ cwd, homeDir });
|
|
648
|
+
const check = result.checks.find((c) => c.id === 'inline-implementation-guard');
|
|
649
|
+
expect(check.ok).toBe(true);
|
|
650
|
+
expect(check.fixAction).toBeNull();
|
|
651
|
+
expect(check.detail).toBe(`guard-inline-implementation PreToolUse hook found in ${cwd}/.claude/settings.json, resolving to ${INLINE_IMPLEMENTATION_GUARD_SCRIPT}`);
|
|
652
|
+
}, 15_000);
|
|
653
|
+
|
|
654
|
+
// installInlineImplementationGuard shares installPrdWriteGuard's matcher
|
|
655
|
+
// (Write|Edit|NotebookEdit), so its interesting merge path is APPENDING into
|
|
656
|
+
// an existing matcher's hooks array — the branch destructive-git-guard's own
|
|
657
|
+
// installer (a different matcher, Bash) never exercises. This is the scenario
|
|
658
|
+
// a project that adopted only the PRD-write guard hits.
|
|
659
|
+
test('installInlineImplementationGuard: appends into the EXISTING Write|Edit|NotebookEdit matcher rather than pushing a new one, preserving unrelated keys byte-for-byte', async () => {
|
|
660
|
+
const { homeDir, cwd } = await makeGreenFixtures();
|
|
661
|
+
const settingsPath = path.join(cwd, '.claude', 'settings.json');
|
|
662
|
+
const seeded = {
|
|
663
|
+
someUnrelatedTopLevelKey: { nested: [1, 2, 3], note: 'do not touch' },
|
|
664
|
+
hooks: {
|
|
665
|
+
PreToolUse: [
|
|
666
|
+
{ matcher: 'Write|Edit|NotebookEdit', hooks: [{ type: 'command', command: `node ${PRD_WRITE_GUARD_SCRIPT}` }] },
|
|
667
|
+
{ matcher: 'Bash', hooks: [{ type: 'command', command: `node ${DESTRUCTIVE_GIT_GUARD_SCRIPT}` }] },
|
|
668
|
+
],
|
|
669
|
+
},
|
|
670
|
+
};
|
|
671
|
+
await writeJson(settingsPath, seeded);
|
|
672
|
+
|
|
673
|
+
const before = await checkDelegationReadiness({ cwd, homeDir });
|
|
674
|
+
expect(before.checks.find((c) => c.id === 'inline-implementation-guard')).toMatchObject({
|
|
675
|
+
ok: false,
|
|
676
|
+
fixAction: 'install-inline-implementation-guard',
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
const r = await installInlineImplementationGuard({ cwd });
|
|
680
|
+
expect(r).toMatchObject({ ok: true, action: 'installed', settingsPath, command: `node ${INLINE_IMPLEMENTATION_GUARD_SCRIPT}` });
|
|
681
|
+
|
|
682
|
+
const written = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
683
|
+
// Exactly ONE Write|Edit|NotebookEdit matcher entry, now carrying BOTH commands.
|
|
684
|
+
const writeMatchers = written.hooks.PreToolUse.filter((m) => m.matcher === 'Write|Edit|NotebookEdit');
|
|
685
|
+
expect(writeMatchers).toHaveLength(1);
|
|
686
|
+
expect(writeMatchers[0].hooks).toEqual([
|
|
687
|
+
{ type: 'command', command: `node ${PRD_WRITE_GUARD_SCRIPT}` },
|
|
688
|
+
{ type: 'command', command: `node ${INLINE_IMPLEMENTATION_GUARD_SCRIPT}` },
|
|
689
|
+
]);
|
|
690
|
+
// The unrelated Bash matcher and every unrelated top-level key survive untouched.
|
|
691
|
+
expect(written.hooks.PreToolUse).toContainEqual(
|
|
692
|
+
{ matcher: 'Bash', hooks: [{ type: 'command', command: `node ${DESTRUCTIVE_GIT_GUARD_SCRIPT}` }] },
|
|
693
|
+
);
|
|
694
|
+
expect(written.someUnrelatedTopLevelKey).toEqual(seeded.someUnrelatedTopLevelKey);
|
|
695
|
+
|
|
696
|
+
const after = await checkDelegationReadiness({ cwd, homeDir });
|
|
697
|
+
expect(after.checks.find((c) => c.id === 'inline-implementation-guard').ok).toBe(true);
|
|
698
|
+
expect(after.checks.find((c) => c.id === 'prd-write-guard').ok).toBe(true);
|
|
699
|
+
expect(after.checks.find((c) => c.id === 'destructive-git-guard').ok).toBe(true);
|
|
700
|
+
}, 15_000);
|
|
701
|
+
|
|
702
|
+
test('installInlineImplementationGuard: is idempotent — running it twice leaves exactly one entry, byte-identical file on the second press', async () => {
|
|
703
|
+
const { cwd } = await makeGreenFixtures();
|
|
704
|
+
await writeJson(path.join(cwd, '.claude', 'settings.json'), {
|
|
705
|
+
hooks: {
|
|
706
|
+
PreToolUse: [
|
|
707
|
+
{ matcher: 'Write|Edit|NotebookEdit', hooks: [{ type: 'command', command: `node ${PRD_WRITE_GUARD_SCRIPT}` }] },
|
|
708
|
+
],
|
|
709
|
+
},
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
const first = await installInlineImplementationGuard({ cwd });
|
|
713
|
+
expect(first.action).toBe('installed');
|
|
714
|
+
const snapshot = fs.readFileSync(path.join(cwd, '.claude', 'settings.json'), 'utf8');
|
|
715
|
+
|
|
716
|
+
const second = await installInlineImplementationGuard({ cwd });
|
|
717
|
+
expect(second.action).toBe('already-installed');
|
|
718
|
+
expect(fs.readFileSync(path.join(cwd, '.claude', 'settings.json'), 'utf8')).toBe(snapshot);
|
|
719
|
+
|
|
720
|
+
const written = JSON.parse(snapshot);
|
|
721
|
+
const writeMatchers = written.hooks.PreToolUse.filter((m) => m.matcher === 'Write|Edit|NotebookEdit');
|
|
722
|
+
expect(writeMatchers).toHaveLength(1);
|
|
723
|
+
expect(writeMatchers[0].hooks.filter((h) => h.command.includes('guard-inline-implementation'))).toHaveLength(1);
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
test('installInlineImplementationGuard: repairs an entry pointing at a now-nonexistent path in place rather than appending a duplicate', async () => {
|
|
727
|
+
const { cwd } = await makeGreenFixtures();
|
|
728
|
+
const settingsPath = path.join(cwd, '.claude', 'settings.json');
|
|
729
|
+
await writeJson(settingsPath, {
|
|
730
|
+
hooks: {
|
|
731
|
+
PreToolUse: [
|
|
732
|
+
{
|
|
733
|
+
matcher: 'Write|Edit|NotebookEdit',
|
|
734
|
+
hooks: [
|
|
735
|
+
{ type: 'command', command: `node ${PRD_WRITE_GUARD_SCRIPT}` },
|
|
736
|
+
{ type: 'command', command: 'node /does/not/exist/guard-inline-implementation.cjs' },
|
|
737
|
+
],
|
|
738
|
+
},
|
|
739
|
+
],
|
|
740
|
+
},
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
const r = await installInlineImplementationGuard({ cwd });
|
|
744
|
+
expect(r.action).toBe('repaired');
|
|
745
|
+
|
|
746
|
+
const written = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
747
|
+
const writeMatchers = written.hooks.PreToolUse.filter((m) => m.matcher === 'Write|Edit|NotebookEdit');
|
|
748
|
+
expect(writeMatchers).toHaveLength(1);
|
|
749
|
+
const inlineHooks = writeMatchers[0].hooks.filter((h) => h.command.includes('guard-inline-implementation'));
|
|
750
|
+
expect(inlineHooks).toHaveLength(1);
|
|
751
|
+
expect(inlineHooks[0].command).toBe(`node ${INLINE_IMPLEMENTATION_GUARD_SCRIPT}`);
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
test('installInlineImplementationGuard: refuses on unparseable settings rather than discarding them', async () => {
|
|
755
|
+
const { cwd } = await makeGreenFixtures();
|
|
756
|
+
await fsp.writeFile(path.join(cwd, '.claude', 'settings.json'), '{ not valid json', 'utf8');
|
|
757
|
+
|
|
758
|
+
const r = await installInlineImplementationGuard({ cwd });
|
|
759
|
+
expect(r.ok).toBe(false);
|
|
760
|
+
expect(r.action).toBe('error');
|
|
761
|
+
expect(fs.readFileSync(path.join(cwd, '.claude', 'settings.json'), 'utf8')).toBe('{ not valid json');
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
// ─────────────────────────────── missing-guard-script refusal (no lying success)
|
|
765
|
+
|
|
766
|
+
// Renames the real guard script aside for the duration of `fn`, then restores
|
|
767
|
+
// it — never deletes a real guard script from the working tree.
|
|
768
|
+
async function withGuardScriptMissing(scriptPath, fn) {
|
|
769
|
+
const movedTo = `${scriptPath}.moved-for-test`;
|
|
770
|
+
await fsp.rename(scriptPath, movedTo);
|
|
771
|
+
try {
|
|
772
|
+
await fn();
|
|
773
|
+
} finally {
|
|
774
|
+
await fsp.rename(movedTo, scriptPath);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
test('all three *_GUARD_SCRIPT constants name a script that exists in this repo', () => {
|
|
779
|
+
expect(fs.existsSync(PRD_WRITE_GUARD_SCRIPT)).toBe(true);
|
|
780
|
+
expect(fs.existsSync(DESTRUCTIVE_GIT_GUARD_SCRIPT)).toBe(true);
|
|
781
|
+
expect(fs.existsSync(INLINE_IMPLEMENTATION_GUARD_SCRIPT)).toBe(true);
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
test('installPrdWriteGuard: refuses when its guard script is missing, and leaves settings.json byte-identical', async () => {
|
|
785
|
+
const { cwd } = await makeGreenFixtures();
|
|
786
|
+
const settingsPath = path.join(cwd, '.claude', 'settings.json');
|
|
787
|
+
await fsp.rm(settingsPath);
|
|
788
|
+
|
|
789
|
+
await withGuardScriptMissing(PRD_WRITE_GUARD_SCRIPT, async () => {
|
|
790
|
+
const r = await installPrdWriteGuard({ cwd });
|
|
791
|
+
expect(r.ok).toBe(false);
|
|
792
|
+
expect(r.action).toBe('error');
|
|
793
|
+
expect(r.error).toContain(PRD_WRITE_GUARD_SCRIPT);
|
|
794
|
+
// Nothing was written — the file must still not exist.
|
|
795
|
+
expect(fs.existsSync(settingsPath)).toBe(false);
|
|
796
|
+
});
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
test('installPrdWriteGuard: refuses when its guard script is missing, leaving an EXISTING settings.json byte-identical', async () => {
|
|
800
|
+
const { cwd } = await makeGreenFixtures();
|
|
801
|
+
const settingsPath = path.join(cwd, '.claude', 'settings.json');
|
|
802
|
+
const before = fs.readFileSync(settingsPath);
|
|
803
|
+
|
|
804
|
+
await withGuardScriptMissing(PRD_WRITE_GUARD_SCRIPT, async () => {
|
|
805
|
+
const r = await installPrdWriteGuard({ cwd });
|
|
806
|
+
expect(r.ok).toBe(false);
|
|
807
|
+
expect(r.action).toBe('error');
|
|
808
|
+
expect(fs.readFileSync(settingsPath).equals(before)).toBe(true);
|
|
809
|
+
});
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
test('installDestructiveGitGuard: refuses when its guard script is missing, and leaves settings.json byte-identical', async () => {
|
|
813
|
+
const { cwd } = await makeGreenFixtures();
|
|
814
|
+
const settingsPath = path.join(cwd, '.claude', 'settings.json');
|
|
815
|
+
await fsp.rm(settingsPath);
|
|
816
|
+
|
|
817
|
+
await withGuardScriptMissing(DESTRUCTIVE_GIT_GUARD_SCRIPT, async () => {
|
|
818
|
+
const r = await installDestructiveGitGuard({ cwd });
|
|
819
|
+
expect(r.ok).toBe(false);
|
|
820
|
+
expect(r.action).toBe('error');
|
|
821
|
+
expect(r.error).toContain(DESTRUCTIVE_GIT_GUARD_SCRIPT);
|
|
822
|
+
expect(fs.existsSync(settingsPath)).toBe(false);
|
|
823
|
+
});
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
test('installDestructiveGitGuard: refuses when its guard script is missing, leaving an EXISTING settings.json byte-identical', async () => {
|
|
827
|
+
const { cwd } = await makeGreenFixtures();
|
|
828
|
+
const settingsPath = path.join(cwd, '.claude', 'settings.json');
|
|
829
|
+
const before = fs.readFileSync(settingsPath);
|
|
830
|
+
|
|
831
|
+
await withGuardScriptMissing(DESTRUCTIVE_GIT_GUARD_SCRIPT, async () => {
|
|
832
|
+
const r = await installDestructiveGitGuard({ cwd });
|
|
833
|
+
expect(r.ok).toBe(false);
|
|
834
|
+
expect(r.action).toBe('error');
|
|
835
|
+
expect(fs.readFileSync(settingsPath).equals(before)).toBe(true);
|
|
836
|
+
});
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
test('installInlineImplementationGuard: refuses when its guard script is missing, and leaves settings.json byte-identical', async () => {
|
|
840
|
+
const { cwd } = await makeGreenFixtures();
|
|
841
|
+
const settingsPath = path.join(cwd, '.claude', 'settings.json');
|
|
842
|
+
await fsp.rm(settingsPath);
|
|
843
|
+
|
|
844
|
+
await withGuardScriptMissing(INLINE_IMPLEMENTATION_GUARD_SCRIPT, async () => {
|
|
845
|
+
const r = await installInlineImplementationGuard({ cwd });
|
|
846
|
+
expect(r.ok).toBe(false);
|
|
847
|
+
expect(r.action).toBe('error');
|
|
848
|
+
expect(r.error).toContain(INLINE_IMPLEMENTATION_GUARD_SCRIPT);
|
|
849
|
+
expect(fs.existsSync(settingsPath)).toBe(false);
|
|
850
|
+
});
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
test('installInlineImplementationGuard: refuses when its guard script is missing, leaving an EXISTING settings.json byte-identical', async () => {
|
|
854
|
+
const { cwd } = await makeGreenFixtures();
|
|
855
|
+
const settingsPath = path.join(cwd, '.claude', 'settings.json');
|
|
856
|
+
const before = fs.readFileSync(settingsPath);
|
|
857
|
+
|
|
858
|
+
await withGuardScriptMissing(INLINE_IMPLEMENTATION_GUARD_SCRIPT, async () => {
|
|
859
|
+
const r = await installInlineImplementationGuard({ cwd });
|
|
860
|
+
expect(r.ok).toBe(false);
|
|
861
|
+
expect(r.action).toBe('error');
|
|
862
|
+
expect(fs.readFileSync(settingsPath).equals(before)).toBe(true);
|
|
863
|
+
});
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
test('installInlineImplementationGuard: uses the ABSOLUTE reference path, never a vendored copy', async () => {
|
|
867
|
+
const { cwd } = await makeGreenFixtures();
|
|
868
|
+
await writeJson(path.join(cwd, '.claude', 'settings.json'), {
|
|
869
|
+
hooks: { PreToolUse: [{ matcher: 'Write|Edit|NotebookEdit', hooks: [{ type: 'command', command: `node ${PRD_WRITE_GUARD_SCRIPT}` }] }] },
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
const r = await installInlineImplementationGuard({ cwd });
|
|
873
|
+
expect(path.isAbsolute(INLINE_IMPLEMENTATION_GUARD_SCRIPT)).toBe(true);
|
|
874
|
+
expect(r.command).toContain(INLINE_IMPLEMENTATION_GUARD_SCRIPT);
|
|
875
|
+
expect(INLINE_IMPLEMENTATION_GUARD_SCRIPT.startsWith(cwd + path.sep)).toBe(false);
|
|
876
|
+
expect(fs.existsSync(path.join(cwd, 'scripts'))).toBe(false);
|
|
877
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"config": {
|
|
3
|
+
"offsetMinutes": 15,
|
|
4
|
+
"defaultCwd": "/home/bilko/Projects/session-manager",
|
|
5
|
+
"firePolicy": "when-available",
|
|
6
|
+
"utilizationThreshold": 90,
|
|
7
|
+
"schemaVersion": 1,
|
|
8
|
+
"supervisor": {
|
|
9
|
+
"enabled": true,
|
|
10
|
+
"intervalMinutes": 15,
|
|
11
|
+
"maxConcurrentProbes": 2,
|
|
12
|
+
"probeStaleThresholdMinutes": 10
|
|
13
|
+
},
|
|
14
|
+
"concurrencyCap": 4,
|
|
15
|
+
"enabled": true,
|
|
16
|
+
"schedulerRunLogRetention": {
|
|
17
|
+
"enabled": true,
|
|
18
|
+
"policy": {
|
|
19
|
+
"maxAgeDays": 7,
|
|
20
|
+
"keepPerSlug": 1
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"scheduledFor": null,
|
|
25
|
+
"lastRunAt": "2026-09-11T10:00:14.441Z",
|
|
26
|
+
"paused": null,
|
|
27
|
+
"launchBlocks": {},
|
|
28
|
+
"launchMitigations": {}
|
|
29
|
+
}00:22.043Z",
|
|
30
|
+
"resumeAt": "2026-09-11T10:30:18.819Z"
|
|
31
|
+
},
|
|
32
|
+
"launchBlocks": {},
|
|
33
|
+
"launchMitigations": {}
|
|
34
|
+
}
|