@holmes-lab/holmes-kit 0.1.10 → 0.1.12

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/CHANGELOG.md CHANGED
@@ -4,6 +4,41 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+ <!-- @implements A-SPEC-209 -->
8
+ ## [0.1.12] - 2026-08-23
9
+
10
+ ### Added
11
+ - **Install Guide (`docs/install-guide.md`, shipped in the package)**: install paths organised by
12
+ account type and privileges — a decision table first, commands second — because the same wrong
13
+ command (`npm install -g` against a protected prefix) was run three times by a real adopter
14
+ before the right one. Troubleshooting is keyed to the exact error text npm prints (`EPERM mkdir`,
15
+ `notarget`, `better-sqlite3` build failures, `spawn sh ENOENT`), and each entry says why
16
+ elevation is the wrong fix.
17
+
18
+ ### Changed
19
+ - **README Quickstart leads with the situation table**, and steps 2–3 use `npx holmes-kit …` —
20
+ the bare `holmes-kit` they previously showed only exists on PATH after a global install, which
21
+ is not the primary path.
22
+
23
+ <!-- @implements A-SPEC-209 -->
24
+ ## [0.1.11] - 2026-08-23
25
+
26
+ ### Added
27
+ - **`doctor` diagnoses the global npm prefix (REQ-243)**: after a Windows adopter hit `EPERM` on
28
+ `npm install -g` twice — once elevated — `doctor` now reads the npm global prefix, computes where
29
+ a global install would land (win32: `<prefix>\\node_modules`; POSIX: `<prefix>/lib/node_modules`),
30
+ and checks writability on the deepest existing ancestor, which is what decides the real mkdir's
31
+ fate. An unwritable prefix is a **WARN** naming the exact directory the install would die in, with
32
+ a user-space prefix prescription — never elevation, which npm's own error text suggests and which
33
+ would run better-sqlite3's install script with system privileges. An unreadable probe is a WARN
34
+ carrying the reason, never a silent pass. The check cannot FAIL: a red doctor would tell a user
35
+ with a healthy local install that the install is broken.
36
+
37
+ ### Unchanged
38
+ - The `-g` EPERM itself is not fixable by any package: it occurs while npm creates the scope
39
+ directory, before a single package file is transferred. The supported regular-account path remains
40
+ the local install (`npm install --save-dev`), which touches no protected directory.
41
+
7
42
  <!-- @implements A-SPEC-209 -->
8
43
  ## [0.1.10] - 2026-08-23
9
44
 
package/README.md CHANGED
@@ -51,58 +51,35 @@ Holmes-Kit prioritizes **CLI-based AI Coding Agents** where OS-level process hoo
51
51
 
52
52
  ## ⚡ Quickstart (3-Minute Setup)
53
53
 
54
- ### 1. Install
55
-
56
- Install it into the project you want to govern. This is the path verified end to end the
57
- wiring `init` writes uses absolute paths, so a local install works from any directory:
58
-
59
- ```bash
60
- npm install --save-dev @holmes-lab/holmes-kit
61
- ```
62
-
63
- <details>
64
- <summary>Prefer one install for many projects? (<code>npm install -g</code>)</summary>
65
-
66
- A global install works too, but **do not elevate to get it**. Where npm puts global packages is
67
- your npm configuration, not ours and on Windows it often points inside the Node.js installation
68
- directory:
69
-
70
- ```text
71
- npm error code EPERM
72
- npm error path C:\Program Files\nodejs\node_modules\@holmes-lab
73
- ```
74
-
75
- Running the install as Administrator would "fix" that by executing this package's native
76
- dependency install scripts with system privileges. Point npm at a user-writable prefix instead:
77
-
78
- ```powershell
79
- npm config set prefix "$env:APPDATA\npm" # Windows
80
- # then ensure %APPDATA%\npm is on PATH
81
- ```
82
- ```bash
83
- npm config set prefix "$HOME/.npm-global" # macOS / Linux
84
- export PATH="$HOME/.npm-global/bin:$PATH"
85
- ```
86
-
87
- A Node version manager (`nvm`, `fnm`, `volta`) does the same thing by keeping the whole
88
- toolchain under your home directory.
89
- </details>
90
-
91
- **Prerequisites** — Node.js `>= 20.0.0`, plus a native binary for two kinds of dependency:
92
-
93
- | Dependency | How the binary arrives |
94
- |---|---|
95
- | 8 tree-sitter grammars | Prebuilt binaries ship inside the packages (`darwin-arm64`, `darwin-x64`, `linux-x64`, `win32-x64`) — nothing is compiled |
96
- | `better-sqlite3` | Downloaded at install time by `prebuild-install`, **falling back to compiling** if no prebuild matches your platform and Node ABI |
97
-
98
- Only the second can require a toolchain. If it has to compile, you need a C++ build environment:
99
- Xcode Command Line Tools on macOS, Visual Studio Build Tools on Windows, or
100
- `apk add --no-cache python3 make g++` on Alpine.
54
+ ### 1. Install — find your row first
55
+
56
+ The same wrong command was run three times by a real adopter before the right one; a table beats
57
+ prose read top-to-bottom.
58
+
59
+ | Which situation are you in? | Privileges | Command |
60
+ |---|---|---|
61
+ | **Using it in one project** (most people) | none | `npm install --save-dev @holmes-lab/holmes-kit` |
62
+ | Company-managed PC / restricted account | none | same — no system directory is touched |
63
+ | CI / container | none | same, plus `--prefer-online` right after a release |
64
+ | CLI across many projects (`-g`) | depends | run `npm config get prefix` first — see below |
65
+
66
+ **Before `npm install -g`**: if `npm config get prefix` names a protected directory
67
+ (`C:\Program Files\nodejs`, `/usr/local`), `-g` dies with `EPERM` **before any package file
68
+ arrives** — no package version can fix that, and elevation is the wrong fix (it runs native
69
+ install scripts with system privileges, and it did not even work in the reported case). Move the
70
+ prefix to user space instead — one-time setup, exact commands in the
71
+ **[Install Guide](docs/install-guide.md)**, along with troubleshooting keyed to the exact error
72
+ text (`EPERM mkdir`, `notarget`, `better-sqlite3` build failures).
73
+
74
+ **Prerequisites** — Node.js `>= 20.0.0`. The 8 tree-sitter grammars ship prebuilt binaries for
75
+ macOS/Linux/Windows and compile nothing; `better-sqlite3` downloads a prebuild at install time,
76
+ falling back to compiling only that fallback needs a C++ toolchain (VS Build Tools / Xcode CLT /
77
+ `apk add python3 make g++`).
101
78
 
102
79
  ### 2. Initialize in Your Project
103
80
  ```bash
104
81
  cd /path/to/your/project
105
- holmes-kit init
82
+ npx holmes-kit init # drop the npx prefix if you installed with -g
106
83
  ```
107
84
  *An interactive prompt will ask which AI agent harnesses to wire into your project:*
108
85
  ```text
@@ -114,7 +91,7 @@ holmes-kit init
114
91
 
115
92
  ### 3. Verify Health
116
93
  ```bash
117
- holmes-kit doctor
94
+ npx holmes-kit doctor
118
95
  ```
119
96
  *If everything is green, your project is governed and ready for AI pair-programming!*
120
97
 
package/dist/.build-id CHANGED
@@ -1 +1 @@
1
- a400784-mt568ove
1
+ b820c0b-mt57zbc7
@@ -28,6 +28,45 @@ export interface DoctorOptions {
28
28
  * real key-related failure.
29
29
  */
30
30
  export declare const STRIPPED_FOR_PROBE: readonly ["HOLMES_APPROVAL", "HOLMES_SPECS"];
31
+ /**
32
+ * Where a global install actually lands for a given prefix.
33
+ *
34
+ * @implements A-SPEC-243
35
+ * The shapes differ, not just the separators: win32 puts global modules directly under the prefix,
36
+ * POSIX puts them under `lib/`. The win32 result is the exact directory the reported EPERM died in
37
+ * — npm mkdir-ing `@holmes-lab` inside `C:\Program Files\nodejs\node_modules`, before a single
38
+ * package file was transferred. That is why no package content can fix that failure, and why the
39
+ * only lever left is naming the condition here.
40
+ */
41
+ export declare function globalInstallDir(prefix: string, platform: string): string;
42
+ /** What the global-prefix probe saw. `unreadable` carries WHY npm could not be consulted. */
43
+ export interface PrefixProbe {
44
+ prefix?: unknown;
45
+ dir?: string;
46
+ writable?: boolean;
47
+ platform?: string;
48
+ unreadable?: string;
49
+ }
50
+ /**
51
+ * Judge the global prefix. Pure — the probing lives with the caller.
52
+ *
53
+ * @implements A-SPEC-243
54
+ * Never FAIL: the `-g` path is optional, and a red doctor tells a user with a healthy local
55
+ * install that the install is broken — a false signal that comes back as distrust of the check.
56
+ *
57
+ * Never elevation: npm's own EPERM text ends with "try running the command again as
58
+ * root/Administrator", and pointing the opposite way is this check's reason to exist — elevation
59
+ * runs better-sqlite3's install script (`prebuild-install || node-gyp rebuild`) with system
60
+ * privileges. The prescription is a user-space prefix, per platform.
61
+ *
62
+ * An unreadable probe is a WARN carrying the reason, not a silent pass: a silent pass reads as
63
+ * "checked, no problem", which translates what was not seen into a verdict.
64
+ */
65
+ export declare function prefixVerdict(input: PrefixProbe): {
66
+ level: Level;
67
+ detail: string;
68
+ fix?: string;
69
+ };
31
70
  /** The parent environment minus the variables that legitimately change a gate decision. Pure. */
32
71
  export declare function probeEnv(parent: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
33
72
  /** Seam for the gate probe's child process. Default is a real `spawnSync`; tests inject a fake. */
@@ -34,6 +34,8 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.STRIPPED_FOR_PROBE = void 0;
37
+ exports.globalInstallDir = globalInstallDir;
38
+ exports.prefixVerdict = prefixVerdict;
37
39
  exports.probeEnv = probeEnv;
38
40
  exports.runDoctor = runDoctor;
39
41
  exports.formatChecks = formatChecks;
@@ -63,6 +65,56 @@ const GRAMMARS = [
63
65
  * real key-related failure.
64
66
  */
65
67
  exports.STRIPPED_FOR_PROBE = ['HOLMES_APPROVAL', 'HOLMES_SPECS'];
68
+ /**
69
+ * Where a global install actually lands for a given prefix.
70
+ *
71
+ * @implements A-SPEC-243
72
+ * The shapes differ, not just the separators: win32 puts global modules directly under the prefix,
73
+ * POSIX puts them under `lib/`. The win32 result is the exact directory the reported EPERM died in
74
+ * — npm mkdir-ing `@holmes-lab` inside `C:\Program Files\nodejs\node_modules`, before a single
75
+ * package file was transferred. That is why no package content can fix that failure, and why the
76
+ * only lever left is naming the condition here.
77
+ */
78
+ function globalInstallDir(prefix, platform) {
79
+ return platform === 'win32'
80
+ ? `${prefix.replace(/[\\/]+$/, '')}\\node_modules`
81
+ : `${prefix.replace(/\/+$/, '')}/lib/node_modules`;
82
+ }
83
+ /**
84
+ * Judge the global prefix. Pure — the probing lives with the caller.
85
+ *
86
+ * @implements A-SPEC-243
87
+ * Never FAIL: the `-g` path is optional, and a red doctor tells a user with a healthy local
88
+ * install that the install is broken — a false signal that comes back as distrust of the check.
89
+ *
90
+ * Never elevation: npm's own EPERM text ends with "try running the command again as
91
+ * root/Administrator", and pointing the opposite way is this check's reason to exist — elevation
92
+ * runs better-sqlite3's install script (`prebuild-install || node-gyp rebuild`) with system
93
+ * privileges. The prescription is a user-space prefix, per platform.
94
+ *
95
+ * An unreadable probe is a WARN carrying the reason, not a silent pass: a silent pass reads as
96
+ * "checked, no problem", which translates what was not seen into a verdict.
97
+ */
98
+ function prefixVerdict(input) {
99
+ const reason = input.unreadable ?? (typeof input.prefix !== 'string' || input.prefix === ''
100
+ ? `npm 이 돌려준 prefix 를 해석할 수 없습니다 (${String(input.prefix)})`
101
+ : undefined);
102
+ if (reason !== undefined) {
103
+ return { level: 'WARN', detail: `전역 prefix 를 판독하지 못했습니다 — ${reason}. 로컬 설치(--save-dev)는 이 판정과 무관하게 동작합니다.` };
104
+ }
105
+ const prefix = input.prefix;
106
+ if (input.writable) {
107
+ return { level: 'PASS', detail: `전역 prefix(${prefix})가 현재 계정으로 쓰기 가능합니다 — npm install -g 가 권한 문제 없이 동작합니다.` };
108
+ }
109
+ const fix = input.platform === 'win32'
110
+ ? 'npm config set prefix "%APPDATA%\\npm" 실행 후 %APPDATA%\\npm 을 PATH 에 추가하고 터미널을 다시 여세요. 로컬 설치(npm install --save-dev)는 이 설정 없이도 동작합니다.'
111
+ : 'npm config set prefix "$HOME/.npm-global" 실행 후 $HOME/.npm-global/bin 을 PATH 에 추가하세요. 로컬 설치(npm install --save-dev)는 이 설정 없이도 동작합니다.';
112
+ return {
113
+ level: 'WARN',
114
+ detail: `전역 prefix(${prefix})가 현재 계정으로 쓰기 불가입니다 — npm install -g 는 ${input.dir ?? ''} 에 디렉터리를 만들다 EPERM 으로 죽습니다. 패키지가 도착하기 전 단계라 어떤 패키지도 이를 고칠 수 없습니다.`,
115
+ fix,
116
+ };
117
+ }
66
118
  /** The parent environment minus the variables that legitimately change a gate decision. Pure. */
67
119
  function probeEnv(parent) {
68
120
  const out = { ...parent };
@@ -451,6 +503,33 @@ async function runDoctor(packageRoot, target, opts, extraChecks) {
451
503
  catch { /* an unreadable settings file is `target wiring`'s business, not this check's */ }
452
504
  }
453
505
  add('gate blind spots', 'PASS', (0, blind_spots_1.blindSpotSummary)());
506
+ // @implements A-SPEC-243 — the writability of the deepest EXISTING ancestor is what decides the
507
+ // real mkdir's fate, since the scope directory being created does not exist yet.
508
+ {
509
+ let verdict;
510
+ try {
511
+ const prefix = (0, node_child_process_1.execFileSync)('npm', ['config', 'get', 'prefix'], { encoding: 'utf8' }).trim();
512
+ const dir = globalInstallDir(prefix, process.platform);
513
+ let probe = dir;
514
+ while (!fs.existsSync(probe)) {
515
+ const parent = path.dirname(probe);
516
+ if (parent === probe)
517
+ break;
518
+ probe = parent;
519
+ }
520
+ let writable = false;
521
+ try {
522
+ fs.accessSync(probe, fs.constants.W_OK);
523
+ writable = true;
524
+ }
525
+ catch { /* not writable */ }
526
+ verdict = prefixVerdict({ prefix, dir, writable, platform: process.platform });
527
+ }
528
+ catch (e) {
529
+ verdict = prefixVerdict({ unreadable: e instanceof Error ? e.message.split('\n')[0] : String(e) });
530
+ }
531
+ add('global prefix', verdict.level, verdict.detail, verdict.fix);
532
+ }
454
533
  add('environment', 'PASS', present);
455
534
  if (extraChecks) {
456
535
  checks.push(...extraChecks);
@@ -0,0 +1,131 @@
1
+ # Install Guide — by account type and permissions
2
+
3
+ This guide exists because of a measured failure, not a hypothetical one: an adopter on Windows ran
4
+ `npm install -g` three times — once from an elevated PowerShell — and hit the same `EPERM` every
5
+ time, because their npm global prefix pointed inside `C:\Program Files\nodejs`. The local install
6
+ worked on the first try. Find your row, run its one command.
7
+
8
+ ## Which situation are you in?
9
+
10
+ | Situation | Privileges needed | Command |
11
+ |---|---|---|
12
+ | **Using it in one project** (most people) | none | `npm install --save-dev @holmes-lab/holmes-kit` then `npx holmes-kit init` |
13
+ | Company-managed PC / restricted account | none | same as above — no system directory is touched |
14
+ | CI / container | none | same as above, plus `--prefer-online` right after a release |
15
+ | Want the CLI across many projects (`-g`) | depends on your prefix | **check first**: `npm config get prefix` ↓ |
16
+
17
+ The local install is the path verified end to end, on macOS and Windows, against the public
18
+ registry. The wiring `init` writes uses absolute paths, so nothing needs to be on `PATH`.
19
+
20
+ ## Before `npm install -g`: check your prefix
21
+
22
+ ```
23
+ npm config get prefix
24
+ ```
25
+
26
+ | Result looks like | Verdict |
27
+ |---|---|
28
+ | `%APPDATA%\npm`, `~/.npm-global`, `/opt/homebrew`, an nvm/fnm/volta directory | user-writable — `npm install -g @holmes-lab/holmes-kit` works as-is |
29
+ | `C:\Program Files\nodejs`, `/usr/local` | protected — `-g` dies with `EPERM` **before any package file arrives**. Move the prefix (below). **Do not elevate.** |
30
+
31
+ `npx holmes-kit doctor` (after a local install) runs this exact check for you — the
32
+ `global prefix` line names the directory and the remedy.
33
+
34
+ ### Moving the prefix to user space — one-time setup
35
+
36
+ Windows (PowerShell):
37
+
38
+ ```powershell
39
+ npm config set prefix "$env:APPDATA\npm"
40
+ [Environment]::SetEnvironmentVariable('Path', "$([Environment]::GetEnvironmentVariable('Path','User'));$env:APPDATA\npm", 'User')
41
+ # open a NEW terminal, then:
42
+ npm install -g @holmes-lab/holmes-kit
43
+ ```
44
+
45
+ macOS / Linux:
46
+
47
+ ```bash
48
+ npm config set prefix "$HOME/.npm-global"
49
+ export PATH="$HOME/.npm-global/bin:$PATH" # add to your shell profile too
50
+ npm install -g @holmes-lab/holmes-kit
51
+ ```
52
+
53
+ A Node version manager (`nvm`, `fnm`, `volta`) achieves the same by keeping the whole toolchain
54
+ under your home directory.
55
+
56
+ ### Why elevation is the wrong fix
57
+
58
+ npm's own error text ends with *"try running the command again as root/Administrator."* Do not
59
+ follow it here, for two reasons:
60
+
61
+ 1. **It may not even work.** The reported failure recurred from an elevated PowerShell — antivirus
62
+ and Windows Controlled Folder Access block protected-folder writes regardless of elevation.
63
+ 2. **When it works, it is worse.** `better-sqlite3` declares
64
+ `install: prebuild-install || node-gyp rebuild` — under an elevated `-g`, that downloads and
65
+ executes, or invokes a compiler, **with system privileges**. Keeping installs in user space is
66
+ what contains a compromised dependency.
67
+
68
+ ## Troubleshooting, by the error you actually see
69
+
70
+ ### `EPERM … mkdir C:\Program Files\nodejs\node_modules\@holmes-lab`
71
+
72
+ ```
73
+ npm error code EPERM
74
+ npm error syscall mkdir
75
+ npm error path C:\Program Files\nodejs\node_modules\@holmes-lab
76
+ ```
77
+
78
+ Your global prefix is a protected directory. The failure happens while npm creates the scope
79
+ folder — **before a single package file is transferred** — so no package version can fix it, and
80
+ neither can this one. Either drop `-g` (the local install needs none of this) or move the prefix
81
+ (one-time setup above).
82
+
83
+ ### `notarget No matching version found`
84
+
85
+ ```
86
+ npm error code ETARGET
87
+ npm error notarget No matching version found for @holmes-lab/holmes-kit@<version>
88
+ ```
89
+
90
+ Your npm metadata cache predates the release — measured minutes after publishing 0.1.11, the
91
+ registry already listed the version while a default-cache install still refused it. Add
92
+ `--prefer-online`, or retry in a few minutes.
93
+
94
+ ### `better-sqlite3` fails to build
95
+
96
+ The one dependency that may need a toolchain. The 8 tree-sitter grammars ship prebuilt binaries
97
+ (`darwin-arm64`, `darwin-x64`, `linux-x64`, `win32-x64`) and compile nothing; `better-sqlite3`
98
+ downloads a prebuild at install time and **falls back to compiling** when none matches your
99
+ platform and Node ABI. If it compiles, you need:
100
+
101
+ | Platform | Toolchain |
102
+ |---|---|
103
+ | Windows | Visual Studio Build Tools (C++ workload) |
104
+ | macOS | Xcode Command Line Tools (`xcode-select --install`) |
105
+ | Alpine | `apk add --no-cache python3 make g++` |
106
+
107
+ ### `spawn sh ENOENT` during a git-URL install
108
+
109
+ `npm i -g git+ssh://…` is not a supported path: npm 11 clones the repository into its cache and
110
+ runs `prepare` there without installing dependencies, so the build tooling is missing. Install
111
+ from the registry or from a packed tarball.
112
+
113
+ ## Verify — the last step of every path
114
+
115
+ ```
116
+ npx holmes-kit doctor # local install
117
+ holmes-kit doctor # global install
118
+ ```
119
+
120
+ Expect `10 pass, 1 warn, 0 fail` on a healthy install. The lines that matter most:
121
+
122
+ - `global prefix` — whether `-g` would work on this machine, and the remedy if not
123
+ - `tree-sitter grammars` / `better-sqlite3` — whether the native modules actually load
124
+
125
+ ## What we deliberately do NOT do
126
+
127
+ | Idea | Why not |
128
+ |---|---|
129
+ | A `postinstall` script that prints guidance | Triggers npm 11's `allow-scripts` warning and forfeits this package's current property of running no install scripts at all |
130
+ | Recommending `npx @holmes-lab/holmes-kit init` with no install | `init` writes wiring with absolute paths; under bare `npx` those point into the npx cache and break when it is pruned |
131
+ | Fixing your npm prefix from inside the package | A package rewriting your npm configuration is exactly the supply-chain behaviour this guide warns about |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "//": "@implements A-SPEC-209",
3
3
  "name": "@holmes-lab/holmes-kit",
4
- "version": "0.1.10",
4
+ "version": "0.1.12",
5
5
  "description": "Holmes-Kit — deterministic Agentic Software Engineering (ASE) harness with causal traceability (spec chain + D-CPG + RTM + phase guardrail)",
6
6
  "main": "dist/holmes/mcp/server.js",
7
7
  "types": "dist/holmes/mcp/server.d.ts",
@@ -13,6 +13,7 @@
13
13
  "bin/",
14
14
  "dist/",
15
15
  "playbooks/",
16
+ "docs/install-guide.md",
16
17
  "CHANGELOG.md"
17
18
  ],
18
19
  "engines": {