token-pilot 0.19.1 → 0.19.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.
@@ -2,14 +2,14 @@ export interface BinaryStatus {
2
2
  available: boolean;
3
3
  path: string;
4
4
  version: string | null;
5
- source: 'system' | 'managed' | 'none';
5
+ source: 'system' | 'npm' | 'managed' | 'none';
6
6
  }
7
7
  /**
8
- * Find ast-index binary: check system PATH first, then managed install.
8
+ * Find ast-index binary: config system PATH npm global → managed install.
9
9
  */
10
10
  export declare function findBinary(configPath?: string | null): Promise<BinaryStatus>;
11
11
  /**
12
- * Download and install ast-index binary from GitHub releases.
12
+ * Install ast-index: tries npm global first (all platforms), falls back to GitHub download.
13
13
  */
14
14
  export declare function installBinary(onProgress?: (msg: string) => void): Promise<BinaryStatus>;
15
15
  /**
@@ -12,7 +12,7 @@ const REPO = 'defendend/Claude-ast-index-search';
12
12
  const BINARY_NAME = platform() === 'win32' ? 'ast-index.exe' : 'ast-index';
13
13
  const INSTALL_DIR = resolve(homedir(), '.token-pilot', 'bin');
14
14
  /**
15
- * Find ast-index binary: check system PATH first, then managed install.
15
+ * Find ast-index binary: config system PATH npm global → managed install.
16
16
  */
17
17
  export async function findBinary(configPath) {
18
18
  // 1. Config override
@@ -28,7 +28,15 @@ export async function findBinary(configPath) {
28
28
  const version = await getBinaryVersion(systemPath);
29
29
  return { available: true, path: systemPath, version, source: 'system' };
30
30
  }
31
- // 3. Managed install
31
+ // 3. npm global install (@ast-index/cli)
32
+ const npmPath = await findViaNpmBin();
33
+ if (npmPath) {
34
+ const version = await getBinaryVersion(npmPath);
35
+ if (version) {
36
+ return { available: true, path: npmPath, version, source: 'npm' };
37
+ }
38
+ }
39
+ // 4. Managed install (GitHub download)
32
40
  const managedPath = resolve(INSTALL_DIR, BINARY_NAME);
33
41
  const version = await getBinaryVersion(managedPath);
34
42
  if (version) {
@@ -37,24 +45,53 @@ export async function findBinary(configPath) {
37
45
  return { available: false, path: '', version: null, source: 'none' };
38
46
  }
39
47
  /**
40
- * Download and install ast-index binary from GitHub releases.
48
+ * Install ast-index: tries npm global first (all platforms), falls back to GitHub download.
41
49
  */
42
50
  export async function installBinary(onProgress) {
43
51
  const log = onProgress ?? (() => { });
52
+ // Try npm first — simpler, handles all platforms including Windows
53
+ try {
54
+ return await installViaNpm(log);
55
+ }
56
+ catch (npmErr) {
57
+ log(`npm install failed (${npmErr instanceof Error ? npmErr.message : npmErr}), trying GitHub download...`);
58
+ }
59
+ // GitHub download fallback
60
+ return installViaNpmFallback(log);
61
+ }
62
+ async function installViaNpm(onProgress) {
63
+ onProgress('Installing @ast-index/cli via npm...');
64
+ await new Promise((resolve, reject) => {
65
+ execFile('npm', ['install', '-g', '@ast-index/cli'], { timeout: 120_000 }, (err, _stdout, stderr) => {
66
+ if (err)
67
+ reject(new Error(stderr.trim() || err.message));
68
+ else
69
+ resolve();
70
+ });
71
+ });
72
+ const binPath = await findViaNpmBin();
73
+ if (!binPath) {
74
+ throw new Error('@ast-index/cli installed but binary not found in npm prefix');
75
+ }
76
+ const version = await getBinaryVersion(binPath);
77
+ onProgress(`Installed ast-index ${version} via npm at ${binPath}`);
78
+ return { available: true, path: binPath, version, source: 'npm' };
79
+ }
80
+ async function installViaNpmFallback(onProgress) {
44
81
  // Determine platform/arch
45
82
  const plat = getPlatform();
46
83
  const ar = getArch();
47
84
  if (!plat || !ar) {
48
85
  throw new Error(`Unsupported platform: ${platform()} ${arch()}`);
49
86
  }
50
- log('Fetching latest release info...');
87
+ onProgress('Fetching latest release info...');
51
88
  const release = await fetchLatestRelease();
52
89
  const assetName = buildAssetName(release.tag, plat, ar);
53
90
  const asset = release.assets.find(a => a.name === assetName);
54
91
  if (!asset) {
55
92
  throw new Error(`No binary found for ${plat}-${ar}. Available: ${release.assets.map(a => a.name).join(', ')}`);
56
93
  }
57
- log(`Downloading ${asset.name} (${(asset.size / 1024 / 1024).toFixed(1)}MB)...`);
94
+ onProgress(`Downloading ${asset.name} (${(asset.size / 1024 / 1024).toFixed(1)}MB)...`);
58
95
  await mkdir(INSTALL_DIR, { recursive: true });
59
96
  const tmpPath = resolve(INSTALL_DIR, `${BINARY_NAME}.tmp`);
60
97
  const finalPath = resolve(INSTALL_DIR, BINARY_NAME);
@@ -62,19 +99,16 @@ export async function installBinary(onProgress) {
62
99
  if (assetName.endsWith('.tar.gz')) {
63
100
  await downloadAndExtractTarGz(asset.url, INSTALL_DIR, BINARY_NAME);
64
101
  }
65
- else if (assetName.endsWith('.zip')) {
66
- // For Windows, download zip and extract
102
+ else {
67
103
  await downloadFile(asset.url, tmpPath);
68
- // Simple approach: use system unzip if available
69
- throw new Error('ZIP extraction not yet supported. Please install ast-index manually on Windows.');
104
+ throw new Error('ZIP extraction not yet supported. Please use: npm install -g @ast-index/cli');
70
105
  }
71
106
  await chmod(finalPath, 0o755);
72
107
  const version = await getBinaryVersion(finalPath);
73
- log(`Installed ast-index ${version} to ${finalPath}`);
108
+ onProgress(`Installed ast-index ${version} to ${finalPath}`);
74
109
  return { available: true, path: finalPath, version, source: 'managed' };
75
110
  }
76
111
  catch (err) {
77
- // Cleanup on failure
78
112
  try {
79
113
  await rm(tmpPath, { force: true });
80
114
  }
@@ -130,6 +164,35 @@ export function isNewerVersion(current, latest) {
130
164
  return false;
131
165
  }
132
166
  // --- Internal helpers ---
167
+ /**
168
+ * Find ast-index binary installed via `npm install -g @ast-index/cli`.
169
+ * Checks the npm global prefix bin directory.
170
+ */
171
+ async function findViaNpmBin() {
172
+ try {
173
+ const prefix = await new Promise((resolve, reject) => {
174
+ execFile('npm', ['config', 'get', 'prefix'], { timeout: 3000 }, (err, stdout) => {
175
+ if (err)
176
+ reject(err);
177
+ else
178
+ resolve(stdout.trim());
179
+ });
180
+ });
181
+ // Unix: <prefix>/bin/ast-index | Windows: <prefix>\ast-index.exe or <prefix>\bin\ast-index.exe
182
+ const candidates = platform() === 'win32'
183
+ ? [resolve(prefix, BINARY_NAME), resolve(prefix, 'bin', BINARY_NAME)]
184
+ : [resolve(prefix, 'bin', BINARY_NAME)];
185
+ for (const candidate of candidates) {
186
+ try {
187
+ await access(candidate);
188
+ return candidate;
189
+ }
190
+ catch { }
191
+ }
192
+ }
193
+ catch { }
194
+ return null;
195
+ }
133
196
  function getPlatform() {
134
197
  switch (platform()) {
135
198
  case 'darwin': return 'darwin';
@@ -27,12 +27,16 @@ export declare class AstIndexClient {
27
27
  symbol(name: string): Promise<AstIndexSymbolDetail | null>;
28
28
  search(query: string, options?: {
29
29
  inFile?: string;
30
+ type?: string;
30
31
  maxResults?: number;
31
32
  fuzzy?: boolean;
32
33
  }): Promise<AstIndexSearchResult[]>;
33
34
  usages(symbolName: string): Promise<AstIndexUsageResult[]>;
34
35
  implementations(name: string): Promise<AstIndexImplementation[]>;
35
- hierarchy(name: string): Promise<AstIndexHierarchyNode | null>;
36
+ hierarchy(name: string, options?: {
37
+ inFile?: string;
38
+ module?: string;
39
+ }): Promise<AstIndexHierarchyNode | null>;
36
40
  stats(): Promise<string | null>;
37
41
  listFiles(): Promise<string[]>;
38
42
  refs(symbolName: string, limit?: number): Promise<AstIndexRefsResponse>;
@@ -223,6 +223,8 @@ export class AstIndexClient {
223
223
  const args = ['search', query, '--format', 'json'];
224
224
  if (options?.inFile)
225
225
  args.push('--in-file', options.inFile);
226
+ if (options?.type)
227
+ args.push('--type', options.type);
226
228
  if (options?.maxResults)
227
229
  args.push('--limit', String(options.maxResults));
228
230
  if (options?.fuzzy)
@@ -295,10 +297,15 @@ export class AstIndexClient {
295
297
  return [];
296
298
  }
297
299
  }
298
- async hierarchy(name) {
300
+ async hierarchy(name, options) {
299
301
  await this.ensureIndex();
300
302
  try {
301
- const result = await this.exec(['hierarchy', name, '--format', 'json']);
303
+ const args = ['hierarchy', name, '--format', 'json'];
304
+ if (options?.inFile)
305
+ args.push('--in-file', options.inFile);
306
+ if (options?.module)
307
+ args.push('--module', options.module);
308
+ const result = await this.exec(args);
302
309
  try {
303
310
  return JSON.parse(result);
304
311
  }
@@ -8,11 +8,17 @@ export interface HookUninstallResult {
8
8
  fatal: boolean;
9
9
  message: string;
10
10
  }
11
+ export interface HookInstallOptions {
12
+ /** Absolute path to the entry script (dist/index.js). When provided, hooks use absolute paths instead of bare "token-pilot". */
13
+ scriptPath?: string;
14
+ /** Absolute path to the node binary. Defaults to process.execPath. */
15
+ nodeExecPath?: string;
16
+ }
11
17
  /**
12
18
  * Install Token Pilot hook into Claude Code settings.
13
19
  * Creates or updates .claude/settings.json with PreToolUse hook.
14
20
  */
15
- export declare function installHook(projectRoot: string): Promise<HookInstallResult>;
21
+ export declare function installHook(projectRoot: string, options?: HookInstallOptions): Promise<HookInstallResult>;
16
22
  /**
17
23
  * Remove Token Pilot hook from Claude Code settings.
18
24
  */
@@ -1,35 +1,55 @@
1
1
  import { readFile, writeFile, mkdir } from 'node:fs/promises';
2
2
  import { resolve, dirname } from 'node:path';
3
- const HOOK_CONFIG = {
4
- hooks: {
5
- PreToolUse: [
6
- {
7
- matcher: "Read",
8
- hooks: [
9
- {
10
- type: "command",
11
- command: "token-pilot hook-read",
12
- },
13
- ],
14
- },
15
- {
16
- matcher: "Edit",
17
- hooks: [
18
- {
19
- type: "command",
20
- command: "token-pilot hook-edit",
21
- },
22
- ],
23
- },
24
- ],
25
- },
26
- };
3
+ /**
4
+ * Build hook command that works in any shell (/bin/sh, bash, etc.)
5
+ * Uses absolute paths to node + script to avoid PATH/nvm issues.
6
+ * Falls back to bare "token-pilot" only for manual CLI installs.
7
+ */
8
+ function buildHookCommand(action, options) {
9
+ if (options?.scriptPath) {
10
+ const node = options.nodeExecPath || process.execPath;
11
+ return `${node} ${options.scriptPath} ${action}`;
12
+ }
13
+ return `token-pilot ${action}`;
14
+ }
15
+ function createHookConfig(options) {
16
+ return {
17
+ hooks: {
18
+ PreToolUse: [
19
+ {
20
+ matcher: "Read",
21
+ hooks: [
22
+ {
23
+ type: "command",
24
+ command: buildHookCommand('hook-read', options),
25
+ },
26
+ ],
27
+ },
28
+ {
29
+ matcher: "Edit",
30
+ hooks: [
31
+ {
32
+ type: "command",
33
+ command: buildHookCommand('hook-edit', options),
34
+ },
35
+ ],
36
+ },
37
+ ],
38
+ },
39
+ };
40
+ }
27
41
  /**
28
42
  * Install Token Pilot hook into Claude Code settings.
29
43
  * Creates or updates .claude/settings.json with PreToolUse hook.
30
44
  */
31
- export async function installHook(projectRoot) {
45
+ export async function installHook(projectRoot, options) {
46
+ // Skip auto-install when running as a Claude Code plugin —
47
+ // the plugin system already registers hooks via .claude-plugin/hooks/hooks.json
48
+ if (process.env.CLAUDE_PLUGIN_ROOT) {
49
+ return { installed: false, fatal: false, message: 'Running as plugin — hooks registered via plugin system.' };
50
+ }
32
51
  const settingsPath = resolve(projectRoot, '.claude', 'settings.json');
52
+ const hookConfig = createHookConfig(options);
33
53
  try {
34
54
  // Ensure .claude dir exists
35
55
  await mkdir(dirname(settingsPath), { recursive: true });
@@ -63,16 +83,26 @@ export async function installHook(projectRoot) {
63
83
  const existingHooks = settings.hooks?.PreToolUse;
64
84
  const isTokenPilotHook = (h) => h.hooks?.some((hook) => hook.command?.includes('token-pilot'));
65
85
  if (Array.isArray(existingHooks)) {
66
- const hasRead = existingHooks.some((h) => h.matcher === 'Read' && isTokenPilotHook(h));
67
- const hasEdit = existingHooks.some((h) => h.matcher === 'Edit' && isTokenPilotHook(h));
68
- if (hasRead && hasEdit) {
69
- return { installed: false, fatal: false, message: 'Token Pilot hooks already installed.' };
86
+ // Remove old broken hooks (bare "token-pilot" without absolute path)
87
+ // and replace with working ones using absolute paths
88
+ const oldBrokenHooks = existingHooks.filter((h) => isTokenPilotHook(h) && h.hooks?.some((hook) => hook.command?.match(/^token-pilot\s/) // bare command without path
89
+ ));
90
+ if (oldBrokenHooks.length > 0 && options?.scriptPath) {
91
+ // Remove old broken hooks, will re-add with absolute paths below
92
+ settings.hooks.PreToolUse = existingHooks.filter((h) => !isTokenPilotHook(h));
93
+ }
94
+ else {
95
+ const hasRead = existingHooks.some((h) => h.matcher === 'Read' && isTokenPilotHook(h));
96
+ const hasEdit = existingHooks.some((h) => h.matcher === 'Edit' && isTokenPilotHook(h));
97
+ if (hasRead && hasEdit) {
98
+ return { installed: false, fatal: false, message: 'Token Pilot hooks already installed.' };
99
+ }
70
100
  }
71
101
  // Add missing hooks
72
- for (const hookDef of HOOK_CONFIG.hooks.PreToolUse) {
73
- const exists = existingHooks.some((h) => h.matcher === hookDef.matcher && isTokenPilotHook(h));
102
+ for (const hookDef of hookConfig.hooks.PreToolUse) {
103
+ const exists = settings.hooks.PreToolUse.some((h) => h.matcher === hookDef.matcher && isTokenPilotHook(h));
74
104
  if (!exists) {
75
- existingHooks.push(hookDef);
105
+ settings.hooks.PreToolUse.push(hookDef);
76
106
  }
77
107
  }
78
108
  }
@@ -80,7 +110,7 @@ export async function installHook(projectRoot) {
80
110
  // Create hooks section
81
111
  if (!settings.hooks)
82
112
  settings.hooks = {};
83
- settings.hooks.PreToolUse = HOOK_CONFIG.hooks.PreToolUse;
113
+ settings.hooks.PreToolUse = hookConfig.hooks.PreToolUse;
84
114
  }
85
115
  await writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n');
86
116
  return {
package/dist/index.js CHANGED
@@ -122,7 +122,16 @@ export async function startServer(cliArgs = process.argv.slice(2)) {
122
122
  const binaryStatus = await findBinary(config.astIndex.binaryPath);
123
123
  checkAllUpdates(config, binaryStatus).catch(() => { });
124
124
  // Auto-install PreToolUse hook (non-blocking, Claude Code only)
125
- installHook(projectRoot).then(result => {
125
+ // Uses absolute paths to node + script so hooks work in /bin/sh (nvm, npx, etc.)
126
+ let hookOptions;
127
+ try {
128
+ const rawPath = fileURLToPath(new URL('./index.js', import.meta.url));
129
+ hookOptions = { scriptPath: realpathSync(rawPath), nodeExecPath: process.execPath };
130
+ }
131
+ catch {
132
+ // Can't resolve script path (e.g. running from src/ in tests) — fall back to bare command
133
+ }
134
+ installHook(projectRoot, hookOptions).then(result => {
126
135
  if (result.installed) {
127
136
  console.error(`[token-pilot] hook auto-installed: ${result.message}`);
128
137
  }
@@ -239,7 +248,15 @@ export function handleHookEdit() {
239
248
  process.exit(0);
240
249
  }
241
250
  export async function handleInstallHook(projectRoot) {
242
- const result = await installHook(projectRoot);
251
+ let hookOptions;
252
+ try {
253
+ const rawPath = fileURLToPath(new URL('./index.js', import.meta.url));
254
+ hookOptions = { scriptPath: realpathSync(rawPath), nodeExecPath: process.execPath };
255
+ }
256
+ catch {
257
+ // Fall back to bare command
258
+ }
259
+ const result = await installHook(projectRoot, hookOptions);
243
260
  console.log(result.message);
244
261
  process.exit(result.fatal ? 1 : 0);
245
262
  }
@@ -443,25 +460,25 @@ export async function checkAllUpdates(config, binaryStatus) {
443
460
  }
444
461
  }
445
462
  export function printHelp() {
446
- console.log(`token-pilot v${getVersion()} — MCP server for token-efficient code reading
447
-
448
- Usage:
449
- token-pilot [project-root] Start MCP server (default: cwd)
450
- token-pilot init [dir] Create .mcp.json with token-pilot + context-mode
451
- token-pilot install-hook [root] Install PreToolUse hook (Claude Code only)
452
- token-pilot uninstall-hook [root] Remove PreToolUse hook
453
- token-pilot install-ast-index Download ast-index binary (auto on first run)
454
- token-pilot doctor Run diagnostics (check ast-index, config, updates)
455
- token-pilot --version Show version
456
- token-pilot --help Show this help
457
-
458
- Quick start:
459
- npx token-pilot init Setup .mcp.json (token-pilot + context-mode)
460
-
461
- MCP Tools (18):
462
- smart_read, read_symbol, read_range, read_diff, read_for_edit, smart_read_many,
463
- find_usages, find_unused, related_files, outline, project_overview, session_analytics,
464
- code_audit, module_info, smart_diff, explore_area, smart_log, test_summary
463
+ console.log(`token-pilot v${getVersion()} — MCP server for token-efficient code reading
464
+
465
+ Usage:
466
+ token-pilot [project-root] Start MCP server (default: cwd)
467
+ token-pilot init [dir] Create .mcp.json with token-pilot + context-mode
468
+ token-pilot install-hook [root] Install PreToolUse hook (Claude Code only)
469
+ token-pilot uninstall-hook [root] Remove PreToolUse hook
470
+ token-pilot install-ast-index Download ast-index binary (auto on first run)
471
+ token-pilot doctor Run diagnostics (check ast-index, config, updates)
472
+ token-pilot --version Show version
473
+ token-pilot --help Show this help
474
+
475
+ Quick start:
476
+ npx token-pilot init Setup .mcp.json (token-pilot + context-mode)
477
+
478
+ MCP Tools (18):
479
+ smart_read, read_symbol, read_range, read_diff, read_for_edit, smart_read_many,
480
+ find_usages, find_unused, related_files, outline, project_overview, session_analytics,
481
+ code_audit, module_info, smart_diff, explore_area, smart_log, test_summary
465
482
  `);
466
483
  process.exit(0);
467
484
  }
package/package.json CHANGED
@@ -1,87 +1,87 @@
1
- {
2
- "name": "token-pilot",
3
- "version": "0.19.1",
4
- "description": "Save up to 80% tokens when AI reads code — MCP server for token-efficient code navigation, AST-aware structural reading instead of dumping full files into context window",
5
- "type": "module",
6
- "main": "dist/index.js",
7
- "bin": {
8
- "token-pilot": "dist/index.js"
9
- },
10
- "files": [
11
- "dist/**/*.js",
12
- "dist/**/*.d.ts",
13
- "start.sh",
14
- ".claude-plugin/",
15
- ".mcp.json",
16
- "skills/",
17
- "README.md",
18
- "CHANGELOG.md"
19
- ],
20
- "scripts": {
21
- "prebuild": "node --input-type=module -e \"import { rm } from 'node:fs/promises'; await rm('dist', { recursive: true, force: true });\"",
22
- "build": "tsc",
23
- "dev": "tsc --watch",
24
- "start": "node dist/index.js",
25
- "test": "vitest run",
26
- "test:coverage": "vitest run --coverage",
27
- "test:watch": "vitest",
28
- "lint": "tsc --noEmit",
29
- "prepublishOnly": "npm run build && node --input-type=module -e \"import { chmod } from 'node:fs/promises'; await chmod('dist/index.js', 0o755);\""
30
- },
31
- "keywords": [
32
- "mcp",
33
- "mcp-server",
34
- "model-context-protocol",
35
- "claude",
36
- "claude-code",
37
- "cursor",
38
- "codex",
39
- "cline",
40
- "ai-coding",
41
- "llm-tools",
42
- "token-savings",
43
- "token-reduction",
44
- "context-window",
45
- "context-optimization",
46
- "ast",
47
- "code-reading",
48
- "code-navigation",
49
- "smart-read",
50
- "developer-tools",
51
- "tree-sitter"
52
- ],
53
- "repository": {
54
- "type": "git",
55
- "url": "git+https://github.com/Digital-Threads/token-pilot.git"
56
- },
57
- "homepage": "https://github.com/Digital-Threads/token-pilot#readme",
58
- "bugs": {
59
- "url": "https://github.com/Digital-Threads/token-pilot/issues"
60
- },
61
- "mcpName": "io.github.Digital-Threads/token-pilot",
62
- "license": "MIT",
63
- "dependencies": {
64
- "@modelcontextprotocol/sdk": "^1.12.0",
65
- "chokidar": "^4.0.3"
66
- },
67
- "devDependencies": {
68
- "@vitest/coverage-v8": "^3.2.4",
69
- "@types/node": "^22.0.0",
70
- "typescript": "^5.7.0",
71
- "vitest": "^3.0.0"
72
- },
73
- "engines": {
74
- "node": ">=18.0.0"
75
- },
76
- "peerDependencies": {
77
- "ast-index": ">=0.1.0"
78
- },
79
- "peerDependenciesMeta": {
80
- "ast-index": {
81
- "optional": true
82
- }
83
- },
84
- "optionalDependencies": {
85
- "@ast-grep/cli": "^0.41.0"
86
- }
87
- }
1
+ {
2
+ "name": "token-pilot",
3
+ "version": "0.19.2",
4
+ "description": "Save up to 80% tokens when AI reads code — MCP server for token-efficient code navigation, AST-aware structural reading instead of dumping full files into context window",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "token-pilot": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist/**/*.js",
12
+ "dist/**/*.d.ts",
13
+ "start.sh",
14
+ ".claude-plugin/",
15
+ ".mcp.json",
16
+ "skills/",
17
+ "README.md",
18
+ "CHANGELOG.md"
19
+ ],
20
+ "scripts": {
21
+ "prebuild": "node --input-type=module -e \"import { rm } from 'node:fs/promises'; await rm('dist', { recursive: true, force: true });\"",
22
+ "build": "tsc",
23
+ "dev": "tsc --watch",
24
+ "start": "node dist/index.js",
25
+ "test": "vitest run",
26
+ "test:coverage": "vitest run --coverage",
27
+ "test:watch": "vitest",
28
+ "lint": "tsc --noEmit",
29
+ "prepublishOnly": "npm run build && node --input-type=module -e \"import { chmod } from 'node:fs/promises'; await chmod('dist/index.js', 0o755);\""
30
+ },
31
+ "keywords": [
32
+ "mcp",
33
+ "mcp-server",
34
+ "model-context-protocol",
35
+ "claude",
36
+ "claude-code",
37
+ "cursor",
38
+ "codex",
39
+ "cline",
40
+ "ai-coding",
41
+ "llm-tools",
42
+ "token-savings",
43
+ "token-reduction",
44
+ "context-window",
45
+ "context-optimization",
46
+ "ast",
47
+ "code-reading",
48
+ "code-navigation",
49
+ "smart-read",
50
+ "developer-tools",
51
+ "tree-sitter"
52
+ ],
53
+ "repository": {
54
+ "type": "git",
55
+ "url": "git+https://github.com/Digital-Threads/token-pilot.git"
56
+ },
57
+ "homepage": "https://github.com/Digital-Threads/token-pilot#readme",
58
+ "bugs": {
59
+ "url": "https://github.com/Digital-Threads/token-pilot/issues"
60
+ },
61
+ "mcpName": "io.github.Digital-Threads/token-pilot",
62
+ "license": "MIT",
63
+ "dependencies": {
64
+ "@modelcontextprotocol/sdk": "^1.12.0",
65
+ "chokidar": "^4.0.3"
66
+ },
67
+ "devDependencies": {
68
+ "@vitest/coverage-v8": "^3.2.4",
69
+ "@types/node": "^22.0.0",
70
+ "typescript": "^5.7.0",
71
+ "vitest": "^3.0.0"
72
+ },
73
+ "engines": {
74
+ "node": ">=18.0.0"
75
+ },
76
+ "peerDependencies": {
77
+ "ast-index": ">=0.1.0"
78
+ },
79
+ "peerDependenciesMeta": {
80
+ "ast-index": {
81
+ "optional": true
82
+ }
83
+ },
84
+ "optionalDependencies": {
85
+ "@ast-grep/cli": "^0.41.0"
86
+ }
87
+ }