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 +11 -2
- package/dist/chunk-KOSJII4R.js +62 -0
- package/dist/chunk-KOSJII4R.js.map +1 -0
- package/dist/cli.js +87 -55
- package/dist/cli.js.map +1 -1
- package/dist/{install-4GQ57KCQ.js → install-U6WARER4.js} +46 -20
- package/dist/install-U6WARER4.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-CSLWJEGD.js +0 -25
- package/dist/chunk-CSLWJEGD.js.map +0 -1
- package/dist/install-4GQ57KCQ.js.map +0 -1
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.
|
|
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.
|
|
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
|
-
|
|
5
|
-
|
|
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
|
|
288
|
-
>
|
|
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
|
|
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
|
-
|
|
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 .
|
|
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
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
1955
|
-
|
|
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
|
|
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
|
|
1975
|
-
|
|
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
|
|
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
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
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-
|
|
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.
|
|
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" });
|