@stan-chen/simple-cli 0.2.3 → 0.2.5
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 +73 -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 +15 -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
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool: runCommand
|
|
3
|
-
* Execute shell commands in a sandboxed environment
|
|
4
|
-
*/
|
|
5
|
-
import { spawn } from 'child_process';
|
|
6
|
-
import { z } from 'zod';
|
|
7
|
-
export const name = 'run_command';
|
|
8
|
-
export const description = 'Execute a shell command with timeout and environment restrictions';
|
|
9
|
-
export const permission = 'execute';
|
|
10
|
-
export const schema = z.object({
|
|
11
|
-
command: z.string().describe('The shell command to execute'),
|
|
12
|
-
cwd: z.string().optional().describe('Working directory for the command'),
|
|
13
|
-
timeout: z.number().optional().describe('Timeout in milliseconds (default: 30000)'),
|
|
14
|
-
env: z.record(z.string()).optional().describe('Additional environment variables')
|
|
15
|
-
});
|
|
16
|
-
// Restricted environment - remove sensitive variables
|
|
17
|
-
const createSafeEnv = (additionalEnv) => {
|
|
18
|
-
// Start from a minimal safe copy to avoid leaking secrets
|
|
19
|
-
const rawEnv = { ...process.env };
|
|
20
|
-
const sensitivePattern = /(API_KEY$|_KEY$|TOKEN|SECRET|PASSWORD|VLM_API|OPENAI_API|GEMINI_API|NPM_ACCESS_TOKEN)/i;
|
|
21
|
-
const env = {};
|
|
22
|
-
for (const [k, v] of Object.entries(rawEnv)) {
|
|
23
|
-
if (!k)
|
|
24
|
-
continue;
|
|
25
|
-
if (sensitivePattern.test(k))
|
|
26
|
-
continue; // skip sensitive keys
|
|
27
|
-
env[k] = v;
|
|
28
|
-
}
|
|
29
|
-
// Add any additional env vars provided explicitly, but still filter them
|
|
30
|
-
if (additionalEnv) {
|
|
31
|
-
for (const [k, v] of Object.entries(additionalEnv)) {
|
|
32
|
-
if (!k)
|
|
33
|
-
continue;
|
|
34
|
-
if (sensitivePattern.test(k))
|
|
35
|
-
continue; // never allow sensitive keys
|
|
36
|
-
env[k] = v;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return env;
|
|
40
|
-
};
|
|
41
|
-
export const execute = async (args) => {
|
|
42
|
-
const parsed = schema.parse(args);
|
|
43
|
-
const timeout = parsed.timeout || 30000;
|
|
44
|
-
const env = createSafeEnv(parsed.env);
|
|
45
|
-
return new Promise((resolve) => {
|
|
46
|
-
let stdout = '';
|
|
47
|
-
let stderr = '';
|
|
48
|
-
let timedOut = false;
|
|
49
|
-
// Quick heuristic: detect obviously unbalanced quotes and treat as syntax error
|
|
50
|
-
const singleQuotes = (parsed.command.match(/'/g) || []).length;
|
|
51
|
-
const doubleQuotes = (parsed.command.match(/"/g) || []).length;
|
|
52
|
-
if (singleQuotes % 2 !== 0 || doubleQuotes % 2 !== 0) {
|
|
53
|
-
return resolve({ exitCode: 1, stdout: '', stderr: 'Invalid shell syntax (unbalanced quotes)', timedOut: false });
|
|
54
|
-
}
|
|
55
|
-
const child = spawn(parsed.command, {
|
|
56
|
-
shell: true,
|
|
57
|
-
cwd: parsed.cwd || process.cwd(),
|
|
58
|
-
env,
|
|
59
|
-
stdio: ['ignore', 'pipe', 'pipe']
|
|
60
|
-
});
|
|
61
|
-
const timer = setTimeout(() => {
|
|
62
|
-
timedOut = true;
|
|
63
|
-
child.kill('SIGTERM');
|
|
64
|
-
setTimeout(() => child.kill('SIGKILL'), 1000);
|
|
65
|
-
}, timeout);
|
|
66
|
-
child.stdout?.on('data', (data) => {
|
|
67
|
-
stdout += data.toString();
|
|
68
|
-
// Limit output size
|
|
69
|
-
if (stdout.length > 100000) {
|
|
70
|
-
stdout = stdout.slice(0, 100000) + '\n... (output truncated)';
|
|
71
|
-
child.kill('SIGTERM');
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
child.stderr?.on('data', (data) => {
|
|
75
|
-
stderr += data.toString();
|
|
76
|
-
if (stderr.length > 50000) {
|
|
77
|
-
stderr = stderr.slice(0, 50000) + '\n... (output truncated)';
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
child.on('close', (code) => {
|
|
81
|
-
clearTimeout(timer);
|
|
82
|
-
// If command failed but provided no stderr, include the command for diagnostics
|
|
83
|
-
if ((code ?? 0) !== 0 && !stderr) {
|
|
84
|
-
stderr = `Command failed: ${parsed.command}`;
|
|
85
|
-
}
|
|
86
|
-
resolve({
|
|
87
|
-
exitCode: code ?? 1,
|
|
88
|
-
stdout: stdout.trim(),
|
|
89
|
-
stderr: stderr.trim(),
|
|
90
|
-
timedOut
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
child.on('error', (error) => {
|
|
94
|
-
clearTimeout(timer);
|
|
95
|
-
resolve({
|
|
96
|
-
exitCode: 1,
|
|
97
|
-
stdout: '',
|
|
98
|
-
stderr: error.message,
|
|
99
|
-
timedOut: false
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool: scheduler
|
|
3
|
-
* Manage Ghost Tasks (scheduled executions) using crontab or schtasks
|
|
4
|
-
*/
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
export declare const name = "scheduler";
|
|
7
|
-
export declare const description = "Schedule a task to run automatically at intervals. CRITICAL for fulfilling \"Every X minutes\" or \"Every day\" intents. Tasks run in --ghost mode.";
|
|
8
|
-
export declare const permission: "execute";
|
|
9
|
-
export declare const schema: z.ZodObject<{
|
|
10
|
-
intent: z.ZodString;
|
|
11
|
-
schedule: z.ZodString;
|
|
12
|
-
name: z.ZodOptional<z.ZodString>;
|
|
13
|
-
targetDir: z.ZodOptional<z.ZodString>;
|
|
14
|
-
}, "strip", z.ZodTypeAny, {
|
|
15
|
-
intent: string;
|
|
16
|
-
schedule: string;
|
|
17
|
-
name?: string | undefined;
|
|
18
|
-
targetDir?: string | undefined;
|
|
19
|
-
}, {
|
|
20
|
-
intent: string;
|
|
21
|
-
schedule: string;
|
|
22
|
-
name?: string | undefined;
|
|
23
|
-
targetDir?: string | undefined;
|
|
24
|
-
}>;
|
|
25
|
-
export declare const execute: (args: Record<string, unknown>) => Promise<string>;
|
package/dist/tools/scheduler.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool: scheduler
|
|
3
|
-
* Manage Ghost Tasks (scheduled executions) using crontab or schtasks
|
|
4
|
-
*/
|
|
5
|
-
import { execSync } from 'child_process';
|
|
6
|
-
import { platform } from 'os';
|
|
7
|
-
import { z } from 'zod';
|
|
8
|
-
import { resolve, join } from 'path';
|
|
9
|
-
export const name = 'scheduler';
|
|
10
|
-
export const description = 'Schedule a task to run automatically at intervals. CRITICAL for fulfilling "Every X minutes" or "Every day" intents. Tasks run in --ghost mode.';
|
|
11
|
-
export const permission = 'execute';
|
|
12
|
-
export const schema = z.object({
|
|
13
|
-
intent: z.string().describe('The task description/intent to execute'),
|
|
14
|
-
schedule: z.string().describe('Cron expression (e.g. "0 * * * *") or interval in minutes'),
|
|
15
|
-
name: z.string().optional().describe('Unique name for the task'),
|
|
16
|
-
targetDir: z.string().optional().describe('Working directory for the task')
|
|
17
|
-
});
|
|
18
|
-
export const execute = async (args) => {
|
|
19
|
-
const { intent, schedule, name: taskName, targetDir } = schema.parse(args);
|
|
20
|
-
const isWindows = platform() === 'win32';
|
|
21
|
-
const cwd = resolve(targetDir || process.cwd());
|
|
22
|
-
const id = taskName || `ghost-${Date.now()}`;
|
|
23
|
-
// Path to simple-cli. We assume it's installed globally or use the current one.
|
|
24
|
-
// For local dev, we use the absolute path to dist/cli.js
|
|
25
|
-
const cliPath = resolve(join(process.cwd(), 'dist', 'cli.js'));
|
|
26
|
-
const command = `node "${cliPath}" "${cwd}" -claw "${intent}" --ghost --yolo`;
|
|
27
|
-
try {
|
|
28
|
-
if (isWindows) {
|
|
29
|
-
const interval = parseInt(schedule) || 60;
|
|
30
|
-
const escapedCommand = command.replace(/"/g, '\\"');
|
|
31
|
-
try {
|
|
32
|
-
execSync(`schtasks /create /sc minute /mo ${interval} /tn "${id}" /tr "${escapedCommand}" /f`);
|
|
33
|
-
return `Task scheduled on Windows: ${id} Every ${interval} minutes.`;
|
|
34
|
-
}
|
|
35
|
-
catch (e) {
|
|
36
|
-
return `Error scheduling on Windows: ${e.message}`;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
try {
|
|
41
|
-
// Determine cron expression
|
|
42
|
-
let cronExpr = schedule;
|
|
43
|
-
if (!schedule.includes('*')) {
|
|
44
|
-
const mins = parseInt(schedule) || 60;
|
|
45
|
-
cronExpr = `*/${mins} * * * *`;
|
|
46
|
-
}
|
|
47
|
-
const cronEntry = `${cronExpr} ${command} # ${id}`;
|
|
48
|
-
let currentCron = '';
|
|
49
|
-
try {
|
|
50
|
-
currentCron = execSync('crontab -l', { encoding: 'utf-8' });
|
|
51
|
-
}
|
|
52
|
-
catch (e) { /* ignore empty crontab */ }
|
|
53
|
-
const newCron = currentCron.trim() + '\n' + cronEntry + '\n';
|
|
54
|
-
execSync(`echo "${newCron}" | crontab -`);
|
|
55
|
-
return `Task scheduled on Linux/Mac: ${id} with schedule ${cronExpr}`;
|
|
56
|
-
}
|
|
57
|
-
catch (e) {
|
|
58
|
-
return `Error scheduling on Linux/Mac: ${e.message}`;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
catch (error) {
|
|
63
|
-
throw new Error(`Failed to schedule task: ${error instanceof Error ? error.message : error}`);
|
|
64
|
-
}
|
|
65
|
-
};
|
package/dist/tools/scraper.d.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Web Scraper Tool - Fetches and converts web content to markdown
|
|
3
|
-
* Uses turndown for robust HTML to Markdown conversion (with regex fallback)
|
|
4
|
-
*/
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
import type { Tool } from '../registry.js';
|
|
7
|
-
export declare const inputSchema: z.ZodObject<{
|
|
8
|
-
url: z.ZodString;
|
|
9
|
-
convertToMarkdown: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
10
|
-
verifySSL: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
11
|
-
timeout: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
12
|
-
}, "strip", z.ZodTypeAny, {
|
|
13
|
-
url: string;
|
|
14
|
-
timeout: number;
|
|
15
|
-
convertToMarkdown: boolean;
|
|
16
|
-
verifySSL: boolean;
|
|
17
|
-
}, {
|
|
18
|
-
url: string;
|
|
19
|
-
timeout?: number | undefined;
|
|
20
|
-
convertToMarkdown?: boolean | undefined;
|
|
21
|
-
verifySSL?: boolean | undefined;
|
|
22
|
-
}>;
|
|
23
|
-
type ScraperInput = z.infer<typeof inputSchema>;
|
|
24
|
-
export declare function execute(input: ScraperInput): Promise<{
|
|
25
|
-
url: string;
|
|
26
|
-
content: string;
|
|
27
|
-
contentType: string;
|
|
28
|
-
error?: string;
|
|
29
|
-
}>;
|
|
30
|
-
export declare const tool: Tool;
|
|
31
|
-
export {};
|
package/dist/tools/scraper.js
DELETED
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Web Scraper Tool - Fetches and converts web content to markdown
|
|
3
|
-
* Uses turndown for robust HTML to Markdown conversion (with regex fallback)
|
|
4
|
-
*/
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
// Input schema
|
|
7
|
-
export const inputSchema = z.object({
|
|
8
|
-
url: z.string().url().describe('URL to scrape'),
|
|
9
|
-
convertToMarkdown: z.boolean().optional().default(true).describe('Convert HTML to Markdown'),
|
|
10
|
-
verifySSL: z.boolean().optional().default(true).describe('Verify SSL certificates'),
|
|
11
|
-
timeout: z.number().optional().default(30000).describe('Timeout in milliseconds'),
|
|
12
|
-
});
|
|
13
|
-
// Cached turndown instance
|
|
14
|
-
let turndownInstance = null;
|
|
15
|
-
let turndownLoaded = false;
|
|
16
|
-
// Get or create turndown instance (lazy load)
|
|
17
|
-
async function getTurndown() {
|
|
18
|
-
if (!turndownLoaded) {
|
|
19
|
-
turndownLoaded = true;
|
|
20
|
-
try {
|
|
21
|
-
const mod = await import('turndown');
|
|
22
|
-
const TurndownService = mod.default;
|
|
23
|
-
turndownInstance = new TurndownService({
|
|
24
|
-
headingStyle: 'atx',
|
|
25
|
-
hr: '---',
|
|
26
|
-
bulletListMarker: '-',
|
|
27
|
-
codeBlockStyle: 'fenced',
|
|
28
|
-
emDelimiter: '*',
|
|
29
|
-
strongDelimiter: '**',
|
|
30
|
-
linkStyle: 'inlined',
|
|
31
|
-
});
|
|
32
|
-
// Remove script and style elements
|
|
33
|
-
turndownInstance.remove(['script', 'style', 'noscript', 'iframe', 'svg']);
|
|
34
|
-
// Custom rule for code blocks with language hints
|
|
35
|
-
turndownInstance.addRule('codeBlock', {
|
|
36
|
-
filter: (node) => {
|
|
37
|
-
return (node.nodeName === 'PRE' &&
|
|
38
|
-
node.firstChild !== null &&
|
|
39
|
-
node.firstChild.nodeName === 'CODE');
|
|
40
|
-
},
|
|
41
|
-
replacement: (_content, node) => {
|
|
42
|
-
const codeNode = node.firstChild;
|
|
43
|
-
const className = codeNode.getAttribute?.('class') || '';
|
|
44
|
-
const langMatch = className.match(/language-(\w+)/);
|
|
45
|
-
const lang = langMatch ? langMatch[1] : '';
|
|
46
|
-
const code = codeNode.textContent || '';
|
|
47
|
-
return `\n\n\`\`\`${lang}\n${code.trim()}\n\`\`\`\n\n`;
|
|
48
|
-
},
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
// turndown not installed, will use fallback
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return turndownInstance;
|
|
56
|
-
}
|
|
57
|
-
// Fallback regex-based HTML to Markdown conversion
|
|
58
|
-
function htmlToMarkdownFallback(html) {
|
|
59
|
-
let md = html;
|
|
60
|
-
// Remove script and style tags
|
|
61
|
-
md = md.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
|
|
62
|
-
md = md.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '');
|
|
63
|
-
// Convert headings
|
|
64
|
-
md = md.replace(/<h1[^>]*>(.*?)<\/h1>/gi, '\n# $1\n');
|
|
65
|
-
md = md.replace(/<h2[^>]*>(.*?)<\/h2>/gi, '\n## $1\n');
|
|
66
|
-
md = md.replace(/<h3[^>]*>(.*?)<\/h3>/gi, '\n### $1\n');
|
|
67
|
-
md = md.replace(/<h4[^>]*>(.*?)<\/h4>/gi, '\n#### $1\n');
|
|
68
|
-
md = md.replace(/<h5[^>]*>(.*?)<\/h5>/gi, '\n##### $1\n');
|
|
69
|
-
md = md.replace(/<h6[^>]*>(.*?)<\/h6>/gi, '\n###### $1\n');
|
|
70
|
-
// Convert paragraphs
|
|
71
|
-
md = md.replace(/<p[^>]*>(.*?)<\/p>/gis, '\n$1\n');
|
|
72
|
-
// Convert links
|
|
73
|
-
md = md.replace(/<a[^>]*href="([^"]*)"[^>]*>(.*?)<\/a>/gi, '[$2]($1)');
|
|
74
|
-
// Convert images
|
|
75
|
-
md = md.replace(/<img[^>]*src="([^"]*)"[^>]*alt="([^"]*)"[^>]*\/?>/gi, '');
|
|
76
|
-
md = md.replace(/<img[^>]*src="([^"]*)"[^>]*\/?>/gi, '');
|
|
77
|
-
// Convert bold
|
|
78
|
-
md = md.replace(/<strong[^>]*>(.*?)<\/strong>/gi, '**$1**');
|
|
79
|
-
md = md.replace(/<b[^>]*>(.*?)<\/b>/gi, '**$1**');
|
|
80
|
-
// Convert italic
|
|
81
|
-
md = md.replace(/<em[^>]*>(.*?)<\/em>/gi, '*$1*');
|
|
82
|
-
md = md.replace(/<i[^>]*>(.*?)<\/i>/gi, '*$1*');
|
|
83
|
-
// Convert inline code
|
|
84
|
-
md = md.replace(/<code[^>]*>(.*?)<\/code>/gi, '`$1`');
|
|
85
|
-
// Convert code blocks
|
|
86
|
-
md = md.replace(/<pre[^>]*><code[^>]*>(.*?)<\/code><\/pre>/gis, '\n```\n$1\n```\n');
|
|
87
|
-
md = md.replace(/<pre[^>]*>(.*?)<\/pre>/gis, '\n```\n$1\n```\n');
|
|
88
|
-
// Convert lists
|
|
89
|
-
md = md.replace(/<ul[^>]*>(.*?)<\/ul>/gis, (_, content) => {
|
|
90
|
-
return content.replace(/<li[^>]*>(.*?)<\/li>/gi, '- $1\n');
|
|
91
|
-
});
|
|
92
|
-
md = md.replace(/<ol[^>]*>(.*?)<\/ol>/gis, (_, content) => {
|
|
93
|
-
let i = 1;
|
|
94
|
-
return content.replace(/<li[^>]*>(.*?)<\/li>/gi, () => `${i++}. $1\n`);
|
|
95
|
-
});
|
|
96
|
-
// Convert blockquotes
|
|
97
|
-
md = md.replace(/<blockquote[^>]*>(.*?)<\/blockquote>/gis, (_, content) => {
|
|
98
|
-
return content.split('\n').map((line) => `> ${line}`).join('\n');
|
|
99
|
-
});
|
|
100
|
-
// Convert line breaks
|
|
101
|
-
md = md.replace(/<br\s*\/?>/gi, '\n');
|
|
102
|
-
// Convert horizontal rules
|
|
103
|
-
md = md.replace(/<hr\s*\/?>/gi, '\n---\n');
|
|
104
|
-
// Remove remaining HTML tags
|
|
105
|
-
md = md.replace(/<[^>]+>/g, '');
|
|
106
|
-
// Decode HTML entities
|
|
107
|
-
md = md.replace(/ /g, ' ');
|
|
108
|
-
md = md.replace(/&/g, '&');
|
|
109
|
-
md = md.replace(/</g, '<');
|
|
110
|
-
md = md.replace(/>/g, '>');
|
|
111
|
-
md = md.replace(/"/g, '"');
|
|
112
|
-
md = md.replace(/'/g, "'");
|
|
113
|
-
// Clean up whitespace
|
|
114
|
-
md = md.replace(/\n{3,}/g, '\n\n');
|
|
115
|
-
md = md.trim();
|
|
116
|
-
return md;
|
|
117
|
-
}
|
|
118
|
-
// Slim down HTML by removing unnecessary elements before conversion
|
|
119
|
-
function slimDownHtml(html) {
|
|
120
|
-
let slim = html;
|
|
121
|
-
// Remove SVG elements
|
|
122
|
-
slim = slim.replace(/<svg\b[^<]*(?:(?!<\/svg>)<[^<]*)*<\/svg>/gi, '');
|
|
123
|
-
// Remove data: URLs (large embedded content)
|
|
124
|
-
slim = slim.replace(/\s*(?:href|src)=["']data:[^"']*["']/gi, '');
|
|
125
|
-
// Remove inline styles
|
|
126
|
-
slim = slim.replace(/\s*style=["'][^"']*["']/gi, '');
|
|
127
|
-
// Remove tracking pixels and tiny images
|
|
128
|
-
slim = slim.replace(/<img[^>]*(?:width|height)=["']?[01](?:px)?["']?[^>]*>/gi, '');
|
|
129
|
-
return slim;
|
|
130
|
-
}
|
|
131
|
-
// Check if content looks like HTML
|
|
132
|
-
function looksLikeHtml(content) {
|
|
133
|
-
const htmlPatterns = [
|
|
134
|
-
/<!DOCTYPE\s+html/i,
|
|
135
|
-
/<html/i,
|
|
136
|
-
/<head/i,
|
|
137
|
-
/<body/i,
|
|
138
|
-
/<div/i,
|
|
139
|
-
/<p>/i,
|
|
140
|
-
/<a\s+href=/i,
|
|
141
|
-
];
|
|
142
|
-
return htmlPatterns.some(pattern => pattern.test(content));
|
|
143
|
-
}
|
|
144
|
-
// Convert HTML to Markdown using turndown or fallback
|
|
145
|
-
async function htmlToMarkdown(html) {
|
|
146
|
-
const turndown = await getTurndown();
|
|
147
|
-
if (turndown) {
|
|
148
|
-
try {
|
|
149
|
-
return turndown.turndown(html);
|
|
150
|
-
}
|
|
151
|
-
catch {
|
|
152
|
-
// Fall through to regex fallback
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
return htmlToMarkdownFallback(html);
|
|
156
|
-
}
|
|
157
|
-
// Execute scraping
|
|
158
|
-
export async function execute(input) {
|
|
159
|
-
const { url, convertToMarkdown, verifySSL, timeout } = inputSchema.parse(input);
|
|
160
|
-
try {
|
|
161
|
-
const controller = new AbortController();
|
|
162
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
163
|
-
const response = await fetch(url, {
|
|
164
|
-
signal: controller.signal,
|
|
165
|
-
headers: {
|
|
166
|
-
'User-Agent': 'simplecli/0.2.1 (+https://github.com/stancsz/simple-cli)',
|
|
167
|
-
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
168
|
-
},
|
|
169
|
-
});
|
|
170
|
-
clearTimeout(timeoutId);
|
|
171
|
-
if (!response.ok) {
|
|
172
|
-
return {
|
|
173
|
-
url,
|
|
174
|
-
content: '',
|
|
175
|
-
contentType: '',
|
|
176
|
-
error: `HTTP ${response.status}: ${response.statusText}`,
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
const contentType = response.headers.get('content-type') || '';
|
|
180
|
-
let content = await response.text();
|
|
181
|
-
// Convert HTML to Markdown if requested
|
|
182
|
-
if (convertToMarkdown && (contentType.includes('text/html') || looksLikeHtml(content))) {
|
|
183
|
-
content = slimDownHtml(content);
|
|
184
|
-
content = await htmlToMarkdown(content);
|
|
185
|
-
// Clean up excessive whitespace
|
|
186
|
-
content = content.replace(/\n{3,}/g, '\n\n').trim();
|
|
187
|
-
}
|
|
188
|
-
return {
|
|
189
|
-
url,
|
|
190
|
-
content,
|
|
191
|
-
contentType,
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
catch (error) {
|
|
195
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
196
|
-
return {
|
|
197
|
-
url,
|
|
198
|
-
content: '',
|
|
199
|
-
contentType: '',
|
|
200
|
-
error: errorMessage.includes('abort') ? 'Request timed out' : errorMessage,
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
// Tool definition
|
|
205
|
-
export const tool = {
|
|
206
|
-
name: 'scrapeUrl',
|
|
207
|
-
description: 'Fetch a URL and convert its content to markdown. Useful for reading web pages, documentation, or API responses.',
|
|
208
|
-
inputSchema,
|
|
209
|
-
permission: 'read',
|
|
210
|
-
execute: async (args) => execute(args),
|
|
211
|
-
};
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool: writeFiles
|
|
3
|
-
* Write or update files with search/replace support (Aider-style)
|
|
4
|
-
*/
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
export declare const name = "writeFiles";
|
|
7
|
-
export declare const description = "Write or modify files. ALWAYS provide an array of objects in the \"files\" parameter, even for a single file. Each object must have \"path\" and either \"content\" (for full write) or \"searchReplace\" (for edits).";
|
|
8
|
-
export declare const permission: "write";
|
|
9
|
-
export declare const schema: z.ZodObject<{
|
|
10
|
-
files: z.ZodArray<z.ZodObject<{
|
|
11
|
-
path: z.ZodString;
|
|
12
|
-
content: z.ZodOptional<z.ZodString>;
|
|
13
|
-
searchReplace: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
14
|
-
search: z.ZodString;
|
|
15
|
-
replace: z.ZodString;
|
|
16
|
-
}, "strip", z.ZodTypeAny, {
|
|
17
|
-
replace: string;
|
|
18
|
-
search: string;
|
|
19
|
-
}, {
|
|
20
|
-
replace: string;
|
|
21
|
-
search: string;
|
|
22
|
-
}>, "many">>;
|
|
23
|
-
}, "strip", z.ZodTypeAny, {
|
|
24
|
-
path: string;
|
|
25
|
-
content?: string | undefined;
|
|
26
|
-
searchReplace?: {
|
|
27
|
-
replace: string;
|
|
28
|
-
search: string;
|
|
29
|
-
}[] | undefined;
|
|
30
|
-
}, {
|
|
31
|
-
path: string;
|
|
32
|
-
content?: string | undefined;
|
|
33
|
-
searchReplace?: {
|
|
34
|
-
replace: string;
|
|
35
|
-
search: string;
|
|
36
|
-
}[] | undefined;
|
|
37
|
-
}>, "many">;
|
|
38
|
-
}, "strip", z.ZodTypeAny, {
|
|
39
|
-
files: {
|
|
40
|
-
path: string;
|
|
41
|
-
content?: string | undefined;
|
|
42
|
-
searchReplace?: {
|
|
43
|
-
replace: string;
|
|
44
|
-
search: string;
|
|
45
|
-
}[] | undefined;
|
|
46
|
-
}[];
|
|
47
|
-
}, {
|
|
48
|
-
files: {
|
|
49
|
-
path: string;
|
|
50
|
-
content?: string | undefined;
|
|
51
|
-
searchReplace?: {
|
|
52
|
-
replace: string;
|
|
53
|
-
search: string;
|
|
54
|
-
}[] | undefined;
|
|
55
|
-
}[];
|
|
56
|
-
}>;
|
|
57
|
-
interface WriteResult {
|
|
58
|
-
path: string;
|
|
59
|
-
success: boolean;
|
|
60
|
-
message: string;
|
|
61
|
-
}
|
|
62
|
-
export declare const execute: (args: Record<string, unknown>) => Promise<WriteResult[]>;
|
|
63
|
-
export {};
|
package/dist/tools/writeFiles.js
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool: writeFiles
|
|
3
|
-
* Write or update files with search/replace support (Aider-style)
|
|
4
|
-
*/
|
|
5
|
-
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
6
|
-
import { dirname, resolve } from 'path';
|
|
7
|
-
import { z } from 'zod';
|
|
8
|
-
export const name = 'writeFiles';
|
|
9
|
-
export const description = 'Write or modify files. ALWAYS provide an array of objects in the "files" parameter, even for a single file. Each object must have "path" and either "content" (for full write) or "searchReplace" (for edits).';
|
|
10
|
-
export const permission = 'write';
|
|
11
|
-
const FileWriteSchema = z.object({
|
|
12
|
-
path: z.string().describe('File path to write'),
|
|
13
|
-
content: z.string().optional().describe('Full content to write (for new files or full rewrites)'),
|
|
14
|
-
searchReplace: z.array(z.object({
|
|
15
|
-
search: z.string().describe('Text to search for'),
|
|
16
|
-
replace: z.string().describe('Text to replace with')
|
|
17
|
-
})).optional().describe('Array of search/replace operations for targeted edits')
|
|
18
|
-
});
|
|
19
|
-
export const schema = z.object({
|
|
20
|
-
files: z.array(FileWriteSchema).describe('Array of files to write/modify')
|
|
21
|
-
});
|
|
22
|
-
export const execute = async (args) => {
|
|
23
|
-
const parsed = schema.parse(args);
|
|
24
|
-
const results = [];
|
|
25
|
-
for (const file of parsed.files) {
|
|
26
|
-
try {
|
|
27
|
-
// Ensure directory exists
|
|
28
|
-
await mkdir(dirname(file.path), { recursive: true });
|
|
29
|
-
if (file.content !== undefined) {
|
|
30
|
-
// Full file write
|
|
31
|
-
await writeFile(file.path, file.content, 'utf-8');
|
|
32
|
-
const absPath = resolve(file.path);
|
|
33
|
-
results.push({
|
|
34
|
-
path: file.path,
|
|
35
|
-
success: true,
|
|
36
|
-
message: `File written successfully to ${absPath}`
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
else if (file.searchReplace && file.searchReplace.length > 0) {
|
|
40
|
-
// Search/replace operations
|
|
41
|
-
let content = await readFile(file.path, 'utf-8');
|
|
42
|
-
let changesApplied = 0;
|
|
43
|
-
const absPath = resolve(file.path);
|
|
44
|
-
for (const { search, replace } of file.searchReplace) {
|
|
45
|
-
if (content.includes(search)) {
|
|
46
|
-
// Apply replacement to all occurrences
|
|
47
|
-
const newContent = content.replace(search, replace);
|
|
48
|
-
if (newContent !== content) {
|
|
49
|
-
content = newContent;
|
|
50
|
-
changesApplied++;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
if (changesApplied > 0) {
|
|
55
|
-
await writeFile(file.path, content, 'utf-8');
|
|
56
|
-
results.push({
|
|
57
|
-
path: file.path,
|
|
58
|
-
success: true,
|
|
59
|
-
message: `Applied ${changesApplied} search/replace operation(s) to ${absPath}`
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
results.push({
|
|
64
|
-
path: file.path,
|
|
65
|
-
success: false,
|
|
66
|
-
message: `No matching search patterns found in ${absPath}`
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
results.push({
|
|
72
|
-
path: file.path,
|
|
73
|
-
success: false,
|
|
74
|
-
message: 'No content or searchReplace provided'
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
catch (error) {
|
|
79
|
-
results.push({
|
|
80
|
-
path: file.path,
|
|
81
|
-
success: false,
|
|
82
|
-
message: error instanceof Error ? error.message : 'Unknown error'
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return results;
|
|
87
|
-
};
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool: writeFiles
|
|
3
|
-
* Write or update files with search/replace support (Aider-style)
|
|
4
|
-
*/
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
export declare const name = "write_files";
|
|
7
|
-
export declare const description = "Write or modify files. ALWAYS provide an array of objects in the \"files\" parameter, even for a single file. Each object must have \"path\" and either \"content\" (for full write) or \"searchReplace\" (for edits).";
|
|
8
|
-
export declare const permission: "write";
|
|
9
|
-
export declare const schema: z.ZodObject<{
|
|
10
|
-
files: z.ZodArray<z.ZodObject<{
|
|
11
|
-
path: z.ZodString;
|
|
12
|
-
content: z.ZodOptional<z.ZodString>;
|
|
13
|
-
searchReplace: z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodObject<{
|
|
14
|
-
search: z.ZodString;
|
|
15
|
-
replace: z.ZodString;
|
|
16
|
-
}, "strip", z.ZodTypeAny, {
|
|
17
|
-
replace: string;
|
|
18
|
-
search: string;
|
|
19
|
-
}, {
|
|
20
|
-
replace: string;
|
|
21
|
-
search: string;
|
|
22
|
-
}>, "many">, z.ZodEffects<z.ZodObject<{
|
|
23
|
-
search: z.ZodString;
|
|
24
|
-
replace: z.ZodString;
|
|
25
|
-
}, "strip", z.ZodTypeAny, {
|
|
26
|
-
replace: string;
|
|
27
|
-
search: string;
|
|
28
|
-
}, {
|
|
29
|
-
replace: string;
|
|
30
|
-
search: string;
|
|
31
|
-
}>, {
|
|
32
|
-
replace: string;
|
|
33
|
-
search: string;
|
|
34
|
-
}[], {
|
|
35
|
-
replace: string;
|
|
36
|
-
search: string;
|
|
37
|
-
}>]>>;
|
|
38
|
-
}, "strip", z.ZodTypeAny, {
|
|
39
|
-
path: string;
|
|
40
|
-
content?: string | undefined;
|
|
41
|
-
searchReplace?: {
|
|
42
|
-
replace: string;
|
|
43
|
-
search: string;
|
|
44
|
-
}[] | undefined;
|
|
45
|
-
}, {
|
|
46
|
-
path: string;
|
|
47
|
-
content?: string | undefined;
|
|
48
|
-
searchReplace?: {
|
|
49
|
-
replace: string;
|
|
50
|
-
search: string;
|
|
51
|
-
} | {
|
|
52
|
-
replace: string;
|
|
53
|
-
search: string;
|
|
54
|
-
}[] | undefined;
|
|
55
|
-
}>, "many">;
|
|
56
|
-
}, "strip", z.ZodTypeAny, {
|
|
57
|
-
files: {
|
|
58
|
-
path: string;
|
|
59
|
-
content?: string | undefined;
|
|
60
|
-
searchReplace?: {
|
|
61
|
-
replace: string;
|
|
62
|
-
search: string;
|
|
63
|
-
}[] | undefined;
|
|
64
|
-
}[];
|
|
65
|
-
}, {
|
|
66
|
-
files: {
|
|
67
|
-
path: string;
|
|
68
|
-
content?: string | undefined;
|
|
69
|
-
searchReplace?: {
|
|
70
|
-
replace: string;
|
|
71
|
-
search: string;
|
|
72
|
-
} | {
|
|
73
|
-
replace: string;
|
|
74
|
-
search: string;
|
|
75
|
-
}[] | undefined;
|
|
76
|
-
}[];
|
|
77
|
-
}>;
|
|
78
|
-
interface WriteResult {
|
|
79
|
-
path: string;
|
|
80
|
-
success: boolean;
|
|
81
|
-
message: string;
|
|
82
|
-
}
|
|
83
|
-
export declare const execute: (args: Record<string, unknown>) => Promise<WriteResult[]>;
|
|
84
|
-
export {};
|