@stan-chen/simple-cli 0.2.3 → 0.2.4
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/README.md +62 -63
- package/dist/anyllm.py +62 -0
- package/dist/builtins.d.ts +726 -0
- package/dist/builtins.js +481 -0
- package/dist/cli.d.ts +0 -4
- package/dist/cli.js +34 -493
- package/dist/engine.d.ts +33 -0
- package/dist/engine.js +138 -0
- package/dist/learnings.d.ts +15 -0
- package/dist/learnings.js +54 -0
- package/dist/llm.d.ts +18 -0
- package/dist/llm.js +66 -0
- package/dist/mcp.d.ts +132 -0
- package/dist/mcp.js +43 -0
- package/dist/skills.d.ts +5 -16
- package/dist/skills.js +91 -253
- package/dist/tui.d.ts +1 -0
- package/dist/tui.js +10 -0
- package/package.json +10 -6
- package/dist/claw/jit.d.ts +0 -5
- package/dist/claw/jit.js +0 -138
- package/dist/claw/management.d.ts +0 -3
- package/dist/claw/management.js +0 -107
- package/dist/commands/add.d.ts +0 -9
- package/dist/commands/add.js +0 -50
- package/dist/commands/git/commit.d.ts +0 -12
- package/dist/commands/git/commit.js +0 -98
- package/dist/commands/git/status.d.ts +0 -6
- package/dist/commands/git/status.js +0 -42
- package/dist/commands/index.d.ts +0 -16
- package/dist/commands/index.js +0 -377
- package/dist/commands/mcp/status.d.ts +0 -6
- package/dist/commands/mcp/status.js +0 -31
- package/dist/commands/swarm.d.ts +0 -36
- package/dist/commands/swarm.js +0 -236
- package/dist/commands.d.ts +0 -32
- package/dist/commands.js +0 -427
- package/dist/context.d.ts +0 -116
- package/dist/context.js +0 -337
- package/dist/index.d.ts +0 -6
- package/dist/index.js +0 -109
- package/dist/lib/agent.d.ts +0 -99
- package/dist/lib/agent.js +0 -313
- package/dist/lib/editor.d.ts +0 -74
- package/dist/lib/editor.js +0 -441
- package/dist/lib/git.d.ts +0 -164
- package/dist/lib/git.js +0 -356
- package/dist/lib/shim.d.ts +0 -4
- package/dist/lib/shim.js +0 -30
- package/dist/lib/ui.d.ts +0 -159
- package/dist/lib/ui.js +0 -277
- package/dist/mcp/client.d.ts +0 -22
- package/dist/mcp/client.js +0 -81
- package/dist/mcp/manager.d.ts +0 -186
- package/dist/mcp/manager.js +0 -446
- package/dist/prompts/provider.d.ts +0 -22
- package/dist/prompts/provider.js +0 -79
- package/dist/providers/index.d.ts +0 -31
- package/dist/providers/index.js +0 -93
- package/dist/providers/multi.d.ts +0 -12
- package/dist/providers/multi.js +0 -28
- package/dist/registry.d.ts +0 -29
- package/dist/registry.js +0 -443
- package/dist/repoMap.d.ts +0 -5
- package/dist/repoMap.js +0 -79
- package/dist/router.d.ts +0 -41
- package/dist/router.js +0 -118
- package/dist/swarm/coordinator.d.ts +0 -86
- package/dist/swarm/coordinator.js +0 -257
- package/dist/swarm/index.d.ts +0 -28
- package/dist/swarm/index.js +0 -29
- package/dist/swarm/task.d.ts +0 -104
- package/dist/swarm/task.js +0 -221
- package/dist/swarm/types.d.ts +0 -132
- package/dist/swarm/types.js +0 -37
- package/dist/swarm/worker.d.ts +0 -109
- package/dist/swarm/worker.js +0 -369
- package/dist/tools/analyzeFile.d.ts +0 -16
- package/dist/tools/analyzeFile.js +0 -43
- package/dist/tools/analyze_file.d.ts +0 -16
- package/dist/tools/analyze_file.js +0 -43
- package/dist/tools/clawBrain.d.ts +0 -23
- package/dist/tools/clawBrain.js +0 -136
- package/dist/tools/claw_brain.d.ts +0 -23
- package/dist/tools/claw_brain.js +0 -139
- package/dist/tools/deleteFile.d.ts +0 -19
- package/dist/tools/deleteFile.js +0 -36
- package/dist/tools/delete_file.d.ts +0 -19
- package/dist/tools/delete_file.js +0 -36
- package/dist/tools/fileOps.d.ts +0 -22
- package/dist/tools/fileOps.js +0 -43
- package/dist/tools/file_ops.d.ts +0 -22
- package/dist/tools/file_ops.js +0 -43
- package/dist/tools/git.d.ts +0 -40
- package/dist/tools/git.js +0 -236
- package/dist/tools/glob.d.ts +0 -34
- package/dist/tools/glob.js +0 -165
- package/dist/tools/grep.d.ts +0 -53
- package/dist/tools/grep.js +0 -296
- package/dist/tools/linter.d.ts +0 -35
- package/dist/tools/linter.js +0 -407
- package/dist/tools/listDir.d.ts +0 -29
- package/dist/tools/listDir.js +0 -50
- package/dist/tools/list_dir.d.ts +0 -29
- package/dist/tools/list_dir.js +0 -50
- package/dist/tools/memory.d.ts +0 -34
- package/dist/tools/memory.js +0 -215
- package/dist/tools/organizer.d.ts +0 -1
- package/dist/tools/organizer.js +0 -65
- package/dist/tools/readFiles.d.ts +0 -25
- package/dist/tools/readFiles.js +0 -31
- package/dist/tools/read_files.d.ts +0 -25
- package/dist/tools/read_files.js +0 -31
- package/dist/tools/reloadTools.d.ts +0 -11
- package/dist/tools/reloadTools.js +0 -22
- package/dist/tools/reload_tools.d.ts +0 -11
- package/dist/tools/reload_tools.js +0 -22
- package/dist/tools/runCommand.d.ts +0 -32
- package/dist/tools/runCommand.js +0 -79
- package/dist/tools/run_command.d.ts +0 -32
- package/dist/tools/run_command.js +0 -103
- package/dist/tools/scheduler.d.ts +0 -25
- package/dist/tools/scheduler.js +0 -65
- package/dist/tools/scraper.d.ts +0 -31
- package/dist/tools/scraper.js +0 -211
- package/dist/tools/writeFiles.d.ts +0 -63
- package/dist/tools/writeFiles.js +0 -87
- package/dist/tools/write_files.d.ts +0 -84
- package/dist/tools/write_files.js +0 -91
- package/dist/tools/write_to_file.d.ts +0 -15
- package/dist/tools/write_to_file.js +0 -21
- package/dist/ui/server.d.ts +0 -5
- package/dist/ui/server.js +0 -74
- package/dist/watcher.d.ts +0 -35
- package/dist/watcher.js +0 -164
- /package/{docs/assets → assets}/logo.jpeg +0 -0
package/dist/tools/glob.d.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Glob Tool - Find files matching patterns
|
|
3
|
-
* Uses fast-glob for reliable glob matching (with fallback)
|
|
4
|
-
*/
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
import type { Tool } from '../registry.js';
|
|
7
|
-
export declare const inputSchema: z.ZodObject<{
|
|
8
|
-
pattern: z.ZodString;
|
|
9
|
-
cwd: z.ZodOptional<z.ZodString>;
|
|
10
|
-
maxResults: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
11
|
-
includeDirectories: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
12
|
-
ignore: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
13
|
-
}, "strip", z.ZodTypeAny, {
|
|
14
|
-
pattern: string;
|
|
15
|
-
maxResults: number;
|
|
16
|
-
includeDirectories: boolean;
|
|
17
|
-
ignore?: string[] | undefined;
|
|
18
|
-
cwd?: string | undefined;
|
|
19
|
-
}, {
|
|
20
|
-
pattern: string;
|
|
21
|
-
ignore?: string[] | undefined;
|
|
22
|
-
cwd?: string | undefined;
|
|
23
|
-
maxResults?: number | undefined;
|
|
24
|
-
includeDirectories?: boolean | undefined;
|
|
25
|
-
}>;
|
|
26
|
-
type GlobInput = z.infer<typeof inputSchema>;
|
|
27
|
-
export declare function execute(input: GlobInput): Promise<{
|
|
28
|
-
pattern: string;
|
|
29
|
-
matches: string[];
|
|
30
|
-
count: number;
|
|
31
|
-
truncated: boolean;
|
|
32
|
-
}>;
|
|
33
|
-
export declare const tool: Tool;
|
|
34
|
-
export {};
|
package/dist/tools/glob.js
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Glob Tool - Find files matching patterns
|
|
3
|
-
* Uses fast-glob for reliable glob matching (with fallback)
|
|
4
|
-
*/
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
import { readdir } from 'fs/promises';
|
|
7
|
-
import { join, relative } from 'path';
|
|
8
|
-
// Input schema
|
|
9
|
-
export const inputSchema = z.object({
|
|
10
|
-
pattern: z.string().describe('Glob pattern to match files (e.g., "**/*.ts", "src/**/*.js")'),
|
|
11
|
-
cwd: z.string().optional().describe('Working directory to search in'),
|
|
12
|
-
maxResults: z.number().optional().default(100).describe('Maximum number of results'),
|
|
13
|
-
includeDirectories: z.boolean().optional().default(false).describe('Include directories in results'),
|
|
14
|
-
ignore: z.array(z.string()).optional().describe('Patterns to ignore'),
|
|
15
|
-
});
|
|
16
|
-
// Default ignore patterns
|
|
17
|
-
const DEFAULT_IGNORE = [
|
|
18
|
-
'**/node_modules/**',
|
|
19
|
-
'**/.git/**',
|
|
20
|
-
'**/.svn/**',
|
|
21
|
-
'**/.hg/**',
|
|
22
|
-
'**/dist/**',
|
|
23
|
-
'**/build/**',
|
|
24
|
-
'**/coverage/**',
|
|
25
|
-
'**/.next/**',
|
|
26
|
-
'**/.nuxt/**',
|
|
27
|
-
'**/__pycache__/**',
|
|
28
|
-
'**/.pytest_cache/**',
|
|
29
|
-
'**/venv/**',
|
|
30
|
-
'**/.venv/**',
|
|
31
|
-
'**/env/**',
|
|
32
|
-
'**/.env',
|
|
33
|
-
'**/.idea/**',
|
|
34
|
-
'**/.vscode/**',
|
|
35
|
-
'**/*.pyc',
|
|
36
|
-
'**/*.pyo',
|
|
37
|
-
'**/.DS_Store',
|
|
38
|
-
'**/Thumbs.db',
|
|
39
|
-
];
|
|
40
|
-
// Simple ignore patterns for fallback (directory names)
|
|
41
|
-
const FALLBACK_IGNORE = [
|
|
42
|
-
'node_modules', '.git', '.svn', '.hg', 'dist', 'build', 'coverage',
|
|
43
|
-
'.next', '.nuxt', '__pycache__', '.pytest_cache', 'venv', '.venv',
|
|
44
|
-
'env', '.idea', '.vscode',
|
|
45
|
-
];
|
|
46
|
-
// Cached fast-glob instance (typed as any to handle missing module)
|
|
47
|
-
let fastGlob = null;
|
|
48
|
-
let fgLoaded = false;
|
|
49
|
-
async function loadFastGlob() {
|
|
50
|
-
if (!fgLoaded) {
|
|
51
|
-
fgLoaded = true;
|
|
52
|
-
try {
|
|
53
|
-
fastGlob = (await import('fast-glob')).default;
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
// fast-glob not installed
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return fastGlob;
|
|
60
|
-
}
|
|
61
|
-
// Convert glob pattern to regex (for fallback)
|
|
62
|
-
function globToRegex(pattern) {
|
|
63
|
-
let prefix = '';
|
|
64
|
-
let workPattern = pattern;
|
|
65
|
-
if (pattern.startsWith('**/')) {
|
|
66
|
-
prefix = '(?:^|.*/)?';
|
|
67
|
-
workPattern = pattern.slice(3);
|
|
68
|
-
}
|
|
69
|
-
let regex = workPattern
|
|
70
|
-
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
71
|
-
.replace(/\*\*/g, '.*')
|
|
72
|
-
.replace(/\*/g, '[^/]*')
|
|
73
|
-
.replace(/\?/g, '[^/]');
|
|
74
|
-
return new RegExp(`${prefix}${regex}$`, 'i');
|
|
75
|
-
}
|
|
76
|
-
// Fallback: recursive file finder
|
|
77
|
-
async function findFilesFallback(dir, pattern, options, results = []) {
|
|
78
|
-
if (results.length >= options.maxResults)
|
|
79
|
-
return results;
|
|
80
|
-
try {
|
|
81
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
82
|
-
for (const entry of entries) {
|
|
83
|
-
if (results.length >= options.maxResults)
|
|
84
|
-
break;
|
|
85
|
-
// Check ignore
|
|
86
|
-
if (options.ignorePatterns.includes(entry.name))
|
|
87
|
-
continue;
|
|
88
|
-
if (entry.name.startsWith('.'))
|
|
89
|
-
continue;
|
|
90
|
-
const fullPath = join(dir, entry.name);
|
|
91
|
-
const relativePath = relative(options.baseDir, fullPath);
|
|
92
|
-
if (entry.isDirectory()) {
|
|
93
|
-
if (options.includeDirectories && pattern.test(relativePath)) {
|
|
94
|
-
results.push(relativePath);
|
|
95
|
-
}
|
|
96
|
-
await findFilesFallback(fullPath, pattern, options, results);
|
|
97
|
-
}
|
|
98
|
-
else if (entry.isFile()) {
|
|
99
|
-
if (pattern.test(relativePath)) {
|
|
100
|
-
results.push(relativePath);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
catch {
|
|
106
|
-
// Ignore permission errors
|
|
107
|
-
}
|
|
108
|
-
return results;
|
|
109
|
-
}
|
|
110
|
-
// Execute glob search
|
|
111
|
-
export async function execute(input) {
|
|
112
|
-
const { pattern, cwd = process.cwd(), maxResults, includeDirectories, ignore = [], } = inputSchema.parse(input);
|
|
113
|
-
const fg = await loadFastGlob();
|
|
114
|
-
if (fg) {
|
|
115
|
-
// Use fast-glob (preferred)
|
|
116
|
-
try {
|
|
117
|
-
const ignorePatterns = [...DEFAULT_IGNORE, ...ignore];
|
|
118
|
-
const matches = await fg(pattern, {
|
|
119
|
-
cwd,
|
|
120
|
-
ignore: ignorePatterns,
|
|
121
|
-
onlyFiles: !includeDirectories,
|
|
122
|
-
onlyDirectories: false,
|
|
123
|
-
dot: false,
|
|
124
|
-
absolute: false,
|
|
125
|
-
suppressErrors: true,
|
|
126
|
-
});
|
|
127
|
-
const sorted = matches.sort();
|
|
128
|
-
const truncated = sorted.length > maxResults;
|
|
129
|
-
const finalMatches = sorted.slice(0, maxResults);
|
|
130
|
-
return {
|
|
131
|
-
pattern,
|
|
132
|
-
matches: finalMatches,
|
|
133
|
-
count: finalMatches.length,
|
|
134
|
-
truncated,
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
catch {
|
|
138
|
-
// Fall through to fallback
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
// Fallback implementation
|
|
142
|
-
const regex = globToRegex(pattern);
|
|
143
|
-
const matches = await findFilesFallback(cwd, regex, {
|
|
144
|
-
maxResults: maxResults + 1,
|
|
145
|
-
includeDirectories,
|
|
146
|
-
ignorePatterns: FALLBACK_IGNORE,
|
|
147
|
-
baseDir: cwd,
|
|
148
|
-
});
|
|
149
|
-
const truncated = matches.length > maxResults;
|
|
150
|
-
const finalMatches = matches.slice(0, maxResults).sort();
|
|
151
|
-
return {
|
|
152
|
-
pattern,
|
|
153
|
-
matches: finalMatches,
|
|
154
|
-
count: finalMatches.length,
|
|
155
|
-
truncated,
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
// Tool definition
|
|
159
|
-
export const tool = {
|
|
160
|
-
name: 'glob',
|
|
161
|
-
description: 'Find files matching a glob pattern. Supports ** for recursive matching, * for wildcards, and ? for single characters.',
|
|
162
|
-
inputSchema,
|
|
163
|
-
permission: 'read',
|
|
164
|
-
execute: async (args) => execute(args),
|
|
165
|
-
};
|
package/dist/tools/grep.d.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Grep Tool - Search for patterns in files
|
|
3
|
-
* Uses ripgrep when available, fast-glob + JS regex as fallback
|
|
4
|
-
*/
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
import type { Tool } from '../registry.js';
|
|
7
|
-
export declare const inputSchema: z.ZodObject<{
|
|
8
|
-
pattern: z.ZodString;
|
|
9
|
-
path: z.ZodOptional<z.ZodString>;
|
|
10
|
-
glob: z.ZodOptional<z.ZodString>;
|
|
11
|
-
ignoreCase: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
12
|
-
maxResults: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
13
|
-
contextLines: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
14
|
-
filesOnly: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
15
|
-
includeHidden: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
16
|
-
}, "strip", z.ZodTypeAny, {
|
|
17
|
-
pattern: string;
|
|
18
|
-
ignoreCase: boolean;
|
|
19
|
-
maxResults: number;
|
|
20
|
-
contextLines: number;
|
|
21
|
-
filesOnly: boolean;
|
|
22
|
-
includeHidden: boolean;
|
|
23
|
-
path?: string | undefined;
|
|
24
|
-
glob?: string | undefined;
|
|
25
|
-
}, {
|
|
26
|
-
pattern: string;
|
|
27
|
-
path?: string | undefined;
|
|
28
|
-
glob?: string | undefined;
|
|
29
|
-
ignoreCase?: boolean | undefined;
|
|
30
|
-
maxResults?: number | undefined;
|
|
31
|
-
contextLines?: number | undefined;
|
|
32
|
-
filesOnly?: boolean | undefined;
|
|
33
|
-
includeHidden?: boolean | undefined;
|
|
34
|
-
}>;
|
|
35
|
-
type GrepInput = z.infer<typeof inputSchema>;
|
|
36
|
-
interface GrepMatch {
|
|
37
|
-
file: string;
|
|
38
|
-
line: number;
|
|
39
|
-
column: number;
|
|
40
|
-
text: string;
|
|
41
|
-
match: string;
|
|
42
|
-
contextBefore?: string[];
|
|
43
|
-
contextAfter?: string[];
|
|
44
|
-
}
|
|
45
|
-
export declare function execute(input: GrepInput): Promise<{
|
|
46
|
-
pattern: string;
|
|
47
|
-
matches: GrepMatch[];
|
|
48
|
-
files: string[];
|
|
49
|
-
count: number;
|
|
50
|
-
truncated: boolean;
|
|
51
|
-
}>;
|
|
52
|
-
export declare const tool: Tool;
|
|
53
|
-
export {};
|
package/dist/tools/grep.js
DELETED
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Grep Tool - Search for patterns in files
|
|
3
|
-
* Uses ripgrep when available, fast-glob + JS regex as fallback
|
|
4
|
-
*/
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
import { readFile, readdir, stat } from 'fs/promises';
|
|
7
|
-
import { join, relative, extname } from 'path';
|
|
8
|
-
import { spawnSync } from 'child_process';
|
|
9
|
-
// Cached fast-glob instance (typed as any to handle missing module)
|
|
10
|
-
let fg = null;
|
|
11
|
-
let fgLoaded = false;
|
|
12
|
-
async function loadFastGlob() {
|
|
13
|
-
if (!fgLoaded) {
|
|
14
|
-
fgLoaded = true;
|
|
15
|
-
try {
|
|
16
|
-
fg = (await import('fast-glob')).default;
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
// fast-glob not installed
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
return fg;
|
|
23
|
-
}
|
|
24
|
-
// Input schema
|
|
25
|
-
export const inputSchema = z.object({
|
|
26
|
-
pattern: z.string().describe('Regular expression pattern to search for'),
|
|
27
|
-
path: z.string().optional().describe('File or directory to search in'),
|
|
28
|
-
glob: z.string().optional().describe('File pattern to filter (e.g., "*.ts")'),
|
|
29
|
-
ignoreCase: z.boolean().optional().default(false).describe('Case insensitive search'),
|
|
30
|
-
maxResults: z.number().optional().default(100).describe('Maximum number of matches'),
|
|
31
|
-
contextLines: z.number().optional().default(0).describe('Number of context lines before and after'),
|
|
32
|
-
filesOnly: z.boolean().optional().default(false).describe('Only return file names, not matches'),
|
|
33
|
-
includeHidden: z.boolean().optional().default(false).describe('Include hidden files'),
|
|
34
|
-
});
|
|
35
|
-
// Default ignore patterns
|
|
36
|
-
const DEFAULT_IGNORE = [
|
|
37
|
-
'node_modules',
|
|
38
|
-
'.git',
|
|
39
|
-
'dist',
|
|
40
|
-
'build',
|
|
41
|
-
'coverage',
|
|
42
|
-
'__pycache__',
|
|
43
|
-
'.pytest_cache',
|
|
44
|
-
'venv',
|
|
45
|
-
'.venv',
|
|
46
|
-
];
|
|
47
|
-
// Text file glob patterns for searching
|
|
48
|
-
const TEXT_FILE_PATTERNS = [
|
|
49
|
-
'**/*.{txt,md,json,yaml,yml,toml,xml,html,htm}',
|
|
50
|
-
'**/*.{css,scss,less,js,jsx,ts,tsx,vue,svelte}',
|
|
51
|
-
'**/*.{py,rb,php,java,c,cpp,h,hpp,cs,go,rs,swift,kt,scala}',
|
|
52
|
-
'**/*.{sh,bash,zsh,fish,sql,graphql,prisma}',
|
|
53
|
-
'**/{Makefile,Dockerfile,Containerfile,.gitignore,.dockerignore,.env}',
|
|
54
|
-
'**/.{editorconfig,eslintrc,prettierrc,babelrc}*',
|
|
55
|
-
];
|
|
56
|
-
// Search a single file
|
|
57
|
-
async function searchFile(filePath, regex, contextLines) {
|
|
58
|
-
const matches = [];
|
|
59
|
-
try {
|
|
60
|
-
const content = await readFile(filePath, 'utf-8');
|
|
61
|
-
const lines = content.split('\n');
|
|
62
|
-
for (let i = 0; i < lines.length; i++) {
|
|
63
|
-
const line = lines[i];
|
|
64
|
-
let match;
|
|
65
|
-
// Reset regex for global searches
|
|
66
|
-
regex.lastIndex = 0;
|
|
67
|
-
while ((match = regex.exec(line)) !== null) {
|
|
68
|
-
const result = {
|
|
69
|
-
file: filePath,
|
|
70
|
-
line: i + 1,
|
|
71
|
-
column: match.index + 1,
|
|
72
|
-
text: line,
|
|
73
|
-
match: match[0],
|
|
74
|
-
};
|
|
75
|
-
if (contextLines > 0) {
|
|
76
|
-
result.contextBefore = lines.slice(Math.max(0, i - contextLines), i);
|
|
77
|
-
result.contextAfter = lines.slice(i + 1, Math.min(lines.length, i + 1 + contextLines));
|
|
78
|
-
}
|
|
79
|
-
matches.push(result);
|
|
80
|
-
// Don't find overlapping matches on same line
|
|
81
|
-
if (!regex.global)
|
|
82
|
-
break;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
catch {
|
|
87
|
-
// Ignore files that can't be read
|
|
88
|
-
}
|
|
89
|
-
return matches;
|
|
90
|
-
}
|
|
91
|
-
// Text file extensions for fallback matching
|
|
92
|
-
const TEXT_EXTENSIONS = new Set([
|
|
93
|
-
'.txt', '.md', '.json', '.yaml', '.yml', '.toml', '.xml', '.html', '.htm',
|
|
94
|
-
'.css', '.scss', '.less', '.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte',
|
|
95
|
-
'.py', '.rb', '.php', '.java', '.c', '.cpp', '.h', '.hpp', '.cs', '.go',
|
|
96
|
-
'.rs', '.swift', '.kt', '.scala', '.sh', '.bash', '.zsh', '.fish',
|
|
97
|
-
'.sql', '.graphql', '.prisma', '',
|
|
98
|
-
]);
|
|
99
|
-
// Fallback: recursive directory search
|
|
100
|
-
async function searchDirectoryFallback(dir, regex, options, results = []) {
|
|
101
|
-
if (results.length >= options.maxResults)
|
|
102
|
-
return results;
|
|
103
|
-
try {
|
|
104
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
105
|
-
for (const entry of entries) {
|
|
106
|
-
if (results.length >= options.maxResults)
|
|
107
|
-
break;
|
|
108
|
-
const fullPath = join(dir, entry.name);
|
|
109
|
-
const relativePath = relative(options.baseDir, fullPath);
|
|
110
|
-
if (entry.isDirectory()) {
|
|
111
|
-
if (DEFAULT_IGNORE.includes(entry.name))
|
|
112
|
-
continue;
|
|
113
|
-
if (!options.includeHidden && entry.name.startsWith('.'))
|
|
114
|
-
continue;
|
|
115
|
-
await searchDirectoryFallback(fullPath, regex, options, results);
|
|
116
|
-
}
|
|
117
|
-
else if (entry.isFile()) {
|
|
118
|
-
const ext = extname(entry.name).toLowerCase();
|
|
119
|
-
if (!TEXT_EXTENSIONS.has(ext))
|
|
120
|
-
continue;
|
|
121
|
-
if (!options.includeHidden && entry.name.startsWith('.'))
|
|
122
|
-
continue;
|
|
123
|
-
const fileMatches = await searchFile(fullPath, regex, options.contextLines);
|
|
124
|
-
for (const match of fileMatches) {
|
|
125
|
-
if (results.length >= options.maxResults)
|
|
126
|
-
break;
|
|
127
|
-
match.file = relativePath;
|
|
128
|
-
results.push(match);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
catch {
|
|
134
|
-
// Ignore permission errors
|
|
135
|
-
}
|
|
136
|
-
return results;
|
|
137
|
-
}
|
|
138
|
-
// Search directory using fast-glob for file discovery (with fallback)
|
|
139
|
-
async function searchDirectory(dir, regex, options) {
|
|
140
|
-
const results = [];
|
|
141
|
-
const fastGlob = await loadFastGlob();
|
|
142
|
-
if (fastGlob) {
|
|
143
|
-
try {
|
|
144
|
-
// Build ignore patterns
|
|
145
|
-
const ignorePatterns = DEFAULT_IGNORE.map(d => `**/${d}/**`);
|
|
146
|
-
// Use user's glob pattern or default to text files
|
|
147
|
-
const patterns = options.glob ? [options.glob] : TEXT_FILE_PATTERNS;
|
|
148
|
-
// Find files using fast-glob
|
|
149
|
-
const files = await fastGlob(patterns, {
|
|
150
|
-
cwd: dir,
|
|
151
|
-
ignore: ignorePatterns,
|
|
152
|
-
dot: options.includeHidden,
|
|
153
|
-
absolute: true,
|
|
154
|
-
suppressErrors: true,
|
|
155
|
-
});
|
|
156
|
-
// Search each file
|
|
157
|
-
for (const fullPath of files) {
|
|
158
|
-
if (results.length >= options.maxResults)
|
|
159
|
-
break;
|
|
160
|
-
const fileMatches = await searchFile(fullPath, regex, options.contextLines);
|
|
161
|
-
for (const match of fileMatches) {
|
|
162
|
-
if (results.length >= options.maxResults)
|
|
163
|
-
break;
|
|
164
|
-
match.file = relative(options.baseDir, fullPath);
|
|
165
|
-
results.push(match);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return results;
|
|
169
|
-
}
|
|
170
|
-
catch {
|
|
171
|
-
// Fall through to fallback
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
// Fallback to recursive directory search
|
|
175
|
-
return searchDirectoryFallback(dir, regex, options, results);
|
|
176
|
-
}
|
|
177
|
-
// Try to use ripgrep if available
|
|
178
|
-
function tryRipgrep(input) {
|
|
179
|
-
try {
|
|
180
|
-
// Request one more than maxResults to detect truncation
|
|
181
|
-
const maxResults = input.maxResults || 100;
|
|
182
|
-
const args = [
|
|
183
|
-
'--json',
|
|
184
|
-
'--max-count', String(maxResults + 1),
|
|
185
|
-
];
|
|
186
|
-
// Ignore common directories by default (matching DEFAULT_IGNORE)
|
|
187
|
-
for (const ignore of DEFAULT_IGNORE) {
|
|
188
|
-
args.push('-g', `!${ignore}`);
|
|
189
|
-
args.push('-g', `!${ignore}/**`);
|
|
190
|
-
}
|
|
191
|
-
if (input.ignoreCase) {
|
|
192
|
-
args.push('-i');
|
|
193
|
-
}
|
|
194
|
-
if (input.glob) {
|
|
195
|
-
args.push('-g', input.glob);
|
|
196
|
-
}
|
|
197
|
-
if (input.contextLines && input.contextLines > 0) {
|
|
198
|
-
args.push('-C', String(input.contextLines));
|
|
199
|
-
}
|
|
200
|
-
args.push(input.pattern);
|
|
201
|
-
if (input.path) {
|
|
202
|
-
args.push(input.path);
|
|
203
|
-
}
|
|
204
|
-
const result = spawnSync('rg', args, {
|
|
205
|
-
encoding: 'utf-8',
|
|
206
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
207
|
-
timeout: 30000,
|
|
208
|
-
});
|
|
209
|
-
if (result.error) {
|
|
210
|
-
return null; // ripgrep not available
|
|
211
|
-
}
|
|
212
|
-
const matches = [];
|
|
213
|
-
const lines = result.stdout.split('\n').filter(Boolean);
|
|
214
|
-
for (const line of lines) {
|
|
215
|
-
try {
|
|
216
|
-
const json = JSON.parse(line);
|
|
217
|
-
if (json.type === 'match') {
|
|
218
|
-
// Extract all submatches, not just the first one
|
|
219
|
-
const submatches = json.data.submatches || [];
|
|
220
|
-
for (const submatch of submatches) {
|
|
221
|
-
matches.push({
|
|
222
|
-
file: json.data.path.text,
|
|
223
|
-
line: json.data.line_number,
|
|
224
|
-
column: (submatch.start || 0) + 1,
|
|
225
|
-
text: json.data.lines.text.replace(/\n$/, ''),
|
|
226
|
-
match: submatch.match?.text || '',
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
catch {
|
|
232
|
-
// Skip invalid JSON lines
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
return matches;
|
|
236
|
-
}
|
|
237
|
-
catch {
|
|
238
|
-
return null;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
// Execute grep search
|
|
242
|
-
export async function execute(input) {
|
|
243
|
-
const { pattern, path = process.cwd(), glob, ignoreCase, maxResults, contextLines, filesOnly, includeHidden, } = inputSchema.parse(input);
|
|
244
|
-
// Try ripgrep first (but skip if context lines requested - ripgrep parsing doesn't support it)
|
|
245
|
-
if (!contextLines || contextLines === 0) {
|
|
246
|
-
const rgMatches = tryRipgrep(input);
|
|
247
|
-
if (rgMatches !== null && rgMatches.length > 0) {
|
|
248
|
-
const uniqueFiles = [...new Set(rgMatches.map(m => m.file))];
|
|
249
|
-
return {
|
|
250
|
-
pattern,
|
|
251
|
-
matches: filesOnly ? [] : rgMatches.slice(0, maxResults),
|
|
252
|
-
files: uniqueFiles,
|
|
253
|
-
count: rgMatches.length,
|
|
254
|
-
truncated: rgMatches.length > maxResults,
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
// Fall back to JavaScript implementation
|
|
259
|
-
const flags = ignoreCase ? 'gi' : 'g';
|
|
260
|
-
const regex = new RegExp(pattern, flags);
|
|
261
|
-
let matches;
|
|
262
|
-
const pathStat = await stat(path).catch(() => null);
|
|
263
|
-
if (pathStat?.isFile()) {
|
|
264
|
-
matches = await searchFile(path, regex, contextLines);
|
|
265
|
-
}
|
|
266
|
-
else if (pathStat?.isDirectory()) {
|
|
267
|
-
matches = await searchDirectory(path, regex, {
|
|
268
|
-
glob,
|
|
269
|
-
contextLines,
|
|
270
|
-
maxResults: maxResults + 1,
|
|
271
|
-
includeHidden,
|
|
272
|
-
baseDir: path,
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
else {
|
|
276
|
-
matches = [];
|
|
277
|
-
}
|
|
278
|
-
const truncated = matches.length > maxResults;
|
|
279
|
-
const finalMatches = matches.slice(0, maxResults);
|
|
280
|
-
const uniqueFiles = [...new Set(finalMatches.map(m => m.file))];
|
|
281
|
-
return {
|
|
282
|
-
pattern,
|
|
283
|
-
matches: filesOnly ? [] : finalMatches,
|
|
284
|
-
files: uniqueFiles,
|
|
285
|
-
count: finalMatches.length,
|
|
286
|
-
truncated,
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
// Tool definition
|
|
290
|
-
export const tool = {
|
|
291
|
-
name: 'grep',
|
|
292
|
-
description: 'Search for a pattern in files using regular expressions. Uses ripgrep if available for better performance.',
|
|
293
|
-
inputSchema,
|
|
294
|
-
permission: 'read',
|
|
295
|
-
execute: async (args) => execute(args),
|
|
296
|
-
};
|
package/dist/tools/linter.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Linter Tool - Checks code for syntax errors
|
|
3
|
-
* Based on Aider's linter.py
|
|
4
|
-
*/
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
import type { Tool } from '../registry.js';
|
|
7
|
-
export declare const inputSchema: z.ZodObject<{
|
|
8
|
-
path: z.ZodString;
|
|
9
|
-
fix: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
10
|
-
}, "strip", z.ZodTypeAny, {
|
|
11
|
-
path: string;
|
|
12
|
-
fix: boolean;
|
|
13
|
-
}, {
|
|
14
|
-
path: string;
|
|
15
|
-
fix?: boolean | undefined;
|
|
16
|
-
}>;
|
|
17
|
-
type LinterInput = z.infer<typeof inputSchema>;
|
|
18
|
-
interface LintResult {
|
|
19
|
-
file: string;
|
|
20
|
-
language: string;
|
|
21
|
-
errors: LintError[];
|
|
22
|
-
warnings: LintError[];
|
|
23
|
-
passed: boolean;
|
|
24
|
-
output: string;
|
|
25
|
-
}
|
|
26
|
-
interface LintError {
|
|
27
|
-
line: number;
|
|
28
|
-
column?: number;
|
|
29
|
-
message: string;
|
|
30
|
-
rule?: string;
|
|
31
|
-
severity: 'error' | 'warning';
|
|
32
|
-
}
|
|
33
|
-
export declare function execute(input: LinterInput): Promise<LintResult>;
|
|
34
|
-
export declare const tool: Tool;
|
|
35
|
-
export {};
|