@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 +31 -13
- package/package.json +1 -1
- package/src/hosts/gemini/gemini-extension.json +1 -1
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 {
|
|
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
|
|
495
|
-
//
|
|
496
|
-
//
|
|
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
|
|
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
|
|
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
|
|
591
|
-
//
|
|
592
|
-
//
|
|
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
|
|
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.
|
|
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
|
-
//
|
|
632
|
-
//
|
|
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