@compilr-dev/agents-coding-ts 0.1.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 (54) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.d.ts +34 -0
  3. package/dist/index.js +66 -0
  4. package/dist/parser/index.d.ts +7 -0
  5. package/dist/parser/index.js +6 -0
  6. package/dist/parser/typescript-parser.d.ts +22 -0
  7. package/dist/parser/typescript-parser.js +423 -0
  8. package/dist/skills/code-health.d.ts +9 -0
  9. package/dist/skills/code-health.js +167 -0
  10. package/dist/skills/code-structure.d.ts +9 -0
  11. package/dist/skills/code-structure.js +97 -0
  12. package/dist/skills/dependency-audit.d.ts +9 -0
  13. package/dist/skills/dependency-audit.js +110 -0
  14. package/dist/skills/index.d.ts +16 -0
  15. package/dist/skills/index.js +27 -0
  16. package/dist/skills/refactor-impact.d.ts +9 -0
  17. package/dist/skills/refactor-impact.js +135 -0
  18. package/dist/skills/type-analysis.d.ts +9 -0
  19. package/dist/skills/type-analysis.js +150 -0
  20. package/dist/tools/find-dead-code.d.ts +20 -0
  21. package/dist/tools/find-dead-code.js +375 -0
  22. package/dist/tools/find-duplicates.d.ts +21 -0
  23. package/dist/tools/find-duplicates.js +274 -0
  24. package/dist/tools/find-implementations.d.ts +21 -0
  25. package/dist/tools/find-implementations.js +436 -0
  26. package/dist/tools/find-patterns.d.ts +21 -0
  27. package/dist/tools/find-patterns.js +457 -0
  28. package/dist/tools/find-references.d.ts +23 -0
  29. package/dist/tools/find-references.js +488 -0
  30. package/dist/tools/find-symbol.d.ts +21 -0
  31. package/dist/tools/find-symbol.js +458 -0
  32. package/dist/tools/get-call-graph.d.ts +23 -0
  33. package/dist/tools/get-call-graph.js +469 -0
  34. package/dist/tools/get-complexity.d.ts +21 -0
  35. package/dist/tools/get-complexity.js +394 -0
  36. package/dist/tools/get-dependency-graph.d.ts +23 -0
  37. package/dist/tools/get-dependency-graph.js +482 -0
  38. package/dist/tools/get-documentation.d.ts +21 -0
  39. package/dist/tools/get-documentation.js +613 -0
  40. package/dist/tools/get-exports.d.ts +21 -0
  41. package/dist/tools/get-exports.js +427 -0
  42. package/dist/tools/get-file-structure.d.ts +27 -0
  43. package/dist/tools/get-file-structure.js +120 -0
  44. package/dist/tools/get-imports.d.ts +23 -0
  45. package/dist/tools/get-imports.js +350 -0
  46. package/dist/tools/get-signature.d.ts +20 -0
  47. package/dist/tools/get-signature.js +758 -0
  48. package/dist/tools/get-type-hierarchy.d.ts +22 -0
  49. package/dist/tools/get-type-hierarchy.js +485 -0
  50. package/dist/tools/index.d.ts +23 -0
  51. package/dist/tools/index.js +25 -0
  52. package/dist/tools/types.d.ts +1302 -0
  53. package/dist/tools/types.js +7 -0
  54. package/package.json +84 -0
@@ -0,0 +1,613 @@
1
+ /**
2
+ * getDocumentation Tool
3
+ *
4
+ * Extract JSDoc/TSDoc documentation from source files.
5
+ * Provides documentation coverage metrics and identifies undocumented exports.
6
+ */
7
+ import * as fs from 'node:fs/promises';
8
+ import * as path from 'node:path';
9
+ import * as ts from 'typescript';
10
+ import { defineTool, createSuccessResult, createErrorResult } from '@compilr-dev/agents';
11
+ import { detectLanguage, isLanguageSupported } from '../parser/typescript-parser.js';
12
+ // Tool description
13
+ const TOOL_DESCRIPTION = `Extract JSDoc/TSDoc documentation from source files.
14
+ Returns documented symbols with their signatures and documentation content.
15
+ Calculates documentation coverage metrics and identifies undocumented exports.
16
+ Useful for generating API documentation and measuring doc coverage.`;
17
+ // Tool input schema
18
+ const TOOL_INPUT_SCHEMA = {
19
+ type: 'object',
20
+ properties: {
21
+ path: {
22
+ type: 'string',
23
+ description: 'File or directory to analyze',
24
+ },
25
+ recursive: {
26
+ type: 'boolean',
27
+ description: 'Recursive analysis for directories (default: false)',
28
+ default: false,
29
+ },
30
+ kinds: {
31
+ type: 'array',
32
+ items: { type: 'string' },
33
+ description: 'Filter by symbol kinds: function, class, interface, type, enum, variable, method, property',
34
+ },
35
+ exportedOnly: {
36
+ type: 'boolean',
37
+ description: 'Only include exported symbols (default: false)',
38
+ default: false,
39
+ },
40
+ documentedOnly: {
41
+ type: 'boolean',
42
+ description: 'Only include symbols with documentation (default: true)',
43
+ default: true,
44
+ },
45
+ maxFiles: {
46
+ type: 'number',
47
+ description: 'Maximum files to analyze (default: 50)',
48
+ default: 50,
49
+ },
50
+ includePrivate: {
51
+ type: 'boolean',
52
+ description: 'Include private class members (default: false)',
53
+ default: false,
54
+ },
55
+ },
56
+ required: ['path'],
57
+ };
58
+ // Default exclusions
59
+ const DEFAULT_EXCLUDE = ['node_modules', 'dist', 'build', '.git', 'coverage'];
60
+ /**
61
+ * getDocumentation tool
62
+ */
63
+ export const getDocumentationTool = defineTool({
64
+ name: 'get_documentation',
65
+ description: TOOL_DESCRIPTION,
66
+ inputSchema: TOOL_INPUT_SCHEMA,
67
+ execute: executeGetDocumentation,
68
+ });
69
+ /**
70
+ * Execute the getDocumentation tool
71
+ */
72
+ async function executeGetDocumentation(input) {
73
+ const { path: inputPath, recursive = false, kinds, exportedOnly = false, documentedOnly = true, maxFiles = 50, includePrivate = false, } = input;
74
+ try {
75
+ const resolvedPath = path.resolve(inputPath);
76
+ // Check if path exists
77
+ try {
78
+ await fs.access(resolvedPath);
79
+ }
80
+ catch {
81
+ return createErrorResult(`Path not found: ${resolvedPath}`);
82
+ }
83
+ const stats = await fs.stat(resolvedPath);
84
+ const files = [];
85
+ if (stats.isDirectory()) {
86
+ await collectFiles(resolvedPath, files, recursive ? 10 : 0, maxFiles);
87
+ }
88
+ else if (stats.isFile()) {
89
+ files.push(resolvedPath);
90
+ }
91
+ // Analyze each file
92
+ const fileResults = [];
93
+ let totalSymbols = 0;
94
+ let documentedSymbols = 0;
95
+ let undocumentedSymbols = 0;
96
+ const undocumentedExports = [];
97
+ for (const file of files) {
98
+ const fileResult = await analyzeFile(file, {
99
+ kinds,
100
+ exportedOnly,
101
+ documentedOnly,
102
+ includePrivate,
103
+ });
104
+ if (fileResult) {
105
+ fileResults.push(fileResult);
106
+ // Update totals
107
+ totalSymbols += fileResult.stats.totalSymbols;
108
+ documentedSymbols += fileResult.stats.documentedSymbols;
109
+ undocumentedSymbols += fileResult.stats.undocumentedSymbols;
110
+ // Track undocumented exports
111
+ for (const symbol of fileResult.symbols) {
112
+ if (symbol.exported && !hasDocumentation(symbol.documentation)) {
113
+ undocumentedExports.push({
114
+ name: symbol.name,
115
+ kind: symbol.kind,
116
+ path: file,
117
+ line: symbol.line,
118
+ });
119
+ }
120
+ }
121
+ }
122
+ }
123
+ const coveragePercent = totalSymbols > 0 ? Math.round((documentedSymbols / totalSymbols) * 100) : 100;
124
+ const result = {
125
+ path: resolvedPath,
126
+ files: fileResults,
127
+ summary: {
128
+ totalFiles: fileResults.length,
129
+ totalSymbols,
130
+ documentedSymbols,
131
+ undocumentedSymbols,
132
+ coveragePercent,
133
+ },
134
+ undocumentedExports: undocumentedExports.length > 0 ? undocumentedExports : undefined,
135
+ };
136
+ return createSuccessResult(result);
137
+ }
138
+ catch (error) {
139
+ return createErrorResult(`Failed to extract documentation: ${error instanceof Error ? error.message : String(error)}`);
140
+ }
141
+ }
142
+ /**
143
+ * Check if documentation has meaningful content
144
+ */
145
+ function hasDocumentation(doc) {
146
+ return !!(doc.summary ||
147
+ doc.description ||
148
+ doc.params ||
149
+ doc.returns ||
150
+ doc.examples?.length ||
151
+ doc.throws?.length);
152
+ }
153
+ /**
154
+ * Collect files to analyze
155
+ */
156
+ async function collectFiles(dirPath, files, maxDepth, maxFiles, currentDepth = 0) {
157
+ if (currentDepth > maxDepth || files.length >= maxFiles)
158
+ return;
159
+ try {
160
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
161
+ for (const entry of entries) {
162
+ if (files.length >= maxFiles)
163
+ break;
164
+ const fullPath = path.join(dirPath, entry.name);
165
+ // Skip excluded directories
166
+ if (entry.isDirectory()) {
167
+ if (DEFAULT_EXCLUDE.includes(entry.name))
168
+ continue;
169
+ await collectFiles(fullPath, files, maxDepth, maxFiles, currentDepth + 1);
170
+ }
171
+ else if (entry.isFile()) {
172
+ // Only include TypeScript/JavaScript files
173
+ if (/\.(ts|tsx|js|jsx)$/.test(entry.name) && !entry.name.endsWith('.d.ts')) {
174
+ files.push(fullPath);
175
+ }
176
+ }
177
+ }
178
+ }
179
+ catch {
180
+ // Ignore permission errors
181
+ }
182
+ }
183
+ /**
184
+ * Analyze documentation of a single file
185
+ */
186
+ async function analyzeFile(filePath, options) {
187
+ try {
188
+ const content = await fs.readFile(filePath, 'utf-8');
189
+ const detection = detectLanguage(filePath);
190
+ if (!detection.language || !isLanguageSupported(detection.language)) {
191
+ return null;
192
+ }
193
+ const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true, filePath.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
194
+ // Extract file-level documentation
195
+ const fileDoc = extractFileDocumentation(sourceFile);
196
+ // Extract symbol documentation
197
+ const allSymbols = [];
198
+ extractSymbols(sourceFile, sourceFile, allSymbols, options);
199
+ // Apply filters
200
+ let symbols = allSymbols;
201
+ if (options.kinds && options.kinds.length > 0) {
202
+ const kinds = options.kinds;
203
+ symbols = symbols.filter((s) => kinds.includes(s.kind));
204
+ }
205
+ if (options.exportedOnly) {
206
+ symbols = symbols.filter((s) => s.exported);
207
+ }
208
+ if (options.documentedOnly) {
209
+ symbols = symbols.filter((s) => hasDocumentation(s.documentation));
210
+ }
211
+ // Calculate stats based on all symbols (before documentedOnly filter)
212
+ const totalSymbols = allSymbols.length;
213
+ const documentedCount = allSymbols.filter((s) => hasDocumentation(s.documentation)).length;
214
+ const undocumentedCount = totalSymbols - documentedCount;
215
+ const coveragePercent = totalSymbols > 0 ? Math.round((documentedCount / totalSymbols) * 100) : 100;
216
+ return {
217
+ path: filePath,
218
+ fileDoc: fileDoc && hasDocumentation(fileDoc) ? fileDoc : undefined,
219
+ symbols,
220
+ stats: {
221
+ totalSymbols,
222
+ documentedSymbols: documentedCount,
223
+ undocumentedSymbols: undocumentedCount,
224
+ coveragePercent,
225
+ },
226
+ };
227
+ }
228
+ catch {
229
+ return null;
230
+ }
231
+ }
232
+ /**
233
+ * Extract file-level documentation (module doc comment at top of file)
234
+ */
235
+ function extractFileDocumentation(sourceFile) {
236
+ // Look for JSDoc at the very beginning of the file
237
+ const text = sourceFile.getFullText();
238
+ const match = text.match(/^\s*\/\*\*([\s\S]*?)\*\//);
239
+ if (!match)
240
+ return undefined;
241
+ const commentText = match[1];
242
+ const lines = commentText.split('\n').map((l) => l.replace(/^\s*\*\s?/, '').trim());
243
+ // Filter out @module and similar file-level tags, and empty lines
244
+ const descLines = [];
245
+ let isFileLevel = false;
246
+ for (const line of lines) {
247
+ if (line.startsWith('@module') ||
248
+ line.startsWith('@fileoverview') ||
249
+ line.startsWith('@file')) {
250
+ isFileLevel = true;
251
+ }
252
+ else if (!line.startsWith('@') && line.length > 0) {
253
+ descLines.push(line);
254
+ }
255
+ }
256
+ // Only return if this looks like a file-level comment
257
+ if (!isFileLevel && descLines.length === 0)
258
+ return undefined;
259
+ const summary = descLines[0] || '';
260
+ const description = descLines.length > 1 ? descLines.slice(1).join('\n').trim() : undefined;
261
+ return {
262
+ summary,
263
+ description,
264
+ };
265
+ }
266
+ /**
267
+ * Extract documented symbols from a node
268
+ */
269
+ function extractSymbols(node, sourceFile, symbols, options, _containerName) {
270
+ // Function declarations
271
+ if (ts.isFunctionDeclaration(node) && node.name) {
272
+ symbols.push(extractSymbolInfo(node, sourceFile, 'function', node.name.text));
273
+ }
274
+ // Variable declarations (arrow functions, const values)
275
+ else if (ts.isVariableStatement(node)) {
276
+ for (const decl of node.declarationList.declarations) {
277
+ if (ts.isIdentifier(decl.name)) {
278
+ const kind = decl.initializer &&
279
+ (ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer))
280
+ ? 'function'
281
+ : 'variable';
282
+ symbols.push(extractSymbolInfo(node, sourceFile, kind, decl.name.text));
283
+ }
284
+ }
285
+ }
286
+ // Class declarations
287
+ else if (ts.isClassDeclaration(node) && node.name) {
288
+ symbols.push(extractSymbolInfo(node, sourceFile, 'class', node.name.text));
289
+ // Extract class members
290
+ for (const member of node.members) {
291
+ // Skip private members if not included
292
+ if (!options.includePrivate && isPrivateMember(member))
293
+ continue;
294
+ if (ts.isMethodDeclaration(member) && ts.isIdentifier(member.name)) {
295
+ symbols.push(extractSymbolInfo(member, sourceFile, 'method', member.name.text, node.name.text));
296
+ }
297
+ else if (ts.isPropertyDeclaration(member) && ts.isIdentifier(member.name)) {
298
+ symbols.push(extractSymbolInfo(member, sourceFile, 'property', member.name.text, node.name.text));
299
+ }
300
+ }
301
+ }
302
+ // Interface declarations
303
+ else if (ts.isInterfaceDeclaration(node)) {
304
+ symbols.push(extractSymbolInfo(node, sourceFile, 'interface', node.name.text));
305
+ // Extract interface members
306
+ for (const member of node.members) {
307
+ if (ts.isPropertySignature(member) && ts.isIdentifier(member.name)) {
308
+ symbols.push(extractSymbolInfo(member, sourceFile, 'property', member.name.text, node.name.text));
309
+ }
310
+ else if (ts.isMethodSignature(member) && ts.isIdentifier(member.name)) {
311
+ symbols.push(extractSymbolInfo(member, sourceFile, 'method', member.name.text, node.name.text));
312
+ }
313
+ }
314
+ }
315
+ // Type alias declarations
316
+ else if (ts.isTypeAliasDeclaration(node)) {
317
+ symbols.push(extractSymbolInfo(node, sourceFile, 'type', node.name.text));
318
+ }
319
+ // Enum declarations
320
+ else if (ts.isEnumDeclaration(node)) {
321
+ symbols.push(extractSymbolInfo(node, sourceFile, 'enum', node.name.text));
322
+ }
323
+ // Continue traversing (but skip into class/interface bodies as they're handled above)
324
+ if (!ts.isClassDeclaration(node) && !ts.isInterfaceDeclaration(node)) {
325
+ ts.forEachChild(node, (child) => {
326
+ extractSymbols(child, sourceFile, symbols, options);
327
+ });
328
+ }
329
+ }
330
+ /**
331
+ * Check if a class member is private
332
+ */
333
+ function isPrivateMember(node) {
334
+ if (!ts.canHaveModifiers(node))
335
+ return false;
336
+ const modifiers = ts.getModifiers(node);
337
+ return modifiers?.some((m) => m.kind === ts.SyntaxKind.PrivateKeyword) ?? false;
338
+ }
339
+ /**
340
+ * Extract symbol information
341
+ */
342
+ function extractSymbolInfo(node, sourceFile, kind, name, containerName) {
343
+ const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
344
+ const exported = isExported(node);
345
+ const documentation = extractDocumentation(node, sourceFile);
346
+ const signature = extractSignature(node, sourceFile, kind);
347
+ return {
348
+ name,
349
+ kind,
350
+ line: line + 1,
351
+ exported,
352
+ signature,
353
+ documentation,
354
+ container: containerName,
355
+ };
356
+ }
357
+ /**
358
+ * Check if node is exported
359
+ */
360
+ function isExported(node) {
361
+ // For variable declarations, check the parent VariableStatement
362
+ if (ts.isVariableDeclaration(node)) {
363
+ const varDeclList = node.parent;
364
+ const varStatement = varDeclList.parent;
365
+ if (ts.canHaveModifiers(varStatement)) {
366
+ const modifiers = ts.getModifiers(varStatement);
367
+ return modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
368
+ }
369
+ return false;
370
+ }
371
+ // For property/method in class/interface - inherit from parent
372
+ if (ts.isPropertyDeclaration(node) ||
373
+ ts.isMethodDeclaration(node) ||
374
+ ts.isPropertySignature(node) ||
375
+ ts.isMethodSignature(node)) {
376
+ const parent = node.parent;
377
+ if (ts.isClassDeclaration(parent) || ts.isInterfaceDeclaration(parent)) {
378
+ return isExported(parent);
379
+ }
380
+ return false;
381
+ }
382
+ if (!ts.canHaveModifiers(node))
383
+ return false;
384
+ const modifiers = ts.getModifiers(node);
385
+ return modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
386
+ }
387
+ /**
388
+ * Extract signature from node
389
+ */
390
+ function extractSignature(node, sourceFile, kind) {
391
+ switch (kind) {
392
+ case 'function': {
393
+ if (ts.isFunctionDeclaration(node) && node.name) {
394
+ const params = node.parameters.map((p) => p.getText(sourceFile)).join(', ');
395
+ const returnType = node.type ? `: ${node.type.getText(sourceFile)}` : '';
396
+ const generics = node.typeParameters
397
+ ? `<${node.typeParameters.map((t) => t.getText(sourceFile)).join(', ')}>`
398
+ : '';
399
+ return `function ${node.name.text}${generics}(${params})${returnType}`;
400
+ }
401
+ if (ts.isVariableStatement(node)) {
402
+ const decl = node.declarationList.declarations[0];
403
+ if (ts.isIdentifier(decl.name)) {
404
+ if (decl.initializer &&
405
+ (ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer))) {
406
+ const func = decl.initializer;
407
+ const params = func.parameters.map((p) => p.getText(sourceFile)).join(', ');
408
+ const returnType = func.type ? `: ${func.type.getText(sourceFile)}` : '';
409
+ return `const ${decl.name.text} = (${params})${returnType} => ...`;
410
+ }
411
+ }
412
+ }
413
+ return node.getText(sourceFile).split('\n')[0];
414
+ }
415
+ case 'class': {
416
+ if (ts.isClassDeclaration(node) && node.name) {
417
+ const generics = node.typeParameters
418
+ ? `<${node.typeParameters.map((t) => t.getText(sourceFile)).join(', ')}>`
419
+ : '';
420
+ let heritage = '';
421
+ if (node.heritageClauses) {
422
+ for (const clause of node.heritageClauses) {
423
+ const tokenKind = clause.token;
424
+ if (tokenKind === ts.SyntaxKind.ExtendsKeyword) {
425
+ heritage += ` extends ${clause.types.map((t) => t.getText(sourceFile)).join(', ')}`;
426
+ }
427
+ else if (tokenKind === ts.SyntaxKind.ImplementsKeyword) {
428
+ heritage += ` implements ${clause.types.map((t) => t.getText(sourceFile)).join(', ')}`;
429
+ }
430
+ }
431
+ }
432
+ return `class ${node.name.text}${generics}${heritage}`;
433
+ }
434
+ return node.getText(sourceFile).split('\n')[0];
435
+ }
436
+ case 'interface': {
437
+ if (ts.isInterfaceDeclaration(node)) {
438
+ const generics = node.typeParameters
439
+ ? `<${node.typeParameters.map((t) => t.getText(sourceFile)).join(', ')}>`
440
+ : '';
441
+ let heritage = '';
442
+ if (node.heritageClauses) {
443
+ for (const clause of node.heritageClauses) {
444
+ heritage += ` extends ${clause.types.map((t) => t.getText(sourceFile)).join(', ')}`;
445
+ }
446
+ }
447
+ return `interface ${node.name.text}${generics}${heritage}`;
448
+ }
449
+ return node.getText(sourceFile).split('\n')[0];
450
+ }
451
+ case 'type': {
452
+ if (ts.isTypeAliasDeclaration(node)) {
453
+ const generics = node.typeParameters
454
+ ? `<${node.typeParameters.map((t) => t.getText(sourceFile)).join(', ')}>`
455
+ : '';
456
+ const typeText = node.type.getText(sourceFile);
457
+ // Truncate long types
458
+ const maxLen = 80;
459
+ const truncatedType = typeText.length > maxLen ? typeText.slice(0, maxLen) + '...' : typeText;
460
+ return `type ${node.name.text}${generics} = ${truncatedType}`;
461
+ }
462
+ return node.getText(sourceFile).split('\n')[0];
463
+ }
464
+ case 'enum': {
465
+ if (ts.isEnumDeclaration(node)) {
466
+ const members = node.members.map((m) => m.name.getText(sourceFile)).slice(0, 5);
467
+ const suffix = node.members.length > 5 ? ', ...' : '';
468
+ return `enum ${node.name.text} { ${members.join(', ')}${suffix} }`;
469
+ }
470
+ return node.getText(sourceFile).split('\n')[0];
471
+ }
472
+ case 'variable': {
473
+ if (ts.isVariableStatement(node)) {
474
+ const decl = node.declarationList.declarations[0];
475
+ if (ts.isIdentifier(decl.name)) {
476
+ const typeText = decl.type ? `: ${decl.type.getText(sourceFile)}` : '';
477
+ return `const ${decl.name.text}${typeText}`;
478
+ }
479
+ }
480
+ return node.getText(sourceFile).split('\n')[0];
481
+ }
482
+ case 'method': {
483
+ if (ts.isMethodDeclaration(node) && ts.isIdentifier(node.name)) {
484
+ const params = node.parameters.map((p) => p.getText(sourceFile)).join(', ');
485
+ const returnType = node.type ? `: ${node.type.getText(sourceFile)}` : '';
486
+ return `${node.name.text}(${params})${returnType}`;
487
+ }
488
+ if (ts.isMethodSignature(node) && ts.isIdentifier(node.name)) {
489
+ const params = node.parameters.map((p) => p.getText(sourceFile)).join(', ');
490
+ const returnType = node.type ? `: ${node.type.getText(sourceFile)}` : '';
491
+ return `${node.name.text}(${params})${returnType}`;
492
+ }
493
+ return node.getText(sourceFile).split('\n')[0];
494
+ }
495
+ case 'property': {
496
+ if (ts.isPropertyDeclaration(node) && ts.isIdentifier(node.name)) {
497
+ const typeText = node.type ? `: ${node.type.getText(sourceFile)}` : '';
498
+ const optional = node.questionToken ? '?' : '';
499
+ return `${node.name.text}${optional}${typeText}`;
500
+ }
501
+ if (ts.isPropertySignature(node) && ts.isIdentifier(node.name)) {
502
+ const typeText = node.type ? `: ${node.type.getText(sourceFile)}` : '';
503
+ const optional = node.questionToken ? '?' : '';
504
+ return `${node.name.text}${optional}${typeText}`;
505
+ }
506
+ return node.getText(sourceFile).split('\n')[0];
507
+ }
508
+ default:
509
+ return node.getText(sourceFile).split('\n')[0];
510
+ }
511
+ }
512
+ /**
513
+ * Extract JSDoc documentation from a node
514
+ */
515
+ function extractDocumentation(node, sourceFile) {
516
+ const jsDocTags = ts.getJSDocTags(node);
517
+ const jsDocComments = node.jsDoc;
518
+ const doc = {
519
+ summary: '',
520
+ };
521
+ // Get comment text
522
+ if (jsDocComments?.length) {
523
+ const firstDoc = jsDocComments[0];
524
+ if (typeof firstDoc.comment === 'string') {
525
+ const lines = firstDoc.comment.split('\n');
526
+ doc.summary = lines[0].trim();
527
+ if (lines.length > 1) {
528
+ doc.description = lines.slice(1).join('\n').trim();
529
+ }
530
+ }
531
+ else if (firstDoc.comment && typeof firstDoc.comment !== 'string') {
532
+ // JSDocComment can be an array of JSDocText | JSDocLink
533
+ const commentParts = firstDoc.comment;
534
+ const text = commentParts
535
+ .map((c) => (c.kind === ts.SyntaxKind.JSDocText ? c.text : ''))
536
+ .join('')
537
+ .trim();
538
+ const lines = text.split('\n');
539
+ doc.summary = lines[0].trim();
540
+ if (lines.length > 1) {
541
+ doc.description = lines.slice(1).join('\n').trim();
542
+ }
543
+ }
544
+ }
545
+ // Process tags
546
+ const params = {};
547
+ const throws = [];
548
+ const examples = [];
549
+ const see = [];
550
+ for (const tag of jsDocTags) {
551
+ const tagName = tag.tagName.text;
552
+ let tagComment = '';
553
+ if (typeof tag.comment === 'string') {
554
+ tagComment = tag.comment;
555
+ }
556
+ else if (tag.comment && typeof tag.comment !== 'string') {
557
+ const commentParts = tag.comment;
558
+ tagComment = commentParts
559
+ .map((c) => (c.kind === ts.SyntaxKind.JSDocText ? c.text : ''))
560
+ .join('')
561
+ .trim();
562
+ }
563
+ if (tagName === 'param' && ts.isJSDocParameterTag(tag)) {
564
+ const paramName = tag.name.getText(sourceFile);
565
+ params[paramName] = tagComment;
566
+ }
567
+ else if (tagName === 'returns' || tagName === 'return') {
568
+ doc.returns = tagComment;
569
+ }
570
+ else if (tagName === 'throws' || tagName === 'exception') {
571
+ throws.push(tagComment);
572
+ }
573
+ else if (tagName === 'example') {
574
+ examples.push(tagComment);
575
+ }
576
+ else if (tagName === 'see') {
577
+ see.push(tagComment);
578
+ }
579
+ else if (ts.isJSDocDeprecatedTag(tag)) {
580
+ doc.deprecatedMessage = tagComment || 'This API is deprecated';
581
+ }
582
+ else if (tagName === 'since') {
583
+ doc.since = tagComment;
584
+ }
585
+ }
586
+ if (Object.keys(params).length > 0)
587
+ doc.params = params;
588
+ if (throws.length > 0)
589
+ doc.throws = throws;
590
+ if (examples.length > 0)
591
+ doc.examples = examples;
592
+ if (see.length > 0)
593
+ doc.see = see;
594
+ return doc;
595
+ }
596
+ /**
597
+ * Create customizable getDocumentation tool
598
+ */
599
+ export function createGetDocumentationTool(options) {
600
+ return defineTool({
601
+ name: options?.name ?? 'get_documentation',
602
+ description: options?.description ?? TOOL_DESCRIPTION,
603
+ inputSchema: TOOL_INPUT_SCHEMA,
604
+ execute: async (input) => {
605
+ const modifiedInput = {
606
+ ...input,
607
+ maxFiles: input.maxFiles ?? options?.defaultMaxFiles,
608
+ documentedOnly: input.documentedOnly ?? options?.defaultDocumentedOnly,
609
+ };
610
+ return executeGetDocumentation(modifiedInput);
611
+ },
612
+ });
613
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * getExports Tool
3
+ *
4
+ * Get all exports from a module.
5
+ * Uses TypeScript Compiler API for accurate AST analysis.
6
+ */
7
+ import type { Tool } from '@compilr-dev/agents';
8
+ import type { GetExportsInput } from './types.js';
9
+ /**
10
+ * getExports tool - Get all exports from a module
11
+ */
12
+ export declare const getExportsTool: Tool<GetExportsInput>;
13
+ /**
14
+ * Factory function to create a customized getExports tool
15
+ */
16
+ export declare function createGetExportsTool(options?: {
17
+ /** Default includeReExports */
18
+ defaultIncludeReExports?: boolean;
19
+ /** Default resolveReExports */
20
+ defaultResolveReExports?: boolean;
21
+ }): Tool<GetExportsInput>;