claude-launchpad 0.15.2 → 0.16.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 (33) hide show
  1. package/README.md +3 -3
  2. package/dist/{chunk-4D3EBDNB.js → chunk-24VLPHJU.js} +16 -2
  3. package/dist/chunk-24VLPHJU.js.map +1 -0
  4. package/dist/{chunk-AHEX2RG7.js → chunk-5MWCQLNL.js} +2 -2
  5. package/dist/{chunk-IPVN6SO4.js → chunk-JQDMBE7W.js} +14 -65
  6. package/dist/chunk-JQDMBE7W.js.map +1 -0
  7. package/dist/{chunk-KOSJII4R.js → chunk-SBA5KYQU.js} +15 -1
  8. package/dist/chunk-SBA5KYQU.js.map +1 -0
  9. package/dist/chunk-TSTTFR4B.js +68 -0
  10. package/dist/chunk-TSTTFR4B.js.map +1 -0
  11. package/dist/cli.js +300 -125
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/memory/server.js +1 -1
  14. package/dist/{context-XPXKLF5V.js → context-CWJUUTTU.js} +3 -3
  15. package/dist/{install-UXO5BISS.js → install-MVATZUXZ.js} +49 -6
  16. package/dist/install-MVATZUXZ.js.map +1 -0
  17. package/dist/{pull-3O4B6EL2.js → pull-YOESZ3UC.js} +9 -7
  18. package/dist/{pull-3O4B6EL2.js.map → pull-YOESZ3UC.js.map} +1 -1
  19. package/dist/{push-BGN3DVV5.js → push-74XC5CUK.js} +18 -8
  20. package/dist/push-74XC5CUK.js.map +1 -0
  21. package/dist/{stats-P7S3FTCQ.js → stats-QUBHHPV7.js} +3 -3
  22. package/dist/{tui-GUCKDWDM.js → tui-XIYOOUP6.js} +113 -46
  23. package/dist/tui-XIYOOUP6.js.map +1 -0
  24. package/package.json +3 -2
  25. package/dist/chunk-4D3EBDNB.js.map +0 -1
  26. package/dist/chunk-IPVN6SO4.js.map +0 -1
  27. package/dist/chunk-KOSJII4R.js.map +0 -1
  28. package/dist/install-UXO5BISS.js.map +0 -1
  29. package/dist/push-BGN3DVV5.js.map +0 -1
  30. package/dist/tui-GUCKDWDM.js.map +0 -1
  31. /package/dist/{chunk-AHEX2RG7.js.map → chunk-5MWCQLNL.js.map} +0 -0
  32. /package/dist/{context-XPXKLF5V.js.map → context-CWJUUTTU.js.map} +0 -0
  33. /package/dist/{stats-P7S3FTCQ.js.map → stats-QUBHHPV7.js.map} +0 -0
package/dist/cli.js CHANGED
@@ -5,7 +5,10 @@ import {
5
5
  readSettingsLocalJson,
6
6
  writeSettingsJson,
7
7
  writeSettingsLocalJson
8
- } from "./chunk-KOSJII4R.js";
8
+ } from "./chunk-SBA5KYQU.js";
9
+ import {
10
+ readSyncConfig
11
+ } from "./chunk-JQDMBE7W.js";
9
12
  import {
10
13
  log,
11
14
  printBanner,
@@ -15,7 +18,7 @@ import {
15
18
 
16
19
  // src/cli.ts
17
20
  import { Command as Command5 } from "commander";
18
- import { join as join11 } from "path";
21
+ import { join as join12 } from "path";
19
22
 
20
23
  // src/commands/init/index.ts
21
24
  import { Command } from "commander";
@@ -945,7 +948,7 @@ async function parseClaudeConfig(projectRoot) {
945
948
  readSettingsFromFile(claudeDir, SETTINGS_LOCAL_FILE),
946
949
  readHooks(claudeDir),
947
950
  readRules(claudeDir),
948
- readMcpServers(claudeDir),
951
+ readMcpServers(claudeDir, root),
949
952
  readSkills(claudeDir),
950
953
  readFileOrNull(join3(root, ".claudeignore"))
951
954
  ]);
@@ -995,7 +998,14 @@ async function readSettingsFromFile(claudeDir, filename) {
995
998
  }
996
999
  }
997
1000
  async function readHooks(claudeDir) {
998
- const settingsRaw = await readFileOrNull(join3(claudeDir, SETTINGS_FILE));
1001
+ const [shared, local] = await Promise.all([
1002
+ readHooksFromFile(join3(claudeDir, SETTINGS_FILE)),
1003
+ readHooksFromFile(join3(claudeDir, SETTINGS_LOCAL_FILE))
1004
+ ]);
1005
+ return [...shared, ...local];
1006
+ }
1007
+ async function readHooksFromFile(settingsPath) {
1008
+ const settingsRaw = await readFileOrNull(settingsPath);
999
1009
  if (settingsRaw === null) return [];
1000
1010
  try {
1001
1011
  const settings = JSON.parse(settingsRaw);
@@ -1039,8 +1049,46 @@ async function readRules(claudeDir) {
1039
1049
  const rulesDir = join3(claudeDir, RULES_DIR);
1040
1050
  return listFilesRecursive(rulesDir, ".md");
1041
1051
  }
1042
- async function readMcpServers(claudeDir) {
1043
- const settingsRaw = await readFileOrNull(join3(claudeDir, SETTINGS_FILE));
1052
+ async function readMcpServers(claudeDir, projectRoot) {
1053
+ const [fromMcpJson, fromSettings, fromLocalSettings] = await Promise.all([
1054
+ readMcpJsonFile(join3(projectRoot, ".mcp.json")),
1055
+ readMcpServersFromSettings(join3(claudeDir, SETTINGS_FILE)),
1056
+ readMcpServersFromSettings(join3(claudeDir, SETTINGS_LOCAL_FILE))
1057
+ ]);
1058
+ const seen = /* @__PURE__ */ new Set();
1059
+ const result = [];
1060
+ for (const server of [...fromMcpJson, ...fromLocalSettings, ...fromSettings]) {
1061
+ if (!seen.has(server.name)) {
1062
+ seen.add(server.name);
1063
+ result.push(server);
1064
+ }
1065
+ }
1066
+ return result;
1067
+ }
1068
+ async function readMcpJsonFile(mcpJsonPath) {
1069
+ const raw = await readFileOrNull(mcpJsonPath);
1070
+ if (raw === null) return [];
1071
+ try {
1072
+ const parsed = JSON.parse(raw);
1073
+ const servers = parsed.mcpServers;
1074
+ if (!servers || typeof servers !== "object") return [];
1075
+ const result = [];
1076
+ for (const [name, config] of Object.entries(servers)) {
1077
+ const c = config;
1078
+ result.push({
1079
+ name,
1080
+ transport: c.transport ?? "stdio",
1081
+ command: c.command,
1082
+ url: c.url
1083
+ });
1084
+ }
1085
+ return result;
1086
+ } catch {
1087
+ return [];
1088
+ }
1089
+ }
1090
+ async function readMcpServersFromSettings(settingsPath) {
1091
+ const settingsRaw = await readFileOrNull(settingsPath);
1044
1092
  if (settingsRaw === null) return [];
1045
1093
  try {
1046
1094
  const settings = JSON.parse(settingsRaw);
@@ -1524,6 +1572,32 @@ async function analyzeMcp(config) {
1524
1572
  }
1525
1573
  }
1526
1574
  }
1575
+ if (servers.length > 0) {
1576
+ const settings = config.settings ?? {};
1577
+ const localSettings = config.localSettings ?? {};
1578
+ const hasAllowList = settings.allowedMcpServers || localSettings.allowedMcpServers;
1579
+ if (!hasAllowList) {
1580
+ issues.push({
1581
+ analyzer: "MCP",
1582
+ severity: "medium",
1583
+ message: "MCP servers configured but no allowedMcpServers list \u2014 any added server is auto-trusted",
1584
+ fix: "Add allowedMcpServers to settings.json listing only trusted server names"
1585
+ });
1586
+ }
1587
+ }
1588
+ if (servers.length > 0) {
1589
+ const sandbox = config.settings?.sandbox ?? config.localSettings?.sandbox;
1590
+ const hasNetwork = sandbox?.network !== void 0;
1591
+ const httpServers = servers.filter((s) => s.transport === "sse" || s.transport === "http");
1592
+ if (sandbox?.enabled === true && httpServers.length > 0 && !hasNetwork) {
1593
+ issues.push({
1594
+ analyzer: "MCP",
1595
+ severity: "low",
1596
+ message: "Sandbox enabled with HTTP MCP servers but no network restrictions configured",
1597
+ fix: "Add sandbox.network.allowedHosts to restrict which hosts MCP servers can reach"
1598
+ });
1599
+ }
1600
+ }
1527
1601
  const score = Math.max(0, 100 - issues.filter((i) => i.severity !== "info").length * 25);
1528
1602
  return { name: "MCP Servers", issues, score };
1529
1603
  }
@@ -1539,7 +1613,16 @@ var MEMORY_MCP_TOOLS = [
1539
1613
  "mcp__agentic-memory__memory_update"
1540
1614
  ];
1541
1615
  function hasMemoryIndicators(config) {
1542
- return config.mcpServers.some((s) => s.name === "agentic-memory");
1616
+ if (config.mcpServers.some((s) => s.name === "agentic-memory")) return true;
1617
+ const permissions = config.settings?.permissions ?? {};
1618
+ const localPermissions = config.localSettings?.permissions ?? {};
1619
+ const allowList = [
1620
+ ...permissions.allow ?? [],
1621
+ ...localPermissions.allow ?? []
1622
+ ];
1623
+ if (allowList.some((t) => t.startsWith("mcp__agentic-memory__"))) return true;
1624
+ if (config.hooks.some((h) => h.event === "SessionStart" && h.command?.includes("memory context"))) return true;
1625
+ return false;
1543
1626
  }
1544
1627
  async function analyzeMemory(config) {
1545
1628
  if (!hasMemoryIndicators(config)) return null;
@@ -1599,6 +1682,31 @@ async function analyzeMemory(config) {
1599
1682
  fix: "Add all agentic-memory tool names to allowedTools in .claude/settings.json"
1600
1683
  });
1601
1684
  }
1685
+ const syncConfig = readSyncConfig();
1686
+ if (syncConfig) {
1687
+ const hasSessionStartPull = config.hooks.some(
1688
+ (h) => h.event === "SessionStart" && h.command?.includes("memory pull")
1689
+ );
1690
+ if (!hasSessionStartPull) {
1691
+ issues.push({
1692
+ analyzer: "Memory",
1693
+ severity: "medium",
1694
+ message: "Sync configured but no SessionStart hook to auto-pull memories before context injection",
1695
+ fix: "Run `doctor --fix` to add a SessionStart hook that pulls memories automatically"
1696
+ });
1697
+ }
1698
+ const hasSessionEndPush = config.hooks.some(
1699
+ (h) => h.event === "SessionEnd" && h.command?.includes("memory push")
1700
+ );
1701
+ if (!hasSessionEndPush) {
1702
+ issues.push({
1703
+ analyzer: "Memory",
1704
+ severity: "medium",
1705
+ message: "Sync configured but no SessionEnd hook to auto-push memories after each session",
1706
+ fix: "Run `doctor --fix` to add a SessionEnd hook that pushes memories automatically"
1707
+ });
1708
+ }
1709
+ }
1602
1710
  const critical = issues.filter((i) => i.severity === "critical").length;
1603
1711
  const high = issues.filter((i) => i.severity === "high").length;
1604
1712
  const medium = issues.filter((i) => i.severity === "medium").length;
@@ -1694,9 +1802,127 @@ async function analyzeQuality(config) {
1694
1802
  }
1695
1803
 
1696
1804
  // src/commands/doctor/fixer.ts
1697
- import { readFile as readFile4, writeFile as writeFile2, mkdir as mkdir2, access as access4 } from "fs/promises";
1698
- import { join as join5 } from "path";
1805
+ import { readFile as readFile5, writeFile as writeFile2, mkdir as mkdir2, access as access4 } from "fs/promises";
1806
+ import { join as join6 } from "path";
1699
1807
  import { homedir as homedir3 } from "os";
1808
+
1809
+ // src/commands/doctor/fixer-memory.ts
1810
+ import { readFile as readFile4 } from "fs/promises";
1811
+ import { join as join5 } from "path";
1812
+ async function addPlacementHook(root, placement, event, dedupKeyword, entry, prepend, successMsg) {
1813
+ const read = placement === "local" ? readSettingsLocalJson : readSettingsJson;
1814
+ const write = placement === "local" ? writeSettingsLocalJson : writeSettingsJson;
1815
+ const settings = await read(root);
1816
+ const hooks = settings.hooks ?? {};
1817
+ const hookList = hooks[event] ?? [];
1818
+ const alreadyHas = hookList.some((g) => {
1819
+ const nested = g.hooks;
1820
+ return nested?.some((h) => String(h.command ?? "").includes(dedupKeyword));
1821
+ });
1822
+ if (alreadyHas) return false;
1823
+ hooks[event] = prepend ? [entry, ...hookList] : [...hookList, entry];
1824
+ settings.hooks = hooks;
1825
+ await write(root, settings);
1826
+ log.success(successMsg);
1827
+ return true;
1828
+ }
1829
+ async function disableAutoMemory(root, placement) {
1830
+ const read = placement === "local" ? readSettingsLocalJson : readSettingsJson;
1831
+ const write = placement === "local" ? writeSettingsLocalJson : writeSettingsJson;
1832
+ const settings = await read(root);
1833
+ if (settings.autoMemoryEnabled === false) return false;
1834
+ settings.autoMemoryEnabled = false;
1835
+ await write(root, settings);
1836
+ const target = placement === "local" ? "settings.local.json" : "settings.json";
1837
+ log.success(`Set autoMemoryEnabled: false in ${target}`);
1838
+ return true;
1839
+ }
1840
+ async function addMemoryToolPermissions(root, placement) {
1841
+ const read = placement === "local" ? readSettingsLocalJson : readSettingsJson;
1842
+ const write = placement === "local" ? writeSettingsLocalJson : writeSettingsJson;
1843
+ const settings = await read(root);
1844
+ const permissions = settings.permissions ?? {};
1845
+ const allow = permissions.allow ?? [];
1846
+ const tools = [
1847
+ "mcp__agentic-memory__memory_store",
1848
+ "mcp__agentic-memory__memory_search",
1849
+ "mcp__agentic-memory__memory_recent",
1850
+ "mcp__agentic-memory__memory_forget",
1851
+ "mcp__agentic-memory__memory_relate",
1852
+ "mcp__agentic-memory__memory_stats",
1853
+ "mcp__agentic-memory__memory_update"
1854
+ ];
1855
+ const missing = tools.filter((t) => !allow.includes(t));
1856
+ if (missing.length === 0) return false;
1857
+ settings.permissions = { ...permissions, allow: [...allow, ...missing] };
1858
+ await write(root, settings);
1859
+ const target = placement === "local" ? "settings.local.json" : "settings.json";
1860
+ log.success(`Added agentic-memory MCP tool permissions to ${target}`);
1861
+ return true;
1862
+ }
1863
+ async function addSessionStartPullHook(root, placement) {
1864
+ const target = placement === "local" ? "settings.local.json" : "settings.json";
1865
+ return addPlacementHook(root, placement, "SessionStart", "memory pull", {
1866
+ matcher: "startup",
1867
+ hooks: [{ type: "command", command: "claude-launchpad memory pull -y 2>/dev/null; exit 0" }]
1868
+ }, true, `Added SessionStart hook for memory sync to ${target}`);
1869
+ }
1870
+ async function addSessionEndPushHook(root, placement) {
1871
+ const target = placement === "local" ? "settings.local.json" : "settings.json";
1872
+ return addPlacementHook(root, placement, "SessionEnd", "memory push", {
1873
+ hooks: [{ type: "command", command: "claude-launchpad memory push -y >/dev/null 2>&1 & exit 0" }]
1874
+ }, false, `Added SessionEnd hook for memory sync to ${target}`);
1875
+ }
1876
+ async function removeStaleStopHook(root) {
1877
+ const settings = await readSettingsJson(root);
1878
+ const hooks = settings.hooks;
1879
+ if (!hooks?.Stop) return false;
1880
+ const stopHooks = hooks.Stop;
1881
+ const filtered = stopHooks.filter((h) => {
1882
+ const innerHooks = h.hooks;
1883
+ return !innerHooks?.some(
1884
+ (ih) => typeof ih.command === "string" && ih.command.includes("memory extract")
1885
+ );
1886
+ });
1887
+ if (filtered.length === stopHooks.length) return false;
1888
+ const updated = filtered.length === 0 ? (({ Stop: _, ...rest }) => rest)(hooks) : { ...hooks, Stop: filtered };
1889
+ settings.hooks = updated;
1890
+ await writeSettingsJson(root, settings);
1891
+ log.success("Removed deprecated Stop hook (memory extract)");
1892
+ return true;
1893
+ }
1894
+ async function addAllowedMcpServers(root, placement) {
1895
+ const read = placement === "local" ? readSettingsLocalJson : readSettingsJson;
1896
+ const write = placement === "local" ? writeSettingsLocalJson : writeSettingsJson;
1897
+ const settings = await read(root);
1898
+ if (settings.allowedMcpServers) return false;
1899
+ const other = placement === "local" ? await readSettingsJson(root) : await readSettingsLocalJson(root);
1900
+ if (other.allowedMcpServers) return false;
1901
+ const serverNames = /* @__PURE__ */ new Set();
1902
+ const settingsServers = settings.mcpServers;
1903
+ if (settingsServers && typeof settingsServers === "object") {
1904
+ for (const name of Object.keys(settingsServers)) serverNames.add(name);
1905
+ }
1906
+ const mcpJsonPath = join5(root, ".mcp.json");
1907
+ try {
1908
+ const mcpJson = JSON.parse(await readFile4(mcpJsonPath, "utf-8"));
1909
+ const mcpServers = mcpJson.mcpServers;
1910
+ if (mcpServers && typeof mcpServers === "object") {
1911
+ for (const name of Object.keys(mcpServers)) serverNames.add(name);
1912
+ }
1913
+ } catch {
1914
+ }
1915
+ if (serverNames.size === 0) return false;
1916
+ settings.allowedMcpServers = [...serverNames].map(
1917
+ (name) => ({ serverName: name })
1918
+ );
1919
+ await write(root, settings);
1920
+ const target = placement === "local" ? "settings.local.json" : "settings.json";
1921
+ log.success(`Added allowedMcpServers from configured servers to ${target}`);
1922
+ return true;
1923
+ }
1924
+
1925
+ // src/commands/doctor/fixer.ts
1700
1926
  async function applyFixes(issues, projectRoot) {
1701
1927
  const detected = await detectProject(projectRoot);
1702
1928
  const hasMemoryIssues = issues.some((i) => i.analyzer === "Memory");
@@ -1718,7 +1944,8 @@ var FIX_TABLE = [
1718
1944
  const a = await addEnvProtectionHook(root);
1719
1945
  const b = await addAutoFormatHook(root, detected);
1720
1946
  const c = await addForcePushProtection(root);
1721
- return a || b || c;
1947
+ const d = await addSessionStartHook(root);
1948
+ return a || b || c || d;
1722
1949
  } },
1723
1950
  { analyzer: "Hooks", match: ".env file protection", fix: (root) => addEnvProtectionHook(root) },
1724
1951
  { analyzer: "Hooks", match: "auto-format", fix: (root, detected) => addAutoFormatHook(root, detected) },
@@ -1750,9 +1977,12 @@ var FIX_TABLE = [
1750
1977
  { analyzer: "Memory", match: "Deprecated Stop hook", fix: (root) => removeStaleStopHook(root) },
1751
1978
  { analyzer: "Memory", match: "autoMemoryEnabled not disabled", fix: (root, _det, placement) => disableAutoMemory(root, placement) },
1752
1979
  { analyzer: "Memory", match: "MCP tool permission", fix: (root, _det, placement) => addMemoryToolPermissions(root, placement) },
1980
+ { analyzer: "MCP", match: "no allowedMcpServers", fix: (root, _det, placement) => addAllowedMcpServers(root, placement) },
1981
+ { analyzer: "Memory", match: "SessionStart hook to auto-pull", fix: (root, _det, placement) => addSessionStartPullHook(root, placement) },
1982
+ { analyzer: "Memory", match: "SessionEnd hook to auto-push", fix: (root, _det, placement) => addSessionEndPushHook(root, placement) },
1753
1983
  { analyzer: "Memory", match: "CLAUDE.md missing memory guidance", fix: (root, _det, placement) => {
1754
1984
  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";
1755
- const target = placement === "local" ? join5(root, ".claude", "CLAUDE.md") : void 0;
1985
+ const target = placement === "local" ? join6(root, ".claude", "CLAUDE.md") : void 0;
1756
1986
  return addClaudeMdSection(root, "## Memory", content, target);
1757
1987
  } }
1758
1988
  ];
@@ -1874,10 +2104,10 @@ async function addSandboxSettings(root) {
1874
2104
  return true;
1875
2105
  }
1876
2106
  async function addEnvToClaudeignore(root) {
1877
- const ignorePath = join5(root, ".claudeignore");
2107
+ const ignorePath = join6(root, ".claudeignore");
1878
2108
  let content;
1879
2109
  try {
1880
- content = await readFile4(ignorePath, "utf-8");
2110
+ content = await readFile5(ignorePath, "utf-8");
1881
2111
  } catch {
1882
2112
  return false;
1883
2113
  }
@@ -1888,13 +2118,13 @@ async function addEnvToClaudeignore(root) {
1888
2118
  return true;
1889
2119
  }
1890
2120
  async function addClaudeMdSection(root, heading, content, targetPath) {
1891
- const claudeMdPath = targetPath ?? join5(root, "CLAUDE.md");
2121
+ const claudeMdPath = targetPath ?? join6(root, "CLAUDE.md");
1892
2122
  let existing;
1893
2123
  try {
1894
- existing = await readFile4(claudeMdPath, "utf-8");
2124
+ existing = await readFile5(claudeMdPath, "utf-8");
1895
2125
  } catch {
1896
2126
  if (!targetPath) return false;
1897
- await mkdir2(join5(root, ".claude"), { recursive: true });
2127
+ await mkdir2(join6(root, ".claude"), { recursive: true });
1898
2128
  existing = "# Local Claude Config\n";
1899
2129
  }
1900
2130
  if (existing.includes(heading)) return false;
@@ -1912,7 +2142,7 @@ ${content}
1912
2142
  return true;
1913
2143
  }
1914
2144
  async function createBacklogMd(root) {
1915
- const backlogPath = join5(root, "BACKLOG.md");
2145
+ const backlogPath = join6(root, "BACKLOG.md");
1916
2146
  try {
1917
2147
  await access4(backlogPath);
1918
2148
  return false;
@@ -1928,7 +2158,7 @@ async function createBacklogMd(root) {
1928
2158
  return true;
1929
2159
  }
1930
2160
  async function createClaudeignore(root, detected) {
1931
- const ignorePath = join5(root, ".claudeignore");
2161
+ const ignorePath = join6(root, ".claudeignore");
1932
2162
  try {
1933
2163
  await access4(ignorePath);
1934
2164
  return false;
@@ -1940,7 +2170,7 @@ async function createClaudeignore(root, detected) {
1940
2170
  return true;
1941
2171
  }
1942
2172
  async function createStarterRules(root) {
1943
- const rulesDir = join5(root, ".claude", "rules");
2173
+ const rulesDir = join6(root, ".claude", "rules");
1944
2174
  try {
1945
2175
  await access4(rulesDir);
1946
2176
  return false;
@@ -1948,7 +2178,7 @@ async function createStarterRules(root) {
1948
2178
  }
1949
2179
  await mkdir2(rulesDir, { recursive: true });
1950
2180
  await writeFile2(
1951
- join5(rulesDir, "conventions.md"),
2181
+ join6(rulesDir, "conventions.md"),
1952
2182
  `# Project Conventions
1953
2183
 
1954
2184
  - Use conventional commits (feat:, fix:, docs:, refactor:, test:, chore:)
@@ -1960,46 +2190,12 @@ async function createStarterRules(root) {
1960
2190
  log.success("Created .claude/rules/conventions.md with starter rules");
1961
2191
  return true;
1962
2192
  }
1963
- async function disableAutoMemory(root, placement) {
1964
- const read = placement === "local" ? readSettingsLocalJson : readSettingsJson;
1965
- const write = placement === "local" ? writeSettingsLocalJson : writeSettingsJson;
1966
- const settings = await read(root);
1967
- if (settings.autoMemoryEnabled === false) return false;
1968
- settings.autoMemoryEnabled = false;
1969
- await write(root, settings);
1970
- const target = placement === "local" ? "settings.local.json" : "settings.json";
1971
- log.success(`Set autoMemoryEnabled: false in ${target}`);
1972
- return true;
1973
- }
1974
- async function addMemoryToolPermissions(root, placement) {
1975
- const read = placement === "local" ? readSettingsLocalJson : readSettingsJson;
1976
- const write = placement === "local" ? writeSettingsLocalJson : writeSettingsJson;
1977
- const settings = await read(root);
1978
- const permissions = settings.permissions ?? {};
1979
- const allow = permissions.allow ?? [];
1980
- const tools = [
1981
- "mcp__agentic-memory__memory_store",
1982
- "mcp__agentic-memory__memory_search",
1983
- "mcp__agentic-memory__memory_recent",
1984
- "mcp__agentic-memory__memory_forget",
1985
- "mcp__agentic-memory__memory_relate",
1986
- "mcp__agentic-memory__memory_stats",
1987
- "mcp__agentic-memory__memory_update"
1988
- ];
1989
- const missing = tools.filter((t) => !allow.includes(t));
1990
- if (missing.length === 0) return false;
1991
- settings.permissions = { ...permissions, allow: [...allow, ...missing] };
1992
- await write(root, settings);
1993
- const target = placement === "local" ? "settings.local.json" : "settings.json";
1994
- log.success(`Added agentic-memory MCP tool permissions to ${target}`);
1995
- return true;
1996
- }
1997
2193
  async function createEnhanceSkill(root) {
1998
- const skillDir = join5(root, ".claude", "skills", "lp-enhance");
1999
- const skillPath = join5(skillDir, "SKILL.md");
2000
- const globalPath = join5(homedir3(), ".claude", "skills", "lp-enhance", "SKILL.md");
2001
- const legacyProject = join5(root, ".claude", "commands", "lp-enhance.md");
2002
- const legacyGlobal = join5(homedir3(), ".claude", "commands", "lp-enhance.md");
2194
+ const skillDir = join6(root, ".claude", "skills", "lp-enhance");
2195
+ const skillPath = join6(skillDir, "SKILL.md");
2196
+ const globalPath = join6(homedir3(), ".claude", "skills", "lp-enhance", "SKILL.md");
2197
+ const legacyProject = join6(root, ".claude", "commands", "lp-enhance.md");
2198
+ const legacyGlobal = join6(homedir3(), ".claude", "commands", "lp-enhance.md");
2003
2199
  if (await fileExists(skillPath) || await fileExists(globalPath) || await fileExists(legacyProject) || await fileExists(legacyGlobal)) return false;
2004
2200
  await mkdir2(skillDir, { recursive: true });
2005
2201
  await writeFile2(skillPath, generateEnhanceSkill());
@@ -2007,39 +2203,18 @@ async function createEnhanceSkill(root) {
2007
2203
  return true;
2008
2204
  }
2009
2205
  async function updateEnhanceSkill(root) {
2010
- const projectPath = join5(root, ".claude", "skills", "lp-enhance", "SKILL.md");
2011
- const globalPath = join5(homedir3(), ".claude", "skills", "lp-enhance", "SKILL.md");
2206
+ const projectPath = join6(root, ".claude", "skills", "lp-enhance", "SKILL.md");
2207
+ const globalPath = join6(homedir3(), ".claude", "skills", "lp-enhance", "SKILL.md");
2012
2208
  const targetPath = await fileExists(projectPath) ? projectPath : await fileExists(globalPath) ? globalPath : null;
2013
2209
  if (!targetPath) return false;
2014
2210
  await writeFile2(targetPath, generateEnhanceSkill());
2015
2211
  log.success("Updated /lp-enhance skill to latest version");
2016
2212
  return true;
2017
2213
  }
2018
- async function removeStaleStopHook(root) {
2019
- const settings = await readSettingsJson(root);
2020
- const hooks = settings.hooks;
2021
- if (!hooks?.Stop) return false;
2022
- const stopHooks = hooks.Stop;
2023
- const filtered = stopHooks.filter((h) => {
2024
- const innerHooks = h.hooks;
2025
- return !innerHooks?.some(
2026
- (ih) => typeof ih.command === "string" && ih.command.includes("memory extract")
2027
- );
2028
- });
2029
- if (filtered.length === stopHooks.length) return false;
2030
- if (filtered.length === 0) {
2031
- delete hooks.Stop;
2032
- } else {
2033
- hooks.Stop = filtered;
2034
- }
2035
- await writeSettingsJson(root, settings);
2036
- log.success("Removed deprecated Stop hook (memory extract)");
2037
- return true;
2038
- }
2039
2214
 
2040
2215
  // src/commands/doctor/watcher.ts
2041
2216
  import { readdir as readdir2, stat } from "fs/promises";
2042
- import { join as join6 } from "path";
2217
+ import { join as join7 } from "path";
2043
2218
  async function watchConfig(projectRoot) {
2044
2219
  await runAndDisplay(projectRoot);
2045
2220
  log.blank();
@@ -2062,16 +2237,16 @@ async function watchConfig(projectRoot) {
2062
2237
  }
2063
2238
  async function getFileSnapshot(projectRoot) {
2064
2239
  const files = [
2065
- join6(projectRoot, "CLAUDE.md"),
2066
- join6(projectRoot, ".claudeignore")
2240
+ join7(projectRoot, "CLAUDE.md"),
2241
+ join7(projectRoot, ".claudeignore")
2067
2242
  ];
2068
- const claudeDir = join6(projectRoot, ".claude");
2243
+ const claudeDir = join7(projectRoot, ".claude");
2069
2244
  try {
2070
2245
  const entries = await readdir2(claudeDir, { withFileTypes: true, recursive: true });
2071
2246
  for (const entry of entries) {
2072
2247
  if (entry.isFile()) {
2073
2248
  const parentPath = entry.parentPath ?? claudeDir;
2074
- files.push(join6(parentPath, entry.name));
2249
+ files.push(join7(parentPath, entry.name));
2075
2250
  }
2076
2251
  }
2077
2252
  } catch {
@@ -2216,11 +2391,11 @@ import { select as select2 } from "@inquirer/prompts";
2216
2391
  import ora from "ora";
2217
2392
  import chalk2 from "chalk";
2218
2393
  import { mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
2219
- import { join as join9 } from "path";
2394
+ import { join as join10 } from "path";
2220
2395
 
2221
2396
  // src/commands/eval/loader.ts
2222
- import { readFile as readFile5, readdir as readdir3, access as access5 } from "fs/promises";
2223
- import { join as join7, resolve as resolve2, dirname as dirname2 } from "path";
2397
+ import { readFile as readFile6, readdir as readdir3, access as access5 } from "fs/promises";
2398
+ import { join as join8, resolve as resolve2, dirname as dirname2 } from "path";
2224
2399
  import { fileURLToPath } from "url";
2225
2400
  import { parse as parseYaml } from "yaml";
2226
2401
 
@@ -2343,14 +2518,14 @@ async function dirExists(path) {
2343
2518
  async function loadScenarios(options) {
2344
2519
  const { suite, customPath } = options;
2345
2520
  const scenarioDir = customPath ? resolve2(customPath) : await findScenariosDir();
2346
- const dirs = suite ? [join7(scenarioDir, suite)] : await getSubdirectories(scenarioDir);
2521
+ const dirs = suite ? [join8(scenarioDir, suite)] : await getSubdirectories(scenarioDir);
2347
2522
  const allDirs = [scenarioDir, ...dirs];
2348
2523
  const scenarios = [];
2349
2524
  for (const dir of allDirs) {
2350
2525
  const files = await listYamlFiles(dir);
2351
2526
  for (const file of files) {
2352
2527
  try {
2353
- const content = await readFile5(file, "utf-8");
2528
+ const content = await readFile6(file, "utf-8");
2354
2529
  const raw = parseYaml(content);
2355
2530
  const scenario = validateScenario(raw, file);
2356
2531
  scenarios.push(scenario);
@@ -2365,7 +2540,7 @@ async function loadScenarios(options) {
2365
2540
  async function getSubdirectories(dir) {
2366
2541
  try {
2367
2542
  const entries = await readdir3(dir, { withFileTypes: true });
2368
- return entries.filter((e) => e.isDirectory()).map((e) => join7(dir, e.name));
2543
+ return entries.filter((e) => e.isDirectory()).map((e) => join8(dir, e.name));
2369
2544
  } catch {
2370
2545
  return [];
2371
2546
  }
@@ -2373,22 +2548,22 @@ async function getSubdirectories(dir) {
2373
2548
  async function listYamlFiles(dir) {
2374
2549
  try {
2375
2550
  const entries = await readdir3(dir, { withFileTypes: true });
2376
- return entries.filter((e) => e.isFile() && (e.name.endsWith(".yaml") || e.name.endsWith(".yml"))).map((e) => join7(dir, e.name));
2551
+ return entries.filter((e) => e.isFile() && (e.name.endsWith(".yaml") || e.name.endsWith(".yml"))).map((e) => join8(dir, e.name));
2377
2552
  } catch {
2378
2553
  return [];
2379
2554
  }
2380
2555
  }
2381
2556
 
2382
2557
  // src/commands/eval/runner.ts
2383
- import { mkdir as mkdir3, writeFile as writeFile3, readFile as readFile6, readdir as readdir4, rm, cp } from "fs/promises";
2384
- import { join as join8, dirname as dirname3 } from "path";
2558
+ import { mkdir as mkdir3, writeFile as writeFile3, readFile as readFile7, readdir as readdir4, rm, cp } from "fs/promises";
2559
+ import { join as join9, dirname as dirname3 } from "path";
2385
2560
  import { tmpdir } from "os";
2386
2561
  import { randomUUID } from "crypto";
2387
2562
  import { execFile } from "child_process";
2388
2563
  import { promisify } from "util";
2389
2564
  var exec = promisify(execFile);
2390
2565
  async function runScenario(scenario, options) {
2391
- const sandboxDir = join8(tmpdir(), `claude-eval-${randomUUID()}`);
2566
+ const sandboxDir = join9(tmpdir(), `claude-eval-${randomUUID()}`);
2392
2567
  try {
2393
2568
  await setupSandbox(sandboxDir, scenario, options.projectRoot);
2394
2569
  await runClaudeInSandbox(sandboxDir, scenario.prompt, options.timeout, options.model);
@@ -2414,14 +2589,14 @@ async function runScenarioWithRetries(scenario, options) {
2414
2589
  async function setupSandbox(sandboxDir, scenario, projectRoot) {
2415
2590
  await mkdir3(sandboxDir, { recursive: true });
2416
2591
  for (const file of scenario.setup.files) {
2417
- const filePath = join8(sandboxDir, file.path);
2592
+ const filePath = join9(sandboxDir, file.path);
2418
2593
  await mkdir3(dirname3(filePath), { recursive: true });
2419
2594
  await writeFile3(filePath, file.content);
2420
2595
  }
2421
2596
  await copyProjectConfig(sandboxDir, projectRoot);
2422
2597
  if (scenario.setup.instructions) {
2423
2598
  await writeFile3(
2424
- join8(sandboxDir, "CLAUDE.md"),
2599
+ join9(sandboxDir, "CLAUDE.md"),
2425
2600
  `# Eval Scenario
2426
2601
 
2427
2602
  ${scenario.setup.instructions}
@@ -2442,20 +2617,20 @@ ${scenario.setup.instructions}
2442
2617
  ], { cwd: sandboxDir });
2443
2618
  }
2444
2619
  async function copyProjectConfig(sandboxDir, projectRoot) {
2445
- const claudeDir = join8(projectRoot, ".claude");
2446
- const sandboxClaudeDir = join8(sandboxDir, ".claude");
2447
- const settingsPath = join8(claudeDir, "settings.json");
2620
+ const claudeDir = join9(projectRoot, ".claude");
2621
+ const sandboxClaudeDir = join9(sandboxDir, ".claude");
2622
+ const settingsPath = join9(claudeDir, "settings.json");
2448
2623
  if (await fileExists(settingsPath)) {
2449
2624
  await mkdir3(sandboxClaudeDir, { recursive: true });
2450
- await cp(settingsPath, join8(sandboxClaudeDir, "settings.json"));
2625
+ await cp(settingsPath, join9(sandboxClaudeDir, "settings.json"));
2451
2626
  }
2452
- const rulesDir = join8(claudeDir, "rules");
2627
+ const rulesDir = join9(claudeDir, "rules");
2453
2628
  if (await fileExists(rulesDir)) {
2454
- await cp(rulesDir, join8(sandboxClaudeDir, "rules"), { recursive: true });
2629
+ await cp(rulesDir, join9(sandboxClaudeDir, "rules"), { recursive: true });
2455
2630
  }
2456
- const ignorePath = join8(projectRoot, ".claudeignore");
2631
+ const ignorePath = join9(projectRoot, ".claudeignore");
2457
2632
  if (await fileExists(ignorePath)) {
2458
- await cp(ignorePath, join8(sandboxDir, ".claudeignore"));
2633
+ await cp(ignorePath, join9(sandboxDir, ".claudeignore"));
2459
2634
  }
2460
2635
  }
2461
2636
  async function runClaudeInSandbox(cwd, prompt, timeout, model) {
@@ -2549,7 +2724,7 @@ async function evaluateSingleCheck(check, sandboxDir) {
2549
2724
  async function checkGrep(check, sandboxDir) {
2550
2725
  if (!check.pattern) return false;
2551
2726
  try {
2552
- const content = await readFile6(join8(sandboxDir, check.target), "utf-8");
2727
+ const content = await readFile7(join9(sandboxDir, check.target), "utf-8");
2553
2728
  let found;
2554
2729
  try {
2555
2730
  found = new RegExp(check.pattern).test(content);
@@ -2563,7 +2738,7 @@ async function checkGrep(check, sandboxDir) {
2563
2738
  }
2564
2739
  async function checkFilePresence(check, sandboxDir) {
2565
2740
  try {
2566
- await readFile6(join8(sandboxDir, check.target));
2741
+ await readFile7(join9(sandboxDir, check.target));
2567
2742
  return check.expect === "present";
2568
2743
  } catch {
2569
2744
  return check.expect === "absent";
@@ -2572,9 +2747,9 @@ async function checkFilePresence(check, sandboxDir) {
2572
2747
  async function checkMaxLines(check, sandboxDir) {
2573
2748
  const maxLines = parseInt(check.pattern ?? "800", 10);
2574
2749
  try {
2575
- const files = await listAllFiles(join8(sandboxDir, check.target));
2750
+ const files = await listAllFiles(join9(sandboxDir, check.target));
2576
2751
  for (const file of files) {
2577
- const content = await readFile6(file, "utf-8");
2752
+ const content = await readFile7(file, "utf-8");
2578
2753
  if (content.split("\n").length > maxLines) {
2579
2754
  return check.expect === "absent";
2580
2755
  }
@@ -2589,7 +2764,7 @@ async function listAllFiles(dir) {
2589
2764
  try {
2590
2765
  const entries = await readdir4(dir, { withFileTypes: true });
2591
2766
  for (const entry of entries) {
2592
- const fullPath = join8(dir, entry.name);
2767
+ const fullPath = join9(dir, entry.name);
2593
2768
  if (entry.isDirectory()) {
2594
2769
  results.push(...await listAllFiles(fullPath));
2595
2770
  } else {
@@ -2775,10 +2950,10 @@ async function saveEvalReport(results, projectRoot, suite, model) {
2775
2950
  lines.push("");
2776
2951
  }
2777
2952
  }
2778
- const evalDir = join9(projectRoot, ".claude", "eval");
2953
+ const evalDir = join10(projectRoot, ".claude", "eval");
2779
2954
  await mkdir4(evalDir, { recursive: true });
2780
2955
  const filename = `eval-${suite ?? "all"}-${timestamp}.md`;
2781
- await writeFile4(join9(evalDir, filename), lines.join("\n"));
2956
+ await writeFile4(join10(evalDir, filename), lines.join("\n"));
2782
2957
  log.success(`Report saved to .claude/eval/${filename}`);
2783
2958
  }
2784
2959
  async function checkClaudeCli() {
@@ -2795,12 +2970,12 @@ async function checkClaudeCli() {
2795
2970
 
2796
2971
  // src/commands/memory/index.ts
2797
2972
  import { readFileSync } from "fs";
2798
- import { join as join10 } from "path";
2973
+ import { join as join11 } from "path";
2799
2974
  import { Command as Command4 } from "commander";
2800
2975
  import { confirm as confirm2 } from "@inquirer/prompts";
2801
2976
  function isMemoryInstalled() {
2802
2977
  const cwd = process.cwd();
2803
- return hasMemoryHook(join10(cwd, ".claude", "settings.json")) || hasMemoryHook(join10(cwd, ".claude", "settings.local.json"));
2978
+ return hasMemoryHook(join11(cwd, ".claude", "settings.json")) || hasMemoryHook(join11(cwd, ".claude", "settings.local.json"));
2804
2979
  }
2805
2980
  function hasMemoryHook(path) {
2806
2981
  try {
@@ -2825,12 +3000,12 @@ function createMemoryCommand() {
2825
3000
  }
2826
3001
  const { requireMemoryDeps } = await import("./require-deps-NKRCPVAO.js");
2827
3002
  await requireMemoryDeps();
2828
- const { startTui } = await import("./tui-GUCKDWDM.js");
3003
+ const { startTui } = await import("./tui-XIYOOUP6.js");
2829
3004
  await startTui();
2830
3005
  return;
2831
3006
  }
2832
3007
  if (!isMemoryInstalled()) {
2833
- const { detectExistingSetup } = await import("./install-UXO5BISS.js");
3008
+ const { detectExistingSetup } = await import("./install-MVATZUXZ.js");
2834
3009
  const existing = detectExistingSetup(process.cwd());
2835
3010
  if (existing) {
2836
3011
  const location = existing === "local" ? ".claude/CLAUDE.md + settings.local.json" : "CLAUDE.md + settings.json";
@@ -2856,18 +3031,18 @@ function createMemoryCommand() {
2856
3031
  log.info("Skipped.");
2857
3032
  return;
2858
3033
  }
2859
- const { runInstall } = await import("./install-UXO5BISS.js");
3034
+ const { runInstall } = await import("./install-MVATZUXZ.js");
2860
3035
  await runInstall({});
2861
3036
  } else {
2862
3037
  const { requireMemoryDeps } = await import("./require-deps-NKRCPVAO.js");
2863
3038
  await requireMemoryDeps();
2864
- const { runStats } = await import("./stats-P7S3FTCQ.js");
3039
+ const { runStats } = await import("./stats-QUBHHPV7.js");
2865
3040
  await runStats({});
2866
3041
  }
2867
3042
  });
2868
3043
  memory.addCommand(
2869
3044
  new Command4("context").description("Load session context (hook handler)").option("--json", "JSON output").action(async (opts) => {
2870
- const { runContext } = await import("./context-XPXKLF5V.js");
3045
+ const { runContext } = await import("./context-CWJUUTTU.js");
2871
3046
  await runContext(opts);
2872
3047
  }).helpCommand(false),
2873
3048
  { hidden: true }
@@ -2881,13 +3056,13 @@ function createMemoryCommand() {
2881
3056
  );
2882
3057
  memory.addCommand(
2883
3058
  new Command4("push").description("Push current project's memories to GitHub Gist").option("--all", "Push all projects").option("-y, --yes", "Skip confirmation prompt").action(async (opts) => {
2884
- const { runPush } = await import("./push-BGN3DVV5.js");
3059
+ const { runPush } = await import("./push-74XC5CUK.js");
2885
3060
  await runPush(opts);
2886
3061
  })
2887
3062
  );
2888
3063
  memory.addCommand(
2889
3064
  new Command4("pull").description("Pull current project's memories from GitHub Gist").option("--all", "Pull all projects").action(async (opts) => {
2890
- const { runPull } = await import("./pull-3O4B6EL2.js");
3065
+ const { runPull } = await import("./pull-YOESZ3UC.js");
2891
3066
  await runPull(opts);
2892
3067
  })
2893
3068
  );
@@ -2895,8 +3070,8 @@ function createMemoryCommand() {
2895
3070
  }
2896
3071
 
2897
3072
  // src/cli.ts
2898
- var program = new Command5().name("claude-launchpad").description("CLI toolkit that makes Claude Code setups measurably good").version("0.15.2", "-v, --version").action(async () => {
2899
- const hasConfig = await fileExists(join11(process.cwd(), "CLAUDE.md")) || await fileExists(join11(process.cwd(), ".claude", "settings.json"));
3073
+ var program = new Command5().name("claude-launchpad").description("CLI toolkit that makes Claude Code setups measurably good").version("0.16.0", "-v, --version").action(async () => {
3074
+ const hasConfig = await fileExists(join12(process.cwd(), "CLAUDE.md")) || await fileExists(join12(process.cwd(), ".claude", "settings.json"));
2900
3075
  if (hasConfig) {
2901
3076
  await program.commands.find((c) => c.name() === "doctor")?.parseAsync([], { from: "user" });
2902
3077
  } else {