claude-code-session-manager 0.76.0 → 0.77.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-CBx9l4zN.js → AgentLibrary-B2ie8bbw.js} +2 -2
- package/dist/assets/{DataModel-Bf0EIE_t.js → DataModel-BIJPYw32.js} +1 -1
- package/dist/assets/{History-CpdtWhC8.js → History-CeY6dk9S.js} +2 -2
- package/dist/assets/{Hooks-DyUbMDmg.js → Hooks-BFH2ocKg.js} +2 -2
- package/dist/assets/{HostBilko-By-wIpry.js → HostBilko-36gj9wLz.js} +1 -1
- package/dist/assets/{Library-CQmo4QVC.js → Library-C-hBct39.js} +1 -1
- package/dist/assets/{ListDetail-BQMd6NOm.js → ListDetail-CNq64VWV.js} +1 -1
- package/dist/assets/{MarkdownEditor-DEp43FXX.js → MarkdownEditor-Bh3qt5-1.js} +1 -1
- package/dist/assets/{McpServers-CLarzwqA.js → McpServers-DpGN0oyz.js} +1 -1
- package/dist/assets/{Memory-B0sCdIy1.js → Memory-D59hUjC4.js} +6 -6
- package/dist/assets/{Panel-BhWPVOCD.js → Panel-DCgbaoci.js} +1 -1
- package/dist/assets/{Permissions-Ddlq8T_O.js → Permissions-DAmQ0DYV.js} +2 -2
- package/dist/assets/{Plugins-D2oA_2Jl.js → Plugins-Dyfgn6Is.js} +2 -2
- package/dist/assets/{ProvenanceBadge-DgAgavUM.js → ProvenanceBadge-BiYhPO1U.js} +1 -1
- package/dist/assets/SaveBar-RV7B6sOh.js +1 -0
- package/dist/assets/Scheduler-BPaNqx1b.js +14 -0
- package/dist/assets/{ScopeSwitcher-C_zWEtIl.js → ScopeSwitcher-P4mdLGNU.js} +1 -1
- package/dist/assets/{Settings-2Vx3X5SI.js → Settings-BL4vf5aX.js} +1 -1
- package/dist/assets/{SkillReferenceGraph-BDEUjlTQ.js → SkillReferenceGraph-BRBDyi1_.js} +1 -1
- package/dist/assets/{Skills-Cmrz_LeN.js → Skills-BV08gDUH.js} +2 -2
- package/dist/assets/{SystemPrompt-DVA1eYDP.js → SystemPrompt-CLftSsDw.js} +1 -1
- package/dist/assets/TagLibrary-Bp8jGsd5.js +1 -0
- package/dist/assets/{TiptapBody-DmPc3amD.js → TiptapBody-jCpuB6E5.js} +1 -1
- package/dist/assets/{Toggle-zfd5LJkK.js → Toggle-D2paA1xf.js} +1 -1
- package/dist/assets/{index-B_4PNh9T.js → index-BDRSqBl3.js} +175 -175
- package/dist/assets/{index-DIjnPkRN.css → index-CYhdtisq.css} +1 -1
- package/dist/assets/{settingsSchema-B9es6fdA.js → settingsSchema-6IOLjZZN.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +8 -2
- package/plugins/session-manager-dev/skills/develop/standards.md +1 -1
- package/scripts/project-pages-logic/dist/logic.cjs +4709 -0
- package/scripts/render-project-pages/dist/renderer.cjs +18900 -0
- package/scripts/render-project-pages.cjs +70 -0
- package/scripts/scheduler-mcp-server.cjs +115 -1
- package/scripts/validate-project-pages-summary.cjs +62 -0
- package/src/main/__tests__/agentModelResolve.test.cjs +66 -0
- package/src/main/__tests__/health-delegation-chain.test.cjs +2 -1
- package/src/main/__tests__/prdAgentType.test.cjs +103 -0
- package/src/main/__tests__/prdCreate.test.cjs +138 -0
- package/src/main/__tests__/prdFrontmatterAgentType.test.cjs +117 -0
- package/src/main/__tests__/prdFrontmatterQuietMachine.test.cjs +108 -0
- package/src/main/__tests__/projectHomeAdminRoutes.test.cjs +485 -0
- package/src/main/__tests__/projectPages.test.cjs +73 -1
- package/src/main/__tests__/rcaReport.test.cjs +54 -0
- package/src/main/__tests__/runVerify.test.cjs +94 -0
- package/src/main/__tests__/scheduler-autofix-select.test.cjs +43 -0
- package/src/main/__tests__/scheduler-bash-timeout-env.test.cjs +103 -0
- package/src/main/__tests__/scheduler-effective-concurrency.test.cjs +10 -0
- package/src/main/__tests__/scheduler-foreign-wip-manifest.test.cjs +78 -0
- package/src/main/__tests__/scheduler-inplace-salvage.test.cjs +242 -0
- package/src/main/__tests__/scheduler-investigation-prompt.test.cjs +31 -0
- package/src/main/__tests__/scheduler-launch-failure.test.cjs +201 -0
- package/src/main/__tests__/scheduler-leftover-fields.test.cjs +52 -0
- package/src/main/__tests__/scheduler-looks-done.test.cjs +241 -0
- package/src/main/__tests__/scheduler-prd-persona-spawn.test.cjs +135 -0
- package/src/main/__tests__/scheduler-quiet-machine-lease.test.cjs +222 -0
- package/src/main/__tests__/scheduler-reap-dead-running-jobs.test.cjs +147 -0
- package/src/main/__tests__/scheduler-shared-tree-guard.test.cjs +212 -0
- package/src/main/__tests__/scheduler-worktree-cap-defer.test.cjs +194 -0
- package/src/main/__tests__/seedAgentPersonas.test.cjs +75 -14
- package/src/main/config.cjs +4 -1
- package/src/main/index.cjs +8 -1
- package/src/main/ipcSchemas.cjs +51 -0
- package/src/main/lib/__tests__/childWithLog.test.cjs +78 -0
- package/src/main/lib/__tests__/delegationReadiness.test.cjs +152 -2
- package/src/main/lib/__tests__/epicWorktreeMint.test.cjs +4 -2
- package/src/main/lib/__tests__/fixChainDepth.test.cjs +40 -0
- package/src/main/lib/__tests__/gitWorktree.test.cjs +277 -4
- package/src/main/lib/__tests__/gitWorktreeSalvageDelta.test.cjs +153 -0
- package/src/main/lib/__tests__/jobWorktree.test.cjs +5 -3
- package/src/main/lib/__tests__/landedSinceRun.test.cjs +73 -0
- package/src/main/lib/__tests__/launchFailure.test.cjs +220 -0
- package/src/main/lib/__tests__/mcpToolCatalog.test.cjs +1 -0
- package/src/main/lib/__tests__/opsOwnership.test.cjs +7 -0
- package/src/main/lib/__tests__/prdDeclaredPaths.test.cjs +82 -0
- package/src/main/lib/__tests__/queueHealth.test.cjs +58 -0
- package/src/main/lib/__tests__/quietMachineLease.test.cjs +39 -0
- package/src/main/lib/__tests__/reaperHelpers.test.cjs +22 -1
- package/src/main/lib/__tests__/schedulerBatchLaunchHold.test.cjs +125 -0
- package/src/main/lib/__tests__/schedulerBatchQuietMachine.test.cjs +109 -0
- package/src/main/lib/__tests__/schedulerMcpServerHeadlessRefusal.test.cjs +71 -0
- package/src/main/lib/__tests__/schedulerMcpServerProjectHome.test.cjs +350 -0
- package/src/main/lib/agentModelResolve.cjs +58 -0
- package/src/main/lib/childWithLog.cjs +40 -5
- package/src/main/lib/claudeBin.cjs +54 -1
- package/src/main/lib/definitionOfDone.cjs +3 -2
- package/src/main/lib/delegationReadiness.cjs +115 -9
- package/src/main/lib/epicWorktreeMint.cjs +5 -2
- package/src/main/lib/fixChainDepth.cjs +45 -0
- package/src/main/lib/gitWorktree.cjs +464 -19
- package/src/main/lib/jobWorktree.cjs +1 -0
- package/src/main/lib/landedSinceRun.cjs +55 -0
- package/src/main/lib/launchFailure.cjs +357 -0
- package/src/main/lib/mcpToolCatalog.cjs +87 -2
- package/src/main/lib/opsOwnership.cjs +12 -0
- package/src/main/lib/prdAgentType.cjs +84 -0
- package/src/main/lib/prdCreate.cjs +57 -1
- package/src/main/lib/prdDeclaredPaths.cjs +70 -0
- package/src/main/lib/prdFrontmatter.cjs +17 -3
- package/src/main/lib/projectHomeAdminRoutes.cjs +402 -0
- package/src/main/lib/projectPageSummarySchema.cjs +181 -0
- package/src/main/lib/queueHealth.cjs +38 -0
- package/src/main/lib/queueStore.cjs +9 -2
- package/src/main/lib/quietMachineLease.cjs +48 -0
- package/src/main/lib/rcaReport.cjs +53 -3
- package/src/main/lib/reaperHelpers.cjs +18 -1
- package/src/main/lib/scheduleJobSchema.cjs +31 -0
- package/src/main/lib/scheduleJobTransitions.cjs +6 -2
- package/src/main/lib/schedulerBatch.cjs +133 -29
- package/src/main/lib/schedulerConfig.cjs +19 -0
- package/src/main/projectPages.cjs +160 -2
- package/src/main/runVerify.cjs +50 -9
- package/src/main/scheduler/prdParser.cjs +18 -1
- package/src/main/scheduler.cjs +1371 -97
- package/src/main/seedAgentPersonas.cjs +62 -21
- package/src/main/templates/project-pages-catalog.json +741 -0
- package/src/main/templates/project-pages-pipeline.md +417 -0
- package/src/preload/api.d.ts +118 -2
- package/src/preload/index.cjs +7 -0
- package/src/seed/agents/project-home-builder.md +59 -0
- package/dist/assets/SaveBar-Qvc4Ek-H.js +0 -1
- package/dist/assets/Scheduler-BmYJvNzK.js +0 -14
- package/dist/assets/TagLibrary-DYJGAKZu.js +0 -1
|
@@ -28,6 +28,8 @@ const DEV_PLUGIN_ENABLED_KEY = 'session-manager-dev@session-manager';
|
|
|
28
28
|
const SCHEDULER_MCP_SERVER_SCRIPT = path.resolve(__dirname, '..', '..', '..', 'scripts', 'scheduler-mcp-server.cjs');
|
|
29
29
|
const PRD_WRITE_GUARD_SCRIPT = path.resolve(__dirname, '..', '..', '..', 'scripts', 'hooks', 'guard-prd-writes.cjs');
|
|
30
30
|
const PRD_WRITE_GUARD_MATCHER = 'Write|Edit|NotebookEdit';
|
|
31
|
+
const DESTRUCTIVE_GIT_GUARD_SCRIPT = path.resolve(__dirname, '..', '..', '..', 'scripts', 'hooks', 'guard-destructive-git.cjs');
|
|
32
|
+
const DESTRUCTIVE_GIT_GUARD_MATCHER = 'Bash';
|
|
31
33
|
const LIVE_PROBE_TIMEOUT_MS = 10_000;
|
|
32
34
|
const LIVE_PROBE_TTL_MS = 60_000;
|
|
33
35
|
const REQUIRED_LIVE_TOOLS = ['scheduler_create_prd', 'session_manager_help'];
|
|
@@ -341,13 +343,15 @@ function checkAgentPersonas({ cwd, homeDir }) {
|
|
|
341
343
|
}
|
|
342
344
|
|
|
343
345
|
/**
|
|
344
|
-
* Pull
|
|
346
|
+
* Pull a guard script's path out of a hook `command` string.
|
|
345
347
|
*
|
|
346
|
-
* Commands are `node <script>` (optionally quoted).
|
|
347
|
-
*
|
|
348
|
+
* Commands are `node <script>` (optionally quoted). `scriptBasename` names
|
|
349
|
+
* the exact guard file we're looking for — anything else is not that hook.
|
|
348
350
|
*/
|
|
349
|
-
function extractGuardScriptPath(command) {
|
|
350
|
-
const
|
|
351
|
+
function extractGuardScriptPath(command, scriptBasename) {
|
|
352
|
+
const esc = scriptBasename.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
353
|
+
const re = new RegExp(`(?:"([^"]*${esc})"|'([^']*${esc})'|(\\S*${esc}))`);
|
|
354
|
+
const match = String(command).match(re);
|
|
351
355
|
if (!match) return null;
|
|
352
356
|
return match[1] || match[2] || match[3] || null;
|
|
353
357
|
}
|
|
@@ -375,7 +379,7 @@ function checkPrdWriteGuard({ cwd }) {
|
|
|
375
379
|
for (const h of matcher.hooks) {
|
|
376
380
|
if (typeof h?.command !== 'string' || !h.command.includes('guard-prd-writes')) continue;
|
|
377
381
|
mentioned = true;
|
|
378
|
-
const raw = extractGuardScriptPath(h.command);
|
|
382
|
+
const raw = extractGuardScriptPath(h.command, 'guard-prd-writes.cjs');
|
|
379
383
|
if (!raw) continue;
|
|
380
384
|
const abs = path.isAbsolute(raw) ? raw : path.resolve(cwd, raw);
|
|
381
385
|
if (fs.existsSync(abs)) { resolvedScript = abs; break; }
|
|
@@ -483,7 +487,105 @@ async function installPrdWriteGuard({ cwd }) {
|
|
|
483
487
|
}
|
|
484
488
|
|
|
485
489
|
/**
|
|
486
|
-
*
|
|
490
|
+
* `ok` must mean "a hook that will actually run", not "the settings file
|
|
491
|
+
* mentions guard-destructive-git somewhere" — same rationale as
|
|
492
|
+
* checkPrdWriteGuard above (a PreToolUse command pointing at a script that
|
|
493
|
+
* doesn't exist in this project exits non-zero WITHOUT code 2, a
|
|
494
|
+
* non-blocking error that silently guards nothing).
|
|
495
|
+
*/
|
|
496
|
+
function checkDestructiveGitGuard({ cwd }) {
|
|
497
|
+
const settings = readJsonSafe(path.join(cwd, '.claude', 'settings.json'), null);
|
|
498
|
+
const preToolUse = Array.isArray(settings?.hooks?.PreToolUse) ? settings.hooks.PreToolUse : [];
|
|
499
|
+
|
|
500
|
+
let mentioned = false;
|
|
501
|
+
let resolvedScript = null;
|
|
502
|
+
for (const matcher of preToolUse) {
|
|
503
|
+
if (!Array.isArray(matcher?.hooks)) continue;
|
|
504
|
+
for (const h of matcher.hooks) {
|
|
505
|
+
if (typeof h?.command !== 'string' || !h.command.includes('guard-destructive-git')) continue;
|
|
506
|
+
mentioned = true;
|
|
507
|
+
const raw = extractGuardScriptPath(h.command, 'guard-destructive-git.cjs');
|
|
508
|
+
if (!raw) continue;
|
|
509
|
+
const abs = path.isAbsolute(raw) ? raw : path.resolve(cwd, raw);
|
|
510
|
+
if (fs.existsSync(abs)) { resolvedScript = abs; break; }
|
|
511
|
+
}
|
|
512
|
+
if (resolvedScript) break;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const ok = !!resolvedScript;
|
|
516
|
+
let detail;
|
|
517
|
+
if (ok) detail = `guard-destructive-git PreToolUse hook found in ${cwd}/.claude/settings.json, resolving to ${resolvedScript}`;
|
|
518
|
+
else if (mentioned) detail = `guard-destructive-git PreToolUse hook in ${cwd}/.claude/settings.json names a script that does not exist — it would silently guard nothing`;
|
|
519
|
+
else detail = `no guard-destructive-git PreToolUse hook in ${cwd}/.claude/settings.json`;
|
|
520
|
+
|
|
521
|
+
return {
|
|
522
|
+
id: 'destructive-git-guard',
|
|
523
|
+
label: 'Destructive-git guard hook installed',
|
|
524
|
+
ok,
|
|
525
|
+
detail,
|
|
526
|
+
fix: ok
|
|
527
|
+
? null
|
|
528
|
+
: `Add a PreToolUse hook to ${cwd}/.claude/settings.json matching Bash that runs node ${DESTRUCTIVE_GIT_GUARD_SCRIPT}`,
|
|
529
|
+
fixAction: ok ? null : 'install-destructive-git-guard',
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Install the destructive-git guard into `<cwd>/.claude/settings.json`.
|
|
535
|
+
* Same reference-not-vendor rationale, and the same merge-into-existing-
|
|
536
|
+
* hooks-block / repair-in-place / idempotent behaviour as installPrdWriteGuard
|
|
537
|
+
* above — see that function's header. The one difference is the matcher
|
|
538
|
+
* (`Bash`, not `Write|Edit|NotebookEdit`), so this lands as its own
|
|
539
|
+
* `PreToolUse` entry rather than sharing installPrdWriteGuard's.
|
|
540
|
+
*/
|
|
541
|
+
async function installDestructiveGitGuard({ cwd }) {
|
|
542
|
+
const settingsPath = path.join(cwd, '.claude', 'settings.json');
|
|
543
|
+
addAllowedRoot(cwd);
|
|
544
|
+
|
|
545
|
+
const existing = fs.existsSync(settingsPath) ? readJsonSafe(settingsPath, undefined) : {};
|
|
546
|
+
if (existing === undefined) {
|
|
547
|
+
return { ok: false, action: 'error', error: `${settingsPath} is not valid JSON — fix it by hand before installing the guard` };
|
|
548
|
+
}
|
|
549
|
+
const settings = (existing && typeof existing === 'object' && !Array.isArray(existing)) ? existing : {};
|
|
550
|
+
|
|
551
|
+
const command = `node ${DESTRUCTIVE_GIT_GUARD_SCRIPT}`;
|
|
552
|
+
const hooks = (settings.hooks && typeof settings.hooks === 'object') ? settings.hooks : {};
|
|
553
|
+
const preToolUse = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse.slice() : [];
|
|
554
|
+
|
|
555
|
+
if (checkDestructiveGitGuard({ cwd }).ok) {
|
|
556
|
+
return { ok: true, action: 'already-installed', settingsPath, command };
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
let repaired = false;
|
|
560
|
+
for (const matcher of preToolUse) {
|
|
561
|
+
if (!Array.isArray(matcher?.hooks)) continue;
|
|
562
|
+
for (const h of matcher.hooks) {
|
|
563
|
+
if (typeof h?.command === 'string' && h.command.includes('guard-destructive-git')) {
|
|
564
|
+
h.command = command;
|
|
565
|
+
h.type = 'command';
|
|
566
|
+
repaired = true;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (!repaired) {
|
|
572
|
+
const target = preToolUse.find((m) => m?.matcher === DESTRUCTIVE_GIT_GUARD_MATCHER);
|
|
573
|
+
if (target) {
|
|
574
|
+
target.hooks = Array.isArray(target.hooks) ? target.hooks : [];
|
|
575
|
+
target.hooks.push({ type: 'command', command });
|
|
576
|
+
} else {
|
|
577
|
+
preToolUse.push({ matcher: DESTRUCTIVE_GIT_GUARD_MATCHER, hooks: [{ type: 'command', command }] });
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
const next = { ...settings, hooks: { ...hooks, PreToolUse: preToolUse } };
|
|
582
|
+
await writeJson(settingsPath, next);
|
|
583
|
+
|
|
584
|
+
return { ok: true, action: repaired ? 'repaired' : 'installed', settingsPath, command };
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Runs all seven delegation-readiness checks for `cwd`. Every filesystem read
|
|
487
589
|
* is wrapped (readJsonSafe / try-catch) so a missing or unparseable file
|
|
488
590
|
* yields ok:false with a detail, never a thrown exception. Async because
|
|
489
591
|
* scheduler-mcp-live genuinely spawns a process and waits on it (bounded by
|
|
@@ -496,8 +598,8 @@ async function checkDelegationReadiness({ cwd, homeDir = os.homedir() }) {
|
|
|
496
598
|
const schedulerMcpLiveCheck = await checkSchedulerMcpLive({ cwd, homeDir, schedulerMcpCheck });
|
|
497
599
|
|
|
498
600
|
// `fixAction: null` / `warn: false` are the defaults — only a check with a
|
|
499
|
-
// sanctioned one-press installer overrides fixAction (today: prd-write-guard
|
|
500
|
-
// and only scheduler-mcp-project-duplicate ever sets warn.
|
|
601
|
+
// sanctioned one-press installer overrides fixAction (today: prd-write-guard,
|
|
602
|
+
// destructive-git-guard), and only scheduler-mcp-project-duplicate ever sets warn.
|
|
501
603
|
const checks = [
|
|
502
604
|
schedulerMcpCheck,
|
|
503
605
|
schedulerMcpLiveCheck,
|
|
@@ -505,6 +607,7 @@ async function checkDelegationReadiness({ cwd, homeDir = os.homedir() }) {
|
|
|
505
607
|
checkDevPlugin({ homeDir }),
|
|
506
608
|
checkAgentPersonas({ cwd, homeDir }),
|
|
507
609
|
checkPrdWriteGuard({ cwd }),
|
|
610
|
+
checkDestructiveGitGuard({ cwd }),
|
|
508
611
|
].map((c) => ({ fixAction: null, warn: false, ...c }));
|
|
509
612
|
|
|
510
613
|
return {
|
|
@@ -516,8 +619,11 @@ async function checkDelegationReadiness({ cwd, homeDir = os.homedir() }) {
|
|
|
516
619
|
module.exports = {
|
|
517
620
|
checkDelegationReadiness,
|
|
518
621
|
installPrdWriteGuard,
|
|
622
|
+
installDestructiveGitGuard,
|
|
519
623
|
probeSchedulerMcpLive,
|
|
520
624
|
clearLiveProbeCache,
|
|
521
625
|
PRD_WRITE_GUARD_SCRIPT,
|
|
522
626
|
PRD_WRITE_GUARD_MATCHER,
|
|
627
|
+
DESTRUCTIVE_GIT_GUARD_SCRIPT,
|
|
628
|
+
DESTRUCTIVE_GIT_GUARD_MATCHER,
|
|
523
629
|
};
|
|
@@ -13,8 +13,11 @@
|
|
|
13
13
|
* that can only run main-process side, so approveProposed fires this IPC
|
|
14
14
|
* call fire-and-forget and patches `worktree` onto the session once it
|
|
15
15
|
* resolves — the transition itself is never blocked or delayed by it, and a
|
|
16
|
-
* failure (not a repo /
|
|
17
|
-
* leaves `worktree` unset, exactly as if this call
|
|
16
|
+
* failure (not a repo / disabled / cap reached / carry-over of a dirty base
|
|
17
|
+
* tree's WIP failed) simply leaves `worktree` unset, exactly as if this call
|
|
18
|
+
* had never been made. A dirty base tree itself no longer disables
|
|
19
|
+
* isolation — its outstanding diff is carried into the fresh worktree
|
|
20
|
+
* instead (gitWorktree.cjs's createWorktree).
|
|
18
21
|
*
|
|
19
22
|
* Mirrors promptSessionsCreateEpic.cjs's shape: `validatePath(cwd)` closes
|
|
20
23
|
* the same "arbitrary renderer-supplied cwd" gap that handler documents,
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fixChainDepth.cjs — pure slug-inspection helpers for the fix-chain depth
|
|
3
|
+
* guard (PRD 1113). Generalizes scheduler.cjs's `isFixPlanSlug` (`^\d+-fix-`)
|
|
4
|
+
* into a counter: how many CONSECUTIVE '-fix-' segments immediately follow
|
|
5
|
+
* the leading `NN-` prefix. Only a run starting right at the prefix counts —
|
|
6
|
+
* a later segment that happens to read 'fix' (e.g.
|
|
7
|
+
* '13-fix-loop-extraction-and-round-trip', where 'loop' breaks the run right
|
|
8
|
+
* after the first 'fix') does not extend the count, and a segment that only
|
|
9
|
+
* contains 'fix' as a substring (e.g. '113-prefix-foo') never matches at all
|
|
10
|
+
* since segments are compared for exact equality after splitting on '-'.
|
|
11
|
+
*
|
|
12
|
+
* No filesystem access, no scheduler.cjs dependency — safe to require from
|
|
13
|
+
* lib/prdCreate.cjs without pulling in the scheduler module graph.
|
|
14
|
+
*/
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
function fixChainDepthOf(slug) {
|
|
18
|
+
const rest = String(slug).replace(/^\d+-/, '');
|
|
19
|
+
const segments = rest.split('-');
|
|
20
|
+
let depth = 0;
|
|
21
|
+
for (const seg of segments) {
|
|
22
|
+
if (seg !== 'fix') break;
|
|
23
|
+
depth += 1;
|
|
24
|
+
}
|
|
25
|
+
return depth;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Strips the leading consecutive '-fix-' chain a slug carries, returning the
|
|
30
|
+
* base slug the chain descends from — e.g. '113-fix-fix-foo' -> '113-foo'.
|
|
31
|
+
* Used only for error messages (naming what actually failed to close).
|
|
32
|
+
*/
|
|
33
|
+
function baseSlugOf(slug) {
|
|
34
|
+
const str = String(slug);
|
|
35
|
+
const prefixMatch = str.match(/^(\d+)-/);
|
|
36
|
+
const prefix = prefixMatch ? prefixMatch[1] : '';
|
|
37
|
+
const rest = str.replace(/^\d+-/, '');
|
|
38
|
+
const segments = rest.split('-');
|
|
39
|
+
let i = 0;
|
|
40
|
+
while (i < segments.length && segments[i] === 'fix') i += 1;
|
|
41
|
+
const remainder = segments.slice(i).join('-');
|
|
42
|
+
return prefix ? `${prefix}-${remainder}` : remainder;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = { fixChainDepthOf, baseSlugOf };
|