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.
- package/README.md +11 -3
- package/dist/{chunk-DTCCQWOU.js → chunk-7KB5YGQZ.js} +3 -3
- package/dist/{chunk-DTCCQWOU.js.map → chunk-7KB5YGQZ.js.map} +1 -1
- package/dist/chunk-DLDVXHI3.js +184 -0
- package/dist/chunk-DLDVXHI3.js.map +1 -0
- package/dist/chunk-HSGSTSVN.js +212 -0
- package/dist/chunk-HSGSTSVN.js.map +1 -0
- package/dist/{chunk-DXDOVWOA.js → chunk-IDH3M4EI.js} +20 -232
- package/dist/chunk-IDH3M4EI.js.map +1 -0
- package/dist/{chunk-YZ53W47Z.js → chunk-IIWOSSL4.js} +2 -1
- package/dist/chunk-IIWOSSL4.js.map +1 -0
- package/dist/{chunk-ZLI4LI33.js → chunk-IPGNXDJF.js} +2 -2
- package/dist/{chunk-PH5OWJ42.js → chunk-LCKV24Q3.js} +9 -5
- package/dist/{chunk-PH5OWJ42.js.map → chunk-LCKV24Q3.js.map} +1 -1
- package/dist/chunk-MX3T2VEX.js +225 -0
- package/dist/chunk-MX3T2VEX.js.map +1 -0
- package/dist/{chunk-F6SLV2FR.js → chunk-N2SCF7DB.js} +5 -6
- package/dist/chunk-N2SCF7DB.js.map +1 -0
- package/dist/{chunk-KPO4YURF.js → chunk-U2TFIUGE.js} +513 -89
- package/dist/chunk-U2TFIUGE.js.map +1 -0
- package/dist/cli.js +424 -130
- package/dist/cli.js.map +1 -1
- package/dist/commands/memory/server.js +48 -14
- package/dist/commands/memory/server.js.map +1 -1
- package/dist/{context-JALSYDYO.js → context-3GHG7KW6.js} +58 -55
- package/dist/context-3GHG7KW6.js.map +1 -0
- package/dist/doctor-EKGJ2YAO.js +108 -0
- package/dist/doctor-EKGJ2YAO.js.map +1 -0
- package/dist/{install-Q23F37GK.js → install-543ZKMXX.js} +12 -10
- package/dist/install-543ZKMXX.js.map +1 -0
- package/dist/{pull-FYKNNLYC.js → pull-ULSZEVCR.js} +17 -19
- package/dist/pull-ULSZEVCR.js.map +1 -0
- package/dist/{push-QSBK7BQU.js → push-JXCWX4NY.js} +13 -12
- package/dist/push-JXCWX4NY.js.map +1 -0
- package/dist/{require-deps-ZISUZLYD.js → require-deps-AEDO5YRM.js} +3 -3
- package/dist/{stats-SV53G6PQ.js → stats-63L7C24W.js} +9 -7
- package/dist/{stats-SV53G6PQ.js.map → stats-63L7C24W.js.map} +1 -1
- package/dist/{sync-2LDM44U4.js → sync-QTJ6BXQ4.js} +5 -5
- package/dist/{sync-clean-HQ523N7V.js → sync-clean-OJEK4XH4.js} +4 -5
- package/dist/sync-clean-OJEK4XH4.js.map +1 -0
- package/dist/{sync-status-V6WTI32X.js → sync-status-EAX527SC.js} +13 -12
- package/dist/sync-status-EAX527SC.js.map +1 -0
- package/dist/{tui-2UJ2OIHB.js → tui-3KITZCNS.js} +11 -7
- package/dist/{tui-2UJ2OIHB.js.map → tui-3KITZCNS.js.map} +1 -1
- package/package.json +27 -7
- package/scenarios/workflow/premature-victory.yaml +44 -0
- package/dist/chunk-DXDOVWOA.js.map +0 -1
- package/dist/chunk-F6SLV2FR.js.map +0 -1
- package/dist/chunk-KPO4YURF.js.map +0 -1
- package/dist/chunk-YZ53W47Z.js.map +0 -1
- package/dist/chunk-ZIG75MOB.js +0 -366
- package/dist/chunk-ZIG75MOB.js.map +0 -1
- package/dist/context-JALSYDYO.js.map +0 -1
- package/dist/install-Q23F37GK.js.map +0 -1
- package/dist/pull-FYKNNLYC.js.map +0 -1
- package/dist/push-QSBK7BQU.js.map +0 -1
- package/dist/sync-clean-HQ523N7V.js.map +0 -1
- package/dist/sync-status-V6WTI32X.js.map +0 -1
- /package/dist/{chunk-ZLI4LI33.js.map → chunk-IPGNXDJF.js.map} +0 -0
- /package/dist/{require-deps-ZISUZLYD.js.map → require-deps-AEDO5YRM.js.map} +0 -0
- /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\
|
|
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(
|
|
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(
|
|
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(
|
|
1454
|
-
|
|
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(
|
|
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({
|
|
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(
|
|
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
|
|
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
|
|
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 '
|
|
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
|
-
{
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 = {
|
|
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: {
|
|
2411
|
+
filesystem: {
|
|
2412
|
+
...filesystem,
|
|
2413
|
+
allowWrite: [...allowWrite, "~/.agentic-memory"]
|
|
2414
|
+
}
|
|
2015
2415
|
}
|
|
2016
2416
|
};
|
|
2017
2417
|
await writeSettingsJson(root, updated);
|
|
2018
|
-
log.success(
|
|
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*"))
|
|
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(
|
|
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(
|
|
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(
|
|
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))
|
|
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(
|
|
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-
|
|
2737
|
+
//# sourceMappingURL=chunk-U2TFIUGE.js.map
|