@harness-engineering/cli 1.9.0 → 1.10.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/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/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-md-EMRFLNBC.js +8 -0
- package/dist/architecture-5JNN5L3M.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-WOKIYGAM.js +12 -0
- package/dist/chunk-46YA6FI3.js +293 -0
- package/dist/chunk-4PFMY3H7.js +248 -0
- package/dist/{chunk-6JIT7CEM.js → chunk-72GHBOL2.js} +1 -1
- package/dist/chunk-7X7ZAYMY.js +373 -0
- package/dist/chunk-B7HFEHWP.js +35 -0
- package/dist/chunk-BM3PWGXQ.js +14 -0
- package/dist/chunk-C2ERUR3L.js +255 -0
- package/dist/chunk-CWZ4Y2PO.js +189 -0
- package/dist/{chunk-ULSRSP53.js → chunk-ECUJQS3B.js} +11 -112
- package/dist/chunk-EOLRW32Q.js +72 -0
- package/dist/chunk-F3YDAJFQ.js +125 -0
- package/dist/chunk-F4PTVZWA.js +116 -0
- package/dist/chunk-FPIPT36X.js +187 -0
- package/dist/chunk-FX7SQHGD.js +103 -0
- package/dist/chunk-HIOXKZYF.js +15 -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-LXU5M77O.js +4028 -0
- package/dist/chunk-MDUK2J2O.js +67 -0
- package/dist/chunk-MHBMTPW7.js +29 -0
- package/dist/chunk-MO4YQOMB.js +85 -0
- package/dist/chunk-NKDM3FMH.js +52 -0
- package/dist/{chunk-CGSHUJES.js → chunk-NX6DSZSM.js} +7 -26
- package/dist/chunk-OPXH4CQN.js +62 -0
- package/dist/{chunk-RTPHUDZS.js → chunk-PAHHT2IK.js} +466 -2714
- package/dist/chunk-PMTFPOCT.js +122 -0
- package/dist/chunk-PSXF277V.js +89 -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-TRAPF4IX.js +185 -0
- package/dist/chunk-VUCPTQ6G.js +67 -0
- package/dist/chunk-W6Y7ZW3Y.js +13 -0
- package/dist/chunk-ZOAWBDWU.js +72 -0
- package/dist/ci-workflow-ZBBUNTHQ.js +8 -0
- package/dist/constants-5JGUXPEK.js +6 -0
- package/dist/create-skill-LUWO46WF.js +11 -0
- package/dist/dist-D4RYGUZE.js +14 -0
- package/dist/dist-L7LAAQAS.js +18 -0
- package/dist/{dist-C5PYIQPF.js → dist-PBTNVK6K.js} +8 -6
- package/dist/docs-PTJGD6XI.js +12 -0
- package/dist/engine-SCMZ3G3E.js +8 -0
- package/dist/entropy-YIUBGKY7.js +12 -0
- package/dist/feedback-WEVQSLAA.js +18 -0
- package/dist/generate-agent-definitions-BU5LOJTI.js +15 -0
- package/dist/glob-helper-5OHBUQAI.js +52 -0
- package/dist/graph-loader-RLO3KRIX.js +8 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.js +84 -33
- package/dist/loader-6S6PVGSF.js +10 -0
- package/dist/mcp-BNLBTCXZ.js +34 -0
- package/dist/performance-5TVW6SA6.js +24 -0
- package/dist/review-pipeline-4JTQAWKW.js +9 -0
- package/dist/runner-VMYLHWOC.js +6 -0
- package/dist/runtime-PXIM7UV6.js +9 -0
- package/dist/security-URYTKLGK.js +9 -0
- package/dist/skill-executor-KVS47DAU.js +8 -0
- package/dist/validate-KSDUUK2M.js +12 -0
- package/dist/validate-cross-check-WZAX357V.js +8 -0
- package/dist/version-KFFPOQAX.js +6 -0
- package/package.json +6 -4
- package/dist/create-skill-UZOHMXRU.js +0 -8
- package/dist/validate-cross-check-VG573VZO.js +0 -7
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Err,
|
|
3
|
+
Ok
|
|
4
|
+
} from "./chunk-MHBMTPW7.js";
|
|
5
|
+
|
|
6
|
+
// src/templates/engine.ts
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
import Handlebars from "handlebars";
|
|
10
|
+
|
|
11
|
+
// src/templates/schema.ts
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
var MergeStrategySchema = z.object({
|
|
14
|
+
json: z.enum(["deep-merge", "overlay-wins"]).default("deep-merge"),
|
|
15
|
+
files: z.literal("overlay-wins").default("overlay-wins")
|
|
16
|
+
});
|
|
17
|
+
var TemplateMetadataSchema = z.object({
|
|
18
|
+
name: z.string(),
|
|
19
|
+
description: z.string(),
|
|
20
|
+
level: z.enum(["basic", "intermediate", "advanced"]).optional(),
|
|
21
|
+
framework: z.string().optional(),
|
|
22
|
+
extends: z.string().optional(),
|
|
23
|
+
mergeStrategy: MergeStrategySchema.default({}),
|
|
24
|
+
version: z.literal(1)
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// src/templates/merger.ts
|
|
28
|
+
function isPlainObject(val) {
|
|
29
|
+
return typeof val === "object" && val !== null && !Array.isArray(val);
|
|
30
|
+
}
|
|
31
|
+
function deepMergeJson(base, overlay) {
|
|
32
|
+
const result = { ...base };
|
|
33
|
+
for (const key of Object.keys(overlay)) {
|
|
34
|
+
if (isPlainObject(result[key]) && isPlainObject(overlay[key])) {
|
|
35
|
+
result[key] = deepMergeJson(
|
|
36
|
+
result[key],
|
|
37
|
+
overlay[key]
|
|
38
|
+
);
|
|
39
|
+
} else {
|
|
40
|
+
result[key] = overlay[key];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
var CONCAT_KEYS = /* @__PURE__ */ new Set(["dependencies", "devDependencies", "peerDependencies"]);
|
|
46
|
+
function mergePackageJson(base, overlay) {
|
|
47
|
+
const result = { ...base };
|
|
48
|
+
for (const key of Object.keys(overlay)) {
|
|
49
|
+
if (CONCAT_KEYS.has(key) && isPlainObject(result[key]) && isPlainObject(overlay[key])) {
|
|
50
|
+
result[key] = {
|
|
51
|
+
...result[key],
|
|
52
|
+
...overlay[key]
|
|
53
|
+
};
|
|
54
|
+
} else if (isPlainObject(result[key]) && isPlainObject(overlay[key])) {
|
|
55
|
+
result[key] = deepMergeJson(
|
|
56
|
+
result[key],
|
|
57
|
+
overlay[key]
|
|
58
|
+
);
|
|
59
|
+
} else {
|
|
60
|
+
result[key] = overlay[key];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/templates/engine.ts
|
|
67
|
+
var TemplateEngine = class {
|
|
68
|
+
constructor(templatesDir) {
|
|
69
|
+
this.templatesDir = templatesDir;
|
|
70
|
+
}
|
|
71
|
+
listTemplates() {
|
|
72
|
+
try {
|
|
73
|
+
const entries = fs.readdirSync(this.templatesDir, { withFileTypes: true });
|
|
74
|
+
const templates = [];
|
|
75
|
+
for (const entry of entries) {
|
|
76
|
+
if (!entry.isDirectory()) continue;
|
|
77
|
+
const metaPath = path.join(this.templatesDir, entry.name, "template.json");
|
|
78
|
+
if (!fs.existsSync(metaPath)) continue;
|
|
79
|
+
const raw = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
|
|
80
|
+
const parsed = TemplateMetadataSchema.safeParse(raw);
|
|
81
|
+
if (parsed.success) templates.push(parsed.data);
|
|
82
|
+
}
|
|
83
|
+
return Ok(templates);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
return Err(
|
|
86
|
+
new Error(
|
|
87
|
+
`Failed to list templates: ${error instanceof Error ? error.message : String(error)}`
|
|
88
|
+
)
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
resolveTemplate(level, framework) {
|
|
93
|
+
const levelDir = this.findTemplateDir(level, "level");
|
|
94
|
+
if (!levelDir) return Err(new Error(`Template not found for level: ${level}`));
|
|
95
|
+
const metaPath = path.join(levelDir, "template.json");
|
|
96
|
+
const metaRaw = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
|
|
97
|
+
const metaResult = TemplateMetadataSchema.safeParse(metaRaw);
|
|
98
|
+
if (!metaResult.success)
|
|
99
|
+
return Err(new Error(`Invalid template.json in ${level}: ${metaResult.error.message}`));
|
|
100
|
+
const metadata = metaResult.data;
|
|
101
|
+
let files = [];
|
|
102
|
+
if (metadata.extends) {
|
|
103
|
+
const baseDir = path.join(this.templatesDir, metadata.extends);
|
|
104
|
+
if (fs.existsSync(baseDir)) files = this.collectFiles(baseDir, metadata.extends);
|
|
105
|
+
}
|
|
106
|
+
const levelFiles = this.collectFiles(levelDir, level);
|
|
107
|
+
files = this.mergeFileLists(files, levelFiles);
|
|
108
|
+
let overlayMetadata;
|
|
109
|
+
if (framework) {
|
|
110
|
+
const frameworkDir = this.findTemplateDir(framework, "framework");
|
|
111
|
+
if (!frameworkDir) return Err(new Error(`Framework template not found: ${framework}`));
|
|
112
|
+
const fMetaPath = path.join(frameworkDir, "template.json");
|
|
113
|
+
const fMetaRaw = JSON.parse(fs.readFileSync(fMetaPath, "utf-8"));
|
|
114
|
+
const fMetaResult = TemplateMetadataSchema.safeParse(fMetaRaw);
|
|
115
|
+
if (fMetaResult.success) overlayMetadata = fMetaResult.data;
|
|
116
|
+
const frameworkFiles = this.collectFiles(frameworkDir, framework);
|
|
117
|
+
files = this.mergeFileLists(files, frameworkFiles);
|
|
118
|
+
}
|
|
119
|
+
files = files.filter((f) => f.relativePath !== "template.json");
|
|
120
|
+
const resolved = { metadata, files };
|
|
121
|
+
if (overlayMetadata !== void 0) resolved.overlayMetadata = overlayMetadata;
|
|
122
|
+
return Ok(resolved);
|
|
123
|
+
}
|
|
124
|
+
render(template, context) {
|
|
125
|
+
const rendered = [];
|
|
126
|
+
const jsonBuffers = /* @__PURE__ */ new Map();
|
|
127
|
+
for (const file of template.files) {
|
|
128
|
+
const outputPath = file.relativePath.replace(/\.hbs$/, "");
|
|
129
|
+
if (file.isHandlebars) {
|
|
130
|
+
try {
|
|
131
|
+
const raw = fs.readFileSync(file.absolutePath, "utf-8");
|
|
132
|
+
const compiled = Handlebars.compile(raw, { strict: true });
|
|
133
|
+
const content = compiled(context);
|
|
134
|
+
if (outputPath.endsWith(".json") && file.relativePath.endsWith(".json.hbs")) {
|
|
135
|
+
if (!jsonBuffers.has(outputPath)) jsonBuffers.set(outputPath, []);
|
|
136
|
+
jsonBuffers.get(outputPath).push(JSON.parse(content));
|
|
137
|
+
} else {
|
|
138
|
+
rendered.push({ relativePath: outputPath, content });
|
|
139
|
+
}
|
|
140
|
+
} catch (error) {
|
|
141
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
142
|
+
return Err(
|
|
143
|
+
new Error(
|
|
144
|
+
`Template render failed in ${file.sourceTemplate}/${file.relativePath}: ${msg}`
|
|
145
|
+
)
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
try {
|
|
150
|
+
const content = fs.readFileSync(file.absolutePath, "utf-8");
|
|
151
|
+
rendered.push({ relativePath: file.relativePath, content });
|
|
152
|
+
} catch (error) {
|
|
153
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
154
|
+
return Err(
|
|
155
|
+
new Error(
|
|
156
|
+
`Template render failed in ${file.sourceTemplate}/${file.relativePath}: ${msg}`
|
|
157
|
+
)
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
for (const [outputPath, jsons] of jsonBuffers) {
|
|
164
|
+
let merged = {};
|
|
165
|
+
for (const json of jsons) {
|
|
166
|
+
merged = outputPath === "package.json" ? mergePackageJson(merged, json) : deepMergeJson(merged, json);
|
|
167
|
+
}
|
|
168
|
+
rendered.push({ relativePath: outputPath, content: JSON.stringify(merged, null, 2) });
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
172
|
+
return Err(new Error(`JSON merge failed: ${msg}`));
|
|
173
|
+
}
|
|
174
|
+
return Ok({ files: rendered });
|
|
175
|
+
}
|
|
176
|
+
write(files, targetDir, options) {
|
|
177
|
+
try {
|
|
178
|
+
const written = [];
|
|
179
|
+
for (const file of files.files) {
|
|
180
|
+
const targetPath = path.join(targetDir, file.relativePath);
|
|
181
|
+
const dir = path.dirname(targetPath);
|
|
182
|
+
if (!options.overwrite && fs.existsSync(targetPath)) continue;
|
|
183
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
184
|
+
fs.writeFileSync(targetPath, file.content);
|
|
185
|
+
written.push(file.relativePath);
|
|
186
|
+
}
|
|
187
|
+
return Ok(written);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
return Err(
|
|
190
|
+
new Error(
|
|
191
|
+
`Failed to write files: ${error instanceof Error ? error.message : String(error)}`
|
|
192
|
+
)
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
findTemplateDir(name, type) {
|
|
197
|
+
const entries = fs.readdirSync(this.templatesDir, { withFileTypes: true });
|
|
198
|
+
for (const entry of entries) {
|
|
199
|
+
if (!entry.isDirectory()) continue;
|
|
200
|
+
const metaPath = path.join(this.templatesDir, entry.name, "template.json");
|
|
201
|
+
if (!fs.existsSync(metaPath)) continue;
|
|
202
|
+
const raw = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
|
|
203
|
+
const parsed = TemplateMetadataSchema.safeParse(raw);
|
|
204
|
+
if (!parsed.success) continue;
|
|
205
|
+
if (type === "level" && parsed.data.level === name)
|
|
206
|
+
return path.join(this.templatesDir, entry.name);
|
|
207
|
+
if (type === "framework" && parsed.data.framework === name)
|
|
208
|
+
return path.join(this.templatesDir, entry.name);
|
|
209
|
+
if (parsed.data.name === name) return path.join(this.templatesDir, entry.name);
|
|
210
|
+
}
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
collectFiles(dir, sourceName) {
|
|
214
|
+
const files = [];
|
|
215
|
+
const walk = (currentDir) => {
|
|
216
|
+
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
217
|
+
for (const entry of entries) {
|
|
218
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
219
|
+
if (entry.isDirectory()) {
|
|
220
|
+
walk(fullPath);
|
|
221
|
+
} else {
|
|
222
|
+
files.push({
|
|
223
|
+
relativePath: path.relative(dir, fullPath).replace(/\\/g, "/"),
|
|
224
|
+
absolutePath: fullPath,
|
|
225
|
+
isHandlebars: entry.name.endsWith(".hbs"),
|
|
226
|
+
sourceTemplate: sourceName
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
walk(dir);
|
|
232
|
+
return files;
|
|
233
|
+
}
|
|
234
|
+
mergeFileLists(base, overlay) {
|
|
235
|
+
const map = /* @__PURE__ */ new Map();
|
|
236
|
+
for (const file of base) map.set(file.relativePath, file);
|
|
237
|
+
for (const file of overlay) {
|
|
238
|
+
if (file.relativePath.endsWith(".json.hbs")) {
|
|
239
|
+
const baseKey = base.find((f) => f.relativePath === file.relativePath);
|
|
240
|
+
if (baseKey) {
|
|
241
|
+
map.set(`__overlay__${file.relativePath}`, file);
|
|
242
|
+
} else {
|
|
243
|
+
map.set(file.relativePath, file);
|
|
244
|
+
}
|
|
245
|
+
} else {
|
|
246
|
+
map.set(file.relativePath, file);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return Array.from(map.values());
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
export {
|
|
254
|
+
TemplateEngine
|
|
255
|
+
};
|
|
@@ -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-RLO3KRIX.js");
|
|
14
|
+
const store = await loadGraphStore(projectPath);
|
|
15
|
+
if (!store) return void 0;
|
|
16
|
+
const { GraphEntropyAdapter } = await import("./dist-I7DB5VKB.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-PBTNVK6K.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-PBTNVK6K.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
|
+
};
|
|
@@ -1,115 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-HIOXKZYF.js";
|
|
4
|
+
import {
|
|
5
|
+
CLIError,
|
|
6
|
+
ExitCode
|
|
7
|
+
} from "./chunk-B7HFEHWP.js";
|
|
8
|
+
import {
|
|
9
|
+
ALLOWED_COGNITIVE_MODES
|
|
10
|
+
} from "./chunk-MDUK2J2O.js";
|
|
11
|
+
|
|
1
12
|
// src/commands/create-skill.ts
|
|
2
13
|
import { Command } from "commander";
|
|
3
14
|
import * as path from "path";
|
|
4
15
|
import * as fs from "fs";
|
|
5
16
|
import YAML from "yaml";
|
|
6
|
-
|
|
7
|
-
// src/skill/schema.ts
|
|
8
|
-
import { z } from "zod";
|
|
9
|
-
var SkillPhaseSchema = z.object({
|
|
10
|
-
name: z.string(),
|
|
11
|
-
description: z.string(),
|
|
12
|
-
required: z.boolean().default(true)
|
|
13
|
-
});
|
|
14
|
-
var SkillCliSchema = z.object({
|
|
15
|
-
command: z.string(),
|
|
16
|
-
args: z.array(
|
|
17
|
-
z.object({
|
|
18
|
-
name: z.string(),
|
|
19
|
-
description: z.string(),
|
|
20
|
-
required: z.boolean().default(false)
|
|
21
|
-
})
|
|
22
|
-
).default([])
|
|
23
|
-
});
|
|
24
|
-
var SkillMcpSchema = z.object({
|
|
25
|
-
tool: z.string(),
|
|
26
|
-
input: z.record(z.string())
|
|
27
|
-
});
|
|
28
|
-
var SkillStateSchema = z.object({
|
|
29
|
-
persistent: z.boolean().default(false),
|
|
30
|
-
files: z.array(z.string()).default([])
|
|
31
|
-
});
|
|
32
|
-
var ALLOWED_TRIGGERS = [
|
|
33
|
-
"manual",
|
|
34
|
-
"on_pr",
|
|
35
|
-
"on_commit",
|
|
36
|
-
"on_new_feature",
|
|
37
|
-
"on_bug_fix",
|
|
38
|
-
"on_refactor",
|
|
39
|
-
"on_project_init",
|
|
40
|
-
"on_review",
|
|
41
|
-
"on_milestone",
|
|
42
|
-
"on_task_complete",
|
|
43
|
-
"on_doc_check"
|
|
44
|
-
];
|
|
45
|
-
var ALLOWED_PLATFORMS = ["claude-code", "gemini-cli"];
|
|
46
|
-
var ALLOWED_COGNITIVE_MODES = [
|
|
47
|
-
"adversarial-reviewer",
|
|
48
|
-
"constructive-architect",
|
|
49
|
-
"meticulous-implementer",
|
|
50
|
-
"diagnostic-investigator",
|
|
51
|
-
"advisory-guide",
|
|
52
|
-
"meticulous-verifier"
|
|
53
|
-
];
|
|
54
|
-
var SkillMetadataSchema = z.object({
|
|
55
|
-
name: z.string().regex(/^[a-z][a-z0-9-]*$/, "Name must be lowercase with hyphens"),
|
|
56
|
-
version: z.string().regex(/^\d+\.\d+\.\d+$/, "Version must be semver format"),
|
|
57
|
-
description: z.string(),
|
|
58
|
-
cognitive_mode: z.string().regex(/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/, "Cognitive mode must be kebab-case").optional(),
|
|
59
|
-
triggers: z.array(z.enum(ALLOWED_TRIGGERS)),
|
|
60
|
-
platforms: z.array(z.enum(ALLOWED_PLATFORMS)),
|
|
61
|
-
tools: z.array(z.string()),
|
|
62
|
-
cli: SkillCliSchema.optional(),
|
|
63
|
-
mcp: SkillMcpSchema.optional(),
|
|
64
|
-
type: z.enum(["rigid", "flexible"]),
|
|
65
|
-
phases: z.array(SkillPhaseSchema).optional(),
|
|
66
|
-
state: SkillStateSchema.default({}),
|
|
67
|
-
depends_on: z.array(z.string()).default([])
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
// src/output/logger.ts
|
|
71
|
-
import chalk from "chalk";
|
|
72
|
-
var logger = {
|
|
73
|
-
info: (message) => console.log(chalk.blue("i"), message),
|
|
74
|
-
success: (message) => console.log(chalk.green("v"), message),
|
|
75
|
-
warn: (message) => console.log(chalk.yellow("!"), message),
|
|
76
|
-
error: (message) => console.error(chalk.red("x"), message),
|
|
77
|
-
dim: (message) => console.log(chalk.dim(message)),
|
|
78
|
-
// For JSON output mode
|
|
79
|
-
raw: (data) => console.log(JSON.stringify(data, null, 2))
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
// src/utils/errors.ts
|
|
83
|
-
var ExitCode = {
|
|
84
|
-
SUCCESS: 0,
|
|
85
|
-
VALIDATION_FAILED: 1,
|
|
86
|
-
ERROR: 2
|
|
87
|
-
};
|
|
88
|
-
var CLIError = class extends Error {
|
|
89
|
-
exitCode;
|
|
90
|
-
constructor(message, exitCode = ExitCode.ERROR) {
|
|
91
|
-
super(message);
|
|
92
|
-
this.name = "CLIError";
|
|
93
|
-
this.exitCode = exitCode;
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
function formatError(error) {
|
|
97
|
-
if (error instanceof CLIError) {
|
|
98
|
-
return `Error: ${error.message}`;
|
|
99
|
-
}
|
|
100
|
-
if (error instanceof Error) {
|
|
101
|
-
return `Error: ${error.message}`;
|
|
102
|
-
}
|
|
103
|
-
return `Error: ${String(error)}`;
|
|
104
|
-
}
|
|
105
|
-
function handleError(error) {
|
|
106
|
-
const message = formatError(error);
|
|
107
|
-
console.error(message);
|
|
108
|
-
const exitCode = error instanceof CLIError ? error.exitCode : ExitCode.ERROR;
|
|
109
|
-
process.exit(exitCode);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// src/commands/create-skill.ts
|
|
113
17
|
var KEBAB_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
114
18
|
function buildSkillYaml(opts) {
|
|
115
19
|
const doc = {
|
|
@@ -258,11 +162,6 @@ function createCreateSkillCommand() {
|
|
|
258
162
|
}
|
|
259
163
|
|
|
260
164
|
export {
|
|
261
|
-
ExitCode,
|
|
262
|
-
CLIError,
|
|
263
|
-
handleError,
|
|
264
|
-
logger,
|
|
265
|
-
SkillMetadataSchema,
|
|
266
165
|
generateSkillFiles,
|
|
267
166
|
createCreateSkillCommand
|
|
268
167
|
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// src/utils/paths.ts
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
var __dirname = path.dirname(__filename);
|
|
7
|
+
function findUpFrom(startDir, targetName, marker, maxLevels) {
|
|
8
|
+
let dir = startDir;
|
|
9
|
+
for (let i = 0; i < maxLevels; i++) {
|
|
10
|
+
const candidate = path.join(dir, targetName);
|
|
11
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
|
|
12
|
+
if (fs.existsSync(path.join(candidate, marker))) {
|
|
13
|
+
return candidate;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
dir = path.dirname(dir);
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
function findUpDir(targetName, marker, maxLevels = 8) {
|
|
21
|
+
const fromModule = findUpFrom(__dirname, targetName, marker, maxLevels);
|
|
22
|
+
if (fromModule) return fromModule;
|
|
23
|
+
return findUpFrom(process.cwd(), targetName, marker, maxLevels);
|
|
24
|
+
}
|
|
25
|
+
function resolveTemplatesDir() {
|
|
26
|
+
return findUpDir("templates", "base") ?? path.join(__dirname, "templates");
|
|
27
|
+
}
|
|
28
|
+
function resolvePersonasDir() {
|
|
29
|
+
const agentsDir = findUpDir("agents", "personas");
|
|
30
|
+
if (agentsDir) {
|
|
31
|
+
return path.join(agentsDir, "personas");
|
|
32
|
+
}
|
|
33
|
+
return path.join(__dirname, "agents", "personas");
|
|
34
|
+
}
|
|
35
|
+
function resolveSkillsDir() {
|
|
36
|
+
const agentsDir = findUpDir("agents", "skills");
|
|
37
|
+
if (agentsDir) {
|
|
38
|
+
return path.join(agentsDir, "skills", "claude-code");
|
|
39
|
+
}
|
|
40
|
+
return path.join(__dirname, "agents", "skills", "claude-code");
|
|
41
|
+
}
|
|
42
|
+
function resolveProjectSkillsDir(cwd) {
|
|
43
|
+
let dir = cwd ?? process.cwd();
|
|
44
|
+
for (let i = 0; i < 8; i++) {
|
|
45
|
+
const candidate = path.join(dir, "agents", "skills", "claude-code");
|
|
46
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
|
|
47
|
+
const agentsDir = path.join(dir, "agents");
|
|
48
|
+
if (fs.existsSync(path.join(agentsDir, "skills"))) {
|
|
49
|
+
return candidate;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const parent = path.dirname(dir);
|
|
53
|
+
if (parent === dir) break;
|
|
54
|
+
dir = parent;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
function resolveGlobalSkillsDir() {
|
|
59
|
+
const agentsDir = findUpDir("agents", "skills");
|
|
60
|
+
if (agentsDir) {
|
|
61
|
+
return path.join(agentsDir, "skills", "claude-code");
|
|
62
|
+
}
|
|
63
|
+
return path.join(__dirname, "agents", "skills", "claude-code");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export {
|
|
67
|
+
resolveTemplatesDir,
|
|
68
|
+
resolvePersonasDir,
|
|
69
|
+
resolveSkillsDir,
|
|
70
|
+
resolveProjectSkillsDir,
|
|
71
|
+
resolveGlobalSkillsDir
|
|
72
|
+
};
|