@vaubaan/harness 0.0.0-stage → 1.3.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/LICENSE +202 -0
- package/NOTICE +5 -0
- package/README.md +190 -2
- package/dist/agents/coding.agent.md +183 -0
- package/dist/agents/merge.agent.md +139 -0
- package/dist/agents/planning.agent.md +194 -0
- package/dist/agents/review.agent.md +176 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +2896 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/commands/clean-code.cmd.md +26 -0
- package/dist/commands/decision.cmd.md +19 -0
- package/dist/commands/doc-audit.cmd.md +24 -0
- package/dist/commands/drift.cmd.md +18 -0
- package/dist/commands/handbook.cmd.md +21 -0
- package/dist/commands/readme.cmd.md +19 -0
- package/dist/commands/spec.cmd.md +20 -0
- package/dist/commands/test-plan.cmd.md +22 -0
- package/dist/commands/workflow.cmd.md +19 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +1673 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +42 -0
- package/dist/init.js +1055 -0
- package/dist/init.js.map +1 -0
- package/dist/linters/index.d.ts +43 -0
- package/dist/linters/index.js +726 -0
- package/dist/linters/index.js.map +1 -0
- package/dist/parser/index.d.ts +247 -0
- package/dist/parser/index.js +114 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/render-B9_kNtQc.d.ts +50 -0
- package/dist/render.d.ts +3 -0
- package/dist/render.js +539 -0
- package/dist/render.js.map +1 -0
- package/dist/skills/clean-code-guardian.skill.md +391 -0
- package/dist/skills/decision-record-writer.skill.md +166 -0
- package/dist/skills/doc-audit.skill.md +257 -0
- package/dist/skills/drift-detector.skill.md +173 -0
- package/dist/skills/handbook-writer.skill.md +185 -0
- package/dist/skills/readme-writer.skill.md +131 -0
- package/dist/skills/spec-writer.skill.md +165 -0
- package/dist/skills/test-plan-writer.skill.md +179 -0
- package/dist/skills/workflow-writer.skill.md +168 -0
- package/dist/templates/code-quality.md.tmpl +57 -0
- package/dist/templates/coding-workflow.md.tmpl +88 -0
- package/dist/templates/decision.md.tmpl +60 -0
- package/dist/templates/e2e-tests.md.tmpl +62 -0
- package/dist/templates/handbook.md.tmpl +58 -0
- package/dist/templates/integration-tests.md.tmpl +50 -0
- package/dist/templates/readme.md.tmpl +51 -0
- package/dist/templates/spec.md.tmpl +64 -0
- package/package.json +77 -4
package/dist/render.js
ADDED
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
// src/mcp.ts
|
|
2
|
+
var MCP_SERVERS = {
|
|
3
|
+
context7: {
|
|
4
|
+
id: "context7",
|
|
5
|
+
description: "Upstash Context7 \u2014 fetches up-to-date library docs via resolve-library-id + get-library-docs.",
|
|
6
|
+
command: "npx",
|
|
7
|
+
args: ["-y", "@upstash/context7-mcp"]
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/renderers/common.ts
|
|
12
|
+
import { stringify as yamlStringify } from "yaml";
|
|
13
|
+
|
|
14
|
+
// src/types.ts
|
|
15
|
+
var CAPABILITIES_MATRIX = {
|
|
16
|
+
"claude-code": ["multi-step", "slash-command", "inline-exec"],
|
|
17
|
+
"codex-cli": ["multi-step", "slash-command", "inline-exec"],
|
|
18
|
+
opencode: ["multi-step", "slash-command", "inline-exec"]
|
|
19
|
+
};
|
|
20
|
+
var DEGRADED_CAPABILITIES = {
|
|
21
|
+
"codex-cli": ["multi-step"]
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// src/renderers/common.ts
|
|
25
|
+
var STOP_HOOK_PACKAGE = "@vaubaan/harness";
|
|
26
|
+
function checkSkillCapabilities(skill, engine) {
|
|
27
|
+
const blockingWarnings = [];
|
|
28
|
+
const degradedWarnings = [];
|
|
29
|
+
const supported = CAPABILITIES_MATRIX[engine];
|
|
30
|
+
const degraded = DEGRADED_CAPABILITIES[engine] ?? [];
|
|
31
|
+
if (!skill.meta.requires) return { blockingWarnings, degradedWarnings };
|
|
32
|
+
for (const cap of skill.meta.requires) {
|
|
33
|
+
if (!supported.includes(cap)) {
|
|
34
|
+
blockingWarnings.push(
|
|
35
|
+
`skill "${skill.meta.id}" requires "${cap}" not supported by "${engine}" \u2014 skipped`
|
|
36
|
+
);
|
|
37
|
+
} else if (degraded.includes(cap)) {
|
|
38
|
+
degradedWarnings.push(
|
|
39
|
+
`skill "${skill.meta.id}" requires "${cap}" \u2014 supported by "${engine}" with degradation`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return { blockingWarnings, degradedWarnings };
|
|
44
|
+
}
|
|
45
|
+
function isSkillEngineAllowed(skill, engine) {
|
|
46
|
+
if (!skill.meta.engines) return true;
|
|
47
|
+
return skill.meta.engines.includes(engine);
|
|
48
|
+
}
|
|
49
|
+
function yamlFrontmatter(data) {
|
|
50
|
+
const rendered = yamlStringify(data, { lineWidth: 0 }).trimEnd();
|
|
51
|
+
return `---
|
|
52
|
+
${rendered}
|
|
53
|
+
---
|
|
54
|
+
`;
|
|
55
|
+
}
|
|
56
|
+
var SCALAR_NEEDS_QUOTE = /[^a-zA-Z0-9_./-]/;
|
|
57
|
+
function tomlEscapeString(s) {
|
|
58
|
+
return `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`;
|
|
59
|
+
}
|
|
60
|
+
function tomlValue(v) {
|
|
61
|
+
if (Array.isArray(v)) {
|
|
62
|
+
return `[${v.map((x) => tomlValue(x)).join(", ")}]`;
|
|
63
|
+
}
|
|
64
|
+
if (typeof v === "string") {
|
|
65
|
+
if (v.includes("\n")) return tomlEscapeString(v);
|
|
66
|
+
return SCALAR_NEEDS_QUOTE.test(v) ? tomlEscapeString(v) : `"${v}"`;
|
|
67
|
+
}
|
|
68
|
+
if (typeof v === "boolean" || typeof v === "number") {
|
|
69
|
+
return String(v);
|
|
70
|
+
}
|
|
71
|
+
return tomlEscapeString(String(v));
|
|
72
|
+
}
|
|
73
|
+
function tomlMultilineString(s) {
|
|
74
|
+
const escaped = s.replace(/\\/g, "\\\\").replace(/"""/g, '""\\"');
|
|
75
|
+
return `"""
|
|
76
|
+
${escaped}
|
|
77
|
+
"""`;
|
|
78
|
+
}
|
|
79
|
+
function tomlQuoteString(s) {
|
|
80
|
+
return tomlValue(s);
|
|
81
|
+
}
|
|
82
|
+
function tomlInlineArray(items) {
|
|
83
|
+
return tomlValue([...items]);
|
|
84
|
+
}
|
|
85
|
+
function tomlInlineTable(table) {
|
|
86
|
+
const entries = Object.entries(table).map(([k, v]) => `${k} = ${tomlValue(v)}`);
|
|
87
|
+
return `{ ${entries.join(", ")} }`;
|
|
88
|
+
}
|
|
89
|
+
function frontmatterDataForSkill(skill) {
|
|
90
|
+
const data = {
|
|
91
|
+
name: skill.meta.id,
|
|
92
|
+
description: skill.meta.description
|
|
93
|
+
};
|
|
94
|
+
if (skill.meta.tags && skill.meta.tags.length > 0) data.tags = skill.meta.tags;
|
|
95
|
+
return data;
|
|
96
|
+
}
|
|
97
|
+
function frontmatterDataForCmd(cmd) {
|
|
98
|
+
const data = {
|
|
99
|
+
id: cmd.meta.id,
|
|
100
|
+
trigger: cmd.meta.trigger,
|
|
101
|
+
description: cmd.meta.description
|
|
102
|
+
};
|
|
103
|
+
if (cmd.meta.args && cmd.meta.args.length > 0) {
|
|
104
|
+
data.args = cmd.meta.args.map((a) => {
|
|
105
|
+
if (a.type === "enum") {
|
|
106
|
+
return a.default !== void 0 ? { name: a.name, type: a.type, values: a.values, default: a.default } : { name: a.name, type: a.type, values: a.values };
|
|
107
|
+
}
|
|
108
|
+
return a.default !== void 0 ? { name: a.name, type: a.type, default: a.default } : { name: a.name, type: a.type };
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return data;
|
|
112
|
+
}
|
|
113
|
+
function buildAgentBody(agent) {
|
|
114
|
+
return `${agent.body.trim()}
|
|
115
|
+
`;
|
|
116
|
+
}
|
|
117
|
+
function buildUmbrellaBody(_agents, extras) {
|
|
118
|
+
const triggerBySkill = new Map(
|
|
119
|
+
extras.availableCommands.map((c) => [c.meta.invokes_skill, c.meta.trigger])
|
|
120
|
+
);
|
|
121
|
+
const skillLines = extras.availableSkills.map((s) => {
|
|
122
|
+
const trigger = triggerBySkill.get(s.meta.id);
|
|
123
|
+
const label = trigger ? `\`${s.meta.id}\` (\`${trigger}\`)` : `\`${s.meta.id}\``;
|
|
124
|
+
return `- ${label} \u2014 ${s.meta.description.split("\n")[0]}`;
|
|
125
|
+
});
|
|
126
|
+
const sections = [
|
|
127
|
+
"# How we work",
|
|
128
|
+
"",
|
|
129
|
+
"This file describes **how to work in this project**. The **business context** (what the project does, how to run it, which subsystems it contains) lives in `README.md` and `docs/`. Read them first.",
|
|
130
|
+
"",
|
|
131
|
+
"## Project map",
|
|
132
|
+
"",
|
|
133
|
+
"- `README.md` \u2014 pitch, quick start, scripts. Entry point.",
|
|
134
|
+
"- `docs/spec/<subsystem>.md` \u2014 *what* and *why* (tech-agnostic).",
|
|
135
|
+
"- `docs/handbook/<feature>.md` \u2014 *how it actually works* today.",
|
|
136
|
+
"- `docs/decisions/<slug>.md` \u2014 ADRs: structural choices and rejected alternatives.",
|
|
137
|
+
"- `docs/tests/integration-tests.md` & `e2e-tests.md` \u2014 core scenarios.",
|
|
138
|
+
"- `docs/workflow/coding-workflow.md` \u2014 CI, branches, PR, release.",
|
|
139
|
+
...extras.hasProjectSkills ? ["- `docs/skills/*.skill.md` \u2014 the project's domain skills."] : [],
|
|
140
|
+
...extras.hasQualityDoc ? [
|
|
141
|
+
"- `docs/quality/code-quality.md` \u2014 the project's code-quality rules: mechanical checks, judgment rules, accepted deviations. Read it before writing or auditing code."
|
|
142
|
+
] : [],
|
|
143
|
+
"",
|
|
144
|
+
"## Doc-first",
|
|
145
|
+
"",
|
|
146
|
+
"The order is: **understand the docs \u2192 fill the gaps \u2192 code \u2192 update the docs \u2192 commit**. If the docs are silent or stale on something you change, fill the gap upfront with the doc-skill writers \u2014 not downstream, not after the fact.",
|
|
147
|
+
"",
|
|
148
|
+
"- Missing spec for a subsystem you touch \u2192 **do not code**, report the gap.",
|
|
149
|
+
"- Implicit structural decision in your changes \u2192 an ADR (`/decision`).",
|
|
150
|
+
"- Changed feature behavior \u2192 update the matching handbook (`/handbook`).",
|
|
151
|
+
"- Suspected code \u2194 doc drift \u2192 `/drift` before moving on."
|
|
152
|
+
];
|
|
153
|
+
if (skillLines.length > 0) {
|
|
154
|
+
sections.push("", "## Doc-skills", "", skillLines.join("\n"));
|
|
155
|
+
}
|
|
156
|
+
return `${sections.join("\n")}
|
|
157
|
+
`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/renderers/claude-code.ts
|
|
161
|
+
var ENGINE = "claude-code";
|
|
162
|
+
var BASE = ".claude";
|
|
163
|
+
var claudeCodeRenderer = {
|
|
164
|
+
engine: ENGINE,
|
|
165
|
+
renderSkill(skill) {
|
|
166
|
+
if (!isSkillEngineAllowed(skill, ENGINE)) {
|
|
167
|
+
return {
|
|
168
|
+
files: [],
|
|
169
|
+
warnings: [`skill "${skill.meta.id}" excluded from engine "${ENGINE}"`]
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const check = checkSkillCapabilities(skill, ENGINE);
|
|
173
|
+
if (check.blockingWarnings.length > 0) {
|
|
174
|
+
return { files: [], warnings: check.blockingWarnings };
|
|
175
|
+
}
|
|
176
|
+
const content = `${yamlFrontmatter(frontmatterDataForSkill(skill))}
|
|
177
|
+
${skill.body}
|
|
178
|
+
`;
|
|
179
|
+
return {
|
|
180
|
+
files: [{ path: `${BASE}/skills/${skill.meta.id}/SKILL.md`, content }],
|
|
181
|
+
warnings: check.degradedWarnings
|
|
182
|
+
};
|
|
183
|
+
},
|
|
184
|
+
renderCmd(cmd) {
|
|
185
|
+
const content = `${yamlFrontmatter(frontmatterDataForCmd(cmd))}
|
|
186
|
+
${cmd.body}
|
|
187
|
+
`;
|
|
188
|
+
return {
|
|
189
|
+
files: [{ path: `${BASE}/commands/${cmd.meta.id}.md`, content }],
|
|
190
|
+
warnings: []
|
|
191
|
+
};
|
|
192
|
+
},
|
|
193
|
+
renderAgents(agents) {
|
|
194
|
+
const files = [];
|
|
195
|
+
for (const agent of agents) {
|
|
196
|
+
const subagentFrontmatter = yamlFrontmatter({
|
|
197
|
+
name: agent.meta.id,
|
|
198
|
+
description: agent.meta.description
|
|
199
|
+
});
|
|
200
|
+
const body = buildAgentBody(agent);
|
|
201
|
+
const content = `${subagentFrontmatter}
|
|
202
|
+
${body}`;
|
|
203
|
+
files.push({ path: `${BASE}/agents/${agent.meta.id}.md`, content });
|
|
204
|
+
}
|
|
205
|
+
return { files, warnings: [] };
|
|
206
|
+
},
|
|
207
|
+
renderMcp(servers) {
|
|
208
|
+
if (servers.length === 0) return { files: [], warnings: [] };
|
|
209
|
+
const mcpServers = {};
|
|
210
|
+
for (const s of servers) {
|
|
211
|
+
const entry = { command: s.command, args: [...s.args] };
|
|
212
|
+
if (s.env) entry.env = s.env;
|
|
213
|
+
mcpServers[s.id] = entry;
|
|
214
|
+
}
|
|
215
|
+
const content = `${JSON.stringify({ mcpServers }, null, 2)}
|
|
216
|
+
`;
|
|
217
|
+
return { files: [{ path: ".mcp.json", content }], warnings: [] };
|
|
218
|
+
},
|
|
219
|
+
renderStopHook() {
|
|
220
|
+
const settings = {
|
|
221
|
+
hooks: {
|
|
222
|
+
Stop: [
|
|
223
|
+
{
|
|
224
|
+
matcher: "",
|
|
225
|
+
hooks: [
|
|
226
|
+
{
|
|
227
|
+
type: "command",
|
|
228
|
+
command: `npx -y ${STOP_HOOK_PACKAGE} stop-hook --engine claude`
|
|
229
|
+
}
|
|
230
|
+
]
|
|
231
|
+
}
|
|
232
|
+
]
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
return {
|
|
236
|
+
files: [{ path: `${BASE}/settings.json`, content: `${JSON.stringify(settings, null, 2)}
|
|
237
|
+
` }],
|
|
238
|
+
warnings: []
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// src/renderers/codex-cli.ts
|
|
244
|
+
var ENGINE2 = "codex-cli";
|
|
245
|
+
var BASE2 = ".codex";
|
|
246
|
+
var SKILLS_BASE = ".agents/skills";
|
|
247
|
+
var codexCliRenderer = {
|
|
248
|
+
engine: ENGINE2,
|
|
249
|
+
renderSkill(skill) {
|
|
250
|
+
if (!isSkillEngineAllowed(skill, ENGINE2)) {
|
|
251
|
+
return {
|
|
252
|
+
files: [],
|
|
253
|
+
warnings: [`skill "${skill.meta.id}" excluded from engine "${ENGINE2}"`]
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
const check = checkSkillCapabilities(skill, ENGINE2);
|
|
257
|
+
if (check.blockingWarnings.length > 0) {
|
|
258
|
+
return { files: [], warnings: check.blockingWarnings };
|
|
259
|
+
}
|
|
260
|
+
const content = `${yamlFrontmatter(frontmatterDataForSkill(skill))}
|
|
261
|
+
${skill.body}
|
|
262
|
+
`;
|
|
263
|
+
return {
|
|
264
|
+
files: [{ path: `${SKILLS_BASE}/${skill.meta.id}/SKILL.md`, content }],
|
|
265
|
+
warnings: check.degradedWarnings
|
|
266
|
+
};
|
|
267
|
+
},
|
|
268
|
+
// Codex custom prompts are deprecated in favour of skills: nothing to render.
|
|
269
|
+
renderCmd(_cmd) {
|
|
270
|
+
return { files: [], warnings: [] };
|
|
271
|
+
},
|
|
272
|
+
renderAgents(agents) {
|
|
273
|
+
const files = [];
|
|
274
|
+
for (const agent of agents) {
|
|
275
|
+
const content = [
|
|
276
|
+
`name = ${tomlQuoteString(agent.meta.id)}`,
|
|
277
|
+
`description = ${tomlQuoteString(agent.meta.description)}`,
|
|
278
|
+
`developer_instructions = ${tomlMultilineString(buildAgentBody(agent).trimEnd())}`,
|
|
279
|
+
""
|
|
280
|
+
].join("\n");
|
|
281
|
+
files.push({ path: `${BASE2}/agents/${agent.meta.id}.toml`, content });
|
|
282
|
+
}
|
|
283
|
+
return { files, warnings: [] };
|
|
284
|
+
},
|
|
285
|
+
renderMcp(servers) {
|
|
286
|
+
if (servers.length === 0) return { files: [], warnings: [] };
|
|
287
|
+
const blocks = [];
|
|
288
|
+
for (const s of servers) {
|
|
289
|
+
const lines = [
|
|
290
|
+
`[mcp_servers.${s.id}]`,
|
|
291
|
+
`command = ${tomlQuoteString(s.command)}`,
|
|
292
|
+
`args = ${tomlInlineArray([...s.args])}`
|
|
293
|
+
];
|
|
294
|
+
if (s.env) lines.push(`env = ${tomlInlineTable(s.env)}`);
|
|
295
|
+
blocks.push(lines.join("\n"));
|
|
296
|
+
}
|
|
297
|
+
const content = `${blocks.join("\n\n")}
|
|
298
|
+
`;
|
|
299
|
+
return { files: [{ path: `${BASE2}/config.toml`, content }], warnings: [] };
|
|
300
|
+
},
|
|
301
|
+
renderStopHook() {
|
|
302
|
+
const hooks = {
|
|
303
|
+
hooks: {
|
|
304
|
+
Stop: [
|
|
305
|
+
{
|
|
306
|
+
hooks: [
|
|
307
|
+
{
|
|
308
|
+
type: "command",
|
|
309
|
+
command: `npx -y ${STOP_HOOK_PACKAGE} stop-hook --engine codex`
|
|
310
|
+
}
|
|
311
|
+
]
|
|
312
|
+
}
|
|
313
|
+
]
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
return {
|
|
317
|
+
files: [{ path: `${BASE2}/hooks.json`, content: `${JSON.stringify(hooks, null, 2)}
|
|
318
|
+
` }],
|
|
319
|
+
warnings: []
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
// src/renderers/opencode.ts
|
|
325
|
+
var ENGINE3 = "opencode";
|
|
326
|
+
var BASE3 = ".opencode";
|
|
327
|
+
var opencodeRenderer = {
|
|
328
|
+
engine: ENGINE3,
|
|
329
|
+
renderSkill(skill) {
|
|
330
|
+
if (!isSkillEngineAllowed(skill, ENGINE3)) {
|
|
331
|
+
return {
|
|
332
|
+
files: [],
|
|
333
|
+
warnings: [`skill "${skill.meta.id}" excluded from engine "${ENGINE3}"`]
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
const check = checkSkillCapabilities(skill, ENGINE3);
|
|
337
|
+
if (check.blockingWarnings.length > 0) {
|
|
338
|
+
return { files: [], warnings: check.blockingWarnings };
|
|
339
|
+
}
|
|
340
|
+
const content = `${yamlFrontmatter(frontmatterDataForSkill(skill))}
|
|
341
|
+
${skill.body}
|
|
342
|
+
`;
|
|
343
|
+
return {
|
|
344
|
+
files: [{ path: `${BASE3}/skills/${skill.meta.id}/SKILL.md`, content }],
|
|
345
|
+
warnings: check.degradedWarnings
|
|
346
|
+
};
|
|
347
|
+
},
|
|
348
|
+
renderCmd(cmd) {
|
|
349
|
+
const content = `${yamlFrontmatter({ description: cmd.meta.description })}
|
|
350
|
+
${cmd.body}
|
|
351
|
+
`;
|
|
352
|
+
return {
|
|
353
|
+
files: [{ path: `${BASE3}/commands/${cmd.meta.id}.md`, content }],
|
|
354
|
+
warnings: []
|
|
355
|
+
};
|
|
356
|
+
},
|
|
357
|
+
renderAgents(agents) {
|
|
358
|
+
const files = [];
|
|
359
|
+
for (const agent of agents) {
|
|
360
|
+
const fm = yamlFrontmatter({
|
|
361
|
+
description: agent.meta.description,
|
|
362
|
+
mode: "all"
|
|
363
|
+
});
|
|
364
|
+
const body = buildAgentBody(agent);
|
|
365
|
+
files.push({ path: `${BASE3}/agents/${agent.meta.id}.md`, content: `${fm}
|
|
366
|
+
${body}` });
|
|
367
|
+
}
|
|
368
|
+
return { files, warnings: [] };
|
|
369
|
+
},
|
|
370
|
+
renderMcp(servers) {
|
|
371
|
+
if (servers.length === 0) return { files: [], warnings: [] };
|
|
372
|
+
const mcp = {};
|
|
373
|
+
for (const s of servers) {
|
|
374
|
+
const entry = {
|
|
375
|
+
type: "local",
|
|
376
|
+
command: [s.command, ...s.args],
|
|
377
|
+
enabled: true
|
|
378
|
+
};
|
|
379
|
+
if (s.env) entry.environment = s.env;
|
|
380
|
+
mcp[s.id] = entry;
|
|
381
|
+
}
|
|
382
|
+
const config = { $schema: "https://opencode.ai/config.json", mcp };
|
|
383
|
+
const content = `${JSON.stringify(config, null, 2)}
|
|
384
|
+
`;
|
|
385
|
+
return { files: [{ path: `${BASE3}/opencode.json`, content }], warnings: [] };
|
|
386
|
+
},
|
|
387
|
+
renderStopHook() {
|
|
388
|
+
const content = `import { spawnSync } from "node:child_process";
|
|
389
|
+
|
|
390
|
+
export const HarnessStopHook = async ({ client, directory }) => ({
|
|
391
|
+
event: async ({ event }) => {
|
|
392
|
+
if (event.type !== "session.idle") return;
|
|
393
|
+
const sessionID = event.properties?.sessionID;
|
|
394
|
+
if (!sessionID) return;
|
|
395
|
+
const r = spawnSync(
|
|
396
|
+
"npx",
|
|
397
|
+
["-y", "${STOP_HOOK_PACKAGE}", "stop-hook", "--engine", "opencode", "--cwd", directory],
|
|
398
|
+
{ input: JSON.stringify({ sessionID }), encoding: "utf-8" },
|
|
399
|
+
);
|
|
400
|
+
const report = (r.stderr ?? "").trim();
|
|
401
|
+
if (r.status !== 2 || report === "") return;
|
|
402
|
+
// OpenCode defaults to its build agent when a prompt omits agent.
|
|
403
|
+
// Preserve the last conversation persona, excluding compaction summaries.
|
|
404
|
+
const history = await client.session.messages({ path: { id: sessionID } }).catch(() => undefined);
|
|
405
|
+
if (history?.error || !Array.isArray(history?.data)) return;
|
|
406
|
+
const current = history.data.findLast(({ info }) =>
|
|
407
|
+
(info.role === "user" || (info.role === "assistant" && !info.summary)) &&
|
|
408
|
+
typeof info.agent === "string" && info.agent.trim() !== "",
|
|
409
|
+
);
|
|
410
|
+
if (!current) return;
|
|
411
|
+
await client.session.prompt({
|
|
412
|
+
path: { id: sessionID },
|
|
413
|
+
body: { agent: current.info.agent, parts: [{ type: "text", text: report }] },
|
|
414
|
+
});
|
|
415
|
+
},
|
|
416
|
+
});
|
|
417
|
+
`;
|
|
418
|
+
return {
|
|
419
|
+
files: [{ path: `${BASE3}/plugins/harness-stop-hook.ts`, content }],
|
|
420
|
+
warnings: []
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
// src/renderers/index.ts
|
|
426
|
+
var RENDERERS = {
|
|
427
|
+
"claude-code": claudeCodeRenderer,
|
|
428
|
+
"codex-cli": codexCliRenderer,
|
|
429
|
+
opencode: opencodeRenderer
|
|
430
|
+
};
|
|
431
|
+
function getRenderer(engine) {
|
|
432
|
+
return RENDERERS[engine];
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// src/render.ts
|
|
436
|
+
var RenderError = class extends Error {
|
|
437
|
+
constructor(message) {
|
|
438
|
+
super(message);
|
|
439
|
+
this.name = "RenderError";
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
function mergeById(stock, added) {
|
|
443
|
+
const result = /* @__PURE__ */ new Map();
|
|
444
|
+
for (const item of stock) result.set(item.meta.id, item);
|
|
445
|
+
if (added) {
|
|
446
|
+
for (const item of added) result.set(item.meta.id, item);
|
|
447
|
+
}
|
|
448
|
+
return Array.from(result.values());
|
|
449
|
+
}
|
|
450
|
+
function removeByIds(items, disabled) {
|
|
451
|
+
if (disabled.length === 0) return [...items];
|
|
452
|
+
const disabledSet = new Set(disabled);
|
|
453
|
+
return items.filter((item) => !disabledSet.has(item.meta.id));
|
|
454
|
+
}
|
|
455
|
+
function applyOverrides(harness, overrides) {
|
|
456
|
+
const disabled = overrides?.disable ?? [];
|
|
457
|
+
return {
|
|
458
|
+
agents: removeByIds(mergeById(harness.agents, overrides?.add?.agents), disabled),
|
|
459
|
+
skills: removeByIds(mergeById(harness.skills, overrides?.add?.skills), disabled),
|
|
460
|
+
cmds: removeByIds(mergeById(harness.cmds, overrides?.add?.cmds), disabled)
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
var CLAUDE_MD_STUB = "@AGENTS.md\n";
|
|
464
|
+
function opencodeLast(engines) {
|
|
465
|
+
return [...engines].sort((a, b) => Number(a === "opencode") - Number(b === "opencode"));
|
|
466
|
+
}
|
|
467
|
+
function render(input) {
|
|
468
|
+
const { engines } = input;
|
|
469
|
+
if (engines.length === 0) {
|
|
470
|
+
throw new RenderError("at least one engine must be selected");
|
|
471
|
+
}
|
|
472
|
+
const base = input.projectSkills?.length ? { ...input.harness, skills: mergeById(input.harness.skills, input.projectSkills) } : input.harness;
|
|
473
|
+
const harness = applyOverrides(base, input.overrides);
|
|
474
|
+
if (harness.agents.length === 0) {
|
|
475
|
+
throw new RenderError("No agents found in harness");
|
|
476
|
+
}
|
|
477
|
+
const files = [];
|
|
478
|
+
const warnings = [];
|
|
479
|
+
const exposedSkills = /* @__PURE__ */ new Map();
|
|
480
|
+
const exposedCmds = /* @__PURE__ */ new Map();
|
|
481
|
+
for (const engine of opencodeLast(engines)) {
|
|
482
|
+
const renderer = getRenderer(engine);
|
|
483
|
+
const renderedSkills = [];
|
|
484
|
+
for (const skill of harness.skills) {
|
|
485
|
+
const result = renderer.renderSkill(skill);
|
|
486
|
+
warnings.push(...result.warnings);
|
|
487
|
+
if (result.files.length > 0) {
|
|
488
|
+
const alreadyVisible = engine === "opencode" && exposedSkills.has(skill.meta.id);
|
|
489
|
+
if (!alreadyVisible) files.push(...result.files);
|
|
490
|
+
renderedSkills.push(skill);
|
|
491
|
+
exposedSkills.set(skill.meta.id, skill);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
const renderedSkillIds = new Set(renderedSkills.map((s) => s.meta.id));
|
|
495
|
+
const renderedCmds = [];
|
|
496
|
+
for (const cmd of harness.cmds) {
|
|
497
|
+
if (!renderedSkillIds.has(cmd.meta.invokes_skill)) continue;
|
|
498
|
+
const result = renderer.renderCmd(cmd);
|
|
499
|
+
warnings.push(...result.warnings);
|
|
500
|
+
if (result.files.length > 0) {
|
|
501
|
+
files.push(...result.files);
|
|
502
|
+
renderedCmds.push(cmd);
|
|
503
|
+
exposedCmds.set(cmd.meta.id, cmd);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
const agentsResult = renderer.renderAgents(harness.agents);
|
|
507
|
+
warnings.push(...agentsResult.warnings);
|
|
508
|
+
files.push(...agentsResult.files);
|
|
509
|
+
if (input.mcpServers && input.mcpServers.length > 0) {
|
|
510
|
+
const specs = input.mcpServers.map((id) => MCP_SERVERS[id]);
|
|
511
|
+
const mcpResult = renderer.renderMcp(specs);
|
|
512
|
+
warnings.push(...mcpResult.warnings);
|
|
513
|
+
files.push(...mcpResult.files);
|
|
514
|
+
}
|
|
515
|
+
if (input.withStopHook !== false) {
|
|
516
|
+
const hookResult = renderer.renderStopHook();
|
|
517
|
+
warnings.push(...hookResult.warnings);
|
|
518
|
+
files.push(...hookResult.files);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
const hasProjectSkills = (input.projectSkills?.length ?? 0) > 0;
|
|
522
|
+
const umbrellaContent = buildUmbrellaBody(harness.agents, {
|
|
523
|
+
availableSkills: Array.from(exposedSkills.values()),
|
|
524
|
+
availableCommands: Array.from(exposedCmds.values()),
|
|
525
|
+
hasProjectSkills,
|
|
526
|
+
hasQualityDoc: input.hasQualityDoc === true
|
|
527
|
+
});
|
|
528
|
+
files.push({ path: "AGENTS.md", content: umbrellaContent });
|
|
529
|
+
if (engines.includes("claude-code")) {
|
|
530
|
+
files.push({ path: "CLAUDE.md", content: CLAUDE_MD_STUB });
|
|
531
|
+
}
|
|
532
|
+
return { files, warnings };
|
|
533
|
+
}
|
|
534
|
+
export {
|
|
535
|
+
RenderError,
|
|
536
|
+
applyOverrides,
|
|
537
|
+
render
|
|
538
|
+
};
|
|
539
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mcp.ts","../src/renderers/common.ts","../src/types.ts","../src/renderers/claude-code.ts","../src/renderers/codex-cli.ts","../src/renderers/opencode.ts","../src/renderers/index.ts","../src/render.ts"],"sourcesContent":["export const MCP_SERVER_IDS = [\"context7\"] as const;\nexport type McpServerId = (typeof MCP_SERVER_IDS)[number];\n\nexport interface McpServerSpec {\n id: McpServerId;\n description: string;\n command: string;\n args: ReadonlyArray<string>;\n env?: Record<string, string>;\n}\n\nexport const MCP_SERVERS: Record<McpServerId, McpServerSpec> = {\n context7: {\n id: \"context7\",\n description:\n \"Upstash Context7 — fetches up-to-date library docs via resolve-library-id + get-library-docs.\",\n command: \"npx\",\n args: [\"-y\", \"@upstash/context7-mcp\"],\n },\n};\n\nexport function resolveMcpIds(requested: ReadonlyArray<string>): McpServerId[] {\n return requested.map((id) => {\n if (!(MCP_SERVER_IDS as ReadonlyArray<string>).includes(id)) {\n throw new Error(`unknown MCP server: ${id} (expected one of ${MCP_SERVER_IDS.join(\", \")})`);\n }\n return id as McpServerId;\n });\n}\n","import { stringify as yamlStringify } from \"yaml\";\nimport type { McpServerSpec } from \"../mcp.js\";\nimport type { Agent } from \"../parser/agent.js\";\nimport type { Cmd } from \"../parser/cmd.js\";\nimport type { Skill } from \"../parser/skill.js\";\nimport {\n CAPABILITIES_MATRIX,\n type Capability,\n DEGRADED_CAPABILITIES,\n type Engine,\n type RenderedFile,\n} from \"../types.js\";\n\nexport interface UmbrellaExtras {\n availableSkills: ReadonlyArray<Skill>;\n availableCommands: ReadonlyArray<Cmd>;\n hasProjectSkills?: boolean;\n hasQualityDoc?: boolean;\n}\n\nexport interface RenderResult {\n files: RenderedFile[];\n warnings: string[];\n}\n\nexport interface Renderer {\n readonly engine: Engine;\n renderSkill(skill: Skill): RenderResult;\n renderCmd(cmd: Cmd): RenderResult;\n renderAgents(agents: ReadonlyArray<Agent>): RenderResult;\n renderMcp(servers: ReadonlyArray<McpServerSpec>): RenderResult;\n renderStopHook(): RenderResult;\n}\n\nexport const STOP_HOOK_PACKAGE = \"@vaubaan/harness\";\n\nexport interface CapabilityCheck {\n blockingWarnings: string[];\n degradedWarnings: string[];\n}\n\nexport function checkSkillCapabilities(skill: Skill, engine: Engine): CapabilityCheck {\n const blockingWarnings: string[] = [];\n const degradedWarnings: string[] = [];\n const supported = CAPABILITIES_MATRIX[engine];\n const degraded = DEGRADED_CAPABILITIES[engine] ?? [];\n if (!skill.meta.requires) return { blockingWarnings, degradedWarnings };\n for (const cap of skill.meta.requires) {\n if (!supported.includes(cap as Capability)) {\n blockingWarnings.push(\n `skill \"${skill.meta.id}\" requires \"${cap}\" not supported by \"${engine}\" — skipped`,\n );\n } else if (degraded.includes(cap as Capability)) {\n degradedWarnings.push(\n `skill \"${skill.meta.id}\" requires \"${cap}\" — supported by \"${engine}\" with degradation`,\n );\n }\n }\n return { blockingWarnings, degradedWarnings };\n}\n\nexport function isSkillEngineAllowed(skill: Skill, engine: Engine): boolean {\n if (!skill.meta.engines) return true;\n return skill.meta.engines.includes(engine);\n}\n\nexport function yamlFrontmatter(data: Record<string, unknown>): string {\n const rendered = yamlStringify(data, { lineWidth: 0 }).trimEnd();\n return `---\\n${rendered}\\n---\\n`;\n}\n\nconst SCALAR_NEEDS_QUOTE = /[^a-zA-Z0-9_./-]/;\n\nfunction tomlEscapeString(s: string): string {\n return `\"${s.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"').replace(/\\n/g, \"\\\\n\")}\"`;\n}\n\nfunction tomlValue(v: unknown): string {\n if (Array.isArray(v)) {\n return `[${v.map((x) => tomlValue(x)).join(\", \")}]`;\n }\n if (typeof v === \"string\") {\n if (v.includes(\"\\n\")) return tomlEscapeString(v);\n return SCALAR_NEEDS_QUOTE.test(v) ? tomlEscapeString(v) : `\"${v}\"`;\n }\n if (typeof v === \"boolean\" || typeof v === \"number\") {\n return String(v);\n }\n return tomlEscapeString(String(v));\n}\n\n/** TOML multi-line basic string (`\"\"\"…\"\"\"`), used for Codex `developer_instructions`. */\nexport function tomlMultilineString(s: string): string {\n const escaped = s.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"\"\"/g, '\"\"\\\\\"');\n return `\"\"\"\\n${escaped}\\n\"\"\"`;\n}\n\nexport function tomlQuoteString(s: string): string {\n return tomlValue(s);\n}\n\nexport function tomlInlineArray(items: ReadonlyArray<unknown>): string {\n return tomlValue([...items]);\n}\n\nexport function tomlInlineTable(table: Record<string, string>): string {\n const entries = Object.entries(table).map(([k, v]) => `${k} = ${tomlValue(v)}`);\n return `{ ${entries.join(\", \")} }`;\n}\n\nexport function frontmatterDataForSkill(skill: Skill): Record<string, unknown> {\n // Agent Skills spec (agentskills.io): `name` must match the directory name.\n const data: Record<string, unknown> = {\n name: skill.meta.id,\n description: skill.meta.description,\n };\n if (skill.meta.tags && skill.meta.tags.length > 0) data.tags = skill.meta.tags;\n return data;\n}\n\nexport function frontmatterDataForCmd(cmd: Cmd): Record<string, unknown> {\n const data: Record<string, unknown> = {\n id: cmd.meta.id,\n trigger: cmd.meta.trigger,\n description: cmd.meta.description,\n };\n if (cmd.meta.args && cmd.meta.args.length > 0) {\n data.args = cmd.meta.args.map((a) => {\n if (a.type === \"enum\") {\n return a.default !== undefined\n ? { name: a.name, type: a.type, values: a.values, default: a.default }\n : { name: a.name, type: a.type, values: a.values };\n }\n return a.default !== undefined\n ? { name: a.name, type: a.type, default: a.default }\n : { name: a.name, type: a.type };\n });\n }\n return data;\n}\n\n/**\n * Personas carry their role contract only: engines discover skills and\n * commands natively, so listing them per persona is pure context cost.\n */\nexport function buildAgentBody(agent: Agent): string {\n return `${agent.body.trim()}\\n`;\n}\n\nexport function buildUmbrellaBody(_agents: ReadonlyArray<Agent>, extras: UmbrellaExtras): string {\n const triggerBySkill = new Map(\n extras.availableCommands.map((c) => [c.meta.invokes_skill, c.meta.trigger]),\n );\n const skillLines = extras.availableSkills.map((s) => {\n const trigger = triggerBySkill.get(s.meta.id);\n const label = trigger ? `\\`${s.meta.id}\\` (\\`${trigger}\\`)` : `\\`${s.meta.id}\\``;\n return `- ${label} — ${s.meta.description.split(\"\\n\")[0]}`;\n });\n const sections: string[] = [\n \"# How we work\",\n \"\",\n \"This file describes **how to work in this project**. The **business context** (what the project does, how to run it, which subsystems it contains) lives in `README.md` and `docs/`. Read them first.\",\n \"\",\n \"## Project map\",\n \"\",\n \"- `README.md` — pitch, quick start, scripts. Entry point.\",\n \"- `docs/spec/<subsystem>.md` — *what* and *why* (tech-agnostic).\",\n \"- `docs/handbook/<feature>.md` — *how it actually works* today.\",\n \"- `docs/decisions/<slug>.md` — ADRs: structural choices and rejected alternatives.\",\n \"- `docs/tests/integration-tests.md` & `e2e-tests.md` — core scenarios.\",\n \"- `docs/workflow/coding-workflow.md` — CI, branches, PR, release.\",\n ...(extras.hasProjectSkills\n ? [\"- `docs/skills/*.skill.md` — the project's domain skills.\"]\n : []),\n ...(extras.hasQualityDoc\n ? [\n \"- `docs/quality/code-quality.md` — the project's code-quality rules: mechanical checks, judgment rules, accepted deviations. Read it before writing or auditing code.\",\n ]\n : []),\n \"\",\n \"## Doc-first\",\n \"\",\n \"The order is: **understand the docs → fill the gaps → code → update the docs → commit**. If the docs are silent or stale on something you change, fill the gap upfront with the doc-skill writers — not downstream, not after the fact.\",\n \"\",\n \"- Missing spec for a subsystem you touch → **do not code**, report the gap.\",\n \"- Implicit structural decision in your changes → an ADR (`/decision`).\",\n \"- Changed feature behavior → update the matching handbook (`/handbook`).\",\n \"- Suspected code ↔ doc drift → `/drift` before moving on.\",\n ];\n if (skillLines.length > 0) {\n sections.push(\"\", \"## Doc-skills\", \"\", skillLines.join(\"\\n\"));\n }\n return `${sections.join(\"\\n\")}\\n`;\n}\n","export const AGENT_IDS = [\"coding\", \"review\", \"merge\", \"planning\"] as const;\nexport type AgentId = (typeof AGENT_IDS)[number];\n\nexport const ENGINES = [\"claude-code\", \"codex-cli\", \"opencode\"] as const;\nexport type Engine = (typeof ENGINES)[number];\n\nexport const ENGINE_ROOT_DIR: Record<Engine, string> = {\n \"claude-code\": \".claude\",\n \"codex-cli\": \".codex\",\n opencode: \".opencode\",\n};\n\nexport const CAPABILITIES = [\"multi-step\", \"slash-command\", \"inline-exec\"] as const;\nexport type Capability = (typeof CAPABILITIES)[number];\n\nexport interface RenderedFile {\n path: string;\n content: string;\n}\n\nexport interface CapabilitiesMatrix {\n [engine: string]: ReadonlyArray<Capability>;\n}\n\nexport const CAPABILITIES_MATRIX: Record<Engine, ReadonlyArray<Capability>> = {\n \"claude-code\": [\"multi-step\", \"slash-command\", \"inline-exec\"],\n \"codex-cli\": [\"multi-step\", \"slash-command\", \"inline-exec\"],\n opencode: [\"multi-step\", \"slash-command\", \"inline-exec\"],\n};\n\nexport const DEGRADED_CAPABILITIES: Partial<Record<Engine, ReadonlyArray<Capability>>> = {\n \"codex-cli\": [\"multi-step\"],\n};\n\nexport function engineSupports(engine: Engine, capability: Capability): boolean {\n return CAPABILITIES_MATRIX[engine].includes(capability);\n}\n","import type { McpServerSpec } from \"../mcp.js\";\nimport type { Agent } from \"../parser/agent.js\";\nimport type { Cmd } from \"../parser/cmd.js\";\nimport type { Skill } from \"../parser/skill.js\";\nimport type { Engine } from \"../types.js\";\nimport {\n type RenderResult,\n type Renderer,\n STOP_HOOK_PACKAGE,\n buildAgentBody,\n checkSkillCapabilities,\n frontmatterDataForCmd,\n frontmatterDataForSkill,\n isSkillEngineAllowed,\n yamlFrontmatter,\n} from \"./common.js\";\n\nconst ENGINE: Engine = \"claude-code\";\nconst BASE = \".claude\";\n\nexport const claudeCodeRenderer: Renderer = {\n engine: ENGINE,\n renderSkill(skill: Skill): RenderResult {\n if (!isSkillEngineAllowed(skill, ENGINE)) {\n return {\n files: [],\n warnings: [`skill \"${skill.meta.id}\" excluded from engine \"${ENGINE}\"`],\n };\n }\n const check = checkSkillCapabilities(skill, ENGINE);\n if (check.blockingWarnings.length > 0) {\n return { files: [], warnings: check.blockingWarnings };\n }\n const content = `${yamlFrontmatter(frontmatterDataForSkill(skill))}\\n${skill.body}\\n`;\n return {\n files: [{ path: `${BASE}/skills/${skill.meta.id}/SKILL.md`, content }],\n warnings: check.degradedWarnings,\n };\n },\n renderCmd(cmd: Cmd): RenderResult {\n const content = `${yamlFrontmatter(frontmatterDataForCmd(cmd))}\\n${cmd.body}\\n`;\n return {\n files: [{ path: `${BASE}/commands/${cmd.meta.id}.md`, content }],\n warnings: [],\n };\n },\n renderAgents(agents: ReadonlyArray<Agent>): RenderResult {\n const files = [];\n for (const agent of agents) {\n const subagentFrontmatter = yamlFrontmatter({\n name: agent.meta.id,\n description: agent.meta.description,\n });\n const body = buildAgentBody(agent);\n const content = `${subagentFrontmatter}\\n${body}`;\n files.push({ path: `${BASE}/agents/${agent.meta.id}.md`, content });\n }\n return { files, warnings: [] };\n },\n renderMcp(servers: ReadonlyArray<McpServerSpec>): RenderResult {\n if (servers.length === 0) return { files: [], warnings: [] };\n const mcpServers: Record<string, Record<string, unknown>> = {};\n for (const s of servers) {\n const entry: Record<string, unknown> = { command: s.command, args: [...s.args] };\n if (s.env) entry.env = s.env;\n mcpServers[s.id] = entry;\n }\n const content = `${JSON.stringify({ mcpServers }, null, 2)}\\n`;\n return { files: [{ path: \".mcp.json\", content }], warnings: [] };\n },\n renderStopHook(): RenderResult {\n const settings = {\n hooks: {\n Stop: [\n {\n matcher: \"\",\n hooks: [\n {\n type: \"command\",\n command: `npx -y ${STOP_HOOK_PACKAGE} stop-hook --engine claude`,\n },\n ],\n },\n ],\n },\n };\n return {\n files: [{ path: `${BASE}/settings.json`, content: `${JSON.stringify(settings, null, 2)}\\n` }],\n warnings: [],\n };\n },\n};\n","import type { McpServerSpec } from \"../mcp.js\";\nimport type { Agent } from \"../parser/agent.js\";\nimport type { Cmd } from \"../parser/cmd.js\";\nimport type { Skill } from \"../parser/skill.js\";\nimport type { Engine } from \"../types.js\";\nimport {\n type RenderResult,\n type Renderer,\n STOP_HOOK_PACKAGE,\n buildAgentBody,\n checkSkillCapabilities,\n frontmatterDataForSkill,\n isSkillEngineAllowed,\n tomlInlineArray,\n tomlInlineTable,\n tomlMultilineString,\n tomlQuoteString,\n yamlFrontmatter,\n} from \"./common.js\";\n\nconst ENGINE: Engine = \"codex-cli\";\nconst BASE = \".codex\";\n// Codex discovers repo skills under `.agents/skills/` only — `.codex/skills/` is not scanned.\nconst SKILLS_BASE = \".agents/skills\";\n\nexport const codexCliRenderer: Renderer = {\n engine: ENGINE,\n renderSkill(skill: Skill): RenderResult {\n if (!isSkillEngineAllowed(skill, ENGINE)) {\n return {\n files: [],\n warnings: [`skill \"${skill.meta.id}\" excluded from engine \"${ENGINE}\"`],\n };\n }\n const check = checkSkillCapabilities(skill, ENGINE);\n if (check.blockingWarnings.length > 0) {\n return { files: [], warnings: check.blockingWarnings };\n }\n const content = `${yamlFrontmatter(frontmatterDataForSkill(skill))}\\n${skill.body}\\n`;\n return {\n files: [{ path: `${SKILLS_BASE}/${skill.meta.id}/SKILL.md`, content }],\n warnings: check.degradedWarnings,\n };\n },\n // Codex custom prompts are deprecated in favour of skills: nothing to render.\n renderCmd(_cmd: Cmd): RenderResult {\n return { files: [], warnings: [] };\n },\n renderAgents(agents: ReadonlyArray<Agent>): RenderResult {\n const files = [];\n for (const agent of agents) {\n const content = [\n `name = ${tomlQuoteString(agent.meta.id)}`,\n `description = ${tomlQuoteString(agent.meta.description)}`,\n `developer_instructions = ${tomlMultilineString(buildAgentBody(agent).trimEnd())}`,\n \"\",\n ].join(\"\\n\");\n files.push({ path: `${BASE}/agents/${agent.meta.id}.toml`, content });\n }\n return { files, warnings: [] };\n },\n renderMcp(servers: ReadonlyArray<McpServerSpec>): RenderResult {\n if (servers.length === 0) return { files: [], warnings: [] };\n const blocks: string[] = [];\n for (const s of servers) {\n const lines = [\n `[mcp_servers.${s.id}]`,\n `command = ${tomlQuoteString(s.command)}`,\n `args = ${tomlInlineArray([...s.args])}`,\n ];\n if (s.env) lines.push(`env = ${tomlInlineTable(s.env)}`);\n blocks.push(lines.join(\"\\n\"));\n }\n const content = `${blocks.join(\"\\n\\n\")}\\n`;\n return { files: [{ path: `${BASE}/config.toml`, content }], warnings: [] };\n },\n renderStopHook(): RenderResult {\n const hooks = {\n hooks: {\n Stop: [\n {\n hooks: [\n {\n type: \"command\",\n command: `npx -y ${STOP_HOOK_PACKAGE} stop-hook --engine codex`,\n },\n ],\n },\n ],\n },\n };\n return {\n files: [{ path: `${BASE}/hooks.json`, content: `${JSON.stringify(hooks, null, 2)}\\n` }],\n warnings: [],\n };\n },\n};\n","import type { McpServerSpec } from \"../mcp.js\";\nimport type { Agent } from \"../parser/agent.js\";\nimport type { Cmd } from \"../parser/cmd.js\";\nimport type { Skill } from \"../parser/skill.js\";\nimport type { Engine } from \"../types.js\";\nimport {\n type RenderResult,\n type Renderer,\n STOP_HOOK_PACKAGE,\n buildAgentBody,\n checkSkillCapabilities,\n frontmatterDataForSkill,\n isSkillEngineAllowed,\n yamlFrontmatter,\n} from \"./common.js\";\n\nconst ENGINE: Engine = \"opencode\";\nconst BASE = \".opencode\";\n\nexport const opencodeRenderer: Renderer = {\n engine: ENGINE,\n renderSkill(skill: Skill): RenderResult {\n if (!isSkillEngineAllowed(skill, ENGINE)) {\n return {\n files: [],\n warnings: [`skill \"${skill.meta.id}\" excluded from engine \"${ENGINE}\"`],\n };\n }\n const check = checkSkillCapabilities(skill, ENGINE);\n if (check.blockingWarnings.length > 0) {\n return { files: [], warnings: check.blockingWarnings };\n }\n const content = `${yamlFrontmatter(frontmatterDataForSkill(skill))}\\n${skill.body}\\n`;\n return {\n files: [{ path: `${BASE}/skills/${skill.meta.id}/SKILL.md`, content }],\n warnings: check.degradedWarnings,\n };\n },\n renderCmd(cmd: Cmd): RenderResult {\n // OpenCode names the command after the file; only `description` is a known key.\n const content = `${yamlFrontmatter({ description: cmd.meta.description })}\\n${cmd.body}\\n`;\n return {\n files: [{ path: `${BASE}/commands/${cmd.meta.id}.md`, content }],\n warnings: [],\n };\n },\n renderAgents(agents: ReadonlyArray<Agent>): RenderResult {\n const files = [];\n for (const agent of agents) {\n // The file name is the agent name. `mode: all` keeps each persona usable\n // both as a session's primary agent and as a subagent.\n const fm = yamlFrontmatter({\n description: agent.meta.description,\n mode: \"all\",\n });\n const body = buildAgentBody(agent);\n files.push({ path: `${BASE}/agents/${agent.meta.id}.md`, content: `${fm}\\n${body}` });\n }\n return { files, warnings: [] };\n },\n renderMcp(servers: ReadonlyArray<McpServerSpec>): RenderResult {\n if (servers.length === 0) return { files: [], warnings: [] };\n const mcp: Record<string, Record<string, unknown>> = {};\n for (const s of servers) {\n const entry: Record<string, unknown> = {\n type: \"local\",\n command: [s.command, ...s.args],\n enabled: true,\n };\n if (s.env) entry.environment = s.env;\n mcp[s.id] = entry;\n }\n const config = { $schema: \"https://opencode.ai/config.json\", mcp };\n const content = `${JSON.stringify(config, null, 2)}\\n`;\n return { files: [{ path: `${BASE}/opencode.json`, content }], warnings: [] };\n },\n renderStopHook(): RenderResult {\n const content = `import { spawnSync } from \"node:child_process\";\n\nexport const HarnessStopHook = async ({ client, directory }) => ({\n event: async ({ event }) => {\n if (event.type !== \"session.idle\") return;\n const sessionID = event.properties?.sessionID;\n if (!sessionID) return;\n const r = spawnSync(\n \"npx\",\n [\"-y\", \"${STOP_HOOK_PACKAGE}\", \"stop-hook\", \"--engine\", \"opencode\", \"--cwd\", directory],\n { input: JSON.stringify({ sessionID }), encoding: \"utf-8\" },\n );\n const report = (r.stderr ?? \"\").trim();\n if (r.status !== 2 || report === \"\") return;\n // OpenCode defaults to its build agent when a prompt omits agent.\n // Preserve the last conversation persona, excluding compaction summaries.\n const history = await client.session.messages({ path: { id: sessionID } }).catch(() => undefined);\n if (history?.error || !Array.isArray(history?.data)) return;\n const current = history.data.findLast(({ info }) =>\n (info.role === \"user\" || (info.role === \"assistant\" && !info.summary)) &&\n typeof info.agent === \"string\" && info.agent.trim() !== \"\",\n );\n if (!current) return;\n await client.session.prompt({\n path: { id: sessionID },\n body: { agent: current.info.agent, parts: [{ type: \"text\", text: report }] },\n });\n },\n});\n`;\n return {\n files: [{ path: `${BASE}/plugins/harness-stop-hook.ts`, content }],\n warnings: [],\n };\n },\n};\n","import type { Engine } from \"../types.js\";\nimport { claudeCodeRenderer } from \"./claude-code.js\";\nimport { codexCliRenderer } from \"./codex-cli.js\";\nimport type { Renderer } from \"./common.js\";\nimport { opencodeRenderer } from \"./opencode.js\";\n\nexport const RENDERERS: Record<Engine, Renderer> = {\n \"claude-code\": claudeCodeRenderer,\n \"codex-cli\": codexCliRenderer,\n opencode: opencodeRenderer,\n};\n\nexport function getRenderer(engine: Engine): Renderer {\n return RENDERERS[engine];\n}\n\nexport type { Renderer, RenderResult } from \"./common.js\";\n","import { MCP_SERVERS, type McpServerId } from \"./mcp.js\";\nimport type { Agent } from \"./parser/agent.js\";\nimport type { Cmd } from \"./parser/cmd.js\";\nimport type { Skill } from \"./parser/skill.js\";\nimport { buildUmbrellaBody } from \"./renderers/common.js\";\nimport { getRenderer } from \"./renderers/index.js\";\nimport type { Engine, RenderedFile } from \"./types.js\";\n\nexport interface Harness {\n agents: ReadonlyArray<Agent>;\n skills: ReadonlyArray<Skill>;\n cmds: ReadonlyArray<Cmd>;\n}\n\nexport interface HarnessOverrides {\n add?: Partial<Harness>;\n disable?: ReadonlyArray<string>;\n}\n\nexport interface RenderInput {\n harness: Harness;\n engines: ReadonlyArray<Engine>;\n overrides?: HarnessOverrides;\n projectSkills?: ReadonlyArray<Skill>;\n /** True when the target project ships `docs/quality/code-quality.md` (optional pillar). */\n hasQualityDoc?: boolean;\n mcpServers?: ReadonlyArray<McpServerId>;\n /**\n * When true (default), each engine's renderer emits a Stop-hook config\n * that calls `vaubaan-harness stop-hook --engine <engine>` and injects\n * the lint report (with skill suggestions) into the running session\n * whenever the worktree is dirty and lint fails. Pass `false` to skip\n * the hook configs entirely (kill-switch at install time; runtime\n * kill-switch is `HARNESS_LINT_DISABLE=1`).\n */\n withStopHook?: boolean;\n}\n\nexport interface RenderOutput {\n files: RenderedFile[];\n warnings: string[];\n}\n\nexport class RenderError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"RenderError\";\n }\n}\n\nfunction mergeById<T extends { meta: { id: string } }>(\n stock: ReadonlyArray<T>,\n added: ReadonlyArray<T> | undefined,\n): T[] {\n const result = new Map<string, T>();\n for (const item of stock) result.set(item.meta.id, item);\n if (added) {\n for (const item of added) result.set(item.meta.id, item);\n }\n return Array.from(result.values());\n}\n\nfunction removeByIds<T extends { meta: { id: string } }>(\n items: ReadonlyArray<T>,\n disabled: ReadonlyArray<string>,\n): T[] {\n if (disabled.length === 0) return [...items];\n const disabledSet = new Set(disabled);\n return items.filter((item) => !disabledSet.has(item.meta.id));\n}\n\nexport function applyOverrides(harness: Harness, overrides: HarnessOverrides | undefined): Harness {\n const disabled = overrides?.disable ?? [];\n return {\n agents: removeByIds(mergeById(harness.agents, overrides?.add?.agents), disabled),\n skills: removeByIds(mergeById(harness.skills, overrides?.add?.skills), disabled),\n cmds: removeByIds(mergeById(harness.cmds, overrides?.add?.cmds), disabled),\n };\n}\n\n// `@AGENTS.md` is Claude Code's import syntax: the umbrella is really loaded,\n// on every version and provider, and never read twice alongside native\n// AGENTS.md support (cf. ADR engine-native-layouts-1-0).\nconst CLAUDE_MD_STUB = \"@AGENTS.md\\n\";\n\n// OpenCode also scans `.claude/skills/` and `.agents/skills/`. It goes last so\n// its own copy of a skill is dropped when another rendered engine exposes it.\nfunction opencodeLast(engines: ReadonlyArray<Engine>): Engine[] {\n return [...engines].sort((a, b) => Number(a === \"opencode\") - Number(b === \"opencode\"));\n}\n\nexport function render(input: RenderInput): RenderOutput {\n const { engines } = input;\n if (engines.length === 0) {\n throw new RenderError(\"at least one engine must be selected\");\n }\n const base: Harness = input.projectSkills?.length\n ? { ...input.harness, skills: mergeById(input.harness.skills, input.projectSkills) }\n : input.harness;\n const harness = applyOverrides(base, input.overrides);\n if (harness.agents.length === 0) {\n throw new RenderError(\"No agents found in harness\");\n }\n\n const files: RenderedFile[] = [];\n const warnings: string[] = [];\n\n // Track skills + commands successfully exposed on at least one engine,\n // for the engine-neutral umbrella's \"Available skills/commands\" sections.\n const exposedSkills = new Map<string, Skill>();\n const exposedCmds = new Map<string, Cmd>();\n\n for (const engine of opencodeLast(engines)) {\n const renderer = getRenderer(engine);\n\n const renderedSkills: Skill[] = [];\n for (const skill of harness.skills) {\n const result = renderer.renderSkill(skill);\n warnings.push(...result.warnings);\n if (result.files.length > 0) {\n const alreadyVisible = engine === \"opencode\" && exposedSkills.has(skill.meta.id);\n if (!alreadyVisible) files.push(...result.files);\n renderedSkills.push(skill);\n exposedSkills.set(skill.meta.id, skill);\n }\n }\n\n const renderedSkillIds = new Set(renderedSkills.map((s) => s.meta.id));\n const renderedCmds: Cmd[] = [];\n for (const cmd of harness.cmds) {\n if (!renderedSkillIds.has(cmd.meta.invokes_skill)) continue;\n const result = renderer.renderCmd(cmd);\n warnings.push(...result.warnings);\n if (result.files.length > 0) {\n files.push(...result.files);\n renderedCmds.push(cmd);\n exposedCmds.set(cmd.meta.id, cmd);\n }\n }\n\n const agentsResult = renderer.renderAgents(harness.agents);\n warnings.push(...agentsResult.warnings);\n files.push(...agentsResult.files);\n\n if (input.mcpServers && input.mcpServers.length > 0) {\n const specs = input.mcpServers.map((id) => MCP_SERVERS[id]);\n const mcpResult = renderer.renderMcp(specs);\n warnings.push(...mcpResult.warnings);\n files.push(...mcpResult.files);\n }\n\n if (input.withStopHook !== false) {\n const hookResult = renderer.renderStopHook();\n warnings.push(...hookResult.warnings);\n files.push(...hookResult.files);\n }\n }\n\n // Engine-neutral umbrella at the root\n const hasProjectSkills = (input.projectSkills?.length ?? 0) > 0;\n const umbrellaContent = buildUmbrellaBody(harness.agents, {\n availableSkills: Array.from(exposedSkills.values()),\n availableCommands: Array.from(exposedCmds.values()),\n hasProjectSkills,\n hasQualityDoc: input.hasQualityDoc === true,\n });\n files.push({ path: \"AGENTS.md\", content: umbrellaContent });\n\n // CLAUDE.md stub only if claude-code is enabled (Claude Code reads it historically)\n if (engines.includes(\"claude-code\")) {\n files.push({ path: \"CLAUDE.md\", content: CLAUDE_MD_STUB });\n }\n\n return { files, warnings };\n}\n"],"mappings":"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","names":["ENGINE","BASE","ENGINE","BASE"]}
|