claude-launchpad 1.8.1 → 1.9.1
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/{chunk-COGKNJJB.js → chunk-72VWDNAE.js} +2 -2
- package/dist/{chunk-H2E7QMF4.js → chunk-DYHPVA6O.js} +2 -2
- package/dist/{chunk-THDBKJAV.js → chunk-GA3IUQUM.js} +3 -3
- package/dist/{chunk-RDID5P4K.js → chunk-I4S4Q2IV.js} +2 -2
- package/dist/{chunk-IY3Z54UK.js → chunk-RCYLZUU6.js} +268 -124
- package/dist/chunk-RCYLZUU6.js.map +1 -0
- package/dist/cli.js +96 -21
- package/dist/cli.js.map +1 -1
- package/dist/commands/memory/server.js +3 -3
- package/dist/{context-BCTOCTZD.js → context-X7UP2ODK.js} +5 -5
- package/dist/{install-L23C4YWJ.js → install-XBCEI5QK.js} +33 -44
- package/dist/install-XBCEI5QK.js.map +1 -0
- package/dist/{pull-5KYLJ6TH.js → pull-VA62U3OP.js} +7 -7
- package/dist/{push-UOVLT5HO.js → push-C3M6Q4V7.js} +7 -7
- package/dist/{require-deps-T2QOGQQ3.js → require-deps-UBU5CYM5.js} +3 -3
- package/dist/{stats-GL7P24U7.js → stats-R4TWCPHW.js} +6 -6
- package/dist/{sync-clean-7RNYS7EH.js → sync-clean-P4S7V2JS.js} +3 -3
- package/dist/{sync-status-ULZCMBQJ.js → sync-status-TPYUF43G.js} +7 -7
- package/dist/{tui-UGBWF2WT.js → tui-FFLCUR7E.js} +4 -4
- package/package.json +1 -1
- package/dist/chunk-IY3Z54UK.js.map +0 -1
- package/dist/install-L23C4YWJ.js.map +0 -1
- /package/dist/{chunk-COGKNJJB.js.map → chunk-72VWDNAE.js.map} +0 -0
- /package/dist/{chunk-H2E7QMF4.js.map → chunk-DYHPVA6O.js.map} +0 -0
- /package/dist/{chunk-THDBKJAV.js.map → chunk-GA3IUQUM.js.map} +0 -0
- /package/dist/{chunk-RDID5P4K.js.map → chunk-I4S4Q2IV.js.map} +0 -0
- /package/dist/{context-BCTOCTZD.js.map → context-X7UP2ODK.js.map} +0 -0
- /package/dist/{pull-5KYLJ6TH.js.map → pull-VA62U3OP.js.map} +0 -0
- /package/dist/{push-UOVLT5HO.js.map → push-C3M6Q4V7.js.map} +0 -0
- /package/dist/{require-deps-T2QOGQQ3.js.map → require-deps-UBU5CYM5.js.map} +0 -0
- /package/dist/{stats-GL7P24U7.js.map → stats-R4TWCPHW.js.map} +0 -0
- /package/dist/{sync-clean-7RNYS7EH.js.map → sync-clean-P4S7V2JS.js.map} +0 -0
- /package/dist/{sync-status-ULZCMBQJ.js.map → sync-status-TPYUF43G.js.map} +0 -0
- /package/dist/{tui-UGBWF2WT.js.map → tui-FFLCUR7E.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
log
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RCYLZUU6.js";
|
|
5
5
|
|
|
6
6
|
// src/commands/memory/utils/gist-transport.ts
|
|
7
7
|
import { execSync } from "child_process";
|
|
@@ -205,4 +205,4 @@ export {
|
|
|
205
205
|
deleteGistFile,
|
|
206
206
|
updateGistFiles
|
|
207
207
|
};
|
|
208
|
-
//# sourceMappingURL=chunk-
|
|
208
|
+
//# sourceMappingURL=chunk-72VWDNAE.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
log
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RCYLZUU6.js";
|
|
5
5
|
|
|
6
6
|
// src/commands/memory/utils/require-deps.ts
|
|
7
7
|
import { createRequire } from "module";
|
|
@@ -44,4 +44,4 @@ export {
|
|
|
44
44
|
cwdRequire,
|
|
45
45
|
requireMemoryDeps
|
|
46
46
|
};
|
|
47
|
-
//# sourceMappingURL=chunk-
|
|
47
|
+
//# sourceMappingURL=chunk-DYHPVA6O.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
cwdRequire
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-DYHPVA6O.js";
|
|
5
5
|
import {
|
|
6
6
|
__export
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-RCYLZUU6.js";
|
|
8
8
|
|
|
9
9
|
// src/commands/memory/config.ts
|
|
10
10
|
import { z } from "zod";
|
|
@@ -363,4 +363,4 @@ export {
|
|
|
363
363
|
closeDatabase,
|
|
364
364
|
migrate
|
|
365
365
|
};
|
|
366
|
-
//# sourceMappingURL=chunk-
|
|
366
|
+
//# sourceMappingURL=chunk-GA3IUQUM.js.map
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
loadConfig,
|
|
11
11
|
migrate,
|
|
12
12
|
resolveDataDir
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-GA3IUQUM.js";
|
|
14
14
|
|
|
15
15
|
// src/commands/memory/subcommands/init-storage.ts
|
|
16
16
|
function initStorage(dbPath) {
|
|
@@ -32,4 +32,4 @@ function initStorage(dbPath) {
|
|
|
32
32
|
export {
|
|
33
33
|
initStorage
|
|
34
34
|
};
|
|
35
|
-
//# sourceMappingURL=chunk-
|
|
35
|
+
//# sourceMappingURL=chunk-I4S4Q2IV.js.map
|
|
@@ -32,15 +32,15 @@ async function readJsonOrNull(path) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// src/lib/settings.ts
|
|
35
|
-
import { readFile as readFile4, writeFile as
|
|
36
|
-
import { join as
|
|
35
|
+
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir3 } from "fs/promises";
|
|
36
|
+
import { join as join6 } from "path";
|
|
37
37
|
|
|
38
38
|
// src/lib/output.ts
|
|
39
39
|
import chalk from "chalk";
|
|
40
40
|
|
|
41
41
|
// src/commands/doctor/fixer.ts
|
|
42
|
-
import { readFile as readFile3, writeFile, mkdir, access as access2 } from "fs/promises";
|
|
43
|
-
import { join as
|
|
42
|
+
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir2, access as access2 } from "fs/promises";
|
|
43
|
+
import { join as join5 } from "path";
|
|
44
44
|
import { homedir } from "os";
|
|
45
45
|
|
|
46
46
|
// src/lib/sections.ts
|
|
@@ -600,24 +600,236 @@ ${content}
|
|
|
600
600
|
${LP_STUB_CLOSE}`;
|
|
601
601
|
}
|
|
602
602
|
|
|
603
|
+
// src/commands/doctor/fixer-sprint.ts
|
|
604
|
+
import { writeFile as writeFile2 } from "fs/promises";
|
|
605
|
+
import { join as join3 } from "path";
|
|
606
|
+
|
|
607
|
+
// src/lib/hook-builder.ts
|
|
608
|
+
function addOrUpdateHook(existingHooks, options) {
|
|
609
|
+
const hookList = existingHooks?.[options.event] ?? [];
|
|
610
|
+
const alreadyHas = hookList.some((group) => {
|
|
611
|
+
const nested = group.hooks;
|
|
612
|
+
return nested?.some((h) => String(h.command ?? "").includes(options.dedupKeyword));
|
|
613
|
+
});
|
|
614
|
+
if (alreadyHas) {
|
|
615
|
+
return { hooks: existingHooks ?? {}, added: false };
|
|
616
|
+
}
|
|
617
|
+
const newEntry = options.entry;
|
|
618
|
+
const updated = options.prepend ? [newEntry, ...hookList] : [...hookList, newEntry];
|
|
619
|
+
return {
|
|
620
|
+
hooks: { ...existingHooks ?? {}, [options.event]: updated },
|
|
621
|
+
added: true
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
async function addHookToSettings(root, event, dedupKeyword, entry, successMsg) {
|
|
625
|
+
const settings = await readSettingsJson(root);
|
|
626
|
+
if (settings === null) return false;
|
|
627
|
+
const existingHooks = settings.hooks;
|
|
628
|
+
const result = addOrUpdateHook(existingHooks, { event, dedupKeyword, entry });
|
|
629
|
+
if (!result.added) return false;
|
|
630
|
+
await writeSettingsJson(root, { ...settings, hooks: result.hooks });
|
|
631
|
+
log.success(successMsg);
|
|
632
|
+
return true;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// src/lib/hook-scripts.ts
|
|
636
|
+
import { writeFile, mkdir, chmod } from "fs/promises";
|
|
637
|
+
import { join as join2 } from "path";
|
|
638
|
+
var SPRINT_SIZE_CHECK = `#!/usr/bin/env bash
|
|
639
|
+
# Warns when the current sprint is too small (<3) or too large (>7).
|
|
640
|
+
# Sweet spot is 3-6 work packages per sprint. Non-blocking (always exits 0).
|
|
641
|
+
|
|
642
|
+
set -u
|
|
643
|
+
tasks="\${1:-TASKS.md}"
|
|
644
|
+
[ -f "$tasks" ] || exit 0
|
|
645
|
+
|
|
646
|
+
section=$(sed -n '/^## Current/,/^## /p' "$tasks" 2>/dev/null)
|
|
647
|
+
[ -z "$section" ] && exit 0
|
|
648
|
+
|
|
649
|
+
unchecked=$(echo "$section" | grep -cF -e '- [ ]' || true)
|
|
650
|
+
checked=$(echo "$section" | grep -cF -e '- [x]' || true)
|
|
651
|
+
total=$((unchecked + checked))
|
|
652
|
+
|
|
653
|
+
if [ "$total" -eq 0 ]; then
|
|
654
|
+
echo "NOTE: Current sprint has no work packages yet. Pull 3-6 from BACKLOG.md to start."
|
|
655
|
+
exit 0
|
|
656
|
+
fi
|
|
657
|
+
|
|
658
|
+
if [ "$unchecked" -eq 0 ]; then exit 0; fi
|
|
659
|
+
|
|
660
|
+
if [ "$unchecked" -lt 3 ]; then
|
|
661
|
+
echo "NOTE: Current sprint has $unchecked open work package(s) \u2014 that's a microsprint. Pull from BACKLOG.md (aim 3-6)."
|
|
662
|
+
exit 0
|
|
663
|
+
fi
|
|
664
|
+
|
|
665
|
+
if [ "$unchecked" -gt 7 ]; then
|
|
666
|
+
echo "NOTE: Current sprint has $unchecked open work packages \u2014 oversized. Move some back to BACKLOG.md (aim 3-6)."
|
|
667
|
+
exit 0
|
|
668
|
+
fi
|
|
669
|
+
|
|
670
|
+
exit 0
|
|
671
|
+
`;
|
|
672
|
+
var SPRINT_OPEN_CHECK = `#!/usr/bin/env bash
|
|
673
|
+
# Warns when TASKS.md opens a new sprint block but BACKLOG.md has no staged
|
|
674
|
+
# deletions, i.e. the "remove pulled WPs from BACKLOG in the same edit" rule
|
|
675
|
+
# from CLAUDE.md was skipped. Non-blocking (always exits 0).
|
|
676
|
+
|
|
677
|
+
set -u
|
|
678
|
+
cmd="\${TOOL_INPUT_COMMAND:-}"
|
|
679
|
+
|
|
680
|
+
# Only act on \`git commit\`, word-boundary match.
|
|
681
|
+
echo "$cmd" | grep -qE '(^|[^a-zA-Z0-9_-])git[[:space:]]+commit([[:space:]]|$)' || exit 0
|
|
682
|
+
|
|
683
|
+
# Nothing staged
|
|
684
|
+
git diff --cached --quiet 2>/dev/null && exit 0
|
|
685
|
+
|
|
686
|
+
# TASKS.md not staged
|
|
687
|
+
git diff --cached --name-only --diff-filter=ACM 2>/dev/null | grep -q '^TASKS\\.md$' || exit 0
|
|
688
|
+
|
|
689
|
+
# Does the staged TASKS.md diff ADD a new \`## Current\` block?
|
|
690
|
+
new_sprint=$(git diff --cached TASKS.md 2>/dev/null | grep -cE '^\\+## Current')
|
|
691
|
+
[ "$new_sprint" -eq 0 ] && exit 0
|
|
692
|
+
|
|
693
|
+
# If a new sprint was opened, BACKLOG.md should have net deletions.
|
|
694
|
+
backlog_deletions=$(git diff --cached BACKLOG.md 2>/dev/null | grep -cE '^-[^-]')
|
|
695
|
+
if [ "$backlog_deletions" -eq 0 ]; then
|
|
696
|
+
echo ""
|
|
697
|
+
echo "WARNING: sprint-open hygiene"
|
|
698
|
+
echo ""
|
|
699
|
+
echo "TASKS.md stages a new '## Current' block, but BACKLOG.md has no"
|
|
700
|
+
echo "staged deletions. When a WP is pulled from BACKLOG.md into a sprint,"
|
|
701
|
+
echo "remove it from BACKLOG.md in the same edit. Overlap = drift."
|
|
702
|
+
echo ""
|
|
703
|
+
echo "If you opened a fresh-scope sprint with no BACKLOG pulls, ignore"
|
|
704
|
+
echo "this. Otherwise scrub BACKLOG.md before committing."
|
|
705
|
+
echo ""
|
|
706
|
+
fi
|
|
707
|
+
|
|
708
|
+
exit 0
|
|
709
|
+
`;
|
|
710
|
+
async function writeSprintHygieneScripts(root) {
|
|
711
|
+
const hooksDir = join2(root, ".claude", "hooks");
|
|
712
|
+
await mkdir(hooksDir, { recursive: true });
|
|
713
|
+
const sizePath = join2(hooksDir, "sprint-size-check.sh");
|
|
714
|
+
const openPath = join2(hooksDir, "sprint-open-check.sh");
|
|
715
|
+
await writeFile(sizePath, SPRINT_SIZE_CHECK);
|
|
716
|
+
await writeFile(openPath, SPRINT_OPEN_CHECK);
|
|
717
|
+
await chmod(sizePath, 493);
|
|
718
|
+
await chmod(openPath, 493);
|
|
719
|
+
return { sizePath, openPath };
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// src/commands/doctor/fixer-sprint.ts
|
|
723
|
+
var WORKTREE_INCLUDE_TEMPLATE = `# Files copied into git worktrees that Claude Code creates for subagents.
|
|
724
|
+
# Listed files must be gitignored \u2014 that's the point: keep secrets out of
|
|
725
|
+
# commits while letting worktree subagents inherit local env so dev servers,
|
|
726
|
+
# tests, and integration runs work the same as the main tree.
|
|
727
|
+
# Anything needed by \`pnpm dev\`, \`pnpm test\`, etc. that's NOT committed
|
|
728
|
+
# should land here.
|
|
729
|
+
|
|
730
|
+
.env.local
|
|
731
|
+
.env
|
|
732
|
+
`;
|
|
733
|
+
async function createWorktreeInclude(root) {
|
|
734
|
+
await writeFile2(join3(root, ".worktreeinclude"), WORKTREE_INCLUDE_TEMPLATE);
|
|
735
|
+
log.success("Generated .worktreeinclude (worktree subagents inherit .env.local / .env)");
|
|
736
|
+
return true;
|
|
737
|
+
}
|
|
738
|
+
async function addSprintSizeHook(root) {
|
|
739
|
+
await writeSprintHygieneScripts(root);
|
|
740
|
+
return addHookToSettings(root, "SessionStart", "sprint-size-check.sh", {
|
|
741
|
+
matcher: "startup|resume",
|
|
742
|
+
hooks: [{ type: "command", command: "bash .claude/hooks/sprint-size-check.sh TASKS.md 2>/dev/null; exit 0" }]
|
|
743
|
+
}, "Added sprint-size-check hook (warns on microsprint/oversized sprints)");
|
|
744
|
+
}
|
|
745
|
+
async function addSprintOpenHook(root) {
|
|
746
|
+
await writeSprintHygieneScripts(root);
|
|
747
|
+
return addHookToSettings(root, "PreToolUse", "sprint-open-check.sh", {
|
|
748
|
+
matcher: "Bash",
|
|
749
|
+
hooks: [{ type: "command", command: "bash .claude/hooks/sprint-open-check.sh 2>/dev/null; exit 0" }]
|
|
750
|
+
}, "Added sprint-open-check hook (warns on new sprint without BACKLOG cleanup)");
|
|
751
|
+
}
|
|
752
|
+
async function addSprintCompleteNudge(root) {
|
|
753
|
+
return addHookToSettings(root, "PostToolUse", "Sprint complete", {
|
|
754
|
+
matcher: "Edit|Write",
|
|
755
|
+
hooks: [{
|
|
756
|
+
type: "command",
|
|
757
|
+
command: `echo "$TOOL_INPUT_FILE_PATH" | grep -q TASKS.md || exit 0; section=$(sed -n '/^## Current/,/^## /p' TASKS.md 2>/dev/null); [ -z "$section" ] && exit 0; unchecked=$(echo "$section" | grep -cF '- [ ]' || true); checked=$(echo "$section" | grep -cF '- [x]' || true); [ "$unchecked" -eq 0 ] && [ "$checked" -gt 0 ] && echo 'Sprint complete \u2014 all current tasks done. Consider a quick quality check before committing: scan for dead code, debug artifacts, TODO hacks, and convention violations. Run tests if available. Skip if trivial.'; exit 0`
|
|
758
|
+
}]
|
|
759
|
+
}, "Added sprint-complete nudge hook");
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// src/commands/doctor/fixer-hooks.ts
|
|
763
|
+
var FORMATTERS = {
|
|
764
|
+
TypeScript: { extensions: ["ts", "tsx"], command: "npx prettier --write" },
|
|
765
|
+
JavaScript: { extensions: ["js", "jsx"], command: "npx prettier --write" },
|
|
766
|
+
Python: { extensions: ["py"], command: "ruff format" },
|
|
767
|
+
Go: { extensions: ["go"], command: "gofmt -w" },
|
|
768
|
+
Rust: { extensions: ["rs"], command: "rustfmt" },
|
|
769
|
+
Ruby: { extensions: ["rb"], command: "rubocop -A" },
|
|
770
|
+
PHP: { extensions: ["php"], command: "vendor/bin/pint" }
|
|
771
|
+
};
|
|
772
|
+
async function addEnvProtectionHook(root) {
|
|
773
|
+
return addHookToSettings(root, "PreToolUse", ".env", {
|
|
774
|
+
matcher: "Read|Write|Edit",
|
|
775
|
+
hooks: [{
|
|
776
|
+
type: "command",
|
|
777
|
+
command: `echo "$TOOL_INPUT_FILE_PATH" | grep -qE '\\.(env|env\\..*)$' && ! echo "$TOOL_INPUT_FILE_PATH" | grep -q '.env.example' && echo 'BLOCKED: .env files contain secrets' && exit 1; exit 0`
|
|
778
|
+
}]
|
|
779
|
+
}, "Added .env file protection hook (PreToolUse)");
|
|
780
|
+
}
|
|
781
|
+
async function addAutoFormatHook(root, detected) {
|
|
782
|
+
if (!detected.language) return false;
|
|
783
|
+
const config = detected.language ? FORMATTERS[detected.language] : null;
|
|
784
|
+
if (!config) return false;
|
|
785
|
+
const extChecks = config.extensions.map((ext) => `[ "$ext" = "${ext}" ]`).join(" || ");
|
|
786
|
+
return addHookToSettings(root, "PostToolUse", "format", {
|
|
787
|
+
matcher: "Write|Edit",
|
|
788
|
+
hooks: [{
|
|
789
|
+
type: "command",
|
|
790
|
+
command: `ext=\${TOOL_INPUT_FILE_PATH##*.}; (${extChecks}) && ${config.command} "$TOOL_INPUT_FILE_PATH" 2>/dev/null; exit 0`
|
|
791
|
+
}]
|
|
792
|
+
}, `Added auto-format hook (PostToolUse \u2192 ${config.command})`);
|
|
793
|
+
}
|
|
794
|
+
async function addForcePushProtection(root) {
|
|
795
|
+
return addHookToSettings(root, "PreToolUse", "force", {
|
|
796
|
+
matcher: "Bash",
|
|
797
|
+
hooks: [{
|
|
798
|
+
type: "command",
|
|
799
|
+
command: `echo "$TOOL_INPUT_COMMAND" | grep -qE 'push.*--force|push.*-f' && echo 'WARNING: Force push detected \u2014 this can destroy remote history' && exit 1; exit 0`
|
|
800
|
+
}]
|
|
801
|
+
}, "Added force-push protection hook (PreToolUse \u2192 Bash)");
|
|
802
|
+
}
|
|
803
|
+
async function addPostCompactHook(root) {
|
|
804
|
+
return addHookToSettings(root, "PostCompact", "TASKS.md", {
|
|
805
|
+
matcher: "",
|
|
806
|
+
hooks: [{ type: "command", command: "cat TASKS.md 2>/dev/null; exit 0" }]
|
|
807
|
+
}, "Added PostCompact hook (re-injects TASKS.md after compaction)");
|
|
808
|
+
}
|
|
809
|
+
async function addSessionStartHook(root) {
|
|
810
|
+
return addHookToSettings(root, "SessionStart", "TASKS.md", {
|
|
811
|
+
matcher: "startup|resume",
|
|
812
|
+
hooks: [{ type: "command", command: "cat TASKS.md 2>/dev/null; exit 0" }]
|
|
813
|
+
}, "Added SessionStart hook (injects TASKS.md at startup)");
|
|
814
|
+
}
|
|
815
|
+
|
|
603
816
|
// src/commands/doctor/fixer-memory.ts
|
|
604
817
|
import { readFile as readFile2 } from "fs/promises";
|
|
605
|
-
import { join as
|
|
818
|
+
import { join as join4 } from "path";
|
|
606
819
|
async function addPlacementHook(root, placement, event, dedupKeyword, entry, prepend, successMsg) {
|
|
607
820
|
const read = placement === "local" ? readSettingsLocalJson : readSettingsJson;
|
|
608
821
|
const write = placement === "local" ? writeSettingsLocalJson : writeSettingsJson;
|
|
609
822
|
const settings = await read(root);
|
|
610
823
|
if (settings === null) return false;
|
|
611
|
-
const
|
|
612
|
-
const
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
824
|
+
const existingHooks = settings.hooks;
|
|
825
|
+
const result = addOrUpdateHook(existingHooks, {
|
|
826
|
+
event,
|
|
827
|
+
dedupKeyword,
|
|
828
|
+
entry,
|
|
829
|
+
prepend
|
|
616
830
|
});
|
|
617
|
-
if (
|
|
618
|
-
|
|
619
|
-
const updatedSettings = { ...settings, hooks: { ...hooks, [event]: updatedList } };
|
|
620
|
-
await write(root, updatedSettings);
|
|
831
|
+
if (!result.added) return false;
|
|
832
|
+
await write(root, { ...settings, hooks: result.hooks });
|
|
621
833
|
log.success(successMsg);
|
|
622
834
|
return true;
|
|
623
835
|
}
|
|
@@ -757,7 +969,7 @@ async function addAllowedMcpServers(root, placement) {
|
|
|
757
969
|
if (settingsServers && typeof settingsServers === "object") {
|
|
758
970
|
for (const name of Object.keys(settingsServers)) serverNames.add(name);
|
|
759
971
|
}
|
|
760
|
-
const mcpJsonPath =
|
|
972
|
+
const mcpJsonPath = join4(root, ".mcp.json");
|
|
761
973
|
try {
|
|
762
974
|
const mcpJson = JSON.parse(await readFile2(mcpJsonPath, "utf-8"));
|
|
763
975
|
const mcpServers = mcpJson.mcpServers;
|
|
@@ -829,6 +1041,10 @@ var FIX_TABLE = [
|
|
|
829
1041
|
{ analyzer: "Permissions", match: "Bypass permissions mode", fix: (root) => addBypassDisable(root) },
|
|
830
1042
|
{ analyzer: "Permissions", match: "Filesystem sandbox enabled", fix: (root) => removeSandboxSettings(root) },
|
|
831
1043
|
{ analyzer: "Permissions", match: ".env is protected by hooks but not in .claudeignore", fix: (root) => addEnvToClaudeignore(root) },
|
|
1044
|
+
{ analyzer: "Permissions", match: ".worktreeinclude is missing or empty", fix: (root) => createWorktreeInclude(root) },
|
|
1045
|
+
{ analyzer: "Hooks", match: "sprint-size-check", fix: (root) => addSprintSizeHook(root) },
|
|
1046
|
+
{ analyzer: "Hooks", match: "sprint-open-check", fix: (root) => addSprintOpenHook(root) },
|
|
1047
|
+
{ analyzer: "Hooks", match: "sprint-complete nudge", fix: (root) => addSprintCompleteNudge(root) },
|
|
832
1048
|
{ analyzer: "Rules", match: "No skill authoring conventions", fix: (root) => addSkillAuthoringConventions(root) },
|
|
833
1049
|
{ analyzer: "Rules", match: "No /lp-enhance skill", fix: (root) => createEnhanceSkill(root) },
|
|
834
1050
|
{ analyzer: "Rules", match: "lp-enhance skill is outdated", fix: (root) => updateEnhanceSkill(root) },
|
|
@@ -844,7 +1060,7 @@ var FIX_TABLE = [
|
|
|
844
1060
|
{ analyzer: "Memory", match: "SessionEnd push hook is not nohup-wrapped", fix: (root) => upgradeStaleSessionEndPushHook(root) },
|
|
845
1061
|
{ analyzer: "Memory", match: "CLAUDE.md missing memory guidance", fix: (root, _det, placement) => {
|
|
846
1062
|
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";
|
|
847
|
-
const target = placement === "local" ?
|
|
1063
|
+
const target = placement === "local" ? join5(root, ".claude", "CLAUDE.md") : void 0;
|
|
848
1064
|
return addClaudeMdSection(root, "## Memory", wrapStub(content), target);
|
|
849
1065
|
} }
|
|
850
1066
|
];
|
|
@@ -859,80 +1075,6 @@ async function tryFix(issue, root, detected, placement) {
|
|
|
859
1075
|
);
|
|
860
1076
|
return entry ? entry.fix(root, detected, placement) : false;
|
|
861
1077
|
}
|
|
862
|
-
async function addHook(root, event, dedupKeyword, entry, successMsg) {
|
|
863
|
-
const settings = await readSettingsJson(root);
|
|
864
|
-
if (settings === null) return false;
|
|
865
|
-
const hooks = settings.hooks ?? {};
|
|
866
|
-
const hookList = hooks[event] ?? [];
|
|
867
|
-
const alreadyHas = hookList.some((g) => {
|
|
868
|
-
const nested = g.hooks;
|
|
869
|
-
return nested?.some((h) => String(h.command ?? "").includes(dedupKeyword));
|
|
870
|
-
});
|
|
871
|
-
if (alreadyHas) return false;
|
|
872
|
-
const updated = [...hookList, entry];
|
|
873
|
-
const updatedSettings = { ...settings, hooks: { ...hooks, [event]: updated } };
|
|
874
|
-
await writeSettingsJson(root, updatedSettings);
|
|
875
|
-
log.success(successMsg);
|
|
876
|
-
return true;
|
|
877
|
-
}
|
|
878
|
-
async function addEnvProtectionHook(root) {
|
|
879
|
-
return addHook(root, "PreToolUse", ".env", {
|
|
880
|
-
matcher: "Read|Write|Edit",
|
|
881
|
-
hooks: [{
|
|
882
|
-
type: "command",
|
|
883
|
-
command: `echo "$TOOL_INPUT_FILE_PATH" | grep -qE '\\.(env|env\\..*)$' && ! echo "$TOOL_INPUT_FILE_PATH" | grep -q '.env.example' && echo 'BLOCKED: .env files contain secrets' && exit 1; exit 0`
|
|
884
|
-
}]
|
|
885
|
-
}, "Added .env file protection hook (PreToolUse)");
|
|
886
|
-
}
|
|
887
|
-
async function addAutoFormatHook(root, detected) {
|
|
888
|
-
if (!detected.language) return false;
|
|
889
|
-
const formatters = {
|
|
890
|
-
TypeScript: { extensions: ["ts", "tsx"], command: "npx prettier --write" },
|
|
891
|
-
JavaScript: { extensions: ["js", "jsx"], command: "npx prettier --write" },
|
|
892
|
-
Python: { extensions: ["py"], command: "ruff format" },
|
|
893
|
-
Go: { extensions: ["go"], command: "gofmt -w" },
|
|
894
|
-
Rust: { extensions: ["rs"], command: "rustfmt" },
|
|
895
|
-
Ruby: { extensions: ["rb"], command: "rubocop -A" },
|
|
896
|
-
PHP: { extensions: ["php"], command: "vendor/bin/pint" }
|
|
897
|
-
};
|
|
898
|
-
const config = formatters[detected.language];
|
|
899
|
-
if (!config) return false;
|
|
900
|
-
const extChecks = config.extensions.map((ext) => `[ "$ext" = "${ext}" ]`).join(" || ");
|
|
901
|
-
return addHook(root, "PostToolUse", "format", {
|
|
902
|
-
matcher: "Write|Edit",
|
|
903
|
-
hooks: [{
|
|
904
|
-
type: "command",
|
|
905
|
-
command: `ext=\${TOOL_INPUT_FILE_PATH##*.}; (${extChecks}) && ${config.command} "$TOOL_INPUT_FILE_PATH" 2>/dev/null; exit 0`
|
|
906
|
-
}]
|
|
907
|
-
}, `Added auto-format hook (PostToolUse \u2192 ${config.command})`);
|
|
908
|
-
}
|
|
909
|
-
async function addForcePushProtection(root) {
|
|
910
|
-
return addHook(root, "PreToolUse", "force", {
|
|
911
|
-
matcher: "Bash",
|
|
912
|
-
hooks: [{
|
|
913
|
-
type: "command",
|
|
914
|
-
command: `echo "$TOOL_INPUT_COMMAND" | grep -qE 'push.*--force|push.*-f' && echo 'WARNING: Force push detected \u2014 this can destroy remote history' && exit 1; exit 0`
|
|
915
|
-
}]
|
|
916
|
-
}, "Added force-push protection hook (PreToolUse \u2192 Bash)");
|
|
917
|
-
}
|
|
918
|
-
async function addPostCompactHook(root) {
|
|
919
|
-
return addHook(root, "PostCompact", "TASKS.md", {
|
|
920
|
-
matcher: "",
|
|
921
|
-
hooks: [{
|
|
922
|
-
type: "command",
|
|
923
|
-
command: "cat TASKS.md 2>/dev/null; exit 0"
|
|
924
|
-
}]
|
|
925
|
-
}, "Added PostCompact hook (re-injects TASKS.md after compaction)");
|
|
926
|
-
}
|
|
927
|
-
async function addSessionStartHook(root) {
|
|
928
|
-
return addHook(root, "SessionStart", "TASKS.md", {
|
|
929
|
-
matcher: "startup|resume",
|
|
930
|
-
hooks: [{
|
|
931
|
-
type: "command",
|
|
932
|
-
command: "cat TASKS.md 2>/dev/null; exit 0"
|
|
933
|
-
}]
|
|
934
|
-
}, "Added SessionStart hook (injects TASKS.md at startup)");
|
|
935
|
-
}
|
|
936
1078
|
async function migrateAttribution(root) {
|
|
937
1079
|
const settings = await readSettingsJson(root);
|
|
938
1080
|
if (settings === null) return false;
|
|
@@ -975,7 +1117,7 @@ async function removeSandboxSettings(root) {
|
|
|
975
1117
|
return true;
|
|
976
1118
|
}
|
|
977
1119
|
async function addEnvToClaudeignore(root) {
|
|
978
|
-
const ignorePath =
|
|
1120
|
+
const ignorePath = join5(root, ".claudeignore");
|
|
979
1121
|
let content;
|
|
980
1122
|
try {
|
|
981
1123
|
content = await readFile3(ignorePath, "utf-8");
|
|
@@ -984,18 +1126,18 @@ async function addEnvToClaudeignore(root) {
|
|
|
984
1126
|
}
|
|
985
1127
|
const lines = content.split("\n").map((l) => l.trim());
|
|
986
1128
|
if (lines.some((l) => l === ".env" || l === ".env.*" || l === ".env*")) return false;
|
|
987
|
-
await
|
|
1129
|
+
await writeFile3(ignorePath, content.trimEnd() + "\n.env\n.env.*\n");
|
|
988
1130
|
log.success("Added .env to .claudeignore");
|
|
989
1131
|
return true;
|
|
990
1132
|
}
|
|
991
1133
|
async function addClaudeMdSection(root, heading, content, targetPath) {
|
|
992
|
-
const claudeMdPath = targetPath ??
|
|
1134
|
+
const claudeMdPath = targetPath ?? join5(root, "CLAUDE.md");
|
|
993
1135
|
let existing;
|
|
994
1136
|
try {
|
|
995
1137
|
existing = await readFile3(claudeMdPath, "utf-8");
|
|
996
1138
|
} catch {
|
|
997
1139
|
if (!targetPath) return false;
|
|
998
|
-
await
|
|
1140
|
+
await mkdir2(join5(root, ".claude"), { recursive: true });
|
|
999
1141
|
existing = "# Local Claude Config\n";
|
|
1000
1142
|
}
|
|
1001
1143
|
if (existing.includes(heading)) return false;
|
|
@@ -1007,20 +1149,20 @@ ${content}
|
|
|
1007
1149
|
|
|
1008
1150
|
`;
|
|
1009
1151
|
const updated = existing.slice(0, insertAt) + section + existing.slice(insertAt);
|
|
1010
|
-
await
|
|
1152
|
+
await writeFile3(claudeMdPath, updated);
|
|
1011
1153
|
const label = targetPath ? ".claude/CLAUDE.md" : "CLAUDE.md";
|
|
1012
1154
|
log.success(`Added "${heading}" section to ${label}`);
|
|
1013
1155
|
return true;
|
|
1014
1156
|
}
|
|
1015
1157
|
async function createBacklogMd(root) {
|
|
1016
|
-
const backlogPath =
|
|
1158
|
+
const backlogPath = join5(root, "BACKLOG.md");
|
|
1017
1159
|
try {
|
|
1018
1160
|
await access2(backlogPath);
|
|
1019
1161
|
return false;
|
|
1020
1162
|
} catch {
|
|
1021
1163
|
}
|
|
1022
1164
|
const name = root.split("/").pop() ?? "Project";
|
|
1023
|
-
await
|
|
1165
|
+
await writeFile3(backlogPath, `# ${name} - Backlog
|
|
1024
1166
|
|
|
1025
1167
|
> Features discussed but deferred. Pick up when relevant.
|
|
1026
1168
|
> Priority: P0 = next sprint, P1 = soon, P2 = when relevant.
|
|
@@ -1029,14 +1171,14 @@ async function createBacklogMd(root) {
|
|
|
1029
1171
|
return true;
|
|
1030
1172
|
}
|
|
1031
1173
|
async function createClaudeignore(root, detected) {
|
|
1032
|
-
const ignorePath =
|
|
1174
|
+
const ignorePath = join5(root, ".claudeignore");
|
|
1033
1175
|
try {
|
|
1034
1176
|
await access2(ignorePath);
|
|
1035
1177
|
return false;
|
|
1036
1178
|
} catch {
|
|
1037
1179
|
}
|
|
1038
1180
|
const content = generateClaudeignore(detected);
|
|
1039
|
-
await
|
|
1181
|
+
await writeFile3(ignorePath, content);
|
|
1040
1182
|
log.success("Generated .claudeignore with language-specific ignore patterns");
|
|
1041
1183
|
return true;
|
|
1042
1184
|
}
|
|
@@ -1046,15 +1188,15 @@ var SKILL_AUTHORING_SECTION = `
|
|
|
1046
1188
|
${SKILL_AUTHORING_CONTENT}
|
|
1047
1189
|
`;
|
|
1048
1190
|
async function createStarterRules(root) {
|
|
1049
|
-
const rulesDir =
|
|
1191
|
+
const rulesDir = join5(root, ".claude", "rules");
|
|
1050
1192
|
try {
|
|
1051
1193
|
await access2(rulesDir);
|
|
1052
1194
|
return false;
|
|
1053
1195
|
} catch {
|
|
1054
1196
|
}
|
|
1055
|
-
await
|
|
1056
|
-
await
|
|
1057
|
-
|
|
1197
|
+
await mkdir2(rulesDir, { recursive: true });
|
|
1198
|
+
await writeFile3(
|
|
1199
|
+
join5(rulesDir, "conventions.md"),
|
|
1058
1200
|
`# Project Conventions
|
|
1059
1201
|
|
|
1060
1202
|
- Use conventional commits (feat:, fix:, docs:, refactor:, test:, chore:)
|
|
@@ -1067,7 +1209,7 @@ ${SKILL_AUTHORING_SECTION}`
|
|
|
1067
1209
|
return true;
|
|
1068
1210
|
}
|
|
1069
1211
|
async function addSkillAuthoringConventions(root) {
|
|
1070
|
-
const conventionsPath =
|
|
1212
|
+
const conventionsPath = join5(root, ".claude", "rules", "conventions.md");
|
|
1071
1213
|
let content;
|
|
1072
1214
|
try {
|
|
1073
1215
|
content = await readFile3(conventionsPath, "utf-8");
|
|
@@ -1075,28 +1217,28 @@ async function addSkillAuthoringConventions(root) {
|
|
|
1075
1217
|
return false;
|
|
1076
1218
|
}
|
|
1077
1219
|
if (/^##\s+Skill\s+Authoring/im.test(content)) return false;
|
|
1078
|
-
await
|
|
1220
|
+
await writeFile3(conventionsPath, content.trimEnd() + "\n" + SKILL_AUTHORING_SECTION);
|
|
1079
1221
|
log.success("Added Skill Authoring section to .claude/rules/conventions.md");
|
|
1080
1222
|
return true;
|
|
1081
1223
|
}
|
|
1082
1224
|
async function createEnhanceSkill(root) {
|
|
1083
|
-
const skillDir =
|
|
1084
|
-
const skillPath =
|
|
1085
|
-
const globalPath =
|
|
1086
|
-
const legacyProject =
|
|
1087
|
-
const legacyGlobal =
|
|
1225
|
+
const skillDir = join5(root, ".claude", "skills", "lp-enhance");
|
|
1226
|
+
const skillPath = join5(skillDir, "SKILL.md");
|
|
1227
|
+
const globalPath = join5(homedir(), ".claude", "skills", "lp-enhance", "SKILL.md");
|
|
1228
|
+
const legacyProject = join5(root, ".claude", "commands", "lp-enhance.md");
|
|
1229
|
+
const legacyGlobal = join5(homedir(), ".claude", "commands", "lp-enhance.md");
|
|
1088
1230
|
if (await fileExists(skillPath) || await fileExists(globalPath) || await fileExists(legacyProject) || await fileExists(legacyGlobal)) return false;
|
|
1089
|
-
await
|
|
1090
|
-
await
|
|
1231
|
+
await mkdir2(skillDir, { recursive: true });
|
|
1232
|
+
await writeFile3(skillPath, generateEnhanceSkill());
|
|
1091
1233
|
log.success("Generated /lp-enhance skill (.claude/skills/lp-enhance/)");
|
|
1092
1234
|
return true;
|
|
1093
1235
|
}
|
|
1094
1236
|
async function updateEnhanceSkill(root) {
|
|
1095
|
-
const projectPath =
|
|
1096
|
-
const globalPath =
|
|
1237
|
+
const projectPath = join5(root, ".claude", "skills", "lp-enhance", "SKILL.md");
|
|
1238
|
+
const globalPath = join5(homedir(), ".claude", "skills", "lp-enhance", "SKILL.md");
|
|
1097
1239
|
const targetPath = await fileExists(projectPath) ? projectPath : await fileExists(globalPath) ? globalPath : null;
|
|
1098
1240
|
if (!targetPath) return false;
|
|
1099
|
-
await
|
|
1241
|
+
await writeFile3(targetPath, generateEnhanceSkill());
|
|
1100
1242
|
log.success("Updated /lp-enhance skill to latest version");
|
|
1101
1243
|
return true;
|
|
1102
1244
|
}
|
|
@@ -1222,20 +1364,20 @@ async function readJsonFile(path) {
|
|
|
1222
1364
|
}
|
|
1223
1365
|
}
|
|
1224
1366
|
async function readSettingsJson(root) {
|
|
1225
|
-
return readJsonFile(
|
|
1367
|
+
return readJsonFile(join6(root, ".claude", "settings.json"));
|
|
1226
1368
|
}
|
|
1227
1369
|
async function writeSettingsJson(root, settings) {
|
|
1228
|
-
const dir =
|
|
1229
|
-
await
|
|
1230
|
-
await
|
|
1370
|
+
const dir = join6(root, ".claude");
|
|
1371
|
+
await mkdir3(dir, { recursive: true });
|
|
1372
|
+
await writeFile4(join6(dir, "settings.json"), JSON.stringify(settings, null, 2) + "\n");
|
|
1231
1373
|
}
|
|
1232
1374
|
async function readSettingsLocalJson(root) {
|
|
1233
|
-
return readJsonFile(
|
|
1375
|
+
return readJsonFile(join6(root, ".claude", "settings.local.json"));
|
|
1234
1376
|
}
|
|
1235
1377
|
async function writeSettingsLocalJson(root, settings) {
|
|
1236
|
-
const dir =
|
|
1237
|
-
await
|
|
1238
|
-
await
|
|
1378
|
+
const dir = join6(root, ".claude");
|
|
1379
|
+
await mkdir3(dir, { recursive: true });
|
|
1380
|
+
await writeFile4(join6(dir, "settings.local.json"), JSON.stringify(settings, null, 2) + "\n");
|
|
1239
1381
|
}
|
|
1240
1382
|
|
|
1241
1383
|
export {
|
|
@@ -1257,10 +1399,12 @@ export {
|
|
|
1257
1399
|
writeSettingsLocalJson,
|
|
1258
1400
|
getMemoryPlacement,
|
|
1259
1401
|
LP_STUB_OPEN,
|
|
1402
|
+
addOrUpdateHook,
|
|
1403
|
+
writeSprintHygieneScripts,
|
|
1260
1404
|
applyFixes,
|
|
1261
1405
|
log,
|
|
1262
1406
|
printBanner,
|
|
1263
1407
|
printScoreCard,
|
|
1264
1408
|
renderDoctorReport
|
|
1265
1409
|
};
|
|
1266
|
-
//# sourceMappingURL=chunk-
|
|
1410
|
+
//# sourceMappingURL=chunk-RCYLZUU6.js.map
|