dev-harness-cli 1.0.5 → 1.2.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/README.md +158 -230
- package/cli/commands/checkpoint.mjs +2 -2
- package/cli/commands/config.mjs +12 -12
- package/cli/commands/contract.mjs +10 -10
- package/cli/commands/detect-tool.mjs +5 -4
- package/cli/commands/init.mjs +10 -10
- package/cli/commands/learn.mjs +3 -3
- package/cli/commands/pause.mjs +1 -1
- package/cli/commands/phase.mjs +6 -6
- package/cli/commands/resume.mjs +3 -3
- package/cli/commands/rollback.mjs +9 -9
- package/cli/commands/set-mode.mjs +5 -5
- package/cli/commands/status.mjs +9 -9
- package/cli/commands/validate.mjs +7 -7
- package/cli/commands/worktree.mjs +6 -6
- package/cli/{harness-dev.mjs → dev-harness.mjs} +2 -2
- package/cli/lib/config-registry.mjs +2 -2
- package/cli/lib/contract.mjs +3 -3
- package/cli/lib/gates.mjs +11 -9
- package/cli/lib/help.mjs +28 -28
- package/cli/lib/paths.mjs +87 -8
- package/cli/lib/ralph-inner.mjs +2 -2
- package/cli/lib/ralph-outer.mjs +3 -3
- package/cli/lib/ralph-output.mjs +1 -1
- package/cli/lib/scaffold.mjs +18 -18
- package/cli/lib/state.mjs +1 -1
- package/cli/lib/templates.mjs +38 -1
- package/package.json +8 -4
- package/schema/feature-list.schema.json +63 -0
- package/templates/AGENTS.md +19 -15
- package/templates/ci/github-actions.yml +2 -2
- package/templates/ci/gitlab-ci.yml +2 -2
- package/templates/docs/agents/generator.md +1 -1
- package/templates/docs/agents/planner.md +1 -1
- package/templates/docs/agents/simplifier.md +1 -1
- package/templates/docs/phases/build.md +3 -3
- package/templates/docs/phases/define.md +3 -3
- package/templates/docs/phases/plan.md +3 -3
- package/templates/docs/phases/review.md +2 -2
- package/templates/docs/phases/ship.md +3 -3
- package/templates/docs/phases/simplify.md +3 -3
- package/templates/docs/phases/verify.md +2 -2
- package/templates/harness-config.json +42 -0
- package/templates/init.ps1 +1 -1
- package/templates/progress.md +18 -0
package/cli/lib/help.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Help text builder — centralized to keep all formatting in one place.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
const USAGE = `Usage: harness
|
|
5
|
+
const USAGE = `Usage: dev-harness <command> [options]
|
|
6
6
|
|
|
7
7
|
Pipeline commands:
|
|
8
8
|
init Scaffold full harness in current directory
|
|
@@ -53,14 +53,14 @@ Exit codes:
|
|
|
53
53
|
2 Usage error (bad arguments)
|
|
54
54
|
3 Internal error`;
|
|
55
55
|
|
|
56
|
-
const VERSION = '1.0
|
|
56
|
+
const VERSION = '1.2.0';
|
|
57
57
|
|
|
58
58
|
// Help text for JSON output
|
|
59
59
|
function buildJsonHelp() {
|
|
60
60
|
return {
|
|
61
61
|
help: true,
|
|
62
62
|
version: VERSION,
|
|
63
|
-
usage: 'harness
|
|
63
|
+
usage: 'dev-harness <command> [options]',
|
|
64
64
|
commands: {
|
|
65
65
|
init: 'Scaffold full harness in current directory',
|
|
66
66
|
status: 'Show current phase + gate state + detected stack',
|
|
@@ -111,12 +111,12 @@ export function versionText(json = false) {
|
|
|
111
111
|
if (json) {
|
|
112
112
|
return JSON.stringify({ version: VERSION });
|
|
113
113
|
}
|
|
114
|
-
return `harness
|
|
114
|
+
return `dev-harness v${VERSION}`;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
// Per-command help text (for `harness
|
|
117
|
+
// Per-command help text (for `dev-harness <command> --help`).
|
|
118
118
|
const COMMAND_HELP = {
|
|
119
|
-
init: `Usage: harness
|
|
119
|
+
init: `Usage: dev-harness init [--stack <name>] [--target <dir>] [--force] [--no-git] [--json]
|
|
120
120
|
|
|
121
121
|
Scaffold a full harness in the target directory:
|
|
122
122
|
- Detects stack (or use --stack)
|
|
@@ -131,7 +131,7 @@ Flags:
|
|
|
131
131
|
--no-git Skip git init
|
|
132
132
|
--json JSON output`,
|
|
133
133
|
|
|
134
|
-
status: `Usage: harness
|
|
134
|
+
status: `Usage: dev-harness status [--target <dir>] [--json]
|
|
135
135
|
|
|
136
136
|
Show current phase, gate state, detected stack, recent lessons, and next action.
|
|
137
137
|
|
|
@@ -139,7 +139,7 @@ Flags:
|
|
|
139
139
|
--target <dir> Project directory (default: cwd)
|
|
140
140
|
--json JSON output`,
|
|
141
141
|
|
|
142
|
-
phase: `Usage: harness
|
|
142
|
+
phase: `Usage: dev-harness phase <name> [--target <dir>] [--git-ops] [--json]
|
|
143
143
|
|
|
144
144
|
Invoke a phase. Valid phases: define, plan, build, verify, simplify, review, ship.
|
|
145
145
|
|
|
@@ -148,7 +148,7 @@ Flags:
|
|
|
148
148
|
--git-ops Execute git reset --hard + clean on retry (fresh context)
|
|
149
149
|
--json JSON output`,
|
|
150
150
|
|
|
151
|
-
validate: `Usage: harness
|
|
151
|
+
validate: `Usage: dev-harness validate [--phase <name>] [--feature <id> --task <id>] [--target <dir>] [--json]
|
|
152
152
|
|
|
153
153
|
Run gate checks for the current (or specified) phase.
|
|
154
154
|
|
|
@@ -159,39 +159,39 @@ Flags:
|
|
|
159
159
|
--target <dir> Project directory (default: cwd)
|
|
160
160
|
--json JSON output`,
|
|
161
161
|
|
|
162
|
-
'set-mode': `Usage: harness
|
|
162
|
+
'set-mode': `Usage: dev-harness set-mode <copilot|autopilot> [--target <dir>] [--json]
|
|
163
163
|
|
|
164
164
|
Switch execution mode. Autopilot requires DEFINE phase or later.`,
|
|
165
165
|
|
|
166
|
-
config: `Usage: harness
|
|
167
|
-
harness
|
|
168
|
-
harness
|
|
166
|
+
config: `Usage: dev-harness config list [--target <dir>] [--json]
|
|
167
|
+
dev-harness config get [key] [--target <dir>] [--json]
|
|
168
|
+
dev-harness config set <key> <value> [--target <dir>] [--json]
|
|
169
169
|
|
|
170
170
|
List all parameters with descriptions, or get/set values via dot-notation.
|
|
171
171
|
Use 'config list' to see all configurable parameters, their current values,
|
|
172
172
|
types, allowed options, and descriptions.
|
|
173
173
|
|
|
174
174
|
Examples:
|
|
175
|
-
harness
|
|
176
|
-
harness
|
|
177
|
-
harness
|
|
178
|
-
harness
|
|
179
|
-
harness
|
|
180
|
-
harness
|
|
175
|
+
dev-harness config list
|
|
176
|
+
dev-harness config list --json
|
|
177
|
+
dev-harness config get gates.enabled
|
|
178
|
+
dev-harness config set gates.enabled true
|
|
179
|
+
dev-harness config set mode autopilot
|
|
180
|
+
dev-harness config set maxRetries 5`,
|
|
181
181
|
|
|
182
|
-
pause: `Usage: harness
|
|
182
|
+
pause: `Usage: dev-harness pause [--target <dir>] [--json]
|
|
183
183
|
|
|
184
184
|
Pause autopilot execution. Autopilot stops after the current phase gate.`,
|
|
185
185
|
|
|
186
|
-
resume: `Usage: harness
|
|
186
|
+
resume: `Usage: dev-harness resume [--target <dir>] [--json]
|
|
187
187
|
|
|
188
188
|
Resume autopilot execution.`,
|
|
189
189
|
|
|
190
|
-
learn: `Usage: harness
|
|
190
|
+
learn: `Usage: dev-harness learn "<message>" [--target <dir>] [--json]
|
|
191
191
|
|
|
192
192
|
Append a lesson to the Lessons section of progress.md.`,
|
|
193
193
|
|
|
194
|
-
contract: `Usage: harness
|
|
194
|
+
contract: `Usage: dev-harness contract <subcommand> [options] [--target <dir>] [--json]
|
|
195
195
|
|
|
196
196
|
Subcommands:
|
|
197
197
|
propose --scope "..." [--exclusions "..."] [--criteria "..."] Generator proposes
|
|
@@ -199,7 +199,7 @@ Subcommands:
|
|
|
199
199
|
status Show contract state
|
|
200
200
|
escalate [--reason "..."] Human adjudication`,
|
|
201
201
|
|
|
202
|
-
worktree: `Usage: harness
|
|
202
|
+
worktree: `Usage: dev-harness worktree <subcommand> [options] [--target <dir>] [--json]
|
|
203
203
|
|
|
204
204
|
Subcommands:
|
|
205
205
|
create <name> Create isolated worktree for a feature
|
|
@@ -207,25 +207,25 @@ Subcommands:
|
|
|
207
207
|
prune Remove orphaned worktrees
|
|
208
208
|
remove <name> Clean up worktree (optionally merge branch)`,
|
|
209
209
|
|
|
210
|
-
rollback: `Usage: harness
|
|
210
|
+
rollback: `Usage: dev-harness rollback <subcommand> [checkpoint] [--target <dir>] [--json]
|
|
211
211
|
|
|
212
212
|
Subcommands:
|
|
213
213
|
list Show available checkpoints
|
|
214
214
|
to <tag> Restore state to a checkpoint
|
|
215
215
|
branch <tag> Branch off a good iteration`,
|
|
216
216
|
|
|
217
|
-
checkpoint: `Usage: harness
|
|
217
|
+
checkpoint: `Usage: dev-harness checkpoint create <label> [--force] [--target <dir>] [--json]
|
|
218
218
|
|
|
219
219
|
Create a manual checkpoint tag (manual/<label>). Requires clean working tree
|
|
220
220
|
unless --force is given.`,
|
|
221
221
|
|
|
222
|
-
'detect-tool': `Usage: harness
|
|
222
|
+
'detect-tool': `Usage: dev-harness detect-tool [--target <dir>] [--json]
|
|
223
223
|
|
|
224
224
|
Scan the project for agent-tool files (CLAUDE.md, .cursorrules, AGENTS.md, etc.)
|
|
225
225
|
and report which coding agents are available. Recommends a tool based on config
|
|
226
226
|
and detected files.`,
|
|
227
227
|
|
|
228
|
-
help: `Usage: harness
|
|
228
|
+
help: `Usage: dev-harness help
|
|
229
229
|
|
|
230
230
|
Show the global help message. Alias for --help.`,
|
|
231
231
|
};
|
package/cli/lib/paths.mjs
CHANGED
|
@@ -37,39 +37,118 @@ export const FEATURE_LIST_SCHEMA_PATH = resolve(SCHEMA_DIR, 'feature-list.schema
|
|
|
37
37
|
export const STACKS_SCHEMA_PATH = resolve(LIB_DIR, 'schemas', 'stacks.json');
|
|
38
38
|
|
|
39
39
|
// ── Project-relative paths (target project, not this CLI) ────────────────────
|
|
40
|
+
// All harness-managed files live under harness/ with subfolder grouping:
|
|
41
|
+
// harness/ — config, progress, contract, rubric, handoff, checklist
|
|
42
|
+
// harness/features/ — feature list + schema
|
|
43
|
+
// harness/docs/ — architecture, constraints, decisions, agent docs, phase docs
|
|
44
|
+
// harness/ci/ — CI/CD templates
|
|
45
|
+
// harness/scripts/ — init scripts
|
|
46
|
+
// AGENTS.md stays in root (agent tools expect it there).
|
|
40
47
|
|
|
41
48
|
/**
|
|
42
|
-
*
|
|
49
|
+
* Harness root directory within a target project.
|
|
50
|
+
* @param {string} targetDir
|
|
51
|
+
* @returns {string}
|
|
52
|
+
*/
|
|
53
|
+
export function HARNESS_DIR(targetDir) {
|
|
54
|
+
return resolve(targetDir, 'harness');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Path to a project's config.json (harness/config.json).
|
|
43
59
|
* @param {string} targetDir
|
|
44
60
|
* @returns {string}
|
|
45
61
|
*/
|
|
46
62
|
export function CONFIG_PATH(targetDir) {
|
|
47
|
-
return resolve(targetDir, '
|
|
63
|
+
return resolve(HARNESS_DIR(targetDir), 'config.json');
|
|
48
64
|
}
|
|
49
65
|
|
|
50
66
|
/**
|
|
51
|
-
* Path to a project's
|
|
67
|
+
* Path to a project's feature-list.json (harness/features/feature-list.json).
|
|
52
68
|
* @param {string} targetDir
|
|
53
69
|
* @returns {string}
|
|
54
70
|
*/
|
|
55
71
|
export function FEATURE_LIST_PATH(targetDir) {
|
|
56
|
-
return resolve(targetDir, '
|
|
72
|
+
return resolve(HARNESS_DIR(targetDir), 'features', 'feature-list.json');
|
|
57
73
|
}
|
|
58
74
|
|
|
59
75
|
/**
|
|
60
|
-
* Path to a project's sprint-contract.md.
|
|
76
|
+
* Path to a project's sprint-contract.md (harness/sprint-contract.md).
|
|
61
77
|
* @param {string} targetDir
|
|
62
78
|
* @returns {string}
|
|
63
79
|
*/
|
|
64
80
|
export function CONTRACT_PATH(targetDir) {
|
|
65
|
-
return resolve(targetDir, 'sprint-contract.md');
|
|
81
|
+
return resolve(HARNESS_DIR(targetDir), 'sprint-contract.md');
|
|
66
82
|
}
|
|
67
83
|
|
|
68
84
|
/**
|
|
69
|
-
* Path to a project's progress.md.
|
|
85
|
+
* Path to a project's progress.md (harness/progress.md).
|
|
70
86
|
* @param {string} targetDir
|
|
71
87
|
* @returns {string}
|
|
72
88
|
*/
|
|
73
89
|
export function PROGRESS_PATH(targetDir) {
|
|
74
|
-
return resolve(targetDir, 'progress.md');
|
|
90
|
+
return resolve(HARNESS_DIR(targetDir), 'progress.md');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Path to a project's evaluator-rubric.md (harness/evaluator-rubric.md).
|
|
95
|
+
* @param {string} targetDir
|
|
96
|
+
* @returns {string}
|
|
97
|
+
*/
|
|
98
|
+
export function RUBRIC_PATH(targetDir) {
|
|
99
|
+
return resolve(HARNESS_DIR(targetDir), 'evaluator-rubric.md');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Path to a project's session-handoff.md (harness/session-handoff.md).
|
|
104
|
+
* @param {string} targetDir
|
|
105
|
+
* @returns {string}
|
|
106
|
+
*/
|
|
107
|
+
export function HANDOFF_PATH(targetDir) {
|
|
108
|
+
return resolve(HARNESS_DIR(targetDir), 'session-handoff.md');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Path to a project's clean-state-checklist.md (harness/clean-state-checklist.md).
|
|
113
|
+
* @param {string} targetDir
|
|
114
|
+
* @returns {string}
|
|
115
|
+
*/
|
|
116
|
+
export function CHECKLIST_PATH(targetDir) {
|
|
117
|
+
return resolve(HARNESS_DIR(targetDir), 'clean-state-checklist.md');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Path to a project's ARCHITECTURE.md (harness/docs/ARCHITECTURE.md).
|
|
122
|
+
* @param {string} targetDir
|
|
123
|
+
* @returns {string}
|
|
124
|
+
*/
|
|
125
|
+
export function ARCHITECTURE_PATH(targetDir) {
|
|
126
|
+
return resolve(HARNESS_DIR(targetDir), 'docs', 'ARCHITECTURE.md');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Path to a project's CONSTRAINTS.md (harness/docs/CONSTRAINTS.md).
|
|
131
|
+
* @param {string} targetDir
|
|
132
|
+
* @returns {string}
|
|
133
|
+
*/
|
|
134
|
+
export function CONSTRAINTS_PATH(targetDir) {
|
|
135
|
+
return resolve(HARNESS_DIR(targetDir), 'docs', 'CONSTRAINTS.md');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Path to a project's DECISIONS.md (harness/docs/DECISIONS.md).
|
|
140
|
+
* @param {string} targetDir
|
|
141
|
+
* @returns {string}
|
|
142
|
+
*/
|
|
143
|
+
export function DECISIONS_PATH(targetDir) {
|
|
144
|
+
return resolve(HARNESS_DIR(targetDir), 'docs', 'DECISIONS.md');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Path to a project's AGENTS.md (stays in root — agent tools expect it there).
|
|
149
|
+
* @param {string} targetDir
|
|
150
|
+
* @returns {string}
|
|
151
|
+
*/
|
|
152
|
+
export function AGENTS_PATH(targetDir) {
|
|
153
|
+
return resolve(targetDir, 'AGENTS.md');
|
|
75
154
|
}
|
package/cli/lib/ralph-inner.mjs
CHANGED
|
@@ -259,7 +259,7 @@ export function runPhase(targetDir, phase, options = {}) {
|
|
|
259
259
|
// Human output
|
|
260
260
|
process.stdout.write(output);
|
|
261
261
|
process.stdout.write(`\n═══════════════════════════════════════\n`);
|
|
262
|
-
process.stdout.write(`Run: harness
|
|
262
|
+
process.stdout.write(`Run: dev-harness validate --feature ${feature.id} --task ${task.id}\n`);
|
|
263
263
|
process.stdout.write(`═══════════════════════════════════════\n`);
|
|
264
264
|
|
|
265
265
|
return {
|
|
@@ -298,7 +298,7 @@ export function runPhase(targetDir, phase, options = {}) {
|
|
|
298
298
|
// Human output
|
|
299
299
|
process.stdout.write(output);
|
|
300
300
|
process.stdout.write(`\n═══════════════════════════════════════\n`);
|
|
301
|
-
process.stdout.write(`Run: harness
|
|
301
|
+
process.stdout.write(`Run: dev-harness validate\n`);
|
|
302
302
|
process.stdout.write(`═══════════════════════════════════════\n`);
|
|
303
303
|
|
|
304
304
|
return {
|
package/cli/lib/ralph-outer.mjs
CHANGED
|
@@ -98,7 +98,7 @@ export function continuePipeline(targetDir, completedPhase, options = {}) {
|
|
|
98
98
|
|
|
99
99
|
if (mode === 'copilot') {
|
|
100
100
|
// Copilot: print instructions for next phase
|
|
101
|
-
const msg = `${completedPhase.toUpperCase()} complete. Next: harness
|
|
101
|
+
const msg = `${completedPhase.toUpperCase()} complete. Next: dev-harness phase ${nextPhase}`;
|
|
102
102
|
if (json) {
|
|
103
103
|
return {
|
|
104
104
|
ok: true,
|
|
@@ -128,7 +128,7 @@ export function continuePipeline(targetDir, completedPhase, options = {}) {
|
|
|
128
128
|
|
|
129
129
|
// Re-check pause before auto-advancing (user may have paused during phase execution)
|
|
130
130
|
if (config.paused) {
|
|
131
|
-
const msg = `Autopilot paused after "${completedPhase}". Run: harness
|
|
131
|
+
const msg = `Autopilot paused after "${completedPhase}". Run: dev-harness resume`;
|
|
132
132
|
if (verbose && !json) {
|
|
133
133
|
process.stdout.write(`\n ⏸ ${msg}\n`);
|
|
134
134
|
}
|
|
@@ -205,7 +205,7 @@ export function continuePipeline(targetDir, completedPhase, options = {}) {
|
|
|
205
205
|
* Run the full autopilot pipeline from current state through SHIP.
|
|
206
206
|
*
|
|
207
207
|
* This is a convenience wrapper — normally autopilot is triggered
|
|
208
|
-
* by calling `harness
|
|
208
|
+
* by calling `dev-harness phase <name>` while in autopilot mode.
|
|
209
209
|
*
|
|
210
210
|
* @param {string} targetDir
|
|
211
211
|
* @param {object} [options]
|
package/cli/lib/ralph-output.mjs
CHANGED
|
@@ -171,7 +171,7 @@ export function buildDeliverableRetryOutput(phase, mode, maxRetries, resetOnRetr
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
out += `\n`;
|
|
174
|
-
out += `When done, run: harness
|
|
174
|
+
out += `When done, run: dev-harness validate\n`;
|
|
175
175
|
if (resetOnRetry) {out += `Git reset on retry: enabled\n`;}
|
|
176
176
|
if (autoCommit) {out += `Auto-commit: enabled\n`;}
|
|
177
177
|
return out;
|
package/cli/lib/scaffold.mjs
CHANGED
|
@@ -212,12 +212,13 @@ work/
|
|
|
212
212
|
// ── Extra scaffolding files (not covered by templates) ───────────────────────
|
|
213
213
|
|
|
214
214
|
/**
|
|
215
|
-
* Inline content for files beyond the
|
|
216
|
-
* Key is relative output path, value is file content.
|
|
215
|
+
* Inline content for files beyond the template-based ones.
|
|
216
|
+
* Key is relative output path (under harness/), value is file content.
|
|
217
|
+
* All harness-managed files go under harness/ with subfolder grouping.
|
|
217
218
|
*/
|
|
218
219
|
export function getExtraFiles(stack) {
|
|
219
220
|
return {
|
|
220
|
-
'
|
|
221
|
+
'harness/features/feature-list.json': JSON.stringify({
|
|
221
222
|
version: '0.1',
|
|
222
223
|
features: [
|
|
223
224
|
{
|
|
@@ -232,7 +233,7 @@ export function getExtraFiles(stack) {
|
|
|
232
233
|
],
|
|
233
234
|
}, null, 2) + '\n',
|
|
234
235
|
|
|
235
|
-
'feature-list.schema.json': JSON.stringify({
|
|
236
|
+
'harness/features/feature-list.schema.json': JSON.stringify({
|
|
236
237
|
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
237
238
|
title: 'Feature List',
|
|
238
239
|
type: 'object',
|
|
@@ -266,7 +267,7 @@ export function getExtraFiles(stack) {
|
|
|
266
267
|
},
|
|
267
268
|
}, null, 2) + '\n',
|
|
268
269
|
|
|
269
|
-
'session-handoff.md': `# Session Handoff
|
|
270
|
+
'harness/session-handoff.md': `# Session Handoff
|
|
270
271
|
|
|
271
272
|
## Context
|
|
272
273
|
|
|
@@ -296,7 +297,7 @@ export function getExtraFiles(stack) {
|
|
|
296
297
|
| ... | ... |
|
|
297
298
|
`,
|
|
298
299
|
|
|
299
|
-
'clean-state-checklist.md': `# Clean State Checklist
|
|
300
|
+
'harness/clean-state-checklist.md': `# Clean State Checklist
|
|
300
301
|
|
|
301
302
|
Run this before starting any phase to ensure deterministic state.
|
|
302
303
|
|
|
@@ -308,10 +309,10 @@ Run this before starting any phase to ensure deterministic state.
|
|
|
308
309
|
|
|
309
310
|
## Harness
|
|
310
311
|
|
|
311
|
-
- [ ] \`harness
|
|
312
|
+
- [ ] \`harness/config.json\` exists and valid
|
|
312
313
|
- [ ] Current phase matches what we're about to run
|
|
313
|
-
- [ ] \`progress.md\` has latest Session State
|
|
314
|
-
- [ ] \`
|
|
314
|
+
- [ ] \`harness/progress.md\` has latest Session State
|
|
315
|
+
- [ ] \`harness/features/feature-list.json\` up-to-date
|
|
315
316
|
|
|
316
317
|
## Environment
|
|
317
318
|
|
|
@@ -320,7 +321,7 @@ Run this before starting any phase to ensure deterministic state.
|
|
|
320
321
|
- [ ] No stale background processes
|
|
321
322
|
`,
|
|
322
323
|
|
|
323
|
-
'ARCHITECTURE.md': `# Architecture
|
|
324
|
+
'harness/docs/ARCHITECTURE.md': `# Architecture
|
|
324
325
|
|
|
325
326
|
## Module Structure
|
|
326
327
|
|
|
@@ -330,7 +331,7 @@ src/
|
|
|
330
331
|
\`\`\`
|
|
331
332
|
`,
|
|
332
333
|
|
|
333
|
-
'CONSTRAINTS.md': `# Constraints
|
|
334
|
+
'harness/docs/CONSTRAINTS.md': `# Constraints
|
|
334
335
|
|
|
335
336
|
## Technical
|
|
336
337
|
|
|
@@ -351,7 +352,7 @@ src/
|
|
|
351
352
|
- Fail fast, fail loud
|
|
352
353
|
`,
|
|
353
354
|
|
|
354
|
-
'DECISIONS.md': `# Decisions
|
|
355
|
+
'harness/docs/DECISIONS.md': `# Decisions
|
|
355
356
|
|
|
356
357
|
<!-- Record architectural and design decisions here. Use the format below. -->
|
|
357
358
|
|
|
@@ -372,7 +373,7 @@ src/
|
|
|
372
373
|
| | | |
|
|
373
374
|
`,
|
|
374
375
|
|
|
375
|
-
'docs/api-patterns.md': `# API Patterns
|
|
376
|
+
'harness/docs/api-patterns.md': `# API Patterns
|
|
376
377
|
|
|
377
378
|
## Conventions
|
|
378
379
|
|
|
@@ -417,11 +418,10 @@ export function getVersionFileContent(stack) {
|
|
|
417
418
|
* Return .gitignore content for the given stack.
|
|
418
419
|
*/
|
|
419
420
|
export function getGitignoreContent(stack) {
|
|
420
|
-
return `# Harness
|
|
421
|
-
harness
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
progress.md
|
|
421
|
+
return `# Harness runtime state (regenerated by dev-harness)
|
|
422
|
+
harness/config.json
|
|
423
|
+
harness/features/feature-list.json
|
|
424
|
+
harness/progress.md
|
|
425
425
|
|
|
426
426
|
${GITIGNORE_PATTERNS[stack] || GITIGNORE_PATTERNS.generic}
|
|
427
427
|
# OS
|
package/cli/lib/state.mjs
CHANGED
package/cli/lib/templates.mjs
CHANGED
|
@@ -111,6 +111,42 @@ export function discoverTemplates() {
|
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Map a template's relative path to its output path under harness/.
|
|
116
|
+
* Templates that are harness-managed go into harness/ subfolders.
|
|
117
|
+
* AGENTS.md stays in project root (agent tools expect it there).
|
|
118
|
+
*
|
|
119
|
+
* Mapping:
|
|
120
|
+
* AGENTS.md → AGENTS.md (root)
|
|
121
|
+
* harness-config.json → harness/config.json
|
|
122
|
+
* progress.md → harness/progress.md
|
|
123
|
+
* sprint-contract.md → harness/sprint-contract.md
|
|
124
|
+
* evaluator-rubric.md → harness/evaluator-rubric.md
|
|
125
|
+
* init.sh / init.ps1 → harness/scripts/init.sh
|
|
126
|
+
* ci/* → harness/ci/*
|
|
127
|
+
* docs/* → harness/docs/*
|
|
128
|
+
*
|
|
129
|
+
* @param {string} relPath — relative path within templates/
|
|
130
|
+
* @returns {string} — relative output path within target
|
|
131
|
+
*/
|
|
132
|
+
function mapTemplateOutput(relPath) {
|
|
133
|
+
// AGENTS.md stays in root
|
|
134
|
+
if (relPath === 'AGENTS.md') return relPath;
|
|
135
|
+
// harness-config.json → harness/config.json
|
|
136
|
+
if (relPath === 'harness-config.json') return 'harness/config.json';
|
|
137
|
+
// Top-level harness files → harness/
|
|
138
|
+
const harnessRootFiles = ['progress.md', 'sprint-contract.md', 'evaluator-rubric.md'];
|
|
139
|
+
if (harnessRootFiles.includes(relPath)) return `harness/${relPath}`;
|
|
140
|
+
// init scripts → harness/scripts/
|
|
141
|
+
if (relPath === 'init.sh' || relPath === 'init.ps1') return `harness/scripts/${relPath}`;
|
|
142
|
+
// ci/ → harness/ci/
|
|
143
|
+
if (relPath.startsWith('ci/')) return `harness/${relPath}`;
|
|
144
|
+
// docs/ → harness/docs/
|
|
145
|
+
if (relPath.startsWith('docs/')) return `harness/${relPath}`;
|
|
146
|
+
// Default: put under harness/
|
|
147
|
+
return `harness/${relPath}`;
|
|
148
|
+
}
|
|
149
|
+
|
|
114
150
|
/**
|
|
115
151
|
* Run the template engine.
|
|
116
152
|
*
|
|
@@ -147,7 +183,8 @@ export function generateTemplates(opts) {
|
|
|
147
183
|
const relativePath = tmplPath.startsWith(TEMPLATES_DIR + '/')
|
|
148
184
|
? tmplPath.slice(TEMPLATES_DIR.length + 1)
|
|
149
185
|
: basename(tmplPath);
|
|
150
|
-
const
|
|
186
|
+
const outputRel = mapTemplateOutput(relativePath);
|
|
187
|
+
const outPath = join(target, outputRel);
|
|
151
188
|
const outDir = dirname(outPath);
|
|
152
189
|
|
|
153
190
|
try {
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dev-harness-cli",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Agent-agnostic software development harness CLI — scaffold, phase orchestration, gate validation for any coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"harness
|
|
7
|
+
"dev-harness": "cli/dev-harness.mjs"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"cli/",
|
|
@@ -37,8 +37,12 @@
|
|
|
37
37
|
"cli"
|
|
38
38
|
],
|
|
39
39
|
"scripts": {
|
|
40
|
-
"
|
|
41
|
-
"
|
|
40
|
+
"lint": "eslint cli/",
|
|
41
|
+
"lint:fix": "eslint cli/ --fix",
|
|
42
|
+
"check": "node --check cli/dev-harness.mjs && echo 'Syntax OK'",
|
|
43
|
+
"test": "node test/run-all.mjs",
|
|
44
|
+
"test:verbose": "node test/run-all.mjs --verbose",
|
|
45
|
+
"postinstall": "node -e \"try{process.stdout.write('dev-harness installed. Run: npx dev-harness --help\\n')}catch(e){}\""
|
|
42
46
|
},
|
|
43
47
|
"devDependencies": {
|
|
44
48
|
"@eslint/js": "^10.0.1",
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "Feature List",
|
|
4
|
+
"description": "Schema for feature_list.json — tracks features and their tasks through the pipeline",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["version", "features"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"version": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "Schema version",
|
|
11
|
+
"default": "0.1"
|
|
12
|
+
},
|
|
13
|
+
"features": {
|
|
14
|
+
"type": "array",
|
|
15
|
+
"items": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"required": ["id", "name", "passes", "tasks"],
|
|
18
|
+
"properties": {
|
|
19
|
+
"id": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "Unique feature identifier (e.g. feature-001)"
|
|
22
|
+
},
|
|
23
|
+
"name": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"description": "Human-readable feature name"
|
|
26
|
+
},
|
|
27
|
+
"description": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "Optional feature description"
|
|
30
|
+
},
|
|
31
|
+
"passes": {
|
|
32
|
+
"type": "boolean",
|
|
33
|
+
"default": false,
|
|
34
|
+
"description": "All tasks completed and verified"
|
|
35
|
+
},
|
|
36
|
+
"tasks": {
|
|
37
|
+
"type": "array",
|
|
38
|
+
"items": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"required": ["id", "description", "status"],
|
|
41
|
+
"properties": {
|
|
42
|
+
"id": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"description": "Unique task identifier"
|
|
45
|
+
},
|
|
46
|
+
"description": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "Task description"
|
|
49
|
+
},
|
|
50
|
+
"status": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"enum": ["pending", "in_progress", "complete", "blocked"],
|
|
53
|
+
"default": "pending",
|
|
54
|
+
"description": "Task status"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
package/templates/AGENTS.md
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
## Quick Start
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
harness
|
|
7
|
-
harness
|
|
8
|
-
harness
|
|
6
|
+
dev-harness status # Where are we?
|
|
7
|
+
dev-harness phase <name> # Invoke a phase
|
|
8
|
+
dev-harness validate # Check gate criteria
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Project
|
|
@@ -18,7 +18,7 @@ harness-dev validate # Check gate criteria
|
|
|
18
18
|
|
|
19
19
|
INIT → DEFINE → PLAN → BUILD → VERIFY → [SIMPLIFY] → REVIEW → SHIP
|
|
20
20
|
|
|
21
|
-
See `docs/phases/` for phase-specific instructions.
|
|
21
|
+
See `harness/docs/phases/` for phase-specific instructions.
|
|
22
22
|
|
|
23
23
|
## Agent Roles
|
|
24
24
|
|
|
@@ -31,23 +31,27 @@ See `docs/phases/` for phase-specific instructions.
|
|
|
31
31
|
|
|
32
32
|
## Key Files
|
|
33
33
|
|
|
34
|
+
All harness-managed files live under `harness/` (except `AGENTS.md` which stays in root for agent tool compatibility).
|
|
35
|
+
|
|
34
36
|
| File | Purpose |
|
|
35
37
|
|------|---------|
|
|
36
|
-
| `
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
39
|
-
| `
|
|
40
|
-
| `
|
|
41
|
-
| `
|
|
42
|
-
|
|
|
38
|
+
| `AGENTS.md` | This file — agent instructions (root) |
|
|
39
|
+
| `harness/config.json` | Config + state |
|
|
40
|
+
| `harness/features/feature-list.json` | Feature list with passes |
|
|
41
|
+
| `harness/progress.md` | Session state + lessons |
|
|
42
|
+
| `harness/sprint-contract.md` | Pre-build agreement |
|
|
43
|
+
| `harness/scripts/init.sh` | Install → verify → start |
|
|
44
|
+
| `harness/evaluator-rubric.md` | Quality scorecard (6 dimensions, 0-2) |
|
|
45
|
+
| `harness/docs/` | Architecture, constraints, decisions, agent guides, phase guides |
|
|
46
|
+
|
|
43
47
|
## Rules (non-negotiable)
|
|
44
48
|
|
|
45
49
|
1. No agent evaluates its own work — Evaluator always judges
|
|
46
|
-
2. Read `progress.md` + this file before each operation
|
|
50
|
+
2. Read `harness/progress.md` + this file before each operation
|
|
47
51
|
3. Commit frequently — each iteration is a checkpoint
|
|
48
|
-
4. If unsure → read the role guide in `docs/agents/`
|
|
49
|
-
5. Never skip gates — run `harness
|
|
50
|
-
6. Fresh context per retry — pass `--git-ops` to `harness
|
|
52
|
+
4. If unsure → read the role guide in `harness/docs/agents/`
|
|
53
|
+
5. Never skip gates — run `dev-harness validate` after each phase
|
|
54
|
+
6. Fresh context per retry — pass `--git-ops` to `dev-harness phase <name>` to auto-reset the working tree on retry (off by default; agent-agnostic)
|
|
51
55
|
7. **No files in project root** unless they are harness-managed files (listed in Key Files above) or standard project files (README.md, LICENSE, CHANGELOG.md, CONTRIBUTING.md, .gitignore, and the stack config file like package.json/pyproject.toml/Cargo.toml). All source code, tests, scripts, and docs go in subdirectories.
|
|
52
56
|
8. **Structure from the start** — create folders for your work on day one and stick to them. Suggested layout: `src/` (source), `tests/` (tests), `docs/` (documentation), `scripts/` (automation). Do not dump files at root "temporarily" — there is no temporary.
|
|
53
57
|
9. **No orphaned files** — every file you create must have a clear purpose and be referenced by imports, configs, docs, or the build system. If you create a file, wire it in immediately. Delete files you stop using.
|