token-pilot 0.46.1 → 0.47.1
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/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +48 -0
- package/agents/tp-api-surface-tracker.md +1 -1
- package/agents/tp-audit-scanner.md +1 -1
- package/agents/tp-commit-writer.md +1 -1
- package/agents/tp-context-engineer.md +1 -1
- package/agents/tp-dead-code-finder.md +1 -1
- package/agents/tp-debugger.md +1 -1
- package/agents/tp-dep-health.md +1 -1
- package/agents/tp-doc-writer.md +1 -1
- package/agents/tp-history-explorer.md +1 -1
- package/agents/tp-impact-analyzer.md +1 -1
- package/agents/tp-incident-timeline.md +1 -1
- package/agents/tp-incremental-builder.md +1 -1
- package/agents/tp-migration-scout.md +1 -1
- package/agents/tp-onboard.md +1 -1
- package/agents/tp-performance-profiler.md +1 -1
- package/agents/tp-pr-reviewer.md +1 -1
- package/agents/tp-refactor-planner.md +1 -1
- package/agents/tp-review-impact.md +1 -1
- package/agents/tp-run.md +1 -1
- package/agents/tp-session-restorer.md +1 -1
- package/agents/tp-ship-coordinator.md +1 -1
- package/agents/tp-spec-writer.md +1 -1
- package/agents/tp-test-coverage-gapper.md +1 -1
- package/agents/tp-test-triage.md +1 -1
- package/agents/tp-test-writer.md +1 -1
- package/dist/ast-index/client.d.ts +12 -1
- package/dist/ast-index/client.js +121 -18
- package/dist/ast-index/types.d.ts +69 -0
- package/dist/core/validation.d.ts +6 -0
- package/dist/core/validation.js +18 -0
- package/dist/handlers/explore.d.ts +17 -0
- package/dist/handlers/explore.js +90 -0
- package/dist/server/tool-definitions.d.ts +150 -0
- package/dist/server/tool-definitions.js +22 -0
- package/dist/server/tool-profiles.js +1 -0
- package/dist/server.js +36 -1
- package/package.json +2 -2
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Token Pilot — save 60-90% tokens when AI reads code",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.47.1"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "token-pilot",
|
|
14
14
|
"source": "./",
|
|
15
|
-
"description": "Reduces token consumption by 60-90% via AST-aware lazy file reading, structural symbol navigation, and cross-session tool-usage analytics.
|
|
16
|
-
"version": "0.
|
|
15
|
+
"description": "Reduces token consumption by 60-90% via AST-aware lazy file reading, structural symbol navigation, and cross-session tool-usage analytics. 24 MCP tools + 25 subagents + budget watchdog hooks.",
|
|
16
|
+
"version": "0.47.1",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "Digital-Threads"
|
|
19
19
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "token-pilot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.47.1",
|
|
4
4
|
"description": "Saves 60-90% tokens on AI code reading. AST-aware lazy reads, symbol navigation, find_usages, structural git diff/log, edit-safety guard, Task-routing matcher, cross-session telemetry (errors + diagnostics), 25 tp-* subagents tiered to haiku/sonnet/opus with budget watchdog.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Digital-Threads",
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,54 @@ All notable changes to Token Pilot will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.47.1] - 2026-06-24
|
|
9
|
+
|
|
10
|
+
### Fixed — gate AST_INDEX_WALK_UP on the `.git` marker (nested-worktree escape)
|
|
11
|
+
|
|
12
|
+
`exec()` set `AST_INDEX_WALK_UP=1` unconditionally. The flag tells ast-index to
|
|
13
|
+
traverse past nested VCS markers and reuse a parent-level index — it exists for
|
|
14
|
+
bare monorepo subdirs (no `.git`, no local DB). But forcing it when `projectRoot`
|
|
15
|
+
is itself a git repo/worktree root let a worktree nested under the main repo
|
|
16
|
+
(`main-repo/.worktrees/feature`) walk up past its own `.git` and **escape to the
|
|
17
|
+
main repo's index, returning the wrong files**. Now the flag is set only when
|
|
18
|
+
`projectRoot` has no `.git` marker of its own (a `.git` dir or worktree gitlink
|
|
19
|
+
file both count). See `docs/adr/0002-ast-index-multi-root-scoping.md` — also
|
|
20
|
+
records the deferred `--local`/subtree plan for multi-repo parents.
|
|
21
|
+
|
|
22
|
+
### Fixed — deterministic CLI tests under parallel sharding
|
|
23
|
+
|
|
24
|
+
`index.test.ts` / `installer.test.ts` now neutralise `CLAUDE_PROJECT_DIR` and
|
|
25
|
+
`CLAUDE_PLUGIN_ROOT` in setup so the git-detection and install assertions don't
|
|
26
|
+
flake when another suite leaks those env vars into a shared vitest worker.
|
|
27
|
+
|
|
28
|
+
## [0.47.0] - 2026-06-24
|
|
29
|
+
|
|
30
|
+
### Added — `explore` tool: one-shot ranked context + graph blast-radius
|
|
31
|
+
|
|
32
|
+
New MCP tool **`explore`** wraps ast-index 3.48's `explore` command: for a query
|
|
33
|
+
it returns ranked relevant symbols, the source heads of the top files, **graph
|
|
34
|
+
neighbours (callers + subclasses — the blast radius, via RWR over the
|
|
35
|
+
call/inheritance graph)**, and related test files — in a single compact block.
|
|
36
|
+
Replaces the common `find_usages` → `read_symbol` → `call_tree` chain with one
|
|
37
|
+
call. `graph` defaults on; `max_files` caps the source heads. Falls back to a
|
|
38
|
+
clear "requires ast-index >= 3.48" message when an older binary is resolved.
|
|
39
|
+
|
|
40
|
+
### Changed — bump `@ast-index/cli` to 3.48.1
|
|
41
|
+
|
|
42
|
+
Picks up the upstream TypeScript-indexing fix, the rebuild **swap-and-restore**
|
|
43
|
+
guard (a failed rebuild no longer wipes the index), memory caps, and FUSE-safe
|
|
44
|
+
canonicalisation. `npm audit` stays at 0 vulnerabilities.
|
|
45
|
+
|
|
46
|
+
### Changed — `buildIndex` trusts swap-and-restore
|
|
47
|
+
|
|
48
|
+
When a rebuild fails, `buildIndex` now checks for the index the binary preserved
|
|
49
|
+
and uses it (instead of throwing and falling back to raw reads). The lock-case
|
|
50
|
+
and generic-failure recovery paths are unified.
|
|
51
|
+
|
|
52
|
+
_Deferred:_ `--local` / subtree query scoping (to re-enable ast-index on
|
|
53
|
+
multi-repo / worktree parents instead of disabling it) needs a rooting-model
|
|
54
|
+
rework and ships separately.
|
|
55
|
+
|
|
8
56
|
## [0.46.1] - 2026-06-18
|
|
9
57
|
|
|
10
58
|
### Fixed — node:test (`node --test`) TAP output parsing
|
|
@@ -9,7 +9,7 @@ tools:
|
|
|
9
9
|
- mcp__token-pilot__read_symbol
|
|
10
10
|
- Bash
|
|
11
11
|
model: haiku
|
|
12
|
-
token_pilot_version: "0.
|
|
12
|
+
token_pilot_version: "0.47.1"
|
|
13
13
|
token_pilot_body_hash: dd184501203fa7f3c73f419c4ffbe33c4be75400cb64a7a51733a3fe23f6e085
|
|
14
14
|
requiredMcpServers:
|
|
15
15
|
- "token-pilot"
|
|
@@ -8,7 +8,7 @@ tools:
|
|
|
8
8
|
- mcp__token-pilot__test_summary
|
|
9
9
|
- mcp__token-pilot__outline
|
|
10
10
|
- Bash
|
|
11
|
-
token_pilot_version: "0.
|
|
11
|
+
token_pilot_version: "0.47.1"
|
|
12
12
|
token_pilot_body_hash: de64a406b5176de19f7422619c7de7949b1f28865f225402c9cea9255f377428
|
|
13
13
|
requiredMcpServers:
|
|
14
14
|
- "token-pilot"
|
package/agents/tp-debugger.md
CHANGED
package/agents/tp-dep-health.md
CHANGED
package/agents/tp-doc-writer.md
CHANGED
|
@@ -12,7 +12,7 @@ tools:
|
|
|
12
12
|
- mcp__token-pilot__read_symbols
|
|
13
13
|
- Read
|
|
14
14
|
model: sonnet
|
|
15
|
-
token_pilot_version: "0.
|
|
15
|
+
token_pilot_version: "0.47.1"
|
|
16
16
|
token_pilot_body_hash: 351a987e11eba63852f5431a16d8eb53104f4f689f82fdcc5a2bf4db948ba92f
|
|
17
17
|
requiredMcpServers:
|
|
18
18
|
- "token-pilot"
|
|
@@ -8,7 +8,7 @@ tools:
|
|
|
8
8
|
- mcp__token-pilot__read_symbol
|
|
9
9
|
- Bash
|
|
10
10
|
model: inherit
|
|
11
|
-
token_pilot_version: "0.
|
|
11
|
+
token_pilot_version: "0.47.1"
|
|
12
12
|
token_pilot_body_hash: de5722bfea374eaab096c1ae635c37879e7a91370ee3cd0532f4240be03c91eb
|
|
13
13
|
requiredMcpServers:
|
|
14
14
|
- "token-pilot"
|
package/agents/tp-onboard.md
CHANGED
|
@@ -10,7 +10,7 @@ tools:
|
|
|
10
10
|
- mcp__token-pilot__smart_read
|
|
11
11
|
- mcp__token-pilot__smart_read_many
|
|
12
12
|
- mcp__token-pilot__read_section
|
|
13
|
-
token_pilot_version: "0.
|
|
13
|
+
token_pilot_version: "0.47.1"
|
|
14
14
|
token_pilot_body_hash: 832e95633fbc8e9b0c10f3e540a327d4be062fb4b3f17a6cce6be13f414e2927
|
|
15
15
|
requiredMcpServers:
|
|
16
16
|
- "token-pilot"
|
package/agents/tp-pr-reviewer.md
CHANGED
|
@@ -11,7 +11,7 @@ tools:
|
|
|
11
11
|
- mcp__token-pilot__read_for_edit
|
|
12
12
|
- Read
|
|
13
13
|
model: sonnet
|
|
14
|
-
token_pilot_version: "0.
|
|
14
|
+
token_pilot_version: "0.47.1"
|
|
15
15
|
token_pilot_body_hash: f83f50d05b4f70285ae7afed2b1a406fc436df56e61a0aedbfb31edc7f2b6e66
|
|
16
16
|
requiredMcpServers:
|
|
17
17
|
- "token-pilot"
|
|
@@ -8,7 +8,7 @@ tools:
|
|
|
8
8
|
- mcp__token-pilot__outline
|
|
9
9
|
- mcp__token-pilot__read_symbol
|
|
10
10
|
model: sonnet
|
|
11
|
-
token_pilot_version: "0.
|
|
11
|
+
token_pilot_version: "0.47.1"
|
|
12
12
|
token_pilot_body_hash: c5f6fc122c89e16e5cf774045f92169ee3468555320b898171ba13eca5323550
|
|
13
13
|
requiredMcpServers:
|
|
14
14
|
- "token-pilot"
|
|
@@ -9,7 +9,7 @@ tools:
|
|
|
9
9
|
- mcp__token-pilot__module_info
|
|
10
10
|
- Bash
|
|
11
11
|
model: sonnet
|
|
12
|
-
token_pilot_version: "0.
|
|
12
|
+
token_pilot_version: "0.47.1"
|
|
13
13
|
token_pilot_body_hash: 8ef3c3341cbfed4eb8dd130126a9683edc57e378c92ff0ca764d584fd941c55c
|
|
14
14
|
requiredMcpServers:
|
|
15
15
|
- "token-pilot"
|
package/agents/tp-run.md
CHANGED
package/agents/tp-spec-writer.md
CHANGED
|
@@ -10,7 +10,7 @@ tools:
|
|
|
10
10
|
- mcp__token-pilot__test_summary
|
|
11
11
|
- Glob
|
|
12
12
|
- Grep
|
|
13
|
-
token_pilot_version: "0.
|
|
13
|
+
token_pilot_version: "0.47.1"
|
|
14
14
|
token_pilot_body_hash: be81eed53a3720d146cf89e4a14a7a56577633f7c84c234c412ab70d64c05b11
|
|
15
15
|
requiredMcpServers:
|
|
16
16
|
- "token-pilot"
|
package/agents/tp-test-triage.md
CHANGED
|
@@ -8,7 +8,7 @@ tools:
|
|
|
8
8
|
- mcp__token-pilot__find_usages
|
|
9
9
|
- mcp__token-pilot__read_symbol
|
|
10
10
|
model: sonnet
|
|
11
|
-
token_pilot_version: "0.
|
|
11
|
+
token_pilot_version: "0.47.1"
|
|
12
12
|
token_pilot_body_hash: 362ecf4cb03b059421ea26933473700900073dc38b3a7fe271208dfb1ae14f90
|
|
13
13
|
requiredMcpServers:
|
|
14
14
|
- "token-pilot"
|
package/agents/tp-test-writer.md
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import type { FileStructure } from "../types.js";
|
|
2
|
-
import type { AstIndexSymbolDetail, AstIndexSearchResult, AstIndexUsageResult, AstIndexImplementation, AstIndexHierarchyNode, AstIndexRefsResponse, AstIndexMapResponse, AstIndexConventionsResponse, AstIndexCallerEntry, AstIndexCallTreeNode, AstIndexChangedEntry, AstIndexUnusedSymbol, AstIndexImportEntry, AstIndexAgrepMatch, AstIndexTodoEntry, AstIndexDeprecatedEntry, AstIndexAnnotationEntry, AstIndexModuleEntry, AstIndexModuleDep, AstIndexUnusedDep, AstIndexModuleApi } from "./types.js";
|
|
2
|
+
import type { AstIndexSymbolDetail, AstIndexSearchResult, AstIndexUsageResult, AstIndexImplementation, AstIndexHierarchyNode, AstIndexRefsResponse, AstIndexMapResponse, AstIndexConventionsResponse, AstIndexCallerEntry, AstIndexCallTreeNode, AstIndexChangedEntry, AstIndexUnusedSymbol, AstIndexImportEntry, AstIndexAgrepMatch, AstIndexTodoEntry, AstIndexDeprecatedEntry, AstIndexAnnotationEntry, AstIndexModuleEntry, AstIndexModuleDep, AstIndexUnusedDep, AstIndexModuleApi, AstIndexExploreResult } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* True when `projectRoot` is itself a git repo or worktree root. A `.git`
|
|
5
|
+
* entry counts whether it's a directory (normal repo) or a file (worktree /
|
|
6
|
+
* submodule gitlink). Used to gate AST_INDEX_WALK_UP — see exec().
|
|
7
|
+
*/
|
|
8
|
+
export declare function computeHasGitMarker(projectRoot: string): boolean;
|
|
3
9
|
export declare class AstIndexClient {
|
|
4
10
|
private static readonly MAX_INDEX_FILES;
|
|
5
11
|
private binaryPath;
|
|
6
12
|
private projectRoot;
|
|
13
|
+
private hasGitMarker;
|
|
7
14
|
private indexed;
|
|
8
15
|
private indexOversized;
|
|
9
16
|
private indexDisabled;
|
|
@@ -34,6 +41,10 @@ export declare class AstIndexClient {
|
|
|
34
41
|
fuzzy?: boolean;
|
|
35
42
|
}): Promise<AstIndexSearchResult[]>;
|
|
36
43
|
usages(symbolName: string): Promise<AstIndexUsageResult[]>;
|
|
44
|
+
explore(query: string, options?: {
|
|
45
|
+
maxFiles?: number;
|
|
46
|
+
graph?: boolean;
|
|
47
|
+
}): Promise<AstIndexExploreResult>;
|
|
37
48
|
implementations(name: string): Promise<AstIndexImplementation[]>;
|
|
38
49
|
hierarchy(name: string, options?: {
|
|
39
50
|
inFile?: string;
|
package/dist/ast-index/client.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
2
4
|
import { promisify } from "node:util";
|
|
3
5
|
import { findBinary, installBinary } from "./binary-manager.js";
|
|
4
6
|
import { parseFileCount, parseOutlineText, parseImportsText, parseImplementationsText, parseHierarchyText, parseAgrepText, parseTodoText, parseDeprecatedText, parseAnnotationsText, parseModuleListText, parseModuleDepText, parseUnusedDepsText, parseModuleApiText, } from "./parser.js";
|
|
@@ -8,10 +10,21 @@ import { parsePythonRegex } from "./regex-parser-python.js";
|
|
|
8
10
|
const TS_JS_EXTENSIONS = new Set(["ts", "tsx", "js", "jsx", "mjs", "cjs"]);
|
|
9
11
|
const PYTHON_EXTENSIONS = new Set(["py", "pyw"]);
|
|
10
12
|
const execFileAsync = promisify(execFile);
|
|
13
|
+
/**
|
|
14
|
+
* True when `projectRoot` is itself a git repo or worktree root. A `.git`
|
|
15
|
+
* entry counts whether it's a directory (normal repo) or a file (worktree /
|
|
16
|
+
* submodule gitlink). Used to gate AST_INDEX_WALK_UP — see exec().
|
|
17
|
+
*/
|
|
18
|
+
export function computeHasGitMarker(projectRoot) {
|
|
19
|
+
return existsSync(resolve(projectRoot, ".git"));
|
|
20
|
+
}
|
|
11
21
|
export class AstIndexClient {
|
|
12
22
|
static MAX_INDEX_FILES = 50_000;
|
|
13
23
|
binaryPath = null;
|
|
14
24
|
projectRoot;
|
|
25
|
+
// True when projectRoot is itself a git repo/worktree root (has a `.git`
|
|
26
|
+
// marker). Gates AST_INDEX_WALK_UP in exec() — see the comment there.
|
|
27
|
+
hasGitMarker;
|
|
15
28
|
indexed = false;
|
|
16
29
|
indexOversized = false;
|
|
17
30
|
indexDisabled = false;
|
|
@@ -26,6 +39,7 @@ export class AstIndexClient {
|
|
|
26
39
|
periodicUpdateInFlight = false;
|
|
27
40
|
constructor(projectRoot, timeout = 5000, options) {
|
|
28
41
|
this.projectRoot = projectRoot;
|
|
42
|
+
this.hasGitMarker = computeHasGitMarker(projectRoot);
|
|
29
43
|
this.timeout = timeout;
|
|
30
44
|
this.configBinaryPath = options?.binaryPath ?? null;
|
|
31
45
|
this.autoInstall = options?.autoInstall ?? true;
|
|
@@ -128,16 +142,22 @@ export class AstIndexClient {
|
|
|
128
142
|
}
|
|
129
143
|
catch (buildErr) {
|
|
130
144
|
const errMsg = buildErr instanceof Error ? buildErr.message : String(buildErr);
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
145
|
+
// ast-index 3.46+ preserves the previous index when a rebuild aborts
|
|
146
|
+
// (swap-and-restore guard). Before giving up, query stats once: if a
|
|
147
|
+
// usable index survived, use it instead of losing all indexed tools and
|
|
148
|
+
// falling back to raw reads. Covers both the lock / already-running case
|
|
149
|
+
// and any generic rebuild failure the binary recovered from.
|
|
150
|
+
const lockCase = errMsg.includes("lock") || errMsg.includes("already running");
|
|
151
|
+
const count = parseFileCount(await this.exec(["--format", "json", "stats"]).catch(() => ""));
|
|
152
|
+
if (count > 0 && count <= AstIndexClient.MAX_INDEX_FILES) {
|
|
153
|
+
this.indexed = true;
|
|
154
|
+
console.error(lockCase
|
|
155
|
+
? `[token-pilot] ast-index: using existing index (${count} files, rebuild skipped due to lock)`
|
|
156
|
+
: `[token-pilot] ast-index: rebuild failed but previous index preserved (${count} files) — using it`);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (count > AstIndexClient.MAX_INDEX_FILES) {
|
|
160
|
+
return this.handleOversizedIndex(count);
|
|
141
161
|
}
|
|
142
162
|
console.error(`[token-pilot] ast-index: rebuild failed — ${errMsg}`);
|
|
143
163
|
throw buildErr;
|
|
@@ -328,6 +348,80 @@ export class AstIndexClient {
|
|
|
328
348
|
return [];
|
|
329
349
|
}
|
|
330
350
|
}
|
|
351
|
+
async explore(query, options) {
|
|
352
|
+
await this.ensureIndex();
|
|
353
|
+
const empty = {
|
|
354
|
+
query,
|
|
355
|
+
dominantLanguage: "",
|
|
356
|
+
symbols: [],
|
|
357
|
+
files: [],
|
|
358
|
+
neighbours: [],
|
|
359
|
+
tests: [],
|
|
360
|
+
};
|
|
361
|
+
const args = ["explore", query, "--format", "json"];
|
|
362
|
+
if (options?.maxFiles)
|
|
363
|
+
args.push("-f", String(options.maxFiles));
|
|
364
|
+
// Default graph ON — call/inheritance blast-radius is the value-add;
|
|
365
|
+
// callers opt out with graph: false.
|
|
366
|
+
if (options?.graph !== false)
|
|
367
|
+
args.push("--rwr");
|
|
368
|
+
try {
|
|
369
|
+
const result = await this.exec(args);
|
|
370
|
+
const parsed = JSON.parse(result);
|
|
371
|
+
return {
|
|
372
|
+
query: typeof parsed.query === "string" ? parsed.query : query,
|
|
373
|
+
dominantLanguage: typeof parsed.dominant_language === "string"
|
|
374
|
+
? parsed.dominant_language
|
|
375
|
+
: "",
|
|
376
|
+
symbols: Array.isArray(parsed.symbols)
|
|
377
|
+
? parsed.symbols.map((s) => ({
|
|
378
|
+
name: s.name,
|
|
379
|
+
kind: s.kind,
|
|
380
|
+
path: s.path,
|
|
381
|
+
line: s.line,
|
|
382
|
+
score: s.score,
|
|
383
|
+
vendor: s.vendor === true,
|
|
384
|
+
}))
|
|
385
|
+
: [],
|
|
386
|
+
files: Array.isArray(parsed.files)
|
|
387
|
+
? parsed.files.map((f) => ({
|
|
388
|
+
path: f.path,
|
|
389
|
+
line: f.line,
|
|
390
|
+
source: f.source,
|
|
391
|
+
}))
|
|
392
|
+
: [],
|
|
393
|
+
neighbours: Array.isArray(parsed.neighbours)
|
|
394
|
+
? parsed.neighbours.map((n) => ({
|
|
395
|
+
name: n.name,
|
|
396
|
+
kind: n.kind,
|
|
397
|
+
path: n.path,
|
|
398
|
+
line: n.line,
|
|
399
|
+
link: n.link,
|
|
400
|
+
}))
|
|
401
|
+
: [],
|
|
402
|
+
tests: Array.isArray(parsed.tests)
|
|
403
|
+
? parsed.tests.map((t) => ({
|
|
404
|
+
source: t.source,
|
|
405
|
+
tests: Array.isArray(t.tests) ? t.tests : [],
|
|
406
|
+
}))
|
|
407
|
+
: [],
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
catch (err) {
|
|
411
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
412
|
+
console.error(`[token-pilot] ast-index explore failed: ${msg}`);
|
|
413
|
+
// `explore` landed in ast-index 3.48. An older resolved binary reports
|
|
414
|
+
// "unrecognized subcommand 'explore'" — surface that instead of an
|
|
415
|
+
// indistinguishable empty result, so the caller knows to update.
|
|
416
|
+
if (/unrecognized subcommand|unexpected argument/.test(msg)) {
|
|
417
|
+
return {
|
|
418
|
+
...empty,
|
|
419
|
+
error: "explore requires ast-index >= 3.48 — update the binary",
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
return empty;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
331
425
|
async implementations(name) {
|
|
332
426
|
await this.ensureIndex();
|
|
333
427
|
try {
|
|
@@ -766,6 +860,7 @@ export class AstIndexClient {
|
|
|
766
860
|
}
|
|
767
861
|
updateProjectRoot(newRoot) {
|
|
768
862
|
this.projectRoot = newRoot;
|
|
863
|
+
this.hasGitMarker = computeHasGitMarker(newRoot);
|
|
769
864
|
this.indexed = false;
|
|
770
865
|
}
|
|
771
866
|
async exec(args, timeoutMs) {
|
|
@@ -791,14 +886,22 @@ export class AstIndexClient {
|
|
|
791
886
|
// ast-index v3.39+ honours AST_INDEX_WALK_UP=1 — read-commands then
|
|
792
887
|
// traverse past nested VCS markers (submodule .git, inner Cargo.toml,
|
|
793
888
|
// nested settings.gradle) to reuse a parent-level index if one exists.
|
|
794
|
-
//
|
|
795
|
-
//
|
|
796
|
-
//
|
|
797
|
-
//
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
889
|
+
// We only want this for a BARE SUBDIR that has no VCS marker of its own
|
|
890
|
+
// and no local DB — without walk-up, `search`/`outline` from a monorepo
|
|
891
|
+
// subdir stops at the nearest marker and finds nothing.
|
|
892
|
+
//
|
|
893
|
+
// We must NOT force it when projectRoot is itself a git repo/worktree
|
|
894
|
+
// root: a git worktree nested under the main repo (e.g.
|
|
895
|
+
// `main-repo/.worktrees/feature`) would walk up past its own `.git`
|
|
896
|
+
// marker and escape to the MAIN repo's parent index, returning the
|
|
897
|
+
// wrong files. Repo/worktree roots already own their index, so walk-up
|
|
898
|
+
// is at best a no-op and at worst that escape bug — skip it. When the
|
|
899
|
+
// user set AST_INDEX_WALK_UP themselves and a marker is present, we
|
|
900
|
+
// leave their value untouched (we only refrain from forcing it).
|
|
901
|
+
const env = { ...process.env };
|
|
902
|
+
if (!this.hasGitMarker) {
|
|
903
|
+
env.AST_INDEX_WALK_UP = "1";
|
|
904
|
+
}
|
|
802
905
|
if (this.astGrepBinDir) {
|
|
803
906
|
env.PATH = `${this.astGrepBinDir}:${process.env.PATH ?? ""}`;
|
|
804
907
|
}
|
|
@@ -208,4 +208,73 @@ export interface AstIndexModuleApi {
|
|
|
208
208
|
file: string;
|
|
209
209
|
line: number;
|
|
210
210
|
}
|
|
211
|
+
/** ast-index explore — one ranked symbol */
|
|
212
|
+
export interface AstIndexExploreSymbol {
|
|
213
|
+
name: string;
|
|
214
|
+
kind: string;
|
|
215
|
+
path: string;
|
|
216
|
+
line: number;
|
|
217
|
+
score: number;
|
|
218
|
+
vendor: boolean;
|
|
219
|
+
}
|
|
220
|
+
/** ast-index explore — one ranked file head (source is line-numbered) */
|
|
221
|
+
export interface AstIndexExploreFile {
|
|
222
|
+
path: string;
|
|
223
|
+
line: number;
|
|
224
|
+
source: string;
|
|
225
|
+
}
|
|
226
|
+
/** ast-index explore — one graph neighbour (blast radius, requires --rwr) */
|
|
227
|
+
export interface AstIndexExploreNeighbour {
|
|
228
|
+
name: string;
|
|
229
|
+
kind: string;
|
|
230
|
+
path: string;
|
|
231
|
+
line: number;
|
|
232
|
+
/** "caller" | "subclass" | string */
|
|
233
|
+
link: string;
|
|
234
|
+
}
|
|
235
|
+
/** ast-index explore — tests grouped by source file */
|
|
236
|
+
export interface AstIndexExploreTestGroup {
|
|
237
|
+
source: string;
|
|
238
|
+
tests: string[];
|
|
239
|
+
}
|
|
240
|
+
/** ast-index explore — mapped result */
|
|
241
|
+
export interface AstIndexExploreResult {
|
|
242
|
+
query: string;
|
|
243
|
+
dominantLanguage: string;
|
|
244
|
+
symbols: AstIndexExploreSymbol[];
|
|
245
|
+
files: AstIndexExploreFile[];
|
|
246
|
+
neighbours: AstIndexExploreNeighbour[];
|
|
247
|
+
tests: AstIndexExploreTestGroup[];
|
|
248
|
+
/** Set when the run failed (e.g. binary too old for `explore`). */
|
|
249
|
+
error?: string;
|
|
250
|
+
}
|
|
251
|
+
/** ast-index explore — raw json shape from the binary (snake_case) */
|
|
252
|
+
export interface AstIndexExploreRaw {
|
|
253
|
+
query?: string;
|
|
254
|
+
dominant_language?: string;
|
|
255
|
+
symbols?: Array<{
|
|
256
|
+
name: string;
|
|
257
|
+
kind: string;
|
|
258
|
+
path: string;
|
|
259
|
+
line: number;
|
|
260
|
+
score: number;
|
|
261
|
+
vendor?: boolean;
|
|
262
|
+
}>;
|
|
263
|
+
files?: Array<{
|
|
264
|
+
path: string;
|
|
265
|
+
line: number;
|
|
266
|
+
source: string;
|
|
267
|
+
}>;
|
|
268
|
+
neighbours?: Array<{
|
|
269
|
+
name: string;
|
|
270
|
+
kind: string;
|
|
271
|
+
path: string;
|
|
272
|
+
line: number;
|
|
273
|
+
link: string;
|
|
274
|
+
}>;
|
|
275
|
+
tests?: Array<{
|
|
276
|
+
source: string;
|
|
277
|
+
tests?: string[];
|
|
278
|
+
}>;
|
|
279
|
+
}
|
|
211
280
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -179,6 +179,12 @@ export interface ExploreAreaArgs {
|
|
|
179
179
|
include?: Array<"outline" | "imports" | "tests" | "changes">;
|
|
180
180
|
}
|
|
181
181
|
export declare function validateExploreAreaArgs(args: unknown): ExploreAreaArgs;
|
|
182
|
+
export interface ExploreArgs {
|
|
183
|
+
query: string;
|
|
184
|
+
max_files?: number;
|
|
185
|
+
graph?: boolean;
|
|
186
|
+
}
|
|
187
|
+
export declare function validateExploreArgs(args: unknown): ExploreArgs;
|
|
182
188
|
export interface SmartLogArgs {
|
|
183
189
|
path?: string;
|
|
184
190
|
count?: number;
|
package/dist/core/validation.js
CHANGED
|
@@ -533,6 +533,24 @@ export function validateExploreAreaArgs(args) {
|
|
|
533
533
|
}
|
|
534
534
|
return { path: a.path };
|
|
535
535
|
}
|
|
536
|
+
export function validateExploreArgs(args) {
|
|
537
|
+
if (!args || typeof args !== "object") {
|
|
538
|
+
throw new Error('Arguments must be an object with a "query" parameter.');
|
|
539
|
+
}
|
|
540
|
+
const a = args;
|
|
541
|
+
if (typeof a.query !== "string" || a.query.length === 0) {
|
|
542
|
+
throw new Error('Required parameter "query" must be a non-empty string.');
|
|
543
|
+
}
|
|
544
|
+
const max_files = optionalNumber(a.max_files, "max_files");
|
|
545
|
+
if (max_files !== undefined && max_files < 1) {
|
|
546
|
+
throw new Error('"max_files" must be at least 1.');
|
|
547
|
+
}
|
|
548
|
+
return {
|
|
549
|
+
query: a.query,
|
|
550
|
+
max_files,
|
|
551
|
+
graph: optionalBool(a.graph, "graph"),
|
|
552
|
+
};
|
|
553
|
+
}
|
|
536
554
|
export function validateSmartLogArgs(args) {
|
|
537
555
|
if (!args || typeof args !== "object")
|
|
538
556
|
return {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AstIndexClient } from "../ast-index/client.js";
|
|
2
|
+
import type { ExploreArgs } from "../core/validation.js";
|
|
3
|
+
export interface ExploreMeta {
|
|
4
|
+
query: string;
|
|
5
|
+
symbolCount: number;
|
|
6
|
+
fileCount: number;
|
|
7
|
+
neighbourCount: number;
|
|
8
|
+
testCount: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function handleExplore(args: ExploreArgs, projectRoot: string, astIndex: AstIndexClient): Promise<{
|
|
11
|
+
content: Array<{
|
|
12
|
+
type: "text";
|
|
13
|
+
text: string;
|
|
14
|
+
}>;
|
|
15
|
+
meta: ExploreMeta;
|
|
16
|
+
}>;
|
|
17
|
+
//# sourceMappingURL=explore.d.ts.map
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// ──────────────────────────────────────────────
|
|
2
|
+
// Constants
|
|
3
|
+
// ──────────────────────────────────────────────
|
|
4
|
+
const MAX_RANKED_SYMBOLS = 12;
|
|
5
|
+
// ──────────────────────────────────────────────
|
|
6
|
+
// Handler — one-shot ranked context + graph blast-radius.
|
|
7
|
+
// Mirrors the shape of handleExploreArea: build a compact, token-efficient
|
|
8
|
+
// text block and return it with lightweight meta.
|
|
9
|
+
// ──────────────────────────────────────────────
|
|
10
|
+
export async function handleExplore(args, projectRoot, astIndex) {
|
|
11
|
+
void projectRoot; // explore runs against the index root, not a path
|
|
12
|
+
const result = await astIndex.explore(args.query, {
|
|
13
|
+
maxFiles: args.max_files,
|
|
14
|
+
graph: args.graph,
|
|
15
|
+
});
|
|
16
|
+
const lines = [];
|
|
17
|
+
lines.push(`# explore: "${result.query}" (lang: ${result.dominantLanguage || "?"})`);
|
|
18
|
+
// Ranked symbols
|
|
19
|
+
if (result.symbols.length > 0) {
|
|
20
|
+
lines.push("");
|
|
21
|
+
lines.push("## Ranked symbols");
|
|
22
|
+
for (const s of result.symbols.slice(0, MAX_RANKED_SYMBOLS)) {
|
|
23
|
+
const vendorTag = s.vendor ? " [vendor]" : "";
|
|
24
|
+
lines.push(`${Math.round(s.score)} ${s.kind} ${s.name} ${s.path}:${s.line}${vendorTag}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Source — file heads (source is already line-numbered)
|
|
28
|
+
if (result.files.length > 0) {
|
|
29
|
+
lines.push("");
|
|
30
|
+
lines.push("## Source");
|
|
31
|
+
for (const f of result.files) {
|
|
32
|
+
lines.push(`${f.path}:${f.line}`);
|
|
33
|
+
lines.push("```");
|
|
34
|
+
lines.push(f.source.replace(/\n+$/, ""));
|
|
35
|
+
lines.push("```");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Graph neighbours (blast radius) — only with --rwr
|
|
39
|
+
if (result.neighbours.length > 0) {
|
|
40
|
+
lines.push("");
|
|
41
|
+
lines.push("## Graph neighbours (blast radius)");
|
|
42
|
+
for (const n of result.neighbours) {
|
|
43
|
+
lines.push(`${n.link} ${n.kind} ${n.name} ${n.path}:${n.line}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Tests grouped by source
|
|
47
|
+
if (result.tests.length > 0) {
|
|
48
|
+
lines.push("");
|
|
49
|
+
lines.push("## Tests");
|
|
50
|
+
for (const t of result.tests) {
|
|
51
|
+
lines.push(`${t.source}:`);
|
|
52
|
+
for (const test of t.tests) {
|
|
53
|
+
lines.push(` ${test}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const empty = result.symbols.length === 0 &&
|
|
58
|
+
result.files.length === 0 &&
|
|
59
|
+
result.neighbours.length === 0 &&
|
|
60
|
+
result.tests.length === 0;
|
|
61
|
+
if (empty) {
|
|
62
|
+
const reason = result.error ?? "No results — index unavailable or query matched nothing.";
|
|
63
|
+
return {
|
|
64
|
+
content: [
|
|
65
|
+
{
|
|
66
|
+
type: "text",
|
|
67
|
+
text: `# explore: "${result.query}"\n\n${reason}`,
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
meta: {
|
|
71
|
+
query: result.query,
|
|
72
|
+
symbolCount: 0,
|
|
73
|
+
fileCount: 0,
|
|
74
|
+
neighbourCount: 0,
|
|
75
|
+
testCount: 0,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
81
|
+
meta: {
|
|
82
|
+
query: result.query,
|
|
83
|
+
symbolCount: result.symbols.length,
|
|
84
|
+
fileCount: result.files.length,
|
|
85
|
+
neighbourCount: result.neighbours.length,
|
|
86
|
+
testCount: result.tests.reduce((n, t) => n + t.tests.length, 0),
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=explore.js.map
|
|
@@ -100,6 +100,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
100
100
|
viaKind?: undefined;
|
|
101
101
|
format?: undefined;
|
|
102
102
|
ref?: undefined;
|
|
103
|
+
query?: undefined;
|
|
104
|
+
max_files?: undefined;
|
|
105
|
+
graph?: undefined;
|
|
103
106
|
count?: undefined;
|
|
104
107
|
command?: undefined;
|
|
105
108
|
runner?: undefined;
|
|
@@ -191,6 +194,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
191
194
|
viaKind?: undefined;
|
|
192
195
|
format?: undefined;
|
|
193
196
|
ref?: undefined;
|
|
197
|
+
query?: undefined;
|
|
198
|
+
max_files?: undefined;
|
|
199
|
+
graph?: undefined;
|
|
194
200
|
count?: undefined;
|
|
195
201
|
command?: undefined;
|
|
196
202
|
runner?: undefined;
|
|
@@ -276,6 +282,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
276
282
|
viaKind?: undefined;
|
|
277
283
|
format?: undefined;
|
|
278
284
|
ref?: undefined;
|
|
285
|
+
query?: undefined;
|
|
286
|
+
max_files?: undefined;
|
|
287
|
+
graph?: undefined;
|
|
279
288
|
count?: undefined;
|
|
280
289
|
command?: undefined;
|
|
281
290
|
runner?: undefined;
|
|
@@ -357,6 +366,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
357
366
|
viaKind?: undefined;
|
|
358
367
|
format?: undefined;
|
|
359
368
|
ref?: undefined;
|
|
369
|
+
query?: undefined;
|
|
370
|
+
max_files?: undefined;
|
|
371
|
+
graph?: undefined;
|
|
360
372
|
count?: undefined;
|
|
361
373
|
command?: undefined;
|
|
362
374
|
runner?: undefined;
|
|
@@ -429,6 +441,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
429
441
|
viaKind?: undefined;
|
|
430
442
|
format?: undefined;
|
|
431
443
|
ref?: undefined;
|
|
444
|
+
query?: undefined;
|
|
445
|
+
max_files?: undefined;
|
|
446
|
+
graph?: undefined;
|
|
432
447
|
count?: undefined;
|
|
433
448
|
command?: undefined;
|
|
434
449
|
runner?: undefined;
|
|
@@ -501,6 +516,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
501
516
|
viaKind?: undefined;
|
|
502
517
|
format?: undefined;
|
|
503
518
|
ref?: undefined;
|
|
519
|
+
query?: undefined;
|
|
520
|
+
max_files?: undefined;
|
|
521
|
+
graph?: undefined;
|
|
504
522
|
count?: undefined;
|
|
505
523
|
command?: undefined;
|
|
506
524
|
runner?: undefined;
|
|
@@ -597,6 +615,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
597
615
|
viaKind?: undefined;
|
|
598
616
|
format?: undefined;
|
|
599
617
|
ref?: undefined;
|
|
618
|
+
query?: undefined;
|
|
619
|
+
max_files?: undefined;
|
|
620
|
+
graph?: undefined;
|
|
600
621
|
count?: undefined;
|
|
601
622
|
command?: undefined;
|
|
602
623
|
runner?: undefined;
|
|
@@ -678,6 +699,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
678
699
|
viaKind?: undefined;
|
|
679
700
|
format?: undefined;
|
|
680
701
|
ref?: undefined;
|
|
702
|
+
query?: undefined;
|
|
703
|
+
max_files?: undefined;
|
|
704
|
+
graph?: undefined;
|
|
681
705
|
count?: undefined;
|
|
682
706
|
command?: undefined;
|
|
683
707
|
runner?: undefined;
|
|
@@ -768,6 +792,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
768
792
|
viaKind?: undefined;
|
|
769
793
|
format?: undefined;
|
|
770
794
|
ref?: undefined;
|
|
795
|
+
query?: undefined;
|
|
796
|
+
max_files?: undefined;
|
|
797
|
+
graph?: undefined;
|
|
771
798
|
count?: undefined;
|
|
772
799
|
command?: undefined;
|
|
773
800
|
runner?: undefined;
|
|
@@ -841,6 +868,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
841
868
|
viaKind?: undefined;
|
|
842
869
|
format?: undefined;
|
|
843
870
|
ref?: undefined;
|
|
871
|
+
query?: undefined;
|
|
872
|
+
max_files?: undefined;
|
|
873
|
+
graph?: undefined;
|
|
844
874
|
count?: undefined;
|
|
845
875
|
command?: undefined;
|
|
846
876
|
runner?: undefined;
|
|
@@ -910,6 +940,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
910
940
|
viaKind?: undefined;
|
|
911
941
|
format?: undefined;
|
|
912
942
|
ref?: undefined;
|
|
943
|
+
query?: undefined;
|
|
944
|
+
max_files?: undefined;
|
|
945
|
+
graph?: undefined;
|
|
913
946
|
count?: undefined;
|
|
914
947
|
command?: undefined;
|
|
915
948
|
runner?: undefined;
|
|
@@ -985,6 +1018,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
985
1018
|
viaKind?: undefined;
|
|
986
1019
|
format?: undefined;
|
|
987
1020
|
ref?: undefined;
|
|
1021
|
+
query?: undefined;
|
|
1022
|
+
max_files?: undefined;
|
|
1023
|
+
graph?: undefined;
|
|
988
1024
|
count?: undefined;
|
|
989
1025
|
command?: undefined;
|
|
990
1026
|
runner?: undefined;
|
|
@@ -1054,6 +1090,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
1054
1090
|
viaKind?: undefined;
|
|
1055
1091
|
format?: undefined;
|
|
1056
1092
|
ref?: undefined;
|
|
1093
|
+
query?: undefined;
|
|
1094
|
+
max_files?: undefined;
|
|
1095
|
+
graph?: undefined;
|
|
1057
1096
|
count?: undefined;
|
|
1058
1097
|
command?: undefined;
|
|
1059
1098
|
runner?: undefined;
|
|
@@ -1126,6 +1165,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
1126
1165
|
viaKind?: undefined;
|
|
1127
1166
|
format?: undefined;
|
|
1128
1167
|
ref?: undefined;
|
|
1168
|
+
query?: undefined;
|
|
1169
|
+
max_files?: undefined;
|
|
1170
|
+
graph?: undefined;
|
|
1129
1171
|
count?: undefined;
|
|
1130
1172
|
command?: undefined;
|
|
1131
1173
|
runner?: undefined;
|
|
@@ -1201,6 +1243,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
1201
1243
|
viaKind?: undefined;
|
|
1202
1244
|
format?: undefined;
|
|
1203
1245
|
ref?: undefined;
|
|
1246
|
+
query?: undefined;
|
|
1247
|
+
max_files?: undefined;
|
|
1248
|
+
graph?: undefined;
|
|
1204
1249
|
count?: undefined;
|
|
1205
1250
|
command?: undefined;
|
|
1206
1251
|
runner?: undefined;
|
|
@@ -1283,6 +1328,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
1283
1328
|
viaKind?: undefined;
|
|
1284
1329
|
format?: undefined;
|
|
1285
1330
|
ref?: undefined;
|
|
1331
|
+
query?: undefined;
|
|
1332
|
+
max_files?: undefined;
|
|
1333
|
+
graph?: undefined;
|
|
1286
1334
|
count?: undefined;
|
|
1287
1335
|
command?: undefined;
|
|
1288
1336
|
runner?: undefined;
|
|
@@ -1356,6 +1404,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
1356
1404
|
viaKind?: undefined;
|
|
1357
1405
|
format?: undefined;
|
|
1358
1406
|
ref?: undefined;
|
|
1407
|
+
query?: undefined;
|
|
1408
|
+
max_files?: undefined;
|
|
1409
|
+
graph?: undefined;
|
|
1359
1410
|
count?: undefined;
|
|
1360
1411
|
command?: undefined;
|
|
1361
1412
|
runner?: undefined;
|
|
@@ -1445,6 +1496,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
1445
1496
|
pattern?: undefined;
|
|
1446
1497
|
name?: undefined;
|
|
1447
1498
|
ref?: undefined;
|
|
1499
|
+
query?: undefined;
|
|
1500
|
+
max_files?: undefined;
|
|
1501
|
+
graph?: undefined;
|
|
1448
1502
|
count?: undefined;
|
|
1449
1503
|
command?: undefined;
|
|
1450
1504
|
runner?: undefined;
|
|
@@ -1521,6 +1575,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
1521
1575
|
maxDepth?: undefined;
|
|
1522
1576
|
viaKind?: undefined;
|
|
1523
1577
|
format?: undefined;
|
|
1578
|
+
query?: undefined;
|
|
1579
|
+
max_files?: undefined;
|
|
1580
|
+
graph?: undefined;
|
|
1524
1581
|
count?: undefined;
|
|
1525
1582
|
command?: undefined;
|
|
1526
1583
|
runner?: undefined;
|
|
@@ -1597,6 +1654,87 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
1597
1654
|
viaKind?: undefined;
|
|
1598
1655
|
format?: undefined;
|
|
1599
1656
|
ref?: undefined;
|
|
1657
|
+
query?: undefined;
|
|
1658
|
+
max_files?: undefined;
|
|
1659
|
+
graph?: undefined;
|
|
1660
|
+
count?: undefined;
|
|
1661
|
+
command?: undefined;
|
|
1662
|
+
runner?: undefined;
|
|
1663
|
+
timeout?: undefined;
|
|
1664
|
+
goal?: undefined;
|
|
1665
|
+
decisions?: undefined;
|
|
1666
|
+
confirmed?: undefined;
|
|
1667
|
+
files?: undefined;
|
|
1668
|
+
blocked?: undefined;
|
|
1669
|
+
next?: undefined;
|
|
1670
|
+
sessionId?: undefined;
|
|
1671
|
+
};
|
|
1672
|
+
required: string[];
|
|
1673
|
+
};
|
|
1674
|
+
} | {
|
|
1675
|
+
name: string;
|
|
1676
|
+
description: string;
|
|
1677
|
+
inputSchema: {
|
|
1678
|
+
type: "object";
|
|
1679
|
+
properties: {
|
|
1680
|
+
query: {
|
|
1681
|
+
type: string;
|
|
1682
|
+
description: string;
|
|
1683
|
+
};
|
|
1684
|
+
max_files: {
|
|
1685
|
+
type: string;
|
|
1686
|
+
description: string;
|
|
1687
|
+
};
|
|
1688
|
+
graph: {
|
|
1689
|
+
type: string;
|
|
1690
|
+
description: string;
|
|
1691
|
+
};
|
|
1692
|
+
path?: undefined;
|
|
1693
|
+
show_imports?: undefined;
|
|
1694
|
+
show_docs?: undefined;
|
|
1695
|
+
depth?: undefined;
|
|
1696
|
+
scope?: undefined;
|
|
1697
|
+
max_tokens?: undefined;
|
|
1698
|
+
session_id?: undefined;
|
|
1699
|
+
force?: undefined;
|
|
1700
|
+
symbol?: undefined;
|
|
1701
|
+
context_before?: undefined;
|
|
1702
|
+
context_after?: undefined;
|
|
1703
|
+
show?: undefined;
|
|
1704
|
+
include_edit_context?: undefined;
|
|
1705
|
+
symbols?: undefined;
|
|
1706
|
+
start_line?: undefined;
|
|
1707
|
+
end_line?: undefined;
|
|
1708
|
+
heading?: undefined;
|
|
1709
|
+
context_lines?: undefined;
|
|
1710
|
+
line?: undefined;
|
|
1711
|
+
context?: undefined;
|
|
1712
|
+
include_callers?: undefined;
|
|
1713
|
+
include_tests?: undefined;
|
|
1714
|
+
include_changes?: undefined;
|
|
1715
|
+
section?: undefined;
|
|
1716
|
+
paths?: undefined;
|
|
1717
|
+
kind?: undefined;
|
|
1718
|
+
limit?: undefined;
|
|
1719
|
+
lang?: undefined;
|
|
1720
|
+
mode?: undefined;
|
|
1721
|
+
include?: undefined;
|
|
1722
|
+
recursive?: undefined;
|
|
1723
|
+
max_depth?: undefined;
|
|
1724
|
+
verbose?: undefined;
|
|
1725
|
+
module?: undefined;
|
|
1726
|
+
export_only?: undefined;
|
|
1727
|
+
check?: undefined;
|
|
1728
|
+
pattern?: undefined;
|
|
1729
|
+
name?: undefined;
|
|
1730
|
+
from?: undefined;
|
|
1731
|
+
to?: undefined;
|
|
1732
|
+
all?: undefined;
|
|
1733
|
+
maxPaths?: undefined;
|
|
1734
|
+
maxDepth?: undefined;
|
|
1735
|
+
viaKind?: undefined;
|
|
1736
|
+
format?: undefined;
|
|
1737
|
+
ref?: undefined;
|
|
1600
1738
|
count?: undefined;
|
|
1601
1739
|
command?: undefined;
|
|
1602
1740
|
runner?: undefined;
|
|
@@ -1673,6 +1811,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
1673
1811
|
maxDepth?: undefined;
|
|
1674
1812
|
viaKind?: undefined;
|
|
1675
1813
|
format?: undefined;
|
|
1814
|
+
query?: undefined;
|
|
1815
|
+
max_files?: undefined;
|
|
1816
|
+
graph?: undefined;
|
|
1676
1817
|
command?: undefined;
|
|
1677
1818
|
runner?: undefined;
|
|
1678
1819
|
timeout?: undefined;
|
|
@@ -1751,6 +1892,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
1751
1892
|
viaKind?: undefined;
|
|
1752
1893
|
format?: undefined;
|
|
1753
1894
|
ref?: undefined;
|
|
1895
|
+
query?: undefined;
|
|
1896
|
+
max_files?: undefined;
|
|
1897
|
+
graph?: undefined;
|
|
1754
1898
|
count?: undefined;
|
|
1755
1899
|
goal?: undefined;
|
|
1756
1900
|
decisions?: undefined;
|
|
@@ -1847,6 +1991,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
1847
1991
|
viaKind?: undefined;
|
|
1848
1992
|
format?: undefined;
|
|
1849
1993
|
ref?: undefined;
|
|
1994
|
+
query?: undefined;
|
|
1995
|
+
max_files?: undefined;
|
|
1996
|
+
graph?: undefined;
|
|
1850
1997
|
count?: undefined;
|
|
1851
1998
|
command?: undefined;
|
|
1852
1999
|
runner?: undefined;
|
|
@@ -1911,6 +2058,9 @@ export declare const TOOL_DEFINITIONS: ({
|
|
|
1911
2058
|
viaKind?: undefined;
|
|
1912
2059
|
format?: undefined;
|
|
1913
2060
|
ref?: undefined;
|
|
2061
|
+
query?: undefined;
|
|
2062
|
+
max_files?: undefined;
|
|
2063
|
+
graph?: undefined;
|
|
1914
2064
|
count?: undefined;
|
|
1915
2065
|
command?: undefined;
|
|
1916
2066
|
runner?: undefined;
|
|
@@ -670,6 +670,28 @@ export const TOOL_DEFINITIONS = [
|
|
|
670
670
|
required: ["path"],
|
|
671
671
|
},
|
|
672
672
|
},
|
|
673
|
+
{
|
|
674
|
+
name: "explore",
|
|
675
|
+
description: "One-shot ranked context + call/inheritance graph blast-radius for a query. Returns ranked symbols, the source heads of the top-ranked files, graph neighbours (callers + subclasses — the blast radius), and related test files in a single compact block. Use INSTEAD OF separate find_usages + read_symbol + call_tree when you need to understand an area fast — cheaper than chaining those three.",
|
|
676
|
+
inputSchema: {
|
|
677
|
+
type: "object",
|
|
678
|
+
properties: {
|
|
679
|
+
query: {
|
|
680
|
+
type: "string",
|
|
681
|
+
description: "Search terms (the binary splits the string into terms itself), e.g. \"AstIndexClient buildIndex\"",
|
|
682
|
+
},
|
|
683
|
+
max_files: {
|
|
684
|
+
type: "number",
|
|
685
|
+
description: "Cap on the number of source file heads returned (default: binary's own limit)",
|
|
686
|
+
},
|
|
687
|
+
graph: {
|
|
688
|
+
type: "boolean",
|
|
689
|
+
description: "Include call/inheritance graph neighbours (blast radius). Default: true. Set false to skip the graph walk.",
|
|
690
|
+
},
|
|
691
|
+
},
|
|
692
|
+
required: ["query"],
|
|
693
|
+
},
|
|
694
|
+
},
|
|
673
695
|
{
|
|
674
696
|
name: "smart_log",
|
|
675
697
|
description: "Use INSTEAD OF raw git log. Structured commit history with category detection (feat/fix/refactor/docs), file stats, author breakdown. Filters by path and ref. HEADS UP: two verification runs measured this tool at ~39% token reduction (borderline — vs 95-99% for outline/smart_diff). Cumulative data being gathered — tool may be dropped or redesigned in v0.30.0 if numbers don't improve. Prefer scoping with `path` or `count` to tighten savings.",
|
package/dist/server.js
CHANGED
|
@@ -38,6 +38,7 @@ import { handleModuleInfo } from "./handlers/module-info.js";
|
|
|
38
38
|
import { handleModuleRoute } from "./handlers/module-route.js";
|
|
39
39
|
import { handleSmartDiff } from "./handlers/smart-diff.js";
|
|
40
40
|
import { handleExploreArea } from "./handlers/explore-area.js";
|
|
41
|
+
import { handleExplore } from "./handlers/explore.js";
|
|
41
42
|
import { handleSmartLog } from "./handlers/smart-log.js";
|
|
42
43
|
import { handleTestSummary } from "./handlers/test-summary.js";
|
|
43
44
|
import { handleSessionSnapshot } from "./handlers/session-snapshot.js";
|
|
@@ -52,7 +53,7 @@ import { getMcpInstructions, TOOL_DEFINITIONS, } from "./server/tool-definitions
|
|
|
52
53
|
import { filterToolsByProfile, parseProfileEnv, } from "./server/tool-profiles.js";
|
|
53
54
|
import { STRICT_SMART_READ_MAX_TOKENS, STRICT_EXPLORE_AREA_INCLUDE, } from "./server/enforcement-mode.js";
|
|
54
55
|
import { createTokenEstimates } from "./server/token-estimates.js";
|
|
55
|
-
import { validateSmartReadArgs, validateReadSymbolArgs, validateReadSymbolsArgs, validateReadRangeArgs, validateReadDiffArgs, validateFindUsagesArgs, validateSmartReadManyArgs, validateReadForEditArgs, validateRelatedFilesArgs, validateOutlineArgs, validateFindUnusedArgs, validateCallTreeArgs, validateCodeAuditArgs, validateProjectOverviewArgs, validateModuleInfoArgs, validateModuleRouteArgs, validateSmartDiffArgs, validateExploreAreaArgs, validateSmartLogArgs, validateTestSummaryArgs, validateReadSectionArgs, } from "./core/validation.js";
|
|
56
|
+
import { validateSmartReadArgs, validateReadSymbolArgs, validateReadSymbolsArgs, validateReadRangeArgs, validateReadDiffArgs, validateFindUsagesArgs, validateSmartReadManyArgs, validateReadForEditArgs, validateRelatedFilesArgs, validateOutlineArgs, validateFindUnusedArgs, validateCallTreeArgs, validateCodeAuditArgs, validateProjectOverviewArgs, validateModuleInfoArgs, validateModuleRouteArgs, validateSmartDiffArgs, validateExploreAreaArgs, validateExploreArgs, validateSmartLogArgs, validateTestSummaryArgs, validateReadSectionArgs, } from "./core/validation.js";
|
|
56
57
|
export async function createServer(projectRoot, options) {
|
|
57
58
|
const mode = options?.enforcementMode ?? "deny";
|
|
58
59
|
// v0.43.0 — the real Claude Code session id. CC exports it to every
|
|
@@ -967,6 +968,40 @@ export async function createServer(projectRoot, options) {
|
|
|
967
968
|
}
|
|
968
969
|
return eaResult;
|
|
969
970
|
}
|
|
971
|
+
case "explore": {
|
|
972
|
+
const exArgs = validateExploreArgs(args);
|
|
973
|
+
const cachedEx = sessionCache?.get("explore", exArgs);
|
|
974
|
+
if (cachedEx) {
|
|
975
|
+
recordWithTrace({
|
|
976
|
+
tool: "explore",
|
|
977
|
+
path: exArgs.query,
|
|
978
|
+
tokensReturned: cachedEx.tokenEstimate,
|
|
979
|
+
tokensWouldBe: cachedEx.tokensWouldBe ?? cachedEx.tokenEstimate,
|
|
980
|
+
timestamp: Date.now(),
|
|
981
|
+
sessionCacheHit: true,
|
|
982
|
+
savingsCategory: "cache",
|
|
983
|
+
args: exArgs,
|
|
984
|
+
});
|
|
985
|
+
return cachedEx.result;
|
|
986
|
+
}
|
|
987
|
+
const exResult = await handleExplore(exArgs, projectRoot, astIndex);
|
|
988
|
+
const exText = exResult.content[0]?.text ?? "";
|
|
989
|
+
const exTokens = estimateTokens(exText);
|
|
990
|
+
// explore replaces a find_usages + read_symbol + call_tree chain;
|
|
991
|
+
// approximate that baseline as ~3x the compacted output.
|
|
992
|
+
const exWouldBe = exTokens * 3;
|
|
993
|
+
sessionCache?.set("explore", exArgs, exResult, { dependsOnAst: true }, exTokens, exWouldBe || exTokens);
|
|
994
|
+
recordWithTrace({
|
|
995
|
+
tool: "explore",
|
|
996
|
+
path: exArgs.query,
|
|
997
|
+
tokensReturned: exTokens,
|
|
998
|
+
tokensWouldBe: exWouldBe || exTokens,
|
|
999
|
+
timestamp: Date.now(),
|
|
1000
|
+
savingsCategory: "compression",
|
|
1001
|
+
args: exArgs,
|
|
1002
|
+
});
|
|
1003
|
+
return exResult;
|
|
1004
|
+
}
|
|
970
1005
|
case "smart_log": {
|
|
971
1006
|
const slArgs = validateSmartLogArgs(args);
|
|
972
1007
|
// v0.30.0 strict mode: bound count to 20 when caller didn't set it.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "token-pilot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.47.1",
|
|
4
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
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"mcpName": "io.github.Digital-Threads/token-pilot",
|
|
68
68
|
"license": "MIT",
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@ast-index/cli": "^3.
|
|
70
|
+
"@ast-index/cli": "^3.48.1",
|
|
71
71
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
72
72
|
"chokidar": "^4.0.3"
|
|
73
73
|
},
|