@holmes-lab/holmes-kit 0.3.3 → 0.3.4

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,26 @@ 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.3.4] - 2026-09-01
9
+
10
+ Docs: a friendly, accurate setup guide for the opt-in semantic tiers.
11
+
12
+ ### Added
13
+
14
+ - README Quickstart step 4: enabling the semantic layer — tier `local` (install
15
+ `@xenova/transformers`, no egress) and tier `cloud` (`gemini-embedding-001`): get a key from
16
+ Google AI Studio, store it out-of-tree via `holmes-kit semantic-key set` (stdin-only, 0600 /
17
+ macOS keychain, value never printed), env-var alternatives for CI
18
+ (`HOLMES_SEMANTIC_API_KEY` / `GEMINI_API_KEY` / `GOOGLE_API_KEY`), revocation via
19
+ `semantic-key unset`, and the security stance (no project-tree key source; agent
20
+ self-consent is gated).
21
+
22
+ ### Internal (measurement plumbing, no default-path change)
23
+
24
+ - `alignExcerpt` request-aligned excerptor + `dumpBodies.mode` (A-SPEC-500.1) — feeds the
25
+ judgment-precision experiment REQ-500; default excerpts stay byte-identical.
26
+
7
27
  <!-- @implements A-SPEC-209 -->
8
28
  ## [0.3.3] - 2026-08-31
9
29
 
package/README.md CHANGED
@@ -117,6 +117,46 @@ npx holmes-kit doctor
117
117
  ```
118
118
  *If everything is green, your project is governed and ready for AI pair-programming!*
119
119
 
120
+ ### 4. Optional: Enable the Semantic Layer (Tier `local` / Tier `cloud`)
121
+
122
+ Out of the box Holmes-Kit runs tier **`none`** — lexical + citation + graph search, **zero
123
+ egress**. Two opt-in tiers raise recall on requests your vocabulary can't reach (measured on 305
124
+ traceability cases — see the feature list above):
125
+
126
+ **Tier `local` — no egress, no account.** Install the optional embedding runtime next to
127
+ holmes-kit and the local model (`bge-m3`) is picked up automatically:
128
+ ```bash
129
+ npm install --save-dev @xenova/transformers
130
+ npx holmes-kit doctor # → semantic tier: local (no egress)
131
+ ```
132
+
133
+ **Tier `cloud` — highest recall, explicit consent (`gemini-embedding-001`).** Setting a key IS
134
+ the consent act: with a key present, repository-derived text is sent to Google's embedding API.
135
+
136
+ 1. Get a Gemini API key (Google AI Studio → <https://aistudio.google.com/apikey>; the free tier
137
+ is enough to try it).
138
+ 2. Store it **outside your project tree** with the built-in command — the key rides **stdin,
139
+ never argv**, lands in `~/.holmes/credentials.json` with `0600` permissions, and no output
140
+ ever contains the value:
141
+ ```bash
142
+ npx holmes-kit semantic-key set # hidden prompt on a TTY; or: echo "$KEY" | npx holmes-kit semantic-key set
143
+ npx holmes-kit semantic-key status # shows the key's SOURCE only, never the value
144
+ npx holmes-kit doctor # → semantic tier: cloud (egress: on)
145
+ ```
146
+ On macOS the key prefers the system keychain; elsewhere the `0600` file is the store.
147
+ Environment variables also work and take precedence (`HOLMES_SEMANTIC_API_KEY` dedicated, or
148
+ the ecosystem-compatible `GEMINI_API_KEY` / `GOOGLE_API_KEY`) — useful for CI. Prefer
149
+ `semantic-key set` on workstations: it keeps the key out of shell history, `.env` files, and
150
+ the repository.
151
+ 3. To revoke consent at any time:
152
+ ```bash
153
+ npx holmes-kit semantic-key unset # clears the stored key; tier falls back to local/none
154
+ ```
155
+
156
+ > 🔒 **Never** commit a key, pass it as a CLI argument, or put it in a file inside the project
157
+ > tree. Holmes-Kit's credential chain has **no project-tree source by design**, and agents are
158
+ > gated from setting keys on their own — consent stays a human act.
159
+
120
160
  ---
121
161
 
122
162
  ## 🔄 Daily Workflow (How It Works)
@@ -193,11 +233,11 @@ Holmes-Kit embeds native AST & Code Property Graph (D-CPG) analyzers to track ca
193
233
  | **macOS** | Apple Silicon (arm64) / Intel (x64) | ✅ Tier 1 | macOS 12+ (Full hook enforcement) |
194
234
  | **Linux** | x86_64 / arm64 | ✅ Tier 1 | Ubuntu, Debian, Fedora, Arch, RHEL |
195
235
  | **Windows (WSL2)** | x86_64 | ✅ Tier 1 | WSL2 Ubuntu/Debian recommended |
196
- | **Windows Native** | x86_64 | ✅ Tier 1 | Windows 10/11 (Node.js 20+; prebuilt natives, no build tools needed in the common case). **Field-validated 2026-08-31** on a real user machine: registry install, natives (better-sqlite3 + 7 tree-sitter grammars), both OS gates, MCP handshake (30 tools), interactive init TUI, out-of-band approval channel (doctor 25 PASS; the 4 false FAILs it also showed were doctor's own win32 spawn bug, fixed in 0.3.2). See [ADR-015](.ax/decisions/ADR-015_platform-tier-is-decided-by-executed-verification.md) for tier criteria and residual risks (NTFS 8.3 names, reserved device names, 260-char paths; no Windows CI yet) |
236
+ | **Windows Native** | x86_64 | ✅ Tier 1 | Windows 10/11 (Node.js 20+; prebuilt natives, no build tools needed in the common case). **Field-validated 2026-08-31** on a real user machine: registry install, natives (better-sqlite3 + 7 tree-sitter grammars), both OS gates, MCP handshake (30 tools), interactive init TUI, out-of-band approval channel (doctor 25 PASS; the 4 false FAILs it also showed were doctor's own win32 spawn bug, fixed in 0.3.2). See ADR-015 (platform tier is decided by executed verification — internal decision record) for tier criteria and residual risks (NTFS 8.3 names, reserved device names, 260-char paths; no Windows CI yet) |
197
237
 
198
238
  > **Runtime Requirement**: Node.js `>= 20.0.0` (LTS recommended)
199
239
  >
200
- > **How a tier is decided**: by the verification that actually executes, not by declaration. A platform is Tier 1 only while its gate verdicts are exercised by the suite; if that stops being true it is demoted and the demotion is recorded. See [ADR-015](.ax/decisions/ADR-015_platform-tier-is-decided-by-executed-verification.md).
240
+ > **How a tier is decided**: by the verification that actually executes, not by declaration. A platform is Tier 1 only while its gate verdicts are exercised by the suite; if that stops being true it is demoted and the demotion is recorded. See ADR-015 (platform tier is decided by executed verification — internal decision record).
201
241
 
202
242
  ---
203
243
 
package/dist/.build-id CHANGED
@@ -1 +1 @@
1
- 8c7f0ed-mthde5yw
1
+ 68b5982-mthe8y11
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Request-aligned excerpting for the judgment-measurement dump. The excerpt ladder
3
+ * (S-505→S-516) showed excerpt information content DOMINATES the agent-judge's precision, and
4
+ * the shipped head-slice starves the judge exactly when the relevant region sits mid-file —
5
+ * imports and module prose fill the window and the judge degrades to filename guessing.
6
+ *
7
+ * The alignment is deliberately dumb and PURE: slide a chars-wide window at half-stride and keep
8
+ * the one containing the most DISTINCT request tokens (presence, not frequency — one dense
9
+ * mention must not outvote three different matched concepts). Ties keep the EARLIEST window and
10
+ * zero overlap falls back to the head slice, so where there is no signal the behavior is
11
+ * byte-identical to the status quo. Every return is a verbatim substring — the judge must only
12
+ * ever see real code.
13
+ */
14
+ export declare function alignExcerpt(text: string, request: string, chars: number): string;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ // @implements A-SPEC-500.1
3
+ /**
4
+ * Request-aligned excerpting for the judgment-measurement dump. The excerpt ladder
5
+ * (S-505→S-516) showed excerpt information content DOMINATES the agent-judge's precision, and
6
+ * the shipped head-slice starves the judge exactly when the relevant region sits mid-file —
7
+ * imports and module prose fill the window and the judge degrades to filename guessing.
8
+ *
9
+ * The alignment is deliberately dumb and PURE: slide a chars-wide window at half-stride and keep
10
+ * the one containing the most DISTINCT request tokens (presence, not frequency — one dense
11
+ * mention must not outvote three different matched concepts). Ties keep the EARLIEST window and
12
+ * zero overlap falls back to the head slice, so where there is no signal the behavior is
13
+ * byte-identical to the status quo. Every return is a verbatim substring — the judge must only
14
+ * ever see real code.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.alignExcerpt = alignExcerpt;
18
+ const tokensOf = (request) => {
19
+ const seen = new Set();
20
+ for (const m of request.toLowerCase().match(/[a-z0-9_$가-힣]+/g) ?? []) {
21
+ if (m.length >= 3)
22
+ seen.add(m);
23
+ }
24
+ return [...seen];
25
+ };
26
+ function alignExcerpt(text, request, chars) {
27
+ if (text.length <= chars)
28
+ return text;
29
+ const tokens = tokensOf(request);
30
+ const head = text.slice(0, chars);
31
+ if (tokens.length === 0)
32
+ return head;
33
+ const stride = Math.ceil(chars / 2);
34
+ let best = head;
35
+ let bestCount = 0;
36
+ for (let at = 0; at < text.length; at += stride) {
37
+ const window = text.slice(at, at + chars);
38
+ const lower = window.toLowerCase();
39
+ let count = 0;
40
+ for (const t of tokens)
41
+ if (lower.includes(t))
42
+ count++;
43
+ if (count > bestCount) {
44
+ bestCount = count;
45
+ best = window;
46
+ }
47
+ if (at + chars >= text.length)
48
+ break;
49
+ }
50
+ return bestCount === 0 ? head : best;
51
+ }
@@ -241,6 +241,8 @@ export declare function runReplay(corpus: ReplayCorpus, limit: number, opts?: {
241
241
  chars: number;
242
242
  /** @implements A-SPEC-490 — drop leading license blocks before excerpting; default false keeps S-507 bytes. */
243
243
  skipLicense?: boolean;
244
+ /** @implements A-SPEC-500.1 — 'aligned' excerpts the request-densest window instead of the head; default 'head' keeps prior bytes. */
245
+ mode?: 'head' | 'aligned';
244
246
  };
245
247
  }): Promise<ReplayResult>;
246
248
  /**
@@ -64,6 +64,7 @@ const localize_1 = require("../rtm/localize");
64
64
  // @implements A-SPEC-483 — content-level verification: the candidate's BODY, read from the
65
65
  // materialized parent tree.
66
66
  const content_verify_1 = require("./content-verify");
67
+ const excerpt_align_1 = require("./excerpt-align");
67
68
  // @implements A-SPEC-467 — the associative-recall arm: measured beside the product, never inside it.
68
69
  const assoc_arm_1 = require("../assoc/assoc-arm");
69
70
  // @implements A-SPEC-470 — the impact axis's Phase-B external yardstick, always scored.
@@ -265,7 +266,11 @@ async function runReplay(corpus, limit, opts = {}) {
265
266
  const raw = fs.readFileSync(path.join(dest, f), 'utf8');
266
267
  // @implements A-SPEC-490 — the informative excerpt: license blocks first, then cut.
267
268
  const text = opts.dumpBodies.skipLicense === true ? (0, content_verify_1.skipLicenseHeader)(raw) : raw;
268
- bodies[f] = text.slice(0, opts.dumpBodies.chars);
269
+ // @implements A-SPEC-500.1 — 'aligned' picks the request-densest window; the
270
+ // default stays the head slice, byte-identical to the pre-mode path.
271
+ bodies[f] = opts.dumpBodies.mode === 'aligned'
272
+ ? (0, excerpt_align_1.alignExcerpt)(text, c.subject, opts.dumpBodies.chars)
273
+ : text.slice(0, opts.dumpBodies.chars);
269
274
  }
270
275
  catch { /* unreadable candidate: omitted */ }
271
276
  }
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.3.3",
4
+ "version": "0.3.4",
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",