token-pilot 0.13.0 → 0.14.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/hooks/hooks.json +9 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +29 -0
- package/README.md +28 -7
- package/dist/config/defaults.js +12 -0
- package/dist/core/architecture-fingerprint.d.ts +34 -0
- package/dist/core/architecture-fingerprint.js +127 -0
- package/dist/core/budget-planner.d.ts +21 -0
- package/dist/core/budget-planner.js +68 -0
- package/dist/core/confidence.d.ts +31 -0
- package/dist/core/confidence.js +99 -0
- package/dist/core/context-registry.d.ts +14 -0
- package/dist/core/context-registry.js +55 -0
- package/dist/core/decision-trace.d.ts +31 -0
- package/dist/core/decision-trace.js +45 -0
- package/dist/core/intent-classifier.d.ts +13 -0
- package/dist/core/intent-classifier.js +44 -0
- package/dist/core/policy-engine.d.ts +41 -0
- package/dist/core/policy-engine.js +76 -0
- package/dist/core/session-analytics.d.ts +8 -0
- package/dist/core/session-analytics.js +86 -7
- package/dist/core/session-cache.d.ts +74 -0
- package/dist/core/session-cache.js +162 -0
- package/dist/core/validation.d.ts +3 -0
- package/dist/core/validation.js +3 -0
- package/dist/git/file-watcher.d.ts +6 -0
- package/dist/git/file-watcher.js +18 -2
- package/dist/git/watcher.d.ts +3 -0
- package/dist/git/watcher.js +6 -0
- package/dist/handlers/code-audit.d.ts +7 -2
- package/dist/handlers/code-audit.js +19 -5
- package/dist/handlers/explore-area.d.ts +10 -0
- package/dist/handlers/explore-area.js +39 -13
- package/dist/handlers/find-unused.d.ts +3 -0
- package/dist/handlers/find-unused.js +3 -2
- package/dist/handlers/find-usages.d.ts +7 -0
- package/dist/handlers/find-usages.js +36 -5
- package/dist/handlers/module-info.d.ts +3 -0
- package/dist/handlers/module-info.js +22 -2
- package/dist/handlers/project-overview.d.ts +1 -1
- package/dist/handlers/project-overview.js +18 -2
- package/dist/handlers/read-for-edit.d.ts +3 -0
- package/dist/handlers/read-for-edit.js +185 -3
- package/dist/handlers/read-range.d.ts +1 -1
- package/dist/handlers/read-range.js +16 -1
- package/dist/handlers/read-symbol.d.ts +1 -1
- package/dist/handlers/read-symbol.js +26 -2
- package/dist/handlers/related-files.d.ts +11 -0
- package/dist/handlers/related-files.js +178 -42
- package/dist/handlers/smart-read-many.js +70 -16
- package/dist/handlers/smart-read.js +10 -1
- package/dist/handlers/test-summary.js +26 -3
- package/dist/hooks/installer.d.ts +12 -8
- package/dist/hooks/installer.js +24 -8
- package/dist/index.d.ts +16 -1
- package/dist/index.js +61 -55
- package/dist/server.js +395 -30
- package/dist/types.d.ts +12 -0
- package/package.json +5 -3
- package/start.sh +28 -27
- package/dist/handlers/class-hierarchy.d.ts +0 -11
- package/dist/handlers/class-hierarchy.js +0 -28
- package/dist/handlers/export-ast-index.d.ts +0 -22
- package/dist/handlers/export-ast-index.js +0 -175
- package/dist/handlers/find-implementations.d.ts +0 -11
- package/dist/handlers/find-implementations.js +0 -27
- package/dist/handlers/search-code.d.ts +0 -14
- package/dist/handlers/search-code.js +0 -32
package/dist/git/file-watcher.js
CHANGED
|
@@ -17,6 +17,8 @@ export class FileWatcher {
|
|
|
17
17
|
watcher = null;
|
|
18
18
|
watchedFiles = new Set();
|
|
19
19
|
updateTimer = null;
|
|
20
|
+
fileChangeCallback = null;
|
|
21
|
+
astUpdateCallback = null;
|
|
20
22
|
constructor(_projectRoot, fileCache, contextRegistry, _ignore, astIndex) {
|
|
21
23
|
this.fileCache = fileCache;
|
|
22
24
|
this.contextRegistry = contextRegistry;
|
|
@@ -37,6 +39,7 @@ export class FileWatcher {
|
|
|
37
39
|
if (this.fileCache.get(absPath)) {
|
|
38
40
|
this.fileCache.invalidate(absPath);
|
|
39
41
|
}
|
|
42
|
+
this.fileChangeCallback?.(absPath);
|
|
40
43
|
this.scheduleIndexUpdate();
|
|
41
44
|
});
|
|
42
45
|
this.watcher.on('unlink', (filePath) => {
|
|
@@ -44,6 +47,7 @@ export class FileWatcher {
|
|
|
44
47
|
this.fileCache.invalidate(absPath);
|
|
45
48
|
this.contextRegistry.forget(absPath);
|
|
46
49
|
this.watchedFiles.delete(absPath);
|
|
50
|
+
this.fileChangeCallback?.(absPath);
|
|
47
51
|
this.scheduleIndexUpdate();
|
|
48
52
|
});
|
|
49
53
|
}
|
|
@@ -53,10 +57,22 @@ export class FileWatcher {
|
|
|
53
57
|
return;
|
|
54
58
|
if (this.updateTimer)
|
|
55
59
|
clearTimeout(this.updateTimer);
|
|
56
|
-
this.updateTimer = setTimeout(() => {
|
|
57
|
-
|
|
60
|
+
this.updateTimer = setTimeout(async () => {
|
|
61
|
+
try {
|
|
62
|
+
await this.astIndex?.incrementalUpdate();
|
|
63
|
+
this.astUpdateCallback?.();
|
|
64
|
+
}
|
|
65
|
+
catch { /* ignore */ }
|
|
58
66
|
}, FileWatcher.UPDATE_DEBOUNCE_MS);
|
|
59
67
|
}
|
|
68
|
+
/** Register callback for file change/unlink events. */
|
|
69
|
+
onFileChange(callback) {
|
|
70
|
+
this.fileChangeCallback = callback;
|
|
71
|
+
}
|
|
72
|
+
/** Register callback for after AST index incremental update completes. */
|
|
73
|
+
onAstUpdate(callback) {
|
|
74
|
+
this.astUpdateCallback = callback;
|
|
75
|
+
}
|
|
60
76
|
/** Add a specific file to watch. Called after smart_read/read_symbol loads a file. */
|
|
61
77
|
watchFile(filePath) {
|
|
62
78
|
const absPath = resolve(filePath);
|
package/dist/git/watcher.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export declare class GitWatcher {
|
|
|
7
7
|
private watcher;
|
|
8
8
|
private headRef;
|
|
9
9
|
private enabled;
|
|
10
|
+
private branchSwitchCallback;
|
|
10
11
|
constructor(projectRoot: string, fileCache: FileCache, contextRegistry: ContextRegistry, enabled: boolean);
|
|
11
12
|
start(): Promise<void>;
|
|
12
13
|
stop(): void;
|
|
@@ -19,6 +20,8 @@ export declare class GitWatcher {
|
|
|
19
20
|
* Get files changed in the last N commits.
|
|
20
21
|
*/
|
|
21
22
|
getRecentlyChangedFiles(commits?: number): Promise<string[]>;
|
|
23
|
+
/** Register callback for branch switch events. */
|
|
24
|
+
onBranchSwitchEvent(callback: (changedFiles: string[]) => void): void;
|
|
22
25
|
private onBranchSwitch;
|
|
23
26
|
private readHead;
|
|
24
27
|
}
|
package/dist/git/watcher.js
CHANGED
|
@@ -11,6 +11,7 @@ export class GitWatcher {
|
|
|
11
11
|
watcher = null;
|
|
12
12
|
headRef = '';
|
|
13
13
|
enabled;
|
|
14
|
+
branchSwitchCallback = null;
|
|
14
15
|
constructor(projectRoot, fileCache, contextRegistry, enabled) {
|
|
15
16
|
this.projectRoot = projectRoot;
|
|
16
17
|
this.fileCache = fileCache;
|
|
@@ -78,6 +79,10 @@ export class GitWatcher {
|
|
|
78
79
|
return [];
|
|
79
80
|
}
|
|
80
81
|
}
|
|
82
|
+
/** Register callback for branch switch events. */
|
|
83
|
+
onBranchSwitchEvent(callback) {
|
|
84
|
+
this.branchSwitchCallback = callback;
|
|
85
|
+
}
|
|
81
86
|
async onBranchSwitch() {
|
|
82
87
|
// On branch switch, get files that differ between old and new branch
|
|
83
88
|
// and selectively invalidate only those
|
|
@@ -85,6 +90,7 @@ export class GitWatcher {
|
|
|
85
90
|
if (changed.length > 0) {
|
|
86
91
|
await this.fileCache.invalidateByGitDiff(changed);
|
|
87
92
|
this.contextRegistry.invalidateByGitDiff(changed);
|
|
93
|
+
this.branchSwitchCallback?.(changed);
|
|
88
94
|
}
|
|
89
95
|
}
|
|
90
96
|
async readHead() {
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import type { AstIndexClient } from '../ast-index/client.js';
|
|
2
2
|
import type { CodeAuditArgs } from '../core/validation.js';
|
|
3
|
-
|
|
3
|
+
type AuditResult = {
|
|
4
4
|
content: Array<{
|
|
5
5
|
type: 'text';
|
|
6
6
|
text: string;
|
|
7
7
|
}>;
|
|
8
|
-
|
|
8
|
+
meta: {
|
|
9
|
+
files: string[];
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export declare function handleCodeAudit(args: CodeAuditArgs, projectRoot: string, astIndex: AstIndexClient): Promise<AuditResult>;
|
|
13
|
+
export {};
|
|
9
14
|
//# sourceMappingURL=code-audit.d.ts.map
|
|
@@ -6,6 +6,7 @@ export async function handleCodeAudit(args, projectRoot, astIndex) {
|
|
|
6
6
|
type: 'text',
|
|
7
7
|
text: 'ast-index is not available (project root too broad or index oversized). Use Grep/ripgrep for pattern search.',
|
|
8
8
|
}],
|
|
9
|
+
meta: { files: [] },
|
|
9
10
|
};
|
|
10
11
|
}
|
|
11
12
|
const limit = args.limit ?? 50;
|
|
@@ -27,6 +28,7 @@ export async function handleCodeAudit(args, projectRoot, astIndex) {
|
|
|
27
28
|
type: 'text',
|
|
28
29
|
text: `Unknown check type: "${args.check}". Use: pattern, todo, deprecated, annotations, all`,
|
|
29
30
|
}],
|
|
31
|
+
meta: { files: [] },
|
|
30
32
|
};
|
|
31
33
|
}
|
|
32
34
|
}
|
|
@@ -42,6 +44,7 @@ async function handlePattern(pattern, lang, limit, projectRoot, astIndex) {
|
|
|
42
44
|
type: 'text',
|
|
43
45
|
text: `PATTERN SEARCH: "${pattern}"${lang ? ` (${lang})` : ''}\n\nNo matches found.\n\nHINT: Try Grep/ripgrep for text-based search if the pattern is not structural.`,
|
|
44
46
|
}],
|
|
47
|
+
meta: { files: [] },
|
|
45
48
|
};
|
|
46
49
|
}
|
|
47
50
|
// Group by file
|
|
@@ -64,7 +67,7 @@ async function handlePattern(pattern, lang, limit, projectRoot, astIndex) {
|
|
|
64
67
|
lines.push('');
|
|
65
68
|
}
|
|
66
69
|
lines.push('HINT: Use read_symbol() to inspect specific matches, or Grep for text-based counting.');
|
|
67
|
-
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
70
|
+
return { content: [{ type: 'text', text: lines.join('\n') }], meta: { files: [...byFile.keys()] } };
|
|
68
71
|
}
|
|
69
72
|
catch (err) {
|
|
70
73
|
// ast-grep not installed — return the error message
|
|
@@ -73,6 +76,7 @@ async function handlePattern(pattern, lang, limit, projectRoot, astIndex) {
|
|
|
73
76
|
type: 'text',
|
|
74
77
|
text: `PATTERN SEARCH ERROR:\n${err instanceof Error ? err.message : String(err)}\n\nFallback: Use Grep/ripgrep for text-based pattern search.`,
|
|
75
78
|
}],
|
|
79
|
+
meta: { files: [] },
|
|
76
80
|
};
|
|
77
81
|
}
|
|
78
82
|
}
|
|
@@ -85,6 +89,7 @@ async function handleTodo(limit, projectRoot, astIndex) {
|
|
|
85
89
|
type: 'text',
|
|
86
90
|
text: 'TODO/FIXME COMMENTS: none found.\n\nHINT: ast-index may not detect all comment formats. Try Grep: grep -rn "TODO\\|FIXME\\|HACK" --include="*.ts"',
|
|
87
91
|
}],
|
|
92
|
+
meta: { files: [] },
|
|
88
93
|
};
|
|
89
94
|
}
|
|
90
95
|
// Group by kind
|
|
@@ -106,7 +111,8 @@ async function handleTodo(limit, projectRoot, astIndex) {
|
|
|
106
111
|
}
|
|
107
112
|
lines.push('');
|
|
108
113
|
}
|
|
109
|
-
|
|
114
|
+
const todoFiles = [...new Set(limited.map(e => e.file))];
|
|
115
|
+
return { content: [{ type: 'text', text: lines.join('\n') }], meta: { files: todoFiles } };
|
|
110
116
|
}
|
|
111
117
|
async function handleDeprecated(limit, projectRoot, astIndex) {
|
|
112
118
|
const entries = await astIndex.deprecated();
|
|
@@ -117,6 +123,7 @@ async function handleDeprecated(limit, projectRoot, astIndex) {
|
|
|
117
123
|
type: 'text',
|
|
118
124
|
text: 'DEPRECATED SYMBOLS: none found.\n\nHINT: ast-index detects @Deprecated annotations. Try Grep for other deprecation patterns.',
|
|
119
125
|
}],
|
|
126
|
+
meta: { files: [] },
|
|
120
127
|
};
|
|
121
128
|
}
|
|
122
129
|
const lines = [
|
|
@@ -129,7 +136,8 @@ async function handleDeprecated(limit, projectRoot, astIndex) {
|
|
|
129
136
|
}
|
|
130
137
|
lines.push('');
|
|
131
138
|
lines.push('HINT: Use read_symbol() to inspect deprecated symbols before removing them.');
|
|
132
|
-
|
|
139
|
+
const depFiles = [...new Set(limited.map(e => e.file))];
|
|
140
|
+
return { content: [{ type: 'text', text: lines.join('\n') }], meta: { files: depFiles } };
|
|
133
141
|
}
|
|
134
142
|
async function handleAnnotations(name, limit, projectRoot, astIndex) {
|
|
135
143
|
const entries = await astIndex.annotations(name);
|
|
@@ -140,6 +148,7 @@ async function handleAnnotations(name, limit, projectRoot, astIndex) {
|
|
|
140
148
|
type: 'text',
|
|
141
149
|
text: `ANNOTATIONS @${name}: none found.\n\nHINT: Try Grep for text-based search: grep -rn "@${name}" --include="*.ts"`,
|
|
142
150
|
}],
|
|
151
|
+
meta: { files: [] },
|
|
143
152
|
};
|
|
144
153
|
}
|
|
145
154
|
// Group by file
|
|
@@ -161,7 +170,8 @@ async function handleAnnotations(name, limit, projectRoot, astIndex) {
|
|
|
161
170
|
}
|
|
162
171
|
lines.push('');
|
|
163
172
|
}
|
|
164
|
-
|
|
173
|
+
const annFiles = [...new Set(limited.map(e => e.file))];
|
|
174
|
+
return { content: [{ type: 'text', text: lines.join('\n') }], meta: { files: annFiles } };
|
|
165
175
|
}
|
|
166
176
|
async function handleAll(limit, projectRoot, astIndex) {
|
|
167
177
|
// Run todo + deprecated in parallel
|
|
@@ -196,6 +206,10 @@ async function handleAll(limit, projectRoot, astIndex) {
|
|
|
196
206
|
sections.push('');
|
|
197
207
|
sections.push('HINT: Use code_audit(check="pattern", pattern="...") for structural pattern search (requires ast-grep).');
|
|
198
208
|
sections.push(' Use Grep for text-based counting and regex search.');
|
|
199
|
-
|
|
209
|
+
const allFiles = [...new Set([
|
|
210
|
+
...todos.slice(0, limit).map(e => e.file),
|
|
211
|
+
...deprecated.slice(0, limit).map(e => e.file),
|
|
212
|
+
])];
|
|
213
|
+
return { content: [{ type: 'text', text: sections.join('\n') }], meta: { files: allFiles } };
|
|
200
214
|
}
|
|
201
215
|
//# sourceMappingURL=code-audit.js.map
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import type { AstIndexClient } from '../ast-index/client.js';
|
|
2
2
|
import type { ExploreAreaArgs } from '../core/validation.js';
|
|
3
|
+
export interface ExploreAreaMeta {
|
|
4
|
+
dir: string;
|
|
5
|
+
codeFiles: string[];
|
|
6
|
+
testFiles: string[];
|
|
7
|
+
internalDeps: string[];
|
|
8
|
+
importedBy: string[];
|
|
9
|
+
externalDeps: string[];
|
|
10
|
+
changeCount: number;
|
|
11
|
+
}
|
|
3
12
|
export declare function handleExploreArea(args: ExploreAreaArgs, projectRoot: string, astIndex: AstIndexClient): Promise<{
|
|
4
13
|
content: Array<{
|
|
5
14
|
type: 'text';
|
|
6
15
|
text: string;
|
|
7
16
|
}>;
|
|
17
|
+
meta: ExploreAreaMeta;
|
|
8
18
|
}>;
|
|
9
19
|
//# sourceMappingURL=explore-area.d.ts.map
|
|
@@ -20,6 +20,15 @@ export async function handleExploreArea(args, projectRoot, astIndex) {
|
|
|
20
20
|
if (!pathStat) {
|
|
21
21
|
return {
|
|
22
22
|
content: [{ type: 'text', text: `Path "${args.path}" not found.` }],
|
|
23
|
+
meta: {
|
|
24
|
+
dir: args.path,
|
|
25
|
+
codeFiles: [],
|
|
26
|
+
testFiles: [],
|
|
27
|
+
internalDeps: [],
|
|
28
|
+
importedBy: [],
|
|
29
|
+
externalDeps: [],
|
|
30
|
+
changeCount: 0,
|
|
31
|
+
},
|
|
23
32
|
};
|
|
24
33
|
}
|
|
25
34
|
if (!pathStat.isDirectory()) {
|
|
@@ -49,17 +58,17 @@ export async function handleExploreArea(args, projectRoot, astIndex) {
|
|
|
49
58
|
lines.push('');
|
|
50
59
|
}
|
|
51
60
|
// Imports
|
|
52
|
-
const importLines = extractResult(importsSection);
|
|
61
|
+
const importLines = extractResult(importsSection)?.lines ?? null;
|
|
53
62
|
if (importLines) {
|
|
54
63
|
lines.push(...importLines);
|
|
55
64
|
}
|
|
56
65
|
// Tests
|
|
57
|
-
const testLines = extractResult(testsSection);
|
|
66
|
+
const testLines = extractResult(testsSection)?.lines ?? null;
|
|
58
67
|
if (testLines) {
|
|
59
68
|
lines.push(...testLines);
|
|
60
69
|
}
|
|
61
70
|
// Changes
|
|
62
|
-
const changeLines = extractResult(changesSection);
|
|
71
|
+
const changeLines = extractResult(changesSection)?.lines ?? null;
|
|
63
72
|
if (changeLines) {
|
|
64
73
|
lines.push(...changeLines);
|
|
65
74
|
}
|
|
@@ -69,7 +78,18 @@ export async function handleExploreArea(args, projectRoot, astIndex) {
|
|
|
69
78
|
lines.push('... truncated. Use outline() on specific subdirectories for details.');
|
|
70
79
|
}
|
|
71
80
|
lines.push('HINT: Use smart_read(file) for details, read_symbol(path, symbol) for source code, find_usages(symbol) for references.');
|
|
72
|
-
return {
|
|
81
|
+
return {
|
|
82
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
83
|
+
meta: {
|
|
84
|
+
dir: relDir,
|
|
85
|
+
codeFiles: codeFiles.map((file) => relative(projectRoot, file)).sort(),
|
|
86
|
+
testFiles: extractResult(testsSection)?.testFiles ?? [],
|
|
87
|
+
internalDeps: extractResult(importsSection)?.internalDeps ?? [],
|
|
88
|
+
importedBy: extractResult(importsSection)?.importedBy ?? [],
|
|
89
|
+
externalDeps: extractResult(importsSection)?.externalDeps ?? [],
|
|
90
|
+
changeCount: extractResult(changesSection)?.count ?? 0,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
73
93
|
}
|
|
74
94
|
// ──────────────────────────────────────────────
|
|
75
95
|
// Outline section — reuses outlineDir from outline.ts
|
|
@@ -84,7 +104,7 @@ async function buildOutlineSection(absPath, projectRoot, astIndex) {
|
|
|
84
104
|
// ──────────────────────────────────────────────
|
|
85
105
|
async function buildImportsSection(codeFiles, absPath, projectRoot, astIndex) {
|
|
86
106
|
if (!astIndex.isAvailable() || astIndex.isDisabled() || astIndex.isOversized()) {
|
|
87
|
-
return [];
|
|
107
|
+
return { lines: [], internalDeps: [], importedBy: [], externalDeps: [] };
|
|
88
108
|
}
|
|
89
109
|
const filesToAnalyze = codeFiles.slice(0, MAX_IMPORT_FILES);
|
|
90
110
|
const externalDeps = new Set();
|
|
@@ -150,7 +170,12 @@ async function buildImportsSection(codeFiles, absPath, projectRoot, astIndex) {
|
|
|
150
170
|
}
|
|
151
171
|
if (lines.length > 0)
|
|
152
172
|
lines.push('');
|
|
153
|
-
return
|
|
173
|
+
return {
|
|
174
|
+
lines,
|
|
175
|
+
internalDeps: Array.from(internalDeps).sort(),
|
|
176
|
+
importedBy: Array.from(importedBy).sort(),
|
|
177
|
+
externalDeps: Array.from(externalDeps).sort(),
|
|
178
|
+
};
|
|
154
179
|
}
|
|
155
180
|
// ──────────────────────────────────────────────
|
|
156
181
|
// Tests section — find test/spec files matching area files
|
|
@@ -215,11 +240,11 @@ async function buildTestsSection(codeFiles, absPath, projectRoot) {
|
|
|
215
240
|
catch { /* skip unreadable dirs */ }
|
|
216
241
|
}
|
|
217
242
|
if (testFiles.length === 0)
|
|
218
|
-
return [];
|
|
243
|
+
return { lines: [], testFiles: [] };
|
|
219
244
|
const lines = [];
|
|
220
245
|
lines.push(`TESTS: ${testFiles.join(', ')}`);
|
|
221
246
|
lines.push('');
|
|
222
|
-
return lines;
|
|
247
|
+
return { lines, testFiles: [...testFiles].sort() };
|
|
223
248
|
}
|
|
224
249
|
// ──────────────────────────────────────────────
|
|
225
250
|
// Changes section — recent git log for this area
|
|
@@ -228,24 +253,25 @@ async function buildChangesSection(relDir, projectRoot) {
|
|
|
228
253
|
try {
|
|
229
254
|
const { stdout } = await execFileAsync('git', ['log', '--oneline', '-5', '--', relDir], { cwd: projectRoot, timeout: 5000 });
|
|
230
255
|
if (!stdout.trim())
|
|
231
|
-
return [];
|
|
256
|
+
return { lines: [], count: 0 };
|
|
232
257
|
const lines = [];
|
|
258
|
+
const commits = stdout.trim().split('\n');
|
|
233
259
|
lines.push('RECENT CHANGES:');
|
|
234
|
-
for (const line of
|
|
260
|
+
for (const line of commits) {
|
|
235
261
|
lines.push(` ${line}`);
|
|
236
262
|
}
|
|
237
263
|
lines.push('');
|
|
238
|
-
return lines;
|
|
264
|
+
return { lines, count: commits.length };
|
|
239
265
|
}
|
|
240
266
|
catch {
|
|
241
|
-
return [];
|
|
267
|
+
return { lines: [], count: 0 };
|
|
242
268
|
}
|
|
243
269
|
}
|
|
244
270
|
// ──────────────────────────────────────────────
|
|
245
271
|
// Helpers
|
|
246
272
|
// ──────────────────────────────────────────────
|
|
247
273
|
function extractResult(settled) {
|
|
248
|
-
if (settled.status === 'fulfilled' && settled.value
|
|
274
|
+
if (settled.status === 'fulfilled' && settled.value) {
|
|
249
275
|
return settled.value;
|
|
250
276
|
}
|
|
251
277
|
return null;
|
|
@@ -18,7 +18,7 @@ export async function handleFindUnused(args, astIndex) {
|
|
|
18
18
|
return { content: [{ type: 'text', text: 'find_unused is disabled: ' + (astIndex.isDisabled()
|
|
19
19
|
? 'project root not detected. Call smart_read() on any project file first — this auto-detects the project root and enables ast-index tools.'
|
|
20
20
|
: 'ast-index built >50k files (likely includes node_modules). Ensure node_modules is in .gitignore.') +
|
|
21
|
-
'\nAlternative: use grep_search to find unused exports manually.' }] };
|
|
21
|
+
'\nAlternative: use grep_search to find unused exports manually.' }], meta: { files: [] } };
|
|
22
22
|
}
|
|
23
23
|
const requestLimit = (args.limit ?? 30) + 20; // extra to compensate for filtering
|
|
24
24
|
const unused = await astIndex.unusedSymbols({
|
|
@@ -67,6 +67,7 @@ export async function handleFindUnused(args, astIndex) {
|
|
|
67
67
|
? `No unused symbols found in module "${args.module}".${excluded > 0 ? ` (${excluded} constructors/protocol methods excluded)` : ''}`
|
|
68
68
|
: `No unused symbols found in the project.${excluded > 0 ? ` (${excluded} constructors/protocol methods excluded)` : ''}`,
|
|
69
69
|
}],
|
|
70
|
+
meta: { files: [] },
|
|
70
71
|
};
|
|
71
72
|
}
|
|
72
73
|
const lines = [];
|
|
@@ -100,7 +101,7 @@ export async function handleFindUnused(args, astIndex) {
|
|
|
100
101
|
lines.push(`(${langExcluded} constructors/protocol methods excluded)`);
|
|
101
102
|
}
|
|
102
103
|
lines.push('NOTE: Verify before removing — symbols may be used dynamically, in tests, or via framework conventions.');
|
|
103
|
-
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
104
|
+
return { content: [{ type: 'text', text: lines.join('\n') }], meta: { files: uniqueFiles } };
|
|
104
105
|
}
|
|
105
106
|
/**
|
|
106
107
|
* Find decorators for a symbol by matching name + line in the outline tree.
|
|
@@ -15,5 +15,12 @@ export declare function handleFindUsages(args: FindUsagesArgs, astIndex: AstInde
|
|
|
15
15
|
type: 'text';
|
|
16
16
|
text: string;
|
|
17
17
|
}>;
|
|
18
|
+
meta: {
|
|
19
|
+
files: string[];
|
|
20
|
+
definitions: number;
|
|
21
|
+
imports: number;
|
|
22
|
+
usages: number;
|
|
23
|
+
total: number;
|
|
24
|
+
};
|
|
18
25
|
}>;
|
|
19
26
|
//# sourceMappingURL=find-usages.d.ts.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { assessConfidence, formatConfidence } from '../core/confidence.js';
|
|
1
2
|
/**
|
|
2
3
|
* Escape special regex characters in a string.
|
|
3
4
|
*/
|
|
@@ -33,10 +34,16 @@ const LANG_EXT_MAP = {
|
|
|
33
34
|
*/
|
|
34
35
|
export async function handleFindUsages(args, astIndex) {
|
|
35
36
|
if (astIndex.isDisabled() || astIndex.isOversized()) {
|
|
36
|
-
return {
|
|
37
|
+
return {
|
|
38
|
+
content: [{
|
|
39
|
+
type: 'text',
|
|
40
|
+
text: 'find_usages is disabled: ' + (astIndex.isDisabled()
|
|
37
41
|
? 'project root not detected. Call smart_read() on any project file first — this auto-detects the project root and enables ast-index tools.'
|
|
38
|
-
: 'ast-index built >50k files (likely includes node_modules). Ensure node_modules is in .gitignore.')
|
|
39
|
-
'\nAlternative: use grep_search to find symbol references.'
|
|
42
|
+
: 'ast-index built >50k files (likely includes node_modules). Ensure node_modules is in .gitignore.')
|
|
43
|
+
+ '\nAlternative: use grep_search to find symbol references.',
|
|
44
|
+
}],
|
|
45
|
+
meta: { files: [], definitions: 0, imports: 0, usages: 0, total: 0 },
|
|
46
|
+
};
|
|
40
47
|
}
|
|
41
48
|
// Run refs + search in parallel
|
|
42
49
|
const [refs, searchResults] = await Promise.all([
|
|
@@ -129,7 +136,10 @@ export async function handleFindUsages(args, astIndex) {
|
|
|
129
136
|
if (!astIndex.isAvailable()) {
|
|
130
137
|
hints.push('WARNING: ast-index is not available.');
|
|
131
138
|
}
|
|
132
|
-
return {
|
|
139
|
+
return {
|
|
140
|
+
content: [{ type: 'text', text: hints.join('\n') }],
|
|
141
|
+
meta: { files: [], definitions: 0, imports: 0, usages: 0, total: 0 },
|
|
142
|
+
};
|
|
133
143
|
}
|
|
134
144
|
// Build header with active filters
|
|
135
145
|
const filterHints = [];
|
|
@@ -169,6 +179,27 @@ export async function handleFindUsages(args, astIndex) {
|
|
|
169
179
|
lines.push('');
|
|
170
180
|
}
|
|
171
181
|
lines.push('HINT: Use read_symbol() or read_range() to load specific results.');
|
|
172
|
-
|
|
182
|
+
// Confidence metadata
|
|
183
|
+
const confidenceMeta = assessConfidence({
|
|
184
|
+
refsFound: totalCount > 0,
|
|
185
|
+
astAvailable: astIndex.isAvailable(),
|
|
186
|
+
symbolResolved: definitions.length > 0,
|
|
187
|
+
});
|
|
188
|
+
lines.push(formatConfidence(confidenceMeta));
|
|
189
|
+
const files = Array.from(new Set([
|
|
190
|
+
...definitions.map((d) => d.file),
|
|
191
|
+
...allImports.map((i) => i.file),
|
|
192
|
+
...allUsages.map((u) => u.file),
|
|
193
|
+
])).sort();
|
|
194
|
+
return {
|
|
195
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
196
|
+
meta: {
|
|
197
|
+
files,
|
|
198
|
+
definitions: definitions.length,
|
|
199
|
+
imports: allImports.length,
|
|
200
|
+
usages: allUsages.length,
|
|
201
|
+
total: totalCount,
|
|
202
|
+
},
|
|
203
|
+
};
|
|
173
204
|
}
|
|
174
205
|
//# sourceMappingURL=find-usages.js.map
|
|
@@ -8,6 +8,7 @@ export async function handleModuleInfo(args, projectRoot, astIndex) {
|
|
|
8
8
|
text: '⚠ ast-index unavailable — module_info requires ast-index.\n' +
|
|
9
9
|
'DEGRADED: Use find_usages() + related_files() as alternatives for dependency analysis.',
|
|
10
10
|
}],
|
|
11
|
+
meta: { files: [] },
|
|
11
12
|
};
|
|
12
13
|
}
|
|
13
14
|
const check = args.check ?? 'all';
|
|
@@ -36,7 +37,7 @@ export async function handleModuleInfo(args, projectRoot, astIndex) {
|
|
|
36
37
|
sections.push('No modules detected. ast-index module analysis requires a modular project structure.');
|
|
37
38
|
sections.push('HINT: Use find_usages() for cross-file symbol references, related_files() for import graphs.');
|
|
38
39
|
}
|
|
39
|
-
return { content: [{ type: 'text', text: sections.join('\n') }] };
|
|
40
|
+
return { content: [{ type: 'text', text: sections.join('\n') }], meta: { files: [] } };
|
|
40
41
|
}
|
|
41
42
|
sections.push('');
|
|
42
43
|
// Run requested checks in parallel
|
|
@@ -115,7 +116,26 @@ export async function handleModuleInfo(args, projectRoot, astIndex) {
|
|
|
115
116
|
}
|
|
116
117
|
}
|
|
117
118
|
sections.push('HINT: Use smart_read() on module files, find_usages() for cross-module references.');
|
|
118
|
-
|
|
119
|
+
// Collect files from results for analytics
|
|
120
|
+
const metaFiles = [];
|
|
121
|
+
for (const r of results) {
|
|
122
|
+
if (r.status !== 'fulfilled')
|
|
123
|
+
continue;
|
|
124
|
+
const { type, data } = r.value;
|
|
125
|
+
switch (type) {
|
|
126
|
+
case 'api':
|
|
127
|
+
for (const a of data)
|
|
128
|
+
metaFiles.push(a.file);
|
|
129
|
+
break;
|
|
130
|
+
case 'deps':
|
|
131
|
+
case 'dependents':
|
|
132
|
+
case 'unused-deps':
|
|
133
|
+
for (const d of data)
|
|
134
|
+
metaFiles.push(d.path);
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { content: [{ type: 'text', text: sections.join('\n') }], meta: { files: [...new Set(metaFiles)] } };
|
|
119
139
|
}
|
|
120
140
|
function rel(projectRoot, absPath) {
|
|
121
141
|
return relative(projectRoot, absPath) || absPath;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AstIndexClient } from '../ast-index/client.js';
|
|
2
2
|
import type { ProjectOverviewArgs } from '../core/validation.js';
|
|
3
|
-
export declare function handleProjectOverview(args: ProjectOverviewArgs, projectRoot: string, astIndex: AstIndexClient): Promise<{
|
|
3
|
+
export declare function handleProjectOverview(args: ProjectOverviewArgs, projectRoot: string, astIndex: AstIndexClient, appVersion?: string): Promise<{
|
|
4
4
|
content: Array<{
|
|
5
5
|
type: 'text';
|
|
6
6
|
text: string;
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { detectProject } from '../core/project-detector.js';
|
|
2
|
-
|
|
2
|
+
import { loadFingerprint, saveFingerprint, buildFingerprint, formatCachedFingerprint, } from '../core/architecture-fingerprint.js';
|
|
3
|
+
export async function handleProjectOverview(args, projectRoot, astIndex, appVersion) {
|
|
3
4
|
const lines = [];
|
|
5
|
+
// Check for cached fingerprint
|
|
6
|
+
const cachedFp = await loadFingerprint(projectRoot);
|
|
7
|
+
if (cachedFp) {
|
|
8
|
+
lines.push(formatCachedFingerprint(cachedFp));
|
|
9
|
+
lines.push('');
|
|
10
|
+
}
|
|
4
11
|
// 1. Dual detection: ast-index + config scanner
|
|
5
12
|
let astIndexType;
|
|
6
13
|
let mapData = null;
|
|
@@ -117,7 +124,16 @@ export async function handleProjectOverview(args, projectRoot, astIndex) {
|
|
|
117
124
|
lines.push('');
|
|
118
125
|
}
|
|
119
126
|
lines.push('HINT: Use smart_read() on files, find_usages() for symbol references, outline() for directory overview.');
|
|
120
|
-
|
|
127
|
+
// Save fingerprint for next session
|
|
128
|
+
const outputText = lines.join('\n');
|
|
129
|
+
try {
|
|
130
|
+
const fp = buildFingerprint(outputText, appVersion ?? 'unknown');
|
|
131
|
+
await saveFingerprint(projectRoot, fp);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// Non-critical — don't fail the handler
|
|
135
|
+
}
|
|
136
|
+
return { content: [{ type: 'text', text: outputText }] };
|
|
121
137
|
}
|
|
122
138
|
// ──────────────────────────────────────────────
|
|
123
139
|
// Formatters
|
|
@@ -7,6 +7,9 @@ export interface ReadForEditArgs {
|
|
|
7
7
|
symbol?: string;
|
|
8
8
|
line?: number;
|
|
9
9
|
context?: number;
|
|
10
|
+
include_callers?: boolean;
|
|
11
|
+
include_tests?: boolean;
|
|
12
|
+
include_changes?: boolean;
|
|
10
13
|
}
|
|
11
14
|
export declare function handleReadForEdit(args: ReadForEditArgs, projectRoot: string, symbolResolver: SymbolResolver, fileCache: FileCache, contextRegistry: ContextRegistry, astIndex: AstIndexClient): Promise<{
|
|
12
15
|
content: Array<{
|