@skyf0xx/hedgehog 3.0.8 → 3.0.10

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
@@ -101,7 +101,7 @@ scoped file access and a verification command per layer.
101
101
 
102
102
  ## Install
103
103
 
104
- From an empty project folder, run:
104
+ From an empty project folder, ask Claude or your Agent to run:
105
105
 
106
106
  ``` bash
107
107
  # Full-stack app
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyf0xx/hedgehog",
3
- "version": "3.0.8",
3
+ "version": "3.0.10",
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,6 +14,7 @@
14
14
  "hedgehog": "bin/cli.mjs"
15
15
  },
16
16
  "scripts": {
17
+ "check": "node scripts/check.mjs",
17
18
  "release": "npm version patch -m \"chore: bump version to %s\""
18
19
  },
19
20
  "files": [
@@ -15,6 +15,7 @@
15
15
 
16
16
  import { createServer } from 'node:http';
17
17
  import { readFile, writeFile, rm } from 'node:fs/promises';
18
+ import { dirname, join } from 'node:path';
18
19
  import { DatabaseSync } from 'node:sqlite';
19
20
  import { buildGraph } from './graph.mjs';
20
21
 
@@ -31,6 +32,15 @@ if (!dbPath || !templatePath || !pidfilePath) {
31
32
 
32
33
  const template = await readFile(templatePath, 'utf8');
33
34
 
35
+ // graph.html loads React/ReactFlow/dagre from here rather than a CDN, so
36
+ // `hedgehog graph` works with no internet connection — vendored files
37
+ // live alongside the template at src/templates/vendor/.
38
+ const VENDOR_DIR = join(dirname(templatePath), 'vendor');
39
+ const VENDOR_CONTENT_TYPES = {
40
+ '.js': 'application/javascript; charset=utf-8',
41
+ '.css': 'text/css; charset=utf-8',
42
+ };
43
+
34
44
  function loadGraphJson() {
35
45
  // Opened and closed per request rather than held open: DatabaseSync is
36
46
  // synchronous and local, so the cost is negligible, and it avoids ever
@@ -44,7 +54,33 @@ function loadGraphJson() {
44
54
  }
45
55
  }
46
56
 
47
- const server = createServer((req, res) => {
57
+ const server = createServer(async (req, res) => {
58
+ if (req.url.startsWith('/vendor/')) {
59
+ // Reject traversal/query strings before touching the filesystem —
60
+ // the vendor set is fixed and flat, so anything else is not a file
61
+ // this route is meant to serve.
62
+ const name = req.url.slice('/vendor/'.length);
63
+ const dotIndex = name.lastIndexOf('.');
64
+ const ext = dotIndex === -1 ? '' : name.slice(dotIndex);
65
+ if (!name || name.includes('/') || name.includes('..') || !(ext in VENDOR_CONTENT_TYPES)) {
66
+ res.writeHead(404);
67
+ res.end('Not found');
68
+ return;
69
+ }
70
+ try {
71
+ const body = await readFile(join(VENDOR_DIR, name));
72
+ res.writeHead(200, {
73
+ 'content-type': VENDOR_CONTENT_TYPES[ext],
74
+ 'cache-control': 'no-store',
75
+ });
76
+ res.end(body);
77
+ } catch {
78
+ res.writeHead(404);
79
+ res.end('Not found');
80
+ }
81
+ return;
82
+ }
83
+
48
84
  if (req.url === '/graph.json') {
49
85
  let body;
50
86
  try {