@harness-engineering/cli 1.21.0 → 1.22.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/agents/skills/claude-code/cleanup-dead-code/skill.yaml +3 -0
- package/dist/agents/skills/claude-code/detect-doc-drift/skill.yaml +5 -0
- package/dist/agents/skills/claude-code/enforce-architecture/skill.yaml +13 -0
- package/dist/agents/skills/claude-code/harness-code-review/skill.yaml +5 -0
- package/dist/agents/skills/claude-code/harness-codebase-cleanup/skill.yaml +5 -0
- package/dist/agents/skills/claude-code/harness-debugging/skill.yaml +5 -0
- package/dist/agents/skills/claude-code/harness-dependency-health/skill.yaml +9 -0
- package/dist/agents/skills/claude-code/harness-hotspot-detector/skill.yaml +9 -0
- package/dist/agents/skills/claude-code/harness-integrity/skill.yaml +5 -0
- package/dist/agents/skills/claude-code/harness-perf/skill.yaml +3 -0
- package/dist/agents/skills/claude-code/harness-refactoring/skill.yaml +9 -0
- package/dist/agents/skills/claude-code/harness-security-scan/skill.yaml +3 -0
- package/dist/agents/skills/claude-code/harness-soundness-review/skill.yaml +5 -0
- package/dist/agents/skills/claude-code/harness-supply-chain-audit/skill.yaml +3 -0
- package/dist/agents/skills/claude-code/harness-tdd/skill.yaml +3 -0
- package/dist/agents/skills/codex/cleanup-dead-code/skill.yaml +3 -0
- package/dist/agents/skills/codex/detect-doc-drift/skill.yaml +5 -0
- package/dist/agents/skills/codex/enforce-architecture/skill.yaml +13 -0
- package/dist/agents/skills/codex/harness-code-review/skill.yaml +5 -0
- package/dist/agents/skills/codex/harness-codebase-cleanup/skill.yaml +5 -0
- package/dist/agents/skills/codex/harness-debugging/skill.yaml +5 -0
- package/dist/agents/skills/codex/harness-dependency-health/skill.yaml +9 -0
- package/dist/agents/skills/codex/harness-hotspot-detector/skill.yaml +9 -0
- package/dist/agents/skills/codex/harness-integrity/skill.yaml +5 -0
- package/dist/agents/skills/codex/harness-perf/skill.yaml +3 -0
- package/dist/agents/skills/codex/harness-refactoring/skill.yaml +9 -0
- package/dist/agents/skills/codex/harness-security-scan/skill.yaml +3 -0
- package/dist/agents/skills/codex/harness-soundness-review/skill.yaml +5 -0
- package/dist/agents/skills/codex/harness-supply-chain-audit/skill.yaml +3 -0
- package/dist/agents/skills/codex/harness-tdd/skill.yaml +3 -0
- package/dist/agents/skills/cursor/cleanup-dead-code/skill.yaml +3 -0
- package/dist/agents/skills/cursor/detect-doc-drift/skill.yaml +5 -0
- package/dist/agents/skills/cursor/enforce-architecture/skill.yaml +13 -0
- package/dist/agents/skills/cursor/harness-code-review/skill.yaml +5 -0
- package/dist/agents/skills/cursor/harness-codebase-cleanup/skill.yaml +5 -0
- package/dist/agents/skills/cursor/harness-debugging/skill.yaml +5 -0
- package/dist/agents/skills/cursor/harness-dependency-health/skill.yaml +9 -0
- package/dist/agents/skills/cursor/harness-hotspot-detector/skill.yaml +9 -0
- package/dist/agents/skills/cursor/harness-integrity/skill.yaml +5 -0
- package/dist/agents/skills/cursor/harness-perf/skill.yaml +3 -0
- package/dist/agents/skills/cursor/harness-refactoring/skill.yaml +9 -0
- package/dist/agents/skills/cursor/harness-security-scan/skill.yaml +3 -0
- package/dist/agents/skills/cursor/harness-soundness-review/skill.yaml +5 -0
- package/dist/agents/skills/cursor/harness-supply-chain-audit/skill.yaml +3 -0
- package/dist/agents/skills/cursor/harness-tdd/skill.yaml +3 -0
- package/dist/agents/skills/gemini-cli/cleanup-dead-code/skill.yaml +3 -0
- package/dist/agents/skills/gemini-cli/detect-doc-drift/skill.yaml +5 -0
- package/dist/agents/skills/gemini-cli/enforce-architecture/skill.yaml +13 -0
- package/dist/agents/skills/gemini-cli/harness-code-review/skill.yaml +5 -0
- package/dist/agents/skills/gemini-cli/harness-codebase-cleanup/skill.yaml +5 -0
- package/dist/agents/skills/gemini-cli/harness-debugging/skill.yaml +5 -0
- package/dist/agents/skills/gemini-cli/harness-dependency-health/skill.yaml +9 -0
- package/dist/agents/skills/gemini-cli/harness-hotspot-detector/skill.yaml +9 -0
- package/dist/agents/skills/gemini-cli/harness-integrity/skill.yaml +5 -0
- package/dist/agents/skills/gemini-cli/harness-perf/skill.yaml +3 -0
- package/dist/agents/skills/gemini-cli/harness-refactoring/skill.yaml +9 -0
- package/dist/agents/skills/gemini-cli/harness-security-scan/skill.yaml +3 -0
- package/dist/agents/skills/gemini-cli/harness-soundness-review/skill.yaml +5 -0
- package/dist/agents/skills/gemini-cli/harness-supply-chain-audit/skill.yaml +3 -0
- package/dist/agents/skills/gemini-cli/harness-tdd/skill.yaml +3 -0
- package/dist/{agents-md-TDTLYAQU.js → agents-md-PM7LO74M.js} +2 -1
- package/dist/{architecture-NANP4XPE.js → architecture-OVOCDTI6.js} +3 -2
- package/dist/assess-project-R2OZIDDS.js +9 -0
- package/dist/bin/harness-mcp.js +15 -13
- package/dist/bin/harness.js +21 -19
- package/dist/{check-phase-gate-I4NQOCSU.js → check-phase-gate-7JQ6EW5R.js} +4 -3
- package/dist/{chunk-M6TIO6NF.js → chunk-2PAPHA77.js} +1 -1
- package/dist/chunk-5FBWWMY2.js +293 -0
- package/dist/{chunk-YF5ROTWR.js → chunk-5QTWFO24.js} +8 -8
- package/dist/{chunk-CZZXE6BL.js → chunk-ASS5TD2Y.js} +1 -1
- package/dist/{chunk-L6LTNZQZ.js → chunk-B4WHXHF7.js} +1 -1
- package/dist/{chunk-TMSGI27F.js → chunk-DJEBBENF.js} +967 -385
- package/dist/{chunk-H6LXAH66.js → chunk-DXYOAQQC.js} +1 -1
- package/dist/{chunk-UVJFBKCX.js → chunk-FSLFBLYW.js} +7 -7
- package/dist/{chunk-SZ5TGZMI.js → chunk-GRJ7A4WT.js} +17 -2
- package/dist/{chunk-WXI5ONCU.js → chunk-IK5GSLW6.js} +4 -4
- package/dist/{chunk-SPUK5W4W.js → chunk-J7W4LTRK.js} +2 -2
- package/dist/{chunk-7G2ZUTZA.js → chunk-PUOMFNRO.js} +26 -3
- package/dist/{chunk-HKUX2X7O.js → chunk-SE4YPMLH.js} +9 -1
- package/dist/{chunk-UEKQ5G3V.js → chunk-SOTTK27D.js} +459 -37
- package/dist/{chunk-LRG3B43J.js → chunk-T5QWCVGK.js} +1 -1
- package/dist/{dist-U7EAO6T2.js → chunk-TEZI27SA.js} +401 -60
- package/dist/{chunk-YZYBQZVL.js → chunk-U44JNY3Y.js} +1539 -587
- package/dist/{chunk-HUDEBSR2.js → chunk-W6MPLFXU.js} +3 -3
- package/dist/{chunk-6GEYPBDU.js → chunk-ZEIEUCZL.js} +9 -9
- package/dist/{ci-workflow-Z4IUJBZL.js → ci-workflow-OTTEERPF.js} +2 -1
- package/dist/{create-skill-NDXQSTIK.js → create-skill-U3XCFRZN.js} +2 -2
- package/dist/dist-IA6XYKNO.js +92 -0
- package/dist/{dist-KV2ICL5X.js → dist-LCR2IO7U.js} +56 -3
- package/dist/{docs-2PCZVSGB.js → docs-CHAYSGOP.js} +4 -3
- package/dist/{engine-EOXMI5MD.js → engine-4MY2U5RZ.js} +2 -1
- package/dist/{entropy-VGXXBIGX.js → entropy-AKSZG7G5.js} +3 -2
- package/dist/{feedback-VTSPL3O7.js → feedback-QGCSW7SB.js} +1 -1
- package/dist/{generate-agent-definitions-QICSCGXB.js → generate-agent-definitions-KU6X2UQN.js} +2 -1
- package/dist/{graph-loader-KMHDQYDT.js → graph-loader-FJN4H7Y4.js} +1 -1
- package/dist/index.d.ts +58 -2
- package/dist/index.js +27 -23
- package/dist/{loader-7S4FYAPP.js → loader-AV5XEMER.js} +2 -1
- package/dist/{mcp-DF25USTE.js → mcp-LWHVQRG7.js} +15 -13
- package/dist/{performance-RV4DUMFI.js → performance-ETZVXXGQ.js} +4 -3
- package/dist/{review-pipeline-7KQJB4SI.js → review-pipeline-3ZS3GJSP.js} +1 -1
- package/dist/{runtime-XKOHGGRC.js → runtime-KQTJRK3H.js} +2 -1
- package/dist/{security-NLWTMK3G.js → security-LJCLZES6.js} +1 -1
- package/dist/{skill-executor-XEVDGXUM.js → skill-executor-2BZQLHYN.js} +2 -2
- package/dist/{validate-VHFE6J6O.js → validate-4IA5RPEX.js} +3 -2
- package/dist/{validate-cross-check-PFRKABCS.js → validate-cross-check-VX2BAHQI.js} +2 -1
- package/package.json +4 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
detectEntropyDefinition,
|
|
3
3
|
handleDetectEntropy
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-IK5GSLW6.js";
|
|
5
5
|
import {
|
|
6
6
|
checkPerformanceDefinition,
|
|
7
7
|
getCriticalPathsDefinition,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
handleGetPerfBaselines,
|
|
12
12
|
handleUpdatePerfBaselines,
|
|
13
13
|
updatePerfBaselinesDefinition
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-FSLFBLYW.js";
|
|
15
15
|
import {
|
|
16
16
|
analyzeDiffDefinition,
|
|
17
17
|
createSelfReviewDefinition,
|
|
@@ -19,15 +19,19 @@ import {
|
|
|
19
19
|
handleCreateSelfReview,
|
|
20
20
|
handleRequestPeerReview,
|
|
21
21
|
requestPeerReviewDefinition
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-ZEIEUCZL.js";
|
|
23
23
|
import {
|
|
24
24
|
handleRunSecurityScan,
|
|
25
25
|
runSecurityScanDefinition
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-DXYOAQQC.js";
|
|
27
27
|
import {
|
|
28
28
|
handleRunCodeReview,
|
|
29
29
|
runCodeReviewDefinition
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-B4WHXHF7.js";
|
|
31
|
+
import {
|
|
32
|
+
assessProjectDefinition,
|
|
33
|
+
handleAssessProject
|
|
34
|
+
} from "./chunk-5FBWWMY2.js";
|
|
31
35
|
import {
|
|
32
36
|
GENERATED_HEADER_CLAUDE,
|
|
33
37
|
GENERATED_HEADER_CODEX,
|
|
@@ -40,25 +44,25 @@ import {
|
|
|
40
44
|
import {
|
|
41
45
|
handleValidateProject,
|
|
42
46
|
validateToolDefinition
|
|
43
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-J7W4LTRK.js";
|
|
44
48
|
import {
|
|
45
49
|
loadGraphStore
|
|
46
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-ASS5TD2Y.js";
|
|
47
51
|
import {
|
|
48
52
|
checkDependenciesDefinition,
|
|
49
53
|
handleCheckDependencies
|
|
50
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-W6MPLFXU.js";
|
|
51
55
|
import {
|
|
52
56
|
resolveProjectConfig
|
|
53
57
|
} from "./chunk-H7Y5CKTM.js";
|
|
54
58
|
import {
|
|
55
59
|
checkDocsDefinition,
|
|
56
60
|
handleCheckDocs
|
|
57
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-5QTWFO24.js";
|
|
58
62
|
import {
|
|
59
63
|
TrackerConfigSchema,
|
|
60
64
|
resolveConfig
|
|
61
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-GRJ7A4WT.js";
|
|
62
66
|
import {
|
|
63
67
|
resultToMcpResponse
|
|
64
68
|
} from "./chunk-IDZNPTYD.js";
|
|
@@ -81,13 +85,13 @@ import {
|
|
|
81
85
|
} from "./chunk-3WGJMBKH.js";
|
|
82
86
|
import {
|
|
83
87
|
SkillMetadataSchema
|
|
84
|
-
} from "./chunk-
|
|
88
|
+
} from "./chunk-SE4YPMLH.js";
|
|
85
89
|
import {
|
|
86
90
|
DESTRUCTIVE_BASH,
|
|
87
91
|
checkTaint,
|
|
88
92
|
scanForInjection,
|
|
89
93
|
writeTaint
|
|
90
|
-
} from "./chunk-
|
|
94
|
+
} from "./chunk-U44JNY3Y.js";
|
|
91
95
|
import {
|
|
92
96
|
Err,
|
|
93
97
|
Ok
|
|
@@ -557,7 +561,7 @@ ${skippedMsg}`
|
|
|
557
561
|
async function handleInitProject(input) {
|
|
558
562
|
const i = input;
|
|
559
563
|
try {
|
|
560
|
-
const { TemplateEngine } = await import("./engine-
|
|
564
|
+
const { TemplateEngine } = await import("./engine-4MY2U5RZ.js");
|
|
561
565
|
const engine = new TemplateEngine(resolveTemplatesDir());
|
|
562
566
|
const safePath = sanitizePath(i.path);
|
|
563
567
|
const detected = tryDetectFramework(engine, safePath, i);
|
|
@@ -578,7 +582,7 @@ var listPersonasDefinition = {
|
|
|
578
582
|
inputSchema: { type: "object", properties: {} }
|
|
579
583
|
};
|
|
580
584
|
async function handleListPersonas() {
|
|
581
|
-
const { listPersonas } = await import("./loader-
|
|
585
|
+
const { listPersonas } = await import("./loader-AV5XEMER.js");
|
|
582
586
|
const result = listPersonas(resolvePersonasDir());
|
|
583
587
|
return resultToMcpResponse(result);
|
|
584
588
|
}
|
|
@@ -602,10 +606,10 @@ async function handleGeneratePersonaArtifacts(input) {
|
|
|
602
606
|
if (!/^[a-z0-9][a-z0-9._-]*$/i.test(input.name)) {
|
|
603
607
|
return resultToMcpResponse(Err(new Error(`Invalid persona name: ${input.name}`)));
|
|
604
608
|
}
|
|
605
|
-
const { loadPersona } = await import("./loader-
|
|
606
|
-
const { generateRuntime } = await import("./runtime-
|
|
607
|
-
const { generateAgentsMd } = await import("./agents-md-
|
|
608
|
-
const { generateCIWorkflow } = await import("./ci-workflow-
|
|
609
|
+
const { loadPersona } = await import("./loader-AV5XEMER.js");
|
|
610
|
+
const { generateRuntime } = await import("./runtime-KQTJRK3H.js");
|
|
611
|
+
const { generateAgentsMd } = await import("./agents-md-PM7LO74M.js");
|
|
612
|
+
const { generateCIWorkflow } = await import("./ci-workflow-OTTEERPF.js");
|
|
609
613
|
const personasDir = resolvePersonasDir();
|
|
610
614
|
const filePath = path3.join(personasDir, `${input.name}.yaml`);
|
|
611
615
|
if (!filePath.startsWith(personasDir)) {
|
|
@@ -660,9 +664,9 @@ async function handleRunPersona(input) {
|
|
|
660
664
|
if (!/^[a-z0-9][a-z0-9._-]*$/i.test(input.persona)) {
|
|
661
665
|
return resultToMcpResponse(Err(new Error(`Invalid persona name: ${input.persona}`)));
|
|
662
666
|
}
|
|
663
|
-
const { loadPersona } = await import("./loader-
|
|
667
|
+
const { loadPersona } = await import("./loader-AV5XEMER.js");
|
|
664
668
|
const { runPersona } = await import("./runner-VMYLHWOC.js");
|
|
665
|
-
const { executeSkill } = await import("./skill-executor-
|
|
669
|
+
const { executeSkill } = await import("./skill-executor-2BZQLHYN.js");
|
|
666
670
|
const personasDir = resolvePersonasDir();
|
|
667
671
|
const filePath = path3.join(personasDir, `${input.persona}.yaml`);
|
|
668
672
|
if (!filePath.startsWith(personasDir)) {
|
|
@@ -842,7 +846,26 @@ var TIER_1_SKILLS = /* @__PURE__ */ new Set([
|
|
|
842
846
|
function isTier1Skill(skillName) {
|
|
843
847
|
return TIER_1_SKILLS.has(skillName);
|
|
844
848
|
}
|
|
845
|
-
function
|
|
849
|
+
function computeHealthScore(entry, snapshot) {
|
|
850
|
+
if (entry.addresses.length === 0) return 0;
|
|
851
|
+
const activeSignals = new Set(snapshot.signals);
|
|
852
|
+
const hasWeights = entry.addresses.some((a) => a.weight !== void 0);
|
|
853
|
+
if (hasWeights) {
|
|
854
|
+
let totalWeight = 0;
|
|
855
|
+
let matchedWeight = 0;
|
|
856
|
+
for (const addr of entry.addresses) {
|
|
857
|
+
const w = addr.weight ?? 0.5;
|
|
858
|
+
totalWeight += w;
|
|
859
|
+
if (activeSignals.has(addr.signal)) {
|
|
860
|
+
matchedWeight += w;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
return totalWeight > 0 ? matchedWeight / totalWeight : 0;
|
|
864
|
+
}
|
|
865
|
+
const matched = entry.addresses.filter((a) => activeSignals.has(a.signal)).length;
|
|
866
|
+
return matched / entry.addresses.length;
|
|
867
|
+
}
|
|
868
|
+
function scoreSkill(entry, queryTerms, profile, recentFiles, skillName, healthSnapshot) {
|
|
846
869
|
const matchedKeywords = entry.keywords.filter(
|
|
847
870
|
(kw) => queryTerms.some(
|
|
848
871
|
(term) => kw.toLowerCase().includes(term.toLowerCase()) || term.toLowerCase().includes(kw.toLowerCase())
|
|
@@ -881,7 +904,12 @@ function scoreSkill(entry, queryTerms, profile, recentFiles, skillName) {
|
|
|
881
904
|
});
|
|
882
905
|
recencyBoost = hasRecentMatch ? 1 : 0;
|
|
883
906
|
}
|
|
884
|
-
|
|
907
|
+
let score = 0.35 * keywordScore + 0.2 * nameScore + 0.1 * descScore + 0.2 * stackScore + 0.15 * recencyBoost;
|
|
908
|
+
if (healthSnapshot) {
|
|
909
|
+
const healthScore = computeHealthScore(entry, healthSnapshot);
|
|
910
|
+
score = 0.7 * score + 0.3 * healthScore;
|
|
911
|
+
}
|
|
912
|
+
return score;
|
|
885
913
|
}
|
|
886
914
|
function suggest(index, taskDescription, profile, recentFiles, config) {
|
|
887
915
|
const queryTerms = taskDescription.toLowerCase().split(/\s+/).filter((t) => t.length > 2);
|
|
@@ -950,7 +978,9 @@ function parseSkillEntry(yamlPath, source, tierOverrides) {
|
|
|
950
978
|
stackSignals: meta.stack_signals ?? [],
|
|
951
979
|
cognitiveMode: meta.cognitive_mode,
|
|
952
980
|
phases: (meta.phases ?? []).map((p) => p.name),
|
|
953
|
-
source
|
|
981
|
+
source,
|
|
982
|
+
addresses: meta.addresses ?? [],
|
|
983
|
+
dependsOn: meta.depends_on ?? []
|
|
954
984
|
};
|
|
955
985
|
}
|
|
956
986
|
function scanDirectory(dir, source, index, tierOverrides) {
|
|
@@ -1174,7 +1204,7 @@ var createSkillDefinition = {
|
|
|
1174
1204
|
};
|
|
1175
1205
|
async function handleCreateSkill(input) {
|
|
1176
1206
|
try {
|
|
1177
|
-
const { generateSkillFiles } = await import("./create-skill-
|
|
1207
|
+
const { generateSkillFiles } = await import("./create-skill-U3XCFRZN.js");
|
|
1178
1208
|
const result = generateSkillFiles({
|
|
1179
1209
|
name: input.name,
|
|
1180
1210
|
description: input.description,
|
|
@@ -1292,7 +1322,7 @@ async function autoSyncRoadmap(projectPath) {
|
|
|
1292
1322
|
try {
|
|
1293
1323
|
const roadmapFile = path11.join(projectPath, "docs", "roadmap.md");
|
|
1294
1324
|
if (!fs10.existsSync(roadmapFile)) return;
|
|
1295
|
-
const { parseRoadmap, serializeRoadmap, syncRoadmap, applySyncChanges } = await import("./dist-
|
|
1325
|
+
const { parseRoadmap, serializeRoadmap, syncRoadmap, applySyncChanges } = await import("./dist-LCR2IO7U.js");
|
|
1296
1326
|
const raw = fs10.readFileSync(roadmapFile, "utf-8");
|
|
1297
1327
|
const parseResult = parseRoadmap(raw);
|
|
1298
1328
|
if (!parseResult.ok) return;
|
|
@@ -1319,7 +1349,7 @@ async function triggerExternalSync(projectPath, roadmapFile) {
|
|
|
1319
1349
|
}
|
|
1320
1350
|
const token = process.env.GITHUB_TOKEN;
|
|
1321
1351
|
if (!token) return;
|
|
1322
|
-
const { fullSync, GitHubIssuesSyncAdapter } = await import("./dist-
|
|
1352
|
+
const { fullSync, GitHubIssuesSyncAdapter } = await import("./dist-LCR2IO7U.js");
|
|
1323
1353
|
const adapter = new GitHubIssuesSyncAdapter({
|
|
1324
1354
|
token,
|
|
1325
1355
|
config: trackerConfig
|
|
@@ -1431,12 +1461,12 @@ var manageStateDefinition = {
|
|
|
1431
1461
|
}
|
|
1432
1462
|
};
|
|
1433
1463
|
async function handleShow(projectPath, input) {
|
|
1434
|
-
const { loadState } = await import("./dist-
|
|
1464
|
+
const { loadState } = await import("./dist-LCR2IO7U.js");
|
|
1435
1465
|
return resultToMcpResponse(await loadState(projectPath, input.stream, input.session));
|
|
1436
1466
|
}
|
|
1437
1467
|
async function handleLearn(projectPath, input) {
|
|
1438
1468
|
if (!input.learning) return mcpError("Error: learning is required for learn action");
|
|
1439
|
-
const { appendLearning } = await import("./dist-
|
|
1469
|
+
const { appendLearning } = await import("./dist-LCR2IO7U.js");
|
|
1440
1470
|
const result = await appendLearning(
|
|
1441
1471
|
projectPath,
|
|
1442
1472
|
input.learning,
|
|
@@ -1451,7 +1481,7 @@ async function handleLearn(projectPath, input) {
|
|
|
1451
1481
|
async function handleFailure(projectPath, input) {
|
|
1452
1482
|
if (!input.description) return mcpError("Error: description is required for failure action");
|
|
1453
1483
|
if (!input.failureType) return mcpError("Error: failureType is required for failure action");
|
|
1454
|
-
const { appendFailure } = await import("./dist-
|
|
1484
|
+
const { appendFailure } = await import("./dist-LCR2IO7U.js");
|
|
1455
1485
|
const result = await appendFailure(
|
|
1456
1486
|
projectPath,
|
|
1457
1487
|
input.description,
|
|
@@ -1464,24 +1494,24 @@ async function handleFailure(projectPath, input) {
|
|
|
1464
1494
|
return resultToMcpResponse(Ok({ recorded: true }));
|
|
1465
1495
|
}
|
|
1466
1496
|
async function handleArchive(projectPath, input) {
|
|
1467
|
-
const { archiveFailures } = await import("./dist-
|
|
1497
|
+
const { archiveFailures } = await import("./dist-LCR2IO7U.js");
|
|
1468
1498
|
const result = await archiveFailures(projectPath, input.stream, input.session);
|
|
1469
1499
|
if (!result.ok) return resultToMcpResponse(result);
|
|
1470
1500
|
return resultToMcpResponse(Ok({ archived: true }));
|
|
1471
1501
|
}
|
|
1472
1502
|
async function handleReset(projectPath, input) {
|
|
1473
|
-
const { saveState, DEFAULT_STATE } = await import("./dist-
|
|
1503
|
+
const { saveState, DEFAULT_STATE } = await import("./dist-LCR2IO7U.js");
|
|
1474
1504
|
const result = await saveState(projectPath, { ...DEFAULT_STATE }, input.stream, input.session);
|
|
1475
1505
|
if (!result.ok) return resultToMcpResponse(result);
|
|
1476
1506
|
return resultToMcpResponse(Ok({ reset: true }));
|
|
1477
1507
|
}
|
|
1478
1508
|
async function handleGate(projectPath, _input) {
|
|
1479
|
-
const { runMechanicalGate } = await import("./dist-
|
|
1509
|
+
const { runMechanicalGate } = await import("./dist-LCR2IO7U.js");
|
|
1480
1510
|
return resultToMcpResponse(await runMechanicalGate(projectPath));
|
|
1481
1511
|
}
|
|
1482
1512
|
async function handleSaveHandoff(projectPath, input) {
|
|
1483
1513
|
if (!input.handoff) return mcpError("Error: handoff is required for save-handoff action");
|
|
1484
|
-
const { saveHandoff } = await import("./dist-
|
|
1514
|
+
const { saveHandoff } = await import("./dist-LCR2IO7U.js");
|
|
1485
1515
|
const result = await saveHandoff(
|
|
1486
1516
|
projectPath,
|
|
1487
1517
|
input.handoff,
|
|
@@ -1493,7 +1523,7 @@ async function handleSaveHandoff(projectPath, input) {
|
|
|
1493
1523
|
return resultToMcpResponse(Ok({ saved: true }));
|
|
1494
1524
|
}
|
|
1495
1525
|
async function handleLoadHandoff(projectPath, input) {
|
|
1496
|
-
const { loadHandoff } = await import("./dist-
|
|
1526
|
+
const { loadHandoff } = await import("./dist-LCR2IO7U.js");
|
|
1497
1527
|
return resultToMcpResponse(await loadHandoff(projectPath, input.stream, input.session));
|
|
1498
1528
|
}
|
|
1499
1529
|
async function handleAppendEntry(projectPath, input) {
|
|
@@ -1501,7 +1531,7 @@ async function handleAppendEntry(projectPath, input) {
|
|
|
1501
1531
|
if (!input.section) return mcpError("Error: section is required for append_entry action");
|
|
1502
1532
|
if (!input.authorSkill) return mcpError("Error: authorSkill is required for append_entry action");
|
|
1503
1533
|
if (!input.content) return mcpError("Error: content is required for append_entry action");
|
|
1504
|
-
const { appendSessionEntry } = await import("./dist-
|
|
1534
|
+
const { appendSessionEntry } = await import("./dist-LCR2IO7U.js");
|
|
1505
1535
|
const result = await appendSessionEntry(
|
|
1506
1536
|
projectPath,
|
|
1507
1537
|
input.session,
|
|
@@ -1517,7 +1547,7 @@ async function handleUpdateEntryStatus(projectPath, input) {
|
|
|
1517
1547
|
if (!input.entryId) return mcpError("Error: entryId is required for update_entry_status action");
|
|
1518
1548
|
if (!input.newStatus)
|
|
1519
1549
|
return mcpError("Error: newStatus is required for update_entry_status action");
|
|
1520
|
-
const { updateSessionEntryStatus } = await import("./dist-
|
|
1550
|
+
const { updateSessionEntryStatus } = await import("./dist-LCR2IO7U.js");
|
|
1521
1551
|
const result = await updateSessionEntryStatus(
|
|
1522
1552
|
projectPath,
|
|
1523
1553
|
input.session,
|
|
@@ -1530,7 +1560,7 @@ async function handleUpdateEntryStatus(projectPath, input) {
|
|
|
1530
1560
|
async function handleReadSection(projectPath, input) {
|
|
1531
1561
|
if (!input.session) return mcpError("Error: session is required for read_section action");
|
|
1532
1562
|
if (!input.section) return mcpError("Error: section is required for read_section action");
|
|
1533
|
-
const { readSessionSection } = await import("./dist-
|
|
1563
|
+
const { readSessionSection } = await import("./dist-LCR2IO7U.js");
|
|
1534
1564
|
const result = await readSessionSection(
|
|
1535
1565
|
projectPath,
|
|
1536
1566
|
input.session,
|
|
@@ -1540,13 +1570,13 @@ async function handleReadSection(projectPath, input) {
|
|
|
1540
1570
|
}
|
|
1541
1571
|
async function handleReadSections(projectPath, input) {
|
|
1542
1572
|
if (!input.session) return mcpError("Error: session is required for read_sections action");
|
|
1543
|
-
const { readSessionSections } = await import("./dist-
|
|
1573
|
+
const { readSessionSections } = await import("./dist-LCR2IO7U.js");
|
|
1544
1574
|
const result = await readSessionSections(projectPath, input.session);
|
|
1545
1575
|
return resultToMcpResponse(result);
|
|
1546
1576
|
}
|
|
1547
1577
|
async function handleArchiveSession(projectPath, input) {
|
|
1548
1578
|
if (!input.session) return mcpError("Error: session is required for archive_session action");
|
|
1549
|
-
const { archiveSession } = await import("./dist-
|
|
1579
|
+
const { archiveSession } = await import("./dist-LCR2IO7U.js");
|
|
1550
1580
|
const result = await archiveSession(projectPath, input.session);
|
|
1551
1581
|
if (!result.ok) return resultToMcpResponse(result);
|
|
1552
1582
|
await autoSyncRoadmap(projectPath);
|
|
@@ -1610,7 +1640,7 @@ var listStreamsDefinition = {
|
|
|
1610
1640
|
};
|
|
1611
1641
|
async function handleListStreams(input) {
|
|
1612
1642
|
try {
|
|
1613
|
-
const { listStreams, loadStreamIndex } = await import("./dist-
|
|
1643
|
+
const { listStreams, loadStreamIndex } = await import("./dist-LCR2IO7U.js");
|
|
1614
1644
|
const projectPath = sanitizePath(input.path);
|
|
1615
1645
|
const indexResult = await loadStreamIndex(projectPath);
|
|
1616
1646
|
const streamsResult = await listStreams(projectPath);
|
|
@@ -1648,7 +1678,7 @@ var checkPhaseGateDefinition = {
|
|
|
1648
1678
|
};
|
|
1649
1679
|
async function handleCheckPhaseGate(input) {
|
|
1650
1680
|
try {
|
|
1651
|
-
const { runCheckPhaseGate } = await import("./check-phase-gate-
|
|
1681
|
+
const { runCheckPhaseGate } = await import("./check-phase-gate-7JQ6EW5R.js");
|
|
1652
1682
|
const result = await runCheckPhaseGate({ cwd: sanitizePath(input.path) });
|
|
1653
1683
|
if (result.ok) {
|
|
1654
1684
|
return { content: [{ type: "text", text: JSON.stringify(result.value) }] };
|
|
@@ -1704,7 +1734,7 @@ async function handleValidateCrossCheck(input) {
|
|
|
1704
1734
|
};
|
|
1705
1735
|
}
|
|
1706
1736
|
try {
|
|
1707
|
-
const { runCrossCheck } = await import("./validate-cross-check-
|
|
1737
|
+
const { runCrossCheck } = await import("./validate-cross-check-VX2BAHQI.js");
|
|
1708
1738
|
const specsDir = path12.resolve(projectPath, input.specsDir ?? "docs/specs");
|
|
1709
1739
|
if (!specsDir.startsWith(projectPath)) {
|
|
1710
1740
|
return {
|
|
@@ -2349,7 +2379,7 @@ async function handleGenerateSlashCommands(input) {
|
|
|
2349
2379
|
// src/mcp/resources/state.ts
|
|
2350
2380
|
async function getStateResource(projectRoot) {
|
|
2351
2381
|
try {
|
|
2352
|
-
const { loadState, migrateToStreams } = await import("./dist-
|
|
2382
|
+
const { loadState, migrateToStreams } = await import("./dist-LCR2IO7U.js");
|
|
2353
2383
|
await migrateToStreams(projectRoot);
|
|
2354
2384
|
const result = await loadState(projectRoot);
|
|
2355
2385
|
if (result.ok) {
|
|
@@ -2437,7 +2467,7 @@ async function handleQueryGraph(input) {
|
|
|
2437
2467
|
const projectPath = sanitizePath(input.path);
|
|
2438
2468
|
const store = await loadGraphStore(projectPath);
|
|
2439
2469
|
if (!store) return graphNotFoundError();
|
|
2440
|
-
const { ContextQL } = await import("./dist-
|
|
2470
|
+
const { ContextQL } = await import("./dist-IA6XYKNO.js");
|
|
2441
2471
|
const cql = new ContextQL(store);
|
|
2442
2472
|
const result = cql.execute({
|
|
2443
2473
|
rootNodeIds: input.rootNodeIds,
|
|
@@ -2528,7 +2558,7 @@ async function handleSearchSimilar(input) {
|
|
|
2528
2558
|
const projectPath = sanitizePath(input.path);
|
|
2529
2559
|
const store = await loadGraphStore(projectPath);
|
|
2530
2560
|
if (!store) return graphNotFoundError();
|
|
2531
|
-
const { FusionLayer } = await import("./dist-
|
|
2561
|
+
const { FusionLayer } = await import("./dist-IA6XYKNO.js");
|
|
2532
2562
|
const fusion = new FusionLayer(store);
|
|
2533
2563
|
const results = fusion.search(input.query, input.topK ?? 10);
|
|
2534
2564
|
if (input.mode === "summary") {
|
|
@@ -2583,7 +2613,7 @@ async function handleFindContextFor(input) {
|
|
|
2583
2613
|
const projectPath = sanitizePath(input.path);
|
|
2584
2614
|
const store = await loadGraphStore(projectPath);
|
|
2585
2615
|
if (!store) return graphNotFoundError();
|
|
2586
|
-
const { FusionLayer, ContextQL } = await import("./dist-
|
|
2616
|
+
const { FusionLayer, ContextQL } = await import("./dist-IA6XYKNO.js");
|
|
2587
2617
|
const fusion = new FusionLayer(store);
|
|
2588
2618
|
const cql = new ContextQL(store);
|
|
2589
2619
|
const tokenBudget = input.tokenBudget ?? 4e3;
|
|
@@ -2679,7 +2709,7 @@ async function handleGetRelationships(input) {
|
|
|
2679
2709
|
const projectPath = sanitizePath(input.path);
|
|
2680
2710
|
const store = await loadGraphStore(projectPath);
|
|
2681
2711
|
if (!store) return graphNotFoundError();
|
|
2682
|
-
const { ContextQL } = await import("./dist-
|
|
2712
|
+
const { ContextQL } = await import("./dist-IA6XYKNO.js");
|
|
2683
2713
|
const cql = new ContextQL(store);
|
|
2684
2714
|
const direction = input.direction ?? "both";
|
|
2685
2715
|
const bidirectional = direction === "both" || direction === "inbound";
|
|
@@ -2785,7 +2815,7 @@ async function handleGetImpact(input) {
|
|
|
2785
2815
|
const projectPath = sanitizePath(input.path);
|
|
2786
2816
|
const store = await loadGraphStore(projectPath);
|
|
2787
2817
|
if (!store) return graphNotFoundError();
|
|
2788
|
-
const { ContextQL } = await import("./dist-
|
|
2818
|
+
const { ContextQL } = await import("./dist-IA6XYKNO.js");
|
|
2789
2819
|
let targetNodeId = input.nodeId;
|
|
2790
2820
|
if (!targetNodeId && input.filePath) {
|
|
2791
2821
|
const fileNodes = store.findNodes({ type: "file" });
|
|
@@ -2917,9 +2947,9 @@ async function handleIngestSource(input) {
|
|
|
2917
2947
|
try {
|
|
2918
2948
|
const projectPath = sanitizePath(input.path);
|
|
2919
2949
|
const graphDir = path16.join(projectPath, ".harness", "graph");
|
|
2920
|
-
const { GraphStore, CodeIngestor, TopologicalLinker, KnowledgeIngestor, GitIngestor } = await import("./dist-
|
|
2921
|
-
const
|
|
2922
|
-
await
|
|
2950
|
+
const { GraphStore, CodeIngestor, TopologicalLinker, KnowledgeIngestor, GitIngestor } = await import("./dist-IA6XYKNO.js");
|
|
2951
|
+
const fs17 = await import("fs/promises");
|
|
2952
|
+
await fs17.mkdir(graphDir, { recursive: true });
|
|
2923
2953
|
const store = new GraphStore();
|
|
2924
2954
|
await store.load(graphDir);
|
|
2925
2955
|
const results = [];
|
|
@@ -2992,7 +3022,7 @@ async function handleDetectAnomalies(input) {
|
|
|
2992
3022
|
const projectPath = sanitizePath(input.path);
|
|
2993
3023
|
const store = await loadGraphStore(projectPath);
|
|
2994
3024
|
if (!store) return graphNotFoundError();
|
|
2995
|
-
const { GraphAnomalyAdapter } = await import("./dist-
|
|
3025
|
+
const { GraphAnomalyAdapter } = await import("./dist-IA6XYKNO.js");
|
|
2996
3026
|
const adapter = new GraphAnomalyAdapter(store);
|
|
2997
3027
|
const report = adapter.detect({
|
|
2998
3028
|
...input.threshold !== void 0 && { threshold: input.threshold },
|
|
@@ -3032,7 +3062,7 @@ async function handleAskGraph(input) {
|
|
|
3032
3062
|
const projectPath = sanitizePath(input.path);
|
|
3033
3063
|
const store = await loadGraphStore(projectPath);
|
|
3034
3064
|
if (!store) return graphNotFoundError();
|
|
3035
|
-
const { askGraph } = await import("./dist-
|
|
3065
|
+
const { askGraph } = await import("./dist-IA6XYKNO.js");
|
|
3036
3066
|
const result = await askGraph(store, input.question);
|
|
3037
3067
|
return {
|
|
3038
3068
|
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
@@ -3168,7 +3198,7 @@ var generateAgentDefinitionsDefinition = {
|
|
|
3168
3198
|
}
|
|
3169
3199
|
};
|
|
3170
3200
|
async function handleGenerateAgentDefinitions(input) {
|
|
3171
|
-
const { generateAgentDefinitions } = await import("./generate-agent-definitions-
|
|
3201
|
+
const { generateAgentDefinitions } = await import("./generate-agent-definitions-KU6X2UQN.js");
|
|
3172
3202
|
const platforms = input.platform === "all" || !input.platform ? ["claude-code", "gemini-cli"] : [input.platform];
|
|
3173
3203
|
const results = generateAgentDefinitions({
|
|
3174
3204
|
platforms: [...platforms],
|
|
@@ -3463,32 +3493,42 @@ function handleSync(projectPath, input, deps) {
|
|
|
3463
3493
|
}
|
|
3464
3494
|
return resultToMcpResponse(Ok2({ changes, applied: false }));
|
|
3465
3495
|
}
|
|
3496
|
+
var readOnlyActions = /* @__PURE__ */ new Set(["show", "query"]);
|
|
3497
|
+
function dispatchAction(action, projectPath, input, deps) {
|
|
3498
|
+
switch (action) {
|
|
3499
|
+
case "show":
|
|
3500
|
+
return handleShow2(projectPath, input, deps);
|
|
3501
|
+
case "add":
|
|
3502
|
+
return handleAdd(projectPath, input, deps);
|
|
3503
|
+
case "update":
|
|
3504
|
+
return handleUpdate(projectPath, input, deps);
|
|
3505
|
+
case "remove":
|
|
3506
|
+
return handleRemove(projectPath, input, deps);
|
|
3507
|
+
case "query":
|
|
3508
|
+
return handleQuery(projectPath, input, deps);
|
|
3509
|
+
case "sync":
|
|
3510
|
+
return handleSync(projectPath, input, deps);
|
|
3511
|
+
default:
|
|
3512
|
+
return { content: [{ type: "text", text: `Error: unknown action` }], isError: true };
|
|
3513
|
+
}
|
|
3514
|
+
}
|
|
3515
|
+
function shouldTriggerExternalSync(input, response) {
|
|
3516
|
+
if (response.isError || readOnlyActions.has(input.action)) return false;
|
|
3517
|
+
if (input.action === "sync") return input.apply === true;
|
|
3518
|
+
return true;
|
|
3519
|
+
}
|
|
3466
3520
|
async function handleManageRoadmap(input) {
|
|
3467
3521
|
try {
|
|
3468
|
-
const { parseRoadmap, serializeRoadmap, syncRoadmap, applySyncChanges } = await import("./dist-
|
|
3522
|
+
const { parseRoadmap, serializeRoadmap, syncRoadmap, applySyncChanges } = await import("./dist-LCR2IO7U.js");
|
|
3469
3523
|
const { Ok: Ok2 } = await import("./dist-USY2C5JL.js");
|
|
3470
3524
|
const projectPath = sanitizePath(input.path);
|
|
3471
3525
|
const deps = { parseRoadmap, serializeRoadmap, syncRoadmap, applySyncChanges, Ok: Ok2 };
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
return handleAdd(projectPath, input, deps);
|
|
3477
|
-
case "update":
|
|
3478
|
-
return handleUpdate(projectPath, input, deps);
|
|
3479
|
-
case "remove":
|
|
3480
|
-
return handleRemove(projectPath, input, deps);
|
|
3481
|
-
case "query":
|
|
3482
|
-
return handleQuery(projectPath, input, deps);
|
|
3483
|
-
case "sync":
|
|
3484
|
-
return handleSync(projectPath, input, deps);
|
|
3485
|
-
default: {
|
|
3486
|
-
return {
|
|
3487
|
-
content: [{ type: "text", text: `Error: unknown action` }],
|
|
3488
|
-
isError: true
|
|
3489
|
-
};
|
|
3490
|
-
}
|
|
3526
|
+
const response = dispatchAction(input.action, projectPath, input, deps);
|
|
3527
|
+
if (shouldTriggerExternalSync(input, response)) {
|
|
3528
|
+
await triggerExternalSync(projectPath, roadmapPath(projectPath)).catch(() => {
|
|
3529
|
+
});
|
|
3491
3530
|
}
|
|
3531
|
+
return response;
|
|
3492
3532
|
} catch (error) {
|
|
3493
3533
|
return {
|
|
3494
3534
|
content: [
|
|
@@ -3907,7 +3947,7 @@ async function handleTransition(validInput, projectPath, id) {
|
|
|
3907
3947
|
const transition = transitionResult.data;
|
|
3908
3948
|
const prompt = renderTransition(transition);
|
|
3909
3949
|
try {
|
|
3910
|
-
const { saveHandoff } = await import("./dist-
|
|
3950
|
+
const { saveHandoff } = await import("./dist-LCR2IO7U.js");
|
|
3911
3951
|
await saveHandoff(
|
|
3912
3952
|
projectPath,
|
|
3913
3953
|
{
|
|
@@ -3975,7 +4015,7 @@ async function handleEmitInteraction(input) {
|
|
|
3975
4015
|
}
|
|
3976
4016
|
async function recordInteraction(projectPath, id, type, decision, stream) {
|
|
3977
4017
|
try {
|
|
3978
|
-
const { loadState, saveState } = await import("./dist-
|
|
4018
|
+
const { loadState, saveState } = await import("./dist-LCR2IO7U.js");
|
|
3979
4019
|
const stateResult = await loadState(projectPath, stream);
|
|
3980
4020
|
if (stateResult.ok) {
|
|
3981
4021
|
const state = stateResult.value;
|
|
@@ -4064,10 +4104,10 @@ async function handleGatherContext(input) {
|
|
|
4064
4104
|
input.include ?? ["state", "learnings", "handoff", "graph", "validation"]
|
|
4065
4105
|
);
|
|
4066
4106
|
const errors = [];
|
|
4067
|
-
const statePromise = includeSet.has("state") ? import("./dist-
|
|
4107
|
+
const statePromise = includeSet.has("state") ? import("./dist-LCR2IO7U.js").then(
|
|
4068
4108
|
(core) => core.loadState(projectPath, void 0, input.session)
|
|
4069
4109
|
) : Promise.resolve(null);
|
|
4070
|
-
const learningsPromise = includeSet.has("learnings") ? import("./dist-
|
|
4110
|
+
const learningsPromise = includeSet.has("learnings") ? import("./dist-LCR2IO7U.js").then(
|
|
4071
4111
|
(core) => core.loadBudgetedLearnings(projectPath, {
|
|
4072
4112
|
intent: input.intent,
|
|
4073
4113
|
tokenBudget: input.learningsBudget ?? 1e3,
|
|
@@ -4076,14 +4116,14 @@ async function handleGatherContext(input) {
|
|
|
4076
4116
|
...input.depth !== void 0 && { depth: input.depth }
|
|
4077
4117
|
})
|
|
4078
4118
|
) : Promise.resolve(null);
|
|
4079
|
-
const handoffPromise = includeSet.has("handoff") ? import("./dist-
|
|
4119
|
+
const handoffPromise = includeSet.has("handoff") ? import("./dist-LCR2IO7U.js").then(
|
|
4080
4120
|
(core) => core.loadHandoff(projectPath, void 0, input.session)
|
|
4081
4121
|
) : Promise.resolve(null);
|
|
4082
4122
|
const graphPromise = includeSet.has("graph") ? (async () => {
|
|
4083
|
-
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-
|
|
4123
|
+
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-FJN4H7Y4.js");
|
|
4084
4124
|
const store = await loadGraphStore2(projectPath);
|
|
4085
4125
|
if (!store) return null;
|
|
4086
|
-
const { FusionLayer, ContextQL } = await import("./dist-
|
|
4126
|
+
const { FusionLayer, ContextQL } = await import("./dist-IA6XYKNO.js");
|
|
4087
4127
|
const fusion = new FusionLayer(store);
|
|
4088
4128
|
const cql = new ContextQL(store);
|
|
4089
4129
|
const tokenBudget = input.tokenBudget ?? 4e3;
|
|
@@ -4120,11 +4160,11 @@ async function handleGatherContext(input) {
|
|
|
4120
4160
|
context: contextBlocks
|
|
4121
4161
|
};
|
|
4122
4162
|
})() : Promise.resolve(null);
|
|
4123
|
-
const sessionsPromise = includeSet.has("sessions") && input.session ? import("./dist-
|
|
4163
|
+
const sessionsPromise = includeSet.has("sessions") && input.session ? import("./dist-LCR2IO7U.js").then(
|
|
4124
4164
|
(core) => core.readSessionSections(projectPath, input.session)
|
|
4125
4165
|
) : Promise.resolve(null);
|
|
4126
4166
|
const shouldIncludeEvents = input.includeEvents !== void 0 ? input.includeEvents : includeSet.has("events") || !!input.session && !input.include;
|
|
4127
|
-
const eventsPromise = shouldIncludeEvents ? import("./dist-
|
|
4167
|
+
const eventsPromise = shouldIncludeEvents ? import("./dist-LCR2IO7U.js").then(async (core) => {
|
|
4128
4168
|
const result = await core.loadEvents(projectPath, {
|
|
4129
4169
|
session: input.session
|
|
4130
4170
|
});
|
|
@@ -4132,7 +4172,7 @@ async function handleGatherContext(input) {
|
|
|
4132
4172
|
return core.formatEventTimeline(result.value);
|
|
4133
4173
|
}) : Promise.resolve(null);
|
|
4134
4174
|
const validationPromise = includeSet.has("validation") ? (async () => {
|
|
4135
|
-
const { handleValidateProject: handleValidateProject2 } = await import("./validate-
|
|
4175
|
+
const { handleValidateProject: handleValidateProject2 } = await import("./validate-4IA5RPEX.js");
|
|
4136
4176
|
const result = await handleValidateProject2({ path: projectPath });
|
|
4137
4177
|
const first = result.content[0];
|
|
4138
4178
|
return first ? JSON.parse(first.text) : null;
|
|
@@ -4226,7 +4266,7 @@ async function handleGatherContext(input) {
|
|
|
4226
4266
|
};
|
|
4227
4267
|
if (input.session) {
|
|
4228
4268
|
try {
|
|
4229
|
-
const core = await import("./dist-
|
|
4269
|
+
const core = await import("./dist-LCR2IO7U.js");
|
|
4230
4270
|
core.updateSessionIndex(
|
|
4231
4271
|
projectPath,
|
|
4232
4272
|
input.session,
|
|
@@ -4248,291 +4288,6 @@ async function handleGatherContext(input) {
|
|
|
4248
4288
|
};
|
|
4249
4289
|
}
|
|
4250
4290
|
|
|
4251
|
-
// src/mcp/tools/assess-project.ts
|
|
4252
|
-
var assessProjectDefinition = {
|
|
4253
|
-
name: "assess_project",
|
|
4254
|
-
description: "Run all project health checks in parallel and return a unified report. Checks: validate, dependencies, docs, entropy, security, performance, lint.",
|
|
4255
|
-
inputSchema: {
|
|
4256
|
-
type: "object",
|
|
4257
|
-
properties: {
|
|
4258
|
-
path: { type: "string", description: "Path to project root" },
|
|
4259
|
-
checks: {
|
|
4260
|
-
type: "array",
|
|
4261
|
-
items: {
|
|
4262
|
-
type: "string",
|
|
4263
|
-
enum: ["validate", "deps", "docs", "entropy", "security", "perf", "lint"]
|
|
4264
|
-
},
|
|
4265
|
-
description: "Which checks to run (default: all)"
|
|
4266
|
-
},
|
|
4267
|
-
mode: {
|
|
4268
|
-
type: "string",
|
|
4269
|
-
enum: ["summary", "detailed"],
|
|
4270
|
-
description: "Response density. Default: summary"
|
|
4271
|
-
}
|
|
4272
|
-
},
|
|
4273
|
-
required: ["path"]
|
|
4274
|
-
}
|
|
4275
|
-
};
|
|
4276
|
-
async function handleAssessProject(input) {
|
|
4277
|
-
const start = Date.now();
|
|
4278
|
-
let projectPath;
|
|
4279
|
-
try {
|
|
4280
|
-
projectPath = sanitizePath(input.path);
|
|
4281
|
-
} catch (error) {
|
|
4282
|
-
return {
|
|
4283
|
-
content: [
|
|
4284
|
-
{
|
|
4285
|
-
type: "text",
|
|
4286
|
-
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
4287
|
-
}
|
|
4288
|
-
],
|
|
4289
|
-
isError: true
|
|
4290
|
-
};
|
|
4291
|
-
}
|
|
4292
|
-
const checksToRun = new Set(
|
|
4293
|
-
input.checks ?? ["validate", "deps", "docs", "entropy", "security", "perf", "lint"]
|
|
4294
|
-
);
|
|
4295
|
-
const mode = input.mode ?? "summary";
|
|
4296
|
-
let validateResult = null;
|
|
4297
|
-
if (checksToRun.has("validate")) {
|
|
4298
|
-
try {
|
|
4299
|
-
const { handleValidateProject: handleValidateProject2 } = await import("./validate-VHFE6J6O.js");
|
|
4300
|
-
const result = await handleValidateProject2({ path: projectPath });
|
|
4301
|
-
const first = result.content[0];
|
|
4302
|
-
const parsed = first ? JSON.parse(first.text) : {};
|
|
4303
|
-
validateResult = {
|
|
4304
|
-
name: "validate",
|
|
4305
|
-
passed: parsed.valid === true,
|
|
4306
|
-
issueCount: parsed.errors?.length ?? 0,
|
|
4307
|
-
...parsed.errors?.length > 0 ? { topIssue: parsed.errors[0] } : {},
|
|
4308
|
-
...mode === "detailed" ? { detailed: parsed } : {}
|
|
4309
|
-
};
|
|
4310
|
-
} catch (error) {
|
|
4311
|
-
validateResult = {
|
|
4312
|
-
name: "validate",
|
|
4313
|
-
passed: false,
|
|
4314
|
-
issueCount: 1,
|
|
4315
|
-
topIssue: error instanceof Error ? error.message : String(error)
|
|
4316
|
-
};
|
|
4317
|
-
}
|
|
4318
|
-
}
|
|
4319
|
-
const parallelChecks = [];
|
|
4320
|
-
if (checksToRun.has("deps")) {
|
|
4321
|
-
parallelChecks.push(
|
|
4322
|
-
(async () => {
|
|
4323
|
-
try {
|
|
4324
|
-
const { handleCheckDependencies: handleCheckDependencies2 } = await import("./architecture-NANP4XPE.js");
|
|
4325
|
-
const result = await handleCheckDependencies2({ path: projectPath });
|
|
4326
|
-
const first = result.content[0];
|
|
4327
|
-
const parsed = first ? JSON.parse(first.text) : {};
|
|
4328
|
-
const violations = parsed.violations ?? [];
|
|
4329
|
-
return {
|
|
4330
|
-
name: "deps",
|
|
4331
|
-
passed: !result.isError && violations.length === 0,
|
|
4332
|
-
issueCount: violations.length,
|
|
4333
|
-
...violations.length > 0 ? { topIssue: violations[0]?.message ?? String(violations[0]) } : {},
|
|
4334
|
-
...mode === "detailed" ? { detailed: parsed } : {}
|
|
4335
|
-
};
|
|
4336
|
-
} catch (error) {
|
|
4337
|
-
return {
|
|
4338
|
-
name: "deps",
|
|
4339
|
-
passed: false,
|
|
4340
|
-
issueCount: 1,
|
|
4341
|
-
topIssue: error instanceof Error ? error.message : String(error)
|
|
4342
|
-
};
|
|
4343
|
-
}
|
|
4344
|
-
})()
|
|
4345
|
-
);
|
|
4346
|
-
}
|
|
4347
|
-
if (checksToRun.has("docs")) {
|
|
4348
|
-
parallelChecks.push(
|
|
4349
|
-
(async () => {
|
|
4350
|
-
try {
|
|
4351
|
-
const { handleCheckDocs: handleCheckDocs2 } = await import("./docs-2PCZVSGB.js");
|
|
4352
|
-
const result = await handleCheckDocs2({ path: projectPath, scope: "coverage" });
|
|
4353
|
-
const first = result.content[0];
|
|
4354
|
-
const parsed = first ? JSON.parse(first.text) : {};
|
|
4355
|
-
const undocumented = parsed.undocumented ?? parsed.files?.undocumented ?? [];
|
|
4356
|
-
return {
|
|
4357
|
-
name: "docs",
|
|
4358
|
-
passed: !result.isError,
|
|
4359
|
-
issueCount: Array.isArray(undocumented) ? undocumented.length : 0,
|
|
4360
|
-
...Array.isArray(undocumented) && undocumented.length > 0 ? { topIssue: `Undocumented: ${undocumented[0]}` } : {},
|
|
4361
|
-
...mode === "detailed" ? { detailed: parsed } : {}
|
|
4362
|
-
};
|
|
4363
|
-
} catch (error) {
|
|
4364
|
-
return {
|
|
4365
|
-
name: "docs",
|
|
4366
|
-
passed: false,
|
|
4367
|
-
issueCount: 1,
|
|
4368
|
-
topIssue: error instanceof Error ? error.message : String(error)
|
|
4369
|
-
};
|
|
4370
|
-
}
|
|
4371
|
-
})()
|
|
4372
|
-
);
|
|
4373
|
-
}
|
|
4374
|
-
if (checksToRun.has("entropy")) {
|
|
4375
|
-
parallelChecks.push(
|
|
4376
|
-
(async () => {
|
|
4377
|
-
try {
|
|
4378
|
-
const { handleDetectEntropy: handleDetectEntropy2 } = await import("./entropy-VGXXBIGX.js");
|
|
4379
|
-
const result = await handleDetectEntropy2({ path: projectPath, type: "all" });
|
|
4380
|
-
const first = result.content[0];
|
|
4381
|
-
const parsed = first ? JSON.parse(first.text) : {};
|
|
4382
|
-
const issues = (parsed.drift?.staleReferences?.length ?? 0) + (parsed.drift?.missingTargets?.length ?? 0) + (parsed.deadCode?.unusedImports?.length ?? 0) + (parsed.deadCode?.unusedExports?.length ?? 0) + (parsed.patterns?.violations?.length ?? 0);
|
|
4383
|
-
return {
|
|
4384
|
-
name: "entropy",
|
|
4385
|
-
passed: !("isError" in result && result.isError) && issues === 0,
|
|
4386
|
-
issueCount: issues,
|
|
4387
|
-
...issues > 0 ? { topIssue: "Entropy detected -- run detect_entropy for details" } : {},
|
|
4388
|
-
...mode === "detailed" ? { detailed: parsed } : {}
|
|
4389
|
-
};
|
|
4390
|
-
} catch (error) {
|
|
4391
|
-
return {
|
|
4392
|
-
name: "entropy",
|
|
4393
|
-
passed: false,
|
|
4394
|
-
issueCount: 1,
|
|
4395
|
-
topIssue: error instanceof Error ? error.message : String(error)
|
|
4396
|
-
};
|
|
4397
|
-
}
|
|
4398
|
-
})()
|
|
4399
|
-
);
|
|
4400
|
-
}
|
|
4401
|
-
if (checksToRun.has("security")) {
|
|
4402
|
-
parallelChecks.push(
|
|
4403
|
-
(async () => {
|
|
4404
|
-
try {
|
|
4405
|
-
const { handleRunSecurityScan: handleRunSecurityScan2 } = await import("./security-NLWTMK3G.js");
|
|
4406
|
-
const result = await handleRunSecurityScan2({ path: projectPath });
|
|
4407
|
-
const first = result.content[0];
|
|
4408
|
-
const parsed = first ? JSON.parse(first.text) : {};
|
|
4409
|
-
const findings = parsed.findings ?? [];
|
|
4410
|
-
const errorCount = findings.filter(
|
|
4411
|
-
(f) => f.severity === "error"
|
|
4412
|
-
).length;
|
|
4413
|
-
return {
|
|
4414
|
-
name: "security",
|
|
4415
|
-
passed: !result.isError && errorCount === 0,
|
|
4416
|
-
issueCount: findings.length,
|
|
4417
|
-
...findings.length > 0 ? {
|
|
4418
|
-
topIssue: `${findings[0]?.rule ?? findings[0]?.type ?? "finding"}: ${findings[0]?.message ?? ""}`
|
|
4419
|
-
} : {},
|
|
4420
|
-
...mode === "detailed" ? { detailed: parsed } : {}
|
|
4421
|
-
};
|
|
4422
|
-
} catch (error) {
|
|
4423
|
-
return {
|
|
4424
|
-
name: "security",
|
|
4425
|
-
passed: false,
|
|
4426
|
-
issueCount: 1,
|
|
4427
|
-
topIssue: error instanceof Error ? error.message : String(error)
|
|
4428
|
-
};
|
|
4429
|
-
}
|
|
4430
|
-
})()
|
|
4431
|
-
);
|
|
4432
|
-
}
|
|
4433
|
-
if (checksToRun.has("perf")) {
|
|
4434
|
-
parallelChecks.push(
|
|
4435
|
-
(async () => {
|
|
4436
|
-
try {
|
|
4437
|
-
const { handleCheckPerformance: handleCheckPerformance2 } = await import("./performance-RV4DUMFI.js");
|
|
4438
|
-
const result = await handleCheckPerformance2({ path: projectPath });
|
|
4439
|
-
if ("isError" in result && result.isError) {
|
|
4440
|
-
const msg = result.content[0]?.text ?? "Performance check failed";
|
|
4441
|
-
return { name: "perf", passed: false, issueCount: 1, topIssue: msg };
|
|
4442
|
-
}
|
|
4443
|
-
const first = result.content[0];
|
|
4444
|
-
let parsed = {};
|
|
4445
|
-
try {
|
|
4446
|
-
parsed = first ? JSON.parse(first.text) : {};
|
|
4447
|
-
} catch {
|
|
4448
|
-
return {
|
|
4449
|
-
name: "perf",
|
|
4450
|
-
passed: false,
|
|
4451
|
-
issueCount: 1,
|
|
4452
|
-
topIssue: first?.text ?? "Invalid perf output"
|
|
4453
|
-
};
|
|
4454
|
-
}
|
|
4455
|
-
const issues = parsed.violations?.length ?? parsed.issues?.length ?? 0;
|
|
4456
|
-
return {
|
|
4457
|
-
name: "perf",
|
|
4458
|
-
passed: issues === 0,
|
|
4459
|
-
issueCount: issues,
|
|
4460
|
-
...issues > 0 ? { topIssue: "Performance issues detected" } : {},
|
|
4461
|
-
...mode === "detailed" ? { detailed: parsed } : {}
|
|
4462
|
-
};
|
|
4463
|
-
} catch (error) {
|
|
4464
|
-
return {
|
|
4465
|
-
name: "perf",
|
|
4466
|
-
passed: false,
|
|
4467
|
-
issueCount: 1,
|
|
4468
|
-
topIssue: error instanceof Error ? error.message : String(error)
|
|
4469
|
-
};
|
|
4470
|
-
}
|
|
4471
|
-
})()
|
|
4472
|
-
);
|
|
4473
|
-
}
|
|
4474
|
-
if (checksToRun.has("lint")) {
|
|
4475
|
-
parallelChecks.push(
|
|
4476
|
-
(async () => {
|
|
4477
|
-
try {
|
|
4478
|
-
const { execFileSync } = await import("child_process");
|
|
4479
|
-
const output = execFileSync("npx", ["turbo", "run", "lint", "--force"], {
|
|
4480
|
-
cwd: projectPath,
|
|
4481
|
-
encoding: "utf-8",
|
|
4482
|
-
timeout: 6e4,
|
|
4483
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
4484
|
-
});
|
|
4485
|
-
return {
|
|
4486
|
-
name: "lint",
|
|
4487
|
-
passed: true,
|
|
4488
|
-
issueCount: 0,
|
|
4489
|
-
...mode === "detailed" ? { detailed: output } : {}
|
|
4490
|
-
};
|
|
4491
|
-
} catch (error) {
|
|
4492
|
-
const stderr = error && typeof error === "object" && "stderr" in error ? String(error.stderr) : "";
|
|
4493
|
-
const stdout = error && typeof error === "object" && "stdout" in error ? String(error.stdout) : "";
|
|
4494
|
-
const combined = (stderr + "\n" + stdout).trim();
|
|
4495
|
-
const errorMatch = combined.match(/(\d+) error/);
|
|
4496
|
-
const issueCount = errorMatch?.[1] ? parseInt(errorMatch[1], 10) : 1;
|
|
4497
|
-
const firstError = combined.split("\n").find((line) => line.includes("error"));
|
|
4498
|
-
return {
|
|
4499
|
-
name: "lint",
|
|
4500
|
-
passed: false,
|
|
4501
|
-
issueCount,
|
|
4502
|
-
topIssue: firstError?.trim() ?? (error instanceof Error ? error.message : String(error)),
|
|
4503
|
-
...mode === "detailed" ? { detailed: combined } : {}
|
|
4504
|
-
};
|
|
4505
|
-
}
|
|
4506
|
-
})()
|
|
4507
|
-
);
|
|
4508
|
-
}
|
|
4509
|
-
const parallelResults = await Promise.all(parallelChecks);
|
|
4510
|
-
const allChecks = [];
|
|
4511
|
-
if (validateResult) allChecks.push(validateResult);
|
|
4512
|
-
allChecks.push(...parallelResults);
|
|
4513
|
-
const healthy = allChecks.every((c) => c.passed);
|
|
4514
|
-
const assessedIn = Date.now() - start;
|
|
4515
|
-
if (mode === "summary") {
|
|
4516
|
-
const summaryChecks = allChecks.map(({ detailed: _d, ...rest }) => rest);
|
|
4517
|
-
return {
|
|
4518
|
-
content: [
|
|
4519
|
-
{
|
|
4520
|
-
type: "text",
|
|
4521
|
-
text: JSON.stringify({ healthy, checks: summaryChecks, assessedIn })
|
|
4522
|
-
}
|
|
4523
|
-
]
|
|
4524
|
-
};
|
|
4525
|
-
}
|
|
4526
|
-
return {
|
|
4527
|
-
content: [
|
|
4528
|
-
{
|
|
4529
|
-
type: "text",
|
|
4530
|
-
text: JSON.stringify({ healthy, checks: allChecks, assessedIn })
|
|
4531
|
-
}
|
|
4532
|
-
]
|
|
4533
|
-
};
|
|
4534
|
-
}
|
|
4535
|
-
|
|
4536
4291
|
// src/mcp/tools/review-changes.ts
|
|
4537
4292
|
var SIZE_GATE_LINES = 1e4;
|
|
4538
4293
|
var reviewChangesDefinition = {
|
|
@@ -4637,7 +4392,7 @@ async function handleReviewChanges(input) {
|
|
|
4637
4392
|
}
|
|
4638
4393
|
}
|
|
4639
4394
|
async function runQuickReview(projectPath, diff, diffLines, downgraded) {
|
|
4640
|
-
const { handleAnalyzeDiff: handleAnalyzeDiff2 } = await import("./feedback-
|
|
4395
|
+
const { handleAnalyzeDiff: handleAnalyzeDiff2 } = await import("./feedback-QGCSW7SB.js");
|
|
4641
4396
|
const result = await handleAnalyzeDiff2({ diff, path: projectPath });
|
|
4642
4397
|
const firstContent = result.content[0];
|
|
4643
4398
|
if (!firstContent) throw new Error("Empty analyze_diff response");
|
|
@@ -4668,7 +4423,7 @@ function extractFileCount(diffParsed) {
|
|
|
4668
4423
|
return files?.length ?? 0;
|
|
4669
4424
|
}
|
|
4670
4425
|
async function runStandardReview(projectPath, diff, diffLines, downgraded) {
|
|
4671
|
-
const { handleAnalyzeDiff: handleAnalyzeDiff2, handleCreateSelfReview: handleCreateSelfReview2 } = await import("./feedback-
|
|
4426
|
+
const { handleAnalyzeDiff: handleAnalyzeDiff2, handleCreateSelfReview: handleCreateSelfReview2 } = await import("./feedback-QGCSW7SB.js");
|
|
4672
4427
|
const [diffResult, reviewResult] = await Promise.all([
|
|
4673
4428
|
handleAnalyzeDiff2({ diff, path: projectPath }),
|
|
4674
4429
|
handleCreateSelfReview2({ path: projectPath, diff })
|
|
@@ -4700,7 +4455,7 @@ async function runStandardReview(projectPath, diff, diffLines, downgraded) {
|
|
|
4700
4455
|
};
|
|
4701
4456
|
}
|
|
4702
4457
|
async function runDeepReview(projectPath, diff, diffLines, _downgraded) {
|
|
4703
|
-
const { handleRunCodeReview: handleRunCodeReview2 } = await import("./review-pipeline-
|
|
4458
|
+
const { handleRunCodeReview: handleRunCodeReview2 } = await import("./review-pipeline-3ZS3GJSP.js");
|
|
4704
4459
|
const result = await handleRunCodeReview2({ path: projectPath, diff });
|
|
4705
4460
|
const deepContent = result.content[0];
|
|
4706
4461
|
if (!deepContent) throw new Error("Empty code review response");
|
|
@@ -4771,7 +4526,7 @@ async function handleCheckTaskIndependence(input) {
|
|
|
4771
4526
|
try {
|
|
4772
4527
|
const projectPath = sanitizePath(input.path);
|
|
4773
4528
|
const store = await loadGraphStore(projectPath);
|
|
4774
|
-
const { TaskIndependenceAnalyzer } = await import("./dist-
|
|
4529
|
+
const { TaskIndependenceAnalyzer } = await import("./dist-IA6XYKNO.js");
|
|
4775
4530
|
const analyzer = new TaskIndependenceAnalyzer(store ?? void 0);
|
|
4776
4531
|
const result = analyzer.analyze({
|
|
4777
4532
|
tasks: input.tasks,
|
|
@@ -4859,7 +4614,7 @@ async function handlePredictConflicts(input) {
|
|
|
4859
4614
|
try {
|
|
4860
4615
|
const projectPath = sanitizePath(input.path);
|
|
4861
4616
|
const store = await loadGraphStore(projectPath);
|
|
4862
|
-
const { ConflictPredictor } = await import("./dist-
|
|
4617
|
+
const { ConflictPredictor } = await import("./dist-IA6XYKNO.js");
|
|
4863
4618
|
const predictor = new ConflictPredictor(store ?? void 0);
|
|
4864
4619
|
const result = predictor.predict({
|
|
4865
4620
|
tasks: input.tasks,
|
|
@@ -4965,7 +4720,7 @@ async function handleDetectStaleConstraints(input) {
|
|
|
4965
4720
|
isError: true
|
|
4966
4721
|
};
|
|
4967
4722
|
}
|
|
4968
|
-
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-
|
|
4723
|
+
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-FJN4H7Y4.js");
|
|
4969
4724
|
const store = await loadGraphStore2(projectPath);
|
|
4970
4725
|
if (!store) {
|
|
4971
4726
|
return {
|
|
@@ -4986,7 +4741,7 @@ async function handleDetectStaleConstraints(input) {
|
|
|
4986
4741
|
]
|
|
4987
4742
|
};
|
|
4988
4743
|
}
|
|
4989
|
-
const { detectStaleConstraints } = await import("./dist-
|
|
4744
|
+
const { detectStaleConstraints } = await import("./dist-LCR2IO7U.js");
|
|
4990
4745
|
const result = detectStaleConstraints(
|
|
4991
4746
|
store,
|
|
4992
4747
|
windowDays,
|
|
@@ -5013,6 +4768,204 @@ async function handleDetectStaleConstraints(input) {
|
|
|
5013
4768
|
}
|
|
5014
4769
|
}
|
|
5015
4770
|
|
|
4771
|
+
// src/skill/health-snapshot.ts
|
|
4772
|
+
import { execSync } from "child_process";
|
|
4773
|
+
import * as fs16 from "fs";
|
|
4774
|
+
import * as path19 from "path";
|
|
4775
|
+
var CACHE_FILE = "health-snapshot.json";
|
|
4776
|
+
var STALENESS_MS = 36e5;
|
|
4777
|
+
function isSnapshotFresh(snapshot, projectPath) {
|
|
4778
|
+
try {
|
|
4779
|
+
const currentHead = execSync("git rev-parse HEAD", {
|
|
4780
|
+
cwd: projectPath,
|
|
4781
|
+
encoding: "utf-8",
|
|
4782
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4783
|
+
}).trim();
|
|
4784
|
+
if (snapshot.gitHead === currentHead) return true;
|
|
4785
|
+
} catch {
|
|
4786
|
+
}
|
|
4787
|
+
const age = Date.now() - new Date(snapshot.capturedAt).getTime();
|
|
4788
|
+
return age < STALENESS_MS;
|
|
4789
|
+
}
|
|
4790
|
+
function loadCachedSnapshot(projectPath) {
|
|
4791
|
+
const filePath = path19.join(projectPath, ".harness", CACHE_FILE);
|
|
4792
|
+
try {
|
|
4793
|
+
const raw = fs16.readFileSync(filePath, "utf-8");
|
|
4794
|
+
return JSON.parse(raw);
|
|
4795
|
+
} catch {
|
|
4796
|
+
return null;
|
|
4797
|
+
}
|
|
4798
|
+
}
|
|
4799
|
+
function saveCachedSnapshot(snapshot, projectPath) {
|
|
4800
|
+
const dir = path19.join(projectPath, ".harness");
|
|
4801
|
+
fs16.mkdirSync(dir, { recursive: true });
|
|
4802
|
+
const filePath = path19.join(dir, CACHE_FILE);
|
|
4803
|
+
fs16.writeFileSync(filePath, JSON.stringify(snapshot, null, 2));
|
|
4804
|
+
}
|
|
4805
|
+
var SIGNAL_RULES = [
|
|
4806
|
+
["circular-deps", (c) => c.deps.circularDeps > 0],
|
|
4807
|
+
["layer-violations", (c) => c.deps.layerViolations > 0],
|
|
4808
|
+
["dead-code", (c) => c.entropy.deadExports > 0 || c.entropy.deadFiles > 0],
|
|
4809
|
+
["drift", (c) => c.entropy.driftCount > 0],
|
|
4810
|
+
["security-findings", (c) => c.security.findingCount > 0],
|
|
4811
|
+
["doc-gaps", (c) => c.docs.undocumentedCount > 0],
|
|
4812
|
+
["perf-regression", (c) => c.perf.violationCount > 0],
|
|
4813
|
+
["anomaly-outlier", (_c, m) => m.anomalyOutlierCount > 0],
|
|
4814
|
+
["articulation-point", (_c, m) => m.articulationPointCount > 0],
|
|
4815
|
+
["high-coupling", (_c, m) => m.avgCouplingRatio > 0.5 || m.maxFanOut > 20],
|
|
4816
|
+
["high-complexity", (_c, m) => m.maxCyclomaticComplexity > 20 || m.avgCyclomaticComplexity > 10],
|
|
4817
|
+
["low-coverage", (_c, m) => m.testCoverage !== null && m.testCoverage < 60]
|
|
4818
|
+
];
|
|
4819
|
+
function deriveSignals(checks, metrics) {
|
|
4820
|
+
const signals = /* @__PURE__ */ new Set();
|
|
4821
|
+
for (const [name, predicate] of SIGNAL_RULES) {
|
|
4822
|
+
if (predicate(checks, metrics)) signals.add(name);
|
|
4823
|
+
}
|
|
4824
|
+
return [...signals];
|
|
4825
|
+
}
|
|
4826
|
+
var DEFAULT_CHECK = { passed: true, issueCount: 0 };
|
|
4827
|
+
function parseToolResult(result) {
|
|
4828
|
+
return JSON.parse(result.content[0]?.text ?? "{}");
|
|
4829
|
+
}
|
|
4830
|
+
function buildCheckMap(assessData) {
|
|
4831
|
+
const map = /* @__PURE__ */ new Map();
|
|
4832
|
+
const checks = assessData.checks ?? [];
|
|
4833
|
+
for (const c of checks) {
|
|
4834
|
+
map.set(c.name, { passed: c.passed, issueCount: c.issueCount });
|
|
4835
|
+
}
|
|
4836
|
+
return map;
|
|
4837
|
+
}
|
|
4838
|
+
function countViolations(depsData) {
|
|
4839
|
+
const violations = depsData.violations ?? [];
|
|
4840
|
+
return {
|
|
4841
|
+
circularDeps: violations.filter((v) => v.reason === "CIRCULAR_DEP").length,
|
|
4842
|
+
layerViolations: violations.filter(
|
|
4843
|
+
(v) => v.reason === "WRONG_LAYER" || v.reason === "FORBIDDEN_IMPORT"
|
|
4844
|
+
).length
|
|
4845
|
+
};
|
|
4846
|
+
}
|
|
4847
|
+
function parseEntropyGranular(entropyData) {
|
|
4848
|
+
const dc = entropyData.deadCode ?? {};
|
|
4849
|
+
const dr = entropyData.drift ?? {};
|
|
4850
|
+
return {
|
|
4851
|
+
deadExports: dc.unusedExports?.length ?? 0,
|
|
4852
|
+
deadFiles: dc.deadFiles?.length ?? 0,
|
|
4853
|
+
driftCount: (dr.staleReferences?.length ?? 0) + (dr.missingTargets?.length ?? 0)
|
|
4854
|
+
};
|
|
4855
|
+
}
|
|
4856
|
+
function countCriticalFindings(securityData) {
|
|
4857
|
+
const findings = securityData.findings ?? [];
|
|
4858
|
+
return findings.filter((f) => f.severity === "error").length;
|
|
4859
|
+
}
|
|
4860
|
+
async function runHealthChecks(projectPath) {
|
|
4861
|
+
const { handleAssessProject: handleAssessProject2 } = await import("./assess-project-R2OZIDDS.js");
|
|
4862
|
+
const { handleCheckDependencies: handleCheckDependencies2 } = await import("./architecture-OVOCDTI6.js");
|
|
4863
|
+
const { handleDetectEntropy: handleDetectEntropy2 } = await import("./entropy-AKSZG7G5.js");
|
|
4864
|
+
const { handleRunSecurityScan: handleRunSecurityScan2 } = await import("./security-LJCLZES6.js");
|
|
4865
|
+
const [assessResult, depsResult, entropyResult, securityResult] = await Promise.all([
|
|
4866
|
+
handleAssessProject2({
|
|
4867
|
+
path: projectPath,
|
|
4868
|
+
checks: ["deps", "entropy", "security", "perf", "docs", "lint"]
|
|
4869
|
+
}),
|
|
4870
|
+
handleCheckDependencies2({ path: projectPath }),
|
|
4871
|
+
handleDetectEntropy2({ path: projectPath, type: "all" }),
|
|
4872
|
+
handleRunSecurityScan2({ path: projectPath })
|
|
4873
|
+
]);
|
|
4874
|
+
const assessData = parseToolResult(assessResult);
|
|
4875
|
+
const checkMap = buildCheckMap(assessData);
|
|
4876
|
+
const { circularDeps, layerViolations } = countViolations(parseToolResult(depsResult));
|
|
4877
|
+
const entropyGranular = parseEntropyGranular(parseToolResult(entropyResult));
|
|
4878
|
+
const criticalCount = countCriticalFindings(parseToolResult(securityResult));
|
|
4879
|
+
const deps = checkMap.get("deps") ?? DEFAULT_CHECK;
|
|
4880
|
+
const entropy = checkMap.get("entropy") ?? DEFAULT_CHECK;
|
|
4881
|
+
const security = checkMap.get("security") ?? DEFAULT_CHECK;
|
|
4882
|
+
const perf = checkMap.get("perf") ?? DEFAULT_CHECK;
|
|
4883
|
+
const docs = checkMap.get("docs") ?? DEFAULT_CHECK;
|
|
4884
|
+
const lint = checkMap.get("lint") ?? DEFAULT_CHECK;
|
|
4885
|
+
return {
|
|
4886
|
+
deps: { passed: deps.passed, issueCount: deps.issueCount, circularDeps, layerViolations },
|
|
4887
|
+
entropy: { passed: entropy.passed, ...entropyGranular },
|
|
4888
|
+
security: { passed: security.passed, findingCount: security.issueCount, criticalCount },
|
|
4889
|
+
perf: { passed: perf.passed, violationCount: perf.issueCount },
|
|
4890
|
+
docs: { passed: docs.passed, undocumentedCount: docs.issueCount },
|
|
4891
|
+
lint: { passed: lint.passed, issueCount: lint.issueCount }
|
|
4892
|
+
};
|
|
4893
|
+
}
|
|
4894
|
+
var ZERO_METRICS = {
|
|
4895
|
+
avgFanOut: 0,
|
|
4896
|
+
maxFanOut: 0,
|
|
4897
|
+
avgCyclomaticComplexity: 0,
|
|
4898
|
+
maxCyclomaticComplexity: 0,
|
|
4899
|
+
avgCouplingRatio: 0,
|
|
4900
|
+
testCoverage: null,
|
|
4901
|
+
anomalyOutlierCount: 0,
|
|
4902
|
+
articulationPointCount: 0
|
|
4903
|
+
};
|
|
4904
|
+
function avg(values) {
|
|
4905
|
+
if (values.length === 0) return 0;
|
|
4906
|
+
return Math.round(values.reduce((s, v) => s + v, 0) / values.length * 1e3) / 1e3;
|
|
4907
|
+
}
|
|
4908
|
+
async function runGraphMetrics(projectPath) {
|
|
4909
|
+
try {
|
|
4910
|
+
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-FJN4H7Y4.js");
|
|
4911
|
+
const store = await loadGraphStore2(projectPath);
|
|
4912
|
+
if (!store) return ZERO_METRICS;
|
|
4913
|
+
const { GraphCouplingAdapter, GraphComplexityAdapter, GraphAnomalyAdapter } = await import("./dist-IA6XYKNO.js");
|
|
4914
|
+
const couplingAdapter = new GraphCouplingAdapter(store);
|
|
4915
|
+
const couplingData = couplingAdapter.computeCouplingData();
|
|
4916
|
+
const files = couplingData.files;
|
|
4917
|
+
const avgFanOut = avg(files.map((f) => f.fanOut));
|
|
4918
|
+
const maxFanOut = files.length > 0 ? Math.max(...files.map((f) => f.fanOut)) : 0;
|
|
4919
|
+
const avgCouplingRatio = avg(files.map((f) => f.couplingRatio));
|
|
4920
|
+
const complexityAdapter = new GraphComplexityAdapter(store);
|
|
4921
|
+
const complexityData = complexityAdapter.computeComplexityHotspots();
|
|
4922
|
+
const hotspots = complexityData.hotspots;
|
|
4923
|
+
const avgCyclomaticComplexity = avg(hotspots.map((h) => h.complexity));
|
|
4924
|
+
const maxCyclomaticComplexity = hotspots.length > 0 ? Math.max(...hotspots.map((h) => h.complexity)) : 0;
|
|
4925
|
+
const anomalyAdapter = new GraphAnomalyAdapter(store);
|
|
4926
|
+
const anomalyReport = anomalyAdapter.detect();
|
|
4927
|
+
return {
|
|
4928
|
+
avgFanOut,
|
|
4929
|
+
maxFanOut,
|
|
4930
|
+
avgCyclomaticComplexity,
|
|
4931
|
+
maxCyclomaticComplexity,
|
|
4932
|
+
avgCouplingRatio,
|
|
4933
|
+
testCoverage: null,
|
|
4934
|
+
// Coverage integration deferred -- not available from graph
|
|
4935
|
+
anomalyOutlierCount: anomalyReport.summary.outlierCount,
|
|
4936
|
+
articulationPointCount: anomalyReport.summary.articulationPointCount
|
|
4937
|
+
};
|
|
4938
|
+
} catch {
|
|
4939
|
+
return ZERO_METRICS;
|
|
4940
|
+
}
|
|
4941
|
+
}
|
|
4942
|
+
async function captureHealthSnapshot(projectPath) {
|
|
4943
|
+
let gitHead = "";
|
|
4944
|
+
try {
|
|
4945
|
+
gitHead = execSync("git rev-parse HEAD", {
|
|
4946
|
+
cwd: projectPath,
|
|
4947
|
+
encoding: "utf-8",
|
|
4948
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4949
|
+
}).trim();
|
|
4950
|
+
} catch {
|
|
4951
|
+
}
|
|
4952
|
+
const [checks, metrics] = await Promise.all([
|
|
4953
|
+
runHealthChecks(projectPath),
|
|
4954
|
+
runGraphMetrics(projectPath)
|
|
4955
|
+
]);
|
|
4956
|
+
const signals = deriveSignals(checks, metrics);
|
|
4957
|
+
const snapshot = {
|
|
4958
|
+
capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4959
|
+
gitHead,
|
|
4960
|
+
projectPath,
|
|
4961
|
+
checks,
|
|
4962
|
+
metrics,
|
|
4963
|
+
signals
|
|
4964
|
+
};
|
|
4965
|
+
saveCachedSnapshot(snapshot, projectPath);
|
|
4966
|
+
return snapshot;
|
|
4967
|
+
}
|
|
4968
|
+
|
|
5016
4969
|
// src/mcp/tools/search-skills.ts
|
|
5017
4970
|
var searchSkillsDefinition = {
|
|
5018
4971
|
name: "search_skills",
|
|
@@ -5045,10 +4998,12 @@ async function handleSearchSkills(input) {
|
|
|
5045
4998
|
const tierOverrides = configResult.ok ? configResult.value.skills?.tierOverrides : void 0;
|
|
5046
4999
|
const index = loadOrRebuildIndex(platform, projectRoot, tierOverrides);
|
|
5047
5000
|
const profile = loadOrGenerateProfile(projectRoot);
|
|
5001
|
+
const snapshot = loadCachedSnapshot(projectRoot);
|
|
5002
|
+
const freshSnapshot = snapshot && isSnapshotFresh(snapshot, projectRoot) ? snapshot : void 0;
|
|
5048
5003
|
const queryTerms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 2);
|
|
5049
5004
|
const results = [];
|
|
5050
5005
|
for (const [name, entry] of Object.entries(index.skills)) {
|
|
5051
|
-
const score = scoreSkill(entry, queryTerms, profile, [], name);
|
|
5006
|
+
const score = scoreSkill(entry, queryTerms, profile, [], name, freshSnapshot);
|
|
5052
5007
|
if (score > 0 || queryTerms.length === 0) {
|
|
5053
5008
|
results.push({
|
|
5054
5009
|
name,
|
|
@@ -5072,6 +5027,125 @@ async function handleSearchSkills(input) {
|
|
|
5072
5027
|
};
|
|
5073
5028
|
}
|
|
5074
5029
|
|
|
5030
|
+
// src/mcp/tools/decay-trends.ts
|
|
5031
|
+
var getDecayTrendsDefinition = {
|
|
5032
|
+
name: "get_decay_trends",
|
|
5033
|
+
description: 'Get architecture decay trends over time. Returns stability score history and per-category trend analysis from timeline snapshots. Use to answer questions like "is the architecture decaying?" or "which metrics are getting worse?"',
|
|
5034
|
+
inputSchema: {
|
|
5035
|
+
type: "object",
|
|
5036
|
+
properties: {
|
|
5037
|
+
path: { type: "string", description: "Path to project root" },
|
|
5038
|
+
last: {
|
|
5039
|
+
type: "number",
|
|
5040
|
+
description: "Number of recent snapshots to analyze (default: 10)"
|
|
5041
|
+
},
|
|
5042
|
+
since: {
|
|
5043
|
+
type: "string",
|
|
5044
|
+
description: "Show trends since this ISO date (e.g., 2026-01-01)"
|
|
5045
|
+
},
|
|
5046
|
+
category: {
|
|
5047
|
+
type: "string",
|
|
5048
|
+
description: "Filter to a single metric category",
|
|
5049
|
+
enum: [
|
|
5050
|
+
"circular-deps",
|
|
5051
|
+
"layer-violations",
|
|
5052
|
+
"complexity",
|
|
5053
|
+
"coupling",
|
|
5054
|
+
"forbidden-imports",
|
|
5055
|
+
"module-size",
|
|
5056
|
+
"dependency-depth"
|
|
5057
|
+
]
|
|
5058
|
+
}
|
|
5059
|
+
},
|
|
5060
|
+
required: ["path"]
|
|
5061
|
+
}
|
|
5062
|
+
};
|
|
5063
|
+
async function handleGetDecayTrends(input) {
|
|
5064
|
+
let projectPath;
|
|
5065
|
+
try {
|
|
5066
|
+
projectPath = sanitizePath(input.path);
|
|
5067
|
+
} catch (error) {
|
|
5068
|
+
return {
|
|
5069
|
+
content: [
|
|
5070
|
+
{
|
|
5071
|
+
type: "text",
|
|
5072
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
5073
|
+
}
|
|
5074
|
+
],
|
|
5075
|
+
isError: true
|
|
5076
|
+
};
|
|
5077
|
+
}
|
|
5078
|
+
try {
|
|
5079
|
+
const core = await import("./dist-LCR2IO7U.js");
|
|
5080
|
+
const { TimelineManager } = core;
|
|
5081
|
+
const manager = new TimelineManager(projectPath);
|
|
5082
|
+
const timeline = manager.load();
|
|
5083
|
+
if (timeline.snapshots.length === 0) {
|
|
5084
|
+
return {
|
|
5085
|
+
content: [
|
|
5086
|
+
{
|
|
5087
|
+
type: "text",
|
|
5088
|
+
text: "No architecture snapshots found. Run `harness snapshot capture` to create the first snapshot."
|
|
5089
|
+
}
|
|
5090
|
+
]
|
|
5091
|
+
};
|
|
5092
|
+
}
|
|
5093
|
+
const trendOptions = {};
|
|
5094
|
+
if (input.last !== void 0) trendOptions.last = input.last;
|
|
5095
|
+
if (input.since !== void 0) trendOptions.since = input.since;
|
|
5096
|
+
const trends = manager.trends(trendOptions);
|
|
5097
|
+
if (input.category) {
|
|
5098
|
+
const categoryTrend = trends.categories[input.category];
|
|
5099
|
+
if (!categoryTrend) {
|
|
5100
|
+
return {
|
|
5101
|
+
content: [
|
|
5102
|
+
{
|
|
5103
|
+
type: "text",
|
|
5104
|
+
text: `No trend data for category "${input.category}".`
|
|
5105
|
+
}
|
|
5106
|
+
]
|
|
5107
|
+
};
|
|
5108
|
+
}
|
|
5109
|
+
return {
|
|
5110
|
+
content: [
|
|
5111
|
+
{
|
|
5112
|
+
type: "text",
|
|
5113
|
+
text: JSON.stringify(
|
|
5114
|
+
{
|
|
5115
|
+
category: input.category,
|
|
5116
|
+
trend: categoryTrend,
|
|
5117
|
+
snapshotCount: trends.snapshotCount,
|
|
5118
|
+
from: trends.from,
|
|
5119
|
+
to: trends.to
|
|
5120
|
+
},
|
|
5121
|
+
null,
|
|
5122
|
+
2
|
|
5123
|
+
)
|
|
5124
|
+
}
|
|
5125
|
+
]
|
|
5126
|
+
};
|
|
5127
|
+
}
|
|
5128
|
+
return {
|
|
5129
|
+
content: [
|
|
5130
|
+
{
|
|
5131
|
+
type: "text",
|
|
5132
|
+
text: JSON.stringify(trends, null, 2)
|
|
5133
|
+
}
|
|
5134
|
+
]
|
|
5135
|
+
};
|
|
5136
|
+
} catch (error) {
|
|
5137
|
+
return {
|
|
5138
|
+
content: [
|
|
5139
|
+
{
|
|
5140
|
+
type: "text",
|
|
5141
|
+
text: `Error computing decay trends: ${error instanceof Error ? error.message : String(error)}`
|
|
5142
|
+
}
|
|
5143
|
+
],
|
|
5144
|
+
isError: true
|
|
5145
|
+
};
|
|
5146
|
+
}
|
|
5147
|
+
}
|
|
5148
|
+
|
|
5075
5149
|
// src/mcp/tools/code-nav.ts
|
|
5076
5150
|
var codeOutlineDefinition = {
|
|
5077
5151
|
name: "code_outline",
|
|
@@ -5107,7 +5181,7 @@ async function handleCodeOutline(input) {
|
|
|
5107
5181
|
};
|
|
5108
5182
|
}
|
|
5109
5183
|
try {
|
|
5110
|
-
const { getOutline, formatOutline, EXTENSION_MAP } = await import("./dist-
|
|
5184
|
+
const { getOutline, formatOutline, EXTENSION_MAP } = await import("./dist-LCR2IO7U.js");
|
|
5111
5185
|
const { stat } = await import("fs/promises");
|
|
5112
5186
|
const stats = await stat(targetPath).catch(() => null);
|
|
5113
5187
|
if (stats?.isFile()) {
|
|
@@ -5187,7 +5261,7 @@ async function handleCodeSearch(input) {
|
|
|
5187
5261
|
};
|
|
5188
5262
|
}
|
|
5189
5263
|
try {
|
|
5190
|
-
const { searchSymbols } = await import("./dist-
|
|
5264
|
+
const { searchSymbols } = await import("./dist-LCR2IO7U.js");
|
|
5191
5265
|
const result = await searchSymbols(input.query, directory, input.glob);
|
|
5192
5266
|
const lines = [`Search: "${result.query}" \u2014 ${result.matches.length} matches`];
|
|
5193
5267
|
for (const match of result.matches) {
|
|
@@ -5256,7 +5330,7 @@ async function handleCodeUnfold(input) {
|
|
|
5256
5330
|
}
|
|
5257
5331
|
try {
|
|
5258
5332
|
if (input.symbol) {
|
|
5259
|
-
const { unfoldSymbol } = await import("./dist-
|
|
5333
|
+
const { unfoldSymbol } = await import("./dist-LCR2IO7U.js");
|
|
5260
5334
|
const result = await unfoldSymbol(filePath, input.symbol);
|
|
5261
5335
|
const header = result.warning ? `${result.file}:${result.startLine}-${result.endLine} ${result.warning}
|
|
5262
5336
|
` : `${result.file}:${result.startLine}-${result.endLine}
|
|
@@ -5264,7 +5338,7 @@ async function handleCodeUnfold(input) {
|
|
|
5264
5338
|
return { content: [{ type: "text", text: header + result.content }] };
|
|
5265
5339
|
}
|
|
5266
5340
|
if (input.startLine != null && input.endLine != null) {
|
|
5267
|
-
const { unfoldRange } = await import("./dist-
|
|
5341
|
+
const { unfoldRange } = await import("./dist-LCR2IO7U.js");
|
|
5268
5342
|
const result = await unfoldRange(filePath, input.startLine, input.endLine);
|
|
5269
5343
|
const header = `${result.file}:${result.startLine}-${result.endLine}
|
|
5270
5344
|
`;
|
|
@@ -5292,6 +5366,501 @@ async function handleCodeUnfold(input) {
|
|
|
5292
5366
|
}
|
|
5293
5367
|
}
|
|
5294
5368
|
|
|
5369
|
+
// src/mcp/tools/traceability.ts
|
|
5370
|
+
var checkTraceabilityDefinition = {
|
|
5371
|
+
name: "check_traceability",
|
|
5372
|
+
description: "Check requirement-to-code-to-test traceability for a spec or all specs",
|
|
5373
|
+
inputSchema: {
|
|
5374
|
+
type: "object",
|
|
5375
|
+
properties: {
|
|
5376
|
+
path: { type: "string", description: "Path to project root" },
|
|
5377
|
+
spec: { type: "string", description: "Specific spec file path to check" },
|
|
5378
|
+
feature: { type: "string", description: "Feature name filter" },
|
|
5379
|
+
mode: {
|
|
5380
|
+
type: "string",
|
|
5381
|
+
enum: ["summary", "detailed"],
|
|
5382
|
+
description: "Response density: summary returns coverage stats only, detailed returns full requirement list. Default: summary"
|
|
5383
|
+
}
|
|
5384
|
+
},
|
|
5385
|
+
required: ["path"]
|
|
5386
|
+
}
|
|
5387
|
+
};
|
|
5388
|
+
async function handleCheckTraceability(input) {
|
|
5389
|
+
try {
|
|
5390
|
+
const projectPath = sanitizePath(input.path);
|
|
5391
|
+
const store = await loadGraphStore(projectPath);
|
|
5392
|
+
if (!store) {
|
|
5393
|
+
return {
|
|
5394
|
+
content: [
|
|
5395
|
+
{
|
|
5396
|
+
type: "text",
|
|
5397
|
+
text: "No graph found. Run `harness scan` or use `ingest_source` tool first."
|
|
5398
|
+
}
|
|
5399
|
+
],
|
|
5400
|
+
isError: true
|
|
5401
|
+
};
|
|
5402
|
+
}
|
|
5403
|
+
const { queryTraceability } = await import("./dist-IA6XYKNO.js");
|
|
5404
|
+
const options = {};
|
|
5405
|
+
if (input.spec) options.specPath = input.spec;
|
|
5406
|
+
if (input.feature) options.featureName = input.feature;
|
|
5407
|
+
const results = queryTraceability(store, options);
|
|
5408
|
+
if (results.length === 0) {
|
|
5409
|
+
return {
|
|
5410
|
+
content: [
|
|
5411
|
+
{
|
|
5412
|
+
type: "text",
|
|
5413
|
+
text: JSON.stringify({
|
|
5414
|
+
status: "no-requirements",
|
|
5415
|
+
message: "No requirement nodes found in graph. Ingest specs with RequirementIngestor first."
|
|
5416
|
+
})
|
|
5417
|
+
}
|
|
5418
|
+
]
|
|
5419
|
+
};
|
|
5420
|
+
}
|
|
5421
|
+
const mode = input.mode ?? "summary";
|
|
5422
|
+
if (mode === "summary") {
|
|
5423
|
+
const summaries = results.map((r) => ({
|
|
5424
|
+
specPath: r.specPath,
|
|
5425
|
+
featureName: r.featureName,
|
|
5426
|
+
...r.summary
|
|
5427
|
+
}));
|
|
5428
|
+
const totals = summaries.reduce(
|
|
5429
|
+
(acc, s) => ({
|
|
5430
|
+
total: acc.total + s.total,
|
|
5431
|
+
withCode: acc.withCode + s.withCode,
|
|
5432
|
+
withTests: acc.withTests + s.withTests,
|
|
5433
|
+
fullyTraced: acc.fullyTraced + s.fullyTraced,
|
|
5434
|
+
untraceable: acc.untraceable + s.untraceable
|
|
5435
|
+
}),
|
|
5436
|
+
{ total: 0, withCode: 0, withTests: 0, fullyTraced: 0, untraceable: 0 }
|
|
5437
|
+
);
|
|
5438
|
+
const overallCoverage = totals.total > 0 ? Math.round(totals.fullyTraced / totals.total * 100) : 0;
|
|
5439
|
+
return {
|
|
5440
|
+
content: [
|
|
5441
|
+
{
|
|
5442
|
+
type: "text",
|
|
5443
|
+
text: JSON.stringify({
|
|
5444
|
+
mode: "summary",
|
|
5445
|
+
overallCoverage,
|
|
5446
|
+
totals,
|
|
5447
|
+
specs: summaries
|
|
5448
|
+
})
|
|
5449
|
+
}
|
|
5450
|
+
]
|
|
5451
|
+
};
|
|
5452
|
+
}
|
|
5453
|
+
return {
|
|
5454
|
+
content: [
|
|
5455
|
+
{
|
|
5456
|
+
type: "text",
|
|
5457
|
+
text: JSON.stringify({
|
|
5458
|
+
mode: "detailed",
|
|
5459
|
+
results
|
|
5460
|
+
})
|
|
5461
|
+
}
|
|
5462
|
+
]
|
|
5463
|
+
};
|
|
5464
|
+
} catch (error) {
|
|
5465
|
+
return {
|
|
5466
|
+
content: [
|
|
5467
|
+
{
|
|
5468
|
+
type: "text",
|
|
5469
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
5470
|
+
}
|
|
5471
|
+
],
|
|
5472
|
+
isError: true
|
|
5473
|
+
};
|
|
5474
|
+
}
|
|
5475
|
+
}
|
|
5476
|
+
|
|
5477
|
+
// src/mcp/tools/predict-failures.ts
|
|
5478
|
+
var predictFailuresDefinition = {
|
|
5479
|
+
name: "predict_failures",
|
|
5480
|
+
description: "Predict which architectural constraints will break and when, based on decay trends and planned roadmap features. Requires at least 3 timeline snapshots.",
|
|
5481
|
+
inputSchema: {
|
|
5482
|
+
type: "object",
|
|
5483
|
+
properties: {
|
|
5484
|
+
path: { type: "string", description: "Path to project root" },
|
|
5485
|
+
horizon: {
|
|
5486
|
+
type: "number",
|
|
5487
|
+
description: "Forecast horizon in weeks (default: 12)"
|
|
5488
|
+
},
|
|
5489
|
+
category: {
|
|
5490
|
+
type: "string",
|
|
5491
|
+
description: "Filter to a single metric category",
|
|
5492
|
+
enum: [
|
|
5493
|
+
"circular-deps",
|
|
5494
|
+
"layer-violations",
|
|
5495
|
+
"complexity",
|
|
5496
|
+
"coupling",
|
|
5497
|
+
"forbidden-imports",
|
|
5498
|
+
"module-size",
|
|
5499
|
+
"dependency-depth"
|
|
5500
|
+
]
|
|
5501
|
+
},
|
|
5502
|
+
includeRoadmap: {
|
|
5503
|
+
type: "boolean",
|
|
5504
|
+
description: "Include roadmap spec impact in forecasts (default: true)"
|
|
5505
|
+
}
|
|
5506
|
+
},
|
|
5507
|
+
required: ["path"]
|
|
5508
|
+
}
|
|
5509
|
+
};
|
|
5510
|
+
async function handlePredictFailures(input) {
|
|
5511
|
+
let projectPath;
|
|
5512
|
+
try {
|
|
5513
|
+
projectPath = sanitizePath(input.path);
|
|
5514
|
+
} catch (error) {
|
|
5515
|
+
return {
|
|
5516
|
+
content: [
|
|
5517
|
+
{
|
|
5518
|
+
type: "text",
|
|
5519
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
5520
|
+
}
|
|
5521
|
+
],
|
|
5522
|
+
isError: true
|
|
5523
|
+
};
|
|
5524
|
+
}
|
|
5525
|
+
try {
|
|
5526
|
+
const core = await import("./dist-LCR2IO7U.js");
|
|
5527
|
+
const { TimelineManager, PredictionEngine, SpecImpactEstimator } = core;
|
|
5528
|
+
const manager = new TimelineManager(projectPath);
|
|
5529
|
+
const includeRoadmap = input.includeRoadmap !== false;
|
|
5530
|
+
const estimator = includeRoadmap ? new SpecImpactEstimator(projectPath) : null;
|
|
5531
|
+
const engine = new PredictionEngine(projectPath, manager, estimator);
|
|
5532
|
+
const opts = {
|
|
5533
|
+
includeRoadmap
|
|
5534
|
+
};
|
|
5535
|
+
if (input.horizon !== void 0) opts.horizon = input.horizon;
|
|
5536
|
+
if (input.category) opts.categories = [input.category];
|
|
5537
|
+
const result = engine.predict(opts);
|
|
5538
|
+
return {
|
|
5539
|
+
content: [
|
|
5540
|
+
{
|
|
5541
|
+
type: "text",
|
|
5542
|
+
text: JSON.stringify(result, null, 2)
|
|
5543
|
+
}
|
|
5544
|
+
]
|
|
5545
|
+
};
|
|
5546
|
+
} catch (error) {
|
|
5547
|
+
return {
|
|
5548
|
+
content: [
|
|
5549
|
+
{
|
|
5550
|
+
type: "text",
|
|
5551
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
5552
|
+
}
|
|
5553
|
+
],
|
|
5554
|
+
isError: true
|
|
5555
|
+
};
|
|
5556
|
+
}
|
|
5557
|
+
}
|
|
5558
|
+
|
|
5559
|
+
// src/skill/recommendation-rules.ts
|
|
5560
|
+
var FALLBACK_RULES = {
|
|
5561
|
+
"enforce-architecture": [
|
|
5562
|
+
{ signal: "circular-deps", hard: true },
|
|
5563
|
+
{ signal: "layer-violations", hard: true },
|
|
5564
|
+
{ signal: "high-coupling", metric: "fanOut", threshold: 20, weight: 0.8 },
|
|
5565
|
+
{ signal: "high-coupling", metric: "couplingRatio", threshold: 0.7, weight: 0.6 }
|
|
5566
|
+
],
|
|
5567
|
+
"dependency-health": [
|
|
5568
|
+
{ signal: "high-coupling", metric: "fanOut", threshold: 15, weight: 0.7 },
|
|
5569
|
+
{ signal: "anomaly-outlier", weight: 0.6 },
|
|
5570
|
+
{ signal: "articulation-point", weight: 0.5 }
|
|
5571
|
+
],
|
|
5572
|
+
tdd: [{ signal: "low-coverage", weight: 0.9 }],
|
|
5573
|
+
"codebase-cleanup": [
|
|
5574
|
+
{ signal: "dead-code", weight: 0.8 },
|
|
5575
|
+
{ signal: "drift", weight: 0.6 }
|
|
5576
|
+
],
|
|
5577
|
+
"security-scan": [{ signal: "security-findings", hard: true }],
|
|
5578
|
+
refactoring: [
|
|
5579
|
+
{ signal: "high-complexity", metric: "cyclomaticComplexity", threshold: 15, weight: 0.8 },
|
|
5580
|
+
{ signal: "high-coupling", metric: "couplingRatio", threshold: 0.5, weight: 0.6 }
|
|
5581
|
+
],
|
|
5582
|
+
"detect-doc-drift": [
|
|
5583
|
+
{ signal: "doc-gaps", weight: 0.7 },
|
|
5584
|
+
{ signal: "drift", weight: 0.5 }
|
|
5585
|
+
],
|
|
5586
|
+
perf: [{ signal: "perf-regression", weight: 0.8 }],
|
|
5587
|
+
"supply-chain-audit": [{ signal: "security-findings", weight: 0.6 }],
|
|
5588
|
+
"code-review": [
|
|
5589
|
+
{ signal: "high-complexity", weight: 0.5 },
|
|
5590
|
+
{ signal: "high-coupling", weight: 0.4 }
|
|
5591
|
+
],
|
|
5592
|
+
integrity: [
|
|
5593
|
+
{ signal: "drift", weight: 0.7 },
|
|
5594
|
+
{ signal: "dead-code", weight: 0.5 }
|
|
5595
|
+
],
|
|
5596
|
+
"soundness-review": [
|
|
5597
|
+
{ signal: "layer-violations", weight: 0.6 },
|
|
5598
|
+
{ signal: "circular-deps", weight: 0.5 }
|
|
5599
|
+
],
|
|
5600
|
+
debugging: [
|
|
5601
|
+
{ signal: "perf-regression", weight: 0.5 },
|
|
5602
|
+
{ signal: "anomaly-outlier", weight: 0.6 }
|
|
5603
|
+
],
|
|
5604
|
+
"hotspot-detector": [
|
|
5605
|
+
{ signal: "high-complexity", metric: "cyclomaticComplexity", threshold: 20, weight: 0.9 },
|
|
5606
|
+
{ signal: "anomaly-outlier", weight: 0.7 },
|
|
5607
|
+
{ signal: "articulation-point", weight: 0.8 }
|
|
5608
|
+
],
|
|
5609
|
+
"cleanup-dead-code": [{ signal: "dead-code", hard: true }]
|
|
5610
|
+
};
|
|
5611
|
+
|
|
5612
|
+
// src/skill/recommendation-engine.ts
|
|
5613
|
+
function resolveMetricValue(metrics, metricName) {
|
|
5614
|
+
switch (metricName) {
|
|
5615
|
+
case "fanOut":
|
|
5616
|
+
return metrics.maxFanOut;
|
|
5617
|
+
case "couplingRatio":
|
|
5618
|
+
return metrics.avgCouplingRatio;
|
|
5619
|
+
case "cyclomaticComplexity":
|
|
5620
|
+
return metrics.maxCyclomaticComplexity;
|
|
5621
|
+
case "coverage":
|
|
5622
|
+
return metrics.testCoverage !== null ? 100 - metrics.testCoverage : null;
|
|
5623
|
+
default:
|
|
5624
|
+
return null;
|
|
5625
|
+
}
|
|
5626
|
+
}
|
|
5627
|
+
function buildSkillAddressIndex(skills) {
|
|
5628
|
+
const index = /* @__PURE__ */ new Map();
|
|
5629
|
+
for (const [name, entry] of Object.entries(skills)) {
|
|
5630
|
+
const addresses = entry.addresses.length > 0 ? entry.addresses : FALLBACK_RULES[name] ?? [];
|
|
5631
|
+
index.set(name, { addresses, dependsOn: entry.dependsOn });
|
|
5632
|
+
}
|
|
5633
|
+
for (const [name, addresses] of Object.entries(FALLBACK_RULES)) {
|
|
5634
|
+
if (!index.has(name)) {
|
|
5635
|
+
index.set(name, { addresses, dependsOn: [] });
|
|
5636
|
+
}
|
|
5637
|
+
}
|
|
5638
|
+
return index;
|
|
5639
|
+
}
|
|
5640
|
+
function matchHardRules(snapshot, skillIndex) {
|
|
5641
|
+
const activeSignals = new Set(snapshot.signals);
|
|
5642
|
+
const results = [];
|
|
5643
|
+
for (const [skillName, entry] of skillIndex) {
|
|
5644
|
+
const hardAddresses = entry.addresses.filter((a) => a.hard === true);
|
|
5645
|
+
const matchedSignals = [];
|
|
5646
|
+
const reasons = [];
|
|
5647
|
+
for (const addr of hardAddresses) {
|
|
5648
|
+
if (activeSignals.has(addr.signal)) {
|
|
5649
|
+
matchedSignals.push(addr.signal);
|
|
5650
|
+
reasons.push(`[CRITICAL] Signal '${addr.signal}' is active`);
|
|
5651
|
+
}
|
|
5652
|
+
}
|
|
5653
|
+
if (matchedSignals.length > 0) {
|
|
5654
|
+
results.push({
|
|
5655
|
+
skillName,
|
|
5656
|
+
score: 1,
|
|
5657
|
+
urgency: "critical",
|
|
5658
|
+
reasons,
|
|
5659
|
+
sequence: 0,
|
|
5660
|
+
// assigned later by sequencer
|
|
5661
|
+
triggeredBy: matchedSignals
|
|
5662
|
+
});
|
|
5663
|
+
}
|
|
5664
|
+
}
|
|
5665
|
+
return results;
|
|
5666
|
+
}
|
|
5667
|
+
function clamp(value, min, max) {
|
|
5668
|
+
return Math.min(Math.max(value, min), max);
|
|
5669
|
+
}
|
|
5670
|
+
var DEFAULT_WEIGHT = 0.5;
|
|
5671
|
+
function scoreByHealth(snapshot, skillIndex) {
|
|
5672
|
+
const activeSignals = new Set(snapshot.signals);
|
|
5673
|
+
const results = [];
|
|
5674
|
+
for (const [skillName, entry] of skillIndex) {
|
|
5675
|
+
const softAddresses = entry.addresses.filter((a) => !a.hard);
|
|
5676
|
+
const contributions = [];
|
|
5677
|
+
const triggeredBy = [];
|
|
5678
|
+
const reasons = [];
|
|
5679
|
+
for (const addr of softAddresses) {
|
|
5680
|
+
if (!activeSignals.has(addr.signal)) continue;
|
|
5681
|
+
const weight = addr.weight ?? DEFAULT_WEIGHT;
|
|
5682
|
+
if (addr.metric && addr.threshold !== void 0) {
|
|
5683
|
+
const actual = resolveMetricValue(snapshot.metrics, addr.metric);
|
|
5684
|
+
if (actual === null) continue;
|
|
5685
|
+
const distance = clamp((actual - addr.threshold) / addr.threshold, 0, 1);
|
|
5686
|
+
const contribution = weight * distance;
|
|
5687
|
+
contributions.push(contribution);
|
|
5688
|
+
triggeredBy.push(addr.signal);
|
|
5689
|
+
reasons.push(
|
|
5690
|
+
`${addr.metric} = ${actual} (threshold ${addr.threshold}, distance ${distance.toFixed(2)})`
|
|
5691
|
+
);
|
|
5692
|
+
} else {
|
|
5693
|
+
contributions.push(weight);
|
|
5694
|
+
triggeredBy.push(addr.signal);
|
|
5695
|
+
reasons.push(`Signal '${addr.signal}' is active (weight ${weight})`);
|
|
5696
|
+
}
|
|
5697
|
+
}
|
|
5698
|
+
if (contributions.length === 0) continue;
|
|
5699
|
+
const score = clamp(contributions.reduce((sum, c) => sum + c, 0) / contributions.length, 0, 1);
|
|
5700
|
+
const urgency = score >= 0.7 ? "recommended" : "nice-to-have";
|
|
5701
|
+
results.push({
|
|
5702
|
+
skillName,
|
|
5703
|
+
score: Math.round(score * 1e3) / 1e3,
|
|
5704
|
+
// round to 3 decimal places
|
|
5705
|
+
urgency,
|
|
5706
|
+
reasons,
|
|
5707
|
+
sequence: 0,
|
|
5708
|
+
triggeredBy: [...new Set(triggeredBy)]
|
|
5709
|
+
});
|
|
5710
|
+
}
|
|
5711
|
+
return results;
|
|
5712
|
+
}
|
|
5713
|
+
var DIAGNOSTIC_KEYWORDS = ["health", "detect", "analyze", "audit", "hotspot", "debugging"];
|
|
5714
|
+
var FIX_KEYWORDS = ["enforce", "cleanup", "fix", "refactor", "codebase"];
|
|
5715
|
+
var VALIDATION_KEYWORDS = ["verify", "test", "tdd", "review", "soundness", "integrity"];
|
|
5716
|
+
function classifyPhase(skillName) {
|
|
5717
|
+
const lower = skillName.toLowerCase();
|
|
5718
|
+
if (DIAGNOSTIC_KEYWORDS.some((kw) => lower.includes(kw))) return 0;
|
|
5719
|
+
if (FIX_KEYWORDS.some((kw) => lower.includes(kw))) return 1;
|
|
5720
|
+
if (VALIDATION_KEYWORDS.some((kw) => lower.includes(kw))) return 2;
|
|
5721
|
+
return 3;
|
|
5722
|
+
}
|
|
5723
|
+
function heuristicComparator(recMap) {
|
|
5724
|
+
return (a, b) => {
|
|
5725
|
+
const phaseA = classifyPhase(a);
|
|
5726
|
+
const phaseB = classifyPhase(b);
|
|
5727
|
+
if (phaseA !== phaseB) return phaseA - phaseB;
|
|
5728
|
+
return (recMap.get(b)?.score ?? 0) - (recMap.get(a)?.score ?? 0);
|
|
5729
|
+
};
|
|
5730
|
+
}
|
|
5731
|
+
function buildDepGraph(nameSet, skillDeps) {
|
|
5732
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
5733
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
5734
|
+
for (const name of nameSet) {
|
|
5735
|
+
inDegree.set(name, 0);
|
|
5736
|
+
adjacency.set(name, []);
|
|
5737
|
+
}
|
|
5738
|
+
for (const name of nameSet) {
|
|
5739
|
+
for (const dep of skillDeps.get(name) ?? []) {
|
|
5740
|
+
if (!nameSet.has(dep)) continue;
|
|
5741
|
+
adjacency.get(dep).push(name);
|
|
5742
|
+
inDegree.set(name, (inDegree.get(name) ?? 0) + 1);
|
|
5743
|
+
}
|
|
5744
|
+
}
|
|
5745
|
+
return { inDegree, adjacency };
|
|
5746
|
+
}
|
|
5747
|
+
function sequenceRecommendations(recommendations, skillDeps) {
|
|
5748
|
+
if (recommendations.length === 0) return [];
|
|
5749
|
+
const nameSet = new Set(recommendations.map((r) => r.skillName));
|
|
5750
|
+
const recMap = new Map(recommendations.map((r) => [r.skillName, r]));
|
|
5751
|
+
const compare = heuristicComparator(recMap);
|
|
5752
|
+
const { inDegree, adjacency } = buildDepGraph(nameSet, skillDeps);
|
|
5753
|
+
const sorted = [];
|
|
5754
|
+
let sequence = 1;
|
|
5755
|
+
let queue = [...nameSet].filter((n) => (inDegree.get(n) ?? 0) === 0).sort(compare);
|
|
5756
|
+
while (queue.length > 0) {
|
|
5757
|
+
const nextQueue = [];
|
|
5758
|
+
for (const name of queue) {
|
|
5759
|
+
const rec = recMap.get(name);
|
|
5760
|
+
rec.sequence = sequence++;
|
|
5761
|
+
sorted.push(rec);
|
|
5762
|
+
for (const dependent of adjacency.get(name) ?? []) {
|
|
5763
|
+
const newDeg = (inDegree.get(dependent) ?? 1) - 1;
|
|
5764
|
+
inDegree.set(dependent, newDeg);
|
|
5765
|
+
if (newDeg === 0) nextQueue.push(dependent);
|
|
5766
|
+
}
|
|
5767
|
+
}
|
|
5768
|
+
queue = nextQueue.sort(compare);
|
|
5769
|
+
}
|
|
5770
|
+
return sorted;
|
|
5771
|
+
}
|
|
5772
|
+
function recommend(snapshot, skills, options = {}) {
|
|
5773
|
+
const top = options.top ?? 5;
|
|
5774
|
+
if (snapshot.signals.length === 0) {
|
|
5775
|
+
return {
|
|
5776
|
+
recommendations: [],
|
|
5777
|
+
snapshotAge: "none",
|
|
5778
|
+
sequenceReasoning: "No active signals detected in health snapshot."
|
|
5779
|
+
};
|
|
5780
|
+
}
|
|
5781
|
+
const addressIndex = buildSkillAddressIndex(skills);
|
|
5782
|
+
const hardRecs = matchHardRules(snapshot, addressIndex);
|
|
5783
|
+
const softRecs = scoreByHealth(snapshot, addressIndex);
|
|
5784
|
+
const hardSkills = new Set(hardRecs.map((r) => r.skillName));
|
|
5785
|
+
const merged = [...hardRecs, ...softRecs.filter((r) => !hardSkills.has(r.skillName))];
|
|
5786
|
+
merged.sort((a, b) => b.score - a.score);
|
|
5787
|
+
const limited = merged.slice(0, top);
|
|
5788
|
+
const depMap = /* @__PURE__ */ new Map();
|
|
5789
|
+
for (const [name, entry] of addressIndex) {
|
|
5790
|
+
depMap.set(name, entry.dependsOn);
|
|
5791
|
+
}
|
|
5792
|
+
const sequenced = sequenceRecommendations(limited, depMap);
|
|
5793
|
+
const criticalCount = sequenced.filter((r) => r.urgency === "critical").length;
|
|
5794
|
+
const phases = sequenced.map((r) => `${r.sequence}. ${r.skillName}`).join(" -> ");
|
|
5795
|
+
const reasoning = criticalCount > 0 ? `${criticalCount} critical issue(s) detected. Sequence: ${phases}. Critical items first, then diagnostic -> fix -> validate heuristic.` : `Sequence: ${phases}. Ordered by dependencies and diagnostic -> fix -> validate heuristic.`;
|
|
5796
|
+
return {
|
|
5797
|
+
recommendations: sequenced,
|
|
5798
|
+
snapshotAge: "fresh",
|
|
5799
|
+
sequenceReasoning: reasoning
|
|
5800
|
+
};
|
|
5801
|
+
}
|
|
5802
|
+
|
|
5803
|
+
// src/mcp/tools/recommend-skills.ts
|
|
5804
|
+
var recommendSkillsDefinition = {
|
|
5805
|
+
name: "recommend_skills",
|
|
5806
|
+
description: "Recommend skills based on codebase health. Returns sequenced workflow with urgency markers.",
|
|
5807
|
+
inputSchema: {
|
|
5808
|
+
type: "object",
|
|
5809
|
+
properties: {
|
|
5810
|
+
path: {
|
|
5811
|
+
type: "string",
|
|
5812
|
+
description: "Project root path (defaults to cwd)"
|
|
5813
|
+
},
|
|
5814
|
+
noCache: {
|
|
5815
|
+
type: "boolean",
|
|
5816
|
+
description: "Force fresh health snapshot even if cache is fresh"
|
|
5817
|
+
},
|
|
5818
|
+
top: {
|
|
5819
|
+
type: "number",
|
|
5820
|
+
description: "Max recommendations to return (default 5)"
|
|
5821
|
+
}
|
|
5822
|
+
},
|
|
5823
|
+
required: []
|
|
5824
|
+
}
|
|
5825
|
+
};
|
|
5826
|
+
async function handleRecommendSkills(input) {
|
|
5827
|
+
const projectRoot = input.path || process.cwd();
|
|
5828
|
+
const noCache = input.noCache || false;
|
|
5829
|
+
const top = input.top || 5;
|
|
5830
|
+
let snapshot = null;
|
|
5831
|
+
let usedCache = false;
|
|
5832
|
+
if (!noCache) {
|
|
5833
|
+
const cached = loadCachedSnapshot(projectRoot);
|
|
5834
|
+
if (cached && isSnapshotFresh(cached, projectRoot)) {
|
|
5835
|
+
snapshot = cached;
|
|
5836
|
+
usedCache = true;
|
|
5837
|
+
}
|
|
5838
|
+
}
|
|
5839
|
+
if (!snapshot) {
|
|
5840
|
+
snapshot = await captureHealthSnapshot(projectRoot);
|
|
5841
|
+
}
|
|
5842
|
+
const configResult = resolveConfig();
|
|
5843
|
+
const tierOverrides = configResult.ok ? configResult.value.skills?.tierOverrides : void 0;
|
|
5844
|
+
const index = loadOrRebuildIndex("claude-code", projectRoot, tierOverrides);
|
|
5845
|
+
const skills = {};
|
|
5846
|
+
for (const [name, entry] of Object.entries(index.skills)) {
|
|
5847
|
+
skills[name] = { addresses: entry.addresses, dependsOn: entry.dependsOn };
|
|
5848
|
+
}
|
|
5849
|
+
const result = recommend(snapshot, skills, { top });
|
|
5850
|
+
const output = {
|
|
5851
|
+
...result,
|
|
5852
|
+
snapshotAge: usedCache ? "cached" : "fresh"
|
|
5853
|
+
};
|
|
5854
|
+
return {
|
|
5855
|
+
content: [
|
|
5856
|
+
{
|
|
5857
|
+
type: "text",
|
|
5858
|
+
text: JSON.stringify(output, null, 2)
|
|
5859
|
+
}
|
|
5860
|
+
]
|
|
5861
|
+
};
|
|
5862
|
+
}
|
|
5863
|
+
|
|
5295
5864
|
// src/mcp/server.ts
|
|
5296
5865
|
var TOOL_DEFINITIONS = [
|
|
5297
5866
|
validateToolDefinition,
|
|
@@ -5342,7 +5911,11 @@ var TOOL_DEFINITIONS = [
|
|
|
5342
5911
|
searchSkillsDefinition,
|
|
5343
5912
|
codeOutlineDefinition,
|
|
5344
5913
|
codeSearchDefinition,
|
|
5345
|
-
codeUnfoldDefinition
|
|
5914
|
+
codeUnfoldDefinition,
|
|
5915
|
+
getDecayTrendsDefinition,
|
|
5916
|
+
checkTraceabilityDefinition,
|
|
5917
|
+
predictFailuresDefinition,
|
|
5918
|
+
recommendSkillsDefinition
|
|
5346
5919
|
].map((def) => ({ ...def, trustedOutput: true }));
|
|
5347
5920
|
var TOOL_HANDLERS = {
|
|
5348
5921
|
validate_project: handleValidateProject,
|
|
@@ -5393,7 +5966,11 @@ var TOOL_HANDLERS = {
|
|
|
5393
5966
|
search_skills: handleSearchSkills,
|
|
5394
5967
|
code_outline: handleCodeOutline,
|
|
5395
5968
|
code_search: handleCodeSearch,
|
|
5396
|
-
code_unfold: handleCodeUnfold
|
|
5969
|
+
code_unfold: handleCodeUnfold,
|
|
5970
|
+
get_decay_trends: handleGetDecayTrends,
|
|
5971
|
+
check_traceability: handleCheckTraceability,
|
|
5972
|
+
predict_failures: handlePredictFailures,
|
|
5973
|
+
recommend_skills: handleRecommendSkills
|
|
5397
5974
|
};
|
|
5398
5975
|
var RESOURCE_DEFINITIONS = [
|
|
5399
5976
|
{
|
|
@@ -5479,7 +6056,7 @@ async function appendUpdateNotification(result, resolvedRoot) {
|
|
|
5479
6056
|
shouldRunCheck,
|
|
5480
6057
|
readCheckState,
|
|
5481
6058
|
spawnBackgroundCheck
|
|
5482
|
-
} = await import("./dist-
|
|
6059
|
+
} = await import("./dist-LCR2IO7U.js");
|
|
5483
6060
|
const { CLI_VERSION } = await import("./version-KFFPOQAX.js");
|
|
5484
6061
|
const configInterval = readConfigInterval(resolvedRoot);
|
|
5485
6062
|
const DEFAULT_INTERVAL = 864e5;
|
|
@@ -5561,7 +6138,12 @@ export {
|
|
|
5561
6138
|
generateSlashCommands,
|
|
5562
6139
|
handleOrphanDeletion,
|
|
5563
6140
|
createGenerateSlashCommandsCommand,
|
|
6141
|
+
loadOrRebuildIndex,
|
|
5564
6142
|
handleGetImpact,
|
|
6143
|
+
isSnapshotFresh,
|
|
6144
|
+
loadCachedSnapshot,
|
|
6145
|
+
captureHealthSnapshot,
|
|
6146
|
+
recommend,
|
|
5565
6147
|
getToolDefinitions,
|
|
5566
6148
|
createHarnessServer,
|
|
5567
6149
|
startServer
|