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
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-dev <command> [options]
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.5';
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-dev <command> [options]',
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-dev v${VERSION}`;
114
+ return `dev-harness v${VERSION}`;
115
115
  }
116
116
 
117
- // Per-command help text (for `harness-dev <command> --help`).
117
+ // Per-command help text (for `dev-harness <command> --help`).
118
118
  const COMMAND_HELP = {
119
- init: `Usage: harness-dev init [--stack <name>] [--target <dir>] [--force] [--no-git] [--json]
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-dev status [--target <dir>] [--json]
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-dev phase <name> [--target <dir>] [--git-ops] [--json]
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-dev validate [--phase <name>] [--feature <id> --task <id>] [--target <dir>] [--json]
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-dev set-mode <copilot|autopilot> [--target <dir>] [--json]
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-dev config list [--target <dir>] [--json]
167
- harness-dev config get [key] [--target <dir>] [--json]
168
- harness-dev config set <key> <value> [--target <dir>] [--json]
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-dev config list
176
- harness-dev config list --json
177
- harness-dev config get gates.enabled
178
- harness-dev config set gates.enabled true
179
- harness-dev config set mode autopilot
180
- harness-dev config set maxRetries 5`,
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-dev pause [--target <dir>] [--json]
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-dev resume [--target <dir>] [--json]
186
+ resume: `Usage: dev-harness resume [--target <dir>] [--json]
187
187
 
188
188
  Resume autopilot execution.`,
189
189
 
190
- learn: `Usage: harness-dev learn "<message>" [--target <dir>] [--json]
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-dev contract <subcommand> [options] [--target <dir>] [--json]
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-dev worktree <subcommand> [options] [--target <dir>] [--json]
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-dev rollback <subcommand> [checkpoint] [--target <dir>] [--json]
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-dev checkpoint create <label> [--force] [--target <dir>] [--json]
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-dev detect-tool [--target <dir>] [--json]
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-dev help
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
- * Path to a project's harness-config.json.
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, 'harness-config.json');
63
+ return resolve(HARNESS_DIR(targetDir), 'config.json');
48
64
  }
49
65
 
50
66
  /**
51
- * Path to a project's feature_list.json.
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, 'feature_list.json');
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
  }
@@ -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-dev validate --feature ${feature.id} --task ${task.id}\n`);
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-dev validate\n`);
301
+ process.stdout.write(`Run: dev-harness validate\n`);
302
302
  process.stdout.write(`═══════════════════════════════════════\n`);
303
303
 
304
304
  return {
@@ -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-dev phase ${nextPhase}`;
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-dev resume`;
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-dev phase <name>` while in autopilot mode.
208
+ * by calling `dev-harness phase <name>` while in autopilot mode.
209
209
  *
210
210
  * @param {string} targetDir
211
211
  * @param {object} [options]
@@ -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-dev validate\n`;
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;
@@ -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 5 template-based ones.
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
- 'feature_list.json': JSON.stringify({
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-config.json\` exists and valid
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
- - [ ] \`feature_list.json\` up-to-date
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 scaffold
421
- harness-config.json
422
- feature_list.json
423
- feature-list.schema.json
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
@@ -124,7 +124,7 @@ export function loadConfig(targetDir) {
124
124
  config: defaults,
125
125
  path: cfgPath,
126
126
  ok: false,
127
- error: `Not found: ${cfgPath}. Run: harness-dev init`,
127
+ error: `Not found: ${cfgPath}. Run: dev-harness init`,
128
128
  };
129
129
  }
130
130
 
@@ -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 outPath = join(target, relativePath);
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.5",
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-dev": "cli/harness-dev.mjs"
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
- "check": "node --check cli/harness-dev.mjs && echo 'Syntax OK'",
41
- "postinstall": "node -e \"try{process.stdout.write('harness-dev installed. Run: npx harness-dev --help\\n')}catch(e){}\""
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
+ }
@@ -3,9 +3,9 @@
3
3
  ## Quick Start
4
4
 
5
5
  ```bash
6
- harness-dev status # Where are we?
7
- harness-dev phase <name> # Invoke a phase
8
- harness-dev validate # Check gate criteria
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
- | `harness-config.json` | Config + state |
37
- | `feature_list.json` | Feature list with passes |
38
- | `progress.md` | Session state + lessons |
39
- | `sprint-contract.md` | Pre-build agreement |
40
- | `init.sh` | Install verify → start |
41
- | `evaluator-rubric.md` | Quality scorecard (6 dimensions, 0-2) |
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-dev validate` after each phase
50
- 6. Fresh context per retry — pass `--git-ops` to `harness-dev phase <name>` to auto-reset the working tree on retry (off by default; agent-agnostic)
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.