dev-harness-cli 1.1.0 → 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 +19 -19
- 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 +3 -3
- package/cli/commands/init.mjs +1 -1
- 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 +6 -6
- package/cli/commands/set-mode.mjs +5 -5
- package/cli/commands/status.mjs +7 -7
- 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 +1 -1
- package/cli/lib/help.mjs +28 -28
- 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 +1 -1
- package/cli/lib/state.mjs +1 -1
- package/package.json +4 -4
- package/templates/AGENTS.md +5 -5
- package/templates/ci/github-actions.yml +1 -1
- package/templates/ci/gitlab-ci.yml +1 -1
- package/templates/docs/agents/generator.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/init.ps1 +1 -1
- package/templates/progress.md +1 -1
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ npx dev-harness-cli init --stack python --target my-project
|
|
|
35
35
|
|
|
36
36
|
# Global install
|
|
37
37
|
npm install -g dev-harness-cli
|
|
38
|
-
harness
|
|
38
|
+
dev-harness --help
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
Requires **Node.js >= 18**. Zero runtime dependencies.
|
|
@@ -44,29 +44,29 @@ Requires **Node.js >= 18**. Zero runtime dependencies.
|
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
46
|
# 1. Scaffold a new project
|
|
47
|
-
harness
|
|
47
|
+
dev-harness init --stack node --target my-app
|
|
48
48
|
cd my-app
|
|
49
49
|
|
|
50
50
|
# 2. Check status
|
|
51
|
-
harness
|
|
51
|
+
dev-harness status
|
|
52
52
|
|
|
53
53
|
# 3. Run the DEFINE phase (agent writes specs)
|
|
54
|
-
harness
|
|
54
|
+
dev-harness phase define
|
|
55
55
|
|
|
56
56
|
# 4. Validate (run gate checks)
|
|
57
|
-
harness
|
|
57
|
+
dev-harness validate
|
|
58
58
|
|
|
59
59
|
# 5. Continue through pipeline
|
|
60
|
-
harness
|
|
61
|
-
harness
|
|
62
|
-
harness
|
|
63
|
-
harness
|
|
64
|
-
harness
|
|
60
|
+
dev-harness phase plan
|
|
61
|
+
dev-harness phase build
|
|
62
|
+
dev-harness phase verify
|
|
63
|
+
dev-harness phase review
|
|
64
|
+
dev-harness phase ship
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
## Project Structure
|
|
68
68
|
|
|
69
|
-
When you run `harness
|
|
69
|
+
When you run `dev-harness init`, all harness-managed files go into a `harness/` subfolder — keeping your project root clean:
|
|
70
70
|
|
|
71
71
|
```
|
|
72
72
|
my-project/
|
|
@@ -157,13 +157,13 @@ Harness works with any coding agent. Use `--agent-tool` during init to generate
|
|
|
157
157
|
|
|
158
158
|
```bash
|
|
159
159
|
# Claude Code → generates CLAUDE.md
|
|
160
|
-
harness
|
|
160
|
+
dev-harness init --stack node --agent-tool claude-code --target my-app
|
|
161
161
|
|
|
162
162
|
# Cursor → generates .cursorrules
|
|
163
|
-
harness
|
|
163
|
+
dev-harness init --stack node --agent-tool cursor --target my-app
|
|
164
164
|
|
|
165
165
|
# GitHub Copilot → generates .github/copilot-instructions.md
|
|
166
|
-
harness
|
|
166
|
+
dev-harness init --stack node --agent-tool copilot --target my-app
|
|
167
167
|
```
|
|
168
168
|
|
|
169
169
|
**18 supported tools:** claude-code, cursor, windsurf, gemini, copilot, cline, roo, kilo-code, amazon-q, codex, opencode, continue, aider, antigravity, openclaw, pi, hermes, generic.
|
|
@@ -175,7 +175,7 @@ See [docs/TOOL_INTEGRATION.md](docs/TOOL_INTEGRATION.md) for per-tool setup guid
|
|
|
175
175
|
Gates are deterministic checks that must pass before advancing to the next phase. Enable with:
|
|
176
176
|
|
|
177
177
|
```bash
|
|
178
|
-
harness
|
|
178
|
+
dev-harness config set gates.enabled true
|
|
179
179
|
```
|
|
180
180
|
|
|
181
181
|
| Phase | Gates |
|
|
@@ -193,7 +193,7 @@ harness-dev config set gates.enabled true
|
|
|
193
193
|
All configuration lives in `harness/config.json`. View with:
|
|
194
194
|
|
|
195
195
|
```bash
|
|
196
|
-
harness
|
|
196
|
+
dev-harness config list
|
|
197
197
|
```
|
|
198
198
|
|
|
199
199
|
29 parameters across 8 groups: Execution, Stack, Agent Tool, Gates, Git, Phases, Agent Tones, Runtime State.
|
|
@@ -205,9 +205,9 @@ See [docs/CONFIGURATION.md](docs/CONFIGURATION.md) for full reference.
|
|
|
205
205
|
All commands support `--json` for machine-parseable output:
|
|
206
206
|
|
|
207
207
|
```bash
|
|
208
|
-
harness
|
|
209
|
-
harness
|
|
210
|
-
harness
|
|
208
|
+
dev-harness status --json
|
|
209
|
+
dev-harness phase define --json
|
|
210
|
+
dev-harness validate --json
|
|
211
211
|
```
|
|
212
212
|
|
|
213
213
|
```json
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* save named recovery points (e.g. "before-refactor") that appear
|
|
10
10
|
* in `rollback list` and can be used with `rollback to/branch`.
|
|
11
11
|
*
|
|
12
|
-
* Usage: harness
|
|
12
|
+
* Usage: dev-harness checkpoint create <label>
|
|
13
13
|
*/
|
|
14
14
|
import { die, CliError, EXIT } from '../lib/errors.mjs';
|
|
15
15
|
import { execGit, getGitRoot, gitTagExists, createGitTag } from '../lib/git.mjs';
|
|
@@ -21,7 +21,7 @@ export default async function checkpointCommand(args) {
|
|
|
21
21
|
const label = args.positionals[0];
|
|
22
22
|
|
|
23
23
|
if (sub !== 'create' || !label) {
|
|
24
|
-
die(new CliError('Usage: harness
|
|
24
|
+
die(new CliError('Usage: dev-harness checkpoint create <label> [--force]', EXIT.USAGE_ERROR), json);
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
27
|
|
package/cli/commands/config.mjs
CHANGED
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
* Uses state.mjs for dot-notation read/write with persistence.
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
|
-
* harness
|
|
8
|
-
* harness
|
|
9
|
-
* harness
|
|
7
|
+
* dev-harness config list — list all parameters with descriptions
|
|
8
|
+
* dev-harness config get [key] — get a value or all config
|
|
9
|
+
* dev-harness config set <key> <value> — set a value
|
|
10
10
|
*
|
|
11
11
|
* Examples:
|
|
12
|
-
* harness
|
|
13
|
-
* harness
|
|
14
|
-
* harness
|
|
15
|
-
* harness
|
|
16
|
-
* harness
|
|
12
|
+
* dev-harness config list
|
|
13
|
+
* dev-harness config list --json
|
|
14
|
+
* dev-harness config get gates.enabled
|
|
15
|
+
* dev-harness config set gates.enabled true
|
|
16
|
+
* dev-harness config set maxRetries 5
|
|
17
17
|
*/
|
|
18
18
|
import { resolve } from 'node:path';
|
|
19
19
|
import { die, CliError, EXIT } from '../lib/errors.mjs';
|
|
@@ -29,7 +29,7 @@ export default async function configCommand(args) {
|
|
|
29
29
|
|
|
30
30
|
if (!sub || (sub !== 'get' && sub !== 'set' && sub !== 'list')) {
|
|
31
31
|
die(new CliError(
|
|
32
|
-
'Usage: harness
|
|
32
|
+
'Usage: dev-harness config list | config get [key] | config set <key> <value>',
|
|
33
33
|
EXIT.USAGE_ERROR,
|
|
34
34
|
), json);
|
|
35
35
|
return;
|
|
@@ -75,7 +75,7 @@ export default async function configCommand(args) {
|
|
|
75
75
|
// Human output — grouped table
|
|
76
76
|
process.stdout.write('═══ Harness Configuration ═══\n\n');
|
|
77
77
|
if (!ok) {
|
|
78
|
-
process.stdout.write(' No harness/config.json found. Run: harness
|
|
78
|
+
process.stdout.write(' No harness/config.json found. Run: dev-harness init\n\n');
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
81
|
|
|
@@ -102,7 +102,7 @@ export default async function configCommand(args) {
|
|
|
102
102
|
process.stdout.write('\n');
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
process.stdout.write('Edit with: harness
|
|
105
|
+
process.stdout.write('Edit with: dev-harness config set <key> <value>\n');
|
|
106
106
|
process.stdout.write('Full docs: docs/CONFIGURATION.md\n');
|
|
107
107
|
return;
|
|
108
108
|
}
|
|
@@ -141,7 +141,7 @@ export default async function configCommand(args) {
|
|
|
141
141
|
if (sub === 'set') {
|
|
142
142
|
if (pos.length < 2) {
|
|
143
143
|
die(new CliError(
|
|
144
|
-
'Usage: harness
|
|
144
|
+
'Usage: dev-harness config set <key> <value>\n' +
|
|
145
145
|
' String values: config set mode copilot\n' +
|
|
146
146
|
' Boolean values: config set gates.enabled true\n' +
|
|
147
147
|
' Numeric values: config set maxRetries 5',
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* Manages the generator-evaluator agreement loop.
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
|
-
* harness
|
|
8
|
-
* harness
|
|
9
|
-
* harness
|
|
10
|
-
* harness
|
|
7
|
+
* dev-harness contract propose [--scope "msg"] [--exclusions "msg"]
|
|
8
|
+
* dev-harness contract review [--agreed|--needs-revision]
|
|
9
|
+
* dev-harness contract status
|
|
10
|
+
* dev-harness contract escalate [--reason "msg"]
|
|
11
11
|
*/
|
|
12
12
|
import { resolve } from 'node:path';
|
|
13
13
|
import { die, CliError, EXIT } from '../lib/errors.mjs';
|
|
@@ -22,7 +22,7 @@ export default async function contractCommand(args) {
|
|
|
22
22
|
const sub = args.subcommand;
|
|
23
23
|
|
|
24
24
|
if (!sub || !SUBCOMMANDS.includes(sub)) {
|
|
25
|
-
die(new CliError(`Usage: harness
|
|
25
|
+
die(new CliError(`Usage: dev-harness contract ${SUBCOMMANDS.join('|')}`, EXIT.USAGE_ERROR), json);
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -34,7 +34,7 @@ export default async function contractCommand(args) {
|
|
|
34
34
|
|
|
35
35
|
if (!scope) {
|
|
36
36
|
die(new CliError(
|
|
37
|
-
'Usage: harness
|
|
37
|
+
'Usage: dev-harness contract propose --scope "I will build X" [--exclusions "W"] [--criteria "test1|test2"]',
|
|
38
38
|
EXIT.USAGE_ERROR,
|
|
39
39
|
), json);
|
|
40
40
|
return;
|
|
@@ -53,7 +53,7 @@ export default async function contractCommand(args) {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
if (result.ok) {
|
|
56
|
-
process.stdout.write('✓ Contract proposed. Run: harness
|
|
56
|
+
process.stdout.write('✓ Contract proposed. Run: dev-harness contract review\n');
|
|
57
57
|
} else {
|
|
58
58
|
process.stderr.write(`✗ ${result.error}\n`);
|
|
59
59
|
}
|
|
@@ -68,7 +68,7 @@ export default async function contractCommand(args) {
|
|
|
68
68
|
|
|
69
69
|
if (!agreed && !needsRevision) {
|
|
70
70
|
die(new CliError(
|
|
71
|
-
'Usage: harness
|
|
71
|
+
'Usage: dev-harness contract review --agreed [--notes "msg"] OR --needs-revision [--notes "msg"]',
|
|
72
72
|
EXIT.USAGE_ERROR,
|
|
73
73
|
), json);
|
|
74
74
|
return;
|
|
@@ -116,7 +116,7 @@ export default async function contractCommand(args) {
|
|
|
116
116
|
rounds,
|
|
117
117
|
message: status
|
|
118
118
|
? `Contract ${status} (round ${rounds}/5)`
|
|
119
|
-
: 'No sprint-contract.md found. Run: harness
|
|
119
|
+
: 'No sprint-contract.md found. Run: dev-harness contract propose',
|
|
120
120
|
}) + '\n');
|
|
121
121
|
return;
|
|
122
122
|
}
|
|
@@ -124,7 +124,7 @@ export default async function contractCommand(args) {
|
|
|
124
124
|
if (status) {
|
|
125
125
|
process.stdout.write(`Contract status: ${status} (round ${rounds}/5)\n`);
|
|
126
126
|
} else {
|
|
127
|
-
process.stdout.write('No sprint-contract.md found. Run: harness
|
|
127
|
+
process.stdout.write('No sprint-contract.md found. Run: dev-harness contract propose\n');
|
|
128
128
|
}
|
|
129
129
|
return;
|
|
130
130
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* Also reads harness-config.json agentTool field if present.
|
|
13
13
|
*
|
|
14
14
|
* Usage:
|
|
15
|
-
* harness
|
|
15
|
+
* dev-harness detect-tool [--target <dir>] [--json]
|
|
16
16
|
*/
|
|
17
17
|
import { existsSync } from 'node:fs';
|
|
18
18
|
import { resolve } from 'node:path';
|
|
@@ -80,7 +80,7 @@ export default async function detectToolCommand(args) {
|
|
|
80
80
|
status: 'ok',
|
|
81
81
|
message: detected.length > 0
|
|
82
82
|
? `Detected ${detected.length} agent tool(s): ${detected.join(', ')}`
|
|
83
|
-
: 'No agent tools detected. Run: harness
|
|
83
|
+
: 'No agent tools detected. Run: dev-harness init',
|
|
84
84
|
available: detected,
|
|
85
85
|
configured: configuredTool,
|
|
86
86
|
recommended,
|
|
@@ -104,7 +104,7 @@ export default async function detectToolCommand(args) {
|
|
|
104
104
|
}
|
|
105
105
|
} else {
|
|
106
106
|
emitHuman(` No agent tools detected.\n`);
|
|
107
|
-
emitHuman(` Run: harness
|
|
107
|
+
emitHuman(` Run: dev-harness init to scaffold a project.\n`);
|
|
108
108
|
}
|
|
109
109
|
if (hasAgentsMd) {
|
|
110
110
|
emitHuman(`\n AGENTS.md present — tools that read it natively are available.\n`);
|
package/cli/commands/init.mjs
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* init.sh, progress.md, sprint-contract.md) plus project files (feature_list.json,
|
|
7
7
|
* feature-list.schema.json, session-handoff.md, etc.), git init, .gitignore.
|
|
8
8
|
*
|
|
9
|
-
* Usage: harness
|
|
9
|
+
* Usage: dev-harness init [--stack <name>] [--target <dir>] [--agent-tool <name>] [--force] [--no-git] [--json]
|
|
10
10
|
*/
|
|
11
11
|
import { resolve, join } from 'node:path';
|
|
12
12
|
import {
|
package/cli/commands/learn.mjs
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* learn — Append a lesson to progress.md.
|
|
3
3
|
*
|
|
4
4
|
* Usage:
|
|
5
|
-
* harness
|
|
6
|
-
* harness
|
|
5
|
+
* dev-harness learn "Lesson text here"
|
|
6
|
+
* dev-harness learn "Lesson text" --json
|
|
7
7
|
*/
|
|
8
8
|
import { CliError, EXIT, die } from '../lib/errors.mjs';
|
|
9
9
|
import { appendLesson } from '../lib/progress.mjs';
|
|
@@ -17,7 +17,7 @@ export default async function learnCommand(args) {
|
|
|
17
17
|
if (!message) {
|
|
18
18
|
die(
|
|
19
19
|
new CliError(
|
|
20
|
-
'Lesson message required.\n Example: harness
|
|
20
|
+
'Lesson message required.\n Example: dev-harness learn "Token refresh gotcha — accepts access_token in body"',
|
|
21
21
|
EXIT.USAGE_ERROR,
|
|
22
22
|
),
|
|
23
23
|
json,
|
package/cli/commands/pause.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Sets config.paused = true. The outer loop checks this
|
|
5
5
|
* before starting a new phase in autopilot mode.
|
|
6
6
|
*
|
|
7
|
-
* Usage: harness
|
|
7
|
+
* Usage: dev-harness pause [--json]
|
|
8
8
|
*/
|
|
9
9
|
import { set } from '../lib/state.mjs';
|
|
10
10
|
import { parseCommandArgs } from '../lib/command-helpers.mjs';
|
package/cli/commands/phase.mjs
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* then triggers the outer loop for autopilot advancement.
|
|
6
6
|
*
|
|
7
7
|
* Usage:
|
|
8
|
-
* harness
|
|
9
|
-
* harness
|
|
8
|
+
* dev-harness phase <name>
|
|
9
|
+
* dev-harness phase <name> --json
|
|
10
10
|
*/
|
|
11
11
|
import { CliError, EXIT, die } from '../lib/errors.mjs';
|
|
12
12
|
import { transitionPhase, getPhaseOrder, loadConfig } from '../lib/state.mjs';
|
|
@@ -51,7 +51,7 @@ export default async function phaseCommand(args) {
|
|
|
51
51
|
const { config: preConfig, ok: preOk } = loadConfig(targetDir);
|
|
52
52
|
const preMode = preOk ? (preConfig.mode ?? 'copilot') : 'copilot';
|
|
53
53
|
if (preOk && preConfig.paused && preMode === 'autopilot') {
|
|
54
|
-
const msg = 'Pipeline is paused. Run: harness
|
|
54
|
+
const msg = 'Pipeline is paused. Run: dev-harness resume';
|
|
55
55
|
if (json) {
|
|
56
56
|
process.stdout.write(JSON.stringify({
|
|
57
57
|
command: 'phase',
|
|
@@ -140,11 +140,11 @@ export default async function phaseCommand(args) {
|
|
|
140
140
|
if (pipelineResult.status === 'complete') {
|
|
141
141
|
process.stdout.write(`\n✓ Pipeline complete. All phases done.\n`);
|
|
142
142
|
} else if (pipelineResult.status === 'instruction') {
|
|
143
|
-
process.stdout.write(`\nNext: harness
|
|
143
|
+
process.stdout.write(`\nNext: dev-harness phase ${pipelineResult.nextPhase}\n`);
|
|
144
144
|
}
|
|
145
145
|
} else if (nextPhase) {
|
|
146
146
|
// Copilot: print next step
|
|
147
|
-
process.stdout.write(`Next: harness
|
|
147
|
+
process.stdout.write(`Next: dev-harness phase ${nextPhase}\n`);
|
|
148
148
|
// Auto-prompt: controlled by two independent flags:
|
|
149
149
|
// autoPrompt=true → show the prompt
|
|
150
150
|
// confirmGates=true → require y/n answer before continuing
|
|
@@ -158,7 +158,7 @@ export default async function phaseCommand(args) {
|
|
|
158
158
|
process.stdout.write(`\n✓ Pipeline complete. All phases done.\n`);
|
|
159
159
|
}
|
|
160
160
|
} else if (answer === false) {
|
|
161
|
-
process.stdout.write(` Staying in ${phase.toUpperCase()}. Run: harness
|
|
161
|
+
process.stdout.write(` Staying in ${phase.toUpperCase()}. Run: dev-harness phase ${nextPhase} when ready.\n`);
|
|
162
162
|
}
|
|
163
163
|
// null = no TTY, skipped
|
|
164
164
|
} else {
|
package/cli/commands/resume.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Sets config.paused = false. Allows autopilot to continue.
|
|
5
5
|
*
|
|
6
|
-
* Usage: harness
|
|
6
|
+
* Usage: dev-harness resume [--json]
|
|
7
7
|
*/
|
|
8
8
|
import { set } from '../lib/state.mjs';
|
|
9
9
|
import { parseCommandArgs } from '../lib/command-helpers.mjs';
|
|
@@ -19,14 +19,14 @@ export default async function resumeCommand(args) {
|
|
|
19
19
|
command: 'resume',
|
|
20
20
|
status: result.ok ? 'ok' : 'error',
|
|
21
21
|
message: result.ok
|
|
22
|
-
? 'Pipeline resumed. Run: harness
|
|
22
|
+
? 'Pipeline resumed. Run: dev-harness phase <name> to continue.'
|
|
23
23
|
: (result.error || 'Failed to resume'),
|
|
24
24
|
});
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
if (result.ok) {
|
|
29
|
-
emitHuman('✓ Pipeline resumed. Run: harness
|
|
29
|
+
emitHuman('✓ Pipeline resumed. Run: dev-harness phase <name> to continue.\n');
|
|
30
30
|
} else {
|
|
31
31
|
emitHumanError(`✗ ${result.error}\n`);
|
|
32
32
|
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* manual/<label> — User-created manual checkpoints
|
|
14
14
|
* recovery/* — Recovery branches (for informational display)
|
|
15
15
|
*
|
|
16
|
-
* Usage: harness
|
|
16
|
+
* Usage: dev-harness rollback <subcommand> [checkpoint]
|
|
17
17
|
*/
|
|
18
18
|
import { die, CliError, EXIT } from '../lib/errors.mjs';
|
|
19
19
|
import { execGit, getGitRoot } from '../lib/git.mjs';
|
|
@@ -67,12 +67,12 @@ export default async function rollbackCommand(args) {
|
|
|
67
67
|
const sub = args.subcommand;
|
|
68
68
|
|
|
69
69
|
if (!sub || !SUBCOMMANDS.includes(sub)) {
|
|
70
|
-
die(new CliError(`Usage: harness
|
|
70
|
+
die(new CliError(`Usage: dev-harness rollback ${SUBCOMMANDS.join('|')}`, EXIT.USAGE_ERROR), json);
|
|
71
71
|
return;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
if (sub !== 'list' && args.positionals.length < 1) {
|
|
75
|
-
die(new CliError(`Usage: harness
|
|
75
|
+
die(new CliError(`Usage: dev-harness rollback ${sub} <checkpoint>`, EXIT.USAGE_ERROR), json);
|
|
76
76
|
return;
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -118,7 +118,7 @@ export default async function rollbackCommand(args) {
|
|
|
118
118
|
} else {
|
|
119
119
|
if (checkpoints.length === 0 && recoveryBranches.length === 0) {
|
|
120
120
|
process.stdout.write('No checkpoints found. Phase tags (phase/*) and iteration tags (iter/*) are created automatically when auto-tagging is enabled.\n');
|
|
121
|
-
process.stdout.write('Manual checkpoints: harness
|
|
121
|
+
process.stdout.write('Manual checkpoints: dev-harness checkpoint create <label>\n');
|
|
122
122
|
} else {
|
|
123
123
|
if (checkpoints.length > 0) {
|
|
124
124
|
process.stdout.write('Checkpoints:\n');
|
|
@@ -146,7 +146,7 @@ export default async function rollbackCommand(args) {
|
|
|
146
146
|
// Verify the tag exists
|
|
147
147
|
const tagCheck = execGit(`git rev-parse --verify "${checkpoint}^{commit}"`, gitRoot);
|
|
148
148
|
if (!tagCheck.ok) {
|
|
149
|
-
const msg = `Checkpoint "${checkpoint}" not found. Run: harness
|
|
149
|
+
const msg = `Checkpoint "${checkpoint}" not found. Run: dev-harness rollback list`;
|
|
150
150
|
if (json) {
|
|
151
151
|
process.stdout.write(JSON.stringify({ command: 'rollback', subcommand: 'to', checkpoint, status: 'error', message: msg }) + '\n');
|
|
152
152
|
} else {
|
|
@@ -205,7 +205,7 @@ export default async function rollbackCommand(args) {
|
|
|
205
205
|
// Verify the tag exists
|
|
206
206
|
const tagCheck = execGit(`git rev-parse --verify "${checkpoint}^{commit}"`, gitRoot);
|
|
207
207
|
if (!tagCheck.ok) {
|
|
208
|
-
const msg = `Checkpoint "${checkpoint}" not found. Run: harness
|
|
208
|
+
const msg = `Checkpoint "${checkpoint}" not found. Run: dev-harness rollback list`;
|
|
209
209
|
if (json) {
|
|
210
210
|
process.stdout.write(JSON.stringify({ command: 'rollback', subcommand: 'branch', checkpoint, status: 'error', message: msg }) + '\n');
|
|
211
211
|
} else {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* set-mode — Switch between copilot and autopilot.
|
|
3
3
|
*
|
|
4
|
-
* Usage: harness
|
|
5
|
-
* harness
|
|
6
|
-
* harness
|
|
7
|
-
* harness
|
|
4
|
+
* Usage: dev-harness set-mode <mode>
|
|
5
|
+
* dev-harness set-mode autopilot
|
|
6
|
+
* dev-harness set-mode copilot
|
|
7
|
+
* dev-harness set-mode autopilot --json
|
|
8
8
|
*/
|
|
9
9
|
import { die, CliError, EXIT } from '../lib/errors.mjs';
|
|
10
10
|
import { set, loadConfig, getPhaseOrder } from '../lib/state.mjs';
|
|
@@ -19,7 +19,7 @@ export default async function setModeCommand(args) {
|
|
|
19
19
|
if (!mode || !valid.includes(mode)) {
|
|
20
20
|
die(
|
|
21
21
|
new CliError(
|
|
22
|
-
`Mode required. Valid: ${valid.join(', ')}.\n Example: harness
|
|
22
|
+
`Mode required. Valid: ${valid.join(', ')}.\n Example: dev-harness set-mode autopilot`,
|
|
23
23
|
EXIT.USAGE_ERROR,
|
|
24
24
|
),
|
|
25
25
|
json,
|
package/cli/commands/status.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Reads harness-config.json via state.mjs for live project state,
|
|
5
5
|
* plus runs stack detection and gate checks for current status.
|
|
6
6
|
*
|
|
7
|
-
* Usage: harness
|
|
7
|
+
* Usage: dev-harness status [--json] [--target <dir>]
|
|
8
8
|
*/
|
|
9
9
|
import { resolve, basename } from 'node:path';
|
|
10
10
|
import { detectStack } from '../lib/detect-stack.mjs';
|
|
@@ -61,7 +61,7 @@ export default async function statusCommand(args) {
|
|
|
61
61
|
status: 'ok',
|
|
62
62
|
message: configOk
|
|
63
63
|
? `Phase: ${phase || 'not started'}, Stack: ${stack.label}`
|
|
64
|
-
: 'No harness/config.json found — run harness
|
|
64
|
+
: 'No harness/config.json found — run dev-harness init',
|
|
65
65
|
project: basename(targetDir),
|
|
66
66
|
stack: stack.name,
|
|
67
67
|
stackLabel: stack.label,
|
|
@@ -150,19 +150,19 @@ function gateStatusLabel(status, passing, total) {
|
|
|
150
150
|
|
|
151
151
|
function determineNextAction(targetDir, configOk, config, phase, gateStatus) {
|
|
152
152
|
if (!configOk) {
|
|
153
|
-
return 'Run: harness
|
|
153
|
+
return 'Run: dev-harness init';
|
|
154
154
|
}
|
|
155
155
|
if (!phase) {
|
|
156
|
-
return 'Run: harness
|
|
156
|
+
return 'Run: dev-harness phase define to start';
|
|
157
157
|
}
|
|
158
158
|
if (gateStatus === 'fail') {
|
|
159
|
-
return 'Run: harness
|
|
159
|
+
return 'Run: dev-harness validate to re-check';
|
|
160
160
|
}
|
|
161
161
|
// Determine next phase
|
|
162
162
|
const order = ['define', 'plan', 'build', 'verify', 'review', 'ship'];
|
|
163
163
|
const idx = order.indexOf(phase);
|
|
164
164
|
if (idx >= 0 && idx < order.length - 1) {
|
|
165
|
-
return `Run: harness
|
|
165
|
+
return `Run: dev-harness phase ${order[idx + 1]}`;
|
|
166
166
|
}
|
|
167
|
-
return `Run: harness
|
|
167
|
+
return `Run: dev-harness validate`;
|
|
168
168
|
}
|
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
* Otherwise runs phase-specific checks and reports results.
|
|
6
6
|
*
|
|
7
7
|
* Usage:
|
|
8
|
-
* harness
|
|
9
|
-
* harness
|
|
10
|
-
* harness
|
|
8
|
+
* dev-harness validate — check current phase
|
|
9
|
+
* dev-harness validate --json — machine-readable output
|
|
10
|
+
* dev-harness validate --phase X — check specific phase
|
|
11
11
|
*
|
|
12
12
|
* Examples:
|
|
13
|
-
* harness
|
|
13
|
+
* dev-harness validate
|
|
14
14
|
* # → BUILD Gate: PASS — 3/3 checks pass
|
|
15
15
|
*
|
|
16
|
-
* harness
|
|
16
|
+
* dev-harness validate --json
|
|
17
17
|
* # → {"phase":"build","checks":[...],"overall":false,"failures":["lint"]}
|
|
18
18
|
*/
|
|
19
19
|
import { resolve } from 'node:path';
|
|
@@ -53,7 +53,7 @@ export default async function validateCommand(args) {
|
|
|
53
53
|
if (task) { out.task = task; }
|
|
54
54
|
process.stdout.write(JSON.stringify(out) + '\n');
|
|
55
55
|
} else {
|
|
56
|
-
process.stdout.write('Gates disabled. Enable with: harness
|
|
56
|
+
process.stdout.write('Gates disabled. Enable with: dev-harness config set gates.enabled true\n');
|
|
57
57
|
}
|
|
58
58
|
return;
|
|
59
59
|
}
|
|
@@ -62,7 +62,7 @@ export default async function validateCommand(args) {
|
|
|
62
62
|
if (!phase) {
|
|
63
63
|
die(
|
|
64
64
|
new CliError(
|
|
65
|
-
'No phase found in config. Run: harness
|
|
65
|
+
'No phase found in config. Run: dev-harness init or dev-harness phase <name>',
|
|
66
66
|
EXIT.VALIDATION_FAILURE,
|
|
67
67
|
),
|
|
68
68
|
json,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* prune — git worktree prune (remove orphaned metadata)
|
|
9
9
|
* remove <name> — git worktree remove + optionally delete branch
|
|
10
10
|
*
|
|
11
|
-
* Usage: harness
|
|
11
|
+
* Usage: dev-harness worktree <subcommand> [name] [options]
|
|
12
12
|
*/
|
|
13
13
|
import { existsSync } from 'node:fs';
|
|
14
14
|
import { resolve, dirname } from 'node:path';
|
|
@@ -25,13 +25,13 @@ export default async function worktreeCommand(args) {
|
|
|
25
25
|
const sub = args.subcommand;
|
|
26
26
|
|
|
27
27
|
if (!sub || !SUBCOMMANDS.includes(sub)) {
|
|
28
|
-
die(new CliError(`Usage: harness
|
|
28
|
+
die(new CliError(`Usage: dev-harness worktree ${SUBCOMMANDS.join('|')}`, EXIT.USAGE_ERROR), json);
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const gitRoot = getGitRoot(targetDir);
|
|
33
33
|
if (!gitRoot) {
|
|
34
|
-
const msg = 'Not inside a git repository. Run: git init first or harness
|
|
34
|
+
const msg = 'Not inside a git repository. Run: git init first or dev-harness init';
|
|
35
35
|
if (json) {
|
|
36
36
|
process.stdout.write(JSON.stringify({ command: 'worktree', subcommand: sub, status: 'error', message: msg }) + '\n');
|
|
37
37
|
} else {
|
|
@@ -44,7 +44,7 @@ export default async function worktreeCommand(args) {
|
|
|
44
44
|
if (sub === 'create') {
|
|
45
45
|
const name = args.positionals[0];
|
|
46
46
|
if (!name) {
|
|
47
|
-
die(new CliError('Usage: harness
|
|
47
|
+
die(new CliError('Usage: dev-harness worktree create <name>', EXIT.USAGE_ERROR), json);
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -88,7 +88,7 @@ export default async function worktreeCommand(args) {
|
|
|
88
88
|
|
|
89
89
|
// Scaffold harness in the new worktree — run full init with parent's detected stack
|
|
90
90
|
const stack = detectStack(worktreePath).name;
|
|
91
|
-
const harnessDevPath = new URL('../harness
|
|
91
|
+
const harnessDevPath = new URL('../dev-harness.mjs', import.meta.url).pathname;
|
|
92
92
|
const initResult = execGit(
|
|
93
93
|
`node "${harnessDevPath}" init --stack "${stack}" --force --no-git --json`,
|
|
94
94
|
worktreePath,
|
|
@@ -232,7 +232,7 @@ export default async function worktreeCommand(args) {
|
|
|
232
232
|
if (sub === 'remove') {
|
|
233
233
|
const name = args.positionals[0];
|
|
234
234
|
if (!name) {
|
|
235
|
-
die(new CliError('Usage: harness
|
|
235
|
+
die(new CliError('Usage: dev-harness worktree remove <name>', EXIT.USAGE_ERROR), json);
|
|
236
236
|
return;
|
|
237
237
|
}
|
|
238
238
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* harness
|
|
3
|
+
* dev-harness — Agent-agnostic development harness CLI.
|
|
4
4
|
*
|
|
5
5
|
* Entry point. Parses args, routes to command handler,
|
|
6
6
|
* formats output (human or JSON), handles errors.
|
|
@@ -68,7 +68,7 @@ async function main() {
|
|
|
68
68
|
const loader = COMMANDS[args.command];
|
|
69
69
|
if (!loader) {
|
|
70
70
|
throw new CliError(
|
|
71
|
-
`Unknown command "${args.command}". See harness
|
|
71
|
+
`Unknown command "${args.command}". See dev-harness --help`,
|
|
72
72
|
EXIT.USAGE_ERROR
|
|
73
73
|
);
|
|
74
74
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Single source of truth for parameter descriptions, types, allowed values,
|
|
5
5
|
* and defaults. Used by:
|
|
6
|
-
* - `harness
|
|
6
|
+
* - `dev-harness config list` — interactive parameter listing
|
|
7
7
|
* - docs/CONFIGURATION.md — generated documentation
|
|
8
8
|
* - config command — validation of set values
|
|
9
9
|
*
|
|
@@ -198,7 +198,7 @@ export const CONFIG_PARAMS = [
|
|
|
198
198
|
group: 'Runtime State',
|
|
199
199
|
label: 'Current Phase',
|
|
200
200
|
type: 'string',
|
|
201
|
-
description: 'Current pipeline phase. Managed by harness
|
|
201
|
+
description: 'Current pipeline phase. Managed by dev-harness phase — do not edit.',
|
|
202
202
|
default: null,
|
|
203
203
|
editable: false,
|
|
204
204
|
},
|
package/cli/lib/contract.mjs
CHANGED
|
@@ -168,7 +168,7 @@ ${existingStatus || `## Agreement Status
|
|
|
168
168
|
export function reviewContract(targetDir, decision, notes) {
|
|
169
169
|
const path = CONTRACT_PATH(targetDir);
|
|
170
170
|
if (!existsSync(path)) {
|
|
171
|
-
return { ok: false, error: 'No sprint-contract.md found. Run: harness
|
|
171
|
+
return { ok: false, error: 'No sprint-contract.md found. Run: dev-harness contract propose first', escalated: false };
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
try {
|
|
@@ -276,7 +276,7 @@ export function validateContract(targetDir) {
|
|
|
276
276
|
return {
|
|
277
277
|
name: 'contract-agreed',
|
|
278
278
|
pass: false,
|
|
279
|
-
detail: 'Sprint contract not yet proposed. Run: harness
|
|
279
|
+
detail: 'Sprint contract not yet proposed. Run: dev-harness contract propose',
|
|
280
280
|
};
|
|
281
281
|
}
|
|
282
282
|
|
|
@@ -301,6 +301,6 @@ export function validateContract(targetDir) {
|
|
|
301
301
|
return {
|
|
302
302
|
name: 'contract-agreed',
|
|
303
303
|
pass: false,
|
|
304
|
-
detail: `Sprint contract ${noun} (round ${rounds}/${MAX_NEGOTIATION_ROUNDS}). Run: harness
|
|
304
|
+
detail: `Sprint contract ${noun} (round ${rounds}/${MAX_NEGOTIATION_ROUNDS}). Run: dev-harness contract review`,
|
|
305
305
|
};
|
|
306
306
|
}
|
package/cli/lib/gates.mjs
CHANGED
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.
|
|
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/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
|
@@ -418,7 +418,7 @@ export function getVersionFileContent(stack) {
|
|
|
418
418
|
* Return .gitignore content for the given stack.
|
|
419
419
|
*/
|
|
420
420
|
export function getGitignoreContent(stack) {
|
|
421
|
-
return `# Harness runtime state (regenerated by harness
|
|
421
|
+
return `# Harness runtime state (regenerated by dev-harness)
|
|
422
422
|
harness/config.json
|
|
423
423
|
harness/features/feature-list.json
|
|
424
424
|
harness/progress.md
|
package/cli/lib/state.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dev-harness-cli",
|
|
3
|
-
"version": "1.
|
|
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/",
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"scripts": {
|
|
40
40
|
"lint": "eslint cli/",
|
|
41
41
|
"lint:fix": "eslint cli/ --fix",
|
|
42
|
-
"check": "node --check cli/harness
|
|
42
|
+
"check": "node --check cli/dev-harness.mjs && echo 'Syntax OK'",
|
|
43
43
|
"test": "node test/run-all.mjs",
|
|
44
44
|
"test:verbose": "node test/run-all.mjs --verbose",
|
|
45
|
-
"postinstall": "node -e \"try{process.stdout.write('harness
|
|
45
|
+
"postinstall": "node -e \"try{process.stdout.write('dev-harness installed. Run: npx dev-harness --help\\n')}catch(e){}\""
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@eslint/js": "^10.0.1",
|
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
|
|
@@ -50,8 +50,8 @@ All harness-managed files live under `harness/` (except `AGENTS.md` which stays
|
|
|
50
50
|
2. Read `harness/progress.md` + this file before each operation
|
|
51
51
|
3. Commit frequently — each iteration is a checkpoint
|
|
52
52
|
4. If unsure → read the role guide in `harness/docs/agents/`
|
|
53
|
-
5. Never skip gates — run `harness
|
|
54
|
-
6. Fresh context per retry — pass `--git-ops` to `harness
|
|
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)
|
|
55
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.
|
|
56
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.
|
|
57
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.
|
|
@@ -10,4 +10,4 @@ You implement. You produce artifacts. You self-check.
|
|
|
10
10
|
- In BUILD: implement ONE task at a time, then validate
|
|
11
11
|
- In VERIFY: run the full test suite
|
|
12
12
|
- In SIMPLIFY: adopt the Simplifier persona (see simplifier.md)
|
|
13
|
-
- When done: call `harness
|
|
13
|
+
- When done: call `dev-harness validate`
|
|
@@ -10,4 +10,4 @@ You refactor. You clean. You never change behavior.
|
|
|
10
10
|
- Break long functions (~40 line threshold)
|
|
11
11
|
- Rename unclear variables
|
|
12
12
|
- ⚠ All tests must still pass after your changes
|
|
13
|
-
- Run `harness
|
|
13
|
+
- Run `dev-harness validate` after each feature to confirm gate
|
|
@@ -21,14 +21,14 @@ fresh context. Only when all features pass does the phase gate run.
|
|
|
21
21
|
1. Read `progress.md`, `AGENTS.md`, `sprint-contract.md`.
|
|
22
22
|
2. Pick next feature where `passes === false`.
|
|
23
23
|
3. Implement the feature's tasks.
|
|
24
|
-
4. Run `harness
|
|
24
|
+
4. Run `dev-harness validate --feature <name> --task <id>` per task.
|
|
25
25
|
5. On pass: mark feature `passes: true`, commit, append lesson to `progress.md`.
|
|
26
26
|
6. On fail (≤ `maxRetries`): retry with fresh context (git reset if `--git-ops`).
|
|
27
27
|
7. On fail (> `maxRetries`): escalate to human.
|
|
28
28
|
|
|
29
29
|
## Exit Gate
|
|
30
30
|
|
|
31
|
-
Run `harness
|
|
31
|
+
Run `dev-harness validate` — checks:
|
|
32
32
|
|
|
33
33
|
- `config-exists`
|
|
34
34
|
- `git-repo`
|
|
@@ -38,4 +38,4 @@ Run `harness-dev validate` — checks:
|
|
|
38
38
|
|
|
39
39
|
## Handoff
|
|
40
40
|
|
|
41
|
-
On gate pass: `harness
|
|
41
|
+
On gate pass: `dev-harness phase verify` (Generator → Evaluator).
|
|
@@ -12,7 +12,7 @@ feature list.
|
|
|
12
12
|
|
|
13
13
|
## Entry
|
|
14
14
|
|
|
15
|
-
- `harness-config.json` exists (created by `harness
|
|
15
|
+
- `harness-config.json` exists (created by `dev-harness init`)
|
|
16
16
|
- `AGENTS.md` present in project root
|
|
17
17
|
|
|
18
18
|
## Work
|
|
@@ -40,7 +40,7 @@ feature list.
|
|
|
40
40
|
|
|
41
41
|
## Exit Gate
|
|
42
42
|
|
|
43
|
-
Run `harness
|
|
43
|
+
Run `dev-harness validate` — checks:
|
|
44
44
|
|
|
45
45
|
- `config-exists`
|
|
46
46
|
- `git-repo`
|
|
@@ -48,4 +48,4 @@ Run `harness-dev validate` — checks:
|
|
|
48
48
|
|
|
49
49
|
## Handoff
|
|
50
50
|
|
|
51
|
-
On gate pass: `harness
|
|
51
|
+
On gate pass: `dev-harness phase plan` (Planner → continues as Planner for decomposition).
|
|
@@ -20,11 +20,11 @@ verifiable acceptance criteria. The Sprint Contract is negotiated here.
|
|
|
20
20
|
2. Decompose into features → tasks in `feature_list.json`.
|
|
21
21
|
3. Planner proposes `sprint-contract.md` (scope, criteria, exclusions).
|
|
22
22
|
4. Evaluator reviews; iterate until `**Status:** Agreed`.
|
|
23
|
-
5. Use `harness
|
|
23
|
+
5. Use `dev-harness contract propose` / `contract review --decision <agreed|needs-revision>`.
|
|
24
24
|
|
|
25
25
|
## Exit Gate
|
|
26
26
|
|
|
27
|
-
Run `harness
|
|
27
|
+
Run `dev-harness validate` — checks:
|
|
28
28
|
|
|
29
29
|
- `config-exists`
|
|
30
30
|
- `git-repo`
|
|
@@ -33,4 +33,4 @@ Run `harness-dev validate` — checks:
|
|
|
33
33
|
|
|
34
34
|
## Handoff
|
|
35
35
|
|
|
36
|
-
On gate pass: `harness
|
|
36
|
+
On gate pass: `dev-harness phase build` (Planner → Generator).
|
|
@@ -27,7 +27,7 @@ overall verdict: Accept / Revise / Block.
|
|
|
27
27
|
|
|
28
28
|
## Exit Gate
|
|
29
29
|
|
|
30
|
-
Run `harness
|
|
30
|
+
Run `dev-harness validate` — checks:
|
|
31
31
|
|
|
32
32
|
- `config-exists`
|
|
33
33
|
- `git-repo`
|
|
@@ -39,4 +39,4 @@ Run `harness-dev validate` — checks:
|
|
|
39
39
|
|
|
40
40
|
## Handoff
|
|
41
41
|
|
|
42
|
-
On gate pass: `harness
|
|
42
|
+
On gate pass: `dev-harness phase ship` (committee → release).
|
|
@@ -17,13 +17,13 @@ The release must be reproducible from a clean checkout.
|
|
|
17
17
|
|
|
18
18
|
1. Update `CHANGELOG.md` with version, date, summary.
|
|
19
19
|
2. Bump version in `package.json` / equivalent manifest.
|
|
20
|
-
3. Run full `harness
|
|
20
|
+
3. Run full `dev-harness validate` — all gates must pass.
|
|
21
21
|
4. Tag the release: `git tag -a v<x.y.z> -m "Release x.y.z"`.
|
|
22
22
|
5. Open or merge the PR per project workflow.
|
|
23
23
|
|
|
24
24
|
## Exit Gate
|
|
25
25
|
|
|
26
|
-
Run `harness
|
|
26
|
+
Run `dev-harness validate` — checks:
|
|
27
27
|
|
|
28
28
|
- `config-exists`
|
|
29
29
|
- `git-repo`
|
|
@@ -38,6 +38,6 @@ Run `harness-dev validate` — checks:
|
|
|
38
38
|
|
|
39
39
|
## Handoff
|
|
40
40
|
|
|
41
|
-
On gate pass: pipeline complete. `harness
|
|
41
|
+
On gate pass: pipeline complete. `dev-harness status` reports
|
|
42
42
|
`Pipeline complete after "ship"`. Increment `pipelineIteration` and loop back
|
|
43
43
|
to DEFINE for the next sprint, or stop if the project is done.
|
|
@@ -20,14 +20,14 @@ must not break the feature's acceptance criteria.
|
|
|
20
20
|
1. Read `progress.md` and `AGENTS.md`.
|
|
21
21
|
2. For each feature: review the implementation, propose deletions/renames.
|
|
22
22
|
3. Apply simplifications.
|
|
23
|
-
4. Re-run `harness
|
|
23
|
+
4. Re-run `dev-harness validate --feature <name>` — criteria must still pass.
|
|
24
24
|
5. On pass: commit, append lesson.
|
|
25
25
|
6. On fail (≤ `maxRetries`): revert and retry.
|
|
26
26
|
7. On fail (> `maxRetries`): escalate.
|
|
27
27
|
|
|
28
28
|
## Exit Gate
|
|
29
29
|
|
|
30
|
-
Run `harness
|
|
30
|
+
Run `dev-harness validate` — checks:
|
|
31
31
|
|
|
32
32
|
- `config-exists`
|
|
33
33
|
- `git-repo`
|
|
@@ -37,4 +37,4 @@ Run `harness-dev validate` — checks:
|
|
|
37
37
|
|
|
38
38
|
## Handoff
|
|
39
39
|
|
|
40
|
-
On gate pass: `harness
|
|
40
|
+
On gate pass: `dev-harness phase review` (Simplifier → multi-agent committee).
|
|
@@ -26,7 +26,7 @@ feature's acceptance criteria from the sprint contract and scores it against
|
|
|
26
26
|
|
|
27
27
|
## Exit Gate
|
|
28
28
|
|
|
29
|
-
Run `harness
|
|
29
|
+
Run `dev-harness validate` — checks:
|
|
30
30
|
|
|
31
31
|
- `config-exists`
|
|
32
32
|
- `git-repo`
|
|
@@ -35,4 +35,4 @@ Run `harness-dev validate` — checks:
|
|
|
35
35
|
|
|
36
36
|
## Handoff
|
|
37
37
|
|
|
38
|
-
On gate pass: `harness
|
|
38
|
+
On gate pass: `dev-harness phase simplify` (Evaluator → Simplifier).
|
package/templates/init.ps1
CHANGED
|
@@ -94,4 +94,4 @@ switch ($Stack) {
|
|
|
94
94
|
Write-Host " ✓ Setup verified" -ForegroundColor Green
|
|
95
95
|
|
|
96
96
|
# ── Start ────────────────────────────────────────────────────────────────────
|
|
97
|
-
Write-Host "`nSetup complete. Run: harness
|
|
97
|
+
Write-Host "`nSetup complete. Run: dev-harness phase define" -ForegroundColor Cyan
|