token-pilot 0.8.3 → 0.9.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +25 -0
- package/README.md +13 -10
- package/dist/ast-index/client.d.ts +15 -1
- package/dist/ast-index/client.js +179 -0
- package/dist/ast-index/types.d.ts +27 -1
- package/dist/ast-index/types.js +1 -1
- package/dist/core/project-detector.d.ts +42 -0
- package/dist/core/project-detector.js +362 -0
- package/dist/core/validation.d.ts +30 -4
- package/dist/core/validation.js +67 -8
- package/dist/handlers/find-usages.d.ts +3 -3
- package/dist/handlers/find-usages.js +88 -13
- package/dist/handlers/module-info.d.ts +9 -0
- package/dist/handlers/module-info.js +123 -0
- package/dist/handlers/outline.d.ts +1 -3
- package/dist/handlers/outline.js +51 -20
- package/dist/handlers/project-overview.d.ts +2 -1
- package/dist/handlers/project-overview.js +146 -107
- package/dist/server.js +46 -6
- package/package.json +1 -1
|
@@ -4,6 +4,23 @@
|
|
|
4
4
|
function escapeRegex(s) {
|
|
5
5
|
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
6
6
|
}
|
|
7
|
+
/** Extension map for lang filter (best-effort) */
|
|
8
|
+
const LANG_EXT_MAP = {
|
|
9
|
+
typescript: ['.ts', '.tsx'],
|
|
10
|
+
javascript: ['.js', '.jsx', '.mjs', '.cjs'],
|
|
11
|
+
php: ['.php'],
|
|
12
|
+
python: ['.py'],
|
|
13
|
+
rust: ['.rs'],
|
|
14
|
+
go: ['.go'],
|
|
15
|
+
java: ['.java'],
|
|
16
|
+
ruby: ['.rb'],
|
|
17
|
+
csharp: ['.cs'],
|
|
18
|
+
kotlin: ['.kt', '.kts'],
|
|
19
|
+
swift: ['.swift'],
|
|
20
|
+
dart: ['.dart'],
|
|
21
|
+
vue: ['.vue'],
|
|
22
|
+
svelte: ['.svelte'],
|
|
23
|
+
};
|
|
7
24
|
/**
|
|
8
25
|
* Find all usages of a symbol across the project.
|
|
9
26
|
*
|
|
@@ -11,6 +28,8 @@ function escapeRegex(s) {
|
|
|
11
28
|
* with `search` (text: catches imports and self-references that refs misses).
|
|
12
29
|
* Filter search results to exact word matches only (no substring matches).
|
|
13
30
|
* Deduplicate by file:line.
|
|
31
|
+
*
|
|
32
|
+
* v1.1: added scope, kind, limit, lang post-filters.
|
|
14
33
|
*/
|
|
15
34
|
export async function handleFindUsages(args, astIndex) {
|
|
16
35
|
if (astIndex.isDisabled() || astIndex.isOversized()) {
|
|
@@ -47,29 +66,89 @@ export async function handleFindUsages(args, astIndex) {
|
|
|
47
66
|
// Categorize additional results
|
|
48
67
|
const additionalImports = additional.filter(r => /\bimport\b/.test(r.text));
|
|
49
68
|
const additionalOther = additional.filter(r => !/\bimport\b/.test(r.text));
|
|
50
|
-
//
|
|
51
|
-
|
|
69
|
+
// Build mutable result arrays
|
|
70
|
+
let definitions = refs.definitions.map(d => ({ file: d.path, line: d.line, text: (d.signature ?? d.name).trim() }));
|
|
71
|
+
let allImports = [
|
|
52
72
|
...refs.imports.map(i => ({ file: i.path, line: i.line, text: (i.context ?? i.name).trim() })),
|
|
53
73
|
...additionalImports.map(r => ({ file: r.file, line: r.line, text: r.text })),
|
|
54
74
|
];
|
|
55
|
-
|
|
56
|
-
|
|
75
|
+
let allUsages = [
|
|
76
|
+
...refs.usages.map(u => ({ file: u.path, line: u.line, text: (u.context ?? u.name).trim() })),
|
|
77
|
+
...additionalOther,
|
|
78
|
+
];
|
|
79
|
+
// ─── Post-filters (v1.1) ───
|
|
80
|
+
// 1. Scope filter — by path prefix
|
|
81
|
+
if (args.scope) {
|
|
82
|
+
const scopePrefix = args.scope;
|
|
83
|
+
definitions = definitions.filter(d => d.file.includes(scopePrefix));
|
|
84
|
+
allImports = allImports.filter(i => i.file.includes(scopePrefix));
|
|
85
|
+
allUsages = allUsages.filter(u => u.file.includes(scopePrefix));
|
|
86
|
+
}
|
|
87
|
+
// 2. Lang filter — best-effort by file extension
|
|
88
|
+
if (args.lang) {
|
|
89
|
+
const langLower = args.lang.toLowerCase();
|
|
90
|
+
const exts = LANG_EXT_MAP[langLower] ?? [`.${langLower}`];
|
|
91
|
+
const matchesLang = (file) => exts.some(e => file.endsWith(e));
|
|
92
|
+
definitions = definitions.filter(d => matchesLang(d.file));
|
|
93
|
+
allImports = allImports.filter(i => matchesLang(i.file));
|
|
94
|
+
allUsages = allUsages.filter(u => matchesLang(u.file));
|
|
95
|
+
}
|
|
96
|
+
// 3. Kind filter — select sections
|
|
97
|
+
const kind = args.kind ?? 'all';
|
|
98
|
+
if (kind !== 'all') {
|
|
99
|
+
switch (kind) {
|
|
100
|
+
case 'definitions':
|
|
101
|
+
allImports = [];
|
|
102
|
+
allUsages = [];
|
|
103
|
+
break;
|
|
104
|
+
case 'imports':
|
|
105
|
+
definitions = [];
|
|
106
|
+
allUsages = [];
|
|
107
|
+
break;
|
|
108
|
+
case 'usages':
|
|
109
|
+
definitions = [];
|
|
110
|
+
allImports = [];
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// 4. Limit — per category
|
|
115
|
+
const limit = args.limit ?? 50;
|
|
116
|
+
definitions = definitions.slice(0, limit);
|
|
117
|
+
allImports = allImports.slice(0, limit);
|
|
118
|
+
allUsages = allUsages.slice(0, limit);
|
|
119
|
+
// ─── Output ───
|
|
120
|
+
const totalCount = definitions.length + allImports.length + allUsages.length;
|
|
57
121
|
if (totalCount === 0) {
|
|
58
122
|
const hints = [`No usages found for "${args.symbol}".`];
|
|
123
|
+
if (args.scope)
|
|
124
|
+
hints.push(` (filtered by scope: "${args.scope}")`);
|
|
125
|
+
if (args.lang)
|
|
126
|
+
hints.push(` (filtered by lang: "${args.lang}")`);
|
|
127
|
+
if (args.kind && args.kind !== 'all')
|
|
128
|
+
hints.push(` (filtered by kind: "${args.kind}")`);
|
|
59
129
|
if (!astIndex.isAvailable()) {
|
|
60
130
|
hints.push('WARNING: ast-index is not available.');
|
|
61
131
|
}
|
|
62
132
|
return { content: [{ type: 'text', text: hints.join('\n') }] };
|
|
63
133
|
}
|
|
134
|
+
// Build header with active filters
|
|
135
|
+
const filterHints = [];
|
|
136
|
+
if (args.scope)
|
|
137
|
+
filterHints.push(`scope="${args.scope}"`);
|
|
138
|
+
if (args.lang)
|
|
139
|
+
filterHints.push(`lang=${args.lang}`);
|
|
140
|
+
if (args.kind && args.kind !== 'all')
|
|
141
|
+
filterHints.push(`kind=${args.kind}`);
|
|
142
|
+
const filterStr = filterHints.length > 0 ? ` [${filterHints.join(', ')}]` : '';
|
|
64
143
|
const lines = [
|
|
65
|
-
`REFS: "${args.symbol}" (${totalCount} total: ${
|
|
144
|
+
`REFS: "${args.symbol}" (${totalCount} total: ${definitions.length} definitions, ${allImports.length} imports, ${allUsages.length} usages)${filterStr}`,
|
|
66
145
|
'',
|
|
67
146
|
];
|
|
68
|
-
if (
|
|
147
|
+
if (definitions.length > 0) {
|
|
69
148
|
lines.push('DEFINITIONS:');
|
|
70
|
-
for (const d of
|
|
71
|
-
lines.push(` ${d.
|
|
72
|
-
lines.push(` ${
|
|
149
|
+
for (const d of definitions) {
|
|
150
|
+
lines.push(` ${d.file}:${d.line}`);
|
|
151
|
+
lines.push(` ${d.text}`);
|
|
73
152
|
}
|
|
74
153
|
lines.push('');
|
|
75
154
|
}
|
|
@@ -81,10 +160,6 @@ export async function handleFindUsages(args, astIndex) {
|
|
|
81
160
|
}
|
|
82
161
|
lines.push('');
|
|
83
162
|
}
|
|
84
|
-
const allUsages = [
|
|
85
|
-
...refs.usages.map(u => ({ file: u.path, line: u.line, text: (u.context ?? u.name).trim() })),
|
|
86
|
-
...additionalOther,
|
|
87
|
-
];
|
|
88
163
|
if (allUsages.length > 0) {
|
|
89
164
|
lines.push('USAGES:');
|
|
90
165
|
for (const u of allUsages) {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AstIndexClient } from '../ast-index/client.js';
|
|
2
|
+
import type { ModuleInfoArgs } from '../core/validation.js';
|
|
3
|
+
export declare function handleModuleInfo(args: ModuleInfoArgs, projectRoot: string, astIndex: AstIndexClient): Promise<{
|
|
4
|
+
content: Array<{
|
|
5
|
+
type: 'text';
|
|
6
|
+
text: string;
|
|
7
|
+
}>;
|
|
8
|
+
}>;
|
|
9
|
+
//# sourceMappingURL=module-info.d.ts.map
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { relative } from 'node:path';
|
|
2
|
+
export async function handleModuleInfo(args, projectRoot, astIndex) {
|
|
3
|
+
// Degradation check
|
|
4
|
+
if (astIndex.isDisabled() || astIndex.isOversized()) {
|
|
5
|
+
return {
|
|
6
|
+
content: [{
|
|
7
|
+
type: 'text',
|
|
8
|
+
text: '⚠ ast-index unavailable — module_info requires ast-index.\n' +
|
|
9
|
+
'DEGRADED: Use find_usages() + related_files() as alternatives for dependency analysis.',
|
|
10
|
+
}],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
const check = args.check ?? 'all';
|
|
14
|
+
const sections = [];
|
|
15
|
+
// Resolve module
|
|
16
|
+
const moduleList = await astIndex.modules(args.module);
|
|
17
|
+
const moduleName = moduleList.length > 0 ? moduleList[0].name : args.module;
|
|
18
|
+
const modulePath = moduleList.length > 0 ? moduleList[0].path : args.module;
|
|
19
|
+
sections.push(`MODULE: ${moduleName} (${modulePath})`);
|
|
20
|
+
if (moduleList.length === 0) {
|
|
21
|
+
sections.push('');
|
|
22
|
+
sections.push(`⚠ Module "${args.module}" not found by ast-index.`);
|
|
23
|
+
sections.push('');
|
|
24
|
+
// List available modules as hint
|
|
25
|
+
const allModules = await astIndex.modules();
|
|
26
|
+
if (allModules.length > 0) {
|
|
27
|
+
sections.push(`Available modules (${allModules.length}):`);
|
|
28
|
+
for (const m of allModules.slice(0, 20)) {
|
|
29
|
+
sections.push(` ${m.name} (${m.path})`);
|
|
30
|
+
}
|
|
31
|
+
if (allModules.length > 20) {
|
|
32
|
+
sections.push(` ... and ${allModules.length - 20} more`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
sections.push('No modules detected. ast-index module analysis requires a modular project structure.');
|
|
37
|
+
sections.push('HINT: Use find_usages() for cross-file symbol references, related_files() for import graphs.');
|
|
38
|
+
}
|
|
39
|
+
return { content: [{ type: 'text', text: sections.join('\n') }] };
|
|
40
|
+
}
|
|
41
|
+
sections.push('');
|
|
42
|
+
// Run requested checks in parallel
|
|
43
|
+
const checks = check === 'all'
|
|
44
|
+
? ['deps', 'dependents', 'api', 'unused-deps']
|
|
45
|
+
: [check];
|
|
46
|
+
const results = await Promise.allSettled(checks.map(async (c) => {
|
|
47
|
+
switch (c) {
|
|
48
|
+
case 'deps': return { type: 'deps', data: await astIndex.moduleDeps(args.module) };
|
|
49
|
+
case 'dependents': return { type: 'dependents', data: await astIndex.moduleDependents(args.module) };
|
|
50
|
+
case 'api': return { type: 'api', data: await astIndex.moduleApi(args.module) };
|
|
51
|
+
case 'unused-deps': return { type: 'unused-deps', data: await astIndex.unusedDeps(args.module) };
|
|
52
|
+
}
|
|
53
|
+
}));
|
|
54
|
+
for (const r of results) {
|
|
55
|
+
if (r.status !== 'fulfilled')
|
|
56
|
+
continue;
|
|
57
|
+
const { type, data } = r.value;
|
|
58
|
+
switch (type) {
|
|
59
|
+
case 'deps': {
|
|
60
|
+
if (data.length > 0) {
|
|
61
|
+
sections.push(`DEPENDENCIES (${data.length}):`);
|
|
62
|
+
for (const d of data) {
|
|
63
|
+
const typeHint = d.type ? ` [${d.type}]` : '';
|
|
64
|
+
sections.push(` → ${d.name} (${d.path})${typeHint}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
sections.push('DEPENDENCIES: none detected');
|
|
69
|
+
}
|
|
70
|
+
sections.push('');
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case 'dependents': {
|
|
74
|
+
if (data.length > 0) {
|
|
75
|
+
sections.push(`DEPENDENTS (${data.length} modules depend on this):`);
|
|
76
|
+
for (const d of data) {
|
|
77
|
+
sections.push(` ← ${d.name} (${d.path})`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
sections.push('DEPENDENTS: none — this module is a leaf');
|
|
82
|
+
}
|
|
83
|
+
sections.push('');
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
case 'api': {
|
|
87
|
+
if (data.length > 0) {
|
|
88
|
+
sections.push(`PUBLIC API (${data.length} symbols):`);
|
|
89
|
+
for (const a of data) {
|
|
90
|
+
const loc = `${rel(projectRoot, a.file)}:${a.line}`;
|
|
91
|
+
const sig = a.signature ? ` — ${a.signature}` : '';
|
|
92
|
+
sections.push(` ${a.kind} ${a.name}${sig} (${loc})`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
sections.push('PUBLIC API: none detected');
|
|
97
|
+
}
|
|
98
|
+
sections.push('');
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
case 'unused-deps': {
|
|
102
|
+
if (data.length > 0) {
|
|
103
|
+
sections.push(`UNUSED DEPENDENCIES (${data.length}):`);
|
|
104
|
+
for (const d of data) {
|
|
105
|
+
const reason = d.reason ? ` — ${d.reason}` : ' — imported but no symbols used';
|
|
106
|
+
sections.push(` ⚠ ${d.name} (${d.path})${reason}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
sections.push('UNUSED DEPENDENCIES: none — all dependencies are used');
|
|
111
|
+
}
|
|
112
|
+
sections.push('');
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
sections.push('HINT: Use smart_read() on module files, find_usages() for cross-module references.');
|
|
118
|
+
return { content: [{ type: 'text', text: sections.join('\n') }] };
|
|
119
|
+
}
|
|
120
|
+
function rel(projectRoot, absPath) {
|
|
121
|
+
return relative(projectRoot, absPath) || absPath;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=module-info.js.map
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type { AstIndexClient } from '../ast-index/client.js';
|
|
2
|
-
|
|
3
|
-
path: string;
|
|
4
|
-
}
|
|
2
|
+
import type { OutlineArgs } from '../core/validation.js';
|
|
5
3
|
export declare function handleOutline(args: OutlineArgs, projectRoot: string, astIndex: AstIndexClient): Promise<{
|
|
6
4
|
content: Array<{
|
|
7
5
|
type: 'text';
|
package/dist/handlers/outline.js
CHANGED
|
@@ -22,7 +22,28 @@ export async function handleOutline(args, projectRoot, astIndex) {
|
|
|
22
22
|
}],
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
|
-
|
|
25
|
+
const recursive = args.recursive ?? false;
|
|
26
|
+
const maxDepth = args.max_depth ?? 2;
|
|
27
|
+
const sections = [];
|
|
28
|
+
await outlineDir(absPath, sections, 0, recursive ? maxDepth : 0, projectRoot, astIndex);
|
|
29
|
+
sections.push('HINT: Use outline(path) on subdirs, smart_read(path) for file structure, read_symbol(path, symbol) for source code.');
|
|
30
|
+
return { content: [{ type: 'text', text: sections.join('\n') }] };
|
|
31
|
+
}
|
|
32
|
+
/** Max output lines to prevent runaway recursive outlines */
|
|
33
|
+
const MAX_OUTLINE_LINES = 500;
|
|
34
|
+
/**
|
|
35
|
+
* Outline a single directory. When depth < maxDepth and recursive,
|
|
36
|
+
* recurse into subdirectories. Otherwise show file counts only.
|
|
37
|
+
*/
|
|
38
|
+
async function outlineDir(absPath, sections, depth, maxDepth, projectRoot, astIndex) {
|
|
39
|
+
// Guard: stop if output is already too large
|
|
40
|
+
if (sections.length >= MAX_OUTLINE_LINES) {
|
|
41
|
+
if (!sections[sections.length - 1]?.startsWith('⚠')) {
|
|
42
|
+
sections.push(`⚠ Output truncated at ${MAX_OUTLINE_LINES} lines. Use outline() on specific subdirectories for details.`);
|
|
43
|
+
}
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// List code files and subdirectories
|
|
26
47
|
const entries = await readdir(absPath, { withFileTypes: true });
|
|
27
48
|
const codeFiles = [];
|
|
28
49
|
const subdirs = [];
|
|
@@ -38,53 +59,63 @@ export async function handleOutline(args, projectRoot, astIndex) {
|
|
|
38
59
|
}
|
|
39
60
|
}
|
|
40
61
|
if (codeFiles.length === 0 && subdirs.length === 0) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}],
|
|
46
|
-
};
|
|
62
|
+
if (depth === 0) {
|
|
63
|
+
sections.push(`No code files or subdirectories found in "${relative(projectRoot, absPath) || '.'}".`);
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
47
66
|
}
|
|
48
67
|
// Sort
|
|
49
68
|
codeFiles.sort();
|
|
50
69
|
subdirs.sort();
|
|
51
70
|
const relDir = relative(projectRoot, absPath) || '.';
|
|
71
|
+
const indent = ' '.repeat(depth);
|
|
52
72
|
const totalLabel = codeFiles.length > 0 ? `${codeFiles.length} files` : '';
|
|
53
73
|
const subLabel = subdirs.length > 0 ? `${subdirs.length} subdirs` : '';
|
|
54
74
|
const countLabel = [totalLabel, subLabel].filter(Boolean).join(', ');
|
|
55
|
-
|
|
56
|
-
|
|
75
|
+
sections.push(`${indent}OUTLINE: ${relDir}/ (${countLabel})`);
|
|
76
|
+
sections.push('');
|
|
77
|
+
// Show subdirectories
|
|
57
78
|
if (subdirs.length > 0) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
79
|
+
if (depth < maxDepth) {
|
|
80
|
+
// Recursive: outline each subdir
|
|
81
|
+
for (const sub of subdirs) {
|
|
82
|
+
const subPath = resolve(absPath, sub);
|
|
83
|
+
await outlineDir(subPath, sections, depth + 1, maxDepth, projectRoot, astIndex);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
// Non-recursive: show file counts only
|
|
88
|
+
for (const sub of subdirs) {
|
|
89
|
+
const subPath = resolve(absPath, sub);
|
|
90
|
+
const fileCount = await countCodeFiles(subPath);
|
|
91
|
+
sections.push(`${indent} ${sub}/ (${fileCount} code files)`);
|
|
92
|
+
}
|
|
93
|
+
sections.push('');
|
|
62
94
|
}
|
|
63
|
-
sections.push('');
|
|
64
95
|
}
|
|
65
96
|
// Show code files at this level with AST outline
|
|
97
|
+
const fileIndent = ' '.repeat(depth + 1);
|
|
98
|
+
const symbolIndent = depth + 2;
|
|
66
99
|
for (const filePath of codeFiles) {
|
|
67
100
|
const name = basename(filePath);
|
|
68
101
|
try {
|
|
69
102
|
const structure = await astIndex.outline(filePath);
|
|
70
103
|
if (!structure) {
|
|
71
|
-
sections.push(`${name} (no AST)`);
|
|
104
|
+
sections.push(`${fileIndent}${name} (no AST)`);
|
|
72
105
|
sections.push('');
|
|
73
106
|
continue;
|
|
74
107
|
}
|
|
75
|
-
sections.push(`${name} (${structure.meta.lines} lines)`);
|
|
108
|
+
sections.push(`${fileIndent}${name} (${structure.meta.lines} lines)`);
|
|
76
109
|
for (const sym of structure.symbols) {
|
|
77
|
-
formatCompactSymbol(sym, sections,
|
|
110
|
+
formatCompactSymbol(sym, sections, symbolIndent);
|
|
78
111
|
}
|
|
79
112
|
sections.push('');
|
|
80
113
|
}
|
|
81
114
|
catch {
|
|
82
|
-
sections.push(`${name} (outline failed)`);
|
|
115
|
+
sections.push(`${fileIndent}${name} (outline failed)`);
|
|
83
116
|
sections.push('');
|
|
84
117
|
}
|
|
85
118
|
}
|
|
86
|
-
sections.push('HINT: Use outline(path) on subdirs, smart_read(path) for file structure, read_symbol(path, symbol) for source code.');
|
|
87
|
-
return { content: [{ type: 'text', text: sections.join('\n') }] };
|
|
88
119
|
}
|
|
89
120
|
/**
|
|
90
121
|
* Format a symbol in compact outline form.
|
|
@@ -1,5 +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
4
|
content: Array<{
|
|
4
5
|
type: 'text';
|
|
5
6
|
text: string;
|