claude-launchpad 1.12.0 → 1.15.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 (61) hide show
  1. package/README.md +11 -3
  2. package/dist/{chunk-DTCCQWOU.js → chunk-7KB5YGQZ.js} +3 -3
  3. package/dist/{chunk-DTCCQWOU.js.map → chunk-7KB5YGQZ.js.map} +1 -1
  4. package/dist/chunk-DLDVXHI3.js +184 -0
  5. package/dist/chunk-DLDVXHI3.js.map +1 -0
  6. package/dist/chunk-HSGSTSVN.js +212 -0
  7. package/dist/chunk-HSGSTSVN.js.map +1 -0
  8. package/dist/{chunk-DXDOVWOA.js → chunk-IDH3M4EI.js} +20 -232
  9. package/dist/chunk-IDH3M4EI.js.map +1 -0
  10. package/dist/{chunk-YZ53W47Z.js → chunk-IIWOSSL4.js} +2 -1
  11. package/dist/chunk-IIWOSSL4.js.map +1 -0
  12. package/dist/{chunk-ZLI4LI33.js → chunk-IPGNXDJF.js} +2 -2
  13. package/dist/{chunk-PH5OWJ42.js → chunk-LCKV24Q3.js} +9 -5
  14. package/dist/{chunk-PH5OWJ42.js.map → chunk-LCKV24Q3.js.map} +1 -1
  15. package/dist/chunk-MX3T2VEX.js +225 -0
  16. package/dist/chunk-MX3T2VEX.js.map +1 -0
  17. package/dist/{chunk-F6SLV2FR.js → chunk-N2SCF7DB.js} +5 -6
  18. package/dist/chunk-N2SCF7DB.js.map +1 -0
  19. package/dist/{chunk-KPO4YURF.js → chunk-U2TFIUGE.js} +513 -89
  20. package/dist/chunk-U2TFIUGE.js.map +1 -0
  21. package/dist/cli.js +424 -130
  22. package/dist/cli.js.map +1 -1
  23. package/dist/commands/memory/server.js +48 -14
  24. package/dist/commands/memory/server.js.map +1 -1
  25. package/dist/{context-JALSYDYO.js → context-3GHG7KW6.js} +58 -55
  26. package/dist/context-3GHG7KW6.js.map +1 -0
  27. package/dist/doctor-EKGJ2YAO.js +108 -0
  28. package/dist/doctor-EKGJ2YAO.js.map +1 -0
  29. package/dist/{install-Q23F37GK.js → install-543ZKMXX.js} +12 -10
  30. package/dist/install-543ZKMXX.js.map +1 -0
  31. package/dist/{pull-FYKNNLYC.js → pull-ULSZEVCR.js} +17 -19
  32. package/dist/pull-ULSZEVCR.js.map +1 -0
  33. package/dist/{push-QSBK7BQU.js → push-JXCWX4NY.js} +13 -12
  34. package/dist/push-JXCWX4NY.js.map +1 -0
  35. package/dist/{require-deps-ZISUZLYD.js → require-deps-AEDO5YRM.js} +3 -3
  36. package/dist/{stats-SV53G6PQ.js → stats-63L7C24W.js} +9 -7
  37. package/dist/{stats-SV53G6PQ.js.map → stats-63L7C24W.js.map} +1 -1
  38. package/dist/{sync-2LDM44U4.js → sync-QTJ6BXQ4.js} +5 -5
  39. package/dist/{sync-clean-HQ523N7V.js → sync-clean-OJEK4XH4.js} +4 -5
  40. package/dist/sync-clean-OJEK4XH4.js.map +1 -0
  41. package/dist/{sync-status-V6WTI32X.js → sync-status-EAX527SC.js} +13 -12
  42. package/dist/sync-status-EAX527SC.js.map +1 -0
  43. package/dist/{tui-2UJ2OIHB.js → tui-3KITZCNS.js} +11 -7
  44. package/dist/{tui-2UJ2OIHB.js.map → tui-3KITZCNS.js.map} +1 -1
  45. package/package.json +27 -7
  46. package/scenarios/workflow/premature-victory.yaml +44 -0
  47. package/dist/chunk-DXDOVWOA.js.map +0 -1
  48. package/dist/chunk-F6SLV2FR.js.map +0 -1
  49. package/dist/chunk-KPO4YURF.js.map +0 -1
  50. package/dist/chunk-YZ53W47Z.js.map +0 -1
  51. package/dist/chunk-ZIG75MOB.js +0 -366
  52. package/dist/chunk-ZIG75MOB.js.map +0 -1
  53. package/dist/context-JALSYDYO.js.map +0 -1
  54. package/dist/install-Q23F37GK.js.map +0 -1
  55. package/dist/pull-FYKNNLYC.js.map +0 -1
  56. package/dist/push-QSBK7BQU.js.map +0 -1
  57. package/dist/sync-clean-HQ523N7V.js.map +0 -1
  58. package/dist/sync-status-V6WTI32X.js.map +0 -1
  59. /package/dist/{chunk-ZLI4LI33.js.map → chunk-IPGNXDJF.js.map} +0 -0
  60. /package/dist/{require-deps-ZISUZLYD.js.map → require-deps-AEDO5YRM.js.map} +0 -0
  61. /package/dist/{sync-2LDM44U4.js.map → sync-QTJ6BXQ4.js.map} +0 -0
@@ -46,7 +46,7 @@ import { homedir } from "os";
46
46
  // src/lib/sections.ts
47
47
  var SESSION_START_CONTENT = "- ALWAYS read @TASKS.md first \u2014 it tracks progress across sessions\n- Check the Session Log at the bottom of TASKS.md for where we left off\n- Update TASKS.md as you complete work";
48
48
  var BACKLOG_CONTENT = "- When a feature is discussed but deferred, add it to BACKLOG.md immediately\n- Never leave future ideas only in TASKS.md or conversation \u2014 they get lost\n- BACKLOG.md is the single source of truth for parked features\n- Every WP uses the 7-field template in BACKLOG.md \u2014 no freeform entries\n- Pull a WP into a sprint = **move**, not copy. A WP lives in exactly one file at a time";
49
- var STOP_AND_SWARM_CONTENT = 'Three failed iterations on the same problem = stop iterating alone.\n(An iteration = an attempted fix that did not change the failing symptom. Announce "Attempt N" when retrying so the count stays visible.)\nFirst, one systematic pass \u2014 it usually resolves the loop without the swarm:\nreproduce the failure, read the FULL error output, state one hypothesis about the root cause, and verify it BEFORE writing any fix.\nOnly if that pass fails, swarm: dispatch at least 3 parallel subagents via the Task tool \u2014 in a single message so they run concurrently \u2014 each investigating from a different angle:\n1. Root-cause debug agent\n2. Upstream library/docs research agent\n3. Alternative architecture agent\nHand each agent the exact repro command, the full error text, and the list of already-failed fixes \u2014 subagents start with empty context.\nWait for all agents to return, synthesize their findings, then act.\nFor re-planning after repeated failure, switch to plan mode instead of attempting again.';
49
+ var STOP_AND_SWARM_CONTENT = 'Three failed iterations on the same problem = stop iterating alone.\n(An iteration = an attempted fix that did not change the failing symptom. Announce "Attempt N" when retrying so the count stays visible.)\nFirst, one systematic pass \u2014 it usually resolves the loop without the swarm:\nreproduce the failure, read the FULL error output, state one hypothesis about the root cause, and verify it BEFORE writing any fix.\nOnly if that pass fails, swarm: dispatch at least 3 parallel subagents via the Task tool \u2014 in a single message so they run concurrently \u2014 each investigating from a different angle:\n1. Root-cause debug agent\n2. Upstream library/docs research agent\n3. Alternative architecture agent\nEvery agent brief has four parts \u2014 agents start with empty context: Mission (one sentence), Context (exact repro command, full error text, already-failed fixes), Scope fence (files/questions NOT to touch), Return format (findings as file:line + evidence).\nTreat agent output as testimony, not truth \u2014 spot-check load-bearing claims (open the cited file, re-run the cited command) before acting on them.\nWait for all agents to return, synthesize their findings, then act.\nFor re-planning after repeated failure, switch to plan mode instead of attempting again.';
50
50
  var STALE_SWARM_PHRASE = "spin up at least 3 parallel agents via the Agent tool";
51
51
  var SWARM_PHRASE_REPLACEMENT = "dispatch at least 3 parallel subagents via the Task tool (in a single message so they run concurrently)";
52
52
  var OFF_LIMITS_CONTENT = "- Never hardcode secrets \u2014 use environment variables\n- Never write to `.env` files\n- Never expose internal error details in API responses";
@@ -757,6 +757,7 @@ function isJqAvailable() {
757
757
  }
758
758
  return jqChecked;
759
759
  }
760
+ var FORCE_PUSH_ERE = "git +(-[cC][^ ]* +([^ ]+ +)?)*push([^|;&]*( -[a-zA-Z]*f[a-zA-Z]*| --force| --force-with-lease(=[^ ;|&]*)?)( |$|;|&)|[^|;&]* \\+[^ ;|&]+)";
760
761
 
761
762
  // src/lib/hook-scripts.ts
762
763
  var SPRINT_SIZE_CHECK = `#!/usr/bin/env bash
@@ -1372,6 +1373,43 @@ Output goes into the session's context. Always exit 0; SessionStart blocking is
1372
1373
  `;
1373
1374
  }
1374
1375
 
1376
+ // src/commands/init/generators/verification-rule.ts
1377
+ var VERIFICATION_RULE_VERSION = 1;
1378
+ function generateVerificationRule() {
1379
+ return `# Verification Rules
1380
+
1381
+ <!-- lp-verification-version: ${VERIFICATION_RULE_VERSION} -->
1382
+
1383
+ These rules govern every claim of "done", "fixed", "passing", or "works". A claim without evidence is a defect, no matter how good the code is.
1384
+
1385
+ ## Evidence before assertion
1386
+
1387
+ - Never claim done/fixed/passing without having run the thing this session and quoting the output. The claim and its evidence travel together: "Done \u2014 14/14 tests pass (output below)", never a bare "done".
1388
+ - Verify the behavior, not the build. Compiling is not working; typechecking is not working. Exercise the changed flow end-to-end: run the command, hit the endpoint, click the path.
1389
+ - For bug fixes: reproduce the failure FIRST and capture the exact error. Then fix, then re-run the original reproduction AND the surrounding tests. If you cannot explain the root cause, you have hidden the bug, not fixed it.
1390
+ - Test the failure case, not just the happy path: feed the change the input that used to break it and confirm it now lands safely.
1391
+
1392
+ ## Label your claims
1393
+
1394
+ Sort every load-bearing statement into one of three buckets, and say which out loud:
1395
+
1396
+ - **verified** \u2014 you read it or ran it this session
1397
+ - **inferred** \u2014 follows logically from verified facts; state the chain
1398
+ - **assumed** \u2014 plausible from prior knowledge; check it before building on it
1399
+
1400
+ Never let an assumption silently graduate into a fact. APIs, config keys, CLI flags, and package names cited from memory are the #1 hallucination vector \u2014 open the real source or current docs before citing them.
1401
+
1402
+ ## When you can't verify
1403
+
1404
+ - If a step cannot be run (missing env, no credentials, no test data), the result is **done-with-gaps** \u2014 name each gap explicitly. Never round done-with-gaps up to done.
1405
+ - Report failure plainly. Failing tests, skipped steps, and partial work get named with the output shown. "2 failures remain, here's the state" preserves trust; a false "all green" destroys it.
1406
+
1407
+ ## End-of-turn check
1408
+
1409
+ Before ending a turn, re-read your last paragraph. If it is a promise ("I'll\u2026", "next I would\u2026"), a plan, or a question you could answer yourself \u2014 do that work now, then end. A turn ends on delivered results, not intentions.
1410
+ `;
1411
+ }
1412
+
1375
1413
  // src/commands/init/generators/agent-reviewer.ts
1376
1414
  var REVIEWER_AGENT_VERSION = 1;
1377
1415
  function generateReviewerAgent() {
@@ -1425,7 +1463,9 @@ async function createWorkflowRule(root) {
1425
1463
  if (await fileExists(workflowPath)) return false;
1426
1464
  await mkdir2(rulesDir, { recursive: true });
1427
1465
  await writeFile3(workflowPath, generateWorkflowRule());
1428
- log.success("Created .claude/rules/workflow.md (path-scoped BACKLOG/TASKS workflow rules)");
1466
+ log.success(
1467
+ "Created .claude/rules/workflow.md (path-scoped BACKLOG/TASKS workflow rules)"
1468
+ );
1429
1469
  return true;
1430
1470
  }
1431
1471
  async function createReviewerAgent(root) {
@@ -1434,7 +1474,9 @@ async function createReviewerAgent(root) {
1434
1474
  if (await fileExists(agentPath)) return false;
1435
1475
  await mkdir2(agentsDir, { recursive: true });
1436
1476
  await writeFile3(agentPath, generateReviewerAgent());
1437
- log.success("Created .claude/agents/code-reviewer.md (fresh-context independent reviewer)");
1477
+ log.success(
1478
+ "Created .claude/agents/code-reviewer.md (fresh-context independent reviewer)"
1479
+ );
1438
1480
  return true;
1439
1481
  }
1440
1482
  async function updateWorkflowRule(root) {
@@ -1450,8 +1492,13 @@ async function fixStaleSwarmPhrase(root) {
1450
1492
  const claudeMdPath = join4(root, "CLAUDE.md");
1451
1493
  const content = await readFile3(claudeMdPath, "utf-8").catch(() => null);
1452
1494
  if (content === null || !content.includes(STALE_SWARM_PHRASE)) return false;
1453
- await writeFile3(claudeMdPath, content.replaceAll(STALE_SWARM_PHRASE, SWARM_PHRASE_REPLACEMENT));
1454
- log.success("Modernized Stop-and-Swarm wording (Agent tool \u2192 Task tool subagents)");
1495
+ await writeFile3(
1496
+ claudeMdPath,
1497
+ content.replaceAll(STALE_SWARM_PHRASE, SWARM_PHRASE_REPLACEMENT)
1498
+ );
1499
+ log.success(
1500
+ "Modernized Stop-and-Swarm wording (Agent tool \u2192 Task tool subagents)"
1501
+ );
1455
1502
  return true;
1456
1503
  }
1457
1504
  async function createHooksRule(root) {
@@ -1460,7 +1507,29 @@ async function createHooksRule(root) {
1460
1507
  if (await fileExists(hooksPath)) return false;
1461
1508
  await mkdir2(rulesDir, { recursive: true });
1462
1509
  await writeFile3(hooksPath, generateHooksRule());
1463
- log.success("Created .claude/rules/hooks.md (path-scoped hook authoring rules)");
1510
+ log.success(
1511
+ "Created .claude/rules/hooks.md (path-scoped hook authoring rules)"
1512
+ );
1513
+ return true;
1514
+ }
1515
+ async function createVerificationRule(root) {
1516
+ const rulesDir = join4(root, ".claude", "rules");
1517
+ const verificationPath = join4(rulesDir, "verification.md");
1518
+ if (await fileExists(verificationPath)) return false;
1519
+ await mkdir2(rulesDir, { recursive: true });
1520
+ await writeFile3(verificationPath, generateVerificationRule());
1521
+ log.success(
1522
+ "Created .claude/rules/verification.md (evidence-before-assertion discipline)"
1523
+ );
1524
+ return true;
1525
+ }
1526
+ async function updateVerificationRule(root) {
1527
+ const verificationPath = join4(root, ".claude", "rules", "verification.md");
1528
+ const content = await readFile3(verificationPath, "utf-8").catch(() => null);
1529
+ if (content === null) return false;
1530
+ if (!/<!-- lp-verification-version: \d+ -->/.test(content)) return false;
1531
+ await writeFile3(verificationPath, generateVerificationRule());
1532
+ log.success("Updated .claude/rules/verification.md to the latest version");
1464
1533
  return true;
1465
1534
  }
1466
1535
  function isMemoryHeading(line) {
@@ -1477,7 +1546,11 @@ function findMemoryBlocks(lines) {
1477
1546
  break;
1478
1547
  }
1479
1548
  }
1480
- blocks.push({ startIdx: i, endIdx: end, tagged: lines[i].includes("(agentic-memory)") });
1549
+ blocks.push({
1550
+ startIdx: i,
1551
+ endIdx: end,
1552
+ tagged: lines[i].includes("(agentic-memory)")
1553
+ });
1481
1554
  i = end - 1;
1482
1555
  }
1483
1556
  return blocks;
@@ -1512,7 +1585,9 @@ async function collapseMemoryHeadings(root) {
1512
1585
  (line) => /^## Memory\s*$/.test(line) ? "## Memory (agentic-memory)" : line
1513
1586
  );
1514
1587
  await writeFile3(claudeMdPath, canonical.join("\n"));
1515
- log.success(`Collapsed ${blocks.length - 1} duplicate ## Memory section(s) in CLAUDE.md`);
1588
+ log.success(
1589
+ `Collapsed ${blocks.length - 1} duplicate ## Memory section(s) in CLAUDE.md`
1590
+ );
1516
1591
  return true;
1517
1592
  }
1518
1593
 
@@ -1553,7 +1628,7 @@ async function addForcePushProtection(root) {
1553
1628
  matcher: "Bash",
1554
1629
  hooks: [{
1555
1630
  type: "command",
1556
- command: `cmd=${jqField("command")}; echo "$cmd" | grep -qE 'push.*--force|push.*-f' && { echo 'WARNING: Force push detected \u2014 this can destroy remote history' >&2; exit 2; }; exit 0`
1631
+ command: `cmd=${jqField("command")}; echo "$cmd" | grep -qE "${FORCE_PUSH_ERE}" && { echo 'WARNING: Force push detected \u2014 this can destroy remote history' >&2; exit 2; }; exit 0`
1557
1632
  }]
1558
1633
  }, "Added force-push protection hook (PreToolUse \u2192 Bash)");
1559
1634
  }
@@ -1611,6 +1686,42 @@ async function addSessionStartHook(root) {
1611
1686
  hooks: [{ type: "command", command: "cat TASKS.md 2>/dev/null; exit 0" }]
1612
1687
  }, "Added SessionStart hook (injects TASKS.md at startup/resume/compact/clear)");
1613
1688
  }
1689
+ async function upgradeForcePushPattern(root) {
1690
+ const stale = "push.*--force|push.*-f";
1691
+ let any = false;
1692
+ for (const [read, write, label] of [
1693
+ [readSettingsJson, writeSettingsJson, "settings.json"],
1694
+ [readSettingsLocalJson, writeSettingsLocalJson, "settings.local.json"]
1695
+ ]) {
1696
+ const settings = await read(root);
1697
+ if (settings === null) continue;
1698
+ const raw = JSON.stringify(settings);
1699
+ if (!raw.includes(stale.replace(/\\/g, "\\\\"))) {
1700
+ if (!raw.includes(stale)) continue;
1701
+ }
1702
+ const hooks = settings.hooks;
1703
+ if (!hooks) continue;
1704
+ let changed = false;
1705
+ for (const groups of Object.values(hooks)) {
1706
+ for (const group of groups) {
1707
+ const inner = group.hooks;
1708
+ if (!inner) continue;
1709
+ for (const h of inner) {
1710
+ const cmd = typeof h.command === "string" ? h.command : "";
1711
+ if (cmd.includes(stale)) {
1712
+ h.command = cmd.replace(stale, FORCE_PUSH_ERE);
1713
+ changed = true;
1714
+ }
1715
+ }
1716
+ }
1717
+ }
1718
+ if (!changed) continue;
1719
+ await write(root, settings);
1720
+ log.success(`Upgraded force-push guard pattern in ${label}`);
1721
+ any = true;
1722
+ }
1723
+ return any;
1724
+ }
1614
1725
 
1615
1726
  // src/commands/doctor/fixer-hook-input.ts
1616
1727
  import { readFile as readFile4 } from "fs/promises";
@@ -1621,10 +1732,10 @@ function rewriteEnvVarHookCommand(cmd) {
1621
1732
  return `fp=${jqField("file_path")}; echo "$fp" | grep -qE '\\.(env|env\\..*)$' && ! echo "$fp" | grep -q '.env.example' && { echo 'BLOCKED: .env files contain secrets' >&2; exit 2; }; exit 0`;
1622
1733
  }
1623
1734
  if (cmd.includes("BLOCKED: Destructive command detected")) {
1624
- return `cmd=${jqField("command")}; echo "$cmd" | grep -qE 'rm\\s+-rf\\s+/|DROP\\s+TABLE|DROP\\s+DATABASE|push.*--force|push.*-f' && { echo 'BLOCKED: Destructive command detected' >&2; exit 2; }; exit 0`;
1735
+ return `cmd=${jqField("command")}; echo "$cmd" | grep -qE 'rm\\s+-rf\\s+/|DROP\\s+TABLE|DROP\\s+DATABASE|${FORCE_PUSH_ERE}' && { echo 'BLOCKED: Destructive command detected' >&2; exit 2; }; exit 0`;
1625
1736
  }
1626
1737
  if (cmd.includes("WARNING: Force push detected") || cmd.includes("Force push detected")) {
1627
- return `cmd=${jqField("command")}; echo "$cmd" | grep -qE 'push.*--force|push.*-f' && { echo 'WARNING: Force push detected \u2014 this can destroy remote history' >&2; exit 2; }; exit 0`;
1738
+ return `cmd=${jqField("command")}; echo "$cmd" | grep -qE '${FORCE_PUSH_ERE}' && { echo 'WARNING: Force push detected \u2014 this can destroy remote history' >&2; exit 2; }; exit 0`;
1628
1739
  }
1629
1740
  if (cmd.includes("Sprint complete") && cmd.includes("TASKS.md")) {
1630
1741
  return SPRINT_COMPLETE_NUDGE;
@@ -1680,6 +1791,15 @@ async function rewriteEnvVarHooks(root) {
1680
1791
  didFix = true;
1681
1792
  }
1682
1793
  }
1794
+ const localSettings = await readSettingsLocalJson(root);
1795
+ if (localSettings !== null) {
1796
+ const outcome = rewriteSettingsHooks(localSettings);
1797
+ if (outcome.changed) {
1798
+ await writeSettingsLocalJson(root, outcome.settings);
1799
+ log.success("Rewrote inert $TOOL_INPUT_* hooks in settings.local.json to canonical jq+stdin form");
1800
+ didFix = true;
1801
+ }
1802
+ }
1683
1803
  const workflowCheckPath = join5(root, ".claude", "hooks", "workflow-check.sh");
1684
1804
  if (await rewriteWrapperScript(workflowCheckPath, () => writeWorkflowCheckScript(root))) {
1685
1805
  log.success("Rewrote .claude/hooks/workflow-check.sh to canonical jq+stdin form");
@@ -1753,13 +1873,13 @@ async function addSessionStartPullHook(root, placement) {
1753
1873
  const target = placement === "local" ? "settings.local.json" : "settings.json";
1754
1874
  return addPlacementHook(root, placement, "SessionStart", "memory pull", {
1755
1875
  matcher: "startup",
1756
- hooks: [{ type: "command", command: "claude-launchpad memory pull -y 2>/dev/null; exit 0" }]
1876
+ hooks: [{ type: "command", command: "npx claude-launchpad memory pull -y 2>/dev/null; exit 0" }]
1757
1877
  }, true, `Added SessionStart hook for memory sync to ${target}`);
1758
1878
  }
1759
1879
  async function addSessionEndPushHook(root, placement) {
1760
1880
  const target = placement === "local" ? "settings.local.json" : "settings.json";
1761
1881
  return addPlacementHook(root, placement, "SessionEnd", "memory push", {
1762
- hooks: [{ type: "command", command: "nohup claude-launchpad memory push -y </dev/null >/dev/null 2>&1 & exit 0" }]
1882
+ hooks: [{ type: "command", command: "nohup npx claude-launchpad memory push -y </dev/null >/dev/null 2>&1 & exit 0" }]
1763
1883
  }, false, `Added SessionEnd hook for memory sync to ${target}`);
1764
1884
  }
1765
1885
  async function upgradeStaleSessionEndPushHook(root) {
@@ -1780,7 +1900,7 @@ async function upgradeStaleSessionEndPushHook(root) {
1780
1900
  const cmd = typeof h.command === "string" ? h.command : "";
1781
1901
  if (!cmd.includes("memory push") || cmd.includes("nohup")) return h;
1782
1902
  changed = true;
1783
- return { ...h, command: "nohup claude-launchpad memory push -y </dev/null >/dev/null 2>&1 & exit 0" };
1903
+ return { ...h, command: "nohup npx claude-launchpad memory push -y </dev/null >/dev/null 2>&1 & exit 0" };
1784
1904
  });
1785
1905
  return { ...group, hooks: rewritten };
1786
1906
  });
@@ -1868,6 +1988,38 @@ async function addAllowedMcpServers(root, placement) {
1868
1988
  log.success(`Added allowedMcpServers from configured servers to ${target}`);
1869
1989
  return true;
1870
1990
  }
1991
+ async function upgradeBareMemoryHooks(root) {
1992
+ let any = false;
1993
+ for (const placement of ["shared", "local"]) {
1994
+ const read = placement === "local" ? readSettingsLocalJson : readSettingsJson;
1995
+ const write = placement === "local" ? writeSettingsLocalJson : writeSettingsJson;
1996
+ const settings = await read(root);
1997
+ if (settings === null) continue;
1998
+ const hooks = settings.hooks;
1999
+ if (!hooks) continue;
2000
+ let changed = false;
2001
+ const upgradedHooks = {};
2002
+ for (const [event, groups] of Object.entries(hooks)) {
2003
+ upgradedHooks[event] = groups.map((group) => {
2004
+ const inner = group.hooks;
2005
+ if (!inner) return group;
2006
+ const rewritten = inner.map((h) => {
2007
+ const cmd = typeof h.command === "string" ? h.command : "";
2008
+ if (!cmd.includes("claude-launchpad memory") || cmd.includes("npx claude-launchpad")) return h;
2009
+ changed = true;
2010
+ return { ...h, command: cmd.replace("claude-launchpad memory", "npx claude-launchpad memory") };
2011
+ });
2012
+ return { ...group, hooks: rewritten };
2013
+ });
2014
+ }
2015
+ if (!changed) continue;
2016
+ await write(root, { ...settings, hooks: upgradedHooks });
2017
+ const target = placement === "local" ? "settings.local.json" : "settings.json";
2018
+ log.success(`Rewrote bare memory hook commands to npx form in ${target}`);
2019
+ any = true;
2020
+ }
2021
+ return any;
2022
+ }
1871
2023
 
1872
2024
  // src/commands/doctor/fixer.ts
1873
2025
  async function applyFixes(issues, projectRoot) {
@@ -1887,74 +2039,316 @@ async function applyFixes(issues, projectRoot) {
1887
2039
  return { fixed, skipped };
1888
2040
  }
1889
2041
  var FIX_TABLE = [
1890
- { analyzer: "Hooks", match: "$TOOL_INPUT_* env var", fix: (root) => rewriteEnvVarHooks(root) },
1891
- { analyzer: "Hooks", match: "No hooks configured", fix: async (root, detected) => {
1892
- const a = await addEnvProtectionHook(root);
1893
- const b = await addAutoFormatHook(root, detected);
1894
- const c = await addForcePushProtection(root);
1895
- const d = await addSessionStartHook(root);
1896
- return a || b || c || d;
1897
- } },
1898
- { analyzer: "Hooks", match: ".env file protection", fix: (root) => addEnvProtectionHook(root) },
1899
- { analyzer: "Hooks", match: "auto-format", fix: (root, detected) => addAutoFormatHook(root, detected) },
1900
- { analyzer: "Hooks", match: "No PreToolUse", fix: (root) => addEnvProtectionHook(root) },
1901
- { analyzer: "Quality", match: "Architecture", fix: (root) => addClaudeMdSection(root, "## Architecture", wrapStub("<!-- TODO: Describe your codebase structure. Run `/lp-enhance` to auto-fill this. -->")) },
1902
- { analyzer: "Quality", match: "Off-Limits", fix: (root) => addClaudeMdSection(root, "## Off-Limits", wrapStub(OFF_LIMITS_CONTENT)) },
1903
- { analyzer: "Quality", match: "Commands", fix: (root) => addClaudeMdSection(root, "## Commands", wrapStub("<!-- TODO: Add your dev/build/test commands -->")) },
1904
- { analyzer: "Quality", match: "Stack", fix: (root, detected) => {
1905
- if (detected.language) {
1906
- const content = `- **Language**: ${detected.language}${detected.framework ? `
2042
+ {
2043
+ analyzer: "Hooks",
2044
+ match: "$TOOL_INPUT_* env var",
2045
+ fix: (root) => rewriteEnvVarHooks(root)
2046
+ },
2047
+ {
2048
+ analyzer: "Hooks",
2049
+ match: "No hooks configured",
2050
+ fix: async (root, detected) => {
2051
+ const a = await addEnvProtectionHook(root);
2052
+ const b = await addAutoFormatHook(root, detected);
2053
+ const c = await addForcePushProtection(root);
2054
+ const d = await addSessionStartHook(root);
2055
+ return a || b || c || d;
2056
+ }
2057
+ },
2058
+ {
2059
+ analyzer: "Hooks",
2060
+ match: "outdated pattern",
2061
+ fix: (root) => upgradeForcePushPattern(root)
2062
+ },
2063
+ {
2064
+ analyzer: "Hooks",
2065
+ match: ".env file protection",
2066
+ fix: (root) => addEnvProtectionHook(root)
2067
+ },
2068
+ {
2069
+ analyzer: "Hooks",
2070
+ match: "auto-format",
2071
+ fix: (root, detected) => addAutoFormatHook(root, detected)
2072
+ },
2073
+ {
2074
+ analyzer: "Hooks",
2075
+ match: "No PreToolUse",
2076
+ fix: (root) => addEnvProtectionHook(root)
2077
+ },
2078
+ {
2079
+ analyzer: "Quality",
2080
+ match: "Architecture",
2081
+ fix: (root) => addClaudeMdSection(
2082
+ root,
2083
+ "## Architecture",
2084
+ wrapStub(
2085
+ "<!-- TODO: Describe your codebase structure. Run `/lp-enhance` to auto-fill this. -->"
2086
+ )
2087
+ )
2088
+ },
2089
+ {
2090
+ analyzer: "Quality",
2091
+ match: "Off-Limits",
2092
+ fix: (root) => addClaudeMdSection(root, "## Off-Limits", wrapStub(OFF_LIMITS_CONTENT))
2093
+ },
2094
+ {
2095
+ analyzer: "Quality",
2096
+ match: "Commands",
2097
+ fix: (root) => addClaudeMdSection(
2098
+ root,
2099
+ "## Commands",
2100
+ wrapStub("<!-- TODO: Add your dev/build/test commands -->")
2101
+ )
2102
+ },
2103
+ {
2104
+ analyzer: "Quality",
2105
+ match: "Stack",
2106
+ fix: (root, detected) => {
2107
+ if (detected.language) {
2108
+ const content = `- **Language**: ${detected.language}${detected.framework ? `
1907
2109
  - **Framework**: ${detected.framework}` : ""}${detected.packageManager ? `
1908
2110
  - **Package Manager**: ${detected.packageManager}` : ""}`;
1909
- return addClaudeMdSection(root, "## Stack", content);
2111
+ return addClaudeMdSection(root, "## Stack", content);
2112
+ }
2113
+ return addClaudeMdSection(
2114
+ root,
2115
+ "## Stack",
2116
+ wrapStub("<!-- TODO: Define your tech stack -->")
2117
+ );
2118
+ }
2119
+ },
2120
+ {
2121
+ analyzer: "Quality",
2122
+ match: "Session Start",
2123
+ fix: (root) => addClaudeMdSection(
2124
+ root,
2125
+ "## Session Start",
2126
+ wrapStub(SESSION_START_CONTENT)
2127
+ )
2128
+ },
2129
+ {
2130
+ analyzer: "Quality",
2131
+ match: "Backlog",
2132
+ fix: (root) => addClaudeMdSection(root, "## Backlog", wrapStub(BACKLOG_CONTENT))
2133
+ },
2134
+ {
2135
+ analyzer: "Quality",
2136
+ match: "Stop-and-Swarm section is outdated",
2137
+ fix: (root) => fixStaleSwarmPhrase(root)
2138
+ },
2139
+ {
2140
+ analyzer: "Quality",
2141
+ match: "Stop-and-Swarm",
2142
+ fix: (root) => addClaudeMdSection(
2143
+ root,
2144
+ "## Stop-and-Swarm",
2145
+ wrapStub(STOP_AND_SWARM_CONTENT)
2146
+ )
2147
+ },
2148
+ {
2149
+ analyzer: "Rules",
2150
+ match: "workflow.md rule is outdated",
2151
+ fix: (root) => updateWorkflowRule(root)
2152
+ },
2153
+ {
2154
+ analyzer: "Rules",
2155
+ match: "verification.md rule is outdated",
2156
+ fix: (root) => updateVerificationRule(root)
2157
+ },
2158
+ {
2159
+ analyzer: "Rules",
2160
+ match: "No .claude/agents/code-reviewer.md",
2161
+ fix: (root) => createReviewerAgent(root)
2162
+ },
2163
+ {
2164
+ analyzer: "Quality",
2165
+ match: "Duplicate ## Memory",
2166
+ fix: (root) => collapseMemoryHeadings(root)
2167
+ },
2168
+ {
2169
+ analyzer: "Rules",
2170
+ match: "No BACKLOG.md",
2171
+ fix: (root) => createBacklogMd(root)
2172
+ },
2173
+ {
2174
+ analyzer: "Rules",
2175
+ match: "No .claudeignore",
2176
+ fix: (root, detected) => createClaudeignore(root, detected)
2177
+ },
2178
+ {
2179
+ analyzer: "Rules",
2180
+ match: "No .claude/rules/workflow.md",
2181
+ fix: (root) => createWorkflowRule(root)
2182
+ },
2183
+ {
2184
+ analyzer: "Rules",
2185
+ match: "No .claude/rules/hooks.md",
2186
+ fix: (root) => createHooksRule(root)
2187
+ },
2188
+ {
2189
+ analyzer: "Rules",
2190
+ match: "No .claude/rules/verification.md",
2191
+ fix: (root) => createVerificationRule(root)
2192
+ },
2193
+ {
2194
+ analyzer: "Rules",
2195
+ match: "No .claude/rules/",
2196
+ fix: (root) => createStarterRules(root)
2197
+ },
2198
+ {
2199
+ analyzer: "Hooks",
2200
+ match: "PostCompact hooks can't inject context",
2201
+ fix: (root) => migratePostCompactHook(root)
2202
+ },
2203
+ {
2204
+ analyzer: "Hooks",
2205
+ match: "compact matcher",
2206
+ fix: (root) => addCompactMatcherHook(root)
2207
+ },
2208
+ {
2209
+ analyzer: "Permissions",
2210
+ match: "force-push",
2211
+ fix: (root) => addForcePushProtection(root)
2212
+ },
2213
+ {
2214
+ analyzer: "Permissions",
2215
+ match: "Credential files not blocked",
2216
+ fix: (root) => addCredentialDenyRules(root)
2217
+ },
2218
+ {
2219
+ analyzer: "Permissions",
2220
+ match: "Bypass permissions mode",
2221
+ fix: (root) => addBypassDisable(root)
2222
+ },
2223
+ {
2224
+ analyzer: "Permissions",
2225
+ match: "Sandbox lacks a write grant",
2226
+ fix: (root) => addSandboxMemoryWriteGrant(root)
2227
+ },
2228
+ {
2229
+ analyzer: "Permissions",
2230
+ match: ".env is protected by hooks but not in .claudeignore",
2231
+ fix: (root) => addEnvToClaudeignore(root)
2232
+ },
2233
+ {
2234
+ analyzer: "Permissions",
2235
+ match: ".worktreeinclude is missing or empty",
2236
+ fix: (root) => createWorktreeInclude(root)
2237
+ },
2238
+ {
2239
+ analyzer: "Hooks",
2240
+ match: "registered on PreToolUse",
2241
+ fix: (root) => migrateSprintOpenHookEvent(root)
2242
+ },
2243
+ {
2244
+ analyzer: "Hooks",
2245
+ match: "nudge uses bare stdout",
2246
+ fix: (root) => upgradeStaleNudge(root)
2247
+ },
2248
+ {
2249
+ analyzer: "Hooks",
2250
+ match: "Outdated hygiene script",
2251
+ fix: (root) => refreshHygieneScripts(root)
2252
+ },
2253
+ {
2254
+ analyzer: "Hooks",
2255
+ match: "sprint-size-check",
2256
+ fix: (root) => addSprintSizeHook(root)
2257
+ },
2258
+ {
2259
+ analyzer: "Hooks",
2260
+ match: "sprint-open-check",
2261
+ fix: (root) => addSprintOpenHook(root)
2262
+ },
2263
+ {
2264
+ analyzer: "Hooks",
2265
+ match: "sprint-complete nudge",
2266
+ fix: (root) => addSprintCompleteNudge(root)
2267
+ },
2268
+ {
2269
+ analyzer: "Hooks",
2270
+ match: "workflow-check.sh",
2271
+ fix: (root) => addWorkflowCheckHook(root)
2272
+ },
2273
+ {
2274
+ analyzer: "Rules",
2275
+ match: "No skill authoring conventions",
2276
+ fix: (root) => addSkillAuthoringConventions(root)
2277
+ },
2278
+ {
2279
+ analyzer: "Rules",
2280
+ match: "No /lp-enhance skill",
2281
+ fix: (root) => createEnhanceSkill(root)
2282
+ },
2283
+ {
2284
+ analyzer: "Rules",
2285
+ match: "lp-enhance skill is outdated",
2286
+ fix: (root) => updateEnhanceSkill(root)
2287
+ },
2288
+ {
2289
+ analyzer: "Settings",
2290
+ match: "Deprecated includeCoAuthoredBy",
2291
+ fix: (root) => migrateAttribution(root)
2292
+ },
2293
+ {
2294
+ analyzer: "Hooks",
2295
+ match: "SessionStart",
2296
+ fix: (root) => addSessionStartHook(root)
2297
+ },
2298
+ {
2299
+ analyzer: "Memory",
2300
+ match: "bare claude-launchpad binary",
2301
+ fix: (root) => upgradeBareMemoryHooks(root)
2302
+ },
2303
+ {
2304
+ analyzer: "Memory",
2305
+ match: "Deprecated Stop hook",
2306
+ fix: (root) => removeStaleStopHook(root)
2307
+ },
2308
+ {
2309
+ analyzer: "Memory",
2310
+ match: "autoMemoryEnabled not disabled",
2311
+ fix: (root, _det, placement) => disableAutoMemory(root, placement)
2312
+ },
2313
+ {
2314
+ analyzer: "Memory",
2315
+ match: "MCP tool permission",
2316
+ fix: (root, _det, placement) => addMemoryToolPermissions(root, placement)
2317
+ },
2318
+ {
2319
+ analyzer: "MCP",
2320
+ match: "no allowedMcpServers",
2321
+ fix: (root, _det, placement) => addAllowedMcpServers(root, placement)
2322
+ },
2323
+ {
2324
+ analyzer: "Memory",
2325
+ match: "allowedMcpServers is set but does not include agentic-memory",
2326
+ fix: (root) => addMemoryToAllowedMcpServers(root)
2327
+ },
2328
+ {
2329
+ analyzer: "Memory",
2330
+ match: "SessionStart hook to auto-pull",
2331
+ fix: (root, _det, placement) => addSessionStartPullHook(root, placement)
2332
+ },
2333
+ {
2334
+ analyzer: "Memory",
2335
+ match: "SessionEnd hook to auto-push",
2336
+ fix: (root, _det, placement) => addSessionEndPushHook(root, placement)
2337
+ },
2338
+ {
2339
+ analyzer: "Memory",
2340
+ match: "SessionEnd push hook is not nohup-wrapped",
2341
+ fix: (root) => upgradeStaleSessionEndPushHook(root)
2342
+ },
2343
+ {
2344
+ analyzer: "Memory",
2345
+ match: "CLAUDE.md missing memory guidance",
2346
+ fix: (root, _det, placement) => {
2347
+ const content = "Use agentic-memory to persist knowledge across sessions:\n- Memories are automatically injected at session start\n- STORE IMMEDIATELY when: a dependency strategy changes, an architecture decision is made, a convention is established, a bug pattern is discovered, or a feature is killed/added\n- Use memory_search before memory_store to check for duplicates\n- NEVER store credentials, API keys, tokens, or secrets in memories";
2348
+ const target = placement === "local" ? join7(root, ".claude", "CLAUDE.md") : void 0;
2349
+ return addClaudeMdSection(root, "## Memory", wrapStub(content), target);
1910
2350
  }
1911
- return addClaudeMdSection(root, "## Stack", wrapStub("<!-- TODO: Define your tech stack -->"));
1912
- } },
1913
- { analyzer: "Quality", match: "Session Start", fix: (root) => addClaudeMdSection(root, "## Session Start", wrapStub(SESSION_START_CONTENT)) },
1914
- { analyzer: "Quality", match: "Backlog", fix: (root) => addClaudeMdSection(root, "## Backlog", wrapStub(BACKLOG_CONTENT)) },
1915
- { analyzer: "Quality", match: "Stop-and-Swarm section is outdated", fix: (root) => fixStaleSwarmPhrase(root) },
1916
- { analyzer: "Quality", match: "Stop-and-Swarm", fix: (root) => addClaudeMdSection(root, "## Stop-and-Swarm", wrapStub(STOP_AND_SWARM_CONTENT)) },
1917
- { analyzer: "Rules", match: "workflow.md rule is outdated", fix: (root) => updateWorkflowRule(root) },
1918
- { analyzer: "Rules", match: "No .claude/agents/code-reviewer.md", fix: (root) => createReviewerAgent(root) },
1919
- { analyzer: "Quality", match: "Duplicate ## Memory", fix: (root) => collapseMemoryHeadings(root) },
1920
- { analyzer: "Rules", match: "No BACKLOG.md", fix: (root) => createBacklogMd(root) },
1921
- { analyzer: "Rules", match: "No .claudeignore", fix: (root, detected) => createClaudeignore(root, detected) },
1922
- { analyzer: "Rules", match: "No .claude/rules/workflow.md", fix: (root) => createWorkflowRule(root) },
1923
- { analyzer: "Rules", match: "No .claude/rules/hooks.md", fix: (root) => createHooksRule(root) },
1924
- { analyzer: "Rules", match: "No .claude/rules/", fix: (root) => createStarterRules(root) },
1925
- { analyzer: "Hooks", match: "PostCompact hooks can't inject context", fix: (root) => migratePostCompactHook(root) },
1926
- { analyzer: "Hooks", match: "compact matcher", fix: (root) => addCompactMatcherHook(root) },
1927
- { analyzer: "Permissions", match: "force-push", fix: (root) => addForcePushProtection(root) },
1928
- { analyzer: "Permissions", match: "Credential files not blocked", fix: (root) => addCredentialDenyRules(root) },
1929
- { analyzer: "Permissions", match: "Bypass permissions mode", fix: (root) => addBypassDisable(root) },
1930
- { analyzer: "Permissions", match: "Sandbox lacks a write grant", fix: (root) => addSandboxMemoryWriteGrant(root) },
1931
- { analyzer: "Permissions", match: ".env is protected by hooks but not in .claudeignore", fix: (root) => addEnvToClaudeignore(root) },
1932
- { analyzer: "Permissions", match: ".worktreeinclude is missing or empty", fix: (root) => createWorktreeInclude(root) },
1933
- { analyzer: "Hooks", match: "registered on PreToolUse", fix: (root) => migrateSprintOpenHookEvent(root) },
1934
- { analyzer: "Hooks", match: "nudge uses bare stdout", fix: (root) => upgradeStaleNudge(root) },
1935
- { analyzer: "Hooks", match: "Outdated hygiene script", fix: (root) => refreshHygieneScripts(root) },
1936
- { analyzer: "Hooks", match: "sprint-size-check", fix: (root) => addSprintSizeHook(root) },
1937
- { analyzer: "Hooks", match: "sprint-open-check", fix: (root) => addSprintOpenHook(root) },
1938
- { analyzer: "Hooks", match: "sprint-complete nudge", fix: (root) => addSprintCompleteNudge(root) },
1939
- { analyzer: "Hooks", match: "workflow-check.sh", fix: (root) => addWorkflowCheckHook(root) },
1940
- { analyzer: "Rules", match: "No skill authoring conventions", fix: (root) => addSkillAuthoringConventions(root) },
1941
- { analyzer: "Rules", match: "No /lp-enhance skill", fix: (root) => createEnhanceSkill(root) },
1942
- { analyzer: "Rules", match: "lp-enhance skill is outdated", fix: (root) => updateEnhanceSkill(root) },
1943
- { analyzer: "Settings", match: "Deprecated includeCoAuthoredBy", fix: (root) => migrateAttribution(root) },
1944
- { analyzer: "Hooks", match: "SessionStart", fix: (root) => addSessionStartHook(root) },
1945
- { analyzer: "Memory", match: "Deprecated Stop hook", fix: (root) => removeStaleStopHook(root) },
1946
- { analyzer: "Memory", match: "autoMemoryEnabled not disabled", fix: (root, _det, placement) => disableAutoMemory(root, placement) },
1947
- { analyzer: "Memory", match: "MCP tool permission", fix: (root, _det, placement) => addMemoryToolPermissions(root, placement) },
1948
- { analyzer: "MCP", match: "no allowedMcpServers", fix: (root, _det, placement) => addAllowedMcpServers(root, placement) },
1949
- { analyzer: "Memory", match: "allowedMcpServers is set but does not include agentic-memory", fix: (root) => addMemoryToAllowedMcpServers(root) },
1950
- { analyzer: "Memory", match: "SessionStart hook to auto-pull", fix: (root, _det, placement) => addSessionStartPullHook(root, placement) },
1951
- { analyzer: "Memory", match: "SessionEnd hook to auto-push", fix: (root, _det, placement) => addSessionEndPushHook(root, placement) },
1952
- { analyzer: "Memory", match: "SessionEnd push hook is not nohup-wrapped", fix: (root) => upgradeStaleSessionEndPushHook(root) },
1953
- { analyzer: "Memory", match: "CLAUDE.md missing memory guidance", fix: (root, _det, placement) => {
1954
- const content = "Use agentic-memory to persist knowledge across sessions:\n- Memories are automatically injected at session start\n- STORE IMMEDIATELY when: a dependency strategy changes, an architecture decision is made, a convention is established, a bug pattern is discovered, or a feature is killed/added\n- Use memory_search before memory_store to check for duplicates\n- NEVER store credentials, API keys, tokens, or secrets in memories";
1955
- const target = placement === "local" ? join7(root, ".claude", "CLAUDE.md") : void 0;
1956
- return addClaudeMdSection(root, "## Memory", wrapStub(content), target);
1957
- } }
2351
+ }
1958
2352
  ];
1959
2353
  function hasAutoFix(issue) {
1960
2354
  return FIX_TABLE.some(
@@ -1985,7 +2379,10 @@ async function addCredentialDenyRules(root) {
1985
2379
  const toAdd = ["Read(~/.ssh/*)", "Read(~/.aws/*)", "Read(~/.npmrc)"];
1986
2380
  const missing = toAdd.filter((p) => !deny.includes(p));
1987
2381
  if (missing.length === 0) return false;
1988
- const updated = { ...settings, permissions: { ...permissions, deny: [...deny, ...missing] } };
2382
+ const updated = {
2383
+ ...settings,
2384
+ permissions: { ...permissions, deny: [...deny, ...missing] }
2385
+ };
1989
2386
  await writeSettingsJson(root, updated);
1990
2387
  log.success("Added credential deny rules (SSH, AWS, npm)");
1991
2388
  return true;
@@ -2011,11 +2408,16 @@ async function addSandboxMemoryWriteGrant(root) {
2011
2408
  ...settings,
2012
2409
  sandbox: {
2013
2410
  ...sandbox,
2014
- filesystem: { ...filesystem, allowWrite: [...allowWrite, "~/.agentic-memory"] }
2411
+ filesystem: {
2412
+ ...filesystem,
2413
+ allowWrite: [...allowWrite, "~/.agentic-memory"]
2414
+ }
2015
2415
  }
2016
2416
  };
2017
2417
  await writeSettingsJson(root, updated);
2018
- log.success("Added ~/.agentic-memory to sandbox.filesystem.allowWrite (sandbox stays enabled)");
2418
+ log.success(
2419
+ "Added ~/.agentic-memory to sandbox.filesystem.allowWrite (sandbox stays enabled)"
2420
+ );
2019
2421
  return true;
2020
2422
  }
2021
2423
  async function addEnvToClaudeignore(root) {
@@ -2027,7 +2429,8 @@ async function addEnvToClaudeignore(root) {
2027
2429
  return false;
2028
2430
  }
2029
2431
  const lines = content.split("\n").map((l) => l.trim());
2030
- if (lines.some((l) => l === ".env" || l === ".env.*" || l === ".env*")) return false;
2432
+ if (lines.some((l) => l === ".env" || l === ".env.*" || l === ".env*"))
2433
+ return false;
2031
2434
  await writeFile4(ignorePath, content.trimEnd() + "\n.env\n.env.*\n");
2032
2435
  log.success("Added .env to .claudeignore");
2033
2436
  return true;
@@ -2061,7 +2464,9 @@ async function createBacklogMd(root) {
2061
2464
  if (await fileExists(backlogPath)) return false;
2062
2465
  const name = root.split("/").pop() ?? "Project";
2063
2466
  await writeFile4(backlogPath, generateBacklogMd({ name, description: "" }));
2064
- log.success("Generated BACKLOG.md (WP template + P0\u2013P3 sections + changelog)");
2467
+ log.success(
2468
+ "Generated BACKLOG.md (WP template + P0\u2013P3 sections + changelog)"
2469
+ );
2065
2470
  return true;
2066
2471
  }
2067
2472
  async function createClaudeignore(root, detected) {
@@ -2103,17 +2508,27 @@ async function addSkillAuthoringConventions(root) {
2103
2508
  return false;
2104
2509
  }
2105
2510
  if (/^##\s+Skill\s+Authoring/im.test(content)) return false;
2106
- await writeFile4(conventionsPath, content.trimEnd() + "\n" + SKILL_AUTHORING_SECTION);
2511
+ await writeFile4(
2512
+ conventionsPath,
2513
+ content.trimEnd() + "\n" + SKILL_AUTHORING_SECTION
2514
+ );
2107
2515
  log.success("Added Skill Authoring section to .claude/rules/conventions.md");
2108
2516
  return true;
2109
2517
  }
2110
2518
  async function createEnhanceSkill(root) {
2111
2519
  const skillDir = join7(root, ".claude", "skills", "lp-enhance");
2112
2520
  const skillPath = join7(skillDir, "SKILL.md");
2113
- const globalPath = join7(homedir(), ".claude", "skills", "lp-enhance", "SKILL.md");
2521
+ const globalPath = join7(
2522
+ homedir(),
2523
+ ".claude",
2524
+ "skills",
2525
+ "lp-enhance",
2526
+ "SKILL.md"
2527
+ );
2114
2528
  const legacyProject = join7(root, ".claude", "commands", "lp-enhance.md");
2115
2529
  const legacyGlobal = join7(homedir(), ".claude", "commands", "lp-enhance.md");
2116
- if (await fileExists(skillPath) || await fileExists(globalPath) || await fileExists(legacyProject) || await fileExists(legacyGlobal)) return false;
2530
+ if (await fileExists(skillPath) || await fileExists(globalPath) || await fileExists(legacyProject) || await fileExists(legacyGlobal))
2531
+ return false;
2117
2532
  await mkdir3(skillDir, { recursive: true });
2118
2533
  await writeFile4(skillPath, generateEnhanceSkill());
2119
2534
  log.success("Generated /lp-enhance skill (.claude/skills/lp-enhance/)");
@@ -2121,7 +2536,13 @@ async function createEnhanceSkill(root) {
2121
2536
  }
2122
2537
  async function updateEnhanceSkill(root) {
2123
2538
  const projectPath = join7(root, ".claude", "skills", "lp-enhance", "SKILL.md");
2124
- const globalPath = join7(homedir(), ".claude", "skills", "lp-enhance", "SKILL.md");
2539
+ const globalPath = join7(
2540
+ homedir(),
2541
+ ".claude",
2542
+ "skills",
2543
+ "lp-enhance",
2544
+ "SKILL.md"
2545
+ );
2125
2546
  const targetPath = await fileExists(projectPath) ? projectPath : await fileExists(globalPath) ? globalPath : null;
2126
2547
  if (!targetPath) return false;
2127
2548
  await writeFile4(targetPath, generateEnhanceSkill());
@@ -2293,6 +2714,7 @@ export {
2293
2714
  jqField,
2294
2715
  hasEnvVarHookPattern,
2295
2716
  isJqAvailable,
2717
+ FORCE_PUSH_ERE,
2296
2718
  SPRINT_COMPLETE_NUDGE,
2297
2719
  WORKFLOW_CHECK_WRAPPER,
2298
2720
  SPRINT_OPEN_WRAPPER,
@@ -2303,6 +2725,8 @@ export {
2303
2725
  WORKFLOW_RULE_VERSION,
2304
2726
  generateWorkflowRule,
2305
2727
  generateHooksRule,
2728
+ VERIFICATION_RULE_VERSION,
2729
+ generateVerificationRule,
2306
2730
  generateReviewerAgent,
2307
2731
  applyFixes,
2308
2732
  log,
@@ -2310,4 +2734,4 @@ export {
2310
2734
  printScoreCard,
2311
2735
  renderDoctorReport
2312
2736
  };
2313
- //# sourceMappingURL=chunk-KPO4YURF.js.map
2737
+ //# sourceMappingURL=chunk-U2TFIUGE.js.map