claude-launchpad 1.3.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/README.md +2 -2
- package/dist/{chunk-UJP5PJTA.js → chunk-5YUKTNBM.js} +3 -3
- package/dist/{chunk-V4NXT4KB.js → chunk-DXDOVWOA.js} +64 -8
- package/dist/chunk-DXDOVWOA.js.map +1 -0
- package/dist/{chunk-N6X3E5AX.js → chunk-F6SLV2FR.js} +24 -5
- package/dist/chunk-F6SLV2FR.js.map +1 -0
- package/dist/{chunk-YXPJDIMK.js → chunk-GLFJ2B43.js} +56 -12
- package/dist/chunk-GLFJ2B43.js.map +1 -0
- package/dist/{chunk-AR64LWGW.js → chunk-WLD2PA3B.js} +25 -4
- package/dist/chunk-WLD2PA3B.js.map +1 -0
- package/dist/{chunk-J765H3HZ.js → chunk-YF6HCPVY.js} +2 -2
- package/dist/{chunk-F5PNKQKW.js → chunk-YZ53W47Z.js} +7 -2
- package/dist/chunk-YZ53W47Z.js.map +1 -0
- package/dist/cli.js +313 -58
- package/dist/cli.js.map +1 -1
- package/dist/commands/memory/server.js +5 -5
- package/dist/{context-VAXF3EW3.js → context-4X4CLMU3.js} +6 -6
- package/dist/{install-M3JWBGMK.js → install-P4TFYUJT.js} +6 -6
- package/dist/install-P4TFYUJT.js.map +1 -0
- package/dist/{pull-ZQFCMK46.js → pull-7SR7P3US.js} +9 -9
- package/dist/{push-5ZJNWAE7.js → push-SCTO5TZQ.js} +40 -17
- package/dist/push-SCTO5TZQ.js.map +1 -0
- package/dist/{require-deps-QW2IU6I3.js → require-deps-MCFEZOIF.js} +3 -3
- package/dist/{stats-NPXPJNBO.js → stats-MLWRNOHU.js} +7 -7
- package/dist/{sync-clean-JQLVE4WU.js → sync-clean-2BMOFDV7.js} +2 -2
- package/dist/{sync-status-IYG7ZYC5.js → sync-status-J7BVY6KF.js} +9 -9
- package/dist/{tui-74FMIMUM.js → tui-JE5L7SXC.js} +5 -5
- package/package.json +3 -1
- package/dist/chunk-AR64LWGW.js.map +0 -1
- package/dist/chunk-F5PNKQKW.js.map +0 -1
- package/dist/chunk-N6X3E5AX.js.map +0 -1
- package/dist/chunk-V4NXT4KB.js.map +0 -1
- package/dist/chunk-YXPJDIMK.js.map +0 -1
- package/dist/install-M3JWBGMK.js.map +0 -1
- package/dist/push-5ZJNWAE7.js.map +0 -1
- /package/dist/{chunk-UJP5PJTA.js.map → chunk-5YUKTNBM.js.map} +0 -0
- /package/dist/{chunk-J765H3HZ.js.map → chunk-YF6HCPVY.js.map} +0 -0
- /package/dist/{context-VAXF3EW3.js.map → context-4X4CLMU3.js.map} +0 -0
- /package/dist/{pull-ZQFCMK46.js.map → pull-7SR7P3US.js.map} +0 -0
- /package/dist/{require-deps-QW2IU6I3.js.map → require-deps-MCFEZOIF.js.map} +0 -0
- /package/dist/{stats-NPXPJNBO.js.map → stats-MLWRNOHU.js.map} +0 -0
- /package/dist/{sync-clean-JQLVE4WU.js.map → sync-clean-2BMOFDV7.js.map} +0 -0
- /package/dist/{sync-status-IYG7ZYC5.js.map → sync-status-J7BVY6KF.js.map} +0 -0
- /package/dist/{tui-74FMIMUM.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({
|
|
@@ -1188,6 +1250,29 @@ async function analyzeMemory(config) {
|
|
|
1188
1250
|
fix: "Run `doctor --fix` to add a SessionEnd hook that pushes memories automatically"
|
|
1189
1251
|
});
|
|
1190
1252
|
}
|
|
1253
|
+
const hasStaleBackgroundedPush = config.hooks.some(
|
|
1254
|
+
(h) => h.event === "SessionEnd" && h.command?.includes("memory push") && /&\s*exit\s+0\s*$/.test(h.command)
|
|
1255
|
+
);
|
|
1256
|
+
if (hasStaleBackgroundedPush) {
|
|
1257
|
+
issues.push({
|
|
1258
|
+
analyzer: "Memory",
|
|
1259
|
+
severity: "high",
|
|
1260
|
+
message: "SessionEnd push hook is backgrounded \u2014 push gets killed before reaching the gist, deletions never sync",
|
|
1261
|
+
fix: "Run `doctor --fix` to upgrade the hook to a synchronous push"
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
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
|
+
}
|
|
1191
1276
|
}
|
|
1192
1277
|
const critical = issues.filter((i) => i.severity === "critical").length;
|
|
1193
1278
|
const high = issues.filter((i) => i.severity === "high").length;
|
|
@@ -1197,17 +1282,189 @@ async function analyzeMemory(config) {
|
|
|
1197
1282
|
return { name: "Memory", issues, score };
|
|
1198
1283
|
}
|
|
1199
1284
|
|
|
1200
|
-
// src/commands/doctor/analyzers/quality.ts
|
|
1201
|
-
var
|
|
1202
|
-
{
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
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
|
+
}
|
|
1209
1412
|
];
|
|
1210
|
-
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
|
|
1211
1468
|
var VAGUE_PATTERNS = [
|
|
1212
1469
|
{ pattern: /write (good|clean|quality|nice) code/i, label: "write good code" },
|
|
1213
1470
|
{ pattern: /be (careful|thorough|diligent)/i, label: "be careful" },
|
|
@@ -1220,7 +1477,7 @@ var SECRET_PATTERNS = [
|
|
|
1220
1477
|
{ pattern: /AKIA[0-9A-Z]{16}/, label: "AWS access key" },
|
|
1221
1478
|
{ pattern: /xoxb-[0-9]+-[a-zA-Z0-9]+/, label: "Slack bot token" }
|
|
1222
1479
|
];
|
|
1223
|
-
async function analyzeQuality(config) {
|
|
1480
|
+
async function analyzeQuality(config, projectRoot) {
|
|
1224
1481
|
const issues = [];
|
|
1225
1482
|
const content = config.claudeMdContent;
|
|
1226
1483
|
if (content === null) {
|
|
@@ -1232,18 +1489,16 @@ async function analyzeQuality(config) {
|
|
|
1232
1489
|
});
|
|
1233
1490
|
return { name: "CLAUDE.md Quality", issues, score: 0 };
|
|
1234
1491
|
}
|
|
1235
|
-
const
|
|
1236
|
-
const combinedContent = [content, config.localClaudeMdContent].filter(Boolean).join("\n");
|
|
1237
|
-
|
|
1238
|
-
for (const
|
|
1239
|
-
if (
|
|
1240
|
-
sectionsFound++;
|
|
1241
|
-
} 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)) {
|
|
1242
1497
|
issues.push({
|
|
1243
1498
|
analyzer: "Quality",
|
|
1244
1499
|
severity: "medium",
|
|
1245
|
-
message: `Missing "## ${
|
|
1246
|
-
fix: `Add a ## ${
|
|
1500
|
+
message: `Missing "## ${rule.name}" section \u2014 ${rule.why}`,
|
|
1501
|
+
fix: `Add a ## ${rule.name} section to CLAUDE.md`
|
|
1247
1502
|
});
|
|
1248
1503
|
}
|
|
1249
1504
|
}
|
|
@@ -1345,7 +1600,7 @@ async function runAndDisplay(projectRoot) {
|
|
|
1345
1600
|
}
|
|
1346
1601
|
const results = await Promise.all([
|
|
1347
1602
|
analyzeBudget(config),
|
|
1348
|
-
analyzeQuality(config),
|
|
1603
|
+
analyzeQuality(config, projectRoot),
|
|
1349
1604
|
analyzeSettings(config),
|
|
1350
1605
|
analyzeHooks(config),
|
|
1351
1606
|
analyzeRules(config),
|
|
@@ -1375,14 +1630,14 @@ function createDoctorCommand() {
|
|
|
1375
1630
|
}
|
|
1376
1631
|
const results = await Promise.all([
|
|
1377
1632
|
analyzeBudget(config),
|
|
1378
|
-
analyzeQuality(config),
|
|
1633
|
+
analyzeQuality(config, opts.path),
|
|
1379
1634
|
analyzeSettings(config),
|
|
1380
1635
|
analyzeHooks(config),
|
|
1381
1636
|
analyzeRules(config),
|
|
1382
1637
|
analyzePermissions(config),
|
|
1383
1638
|
analyzeMcp(config)
|
|
1384
1639
|
]);
|
|
1385
|
-
const memoryResult = await analyzeMemory(config);
|
|
1640
|
+
const memoryResult = await analyzeMemory(config, opts.path);
|
|
1386
1641
|
if (memoryResult) {
|
|
1387
1642
|
results.push(memoryResult);
|
|
1388
1643
|
}
|
|
@@ -1433,14 +1688,14 @@ function createDoctorCommand() {
|
|
|
1433
1688
|
const updatedConfig = await parseClaudeConfig(opts.path);
|
|
1434
1689
|
const updatedResults = await Promise.all([
|
|
1435
1690
|
analyzeBudget(updatedConfig),
|
|
1436
|
-
analyzeQuality(updatedConfig),
|
|
1691
|
+
analyzeQuality(updatedConfig, opts.path),
|
|
1437
1692
|
analyzeSettings(updatedConfig),
|
|
1438
1693
|
analyzeHooks(updatedConfig),
|
|
1439
1694
|
analyzeRules(updatedConfig),
|
|
1440
1695
|
analyzePermissions(updatedConfig),
|
|
1441
1696
|
analyzeMcp(updatedConfig)
|
|
1442
1697
|
]);
|
|
1443
|
-
const updatedMemoryResult = await analyzeMemory(updatedConfig);
|
|
1698
|
+
const updatedMemoryResult = await analyzeMemory(updatedConfig, opts.path);
|
|
1444
1699
|
if (updatedMemoryResult) {
|
|
1445
1700
|
updatedResults.push(updatedMemoryResult);
|
|
1446
1701
|
}
|
|
@@ -1466,8 +1721,8 @@ import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
|
1466
1721
|
import { join as join7 } from "path";
|
|
1467
1722
|
|
|
1468
1723
|
// src/commands/eval/loader.ts
|
|
1469
|
-
import { readFile as
|
|
1470
|
-
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";
|
|
1471
1726
|
import { fileURLToPath } from "url";
|
|
1472
1727
|
import { parse as parseYaml } from "yaml";
|
|
1473
1728
|
|
|
@@ -1571,11 +1826,11 @@ var ScenarioError = class extends Error {
|
|
|
1571
1826
|
// src/commands/eval/loader.ts
|
|
1572
1827
|
async function findScenariosDir() {
|
|
1573
1828
|
const thisDir = dirname2(fileURLToPath(import.meta.url));
|
|
1574
|
-
const devPath =
|
|
1829
|
+
const devPath = resolve3(thisDir, "../../../scenarios");
|
|
1575
1830
|
if (await dirExists(devPath)) return devPath;
|
|
1576
|
-
const bundledPath =
|
|
1831
|
+
const bundledPath = resolve3(thisDir, "../scenarios");
|
|
1577
1832
|
if (await dirExists(bundledPath)) return bundledPath;
|
|
1578
|
-
const rootPath =
|
|
1833
|
+
const rootPath = resolve3(thisDir, "../../scenarios");
|
|
1579
1834
|
if (await dirExists(rootPath)) return rootPath;
|
|
1580
1835
|
return devPath;
|
|
1581
1836
|
}
|
|
@@ -1589,7 +1844,7 @@ async function dirExists(path) {
|
|
|
1589
1844
|
}
|
|
1590
1845
|
async function loadScenarios(options) {
|
|
1591
1846
|
const { suite, customPath } = options;
|
|
1592
|
-
const scenarioDir = customPath ?
|
|
1847
|
+
const scenarioDir = customPath ? resolve3(customPath) : await findScenariosDir();
|
|
1593
1848
|
const dirs = suite ? [join5(scenarioDir, suite)] : await getSubdirectories(scenarioDir);
|
|
1594
1849
|
const allDirs = [scenarioDir, ...dirs];
|
|
1595
1850
|
const scenarios = [];
|
|
@@ -1597,7 +1852,7 @@ async function loadScenarios(options) {
|
|
|
1597
1852
|
const files = await listYamlFiles(dir);
|
|
1598
1853
|
for (const file of files) {
|
|
1599
1854
|
try {
|
|
1600
|
-
const content = await
|
|
1855
|
+
const content = await readFile4(file, "utf-8");
|
|
1601
1856
|
const raw = parseYaml(content);
|
|
1602
1857
|
const scenario = validateScenario(raw, file);
|
|
1603
1858
|
scenarios.push(scenario);
|
|
@@ -1627,7 +1882,7 @@ async function listYamlFiles(dir) {
|
|
|
1627
1882
|
}
|
|
1628
1883
|
|
|
1629
1884
|
// src/commands/eval/runner.ts
|
|
1630
|
-
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";
|
|
1631
1886
|
import { join as join6, dirname as dirname3 } from "path";
|
|
1632
1887
|
import { tmpdir } from "os";
|
|
1633
1888
|
import { randomUUID } from "crypto";
|
|
@@ -1796,7 +2051,7 @@ async function evaluateSingleCheck(check, sandboxDir) {
|
|
|
1796
2051
|
async function checkGrep(check, sandboxDir) {
|
|
1797
2052
|
if (!check.pattern) return false;
|
|
1798
2053
|
try {
|
|
1799
|
-
const content = await
|
|
2054
|
+
const content = await readFile5(join6(sandboxDir, check.target), "utf-8");
|
|
1800
2055
|
let found;
|
|
1801
2056
|
try {
|
|
1802
2057
|
found = new RegExp(check.pattern).test(content);
|
|
@@ -1810,7 +2065,7 @@ async function checkGrep(check, sandboxDir) {
|
|
|
1810
2065
|
}
|
|
1811
2066
|
async function checkFilePresence(check, sandboxDir) {
|
|
1812
2067
|
try {
|
|
1813
|
-
await
|
|
2068
|
+
await readFile5(join6(sandboxDir, check.target));
|
|
1814
2069
|
return check.expect === "present";
|
|
1815
2070
|
} catch {
|
|
1816
2071
|
return check.expect === "absent";
|
|
@@ -1821,7 +2076,7 @@ async function checkMaxLines(check, sandboxDir) {
|
|
|
1821
2076
|
try {
|
|
1822
2077
|
const files = await listAllFiles(join6(sandboxDir, check.target));
|
|
1823
2078
|
for (const file of files) {
|
|
1824
|
-
const content = await
|
|
2079
|
+
const content = await readFile5(file, "utf-8");
|
|
1825
2080
|
if (content.split("\n").length > maxLines) {
|
|
1826
2081
|
return check.expect === "absent";
|
|
1827
2082
|
}
|
|
@@ -2077,14 +2332,14 @@ function createMemoryCommand() {
|
|
|
2077
2332
|
log.error("Knowledge base not set up yet. Run `claude-launchpad memory` first.");
|
|
2078
2333
|
return;
|
|
2079
2334
|
}
|
|
2080
|
-
const { requireMemoryDeps } = await import("./require-deps-
|
|
2335
|
+
const { requireMemoryDeps } = await import("./require-deps-MCFEZOIF.js");
|
|
2081
2336
|
await requireMemoryDeps();
|
|
2082
|
-
const { startTui } = await import("./tui-
|
|
2337
|
+
const { startTui } = await import("./tui-JE5L7SXC.js");
|
|
2083
2338
|
await startTui();
|
|
2084
2339
|
return;
|
|
2085
2340
|
}
|
|
2086
2341
|
if (!isMemoryInstalled()) {
|
|
2087
|
-
const { detectExistingSetup } = await import("./install-
|
|
2342
|
+
const { detectExistingSetup } = await import("./install-P4TFYUJT.js");
|
|
2088
2343
|
const existing = detectExistingSetup(process.cwd());
|
|
2089
2344
|
if (existing) {
|
|
2090
2345
|
const location = existing === "local" ? ".claude/CLAUDE.md + settings.local.json" : "CLAUDE.md + settings.json";
|
|
@@ -2110,18 +2365,18 @@ function createMemoryCommand() {
|
|
|
2110
2365
|
log.info("Skipped.");
|
|
2111
2366
|
return;
|
|
2112
2367
|
}
|
|
2113
|
-
const { runInstall } = await import("./install-
|
|
2368
|
+
const { runInstall } = await import("./install-P4TFYUJT.js");
|
|
2114
2369
|
await runInstall({});
|
|
2115
2370
|
} else {
|
|
2116
|
-
const { requireMemoryDeps } = await import("./require-deps-
|
|
2371
|
+
const { requireMemoryDeps } = await import("./require-deps-MCFEZOIF.js");
|
|
2117
2372
|
await requireMemoryDeps();
|
|
2118
|
-
const { runStats } = await import("./stats-
|
|
2373
|
+
const { runStats } = await import("./stats-MLWRNOHU.js");
|
|
2119
2374
|
await runStats({});
|
|
2120
2375
|
}
|
|
2121
2376
|
});
|
|
2122
2377
|
memory.addCommand(
|
|
2123
2378
|
new Command4("context").description("Load session context (hook handler)").option("--json", "JSON output").action(async (opts) => {
|
|
2124
|
-
const { runContext } = await import("./context-
|
|
2379
|
+
const { runContext } = await import("./context-4X4CLMU3.js");
|
|
2125
2380
|
await runContext(opts);
|
|
2126
2381
|
}).helpCommand(false),
|
|
2127
2382
|
{ hidden: true }
|
|
@@ -2136,7 +2391,7 @@ function createMemoryCommand() {
|
|
|
2136
2391
|
memory.addCommand(
|
|
2137
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) => {
|
|
2138
2393
|
await handleSyncErrors(async () => {
|
|
2139
|
-
const { runPush } = await import("./push-
|
|
2394
|
+
const { runPush } = await import("./push-SCTO5TZQ.js");
|
|
2140
2395
|
await runPush(opts);
|
|
2141
2396
|
});
|
|
2142
2397
|
})
|
|
@@ -2144,7 +2399,7 @@ function createMemoryCommand() {
|
|
|
2144
2399
|
memory.addCommand(
|
|
2145
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) => {
|
|
2146
2401
|
await handleSyncErrors(async () => {
|
|
2147
|
-
const { runPull } = await import("./pull-
|
|
2402
|
+
const { runPull } = await import("./pull-7SR7P3US.js");
|
|
2148
2403
|
await runPull(opts);
|
|
2149
2404
|
});
|
|
2150
2405
|
})
|
|
@@ -2153,7 +2408,7 @@ function createMemoryCommand() {
|
|
|
2153
2408
|
sync.addCommand(
|
|
2154
2409
|
new Command4("status").description("Show local vs remote memory counts per project").action(async () => {
|
|
2155
2410
|
await handleSyncErrors(async () => {
|
|
2156
|
-
const { runSyncStatus } = await import("./sync-status-
|
|
2411
|
+
const { runSyncStatus } = await import("./sync-status-J7BVY6KF.js");
|
|
2157
2412
|
await runSyncStatus();
|
|
2158
2413
|
});
|
|
2159
2414
|
})
|
|
@@ -2161,7 +2416,7 @@ function createMemoryCommand() {
|
|
|
2161
2416
|
sync.addCommand(
|
|
2162
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) => {
|
|
2163
2418
|
await handleSyncErrors(async () => {
|
|
2164
|
-
const { runSyncClean } = await import("./sync-clean-
|
|
2419
|
+
const { runSyncClean } = await import("./sync-clean-2BMOFDV7.js");
|
|
2165
2420
|
await runSyncClean(project, opts);
|
|
2166
2421
|
});
|
|
2167
2422
|
})
|
|
@@ -2171,7 +2426,7 @@ function createMemoryCommand() {
|
|
|
2171
2426
|
}
|
|
2172
2427
|
|
|
2173
2428
|
// src/cli.ts
|
|
2174
|
-
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 () => {
|
|
2175
2430
|
const hasConfig = await fileExists(join9(process.cwd(), "CLAUDE.md")) || await fileExists(join9(process.cwd(), ".claude", "settings.json"));
|
|
2176
2431
|
if (hasConfig) {
|
|
2177
2432
|
await program.commands.find((c) => c.name() === "doctor")?.parseAsync([], { from: "user" });
|