centaurus-cli 3.1.3 → 3.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/dist/cli-adapter.js +685 -153
  2. package/dist/cli-adapter.js.map +1 -1
  3. package/dist/config/defaultConfig.js +1 -4
  4. package/dist/config/defaultConfig.js.map +1 -1
  5. package/dist/config/models.js +4 -0
  6. package/dist/config/models.js.map +1 -1
  7. package/dist/config/slash-commands.js +66 -2
  8. package/dist/config/slash-commands.js.map +1 -1
  9. package/dist/config/types.js +4 -4
  10. package/dist/config/types.js.map +1 -1
  11. package/dist/index.js +36 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/services/ai-context-injector.js +109 -0
  14. package/dist/services/ai-context-injector.js.map +1 -1
  15. package/dist/services/api-client.js.map +1 -1
  16. package/dist/services/background-task-manager.js +59 -0
  17. package/dist/services/background-task-manager.js.map +1 -1
  18. package/dist/services/local-chat-storage.js +2 -0
  19. package/dist/services/local-chat-storage.js.map +1 -1
  20. package/dist/services/skill-storage.js +141 -0
  21. package/dist/services/skill-storage.js.map +1 -0
  22. package/dist/services/sub-agent-manager.js +49 -8
  23. package/dist/services/sub-agent-manager.js.map +1 -1
  24. package/dist/services/warpify-detector.js +17 -5
  25. package/dist/services/warpify-detector.js.map +1 -1
  26. package/dist/tools/background-command.js +5 -2
  27. package/dist/tools/background-command.js.map +1 -1
  28. package/dist/tools/command.js +367 -109
  29. package/dist/tools/command.js.map +1 -1
  30. package/dist/tools/file-ops.js +23 -6
  31. package/dist/tools/file-ops.js.map +1 -1
  32. package/dist/tools/plan-mode.js +184 -336
  33. package/dist/tools/plan-mode.js.map +1 -1
  34. package/dist/tools/sub-agent.js +24 -5
  35. package/dist/tools/sub-agent.js.map +1 -1
  36. package/dist/tools/todo-list.js +157 -0
  37. package/dist/tools/todo-list.js.map +1 -0
  38. package/dist/types/skill.js +30 -0
  39. package/dist/types/skill.js.map +1 -0
  40. package/dist/ui/components/App.js +956 -162
  41. package/dist/ui/components/App.js.map +1 -1
  42. package/dist/ui/components/AuthScreen.js +3 -1
  43. package/dist/ui/components/AuthScreen.js.map +1 -1
  44. package/dist/ui/components/AuthWelcomeScreen.js +3 -1
  45. package/dist/ui/components/AuthWelcomeScreen.js.map +1 -1
  46. package/dist/ui/components/CodeBlock.js +3 -1
  47. package/dist/ui/components/CodeBlock.js.map +1 -1
  48. package/dist/ui/components/CompactShellPreview.js +44 -0
  49. package/dist/ui/components/CompactShellPreview.js.map +1 -0
  50. package/dist/ui/components/ConfigViewer.js +3 -1
  51. package/dist/ui/components/ConfigViewer.js.map +1 -1
  52. package/dist/ui/components/ConfirmPrompt.js +3 -1
  53. package/dist/ui/components/ConfirmPrompt.js.map +1 -1
  54. package/dist/ui/components/ConnectionStatusMessage.js +3 -1
  55. package/dist/ui/components/ConnectionStatusMessage.js.map +1 -1
  56. package/dist/ui/components/DetailedPlanReviewScreen.js +84 -74
  57. package/dist/ui/components/DetailedPlanReviewScreen.js.map +1 -1
  58. package/dist/ui/components/DiffViewer.js +6 -3
  59. package/dist/ui/components/DiffViewer.js.map +1 -1
  60. package/dist/ui/components/FileCreationPreview.js.map +1 -1
  61. package/dist/ui/components/FileTagAutocomplete.js +4 -2
  62. package/dist/ui/components/FileTagAutocomplete.js.map +1 -1
  63. package/dist/ui/components/InputBox.js +243 -40
  64. package/dist/ui/components/InputBox.js.map +1 -1
  65. package/dist/ui/components/InteractiveShell.js +5 -3
  66. package/dist/ui/components/InteractiveShell.js.map +1 -1
  67. package/dist/ui/components/KeyboardHelp.js +4 -1
  68. package/dist/ui/components/KeyboardHelp.js.map +1 -1
  69. package/dist/ui/components/LoadingIndicator.js +3 -1
  70. package/dist/ui/components/LoadingIndicator.js.map +1 -1
  71. package/dist/ui/components/MCPAddScreen.js +63 -13
  72. package/dist/ui/components/MCPAddScreen.js.map +1 -1
  73. package/dist/ui/components/MarkdownRenderer.js +3 -1
  74. package/dist/ui/components/MarkdownRenderer.js.map +1 -1
  75. package/dist/ui/components/MessageDisplay.js +9 -7
  76. package/dist/ui/components/MessageDisplay.js.map +1 -1
  77. package/dist/ui/components/ModelPicker.js +170 -0
  78. package/dist/ui/components/ModelPicker.js.map +1 -0
  79. package/dist/ui/components/MonitorModeAIPanel.js +3 -1
  80. package/dist/ui/components/MonitorModeAIPanel.js.map +1 -1
  81. package/dist/ui/components/PlanAcceptedMessage.js +12 -6
  82. package/dist/ui/components/PlanAcceptedMessage.js.map +1 -1
  83. package/dist/ui/components/PlanQuestionMessage.js +37 -0
  84. package/dist/ui/components/PlanQuestionMessage.js.map +1 -0
  85. package/dist/ui/components/PlanQuestionScreen.js +138 -0
  86. package/dist/ui/components/PlanQuestionScreen.js.map +1 -0
  87. package/dist/ui/components/PlanReviewScreen.js +7 -9
  88. package/dist/ui/components/PlanReviewScreen.js.map +1 -1
  89. package/dist/ui/components/RulesEditorScreen.js +65 -28
  90. package/dist/ui/components/RulesEditorScreen.js.map +1 -1
  91. package/dist/ui/components/SelectPrompt.js +3 -1
  92. package/dist/ui/components/SelectPrompt.js.map +1 -1
  93. package/dist/ui/components/SkillCreatorScreen.js +217 -0
  94. package/dist/ui/components/SkillCreatorScreen.js.map +1 -0
  95. package/dist/ui/components/SlashCommandAutocomplete.js +4 -2
  96. package/dist/ui/components/SlashCommandAutocomplete.js.map +1 -1
  97. package/dist/ui/components/StatusBar.js +4 -2
  98. package/dist/ui/components/StatusBar.js.map +1 -1
  99. package/dist/ui/components/StreamingMessageDisplay.js +5 -3
  100. package/dist/ui/components/StreamingMessageDisplay.js.map +1 -1
  101. package/dist/ui/components/SubAgentListScreen.js +65 -0
  102. package/dist/ui/components/SubAgentListScreen.js.map +1 -0
  103. package/dist/ui/components/SubAgentViewScreen.js +123 -0
  104. package/dist/ui/components/SubAgentViewScreen.js.map +1 -0
  105. package/dist/ui/components/TaskCompletedMessage.js +40 -8
  106. package/dist/ui/components/TaskCompletedMessage.js.map +1 -1
  107. package/dist/ui/components/TaskProgressIndicator.js +6 -4
  108. package/dist/ui/components/TaskProgressIndicator.js.map +1 -1
  109. package/dist/ui/components/TextEditor.js +297 -0
  110. package/dist/ui/components/TextEditor.js.map +1 -0
  111. package/dist/ui/components/TodoListMessage.js +59 -0
  112. package/dist/ui/components/TodoListMessage.js.map +1 -0
  113. package/dist/ui/components/ToolExecutionMessage.js +134 -84
  114. package/dist/ui/components/ToolExecutionMessage.js.map +1 -1
  115. package/dist/ui/components/ToolExecutionStatus.js +3 -1
  116. package/dist/ui/components/ToolExecutionStatus.js.map +1 -1
  117. package/dist/ui/components/WelcomeBanner.js +33 -33
  118. package/dist/ui/components/WelcomeBanner.js.map +1 -1
  119. package/dist/ui/components/WorkflowCreatorScreen.js +5 -3
  120. package/dist/ui/components/WorkflowCreatorScreen.js.map +1 -1
  121. package/dist/ui/theme.js +97 -0
  122. package/dist/ui/theme.js.map +1 -0
  123. package/dist/ui/utils/chat-history-limit.js +247 -0
  124. package/dist/ui/utils/chat-history-limit.js.map +1 -0
  125. package/dist/utils/chat-formatter.js +22 -9
  126. package/dist/utils/chat-formatter.js.map +1 -1
  127. package/dist/utils/input-classifier.js +11 -1
  128. package/dist/utils/input-classifier.js.map +1 -1
  129. package/dist/utils/output-truncation.js +175 -0
  130. package/dist/utils/output-truncation.js.map +1 -0
  131. package/dist/utils/rule-reference-resolver.js +3 -3
  132. package/dist/utils/rule-reference-resolver.js.map +1 -1
  133. package/dist/utils/tunnel-commands-manager.js +134 -0
  134. package/dist/utils/tunnel-commands-manager.js.map +1 -0
  135. package/package.json +91 -90
  136. package/postinstall.js +4 -11
  137. package/dist/ui/components/MultiLineInput.js +0 -255
  138. package/dist/ui/components/MultiLineInput.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/tools/command.ts"],"sourcesContent":["import { Tool } from './types.js';\r\nimport * as shellUtils from '../utils/shell.js';\r\nimport * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport { ContextManager } from '../context/context-manager.js';\r\nimport { runWSLCommand, runDockerCommand, runSSHCommand } from '../utils/editor-utils.js';\r\n\r\n// Helper to process shell input with escape sequences and special keys\r\nfunction processShellInput(input: string): string {\r\n let processed = input\r\n // Handle standard escape sequences\r\n .replace(/\\\\n/g, '\\r') // Use \\r for Enter/Newline in interactive shell\r\n .replace(/\\\\r/g, '\\r')\r\n .replace(/\\\\t/g, '\\t')\r\n .replace(/\\\\b/g, '\\b')\r\n .replace(/\\\\x1B/g, '\\x1B');\r\n\r\n // Handle special key codes\r\n const keyMap: { [key: string]: string } = {\r\n '[UP]': '\\u001b[A',\r\n '[DOWN]': '\\u001b[B',\r\n '[RIGHT]': '\\u001b[C',\r\n '[LEFT]': '\\u001b[D',\r\n '[ENTER]': '\\r', // Enter key sends CR\r\n '[TAB]': '\\t',\r\n '[ESC]': '\\u001b',\r\n '[BACKSPACE]': '\\b',\r\n '[CTRL+C]': '\\u0003',\r\n '[CTRL+D]': '\\u0004',\r\n '[CTRL+Z]': '\\u001a'\r\n };\r\n\r\n // Replace special keys\r\n for (const [key, code] of Object.entries(keyMap)) {\r\n processed = processed.split(key).join(code);\r\n }\r\n\r\n return processed;\r\n}\r\n\r\nfunction resolveCommandWorkingDirectory(\r\n currentContext: any,\r\n contextCwd: string,\r\n commandCwd?: string\r\n): string {\r\n if (currentContext.type === 'local') {\r\n return commandCwd ? path.resolve(contextCwd, commandCwd) : contextCwd;\r\n }\r\n\r\n const remoteBase = currentContext.metadata?.workingDirectory || contextCwd || '~';\r\n if (!commandCwd) {\r\n return remoteBase;\r\n }\r\n\r\n return path.posix.isAbsolute(commandCwd)\r\n ? commandCwd\r\n : path.posix.join(remoteBase, commandCwd);\r\n}\r\n\r\n\r\nexport const runCommandTool: Tool = {\r\n schema: {\r\n name: 'execute_command',\r\n description: `PROPOSE a command to run on behalf of the user. Operating System: windows. Shell: pwsh.\r\n**NEVER PROPOSE A cd COMMAND**.\r\nIf you have this tool, note that you DO have the ability to run commands directly on the USER's system.\r\nMake sure to specify CommandLine exactly as it should be run in the shell.\r\nNote that the user will have to approve the command before it is executed. The user may reject it if it is not to their liking.\r\nThe actual command will NOT execute until the user approves it. The user may not approve it immediately.\r\nIf the step is WAITING for user approval, it has NOT started running.\r\nIf the step returns a command id, it means that the command was sent to the background. You should use the command_status tool to monitor the output and status of the command.\r\nCommands will be run with PAGER=cat. You may want to limit the length of output for commands that usually rely on paging and may contain very long output (e.g. git log, use git log -n <N>).`,\r\n parameters: {\r\n type: 'object',\r\n properties: {\r\n reason_text: {\r\n type: 'string',\r\n description: 'REQUIRED: A brief explanation of why you are executing this command and what you expect it to do. This will be shown to the user. Example: \"Running npm install to install the project dependencies\"',\r\n },\r\n CommandLine: {\r\n type: 'string',\r\n description: 'The exact command line string to execute.',\r\n },\r\n Cwd: {\r\n type: 'string',\r\n description: 'The current working directory for the command',\r\n },\r\n SafeToAutoRun: {\r\n type: 'boolean',\r\n description: 'Set to true if you believe that this command is safe to run WITHOUT user approval. A command is unsafe if it may have some destructive side-effects. Example unsafe side-effects include: deleting files, mutating state, installing system dependencies, making external requests, etc. Set to true only if you are extremely confident it is safe. If you feel the command could be unsafe, never set this to true, EVEN if the USER asks you to. It is imperative that you never auto-run a potentially unsafe command.',\r\n },\r\n WaitMsBeforeAsync: {\r\n type: 'integer',\r\n description: 'This specifies the number of milliseconds to wait after starting the command before sending it to the background. If you want the command to complete execution synchronously, set this to a large enough value that you expect the command to complete in that time under ordinary circumstances. If you\\'re starting an interactive or long-running command, set it to a large enough value that it would cause possible failure cases to execute synchronously (e.g. 500ms). Keep the value as small as possible, with a maximum of 10000ms.',\r\n },\r\n shell_input: {\r\n type: 'string',\r\n description: '[INTERNAL - DO NOT USE FROM MAIN AGENT] Input to send to a CURRENTLY RUNNING interactive shell. This parameter is used internally by the shell input sub-agent. When starting a command, do NOT set this parameter - just start the command and the user will provide input through the shell UI or agent control mode.',\r\n },\r\n },\r\n required: ['reason_text', 'CommandLine', 'Cwd', 'SafeToAutoRun', 'WaitMsBeforeAsync'],\r\n },\r\n },\r\n async execute(args, context) {\r\n const { CommandLine, Cwd, shell_input } = args;\r\n\r\n // Handle shell_input action - send input to a running shell\r\n // NOTE: shell_input should only be used by ShellInputAgent, not the main agent\r\n if (shell_input) {\r\n if (context.cliAdapter) {\r\n const interactiveProcess = context.cliAdapter.getCurrentInteractiveProcess();\r\n if (!interactiveProcess) {\r\n // Provide a helpful error message guiding the correct approach\r\n throw new Error(\r\n `Cannot use shell_input when no interactive shell is running. ` +\r\n `To run an interactive command:\\n` +\r\n `1. First, run the command WITHOUT shell_input - just use CommandLine\\n` +\r\n `2. The interactive shell will open and you can provide input there\\n` +\r\n `3. Agent Control mode (if enabled) will automatically handle input prompts\\n\\n` +\r\n `Please retry: run the command first, then input will be handled automatically.`\r\n );\r\n }\r\n // Process input to handle escapes and special keys\r\n const processedInput = processShellInput(shell_input);\r\n\r\n // Send the processed input\r\n interactiveProcess.write(processedInput);\r\n\r\n // Return readable confirmation (showing what was actually sent)\r\n const readableSent = processedInput\r\n .replace(/\\n/g, '\\\\n')\r\n .replace(/\\r/g, '\\\\r')\r\n .replace(/\\x1B/g, '^[')\r\n .replace(/\\u0003/g, '^C');\r\n\r\n return `Sent input to active shell: ${readableSent}`;\r\n } else {\r\n throw new Error('CLI adapter not available for shell input.');\r\n }\r\n }\r\n\r\n const command = CommandLine; // Map to internal variable\r\n const contextManager = context.contextManager as ContextManager;\r\n const currentContext = contextManager.getCurrentContext();\r\n\r\n // Special handling for cd command - change the actual working directory\r\n // Only intercept simple cd commands, not chained ones (cd path && cmd, cd path; cmd)\r\n // Chained commands will be executed by the shell directly\r\n const isChainedCommand = /&&|;/.test(command);\r\n const cdMatch = !isChainedCommand && command.match(/^cd\\s+(.+)$/);\r\n if (cdMatch) {\r\n if (currentContext.type === 'local') {\r\n const targetDir = cdMatch[1].trim();\r\n const newCwd = path.resolve(Cwd || context.cwd, targetDir);\r\n\r\n if (!fs.existsSync(newCwd)) {\r\n throw new Error(`Directory not found: ${targetDir}\\n\\nAction: Use 'list_directory' to verify the directory structure.`);\r\n }\r\n\r\n if (!fs.statSync(newCwd).isDirectory()) {\r\n throw new Error(`Path is not a directory: ${targetDir}\\n\\nAction: Verify the path points to a directory, not a file.`);\r\n }\r\n\r\n contextManager.updateWorkingDirectory(newCwd);\r\n\r\n // Update CLI adapter CWD and notify UI\r\n if (context.cliAdapter) {\r\n context.cliAdapter.cwd = newCwd;\r\n if (context.cliAdapter.onCwdChange) {\r\n context.cliAdapter.onCwdChange(newCwd);\r\n }\r\n }\r\n\r\n return `Changed directory to: ${newCwd}`;\r\n } else {\r\n // Execute cd in subshell and update working directory\r\n const targetDir = cdMatch[1].trim();\r\n const result = await contextManager.executeCommand(`cd ${targetDir}`);\r\n if (result.exitCode === 0) {\r\n // Get the actual new working directory from the handler\r\n if (currentContext.handler) {\r\n const newCwd = await currentContext.handler.getCurrentWorkingDirectory();\r\n if (newCwd) {\r\n contextManager.updateWorkingDirectory(newCwd);\r\n\r\n // Update CLI adapter CWD and notify UI\r\n if (context.cliAdapter) {\r\n context.cliAdapter.cwd = newCwd;\r\n if (context.cliAdapter.onCwdChange) {\r\n context.cliAdapter.onCwdChange(newCwd);\r\n }\r\n }\r\n }\r\n }\r\n return `Changed directory to: ${contextManager.getCurrentContext().metadata.workingDirectory}`;\r\n } else {\r\n throw new Error(`${result.stderr || 'Failed to change directory'}\\n\\nAction: Verify the directory exists in the remote environment.`);\r\n }\r\n }\r\n }\r\n\r\n // Check for long-running commands\r\n const longRunningPatterns = [\r\n /npm\\s+run\\s+dev/,\r\n /npm\\s+run\\s+start/,\r\n /npm\\s+start/,\r\n /yarn\\s+dev/,\r\n /yarn\\s+start/,\r\n /pnpm\\s+dev/,\r\n /pnpm\\s+start/,\r\n /python\\s+-m\\s+http\\.server/,\r\n /python3\\s+-m\\s+http\\.server/,\r\n /php\\s+-S/,\r\n /ruby\\s+-run\\s+-e\\s+httpd/,\r\n /serve\\s+/,\r\n /http-server/,\r\n /webpack\\s+--watch/,\r\n /webpack-dev-server/,\r\n /vite(?:\\s+(?!build)|\\s*$)/,\r\n /next\\s+dev/,\r\n /gatsby\\s+develop/,\r\n /rails\\s+server/,\r\n /rails\\s+s\\b/,\r\n /django\\s+runserver/,\r\n /manage\\.py\\s+runserver/,\r\n /flask\\s+run/,\r\n /nodemon/,\r\n /ng\\s+serve/,\r\n /ember\\s+serve/,\r\n /hugo\\s+server/,\r\n /jekyll\\s+serve/,\r\n /docker\\s+run.*-d/,\r\n /docker-compose\\s+up/,\r\n /tail\\s+-f/,\r\n /watch\\s+/,\r\n /less\\s+/,\r\n /more\\s+/,\r\n /vim\\s+/,\r\n /vi\\s+/,\r\n /nano\\s+/,\r\n /emacs\\s+/,\r\n ];\r\n\r\n const isLongRunning = longRunningPatterns.some((pattern) => pattern.test(command));\r\n\r\n if (isLongRunning) {\r\n throw new Error(\r\n `This command appears to be a long-running process (like a development server or file watcher). ` +\r\n `Long-running commands cannot be executed through execute_command as they would block execution.\\n\\n` +\r\n `USE THE background_command TOOL INSTEAD:\\n` +\r\n `- action: \"start\" to run the command in the background\\n` +\r\n `- action: \"status\" to check the output while it runs\\n` +\r\n `- action: \"kill\" to terminate when done\\n\\n` +\r\n `Command that needs background execution: ${command}`\r\n );\r\n }\r\n\r\n // Check for risky commands\r\n const riskyPatterns = [/rm\\s+-rf/, /del\\s+\\/s/, /format/, /dd\\s+if=/];\r\n const isRisky = riskyPatterns.some((pattern) => pattern.test(command));\r\n\r\n const approved = await context.requireApproval(\r\n `Execute command: ${command}${currentContext.type !== 'local' ? ` (in ${currentContext.type} environment)` : ''}`,\r\n isRisky,\r\n undefined,\r\n 'execute_command',\r\n { command }\r\n );\r\n\r\n // Check for feedback object (user rejected with feedback)\r\n if (typeof approved === 'object' && 'feedback' in approved) {\r\n throw new Error(`USER_FEEDBACK: ${approved.feedback}`);\r\n }\r\n\r\n // Check for boolean false (user rejected)\r\n if (!approved) {\r\n throw new Error('Operation cancelled by user.');\r\n }\r\n\r\n const executionCwd = resolveCommandWorkingDirectory(currentContext, context.cwd, Cwd);\r\n\r\n // Execute command with interactive support (allows user input via stdin)\r\n if (currentContext.type === 'local') {\r\n // Use interactive process for full stdin/stdout/stderr support\r\n return new Promise<string>((resolve, reject) => {\r\n let output = '';\r\n let exitCode: number | undefined;\r\n\r\n // Start interactive process\r\n const interactiveProcess = shellUtils.executeCommandInteractive(\r\n command,\r\n executionCwd,\r\n (data: string) => {\r\n // Accumulate output\r\n output += data;\r\n\r\n // Stream to UI if callback is available\r\n if (context.onStreamingOutput) {\r\n context.onStreamingOutput(data, 'stdout');\r\n }\r\n },\r\n (code: number) => {\r\n // Process exited\r\n exitCode = code;\r\n\r\n // Clear the interactive process reference\r\n if (context.cliAdapter) {\r\n context.cliAdapter.setCurrentInteractiveProcess(undefined);\r\n }\r\n\r\n // Resolve or reject based on exit code\r\n if (code === 0) {\r\n resolve(output || 'Command executed successfully');\r\n } else {\r\n const errorOutput = output + `\\nExit Code: ${code}`;\r\n reject(new Error(`${errorOutput || `Command failed with exit code ${code}`}\\n\\nPossible reasons:\\n1. The command is invalid or not installed.\\n2. The arguments are incorrect.\\n3. The command requires user input (interactive mode).`));\r\n }\r\n }\r\n );\r\n\r\n // Store the interactive process reference so user can send input\r\n if (context.cliAdapter) {\r\n context.cliAdapter.setCurrentInteractiveProcess(interactiveProcess);\r\n }\r\n });\r\n } else if (currentContext.type === 'wsl') {\r\n // WSL execution with PTY for proper TTY handling (sudo, etc.)\r\n const remoteCwd = executionCwd;\r\n const distribution = currentContext.metadata?.distroName || 'Ubuntu';\r\n\r\n return new Promise<string>((resolve, reject) => {\r\n let output = '';\r\n\r\n // Calculate constrained terminal dimensions for proper escape sequence handling\r\n // Reduce width to account for UI borders/padding (~4 chars for left+right borders/padding)\r\n // Reduce height to account for available display area in InteractiveShell\r\n const constrainedCols = Math.max(40, (process.stdout.columns || 80) - 4);\r\n const constrainedRows = Math.min(process.stdout.rows || 24, 50);\r\n\r\n const wslPty = runWSLCommand(\r\n distribution,\r\n command,\r\n remoteCwd,\r\n (data: string) => {\r\n // Stream output to UI\r\n output += data;\r\n if (context.onStreamingOutput) {\r\n context.onStreamingOutput(data, 'stdout');\r\n }\r\n },\r\n (exitCode: number) => {\r\n // Clear the interactive process\r\n if (context.cliAdapter) {\r\n context.cliAdapter.setCurrentInteractiveProcess(undefined);\r\n }\r\n\r\n if (exitCode !== 0) {\r\n reject(new Error(`${output || `Command failed with exit code ${exitCode}`}\\n\\nPossible reasons:\\n1. The command is invalid or not installed.\\n2. The arguments are incorrect.`));\r\n } else {\r\n resolve(output || 'Command executed successfully');\r\n }\r\n },\r\n constrainedCols,\r\n constrainedRows\r\n );\r\n\r\n // Set up interactive process for stdin\r\n if (context.cliAdapter) {\r\n context.cliAdapter.setCurrentInteractiveProcess({\r\n process: null,\r\n write: (data: string) => wslPty.write(data),\r\n kill: () => wslPty.kill(),\r\n signal: (sig: NodeJS.Signals) => {\r\n if (sig === 'SIGINT') {\r\n wslPty.write('\\x03'); // Ctrl+C\r\n }\r\n },\r\n resize: (cols: number, rows: number) => wslPty.resize(cols, rows),\r\n isPty: true\r\n });\r\n }\r\n });\r\n } else if (currentContext.type === 'docker') {\r\n // Docker execution - check if nested inside SSH\r\n const parentContext = contextManager.getParentContext();\r\n\r\n const remoteCwd = executionCwd;\r\n const containerId = currentContext.metadata?.containerId || '';\r\n\r\n // Calculate constrained terminal dimensions for proper escape sequence handling\r\n const constrainedCols = Math.max(40, (process.stdout.columns || 80) - 4);\r\n const constrainedRows = Math.min(process.stdout.rows || 24, 50);\r\n\r\n if (parentContext && parentContext.type === 'ssh') {\r\n // Nested Docker inside SSH: route docker exec command through SSH\r\n const sshClient = parentContext.handler?.client;\r\n\r\n if (!sshClient) {\r\n throw new Error('SSH client not available for nested Docker session');\r\n }\r\n\r\n // Build docker exec command to run via SSH\r\n // Escape the command properly for nested shell execution\r\n const escapedCommand = command.replace(/\"/g, '\\\\\"');\r\n const dockerCommand = `docker exec -w \"${remoteCwd}\" ${containerId} sh -c \"${escapedCommand}\"`;\r\n\r\n return new Promise<string>((resolve, reject) => {\r\n let output = '';\r\n\r\n const sshPty = runSSHCommand(\r\n sshClient,\r\n dockerCommand,\r\n parentContext.metadata.workingDirectory || '~',\r\n (data: string) => {\r\n // Stream output to UI\r\n output += data;\r\n if (context.onStreamingOutput) {\r\n context.onStreamingOutput(data, 'stdout');\r\n }\r\n },\r\n (exitCode: number) => {\r\n // Clear the interactive process\r\n if (context.cliAdapter) {\r\n context.cliAdapter.setCurrentInteractiveProcess(undefined);\r\n }\r\n\r\n if (exitCode !== 0) {\r\n reject(new Error(`${output || `Command failed with exit code ${exitCode}`}\\n\\nPossible reasons:\\n1. The command is invalid or not installed in the container.\\n2. The arguments are incorrect.\\n3. The Docker container may not be running.`));\r\n } else {\r\n resolve(output || 'Command executed successfully');\r\n }\r\n },\r\n constrainedCols,\r\n constrainedRows\r\n );\r\n\r\n // Set up interactive process for stdin\r\n if (context.cliAdapter) {\r\n context.cliAdapter.setCurrentInteractiveProcess({\r\n process: null,\r\n write: (data: string) => sshPty.write(data),\r\n kill: () => sshPty.kill(),\r\n signal: (sig: NodeJS.Signals) => {\r\n if (sig === 'SIGINT') {\r\n sshPty.write('\\x03'); // Ctrl+C\r\n }\r\n },\r\n resize: (cols: number, rows: number) => sshPty.resize(cols, rows),\r\n isPty: true\r\n });\r\n }\r\n });\r\n } else {\r\n // Local Docker: use standard runDockerCommand\r\n return new Promise<string>((resolve, reject) => {\r\n let output = '';\r\n\r\n const dockerPty = runDockerCommand(\r\n containerId,\r\n command,\r\n remoteCwd,\r\n (data: string) => {\r\n // Stream output to UI\r\n output += data;\r\n if (context.onStreamingOutput) {\r\n context.onStreamingOutput(data, 'stdout');\r\n }\r\n },\r\n (exitCode: number) => {\r\n // Clear the interactive process\r\n if (context.cliAdapter) {\r\n context.cliAdapter.setCurrentInteractiveProcess(undefined);\r\n }\r\n\r\n if (exitCode !== 0) {\r\n reject(new Error(`${output || `Command failed with exit code ${exitCode}`}\\n\\nPossible reasons:\\n1. The command is invalid or not installed.\\n2. The arguments are incorrect.`));\r\n } else {\r\n resolve(output || 'Command executed successfully');\r\n }\r\n },\r\n constrainedCols,\r\n constrainedRows\r\n );\r\n\r\n // Set up interactive process for stdin\r\n if (context.cliAdapter) {\r\n context.cliAdapter.setCurrentInteractiveProcess({\r\n process: null,\r\n write: (data: string) => dockerPty.write(data),\r\n kill: () => dockerPty.kill(),\r\n signal: (sig: NodeJS.Signals) => {\r\n if (sig === 'SIGINT') {\r\n dockerPty.write('\\x03'); // Ctrl+C\r\n }\r\n },\r\n resize: (cols: number, rows: number) => dockerPty.resize(cols, rows),\r\n isPty: true\r\n });\r\n }\r\n });\r\n }\r\n } else if (currentContext.type === 'ssh') {\r\n // SSH execution with PTY for proper TTY handling\r\n const remoteCwd = executionCwd;\r\n const sshClient = currentContext.handler?.client;\r\n\r\n if (!sshClient) {\r\n throw new Error('SSH client not available');\r\n }\r\n\r\n return new Promise<string>((resolve, reject) => {\r\n let output = '';\r\n\r\n // Calculate constrained terminal dimensions for proper escape sequence handling\r\n const constrainedCols = Math.max(40, (process.stdout.columns || 80) - 4);\r\n const constrainedRows = Math.min(process.stdout.rows || 24, 50);\r\n\r\n const sshPty = runSSHCommand(\r\n sshClient,\r\n command,\r\n remoteCwd,\r\n (data: string) => {\r\n // Stream output to UI\r\n output += data;\r\n if (context.onStreamingOutput) {\r\n context.onStreamingOutput(data, 'stdout');\r\n }\r\n },\r\n (exitCode: number) => {\r\n // Clear the interactive process\r\n if (context.cliAdapter) {\r\n context.cliAdapter.setCurrentInteractiveProcess(undefined);\r\n }\r\n\r\n if (exitCode !== 0) {\r\n reject(new Error(`${output || `Command failed with exit code ${exitCode}`}\\n\\nPossible reasons:\\n1. The command is invalid or not installed.\\n2. The arguments are incorrect.`));\r\n } else {\r\n resolve(output || 'Command executed successfully');\r\n }\r\n },\r\n constrainedCols,\r\n constrainedRows\r\n );\r\n\r\n // Set up interactive process for stdin\r\n if (context.cliAdapter) {\r\n context.cliAdapter.setCurrentInteractiveProcess({\r\n process: null,\r\n write: (data: string) => sshPty.write(data),\r\n kill: () => sshPty.kill(),\r\n signal: (sig: NodeJS.Signals) => {\r\n if (sig === 'SIGINT') {\r\n sshPty.write('\\x03'); // Ctrl+C\r\n }\r\n },\r\n resize: (cols: number, rows: number) => sshPty.resize(cols, rows),\r\n isPty: true\r\n });\r\n }\r\n });\r\n }\r\n },\r\n};\r\n"],"mappings":"AACA,YAAY,gBAAgB;AAC5B,YAAY,QAAQ;AACpB,YAAY,UAAU;AAEtB,SAAS,eAAe,kBAAkB,qBAAqB;AAG/D,SAAS,kBAAkB,OAAuB;AAC9C,MAAI,YAAY,MAEX,QAAQ,QAAQ,IAAI,EACpB,QAAQ,QAAQ,IAAI,EACpB,QAAQ,QAAQ,GAAI,EACpB,QAAQ,QAAQ,IAAI,EACpB,QAAQ,UAAU,MAAM;AAG7B,QAAM,SAAoC;AAAA,IACtC,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,WAAW;AAAA,IACX,UAAU;AAAA,IACV,WAAW;AAAA;AAAA,IACX,SAAS;AAAA,IACT,SAAS;AAAA,IACT,eAAe;AAAA,IACf,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,YAAY;AAAA,EAChB;AAGA,aAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC9C,gBAAY,UAAU,MAAM,GAAG,EAAE,KAAK,IAAI;AAAA,EAC9C;AAEA,SAAO;AACX;AAEA,SAAS,+BACL,gBACA,YACA,YACM;AACN,MAAI,eAAe,SAAS,SAAS;AACjC,WAAO,aAAa,KAAK,QAAQ,YAAY,UAAU,IAAI;AAAA,EAC/D;AAEA,QAAM,aAAa,eAAe,UAAU,oBAAoB,cAAc;AAC9E,MAAI,CAAC,YAAY;AACb,WAAO;AAAA,EACX;AAEA,SAAO,KAAK,MAAM,WAAW,UAAU,IACjC,aACA,KAAK,MAAM,KAAK,YAAY,UAAU;AAChD;AAGO,MAAM,iBAAuB;AAAA,EAChC,QAAQ;AAAA,IACJ,MAAM;AAAA,IACN,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASb,YAAY;AAAA,MACR,MAAM;AAAA,MACN,YAAY;AAAA,QACR,aAAa;AAAA,UACT,MAAM;AAAA,UACN,aAAa;AAAA,QACjB;AAAA,QACA,aAAa;AAAA,UACT,MAAM;AAAA,UACN,aAAa;AAAA,QACjB;AAAA,QACA,KAAK;AAAA,UACD,MAAM;AAAA,UACN,aAAa;AAAA,QACjB;AAAA,QACA,eAAe;AAAA,UACX,MAAM;AAAA,UACN,aAAa;AAAA,QACjB;AAAA,QACA,mBAAmB;AAAA,UACf,MAAM;AAAA,UACN,aAAa;AAAA,QACjB;AAAA,QACA,aAAa;AAAA,UACT,MAAM;AAAA,UACN,aAAa;AAAA,QACjB;AAAA,MACJ;AAAA,MACA,UAAU,CAAC,eAAe,eAAe,OAAO,iBAAiB,mBAAmB;AAAA,IACxF;AAAA,EACJ;AAAA,EACA,MAAM,QAAQ,MAAM,SAAS;AACzB,UAAM,EAAE,aAAa,KAAK,YAAY,IAAI;AAI1C,QAAI,aAAa;AACb,UAAI,QAAQ,YAAY;AACpB,cAAM,qBAAqB,QAAQ,WAAW,6BAA6B;AAC3E,YAAI,CAAC,oBAAoB;AAErB,gBAAM,IAAI;AAAA,YACN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAMJ;AAAA,QACJ;AAEA,cAAM,iBAAiB,kBAAkB,WAAW;AAGpD,2BAAmB,MAAM,cAAc;AAGvC,cAAM,eAAe,eAChB,QAAQ,OAAO,KAAK,EACpB,QAAQ,OAAO,KAAK,EACpB,QAAQ,SAAS,IAAI,EACrB,QAAQ,WAAW,IAAI;AAE5B,eAAO,+BAA+B,YAAY;AAAA,MACtD,OAAO;AACH,cAAM,IAAI,MAAM,4CAA4C;AAAA,MAChE;AAAA,IACJ;AAEA,UAAM,UAAU;AAChB,UAAM,iBAAiB,QAAQ;AAC/B,UAAM,iBAAiB,eAAe,kBAAkB;AAKxD,UAAM,mBAAmB,OAAO,KAAK,OAAO;AAC5C,UAAM,UAAU,CAAC,oBAAoB,QAAQ,MAAM,aAAa;AAChE,QAAI,SAAS;AACT,UAAI,eAAe,SAAS,SAAS;AACjC,cAAM,YAAY,QAAQ,CAAC,EAAE,KAAK;AAClC,cAAM,SAAS,KAAK,QAAQ,OAAO,QAAQ,KAAK,SAAS;AAEzD,YAAI,CAAC,GAAG,WAAW,MAAM,GAAG;AACxB,gBAAM,IAAI,MAAM,wBAAwB,SAAS;AAAA;AAAA,gEAAqE;AAAA,QAC1H;AAEA,YAAI,CAAC,GAAG,SAAS,MAAM,EAAE,YAAY,GAAG;AACpC,gBAAM,IAAI,MAAM,4BAA4B,SAAS;AAAA;AAAA,2DAAgE;AAAA,QACzH;AAEA,uBAAe,uBAAuB,MAAM;AAG5C,YAAI,QAAQ,YAAY;AACpB,kBAAQ,WAAW,MAAM;AACzB,cAAI,QAAQ,WAAW,aAAa;AAChC,oBAAQ,WAAW,YAAY,MAAM;AAAA,UACzC;AAAA,QACJ;AAEA,eAAO,yBAAyB,MAAM;AAAA,MAC1C,OAAO;AAEH,cAAM,YAAY,QAAQ,CAAC,EAAE,KAAK;AAClC,cAAM,SAAS,MAAM,eAAe,eAAe,MAAM,SAAS,EAAE;AACpE,YAAI,OAAO,aAAa,GAAG;AAEvB,cAAI,eAAe,SAAS;AACxB,kBAAM,SAAS,MAAM,eAAe,QAAQ,2BAA2B;AACvE,gBAAI,QAAQ;AACR,6BAAe,uBAAuB,MAAM;AAG5C,kBAAI,QAAQ,YAAY;AACpB,wBAAQ,WAAW,MAAM;AACzB,oBAAI,QAAQ,WAAW,aAAa;AAChC,0BAAQ,WAAW,YAAY,MAAM;AAAA,gBACzC;AAAA,cACJ;AAAA,YACJ;AAAA,UACJ;AACA,iBAAO,yBAAyB,eAAe,kBAAkB,EAAE,SAAS,gBAAgB;AAAA,QAChG,OAAO;AACH,gBAAM,IAAI,MAAM,GAAG,OAAO,UAAU,4BAA4B;AAAA;AAAA,+DAAoE;AAAA,QACxI;AAAA,MACJ;AAAA,IACJ;AAGA,UAAM,sBAAsB;AAAA,MACxB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AAEA,UAAM,gBAAgB,oBAAoB,KAAK,CAAC,YAAY,QAAQ,KAAK,OAAO,CAAC;AAEjF,QAAI,eAAe;AACf,YAAM,IAAI;AAAA,QACN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2CAM4C,OAAO;AAAA,MACvD;AAAA,IACJ;AAGA,UAAM,gBAAgB,CAAC,YAAY,aAAa,UAAU,UAAU;AACpE,UAAM,UAAU,cAAc,KAAK,CAAC,YAAY,QAAQ,KAAK,OAAO,CAAC;AAErE,UAAM,WAAW,MAAM,QAAQ;AAAA,MAC3B,oBAAoB,OAAO,GAAG,eAAe,SAAS,UAAU,QAAQ,eAAe,IAAI,kBAAkB,EAAE;AAAA,MAC/G;AAAA,MACA;AAAA,MACA;AAAA,MACA,EAAE,QAAQ;AAAA,IACd;AAGA,QAAI,OAAO,aAAa,YAAY,cAAc,UAAU;AACxD,YAAM,IAAI,MAAM,kBAAkB,SAAS,QAAQ,EAAE;AAAA,IACzD;AAGA,QAAI,CAAC,UAAU;AACX,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAClD;AAEA,UAAM,eAAe,+BAA+B,gBAAgB,QAAQ,KAAK,GAAG;AAGpF,QAAI,eAAe,SAAS,SAAS;AAEjC,aAAO,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC5C,YAAI,SAAS;AACb,YAAI;AAGJ,cAAM,qBAAqB,WAAW;AAAA,UAClC;AAAA,UACA;AAAA,UACA,CAAC,SAAiB;AAEd,sBAAU;AAGV,gBAAI,QAAQ,mBAAmB;AAC3B,sBAAQ,kBAAkB,MAAM,QAAQ;AAAA,YAC5C;AAAA,UACJ;AAAA,UACA,CAAC,SAAiB;AAEd,uBAAW;AAGX,gBAAI,QAAQ,YAAY;AACpB,sBAAQ,WAAW,6BAA6B,MAAS;AAAA,YAC7D;AAGA,gBAAI,SAAS,GAAG;AACZ,sBAAQ,UAAU,+BAA+B;AAAA,YACrD,OAAO;AACH,oBAAM,cAAc,SAAS;AAAA,aAAgB,IAAI;AACjD,qBAAO,IAAI,MAAM,GAAG,eAAe,iCAAiC,IAAI,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA,uDAA6J,CAAC;AAAA,YAC5O;AAAA,UACJ;AAAA,QACJ;AAGA,YAAI,QAAQ,YAAY;AACpB,kBAAQ,WAAW,6BAA6B,kBAAkB;AAAA,QACtE;AAAA,MACJ,CAAC;AAAA,IACL,WAAW,eAAe,SAAS,OAAO;AAEtC,YAAM,YAAY;AAClB,YAAM,eAAe,eAAe,UAAU,cAAc;AAE5D,aAAO,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC5C,YAAI,SAAS;AAKb,cAAM,kBAAkB,KAAK,IAAI,KAAK,QAAQ,OAAO,WAAW,MAAM,CAAC;AACvE,cAAM,kBAAkB,KAAK,IAAI,QAAQ,OAAO,QAAQ,IAAI,EAAE;AAE9D,cAAM,SAAS;AAAA,UACX;AAAA,UACA;AAAA,UACA;AAAA,UACA,CAAC,SAAiB;AAEd,sBAAU;AACV,gBAAI,QAAQ,mBAAmB;AAC3B,sBAAQ,kBAAkB,MAAM,QAAQ;AAAA,YAC5C;AAAA,UACJ;AAAA,UACA,CAAC,aAAqB;AAElB,gBAAI,QAAQ,YAAY;AACpB,sBAAQ,WAAW,6BAA6B,MAAS;AAAA,YAC7D;AAEA,gBAAI,aAAa,GAAG;AAChB,qBAAO,IAAI,MAAM,GAAG,UAAU,iCAAiC,QAAQ,EAAE;AAAA;AAAA;AAAA;AAAA,gCAAqG,CAAC;AAAA,YACnL,OAAO;AACH,sBAAQ,UAAU,+BAA+B;AAAA,YACrD;AAAA,UACJ;AAAA,UACA;AAAA,UACA;AAAA,QACJ;AAGA,YAAI,QAAQ,YAAY;AACpB,kBAAQ,WAAW,6BAA6B;AAAA,YAC5C,SAAS;AAAA,YACT,OAAO,CAAC,SAAiB,OAAO,MAAM,IAAI;AAAA,YAC1C,MAAM,MAAM,OAAO,KAAK;AAAA,YACxB,QAAQ,CAAC,QAAwB;AAC7B,kBAAI,QAAQ,UAAU;AAClB,uBAAO,MAAM,GAAM;AAAA,cACvB;AAAA,YACJ;AAAA,YACA,QAAQ,CAAC,MAAc,SAAiB,OAAO,OAAO,MAAM,IAAI;AAAA,YAChE,OAAO;AAAA,UACX,CAAC;AAAA,QACL;AAAA,MACJ,CAAC;AAAA,IACL,WAAW,eAAe,SAAS,UAAU;AAEzC,YAAM,gBAAgB,eAAe,iBAAiB;AAEtD,YAAM,YAAY;AAClB,YAAM,cAAc,eAAe,UAAU,eAAe;AAG5D,YAAM,kBAAkB,KAAK,IAAI,KAAK,QAAQ,OAAO,WAAW,MAAM,CAAC;AACvE,YAAM,kBAAkB,KAAK,IAAI,QAAQ,OAAO,QAAQ,IAAI,EAAE;AAE9D,UAAI,iBAAiB,cAAc,SAAS,OAAO;AAE/C,cAAM,YAAY,cAAc,SAAS;AAEzC,YAAI,CAAC,WAAW;AACZ,gBAAM,IAAI,MAAM,oDAAoD;AAAA,QACxE;AAIA,cAAM,iBAAiB,QAAQ,QAAQ,MAAM,KAAK;AAClD,cAAM,gBAAgB,mBAAmB,SAAS,KAAK,WAAW,WAAW,cAAc;AAE3F,eAAO,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC5C,cAAI,SAAS;AAEb,gBAAM,SAAS;AAAA,YACX;AAAA,YACA;AAAA,YACA,cAAc,SAAS,oBAAoB;AAAA,YAC3C,CAAC,SAAiB;AAEd,wBAAU;AACV,kBAAI,QAAQ,mBAAmB;AAC3B,wBAAQ,kBAAkB,MAAM,QAAQ;AAAA,cAC5C;AAAA,YACJ;AAAA,YACA,CAAC,aAAqB;AAElB,kBAAI,QAAQ,YAAY;AACpB,wBAAQ,WAAW,6BAA6B,MAAS;AAAA,cAC7D;AAEA,kBAAI,aAAa,GAAG;AAChB,uBAAO,IAAI,MAAM,GAAG,UAAU,iCAAiC,QAAQ,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA,4CAAmK,CAAC;AAAA,cACjP,OAAO;AACH,wBAAQ,UAAU,+BAA+B;AAAA,cACrD;AAAA,YACJ;AAAA,YACA;AAAA,YACA;AAAA,UACJ;AAGA,cAAI,QAAQ,YAAY;AACpB,oBAAQ,WAAW,6BAA6B;AAAA,cAC5C,SAAS;AAAA,cACT,OAAO,CAAC,SAAiB,OAAO,MAAM,IAAI;AAAA,cAC1C,MAAM,MAAM,OAAO,KAAK;AAAA,cACxB,QAAQ,CAAC,QAAwB;AAC7B,oBAAI,QAAQ,UAAU;AAClB,yBAAO,MAAM,GAAM;AAAA,gBACvB;AAAA,cACJ;AAAA,cACA,QAAQ,CAAC,MAAc,SAAiB,OAAO,OAAO,MAAM,IAAI;AAAA,cAChE,OAAO;AAAA,YACX,CAAC;AAAA,UACL;AAAA,QACJ,CAAC;AAAA,MACL,OAAO;AAEH,eAAO,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC5C,cAAI,SAAS;AAEb,gBAAM,YAAY;AAAA,YACd;AAAA,YACA;AAAA,YACA;AAAA,YACA,CAAC,SAAiB;AAEd,wBAAU;AACV,kBAAI,QAAQ,mBAAmB;AAC3B,wBAAQ,kBAAkB,MAAM,QAAQ;AAAA,cAC5C;AAAA,YACJ;AAAA,YACA,CAAC,aAAqB;AAElB,kBAAI,QAAQ,YAAY;AACpB,wBAAQ,WAAW,6BAA6B,MAAS;AAAA,cAC7D;AAEA,kBAAI,aAAa,GAAG;AAChB,uBAAO,IAAI,MAAM,GAAG,UAAU,iCAAiC,QAAQ,EAAE;AAAA;AAAA;AAAA;AAAA,gCAAqG,CAAC;AAAA,cACnL,OAAO;AACH,wBAAQ,UAAU,+BAA+B;AAAA,cACrD;AAAA,YACJ;AAAA,YACA;AAAA,YACA;AAAA,UACJ;AAGA,cAAI,QAAQ,YAAY;AACpB,oBAAQ,WAAW,6BAA6B;AAAA,cAC5C,SAAS;AAAA,cACT,OAAO,CAAC,SAAiB,UAAU,MAAM,IAAI;AAAA,cAC7C,MAAM,MAAM,UAAU,KAAK;AAAA,cAC3B,QAAQ,CAAC,QAAwB;AAC7B,oBAAI,QAAQ,UAAU;AAClB,4BAAU,MAAM,GAAM;AAAA,gBAC1B;AAAA,cACJ;AAAA,cACA,QAAQ,CAAC,MAAc,SAAiB,UAAU,OAAO,MAAM,IAAI;AAAA,cACnE,OAAO;AAAA,YACX,CAAC;AAAA,UACL;AAAA,QACJ,CAAC;AAAA,MACL;AAAA,IACJ,WAAW,eAAe,SAAS,OAAO;AAEtC,YAAM,YAAY;AAClB,YAAM,YAAY,eAAe,SAAS;AAE1C,UAAI,CAAC,WAAW;AACZ,cAAM,IAAI,MAAM,0BAA0B;AAAA,MAC9C;AAEA,aAAO,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC5C,YAAI,SAAS;AAGb,cAAM,kBAAkB,KAAK,IAAI,KAAK,QAAQ,OAAO,WAAW,MAAM,CAAC;AACvE,cAAM,kBAAkB,KAAK,IAAI,QAAQ,OAAO,QAAQ,IAAI,EAAE;AAE9D,cAAM,SAAS;AAAA,UACX;AAAA,UACA;AAAA,UACA;AAAA,UACA,CAAC,SAAiB;AAEd,sBAAU;AACV,gBAAI,QAAQ,mBAAmB;AAC3B,sBAAQ,kBAAkB,MAAM,QAAQ;AAAA,YAC5C;AAAA,UACJ;AAAA,UACA,CAAC,aAAqB;AAElB,gBAAI,QAAQ,YAAY;AACpB,sBAAQ,WAAW,6BAA6B,MAAS;AAAA,YAC7D;AAEA,gBAAI,aAAa,GAAG;AAChB,qBAAO,IAAI,MAAM,GAAG,UAAU,iCAAiC,QAAQ,EAAE;AAAA;AAAA;AAAA;AAAA,gCAAqG,CAAC;AAAA,YACnL,OAAO;AACH,sBAAQ,UAAU,+BAA+B;AAAA,YACrD;AAAA,UACJ;AAAA,UACA;AAAA,UACA;AAAA,QACJ;AAGA,YAAI,QAAQ,YAAY;AACpB,kBAAQ,WAAW,6BAA6B;AAAA,YAC5C,SAAS;AAAA,YACT,OAAO,CAAC,SAAiB,OAAO,MAAM,IAAI;AAAA,YAC1C,MAAM,MAAM,OAAO,KAAK;AAAA,YACxB,QAAQ,CAAC,QAAwB;AAC7B,kBAAI,QAAQ,UAAU;AAClB,uBAAO,MAAM,GAAM;AAAA,cACvB;AAAA,YACJ;AAAA,YACA,QAAQ,CAAC,MAAc,SAAiB,OAAO,OAAO,MAAM,IAAI;AAAA,YAChE,OAAO;AAAA,UACX,CAAC;AAAA,QACL;AAAA,MACJ,CAAC;AAAA,IACL;AAAA,EACJ;AACJ;","names":[]}
1
+ {"version":3,"sources":["../../src/tools/command.ts"],"sourcesContent":["import { Tool } from './types.js';\r\nimport * as shellUtils from '../utils/shell.js';\r\nimport * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport { ContextManager } from '../context/context-manager.js';\r\nimport { runWSLCommand, runDockerCommand, runSSHCommand } from '../utils/editor-utils.js';\r\nimport { BackgroundTaskManager, RemotePtyProcess } from '../services/background-task-manager.js';\r\nimport { quickLog } from '../utils/conversation-logger.js';\r\n\r\n/**\r\n * Extract cd targets from a compound command string.\r\n * Handles commands like:\r\n * - \"mkdir foo && cd foo\"\r\n * - \"cd foo && ls && cd bar\"\r\n * - \"echo hello; cd /tmp; pwd\"\r\n * - \"Set-Location foo\" (PowerShell)\r\n * - \"cd /d C:\\path\" (cmd.exe)\r\n * - \"pushd /tmp && some_cmd\"\r\n *\r\n * Splits by &&, ;, and || then scans each segment for cd-like commands.\r\n * Returns an ordered list of target directory strings.\r\n */\r\nexport function extractCdTargetsFromCommand(command: string): string[] {\r\n const targets: string[] = [];\r\n\r\n // Split by common shell compound operators: &&, ||, ;\r\n // We do NOT split by | (pipe) since cd in a pipeline subshell doesn't affect parent\r\n const segments = command.split(/\\s*(?:&&|\\|\\||;)\\s*/);\r\n\r\n for (const segment of segments) {\r\n const trimmed = segment.trim();\r\n if (!trimmed) continue;\r\n\r\n // Match various cd/pushd syntaxes across platforms:\r\n // cd <path> (all platforms)\r\n // cd /d <path> (cmd.exe)\r\n // pushd <path> (bash / cmd / PowerShell)\r\n // Set-Location <path> (PowerShell)\r\n // sl <path> (PowerShell alias)\r\n // chdir <path> (cmd.exe alias)\r\n\r\n let match: RegExpMatchArray | null = null;\r\n\r\n // cd /d <path> (Windows cmd)\r\n match = trimmed.match(/^cd\\s+\\/d\\s+(.+)$/i);\r\n if (!match) {\r\n // cd <path> (universal)\r\n match = trimmed.match(/^cd\\s+(.+)$/i);\r\n }\r\n if (!match) {\r\n // pushd <path>\r\n match = trimmed.match(/^pushd\\s+(.+)$/i);\r\n }\r\n if (!match) {\r\n // Set-Location <path> (PowerShell)\r\n match = trimmed.match(/^Set-Location\\s+(.+)$/i);\r\n }\r\n if (!match) {\r\n // sl <path> (PowerShell short alias, but only if it looks like a directory path)\r\n match = trimmed.match(/^sl\\s+(.+)$/i);\r\n }\r\n if (!match) {\r\n // chdir <path> (cmd.exe)\r\n match = trimmed.match(/^chdir\\s+(.+)$/i);\r\n }\r\n\r\n if (match) {\r\n // Clean up the extracted path: remove surrounding quotes, trailing whitespace\r\n let target = match[1].trim();\r\n target = target.replace(/^[\"']|[\"']$/g, '');\r\n // Strip trailing slashes/backslashes (except root paths)\r\n if (target.length > 1) {\r\n target = target.replace(/[\\/\\\\]+$/, '');\r\n }\r\n targets.push(target);\r\n }\r\n }\r\n\r\n return targets;\r\n}\r\n\r\n/**\r\n * Resolve the final CWD after executing a command that may contain cd subcommands.\r\n * Walks through the cd targets sequentially, resolving each relative to the previous.\r\n * Returns the final resolved CWD, or null if no cd was found or the path doesn't exist.\r\n */\r\nexport function resolvePostCommandCwd(\r\n currentCwd: string,\r\n command: string,\r\n isWindows: boolean = false\r\n): string | null {\r\n const targets = extractCdTargetsFromCommand(command);\r\n if (targets.length === 0) return null;\r\n\r\n let cwd = currentCwd;\r\n for (const target of targets) {\r\n // Handle home directory expansion\r\n let expanded = target;\r\n if (expanded === '~' || expanded.startsWith('~/') || expanded.startsWith('~\\\\')) {\r\n const homeDir = process.env.HOME || process.env.USERPROFILE || '';\r\n expanded = homeDir + expanded.substring(1);\r\n }\r\n // Handle - (previous directory) - can't resolve this, skip\r\n if (expanded === '-') continue;\r\n\r\n cwd = path.resolve(cwd, expanded);\r\n }\r\n\r\n // Verify the resolved path exists and is a directory\r\n try {\r\n if (fs.existsSync(cwd) && fs.statSync(cwd).isDirectory()) {\r\n return cwd;\r\n }\r\n } catch {\r\n // Path doesn't exist or can't be accessed\r\n }\r\n\r\n return null;\r\n}\r\n\r\n// Helper to process shell input with escape sequences and special keys\r\nfunction processShellInput(input: string): string {\r\n let processed = input\r\n // Handle standard escape sequences\r\n .replace(/\\\\n/g, '\\r') // Use \\r for Enter/Newline in interactive shell\r\n .replace(/\\\\r/g, '\\r')\r\n .replace(/\\\\t/g, '\\t')\r\n .replace(/\\\\b/g, '\\b')\r\n .replace(/\\\\x1B/g, '\\x1B');\r\n\r\n // Handle special key codes\r\n const keyMap: { [key: string]: string } = {\r\n '[UP]': '\\u001b[A',\r\n '[DOWN]': '\\u001b[B',\r\n '[RIGHT]': '\\u001b[C',\r\n '[LEFT]': '\\u001b[D',\r\n '[ENTER]': '\\r', // Enter key sends CR\r\n '[TAB]': '\\t',\r\n '[ESC]': '\\u001b',\r\n '[BACKSPACE]': '\\b',\r\n '[CTRL+C]': '\\u0003',\r\n '[CTRL+D]': '\\u0004',\r\n '[CTRL+Z]': '\\u001a'\r\n };\r\n\r\n // Replace special keys\r\n for (const [key, code] of Object.entries(keyMap)) {\r\n processed = processed.split(key).join(code);\r\n }\r\n\r\n return processed;\r\n}\r\n\r\nfunction resolveCommandWorkingDirectory(\r\n currentContext: any,\r\n contextCwd: string,\r\n commandCwd?: string\r\n): string {\r\n if (currentContext.type === 'local') {\r\n return commandCwd ? path.resolve(contextCwd, commandCwd) : contextCwd;\r\n }\r\n\r\n const remoteBase = currentContext.metadata?.workingDirectory || contextCwd || '~';\r\n if (!commandCwd) {\r\n return remoteBase;\r\n }\r\n\r\n return path.posix.isAbsolute(commandCwd)\r\n ? commandCwd\r\n : path.posix.join(remoteBase, commandCwd);\r\n}\r\n\r\n/**\r\n * Sentinel value used to signal that a timeout-based transfer occurred.\r\n * The resolve callback receives this instead of normal command output.\r\n */\r\nconst TIMEOUT_TRANSFER_PREFIX = '__TIMEOUT_TRANSFER__';\r\n\r\n/**\r\n * Build the transfer message that the AI will receive as the tool result.\r\n */\r\nfunction buildTransferMessage(taskId: string, command: string, cwd: string, timeoutSeconds: number, outputSoFar: string, remoteContext?: string): string {\r\n const outputPreview = outputSoFar.length > 500\r\n ? outputSoFar.slice(-500)\r\n : outputSoFar;\r\n\r\n const location = remoteContext ? ` (on ${remoteContext})` : '';\r\n\r\n return `Command exceeded timeout of ${timeoutSeconds}s and has been transferred to background${location}.\r\n\r\n**Background Task ID:** ${taskId}\r\n**Command:** ${command}\r\n**Working Directory:** ${cwd}\r\n**Status:** Still running in background\r\n\r\n**Output so far (last 500 chars):**\r\n\\`\\`\\`\r\n${outputPreview || '(no output yet)'}\r\n\\`\\`\\`\r\n\r\nUse \\`background_command\\` with action=\"status\" and task_id=\"${taskId}\" to check progress.\r\nUse \\`background_command\\` with action=\"kill\" and task_id=\"${taskId}\" to terminate it.`;\r\n}\r\n\r\n\r\nexport const runCommandTool: Tool = {\r\n schema: {\r\n name: 'execute_command',\r\n description: `PROPOSE a command to run on behalf of the user. Operating System: windows. Shell: pwsh.\r\n**NEVER PROPOSE A cd COMMAND**.\r\nIf you have this tool, note that you DO have the ability to run commands directly on the USER's system.\r\nMake sure to specify CommandLine exactly as it should be run in the shell.\r\nNote that the user will have to approve the command before it is executed. The user may reject it if it is not to their liking.\r\nThe actual command will NOT execute until the user approves it. The user may not approve it immediately.\r\nIf the step is WAITING for user approval, it has NOT started running.\r\nIf the step returns a command id, it means that the command was sent to the background. You should use the command_status tool to monitor the output and status of the command.\r\nCommands will be run with PAGER=cat. You may want to limit the length of output for commands that usually rely on paging and may contain very long output (e.g. git log, use git log -n <N>).`,\r\n parameters: {\r\n type: 'object',\r\n properties: {\r\n reason_text: {\r\n type: 'string',\r\n description: 'REQUIRED: A brief explanation of why you are executing this command and what you expect it to do. This will be shown to the user. Example: \"Running npm install to install the project dependencies\"',\r\n },\r\n CommandLine: {\r\n type: 'string',\r\n description: 'The exact command line string to execute.',\r\n },\r\n Cwd: {\r\n type: 'string',\r\n description: 'The current working directory for the command',\r\n },\r\n SafeToAutoRun: {\r\n type: 'boolean',\r\n description: 'Set to true if you believe that this command is safe to run WITHOUT user approval. A command is unsafe if it may have some destructive side-effects. Example unsafe side-effects include: deleting files, mutating state, installing system dependencies, making external requests, etc. Set to true only if you are extremely confident it is safe. If you feel the command could be unsafe, never set this to true, EVEN if the USER asks you to. It is imperative that you never auto-run a potentially unsafe command.',\r\n },\r\n WaitMsBeforeAsync: {\r\n type: 'integer',\r\n description: 'This specifies the number of milliseconds to wait after starting the command before sending it to the background. If you want the command to complete execution synchronously, set this to a large enough value that you expect the command to complete in that time under ordinary circumstances. If you\\'re starting an interactive or long-running command, set it to a large enough value that it would cause possible failure cases to execute synchronously (e.g. 500ms). Keep the value as small as possible, with a maximum of 10000ms.',\r\n },\r\n shell_input: {\r\n type: 'string',\r\n description: '[INTERNAL - DO NOT USE FROM MAIN AGENT] Input to send to a CURRENTLY RUNNING interactive shell. This parameter is used internally by the shell input sub-agent. When starting a command, do NOT set this parameter - just start the command and the user will provide input through the shell UI or agent control mode.',\r\n },\r\n timeout_seconds: {\r\n type: 'integer',\r\n description: 'Optional timeout in seconds. If the command takes longer than this, it will be automatically transferred to a background shell and you will receive the background task ID to monitor it. Use this for commands that might take a while (e.g., builds, installs, tests) where you want to continue working if they run long. If not set, the command runs until completion with no timeout.',\r\n },\r\n },\r\n required: ['reason_text', 'CommandLine', 'Cwd', 'SafeToAutoRun', 'WaitMsBeforeAsync'],\r\n },\r\n },\r\n async execute(args, context) {\r\n const { CommandLine, Cwd, shell_input, timeout_seconds } = args;\r\n const timeoutMs = timeout_seconds && timeout_seconds > 0 ? timeout_seconds * 1000 : 0;\r\n\r\n // SAFETY NET: If a custom tunnel is active, ALWAYS route through the tunnel PTY.\r\n // This prevents any execute_command from accidentally creating a new shell and\r\n // destroying the tunnel, even if the in-stream/post-stream interception missed it.\r\n if (!shell_input && context.cliAdapter && context.cliAdapter.isCustomTunnelActive()) {\r\n const interactiveProcess = context.cliAdapter.getCurrentInteractiveProcess();\r\n if (interactiveProcess && CommandLine) {\r\n const inputWithEnter = CommandLine.endsWith('\\n') || CommandLine.endsWith('\\r')\r\n ? CommandLine : CommandLine + '\\r';\r\n const processedInput = processShellInput(inputWithEnter);\r\n interactiveProcess.write(processedInput);\r\n\r\n // Wait for output to stabilize by polling the tunnel output buffer\r\n const tunnelBuf = () => (context.cliAdapter as any).tunnelOutputBuffer as string || '';\r\n const beforeLen = tunnelBuf().length;\r\n let lastLen = beforeLen;\r\n let stableTime = 0;\r\n const waitStart = Date.now();\r\n while (Date.now() - waitStart < 5000) {\r\n await new Promise(r => setTimeout(r, 300));\r\n const curLen = tunnelBuf().length;\r\n if (curLen > lastLen) { lastLen = curLen; stableTime = 0; }\r\n else { stableTime += 300; if (stableTime >= 800) break; }\r\n }\r\n\r\n const captured = tunnelBuf().substring(beforeLen);\r\n const { processTerminalOutput } = await import('../utils/terminal-output.js');\r\n return processTerminalOutput(captured).trim() || '(command sent, no output captured)';\r\n }\r\n // Process gone — clear tunnel and fall through to normal execution\r\n if (!interactiveProcess) {\r\n (context.cliAdapter as any).customTunnelCommand = null;\r\n }\r\n }\r\n\r\n // Handle shell_input action - send input to a running shell\r\n // NOTE: shell_input should only be used by ShellInputAgent, not the main agent\r\n if (shell_input) {\r\n if (context.cliAdapter) {\r\n const interactiveProcess = context.cliAdapter.getCurrentInteractiveProcess();\r\n if (!interactiveProcess) {\r\n // Provide a helpful error message guiding the correct approach\r\n throw new Error(\r\n `Cannot use shell_input when no interactive shell is running. ` +\r\n `To run an interactive command:\\n` +\r\n `1. First, run the command WITHOUT shell_input - just use CommandLine\\n` +\r\n `2. The interactive shell will open and you can provide input there\\n` +\r\n `3. Agent Control mode (if enabled) will automatically handle input prompts\\n\\n` +\r\n `Please retry: run the command first, then input will be handled automatically.`\r\n );\r\n }\r\n // Process input to handle escapes and special keys\r\n const processedInput = processShellInput(shell_input);\r\n\r\n // Send the processed input\r\n interactiveProcess.write(processedInput);\r\n\r\n // Return readable confirmation (showing what was actually sent)\r\n const readableSent = processedInput\r\n .replace(/\\n/g, '\\\\n')\r\n .replace(/\\r/g, '\\\\r')\r\n .replace(/\\x1B/g, '^[')\r\n .replace(/\\u0003/g, '^C');\r\n\r\n return `Sent input to active shell: ${readableSent}`;\r\n } else {\r\n throw new Error('CLI adapter not available for shell input.');\r\n }\r\n }\r\n\r\n const command = CommandLine; // Map to internal variable\r\n const contextManager = context.contextManager as ContextManager;\r\n const currentContext = contextManager.getCurrentContext();\r\n\r\n // Special handling for cd command - change the actual working directory\r\n // Only intercept simple cd commands, not chained ones (cd path && cmd, cd path; cmd)\r\n // Chained commands will be executed by the shell directly\r\n const isChainedCommand = /&&|;/.test(command);\r\n const cdMatch = !isChainedCommand && command.match(/^cd\\s+(.+)$/);\r\n if (cdMatch) {\r\n if (currentContext.type === 'local') {\r\n const targetDir = cdMatch[1].trim();\r\n const newCwd = path.resolve(Cwd || context.cwd, targetDir);\r\n\r\n if (!fs.existsSync(newCwd)) {\r\n throw new Error(`Directory not found: ${targetDir}\\n\\nAction: Use 'list_directory' to verify the directory structure.`);\r\n }\r\n\r\n if (!fs.statSync(newCwd).isDirectory()) {\r\n throw new Error(`Path is not a directory: ${targetDir}\\n\\nAction: Verify the path points to a directory, not a file.`);\r\n }\r\n\r\n contextManager.updateWorkingDirectory(newCwd);\r\n\r\n // Update CLI adapter CWD and notify UI\r\n if (context.cliAdapter) {\r\n context.cliAdapter.cwd = newCwd;\r\n if (context.cliAdapter.onCwdChange) {\r\n context.cliAdapter.onCwdChange(newCwd);\r\n }\r\n }\r\n\r\n return `Changed directory to: ${newCwd}`;\r\n } else {\r\n // Execute cd in subshell and update working directory\r\n const targetDir = cdMatch[1].trim();\r\n const result = await contextManager.executeCommand(`cd ${targetDir}`);\r\n if (result.exitCode === 0) {\r\n // Get the actual new working directory from the handler\r\n if (currentContext.handler) {\r\n const newCwd = await currentContext.handler.getCurrentWorkingDirectory();\r\n if (newCwd) {\r\n contextManager.updateWorkingDirectory(newCwd);\r\n\r\n // Update CLI adapter CWD and notify UI\r\n if (context.cliAdapter) {\r\n context.cliAdapter.cwd = newCwd;\r\n if (context.cliAdapter.onCwdChange) {\r\n context.cliAdapter.onCwdChange(newCwd);\r\n }\r\n }\r\n }\r\n }\r\n return `Changed directory to: ${contextManager.getCurrentContext().metadata.workingDirectory}`;\r\n } else {\r\n throw new Error(`${result.stderr || 'Failed to change directory'}\\n\\nAction: Verify the directory exists in the remote environment.`);\r\n }\r\n }\r\n }\r\n\r\n // Check for long-running commands\r\n const longRunningPatterns = [\r\n /npm\\s+run\\s+dev/,\r\n /npm\\s+run\\s+start/,\r\n /npm\\s+start/,\r\n /yarn\\s+dev/,\r\n /yarn\\s+start/,\r\n /pnpm\\s+dev/,\r\n /pnpm\\s+start/,\r\n /python\\s+-m\\s+http\\.server/,\r\n /python3\\s+-m\\s+http\\.server/,\r\n /php\\s+-S/,\r\n /ruby\\s+-run\\s+-e\\s+httpd/,\r\n /serve\\s+/,\r\n /http-server/,\r\n /webpack\\s+--watch/,\r\n /webpack-dev-server/,\r\n /vite(?:\\s+(?!build)|\\s*$)/,\r\n /next\\s+dev/,\r\n /gatsby\\s+develop/,\r\n /rails\\s+server/,\r\n /rails\\s+s\\b/,\r\n /django\\s+runserver/,\r\n /manage\\.py\\s+runserver/,\r\n /flask\\s+run/,\r\n /nodemon/,\r\n /ng\\s+serve/,\r\n /ember\\s+serve/,\r\n /hugo\\s+server/,\r\n /jekyll\\s+serve/,\r\n /docker\\s+run.*-d/,\r\n /docker-compose\\s+up/,\r\n /tail\\s+-f/,\r\n /watch\\s+/,\r\n /less\\s+/,\r\n /more\\s+/,\r\n /vim\\s+/,\r\n /vi\\s+/,\r\n /nano\\s+/,\r\n /emacs\\s+/,\r\n ];\r\n\r\n const isLongRunning = longRunningPatterns.some((pattern) => pattern.test(command));\r\n\r\n if (isLongRunning) {\r\n throw new Error(\r\n `This command appears to be a long-running process (like a development server or file watcher). ` +\r\n `Long-running commands cannot be executed through execute_command as they would block execution.\\n\\n` +\r\n `USE THE background_command TOOL INSTEAD:\\n` +\r\n `- action: \"start\" to run the command in the background\\n` +\r\n `- action: \"status\" to check the output while it runs\\n` +\r\n `- action: \"kill\" to terminate when done\\n\\n` +\r\n `Command that needs background execution: ${command}`\r\n );\r\n }\r\n\r\n // Check for risky commands\r\n const riskyPatterns = [/rm\\s+-rf/, /del\\s+\\/s/, /format/, /dd\\s+if=/];\r\n const isRisky = riskyPatterns.some((pattern) => pattern.test(command));\r\n\r\n const approved = await context.requireApproval(\r\n `Execute command: ${command}${currentContext.type !== 'local' ? ` (in ${currentContext.type} environment)` : ''}`,\r\n isRisky,\r\n undefined,\r\n 'execute_command',\r\n { command }\r\n );\r\n\r\n // Check for feedback object (user rejected with feedback)\r\n if (typeof approved === 'object' && 'feedback' in approved) {\r\n throw new Error(`USER_FEEDBACK: ${approved.feedback}`);\r\n }\r\n\r\n // Check for boolean false (user rejected)\r\n if (!approved) {\r\n throw new Error('Operation cancelled by user.');\r\n }\r\n\r\n const executionCwd = resolveCommandWorkingDirectory(currentContext, context.cwd, Cwd);\r\n\r\n // Execute command with interactive support (allows user input via stdin)\r\n if (currentContext.type === 'local') {\r\n // Use interactive process for full stdin/stdout/stderr support\r\n return new Promise<string>((resolve, reject) => {\r\n let output = '';\r\n let exitCode: number | undefined;\r\n let transferred = false; // Flag: was this transferred to background?\r\n let timeoutTimer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n // Mutable callback refs so timeout can rewire them to background task\r\n let onDataCallback = (data: string) => {\r\n output += data;\r\n if (context.onStreamingOutput) {\r\n context.onStreamingOutput(data, 'stdout');\r\n }\r\n };\r\n\r\n let onExitCallback = (code: number) => {\r\n exitCode = code;\r\n if (timeoutTimer) { clearTimeout(timeoutTimer); timeoutTimer = null; }\r\n if (transferred) return; // Background task owns the exit now\r\n\r\n if (context.cliAdapter) {\r\n context.cliAdapter.setCurrentInteractiveProcess(undefined);\r\n }\r\n\r\n if (code === 0) {\r\n const newCwd = resolvePostCommandCwd(executionCwd, command);\r\n if (newCwd && newCwd !== executionCwd && context.cliAdapter) {\r\n context.cliAdapter.cwd = newCwd;\r\n contextManager.updateWorkingDirectory(newCwd);\r\n if (context.cliAdapter.onCwdChange) {\r\n context.cliAdapter.onCwdChange(newCwd);\r\n }\r\n }\r\n resolve(output || 'Command executed successfully');\r\n } else {\r\n const errorOutput = output + `\\nExit Code: ${code}`;\r\n reject(new Error(`${errorOutput || `Command failed with exit code ${code}`}\\n\\nPossible reasons:\\n1. The command is invalid or not installed.\\n2. The arguments are incorrect.\\n3. The command requires user input (interactive mode).`));\r\n }\r\n };\r\n\r\n // Start interactive process\r\n const interactiveProcess = shellUtils.executeCommandInteractive(\r\n command,\r\n executionCwd,\r\n (data: string) => onDataCallback(data),\r\n (code: number) => onExitCallback(code)\r\n );\r\n\r\n // Store the interactive process reference so user can send input\r\n if (context.cliAdapter) {\r\n context.cliAdapter.setCurrentInteractiveProcess(interactiveProcess);\r\n }\r\n\r\n // Set up timeout-based background transfer\r\n if (timeoutMs > 0) {\r\n timeoutTimer = setTimeout(() => {\r\n if (transferred || exitCode !== undefined) return; // Already done\r\n transferred = true;\r\n args.timeoutTransferred = true; // Flag for UI to show yellow transfer state\r\n\r\n quickLog(`[${new Date().toISOString()}] [execute_command] Timeout (${timeout_seconds}s) exceeded for: ${command}. Transferring to background.\\n`);\r\n\r\n // Adopt the still-running process into background task manager\r\n const adopted = BackgroundTaskManager.adoptRunningProcess(\r\n command, executionCwd, output\r\n );\r\n\r\n // Build a RemotePtyProcess wrapper for the interactive process\r\n const remotePty: RemotePtyProcess = {\r\n write: (data: string) => interactiveProcess.write(data),\r\n kill: () => interactiveProcess.kill(),\r\n resize: (cols: number, rows: number) => { if (interactiveProcess.resize) interactiveProcess.resize(cols, rows); },\r\n isRunning: () => interactiveProcess.ptyProcess?.isRunning?.() ?? false,\r\n };\r\n adopted.setRemotePty(remotePty);\r\n\r\n // Rewire callbacks so future output goes to background task\r\n onDataCallback = adopted.onData;\r\n onExitCallback = adopted.onExit;\r\n\r\n // Clear the foreground interactive process reference\r\n if (context.cliAdapter) {\r\n context.cliAdapter.setCurrentInteractiveProcess(undefined);\r\n }\r\n\r\n // Resolve the tool call with the transfer message\r\n resolve(buildTransferMessage(adopted.id, command, executionCwd, timeout_seconds, output));\r\n }, timeoutMs);\r\n }\r\n });\r\n } else if (currentContext.type === 'wsl') {\r\n // WSL execution with PTY for proper TTY handling (sudo, etc.)\r\n const remoteCwd = executionCwd;\r\n const distribution = currentContext.metadata?.distroName || 'Ubuntu';\r\n\r\n return new Promise<string>((resolve, reject) => {\r\n let output = '';\r\n let transferred = false;\r\n let timeoutTimer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n const constrainedCols = Math.max(40, (process.stdout.columns || 80) - 4);\r\n const constrainedRows = Math.min(process.stdout.rows || 24, 50);\r\n\r\n let onDataCallback = (data: string) => {\r\n output += data;\r\n if (context.onStreamingOutput) {\r\n context.onStreamingOutput(data, 'stdout');\r\n }\r\n };\r\n\r\n let onExitCallback = (exitCode: number) => {\r\n if (timeoutTimer) { clearTimeout(timeoutTimer); timeoutTimer = null; }\r\n if (transferred) return;\r\n if (context.cliAdapter) {\r\n context.cliAdapter.setCurrentInteractiveProcess(undefined);\r\n }\r\n if (exitCode !== 0) {\r\n reject(new Error(`${output || `Command failed with exit code ${exitCode}`}\\n\\nPossible reasons:\\n1. The command is invalid or not installed.\\n2. The arguments are incorrect.`));\r\n } else {\r\n resolve(output || 'Command executed successfully');\r\n }\r\n };\r\n\r\n const wslPty = runWSLCommand(\r\n distribution,\r\n command,\r\n remoteCwd,\r\n (data: string) => onDataCallback(data),\r\n (exitCode: number) => onExitCallback(exitCode),\r\n constrainedCols,\r\n constrainedRows\r\n );\r\n\r\n if (context.cliAdapter) {\r\n context.cliAdapter.setCurrentInteractiveProcess({\r\n process: null,\r\n write: (data: string) => wslPty.write(data),\r\n kill: () => wslPty.kill(),\r\n signal: (sig: NodeJS.Signals) => {\r\n if (sig === 'SIGINT') {\r\n wslPty.write('\\x03');\r\n }\r\n },\r\n resize: (cols: number, rows: number) => wslPty.resize(cols, rows),\r\n isPty: true\r\n });\r\n }\r\n\r\n if (timeoutMs > 0) {\r\n timeoutTimer = setTimeout(() => {\r\n if (transferred) return;\r\n transferred = true;\r\n args.timeoutTransferred = true; // Flag for UI to show yellow transfer state\r\n const adopted = BackgroundTaskManager.adoptRunningProcess(command, remoteCwd, output, `wsl:${distribution}`);\r\n adopted.setRemotePty({\r\n write: (data: string) => wslPty.write(data),\r\n kill: () => wslPty.kill(),\r\n resize: (cols: number, rows: number) => wslPty.resize(cols, rows),\r\n isRunning: () => wslPty.isRunning(),\r\n });\r\n onDataCallback = adopted.onData;\r\n onExitCallback = adopted.onExit;\r\n if (context.cliAdapter) context.cliAdapter.setCurrentInteractiveProcess(undefined);\r\n resolve(buildTransferMessage(adopted.id, command, remoteCwd, timeout_seconds, output, `wsl:${distribution}`));\r\n }, timeoutMs);\r\n }\r\n });\r\n } else if (currentContext.type === 'docker') {\r\n // Docker execution - check if nested inside SSH\r\n const parentContext = contextManager.getParentContext();\r\n\r\n const remoteCwd = executionCwd;\r\n const containerId = currentContext.metadata?.containerId || '';\r\n\r\n // Calculate constrained terminal dimensions for proper escape sequence handling\r\n const constrainedCols = Math.max(40, (process.stdout.columns || 80) - 4);\r\n const constrainedRows = Math.min(process.stdout.rows || 24, 50);\r\n\r\n if (parentContext && parentContext.type === 'ssh') {\r\n // Nested Docker inside SSH: route docker exec command through SSH\r\n const sshClient = parentContext.handler?.client;\r\n\r\n if (!sshClient) {\r\n throw new Error('SSH client not available for nested Docker session');\r\n }\r\n\r\n const escapedCommand = command.replace(/\"/g, '\\\\\"');\r\n const dockerCommand = `docker exec -w \"${remoteCwd}\" ${containerId} sh -c \"${escapedCommand}\"`;\r\n const remoteLabel = `docker:${containerId.substring(0, 12)} via ssh`;\r\n\r\n return new Promise<string>((resolve, reject) => {\r\n let output = '';\r\n let transferred = false;\r\n let timeoutTimer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n let onDataCallback = (data: string) => {\r\n output += data;\r\n if (context.onStreamingOutput) {\r\n context.onStreamingOutput(data, 'stdout');\r\n }\r\n };\r\n\r\n let onExitCallback = (exitCode: number) => {\r\n if (timeoutTimer) { clearTimeout(timeoutTimer); timeoutTimer = null; }\r\n if (transferred) return;\r\n if (context.cliAdapter) context.cliAdapter.setCurrentInteractiveProcess(undefined);\r\n if (exitCode !== 0) {\r\n reject(new Error(`${output || `Command failed with exit code ${exitCode}`}\\n\\nPossible reasons:\\n1. The command is invalid or not installed in the container.\\n2. The arguments are incorrect.\\n3. The Docker container may not be running.`));\r\n } else {\r\n resolve(output || 'Command executed successfully');\r\n }\r\n };\r\n\r\n const sshPty = runSSHCommand(\r\n sshClient,\r\n dockerCommand,\r\n parentContext.metadata.workingDirectory || '~',\r\n (data: string) => onDataCallback(data),\r\n (exitCode: number) => onExitCallback(exitCode),\r\n constrainedCols,\r\n constrainedRows\r\n );\r\n\r\n if (context.cliAdapter) {\r\n context.cliAdapter.setCurrentInteractiveProcess({\r\n process: null,\r\n write: (data: string) => sshPty.write(data),\r\n kill: () => sshPty.kill(),\r\n signal: (sig: NodeJS.Signals) => {\r\n if (sig === 'SIGINT') sshPty.write('\\x03');\r\n },\r\n resize: (cols: number, rows: number) => sshPty.resize(cols, rows),\r\n isPty: true\r\n });\r\n }\r\n\r\n if (timeoutMs > 0) {\r\n timeoutTimer = setTimeout(() => {\r\n if (transferred) return;\r\n transferred = true;\r\n args.timeoutTransferred = true; // Flag for UI to show yellow transfer state\r\n const adopted = BackgroundTaskManager.adoptRunningProcess(command, remoteCwd, output, remoteLabel);\r\n adopted.setRemotePty({\r\n write: (data: string) => sshPty.write(data),\r\n kill: () => sshPty.kill(),\r\n resize: (cols: number, rows: number) => sshPty.resize(cols, rows),\r\n isRunning: () => sshPty.isRunning(),\r\n });\r\n onDataCallback = adopted.onData;\r\n onExitCallback = adopted.onExit;\r\n if (context.cliAdapter) context.cliAdapter.setCurrentInteractiveProcess(undefined);\r\n resolve(buildTransferMessage(adopted.id, command, remoteCwd, timeout_seconds, output, remoteLabel));\r\n }, timeoutMs);\r\n }\r\n });\r\n } else {\r\n // Local Docker: use standard runDockerCommand\r\n const dockerLabel = `docker:${containerId.substring(0, 12)}`;\r\n\r\n return new Promise<string>((resolve, reject) => {\r\n let output = '';\r\n let transferred = false;\r\n let timeoutTimer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n let onDataCallback = (data: string) => {\r\n output += data;\r\n if (context.onStreamingOutput) {\r\n context.onStreamingOutput(data, 'stdout');\r\n }\r\n };\r\n\r\n let onExitCallback = (exitCode: number) => {\r\n if (timeoutTimer) { clearTimeout(timeoutTimer); timeoutTimer = null; }\r\n if (transferred) return;\r\n if (context.cliAdapter) context.cliAdapter.setCurrentInteractiveProcess(undefined);\r\n if (exitCode !== 0) {\r\n reject(new Error(`${output || `Command failed with exit code ${exitCode}`}\\n\\nPossible reasons:\\n1. The command is invalid or not installed.\\n2. The arguments are incorrect.`));\r\n } else {\r\n resolve(output || 'Command executed successfully');\r\n }\r\n };\r\n\r\n const dockerPty = runDockerCommand(\r\n containerId,\r\n command,\r\n remoteCwd,\r\n (data: string) => onDataCallback(data),\r\n (exitCode: number) => onExitCallback(exitCode),\r\n constrainedCols,\r\n constrainedRows\r\n );\r\n\r\n if (context.cliAdapter) {\r\n context.cliAdapter.setCurrentInteractiveProcess({\r\n process: null,\r\n write: (data: string) => dockerPty.write(data),\r\n kill: () => dockerPty.kill(),\r\n signal: (sig: NodeJS.Signals) => {\r\n if (sig === 'SIGINT') dockerPty.write('\\x03');\r\n },\r\n resize: (cols: number, rows: number) => dockerPty.resize(cols, rows),\r\n isPty: true\r\n });\r\n }\r\n\r\n if (timeoutMs > 0) {\r\n timeoutTimer = setTimeout(() => {\r\n if (transferred) return;\r\n transferred = true;\r\n args.timeoutTransferred = true; // Flag for UI to show yellow transfer state\r\n const adopted = BackgroundTaskManager.adoptRunningProcess(command, remoteCwd, output, dockerLabel);\r\n adopted.setRemotePty({\r\n write: (data: string) => dockerPty.write(data),\r\n kill: () => dockerPty.kill(),\r\n resize: (cols: number, rows: number) => dockerPty.resize(cols, rows),\r\n isRunning: () => dockerPty.isRunning(),\r\n });\r\n onDataCallback = adopted.onData;\r\n onExitCallback = adopted.onExit;\r\n if (context.cliAdapter) context.cliAdapter.setCurrentInteractiveProcess(undefined);\r\n resolve(buildTransferMessage(adopted.id, command, remoteCwd, timeout_seconds, output, dockerLabel));\r\n }, timeoutMs);\r\n }\r\n });\r\n }\r\n } else if (currentContext.type === 'ssh') {\r\n // SSH execution with PTY for proper TTY handling\r\n const remoteCwd = executionCwd;\r\n const sshClient = currentContext.handler?.client;\r\n\r\n if (!sshClient) {\r\n throw new Error('SSH client not available');\r\n }\r\n\r\n const sshLabel = `${currentContext.metadata?.username || 'user'}@${currentContext.metadata?.hostname || 'remote'}`;\r\n\r\n return new Promise<string>((resolve, reject) => {\r\n let output = '';\r\n let transferred = false;\r\n let timeoutTimer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n const constrainedCols = Math.max(40, (process.stdout.columns || 80) - 4);\r\n const constrainedRows = Math.min(process.stdout.rows || 24, 50);\r\n\r\n let onDataCallback = (data: string) => {\r\n output += data;\r\n if (context.onStreamingOutput) {\r\n context.onStreamingOutput(data, 'stdout');\r\n }\r\n };\r\n\r\n let onExitCallback = (exitCode: number) => {\r\n if (timeoutTimer) { clearTimeout(timeoutTimer); timeoutTimer = null; }\r\n if (transferred) return;\r\n if (context.cliAdapter) context.cliAdapter.setCurrentInteractiveProcess(undefined);\r\n if (exitCode !== 0) {\r\n reject(new Error(`${output || `Command failed with exit code ${exitCode}`}\\n\\nPossible reasons:\\n1. The command is invalid or not installed.\\n2. The arguments are incorrect.`));\r\n } else {\r\n resolve(output || 'Command executed successfully');\r\n }\r\n };\r\n\r\n const sshPty = runSSHCommand(\r\n sshClient,\r\n command,\r\n remoteCwd,\r\n (data: string) => onDataCallback(data),\r\n (exitCode: number) => onExitCallback(exitCode),\r\n constrainedCols,\r\n constrainedRows\r\n );\r\n\r\n if (context.cliAdapter) {\r\n context.cliAdapter.setCurrentInteractiveProcess({\r\n process: null,\r\n write: (data: string) => sshPty.write(data),\r\n kill: () => sshPty.kill(),\r\n signal: (sig: NodeJS.Signals) => {\r\n if (sig === 'SIGINT') sshPty.write('\\x03');\r\n },\r\n resize: (cols: number, rows: number) => sshPty.resize(cols, rows),\r\n isPty: true\r\n });\r\n }\r\n\r\n if (timeoutMs > 0) {\r\n timeoutTimer = setTimeout(() => {\r\n if (transferred) return;\r\n transferred = true;\r\n args.timeoutTransferred = true; // Flag for UI to show yellow transfer state\r\n const adopted = BackgroundTaskManager.adoptRunningProcess(command, remoteCwd, output, sshLabel);\r\n adopted.setRemotePty({\r\n write: (data: string) => sshPty.write(data),\r\n kill: () => sshPty.kill(),\r\n resize: (cols: number, rows: number) => sshPty.resize(cols, rows),\r\n isRunning: () => sshPty.isRunning(),\r\n });\r\n onDataCallback = adopted.onData;\r\n onExitCallback = adopted.onExit;\r\n if (context.cliAdapter) context.cliAdapter.setCurrentInteractiveProcess(undefined);\r\n resolve(buildTransferMessage(adopted.id, command, remoteCwd, timeout_seconds, output, sshLabel));\r\n }, timeoutMs);\r\n }\r\n });\r\n }\r\n },\r\n};\r\n"],"mappings":"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","names":[]}
@@ -41,7 +41,11 @@ Text file usage:
41
41
  - You can view at most 500 lines at a time to prevent context explosion.
42
42
  Binary file usage:
43
43
  - Do not provide StartLine or EndLine arguments, this tool always returns the entire file
44
- - The output includes line numbers (e.g., "1: code") for reference. DO NOT include these line numbers in your search_pattern when editing.`,
44
+ - The output includes line numbers (e.g., "1: code") for reference. DO NOT include these line numbers in your search_pattern when editing.
45
+ Environment parameter:
46
+ - By default, files are read from the current session (local machine, or SSH/Docker/WSL remote).
47
+ - Set Environment to "local" to FORCE reading from the local machine's filesystem, even when inside a remote session.
48
+ - This is useful for reading truncated tool output files saved to ~/.centaurus/tool-outputs/ which always exist locally.`,
45
49
  parameters: {
46
50
  type: "object",
47
51
  properties: {
@@ -60,19 +64,25 @@ Binary file usage:
60
64
  EndLine: {
61
65
  type: "integer",
62
66
  description: "Optional. Endline to view, 1-indexed as usual, inclusive. This value must be greater than or equal to StartLine."
67
+ },
68
+ Environment: {
69
+ type: "string",
70
+ enum: ["current", "local"],
71
+ description: 'Optional. Where to read the file from. "current" (default) reads from the active session (local or remote). "local" forces reading from the local machine filesystem, even when inside an SSH/Docker/WSL remote session. Use "local" when reading truncated tool output files saved to ~/.centaurus/tool-outputs/.'
63
72
  }
64
73
  },
65
74
  required: ["reason_text", "AbsolutePath"]
66
75
  }
67
76
  },
68
77
  async execute(args, context) {
69
- const { AbsolutePath, StartLine, EndLine } = args;
78
+ const { AbsolutePath, StartLine, EndLine, Environment: env } = args;
70
79
  const file_path = AbsolutePath;
71
80
  const contextManager = context.contextManager;
72
81
  const currentContext = contextManager.getCurrentContext();
82
+ const forceLocal = env === "local";
73
83
  let content;
74
84
  let fullPath;
75
- if (currentContext.type === "local") {
85
+ if (currentContext.type === "local" || forceLocal) {
76
86
  fullPath = path.isAbsolute(file_path) ? file_path : path.resolve(context.cwd, file_path);
77
87
  if (!fs.existsSync(fullPath)) {
78
88
  throw new Error(`File not found: ${file_path}
@@ -322,6 +332,8 @@ Action: Verify the file path using 'list_directory'.`);
322
332
  if (matches.length > 1) {
323
333
  throw new Error(`Pattern matches ${matches.length} times. Search pattern must be unique.`);
324
334
  }
335
+ const matchIndex = oldContent.search(regex);
336
+ const matchLineNumber = matchIndex >= 0 ? oldContent.substring(0, matchIndex).split("\n").length : 1;
325
337
  const newContent = oldContent.replace(regex, replacement);
326
338
  const patch = Diff.createTwoFilesPatch(
327
339
  file_path,
@@ -359,12 +371,12 @@ Action: Verify the file path using 'list_directory'.`);
359
371
  fs.writeFileSync(fullPath, newContent, "utf-8");
360
372
  const syntaxResult = await checkSyntax(fullPath, newContent);
361
373
  const syntaxFeedback = formatSyntaxCheckResult(syntaxResult);
362
- return `Successfully replaced pattern in ${file_path}
374
+ return `Successfully replaced pattern in ${file_path} (at line ${matchLineNumber})
363
375
  ${syntaxFeedback}`;
364
376
  } else {
365
377
  await backupBeforeChange(context, fullPath);
366
378
  await contextManager.writeFile(file_path, newContent);
367
- return `Successfully replaced pattern in ${file_path} (${currentContext.type} environment)
379
+ return `Successfully replaced pattern in ${file_path} (at line ${matchLineNumber}, ${currentContext.type} environment)
368
380
  (Syntax check skipped for remote files)`;
369
381
  }
370
382
  }
@@ -460,14 +472,19 @@ ${errors.join("\n")}
460
472
 
461
473
  No changes were made.`);
462
474
  }
475
+ const editLineNumbers = [];
463
476
  for (let i = 0; i < edits.length; i++) {
464
477
  const edit = edits[i];
465
478
  const regex = createFlexibleRegex(edit.search_pattern);
466
479
  const matches = content.match(regex);
467
480
  if (!matches || matches.length === 0) {
468
481
  errors.push(`Edit ${i + 1}: Pattern no longer found after previous edits`);
482
+ editLineNumbers.push(1);
469
483
  continue;
470
484
  }
485
+ const matchIdx = content.search(regex);
486
+ const lineNum = matchIdx >= 0 ? content.substring(0, matchIdx).split("\n").length : 1;
487
+ editLineNumbers.push(lineNum);
471
488
  content = content.replace(regex, edit.replacement);
472
489
  appliedEdits.push(edit.description || `Edit ${i + 1}`);
473
490
  }
@@ -507,7 +524,7 @@ No changes were made.`);
507
524
  fs.writeFileSync(fullPath, content, "utf-8");
508
525
  const syntaxResult = await checkSyntax(fullPath, content);
509
526
  const syntaxFeedback = formatSyntaxCheckResult(syntaxResult);
510
- return `Successfully applied ${appliedEdits.length} edits to ${file_path}:
527
+ return `Successfully applied ${appliedEdits.length} edits to ${file_path} (at lines ${editLineNumbers.join(", ")}):
511
528
  ${appliedEdits.map((e, i) => ` ${i + 1}. ${e}`).join("\n")}
512
529
  ${syntaxFeedback}`;
513
530
  } else {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/tools/file-ops.ts"],"sourcesContent":["import * as path from 'path';\r\nimport * as fs from 'fs';\r\nimport { Tool } from './types.js';\r\nimport * as fileUtils from '../utils/file.js';\r\nimport * as Diff from 'diff';\r\nimport { ContextManager } from '../context/context-manager.js';\r\nimport { ToolValidator } from './validation.js';\r\nimport { checkSyntax, formatSyntaxCheckResult } from '../utils/syntax-checker.js';\r\nimport { logWarning } from '../utils/logger.js';\r\nimport type { RemoteFileHandler } from '../services/checkpoint-manager.js';\r\n\r\n/**\r\n * Helper: back up a file before the AI modifies it (for checkpoint revert).\r\n * Non-critical — if backup fails, the file operation proceeds anyway.\r\n */\r\nasync function backupBeforeChange(\r\n context: { checkpointManager?: any; currentCheckpointId?: string; cwd: string; contextManager?: any },\r\n absoluteFilePath: string\r\n): Promise<void> {\r\n if (!context.checkpointManager || !context.currentCheckpointId) return;\r\n try {\r\n // Determine remote handler if in remote context\r\n let remoteHandler: RemoteFileHandler | undefined;\r\n if (context.contextManager) {\r\n const currentCtx = (context.contextManager as ContextManager).getCurrentContext();\r\n if (currentCtx.type !== 'local' && currentCtx.handler) {\r\n const h = currentCtx.handler;\r\n if (typeof h.readFile === 'function' && typeof h.writeFile === 'function' &&\r\n typeof h.executeCommand === 'function' && typeof h.isConnected === 'function') {\r\n remoteHandler = h as RemoteFileHandler;\r\n }\r\n }\r\n }\r\n await context.checkpointManager.backupFileBeforeChange(\r\n context.currentCheckpointId,\r\n absoluteFilePath,\r\n context.cwd,\r\n remoteHandler\r\n );\r\n } catch (err) {\r\n logWarning(`Checkpoint backup before change failed (non-critical): ${(err as Error).message}`);\r\n }\r\n}\r\n\r\nexport const viewFileTool: Tool = {\r\n schema: {\r\n name: 'view_file',\r\n description: `View the contents of a file from the local filesystem. This tool supports some binary files such as images and videos.\r\nText file usage:\r\n- The lines of the file are 1-indexed\r\n- If you do not provide StartLine/EndLine, the tool will read the ENTIRE file.\r\n - WARNING: If the file is >500 lines, output will be TRUNCATED to the first 500 lines.\r\n - You will be told the total line count and must use targeted reads (StartLine/EndLine) for the rest.\r\n- PREFER TARGETED READS: Use 'grep_search' to find line numbers, then read specific ranges.\r\n- You can view at most 500 lines at a time to prevent context explosion.\r\nBinary file usage:\r\n- Do not provide StartLine or EndLine arguments, this tool always returns the entire file\r\n- The output includes line numbers (e.g., \"1: code\") for reference. DO NOT include these line numbers in your search_pattern when editing.`,\r\n parameters: {\r\n type: 'object',\r\n properties: {\r\n reason_text: {\r\n type: 'string',\r\n description: 'REQUIRED: A brief explanation of why you are reading this file and what you are looking for. This will be shown to the user. Example: \"Reading package.json to check the current version number\"',\r\n },\r\n AbsolutePath: {\r\n type: 'string',\r\n description: 'Path to file to view. Must be an absolute path.',\r\n },\r\n StartLine: {\r\n type: 'integer',\r\n description: 'Optional. Startline to view, 1-indexed as usual, inclusive. This value must be less than or equal to EndLine.',\r\n },\r\n EndLine: {\r\n type: 'integer',\r\n description: 'Optional. Endline to view, 1-indexed as usual, inclusive. This value must be greater than or equal to StartLine.',\r\n },\r\n },\r\n required: ['reason_text', 'AbsolutePath'],\r\n },\r\n },\r\n async execute(args, context) {\r\n const { AbsolutePath, StartLine, EndLine } = args;\r\n const file_path = AbsolutePath; // Map to internal variable\r\n const contextManager = context.contextManager as ContextManager;\r\n const currentContext = contextManager.getCurrentContext();\r\n\r\n // Use Context Manager for file operations\r\n let content: string;\r\n let fullPath: string | undefined;\r\n\r\n if (currentContext.type === 'local') {\r\n // Since AbsolutePath is required to be absolute, we might need to handle relative paths gracefully or enforce absolute\r\n // The tool description says \"Must be an absolute path\", but for usability we can resolve it\r\n fullPath = path.isAbsolute(file_path) ? file_path : path.resolve(context.cwd, file_path);\r\n\r\n if (!fs.existsSync(fullPath)) {\r\n throw new Error(`File not found: ${file_path}\\n\\nAction: Verify the file path using 'list_directory'.`);\r\n }\r\n\r\n // If user specifically requested lines, respect that lookup\r\n // Otherwise read whole file first to check length\r\n if (StartLine !== undefined || EndLine !== undefined) {\r\n content = fileUtils.readFile(fullPath, StartLine, EndLine);\r\n } else {\r\n // Read full file to check length\r\n content = fs.readFileSync(fullPath, 'utf-8');\r\n }\r\n\r\n } else {\r\n // Read from subshell\r\n content = await contextManager.readFile(file_path);\r\n\r\n // Apply line range if logic requires it (for explicit range requests)\r\n if (StartLine !== undefined || EndLine !== undefined) {\r\n const lines = content.split('\\n');\r\n const start = StartLine ? StartLine - 1 : 0;\r\n const end = EndLine ? EndLine : lines.length;\r\n content = lines.slice(start, end).join('\\n');\r\n }\r\n }\r\n\r\n // Process content (add line numbers, handle truncation)\r\n const lines = content.split('\\n');\r\n let displayedLines = lines;\r\n let truncationMsg = '';\r\n const startNum = StartLine || 1;\r\n\r\n // TRUNCATION LOGIC:\r\n // Only apply if NO range was specified (viewing \"whole\" file) AND file is too long\r\n if (StartLine === undefined && EndLine === undefined && lines.length > 500) {\r\n displayedLines = lines.slice(0, 500);\r\n truncationMsg = `\\n\\n... [File truncated. Total lines: ${lines.length}. Showing first 500 lines only.]\\n[Action: Use StartLine/EndLine parameters to view specific sections of the rest of the file.]`;\r\n }\r\n\r\n const numberedContent = displayedLines.map((line, idx) => `${startNum + idx}: ${line}`).join('\\n');\r\n\r\n return `File: ${file_path}\\nLines: ${StartLine === undefined && EndLine === undefined ? lines.length : displayedLines.length} ${truncationMsg ? '(Truncated)' : ''}\\n\\n${numberedContent}${truncationMsg}`;\r\n },\r\n};\r\n\r\nexport const writeToFileTool: Tool = {\r\n schema: {\r\n name: 'write_to_file',\r\n description: `Use this tool to create new files. The file and any parent directories will be created for you if they do not already exist.\r\nFollow these instructions:\r\n1. By default this tool will error if TargetFile already exists. To overwrite an existing file, set Overwrite to true.\r\n2. You MUST specify TargetFile as the FIRST argument. Please specify the full TargetFile before any of the code contents.\r\nIMPORTANT: You must generate the following arguments first, before any others: [TargetFile, Overwrite]`,\r\n parameters: {\r\n type: 'object',\r\n properties: {\r\n reason_text: {\r\n type: 'string',\r\n description: 'REQUIRED: A brief explanation of why you are creating/writing this file. This will be shown to the user. Example: \"Creating a new configuration file for the database connection\"',\r\n },\r\n TargetFile: {\r\n type: 'string',\r\n description: 'The target file to create and write code to.',\r\n },\r\n CodeContent: {\r\n type: 'string',\r\n description: 'The code contents to write to the file.',\r\n },\r\n Overwrite: {\r\n type: 'boolean',\r\n description: 'Set this to true to overwrite an existing file. WARNING: This will replace the entire file contents. Only use when you explicitly intend to overwrite. Otherwise, use a code edit tool to modify existing files.',\r\n },\r\n EmptyFile: {\r\n type: 'boolean',\r\n description: 'Set this to true to create an empty file.',\r\n },\r\n Description: {\r\n type: 'string',\r\n description: 'Brief, user-facing explanation of what this change did. Focus on non-obvious rationale, design decisions, or important context. Don\\'t just restate what the code does.',\r\n },\r\n Complexity: {\r\n type: 'integer',\r\n description: 'A 1-10 rating of how important it is for the user to review this change. Rate based on: 1-3 (routine/obvious), 4-6 (worth noting), 7-10 (critical or subtle and warrants explanation).',\r\n },\r\n },\r\n required: ['reason_text', 'TargetFile', 'Overwrite', 'CodeContent', 'EmptyFile', 'Description', 'Complexity'],\r\n },\r\n },\r\n async execute(args, context) {\r\n const { TargetFile, CodeContent, Overwrite, EmptyFile } = args;\r\n const file_path = TargetFile; // Map to internal variable\r\n const content = EmptyFile ? '' : CodeContent;\r\n\r\n const contextManager = context.contextManager as ContextManager;\r\n const currentContext = contextManager.getCurrentContext();\r\n\r\n if (currentContext.type === 'local') {\r\n const fullPath = path.resolve(context.cwd, file_path);\r\n\r\n // Ask for approval if file exists\r\n if (fs.existsSync(fullPath)) {\r\n if (!Overwrite) {\r\n throw new Error(`File ${file_path} already exists. Set Overwrite to true if you intend to replace it.`);\r\n }\r\n\r\n // Show preview of new content for overwrite\r\n const language = file_path.split('.').pop() || 'text';\r\n const result = await context.requireApproval(\r\n `File ${fullPath} already exists. Overwrite it?`,\r\n true,\r\n {\r\n type: 'code',\r\n content,\r\n language\r\n },\r\n 'write_to_file',\r\n { file_path: fullPath, operation: 'overwrite' }\r\n );\r\n\r\n if (typeof result === 'object' && 'feedback' in result) {\r\n // User provided feedback - throw special error with feedback\r\n throw new Error(`USER_FEEDBACK: ${result.feedback}`);\r\n }\r\n\r\n if (!result) {\r\n throw new Error('Operation cancelled by user.');\r\n }\r\n } else {\r\n // Ask for approval to create new file with code preview\r\n const language = file_path.split('.').pop() || 'text';\r\n\r\n const result = await context.requireApproval(\r\n `Create new file ${fullPath}?`,\r\n false,\r\n {\r\n type: 'code',\r\n content: content,\r\n language\r\n },\r\n 'write_to_file',\r\n { file_path: fullPath, operation: 'create' }\r\n );\r\n\r\n if (typeof result === 'object' && 'feedback' in result) {\r\n // User provided feedback - throw special error with feedback\r\n throw new Error(`USER_FEEDBACK: ${result.feedback}`);\r\n }\r\n\r\n if (!result) {\r\n throw new Error('Operation cancelled by user.');\r\n }\r\n }\r\n\r\n // Backup file before modification (for checkpoint revert)\r\n await backupBeforeChange(context, fullPath);\r\n\r\n fileUtils.writeFile(fullPath, content);\r\n\r\n // Run syntax check on the written file\r\n const syntaxResult = await checkSyntax(fullPath, content);\r\n const syntaxFeedback = formatSyntaxCheckResult(syntaxResult);\r\n\r\n return `Successfully wrote ${content.length} characters to ${file_path}\\n${syntaxFeedback}`;\r\n } else {\r\n // Write to subshell - always ask for approval\r\n const language = file_path.split('.').pop() || 'text';\r\n const result = await context.requireApproval(\r\n `Write file ${file_path} in ${currentContext.type} environment?`,\r\n true,\r\n {\r\n type: 'code',\r\n content,\r\n language\r\n },\r\n 'write_to_file',\r\n { file_path, operation: 'write_remote' }\r\n );\r\n\r\n if (typeof result === 'object' && 'feedback' in result) {\r\n throw new Error(`USER_FEEDBACK: ${result.feedback}`);\r\n }\r\n\r\n if (!result) {\r\n throw new Error('Operation cancelled by user.');\r\n }\r\n\r\n // Backup remote file before modification (for checkpoint revert)\r\n await backupBeforeChange(context, file_path);\r\n\r\n await contextManager.writeFile(file_path, content);\r\n\r\n // Note: Syntax check skipped for remote files (would need to read back)\r\n return `Successfully wrote ${content.length} characters to ${file_path} in ${currentContext.type} environment\\n(Syntax check skipped for remote files)`;\r\n }\r\n },\r\n};\r\n\r\nexport const editFileTool: Tool = {\r\n schema: {\r\n name: 'edit_file',\r\n description: `Search for a specific pattern in a file and replace it with new content. Shows a diff preview before applying.\r\n\r\nIMPORTANT: You MUST provide a reason_text parameter explaining what change you are making and why. This will be shown to the user before the edit is applied.\r\n\r\nCRITICAL REQUIREMENTS:\r\n- 'search_pattern' must match the file content's meaningful tokens exactly.\r\n- Leading and trailing whitespace/indentation around the pattern will be ignored for the match.\r\n- You must still provide the full code block, but do not worry about exact indentation.\r\n- The pattern must be UNIQUE in the file (appear exactly once).\r\n- You MUST have read the file in a previous turn to know the exact content.\r\n- Include surrounding context (2-3 lines) to ensure uniqueness.\r\n\r\nCOMMON MISTAKES TO AVOID:\r\n- Using patterns that appear multiple times\r\n- Not including enough context for uniqueness\r\n- Modifying the internal tokens of the code (variable names, strings, etc.)\r\n- Including the line numbers (e.g., \"1: \") in the search_pattern. Copy ONLY the code.`,\r\n parameters: {\r\n type: 'object',\r\n properties: {\r\n reason_text: {\r\n type: 'string',\r\n description: 'REQUIRED: A brief explanation of what change you are making and why. This will be shown to the user. Example: \"Updating the port number from 3000 to 8080 to match the production configuration\"',\r\n },\r\n file_path: {\r\n type: 'string',\r\n description: 'The path to the file to edit (relative to current working directory)',\r\n },\r\n search_pattern: {\r\n type: 'string',\r\n description: `The EXACT block of text to replace. Must match character-for-character (ignoring whitespace differences).\r\nCopy this directly from a recent view_file output.`,\r\n },\r\n replacement: {\r\n type: 'string',\r\n description: 'The new text to insert in place of search_pattern. Use the same indentation style as the surrounding code.',\r\n },\r\n },\r\n required: ['reason_text', 'file_path', 'search_pattern', 'replacement'],\r\n },\r\n },\r\n async execute(args, context) {\r\n const { file_path, search_pattern, replacement } = args;\r\n const contextManager = context.contextManager as ContextManager;\r\n const currentContext = contextManager.getCurrentContext();\r\n\r\n // Validate edit operation for local files\r\n if (currentContext.type === 'local') {\r\n const validator = new ToolValidator();\r\n const validationResult = await validator.validateEditFile(\r\n file_path,\r\n search_pattern,\r\n replacement,\r\n context.cwd\r\n );\r\n\r\n if (!validationResult.valid) {\r\n const errorMessage = [\r\n validationResult.error,\r\n validationResult.suggestion ? `\\nSuggestion: ${validationResult.suggestion}` : '',\r\n ].join('');\r\n throw new Error(errorMessage);\r\n }\r\n }\r\n\r\n // Read the current content\r\n let oldContent: string;\r\n // Compute the full path for local files\r\n const fullPath = currentContext.type === 'local' ? path.resolve(context.cwd, file_path) : file_path;\r\n\r\n if (currentContext.type === 'local') {\r\n if (!fs.existsSync(fullPath)) {\r\n throw new Error(`File not found: ${file_path}\\n\\nAction: Verify the file path using 'list_directory'.`);\r\n }\r\n\r\n oldContent = fs.readFileSync(fullPath, 'utf-8');\r\n } else {\r\n oldContent = await contextManager.readFile(file_path);\r\n }\r\n\r\n // Create flexible regex for matching and replacement\r\n const { createFlexibleRegex } = await import('./validation.js');\r\n const regex = createFlexibleRegex(search_pattern);\r\n\r\n // SAFETY CHECK: Ensure we don't accidentally match 0 times or >1 times \r\n // (In case file changed between validation and execution)\r\n const matches = oldContent.match(regex);\r\n if (!matches || matches.length === 0) {\r\n throw new Error(`Pattern not found in file (even with flexible whitespace).`);\r\n }\r\n if (matches.length > 1) {\r\n throw new Error(`Pattern matches ${matches.length} times. Search pattern must be unique.`);\r\n }\r\n\r\n // APPLY REPLACEMENT\r\n // We use the regex to replace. \r\n // Since we validated count === 1, the global 'g' flag is safe/necessary.\r\n const newContent = oldContent.replace(regex, replacement);\r\n\r\n // Build unified diff text for preview\r\n const patch = Diff.createTwoFilesPatch(\r\n file_path,\r\n file_path,\r\n oldContent,\r\n newContent,\r\n '',\r\n ''\r\n );\r\n\r\n // Generate full diff with infinite context for \"Full File\" view\r\n const fullDiff = Diff.createTwoFilesPatch(\r\n file_path,\r\n file_path,\r\n oldContent,\r\n newContent,\r\n '',\r\n '',\r\n { context: Number.MAX_SAFE_INTEGER } as any\r\n );\r\n\r\n // Ask for approval with diff preview\r\n const approved = await context.requireApproval(\r\n `Apply these changes to ${fullPath}${currentContext.type !== 'local' ? ` in ${currentContext.type} environment` : ''}?`,\r\n false,\r\n {\r\n type: 'diff',\r\n content: patch,\r\n fullDiff: fullDiff\r\n },\r\n 'edit_file',\r\n { file_path: fullPath }\r\n );\r\n\r\n if (!approved) {\r\n throw new Error('Operation cancelled by user.');\r\n }\r\n\r\n // Apply the changes\r\n if (currentContext.type === 'local') {\r\n // Backup file before modification (for checkpoint revert)\r\n await backupBeforeChange(context, fullPath);\r\n\r\n fs.writeFileSync(fullPath, newContent, 'utf-8');\r\n\r\n // Run syntax check on the edited file\r\n const syntaxResult = await checkSyntax(fullPath, newContent);\r\n const syntaxFeedback = formatSyntaxCheckResult(syntaxResult);\r\n\r\n return `Successfully replaced pattern in ${file_path}\\n${syntaxFeedback}`;\r\n } else {\r\n // Backup remote file before modification (for checkpoint revert)\r\n await backupBeforeChange(context, fullPath);\r\n\r\n await contextManager.writeFile(file_path, newContent);\r\n\r\n // Note: Syntax check skipped for remote files\r\n return `Successfully replaced pattern in ${file_path} (${currentContext.type} environment)\\n(Syntax check skipped for remote files)`;\r\n }\r\n },\r\n};\r\n\r\nexport const multiEditFileTool: Tool = {\r\n schema: {\r\n name: 'multi_edit_file',\r\n description: `Apply MULTIPLE search-and-replace edits to a SINGLE file in ONE tool call.\r\n \r\nUse this instead of calling edit_file multiple times on the same file. This is more efficient and ensures all edits are applied atomically.\r\n\r\nWHEN TO USE:\r\n- You need to make 2+ edits to the same file\r\n- You want to batch changes to avoid multiple tool calls\r\n- The edits are independent (don't overlap)\r\n\r\nCRITICAL REQUIREMENTS:\r\n- Each edit's 'search_pattern' must be UNIQUE in the file\r\n- Edits are applied in order - earlier edits can affect line positions\r\n- All patterns must exist in the file or the entire operation fails\r\n- You MUST have read the file first to know exact content`,\r\n parameters: {\r\n type: 'object',\r\n properties: {\r\n reason_text: {\r\n type: 'string',\r\n description: 'REQUIRED: A brief explanation of what changes you are making and why.',\r\n },\r\n file_path: {\r\n type: 'string',\r\n description: 'The path to the file to edit',\r\n },\r\n edits: {\r\n type: 'array',\r\n description: 'Array of edit operations to apply, in order',\r\n items: {\r\n type: 'object',\r\n properties: {\r\n search_pattern: {\r\n type: 'string',\r\n description: 'The EXACT block of text to replace. Must be unique in the file.',\r\n },\r\n replacement: {\r\n type: 'string',\r\n description: 'The new text to insert in place of search_pattern.',\r\n },\r\n description: {\r\n type: 'string',\r\n description: 'Brief description of this specific edit',\r\n },\r\n },\r\n required: ['search_pattern', 'replacement'],\r\n },\r\n },\r\n },\r\n required: ['reason_text', 'file_path', 'edits'],\r\n },\r\n },\r\n async execute(args, context) {\r\n const { file_path, edits } = args;\r\n const contextManager = context.contextManager as ContextManager;\r\n const currentContext = contextManager.getCurrentContext();\r\n\r\n if (!edits || !Array.isArray(edits) || edits.length === 0) {\r\n throw new Error('No edits provided. The edits array must contain at least one edit operation.');\r\n }\r\n\r\n // Read the current content\r\n let content: string;\r\n // Compute the full path for local files\r\n const fullPath = currentContext.type === 'local' ? path.resolve(context.cwd, file_path) : file_path;\r\n\r\n if (currentContext.type === 'local') {\r\n if (!fs.existsSync(fullPath)) {\r\n throw new Error(`File not found: ${file_path}`);\r\n }\r\n content = fs.readFileSync(fullPath, 'utf-8');\r\n } else {\r\n content = await contextManager.readFile(file_path);\r\n }\r\n\r\n const originalContent = content;\r\n const { createFlexibleRegex } = await import('./validation.js');\r\n const appliedEdits: string[] = [];\r\n const errors: string[] = [];\r\n\r\n // Validate all patterns exist before applying any\r\n for (let i = 0; i < edits.length; i++) {\r\n const edit = edits[i];\r\n const regex = createFlexibleRegex(edit.search_pattern);\r\n const matches = content.match(regex);\r\n\r\n if (!matches || matches.length === 0) {\r\n errors.push(`Edit ${i + 1}: Pattern not found in file`);\r\n } else if (matches.length > 1) {\r\n errors.push(`Edit ${i + 1}: Pattern matches ${matches.length} times (must be unique)`);\r\n }\r\n }\r\n\r\n if (errors.length > 0) {\r\n throw new Error(`Validation failed:\\n${errors.join('\\n')}\\n\\nNo changes were made.`);\r\n }\r\n\r\n // Apply edits in order\r\n for (let i = 0; i < edits.length; i++) {\r\n const edit = edits[i];\r\n const regex = createFlexibleRegex(edit.search_pattern);\r\n\r\n // Re-check match after previous edits\r\n const matches = content.match(regex);\r\n if (!matches || matches.length === 0) {\r\n errors.push(`Edit ${i + 1}: Pattern no longer found after previous edits`);\r\n continue;\r\n }\r\n\r\n content = content.replace(regex, edit.replacement);\r\n appliedEdits.push(edit.description || `Edit ${i + 1}`);\r\n }\r\n\r\n // Generate combined diff\r\n const patch = Diff.createTwoFilesPatch(\r\n file_path,\r\n file_path,\r\n originalContent,\r\n content,\r\n '',\r\n ''\r\n );\r\n\r\n const fullDiff = Diff.createTwoFilesPatch(\r\n file_path,\r\n file_path,\r\n originalContent,\r\n content,\r\n '',\r\n '',\r\n { context: Number.MAX_SAFE_INTEGER } as any\r\n );\r\n\r\n // Ask for approval\r\n const approved = await context.requireApproval(\r\n `Apply ${edits.length} edits to ${fullPath}?`,\r\n false,\r\n {\r\n type: 'diff',\r\n content: patch,\r\n fullDiff: fullDiff\r\n },\r\n 'multi_edit_file',\r\n { file_path: fullPath, editCount: edits.length }\r\n );\r\n\r\n if (!approved) {\r\n throw new Error('Operation cancelled by user.');\r\n }\r\n\r\n // Write changes\r\n if (currentContext.type === 'local') {\r\n // Backup file before modification (for checkpoint revert)\r\n await backupBeforeChange(context, fullPath);\r\n\r\n fs.writeFileSync(fullPath, content, 'utf-8');\r\n\r\n const syntaxResult = await checkSyntax(fullPath, content);\r\n const syntaxFeedback = formatSyntaxCheckResult(syntaxResult);\r\n\r\n return `Successfully applied ${appliedEdits.length} edits to ${file_path}:\\n${appliedEdits.map((e, i) => ` ${i + 1}. ${e}`).join('\\n')}\\n${syntaxFeedback}`;\r\n } else {\r\n // Backup remote file before modification (for checkpoint revert)\r\n await backupBeforeChange(context, fullPath);\r\n\r\n await contextManager.writeFile(file_path, content);\r\n return `Successfully applied ${appliedEdits.length} edits to ${file_path} (${currentContext.type} environment)`;\r\n }\r\n },\r\n};\r\n\r\nexport const listDirTool: Tool = {\r\n schema: {\r\n name: 'list_dir',\r\n description: `List the contents of a directory with statistics and smart limiting.\r\n\r\nFor each directory child, output includes:\r\n- Directories: name and item count (sorted by size, largest first)\r\n- Files: name and size in KB (limited to 100 by default)\r\n\r\nSMART LIMITING:\r\n- Shows all directories (up to 50) - navigation is critical\r\n- Limits files to 100 - prevents context explosion\r\n- Always shows complete statistics (total dirs, files, items)\r\n- Highlights large directories (>100 items) for awareness\r\n\r\nIMPORTANT: You MUST provide a reason_text parameter explaining why you are listing this directory.`,\r\n parameters: {\r\n type: 'object',\r\n properties: {\r\n reason_text: {\r\n type: 'string',\r\n description: 'REQUIRED: A brief explanation of why you are listing this directory.',\r\n },\r\n DirectoryPath: {\r\n type: 'string',\r\n description: 'Path to list contents of, should be absolute path to a directory',\r\n },\r\n fileLimit: {\r\n type: 'integer',\r\n description: 'Optional: Maximum number of files to show (default: 100)',\r\n },\r\n showHidden: {\r\n type: 'boolean',\r\n description: 'Optional: Whether to show hidden files (dotfiles). Defaults to true.',\r\n },\r\n },\r\n required: ['reason_text', 'DirectoryPath'],\r\n },\r\n },\r\n async execute(args, context) {\r\n const { DirectoryPath, fileLimit = 100, showHidden = true } = args;\r\n const directory_path = DirectoryPath;\r\n const contextManager = context.contextManager as ContextManager;\r\n const currentContext = contextManager.getCurrentContext();\r\n\r\n if (currentContext.type === 'local') {\r\n const fullPath = path.isAbsolute(directory_path) ? directory_path : path.resolve(context.cwd, directory_path);\r\n\r\n if (!fs.existsSync(fullPath)) {\r\n throw new Error(`Directory not found: ${directory_path}\\n\\nAction: Check the path or use 'find_files' to locate the directory.`);\r\n }\r\n\r\n if (!fs.statSync(fullPath).isDirectory()) {\r\n throw new Error(`Path is not a directory: ${directory_path}\\n\\nAction: Verify the path points to a directory, not a file.`);\r\n }\r\n\r\n // Use enhanced listing\r\n const listing = fileUtils.listDirectoryEnhanced(fullPath, {\r\n fileLimit: fileLimit,\r\n dirLimit: 50,\r\n showHidden: showHidden,\r\n largeThreshold: 100\r\n });\r\n\r\n return fileUtils.formatDirectoryListing(directory_path, listing);\r\n } else {\r\n // For subshell contexts, use existing simple format\r\n // (enhanced format would require additional subshell handler changes)\r\n const entries = await contextManager.listDirectory(directory_path);\r\n\r\n const directories: string[] = [];\r\n const files: string[] = [];\r\n\r\n for (const entry of entries) {\r\n if (entry.type === 'directory') {\r\n directories.push(` ${entry.name}/`);\r\n } else {\r\n files.push(` ${entry.name}`);\r\n }\r\n }\r\n\r\n const lines: string[] = [\r\n `📁 Directory: ${directory_path} (${currentContext.type} environment)`,\r\n '═'.repeat(50),\r\n `Total: ${directories.length} directories, ${files.length} files, ${entries.length} items`,\r\n ''\r\n ];\r\n\r\n if (directories.length > 0) {\r\n lines.push('📂 Directories:');\r\n lines.push(...directories.slice(0, 50));\r\n if (directories.length > 50) {\r\n lines.push(` ... and ${directories.length - 50} more directories`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n if (files.length > 0) {\r\n const fileLimit = 100;\r\n const shownFiles = files.slice(0, fileLimit);\r\n const hiddenCount = Math.max(0, files.length - fileLimit);\r\n\r\n lines.push(hiddenCount > 0\r\n ? `📄 Files (${shownFiles.length} of ${files.length} shown):`\r\n : '📄 Files:');\r\n lines.push(...shownFiles);\r\n if (hiddenCount > 0) {\r\n lines.push(` ... and ${hiddenCount} more files`);\r\n lines.push('');\r\n lines.push('💡 To see more: List subdirectories individually or use find_files to search');\r\n }\r\n }\r\n\r\n return lines.join('\\n');\r\n }\r\n },\r\n};\r\n\r\n"],"mappings":"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","names":["lines","fileLimit"]}
1
+ {"version":3,"sources":["../../src/tools/file-ops.ts"],"sourcesContent":["import * as path from 'path';\r\nimport * as fs from 'fs';\r\nimport { Tool } from './types.js';\r\nimport * as fileUtils from '../utils/file.js';\r\nimport * as Diff from 'diff';\r\nimport { ContextManager } from '../context/context-manager.js';\r\nimport { ToolValidator } from './validation.js';\r\nimport { checkSyntax, formatSyntaxCheckResult } from '../utils/syntax-checker.js';\r\nimport { logWarning } from '../utils/logger.js';\r\nimport type { RemoteFileHandler } from '../services/checkpoint-manager.js';\r\n\r\n/**\r\n * Helper: back up a file before the AI modifies it (for checkpoint revert).\r\n * Non-critical — if backup fails, the file operation proceeds anyway.\r\n */\r\nasync function backupBeforeChange(\r\n context: { checkpointManager?: any; currentCheckpointId?: string; cwd: string; contextManager?: any },\r\n absoluteFilePath: string\r\n): Promise<void> {\r\n if (!context.checkpointManager || !context.currentCheckpointId) return;\r\n try {\r\n // Determine remote handler if in remote context\r\n let remoteHandler: RemoteFileHandler | undefined;\r\n if (context.contextManager) {\r\n const currentCtx = (context.contextManager as ContextManager).getCurrentContext();\r\n if (currentCtx.type !== 'local' && currentCtx.handler) {\r\n const h = currentCtx.handler;\r\n if (typeof h.readFile === 'function' && typeof h.writeFile === 'function' &&\r\n typeof h.executeCommand === 'function' && typeof h.isConnected === 'function') {\r\n remoteHandler = h as RemoteFileHandler;\r\n }\r\n }\r\n }\r\n await context.checkpointManager.backupFileBeforeChange(\r\n context.currentCheckpointId,\r\n absoluteFilePath,\r\n context.cwd,\r\n remoteHandler\r\n );\r\n } catch (err) {\r\n logWarning(`Checkpoint backup before change failed (non-critical): ${(err as Error).message}`);\r\n }\r\n}\r\n\r\nexport const viewFileTool: Tool = {\r\n schema: {\r\n name: 'view_file',\r\n description: `View the contents of a file from the local filesystem. This tool supports some binary files such as images and videos.\r\nText file usage:\r\n- The lines of the file are 1-indexed\r\n- If you do not provide StartLine/EndLine, the tool will read the ENTIRE file.\r\n - WARNING: If the file is >500 lines, output will be TRUNCATED to the first 500 lines.\r\n - You will be told the total line count and must use targeted reads (StartLine/EndLine) for the rest.\r\n- PREFER TARGETED READS: Use 'grep_search' to find line numbers, then read specific ranges.\r\n- You can view at most 500 lines at a time to prevent context explosion.\r\nBinary file usage:\r\n- Do not provide StartLine or EndLine arguments, this tool always returns the entire file\r\n- The output includes line numbers (e.g., \"1: code\") for reference. DO NOT include these line numbers in your search_pattern when editing.\r\nEnvironment parameter:\r\n- By default, files are read from the current session (local machine, or SSH/Docker/WSL remote).\r\n- Set Environment to \"local\" to FORCE reading from the local machine's filesystem, even when inside a remote session.\r\n- This is useful for reading truncated tool output files saved to ~/.centaurus/tool-outputs/ which always exist locally.`,\r\n parameters: {\r\n type: 'object',\r\n properties: {\r\n reason_text: {\r\n type: 'string',\r\n description: 'REQUIRED: A brief explanation of why you are reading this file and what you are looking for. This will be shown to the user. Example: \"Reading package.json to check the current version number\"',\r\n },\r\n AbsolutePath: {\r\n type: 'string',\r\n description: 'Path to file to view. Must be an absolute path.',\r\n },\r\n StartLine: {\r\n type: 'integer',\r\n description: 'Optional. Startline to view, 1-indexed as usual, inclusive. This value must be less than or equal to EndLine.',\r\n },\r\n EndLine: {\r\n type: 'integer',\r\n description: 'Optional. Endline to view, 1-indexed as usual, inclusive. This value must be greater than or equal to StartLine.',\r\n },\r\n Environment: {\r\n type: 'string',\r\n enum: ['current', 'local'],\r\n description: 'Optional. Where to read the file from. \"current\" (default) reads from the active session (local or remote). \"local\" forces reading from the local machine filesystem, even when inside an SSH/Docker/WSL remote session. Use \"local\" when reading truncated tool output files saved to ~/.centaurus/tool-outputs/.',\r\n },\r\n },\r\n required: ['reason_text', 'AbsolutePath'],\r\n },\r\n },\r\n async execute(args, context) {\r\n const { AbsolutePath, StartLine, EndLine, Environment: env } = args;\r\n const file_path = AbsolutePath; // Map to internal variable\r\n const contextManager = context.contextManager as ContextManager;\r\n const currentContext = contextManager.getCurrentContext();\r\n\r\n // Determine if we should force local reads.\r\n // Environment='local' bypasses remote context and reads from the local filesystem.\r\n const forceLocal = env === 'local';\r\n\r\n // Use Context Manager for file operations\r\n let content: string;\r\n let fullPath: string | undefined;\r\n\r\n if (currentContext.type === 'local' || forceLocal) {\r\n // Local filesystem read (either native local context or forced via Environment='local')\r\n // Since AbsolutePath is required to be absolute, we might need to handle relative paths gracefully or enforce absolute\r\n // The tool description says \"Must be an absolute path\", but for usability we can resolve it\r\n fullPath = path.isAbsolute(file_path) ? file_path : path.resolve(context.cwd, file_path);\r\n\r\n if (!fs.existsSync(fullPath)) {\r\n throw new Error(`File not found: ${file_path}\\n\\nAction: Verify the file path using 'list_directory'.`);\r\n }\r\n\r\n // If user specifically requested lines, respect that lookup\r\n // Otherwise read whole file first to check length\r\n if (StartLine !== undefined || EndLine !== undefined) {\r\n content = fileUtils.readFile(fullPath, StartLine, EndLine);\r\n } else {\r\n // Read full file to check length\r\n content = fs.readFileSync(fullPath, 'utf-8');\r\n }\r\n\r\n } else {\r\n // Read from subshell\r\n content = await contextManager.readFile(file_path);\r\n\r\n // Apply line range if logic requires it (for explicit range requests)\r\n if (StartLine !== undefined || EndLine !== undefined) {\r\n const lines = content.split('\\n');\r\n const start = StartLine ? StartLine - 1 : 0;\r\n const end = EndLine ? EndLine : lines.length;\r\n content = lines.slice(start, end).join('\\n');\r\n }\r\n }\r\n\r\n // Process content (add line numbers, handle truncation)\r\n const lines = content.split('\\n');\r\n let displayedLines = lines;\r\n let truncationMsg = '';\r\n const startNum = StartLine || 1;\r\n\r\n // TRUNCATION LOGIC:\r\n // Only apply if NO range was specified (viewing \"whole\" file) AND file is too long\r\n if (StartLine === undefined && EndLine === undefined && lines.length > 500) {\r\n displayedLines = lines.slice(0, 500);\r\n truncationMsg = `\\n\\n... [File truncated. Total lines: ${lines.length}. Showing first 500 lines only.]\\n[Action: Use StartLine/EndLine parameters to view specific sections of the rest of the file.]`;\r\n }\r\n\r\n const numberedContent = displayedLines.map((line, idx) => `${startNum + idx}: ${line}`).join('\\n');\r\n\r\n return `File: ${file_path}\\nLines: ${StartLine === undefined && EndLine === undefined ? lines.length : displayedLines.length} ${truncationMsg ? '(Truncated)' : ''}\\n\\n${numberedContent}${truncationMsg}`;\r\n },\r\n};\r\n\r\nexport const writeToFileTool: Tool = {\r\n schema: {\r\n name: 'write_to_file',\r\n description: `Use this tool to create new files. The file and any parent directories will be created for you if they do not already exist.\r\nFollow these instructions:\r\n1. By default this tool will error if TargetFile already exists. To overwrite an existing file, set Overwrite to true.\r\n2. You MUST specify TargetFile as the FIRST argument. Please specify the full TargetFile before any of the code contents.\r\nIMPORTANT: You must generate the following arguments first, before any others: [TargetFile, Overwrite]`,\r\n parameters: {\r\n type: 'object',\r\n properties: {\r\n reason_text: {\r\n type: 'string',\r\n description: 'REQUIRED: A brief explanation of why you are creating/writing this file. This will be shown to the user. Example: \"Creating a new configuration file for the database connection\"',\r\n },\r\n TargetFile: {\r\n type: 'string',\r\n description: 'The target file to create and write code to.',\r\n },\r\n CodeContent: {\r\n type: 'string',\r\n description: 'The code contents to write to the file.',\r\n },\r\n Overwrite: {\r\n type: 'boolean',\r\n description: 'Set this to true to overwrite an existing file. WARNING: This will replace the entire file contents. Only use when you explicitly intend to overwrite. Otherwise, use a code edit tool to modify existing files.',\r\n },\r\n EmptyFile: {\r\n type: 'boolean',\r\n description: 'Set this to true to create an empty file.',\r\n },\r\n Description: {\r\n type: 'string',\r\n description: 'Brief, user-facing explanation of what this change did. Focus on non-obvious rationale, design decisions, or important context. Don\\'t just restate what the code does.',\r\n },\r\n Complexity: {\r\n type: 'integer',\r\n description: 'A 1-10 rating of how important it is for the user to review this change. Rate based on: 1-3 (routine/obvious), 4-6 (worth noting), 7-10 (critical or subtle and warrants explanation).',\r\n },\r\n },\r\n required: ['reason_text', 'TargetFile', 'Overwrite', 'CodeContent', 'EmptyFile', 'Description', 'Complexity'],\r\n },\r\n },\r\n async execute(args, context) {\r\n const { TargetFile, CodeContent, Overwrite, EmptyFile } = args;\r\n const file_path = TargetFile; // Map to internal variable\r\n const content = EmptyFile ? '' : CodeContent;\r\n\r\n const contextManager = context.contextManager as ContextManager;\r\n const currentContext = contextManager.getCurrentContext();\r\n\r\n if (currentContext.type === 'local') {\r\n const fullPath = path.resolve(context.cwd, file_path);\r\n\r\n // Ask for approval if file exists\r\n if (fs.existsSync(fullPath)) {\r\n if (!Overwrite) {\r\n throw new Error(`File ${file_path} already exists. Set Overwrite to true if you intend to replace it.`);\r\n }\r\n\r\n // Show preview of new content for overwrite\r\n const language = file_path.split('.').pop() || 'text';\r\n const result = await context.requireApproval(\r\n `File ${fullPath} already exists. Overwrite it?`,\r\n true,\r\n {\r\n type: 'code',\r\n content,\r\n language\r\n },\r\n 'write_to_file',\r\n { file_path: fullPath, operation: 'overwrite' }\r\n );\r\n\r\n if (typeof result === 'object' && 'feedback' in result) {\r\n // User provided feedback - throw special error with feedback\r\n throw new Error(`USER_FEEDBACK: ${result.feedback}`);\r\n }\r\n\r\n if (!result) {\r\n throw new Error('Operation cancelled by user.');\r\n }\r\n } else {\r\n // Ask for approval to create new file with code preview\r\n const language = file_path.split('.').pop() || 'text';\r\n\r\n const result = await context.requireApproval(\r\n `Create new file ${fullPath}?`,\r\n false,\r\n {\r\n type: 'code',\r\n content: content,\r\n language\r\n },\r\n 'write_to_file',\r\n { file_path: fullPath, operation: 'create' }\r\n );\r\n\r\n if (typeof result === 'object' && 'feedback' in result) {\r\n // User provided feedback - throw special error with feedback\r\n throw new Error(`USER_FEEDBACK: ${result.feedback}`);\r\n }\r\n\r\n if (!result) {\r\n throw new Error('Operation cancelled by user.');\r\n }\r\n }\r\n\r\n // Backup file before modification (for checkpoint revert)\r\n await backupBeforeChange(context, fullPath);\r\n\r\n fileUtils.writeFile(fullPath, content);\r\n\r\n // Run syntax check on the written file\r\n const syntaxResult = await checkSyntax(fullPath, content);\r\n const syntaxFeedback = formatSyntaxCheckResult(syntaxResult);\r\n\r\n return `Successfully wrote ${content.length} characters to ${file_path}\\n${syntaxFeedback}`;\r\n } else {\r\n // Write to subshell - always ask for approval\r\n const language = file_path.split('.').pop() || 'text';\r\n const result = await context.requireApproval(\r\n `Write file ${file_path} in ${currentContext.type} environment?`,\r\n true,\r\n {\r\n type: 'code',\r\n content,\r\n language\r\n },\r\n 'write_to_file',\r\n { file_path, operation: 'write_remote' }\r\n );\r\n\r\n if (typeof result === 'object' && 'feedback' in result) {\r\n throw new Error(`USER_FEEDBACK: ${result.feedback}`);\r\n }\r\n\r\n if (!result) {\r\n throw new Error('Operation cancelled by user.');\r\n }\r\n\r\n // Backup remote file before modification (for checkpoint revert)\r\n await backupBeforeChange(context, file_path);\r\n\r\n await contextManager.writeFile(file_path, content);\r\n\r\n // Note: Syntax check skipped for remote files (would need to read back)\r\n return `Successfully wrote ${content.length} characters to ${file_path} in ${currentContext.type} environment\\n(Syntax check skipped for remote files)`;\r\n }\r\n },\r\n};\r\n\r\nexport const editFileTool: Tool = {\r\n schema: {\r\n name: 'edit_file',\r\n description: `Search for a specific pattern in a file and replace it with new content. Shows a diff preview before applying.\r\n\r\nIMPORTANT: You MUST provide a reason_text parameter explaining what change you are making and why. This will be shown to the user before the edit is applied.\r\n\r\nCRITICAL REQUIREMENTS:\r\n- 'search_pattern' must match the file content's meaningful tokens exactly.\r\n- Leading and trailing whitespace/indentation around the pattern will be ignored for the match.\r\n- You must still provide the full code block, but do not worry about exact indentation.\r\n- The pattern must be UNIQUE in the file (appear exactly once).\r\n- You MUST have read the file in a previous turn to know the exact content.\r\n- Include surrounding context (2-3 lines) to ensure uniqueness.\r\n\r\nCOMMON MISTAKES TO AVOID:\r\n- Using patterns that appear multiple times\r\n- Not including enough context for uniqueness\r\n- Modifying the internal tokens of the code (variable names, strings, etc.)\r\n- Including the line numbers (e.g., \"1: \") in the search_pattern. Copy ONLY the code.`,\r\n parameters: {\r\n type: 'object',\r\n properties: {\r\n reason_text: {\r\n type: 'string',\r\n description: 'REQUIRED: A brief explanation of what change you are making and why. This will be shown to the user. Example: \"Updating the port number from 3000 to 8080 to match the production configuration\"',\r\n },\r\n file_path: {\r\n type: 'string',\r\n description: 'The path to the file to edit (relative to current working directory)',\r\n },\r\n search_pattern: {\r\n type: 'string',\r\n description: `The EXACT block of text to replace. Must match character-for-character (ignoring whitespace differences).\r\nCopy this directly from a recent view_file output.`,\r\n },\r\n replacement: {\r\n type: 'string',\r\n description: 'The new text to insert in place of search_pattern. Use the same indentation style as the surrounding code.',\r\n },\r\n },\r\n required: ['reason_text', 'file_path', 'search_pattern', 'replacement'],\r\n },\r\n },\r\n async execute(args, context) {\r\n const { file_path, search_pattern, replacement } = args;\r\n const contextManager = context.contextManager as ContextManager;\r\n const currentContext = contextManager.getCurrentContext();\r\n\r\n // Validate edit operation for local files\r\n if (currentContext.type === 'local') {\r\n const validator = new ToolValidator();\r\n const validationResult = await validator.validateEditFile(\r\n file_path,\r\n search_pattern,\r\n replacement,\r\n context.cwd\r\n );\r\n\r\n if (!validationResult.valid) {\r\n const errorMessage = [\r\n validationResult.error,\r\n validationResult.suggestion ? `\\nSuggestion: ${validationResult.suggestion}` : '',\r\n ].join('');\r\n throw new Error(errorMessage);\r\n }\r\n }\r\n\r\n // Read the current content\r\n let oldContent: string;\r\n // Compute the full path for local files\r\n const fullPath = currentContext.type === 'local' ? path.resolve(context.cwd, file_path) : file_path;\r\n\r\n if (currentContext.type === 'local') {\r\n if (!fs.existsSync(fullPath)) {\r\n throw new Error(`File not found: ${file_path}\\n\\nAction: Verify the file path using 'list_directory'.`);\r\n }\r\n\r\n oldContent = fs.readFileSync(fullPath, 'utf-8');\r\n } else {\r\n oldContent = await contextManager.readFile(file_path);\r\n }\r\n\r\n // Create flexible regex for matching and replacement\r\n const { createFlexibleRegex } = await import('./validation.js');\r\n const regex = createFlexibleRegex(search_pattern);\r\n\r\n // SAFETY CHECK: Ensure we don't accidentally match 0 times or >1 times \r\n // (In case file changed between validation and execution)\r\n const matches = oldContent.match(regex);\r\n if (!matches || matches.length === 0) {\r\n throw new Error(`Pattern not found in file (even with flexible whitespace).`);\r\n }\r\n if (matches.length > 1) {\r\n throw new Error(`Pattern matches ${matches.length} times. Search pattern must be unique.`);\r\n }\r\n\r\n // Find the line number where the match starts (for UI display)\r\n const matchIndex = oldContent.search(regex);\r\n const matchLineNumber = matchIndex >= 0 ? oldContent.substring(0, matchIndex).split('\\n').length : 1;\r\n\r\n // APPLY REPLACEMENT\r\n // We use the regex to replace. \r\n // Since we validated count === 1, the global 'g' flag is safe/necessary.\r\n const newContent = oldContent.replace(regex, replacement);\r\n\r\n // Build unified diff text for preview\r\n const patch = Diff.createTwoFilesPatch(\r\n file_path,\r\n file_path,\r\n oldContent,\r\n newContent,\r\n '',\r\n ''\r\n );\r\n\r\n // Generate full diff with infinite context for \"Full File\" view\r\n const fullDiff = Diff.createTwoFilesPatch(\r\n file_path,\r\n file_path,\r\n oldContent,\r\n newContent,\r\n '',\r\n '',\r\n { context: Number.MAX_SAFE_INTEGER } as any\r\n );\r\n\r\n // Ask for approval with diff preview\r\n const approved = await context.requireApproval(\r\n `Apply these changes to ${fullPath}${currentContext.type !== 'local' ? ` in ${currentContext.type} environment` : ''}?`,\r\n false,\r\n {\r\n type: 'diff',\r\n content: patch,\r\n fullDiff: fullDiff\r\n },\r\n 'edit_file',\r\n { file_path: fullPath }\r\n );\r\n\r\n if (!approved) {\r\n throw new Error('Operation cancelled by user.');\r\n }\r\n\r\n // Apply the changes\r\n if (currentContext.type === 'local') {\r\n // Backup file before modification (for checkpoint revert)\r\n await backupBeforeChange(context, fullPath);\r\n\r\n fs.writeFileSync(fullPath, newContent, 'utf-8');\r\n\r\n // Run syntax check on the edited file\r\n const syntaxResult = await checkSyntax(fullPath, newContent);\r\n const syntaxFeedback = formatSyntaxCheckResult(syntaxResult);\r\n\r\n return `Successfully replaced pattern in ${file_path} (at line ${matchLineNumber})\\n${syntaxFeedback}`;\r\n } else {\r\n // Backup remote file before modification (for checkpoint revert)\r\n await backupBeforeChange(context, fullPath);\r\n\r\n await contextManager.writeFile(file_path, newContent);\r\n\r\n // Note: Syntax check skipped for remote files\r\n return `Successfully replaced pattern in ${file_path} (at line ${matchLineNumber}, ${currentContext.type} environment)\\n(Syntax check skipped for remote files)`;\r\n }\r\n },\r\n};\r\n\r\nexport const multiEditFileTool: Tool = {\r\n schema: {\r\n name: 'multi_edit_file',\r\n description: `Apply MULTIPLE search-and-replace edits to a SINGLE file in ONE tool call.\r\n \r\nUse this instead of calling edit_file multiple times on the same file. This is more efficient and ensures all edits are applied atomically.\r\n\r\nWHEN TO USE:\r\n- You need to make 2+ edits to the same file\r\n- You want to batch changes to avoid multiple tool calls\r\n- The edits are independent (don't overlap)\r\n\r\nCRITICAL REQUIREMENTS:\r\n- Each edit's 'search_pattern' must be UNIQUE in the file\r\n- Edits are applied in order - earlier edits can affect line positions\r\n- All patterns must exist in the file or the entire operation fails\r\n- You MUST have read the file first to know exact content`,\r\n parameters: {\r\n type: 'object',\r\n properties: {\r\n reason_text: {\r\n type: 'string',\r\n description: 'REQUIRED: A brief explanation of what changes you are making and why.',\r\n },\r\n file_path: {\r\n type: 'string',\r\n description: 'The path to the file to edit',\r\n },\r\n edits: {\r\n type: 'array',\r\n description: 'Array of edit operations to apply, in order',\r\n items: {\r\n type: 'object',\r\n properties: {\r\n search_pattern: {\r\n type: 'string',\r\n description: 'The EXACT block of text to replace. Must be unique in the file.',\r\n },\r\n replacement: {\r\n type: 'string',\r\n description: 'The new text to insert in place of search_pattern.',\r\n },\r\n description: {\r\n type: 'string',\r\n description: 'Brief description of this specific edit',\r\n },\r\n },\r\n required: ['search_pattern', 'replacement'],\r\n },\r\n },\r\n },\r\n required: ['reason_text', 'file_path', 'edits'],\r\n },\r\n },\r\n async execute(args, context) {\r\n const { file_path, edits } = args;\r\n const contextManager = context.contextManager as ContextManager;\r\n const currentContext = contextManager.getCurrentContext();\r\n\r\n if (!edits || !Array.isArray(edits) || edits.length === 0) {\r\n throw new Error('No edits provided. The edits array must contain at least one edit operation.');\r\n }\r\n\r\n // Read the current content\r\n let content: string;\r\n // Compute the full path for local files\r\n const fullPath = currentContext.type === 'local' ? path.resolve(context.cwd, file_path) : file_path;\r\n\r\n if (currentContext.type === 'local') {\r\n if (!fs.existsSync(fullPath)) {\r\n throw new Error(`File not found: ${file_path}`);\r\n }\r\n content = fs.readFileSync(fullPath, 'utf-8');\r\n } else {\r\n content = await contextManager.readFile(file_path);\r\n }\r\n\r\n const originalContent = content;\r\n const { createFlexibleRegex } = await import('./validation.js');\r\n const appliedEdits: string[] = [];\r\n const errors: string[] = [];\r\n\r\n // Validate all patterns exist before applying any\r\n for (let i = 0; i < edits.length; i++) {\r\n const edit = edits[i];\r\n const regex = createFlexibleRegex(edit.search_pattern);\r\n const matches = content.match(regex);\r\n\r\n if (!matches || matches.length === 0) {\r\n errors.push(`Edit ${i + 1}: Pattern not found in file`);\r\n } else if (matches.length > 1) {\r\n errors.push(`Edit ${i + 1}: Pattern matches ${matches.length} times (must be unique)`);\r\n }\r\n }\r\n\r\n if (errors.length > 0) {\r\n throw new Error(`Validation failed:\\n${errors.join('\\n')}\\n\\nNo changes were made.`);\r\n }\r\n\r\n // Apply edits in order, tracking match line numbers for each\r\n const editLineNumbers: number[] = [];\r\n for (let i = 0; i < edits.length; i++) {\r\n const edit = edits[i];\r\n const regex = createFlexibleRegex(edit.search_pattern);\r\n\r\n // Re-check match after previous edits\r\n const matches = content.match(regex);\r\n if (!matches || matches.length === 0) {\r\n errors.push(`Edit ${i + 1}: Pattern no longer found after previous edits`);\r\n editLineNumbers.push(1);\r\n continue;\r\n }\r\n\r\n // Find the line number where this edit's match starts\r\n const matchIdx = content.search(regex);\r\n const lineNum = matchIdx >= 0 ? content.substring(0, matchIdx).split('\\n').length : 1;\r\n editLineNumbers.push(lineNum);\r\n\r\n content = content.replace(regex, edit.replacement);\r\n appliedEdits.push(edit.description || `Edit ${i + 1}`);\r\n }\r\n\r\n // Generate combined diff\r\n const patch = Diff.createTwoFilesPatch(\r\n file_path,\r\n file_path,\r\n originalContent,\r\n content,\r\n '',\r\n ''\r\n );\r\n\r\n const fullDiff = Diff.createTwoFilesPatch(\r\n file_path,\r\n file_path,\r\n originalContent,\r\n content,\r\n '',\r\n '',\r\n { context: Number.MAX_SAFE_INTEGER } as any\r\n );\r\n\r\n // Ask for approval\r\n const approved = await context.requireApproval(\r\n `Apply ${edits.length} edits to ${fullPath}?`,\r\n false,\r\n {\r\n type: 'diff',\r\n content: patch,\r\n fullDiff: fullDiff\r\n },\r\n 'multi_edit_file',\r\n { file_path: fullPath, editCount: edits.length }\r\n );\r\n\r\n if (!approved) {\r\n throw new Error('Operation cancelled by user.');\r\n }\r\n\r\n // Write changes\r\n if (currentContext.type === 'local') {\r\n // Backup file before modification (for checkpoint revert)\r\n await backupBeforeChange(context, fullPath);\r\n\r\n fs.writeFileSync(fullPath, content, 'utf-8');\r\n\r\n const syntaxResult = await checkSyntax(fullPath, content);\r\n const syntaxFeedback = formatSyntaxCheckResult(syntaxResult);\r\n\r\n return `Successfully applied ${appliedEdits.length} edits to ${file_path} (at lines ${editLineNumbers.join(', ')}):\\n${appliedEdits.map((e, i) => ` ${i + 1}. ${e}`).join('\\n')}\\n${syntaxFeedback}`;\r\n } else {\r\n // Backup remote file before modification (for checkpoint revert)\r\n await backupBeforeChange(context, fullPath);\r\n\r\n await contextManager.writeFile(file_path, content);\r\n return `Successfully applied ${appliedEdits.length} edits to ${file_path} (${currentContext.type} environment)`;\r\n }\r\n },\r\n};\r\n\r\nexport const listDirTool: Tool = {\r\n schema: {\r\n name: 'list_dir',\r\n description: `List the contents of a directory with statistics and smart limiting.\r\n\r\nFor each directory child, output includes:\r\n- Directories: name and item count (sorted by size, largest first)\r\n- Files: name and size in KB (limited to 100 by default)\r\n\r\nSMART LIMITING:\r\n- Shows all directories (up to 50) - navigation is critical\r\n- Limits files to 100 - prevents context explosion\r\n- Always shows complete statistics (total dirs, files, items)\r\n- Highlights large directories (>100 items) for awareness\r\n\r\nIMPORTANT: You MUST provide a reason_text parameter explaining why you are listing this directory.`,\r\n parameters: {\r\n type: 'object',\r\n properties: {\r\n reason_text: {\r\n type: 'string',\r\n description: 'REQUIRED: A brief explanation of why you are listing this directory.',\r\n },\r\n DirectoryPath: {\r\n type: 'string',\r\n description: 'Path to list contents of, should be absolute path to a directory',\r\n },\r\n fileLimit: {\r\n type: 'integer',\r\n description: 'Optional: Maximum number of files to show (default: 100)',\r\n },\r\n showHidden: {\r\n type: 'boolean',\r\n description: 'Optional: Whether to show hidden files (dotfiles). Defaults to true.',\r\n },\r\n },\r\n required: ['reason_text', 'DirectoryPath'],\r\n },\r\n },\r\n async execute(args, context) {\r\n const { DirectoryPath, fileLimit = 100, showHidden = true } = args;\r\n const directory_path = DirectoryPath;\r\n const contextManager = context.contextManager as ContextManager;\r\n const currentContext = contextManager.getCurrentContext();\r\n\r\n if (currentContext.type === 'local') {\r\n const fullPath = path.isAbsolute(directory_path) ? directory_path : path.resolve(context.cwd, directory_path);\r\n\r\n if (!fs.existsSync(fullPath)) {\r\n throw new Error(`Directory not found: ${directory_path}\\n\\nAction: Check the path or use 'find_files' to locate the directory.`);\r\n }\r\n\r\n if (!fs.statSync(fullPath).isDirectory()) {\r\n throw new Error(`Path is not a directory: ${directory_path}\\n\\nAction: Verify the path points to a directory, not a file.`);\r\n }\r\n\r\n // Use enhanced listing\r\n const listing = fileUtils.listDirectoryEnhanced(fullPath, {\r\n fileLimit: fileLimit,\r\n dirLimit: 50,\r\n showHidden: showHidden,\r\n largeThreshold: 100\r\n });\r\n\r\n return fileUtils.formatDirectoryListing(directory_path, listing);\r\n } else {\r\n // For subshell contexts, use existing simple format\r\n // (enhanced format would require additional subshell handler changes)\r\n const entries = await contextManager.listDirectory(directory_path);\r\n\r\n const directories: string[] = [];\r\n const files: string[] = [];\r\n\r\n for (const entry of entries) {\r\n if (entry.type === 'directory') {\r\n directories.push(` ${entry.name}/`);\r\n } else {\r\n files.push(` ${entry.name}`);\r\n }\r\n }\r\n\r\n const lines: string[] = [\r\n `📁 Directory: ${directory_path} (${currentContext.type} environment)`,\r\n '═'.repeat(50),\r\n `Total: ${directories.length} directories, ${files.length} files, ${entries.length} items`,\r\n ''\r\n ];\r\n\r\n if (directories.length > 0) {\r\n lines.push('📂 Directories:');\r\n lines.push(...directories.slice(0, 50));\r\n if (directories.length > 50) {\r\n lines.push(` ... and ${directories.length - 50} more directories`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n if (files.length > 0) {\r\n const fileLimit = 100;\r\n const shownFiles = files.slice(0, fileLimit);\r\n const hiddenCount = Math.max(0, files.length - fileLimit);\r\n\r\n lines.push(hiddenCount > 0\r\n ? `📄 Files (${shownFiles.length} of ${files.length} shown):`\r\n : '📄 Files:');\r\n lines.push(...shownFiles);\r\n if (hiddenCount > 0) {\r\n lines.push(` ... and ${hiddenCount} more files`);\r\n lines.push('');\r\n lines.push('💡 To see more: List subdirectories individually or use find_files to search');\r\n }\r\n }\r\n\r\n return lines.join('\\n');\r\n }\r\n },\r\n};\r\n\r\n"],"mappings":"AAAA,YAAY,UAAU;AACtB,YAAY,QAAQ;AAEpB,YAAY,eAAe;AAC3B,YAAY,UAAU;AAEtB,SAAS,qBAAqB;AAC9B,SAAS,aAAa,+BAA+B;AACrD,SAAS,kBAAkB;AAO3B,eAAe,mBACb,SACA,kBACe;AACf,MAAI,CAAC,QAAQ,qBAAqB,CAAC,QAAQ,oBAAqB;AAChE,MAAI;AAEF,QAAI;AACJ,QAAI,QAAQ,gBAAgB;AAC1B,YAAM,aAAc,QAAQ,eAAkC,kBAAkB;AAChF,UAAI,WAAW,SAAS,WAAW,WAAW,SAAS;AACrD,cAAM,IAAI,WAAW;AACrB,YAAI,OAAO,EAAE,aAAa,cAAc,OAAO,EAAE,cAAc,cAC3D,OAAO,EAAE,mBAAmB,cAAc,OAAO,EAAE,gBAAgB,YAAY;AACjF,0BAAgB;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AACA,UAAM,QAAQ,kBAAkB;AAAA,MAC9B,QAAQ;AAAA,MACR;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,IACF;AAAA,EACF,SAAS,KAAK;AACZ,eAAW,0DAA2D,IAAc,OAAO,EAAE;AAAA,EAC/F;AACF;AAEO,MAAM,eAAqB;AAAA,EAChC,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAeb,YAAY;AAAA,MACV,MAAM;AAAA,MACN,YAAY;AAAA,QACV,aAAa;AAAA,UACX,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,cAAc;AAAA,UACZ,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,WAAW;AAAA,UACT,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,SAAS;AAAA,UACP,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,aAAa;AAAA,UACX,MAAM;AAAA,UACN,MAAM,CAAC,WAAW,OAAO;AAAA,UACzB,aAAa;AAAA,QACf;AAAA,MACF;AAAA,MACA,UAAU,CAAC,eAAe,cAAc;AAAA,IAC1C;AAAA,EACF;AAAA,EACA,MAAM,QAAQ,MAAM,SAAS;AAC3B,UAAM,EAAE,cAAc,WAAW,SAAS,aAAa,IAAI,IAAI;AAC/D,UAAM,YAAY;AAClB,UAAM,iBAAiB,QAAQ;AAC/B,UAAM,iBAAiB,eAAe,kBAAkB;AAIxD,UAAM,aAAa,QAAQ;AAG3B,QAAI;AACJ,QAAI;AAEJ,QAAI,eAAe,SAAS,WAAW,YAAY;AAIjD,iBAAW,KAAK,WAAW,SAAS,IAAI,YAAY,KAAK,QAAQ,QAAQ,KAAK,SAAS;AAEvF,UAAI,CAAC,GAAG,WAAW,QAAQ,GAAG;AAC5B,cAAM,IAAI,MAAM,mBAAmB,SAAS;AAAA;AAAA,qDAA0D;AAAA,MACxG;AAIA,UAAI,cAAc,UAAa,YAAY,QAAW;AACpD,kBAAU,UAAU,SAAS,UAAU,WAAW,OAAO;AAAA,MAC3D,OAAO;AAEL,kBAAU,GAAG,aAAa,UAAU,OAAO;AAAA,MAC7C;AAAA,IAEF,OAAO;AAEL,gBAAU,MAAM,eAAe,SAAS,SAAS;AAGjD,UAAI,cAAc,UAAa,YAAY,QAAW;AACpD,cAAMA,SAAQ,QAAQ,MAAM,IAAI;AAChC,cAAM,QAAQ,YAAY,YAAY,IAAI;AAC1C,cAAM,MAAM,UAAU,UAAUA,OAAM;AACtC,kBAAUA,OAAM,MAAM,OAAO,GAAG,EAAE,KAAK,IAAI;AAAA,MAC7C;AAAA,IACF;AAGA,UAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAI,iBAAiB;AACrB,QAAI,gBAAgB;AACpB,UAAM,WAAW,aAAa;AAI9B,QAAI,cAAc,UAAa,YAAY,UAAa,MAAM,SAAS,KAAK;AAC1E,uBAAiB,MAAM,MAAM,GAAG,GAAG;AACnC,sBAAgB;AAAA;AAAA,oCAAyC,MAAM,MAAM;AAAA;AAAA,IACvE;AAEA,UAAM,kBAAkB,eAAe,IAAI,CAAC,MAAM,QAAQ,GAAG,WAAW,GAAG,KAAK,IAAI,EAAE,EAAE,KAAK,IAAI;AAEjG,WAAO,SAAS,SAAS;AAAA,SAAY,cAAc,UAAa,YAAY,SAAY,MAAM,SAAS,eAAe,MAAM,IAAI,gBAAgB,gBAAgB,EAAE;AAAA;AAAA,EAAO,eAAe,GAAG,aAAa;AAAA,EAC1M;AACF;AAEO,MAAM,kBAAwB;AAAA,EACnC,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,IAKb,YAAY;AAAA,MACV,MAAM;AAAA,MACN,YAAY;AAAA,QACV,aAAa;AAAA,UACX,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,YAAY;AAAA,UACV,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,aAAa;AAAA,UACX,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,WAAW;AAAA,UACT,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,WAAW;AAAA,UACT,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,aAAa;AAAA,UACX,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,YAAY;AAAA,UACV,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,MACF;AAAA,MACA,UAAU,CAAC,eAAe,cAAc,aAAa,eAAe,aAAa,eAAe,YAAY;AAAA,IAC9G;AAAA,EACF;AAAA,EACA,MAAM,QAAQ,MAAM,SAAS;AAC3B,UAAM,EAAE,YAAY,aAAa,WAAW,UAAU,IAAI;AAC1D,UAAM,YAAY;AAClB,UAAM,UAAU,YAAY,KAAK;AAEjC,UAAM,iBAAiB,QAAQ;AAC/B,UAAM,iBAAiB,eAAe,kBAAkB;AAExD,QAAI,eAAe,SAAS,SAAS;AACnC,YAAM,WAAW,KAAK,QAAQ,QAAQ,KAAK,SAAS;AAGpD,UAAI,GAAG,WAAW,QAAQ,GAAG;AAC3B,YAAI,CAAC,WAAW;AACd,gBAAM,IAAI,MAAM,QAAQ,SAAS,qEAAqE;AAAA,QACxG;AAGA,cAAM,WAAW,UAAU,MAAM,GAAG,EAAE,IAAI,KAAK;AAC/C,cAAM,SAAS,MAAM,QAAQ;AAAA,UAC3B,QAAQ,QAAQ;AAAA,UAChB;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN;AAAA,YACA;AAAA,UACF;AAAA,UACA;AAAA,UACA,EAAE,WAAW,UAAU,WAAW,YAAY;AAAA,QAChD;AAEA,YAAI,OAAO,WAAW,YAAY,cAAc,QAAQ;AAEtD,gBAAM,IAAI,MAAM,kBAAkB,OAAO,QAAQ,EAAE;AAAA,QACrD;AAEA,YAAI,CAAC,QAAQ;AACX,gBAAM,IAAI,MAAM,8BAA8B;AAAA,QAChD;AAAA,MACF,OAAO;AAEL,cAAM,WAAW,UAAU,MAAM,GAAG,EAAE,IAAI,KAAK;AAE/C,cAAM,SAAS,MAAM,QAAQ;AAAA,UAC3B,mBAAmB,QAAQ;AAAA,UAC3B;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN;AAAA,YACA;AAAA,UACF;AAAA,UACA;AAAA,UACA,EAAE,WAAW,UAAU,WAAW,SAAS;AAAA,QAC7C;AAEA,YAAI,OAAO,WAAW,YAAY,cAAc,QAAQ;AAEtD,gBAAM,IAAI,MAAM,kBAAkB,OAAO,QAAQ,EAAE;AAAA,QACrD;AAEA,YAAI,CAAC,QAAQ;AACX,gBAAM,IAAI,MAAM,8BAA8B;AAAA,QAChD;AAAA,MACF;AAGA,YAAM,mBAAmB,SAAS,QAAQ;AAE1C,gBAAU,UAAU,UAAU,OAAO;AAGrC,YAAM,eAAe,MAAM,YAAY,UAAU,OAAO;AACxD,YAAM,iBAAiB,wBAAwB,YAAY;AAE3D,aAAO,sBAAsB,QAAQ,MAAM,kBAAkB,SAAS;AAAA,EAAK,cAAc;AAAA,IAC3F,OAAO;AAEL,YAAM,WAAW,UAAU,MAAM,GAAG,EAAE,IAAI,KAAK;AAC/C,YAAM,SAAS,MAAM,QAAQ;AAAA,QAC3B,cAAc,SAAS,OAAO,eAAe,IAAI;AAAA,QACjD;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN;AAAA,UACA;AAAA,QACF;AAAA,QACA;AAAA,QACA,EAAE,WAAW,WAAW,eAAe;AAAA,MACzC;AAEA,UAAI,OAAO,WAAW,YAAY,cAAc,QAAQ;AACtD,cAAM,IAAI,MAAM,kBAAkB,OAAO,QAAQ,EAAE;AAAA,MACrD;AAEA,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI,MAAM,8BAA8B;AAAA,MAChD;AAGA,YAAM,mBAAmB,SAAS,SAAS;AAE3C,YAAM,eAAe,UAAU,WAAW,OAAO;AAGjD,aAAO,sBAAsB,QAAQ,MAAM,kBAAkB,SAAS,OAAO,eAAe,IAAI;AAAA;AAAA,IAClG;AAAA,EACF;AACF;AAEO,MAAM,eAAqB;AAAA,EAChC,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAiBb,YAAY;AAAA,MACV,MAAM;AAAA,MACN,YAAY;AAAA,QACV,aAAa;AAAA,UACX,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,WAAW;AAAA,UACT,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,gBAAgB;AAAA,UACd,MAAM;AAAA,UACN,aAAa;AAAA;AAAA,QAEf;AAAA,QACA,aAAa;AAAA,UACX,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,MACF;AAAA,MACA,UAAU,CAAC,eAAe,aAAa,kBAAkB,aAAa;AAAA,IACxE;AAAA,EACF;AAAA,EACA,MAAM,QAAQ,MAAM,SAAS;AAC3B,UAAM,EAAE,WAAW,gBAAgB,YAAY,IAAI;AACnD,UAAM,iBAAiB,QAAQ;AAC/B,UAAM,iBAAiB,eAAe,kBAAkB;AAGxD,QAAI,eAAe,SAAS,SAAS;AACnC,YAAM,YAAY,IAAI,cAAc;AACpC,YAAM,mBAAmB,MAAM,UAAU;AAAA,QACvC;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,MACV;AAEA,UAAI,CAAC,iBAAiB,OAAO;AAC3B,cAAM,eAAe;AAAA,UACnB,iBAAiB;AAAA,UACjB,iBAAiB,aAAa;AAAA,cAAiB,iBAAiB,UAAU,KAAK;AAAA,QACjF,EAAE,KAAK,EAAE;AACT,cAAM,IAAI,MAAM,YAAY;AAAA,MAC9B;AAAA,IACF;AAGA,QAAI;AAEJ,UAAM,WAAW,eAAe,SAAS,UAAU,KAAK,QAAQ,QAAQ,KAAK,SAAS,IAAI;AAE1F,QAAI,eAAe,SAAS,SAAS;AACnC,UAAI,CAAC,GAAG,WAAW,QAAQ,GAAG;AAC5B,cAAM,IAAI,MAAM,mBAAmB,SAAS;AAAA;AAAA,qDAA0D;AAAA,MACxG;AAEA,mBAAa,GAAG,aAAa,UAAU,OAAO;AAAA,IAChD,OAAO;AACL,mBAAa,MAAM,eAAe,SAAS,SAAS;AAAA,IACtD;AAGA,UAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,iBAAiB;AAC9D,UAAM,QAAQ,oBAAoB,cAAc;AAIhD,UAAM,UAAU,WAAW,MAAM,KAAK;AACtC,QAAI,CAAC,WAAW,QAAQ,WAAW,GAAG;AACpC,YAAM,IAAI,MAAM,4DAA4D;AAAA,IAC9E;AACA,QAAI,QAAQ,SAAS,GAAG;AACtB,YAAM,IAAI,MAAM,mBAAmB,QAAQ,MAAM,wCAAwC;AAAA,IAC3F;AAGA,UAAM,aAAa,WAAW,OAAO,KAAK;AAC1C,UAAM,kBAAkB,cAAc,IAAI,WAAW,UAAU,GAAG,UAAU,EAAE,MAAM,IAAI,EAAE,SAAS;AAKnG,UAAM,aAAa,WAAW,QAAQ,OAAO,WAAW;AAGxD,UAAM,QAAQ,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAGA,UAAM,WAAW,KAAK;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,EAAE,SAAS,OAAO,iBAAiB;AAAA,IACrC;AAGA,UAAM,WAAW,MAAM,QAAQ;AAAA,MAC7B,0BAA0B,QAAQ,GAAG,eAAe,SAAS,UAAU,OAAO,eAAe,IAAI,iBAAiB,EAAE;AAAA,MACpH;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA,QACT;AAAA,MACF;AAAA,MACA;AAAA,MACA,EAAE,WAAW,SAAS;AAAA,IACxB;AAEA,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAGA,QAAI,eAAe,SAAS,SAAS;AAEnC,YAAM,mBAAmB,SAAS,QAAQ;AAE1C,SAAG,cAAc,UAAU,YAAY,OAAO;AAG9C,YAAM,eAAe,MAAM,YAAY,UAAU,UAAU;AAC3D,YAAM,iBAAiB,wBAAwB,YAAY;AAE3D,aAAO,oCAAoC,SAAS,aAAa,eAAe;AAAA,EAAM,cAAc;AAAA,IACtG,OAAO;AAEL,YAAM,mBAAmB,SAAS,QAAQ;AAE1C,YAAM,eAAe,UAAU,WAAW,UAAU;AAGpD,aAAO,oCAAoC,SAAS,aAAa,eAAe,KAAK,eAAe,IAAI;AAAA;AAAA,IAC1G;AAAA,EACF;AACF;AAEO,MAAM,oBAA0B;AAAA,EACrC,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAcb,YAAY;AAAA,MACV,MAAM;AAAA,MACN,YAAY;AAAA,QACV,aAAa;AAAA,UACX,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,WAAW;AAAA,UACT,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,OAAO;AAAA,UACL,MAAM;AAAA,UACN,aAAa;AAAA,UACb,OAAO;AAAA,YACL,MAAM;AAAA,YACN,YAAY;AAAA,cACV,gBAAgB;AAAA,gBACd,MAAM;AAAA,gBACN,aAAa;AAAA,cACf;AAAA,cACA,aAAa;AAAA,gBACX,MAAM;AAAA,gBACN,aAAa;AAAA,cACf;AAAA,cACA,aAAa;AAAA,gBACX,MAAM;AAAA,gBACN,aAAa;AAAA,cACf;AAAA,YACF;AAAA,YACA,UAAU,CAAC,kBAAkB,aAAa;AAAA,UAC5C;AAAA,QACF;AAAA,MACF;AAAA,MACA,UAAU,CAAC,eAAe,aAAa,OAAO;AAAA,IAChD;AAAA,EACF;AAAA,EACA,MAAM,QAAQ,MAAM,SAAS;AAC3B,UAAM,EAAE,WAAW,MAAM,IAAI;AAC7B,UAAM,iBAAiB,QAAQ;AAC/B,UAAM,iBAAiB,eAAe,kBAAkB;AAExD,QAAI,CAAC,SAAS,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG;AACzD,YAAM,IAAI,MAAM,8EAA8E;AAAA,IAChG;AAGA,QAAI;AAEJ,UAAM,WAAW,eAAe,SAAS,UAAU,KAAK,QAAQ,QAAQ,KAAK,SAAS,IAAI;AAE1F,QAAI,eAAe,SAAS,SAAS;AACnC,UAAI,CAAC,GAAG,WAAW,QAAQ,GAAG;AAC5B,cAAM,IAAI,MAAM,mBAAmB,SAAS,EAAE;AAAA,MAChD;AACA,gBAAU,GAAG,aAAa,UAAU,OAAO;AAAA,IAC7C,OAAO;AACL,gBAAU,MAAM,eAAe,SAAS,SAAS;AAAA,IACnD;AAEA,UAAM,kBAAkB;AACxB,UAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,iBAAiB;AAC9D,UAAM,eAAyB,CAAC;AAChC,UAAM,SAAmB,CAAC;AAG1B,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAM,OAAO,MAAM,CAAC;AACpB,YAAM,QAAQ,oBAAoB,KAAK,cAAc;AACrD,YAAM,UAAU,QAAQ,MAAM,KAAK;AAEnC,UAAI,CAAC,WAAW,QAAQ,WAAW,GAAG;AACpC,eAAO,KAAK,QAAQ,IAAI,CAAC,6BAA6B;AAAA,MACxD,WAAW,QAAQ,SAAS,GAAG;AAC7B,eAAO,KAAK,QAAQ,IAAI,CAAC,qBAAqB,QAAQ,MAAM,yBAAyB;AAAA,MACvF;AAAA,IACF;AAEA,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,IAAI,MAAM;AAAA,EAAuB,OAAO,KAAK,IAAI,CAAC;AAAA;AAAA,sBAA2B;AAAA,IACrF;AAGA,UAAM,kBAA4B,CAAC;AACnC,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAM,OAAO,MAAM,CAAC;AACpB,YAAM,QAAQ,oBAAoB,KAAK,cAAc;AAGrD,YAAM,UAAU,QAAQ,MAAM,KAAK;AACnC,UAAI,CAAC,WAAW,QAAQ,WAAW,GAAG;AACpC,eAAO,KAAK,QAAQ,IAAI,CAAC,gDAAgD;AACzE,wBAAgB,KAAK,CAAC;AACtB;AAAA,MACF;AAGA,YAAM,WAAW,QAAQ,OAAO,KAAK;AACrC,YAAM,UAAU,YAAY,IAAI,QAAQ,UAAU,GAAG,QAAQ,EAAE,MAAM,IAAI,EAAE,SAAS;AACpF,sBAAgB,KAAK,OAAO;AAE5B,gBAAU,QAAQ,QAAQ,OAAO,KAAK,WAAW;AACjD,mBAAa,KAAK,KAAK,eAAe,QAAQ,IAAI,CAAC,EAAE;AAAA,IACvD;AAGA,UAAM,QAAQ,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,UAAM,WAAW,KAAK;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,EAAE,SAAS,OAAO,iBAAiB;AAAA,IACrC;AAGA,UAAM,WAAW,MAAM,QAAQ;AAAA,MAC7B,SAAS,MAAM,MAAM,aAAa,QAAQ;AAAA,MAC1C;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA,QACT;AAAA,MACF;AAAA,MACA;AAAA,MACA,EAAE,WAAW,UAAU,WAAW,MAAM,OAAO;AAAA,IACjD;AAEA,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAGA,QAAI,eAAe,SAAS,SAAS;AAEnC,YAAM,mBAAmB,SAAS,QAAQ;AAE1C,SAAG,cAAc,UAAU,SAAS,OAAO;AAE3C,YAAM,eAAe,MAAM,YAAY,UAAU,OAAO;AACxD,YAAM,iBAAiB,wBAAwB,YAAY;AAE3D,aAAO,wBAAwB,aAAa,MAAM,aAAa,SAAS,cAAc,gBAAgB,KAAK,IAAI,CAAC;AAAA,EAAO,aAAa,IAAI,CAAC,GAAG,MAAM,KAAK,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,IAAI,CAAC;AAAA,EAAK,cAAc;AAAA,IACrM,OAAO;AAEL,YAAM,mBAAmB,SAAS,QAAQ;AAE1C,YAAM,eAAe,UAAU,WAAW,OAAO;AACjD,aAAO,wBAAwB,aAAa,MAAM,aAAa,SAAS,KAAK,eAAe,IAAI;AAAA,IAClG;AAAA,EACF;AACF;AAEO,MAAM,cAAoB;AAAA,EAC/B,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAab,YAAY;AAAA,MACV,MAAM;AAAA,MACN,YAAY;AAAA,QACV,aAAa;AAAA,UACX,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,eAAe;AAAA,UACb,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,WAAW;AAAA,UACT,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,YAAY;AAAA,UACV,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,MACF;AAAA,MACA,UAAU,CAAC,eAAe,eAAe;AAAA,IAC3C;AAAA,EACF;AAAA,EACA,MAAM,QAAQ,MAAM,SAAS;AAC3B,UAAM,EAAE,eAAe,YAAY,KAAK,aAAa,KAAK,IAAI;AAC9D,UAAM,iBAAiB;AACvB,UAAM,iBAAiB,QAAQ;AAC/B,UAAM,iBAAiB,eAAe,kBAAkB;AAExD,QAAI,eAAe,SAAS,SAAS;AACnC,YAAM,WAAW,KAAK,WAAW,cAAc,IAAI,iBAAiB,KAAK,QAAQ,QAAQ,KAAK,cAAc;AAE5G,UAAI,CAAC,GAAG,WAAW,QAAQ,GAAG;AAC5B,cAAM,IAAI,MAAM,wBAAwB,cAAc;AAAA;AAAA,oEAAyE;AAAA,MACjI;AAEA,UAAI,CAAC,GAAG,SAAS,QAAQ,EAAE,YAAY,GAAG;AACxC,cAAM,IAAI,MAAM,4BAA4B,cAAc;AAAA;AAAA,2DAAgE;AAAA,MAC5H;AAGA,YAAM,UAAU,UAAU,sBAAsB,UAAU;AAAA,QACxD;AAAA,QACA,UAAU;AAAA,QACV;AAAA,QACA,gBAAgB;AAAA,MAClB,CAAC;AAED,aAAO,UAAU,uBAAuB,gBAAgB,OAAO;AAAA,IACjE,OAAO;AAGL,YAAM,UAAU,MAAM,eAAe,cAAc,cAAc;AAEjE,YAAM,cAAwB,CAAC;AAC/B,YAAM,QAAkB,CAAC;AAEzB,iBAAW,SAAS,SAAS;AAC3B,YAAI,MAAM,SAAS,aAAa;AAC9B,sBAAY,KAAK,KAAK,MAAM,IAAI,GAAG;AAAA,QACrC,OAAO;AACL,gBAAM,KAAK,KAAK,MAAM,IAAI,EAAE;AAAA,QAC9B;AAAA,MACF;AAEA,YAAM,QAAkB;AAAA,QACtB,wBAAiB,cAAc,KAAK,eAAe,IAAI;AAAA,QACvD,SAAI,OAAO,EAAE;AAAA,QACb,UAAU,YAAY,MAAM,iBAAiB,MAAM,MAAM,WAAW,QAAQ,MAAM;AAAA,QAClF;AAAA,MACF;AAEA,UAAI,YAAY,SAAS,GAAG;AAC1B,cAAM,KAAK,wBAAiB;AAC5B,cAAM,KAAK,GAAG,YAAY,MAAM,GAAG,EAAE,CAAC;AACtC,YAAI,YAAY,SAAS,IAAI;AAC3B,gBAAM,KAAK,aAAa,YAAY,SAAS,EAAE,mBAAmB;AAAA,QACpE;AACA,cAAM,KAAK,EAAE;AAAA,MACf;AAEA,UAAI,MAAM,SAAS,GAAG;AACpB,cAAMC,aAAY;AAClB,cAAM,aAAa,MAAM,MAAM,GAAGA,UAAS;AAC3C,cAAM,cAAc,KAAK,IAAI,GAAG,MAAM,SAASA,UAAS;AAExD,cAAM,KAAK,cAAc,IACrB,oBAAa,WAAW,MAAM,OAAO,MAAM,MAAM,aACjD,kBAAW;AACf,cAAM,KAAK,GAAG,UAAU;AACxB,YAAI,cAAc,GAAG;AACnB,gBAAM,KAAK,aAAa,WAAW,aAAa;AAChD,gBAAM,KAAK,EAAE;AACb,gBAAM,KAAK,qFAA8E;AAAA,QAC3F;AAAA,MACF;AAEA,aAAO,MAAM,KAAK,IAAI;AAAA,IACxB;AAAA,EACF;AACF;","names":["lines","fileLimit"]}