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.
Files changed (92) hide show
  1. package/dist/assets/{AgentLibrary-B-OM-4gk.js → AgentLibrary-CCVIpoSz.js} +1 -1
  2. package/dist/assets/{DataModel-BC6ppcBr.js → DataModel-BltREYde.js} +1 -1
  3. package/dist/assets/{History-D3Qw2BWn.js → History-BZxFkOp6.js} +2 -2
  4. package/dist/assets/{Hooks-OxBQ0nIc.js → Hooks-Bznlfaaa.js} +1 -1
  5. package/dist/assets/{HostBilko-BNFC6bNZ.js → HostBilko-DUG5YHA_.js} +1 -1
  6. package/dist/assets/{Library-DAMyrUtY.js → Library-Bi2Fn3w9.js} +1 -1
  7. package/dist/assets/{ListDetail-BwywrpvE.js → ListDetail-4VBvXKrz.js} +1 -1
  8. package/dist/assets/{MarkdownEditor-HEwjN1bp.js → MarkdownEditor-D2ft_v5j.js} +1 -1
  9. package/dist/assets/{McpServers-D1VZF7Ix.js → McpServers-FDekKkyE.js} +1 -1
  10. package/dist/assets/{Memory-CgYJ_QqD.js → Memory-Dkl80uj_.js} +6 -6
  11. package/dist/assets/{Panel-dTNWC59Q.js → Panel-LurG5VfD.js} +1 -1
  12. package/dist/assets/{Permissions-qI_NKi1b.js → Permissions-D2wHBCFA.js} +1 -1
  13. package/dist/assets/{Plugins-DZ0_CHiF.js → Plugins-CTw_wwbI.js} +2 -2
  14. package/dist/assets/{ProvenanceBadge-B2i_uxCu.js → ProvenanceBadge-CW6HNUv6.js} +1 -1
  15. package/dist/assets/{SaveBar-4tNMCjUd.js → SaveBar-alDHG6cP.js} +1 -1
  16. package/dist/assets/{Scheduler-CUen18Gc.js → Scheduler-DxiPcaiW.js} +7 -7
  17. package/dist/assets/{ScopeSwitcher-Co3tkLTT.js → ScopeSwitcher-DzFXUKLZ.js} +1 -1
  18. package/dist/assets/Settings-B6H3v2am.js +3 -0
  19. package/dist/assets/{SkillReferenceGraph-BKcHtMPc.js → SkillReferenceGraph-B8EZalpN.js} +1 -1
  20. package/dist/assets/{Skills-DHb4INtF.js → Skills-DwuHuA08.js} +2 -2
  21. package/dist/assets/SystemPrompt-CMMqpGYn.js +1 -0
  22. package/dist/assets/{TagLibrary-xLP5dJvh.js → TagLibrary-C2N5C1n9.js} +1 -1
  23. package/dist/assets/{TiptapBody-T-ULTjK4.js → TiptapBody-btlID-dQ.js} +1 -1
  24. package/dist/assets/{Toggle-B-5M2hGe.js → Toggle-BGOn3DZj.js} +1 -1
  25. package/dist/assets/{index-CzAxC432.js → index-QLRf0epp.js} +316 -314
  26. package/dist/assets/{index-CYhdtisq.css → index-mnjNDpb1.css} +1 -1
  27. package/dist/assets/{settingsSchema-C_nsemcF.js → settingsSchema-DA3N2Up3.js} +1 -1
  28. package/dist/index.html +2 -2
  29. package/package.json +4 -1
  30. package/scripts/hooks/guard-destructive-git.cjs +514 -0
  31. package/scripts/hooks/guard-inline-implementation.cjs +219 -0
  32. package/scripts/hooks/guard-prd-writes.cjs +200 -0
  33. package/src/main/__tests__/epicMintTelemetryTap.test.cjs +64 -0
  34. package/src/main/__tests__/health-delegation-chain.test.cjs +2 -1
  35. package/src/main/__tests__/health-queue-dispatch.test.cjs +84 -0
  36. package/src/main/__tests__/health-usage-poller.test.cjs +97 -0
  37. package/src/main/__tests__/health-worktree-cap-blocked.test.cjs +65 -0
  38. package/src/main/__tests__/opsErrorLogTelemetryTap.test.cjs +143 -0
  39. package/src/main/__tests__/pollLoop-dispatch-on-failure.test.cjs +120 -0
  40. package/src/main/__tests__/promptSessionTranscript.test.cjs +0 -0
  41. package/src/main/__tests__/queue-starvation-dispatch-driver.test.cjs +143 -0
  42. package/src/main/__tests__/rateLimitPollerStreak.test.cjs +79 -0
  43. package/src/main/__tests__/scheduleJobTransitionsTelemetryTap.test.cjs +72 -0
  44. package/src/main/__tests__/scheduler-inplace-salvage.test.cjs +74 -0
  45. package/src/main/__tests__/scheduler-job-overrun.test.cjs +58 -0
  46. package/src/main/__tests__/scheduler-notify-originating-tab-transcript.test.cjs +1 -0
  47. package/src/main/__tests__/scheduler-periodic-reverify-guard.test.cjs +134 -2
  48. package/src/main/__tests__/scheduler-reap-dead-running-jobs.test.cjs +33 -0
  49. package/src/main/__tests__/scheduler-stuck-failed-escalation.test.cjs +136 -0
  50. package/src/main/__tests__/telemetryClient.test.cjs +66 -0
  51. package/src/main/__tests__/telemetryContract.test.cjs +883 -0
  52. package/src/main/crashDiagnostics.cjs +29 -1
  53. package/src/main/health.cjs +197 -3
  54. package/src/main/index.cjs +65 -6
  55. package/src/main/ipcSchemas.cjs +19 -2
  56. package/src/main/lib/__tests__/crashTelemetry.test.cjs +97 -0
  57. package/src/main/lib/__tests__/delegationReadiness.test.cjs +302 -4
  58. package/src/main/lib/__tests__/fixtures/scheduler-machine.json.corrupt-1789147548 +34 -0
  59. package/src/main/lib/__tests__/gitWorktree.test.cjs +413 -1
  60. package/src/main/lib/__tests__/jobWorktreeBootLive.test.cjs +71 -0
  61. package/src/main/lib/__tests__/queueStoreAtomicWrite.test.cjs +88 -0
  62. package/src/main/lib/__tests__/queueStoreMachineStateRecovery.test.cjs +123 -0
  63. package/src/main/lib/__tests__/reaperHelpers.test.cjs +58 -0
  64. package/src/main/lib/__tests__/telemetryBacklog.test.cjs +620 -0
  65. package/src/main/lib/__tests__/telemetryBoot.test.cjs +125 -0
  66. package/src/main/lib/__tests__/telemetryConsent.test.cjs +130 -0
  67. package/src/main/lib/__tests__/telemetryCounters.test.cjs +57 -0
  68. package/src/main/lib/__tests__/telemetryCountersMetadataColumn.test.cjs +89 -0
  69. package/src/main/lib/crashTelemetry.cjs +37 -0
  70. package/src/main/lib/delegationReadiness.cjs +119 -1
  71. package/src/main/lib/epicMint.cjs +2 -0
  72. package/src/main/lib/gitWorktree.cjs +427 -17
  73. package/src/main/lib/jobWorktree.cjs +2 -1
  74. package/src/main/lib/jobWorktreeBootLive.cjs +51 -0
  75. package/src/main/lib/jobWorktreeTerminalOrphanLive.cjs +68 -0
  76. package/src/main/lib/opsErrorLog.cjs +78 -25
  77. package/src/main/lib/queueStore.cjs +233 -20
  78. package/src/main/lib/reaperHelpers.cjs +23 -1
  79. package/src/main/lib/scheduleJobSchema.cjs +7 -0
  80. package/src/main/lib/scheduleJobTransitions.cjs +12 -0
  81. package/src/main/lib/telemetryBacklog.cjs +601 -0
  82. package/src/main/lib/telemetryBoot.cjs +71 -0
  83. package/src/main/lib/telemetryClient.cjs +31 -0
  84. package/src/main/lib/telemetryConsent.cjs +34 -0
  85. package/src/main/lib/telemetryCounters.cjs +45 -0
  86. package/src/main/promptSessionTranscript.cjs +0 -0
  87. package/src/main/pty.cjs +2 -0
  88. package/src/main/scheduler.cjs +481 -33
  89. package/src/preload/api.d.ts +84 -4
  90. package/src/preload/index.cjs +9 -0
  91. package/dist/assets/Settings-DXVgKLUx.js +0 -3
  92. 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: [{ type: 'command', command: `node ${PRD_WRITE_GUARD_SCRIPT}` }],
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 seven checks pass on a fully-configured project', async () => {
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(7);
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: [{ type: 'command', command: `node ${PRD_WRITE_GUARD_SCRIPT}` }],
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
+ }