token-pilot 0.1.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.
Files changed (142) hide show
  1. package/.claude-plugin/hooks/hooks.json +15 -0
  2. package/.claude-plugin/marketplace.json +15 -0
  3. package/.claude-plugin/plugin.json +9 -0
  4. package/.mcp.json +8 -0
  5. package/CHANGELOG.md +48 -0
  6. package/README.md +282 -0
  7. package/dist/ast-index/binary-manager.d.ts +15 -0
  8. package/dist/ast-index/binary-manager.d.ts.map +1 -0
  9. package/dist/ast-index/binary-manager.js +222 -0
  10. package/dist/ast-index/binary-manager.js.map +1 -0
  11. package/dist/ast-index/client.d.ts +48 -0
  12. package/dist/ast-index/client.d.ts.map +1 -0
  13. package/dist/ast-index/client.js +371 -0
  14. package/dist/ast-index/client.js.map +1 -0
  15. package/dist/ast-index/tar-extract.d.ts +6 -0
  16. package/dist/ast-index/tar-extract.d.ts.map +1 -0
  17. package/dist/ast-index/tar-extract.js +39 -0
  18. package/dist/ast-index/tar-extract.js.map +1 -0
  19. package/dist/ast-index/types.d.ts +78 -0
  20. package/dist/ast-index/types.d.ts.map +1 -0
  21. package/dist/ast-index/types.js +6 -0
  22. package/dist/ast-index/types.js.map +1 -0
  23. package/dist/config/defaults.d.ts +3 -0
  24. package/dist/config/defaults.d.ts.map +1 -0
  25. package/dist/config/defaults.js +51 -0
  26. package/dist/config/defaults.js.map +1 -0
  27. package/dist/config/loader.d.ts +3 -0
  28. package/dist/config/loader.d.ts.map +1 -0
  29. package/dist/config/loader.js +31 -0
  30. package/dist/config/loader.js.map +1 -0
  31. package/dist/core/context-registry.d.ts +31 -0
  32. package/dist/core/context-registry.d.ts.map +1 -0
  33. package/dist/core/context-registry.js +133 -0
  34. package/dist/core/context-registry.js.map +1 -0
  35. package/dist/core/file-cache.d.ts +34 -0
  36. package/dist/core/file-cache.d.ts.map +1 -0
  37. package/dist/core/file-cache.js +120 -0
  38. package/dist/core/file-cache.js.map +1 -0
  39. package/dist/core/format-duration.d.ts +5 -0
  40. package/dist/core/format-duration.d.ts.map +1 -0
  41. package/dist/core/format-duration.js +13 -0
  42. package/dist/core/format-duration.js.map +1 -0
  43. package/dist/core/session-analytics.d.ts +26 -0
  44. package/dist/core/session-analytics.d.ts.map +1 -0
  45. package/dist/core/session-analytics.js +91 -0
  46. package/dist/core/session-analytics.js.map +1 -0
  47. package/dist/core/symbol-resolver.d.ts +21 -0
  48. package/dist/core/symbol-resolver.d.ts.map +1 -0
  49. package/dist/core/symbol-resolver.js +102 -0
  50. package/dist/core/symbol-resolver.js.map +1 -0
  51. package/dist/core/token-estimator.d.ts +10 -0
  52. package/dist/core/token-estimator.d.ts.map +1 -0
  53. package/dist/core/token-estimator.js +22 -0
  54. package/dist/core/token-estimator.js.map +1 -0
  55. package/dist/core/validation.d.ts +77 -0
  56. package/dist/core/validation.d.ts.map +1 -0
  57. package/dist/core/validation.js +208 -0
  58. package/dist/core/validation.js.map +1 -0
  59. package/dist/formatters/structure.d.ts +13 -0
  60. package/dist/formatters/structure.d.ts.map +1 -0
  61. package/dist/formatters/structure.js +90 -0
  62. package/dist/formatters/structure.js.map +1 -0
  63. package/dist/git/file-watcher.d.ts +17 -0
  64. package/dist/git/file-watcher.d.ts.map +1 -0
  65. package/dist/git/file-watcher.js +54 -0
  66. package/dist/git/file-watcher.js.map +1 -0
  67. package/dist/git/watcher.d.ts +25 -0
  68. package/dist/git/watcher.d.ts.map +1 -0
  69. package/dist/git/watcher.js +95 -0
  70. package/dist/git/watcher.js.map +1 -0
  71. package/dist/handlers/class-hierarchy.d.ts +11 -0
  72. package/dist/handlers/class-hierarchy.d.ts.map +1 -0
  73. package/dist/handlers/class-hierarchy.js +28 -0
  74. package/dist/handlers/class-hierarchy.js.map +1 -0
  75. package/dist/handlers/export-ast-index.d.ts +19 -0
  76. package/dist/handlers/export-ast-index.d.ts.map +1 -0
  77. package/dist/handlers/export-ast-index.js +107 -0
  78. package/dist/handlers/export-ast-index.js.map +1 -0
  79. package/dist/handlers/find-implementations.d.ts +11 -0
  80. package/dist/handlers/find-implementations.d.ts.map +1 -0
  81. package/dist/handlers/find-implementations.js +25 -0
  82. package/dist/handlers/find-implementations.js.map +1 -0
  83. package/dist/handlers/find-usages.d.ts +11 -0
  84. package/dist/handlers/find-usages.d.ts.map +1 -0
  85. package/dist/handlers/find-usages.js +23 -0
  86. package/dist/handlers/find-usages.js.map +1 -0
  87. package/dist/handlers/non-code.d.ts +25 -0
  88. package/dist/handlers/non-code.d.ts.map +1 -0
  89. package/dist/handlers/non-code.js +152 -0
  90. package/dist/handlers/non-code.js.map +1 -0
  91. package/dist/handlers/project-overview.d.ts +8 -0
  92. package/dist/handlers/project-overview.d.ts.map +1 -0
  93. package/dist/handlers/project-overview.js +84 -0
  94. package/dist/handlers/project-overview.js.map +1 -0
  95. package/dist/handlers/read-diff.d.ts +13 -0
  96. package/dist/handlers/read-diff.d.ts.map +1 -0
  97. package/dist/handlers/read-diff.js +174 -0
  98. package/dist/handlers/read-diff.js.map +1 -0
  99. package/dist/handlers/read-range.d.ts +14 -0
  100. package/dist/handlers/read-range.d.ts.map +1 -0
  101. package/dist/handlers/read-range.js +44 -0
  102. package/dist/handlers/read-range.js.map +1 -0
  103. package/dist/handlers/read-symbol.d.ts +16 -0
  104. package/dist/handlers/read-symbol.d.ts.map +1 -0
  105. package/dist/handlers/read-symbol.js +59 -0
  106. package/dist/handlers/read-symbol.js.map +1 -0
  107. package/dist/handlers/search-code.d.ts +14 -0
  108. package/dist/handlers/search-code.d.ts.map +1 -0
  109. package/dist/handlers/search-code.js +27 -0
  110. package/dist/handlers/search-code.js.map +1 -0
  111. package/dist/handlers/smart-read-many.d.ts +14 -0
  112. package/dist/handlers/smart-read-many.d.ts.map +1 -0
  113. package/dist/handlers/smart-read-many.js +32 -0
  114. package/dist/handlers/smart-read-many.js.map +1 -0
  115. package/dist/handlers/smart-read.d.ts +18 -0
  116. package/dist/handlers/smart-read.d.ts.map +1 -0
  117. package/dist/handlers/smart-read.js +86 -0
  118. package/dist/handlers/smart-read.js.map +1 -0
  119. package/dist/hooks/installer.d.ts +16 -0
  120. package/dist/hooks/installer.d.ts.map +1 -0
  121. package/dist/hooks/installer.js +89 -0
  122. package/dist/hooks/installer.js.map +1 -0
  123. package/dist/index.d.ts +3 -0
  124. package/dist/index.d.ts.map +1 -0
  125. package/dist/index.js +120 -0
  126. package/dist/index.js.map +1 -0
  127. package/dist/integration/context-mode-detector.d.ts +16 -0
  128. package/dist/integration/context-mode-detector.d.ts.map +1 -0
  129. package/dist/integration/context-mode-detector.js +53 -0
  130. package/dist/integration/context-mode-detector.js.map +1 -0
  131. package/dist/server.d.ts +36 -0
  132. package/dist/server.d.ts.map +1 -0
  133. package/dist/server.js +375 -0
  134. package/dist/server.js.map +1 -0
  135. package/dist/types.d.ts +122 -0
  136. package/dist/types.d.ts.map +1 -0
  137. package/dist/types.js +5 -0
  138. package/dist/types.js.map +1 -0
  139. package/package.json +67 -0
  140. package/skills/install/SKILL.md +14 -0
  141. package/skills/stats/SKILL.md +8 -0
  142. package/start.sh +27 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,wBAAsB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAU/E"}
@@ -0,0 +1,31 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+ import { DEFAULT_CONFIG } from './defaults.js';
4
+ export async function loadConfig(projectRoot) {
5
+ const configPath = resolve(projectRoot, '.token-pilot.json');
6
+ try {
7
+ const raw = await readFile(configPath, 'utf-8');
8
+ const userConfig = JSON.parse(raw);
9
+ return deepMerge(structuredClone(DEFAULT_CONFIG), userConfig);
10
+ }
11
+ catch {
12
+ return structuredClone(DEFAULT_CONFIG);
13
+ }
14
+ }
15
+ function deepMerge(target, source) {
16
+ const result = { ...target };
17
+ for (const key of Object.keys(source)) {
18
+ if (source[key] &&
19
+ typeof source[key] === 'object' &&
20
+ !Array.isArray(source[key]) &&
21
+ target[key] &&
22
+ typeof target[key] === 'object') {
23
+ result[key] = deepMerge(target[key], source[key]);
24
+ }
25
+ else {
26
+ result[key] = source[key];
27
+ }
28
+ }
29
+ return result;
30
+ }
31
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,WAAmB;IAClD,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAAC;IAE7D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAChD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnC,OAAO,SAAS,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,UAAU,CAAqB,CAAC;IACpF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,eAAe,CAAC,cAAc,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,MAA2B,EAAE,MAA2B;IACzE,MAAM,MAAM,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC;IAE7B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,IACE,MAAM,CAAC,GAAG,CAAC;YACX,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,QAAQ;YAC/B,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC3B,MAAM,CAAC,GAAG,CAAC;YACX,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,QAAQ,EAC/B,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QACpD,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { ContextEntry, LoadedRegion, SymbolInfo } from '../types.js';
2
+ /**
3
+ * Advisory Context Registry.
4
+ * Tracks what was sent to the LLM but never blocks re-sends.
5
+ * The MCP server cannot know the true state of the LLM's context window.
6
+ */
7
+ export declare class ContextRegistry {
8
+ private entries;
9
+ private sessionStart;
10
+ trackLoad(path: string, region: LoadedRegion): void;
11
+ setContentHash(path: string, hash: string): void;
12
+ getLoaded(path: string): LoadedRegion[] | null;
13
+ isSymbolLoaded(path: string, symbolName: string): boolean;
14
+ isStale(path: string, currentHash: string): boolean;
15
+ /**
16
+ * Generate a compact reminder for previously loaded content.
17
+ * Returns a brief summary instead of full re-read.
18
+ */
19
+ compactReminder(path: string, symbols: SymbolInfo[]): string;
20
+ forget(path: string, symbolName?: string): void;
21
+ forgetAll(): void;
22
+ summary(): {
23
+ files: number;
24
+ totalTokens: number;
25
+ sessionDuration: number;
26
+ entries: ContextEntry[];
27
+ };
28
+ estimateTokens(): number;
29
+ invalidateByGitDiff(changedFiles: string[]): void;
30
+ }
31
+ //# sourceMappingURL=context-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-registry.d.ts","sourceRoot":"","sources":["../../src/core/context-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAI1E;;;;GAIG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,YAAY,CAAc;IAElC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI;IA0BnD,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAOhD,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,EAAE,GAAG,IAAI;IAK9C,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO;IAMzD,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO;IAMnD;;;OAGG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM;IAiC5D,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI;IAgB/C,SAAS,IAAI,IAAI;IAIjB,OAAO,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,YAAY,EAAE,CAAA;KAAE;IAUnG,cAAc,IAAI,MAAM;IAQxB,mBAAmB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI;CAMlD"}
@@ -0,0 +1,133 @@
1
+ import { formatDuration } from './format-duration.js';
2
+ /**
3
+ * Advisory Context Registry.
4
+ * Tracks what was sent to the LLM but never blocks re-sends.
5
+ * The MCP server cannot know the true state of the LLM's context window.
6
+ */
7
+ export class ContextRegistry {
8
+ entries = new Map();
9
+ sessionStart = Date.now();
10
+ trackLoad(path, region) {
11
+ const existing = this.entries.get(path);
12
+ if (existing) {
13
+ // Replace region of same type/symbol, add new ones
14
+ const idx = existing.loaded.findIndex(r => r.type === region.type && r.symbolName === region.symbolName);
15
+ if (idx >= 0) {
16
+ existing.loaded[idx] = region;
17
+ }
18
+ else {
19
+ existing.loaded.push(region);
20
+ }
21
+ existing.tokenEstimate = existing.loaded.reduce((sum, r) => sum + r.tokens, 0);
22
+ existing.loadedAt = Date.now();
23
+ }
24
+ else {
25
+ this.entries.set(path, {
26
+ path,
27
+ loaded: [region],
28
+ contentHash: '',
29
+ tokenEstimate: region.tokens,
30
+ loadedAt: Date.now(),
31
+ });
32
+ }
33
+ }
34
+ setContentHash(path, hash) {
35
+ const entry = this.entries.get(path);
36
+ if (entry) {
37
+ entry.contentHash = hash;
38
+ }
39
+ }
40
+ getLoaded(path) {
41
+ const entry = this.entries.get(path);
42
+ return entry?.loaded ?? null;
43
+ }
44
+ isSymbolLoaded(path, symbolName) {
45
+ const entry = this.entries.get(path);
46
+ if (!entry)
47
+ return false;
48
+ return entry.loaded.some(r => r.symbolName === symbolName);
49
+ }
50
+ isStale(path, currentHash) {
51
+ const entry = this.entries.get(path);
52
+ if (!entry || !entry.contentHash)
53
+ return true;
54
+ return entry.contentHash !== currentHash;
55
+ }
56
+ /**
57
+ * Generate a compact reminder for previously loaded content.
58
+ * Returns a brief summary instead of full re-read.
59
+ */
60
+ compactReminder(path, symbols) {
61
+ const entry = this.entries.get(path);
62
+ if (!entry)
63
+ return '';
64
+ const elapsed = formatDuration(Date.now() - entry.loadedAt);
65
+ const lines = [
66
+ `REMINDER: ${path} (previously loaded ${elapsed} ago, unchanged)`,
67
+ '',
68
+ ];
69
+ for (const region of entry.loaded) {
70
+ if (region.type === 'structure') {
71
+ lines.push(` Structure loaded (${region.tokens} tokens)`);
72
+ // Add brief symbol list
73
+ for (const sym of symbols.slice(0, 5)) {
74
+ lines.push(` ${sym.kind} ${sym.name} [L${sym.location.startLine}-${sym.location.endLine}]`);
75
+ }
76
+ if (symbols.length > 5) {
77
+ lines.push(` ... (${symbols.length - 5} more symbols)`);
78
+ }
79
+ }
80
+ else if (region.type === 'symbol' && region.symbolName) {
81
+ lines.push(` ${region.symbolName} [L${region.startLine}-${region.endLine}] (${region.tokens} tokens)`);
82
+ }
83
+ else if (region.type === 'full') {
84
+ lines.push(` Full file loaded (${region.tokens} tokens)`);
85
+ }
86
+ }
87
+ lines.push('');
88
+ lines.push('HINT: File unchanged since last read. Use read_symbol() to reload specific parts, or read_diff() to see changes.');
89
+ return lines.join('\n');
90
+ }
91
+ forget(path, symbolName) {
92
+ if (symbolName) {
93
+ const entry = this.entries.get(path);
94
+ if (entry) {
95
+ entry.loaded = entry.loaded.filter(r => r.symbolName !== symbolName);
96
+ if (entry.loaded.length === 0) {
97
+ this.entries.delete(path);
98
+ }
99
+ else {
100
+ entry.tokenEstimate = entry.loaded.reduce((sum, r) => sum + r.tokens, 0);
101
+ }
102
+ }
103
+ }
104
+ else {
105
+ this.entries.delete(path);
106
+ }
107
+ }
108
+ forgetAll() {
109
+ this.entries.clear();
110
+ }
111
+ summary() {
112
+ const allEntries = Array.from(this.entries.values());
113
+ return {
114
+ files: allEntries.length,
115
+ totalTokens: allEntries.reduce((sum, e) => sum + e.tokenEstimate, 0),
116
+ sessionDuration: Date.now() - this.sessionStart,
117
+ entries: allEntries,
118
+ };
119
+ }
120
+ estimateTokens() {
121
+ let total = 0;
122
+ for (const entry of this.entries.values()) {
123
+ total += entry.tokenEstimate;
124
+ }
125
+ return total;
126
+ }
127
+ invalidateByGitDiff(changedFiles) {
128
+ for (const file of changedFiles) {
129
+ this.entries.delete(file);
130
+ }
131
+ }
132
+ }
133
+ //# sourceMappingURL=context-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-registry.js","sourceRoot":"","sources":["../../src/core/context-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { CacheEntry } from '../types.js';
2
+ export declare class FileCache {
3
+ private cache;
4
+ private maxSizeBytes;
5
+ private currentSizeBytes;
6
+ private smallFileThreshold;
7
+ private hits;
8
+ private misses;
9
+ constructor(maxSizeMB?: number, smallFileThreshold?: number);
10
+ get(filePath: string): CacheEntry | null;
11
+ set(filePath: string, entry: CacheEntry): void;
12
+ isSmallFile(filePath: string): Promise<boolean>;
13
+ isStale(filePath: string): Promise<boolean>;
14
+ invalidate(filePath?: string): void;
15
+ invalidateByGitDiff(changedFiles: string[]): Promise<void>;
16
+ stats(): {
17
+ entries: number;
18
+ sizeBytes: number;
19
+ hitRate: number;
20
+ };
21
+ getSmallFileThreshold(): number;
22
+ cachedPaths(): string[];
23
+ private evictLRU;
24
+ }
25
+ /**
26
+ * Read a file and create a cache-ready content hash.
27
+ */
28
+ export declare function readFileWithHash(filePath: string): Promise<{
29
+ content: string;
30
+ lines: string[];
31
+ hash: string;
32
+ mtime: number;
33
+ }>;
34
+ //# sourceMappingURL=file-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-cache.d.ts","sourceRoot":"","sources":["../../src/core/file-cache.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,aAAa,CAAC;AAE7D,qBAAa,SAAS;IACpB,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,MAAM,CAAK;gBAEP,SAAS,SAAM,EAAE,kBAAkB,SAAK;IAKpD,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAWxC,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI;IAiBxC,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAS/C,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAejD,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAa7B,mBAAmB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAMhE,KAAK,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAShE,qBAAqB,IAAI,MAAM;IAI/B,WAAW,IAAI,MAAM,EAAE;IAIvB,OAAO,CAAC,QAAQ;CAejB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CASD"}
@@ -0,0 +1,120 @@
1
+ import { stat, readFile } from 'node:fs/promises';
2
+ import { createHash } from 'node:crypto';
3
+ export class FileCache {
4
+ cache = new Map();
5
+ maxSizeBytes;
6
+ currentSizeBytes = 0;
7
+ smallFileThreshold;
8
+ hits = 0;
9
+ misses = 0;
10
+ constructor(maxSizeMB = 100, smallFileThreshold = 80) {
11
+ this.maxSizeBytes = maxSizeMB * 1024 * 1024;
12
+ this.smallFileThreshold = smallFileThreshold;
13
+ }
14
+ get(filePath) {
15
+ const entry = this.cache.get(filePath);
16
+ if (!entry) {
17
+ this.misses++;
18
+ return null;
19
+ }
20
+ entry.lastAccess = Date.now();
21
+ this.hits++;
22
+ return entry;
23
+ }
24
+ set(filePath, entry) {
25
+ const existingSize = this.cache.get(filePath)?.content.length ?? 0;
26
+ const newSize = entry.content.length;
27
+ // Evict LRU if needed
28
+ while (this.currentSizeBytes - existingSize + newSize > this.maxSizeBytes && this.cache.size > 0) {
29
+ this.evictLRU();
30
+ }
31
+ if (existingSize > 0) {
32
+ this.currentSizeBytes -= existingSize;
33
+ }
34
+ this.cache.set(filePath, entry);
35
+ this.currentSizeBytes += newSize;
36
+ }
37
+ async isSmallFile(filePath) {
38
+ try {
39
+ const content = await readFile(filePath, 'utf-8');
40
+ return content.split('\n').length <= this.smallFileThreshold;
41
+ }
42
+ catch {
43
+ return false;
44
+ }
45
+ }
46
+ async isStale(filePath) {
47
+ const entry = this.cache.get(filePath);
48
+ if (!entry)
49
+ return true;
50
+ try {
51
+ const fileStat = await stat(filePath);
52
+ if (fileStat.mtimeMs !== entry.mtime) {
53
+ return true;
54
+ }
55
+ return false;
56
+ }
57
+ catch {
58
+ return true;
59
+ }
60
+ }
61
+ invalidate(filePath) {
62
+ if (filePath) {
63
+ const entry = this.cache.get(filePath);
64
+ if (entry) {
65
+ this.currentSizeBytes -= entry.content.length;
66
+ this.cache.delete(filePath);
67
+ }
68
+ }
69
+ else {
70
+ this.cache.clear();
71
+ this.currentSizeBytes = 0;
72
+ }
73
+ }
74
+ async invalidateByGitDiff(changedFiles) {
75
+ for (const file of changedFiles) {
76
+ this.invalidate(file);
77
+ }
78
+ }
79
+ stats() {
80
+ const total = this.hits + this.misses;
81
+ return {
82
+ entries: this.cache.size,
83
+ sizeBytes: this.currentSizeBytes,
84
+ hitRate: total > 0 ? this.hits / total : 0,
85
+ };
86
+ }
87
+ getSmallFileThreshold() {
88
+ return this.smallFileThreshold;
89
+ }
90
+ cachedPaths() {
91
+ return Array.from(this.cache.keys());
92
+ }
93
+ evictLRU() {
94
+ let oldest = null;
95
+ let oldestTime = Infinity;
96
+ for (const [path, entry] of this.cache) {
97
+ if (entry.lastAccess < oldestTime) {
98
+ oldestTime = entry.lastAccess;
99
+ oldest = path;
100
+ }
101
+ }
102
+ if (oldest) {
103
+ this.invalidate(oldest);
104
+ }
105
+ }
106
+ }
107
+ /**
108
+ * Read a file and create a cache-ready content hash.
109
+ */
110
+ export async function readFileWithHash(filePath) {
111
+ const content = await readFile(filePath, 'utf-8');
112
+ const fileStat = await stat(filePath);
113
+ return {
114
+ content,
115
+ lines: content.split('\n'),
116
+ hash: createHash('sha256').update(content).digest('hex'),
117
+ mtime: fileStat.mtimeMs,
118
+ };
119
+ }
120
+ //# sourceMappingURL=file-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-cache.js","sourceRoot":"","sources":["../../src/core/file-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Shared duration formatter. Used by server and context-registry.
3
+ */
4
+ export declare function formatDuration(ms: number): string;
5
+ //# sourceMappingURL=format-duration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-duration.d.ts","sourceRoot":"","sources":["../../src/core/format-duration.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAMjD"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Shared duration formatter. Used by server and context-registry.
3
+ */
4
+ export function formatDuration(ms) {
5
+ const s = Math.floor(ms / 1000);
6
+ if (s < 60)
7
+ return `${s}s`;
8
+ const m = Math.floor(s / 60);
9
+ if (m < 60)
10
+ return `${m}m ${s % 60}s`;
11
+ return `${Math.floor(m / 60)}h ${m % 60}m`;
12
+ }
13
+ //# sourceMappingURL=format-duration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-duration.js","sourceRoot":"","sources":["../../src/core/format-duration.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,EAAU;IACvC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,GAAG,CAAC,GAAG,CAAC;IAC3B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IAC7B,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC;IACtC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,26 @@
1
+ import type { ContextModeStatus } from '../integration/context-mode-detector.js';
2
+ export interface ToolCall {
3
+ tool: string;
4
+ path?: string;
5
+ tokensReturned: number;
6
+ tokensWouldBe: number;
7
+ timestamp: number;
8
+ delegatedToContextMode?: boolean;
9
+ }
10
+ /**
11
+ * Tracks token savings and tool usage across a session.
12
+ * When context-mode is detected, includes unified reporting.
13
+ */
14
+ export declare class SessionAnalytics {
15
+ private calls;
16
+ private sessionStart;
17
+ private contextModeStatus;
18
+ setContextModeStatus(status: ContextModeStatus): void;
19
+ record(call: ToolCall): void;
20
+ /**
21
+ * Generate a session report.
22
+ */
23
+ report(): string;
24
+ reset(): void;
25
+ }
26
+ //# sourceMappingURL=session-analytics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-analytics.d.ts","sourceRoot":"","sources":["../../src/core/session-analytics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAEjF,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,iBAAiB,CAA0E;IAEnG,oBAAoB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;IAIrD,MAAM,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI;IAI5B;;OAEG;IACH,MAAM,IAAI,MAAM;IA8EhB,KAAK,IAAI,IAAI;CAId"}
@@ -0,0 +1,91 @@
1
+ import { formatDuration } from './format-duration.js';
2
+ /**
3
+ * Tracks token savings and tool usage across a session.
4
+ * When context-mode is detected, includes unified reporting.
5
+ */
6
+ export class SessionAnalytics {
7
+ calls = [];
8
+ sessionStart = Date.now();
9
+ contextModeStatus = { detected: false, source: 'none', toolPrefix: '' };
10
+ setContextModeStatus(status) {
11
+ this.contextModeStatus = status;
12
+ }
13
+ record(call) {
14
+ this.calls.push(call);
15
+ }
16
+ /**
17
+ * Generate a session report.
18
+ */
19
+ report() {
20
+ const duration = formatDuration(Date.now() - this.sessionStart);
21
+ const totalReturned = this.calls.reduce((s, c) => s + c.tokensReturned, 0);
22
+ const totalWouldBe = this.calls.reduce((s, c) => s + c.tokensWouldBe, 0);
23
+ const saved = totalWouldBe > 0 ? Math.round((1 - totalReturned / totalWouldBe) * 100) : 0;
24
+ // Group by tool
25
+ const byTool = new Map();
26
+ for (const c of this.calls) {
27
+ const existing = byTool.get(c.tool) ?? { count: 0, tokens: 0, saved: 0 };
28
+ existing.count++;
29
+ existing.tokens += c.tokensReturned;
30
+ existing.saved += Math.max(0, c.tokensWouldBe - c.tokensReturned);
31
+ byTool.set(c.tool, existing);
32
+ }
33
+ const lines = [
34
+ `SESSION ANALYTICS (${duration})`,
35
+ '',
36
+ `Total tool calls: ${this.calls.length}`,
37
+ `Tokens returned: ~${totalReturned}`,
38
+ `Tokens saved: ~${totalWouldBe - totalReturned} (${saved}% reduction)`,
39
+ '',
40
+ 'By tool:',
41
+ ];
42
+ for (const [tool, stats] of byTool) {
43
+ lines.push(` ${tool}: ${stats.count} calls, ~${stats.tokens} tokens returned, ~${stats.saved} saved`);
44
+ }
45
+ // Top files by savings
46
+ const byFile = new Map();
47
+ for (const c of this.calls) {
48
+ if (c.path) {
49
+ const current = byFile.get(c.path) ?? 0;
50
+ byFile.set(c.path, current + Math.max(0, c.tokensWouldBe - c.tokensReturned));
51
+ }
52
+ }
53
+ const topFiles = Array.from(byFile.entries())
54
+ .sort((a, b) => b[1] - a[1])
55
+ .slice(0, 5);
56
+ if (topFiles.length > 0) {
57
+ lines.push('');
58
+ lines.push('Top files by savings:');
59
+ for (const [file, saved] of topFiles) {
60
+ lines.push(` ${file}: ~${saved} tokens saved`);
61
+ }
62
+ }
63
+ // Reminders served (compact reminders that avoided re-reads)
64
+ const reminders = this.calls.filter(c => c.tokensWouldBe > 0 && c.tokensReturned < c.tokensWouldBe * 0.1);
65
+ if (reminders.length > 0) {
66
+ lines.push('');
67
+ lines.push(`Compact reminders served: ${reminders.length} (avoided full re-reads)`);
68
+ }
69
+ // Delegation stats
70
+ const delegated = this.calls.filter(c => c.delegatedToContextMode);
71
+ if (delegated.length > 0) {
72
+ lines.push('');
73
+ lines.push(`Delegated to context-mode: ${delegated.length} calls`);
74
+ }
75
+ // Context-mode companion status
76
+ if (this.contextModeStatus.detected) {
77
+ lines.push('');
78
+ lines.push('--- Combined Architecture ---');
79
+ lines.push(`context-mode: active (detected via ${this.contextModeStatus.source})`);
80
+ lines.push('Token Pilot handles: code files (AST-level structural reading)');
81
+ lines.push('context-mode handles: shell output, logs, large data files (BM25-indexed)');
82
+ lines.push('TIP: Use /context-mode:stats for context-mode savings breakdown.');
83
+ }
84
+ return lines.join('\n');
85
+ }
86
+ reset() {
87
+ this.calls = [];
88
+ this.sessionStart = Date.now();
89
+ }
90
+ }
91
+ //# sourceMappingURL=session-analytics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-analytics.js","sourceRoot":"","sources":["../../src/core/session-analytics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { AstIndexClient } from '../ast-index/client.js';
2
+ import type { ResolvedSymbol, FileStructure } from '../types.js';
3
+ export declare class SymbolResolver {
4
+ private astIndex;
5
+ constructor(astIndex: AstIndexClient);
6
+ /**
7
+ * Resolve a symbol by qualified name.
8
+ * First tries ast-index, falls back to searching cached structure.
9
+ */
10
+ resolve(qualifiedName: string, structure?: FileStructure): Promise<ResolvedSymbol | null>;
11
+ /**
12
+ * Extract source code for a resolved symbol from file lines.
13
+ */
14
+ extractSource(resolved: ResolvedSymbol, lines: string[], options?: {
15
+ contextBefore?: number;
16
+ contextAfter?: number;
17
+ }): string;
18
+ private mapKind;
19
+ private findInStructure;
20
+ }
21
+ //# sourceMappingURL=symbol-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"symbol-resolver.d.ts","sourceRoot":"","sources":["../../src/core/symbol-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAc,aAAa,EAAc,MAAM,aAAa,CAAC;AAEzF,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAiB;gBAErB,QAAQ,EAAE,cAAc;IAIpC;;;OAGG;IACG,OAAO,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAoD/F;;OAEG;IACH,aAAa,CACX,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,GAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAO,GAC9D,MAAM;IAeT,OAAO,CAAC,OAAO;IAWf,OAAO,CAAC,eAAe;CAoBxB"}