@harness-engineering/cli 1.9.0 → 1.11.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/enforce-architecture/SKILL.md +4 -0
- package/dist/agents/skills/claude-code/harness-autopilot/SKILL.md +7 -2
- package/dist/agents/skills/claude-code/harness-brainstorming/SKILL.md +10 -1
- package/dist/agents/skills/claude-code/harness-execution/SKILL.md +2 -2
- package/dist/agents/skills/claude-code/harness-parallel-agents/SKILL.md +105 -20
- package/dist/agents/skills/claude-code/harness-pre-commit-review/SKILL.md +37 -0
- package/dist/agents/skills/gemini-cli/enforce-architecture/SKILL.md +4 -0
- package/dist/agents/skills/gemini-cli/harness-autopilot/SKILL.md +7 -2
- package/dist/agents/skills/gemini-cli/harness-brainstorming/SKILL.md +10 -1
- package/dist/agents/skills/gemini-cli/harness-execution/SKILL.md +2 -2
- package/dist/agents/skills/gemini-cli/harness-parallel-agents/SKILL.md +105 -20
- package/dist/agents/skills/gemini-cli/harness-pre-commit-review/SKILL.md +37 -0
- package/dist/agents-md-ZFV6RR5J.js +8 -0
- package/dist/architecture-EXNUMH5R.js +13 -0
- package/dist/bin/harness-mcp.d.ts +1 -0
- package/dist/bin/harness-mcp.js +28 -0
- package/dist/bin/harness.js +42 -8
- package/dist/check-phase-gate-VZFOY2PO.js +12 -0
- package/dist/chunk-2NCIKJES.js +470 -0
- package/dist/chunk-2YPZKGAG.js +62 -0
- package/dist/{chunk-CGSHUJES.js → chunk-2YSQOUHO.js} +4484 -2688
- package/dist/chunk-3WGJMBKH.js +45 -0
- package/dist/{chunk-ULSRSP53.js → chunk-6N4R6FVX.js} +11 -112
- package/dist/{chunk-6JIT7CEM.js → chunk-72GHBOL2.js} +1 -1
- package/dist/chunk-BM3PWGXQ.js +14 -0
- package/dist/chunk-C2ERUR3L.js +255 -0
- package/dist/chunk-EBJQ6N4M.js +39 -0
- package/dist/chunk-GNGELAXY.js +293 -0
- package/dist/chunk-GSIVNYVJ.js +187 -0
- package/dist/chunk-HD4IBGLA.js +80 -0
- package/dist/chunk-I6JZYEGT.js +4361 -0
- package/dist/chunk-IDZNPTYD.js +16 -0
- package/dist/chunk-JSTQ3AWB.js +31 -0
- package/dist/chunk-K6XAPGML.js +27 -0
- package/dist/chunk-KET4QQZB.js +8 -0
- package/dist/chunk-L2KLU56K.js +125 -0
- package/dist/chunk-MHBMTPW7.js +29 -0
- package/dist/chunk-NC6PXVWT.js +116 -0
- package/dist/chunk-NKDM3FMH.js +52 -0
- package/dist/chunk-PA2XHK75.js +248 -0
- package/dist/chunk-Q6AB7W5Z.js +135 -0
- package/dist/chunk-QPEH2QPG.js +347 -0
- package/dist/chunk-TEFCFC4H.js +15 -0
- package/dist/chunk-TI4TGEX6.js +85 -0
- package/dist/chunk-TRAPF4IX.js +185 -0
- package/dist/chunk-VRFZWGMS.js +68 -0
- package/dist/chunk-VUCPTQ6G.js +67 -0
- package/dist/chunk-W6Y7ZW3Y.js +13 -0
- package/dist/chunk-WJZDO6OY.js +103 -0
- package/dist/chunk-WUJTCNOU.js +122 -0
- package/dist/chunk-X3MN5UQJ.js +89 -0
- package/dist/chunk-Z75JC6I2.js +189 -0
- package/dist/chunk-ZOAWBDWU.js +72 -0
- package/dist/{chunk-RTPHUDZS.js → chunk-ZWC3MN5E.js} +1944 -2779
- package/dist/ci-workflow-K5RCRNYR.js +8 -0
- package/dist/constants-5JGUXPEK.js +6 -0
- package/dist/create-skill-WPXHSLX2.js +11 -0
- package/dist/dist-D4RYGUZE.js +14 -0
- package/dist/{dist-C5PYIQPF.js → dist-JVZ2MKBC.js} +108 -6
- package/dist/dist-L7LAAQAS.js +18 -0
- package/dist/{dist-I7DB5VKB.js → dist-M6BQODWC.js} +1145 -0
- package/dist/docs-PWCUVYWU.js +12 -0
- package/dist/engine-6XUP6GAK.js +8 -0
- package/dist/entropy-4I6JEYAC.js +12 -0
- package/dist/feedback-TNIW534S.js +18 -0
- package/dist/generate-agent-definitions-MWKEA5NU.js +15 -0
- package/dist/glob-helper-5OHBUQAI.js +52 -0
- package/dist/graph-loader-KO4GJ5N2.js +8 -0
- package/dist/index.d.ts +328 -12
- package/dist/index.js +93 -34
- package/dist/loader-4FIPIFII.js +10 -0
- package/dist/mcp-MOKLYNZL.js +34 -0
- package/dist/performance-BTOJCPXU.js +24 -0
- package/dist/review-pipeline-3YTW3463.js +9 -0
- package/dist/runner-VMYLHWOC.js +6 -0
- package/dist/runtime-GO7K2PJE.js +9 -0
- package/dist/security-4P2GGFF6.js +9 -0
- package/dist/skill-executor-RG45LUO5.js +8 -0
- package/dist/templates/orchestrator/WORKFLOW.md +48 -0
- package/dist/templates/orchestrator/template.json +6 -0
- package/dist/validate-JN44D2Q7.js +12 -0
- package/dist/validate-cross-check-DB7RIFFF.js +8 -0
- package/dist/version-KFFPOQAX.js +6 -0
- package/package.json +13 -7
- package/dist/create-skill-UZOHMXRU.js +0 -8
- package/dist/validate-cross-check-VG573VZO.js +0 -7
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resultToMcpResponse
|
|
3
|
+
} from "./chunk-IDZNPTYD.js";
|
|
4
|
+
import {
|
|
5
|
+
sanitizePath
|
|
6
|
+
} from "./chunk-W6Y7ZW3Y.js";
|
|
7
|
+
import {
|
|
8
|
+
Ok
|
|
9
|
+
} from "./chunk-MHBMTPW7.js";
|
|
10
|
+
|
|
11
|
+
// src/mcp/tools/entropy.ts
|
|
12
|
+
async function loadEntropyGraphOptions(projectPath) {
|
|
13
|
+
const { loadGraphStore } = await import("./graph-loader-KO4GJ5N2.js");
|
|
14
|
+
const store = await loadGraphStore(projectPath);
|
|
15
|
+
if (!store) return void 0;
|
|
16
|
+
const { GraphEntropyAdapter } = await import("./dist-M6BQODWC.js");
|
|
17
|
+
const adapter = new GraphEntropyAdapter(store);
|
|
18
|
+
const driftData = adapter.computeDriftData();
|
|
19
|
+
const deadCodeData = adapter.computeDeadCodeData();
|
|
20
|
+
return {
|
|
21
|
+
graphDriftData: {
|
|
22
|
+
staleEdges: driftData.staleEdges.map((e) => ({
|
|
23
|
+
docNodeId: e.docNodeId,
|
|
24
|
+
codeNodeId: e.codeNodeId,
|
|
25
|
+
edgeType: e.edgeType
|
|
26
|
+
})),
|
|
27
|
+
missingTargets: [...driftData.missingTargets]
|
|
28
|
+
},
|
|
29
|
+
graphDeadCodeData: {
|
|
30
|
+
reachableNodeIds: new Set(deadCodeData.reachableNodeIds),
|
|
31
|
+
unreachableNodes: deadCodeData.unreachableNodes.map((n) => ({
|
|
32
|
+
id: n.id,
|
|
33
|
+
type: n.type,
|
|
34
|
+
name: n.name,
|
|
35
|
+
...n.path !== void 0 && { path: n.path }
|
|
36
|
+
}))
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
var detectEntropyDefinition = {
|
|
41
|
+
name: "detect_entropy",
|
|
42
|
+
description: "Detect documentation drift, dead code, and pattern violations. Optionally auto-fix detected issues.",
|
|
43
|
+
inputSchema: {
|
|
44
|
+
type: "object",
|
|
45
|
+
properties: {
|
|
46
|
+
path: { type: "string", description: "Path to project root" },
|
|
47
|
+
type: {
|
|
48
|
+
type: "string",
|
|
49
|
+
enum: ["drift", "dead-code", "patterns", "all"],
|
|
50
|
+
description: "Type of entropy to detect (default: all)"
|
|
51
|
+
},
|
|
52
|
+
autoFix: {
|
|
53
|
+
type: "boolean",
|
|
54
|
+
description: "When true, apply fixes after analysis. Default: false (analysis only)"
|
|
55
|
+
},
|
|
56
|
+
dryRun: {
|
|
57
|
+
type: "boolean",
|
|
58
|
+
description: "Preview fixes without applying (only used when autoFix is true)"
|
|
59
|
+
},
|
|
60
|
+
fixTypes: {
|
|
61
|
+
type: "array",
|
|
62
|
+
items: {
|
|
63
|
+
type: "string",
|
|
64
|
+
enum: [
|
|
65
|
+
"unused-imports",
|
|
66
|
+
"dead-files",
|
|
67
|
+
"dead-exports",
|
|
68
|
+
"commented-code",
|
|
69
|
+
"orphaned-deps",
|
|
70
|
+
"forbidden-import-replacement",
|
|
71
|
+
"import-ordering"
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
description: "Specific fix types to apply (default: all safe types). Only used when autoFix is true."
|
|
75
|
+
},
|
|
76
|
+
mode: {
|
|
77
|
+
type: "string",
|
|
78
|
+
enum: ["summary", "detailed"],
|
|
79
|
+
description: "Response density: summary returns issue counts and top issues per category, detailed returns full findings. Default: detailed"
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
required: ["path"]
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
async function handleDetectEntropy(input) {
|
|
86
|
+
try {
|
|
87
|
+
const { EntropyAnalyzer } = await import("./dist-JVZ2MKBC.js");
|
|
88
|
+
const typeFilter = input.type ?? "all";
|
|
89
|
+
const analyzer = new EntropyAnalyzer({
|
|
90
|
+
rootDir: sanitizePath(input.path),
|
|
91
|
+
analyze: {
|
|
92
|
+
drift: typeFilter === "all" || typeFilter === "drift",
|
|
93
|
+
deadCode: typeFilter === "all" || typeFilter === "dead-code",
|
|
94
|
+
patterns: typeFilter === "all" || typeFilter === "patterns"
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
const graphOptions = await loadEntropyGraphOptions(sanitizePath(input.path));
|
|
98
|
+
const result = await analyzer.analyze(graphOptions);
|
|
99
|
+
if (input.mode === "summary" && result.ok && !input.autoFix) {
|
|
100
|
+
const report2 = result.value;
|
|
101
|
+
const summary = {};
|
|
102
|
+
if (report2.drift) {
|
|
103
|
+
const driftIssues = (report2.drift.drifts ?? []).map(
|
|
104
|
+
(d) => `Drift: ${d.type}${d.file ? ` in ${d.file}` : ""}`
|
|
105
|
+
);
|
|
106
|
+
summary["drift"] = {
|
|
107
|
+
issueCount: driftIssues.length,
|
|
108
|
+
topIssues: driftIssues.slice(0, 3)
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (report2.deadCode) {
|
|
112
|
+
const deadIssues = [
|
|
113
|
+
...(report2.deadCode.unusedImports ?? []).map(
|
|
114
|
+
(i) => `Unused import: ${i.specifiers.join(", ")} from ${i.source}`
|
|
115
|
+
),
|
|
116
|
+
...(report2.deadCode.deadExports ?? []).map((e) => `Dead export: ${e.name} in ${e.file}`),
|
|
117
|
+
...(report2.deadCode.deadFiles ?? []).map((f) => `Dead file: ${f.path}`)
|
|
118
|
+
];
|
|
119
|
+
summary["deadCode"] = {
|
|
120
|
+
issueCount: deadIssues.length,
|
|
121
|
+
topIssues: deadIssues.slice(0, 3)
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
if (report2.patterns) {
|
|
125
|
+
const patternIssues = (report2.patterns.violations ?? []).map(
|
|
126
|
+
(v) => `${v.pattern}: ${v.file}`
|
|
127
|
+
);
|
|
128
|
+
summary["patterns"] = {
|
|
129
|
+
issueCount: patternIssues.length,
|
|
130
|
+
topIssues: patternIssues.slice(0, 3)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const totalIssues = Object.values(summary).reduce((s, c) => s + c.issueCount, 0);
|
|
134
|
+
return {
|
|
135
|
+
content: [
|
|
136
|
+
{
|
|
137
|
+
type: "text",
|
|
138
|
+
text: JSON.stringify({ mode: "summary", totalIssues, categories: summary })
|
|
139
|
+
}
|
|
140
|
+
]
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (!input.autoFix) {
|
|
144
|
+
return resultToMcpResponse(result);
|
|
145
|
+
}
|
|
146
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
147
|
+
const { createFixes, applyFixes, generateSuggestions } = await import("./dist-JVZ2MKBC.js");
|
|
148
|
+
const report = result.value;
|
|
149
|
+
const deadCode = report.deadCode;
|
|
150
|
+
const fixTypesConfig = input.fixTypes ? { fixTypes: input.fixTypes } : void 0;
|
|
151
|
+
const fixes = deadCode ? createFixes(deadCode, fixTypesConfig) : [];
|
|
152
|
+
const suggestions = generateSuggestions(report.deadCode, report.drift, report.patterns);
|
|
153
|
+
if (input.dryRun) {
|
|
154
|
+
return {
|
|
155
|
+
content: [
|
|
156
|
+
{ type: "text", text: JSON.stringify({ analysis: report, fixes, suggestions }) }
|
|
157
|
+
]
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if (fixes.length > 0) {
|
|
161
|
+
const applied = await applyFixes(fixes, {});
|
|
162
|
+
if (!applied.ok) return resultToMcpResponse(applied);
|
|
163
|
+
return {
|
|
164
|
+
content: [
|
|
165
|
+
{
|
|
166
|
+
type: "text",
|
|
167
|
+
text: JSON.stringify({ analysis: report, ...applied.value, suggestions })
|
|
168
|
+
}
|
|
169
|
+
]
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return resultToMcpResponse(Ok({ analysis: report, fixes: [], applied: 0, suggestions }));
|
|
173
|
+
} catch (error) {
|
|
174
|
+
return {
|
|
175
|
+
content: [
|
|
176
|
+
{
|
|
177
|
+
type: "text",
|
|
178
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
179
|
+
}
|
|
180
|
+
],
|
|
181
|
+
isError: true
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export {
|
|
187
|
+
detectEntropyDefinition,
|
|
188
|
+
handleDetectEntropy
|
|
189
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// src/slash-commands/types.ts
|
|
2
|
+
var VALID_PLATFORMS = ["claude-code", "gemini-cli"];
|
|
3
|
+
var GENERATED_HEADER_CLAUDE = "<!-- Generated by harness generate-slash-commands. Do not edit. -->";
|
|
4
|
+
var GENERATED_HEADER_GEMINI = "# Generated by harness generate-slash-commands. Do not edit.";
|
|
5
|
+
|
|
6
|
+
// src/slash-commands/sync.ts
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
import path from "path";
|
|
9
|
+
|
|
10
|
+
// src/agent-definitions/constants.ts
|
|
11
|
+
var GENERATED_HEADER_AGENT = "<!-- Generated by harness generate-agent-definitions. Do not edit. -->";
|
|
12
|
+
|
|
13
|
+
// src/slash-commands/sync.ts
|
|
14
|
+
function computeSyncPlan(outputDir, rendered) {
|
|
15
|
+
const added = [];
|
|
16
|
+
const updated = [];
|
|
17
|
+
const removed = [];
|
|
18
|
+
const unchanged = [];
|
|
19
|
+
for (const [filename, content] of rendered) {
|
|
20
|
+
const filePath = path.join(outputDir, filename);
|
|
21
|
+
if (!fs.existsSync(filePath)) {
|
|
22
|
+
added.push(filename);
|
|
23
|
+
} else {
|
|
24
|
+
const existing = fs.readFileSync(filePath, "utf-8");
|
|
25
|
+
if (existing === content) {
|
|
26
|
+
unchanged.push(filename);
|
|
27
|
+
} else {
|
|
28
|
+
updated.push(filename);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (fs.existsSync(outputDir)) {
|
|
33
|
+
const existing = fs.readdirSync(outputDir).filter((f) => {
|
|
34
|
+
const stat = fs.statSync(path.join(outputDir, f));
|
|
35
|
+
return stat.isFile();
|
|
36
|
+
});
|
|
37
|
+
for (const filename of existing) {
|
|
38
|
+
if (rendered.has(filename)) continue;
|
|
39
|
+
const content = fs.readFileSync(path.join(outputDir, filename), "utf-8");
|
|
40
|
+
if (content.includes(GENERATED_HEADER_CLAUDE) || content.includes(GENERATED_HEADER_GEMINI) || content.includes(GENERATED_HEADER_AGENT)) {
|
|
41
|
+
removed.push(filename);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return { added, updated, removed, unchanged };
|
|
46
|
+
}
|
|
47
|
+
function applySyncPlan(outputDir, rendered, plan, deleteOrphans) {
|
|
48
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
49
|
+
for (const filename of [...plan.added, ...plan.updated]) {
|
|
50
|
+
const content = rendered.get(filename);
|
|
51
|
+
if (content !== void 0) {
|
|
52
|
+
fs.writeFileSync(path.join(outputDir, filename), content);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (deleteOrphans) {
|
|
56
|
+
for (const filename of plan.removed) {
|
|
57
|
+
const filePath = path.join(outputDir, filename);
|
|
58
|
+
if (fs.existsSync(filePath)) {
|
|
59
|
+
fs.unlinkSync(filePath);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export {
|
|
66
|
+
VALID_PLATFORMS,
|
|
67
|
+
GENERATED_HEADER_CLAUDE,
|
|
68
|
+
GENERATED_HEADER_GEMINI,
|
|
69
|
+
GENERATED_HEADER_AGENT,
|
|
70
|
+
computeSyncPlan,
|
|
71
|
+
applySyncPlan
|
|
72
|
+
};
|