@skyf0xx/hedgehog 6.2.1 → 6.2.3

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/README.md CHANGED
@@ -12,6 +12,7 @@ English | [简体中文](README.zh-CN.md)
12
12
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
13
13
  [![CodeQL](https://img.shields.io/github/actions/workflow/status/skyf0xx/hedgehog/codeql.yml?branch=master&label=CodeQL)](https://github.com/skyf0xx/hedgehog/actions/workflows/codeql.yml)
14
14
  [![Check](https://img.shields.io/github/actions/workflow/status/skyf0xx/hedgehog/check.yml?branch=master&label=Check)](https://github.com/skyf0xx/hedgehog/actions/workflows/check.yml)
15
+ [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/14399/badge)](https://www.bestpractices.dev/projects/14399)
15
16
 
16
17
  AI can write code in seconds.
17
18
 
package/README.zh-CN.md CHANGED
@@ -12,6 +12,7 @@
12
12
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
13
13
  [![CodeQL](https://img.shields.io/github/actions/workflow/status/skyf0xx/hedgehog/codeql.yml?branch=master&label=CodeQL)](https://github.com/skyf0xx/hedgehog/actions/workflows/codeql.yml)
14
14
  [![Check](https://img.shields.io/github/actions/workflow/status/skyf0xx/hedgehog/check.yml?branch=master&label=Check)](https://github.com/skyf0xx/hedgehog/actions/workflows/check.yml)
15
+ [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/14399/badge)](https://www.bestpractices.dev/projects/14399)
15
16
 
16
17
  AI 能在几秒钟内写出代码。
17
18
 
package/bin/cli.mjs CHANGED
@@ -78,17 +78,13 @@ import {
78
78
  createWorktree,
79
79
  hedgehogWorktrees,
80
80
  branchName,
81
- worktreePath,
82
81
  intentReadyToMerge,
83
- intentTaskStatuses,
84
82
  mergeBranch,
85
83
  removeWorktree,
86
84
  intentFileCommitted,
87
85
  onHedgehogBranch,
88
- loadAbandoned,
89
86
  writeAbandonedFile,
90
87
  applyAbandonment,
91
- replayAbandonments,
92
88
  ABANDONED_DIR,
93
89
  } from '../src/db/worktree.mjs';
94
90
  import { loadOverrides, addOverride, orphanedOverrides, OVERRIDES_DIR } from '../src/db/overrides.mjs';
@@ -126,7 +122,6 @@ const BLOCKED_REASON_LABELS = {
126
122
  const __dirname = dirname(fileURLToPath(import.meta.url));
127
123
  const PKG_ROOT = resolve(__dirname, '..');
128
124
  const DEST_ROOT = process.cwd();
129
- const DEFAULT_CORE = 'full-stack-app';
130
125
 
131
126
  // The version of the payload this CLI carries — what `init` and
132
127
  // `update` stamp into the project they write to.
@@ -3342,7 +3337,7 @@ async function reconcileCommand(args) {
3342
3337
  if (sub === 'list') {
3343
3338
  const reconciliations = await loadReconciliations();
3344
3339
  const db = openDb({ readOnly: true });
3345
- let orphaned = [];
3340
+ let orphaned;
3346
3341
  try {
3347
3342
  orphaned = orphanedReconciliations(db, reconciliations);
3348
3343
  } finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyf0xx/hedgehog",
3
- "version": "6.2.1",
3
+ "version": "6.2.3",
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": {
@@ -14,7 +14,8 @@
14
14
  "hedgehog": "bin/cli.mjs"
15
15
  },
16
16
  "scripts": {
17
- "check": "node scripts/check.mjs",
17
+ "lint": "eslint .",
18
+ "check": "npm run lint && node scripts/check.mjs",
18
19
  "repro": "node --experimental-sqlite repro/run-all.mjs",
19
20
  "release": "node scripts/bump-package-version.mjs",
20
21
  "release:plugin": "node scripts/bump-plugin-version.mjs"
@@ -43,5 +44,9 @@
43
44
  "scaffold",
44
45
  "hedgehog"
45
46
  ],
46
- "license": "MIT"
47
+ "license": "MIT",
48
+ "devDependencies": {
49
+ "@eslint/js": "^10.0.1",
50
+ "eslint": "^10.9.1"
51
+ }
47
52
  }
@@ -218,9 +218,6 @@ export function formatShowcasePrompt() {
218
218
  " 🚀 Want to show off what you're building?",
219
219
  '',
220
220
  ` Add your repo to Hedgehog's public showcase: ${SHOWCASE_REPO_URL}`,
221
- ' Sharing commits your repo URL, the core you built with, and an',
222
- ' optional description into that public repo — public data, not',
223
- ' private telemetry, and entirely optional.',
224
221
  '',
225
222
  ' Then offer exactly three options and wait:',
226
223
  '',
package/src/db/drift.mjs CHANGED
@@ -32,7 +32,6 @@ import {
32
32
  CORE_MODULE,
33
33
  loadIntentDependencies,
34
34
  } from './plan.mjs';
35
- import { loadOverrides } from './overrides.mjs';
36
35
 
37
36
  // Statuses a field rewrite is safe on: nothing has been built, leased,
38
37
  // committed, or failed against these rows yet, so moving their ALLOWED
package/src/db/gate.mjs CHANGED
@@ -97,7 +97,7 @@ export async function commitGateStatus(projectRoot) {
97
97
  };
98
98
  }
99
99
 
100
- let script = '';
100
+ let script;
101
101
  try {
102
102
  script = await readFile(hook, 'utf8');
103
103
  } catch {
package/src/db/notes.mjs CHANGED
@@ -79,7 +79,7 @@ export async function loadNotes(notesDir = NOTES_DIR) {
79
79
  try {
80
80
  parsed = JSON.parse(await readFile(path, 'utf8'));
81
81
  } catch (err) {
82
- throw new Error(`could not read notes file ${path}: ${err.message}`);
82
+ throw new Error(`could not read notes file ${path}: ${err.message}`, { cause: err });
83
83
  }
84
84
  const { task, notes } = validateNotesFile(parsed, path);
85
85
  byTask.set(task, notes);
@@ -122,7 +122,7 @@ export async function loadOverrides(overridesDir = OVERRIDES_DIR) {
122
122
  try {
123
123
  parsed = JSON.parse(await readFile(path, 'utf8'));
124
124
  } catch (err) {
125
- throw new Error(`could not read override file ${path}: ${err.message}`);
125
+ throw new Error(`could not read override file ${path}: ${err.message}`, { cause: err });
126
126
  }
127
127
  const record = validateOverride(parsed, path);
128
128
  if (!byTask.has(record.task)) byTask.set(record.task, []);
package/src/db/plan.mjs CHANGED
@@ -19,7 +19,7 @@
19
19
  // graph holds. Without it such a layer compiles once per intent and
20
20
  // every copy but the first is a replay of the same command.
21
21
 
22
- import { composeScope, loadOverrides } from './overrides.mjs';
22
+ import { composeScope } from './overrides.mjs';
23
23
 
24
24
  export function fillModule(template, module) {
25
25
  return template.replaceAll('{module}', module);
@@ -535,7 +535,7 @@ export function planTasks(db, core, overrides = new Map(), { excludeIntentIds =
535
535
  const compiledOnceTaskIds = [];
536
536
  // Once-tasks that a newly compiled intent hangs a prerequisite on.
537
537
  const onceTasksGainingPrereqs = [];
538
- let reopenedOnceTaskIds = [];
538
+ let reopenedOnceTaskIds;
539
539
 
540
540
  db.exec('BEGIN IMMEDIATE');
541
541
  try {
@@ -198,12 +198,12 @@ async function replayIntents(db, intentsDir) {
198
198
  try {
199
199
  parsed = JSON.parse(await readFile(path, 'utf8'));
200
200
  } catch (err) {
201
- throw new Error(`could not read intent file ${path}: ${err.message}`);
201
+ throw new Error(`could not read intent file ${path}: ${err.message}`, { cause: err });
202
202
  }
203
203
  try {
204
204
  records.push({ file, intent: normalizeIntent(parsed) });
205
205
  } catch (err) {
206
- throw new Error(`invalid intent file ${path}: ${err.message}`);
206
+ throw new Error(`invalid intent file ${path}: ${err.message}`, { cause: err });
207
207
  }
208
208
  }
209
209
 
@@ -148,7 +148,7 @@ export async function loadReconciliations(reconciledDir = RECONCILED_DIR) {
148
148
  try {
149
149
  parsed = JSON.parse(await readFile(path, 'utf8'));
150
150
  } catch (err) {
151
- throw new Error(`could not read reconciliation file ${path}: ${err.message}`);
151
+ throw new Error(`could not read reconciliation file ${path}: ${err.message}`, { cause: err });
152
152
  }
153
153
  const record = validateReconciled(parsed, path);
154
154
  byTask.set(record.task, record);
package/src/db/verify.mjs CHANGED
@@ -562,6 +562,7 @@ export function verifyTask(db, taskId, owner) {
562
562
  `the cause (a rejecting git hook, commit signing, or git identity), then ` +
563
563
  `re-run \`hedgehog verify ${task.id} --owner ${owner}\`, or hand it back ` +
564
564
  `with \`hedgehog release ${task.id} --owner ${owner}\`.\n\n${err.message}`,
565
+ { cause: err },
565
566
  );
566
567
  }
567
568
 
@@ -376,7 +376,7 @@ export async function loadAbandoned(abandonedDir = ABANDONED_DIR) {
376
376
  try {
377
377
  parsed = JSON.parse(await readFile(path, 'utf8'));
378
378
  } catch (err) {
379
- throw new Error(`could not read abandonment file ${path}: ${err.message}`);
379
+ throw new Error(`could not read abandonment file ${path}: ${err.message}`, { cause: err });
380
380
  }
381
381
  const record = validateAbandoned(parsed, path);
382
382
  byIntent.set(record.intent, record);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgehog",
3
- "version": "6.2.1",
3
+ "version": "6.2.3",
4
4
  "description": "Hedgehog build discipline: ordered, tested, verified build steps.",
5
5
  "contextFileName": "GEMINI.md"
6
6
  }
@@ -122,14 +122,14 @@ async function packAndExtract(entry) {
122
122
  tarball = join(tmp, JSON.parse(stdout)[0].filename);
123
123
  } catch (err) {
124
124
  await rm(tmp, { recursive: true, force: true });
125
- throw new Error(unresolvableMessage(entry, spec, err));
125
+ throw new Error(unresolvableMessage(entry, spec, err), { cause: err });
126
126
  }
127
127
 
128
128
  try {
129
129
  await execFileAsync('tar', ['-xzf', tarball, '-C', tmp]);
130
130
  } catch (err) {
131
131
  await rm(tmp, { recursive: true, force: true });
132
- throw new Error(`Could not extract ${entry.package} from ${tarball}: ${err.message}`);
132
+ throw new Error(`Could not extract ${entry.package} from ${tarball}: ${err.message}`, { cause: err });
133
133
  }
134
134
 
135
135
  const root = join(tmp, TARBALL_ROOT);
@@ -29,22 +29,31 @@ with a real cost, taken on stated conditions — not the safe default.
29
29
  ## The three exits
30
30
 
31
31
  Decide by the conditions, in order. The first one that holds is the exit.
32
+ Re-plan is checked first, and its check is a read to make, not a
33
+ suspicion to already hold — do not reach Change-work or Tweak without
34
+ having made it.
32
35
 
33
36
  ### Re-plan
34
37
 
35
- The locked planning artifact that governs this project no longer
36
- describes what is being asked for. The core's own loop skill names which
37
- artifact governs the brief and layer sequence for a shipped core,
38
- `.hedgehog/core-design.md` for an authored one, `.hedgehog/adoption.md`
39
- for an adopted one.
38
+ The core's own loop skill names which artifact governs this project
39
+ the brief and layer sequence for a shipped core, `.hedgehog/core-design.md`
40
+ for an authored one, `.hedgehog/adoption.md` for an adopted one. Read
41
+ that artifact, then check:
40
42
 
41
- Route to `planner`'s re-entry pass, which adds intents for new work
42
- without re-running planning from scratch and without disturbing anything
43
- already built.
43
+ - Does anything the request needs contradict a statement locked there —
44
+ an interface, a stack choice, a scope boundary?
44
45
 
45
- Where the artifact's failure means the request is a different project
46
- rather than an extension of this one, say so plainly instead of routing.
47
- That artifact is never rewritten to accommodate new scope.
46
+ If it holds, this is Re-plan regardless of how many layers the request
47
+ reaches or whether every file it touches already exists. Route to
48
+ `planner`'s re-entry pass, which adds intents for new work without
49
+ re-running planning from scratch and without disturbing anything already
50
+ built.
51
+
52
+ Where the contradiction means the request is a different project rather
53
+ than an extension of this one, say so plainly instead of routing. That
54
+ artifact is never rewritten to accommodate new scope.
55
+
56
+ If it does not hold, move on to Change-work.
48
57
 
49
58
  ### Change-work
50
59