@skyf0xx/hedgehog 6.1.0 → 6.1.1

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/bin/cli.mjs CHANGED
@@ -424,6 +424,17 @@ async function writePlannedFile(f) {
424
424
  const existing = await readFile(f.dest, 'utf8');
425
425
  if (!existing.includes('{{PROJECT_NAME}}') && existing.includes('{{CORE_SECTION}}')) {
426
426
  out = existing;
427
+ } else if (!existing.includes('{{PROJECT_NAME}}') && !f.merge.include) {
428
+ // Brownfield: hand-written content that predates Hedgehog, with
429
+ // no shell markers at all, on the coreless deferred path (no
430
+ // `include` — there's no core section yet to fill in). Nothing
431
+ // to merge in here; the file is untouched until `hedgehog-adopt`
432
+ // later appends its own core section via appendCoreSection
433
+ // (src/hosts/claude-md-merge.mjs). Overwriting it with the
434
+ // greenfield shell, or even just filling {{HOST_DISPATCH}} into
435
+ // it, would assert a fresh-install shape onto a repo that isn't
436
+ // new.
437
+ return;
427
438
  }
428
439
  }
429
440
  if (out === null) out = await readFile(join(PKG_ROOT, f.merge.shell), 'utf8');
@@ -688,11 +699,26 @@ async function init({ force, core, host = DEFAULT_HOST, hostOnly = false, global
688
699
  // the project, so rewriting it loses nothing and never counts as a
689
700
  // conflict — that's what lets a second host be added to a project the
690
701
  // first one already set up.
702
+ //
703
+ // A deferred (coreless) install's root-instructions merge is exempted
704
+ // too, but only when the existing file is a brownfield one: hand-written
705
+ // content with no {{PROJECT_NAME}} shell marker. That file was never a
706
+ // Hedgehog shell to overwrite — writePlannedFile leaves it untouched,
707
+ // for hedgehog-adopt's own merge step to append its delimited core
708
+ // section into later (src/hosts/claude-md-merge.mjs), once a core is
709
+ // actually known. A file still carrying {{PROJECT_NAME}} (a previous
710
+ // deferred init's untouched shell) is not brownfield content and keeps
711
+ // hitting the ordinary conflict/--force path below.
691
712
  const conflicts = [];
692
713
  for (const { entry, files } of groups) {
693
714
  if (entry.type === 'generated') continue;
694
715
  for (const f of files) {
695
- if (await exists(f.dest)) conflicts.push(f.dest);
716
+ if (!(await exists(f.dest))) continue;
717
+ if (core === null && f.merge) {
718
+ const existing = await readFile(f.dest, 'utf8');
719
+ if (!existing.includes('{{PROJECT_NAME}}')) continue;
720
+ }
721
+ conflicts.push(f.dest);
696
722
  }
697
723
  }
698
724
 
@@ -719,7 +745,15 @@ async function init({ force, core, host = DEFAULT_HOST, hostOnly = false, global
719
745
  for (const { files } of groups) {
720
746
  for (const f of files) {
721
747
  const already = await exists(f.dest);
748
+ // Brownfield CLAUDE.md on the coreless path: writePlannedFile
749
+ // leaves it untouched (see its own comment) rather than overwriting
750
+ // it, so this write must not be logged or counted as one.
751
+ const before = already && f.merge ? await readFile(f.dest, 'utf8') : null;
722
752
  await writePlannedFile(f);
753
+ if (before !== null && (await readFile(f.dest, 'utf8')) === before) {
754
+ console.log(` ${dim('keep')} ${relative(DEST_ROOT, f.dest)}`);
755
+ continue;
756
+ }
723
757
  if (already) overwritten++;
724
758
  else written++;
725
759
  const label = already ? yellow('overwrite') : green('create');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyf0xx/hedgehog",
3
- "version": "6.1.0",
3
+ "version": "6.1.1",
4
4
  "description": "Install the Hedgehog build discipline (agents + skills) into a repo, for Claude Code, Cursor, or Gemini CLI.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -202,9 +202,11 @@ none of them is a description matching a `when` paragraph:
202
202
  enforcement on my changes here"). This is a distinct question from
203
203
  everything above: it's not about which core fits new work, because no
204
204
  new workspace gets built at all. This project gets the **adopted
205
- core**. Route straight to `hedgehog-adopt` — bootstrap and every other
206
- Phase 0 outcome are skipped entirely, since there is no workspace to
207
- scaffold and no shipped stack to adopt toward. `hedgehog-adopt` runs
205
+ core**. Run `hedgehog core record-adopted` first a no-flag `init`
206
+ never fetched the `adopted` package, so `hedgehog-adopt` is not yet on
207
+ disk to route to then route to `hedgehog-adopt`. Bootstrap and every
208
+ other Phase 0 outcome are skipped entirely, since there is no workspace
209
+ to scaffold and no shipped stack to adopt toward. `hedgehog-adopt` runs
208
210
  its own read-only intake and writes its own `.hedgehog/core.yaml`;
209
211
  don't run `hedgehog-planning-intake`'s BMAD shelf first — the drivers
210
212
  that skill elicits (persistence, stack, deployment target) are already
@@ -389,18 +391,23 @@ as full-stack-app's Auth/Queue/Mobile trio.
389
391
  run.** Continue at step 3.
390
392
  - **No intents in the graph, and the request is adoption onto an
391
393
  existing repo → brownfield first run.** Skip Phase 0's core
392
- selection and every step below through step 9 go straight to
393
- `hedgehog-adopt`. It runs its own intake and Confirm & Lock, writes
394
- `.hedgehog/core.yaml` and `.hedgehog/adoption.md`, and adds the
395
- first intent(s) itself. Return the summary (step 10) once it's done.
394
+ selection and every step below through step 9. Run `hedgehog core
395
+ record-adopted` first a no-flag `init` never fetched the
396
+ `adopted` package, so `hedgehog-adopt` is not yet on disk — then go
397
+ straight to `hedgehog-adopt`. It runs its own intake and Confirm &
398
+ Lock, writes `.hedgehog/core.yaml` and `.hedgehog/adoption.md`, and
399
+ adds the first intent(s) itself. Return the summary (step 10) once
400
+ it's done.
396
401
  - **One or more intents, on `.hedgehog/core.yaml` written by
397
402
  `hedgehog-adopt` → adoption re-entry.** New change-work on a repo
398
- already under adoption. Skip steps 3 through 9 route straight to
399
- `hedgehog-adopt` again instead, same as brownfield first run above.
400
- It owns everything the other path's steps 5, 7, 8, and 9 would
401
- otherwise do: it sizes the request (a large or ambiguous one gets its
402
- own short clarifying pass, a clear small one doesn't), adds the
403
- intent(s), runs `hedgehog plan`, and commits its own work as `chore
403
+ already under adoption. Skip steps 3 through 9. Run `hedgehog core
404
+ record-adopted` first safe and idempotent to re-run, and the only
405
+ guarantee that `hedgehog-adopt` is on disk in this session then
406
+ route straight to `hedgehog-adopt` again, same as brownfield first
407
+ run above. It owns everything the other path's steps 5, 7, 8, and 9
408
+ would otherwise do: it sizes the request (a large or ambiguous one
409
+ gets its own short clarifying pass, a clear small one doesn't), adds
410
+ the intent(s), runs `hedgehog plan`, and commits its own work as `chore
404
411
  (planning): adopt change`. Don't run `hedgehog-planning-intake`'s
405
412
  Re-entry pass here — there is no BMAD archive to read as context on
406
413
  this path, since adoption never runs one. Return the summary (step
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgehog",
3
- "version": "6.1.0",
3
+ "version": "6.1.1",
4
4
  "description": "Hedgehog build discipline: ordered, tested, verified build steps.",
5
5
  "contextFileName": "GEMINI.md"
6
6
  }