@skyf0xx/hedgehog 6.4.0 → 6.4.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
@@ -105,7 +105,12 @@ import { NOOP_DIR } from '../src/db/noop.mjs';
105
105
  import { runFastpath, loadFastpaths, orphanedFastpathTasks, FASTPATH_DIR } from '../src/db/fastpath.mjs';
106
106
  import { HOSTS, HOST_FLAGS, DEFAULT_HOST, availableHosts } from '../src/hosts/index.mjs';
107
107
  import { recordHosts, installedHosts } from '../src/hosts/installed.mjs';
108
- import { wrapSection, stripPhaseBlocks } from '../src/hosts/claude-md-merge.mjs';
108
+ import {
109
+ wrapSection,
110
+ stripPhaseBlocks,
111
+ hasCoreSection,
112
+ appendCoreSection,
113
+ } from '../src/hosts/claude-md-merge.mjs';
109
114
  import {
110
115
  recordVersion,
111
116
  checkForUpdate,
@@ -491,9 +496,9 @@ const PHASE_NAME = 'bootstrap-only';
491
496
  // astro.config.mjs, which the engine has no business knowing about. Both
492
497
  // must hold: a build graph exists, and at least one task has been
493
498
  // compiled into it. The bootstrap file's own {{PROJECT_SUMMARY}}
494
- // placeholder is the third condition (see shedCommand and
495
- // writePlannedFile's self-heal) but is checked per-file, since a
496
- // multi-host project can have several bootstrap files to test.
499
+ // placeholder is the third condition (see summaryUnfilled) but is
500
+ // checked per-file, since a multi-host project can have several
501
+ // bootstrap files to test.
497
502
  async function graphPastBootstrap() {
498
503
  if (!(await exists(DB_PATH))) return false;
499
504
  const db = openDb();
@@ -504,9 +509,18 @@ async function graphPastBootstrap() {
504
509
  }
505
510
  }
506
511
 
512
+ // The shell's placeholder carries its own instructions inside the braces
513
+ // (`{{PROJECT_SUMMARY — 2–4 sentences…}}`), so the match is on the prefix.
514
+ // Bootstrap-only blocks are excluded because the shell's first-message
515
+ // block names the placeholder in prose, and that block is exactly what
516
+ // shedding removes.
517
+ function summaryUnfilled(bootstrapContent) {
518
+ return stripPhaseBlocks(bootstrapContent, PHASE_NAME).includes('{{PROJECT_SUMMARY');
519
+ }
520
+
507
521
  async function canShed(bootstrapContent) {
508
522
  if (!(await graphPastBootstrap())) return false;
509
- return !bootstrapContent.includes('{{PROJECT_SUMMARY}}');
523
+ return !summaryUnfilled(bootstrapContent);
510
524
  }
511
525
 
512
526
  // `hedgehog shed` — strips bootstrap-only content from every host
@@ -550,7 +564,7 @@ async function shedCommand() {
550
564
  const unfilled = [];
551
565
  for (const { rel, abs } of present) {
552
566
  const content = await readFile(abs, 'utf8');
553
- if (content.includes('{{PROJECT_SUMMARY}}')) unfilled.push(rel);
567
+ if (summaryUnfilled(content)) unfilled.push(rel);
554
568
  }
555
569
  if (unfilled.length > 0) {
556
570
  console.error(
@@ -587,13 +601,15 @@ async function writePlannedFile(f) {
587
601
  // `planner` already filled in: {{PROJECT_NAME}}/{{PROJECT_SUMMARY}}
588
602
  // are filled once, by hand or by `planner`, and never touched again.
589
603
  // Starting from the existing file instead of the pristine shell
590
- // whenever it's still missing only {{CORE_SECTION}} preserves that —
591
- // the shell is the base only for a destination that doesn't exist
592
- // yet, or one still holding the raw, unfilled template.
604
+ // whenever those are filled preserves that — whether the core section
605
+ // is still the {{CORE_SECTION}} placeholder or an already-filled,
606
+ // marked section that the new core's content replaces in place. The
607
+ // shell is the base only for a destination that doesn't exist yet, or
608
+ // one still holding the raw, unfilled template.
593
609
  let out = null;
594
610
  if (await exists(f.dest)) {
595
611
  const existing = await readFile(f.dest, 'utf8');
596
- if (!existing.includes('{{PROJECT_NAME}}') && existing.includes('{{CORE_SECTION}}')) {
612
+ if (!existing.includes('{{PROJECT_NAME}}') && hasCoreSection(existing)) {
597
613
  out = existing;
598
614
  } else if (!existing.includes('{{PROJECT_NAME}}') && !f.merge.include) {
599
615
  // Brownfield: hand-written content that predates Hedgehog, with
@@ -623,13 +639,15 @@ async function writePlannedFile(f) {
623
639
  // both paths' output mutually recognizable is what lets
624
640
  // hasCoreSection/appendCoreSection treat a template-filled file
625
641
  // and an appended one the same way on a later re-run.
626
- out = out.replaceAll('{{CORE_SECTION}}', wrapSection(section));
642
+ out = out.includes('{{CORE_SECTION}}')
643
+ ? out.replaceAll('{{CORE_SECTION}}', wrapSection(section))
644
+ : appendCoreSection(out, section);
627
645
  }
628
646
  const dispatch = await readFile(join(PKG_ROOT, f.merge.dispatch), 'utf8');
629
647
  out = out.replaceAll('{{HOST_DISPATCH}}', dispatch.trimEnd());
630
648
  // Self-heal: a project past bootstrap that re-runs `init --force`
631
- // must not silently regain bootstrap-only content just because this
632
- // write started from the pristine shell again.
649
+ // leaves this write with no bootstrap-only content, whether or not
650
+ // `hedgehog shed` ever ran.
633
651
  if (await canShed(out)) out = stripPhaseBlocks(out, PHASE_NAME);
634
652
  await writeFile(f.dest, out);
635
653
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyf0xx/hedgehog",
3
- "version": "6.4.0",
3
+ "version": "6.4.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": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgehog",
3
- "version": "6.4.0",
3
+ "version": "6.4.1",
4
4
  "description": "Hedgehog build discipline: ordered, tested, verified build steps.",
5
5
  "contextFileName": "GEMINI.md"
6
6
  }