@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,135 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Err,
|
|
3
|
+
Ok
|
|
4
|
+
} from "./chunk-MHBMTPW7.js";
|
|
5
|
+
|
|
6
|
+
// src/persona/loader.ts
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
import YAML from "yaml";
|
|
10
|
+
|
|
11
|
+
// src/persona/schema.ts
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
var TriggerContextSchema = z.enum(["always", "on_pr", "on_commit", "on_review", "scheduled", "manual", "on_plan_approved"]).default("always");
|
|
14
|
+
var CommandStepSchema = z.object({
|
|
15
|
+
command: z.string(),
|
|
16
|
+
when: TriggerContextSchema
|
|
17
|
+
});
|
|
18
|
+
var SkillStepSchema = z.object({
|
|
19
|
+
skill: z.string(),
|
|
20
|
+
when: TriggerContextSchema,
|
|
21
|
+
output: z.enum(["inline", "artifact", "auto"]).default("auto")
|
|
22
|
+
});
|
|
23
|
+
var StepSchema = z.union([CommandStepSchema, SkillStepSchema]);
|
|
24
|
+
var PersonaTriggerSchema = z.discriminatedUnion("event", [
|
|
25
|
+
z.object({
|
|
26
|
+
event: z.literal("on_pr"),
|
|
27
|
+
conditions: z.object({
|
|
28
|
+
paths: z.array(z.string()).optional(),
|
|
29
|
+
min_files: z.number().optional()
|
|
30
|
+
}).optional()
|
|
31
|
+
}),
|
|
32
|
+
z.object({
|
|
33
|
+
event: z.literal("on_commit"),
|
|
34
|
+
conditions: z.object({ branches: z.array(z.string()).optional() }).optional()
|
|
35
|
+
}),
|
|
36
|
+
z.object({
|
|
37
|
+
event: z.literal("scheduled"),
|
|
38
|
+
cron: z.string()
|
|
39
|
+
}),
|
|
40
|
+
z.object({
|
|
41
|
+
event: z.literal("manual")
|
|
42
|
+
})
|
|
43
|
+
]);
|
|
44
|
+
var PersonaConfigSchema = z.object({
|
|
45
|
+
severity: z.enum(["error", "warning"]).default("error"),
|
|
46
|
+
autoFix: z.boolean().default(false),
|
|
47
|
+
timeout: z.number().default(3e5)
|
|
48
|
+
});
|
|
49
|
+
var PersonaOutputsSchema = z.object({
|
|
50
|
+
"agents-md": z.boolean().default(true),
|
|
51
|
+
"ci-workflow": z.boolean().default(true),
|
|
52
|
+
"runtime-config": z.boolean().default(true)
|
|
53
|
+
});
|
|
54
|
+
var PersonaSchemaV1 = z.object({
|
|
55
|
+
version: z.literal(1),
|
|
56
|
+
name: z.string(),
|
|
57
|
+
description: z.string(),
|
|
58
|
+
role: z.string(),
|
|
59
|
+
skills: z.array(z.string()),
|
|
60
|
+
commands: z.array(z.string()),
|
|
61
|
+
triggers: z.array(PersonaTriggerSchema),
|
|
62
|
+
config: PersonaConfigSchema.default({}),
|
|
63
|
+
outputs: PersonaOutputsSchema.default({})
|
|
64
|
+
});
|
|
65
|
+
var PersonaSchemaV2 = z.object({
|
|
66
|
+
version: z.literal(2),
|
|
67
|
+
name: z.string(),
|
|
68
|
+
description: z.string(),
|
|
69
|
+
role: z.string(),
|
|
70
|
+
skills: z.array(z.string()),
|
|
71
|
+
steps: z.array(StepSchema),
|
|
72
|
+
triggers: z.array(PersonaTriggerSchema),
|
|
73
|
+
config: PersonaConfigSchema.default({}),
|
|
74
|
+
outputs: PersonaOutputsSchema.default({})
|
|
75
|
+
});
|
|
76
|
+
var PersonaSchema = z.union([PersonaSchemaV1, PersonaSchemaV2]);
|
|
77
|
+
|
|
78
|
+
// src/persona/loader.ts
|
|
79
|
+
function normalizePersona(raw) {
|
|
80
|
+
if (raw.version === 1 && Array.isArray(raw.commands)) {
|
|
81
|
+
const { commands, ...rest } = raw;
|
|
82
|
+
return {
|
|
83
|
+
...rest,
|
|
84
|
+
steps: commands.map((cmd) => ({
|
|
85
|
+
command: cmd,
|
|
86
|
+
when: "always"
|
|
87
|
+
}))
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return raw;
|
|
91
|
+
}
|
|
92
|
+
function loadPersona(filePath) {
|
|
93
|
+
try {
|
|
94
|
+
if (!fs.existsSync(filePath)) {
|
|
95
|
+
return Err(new Error(`Persona file not found: ${filePath}`));
|
|
96
|
+
}
|
|
97
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
98
|
+
const parsed = YAML.parse(raw);
|
|
99
|
+
const result = PersonaSchema.safeParse(parsed);
|
|
100
|
+
if (!result.success) {
|
|
101
|
+
return Err(new Error(`Invalid persona ${filePath}: ${result.error.message}`));
|
|
102
|
+
}
|
|
103
|
+
return Ok(normalizePersona(result.data));
|
|
104
|
+
} catch (error) {
|
|
105
|
+
return Err(
|
|
106
|
+
new Error(`Failed to load persona: ${error instanceof Error ? error.message : String(error)}`)
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function listPersonas(dir) {
|
|
111
|
+
try {
|
|
112
|
+
if (!fs.existsSync(dir)) return Ok([]);
|
|
113
|
+
const entries = fs.readdirSync(dir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
114
|
+
const personas = [];
|
|
115
|
+
for (const entry of entries) {
|
|
116
|
+
const filePath = path.join(dir, entry);
|
|
117
|
+
const result = loadPersona(filePath);
|
|
118
|
+
if (result.ok) {
|
|
119
|
+
personas.push({ name: result.value.name, description: result.value.description, filePath });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return Ok(personas);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
return Err(
|
|
125
|
+
new Error(
|
|
126
|
+
`Failed to list personas: ${error instanceof Error ? error.message : String(error)}`
|
|
127
|
+
)
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export {
|
|
133
|
+
loadPersona,
|
|
134
|
+
listPersonas
|
|
135
|
+
};
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
// ../linter-gen/dist/generator/orchestrator.js
|
|
2
|
+
import * as fs3 from "fs/promises";
|
|
3
|
+
import * as path3 from "path";
|
|
4
|
+
|
|
5
|
+
// ../linter-gen/dist/parser/config-parser.js
|
|
6
|
+
import * as fs from "fs/promises";
|
|
7
|
+
import * as yaml from "yaml";
|
|
8
|
+
|
|
9
|
+
// ../linter-gen/dist/schema/linter-config.js
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
var RuleConfigSchema = z.object({
|
|
12
|
+
/** Rule name in kebab-case (e.g., 'no-ui-in-services') */
|
|
13
|
+
name: z.string().regex(/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/, "Rule name must be kebab-case"),
|
|
14
|
+
/** Rule type - determines which template to use */
|
|
15
|
+
type: z.string().min(1),
|
|
16
|
+
/** ESLint severity level */
|
|
17
|
+
severity: z.enum(["error", "warn", "off"]).default("error"),
|
|
18
|
+
/** Template-specific configuration */
|
|
19
|
+
config: z.record(z.unknown())
|
|
20
|
+
});
|
|
21
|
+
var LinterConfigSchema = z.object({
|
|
22
|
+
/** Config version - currently only 1 is supported */
|
|
23
|
+
version: z.literal(1),
|
|
24
|
+
/** Output directory for generated rules */
|
|
25
|
+
output: z.string().min(1),
|
|
26
|
+
/** Optional explicit template path mappings (type → path) */
|
|
27
|
+
templates: z.record(z.string()).optional(),
|
|
28
|
+
/** Rules to generate */
|
|
29
|
+
rules: z.array(RuleConfigSchema).min(1, "At least one rule is required")
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// ../linter-gen/dist/parser/config-parser.js
|
|
33
|
+
var ParseError = class extends Error {
|
|
34
|
+
code;
|
|
35
|
+
cause;
|
|
36
|
+
constructor(message, code, cause) {
|
|
37
|
+
super(message);
|
|
38
|
+
this.code = code;
|
|
39
|
+
this.cause = cause;
|
|
40
|
+
this.name = "ParseError";
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
async function parseConfig(configPath) {
|
|
44
|
+
let content;
|
|
45
|
+
try {
|
|
46
|
+
content = await fs.readFile(configPath, "utf-8");
|
|
47
|
+
} catch (err) {
|
|
48
|
+
if (err.code === "ENOENT") {
|
|
49
|
+
return {
|
|
50
|
+
success: false,
|
|
51
|
+
error: new ParseError(`Config file not found: ${configPath}`, "FILE_NOT_FOUND", err)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
success: false,
|
|
56
|
+
error: new ParseError(`Failed to read config file: ${configPath}`, "FILE_READ_ERROR", err)
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
let parsed;
|
|
60
|
+
try {
|
|
61
|
+
parsed = yaml.parse(content);
|
|
62
|
+
} catch (err) {
|
|
63
|
+
return {
|
|
64
|
+
success: false,
|
|
65
|
+
error: new ParseError(`Invalid YAML syntax in ${configPath}: ${err.message}`, "YAML_PARSE_ERROR", err)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const result = LinterConfigSchema.safeParse(parsed);
|
|
69
|
+
if (!result.success) {
|
|
70
|
+
const issues = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
71
|
+
return {
|
|
72
|
+
success: false,
|
|
73
|
+
error: new ParseError(`Invalid config: ${issues}`, "VALIDATION_ERROR", result.error)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
success: true,
|
|
78
|
+
data: result.data,
|
|
79
|
+
configPath
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ../linter-gen/dist/engine/template-loader.js
|
|
84
|
+
import * as fs2 from "fs/promises";
|
|
85
|
+
import * as path from "path";
|
|
86
|
+
import { fileURLToPath } from "url";
|
|
87
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
88
|
+
var __dirname = path.dirname(__filename);
|
|
89
|
+
var TemplateLoadError = class extends Error {
|
|
90
|
+
code;
|
|
91
|
+
cause;
|
|
92
|
+
constructor(message, code, cause) {
|
|
93
|
+
super(message);
|
|
94
|
+
this.code = code;
|
|
95
|
+
this.cause = cause;
|
|
96
|
+
this.name = "TemplateLoadError";
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
var BUILTIN_TEMPLATES = ["import-restriction", "boundary-validation", "dependency-graph"];
|
|
100
|
+
async function fileExists(filePath) {
|
|
101
|
+
try {
|
|
102
|
+
await fs2.access(filePath);
|
|
103
|
+
return true;
|
|
104
|
+
} catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async function loadTemplateFile(filePath, type) {
|
|
109
|
+
try {
|
|
110
|
+
const content = await fs2.readFile(filePath, "utf-8");
|
|
111
|
+
return {
|
|
112
|
+
success: true,
|
|
113
|
+
source: { type, path: filePath, content }
|
|
114
|
+
};
|
|
115
|
+
} catch (err) {
|
|
116
|
+
return {
|
|
117
|
+
success: false,
|
|
118
|
+
error: new TemplateLoadError(`Failed to read template: ${filePath}`, "TEMPLATE_READ_ERROR", err)
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async function loadTemplate(ruleType, templatesConfig, configDir) {
|
|
123
|
+
if (templatesConfig?.[ruleType]) {
|
|
124
|
+
const explicitPath = path.resolve(configDir, templatesConfig[ruleType]);
|
|
125
|
+
if (await fileExists(explicitPath)) {
|
|
126
|
+
return loadTemplateFile(explicitPath, "explicit");
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
success: false,
|
|
130
|
+
error: new TemplateLoadError(`Explicit template not found: ${explicitPath}`, "TEMPLATE_NOT_FOUND")
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const conventionPath = path.join(configDir, "templates", `${ruleType}.ts.hbs`);
|
|
134
|
+
if (await fileExists(conventionPath)) {
|
|
135
|
+
return loadTemplateFile(conventionPath, "convention");
|
|
136
|
+
}
|
|
137
|
+
if (BUILTIN_TEMPLATES.includes(ruleType)) {
|
|
138
|
+
const builtinPath = path.join(__dirname, "..", "templates", `${ruleType}.ts.hbs`);
|
|
139
|
+
if (await fileExists(builtinPath)) {
|
|
140
|
+
return loadTemplateFile(builtinPath, "builtin");
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
success: false,
|
|
145
|
+
error: new TemplateLoadError(`Template not found for type '${ruleType}'. Checked: explicit config, ./templates/${ruleType}.ts.hbs, built-in templates.`, "TEMPLATE_NOT_FOUND")
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ../linter-gen/dist/generator/rule-generator.js
|
|
150
|
+
import * as path2 from "path";
|
|
151
|
+
|
|
152
|
+
// ../linter-gen/dist/engine/context-builder.js
|
|
153
|
+
var GENERATOR_VERSION = "0.1.0";
|
|
154
|
+
function toCamelCase(str) {
|
|
155
|
+
return str.replace(/-([a-z0-9])/g, (_, char) => char.toUpperCase());
|
|
156
|
+
}
|
|
157
|
+
function toPascalCase(str) {
|
|
158
|
+
const camel = toCamelCase(str);
|
|
159
|
+
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
160
|
+
}
|
|
161
|
+
function buildRuleContext(rule, configPath) {
|
|
162
|
+
return {
|
|
163
|
+
name: rule.name,
|
|
164
|
+
nameCamel: toCamelCase(rule.name),
|
|
165
|
+
namePascal: toPascalCase(rule.name),
|
|
166
|
+
severity: rule.severity,
|
|
167
|
+
config: rule.config,
|
|
168
|
+
meta: {
|
|
169
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
170
|
+
generatorVersion: GENERATOR_VERSION,
|
|
171
|
+
configPath
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ../linter-gen/dist/engine/template-renderer.js
|
|
177
|
+
import Handlebars from "handlebars";
|
|
178
|
+
var TemplateError = class extends Error {
|
|
179
|
+
cause;
|
|
180
|
+
constructor(message, cause) {
|
|
181
|
+
super(message);
|
|
182
|
+
this.cause = cause;
|
|
183
|
+
this.name = "TemplateError";
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
function toCamelCase2(str) {
|
|
187
|
+
return str.replace(/-([a-z0-9])/g, (_, char) => char.toUpperCase());
|
|
188
|
+
}
|
|
189
|
+
function toPascalCase2(str) {
|
|
190
|
+
const camel = toCamelCase2(str);
|
|
191
|
+
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
192
|
+
}
|
|
193
|
+
Handlebars.registerHelper("json", (obj) => JSON.stringify(obj));
|
|
194
|
+
Handlebars.registerHelper("jsonPretty", (obj) => JSON.stringify(obj, null, 2));
|
|
195
|
+
Handlebars.registerHelper("camelCase", (str) => toCamelCase2(str));
|
|
196
|
+
Handlebars.registerHelper("pascalCase", (str) => toPascalCase2(str));
|
|
197
|
+
function renderTemplate(templateSource, context) {
|
|
198
|
+
try {
|
|
199
|
+
const compiled = Handlebars.compile(templateSource, { strict: true });
|
|
200
|
+
const output = compiled(context);
|
|
201
|
+
return { success: true, output };
|
|
202
|
+
} catch (err) {
|
|
203
|
+
return {
|
|
204
|
+
success: false,
|
|
205
|
+
error: new TemplateError(`Template rendering failed: ${err.message}`, err)
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ../linter-gen/dist/generator/rule-generator.js
|
|
211
|
+
function generateRule(rule, template, outputDir, configPath) {
|
|
212
|
+
const context = buildRuleContext(rule, configPath);
|
|
213
|
+
const renderResult = renderTemplate(template.content, context);
|
|
214
|
+
if (!renderResult.success) {
|
|
215
|
+
return {
|
|
216
|
+
success: false,
|
|
217
|
+
error: renderResult.error,
|
|
218
|
+
ruleName: rule.name
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
const outputPath = path2.join(outputDir, `${rule.name}.ts`);
|
|
222
|
+
return {
|
|
223
|
+
success: true,
|
|
224
|
+
rule: {
|
|
225
|
+
name: rule.name,
|
|
226
|
+
outputPath,
|
|
227
|
+
content: renderResult.output
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ../linter-gen/dist/generator/index-generator.js
|
|
233
|
+
function toCamelCase3(str) {
|
|
234
|
+
return str.replace(/-([a-z0-9])/g, (_, char) => char.toUpperCase());
|
|
235
|
+
}
|
|
236
|
+
function generateIndex(ruleNames) {
|
|
237
|
+
const imports = ruleNames.map((name) => {
|
|
238
|
+
const camel = toCamelCase3(name);
|
|
239
|
+
return `import ${camel} from './${name}';`;
|
|
240
|
+
}).join("\n");
|
|
241
|
+
const rulesObject = ruleNames.map((name) => {
|
|
242
|
+
const camel = toCamelCase3(name);
|
|
243
|
+
return ` '${name}': ${camel},`;
|
|
244
|
+
}).join("\n");
|
|
245
|
+
const namedExports = ruleNames.map(toCamelCase3).join(", ");
|
|
246
|
+
return `// Generated by @harness-engineering/linter-gen
|
|
247
|
+
// Do not edit manually - regenerate from harness-linter.yml
|
|
248
|
+
|
|
249
|
+
${imports}
|
|
250
|
+
|
|
251
|
+
export const rules = {
|
|
252
|
+
${rulesObject}
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
export { ${namedExports} };
|
|
256
|
+
`;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// ../linter-gen/dist/generator/orchestrator.js
|
|
260
|
+
async function validate(options) {
|
|
261
|
+
const parseResult = await parseConfig(options.configPath);
|
|
262
|
+
if (!parseResult.success) {
|
|
263
|
+
return { success: false, error: parseResult.error };
|
|
264
|
+
}
|
|
265
|
+
return { success: true, ruleCount: parseResult.data.rules.length };
|
|
266
|
+
}
|
|
267
|
+
async function generate(options) {
|
|
268
|
+
const errors = [];
|
|
269
|
+
const parseResult = await parseConfig(options.configPath);
|
|
270
|
+
if (!parseResult.success) {
|
|
271
|
+
return { success: false, errors: [{ type: "parse", error: parseResult.error }] };
|
|
272
|
+
}
|
|
273
|
+
const config = parseResult.data;
|
|
274
|
+
const configDir = path3.dirname(path3.resolve(options.configPath));
|
|
275
|
+
const outputDir = options.outputDir ? path3.resolve(options.outputDir) : path3.resolve(configDir, config.output);
|
|
276
|
+
if (options.clean && !options.dryRun) {
|
|
277
|
+
try {
|
|
278
|
+
await fs3.rm(outputDir, { recursive: true, force: true });
|
|
279
|
+
} catch {
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (!options.dryRun) {
|
|
283
|
+
await fs3.mkdir(outputDir, { recursive: true });
|
|
284
|
+
}
|
|
285
|
+
const generatedRules = [];
|
|
286
|
+
for (const rule of config.rules) {
|
|
287
|
+
const templateResult = await loadTemplate(rule.type, config.templates, configDir);
|
|
288
|
+
if (!templateResult.success) {
|
|
289
|
+
errors.push({
|
|
290
|
+
type: "template",
|
|
291
|
+
error: templateResult.error,
|
|
292
|
+
ruleName: rule.name
|
|
293
|
+
});
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
const ruleResult = generateRule(rule, templateResult.source, outputDir, options.configPath);
|
|
297
|
+
if (!ruleResult.success) {
|
|
298
|
+
errors.push({
|
|
299
|
+
type: "render",
|
|
300
|
+
error: ruleResult.error,
|
|
301
|
+
ruleName: ruleResult.ruleName
|
|
302
|
+
});
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
if (!options.dryRun) {
|
|
306
|
+
try {
|
|
307
|
+
await fs3.writeFile(ruleResult.rule.outputPath, ruleResult.rule.content, "utf-8");
|
|
308
|
+
} catch (err) {
|
|
309
|
+
errors.push({
|
|
310
|
+
type: "write",
|
|
311
|
+
error: err,
|
|
312
|
+
path: ruleResult.rule.outputPath
|
|
313
|
+
});
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
generatedRules.push(rule.name);
|
|
318
|
+
}
|
|
319
|
+
if (generatedRules.length > 0 && !options.dryRun) {
|
|
320
|
+
const indexContent = generateIndex(generatedRules);
|
|
321
|
+
const indexPath = path3.join(outputDir, "index.ts");
|
|
322
|
+
try {
|
|
323
|
+
await fs3.writeFile(indexPath, indexContent, "utf-8");
|
|
324
|
+
} catch (err) {
|
|
325
|
+
errors.push({ type: "write", error: err, path: indexPath });
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (errors.length > 0) {
|
|
329
|
+
return { success: false, errors };
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
success: true,
|
|
333
|
+
rulesGenerated: generatedRules,
|
|
334
|
+
outputDir,
|
|
335
|
+
dryRun: options.dryRun ?? false
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export {
|
|
340
|
+
RuleConfigSchema,
|
|
341
|
+
LinterConfigSchema,
|
|
342
|
+
ParseError,
|
|
343
|
+
TemplateLoadError,
|
|
344
|
+
TemplateError,
|
|
345
|
+
validate,
|
|
346
|
+
generate
|
|
347
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/persona/constants.ts
|
|
2
|
+
var ALLOWED_PERSONA_COMMANDS = /* @__PURE__ */ new Set([
|
|
3
|
+
"validate",
|
|
4
|
+
"check-deps",
|
|
5
|
+
"check-docs",
|
|
6
|
+
"check-perf",
|
|
7
|
+
"check-security",
|
|
8
|
+
"cleanup",
|
|
9
|
+
"fix-drift",
|
|
10
|
+
"add"
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
ALLOWED_PERSONA_COMMANDS
|
|
15
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveProjectConfig
|
|
3
|
+
} from "./chunk-K6XAPGML.js";
|
|
4
|
+
import {
|
|
5
|
+
resultToMcpResponse
|
|
6
|
+
} from "./chunk-IDZNPTYD.js";
|
|
7
|
+
import {
|
|
8
|
+
sanitizePath
|
|
9
|
+
} from "./chunk-W6Y7ZW3Y.js";
|
|
10
|
+
|
|
11
|
+
// src/mcp/tools/architecture.ts
|
|
12
|
+
var checkDependenciesDefinition = {
|
|
13
|
+
name: "check_dependencies",
|
|
14
|
+
description: "Validate layer boundaries and detect circular dependencies",
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: "object",
|
|
17
|
+
properties: {
|
|
18
|
+
path: { type: "string", description: "Path to project root" }
|
|
19
|
+
},
|
|
20
|
+
required: ["path"]
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
async function handleCheckDependencies(input) {
|
|
24
|
+
let projectPath;
|
|
25
|
+
try {
|
|
26
|
+
projectPath = sanitizePath(input.path);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
return {
|
|
29
|
+
content: [
|
|
30
|
+
{
|
|
31
|
+
type: "text",
|
|
32
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
isError: true
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const configResult = resolveProjectConfig(projectPath);
|
|
39
|
+
if (!configResult.ok) return resultToMcpResponse(configResult);
|
|
40
|
+
try {
|
|
41
|
+
const { validateDependencies, TypeScriptParser } = await import("./dist-JVZ2MKBC.js");
|
|
42
|
+
const config = configResult.value;
|
|
43
|
+
const rawLayers = Array.isArray(config.layers) ? config.layers : [];
|
|
44
|
+
const layers = rawLayers.map((l) => ({
|
|
45
|
+
name: l.name,
|
|
46
|
+
patterns: [l.pattern],
|
|
47
|
+
allowedDependencies: l.allowedDependencies
|
|
48
|
+
}));
|
|
49
|
+
const parser = new TypeScriptParser();
|
|
50
|
+
const { loadGraphStore } = await import("./graph-loader-KO4GJ5N2.js");
|
|
51
|
+
const store = await loadGraphStore(projectPath);
|
|
52
|
+
let graphDependencyData;
|
|
53
|
+
if (store) {
|
|
54
|
+
const { GraphConstraintAdapter } = await import("./dist-M6BQODWC.js");
|
|
55
|
+
const adapter = new GraphConstraintAdapter(store);
|
|
56
|
+
const graphData = adapter.computeDependencyGraph();
|
|
57
|
+
graphDependencyData = {
|
|
58
|
+
nodes: [...graphData.nodes],
|
|
59
|
+
edges: graphData.edges.map((e) => ({ ...e }))
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const result = await validateDependencies({
|
|
63
|
+
layers,
|
|
64
|
+
rootDir: projectPath,
|
|
65
|
+
parser,
|
|
66
|
+
...graphDependencyData !== void 0 && { graphDependencyData }
|
|
67
|
+
});
|
|
68
|
+
return resultToMcpResponse(result);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
return {
|
|
71
|
+
content: [
|
|
72
|
+
{
|
|
73
|
+
type: "text",
|
|
74
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
isError: true
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export {
|
|
83
|
+
checkDependenciesDefinition,
|
|
84
|
+
handleCheckDependencies
|
|
85
|
+
};
|