context-slice 1.8.2
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/LICENSE +21 -0
- package/README.md +349 -0
- package/dist/src/cli.js +199 -0
- package/dist/src/indexer/index.js +325 -0
- package/dist/src/languages/adapter.js +23 -0
- package/dist/src/languages/go/index.js +15 -0
- package/dist/src/languages/go/parse.js +443 -0
- package/dist/src/languages/go/resolve.js +313 -0
- package/dist/src/languages/java/enterprise/dependency-injection.js +175 -0
- package/dist/src/languages/java/enterprise/jpa-entity.js +89 -0
- package/dist/src/languages/java/enterprise/registry.js +22 -0
- package/dist/src/languages/java/enterprise/spring-data.js +239 -0
- package/dist/src/languages/java/enterprise/spring-mvc.js +183 -0
- package/dist/src/languages/java/enterprise/transactions.js +110 -0
- package/dist/src/languages/java.js +84 -0
- package/dist/src/languages/javascript/index.js +25 -0
- package/dist/src/languages/python/index.js +29 -0
- package/dist/src/languages/python/parse.js +415 -0
- package/dist/src/languages/python/resolve.js +413 -0
- package/dist/src/languages/rust/calls-resolve.js +1405 -0
- package/dist/src/languages/rust/index.js +12 -0
- package/dist/src/languages/rust/parse.js +545 -0
- package/dist/src/languages/rust/resolve.js +284 -0
- package/dist/src/languages/typescript/index.js +25 -0
- package/dist/src/languages/typescript/parse.js +793 -0
- package/dist/src/languages/typescript/resolve.js +463 -0
- package/dist/src/package-info.js +16 -0
- package/dist/src/parser/java-parser.js +339 -0
- package/dist/src/planner/budget.js +1 -0
- package/dist/src/planner/composition.js +372 -0
- package/dist/src/planner/rank.js +10 -0
- package/dist/src/render/compact-context.js +11 -0
- package/dist/src/server/mcp-server.js +154 -0
- package/dist/src/storage/sqlite.js +105 -0
- package/dist/src/types/enterprise.js +1 -0
- package/dist/src/types/model.js +1 -0
- package/dist/src/workflow/errors.js +10 -0
- package/dist/src/workflow/preview.js +220 -0
- package/dist/src/workflow/repository.js +56 -0
- package/mcp.json +10 -0
- package/package.json +76 -0
- package/plugin.json +15 -0
- package/queries/java/annotations.scm +1 -0
- package/queries/java/calls.scm +1 -0
- package/queries/java/imports.scm +1 -0
- package/queries/java/symbols.scm +2 -0
- package/skills/context-slice/SKILL.md +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ContextSlice contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
# ContextSlice
|
|
2
|
+
|
|
3
|
+
> Less context. Unchanged signal.
|
|
4
|
+
>
|
|
5
|
+
> Give your coding assistant the smallest useful slice of your codebase.
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<img src="assets/context-slice-hero.png" alt="Source files converging into a focused ContextSlice context" width="100%" />
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
You just joined a codebase with hundreds of files. Where do you start without
|
|
12
|
+
feeding an entire repository to an AI assistant?
|
|
13
|
+
|
|
14
|
+
ContextSlice builds a small, task-specific context from Java, TypeScript, TSX,
|
|
15
|
+
JavaScript, Python, Rust, and Go source. It finds the target symbol, follows
|
|
16
|
+
relevant callers and callees, and reports what was included or left out.
|
|
17
|
+
|
|
18
|
+
It is local, read-only, and deterministic: Tree-sitter performs the structural
|
|
19
|
+
analysis, SQLite stores the index, and no source code is sent to a hosted
|
|
20
|
+
service by ContextSlice.
|
|
21
|
+
|
|
22
|
+
## Quick start
|
|
23
|
+
|
|
24
|
+
### Claude Code plugin
|
|
25
|
+
|
|
26
|
+
Install it directly from the GitHub marketplace:
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
/plugin marketplace add nvxtien/context-slice
|
|
30
|
+
/plugin install context-slice@context-slice-marketplace
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The plugin provides a skill that tells Claude Code to request a focused
|
|
34
|
+
ContextSlice preview before reading source files. The MCP server then targets
|
|
35
|
+
the project Claude Code has open.
|
|
36
|
+
|
|
37
|
+
### Codex plugin
|
|
38
|
+
|
|
39
|
+
Add the GitHub marketplace to Codex:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
codex plugin marketplace add nvxtien/context-slice
|
|
43
|
+
codex plugin marketplace list
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Then open `/plugins`, choose `Context Slice Marketplace`, and install
|
|
47
|
+
`context-slice`. The plugin includes the same ContextSlice skill and stdio MCP
|
|
48
|
+
server for the project Codex has open.
|
|
49
|
+
|
|
50
|
+
### CLI and MCP
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
context-slice init
|
|
54
|
+
context-slice preview "explain the payment retry flow" --explain
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
For a new checkout, see [Installation](#installation) for the local build and
|
|
58
|
+
MCP setup.
|
|
59
|
+
|
|
60
|
+
## Why use it
|
|
61
|
+
|
|
62
|
+
- Whole files contain too much unrelated code.
|
|
63
|
+
- Task names are often enough to locate the relevant symbol, callers, and callees.
|
|
64
|
+
- Strict budgets make omissions visible instead of silently overflowing context.
|
|
65
|
+
- Unresolved dynamic dispatch stays unresolved rather than being guessed.
|
|
66
|
+
- The target repository is never edited; only `.context-slice/` is written locally.
|
|
67
|
+
|
|
68
|
+
## Supported languages
|
|
69
|
+
|
|
70
|
+
| Language | Extensions | Notes |
|
|
71
|
+
| ---------- | ------------------------------ | ------------------------------------------------------------------------- |
|
|
72
|
+
| Java | `.java` | Classes, interfaces, records, enums, methods, constructors |
|
|
73
|
+
| TypeScript | `.ts`, `.mts`, `.cts`, `.d.ts` | Imports, re-exports and barrels, overloads, arrow functions |
|
|
74
|
+
| TSX | `.tsx` | React components, handlers, JSX component references |
|
|
75
|
+
| JavaScript | `.js`, `.jsx`, `.mjs`, `.cjs` | Same adapter as TypeScript (JS is parsed as untyped TS); CommonJS (`require`/`module.exports`) is not recognized as imports/exports — see Known limitations below |
|
|
76
|
+
| Python | `.py`, `.pyi` | Packages and `__init__` re-exports, decorators, `self`/`cls`, dataclasses |
|
|
77
|
+
| Rust | `.rs` | Functions, structs, enums, traits, impls, modules; `use`/re-export resolution; self/associated/trait call resolution |
|
|
78
|
+
| Go | `.go` | Functions, methods (incl. generic receivers), structs, interfaces, struct embedding and interface satisfaction; same-package, import-qualified and receiver-typed call resolution |
|
|
79
|
+
|
|
80
|
+
One repository can hold all of them. See [docs/typescript-support.md](docs/typescript-support.md), [docs/python-support.md](docs/python-support.md) and [docs/rust-support.md](docs/rust-support.md) for what each language's resolution does and does not cover.
|
|
81
|
+
|
|
82
|
+
## Scope
|
|
83
|
+
|
|
84
|
+
- Tree-sitter structural and semantic analysis; no compiler, JDT, tsserver, Pyright, mypy, or LSP dependency, and no code is executed.
|
|
85
|
+
- A local stdio MCP server and a local SQLite cache under `.context-slice/`.
|
|
86
|
+
- Integrates with Codex and Claude Code through one stable command: `context-slice mcp`.
|
|
87
|
+
- Validated on macOS arm64 with Node 20 and Node 22. Other platforms are expected to work but are unverified.
|
|
88
|
+
|
|
89
|
+
## Installation
|
|
90
|
+
|
|
91
|
+
### Codex plugin (GitHub marketplace)
|
|
92
|
+
|
|
93
|
+
This repository includes a portable Codex plugin manifest and a separate
|
|
94
|
+
Codex marketplace entry backed by the npm runtime package. After publishing
|
|
95
|
+
the package, add the marketplace and install `context-slice` from `/plugins`:
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
codex plugin marketplace add nvxtien/context-slice
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Restart Codex after changing the plugin or marketplace files so it refreshes
|
|
102
|
+
the local marketplace snapshot.
|
|
103
|
+
|
|
104
|
+
### Claude Code plugin (GitHub marketplace)
|
|
105
|
+
|
|
106
|
+
This repository is also a Claude Code plugin. For local development or a local
|
|
107
|
+
checkout, use the path form instead:
|
|
108
|
+
|
|
109
|
+
```sh
|
|
110
|
+
git clone https://github.com/nvxtien/context-slice.git
|
|
111
|
+
cd context-slice
|
|
112
|
+
npm ci
|
|
113
|
+
npm run build
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```text
|
|
117
|
+
/plugin marketplace add /absolute/path/to/context-slice
|
|
118
|
+
/plugin install context-slice@context-slice-marketplace
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The current plugin runs the compiled `dist/` output, so a local checkout must
|
|
122
|
+
run `npm ci` and `npm run build` before first use. Re-run `npm run build` after
|
|
123
|
+
pulling updates. Its MCP server targets `${CLAUDE_PROJECT_DIR}`, not the plugin
|
|
124
|
+
repository itself.
|
|
125
|
+
|
|
126
|
+
### Local development
|
|
127
|
+
|
|
128
|
+
ContextSlice is not published to npm yet. From this checkout, install and link the local executable:
|
|
129
|
+
|
|
130
|
+
```sh
|
|
131
|
+
npm ci
|
|
132
|
+
npm run build
|
|
133
|
+
npm link
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Tarball validation
|
|
137
|
+
|
|
138
|
+
The package is publish-ready but is not currently published to the npm registry. Build and install the release-candidate tarball from a checkout (`npm ci` is required because `npm pack` compiles TypeScript first):
|
|
139
|
+
|
|
140
|
+
```sh
|
|
141
|
+
npm ci
|
|
142
|
+
npm pack
|
|
143
|
+
npm install -g ./context-slice-1.8.2.tgz
|
|
144
|
+
context-slice --version
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Node.js 20 or newer is required. `npm install` downloads the native `better-sqlite3` and `tree-sitter` builds for your platform, so it needs registry access.
|
|
148
|
+
|
|
149
|
+
The isolated packaging smoke test uses a temporary npm prefix and does not depend on `npm link`:
|
|
150
|
+
|
|
151
|
+
```sh
|
|
152
|
+
npm run benchmark:v08
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Registry installation (`npm install -g context-slice`) and `npx context-slice` remain publication-dependent and are not claimed as supported yet.
|
|
156
|
+
|
|
157
|
+
### CLI examples
|
|
158
|
+
|
|
159
|
+
Then, in a Java repository:
|
|
160
|
+
|
|
161
|
+
```sh
|
|
162
|
+
cd /absolute/path/to/my-java-project
|
|
163
|
+
context-slice init
|
|
164
|
+
context-slice preview "explain payment retry flow" --explain
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`init` creates `.context-slice/index.sqlite` automatically. Repository discovery uses `--repo` when given, otherwise the nearest Git root, otherwise the working directory.
|
|
168
|
+
|
|
169
|
+
In a TypeScript or TSX repository the workflow is identical:
|
|
170
|
+
|
|
171
|
+
```sh
|
|
172
|
+
cd /absolute/path/to/my-typescript-project
|
|
173
|
+
context-slice init
|
|
174
|
+
context-slice preview "explain the order create flow" --explain
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
```text
|
|
178
|
+
Target: OrderService.create
|
|
179
|
+
Context: 139/1200 tokens; 5 items included
|
|
180
|
+
|
|
181
|
+
Included:
|
|
182
|
+
- task target: OrderService.create — Selected because the task names create.
|
|
183
|
+
- direct caller: createOrder — Direct caller of OrderService.create.
|
|
184
|
+
- direct callee: SqlOrderRepository.save — Direct callee of OrderService.create.
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
In a Rust repository the workflow is identical:
|
|
188
|
+
|
|
189
|
+
```sh
|
|
190
|
+
cd /absolute/path/to/my-rust-project
|
|
191
|
+
context-slice init
|
|
192
|
+
context-slice preview "explain the retry loop" --explain
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
`status` reports the file count per extension, so a mixed repository shows `.java`, `.ts`, `.tsx` and `.rs` separately.
|
|
196
|
+
|
|
197
|
+
Use `context-slice --version` and `context-slice --help` to inspect the installed package without relying on the source checkout.
|
|
198
|
+
|
|
199
|
+
### Cache, cleanup, and uninstall
|
|
200
|
+
|
|
201
|
+
The only files ContextSlice writes are in `<repository>/.context-slice/`. That directory contains its own `.gitignore`, so it never shows up in `git status` and you do not need to edit your repository's `.gitignore`. ContextSlice never writes into its installed package directory.
|
|
202
|
+
|
|
203
|
+
- Rebuild from scratch: `rm -rf .context-slice && context-slice init`
|
|
204
|
+
- Remove ContextSlice from a repository: `rm -rf .context-slice`
|
|
205
|
+
- Uninstall the CLI: `npm uninstall -g context-slice` (repository caches are left in place; remove them as above)
|
|
206
|
+
|
|
207
|
+
Caches are versioned. A cache written by a different index schema, older or newer, is discarded and rebuilt automatically; it is never reused.
|
|
208
|
+
|
|
209
|
+
## CLI workflow
|
|
210
|
+
|
|
211
|
+
```sh
|
|
212
|
+
context-slice init
|
|
213
|
+
context-slice status
|
|
214
|
+
context-slice doctor
|
|
215
|
+
context-slice preview "explain retryPayment" --budget 1200 --explain
|
|
216
|
+
context-slice preview "explain retryPayment" --json
|
|
217
|
+
context-slice mcp
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
| Command | Purpose |
|
|
221
|
+
| ---------------- | ---------------------------------------------------------------- |
|
|
222
|
+
| `init` | Discover the repository and create/refresh the local index. |
|
|
223
|
+
| `index` | Refresh the index explicitly. |
|
|
224
|
+
| `status` | Show readiness, schema, cache freshness, and last refresh. |
|
|
225
|
+
| `doctor` | Check repository, Java source, cache, and MCP command readiness. |
|
|
226
|
+
| `preview <task>` | Return a deterministic, strict-budget context preview. |
|
|
227
|
+
| `mcp` | Start the stdio MCP server with the stable public command. |
|
|
228
|
+
|
|
229
|
+
Use `--repo /absolute/path` to select a repository. `--json` provides a stable automation-oriented result. Normal commands are quiet; `--explain` displays why each item was included or omitted.
|
|
230
|
+
|
|
231
|
+
Exit codes are `0` for success, `2` for user or configuration errors, and `1` for unexpected failures. Errors include a remediation, for example increasing `--budget` when the selected target cannot fit.
|
|
232
|
+
|
|
233
|
+
## Example: inspectable context reduction
|
|
234
|
+
|
|
235
|
+
```text
|
|
236
|
+
Target: demo.PaymentService.retryPayment
|
|
237
|
+
Context: 286/1200 tokens; 3 items included
|
|
238
|
+
|
|
239
|
+
Included:
|
|
240
|
+
- task target: demo.PaymentService.retryPayment — Selected because the task names retryPayment.
|
|
241
|
+
- direct caller: demo.PaymentController.retry — Direct caller of demo.PaymentService.retryPayment.
|
|
242
|
+
- direct callee: demo.PaymentService.audit — Direct callee of demo.PaymentService.retryPayment.
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
ContextSlice may also include sibling members that share state or local semantics with the target — a field it writes, the getter that exposes it, the constructor that supplies a dependency — plus a declaration-line skeleton of the enclosing type. It does not expand to the whole class or file. See [docs/context-composition.md](docs/context-composition.md).
|
|
246
|
+
|
|
247
|
+
The target body is always first. Related symbols use compact skeletons. The command never silently exceeds its budget; skipped candidates are reported as `context budget`, and unresolved calls remain unresolved rather than being guessed.
|
|
248
|
+
|
|
249
|
+
## Codex setup
|
|
250
|
+
|
|
251
|
+
After installing ContextSlice, register its one stable MCP command for a Java repository:
|
|
252
|
+
|
|
253
|
+
```sh
|
|
254
|
+
codex mcp add context-slice -- context-slice mcp --repo /absolute/path/to/my-java-project
|
|
255
|
+
codex mcp list
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Codex also supports project-scoped configuration in `.codex/config.toml` for trusted projects. See the [official OpenAI MCP documentation](https://developers.openai.com/es-419/docs/extend/mcp?surface=cli) for the current Codex CLI and configuration options.
|
|
259
|
+
|
|
260
|
+
Suggested assistant instruction:
|
|
261
|
+
|
|
262
|
+
```text
|
|
263
|
+
For Java implementation or explanation tasks, request context.preview with the task first.
|
|
264
|
+
Use the target, inclusion explanations, omissions, and unresolved calls to decide whether to
|
|
265
|
+
request context.symbol, context.callers, or context.slice. Do not assume unresolved runtime
|
|
266
|
+
dispatch has a concrete implementation.
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Claude Code setup
|
|
270
|
+
|
|
271
|
+
Use the equivalent local stdio registration for the same command:
|
|
272
|
+
|
|
273
|
+
```sh
|
|
274
|
+
claude mcp add --transport stdio context-slice -- context-slice mcp --repo /absolute/path/to/my-java-project
|
|
275
|
+
claude mcp list
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Verify command syntax against `claude mcp --help` in the installed Claude Code version before sharing configuration. ContextSlice itself speaks standard stdio MCP; this repository does not claim to have exercised every Claude Code release.
|
|
279
|
+
|
|
280
|
+
## How it works
|
|
281
|
+
|
|
282
|
+
1. Discover a Java repository and scan source while ignoring common generated/build directories.
|
|
283
|
+
2. Store symbols, call edges, hashes, schema version, and refresh time in a local SQLite cache.
|
|
284
|
+
3. Refresh before preview or MCP tool execution so changed Java files are not silently served stale.
|
|
285
|
+
4. Select a target from task text, then include the target body plus ranked direct callers/callees until the strict token budget is full.
|
|
286
|
+
|
|
287
|
+
Available MCP tools are `context.search`, `context.symbol`, `context.callers`, `context.preview`, `context.slice`, and `context.diff`. MCP stdout contains protocol messages only; diagnostics must not corrupt stdio framing.
|
|
288
|
+
|
|
289
|
+
## Trust and explainability
|
|
290
|
+
|
|
291
|
+
ContextSlice is deliberately conservative:
|
|
292
|
+
|
|
293
|
+
- Context previews use only task text, repository source, index data, and configuration. They do not read benchmark answers, required facts, expected symbols, or manual baselines.
|
|
294
|
+
- Call resolution distinguishes exact, probable, and unresolved edges. It does not invent runtime dispatch targets.
|
|
295
|
+
- `CURRENT`, `STALE`, `REFRESHING`, and `ERROR` are the workflow state vocabulary. The CLI exposes the observable cache state; preview/MCP refresh before serving context.
|
|
296
|
+
- Preview, status, doctor, and MCP lookup operations are read-only except for the local cache.
|
|
297
|
+
|
|
298
|
+
## Benchmarks
|
|
299
|
+
|
|
300
|
+
Run the workflow benchmark locally:
|
|
301
|
+
|
|
302
|
+
```sh
|
|
303
|
+
npm run benchmark:v07
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
It writes [JSON](benchmarks/results/v0.7-developer-workflow.json) and [Markdown](benchmarks/results/v0.7-developer-workflow.md) reports with fresh init, cold index, first/warm preview, one-file refresh, and first/subsequent MCP query timings. Timings apply only to the recorded local fixture environment. Codex/Claude telemetry is optional and is reported as unavailable when the runtime provides none.
|
|
307
|
+
|
|
308
|
+
`npm run benchmark:v03` through `benchmark:v06` first run `npm run benchmark:checkouts`, which fetches the pinned benchmark repositories from `benchmarks/repositories.json` (network required; about 120 MB).
|
|
309
|
+
|
|
310
|
+
Run `npm run benchmark:v08` for the tarball packaging, isolated installation, upgrade, uninstall, MCP, path-with-spaces, nested-cwd, publish-dry-run, and clean-room self-trial report in [JSON](benchmarks/results/v0.8-packaging-installation.json) and [Markdown](benchmarks/results/v0.8-packaging-installation.md). External developer participation is explicitly deferred; this is not a multi-user study.
|
|
311
|
+
|
|
312
|
+
Earlier semantic/context measurements remain available:
|
|
313
|
+
|
|
314
|
+
In the 15-task Java benchmark across three pinned Java repositories, ContextSlice reduced median context size by 94.55% while preserving 100% required-fact recall and 100% retrieval recall. In the separate 15-task TypeScript benchmark across three pinned TypeScript/TSX repositories, it reduced median context size by 83.32% with 95.56% required-fact recall and 100% retrieval recall. Context sizes are deterministic estimates from the built-in estimator, not assistant telemetry, so these are estimated token reductions rather than observed input token usage. The two benchmarks use different repositories and tasks and are not comparable to each other.
|
|
315
|
+
|
|
316
|
+
Run the TypeScript benchmark with `npm run benchmark:v11`; its report is [v1.1 TypeScript support](benchmarks/results/v1.1-typescript-support.md).
|
|
317
|
+
|
|
318
|
+
Rust support is measured separately across three pinned repositories (walkdir, mini-redis, ripgrep's `crates/ignore`). Module-path assignment, `use` resolution and re-export resolution were measured on real repositories (module resolution 64-100% depending on crate layout, anchored `use` resolution 100%, re-export resolution 100%; see [v1.5 Phase 1](benchmarks/results/v1.5-phase1-rust-real-repositories.md)). In the 15-task Rust benchmark across the same three repositories, ContextSlice reduced median context size by 93.52% while preserving 100% required-fact recall and 100% retrieval recall, with 0% whole-file fallback; see [v1.5 Phase 3](benchmarks/results/v1.5-phase3-rust-tasks.md). Rust resolution is static analysis, Tree-sitter-first: there is no rust-analyzer or rustc dependency, macro-generated semantics may remain unresolved, and trait dispatch may remain conservative (structural evidence only).
|
|
319
|
+
|
|
320
|
+
- [v0.6 developer context efficiency](benchmarks/results/v0.6-developer-context-efficiency.md) compares auditable manual whole-file baselines with ContextSlice on pinned Java repositories. Token counts are deterministic estimates unless telemetry is explicitly available.
|
|
321
|
+
- [v0.5 semantic call resolution](benchmarks/results/v0.5-semantic-call-resolution.md) documents declared versus runtime target limitations.
|
|
322
|
+
- [v0.4 symbol index hardening](benchmarks/results/v0.4-symbol-index-hardening.md) documents stable symbol identity and lookup behavior.
|
|
323
|
+
|
|
324
|
+
## Limitations
|
|
325
|
+
|
|
326
|
+
- Java, TypeScript, TSX, JavaScript, Python, Rust and Go only; no other languages, embeddings, vector database, compiler, tsserver, type checker, rust-analyzer, rustc, or LSP integration.
|
|
327
|
+
- Python is dynamic: receivers built by factories, `getattr`, dynamic imports and monkey patching stay unresolved rather than guessed.
|
|
328
|
+
- Rust: `#[cfg(...)]` alternatives are all attached as probable call targets and all reachable via context composition (`runtimeTargetIds`), not just the first-listed one. The macro-argument call-recovery denylist covers format/log/assert/panic-style macros plus `anyhow!`/`bail!`/`matches!`; `ensure!`/`dbg!` are deliberately not denylisted (their arguments are genuine expressions). `Cargo.toml` is read for workspace crate-name resolution (`[package]` + `[workspace].members`, including simple `dir/*` globs); still inferred from directory layout only within a single crate (e.g. a crate's own `tests/`/`src/bin/*` referencing it by name is classified external).
|
|
329
|
+
- TypeScript resolution is structural. Receivers whose type needs inference, CommonJS `require`, and imports that leave the checked-out source stay unresolved rather than guessed.
|
|
330
|
+
- JavaScript reuses the TypeScript adapter as-is (JS is a syntactic subset of TS). CommonJS (`require()`/`module.exports`) is not recognized as imports/exports at all — only ES `import`/`export` syntax is; symbol and call extraction are unaffected by module system. A property-assigned function expression (`obj.method = function(){}`) at module level IS extracted as a symbol; `module.exports`/`exports` targets are left alone, per the CommonJS limitation above. See [benchmarks/results/v1.7-javascript-support.md](benchmarks/results/v1.7-javascript-support.md).
|
|
331
|
+
- Go resolution reaches across packages within the same module (interface satisfaction, and method calls through a package-qualified local variable), and `go.work` multi-module workspaces are supported. Interface satisfaction now matches exact method signatures, not just names, and a constructor-typed local variable (`x := NewFoo()`) resolves by the constructor's real declared return type. Still: struct embedding's own promotion lookup stays same-package. See [benchmarks/results/v1.6-go-support.md](benchmarks/results/v1.6-go-support.md).
|
|
332
|
+
- Target selection from task text is heuristic and may choose a nearby but not ideal symbol. Naming the method in the task gives a better slice.
|
|
333
|
+
- Tree-sitter analysis cannot prove runtime dispatch, framework-generated implementations, or all generic/fluent call behavior.
|
|
334
|
+
- Token counts are estimates, not model-provider usage telemetry.
|
|
335
|
+
- Sibling composition uses syntactic evidence (`this.field` and Java field names). State shared through an intermediate object is not detected.
|
|
336
|
+
- The local index is an aid to request context, not a substitute for code review or tests.
|
|
337
|
+
- Validated on macOS arm64 (Node 20.19.5 and 22.12.0). Linux and Windows are unverified.
|
|
338
|
+
- Usability evidence comes from a scripted self clean-room trial; no external developer trial has been run yet.
|
|
339
|
+
|
|
340
|
+
## Development
|
|
341
|
+
|
|
342
|
+
```sh
|
|
343
|
+
npm ci
|
|
344
|
+
npm run build
|
|
345
|
+
npm test
|
|
346
|
+
npm run benchmark:v07
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
`npm run release:rc` performs the v0.9 clean-room release-candidate validation: fresh clone, `npm ci`, build, tests, regressions, `npm pack`, and an isolated install with a temporary `HOME` and npm cache against a freshly cloned Java repository. See [docs/release-readiness-v0.9.md](docs/release-readiness-v0.9.md) and [CHANGELOG.md](CHANGELOG.md).
|
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { ProjectIndex } from "./indexer/index.js";
|
|
3
|
+
import { packageInfo } from "./package-info.js";
|
|
4
|
+
import { buildPreview } from "./workflow/preview.js";
|
|
5
|
+
import { WorkflowError } from "./workflow/errors.js";
|
|
6
|
+
import { resolveRepositoryRoot } from "./workflow/repository.js";
|
|
7
|
+
function usage() {
|
|
8
|
+
return [
|
|
9
|
+
"Usage: context-slice <init|index|status|doctor|preview|mcp> [options]",
|
|
10
|
+
"",
|
|
11
|
+
"Options:",
|
|
12
|
+
" --repo <path> Repository root (defaults to nearest Git root)",
|
|
13
|
+
" --budget <tokens> Strict token budget for preview",
|
|
14
|
+
" --json Emit stable JSON output",
|
|
15
|
+
" --explain Include inclusion and omission explanations",
|
|
16
|
+
" --verbose Include additional operational detail",
|
|
17
|
+
"",
|
|
18
|
+
"Commands:",
|
|
19
|
+
" init Create or refresh the repository index",
|
|
20
|
+
" index Refresh the repository index",
|
|
21
|
+
" status Show cache freshness and readiness",
|
|
22
|
+
" doctor Diagnose repository and cache setup",
|
|
23
|
+
" preview <task> Build a strict-budget context preview",
|
|
24
|
+
" mcp Start the stdio MCP server",
|
|
25
|
+
].join("\n");
|
|
26
|
+
}
|
|
27
|
+
function parse(argv) {
|
|
28
|
+
const result = {
|
|
29
|
+
positional: [],
|
|
30
|
+
json: false,
|
|
31
|
+
explain: false,
|
|
32
|
+
verbose: false,
|
|
33
|
+
version: false,
|
|
34
|
+
};
|
|
35
|
+
for (let index = 0; index < argv.length; index++) {
|
|
36
|
+
const value = argv[index];
|
|
37
|
+
if (!result.command && !value.startsWith("-")) {
|
|
38
|
+
result.command = value;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (value === "--repo") {
|
|
42
|
+
result.repository = argv[++index];
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (value === "--budget") {
|
|
46
|
+
const raw = argv[++index];
|
|
47
|
+
const budget = Number(raw);
|
|
48
|
+
if (!Number.isInteger(budget) || budget <= 0)
|
|
49
|
+
throw new WorkflowError("INVALID_ARGUMENT", `Invalid --budget value: ${raw ?? "missing"}`, "Pass a positive integer token budget.");
|
|
50
|
+
result.budget = budget;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (value === "--json") {
|
|
54
|
+
result.json = true;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (value === "--explain") {
|
|
58
|
+
result.explain = true;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (value === "--verbose") {
|
|
62
|
+
result.verbose = true;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (value === "--version") {
|
|
66
|
+
result.version = true;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (value === "--help" || value === "-h") {
|
|
70
|
+
result.command = "help";
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (value.startsWith("-"))
|
|
74
|
+
throw new WorkflowError("INVALID_ARGUMENT", `Unknown option: ${value}`, "Run context-slice --help to see supported options.");
|
|
75
|
+
result.positional.push(value);
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
function plural(count, word) {
|
|
80
|
+
return `${count} ${word}${count === 1 ? "" : "s"}`;
|
|
81
|
+
}
|
|
82
|
+
function print(value, args, command, human) {
|
|
83
|
+
process.stdout.write(args.json
|
|
84
|
+
? `${JSON.stringify({ ok: true, command, result: value }, null, 2)}\n`
|
|
85
|
+
: `${human}\n`);
|
|
86
|
+
}
|
|
87
|
+
function renderPreview(preview, explain) {
|
|
88
|
+
const lines = [`Target: ${preview.target.qualifiedName ?? preview.target.name}`];
|
|
89
|
+
if (preview.baseline.wholeFileTokens > 0)
|
|
90
|
+
lines.push(`Saved ${Math.round(preview.baseline.reduction * 100)}% context ` +
|
|
91
|
+
`(${preview.estimatedTokens} vs ${preview.baseline.wholeFileTokens} tokens, ` +
|
|
92
|
+
`${plural(preview.baseline.files, "file")} read in full instead of sliced)`);
|
|
93
|
+
lines.push(`Context: ${preview.estimatedTokens}/${preview.budget} tokens; ${plural(preview.included.length, "item")} included`);
|
|
94
|
+
if (explain) {
|
|
95
|
+
lines.push("", "Included:", ...preview.included.map((item) => `- ${item.reason}: ${item.symbol} — ${item.explanation}`));
|
|
96
|
+
if (preview.omitted.length)
|
|
97
|
+
lines.push("Omitted:", ...preview.omitted.map((item) => `- ${item.symbol}: ${item.reason}`));
|
|
98
|
+
if (preview.unresolved.length)
|
|
99
|
+
lines.push("Unresolved calls:", ...preview.unresolved.map((call) => `- ${call.calleeName}`));
|
|
100
|
+
}
|
|
101
|
+
return `${lines.join("\n")}\n\n${preview.rendered}`;
|
|
102
|
+
}
|
|
103
|
+
async function execute(args) {
|
|
104
|
+
const command = args.command;
|
|
105
|
+
if (args.version)
|
|
106
|
+
return print(packageInfo.version, args, "version", packageInfo.version);
|
|
107
|
+
if (!command || command === "help")
|
|
108
|
+
return print({ usage: usage() }, args, "help", usage());
|
|
109
|
+
if (!["init", "index", "status", "doctor", "preview", "mcp"].includes(command))
|
|
110
|
+
throw new WorkflowError("INVALID_ARGUMENT", `Unknown command: ${command}`, usage());
|
|
111
|
+
const repository = resolveRepositoryRoot({
|
|
112
|
+
cwd: process.cwd(),
|
|
113
|
+
repository: args.repository,
|
|
114
|
+
});
|
|
115
|
+
if (command === "mcp") {
|
|
116
|
+
const { startMcpServer } = await import("./server/mcp-server.js");
|
|
117
|
+
await startMcpServer(repository);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const index = new ProjectIndex(repository);
|
|
121
|
+
if (command === "init" || command === "index") {
|
|
122
|
+
const refreshed = index.refresh();
|
|
123
|
+
const body = { repository, ...refreshed };
|
|
124
|
+
const human = [
|
|
125
|
+
`Repository: ${repository}`,
|
|
126
|
+
`Indexed ${plural(refreshed.summary.files, "source file")} (${plural(refreshed.summary.symbols, "symbol")}) across ${Object.entries(refreshed.summary.filesByLanguage)
|
|
127
|
+
.map(([language, count]) => `${language}: ${count}`)
|
|
128
|
+
.join(", ")}.`,
|
|
129
|
+
'Next: context-slice preview "explain <symbol>"',
|
|
130
|
+
].join("\n");
|
|
131
|
+
return print(body, args, command, human);
|
|
132
|
+
}
|
|
133
|
+
if (command === "status") {
|
|
134
|
+
const freshness = index.inspect();
|
|
135
|
+
const body = {
|
|
136
|
+
repository,
|
|
137
|
+
ready: freshness.state === "CURRENT",
|
|
138
|
+
freshness,
|
|
139
|
+
};
|
|
140
|
+
const human = [
|
|
141
|
+
`Repository: ${repository}`,
|
|
142
|
+
`Index: ${freshness.state}`,
|
|
143
|
+
`Source files: ${freshness.indexedFiles}/${freshness.sourceFiles}`,
|
|
144
|
+
...Object.entries(freshness.filesByExtension)
|
|
145
|
+
.sort()
|
|
146
|
+
.map(([extension, count]) => ` ${extension}: ${count}`),
|
|
147
|
+
`Schema: ${freshness.schemaVersion}`,
|
|
148
|
+
`Last refresh: ${freshness.lastRefreshedAt ?? "never"}`,
|
|
149
|
+
].join("\n");
|
|
150
|
+
return print(body, args, command, human);
|
|
151
|
+
}
|
|
152
|
+
if (command === "doctor") {
|
|
153
|
+
const freshness = index.inspect();
|
|
154
|
+
const checks = [
|
|
155
|
+
{ name: "repository", status: "ok", detail: repository },
|
|
156
|
+
{
|
|
157
|
+
name: "source",
|
|
158
|
+
status: "ok",
|
|
159
|
+
detail: `${plural(freshness.sourceFiles, "file")} found`,
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: "index",
|
|
163
|
+
status: freshness.state === "CURRENT" ? "ok" : "action",
|
|
164
|
+
detail: freshness.state === "CURRENT" ? "ready" : "Run context-slice index",
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
name: "mcp",
|
|
168
|
+
status: "ok",
|
|
169
|
+
detail: "Configure command: context-slice mcp",
|
|
170
|
+
},
|
|
171
|
+
];
|
|
172
|
+
const body = { repository, freshness, checks };
|
|
173
|
+
const human = checks
|
|
174
|
+
.map((check) => `${check.status === "ok" ? "OK" : "ACTION"} ${check.name}: ${check.detail}`)
|
|
175
|
+
.join("\n");
|
|
176
|
+
return print(body, args, command, human);
|
|
177
|
+
}
|
|
178
|
+
const refreshed = index.refresh();
|
|
179
|
+
const preview = buildPreview(index, args.positional.join(" "), {
|
|
180
|
+
budget: args.budget,
|
|
181
|
+
});
|
|
182
|
+
const body = { repository, refresh: refreshed.freshness, ...preview };
|
|
183
|
+
return print(body, args, command, renderPreview(preview, args.explain));
|
|
184
|
+
}
|
|
185
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
186
|
+
try {
|
|
187
|
+
await execute(parse(argv));
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
if (error instanceof WorkflowError) {
|
|
191
|
+
process.stderr.write(`${error.code}: ${error.message}\n${error.remediation}\n`);
|
|
192
|
+
process.exitCode = 2;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
process.stderr.write(`INTERNAL_ERROR: ${error instanceof Error ? error.message : String(error)}\nRun context-slice doctor for repository readiness.\n`);
|
|
196
|
+
process.exitCode = 1;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
await main();
|