token-pilot 0.42.3 → 0.43.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.
Files changed (44) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/.claude-plugin/plugin.json +4 -2
  3. package/CHANGELOG.md +101 -0
  4. package/README.md +1 -1
  5. package/agents/tp-api-surface-tracker.md +1 -1
  6. package/agents/tp-audit-scanner.md +1 -1
  7. package/agents/tp-commit-writer.md +1 -1
  8. package/agents/tp-context-engineer.md +1 -1
  9. package/agents/tp-dead-code-finder.md +1 -1
  10. package/agents/tp-debugger.md +1 -1
  11. package/agents/tp-dep-health.md +1 -1
  12. package/agents/tp-doc-writer.md +1 -1
  13. package/agents/tp-history-explorer.md +1 -1
  14. package/agents/tp-impact-analyzer.md +1 -1
  15. package/agents/tp-incident-timeline.md +1 -1
  16. package/agents/tp-incremental-builder.md +1 -1
  17. package/agents/tp-migration-scout.md +1 -1
  18. package/agents/tp-onboard.md +1 -1
  19. package/agents/tp-performance-profiler.md +1 -1
  20. package/agents/tp-pr-reviewer.md +1 -1
  21. package/agents/tp-refactor-planner.md +1 -1
  22. package/agents/tp-review-impact.md +1 -1
  23. package/agents/tp-run.md +1 -1
  24. package/agents/tp-session-restorer.md +1 -1
  25. package/agents/tp-ship-coordinator.md +1 -1
  26. package/agents/tp-spec-writer.md +1 -1
  27. package/agents/tp-test-coverage-gapper.md +1 -1
  28. package/agents/tp-test-triage.md +1 -1
  29. package/agents/tp-test-writer.md +1 -1
  30. package/dist/ast-index/client.d.ts +16 -0
  31. package/dist/ast-index/client.js +30 -0
  32. package/dist/core/validation.d.ts +17 -0
  33. package/dist/core/validation.js +54 -0
  34. package/dist/handlers/module-route.d.ts +20 -0
  35. package/dist/handlers/module-route.js +73 -0
  36. package/dist/index.js +3 -3
  37. package/dist/server/tool-definitions.d.ts +250 -0
  38. package/dist/server/tool-definitions.js +40 -0
  39. package/dist/server.js +42 -2
  40. package/docs/tools.md +2 -1
  41. package/package.json +1 -1
  42. package/skills/guide/SKILL.md +4 -0
  43. package/skills/install/SKILL.md +4 -0
  44. package/skills/stats/SKILL.md +3 -0
@@ -5,15 +5,15 @@
5
5
  "email": "shahinyanm@gmail.com"
6
6
  },
7
7
  "metadata": {
8
- "description": "Token Pilot \u2014 save 60-90% tokens when AI reads code",
9
- "version": "0.42.3"
8
+ "description": "Token Pilot save 60-90% tokens when AI reads code",
9
+ "version": "0.43.0"
10
10
  },
11
11
  "plugins": [
12
12
  {
13
13
  "name": "token-pilot",
14
14
  "source": "./",
15
15
  "description": "Reduces token consumption by 60-90% via AST-aware lazy file reading, structural symbol navigation, and cross-session tool-usage analytics. 22 MCP tools + 19 subagents + budget watchdog hooks.",
16
- "version": "0.42.3",
16
+ "version": "0.43.0",
17
17
  "author": {
18
18
  "name": "Digital-Threads"
19
19
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "token-pilot",
3
- "version": "0.42.3",
3
+ "version": "0.43.0",
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",
@@ -21,7 +21,9 @@
21
21
  "mcpServers": {
22
22
  "token-pilot": {
23
23
  "command": "sh",
24
- "args": ["${CLAUDE_PLUGIN_ROOT}/start.sh"]
24
+ "args": [
25
+ "${CLAUDE_PLUGIN_ROOT}/start.sh"
26
+ ]
25
27
  }
26
28
  }
27
29
  }
package/CHANGELOG.md CHANGED
@@ -5,6 +5,107 @@ 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.43.0] - 2026-06-06
9
+
10
+ Bundled release: everything actionable from **ast-index 3.41→3.45** and
11
+ **Claude Code 2.1.163→2.1.167**, plus a statusline fix.
12
+
13
+ ### Added — `module_route` MCP tool (ast-index 3.44)
14
+
15
+ New tool wrapping ast-index's `module-route` command: the transitive
16
+ dependency path(s) between two modules. Answers "how does module A reach
17
+ module B through the import graph?", traces coupling, and can emit a
18
+ dependency diagram.
19
+
20
+ - `from` / `to` (required), plus `all`, `maxPaths` (≤200), `maxDepth`
21
+ (≤50), `viaKind` (`api`/`implementation`/`all`), `format`
22
+ (`text`/`json`/`mermaid`/`dot`).
23
+ - Machine formats (json/mermaid/dot) pass through clean — no header that
24
+ would corrupt a diagram/parse. Text format gets a `MODULE ROUTE: a → b`
25
+ header.
26
+ - Empty output explains both causes (modules unrelated **or** the
27
+ module-dependency graph isn't indexed — `ast-index rebuild`).
28
+ - Full-profile only (not in nav/edit/minimal sets) — a specialised
29
+ analysis tool, like the audit tools.
30
+ - `exec` already sets `AST_INDEX_WALK_UP=1`, so the route resolves from a
31
+ monorepo subdir without passing `--walk-up`.
32
+
33
+ ### Fixed — statusline now counts MCP-tool savings, not just hook denials
34
+
35
+ The badge summed only `hook-events.jsonl` (`savedTokens` from intercepted
36
+ raw `Read`/`Grep`). The larger share — savings from the MCP tools
37
+ themselves (`smart_read`, `outline`, `find_usages`, …) — lives in
38
+ `tool-calls.jsonl` and was **invisible** to the badge, so the displayed
39
+ figure undercounted real savings (often by an order of magnitude).
40
+
41
+ Two changes fix it:
42
+
43
+ - The MCP server now stamps every `tool-calls.jsonl` row with the real
44
+ Claude Code session id, read from `CLAUDE_CODE_SESSION_ID` (exported to
45
+ child processes; verified against the 2.1.167 bundle — the same value
46
+ the hooks receive and the statusline payload carries). Previously these
47
+ rows had an empty `session_id`, so they could not be attributed to a
48
+ session at all.
49
+ - The statusline sums **both** logs: `hook-events.jsonl` `savedTokens`
50
+ plus `tool-calls.jsonl` (`tokensWouldBe − tokensReturned`), each split
51
+ into the current session and the project total. Zero/negative deltas
52
+ (pass-throughs) are ignored.
53
+
54
+ ### Added — statusline Claude.ai rate limits (CC 2.1.80+)
55
+
56
+ The badge now appends `5h:42% 7d:13%` when the statusline payload carries
57
+ `rate_limits` (subscribers only, after the first API response). Schema
58
+ verified against the 2.1.167 bundle: `rate_limits.five_hour.used_percentage`
59
+ and `rate_limits.seven_day.used_percentage`. Unlike the cumulative token
60
+ total, these numbers move every turn — the badge finally shows something
61
+ live. Parsed with `sed` (no `jq` dependency) and whitelisted to digits.
62
+
63
+ ### Fixed — statusline bare `[TP]` in monorepos / subdirs / worktrees
64
+
65
+ The badge resolved the events log only at the exact `cwd` from the
66
+ payload. In a monorepo (or worktree, or any session `cd`'d into a
67
+ subpackage) the `.token-pilot/` dir is at the repo root, so the lookup
68
+ failed and rendered a bare `[TP]` with no token count. The script now
69
+ **walks up** to the nearest ancestor with `.token-pilot/` (bounded to 40
70
+ levels), the same way git finds `.git`.
71
+
72
+ ### Changed — statusline shows session + project savings (`s:12.3k · 172.6k`)
73
+
74
+ The badge now renders **both** the current session's saved tokens (the
75
+ number you watch climb during a run) and the cumulative project total,
76
+ e.g. `[TP s:12.3k · 172.6k]`. A fresh session that hasn't saved anything
77
+ yet falls back to the project total alone (never an empty badge after
78
+ first use). Numbers render with one decimal (`172.6k`, not `172k`) so a
79
+ single turn's ~100-token savings is visible on each render — whole-`k`
80
+ rounding previously made the figure look frozen.
81
+
82
+ ### Added — `effort: low` on the bundled skills (CC 2.1.16x)
83
+
84
+ `guide` / `install` / `stats` declare `effort: low` — they render static
85
+ output or run one CLI call, so they don't need a high-effort model.
86
+ Faster and cheaper when invoked.
87
+
88
+ ### Notes — Claude Code 2.1.16x integration (no code change required)
89
+
90
+ - **`additionalContext` no longer dropped on a failed tool call** — our
91
+ PreToolUse routing guidance now survives a failed call (we benefit
92
+ automatically).
93
+ - **`SubagentStop` input gained `background_tasks` / `session_crons`** —
94
+ available to our subagent-stop hook for future budget feedback.
95
+ - **Glob deny rules (`"*"` denies all tools)** — usable as a second
96
+ enforcement layer on top of our deny hooks for stricter TP adoption.
97
+
98
+ ### Deferred — `MessageDisplay` hook (researched, not shipped)
99
+
100
+ CC's new `MessageDisplay` hook transforms assistant text *as displayed*.
101
+ Deliberately NOT wired this release: (1) the output contract (the field
102
+ that returns replacement text) is not confirmable from the minified
103
+ 2.1.167 bundle — and our rule is never to ship an unverified CC field
104
+ beside working hooks; (2) it is display-only, so it saves no input or
105
+ output tokens (the text is already generated and already in context).
106
+ That is caveman's cosmetic-output domain, not token-pilot's input domain.
107
+ Revisit once a live MessageDisplay payload pins the contract.
108
+
8
109
  ## [0.30.0] - 2026-04-19
9
110
 
10
111
  ### Added — `minimal` profile (5 tools, near-zero overhead)
package/README.md CHANGED
@@ -43,7 +43,7 @@ Creates (or merges into) `.mcp.json` with `token-pilot` + [`context-mode`](https
43
43
 
44
44
  ## What You Get
45
45
 
46
- - **22 MCP tools** — structural reads, symbol search, git analysis, session analytics → [tools reference](docs/tools.md)
46
+ - **23 MCP tools** — structural reads, symbol search, git analysis, module routing, session analytics → [tools reference](docs/tools.md)
47
47
  - **PreToolUse hooks** — block heavy `Grep`/`Bash`/`Read` calls; redirect to efficient alternatives → [hooks & modes](docs/hooks.md)
48
48
  - **25 `tp-*` subagents** (Claude Code only) — MCP-first delegates with haiku/sonnet model tiers and budget enforcement → [agents reference](docs/agents.md)
49
49
  - **Tool profiles** — trim advertised `tools/list` to save ~2 k tokens per session → [profiles & config](docs/configuration.md)
@@ -9,7 +9,7 @@ tools:
9
9
  - mcp__token-pilot__read_symbol
10
10
  - Bash
11
11
  model: haiku
12
- token_pilot_version: "0.42.3"
12
+ token_pilot_version: "0.43.0"
13
13
  token_pilot_body_hash: dd184501203fa7f3c73f419c4ffbe33c4be75400cb64a7a51733a3fe23f6e085
14
14
  requiredMcpServers:
15
15
  - "token-pilot"
@@ -11,7 +11,7 @@ tools:
11
11
  - Grep
12
12
  - Read
13
13
  model: sonnet
14
- token_pilot_version: "0.42.3"
14
+ token_pilot_version: "0.43.0"
15
15
  token_pilot_body_hash: d172f600bf32277ea6eb4cbbee4542ddd698a986dcd96997d33930561964569b
16
16
  requiredMcpServers:
17
17
  - "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.42.3"
11
+ token_pilot_version: "0.43.0"
12
12
  token_pilot_body_hash: de64a406b5176de19f7422619c7de7949b1f28865f225402c9cea9255f377428
13
13
  requiredMcpServers:
14
14
  - "token-pilot"
@@ -13,7 +13,7 @@ tools:
13
13
  - Edit
14
14
  - Glob
15
15
  model: sonnet
16
- token_pilot_version: "0.42.3"
16
+ token_pilot_version: "0.43.0"
17
17
  token_pilot_body_hash: 68b32af2dacd82ebe52c4eec93edb903d452688274c3065218270627c564d8b0
18
18
  requiredMcpServers:
19
19
  - "token-pilot"
@@ -11,7 +11,7 @@ tools:
11
11
  - Grep
12
12
  - Read
13
13
  model: sonnet
14
- token_pilot_version: "0.42.3"
14
+ token_pilot_version: "0.43.0"
15
15
  token_pilot_body_hash: d9b7f5b7ae6f4ae21305c775361bcab097cc774370a6d976c093571d46d55021
16
16
  requiredMcpServers:
17
17
  - "token-pilot"
@@ -12,7 +12,7 @@ tools:
12
12
  - Read
13
13
  - Bash
14
14
  model: sonnet
15
- token_pilot_version: "0.42.3"
15
+ token_pilot_version: "0.43.0"
16
16
  token_pilot_body_hash: 052413de8d92377edcde6ae5c823f5378db304baccfa29e8866467f42553a500
17
17
  requiredMcpServers:
18
18
  - "token-pilot"
@@ -9,7 +9,7 @@ tools:
9
9
  - Bash
10
10
  - Read
11
11
  model: haiku
12
- token_pilot_version: "0.42.3"
12
+ token_pilot_version: "0.43.0"
13
13
  token_pilot_body_hash: e14dc57493d816f8c2e017963e2ef5f66bea50fd0b805a80e8a0d97c968427e7
14
14
  requiredMcpServers:
15
15
  - "token-pilot"
@@ -13,7 +13,7 @@ tools:
13
13
  - Edit
14
14
  - Glob
15
15
  model: haiku
16
- token_pilot_version: "0.42.3"
16
+ token_pilot_version: "0.43.0"
17
17
  token_pilot_body_hash: 57d741794ab40e31a7ac49c68ea39a9088f5827cdef866ce81bfca1b7c9180cf
18
18
  requiredMcpServers:
19
19
  - "token-pilot"
@@ -10,7 +10,7 @@ tools:
10
10
  - Bash
11
11
  - Read
12
12
  model: haiku
13
- token_pilot_version: "0.42.3"
13
+ token_pilot_version: "0.43.0"
14
14
  token_pilot_body_hash: 7b70fa76a60e3c58a1de4f56c32c0f166424137e203a0cf1c8654e7c9235d904
15
15
  requiredMcpServers:
16
16
  - "token-pilot"
@@ -12,7 +12,7 @@ tools:
12
12
  - mcp__token-pilot__read_symbols
13
13
  - Read
14
14
  model: sonnet
15
- token_pilot_version: "0.42.3"
15
+ token_pilot_version: "0.43.0"
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.42.3"
11
+ token_pilot_version: "0.43.0"
12
12
  token_pilot_body_hash: de5722bfea374eaab096c1ae635c37879e7a91370ee3cd0532f4240be03c91eb
13
13
  requiredMcpServers:
14
14
  - "token-pilot"
@@ -13,7 +13,7 @@ tools:
13
13
  - Edit
14
14
  - Bash
15
15
  model: sonnet
16
- token_pilot_version: "0.42.3"
16
+ token_pilot_version: "0.43.0"
17
17
  token_pilot_body_hash: 375a824d0d847bb5453ec594c7a62ad566ee7e4d92717b0473f771f1a0477c60
18
18
  requiredMcpServers:
19
19
  - "token-pilot"
@@ -11,7 +11,7 @@ tools:
11
11
  - Grep
12
12
  - Glob
13
13
  model: sonnet
14
- token_pilot_version: "0.42.3"
14
+ token_pilot_version: "0.43.0"
15
15
  token_pilot_body_hash: 0334de1bf99b431b65359637d125cda7c44c6f780eb92c57cc538715b1939536
16
16
  requiredMcpServers:
17
17
  - "token-pilot"
@@ -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.42.3"
13
+ token_pilot_version: "0.43.0"
14
14
  token_pilot_body_hash: 832e95633fbc8e9b0c10f3e540a327d4be062fb4b3f17a6cce6be13f414e2927
15
15
  requiredMcpServers:
16
16
  - "token-pilot"
@@ -11,7 +11,7 @@ tools:
11
11
  - Bash
12
12
  - Read
13
13
  model: sonnet
14
- token_pilot_version: "0.42.3"
14
+ token_pilot_version: "0.43.0"
15
15
  token_pilot_body_hash: b61f06380d80798fa2e49d37bcba0653495bee04dd6bdbc1feff9a75607b0508
16
16
  requiredMcpServers:
17
17
  - "token-pilot"
@@ -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.42.3"
14
+ token_pilot_version: "0.43.0"
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.42.3"
11
+ token_pilot_version: "0.43.0"
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.42.3"
12
+ token_pilot_version: "0.43.0"
13
13
  token_pilot_body_hash: 8ef3c3341cbfed4eb8dd130126a9683edc57e378c92ff0ca764d584fd941c55c
14
14
  requiredMcpServers:
15
15
  - "token-pilot"
package/agents/tp-run.md CHANGED
@@ -16,7 +16,7 @@ tools:
16
16
  - Glob
17
17
  - Bash
18
18
  model: haiku
19
- token_pilot_version: "0.42.3"
19
+ token_pilot_version: "0.43.0"
20
20
  token_pilot_body_hash: 2b08618d34a61f00aafccbda9fed6d83243296dedb83440edbd2d5c28bb6dbc4
21
21
  requiredMcpServers:
22
22
  - "token-pilot"
@@ -9,7 +9,7 @@ tools:
9
9
  - mcp__token-pilot__session_budget
10
10
  - Bash
11
11
  - Read
12
- token_pilot_version: "0.42.3"
12
+ token_pilot_version: "0.43.0"
13
13
  token_pilot_body_hash: 529374ed728f5eed5b758b3be3da65624783c0bf0c1a253d7d661a843eb5f767
14
14
  requiredMcpServers:
15
15
  - "token-pilot"
@@ -11,7 +11,7 @@ tools:
11
11
  - Read
12
12
  - Grep
13
13
  model: sonnet
14
- token_pilot_version: "0.42.3"
14
+ token_pilot_version: "0.43.0"
15
15
  token_pilot_body_hash: a60f6ae110eb3138064bce074e8ba26fa0ce5f4659df1624a9d9d3646803391b
16
16
  requiredMcpServers:
17
17
  - "token-pilot"
@@ -9,7 +9,7 @@ tools:
9
9
  - Read
10
10
  - Write
11
11
  model: sonnet
12
- token_pilot_version: "0.42.3"
12
+ token_pilot_version: "0.43.0"
13
13
  token_pilot_body_hash: c7a4e8b39228fd5158528f389c924c5ff2d98c4b9b05ee0106d54a26c5dc1350
14
14
  requiredMcpServers:
15
15
  - "token-pilot"
@@ -10,7 +10,7 @@ tools:
10
10
  - mcp__token-pilot__test_summary
11
11
  - Glob
12
12
  - Grep
13
- token_pilot_version: "0.42.3"
13
+ token_pilot_version: "0.43.0"
14
14
  token_pilot_body_hash: be81eed53a3720d146cf89e4a14a7a56577633f7c84c234c412ab70d64c05b11
15
15
  requiredMcpServers:
16
16
  - "token-pilot"
@@ -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.42.3"
11
+ token_pilot_version: "0.43.0"
12
12
  token_pilot_body_hash: 362ecf4cb03b059421ea26933473700900073dc38b3a7fe271208dfb1ae14f90
13
13
  requiredMcpServers:
14
14
  - "token-pilot"
@@ -13,7 +13,7 @@ tools:
13
13
  - Edit
14
14
  - Bash
15
15
  model: sonnet
16
- token_pilot_version: "0.42.3"
16
+ token_pilot_version: "0.43.0"
17
17
  token_pilot_body_hash: 269f2fe22ff4517c277d3f56ca67d8a5527b93290ab21079a83ba7af22c1b5a9
18
18
  requiredMcpServers:
19
19
  - "token-pilot"
@@ -80,6 +80,22 @@ export declare class AstIndexClient {
80
80
  stopPeriodicUpdate(): void;
81
81
  modules(pattern?: string): Promise<AstIndexModuleEntry[]>;
82
82
  moduleDeps(module: string): Promise<AstIndexModuleDep[]>;
83
+ /**
84
+ * Transitive dependency path(s) between two modules — ast-index 3.44
85
+ * `module-route`. Returns the raw CLI output for the requested format
86
+ * (text/json/mermaid/dot); the handler frames it. Null when the index
87
+ * is unavailable. `exec` already sets AST_INDEX_WALK_UP=1, so the
88
+ * `--walk-up` flag is implied and not passed here.
89
+ */
90
+ moduleRoute(opts: {
91
+ from: string;
92
+ to: string;
93
+ all?: boolean;
94
+ maxPaths?: number;
95
+ maxDepth?: number;
96
+ viaKind?: string;
97
+ format?: string;
98
+ }): Promise<string | null>;
83
99
  moduleDependents(module: string): Promise<AstIndexModuleDep[]>;
84
100
  unusedDeps(module: string): Promise<AstIndexUnusedDep[]>;
85
101
  moduleApi(module: string): Promise<AstIndexModuleApi[]>;
@@ -679,6 +679,36 @@ export class AstIndexClient {
679
679
  return [];
680
680
  }
681
681
  }
682
+ /**
683
+ * Transitive dependency path(s) between two modules — ast-index 3.44
684
+ * `module-route`. Returns the raw CLI output for the requested format
685
+ * (text/json/mermaid/dot); the handler frames it. Null when the index
686
+ * is unavailable. `exec` already sets AST_INDEX_WALK_UP=1, so the
687
+ * `--walk-up` flag is implied and not passed here.
688
+ */
689
+ async moduleRoute(opts) {
690
+ if (this.indexDisabled || this.indexOversized)
691
+ return null;
692
+ await this.ensureIndex();
693
+ try {
694
+ const cmdArgs = ["module-route", "--from", opts.from, "--to", opts.to];
695
+ if (opts.all)
696
+ cmdArgs.push("--all");
697
+ if (opts.maxPaths != null)
698
+ cmdArgs.push("--max-paths", String(opts.maxPaths));
699
+ if (opts.maxDepth != null)
700
+ cmdArgs.push("--max-depth", String(opts.maxDepth));
701
+ if (opts.viaKind)
702
+ cmdArgs.push("--via-kind", opts.viaKind);
703
+ if (opts.format)
704
+ cmdArgs.push("--format", opts.format);
705
+ return await this.exec(cmdArgs, 15000);
706
+ }
707
+ catch (err) {
708
+ console.error(`[token-pilot] ast-index module-route failed: ${err instanceof Error ? err.message : err}`);
709
+ return null;
710
+ }
711
+ }
682
712
  async moduleDependents(module) {
683
713
  if (this.indexDisabled || this.indexOversized)
684
714
  return [];
@@ -145,6 +145,23 @@ export interface ModuleInfoArgs {
145
145
  check?: "deps" | "dependents" | "api" | "unused-deps" | "all";
146
146
  }
147
147
  export declare function validateModuleInfoArgs(args: unknown): ModuleInfoArgs;
148
+ /**
149
+ * Validate module_route arguments.
150
+ *
151
+ * Wraps ast-index 3.44 `module-route` — the transitive dependency path
152
+ * between two modules. `from`/`to` are required; everything else mirrors
153
+ * the CLI flags with sane caps.
154
+ */
155
+ export interface ModuleRouteArgs {
156
+ from: string;
157
+ to: string;
158
+ all?: boolean;
159
+ maxPaths?: number;
160
+ maxDepth?: number;
161
+ viaKind?: "api" | "implementation" | "all";
162
+ format?: "text" | "json" | "mermaid" | "dot";
163
+ }
164
+ export declare function validateModuleRouteArgs(args: unknown): ModuleRouteArgs;
148
165
  /**
149
166
  * Validate smart_diff arguments.
150
167
  */
@@ -428,6 +428,60 @@ export function validateModuleInfoArgs(args) {
428
428
  check: check ?? "all",
429
429
  };
430
430
  }
431
+ export function validateModuleRouteArgs(args) {
432
+ if (!args || typeof args !== "object") {
433
+ throw new Error('Arguments must be an object with "from" and "to" parameters.');
434
+ }
435
+ const a = args;
436
+ if (typeof a.from !== "string" || a.from.length === 0) {
437
+ throw new Error('Required parameter "from" must be a non-empty string.');
438
+ }
439
+ if (typeof a.to !== "string" || a.to.length === 0) {
440
+ throw new Error('Required parameter "to" must be a non-empty string.');
441
+ }
442
+ let viaKind;
443
+ if (a.viaKind !== undefined && a.viaKind !== null) {
444
+ const valid = ["api", "implementation", "all"];
445
+ if (typeof a.viaKind !== "string" || !valid.includes(a.viaKind)) {
446
+ throw new Error(`"viaKind" must be one of: ${valid.join(", ")}`);
447
+ }
448
+ viaKind = a.viaKind;
449
+ }
450
+ let format;
451
+ if (a.format !== undefined && a.format !== null) {
452
+ const valid = ["text", "json", "mermaid", "dot"];
453
+ if (typeof a.format !== "string" || !valid.includes(a.format)) {
454
+ throw new Error(`"format" must be one of: ${valid.join(", ")}`);
455
+ }
456
+ format = a.format;
457
+ }
458
+ // Clamp numeric caps to safe ranges — never trust the caller blindly.
459
+ let maxPaths;
460
+ if (a.maxPaths !== undefined && a.maxPaths !== null) {
461
+ const n = Number(a.maxPaths);
462
+ if (!Number.isFinite(n) || n < 1) {
463
+ throw new Error('"maxPaths" must be a positive number.');
464
+ }
465
+ maxPaths = Math.min(Math.floor(n), 200);
466
+ }
467
+ let maxDepth;
468
+ if (a.maxDepth !== undefined && a.maxDepth !== null) {
469
+ const n = Number(a.maxDepth);
470
+ if (!Number.isFinite(n) || n < 1) {
471
+ throw new Error('"maxDepth" must be a positive number.');
472
+ }
473
+ maxDepth = Math.min(Math.floor(n), 50);
474
+ }
475
+ return {
476
+ from: a.from,
477
+ to: a.to,
478
+ all: a.all === true,
479
+ maxPaths,
480
+ maxDepth,
481
+ viaKind,
482
+ format,
483
+ };
484
+ }
431
485
  export function validateSmartDiffArgs(args) {
432
486
  if (!args || typeof args !== "object")
433
487
  return { scope: "unstaged" };
@@ -0,0 +1,20 @@
1
+ import type { AstIndexClient } from "../ast-index/client.js";
2
+ import type { ModuleRouteArgs } from "../core/validation.js";
3
+ /**
4
+ * module_route — transitive dependency path(s) between two modules.
5
+ *
6
+ * Thin wrapper over ast-index 3.44 `module-route`. The CLI already
7
+ * produces compact, purpose-built output (a path listing, or
8
+ * mermaid/dot/json for diagramming), so the handler only frames it and
9
+ * handles the empty / degraded cases — it does not re-parse the graph.
10
+ */
11
+ export declare function handleModuleRoute(args: ModuleRouteArgs, _projectRoot: string, astIndex: AstIndexClient): Promise<{
12
+ content: Array<{
13
+ type: "text";
14
+ text: string;
15
+ }>;
16
+ meta: {
17
+ files: string[];
18
+ };
19
+ }>;
20
+ //# sourceMappingURL=module-route.d.ts.map
@@ -0,0 +1,73 @@
1
+ /**
2
+ * module_route — transitive dependency path(s) between two modules.
3
+ *
4
+ * Thin wrapper over ast-index 3.44 `module-route`. The CLI already
5
+ * produces compact, purpose-built output (a path listing, or
6
+ * mermaid/dot/json for diagramming), so the handler only frames it and
7
+ * handles the empty / degraded cases — it does not re-parse the graph.
8
+ */
9
+ export async function handleModuleRoute(args, _projectRoot, astIndex) {
10
+ // Degradation check — same contract as module_info.
11
+ if (astIndex.isDisabled() || astIndex.isOversized()) {
12
+ return {
13
+ content: [
14
+ {
15
+ type: "text",
16
+ text: "⚠ ast-index unavailable — module_route requires ast-index.\n" +
17
+ "DEGRADED: Use module_info() on each module + related_files() to trace dependencies manually.",
18
+ },
19
+ ],
20
+ meta: { files: [] },
21
+ };
22
+ }
23
+ const output = await astIndex.moduleRoute({
24
+ from: args.from,
25
+ to: args.to,
26
+ all: args.all,
27
+ maxPaths: args.maxPaths,
28
+ maxDepth: args.maxDepth,
29
+ viaKind: args.viaKind,
30
+ format: args.format,
31
+ });
32
+ const header = `MODULE ROUTE: ${args.from} → ${args.to}`;
33
+ if (output == null) {
34
+ return {
35
+ content: [
36
+ {
37
+ type: "text",
38
+ text: `${header}\n\n` +
39
+ "⚠ module-route failed (index unavailable or command error).\n" +
40
+ "HINT: run `npx token-pilot doctor` to check ast-index, or fall back to module_info().",
41
+ },
42
+ ],
43
+ meta: { files: [] },
44
+ };
45
+ }
46
+ const body = output.trim();
47
+ if (body.length === 0) {
48
+ return {
49
+ content: [
50
+ {
51
+ type: "text",
52
+ text: `${header}\n\n` +
53
+ `No dependency path returned from "${args.from}" to "${args.to}" ` +
54
+ `(within ${args.maxDepth ?? 20} hops).\n` +
55
+ "This means one of:\n" +
56
+ " • the modules are genuinely unrelated, or\n" +
57
+ " • the module-dependency graph isn't indexed yet — run `ast-index rebuild`.\n" +
58
+ 'HINT: also try a higher "maxDepth" / wider "viaKind", or module_info() to confirm each module resolves.',
59
+ },
60
+ ],
61
+ meta: { files: [] },
62
+ };
63
+ }
64
+ // For machine formats (json/mermaid/dot) pass the payload through clean —
65
+ // a header would corrupt a diagram/parse. Text format gets the header.
66
+ const isMachineFormat = args.format === "json" || args.format === "mermaid" || args.format === "dot";
67
+ const text = isMachineFormat ? body : `${header}\n\n${body}`;
68
+ return {
69
+ content: [{ type: "text", text }],
70
+ meta: { files: [] },
71
+ };
72
+ }
73
+ //# sourceMappingURL=module-route.js.map
package/dist/index.js CHANGED
@@ -1625,12 +1625,12 @@ Usage:
1625
1625
  Quick start:
1626
1626
  npx token-pilot init Setup .mcp.json (token-pilot + context-mode)
1627
1627
 
1628
- MCP Tools (22):
1628
+ MCP Tools (23):
1629
1629
  smart_read, read_symbol, read_symbols, read_range, read_section, read_diff,
1630
1630
  read_for_edit, smart_read_many, find_usages, find_unused, related_files,
1631
1631
  outline, project_overview, session_analytics, code_audit, module_info,
1632
- smart_diff, explore_area, smart_log, test_summary, session_snapshot,
1633
- session_budget
1632
+ module_route, smart_diff, explore_area, smart_log, test_summary,
1633
+ session_snapshot, session_budget
1634
1634
  `);
1635
1635
  process.exit(0);
1636
1636
  }
@@ -92,6 +92,13 @@ export declare const TOOL_DEFINITIONS: ({
92
92
  check?: undefined;
93
93
  pattern?: undefined;
94
94
  name?: undefined;
95
+ from?: undefined;
96
+ to?: undefined;
97
+ all?: undefined;
98
+ maxPaths?: undefined;
99
+ maxDepth?: undefined;
100
+ viaKind?: undefined;
101
+ format?: undefined;
95
102
  ref?: undefined;
96
103
  count?: undefined;
97
104
  command?: undefined;
@@ -176,6 +183,13 @@ export declare const TOOL_DEFINITIONS: ({
176
183
  check?: undefined;
177
184
  pattern?: undefined;
178
185
  name?: undefined;
186
+ from?: undefined;
187
+ to?: undefined;
188
+ all?: undefined;
189
+ maxPaths?: undefined;
190
+ maxDepth?: undefined;
191
+ viaKind?: undefined;
192
+ format?: undefined;
179
193
  ref?: undefined;
180
194
  count?: undefined;
181
195
  command?: undefined;
@@ -254,6 +268,13 @@ export declare const TOOL_DEFINITIONS: ({
254
268
  check?: undefined;
255
269
  pattern?: undefined;
256
270
  name?: undefined;
271
+ from?: undefined;
272
+ to?: undefined;
273
+ all?: undefined;
274
+ maxPaths?: undefined;
275
+ maxDepth?: undefined;
276
+ viaKind?: undefined;
277
+ format?: undefined;
257
278
  ref?: undefined;
258
279
  count?: undefined;
259
280
  command?: undefined;
@@ -328,6 +349,13 @@ export declare const TOOL_DEFINITIONS: ({
328
349
  check?: undefined;
329
350
  pattern?: undefined;
330
351
  name?: undefined;
352
+ from?: undefined;
353
+ to?: undefined;
354
+ all?: undefined;
355
+ maxPaths?: undefined;
356
+ maxDepth?: undefined;
357
+ viaKind?: undefined;
358
+ format?: undefined;
331
359
  ref?: undefined;
332
360
  count?: undefined;
333
361
  command?: undefined;
@@ -393,6 +421,13 @@ export declare const TOOL_DEFINITIONS: ({
393
421
  check?: undefined;
394
422
  pattern?: undefined;
395
423
  name?: undefined;
424
+ from?: undefined;
425
+ to?: undefined;
426
+ all?: undefined;
427
+ maxPaths?: undefined;
428
+ maxDepth?: undefined;
429
+ viaKind?: undefined;
430
+ format?: undefined;
396
431
  ref?: undefined;
397
432
  count?: undefined;
398
433
  command?: undefined;
@@ -458,6 +493,13 @@ export declare const TOOL_DEFINITIONS: ({
458
493
  check?: undefined;
459
494
  pattern?: undefined;
460
495
  name?: undefined;
496
+ from?: undefined;
497
+ to?: undefined;
498
+ all?: undefined;
499
+ maxPaths?: undefined;
500
+ maxDepth?: undefined;
501
+ viaKind?: undefined;
502
+ format?: undefined;
461
503
  ref?: undefined;
462
504
  count?: undefined;
463
505
  command?: undefined;
@@ -547,6 +589,13 @@ export declare const TOOL_DEFINITIONS: ({
547
589
  check?: undefined;
548
590
  pattern?: undefined;
549
591
  name?: undefined;
592
+ from?: undefined;
593
+ to?: undefined;
594
+ all?: undefined;
595
+ maxPaths?: undefined;
596
+ maxDepth?: undefined;
597
+ viaKind?: undefined;
598
+ format?: undefined;
550
599
  ref?: undefined;
551
600
  count?: undefined;
552
601
  command?: undefined;
@@ -621,6 +670,13 @@ export declare const TOOL_DEFINITIONS: ({
621
670
  check?: undefined;
622
671
  pattern?: undefined;
623
672
  name?: undefined;
673
+ from?: undefined;
674
+ to?: undefined;
675
+ all?: undefined;
676
+ maxPaths?: undefined;
677
+ maxDepth?: undefined;
678
+ viaKind?: undefined;
679
+ format?: undefined;
624
680
  ref?: undefined;
625
681
  count?: undefined;
626
682
  command?: undefined;
@@ -704,6 +760,13 @@ export declare const TOOL_DEFINITIONS: ({
704
760
  check?: undefined;
705
761
  pattern?: undefined;
706
762
  name?: undefined;
763
+ from?: undefined;
764
+ to?: undefined;
765
+ all?: undefined;
766
+ maxPaths?: undefined;
767
+ maxDepth?: undefined;
768
+ viaKind?: undefined;
769
+ format?: undefined;
707
770
  ref?: undefined;
708
771
  count?: undefined;
709
772
  command?: undefined;
@@ -770,6 +833,13 @@ export declare const TOOL_DEFINITIONS: ({
770
833
  check?: undefined;
771
834
  pattern?: undefined;
772
835
  name?: undefined;
836
+ from?: undefined;
837
+ to?: undefined;
838
+ all?: undefined;
839
+ maxPaths?: undefined;
840
+ maxDepth?: undefined;
841
+ viaKind?: undefined;
842
+ format?: undefined;
773
843
  ref?: undefined;
774
844
  count?: undefined;
775
845
  command?: undefined;
@@ -832,6 +902,13 @@ export declare const TOOL_DEFINITIONS: ({
832
902
  check?: undefined;
833
903
  pattern?: undefined;
834
904
  name?: undefined;
905
+ from?: undefined;
906
+ to?: undefined;
907
+ all?: undefined;
908
+ maxPaths?: undefined;
909
+ maxDepth?: undefined;
910
+ viaKind?: undefined;
911
+ format?: undefined;
835
912
  ref?: undefined;
836
913
  count?: undefined;
837
914
  command?: undefined;
@@ -900,6 +977,13 @@ export declare const TOOL_DEFINITIONS: ({
900
977
  check?: undefined;
901
978
  pattern?: undefined;
902
979
  name?: undefined;
980
+ from?: undefined;
981
+ to?: undefined;
982
+ all?: undefined;
983
+ maxPaths?: undefined;
984
+ maxDepth?: undefined;
985
+ viaKind?: undefined;
986
+ format?: undefined;
903
987
  ref?: undefined;
904
988
  count?: undefined;
905
989
  command?: undefined;
@@ -962,6 +1046,13 @@ export declare const TOOL_DEFINITIONS: ({
962
1046
  check?: undefined;
963
1047
  pattern?: undefined;
964
1048
  name?: undefined;
1049
+ from?: undefined;
1050
+ to?: undefined;
1051
+ all?: undefined;
1052
+ maxPaths?: undefined;
1053
+ maxDepth?: undefined;
1054
+ viaKind?: undefined;
1055
+ format?: undefined;
965
1056
  ref?: undefined;
966
1057
  count?: undefined;
967
1058
  command?: undefined;
@@ -1027,6 +1118,13 @@ export declare const TOOL_DEFINITIONS: ({
1027
1118
  check?: undefined;
1028
1119
  pattern?: undefined;
1029
1120
  name?: undefined;
1121
+ from?: undefined;
1122
+ to?: undefined;
1123
+ all?: undefined;
1124
+ maxPaths?: undefined;
1125
+ maxDepth?: undefined;
1126
+ viaKind?: undefined;
1127
+ format?: undefined;
1030
1128
  ref?: undefined;
1031
1129
  count?: undefined;
1032
1130
  command?: undefined;
@@ -1095,6 +1193,13 @@ export declare const TOOL_DEFINITIONS: ({
1095
1193
  check?: undefined;
1096
1194
  pattern?: undefined;
1097
1195
  name?: undefined;
1196
+ from?: undefined;
1197
+ to?: undefined;
1198
+ all?: undefined;
1199
+ maxPaths?: undefined;
1200
+ maxDepth?: undefined;
1201
+ viaKind?: undefined;
1202
+ format?: undefined;
1098
1203
  ref?: undefined;
1099
1204
  count?: undefined;
1100
1205
  command?: undefined;
@@ -1170,6 +1275,13 @@ export declare const TOOL_DEFINITIONS: ({
1170
1275
  verbose?: undefined;
1171
1276
  module?: undefined;
1172
1277
  export_only?: undefined;
1278
+ from?: undefined;
1279
+ to?: undefined;
1280
+ all?: undefined;
1281
+ maxPaths?: undefined;
1282
+ maxDepth?: undefined;
1283
+ viaKind?: undefined;
1284
+ format?: undefined;
1173
1285
  ref?: undefined;
1174
1286
  count?: undefined;
1175
1287
  command?: undefined;
@@ -1236,6 +1348,102 @@ export declare const TOOL_DEFINITIONS: ({
1236
1348
  export_only?: undefined;
1237
1349
  pattern?: undefined;
1238
1350
  name?: undefined;
1351
+ from?: undefined;
1352
+ to?: undefined;
1353
+ all?: undefined;
1354
+ maxPaths?: undefined;
1355
+ maxDepth?: undefined;
1356
+ viaKind?: undefined;
1357
+ format?: undefined;
1358
+ ref?: undefined;
1359
+ count?: undefined;
1360
+ command?: undefined;
1361
+ runner?: undefined;
1362
+ timeout?: undefined;
1363
+ goal?: undefined;
1364
+ decisions?: undefined;
1365
+ confirmed?: undefined;
1366
+ files?: undefined;
1367
+ blocked?: undefined;
1368
+ next?: undefined;
1369
+ sessionId?: undefined;
1370
+ };
1371
+ required: string[];
1372
+ };
1373
+ } | {
1374
+ name: string;
1375
+ description: string;
1376
+ inputSchema: {
1377
+ type: "object";
1378
+ properties: {
1379
+ from: {
1380
+ type: string;
1381
+ description: string;
1382
+ };
1383
+ to: {
1384
+ type: string;
1385
+ description: string;
1386
+ };
1387
+ all: {
1388
+ type: string;
1389
+ description: string;
1390
+ };
1391
+ maxPaths: {
1392
+ type: string;
1393
+ description: string;
1394
+ };
1395
+ maxDepth: {
1396
+ type: string;
1397
+ description: string;
1398
+ };
1399
+ viaKind: {
1400
+ type: string;
1401
+ enum: string[];
1402
+ description: string;
1403
+ };
1404
+ format: {
1405
+ type: string;
1406
+ enum: string[];
1407
+ description: string;
1408
+ };
1409
+ path?: undefined;
1410
+ show_imports?: undefined;
1411
+ show_docs?: undefined;
1412
+ depth?: undefined;
1413
+ scope?: undefined;
1414
+ max_tokens?: undefined;
1415
+ session_id?: undefined;
1416
+ force?: undefined;
1417
+ symbol?: undefined;
1418
+ context_before?: undefined;
1419
+ context_after?: undefined;
1420
+ show?: undefined;
1421
+ include_edit_context?: undefined;
1422
+ symbols?: undefined;
1423
+ start_line?: undefined;
1424
+ end_line?: undefined;
1425
+ heading?: undefined;
1426
+ context_lines?: undefined;
1427
+ line?: undefined;
1428
+ context?: undefined;
1429
+ include_callers?: undefined;
1430
+ include_tests?: undefined;
1431
+ include_changes?: undefined;
1432
+ section?: undefined;
1433
+ paths?: undefined;
1434
+ kind?: undefined;
1435
+ limit?: undefined;
1436
+ lang?: undefined;
1437
+ mode?: undefined;
1438
+ include?: undefined;
1439
+ recursive?: undefined;
1440
+ max_depth?: undefined;
1441
+ verbose?: undefined;
1442
+ module?: undefined;
1443
+ export_only?: undefined;
1444
+ check?: undefined;
1445
+ pattern?: undefined;
1446
+ name?: undefined;
1239
1447
  ref?: undefined;
1240
1448
  count?: undefined;
1241
1449
  command?: undefined;
@@ -1306,6 +1514,13 @@ export declare const TOOL_DEFINITIONS: ({
1306
1514
  check?: undefined;
1307
1515
  pattern?: undefined;
1308
1516
  name?: undefined;
1517
+ from?: undefined;
1518
+ to?: undefined;
1519
+ all?: undefined;
1520
+ maxPaths?: undefined;
1521
+ maxDepth?: undefined;
1522
+ viaKind?: undefined;
1523
+ format?: undefined;
1309
1524
  count?: undefined;
1310
1525
  command?: undefined;
1311
1526
  runner?: undefined;
@@ -1374,6 +1589,13 @@ export declare const TOOL_DEFINITIONS: ({
1374
1589
  check?: undefined;
1375
1590
  pattern?: undefined;
1376
1591
  name?: undefined;
1592
+ from?: undefined;
1593
+ to?: undefined;
1594
+ all?: undefined;
1595
+ maxPaths?: undefined;
1596
+ maxDepth?: undefined;
1597
+ viaKind?: undefined;
1598
+ format?: undefined;
1377
1599
  ref?: undefined;
1378
1600
  count?: undefined;
1379
1601
  command?: undefined;
@@ -1444,6 +1666,13 @@ export declare const TOOL_DEFINITIONS: ({
1444
1666
  check?: undefined;
1445
1667
  pattern?: undefined;
1446
1668
  name?: undefined;
1669
+ from?: undefined;
1670
+ to?: undefined;
1671
+ all?: undefined;
1672
+ maxPaths?: undefined;
1673
+ maxDepth?: undefined;
1674
+ viaKind?: undefined;
1675
+ format?: undefined;
1447
1676
  command?: undefined;
1448
1677
  runner?: undefined;
1449
1678
  timeout?: undefined;
@@ -1514,6 +1743,13 @@ export declare const TOOL_DEFINITIONS: ({
1514
1743
  check?: undefined;
1515
1744
  pattern?: undefined;
1516
1745
  name?: undefined;
1746
+ from?: undefined;
1747
+ to?: undefined;
1748
+ all?: undefined;
1749
+ maxPaths?: undefined;
1750
+ maxDepth?: undefined;
1751
+ viaKind?: undefined;
1752
+ format?: undefined;
1517
1753
  ref?: undefined;
1518
1754
  count?: undefined;
1519
1755
  goal?: undefined;
@@ -1603,6 +1839,13 @@ export declare const TOOL_DEFINITIONS: ({
1603
1839
  check?: undefined;
1604
1840
  pattern?: undefined;
1605
1841
  name?: undefined;
1842
+ from?: undefined;
1843
+ to?: undefined;
1844
+ all?: undefined;
1845
+ maxPaths?: undefined;
1846
+ maxDepth?: undefined;
1847
+ viaKind?: undefined;
1848
+ format?: undefined;
1606
1849
  ref?: undefined;
1607
1850
  count?: undefined;
1608
1851
  command?: undefined;
@@ -1660,6 +1903,13 @@ export declare const TOOL_DEFINITIONS: ({
1660
1903
  check?: undefined;
1661
1904
  pattern?: undefined;
1662
1905
  name?: undefined;
1906
+ from?: undefined;
1907
+ to?: undefined;
1908
+ all?: undefined;
1909
+ maxPaths?: undefined;
1910
+ maxDepth?: undefined;
1911
+ viaKind?: undefined;
1912
+ format?: undefined;
1663
1913
  ref?: undefined;
1664
1914
  count?: undefined;
1665
1915
  command?: undefined;
@@ -585,6 +585,46 @@ export const TOOL_DEFINITIONS = [
585
585
  required: ["module"],
586
586
  },
587
587
  },
588
+ {
589
+ name: "module_route",
590
+ description: "Show the transitive dependency path(s) between two modules — how module A reaches module B through the import graph. Use to answer 'how does X depend on Y?', trace coupling, or generate a dependency diagram. format='mermaid'/'dot' emits a diagram; default text lists the hops.",
591
+ inputSchema: {
592
+ type: "object",
593
+ properties: {
594
+ from: {
595
+ type: "string",
596
+ description: 'Source module — the one whose dependencies are followed (name or path, e.g. "auth", "apps/api")',
597
+ },
598
+ to: {
599
+ type: "string",
600
+ description: "Target module to reach",
601
+ },
602
+ all: {
603
+ type: "boolean",
604
+ description: "Show all simple paths instead of just the shortest (default: false)",
605
+ },
606
+ maxPaths: {
607
+ type: "number",
608
+ description: "Cap on number of paths returned (default: 50, max: 200)",
609
+ },
610
+ maxDepth: {
611
+ type: "number",
612
+ description: "Cap on path length in hops (default: 20, max: 50)",
613
+ },
614
+ viaKind: {
615
+ type: "string",
616
+ enum: ["api", "implementation", "all"],
617
+ description: 'Restrict traversal to a dependency kind (default: "all")',
618
+ },
619
+ format: {
620
+ type: "string",
621
+ enum: ["text", "json", "mermaid", "dot"],
622
+ description: 'Output format: "text" (default, hop listing), "json", "mermaid" or "dot" (dependency diagram)',
623
+ },
624
+ },
625
+ required: ["from", "to"],
626
+ },
627
+ },
588
628
  // --- Diff & exploration ---
589
629
  {
590
630
  name: "smart_diff",
package/dist/server.js CHANGED
@@ -34,6 +34,7 @@ import { handleRelatedFiles } from "./handlers/related-files.js";
34
34
  import { handleOutline } from "./handlers/outline.js";
35
35
  import { handleCodeAudit } from "./handlers/code-audit.js";
36
36
  import { handleModuleInfo } from "./handlers/module-info.js";
37
+ import { handleModuleRoute } from "./handlers/module-route.js";
37
38
  import { handleSmartDiff } from "./handlers/smart-diff.js";
38
39
  import { handleExploreArea } from "./handlers/explore-area.js";
39
40
  import { handleSmartLog } from "./handlers/smart-log.js";
@@ -50,9 +51,17 @@ import { getMcpInstructions, TOOL_DEFINITIONS, } from "./server/tool-definitions
50
51
  import { filterToolsByProfile, parseProfileEnv, } from "./server/tool-profiles.js";
51
52
  import { STRICT_SMART_READ_MAX_TOKENS, STRICT_EXPLORE_AREA_INCLUDE, } from "./server/enforcement-mode.js";
52
53
  import { createTokenEstimates } from "./server/token-estimates.js";
53
- import { validateSmartReadArgs, validateReadSymbolArgs, validateReadSymbolsArgs, validateReadRangeArgs, validateReadDiffArgs, validateFindUsagesArgs, validateSmartReadManyArgs, validateReadForEditArgs, validateRelatedFilesArgs, validateOutlineArgs, validateFindUnusedArgs, validateCallTreeArgs, validateCodeAuditArgs, validateProjectOverviewArgs, validateModuleInfoArgs, validateSmartDiffArgs, validateExploreAreaArgs, validateSmartLogArgs, validateTestSummaryArgs, validateReadSectionArgs, } from "./core/validation.js";
54
+ 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";
54
55
  export async function createServer(projectRoot, options) {
55
56
  const mode = options?.enforcementMode ?? "deny";
57
+ // v0.43.0 — the real Claude Code session id. CC exports it to every
58
+ // child process (MCP server, hooks, Bash) via CLAUDE_CODE_SESSION_ID —
59
+ // verified against the 2.1.167 bundle, and it is the exact same value
60
+ // the PreToolUse hooks receive and the statusline payload carries.
61
+ // Stamping it on each tool-call row lets the statusline badge attribute
62
+ // MCP-tool savings to the current session; before this the rows carried
63
+ // an empty session_id and the badge ignored them, undercounting savings.
64
+ const ccSessionId = process.env.CLAUDE_CODE_SESSION_ID ?? "";
56
65
  const config = await loadConfig(projectRoot);
57
66
  const astIndex = new AstIndexClient(projectRoot, config.astIndex.timeout, {
58
67
  binaryPath: config.astIndex.binaryPath,
@@ -323,7 +332,7 @@ export async function createServer(projectRoot, options) {
323
332
  // future prune/fix decision.
324
333
  void appendToolCall(projectRoot, {
325
334
  ts: rest.timestamp,
326
- session_id: call.sessionId ?? "",
335
+ session_id: call.sessionId ?? ccSessionId,
327
336
  tool: rest.tool,
328
337
  path: rest.path,
329
338
  tokensReturned: rest.tokensReturned,
@@ -857,6 +866,37 @@ export async function createServer(projectRoot, options) {
857
866
  });
858
867
  return moduleResult;
859
868
  }
869
+ case "module_route": {
870
+ const routeArgs = validateModuleRouteArgs(args);
871
+ const cachedRoute = sessionCache?.get("module_route", routeArgs);
872
+ if (cachedRoute) {
873
+ recordWithTrace({
874
+ tool: "module_route",
875
+ path: `${routeArgs.from}→${routeArgs.to}`,
876
+ tokensReturned: cachedRoute.tokenEstimate,
877
+ tokensWouldBe: cachedRoute.tokensWouldBe ?? cachedRoute.tokenEstimate,
878
+ timestamp: Date.now(),
879
+ sessionCacheHit: true,
880
+ savingsCategory: "cache",
881
+ args: routeArgs,
882
+ });
883
+ return cachedRoute.result;
884
+ }
885
+ const routeResult = await handleModuleRoute(routeArgs, projectRoot, astIndex);
886
+ const routeText = routeResult.content[0]?.text ?? "";
887
+ const routeTokens = estimateTokens(routeText);
888
+ sessionCache?.set("module_route", routeArgs, routeResult, { dependsOnAst: true }, routeTokens, routeTokens);
889
+ recordWithTrace({
890
+ tool: "module_route",
891
+ path: `${routeArgs.from}→${routeArgs.to}`,
892
+ tokensReturned: routeTokens,
893
+ tokensWouldBe: routeTokens,
894
+ timestamp: Date.now(),
895
+ savingsCategory: "compression",
896
+ args: routeArgs,
897
+ });
898
+ return routeResult;
899
+ }
860
900
  case "smart_diff": {
861
901
  const sdArgs = validateSmartDiffArgs(args);
862
902
  const sdResult = await handleSmartDiff(sdArgs, projectRoot, astIndex);
package/docs/tools.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # MCP Tools Reference
2
2
 
3
- Token Pilot exposes 22 MCP tools. All handlers remain active regardless of [tool profile](configuration.md#tool-profiles) — the profile only trims what appears in `tools/list` at session start.
3
+ Token Pilot exposes 23 MCP tools. All handlers remain active regardless of [tool profile](configuration.md#tool-profiles) — the profile only trims what appears in `tools/list` at session start.
4
4
 
5
5
  ## Reading
6
6
 
@@ -26,6 +26,7 @@ Token Pilot exposes 22 MCP tools. All handlers remain active regardless of [tool
26
26
  | `find_unused` | manual | Dead code detection — unreferenced exported symbols |
27
27
  | `code_audit` | multiple `Grep` | TODOs, deprecated symbols, structural patterns |
28
28
  | `module_info` | manual | Deps, dependents, public API, unused deps |
29
+ | `module_route` | manual | Transitive dependency path(s) between two modules; `format=mermaid`/`dot` emits a diagram |
29
30
  | `smart_diff` | raw `git diff` | Structural diff with symbol mapping |
30
31
  | `explore_area` | 3–5 calls | Structure + imports + tests + recent changes in one call |
31
32
  | `smart_log` | raw `git log` | Structured commits with category detection |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "token-pilot",
3
- "version": "0.42.3",
3
+ "version": "0.43.0",
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",
@@ -3,6 +3,10 @@ name: guide
3
3
  description: Show a quick-reference guide for all Token Pilot tools — when to use each one
4
4
  command: guide
5
5
  user_invocable: true
6
+ # v0.43.0 — effort (Claude Code 2.1.16x) overrides model effort while
7
+ # this skill runs. The guide just renders a static Markdown reference —
8
+ # it needs no reasoning, so `low` keeps it fast and cheap.
9
+ effort: low
6
10
  # v0.36.0 — disallowed-tools is a Claude Code 2.1.152+ field that
7
11
  # strips the listed tools from the model's surface while this skill
8
12
  # is active. The guide just renders Markdown; it never needs Edit /
@@ -3,6 +3,10 @@ name: install
3
3
  description: Install or check ast-index binary (auto-downloads if missing)
4
4
  command: install
5
5
  user_invocable: true
6
+ # v0.43.0 — effort (Claude Code 2.1.16x). install drives one
7
+ # `npx token-pilot install-ast-index` Bash call; no reasoning needed,
8
+ # so `low` keeps it fast and cheap.
9
+ effort: low
6
10
  # v0.36.0 — disallowed-tools (Claude Code 2.1.152+). The install
7
11
  # skill drives one `npx token-pilot install-ast-index` Bash command;
8
12
  # nothing else. Keep Bash, block all write/edit/delegation tools so
@@ -3,6 +3,9 @@ name: stats
3
3
  description: Show Token Pilot session analytics — token savings, per-tool breakdown, top files, per-agent grouping
4
4
  command: stats
5
5
  user_invocable: true
6
+ # v0.43.0 — effort (Claude Code 2.1.16x). stats reads the events log and
7
+ # runs `token-pilot stats` variants — no reasoning, so `low` is enough.
8
+ effort: low
6
9
  # v0.36.0 — disallowed-tools (Claude Code 2.1.152+). stats only reads
7
10
  # the events log and runs `token-pilot stats` variants. No
8
11
  # Edit/Write/Task ever — block them defensively.