@skyf0xx/hedgehog 4.0.1 → 4.0.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
@@ -60,6 +60,14 @@ npx @skyf0xx/hedgehog graph # show graph
60
60
 
61
61
  ![The Hedgehog build graph](https://raw.githubusercontent.com/skyf0xx/hedgehog/master/docs/images/graph.png)
62
62
 
63
+ ## Parallel by Default
64
+
65
+ Every dependency is explicit, so Hedgehog knows which tasks can run in parallel.
66
+
67
+ ![Comparison](https://raw.githubusercontent.com/skyf0xx/hedgehog/master/docs/images/comparison.png)
68
+
69
+ Agents fan out to give you great outcomes at **faster speeds**.
70
+
63
71
  ## What Hedgehog builds
64
72
 
65
73
  ### Full-stack applications
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyf0xx/hedgehog",
3
- "version": "4.0.1",
3
+ "version": "4.0.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": {
@@ -86,6 +86,10 @@ needed.
86
86
 
87
87
  ## Constraints
88
88
 
89
+ - Default to no comments. Add one only when the WHY is non-obvious — a
90
+ hidden constraint, a workaround for a specific bug, an invariant the
91
+ code alone can't convey. Never comment WHAT the code does; a
92
+ well-named schema field, function, or variable already says that.
89
93
  - Never self-certify a task as done or run `git commit` for its changes —
90
94
  see Workflow step 3.
91
95
  - Never import another module's repository, service, or schema directly
@@ -89,6 +89,10 @@ don't reach for a second one.
89
89
 
90
90
  ## Constraints
91
91
 
92
+ - Default to no comments. Add one only when the WHY is non-obvious — a
93
+ hidden constraint, a workaround for a specific bug, an invariant the
94
+ code alone can't convey. Never comment WHAT the code does; a
95
+ well-named component, hook, or variable already says that.
92
96
  - Never self-certify a task as done or run `git commit` for its changes —
93
97
  see Workflow step 3.
94
98
  - Never add a data-fetching call that bypasses the hook/contract layer —
@@ -68,6 +68,10 @@ parsing and typing, and the layer after it consumes the result.
68
68
 
69
69
  ## Constraints
70
70
 
71
+ - Default to no comments. Add one only when the WHY is non-obvious — a
72
+ hidden constraint, a workaround for a specific bug, an invariant the
73
+ code alone can't convey. Never comment WHAT the code does; a
74
+ well-named symbol already says that.
71
75
  - Never self-certify a task as done or run `git commit` for its changes —
72
76
  see Workflow step 4.
73
77
  - Never write outside the packet's ALLOWED SCOPE. Scope is what stops
@@ -263,8 +263,10 @@ accounts get added where there were none).
263
263
  own archival planning output (`.hedgehog/BMAD/` or `.hedgehog/chain/`,
264
264
  first run only), the authored core's `.hedgehog/core.yaml` and
265
265
  `.hedgehog/core-design.md` if step 4 ran, and root `CLAUDE.md`'s filled
266
- placeholders (first run only). This is planning intake's own unit of
267
- work, landed before `bootstrap` touches anything.
266
+ placeholders (first run only). Write these with the `no-history-in-output`
267
+ skill: current state only, no narration of the intake conversation.
268
+ This is planning intake's own unit of work, landed before `bootstrap`
269
+ touches anything.
268
270
  9. **First run only — hand off to the `bootstrap` agent** once the
269
271
  commit lands. It scaffolds the chosen core's workspace (and, for
270
272
  full-stack-app, whichever add-ons are on) before any build step
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: no-history-in-output
3
+ description: Apply whenever writing or updating a project-facing document — root CLAUDE.md, `.hedgehog/core-design.md`, specs, READMEs, or similar. Ensures the file reads as a clean, current, as-is snapshot rather than a log of edits or decisions. Applies on first generation and on every later revision, including "update," "rewrite," or "clean up" requests.
4
+ ---
5
+
6
+ # No History in Output
7
+
8
+ Project-facing documents describe the current state of the thing, not the
9
+ process that produced it.
10
+
11
+ ## Rules
12
+
13
+ - Never include labels like "(unchanged)", "(updated)", "(new)",
14
+ "(revised)", or similar edit-tracking annotations in headings or body
15
+ text.
16
+ - Never narrate the discussion that led to a decision (e.g. "we
17
+ considered X but decided against it," "originally this was Y, now it's
18
+ Z," "per your feedback...").
19
+ - Never include changelogs, version history, "what changed" sections, or
20
+ meta-commentary about the conversation, unless the user explicitly
21
+ asks for a changelog as a deliverable in its own right.
22
+ - Write every section as a plain statement of fact about the current
23
+ design/plan/content, as if writing it fresh with full knowledge of the
24
+ final decision — not as a diff against a prior version.
25
+ - If a section documents a resolved tradeoff or an intentional design
26
+ choice, state the choice and its rationale plainly (e.g. "X is an
27
+ accepted tradeoff because..."), without referencing that alternatives
28
+ were debated or rejected earlier in conversation.
29
+ - An "update this doc" request re-derives a clean as-is document — it
30
+ does not layer edit notes onto the previous version.
31
+
32
+ ## When NOT to apply
33
+
34
+ - The user explicitly asks for a changelog, revision history, or "show
35
+ me what changed."
36
+ - The document's entire purpose is to record a process over time — a
37
+ friction log entry (`hedgehog friction add`), a commit message, or
38
+ meeting notes. Those describe what happened and when on purpose.
@@ -728,7 +728,6 @@
728
728
  const [selectedId, setSelectedId] = useState(null);
729
729
  const [chainRootId, setChainRootId] = useState(null);
730
730
  const [collapsedModules, setCollapsedModules] = useState(() => new Set());
731
- const [autoCollapseApplied, setAutoCollapseApplied] = useState(false);
732
731
  // Tracks the last-rendered graph's JSON so a poll that returns
733
732
  // identical data (the common case — nothing changed since last
734
733
  // tick) skips relayout entirely instead of jittering node positions
@@ -765,10 +764,12 @@
765
764
  const isLargeGraph = !!raw && raw.nodes.length > LARGE_GRAPH_THRESHOLD;
766
765
 
767
766
  // The modules worth a first look — anything with a blocked task
768
- // (needs a fix) or a ready task (actionable). Drives both which lanes
769
- // start collapsed and, on first paint, which lanes fitView zooms to
770
- // (see attentionModulesRef below) a large graph should open on what
771
- // needs attention, not zoomed out far enough to fit every idle lane.
767
+ // (needs a fix) or a ready task (actionable). Falls back to the first
768
+ // module alphabetically when nothing qualifies (a fresh build, every
769
+ // task still `planned`), so there's always a target. Drives which lane
770
+ // the initial viewport jump (below) lands on for a large graph — it
771
+ // should open on what needs attention, not zoomed out far enough to
772
+ // fit every idle lane.
772
773
  const attentionModules = useMemo(() => {
773
774
  if (!raw) return new Set();
774
775
  const byModule = new Map();
@@ -780,22 +781,14 @@
780
781
  for (const [moduleName, statuses] of byModule) {
781
782
  if (statuses.some((s) => s === 'blocked' || s === 'ready')) next.add(moduleName);
782
783
  }
784
+ if (next.size === 0 && byModule.size > 0) {
785
+ next.add([...byModule.keys()].sort((a, b) => a.localeCompare(b))[0]);
786
+ }
783
787
  return next;
784
788
  }, [raw]);
785
789
 
786
- // Auto-collapse every lane except the ones in attentionModules, once,
787
- // the first time a graph crosses the large-graph threshold. A user's
788
- // own later clicks always win after that: this never re-collapses a
789
- // lane the user chose to open.
790
- useEffect(() => {
791
- if (!raw || !isLargeGraph || autoCollapseApplied) return;
792
- const next = new Set();
793
- for (const n of raw.nodes) {
794
- if (!attentionModules.has(n.module)) next.add(n.module);
795
- }
796
- setCollapsedModules(next);
797
- setAutoCollapseApplied(true);
798
- }, [raw, isLargeGraph, autoCollapseApplied, attentionModules]);
790
+ // Lanes always start open — collapsing is a choice the user makes by
791
+ // clicking a lane header (toggleLane), never applied on their behalf.
799
792
 
800
793
  const edgesBySource = useMemo(() => {
801
794
  const map = new Map();
@@ -897,19 +890,15 @@
897
890
  // corner of the first lane worth attention (or the first lane at all,
898
891
  // if none need attention) at a fixed, legible zoom — rather than
899
892
  // fitting/centering on a bounding box, which (see above) can leave
900
- // the viewport scrolled past the top of the content it fit to. Gated
901
- // on autoCollapseApplied (not just isLargeGraph) so this runs once
902
- // `lanes` reflects the post-collapse layout, not the pre-collapse one
903
- // — setting the viewport before collapse applies would target
904
- // coordinates that shift out from under it a moment later.
893
+ // the viewport scrolled past the top of the content it fit to.
905
894
  useEffect(() => {
906
- if (!isLargeGraph || !autoCollapseApplied || initialViewSetRef.current) return;
895
+ if (!isLargeGraph || initialViewSetRef.current) return;
907
896
  const instance = flowInstanceRef.current;
908
897
  if (!instance || lanes.length === 0) return;
909
898
  const targetLane = lanes.find((l) => attentionModules.has(l.module)) ?? lanes[0];
910
899
  instance.setViewport({ x: -targetLane.x + 40, y: -targetLane.y + 40, zoom: 1 });
911
900
  initialViewSetRef.current = true;
912
- }, [isLargeGraph, autoCollapseApplied, lanes, attentionModules]);
901
+ }, [isLargeGraph, lanes, attentionModules]);
913
902
 
914
903
  if (!raw) {
915
904
  return h('div', { id: 'loading' }, 'Loading build graph…');