claude-launchpad 1.4.0 → 1.6.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 (37) hide show
  1. package/dist/{chunk-UQOBOHKN.js → chunk-GLFJ2B43.js} +24 -11
  2. package/dist/chunk-GLFJ2B43.js.map +1 -0
  3. package/dist/{chunk-ADZ45KHX.js → chunk-LOUSTE6C.js} +2 -2
  4. package/dist/{chunk-RJEPJ4JE.js → chunk-WUJWETUJ.js} +14 -3
  5. package/dist/chunk-WUJWETUJ.js.map +1 -0
  6. package/dist/{chunk-WOC2PKT2.js → chunk-YF6HCPVY.js} +2 -2
  7. package/dist/{chunk-MQJA7TGY.js → chunk-ZI2PZSG4.js} +88 -2
  8. package/dist/chunk-ZI2PZSG4.js.map +1 -0
  9. package/dist/cli.js +302 -58
  10. package/dist/cli.js.map +1 -1
  11. package/dist/commands/memory/server.js +8 -74
  12. package/dist/commands/memory/server.js.map +1 -1
  13. package/dist/{context-NCV46PTY.js → context-DQQGQ767.js} +73 -9
  14. package/dist/context-DQQGQ767.js.map +1 -0
  15. package/dist/{install-DORMJYCU.js → install-MTDWI6RO.js} +5 -5
  16. package/dist/{pull-UYLUGILU.js → pull-O6TLQGAQ.js} +6 -6
  17. package/dist/{push-HWXJGSTL.js → push-VDADQVA5.js} +6 -6
  18. package/dist/{require-deps-RUXTMQUV.js → require-deps-MCFEZOIF.js} +3 -3
  19. package/dist/{stats-DFV24AJW.js → stats-OJEG5W2D.js} +6 -6
  20. package/dist/{sync-clean-GFX5F55E.js → sync-clean-2BMOFDV7.js} +2 -2
  21. package/dist/{sync-status-VXMDWDRG.js → sync-status-IN5434DI.js} +6 -6
  22. package/dist/{tui-ELOJ37ZA.js → tui-DQD26BU6.js} +4 -4
  23. package/package.json +3 -2
  24. package/dist/chunk-MQJA7TGY.js.map +0 -1
  25. package/dist/chunk-RJEPJ4JE.js.map +0 -1
  26. package/dist/chunk-UQOBOHKN.js.map +0 -1
  27. package/dist/context-NCV46PTY.js.map +0 -1
  28. /package/dist/{chunk-ADZ45KHX.js.map → chunk-LOUSTE6C.js.map} +0 -0
  29. /package/dist/{chunk-WOC2PKT2.js.map → chunk-YF6HCPVY.js.map} +0 -0
  30. /package/dist/{install-DORMJYCU.js.map → install-MTDWI6RO.js.map} +0 -0
  31. /package/dist/{pull-UYLUGILU.js.map → pull-O6TLQGAQ.js.map} +0 -0
  32. /package/dist/{push-HWXJGSTL.js.map → push-VDADQVA5.js.map} +0 -0
  33. /package/dist/{require-deps-RUXTMQUV.js.map → require-deps-MCFEZOIF.js.map} +0 -0
  34. /package/dist/{stats-DFV24AJW.js.map → stats-OJEG5W2D.js.map} +0 -0
  35. /package/dist/{sync-clean-GFX5F55E.js.map → sync-clean-2BMOFDV7.js.map} +0 -0
  36. /package/dist/{sync-status-VXMDWDRG.js.map → sync-status-IN5434DI.js.map} +0 -0
  37. /package/dist/{tui-ELOJ37ZA.js.map → tui-DQD26BU6.js.map} +0 -0
package/dist/cli.js CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  import {
6
6
  BACKLOG_CONTENT,
7
7
  ENHANCE_SKILL_VERSION,
8
+ LP_STUB_OPEN,
8
9
  OFF_LIMITS_CONTENT,
9
10
  SESSION_START_CONTENT,
10
11
  SKILL_AUTHORING_CONTENT,
@@ -19,7 +20,7 @@ import {
19
20
  printScoreCard,
20
21
  readFileOrNull,
21
22
  renderDoctorReport
22
- } from "./chunk-UQOBOHKN.js";
23
+ } from "./chunk-GLFJ2B43.js";
23
24
 
24
25
  // src/cli.ts
25
26
  import { Command as Command5 } from "commander";
@@ -1084,6 +1085,60 @@ async function analyzeMcp(config) {
1084
1085
  return { name: "MCP Servers", issues, score };
1085
1086
  }
1086
1087
 
1088
+ // src/commands/doctor/analyzers/hook-resolver.ts
1089
+ import { readFile as readFile3, realpath } from "fs/promises";
1090
+ import { resolve as resolve2, sep } from "path";
1091
+ import { parse } from "shell-quote";
1092
+ async function resolveHookCommand(hook, projectRoot) {
1093
+ const command = hook.command ?? "";
1094
+ if (!command) return { command: "", expansions: [], missingScripts: [] };
1095
+ const scriptPaths = extractShellScripts(command);
1096
+ if (scriptPaths.length === 0) return { command, expansions: [], missingScripts: [] };
1097
+ let projectRootReal;
1098
+ try {
1099
+ projectRootReal = await realpath(projectRoot);
1100
+ } catch {
1101
+ return { command, expansions: [], missingScripts: [] };
1102
+ }
1103
+ const expansions = [];
1104
+ const missingScripts = [];
1105
+ for (const relPath of scriptPaths) {
1106
+ const resolved = resolve2(projectRoot, relPath);
1107
+ let realResolved;
1108
+ try {
1109
+ realResolved = await realpath(resolved);
1110
+ } catch {
1111
+ missingScripts.push(relPath);
1112
+ continue;
1113
+ }
1114
+ if (realResolved !== projectRootReal && !realResolved.startsWith(projectRootReal + sep)) {
1115
+ continue;
1116
+ }
1117
+ try {
1118
+ const body = await readFile3(realResolved, "utf-8");
1119
+ expansions.push({ path: relPath, body });
1120
+ } catch {
1121
+ missingScripts.push(relPath);
1122
+ }
1123
+ }
1124
+ return { command, expansions, missingScripts };
1125
+ }
1126
+ function effectiveCommandText(resolved) {
1127
+ if (resolved.expansions.length === 0) return resolved.command;
1128
+ return resolved.command + "\n" + resolved.expansions.map((e) => e.body).join("\n");
1129
+ }
1130
+ function extractShellScripts(command) {
1131
+ const tokens = parse(command);
1132
+ const scripts = [];
1133
+ for (const token of tokens) {
1134
+ if (typeof token !== "string") continue;
1135
+ if (!token.endsWith(".sh")) continue;
1136
+ if (token.startsWith("~") || token.startsWith("$")) continue;
1137
+ scripts.push(token);
1138
+ }
1139
+ return scripts;
1140
+ }
1141
+
1087
1142
  // src/commands/doctor/analyzers/memory.ts
1088
1143
  var MEMORY_MCP_TOOLS = [
1089
1144
  "mcp__agentic-memory__memory_store",
@@ -1094,7 +1149,10 @@ var MEMORY_MCP_TOOLS = [
1094
1149
  "mcp__agentic-memory__memory_stats",
1095
1150
  "mcp__agentic-memory__memory_update"
1096
1151
  ];
1097
- function hasMemoryIndicators(config) {
1152
+ async function resolveAllHooks(hooks, projectRoot) {
1153
+ return Promise.all(hooks.map((h) => resolveHookCommand(h, projectRoot)));
1154
+ }
1155
+ async function hasMemoryIndicators(config, projectRoot) {
1098
1156
  if (config.mcpServers.some((s) => s.name === "agentic-memory")) return true;
1099
1157
  const permissions = config.settings?.permissions ?? {};
1100
1158
  const localPermissions = config.localSettings?.permissions ?? {};
@@ -1103,14 +1161,18 @@ function hasMemoryIndicators(config) {
1103
1161
  ...localPermissions.allow ?? []
1104
1162
  ];
1105
1163
  if (allowList.some((t) => t.startsWith("mcp__agentic-memory__"))) return true;
1106
- if (config.hooks.some((h) => h.event === "SessionStart" && h.command?.includes("memory context"))) return true;
1107
- return false;
1164
+ const resolved = await resolveAllHooks(config.hooks, projectRoot);
1165
+ return config.hooks.some(
1166
+ (h, i) => h.event === "SessionStart" && effectiveCommandText(resolved[i]).includes("memory context")
1167
+ );
1108
1168
  }
1109
- async function analyzeMemory(config) {
1110
- if (!hasMemoryIndicators(config)) return null;
1169
+ async function analyzeMemory(config, projectRoot) {
1170
+ if (!await hasMemoryIndicators(config, projectRoot)) return null;
1111
1171
  const issues = [];
1172
+ const resolved = await resolveAllHooks(config.hooks, projectRoot);
1173
+ const effectiveAt = (i) => effectiveCommandText(resolved[i]);
1112
1174
  const hasSessionStart = config.hooks.some(
1113
- (h) => h.event === "SessionStart" && h.command?.includes("memory context")
1175
+ (h, i) => h.event === "SessionStart" && effectiveAt(i).includes("memory context")
1114
1176
  );
1115
1177
  if (!hasSessionStart) {
1116
1178
  issues.push({
@@ -1121,7 +1183,7 @@ async function analyzeMemory(config) {
1121
1183
  });
1122
1184
  }
1123
1185
  const hasStaleStopHook = config.hooks.some(
1124
- (h) => h.event === "Stop" && h.command?.includes("memory extract")
1186
+ (h, i) => h.event === "Stop" && effectiveAt(i).includes("memory extract")
1125
1187
  );
1126
1188
  if (hasStaleStopHook) {
1127
1189
  issues.push({
@@ -1167,7 +1229,7 @@ async function analyzeMemory(config) {
1167
1229
  const syncConfig = readSyncConfig();
1168
1230
  if (syncConfig) {
1169
1231
  const hasSessionStartPull = config.hooks.some(
1170
- (h) => h.event === "SessionStart" && h.command?.includes("memory pull")
1232
+ (h, i) => h.event === "SessionStart" && effectiveAt(i).includes("memory pull")
1171
1233
  );
1172
1234
  if (!hasSessionStartPull) {
1173
1235
  issues.push({
@@ -1178,7 +1240,7 @@ async function analyzeMemory(config) {
1178
1240
  });
1179
1241
  }
1180
1242
  const hasSessionEndPush = config.hooks.some(
1181
- (h) => h.event === "SessionEnd" && h.command?.includes("memory push")
1243
+ (h, i) => h.event === "SessionEnd" && effectiveAt(i).includes("memory push")
1182
1244
  );
1183
1245
  if (!hasSessionEndPush) {
1184
1246
  issues.push({
@@ -1200,6 +1262,18 @@ async function analyzeMemory(config) {
1200
1262
  });
1201
1263
  }
1202
1264
  }
1265
+ for (let i = 0; i < config.hooks.length; i++) {
1266
+ const h = config.hooks[i];
1267
+ if (h.event !== "SessionStart" && h.event !== "SessionEnd") continue;
1268
+ for (const missing of resolved[i].missingScripts) {
1269
+ issues.push({
1270
+ analyzer: "Memory",
1271
+ severity: "low",
1272
+ message: `${h.event} hook references \`${missing}\` but the file is missing \u2014 wrapper can't run`,
1273
+ fix: `Create ${missing} or remove the broken hook from .claude/settings.json`
1274
+ });
1275
+ }
1276
+ }
1203
1277
  const critical = issues.filter((i) => i.severity === "critical").length;
1204
1278
  const high = issues.filter((i) => i.severity === "high").length;
1205
1279
  const medium = issues.filter((i) => i.severity === "medium").length;
@@ -1208,17 +1282,189 @@ async function analyzeMemory(config) {
1208
1282
  return { name: "Memory", issues, score };
1209
1283
  }
1210
1284
 
1211
- // src/commands/doctor/analyzers/quality.ts
1212
- var BASE_SECTIONS = [
1213
- { pattern: /^##\s+(Tech )?Stack/m, name: "Stack", why: "Claude performs worse without knowing the tech stack" },
1214
- { pattern: /^##\s+Commands/m, name: "Commands", why: "Claude guesses wrong without explicit dev/build/test commands" },
1215
- { pattern: /^##\s+Session Start/m, name: "Session Start", why: "Without this, Claude won't read TASKS.md or maintain continuity" },
1216
- { pattern: /^##\s+Off.?Limits/m, name: "Off-Limits", why: "Without guardrails, Claude has no boundaries beyond defaults" },
1217
- { pattern: /^##\s+(Architecture|Project Structure)/m, name: "Architecture/Structure", why: "Claude makes better decisions when it understands the codebase shape" },
1218
- { pattern: /^##\s+Backlog/m, name: "Backlog", why: "Without backlog instructions, deferred features get lost in conversation history" },
1219
- { pattern: /^##\s+(Stop.and.Swarm|When Stuck|Debug)/m, name: "Stop-and-Swarm", why: "Without a stop-and-swarm rule, Claude keeps guessing in circles instead of parallelizing research" }
1285
+ // src/commands/doctor/analyzers/quality-intents.ts
1286
+ var INTENT_RULES = [
1287
+ {
1288
+ name: "Stack",
1289
+ why: "Claude performs worse without knowing the tech stack",
1290
+ headingPatterns: [/^tech\s+stack$/i, /^stack$/i, /^technology$/i, /^tech$/i],
1291
+ bodyKeywords: [
1292
+ /\blanguage:/i,
1293
+ /\bframework:/i,
1294
+ /\bpackage\s+manager:/i,
1295
+ /\bruntime:/i,
1296
+ /\b(typescript|javascript|python|ruby|go|rust|java|php|swift|kotlin)\b/i,
1297
+ /\b(react|next\.?js|vue|svelte|angular|express|fastify|laravel|rails|django|flask)\b/i,
1298
+ /\b(node(?:\.?js)?|deno|bun|cpython)\b/i
1299
+ ],
1300
+ minBodyKeywords: 2
1301
+ },
1302
+ {
1303
+ name: "Commands",
1304
+ why: "Claude guesses wrong without explicit dev/build/test commands",
1305
+ headingPatterns: [/^commands?$/i, /^scripts$/i, /^dev\s+commands$/i, /^development$/i],
1306
+ bodyKeywords: [
1307
+ /\b(pnpm|npm|yarn|bun)\s+\w+/i,
1308
+ /\b(build|test|dev|lint|typecheck|start):/i,
1309
+ /\brun\s+(tests?|build|dev)/i,
1310
+ /\bmake\s+\w+/i,
1311
+ /\bcargo\s+\w+/i
1312
+ ],
1313
+ minBodyKeywords: 2
1314
+ },
1315
+ {
1316
+ name: "Session Start",
1317
+ why: "Without this, Claude won't read TASKS.md or maintain continuity",
1318
+ headingPatterns: [
1319
+ /^session\s+start$/i,
1320
+ /^session$/i,
1321
+ /^sprint\s+planning$/i,
1322
+ /^workflow$/i,
1323
+ /^getting\s+started$/i,
1324
+ /^at\s+session\s+start$/i
1325
+ ],
1326
+ bodyKeywords: [
1327
+ /\btasks?\.md\b/i,
1328
+ /\bsession\s+(start|log)\b/i,
1329
+ /\bsprint\s+(log|planning)\b/i,
1330
+ /\b(read|check).*at\s+(session|start)/i,
1331
+ /\btrack\s+progress/i
1332
+ ],
1333
+ minBodyKeywords: 1
1334
+ },
1335
+ {
1336
+ name: "Off-Limits",
1337
+ why: "Without guardrails, Claude has no boundaries beyond defaults",
1338
+ headingPatterns: [
1339
+ /^off.?limits$/i,
1340
+ /^constraints$/i,
1341
+ /^don'?t$/i,
1342
+ /^rules$/i,
1343
+ /^guardrails$/i,
1344
+ /^forbidden$/i,
1345
+ /^security\s+notes$/i
1346
+ ],
1347
+ bodyKeywords: [
1348
+ /\bnever\s+\w+/i,
1349
+ /\bforbidden/i,
1350
+ /\bdo\s+not\b/i,
1351
+ /\b(secret|credential|api\s+key|password|token)/i,
1352
+ /\.env\b/
1353
+ ],
1354
+ minBodyKeywords: 2
1355
+ },
1356
+ {
1357
+ name: "Architecture/Structure",
1358
+ why: "Claude makes better decisions when it understands the codebase shape",
1359
+ headingPatterns: [
1360
+ /^architecture$/i,
1361
+ /^project\s+structure$/i,
1362
+ /^structure$/i,
1363
+ /^codebase$/i,
1364
+ /^layout$/i,
1365
+ /^repo\s+layout$/i
1366
+ ],
1367
+ bodyKeywords: [
1368
+ /\bsrc\//,
1369
+ /\b(directory|directories|folder|module|layer)\b/i,
1370
+ /\barchitecture\b/i
1371
+ ],
1372
+ minBodyKeywords: 1
1373
+ },
1374
+ {
1375
+ name: "Backlog",
1376
+ why: "Without backlog instructions, deferred features get lost in conversation history",
1377
+ headingPatterns: [
1378
+ /^backlog$/i,
1379
+ /^roadmap$/i,
1380
+ /^parked$/i,
1381
+ /^future\s+work$/i,
1382
+ /^parked\s+features?$/i
1383
+ ],
1384
+ bodyKeywords: [
1385
+ /\bbacklog\.md\b/i,
1386
+ /\bbacklog\b/i,
1387
+ /\bdeferred\b/i,
1388
+ /\bparked\s+features?\b/i
1389
+ ],
1390
+ minBodyKeywords: 1
1391
+ },
1392
+ {
1393
+ name: "Stop-and-Swarm",
1394
+ why: "Without a stop-and-swarm rule, Claude keeps guessing in circles instead of parallelizing research",
1395
+ headingPatterns: [
1396
+ /^stop.and.swarm$/i,
1397
+ /^when\s+stuck$/i,
1398
+ /^debug$/i,
1399
+ /^escalation$/i,
1400
+ /^swarm$/i,
1401
+ /^parallel\s+agents?$/i
1402
+ ],
1403
+ bodyKeywords: [
1404
+ /\bstop-and-swarm\b/i,
1405
+ /\bparallel\s+agents?\b/i,
1406
+ /\bspin\s+up\b/i,
1407
+ /\b(3|three)\s+(parallel\s+)?agents?\b/i,
1408
+ /\bfailed\s+iterations?\b/i
1409
+ ],
1410
+ minBodyKeywords: 1
1411
+ }
1220
1412
  ];
1221
- var MEMORY_SECTION = { pattern: /^##\s+Memory/m, name: "Memory & Learnings", why: "Without memory instructions, Claude forgets learnings and repeats mistakes across sessions" };
1413
+ var MEMORY_INTENT = {
1414
+ name: "Memory & Learnings",
1415
+ why: "Without memory instructions, Claude forgets learnings and repeats mistakes across sessions",
1416
+ headingPatterns: [/^memory$/i, /^learnings?$/i, /^memory\s*(&|and)\s*learnings?$/i],
1417
+ bodyKeywords: [
1418
+ /\bmemory_search\b/i,
1419
+ /\bmemory_store\b/i,
1420
+ /\bagentic-memory\b/i,
1421
+ /\bstore\s+memories?\b/i,
1422
+ /\binject(ed)?\s+(at|in|into)\s+(session|startup)/i
1423
+ ],
1424
+ minBodyKeywords: 1
1425
+ };
1426
+ function parseSections(content) {
1427
+ const lines = content.split("\n");
1428
+ const sections = [];
1429
+ let currentHeading = null;
1430
+ let currentBody = [];
1431
+ const flush = () => {
1432
+ if (currentHeading === null) return;
1433
+ const body = currentBody.join("\n");
1434
+ sections.push({
1435
+ heading: currentHeading,
1436
+ body,
1437
+ isStub: body.includes(LP_STUB_OPEN)
1438
+ });
1439
+ };
1440
+ for (const line of lines) {
1441
+ const match = line.match(/^##\s+(.+?)\s*$/);
1442
+ if (match) {
1443
+ flush();
1444
+ currentHeading = match[1];
1445
+ currentBody = [];
1446
+ } else if (currentHeading !== null) {
1447
+ currentBody.push(line);
1448
+ }
1449
+ }
1450
+ flush();
1451
+ return sections;
1452
+ }
1453
+ function sectionSatisfiesIntent(section, rule) {
1454
+ if (section.isStub) return false;
1455
+ const headingMatch = rule.headingPatterns.some((p) => p.test(section.heading));
1456
+ if (headingMatch) return true;
1457
+ const keywordHits = rule.bodyKeywords.reduce(
1458
+ (n, p) => p.test(section.body) ? n + 1 : n,
1459
+ 0
1460
+ );
1461
+ return keywordHits >= rule.minBodyKeywords;
1462
+ }
1463
+ function documentSatisfiesIntent(sections, rule) {
1464
+ return sections.some((s) => sectionSatisfiesIntent(s, rule));
1465
+ }
1466
+
1467
+ // src/commands/doctor/analyzers/quality.ts
1222
1468
  var VAGUE_PATTERNS = [
1223
1469
  { pattern: /write (good|clean|quality|nice) code/i, label: "write good code" },
1224
1470
  { pattern: /be (careful|thorough|diligent)/i, label: "be careful" },
@@ -1231,7 +1477,7 @@ var SECRET_PATTERNS = [
1231
1477
  { pattern: /AKIA[0-9A-Z]{16}/, label: "AWS access key" },
1232
1478
  { pattern: /xoxb-[0-9]+-[a-zA-Z0-9]+/, label: "Slack bot token" }
1233
1479
  ];
1234
- async function analyzeQuality(config) {
1480
+ async function analyzeQuality(config, projectRoot) {
1235
1481
  const issues = [];
1236
1482
  const content = config.claudeMdContent;
1237
1483
  if (content === null) {
@@ -1243,18 +1489,16 @@ async function analyzeQuality(config) {
1243
1489
  });
1244
1490
  return { name: "CLAUDE.md Quality", issues, score: 0 };
1245
1491
  }
1246
- const sections = hasMemoryIndicators(config) ? [...BASE_SECTIONS, MEMORY_SECTION] : [...BASE_SECTIONS];
1247
- const combinedContent = [content, config.localClaudeMdContent].filter(Boolean).join("\n");
1248
- let sectionsFound = 0;
1249
- for (const section of sections) {
1250
- if (section.pattern.test(combinedContent)) {
1251
- sectionsFound++;
1252
- } else {
1492
+ const rules = await hasMemoryIndicators(config, projectRoot) ? [...INTENT_RULES, MEMORY_INTENT] : [...INTENT_RULES];
1493
+ const combinedContent = [content, config.localClaudeMdContent].filter(Boolean).join("\n\n");
1494
+ const sections = parseSections(combinedContent);
1495
+ for (const rule of rules) {
1496
+ if (!documentSatisfiesIntent(sections, rule)) {
1253
1497
  issues.push({
1254
1498
  analyzer: "Quality",
1255
1499
  severity: "medium",
1256
- message: `Missing "## ${section.name}" section \u2014 ${section.why}`,
1257
- fix: `Add a ## ${section.name} section to CLAUDE.md`
1500
+ message: `Missing "## ${rule.name}" section \u2014 ${rule.why}`,
1501
+ fix: `Add a ## ${rule.name} section to CLAUDE.md`
1258
1502
  });
1259
1503
  }
1260
1504
  }
@@ -1356,7 +1600,7 @@ async function runAndDisplay(projectRoot) {
1356
1600
  }
1357
1601
  const results = await Promise.all([
1358
1602
  analyzeBudget(config),
1359
- analyzeQuality(config),
1603
+ analyzeQuality(config, projectRoot),
1360
1604
  analyzeSettings(config),
1361
1605
  analyzeHooks(config),
1362
1606
  analyzeRules(config),
@@ -1386,14 +1630,14 @@ function createDoctorCommand() {
1386
1630
  }
1387
1631
  const results = await Promise.all([
1388
1632
  analyzeBudget(config),
1389
- analyzeQuality(config),
1633
+ analyzeQuality(config, opts.path),
1390
1634
  analyzeSettings(config),
1391
1635
  analyzeHooks(config),
1392
1636
  analyzeRules(config),
1393
1637
  analyzePermissions(config),
1394
1638
  analyzeMcp(config)
1395
1639
  ]);
1396
- const memoryResult = await analyzeMemory(config);
1640
+ const memoryResult = await analyzeMemory(config, opts.path);
1397
1641
  if (memoryResult) {
1398
1642
  results.push(memoryResult);
1399
1643
  }
@@ -1444,14 +1688,14 @@ function createDoctorCommand() {
1444
1688
  const updatedConfig = await parseClaudeConfig(opts.path);
1445
1689
  const updatedResults = await Promise.all([
1446
1690
  analyzeBudget(updatedConfig),
1447
- analyzeQuality(updatedConfig),
1691
+ analyzeQuality(updatedConfig, opts.path),
1448
1692
  analyzeSettings(updatedConfig),
1449
1693
  analyzeHooks(updatedConfig),
1450
1694
  analyzeRules(updatedConfig),
1451
1695
  analyzePermissions(updatedConfig),
1452
1696
  analyzeMcp(updatedConfig)
1453
1697
  ]);
1454
- const updatedMemoryResult = await analyzeMemory(updatedConfig);
1698
+ const updatedMemoryResult = await analyzeMemory(updatedConfig, opts.path);
1455
1699
  if (updatedMemoryResult) {
1456
1700
  updatedResults.push(updatedMemoryResult);
1457
1701
  }
@@ -1477,8 +1721,8 @@ import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
1477
1721
  import { join as join7 } from "path";
1478
1722
 
1479
1723
  // src/commands/eval/loader.ts
1480
- import { readFile as readFile3, readdir as readdir3, access as access3 } from "fs/promises";
1481
- import { join as join5, resolve as resolve2, dirname as dirname2 } from "path";
1724
+ import { readFile as readFile4, readdir as readdir3, access as access3 } from "fs/promises";
1725
+ import { join as join5, resolve as resolve3, dirname as dirname2 } from "path";
1482
1726
  import { fileURLToPath } from "url";
1483
1727
  import { parse as parseYaml } from "yaml";
1484
1728
 
@@ -1582,11 +1826,11 @@ var ScenarioError = class extends Error {
1582
1826
  // src/commands/eval/loader.ts
1583
1827
  async function findScenariosDir() {
1584
1828
  const thisDir = dirname2(fileURLToPath(import.meta.url));
1585
- const devPath = resolve2(thisDir, "../../../scenarios");
1829
+ const devPath = resolve3(thisDir, "../../../scenarios");
1586
1830
  if (await dirExists(devPath)) return devPath;
1587
- const bundledPath = resolve2(thisDir, "../scenarios");
1831
+ const bundledPath = resolve3(thisDir, "../scenarios");
1588
1832
  if (await dirExists(bundledPath)) return bundledPath;
1589
- const rootPath = resolve2(thisDir, "../../scenarios");
1833
+ const rootPath = resolve3(thisDir, "../../scenarios");
1590
1834
  if (await dirExists(rootPath)) return rootPath;
1591
1835
  return devPath;
1592
1836
  }
@@ -1600,7 +1844,7 @@ async function dirExists(path) {
1600
1844
  }
1601
1845
  async function loadScenarios(options) {
1602
1846
  const { suite, customPath } = options;
1603
- const scenarioDir = customPath ? resolve2(customPath) : await findScenariosDir();
1847
+ const scenarioDir = customPath ? resolve3(customPath) : await findScenariosDir();
1604
1848
  const dirs = suite ? [join5(scenarioDir, suite)] : await getSubdirectories(scenarioDir);
1605
1849
  const allDirs = [scenarioDir, ...dirs];
1606
1850
  const scenarios = [];
@@ -1608,7 +1852,7 @@ async function loadScenarios(options) {
1608
1852
  const files = await listYamlFiles(dir);
1609
1853
  for (const file of files) {
1610
1854
  try {
1611
- const content = await readFile3(file, "utf-8");
1855
+ const content = await readFile4(file, "utf-8");
1612
1856
  const raw = parseYaml(content);
1613
1857
  const scenario = validateScenario(raw, file);
1614
1858
  scenarios.push(scenario);
@@ -1638,7 +1882,7 @@ async function listYamlFiles(dir) {
1638
1882
  }
1639
1883
 
1640
1884
  // src/commands/eval/runner.ts
1641
- import { mkdir as mkdir2, writeFile as writeFile2, readFile as readFile4, readdir as readdir4, rm, cp } from "fs/promises";
1885
+ import { mkdir as mkdir2, writeFile as writeFile2, readFile as readFile5, readdir as readdir4, rm, cp } from "fs/promises";
1642
1886
  import { join as join6, dirname as dirname3 } from "path";
1643
1887
  import { tmpdir } from "os";
1644
1888
  import { randomUUID } from "crypto";
@@ -1807,7 +2051,7 @@ async function evaluateSingleCheck(check, sandboxDir) {
1807
2051
  async function checkGrep(check, sandboxDir) {
1808
2052
  if (!check.pattern) return false;
1809
2053
  try {
1810
- const content = await readFile4(join6(sandboxDir, check.target), "utf-8");
2054
+ const content = await readFile5(join6(sandboxDir, check.target), "utf-8");
1811
2055
  let found;
1812
2056
  try {
1813
2057
  found = new RegExp(check.pattern).test(content);
@@ -1821,7 +2065,7 @@ async function checkGrep(check, sandboxDir) {
1821
2065
  }
1822
2066
  async function checkFilePresence(check, sandboxDir) {
1823
2067
  try {
1824
- await readFile4(join6(sandboxDir, check.target));
2068
+ await readFile5(join6(sandboxDir, check.target));
1825
2069
  return check.expect === "present";
1826
2070
  } catch {
1827
2071
  return check.expect === "absent";
@@ -1832,7 +2076,7 @@ async function checkMaxLines(check, sandboxDir) {
1832
2076
  try {
1833
2077
  const files = await listAllFiles(join6(sandboxDir, check.target));
1834
2078
  for (const file of files) {
1835
- const content = await readFile4(file, "utf-8");
2079
+ const content = await readFile5(file, "utf-8");
1836
2080
  if (content.split("\n").length > maxLines) {
1837
2081
  return check.expect === "absent";
1838
2082
  }
@@ -2088,14 +2332,14 @@ function createMemoryCommand() {
2088
2332
  log.error("Knowledge base not set up yet. Run `claude-launchpad memory` first.");
2089
2333
  return;
2090
2334
  }
2091
- const { requireMemoryDeps } = await import("./require-deps-RUXTMQUV.js");
2335
+ const { requireMemoryDeps } = await import("./require-deps-MCFEZOIF.js");
2092
2336
  await requireMemoryDeps();
2093
- const { startTui } = await import("./tui-ELOJ37ZA.js");
2337
+ const { startTui } = await import("./tui-DQD26BU6.js");
2094
2338
  await startTui();
2095
2339
  return;
2096
2340
  }
2097
2341
  if (!isMemoryInstalled()) {
2098
- const { detectExistingSetup } = await import("./install-DORMJYCU.js");
2342
+ const { detectExistingSetup } = await import("./install-MTDWI6RO.js");
2099
2343
  const existing = detectExistingSetup(process.cwd());
2100
2344
  if (existing) {
2101
2345
  const location = existing === "local" ? ".claude/CLAUDE.md + settings.local.json" : "CLAUDE.md + settings.json";
@@ -2121,18 +2365,18 @@ function createMemoryCommand() {
2121
2365
  log.info("Skipped.");
2122
2366
  return;
2123
2367
  }
2124
- const { runInstall } = await import("./install-DORMJYCU.js");
2368
+ const { runInstall } = await import("./install-MTDWI6RO.js");
2125
2369
  await runInstall({});
2126
2370
  } else {
2127
- const { requireMemoryDeps } = await import("./require-deps-RUXTMQUV.js");
2371
+ const { requireMemoryDeps } = await import("./require-deps-MCFEZOIF.js");
2128
2372
  await requireMemoryDeps();
2129
- const { runStats } = await import("./stats-DFV24AJW.js");
2373
+ const { runStats } = await import("./stats-OJEG5W2D.js");
2130
2374
  await runStats({});
2131
2375
  }
2132
2376
  });
2133
2377
  memory.addCommand(
2134
2378
  new Command4("context").description("Load session context (hook handler)").option("--json", "JSON output").action(async (opts) => {
2135
- const { runContext } = await import("./context-NCV46PTY.js");
2379
+ const { runContext } = await import("./context-DQQGQ767.js");
2136
2380
  await runContext(opts);
2137
2381
  }).helpCommand(false),
2138
2382
  { hidden: true }
@@ -2147,7 +2391,7 @@ function createMemoryCommand() {
2147
2391
  memory.addCommand(
2148
2392
  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) => {
2149
2393
  await handleSyncErrors(async () => {
2150
- const { runPush } = await import("./push-HWXJGSTL.js");
2394
+ const { runPush } = await import("./push-VDADQVA5.js");
2151
2395
  await runPush(opts);
2152
2396
  });
2153
2397
  })
@@ -2155,7 +2399,7 @@ function createMemoryCommand() {
2155
2399
  memory.addCommand(
2156
2400
  new Command4("pull").description("Pull current project's memories from GitHub Gist").option("--all", "Pull all projects").option("-y, --yes", "Non-interactive (accepted for symmetry with push; pull never prompts)").action(async (opts) => {
2157
2401
  await handleSyncErrors(async () => {
2158
- const { runPull } = await import("./pull-UYLUGILU.js");
2402
+ const { runPull } = await import("./pull-O6TLQGAQ.js");
2159
2403
  await runPull(opts);
2160
2404
  });
2161
2405
  })
@@ -2164,7 +2408,7 @@ function createMemoryCommand() {
2164
2408
  sync.addCommand(
2165
2409
  new Command4("status").description("Show local vs remote memory counts per project").action(async () => {
2166
2410
  await handleSyncErrors(async () => {
2167
- const { runSyncStatus } = await import("./sync-status-VXMDWDRG.js");
2411
+ const { runSyncStatus } = await import("./sync-status-IN5434DI.js");
2168
2412
  await runSyncStatus();
2169
2413
  });
2170
2414
  })
@@ -2172,7 +2416,7 @@ function createMemoryCommand() {
2172
2416
  sync.addCommand(
2173
2417
  new Command4("clean").description("Remove a project from the sync gist").argument("<project>", "Project slug to remove").option("-y, --yes", "Skip confirmation prompt").action(async (project, opts) => {
2174
2418
  await handleSyncErrors(async () => {
2175
- const { runSyncClean } = await import("./sync-clean-GFX5F55E.js");
2419
+ const { runSyncClean } = await import("./sync-clean-2BMOFDV7.js");
2176
2420
  await runSyncClean(project, opts);
2177
2421
  });
2178
2422
  })
@@ -2182,7 +2426,7 @@ function createMemoryCommand() {
2182
2426
  }
2183
2427
 
2184
2428
  // src/cli.ts
2185
- var program = new Command5().name("claude-launchpad").description("CLI toolkit that makes Claude Code setups measurably good").version("1.4.0", "-v, --version").action(async () => {
2429
+ var program = new Command5().name("claude-launchpad").description("CLI toolkit that makes Claude Code setups measurably good").version("1.6.0", "-v, --version").action(async () => {
2186
2430
  const hasConfig = await fileExists(join9(process.cwd(), "CLAUDE.md")) || await fileExists(join9(process.cwd(), ".claude", "settings.json"));
2187
2431
  if (hasConfig) {
2188
2432
  await program.commands.find((c) => c.name() === "doctor")?.parseAsync([], { from: "user" });