{"_id":"@ajdev0/token-shrink","_rev":"5-fb2695070e81870a25825c82511e2660","name":"@ajdev0/token-shrink","dist-tags":{"latest":"2.0.4"},"versions":{"2.0.0":{"name":"@ajdev0/token-shrink","version":"2.0.0","keywords":["token-reduction","tree-sitter","mcp","llm","context-engineering","ast"],"license":"MIT","_id":"@ajdev0/token-shrink@2.0.0","maintainers":[{"name":"ajdev0","email":"abrarnourain7@gmail.com"}],"bin":{"token-shrink":"dist/cli.cjs","token-shrink-mcp":"dist/mcp.cjs"},"dist":{"shasum":"033026cd6c95cc5f92d31b35f87180a9aee49bd6","tarball":"https://registry.npmjs.org/@ajdev0/token-shrink/-/token-shrink-2.0.0.tgz","fileCount":34,"integrity":"sha512-ysZMkqf8cR/v7dK7swQ5dx9Q1t1PVA2wA+8ZnJ5UhL2UzNeHBLAOE0GOPaEJASsM4K21FcyRxCaiOsTIWdfy5w==","signatures":[{"sig":"MEUCIFp6ZmaGq/9x5lZo8wezrvyQzjET5BF9rk5QegxBWtdsAiEAvt+GJrd9/NoUTYN5evraqIt+1HSj9sVVvvA7fhs+kGo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":3244533},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"89ca9908a8ca1033333c8f61dc75bc38d7b375f2","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","start":"node dist/cli.js","typecheck":"tsc --noEmit","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"ajdev0","email":"abrarnourain7@gmail.com"},"_npmVersion":"11.17.0","description":"Polyglot AST Semantic Proxy & MCP Server — local-first, framework-aware token reduction engine","directories":{},"_nodeVersion":"26.5.0","dependencies":{"zod":"^3.24.1","fastify":"^5.2.1","chokidar":"^4.0.3","picocolors":"^1.1.1","web-tree-sitter":"^0.24.6","@modelcontextprotocol/sdk":"^1.12.0"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^3.0.2","typescript":"^5.7.3","@types/node":"^22.10.7"},"_npmOperationalInternal":{"tmp":"tmp/token-shrink_2.0.0_1787950192949_0.2832867359205111","host":"s3://npm-registry-packages-npm-production"}},"2.0.1":{"name":"@ajdev0/token-shrink","version":"2.0.1","keywords":["token-reduction","tree-sitter","mcp","llm","context-engineering","ast"],"license":"MIT","_id":"@ajdev0/token-shrink@2.0.1","maintainers":[{"name":"ajdev0","email":"abrarnourain7@gmail.com"}],"bin":{"token-shrink":"dist/cli.cjs","token-shrink-mcp":"dist/mcp.cjs"},"dist":{"shasum":"77cb788014526c621d119d7a56c6671226d531fe","tarball":"https://registry.npmjs.org/@ajdev0/token-shrink/-/token-shrink-2.0.1.tgz","fileCount":34,"integrity":"sha512-3LQuuJNjDK0uUn3wgB8h/YyWwnHvTtmtt19dPwxVunK1SEMihb2PBMn7THbBzNpw3Ty8aOgmv5nXZxdsKkN5zg==","signatures":[{"sig":"MEQCICFAK75NzuqVHe36tgtmRzNtrKG8lYYmbHmf+wfH/eeSAiBPyBfU175CQF0nq91ySd+dwjD+ZckRxDLHGtvB1ykMCw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":3251295},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"6e7edac21e207f6e7624b088d052b8efb11c479b","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","start":"node dist/cli.js","typecheck":"tsc --noEmit","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"ajdev0","email":"abrarnourain7@gmail.com"},"_npmVersion":"11.17.0","description":"Polyglot AST Semantic Proxy & MCP Server — local-first, framework-aware token reduction engine","directories":{},"_nodeVersion":"26.5.0","dependencies":{"zod":"^3.24.1","fastify":"^5.2.1","chokidar":"^4.0.3","picocolors":"^1.1.1","web-tree-sitter":"^0.24.6","@modelcontextprotocol/sdk":"^1.12.0"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^3.0.2","typescript":"^5.7.3","@types/node":"^22.10.7"},"_npmOperationalInternal":{"tmp":"tmp/token-shrink_2.0.1_1787960194672_0.7475969394434374","host":"s3://npm-registry-packages-npm-production"}},"2.0.2":{"name":"@ajdev0/token-shrink","version":"2.0.2","keywords":["token-reduction","tree-sitter","mcp","llm","context-engineering","ast"],"license":"MIT","_id":"@ajdev0/token-shrink@2.0.2","maintainers":[{"name":"ajdev0","email":"abrarnourain7@gmail.com"}],"bin":{"token-shrink":"dist/cli.cjs","token-shrink-mcp":"dist/mcp.cjs"},"dist":{"shasum":"e3d263923f5ac2311cf1259bf3bd70c8de1ce2c0","tarball":"https://registry.npmjs.org/@ajdev0/token-shrink/-/token-shrink-2.0.2.tgz","fileCount":34,"integrity":"sha512-pmDM+/0mo3LO8uQ8hlMAPwiTY5Js9C02MUu8TvOIpd/GPSHZDnr7dlMefGvACyENu/1ryQiBf30eKxw/QfpuRw==","signatures":[{"sig":"MEYCIQDRgpTBV6emtfvfV5JKSIC9HWugRybv+Y9za7KN9cCjOgIhAOrWM2RRuVTVbmGIveYI9uyzMrMjJzXOVrxn0DRoJFGi","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":3257468},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"abcc2cc5a5ae329b510aa6fba372314c36f7f370","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","start":"node dist/cli.js","typecheck":"tsc --noEmit","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"ajdev0","email":"abrarnourain7@gmail.com"},"_npmVersion":"11.17.0","description":"Polyglot AST Semantic Proxy & MCP Server — local-first, framework-aware token reduction engine","directories":{},"_nodeVersion":"26.5.0","dependencies":{"zod":"^3.24.1","fastify":"^5.2.1","chokidar":"^4.0.3","picocolors":"^1.1.1","web-tree-sitter":"^0.24.6","@ajdev0/token-shrink":"^2.0.1","@modelcontextprotocol/sdk":"^1.12.0"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^3.0.2","typescript":"^5.7.3","@types/node":"^22.10.7"},"_npmOperationalInternal":{"tmp":"tmp/token-shrink_2.0.2_1787961472034_0.31244081886025565","host":"s3://npm-registry-packages-npm-production"}},"2.0.3":{"name":"@ajdev0/token-shrink","version":"2.0.3","keywords":["token-reduction","tree-sitter","mcp","llm","context-engineering","ast"],"license":"MIT","_id":"@ajdev0/token-shrink@2.0.3","maintainers":[{"name":"ajdev0","email":"abrarnourain7@gmail.com"}],"bin":{"token-shrink":"dist/cli.cjs","token-shrink-mcp":"dist/mcp.cjs"},"dist":{"shasum":"8ad4d320732972faaa594cb7e254d6ea4684f8a7","tarball":"https://registry.npmjs.org/@ajdev0/token-shrink/-/token-shrink-2.0.3.tgz","fileCount":37,"integrity":"sha512-10t2y6kbIInkuqG0sc+f/ZlvRWL1de8K1KBa3C164B6q6ddFr3MiWYn706c0whRSAJKpzIw5HBd5Gc1iyq1dBQ==","signatures":[{"sig":"MEQCIB34Bxyj/wPSex0KK+sf9IIXNNPZ//qfSTf+njzl7h9uAiAGzXRWqK7CkylYtWZ5/O7PiaomP/TjglrZl37SkfYs8Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":3690446},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"2caa5b1300a0a6dbe5149e6c5b010d7a10e39723","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","start":"node dist/cli.js","typecheck":"tsc --noEmit","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"ajdev0","email":"abrarnourain7@gmail.com"},"_npmVersion":"11.17.0","description":"Polyglot AST Semantic Proxy & MCP Server — local-first, framework-aware token reduction engine","directories":{},"_nodeVersion":"26.5.0","dependencies":{"zod":"^3.24.1","fastify":"^5.2.1","chokidar":"^4.0.3","picocolors":"^1.1.1","web-tree-sitter":"^0.24.6","@ajdev0/token-shrink":"^2.0.1","@modelcontextprotocol/sdk":"^1.12.0"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^3.0.2","typescript":"^5.7.3","@types/node":"^22.10.7"},"_npmOperationalInternal":{"tmp":"tmp/token-shrink_2.0.3_1788697674818_0.8226282302156722","host":"s3://npm-registry-packages-npm-production"}},"2.0.4":{"name":"@ajdev0/token-shrink","version":"2.0.4","description":"Polyglot AST Semantic Proxy & MCP Server — local-first, framework-aware token reduction engine","type":"module","license":"MIT","keywords":["token-reduction","tree-sitter","mcp","llm","context-engineering","ast"],"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","require":"./dist/index.cjs","types":"./dist/index.d.ts"}},"bin":{"token-shrink":"dist/cli.cjs","token-shrink-mcp":"dist/mcp.cjs"},"engines":{"node":">=18"},"scripts":{"build":"tsup","dev":"tsup --watch","typecheck":"tsc --noEmit","test":"vitest run","start":"node dist/cli.js","prepublishOnly":"npm run build && npm test"},"dependencies":{"@ajdev0/token-shrink":"^2.0.1","@modelcontextprotocol/sdk":"^1.12.0","chokidar":"^4.0.3","fastify":"^5.2.1","picocolors":"^1.1.1","web-tree-sitter":"^0.24.6","zod":"^3.24.1"},"devDependencies":{"@types/node":"^22.10.7","tsup":"^8.3.5","typescript":"^5.7.3","vitest":"^3.0.2"},"gitHead":"84cf9cee52a694cd7fabf472de92284e9b7d0ae4","_id":"@ajdev0/token-shrink@2.0.4","_nodeVersion":"26.5.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-SPxki/easz36DqfrBSc/rwwOK4C3i64QZ3W+ApJm9IqtjBs1tx6t/B/dTt0UwStzA3+xGlWKyZUEgGLiGjIDQw==","shasum":"b12a15cc92ba7af1eb118aa7b91542a7b3106c14","tarball":"https://registry.npmjs.org/@ajdev0/token-shrink/-/token-shrink-2.0.4.tgz","fileCount":37,"unpackedSize":3699346,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDt9jyggoMljaVseghh4QamCJYKZ2CaaRULFIIWjmDVNwIgJ7X/UH+fHCzOX4TTIx6ddJAZ0TmQw3/l5dZO97KJaag="}]},"_npmUser":{"name":"ajdev0","email":"abrarnourain7@gmail.com"},"directories":{},"maintainers":[{"name":"ajdev0","email":"abrarnourain7@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/token-shrink_2.0.4_1788959936587_0.034221135261699454"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-28T20:49:52.689Z","modified":"2026-09-09T13:18:56.918Z","2.0.0":"2026-08-28T20:49:53.106Z","2.0.1":"2026-08-28T23:36:34.838Z","2.0.2":"2026-08-28T23:57:52.165Z","2.0.3":"2026-09-06T12:27:54.948Z","2.0.4":"2026-09-09T13:18:56.735Z"},"license":"MIT","keywords":["token-reduction","tree-sitter","mcp","llm","context-engineering","ast"],"description":"Polyglot AST Semantic Proxy & MCP Server — local-first, framework-aware token reduction engine","maintainers":[{"name":"ajdev0","email":"abrarnourain7@gmail.com"}],"readme":"# token-shrink\n\nA **local-first, framework-aware token reduction engine** — a polyglot AST semantic proxy and MCP server. It prunes implementation bodies out of **dependency files** while preserving every type signature, interface, and module export, so LLM agents see the full shape of the code at a fraction of the tokens.\n\n> **The 80–90% reduction target**: full type information, no implementation noise. Ring 0 (your active file) stays complete; Ring 1 (its direct imports) is delivered as pruned skeletons.\n\n---\n\n## How it works\n\n```\n       active file                       imports (Ring 1)\n  ┌──────────────────┐       ┌──────────────────────┐\n  │  src/page.ts     │  ──►  │  src/util.ts         │\n  └──────────────────┘       └──────────────────────┘\n          ▾                              ▾\n  tree-sitter (WASM) ─────────────► prune impl blocks\n       parse & query               keep interfaces · types ·\n                                   signatures · exports\n                                          ▾\n                                  pruned skeleton (Ring 0 full source)\n                                          ▾\n                          Compressed Code Context (Markdown)\n                                  │               │\n                            via MCP (stdio)   via HTTP (Fastify)\n                      get_compressed_code_context  POST /v1/context\n```\n\nPipeline stages:\n\n1. **Parse** — `web-tree-sitter` loads a `.wasm` grammar per language (auto-downloaded on first run).\n2. **Prune** — an S-expression query matches implementation blocks (`statement_block`, `block`, `compound_statement`…), which are replaced with a short token (`/* ... */`, or `pass` for Python) using **descending-order splicing** so offsets stay valid.\n3. **Watch** — `chokidar` watches the repo, `sha1`-hashes file contents, and refreshes the cache only on change.\n4. **Assemble** — the active file's imports are resolved and merged into a Markdown context payload (Ring 0 + Ring 1).\n\n---\n\n\n\n## Install\n\nRequires **Node.js 18+**.\n\n```bash\n# run anywhere without installing\n#   --root   project root   --port   http port   --host   bind address\nnpx @ajdev0/token-shrink --root /path/to/project\n\n# or install locally\nnpm install @ajdev0/token-shrink\n```\n\n\n\n### Build from source\n\n```bash\n# install deps\nnpm install\n\n# compile (tsup -> dist/), typecheck, and run tests\nnpm run build\nnpm run typecheck\nnpm test\n```\n\nThe build produces three binaries:\n\n\n| Binary               | Entry           | Purpose                                      |\n| -------------------- | --------------- | -------------------------------------------- |\n| `@ajdev0/token-shrink`       | `dist/cli.cjs`   | Fastify HTTP server (`POST /v1/context`)     |\n| `@ajdev0/token-shrink-mcp`   | `dist/mcp.cjs`   | MCP stdio server for AI agents               |\n| library              | `dist/index.js` | `prune()`, `assemble()`, `createWatcher()` … |\n\n\n---\n\n## WASM grammars (auto-download)\n\nGrammars are fetched from the official tree-sitter GitHub releases on **first use** and cached in `wasm/`:\n\n```text\nwasm/\n├── tree-sitter-typescript.wasm\n├── tree-sitter-javascript.wasm\n├── tree-sitter-tsx.wasm\n├── tree-sitter-python.wasm\n├── tree-sitter-go.wasm\n├── ...\n```\n\n- First run requires network access; afterwards everything is offline and fast.\n- Files are written atomically (`*.tmp` → rename) with an in-flight lock, so concurrent first-run parses never corrupt the cache.\n\n---\n\n\n\n## Usage\n\n\n\n### 1. MCP server (AI agents — Cursor, Claude, Cline, etc.)\n\nRun the stdio MCP server and expose the `get_compressed_code_context` tool:\n\n```bash\n# point it at your project\ntoken-shrink-mcp --root /path/to/project\n\n# root also works via env or cwd\nROOT=/path/to/project token-shrink-mcp\ncd /path/to/project && token-shrink-mcp\n```\n\n> **Zero-config auto-detect**: `--root` is optional. When neither `--root` nor\n> `ROOT` is set, the server finds the project itself — it walks up for VCS\n> directories (`.git`/`.hg`/`.svn`) or project manifests (`package.json`,\n> `pyproject.toml`, `go.mod`, `Cargo.toml`, …), first around the directory the\n> client launched it from, and otherwise lazily from the `activeFilePath` of the\n> first `get_compressed_code_context` call (re-pointing if a later call opens a\n> different project). The config examples below keep `--root` so the behavior is\n> pinned and the index is already warm before the first request — but you may\n> simply drop the `--root` argument entirely.\n\n**Cursor MCP config** (`.cursor/mcp.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"token-shrink\": {\n      \"command\": \"token-shrink-mcp\",\n      \"args\": [\"--root\", \"/absolute/path/to/your/project\"]\n    }\n  }\n}\n```\n\n**Claude Code MCP config** — add it to the project's `.mcp.json`, or register with the Claude CLI:\n\n```bash\n# register the server for this project\nclaude mcp add token-shrink -- token-shrink-mcp --root /path/to/project\n# persistent flag: -- transport stdio\nclaude mcp add token-shrink --transport stdio -- token-shrink-mcp --root /path/to/project\n```\n\nor place in `.claude/settings.json` / project `.mcp.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"token-shrink\": {\n      \"command\": \"token-shrink-mcp\",\n      \"args\": [\"--root\", \"/path/to/project\"]\n    }\n  }\n}\n```\n\n**Cline MCP config** — add it to the project's `.mcp.json` (or `mcp.json` in the `.cline` settings directory), or add the server via the Cline UI (**MCP Servers → Configure MCP Servers**):\n\n```json\n{\n  \"mcpServers\": {\n    \"token-shrink\": {\n      \"command\": \"token-shrink-mcp\",\n      \"args\": [\"--root\", \"/path/to/project\"]\n    }\n  }\n}\n```\n\n**Auto rule**: by default the server writes agent integration rules so the tool is used automatically on every prompt:\n\n- **Cursor**: `.cursor/rules/token-shrink.mdc`\n- **Claude Code**: `.claude/rules/token-shrink.md`\n- **Cline**: `.clinerules/token-shrink.md` (Cline's `.clinerules/` directory — every `.md`/`.txt` file there is loaded on every task)\n\nAll are sentinel-tagged and never rewrite a user-authored file at the same path. Repeated starts are no-ops. Choose the target(s) with `--rule-target=cursor|claude|cline|all` (default `all`, comma-separated values allowed):\n\n```bash\n# only Claude Code\ntoken-shrink-mcp --root /path/to/project --rule-target=claude\n\n# Cursor + Cline, no Claude rule\ntoken-shrink-mcp --root /path/to/project --rule-target=cursor,cline\n\n# completely disable auto-rules\ntoken-shrink-mcp --root /path/to/project --no-create-rule\n```\n\nOpt out also via `--create-rule=false` or `TOKEN_SHRINK_CREATE_RULE=0`.\n\n**MCP tools**\n\n`get_compressed_code_context` — compressed context for one or more active files (Ring 0 full, Ring 1 pruned):\n\n| Argument         | Type       | Required | Description                                        |\n| ---------------- | ---------- | -------- | -------------------------------------------------- |\n| `activeFilePath` | `string`   | no*      | Single file the agent is working on                |\n| `activeFiles`    | `string[]` | no*      | Multiple Ring-0 files (combined Ring 1)            |\n| `maxSkeletons`   | `number`   | no       | Cap on Ring-1 files (default `50`, max `200`)      |\n| `maxTokens`      | `number`   | no       | Hard token budget; Ring 1 is relevance-packed to fit |\n| `includeStats`   | `boolean`  | no       | Append approximate token counts                    |\n\n\\* Provide exactly one of `activeFilePath` / `activeFiles`. Ring 0 stays full; Ring 1 is the union of each file's local imports, minus files already in Ring 0.\n\n`expand_symbol({ filePath, symbolName, maxMatches? })` — when a skeleton isn't enough, returns the **full, un-pruned definition** (function/class/method/… bodies included) for the named symbol in that file. Overloads and same-named members are all returned.\n\n`git_diff_context({ scope?, base?, head?, includeUntracked?, maxFiles?, maxImporters?, maxSkeletons?, maxTokens? })` — impact analysis for changed code:\n- `scope`: `worktree` (default) · `staged` · `branch` (`base...head`, defaults `HEAD~1...HEAD`)\n- changed files are emitted in full as Ring 0; their imports **and** the files that import them (file-level callers) are attached as pruned skeletons. Great for PR reviews and multi-file regressions where there is no single active file.\n\n`search_symbol_signatures({ query, maxResults?, kind? })` — repo-wide lookup of definitions backed by an in-memory index of the tree-sitter symbol pass. Returns compact `` `file:line — signature` `` lines (not raw file dumps), ranked exact → prefix → substring.\n\n**Project config (`.tokenshrinkrc.json`)** at the repository root — hot-reloaded:\n\n```json\n{\n  \"ignorePatterns\": [\"**/dist/**\", \"**/generated/**\"],\n  \"keepUnpruned\": [\"src/types/global.d.ts\", \"lib/models/*.dart\"],\n  \"preserveAnnotations\": [\"@keepContext\", \"@api\"]\n}\n```\n\n- `ignorePatterns` — globs that are never indexed or watched.\n- `keepUnpruned` — files that are indexed but never pruned (always full text).\n- `preserveAnnotations` — definitions (and everything nested in them) preceded by `@marker` are kept fully un-pruned.\n\nInvalid JSON logs a warning and falls back to defaults; editing the file while the server runs re-indexes automatically.\n\n### 2. HTTP server (Fastify)\n\n```bash\ntoken-shrink --root /path/to/project --port 3000 --max-tokens 4000\n# env equivalents: ROOT=… PORT=… HOST=…\n```\n\n| Route         | Method | Body                                                   | Returns                          |\n| ------------- | ------ | ------------------------------------------------------ | -------------------------------- |\n| `/health`     | `GET`  | —                                                      | status, root, indexed file count |\n| `/v1/context` | `POST` | `{ activeFilePath? \\| activeFiles?, maxSkeletons?, maxTokens?, includeStats? }` | assembled Markdown + deps + stats |\n\n\n```bash\ncurl -s http://localhost:3000/health\n# {\"status\":\"ok\",\"service\":\"token-shrink\",\"version\":\"2.0.0\",\"root\":\".\",\"indexed\":182}\n\ncurl -s -X POST http://localhost:3000/v1/context \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"activeFiles\":[\"./src/page.ts\",\"./src/api.ts\"],\"includeStats\":true}'\n```\n\n\n\n### 3. Library API\n\n```ts\nimport { prune, assemble, createWatcher } from 'token-shrink';\n\n// prune a single file -> skeleton (keeps signatures, strips bodies)\nconst { code, removed } = await prune('src/util.ts', sourceText);\n\n// assemble context for an active file from a warm cache\nconst { markdown } = assemble('src/page.ts', watcher.cache.entries, {\n  includeStats: true,\n});\n\n// incremental watcher\nconst watcher = createWatcher({ root: process.cwd(), ignored: ['node_modules'] });\nawait watcher.indexAll();\n```\n\n---\n\n\n\n## Supported languages\n\nS-expression queries match implementation blocks; interfaces, signatures, and exports are never touched. The `Block node` column shows the AST node that gets collapsed during pruning.\n\n\n| Language        | Extensions                              | Grammar wasm                  | Block node           |\n| --------------- | --------------------------------------- | ----------------------------- | -------------------- |\n| TypeScript      | `.ts` `.cts` `.mts`                     | `tree-sitter-typescript.wasm` | `statement_block`    |\n| JavaScript      | `.js` `.cjs` `.mjs`                     | `tree-sitter-javascript.wasm` | `statement_block`    |\n| React / Next.js | `.tsx`                                  | `tree-sitter-tsx.wasm`        | `statement_block`¹   |\n| React (JSX)     | `.jsx`                                  | `tree-sitter-javascript.wasm` | `statement_block`¹   |\n| Python          | `.py` `.pyi`                            | `tree-sitter-python.wasm`     | `block` → `pass`     |\n| Dart / Flutter  | `.dart`                                 | `tree-sitter-dart.wasm`       | `block`              |\n| Swift / SwiftUI | `.swift`                                | `tree-sitter-swift.wasm`      | `statements`         |\n| Go              | `.go`                                   | `tree-sitter-go.wasm`         | `block`              |\n| Rust            | `.rs`                                   | `tree-sitter-rust.wasm`       | `block`              |\n| Java            | `.java`                                 | `tree-sitter-java.wasm`       | `block`              |\n| Kotlin          | `.kt` `.kts`                            | `tree-sitter-kotlin.wasm`     | `block`              |\n| C               | `.c` `.h`                               | `tree-sitter-c.wasm`          | `compound_statement` |\n| C++             | `.cc` `.cpp` `.cxx` `.hpp` `.hh` `.hxx` | `tree-sitter-cpp.wasm`        | `compound_statement` |\n| PHP             | `.php`                                  | `tree-sitter-php.wasm`        | `compound_statement` |\n\n\n> ¹ TSX/JSX also preserve `'use client'` / `'use server'` directive lines inside otherwise-pruned bodies (framework-aware).\n\nLanguage IDs: `typescript · javascript · tsx · jsx · python · dart · swift · go · rust · java · kotlin · c · cpp · php`.\n\n---\n\n\n\n## Example\n\n**Input** `src/util.ts`\n\n```ts\nexport interface User {\n  id: number;\n  name: string;\n}\nexport function buildGreeting(u: User) {\n  const parts = [u.name, u.email];\n  return parts.join(' | ');\n}\nexport const formatEmail = (u: User) => {\n  return u.email.toLowerCase().trim();\n};\n```\n\n**Pruned skeleton (Ring 1)** — signatures and the interface intact, bodies collapsed:\n\n```ts\nexport interface User {\n  id: number;\n  name: string;\n}\nexport function buildGreeting(u: User) /* ... */\nexport const formatEmail = (u: User) => /* ... */;\n```\n\n---\n\n\n\n## Design notes\n\n- **Bottom-up splicing** — ranges are sorted by start index descending and replaced in place, so earlier offsets never shift and the output stays a valid, parseable file.\n- **Regex-based import extraction** — resilient across languages; resolves relative imports (`./x`, `../y`), aliases (`@/`, `~`), and skips bare package specifiers.\n- **Incremental hashing** — files are re-pruned only when their `sha1` hash changes; the watcher is debounced (100 ms) and zero-CPU while idle.\n- **Ram-safe watchers** — sockets / non-regular files are never opened with `fs.watch`, so stray unix sockets in the tree can't crash the server.\n\n---\n\n\n\n## Project layout\n\n```\ntoken-shrink/\n├── package.json / tsconfig.json / tsup.config.ts / vitest.config.ts\n├── src/\n│   ├── index.ts             # library entry (exports)\n│   ├── cli.ts               # Fastify HTTP server\n│   ├── mcp.ts               # MCP stdio server\n│   ├── parser/\n│   │   ├── registry.ts      # extension → language spec + S-queries\n│   │   ├── wasm.ts          # auto-download + cache of .wasm files\n│   │   └── pruner.ts        # prune(filePath, source) → skeleton\n│   ├── watcher/\n│   │   └── sync.ts          # chokidar watch + hash cache + import graph\n│   └── server/\n│       └── assembler.ts     # Ring 0 + Ring 1 Markdown payload\n├── tests/                   # pruning integrity + token-reduction tests\n└── wasm/                    # auto-downloaded grammars (gitignored)\n```\n\n\n\n## License\n\nMIT","readmeFilename":"README.md"}