claude-launchpad 0.7.9 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -270,14 +270,6 @@ When all tasks in the current sprint are complete, do a quick quality check befo
270
270
  - Never hardcode secrets \u2014 use environment variables
271
271
  - Never write to \`.env\` files
272
272
  - Never expose internal error details in API responses`);
273
- sections.push("", `## Memory & Learnings
274
- Use the built-in memory system to persist knowledge across sessions:
275
- - **Save immediately** when you discover: a non-obvious fix, a gotcha, an external resource, a decision with context that would be lost, or a known issue to fix later
276
- - **Categories**: \`decision\` (why X over Y), \`gotcha\` (non-obvious pitfall), \`deferred\` (known issue, not urgent), \`reference\` (where to find things)
277
- - **Where**: project memory for this repo, global memory for cross-project learnings
278
- - **Format**: one fact per memory, include date and why \u2014 not just what
279
- - **Prune**: check if a memory on this topic exists before saving \u2014 update, don't duplicate
280
- - Before starting work, check memory for relevant context from previous sessions`);
281
273
  sections.push("", `## Key Decisions
282
274
  <!-- Record architectural decisions as you make them -->`);
283
275
  return sections.join("\n") + "\n";
@@ -561,7 +553,7 @@ CLAUDE.md must stay UNDER 120 lines of actionable content (not counting headings
561
553
  2. **## Architecture** - 3-5 bullet points describing the codebase shape (not a full directory tree)
562
554
  3. **## Conventions** - max 8 key patterns. Move detailed rules to .claude/rules/conventions.md
563
555
  4. **## Off-Limits** - max 8 guardrails specific to this project
564
- 5. **## Memory & Learnings** - max 6 bullets. If missing, add instructions for using the built-in memory system: what to save (gotchas, decisions, deferred issues, references), where (project vs global memory), and the rule to check existing memories before creating duplicates
556
+ 5. **## Memory & Learnings** - ONLY if the project already has a ## Memory section or agentic-memory is configured in .claude/settings.json. If present, keep to max 6 bullets. If the project does NOT use memory, do NOT add this section
565
557
  6. **## Key Decisions** - only decisions that affect how Claude should work in this codebase
566
558
  7. **MCP server suggestions** - look at what external services the project uses (databases, APIs, storage). If you spot Postgres, Redis, Stripe, GitHub API, or similar, suggest relevant MCP servers. Print as suggestions at the end, not in CLAUDE.md.
567
559
 
@@ -1314,15 +1306,93 @@ async function analyzeMcp(config) {
1314
1306
  return { name: "MCP Servers", issues, score };
1315
1307
  }
1316
1308
 
1309
+ // src/commands/doctor/analyzers/memory.ts
1310
+ var MEMORY_MCP_TOOLS = [
1311
+ "mcp__agentic-memory__memory_store",
1312
+ "mcp__agentic-memory__memory_search",
1313
+ "mcp__agentic-memory__memory_recent",
1314
+ "mcp__agentic-memory__memory_forget",
1315
+ "mcp__agentic-memory__memory_relate",
1316
+ "mcp__agentic-memory__memory_stats",
1317
+ "mcp__agentic-memory__memory_update"
1318
+ ];
1319
+ function hasMemoryIndicators(config) {
1320
+ const hasMcpServer = config.mcpServers.some((s) => s.name === "agentic-memory");
1321
+ const hasHookRef = config.hooks.some(
1322
+ (h) => h.command?.includes("memory context") || h.command?.includes("memory extract")
1323
+ );
1324
+ return hasMcpServer || hasHookRef;
1325
+ }
1326
+ async function analyzeMemory(config) {
1327
+ if (!hasMemoryIndicators(config)) return null;
1328
+ const issues = [];
1329
+ const hasSessionStart = config.hooks.some(
1330
+ (h) => h.event === "SessionStart" && h.command?.includes("memory context")
1331
+ );
1332
+ if (!hasSessionStart) {
1333
+ issues.push({
1334
+ analyzer: "Memory",
1335
+ severity: "high",
1336
+ message: "No SessionStart hook with memory context injection",
1337
+ fix: "Add a SessionStart hook that runs `memory context` to inject relevant memories"
1338
+ });
1339
+ }
1340
+ const hasStopHook = config.hooks.some(
1341
+ (h) => h.event === "Stop" && h.command?.includes("memory extract")
1342
+ );
1343
+ if (!hasStopHook) {
1344
+ issues.push({
1345
+ analyzer: "Memory",
1346
+ severity: "medium",
1347
+ message: "No Stop hook with memory extract for session learnings",
1348
+ fix: "Add a Stop hook that runs `memory extract` to capture session insights"
1349
+ });
1350
+ }
1351
+ const autoMemoryDisabled = config.settings?.autoMemoryEnabled === false;
1352
+ if (!autoMemoryDisabled) {
1353
+ issues.push({
1354
+ analyzer: "Memory",
1355
+ severity: "medium",
1356
+ message: "autoMemoryEnabled not disabled \u2014 built-in memory may conflict with agentic-memory",
1357
+ fix: "Set autoMemoryEnabled: false in .claude/settings.json"
1358
+ });
1359
+ }
1360
+ const hasMemoryGuidance = config.claudeMdContent?.includes("agentic-memory") || config.claudeMdContent?.includes("## Memory");
1361
+ if (!hasMemoryGuidance) {
1362
+ issues.push({
1363
+ analyzer: "Memory",
1364
+ severity: "low",
1365
+ message: "CLAUDE.md missing memory guidance section",
1366
+ fix: "Add a ## Memory section to CLAUDE.md describing when and how to use agentic-memory"
1367
+ });
1368
+ }
1369
+ const allowList = config.settings?.allowedTools ?? [];
1370
+ const missingTools = MEMORY_MCP_TOOLS.filter((t) => !allowList.includes(t));
1371
+ if (missingTools.length > 0) {
1372
+ issues.push({
1373
+ analyzer: "Memory",
1374
+ severity: "low",
1375
+ message: `${missingTools.length} agentic-memory MCP tool permission(s) missing from allowedTools`,
1376
+ fix: "Add all agentic-memory tool names to allowedTools in .claude/settings.json"
1377
+ });
1378
+ }
1379
+ const critical = issues.filter((i) => i.severity === "critical").length;
1380
+ const high = issues.filter((i) => i.severity === "high").length;
1381
+ const medium = issues.filter((i) => i.severity === "medium").length;
1382
+ const low = issues.filter((i) => i.severity === "low").length;
1383
+ const score = Math.max(0, 100 - (critical * 40 + high * 20 + medium * 10 + low * 5));
1384
+ return { name: "Memory", issues, score };
1385
+ }
1386
+
1317
1387
  // src/commands/doctor/analyzers/quality.ts
1318
- var ESSENTIAL_SECTIONS = [
1388
+ var BASE_SECTIONS = [
1319
1389
  { pattern: /^##\s+(Tech )?Stack/m, name: "Stack", why: "Claude performs worse without knowing the tech stack" },
1320
1390
  { pattern: /^##\s+Commands/m, name: "Commands", why: "Claude guesses wrong without explicit dev/build/test commands" },
1321
1391
  { pattern: /^##\s+Session Start/m, name: "Session Start", why: "Without this, Claude won't read TASKS.md or maintain continuity" },
1322
1392
  { pattern: /^##\s+Off.?Limits/m, name: "Off-Limits", why: "Without guardrails, Claude has no boundaries beyond defaults" },
1323
- { pattern: /^##\s+(Architecture|Project Structure)/m, name: "Architecture/Structure", why: "Claude makes better decisions when it understands the codebase shape" },
1324
- { pattern: /^##\s+Memory/m, name: "Memory & Learnings", why: "Without memory instructions, Claude forgets learnings and repeats mistakes across sessions" }
1393
+ { pattern: /^##\s+(Architecture|Project Structure)/m, name: "Architecture/Structure", why: "Claude makes better decisions when it understands the codebase shape" }
1325
1394
  ];
1395
+ var MEMORY_SECTION = { pattern: /^##\s+Memory/m, name: "Memory & Learnings", why: "Without memory instructions, Claude forgets learnings and repeats mistakes across sessions" };
1326
1396
  var VAGUE_PATTERNS = [
1327
1397
  { pattern: /write (good|clean|quality|nice) code/i, label: "write good code" },
1328
1398
  { pattern: /be (careful|thorough|diligent)/i, label: "be careful" },
@@ -1347,8 +1417,9 @@ async function analyzeQuality(config) {
1347
1417
  });
1348
1418
  return { name: "CLAUDE.md Quality", issues, score: 0 };
1349
1419
  }
1420
+ const sections = hasMemoryIndicators(config) ? [...BASE_SECTIONS, MEMORY_SECTION] : [...BASE_SECTIONS];
1350
1421
  let sectionsFound = 0;
1351
- for (const section of ESSENTIAL_SECTIONS) {
1422
+ for (const section of sections) {
1352
1423
  if (section.pattern.test(content)) {
1353
1424
  sectionsFound++;
1354
1425
  } else {
@@ -1397,93 +1468,6 @@ async function analyzeQuality(config) {
1397
1468
  return { name: "CLAUDE.md Quality", issues, score };
1398
1469
  }
1399
1470
 
1400
- // src/commands/doctor/analyzers/memory.ts
1401
- var MEMORY_MCP_TOOLS = [
1402
- "mcp__agentic-memory__memory_store",
1403
- "mcp__agentic-memory__memory_search",
1404
- "mcp__agentic-memory__memory_recent",
1405
- "mcp__agentic-memory__memory_forget",
1406
- "mcp__agentic-memory__memory_relate",
1407
- "mcp__agentic-memory__memory_stats",
1408
- "mcp__agentic-memory__memory_update"
1409
- ];
1410
- function hasMemoryIndicators(config) {
1411
- const hasMcpServer = config.mcpServers.some((s) => s.name === "agentic-memory");
1412
- const hasHookRef = config.hooks.some(
1413
- (h) => h.command?.includes("memory context") || h.command?.includes("memory extract")
1414
- );
1415
- return hasMcpServer || hasHookRef;
1416
- }
1417
- async function analyzeMemory(config) {
1418
- if (!hasMemoryIndicators(config)) return null;
1419
- const issues = [];
1420
- const hasMcpServer = config.mcpServers.some((s) => s.name === "agentic-memory");
1421
- if (!hasMcpServer) {
1422
- issues.push({
1423
- analyzer: "Memory",
1424
- severity: "high",
1425
- message: "agentic-memory MCP server not found in mcpServers",
1426
- fix: "Add agentic-memory to mcpServers in .claude/settings.json"
1427
- });
1428
- }
1429
- const hasSessionStart = config.hooks.some(
1430
- (h) => h.event === "SessionStart" && h.command?.includes("memory context")
1431
- );
1432
- if (!hasSessionStart) {
1433
- issues.push({
1434
- analyzer: "Memory",
1435
- severity: "high",
1436
- message: "No SessionStart hook with memory context injection",
1437
- fix: "Add a SessionStart hook that runs `memory context` to inject relevant memories"
1438
- });
1439
- }
1440
- const hasStopHook = config.hooks.some(
1441
- (h) => h.event === "Stop" && h.command?.includes("memory extract")
1442
- );
1443
- if (!hasStopHook) {
1444
- issues.push({
1445
- analyzer: "Memory",
1446
- severity: "medium",
1447
- message: "No Stop hook with memory extract for session learnings",
1448
- fix: "Add a Stop hook that runs `memory extract` to capture session insights"
1449
- });
1450
- }
1451
- const autoMemoryDisabled = config.settings?.autoMemoryEnabled === false;
1452
- if (!autoMemoryDisabled) {
1453
- issues.push({
1454
- analyzer: "Memory",
1455
- severity: "medium",
1456
- message: "autoMemoryEnabled not disabled \u2014 built-in memory may conflict with agentic-memory",
1457
- fix: "Set autoMemoryEnabled: false in .claude/settings.json"
1458
- });
1459
- }
1460
- const hasMemoryGuidance = config.claudeMdContent?.includes("agentic-memory") || config.claudeMdContent?.includes("## Memory");
1461
- if (!hasMemoryGuidance) {
1462
- issues.push({
1463
- analyzer: "Memory",
1464
- severity: "low",
1465
- message: "CLAUDE.md missing memory guidance section",
1466
- fix: "Add a ## Memory section to CLAUDE.md describing when and how to use agentic-memory"
1467
- });
1468
- }
1469
- const allowList = config.settings?.allowedTools ?? [];
1470
- const missingTools = MEMORY_MCP_TOOLS.filter((t) => !allowList.includes(t));
1471
- if (missingTools.length > 0) {
1472
- issues.push({
1473
- analyzer: "Memory",
1474
- severity: "low",
1475
- message: `${missingTools.length} agentic-memory MCP tool permission(s) missing from allowedTools`,
1476
- fix: "Add all agentic-memory tool names to allowedTools in .claude/settings.json"
1477
- });
1478
- }
1479
- const critical = issues.filter((i) => i.severity === "critical").length;
1480
- const high = issues.filter((i) => i.severity === "high").length;
1481
- const medium = issues.filter((i) => i.severity === "medium").length;
1482
- const low = issues.filter((i) => i.severity === "low").length;
1483
- const score = Math.max(0, 100 - (critical * 40 + high * 20 + medium * 10 + low * 5));
1484
- return { name: "Memory", issues, score };
1485
- }
1486
-
1487
1471
  // src/commands/doctor/fixer.ts
1488
1472
  import { readFile as readFile4, writeFile as writeFile2, mkdir as mkdir2, access as access4 } from "fs/promises";
1489
1473
  import { join as join5 } from "path";
@@ -1524,7 +1508,6 @@ var FIX_TABLE = [
1524
1508
  { analyzer: "Quality", match: "Session Start", fix: (root) => addClaudeMdSection(root, "## Session Start", "- ALWAYS read @TASKS.md first - it tracks progress across sessions\n- Update TASKS.md as you complete work") },
1525
1509
  { analyzer: "Rules", match: "No .claudeignore", fix: (root, detected) => createClaudeignore(root, detected) },
1526
1510
  { analyzer: "Rules", match: "No .claude/rules/", fix: (root) => createStarterRules(root) },
1527
- { analyzer: "Quality", match: "Memory", fix: (root) => addClaudeMdSection(root, "## Memory & Learnings", "Use the built-in memory system to persist knowledge across sessions:\n- **Save immediately** when you discover: a non-obvious fix, a gotcha, an external resource, a decision with context that would be lost, or a known issue to fix later\n- **Categories**: `decision` (why X over Y), `gotcha` (non-obvious pitfall), `deferred` (known issue, not urgent), `reference` (where to find things)\n- **Where**: project memory for this repo, global memory for cross-project learnings\n- **Format**: one fact per memory, include date and why \u2014 not just what\n- **Prune**: check if a memory on this topic exists before saving \u2014 update, don't duplicate\n- Before starting work, check memory for relevant context from previous sessions") },
1528
1511
  { analyzer: "Hooks", match: "PostCompact", fix: (root) => addPostCompactHook(root) },
1529
1512
  { analyzer: "Permissions", match: "force-push", fix: (root) => addForcePushProtection(root) },
1530
1513
  { analyzer: "Permissions", match: "Credential files not blocked", fix: (root) => addCredentialDenyRules(root) },
@@ -1947,23 +1930,23 @@ function createDoctorCommand() {
1947
1930
  if (fixed > 0) {
1948
1931
  log.success(`Applied ${fixed} fix(es). Re-scanning...`);
1949
1932
  log.blank();
1950
- const updatedConfig = await parseClaudeConfig(opts.path);
1951
- const updatedResults = await Promise.all([
1952
- analyzeBudget(updatedConfig),
1953
- analyzeQuality(updatedConfig),
1954
- analyzeSettings(updatedConfig),
1955
- analyzeHooks(updatedConfig),
1956
- analyzeRules(updatedConfig),
1957
- analyzePermissions(updatedConfig),
1958
- analyzeMcp(updatedConfig)
1959
- ]);
1960
- const updatedMemoryResult = await analyzeMemory(updatedConfig);
1961
- if (updatedMemoryResult) {
1962
- updatedResults.push(updatedMemoryResult);
1963
- }
1964
- renderDoctorReport(updatedResults, { afterFix: true });
1965
- log.info(`Then use ${chalk.bold("/lp-enhance")} inside Claude Code to have Claude restructure and complete your CLAUDE.md.`);
1966
1933
  }
1934
+ const updatedConfig = await parseClaudeConfig(opts.path);
1935
+ const updatedResults = await Promise.all([
1936
+ analyzeBudget(updatedConfig),
1937
+ analyzeQuality(updatedConfig),
1938
+ analyzeSettings(updatedConfig),
1939
+ analyzeHooks(updatedConfig),
1940
+ analyzeRules(updatedConfig),
1941
+ analyzePermissions(updatedConfig),
1942
+ analyzeMcp(updatedConfig)
1943
+ ]);
1944
+ const updatedMemoryResult = await analyzeMemory(updatedConfig);
1945
+ if (updatedMemoryResult) {
1946
+ updatedResults.push(updatedMemoryResult);
1947
+ }
1948
+ renderDoctorReport(updatedResults, { afterFix: true });
1949
+ log.info(`Then use ${chalk.bold("/lp-enhance")} inside Claude Code to have Claude restructure and complete your CLAUDE.md.`);
1967
1950
  }
1968
1951
  }
1969
1952
  if (opts.minScore) {
@@ -2660,7 +2643,7 @@ function createMemoryCommand() {
2660
2643
  }
2661
2644
 
2662
2645
  // src/cli.ts
2663
- var program = new Command5().name("claude-launchpad").description("CLI toolkit that makes Claude Code setups measurably good").version("0.7.9", "-v, --version").action(async () => {
2646
+ var program = new Command5().name("claude-launchpad").description("CLI toolkit that makes Claude Code setups measurably good").version("0.8.1", "-v, --version").action(async () => {
2664
2647
  const hasConfig = await fileExists(join11(process.cwd(), "CLAUDE.md")) || await fileExists(join11(process.cwd(), ".claude", "settings.json"));
2665
2648
  if (hasConfig) {
2666
2649
  await program.commands.find((c) => c.name() === "doctor")?.parseAsync([], { from: "user" });