@skyf0xx/hedgehog 6.0.5 → 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
@@ -281,19 +281,6 @@ function corePayload(core, h, { hostOnly = false } = {}) {
281
281
  // than generating it live. A core that scaffolds nothing (its
282
282
  // workspace is designed during planning) declares no `workspace`.
283
283
  ...(manifest.workspace ? [{ type: 'dir', root, from: manifest.workspace, to: '.' }] : []),
284
- // A second CLAUDE.md section for cores whose path fills
285
- // {{CORE_SECTION}} after install rather than at it — adoption reads
286
- // it from the project instead of the package it ships in.
287
- ...(manifest.template_adopted
288
- ? [
289
- {
290
- type: 'file',
291
- root,
292
- from: manifest.template_adopted,
293
- to: `.hedgehog/${manifest.template_adopted}`,
294
- },
295
- ]
296
- : []),
297
284
  ];
298
285
  }
299
286
 
@@ -437,6 +424,17 @@ async function writePlannedFile(f) {
437
424
  const existing = await readFile(f.dest, 'utf8');
438
425
  if (!existing.includes('{{PROJECT_NAME}}') && existing.includes('{{CORE_SECTION}}')) {
439
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;
440
438
  }
441
439
  }
442
440
  if (out === null) out = await readFile(join(PKG_ROOT, f.merge.shell), 'utf8');
@@ -526,7 +524,7 @@ ${bold('Usage')}
526
524
  npx @skyf0xx/hedgehog init --pwa-app scaffold the pwa-app core now
527
525
  npx @skyf0xx/hedgehog init --landing-page scaffold the landing-page core now
528
526
  npx @skyf0xx/hedgehog cores list every core this release can install
529
- npx @skyf0xx/hedgehog core record-adopted land the authored core's agents/skills and record
527
+ npx @skyf0xx/hedgehog core record-adopted land the adopted core's agents/skills and record
530
528
  this project as adopted (hedgehog-adopt calls this
531
529
  after writing .hedgehog/core.yaml; not for other cores)
532
530
  npx @skyf0xx/hedgehog init --cursor install for Cursor (default: Claude Code)
@@ -701,11 +699,26 @@ async function init({ force, core, host = DEFAULT_HOST, hostOnly = false, global
701
699
  // the project, so rewriting it loses nothing and never counts as a
702
700
  // conflict — that's what lets a second host be added to a project the
703
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.
704
712
  const conflicts = [];
705
713
  for (const { entry, files } of groups) {
706
714
  if (entry.type === 'generated') continue;
707
715
  for (const f of files) {
708
- 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);
709
722
  }
710
723
  }
711
724
 
@@ -732,7 +745,15 @@ async function init({ force, core, host = DEFAULT_HOST, hostOnly = false, global
732
745
  for (const { files } of groups) {
733
746
  for (const f of files) {
734
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;
735
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
+ }
736
757
  if (already) overwritten++;
737
758
  else written++;
738
759
  const label = already ? yellow('overwrite') : green('create');
@@ -1046,10 +1067,10 @@ async function resolveInstalledCore() {
1046
1067
  }
1047
1068
 
1048
1069
  // `hedgehog core record-adopted` — the record path for `hedgehog-adopt`
1049
- // (shipped in @skyf0xx/hedgehog-core-authored), which brings the
1070
+ // (shipped in @skyf0xx/hedgehog-core-adopted), which brings the
1050
1071
  // discipline to an existing repo by writing `.hedgehog/core.yaml` and
1051
1072
  // `.hedgehog/adoption.md` directly. That path has no `init` step and so
1052
- // never fetches the `authored` package or calls `recordCore` — a no-flag
1073
+ // never fetches the `adopted` package or calls `recordCore` — a no-flag
1053
1074
  // `init` (what the offer skill actually runs before adoption) installs
1054
1075
  // only the shared engine payload, never a core's own agents/skills, and
1055
1076
  // `bootstrap` (the only other place a core package gets fetched) is
@@ -1061,24 +1082,27 @@ async function resolveInstalledCore() {
1061
1082
  // `core.yaml` (the shipped-core workspace marker) and finds nothing for
1062
1083
  // an adopted repo's `.hedgehog/core.yaml`, so it would read as "no core
1063
1084
  // yet" and update would silently rewrite `.claude/agents`/`.claude/skills`
1064
- // down to just the shared payload, deleting the authored package's files
1085
+ // down to just the shared payload, deleting the adopted package's files
1065
1086
  // with nothing put back.
1066
1087
  //
1067
- // This command is both fixes at once: it fetches the `authored` package
1088
+ // This command is both fixes at once: it fetches the `adopted` package
1068
1089
  // and lands its agents/skills for every host this project already has
1069
- // installed (never its workspace, template, or vendor_skills — adoption
1070
- // already wrote its own CLAUDE.md section and root workspace is the one
1071
- // thing adoption must never touch), then records the core with
1072
- // `adopted: true` so `update` refreshes it correctly from here on.
1073
- // Idempotent and safe to re-run e.g. from a later `hedgehog-adopt` pass
1074
- // adding new change-work since it always overwrites from the current
1075
- // package rather than merging.
1090
+ // installed (never its workspace or vendor_skills — root workspace is
1091
+ // the one thing adoption must never touch), plus a local copy of its
1092
+ // `CLAUDE.core.md` at `.hedgehog/CLAUDE.core.md` `hedgehog-adopt`'s own
1093
+ // Confirm & Lock step reads that file from there to merge into root
1094
+ // `CLAUDE.md`, since a no-flag `init` never ran the normal
1095
+ // `{{CORE_SECTION}}` merge this project's core would otherwise get then
1096
+ // records the core the same as any other, naming `adopted`, so `update`
1097
+ // refreshes it correctly from here on. Idempotent and safe to re-run —
1098
+ // e.g. from a later `hedgehog-adopt` pass adding new change-work — since
1099
+ // it always overwrites from the current package rather than merging.
1076
1100
  async function recordAdoptedCommand() {
1077
1101
  const entry = await resolveCore(ADOPTED_CORE_NAME);
1078
1102
  if (!entry) {
1079
1103
  console.error(
1080
- `${red('authored core not in this release.')} This Hedgehog release's registry has no\n` +
1081
- `entry named "authored" — nothing to install. Update Hedgehog and retry.\n`,
1104
+ `${red('adopted core not in this release.')} This Hedgehog release's registry has no\n` +
1105
+ `entry named "adopted" — nothing to install. Update Hedgehog and retry.\n`,
1082
1106
  );
1083
1107
  process.exitCode = 1;
1084
1108
  return;
@@ -1106,7 +1130,19 @@ async function recordAdoptedCommand() {
1106
1130
  }
1107
1131
  }
1108
1132
 
1109
- await recordCore(DEST_ROOT, { name: core.manifest.name, version: core.version, adopted: true });
1133
+ const templateFile = (
1134
+ await plannedFiles({
1135
+ type: 'file',
1136
+ root: core.root,
1137
+ from: core.manifest.template,
1138
+ to: '.hedgehog/CLAUDE.core.md',
1139
+ })
1140
+ )[0];
1141
+ await writePlannedFile(templateFile);
1142
+ written++;
1143
+ console.log(` ${green('install')} ${relative(DEST_ROOT, templateFile.dest)}`);
1144
+
1145
+ await recordCore(DEST_ROOT, { name: core.manifest.name, version: core.version });
1110
1146
 
1111
1147
  console.log(
1112
1148
  `\n${green(bold('Adopted core recorded.'))} ${dim(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyf0xx/hedgehog",
3
- "version": "6.0.5",
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": {
@@ -31,7 +31,8 @@ You run on two paths, and Workflow step 2 decides which:
31
31
  scope into additional intents without re-running the BMAD shelf.
32
32
  Landing-page has no module axis, so this path doesn't apply to it — see
33
33
  the landing-page constraint below for where its new scope actually
34
- goes.
34
+ goes. New change-work on an **adopted** core is a separate case again —
35
+ see "An existing repo, ongoing adoption" below.
35
36
 
36
37
  Either path is entered when the user says "plan", "scope", "break down",
37
38
  asks for something that's new scope rather than a tweak (routed here by
@@ -200,14 +201,16 @@ none of them is a description matching a `when` paragraph:
200
201
  repo", "add Hedgehog to my existing project", "I want scope/verify
201
202
  enforcement on my changes here"). This is a distinct question from
202
203
  everything above: it's not about which core fits new work, because no
203
- new workspace gets built at all. Route straight to `hedgehog-adopt`
204
- bootstrap and every other Phase 0 outcome are skipped entirely, since
205
- there is no workspace to scaffold and no shipped stack to adopt toward.
206
- `hedgehog-adopt` runs its own read-only intake and writes its own
207
- `.hedgehog/core.yaml`; don't run `hedgehog-planning-intake`'s BMAD shelf
208
- first the drivers that skill elicits (persistence, stack, deployment
209
- target) are already settled facts of the existing repo, not open
210
- decisions.
204
+ new workspace gets built at all. This project gets the **adopted
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
210
+ its own read-only intake and writes its own `.hedgehog/core.yaml`;
211
+ don't run `hedgehog-planning-intake`'s BMAD shelf first — the drivers
212
+ that skill elicits (persistence, stack, deployment target) are already
213
+ settled facts of the existing repo, not open decisions.
211
214
 
212
215
  State the decision plainly before Phase 1 begins, with the one-line
213
216
  reason it landed there — this is cheap to correct now and expensive once
@@ -375,7 +378,7 @@ as full-stack-app's Auth/Queue/Mobile trio.
375
378
  the build graph.
376
379
  - **landing-page**: owns `.hedgehog/BMAD/` and
377
380
  `.hedgehog/chain/00-brief.md` as artifacts.
378
- - **brownfield adoption**: owns nothing here — `hedgehog-adopt` owns
381
+ - **adopted**: owns nothing here — `hedgehog-adopt` owns
379
382
  `.hedgehog/core.yaml` and `.hedgehog/adoption.md`, the same way an
380
383
  authored core's design is `hedgehog-core-design`'s.
381
384
 
@@ -388,18 +391,23 @@ as full-stack-app's Auth/Queue/Mobile trio.
388
391
  run.** Continue at step 3.
389
392
  - **No intents in the graph, and the request is adoption onto an
390
393
  existing repo → brownfield first run.** Skip Phase 0's core
391
- selection and every step below through step 9 go straight to
392
- `hedgehog-adopt`. It runs its own intake and Confirm & Lock, writes
393
- `.hedgehog/core.yaml` and `.hedgehog/adoption.md`, and adds the
394
- 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.
395
401
  - **One or more intents, on `.hedgehog/core.yaml` written by
396
402
  `hedgehog-adopt` → adoption re-entry.** New change-work on a repo
397
- already under adoption. Skip steps 3 through 9 route straight to
398
- `hedgehog-adopt` again instead, same as brownfield first run above.
399
- It owns everything the other path's steps 5, 7, 8, and 9 would
400
- otherwise do: it sizes the request (a large or ambiguous one gets its
401
- own short clarifying pass, a clear small one doesn't), adds the
402
- 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
403
411
  (planning): adopt change`. Don't run `hedgehog-planning-intake`'s
404
412
  Re-entry pass here — there is no BMAD archive to read as context on
405
413
  this path, since adoption never runs one. Return the summary (step
@@ -30,7 +30,8 @@ Everything the commit gate already enforces — the layer's own `verify`
30
30
  command, and whatever typecheck/lint/test it runs — is out of scope;
31
31
  don't re-report a green gate. Read the core's own design first: its loop
32
32
  skill for a shipped core, `.hedgehog/core.yaml` and
33
- `.hedgehog/core-design.md` for an authored one. That is where the layer
33
+ `.hedgehog/core-design.md` for an authored one, `.hedgehog/core.yaml` and
34
+ `.hedgehog/adoption.md` for an adopted one. That is where the layer
34
35
  boundaries, the interface between them, and this core's own conventions
35
36
  are stated. Your checklist is derived from it, not from a stack you
36
37
  recognize.
@@ -96,7 +97,7 @@ Check what the gate structurally cannot:
96
97
  - Don't nitpick style. Focus on structural correctness relative to the
97
98
  stack and build order the core's own design fixed — its loop and
98
99
  bootstrap skills on a shipped core, `.hedgehog/core-design.md` on an
99
- authored one.
100
+ authored one, `.hedgehog/adoption.md` on an adopted one.
100
101
  - 3 real findings beats 20 suggestions. This review sits at a phase or
101
102
  layer boundary, not mid-Loop — don't slow the Loop down for anything
102
103
  that isn't load-bearing for the work that comes next.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: tweaker
3
- description: Use once a core's build is complete (every task in the build graph `complete`) and the user is offered a fresh-context session to iterate. Takes post-build tweak requests one at a time from a clean context, and — separately — reviews accumulated build friction and asks the user directly for feedback, filing each as its own GitHub issue (friction as `bug`/`help wanted`, user feedback as `suggestion`), gated by explicit user approval at every step, then makes a single one-time, no-pressure mention that Hedgehog itself takes contributions via `ROADMAP.md`. Shared by every core with a Stop Condition — not an adopted repo (`hedgehog-adopt`), which has none; there, new change-work goes straight through `hedgehog-adopt` and `hedgehog-authored-loop` instead.
3
+ description: Use once a core's build is complete (every task in the build graph `complete`) and the user is offered a fresh-context session to iterate. Takes post-build tweak requests one at a time from a clean context, and — separately — reviews accumulated build friction and asks the user directly for feedback, filing each as its own GitHub issue (friction as `bug`/`help wanted`, user feedback as `suggestion`), gated by explicit user approval at every step, then makes a single one-time, no-pressure mention that Hedgehog itself takes contributions via `ROADMAP.md`. Shared by every core with a Stop Condition — not the `adopted` core, which has none; there, new change-work goes straight through `hedgehog-adopt` and `hedgehog-authored-loop` instead.
4
4
  model: sonnet
5
5
  color: green
6
6
  tools: Read, Glob, Grep, Edit, Write, Bash
@@ -15,7 +15,7 @@ conversation. You start from a cleared context on purpose. Re-read the
15
15
  friction log (`hedgehog friction list`) and the commit log rather than
16
16
  expecting anything to be remembered.
17
17
 
18
- **Not for an adopted repo (`.hedgehog/core.yaml` written by
18
+ **Not for the `adopted` core (`.hedgehog/core.yaml` written by
19
19
  `hedgehog-adopt`).** That core has no Stop Condition and no "build
20
20
  finished" moment for you to follow — adoption is the permanent way
21
21
  change lands, not a project with an end. A request there is just the
@@ -44,8 +44,8 @@ straight to job 1.
44
44
 
45
45
  None of its own — you work inside whichever core's stack is already
46
46
  installed (a shipped core's, or the stack an authored core's
47
- `.hedgehog/core-design.md` names — an adopted repo never reaches you, per
48
- the note above), editing the same files the core's own build agents
47
+ `.hedgehog/core-design.md` names — the `adopted` core never reaches you,
48
+ per the note above), editing the same files the core's own build agents
49
49
  would. `gh` (GitHub CLI) for issue creation only, and only against
50
50
  `skyf0xx/hedgehog`, never the project's own remote.
51
51
 
@@ -13,7 +13,7 @@
13
13
  // instead of substituting into one.
14
14
  //
15
15
  // Referenced by name (not substance) from hedgehog-adopt's own SKILL.md
16
- // in the @skyf0xx/hedgehog-core-authored package — that skill invokes
16
+ // in the @skyf0xx/hedgehog-core-adopted package — that skill invokes
17
17
  // `appendCoreSection` the same way it already invokes `loadCore` from
18
18
  // src/db/core.mjs, via `node -e "import('<path-to-hedgehog-install>/
19
19
  // src/hosts/claude-md-merge.mjs')..."`.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgehog",
3
- "version": "6.0.5",
3
+ "version": "6.1.1",
4
4
  "description": "Hedgehog build discipline: ordered, tested, verified build steps.",
5
5
  "contextFileName": "GEMINI.md"
6
6
  }
@@ -39,10 +39,18 @@
39
39
  {
40
40
  "name": "authored",
41
41
  "package": "@skyf0xx/hedgehog-core-authored",
42
- "version": "^1.0.2",
42
+ "version": "^1.1.0",
43
43
  "language": "typescript",
44
44
  "repository": "https://github.com/skyf0xx/hedgehog-core-authored",
45
- "selects_when": "Neither shipped core fits, but the description names a real artifact a Builder step would produce — just not in either shipped core's shape. This core is designed by the planner rather than chosen from a fixed set: hedgehog-planning-intake's Phase 0 elicits the drivers first, then hedgehog-core-design names the system shape, picks the stack, derives the layers, and writes .hedgehog/core.yaml. It carries the same enforcement as a shipped core — ordered layers, scoped file access, verification before completion — but the sequence is designed for this project rather than battle-tested across many."
45
+ "selects_when": "Neither shipped core fits, but the description names a real artifact a Builder step would produce — just not in either shipped core's shape. This core is designed by the planner rather than chosen from a fixed set: hedgehog-planning-intake's Phase 0 elicits the drivers first, then hedgehog-core-design names the system shape, picks the stack, derives the layers, and writes .hedgehog/core.yaml. It carries the same enforcement as a shipped core — ordered layers, scoped file access, verification before completion — but the sequence is designed for this project rather than battle-tested across many. This is the core most often confused with adopted: authored designs a workspace from scratch for a project being built new, while adopted brings Hedgehog's discipline to a repo that already exists — route here only when there is no existing codebase this work is being added to."
46
+ },
47
+ {
48
+ "name": "adopted",
49
+ "package": "@skyf0xx/hedgehog-core-adopted",
50
+ "version": "^1.0.0",
51
+ "language": "typescript",
52
+ "repository": "https://github.com/skyf0xx/hedgehog-core-adopted",
53
+ "selects_when": "The description is about bringing Hedgehog's discipline to a codebase that already exists, rather than building something new — the repo already has real source files, or the user says so explicitly: \"adopt this repo\", \"add Hedgehog to my existing project\", \"I want scope/verify enforcement on my changes here\". Not chosen by matching a `when` paragraph the way a shipped core is: hedgehog-adopt reads the repo read-only, proposes a linear-chain .hedgehog/core.yaml whose verify commands are the repo's own, and writes only .hedgehog/ — never a workspace, never a stack migration. This is the core most often confused with authored: adopted brings discipline to an existing repo, while authored designs and scaffolds a workspace from scratch for something being built new — route here only when the work is landing on a codebase that already exists."
46
54
  }
47
55
  ]
48
56
  }
@@ -7,21 +7,19 @@
7
7
  // the core — `installedCore` returns null until then, and `update` limits
8
8
  // itself to the shared payload.
9
9
  //
10
- // `authored` has a second entry point with no `init` step at all:
11
- // `hedgehog-adopt` brings the discipline to a repo that already exists by
12
- // writing `.hedgehog/core.yaml` itself, directly, and never calls
13
- // `recordCore` (see that skill, shipped in
14
- // @skyf0xx/hedgehog-core-authored adoption has no npm package of its
15
- // own to fetch, just a core.yaml and adoption.md derived from the
16
- // existing repo). `hedgehog core record-adopted` (bin/cli.mjs) is the
17
- // record path for that case instead: it lands the authored package's
18
- // agents/skills otherwise never installed, since adoption's `init` runs
19
- // with no `--core` flag and `bootstrap` is skipped entirely for adoption
20
- // and calls `recordCore` with `adopted: true`. That flag is the one
21
- // thing distinguishing an adopted record from a normal one: `update`'s
22
- // resolveInstalledCore refreshes an adopted project's payload the same as
23
- // any other (same package, same agents/skills, kept current) but must
24
- // never treat `record.name` changing upstream, or the record going
10
+ // `adopted` has no `init` step at all: `hedgehog-adopt` (shipped in
11
+ // @skyf0xx/hedgehog-core-adopted) brings the discipline to a repo that
12
+ // already exists by writing `.hedgehog/core.yaml` itself, directly, and
13
+ // never calls `recordCore`. `hedgehog core record-adopted` (bin/cli.mjs)
14
+ // is the record path for that case instead: it fetches the `adopted`
15
+ // package and lands its agents/skills otherwise never installed, since
16
+ // adoption's `init` runs with no `--core` flag and `bootstrap` is skipped
17
+ // entirely for adoption and calls `recordCore` the same as any other
18
+ // core, naming `adopted`. That name is the one thing distinguishing an
19
+ // adopted record from a normal one: `update`'s resolveInstalledCore
20
+ // refreshes an adopted project's payload the same as any other (same
21
+ // package, same agents/skills, kept current) but must never treat
22
+ // `record.name === 'adopted'` changing upstream, or the record going
25
23
  // missing, as license to fetch and install a *different* core the way it
26
24
  // would for a project that chose one at `init` — adoption's core is a
27
25
  // fixed fact of the repo (its `.hedgehog/core.yaml`), not a choice
@@ -30,38 +28,34 @@
30
28
  import { readFile, writeFile, mkdir } from 'node:fs/promises';
31
29
  import { dirname, join } from 'node:path';
32
30
 
33
- export const ADOPTED_CORE_NAME = 'authored';
31
+ export const ADOPTED_CORE_NAME = 'adopted';
34
32
 
35
33
  const CORE_PATH = '.hedgehog/core.json';
36
34
 
37
35
  /**
38
36
  * Record the core a project installed and the exact version resolved for
39
37
  * it. Written after the core's files land, so the record describes what
40
- * is on disk. `adopted: true` marks a record written by `hedgehog core
41
- * record-adopted` rather than by `init` — see the module comment above.
38
+ * is on disk.
42
39
  */
43
- export async function recordCore(root, { name, version, adopted = false }) {
40
+ export async function recordCore(root, { name, version }) {
44
41
  const path = join(root, CORE_PATH);
45
42
  await mkdir(dirname(path), { recursive: true });
46
43
  await writeFile(
47
44
  path,
48
- `${JSON.stringify(
49
- { core: name, version, installedAt: new Date().toISOString(), ...(adopted ? { adopted: true } : {}) },
50
- null,
51
- 2,
52
- )}\n`,
45
+ `${JSON.stringify({ core: name, version, installedAt: new Date().toISOString() }, null, 2)}\n`,
53
46
  );
54
47
  }
55
48
 
56
49
  /**
57
- * The core `update` should refresh — `{ name, version, adopted }` — or
58
- * null when this project has no core installed yet. `adopted` is always a
59
- * boolean, `false` for every record `init` writes.
50
+ * The core `update` should refresh — `{ name, version }` — or null when
51
+ * this project has no core installed yet. `name === 'adopted'` marks a
52
+ * record written by `hedgehog core record-adopted` rather than by `init`
53
+ * — see the module comment above.
60
54
  */
61
55
  export async function installedCore(root) {
62
56
  try {
63
- const { core, version, adopted } = JSON.parse(await readFile(join(root, CORE_PATH), 'utf8'));
64
- return core ? { name: core, version, adopted: adopted === true } : null;
57
+ const { core, version } = JSON.parse(await readFile(join(root, CORE_PATH), 'utf8'));
58
+ return core ? { name: core, version } : null;
65
59
  } catch {
66
60
  return null;
67
61
  }
@@ -11,7 +11,6 @@
11
11
  // engine: "^<major>.<minor>.<patch>" which CLI versions can install it
12
12
  // workspace: workspace/ omitted by a core that scaffolds nothing
13
13
  // template: CLAUDE.core.md fills the CLAUDE.md shell's core section
14
- // template_adopted: <path> optional second section, for adoption
15
14
  // agents: [<name>, ...] agents/<name>.md in the package
16
15
  // skills: [<name>, ...] skills/<name>/ in the package
17
16
  // vendor_skills: [<name>, ...] vendor-skills/<name>/ in the package