@ajdev0/token-shrink 2.0.2 → 2.0.3
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/README.md +52 -14
- package/dist/chunk-FYCLLG7F.js +847 -0
- package/dist/chunk-FYCLLG7F.js.map +1 -0
- package/dist/{chunk-KN4YKUZM.js → chunk-RI64PBQ5.js} +17 -7
- package/dist/chunk-RI64PBQ5.js.map +1 -0
- package/dist/chunk-RLHEIKPR.js +894 -0
- package/dist/chunk-RLHEIKPR.js.map +1 -0
- package/dist/cli-DzY7l7Rr.d.cts +182 -0
- package/dist/cli-DzY7l7Rr.d.ts +182 -0
- package/dist/cli.cjs +391 -119
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +2 -1
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +2 -2
- package/dist/index.cjs +1189 -152
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +160 -20
- package/dist/index.d.ts +160 -20
- package/dist/index.js +37 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp.cjs +1126 -136
- package/dist/mcp.cjs.map +1 -1
- package/dist/mcp.d.cts +21 -6
- package/dist/mcp.d.ts +21 -6
- package/dist/mcp.js +6 -2
- package/hooks/claude/README.md +29 -0
- package/hooks/claude/git-impact.mjs +47 -0
- package/hooks/claude/settings.example.json +24 -0
- package/package.json +2 -1
- package/dist/chunk-HBYIAFR4.js +0 -204
- package/dist/chunk-HBYIAFR4.js.map +0 -1
- package/dist/chunk-HRF3BIOV.js +0 -518
- package/dist/chunk-HRF3BIOV.js.map +0 -1
- package/dist/chunk-KN4YKUZM.js.map +0 -1
- package/dist/cli-EpVqinpB.d.cts +0 -96
- package/dist/cli-EpVqinpB.d.ts +0 -96
package/dist/mcp.d.ts
CHANGED
|
@@ -8,6 +8,11 @@ import { Matcher } from 'chokidar';
|
|
|
8
8
|
*
|
|
9
9
|
* It maintains its own graph cache by watching the repository root derived
|
|
10
10
|
* from the active file, so Cursor/Claude/Cline agents can request pruned context.
|
|
11
|
+
*
|
|
12
|
+
* Root resolution: `--root` (or `ROOT`) pins the project; without one the
|
|
13
|
+
* server auto-detects it from project markers (`.git`, manifests) — first
|
|
14
|
+
* around the process cwd, and lazily from `activeFilePath` once a tool call
|
|
15
|
+
* arrives.
|
|
11
16
|
*/
|
|
12
17
|
|
|
13
18
|
/** Supported auto-rule integration targets. */
|
|
@@ -17,6 +22,10 @@ declare const RULE_TARGET_PATH: {
|
|
|
17
22
|
readonly claude: ".claude/rules/token-shrink.md";
|
|
18
23
|
readonly cline: ".clinerules/token-shrink.md";
|
|
19
24
|
};
|
|
25
|
+
/** Bump when the generated rule bodies gain new tool guidance. */
|
|
26
|
+
declare const RULE_VERSION = 3;
|
|
27
|
+
/** Marker line inside generated rules; its presence means "current version". */
|
|
28
|
+
declare const RULE_VERSION_MARKER = "# token-shrink rule v3";
|
|
20
29
|
declare const AUTO_RULE_SENTINEL = "# auto-generated by token-shrink";
|
|
21
30
|
declare const CURSOR_RULE_PATH: ".cursor/rules/token-shrink.mdc";
|
|
22
31
|
/** Back-compat alias for the previous single-target constant. */
|
|
@@ -26,7 +35,12 @@ declare const CLAUDE_RULE_SENTINEL = "# auto-generated by token-shrink (claude)"
|
|
|
26
35
|
declare const CLINE_RULE_PATH: ".clinerules/token-shrink.md";
|
|
27
36
|
declare const CLINE_RULE_SENTINEL = "# auto-generated by token-shrink (cline)";
|
|
28
37
|
interface McpServerOptions {
|
|
29
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* Project root to watch. When omitted (and the `ROOT` env var is unset) the
|
|
40
|
+
* server runs in auto-detect mode: it locates the project from markers
|
|
41
|
+
* around the process cwd if possible, otherwise lazily from the first tool
|
|
42
|
+
* call's `activeFilePath`.
|
|
43
|
+
*/
|
|
30
44
|
root?: string;
|
|
31
45
|
/** Extra ignore globs for the watcher. */
|
|
32
46
|
ignored?: Matcher[];
|
|
@@ -44,10 +58,11 @@ interface RuleWriteResult {
|
|
|
44
58
|
filePath: string;
|
|
45
59
|
}
|
|
46
60
|
/**
|
|
47
|
-
* Write a rules file for a given agent target. Idempotent:
|
|
48
|
-
* - absent
|
|
49
|
-
* - has our sentinel -> skip (already ours);
|
|
50
|
-
* -
|
|
61
|
+
* Write a rules file for a given agent target. Idempotent and version-aware:
|
|
62
|
+
* - absent -> create it (tagged with our sentinel + version marker);
|
|
63
|
+
* - has our sentinel AND current version marker -> skip (already ours);
|
|
64
|
+
* - has our sentinel but predates the version marker -> rewrite (upgrade);
|
|
65
|
+
* - present without our sentinel -> leave the user's file untouched.
|
|
51
66
|
*/
|
|
52
67
|
declare function createAutoRule(root: string, target: RuleTarget): RuleWriteResult;
|
|
53
68
|
/** Back-compat alias for code that imported the old Cursor-only helper. */
|
|
@@ -58,4 +73,4 @@ declare function createCursorRule(root: string): RuleWriteResult;
|
|
|
58
73
|
*/
|
|
59
74
|
declare function startMcpServer(opts?: McpServerOptions): Promise<McpServer>;
|
|
60
75
|
|
|
61
|
-
export { AUTO_RULE_PATH, AUTO_RULE_SENTINEL, CLAUDE_RULE_PATH, CLAUDE_RULE_SENTINEL, CLINE_RULE_PATH, CLINE_RULE_SENTINEL, CURSOR_RULE_PATH, type McpServerOptions, RULE_TARGET_PATH, type RuleTarget, type RuleWriteResult, createAutoRule, createCursorRule, startMcpServer };
|
|
76
|
+
export { AUTO_RULE_PATH, AUTO_RULE_SENTINEL, CLAUDE_RULE_PATH, CLAUDE_RULE_SENTINEL, CLINE_RULE_PATH, CLINE_RULE_SENTINEL, CURSOR_RULE_PATH, type McpServerOptions, RULE_TARGET_PATH, RULE_VERSION, RULE_VERSION_MARKER, type RuleTarget, type RuleWriteResult, createAutoRule, createCursorRule, startMcpServer };
|
package/dist/mcp.js
CHANGED
|
@@ -8,11 +8,13 @@ import {
|
|
|
8
8
|
CLINE_RULE_SENTINEL,
|
|
9
9
|
CURSOR_RULE_PATH,
|
|
10
10
|
RULE_TARGET_PATH,
|
|
11
|
+
RULE_VERSION,
|
|
12
|
+
RULE_VERSION_MARKER,
|
|
11
13
|
createAutoRule,
|
|
12
14
|
createCursorRule,
|
|
13
15
|
startMcpServer
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
16
|
+
} from "./chunk-RLHEIKPR.js";
|
|
17
|
+
import "./chunk-FYCLLG7F.js";
|
|
16
18
|
import "./chunk-7SQ6HMWM.js";
|
|
17
19
|
export {
|
|
18
20
|
AUTO_RULE_PATH,
|
|
@@ -23,6 +25,8 @@ export {
|
|
|
23
25
|
CLINE_RULE_SENTINEL,
|
|
24
26
|
CURSOR_RULE_PATH,
|
|
25
27
|
RULE_TARGET_PATH,
|
|
28
|
+
RULE_VERSION,
|
|
29
|
+
RULE_VERSION_MARKER,
|
|
26
30
|
createAutoRule,
|
|
27
31
|
createCursorRule,
|
|
28
32
|
startMcpServer
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Claude Code hooks (optional auto-context)
|
|
2
|
+
|
|
3
|
+
Copy these into your project and merge `settings.example.json` into
|
|
4
|
+
`.claude/settings.json`:
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
mkdir -p .claude/hooks
|
|
8
|
+
cp hooks/claude/git-impact.mjs .claude/hooks/
|
|
9
|
+
cp hooks/claude/settings.example.json .claude/settings.example.json
|
|
10
|
+
# then merge the "hooks" block from settings.example.json into your
|
|
11
|
+
# .claude/settings.json (project- or user-level)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
What it does:
|
|
15
|
+
|
|
16
|
+
- `SessionStart` + `Stop` hooks run `git-impact.mjs`, which prints a compact
|
|
17
|
+
**git-impact summary** (changed files, unstaged/staged diff stat) whenever the
|
|
18
|
+
working tree is dirty.
|
|
19
|
+
- Claude Code surfaces hook stdout in the session, so diff context is injected
|
|
20
|
+
automatically — no prompt engineering required.
|
|
21
|
+
|
|
22
|
+
Notes & limits:
|
|
23
|
+
|
|
24
|
+
- This is the lightweight, always-on complement to the heavier
|
|
25
|
+
`git_diff_context` MCP tool (full changed-file code + pruned caller skeletons),
|
|
26
|
+
which the auto-workflow rule triggers for multi-file/review work.
|
|
27
|
+
- Hook stdout is cheap: one or two `git` calls per event.
|
|
28
|
+
- If your tree is clean the hook prints nothing useful and exits quietly.
|
|
29
|
+
- The hook runs `git` in `CLAUDE_PROJECT_DIR` (or `$1` when invoked manually).
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Compact git-impact summary for Claude Code hooks.
|
|
4
|
+
*
|
|
5
|
+
* Prints changed files + diff stat when the working tree is dirty; prints
|
|
6
|
+
* nothing useful (and exits 0) when clean. Intended to be cheap: two git
|
|
7
|
+
* calls. Pair with the `git_diff_context` MCP tool for the heavy payload.
|
|
8
|
+
*/
|
|
9
|
+
import { execFileSync } from 'node:child_process';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import fs from 'node:fs';
|
|
12
|
+
|
|
13
|
+
const root = path.resolve(process.argv[2] || process.env.CLAUDE_PROJECT_DIR || process.cwd());
|
|
14
|
+
|
|
15
|
+
function run(args) {
|
|
16
|
+
try {
|
|
17
|
+
return execFileSync('git', ['--no-pager', ...args], {
|
|
18
|
+
cwd: root,
|
|
19
|
+
encoding: 'utf8',
|
|
20
|
+
env: { ...process.env, LC_ALL: 'C' },
|
|
21
|
+
});
|
|
22
|
+
} catch {
|
|
23
|
+
return '';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Not a repo (or git missing) -> stay quiet so hooks never error.
|
|
28
|
+
if (!fs.existsSync(path.join(root, '.git')) && !run(['rev-parse', '--is-inside-work-tree']).trim()) {
|
|
29
|
+
process.exit(0);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const status = run(['status', '--porcelain']).split('\n').filter(Boolean).slice(0, 40);
|
|
33
|
+
if (status.length === 0) {
|
|
34
|
+
process.exit(0);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const unstaged = run(['diff', '--stat']).trim();
|
|
38
|
+
const staged = run(['diff', '--cached', '--stat']).trim();
|
|
39
|
+
|
|
40
|
+
const lines = [
|
|
41
|
+
`[token-shrink] git impact — ${status.length} changed file${status.length === 1 ? '' : 's'}:`,
|
|
42
|
+
'',
|
|
43
|
+
...status.map((s) => ` ${s}`),
|
|
44
|
+
];
|
|
45
|
+
if (unstaged) lines.push('', 'Unstaged changes:', unstaged);
|
|
46
|
+
if (staged) lines.push('', 'Staged changes:', staged);
|
|
47
|
+
console.log(lines.join('\n'));
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"hooks": [
|
|
6
|
+
{
|
|
7
|
+
"type": "command",
|
|
8
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/git-impact.mjs\""
|
|
9
|
+
}
|
|
10
|
+
]
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"Stop": [
|
|
14
|
+
{
|
|
15
|
+
"hooks": [
|
|
16
|
+
{
|
|
17
|
+
"type": "command",
|
|
18
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/git-impact.mjs\""
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ajdev0/token-shrink",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"description": "Polyglot AST Semantic Proxy & MCP Server — local-first, framework-aware token reduction engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"files": [
|
|
30
30
|
"dist",
|
|
31
31
|
"wasm",
|
|
32
|
+
"hooks",
|
|
32
33
|
"README.md"
|
|
33
34
|
],
|
|
34
35
|
"engines": {
|
package/dist/chunk-HBYIAFR4.js
DELETED
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
assemble,
|
|
4
|
-
createWatcher
|
|
5
|
-
} from "./chunk-HRF3BIOV.js";
|
|
6
|
-
|
|
7
|
-
// src/mcp.ts
|
|
8
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
10
|
-
import { z } from "zod";
|
|
11
|
-
import fs from "fs";
|
|
12
|
-
import path from "path";
|
|
13
|
-
var RULE_TARGET_PATH = {
|
|
14
|
-
cursor: ".cursor/rules/token-shrink.mdc",
|
|
15
|
-
claude: ".claude/rules/token-shrink.md",
|
|
16
|
-
cline: ".clinerules/token-shrink.md"
|
|
17
|
-
};
|
|
18
|
-
var AUTO_RULE_SENTINEL = "# auto-generated by token-shrink";
|
|
19
|
-
var CURSOR_RULE_PATH = RULE_TARGET_PATH.cursor;
|
|
20
|
-
var AUTO_RULE_PATH = CURSOR_RULE_PATH;
|
|
21
|
-
var CLAUDE_RULE_PATH = RULE_TARGET_PATH.claude;
|
|
22
|
-
var CLAUDE_RULE_SENTINEL = "# auto-generated by token-shrink (claude)";
|
|
23
|
-
var CLINE_RULE_PATH = RULE_TARGET_PATH.cline;
|
|
24
|
-
var CLINE_RULE_SENTINEL = "# auto-generated by token-shrink (cline)";
|
|
25
|
-
var ruleTargets = {
|
|
26
|
-
cursor: {
|
|
27
|
-
relPath: CURSOR_RULE_PATH,
|
|
28
|
-
sentinel: AUTO_RULE_SENTINEL,
|
|
29
|
-
body: `---
|
|
30
|
-
description: Compress dependency context with token-shrink on every task
|
|
31
|
-
globs: **/*.{ts,tsx,js,jsx,py,go,rs,dart,swift,java,kt,c,cpp,h,hpp,php}
|
|
32
|
-
alwaysApply: true
|
|
33
|
-
---
|
|
34
|
-
Before working on a file in this repo, call the \`get_compressed_code_context\` MCP tool with
|
|
35
|
-
that file's path, and use the returned Ring 0 + Ring 1 payload as the context for that file
|
|
36
|
-
and its direct imports.
|
|
37
|
-
|
|
38
|
-
${AUTO_RULE_SENTINEL}
|
|
39
|
-
`
|
|
40
|
-
},
|
|
41
|
-
claude: {
|
|
42
|
-
relPath: CLAUDE_RULE_PATH,
|
|
43
|
-
sentinel: CLAUDE_RULE_SENTINEL,
|
|
44
|
-
body: `---
|
|
45
|
-
description: Compress dependency context with token-shrink on every task
|
|
46
|
-
paths: ["**/*.{ts,tsx,js,jsx,py,go,rs,dart,swift,java,kt,c,cpp,h,hpp,php}"]
|
|
47
|
-
---
|
|
48
|
-
Before working on a file in this repo, call the \`get_compressed_code_context\` MCP tool with
|
|
49
|
-
that file's path, and use the returned Ring 0 + Ring 1 payload as the context for that file
|
|
50
|
-
and its direct imports.
|
|
51
|
-
|
|
52
|
-
${CLAUDE_RULE_SENTINEL}
|
|
53
|
-
`
|
|
54
|
-
},
|
|
55
|
-
cline: {
|
|
56
|
-
relPath: CLINE_RULE_PATH,
|
|
57
|
-
sentinel: CLINE_RULE_SENTINEL,
|
|
58
|
-
// No frontmatter -> always-on rule (Cline treats unconditional .clinerules/*.md
|
|
59
|
-
// files as universal rules, applied to every task regardless of active file).
|
|
60
|
-
body: `# token-shrink
|
|
61
|
-
|
|
62
|
-
Before working on a file in this repo, call the \`get_compressed_code_context\` MCP tool with
|
|
63
|
-
that file's path, and use the returned Ring 0 + Ring 1 payload as the context for that file
|
|
64
|
-
and its direct imports.
|
|
65
|
-
|
|
66
|
-
${CLINE_RULE_SENTINEL}
|
|
67
|
-
`
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
function createAutoRule(root, target) {
|
|
71
|
-
const spec = ruleTargets[target];
|
|
72
|
-
const rulePath = path.join(root, spec.relPath);
|
|
73
|
-
try {
|
|
74
|
-
if (fs.existsSync(rulePath)) {
|
|
75
|
-
const existing = fs.readFileSync(rulePath, "utf8");
|
|
76
|
-
if (existing.includes(spec.sentinel)) {
|
|
77
|
-
return { created: false, skipped: "exists", filePath: rulePath };
|
|
78
|
-
}
|
|
79
|
-
return { created: false, skipped: "user", filePath: rulePath };
|
|
80
|
-
}
|
|
81
|
-
fs.mkdirSync(path.dirname(rulePath), { recursive: true });
|
|
82
|
-
fs.writeFileSync(rulePath, spec.body, "utf8");
|
|
83
|
-
return { created: true, skipped: "none", filePath: rulePath };
|
|
84
|
-
} catch (err) {
|
|
85
|
-
process.stderr.write(
|
|
86
|
-
`[token-shrink] Failed to write rule for "${target}": ${err.message}
|
|
87
|
-
`
|
|
88
|
-
);
|
|
89
|
-
return { created: false, skipped: "none", filePath: rulePath };
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
function createCursorRule(root) {
|
|
93
|
-
return createAutoRule(root, "cursor");
|
|
94
|
-
}
|
|
95
|
-
function resolveTargets(ruleTarget) {
|
|
96
|
-
if (!ruleTarget) return ["cursor", "claude", "cline"];
|
|
97
|
-
const list = Array.isArray(ruleTarget) ? ruleTarget : [ruleTarget];
|
|
98
|
-
if (list.includes("all")) return ["cursor", "claude", "cline"];
|
|
99
|
-
return list;
|
|
100
|
-
}
|
|
101
|
-
async function startMcpServer(opts = {}) {
|
|
102
|
-
const root = path.resolve(opts.root ?? process.env.ROOT ?? process.cwd());
|
|
103
|
-
const log = (msg) => {
|
|
104
|
-
if (!opts.silent) process.stderr.write(`[token-shrink] ${msg}
|
|
105
|
-
`);
|
|
106
|
-
};
|
|
107
|
-
const watcher = createWatcher({ root, ignored: opts.ignored });
|
|
108
|
-
if (!opts.silent) {
|
|
109
|
-
log(`Indexing ${root} in the background\u2026`);
|
|
110
|
-
}
|
|
111
|
-
if (opts.createRule !== false) {
|
|
112
|
-
for (const target of resolveTargets(opts.ruleTarget)) {
|
|
113
|
-
const res = createAutoRule(root, target);
|
|
114
|
-
if (res.created) {
|
|
115
|
-
log(`Wrote ${target} rule to ${res.filePath}`);
|
|
116
|
-
} else if (res.skipped === "user") {
|
|
117
|
-
log(`${target} rule exists (user-authored); leaving it untouched.`);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
void watcher.indexAll().then((n) => log(`Indexed ${n} files.`));
|
|
122
|
-
const server = new McpServer(
|
|
123
|
-
{ name: "token-shrink", version: "2.0.0" },
|
|
124
|
-
{ capabilities: { tools: {} } }
|
|
125
|
-
);
|
|
126
|
-
server.registerTool(
|
|
127
|
-
"get_compressed_code_context",
|
|
128
|
-
{
|
|
129
|
-
title: "Get Compressed Code Context",
|
|
130
|
-
description: "Returns a compressed, framework-aware AST context payload for a file: the active file\u2019s full source (Ring 0) plus pruned skeletons of its direct imports (Ring 1). Implementation bodies are removed but type signatures, interfaces, and module exports are preserved for ~80-90% token reduction.",
|
|
131
|
-
inputSchema: {
|
|
132
|
-
activeFilePath: z.string().describe("Path to the file the agent is working on"),
|
|
133
|
-
maxSkeletons: z.number().int().min(1).max(200).optional().describe("Cap on number of dependency skeletons to include"),
|
|
134
|
-
includeStats: z.boolean().optional().describe("Append approximate token-count stats")
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
async ({ activeFilePath, maxSkeletons, includeStats }) => {
|
|
138
|
-
const result = assemble(activeFilePath, watcher.cache.entries, {
|
|
139
|
-
maxSkeletons,
|
|
140
|
-
includeStats
|
|
141
|
-
});
|
|
142
|
-
return {
|
|
143
|
-
content: [
|
|
144
|
-
{
|
|
145
|
-
type: "text",
|
|
146
|
-
text: result.markdown
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
type: "text",
|
|
150
|
-
text: `[stats] active=${result.activeFilePath} dependencies=${result.included.length} unresolved=${result.unresolved.length}`
|
|
151
|
-
}
|
|
152
|
-
]
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
);
|
|
156
|
-
const transport = new StdioServerTransport();
|
|
157
|
-
await server.connect(transport);
|
|
158
|
-
log("MCP server connected.");
|
|
159
|
-
return server;
|
|
160
|
-
}
|
|
161
|
-
var argv1 = process.argv[1] ? path.basename(process.argv[1]) : "";
|
|
162
|
-
var argv1Real = "";
|
|
163
|
-
try {
|
|
164
|
-
argv1Real = process.argv[1] ? path.basename(fs.realpathSync(process.argv[1])) : "";
|
|
165
|
-
} catch {
|
|
166
|
-
}
|
|
167
|
-
var invokedAsMcp = argv1 === "mcp.js" || argv1 === "mcp.mjs" || argv1 === "mcp.cjs" || argv1 === "mcp.ts" || argv1Real === "mcp.js" || argv1Real === "mcp.mjs" || argv1Real === "mcp.cjs" || argv1Real === "mcp.ts";
|
|
168
|
-
if (invokedAsMcp) {
|
|
169
|
-
const rootArg = (() => {
|
|
170
|
-
const i = process.argv.indexOf("--root");
|
|
171
|
-
if (i !== -1 && process.argv[i + 1]) return process.argv[i + 1];
|
|
172
|
-
const eq = process.argv.find((a) => a.startsWith("--root="));
|
|
173
|
-
if (eq) return eq.slice("--root=".length);
|
|
174
|
-
return void 0;
|
|
175
|
-
})();
|
|
176
|
-
const createRule = !(process.env.TOKEN_SHRINK_CREATE_RULE === "0" || process.env.TOKEN_SHRINK_CREATE_RULE === "false" || process.env.CONTEXT_SHRINK_CREATE_RULE === "0" || process.env.CONTEXT_SHRINK_CREATE_RULE === "false" || process.argv.includes("--no-create-rule") || (() => {
|
|
177
|
-
const f = process.argv.find((a) => a.startsWith("--create-rule="));
|
|
178
|
-
return f ? f.slice("--create-rule=".length) === "false" : false;
|
|
179
|
-
})());
|
|
180
|
-
const rawTargets = process.argv.filter((a) => a.startsWith("--rule-target=")).flatMap((a) => a.slice("--rule-target=".length).split(","));
|
|
181
|
-
const ruleTarget = rawTargets.length > 0 && !rawTargets.includes("all") ? [...new Set(rawTargets)].filter(
|
|
182
|
-
(v) => v === "cursor" || v === "claude" || v === "cline"
|
|
183
|
-
) : void 0;
|
|
184
|
-
void startMcpServer({ root: rootArg, createRule, ruleTarget }).catch((err) => {
|
|
185
|
-
process.stderr.write(`[token-shrink] MCP server error: ${err.message}
|
|
186
|
-
`);
|
|
187
|
-
process.exitCode = 1;
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export {
|
|
192
|
-
RULE_TARGET_PATH,
|
|
193
|
-
AUTO_RULE_SENTINEL,
|
|
194
|
-
CURSOR_RULE_PATH,
|
|
195
|
-
AUTO_RULE_PATH,
|
|
196
|
-
CLAUDE_RULE_PATH,
|
|
197
|
-
CLAUDE_RULE_SENTINEL,
|
|
198
|
-
CLINE_RULE_PATH,
|
|
199
|
-
CLINE_RULE_SENTINEL,
|
|
200
|
-
createAutoRule,
|
|
201
|
-
createCursorRule,
|
|
202
|
-
startMcpServer
|
|
203
|
-
};
|
|
204
|
-
//# sourceMappingURL=chunk-HBYIAFR4.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mcp.ts"],"sourcesContent":["/**\n * MCP stdio server. Exposes a single tool:\n *\n * get_compressed_code_context({ activeFilePath, maxSkeletons?, includeStats? })\n *\n * It maintains its own graph cache by watching the repository root derived\n * from the active file, so Cursor/Claude/Cline agents can request pruned context.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { assemble } from './server/assembler.js';\nimport { createWatcher, type Matcher } from './watcher/sync.js';\n\n/** Supported auto-rule integration targets. */\nexport type RuleTarget = 'cursor' | 'claude' | 'cline';\n\n/** Agent integration file descriptors written when the auto-rule is on. */\ninterface RuleTargetSpec {\n relPath: string;\n sentinel: string;\n body: string;\n}\n\nexport const RULE_TARGET_PATH = {\n cursor: '.cursor/rules/token-shrink.mdc',\n claude: '.claude/rules/token-shrink.md',\n cline: '.clinerules/token-shrink.md',\n} as const;\n\nexport const AUTO_RULE_SENTINEL = '# auto-generated by token-shrink';\nexport const CURSOR_RULE_PATH = RULE_TARGET_PATH.cursor;\n/** Back-compat alias for the previous single-target constant. */\nexport const AUTO_RULE_PATH = CURSOR_RULE_PATH;\nexport const CLAUDE_RULE_PATH = RULE_TARGET_PATH.claude;\nexport const CLAUDE_RULE_SENTINEL = '# auto-generated by token-shrink (claude)';\nexport const CLINE_RULE_PATH = RULE_TARGET_PATH.cline;\nexport const CLINE_RULE_SENTINEL = '# auto-generated by token-shrink (cline)';\n\nconst ruleTargets: Record<RuleTarget, RuleTargetSpec> = {\n cursor: {\n relPath: CURSOR_RULE_PATH,\n sentinel: AUTO_RULE_SENTINEL,\n body: `---\ndescription: Compress dependency context with token-shrink on every task\nglobs: **/*.{ts,tsx,js,jsx,py,go,rs,dart,swift,java,kt,c,cpp,h,hpp,php}\nalwaysApply: true\n---\nBefore working on a file in this repo, call the \\`get_compressed_code_context\\` MCP tool with\nthat file's path, and use the returned Ring 0 + Ring 1 payload as the context for that file\nand its direct imports.\n\n${AUTO_RULE_SENTINEL}\n`,\n },\n claude: {\n relPath: CLAUDE_RULE_PATH,\n sentinel: CLAUDE_RULE_SENTINEL,\n body: `---\ndescription: Compress dependency context with token-shrink on every task\npaths: [\"**/*.{ts,tsx,js,jsx,py,go,rs,dart,swift,java,kt,c,cpp,h,hpp,php}\"]\n---\nBefore working on a file in this repo, call the \\`get_compressed_code_context\\` MCP tool with\nthat file's path, and use the returned Ring 0 + Ring 1 payload as the context for that file\nand its direct imports.\n\n${CLAUDE_RULE_SENTINEL}\n`,\n },\n cline: {\n relPath: CLINE_RULE_PATH,\n sentinel: CLINE_RULE_SENTINEL,\n // No frontmatter -> always-on rule (Cline treats unconditional .clinerules/*.md\n // files as universal rules, applied to every task regardless of active file).\n body: `# token-shrink\n\nBefore working on a file in this repo, call the \\`get_compressed_code_context\\` MCP tool with\nthat file's path, and use the returned Ring 0 + Ring 1 payload as the context for that file\nand its direct imports.\n\n${CLINE_RULE_SENTINEL}\n`,\n },\n};\n\nexport interface McpServerOptions {\n /** Project root. Defaults to cwd. */\n root?: string;\n /** Extra ignore globs for the watcher. */\n ignored?: Matcher[];\n /** Silence log output (stdio must stay clean for the MCP protocol). */\n silent?: boolean;\n /** Whether to auto-write agent integration rules at all. Default true. */\n createRule?: boolean;\n /** Which agent rule target(s) to write. Default all. */\n ruleTarget?: RuleTarget | RuleTarget[];\n}\n\nexport interface RuleWriteResult {\n created: boolean;\n skipped: 'none' | 'exists' | 'user';\n /** Path of the rule file that was considered. */\n filePath: string;\n}\n\n/**\n * Write a rules file for a given agent target. Idempotent:\n * - absent -> create it (tagged with our sentinel);\n * - has our sentinel -> skip (already ours);\n * - present without sentinel -> leave the user's file untouched.\n */\nexport function createAutoRule(root: string, target: RuleTarget): RuleWriteResult {\n const spec = ruleTargets[target];\n const rulePath = path.join(root, spec.relPath);\n try {\n if (fs.existsSync(rulePath)) {\n const existing = fs.readFileSync(rulePath, 'utf8');\n if (existing.includes(spec.sentinel)) {\n return { created: false, skipped: 'exists', filePath: rulePath };\n }\n return { created: false, skipped: 'user', filePath: rulePath };\n }\n fs.mkdirSync(path.dirname(rulePath), { recursive: true });\n fs.writeFileSync(rulePath, spec.body, 'utf8');\n return { created: true, skipped: 'none', filePath: rulePath };\n } catch (err) {\n // Never let a rule-write failure take down the MCP server; surface via result.\n process.stderr.write(\n `[token-shrink] Failed to write rule for \"${target}\": ${(err as Error).message}\\n`,\n );\n return { created: false, skipped: 'none', filePath: rulePath };\n }\n}\n\n/** Back-compat alias for code that imported the old Cursor-only helper. */\nexport function createCursorRule(root: string): RuleWriteResult {\n return createAutoRule(root, 'cursor');\n}\n\n/** Expand `ruleTarget` (single / array / all) into the ordered list to write. */\nfunction resolveTargets(ruleTarget: McpServerOptions['ruleTarget']): RuleTarget[] {\n if (!ruleTarget) return ['cursor', 'claude', 'cline'];\n const list = Array.isArray(ruleTarget) ? ruleTarget : [ruleTarget];\n if (list.includes('all' as unknown as RuleTarget)) return ['cursor', 'claude', 'cline'];\n return list;\n}\n\n/**\n * Start the MCP server. Because the MCP protocol runs over stdio, all human\n * logging should go to stderr; stdout is reserved for JSON-RPC.\n */\nexport async function startMcpServer(opts: McpServerOptions = {}): Promise<McpServer> {\n const root = path.resolve(opts.root ?? process.env.ROOT ?? process.cwd());\n const log = (msg: string) => {\n if (!opts.silent) process.stderr.write(`[token-shrink] ${msg}\\n`);\n };\n\n const watcher = createWatcher({ root, ignored: opts.ignored });\n if (!opts.silent) {\n log(`Indexing ${root} in the background…`);\n }\n\n // Auto-create agent integration rules so the tool is used by default.\n if (opts.createRule !== false) {\n for (const target of resolveTargets(opts.ruleTarget)) {\n const res = createAutoRule(root, target);\n if (res.created) {\n log(`Wrote ${target} rule to ${res.filePath}`);\n } else if (res.skipped === 'user') {\n log(`${target} rule exists (user-authored); leaving it untouched.`);\n }\n }\n }\n // Fire-and-forget cold start; zero CPU afterward thanks to the watcher.\n void watcher.indexAll().then((n) => log(`Indexed ${n} files.`));\n\n const server = new McpServer(\n { name: 'token-shrink', version: '2.0.0' },\n { capabilities: { tools: {} } },\n );\n\n server.registerTool(\n 'get_compressed_code_context',\n {\n title: 'Get Compressed Code Context',\n description:\n 'Returns a compressed, framework-aware AST context payload for a file: ' +\n 'the active file’s full source (Ring 0) plus pruned skeletons of its direct ' +\n 'imports (Ring 1). Implementation bodies are removed but type signatures, ' +\n 'interfaces, and module exports are preserved for ~80-90% token reduction.',\n inputSchema: {\n activeFilePath: z.string().describe('Path to the file the agent is working on'),\n maxSkeletons: z\n .number()\n .int()\n .min(1)\n .max(200)\n .optional()\n .describe('Cap on number of dependency skeletons to include'),\n includeStats: z\n .boolean()\n .optional()\n .describe('Append approximate token-count stats'),\n },\n },\n async ({ activeFilePath, maxSkeletons, includeStats }) => {\n const result = assemble(activeFilePath, watcher.cache.entries, {\n maxSkeletons,\n includeStats,\n });\n return {\n content: [\n {\n type: 'text' as const,\n text: result.markdown,\n },\n {\n type: 'text' as const,\n text: `[stats] active=${result.activeFilePath} dependencies=${result.included.length} unresolved=${result.unresolved.length}`,\n },\n ],\n };\n },\n );\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n log('MCP server connected.');\n return server;\n}\n\n// Start the server only when run as the MCP binary, not when imported as a\n// library. Guard against both the source filenames and the tsup bundles:\n// `dist/mcp.cjs` (used by the npm `token-shrink-mcp` bin). `realpathSync`\n// resolves the npm bin symlink, so `token-shrink-mcp` -> `dist/mcp.cjs` is\n// detected as `mcp.cjs`.\nconst argv1 = process.argv[1] ? path.basename(process.argv[1]) : '';\nlet argv1Real = '';\ntry {\n argv1Real = process.argv[1]\n ? path.basename(fs.realpathSync(process.argv[1]))\n : '';\n} catch {\n /* path may not exist yet (tsx/dev runners) — fall back to argv basename */\n}\nconst invokedAsMcp =\n argv1 === 'mcp.js' || argv1 === 'mcp.mjs' ||\n argv1 === 'mcp.cjs' || argv1 === 'mcp.ts' ||\n argv1Real === 'mcp.js' || argv1Real === 'mcp.mjs' ||\n argv1Real === 'mcp.cjs' || argv1Real === 'mcp.ts';\nif (invokedAsMcp) {\n const rootArg = (() => {\n const i = process.argv.indexOf('--root');\n if (i !== -1 && process.argv[i + 1]) return process.argv[i + 1];\n const eq = process.argv.find((a) => a.startsWith('--root='));\n if (eq) return eq.slice('--root='.length);\n return undefined;\n })();\n const createRule = !(\n process.env.TOKEN_SHRINK_CREATE_RULE === '0' ||\n process.env.TOKEN_SHRINK_CREATE_RULE === 'false' ||\n process.env.CONTEXT_SHRINK_CREATE_RULE === '0' ||\n process.env.CONTEXT_SHRINK_CREATE_RULE === 'false' ||\n process.argv.includes('--no-create-rule') ||\n (() => {\n const f = process.argv.find((a) => a.startsWith('--create-rule='));\n return f ? f.slice('--create-rule='.length) === 'false' : false;\n })()\n );\n // --rule-target=cursor|claude|cline|all (repeatable / comma-separated), default all.\n const rawTargets = process.argv\n .filter((a) => a.startsWith('--rule-target='))\n .flatMap((a) => a.slice('--rule-target='.length).split(','));\n const ruleTarget: RuleTarget | RuleTarget[] | undefined =\n rawTargets.length > 0 && !rawTargets.includes('all')\n ? ([...new Set(rawTargets)].filter(\n (v): v is RuleTarget => v === 'cursor' || v === 'claude' || v === 'cline',\n ) as RuleTarget[])\n : undefined;\n void startMcpServer({ root: rootArg, createRule, ruleTarget }).catch((err) => {\n process.stderr.write(`[token-shrink] MCP server error: ${err.message}\\n`);\n process.exitCode = 1;\n });\n}\n"],"mappings":"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","names":[]}
|