claude-launchpad 1.4.0 → 1.5.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.
- package/dist/{chunk-ADZ45KHX.js → chunk-5YUKTNBM.js} +2 -2
- package/dist/{chunk-UQOBOHKN.js → chunk-GLFJ2B43.js} +24 -11
- package/dist/chunk-GLFJ2B43.js.map +1 -0
- package/dist/{chunk-RJEPJ4JE.js → chunk-WLD2PA3B.js} +3 -3
- package/dist/{chunk-WOC2PKT2.js → chunk-YF6HCPVY.js} +2 -2
- package/dist/cli.js +302 -58
- package/dist/cli.js.map +1 -1
- package/dist/commands/memory/server.js +3 -3
- package/dist/{context-NCV46PTY.js → context-4X4CLMU3.js} +5 -5
- package/dist/{install-DORMJYCU.js → install-P4TFYUJT.js} +5 -5
- package/dist/{pull-UYLUGILU.js → pull-7SR7P3US.js} +6 -6
- package/dist/{push-HWXJGSTL.js → push-SCTO5TZQ.js} +6 -6
- package/dist/{require-deps-RUXTMQUV.js → require-deps-MCFEZOIF.js} +3 -3
- package/dist/{stats-DFV24AJW.js → stats-MLWRNOHU.js} +6 -6
- package/dist/{sync-clean-GFX5F55E.js → sync-clean-2BMOFDV7.js} +2 -2
- package/dist/{sync-status-VXMDWDRG.js → sync-status-J7BVY6KF.js} +6 -6
- package/dist/{tui-ELOJ37ZA.js → tui-JE5L7SXC.js} +4 -4
- package/package.json +3 -1
- package/dist/chunk-UQOBOHKN.js.map +0 -1
- /package/dist/{chunk-ADZ45KHX.js.map → chunk-5YUKTNBM.js.map} +0 -0
- /package/dist/{chunk-RJEPJ4JE.js.map → chunk-WLD2PA3B.js.map} +0 -0
- /package/dist/{chunk-WOC2PKT2.js.map → chunk-YF6HCPVY.js.map} +0 -0
- /package/dist/{context-NCV46PTY.js.map → context-4X4CLMU3.js.map} +0 -0
- /package/dist/{install-DORMJYCU.js.map → install-P4TFYUJT.js.map} +0 -0
- /package/dist/{pull-UYLUGILU.js.map → pull-7SR7P3US.js.map} +0 -0
- /package/dist/{push-HWXJGSTL.js.map → push-SCTO5TZQ.js.map} +0 -0
- /package/dist/{require-deps-RUXTMQUV.js.map → require-deps-MCFEZOIF.js.map} +0 -0
- /package/dist/{stats-DFV24AJW.js.map → stats-MLWRNOHU.js.map} +0 -0
- /package/dist/{sync-clean-GFX5F55E.js.map → sync-clean-2BMOFDV7.js.map} +0 -0
- /package/dist/{sync-status-VXMDWDRG.js.map → sync-status-J7BVY6KF.js.map} +0 -0
- /package/dist/{tui-ELOJ37ZA.js.map → tui-JE5L7SXC.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-
|
|
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
|
|
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
|
-
|
|
1107
|
-
return
|
|
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" &&
|
|
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" &&
|
|
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" &&
|
|
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" &&
|
|
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
|
|
1213
|
-
{
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
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
|
|
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
|
|
1247
|
-
const combinedContent = [content, config.localClaudeMdContent].filter(Boolean).join("\n");
|
|
1248
|
-
|
|
1249
|
-
for (const
|
|
1250
|
-
if (
|
|
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 "## ${
|
|
1257
|
-
fix: `Add a ## ${
|
|
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
|
|
1481
|
-
import { join as join5, resolve as
|
|
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 =
|
|
1829
|
+
const devPath = resolve3(thisDir, "../../../scenarios");
|
|
1586
1830
|
if (await dirExists(devPath)) return devPath;
|
|
1587
|
-
const bundledPath =
|
|
1831
|
+
const bundledPath = resolve3(thisDir, "../scenarios");
|
|
1588
1832
|
if (await dirExists(bundledPath)) return bundledPath;
|
|
1589
|
-
const rootPath =
|
|
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 ?
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
2335
|
+
const { requireMemoryDeps } = await import("./require-deps-MCFEZOIF.js");
|
|
2092
2336
|
await requireMemoryDeps();
|
|
2093
|
-
const { startTui } = await import("./tui-
|
|
2337
|
+
const { startTui } = await import("./tui-JE5L7SXC.js");
|
|
2094
2338
|
await startTui();
|
|
2095
2339
|
return;
|
|
2096
2340
|
}
|
|
2097
2341
|
if (!isMemoryInstalled()) {
|
|
2098
|
-
const { detectExistingSetup } = await import("./install-
|
|
2342
|
+
const { detectExistingSetup } = await import("./install-P4TFYUJT.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-
|
|
2368
|
+
const { runInstall } = await import("./install-P4TFYUJT.js");
|
|
2125
2369
|
await runInstall({});
|
|
2126
2370
|
} else {
|
|
2127
|
-
const { requireMemoryDeps } = await import("./require-deps-
|
|
2371
|
+
const { requireMemoryDeps } = await import("./require-deps-MCFEZOIF.js");
|
|
2128
2372
|
await requireMemoryDeps();
|
|
2129
|
-
const { runStats } = await import("./stats-
|
|
2373
|
+
const { runStats } = await import("./stats-MLWRNOHU.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-
|
|
2379
|
+
const { runContext } = await import("./context-4X4CLMU3.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-
|
|
2394
|
+
const { runPush } = await import("./push-SCTO5TZQ.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-
|
|
2402
|
+
const { runPull } = await import("./pull-7SR7P3US.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-
|
|
2411
|
+
const { runSyncStatus } = await import("./sync-status-J7BVY6KF.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-
|
|
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.
|
|
2429
|
+
var program = new Command5().name("claude-launchpad").description("CLI toolkit that makes Claude Code setups measurably good").version("1.5.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" });
|