protoagent 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -175,38 +175,66 @@ When users ask you to work with files or run commands, you should:
175
175
 
176
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
177
 
178
- ## TODO TRACKING - MANDATORY REQUIREMENT:
178
+ ## WORK TRACKING - MANDATORY REQUIREMENT:
179
179
 
180
- **YOU MUST ALWAYS USE TODO TRACKING FOR ANY NON-TRIVIAL TASK:**
180
+ **YOU MUST ALWAYS CREATE AND MAINTAIN A PROTOAGENT_TODO.md FILE FOR ANY NON-TRIVIAL TASK:**
181
181
 
182
- Before starting any complex task (more than a single file operation), you MUST:
183
- 1. Use the todo_write tool to create a detailed plan breaking down the work into steps
184
- 2. Update the TODO file as you complete each step using todo_write
185
- 3. Use todo_read to check your current progress and remaining tasks
186
- 4. Keep the TODO file updated throughout the entire process
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
187
 
188
- **TODO File Format Requirements:**
189
- - Use clear, actionable items with checkboxes: [ ] for incomplete, [x] for complete
188
+ **PROTOAGENT_TODO.md Format Requirements:**
189
+ - Use clear markdown with checkboxes: [ ] for incomplete, [x] for complete
190
190
  - Include relevant file paths and specific changes needed
191
191
  - Break down complex tasks into smaller, manageable steps
192
192
  - Add context and rationale for decisions
193
- - Update status as you progress through the work
193
+ - Include current status and next steps
194
+ - Add timestamps for major updates
194
195
 
195
- **Examples of tasks that REQUIRE TODO tracking:**
196
- - Implementing new features
197
- - Refactoring code across multiple files
198
- - Setting up project structures
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
199
227
  - Debugging complex issues
200
228
  - Any task involving more than 2 file operations
229
+ - Multi-step processes like build setup, testing, deployment
201
230
 
202
- **Example TODO format:**
203
- Use markdown format with checkboxes and clear structure like this:
204
- # Task: Implement user authentication system
205
- ## Plan: [ ] Create user model, [ ] Set up middleware, [ ] Create endpoints
206
- ## Progress: [x] User model created, [ ] Working on middleware
207
- ## Notes: Using JWT tokens with 24h expiration
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
208
236
 
209
- **FAILURE TO USE TODO TRACKING WILL RESULT IN POOR TASK COMPLETION.**
237
+ **FAILURE TO CREATE AND MAINTAIN PROTOAGENT_TODO.md WILL RESULT IN POOR TASK COMPLETION AND USER CONFUSION.**
210
238
 
211
239
  ## File Operation Guidelines - CRITICAL:
212
240
 
@@ -262,6 +290,9 @@ Shell Command Non-Interactive Mode:
262
290
 
263
291
  ${generateToolDescriptions()}
264
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
+
265
296
  Be helpful, accurate, and always explain what you're doing with the files and commands.
266
297
 
267
298
  ---
@@ -360,4 +391,7 @@ Shell Command Non-Interactive Mode:
360
391
 
361
392
  ${generateToolDescriptions()}
362
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
+
363
397
  Be helpful, accurate, and always explain what you're doing with the files and commands.`;
@@ -2,6 +2,7 @@ import fs from 'fs/promises';
2
2
  import path from 'path';
3
3
  import { randomBytes } from 'crypto';
4
4
  import { requestFileOperationApproval } from '../utils/file-operations-approval.js';
5
+ import { isUserCancellation } from '../utils/user-cancellation.js';
5
6
  import { logger } from '../utils/logger.js';
6
7
  // Current working directory for file operations
7
8
  const workingDirectory = process.cwd();
@@ -72,18 +73,28 @@ export async function editFile(filePath, oldString, newString, expectedReplaceme
72
73
  if (updatedContent === currentContent) {
73
74
  throw new Error(`No changes were made to the file. The old_string and new_string appear to be identical.`);
74
75
  }
76
+ // Calculate change context for better preview
77
+ const oldLines = currentContent.split('\n');
78
+ const newLines = updatedContent.split('\n');
79
+ const changeContext = {
80
+ linesAdded: newLines.length - oldLines.length,
81
+ linesRemoved: oldLines.length > newLines.length ? oldLines.length - newLines.length : 0,
82
+ totalLines: newLines.length,
83
+ affectedLineNumbers: findAffectedLineNumbers(oldString, currentContent)
84
+ };
75
85
  // Request user approval for edit operation
76
86
  const replacementCount = expectedReplacements || 1;
77
87
  const approvalContext = {
78
88
  operation: 'edit',
79
89
  filePath: filePath,
80
- description: `Replace ${replacementCount} occurrence${replacementCount === 1 ? '' : 's'} of text in file`,
81
- contentPreview: `OLD TEXT (${oldString.length} chars):\n${oldString.substring(0, 200)}${oldString.length > 200 ? '...' : ''}\n\nNEW TEXT (${newString.length} chars):\n${newString.substring(0, 200)}${newString.length > 200 ? '...' : ''}`
90
+ description: `Replace ${replacementCount} occurrence${replacementCount === 1 ? '' : 's'} of text (${oldString.length} → ${newString.length} chars)`,
91
+ contentPreview: createEditPreview(oldString, newString),
92
+ oldContent: currentContent,
93
+ newContent: updatedContent,
94
+ changeContext
82
95
  };
83
- const approved = await requestFileOperationApproval(approvalContext);
84
- if (!approved) {
85
- return `Edit operation cancelled by user: ${filePath}`;
86
- }
96
+ // Request user approval for edit operation (throws UserCancellationError if cancelled)
97
+ await requestFileOperationApproval(approvalContext);
87
98
  logger.debug(`✏️ Editing file: ${filePath} (${replacementCount} replacement${replacementCount === 1 ? '' : 's'})`, { component: 'EditFile', operation: 'editFile' });
88
99
  // Write the updated content using atomic operation
89
100
  const tempPath = `${validPath}.${randomBytes(16).toString('hex')}.tmp`;
@@ -102,6 +113,10 @@ export async function editFile(filePath, oldString, newString, expectedReplaceme
102
113
  return `Successfully edited ${filePath} - replaced ${replacementCount} occurrence${replacementCount === 1 ? '' : 's'} of the specified text`;
103
114
  }
104
115
  catch (error) {
116
+ // Re-throw UserCancellationError without modification
117
+ if (isUserCancellation(error)) {
118
+ throw error;
119
+ }
105
120
  if (error instanceof Error) {
106
121
  throw new Error(`Failed to edit file: ${error.message}`);
107
122
  }
@@ -112,6 +127,28 @@ export async function editFile(filePath, oldString, newString, expectedReplaceme
112
127
  function escapeRegExp(string) {
113
128
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
114
129
  }
130
+ // Helper function to find affected line numbers
131
+ function findAffectedLineNumbers(searchString, content) {
132
+ const lines = content.split('\n');
133
+ const affectedLines = [];
134
+ lines.forEach((line, index) => {
135
+ if (line.includes(searchString)) {
136
+ affectedLines.push(index + 1); // Line numbers are 1-based
137
+ }
138
+ });
139
+ return affectedLines;
140
+ }
141
+ // Helper function to create a concise edit preview
142
+ function createEditPreview(oldText, newText) {
143
+ const maxLength = 150;
144
+ const oldPreview = oldText.length > maxLength
145
+ ? `${oldText.substring(0, maxLength)}...`
146
+ : oldText;
147
+ const newPreview = newText.length > maxLength
148
+ ? `${newText.substring(0, maxLength)}...`
149
+ : newText;
150
+ return `REMOVE: "${oldPreview}"\nADD: "${newPreview}"`;
151
+ }
115
152
  // Tool definition
116
153
  export const editFileTool = {
117
154
  type: 'function',
@@ -7,7 +7,6 @@ export { createDirectory, createDirectoryTool } from './create-directory.js';
7
7
  export { viewDirectoryTree, viewDirectoryTreeTool } from './view-directory-tree.js';
8
8
  export { searchFiles, searchFilesTool } from './search-files.js';
9
9
  export { runShellCommand, setShellConfig, setDangerouslyAcceptAll, runShellCommandTool } from './run-shell-command.js';
10
- export { handleTodoTool, todoReadTool, todoWriteTool } from './todo.js';
11
10
  export { setFileOperationConfig, setDangerouslyAcceptAllFileOps, getSessionApprovalStatus, clearSessionApprovals, showApprovalStatus } from '../utils/file-operations-approval.js';
12
11
  // Import all tool definitions
13
12
  import { readFileTool } from './read-file.js';
@@ -18,7 +17,6 @@ import { createDirectoryTool } from './create-directory.js';
18
17
  import { viewDirectoryTreeTool } from './view-directory-tree.js';
19
18
  import { searchFilesTool } from './search-files.js';
20
19
  import { runShellCommandTool } from './run-shell-command.js';
21
- import { todoReadTool, todoWriteTool } from './todo.js';
22
20
  // Import all tool implementations
23
21
  import { readFile } from './read-file.js';
24
22
  import { writeFile } from './write-file.js';
@@ -28,8 +26,8 @@ import { createDirectory } from './create-directory.js';
28
26
  import { viewDirectoryTree } from './view-directory-tree.js';
29
27
  import { searchFiles } from './search-files.js';
30
28
  import { runShellCommand } from './run-shell-command.js';
31
- import { handleTodoTool } from './todo.js';
32
29
  import { logger } from '../utils/logger.js';
30
+ import { isUserCancellation } from '../utils/user-cancellation.js';
33
31
  // Consolidated tool definitions
34
32
  export const tools = [
35
33
  readFileTool,
@@ -39,9 +37,7 @@ export const tools = [
39
37
  createDirectoryTool,
40
38
  viewDirectoryTreeTool,
41
39
  searchFilesTool,
42
- runShellCommandTool,
43
- todoReadTool,
44
- todoWriteTool
40
+ runShellCommandTool
45
41
  ];
46
42
  // Tool handler function
47
43
  export async function handleToolCall(toolName, args) {
@@ -105,14 +101,10 @@ export async function handleToolCall(toolName, args) {
105
101
  component: 'ToolHandler',
106
102
  command: args.command,
107
103
  argsCount: args.args?.length || 0,
108
- timeout: args.timeout_ms || 30000
104
+ timeout: args.timeout_ms || 30000,
105
+ directory: args.directory || 'current'
109
106
  });
110
- result = await runShellCommand(args.command, args.args || [], args.timeout_ms || 30000);
111
- break;
112
- case 'todo_read':
113
- case 'todo_write':
114
- logger.debug('📋 Executing todo tool', { component: 'ToolHandler', toolName });
115
- result = await handleTodoTool(toolName, args);
107
+ result = await runShellCommand(args.command, args.args || [], args.timeout_ms || 30000, args.directory);
116
108
  break;
117
109
  default:
118
110
  logger.error('❌ Unknown tool requested', { component: 'ToolHandler', toolName });
@@ -129,6 +121,15 @@ export async function handleToolCall(toolName, args) {
129
121
  return result;
130
122
  }
131
123
  catch (error) {
124
+ // Re-throw UserCancellationError so it can be handled properly by the agentic loop
125
+ if (isUserCancellation(error)) {
126
+ logger.debug('🚪 Re-throwing UserCancellationError for proper handling', {
127
+ component: 'ToolHandler',
128
+ toolName,
129
+ cancellationReason: error.message
130
+ });
131
+ throw error; // Re-throw to let the agentic loop handle it
132
+ }
132
133
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
133
134
  logger.error('❌ Tool execution failed with exception', {
134
135
  component: 'ToolHandler',