k-harness 0.1.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 +21 -0
- package/README.md +105 -0
- package/bin/cli.js +4 -0
- package/harness/agents/planner.md +100 -0
- package/harness/agents/reviewer.md +86 -0
- package/harness/agents/sprint-manager.md +73 -0
- package/harness/backend-rules.md +22 -0
- package/harness/core-rules.md +76 -0
- package/harness/dependency-map.md +44 -0
- package/harness/failure-patterns.md +48 -0
- package/harness/features.md +38 -0
- package/harness/project-brief.md +49 -0
- package/harness/project-state.md +55 -0
- package/harness/skills/feature-breakdown.md +80 -0
- package/harness/skills/impact-analysis.md +63 -0
- package/harness/skills/investigate.md +73 -0
- package/harness/skills/security-checklist.md +49 -0
- package/harness/skills/test-integrity.md +46 -0
- package/harness/testing-rules.md +21 -0
- package/package.json +41 -0
- package/src/init.js +313 -0
package/src/init.js
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const readline = require('node:readline');
|
|
6
|
+
|
|
7
|
+
const HARNESS_DIR = path.join(__dirname, '..', 'harness');
|
|
8
|
+
|
|
9
|
+
// ─── Template reader ─────────────────────────────────────────
|
|
10
|
+
function readTemplate(name) {
|
|
11
|
+
return fs.readFileSync(path.join(HARNESS_DIR, name), 'utf8');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// ─── File writer (mkdir -p + conflict check) ─────────────────
|
|
15
|
+
function writeFile(targetDir, relPath, content, overwrite) {
|
|
16
|
+
const fullPath = path.join(targetDir, relPath);
|
|
17
|
+
if (fs.existsSync(fullPath) && !overwrite) {
|
|
18
|
+
console.log(` ⏭ Skipped (exists): ${relPath}`);
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
22
|
+
fs.writeFileSync(fullPath, content, 'utf8');
|
|
23
|
+
console.log(` ✓ ${relPath}`);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ─── IDE Generators ──────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
function generateVscode(targetDir, overwrite) {
|
|
30
|
+
const coreRules = readTemplate('core-rules.md');
|
|
31
|
+
const testingRules = readTemplate('testing-rules.md');
|
|
32
|
+
const backendRules = readTemplate('backend-rules.md');
|
|
33
|
+
|
|
34
|
+
// Global instructions
|
|
35
|
+
writeFile(targetDir, '.github/copilot-instructions.md', coreRules, overwrite);
|
|
36
|
+
|
|
37
|
+
// File-scoped instructions (add VS Code applyTo frontmatter)
|
|
38
|
+
const testingWithFrontmatter =
|
|
39
|
+
'---\napplyTo: "**/*.test.ts,**/*.test.js,**/*.spec.ts,**/*.spec.js,**/__mocks__/**,**/__tests__/**"\n---\n\n' +
|
|
40
|
+
testingRules;
|
|
41
|
+
writeFile(targetDir, '.vscode/instructions/testing.instructions.md', testingWithFrontmatter, overwrite);
|
|
42
|
+
|
|
43
|
+
const backendWithFrontmatter =
|
|
44
|
+
'---\napplyTo: "src/**/*.ts,src/**/*.js"\n---\n\n' +
|
|
45
|
+
backendRules;
|
|
46
|
+
writeFile(targetDir, '.vscode/instructions/backend.instructions.md', backendWithFrontmatter, overwrite);
|
|
47
|
+
|
|
48
|
+
// Skills (.github/skills — VS Code default search path)
|
|
49
|
+
writeFile(targetDir, '.github/skills/test-integrity/SKILL.md', readTemplate('skills/test-integrity.md'), overwrite);
|
|
50
|
+
writeFile(targetDir, '.github/skills/security-checklist/SKILL.md', readTemplate('skills/security-checklist.md'), overwrite);
|
|
51
|
+
writeFile(targetDir, '.github/skills/investigate/SKILL.md', readTemplate('skills/investigate.md'), overwrite);
|
|
52
|
+
writeFile(targetDir, '.github/skills/impact-analysis/SKILL.md', readTemplate('skills/impact-analysis.md'), overwrite);
|
|
53
|
+
writeFile(targetDir, '.github/skills/feature-breakdown/SKILL.md', readTemplate('skills/feature-breakdown.md'), overwrite);
|
|
54
|
+
|
|
55
|
+
// Agents (.github/agents — VS Code default search path)
|
|
56
|
+
const reviewerContent = readTemplate('agents/reviewer.md');
|
|
57
|
+
const reviewerAgent =
|
|
58
|
+
'---\nname: reviewer\ndescription: "Code review + auto-fix. Validates quality, security, and test integrity before commits."\n---\n\n' +
|
|
59
|
+
reviewerContent;
|
|
60
|
+
writeFile(targetDir, '.github/agents/reviewer.agent.md', reviewerAgent, overwrite);
|
|
61
|
+
|
|
62
|
+
const sprintContent = readTemplate('agents/sprint-manager.md');
|
|
63
|
+
const sprintAgent =
|
|
64
|
+
'---\nname: sprint-manager\ndescription: "Sprint/Story state tracking, next task guidance, scope drift prevention."\n---\n\n' +
|
|
65
|
+
sprintContent;
|
|
66
|
+
writeFile(targetDir, '.github/agents/sprint-manager.agent.md', sprintAgent, overwrite);
|
|
67
|
+
|
|
68
|
+
const plannerContent = readTemplate('agents/planner.md');
|
|
69
|
+
const plannerAgent =
|
|
70
|
+
'---\nname: planner\ndescription: "Feature planning and dependency management. Analyze architecture, break down features, track module relationships."\n---\n\n' +
|
|
71
|
+
plannerContent;
|
|
72
|
+
writeFile(targetDir, '.github/agents/planner.agent.md', plannerAgent, overwrite);
|
|
73
|
+
|
|
74
|
+
// State files
|
|
75
|
+
writeFile(targetDir, 'project-state.md', readTemplate('project-state.md'), overwrite);
|
|
76
|
+
writeFile(targetDir, 'failure-patterns.md', readTemplate('failure-patterns.md'), overwrite);
|
|
77
|
+
writeFile(targetDir, 'dependency-map.md', readTemplate('dependency-map.md'), overwrite);
|
|
78
|
+
writeFile(targetDir, 'features.md', readTemplate('features.md'), overwrite);
|
|
79
|
+
writeFile(targetDir, 'project-brief.md', readTemplate('project-brief.md'), overwrite);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function generateClaude(targetDir, overwrite) {
|
|
83
|
+
// CLAUDE.md — merge core + testing + backend rules
|
|
84
|
+
const merged = [
|
|
85
|
+
readTemplate('core-rules.md'),
|
|
86
|
+
'\n---\n\n',
|
|
87
|
+
readTemplate('testing-rules.md'),
|
|
88
|
+
'\n---\n\n',
|
|
89
|
+
readTemplate('backend-rules.md'),
|
|
90
|
+
].join('');
|
|
91
|
+
writeFile(targetDir, 'CLAUDE.md', merged, overwrite);
|
|
92
|
+
|
|
93
|
+
// Skills (Claude uses same SKILL.md format)
|
|
94
|
+
writeFile(targetDir, '.claude/skills/test-integrity/SKILL.md', readTemplate('skills/test-integrity.md'), overwrite);
|
|
95
|
+
writeFile(targetDir, '.claude/skills/security-checklist/SKILL.md', readTemplate('skills/security-checklist.md'), overwrite);
|
|
96
|
+
writeFile(targetDir, '.claude/skills/investigate/SKILL.md', readTemplate('skills/investigate.md'), overwrite);
|
|
97
|
+
writeFile(targetDir, '.claude/skills/impact-analysis/SKILL.md', readTemplate('skills/impact-analysis.md'), overwrite);
|
|
98
|
+
writeFile(targetDir, '.claude/skills/feature-breakdown/SKILL.md', readTemplate('skills/feature-breakdown.md'), overwrite);
|
|
99
|
+
|
|
100
|
+
// State files
|
|
101
|
+
writeFile(targetDir, 'project-state.md', readTemplate('project-state.md'), overwrite);
|
|
102
|
+
writeFile(targetDir, 'failure-patterns.md', readTemplate('failure-patterns.md'), overwrite);
|
|
103
|
+
writeFile(targetDir, 'dependency-map.md', readTemplate('dependency-map.md'), overwrite);
|
|
104
|
+
writeFile(targetDir, 'features.md', readTemplate('features.md'), overwrite);
|
|
105
|
+
writeFile(targetDir, 'project-brief.md', readTemplate('project-brief.md'), overwrite);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function generateCursor(targetDir, overwrite) {
|
|
109
|
+
// .cursor/rules/*.mdc — each needs frontmatter
|
|
110
|
+
const coreRules = readTemplate('core-rules.md');
|
|
111
|
+
const coreMdc =
|
|
112
|
+
'---\ndescription: Core project rules — Iron Laws, completion protocol, concreteness\nalwaysApply: true\n---\n\n' +
|
|
113
|
+
coreRules;
|
|
114
|
+
writeFile(targetDir, '.cursor/rules/core.mdc', coreMdc, overwrite);
|
|
115
|
+
|
|
116
|
+
const testingRules = readTemplate('testing-rules.md');
|
|
117
|
+
const testingMdc =
|
|
118
|
+
'---\ndescription: Testing rules — mock sync, forbidden patterns\nglobs: "**/*.test.*,**/*.spec.*,**/__mocks__/**,**/__tests__/**"\nalwaysApply: false\n---\n\n' +
|
|
119
|
+
testingRules;
|
|
120
|
+
writeFile(targetDir, '.cursor/rules/testing.mdc', testingMdc, overwrite);
|
|
121
|
+
|
|
122
|
+
const backendRules = readTemplate('backend-rules.md');
|
|
123
|
+
const backendMdc =
|
|
124
|
+
'---\ndescription: Backend code rules — architecture enforcement, type safety\nglobs: "src/**/*.ts,src/**/*.js"\nalwaysApply: false\n---\n\n' +
|
|
125
|
+
backendRules;
|
|
126
|
+
writeFile(targetDir, '.cursor/rules/backend.mdc', backendMdc, overwrite);
|
|
127
|
+
|
|
128
|
+
// Skills as rules
|
|
129
|
+
const skills = ['test-integrity', 'security-checklist', 'investigate', 'impact-analysis', 'feature-breakdown'];
|
|
130
|
+
for (const skill of skills) {
|
|
131
|
+
const content = readTemplate(`skills/${skill}.md`);
|
|
132
|
+
const mdc =
|
|
133
|
+
`---\ndescription: Skill — ${skill}\nalwaysApply: false\n---\n\n` +
|
|
134
|
+
content;
|
|
135
|
+
writeFile(targetDir, `.cursor/rules/${skill}.mdc`, mdc, overwrite);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Agents as rules
|
|
139
|
+
const agents = [
|
|
140
|
+
{ name: 'reviewer', file: 'agents/reviewer.md' },
|
|
141
|
+
{ name: 'sprint-manager', file: 'agents/sprint-manager.md' },
|
|
142
|
+
{ name: 'planner', file: 'agents/planner.md' },
|
|
143
|
+
];
|
|
144
|
+
for (const agent of agents) {
|
|
145
|
+
const content = readTemplate(agent.file);
|
|
146
|
+
const mdc =
|
|
147
|
+
`---\ndescription: Agent — ${agent.name}\nalwaysApply: false\n---\n\n` +
|
|
148
|
+
content;
|
|
149
|
+
writeFile(targetDir, `.cursor/rules/${agent.name}.mdc`, mdc, overwrite);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// State files
|
|
153
|
+
writeFile(targetDir, 'project-state.md', readTemplate('project-state.md'), overwrite);
|
|
154
|
+
writeFile(targetDir, 'failure-patterns.md', readTemplate('failure-patterns.md'), overwrite);
|
|
155
|
+
writeFile(targetDir, 'dependency-map.md', readTemplate('dependency-map.md'), overwrite);
|
|
156
|
+
writeFile(targetDir, 'features.md', readTemplate('features.md'), overwrite);
|
|
157
|
+
writeFile(targetDir, 'project-brief.md', readTemplate('project-brief.md'), overwrite);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function generateCodex(targetDir, overwrite) {
|
|
161
|
+
// AGENTS.md — merge core + testing + backend rules
|
|
162
|
+
const merged = [
|
|
163
|
+
readTemplate('core-rules.md'),
|
|
164
|
+
'\n---\n\n',
|
|
165
|
+
readTemplate('testing-rules.md'),
|
|
166
|
+
'\n---\n\n',
|
|
167
|
+
readTemplate('backend-rules.md'),
|
|
168
|
+
].join('');
|
|
169
|
+
writeFile(targetDir, 'AGENTS.md', merged, overwrite);
|
|
170
|
+
|
|
171
|
+
// Skills (Codex uses .agents/skills/ format)
|
|
172
|
+
writeFile(targetDir, '.agents/skills/test-integrity/SKILL.md', readTemplate('skills/test-integrity.md'), overwrite);
|
|
173
|
+
writeFile(targetDir, '.agents/skills/security-checklist/SKILL.md', readTemplate('skills/security-checklist.md'), overwrite);
|
|
174
|
+
writeFile(targetDir, '.agents/skills/investigate/SKILL.md', readTemplate('skills/investigate.md'), overwrite);
|
|
175
|
+
writeFile(targetDir, '.agents/skills/impact-analysis/SKILL.md', readTemplate('skills/impact-analysis.md'), overwrite);
|
|
176
|
+
writeFile(targetDir, '.agents/skills/feature-breakdown/SKILL.md', readTemplate('skills/feature-breakdown.md'), overwrite);
|
|
177
|
+
|
|
178
|
+
// State files
|
|
179
|
+
writeFile(targetDir, 'project-state.md', readTemplate('project-state.md'), overwrite);
|
|
180
|
+
writeFile(targetDir, 'failure-patterns.md', readTemplate('failure-patterns.md'), overwrite);
|
|
181
|
+
writeFile(targetDir, 'dependency-map.md', readTemplate('dependency-map.md'), overwrite);
|
|
182
|
+
writeFile(targetDir, 'features.md', readTemplate('features.md'), overwrite);
|
|
183
|
+
writeFile(targetDir, 'project-brief.md', readTemplate('project-brief.md'), overwrite);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function generateWindsurf(targetDir, overwrite) {
|
|
187
|
+
// .windsurfrules — everything in one file
|
|
188
|
+
const sections = [
|
|
189
|
+
readTemplate('core-rules.md'),
|
|
190
|
+
readTemplate('testing-rules.md'),
|
|
191
|
+
readTemplate('backend-rules.md'),
|
|
192
|
+
'---\n\n# Skills\n\n',
|
|
193
|
+
readTemplate('skills/test-integrity.md'),
|
|
194
|
+
readTemplate('skills/security-checklist.md'),
|
|
195
|
+
readTemplate('skills/investigate.md'),
|
|
196
|
+
readTemplate('skills/impact-analysis.md'),
|
|
197
|
+
readTemplate('skills/feature-breakdown.md'),
|
|
198
|
+
'---\n\n# Agents\n\n',
|
|
199
|
+
readTemplate('agents/reviewer.md'),
|
|
200
|
+
readTemplate('agents/sprint-manager.md'),
|
|
201
|
+
readTemplate('agents/planner.md'),
|
|
202
|
+
];
|
|
203
|
+
writeFile(targetDir, '.windsurfrules', sections.join('\n\n---\n\n'), overwrite);
|
|
204
|
+
|
|
205
|
+
// State files
|
|
206
|
+
writeFile(targetDir, 'project-state.md', readTemplate('project-state.md'), overwrite);
|
|
207
|
+
writeFile(targetDir, 'failure-patterns.md', readTemplate('failure-patterns.md'), overwrite);
|
|
208
|
+
writeFile(targetDir, 'dependency-map.md', readTemplate('dependency-map.md'), overwrite);
|
|
209
|
+
writeFile(targetDir, 'features.md', readTemplate('features.md'), overwrite);
|
|
210
|
+
writeFile(targetDir, 'project-brief.md', readTemplate('project-brief.md'), overwrite);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ─── IDE registry ────────────────────────────────────────────
|
|
214
|
+
const GENERATORS = {
|
|
215
|
+
vscode: { name: 'VS Code Copilot', fn: generateVscode },
|
|
216
|
+
claude: { name: 'Claude Code', fn: generateClaude },
|
|
217
|
+
cursor: { name: 'Cursor', fn: generateCursor },
|
|
218
|
+
codex: { name: 'Codex (OpenAI)', fn: generateCodex },
|
|
219
|
+
windsurf: { name: 'Windsurf', fn: generateWindsurf },
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// ─── Interactive prompt ──────────────────────────────────────
|
|
223
|
+
function askQuestion(query) {
|
|
224
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
225
|
+
return new Promise((resolve) => {
|
|
226
|
+
rl.question(query, (answer) => {
|
|
227
|
+
rl.close();
|
|
228
|
+
resolve(answer.trim());
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async function promptIde() {
|
|
234
|
+
const keys = Object.keys(GENERATORS);
|
|
235
|
+
console.log('\n Select your IDE:\n');
|
|
236
|
+
keys.forEach((key, i) => {
|
|
237
|
+
console.log(` ${i + 1}. ${GENERATORS[key].name}`);
|
|
238
|
+
});
|
|
239
|
+
console.log();
|
|
240
|
+
|
|
241
|
+
const answer = await askQuestion(' Choice (1-5): ');
|
|
242
|
+
const idx = parseInt(answer, 10) - 1;
|
|
243
|
+
if (idx < 0 || idx >= keys.length || isNaN(idx)) {
|
|
244
|
+
console.error(' Invalid choice.');
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
247
|
+
return keys[idx];
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ─── CLI entry ───────────────────────────────────────────────
|
|
251
|
+
function showHelp() {
|
|
252
|
+
console.log(`
|
|
253
|
+
K-Harness — LLM Development Harness
|
|
254
|
+
|
|
255
|
+
Usage:
|
|
256
|
+
npx k-harness init [options]
|
|
257
|
+
|
|
258
|
+
Options:
|
|
259
|
+
--ide <name> IDE target: vscode, claude, cursor, codex, windsurf
|
|
260
|
+
--dir <path> Target directory (default: current directory)
|
|
261
|
+
--overwrite Overwrite existing files
|
|
262
|
+
--help Show this help
|
|
263
|
+
|
|
264
|
+
Examples:
|
|
265
|
+
npx k-harness init
|
|
266
|
+
npx k-harness init --ide vscode
|
|
267
|
+
npx k-harness init --ide claude --dir ./my-project
|
|
268
|
+
`);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function parseArgs(argv) {
|
|
272
|
+
const args = { command: null, ide: null, dir: process.cwd(), overwrite: false, help: false };
|
|
273
|
+
for (let i = 0; i < argv.length; i++) {
|
|
274
|
+
const arg = argv[i];
|
|
275
|
+
if (arg === 'init') args.command = 'init';
|
|
276
|
+
else if (arg === '--ide' && argv[i + 1]) { args.ide = argv[++i]; }
|
|
277
|
+
else if (arg === '--dir' && argv[i + 1]) { args.dir = path.resolve(argv[++i]); }
|
|
278
|
+
else if (arg === '--overwrite') args.overwrite = true;
|
|
279
|
+
else if (arg === '--help' || arg === '-h') args.help = true;
|
|
280
|
+
}
|
|
281
|
+
return args;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async function run(argv) {
|
|
285
|
+
const args = parseArgs(argv);
|
|
286
|
+
|
|
287
|
+
if (args.help || !args.command) {
|
|
288
|
+
showHelp();
|
|
289
|
+
process.exit(args.help ? 0 : 1);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (args.command === 'init') {
|
|
293
|
+
console.log('\n K-Harness — LLM Development Harness\n');
|
|
294
|
+
|
|
295
|
+
// Determine IDE
|
|
296
|
+
let ide = args.ide;
|
|
297
|
+
if (ide && !GENERATORS[ide]) {
|
|
298
|
+
console.error(` Unknown IDE: ${ide}`);
|
|
299
|
+
console.error(` Available: ${Object.keys(GENERATORS).join(', ')}`);
|
|
300
|
+
process.exit(1);
|
|
301
|
+
}
|
|
302
|
+
if (!ide) {
|
|
303
|
+
ide = await promptIde();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const gen = GENERATORS[ide];
|
|
307
|
+
console.log(`\n Installing for ${gen.name}...\n`);
|
|
308
|
+
gen.fn(args.dir, args.overwrite);
|
|
309
|
+
console.log(`\n Done! Edit project-state.md to set up your first sprint.\n`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
module.exports = { run };
|