@harness-engineering/cli 1.21.0 → 1.23.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-autopilot/SKILL.md +2 -2
- package/dist/agents/skills/claude-code/harness-brainstorming/SKILL.md +1 -1
- 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-execution/SKILL.md +1 -1
- 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-roadmap/SKILL.md +5 -5
- package/dist/agents/skills/claude-code/harness-roadmap-pilot/SKILL.md +18 -14
- 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-autopilot/SKILL.md +2 -2
- package/dist/agents/skills/codex/harness-brainstorming/SKILL.md +1 -1
- 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-execution/SKILL.md +1 -1
- 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-roadmap/SKILL.md +5 -5
- package/dist/agents/skills/codex/harness-roadmap-pilot/SKILL.md +18 -14
- 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-autopilot/SKILL.md +2 -2
- package/dist/agents/skills/cursor/harness-brainstorming/SKILL.md +1 -1
- 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-execution/SKILL.md +1 -1
- 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-roadmap/SKILL.md +5 -5
- package/dist/agents/skills/cursor/harness-roadmap-pilot/SKILL.md +18 -14
- 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-autopilot/SKILL.md +2 -2
- package/dist/agents/skills/gemini-cli/harness-brainstorming/SKILL.md +1 -1
- 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-execution/SKILL.md +1 -1
- 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-roadmap/SKILL.md +5 -5
- package/dist/agents/skills/gemini-cli/harness-roadmap-pilot/SKILL.md +18 -14
- 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-GLKJSGKT.js} +2 -1
- package/dist/{architecture-NANP4XPE.js → architecture-EDSBAGR4.js} +3 -2
- package/dist/assess-project-CEDY4JU3.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-N3DTKFCZ.js} +4 -3
- package/dist/{chunk-UEKQ5G3V.js → chunk-26AUZBV4.js} +459 -37
- package/dist/chunk-2LAEDVOC.js +293 -0
- package/dist/{chunk-M6TIO6NF.js → chunk-2PAPHA77.js} +1 -1
- package/dist/{dist-U7EAO6T2.js → chunk-5SWE24IG.js} +401 -60
- package/dist/{chunk-7G2ZUTZA.js → chunk-A4AI3H3R.js} +26 -3
- package/dist/{chunk-HUDEBSR2.js → chunk-AIBAYANF.js} +3 -3
- package/dist/{chunk-6GEYPBDU.js → chunk-AKVG4MMZ.js} +9 -9
- package/dist/{chunk-SPUK5W4W.js → chunk-ENA4O4WD.js} +2 -2
- package/dist/{chunk-SZ5TGZMI.js → chunk-GJRUIXUK.js} +17 -2
- package/dist/{chunk-YF5ROTWR.js → chunk-HT4VPPB4.js} +8 -8
- package/dist/{chunk-L6LTNZQZ.js → chunk-LIWGCYON.js} +1 -1
- package/dist/{chunk-UVJFBKCX.js → chunk-QUKH6QCJ.js} +7 -7
- package/dist/{chunk-HKUX2X7O.js → chunk-SE4YPMLH.js} +9 -1
- package/dist/{chunk-TMSGI27F.js → chunk-SM22U22L.js} +982 -386
- package/dist/{chunk-LRG3B43J.js → chunk-T5QWCVGK.js} +1 -1
- package/dist/{chunk-H6LXAH66.js → chunk-TD6MQUV2.js} +1 -1
- package/dist/{chunk-WXI5ONCU.js → chunk-TJ6NLLAY.js} +4 -4
- package/dist/{chunk-CZZXE6BL.js → chunk-TLDCCPUZ.js} +1 -1
- package/dist/{chunk-YZYBQZVL.js → chunk-XDAIFVGC.js} +1539 -587
- package/dist/{ci-workflow-Z4IUJBZL.js → ci-workflow-LE3QF4FP.js} +2 -1
- package/dist/{create-skill-NDXQSTIK.js → create-skill-U3XCFRZN.js} +2 -2
- package/dist/dist-OEXTQQZC.js +92 -0
- package/dist/{dist-KV2ICL5X.js → dist-YIKUBJLQ.js} +56 -3
- package/dist/{docs-2PCZVSGB.js → docs-F5G7NAFF.js} +4 -3
- package/dist/{engine-EOXMI5MD.js → engine-LX5RVGXN.js} +2 -1
- package/dist/{entropy-VGXXBIGX.js → entropy-A5Q2USYX.js} +3 -2
- package/dist/{feedback-VTSPL3O7.js → feedback-2EU25RIW.js} +1 -1
- package/dist/{generate-agent-definitions-QICSCGXB.js → generate-agent-definitions-HNJHO5YQ.js} +2 -1
- package/dist/{graph-loader-KMHDQYDT.js → graph-loader-XULF5QF7.js} +1 -1
- package/dist/index.d.ts +66 -10
- package/dist/index.js +27 -23
- package/dist/{loader-7S4FYAPP.js → loader-GWIEW4HM.js} +2 -1
- package/dist/{mcp-DF25USTE.js → mcp-ID3LR6JB.js} +15 -13
- package/dist/{performance-RV4DUMFI.js → performance-YAY2A6A6.js} +4 -3
- package/dist/{review-pipeline-7KQJB4SI.js → review-pipeline-YD4WI3JM.js} +1 -1
- package/dist/{runtime-XKOHGGRC.js → runtime-UJ4YO4CA.js} +2 -1
- package/dist/{security-NLWTMK3G.js → security-IBSUKMVD.js} +1 -1
- package/dist/{skill-executor-XEVDGXUM.js → skill-executor-2BZQLHYN.js} +2 -2
- package/dist/{validate-VHFE6J6O.js → validate-NHXWKMCR.js} +3 -2
- package/dist/{validate-cross-check-PFRKABCS.js → validate-cross-check-R3GV2MLM.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-TJ6NLLAY.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-QUKH6QCJ.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-AKVG4MMZ.js";
|
|
23
23
|
import {
|
|
24
24
|
handleRunSecurityScan,
|
|
25
25
|
runSecurityScanDefinition
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-TD6MQUV2.js";
|
|
27
27
|
import {
|
|
28
28
|
handleRunCodeReview,
|
|
29
29
|
runCodeReviewDefinition
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-LIWGCYON.js";
|
|
31
|
+
import {
|
|
32
|
+
assessProjectDefinition,
|
|
33
|
+
handleAssessProject
|
|
34
|
+
} from "./chunk-2LAEDVOC.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-ENA4O4WD.js";
|
|
44
48
|
import {
|
|
45
49
|
loadGraphStore
|
|
46
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-TLDCCPUZ.js";
|
|
47
51
|
import {
|
|
48
52
|
checkDependenciesDefinition,
|
|
49
53
|
handleCheckDependencies
|
|
50
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-AIBAYANF.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-HT4VPPB4.js";
|
|
58
62
|
import {
|
|
59
63
|
TrackerConfigSchema,
|
|
60
64
|
resolveConfig
|
|
61
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-GJRUIXUK.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-XDAIFVGC.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-LX5RVGXN.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-GWIEW4HM.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-GWIEW4HM.js");
|
|
610
|
+
const { generateRuntime } = await import("./runtime-UJ4YO4CA.js");
|
|
611
|
+
const { generateAgentsMd } = await import("./agents-md-GLKJSGKT.js");
|
|
612
|
+
const { generateCIWorkflow } = await import("./ci-workflow-LE3QF4FP.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-GWIEW4HM.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-YIKUBJLQ.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-YIKUBJLQ.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-YIKUBJLQ.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-YIKUBJLQ.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-YIKUBJLQ.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-YIKUBJLQ.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-YIKUBJLQ.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-YIKUBJLQ.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-YIKUBJLQ.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-YIKUBJLQ.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-YIKUBJLQ.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-YIKUBJLQ.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-YIKUBJLQ.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-YIKUBJLQ.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-YIKUBJLQ.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-YIKUBJLQ.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-N3DTKFCZ.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-R3GV2MLM.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-YIKUBJLQ.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-OEXTQQZC.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-OEXTQQZC.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-OEXTQQZC.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-OEXTQQZC.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-OEXTQQZC.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-OEXTQQZC.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-OEXTQQZC.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-OEXTQQZC.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-HNJHO5YQ.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],
|
|
@@ -3218,6 +3248,10 @@ var manageRoadmapDefinition = {
|
|
|
3218
3248
|
items: { type: "string" },
|
|
3219
3249
|
description: "Blocking feature names (optional for add/update)"
|
|
3220
3250
|
},
|
|
3251
|
+
assignee: {
|
|
3252
|
+
type: "string",
|
|
3253
|
+
description: "Assignee username/email (optional for update). Tracks assignment history."
|
|
3254
|
+
},
|
|
3221
3255
|
filter: {
|
|
3222
3256
|
type: "string",
|
|
3223
3257
|
description: 'Query filter: "blocked", "in-progress", "done", "planned", "backlog", or "milestone:<name>" (required for query)'
|
|
@@ -3367,6 +3401,9 @@ function handleUpdate(projectPath, input, deps) {
|
|
|
3367
3401
|
if (input.spec !== void 0) feature.spec = input.spec || null;
|
|
3368
3402
|
if (input.plans !== void 0) feature.plans = input.plans;
|
|
3369
3403
|
if (input.blocked_by !== void 0) feature.blockedBy = input.blocked_by;
|
|
3404
|
+
if (input.assignee !== void 0) {
|
|
3405
|
+
deps.assignFeature(roadmap, feature, input.assignee, (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
|
|
3406
|
+
}
|
|
3370
3407
|
found = true;
|
|
3371
3408
|
break;
|
|
3372
3409
|
}
|
|
@@ -3463,32 +3500,49 @@ function handleSync(projectPath, input, deps) {
|
|
|
3463
3500
|
}
|
|
3464
3501
|
return resultToMcpResponse(Ok2({ changes, applied: false }));
|
|
3465
3502
|
}
|
|
3503
|
+
var readOnlyActions = /* @__PURE__ */ new Set(["show", "query"]);
|
|
3504
|
+
function dispatchAction(action, projectPath, input, deps) {
|
|
3505
|
+
switch (action) {
|
|
3506
|
+
case "show":
|
|
3507
|
+
return handleShow2(projectPath, input, deps);
|
|
3508
|
+
case "add":
|
|
3509
|
+
return handleAdd(projectPath, input, deps);
|
|
3510
|
+
case "update":
|
|
3511
|
+
return handleUpdate(projectPath, input, deps);
|
|
3512
|
+
case "remove":
|
|
3513
|
+
return handleRemove(projectPath, input, deps);
|
|
3514
|
+
case "query":
|
|
3515
|
+
return handleQuery(projectPath, input, deps);
|
|
3516
|
+
case "sync":
|
|
3517
|
+
return handleSync(projectPath, input, deps);
|
|
3518
|
+
default:
|
|
3519
|
+
return { content: [{ type: "text", text: `Error: unknown action` }], isError: true };
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
function shouldTriggerExternalSync(input, response) {
|
|
3523
|
+
if (response.isError || readOnlyActions.has(input.action)) return false;
|
|
3524
|
+
if (input.action === "sync") return input.apply === true;
|
|
3525
|
+
return true;
|
|
3526
|
+
}
|
|
3466
3527
|
async function handleManageRoadmap(input) {
|
|
3467
3528
|
try {
|
|
3468
|
-
const { parseRoadmap, serializeRoadmap, syncRoadmap, applySyncChanges } = await import("./dist-
|
|
3529
|
+
const { parseRoadmap, serializeRoadmap, syncRoadmap, applySyncChanges, assignFeature } = await import("./dist-YIKUBJLQ.js");
|
|
3469
3530
|
const { Ok: Ok2 } = await import("./dist-USY2C5JL.js");
|
|
3470
3531
|
const projectPath = sanitizePath(input.path);
|
|
3471
|
-
const deps = {
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
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
|
-
}
|
|
3532
|
+
const deps = {
|
|
3533
|
+
parseRoadmap,
|
|
3534
|
+
serializeRoadmap,
|
|
3535
|
+
syncRoadmap,
|
|
3536
|
+
applySyncChanges,
|
|
3537
|
+
assignFeature,
|
|
3538
|
+
Ok: Ok2
|
|
3539
|
+
};
|
|
3540
|
+
const response = dispatchAction(input.action, projectPath, input, deps);
|
|
3541
|
+
if (shouldTriggerExternalSync(input, response)) {
|
|
3542
|
+
await triggerExternalSync(projectPath, roadmapPath(projectPath)).catch(() => {
|
|
3543
|
+
});
|
|
3491
3544
|
}
|
|
3545
|
+
return response;
|
|
3492
3546
|
} catch (error) {
|
|
3493
3547
|
return {
|
|
3494
3548
|
content: [
|
|
@@ -3907,7 +3961,7 @@ async function handleTransition(validInput, projectPath, id) {
|
|
|
3907
3961
|
const transition = transitionResult.data;
|
|
3908
3962
|
const prompt = renderTransition(transition);
|
|
3909
3963
|
try {
|
|
3910
|
-
const { saveHandoff } = await import("./dist-
|
|
3964
|
+
const { saveHandoff } = await import("./dist-YIKUBJLQ.js");
|
|
3911
3965
|
await saveHandoff(
|
|
3912
3966
|
projectPath,
|
|
3913
3967
|
{
|
|
@@ -3975,7 +4029,7 @@ async function handleEmitInteraction(input) {
|
|
|
3975
4029
|
}
|
|
3976
4030
|
async function recordInteraction(projectPath, id, type, decision, stream) {
|
|
3977
4031
|
try {
|
|
3978
|
-
const { loadState, saveState } = await import("./dist-
|
|
4032
|
+
const { loadState, saveState } = await import("./dist-YIKUBJLQ.js");
|
|
3979
4033
|
const stateResult = await loadState(projectPath, stream);
|
|
3980
4034
|
if (stateResult.ok) {
|
|
3981
4035
|
const state = stateResult.value;
|
|
@@ -4064,10 +4118,10 @@ async function handleGatherContext(input) {
|
|
|
4064
4118
|
input.include ?? ["state", "learnings", "handoff", "graph", "validation"]
|
|
4065
4119
|
);
|
|
4066
4120
|
const errors = [];
|
|
4067
|
-
const statePromise = includeSet.has("state") ? import("./dist-
|
|
4121
|
+
const statePromise = includeSet.has("state") ? import("./dist-YIKUBJLQ.js").then(
|
|
4068
4122
|
(core) => core.loadState(projectPath, void 0, input.session)
|
|
4069
4123
|
) : Promise.resolve(null);
|
|
4070
|
-
const learningsPromise = includeSet.has("learnings") ? import("./dist-
|
|
4124
|
+
const learningsPromise = includeSet.has("learnings") ? import("./dist-YIKUBJLQ.js").then(
|
|
4071
4125
|
(core) => core.loadBudgetedLearnings(projectPath, {
|
|
4072
4126
|
intent: input.intent,
|
|
4073
4127
|
tokenBudget: input.learningsBudget ?? 1e3,
|
|
@@ -4076,14 +4130,14 @@ async function handleGatherContext(input) {
|
|
|
4076
4130
|
...input.depth !== void 0 && { depth: input.depth }
|
|
4077
4131
|
})
|
|
4078
4132
|
) : Promise.resolve(null);
|
|
4079
|
-
const handoffPromise = includeSet.has("handoff") ? import("./dist-
|
|
4133
|
+
const handoffPromise = includeSet.has("handoff") ? import("./dist-YIKUBJLQ.js").then(
|
|
4080
4134
|
(core) => core.loadHandoff(projectPath, void 0, input.session)
|
|
4081
4135
|
) : Promise.resolve(null);
|
|
4082
4136
|
const graphPromise = includeSet.has("graph") ? (async () => {
|
|
4083
|
-
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-
|
|
4137
|
+
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-XULF5QF7.js");
|
|
4084
4138
|
const store = await loadGraphStore2(projectPath);
|
|
4085
4139
|
if (!store) return null;
|
|
4086
|
-
const { FusionLayer, ContextQL } = await import("./dist-
|
|
4140
|
+
const { FusionLayer, ContextQL } = await import("./dist-OEXTQQZC.js");
|
|
4087
4141
|
const fusion = new FusionLayer(store);
|
|
4088
4142
|
const cql = new ContextQL(store);
|
|
4089
4143
|
const tokenBudget = input.tokenBudget ?? 4e3;
|
|
@@ -4120,11 +4174,11 @@ async function handleGatherContext(input) {
|
|
|
4120
4174
|
context: contextBlocks
|
|
4121
4175
|
};
|
|
4122
4176
|
})() : Promise.resolve(null);
|
|
4123
|
-
const sessionsPromise = includeSet.has("sessions") && input.session ? import("./dist-
|
|
4177
|
+
const sessionsPromise = includeSet.has("sessions") && input.session ? import("./dist-YIKUBJLQ.js").then(
|
|
4124
4178
|
(core) => core.readSessionSections(projectPath, input.session)
|
|
4125
4179
|
) : Promise.resolve(null);
|
|
4126
4180
|
const shouldIncludeEvents = input.includeEvents !== void 0 ? input.includeEvents : includeSet.has("events") || !!input.session && !input.include;
|
|
4127
|
-
const eventsPromise = shouldIncludeEvents ? import("./dist-
|
|
4181
|
+
const eventsPromise = shouldIncludeEvents ? import("./dist-YIKUBJLQ.js").then(async (core) => {
|
|
4128
4182
|
const result = await core.loadEvents(projectPath, {
|
|
4129
4183
|
session: input.session
|
|
4130
4184
|
});
|
|
@@ -4132,7 +4186,7 @@ async function handleGatherContext(input) {
|
|
|
4132
4186
|
return core.formatEventTimeline(result.value);
|
|
4133
4187
|
}) : Promise.resolve(null);
|
|
4134
4188
|
const validationPromise = includeSet.has("validation") ? (async () => {
|
|
4135
|
-
const { handleValidateProject: handleValidateProject2 } = await import("./validate-
|
|
4189
|
+
const { handleValidateProject: handleValidateProject2 } = await import("./validate-NHXWKMCR.js");
|
|
4136
4190
|
const result = await handleValidateProject2({ path: projectPath });
|
|
4137
4191
|
const first = result.content[0];
|
|
4138
4192
|
return first ? JSON.parse(first.text) : null;
|
|
@@ -4226,7 +4280,7 @@ async function handleGatherContext(input) {
|
|
|
4226
4280
|
};
|
|
4227
4281
|
if (input.session) {
|
|
4228
4282
|
try {
|
|
4229
|
-
const core = await import("./dist-
|
|
4283
|
+
const core = await import("./dist-YIKUBJLQ.js");
|
|
4230
4284
|
core.updateSessionIndex(
|
|
4231
4285
|
projectPath,
|
|
4232
4286
|
input.session,
|
|
@@ -4248,291 +4302,6 @@ async function handleGatherContext(input) {
|
|
|
4248
4302
|
};
|
|
4249
4303
|
}
|
|
4250
4304
|
|
|
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
4305
|
// src/mcp/tools/review-changes.ts
|
|
4537
4306
|
var SIZE_GATE_LINES = 1e4;
|
|
4538
4307
|
var reviewChangesDefinition = {
|
|
@@ -4637,7 +4406,7 @@ async function handleReviewChanges(input) {
|
|
|
4637
4406
|
}
|
|
4638
4407
|
}
|
|
4639
4408
|
async function runQuickReview(projectPath, diff, diffLines, downgraded) {
|
|
4640
|
-
const { handleAnalyzeDiff: handleAnalyzeDiff2 } = await import("./feedback-
|
|
4409
|
+
const { handleAnalyzeDiff: handleAnalyzeDiff2 } = await import("./feedback-2EU25RIW.js");
|
|
4641
4410
|
const result = await handleAnalyzeDiff2({ diff, path: projectPath });
|
|
4642
4411
|
const firstContent = result.content[0];
|
|
4643
4412
|
if (!firstContent) throw new Error("Empty analyze_diff response");
|
|
@@ -4668,7 +4437,7 @@ function extractFileCount(diffParsed) {
|
|
|
4668
4437
|
return files?.length ?? 0;
|
|
4669
4438
|
}
|
|
4670
4439
|
async function runStandardReview(projectPath, diff, diffLines, downgraded) {
|
|
4671
|
-
const { handleAnalyzeDiff: handleAnalyzeDiff2, handleCreateSelfReview: handleCreateSelfReview2 } = await import("./feedback-
|
|
4440
|
+
const { handleAnalyzeDiff: handleAnalyzeDiff2, handleCreateSelfReview: handleCreateSelfReview2 } = await import("./feedback-2EU25RIW.js");
|
|
4672
4441
|
const [diffResult, reviewResult] = await Promise.all([
|
|
4673
4442
|
handleAnalyzeDiff2({ diff, path: projectPath }),
|
|
4674
4443
|
handleCreateSelfReview2({ path: projectPath, diff })
|
|
@@ -4700,7 +4469,7 @@ async function runStandardReview(projectPath, diff, diffLines, downgraded) {
|
|
|
4700
4469
|
};
|
|
4701
4470
|
}
|
|
4702
4471
|
async function runDeepReview(projectPath, diff, diffLines, _downgraded) {
|
|
4703
|
-
const { handleRunCodeReview: handleRunCodeReview2 } = await import("./review-pipeline-
|
|
4472
|
+
const { handleRunCodeReview: handleRunCodeReview2 } = await import("./review-pipeline-YD4WI3JM.js");
|
|
4704
4473
|
const result = await handleRunCodeReview2({ path: projectPath, diff });
|
|
4705
4474
|
const deepContent = result.content[0];
|
|
4706
4475
|
if (!deepContent) throw new Error("Empty code review response");
|
|
@@ -4771,7 +4540,7 @@ async function handleCheckTaskIndependence(input) {
|
|
|
4771
4540
|
try {
|
|
4772
4541
|
const projectPath = sanitizePath(input.path);
|
|
4773
4542
|
const store = await loadGraphStore(projectPath);
|
|
4774
|
-
const { TaskIndependenceAnalyzer } = await import("./dist-
|
|
4543
|
+
const { TaskIndependenceAnalyzer } = await import("./dist-OEXTQQZC.js");
|
|
4775
4544
|
const analyzer = new TaskIndependenceAnalyzer(store ?? void 0);
|
|
4776
4545
|
const result = analyzer.analyze({
|
|
4777
4546
|
tasks: input.tasks,
|
|
@@ -4859,7 +4628,7 @@ async function handlePredictConflicts(input) {
|
|
|
4859
4628
|
try {
|
|
4860
4629
|
const projectPath = sanitizePath(input.path);
|
|
4861
4630
|
const store = await loadGraphStore(projectPath);
|
|
4862
|
-
const { ConflictPredictor } = await import("./dist-
|
|
4631
|
+
const { ConflictPredictor } = await import("./dist-OEXTQQZC.js");
|
|
4863
4632
|
const predictor = new ConflictPredictor(store ?? void 0);
|
|
4864
4633
|
const result = predictor.predict({
|
|
4865
4634
|
tasks: input.tasks,
|
|
@@ -4965,7 +4734,7 @@ async function handleDetectStaleConstraints(input) {
|
|
|
4965
4734
|
isError: true
|
|
4966
4735
|
};
|
|
4967
4736
|
}
|
|
4968
|
-
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-
|
|
4737
|
+
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-XULF5QF7.js");
|
|
4969
4738
|
const store = await loadGraphStore2(projectPath);
|
|
4970
4739
|
if (!store) {
|
|
4971
4740
|
return {
|
|
@@ -4986,7 +4755,7 @@ async function handleDetectStaleConstraints(input) {
|
|
|
4986
4755
|
]
|
|
4987
4756
|
};
|
|
4988
4757
|
}
|
|
4989
|
-
const { detectStaleConstraints } = await import("./dist-
|
|
4758
|
+
const { detectStaleConstraints } = await import("./dist-YIKUBJLQ.js");
|
|
4990
4759
|
const result = detectStaleConstraints(
|
|
4991
4760
|
store,
|
|
4992
4761
|
windowDays,
|
|
@@ -5013,6 +4782,204 @@ async function handleDetectStaleConstraints(input) {
|
|
|
5013
4782
|
}
|
|
5014
4783
|
}
|
|
5015
4784
|
|
|
4785
|
+
// src/skill/health-snapshot.ts
|
|
4786
|
+
import { execSync } from "child_process";
|
|
4787
|
+
import * as fs16 from "fs";
|
|
4788
|
+
import * as path19 from "path";
|
|
4789
|
+
var CACHE_FILE = "health-snapshot.json";
|
|
4790
|
+
var STALENESS_MS = 36e5;
|
|
4791
|
+
function isSnapshotFresh(snapshot, projectPath) {
|
|
4792
|
+
try {
|
|
4793
|
+
const currentHead = execSync("git rev-parse HEAD", {
|
|
4794
|
+
cwd: projectPath,
|
|
4795
|
+
encoding: "utf-8",
|
|
4796
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4797
|
+
}).trim();
|
|
4798
|
+
if (snapshot.gitHead === currentHead) return true;
|
|
4799
|
+
} catch {
|
|
4800
|
+
}
|
|
4801
|
+
const age = Date.now() - new Date(snapshot.capturedAt).getTime();
|
|
4802
|
+
return age < STALENESS_MS;
|
|
4803
|
+
}
|
|
4804
|
+
function loadCachedSnapshot(projectPath) {
|
|
4805
|
+
const filePath = path19.join(projectPath, ".harness", CACHE_FILE);
|
|
4806
|
+
try {
|
|
4807
|
+
const raw = fs16.readFileSync(filePath, "utf-8");
|
|
4808
|
+
return JSON.parse(raw);
|
|
4809
|
+
} catch {
|
|
4810
|
+
return null;
|
|
4811
|
+
}
|
|
4812
|
+
}
|
|
4813
|
+
function saveCachedSnapshot(snapshot, projectPath) {
|
|
4814
|
+
const dir = path19.join(projectPath, ".harness");
|
|
4815
|
+
fs16.mkdirSync(dir, { recursive: true });
|
|
4816
|
+
const filePath = path19.join(dir, CACHE_FILE);
|
|
4817
|
+
fs16.writeFileSync(filePath, JSON.stringify(snapshot, null, 2));
|
|
4818
|
+
}
|
|
4819
|
+
var SIGNAL_RULES = [
|
|
4820
|
+
["circular-deps", (c) => c.deps.circularDeps > 0],
|
|
4821
|
+
["layer-violations", (c) => c.deps.layerViolations > 0],
|
|
4822
|
+
["dead-code", (c) => c.entropy.deadExports > 0 || c.entropy.deadFiles > 0],
|
|
4823
|
+
["drift", (c) => c.entropy.driftCount > 0],
|
|
4824
|
+
["security-findings", (c) => c.security.findingCount > 0],
|
|
4825
|
+
["doc-gaps", (c) => c.docs.undocumentedCount > 0],
|
|
4826
|
+
["perf-regression", (c) => c.perf.violationCount > 0],
|
|
4827
|
+
["anomaly-outlier", (_c, m) => m.anomalyOutlierCount > 0],
|
|
4828
|
+
["articulation-point", (_c, m) => m.articulationPointCount > 0],
|
|
4829
|
+
["high-coupling", (_c, m) => m.avgCouplingRatio > 0.5 || m.maxFanOut > 20],
|
|
4830
|
+
["high-complexity", (_c, m) => m.maxCyclomaticComplexity > 20 || m.avgCyclomaticComplexity > 10],
|
|
4831
|
+
["low-coverage", (_c, m) => m.testCoverage !== null && m.testCoverage < 60]
|
|
4832
|
+
];
|
|
4833
|
+
function deriveSignals(checks, metrics) {
|
|
4834
|
+
const signals = /* @__PURE__ */ new Set();
|
|
4835
|
+
for (const [name, predicate] of SIGNAL_RULES) {
|
|
4836
|
+
if (predicate(checks, metrics)) signals.add(name);
|
|
4837
|
+
}
|
|
4838
|
+
return [...signals];
|
|
4839
|
+
}
|
|
4840
|
+
var DEFAULT_CHECK = { passed: true, issueCount: 0 };
|
|
4841
|
+
function parseToolResult(result) {
|
|
4842
|
+
return JSON.parse(result.content[0]?.text ?? "{}");
|
|
4843
|
+
}
|
|
4844
|
+
function buildCheckMap(assessData) {
|
|
4845
|
+
const map = /* @__PURE__ */ new Map();
|
|
4846
|
+
const checks = assessData.checks ?? [];
|
|
4847
|
+
for (const c of checks) {
|
|
4848
|
+
map.set(c.name, { passed: c.passed, issueCount: c.issueCount });
|
|
4849
|
+
}
|
|
4850
|
+
return map;
|
|
4851
|
+
}
|
|
4852
|
+
function countViolations(depsData) {
|
|
4853
|
+
const violations = depsData.violations ?? [];
|
|
4854
|
+
return {
|
|
4855
|
+
circularDeps: violations.filter((v) => v.reason === "CIRCULAR_DEP").length,
|
|
4856
|
+
layerViolations: violations.filter(
|
|
4857
|
+
(v) => v.reason === "WRONG_LAYER" || v.reason === "FORBIDDEN_IMPORT"
|
|
4858
|
+
).length
|
|
4859
|
+
};
|
|
4860
|
+
}
|
|
4861
|
+
function parseEntropyGranular(entropyData) {
|
|
4862
|
+
const dc = entropyData.deadCode ?? {};
|
|
4863
|
+
const dr = entropyData.drift ?? {};
|
|
4864
|
+
return {
|
|
4865
|
+
deadExports: dc.unusedExports?.length ?? 0,
|
|
4866
|
+
deadFiles: dc.deadFiles?.length ?? 0,
|
|
4867
|
+
driftCount: (dr.staleReferences?.length ?? 0) + (dr.missingTargets?.length ?? 0)
|
|
4868
|
+
};
|
|
4869
|
+
}
|
|
4870
|
+
function countCriticalFindings(securityData) {
|
|
4871
|
+
const findings = securityData.findings ?? [];
|
|
4872
|
+
return findings.filter((f) => f.severity === "error").length;
|
|
4873
|
+
}
|
|
4874
|
+
async function runHealthChecks(projectPath) {
|
|
4875
|
+
const { handleAssessProject: handleAssessProject2 } = await import("./assess-project-CEDY4JU3.js");
|
|
4876
|
+
const { handleCheckDependencies: handleCheckDependencies2 } = await import("./architecture-EDSBAGR4.js");
|
|
4877
|
+
const { handleDetectEntropy: handleDetectEntropy2 } = await import("./entropy-A5Q2USYX.js");
|
|
4878
|
+
const { handleRunSecurityScan: handleRunSecurityScan2 } = await import("./security-IBSUKMVD.js");
|
|
4879
|
+
const [assessResult, depsResult, entropyResult, securityResult] = await Promise.all([
|
|
4880
|
+
handleAssessProject2({
|
|
4881
|
+
path: projectPath,
|
|
4882
|
+
checks: ["deps", "entropy", "security", "perf", "docs", "lint"]
|
|
4883
|
+
}),
|
|
4884
|
+
handleCheckDependencies2({ path: projectPath }),
|
|
4885
|
+
handleDetectEntropy2({ path: projectPath, type: "all" }),
|
|
4886
|
+
handleRunSecurityScan2({ path: projectPath })
|
|
4887
|
+
]);
|
|
4888
|
+
const assessData = parseToolResult(assessResult);
|
|
4889
|
+
const checkMap = buildCheckMap(assessData);
|
|
4890
|
+
const { circularDeps, layerViolations } = countViolations(parseToolResult(depsResult));
|
|
4891
|
+
const entropyGranular = parseEntropyGranular(parseToolResult(entropyResult));
|
|
4892
|
+
const criticalCount = countCriticalFindings(parseToolResult(securityResult));
|
|
4893
|
+
const deps = checkMap.get("deps") ?? DEFAULT_CHECK;
|
|
4894
|
+
const entropy = checkMap.get("entropy") ?? DEFAULT_CHECK;
|
|
4895
|
+
const security = checkMap.get("security") ?? DEFAULT_CHECK;
|
|
4896
|
+
const perf = checkMap.get("perf") ?? DEFAULT_CHECK;
|
|
4897
|
+
const docs = checkMap.get("docs") ?? DEFAULT_CHECK;
|
|
4898
|
+
const lint = checkMap.get("lint") ?? DEFAULT_CHECK;
|
|
4899
|
+
return {
|
|
4900
|
+
deps: { passed: deps.passed, issueCount: deps.issueCount, circularDeps, layerViolations },
|
|
4901
|
+
entropy: { passed: entropy.passed, ...entropyGranular },
|
|
4902
|
+
security: { passed: security.passed, findingCount: security.issueCount, criticalCount },
|
|
4903
|
+
perf: { passed: perf.passed, violationCount: perf.issueCount },
|
|
4904
|
+
docs: { passed: docs.passed, undocumentedCount: docs.issueCount },
|
|
4905
|
+
lint: { passed: lint.passed, issueCount: lint.issueCount }
|
|
4906
|
+
};
|
|
4907
|
+
}
|
|
4908
|
+
var ZERO_METRICS = {
|
|
4909
|
+
avgFanOut: 0,
|
|
4910
|
+
maxFanOut: 0,
|
|
4911
|
+
avgCyclomaticComplexity: 0,
|
|
4912
|
+
maxCyclomaticComplexity: 0,
|
|
4913
|
+
avgCouplingRatio: 0,
|
|
4914
|
+
testCoverage: null,
|
|
4915
|
+
anomalyOutlierCount: 0,
|
|
4916
|
+
articulationPointCount: 0
|
|
4917
|
+
};
|
|
4918
|
+
function avg(values) {
|
|
4919
|
+
if (values.length === 0) return 0;
|
|
4920
|
+
return Math.round(values.reduce((s, v) => s + v, 0) / values.length * 1e3) / 1e3;
|
|
4921
|
+
}
|
|
4922
|
+
async function runGraphMetrics(projectPath) {
|
|
4923
|
+
try {
|
|
4924
|
+
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-XULF5QF7.js");
|
|
4925
|
+
const store = await loadGraphStore2(projectPath);
|
|
4926
|
+
if (!store) return ZERO_METRICS;
|
|
4927
|
+
const { GraphCouplingAdapter, GraphComplexityAdapter, GraphAnomalyAdapter } = await import("./dist-OEXTQQZC.js");
|
|
4928
|
+
const couplingAdapter = new GraphCouplingAdapter(store);
|
|
4929
|
+
const couplingData = couplingAdapter.computeCouplingData();
|
|
4930
|
+
const files = couplingData.files;
|
|
4931
|
+
const avgFanOut = avg(files.map((f) => f.fanOut));
|
|
4932
|
+
const maxFanOut = files.length > 0 ? Math.max(...files.map((f) => f.fanOut)) : 0;
|
|
4933
|
+
const avgCouplingRatio = avg(files.map((f) => f.couplingRatio));
|
|
4934
|
+
const complexityAdapter = new GraphComplexityAdapter(store);
|
|
4935
|
+
const complexityData = complexityAdapter.computeComplexityHotspots();
|
|
4936
|
+
const hotspots = complexityData.hotspots;
|
|
4937
|
+
const avgCyclomaticComplexity = avg(hotspots.map((h) => h.complexity));
|
|
4938
|
+
const maxCyclomaticComplexity = hotspots.length > 0 ? Math.max(...hotspots.map((h) => h.complexity)) : 0;
|
|
4939
|
+
const anomalyAdapter = new GraphAnomalyAdapter(store);
|
|
4940
|
+
const anomalyReport = anomalyAdapter.detect();
|
|
4941
|
+
return {
|
|
4942
|
+
avgFanOut,
|
|
4943
|
+
maxFanOut,
|
|
4944
|
+
avgCyclomaticComplexity,
|
|
4945
|
+
maxCyclomaticComplexity,
|
|
4946
|
+
avgCouplingRatio,
|
|
4947
|
+
testCoverage: null,
|
|
4948
|
+
// Coverage integration deferred -- not available from graph
|
|
4949
|
+
anomalyOutlierCount: anomalyReport.summary.outlierCount,
|
|
4950
|
+
articulationPointCount: anomalyReport.summary.articulationPointCount
|
|
4951
|
+
};
|
|
4952
|
+
} catch {
|
|
4953
|
+
return ZERO_METRICS;
|
|
4954
|
+
}
|
|
4955
|
+
}
|
|
4956
|
+
async function captureHealthSnapshot(projectPath) {
|
|
4957
|
+
let gitHead = "";
|
|
4958
|
+
try {
|
|
4959
|
+
gitHead = execSync("git rev-parse HEAD", {
|
|
4960
|
+
cwd: projectPath,
|
|
4961
|
+
encoding: "utf-8",
|
|
4962
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4963
|
+
}).trim();
|
|
4964
|
+
} catch {
|
|
4965
|
+
}
|
|
4966
|
+
const [checks, metrics] = await Promise.all([
|
|
4967
|
+
runHealthChecks(projectPath),
|
|
4968
|
+
runGraphMetrics(projectPath)
|
|
4969
|
+
]);
|
|
4970
|
+
const signals = deriveSignals(checks, metrics);
|
|
4971
|
+
const snapshot = {
|
|
4972
|
+
capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4973
|
+
gitHead,
|
|
4974
|
+
projectPath,
|
|
4975
|
+
checks,
|
|
4976
|
+
metrics,
|
|
4977
|
+
signals
|
|
4978
|
+
};
|
|
4979
|
+
saveCachedSnapshot(snapshot, projectPath);
|
|
4980
|
+
return snapshot;
|
|
4981
|
+
}
|
|
4982
|
+
|
|
5016
4983
|
// src/mcp/tools/search-skills.ts
|
|
5017
4984
|
var searchSkillsDefinition = {
|
|
5018
4985
|
name: "search_skills",
|
|
@@ -5045,10 +5012,12 @@ async function handleSearchSkills(input) {
|
|
|
5045
5012
|
const tierOverrides = configResult.ok ? configResult.value.skills?.tierOverrides : void 0;
|
|
5046
5013
|
const index = loadOrRebuildIndex(platform, projectRoot, tierOverrides);
|
|
5047
5014
|
const profile = loadOrGenerateProfile(projectRoot);
|
|
5015
|
+
const snapshot = loadCachedSnapshot(projectRoot);
|
|
5016
|
+
const freshSnapshot = snapshot && isSnapshotFresh(snapshot, projectRoot) ? snapshot : void 0;
|
|
5048
5017
|
const queryTerms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 2);
|
|
5049
5018
|
const results = [];
|
|
5050
5019
|
for (const [name, entry] of Object.entries(index.skills)) {
|
|
5051
|
-
const score = scoreSkill(entry, queryTerms, profile, [], name);
|
|
5020
|
+
const score = scoreSkill(entry, queryTerms, profile, [], name, freshSnapshot);
|
|
5052
5021
|
if (score > 0 || queryTerms.length === 0) {
|
|
5053
5022
|
results.push({
|
|
5054
5023
|
name,
|
|
@@ -5072,6 +5041,125 @@ async function handleSearchSkills(input) {
|
|
|
5072
5041
|
};
|
|
5073
5042
|
}
|
|
5074
5043
|
|
|
5044
|
+
// src/mcp/tools/decay-trends.ts
|
|
5045
|
+
var getDecayTrendsDefinition = {
|
|
5046
|
+
name: "get_decay_trends",
|
|
5047
|
+
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?"',
|
|
5048
|
+
inputSchema: {
|
|
5049
|
+
type: "object",
|
|
5050
|
+
properties: {
|
|
5051
|
+
path: { type: "string", description: "Path to project root" },
|
|
5052
|
+
last: {
|
|
5053
|
+
type: "number",
|
|
5054
|
+
description: "Number of recent snapshots to analyze (default: 10)"
|
|
5055
|
+
},
|
|
5056
|
+
since: {
|
|
5057
|
+
type: "string",
|
|
5058
|
+
description: "Show trends since this ISO date (e.g., 2026-01-01)"
|
|
5059
|
+
},
|
|
5060
|
+
category: {
|
|
5061
|
+
type: "string",
|
|
5062
|
+
description: "Filter to a single metric category",
|
|
5063
|
+
enum: [
|
|
5064
|
+
"circular-deps",
|
|
5065
|
+
"layer-violations",
|
|
5066
|
+
"complexity",
|
|
5067
|
+
"coupling",
|
|
5068
|
+
"forbidden-imports",
|
|
5069
|
+
"module-size",
|
|
5070
|
+
"dependency-depth"
|
|
5071
|
+
]
|
|
5072
|
+
}
|
|
5073
|
+
},
|
|
5074
|
+
required: ["path"]
|
|
5075
|
+
}
|
|
5076
|
+
};
|
|
5077
|
+
async function handleGetDecayTrends(input) {
|
|
5078
|
+
let projectPath;
|
|
5079
|
+
try {
|
|
5080
|
+
projectPath = sanitizePath(input.path);
|
|
5081
|
+
} catch (error) {
|
|
5082
|
+
return {
|
|
5083
|
+
content: [
|
|
5084
|
+
{
|
|
5085
|
+
type: "text",
|
|
5086
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
5087
|
+
}
|
|
5088
|
+
],
|
|
5089
|
+
isError: true
|
|
5090
|
+
};
|
|
5091
|
+
}
|
|
5092
|
+
try {
|
|
5093
|
+
const core = await import("./dist-YIKUBJLQ.js");
|
|
5094
|
+
const { TimelineManager } = core;
|
|
5095
|
+
const manager = new TimelineManager(projectPath);
|
|
5096
|
+
const timeline = manager.load();
|
|
5097
|
+
if (timeline.snapshots.length === 0) {
|
|
5098
|
+
return {
|
|
5099
|
+
content: [
|
|
5100
|
+
{
|
|
5101
|
+
type: "text",
|
|
5102
|
+
text: "No architecture snapshots found. Run `harness snapshot capture` to create the first snapshot."
|
|
5103
|
+
}
|
|
5104
|
+
]
|
|
5105
|
+
};
|
|
5106
|
+
}
|
|
5107
|
+
const trendOptions = {};
|
|
5108
|
+
if (input.last !== void 0) trendOptions.last = input.last;
|
|
5109
|
+
if (input.since !== void 0) trendOptions.since = input.since;
|
|
5110
|
+
const trends = manager.trends(trendOptions);
|
|
5111
|
+
if (input.category) {
|
|
5112
|
+
const categoryTrend = trends.categories[input.category];
|
|
5113
|
+
if (!categoryTrend) {
|
|
5114
|
+
return {
|
|
5115
|
+
content: [
|
|
5116
|
+
{
|
|
5117
|
+
type: "text",
|
|
5118
|
+
text: `No trend data for category "${input.category}".`
|
|
5119
|
+
}
|
|
5120
|
+
]
|
|
5121
|
+
};
|
|
5122
|
+
}
|
|
5123
|
+
return {
|
|
5124
|
+
content: [
|
|
5125
|
+
{
|
|
5126
|
+
type: "text",
|
|
5127
|
+
text: JSON.stringify(
|
|
5128
|
+
{
|
|
5129
|
+
category: input.category,
|
|
5130
|
+
trend: categoryTrend,
|
|
5131
|
+
snapshotCount: trends.snapshotCount,
|
|
5132
|
+
from: trends.from,
|
|
5133
|
+
to: trends.to
|
|
5134
|
+
},
|
|
5135
|
+
null,
|
|
5136
|
+
2
|
|
5137
|
+
)
|
|
5138
|
+
}
|
|
5139
|
+
]
|
|
5140
|
+
};
|
|
5141
|
+
}
|
|
5142
|
+
return {
|
|
5143
|
+
content: [
|
|
5144
|
+
{
|
|
5145
|
+
type: "text",
|
|
5146
|
+
text: JSON.stringify(trends, null, 2)
|
|
5147
|
+
}
|
|
5148
|
+
]
|
|
5149
|
+
};
|
|
5150
|
+
} catch (error) {
|
|
5151
|
+
return {
|
|
5152
|
+
content: [
|
|
5153
|
+
{
|
|
5154
|
+
type: "text",
|
|
5155
|
+
text: `Error computing decay trends: ${error instanceof Error ? error.message : String(error)}`
|
|
5156
|
+
}
|
|
5157
|
+
],
|
|
5158
|
+
isError: true
|
|
5159
|
+
};
|
|
5160
|
+
}
|
|
5161
|
+
}
|
|
5162
|
+
|
|
5075
5163
|
// src/mcp/tools/code-nav.ts
|
|
5076
5164
|
var codeOutlineDefinition = {
|
|
5077
5165
|
name: "code_outline",
|
|
@@ -5107,7 +5195,7 @@ async function handleCodeOutline(input) {
|
|
|
5107
5195
|
};
|
|
5108
5196
|
}
|
|
5109
5197
|
try {
|
|
5110
|
-
const { getOutline, formatOutline, EXTENSION_MAP } = await import("./dist-
|
|
5198
|
+
const { getOutline, formatOutline, EXTENSION_MAP } = await import("./dist-YIKUBJLQ.js");
|
|
5111
5199
|
const { stat } = await import("fs/promises");
|
|
5112
5200
|
const stats = await stat(targetPath).catch(() => null);
|
|
5113
5201
|
if (stats?.isFile()) {
|
|
@@ -5187,7 +5275,7 @@ async function handleCodeSearch(input) {
|
|
|
5187
5275
|
};
|
|
5188
5276
|
}
|
|
5189
5277
|
try {
|
|
5190
|
-
const { searchSymbols } = await import("./dist-
|
|
5278
|
+
const { searchSymbols } = await import("./dist-YIKUBJLQ.js");
|
|
5191
5279
|
const result = await searchSymbols(input.query, directory, input.glob);
|
|
5192
5280
|
const lines = [`Search: "${result.query}" \u2014 ${result.matches.length} matches`];
|
|
5193
5281
|
for (const match of result.matches) {
|
|
@@ -5256,7 +5344,7 @@ async function handleCodeUnfold(input) {
|
|
|
5256
5344
|
}
|
|
5257
5345
|
try {
|
|
5258
5346
|
if (input.symbol) {
|
|
5259
|
-
const { unfoldSymbol } = await import("./dist-
|
|
5347
|
+
const { unfoldSymbol } = await import("./dist-YIKUBJLQ.js");
|
|
5260
5348
|
const result = await unfoldSymbol(filePath, input.symbol);
|
|
5261
5349
|
const header = result.warning ? `${result.file}:${result.startLine}-${result.endLine} ${result.warning}
|
|
5262
5350
|
` : `${result.file}:${result.startLine}-${result.endLine}
|
|
@@ -5264,7 +5352,7 @@ async function handleCodeUnfold(input) {
|
|
|
5264
5352
|
return { content: [{ type: "text", text: header + result.content }] };
|
|
5265
5353
|
}
|
|
5266
5354
|
if (input.startLine != null && input.endLine != null) {
|
|
5267
|
-
const { unfoldRange } = await import("./dist-
|
|
5355
|
+
const { unfoldRange } = await import("./dist-YIKUBJLQ.js");
|
|
5268
5356
|
const result = await unfoldRange(filePath, input.startLine, input.endLine);
|
|
5269
5357
|
const header = `${result.file}:${result.startLine}-${result.endLine}
|
|
5270
5358
|
`;
|
|
@@ -5292,6 +5380,501 @@ async function handleCodeUnfold(input) {
|
|
|
5292
5380
|
}
|
|
5293
5381
|
}
|
|
5294
5382
|
|
|
5383
|
+
// src/mcp/tools/traceability.ts
|
|
5384
|
+
var checkTraceabilityDefinition = {
|
|
5385
|
+
name: "check_traceability",
|
|
5386
|
+
description: "Check requirement-to-code-to-test traceability for a spec or all specs",
|
|
5387
|
+
inputSchema: {
|
|
5388
|
+
type: "object",
|
|
5389
|
+
properties: {
|
|
5390
|
+
path: { type: "string", description: "Path to project root" },
|
|
5391
|
+
spec: { type: "string", description: "Specific spec file path to check" },
|
|
5392
|
+
feature: { type: "string", description: "Feature name filter" },
|
|
5393
|
+
mode: {
|
|
5394
|
+
type: "string",
|
|
5395
|
+
enum: ["summary", "detailed"],
|
|
5396
|
+
description: "Response density: summary returns coverage stats only, detailed returns full requirement list. Default: summary"
|
|
5397
|
+
}
|
|
5398
|
+
},
|
|
5399
|
+
required: ["path"]
|
|
5400
|
+
}
|
|
5401
|
+
};
|
|
5402
|
+
async function handleCheckTraceability(input) {
|
|
5403
|
+
try {
|
|
5404
|
+
const projectPath = sanitizePath(input.path);
|
|
5405
|
+
const store = await loadGraphStore(projectPath);
|
|
5406
|
+
if (!store) {
|
|
5407
|
+
return {
|
|
5408
|
+
content: [
|
|
5409
|
+
{
|
|
5410
|
+
type: "text",
|
|
5411
|
+
text: "No graph found. Run `harness scan` or use `ingest_source` tool first."
|
|
5412
|
+
}
|
|
5413
|
+
],
|
|
5414
|
+
isError: true
|
|
5415
|
+
};
|
|
5416
|
+
}
|
|
5417
|
+
const { queryTraceability } = await import("./dist-OEXTQQZC.js");
|
|
5418
|
+
const options = {};
|
|
5419
|
+
if (input.spec) options.specPath = input.spec;
|
|
5420
|
+
if (input.feature) options.featureName = input.feature;
|
|
5421
|
+
const results = queryTraceability(store, options);
|
|
5422
|
+
if (results.length === 0) {
|
|
5423
|
+
return {
|
|
5424
|
+
content: [
|
|
5425
|
+
{
|
|
5426
|
+
type: "text",
|
|
5427
|
+
text: JSON.stringify({
|
|
5428
|
+
status: "no-requirements",
|
|
5429
|
+
message: "No requirement nodes found in graph. Ingest specs with RequirementIngestor first."
|
|
5430
|
+
})
|
|
5431
|
+
}
|
|
5432
|
+
]
|
|
5433
|
+
};
|
|
5434
|
+
}
|
|
5435
|
+
const mode = input.mode ?? "summary";
|
|
5436
|
+
if (mode === "summary") {
|
|
5437
|
+
const summaries = results.map((r) => ({
|
|
5438
|
+
specPath: r.specPath,
|
|
5439
|
+
featureName: r.featureName,
|
|
5440
|
+
...r.summary
|
|
5441
|
+
}));
|
|
5442
|
+
const totals = summaries.reduce(
|
|
5443
|
+
(acc, s) => ({
|
|
5444
|
+
total: acc.total + s.total,
|
|
5445
|
+
withCode: acc.withCode + s.withCode,
|
|
5446
|
+
withTests: acc.withTests + s.withTests,
|
|
5447
|
+
fullyTraced: acc.fullyTraced + s.fullyTraced,
|
|
5448
|
+
untraceable: acc.untraceable + s.untraceable
|
|
5449
|
+
}),
|
|
5450
|
+
{ total: 0, withCode: 0, withTests: 0, fullyTraced: 0, untraceable: 0 }
|
|
5451
|
+
);
|
|
5452
|
+
const overallCoverage = totals.total > 0 ? Math.round(totals.fullyTraced / totals.total * 100) : 0;
|
|
5453
|
+
return {
|
|
5454
|
+
content: [
|
|
5455
|
+
{
|
|
5456
|
+
type: "text",
|
|
5457
|
+
text: JSON.stringify({
|
|
5458
|
+
mode: "summary",
|
|
5459
|
+
overallCoverage,
|
|
5460
|
+
totals,
|
|
5461
|
+
specs: summaries
|
|
5462
|
+
})
|
|
5463
|
+
}
|
|
5464
|
+
]
|
|
5465
|
+
};
|
|
5466
|
+
}
|
|
5467
|
+
return {
|
|
5468
|
+
content: [
|
|
5469
|
+
{
|
|
5470
|
+
type: "text",
|
|
5471
|
+
text: JSON.stringify({
|
|
5472
|
+
mode: "detailed",
|
|
5473
|
+
results
|
|
5474
|
+
})
|
|
5475
|
+
}
|
|
5476
|
+
]
|
|
5477
|
+
};
|
|
5478
|
+
} catch (error) {
|
|
5479
|
+
return {
|
|
5480
|
+
content: [
|
|
5481
|
+
{
|
|
5482
|
+
type: "text",
|
|
5483
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
5484
|
+
}
|
|
5485
|
+
],
|
|
5486
|
+
isError: true
|
|
5487
|
+
};
|
|
5488
|
+
}
|
|
5489
|
+
}
|
|
5490
|
+
|
|
5491
|
+
// src/mcp/tools/predict-failures.ts
|
|
5492
|
+
var predictFailuresDefinition = {
|
|
5493
|
+
name: "predict_failures",
|
|
5494
|
+
description: "Predict which architectural constraints will break and when, based on decay trends and planned roadmap features. Requires at least 3 timeline snapshots.",
|
|
5495
|
+
inputSchema: {
|
|
5496
|
+
type: "object",
|
|
5497
|
+
properties: {
|
|
5498
|
+
path: { type: "string", description: "Path to project root" },
|
|
5499
|
+
horizon: {
|
|
5500
|
+
type: "number",
|
|
5501
|
+
description: "Forecast horizon in weeks (default: 12)"
|
|
5502
|
+
},
|
|
5503
|
+
category: {
|
|
5504
|
+
type: "string",
|
|
5505
|
+
description: "Filter to a single metric category",
|
|
5506
|
+
enum: [
|
|
5507
|
+
"circular-deps",
|
|
5508
|
+
"layer-violations",
|
|
5509
|
+
"complexity",
|
|
5510
|
+
"coupling",
|
|
5511
|
+
"forbidden-imports",
|
|
5512
|
+
"module-size",
|
|
5513
|
+
"dependency-depth"
|
|
5514
|
+
]
|
|
5515
|
+
},
|
|
5516
|
+
includeRoadmap: {
|
|
5517
|
+
type: "boolean",
|
|
5518
|
+
description: "Include roadmap spec impact in forecasts (default: true)"
|
|
5519
|
+
}
|
|
5520
|
+
},
|
|
5521
|
+
required: ["path"]
|
|
5522
|
+
}
|
|
5523
|
+
};
|
|
5524
|
+
async function handlePredictFailures(input) {
|
|
5525
|
+
let projectPath;
|
|
5526
|
+
try {
|
|
5527
|
+
projectPath = sanitizePath(input.path);
|
|
5528
|
+
} catch (error) {
|
|
5529
|
+
return {
|
|
5530
|
+
content: [
|
|
5531
|
+
{
|
|
5532
|
+
type: "text",
|
|
5533
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
5534
|
+
}
|
|
5535
|
+
],
|
|
5536
|
+
isError: true
|
|
5537
|
+
};
|
|
5538
|
+
}
|
|
5539
|
+
try {
|
|
5540
|
+
const core = await import("./dist-YIKUBJLQ.js");
|
|
5541
|
+
const { TimelineManager, PredictionEngine, SpecImpactEstimator } = core;
|
|
5542
|
+
const manager = new TimelineManager(projectPath);
|
|
5543
|
+
const includeRoadmap = input.includeRoadmap !== false;
|
|
5544
|
+
const estimator = includeRoadmap ? new SpecImpactEstimator(projectPath) : null;
|
|
5545
|
+
const engine = new PredictionEngine(projectPath, manager, estimator);
|
|
5546
|
+
const opts = {
|
|
5547
|
+
includeRoadmap
|
|
5548
|
+
};
|
|
5549
|
+
if (input.horizon !== void 0) opts.horizon = input.horizon;
|
|
5550
|
+
if (input.category) opts.categories = [input.category];
|
|
5551
|
+
const result = engine.predict(opts);
|
|
5552
|
+
return {
|
|
5553
|
+
content: [
|
|
5554
|
+
{
|
|
5555
|
+
type: "text",
|
|
5556
|
+
text: JSON.stringify(result, null, 2)
|
|
5557
|
+
}
|
|
5558
|
+
]
|
|
5559
|
+
};
|
|
5560
|
+
} catch (error) {
|
|
5561
|
+
return {
|
|
5562
|
+
content: [
|
|
5563
|
+
{
|
|
5564
|
+
type: "text",
|
|
5565
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
5566
|
+
}
|
|
5567
|
+
],
|
|
5568
|
+
isError: true
|
|
5569
|
+
};
|
|
5570
|
+
}
|
|
5571
|
+
}
|
|
5572
|
+
|
|
5573
|
+
// src/skill/recommendation-rules.ts
|
|
5574
|
+
var FALLBACK_RULES = {
|
|
5575
|
+
"enforce-architecture": [
|
|
5576
|
+
{ signal: "circular-deps", hard: true },
|
|
5577
|
+
{ signal: "layer-violations", hard: true },
|
|
5578
|
+
{ signal: "high-coupling", metric: "fanOut", threshold: 20, weight: 0.8 },
|
|
5579
|
+
{ signal: "high-coupling", metric: "couplingRatio", threshold: 0.7, weight: 0.6 }
|
|
5580
|
+
],
|
|
5581
|
+
"dependency-health": [
|
|
5582
|
+
{ signal: "high-coupling", metric: "fanOut", threshold: 15, weight: 0.7 },
|
|
5583
|
+
{ signal: "anomaly-outlier", weight: 0.6 },
|
|
5584
|
+
{ signal: "articulation-point", weight: 0.5 }
|
|
5585
|
+
],
|
|
5586
|
+
tdd: [{ signal: "low-coverage", weight: 0.9 }],
|
|
5587
|
+
"codebase-cleanup": [
|
|
5588
|
+
{ signal: "dead-code", weight: 0.8 },
|
|
5589
|
+
{ signal: "drift", weight: 0.6 }
|
|
5590
|
+
],
|
|
5591
|
+
"security-scan": [{ signal: "security-findings", hard: true }],
|
|
5592
|
+
refactoring: [
|
|
5593
|
+
{ signal: "high-complexity", metric: "cyclomaticComplexity", threshold: 15, weight: 0.8 },
|
|
5594
|
+
{ signal: "high-coupling", metric: "couplingRatio", threshold: 0.5, weight: 0.6 }
|
|
5595
|
+
],
|
|
5596
|
+
"detect-doc-drift": [
|
|
5597
|
+
{ signal: "doc-gaps", weight: 0.7 },
|
|
5598
|
+
{ signal: "drift", weight: 0.5 }
|
|
5599
|
+
],
|
|
5600
|
+
perf: [{ signal: "perf-regression", weight: 0.8 }],
|
|
5601
|
+
"supply-chain-audit": [{ signal: "security-findings", weight: 0.6 }],
|
|
5602
|
+
"code-review": [
|
|
5603
|
+
{ signal: "high-complexity", weight: 0.5 },
|
|
5604
|
+
{ signal: "high-coupling", weight: 0.4 }
|
|
5605
|
+
],
|
|
5606
|
+
integrity: [
|
|
5607
|
+
{ signal: "drift", weight: 0.7 },
|
|
5608
|
+
{ signal: "dead-code", weight: 0.5 }
|
|
5609
|
+
],
|
|
5610
|
+
"soundness-review": [
|
|
5611
|
+
{ signal: "layer-violations", weight: 0.6 },
|
|
5612
|
+
{ signal: "circular-deps", weight: 0.5 }
|
|
5613
|
+
],
|
|
5614
|
+
debugging: [
|
|
5615
|
+
{ signal: "perf-regression", weight: 0.5 },
|
|
5616
|
+
{ signal: "anomaly-outlier", weight: 0.6 }
|
|
5617
|
+
],
|
|
5618
|
+
"hotspot-detector": [
|
|
5619
|
+
{ signal: "high-complexity", metric: "cyclomaticComplexity", threshold: 20, weight: 0.9 },
|
|
5620
|
+
{ signal: "anomaly-outlier", weight: 0.7 },
|
|
5621
|
+
{ signal: "articulation-point", weight: 0.8 }
|
|
5622
|
+
],
|
|
5623
|
+
"cleanup-dead-code": [{ signal: "dead-code", hard: true }]
|
|
5624
|
+
};
|
|
5625
|
+
|
|
5626
|
+
// src/skill/recommendation-engine.ts
|
|
5627
|
+
function resolveMetricValue(metrics, metricName) {
|
|
5628
|
+
switch (metricName) {
|
|
5629
|
+
case "fanOut":
|
|
5630
|
+
return metrics.maxFanOut;
|
|
5631
|
+
case "couplingRatio":
|
|
5632
|
+
return metrics.avgCouplingRatio;
|
|
5633
|
+
case "cyclomaticComplexity":
|
|
5634
|
+
return metrics.maxCyclomaticComplexity;
|
|
5635
|
+
case "coverage":
|
|
5636
|
+
return metrics.testCoverage !== null ? 100 - metrics.testCoverage : null;
|
|
5637
|
+
default:
|
|
5638
|
+
return null;
|
|
5639
|
+
}
|
|
5640
|
+
}
|
|
5641
|
+
function buildSkillAddressIndex(skills) {
|
|
5642
|
+
const index = /* @__PURE__ */ new Map();
|
|
5643
|
+
for (const [name, entry] of Object.entries(skills)) {
|
|
5644
|
+
const addresses = entry.addresses.length > 0 ? entry.addresses : FALLBACK_RULES[name] ?? [];
|
|
5645
|
+
index.set(name, { addresses, dependsOn: entry.dependsOn });
|
|
5646
|
+
}
|
|
5647
|
+
for (const [name, addresses] of Object.entries(FALLBACK_RULES)) {
|
|
5648
|
+
if (!index.has(name)) {
|
|
5649
|
+
index.set(name, { addresses, dependsOn: [] });
|
|
5650
|
+
}
|
|
5651
|
+
}
|
|
5652
|
+
return index;
|
|
5653
|
+
}
|
|
5654
|
+
function matchHardRules(snapshot, skillIndex) {
|
|
5655
|
+
const activeSignals = new Set(snapshot.signals);
|
|
5656
|
+
const results = [];
|
|
5657
|
+
for (const [skillName, entry] of skillIndex) {
|
|
5658
|
+
const hardAddresses = entry.addresses.filter((a) => a.hard === true);
|
|
5659
|
+
const matchedSignals = [];
|
|
5660
|
+
const reasons = [];
|
|
5661
|
+
for (const addr of hardAddresses) {
|
|
5662
|
+
if (activeSignals.has(addr.signal)) {
|
|
5663
|
+
matchedSignals.push(addr.signal);
|
|
5664
|
+
reasons.push(`[CRITICAL] Signal '${addr.signal}' is active`);
|
|
5665
|
+
}
|
|
5666
|
+
}
|
|
5667
|
+
if (matchedSignals.length > 0) {
|
|
5668
|
+
results.push({
|
|
5669
|
+
skillName,
|
|
5670
|
+
score: 1,
|
|
5671
|
+
urgency: "critical",
|
|
5672
|
+
reasons,
|
|
5673
|
+
sequence: 0,
|
|
5674
|
+
// assigned later by sequencer
|
|
5675
|
+
triggeredBy: matchedSignals
|
|
5676
|
+
});
|
|
5677
|
+
}
|
|
5678
|
+
}
|
|
5679
|
+
return results;
|
|
5680
|
+
}
|
|
5681
|
+
function clamp(value, min, max) {
|
|
5682
|
+
return Math.min(Math.max(value, min), max);
|
|
5683
|
+
}
|
|
5684
|
+
var DEFAULT_WEIGHT = 0.5;
|
|
5685
|
+
function scoreByHealth(snapshot, skillIndex) {
|
|
5686
|
+
const activeSignals = new Set(snapshot.signals);
|
|
5687
|
+
const results = [];
|
|
5688
|
+
for (const [skillName, entry] of skillIndex) {
|
|
5689
|
+
const softAddresses = entry.addresses.filter((a) => !a.hard);
|
|
5690
|
+
const contributions = [];
|
|
5691
|
+
const triggeredBy = [];
|
|
5692
|
+
const reasons = [];
|
|
5693
|
+
for (const addr of softAddresses) {
|
|
5694
|
+
if (!activeSignals.has(addr.signal)) continue;
|
|
5695
|
+
const weight = addr.weight ?? DEFAULT_WEIGHT;
|
|
5696
|
+
if (addr.metric && addr.threshold !== void 0) {
|
|
5697
|
+
const actual = resolveMetricValue(snapshot.metrics, addr.metric);
|
|
5698
|
+
if (actual === null) continue;
|
|
5699
|
+
const distance = clamp((actual - addr.threshold) / addr.threshold, 0, 1);
|
|
5700
|
+
const contribution = weight * distance;
|
|
5701
|
+
contributions.push(contribution);
|
|
5702
|
+
triggeredBy.push(addr.signal);
|
|
5703
|
+
reasons.push(
|
|
5704
|
+
`${addr.metric} = ${actual} (threshold ${addr.threshold}, distance ${distance.toFixed(2)})`
|
|
5705
|
+
);
|
|
5706
|
+
} else {
|
|
5707
|
+
contributions.push(weight);
|
|
5708
|
+
triggeredBy.push(addr.signal);
|
|
5709
|
+
reasons.push(`Signal '${addr.signal}' is active (weight ${weight})`);
|
|
5710
|
+
}
|
|
5711
|
+
}
|
|
5712
|
+
if (contributions.length === 0) continue;
|
|
5713
|
+
const score = clamp(contributions.reduce((sum, c) => sum + c, 0) / contributions.length, 0, 1);
|
|
5714
|
+
const urgency = score >= 0.7 ? "recommended" : "nice-to-have";
|
|
5715
|
+
results.push({
|
|
5716
|
+
skillName,
|
|
5717
|
+
score: Math.round(score * 1e3) / 1e3,
|
|
5718
|
+
// round to 3 decimal places
|
|
5719
|
+
urgency,
|
|
5720
|
+
reasons,
|
|
5721
|
+
sequence: 0,
|
|
5722
|
+
triggeredBy: [...new Set(triggeredBy)]
|
|
5723
|
+
});
|
|
5724
|
+
}
|
|
5725
|
+
return results;
|
|
5726
|
+
}
|
|
5727
|
+
var DIAGNOSTIC_KEYWORDS = ["health", "detect", "analyze", "audit", "hotspot", "debugging"];
|
|
5728
|
+
var FIX_KEYWORDS = ["enforce", "cleanup", "fix", "refactor", "codebase"];
|
|
5729
|
+
var VALIDATION_KEYWORDS = ["verify", "test", "tdd", "review", "soundness", "integrity"];
|
|
5730
|
+
function classifyPhase(skillName) {
|
|
5731
|
+
const lower = skillName.toLowerCase();
|
|
5732
|
+
if (DIAGNOSTIC_KEYWORDS.some((kw) => lower.includes(kw))) return 0;
|
|
5733
|
+
if (FIX_KEYWORDS.some((kw) => lower.includes(kw))) return 1;
|
|
5734
|
+
if (VALIDATION_KEYWORDS.some((kw) => lower.includes(kw))) return 2;
|
|
5735
|
+
return 3;
|
|
5736
|
+
}
|
|
5737
|
+
function heuristicComparator(recMap) {
|
|
5738
|
+
return (a, b) => {
|
|
5739
|
+
const phaseA = classifyPhase(a);
|
|
5740
|
+
const phaseB = classifyPhase(b);
|
|
5741
|
+
if (phaseA !== phaseB) return phaseA - phaseB;
|
|
5742
|
+
return (recMap.get(b)?.score ?? 0) - (recMap.get(a)?.score ?? 0);
|
|
5743
|
+
};
|
|
5744
|
+
}
|
|
5745
|
+
function buildDepGraph(nameSet, skillDeps) {
|
|
5746
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
5747
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
5748
|
+
for (const name of nameSet) {
|
|
5749
|
+
inDegree.set(name, 0);
|
|
5750
|
+
adjacency.set(name, []);
|
|
5751
|
+
}
|
|
5752
|
+
for (const name of nameSet) {
|
|
5753
|
+
for (const dep of skillDeps.get(name) ?? []) {
|
|
5754
|
+
if (!nameSet.has(dep)) continue;
|
|
5755
|
+
adjacency.get(dep).push(name);
|
|
5756
|
+
inDegree.set(name, (inDegree.get(name) ?? 0) + 1);
|
|
5757
|
+
}
|
|
5758
|
+
}
|
|
5759
|
+
return { inDegree, adjacency };
|
|
5760
|
+
}
|
|
5761
|
+
function sequenceRecommendations(recommendations, skillDeps) {
|
|
5762
|
+
if (recommendations.length === 0) return [];
|
|
5763
|
+
const nameSet = new Set(recommendations.map((r) => r.skillName));
|
|
5764
|
+
const recMap = new Map(recommendations.map((r) => [r.skillName, r]));
|
|
5765
|
+
const compare = heuristicComparator(recMap);
|
|
5766
|
+
const { inDegree, adjacency } = buildDepGraph(nameSet, skillDeps);
|
|
5767
|
+
const sorted = [];
|
|
5768
|
+
let sequence = 1;
|
|
5769
|
+
let queue = [...nameSet].filter((n) => (inDegree.get(n) ?? 0) === 0).sort(compare);
|
|
5770
|
+
while (queue.length > 0) {
|
|
5771
|
+
const nextQueue = [];
|
|
5772
|
+
for (const name of queue) {
|
|
5773
|
+
const rec = recMap.get(name);
|
|
5774
|
+
rec.sequence = sequence++;
|
|
5775
|
+
sorted.push(rec);
|
|
5776
|
+
for (const dependent of adjacency.get(name) ?? []) {
|
|
5777
|
+
const newDeg = (inDegree.get(dependent) ?? 1) - 1;
|
|
5778
|
+
inDegree.set(dependent, newDeg);
|
|
5779
|
+
if (newDeg === 0) nextQueue.push(dependent);
|
|
5780
|
+
}
|
|
5781
|
+
}
|
|
5782
|
+
queue = nextQueue.sort(compare);
|
|
5783
|
+
}
|
|
5784
|
+
return sorted;
|
|
5785
|
+
}
|
|
5786
|
+
function recommend(snapshot, skills, options = {}) {
|
|
5787
|
+
const top = options.top ?? 5;
|
|
5788
|
+
if (snapshot.signals.length === 0) {
|
|
5789
|
+
return {
|
|
5790
|
+
recommendations: [],
|
|
5791
|
+
snapshotAge: "none",
|
|
5792
|
+
sequenceReasoning: "No active signals detected in health snapshot."
|
|
5793
|
+
};
|
|
5794
|
+
}
|
|
5795
|
+
const addressIndex = buildSkillAddressIndex(skills);
|
|
5796
|
+
const hardRecs = matchHardRules(snapshot, addressIndex);
|
|
5797
|
+
const softRecs = scoreByHealth(snapshot, addressIndex);
|
|
5798
|
+
const hardSkills = new Set(hardRecs.map((r) => r.skillName));
|
|
5799
|
+
const merged = [...hardRecs, ...softRecs.filter((r) => !hardSkills.has(r.skillName))];
|
|
5800
|
+
merged.sort((a, b) => b.score - a.score);
|
|
5801
|
+
const limited = merged.slice(0, top);
|
|
5802
|
+
const depMap = /* @__PURE__ */ new Map();
|
|
5803
|
+
for (const [name, entry] of addressIndex) {
|
|
5804
|
+
depMap.set(name, entry.dependsOn);
|
|
5805
|
+
}
|
|
5806
|
+
const sequenced = sequenceRecommendations(limited, depMap);
|
|
5807
|
+
const criticalCount = sequenced.filter((r) => r.urgency === "critical").length;
|
|
5808
|
+
const phases = sequenced.map((r) => `${r.sequence}. ${r.skillName}`).join(" -> ");
|
|
5809
|
+
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.`;
|
|
5810
|
+
return {
|
|
5811
|
+
recommendations: sequenced,
|
|
5812
|
+
snapshotAge: "fresh",
|
|
5813
|
+
sequenceReasoning: reasoning
|
|
5814
|
+
};
|
|
5815
|
+
}
|
|
5816
|
+
|
|
5817
|
+
// src/mcp/tools/recommend-skills.ts
|
|
5818
|
+
var recommendSkillsDefinition = {
|
|
5819
|
+
name: "recommend_skills",
|
|
5820
|
+
description: "Recommend skills based on codebase health. Returns sequenced workflow with urgency markers.",
|
|
5821
|
+
inputSchema: {
|
|
5822
|
+
type: "object",
|
|
5823
|
+
properties: {
|
|
5824
|
+
path: {
|
|
5825
|
+
type: "string",
|
|
5826
|
+
description: "Project root path (defaults to cwd)"
|
|
5827
|
+
},
|
|
5828
|
+
noCache: {
|
|
5829
|
+
type: "boolean",
|
|
5830
|
+
description: "Force fresh health snapshot even if cache is fresh"
|
|
5831
|
+
},
|
|
5832
|
+
top: {
|
|
5833
|
+
type: "number",
|
|
5834
|
+
description: "Max recommendations to return (default 5)"
|
|
5835
|
+
}
|
|
5836
|
+
},
|
|
5837
|
+
required: []
|
|
5838
|
+
}
|
|
5839
|
+
};
|
|
5840
|
+
async function handleRecommendSkills(input) {
|
|
5841
|
+
const projectRoot = input.path || process.cwd();
|
|
5842
|
+
const noCache = input.noCache || false;
|
|
5843
|
+
const top = input.top || 5;
|
|
5844
|
+
let snapshot = null;
|
|
5845
|
+
let usedCache = false;
|
|
5846
|
+
if (!noCache) {
|
|
5847
|
+
const cached = loadCachedSnapshot(projectRoot);
|
|
5848
|
+
if (cached && isSnapshotFresh(cached, projectRoot)) {
|
|
5849
|
+
snapshot = cached;
|
|
5850
|
+
usedCache = true;
|
|
5851
|
+
}
|
|
5852
|
+
}
|
|
5853
|
+
if (!snapshot) {
|
|
5854
|
+
snapshot = await captureHealthSnapshot(projectRoot);
|
|
5855
|
+
}
|
|
5856
|
+
const configResult = resolveConfig();
|
|
5857
|
+
const tierOverrides = configResult.ok ? configResult.value.skills?.tierOverrides : void 0;
|
|
5858
|
+
const index = loadOrRebuildIndex("claude-code", projectRoot, tierOverrides);
|
|
5859
|
+
const skills = {};
|
|
5860
|
+
for (const [name, entry] of Object.entries(index.skills)) {
|
|
5861
|
+
skills[name] = { addresses: entry.addresses, dependsOn: entry.dependsOn };
|
|
5862
|
+
}
|
|
5863
|
+
const result = recommend(snapshot, skills, { top });
|
|
5864
|
+
const output = {
|
|
5865
|
+
...result,
|
|
5866
|
+
snapshotAge: usedCache ? "cached" : "fresh"
|
|
5867
|
+
};
|
|
5868
|
+
return {
|
|
5869
|
+
content: [
|
|
5870
|
+
{
|
|
5871
|
+
type: "text",
|
|
5872
|
+
text: JSON.stringify(output, null, 2)
|
|
5873
|
+
}
|
|
5874
|
+
]
|
|
5875
|
+
};
|
|
5876
|
+
}
|
|
5877
|
+
|
|
5295
5878
|
// src/mcp/server.ts
|
|
5296
5879
|
var TOOL_DEFINITIONS = [
|
|
5297
5880
|
validateToolDefinition,
|
|
@@ -5342,7 +5925,11 @@ var TOOL_DEFINITIONS = [
|
|
|
5342
5925
|
searchSkillsDefinition,
|
|
5343
5926
|
codeOutlineDefinition,
|
|
5344
5927
|
codeSearchDefinition,
|
|
5345
|
-
codeUnfoldDefinition
|
|
5928
|
+
codeUnfoldDefinition,
|
|
5929
|
+
getDecayTrendsDefinition,
|
|
5930
|
+
checkTraceabilityDefinition,
|
|
5931
|
+
predictFailuresDefinition,
|
|
5932
|
+
recommendSkillsDefinition
|
|
5346
5933
|
].map((def) => ({ ...def, trustedOutput: true }));
|
|
5347
5934
|
var TOOL_HANDLERS = {
|
|
5348
5935
|
validate_project: handleValidateProject,
|
|
@@ -5393,7 +5980,11 @@ var TOOL_HANDLERS = {
|
|
|
5393
5980
|
search_skills: handleSearchSkills,
|
|
5394
5981
|
code_outline: handleCodeOutline,
|
|
5395
5982
|
code_search: handleCodeSearch,
|
|
5396
|
-
code_unfold: handleCodeUnfold
|
|
5983
|
+
code_unfold: handleCodeUnfold,
|
|
5984
|
+
get_decay_trends: handleGetDecayTrends,
|
|
5985
|
+
check_traceability: handleCheckTraceability,
|
|
5986
|
+
predict_failures: handlePredictFailures,
|
|
5987
|
+
recommend_skills: handleRecommendSkills
|
|
5397
5988
|
};
|
|
5398
5989
|
var RESOURCE_DEFINITIONS = [
|
|
5399
5990
|
{
|
|
@@ -5479,7 +6070,7 @@ async function appendUpdateNotification(result, resolvedRoot) {
|
|
|
5479
6070
|
shouldRunCheck,
|
|
5480
6071
|
readCheckState,
|
|
5481
6072
|
spawnBackgroundCheck
|
|
5482
|
-
} = await import("./dist-
|
|
6073
|
+
} = await import("./dist-YIKUBJLQ.js");
|
|
5483
6074
|
const { CLI_VERSION } = await import("./version-KFFPOQAX.js");
|
|
5484
6075
|
const configInterval = readConfigInterval(resolvedRoot);
|
|
5485
6076
|
const DEFAULT_INTERVAL = 864e5;
|
|
@@ -5561,7 +6152,12 @@ export {
|
|
|
5561
6152
|
generateSlashCommands,
|
|
5562
6153
|
handleOrphanDeletion,
|
|
5563
6154
|
createGenerateSlashCommandsCommand,
|
|
6155
|
+
loadOrRebuildIndex,
|
|
5564
6156
|
handleGetImpact,
|
|
6157
|
+
isSnapshotFresh,
|
|
6158
|
+
loadCachedSnapshot,
|
|
6159
|
+
captureHealthSnapshot,
|
|
6160
|
+
recommend,
|
|
5565
6161
|
getToolDefinitions,
|
|
5566
6162
|
createHarnessServer,
|
|
5567
6163
|
startServer
|