@skyf0xx/hedgehog 4.2.0 → 4.2.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.
- package/README.md +20 -0
- package/bin/cli.mjs +12 -0
- package/package.json +2 -2
- package/src/agents/backend-eng.md +30 -16
- package/src/agents/front-end-eng.md +29 -12
- package/src/db/next.mjs +46 -8
- package/src/golden-cores/full-stack-app/apps/api/package.json +19 -1
- package/src/golden-cores/full-stack-app/apps/api/src/app/app.module.spec.ts +15 -0
- package/src/golden-cores/full-stack-app/apps/api/src/app/app.module.ts +6 -1
- package/src/golden-cores/full-stack-app/apps/api/src/app/feature-modules.ts +8 -0
- package/src/golden-cores/full-stack-app/apps/api/tsconfig.app.json +3 -0
- package/src/golden-cores/full-stack-app/apps/api/tsconfig.json +3 -0
- package/src/golden-cores/full-stack-app/apps/api/tsconfig.spec.json +36 -0
- package/src/golden-cores/full-stack-app/apps/api/vitest.config.mts +18 -0
- package/src/golden-cores/full-stack-app/apps/web/src/components/theme-toggle.spec.tsx +20 -0
- package/src/golden-cores/full-stack-app/apps/web/src/test-setup.ts +1 -0
- package/src/golden-cores/full-stack-app/apps/web/tsconfig.json +6 -0
- package/src/golden-cores/full-stack-app/apps/web/tsconfig.spec.json +37 -0
- package/src/golden-cores/full-stack-app/apps/web/vitest.config.mts +27 -0
- package/src/golden-cores/full-stack-app/nx.json +4 -1
- package/src/golden-cores/full-stack-app/package.json +8 -0
- package/src/golden-cores/full-stack-app/pnpm-lock.yaml +8735 -2907
- package/src/golden-cores/full-stack-app/tools/generate-feature-modules.cjs +104 -0
- package/src/golden-cores/full-stack-app/tools/generators/contract/generator.ts +283 -0
- package/src/golden-cores/full-stack-app/tools/generators/contract/schema.json +20 -0
- package/src/golden-cores/full-stack-app/tools/generators/controller/generator.ts +323 -0
- package/src/golden-cores/full-stack-app/tools/generators/controller/schema.json +20 -0
- package/src/golden-cores/full-stack-app/tools/generators/fields.ts +126 -0
- package/src/golden-cores/full-stack-app/tools/generators/generators.json +42 -0
- package/src/golden-cores/full-stack-app/tools/generators/hook/generator.ts +274 -0
- package/src/golden-cores/full-stack-app/tools/generators/hook/schema.json +19 -0
- package/src/golden-cores/full-stack-app/tools/generators/lib-shell.ts +124 -0
- package/src/golden-cores/full-stack-app/tools/generators/naming.ts +84 -0
- package/src/golden-cores/full-stack-app/tools/generators/package.json +6 -0
- package/src/golden-cores/full-stack-app/tools/generators/repository/generator.ts +298 -0
- package/src/golden-cores/full-stack-app/tools/generators/repository/schema.json +15 -0
- package/src/golden-cores/full-stack-app/tools/generators/schema/generator.ts +169 -0
- package/src/golden-cores/full-stack-app/tools/generators/schema/schema.json +20 -0
- package/src/golden-cores/full-stack-app/tools/generators/screen/generator.ts +218 -0
- package/src/golden-cores/full-stack-app/tools/generators/screen/schema.json +15 -0
- package/src/golden-cores/full-stack-app/tools/generators/service/generator.ts +194 -0
- package/src/golden-cores/full-stack-app/tools/generators/service/schema.json +15 -0
- package/src/skills/hedgehog-bootstrap-full-stack-app-core/SKILL.md +67 -6
- package/src/skills/hedgehog-loop/SKILL.md +119 -53
|
@@ -87,10 +87,12 @@ stop — an operation that seems to want async processing on a Queue-off
|
|
|
87
87
|
project is a signal to revisit that add-on decision with `planner`, not
|
|
88
88
|
to build a one-off queue outside the add-on's scaffolding.
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
Every layer scaffolds from its own generator in `tools/generators/` (see
|
|
91
|
+
"Scaffolding a layer" below) — package shell, tags, files, barrel wiring,
|
|
92
|
+
and the layer's conventional shape all land in one deterministic step.
|
|
93
|
+
What's authored on top is the entity-specific delta: the field list and
|
|
94
|
+
its types, the module's business rules, and the UX intent behind its
|
|
95
|
+
screen.
|
|
94
96
|
|
|
95
97
|
## Domain Module — Backend Steps (Phase A, every module in scope)
|
|
96
98
|
|
|
@@ -211,6 +213,102 @@ Correction Protocol. Valid task statuses are `planned`, `ready`,
|
|
|
211
213
|
also carries a `blocked_reason` (`scope_violation`, `verification_failed`,
|
|
212
214
|
or `lease_expired`).
|
|
213
215
|
|
|
216
|
+
## Scaffolding a layer
|
|
217
|
+
|
|
218
|
+
`tools/generators/` holds one Nx generator per layer, and every layer
|
|
219
|
+
starts from its own:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
nx g ./tools/generators:schema --module=<module> --fields='<name:type,...>'
|
|
223
|
+
nx g ./tools/generators:contract --module=<module> --fields='<name:type,...>'
|
|
224
|
+
nx g ./tools/generators:repository --module=<module>
|
|
225
|
+
nx g ./tools/generators:service --module=<module>
|
|
226
|
+
nx g ./tools/generators:controller --module=<module> --fields='<name:type,...>'
|
|
227
|
+
nx g ./tools/generators:hook --module=<module> [--toggleField=<boolField>]
|
|
228
|
+
nx g ./tools/generators:screen --module=<module>
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
`--module` is the domain module's plural kebab-case name (`tasks`,
|
|
232
|
+
`order-items`). `--fields` is a comma-separated list of `name:type` pairs
|
|
233
|
+
over `string`, `text`, `boolean`, `integer`, and `timestamp`, with a
|
|
234
|
+
trailing `?` marking the column nullable
|
|
235
|
+
(`--fields='title:string,done:boolean,dueDate:timestamp?'`); `contract`
|
|
236
|
+
and `controller` take the same list the module's `schema` was generated
|
|
237
|
+
with. `hook`'s optional `--toggleField` names a boolean field from the
|
|
238
|
+
schema to expose as a dedicated toggle mutation.
|
|
239
|
+
|
|
240
|
+
Each generator lands the whole conventional shape of its layer in one
|
|
241
|
+
deterministic step — the package shell (`package.json`, `tsconfig*.json`,
|
|
242
|
+
`vitest.config.mts`, `src/index.ts`) where the layer creates one, the
|
|
243
|
+
`nx.tags` pair `packages/config/eslint-base.js`'s `depConstraints` keys
|
|
244
|
+
on, the port-discipline file suffixes lint checks for, the Nest module
|
|
245
|
+
and controller pair with `@Controller()` left bare (the ts-rest contract
|
|
246
|
+
already encodes full route paths), and every barrel export the new files
|
|
247
|
+
need. Hand-copying a sibling module's files invites exactly the drift
|
|
248
|
+
`hedgehog verify`'s lint step then has to catch: missing tags, missing
|
|
249
|
+
project references, a doubled route prefix.
|
|
250
|
+
|
|
251
|
+
What the generator lands is the layer's skeleton, not the layer. Author
|
|
252
|
+
the entity-specific delta on top: the module's business rules in
|
|
253
|
+
`service`, its domain-error mapping in `controller`, and — for `screen`,
|
|
254
|
+
which is skeleton-only by design — the layout, information hierarchy, and
|
|
255
|
+
interaction pattern from `ux-planner`'s rationale, over the placeholders
|
|
256
|
+
the generator leaves for the list, filter shell, empty state, and form.
|
|
257
|
+
|
|
258
|
+
Registration inside `apps/api` is automatic and stays that way:
|
|
259
|
+
`apps/api/src/app/feature-modules.ts` globs
|
|
260
|
+
`apps/api/src/app/*/*.module.ts` and is regenerated by the
|
|
261
|
+
`generate-feature-modules` Nx target that `build`/`typecheck`/`test`
|
|
262
|
+
depend on. Never register a module by editing `app.module.ts` — a shared
|
|
263
|
+
file no module-scoped task can safely touch. Validation is ts-rest + Zod,
|
|
264
|
+
so this core has no Nest DTOs and no class-validator.
|
|
265
|
+
|
|
266
|
+
**A new package needs wiring into the workspace before `hedgehog verify`
|
|
267
|
+
runs on it.** A package that exists on disk isn't yet part of the
|
|
268
|
+
workspace:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
pnpm install # link the new workspace:* deps
|
|
272
|
+
pnpm nx sync # regenerate TypeScript project references
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
`pnpm-workspace.yaml` already globs `packages/*`, `apps/*` and `libs/*/*`,
|
|
276
|
+
so a package under any of those needs no edit there. This is the common
|
|
277
|
+
case, not an edge case: any layer that is the first arrival in a package
|
|
278
|
+
(`contract`, `hook`, each module's `repository` and `service`) or that
|
|
279
|
+
wires a new package into an existing one (`controller`, adding the
|
|
280
|
+
module's `contracts`/`repository`/`service` packages to `apps/api`) needs
|
|
281
|
+
it — on a module's first pass through Phase A/B that is most of the
|
|
282
|
+
layers, not an occasional one.
|
|
283
|
+
|
|
284
|
+
The building agent runs `pnpm install` / `pnpm nx sync` and reports back
|
|
285
|
+
which shared files changed (typically `pnpm-lock.yaml`, root
|
|
286
|
+
`tsconfig.json`, and — on a `controller` layer — `apps/api/package.json`,
|
|
287
|
+
`apps/api/tsconfig.app.json`), because it has the shell access to run
|
|
288
|
+
them, but it never commits: no agent reporting success moves a task or
|
|
289
|
+
touches git, only `hedgehog verify`'s passing exit code does (see the
|
|
290
|
+
building agents' own Workflow step on this). Committing those shared
|
|
291
|
+
files is the orchestrating session's job, done between dispatch and
|
|
292
|
+
`hedgehog verify` on every layer where the agent flagged a change: expect
|
|
293
|
+
it, don't wait to be reminded.
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
git add pnpm-lock.yaml tsconfig.json # plus apps/*/package.json,
|
|
297
|
+
# apps/*/tsconfig.app.json on a
|
|
298
|
+
# controller layer
|
|
299
|
+
git commit -m "chore(workspace): sync project references"
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
These files are mechanically derived by `pnpm install` and `pnpm nx
|
|
303
|
+
sync`, not authored content, and sit outside every module-scoped layer's
|
|
304
|
+
scope — they belong to no layer, and no override covers them. Committing
|
|
305
|
+
them separately, before `hedgehog verify` runs, keeps the layer's own
|
|
306
|
+
commit exactly the layer.
|
|
307
|
+
|
|
308
|
+
This is the orchestrating session's step rather than a verify post-step
|
|
309
|
+
on purpose: `hedgehog verify` gates the tree it's handed, and a gate that
|
|
310
|
+
mutates that tree would manufacture the scope violation it then reports.
|
|
311
|
+
|
|
214
312
|
## First arrival in a package
|
|
215
313
|
|
|
216
314
|
Every layer scope names a directory *inside* a package
|
|
@@ -223,15 +321,25 @@ sit on disk uncommitted until the `join` layer's `**` scope sweeps them in,
|
|
|
223
321
|
so `git log -- packages/contracts/` shows source with no buildable package
|
|
224
322
|
behind it for the whole middle of the build.
|
|
225
323
|
|
|
324
|
+
A generator can also drop shared, package-wide source at the `src/` root
|
|
325
|
+
alongside the module's own files on that same first pass — the `contract`
|
|
326
|
+
generator's `timestamp.ts` is one (a shared Zod util every module in the
|
|
327
|
+
package imports, written once, sibling to `src/index.ts`). That file needs
|
|
328
|
+
the same widening as the shell itself.
|
|
329
|
+
|
|
226
330
|
Widen that one task, before building it:
|
|
227
331
|
|
|
228
332
|
```bash
|
|
229
333
|
hedgehog override add TASKS-CONTRACT \
|
|
230
334
|
--scope 'packages/contracts/*' \
|
|
231
|
-
--scope 'packages/contracts/src
|
|
335
|
+
--scope 'packages/contracts/src/*' \
|
|
232
336
|
--reason 'first module through the contract layer also creates the package shell'
|
|
233
337
|
```
|
|
234
338
|
|
|
339
|
+
`packages/contracts/src/*` is non-recursive, so it covers `src/index.ts`
|
|
340
|
+
and `src/timestamp.ts` without also granting the module subdirectory the
|
|
341
|
+
layer's own `packages/contracts/src/{module}/**` scope already covers.
|
|
342
|
+
|
|
235
343
|
`.hedgehog/overrides/*.json` is additive, per-task, committed, and replayed
|
|
236
344
|
by `plan`, `--recompile` and `db rebuild` alike, so the exception survives a
|
|
237
345
|
rebuild and stays reviewable in the diff — unlike a hand-edited task row,
|
|
@@ -245,57 +353,15 @@ module's `repository` and `service`, since `libs/{module}/repository` and
|
|
|
245
353
|
`libs/{module}/service` are new libs per module — there, the layer's own
|
|
246
354
|
`libs/{module}/repository/**` glob already covers the package root, so no
|
|
247
355
|
override is needed. `packages/db` and `packages/config` ship with core, so
|
|
248
|
-
`schema` never needs one.
|
|
356
|
+
`schema` never needs one. `controller` never needs one either — `apps/api`
|
|
357
|
+
ships with core, and its `apps/api/src/app/{module}/**` scope already
|
|
358
|
+
covers the module's generated directory.
|
|
249
359
|
|
|
250
360
|
Never widen a scope to route around a violation the Correction Protocol
|
|
251
361
|
should handle — this is for a package shell the layer genuinely creates,
|
|
252
|
-
nothing else.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
correct, non-buildable, Vitest-wired `package.json`/`tsconfig*.json`/
|
|
256
|
-
`vitest.config.mts`/`src/index.ts` in one deterministic step; hand-copying
|
|
257
|
-
a sibling package invites exactly the kind of drift (missing `nx.tags`,
|
|
258
|
-
missing project references) `hedgehog verify`'s lint step then has to
|
|
259
|
-
catch. Run the generator for the layer that's first-arriving, then add its
|
|
260
|
-
tags — the pair `eslint-base.js`'s `depConstraints` expects (see its own
|
|
261
|
-
tag reference table) — to the generated `package.json`'s `"nx".tags`:
|
|
262
|
-
|
|
263
|
-
```bash
|
|
264
|
-
# contract — first module through this layer only
|
|
265
|
-
npx nx g @nx/js:lib packages/contracts --bundler=none --unitTestRunner=vitest
|
|
266
|
-
# then set "nx": { "tags": ["scope:contracts", "type:contract"] } in packages/contracts/package.json
|
|
267
|
-
|
|
268
|
-
# hook — first module through this layer only
|
|
269
|
-
npx nx g @nx/js:lib packages/hooks --bundler=none --unitTestRunner=vitest
|
|
270
|
-
# then set "nx": { "tags": ["scope:hooks", "type:hook"] } in packages/hooks/package.json
|
|
271
|
-
|
|
272
|
-
# repository — every module, new lib each time
|
|
273
|
-
npx nx g @nx/js:lib libs/{module}/repository --bundler=none --unitTestRunner=vitest
|
|
274
|
-
# then set "nx": { "tags": ["scope:{module}", "type:adapter"] } in libs/{module}/repository/package.json
|
|
275
|
-
|
|
276
|
-
# service — every module, new lib each time
|
|
277
|
-
npx nx g @nx/js:lib libs/{module}/service --bundler=none --unitTestRunner=vitest
|
|
278
|
-
# then set "nx": { "tags": ["scope:{module}", "type:service"] } in libs/{module}/service/package.json
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
**Wire the new package in before reporting the layer done.** A package
|
|
282
|
-
that exists on disk isn't yet part of the workspace:
|
|
283
|
-
|
|
284
|
-
```bash
|
|
285
|
-
pnpm install # link the new workspace:* deps
|
|
286
|
-
pnpm nx sync # regenerate TypeScript project references
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
`pnpm-workspace.yaml` already globs `packages/*`, `apps/*` and `libs/*/*`,
|
|
290
|
-
so a package under any of those needs no edit there. `nx sync` writes root
|
|
291
|
-
`tsconfig.json`, which sits outside every module-scoped layer's scope — it
|
|
292
|
-
belongs to no layer and is not part of the override above. Commit it
|
|
293
|
-
separately as `chore(workspace): sync project references`, before
|
|
294
|
-
`hedgehog verify` runs, so the layer's own commit stays exactly the layer.
|
|
295
|
-
|
|
296
|
-
This is the building agent's step rather than a verify post-step on
|
|
297
|
-
purpose: `hedgehog verify` gates the tree it's handed, and a gate that
|
|
298
|
-
mutates that tree would manufacture the scope violation it then reports.
|
|
362
|
+
nothing else. The shell itself comes from the layer's generator
|
|
363
|
+
("Scaffolding a layer" above), which is also where the workspace wiring a
|
|
364
|
+
new package needs lives.
|
|
299
365
|
|
|
300
366
|
## Intra-step conventions
|
|
301
367
|
|