@bluehawks/cli 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/.eslintrc.json +36 -0
- package/.prettierrc +8 -0
- package/README.md +288 -0
- package/dist/cli/app.d.ts +12 -0
- package/dist/cli/app.d.ts.map +1 -0
- package/dist/cli/app.js +201 -0
- package/dist/cli/app.js.map +1 -0
- package/dist/cli/commands/index.d.ts +56 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/index.js +201 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/config/constants.d.ts +32 -0
- package/dist/config/constants.d.ts.map +1 -0
- package/dist/config/constants.js +39 -0
- package/dist/config/constants.js.map +1 -0
- package/dist/config/index.d.ts +4 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +4 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/schema.d.ts +56 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +28 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/settings.d.ts +20 -0
- package/dist/config/settings.d.ts.map +1 -0
- package/dist/config/settings.js +102 -0
- package/dist/config/settings.js.map +1 -0
- package/dist/core/agents/agent.d.ts +33 -0
- package/dist/core/agents/agent.d.ts.map +1 -0
- package/dist/core/agents/agent.js +156 -0
- package/dist/core/agents/agent.js.map +1 -0
- package/dist/core/agents/index.d.ts +3 -0
- package/dist/core/agents/index.d.ts.map +1 -0
- package/dist/core/agents/index.js +3 -0
- package/dist/core/agents/index.js.map +1 -0
- package/dist/core/agents/orchestrator.d.ts +56 -0
- package/dist/core/agents/orchestrator.d.ts.map +1 -0
- package/dist/core/agents/orchestrator.js +151 -0
- package/dist/core/agents/orchestrator.js.map +1 -0
- package/dist/core/api/client.d.ts +46 -0
- package/dist/core/api/client.d.ts.map +1 -0
- package/dist/core/api/client.js +223 -0
- package/dist/core/api/client.js.map +1 -0
- package/dist/core/api/index.d.ts +3 -0
- package/dist/core/api/index.d.ts.map +1 -0
- package/dist/core/api/index.js +3 -0
- package/dist/core/api/index.js.map +1 -0
- package/dist/core/api/types.d.ts +126 -0
- package/dist/core/api/types.d.ts.map +1 -0
- package/dist/core/api/types.js +16 -0
- package/dist/core/api/types.js.map +1 -0
- package/dist/core/hooks/index.d.ts +3 -0
- package/dist/core/hooks/index.d.ts.map +1 -0
- package/dist/core/hooks/index.js +3 -0
- package/dist/core/hooks/index.js.map +1 -0
- package/dist/core/hooks/manager.d.ts +43 -0
- package/dist/core/hooks/manager.d.ts.map +1 -0
- package/dist/core/hooks/manager.js +178 -0
- package/dist/core/hooks/manager.js.map +1 -0
- package/dist/core/hooks/types.d.ts +68 -0
- package/dist/core/hooks/types.d.ts.map +1 -0
- package/dist/core/hooks/types.js +6 -0
- package/dist/core/hooks/types.js.map +1 -0
- package/dist/core/mcp/client.d.ts +48 -0
- package/dist/core/mcp/client.d.ts.map +1 -0
- package/dist/core/mcp/client.js +139 -0
- package/dist/core/mcp/client.js.map +1 -0
- package/dist/core/mcp/index.d.ts +3 -0
- package/dist/core/mcp/index.d.ts.map +1 -0
- package/dist/core/mcp/index.js +3 -0
- package/dist/core/mcp/index.js.map +1 -0
- package/dist/core/mcp/manager.d.ts +46 -0
- package/dist/core/mcp/manager.d.ts.map +1 -0
- package/dist/core/mcp/manager.js +133 -0
- package/dist/core/mcp/manager.js.map +1 -0
- package/dist/core/plugins/index.d.ts +3 -0
- package/dist/core/plugins/index.d.ts.map +1 -0
- package/dist/core/plugins/index.js +3 -0
- package/dist/core/plugins/index.js.map +1 -0
- package/dist/core/plugins/loader.d.ts +63 -0
- package/dist/core/plugins/loader.d.ts.map +1 -0
- package/dist/core/plugins/loader.js +258 -0
- package/dist/core/plugins/loader.js.map +1 -0
- package/dist/core/plugins/types.d.ts +95 -0
- package/dist/core/plugins/types.d.ts.map +1 -0
- package/dist/core/plugins/types.js +6 -0
- package/dist/core/plugins/types.js.map +1 -0
- package/dist/core/session/index.d.ts +3 -0
- package/dist/core/session/index.d.ts.map +1 -0
- package/dist/core/session/index.js +3 -0
- package/dist/core/session/index.js.map +1 -0
- package/dist/core/session/manager.d.ts +57 -0
- package/dist/core/session/manager.d.ts.map +1 -0
- package/dist/core/session/manager.js +182 -0
- package/dist/core/session/manager.js.map +1 -0
- package/dist/core/session/storage.d.ts +42 -0
- package/dist/core/session/storage.d.ts.map +1 -0
- package/dist/core/session/storage.js +138 -0
- package/dist/core/session/storage.js.map +1 -0
- package/dist/core/tools/definitions/file.d.ts +6 -0
- package/dist/core/tools/definitions/file.d.ts.map +1 -0
- package/dist/core/tools/definitions/file.js +276 -0
- package/dist/core/tools/definitions/file.js.map +1 -0
- package/dist/core/tools/definitions/git.d.ts +6 -0
- package/dist/core/tools/definitions/git.d.ts.map +1 -0
- package/dist/core/tools/definitions/git.js +294 -0
- package/dist/core/tools/definitions/git.js.map +1 -0
- package/dist/core/tools/definitions/index.d.ts +11 -0
- package/dist/core/tools/definitions/index.d.ts.map +1 -0
- package/dist/core/tools/definitions/index.js +22 -0
- package/dist/core/tools/definitions/index.js.map +1 -0
- package/dist/core/tools/definitions/search.d.ts +6 -0
- package/dist/core/tools/definitions/search.d.ts.map +1 -0
- package/dist/core/tools/definitions/search.js +223 -0
- package/dist/core/tools/definitions/search.js.map +1 -0
- package/dist/core/tools/definitions/shell.d.ts +6 -0
- package/dist/core/tools/definitions/shell.d.ts.map +1 -0
- package/dist/core/tools/definitions/shell.js +190 -0
- package/dist/core/tools/definitions/shell.js.map +1 -0
- package/dist/core/tools/definitions/web.d.ts +6 -0
- package/dist/core/tools/definitions/web.d.ts.map +1 -0
- package/dist/core/tools/definitions/web.js +104 -0
- package/dist/core/tools/definitions/web.js.map +1 -0
- package/dist/core/tools/executor.d.ts +24 -0
- package/dist/core/tools/executor.d.ts.map +1 -0
- package/dist/core/tools/executor.js +111 -0
- package/dist/core/tools/executor.js.map +1 -0
- package/dist/core/tools/index.d.ts +4 -0
- package/dist/core/tools/index.d.ts.map +1 -0
- package/dist/core/tools/index.js +4 -0
- package/dist/core/tools/index.js.map +1 -0
- package/dist/core/tools/registry.d.ts +23 -0
- package/dist/core/tools/registry.d.ts.map +1 -0
- package/dist/core/tools/registry.js +28 -0
- package/dist/core/tools/registry.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +352 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
- package/src/cli/app.tsx +319 -0
- package/src/cli/commands/index.ts +261 -0
- package/src/config/constants.ts +45 -0
- package/src/config/index.ts +3 -0
- package/src/config/schema.ts +36 -0
- package/src/config/settings.ts +121 -0
- package/src/core/agents/agent.ts +205 -0
- package/src/core/agents/index.ts +2 -0
- package/src/core/agents/orchestrator.ts +223 -0
- package/src/core/api/client.ts +300 -0
- package/src/core/api/index.ts +2 -0
- package/src/core/api/types.ts +149 -0
- package/src/core/hooks/index.ts +2 -0
- package/src/core/hooks/manager.ts +212 -0
- package/src/core/hooks/types.ts +116 -0
- package/src/core/mcp/client.ts +198 -0
- package/src/core/mcp/index.ts +2 -0
- package/src/core/mcp/manager.ts +153 -0
- package/src/core/plugins/index.ts +2 -0
- package/src/core/plugins/loader.ts +312 -0
- package/src/core/plugins/types.ts +111 -0
- package/src/core/session/index.ts +2 -0
- package/src/core/session/manager.ts +246 -0
- package/src/core/session/storage.ts +184 -0
- package/src/core/tools/definitions/file.ts +312 -0
- package/src/core/tools/definitions/git.ts +326 -0
- package/src/core/tools/definitions/index.ts +24 -0
- package/src/core/tools/definitions/search.ts +266 -0
- package/src/core/tools/definitions/shell.ts +228 -0
- package/src/core/tools/definitions/web.ts +113 -0
- package/src/core/tools/executor.ts +145 -0
- package/src/core/tools/index.ts +3 -0
- package/src/core/tools/registry.ts +44 -0
- package/src/index.ts +407 -0
- package/tsconfig.json +40 -0
- package/vitest.config.ts +13 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bluehawks CLI - Session Storage
|
|
3
|
+
* Manages named sessions and session history
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as fs from 'node:fs/promises';
|
|
7
|
+
import * as path from 'node:path';
|
|
8
|
+
import * as os from 'node:os';
|
|
9
|
+
|
|
10
|
+
export interface StoredSession {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
startTime: string;
|
|
14
|
+
lastAccessTime: string;
|
|
15
|
+
projectPath: string;
|
|
16
|
+
model: string;
|
|
17
|
+
messageCount: number;
|
|
18
|
+
preview: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface SessionIndex {
|
|
22
|
+
lastSessionId: string | null;
|
|
23
|
+
sessions: Record<string, StoredSession>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const SESSIONS_DIR = path.join(os.homedir(), '.bluehawks', 'sessions');
|
|
27
|
+
const INDEX_FILE = 'index.json';
|
|
28
|
+
|
|
29
|
+
export class SessionStorage {
|
|
30
|
+
private indexPath: string;
|
|
31
|
+
private sessionsDir: string;
|
|
32
|
+
|
|
33
|
+
constructor() {
|
|
34
|
+
this.sessionsDir = SESSIONS_DIR;
|
|
35
|
+
this.indexPath = path.join(SESSIONS_DIR, INDEX_FILE);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async ensureDir(): Promise<void> {
|
|
39
|
+
await fs.mkdir(this.sessionsDir, { recursive: true });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async getIndex(): Promise<SessionIndex> {
|
|
43
|
+
try {
|
|
44
|
+
const content = await fs.readFile(this.indexPath, 'utf-8');
|
|
45
|
+
return JSON.parse(content);
|
|
46
|
+
} catch {
|
|
47
|
+
return { lastSessionId: null, sessions: {} };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async saveIndex(index: SessionIndex): Promise<void> {
|
|
52
|
+
await this.ensureDir();
|
|
53
|
+
await fs.writeFile(this.indexPath, JSON.stringify(index, null, 2));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async saveSession(
|
|
57
|
+
sessionId: string,
|
|
58
|
+
name: string | null,
|
|
59
|
+
data: unknown,
|
|
60
|
+
metadata: { projectPath: string; model: string; messageCount: number; preview: string }
|
|
61
|
+
): Promise<void> {
|
|
62
|
+
await this.ensureDir();
|
|
63
|
+
|
|
64
|
+
// Save session data
|
|
65
|
+
const sessionFile = path.join(this.sessionsDir, `${sessionId}.json`);
|
|
66
|
+
await fs.writeFile(sessionFile, JSON.stringify(data, null, 2));
|
|
67
|
+
|
|
68
|
+
// Update index
|
|
69
|
+
const index = await this.getIndex();
|
|
70
|
+
const storedSession: StoredSession = {
|
|
71
|
+
id: sessionId,
|
|
72
|
+
name: name || sessionId,
|
|
73
|
+
startTime: index.sessions[sessionId]?.startTime || new Date().toISOString(),
|
|
74
|
+
lastAccessTime: new Date().toISOString(),
|
|
75
|
+
projectPath: metadata.projectPath,
|
|
76
|
+
model: metadata.model,
|
|
77
|
+
messageCount: metadata.messageCount,
|
|
78
|
+
preview: metadata.preview,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
index.sessions[sessionId] = storedSession;
|
|
82
|
+
index.lastSessionId = sessionId;
|
|
83
|
+
await this.saveIndex(index);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async loadSession(sessionIdOrName: string): Promise<unknown | null> {
|
|
87
|
+
const index = await this.getIndex();
|
|
88
|
+
|
|
89
|
+
// Try to find by ID first
|
|
90
|
+
let sessionId = sessionIdOrName;
|
|
91
|
+
|
|
92
|
+
// If not found by ID, search by name
|
|
93
|
+
if (!index.sessions[sessionIdOrName]) {
|
|
94
|
+
const found = Object.values(index.sessions).find(
|
|
95
|
+
(s) => s.name.toLowerCase() === sessionIdOrName.toLowerCase()
|
|
96
|
+
);
|
|
97
|
+
if (found) {
|
|
98
|
+
sessionId = found.id;
|
|
99
|
+
} else {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const sessionFile = path.join(this.sessionsDir, `${sessionId}.json`);
|
|
106
|
+
const content = await fs.readFile(sessionFile, 'utf-8');
|
|
107
|
+
return JSON.parse(content);
|
|
108
|
+
} catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async loadLastSession(): Promise<{ id: string; data: unknown } | null> {
|
|
114
|
+
const index = await this.getIndex();
|
|
115
|
+
if (!index.lastSessionId) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const data = await this.loadSession(index.lastSessionId);
|
|
120
|
+
return data ? { id: index.lastSessionId, data } : null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async listSessions(limit = 10): Promise<StoredSession[]> {
|
|
124
|
+
const index = await this.getIndex();
|
|
125
|
+
return Object.values(index.sessions)
|
|
126
|
+
.sort((a, b) => new Date(b.lastAccessTime).getTime() - new Date(a.lastAccessTime).getTime())
|
|
127
|
+
.slice(0, limit);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async deleteSession(sessionIdOrName: string): Promise<boolean> {
|
|
131
|
+
const index = await this.getIndex();
|
|
132
|
+
|
|
133
|
+
let sessionId = sessionIdOrName;
|
|
134
|
+
if (!index.sessions[sessionIdOrName]) {
|
|
135
|
+
const found = Object.values(index.sessions).find(
|
|
136
|
+
(s) => s.name.toLowerCase() === sessionIdOrName.toLowerCase()
|
|
137
|
+
);
|
|
138
|
+
if (found) {
|
|
139
|
+
sessionId = found.id;
|
|
140
|
+
} else {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
const sessionFile = path.join(this.sessionsDir, `${sessionId}.json`);
|
|
147
|
+
await fs.unlink(sessionFile);
|
|
148
|
+
delete index.sessions[sessionId];
|
|
149
|
+
if (index.lastSessionId === sessionId) {
|
|
150
|
+
index.lastSessionId = null;
|
|
151
|
+
}
|
|
152
|
+
await this.saveIndex(index);
|
|
153
|
+
return true;
|
|
154
|
+
} catch {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async renameSession(sessionIdOrName: string, newName: string): Promise<boolean> {
|
|
160
|
+
const index = await this.getIndex();
|
|
161
|
+
|
|
162
|
+
let sessionId = sessionIdOrName;
|
|
163
|
+
if (!index.sessions[sessionIdOrName]) {
|
|
164
|
+
const found = Object.values(index.sessions).find(
|
|
165
|
+
(s) => s.name.toLowerCase() === sessionIdOrName.toLowerCase()
|
|
166
|
+
);
|
|
167
|
+
if (found) {
|
|
168
|
+
sessionId = found.id;
|
|
169
|
+
} else {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (index.sessions[sessionId]) {
|
|
175
|
+
index.sessions[sessionId].name = newName;
|
|
176
|
+
await this.saveIndex(index);
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export const sessionStorage = new SessionStorage();
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bluehawks CLI - File System Tools
|
|
3
|
+
* Tools for reading, writing, and editing files
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as fs from 'node:fs/promises';
|
|
7
|
+
import * as path from 'node:path';
|
|
8
|
+
import { toolRegistry, type ToolHandler } from '../registry.js';
|
|
9
|
+
import { MAX_FILE_SIZE_BYTES } from '../../../config/constants.js';
|
|
10
|
+
|
|
11
|
+
// Read File Tool
|
|
12
|
+
const readFileTool: ToolHandler = {
|
|
13
|
+
name: 'read_file',
|
|
14
|
+
safeToAutoRun: true,
|
|
15
|
+
definition: {
|
|
16
|
+
type: 'function',
|
|
17
|
+
function: {
|
|
18
|
+
name: 'read_file',
|
|
19
|
+
description:
|
|
20
|
+
'Read the contents of a file. Use this to understand code, configuration, or any text file.',
|
|
21
|
+
parameters: {
|
|
22
|
+
type: 'object',
|
|
23
|
+
properties: {
|
|
24
|
+
path: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
description: 'The absolute or relative path to the file to read.',
|
|
27
|
+
},
|
|
28
|
+
start_line: {
|
|
29
|
+
type: 'number',
|
|
30
|
+
description: 'Optional. The starting line number (1-indexed) to read from.',
|
|
31
|
+
},
|
|
32
|
+
end_line: {
|
|
33
|
+
type: 'number',
|
|
34
|
+
description: 'Optional. The ending line number (1-indexed) to read to.',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
required: ['path'],
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
async execute(args) {
|
|
42
|
+
const filePath = args.path as string;
|
|
43
|
+
const startLine = args.start_line as number | undefined;
|
|
44
|
+
const endLine = args.end_line as number | undefined;
|
|
45
|
+
|
|
46
|
+
const absolutePath = path.resolve(process.cwd(), filePath);
|
|
47
|
+
|
|
48
|
+
// Check if file exists
|
|
49
|
+
try {
|
|
50
|
+
await fs.access(absolutePath);
|
|
51
|
+
} catch {
|
|
52
|
+
throw new Error(`File not found: ${filePath}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Check file size
|
|
56
|
+
const stats = await fs.stat(absolutePath);
|
|
57
|
+
if (stats.size > MAX_FILE_SIZE_BYTES) {
|
|
58
|
+
throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES})`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let content = await fs.readFile(absolutePath, 'utf-8');
|
|
62
|
+
|
|
63
|
+
// Handle line range if specified
|
|
64
|
+
if (startLine !== undefined || endLine !== undefined) {
|
|
65
|
+
const lines = content.split('\n');
|
|
66
|
+
const start = Math.max(1, startLine || 1) - 1;
|
|
67
|
+
const end = Math.min(lines.length, endLine || lines.length);
|
|
68
|
+
content = lines.slice(start, end).join('\n');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return content;
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Write File Tool
|
|
76
|
+
const writeFileTool: ToolHandler = {
|
|
77
|
+
name: 'write_file',
|
|
78
|
+
safeToAutoRun: false,
|
|
79
|
+
definition: {
|
|
80
|
+
type: 'function',
|
|
81
|
+
function: {
|
|
82
|
+
name: 'write_file',
|
|
83
|
+
description:
|
|
84
|
+
'Write content to a file. Creates the file if it does not exist, or overwrites if it does.',
|
|
85
|
+
parameters: {
|
|
86
|
+
type: 'object',
|
|
87
|
+
properties: {
|
|
88
|
+
path: {
|
|
89
|
+
type: 'string',
|
|
90
|
+
description: 'The absolute or relative path to the file to write.',
|
|
91
|
+
},
|
|
92
|
+
content: {
|
|
93
|
+
type: 'string',
|
|
94
|
+
description: 'The content to write to the file.',
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
required: ['path', 'content'],
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
async execute(args) {
|
|
102
|
+
const filePath = args.path as string;
|
|
103
|
+
const content = args.content as string;
|
|
104
|
+
|
|
105
|
+
const absolutePath = path.resolve(process.cwd(), filePath);
|
|
106
|
+
|
|
107
|
+
// Create directory if it doesn't exist
|
|
108
|
+
const dir = path.dirname(absolutePath);
|
|
109
|
+
await fs.mkdir(dir, { recursive: true });
|
|
110
|
+
|
|
111
|
+
await fs.writeFile(absolutePath, content, 'utf-8');
|
|
112
|
+
|
|
113
|
+
return `Successfully wrote ${content.length} characters to ${filePath}`;
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Edit File Tool
|
|
118
|
+
const editFileTool: ToolHandler = {
|
|
119
|
+
name: 'edit_file',
|
|
120
|
+
safeToAutoRun: false,
|
|
121
|
+
definition: {
|
|
122
|
+
type: 'function',
|
|
123
|
+
function: {
|
|
124
|
+
name: 'edit_file',
|
|
125
|
+
description:
|
|
126
|
+
'Edit a file by replacing specific content. Use this to make targeted changes to existing files.',
|
|
127
|
+
parameters: {
|
|
128
|
+
type: 'object',
|
|
129
|
+
properties: {
|
|
130
|
+
path: {
|
|
131
|
+
type: 'string',
|
|
132
|
+
description: 'The absolute or relative path to the file to edit.',
|
|
133
|
+
},
|
|
134
|
+
old_content: {
|
|
135
|
+
type: 'string',
|
|
136
|
+
description: 'The exact content to find and replace.',
|
|
137
|
+
},
|
|
138
|
+
new_content: {
|
|
139
|
+
type: 'string',
|
|
140
|
+
description: 'The new content to replace with.',
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
required: ['path', 'old_content', 'new_content'],
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
async execute(args) {
|
|
148
|
+
const filePath = args.path as string;
|
|
149
|
+
const oldContent = args.old_content as string;
|
|
150
|
+
const newContent = args.new_content as string;
|
|
151
|
+
|
|
152
|
+
const absolutePath = path.resolve(process.cwd(), filePath);
|
|
153
|
+
|
|
154
|
+
// Read current content
|
|
155
|
+
const content = await fs.readFile(absolutePath, 'utf-8');
|
|
156
|
+
|
|
157
|
+
// Check if old content exists
|
|
158
|
+
if (!content.includes(oldContent)) {
|
|
159
|
+
throw new Error(`Could not find the specified content to replace in ${filePath}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Replace content
|
|
163
|
+
const newFileContent = content.replace(oldContent, newContent);
|
|
164
|
+
|
|
165
|
+
// Write back
|
|
166
|
+
await fs.writeFile(absolutePath, newFileContent, 'utf-8');
|
|
167
|
+
|
|
168
|
+
return `Successfully edited ${filePath}`;
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// List Directory Tool
|
|
173
|
+
const listDirTool: ToolHandler = {
|
|
174
|
+
name: 'list_directory',
|
|
175
|
+
safeToAutoRun: true,
|
|
176
|
+
definition: {
|
|
177
|
+
type: 'function',
|
|
178
|
+
function: {
|
|
179
|
+
name: 'list_directory',
|
|
180
|
+
description: 'List the contents of a directory, including files and subdirectories.',
|
|
181
|
+
parameters: {
|
|
182
|
+
type: 'object',
|
|
183
|
+
properties: {
|
|
184
|
+
path: {
|
|
185
|
+
type: 'string',
|
|
186
|
+
description: 'The absolute or relative path to the directory to list.',
|
|
187
|
+
},
|
|
188
|
+
recursive: {
|
|
189
|
+
type: 'boolean',
|
|
190
|
+
description: 'Whether to list recursively. Default is false.',
|
|
191
|
+
},
|
|
192
|
+
max_depth: {
|
|
193
|
+
type: 'number',
|
|
194
|
+
description: 'Maximum depth to recurse. Default is 3.',
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
required: ['path'],
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
async execute(args) {
|
|
202
|
+
const dirPath = args.path as string;
|
|
203
|
+
const recursive = (args.recursive as boolean) ?? false;
|
|
204
|
+
const maxDepth = (args.max_depth as number) ?? 3;
|
|
205
|
+
|
|
206
|
+
const absolutePath = path.resolve(process.cwd(), dirPath);
|
|
207
|
+
|
|
208
|
+
async function listDir(dir: string, depth: number): Promise<string[]> {
|
|
209
|
+
if (depth > maxDepth) return [];
|
|
210
|
+
|
|
211
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
212
|
+
const results: string[] = [];
|
|
213
|
+
|
|
214
|
+
for (const entry of entries) {
|
|
215
|
+
// Skip hidden files and common excludes
|
|
216
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
|
|
217
|
+
|
|
218
|
+
const relativePath = path.relative(absolutePath, path.join(dir, entry.name));
|
|
219
|
+
const prefix = entry.isDirectory() ? '📁 ' : '📄 ';
|
|
220
|
+
results.push(prefix + relativePath);
|
|
221
|
+
|
|
222
|
+
if (recursive && entry.isDirectory()) {
|
|
223
|
+
const subEntries = await listDir(path.join(dir, entry.name), depth + 1);
|
|
224
|
+
results.push(...subEntries);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return results;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const entries = await listDir(absolutePath, 0);
|
|
232
|
+
return entries.length > 0 ? entries.join('\n') : 'Directory is empty';
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
// Create Directory Tool
|
|
237
|
+
const createDirTool: ToolHandler = {
|
|
238
|
+
name: 'create_directory',
|
|
239
|
+
safeToAutoRun: false,
|
|
240
|
+
definition: {
|
|
241
|
+
type: 'function',
|
|
242
|
+
function: {
|
|
243
|
+
name: 'create_directory',
|
|
244
|
+
description: 'Create a new directory, including any necessary parent directories.',
|
|
245
|
+
parameters: {
|
|
246
|
+
type: 'object',
|
|
247
|
+
properties: {
|
|
248
|
+
path: {
|
|
249
|
+
type: 'string',
|
|
250
|
+
description: 'The absolute or relative path of the directory to create.',
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
required: ['path'],
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
async execute(args) {
|
|
258
|
+
const dirPath = args.path as string;
|
|
259
|
+
const absolutePath = path.resolve(process.cwd(), dirPath);
|
|
260
|
+
|
|
261
|
+
await fs.mkdir(absolutePath, { recursive: true });
|
|
262
|
+
|
|
263
|
+
return `Successfully created directory: ${dirPath}`;
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// Delete File Tool
|
|
268
|
+
const deleteFileTool: ToolHandler = {
|
|
269
|
+
name: 'delete_file',
|
|
270
|
+
safeToAutoRun: false,
|
|
271
|
+
definition: {
|
|
272
|
+
type: 'function',
|
|
273
|
+
function: {
|
|
274
|
+
name: 'delete_file',
|
|
275
|
+
description: 'Delete a file or directory.',
|
|
276
|
+
parameters: {
|
|
277
|
+
type: 'object',
|
|
278
|
+
properties: {
|
|
279
|
+
path: {
|
|
280
|
+
type: 'string',
|
|
281
|
+
description: 'The absolute or relative path to delete.',
|
|
282
|
+
},
|
|
283
|
+
recursive: {
|
|
284
|
+
type: 'boolean',
|
|
285
|
+
description: 'If true, recursively delete directories. Default is false.',
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
required: ['path'],
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
async execute(args) {
|
|
293
|
+
const filePath = args.path as string;
|
|
294
|
+
const recursive = (args.recursive as boolean) ?? false;
|
|
295
|
+
|
|
296
|
+
const absolutePath = path.resolve(process.cwd(), filePath);
|
|
297
|
+
|
|
298
|
+
await fs.rm(absolutePath, { recursive, force: false });
|
|
299
|
+
|
|
300
|
+
return `Successfully deleted: ${filePath}`;
|
|
301
|
+
},
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
// Register all file tools
|
|
305
|
+
export function registerFileTools(): void {
|
|
306
|
+
toolRegistry.register(readFileTool);
|
|
307
|
+
toolRegistry.register(writeFileTool);
|
|
308
|
+
toolRegistry.register(editFileTool);
|
|
309
|
+
toolRegistry.register(listDirTool);
|
|
310
|
+
toolRegistry.register(createDirTool);
|
|
311
|
+
toolRegistry.register(deleteFileTool);
|
|
312
|
+
}
|