@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,350 @@
1
+ /**
2
+ * getImports Tool
3
+ *
4
+ * Get detailed import information for a file or module.
5
+ * Uses TypeScript Compiler API for accurate AST analysis.
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
+ // Supported file extensions for searching
13
+ const SUPPORTED_EXTENSIONS = new Set([
14
+ '.ts',
15
+ '.tsx',
16
+ '.js',
17
+ '.jsx',
18
+ '.mts',
19
+ '.mjs',
20
+ '.cts',
21
+ '.cjs',
22
+ ]);
23
+ // Tool description
24
+ const TOOL_DESCRIPTION = `Get detailed import information for a file or module.
25
+ Returns structured JSON with import sources, symbols, and whether imports are external or internal.
26
+ Use this to understand a file's dependencies and what it imports from other modules.`;
27
+ // Tool input schema
28
+ const TOOL_INPUT_SCHEMA = {
29
+ type: 'object',
30
+ properties: {
31
+ path: {
32
+ type: 'string',
33
+ description: 'File or directory to analyze',
34
+ },
35
+ recursive: {
36
+ type: 'boolean',
37
+ description: 'Recursively analyze directory (default: false)',
38
+ default: false,
39
+ },
40
+ filterSource: {
41
+ type: 'string',
42
+ description: 'Filter to specific import sources (partial match)',
43
+ },
44
+ transitive: {
45
+ type: 'boolean',
46
+ description: 'Include transitive imports (default: false)',
47
+ default: false,
48
+ },
49
+ maxDepth: {
50
+ type: 'number',
51
+ description: 'Max depth for transitive imports (default: 3)',
52
+ default: 3,
53
+ },
54
+ },
55
+ required: ['path'],
56
+ };
57
+ /**
58
+ * getImports tool - Get detailed import information
59
+ */
60
+ export const getImportsTool = defineTool({
61
+ name: 'get_imports',
62
+ description: TOOL_DESCRIPTION,
63
+ inputSchema: TOOL_INPUT_SCHEMA,
64
+ execute: executeGetImports,
65
+ });
66
+ /**
67
+ * Execute the getImports tool
68
+ */
69
+ async function executeGetImports(input) {
70
+ const { path: inputPath, recursive = false, filterSource, transitive = false, maxDepth = 3, } = input;
71
+ // Validate input
72
+ if (!inputPath || inputPath.trim().length === 0) {
73
+ return createErrorResult('Path is required');
74
+ }
75
+ try {
76
+ // Check if path exists
77
+ try {
78
+ await fs.access(inputPath);
79
+ }
80
+ catch {
81
+ return createErrorResult(`Path not found: ${inputPath}`);
82
+ }
83
+ const stats = await fs.stat(inputPath);
84
+ let allImports = [];
85
+ const analyzedPath = inputPath;
86
+ if (stats.isFile()) {
87
+ // Analyze single file
88
+ const detection = detectLanguage(inputPath);
89
+ if (!detection.language || !isLanguageSupported(detection.language)) {
90
+ return createErrorResult(`Unsupported file type: ${path.extname(inputPath)}`);
91
+ }
92
+ allImports = await analyzeFileImports(inputPath);
93
+ }
94
+ else if (stats.isDirectory()) {
95
+ // Analyze directory
96
+ const files = await collectFiles(inputPath, recursive);
97
+ for (const filePath of files) {
98
+ const fileImports = await analyzeFileImports(filePath);
99
+ allImports.push(...fileImports);
100
+ }
101
+ }
102
+ else {
103
+ return createErrorResult(`Path is neither a file nor directory: ${inputPath}`);
104
+ }
105
+ // Apply filter if specified
106
+ if (filterSource) {
107
+ allImports = allImports.filter((imp) => imp.source.includes(filterSource));
108
+ }
109
+ // Handle transitive imports
110
+ let transitiveImports;
111
+ if (transitive && stats.isFile()) {
112
+ transitiveImports = await getTransitiveImports(inputPath, allImports, maxDepth);
113
+ }
114
+ // Calculate statistics
115
+ const importStats = {
116
+ totalImports: allImports.length,
117
+ externalPackages: allImports.filter((imp) => imp.isExternal).length,
118
+ internalModules: allImports.filter((imp) => !imp.isExternal).length,
119
+ typeOnlyImports: allImports.filter((imp) => imp.typeOnly).length,
120
+ };
121
+ const result = {
122
+ path: analyzedPath,
123
+ imports: allImports,
124
+ transitiveImports,
125
+ stats: importStats,
126
+ };
127
+ return createSuccessResult(result);
128
+ }
129
+ catch (error) {
130
+ const message = error instanceof Error ? error.message : String(error);
131
+ return createErrorResult(`Failed to analyze imports: ${message}`);
132
+ }
133
+ }
134
+ /**
135
+ * Collect all supported files in a directory
136
+ */
137
+ async function collectFiles(dir, recursive) {
138
+ const files = [];
139
+ async function walk(currentDir) {
140
+ const entries = await fs.readdir(currentDir, { withFileTypes: true });
141
+ for (const entry of entries) {
142
+ const fullPath = path.join(currentDir, entry.name);
143
+ if (entry.isDirectory()) {
144
+ if (!recursive)
145
+ continue;
146
+ // Skip node_modules and common non-source directories
147
+ if (entry.name === 'node_modules' ||
148
+ entry.name.startsWith('.') ||
149
+ entry.name === 'dist' ||
150
+ entry.name === 'build' ||
151
+ entry.name === 'coverage') {
152
+ continue;
153
+ }
154
+ await walk(fullPath);
155
+ }
156
+ else if (entry.isFile()) {
157
+ const ext = path.extname(entry.name).toLowerCase();
158
+ if (SUPPORTED_EXTENSIONS.has(ext)) {
159
+ files.push(fullPath);
160
+ }
161
+ }
162
+ }
163
+ }
164
+ await walk(dir);
165
+ return files;
166
+ }
167
+ /**
168
+ * Analyze imports in a single file
169
+ */
170
+ async function analyzeFileImports(filePath) {
171
+ const imports = [];
172
+ try {
173
+ const sourceCode = await fs.readFile(filePath, 'utf-8');
174
+ const scriptKind = getScriptKind(filePath);
175
+ const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest, true, scriptKind);
176
+ const fileDir = path.dirname(filePath);
177
+ ts.forEachChild(sourceFile, (node) => {
178
+ if (ts.isImportDeclaration(node)) {
179
+ const imp = extractImportInfo(node, sourceFile, fileDir);
180
+ if (imp) {
181
+ imports.push(imp);
182
+ }
183
+ }
184
+ });
185
+ }
186
+ catch {
187
+ // Silently skip files that can't be parsed
188
+ }
189
+ return imports;
190
+ }
191
+ /**
192
+ * Extract import information from an import declaration
193
+ */
194
+ function extractImportInfo(node, sourceFile, fileDir) {
195
+ const moduleSpecifier = node.moduleSpecifier;
196
+ if (!ts.isStringLiteral(moduleSpecifier))
197
+ return null;
198
+ const source = moduleSpecifier.text;
199
+ const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
200
+ // Check for type-only import (import type { ... } from '...')
201
+ // phaseModifier is directly the SyntaxKind value (TypeKeyword or DeferKeyword), not a node
202
+ const typeOnly = node.importClause?.phaseModifier === ts.SyntaxKind.TypeKeyword;
203
+ // Determine if external (doesn't start with . or /)
204
+ const isExternal = !source.startsWith('.') && !source.startsWith('/');
205
+ // Try to resolve local imports
206
+ let resolvedPath;
207
+ if (!isExternal) {
208
+ resolvedPath = resolveLocalImport(source, fileDir);
209
+ }
210
+ // Extract imported symbols
211
+ const symbols = [];
212
+ const importClause = node.importClause;
213
+ if (importClause) {
214
+ // Default import
215
+ if (importClause.name) {
216
+ symbols.push({
217
+ name: importClause.name.text,
218
+ isDefault: true,
219
+ isNamespace: false,
220
+ });
221
+ }
222
+ // Named imports or namespace import
223
+ if (importClause.namedBindings) {
224
+ if (ts.isNamedImports(importClause.namedBindings)) {
225
+ for (const element of importClause.namedBindings.elements) {
226
+ symbols.push({
227
+ name: element.name.text,
228
+ originalName: element.propertyName?.text,
229
+ isDefault: false,
230
+ isNamespace: false,
231
+ });
232
+ }
233
+ }
234
+ else if (ts.isNamespaceImport(importClause.namedBindings)) {
235
+ symbols.push({
236
+ name: importClause.namedBindings.name.text,
237
+ isDefault: false,
238
+ isNamespace: true,
239
+ });
240
+ }
241
+ }
242
+ }
243
+ // Side-effect import (no symbols)
244
+ if (symbols.length === 0) {
245
+ symbols.push({
246
+ name: '*',
247
+ isDefault: false,
248
+ isNamespace: false,
249
+ });
250
+ }
251
+ return {
252
+ source,
253
+ resolvedPath,
254
+ isExternal,
255
+ typeOnly,
256
+ symbols,
257
+ line: line + 1, // Convert to 1-based
258
+ };
259
+ }
260
+ /**
261
+ * Try to resolve a local import path
262
+ */
263
+ function resolveLocalImport(source, fromDir) {
264
+ const basePath = path.resolve(fromDir, source);
265
+ // First try exact match for paths that already have extensions
266
+ if (basePath.match(/\.(ts|tsx|js|jsx|mts|mjs|cts|cjs)$/)) {
267
+ return basePath;
268
+ }
269
+ // Try with most likely TypeScript extensions first
270
+ // We don't check if file exists to avoid async issues
271
+ // Just return the most likely path (.ts is most common)
272
+ return basePath + '.ts';
273
+ }
274
+ /**
275
+ * Get transitive imports (imports of imports)
276
+ */
277
+ async function getTransitiveImports(rootFile, directImports, maxDepth) {
278
+ const transitiveImports = [];
279
+ const visited = new Set([rootFile]);
280
+ async function traverse(imports, chain, depth) {
281
+ if (depth > maxDepth)
282
+ return;
283
+ for (const imp of imports) {
284
+ if (imp.isExternal || !imp.resolvedPath)
285
+ continue;
286
+ if (visited.has(imp.resolvedPath))
287
+ continue;
288
+ visited.add(imp.resolvedPath);
289
+ const newChain = [...chain, imp.resolvedPath];
290
+ transitiveImports.push({
291
+ chain: newChain,
292
+ target: imp.source,
293
+ depth,
294
+ });
295
+ // Get imports from this file
296
+ try {
297
+ const fileImports = await analyzeFileImports(imp.resolvedPath);
298
+ await traverse(fileImports, newChain, depth + 1);
299
+ }
300
+ catch {
301
+ // Skip files that can't be analyzed
302
+ }
303
+ }
304
+ }
305
+ await traverse(directImports, [rootFile], 1);
306
+ return transitiveImports;
307
+ }
308
+ /**
309
+ * Get TypeScript script kind from file extension
310
+ */
311
+ function getScriptKind(filePath) {
312
+ const ext = filePath.toLowerCase().split('.').pop();
313
+ switch (ext) {
314
+ case 'ts':
315
+ return ts.ScriptKind.TS;
316
+ case 'tsx':
317
+ return ts.ScriptKind.TSX;
318
+ case 'js':
319
+ return ts.ScriptKind.JS;
320
+ case 'jsx':
321
+ return ts.ScriptKind.JSX;
322
+ case 'mts':
323
+ case 'cts':
324
+ return ts.ScriptKind.TS;
325
+ case 'mjs':
326
+ case 'cjs':
327
+ return ts.ScriptKind.JS;
328
+ default:
329
+ return ts.ScriptKind.TS;
330
+ }
331
+ }
332
+ /**
333
+ * Factory function to create a customized getImports tool
334
+ */
335
+ export function createGetImportsTool(options) {
336
+ const { defaultRecursive = false, defaultTransitive = false, defaultMaxDepth = 3, } = options ?? {};
337
+ return defineTool({
338
+ name: 'get_imports',
339
+ description: TOOL_DESCRIPTION,
340
+ inputSchema: TOOL_INPUT_SCHEMA,
341
+ execute: async (input) => {
342
+ return executeGetImports({
343
+ ...input,
344
+ recursive: input.recursive ?? defaultRecursive,
345
+ transitive: input.transitive ?? defaultTransitive,
346
+ maxDepth: input.maxDepth ?? defaultMaxDepth,
347
+ });
348
+ },
349
+ });
350
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * getSignature Tool
3
+ *
4
+ * Get detailed signature information for a function, method, class, or type.
5
+ * Includes parameters, return types, generics, and documentation.
6
+ */
7
+ import type { Tool } from '@compilr-dev/agents';
8
+ import type { GetSignatureInput } from './types.js';
9
+ /**
10
+ * getSignature tool
11
+ */
12
+ export declare const getSignatureTool: Tool<GetSignatureInput>;
13
+ /**
14
+ * Create customizable getSignature tool
15
+ */
16
+ export declare function createGetSignatureTool(options?: {
17
+ name?: string;
18
+ description?: string;
19
+ defaultIncludeDoc?: boolean;
20
+ }): Tool<GetSignatureInput>;