claude-launchpad 0.14.3 → 0.15.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/README.md CHANGED
@@ -56,6 +56,8 @@ Scores your config, auto-repairs everything it can.
56
56
 
57
57
  ## The Three-File System
58
58
 
59
+ Without structure, CLAUDE.md becomes a dumping ground. Future ideas bury active guidance. Sprint notes push conventions off-screen. Past the ~200 line budget, Claude starts ignoring rules at the bottom. The three-file split keeps each concern where it belongs:
60
+
59
61
  | File | Purpose | Example |
60
62
  |---|---|---|
61
63
  | `CLAUDE.md` | What Claude needs to know | Stack, commands, conventions, guardrails |
@@ -201,6 +203,13 @@ claude-launchpad memory
201
203
 
202
204
  If memory is not installed, it runs interactive setup. If installed, it shows stats. Requires native deps first: `npm install better-sqlite3 sqlite-vec`.
203
205
 
206
+ During setup, you choose where memory config lives:
207
+
208
+ - **Shared** (default) — config goes to `CLAUDE.md` + `settings.json` (committed, team sees it)
209
+ - **Local** — config goes to `.claude/CLAUDE.md` + `settings.local.json` (gitignored, only you)
210
+
211
+ Use "local" when co-devs have different memory setups (e.g. you use agentic-memory, they use built-in). Your choice is persisted so `doctor --fix` won't re-ask.
212
+
204
213
  Every session, Claude loads what it needs to know and stores new knowledge as it works. Stale facts fade on their own. Knowledge Claude actually uses gets reinforced. Each project has its own isolated memory, and you can sync it across machines via private GitHub Gist.
205
214
 
206
215
  Browse everything with `--dashboard` -- a terminal UI with vim navigation, filtering, and search.
@@ -248,10 +257,10 @@ New to Claude Code? Here's what the terms mean.
248
257
  | **CLAUDE.md** | A markdown file in your project root that tells Claude how to work on your code. Think of it as instructions for your AI pair programmer. [Official docs](https://docs.anthropic.com/en/docs/claude-code/memory#claudemd) |
249
258
  | **TASKS.md** | Sprint tracker and session log. Claude reads this at session start to pick up where you left off. |
250
259
  | **BACKLOG.md** | Where deferred features live. Priority tiers (P0/P1/P2) keep future ideas organized without cluttering TASKS.md. |
251
- | **Hooks** | Shell commands that run automatically when Claude does something. Example: auto-format after edits, block reading `.env`. They live in `.claude/settings.json`. |
260
+ | **Hooks** | Shell commands that run automatically when Claude does something. CLAUDE.md rules are ~80% reliable. Hooks are 100% enforced. A SessionStart hook that runs `cat TASKS.md` means Claude sees your task list at every session start. |
252
261
  | **Instruction budget** | CLAUDE.md has a soft limit of ~200 actionable lines. Past that, Claude starts ignoring rules at the bottom. Doctor counts your lines and warns you. |
253
262
  | **Rules** | Extra markdown files in `.claude/rules/` that Claude reads alongside CLAUDE.md. Use them to offload detailed conventions so CLAUDE.md stays under budget. |
254
- | **Compaction** | When a conversation gets too long, Claude compresses older messages. This can lose context. A PostCompact hook re-injects critical files (like TASKS.md) after compaction. |
263
+ | **Compaction** | When a conversation gets too long, Claude compresses older messages. Without a PostCompact hook, Claude loses track of your sprint and session context mid-work. The hook re-injects TASKS.md after compaction so Claude stays on track. |
255
264
  | **MCP Servers** | External tools Claude can connect to (databases, APIs, docs). Configured in `.claude/settings.json`. Most projects don't need them. |
256
265
  | **.claudeignore** | Like `.gitignore` but for Claude. Tells Claude which files to skip so it doesn't waste time reading noise. |
257
266
 
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/lib/settings.ts
4
+ import { readFile, writeFile, mkdir } from "fs/promises";
5
+ import { join } from "path";
6
+ async function readSettingsJson(root) {
7
+ const path = join(root, ".claude", "settings.json");
8
+ try {
9
+ const content = await readFile(path, "utf-8");
10
+ return JSON.parse(content);
11
+ } catch {
12
+ return {};
13
+ }
14
+ }
15
+ async function writeSettingsJson(root, settings) {
16
+ const dir = join(root, ".claude");
17
+ await mkdir(dir, { recursive: true });
18
+ await writeFile(join(dir, "settings.json"), JSON.stringify(settings, null, 2) + "\n");
19
+ }
20
+ async function readSettingsLocalJson(root) {
21
+ const path = join(root, ".claude", "settings.local.json");
22
+ try {
23
+ const content = await readFile(path, "utf-8");
24
+ return JSON.parse(content);
25
+ } catch {
26
+ return {};
27
+ }
28
+ }
29
+ async function writeSettingsLocalJson(root, settings) {
30
+ const dir = join(root, ".claude");
31
+ await mkdir(dir, { recursive: true });
32
+ await writeFile(join(dir, "settings.local.json"), JSON.stringify(settings, null, 2) + "\n");
33
+ }
34
+
35
+ // src/lib/memory-placement.ts
36
+ import { select } from "@inquirer/prompts";
37
+ async function getMemoryPlacement(root, skipPrompt = false) {
38
+ const local = await readSettingsLocalJson(root);
39
+ const persisted = local.memoryPlacement;
40
+ if (persisted === "shared" || persisted === "local") {
41
+ return persisted;
42
+ }
43
+ if (skipPrompt) return "shared";
44
+ const choice = await select({
45
+ message: "Where should memory config go?",
46
+ choices: [
47
+ { value: "shared", name: "Shared (team sees it) \u2014 CLAUDE.md + settings.json" },
48
+ { value: "local", name: "Local (only you) \u2014 .claude/CLAUDE.md + settings.local.json" }
49
+ ]
50
+ });
51
+ await writeSettingsLocalJson(root, { ...local, memoryPlacement: choice });
52
+ return choice;
53
+ }
54
+
55
+ export {
56
+ readSettingsJson,
57
+ writeSettingsJson,
58
+ readSettingsLocalJson,
59
+ writeSettingsLocalJson,
60
+ getMemoryPlacement
61
+ };
62
+ //# sourceMappingURL=chunk-KOSJII4R.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/settings.ts","../src/lib/memory-placement.ts"],"sourcesContent":["import { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nexport async function readSettingsJson(root: string): Promise<Record<string, unknown>> {\n const path = join(root, \".claude\", \"settings.json\");\n try {\n const content = await readFile(path, \"utf-8\");\n return JSON.parse(content) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nexport async function writeSettingsJson(root: string, settings: Record<string, unknown>): Promise<void> {\n const dir = join(root, \".claude\");\n await mkdir(dir, { recursive: true });\n await writeFile(join(dir, \"settings.json\"), JSON.stringify(settings, null, 2) + \"\\n\");\n}\n\nexport async function readSettingsLocalJson(root: string): Promise<Record<string, unknown>> {\n const path = join(root, \".claude\", \"settings.local.json\");\n try {\n const content = await readFile(path, \"utf-8\");\n return JSON.parse(content) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nexport async function writeSettingsLocalJson(root: string, settings: Record<string, unknown>): Promise<void> {\n const dir = join(root, \".claude\");\n await mkdir(dir, { recursive: true });\n await writeFile(join(dir, \"settings.local.json\"), JSON.stringify(settings, null, 2) + \"\\n\");\n}\n","import { select } from \"@inquirer/prompts\";\nimport { readSettingsLocalJson, writeSettingsLocalJson } from \"./settings.js\";\nimport type { MemoryPlacement } from \"../types/index.js\";\n\nexport async function getMemoryPlacement(root: string, skipPrompt = false): Promise<MemoryPlacement> {\n const local = await readSettingsLocalJson(root);\n const persisted = local.memoryPlacement;\n if (persisted === \"shared\" || persisted === \"local\") {\n return persisted;\n }\n\n if (skipPrompt) return \"shared\";\n\n const choice = await select<MemoryPlacement>({\n message: \"Where should memory config go?\",\n choices: [\n { value: \"shared\", name: \"Shared (team sees it) — CLAUDE.md + settings.json\" },\n { value: \"local\", name: \"Local (only you) — .claude/CLAUDE.md + settings.local.json\" },\n ],\n });\n\n await writeSettingsLocalJson(root, { ...local, memoryPlacement: choice });\n return choice;\n}\n"],"mappings":";;;AAAA,SAAS,UAAU,WAAW,aAAa;AAC3C,SAAS,YAAY;AAErB,eAAsB,iBAAiB,MAAgD;AACrF,QAAM,OAAO,KAAK,MAAM,WAAW,eAAe;AAClD,MAAI;AACF,UAAM,UAAU,MAAM,SAAS,MAAM,OAAO;AAC5C,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,eAAsB,kBAAkB,MAAc,UAAkD;AACtG,QAAM,MAAM,KAAK,MAAM,SAAS;AAChC,QAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC,QAAM,UAAU,KAAK,KAAK,eAAe,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,IAAI;AACtF;AAEA,eAAsB,sBAAsB,MAAgD;AAC1F,QAAM,OAAO,KAAK,MAAM,WAAW,qBAAqB;AACxD,MAAI;AACF,UAAM,UAAU,MAAM,SAAS,MAAM,OAAO;AAC5C,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,eAAsB,uBAAuB,MAAc,UAAkD;AAC3G,QAAM,MAAM,KAAK,MAAM,SAAS;AAChC,QAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC,QAAM,UAAU,KAAK,KAAK,qBAAqB,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,IAAI;AAC5F;;;ACjCA,SAAS,cAAc;AAIvB,eAAsB,mBAAmB,MAAc,aAAa,OAAiC;AACnG,QAAM,QAAQ,MAAM,sBAAsB,IAAI;AAC9C,QAAM,YAAY,MAAM;AACxB,MAAI,cAAc,YAAY,cAAc,SAAS;AACnD,WAAO;AAAA,EACT;AAEA,MAAI,WAAY,QAAO;AAEvB,QAAM,SAAS,MAAM,OAAwB;AAAA,IAC3C,SAAS;AAAA,IACT,SAAS;AAAA,MACP,EAAE,OAAO,UAAU,MAAM,yDAAoD;AAAA,MAC7E,EAAE,OAAO,SAAS,MAAM,kEAA6D;AAAA,IACvF;AAAA,EACF,CAAC;AAED,QAAM,uBAAuB,MAAM,EAAE,GAAG,OAAO,iBAAiB,OAAO,CAAC;AACxE,SAAO;AACT;","names":[]}
package/dist/cli.js CHANGED
@@ -1,8 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ getMemoryPlacement,
3
4
  readSettingsJson,
4
- writeSettingsJson
5
- } from "./chunk-CSLWJEGD.js";
5
+ readSettingsLocalJson,
6
+ writeSettingsJson,
7
+ writeSettingsLocalJson
8
+ } from "./chunk-KOSJII4R.js";
6
9
  import {
7
10
  log,
8
11
  printBanner,
@@ -284,30 +287,17 @@ When all tasks in the current sprint are complete, do a quick quality check befo
284
287
  function generateTasksMd(options) {
285
288
  return `# ${options.name} \u2014 Task Tracker
286
289
 
287
- > Claude: Read this at session start. Keep this file SHORT \u2014 only current state matters.
288
- > Rules: (1) Only show current + next sprint tasks. (2) Completed sprints get one summary line. (3) Session log: max 3 lines per session, keep only last 3 sessions. (4) Target: under 80 lines total.
290
+ > Claude: Read at session start. Keep SHORT \u2014 only current state matters.
291
+ > Completed sprints: one-liner. Session log: 3 lines max, last 3 sessions. Under 80 lines.
289
292
 
290
293
  ## Completed Sprints
291
294
 
292
- ## Current Sprint: Sprint 1 \u2014 Setup
293
-
294
- ### In Progress
295
-
296
- ### To Do
295
+ ## Current: Sprint 1 \u2014 Setup
297
296
  - [ ] Project scaffolding and environment setup
298
297
  - [ ] Core feature implementation
299
298
  - [ ] Test infrastructure
300
299
 
301
- ### Done
302
-
303
- ## Deferred
304
- <!-- Known issues not urgent enough for the current sprint. Include date and reason. -->
305
-
306
- ## Next Sprint: Sprint 2 \u2014 Core Features
307
- - [ ] ...
308
-
309
300
  ## Session Log
310
- <!-- Keep last 3 sessions only. Max 3 lines each. -->
311
301
  `;
312
302
  }
313
303
 
@@ -824,6 +814,7 @@ async function scaffold(root, options, detected, skipPrompts) {
824
814
  if (!hasClaudeGitignore) {
825
815
  writes.push(writeFile(claudeGitignorePath, [
826
816
  "# Local-only Claude Code files (never commit these)",
817
+ "CLAUDE.md",
827
818
  "settings.local.json",
828
819
  "plans/",
829
820
  "memory/",
@@ -942,13 +933,16 @@ import { join as join3, resolve } from "path";
942
933
  var CLAUDE_MD = "CLAUDE.md";
943
934
  var CLAUDE_DIR = ".claude";
944
935
  var SETTINGS_FILE = "settings.json";
936
+ var SETTINGS_LOCAL_FILE = "settings.local.json";
945
937
  var RULES_DIR = "rules";
946
938
  async function parseClaudeConfig(projectRoot) {
947
939
  const root = resolve(projectRoot);
948
940
  const claudeDir = join3(root, CLAUDE_DIR);
949
- const [claudeMd, settings, hooks, rules, mcpServers, skills, claudeignore] = await Promise.all([
941
+ const [claudeMd, localClaudeMd, settings, localSettings, hooks, rules, mcpServers, skills, claudeignore] = await Promise.all([
950
942
  readClaudeMd(root),
943
+ readFileOrNull(join3(claudeDir, CLAUDE_MD)),
951
944
  readSettings(claudeDir),
945
+ readSettingsFromFile(claudeDir, SETTINGS_LOCAL_FILE),
952
946
  readHooks(claudeDir),
953
947
  readRules(claudeDir),
954
948
  readMcpServers(claudeDir),
@@ -962,6 +956,8 @@ async function parseClaudeConfig(projectRoot) {
962
956
  claudeMdInstructionCount: instructionCount,
963
957
  settingsPath: settings !== null ? join3(claudeDir, SETTINGS_FILE) : null,
964
958
  settings,
959
+ localClaudeMdContent: localClaudeMd,
960
+ localSettings,
965
961
  hooks,
966
962
  rules,
967
963
  mcpServers,
@@ -987,7 +983,10 @@ function countInstructions(content) {
987
983
  return count;
988
984
  }
989
985
  async function readSettings(claudeDir) {
990
- const raw = await readFileOrNull(join3(claudeDir, SETTINGS_FILE));
986
+ return readSettingsFromFile(claudeDir, SETTINGS_FILE);
987
+ }
988
+ async function readSettingsFromFile(claudeDir, filename) {
989
+ const raw = await readFileOrNull(join3(claudeDir, filename));
991
990
  if (raw === null) return null;
992
991
  try {
993
992
  return JSON.parse(raw);
@@ -1567,16 +1566,16 @@ async function analyzeMemory(config) {
1567
1566
  fix: "Run `doctor --fix` to remove the stale Stop hook"
1568
1567
  });
1569
1568
  }
1570
- const autoMemoryDisabled = config.settings?.autoMemoryEnabled === false;
1569
+ const autoMemoryDisabled = config.settings?.autoMemoryEnabled === false || config.localSettings?.autoMemoryEnabled === false;
1571
1570
  if (!autoMemoryDisabled) {
1572
1571
  issues.push({
1573
1572
  analyzer: "Memory",
1574
1573
  severity: "medium",
1575
1574
  message: "autoMemoryEnabled not disabled \u2014 built-in memory may conflict with agentic-memory",
1576
- fix: "Set autoMemoryEnabled: false in .claude/settings.json"
1575
+ fix: "Set autoMemoryEnabled: false in settings.json or settings.local.json"
1577
1576
  });
1578
1577
  }
1579
- const hasMemoryGuidance = config.claudeMdContent?.includes("agentic-memory") || config.claudeMdContent?.includes("## Memory");
1578
+ const hasMemoryGuidance = config.claudeMdContent?.includes("agentic-memory") || config.claudeMdContent?.includes("## Memory") || config.localClaudeMdContent?.includes("agentic-memory") || config.localClaudeMdContent?.includes("## Memory");
1580
1579
  if (!hasMemoryGuidance) {
1581
1580
  issues.push({
1582
1581
  analyzer: "Memory",
@@ -1586,7 +1585,11 @@ async function analyzeMemory(config) {
1586
1585
  });
1587
1586
  }
1588
1587
  const permissions = config.settings?.permissions ?? {};
1589
- const allowList = permissions.allow ?? [];
1588
+ const localPermissions = config.localSettings?.permissions ?? {};
1589
+ const allowList = [
1590
+ ...permissions.allow ?? [],
1591
+ ...localPermissions.allow ?? []
1592
+ ];
1590
1593
  const missingTools = MEMORY_MCP_TOOLS.filter((t) => !allowList.includes(t));
1591
1594
  if (missingTools.length > 0) {
1592
1595
  issues.push({
@@ -1639,9 +1642,10 @@ async function analyzeQuality(config) {
1639
1642
  return { name: "CLAUDE.md Quality", issues, score: 0 };
1640
1643
  }
1641
1644
  const sections = hasMemoryIndicators(config) ? [...BASE_SECTIONS, MEMORY_SECTION] : [...BASE_SECTIONS];
1645
+ const combinedContent = [content, config.localClaudeMdContent].filter(Boolean).join("\n");
1642
1646
  let sectionsFound = 0;
1643
1647
  for (const section of sections) {
1644
- if (section.pattern.test(content)) {
1648
+ if (section.pattern.test(combinedContent)) {
1645
1649
  sectionsFound++;
1646
1650
  } else {
1647
1651
  issues.push({
@@ -1695,10 +1699,12 @@ import { join as join5 } from "path";
1695
1699
  import { homedir as homedir3 } from "os";
1696
1700
  async function applyFixes(issues, projectRoot) {
1697
1701
  const detected = await detectProject(projectRoot);
1702
+ const hasMemoryIssues = issues.some((i) => i.analyzer === "Memory");
1703
+ const placement = hasMemoryIssues ? await getMemoryPlacement(projectRoot) : "shared";
1698
1704
  let fixed = 0;
1699
1705
  let skipped = 0;
1700
1706
  for (const issue of issues) {
1701
- const applied = await tryFix(issue, projectRoot, detected);
1707
+ const applied = await tryFix(issue, projectRoot, detected, placement);
1702
1708
  if (applied) {
1703
1709
  fixed++;
1704
1710
  } else {
@@ -1742,15 +1748,19 @@ var FIX_TABLE = [
1742
1748
  { analyzer: "Settings", match: "Deprecated includeCoAuthoredBy", fix: (root) => migrateAttribution(root) },
1743
1749
  { analyzer: "Hooks", match: "SessionStart", fix: (root) => addSessionStartHook(root) },
1744
1750
  { analyzer: "Memory", match: "Deprecated Stop hook", fix: (root) => removeStaleStopHook(root) },
1745
- { analyzer: "Memory", match: "autoMemoryEnabled not disabled", fix: (root) => disableAutoMemory(root) },
1746
- { analyzer: "Memory", match: "MCP tool permission", fix: (root) => addMemoryToolPermissions(root) },
1747
- { analyzer: "Memory", match: "CLAUDE.md missing memory guidance", fix: (root) => addClaudeMdSection(root, "## Memory", "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") }
1751
+ { analyzer: "Memory", match: "autoMemoryEnabled not disabled", fix: (root, _det, placement) => disableAutoMemory(root, placement) },
1752
+ { analyzer: "Memory", match: "MCP tool permission", fix: (root, _det, placement) => addMemoryToolPermissions(root, placement) },
1753
+ { analyzer: "Memory", match: "CLAUDE.md missing memory guidance", fix: (root, _det, placement) => {
1754
+ 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;
1756
+ return addClaudeMdSection(root, "## Memory", content, target);
1757
+ } }
1748
1758
  ];
1749
- async function tryFix(issue, root, detected) {
1759
+ async function tryFix(issue, root, detected, placement) {
1750
1760
  const entry = FIX_TABLE.find(
1751
1761
  (e) => e.analyzer === issue.analyzer && issue.message.includes(e.match)
1752
1762
  );
1753
- return entry ? entry.fix(root, detected) : false;
1763
+ return entry ? entry.fix(root, detected, placement) : false;
1754
1764
  }
1755
1765
  async function addHook(root, event, dedupKeyword, entry, successMsg) {
1756
1766
  const settings = await readSettingsJson(root);
@@ -1877,13 +1887,15 @@ async function addEnvToClaudeignore(root) {
1877
1887
  log.success("Added .env to .claudeignore");
1878
1888
  return true;
1879
1889
  }
1880
- async function addClaudeMdSection(root, heading, content) {
1881
- const claudeMdPath = join5(root, "CLAUDE.md");
1890
+ async function addClaudeMdSection(root, heading, content, targetPath) {
1891
+ const claudeMdPath = targetPath ?? join5(root, "CLAUDE.md");
1882
1892
  let existing;
1883
1893
  try {
1884
1894
  existing = await readFile4(claudeMdPath, "utf-8");
1885
1895
  } catch {
1886
- return false;
1896
+ if (!targetPath) return false;
1897
+ await mkdir2(join5(root, ".claude"), { recursive: true });
1898
+ existing = "# Local Claude Config\n";
1887
1899
  }
1888
1900
  if (existing.includes(heading)) return false;
1889
1901
  const keyDecisionsIdx = existing.indexOf("## Key Decisions");
@@ -1895,7 +1907,8 @@ ${content}
1895
1907
  `;
1896
1908
  const updated = existing.slice(0, insertAt) + section + existing.slice(insertAt);
1897
1909
  await writeFile2(claudeMdPath, updated);
1898
- log.success(`Added "${heading}" section to CLAUDE.md`);
1910
+ const label = targetPath ? ".claude/CLAUDE.md" : "CLAUDE.md";
1911
+ log.success(`Added "${heading}" section to ${label}`);
1899
1912
  return true;
1900
1913
  }
1901
1914
  async function createBacklogMd(root) {
@@ -1947,16 +1960,21 @@ async function createStarterRules(root) {
1947
1960
  log.success("Created .claude/rules/conventions.md with starter rules");
1948
1961
  return true;
1949
1962
  }
1950
- async function disableAutoMemory(root) {
1951
- const settings = await readSettingsJson(root);
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);
1952
1967
  if (settings.autoMemoryEnabled === false) return false;
1953
1968
  settings.autoMemoryEnabled = false;
1954
- await writeSettingsJson(root, settings);
1955
- log.success("Set autoMemoryEnabled: false (prevents conflict with agentic-memory)");
1969
+ await write(root, settings);
1970
+ const target = placement === "local" ? "settings.local.json" : "settings.json";
1971
+ log.success(`Set autoMemoryEnabled: false in ${target}`);
1956
1972
  return true;
1957
1973
  }
1958
- async function addMemoryToolPermissions(root) {
1959
- const settings = await readSettingsJson(root);
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);
1960
1978
  const permissions = settings.permissions ?? {};
1961
1979
  const allow = permissions.allow ?? [];
1962
1980
  const tools = [
@@ -1971,8 +1989,9 @@ async function addMemoryToolPermissions(root) {
1971
1989
  const missing = tools.filter((t) => !allow.includes(t));
1972
1990
  if (missing.length === 0) return false;
1973
1991
  settings.permissions = { ...permissions, allow: [...allow, ...missing] };
1974
- await writeSettingsJson(root, settings);
1975
- log.success("Added agentic-memory MCP tool permissions to allowedTools");
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}`);
1976
1995
  return true;
1977
1996
  }
1978
1997
  async function createEnhanceSkill(root) {
@@ -2780,9 +2799,12 @@ import { join as join10 } from "path";
2780
2799
  import { Command as Command4 } from "commander";
2781
2800
  import { confirm as confirm2 } from "@inquirer/prompts";
2782
2801
  function isMemoryInstalled() {
2802
+ const cwd = process.cwd();
2803
+ return hasMemoryHook(join10(cwd, ".claude", "settings.json")) || hasMemoryHook(join10(cwd, ".claude", "settings.local.json"));
2804
+ }
2805
+ function hasMemoryHook(path) {
2783
2806
  try {
2784
- const settingsPath = join10(process.cwd(), ".claude", "settings.json");
2785
- const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
2807
+ const settings = JSON.parse(readFileSync(path, "utf-8"));
2786
2808
  const hooks = settings.hooks;
2787
2809
  if (!hooks) return false;
2788
2810
  const sessionStart = hooks.SessionStart;
@@ -2808,14 +2830,24 @@ function createMemoryCommand() {
2808
2830
  return;
2809
2831
  }
2810
2832
  if (!isMemoryInstalled()) {
2811
- log.blank();
2812
- log.step("Claude doesn't have a knowledge base for this project yet.");
2813
- log.blank();
2814
- log.info("After setup, Claude will:");
2815
- log.info(" - Remember decisions, gotchas, and learnings across sessions");
2816
- log.info(" - Automatically recall relevant context when you start a session");
2817
- log.info(" - Save important facts as you work, so nothing gets lost");
2818
- log.blank();
2833
+ const { detectExistingSetup } = await import("./install-U6WARER4.js");
2834
+ const existing = detectExistingSetup(process.cwd());
2835
+ if (existing) {
2836
+ const location = existing === "local" ? ".claude/CLAUDE.md + settings.local.json" : "CLAUDE.md + settings.json";
2837
+ log.blank();
2838
+ log.success(`Memory config found (${location}) but database not set up.`);
2839
+ log.info("Run the install to complete setup.");
2840
+ log.blank();
2841
+ } else {
2842
+ log.blank();
2843
+ log.step("Claude doesn't have a knowledge base for this project yet.");
2844
+ log.blank();
2845
+ log.info("After setup, Claude will:");
2846
+ log.info(" - Remember decisions, gotchas, and learnings across sessions");
2847
+ log.info(" - Automatically recall relevant context when you start a session");
2848
+ log.info(" - Save important facts as you work, so nothing gets lost");
2849
+ log.blank();
2850
+ }
2819
2851
  const proceed = await confirm2({
2820
2852
  message: "Set up knowledge base?",
2821
2853
  default: true
@@ -2824,7 +2856,7 @@ function createMemoryCommand() {
2824
2856
  log.info("Skipped.");
2825
2857
  return;
2826
2858
  }
2827
- const { runInstall } = await import("./install-4GQ57KCQ.js");
2859
+ const { runInstall } = await import("./install-U6WARER4.js");
2828
2860
  await runInstall({});
2829
2861
  } else {
2830
2862
  const { requireMemoryDeps } = await import("./require-deps-NKRCPVAO.js");
@@ -2863,7 +2895,7 @@ function createMemoryCommand() {
2863
2895
  }
2864
2896
 
2865
2897
  // src/cli.ts
2866
- var program = new Command5().name("claude-launchpad").description("CLI toolkit that makes Claude Code setups measurably good").version("0.14.3", "-v, --version").action(async () => {
2898
+ var program = new Command5().name("claude-launchpad").description("CLI toolkit that makes Claude Code setups measurably good").version("0.15.1", "-v, --version").action(async () => {
2867
2899
  const hasConfig = await fileExists(join11(process.cwd(), "CLAUDE.md")) || await fileExists(join11(process.cwd(), ".claude", "settings.json"));
2868
2900
  if (hasConfig) {
2869
2901
  await program.commands.find((c) => c.name() === "doctor")?.parseAsync([], { from: "user" });