@nils.del/supergrok 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env +45 -0
- package/.grok/settings.json +13 -0
- package/dist/agent/index.d.ts +35 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +244 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +242 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/client.d.ts +33 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +134 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/config.d.ts +15 -0
- package/dist/mcp/config.d.ts.map +1 -0
- package/dist/mcp/config.js +123 -0
- package/dist/mcp/config.js.map +1 -0
- package/dist/mcp/transports.d.ts +24 -0
- package/dist/mcp/transports.d.ts.map +1 -0
- package/dist/mcp/transports.js +91 -0
- package/dist/mcp/transports.js.map +1 -0
- package/dist/tools/bash.d.ts +28 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +70 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/index.d.ts +77 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +80 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/text-editor.d.ts +69 -0
- package/dist/tools/text-editor.d.ts.map +1 -0
- package/dist/tools/text-editor.js +178 -0
- package/dist/tools/text-editor.js.map +1 -0
- package/dist/tools/types.d.ts +14 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/ui/readline-ui.d.ts +32 -0
- package/dist/ui/readline-ui.d.ts.map +1 -0
- package/dist/ui/readline-ui.js +256 -0
- package/dist/ui/readline-ui.js.map +1 -0
- package/dist/utils/settings.d.ts +20 -0
- package/dist/utils/settings.d.ts.map +1 -0
- package/dist/utils/settings.js +101 -0
- package/dist/utils/settings.js.map +1 -0
- package/package.json +51 -0
- package/src/agent/index.ts +302 -0
- package/src/index.ts +275 -0
- package/src/mcp/client.ts +178 -0
- package/src/mcp/config.ts +149 -0
- package/src/mcp/transports.ts +142 -0
- package/src/tools/bash.ts +84 -0
- package/src/tools/index.ts +91 -0
- package/src/tools/text-editor.ts +222 -0
- package/src/tools/types.ts +14 -0
- package/src/ui/readline-ui.ts +294 -0
- package/src/utils/settings.ts +129 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
2
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
3
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
4
|
+
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
5
|
+
|
|
6
|
+
export type TransportType = 'stdio' | 'http' | 'sse';
|
|
7
|
+
|
|
8
|
+
export interface StdioTransportConfig {
|
|
9
|
+
type: 'stdio';
|
|
10
|
+
command: string;
|
|
11
|
+
args?: string[];
|
|
12
|
+
env?: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface HttpTransportConfig {
|
|
16
|
+
type: 'http';
|
|
17
|
+
url: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface SSETransportConfig {
|
|
21
|
+
type: 'sse';
|
|
22
|
+
url: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type TransportConfig = StdioTransportConfig | HttpTransportConfig | SSETransportConfig;
|
|
26
|
+
|
|
27
|
+
export interface MCPTransport {
|
|
28
|
+
connect(): Promise<Transport>;
|
|
29
|
+
disconnect(): Promise<void>;
|
|
30
|
+
getType(): TransportType;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class StdioMCPTransport implements MCPTransport {
|
|
34
|
+
private config: StdioTransportConfig;
|
|
35
|
+
private transport: StdioClientTransport | null = null;
|
|
36
|
+
|
|
37
|
+
constructor(config: StdioTransportConfig) {
|
|
38
|
+
this.config = config;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async connect(): Promise<Transport> {
|
|
42
|
+
const env: Record<string, string> = {};
|
|
43
|
+
|
|
44
|
+
// Copy process.env, filtering out undefined values
|
|
45
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
46
|
+
if (value !== undefined) {
|
|
47
|
+
env[key] = value;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Merge with config env
|
|
52
|
+
if (this.config.env) {
|
|
53
|
+
Object.assign(env, this.config.env);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this.transport = new StdioClientTransport({
|
|
57
|
+
command: this.config.command,
|
|
58
|
+
args: this.config.args || [],
|
|
59
|
+
env
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return this.transport;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async disconnect(): Promise<void> {
|
|
66
|
+
if (this.transport) {
|
|
67
|
+
await this.transport.close();
|
|
68
|
+
this.transport = null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
getType(): TransportType {
|
|
73
|
+
return 'stdio';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
class HttpMCPTransport implements MCPTransport {
|
|
78
|
+
private config: HttpTransportConfig;
|
|
79
|
+
private transport: StreamableHTTPClientTransport | null = null;
|
|
80
|
+
|
|
81
|
+
constructor(config: HttpTransportConfig) {
|
|
82
|
+
this.config = config;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async connect(): Promise<Transport> {
|
|
86
|
+
this.transport = new StreamableHTTPClientTransport(
|
|
87
|
+
new URL(this.config.url)
|
|
88
|
+
);
|
|
89
|
+
return this.transport;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async disconnect(): Promise<void> {
|
|
93
|
+
if (this.transport) {
|
|
94
|
+
await this.transport.close();
|
|
95
|
+
this.transport = null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
getType(): TransportType {
|
|
100
|
+
return 'http';
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
class SSEMCPTransport implements MCPTransport {
|
|
105
|
+
private config: SSETransportConfig;
|
|
106
|
+
private transport: SSEClientTransport | null = null;
|
|
107
|
+
|
|
108
|
+
constructor(config: SSETransportConfig) {
|
|
109
|
+
this.config = config;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async connect(): Promise<Transport> {
|
|
113
|
+
this.transport = new SSEClientTransport(
|
|
114
|
+
new URL(this.config.url)
|
|
115
|
+
);
|
|
116
|
+
return this.transport;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async disconnect(): Promise<void> {
|
|
120
|
+
if (this.transport) {
|
|
121
|
+
await this.transport.close();
|
|
122
|
+
this.transport = null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
getType(): TransportType {
|
|
127
|
+
return 'sse';
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function createTransport(config: TransportConfig): MCPTransport {
|
|
132
|
+
switch (config.type) {
|
|
133
|
+
case 'stdio':
|
|
134
|
+
return new StdioMCPTransport(config);
|
|
135
|
+
case 'http':
|
|
136
|
+
return new HttpMCPTransport(config);
|
|
137
|
+
case 'sse':
|
|
138
|
+
return new SSEMCPTransport(config);
|
|
139
|
+
default:
|
|
140
|
+
throw new Error(`Unknown transport type: ${(config as any).type}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import { ToolResult } from './types.js';
|
|
3
|
+
|
|
4
|
+
export interface BashToolArgs {
|
|
5
|
+
command: string;
|
|
6
|
+
timeout?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function executeBashCommand(args: BashToolArgs): Promise<ToolResult> {
|
|
10
|
+
const { command, timeout = 120000 } = args;
|
|
11
|
+
|
|
12
|
+
return new Promise((resolve) => {
|
|
13
|
+
const childProcess = spawn('bash', ['-c', command], {
|
|
14
|
+
cwd: process.cwd(),
|
|
15
|
+
env: process.env,
|
|
16
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
let stdout = '';
|
|
20
|
+
let stderr = '';
|
|
21
|
+
|
|
22
|
+
const timer = setTimeout(() => {
|
|
23
|
+
childProcess.kill('SIGTERM');
|
|
24
|
+
resolve({
|
|
25
|
+
success: false,
|
|
26
|
+
error: `Command timed out after ${timeout}ms`
|
|
27
|
+
});
|
|
28
|
+
}, timeout);
|
|
29
|
+
|
|
30
|
+
childProcess.stdout.on('data', (data) => {
|
|
31
|
+
stdout += data.toString();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
childProcess.stderr.on('data', (data) => {
|
|
35
|
+
stderr += data.toString();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
childProcess.on('close', (code) => {
|
|
39
|
+
clearTimeout(timer);
|
|
40
|
+
|
|
41
|
+
if (code === 0) {
|
|
42
|
+
resolve({
|
|
43
|
+
success: true,
|
|
44
|
+
output: stdout.trim() || stderr.trim() || 'Command completed successfully'
|
|
45
|
+
});
|
|
46
|
+
} else {
|
|
47
|
+
resolve({
|
|
48
|
+
success: false,
|
|
49
|
+
error: stderr.trim() || stdout.trim() || `Command failed with exit code ${code}`
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
childProcess.on('error', (error) => {
|
|
55
|
+
clearTimeout(timer);
|
|
56
|
+
resolve({
|
|
57
|
+
success: false,
|
|
58
|
+
error: `Failed to execute command: ${error.message}`
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const bashToolDefinition = {
|
|
65
|
+
type: "function" as const,
|
|
66
|
+
function: {
|
|
67
|
+
name: "bash",
|
|
68
|
+
description: "Execute a bash command and return the output. Use this for running shell commands, file operations, git commands, etc.",
|
|
69
|
+
parameters: {
|
|
70
|
+
type: "object",
|
|
71
|
+
properties: {
|
|
72
|
+
command: {
|
|
73
|
+
type: "string",
|
|
74
|
+
description: "The bash command to execute"
|
|
75
|
+
},
|
|
76
|
+
timeout: {
|
|
77
|
+
type: "number",
|
|
78
|
+
description: "Optional timeout in milliseconds (default: 120000)"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
required: ["command"]
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { ToolResult, ToolCall } from './types.js';
|
|
2
|
+
import { executeBashCommand, bashToolDefinition } from './bash.js';
|
|
3
|
+
import { viewFile, createFile, strReplace, insertText, textEditorToolDefinition } from './text-editor.js';
|
|
4
|
+
import { getMCPManager } from '../mcp/client.js';
|
|
5
|
+
|
|
6
|
+
export { ToolResult, ToolCall };
|
|
7
|
+
|
|
8
|
+
export function getBuiltInTools() {
|
|
9
|
+
return [bashToolDefinition, textEditorToolDefinition];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function getAllTools() {
|
|
13
|
+
const builtInTools = getBuiltInTools();
|
|
14
|
+
const mcpManager = getMCPManager();
|
|
15
|
+
|
|
16
|
+
// Ensure MCP servers are initialized
|
|
17
|
+
await mcpManager.ensureServersInitialized();
|
|
18
|
+
|
|
19
|
+
const mcpTools = mcpManager.getTools().map(tool => ({
|
|
20
|
+
type: "function" as const,
|
|
21
|
+
function: {
|
|
22
|
+
name: tool.name,
|
|
23
|
+
description: tool.description,
|
|
24
|
+
parameters: tool.inputSchema
|
|
25
|
+
}
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
return [...builtInTools, ...mcpTools];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function executeToolCall(toolCall: ToolCall): Promise<ToolResult> {
|
|
32
|
+
const { function: fn } = toolCall;
|
|
33
|
+
const args = JSON.parse(fn.arguments || '{}');
|
|
34
|
+
|
|
35
|
+
// Handle MCP tools
|
|
36
|
+
if (fn.name.startsWith('mcp__')) {
|
|
37
|
+
try {
|
|
38
|
+
const mcpManager = getMCPManager();
|
|
39
|
+
const result = await mcpManager.callTool(fn.name, args);
|
|
40
|
+
|
|
41
|
+
// Convert MCP result to our format
|
|
42
|
+
const content = result.content
|
|
43
|
+
.map((c: any) => {
|
|
44
|
+
if (c.type === 'text') return c.text;
|
|
45
|
+
if (c.type === 'image') return `[Image: ${c.mimeType}]`;
|
|
46
|
+
return JSON.stringify(c);
|
|
47
|
+
})
|
|
48
|
+
.join('\n');
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
success: !result.isError,
|
|
52
|
+
output: result.isError ? undefined : content,
|
|
53
|
+
error: result.isError ? content : undefined
|
|
54
|
+
};
|
|
55
|
+
} catch (error: any) {
|
|
56
|
+
return {
|
|
57
|
+
success: false,
|
|
58
|
+
error: `MCP tool error: ${error.message}`
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Handle built-in tools
|
|
64
|
+
switch (fn.name) {
|
|
65
|
+
case 'bash':
|
|
66
|
+
return await executeBashCommand(args);
|
|
67
|
+
|
|
68
|
+
case 'str_replace_editor':
|
|
69
|
+
switch (args.command) {
|
|
70
|
+
case 'view':
|
|
71
|
+
return await viewFile({ path: args.path, view_range: args.view_range });
|
|
72
|
+
case 'create':
|
|
73
|
+
return await createFile({ path: args.path, file_text: args.file_text });
|
|
74
|
+
case 'str_replace':
|
|
75
|
+
return await strReplace({ path: args.path, old_str: args.old_str, new_str: args.new_str });
|
|
76
|
+
case 'insert':
|
|
77
|
+
return await insertText({ path: args.path, insert_line: args.insert_line, new_str: args.new_str });
|
|
78
|
+
default:
|
|
79
|
+
return {
|
|
80
|
+
success: false,
|
|
81
|
+
error: `Unknown editor command: ${args.command}`
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
default:
|
|
86
|
+
return {
|
|
87
|
+
success: false,
|
|
88
|
+
error: `Unknown tool: ${fn.name}`
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { ToolResult } from './types.js';
|
|
4
|
+
|
|
5
|
+
export interface ViewArgs {
|
|
6
|
+
path: string;
|
|
7
|
+
view_range?: [number, number];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface CreateArgs {
|
|
11
|
+
path: string;
|
|
12
|
+
file_text: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface StrReplaceArgs {
|
|
16
|
+
path: string;
|
|
17
|
+
old_str: string;
|
|
18
|
+
new_str: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface InsertArgs {
|
|
22
|
+
path: string;
|
|
23
|
+
insert_line: number;
|
|
24
|
+
new_str: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function viewFile(args: ViewArgs): Promise<ToolResult> {
|
|
28
|
+
try {
|
|
29
|
+
const filePath = path.resolve(process.cwd(), args.path);
|
|
30
|
+
|
|
31
|
+
if (!fs.existsSync(filePath)) {
|
|
32
|
+
return {
|
|
33
|
+
success: false,
|
|
34
|
+
error: `File not found: ${args.path}`
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
39
|
+
const lines = content.split('\n');
|
|
40
|
+
|
|
41
|
+
if (args.view_range) {
|
|
42
|
+
const [start, end] = args.view_range;
|
|
43
|
+
const startIdx = Math.max(0, start - 1);
|
|
44
|
+
const endIdx = Math.min(lines.length, end);
|
|
45
|
+
|
|
46
|
+
const numberedLines = lines
|
|
47
|
+
.slice(startIdx, endIdx)
|
|
48
|
+
.map((line, i) => `${startIdx + i + 1}: ${line}`)
|
|
49
|
+
.join('\n');
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
success: true,
|
|
53
|
+
output: `File: ${args.path} (lines ${start}-${end})\n\n${numberedLines}`
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const numberedLines = lines
|
|
58
|
+
.map((line, i) => `${i + 1}: ${line}`)
|
|
59
|
+
.join('\n');
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
success: true,
|
|
63
|
+
output: `File: ${args.path} (${lines.length} lines)\n\n${numberedLines}`
|
|
64
|
+
};
|
|
65
|
+
} catch (error: any) {
|
|
66
|
+
return {
|
|
67
|
+
success: false,
|
|
68
|
+
error: `Failed to read file: ${error.message}`
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function createFile(args: CreateArgs): Promise<ToolResult> {
|
|
74
|
+
try {
|
|
75
|
+
const filePath = path.resolve(process.cwd(), args.path);
|
|
76
|
+
const dir = path.dirname(filePath);
|
|
77
|
+
|
|
78
|
+
if (!fs.existsSync(dir)) {
|
|
79
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
fs.writeFileSync(filePath, args.file_text);
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
success: true,
|
|
86
|
+
output: `File created successfully: ${args.path}`
|
|
87
|
+
};
|
|
88
|
+
} catch (error: any) {
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
error: `Failed to create file: ${error.message}`
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export async function strReplace(args: StrReplaceArgs): Promise<ToolResult> {
|
|
97
|
+
try {
|
|
98
|
+
const filePath = path.resolve(process.cwd(), args.path);
|
|
99
|
+
|
|
100
|
+
if (!fs.existsSync(filePath)) {
|
|
101
|
+
return {
|
|
102
|
+
success: false,
|
|
103
|
+
error: `File not found: ${args.path}`
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
108
|
+
|
|
109
|
+
if (!content.includes(args.old_str)) {
|
|
110
|
+
return {
|
|
111
|
+
success: false,
|
|
112
|
+
error: `String not found in file: "${args.old_str.substring(0, 50)}..."`
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const occurrences = content.split(args.old_str).length - 1;
|
|
117
|
+
if (occurrences > 1) {
|
|
118
|
+
return {
|
|
119
|
+
success: false,
|
|
120
|
+
error: `Found ${occurrences} occurrences of the string. Please provide a more specific string to replace.`
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const newContent = content.replace(args.old_str, args.new_str);
|
|
125
|
+
fs.writeFileSync(filePath, newContent);
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
success: true,
|
|
129
|
+
output: `Successfully replaced text in ${args.path}`
|
|
130
|
+
};
|
|
131
|
+
} catch (error: any) {
|
|
132
|
+
return {
|
|
133
|
+
success: false,
|
|
134
|
+
error: `Failed to replace text: ${error.message}`
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export async function insertText(args: InsertArgs): Promise<ToolResult> {
|
|
140
|
+
try {
|
|
141
|
+
const filePath = path.resolve(process.cwd(), args.path);
|
|
142
|
+
|
|
143
|
+
if (!fs.existsSync(filePath)) {
|
|
144
|
+
return {
|
|
145
|
+
success: false,
|
|
146
|
+
error: `File not found: ${args.path}`
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
151
|
+
const lines = content.split('\n');
|
|
152
|
+
|
|
153
|
+
if (args.insert_line < 0 || args.insert_line > lines.length) {
|
|
154
|
+
return {
|
|
155
|
+
success: false,
|
|
156
|
+
error: `Invalid line number: ${args.insert_line}. File has ${lines.length} lines.`
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
lines.splice(args.insert_line, 0, args.new_str);
|
|
161
|
+
const newContent = lines.join('\n');
|
|
162
|
+
fs.writeFileSync(filePath, newContent);
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
success: true,
|
|
166
|
+
output: `Successfully inserted text at line ${args.insert_line} in ${args.path}`
|
|
167
|
+
};
|
|
168
|
+
} catch (error: any) {
|
|
169
|
+
return {
|
|
170
|
+
success: false,
|
|
171
|
+
error: `Failed to insert text: ${error.message}`
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export const textEditorToolDefinition = {
|
|
177
|
+
type: "function" as const,
|
|
178
|
+
function: {
|
|
179
|
+
name: "str_replace_editor",
|
|
180
|
+
description: `A powerful text editor tool for viewing and modifying files. Commands:
|
|
181
|
+
- view: View file contents with line numbers
|
|
182
|
+
- create: Create a new file with specified content
|
|
183
|
+
- str_replace: Replace a specific string in a file (must be unique)
|
|
184
|
+
- insert: Insert text at a specific line number`,
|
|
185
|
+
parameters: {
|
|
186
|
+
type: "object",
|
|
187
|
+
properties: {
|
|
188
|
+
command: {
|
|
189
|
+
type: "string",
|
|
190
|
+
enum: ["view", "create", "str_replace", "insert"],
|
|
191
|
+
description: "The editing command to execute"
|
|
192
|
+
},
|
|
193
|
+
path: {
|
|
194
|
+
type: "string",
|
|
195
|
+
description: "The file path to operate on"
|
|
196
|
+
},
|
|
197
|
+
file_text: {
|
|
198
|
+
type: "string",
|
|
199
|
+
description: "For create command: the content of the new file"
|
|
200
|
+
},
|
|
201
|
+
old_str: {
|
|
202
|
+
type: "string",
|
|
203
|
+
description: "For str_replace command: the exact string to replace"
|
|
204
|
+
},
|
|
205
|
+
new_str: {
|
|
206
|
+
type: "string",
|
|
207
|
+
description: "For str_replace/insert commands: the new string to insert"
|
|
208
|
+
},
|
|
209
|
+
insert_line: {
|
|
210
|
+
type: "number",
|
|
211
|
+
description: "For insert command: the line number to insert at (0-indexed)"
|
|
212
|
+
},
|
|
213
|
+
view_range: {
|
|
214
|
+
type: "array",
|
|
215
|
+
items: { type: "number" },
|
|
216
|
+
description: "For view command: optional [start, end] line range"
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
required: ["command", "path"]
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
};
|