@wrongstack/tools 1.0.1 → 1.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.
Files changed (98) hide show
  1. package/dist/_win32-resolve.d.ts +1 -6
  2. package/dist/audit.js +5 -5
  3. package/dist/bash.js +2 -2
  4. package/dist/builtin.js +30 -30
  5. package/dist/{chunk-7LQCT3LT.js → chunk-32XPUSY3.js} +3 -3
  6. package/dist/{chunk-N3K2SFEI.js → chunk-3JVSOBHU.js} +1408 -200
  7. package/dist/{chunk-7N5U2RAU.js → chunk-3LS3FYEF.js} +2 -2
  8. package/dist/{chunk-FBUP524S.js → chunk-4YKVHCQC.js} +13 -2
  9. package/dist/{chunk-KE7A76VC.js → chunk-5U5ULQX2.js} +4 -14
  10. package/dist/{chunk-NU2IZ72J.js → chunk-6LCKF5KH.js} +8 -6
  11. package/dist/{chunk-GDDXX27P.js → chunk-6ZLRRC74.js} +2 -2
  12. package/dist/{chunk-IYH4OHBP.js → chunk-77MBK46T.js} +3 -3
  13. package/dist/{chunk-DK4J2WMH.js → chunk-AMURQAJX.js} +2 -2
  14. package/dist/{chunk-ZTCJCYLN.js → chunk-CVNCSMVU.js} +49 -6
  15. package/dist/{chunk-MJSJWXBF.js → chunk-DX5KZEMK.js} +14 -2
  16. package/dist/{chunk-CGXTR34H.js → chunk-E2ZD4ERD.js} +2 -2
  17. package/dist/{chunk-DRRY6SCM.js → chunk-EMA2XENR.js} +2 -2
  18. package/dist/{chunk-3D5DJWOE.js → chunk-FC3X7GGJ.js} +11 -3
  19. package/dist/{chunk-T3SMFF5Q.js → chunk-GU7QJ37F.js} +2 -2
  20. package/dist/{chunk-4WPBS45A.js → chunk-IRUEHGCR.js} +3 -3
  21. package/dist/{chunk-WEEVSWQB.js → chunk-KIQTABBJ.js} +3 -3
  22. package/dist/{chunk-6H4ERJP7.js → chunk-KYVOTHG7.js} +2 -2
  23. package/dist/{chunk-O6BY43ZB.js → chunk-MIV2RU5B.js} +3 -3
  24. package/dist/{chunk-5KS24LTS.js → chunk-NOCU555K.js} +2 -2
  25. package/dist/{chunk-6GOZTS6F.js → chunk-OUEH4I4X.js} +3 -3
  26. package/dist/{chunk-YEISRT4S.js → chunk-PNLDTRC5.js} +1429 -242
  27. package/dist/{chunk-GKSG5CPH.js → chunk-QU2VRCMM.js} +2 -2
  28. package/dist/{chunk-VVZPQPTY.js → chunk-REWOZIA4.js} +5 -3
  29. package/dist/{chunk-MGPR7VG3.js → chunk-T7YVY2BV.js} +2 -2
  30. package/dist/{chunk-IAYMTTR2.js → chunk-TYRVYBHW.js} +27 -23
  31. package/dist/{chunk-LQSOU63Z.js → chunk-VKAJMHVG.js} +2 -2
  32. package/dist/{chunk-E7JE3UEX.js → chunk-VO6M2FVG.js} +2 -2
  33. package/dist/{chunk-NJX2RXKJ.js → chunk-YDTRKEYY.js} +11 -3
  34. package/dist/{chunk-MK2JK35J.js → chunk-YECHJJJF.js} +3 -3
  35. package/dist/{chunk-QA6IASYX.js → chunk-Z34CUZCZ.js} +3 -3
  36. package/dist/{chunk-UCYQTZ7F.js → chunk-ZKC5UNGD.js} +56 -2
  37. package/dist/codebase-index/atlas-brief.d.ts +90 -0
  38. package/dist/codebase-index/atlas-export.d.ts +38 -0
  39. package/dist/codebase-index/atlas-projection.d.ts +114 -0
  40. package/dist/codebase-index/atlas-types.d.ts +90 -0
  41. package/dist/codebase-index/background-indexer.d.ts +15 -4
  42. package/dist/codebase-index/codebase-context-tool.d.ts +54 -0
  43. package/dist/codebase-index/concept-enrichment.d.ts +154 -0
  44. package/dist/codebase-index/context-retrieval.d.ts +91 -0
  45. package/dist/codebase-index/embedding-pass.d.ts +95 -0
  46. package/dist/codebase-index/graph-adjacency-cache.d.ts +36 -0
  47. package/dist/codebase-index/graph-rank-pass.d.ts +46 -0
  48. package/dist/codebase-index/graph-rank.d.ts +198 -0
  49. package/dist/codebase-index/index-service.d.ts +15 -1
  50. package/dist/codebase-index/index.d.ts +34 -19
  51. package/dist/codebase-index/index.js +128 -11
  52. package/dist/codebase-index/parser-worker-script.js +4 -4
  53. package/dist/codebase-index/perf-metrics.d.ts +19 -0
  54. package/dist/codebase-index/project-server-protocol.d.ts +2 -0
  55. package/dist/codebase-index/project-server-query-cache.d.ts +4 -0
  56. package/dist/codebase-index/project-server.js +55 -8
  57. package/dist/codebase-index/repo-map-fallback.d.ts +18 -0
  58. package/dist/codebase-index/repo-map-render.d.ts +42 -0
  59. package/dist/codebase-index/repo-map-types.d.ts +21 -0
  60. package/dist/codebase-index/repo-map.d.ts +28 -16
  61. package/dist/codebase-index/schema.d.ts +20 -0
  62. package/dist/codebase-index/worker-protocol.d.ts +47 -1
  63. package/dist/codebase-index/worker.js +12 -6
  64. package/dist/codebase-index/writer-concepts.d.ts +84 -0
  65. package/dist/codebase-index/writer-graph-decorate.d.ts +30 -0
  66. package/dist/codebase-index/writer-graph-reader.d.ts +22 -0
  67. package/dist/codebase-index/writer-rank.d.ts +78 -0
  68. package/dist/codebase-index/writer-schema.d.ts +51 -0
  69. package/dist/codebase-index/writer-vectors.d.ts +53 -0
  70. package/dist/codebase-index/writer.d.ts +59 -0
  71. package/dist/edit.js +8 -8
  72. package/dist/exec.js +2 -2
  73. package/dist/fetch.js +1 -1
  74. package/dist/format.js +5 -5
  75. package/dist/{go-parser-D7ELWQ7F.js → go-parser-EMKTAAD3.js} +5 -3
  76. package/dist/index.d.ts +48 -48
  77. package/dist/index.js +57 -33
  78. package/dist/install.js +5 -5
  79. package/dist/languages/index.js +3 -3
  80. package/dist/{legacy-bridge-YXANP5QB.js → legacy-bridge-WVQ4BHDI.js} +5 -5
  81. package/dist/lint.js +5 -5
  82. package/dist/outdated.js +2 -2
  83. package/dist/pack.js +31 -31
  84. package/dist/parser-dispatch-UQAB4E6V.js +14 -0
  85. package/dist/patch.js +8 -8
  86. package/dist/plan.js +2 -2
  87. package/dist/{py-parser-QC6MR4GB.js → py-parser-DDG53RXI.js} +4 -4
  88. package/dist/read.js +8 -8
  89. package/dist/replace.js +8 -8
  90. package/dist/task.js +2 -2
  91. package/dist/test.js +5 -5
  92. package/dist/todo.js +1 -1
  93. package/dist/tool-tier.js +32 -32
  94. package/dist/typecheck.js +5 -5
  95. package/dist/win32.js +1 -1
  96. package/dist/write.js +8 -8
  97. package/package.json +5 -5
  98. package/dist/parser-dispatch-BDVCJXTV.js +0 -14
@@ -0,0 +1,90 @@
1
+ /**
2
+ * The shapes the Codebase Atlas projection emits.
3
+ *
4
+ * They live apart from the code that builds them so that a renderer can name
5
+ * what it renders without importing the builder: `atlas-projection` produces
6
+ * these and calls `atlas-export` to render them, and `atlas-export` needs the
7
+ * types but must never reach back for the builder. Declaring them here is what
8
+ * keeps that a one-way edge instead of a module cycle.
9
+ *
10
+ * Everything here is plain data — no imports, no behaviour. `atlas-projection`
11
+ * re-exports the whole set, so consumers keep importing from there.
12
+ */
13
+ export interface AtlasSymbol {
14
+ name: string;
15
+ kind: string;
16
+ line: number;
17
+ }
18
+ export interface AtlasFile {
19
+ path: string;
20
+ rank: number;
21
+ inDeg: number;
22
+ outDeg: number;
23
+ package: string;
24
+ symbols: AtlasSymbol[];
25
+ /** Concept-layer summary. Omitted entirely when the layer has not run. */
26
+ concept?: string;
27
+ }
28
+ export interface AtlasPackage {
29
+ name: string;
30
+ files: number;
31
+ /** Highest-ranked file in the package. */
32
+ hub: string;
33
+ rank: number;
34
+ /** Subsystem summary for this package, when the concept layer derived one. */
35
+ summary?: string;
36
+ }
37
+ /**
38
+ * A package-level dependency, aggregated from the reference graph.
39
+ *
40
+ * Only the package level is projected. File-level edges would be tens of
41
+ * thousands of rows — a diff nobody can read and a file nobody can render,
42
+ * for a level of detail the index itself answers better on demand.
43
+ */
44
+ export interface AtlasEdge {
45
+ from: string;
46
+ to: string;
47
+ weight: number;
48
+ refType: string;
49
+ }
50
+ export interface AtlasDocument {
51
+ schema: number;
52
+ counts: {
53
+ files: number;
54
+ symbols: number;
55
+ packages: number;
56
+ };
57
+ packages: AtlasPackage[];
58
+ edges: AtlasEdge[];
59
+ files: AtlasFile[];
60
+ }
61
+ export interface AtlasManifest {
62
+ schema: number;
63
+ /**
64
+ * `files`/`symbols` are the index's own totals, for display.
65
+ *
66
+ * `tracked` is the size of the file set the digest was computed over — the
67
+ * only number a freshness check may compare its own file set against. The
68
+ * two can differ (a file with no symbols is tracked but may not count
69
+ * towards `files`), and comparing across them reported phantom additions.
70
+ */
71
+ counts: {
72
+ files: number;
73
+ symbols: number;
74
+ tracked: number;
75
+ };
76
+ /**
77
+ * sha-256 over every indexed file's `path\0contentHash`, sorted. Catches a
78
+ * change anywhere in the repository, including files the atlas does not
79
+ * itself carry.
80
+ */
81
+ digest: string;
82
+ /** Per-file content hashes, for the files the atlas describes. */
83
+ files: Record<string, string>;
84
+ }
85
+ export interface AtlasProjection {
86
+ document: AtlasDocument;
87
+ manifest: AtlasManifest;
88
+ markdown: string;
89
+ }
90
+ //# sourceMappingURL=atlas-types.d.ts.map
@@ -26,10 +26,11 @@
26
26
  * for the TUI status chip and the search/stats tools' gating.
27
27
  */
28
28
  import { type CircuitSnapshot } from './circuit-breaker.js';
29
+ import type { ContextResult } from './context-retrieval.js';
29
30
  import { type IncomingCallsResult, type OutgoingCallsResult } from './index-service.js';
30
31
  import { type ProjectIndexServerClientHealth, type ProjectIndexServerConnectionState, type ProjectIndexServerShutdownResult } from './project-server-client.js';
31
32
  import type { CodeMapGraph, IndexResult, IndexStats } from './schema.js';
32
- import type { CallRefsOpArgs, FileGraphOpArgs, SearchOpArgs, SearchOpResult, StatsOpArgs, SymbolGraphOpArgs } from './worker-protocol.js';
33
+ import type { CallRefsOpArgs, ContextOpArgs, FileGraphOpArgs, SearchOpArgs, SearchOpResult, StatsOpArgs, SymbolGraphOpArgs, VectorSearchOpArgs, VectorSearchOpResult } from './worker-protocol.js';
33
34
  /** True once the first full-project index has completed (success or failure). */
34
35
  export declare function isIndexReady(): boolean;
35
36
  /**
@@ -89,14 +90,13 @@ export declare function enqueueReindex(opts: {
89
90
  * Per-project trailing coalescing window. After a file's debounce timer
90
91
  * fires, the ready batch stays open for this long before flushing. Any file
91
92
  * whose timer fires within the window joins the same batch and resets the
92
- * timer (sliding). Default: 50ms. Set to 0 to flush immediately after each
93
- * debounce timer fires (legacy behavior — staggered bursts don't coalesce).
93
+ * timer (sliding). Default: 50ms; set to 0 for immediate flush.
94
94
  */
95
95
  coalesceWindowMs?: number | undefined;
96
96
  /** Watchdog timeout per file. Default: 30s. */
97
97
  timeoutMs?: number | undefined;
98
98
  onError?: ((err: unknown) => void) | undefined;
99
- }): void;
99
+ }): Promise<void>;
100
100
  /** Cancel all pending debounced reindexes. For teardown / tests. */
101
101
  export declare function cancelPendingReindexes(): void;
102
102
  /**
@@ -115,6 +115,17 @@ export declare function codebaseIndexStats(args: StatsOpArgs, opts?: {
115
115
  timeoutMs?: number | undefined;
116
116
  signal?: AbortSignal | undefined;
117
117
  }): Promise<IndexStats>;
118
+ /**
119
+ * Personalised retrieval — the single call that answers "which files does this
120
+ * task touch?", served by the same per-project index process so the wiring
121
+ * graph is built once per generation rather than once per query.
122
+ */
123
+ export declare function codebaseContext(args: ContextOpArgs): Promise<ContextResult>;
124
+ /**
125
+ * Nearest files to an already-embedded query vector. The model stays in the
126
+ * caller's process; only numbers cross IPC.
127
+ */
128
+ export declare function codebaseVectorSearch(args: VectorSearchOpArgs): Promise<VectorSearchOpResult>;
118
129
  /** Package dependency graph, served by the same per-project index process. */
119
130
  export declare function packageGraphService(args: StatsOpArgs): Promise<CodeMapGraph>;
120
131
  /** File dependency graph, served by the same per-project index process. */
@@ -0,0 +1,54 @@
1
+ /**
2
+ * `codebase-context` tool — one call that answers "which files does this task
3
+ * touch, and what is in them?".
4
+ *
5
+ * The index already exposed the ingredients: `codebase-search` finds symbols
6
+ * by name, `codebase-skeleton` shows a file's shape, `codebase-incoming-calls`
7
+ * finds callers. Composing them was left to the model, which meant re-deriving
8
+ * the same chain on every task and spending several round trips on plumbing.
9
+ * This tool runs the composition server-side: lexical search seeds a
10
+ * personalised PageRank walk over the reference graph, and what comes back is
11
+ * the ranked file set with the declarations that matter in each.
12
+ *
13
+ * It deliberately returns signatures and line numbers rather than source. The
14
+ * point is to make the subsequent `read` land in the right place, not to
15
+ * replace it — inlining bodies here would spend the caller's context on code
16
+ * they may not need.
17
+ */
18
+ import type { Tool } from '@wrongstack/core/types';
19
+ import type { ContextEntry } from './context-retrieval.js';
20
+ import type { EmbeddingPort } from './embedding-pass.js';
21
+ export interface CodebaseContextInput {
22
+ /** What you are trying to do or understand, in plain words. */
23
+ query: string;
24
+ /** Files to return. Defaults to 12, capped at 50. */
25
+ limit?: number | undefined;
26
+ /** Declarations shown per file. Defaults to 4, capped at 20. */
27
+ symbolsPerFile?: number | undefined;
28
+ /** Restrict results to a project-relative path prefix. */
29
+ pathPrefix?: string | undefined;
30
+ }
31
+ export interface CodebaseContextOutput {
32
+ query: string;
33
+ entries: ContextEntry[];
34
+ /** Lexical hits that seeded the walk. */
35
+ seedCount: number;
36
+ /** Semantic hits that also seeded it. Zero without an embedding model. */
37
+ semanticSeedCount: number;
38
+ /** Files the walk reached before truncation to `limit`. */
39
+ totalCandidates: number;
40
+ /**
41
+ * `ok` — a ranked answer. `no-index` — nothing indexed yet.
42
+ * `no-matches` — the index exists but the query matched nothing.
43
+ * `unranked` — symbols exist but no reference graph, so results are lexical
44
+ * only. `error` — the lookup itself failed; see `error`.
45
+ */
46
+ indexStatus: 'ok' | 'no-index' | 'no-matches' | 'unranked' | 'error';
47
+ /** True when a cached answer from a previous generation was served. */
48
+ stale?: boolean | undefined;
49
+ error?: string | undefined;
50
+ }
51
+ /** Install (or clear) the query-time embedding model. */
52
+ export declare function setContextQueryEmbedder(port: EmbeddingPort | undefined): void;
53
+ export declare const codebaseContextTool: Tool<CodebaseContextInput, CodebaseContextOutput>;
54
+ //# sourceMappingURL=codebase-context-tool.d.ts.map
@@ -0,0 +1,154 @@
1
+ /**
2
+ * The concept-layer enrichment pass.
3
+ *
4
+ * Walks the index's files most-central-first and asks a model, once per file,
5
+ * what the file is for. The answer is a short plain-English summary plus a
6
+ * **crux**: the line span that actually carries the file's meaning. A summary
7
+ * can drift from the truth; a pointer into the source cannot, so the two are
8
+ * always stored together.
9
+ *
10
+ * ## Why this is a separate pass
11
+ *
12
+ * Model calls take seconds. `runIndexerAtomic` is a SQLite write transaction —
13
+ * holding one open across thousands of network round trips would block every
14
+ * other writer for the duration and roll the whole thing back on the first
15
+ * failure. So enrichment runs on its own, outside the indexer, writing each
16
+ * result as it arrives.
17
+ *
18
+ * ## Why it is affordable
19
+ *
20
+ * `files.content_hash` already exists and is exactly the right cache key: a
21
+ * file whose bytes have not changed is never re-sent. A first pass over this
22
+ * repository is thousands of calls; every pass after it is only the files that
23
+ * actually changed. That is the difference between a one-off cost and a
24
+ * recurring one.
25
+ *
26
+ * ## Why it never takes the caller down
27
+ *
28
+ * Enrichment is an optional layer over a working index. A model that refuses,
29
+ * times out, or returns something unparseable degrades one file's summary — it
30
+ * is recorded in `errors` and the walk continues. Cancellation is honoured
31
+ * between files, and everything already written stays written.
32
+ */
33
+ import { type IndexStore } from './writer.js';
34
+ /** What the summariser is given about one file. */
35
+ export interface SummarizeFileInput {
36
+ /** Project-relative path, for the model's benefit. */
37
+ file: string;
38
+ /** Absolute path, if the summariser wants to read more itself. */
39
+ absolutePath: string;
40
+ language: string;
41
+ /** File source, already truncated to {@link MAX_SOURCE_CHARS}. */
42
+ source: string;
43
+ /** Whether `source` was cut short. */
44
+ truncated: boolean;
45
+ /** Declarations the index recorded, as orientation. */
46
+ declarations: ReadonlyArray<{
47
+ name: string;
48
+ kind: string;
49
+ line: number;
50
+ }>;
51
+ /** A previous, now-outdated summary, when one exists. */
52
+ staleSummary?: string | undefined;
53
+ signal?: AbortSignal | undefined;
54
+ }
55
+ export interface SummarizeFileResult {
56
+ /** One or two sentences on what the file is for. */
57
+ summary: string;
58
+ /** 1-based inclusive line span of the load-bearing lines. */
59
+ cruxStart?: number | undefined;
60
+ cruxEnd?: number | undefined;
61
+ /** Model identifier, recorded so a later pass can tell what produced this. */
62
+ model?: string | undefined;
63
+ }
64
+ export interface SummarizeSubsystemInput {
65
+ /** Package or directory label. */
66
+ name: string;
67
+ files: ReadonlyArray<{
68
+ file: string;
69
+ summary: string;
70
+ rank: number;
71
+ }>;
72
+ signal?: AbortSignal | undefined;
73
+ }
74
+ export interface SummarizeSubsystemResult {
75
+ summary: string;
76
+ /**
77
+ * Other subsystem names this one relates to, with a relation from the closed
78
+ * vocabulary. Unknown relations and unknown targets are dropped by the caller.
79
+ */
80
+ relations?: ReadonlyArray<{
81
+ to: string;
82
+ relation: string;
83
+ }> | undefined;
84
+ model?: string | undefined;
85
+ }
86
+ /**
87
+ * The host-supplied model transport.
88
+ *
89
+ * Injected rather than imported: producing a summary needs a configured
90
+ * provider and the model-tier policy, both of which live in the host. This
91
+ * mirrors how SAGE takes `getLlmCall` — `packages/tools` stays free of
92
+ * provider wiring, and a host that supplies no port simply gets no concepts.
93
+ */
94
+ export interface SummarizerPort {
95
+ describeFile(input: SummarizeFileInput): Promise<SummarizeFileResult | null>;
96
+ describeSubsystem?(input: SummarizeSubsystemInput): Promise<SummarizeSubsystemResult | null>;
97
+ }
98
+ /** Source sent per file. Enough for a summary; short enough to stay cheap. */
99
+ export declare const MAX_SOURCE_CHARS = 12000;
100
+ /** Crux span ceiling. Graft uses twelve lines; longer stops being a pointer. */
101
+ export declare const MAX_CRUX_LINES = 12;
102
+ export declare const DEFAULT_CONCURRENCY = 5;
103
+ /** Summary length ceiling, so one verbose model cannot bloat the layer. */
104
+ export declare const MAX_SUMMARY_CHARS = 400;
105
+ export interface EnrichOptions {
106
+ /** Stop after this many files. The natural way to sample the cost first. */
107
+ maxFiles?: number | undefined;
108
+ /** Files summarised in parallel. */
109
+ concurrency?: number | undefined;
110
+ /** Re-summarise files whose summary is already current. */
111
+ force?: boolean | undefined;
112
+ /** Also derive the subsystem layer, when the port supports it. */
113
+ subsystems?: boolean | undefined;
114
+ signal?: AbortSignal | undefined;
115
+ onProgress?: ((done: number, total: number) => void) | undefined;
116
+ }
117
+ export interface EnrichResult {
118
+ /** Files sent to the model. */
119
+ summarised: number;
120
+ /** Files skipped because their stored summary already matched. */
121
+ cached: number;
122
+ /** Files the model declined or failed on. */
123
+ failed: number;
124
+ /** Concepts marked stale before the walk started. */
125
+ markedStale: number;
126
+ /** Concepts dropped because their file left the index. */
127
+ pruned: number;
128
+ subsystems: number;
129
+ durationMs: number;
130
+ errors: string[];
131
+ }
132
+ /**
133
+ * Run one enrichment pass.
134
+ *
135
+ * `relativeOf` is injected for the same reason the retrieval walk takes it —
136
+ * the caller owns what "project-relative" means, and this module stays free of
137
+ * path policy.
138
+ */
139
+ export declare function enrichConcepts(store: IndexStore, port: SummarizerPort, relativeOf: (file: string) => string, options?: EnrichOptions): Promise<EnrichResult>;
140
+ /** Reported when a project has no index to enrich. */
141
+ export type ConceptIndexMissing = {
142
+ indexed: false;
143
+ };
144
+ /**
145
+ * Enrich a project's concept layer, owning the store lifetime so callers
146
+ * outside this package never touch `indexStorePool`.
147
+ *
148
+ * Refuses to run without an existing index: opening a store CREATES the
149
+ * database, and spending money summarising an empty index helps nobody.
150
+ */
151
+ export declare function enrichProjectConcepts(projectRoot: string, port: SummarizerPort, options?: EnrichOptions & {
152
+ indexDir?: string | undefined;
153
+ }): Promise<EnrichResult | ConceptIndexMissing>;
154
+ //# sourceMappingURL=concept-enrichment.d.ts.map
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Personalised retrieval — "which files does this task touch?"
3
+ *
4
+ * Lexical search proposes, the graph disposes. A BM25/FTS query produces a set
5
+ * of seed symbols with scores; those scores become the restart distribution of
6
+ * a personalised PageRank walk over the wiring graph. The walk then surfaces
7
+ * what the seeds are structurally attached to: the interface a matched
8
+ * function implements, the module every matched call site imports, the store
9
+ * behind the handler that matched by name.
10
+ *
11
+ * This is the piece that turns the index from a set of primitives an agent has
12
+ * to compose by hand — search, then skeleton, then incoming-calls, then read —
13
+ * into one answer. The composition was previously re-derived by the model on
14
+ * every task, at the cost of several round trips and a great deal of judgement
15
+ * spent on plumbing rather than on the problem.
16
+ *
17
+ * What comes back is declarations, not source: file, relevance, and the
18
+ * matching symbols with their signatures and line numbers. Reading the actual
19
+ * code stays a deliberate `read` — the point is to make that read land in the
20
+ * right place the first time.
21
+ */
22
+ import type { IndexStore } from './writer.js';
23
+ /** One symbol worth showing inside a returned file. */
24
+ export interface ContextSymbol {
25
+ name: string;
26
+ kind: string;
27
+ line: number;
28
+ signature: string;
29
+ /** True when lexical search matched this symbol directly. */
30
+ seed: boolean;
31
+ }
32
+ /** One file in the answer, most relevant first. */
33
+ export interface ContextEntry {
34
+ /** Project-relative POSIX path. */
35
+ file: string;
36
+ /** Relevance to this query, normalised so the top entry is 1.0. */
37
+ relevance: number;
38
+ /** Whether any symbol here matched the query lexically. */
39
+ matched: boolean;
40
+ symbols: ContextSymbol[];
41
+ }
42
+ export interface ContextResult {
43
+ query: string;
44
+ entries: ContextEntry[];
45
+ /** Lexical hits that seeded the walk. Zero means nothing matched. */
46
+ seedCount: number;
47
+ /** Semantic hits that also seeded it. */
48
+ semanticSeedCount: number;
49
+ /** Files the walk reached before truncation to `limit`. */
50
+ totalCandidates: number;
51
+ indexStatus: 'ok' | 'no-index' | 'no-matches' | 'unranked';
52
+ }
53
+ export interface ContextOptions {
54
+ query: string;
55
+ /**
56
+ * Files a semantic search already matched, with their cosine scores.
57
+ *
58
+ * Supplied by the caller rather than computed here because the embedding
59
+ * model lives host-side — functions cannot cross the daemon's IPC boundary,
60
+ * so only the resulting file scores travel. These become additional restart
61
+ * mass, letting a query phrased in the problem's vocabulary reach code whose
62
+ * identifiers never use those words.
63
+ */
64
+ vectorFiles?: ReadonlyArray<{
65
+ file: string;
66
+ score: number;
67
+ }> | undefined;
68
+ /** Files to return. */
69
+ limit?: number | undefined;
70
+ /** Symbols to show per file. */
71
+ symbolsPerFile?: number | undefined;
72
+ /** Restrict results to files under this project-relative prefix. */
73
+ pathPrefix?: string | undefined;
74
+ }
75
+ /**
76
+ * Lexical hits used as restart mass. More seeds make the walk broader and
77
+ * blunter; this is enough to cover a multi-word query's separate senses
78
+ * without letting a common token dominate.
79
+ */
80
+ export declare const SEED_LIMIT = 40;
81
+ export declare const DEFAULT_LIMIT = 12;
82
+ export declare const DEFAULT_SYMBOLS_PER_FILE = 4;
83
+ /**
84
+ * Run the personalised walk and group the winners into files.
85
+ *
86
+ * Returns an empty result rather than throwing whenever the index cannot
87
+ * answer — an unbuilt index and a query nobody matches are ordinary states,
88
+ * and `indexStatus` says which one happened.
89
+ */
90
+ export declare function retrieveContext(store: IndexStore, projectRoot: string, indexDir: string | undefined, options: ContextOptions, relativeOf: (file: string) => string): ContextResult;
91
+ //# sourceMappingURL=context-retrieval.d.ts.map
@@ -0,0 +1,95 @@
1
+ /**
2
+ * The semantic embedding pass.
3
+ *
4
+ * Embeds one vector per file so the index can answer questions phrased in the
5
+ * problem's vocabulary rather than the code's — "where do we back off after a
6
+ * 429" instead of `retryAfterMsFromHeaders`.
7
+ *
8
+ * ## What gets embedded, and why it is the file
9
+ *
10
+ * A bare declaration is poor material for an embedding: `function resolve(id:
11
+ * string): Widget` carries almost nothing a lexical index does not already
12
+ * have, and BM25 over FTS5 already matches it better. What carries meaning is
13
+ * the concept layer's description of what a file is *for*, so that is the text
14
+ * this pass embeds, falling back to the file's declaration names when no
15
+ * summary exists yet.
16
+ *
17
+ * That also makes it eight times cheaper than a symbol-level pass on this
18
+ * repository — eight thousand files against sixty-six thousand symbols.
19
+ *
20
+ * ## Why it is a pass and not part of indexing
21
+ *
22
+ * Model inference is asynchronous and batched. The indexer's write path is a
23
+ * synchronous SQLite transaction; running inference inside it would hold a
24
+ * write lock open across thousands of inferences. The older char-trigram
25
+ * embedding could live there precisely because it was neither of those things
26
+ * — and it was also not semantic, which is why it is being superseded rather
27
+ * than extended.
28
+ *
29
+ * ## Why the vectors never silently mix
30
+ *
31
+ * Two models produce incomparable spaces. The provider id is stored with every
32
+ * row and checked before a pass; a change wipes the table and re-embeds rather
33
+ * than leaving half the index in one space and half in another.
34
+ */
35
+ import { type IndexStore } from './writer.js';
36
+ /**
37
+ * The host-supplied embedding model.
38
+ *
39
+ * Structurally identical to the `EmbeddingProvider` that
40
+ * `packages/vector-memory` already defines, so its transformers-backed
41
+ * implementation satisfies this without an adapter. Injected rather than
42
+ * imported for the same reason the summariser is: `packages/tools` must not
43
+ * depend on an optional native model runtime.
44
+ */
45
+ export interface EmbeddingPort {
46
+ /** Stable id including the model and quantisation. Changing it re-embeds. */
47
+ readonly id: string;
48
+ /** Vector length. Must not change for a given `id`. */
49
+ readonly dimensions: number;
50
+ /** Embed a batch. Must return exactly one vector per input, in order. */
51
+ embed(texts: string[]): Promise<Float32Array[]>;
52
+ }
53
+ /** Texts sent per inference call. */
54
+ export declare const DEFAULT_BATCH_SIZE = 16;
55
+ /** Ceiling on embedded text. Beyond this a summary is not a summary. */
56
+ export declare const MAX_EMBED_CHARS = 2000;
57
+ export interface EmbedOptions {
58
+ /** Stop after this many files. */
59
+ maxFiles?: number | undefined;
60
+ batchSize?: number | undefined;
61
+ /** Re-embed even when the stored text hash still matches. */
62
+ force?: boolean | undefined;
63
+ signal?: AbortSignal | undefined;
64
+ onProgress?: ((done: number, total: number) => void) | undefined;
65
+ }
66
+ export interface EmbedResult {
67
+ embedded: number;
68
+ /** Files whose embedded text was unchanged. */
69
+ cached: number;
70
+ /** Vectors dropped because their file left the index. */
71
+ pruned: number;
72
+ /** True when a provider change forced a full re-embed. */
73
+ providerChanged: boolean;
74
+ /** Files with no summary, embedded from their declaration names instead. */
75
+ fromDeclarations: number;
76
+ durationMs: number;
77
+ errors: string[];
78
+ }
79
+ /**
80
+ * Run one embedding pass. Never throws: an inference failure costs that batch
81
+ * its vectors, is recorded in `errors`, and the walk continues.
82
+ */
83
+ export declare function embedFiles(store: IndexStore, port: EmbeddingPort, relativeOf: (file: string) => string, options?: EmbedOptions): Promise<EmbedResult>;
84
+ /** Reported when a project has no index to embed. */
85
+ export type EmbedIndexMissing = {
86
+ indexed: false;
87
+ };
88
+ /**
89
+ * Embed a project's files, owning the store lifetime so callers outside this
90
+ * package never touch `indexStorePool`. Refuses to run without an index.
91
+ */
92
+ export declare function embedProjectFiles(projectRoot: string, port: EmbeddingPort, options?: EmbedOptions & {
93
+ indexDir?: string | undefined;
94
+ }): Promise<EmbedResult | EmbedIndexMissing>;
95
+ //# sourceMappingURL=embedding-pass.d.ts.map
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Process-local cache of the wiring graph.
3
+ *
4
+ * Building the graph means pulling ~180k resolved refs, ~66k symbol→file
5
+ * pairs, the homonym counts and the import-visibility map, then laying the
6
+ * whole thing out as CSR — around half a second on this repo. That is fine
7
+ * once per index generation and unacceptable once per query, and the
8
+ * personalised retrieval walk is a per-query operation.
9
+ *
10
+ * The cache holds one graph. In the detached project server that is exactly
11
+ * right: one daemon serves one project. A caller for a different project (or
12
+ * a different index directory) simply replaces the entry rather than growing
13
+ * a map that would keep tens of megabytes alive for a project nobody is
14
+ * querying any more.
15
+ *
16
+ * Freshness is decided by the index's own `last_indexed` stamp plus the row
17
+ * counts, read fresh on every request. That costs three trivial queries and
18
+ * makes the cache self-invalidating from any path — daemon, worker, or
19
+ * inline — without having to be wired into the server's generation plumbing.
20
+ */
21
+ import { type WiringGraph } from './graph-rank.js';
22
+ import type { IndexStore } from './writer.js';
23
+ /** Everything the walk needs, built together and invalidated together. */
24
+ export interface WiringSnapshot {
25
+ graph: WiringGraph;
26
+ /** Declaring file per symbol id — the walk reports files, not just nodes. */
27
+ fileOf: ReadonlyMap<number, string>;
28
+ }
29
+ /**
30
+ * Return the wiring graph for this store, building it only when the index has
31
+ * changed since the last call.
32
+ */
33
+ export declare function getWiringSnapshot(store: IndexStore, projectRoot: string, indexDir: string | undefined): WiringSnapshot;
34
+ /** Drop the cached graph. Tests and shutdown paths only. */
35
+ export declare function clearWiringSnapshot(): void;
36
+ //# sourceMappingURL=graph-adjacency-cache.d.ts.map
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The index-time centrality pass.
3
+ *
4
+ * Runs inside the indexer's atomic update, immediately after ref resolution
5
+ * and module resolution have settled: `refs.to_id` is only final at that
6
+ * point, and a rank computed from half-resolved refs would describe a graph
7
+ * that never existed.
8
+ *
9
+ * Rank is a property of the whole graph, so this recomputes everything or
10
+ * nothing. That is affordable on a full run and wasteful on a one-file
11
+ * watcher run, which is what {@link shouldRefreshRanks} arbitrates.
12
+ */
13
+ import type { IndexStore } from './writer.js';
14
+ /**
15
+ * Data-version marker for the rank layer, in the same spirit as
16
+ * `relation_graph_version`: bumping it forces every index to recompute ranks
17
+ * once without a `SCHEMA_VERSION` bump (which would drop and rebuild the
18
+ * entire database). Older processes sharing the DB cannot downgrade it.
19
+ */
20
+ export declare const RANK_VERSION = "1";
21
+ export declare const RANK_VERSION_KEY = "rank_version";
22
+ /**
23
+ * Above this many files in a targeted run, recompute. A watcher run that
24
+ * touches one file barely moves any score, so leaving ranks one generation
25
+ * stale is the cheaper trade; a bulk targeted run is effectively a full run
26
+ * and should not be served stale ranks.
27
+ */
28
+ export declare const RANK_REFRESH_FILE_THRESHOLD = 25;
29
+ export interface RankPassResult {
30
+ /** False when the pass decided the existing ranks were good enough. */
31
+ computed: boolean;
32
+ symbols: number;
33
+ files: number;
34
+ durationMs: number;
35
+ }
36
+ export declare function shouldRefreshRanks(store: IndexStore, opts: {
37
+ files?: readonly string[] | undefined;
38
+ force?: boolean | undefined;
39
+ }): boolean;
40
+ /**
41
+ * Recompute and persist both rank tables. Never throws: a failed rank pass
42
+ * leaves the previous (or empty) ranks in place and must not take the index
43
+ * run down with it — every consumer treats a missing rank as "unranked".
44
+ */
45
+ export declare function runGraphRankPass(store: IndexStore, errors: string[]): RankPassResult;
46
+ //# sourceMappingURL=graph-rank-pass.d.ts.map