hardness 1.0.0 → 1.1.2

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 (52) hide show
  1. package/AGENTS.md +11 -0
  2. package/CHANGELOG.md +36 -0
  3. package/README.md +62 -15
  4. package/node_modules/@hardness/analyzers/package.json +1 -1
  5. package/node_modules/@hardness/core/dist/common/paths.js +2 -2
  6. package/node_modules/@hardness/core/dist/common/paths.js.map +1 -1
  7. package/node_modules/@hardness/core/package.json +1 -1
  8. package/node_modules/@hardness/prompts/package.json +1 -1
  9. package/package.json +1 -1
  10. package/packages/analyzers/package.json +1 -1
  11. package/packages/cli/dist/commands/discover.js +47 -6
  12. package/packages/cli/dist/commands/discover.js.map +1 -1
  13. package/packages/cli/dist/commands/plan.js +39 -6
  14. package/packages/cli/dist/commands/plan.js.map +1 -1
  15. package/packages/cli/dist/commands/spec.js +34 -6
  16. package/packages/cli/dist/commands/spec.js.map +1 -1
  17. package/packages/cli/dist/dispatcher.d.ts +2 -0
  18. package/packages/cli/dist/dispatcher.js +63 -62
  19. package/packages/cli/dist/dispatcher.js.map +1 -1
  20. package/packages/cli/dist/generators/prd-generator.d.ts +14 -0
  21. package/packages/cli/dist/generators/prd-generator.js +164 -0
  22. package/packages/cli/dist/generators/prd-generator.js.map +1 -0
  23. package/packages/cli/dist/generators/spec-generator.d.ts +35 -0
  24. package/packages/cli/dist/generators/spec-generator.js +245 -0
  25. package/packages/cli/dist/generators/spec-generator.js.map +1 -0
  26. package/packages/cli/dist/generators/sprint-generator.d.ts +51 -0
  27. package/packages/cli/dist/generators/sprint-generator.js +162 -0
  28. package/packages/cli/dist/generators/sprint-generator.js.map +1 -0
  29. package/packages/cli/dist/index.js +1 -1
  30. package/packages/cli/dist/interview/evaluator-prompt.d.ts +9 -0
  31. package/packages/cli/dist/interview/evaluator-prompt.js +192 -0
  32. package/packages/cli/dist/interview/evaluator-prompt.js.map +1 -0
  33. package/packages/cli/dist/interview/evaluator.d.ts +46 -0
  34. package/packages/cli/dist/interview/evaluator.js +142 -0
  35. package/packages/cli/dist/interview/evaluator.js.map +1 -0
  36. package/packages/cli/dist/interview/questions.d.ts +29 -0
  37. package/packages/cli/dist/interview/questions.js +642 -0
  38. package/packages/cli/dist/interview/questions.js.map +1 -0
  39. package/packages/cli/dist/interview/runner.d.ts +14 -0
  40. package/packages/cli/dist/interview/runner.js +327 -0
  41. package/packages/cli/dist/interview/runner.js.map +1 -0
  42. package/packages/cli/dist/interview/suggestions.d.ts +6 -0
  43. package/packages/cli/dist/interview/suggestions.js +230 -0
  44. package/packages/cli/dist/interview/suggestions.js.map +1 -0
  45. package/packages/cli/dist/interview/types.d.ts +46 -0
  46. package/packages/cli/dist/interview/types.js +50 -0
  47. package/packages/cli/dist/interview/types.js.map +1 -0
  48. package/packages/cli/package.json +1 -1
  49. package/packages/core/dist/common/paths.js +2 -2
  50. package/packages/core/dist/common/paths.js.map +1 -1
  51. package/packages/core/package.json +1 -1
  52. package/packages/prompts/package.json +1 -1
package/AGENTS.md CHANGED
@@ -60,6 +60,17 @@ Read it carefully. Your `agentCommand` receives the **path** to this file via `{
60
60
  claude -p "$(cat {context_file})"
61
61
  ```
62
62
 
63
+ ### About `specExcerpt`
64
+
65
+ You do **not** receive the entire `SPEC.md` — only the line ranges referenced by `feature.specLines`. These ranges are selected by the sprint author to give you:
66
+ - The specific requirement(s) you must implement (from section 7 of the SPEC)
67
+ - The relevant module table (section 3) showing where your code lives
68
+ - The relevant data model (section 4) showing the types/interfaces to use
69
+ - The relevant flow (section 5) showing how your feature fits the pipeline
70
+ - Integration contracts (section 6) showing templates, schemas and output paths
71
+
72
+ If you feel you need more context from `SPEC.md` than what `specExcerpt` provides, do **not** read `SPEC.md` directly — instead, note the gap in your output and the sprint author will adjust `specLines` on the next iteration.
73
+
63
74
  ## Gates you will be measured against
64
75
 
65
76
  The orchestrator runs these gates after you return. You don't invoke them — but you must produce code that passes them.
package/CHANGELOG.md CHANGED
@@ -5,8 +5,44 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.1.1] — 2026-07-05
9
+
10
+ ### Added
11
+ - **Automated versioning**: added `scripts/bump-version.js` supporting `major`, `minor`, and `patch` bumps.
12
+ - **Agent workspace rules**: added `.agents/AGENTS.md` to enforce version bumps, builds, and tags before committing.
13
+ - **CI/CD Release workflow**: configured GitHub Action `.github/workflows/release.yml` to compile, test, and release to GitHub/NPM on tag push.
14
+ - **Changelog extraction formatting**: Skip `###` subheaders and clean consecutive newlines in extracted release notes to match the user-friendly format of previous releases.
15
+
16
+ ## [1.1.0] — 2026-07-05
17
+
18
+ ### Added — Milestone 4: Automated Pipeline
19
+
20
+ - **`hardness discover`**: fully interactive 6-phase PRD interview (Vision → People → Features → Constraints → Boundaries → Review). Deterministic — no LLM required. Suggestions adapt to detected stack and project type. Ctrl+C saves partial session to `.hardness/discover-session.json`.
21
+ - **Interview engine** (`packages/cli/src/interview/`):
22
+ - `types.ts`: `InterviewState`, `ProjectType`, `StackDetection`, `SuggestionEngine` interfaces. `detectStack()` extracted as a shared utility.
23
+ - `suggestions.ts`: static suggestion engine for all 7 project types (web-app, api, cli, library, desktop, mobile, other) with stack-aware deployment overrides.
24
+ - `questions.ts`: 22 questions across 6 phases (Vision=4, People=3, Features=3, Constraints=8, Boundaries=3, Review=2). `LineQueue`-based runner for reliable test isolation.
25
+ - `runner.ts`: readline loop with `?`-for-suggestion, empty-input defaults, multiline support, and graceful SIGINT handling.
26
+ - **`hardness spec`**: reads `PRD.md`, parses all 7 sections, maps functional requirements to technical requirements, renders `SPEC.md` from template. Stack-aware test framework detection (Vitest / pytest / go test / cargo test).
27
+ - **SPEC generator** (`packages/cli/src/generators/spec-generator.ts`): `parsePrd()` + `generateSpec()` with full PRD section parsing.
28
+ - **`hardness plan`**: reads `SPEC.md`, parses section 7 technical requirements, groups into sprints of 2-4 features, derives `files`, `acceptanceCriteria`, `hints`, `verification` per feature, writes `NN-name.json` + `00-index.json` + updates `current.txt`.
29
+ - **Sprint generator** (`packages/cli/src/generators/sprint-generator.ts`): `parseSpec()` + `generateSprints()` with dryRun mode.
30
+ - **PRD generator** (`packages/cli/src/generators/prd-generator.ts`): renders structured `PRD.md` from `InterviewState` following `PRD-TEMPLATE.md`.
31
+
32
+ ### Changed
33
+
34
+ - **Dispatcher auto-trigger** (`dispatcher.ts`): bare `hardness` now auto-routes: empty dir → `discover`, `PRD.md` but no `SPEC.md` → `spec`, `SPEC.md` but no sprints → `plan`. Existing Hardness projects continue to show status.
35
+ - **`dispatcher.ts`**: `detectStack()` and `StackDetection` extracted to `interview/types.ts` for sharing across modules.
36
+ - **README.md** + 8 language translations: removed all "(planned, M4)" labels from pipeline table and status callouts. `discover`, `spec`, `plan` are now fully implemented.
37
+
38
+ ### Tests
39
+
40
+ - Added 29 new tests across: `runner.test.ts`, `prd-generator.test.ts`, `spec-generator.test.ts`, `sprint-generator.test.ts`, `dispatcher.test.ts` (M4 scenarios), `index.test.ts` (stub removal verification).
41
+ - Total: **126 tests** passing (up from 84 at 1.0.0).
42
+
8
43
  ## [1.0.0] — 2026-07-04
9
44
 
45
+
10
46
  ### Added
11
47
  - Initial public release of Hardness — Universal Agentic Development Harness.
12
48
  - **CLI** (`hardness`): `init`, `validate`, `score`, `status`, `run`, `audit`, `dry-run`, plus stubs for `discover`, `spec`, `plan` (M4).
package/README.md CHANGED
@@ -49,19 +49,45 @@ hardness init --preset manual
49
49
 
50
50
  ## Quick start
51
51
 
52
+ The intended flow is **fully guided** — you only need to describe your idea, and Hardness takes care of the rest:
53
+
52
54
  ```bash
53
- # 1. Initialize Hardness in your project
54
- npx hardness@latest init --preset manual
55
+ # 1. Run Hardness in an empty directory
56
+ npx hardness@latest
57
+
58
+ # 2. Hardness interviews you (interactive PRD questionnaire)
59
+ # — you answer questions about your project
60
+ # — when you don't know, Hardness suggests best practices for your stack
61
+ # — produces PRD.md with your business rules
62
+
63
+ # 3. Hardness generates everything automatically:
64
+ # PRD.md → SPEC.md → sprint JSONs → .hardness/current.txt
65
+
66
+ # 4. Run the loop — the agent implements, gates validate, features advance
67
+ npx hardness@latest run
68
+
69
+ # 5. Final static-analysis audit
70
+ npx hardness@latest audit
71
+ ```
72
+
73
+ > Running `hardness` in an empty directory launches the interactive PRD interview; with a `PRD.md` it runs `spec`; with a `SPEC.md` it runs `plan`. You can also use `init`, `validate`, `run` and `audit` with hand-written sprints (see `templates/`).
74
+
75
+ ### Already have a project?
76
+
77
+ ```bash
78
+ npx hardness@latest # detects your stack and suggests an adequacy plan
79
+ npx hardness@latest init --preset claude-code
80
+ npx hardness@latest run
81
+ npx hardness@latest audit
82
+ ```
55
83
 
56
- # 2. Write your sprints under .hardness/sprints/
57
- # 3. Point .hardness/current.txt to the first sprint
84
+ ### Optional checks along the way
58
85
 
59
- npx hardness validate # check sprint structure
60
- npx hardness score # quality score 0–10
61
- npx hardness status # where am I?
62
- npx hardness dry-run # simulate without invoking the agent
63
- npx hardness run # real loop: agent gates → advance
64
- npx hardness audit # final static-analysis report
86
+ ```bash
87
+ npx hardness@latest validate # check sprint structure
88
+ npx hardness@latest score # quality score 0–10
89
+ npx hardness@latest status # where am I?
90
+ npx hardness@latest dry-run # simulate without invoking the agent
65
91
  ```
66
92
 
67
93
  ## The 6-phase pipeline
@@ -72,14 +98,14 @@ npx hardness audit # final static-analysis report
72
98
 
73
99
  | Phase | Command | What happens |
74
100
  |---|---|---|
75
- | Discover | `hardness discover` | Interview → `PRD.md` *(planned, M4)* |
76
- | Spec | `hardness spec` | `PRD.md` → `SPEC.md` *(planned, M4)* |
77
- | Plan | `hardness plan` | `SPEC.md` → sprint JSON files *(planned, M4)* |
101
+ | Discover | `hardness discover` | Interview → `PRD.md` |
102
+ | Spec | `hardness spec` | `PRD.md` → `SPEC.md` |
103
+ | Plan | `hardness plan` | `SPEC.md` → sprint JSON files |
78
104
  | Validate | `hardness validate` | Mechanical structural validation of sprints |
79
105
  | Run | `hardness run` | Orchestrator loop: agent → gates → advance |
80
106
  | Audit | `hardness audit` | Static analysis per stack profile, Markdown + JSON report |
81
107
 
82
- Today `validate`, `score`, `status`, `run`, `audit` and `dry-run` are fully implemented. `discover`, `spec`, `plan` are stubs landing in M4 for now you can write `SPEC.md` and sprint JSONs by hand using the templates in `templates/`.
108
+ All commands are fully implemented. `discover` runs an interactive PRD interview, `spec` converts PRD.md to SPEC.md, and `plan` generates sprint JSON files. You can also write `SPEC.md` and sprint JSONs by hand using the templates in `templates/`.
83
109
 
84
110
  ## Commands
85
111
 
@@ -89,7 +115,7 @@ Running `hardness` with **no arguments** triggers an intelligent dispatcher:
89
115
  2. **Existing project detected** (a `package.json`, `go.mod`, `Cargo.toml`, `pyproject.toml` or source files are present) — presents an **adequacy plan**: detected stack, recommended `stackProfile`, and the sequence `init → validate → run → audit`.
90
116
  3. **Empty / new directory** — launches the **PRD interview** (`discover`), an interactive questionnaire that captures business rules and produces `PRD.md`, then proceeds automatically to `spec` (PRD→SPEC), `plan` (SPEC→sprints), `validate`, `run` and `audit`.
91
117
 
92
- > The PRD interview (`discover`) and automated `spec`/`plan` are landing in M4. Today the dispatcher prints a guided manual path for the empty-directory case.
118
+
93
119
 
94
120
  | Command | Description |
95
121
  |---|---|
@@ -264,6 +290,27 @@ npm test
264
290
 
265
291
  See [`CONTRIBUTING.md`](docs/CONTRIBUTING.md) for details.
266
292
 
293
+ ## Acknowledgements
294
+
295
+ This project exists thanks to people and projects who helped shape it, directly or indirectly:
296
+
297
+ - **Bruno Galego** — [augustogalego.com](https://www.augustogalego.com/)
298
+ - **Breno Vieira** — [lionlabs.com.br](https://www.lionlabs.com.br/)
299
+ - **OpenSpec** — [openspec.dev](https://openspec.dev/)
300
+ - **Sandeco** — [github.com/sandeco](https://github.com/sandeco)
301
+ - **Spec-Kit (GitHub Spec Kit)** — [github.com/github/spec-kit](https://github.com/github/spec-kit)
302
+ - **Waldemar Neto** — [techleads.club](https://www.techleads.club/)
303
+ - **My wife** — for the patience, support and countless cans of Coke Zero
304
+ - **The cold of my city** — for keeping me indoors and coding
305
+
306
+ ## Contributing
307
+
308
+ Found a bug or have a suggestion? We'd love to hear from you:
309
+
310
+ - 🐛 **Report a bug** — [GitHub Issues](https://github.com/cmt-t/HardNess/issues)
311
+ - 💡 **Suggest a feature** — [GitHub Issues](https://github.com/cmt-t/HardNess/issues/new)
312
+ - 🔧 **Submit a pull request** — see [`CONTRIBUTING.md`](docs/CONTRIBUTING.md)
313
+
267
314
  ## License
268
315
 
269
316
  [MIT](LICENSE) © 2026 cmt-t
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardness/analyzers",
3
- "version": "1.0.0",
3
+ "version": "1.1.2",
4
4
  "description": "Hardness Stack Analyzers — static analysis registry per stack profile",
5
5
  "author": "cmt-t",
6
6
  "license": "MIT",
@@ -33,8 +33,8 @@ export function normalizeProjectRel(filePath, customRoot) {
33
33
  if (relative.startsWith('..') || path.isAbsolute(relative)) {
34
34
  throw new Error(`Path "${filePath}" is outside the project root "${root}"`);
35
35
  }
36
- // Normalize path separators to '/'
37
- return relative.split(path.sep).join('/');
36
+ // Normalize path separators to '/' (handle both \\ and / regardless of OS)
37
+ return relative.split(path.sep).join('/').replace(/\\/g, '/');
38
38
  }
39
39
  /**
40
40
  * Returns the list of protected paths/directories that the agent cannot modify.
@@ -1 +1 @@
1
- {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/common/paths.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB;;;GAGG;AACH,MAAM,UAAU,cAAc;IAC5B,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;AACrC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,sBAA8B,EAAE,UAAmB;IACjF,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC;IACtE,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAAC;IAEhE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAEnD,yEAAyE;IACzE,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,6BAA6B,sBAAsB,0BAA0B,IAAI,GAAG,CAAC,CAAC;IACxG,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB,EAAE,UAAmB;IACvE,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC;IACtE,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAEnD,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,SAAS,QAAQ,kCAAkC,IAAI,GAAG,CAAC,CAAC;IAC9E,CAAC;IAED,mCAAmC;IACnC,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO;QACL,oBAAoB;QACpB,mBAAmB;QACnB,kBAAkB;QAClB,YAAY;KACb,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,sBAA8B,EAAE,UAAmB;IACjF,IAAI,YAAoB,CAAC;IACzB,IAAI,CAAC;QACH,YAAY,GAAG,mBAAmB,CAAC,sBAAsB,EAAE,UAAU,CAAC,CAAC;IACzE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,sDAAsD;QACtD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,iBAAiB,GAAG,oBAAoB,EAAE,CAAC;IAEjD,mDAAmD;IACnD,IACE,YAAY,KAAK,WAAW;QAC5B,YAAY,KAAK,uBAAuB;QACxC,YAAY,KAAK,uBAAuB,EACxC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CACrC,YAAY,CAAC,UAAU,CAAC,MAAM,CAAC;QAC/B,CAAC,YAAY,GAAG,GAAG,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CACxC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/common/paths.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardness/core",
3
- "version": "1.0.0",
3
+ "version": "1.1.2",
4
4
  "description": "Hardness Core — orchestrator, gates, locks, config and security",
5
5
  "author": "cmt-t",
6
6
  "license": "MIT",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardness/prompts",
3
- "version": "1.0.0",
3
+ "version": "1.1.2",
4
4
  "description": "Hardness Prompts — role-based prompt templates for the agent",
5
5
  "author": "cmt-t",
6
6
  "license": "MIT",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hardness",
3
- "version": "1.0.0",
3
+ "version": "1.1.2",
4
4
  "description": "Universal Agentic Development Harness — governance, security, validation and audit for AI-assisted development, model/tool/IDE agnostic.",
5
5
  "author": "cmt-t",
6
6
  "license": "MIT",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardness/analyzers",
3
- "version": "1.0.0",
3
+ "version": "1.1.2",
4
4
  "description": "Hardness Stack Analyzers — static analysis registry per stack profile",
5
5
  "author": "cmt-t",
6
6
  "license": "MIT",
@@ -1,12 +1,53 @@
1
- import { logger } from '@hardness/core';
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import pc from 'picocolors';
4
+ import { logger, getProjectRoot } from '@hardness/core';
5
+ import { runInterview } from '../interview/runner.js';
6
+ import { generatePrd } from '../generators/prd-generator.js';
2
7
  export function discoverCommand(program) {
3
8
  program
4
9
  .command('discover')
5
- .description('Interactive interview to generate PRD.md (planned for M4)')
6
- .action(() => {
7
- logger.warn('`hardness discover` is not yet implemented (planned for Milestone 4).');
8
- logger.info('Use this flow instead: write PRD.md manually and run `hardness spec`.');
9
- process.exit(2);
10
+ .description('Interactive interview to generate PRD.md from your project requirements')
11
+ .option('--root <path>', 'Project root directory (defaults to cwd or HARDNESS_ROOT)')
12
+ .action(async (opts) => {
13
+ const root = opts.root ?? getProjectRoot();
14
+ // Check if a partial session exists
15
+ const sessionPath = path.join(root, '.hardness', 'discover-session.json');
16
+ if (fs.existsSync(sessionPath)) {
17
+ logger.info('Partial session found at .hardness/discover-session.json');
18
+ logger.info('Resuming from where you left off is not yet supported — starting fresh.');
19
+ }
20
+ // Check if PRD.md already exists
21
+ const prdPath = path.join(root, 'PRD.md');
22
+ if (fs.existsSync(prdPath)) {
23
+ logger.warn('PRD.md already exists. It will be overwritten.');
24
+ }
25
+ process.stdout.write(`\n${pc.bold(pc.green('Hardness — Interactive PRD Interview'))}\n`);
26
+ process.stdout.write(pc.dim('Answer each question to generate your PRD.md.\n' +
27
+ 'Type ? for a suggestion, or press Enter to accept the default.\n' +
28
+ 'Press Ctrl+C at any time to save and exit.\n\n'));
29
+ let state;
30
+ try {
31
+ state = await runInterview({ root });
32
+ }
33
+ catch (err) {
34
+ logger.error(`Interview failed: ${err.message}`);
35
+ process.exit(1);
36
+ }
37
+ process.stdout.write('\n');
38
+ logger.info('Generating PRD.md...');
39
+ try {
40
+ generatePrd(state, { outputPath: prdPath });
41
+ }
42
+ catch (err) {
43
+ logger.error(`PRD generation failed: ${err.message}`);
44
+ process.exit(1);
45
+ }
46
+ logger.success(`PRD.md created at: ${prdPath}`);
47
+ logger.info('');
48
+ logger.info('Next steps:');
49
+ logger.info(' • Review PRD.md and adjust as needed');
50
+ logger.info(' • Run `hardness spec` to generate SPEC.md');
10
51
  });
11
52
  }
12
53
  //# sourceMappingURL=discover.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/commands/discover.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAExC,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,OAAO;SACJ,OAAO,CAAC,UAAU,CAAC;SACnB,WAAW,CAAC,2DAA2D,CAAC;SACxE,MAAM,CAAC,GAAG,EAAE;QACX,MAAM,CAAC,IAAI,CAAC,uEAAuE,CAAC,CAAC;QACrF,MAAM,CAAC,IAAI,CAAC,uEAAuE,CAAC,CAAC;QACrF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACP,CAAC"}
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/commands/discover.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAE7D,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,OAAO;SACJ,OAAO,CAAC,UAAU,CAAC;SACnB,WAAW,CAAC,yEAAyE,CAAC;SACtF,MAAM,CAAC,eAAe,EAAE,2DAA2D,CAAC;SACpF,MAAM,CAAC,KAAK,EAAE,IAAuB,EAAE,EAAE;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,cAAc,EAAE,CAAC;QAE3C,oCAAoC;QACpC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,uBAAuB,CAAC,CAAC;QAC1E,IAAI,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAC/B,MAAM,CAAC,IAAI,CAAC,0DAA0D,CAAC,CAAC;YACxE,MAAM,CAAC,IAAI,CAAC,yEAAyE,CAAC,CAAC;QACzF,CAAC;QAED,iCAAiC;QACjC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC1C,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;QAChE,CAAC;QAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC,IAAI,CAAC,CAAC;QACzF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,EAAE,CAAC,GAAG,CACJ,iDAAiD;YACjD,kEAAkE;YAClE,gDAAgD,CACjD,CACF,CAAC;QAEF,IAAI,KAAK,CAAC;QACV,IAAI,CAAC;YACH,KAAK,GAAG,MAAM,YAAY,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,qBAAsB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3B,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QAEpC,IAAI,CAAC;YACH,WAAW,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC;QAC9C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,0BAA2B,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;YACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,CAAC,OAAO,CAAC,sBAAsB,OAAO,EAAE,CAAC,CAAC;QAChD,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC3B,MAAM,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;QACtD,MAAM,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -1,12 +1,45 @@
1
- import { logger } from '@hardness/core';
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { logger, getProjectRoot } from '@hardness/core';
4
+ import { generateSprints } from '../generators/sprint-generator.js';
2
5
  export function planCommand(program) {
3
6
  program
4
7
  .command('plan')
5
- .description('Consumes SPEC.md and generates sprints in .hardness/sprints/ (planned for M4)')
6
- .action(() => {
7
- logger.warn('`hardness plan` is not yet implemented (planned for Milestone 4).');
8
- logger.info('For now, create NN-name.json files in .hardness/sprints/ manually.');
9
- process.exit(2);
8
+ .description('Consumes SPEC.md and generates sprint JSON files in .hardness/sprints/')
9
+ .option('--root <path>', 'Project root directory (defaults to cwd or HARDNESS_ROOT)')
10
+ .option('--spec <path>', 'Path to SPEC.md (defaults to <root>/SPEC.md)')
11
+ .action(async (opts) => {
12
+ const root = opts.root ?? getProjectRoot();
13
+ const specPath = opts.spec ?? path.join(root, 'SPEC.md');
14
+ const sprintsDir = path.join(root, '.hardness', 'sprints');
15
+ if (!fs.existsSync(specPath)) {
16
+ logger.error(`SPEC.md not found at: ${specPath}`);
17
+ logger.info('Run `hardness spec` first to generate SPEC.md.');
18
+ process.exit(1);
19
+ }
20
+ if (fs.existsSync(sprintsDir) && fs.readdirSync(sprintsDir).length > 0) {
21
+ logger.warn('.hardness/sprints/ already contains files. They will be overwritten.');
22
+ }
23
+ logger.info(`Reading SPEC.md from: ${specPath}`);
24
+ const specContent = fs.readFileSync(specPath, 'utf-8');
25
+ logger.info('Generating sprint files...');
26
+ let result;
27
+ try {
28
+ result = generateSprints(specContent, { root, sprintsDir });
29
+ }
30
+ catch (err) {
31
+ logger.error(`Sprint generation failed: ${err.message}`);
32
+ process.exit(1);
33
+ }
34
+ logger.success(`Generated ${result.sprints.length} sprint(s) in .hardness/sprints/`);
35
+ result.sprintFiles.forEach((f, i) => {
36
+ logger.info(` Sprint ${i + 1}: ${f} (${result.sprints[i].features.length} features)`);
37
+ });
38
+ logger.info('');
39
+ logger.info('Next steps:');
40
+ logger.info(' • Review .hardness/sprints/ and adjust as needed');
41
+ logger.info(' • Run `hardness validate` to check sprint structure');
42
+ logger.info(' • Run `hardness run` to start executing features');
10
43
  });
11
44
  }
12
45
  //# sourceMappingURL=plan.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/commands/plan.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAExC,MAAM,UAAU,WAAW,CAAC,OAAgB;IAC1C,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,+EAA+E,CAAC;SAC5F,MAAM,CAAC,GAAG,EAAE;QACX,MAAM,CAAC,IAAI,CAAC,mEAAmE,CAAC,CAAC;QACjF,MAAM,CAAC,IAAI,CAAC,oEAAoE,CAAC,CAAC;QAClF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACP,CAAC"}
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/commands/plan.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAEpE,MAAM,UAAU,WAAW,CAAC,OAAgB;IAC1C,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,wEAAwE,CAAC;SACrF,MAAM,CAAC,eAAe,EAAE,2DAA2D,CAAC;SACpF,MAAM,CAAC,eAAe,EAAE,8CAA8C,CAAC;SACvE,MAAM,CAAC,KAAK,EAAE,IAAsC,EAAE,EAAE;QACvD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,cAAc,EAAE,CAAC;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACzD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;QAE3D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,MAAM,CAAC,KAAK,CAAC,yBAAyB,QAAQ,EAAE,CAAC,CAAC;YAClD,MAAM,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;YAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvE,MAAM,CAAC,IAAI,CAAC,sEAAsE,CAAC,CAAC;QACtF,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,yBAAyB,QAAQ,EAAE,CAAC,CAAC;QACjD,MAAM,WAAW,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEvD,MAAM,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;QAE1C,IAAI,MAAM,CAAC;QACX,IAAI,CAAC;YACH,MAAM,GAAG,eAAe,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;QAC9D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,6BAA8B,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;YACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,CAAC,OAAO,CAAC,aAAa,MAAM,CAAC,OAAO,CAAC,MAAM,kCAAkC,CAAC,CAAC;QACrF,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAClC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,YAAY,CAAC,CAAC;QACzF,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC3B,MAAM,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAC;QAClE,MAAM,CAAC,IAAI,CAAC,uDAAuD,CAAC,CAAC;QACrE,MAAM,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -1,12 +1,40 @@
1
- import { logger } from '@hardness/core';
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { logger, getProjectRoot } from '@hardness/core';
4
+ import { generateSpec } from '../generators/spec-generator.js';
2
5
  export function specCommand(program) {
3
6
  program
4
7
  .command('spec')
5
- .description('Consumes PRD.md and generates SPEC.md (planned for M4)')
6
- .action(() => {
7
- logger.warn('`hardness spec` is not yet implemented (planned for Milestone 4).');
8
- logger.info('For now, write SPEC.md manually following templates/SPEC-TEMPLATE.md.');
9
- process.exit(2);
8
+ .description('Consumes PRD.md and generates SPEC.md')
9
+ .option('--root <path>', 'Project root directory (defaults to cwd or HARDNESS_ROOT)')
10
+ .option('--prd <path>', 'Path to PRD.md (defaults to <root>/PRD.md)')
11
+ .action(async (opts) => {
12
+ const root = opts.root ?? getProjectRoot();
13
+ const prdPath = opts.prd ?? path.join(root, 'PRD.md');
14
+ const specPath = path.join(root, 'SPEC.md');
15
+ if (!fs.existsSync(prdPath)) {
16
+ logger.error(`PRD.md not found at: ${prdPath}`);
17
+ logger.info('Run `hardness discover` first to generate PRD.md.');
18
+ process.exit(1);
19
+ }
20
+ if (fs.existsSync(specPath)) {
21
+ logger.warn('SPEC.md already exists. It will be overwritten.');
22
+ }
23
+ logger.info(`Reading PRD.md from: ${prdPath}`);
24
+ const prdContent = fs.readFileSync(prdPath, 'utf-8');
25
+ logger.info('Generating SPEC.md...');
26
+ try {
27
+ generateSpec(prdContent, { outputPath: specPath });
28
+ }
29
+ catch (err) {
30
+ logger.error(`SPEC generation failed: ${err.message}`);
31
+ process.exit(1);
32
+ }
33
+ logger.success(`SPEC.md created at: ${specPath}`);
34
+ logger.info('');
35
+ logger.info('Next steps:');
36
+ logger.info(' • Review SPEC.md and adjust technical requirements');
37
+ logger.info(' • Run `hardness plan` to generate sprint files');
10
38
  });
11
39
  }
12
40
  //# sourceMappingURL=spec.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"spec.js","sourceRoot":"","sources":["../../src/commands/spec.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAExC,MAAM,UAAU,WAAW,CAAC,OAAgB;IAC1C,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,wDAAwD,CAAC;SACrE,MAAM,CAAC,GAAG,EAAE;QACX,MAAM,CAAC,IAAI,CAAC,mEAAmE,CAAC,CAAC;QACjF,MAAM,CAAC,IAAI,CAAC,uEAAuE,CAAC,CAAC;QACrF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACP,CAAC"}
1
+ {"version":3,"file":"spec.js","sourceRoot":"","sources":["../../src/commands/spec.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAE/D,MAAM,UAAU,WAAW,CAAC,OAAgB;IAC1C,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,uCAAuC,CAAC;SACpD,MAAM,CAAC,eAAe,EAAE,2DAA2D,CAAC;SACpF,MAAM,CAAC,cAAc,EAAE,4CAA4C,CAAC;SACpE,MAAM,CAAC,KAAK,EAAE,IAAqC,EAAE,EAAE;QACtD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,cAAc,EAAE,CAAC;QAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAE5C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,MAAM,CAAC,KAAK,CAAC,wBAAwB,OAAO,EAAE,CAAC,CAAC;YAChD,MAAM,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;YACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,MAAM,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;QACjE,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,wBAAwB,OAAO,EAAE,CAAC,CAAC;QAC/C,MAAM,UAAU,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAErD,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QAErC,IAAI,CAAC;YACH,YAAY,CAAC,UAAU,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAC;QACrD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,2BAA4B,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;YAClE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,CAAC,OAAO,CAAC,uBAAuB,QAAQ,EAAE,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC3B,MAAM,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC;QACpE,MAAM,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -1 +1,3 @@
1
+ import { detectStack, type StackDetection } from './interview/types.js';
2
+ export { detectStack, type StackDetection };
1
3
  export declare function runDispatcher(): void;
@@ -1,56 +1,30 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { logger, getProjectRoot } from '@hardness/core';
4
- const MANIFEST_MAP = {
5
- 'package.json': { stackProfile: 'typescript-node', label: 'Node.js / TypeScript' },
6
- 'tsconfig.json': { stackProfile: 'typescript-node', label: 'TypeScript' },
7
- 'go.mod': { stackProfile: 'generic', label: 'Go' },
8
- 'Cargo.toml': { stackProfile: 'generic', label: 'Rust' },
9
- 'pyproject.toml': { stackProfile: 'generic', label: 'Python' },
10
- 'requirements.txt': { stackProfile: 'generic', label: 'Python' },
11
- 'pom.xml': { stackProfile: 'generic', label: 'Java / Maven' },
12
- 'build.gradle': { stackProfile: 'generic', label: 'Java / Gradle' },
13
- 'build.gradle.kts': { stackProfile: 'generic', label: 'Kotlin / Gradle' },
14
- 'Gemfile': { stackProfile: 'generic', label: 'Ruby' },
15
- 'composer.json': { stackProfile: 'generic', label: 'PHP / Composer' },
16
- 'mix.exs': { stackProfile: 'generic', label: 'Elixir' },
17
- 'CMakeLists.txt': { stackProfile: 'generic', label: 'C / C++ (CMake)' },
18
- 'Makefile': { stackProfile: 'generic', label: 'Make' },
19
- };
20
- const SOURCE_EXTENSIONS = new Set([
21
- '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
22
- '.py', '.go', '.rs', '.java', '.kt', '.rb', '.php',
23
- '.cs', '.cpp', '.c', '.h', '.hpp', '.swift', '.scala',
24
- '.vue', '.svelte', '.astro',
25
- ]);
26
- function listTopLevelFiles(dir) {
27
- try {
28
- return fs
29
- .readdirSync(dir, { withFileTypes: true })
30
- .filter((e) => e.isFile())
31
- .map((e) => e.name)
32
- .filter((n) => !n.startsWith('.'));
33
- }
34
- catch {
35
- return [];
36
- }
37
- }
38
- function detectStack(root) {
39
- const files = listTopLevelFiles(root);
40
- for (const [manifest, info] of Object.entries(MANIFEST_MAP)) {
41
- if (files.includes(manifest)) {
42
- return { manifest, stackProfile: info.stackProfile, label: info.label, files };
43
- }
44
- }
45
- const hasSource = files.some((f) => SOURCE_EXTENSIONS.has(path.extname(f)));
46
- if (hasSource) {
47
- return { manifest: null, stackProfile: 'generic', label: 'Generic (source files detected)', files };
48
- }
49
- return { manifest: null, stackProfile: 'generic', label: 'Empty / unknown', files };
50
- }
4
+ import { detectStack } from './interview/types.js';
5
+ export { detectStack };
6
+ // ---------------------------------------------------------------------------
7
+ // State detection helpers
8
+ // ---------------------------------------------------------------------------
51
9
  function hasHardness(root) {
52
10
  return fs.existsSync(path.join(root, '.hardness', 'config.json'));
53
11
  }
12
+ function hasPrd(root) {
13
+ return fs.existsSync(path.join(root, 'PRD.md'));
14
+ }
15
+ function hasSpec(root) {
16
+ return fs.existsSync(path.join(root, 'SPEC.md'));
17
+ }
18
+ function hasSprints(root) {
19
+ const sprintsDir = path.join(root, '.hardness', 'sprints');
20
+ if (!fs.existsSync(sprintsDir))
21
+ return false;
22
+ const files = fs.readdirSync(sprintsDir).filter((f) => f.endsWith('.json') && f !== '00-index.json');
23
+ return files.length > 0;
24
+ }
25
+ // ---------------------------------------------------------------------------
26
+ // Route handlers
27
+ // ---------------------------------------------------------------------------
54
28
  function routeExistingHardness(root) {
55
29
  const currentPath = path.join(root, '.hardness', 'current.txt');
56
30
  let active = '';
@@ -92,34 +66,61 @@ function routeExistingProject(detection) {
92
66
  logger.info(' 4. Run: hardness run');
93
67
  logger.info(' 5. Audit: hardness audit');
94
68
  logger.info('');
95
- logger.info('Tip: if you do not have a SPEC yet, run `hardness discover` (planned, M4)');
69
+ logger.info('Tip: if you do not have a SPEC yet, run `hardness discover`');
96
70
  logger.info(' to start an interactive PRD interview.');
97
71
  }
98
72
  function routeEmptyProject() {
99
73
  logger.info('No existing project detected in the current directory.');
100
74
  logger.info('');
101
- logger.warn('`hardness discover` (interactive PRD interview) is not yet implemented (planned for M4).');
102
- logger.info('');
103
- logger.info('For now you can:');
104
- logger.info(' 1. Initialize Hardness manually: hardness init --preset manual');
105
- logger.info(' 2. Write PRD.md and SPEC.md using templates/ as a guide');
106
- logger.info(' 3. Create sprint JSONs under .hardness/sprints/');
107
- logger.info(' 4. Point .hardness/current.txt to the first sprint');
108
- logger.info(' 5. Run: hardness run');
109
- logger.info('');
110
- logger.info('Once `discover` ships (M4), running `hardness` with no arguments in an');
111
- logger.info('empty directory will start an interactive PRD interview that guides you');
112
- logger.info('through capturing business rules, then proceeds to SPEC and sprint');
113
- logger.info('generation automatically.');
75
+ logger.info('Starting interactive PRD interview...');
76
+ logger.info('Run `hardness discover` to begin.');
114
77
  }
78
+ // ---------------------------------------------------------------------------
79
+ // Auto-trigger routing (M4)
80
+ // ---------------------------------------------------------------------------
81
+ function routeAutoTrigger(root, detection) {
82
+ // PRD exists but no SPEC → spec (takes priority over empty-dir check)
83
+ if (hasPrd(root) && !hasSpec(root)) {
84
+ return 'spec';
85
+ }
86
+ // SPEC exists but no sprints → plan
87
+ if (hasSpec(root) && !hasSprints(root)) {
88
+ return 'plan';
89
+ }
90
+ // Truly empty directory with no docs → discover
91
+ if (detection.label === 'Empty / unknown' && !hasPrd(root) && !hasSpec(root)) {
92
+ return 'discover';
93
+ }
94
+ return null;
95
+ }
96
+ // ---------------------------------------------------------------------------
97
+ // Main dispatcher
98
+ // ---------------------------------------------------------------------------
115
99
  export function runDispatcher() {
116
100
  const root = getProjectRoot();
101
+ // Existing Hardness project: always show status
117
102
  if (hasHardness(root)) {
118
103
  routeExistingHardness(root);
119
104
  return;
120
105
  }
121
106
  const detection = detectStack(root);
122
- if (detection.label !== 'Empty / unknown') {
107
+ const trigger = routeAutoTrigger(root, detection);
108
+ if (trigger === 'discover') {
109
+ routeEmptyProject();
110
+ return;
111
+ }
112
+ if (trigger === 'spec') {
113
+ logger.info('PRD.md found but no SPEC.md detected.');
114
+ logger.info('Run `hardness spec` to generate SPEC.md from PRD.md.');
115
+ return;
116
+ }
117
+ if (trigger === 'plan') {
118
+ logger.info('SPEC.md found but no sprint files detected.');
119
+ logger.info('Run `hardness plan` to generate sprint files from SPEC.md.');
120
+ return;
121
+ }
122
+ // Existing project (files detected) with no clear M4 trigger
123
+ if (detection.label !== 'Empty / unknown' || hasPrd(root) || hasSpec(root) || hasSprints(root)) {
123
124
  routeExistingProject(detection);
124
125
  return;
125
126
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../src/dispatcher.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../src/dispatcher.ts"],"names":[],"mappings":"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"}