@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,341 @@
1
+ /**
2
+ * Project Detection Tool
3
+ * Detects project type, package manager, and available tools
4
+ */
5
+ import { readdir, readFile, stat } from 'node:fs/promises';
6
+ import { join } from 'node:path';
7
+ import { defineTool, createSuccessResult, createErrorResult } from '@compilr-dev/agents';
8
+ /**
9
+ * Project detection rules - ordered by priority
10
+ */
11
+ const PROJECT_RULES = [
12
+ // Node.js
13
+ { type: 'node', files: ['package.json'], priority: 10 },
14
+ // Python
15
+ { type: 'python', files: ['pyproject.toml'], packageManager: 'poetry', priority: 10 },
16
+ { type: 'python', files: ['setup.py'], packageManager: 'pip', priority: 8 },
17
+ { type: 'python', files: ['requirements.txt'], packageManager: 'pip', priority: 7 },
18
+ { type: 'python', files: ['Pipfile'], packageManager: 'pip', priority: 9 },
19
+ // Rust
20
+ { type: 'rust', files: ['Cargo.toml'], packageManager: 'cargo', priority: 10 },
21
+ // Go
22
+ { type: 'go', files: ['go.mod'], packageManager: 'go', priority: 10 },
23
+ // Java
24
+ { type: 'java', files: ['pom.xml'], packageManager: 'maven', priority: 10 },
25
+ { type: 'java', files: ['build.gradle'], packageManager: 'gradle', priority: 10 },
26
+ { type: 'java', files: ['build.gradle.kts'], packageManager: 'gradle', priority: 10 },
27
+ // Ruby
28
+ { type: 'ruby', files: ['Gemfile'], packageManager: 'bundler', priority: 10 },
29
+ // PHP
30
+ { type: 'php', files: ['composer.json'], packageManager: 'composer', priority: 10 },
31
+ // .NET
32
+ { type: 'dotnet', files: ['*.csproj'], packageManager: 'dotnet', priority: 10 },
33
+ { type: 'dotnet', files: ['*.sln'], packageManager: 'dotnet', priority: 9 },
34
+ { type: 'dotnet', files: ['*.fsproj'], packageManager: 'dotnet', priority: 10 },
35
+ ];
36
+ /**
37
+ * Test framework detection rules
38
+ */
39
+ const TEST_RULES = [
40
+ // Node.js
41
+ { files: ['vitest.config.ts', 'vitest.config.js'], dependencies: ['vitest'], name: 'vitest' },
42
+ { files: ['jest.config.ts', 'jest.config.js'], dependencies: ['jest'], name: 'jest' },
43
+ { files: ['.mocharc.json', '.mocharc.js'], dependencies: ['mocha'], name: 'mocha' },
44
+ { files: ['ava.config.js'], dependencies: ['ava'], name: 'ava' },
45
+ { files: ['playwright.config.ts'], dependencies: ['@playwright/test'], name: 'playwright' },
46
+ { files: ['cypress.config.ts'], dependencies: ['cypress'], name: 'cypress' },
47
+ // Python
48
+ { files: ['pytest.ini', 'pyproject.toml'], name: 'pytest' },
49
+ { files: ['setup.cfg'], name: 'unittest' },
50
+ // Rust
51
+ { files: ['Cargo.toml'], name: 'cargo test' },
52
+ // Go
53
+ { files: ['*_test.go'], name: 'go test' },
54
+ ];
55
+ /**
56
+ * Linter detection rules
57
+ */
58
+ const LINTER_RULES = [
59
+ // Node.js
60
+ {
61
+ files: ['eslint.config.js', 'eslint.config.mjs', '.eslintrc.js', '.eslintrc.json'],
62
+ dependencies: ['eslint'],
63
+ name: 'eslint',
64
+ },
65
+ { files: ['biome.json'], dependencies: ['@biomejs/biome'], name: 'biome' },
66
+ { files: ['tslint.json'], dependencies: ['tslint'], name: 'tslint' },
67
+ // Python
68
+ { files: ['ruff.toml', 'pyproject.toml'], name: 'ruff' },
69
+ { files: ['.pylintrc', 'pyproject.toml'], name: 'pylint' },
70
+ { files: ['.flake8', 'setup.cfg'], name: 'flake8' },
71
+ // Rust
72
+ { files: ['Cargo.toml'], name: 'clippy' },
73
+ // Go
74
+ { files: ['.golangci.yml', '.golangci.yaml'], name: 'golangci-lint' },
75
+ ];
76
+ /**
77
+ * Formatter detection rules
78
+ */
79
+ const FORMATTER_RULES = [
80
+ // Node.js
81
+ {
82
+ files: ['.prettierrc', '.prettierrc.js', '.prettierrc.json'],
83
+ dependencies: ['prettier'],
84
+ name: 'prettier',
85
+ },
86
+ { files: ['biome.json'], dependencies: ['@biomejs/biome'], name: 'biome' },
87
+ // Python
88
+ { files: ['pyproject.toml'], name: 'black' },
89
+ { files: ['ruff.toml', 'pyproject.toml'], name: 'ruff format' },
90
+ // Rust
91
+ { files: ['rustfmt.toml', '.rustfmt.toml'], name: 'rustfmt' },
92
+ // Go
93
+ { files: ['go.mod'], name: 'gofmt' },
94
+ ];
95
+ /**
96
+ * Build tool detection rules
97
+ */
98
+ const BUILD_RULES = [
99
+ // Node.js
100
+ { files: ['vite.config.ts', 'vite.config.js'], dependencies: ['vite'], name: 'vite' },
101
+ { files: ['webpack.config.js'], dependencies: ['webpack'], name: 'webpack' },
102
+ { files: ['rollup.config.js'], dependencies: ['rollup'], name: 'rollup' },
103
+ { files: ['esbuild.config.js'], dependencies: ['esbuild'], name: 'esbuild' },
104
+ { files: ['tsconfig.json'], dependencies: ['typescript'], scripts: ['build'], name: 'tsc' },
105
+ { files: ['turbo.json'], dependencies: ['turbo'], name: 'turborepo' },
106
+ { files: ['nx.json'], dependencies: ['nx'], name: 'nx' },
107
+ // Rust
108
+ { files: ['Cargo.toml'], name: 'cargo build' },
109
+ // Go
110
+ { files: ['go.mod'], name: 'go build' },
111
+ ];
112
+ /**
113
+ * Detect project tool
114
+ */
115
+ export const detectProjectTool = defineTool({
116
+ name: 'detect_project',
117
+ description: 'Detect the type of project in a directory. ' +
118
+ 'Returns project type (node, python, rust, go, java, etc.), ' +
119
+ 'package manager, test framework, linter, formatter, and build tool.',
120
+ inputSchema: {
121
+ type: 'object',
122
+ properties: {
123
+ path: {
124
+ type: 'string',
125
+ description: 'Directory to analyze (default: current directory)',
126
+ },
127
+ },
128
+ required: [],
129
+ },
130
+ execute: executeDetectProject,
131
+ });
132
+ /**
133
+ * Execute project detection
134
+ */
135
+ async function executeDetectProject(input) {
136
+ const targetPath = input.path ?? process.cwd();
137
+ // Check if directory exists
138
+ try {
139
+ const stats = await stat(targetPath);
140
+ if (!stats.isDirectory()) {
141
+ return createErrorResult(`Not a directory: ${targetPath}`);
142
+ }
143
+ }
144
+ catch (error) {
145
+ if (isNodeError(error) && error.code === 'ENOENT') {
146
+ return createErrorResult(`Directory not found: ${targetPath}`);
147
+ }
148
+ return createErrorResult(error instanceof Error ? error.message : String(error));
149
+ }
150
+ try {
151
+ // List directory contents
152
+ const entries = await readdir(targetPath);
153
+ const configFiles = [];
154
+ // Detect project types
155
+ const detectedTypes = new Map();
156
+ for (const rule of PROJECT_RULES) {
157
+ for (const pattern of rule.files) {
158
+ if (pattern.includes('*')) {
159
+ // Glob pattern
160
+ const regex = new RegExp('^' + pattern.replace(/\./g, '\\.').replace(/\*/g, '.*') + '$');
161
+ const matches = entries.filter((e) => regex.test(e));
162
+ if (matches.length > 0) {
163
+ configFiles.push(...matches);
164
+ const existing = detectedTypes.get(rule.type);
165
+ if (!existing || existing.priority < rule.priority) {
166
+ detectedTypes.set(rule.type, {
167
+ packageManager: rule.packageManager,
168
+ priority: rule.priority,
169
+ });
170
+ }
171
+ }
172
+ }
173
+ else {
174
+ // Exact match
175
+ if (entries.includes(pattern)) {
176
+ configFiles.push(pattern);
177
+ const existing = detectedTypes.get(rule.type);
178
+ if (!existing || existing.priority < rule.priority) {
179
+ detectedTypes.set(rule.type, {
180
+ packageManager: rule.packageManager,
181
+ priority: rule.priority,
182
+ });
183
+ }
184
+ }
185
+ }
186
+ }
187
+ }
188
+ // Load package.json for additional detection
189
+ let packageJson = null;
190
+ if (entries.includes('package.json')) {
191
+ try {
192
+ const content = await readFile(join(targetPath, 'package.json'), 'utf-8');
193
+ packageJson = JSON.parse(content);
194
+ }
195
+ catch {
196
+ // Ignore parse errors
197
+ }
198
+ }
199
+ // Detect Node.js package manager from lockfiles
200
+ let nodePackageManager;
201
+ if (entries.includes('pnpm-lock.yaml')) {
202
+ nodePackageManager = 'pnpm';
203
+ }
204
+ else if (entries.includes('yarn.lock')) {
205
+ nodePackageManager = 'yarn';
206
+ }
207
+ else if (entries.includes('package-lock.json')) {
208
+ nodePackageManager = 'npm';
209
+ }
210
+ // Update node package manager if detected
211
+ if (nodePackageManager && detectedTypes.has('node')) {
212
+ const nodeInfo = detectedTypes.get('node');
213
+ if (nodeInfo) {
214
+ detectedTypes.set('node', { ...nodeInfo, packageManager: nodePackageManager });
215
+ }
216
+ }
217
+ // Determine primary type (highest priority)
218
+ let primaryType = 'unknown';
219
+ let primaryPackageManager;
220
+ let highestPriority = -1;
221
+ for (const [type, info] of detectedTypes) {
222
+ if (info.priority > highestPriority) {
223
+ highestPriority = info.priority;
224
+ primaryType = type;
225
+ primaryPackageManager = info.packageManager;
226
+ }
227
+ }
228
+ // Detect tools
229
+ const testFramework = await detectTool(targetPath, entries, TEST_RULES, packageJson);
230
+ const linter = await detectTool(targetPath, entries, LINTER_RULES, packageJson);
231
+ const formatter = await detectTool(targetPath, entries, FORMATTER_RULES, packageJson);
232
+ const buildTool = await detectTool(targetPath, entries, BUILD_RULES, packageJson);
233
+ const result = {
234
+ type: primaryType,
235
+ types: Array.from(detectedTypes.keys()),
236
+ packageManager: primaryPackageManager,
237
+ testFramework,
238
+ linter,
239
+ formatter,
240
+ buildTool,
241
+ configFiles: [...new Set(configFiles)],
242
+ name: packageJson?.name,
243
+ };
244
+ return createSuccessResult(result);
245
+ }
246
+ catch (error) {
247
+ return createErrorResult(error instanceof Error ? error.message : String(error));
248
+ }
249
+ }
250
+ /**
251
+ * Detect a tool from rules
252
+ */
253
+ async function detectTool(targetPath, entries, rules, packageJson) {
254
+ for (const rule of rules) {
255
+ // Check files
256
+ for (const pattern of rule.files) {
257
+ if (pattern.includes('*')) {
258
+ const regex = new RegExp('^' + pattern.replace(/\./g, '\\.').replace(/\*/g, '.*') + '$');
259
+ if (entries.some((e) => regex.test(e))) {
260
+ // Additional check for pyproject.toml - verify it contains the tool
261
+ if (pattern === 'pyproject.toml' && entries.includes('pyproject.toml')) {
262
+ try {
263
+ const content = await readFile(join(targetPath, 'pyproject.toml'), 'utf-8');
264
+ if ((rule.name === 'pytest' && content.includes('[tool.pytest')) ||
265
+ (rule.name === 'black' && content.includes('[tool.black')) ||
266
+ (rule.name === 'ruff' && content.includes('[tool.ruff')) ||
267
+ (rule.name === 'ruff format' && content.includes('[tool.ruff'))) {
268
+ return rule.name;
269
+ }
270
+ }
271
+ catch {
272
+ // Ignore read errors
273
+ }
274
+ }
275
+ else {
276
+ return rule.name;
277
+ }
278
+ }
279
+ }
280
+ else if (entries.includes(pattern)) {
281
+ return rule.name;
282
+ }
283
+ }
284
+ // Check package.json dependencies
285
+ if (packageJson && rule.dependencies) {
286
+ const allDeps = {
287
+ ...packageJson.dependencies,
288
+ ...packageJson.devDependencies,
289
+ };
290
+ for (const dep of rule.dependencies) {
291
+ if (allDeps[dep]) {
292
+ return rule.name;
293
+ }
294
+ }
295
+ }
296
+ // Check package.json scripts
297
+ if (packageJson?.scripts && rule.scripts) {
298
+ for (const script of rule.scripts) {
299
+ if (packageJson.scripts[script]) {
300
+ return rule.name;
301
+ }
302
+ }
303
+ }
304
+ }
305
+ return undefined;
306
+ }
307
+ /**
308
+ * Check if error is a Node.js error with code
309
+ */
310
+ function isNodeError(error) {
311
+ return error instanceof Error && 'code' in error;
312
+ }
313
+ /**
314
+ * Factory function to create detect project tool with custom options
315
+ */
316
+ export function createDetectProjectTool(options) {
317
+ return defineTool({
318
+ name: 'detect_project',
319
+ description: 'Detect the type of project in a directory. ' +
320
+ 'Returns project type (node, python, rust, go, java, etc.), ' +
321
+ 'package manager, test framework, linter, formatter, and build tool.',
322
+ inputSchema: {
323
+ type: 'object',
324
+ properties: {
325
+ path: {
326
+ type: 'string',
327
+ description: 'Directory to analyze (default: current directory)',
328
+ },
329
+ },
330
+ required: [],
331
+ },
332
+ execute: async (input) => {
333
+ let targetPath = input.path ?? '.';
334
+ // Resolve relative paths
335
+ if (options?.baseDir && !targetPath.startsWith('/')) {
336
+ targetPath = join(options.baseDir, targetPath);
337
+ }
338
+ return executeDetectProject({ path: targetPath });
339
+ },
340
+ });
341
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Find Project Root Tool
3
+ * Finds the root directory of a project by looking for marker files
4
+ */
5
+ import type { Tool } from '@compilr-dev/agents';
6
+ import type { FindProjectRootInput } from './types.js';
7
+ /**
8
+ * Find project root tool
9
+ */
10
+ export declare const findProjectRootTool: Tool<FindProjectRootInput>;
11
+ /**
12
+ * Factory function to create find project root tool with custom options
13
+ */
14
+ export declare function createFindProjectRootTool(options?: {
15
+ /** Base directory for relative paths */
16
+ baseDir?: string;
17
+ /** Default markers to use */
18
+ defaultMarkers?: string[];
19
+ /** Maximum depth to search */
20
+ maxDepth?: number;
21
+ }): Tool<FindProjectRootInput>;
@@ -0,0 +1,239 @@
1
+ /**
2
+ * Find Project Root Tool
3
+ * Finds the root directory of a project by looking for marker files
4
+ */
5
+ import { access, stat } from 'node:fs/promises';
6
+ import { join, dirname, resolve, sep } from 'node:path';
7
+ import { defineTool, createSuccessResult, createErrorResult } from '@compilr-dev/agents';
8
+ /**
9
+ * Default marker files to look for when finding project root
10
+ */
11
+ const DEFAULT_MARKERS = [
12
+ // Version control
13
+ '.git',
14
+ '.hg',
15
+ '.svn',
16
+ // Node.js
17
+ 'package.json',
18
+ // Python
19
+ 'pyproject.toml',
20
+ 'setup.py',
21
+ 'setup.cfg',
22
+ // Rust
23
+ 'Cargo.toml',
24
+ // Go
25
+ 'go.mod',
26
+ // Java
27
+ 'pom.xml',
28
+ 'build.gradle',
29
+ 'build.gradle.kts',
30
+ // Ruby
31
+ 'Gemfile',
32
+ // PHP
33
+ 'composer.json',
34
+ // .NET
35
+ '*.sln',
36
+ '*.csproj',
37
+ // Generic
38
+ '.project-root', // Explicit marker file
39
+ ];
40
+ /**
41
+ * Find project root tool
42
+ */
43
+ export const findProjectRootTool = defineTool({
44
+ name: 'find_project_root',
45
+ description: 'Find the root directory of a project by looking for marker files. ' +
46
+ 'Searches upward from the starting directory until a marker is found. ' +
47
+ 'Common markers: .git, package.json, Cargo.toml, go.mod, etc.',
48
+ inputSchema: {
49
+ type: 'object',
50
+ properties: {
51
+ path: {
52
+ type: 'string',
53
+ description: 'Starting directory (default: current directory)',
54
+ },
55
+ markers: {
56
+ type: 'array',
57
+ items: { type: 'string' },
58
+ description: 'Marker files to look for (default: common project files)',
59
+ },
60
+ },
61
+ required: [],
62
+ },
63
+ execute: executeFindProjectRoot,
64
+ });
65
+ /**
66
+ * Execute find project root
67
+ */
68
+ async function executeFindProjectRoot(input) {
69
+ const startPath = resolve(input.path ?? process.cwd());
70
+ const markers = input.markers ?? DEFAULT_MARKERS;
71
+ // Check if start path exists
72
+ try {
73
+ const stats = await stat(startPath);
74
+ if (!stats.isDirectory()) {
75
+ return createErrorResult(`Not a directory: ${startPath}`);
76
+ }
77
+ }
78
+ catch (error) {
79
+ if (isNodeError(error) && error.code === 'ENOENT') {
80
+ return createErrorResult(`Directory not found: ${startPath}`);
81
+ }
82
+ return createErrorResult(error instanceof Error ? error.message : String(error));
83
+ }
84
+ try {
85
+ let currentPath = startPath;
86
+ let depth = 0;
87
+ const root = getRootPath();
88
+ while (currentPath.length >= root.length) {
89
+ // Check each marker
90
+ for (const marker of markers) {
91
+ if (marker.includes('*')) {
92
+ // Glob pattern - check if any matching file exists
93
+ const found = await checkGlobMarker(currentPath, marker);
94
+ if (found) {
95
+ const result = {
96
+ root: currentPath,
97
+ foundMarker: found,
98
+ depth,
99
+ };
100
+ return createSuccessResult(result);
101
+ }
102
+ }
103
+ else {
104
+ // Exact match
105
+ const markerPath = join(currentPath, marker);
106
+ if (await fileExists(markerPath)) {
107
+ const result = {
108
+ root: currentPath,
109
+ foundMarker: marker,
110
+ depth,
111
+ };
112
+ return createSuccessResult(result);
113
+ }
114
+ }
115
+ }
116
+ // Move up one directory
117
+ const parentPath = dirname(currentPath);
118
+ if (parentPath === currentPath) {
119
+ // Reached filesystem root
120
+ break;
121
+ }
122
+ currentPath = parentPath;
123
+ depth++;
124
+ }
125
+ return createErrorResult(`No project root found. Searched from ${startPath} to filesystem root.`);
126
+ }
127
+ catch (error) {
128
+ return createErrorResult(error instanceof Error ? error.message : String(error));
129
+ }
130
+ }
131
+ /**
132
+ * Check if a file exists
133
+ */
134
+ async function fileExists(path) {
135
+ try {
136
+ await access(path);
137
+ return true;
138
+ }
139
+ catch {
140
+ return false;
141
+ }
142
+ }
143
+ /**
144
+ * Check for glob marker pattern (e.g., *.sln)
145
+ */
146
+ async function checkGlobMarker(directory, pattern) {
147
+ const { readdir } = await import('node:fs/promises');
148
+ try {
149
+ const entries = await readdir(directory);
150
+ const regex = new RegExp('^' + pattern.replace(/\./g, '\\.').replace(/\*/g, '.*') + '$');
151
+ for (const entry of entries) {
152
+ if (regex.test(entry)) {
153
+ return entry;
154
+ }
155
+ }
156
+ return null;
157
+ }
158
+ catch {
159
+ return null;
160
+ }
161
+ }
162
+ /**
163
+ * Get the root path for the current platform
164
+ */
165
+ function getRootPath() {
166
+ // On Windows, root could be C:\, D:\, etc.
167
+ // On Unix, root is /
168
+ if (process.platform === 'win32') {
169
+ return process.cwd().split(sep)[0] + sep;
170
+ }
171
+ return '/';
172
+ }
173
+ /**
174
+ * Check if error is a Node.js error with code
175
+ */
176
+ function isNodeError(error) {
177
+ return error instanceof Error && 'code' in error;
178
+ }
179
+ /**
180
+ * Factory function to create find project root tool with custom options
181
+ */
182
+ export function createFindProjectRootTool(options) {
183
+ return defineTool({
184
+ name: 'find_project_root',
185
+ description: 'Find the root directory of a project by looking for marker files. ' +
186
+ 'Searches upward from the starting directory until a marker is found. ' +
187
+ 'Common markers: .git, package.json, Cargo.toml, go.mod, etc.',
188
+ inputSchema: {
189
+ type: 'object',
190
+ properties: {
191
+ path: {
192
+ type: 'string',
193
+ description: 'Starting directory (default: current directory)',
194
+ },
195
+ markers: {
196
+ type: 'array',
197
+ items: { type: 'string' },
198
+ description: 'Marker files to look for (default: common project files)',
199
+ },
200
+ },
201
+ required: [],
202
+ },
203
+ execute: async (input) => {
204
+ let startPath = input.path ?? '.';
205
+ const markers = input.markers ?? options?.defaultMarkers ?? DEFAULT_MARKERS;
206
+ // Resolve relative paths
207
+ if (options?.baseDir && !startPath.startsWith('/')) {
208
+ startPath = join(options.baseDir, startPath);
209
+ }
210
+ // If maxDepth is set, limit the search
211
+ if (options?.maxDepth !== undefined) {
212
+ const absoluteStart = resolve(startPath);
213
+ let currentPath = absoluteStart;
214
+ let depth = 0;
215
+ while (depth <= options.maxDepth) {
216
+ // Check markers at current depth
217
+ for (const marker of markers) {
218
+ const markerPath = join(currentPath, marker);
219
+ if (await fileExists(markerPath)) {
220
+ const result = {
221
+ root: currentPath,
222
+ foundMarker: marker,
223
+ depth,
224
+ };
225
+ return createSuccessResult(result);
226
+ }
227
+ }
228
+ const parentPath = dirname(currentPath);
229
+ if (parentPath === currentPath)
230
+ break;
231
+ currentPath = parentPath;
232
+ depth++;
233
+ }
234
+ return createErrorResult(`No project root found within ${String(options.maxDepth)} directories of ${startPath}`);
235
+ }
236
+ return executeFindProjectRoot({ path: startPath, markers });
237
+ },
238
+ });
239
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Project detection tools
3
+ */
4
+ export { detectProjectTool, createDetectProjectTool } from './detect.js';
5
+ export { findProjectRootTool, createFindProjectRootTool } from './find-root.js';
6
+ export type { DetectProjectInput, DetectProjectResult, ProjectType, PackageManager, FindProjectRootInput, FindProjectRootResult, } from './types.js';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Project detection tools
3
+ */
4
+ export { detectProjectTool, createDetectProjectTool } from './detect.js';
5
+ export { findProjectRootTool, createFindProjectRootTool } from './find-root.js';