@skyf0xx/hedgehog 0.1.5 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -31
- package/package.json +4 -1
- package/src/agents/bootstrap.md +110 -0
- package/src/agents/planner.md +30 -10
- package/src/skills/hedgehog-bootstrap/SKILL.md +224 -9
- package/src/templates/CLAUDE.md +17 -8
- package/src/templates/TODO.md +1 -1
package/README.md
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
|
-
# The Antidote to AI Spaghetti Code
|
|
1
|
+
# The Antidote to AI Spaghetti Code ⭐
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/skyf0xx/hedgehog/stargazers)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
AI writes code faster than humans ever could, but speed without discipline creates chaos.
|
|
6
6
|
|
|
7
|
-
Hedgehog gives AI the guard-rails
|
|
7
|
+
Hedgehog gives AI the guard-rails to build software that stays clean: structured workflows, opinionated architecture, composable skills, incremental build loops, and enforced quality gates.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- structured workflows
|
|
12
|
-
- opinionated architecture
|
|
13
|
-
- composable skills
|
|
14
|
-
- incremental build loops
|
|
15
|
-
- enforced quality gates
|
|
16
|
-
|
|
17
|
-
**Build faster, Save context**. Stay aligned. Ship software you can still understand six months later.
|
|
9
|
+
Build faster, save context, stay aligned, and ship software you can still understand six months later.
|
|
18
10
|
|
|
19
11
|

|
|
20
12
|
|
|
@@ -22,27 +14,22 @@ A complete development methodology combining:
|
|
|
22
14
|
|
|
23
15
|
1. An opinionated stack
|
|
24
16
|
2. An enforced build order
|
|
25
|
-
3. Agents and skills that make good engineering the default
|
|
17
|
+
3. Agents and skills that make good engineering the default
|
|
26
18
|
|
|
27
|
-
##
|
|
19
|
+
## Hedgehog's secret to great outcomes
|
|
28
20
|
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
21
|
+
- **Progressive layering:** types → schema → backend → UI, each layer built on a stable one beneath it
|
|
22
|
+
- **Small context loops:** decompose work into atomic, verifiable changes
|
|
23
|
+
- **Self-documenting architecture:** the codebase carries the context, not the AI
|
|
24
|
+
- **Traceable evolution:** decisions are preserved through conventional commits
|
|
33
25
|
|
|
34
26
|

|
|
35
27
|
|
|
36
28
|
## Why Hedgehog Exists
|
|
37
29
|
|
|
38
|
-
AI coding starts fast then breaks down.
|
|
39
|
-
|
|
40
|
-
Context accumulates, prompts get longer, architecture drifts.
|
|
41
|
-
|
|
42
|
-
Eventually, adding one more feature feels
|
|
43
|
-
dangerous.
|
|
30
|
+
AI coding starts fast, then breaks down. Context accumulates, prompts get longer, architecture drifts. Eventually, adding one more feature feels dangerous.
|
|
44
31
|
|
|
45
|
-
|
|
32
|
+
Hedgehog's answer: guardrails, not more discipline from the AI.
|
|
46
33
|
|
|
47
34
|
## Plans Expire. Structure Doesn't
|
|
48
35
|
|
|
@@ -115,7 +102,7 @@ AI becomes the builder operating inside those constraints — turning ideas into
|
|
|
115
102
|
|
|
116
103
|
## Architecture
|
|
117
104
|
|
|
118
|
-
Hedgehog is a package of agents and skills
|
|
105
|
+
Hedgehog is a package of agents and skills, built on an opinionated stack so the build order above is mechanical and enforced by the tooling itself:
|
|
119
106
|
|
|
120
107
|
| Layer | Choice | Why |
|
|
121
108
|
| --- | --- | --- |
|
|
@@ -124,6 +111,7 @@ Hedgehog is a package of agents and skills. An opinionated stack is used so the
|
|
|
124
111
|
| Backend | NestJS | Modules naturally mirror Hedgehog's build progression. |
|
|
125
112
|
| ORM | Drizzle + drizzle-zod | Database schema is the single source of truth. |
|
|
126
113
|
| Database | PostgreSQL | Simple, relational, predictable. |
|
|
114
|
+
| Local infra | Docker Compose | Postgres/Redis run identically on every machine. |
|
|
127
115
|
| Platform | Railway | Infrastructure is available from the first commit. |
|
|
128
116
|
| API contract | ts-rest | Contracts are code, not documentation. |
|
|
129
117
|
| Validation | Zod | One schema for runtime and compile time. |
|
|
@@ -140,11 +128,9 @@ Hedgehog is a package of agents and skills. An opinionated stack is used so the
|
|
|
140
128
|
|
|
141
129
|
## How Hedgehog Compares
|
|
142
130
|
|
|
143
|
-
Superpowers and BMAD both improve on raw prompting: one gives the AI good habits, the other gives it a planning process.
|
|
131
|
+
Superpowers and BMAD both improve on raw prompting: one gives the AI good habits, the other gives it a planning process. In both, the order of work is a convention the AI can still break.
|
|
144
132
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
Hedgehog **enforces its build order with tooling** instead: Nx module boundaries, commit hooks, phase gates. The order holds because the tooling holds it, not because the discipline was followed.
|
|
133
|
+
Hedgehog enforces its build order with tooling instead: Nx module boundaries, commit hooks, phase gates. The order holds because the tooling holds it, not because the AI followed the discipline.
|
|
148
134
|
|
|
149
135
|
| | Superpowers | BMAD | Hedgehog |
|
|
150
136
|
| --- | --- | --- | --- |
|
|
@@ -156,3 +142,9 @@ Hedgehog **enforces its build order with tooling** instead: Nx module boundaries
|
|
|
156
142
|
| **Context per step** | As much as the task pulls in | A full brief, PRD, and architecture doc per story | One module layer at a time (e.g. just the repository, just the controller) |
|
|
157
143
|
| **Finding a bug** | Search wherever the task touched | Search wherever the story touched | Search one layer, in one module, in a fixed order |
|
|
158
144
|
| **Real cost** | No safety net if the model shortcuts its own process | Documentation overhead most solo projects don't need | Less flexibility: the stack and order aren't negotiable |
|
|
145
|
+
|
|
146
|
+
## Support Hedgehog
|
|
147
|
+
|
|
148
|
+
If Hedgehog helps you build better AI software, consider giving it a ⭐ on GitHub.
|
|
149
|
+
|
|
150
|
+
[](https://github.com/skyf0xx/hedgehog/stargazers)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skyf0xx/hedgehog",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Install the Hedgehog build discipline (agents + skills) into a repo.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
"bin": {
|
|
14
14
|
"hedgehog": "bin/cli.mjs"
|
|
15
15
|
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"release": "npm version patch -m \"chore: bump version to %s\""
|
|
18
|
+
},
|
|
16
19
|
"files": [
|
|
17
20
|
"bin",
|
|
18
21
|
"src/agents",
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bootstrap
|
|
3
|
+
description: Use once per invocation, at the start of a new Hedgehog project, to run exactly ONE step of the hedgehog-bootstrap skill's 7-step scaffold, then hand off to a fresh instance of itself for the next step. Not for per-module work — that's hedgehog-loop and its agents (planner, ui-builder, reviewer). Skip entirely if nx.json already exists.
|
|
4
|
+
model: sonnet
|
|
5
|
+
color: green
|
|
6
|
+
tools: Read, Glob, Grep, Edit, Write, Bash
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
You are the bootstrap role in the Hedgehog discipline. `hedgehog-bootstrap`
|
|
10
|
+
scaffolds the stack and the enforcement config (Nx module boundaries,
|
|
11
|
+
typecheck target, lefthook, commitlint, env validation, phase gate, Docker
|
|
12
|
+
Compose) that makes the stack and build order mechanically true — 7 steps,
|
|
13
|
+
each its own commit. **You run exactly one step per invocation, then
|
|
14
|
+
stop.** Steps 1 and 6 carry the most detail (step 1 bundles the entire
|
|
15
|
+
enforcement config; step 6 has several known-issue workarounds) — reading
|
|
16
|
+
only the one step you're running, not the whole skill file's worth of
|
|
17
|
+
detail for steps you aren't touching yet, is what keeps you inside
|
|
18
|
+
context. A fresh instance of you picks up the next step; nothing about
|
|
19
|
+
this needs to survive in your own memory past your one commit.
|
|
20
|
+
|
|
21
|
+
You touch no domain modules — no schema, no contract, nothing under
|
|
22
|
+
`libs/<module>/`. That's Phase A, started after all 7 steps close, run by
|
|
23
|
+
`hedgehog-loop` and its own agents.
|
|
24
|
+
|
|
25
|
+
## Which step is yours
|
|
26
|
+
|
|
27
|
+
`TODO.md`'s `## Bootstrap` section has one checkbox per step, in order.
|
|
28
|
+
Before doing anything else:
|
|
29
|
+
|
|
30
|
+
1. Read `TODO.md`. Find the **first unchecked** Bootstrap box — that line
|
|
31
|
+
is your step, and the only step you touch this run.
|
|
32
|
+
2. Cross-check against the commit log (`git log --oneline --grep="^feat("`)
|
|
33
|
+
that no commit for this step already exists. TODO.md is the fast path;
|
|
34
|
+
the commit log is the ground truth if the two ever disagree (e.g. a
|
|
35
|
+
commit landed but the box wasn't checked). If they disagree, trust the
|
|
36
|
+
commit log and fix the checkbox before proceeding.
|
|
37
|
+
3. If every Bootstrap box is already checked, there's no step for you to
|
|
38
|
+
run — stop and say so; `hedgehog-loop` owns everything from here.
|
|
39
|
+
4. If `nx.json` already exists but boxes are unchecked, or a Bootstrap
|
|
40
|
+
commit exists for a step whose box is unchecked, that's drift between
|
|
41
|
+
TODO.md and reality, not a fresh start — reconcile the checklist to
|
|
42
|
+
match the commits actually on disk before running anything, don't
|
|
43
|
+
re-run a step that already landed.
|
|
44
|
+
|
|
45
|
+
## Running your one step
|
|
46
|
+
|
|
47
|
+
Open `hedgehog-bootstrap` and read **only the section for your step**
|
|
48
|
+
(plus "Before running" and "Enforcement wiring" if your step is step 1 —
|
|
49
|
+
that's where the enforcement config lives, bundled into step 1's commit).
|
|
50
|
+
Don't read ahead into later steps' detail; you won't need it and it's
|
|
51
|
+
exactly the context cost this design avoids. Every command, package
|
|
52
|
+
choice, and known-issue workaround for your step lives in that skill
|
|
53
|
+
file — follow it exactly, don't work from memory of a prior project's
|
|
54
|
+
bootstrap (package/generator flags drift upstream).
|
|
55
|
+
|
|
56
|
+
If your step is step 1: confirm Docker is available (`docker --version`)
|
|
57
|
+
first — stop and point to installing Docker Desktop/Engine if not, rather
|
|
58
|
+
than falling back to a native Postgres/Redis install. Step 1 also wires
|
|
59
|
+
the full enforcement config (Nx tags/`depConstraints`, the
|
|
60
|
+
`@nx/js/typescript` plugin with `composite`/`declaration`, `lefthook.yml`
|
|
61
|
+
+ `commitlint.config.js`, `packages/config/env.schema.ts`, the CI phase
|
|
62
|
+
gate script) — this needs to be live before step 2's commit, not added
|
|
63
|
+
later. After `nx init`, verify it didn't leave a stray
|
|
64
|
+
`package-lock.json` next to `pnpm-lock.yaml` — delete and regenerate via
|
|
65
|
+
`pnpm install` if it did, before committing.
|
|
66
|
+
|
|
67
|
+
If your step is step 7 (`apps/mobile`): check the scope boundary
|
|
68
|
+
`planner` produced at Intake first. Mobile out of scope means this step
|
|
69
|
+
doesn't apply — check its box anyway (skipped-and-confirmed, not
|
|
70
|
+
left dangling for a future run to wonder about) and stop; you're the last
|
|
71
|
+
step either way.
|
|
72
|
+
|
|
73
|
+
## Closing your step
|
|
74
|
+
|
|
75
|
+
1. Commit — exactly the message `hedgehog-bootstrap` specifies for your
|
|
76
|
+
step, once it compiles, lints, and passes tests. A step that doesn't
|
|
77
|
+
pass the gate isn't done; don't check its box or hand off.
|
|
78
|
+
2. Check that one box in `TODO.md`'s `## Bootstrap` section. Leave every
|
|
79
|
+
other box and every other section untouched.
|
|
80
|
+
3. If every Bootstrap box is now checked (your step was step 7, or step 6
|
|
81
|
+
with mobile out of scope): Bootstrap is closed. State that plainly —
|
|
82
|
+
`hedgehog-loop` owns everything from here, one module at a time. Don't
|
|
83
|
+
hand off again.
|
|
84
|
+
4. Otherwise: hand off to a fresh instance of yourself for the next step.
|
|
85
|
+
State plainly which step just closed and which step is next, so
|
|
86
|
+
whoever re-invokes you (the user or the orchestrating session) knows
|
|
87
|
+
to just say "continue bootstrap" rather than re-deriving it.
|
|
88
|
+
|
|
89
|
+
## Constraints
|
|
90
|
+
|
|
91
|
+
- One step per invocation. Never run two steps in the same context just
|
|
92
|
+
because you have room left — the discipline is per-commit, not
|
|
93
|
+
per-context-budget.
|
|
94
|
+
- Never re-run a step whose commit already exists — see "Which step is
|
|
95
|
+
yours." A felt need to redo a landed step is a Correction Protocol case
|
|
96
|
+
(patch it at its source, per `hedgehog-loop`), not a re-run.
|
|
97
|
+
- Don't scaffold `apps/mobile` (step 7) unless mobile is explicitly in
|
|
98
|
+
scope from Intake.
|
|
99
|
+
- Don't add domain schema, contracts, or any `libs/<module>/*` content —
|
|
100
|
+
that's Phase A, started only after every Bootstrap box is checked.
|
|
101
|
+
- Don't deviate from the locked stack or package choices in
|
|
102
|
+
`hedgehog-bootstrap`. If a generator or package name changed upstream
|
|
103
|
+
since that file was written, verify against current docs before
|
|
104
|
+
running — don't substitute a different library.
|
|
105
|
+
- Local Postgres/Redis always run through the `docker-compose.yml` from
|
|
106
|
+
step 1, on every host OS. Never a natively-installed Postgres/Redis,
|
|
107
|
+
even to match a contributor's existing local setup.
|
|
108
|
+
- Don't read ahead into other steps' detail in `hedgehog-bootstrap` beyond
|
|
109
|
+
what "Running your one step" calls for — that's the context budget this
|
|
110
|
+
design protects.
|
package/src/agents/planner.md
CHANGED
|
@@ -3,7 +3,7 @@ name: planner
|
|
|
3
3
|
description: Use for Intake (scope boundary + domain vocabulary) at the start of a project, and for determining module scope/order when a new set of domain modules enters play. Not a per-step planner — the step sequence within a module and TODO.md already handle that.
|
|
4
4
|
model: sonnet
|
|
5
5
|
color: yellow
|
|
6
|
-
tools: Read, Glob, Grep, Write
|
|
6
|
+
tools: Read, Glob, Grep, Edit, Write
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
You are the planner role in the Hedgehog discipline. The build sequence
|
|
@@ -59,7 +59,11 @@ Revising a draft is a normal edit — the Correction Protocol
|
|
|
59
59
|
3. **`docs/context.md`** — the product narrative, scope boundary, and
|
|
60
60
|
domain vocabulary, written as current state (see below). Mandatory,
|
|
61
61
|
every project gets one.
|
|
62
|
-
4. **
|
|
62
|
+
4. **Root `CLAUDE.md`'s `{{PROJECT_NAME}}` and `{{PROJECT_SUMMARY}}`
|
|
63
|
+
placeholders** — filled in on the project's first Intake only (see
|
|
64
|
+
below). A later Intake doesn't touch these unless the project's
|
|
65
|
+
identity itself changed, not just its scope.
|
|
66
|
+
5. **Screen/flow notes** — captured by module in
|
|
63
67
|
`docs/design/<module>-notes.md`, for `ux-planner` to act on at that
|
|
64
68
|
module's Phase B (see below). Mandatory per module in scope, even when
|
|
65
69
|
nothing was offered for that module.
|
|
@@ -206,13 +210,19 @@ on — read which one this is early and let it set the pace.
|
|
|
206
210
|
5. **Mark it provisional** — consumed by Bootstrap and revised there or
|
|
207
211
|
at the schema step as needed.
|
|
208
212
|
6. **Run Confirm & lock** (below) before writing anything. Only after
|
|
209
|
-
the person confirms does synthesis proceed to steps 7–
|
|
213
|
+
the person confirms does synthesis proceed to steps 7–9.
|
|
210
214
|
7. **Write `docs/context.md`**: product narrative, scope boundary, and
|
|
211
215
|
the domain vocabulary table, stated as current state only — no record
|
|
212
216
|
of alternatives considered, no "originally X, now Y." A later Intake
|
|
213
217
|
updates this file in place so it keeps reading as current state; it
|
|
214
218
|
never grows into a history.
|
|
215
|
-
8. **
|
|
219
|
+
8. **Fill root `CLAUDE.md`'s `{{PROJECT_NAME}}` and `{{PROJECT_SUMMARY}}`
|
|
220
|
+
placeholders**, first Intake only, then delete the installer's HTML
|
|
221
|
+
comment block at the top of that file (its job — marking what's
|
|
222
|
+
placeholder vs. constant — is done once both are filled). Leave every
|
|
223
|
+
other line untouched; the rest of the file is a Hedgehog constant, not
|
|
224
|
+
project-specific content.
|
|
225
|
+
9. **File screen/flow notes** under their module in
|
|
216
226
|
`docs/design/<module>-notes.md`, one file per module in scope, even
|
|
217
227
|
when nothing was offered for that module (say so plainly instead of
|
|
218
228
|
omitting the file) — verbatim or lightly organized, raw material for
|
|
@@ -338,17 +348,27 @@ ask now than fix forward later.
|
|
|
338
348
|
domain vocabulary — current state only. On a second Intake, update it
|
|
339
349
|
in place to reflect the new current state; don't append a log of what
|
|
340
350
|
changed or why.
|
|
341
|
-
9. **
|
|
342
|
-
`
|
|
343
|
-
|
|
344
|
-
|
|
351
|
+
9. **Fill root `CLAUDE.md`'s placeholders**, first Intake only —
|
|
352
|
+
`{{PROJECT_NAME}}`, `{{PROJECT_SUMMARY}}`, then delete the installer's
|
|
353
|
+
comment block. Skip this step entirely on a second or later Intake;
|
|
354
|
+
the file has no other project-specific content to update.
|
|
355
|
+
10. **File screen/flow notes** captured during Intake under
|
|
356
|
+
`docs/design/<module>-notes.md`, one file per module in scope — create
|
|
357
|
+
it even for a module with no screen input yet, stating that plainly.
|
|
358
|
+
11. **Return a summary**: scope boundary, module list, any open
|
|
345
359
|
questions.
|
|
346
360
|
|
|
347
361
|
## Constraints
|
|
348
362
|
|
|
349
363
|
- Never write or modify application code. Read-only against the
|
|
350
|
-
codebase; you may write `TODO.md`, `docs/context.md`,
|
|
351
|
-
`docs/design/<module>-notes.md
|
|
364
|
+
codebase; you may write `TODO.md`, `docs/context.md`,
|
|
365
|
+
`docs/design/<module>-notes.md`, and — first Intake only — root
|
|
366
|
+
`CLAUDE.md`'s `{{PROJECT_NAME}}`/`{{PROJECT_SUMMARY}}` placeholders and
|
|
367
|
+
its installer comment block.
|
|
368
|
+
- Never touch root `CLAUDE.md` outside those placeholders. Every other
|
|
369
|
+
line is a Hedgehog constant (stack, layout, rules, agent/skill
|
|
370
|
+
pointers) shared verbatim across every Hedgehog project — not
|
|
371
|
+
project-specific content to edit, extend, or "improve."
|
|
352
372
|
- `docs/context.md` and `docs/design/<module>-notes.md` are not
|
|
353
373
|
optional — every project gets the former, every module in scope gets
|
|
354
374
|
the latter, regardless of how much material Intake produced.
|
|
@@ -29,6 +29,7 @@ One opinionated stack, applied the same way on every project:
|
|
|
29
29
|
| Backend framework | NestJS |
|
|
30
30
|
| ORM | Drizzle (+ `drizzle-zod`) |
|
|
31
31
|
| Database | PostgreSQL |
|
|
32
|
+
| Local infra | Docker Compose (Postgres + Redis) |
|
|
32
33
|
| Platform | Railway |
|
|
33
34
|
| API contract | ts-rest |
|
|
34
35
|
| Validation | Zod |
|
|
@@ -106,6 +107,13 @@ project misconfiguration or a corrupted pnpm store — check **Known issue:
|
|
|
106
107
|
esbuild postinstall version mismatch** below first; this is a known,
|
|
107
108
|
deterministic collision, not something to misdiagnose from scratch.
|
|
108
109
|
|
|
110
|
+
Confirm Docker is available (`docker --version`) before step 1. Local
|
|
111
|
+
Postgres/Redis run through Docker Compose on every host OS — see **Local
|
|
112
|
+
infra: Docker, always** below. No Docker installed: stop and point the
|
|
113
|
+
user to installing Docker Desktop (macOS/Windows) or Docker
|
|
114
|
+
Engine (Linux) rather than falling back to a natively-installed
|
|
115
|
+
Postgres/Redis.
|
|
116
|
+
|
|
109
117
|
## Steps (run in sequence, one commit per step)
|
|
110
118
|
|
|
111
119
|
### 1. Nx workspace + `packages/config`
|
|
@@ -134,6 +142,18 @@ root; if present, delete it and run `pnpm install` to regenerate
|
|
|
134
142
|
`pnpm-lock.yaml` before continuing to step 2. Don't assume `nx init`
|
|
135
143
|
respects the locked package manager — verify.
|
|
136
144
|
|
|
145
|
+
`nx init` also does not create `pnpm-workspace.yaml`. Without it, pnpm
|
|
146
|
+
doesn't recognize `packages/*` or `apps/*` as workspace members —
|
|
147
|
+
`pnpm add -w` fails with `ERR_PNPM_ADDING_TO_ROOT`-adjacent errors, and
|
|
148
|
+
cross-package `pnpm add` for a lib silently installs to the wrong place.
|
|
149
|
+
Create it manually right after `nx init`, before the first `pnpm add`:
|
|
150
|
+
|
|
151
|
+
```yaml
|
|
152
|
+
packages:
|
|
153
|
+
- 'packages/*'
|
|
154
|
+
- 'apps/*'
|
|
155
|
+
```
|
|
156
|
+
|
|
137
157
|
Then generate the first lib. The **first** `@nx/js:lib` call materializes
|
|
138
158
|
the whole workspace shape (`tsconfig.base.json`, root `eslint.config.mjs`,
|
|
139
159
|
`.prettierrc`, `vitest.workspace.ts`, the `packages/` layout, and the
|
|
@@ -150,7 +170,16 @@ files:
|
|
|
150
170
|
- `packages/config/eslint-base.js` — flat config, extended by every
|
|
151
171
|
app/lib. Include `@nx/enforce-module-boundaries` and `depConstraints`
|
|
152
172
|
from Enforcement wiring below, verbatim.
|
|
153
|
-
- `packages/config/prettier.js` —
|
|
173
|
+
- `packages/config/prettier.js` — **don't** add `prettier-plugin-tailwindcss`
|
|
174
|
+
here. The plugin parses every file prettier touches (not just files with
|
|
175
|
+
Tailwind classes) and throws (`TypeError: e.charAt is not a function` /
|
|
176
|
+
`a.startsWith is not a function`) on any file when no Tailwind config is
|
|
177
|
+
resolvable yet — which is every project from step 1 through step 5, since
|
|
178
|
+
Tailwind doesn't arrive until `apps/web` in step 6. Loading it globally
|
|
179
|
+
here breaks `nx format:write` and any bare `prettier --check` for the
|
|
180
|
+
first five steps of every project. Add the plugin to `apps/web`'s own
|
|
181
|
+
prettier setup (extending this base config) once Tailwind exists, not to
|
|
182
|
+
the shared base.
|
|
154
183
|
- `packages/config/env.schema.ts` — the Zod env schema from Enforcement
|
|
155
184
|
wiring below (`DATABASE_URL`, `BETTER_AUTH_SECRET`, `REDIS_URL`,
|
|
156
185
|
`NODE_ENV`; extend per project as new infra is added later).
|
|
@@ -158,6 +187,53 @@ files:
|
|
|
158
187
|
declares the project tags table below (`scope:*`, `type:*`) as comments
|
|
159
188
|
or a lookup, so every later generator step tags its project correctly.
|
|
160
189
|
|
|
190
|
+
Root `eslint.config.mjs` is itself ESM (`.mjs`), which surfaces a Node
|
|
191
|
+
warning ("Module type of file... is not specified") unless the root
|
|
192
|
+
`package.json` declares `"type": "module"`. Setting that is correct and
|
|
193
|
+
worth doing — but it changes how Node resolves *every* plain `.js` file
|
|
194
|
+
in the repo from CommonJS to ESM by default. Several files later steps
|
|
195
|
+
generate are CommonJS (`require`/`module.exports`) and will break with
|
|
196
|
+
`ReferenceError: require is not defined` or `module is not defined` the
|
|
197
|
+
moment `"type": "module"` is set: `apps/*/webpack.config.js`,
|
|
198
|
+
`commitlint.config.js`, and any hand-written CommonJS tool script (e.g.
|
|
199
|
+
`tools/phase-gate.js`). Rename each to `.cjs` as it's created (or convert
|
|
200
|
+
its content to ESM, as `next.config.js` supports natively via
|
|
201
|
+
`export default`) rather than discovering the break later when `nx
|
|
202
|
+
graph` or a generator that depends on the project graph fails
|
|
203
|
+
opaquely — Nx surfaces this as "Failed to process project graph" pointing
|
|
204
|
+
at the offending file, not as a module-system explanation.
|
|
205
|
+
|
|
206
|
+
Add a root `docker-compose.yml` provisioning Postgres and Redis for local
|
|
207
|
+
dev, regardless of host OS (macOS, Windows, Linux) — see **Local infra:
|
|
208
|
+
Docker, always** below for why this isn't optional. `DATABASE_URL` and
|
|
209
|
+
`REDIS_URL` in `.env` point at the compose services from the first
|
|
210
|
+
commit, matching the env schema below.
|
|
211
|
+
|
|
212
|
+
```yaml
|
|
213
|
+
services:
|
|
214
|
+
postgres:
|
|
215
|
+
image: postgres:16-alpine
|
|
216
|
+
environment:
|
|
217
|
+
POSTGRES_USER: postgres
|
|
218
|
+
POSTGRES_PASSWORD: postgres
|
|
219
|
+
POSTGRES_DB: app
|
|
220
|
+
ports:
|
|
221
|
+
- '5432:5432'
|
|
222
|
+
volumes:
|
|
223
|
+
- postgres-data:/var/lib/postgresql/data
|
|
224
|
+
|
|
225
|
+
redis:
|
|
226
|
+
image: redis:7-alpine
|
|
227
|
+
ports:
|
|
228
|
+
- '6379:6379'
|
|
229
|
+
volumes:
|
|
230
|
+
- redis-data:/data
|
|
231
|
+
|
|
232
|
+
volumes:
|
|
233
|
+
postgres-data:
|
|
234
|
+
redis-data:
|
|
235
|
+
```
|
|
236
|
+
|
|
161
237
|
Add an `esbuild` override to root `package.json` in this step, before step
|
|
162
238
|
2 installs `drizzle-kit` — see **Known issue: esbuild postinstall version
|
|
163
239
|
mismatch** below for why.
|
|
@@ -209,6 +285,20 @@ structured logging. Call `loadEnv()` at the top of `apps/api/src/main.ts`.
|
|
|
209
285
|
Tag: `scope:api`. No controllers beyond a health check — domain
|
|
210
286
|
controllers arrive per module in Phase A.
|
|
211
287
|
|
|
288
|
+
`@nx/nest:app` also scaffolds a companion `apps/api-e2e` project wired to
|
|
289
|
+
Jest (`jest.config.cts`), not Vitest — inconsistent with the locked stack.
|
|
290
|
+
Convert it: delete the Jest config, add a `vitest.config.mts` (mirroring
|
|
291
|
+
`packages/db`'s), and give `apps/api-e2e`'s `tsconfig.spec.json` the same
|
|
292
|
+
`composite`/`declaration` treatment as above. Rename its target from the
|
|
293
|
+
plugin-inferred `test` to an explicit `e2e` — an HTTP e2e suite needs a
|
|
294
|
+
live server (`dependsOn: ["api:build", "api:serve"]`), and if it keeps the
|
|
295
|
+
default `test` name, `nx affected -t test` (what lefthook's pre-commit
|
|
296
|
+
hook runs on every commit, per the Commit gate below) will try to boot a
|
|
297
|
+
server on every commit. Exclude `apps/api-e2e` from the `@nx/vitest`
|
|
298
|
+
plugin's auto-inference in `nx.json` (`"exclude": ["apps/api-e2e/**"]` on
|
|
299
|
+
that plugin entry) so it stops registering a `test` target for this
|
|
300
|
+
project at all, keeping only the manually-defined `e2e` target.
|
|
301
|
+
|
|
212
302
|
Commit: `feat(api): nest shell + global guard + pino`
|
|
213
303
|
|
|
214
304
|
### 5. `apps/worker` — BullMQ seam (Redis, no consumers yet)
|
|
@@ -230,17 +320,73 @@ Commit: `feat(worker): bullmq seam, no consumers`
|
|
|
230
320
|
```bash
|
|
231
321
|
npx nx g @nx/next:app apps/web
|
|
232
322
|
pnpm add @tanstack/react-query
|
|
233
|
-
pnpm dlx shadcn@latest init
|
|
234
323
|
```
|
|
235
324
|
|
|
236
|
-
|
|
237
|
-
`apps/web`
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
325
|
+
**Do not run `pnpm dlx shadcn@latest init` (or `shadcn add`) against
|
|
326
|
+
`apps/web` directly.** The shadcn CLI hard-requires a `package.json` in
|
|
327
|
+
its target directory to detect the project; an Nx-generated app under
|
|
328
|
+
this stack has no per-app `package.json` (dependencies live at the
|
|
329
|
+
workspace root). Finding none, the CLI's default behavior is to offer —
|
|
330
|
+
and on `--yes`, silently proceed — to scaffold a *brand-new*
|
|
331
|
+
`create-next-app` project **inside** `apps/web`, complete with its own
|
|
332
|
+
nested `.git`, its own lockfile, and its own `package.json`. This doesn't
|
|
333
|
+
error; it succeeds and leaves a corrupted nested project (e.g.
|
|
334
|
+
`apps/web/web/`) that has to be manually detected and deleted. There is
|
|
335
|
+
currently no supported non-interactive flag that makes shadcn's CLI
|
|
336
|
+
target an existing package-json-less directory in place.
|
|
337
|
+
|
|
338
|
+
Build the base theme by hand instead: write `apps/web/components.json`
|
|
339
|
+
directly (style, aliases, `tailwind.css` path — the `css` field), add
|
|
340
|
+
`class-variance-authority`, `clsx`, `tailwind-merge`, `lucide-react`, and
|
|
341
|
+
`@radix-ui/react-slot` as dependencies, write `apps/web/src/lib/utils.ts`
|
|
342
|
+
(the standard `cn()` helper), and hand-write the CSS variable theme block
|
|
343
|
+
(light/dark, using shadcn's published default token values) into
|
|
344
|
+
`apps/web`'s global stylesheet. Individual components (e.g. `button.tsx`)
|
|
345
|
+
can then be hand-written from shadcn's published source for that
|
|
346
|
+
component — small, stable, well-known files — rather than fetched via the
|
|
347
|
+
CLI. Set the actual palette here, once, rather than leaving placeholder
|
|
348
|
+
values for `ui-builder` to inherit unnoticed on the first screen. Light/
|
|
349
|
+
dark mode toggle wiring belongs here too, not as a per-screen decision
|
|
350
|
+
later.
|
|
351
|
+
|
|
352
|
+
Wire the TanStack Query provider at the root layout (App Router: a
|
|
353
|
+
`'use client'` `Providers` wrapper component, since the root layout
|
|
354
|
+
itself is a server component). No screens or hooks yet — Phase B doesn't
|
|
242
355
|
start until Phase A closes for at least one module. Tag: `scope:web`.
|
|
243
356
|
|
|
357
|
+
**Set `NODE_ENV` explicitly on the `build` target, or the production
|
|
358
|
+
build silently runs in dev mode under Nx.** Nx's task runner sets
|
|
359
|
+
`NODE_ENV=development` by default for every task unless a target
|
|
360
|
+
overrides it — including a plain `nx:run-commands` target running `next
|
|
361
|
+
build`. Next.js's production build pipeline assumes `NODE_ENV=production`;
|
|
362
|
+
running it under `development` produces a build that compiles and
|
|
363
|
+
appears to succeed on individual pages but crashes prerendering the
|
|
364
|
+
auto-generated `/_global-error` route with `TypeError: Cannot read
|
|
365
|
+
properties of null (reading 'useContext')` — a React-internals mismatch
|
|
366
|
+
from the dev/prod build split, not an app bug. This only reproduces
|
|
367
|
+
through `nx run web:build`; the identical `next build --webpack` run
|
|
368
|
+
directly from `apps/web` succeeds, because a bare shell has no `NODE_ENV`
|
|
369
|
+
set and Next defaults it correctly itself — which makes the symptom look
|
|
370
|
+
Nx-specific and environment-related rather than what it is (a task-runner
|
|
371
|
+
default silently overriding a value the build assumes). Set it on the
|
|
372
|
+
`build` target only, in `apps/web/project.json`:
|
|
373
|
+
|
|
374
|
+
```json
|
|
375
|
+
{
|
|
376
|
+
"targets": {
|
|
377
|
+
"build": {
|
|
378
|
+
"options": {
|
|
379
|
+
"command": "next build --webpack",
|
|
380
|
+
"env": { "NODE_ENV": "production" }
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Leave `dev` alone — it correctly wants `NODE_ENV=development`, which is
|
|
388
|
+
also what Nx already defaults it to.
|
|
389
|
+
|
|
244
390
|
Commit: `feat(web): next shell + query provider + base theme`
|
|
245
391
|
|
|
246
392
|
### 7. `apps/mobile` — Expo shell (only if mobile is in scope)
|
|
@@ -315,6 +461,51 @@ e.g. `libs/orders/port`, `libs/orders/repository`, `libs/orders/service`.
|
|
|
315
461
|
Building out of order (a controller before a service exists, a hook
|
|
316
462
|
reaching into `apps/api` directly) fails `nx lint`.
|
|
317
463
|
|
|
464
|
+
The rule list above is illustrative, not exhaustive — `@nx/enforce-
|
|
465
|
+
module-boundaries` denies by default for any project whose tag isn't
|
|
466
|
+
named as a `sourceTag` in some `depConstraints` entry ("A project without
|
|
467
|
+
tags matching at least one constraint cannot depend on any libraries").
|
|
468
|
+
`packages/db` (`scope:db`, `type:adapter`) and `apps/api` (`scope:api`)
|
|
469
|
+
both hit this the moment they're generated and tagged in steps 2 and 4,
|
|
470
|
+
because neither `type:adapter` nor `scope:api` has an entry above. Add
|
|
471
|
+
constraints for every tag combination as it's introduced, not only the
|
|
472
|
+
ones in the illustrative list — at minimum `type:adapter` (needs
|
|
473
|
+
`type:util`, for `packages/config`) and `scope:api` (needs `type:port`,
|
|
474
|
+
`type:service`, `type:util` — never `scope:db` directly; `apps/api`
|
|
475
|
+
reaches storage through a module's repository/service, not by importing
|
|
476
|
+
`packages/db` itself).
|
|
477
|
+
|
|
478
|
+
### Typecheck target (required before the commit gate can work)
|
|
479
|
+
|
|
480
|
+
`lefthook.yml`'s pre-commit below runs `nx affected -t typecheck`, but
|
|
481
|
+
`@nx/js:lib` and `@nx/nest:app` don't register a `typecheck` target on
|
|
482
|
+
their own — it only exists once the `@nx/js/typescript` plugin is
|
|
483
|
+
registered in `nx.json`, added in step 1 alongside the eslint and
|
|
484
|
+
vitest plugins:
|
|
485
|
+
|
|
486
|
+
```json
|
|
487
|
+
{
|
|
488
|
+
"plugin": "@nx/js/typescript",
|
|
489
|
+
"options": { "typecheck": { "targetName": "typecheck" } }
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
Registering the plugin isn't sufficient on its own, either: `tsc
|
|
494
|
+
--build`'s composite-project mode (what the inferred `typecheck` target
|
|
495
|
+
actually runs) requires every `tsconfig.lib.json` and `tsconfig.spec.json`
|
|
496
|
+
it touches to set `"composite": true` and `"declaration": true`. The
|
|
497
|
+
generators don't set these — every `@nx/js:lib` and `@nx/nest:app`
|
|
498
|
+
project needs both added by hand to `tsconfig.lib.json` /
|
|
499
|
+
`tsconfig.app.json` and `tsconfig.spec.json` right after generation, or
|
|
500
|
+
`nx run <proj>:typecheck` fails immediately with `TS5069: Option
|
|
501
|
+
'emitDeclarationOnly' cannot be specified without specifying option
|
|
502
|
+
'declaration' or option 'composite'`. If a project's spec file imports
|
|
503
|
+
from its own lib source (the common case), `tsconfig.spec.json` also
|
|
504
|
+
needs an explicit `references` entry pointing at `tsconfig.lib.json`, or
|
|
505
|
+
`tsc` reports `TS6307: File '...' is not listed within the file list of
|
|
506
|
+
project`. Do this for `packages/config` in step 1 so the pattern is
|
|
507
|
+
established before every later step repeats it.
|
|
508
|
+
|
|
318
509
|
### Commit gate (lefthook + commitlint)
|
|
319
510
|
|
|
320
511
|
Enforces one coherent change, tested, conventionally committed. Runs on
|
|
@@ -441,10 +632,30 @@ One shared config, extended everywhere:
|
|
|
441
632
|
|
|
442
633
|
- `packages/config/eslint-base.js` — flat config, extended by every
|
|
443
634
|
app/lib.
|
|
444
|
-
- `packages/config/prettier.js` —
|
|
635
|
+
- `packages/config/prettier.js` — the shared base, *without*
|
|
636
|
+
`prettier-plugin-tailwindcss` (see step 1 — it belongs in `apps/web`'s
|
|
637
|
+
own config once Tailwind exists, not the shared base).
|
|
445
638
|
|
|
446
639
|
A per-app override request signals to fix the base config at the source.
|
|
447
640
|
|
|
641
|
+
## Local infra: Docker, always
|
|
642
|
+
|
|
643
|
+
Postgres and Redis run through the `docker-compose.yml` from step 1 on
|
|
644
|
+
every project, on every host OS — macOS, Windows, Linux alike. This isn't
|
|
645
|
+
a convenience default; it's what makes "clone the repo, run the stack"
|
|
646
|
+
mechanically true regardless of who's building. A natively-installed
|
|
647
|
+
Postgres or Redis (Homebrew, an existing Windows service, a system
|
|
648
|
+
package) is a per-machine setup step that isn't in the commit history and
|
|
649
|
+
isn't reproducible on the next machine — exactly the kind of
|
|
650
|
+
tribal-knowledge dependency Hedgehog's enforcement exists to remove.
|
|
651
|
+
|
|
652
|
+
Don't offer a "native install" path as an alternative, even if a
|
|
653
|
+
contributor already has Postgres running locally for another project. One
|
|
654
|
+
mechanism, every machine: `docker compose up -d` before `pnpm install`,
|
|
655
|
+
every time. If Docker genuinely can't run on a target machine, that's a
|
|
656
|
+
platform-support gap to raise, not a reason to quietly fall back to a
|
|
657
|
+
native install for that one contributor.
|
|
658
|
+
|
|
448
659
|
## Known issue: esbuild postinstall version mismatch
|
|
449
660
|
|
|
450
661
|
`@nx/vite` (step 1) declares `esbuild` as an *optional* peer dependency
|
|
@@ -494,3 +705,7 @@ gated by lefthook, each its own commit.
|
|
|
494
705
|
- Each of the 7 steps is its own commit, in order — same unit-of-work
|
|
495
706
|
discipline as every other step in the discipline, even though this is
|
|
496
707
|
infra rather than a domain module.
|
|
708
|
+
- Local Postgres/Redis always run through the `docker-compose.yml` from
|
|
709
|
+
step 1, on every host OS. Never substitute a natively-installed
|
|
710
|
+
Postgres/Redis, even to match a contributor's existing local setup —
|
|
711
|
+
see **Local infra: Docker, always**.
|
package/src/templates/CLAUDE.md
CHANGED
|
@@ -66,6 +66,8 @@ steps from memory:
|
|
|
66
66
|
|
|
67
67
|
### The agents — delegate the judgment calls
|
|
68
68
|
|
|
69
|
+
- **`bootstrap`** — runs `hedgehog-bootstrap`'s 7 steps once, at project
|
|
70
|
+
start, after Intake. Skip if `nx.json` already exists.
|
|
69
71
|
- **`planner`** — Intake (scope boundary + domain vocabulary) at project
|
|
70
72
|
start, and module scoping when new scope enters play. Writes `TODO.md`,
|
|
71
73
|
`docs/context.md`, and `docs/design/<module>-notes.md`.
|
|
@@ -81,11 +83,13 @@ steps from memory:
|
|
|
81
83
|
### Stack (locked)
|
|
82
84
|
|
|
83
85
|
Nx monorepo · pnpm · **NestJS** (all domain logic + DB access) · **Drizzle**
|
|
84
|
-
(+ `drizzle-zod`) · **PostgreSQL** ·
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
(
|
|
88
|
-
|
|
86
|
+
(+ `drizzle-zod`) · **PostgreSQL** · **Docker Compose** (local Postgres +
|
|
87
|
+
Redis, every host OS) · Railway · **ts-rest** contracts · **Zod**
|
|
88
|
+
validation · **Better Auth** · **TanStack Query** hooks · **Next.js** +
|
|
89
|
+
ShadCN + Tailwind (web, UI only) · Expo + React Native Reusables +
|
|
90
|
+
NativeWind (mobile, optional) · **BullMQ + Redis** (queues) · Pino logging ·
|
|
91
|
+
Vitest + Playwright (tests) · Conventional Commits + commitlint + lefthook ·
|
|
92
|
+
Sentry.
|
|
89
93
|
|
|
90
94
|
Don't substitute libraries. If a package or generator name changed
|
|
91
95
|
upstream, verify against current docs before running — don't swap in a
|
|
@@ -94,6 +98,7 @@ different library.
|
|
|
94
98
|
### Layout
|
|
95
99
|
|
|
96
100
|
```
|
|
101
|
+
docker-compose.yml local Postgres + Redis — every host OS, no native install
|
|
97
102
|
apps/
|
|
98
103
|
web Next.js — UI only
|
|
99
104
|
mobile Expo — optional
|
|
@@ -130,6 +135,9 @@ docs/
|
|
|
130
135
|
(lefthook gate).
|
|
131
136
|
- **Fix wrong steps at the source** via the Correction Protocol — never a
|
|
132
137
|
downstream workaround.
|
|
138
|
+
- **Local Postgres/Redis always run through `docker-compose.yml`**, on
|
|
139
|
+
every host OS. Never a natively-installed Postgres/Redis, even to match
|
|
140
|
+
a contributor's existing local setup.
|
|
133
141
|
- **`packages/config` is the single source** for shared config. A per-app
|
|
134
142
|
override request means fix the base config, not add an override.
|
|
135
143
|
|
|
@@ -168,9 +176,10 @@ context small:
|
|
|
168
176
|
- **A cleared or new session recovers by reading `TODO.md`,
|
|
169
177
|
`docs/context.md`, and the commit log**, never by needing the prior
|
|
170
178
|
conversation.
|
|
171
|
-
- **Delegate heavy work to agents.**
|
|
172
|
-
|
|
173
|
-
own isolated context — so that
|
|
179
|
+
- **Delegate heavy work to agents.** The project scaffold (`bootstrap`),
|
|
180
|
+
Intake elicitation (`planner`), screen builds (`ui-builder`), and
|
|
181
|
+
reviews (`reviewer`) each run in their own isolated context — so that
|
|
182
|
+
work doesn't pile up in the main thread.
|
|
174
183
|
- **Don't paste large context back in.** If you find yourself
|
|
175
184
|
re-explaining the architecture, stop — it's fixed and stated in this
|
|
176
185
|
file, not something to reconstruct. If you need a project specific, read
|
package/src/templates/TODO.md
CHANGED
|
@@ -10,7 +10,7 @@ has one, written by planner at Intake. -->
|
|
|
10
10
|
|
|
11
11
|
## Bootstrap
|
|
12
12
|
|
|
13
|
-
- [ ] Nx workspace + `packages/config`
|
|
13
|
+
- [ ] Nx workspace + `packages/config` (incl. `docker-compose.yml` for local Postgres/Redis)
|
|
14
14
|
- [ ] `packages/db` — Drizzle client
|
|
15
15
|
- [ ] `packages/auth` — Better Auth config
|
|
16
16
|
- [ ] `apps/api` — Nest shell, global guard, Pino
|