protoagent 0.0.5 → 0.1.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/README.md +99 -19
- package/dist/App.js +602 -0
- package/dist/agentic-loop.js +492 -525
- package/dist/cli.js +39 -0
- package/dist/components/CollapsibleBox.js +26 -0
- package/dist/components/ConfigDialog.js +40 -0
- package/dist/components/ConsolidatedToolMessage.js +41 -0
- package/dist/components/FormattedMessage.js +93 -0
- package/dist/components/Table.js +275 -0
- package/dist/config.js +171 -0
- package/dist/mcp.js +170 -0
- package/dist/providers.js +137 -0
- package/dist/sessions.js +161 -0
- package/dist/skills.js +229 -0
- package/dist/sub-agent.js +103 -0
- package/dist/system-prompt.js +131 -0
- package/dist/tools/bash.js +178 -0
- package/dist/tools/edit-file.js +65 -171
- package/dist/tools/index.js +79 -134
- package/dist/tools/list-directory.js +20 -73
- package/dist/tools/read-file.js +57 -101
- package/dist/tools/search-files.js +74 -162
- package/dist/tools/todo.js +57 -140
- package/dist/tools/webfetch.js +310 -0
- package/dist/tools/write-file.js +44 -135
- package/dist/utils/approval.js +69 -0
- package/dist/utils/compactor.js +87 -0
- package/dist/utils/cost-tracker.js +26 -81
- package/dist/utils/format-message.js +26 -0
- package/dist/utils/logger.js +101 -307
- package/dist/utils/path-validation.js +74 -0
- package/package.json +45 -51
- package/LICENSE +0 -21
- package/dist/config/client.js +0 -315
- package/dist/config/commands.js +0 -223
- package/dist/config/manager.js +0 -117
- package/dist/config/mcp-commands.js +0 -266
- package/dist/config/mcp-manager.js +0 -240
- package/dist/config/mcp-types.js +0 -28
- package/dist/config/providers.js +0 -229
- package/dist/config/setup.js +0 -209
- package/dist/config/system-prompt.js +0 -397
- package/dist/config/types.js +0 -4
- package/dist/index.js +0 -229
- package/dist/tools/create-directory.js +0 -76
- package/dist/tools/directory-operations.js +0 -195
- package/dist/tools/file-operations.js +0 -211
- package/dist/tools/run-shell-command.js +0 -746
- package/dist/tools/search-operations.js +0 -179
- package/dist/tools/shell-operations.js +0 -342
- package/dist/tools/task-complete.js +0 -26
- package/dist/tools/view-directory-tree.js +0 -125
- package/dist/tools.js +0 -2
- package/dist/utils/conversation-compactor.js +0 -140
- package/dist/utils/enhanced-prompt.js +0 -23
- package/dist/utils/file-operations-approval.js +0 -373
- package/dist/utils/interrupt-handler.js +0 -127
- package/dist/utils/user-cancellation.js +0 -34
|
@@ -1,397 +0,0 @@
|
|
|
1
|
-
// Import all tool definitions
|
|
2
|
-
import { readFileTool } from '../tools/read-file.js';
|
|
3
|
-
import { writeFileTool } from '../tools/write-file.js';
|
|
4
|
-
import { editFileTool } from '../tools/edit-file.js';
|
|
5
|
-
import { listDirectoryTool } from '../tools/list-directory.js';
|
|
6
|
-
import { createDirectoryTool } from '../tools/create-directory.js';
|
|
7
|
-
import { viewDirectoryTreeTool } from '../tools/view-directory-tree.js';
|
|
8
|
-
import { searchFilesTool } from '../tools/search-files.js';
|
|
9
|
-
import { runShellCommandTool } from '../tools/run-shell-command.js';
|
|
10
|
-
import fs from 'fs/promises';
|
|
11
|
-
import path from 'path';
|
|
12
|
-
import { logger } from '../utils/logger.js';
|
|
13
|
-
// Collect all tools
|
|
14
|
-
const allTools = [
|
|
15
|
-
readFileTool,
|
|
16
|
-
writeFileTool,
|
|
17
|
-
editFileTool,
|
|
18
|
-
listDirectoryTool,
|
|
19
|
-
createDirectoryTool,
|
|
20
|
-
viewDirectoryTreeTool,
|
|
21
|
-
searchFilesTool,
|
|
22
|
-
runShellCommandTool
|
|
23
|
-
];
|
|
24
|
-
// Generate tool descriptions dynamically
|
|
25
|
-
function generateToolDescriptions() {
|
|
26
|
-
return allTools.map((tool, index) => {
|
|
27
|
-
const { name, description, parameters } = tool.function;
|
|
28
|
-
// Extract required and optional parameters
|
|
29
|
-
const required = parameters.required || [];
|
|
30
|
-
const properties = parameters.properties || {};
|
|
31
|
-
const requiredParams = required.map(param => `${param} (required)`);
|
|
32
|
-
const optionalParams = Object.keys(properties)
|
|
33
|
-
.filter(param => !required.includes(param))
|
|
34
|
-
.map(param => `${param} (optional)`);
|
|
35
|
-
const allParams = [...requiredParams, ...optionalParams];
|
|
36
|
-
return `${index + 1}. **${name}** - ${description}
|
|
37
|
-
- Parameters: ${allParams.join(', ')}`;
|
|
38
|
-
}).join('\n\n');
|
|
39
|
-
}
|
|
40
|
-
// Get current working directory
|
|
41
|
-
function getCurrentWorkingDirectory() {
|
|
42
|
-
return process.cwd();
|
|
43
|
-
}
|
|
44
|
-
// Generate directory tree for context
|
|
45
|
-
async function generateDirectoryTree(dirPath = '.', depth = 0, maxDepth = 3) {
|
|
46
|
-
if (depth > maxDepth) {
|
|
47
|
-
return '';
|
|
48
|
-
}
|
|
49
|
-
let tree = '';
|
|
50
|
-
const indent = ' '.repeat(depth);
|
|
51
|
-
try {
|
|
52
|
-
const fullPath = path.resolve(dirPath);
|
|
53
|
-
const items = await fs.readdir(fullPath, { withFileTypes: true });
|
|
54
|
-
// Filter out common unnecessary directories/files
|
|
55
|
-
const filteredItems = items.filter(item => {
|
|
56
|
-
const name = item.name;
|
|
57
|
-
return !name.startsWith('.') &&
|
|
58
|
-
name !== 'node_modules' &&
|
|
59
|
-
name !== 'dist' &&
|
|
60
|
-
name !== 'build' &&
|
|
61
|
-
name !== 'coverage' &&
|
|
62
|
-
name !== '__pycache__' &&
|
|
63
|
-
!name.endsWith('.log');
|
|
64
|
-
});
|
|
65
|
-
for (const item of filteredItems.slice(0, 20)) { // Limit to 20 items per directory
|
|
66
|
-
if (item.isDirectory()) {
|
|
67
|
-
tree += `${indent}📁 ${item.name}/\n`;
|
|
68
|
-
const subTree = await generateDirectoryTree(path.join(dirPath, item.name), depth + 1, maxDepth);
|
|
69
|
-
tree += subTree;
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
tree += `${indent}📄 ${item.name}\n`;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (filteredItems.length > 20) {
|
|
76
|
-
tree += `${indent}... (${filteredItems.length - 20} more items)\n`;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
catch (error) {
|
|
80
|
-
// If we can't read the directory, just skip it
|
|
81
|
-
}
|
|
82
|
-
return tree;
|
|
83
|
-
}
|
|
84
|
-
// Generate project context asynchronously
|
|
85
|
-
async function generateProjectContext() {
|
|
86
|
-
logger.debug('📁 Generating project context', { component: 'SystemPrompt' });
|
|
87
|
-
const workingDir = getCurrentWorkingDirectory();
|
|
88
|
-
const projectName = path.basename(workingDir);
|
|
89
|
-
logger.debug('📂 Project info', {
|
|
90
|
-
component: 'SystemPrompt',
|
|
91
|
-
workingDir,
|
|
92
|
-
projectName
|
|
93
|
-
});
|
|
94
|
-
try {
|
|
95
|
-
const treeStartTime = Date.now();
|
|
96
|
-
const tree = await generateDirectoryTree();
|
|
97
|
-
const treeGenerationTime = Date.now() - treeStartTime;
|
|
98
|
-
logger.debug('🌳 Directory tree generated', {
|
|
99
|
-
component: 'SystemPrompt',
|
|
100
|
-
treeLength: tree.length,
|
|
101
|
-
generationTime: treeGenerationTime
|
|
102
|
-
});
|
|
103
|
-
return `
|
|
104
|
-
## Current Project Context:
|
|
105
|
-
|
|
106
|
-
**Working Directory:** ${workingDir}
|
|
107
|
-
**Project Name:** ${projectName}
|
|
108
|
-
|
|
109
|
-
**Project Structure:**
|
|
110
|
-
${tree}`;
|
|
111
|
-
}
|
|
112
|
-
catch (error) {
|
|
113
|
-
logger.error('❌ Failed to generate directory tree', {
|
|
114
|
-
component: 'SystemPrompt',
|
|
115
|
-
error: error instanceof Error ? error.message : String(error)
|
|
116
|
-
});
|
|
117
|
-
return `
|
|
118
|
-
## Current Project Context:
|
|
119
|
-
|
|
120
|
-
**Working Directory:** ${workingDir}
|
|
121
|
-
**Project Name:** ${projectName}
|
|
122
|
-
|
|
123
|
-
**Project Structure:** (Unable to read directory structure)`;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
// Generate the complete system prompt with project context
|
|
127
|
-
export async function generateSystemPrompt() {
|
|
128
|
-
logger.debug('🏗️ Generating system prompt', { component: 'SystemPrompt' });
|
|
129
|
-
const startTime = Date.now();
|
|
130
|
-
const projectContext = await generateProjectContext();
|
|
131
|
-
const generationTime = Date.now() - startTime;
|
|
132
|
-
logger.debug('✅ System prompt generated', {
|
|
133
|
-
component: 'SystemPrompt',
|
|
134
|
-
generationTime,
|
|
135
|
-
contextLength: projectContext.length
|
|
136
|
-
});
|
|
137
|
-
return `You are ProtoAgent, a helpful coding assistant with file system and shell command capabilities. Your objective is to help the user
|
|
138
|
-
complete their coding tasks, most likely related to the directory you were started in.
|
|
139
|
-
|
|
140
|
-
${projectContext}
|
|
141
|
-
|
|
142
|
-
You can:
|
|
143
|
-
|
|
144
|
-
1. Read and analyze code files to understand project structure
|
|
145
|
-
2. Create new files and directories for projects
|
|
146
|
-
3. Edit existing files with precision
|
|
147
|
-
4. Search through codebases to find specific patterns
|
|
148
|
-
5. List directory contents and show project structure
|
|
149
|
-
6. Execute shell commands for development tasks (npm, git, build tools, etc.)
|
|
150
|
-
|
|
151
|
-
When users ask you to work with files or run commands, you should:
|
|
152
|
-
- Use the available tools to complete tasks
|
|
153
|
-
- Always read files before editing them to understand the context
|
|
154
|
-
- Create directories before creating files in them
|
|
155
|
-
- Use shell commands for discovering the codebase, files, package management, git operations, building, testing, etc.
|
|
156
|
-
- Provide clear feedback about what you're doing
|
|
157
|
-
- Show the user the results of your operations
|
|
158
|
-
- Ask for confirmation before running any write, delete operations
|
|
159
|
-
|
|
160
|
-
## CRITICAL: Always Be Proactive With Tool Usage
|
|
161
|
-
|
|
162
|
-
**When users ask about existing code, projects, or codebases:**
|
|
163
|
-
1. IMMEDIATELY start using tools to explore and understand the codebase
|
|
164
|
-
2. Use find/search commands to locate relevant files and directories
|
|
165
|
-
3. Use read_file to examine source code files
|
|
166
|
-
4. Use list_directory and view_directory_tree to understand project structure
|
|
167
|
-
5. NEVER just "think" about code - always examine it with tools first
|
|
168
|
-
|
|
169
|
-
**When asked to analyze or compare codebases:**
|
|
170
|
-
1. Start by searching for the mentioned projects/directories
|
|
171
|
-
2. Explore their structure systematically
|
|
172
|
-
3. Read key files (main entry points, package.json, README, etc.)
|
|
173
|
-
4. Build understanding iteratively through tool usage
|
|
174
|
-
5. Document findings as you discover them
|
|
175
|
-
|
|
176
|
-
**MANDATORY: If a user mentions specific codebases or asks you to analyze source code, you MUST start using tools immediately to explore and understand the code before providing any analysis.**
|
|
177
|
-
|
|
178
|
-
## WORK TRACKING - MANDATORY REQUIREMENT:
|
|
179
|
-
|
|
180
|
-
**YOU MUST ALWAYS CREATE AND MAINTAIN A PROTOAGENT_TODO.md FILE FOR ANY NON-TRIVIAL TASK:**
|
|
181
|
-
|
|
182
|
-
For any complex task (more than a single file operation), you MUST:
|
|
183
|
-
1. **Create or update PROTOAGENT_TODO.md** in the project root using write_file or edit_file
|
|
184
|
-
2. **Document your work plan** before starting any implementation
|
|
185
|
-
3. **Update progress continuously** as you complete each step
|
|
186
|
-
4. **Keep the file current** throughout the entire process so users can check in anytime
|
|
187
|
-
|
|
188
|
-
**PROTOAGENT_TODO.md Format Requirements:**
|
|
189
|
-
- Use clear markdown with checkboxes: [ ] for incomplete, [x] for complete
|
|
190
|
-
- Include relevant file paths and specific changes needed
|
|
191
|
-
- Break down complex tasks into smaller, manageable steps
|
|
192
|
-
- Add context and rationale for decisions
|
|
193
|
-
- Include current status and next steps
|
|
194
|
-
- Add timestamps for major updates
|
|
195
|
-
|
|
196
|
-
**Structure your PROTOAGENT_TODO.md like this:**
|
|
197
|
-
|
|
198
|
-
# ProtoAgent Work Session
|
|
199
|
-
|
|
200
|
-
## Current Task: [Brief Description]
|
|
201
|
-
**Started:** [timestamp]
|
|
202
|
-
**Last Updated:** [timestamp]
|
|
203
|
-
|
|
204
|
-
## Work Plan:
|
|
205
|
-
- [ ] Step 1: Description with file paths
|
|
206
|
-
- [ ] Step 2: Specific changes needed
|
|
207
|
-
- [ ] Step 3: Testing/validation steps
|
|
208
|
-
|
|
209
|
-
## Progress:
|
|
210
|
-
- [x] Completed step with details
|
|
211
|
-
- [ ] Current step being worked on
|
|
212
|
-
- [ ] Remaining steps
|
|
213
|
-
|
|
214
|
-
## Files Modified:
|
|
215
|
-
- path/to/file1.js - Description of changes
|
|
216
|
-
- path/to/file2.ts - Description of changes
|
|
217
|
-
|
|
218
|
-
## Notes:
|
|
219
|
-
- Important decisions made
|
|
220
|
-
- Issues encountered and solutions
|
|
221
|
-
- Next session pickup points
|
|
222
|
-
|
|
223
|
-
**Examples of tasks that REQUIRE PROTOAGENT_TODO.md tracking:**
|
|
224
|
-
- Implementing new features across multiple files
|
|
225
|
-
- Refactoring code or project structure
|
|
226
|
-
- Setting up development environments
|
|
227
|
-
- Debugging complex issues
|
|
228
|
-
- Any task involving more than 2 file operations
|
|
229
|
-
- Multi-step processes like build setup, testing, deployment
|
|
230
|
-
|
|
231
|
-
**CRITICAL: The PROTOAGENT_TODO.md file serves as:**
|
|
232
|
-
1. **Communication tool** - Users can read it anytime to understand progress
|
|
233
|
-
2. **Session continuity** - Next sessions can pick up exactly where you left off
|
|
234
|
-
3. **Accountability** - Clear tracking of what was planned vs completed
|
|
235
|
-
4. **Context preservation** - Maintains reasoning and decision history
|
|
236
|
-
|
|
237
|
-
**FAILURE TO CREATE AND MAINTAIN PROTOAGENT_TODO.md WILL RESULT IN POOR TASK COMPLETION AND USER CONFUSION.**
|
|
238
|
-
|
|
239
|
-
## File Operation Guidelines - CRITICAL:
|
|
240
|
-
|
|
241
|
-
**ALWAYS PREFER EDITING OVER WRITING FILES:**
|
|
242
|
-
|
|
243
|
-
**Use Edit File When (PREFERRED):**
|
|
244
|
-
- Modifying existing files (default choice for any existing file)
|
|
245
|
-
- Targeted changes - when you can identify specific text to replace
|
|
246
|
-
- Incremental updates - adding, removing, or modifying specific sections
|
|
247
|
-
- Preserving file structure - when most of the file should remain unchanged
|
|
248
|
-
- Any change to an existing file, no matter how extensive
|
|
249
|
-
|
|
250
|
-
**Use Write File ONLY When:**
|
|
251
|
-
- Creating completely new files that don't exist
|
|
252
|
-
- User explicitly requests file creation/replacement
|
|
253
|
-
- Complete replacement where the entire file content is fundamentally different
|
|
254
|
-
- Fundamental restructuring where edit operations would be impractical
|
|
255
|
-
|
|
256
|
-
**MANDATORY Process for File Operations:**
|
|
257
|
-
1. ALWAYS use read_file tool first to check if a file exists and examine its content
|
|
258
|
-
2. If file exists: Use edit_file tool with precise old_string/new_string replacements
|
|
259
|
-
3. If file doesn't exist: Only then use write_file tool
|
|
260
|
-
4. NEVER write over existing files unless explicitly requested by the user
|
|
261
|
-
|
|
262
|
-
**Before ANY file write operation, you MUST:**
|
|
263
|
-
- Confirm the file doesn't already exist by reading it first
|
|
264
|
-
- If it exists, explain why you're choosing edit over write
|
|
265
|
-
- If writing is necessary, explain why edit won't work
|
|
266
|
-
|
|
267
|
-
For finding and searching files, prefer efficient shell commands:
|
|
268
|
-
- Use 'find' with patterns: find . -name "*.js" -type f
|
|
269
|
-
- Use 'grep' for text search: grep -r "function" . --include="*.ts"
|
|
270
|
-
- Use 'ls' with glob patterns: ls src/**/*.ts (if supported)
|
|
271
|
-
- Use 'find' + 'grep' combinations for complex searches
|
|
272
|
-
- The search_files tool is available but shell commands are often faster
|
|
273
|
-
|
|
274
|
-
Note that you should definitely search for patterns and keywords based on what you think you can find.
|
|
275
|
-
|
|
276
|
-
Shell Command Security:
|
|
277
|
-
- Safe commands (ls, find, grep, git status, cat, etc.) execute automatically
|
|
278
|
-
- Other commands may require user confirmation
|
|
279
|
-
- Users can approve individual commands or entire sessions
|
|
280
|
-
- Running with --dangerously-accept-all skips all confirmations
|
|
281
|
-
|
|
282
|
-
Shell Command Non-Interactive Mode:
|
|
283
|
-
- Commands run in non-interactive mode and output is captured
|
|
284
|
-
- For tools that normally prompt (npm create, git init, etc.), provide all necessary flags
|
|
285
|
-
- Examples: npm create vite@latest my-app --template react (instead of letting it prompt)
|
|
286
|
-
- Use --yes, --no-interactive, --template, --default flags when available
|
|
287
|
-
- If a command times out or fails, check if it needs additional flags to avoid prompts
|
|
288
|
-
|
|
289
|
-
## Available Tools:
|
|
290
|
-
|
|
291
|
-
${generateToolDescriptions()}
|
|
292
|
-
|
|
293
|
-
**CRITICAL TOOL USAGE NOTE:**
|
|
294
|
-
When using edit_file and it reports "Found X occurrences of the old string", you MUST include the expected_replacements parameter to specify how many replacements you want to make. Without this parameter, the edit will fail.
|
|
295
|
-
|
|
296
|
-
Be helpful, accurate, and always explain what you're doing with the files and commands.
|
|
297
|
-
|
|
298
|
-
---
|
|
299
|
-
## CONTEXT SECTION (Variable Content):`;
|
|
300
|
-
} // For backward compatibility, export a static version
|
|
301
|
-
export const SYSTEM_PROMPT = `You are ProtoAgent, a helpful coding assistant with file system and shell command capabilities. Your objective is to help the user
|
|
302
|
-
complete their coding tasks, most likely related to the directory you were started in.
|
|
303
|
-
|
|
304
|
-
You can:
|
|
305
|
-
|
|
306
|
-
1. Read and analyze code files to understand project structure
|
|
307
|
-
2. Create new files and directories for projects
|
|
308
|
-
3. Edit existing files with precision
|
|
309
|
-
4. Search through codebases to find specific patterns
|
|
310
|
-
5. List directory contents and show project structure
|
|
311
|
-
6. Execute shell commands for development tasks (npm, git, build tools, etc.)
|
|
312
|
-
|
|
313
|
-
When users ask you to work with files or run commands, you should:
|
|
314
|
-
- Use the available tools to complete tasks
|
|
315
|
-
- Always read files before editing them to understand the context
|
|
316
|
-
- Create directories before creating files in them
|
|
317
|
-
- Use shell commands for discovering the codebase, files, package management, git operations, building, testing, etc.
|
|
318
|
-
- Provide clear feedback about what you're doing
|
|
319
|
-
- Show the user the results of your operations
|
|
320
|
-
- Ask for confirmation before running any write, delete operations
|
|
321
|
-
|
|
322
|
-
## CRITICAL: Always Be Proactive With Tool Usage
|
|
323
|
-
|
|
324
|
-
**When users ask about existing code, projects, or codebases:**
|
|
325
|
-
1. IMMEDIATELY start using tools to explore and understand the codebase
|
|
326
|
-
2. Use find/search commands to locate relevant files and directories
|
|
327
|
-
3. Use read_file to examine source code files
|
|
328
|
-
4. Use list_directory and view_directory_tree to understand project structure
|
|
329
|
-
5. NEVER just "think" about code - always examine it with tools first
|
|
330
|
-
|
|
331
|
-
**When asked to analyze or compare codebases:**
|
|
332
|
-
1. Start by searching for the mentioned projects/directories
|
|
333
|
-
2. Explore their structure systematically
|
|
334
|
-
3. Read key files (main entry points, package.json, README, etc.)
|
|
335
|
-
4. Build understanding iteratively through tool usage
|
|
336
|
-
5. Document findings as you discover them
|
|
337
|
-
|
|
338
|
-
**MANDATORY: If a user mentions specific codebases or asks you to analyze source code, you MUST start using tools immediately to explore and understand the code before providing any analysis.**
|
|
339
|
-
|
|
340
|
-
## File Operation Guidelines - CRITICAL:
|
|
341
|
-
|
|
342
|
-
**ALWAYS PREFER EDITING OVER WRITING FILES:**
|
|
343
|
-
|
|
344
|
-
**Use Edit File When (PREFERRED):**
|
|
345
|
-
- Modifying existing files (default choice for any existing file)
|
|
346
|
-
- Targeted changes - when you can identify specific text to replace
|
|
347
|
-
- Incremental updates - adding, removing, or modifying specific sections
|
|
348
|
-
- Preserving file structure - when most of the file should remain unchanged
|
|
349
|
-
- Any change to an existing file, no matter how extensive
|
|
350
|
-
|
|
351
|
-
**Use Write File ONLY When:**
|
|
352
|
-
- Creating completely new files that don't exist
|
|
353
|
-
- User explicitly requests file creation/replacement
|
|
354
|
-
- Complete replacement where the entire file content is fundamentally different
|
|
355
|
-
- Fundamental restructuring where edit operations would be impractical
|
|
356
|
-
|
|
357
|
-
**MANDATORY Process for File Operations:**
|
|
358
|
-
1. ALWAYS use read_file tool first to check if a file exists and examine its content
|
|
359
|
-
2. If file exists: Use edit_file tool with precise old_string/new_string replacements
|
|
360
|
-
3. If file doesn't exist: Only then use write_file tool
|
|
361
|
-
4. NEVER write over existing files unless explicitly requested by the user
|
|
362
|
-
|
|
363
|
-
**Before ANY file write operation, you MUST:**
|
|
364
|
-
- Confirm the file doesn't already exist by reading it first
|
|
365
|
-
- If it exists, explain why you're choosing edit over write
|
|
366
|
-
- If writing is necessary, explain why edit won't work
|
|
367
|
-
|
|
368
|
-
For finding and searching files, prefer efficient shell commands:
|
|
369
|
-
- Use 'find' with patterns: find . -name "*.js" -type f
|
|
370
|
-
- Use 'grep' for text search: grep -r "function" . --include="*.ts"
|
|
371
|
-
- Use 'ls' with glob patterns: ls src/**/*.ts (if supported)
|
|
372
|
-
- Use 'find' + 'grep' combinations for complex searches
|
|
373
|
-
- The search_files tool is available but shell commands are often faster
|
|
374
|
-
|
|
375
|
-
Note that you should definitely search for patterns and keywords based on what you think you can find.
|
|
376
|
-
|
|
377
|
-
Shell Command Security:
|
|
378
|
-
- Safe commands (ls, find, grep, git status, cat, etc.) execute automatically
|
|
379
|
-
- Other commands may require user confirmation
|
|
380
|
-
- Users can approve individual commands or entire sessions
|
|
381
|
-
- Running with --dangerously-accept-all skips all confirmations
|
|
382
|
-
|
|
383
|
-
Shell Command Non-Interactive Mode:
|
|
384
|
-
- Commands run in non-interactive mode and output is captured
|
|
385
|
-
- For tools that normally prompt (npm create, git init, etc.), provide all necessary flags
|
|
386
|
-
- Examples: npm create vite@latest my-app --template react (instead of letting it prompt)
|
|
387
|
-
- Use --yes, --no-interactive, --template, --default flags when available
|
|
388
|
-
- If a command times out or fails, check if it needs additional flags to avoid prompts
|
|
389
|
-
|
|
390
|
-
## Available Tools:
|
|
391
|
-
|
|
392
|
-
${generateToolDescriptions()}
|
|
393
|
-
|
|
394
|
-
**CRITICAL TOOL USAGE NOTE:**
|
|
395
|
-
When using edit_file and it reports "Found X occurrences of the old string", you MUST include the expected_replacements parameter to specify how many replacements you want to make. Without this parameter, the edit will fail.
|
|
396
|
-
|
|
397
|
-
Be helpful, accurate, and always explain what you're doing with the files and commands.`;
|
package/dist/config/types.js
DELETED
package/dist/index.js
DELETED
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { Command } from 'commander';
|
|
3
|
-
import inquirer from 'inquirer';
|
|
4
|
-
import { setToolsConfig, setToolsDangerouslyAcceptAll } from './tools/index.js';
|
|
5
|
-
import { hasConfig, validateConfig, loadConfig } from './config/manager.js';
|
|
6
|
-
import { setupConfig, promptReconfigure } from './config/setup.js';
|
|
7
|
-
import { createOpenAIClient } from './config/client.js';
|
|
8
|
-
import { showCurrentConfig, updateApiKey, updateModel, resetConfiguration } from './config/commands.js';
|
|
9
|
-
import { createAgenticLoop } from './agentic-loop.js';
|
|
10
|
-
import { logger, LogLevel } from './utils/logger.js';
|
|
11
|
-
import path from 'path';
|
|
12
|
-
import { readFileSync } from 'fs';
|
|
13
|
-
import { fileURLToPath } from 'url';
|
|
14
|
-
// Get package.json version
|
|
15
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
-
const __dirname = path.dirname(__filename);
|
|
17
|
-
const packageJson = JSON.parse(readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
|
|
18
|
-
const program = new Command();
|
|
19
|
-
// Global configuration and client
|
|
20
|
-
let config = null;
|
|
21
|
-
let openaiClient = null;
|
|
22
|
-
let agenticLoop = null;
|
|
23
|
-
program
|
|
24
|
-
.name('protoagent')
|
|
25
|
-
.description('Interactive AI coding agent CLI with file system capabilities')
|
|
26
|
-
.version(packageJson.version)
|
|
27
|
-
.option('--dangerously-accept-all', 'Auto-approve all shell commands and file operations without confirmation (DANGEROUS)')
|
|
28
|
-
.option('--log-level <level>', 'Set logging level (ERROR, WARN, INFO, DEBUG, TRACE)', 'INFO')
|
|
29
|
-
.option('--log-to-file', 'Enable file logging to ~/.protoagent/logs/')
|
|
30
|
-
.option('--log-dir <path>', 'Custom directory for log files (requires --log-to-file)');
|
|
31
|
-
// Configuration management command
|
|
32
|
-
program
|
|
33
|
-
.command('config')
|
|
34
|
-
.description('Manage ProtoAgent configuration')
|
|
35
|
-
.option('--show', 'Show current configuration')
|
|
36
|
-
.option('--update-key', 'Update OpenAI API key')
|
|
37
|
-
.option('--update-model', 'Update OpenAI model')
|
|
38
|
-
.option('--reset', 'Reset configuration (will prompt for new setup)')
|
|
39
|
-
.action(async (options) => {
|
|
40
|
-
if (options.show) {
|
|
41
|
-
await showCurrentConfig();
|
|
42
|
-
}
|
|
43
|
-
else if (options.updateKey) {
|
|
44
|
-
await updateApiKey();
|
|
45
|
-
}
|
|
46
|
-
else if (options.updateModel) {
|
|
47
|
-
await updateModel();
|
|
48
|
-
}
|
|
49
|
-
else if (options.reset) {
|
|
50
|
-
await resetConfiguration();
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
logger.consoleLog('Use --help to see available configuration options');
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
async function respondToUser(userInput) {
|
|
57
|
-
logger.debug('🤖 respondToUser called', { component: 'Main', inputLength: userInput.length });
|
|
58
|
-
if (agenticLoop) {
|
|
59
|
-
logger.debug('✅ Agentic loop available - processing input');
|
|
60
|
-
await agenticLoop.processUserInput(userInput);
|
|
61
|
-
logger.debug('✅ Agentic loop processing complete');
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
logger.error('❌ Agentic loop not initialized', { component: 'Main' });
|
|
65
|
-
console.error('❌ Agentic loop not initialized');
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
async function initializeConfig() {
|
|
69
|
-
logger.debug('🔧 Starting configuration initialization', { component: 'Config' });
|
|
70
|
-
// Check if configuration exists
|
|
71
|
-
if (await hasConfig()) {
|
|
72
|
-
logger.debug('📋 Configuration file exists - loading', { component: 'Config' });
|
|
73
|
-
try {
|
|
74
|
-
config = await loadConfig();
|
|
75
|
-
logger.debug('✅ Configuration loaded successfully', { component: 'Config', provider: config.provider, model: config.model });
|
|
76
|
-
if (!validateConfig(config)) {
|
|
77
|
-
logger.warn('⚠️ Configuration validation failed', { component: 'Config' });
|
|
78
|
-
logger.consoleLog('⚠️ Configuration is invalid or corrupted.');
|
|
79
|
-
logger.info('⚠️ Invalid config message displayed to user');
|
|
80
|
-
if (await promptReconfigure()) {
|
|
81
|
-
logger.debug('🔄 User chose to reconfigure', { component: 'Config' });
|
|
82
|
-
config = await setupConfig();
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
logger.error('❌ User declined reconfiguration', { component: 'Config' });
|
|
86
|
-
logger.consoleLog('Cannot proceed without valid configuration.');
|
|
87
|
-
logger.error('❌ Cannot proceed message displayed');
|
|
88
|
-
process.exit(1);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
catch (error) {
|
|
93
|
-
logger.error('❌ Error loading configuration', { component: 'Config', error: error.message });
|
|
94
|
-
logger.consoleLog(`❌ Error loading configuration: ${error.message}`);
|
|
95
|
-
logger.info('❌ Config error message displayed to user');
|
|
96
|
-
if (await promptReconfigure()) {
|
|
97
|
-
logger.debug('🔄 User chose to reconfigure after error', { component: 'Config' });
|
|
98
|
-
config = await setupConfig();
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
logger.error('❌ User declined reconfiguration after error', { component: 'Config' });
|
|
102
|
-
logger.consoleLog('Cannot proceed without valid configuration.');
|
|
103
|
-
logger.error('❌ Cannot proceed after error message displayed');
|
|
104
|
-
process.exit(1);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
logger.debug('📝 No configuration exists - running setup', { component: 'Config' });
|
|
110
|
-
// No configuration exists, run setup
|
|
111
|
-
config = await setupConfig();
|
|
112
|
-
}
|
|
113
|
-
logger.debug('🔧 Setting tools configuration', { component: 'Config' });
|
|
114
|
-
// Set config for tools
|
|
115
|
-
setToolsConfig(config);
|
|
116
|
-
logger.debug('🤖 Initializing OpenAI client', { component: 'Config', provider: config.provider });
|
|
117
|
-
// Initialize OpenAI client with the configuration
|
|
118
|
-
try {
|
|
119
|
-
openaiClient = createOpenAIClient(config);
|
|
120
|
-
if (!openaiClient) {
|
|
121
|
-
throw new Error('Failed to create OpenAI client');
|
|
122
|
-
}
|
|
123
|
-
logger.debug('✅ OpenAI client created successfully', { component: 'Config' });
|
|
124
|
-
logger.debug('🔄 Creating agentic loop', { component: 'Config', maxIterations: 100, streamOutput: true });
|
|
125
|
-
// Initialize the agentic loop
|
|
126
|
-
agenticLoop = await createAgenticLoop(openaiClient, config, {
|
|
127
|
-
maxIterations: 100,
|
|
128
|
-
streamOutput: true
|
|
129
|
-
});
|
|
130
|
-
logger.debug(`✅ Successfully initialized ProtoAgent`, { component: 'Config' });
|
|
131
|
-
}
|
|
132
|
-
catch (error) {
|
|
133
|
-
logger.error(`❌ Failed to initialize OpenAI client: ${error.message}`, { component: 'Config', error: error.message });
|
|
134
|
-
process.exit(1);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
async function startInteractiveMode() {
|
|
138
|
-
logger.debug('🚀 Starting interactive mode initialization');
|
|
139
|
-
logger.debug(' Parsing command line options');
|
|
140
|
-
// Check for options
|
|
141
|
-
const options = program.opts();
|
|
142
|
-
logger.debug('🔍 Command line options parsed', { component: 'Main', options });
|
|
143
|
-
// Set log level
|
|
144
|
-
if (options.logLevel) {
|
|
145
|
-
logger.debug('🔧 Setting log level', { component: 'Main', requestedLevel: options.logLevel });
|
|
146
|
-
const logLevel = LogLevel[options.logLevel.toUpperCase()];
|
|
147
|
-
if (logLevel !== undefined) {
|
|
148
|
-
logger.setLevel(logLevel);
|
|
149
|
-
logger.debug(`🔍 Log level set to: ${options.logLevel.toUpperCase()}`);
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
logger.warn(`⚠️ Invalid log level: ${options.logLevel}. Using INFO.`);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
// Enable file logging if requested (BEFORE welcome messages)
|
|
156
|
-
if (options.logToFile) {
|
|
157
|
-
logger.debug('📝 Enabling file logging', { component: 'Main', logDir: options.logDir || 'default' });
|
|
158
|
-
logger.enableFileLogging(options.logDir);
|
|
159
|
-
logger.info('📝 File logging enabled');
|
|
160
|
-
}
|
|
161
|
-
// Check for dangerous flag
|
|
162
|
-
if (options.dangerouslyAcceptAll) {
|
|
163
|
-
logger.debug('⚠️ Enabling dangerous mode', { component: 'Main' });
|
|
164
|
-
logger.warn('⚠️ DANGER MODE: All shell commands and file operations will be auto-approved without confirmation!');
|
|
165
|
-
setToolsDangerouslyAcceptAll(true);
|
|
166
|
-
}
|
|
167
|
-
// NOW display welcome messages (after file logging is enabled)
|
|
168
|
-
logger.consoleLog('🤖 Welcome to ProtoAgent - Your AI Coding Assistant with File System & Shell Powers!');
|
|
169
|
-
logger.debug('🤖 Welcome message displayed');
|
|
170
|
-
logger.consoleLog('💡 I can read, write, edit, search files and run shell commands in your project.');
|
|
171
|
-
logger.consoleLog('🔧 Ask me to create files, analyze code, run npm commands, git operations, or execute any shell command.');
|
|
172
|
-
logger.debug('💡 Capability messages displayed');
|
|
173
|
-
logger.consoleLog('🚪 Type "exit" to quit.');
|
|
174
|
-
logger.debug('🚪 Exit instruction displayed');
|
|
175
|
-
logger.debug('🔧 Initializing configuration');
|
|
176
|
-
// Initialize configuration
|
|
177
|
-
await initializeConfig();
|
|
178
|
-
// Set up cleanup handlers for file logging
|
|
179
|
-
logger.debug('🛡️ Setting up cleanup handlers');
|
|
180
|
-
process.on('SIGINT', () => {
|
|
181
|
-
logger.debug('🛑 SIGINT received - cleaning up');
|
|
182
|
-
logger.consoleLog('\n👋 Goodbye! Happy coding!');
|
|
183
|
-
logger.info('👋 Goodbye message displayed (SIGINT)');
|
|
184
|
-
logger.disableFileLogging();
|
|
185
|
-
process.exit(0);
|
|
186
|
-
});
|
|
187
|
-
process.on('SIGTERM', () => {
|
|
188
|
-
logger.debug('🛑 SIGTERM received - cleaning up');
|
|
189
|
-
logger.disableFileLogging();
|
|
190
|
-
process.exit(0);
|
|
191
|
-
});
|
|
192
|
-
logger.consoleLog(`🧠 Using ${config.provider} with model: ${config.model}`);
|
|
193
|
-
// Show current working directory
|
|
194
|
-
logger.consoleLog(`📁 Working directory: ${process.cwd()}`);
|
|
195
|
-
logger.debug('📁 Working directory displayed to user');
|
|
196
|
-
logger.debug('🔄 Starting main interaction loop');
|
|
197
|
-
while (true) {
|
|
198
|
-
logger.trace('📥 Waiting for user input');
|
|
199
|
-
const { input } = await inquirer.prompt([
|
|
200
|
-
{
|
|
201
|
-
type: 'input',
|
|
202
|
-
name: 'input',
|
|
203
|
-
message: 'protoagent>',
|
|
204
|
-
}
|
|
205
|
-
]);
|
|
206
|
-
logger.debug('📝 User input received', { component: 'Main', inputLength: input.length, isEmpty: input.trim() === '' });
|
|
207
|
-
if (input.toLowerCase() === 'exit' || input.toLowerCase() === 'quit') {
|
|
208
|
-
logger.debug('🚪 Exit command received');
|
|
209
|
-
logger.consoleLog('👋 Goodbye! Happy coding!');
|
|
210
|
-
logger.info('👋 Goodbye message displayed (normal exit)');
|
|
211
|
-
// Clean up file logging
|
|
212
|
-
logger.debug('🧹 Cleaning up file logging');
|
|
213
|
-
logger.disableFileLogging();
|
|
214
|
-
break;
|
|
215
|
-
}
|
|
216
|
-
if (input.trim() === '') {
|
|
217
|
-
logger.trace('⏭️ Empty input - continuing');
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
logger.debug('🤖 Forwarding input to agent', { component: 'Main', input: input.slice(0, 100) + (input.length > 100 ? '...' : '') });
|
|
221
|
-
await respondToUser(input);
|
|
222
|
-
logger.trace('✅ User input processing complete');
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
program
|
|
226
|
-
.action(() => {
|
|
227
|
-
startInteractiveMode();
|
|
228
|
-
});
|
|
229
|
-
program.parse();
|