@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 +10 -0
- package/LICENSE +201 -0
- package/README.md +123 -0
- package/dist/code/index.d.ts +51 -0
- package/dist/code/index.d.ts.map +1 -0
- package/dist/code/index.js +198 -0
- package/dist/code/index.js.map +1 -0
- package/dist/index.d.ts +91 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +127 -0
- package/dist/index.js.map +1 -0
- package/dist/sources/index.d.ts +22 -0
- package/dist/sources/index.d.ts.map +1 -0
- package/dist/sources/index.js +38 -0
- package/dist/sources/index.js.map +1 -0
- package/llms.txt +82 -0
- package/package.json +78 -0
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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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|