@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 +65 -29
- package/package.json +1 -1
- package/src/agents/planner.md +28 -20
- package/src/agents/reviewer.md +3 -2
- package/src/agents/tweaker.md +4 -4
- package/src/hosts/claude-md-merge.mjs +1 -1
- package/src/hosts/gemini/gemini-extension.json +1 -1
- package/src/registry/cores.json +10 -2
- package/src/registry/installed.mjs +23 -29
- package/src/registry/manifest.mjs +0 -1
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
|
|
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))
|
|
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-
|
|
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 `
|
|
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
|
|
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 `
|
|
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
|
|
1070
|
-
//
|
|
1071
|
-
//
|
|
1072
|
-
//
|
|
1073
|
-
//
|
|
1074
|
-
//
|
|
1075
|
-
//
|
|
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('
|
|
1081
|
-
`entry named "
|
|
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
|
-
|
|
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
package/src/agents/planner.md
CHANGED
|
@@ -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.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
- **
|
|
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
|
|
392
|
-
`
|
|
393
|
-
|
|
394
|
-
|
|
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
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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
|
package/src/agents/reviewer.md
CHANGED
|
@@ -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.
|
|
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.
|
package/src/agents/tweaker.md
CHANGED
|
@@ -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
|
|
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
|
|
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 —
|
|
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-
|
|
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')..."`.
|
package/src/registry/cores.json
CHANGED
|
@@ -39,10 +39,18 @@
|
|
|
39
39
|
{
|
|
40
40
|
"name": "authored",
|
|
41
41
|
"package": "@skyf0xx/hedgehog-core-authored",
|
|
42
|
-
"version": "^1.0
|
|
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
|
-
// `
|
|
11
|
-
//
|
|
12
|
-
// writing `.hedgehog/core.yaml` itself, directly, and
|
|
13
|
-
// `recordCore`
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
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 = '
|
|
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.
|
|
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
|
|
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
|
|
58
|
-
*
|
|
59
|
-
*
|
|
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
|
|
64
|
-
return core ? { name: core, version
|
|
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
|