@compilr-dev/agents-coding 0.0.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 (60) hide show
  1. package/README.md +788 -0
  2. package/dist/index.d.ts +39 -0
  3. package/dist/index.js +75 -0
  4. package/dist/skills/index.d.ts +39 -0
  5. package/dist/skills/index.js +322 -0
  6. package/dist/tools/git/branch.d.ts +17 -0
  7. package/dist/tools/git/branch.js +264 -0
  8. package/dist/tools/git/commit.d.ts +23 -0
  9. package/dist/tools/git/commit.js +280 -0
  10. package/dist/tools/git/diff.d.ts +19 -0
  11. package/dist/tools/git/diff.js +221 -0
  12. package/dist/tools/git/index.d.ts +10 -0
  13. package/dist/tools/git/index.js +11 -0
  14. package/dist/tools/git/log.d.ts +19 -0
  15. package/dist/tools/git/log.js +235 -0
  16. package/dist/tools/git/stash.d.ts +17 -0
  17. package/dist/tools/git/stash.js +294 -0
  18. package/dist/tools/git/status.d.ts +19 -0
  19. package/dist/tools/git/status.js +160 -0
  20. package/dist/tools/git/types.d.ts +293 -0
  21. package/dist/tools/git/types.js +4 -0
  22. package/dist/tools/git/utils.d.ts +58 -0
  23. package/dist/tools/git/utils.js +197 -0
  24. package/dist/tools/index.d.ts +5 -0
  25. package/dist/tools/index.js +5 -0
  26. package/dist/tools/project/detect.d.ts +19 -0
  27. package/dist/tools/project/detect.js +341 -0
  28. package/dist/tools/project/find-root.d.ts +21 -0
  29. package/dist/tools/project/find-root.js +239 -0
  30. package/dist/tools/project/index.d.ts +6 -0
  31. package/dist/tools/project/index.js +5 -0
  32. package/dist/tools/project/types.d.ts +83 -0
  33. package/dist/tools/project/types.js +4 -0
  34. package/dist/tools/runners/build.d.ts +19 -0
  35. package/dist/tools/runners/build.js +306 -0
  36. package/dist/tools/runners/format.d.ts +19 -0
  37. package/dist/tools/runners/format.js +376 -0
  38. package/dist/tools/runners/index.d.ts +9 -0
  39. package/dist/tools/runners/index.js +9 -0
  40. package/dist/tools/runners/lint.d.ts +19 -0
  41. package/dist/tools/runners/lint.js +356 -0
  42. package/dist/tools/runners/test.d.ts +19 -0
  43. package/dist/tools/runners/test.js +386 -0
  44. package/dist/tools/runners/types.d.ts +97 -0
  45. package/dist/tools/runners/types.js +4 -0
  46. package/dist/tools/runners/utils.d.ts +69 -0
  47. package/dist/tools/runners/utils.js +179 -0
  48. package/dist/tools/search/definition.d.ts +19 -0
  49. package/dist/tools/search/definition.js +305 -0
  50. package/dist/tools/search/index.d.ts +8 -0
  51. package/dist/tools/search/index.js +8 -0
  52. package/dist/tools/search/references.d.ts +19 -0
  53. package/dist/tools/search/references.js +179 -0
  54. package/dist/tools/search/todos.d.ts +19 -0
  55. package/dist/tools/search/todos.js +269 -0
  56. package/dist/tools/search/types.d.ts +132 -0
  57. package/dist/tools/search/types.js +4 -0
  58. package/dist/tools/search/utils.d.ts +45 -0
  59. package/dist/tools/search/utils.js +152 -0
  60. package/package.json +88 -0
@@ -0,0 +1,386 @@
1
+ /**
2
+ * Run Tests Tool
3
+ * Auto-detect and run tests using the appropriate framework
4
+ */
5
+ import { defineTool, createSuccessResult, createErrorResult } from '@compilr-dev/agents';
6
+ import { isDirectory, readPackageJson, listDirectory, hasFile, hasDependency, hasScript, runCommand, buildCommand, formatCommand, DEFAULT_TIMEOUT, } from './utils.js';
7
+ const TEST_FRAMEWORKS = [
8
+ // Vitest (Node.js)
9
+ {
10
+ name: 'vitest',
11
+ detect: (entries, pkg) => hasFile(entries, 'vitest.config.ts') ||
12
+ hasFile(entries, 'vitest.config.js') ||
13
+ hasFile(entries, 'vitest.config.mts') ||
14
+ hasDependency(pkg, 'vitest'),
15
+ getCommand: (input) => {
16
+ const args = ['vitest'];
17
+ if (!input.watch)
18
+ args.push('run');
19
+ if (input.pattern)
20
+ args.push(input.pattern);
21
+ if (input.coverage)
22
+ args.push('--coverage');
23
+ if (input.updateSnapshots)
24
+ args.push('-u');
25
+ return { command: 'npx', args, useNpx: false };
26
+ },
27
+ },
28
+ // Jest (Node.js)
29
+ {
30
+ name: 'jest',
31
+ detect: (entries, pkg) => hasFile(entries, 'jest.config.ts') ||
32
+ hasFile(entries, 'jest.config.js') ||
33
+ hasFile(entries, 'jest.config.json') ||
34
+ hasDependency(pkg, 'jest'),
35
+ getCommand: (input) => {
36
+ const args = ['jest'];
37
+ if (input.pattern)
38
+ args.push(input.pattern);
39
+ if (!input.watch)
40
+ args.push('--runInBand');
41
+ if (input.watch)
42
+ args.push('--watch');
43
+ if (input.coverage)
44
+ args.push('--coverage');
45
+ if (input.updateSnapshots)
46
+ args.push('-u');
47
+ if (input.verbose)
48
+ args.push('--verbose');
49
+ return { command: 'npx', args, useNpx: false };
50
+ },
51
+ },
52
+ // Mocha (Node.js)
53
+ {
54
+ name: 'mocha',
55
+ detect: (entries, pkg) => hasFile(entries, '.mocharc.json') ||
56
+ hasFile(entries, '.mocharc.js') ||
57
+ hasFile(entries, '.mocharc.yaml') ||
58
+ hasDependency(pkg, 'mocha'),
59
+ getCommand: (input) => {
60
+ const args = ['mocha'];
61
+ if (input.pattern)
62
+ args.push(input.pattern);
63
+ if (input.watch)
64
+ args.push('--watch');
65
+ return { command: 'npx', args, useNpx: false };
66
+ },
67
+ },
68
+ // AVA (Node.js)
69
+ {
70
+ name: 'ava',
71
+ detect: (entries, pkg) => hasFile(entries, 'ava.config.js') ||
72
+ hasFile(entries, 'ava.config.cjs') ||
73
+ hasDependency(pkg, 'ava'),
74
+ getCommand: (input) => {
75
+ const args = ['ava'];
76
+ if (input.pattern)
77
+ args.push(input.pattern);
78
+ if (input.watch)
79
+ args.push('--watch');
80
+ if (input.verbose)
81
+ args.push('--verbose');
82
+ if (input.updateSnapshots)
83
+ args.push('-u');
84
+ return { command: 'npx', args, useNpx: false };
85
+ },
86
+ },
87
+ // npm test script fallback (Node.js)
88
+ {
89
+ name: 'npm test',
90
+ detect: (_entries, pkg) => hasScript(pkg, 'test'),
91
+ getCommand: () => ({
92
+ command: 'npm',
93
+ args: ['test'],
94
+ useScript: true,
95
+ scriptName: 'test',
96
+ }),
97
+ },
98
+ // pytest (Python)
99
+ {
100
+ name: 'pytest',
101
+ detect: (entries) => hasFile(entries, 'pytest.ini') ||
102
+ hasFile(entries, 'pyproject.toml') ||
103
+ hasFile(entries, 'conftest.py'),
104
+ getCommand: (input) => {
105
+ const args = [];
106
+ if (input.pattern)
107
+ args.push(input.pattern);
108
+ if (input.verbose)
109
+ args.push('-v');
110
+ if (input.coverage)
111
+ args.push('--cov');
112
+ return { command: 'pytest', args };
113
+ },
114
+ },
115
+ // Python unittest
116
+ {
117
+ name: 'unittest',
118
+ detect: (entries) => hasFile(entries, 'setup.py') || hasFile(entries, 'setup.cfg'),
119
+ getCommand: (input) => {
120
+ const args = ['-m', 'unittest'];
121
+ if (input.pattern)
122
+ args.push('discover', '-p', input.pattern);
123
+ else
124
+ args.push('discover');
125
+ if (input.verbose)
126
+ args.push('-v');
127
+ return { command: 'python', args };
128
+ },
129
+ },
130
+ // Cargo test (Rust)
131
+ {
132
+ name: 'cargo test',
133
+ detect: (entries) => hasFile(entries, 'Cargo.toml'),
134
+ getCommand: (input) => {
135
+ const args = ['test'];
136
+ if (input.pattern)
137
+ args.push(input.pattern);
138
+ if (input.verbose)
139
+ args.push('--', '--nocapture');
140
+ return { command: 'cargo', args };
141
+ },
142
+ },
143
+ // Go test
144
+ {
145
+ name: 'go test',
146
+ detect: (entries) => hasFile(entries, 'go.mod'),
147
+ getCommand: (input) => {
148
+ const args = ['test'];
149
+ if (input.pattern)
150
+ args.push(input.pattern);
151
+ else
152
+ args.push('./...');
153
+ if (input.verbose)
154
+ args.push('-v');
155
+ if (input.coverage)
156
+ args.push('-cover');
157
+ return { command: 'go', args };
158
+ },
159
+ },
160
+ ];
161
+ /**
162
+ * Run Tests Tool
163
+ */
164
+ export const runTestsTool = defineTool({
165
+ name: 'run_tests',
166
+ description: 'Run tests using the auto-detected test framework. ' +
167
+ 'Supports vitest, jest, mocha, pytest, cargo test, go test, and npm test scripts. ' +
168
+ 'Options: pattern (filter tests), watch, coverage, verbose, updateSnapshots.',
169
+ inputSchema: {
170
+ type: 'object',
171
+ properties: {
172
+ path: {
173
+ type: 'string',
174
+ description: 'Working directory (default: current directory)',
175
+ },
176
+ pattern: {
177
+ type: 'string',
178
+ description: 'Test file pattern or test name filter',
179
+ },
180
+ watch: {
181
+ type: 'boolean',
182
+ description: 'Run in watch mode (if supported)',
183
+ },
184
+ verbose: {
185
+ type: 'boolean',
186
+ description: 'Verbose output',
187
+ },
188
+ coverage: {
189
+ type: 'boolean',
190
+ description: 'Generate coverage report',
191
+ },
192
+ updateSnapshots: {
193
+ type: 'boolean',
194
+ description: 'Update test snapshots (jest/vitest)',
195
+ },
196
+ timeout: {
197
+ type: 'number',
198
+ description: 'Timeout in milliseconds (default: 300000)',
199
+ },
200
+ dryRun: {
201
+ type: 'boolean',
202
+ description: 'Detect test framework and return command without executing (default: false)',
203
+ },
204
+ },
205
+ required: [],
206
+ },
207
+ execute: executeRunTests,
208
+ });
209
+ /**
210
+ * Execute run tests
211
+ */
212
+ async function executeRunTests(input) {
213
+ const targetPath = input.path ?? process.cwd();
214
+ const timeout = input.timeout ?? DEFAULT_TIMEOUT;
215
+ // Check if directory exists
216
+ if (!(await isDirectory(targetPath))) {
217
+ return createErrorResult(`Directory not found: ${targetPath}`);
218
+ }
219
+ try {
220
+ // Read directory and package.json
221
+ const entries = await listDirectory(targetPath);
222
+ const packageJson = await readPackageJson(targetPath);
223
+ // Detect test framework
224
+ let framework;
225
+ for (const fw of TEST_FRAMEWORKS) {
226
+ if (fw.detect(entries, packageJson)) {
227
+ framework = fw;
228
+ break;
229
+ }
230
+ }
231
+ if (!framework) {
232
+ return createErrorResult('No test framework detected. Supported: vitest, jest, mocha, ava, pytest, cargo test, go test, npm test script.');
233
+ }
234
+ // Build command
235
+ const template = framework.getCommand(input);
236
+ const { command, args } = buildCommand(template);
237
+ const commandString = formatCommand(command, args);
238
+ // Dry run - return detected info without executing
239
+ if (input.dryRun) {
240
+ const testResult = {
241
+ command: commandString,
242
+ output: '(dry run - command not executed)',
243
+ exitCode: 0,
244
+ duration: 0,
245
+ success: true,
246
+ framework: framework.name,
247
+ };
248
+ return createSuccessResult(testResult);
249
+ }
250
+ // Run tests
251
+ const result = await runCommand(command, args, {
252
+ cwd: targetPath,
253
+ timeout,
254
+ env: {
255
+ FORCE_COLOR: '1',
256
+ CI: 'true',
257
+ },
258
+ });
259
+ // Combine output
260
+ const output = [result.stdout, result.stderr].filter(Boolean).join('\n');
261
+ // Parse test counts from output (best effort)
262
+ const counts = parseTestCounts(output, framework.name);
263
+ const testResult = {
264
+ command: commandString,
265
+ output,
266
+ exitCode: result.exitCode,
267
+ duration: result.duration,
268
+ success: result.exitCode === 0,
269
+ framework: framework.name,
270
+ ...counts,
271
+ };
272
+ return createSuccessResult(testResult);
273
+ }
274
+ catch (error) {
275
+ return createErrorResult(error instanceof Error ? error.message : String(error));
276
+ }
277
+ }
278
+ /**
279
+ * Parse test counts from output (best effort)
280
+ */
281
+ function parseTestCounts(output, _framework) {
282
+ const counts = {};
283
+ // Vitest pattern: "✓ 70 tests passed"
284
+ const vitestMatch = output.match(/(\d+)\s+tests?\s+passed/i);
285
+ if (vitestMatch)
286
+ counts.passed = parseInt(vitestMatch[1], 10);
287
+ const vitestFailMatch = output.match(/(\d+)\s+tests?\s+failed/i);
288
+ if (vitestFailMatch)
289
+ counts.failed = parseInt(vitestFailMatch[1], 10);
290
+ // Jest pattern: "Tests: 5 passed, 1 failed, 6 total"
291
+ const jestMatch = output.match(/Tests:\s+(\d+)\s+passed,?\s*(\d+)?\s*failed?,?\s*(\d+)?\s*skipped?,?\s*(\d+)?\s*total/i);
292
+ if (jestMatch) {
293
+ counts.passed = parseInt(jestMatch[1], 10);
294
+ if (jestMatch[2])
295
+ counts.failed = parseInt(jestMatch[2], 10);
296
+ if (jestMatch[3])
297
+ counts.skipped = parseInt(jestMatch[3], 10);
298
+ if (jestMatch[4])
299
+ counts.total = parseInt(jestMatch[4], 10);
300
+ }
301
+ // pytest pattern: "5 passed, 1 failed"
302
+ const pytestPassMatch = output.match(/(\d+)\s+passed/i);
303
+ if (pytestPassMatch)
304
+ counts.passed = parseInt(pytestPassMatch[1], 10);
305
+ const pytestFailMatch = output.match(/(\d+)\s+failed/i);
306
+ if (pytestFailMatch)
307
+ counts.failed = parseInt(pytestFailMatch[1], 10);
308
+ const pytestSkipMatch = output.match(/(\d+)\s+skipped/i);
309
+ if (pytestSkipMatch)
310
+ counts.skipped = parseInt(pytestSkipMatch[1], 10);
311
+ // Go test pattern: "ok" or "FAIL"
312
+ const goOkMatch = output.match(/^ok\s+/gm);
313
+ if (goOkMatch)
314
+ counts.passed = goOkMatch.length;
315
+ const goFailMatch = output.match(/^FAIL\s+/gm);
316
+ if (goFailMatch)
317
+ counts.failed = goFailMatch.length;
318
+ // Cargo test pattern: "test result: ok. 10 passed; 0 failed"
319
+ const cargoMatch = output.match(/(\d+)\s+passed;\s+(\d+)\s+failed/);
320
+ if (cargoMatch) {
321
+ counts.passed = parseInt(cargoMatch[1], 10);
322
+ counts.failed = parseInt(cargoMatch[2], 10);
323
+ }
324
+ // Calculate total if we have passed and failed
325
+ if (counts.passed !== undefined || counts.failed !== undefined) {
326
+ counts.total = (counts.passed ?? 0) + (counts.failed ?? 0) + (counts.skipped ?? 0);
327
+ }
328
+ return counts;
329
+ }
330
+ /**
331
+ * Factory function to create run tests tool with custom options
332
+ */
333
+ export function createRunTestsTool(options) {
334
+ return defineTool({
335
+ name: 'run_tests',
336
+ description: 'Run tests using the auto-detected test framework. ' +
337
+ 'Supports vitest, jest, mocha, ava, pytest, cargo test, go test.',
338
+ inputSchema: {
339
+ type: 'object',
340
+ properties: {
341
+ path: {
342
+ type: 'string',
343
+ description: 'Working directory (default: current directory)',
344
+ },
345
+ pattern: {
346
+ type: 'string',
347
+ description: 'Test file pattern or test name filter',
348
+ },
349
+ watch: {
350
+ type: 'boolean',
351
+ description: 'Run in watch mode',
352
+ },
353
+ verbose: {
354
+ type: 'boolean',
355
+ description: 'Verbose output',
356
+ },
357
+ coverage: {
358
+ type: 'boolean',
359
+ description: 'Generate coverage report',
360
+ },
361
+ updateSnapshots: {
362
+ type: 'boolean',
363
+ description: 'Update test snapshots',
364
+ },
365
+ timeout: {
366
+ type: 'number',
367
+ description: 'Timeout in milliseconds',
368
+ },
369
+ },
370
+ required: [],
371
+ },
372
+ execute: async (input) => {
373
+ let targetPath = input.path ?? '.';
374
+ // Resolve relative paths
375
+ if (options?.baseDir && !targetPath.startsWith('/')) {
376
+ const nodePath = await import('node:path');
377
+ targetPath = nodePath.join(options.baseDir, targetPath);
378
+ }
379
+ return executeRunTests({
380
+ ...input,
381
+ path: targetPath,
382
+ timeout: input.timeout ?? options?.defaultTimeout,
383
+ });
384
+ },
385
+ });
386
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Types for Smart Runner Tools
3
+ */
4
+ export interface BaseRunnerInput {
5
+ /** Working directory path (default: current directory) */
6
+ path?: string;
7
+ /** Timeout in milliseconds (default: 300000 = 5 min) */
8
+ timeout?: number;
9
+ /** Dry run - detect and return command without executing (default: false) */
10
+ dryRun?: boolean;
11
+ }
12
+ export interface BaseRunnerResult {
13
+ /** Command that was executed */
14
+ command: string;
15
+ /** Command output (stdout + stderr) */
16
+ output: string;
17
+ /** Exit code */
18
+ exitCode: number;
19
+ /** Duration in milliseconds */
20
+ duration: number;
21
+ /** Whether the command succeeded (exitCode === 0) */
22
+ success: boolean;
23
+ }
24
+ export interface RunTestsInput extends BaseRunnerInput {
25
+ /** Specific test file or pattern to run */
26
+ pattern?: string;
27
+ /** Run in watch mode (if supported) */
28
+ watch?: boolean;
29
+ /** Verbose output */
30
+ verbose?: boolean;
31
+ /** Generate coverage report */
32
+ coverage?: boolean;
33
+ /** Update snapshots (for jest/vitest) */
34
+ updateSnapshots?: boolean;
35
+ }
36
+ export interface RunTestsResult extends BaseRunnerResult {
37
+ /** Number of tests passed (if parseable) */
38
+ passed?: number;
39
+ /** Number of tests failed (if parseable) */
40
+ failed?: number;
41
+ /** Number of tests skipped (if parseable) */
42
+ skipped?: number;
43
+ /** Total number of tests (if parseable) */
44
+ total?: number;
45
+ /** Detected test framework */
46
+ framework: string;
47
+ }
48
+ export interface RunLintInput extends BaseRunnerInput {
49
+ /** Specific files to lint */
50
+ files?: string[];
51
+ /** Auto-fix issues (if supported) */
52
+ fix?: boolean;
53
+ /** Output format (json, stylish, etc.) */
54
+ format?: string;
55
+ }
56
+ export interface RunLintResult extends BaseRunnerResult {
57
+ /** Number of errors (if parseable) */
58
+ errors?: number;
59
+ /** Number of warnings (if parseable) */
60
+ warnings?: number;
61
+ /** Detected linter */
62
+ linter: string;
63
+ }
64
+ export interface RunBuildInput extends BaseRunnerInput {
65
+ /** Build for production (optimized) */
66
+ production?: boolean;
67
+ /** Clean before build */
68
+ clean?: boolean;
69
+ }
70
+ export interface RunBuildResult extends BaseRunnerResult {
71
+ /** Detected build tool */
72
+ buildTool: string;
73
+ }
74
+ export interface RunFormatInput extends BaseRunnerInput {
75
+ /** Specific files to format */
76
+ files?: string[];
77
+ /** Check only, don't write changes */
78
+ check?: boolean;
79
+ }
80
+ export interface RunFormatResult extends BaseRunnerResult {
81
+ /** Number of files formatted/checked (if parseable) */
82
+ filesProcessed?: number;
83
+ /** Detected formatter */
84
+ formatter: string;
85
+ }
86
+ export interface CommandTemplate {
87
+ /** Base command */
88
+ command: string;
89
+ /** Arguments to add */
90
+ args: string[];
91
+ /** Whether this uses npm/npx */
92
+ useNpx?: boolean;
93
+ /** Whether this uses package.json scripts */
94
+ useScript?: boolean;
95
+ /** Script name for npm run */
96
+ scriptName?: string;
97
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Types for Smart Runner Tools
3
+ */
4
+ export {};
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Shared utilities for Smart Runner Tools
3
+ */
4
+ import type { CommandTemplate } from './types.js';
5
+ /**
6
+ * Default timeout for runner commands (5 minutes)
7
+ */
8
+ export declare const DEFAULT_TIMEOUT = 300000;
9
+ /**
10
+ * Maximum output size (50KB)
11
+ */
12
+ export declare const MAX_OUTPUT_SIZE: number;
13
+ /**
14
+ * Package.json content type
15
+ */
16
+ export interface PackageJsonContent {
17
+ name?: string;
18
+ dependencies?: Record<string, string>;
19
+ devDependencies?: Record<string, string>;
20
+ scripts?: Record<string, string>;
21
+ }
22
+ /**
23
+ * Check if directory exists
24
+ */
25
+ export declare function isDirectory(path: string): Promise<boolean>;
26
+ /**
27
+ * Read package.json from a directory
28
+ */
29
+ export declare function readPackageJson(dirPath: string): Promise<PackageJsonContent | null>;
30
+ /**
31
+ * List directory entries
32
+ */
33
+ export declare function listDirectory(dirPath: string): Promise<string[]>;
34
+ /**
35
+ * Check if a file exists in directory
36
+ */
37
+ export declare function hasFile(entries: string[], pattern: string): boolean;
38
+ /**
39
+ * Check if a dependency exists in package.json
40
+ */
41
+ export declare function hasDependency(packageJson: PackageJsonContent | null, dep: string): boolean;
42
+ /**
43
+ * Check if a script exists in package.json
44
+ */
45
+ export declare function hasScript(packageJson: PackageJsonContent | null, script: string): boolean;
46
+ /**
47
+ * Run a command and capture output
48
+ */
49
+ export declare function runCommand(command: string, args: string[], options: {
50
+ cwd: string;
51
+ timeout: number;
52
+ env?: Record<string, string>;
53
+ }): Promise<{
54
+ stdout: string;
55
+ stderr: string;
56
+ exitCode: number;
57
+ duration: number;
58
+ }>;
59
+ /**
60
+ * Build command from template
61
+ */
62
+ export declare function buildCommand(template: CommandTemplate): {
63
+ command: string;
64
+ args: string[];
65
+ };
66
+ /**
67
+ * Format command for display
68
+ */
69
+ export declare function formatCommand(command: string, args: string[]): string;