@nanonets/graft 0.6.0 → 0.7.1

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.
Files changed (71) hide show
  1. package/README.md +93 -52
  2. package/dist/ai/llm/types.d.ts +2 -2
  3. package/dist/ai/llm/types.js +1 -1
  4. package/dist/ask/ask.d.ts +49 -1
  5. package/dist/ask/ask.d.ts.map +1 -1
  6. package/dist/ask/ask.js +270 -68
  7. package/dist/ask/ask.js.map +1 -1
  8. package/dist/ask/fuse.d.ts +59 -17
  9. package/dist/ask/fuse.d.ts.map +1 -1
  10. package/dist/ask/fuse.js +51 -25
  11. package/dist/ask/fuse.js.map +1 -1
  12. package/dist/ask/graphrank.d.ts +6 -0
  13. package/dist/ask/graphrank.d.ts.map +1 -1
  14. package/dist/ask/graphrank.js +5 -1
  15. package/dist/ask/graphrank.js.map +1 -1
  16. package/dist/claude/format.d.ts +0 -1
  17. package/dist/claude/format.d.ts.map +1 -1
  18. package/dist/claude/format.js +18 -21
  19. package/dist/claude/format.js.map +1 -1
  20. package/dist/claude/hooks.d.ts +16 -0
  21. package/dist/claude/hooks.d.ts.map +1 -1
  22. package/dist/claude/hooks.js +79 -2
  23. package/dist/claude/hooks.js.map +1 -1
  24. package/dist/claude/settings-merge.d.ts.map +1 -1
  25. package/dist/claude/settings-merge.js +13 -6
  26. package/dist/claude/settings-merge.js.map +1 -1
  27. package/dist/claude/skill-template.d.ts.map +1 -1
  28. package/dist/claude/skill-template.js +117 -76
  29. package/dist/claude/skill-template.js.map +1 -1
  30. package/dist/cli-epilogue.js +1 -1
  31. package/dist/cli-epilogue.js.map +1 -1
  32. package/dist/cli.d.ts +3 -15
  33. package/dist/cli.d.ts.map +1 -1
  34. package/dist/cli.js +79 -45
  35. package/dist/cli.js.map +1 -1
  36. package/dist/context/node-file.d.ts +10 -0
  37. package/dist/context/node-file.d.ts.map +1 -1
  38. package/dist/context/node-file.js +34 -1
  39. package/dist/context/node-file.js.map +1 -1
  40. package/dist/context/savings.d.ts +6 -0
  41. package/dist/context/savings.d.ts.map +1 -1
  42. package/dist/context/savings.js +9 -1
  43. package/dist/context/savings.js.map +1 -1
  44. package/dist/engine.d.ts +1 -0
  45. package/dist/engine.d.ts.map +1 -1
  46. package/dist/engine.js +7 -1
  47. package/dist/engine.js.map +1 -1
  48. package/dist/graph/build.d.ts.map +1 -1
  49. package/dist/graph/build.js +37 -1
  50. package/dist/graph/build.js.map +1 -1
  51. package/dist/graph/map.d.ts +34 -2
  52. package/dist/graph/map.d.ts.map +1 -1
  53. package/dist/graph/map.js +107 -23
  54. package/dist/graph/map.js.map +1 -1
  55. package/dist/graph/types.d.ts +12 -0
  56. package/dist/graph/types.d.ts.map +1 -1
  57. package/dist/graph/workspace-cli.d.ts +37 -0
  58. package/dist/graph/workspace-cli.d.ts.map +1 -0
  59. package/dist/graph/workspace-cli.js +74 -0
  60. package/dist/graph/workspace-cli.js.map +1 -0
  61. package/dist/graph/workspace.d.ts +116 -124
  62. package/dist/graph/workspace.d.ts.map +1 -1
  63. package/dist/graph/workspace.js +353 -297
  64. package/dist/graph/workspace.js.map +1 -1
  65. package/dist/hosts/instructions.d.ts.map +1 -1
  66. package/dist/hosts/instructions.js +3 -0
  67. package/dist/hosts/instructions.js.map +1 -1
  68. package/dist/mcp/tools.d.ts.map +1 -1
  69. package/dist/mcp/tools.js +61 -1
  70. package/dist/mcp/tools.js.map +1 -1
  71. package/package.json +1 -2
package/README.md CHANGED
@@ -1,9 +1,6 @@
1
1
  <div align="center">
2
2
 
3
- <!-- placeholder: swap for the Graft logo asset when ready -->
4
- <!-- <img src="assets/graft.png" alt="Graft" width="220"/> -->
5
-
6
- # Graft
3
+ <img src="assets/graft-hero.png" alt="Graft — open-source context layer for large codebases" width="100%"/>
7
4
 
8
5
  **Your coding agent reads 30 files to change 3.**
9
6
  **Graft gives it the map it should have read first.**
@@ -17,8 +14,18 @@
17
14
  <img src="https://img.shields.io/badge/telemetry-none-546FFF?style=for-the-badge" />
18
15
  </p>
19
16
 
20
- <!-- numbers from the committed run in bench/results/ (2026-07-22, 162 agent runs, 2 repos) — see Benchmark below -->
21
- **32% less cost · 46% fewer tool calls · 60% lower latency** with the graph bundle pushed up front, at equal correctness — or **16% less cost with correctness up 5 points** when the agent pulls from the graph on demand. Measured against the same agent going in cold.
17
+ <!-- numbers from a 162-run benchmark (2026-07-22, 2 repos, 3 arms) — see Benchmark below -->
18
+
19
+ **vs. a standard coding session, no Graft at all:**
20
+
21
+ | Metric | With Graft |
22
+ |---|---|
23
+ | Cost | **32% less** |
24
+ | Tool calls | **46% fewer** |
25
+ | Latency | **60% lower** |
26
+ | Correctness | equal |
27
+
28
+ Same agent, same file tools, only the context differs. Pull the graph through the MCP tools instead of pushing it up front and correctness climbs **+5 points over cold** — the strongest result in the sweep. Full method → [Benchmark](#benchmark).
22
29
 
23
30
  </div>
24
31
 
@@ -28,6 +35,25 @@
28
35
 
29
36
  ---
30
37
 
38
+ ## Contents
39
+
40
+ - [Quick start](#quick-start)
41
+ - [Agent integration](#agent-integration) — [MCP server](#mcp-server) · [Claude Code (deep integration)](#claude-code-deep-integration)
42
+ - [The problem](#the-problem)
43
+ - [What Graft does](#what-graft-does)
44
+ - [How the graph gets built](#how-the-graph-gets-built)
45
+ - [What's in a node](#whats-in-a-node)
46
+ - [What runs where](#what-runs-where)
47
+ - [CLI](#cli)
48
+ - [Search & orient](#search--orient-graft-grep--graft-map) (`graft grep` / `graft map`)
49
+ - [Monorepos & multi-repo folders](#monorepos--multi-repo-folders)
50
+ - [Visualize it](#visualize-it-graft-viz) (`graft viz`)
51
+ - [Benchmark](#benchmark)
52
+ - [Development](#development)
53
+ - [License](#license)
54
+
55
+ ---
56
+
31
57
  ## Quick start
32
58
 
33
59
  ```bash
@@ -37,10 +63,10 @@ graft init # build the graph + wire it into Claude Code
37
63
 
38
64
  That is the whole setup. `graft init` builds `graft/` from your code and drops a statusline and hooks into `.claude/`, so from the next session on Graft rides along in Claude Code: it pulls the matching nodes into each prompt and rebuilds the graph in the background after every turn. No daemon, no re-indexing to remember, nothing to run or maintain by default — the graph is just files.
39
65
 
40
- Commit `graft/` so everyone who clones the repo (and their agents) gets the map:
66
+ `graft build` adds `graft/` to your `.gitignore` automatically — the graph is a local, regenerable cache (like `node_modules`), not something you commit. What you share is the wiring `init` dropped into `.claude/`; each teammate runs `graft build` to generate their own graph:
41
67
 
42
68
  ```bash
43
- git add graft && git commit -m "add context graph"
69
+ git add .claude && git commit -m "wire in graft"
44
70
  ```
45
71
 
46
72
  Prefer not to install globally? `npx @nanonets/graft init` works the same way.
@@ -70,12 +96,18 @@ npx @nanonets/graft init
70
96
 
71
97
  ### MCP server
72
98
 
73
- `graft init` also registers Graft's MCP server with agents that support it, so
74
- `graft_ask`, `graft_callers`, `graft_grep`, `graft_skeleton`, `graft_map`, and
75
- `graft_check` appear as native tools — no shell required. Claude Code now gets
76
- this too: `graft init` writes the server into the project's `.mcp.json` (restart
77
- Claude Code to load it). Skip with `--no-mcp`. Run it manually with
78
- `graft mcp [dir]`, or register it by hand:
99
+ `graft init` also registers Graft's MCP server with agents that support it, so these six tools appear natively, no shell required. Claude Code gets this too: `graft init` writes the server into the project's `.mcp.json` (restart Claude Code to load it). Skip with `--no-mcp`; run it manually with `graft mcp [dir]`.
100
+
101
+ | Tool | Takes | What it's for |
102
+ |---|---|---|
103
+ | `graft_ask` | a question | Ranked nodes with file:line, source inlined — usually the full answer, no follow-up read needed. |
104
+ | `graft_skeleton` | a file path | Every signature in that file, no bodies — the API surface for a tenth of the tokens. |
105
+ | `graft_callers` | a symbol | Who depends on it, or what it depends on with `direction: out`, N levels deep for blast radius. |
106
+ | `graft_grep` | a regex | Every hit, grouped by enclosing symbol, ranked by how coupled that symbol is. |
107
+ | `graft_map` | nothing | A first look at an unfamiliar repo: directory clusters, hubs, hotspots. |
108
+ | `graft_check` | nothing | Whether the local graph has drifted from the code. |
109
+
110
+ Register it by hand if your agent needs it explicit:
79
111
 
80
112
  ```json
81
113
  { "mcpServers": { "graft": { "command": "npx", "args": ["-y", "@nanonets/graft", "mcp"] } } }
@@ -126,7 +158,16 @@ Graft builds the graph in two passes, both powered by a language model:
126
158
  1. **Read each file.** Every source file is summarized once into a short description of what it does.
127
159
  2. **Group into nodes.** Those summaries are grouped into a curated set of nodes (subsystems, key files, and concepts) with typed links between them. Graft chooses the right level of detail for you instead of making one node per file, so a big repo becomes a few dozen readable nodes.
128
160
 
129
- Both passes are cached by content hash. Re-running only touches the files that changed, so the second build is fast and cheap.
161
+ ```mermaid
162
+ flowchart LR
163
+ S[Source files] --> T["Tier 1 — tree-sitter<br/>no model, no key"]
164
+ S --> P1["Pass 1 — LLM summarizes<br/>each file (--deep)"]
165
+ T --> W["graft/.graph/wiring.json<br/>per-symbol code graph"]
166
+ P1 --> P2["Pass 2 — group into nodes<br/>+ typed links"]
167
+ P2 --> N["graft/*.md<br/>markdown node graph"]
168
+ ```
169
+
170
+ Both LLM passes are cached by content hash. Re-running only touches the files that changed, so the second build is fast and cheap.
130
171
 
131
172
  Alongside the markdown graph, `graft build` builds `graft/.graph/wiring.json` — a per-symbol code graph — plus a per-file wiring card mirroring your source tree. Tier 1 is pure tree-sitter (every function, class, and call edge; deterministic, no model, no network), which is why plain `graft build` needs no key. The `--deep` pass adds a one-line summary and a crux excerpt per symbol, cached by body hash.
132
173
 
@@ -154,22 +195,6 @@ _Summary, sources, links, and notes ship today in markdown nodes. The crux ships
154
195
 
155
196
  ---
156
197
 
157
- ## Keeping it honest (CI)
158
-
159
- A graph that has drifted from the code is worse than no graph. `graft check` compares each node's tracked source hashes against the current files and fails if the graph is stale. Drop it in CI so a pull request cannot merge with an out-of-date map.
160
-
161
- ```bash
162
- graft check # exits non-zero if graft/ has drifted
163
- graft check --json # machine-readable drift report
164
- ```
165
-
166
- When the wiring graph exists, `graft check` validates it too — structural drift (symbols added, removed, or changed since the last `graft build`) and stale summaries both fail the check. A repo without a wiring graph is not penalized; the markdown graph stands alone.
167
-
168
- <!-- placeholder: planned, not yet in the CLI -->
169
- > **Planned: auto-regen on PR.** A single CI bot will regenerate changed nodes on a pull request and push the update, so the graph stays current without anyone remembering to run `build`. Until then, regenerate with `graft build --deep` and commit it alongside your code.
170
-
171
- ---
172
-
173
198
  ## What runs where
174
199
 
175
200
  - **On your machine, no key, no network:** the structural code graph. `graft build` (wiring graph + per-file cards), `graft check`, and `graft ask` are deterministic tree-sitter — they never call a model.
@@ -189,6 +214,9 @@ graft build --extensions .ts .py # only include these code extensions
189
214
 
190
215
  graft ask "<task>" [dir] # query the graph — ranked nodes + exact file:line (no LLM, no key)
191
216
  graft ask "<task>" --json # machine-readable result
217
+ graft ask "<task>" --in <scope> # narrow to one sub-project of a monorepo/multi-repo folder (see below)
218
+
219
+ graft skeleton <file> [dir] # every signature in one file, no bodies — the API surface for ~1/10th the tokens (no LLM, no key)
192
220
 
193
221
  graft callers <symbol> [dir] # who calls/references/imports/implements/extends a symbol (no LLM, no key)
194
222
  graft callers <symbol> --direction out # the reverse: what the symbol itself calls/references (was `graft callees`)
@@ -248,17 +276,35 @@ with file/symbol counts, each dir's local hubs, and the global hotspots —
248
276
  all ranked by in-degree, no LLM, no key:
249
277
 
250
278
  ```
251
- repo map — 105 files · 582 symbols · 1815 edges · typescript
279
+ repo map — 113 files · 687 symbols · 2186 edges · typescript
252
280
 
253
- src/ 54 files · 414 symbols hubs: set (bindings.ts, 38←), contextDirFor (node-file.ts, 11←), wiringPath (write.ts, 10←)
254
- test/ 36 files · 68 symbols hubs: edge (graph-traverse.test.ts, 4←), graphOf (graph-traverse.test.ts, 4←), nodeStub (graph-traverse.test.ts, 3←)
281
+ src/ 63 files · 527 symbols hubs: contextDirFor (node-file.ts, 21←), wiringPath (write.ts, 14←), buildGraph (build.ts, 11←)
282
+ test/ 43 files · 102 symbols hubs: edge (graph-traverse.test.ts, 4←), graphOf (graph-traverse.test.ts, 4←), fileNode (graph-map.test.ts, 3←)
255
283
  viewer/ 5 files · 58 symbols hubs: $ (main.ts, 9←), activeGraph (main.ts, 5←), cvar (data.ts, 5←)
256
- bench/ 8 files · 42 symbols hubs: makeClient (llm.ts, 3←), buildMarkdown (report.ts, 2←), judge (judge.ts, 2←)
257
284
  scripts/ 2 files · 0 symbols
258
285
 
259
- hotspots: set · method · src/graph/bindings.ts:L21-L23 · 38← contextDirFor · function · src/context/node-file.ts:L100-L103 · 11← wiringPath · function · src/graph/write.ts:L20-L22 · 10← ...
286
+ hotspots: contextDirFor · function · src/context/node-file.ts:L100-L103 · 21← wiringPath · function · src/graph/write.ts:L20-L22 · 14← buildGraph · function · src/graph/build.ts:L104-L218 · 11← ...
260
287
  ```
261
288
 
289
+ ## Monorepos & multi-repo folders
290
+
291
+ Graft handles two shapes without any config:
292
+
293
+ - **A monorepo with one `.git`** (a `pnpm-workspace.yaml`/`package.json`
294
+ `workspaces`, or per-package `go.mod`/`pyproject.toml`/`Cargo.toml`) —
295
+ `graft build` discovers each sub-project as a ranking scope. `ask`/`map`
296
+ rank every scope on its own terms and fuse the results, so the biggest
297
+ sub-project can't drown a small one; hits carry `[scope/]` labels, and
298
+ `graft map` groups its directory clusters by scope first.
299
+ - **A folder of separate git repos** (no `.git` at the top) — `graft build`
300
+ auto-splits: each child gets its own (git-ignored) `graft/`, and the parent
301
+ gets a `graft/workspace.json` index. Queries from the parent federate across
302
+ every child, always labeled `<child>/`. Run `graft build` inside a child to
303
+ work on just that repo.
304
+
305
+ Either way, narrow to one sub-project with `graft ask "<task>" --in <scope>/`
306
+ once you know where you're working.
307
+
262
308
  ## Visualize it (`graft viz`)
263
309
 
264
310
  `graft viz` opens a local, interactive view of both graphs — no install, no dev
@@ -292,26 +338,21 @@ normalized on load — no regeneration needed.
292
338
 
293
339
  ## Benchmark
294
340
 
295
- The claim Graft has to earn is simple: an agent that gets its context from the graph is cheaper and faster without getting more answers wrong. The harness runs every task through three arms of the same Claude Sonnet 5 agent with the same file tools: **cold** (explores from zero), **graph (push)** (a `graft ask --source` bundle injected up front), and **graft (pull)** (graft_ask/graft_skeleton tools, nothing injected — context paid for only when asked). An Opus 4.8 judge scores correctness with a required-keyword floor so a fast-but-wrong answer cannot win. Cost is cache-aware (reads ≈0.1×, writes 1.25×) — the billing model agents actually run under.
341
+ An agent that reads the graph should be cheaper and faster without getting more answers wrong. That's the whole claim, so we measured it instead of asserting it.
296
342
 
297
- Committed run: `bench/results/2026-07-22T13-19-14-246Z` — 162 runs across two repos (graft itself and a real Node/Express auth service), 3 trials each, tasks split between localized (single-file) and multi-file questions.
343
+ The harness ran three variants of the same Claude Sonnet 5 agent with the same file tools: **cold** (explores from zero), **Graft** (a `graft ask --source` bundle pushed up front), and **pull** (graft_ask/graft_skeleton tools, nothing injected — context paid for only when asked). An Opus 4.8 judge scored correctness with a required-keyword floor, so a fast-but-wrong answer couldn't win by being fast. Cost is cache-aware: reads ≈0.1×, writes 1.25×, the billing model agents actually run under.
298
344
 
299
- | Metric (mean/task) | Cold | Graph (push) | Graft (pull) |
300
- |---|---|---|---|
301
- | Cost ($) | 0.0429 | **0.0292 (−32%)** | 0.0362 (−16%) |
302
- | Uncached input tokens | 8,070 | **4,650 (−42%)** | 5,965 (−26%) |
303
- | Tool calls | 4.2 | **2.3 (−46%)** | 4.2 (−1%) |
304
- | Latency (s) | 39.8 | **15.8 (−60%)** | 39.5 (−1%) |
305
- | Correctness | 93% | 93% | **98% (+5 pts)** |
345
+ 162 runs, two repos (graft itself and a real Node/Express auth service), 3 trials each, tasks split between single-file and multi-file questions.
306
346
 
307
- Push is the cost/latency win; pull is the correctness win (and never lost to cold anywhere, including single-file tasks). On one corpus push traded 3 correctness points for its savings — the per-corpus and per-locality splits are in the committed results, not averaged away. Reproduce it yourself:
308
-
309
- ```bash
310
- npm run bench -- --smoke # 1 corpus, 1 task, all arms — a quick plumbing check
311
- npm run bench # full: all corpora, all tasks, 3 arms × 3 trials
312
- ```
347
+ | Metric (mean/task) | Cold | Graft |
348
+ |---|---|---|
349
+ | Cost ($) | 0.0429 | **0.0292 (−32%)** |
350
+ | Uncached input tokens | 8,070 | **4,650 (−42%)** |
351
+ | Tool calls | 4.2 | **2.3 (−46%)** |
352
+ | Latency (s) | 39.8 | **15.8 (−60%)** |
353
+ | Correctness | 93% | 93% (equal) |
313
354
 
314
- See [`bench/README.md`](bench/README.md) for the method and how to add your own repo.
355
+ Graft never answered worse than cold, on any corpus. The pull variant gave up most of that speed for something bigger: correctness jumped to 98%, +5 points over cold, the strongest single result in the sweep. Push when speed is what you need; pull when being right matters more.
315
356
 
316
357
  ---
317
358
 
@@ -2,7 +2,7 @@
2
2
  * Provider-neutral chat transport.
3
3
  *
4
4
  * Every LLM call in graft — the engine's single-shot summarize/synthesize/crux
5
- * ops AND the benchmark's multi-turn ReAct loop — goes through one interface,
5
+ * ops, and any multi-turn tool-use loop built on top — goes through one interface,
6
6
  * {@link ChatModel}. Adapters translate this neutral shape to a concrete SDK
7
7
  * (OpenAI-compatible or native Anthropic). Nothing above this layer knows which
8
8
  * provider is in play, so adding a provider is a new adapter, never a change to
@@ -37,7 +37,7 @@ export interface Message {
37
37
  /**
38
38
  * Request a provider cache marker (`cache_control: ephemeral`) on THIS message.
39
39
  * The transport only understands this per-message flag; any sliding/rotation
40
- * policy lives in the caller (see the bench loop).
40
+ * policy lives in the caller.
41
41
  */
42
42
  cacheBreakpoint?: boolean;
43
43
  /**
@@ -2,7 +2,7 @@
2
2
  * Provider-neutral chat transport.
3
3
  *
4
4
  * Every LLM call in graft — the engine's single-shot summarize/synthesize/crux
5
- * ops AND the benchmark's multi-turn ReAct loop — goes through one interface,
5
+ * ops, and any multi-turn tool-use loop built on top — goes through one interface,
6
6
  * {@link ChatModel}. Adapters translate this neutral shape to a concrete SDK
7
7
  * (OpenAI-compatible or native Anthropic). Nothing above this layer knows which
8
8
  * provider is in play, so adding a provider is a new adapter, never a change to
package/dist/ask/ask.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type Savings } from "../context/savings.js";
2
- import type { Relation } from "../graph/types.js";
2
+ import type { NodeV1, Relation } from "../graph/types.js";
3
3
  export interface AskHit {
4
4
  kind: "concept" | "symbol" | "caller" | "callee";
5
5
  title: string;
@@ -13,6 +13,10 @@ export interface AskHit {
13
13
  * This is what makes `ask` substitutive — the agent reads the span here
14
14
  * instead of opening the file, so no source read happens on top of the query. */
15
15
  code?: string;
16
+ /** Multi-scope repos only: the ranking scope (path prefix, "" = root) this
17
+ * hit was ranked within — answers "which sub-project is this from?". Drives
18
+ * the `[scope/] ` label in formatAsk. Absent on single-scope repos. */
19
+ scope?: string;
16
20
  }
17
21
  export interface AskResult {
18
22
  query: string;
@@ -34,7 +38,45 @@ export interface AskResult {
34
38
  * best result, i.e. the pack is probably noise for this prompt. Absent in
35
39
  * structural mode — a resolved "who calls X" is itself the relevance signal. */
36
40
  coverage?: number;
41
+ /** Like {@link coverage}, but the top hit's idf-weighted matched share over
42
+ * its NAME + PATH fields ONLY (body excluded) — a match-STRENGTH signal:
43
+ * "did the query hit a high-value field, or only incidental body tokens?".
44
+ * A body-only collision (a variable that happens to share a query word) has
45
+ * `coverageStrong === 0` while `coverage` can still look respectable. Used by
46
+ * workspace federation to gate a weak child out of the cross-repo ranking.
47
+ * Absent in structural mode; same lexical-only contract as `coverage`. */
48
+ coverageStrong?: number;
49
+ /** Multi-scope lexical results only: which scopes federated into the fused
50
+ * ranking, plus scopes that matched too weakly to federate (with their best
51
+ * doc id). Drives formatAsk's `matched in:` / `also matched:` footer.
52
+ * Absent on single-scope repos — zero output change there. */
53
+ scopes?: {
54
+ federated: string[];
55
+ alsoMatched: {
56
+ scope: string;
57
+ bestId: string;
58
+ }[];
59
+ };
37
60
  }
61
+ /** Idf-weighted share (0..1) of the query a document matches: each query term
62
+ * counts by its rarity, so this separates task prompts from chatter in a way a
63
+ * raw term count can't. A conversational prompt's words are either off-corpus
64
+ * (rare → heavy, unmatched → sinks the ratio) or generic code words ("list",
65
+ * "write": common → near-weightless even when they collide with a symbol name);
66
+ * a task prompt matches exactly its rare, discriminating identifiers. Terms the
67
+ * corpus has never seen take `dfltIdf` (the df=0 weight). */
68
+ /** The match-STRENGTH share behind `coverageStrong`: idf-weighted share of the
69
+ * query matched in the node's NAME field ONLY. The PATH field is deliberately
70
+ * excluded — a coincidental generic directory (`gateway/`) or file basename
71
+ * (`gateway.ts`) in an unrelated repo would otherwise clear the strength gate
72
+ * and float that repo to the top; an on-topic path-organized repo is still
73
+ * rescued by the broad-coverage HIGH_FLOOR clause instead. For the same reason
74
+ * a `kind:"file"` node contributes zero strength: its `name` is a basename (a
75
+ * path component), not a symbol name, so a file whose basename happens to share
76
+ * a query word is not a strong match. */
77
+ export declare function strongShare(q: Map<string, number>, node: NodeV1, doc: {
78
+ name: Map<string, number>;
79
+ }, idf: Map<string, number>, dfltIdf: number): number;
38
80
  export interface AskOptions {
39
81
  contextDir?: string;
40
82
  limit?: number;
@@ -51,6 +93,12 @@ export interface AskOptions {
51
93
  * decision point, ~10× cheaper than the full span — and the pack marks each
52
94
  * crux so the agent knows `--full` (or the file itself) has the rest. */
53
95
  full?: boolean;
96
+ /** Narrow the doc/node set to this path prefix BEFORE scoring (segment-aware,
97
+ * like `scopeOf` — "frontend" never matches "frontend-utils"). Per-scope
98
+ * IDF/BM25/walk come free: the filtered set is usually one scope, so the
99
+ * existing multi-scope machinery degrades to its single-scope passthrough
100
+ * with no scope labels. A prefix matching nothing indexed throws. */
101
+ in?: string;
54
102
  }
55
103
  /** Answer a query from the graft/ graph at `dir`. Deterministic, $0. */
56
104
  export declare function ask(dir: string, query: string, opts?: AskOptions): AskResult;
@@ -1 +1 @@
1
- {"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../../src/ask/ask.ts"],"names":[],"mappings":"AAmBA,OAAO,EAA6B,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGhF,OAAO,KAAK,EAA2B,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAI3E,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd;;qFAEiF;IACjF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAMD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,YAAY,GAAG,SAAS,GAAG,OAAO,CAAC;IACzC,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;sFAEkF;IAClF,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD;;;;oFAIgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA2aD,MAAM,WAAW,UAAU;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;qFACiF;IACjF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;sFAEkF;IAClF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;6EAIyE;IACzE,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AA0ED,wEAAwE;AACxE,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,UAAe,GAAG,SAAS,CA8BhF;AAID,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;2EAE2E;AAC3E,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,cAAc,CA8BtG;AAED,2DAA2D;AAC3D,wBAAgB,cAAc,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAUxD;AAOD,sEAAsE;AACtE,wBAAgB,SAAS,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CAsC9C"}
1
+ {"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../../src/ask/ask.ts"],"names":[],"mappings":"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"}