@verevoir/context 0.1.0

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 ADDED
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 — 2026-05-23
4
+
5
+ Initial release.
6
+
7
+ - `@verevoir/context` — `ContextStore` (content + symbol cache) keyed `(sourceId, version, itemId)`. `createContextStore` factory + default singleton. `grep` over cached content. `IndexKey` + `SymbolEntry` types.
8
+ - `@verevoir/context/code` — tree-sitter symbol extraction (`parseSymbols`, `detectLanguage`) for TypeScript / TSX / JavaScript + `findSymbols` over the store. Optional peer deps on `tree-sitter`, `tree-sitter-typescript`, `tree-sitter-javascript`.
9
+ - `@verevoir/context/sources` — `cachedReadFile` bridge that pairs the store with `@verevoir/sources/github`. Optional peer dep on `@verevoir/sources`.
10
+ - Extracted from aigency-web's `src/server/code-index/*` per ADR 019 (substrate libraries).
package/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,123 @@
1
+ # @verevoir/context
2
+
3
+ In-process content + symbol cache for LLM context windows. Keyed `(sourceId, version, itemId)`. Tree-sitter symbol extraction + grep + a cached-read bridge to `@verevoir/sources`, all as optional subpath imports.
4
+
5
+ ## Purpose
6
+
7
+ Lets an LLM agent navigate a codebase (or any versioned content source) without re-fetching the same files every turn. The cache is purpose-built for LLM-context flows: lazy population, cheap repeated lookups, symbol-aware indexing for files where that helps.
8
+
9
+ Pairs naturally with [`@verevoir/sources`](https://github.com/verevoir/sources) for the read side, but doesn't require it — consumers can populate the store from any source.
10
+
11
+ ## Subpaths
12
+
13
+ - `@verevoir/context` — core `ContextStore` (content + symbol cache), `grep` over cached content, `IndexKey` + `SymbolEntry` types. No external dependencies.
14
+ - `@verevoir/context/code` — tree-sitter symbol extraction (`parseSymbols`, `detectLanguage`) + `findSymbols` over the store. Optional peer deps on `tree-sitter`, `tree-sitter-typescript`, `tree-sitter-javascript`.
15
+ - `@verevoir/context/sources` — `cachedReadFile` bridge that pairs the store with `@verevoir/sources/github`. Optional peer dep on `@verevoir/sources`.
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ # Core only — no peer deps required.
21
+ npm install @verevoir/context
22
+
23
+ # Add tree-sitter symbol extraction.
24
+ npm install tree-sitter tree-sitter-typescript tree-sitter-javascript
25
+
26
+ # Add the cached-read bridge to @verevoir/sources.
27
+ npm install @verevoir/sources
28
+ ```
29
+
30
+ Peer dependencies are optional — install only the subpaths you import.
31
+
32
+ ## Canonical usage
33
+
34
+ ### Store + grep (root only)
35
+
36
+ ```ts
37
+ import { contextStore, grep } from '@verevoir/context';
38
+
39
+ // Populate the store with content from any source.
40
+ contextStore.setContent(
41
+ { sourceId: 'https://github.com/acme/charts', version: '', itemId: 'README.md' },
42
+ '# Charts\n\nA collection of charts.\n'
43
+ );
44
+
45
+ // Grep across cached items in a scope.
46
+ const hits = grep('charts', {
47
+ sources: [{ sourceId: 'https://github.com/acme/charts', version: '' }],
48
+ });
49
+ ```
50
+
51
+ ### Symbol search (with tree-sitter)
52
+
53
+ ```ts
54
+ import { contextStore } from '@verevoir/context';
55
+ import { findSymbols } from '@verevoir/context/code';
56
+
57
+ contextStore.setContent(
58
+ { sourceId: 'https://github.com/acme/charts', version: '', itemId: 'src/auth.ts' },
59
+ 'export class AuthHandler { authenticate() {} }'
60
+ );
61
+
62
+ const hits = findSymbols('auth', {
63
+ sources: [{ sourceId: 'https://github.com/acme/charts', version: '' }],
64
+ });
65
+ // → [{ name: 'AuthHandler', kind: 'class', ... }, { name: 'authenticate', kind: 'method', ... }]
66
+ ```
67
+
68
+ ### Cached reads against `@verevoir/sources`
69
+
70
+ ```ts
71
+ import { envFromProcessEnv } from '@verevoir/sources';
72
+ import { cachedReadFile } from '@verevoir/context/sources';
73
+
74
+ const env = envFromProcessEnv();
75
+ if (!env) throw new Error('GITHUB_TOKEN not set');
76
+
77
+ // First call fetches and caches; second call hits the cache.
78
+ const a = await cachedReadFile(env, 'https://github.com/acme/charts', 'README.md');
79
+ const b = await cachedReadFile(env, 'https://github.com/acme/charts', 'README.md');
80
+ // `b` came from cache; only one HTTP call was made.
81
+ ```
82
+
83
+ ## Key shape
84
+
85
+ The cache key is `(sourceId, version, itemId)`:
86
+
87
+ - **`sourceId`** — opaque identifier for the source. Typically a URL.
88
+ - **`version`** — version handle for the source. Git ref for code sources; etag / last-modified / content-hash for non-git. Empty string `''` is the canonical "default branch / latest" sentinel.
89
+ - **`itemId`** — item identifier within the source. Usually a path.
90
+
91
+ Different versions of the same item are independent cache entries. The store doesn't fetch — it caches what consumers put in it.
92
+
93
+ ## Symbol shape
94
+
95
+ ```ts
96
+ interface SymbolEntry {
97
+ name: string; // bare identifier: `AuthHandler`, not `class AuthHandler`
98
+ kind: SymbolKind; // 'function' | 'class' | 'method' | 'interface' | 'type' | 'enum'
99
+ startLine: number; // 1-indexed
100
+ endLine: number; // 1-indexed
101
+ }
102
+ ```
103
+
104
+ `@verevoir/context/code` populates these from tree-sitter parses (TypeScript / TSX / JavaScript today). Consumers can also populate from their own parsers.
105
+
106
+ ## Per-instance + singleton
107
+
108
+ The default singleton `contextStore` is shared across imports of the same module. Tests and multi-tenant consumers can call `createContextStore()` to get an isolated instance and pass it via the `store` option on `grep`, `findSymbols`, and `cachedReadFile`.
109
+
110
+ ## What this is NOT
111
+
112
+ - Not LSP. Tree-sitter gives structure (symbols, locations); no type resolution, no cross-reference. The LSP comparison is in ADR 019 in the aigency docs repo if relevant.
113
+ - Not a fetch layer. Reads happen via `@verevoir/sources` or whatever adapter you bring. The store caches what's handed to it.
114
+ - Not persistent. Per-process, in-memory. Cross-instance shared cache lands when forcing functions arrive.
115
+
116
+ ## See also
117
+
118
+ - [`@verevoir/sources`](https://github.com/verevoir/sources) — the SourceAdapter contract + implementations the cached-read bridge pairs with.
119
+ - [`@verevoir/llm`](https://github.com/verevoir/llm) — provider-agnostic LLM call surface.
120
+
121
+ ## License
122
+
123
+ Apache-2.0.
@@ -0,0 +1,51 @@
1
+ import { type ContextStore, type SymbolEntry, type SymbolKind } from '../index.js';
2
+ export type SupportedLanguage = 'typescript' | 'tsx' | 'javascript';
3
+ /** Heuristic mapping from a path → SupportedLanguage. Returns null
4
+ * when the file extension is not yet supported; callers should treat
5
+ * unknown extensions as "no symbols". */
6
+ export declare function detectLanguage(itemId: string): SupportedLanguage | null;
7
+ /** Walk the tree-sitter AST and pull out top-level + class-method
8
+ * symbol declarations. Nested function declarations inside other
9
+ * functions are skipped — they're not meaningful index entries at
10
+ * v0; the outer symbol's name suffices to locate them.
11
+ *
12
+ * Anonymous functions (function expressions / arrow functions
13
+ * without a binding name) are also skipped — they're not retrievable
14
+ * by `findSymbols(name)`. Named function expressions assigned to
15
+ * `const X = function () {...}` are captured under `X` via the
16
+ * variable-declarator branch. */
17
+ export declare function parseSymbols(language: SupportedLanguage, source: string): SymbolEntry[];
18
+ export interface FindScope {
19
+ /** Set of `(sourceId, version)` pairs to search across. Typically
20
+ * the caller passes the set of attached sources at a given
21
+ * version (e.g. all attached repos at the conversation's working
22
+ * branch). */
23
+ sources: Array<{
24
+ sourceId: string;
25
+ version: string;
26
+ }>;
27
+ }
28
+ export interface SymbolHit {
29
+ sourceId: string;
30
+ itemId: string;
31
+ name: string;
32
+ kind: SymbolKind;
33
+ startLine: number;
34
+ endLine: number;
35
+ }
36
+ export interface FindSymbolOptions {
37
+ /** Hard cap on hits returned. Default 50. */
38
+ maxResults?: number;
39
+ /** Match strategy. Default 'substring' — callers usually don't
40
+ * know exact symbol names. 'exact' is available for callers that
41
+ * do. Both are case-insensitive. */
42
+ match?: 'substring' | 'exact';
43
+ /** Store to search. Defaults to the module's singleton. */
44
+ store?: ContextStore;
45
+ }
46
+ /** Search the symbol index for entries whose name matches `query`.
47
+ * Triggers lazy parsing of any cached items that haven't been
48
+ * parsed yet — content cache + parse module + symbol cache compose
49
+ * into one fall-through. */
50
+ export declare function findSymbols(query: string, scope: FindScope, options?: FindSymbolOptions): SymbolHit[];
51
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/code/index.ts"],"names":[],"mappings":"AAwBA,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AAErB,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG,KAAK,GAAG,YAAY,CAAC;AAmCpE;;yCAEyC;AACzC,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAQvE;AAED;;;;;;;;;iCASiC;AACjC,wBAAgB,YAAY,CAAC,QAAQ,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE,CAOvF;AAoED,MAAM,WAAW,SAAS;IACxB;;;kBAGc;IACd,OAAO,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACvD;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;wCAEoC;IACpC,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC;IAC9B,2DAA2D;IAC3D,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAID;;;4BAG4B;AAC5B,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,SAAS,EAChB,OAAO,GAAE,iBAAsB,GAC9B,SAAS,EAAE,CA8Bb"}
@@ -0,0 +1,198 @@
1
+ // @verevoir/context/code — tree-sitter symbol extraction + symbol
2
+ // search over the ContextStore.
3
+ //
4
+ // Parses source text into a flat list of top-level symbols (functions,
5
+ // classes, methods, interfaces, types, enums) with their `:line`
6
+ // locations. No semantic analysis, no cross-reference — just the
7
+ // structural surface a downstream index uses to answer
8
+ // `find_symbol(name)` and to render compact repo-map summaries.
9
+ //
10
+ // `findSymbols` is the chat-time entry point. It walks every cached
11
+ // item in scope, lazily parses any whose symbols aren't yet cached,
12
+ // and returns hits. Composes the root store's content cache + this
13
+ // module's parse module + the root's symbol cache into one
14
+ // fall-through.
15
+ //
16
+ // v0 languages: TypeScript, TSX, JavaScript. Python + others follow.
17
+ //
18
+ // Peer deps on `tree-sitter`, `tree-sitter-typescript`, `tree-sitter-
19
+ // javascript` are optional in the package manifest — consumers who
20
+ // don't import this subpath don't need to install them.
21
+ import Parser from 'tree-sitter';
22
+ import TreeSitterTypeScript from 'tree-sitter-typescript';
23
+ import TreeSitterJavaScript from 'tree-sitter-javascript';
24
+ import { contextStore as defaultContextStore, } from '../index.js';
25
+ const LANGUAGES = {
26
+ typescript: TreeSitterTypeScript.typescript,
27
+ tsx: TreeSitterTypeScript.tsx,
28
+ javascript: TreeSitterJavaScript,
29
+ };
30
+ /** Per-language tree-sitter node-type → SymbolKind mapping. Shared
31
+ * across typescript/tsx/javascript since their grammars use the same
32
+ * relevant node names; centralised so adding Python or other
33
+ * languages keeps the divergence in one place. */
34
+ const NODE_TYPE_TO_KIND = {
35
+ function_declaration: 'function',
36
+ function_expression: 'function',
37
+ arrow_function: 'function',
38
+ class_declaration: 'class',
39
+ method_definition: 'method',
40
+ interface_declaration: 'interface',
41
+ type_alias_declaration: 'type',
42
+ enum_declaration: 'enum',
43
+ };
44
+ let cachedParser = null;
45
+ function getParser() {
46
+ if (cachedParser)
47
+ return cachedParser;
48
+ cachedParser = new Parser();
49
+ return cachedParser;
50
+ }
51
+ /** Heuristic mapping from a path → SupportedLanguage. Returns null
52
+ * when the file extension is not yet supported; callers should treat
53
+ * unknown extensions as "no symbols". */
54
+ export function detectLanguage(itemId) {
55
+ const lower = itemId.toLowerCase();
56
+ if (lower.endsWith('.ts'))
57
+ return 'typescript';
58
+ if (lower.endsWith('.tsx'))
59
+ return 'tsx';
60
+ if (lower.endsWith('.js') || lower.endsWith('.mjs') || lower.endsWith('.cjs'))
61
+ return 'javascript';
62
+ if (lower.endsWith('.jsx'))
63
+ return 'tsx';
64
+ return null;
65
+ }
66
+ /** Walk the tree-sitter AST and pull out top-level + class-method
67
+ * symbol declarations. Nested function declarations inside other
68
+ * functions are skipped — they're not meaningful index entries at
69
+ * v0; the outer symbol's name suffices to locate them.
70
+ *
71
+ * Anonymous functions (function expressions / arrow functions
72
+ * without a binding name) are also skipped — they're not retrievable
73
+ * by `findSymbols(name)`. Named function expressions assigned to
74
+ * `const X = function () {...}` are captured under `X` via the
75
+ * variable-declarator branch. */
76
+ export function parseSymbols(language, source) {
77
+ const parser = getParser();
78
+ parser.setLanguage(LANGUAGES[language]);
79
+ const tree = parser.parse(source);
80
+ const entries = [];
81
+ walk(tree.rootNode, entries, source);
82
+ return entries;
83
+ }
84
+ function walk(node, out, source) {
85
+ const kind = NODE_TYPE_TO_KIND[node.type];
86
+ if (kind) {
87
+ const name = extractName(node, kind);
88
+ if (name) {
89
+ out.push({
90
+ name,
91
+ kind,
92
+ startLine: node.startPosition.row + 1,
93
+ endLine: node.endPosition.row + 1,
94
+ });
95
+ }
96
+ }
97
+ // `const X = function() {}` / `const X = () => {}` — the function
98
+ // is in the initializer, the name is on the declarator. Walk into
99
+ // the declarator's initializer; if it's a function-shape, capture
100
+ // it under the declarator's name.
101
+ if (node.type === 'variable_declarator') {
102
+ const declarator = node;
103
+ const nameNode = declarator.childForFieldName('name');
104
+ const valueNode = declarator.childForFieldName('value');
105
+ if (nameNode &&
106
+ valueNode &&
107
+ (valueNode.type === 'arrow_function' || valueNode.type === 'function_expression')) {
108
+ out.push({
109
+ name: nameNode.text,
110
+ kind: 'function',
111
+ startLine: node.startPosition.row + 1,
112
+ endLine: node.endPosition.row + 1,
113
+ });
114
+ return;
115
+ }
116
+ }
117
+ for (const child of node.namedChildren) {
118
+ if (kind === 'function' || kind === 'method')
119
+ continue;
120
+ walk(child, out, source);
121
+ }
122
+ void source;
123
+ }
124
+ function extractName(node, kind) {
125
+ const nameNode = node.childForFieldName('name');
126
+ if (nameNode)
127
+ return nameNode.text;
128
+ if (kind === 'method') {
129
+ const first = node.namedChildren[0];
130
+ if (first && first.type === 'property_identifier')
131
+ return first.text;
132
+ }
133
+ return null;
134
+ }
135
+ const DEFAULT_FIND_MAX = 50;
136
+ /** Search the symbol index for entries whose name matches `query`.
137
+ * Triggers lazy parsing of any cached items that haven't been
138
+ * parsed yet — content cache + parse module + symbol cache compose
139
+ * into one fall-through. */
140
+ export function findSymbols(query, scope, options = {}) {
141
+ const max = options.maxResults ?? DEFAULT_FIND_MAX;
142
+ const match = options.match ?? 'substring';
143
+ const store = options.store ?? defaultContextStore;
144
+ const q = query.toLowerCase();
145
+ const hits = [];
146
+ for (const { sourceId, version } of scope.sources) {
147
+ const items = store.listIndexedItems(sourceId, version);
148
+ for (const itemId of items) {
149
+ if (hits.length >= max)
150
+ return hits;
151
+ const symbols = symbolsForItem(store, sourceId, version, itemId);
152
+ if (!symbols)
153
+ continue;
154
+ for (const sym of symbols) {
155
+ if (hits.length >= max)
156
+ return hits;
157
+ const name = sym.name.toLowerCase();
158
+ const isHit = match === 'exact' ? name === q : name.includes(q);
159
+ if (!isHit)
160
+ continue;
161
+ hits.push({
162
+ sourceId,
163
+ itemId,
164
+ name: sym.name,
165
+ kind: sym.kind,
166
+ startLine: sym.startLine,
167
+ endLine: sym.endLine,
168
+ });
169
+ }
170
+ }
171
+ }
172
+ return hits;
173
+ }
174
+ /** Get the symbols for a `(sourceId, version, itemId)`, lazily
175
+ * parsing on miss. Returns null when the item has no cached content
176
+ * (caller should treat as "not indexed") or when no language is
177
+ * detected for the path (e.g. .yaml, .md — no symbols to extract). */
178
+ function symbolsForItem(store, sourceId, version, itemId) {
179
+ const key = { sourceId, version, itemId };
180
+ const cached = store.getSymbols(key);
181
+ if (cached)
182
+ return cached;
183
+ const content = store.getContent(key);
184
+ if (content === undefined)
185
+ return null;
186
+ const language = detectLanguage(itemId);
187
+ if (!language) {
188
+ // Don't reparse on every call — empty result for non-code items
189
+ // is a legitimate answer. Cache an empty list so the language
190
+ // detect happens once per item.
191
+ store.setSymbols(key, []);
192
+ return [];
193
+ }
194
+ const symbols = parseSymbols(language, content);
195
+ store.setSymbols(key, symbols);
196
+ return symbols;
197
+ }
198
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/code/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ /** Symbol payload — language-agnostic structural shape. The `/code`
2
+ * subpath populates these from tree-sitter parses; consumers can
3
+ * also populate from their own parsers. */
4
+ export type SymbolKind = 'function' | 'class' | 'method' | 'interface' | 'type' | 'enum';
5
+ export interface SymbolEntry {
6
+ /** Bare identifier — `AuthHandler`, not `class AuthHandler`. */
7
+ name: string;
8
+ kind: SymbolKind;
9
+ /** 1-indexed line in the source. */
10
+ startLine: number;
11
+ /** 1-indexed line at the end of the declaration. */
12
+ endLine: number;
13
+ }
14
+ /** Three-part lookup key for content + symbols. The `sourceId` is
15
+ * the canonical identifier (typically a URL); `version` is the
16
+ * source-specific version handle (git ref, etag, content hash); the
17
+ * `itemId` is the source-local path (file path, page id, etc.). */
18
+ export interface IndexKey {
19
+ /** Today named `repoUrl` for git sources; generalises to source
20
+ * URLs (S3 bucket URL, wiki space URL) for other adapters. */
21
+ sourceId: string;
22
+ /** Git ref for code; etag / last-modified / content-hash for
23
+ * non-git sources. Empty string is the canonical "default" /
24
+ * "latest" sentinel. */
25
+ version: string;
26
+ /** Item identifier within the source — usually a path. */
27
+ itemId: string;
28
+ }
29
+ /** A ContextStore instance — content cache + symbol cache + the
30
+ * housekeeping that keeps them in sync (set-content drops the
31
+ * matching symbols entry; set-symbols presumes content is fresh).
32
+ *
33
+ * The module exports a default singleton (`contextStore`) plus a
34
+ * factory (`createContextStore`) so tests and multi-tenant
35
+ * consumers can spin up isolated instances. */
36
+ export interface ContextStore {
37
+ getContent(key: IndexKey): string | undefined;
38
+ setContent(key: IndexKey, content: string): void;
39
+ getSymbols(key: IndexKey): SymbolEntry[] | undefined;
40
+ setSymbols(key: IndexKey, entries: SymbolEntry[]): void;
41
+ /** Drop both content and symbols for one item. */
42
+ invalidateItem(key: IndexKey): void;
43
+ /** Drop every entry (content + symbols) for one
44
+ * `(sourceId, version)` — used when a commit lands on the ref,
45
+ * or an etag changes. */
46
+ invalidateVersion(sourceId: string, version: string): void;
47
+ /** Items under a given `(sourceId, version)` that have cached
48
+ * content. Backs `grep` and repo-map style listings. */
49
+ listIndexedItems(sourceId: string, version: string): string[];
50
+ /** Drop everything. Test affordance; in production state lives
51
+ * the lifetime of the host process. */
52
+ clearAll(): void;
53
+ }
54
+ export declare function createContextStore(): ContextStore;
55
+ /** Default singleton used by typical consumers. Tests and isolated
56
+ * use cases should prefer `createContextStore()`. */
57
+ export declare const contextStore: ContextStore;
58
+ export interface GrepScope {
59
+ /** Set of `(sourceId, version)` pairs to search across. */
60
+ sources: Array<{
61
+ sourceId: string;
62
+ version: string;
63
+ }>;
64
+ }
65
+ export interface GrepHit {
66
+ sourceId: string;
67
+ itemId: string;
68
+ /** 1-indexed line number. */
69
+ lineNumber: number;
70
+ /** The full matching line. */
71
+ line: string;
72
+ /** Up to `contextLines` lines before the hit (default 2). */
73
+ contextBefore: string[];
74
+ /** Up to `contextLines` lines after the hit (default 2). */
75
+ contextAfter: string[];
76
+ }
77
+ export interface GrepOptions {
78
+ /** Hard cap on hits returned. Default 50. */
79
+ maxResults?: number;
80
+ /** Case-insensitive substring match. Default false (case-sensitive). */
81
+ ignoreCase?: boolean;
82
+ /** Lines of context before + after each hit. Default 2. */
83
+ contextLines?: number;
84
+ /** Store to search. Defaults to the module's singleton. */
85
+ store?: ContextStore;
86
+ }
87
+ /** Plain-substring search across cached content. Operates on items
88
+ * the consumer has fetched into the store — does not fan out to
89
+ * the underlying source. Pure local lookup. */
90
+ export declare function grep(pattern: string, scope: GrepScope, options?: GrepOptions): GrepHit[];
91
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAoBA;;2CAE2C;AAC3C,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,MAAM,GAAG,MAAM,CAAC;AAEzF,MAAM,WAAW,WAAW;IAC1B,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,CAAC;IACjB,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;mEAGmE;AACnE,MAAM,WAAW,QAAQ;IACvB;kEAC8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB;;4BAEwB;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;CAChB;AAcD;;;;;;+CAM+C;AAC/C,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;IAC9C,UAAU,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACjD,UAAU,CAAC,GAAG,EAAE,QAAQ,GAAG,WAAW,EAAE,GAAG,SAAS,CAAC;IACrD,UAAU,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IACxD,kDAAkD;IAClD,cAAc,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAAC;IACpC;;6BAEyB;IACzB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3D;4DACwD;IACxD,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC9D;2CACuC;IACvC,QAAQ,IAAI,IAAI,CAAC;CAClB;AAED,wBAAgB,kBAAkB,IAAI,YAAY,CAmDjD;AAED;qDACqD;AACrD,eAAO,MAAM,YAAY,EAAE,YAAmC,CAAC;AAM/D,MAAM,WAAW,SAAS;IACxB,2DAA2D;IAC3D,OAAO,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACvD;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,4DAA4D;IAC5D,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wEAAwE;IACxE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAKD;;+CAE+C;AAC/C,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,EAAE,CA+B5F"}
package/dist/index.js ADDED
@@ -0,0 +1,127 @@
1
+ // @verevoir/context — in-process content + symbol cache for LLM
2
+ // context windows.
3
+ //
4
+ // Keyed `(sourceId, version, itemId)` — today realised as
5
+ // `(repoUrl, ref, path)` for git-style sources but the shape is
6
+ // generic. Two payload types:
7
+ //
8
+ // - content cache: bytes (or text) the consumer has fetched
9
+ // - symbol cache: parsed-symbol view of the same item (when the
10
+ // payload is code; populated lazily by the `/code` subpath)
11
+ //
12
+ // Pure in-memory. No I/O — the cache holds what consumers put in it.
13
+ // Bridges to actual sources live in subpaths (`/code` for tree-sitter
14
+ // symbol extraction; `/sources` for the cached-read helper that pairs
15
+ // with `@verevoir/sources`).
16
+ //
17
+ // Scope: per-process. A Cloud Run instance, a long-running worker,
18
+ // a CLI invocation — each holds its own. Cross-instance share lands
19
+ // when projects grow past per-process working sets.
20
+ // NUL byte as separator: never appears in URLs, refs, or paths, so
21
+ // concatenation can't collide. The NUL also makes the
22
+ // `(sourceId, version)` prefix a clean substring boundary for bulk
23
+ // invalidations + listIndexedItems.
24
+ const SEP = '\x00';
25
+ function flatKey(key) {
26
+ return `${key.sourceId}${SEP}${key.version}${SEP}${key.itemId}`;
27
+ }
28
+ function versionPrefix(sourceId, version) {
29
+ return `${sourceId}${SEP}${version}${SEP}`;
30
+ }
31
+ export function createContextStore() {
32
+ const contents = new Map();
33
+ const symbols = new Map();
34
+ return {
35
+ getContent(key) {
36
+ return contents.get(flatKey(key));
37
+ },
38
+ setContent(key, content) {
39
+ const k = flatKey(key);
40
+ contents.set(k, content);
41
+ // Content changed → any cached symbols for this item are
42
+ // stale; drop them. The next getSymbols miss tells the
43
+ // caller to re-parse.
44
+ symbols.delete(k);
45
+ },
46
+ getSymbols(key) {
47
+ return symbols.get(flatKey(key));
48
+ },
49
+ setSymbols(key, entries) {
50
+ symbols.set(flatKey(key), entries);
51
+ },
52
+ invalidateItem(key) {
53
+ const k = flatKey(key);
54
+ contents.delete(k);
55
+ symbols.delete(k);
56
+ },
57
+ invalidateVersion(sourceId, version) {
58
+ const prefix = versionPrefix(sourceId, version);
59
+ for (const k of contents.keys()) {
60
+ if (k.startsWith(prefix))
61
+ contents.delete(k);
62
+ }
63
+ for (const k of symbols.keys()) {
64
+ if (k.startsWith(prefix))
65
+ symbols.delete(k);
66
+ }
67
+ },
68
+ listIndexedItems(sourceId, version) {
69
+ const prefix = versionPrefix(sourceId, version);
70
+ const out = [];
71
+ for (const k of contents.keys()) {
72
+ if (k.startsWith(prefix)) {
73
+ out.push(k.slice(prefix.length));
74
+ }
75
+ }
76
+ return out.sort();
77
+ },
78
+ clearAll() {
79
+ contents.clear();
80
+ symbols.clear();
81
+ },
82
+ };
83
+ }
84
+ /** Default singleton used by typical consumers. Tests and isolated
85
+ * use cases should prefer `createContextStore()`. */
86
+ export const contextStore = createContextStore();
87
+ const DEFAULT_GREP_MAX = 50;
88
+ const DEFAULT_GREP_CONTEXT = 2;
89
+ /** Plain-substring search across cached content. Operates on items
90
+ * the consumer has fetched into the store — does not fan out to
91
+ * the underlying source. Pure local lookup. */
92
+ export function grep(pattern, scope, options = {}) {
93
+ const max = options.maxResults ?? DEFAULT_GREP_MAX;
94
+ const context = options.contextLines ?? DEFAULT_GREP_CONTEXT;
95
+ const ignoreCase = options.ignoreCase ?? false;
96
+ const store = options.store ?? contextStore;
97
+ const needle = ignoreCase ? pattern.toLowerCase() : pattern;
98
+ const hits = [];
99
+ for (const { sourceId, version } of scope.sources) {
100
+ const items = store.listIndexedItems(sourceId, version);
101
+ for (const itemId of items) {
102
+ if (hits.length >= max)
103
+ return hits;
104
+ const content = store.getContent({ sourceId, version, itemId });
105
+ if (content === undefined)
106
+ continue;
107
+ const lines = content.split('\n');
108
+ for (let i = 0; i < lines.length; i++) {
109
+ if (hits.length >= max)
110
+ return hits;
111
+ const haystack = ignoreCase ? lines[i].toLowerCase() : lines[i];
112
+ if (!haystack.includes(needle))
113
+ continue;
114
+ hits.push({
115
+ sourceId,
116
+ itemId,
117
+ lineNumber: i + 1,
118
+ line: lines[i],
119
+ contextBefore: lines.slice(Math.max(0, i - context), i),
120
+ contextAfter: lines.slice(i + 1, i + 1 + context),
121
+ });
122
+ }
123
+ }
124
+ }
125
+ return hits;
126
+ }
127
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { type SourceEnv } from '@verevoir/sources';
2
+ import { type ContextStore } from '../index.js';
3
+ export interface CachedReadOptions {
4
+ /** Source version to use as the cache key. Defaults to the empty
5
+ * string — the canonical "default branch / latest" sentinel. */
6
+ version?: string;
7
+ /** Store to read/write. Defaults to the module's singleton. */
8
+ store?: ContextStore;
9
+ }
10
+ /** Read a file from a GitHub repo via `@verevoir/sources/github`,
11
+ * with an in-process cache layered on top via `ContextStore`. Misses
12
+ * fetch and populate the store; hits return immediately.
13
+ *
14
+ * The cache key is `(repoUrl, version, path)` — the empty-string
15
+ * version sentinel keeps default-branch reads in a stable slot so
16
+ * different call sites reading the same file at default share the
17
+ * cache.
18
+ *
19
+ * For non-GitHub sources, callers can implement an equivalent bridge
20
+ * using their adapter's `readFile` and the same store. */
21
+ export declare function cachedReadFile(env: SourceEnv, repoUrl: string, path: string, options?: CachedReadOptions): Promise<string>;
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sources/index.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,EAAuC,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAErF,MAAM,WAAW,iBAAiB;IAChC;oEACgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED;;;;;;;;;;0DAU0D;AAC1D,wBAAsB,cAAc,CAClC,GAAG,EAAE,SAAS,EACd,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,MAAM,CAAC,CAWjB"}
@@ -0,0 +1,38 @@
1
+ // @verevoir/context/sources — bridge between a `@verevoir/sources`
2
+ // SourceAdapter and the root `ContextStore`.
3
+ //
4
+ // Wraps `readFile` with cache-lookup + populate-on-miss. First call
5
+ // for a given `(sourceId, version, itemId)` triggers the adapter
6
+ // fetch; subsequent calls return cached content without re-fetching.
7
+ //
8
+ // Peer dep on `@verevoir/sources` is optional in the package
9
+ // manifest — consumers using `@verevoir/context` purely as an
10
+ // in-memory cache (writing their own values via `setContent`) don't
11
+ // need to install it.
12
+ import { readFile } from '@verevoir/sources/github';
13
+ import { contextStore as defaultContextStore } from '../index.js';
14
+ /** Read a file from a GitHub repo via `@verevoir/sources/github`,
15
+ * with an in-process cache layered on top via `ContextStore`. Misses
16
+ * fetch and populate the store; hits return immediately.
17
+ *
18
+ * The cache key is `(repoUrl, version, path)` — the empty-string
19
+ * version sentinel keeps default-branch reads in a stable slot so
20
+ * different call sites reading the same file at default share the
21
+ * cache.
22
+ *
23
+ * For non-GitHub sources, callers can implement an equivalent bridge
24
+ * using their adapter's `readFile` and the same store. */
25
+ export async function cachedReadFile(env, repoUrl, path, options = {}) {
26
+ const version = options.version ?? '';
27
+ const store = options.store ?? defaultContextStore;
28
+ const key = { sourceId: repoUrl, version, itemId: path };
29
+ const cached = store.getContent(key);
30
+ if (cached !== undefined)
31
+ return cached;
32
+ // Map empty-string sentinel back to "no ref" for the adapter.
33
+ const ref = version === '' ? undefined : version;
34
+ const { content } = await readFile(env, repoUrl, path, ref);
35
+ store.setContent(key, content);
36
+ return content;
37
+ }
38
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sources/index.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,6CAA6C;AAC7C,EAAE;AACF,oEAAoE;AACpE,iEAAiE;AACjE,qEAAqE;AACrE,EAAE;AACF,6DAA6D;AAC7D,8DAA8D;AAC9D,oEAAoE;AACpE,sBAAsB;AAGtB,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,YAAY,IAAI,mBAAmB,EAAqB,MAAM,aAAa,CAAC;AAUrF;;;;;;;;;;0DAU0D;AAC1D,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAc,EACd,OAAe,EACf,IAAY,EACZ,UAA6B,EAAE;IAE/B,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;IACtC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,mBAAmB,CAAC;IACnD,MAAM,GAAG,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACzD,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACxC,8DAA8D;IAC9D,MAAM,GAAG,GAAG,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;IACjD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;IAC5D,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC/B,OAAO,OAAO,CAAC;AACjB,CAAC"}
package/llms.txt ADDED
@@ -0,0 +1,82 @@
1
+ # @verevoir/context
2
+
3
+ In-process content + symbol cache for LLM context windows. Keyed `(sourceId, version, itemId)`. Tree-sitter symbol extraction + grep + a cached-read bridge to `@verevoir/sources`, all as optional subpath imports.
4
+
5
+ ## Purpose
6
+
7
+ Lets an LLM agent navigate a codebase (or any versioned content source) without re-fetching the same files every turn. The cache is purpose-built for LLM-context flows: lazy population, cheap repeated lookups, symbol-aware indexing for files where that helps.
8
+
9
+ Pairs naturally with `@verevoir/sources` for the read side, but does not require it — consumers can populate the store from any source.
10
+
11
+ ## Subpaths
12
+
13
+ - `@verevoir/context` — `ContextStore` (content + symbol cache), `grep`, key + symbol types. No external dependencies.
14
+ - `@verevoir/context/code` — tree-sitter `parseSymbols`, `detectLanguage`, `findSymbols` over the store. Optional peer deps on tree-sitter packages.
15
+ - `@verevoir/context/sources` — `cachedReadFile` bridge to `@verevoir/sources/github`. Optional peer dep on `@verevoir/sources`.
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ npm install @verevoir/context
21
+ # Optional add-ons for the subpaths:
22
+ npm install tree-sitter tree-sitter-typescript tree-sitter-javascript
23
+ npm install @verevoir/sources
24
+ ```
25
+
26
+ ## Canonical usage
27
+
28
+ ```ts
29
+ import { contextStore, grep, createContextStore } from '@verevoir/context';
30
+ import { findSymbols, parseSymbols, detectLanguage } from '@verevoir/context/code';
31
+ import { cachedReadFile } from '@verevoir/context/sources';
32
+
33
+ // Populate the store.
34
+ contextStore.setContent(
35
+ { sourceId: 'https://github.com/acme/repo', version: '', itemId: 'src/auth.ts' },
36
+ 'export class AuthHandler {}',
37
+ );
38
+
39
+ // Grep across cached items.
40
+ const hits = grep('Auth', { sources: [{ sourceId: '...', version: '' }] });
41
+
42
+ // Find symbols by name (lazy-parses tree-sitter on miss).
43
+ const syms = findSymbols('Auth', { sources: [{ sourceId: '...', version: '' }] });
44
+
45
+ // Cached reads via the @verevoir/sources bridge.
46
+ const content = await cachedReadFile(env, repoUrl, path);
47
+ ```
48
+
49
+ ## Key shape
50
+
51
+ ```ts
52
+ interface IndexKey {
53
+ sourceId: string; // typically a URL
54
+ version: string; // git ref / etag / content hash; '' = default
55
+ itemId: string; // path within the source
56
+ }
57
+ ```
58
+
59
+ ## Symbol shape
60
+
61
+ ```ts
62
+ interface SymbolEntry {
63
+ name: string;
64
+ kind: 'function' | 'class' | 'method' | 'interface' | 'type' | 'enum';
65
+ startLine: number;
66
+ endLine: number;
67
+ }
68
+ ```
69
+
70
+ ## Per-instance + singleton
71
+
72
+ Default singleton `contextStore` shared across imports. Isolated instances via `createContextStore()`. `grep`, `findSymbols`, `cachedReadFile` accept a `store` option to target an isolated instance.
73
+
74
+ ## What this is NOT
75
+
76
+ - Not LSP. Structural symbols only; no type resolution.
77
+ - Not a fetch layer. Reads come from external sources (e.g. `@verevoir/sources`); the store caches what is handed to it.
78
+ - Not persistent. Per-process, in-memory.
79
+
80
+ ## License
81
+
82
+ Apache-2.0.
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@verevoir/context",
3
+ "version": "0.1.0",
4
+ "description": "In-process content + symbol cache for LLM context windows. Keyed (source, version, item). Tree-sitter symbol extraction + grep + cached-read bridge as subpath imports.",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./code": {
15
+ "types": "./dist/code/index.d.ts",
16
+ "import": "./dist/code/index.js"
17
+ },
18
+ "./sources": {
19
+ "types": "./dist/sources/index.d.ts",
20
+ "import": "./dist/sources/index.js"
21
+ }
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "README.md",
26
+ "LICENSE",
27
+ "llms.txt",
28
+ "CHANGELOG.md"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsc -p tsconfig.build.json",
32
+ "test": "vitest run",
33
+ "typecheck": "tsc --noEmit",
34
+ "lint": "prettier --check .",
35
+ "format": "prettier --write .",
36
+ "prepublishOnly": "npm run typecheck && npm test && npm run build"
37
+ },
38
+ "peerDependencies": {
39
+ "@verevoir/sources": "*",
40
+ "tree-sitter": "*",
41
+ "tree-sitter-javascript": "*",
42
+ "tree-sitter-typescript": "*"
43
+ },
44
+ "peerDependenciesMeta": {
45
+ "@verevoir/sources": {
46
+ "optional": true
47
+ },
48
+ "tree-sitter": {
49
+ "optional": true
50
+ },
51
+ "tree-sitter-javascript": {
52
+ "optional": true
53
+ },
54
+ "tree-sitter-typescript": {
55
+ "optional": true
56
+ }
57
+ },
58
+ "devDependencies": {
59
+ "@types/node": "^25.8.0",
60
+ "@verevoir/sources": "^0.1.0",
61
+ "prettier": "^3",
62
+ "tree-sitter": "^0.21.1",
63
+ "tree-sitter-javascript": "^0.23.1",
64
+ "tree-sitter-typescript": "^0.23.2",
65
+ "typescript": "^5",
66
+ "vitest": "^4"
67
+ },
68
+ "publishConfig": {
69
+ "access": "public"
70
+ },
71
+ "repository": {
72
+ "type": "git",
73
+ "url": "git+https://github.com/verevoir/context.git"
74
+ },
75
+ "engines": {
76
+ "node": ">=20"
77
+ }
78
+ }