@ajdev0/token-shrink 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,179 @@
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
+ };
17
+ var AUTO_RULE_SENTINEL = "# auto-generated by token-shrink";
18
+ var CURSOR_RULE_PATH = RULE_TARGET_PATH.cursor;
19
+ var AUTO_RULE_PATH = CURSOR_RULE_PATH;
20
+ var CLAUDE_RULE_PATH = RULE_TARGET_PATH.claude;
21
+ var CLAUDE_RULE_SENTINEL = "# auto-generated by token-shrink (claude)";
22
+ var ruleTargets = {
23
+ cursor: {
24
+ relPath: CURSOR_RULE_PATH,
25
+ sentinel: AUTO_RULE_SENTINEL,
26
+ body: `---
27
+ description: Compress dependency context with token-shrink on every task
28
+ globs: **/*.{ts,tsx,js,jsx,py,go,rs,dart,swift,java,kt,c,cpp,h,hpp,php}
29
+ alwaysApply: true
30
+ ---
31
+ Before working on a file in this repo, call the \`get_compressed_code_context\` MCP tool with
32
+ that file's path, and use the returned Ring 0 + Ring 1 payload as the context for that file
33
+ and its direct imports.
34
+
35
+ ${AUTO_RULE_SENTINEL}
36
+ `
37
+ },
38
+ claude: {
39
+ relPath: CLAUDE_RULE_PATH,
40
+ sentinel: CLAUDE_RULE_SENTINEL,
41
+ body: `---
42
+ description: Compress dependency context with token-shrink on every task
43
+ paths: ["**/*.{ts,tsx,js,jsx,py,go,rs,dart,swift,java,kt,c,cpp,h,hpp,php}"]
44
+ ---
45
+ Before working on a file in this repo, call the \`get_compressed_code_context\` MCP tool with
46
+ that file's path, and use the returned Ring 0 + Ring 1 payload as the context for that file
47
+ and its direct imports.
48
+
49
+ ${CLAUDE_RULE_SENTINEL}
50
+ `
51
+ }
52
+ };
53
+ function createAutoRule(root, target) {
54
+ const spec = ruleTargets[target];
55
+ const rulePath = path.join(root, spec.relPath);
56
+ try {
57
+ if (fs.existsSync(rulePath)) {
58
+ const existing = fs.readFileSync(rulePath, "utf8");
59
+ if (existing.includes(spec.sentinel)) {
60
+ return { created: false, skipped: "exists", filePath: rulePath };
61
+ }
62
+ return { created: false, skipped: "user", filePath: rulePath };
63
+ }
64
+ fs.mkdirSync(path.dirname(rulePath), { recursive: true });
65
+ fs.writeFileSync(rulePath, spec.body, "utf8");
66
+ return { created: true, skipped: "none", filePath: rulePath };
67
+ } catch (err) {
68
+ process.stderr.write(
69
+ `[token-shrink] Failed to write rule for "${target}": ${err.message}
70
+ `
71
+ );
72
+ return { created: false, skipped: "none", filePath: rulePath };
73
+ }
74
+ }
75
+ function createCursorRule(root) {
76
+ return createAutoRule(root, "cursor");
77
+ }
78
+ function resolveTargets(ruleTarget) {
79
+ if (!ruleTarget) return ["cursor", "claude"];
80
+ const list = Array.isArray(ruleTarget) ? ruleTarget : [ruleTarget];
81
+ if (list.includes("all")) return ["cursor", "claude"];
82
+ return list;
83
+ }
84
+ async function startMcpServer(opts = {}) {
85
+ const root = path.resolve(opts.root ?? process.env.ROOT ?? process.cwd());
86
+ const log = (msg) => {
87
+ if (!opts.silent) process.stderr.write(`[token-shrink] ${msg}
88
+ `);
89
+ };
90
+ const watcher = createWatcher({ root, ignored: opts.ignored });
91
+ if (!opts.silent) {
92
+ log(`Indexing ${root} in the background\u2026`);
93
+ }
94
+ if (opts.createRule !== false) {
95
+ for (const target of resolveTargets(opts.ruleTarget)) {
96
+ const res = createAutoRule(root, target);
97
+ if (res.created) {
98
+ log(`Wrote ${target} rule to ${res.filePath}`);
99
+ } else if (res.skipped === "user") {
100
+ log(`${target} rule exists (user-authored); leaving it untouched.`);
101
+ }
102
+ }
103
+ }
104
+ void watcher.indexAll().then((n) => log(`Indexed ${n} files.`));
105
+ const server = new McpServer(
106
+ { name: "token-shrink", version: "2.0.0" },
107
+ { capabilities: { tools: {} } }
108
+ );
109
+ server.registerTool(
110
+ "get_compressed_code_context",
111
+ {
112
+ title: "Get Compressed Code Context",
113
+ 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.",
114
+ inputSchema: {
115
+ activeFilePath: z.string().describe("Path to the file the agent is working on"),
116
+ maxSkeletons: z.number().int().min(1).max(200).optional().describe("Cap on number of dependency skeletons to include"),
117
+ includeStats: z.boolean().optional().describe("Append approximate token-count stats")
118
+ }
119
+ },
120
+ async ({ activeFilePath, maxSkeletons, includeStats }) => {
121
+ const result = assemble(activeFilePath, watcher.cache.entries, {
122
+ maxSkeletons,
123
+ includeStats
124
+ });
125
+ return {
126
+ content: [
127
+ {
128
+ type: "text",
129
+ text: result.markdown
130
+ },
131
+ {
132
+ type: "text",
133
+ text: `[stats] active=${result.activeFilePath} dependencies=${result.included.length} unresolved=${result.unresolved.length}`
134
+ }
135
+ ]
136
+ };
137
+ }
138
+ );
139
+ const transport = new StdioServerTransport();
140
+ await server.connect(transport);
141
+ log("MCP server connected.");
142
+ return server;
143
+ }
144
+ var argv1 = process.argv[1] ? path.basename(process.argv[1]) : "";
145
+ if (argv1 === "mcp.js" || argv1 === "mcp.mjs" || argv1 === "mcp.cjs" || argv1 === "mcp.ts") {
146
+ const rootArg = (() => {
147
+ const i = process.argv.indexOf("--root");
148
+ if (i !== -1 && process.argv[i + 1]) return process.argv[i + 1];
149
+ const eq = process.argv.find((a) => a.startsWith("--root="));
150
+ if (eq) return eq.slice("--root=".length);
151
+ return void 0;
152
+ })();
153
+ 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") || (() => {
154
+ const f = process.argv.find((a) => a.startsWith("--create-rule="));
155
+ return f ? f.slice("--create-rule=".length) === "false" : false;
156
+ })());
157
+ const rawTargets = process.argv.filter((a) => a.startsWith("--rule-target=")).flatMap((a) => a.slice("--rule-target=".length).split(","));
158
+ const ruleTarget = rawTargets.length > 0 && !rawTargets.includes("all") ? [...new Set(rawTargets)].filter(
159
+ (v) => v === "cursor" || v === "claude"
160
+ ) : void 0;
161
+ void startMcpServer({ root: rootArg, createRule, ruleTarget }).catch((err) => {
162
+ process.stderr.write(`[token-shrink] MCP server error: ${err.message}
163
+ `);
164
+ process.exitCode = 1;
165
+ });
166
+ }
167
+
168
+ export {
169
+ RULE_TARGET_PATH,
170
+ AUTO_RULE_SENTINEL,
171
+ CURSOR_RULE_PATH,
172
+ AUTO_RULE_PATH,
173
+ CLAUDE_RULE_PATH,
174
+ CLAUDE_RULE_SENTINEL,
175
+ createAutoRule,
176
+ createCursorRule,
177
+ startMcpServer
178
+ };
179
+ //# sourceMappingURL=chunk-LPJMNP4N.js.map
@@ -0,0 +1 @@
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 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';\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} 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)';\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};\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 both. */\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'];\n const list = Array.isArray(ruleTarget) ? ruleTarget : [ruleTarget];\n if (list.includes('all' as unknown as RuleTarget)) return ['cursor', 'claude'];\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 (`dist/mcp.cjs`), not when\n// imported as a library. Guard against both the source and bundled filenames.\nconst argv1 = process.argv[1] ? path.basename(process.argv[1]) : '';\nif (\n argv1 === 'mcp.js' || argv1 === 'mcp.mjs' ||\n argv1 === 'mcp.cjs' || argv1 === 'mcp.ts'\n) {\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|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',\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":[]}
@@ -0,0 +1,96 @@
1
+ import * as chokidar from 'chokidar';
2
+ import { Matcher, FSWatcher } from 'chokidar';
3
+ import * as fastify from 'fastify';
4
+ import * as http from 'http';
5
+
6
+ /**
7
+ * Incremental synchronization layer (Phase 3).
8
+ *
9
+ * - Watches the repository with chokidar (ignoring vendor/build dirs).
10
+ * - On add/change, hashes the file and, if the hash changed, re-prunes it and
11
+ * updates the in-memory graph cache.
12
+ * - Extracts import/require specifiers from each file and normalizes relative
13
+ * and aliased paths to absolute file paths on disk.
14
+ *
15
+ * The graph cache is shared with the Context Assembler, which reads ring-1
16
+ * skeletons out of it.
17
+ */
18
+
19
+ /** Default paths that will never be indexed or watched. */
20
+ declare const DEFAULT_IGNORED: RegExp[];
21
+ interface CacheEntry {
22
+ /** sha1 of the last-indexed file content. */
23
+ hash: string;
24
+ /** Pruned skeleton for this file. */
25
+ skeleton: string;
26
+ /** The language name used to prune it (or null if unsupported). */
27
+ language: string | null;
28
+ /** Absolute paths of the files this file imports. */
29
+ imports: string[];
30
+ }
31
+ type GraphCache = Map<string, CacheEntry>;
32
+ declare class ContextCache {
33
+ readonly entries: GraphCache;
34
+ private initPromise;
35
+ ensureInit(): Promise<void>;
36
+ /** Returns the cached skeleton for a file, if present. */
37
+ getSkeleton(filePath: string): CacheEntry | null;
38
+ get imports(): GraphCache;
39
+ }
40
+ /** Compute a sha1 of a string. */
41
+ declare function hashOf(text: string): string;
42
+ /**
43
+ * Extract import/require specifiers from source text using a robust,
44
+ * language-agnostic regex (AST-based import queries are grammar-fragile and
45
+ * occasionally malformed; regex covers the common import forms across JS/TS,
46
+ * Python, Go, Rust, Dart, Swift, Java, Kotlin, PHP, C/C++).
47
+ * Returns the matched specifier strings (may be relative or absolute).
48
+ */
49
+ declare function extractImports(_filePath: string, source: string): string[];
50
+ /** Normalize a specifier to an absolute file path when it resolves locally. */
51
+ declare function resolveImport(importer: string, specifier: string, root: string): string | null;
52
+ interface SyncOptions {
53
+ root: string;
54
+ ignored?: Matcher[];
55
+ onIndexed?: (filePath: string, entry: CacheEntry) => void;
56
+ }
57
+ /**
58
+ * Watch a project root and keep the graph cache fresh. Returns the cache and
59
+ * a close() handle. `prune` is awaited per change to keep hot-reload latency
60
+ * predictable.
61
+ */
62
+ declare function createWatcher(opts: SyncOptions): {
63
+ cache: ContextCache;
64
+ watcher: FSWatcher;
65
+ /** Index an existing file right now (bypasses the watcher). */
66
+ index: (filePath: string) => Promise<void>;
67
+ /**
68
+ * Index the whole tree once (cold start). Returns the number of files
69
+ * successfully indexed.
70
+ */
71
+ indexAll(): Promise<number>;
72
+ close: () => Promise<void>;
73
+ };
74
+
75
+ interface CliOptions {
76
+ port?: number;
77
+ host?: string;
78
+ root?: string;
79
+ ignored?: Matcher[];
80
+ silent?: boolean;
81
+ }
82
+ /** Start the Fastify server; returns the running instance + watcher handle. */
83
+ declare function startServer(opts?: CliOptions): Promise<{
84
+ app: fastify.FastifyInstance<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>, http.IncomingMessage, http.ServerResponse<http.IncomingMessage>, fastify.FastifyBaseLogger, fastify.FastifyTypeProviderDefault> & PromiseLike<fastify.FastifyInstance<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>, http.IncomingMessage, http.ServerResponse<http.IncomingMessage>, fastify.FastifyBaseLogger, fastify.FastifyTypeProviderDefault>> & {
85
+ __linterBrands: "SafePromiseLike";
86
+ };
87
+ watcher: {
88
+ cache: ContextCache;
89
+ watcher: chokidar.FSWatcher;
90
+ index: (filePath: string) => Promise<void>;
91
+ indexAll(): Promise<number>;
92
+ close: () => Promise<void>;
93
+ };
94
+ }>;
95
+
96
+ export { type CacheEntry as C, DEFAULT_IGNORED as D, type GraphCache as G, type SyncOptions as S, type CliOptions as a, createWatcher as c, extractImports as e, hashOf as h, resolveImport as r, startServer as s };
@@ -0,0 +1,96 @@
1
+ import * as chokidar from 'chokidar';
2
+ import { Matcher, FSWatcher } from 'chokidar';
3
+ import * as fastify from 'fastify';
4
+ import * as http from 'http';
5
+
6
+ /**
7
+ * Incremental synchronization layer (Phase 3).
8
+ *
9
+ * - Watches the repository with chokidar (ignoring vendor/build dirs).
10
+ * - On add/change, hashes the file and, if the hash changed, re-prunes it and
11
+ * updates the in-memory graph cache.
12
+ * - Extracts import/require specifiers from each file and normalizes relative
13
+ * and aliased paths to absolute file paths on disk.
14
+ *
15
+ * The graph cache is shared with the Context Assembler, which reads ring-1
16
+ * skeletons out of it.
17
+ */
18
+
19
+ /** Default paths that will never be indexed or watched. */
20
+ declare const DEFAULT_IGNORED: RegExp[];
21
+ interface CacheEntry {
22
+ /** sha1 of the last-indexed file content. */
23
+ hash: string;
24
+ /** Pruned skeleton for this file. */
25
+ skeleton: string;
26
+ /** The language name used to prune it (or null if unsupported). */
27
+ language: string | null;
28
+ /** Absolute paths of the files this file imports. */
29
+ imports: string[];
30
+ }
31
+ type GraphCache = Map<string, CacheEntry>;
32
+ declare class ContextCache {
33
+ readonly entries: GraphCache;
34
+ private initPromise;
35
+ ensureInit(): Promise<void>;
36
+ /** Returns the cached skeleton for a file, if present. */
37
+ getSkeleton(filePath: string): CacheEntry | null;
38
+ get imports(): GraphCache;
39
+ }
40
+ /** Compute a sha1 of a string. */
41
+ declare function hashOf(text: string): string;
42
+ /**
43
+ * Extract import/require specifiers from source text using a robust,
44
+ * language-agnostic regex (AST-based import queries are grammar-fragile and
45
+ * occasionally malformed; regex covers the common import forms across JS/TS,
46
+ * Python, Go, Rust, Dart, Swift, Java, Kotlin, PHP, C/C++).
47
+ * Returns the matched specifier strings (may be relative or absolute).
48
+ */
49
+ declare function extractImports(_filePath: string, source: string): string[];
50
+ /** Normalize a specifier to an absolute file path when it resolves locally. */
51
+ declare function resolveImport(importer: string, specifier: string, root: string): string | null;
52
+ interface SyncOptions {
53
+ root: string;
54
+ ignored?: Matcher[];
55
+ onIndexed?: (filePath: string, entry: CacheEntry) => void;
56
+ }
57
+ /**
58
+ * Watch a project root and keep the graph cache fresh. Returns the cache and
59
+ * a close() handle. `prune` is awaited per change to keep hot-reload latency
60
+ * predictable.
61
+ */
62
+ declare function createWatcher(opts: SyncOptions): {
63
+ cache: ContextCache;
64
+ watcher: FSWatcher;
65
+ /** Index an existing file right now (bypasses the watcher). */
66
+ index: (filePath: string) => Promise<void>;
67
+ /**
68
+ * Index the whole tree once (cold start). Returns the number of files
69
+ * successfully indexed.
70
+ */
71
+ indexAll(): Promise<number>;
72
+ close: () => Promise<void>;
73
+ };
74
+
75
+ interface CliOptions {
76
+ port?: number;
77
+ host?: string;
78
+ root?: string;
79
+ ignored?: Matcher[];
80
+ silent?: boolean;
81
+ }
82
+ /** Start the Fastify server; returns the running instance + watcher handle. */
83
+ declare function startServer(opts?: CliOptions): Promise<{
84
+ app: fastify.FastifyInstance<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>, http.IncomingMessage, http.ServerResponse<http.IncomingMessage>, fastify.FastifyBaseLogger, fastify.FastifyTypeProviderDefault> & PromiseLike<fastify.FastifyInstance<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>, http.IncomingMessage, http.ServerResponse<http.IncomingMessage>, fastify.FastifyBaseLogger, fastify.FastifyTypeProviderDefault>> & {
85
+ __linterBrands: "SafePromiseLike";
86
+ };
87
+ watcher: {
88
+ cache: ContextCache;
89
+ watcher: chokidar.FSWatcher;
90
+ index: (filePath: string) => Promise<void>;
91
+ indexAll(): Promise<number>;
92
+ close: () => Promise<void>;
93
+ };
94
+ }>;
95
+
96
+ export { type CacheEntry as C, DEFAULT_IGNORED as D, type GraphCache as G, type SyncOptions as S, type CliOptions as a, createWatcher as c, extractImports as e, hashOf as h, resolveImport as r, startServer as s };