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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +349 -0
  3. package/dist/src/cli.js +199 -0
  4. package/dist/src/indexer/index.js +325 -0
  5. package/dist/src/languages/adapter.js +23 -0
  6. package/dist/src/languages/go/index.js +15 -0
  7. package/dist/src/languages/go/parse.js +443 -0
  8. package/dist/src/languages/go/resolve.js +313 -0
  9. package/dist/src/languages/java/enterprise/dependency-injection.js +175 -0
  10. package/dist/src/languages/java/enterprise/jpa-entity.js +89 -0
  11. package/dist/src/languages/java/enterprise/registry.js +22 -0
  12. package/dist/src/languages/java/enterprise/spring-data.js +239 -0
  13. package/dist/src/languages/java/enterprise/spring-mvc.js +183 -0
  14. package/dist/src/languages/java/enterprise/transactions.js +110 -0
  15. package/dist/src/languages/java.js +84 -0
  16. package/dist/src/languages/javascript/index.js +25 -0
  17. package/dist/src/languages/python/index.js +29 -0
  18. package/dist/src/languages/python/parse.js +415 -0
  19. package/dist/src/languages/python/resolve.js +413 -0
  20. package/dist/src/languages/rust/calls-resolve.js +1405 -0
  21. package/dist/src/languages/rust/index.js +12 -0
  22. package/dist/src/languages/rust/parse.js +545 -0
  23. package/dist/src/languages/rust/resolve.js +284 -0
  24. package/dist/src/languages/typescript/index.js +25 -0
  25. package/dist/src/languages/typescript/parse.js +793 -0
  26. package/dist/src/languages/typescript/resolve.js +463 -0
  27. package/dist/src/package-info.js +16 -0
  28. package/dist/src/parser/java-parser.js +339 -0
  29. package/dist/src/planner/budget.js +1 -0
  30. package/dist/src/planner/composition.js +372 -0
  31. package/dist/src/planner/rank.js +10 -0
  32. package/dist/src/render/compact-context.js +11 -0
  33. package/dist/src/server/mcp-server.js +154 -0
  34. package/dist/src/storage/sqlite.js +105 -0
  35. package/dist/src/types/enterprise.js +1 -0
  36. package/dist/src/types/model.js +1 -0
  37. package/dist/src/workflow/errors.js +10 -0
  38. package/dist/src/workflow/preview.js +220 -0
  39. package/dist/src/workflow/repository.js +56 -0
  40. package/mcp.json +10 -0
  41. package/package.json +76 -0
  42. package/plugin.json +15 -0
  43. package/queries/java/annotations.scm +1 -0
  44. package/queries/java/calls.scm +1 -0
  45. package/queries/java/imports.scm +1 -0
  46. package/queries/java/symbols.scm +2 -0
  47. 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).
@@ -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();