@skyf0xx/hedgehog 6.2.10 → 6.2.11

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
@@ -13,10 +13,11 @@
13
13
  // npx @skyf0xx/hedgehog update refresh the installed agents + skills
14
14
  // npx @skyf0xx/hedgehog --help
15
15
 
16
- import { cp, mkdir, access, readdir, stat, rm, readFile, writeFile } from 'node:fs/promises';
16
+ import { cp, mkdir, access, readdir, stat, rm, readFile, writeFile, mkdtemp } from 'node:fs/promises';
17
17
  import { constants, existsSync, realpathSync } from 'node:fs';
18
18
  import { fileURLToPath } from 'node:url';
19
19
  import { dirname, join, relative, resolve } from 'node:path';
20
+ import { tmpdir } from 'node:os';
20
21
  import { spawn, execFileSync } from 'node:child_process';
21
22
  import { dbInit, DB_PATH, dbAbsPath, openDb, openDbAt } from '../src/db/init.mjs';
22
23
  import { loadCore, lintCore, isModuleAxis } from '../src/db/core.mjs';
@@ -121,6 +122,40 @@ const BLOCKED_REASON_LABELS = {
121
122
 
122
123
  const __dirname = dirname(fileURLToPath(import.meta.url));
123
124
  const PKG_ROOT = resolve(__dirname, '..');
125
+
126
+ // The copywriting core never installs into the directory it was invoked
127
+ // from — every draft it produces is ephemeral scratch, and only the
128
+ // finished piece is meant to reach the user's real project (as a plain
129
+ // file, copied out by hedgehog-copywriting-loop's own courtesy-export
130
+ // step). Relying on every caller (an agent reading a skill's prose, a
131
+ // human typing the command by hand) to remember to cd into a scratch
132
+ // directory first is exactly the kind of instruction that gets skipped
133
+ // under context pressure — so `init --copywriting` enforces it here
134
+ // instead, unconditionally, before DEST_ROOT is ever read. This has to
135
+ // run before any other module-level state is computed from `process.cwd()`
136
+ // — a raw argv scan rather than the full async arg parser lower in this
137
+ // file, since nothing below has run yet at this point in module load.
138
+ const ORIGDIR = process.cwd();
139
+ {
140
+ // Catches both the shorthand (`--copywriting`) and the generic named-core
141
+ // form (`--core copywriting`, `--core=copywriting`) — namedCore()/
142
+ // coreFlags() do the real resolution later, async, off the registry, but
143
+ // this has to run synchronously before that and before DEST_ROOT below,
144
+ // so it matches the raw argv directly rather than waiting for it.
145
+ const rest = process.argv.slice(2);
146
+ const coreIdx = rest.indexOf('--core');
147
+ const isCopywriting =
148
+ rest.includes('--copywriting') ||
149
+ rest[coreIdx + 1] === 'copywriting' ||
150
+ rest.includes('--core=copywriting');
151
+ if (isCopywriting) {
152
+ const scratch = await mkdtemp(join(tmpdir(), 'hedgehog-copywriting-'));
153
+ process.chdir(scratch);
154
+ // No ANSI helpers yet — `dim` etc. are defined further down, after
155
+ // DEST_ROOT, and this has to run before DEST_ROOT is computed.
156
+ console.log(` (copywriting installs to a scratch directory, never here — using ${scratch})`);
157
+ }
158
+ }
124
159
  const DEST_ROOT = process.cwd();
125
160
 
126
161
  // The version of the payload this CLI carries — what `init` and
@@ -865,9 +900,22 @@ async function init({ force, core, host = DEFAULT_HOST, hostOnly = false, global
865
900
  ` 0. ${bold(`npx @skyf0xx/hedgehog@latest update`)} ${dim(`(picks up ${globalInstall.latest} before intake begins)`)}`,
866
901
  );
867
902
  }
868
- // A core that landed a workspace landed its root package.json with it,
869
- // so there are dependencies to install before the first commit.
870
- if (core?.manifest.workspace) {
903
+ // Copywriting never lands anywhere the caller can see directly (see the
904
+ // chdir into a scratch tempdir at module load, above) — the ordinary
905
+ // "commit here, describe what you want to build" steps assume a project
906
+ // the human is looking at, which this scratch directory isn't. Point at
907
+ // the loop skill instead; it owns pnpm install, the draft/checkCopy()
908
+ // cycle, and the courtesy export of the finished piece back to ORIGDIR.
909
+ if (core?.manifest.name === 'copywriting') {
910
+ console.log(
911
+ ` 1. Read ${bold(`${HOSTS[host].skillsDir}/hedgehog-copywriting-loop/SKILL.md`)} and follow it`,
912
+ );
913
+ console.log(dim(` for everything from here, staying inside ${bold(DEST_ROOT)}.`));
914
+ console.log(
915
+ dim(` The finished piece lands back at ${bold(ORIGDIR)} on its own — nothing`),
916
+ );
917
+ console.log(dim(' else needs doing there.'));
918
+ } else if (core?.manifest.workspace) {
871
919
  // `pnpm install` before the first commit, not after it. The core's
872
920
  // commit gate is lefthook, and lefthook's hooks are written by its
873
921
  // own postinstall — so a commit made before the install is a commit
@@ -900,19 +948,23 @@ async function init({ force, core, host = DEFAULT_HOST, hostOnly = false, global
900
948
  // the session that ran this install — no restart, no dependence on whether
901
949
  // the harness re-scans its agent directory, and no chance of a bare
902
950
  // `planner` resolving to an unrelated global agent of the same name.
903
- const agents = HOSTS[host].agentsDir;
904
- const skills = HOSTS[host].skillsDir;
905
- console.log(
906
- dim(
907
- ` Read ${bold(`${agents}/planner.md`)} and follow it — it runs planning\n` +
908
- ` intake (${skills}/hedgehog-planning-intake/SKILL.md), then hands\n` +
909
- ` off to ${agents}/bootstrap.md.\n` +
910
- ` Some hosts register agents/skills once, at session start: if a\n` +
911
- ` later dispatch by name reports one of these as not found, read\n` +
912
- ` its file directly instead — it becomes dispatchable by name after\n` +
913
- ` a session restart or a fresh context.`,
914
- ),
915
- );
951
+ // Copywriting's own branch above already named its hand-off (the loop
952
+ // skill, not planner/bootstrap — this core has no bootstrap step).
953
+ if (core?.manifest.name !== 'copywriting') {
954
+ const agents = HOSTS[host].agentsDir;
955
+ const skills = HOSTS[host].skillsDir;
956
+ console.log(
957
+ dim(
958
+ ` Read ${bold(`${agents}/planner.md`)} and follow it it runs planning\n` +
959
+ ` intake (${skills}/hedgehog-planning-intake/SKILL.md), then hands\n` +
960
+ ` off to ${agents}/bootstrap.md.\n` +
961
+ ` Some hosts register agents/skills once, at session start: if a\n` +
962
+ ` later dispatch by name reports one of these as not found, read\n` +
963
+ ` its file directly instead — it becomes dispatchable by name after\n` +
964
+ ` a session restart or a fresh context.`,
965
+ ),
966
+ );
967
+ }
916
968
  console.log();
917
969
  if (core) {
918
970
  console.log(dim(`Core: ${bold(core.manifest.name)} ${dim(`(${core.version})`)}.`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyf0xx/hedgehog",
3
- "version": "6.2.10",
3
+ "version": "6.2.11",
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": {
@@ -17,8 +17,7 @@
17
17
  "lint": "eslint .",
18
18
  "check": "npm run lint && node scripts/check.mjs",
19
19
  "repro": "node --experimental-sqlite repro/run-all.mjs",
20
- "release": "node scripts/bump-package-version.mjs",
21
- "release:plugin": "node scripts/bump-plugin-version.mjs"
20
+ "release": "node scripts/bump-package-version.mjs"
22
21
  },
23
22
  "files": [
24
23
  "bin",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgehog",
3
- "version": "6.2.10",
3
+ "version": "6.2.11",
4
4
  "description": "Hedgehog build discipline: ordered, tested, verified build steps.",
5
5
  "contextFileName": "GEMINI.md"
6
6
  }