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.
Files changed (45) hide show
  1. package/README.md +158 -230
  2. package/cli/commands/checkpoint.mjs +2 -2
  3. package/cli/commands/config.mjs +12 -12
  4. package/cli/commands/contract.mjs +10 -10
  5. package/cli/commands/detect-tool.mjs +5 -4
  6. package/cli/commands/init.mjs +10 -10
  7. package/cli/commands/learn.mjs +3 -3
  8. package/cli/commands/pause.mjs +1 -1
  9. package/cli/commands/phase.mjs +6 -6
  10. package/cli/commands/resume.mjs +3 -3
  11. package/cli/commands/rollback.mjs +9 -9
  12. package/cli/commands/set-mode.mjs +5 -5
  13. package/cli/commands/status.mjs +9 -9
  14. package/cli/commands/validate.mjs +7 -7
  15. package/cli/commands/worktree.mjs +6 -6
  16. package/cli/{harness-dev.mjs → dev-harness.mjs} +2 -2
  17. package/cli/lib/config-registry.mjs +2 -2
  18. package/cli/lib/contract.mjs +3 -3
  19. package/cli/lib/gates.mjs +11 -9
  20. package/cli/lib/help.mjs +28 -28
  21. package/cli/lib/paths.mjs +87 -8
  22. package/cli/lib/ralph-inner.mjs +2 -2
  23. package/cli/lib/ralph-outer.mjs +3 -3
  24. package/cli/lib/ralph-output.mjs +1 -1
  25. package/cli/lib/scaffold.mjs +18 -18
  26. package/cli/lib/state.mjs +1 -1
  27. package/cli/lib/templates.mjs +38 -1
  28. package/package.json +8 -4
  29. package/schema/feature-list.schema.json +63 -0
  30. package/templates/AGENTS.md +19 -15
  31. package/templates/ci/github-actions.yml +2 -2
  32. package/templates/ci/gitlab-ci.yml +2 -2
  33. package/templates/docs/agents/generator.md +1 -1
  34. package/templates/docs/agents/planner.md +1 -1
  35. package/templates/docs/agents/simplifier.md +1 -1
  36. package/templates/docs/phases/build.md +3 -3
  37. package/templates/docs/phases/define.md +3 -3
  38. package/templates/docs/phases/plan.md +3 -3
  39. package/templates/docs/phases/review.md +2 -2
  40. package/templates/docs/phases/ship.md +3 -3
  41. package/templates/docs/phases/simplify.md +3 -3
  42. package/templates/docs/phases/verify.md +2 -2
  43. package/templates/harness-config.json +42 -0
  44. package/templates/init.ps1 +1 -1
  45. package/templates/progress.md +18 -0
@@ -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-dev init [--stack <name>] [--target <dir>] [--agent-tool <name>] [--force] [--no-git] [--json]
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 {
@@ -144,16 +144,16 @@ export default async function initCommand(args) {
144
144
  const harnessPaths = [];
145
145
  const projectPaths = [];
146
146
 
147
- // Template files — known template names
147
+ // Template files — known template names (mapped to harness/ paths by templates.mjs)
148
148
  const templateNames = [
149
- 'AGENTS.md', 'harness-config.json', 'init.sh',
150
- 'progress.md', 'sprint-contract.md', 'evaluator-rubric.md',
149
+ 'AGENTS.md', 'harness/config.json', 'harness/scripts/init.sh',
150
+ 'harness/progress.md', 'harness/sprint-contract.md', 'harness/evaluator-rubric.md',
151
151
  ];
152
152
  for (const name of templateNames) {
153
153
  harnessPaths.push(join(targetDir, name));
154
154
  }
155
155
 
156
- // Extra scaffold files
156
+ // Extra scaffold files (already have harness/ prefix from getExtraFiles)
157
157
  for (const relPath of Object.keys(extraFiles)) {
158
158
  harnessPaths.push(join(targetDir, relPath));
159
159
  }
@@ -190,8 +190,8 @@ export default async function initCommand(args) {
190
190
  // Ensure target directory exists
191
191
  mkdirSync(targetDir, { recursive: true });
192
192
 
193
- // Ensure docs/ directory exists
194
- mkdirSync(join(targetDir, 'docs'), { recursive: true });
193
+ // Ensure harness/ directory exists (all harness files go here)
194
+ mkdirSync(join(targetDir, 'harness'), { recursive: true });
195
195
 
196
196
  const created = [];
197
197
  const errors = [];
@@ -289,15 +289,15 @@ export default async function initCommand(args) {
289
289
  }
290
290
  }
291
291
 
292
- // Set agentTool in the generated harness-config.json
293
- const configPath = join(targetDir, 'harness-config.json');
292
+ // Set agentTool in the generated harness/config.json
293
+ const configPath = join(targetDir, 'harness', 'config.json');
294
294
  if (existsSync(configPath)) {
295
295
  try {
296
296
  const cfg = JSON.parse(readFileSync(configPath, 'utf-8'));
297
297
  cfg.agentTool = agentTool;
298
298
  writeFileSync(configPath, JSON.stringify(cfg, null, 2) + '\n', 'utf-8');
299
299
  } catch (err) {
300
- errors.push(`harness-config.json agentTool: ${err.message}`);
300
+ errors.push(`harness/config.json agentTool: ${err.message}`);
301
301
  }
302
302
  }
303
303
  }
@@ -2,8 +2,8 @@
2
2
  * learn — Append a lesson to progress.md.
3
3
  *
4
4
  * Usage:
5
- * harness-dev learn "Lesson text here"
6
- * harness-dev learn "Lesson text" --json
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-dev learn "Token refresh gotcha — accepts access_token in body"',
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,
@@ -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-dev pause [--json]
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';
@@ -5,8 +5,8 @@
5
5
  * then triggers the outer loop for autopilot advancement.
6
6
  *
7
7
  * Usage:
8
- * harness-dev phase <name>
9
- * harness-dev phase <name> --json
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-dev resume';
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-dev phase ${pipelineResult.nextPhase}\n`);
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-dev phase ${nextPhase}\n`);
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-dev phase ${nextPhase} when ready.\n`);
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 {
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Sets config.paused = false. Allows autopilot to continue.
5
5
  *
6
- * Usage: harness-dev resume [--json]
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-dev phase <name> to continue.'
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-dev phase <name> to continue.\n');
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-dev rollback <subcommand> [checkpoint]
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-dev rollback ${SUBCOMMANDS.join('|')}`, EXIT.USAGE_ERROR), json);
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-dev rollback ${sub} <checkpoint>`, EXIT.USAGE_ERROR), json);
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-dev checkpoint create <label>\n');
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-dev rollback list`;
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 {
@@ -161,9 +161,9 @@ export default async function rollbackCommand(args) {
161
161
 
162
162
  // Restore all files from the checkpoint
163
163
  const restoreFiles = [
164
- 'harness-config.json',
165
- 'progress.md',
166
- 'feature_list.json',
164
+ 'harness/config.json',
165
+ 'harness/progress.md',
166
+ 'harness/features/feature-list.json',
167
167
  ];
168
168
 
169
169
  // First, restore the whole working tree from the tag
@@ -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-dev rollback list`;
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-dev set-mode <mode>
5
- * harness-dev set-mode autopilot
6
- * harness-dev set-mode copilot
7
- * harness-dev set-mode autopilot --json
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-dev set-mode autopilot`,
22
+ `Mode required. Valid: ${valid.join(', ')}.\n Example: dev-harness set-mode autopilot`,
23
23
  EXIT.USAGE_ERROR,
24
24
  ),
25
25
  json,
@@ -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-dev status [--json] [--target <dir>]
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-dev init',
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,
@@ -84,7 +84,7 @@ export default async function statusCommand(args) {
84
84
 
85
85
  // ── Human-readable output ─────────────────────────────────────────────
86
86
  let out = '';
87
- out += '═══ dev-harness Status ═══\n';
87
+ out += '═══ harness Status ═══\n';
88
88
  out += line('Project:', basename(targetDir)) + '\n';
89
89
  out += line('Stack:', `${stack.label}${stack.name !== 'generic' ? '' : ' (not detected)'}`) + '\n';
90
90
  out += line('Mode:', modeLabel(mode)) + '\n';
@@ -104,7 +104,7 @@ export default async function statusCommand(args) {
104
104
  out += ' Phase: not started.\n';
105
105
  out += '\n';
106
106
  } else {
107
- out += ' No harness-config.json found.\n';
107
+ out += ' No harness/config.json found.\n';
108
108
  out += '\n';
109
109
  }
110
110
 
@@ -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-dev init';
153
+ return 'Run: dev-harness init';
154
154
  }
155
155
  if (!phase) {
156
- return 'Run: harness-dev phase define to start';
156
+ return 'Run: dev-harness phase define to start';
157
157
  }
158
158
  if (gateStatus === 'fail') {
159
- return 'Run: harness-dev validate to re-check';
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-dev phase ${order[idx + 1]}`;
165
+ return `Run: dev-harness phase ${order[idx + 1]}`;
166
166
  }
167
- return `Run: harness-dev validate`;
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-dev validate — check current phase
9
- * harness-dev validate --json — machine-readable output
10
- * harness-dev validate --phase X — check specific phase
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-dev validate
13
+ * dev-harness validate
14
14
  * # → BUILD Gate: PASS — 3/3 checks pass
15
15
  *
16
- * harness-dev validate --json
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-dev config set gates.enabled true\n');
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-dev init or harness-dev phase <name>',
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-dev worktree <subcommand> [name] [options]
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-dev worktree ${SUBCOMMANDS.join('|')}`, EXIT.USAGE_ERROR), json);
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-dev init';
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-dev worktree create <name>', EXIT.USAGE_ERROR), json);
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-dev.mjs', import.meta.url).pathname;
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-dev worktree remove <name>', EXIT.USAGE_ERROR), json);
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-dev — Agent-agnostic development harness CLI.
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-dev --help`,
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-dev config list` — interactive parameter listing
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-dev phase — do not edit.',
201
+ description: 'Current pipeline phase. Managed by dev-harness phase — do not edit.',
202
202
  default: null,
203
203
  editable: false,
204
204
  },
@@ -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-dev contract propose first', escalated: false };
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-dev contract propose',
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-dev contract review`,
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
@@ -5,7 +5,7 @@
5
5
  * that return { name, pass, detail }.
6
6
  *
7
7
  * Phase gates are disabled by default (gates.enabled: false).
8
- * Run via: harness-dev validate
8
+ * Run via: dev-harness validate
9
9
  *
10
10
  * Usage:
11
11
  * import { runChecks, getPhase } from './gates.mjs';
@@ -18,6 +18,7 @@ import { loadConfig } from './state.mjs';
18
18
  import { getStackMeta, detectStack } from './detect-stack.mjs';
19
19
  import { validateContract } from './contract.mjs';
20
20
  import { execGitCheck as execCheck } from './git.mjs';
21
+ import { CONFIG_PATH, RUBRIC_PATH, ARCHITECTURE_PATH, DECISIONS_PATH, HARNESS_DIR } from './paths.mjs';
21
22
  import { COVERAGE_TIMEOUT, COVERAGE_THRESHOLD_DEFAULT } from './constants.mjs';
22
23
 
23
24
  function getStackLabel(targetDir) {
@@ -38,19 +39,20 @@ function checkGitRepo(targetDir) {
38
39
  }
39
40
 
40
41
  function checkConfigExists(targetDir) {
41
- const cfgPath = resolve(targetDir, 'harness-config.json');
42
+ const cfgPath = CONFIG_PATH(targetDir);
42
43
  const exists = existsSync(cfgPath);
43
44
  return {
44
45
  name: 'config-exists',
45
46
  pass: exists,
46
- detail: exists ? 'harness-config.json present' : 'Missing: harness-config.json',
47
+ detail: exists ? 'harness/config.json present' : 'Missing: harness/config.json',
47
48
  };
48
49
  }
49
50
 
50
51
  function checkInitExecutable(targetDir) {
52
+ // init.sh is now at harness/scripts/init.sh
53
+ const initSh = resolve(HARNESS_DIR(targetDir), 'scripts', 'init.sh');
51
54
  // Windows has no POSIX executable bit — skip the exec-bit check there.
52
55
  if (process.platform === 'win32') {
53
- const initSh = resolve(targetDir, 'init.sh');
54
56
  return {
55
57
  name: 'init-executable',
56
58
  pass: existsSync(initSh),
@@ -58,7 +60,7 @@ function checkInitExecutable(targetDir) {
58
60
  };
59
61
  }
60
62
  try {
61
- const { exitCode } = execCheck('test -x init.sh', targetDir);
63
+ const { exitCode } = execCheck(`test -x "${initSh}"`, targetDir);
62
64
  return {
63
65
  name: 'init-executable',
64
66
  pass: exitCode === 0,
@@ -177,11 +179,11 @@ function checkContractAgreed(targetDir) {
177
179
 
178
180
  /** Check that evaluator-rubric.md exists in the project. */
179
181
  function checkRubricExists(targetDir) {
180
- const found = existsSync(resolve(targetDir, 'evaluator-rubric.md'));
182
+ const found = existsSync(RUBRIC_PATH(targetDir));
181
183
  return {
182
184
  name: 'rubric-exists',
183
185
  pass: found,
184
- detail: found ? 'evaluator-rubric.md found' : 'evaluator-rubric.md missing — run init to scaffold',
186
+ detail: found ? 'harness/evaluator-rubric.md found' : 'harness/evaluator-rubric.md missing — run init to scaffold',
185
187
  };
186
188
  }
187
189
 
@@ -291,7 +293,7 @@ function checkChangelogContent(targetDir) {
291
293
 
292
294
  /** Check that ARCHITECTURE.md is filled in (if file exists, not just stub). */
293
295
  function checkArchitectureDoc(targetDir) {
294
- const archPath = resolve(targetDir, 'ARCHITECTURE.md');
296
+ const archPath = ARCHITECTURE_PATH(targetDir);
295
297
  if (!existsSync(archPath)) {
296
298
  return { name: 'architecture-doc', pass: true, detail: 'ARCHITECTURE.md not present (optional)' };
297
299
  }
@@ -304,7 +306,7 @@ function checkArchitectureDoc(targetDir) {
304
306
 
305
307
  /** Check that DECISIONS.md has at least one recorded decision (if file exists). */
306
308
  function checkDecisionsLogged(targetDir) {
307
- const decPath = resolve(targetDir, 'DECISIONS.md');
309
+ const decPath = DECISIONS_PATH(targetDir);
308
310
  if (!existsSync(decPath)) {
309
311
  return { name: 'decisions-logged', pass: true, detail: 'DECISIONS.md not present (optional)' };
310
312
  }