@qduc/term2 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.d.ts +5 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +61 -8
- package/dist/agent.js.map +1 -1
- package/dist/agent.test.js +122 -10
- package/dist/agent.test.js.map +1 -1
- package/dist/app.d.ts +13 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +71 -18
- package/dist/app.js.map +1 -1
- package/dist/app.startup-banner.test.js +48 -1
- package/dist/app.startup-banner.test.js.map +1 -1
- package/dist/cli.js +197 -28
- package/dist/cli.js.map +1 -1
- package/dist/components/ApprovalPrompt.d.ts.map +1 -1
- package/dist/components/ApprovalPrompt.js +11 -34
- package/dist/components/ApprovalPrompt.js.map +1 -1
- package/dist/components/Banner.d.ts.map +1 -1
- package/dist/components/Banner.js +2 -7
- package/dist/components/Banner.js.map +1 -1
- package/dist/components/BottomArea.d.ts +7 -1
- package/dist/components/BottomArea.d.ts.map +1 -1
- package/dist/components/BottomArea.js +2 -2
- package/dist/components/BottomArea.js.map +1 -1
- package/dist/components/ChatMessage.d.ts.map +1 -1
- package/dist/components/ChatMessage.js +2 -1
- package/dist/components/ChatMessage.js.map +1 -1
- package/dist/components/CommandMessage.d.ts.map +1 -1
- package/dist/components/CommandMessage.js +45 -48
- package/dist/components/CommandMessage.js.map +1 -1
- package/dist/components/CommandMessage.test.js +69 -0
- package/dist/components/CommandMessage.test.js.map +1 -1
- package/dist/components/DiffView.d.ts +7 -0
- package/dist/components/DiffView.d.ts.map +1 -0
- package/dist/components/DiffView.js +80 -0
- package/dist/components/DiffView.js.map +1 -0
- package/dist/components/DiffView.test.d.ts +2 -0
- package/dist/components/DiffView.test.d.ts.map +1 -0
- package/dist/components/DiffView.test.js +45 -0
- package/dist/components/DiffView.test.js.map +1 -0
- package/dist/components/Input/PopupManager.d.ts +7 -0
- package/dist/components/Input/PopupManager.d.ts.map +1 -1
- package/dist/components/Input/PopupManager.js +4 -2
- package/dist/components/Input/PopupManager.js.map +1 -1
- package/dist/components/Input/determine-active-menu.d.ts.map +1 -1
- package/dist/components/Input/determine-active-menu.js +4 -1
- package/dist/components/Input/determine-active-menu.js.map +1 -1
- package/dist/components/Input/popup-props.d.ts +3 -1
- package/dist/components/Input/popup-props.d.ts.map +1 -1
- package/dist/components/Input/popup-props.js +7 -1
- package/dist/components/Input/popup-props.js.map +1 -1
- package/dist/components/InputBox.d.ts +6 -0
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +74 -11
- package/dist/components/InputBox.js.map +1 -1
- package/dist/components/InputBox.menu-logic.test.js +16 -1
- package/dist/components/InputBox.menu-logic.test.js.map +1 -1
- package/dist/components/InputBox.test.js +1 -0
- package/dist/components/InputBox.test.js.map +1 -1
- package/dist/components/MarkdownRenderer.d.ts.map +1 -1
- package/dist/components/MarkdownRenderer.js +65 -7
- package/dist/components/MarkdownRenderer.js.map +1 -1
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +62 -9
- package/dist/components/MessageList.js.map +1 -1
- package/dist/components/MessageList.test.js +128 -0
- package/dist/components/MessageList.test.js.map +1 -1
- package/dist/components/SettingsValueSelectionMenu.d.ts.map +1 -1
- package/dist/components/SettingsValueSelectionMenu.js +4 -1
- package/dist/components/SettingsValueSelectionMenu.js.map +1 -1
- package/dist/components/StatusBar.d.ts.map +1 -1
- package/dist/components/StatusBar.js +8 -6
- package/dist/components/StatusBar.js.map +1 -1
- package/dist/components/StatusBar.test.js +24 -0
- package/dist/components/StatusBar.test.js.map +1 -1
- package/dist/components/SubagentActivityMessage.d.ts +12 -0
- package/dist/components/SubagentActivityMessage.d.ts.map +1 -0
- package/dist/components/SubagentActivityMessage.js +30 -0
- package/dist/components/SubagentActivityMessage.js.map +1 -0
- package/dist/components/UndoSelectionMenu.d.ts +12 -0
- package/dist/components/UndoSelectionMenu.d.ts.map +1 -0
- package/dist/components/UndoSelectionMenu.js +46 -0
- package/dist/components/UndoSelectionMenu.js.map +1 -0
- package/dist/components/UndoSelectionMenu.test.d.ts +2 -0
- package/dist/components/UndoSelectionMenu.test.d.ts.map +1 -0
- package/dist/components/UndoSelectionMenu.test.js +40 -0
- package/dist/components/UndoSelectionMenu.test.js.map +1 -0
- package/dist/components/theme.d.ts +8 -0
- package/dist/components/theme.d.ts.map +1 -0
- package/dist/components/theme.js +20 -0
- package/dist/components/theme.js.map +1 -0
- package/dist/context/InputContext.d.ts +1 -1
- package/dist/context/InputContext.d.ts.map +1 -1
- package/dist/context/InputContext.js.map +1 -1
- package/dist/hooks/use-app-commands.d.ts +20 -2
- package/dist/hooks/use-app-commands.d.ts.map +1 -1
- package/dist/hooks/use-app-commands.js +138 -52
- package/dist/hooks/use-app-commands.js.map +1 -1
- package/dist/hooks/use-app-commands.test.js +264 -1
- package/dist/hooks/use-app-commands.test.js.map +1 -1
- package/dist/hooks/use-conversation.d.ts +20 -2
- package/dist/hooks/use-conversation.d.ts.map +1 -1
- package/dist/hooks/use-conversation.js +114 -14
- package/dist/hooks/use-conversation.js.map +1 -1
- package/dist/hooks/use-conversation.resume.test.d.ts +2 -0
- package/dist/hooks/use-conversation.resume.test.d.ts.map +1 -0
- package/dist/hooks/use-conversation.resume.test.js +26 -0
- package/dist/hooks/use-conversation.resume.test.js.map +1 -0
- package/dist/hooks/use-escape-key.d.ts.map +1 -1
- package/dist/hooks/use-escape-key.js +14 -7
- package/dist/hooks/use-escape-key.js.map +1 -1
- package/dist/hooks/use-escape-key.test.js +168 -4
- package/dist/hooks/use-escape-key.test.js.map +1 -1
- package/dist/hooks/use-input-history.d.ts +4 -3
- package/dist/hooks/use-input-history.d.ts.map +1 -1
- package/dist/hooks/use-input-history.js +10 -8
- package/dist/hooks/use-input-history.js.map +1 -1
- package/dist/hooks/use-input-history.test.d.ts +2 -0
- package/dist/hooks/use-input-history.test.d.ts.map +1 -0
- package/dist/hooks/use-input-history.test.js +43 -0
- package/dist/hooks/use-input-history.test.js.map +1 -0
- package/dist/hooks/use-mode-handlers.d.ts +10 -2
- package/dist/hooks/use-mode-handlers.d.ts.map +1 -1
- package/dist/hooks/use-mode-handlers.js +21 -2
- package/dist/hooks/use-mode-handlers.js.map +1 -1
- package/dist/hooks/use-model-selection.d.ts +3 -0
- package/dist/hooks/use-model-selection.d.ts.map +1 -1
- package/dist/hooks/use-model-selection.js +3 -0
- package/dist/hooks/use-model-selection.js.map +1 -1
- package/dist/hooks/use-path-completion.test.d.ts +2 -0
- package/dist/hooks/use-path-completion.test.d.ts.map +1 -0
- package/dist/hooks/use-path-completion.test.js +54 -0
- package/dist/hooks/use-path-completion.test.js.map +1 -0
- package/dist/hooks/use-runtime-settings.d.ts.map +1 -1
- package/dist/hooks/use-runtime-settings.js +15 -6
- package/dist/hooks/use-runtime-settings.js.map +1 -1
- package/dist/hooks/use-settings-completion.d.ts.map +1 -1
- package/dist/hooks/use-settings-completion.js +0 -1
- package/dist/hooks/use-settings-completion.js.map +1 -1
- package/dist/hooks/use-settings-value-completion.d.ts +1 -0
- package/dist/hooks/use-settings-value-completion.d.ts.map +1 -1
- package/dist/hooks/use-settings-value-completion.js +25 -19
- package/dist/hooks/use-settings-value-completion.js.map +1 -1
- package/dist/hooks/use-shell-mode.d.ts.map +1 -1
- package/dist/hooks/use-shell-mode.js +2 -1
- package/dist/hooks/use-shell-mode.js.map +1 -1
- package/dist/hooks/use-slash-commands.d.ts +1 -0
- package/dist/hooks/use-slash-commands.d.ts.map +1 -1
- package/dist/hooks/use-slash-commands.js +10 -1
- package/dist/hooks/use-slash-commands.js.map +1 -1
- package/dist/hooks/use-undo-selection.d.ts +19 -0
- package/dist/hooks/use-undo-selection.d.ts.map +1 -0
- package/dist/hooks/use-undo-selection.js +76 -0
- package/dist/hooks/use-undo-selection.js.map +1 -0
- package/dist/lib/editor-impl.test.js +63 -50
- package/dist/lib/editor-impl.test.js.map +1 -1
- package/dist/lib/openai-agent-client.d.ts +9 -0
- package/dist/lib/openai-agent-client.d.ts.map +1 -1
- package/dist/lib/openai-agent-client.js +104 -134
- package/dist/lib/openai-agent-client.js.map +1 -1
- package/dist/lib/openai-agent-client.test.js +30 -0
- package/dist/lib/openai-agent-client.test.js.map +1 -1
- package/dist/non-interactive.d.ts +6 -1
- package/dist/non-interactive.d.ts.map +1 -1
- package/dist/non-interactive.js +70 -4
- package/dist/non-interactive.js.map +1 -1
- package/dist/non-interactive.test.js +176 -1
- package/dist/non-interactive.test.js.map +1 -1
- package/dist/prompts/orchestrator.md +46 -0
- package/dist/prompts/plan-mode-info.md +17 -0
- package/dist/prompts/prompt-selector.d.ts +2 -1
- package/dist/prompts/prompt-selector.d.ts.map +1 -1
- package/dist/prompts/prompt-selector.js +5 -1
- package/dist/prompts/prompt-selector.js.map +1 -1
- package/dist/prompts/prompt-selector.test.js +7 -0
- package/dist/prompts/prompt-selector.test.js.map +1 -1
- package/dist/prompts/prompt-selector.test.ts +18 -0
- package/dist/prompts/prompt-selector.ts +7 -1
- package/dist/prompts/reasoning-efficiency.d.ts +7 -0
- package/dist/prompts/reasoning-efficiency.d.ts.map +1 -0
- package/dist/prompts/reasoning-efficiency.js +19 -0
- package/dist/prompts/reasoning-efficiency.js.map +1 -0
- package/dist/prompts/reasoning-efficiency.ts +18 -0
- package/dist/prompts/search-via-shell.test.js +17 -17
- package/dist/prompts/search-via-shell.test.js.map +1 -1
- package/dist/prompts/search-via-shell.test.ts +17 -17
- package/dist/prompts/shell-auto-approval.d.ts +3 -0
- package/dist/prompts/shell-auto-approval.d.ts.map +1 -0
- package/dist/prompts/shell-auto-approval.js +23 -0
- package/dist/prompts/shell-auto-approval.js.map +1 -0
- package/dist/prompts/shell-auto-approval.ts +23 -0
- package/dist/prompts/subagent-delegation.d.ts +10 -0
- package/dist/prompts/subagent-delegation.d.ts.map +1 -0
- package/dist/prompts/subagent-delegation.js +33 -0
- package/dist/prompts/subagent-delegation.js.map +1 -0
- package/dist/prompts/subagent-delegation.test.d.ts +2 -0
- package/dist/prompts/subagent-delegation.test.d.ts.map +1 -0
- package/dist/prompts/subagent-delegation.test.js +17 -0
- package/dist/prompts/subagent-delegation.test.js.map +1 -0
- package/dist/prompts/subagent-delegation.test.ts +21 -0
- package/dist/prompts/subagent-delegation.ts +40 -0
- package/dist/prompts/subagents/explorer.md +41 -0
- package/dist/prompts/subagents/mentor.md +15 -0
- package/dist/prompts/subagents/researcher.md +39 -0
- package/dist/prompts/subagents/worker.md +40 -0
- package/dist/providers/ai-sdk-anthropic.provider.d.ts +1 -0
- package/dist/providers/ai-sdk-anthropic.provider.d.ts.map +1 -1
- package/dist/providers/ai-sdk-anthropic.provider.js +67 -1
- package/dist/providers/ai-sdk-anthropic.provider.js.map +1 -1
- package/dist/providers/ai-sdk-anthropic.provider.test.d.ts +2 -0
- package/dist/providers/ai-sdk-anthropic.provider.test.d.ts.map +1 -0
- package/dist/providers/ai-sdk-anthropic.provider.test.js +87 -0
- package/dist/providers/ai-sdk-anthropic.provider.test.js.map +1 -0
- package/dist/providers/common/openai-compatible-messages.d.ts +1 -1
- package/dist/providers/common/openai-compatible-messages.d.ts.map +1 -1
- package/dist/providers/common/openai-compatible-messages.js +47 -16
- package/dist/providers/common/openai-compatible-messages.js.map +1 -1
- package/dist/providers/common/openai-compatible-messages.test.js +77 -14
- package/dist/providers/common/openai-compatible-messages.test.js.map +1 -1
- package/dist/providers/custom-provider-adapter.test.js +39 -1
- package/dist/providers/custom-provider-adapter.test.js.map +1 -1
- package/dist/providers/openai-compatible.provider.d.ts +11 -1
- package/dist/providers/openai-compatible.provider.d.ts.map +1 -1
- package/dist/providers/openai-compatible.provider.js +124 -10
- package/dist/providers/openai-compatible.provider.js.map +1 -1
- package/dist/providers/openai-compatible.provider.test.js +62 -1
- package/dist/providers/openai-compatible.provider.test.js.map +1 -1
- package/dist/providers/openrouter.provider.js +1 -1
- package/dist/providers/openrouter.provider.js.map +1 -1
- package/dist/scripts/eval-auto-approval/dataset.d.ts +1 -1
- package/dist/services/agent-stream.d.ts +1 -0
- package/dist/services/agent-stream.d.ts.map +1 -1
- package/dist/services/approval-flow-coordinator.d.ts +2 -0
- package/dist/services/approval-flow-coordinator.d.ts.map +1 -1
- package/dist/services/approval-flow-coordinator.js +45 -18
- package/dist/services/approval-flow-coordinator.js.map +1 -1
- package/dist/services/approval-flow-coordinator.test.js +87 -0
- package/dist/services/approval-flow-coordinator.test.js.map +1 -1
- package/dist/services/approval-rejection-interceptor.test.js +5 -0
- package/dist/services/approval-rejection-interceptor.test.js.map +1 -1
- package/dist/services/approval-state.d.ts +4 -0
- package/dist/services/approval-state.d.ts.map +1 -1
- package/dist/services/approval-state.js +2 -0
- package/dist/services/approval-state.js.map +1 -1
- package/dist/services/approval-state.test.js +21 -0
- package/dist/services/approval-state.test.js.map +1 -1
- package/dist/services/conversation-events.d.ts +25 -1
- package/dist/services/conversation-events.d.ts.map +1 -1
- package/dist/services/conversation-persistence.d.ts +52 -0
- package/dist/services/conversation-persistence.d.ts.map +1 -0
- package/dist/services/conversation-persistence.js +163 -0
- package/dist/services/conversation-persistence.js.map +1 -0
- package/dist/services/conversation-persistence.test.d.ts +2 -0
- package/dist/services/conversation-persistence.test.d.ts.map +1 -0
- package/dist/services/conversation-persistence.test.js +418 -0
- package/dist/services/conversation-persistence.test.js.map +1 -0
- package/dist/services/conversation-result-builder.d.ts.map +1 -1
- package/dist/services/conversation-result-builder.js +13 -0
- package/dist/services/conversation-result-builder.js.map +1 -1
- package/dist/services/conversation-service.d.ts +22 -0
- package/dist/services/conversation-service.d.ts.map +1 -1
- package/dist/services/conversation-service.js +18 -0
- package/dist/services/conversation-service.js.map +1 -1
- package/dist/services/conversation-session.auto-approval.test.js +57 -9
- package/dist/services/conversation-session.auto-approval.test.js.map +1 -1
- package/dist/services/conversation-session.d.ts +25 -0
- package/dist/services/conversation-session.d.ts.map +1 -1
- package/dist/services/conversation-session.js +208 -40
- package/dist/services/conversation-session.js.map +1 -1
- package/dist/services/conversation-store.d.ts +42 -0
- package/dist/services/conversation-store.d.ts.map +1 -1
- package/dist/services/conversation-store.js +182 -1
- package/dist/services/conversation-store.js.map +1 -1
- package/dist/services/conversation-store.test.js +310 -1
- package/dist/services/conversation-store.test.js.map +1 -1
- package/dist/services/history-service.d.ts +6 -1
- package/dist/services/history-service.d.ts.map +1 -1
- package/dist/services/history-service.js +46 -6
- package/dist/services/history-service.js.map +1 -1
- package/dist/services/history-service.test.d.ts +2 -0
- package/dist/services/history-service.test.d.ts.map +1 -0
- package/dist/services/history-service.test.js +77 -0
- package/dist/services/history-service.test.js.map +1 -0
- package/dist/services/input-surge-guard.d.ts +37 -0
- package/dist/services/input-surge-guard.d.ts.map +1 -0
- package/dist/services/input-surge-guard.js +190 -0
- package/dist/services/input-surge-guard.js.map +1 -0
- package/dist/services/input-surge-guard.test.d.ts +2 -0
- package/dist/services/input-surge-guard.test.d.ts.map +1 -0
- package/dist/services/input-surge-guard.test.js +126 -0
- package/dist/services/input-surge-guard.test.js.map +1 -0
- package/dist/services/interruption-info.js +1 -1
- package/dist/services/interruption-info.js.map +1 -1
- package/dist/services/interruption-info.test.js +12 -0
- package/dist/services/interruption-info.test.js.map +1 -1
- package/dist/services/mode-notices.d.ts +11 -0
- package/dist/services/mode-notices.d.ts.map +1 -0
- package/dist/services/mode-notices.js +16 -0
- package/dist/services/mode-notices.js.map +1 -0
- package/dist/services/plan-mode-interceptor.d.ts +10 -0
- package/dist/services/plan-mode-interceptor.d.ts.map +1 -0
- package/dist/services/plan-mode-interceptor.js +38 -0
- package/dist/services/plan-mode-interceptor.js.map +1 -0
- package/dist/services/plan-mode-interceptor.test.d.ts +2 -0
- package/dist/services/plan-mode-interceptor.test.d.ts.map +1 -0
- package/dist/services/plan-mode-interceptor.test.js +55 -0
- package/dist/services/plan-mode-interceptor.test.js.map +1 -0
- package/dist/services/rtk-service.d.ts.map +1 -1
- package/dist/services/rtk-service.js +104 -9
- package/dist/services/rtk-service.js.map +1 -1
- package/dist/services/rtk-service.test.js +55 -20
- package/dist/services/rtk-service.test.js.map +1 -1
- package/dist/services/service-interfaces.d.ts +4 -1
- package/dist/services/service-interfaces.d.ts.map +1 -1
- package/dist/services/settings-persistence.js +1 -1
- package/dist/services/settings-schema.d.ts +107 -4
- package/dist/services/settings-schema.d.ts.map +1 -1
- package/dist/services/settings-schema.js +103 -5
- package/dist/services/settings-schema.js.map +1 -1
- package/dist/services/settings-schema.test.js +35 -1
- package/dist/services/settings-schema.test.js.map +1 -1
- package/dist/services/settings-service.d.ts +3 -1
- package/dist/services/settings-service.d.ts.map +1 -1
- package/dist/services/settings-service.js +40 -4
- package/dist/services/settings-service.js.map +1 -1
- package/dist/services/settings-sources.d.ts.map +1 -1
- package/dist/services/settings-sources.js +2 -1
- package/dist/services/settings-sources.js.map +1 -1
- package/dist/services/settings-sources.test.js +4 -0
- package/dist/services/settings-sources.test.js.map +1 -1
- package/dist/services/shell-auto-approval-evaluator.d.ts +2 -1
- package/dist/services/shell-auto-approval-evaluator.d.ts.map +1 -1
- package/dist/services/shell-auto-approval-evaluator.js +12 -29
- package/dist/services/shell-auto-approval-evaluator.js.map +1 -1
- package/dist/services/shell-auto-approval-evaluator.test.js +2 -4
- package/dist/services/shell-auto-approval-evaluator.test.js.map +1 -1
- package/dist/services/shell-auto-approval-resolver.test.js +1 -1
- package/dist/services/shell-auto-approval-resolver.test.js.map +1 -1
- package/dist/services/stream-event-processor.d.ts.map +1 -1
- package/dist/services/stream-event-processor.js +24 -8
- package/dist/services/stream-event-processor.js.map +1 -1
- package/dist/services/stream-event-processor.test.js +27 -0
- package/dist/services/stream-event-processor.test.js.map +1 -1
- package/dist/services/subagents/subagent-manager.d.ts +16 -0
- package/dist/services/subagents/subagent-manager.d.ts.map +1 -0
- package/dist/services/subagents/subagent-manager.js +670 -0
- package/dist/services/subagents/subagent-manager.js.map +1 -0
- package/dist/services/subagents/subagent-manager.test.d.ts +2 -0
- package/dist/services/subagents/subagent-manager.test.d.ts.map +1 -0
- package/dist/services/subagents/subagent-manager.test.js +880 -0
- package/dist/services/subagents/subagent-manager.test.js.map +1 -0
- package/dist/services/subagents/subagent-session.d.ts +26 -0
- package/dist/services/subagents/subagent-session.d.ts.map +1 -0
- package/dist/services/subagents/subagent-session.js +86 -0
- package/dist/services/subagents/subagent-session.js.map +1 -0
- package/dist/services/subagents/subagent-session.test.d.ts +2 -0
- package/dist/services/subagents/subagent-session.test.d.ts.map +1 -0
- package/dist/services/subagents/subagent-session.test.js +118 -0
- package/dist/services/subagents/subagent-session.test.js.map +1 -0
- package/dist/services/subagents/types.d.ts +41 -0
- package/dist/services/subagents/types.d.ts.map +1 -0
- package/dist/services/subagents/types.js +2 -0
- package/dist/services/subagents/types.js.map +1 -0
- package/dist/services/terminal-result-collector.d.ts.map +1 -1
- package/dist/services/terminal-result-collector.js +31 -5
- package/dist/services/terminal-result-collector.js.map +1 -1
- package/dist/services/terminal-result-collector.test.js +81 -0
- package/dist/services/terminal-result-collector.test.js.map +1 -1
- package/dist/slash-commands.d.ts +1 -0
- package/dist/slash-commands.d.ts.map +1 -1
- package/dist/slash-commands.js +8 -1
- package/dist/slash-commands.js.map +1 -1
- package/dist/slash-commands.test.d.ts +2 -0
- package/dist/slash-commands.test.d.ts.map +1 -0
- package/dist/slash-commands.test.js +22 -0
- package/dist/slash-commands.test.js.map +1 -0
- package/dist/tools/apply-patch.d.ts +2 -2
- package/dist/tools/apply-patch.d.ts.map +1 -1
- package/dist/tools/apply-patch.js +2 -6
- package/dist/tools/apply-patch.js.map +1 -1
- package/dist/tools/apply-patch.test.js +2 -13
- package/dist/tools/apply-patch.test.js.map +1 -1
- package/dist/tools/ask-mentor.d.ts +2 -2
- package/dist/tools/ask-mentor.d.ts.map +1 -1
- package/dist/tools/ask-mentor.js.map +1 -1
- package/dist/tools/code-context.d.ts +3 -3
- package/dist/tools/code-context.d.ts.map +1 -1
- package/dist/tools/code-context.js.map +1 -1
- package/dist/tools/code-context.test.js +2 -2
- package/dist/tools/code-context.test.js.map +1 -1
- package/dist/tools/command-message-formatters.d.ts +5 -0
- package/dist/tools/command-message-formatters.d.ts.map +1 -0
- package/dist/tools/command-message-formatters.js +13 -0
- package/dist/tools/command-message-formatters.js.map +1 -0
- package/dist/tools/command-message-formatters.test.d.ts +2 -0
- package/dist/tools/command-message-formatters.test.d.ts.map +1 -0
- package/dist/tools/command-message-formatters.test.js +23 -0
- package/dist/tools/command-message-formatters.test.js.map +1 -0
- package/dist/tools/create-file.d.ts +2 -2
- package/dist/tools/create-file.d.ts.map +1 -1
- package/dist/tools/create-file.js +14 -9
- package/dist/tools/create-file.js.map +1 -1
- package/dist/tools/create-file.test.js +13 -13
- package/dist/tools/create-file.test.js.map +1 -1
- package/dist/tools/edit-healing.d.ts +3 -3
- package/dist/tools/edit-healing.d.ts.map +1 -1
- package/dist/tools/edit-healing.js.map +1 -1
- package/dist/tools/edit-healing.test.js +0 -1
- package/dist/tools/edit-healing.test.js.map +1 -1
- package/dist/tools/file-locks.d.ts +1 -0
- package/dist/tools/file-locks.d.ts.map +1 -1
- package/dist/tools/file-locks.js +16 -0
- package/dist/tools/file-locks.js.map +1 -1
- package/dist/tools/find-files.d.ts +2 -2
- package/dist/tools/find-files.d.ts.map +1 -1
- package/dist/tools/find-files.js.map +1 -1
- package/dist/tools/find-files.test.js +1 -1
- package/dist/tools/find-files.test.js.map +1 -1
- package/dist/tools/grep.d.ts +3 -2
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +65 -4
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/grep.test.d.ts +2 -0
- package/dist/tools/grep.test.d.ts.map +1 -0
- package/dist/tools/grep.test.js +39 -0
- package/dist/tools/grep.test.js.map +1 -0
- package/dist/tools/read-file.d.ts +3 -2
- package/dist/tools/read-file.d.ts.map +1 -1
- package/dist/tools/read-file.js +7 -5
- package/dist/tools/read-file.js.map +1 -1
- package/dist/tools/read-file.test.js +2 -2
- package/dist/tools/read-file.test.js.map +1 -1
- package/dist/tools/run-subagent.d.ts +14 -0
- package/dist/tools/run-subagent.d.ts.map +1 -0
- package/dist/tools/run-subagent.js +200 -0
- package/dist/tools/run-subagent.js.map +1 -0
- package/dist/tools/run-subagent.test.d.ts +2 -0
- package/dist/tools/run-subagent.test.d.ts.map +1 -0
- package/dist/tools/run-subagent.test.js +206 -0
- package/dist/tools/run-subagent.test.js.map +1 -0
- package/dist/tools/search-replace.d.ts +15 -7
- package/dist/tools/search-replace.d.ts.map +1 -1
- package/dist/tools/search-replace.js +93 -119
- package/dist/tools/search-replace.js.map +1 -1
- package/dist/tools/search-replace.test.js +330 -217
- package/dist/tools/search-replace.test.js.map +1 -1
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/shell.d.ts +3 -2
- package/dist/tools/shell.d.ts.map +1 -1
- package/dist/tools/shell.js +14 -6
- package/dist/tools/shell.js.map +1 -1
- package/dist/tools/shell.test.js +189 -11
- package/dist/tools/shell.test.js.map +1 -1
- package/dist/tools/tool-parameter-schema.test.js +13 -4
- package/dist/tools/tool-parameter-schema.test.js.map +1 -1
- package/dist/tools/types.d.ts +3 -2
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/utils.d.ts.map +1 -1
- package/dist/tools/utils.js +11 -1
- package/dist/tools/utils.js.map +1 -1
- package/dist/tools/utils.test.js +13 -0
- package/dist/tools/utils.test.js.map +1 -1
- package/dist/tools/web-fetch.d.ts +2 -2
- package/dist/tools/web-fetch.d.ts.map +1 -1
- package/dist/tools/web-fetch.js +29 -4
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/tools/web-fetch.test.js +64 -0
- package/dist/tools/web-fetch.test.js.map +1 -1
- package/dist/tools/web-search.d.ts +2 -2
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js.map +1 -1
- package/dist/utils/clipboard.d.ts +2 -2
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +58 -22
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/clipboard.test.js +25 -6
- package/dist/utils/clipboard.test.js.map +1 -1
- package/dist/utils/command-safety/handlers/sed-handler.d.ts.map +1 -1
- package/dist/utils/command-safety/handlers/sed-handler.js +10 -7
- package/dist/utils/command-safety/handlers/sed-handler.js.map +1 -1
- package/dist/utils/command-safety/index.d.ts.map +1 -1
- package/dist/utils/command-safety/index.js +153 -117
- package/dist/utils/command-safety/index.js.map +1 -1
- package/dist/utils/command-safety/path-analysis.d.ts.map +1 -1
- package/dist/utils/command-safety/path-analysis.js +6 -2
- package/dist/utils/command-safety/path-analysis.js.map +1 -1
- package/dist/utils/command-safety/utils.js +2 -2
- package/dist/utils/command-safety/utils.js.map +1 -1
- package/dist/utils/command-safety.path.test.js +15 -1
- package/dist/utils/command-safety.path.test.js.map +1 -1
- package/dist/utils/conversation-event-handler.d.ts +15 -0
- package/dist/utils/conversation-event-handler.d.ts.map +1 -1
- package/dist/utils/conversation-event-handler.js +108 -8
- package/dist/utils/conversation-event-handler.js.map +1 -1
- package/dist/utils/conversation-event-handler.test.js +207 -2
- package/dist/utils/conversation-event-handler.test.js.map +1 -1
- package/dist/utils/conversation-utils.d.ts +1 -0
- package/dist/utils/conversation-utils.d.ts.map +1 -1
- package/dist/utils/conversation-utils.js +1 -0
- package/dist/utils/conversation-utils.js.map +1 -1
- package/dist/utils/execute-shell.d.ts +2 -0
- package/dist/utils/execute-shell.d.ts.map +1 -1
- package/dist/utils/execute-shell.js +25 -1
- package/dist/utils/execute-shell.js.map +1 -1
- package/dist/utils/execute-shell.test.js +22 -0
- package/dist/utils/execute-shell.test.js.map +1 -1
- package/dist/utils/extract-command-messages.d.ts.map +1 -1
- package/dist/utils/extract-command-messages.js +2 -21
- package/dist/utils/extract-command-messages.js.map +1 -1
- package/dist/utils/model-settings.d.ts.map +1 -1
- package/dist/utils/model-settings.js +18 -0
- package/dist/utils/model-settings.js.map +1 -1
- package/dist/utils/output-trim.d.ts.map +1 -1
- package/dist/utils/output-trim.js +14 -6
- package/dist/utils/output-trim.js.map +1 -1
- package/dist/utils/streaming-session-factory.d.ts.map +1 -1
- package/dist/utils/streaming-session-factory.js +16 -1
- package/dist/utils/streaming-session-factory.js.map +1 -1
- package/dist/utils/streaming-session-factory.test.js +42 -0
- package/dist/utils/streaming-session-factory.test.js.map +1 -1
- package/dist/utils/token-usage.d.ts +13 -0
- package/dist/utils/token-usage.d.ts.map +1 -1
- package/dist/utils/token-usage.js +92 -1
- package/dist/utils/token-usage.js.map +1 -1
- package/dist/utils/token-usage.test.js +93 -1
- package/dist/utils/token-usage.test.js.map +1 -1
- package/package.json +4 -4
- package/readme.md +271 -44
|
@@ -35,68 +35,80 @@ function createTool(settingsService = createMockSettingsService(), editHealing)
|
|
|
35
35
|
...(editHealing ? { editHealing } : {}),
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
|
-
test.serial('needsApproval auto-approves creation when search_content is empty and file is missing
|
|
38
|
+
test.serial('needsApproval auto-approves creation when search_content is empty and file is missing', async (t) => {
|
|
39
39
|
await withTempDir(async () => {
|
|
40
|
-
const tool = createTool(createMockSettingsService(
|
|
40
|
+
const tool = createTool(createMockSettingsService());
|
|
41
41
|
const filePath = 'new-file.txt';
|
|
42
42
|
const result = await tool.needsApproval({
|
|
43
43
|
path: filePath,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
replacements: [
|
|
45
|
+
{
|
|
46
|
+
search_content: '',
|
|
47
|
+
replace_content: 'initial content',
|
|
48
|
+
},
|
|
49
|
+
],
|
|
47
50
|
});
|
|
48
51
|
t.false(result);
|
|
49
52
|
});
|
|
50
53
|
});
|
|
51
|
-
test.serial('needsApproval auto-approves a unique exact match
|
|
54
|
+
test.serial('needsApproval auto-approves a unique exact match', async (t) => {
|
|
52
55
|
await withTempDir(async (dir) => {
|
|
53
|
-
const tool = createTool(createMockSettingsService(
|
|
56
|
+
const tool = createTool(createMockSettingsService());
|
|
54
57
|
const filePath = 'sample.txt';
|
|
55
58
|
const absPath = path.join(dir, filePath);
|
|
56
59
|
await fs.writeFile(absPath, 'hello world');
|
|
57
60
|
const result = await tool.needsApproval({
|
|
58
61
|
path: filePath,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
replacements: [
|
|
63
|
+
{
|
|
64
|
+
search_content: 'hello',
|
|
65
|
+
replace_content: 'hi',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
62
68
|
});
|
|
63
69
|
t.false(result);
|
|
64
70
|
});
|
|
65
71
|
});
|
|
66
|
-
test.serial('needsApproval
|
|
72
|
+
test.serial('needsApproval auto-approves when multiple exact matches are found', async (t) => {
|
|
67
73
|
await withTempDir(async (dir) => {
|
|
68
|
-
const tool = createTool(createMockSettingsService(
|
|
74
|
+
const tool = createTool(createMockSettingsService());
|
|
69
75
|
const filePath = 'sample.txt';
|
|
70
76
|
const absPath = path.join(dir, filePath);
|
|
71
77
|
await fs.writeFile(absPath, 'hello world hello');
|
|
72
78
|
const result = await tool.needsApproval({
|
|
73
79
|
path: filePath,
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
80
|
+
replacements: [
|
|
81
|
+
{
|
|
82
|
+
search_content: 'hello',
|
|
83
|
+
replace_content: 'hi',
|
|
84
|
+
},
|
|
85
|
+
],
|
|
77
86
|
});
|
|
78
|
-
t.
|
|
87
|
+
t.false(result);
|
|
79
88
|
});
|
|
80
89
|
});
|
|
81
|
-
test.serial('execute replaces only
|
|
90
|
+
test.serial('execute replaces only a unique exact match', async (t) => {
|
|
82
91
|
await withTempDir(async (dir) => {
|
|
83
92
|
const tool = createTool();
|
|
84
93
|
const filePath = 'content.txt';
|
|
85
94
|
const absPath = path.join(dir, filePath);
|
|
86
|
-
await fs.writeFile(absPath, 'target before
|
|
95
|
+
await fs.writeFile(absPath, 'target before unique after');
|
|
87
96
|
const result = await tool.execute({
|
|
88
97
|
path: filePath,
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
98
|
+
replacements: [
|
|
99
|
+
{
|
|
100
|
+
search_content: 'target',
|
|
101
|
+
replace_content: 'done',
|
|
102
|
+
},
|
|
103
|
+
],
|
|
92
104
|
});
|
|
93
105
|
const parsed = JSON.parse(result);
|
|
94
106
|
t.true(parsed.output[0].success);
|
|
95
107
|
const updated = await fs.readFile(absPath, 'utf8');
|
|
96
|
-
t.is(updated, 'done before
|
|
108
|
+
t.is(updated, 'done before unique after');
|
|
97
109
|
});
|
|
98
110
|
});
|
|
99
|
-
test.serial('execute
|
|
111
|
+
test.serial('execute fails when multiple exact matches are found', async (t) => {
|
|
100
112
|
await withTempDir(async (dir) => {
|
|
101
113
|
const tool = createTool();
|
|
102
114
|
const filePath = 'content.txt';
|
|
@@ -104,14 +116,18 @@ test.serial('execute replaces all exact matches when replace_all is true', async
|
|
|
104
116
|
await fs.writeFile(absPath, 'foo foo foo');
|
|
105
117
|
const result = await tool.execute({
|
|
106
118
|
path: filePath,
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
119
|
+
replacements: [
|
|
120
|
+
{
|
|
121
|
+
search_content: 'foo',
|
|
122
|
+
replace_content: 'bar',
|
|
123
|
+
},
|
|
124
|
+
],
|
|
110
125
|
});
|
|
111
126
|
const parsed = JSON.parse(result);
|
|
112
|
-
t.
|
|
127
|
+
t.false(parsed.output[0].success);
|
|
128
|
+
t.regex(parsed.output[0].error, /Found 3 exact matches/);
|
|
113
129
|
const updated = await fs.readFile(absPath, 'utf8');
|
|
114
|
-
t.is(updated, '
|
|
130
|
+
t.is(updated, 'foo foo foo');
|
|
115
131
|
});
|
|
116
132
|
});
|
|
117
133
|
test.serial('execute performs relaxed match replacement when exact match is not found', async (t) => {
|
|
@@ -122,9 +138,12 @@ test.serial('execute performs relaxed match replacement when exact match is not
|
|
|
122
138
|
await fs.writeFile(absPath, ' line one\n\tline two\nremainder');
|
|
123
139
|
const result = await tool.execute({
|
|
124
140
|
path: filePath,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
141
|
+
replacements: [
|
|
142
|
+
{
|
|
143
|
+
search_content: 'line one\nline two',
|
|
144
|
+
replace_content: 'new block\n',
|
|
145
|
+
},
|
|
146
|
+
],
|
|
128
147
|
});
|
|
129
148
|
const parsed = JSON.parse(result);
|
|
130
149
|
t.true(parsed.output[0].success);
|
|
@@ -132,25 +151,7 @@ test.serial('execute performs relaxed match replacement when exact match is not
|
|
|
132
151
|
t.is(updated, 'new block\nremainder');
|
|
133
152
|
});
|
|
134
153
|
});
|
|
135
|
-
test.serial('execute
|
|
136
|
-
await withTempDir(async (dir) => {
|
|
137
|
-
const tool = createTool();
|
|
138
|
-
const filePath = 'content.txt';
|
|
139
|
-
const absPath = path.join(dir, filePath);
|
|
140
|
-
await fs.writeFile(absPath, 'foo\nbar\n---\nfoo\nbar\n');
|
|
141
|
-
const result = await tool.execute({
|
|
142
|
-
path: filePath,
|
|
143
|
-
search_content: 'foo\nbar',
|
|
144
|
-
replace_content: 'replacement',
|
|
145
|
-
replace_all: false,
|
|
146
|
-
});
|
|
147
|
-
const parsed = JSON.parse(result);
|
|
148
|
-
t.true(parsed.output[0].success);
|
|
149
|
-
const updated = await fs.readFile(absPath, 'utf8');
|
|
150
|
-
t.is(updated, 'replacement\n---\nfoo\nbar\n');
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
test.serial('execute rejects multiple relaxed matches when replace_all is false', async (t) => {
|
|
154
|
+
test.serial('execute fails when multiple relaxed matches are found', async (t) => {
|
|
154
155
|
await withTempDir(async (dir) => {
|
|
155
156
|
const tool = createTool();
|
|
156
157
|
const filePath = 'content.txt';
|
|
@@ -158,36 +159,20 @@ test.serial('execute rejects multiple relaxed matches when replace_all is false'
|
|
|
158
159
|
await fs.writeFile(absPath, ' foo \n\tbar\n---\n foo \n\tbar\n');
|
|
159
160
|
const result = await tool.execute({
|
|
160
161
|
path: filePath,
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
162
|
+
replacements: [
|
|
163
|
+
{
|
|
164
|
+
search_content: 'foo\nbar',
|
|
165
|
+
replace_content: 'replacement',
|
|
166
|
+
},
|
|
167
|
+
],
|
|
164
168
|
});
|
|
165
169
|
const parsed = JSON.parse(result);
|
|
166
170
|
t.false(parsed.output[0].success);
|
|
167
|
-
t.true(parsed.output[0].error.includes('relaxed matches'));
|
|
171
|
+
t.true(parsed.output[0].error.includes('relaxed matches') || parsed.output[0].error.includes('Found 2 relaxed matches'));
|
|
168
172
|
const unchanged = await fs.readFile(absPath, 'utf8');
|
|
169
173
|
t.is(unchanged, ' foo \n\tbar\n---\n foo \n\tbar\n');
|
|
170
174
|
});
|
|
171
175
|
});
|
|
172
|
-
test.serial('execute replaces all relaxed matches when replace_all is true', async (t) => {
|
|
173
|
-
await withTempDir(async (dir) => {
|
|
174
|
-
const tool = createTool(createMockSettingsService({ 'tools.enableEditHealing': false }));
|
|
175
|
-
const filePath = 'content.txt';
|
|
176
|
-
const absPath = path.join(dir, filePath);
|
|
177
|
-
await fs.writeFile(absPath, ' foo \n\tbar\n---\n foo \n\tbar\n');
|
|
178
|
-
const result = await tool.execute({
|
|
179
|
-
path: filePath,
|
|
180
|
-
search_content: 'foo\nbar',
|
|
181
|
-
replace_content: 'replacement',
|
|
182
|
-
replace_all: true,
|
|
183
|
-
});
|
|
184
|
-
const parsed = JSON.parse(result);
|
|
185
|
-
t.true(parsed.output[0].success);
|
|
186
|
-
t.true(parsed.output[0].message.includes('relaxed'));
|
|
187
|
-
const updated = await fs.readFile(absPath, 'utf8');
|
|
188
|
-
t.is(updated, 'replacement---\nreplacement');
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
176
|
test.serial('execute creates a new file when search_content is empty and file is missing', async (t) => {
|
|
192
177
|
await withTempDir(async (dir) => {
|
|
193
178
|
const tool = createTool();
|
|
@@ -195,9 +180,12 @@ test.serial('execute creates a new file when search_content is empty and file is
|
|
|
195
180
|
const absPath = path.join(dir, filePath);
|
|
196
181
|
const result = await tool.execute({
|
|
197
182
|
path: filePath,
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
183
|
+
replacements: [
|
|
184
|
+
{
|
|
185
|
+
search_content: '',
|
|
186
|
+
replace_content: 'new content',
|
|
187
|
+
},
|
|
188
|
+
],
|
|
201
189
|
});
|
|
202
190
|
const parsed = JSON.parse(result);
|
|
203
191
|
t.true(parsed.output[0].success);
|
|
@@ -210,13 +198,16 @@ test.serial('execute preserves parallel edits to different regions of the same f
|
|
|
210
198
|
const tool = createTool(createMockSettingsService({ 'tools.enableEditHealing': false }));
|
|
211
199
|
const filePath = 'parallel.txt';
|
|
212
200
|
const absPath = path.join(dir, filePath);
|
|
213
|
-
const tokens = Array.from({ length: 20 }, (_, index) => `token_${index}`);
|
|
201
|
+
const tokens = Array.from({ length: 20 }, (_, index) => `token_${String(index).padStart(2, '0')}`);
|
|
214
202
|
await fs.writeFile(absPath, tokens.join('\n'));
|
|
215
203
|
const results = await Promise.all(tokens.map((token, index) => tool.execute({
|
|
216
204
|
path: filePath,
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
205
|
+
replacements: [
|
|
206
|
+
{
|
|
207
|
+
search_content: token,
|
|
208
|
+
replace_content: `done_${index}`,
|
|
209
|
+
},
|
|
210
|
+
],
|
|
220
211
|
})));
|
|
221
212
|
for (const result of results) {
|
|
222
213
|
const parsed = JSON.parse(result);
|
|
@@ -233,18 +224,15 @@ test.serial('execute applies batched replacements to one file with a single resu
|
|
|
233
224
|
const absPath = path.join(dir, filePath);
|
|
234
225
|
await fs.writeFile(absPath, 'alpha\nbeta\ngamma\n');
|
|
235
226
|
const result = await tool.execute({
|
|
227
|
+
path: filePath,
|
|
236
228
|
replacements: [
|
|
237
229
|
{
|
|
238
|
-
path: filePath,
|
|
239
230
|
search_content: 'alpha',
|
|
240
231
|
replace_content: 'ALPHA',
|
|
241
|
-
replace_all: false,
|
|
242
232
|
},
|
|
243
233
|
{
|
|
244
|
-
path: filePath,
|
|
245
234
|
search_content: 'gamma',
|
|
246
235
|
replace_content: 'GAMMA',
|
|
247
|
-
replace_all: false,
|
|
248
236
|
},
|
|
249
237
|
],
|
|
250
238
|
});
|
|
@@ -263,24 +251,19 @@ test.serial('execute keeps successful batched replacements when another replacem
|
|
|
263
251
|
const originalContent = 'alpha\nbeta\ngamma\n';
|
|
264
252
|
await fs.writeFile(absPath, originalContent);
|
|
265
253
|
const result = await tool.execute({
|
|
254
|
+
path: filePath,
|
|
266
255
|
replacements: [
|
|
267
256
|
{
|
|
268
|
-
path: filePath,
|
|
269
257
|
search_content: 'alpha',
|
|
270
258
|
replace_content: 'ALPHA',
|
|
271
|
-
replace_all: false,
|
|
272
259
|
},
|
|
273
260
|
{
|
|
274
|
-
path: filePath,
|
|
275
261
|
search_content: 'missing',
|
|
276
262
|
replace_content: 'MISSING',
|
|
277
|
-
replace_all: false,
|
|
278
263
|
},
|
|
279
264
|
{
|
|
280
|
-
path: filePath,
|
|
281
265
|
search_content: 'gamma',
|
|
282
266
|
replace_content: 'GAMMA',
|
|
283
|
-
replace_all: false,
|
|
284
267
|
},
|
|
285
268
|
],
|
|
286
269
|
});
|
|
@@ -301,9 +284,12 @@ test.serial('execute reports failure when search string is not found', async (t)
|
|
|
301
284
|
await fs.writeFile(absPath, originalContent);
|
|
302
285
|
const result = await tool.execute({
|
|
303
286
|
path: filePath,
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
287
|
+
replacements: [
|
|
288
|
+
{
|
|
289
|
+
search_content: 'nonexistent',
|
|
290
|
+
replace_content: 'replacement',
|
|
291
|
+
},
|
|
292
|
+
],
|
|
307
293
|
});
|
|
308
294
|
const parsed = JSON.parse(result);
|
|
309
295
|
t.false(parsed.output[0].success);
|
|
@@ -326,9 +312,12 @@ test.serial('execute heals search content when no match is found', async (t) =>
|
|
|
326
312
|
}));
|
|
327
313
|
const result = await tool.execute({
|
|
328
314
|
path: filePath,
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
315
|
+
replacements: [
|
|
316
|
+
{
|
|
317
|
+
search_content: 'const foo = 2;\n',
|
|
318
|
+
replace_content: 'const foo = 3;\n',
|
|
319
|
+
},
|
|
320
|
+
],
|
|
332
321
|
});
|
|
333
322
|
const parsed = JSON.parse(result);
|
|
334
323
|
t.true(parsed.output[0].success);
|
|
@@ -350,9 +339,12 @@ test.serial('execute includes auto-healing failure reason when healing does not
|
|
|
350
339
|
}));
|
|
351
340
|
const result = await tool.execute({
|
|
352
341
|
path: filePath,
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
342
|
+
replacements: [
|
|
343
|
+
{
|
|
344
|
+
search_content: 'const foo = 2;\n',
|
|
345
|
+
replace_content: 'const foo = 3;\n',
|
|
346
|
+
},
|
|
347
|
+
],
|
|
356
348
|
});
|
|
357
349
|
const parsed = JSON.parse(result);
|
|
358
350
|
t.false(parsed.output[0].success);
|
|
@@ -369,9 +361,12 @@ test.serial('execute treats special regex characters literally', async (t) => {
|
|
|
369
361
|
await fs.writeFile(absPath, originalContent);
|
|
370
362
|
const result = await tool.execute({
|
|
371
363
|
path: filePath,
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
364
|
+
replacements: [
|
|
365
|
+
{
|
|
366
|
+
search_content: '[1, 2, 3]',
|
|
367
|
+
replace_content: '[4, 5, 6]',
|
|
368
|
+
},
|
|
369
|
+
],
|
|
375
370
|
});
|
|
376
371
|
const parsed = JSON.parse(result);
|
|
377
372
|
t.true(parsed.output[0].success);
|
|
@@ -387,9 +382,12 @@ test.serial('execute deletes content when replacement is empty string', async (t
|
|
|
387
382
|
await fs.writeFile(absPath, 'before DELETE_ME after');
|
|
388
383
|
const result = await tool.execute({
|
|
389
384
|
path: filePath,
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
385
|
+
replacements: [
|
|
386
|
+
{
|
|
387
|
+
search_content: 'DELETE_ME ',
|
|
388
|
+
replace_content: '',
|
|
389
|
+
},
|
|
390
|
+
],
|
|
393
391
|
});
|
|
394
392
|
const parsed = JSON.parse(result);
|
|
395
393
|
t.true(parsed.output[0].success);
|
|
@@ -405,9 +403,12 @@ test.serial('execute performs exact multi-line match without whitespace normaliz
|
|
|
405
403
|
await fs.writeFile(absPath, 'line one\nline two\nline three');
|
|
406
404
|
const result = await tool.execute({
|
|
407
405
|
path: filePath,
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
406
|
+
replacements: [
|
|
407
|
+
{
|
|
408
|
+
search_content: 'line one\nline two',
|
|
409
|
+
replace_content: 'new content',
|
|
410
|
+
},
|
|
411
|
+
],
|
|
411
412
|
});
|
|
412
413
|
const parsed = JSON.parse(result);
|
|
413
414
|
t.true(parsed.output[0].success);
|
|
@@ -423,9 +424,12 @@ test.serial('execute handles leading/trailing whitespace differences in relaxed
|
|
|
423
424
|
await fs.writeFile(absPath, ' foo \nbar');
|
|
424
425
|
const result = await tool.execute({
|
|
425
426
|
path: filePath,
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
427
|
+
replacements: [
|
|
428
|
+
{
|
|
429
|
+
search_content: 'foo\nbar',
|
|
430
|
+
replace_content: 'replaced',
|
|
431
|
+
},
|
|
432
|
+
],
|
|
429
433
|
});
|
|
430
434
|
const parsed = JSON.parse(result);
|
|
431
435
|
t.true(parsed.output[0].success);
|
|
@@ -441,9 +445,12 @@ test.serial('execute performs normalized whitespace match across line breaks', a
|
|
|
441
445
|
await fs.writeFile(absPath, 'const foo = 1;\nconst bar = 2;\nconst baz = 3;\n');
|
|
442
446
|
const result = await tool.execute({
|
|
443
447
|
path: filePath,
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
448
|
+
replacements: [
|
|
449
|
+
{
|
|
450
|
+
search_content: 'const foo = 1; const bar = 2;',
|
|
451
|
+
replace_content: 'const foo = 1;\nconst bar = 42;',
|
|
452
|
+
},
|
|
453
|
+
],
|
|
447
454
|
});
|
|
448
455
|
const parsed = JSON.parse(result);
|
|
449
456
|
t.true(parsed.output[0].success);
|
|
@@ -451,7 +458,7 @@ test.serial('execute performs normalized whitespace match across line breaks', a
|
|
|
451
458
|
t.is(updated, 'const foo = 1;\nconst bar = 42;\nconst baz = 3;\n');
|
|
452
459
|
});
|
|
453
460
|
});
|
|
454
|
-
test.serial('execute rejects multiple normalized matches
|
|
461
|
+
test.serial('execute rejects multiple normalized matches', async (t) => {
|
|
455
462
|
await withTempDir(async (dir) => {
|
|
456
463
|
const tool = createTool(createMockSettingsService({ 'tools.enableEditHealing': false }));
|
|
457
464
|
const filePath = 'normalized-multi.txt';
|
|
@@ -459,9 +466,12 @@ test.serial('execute rejects multiple normalized matches when replace_all is fal
|
|
|
459
466
|
await fs.writeFile(absPath, 'const foo = 1;\n const bar = 2;\n---\nconst foo = 1;\tconst bar = 2;\n');
|
|
460
467
|
const result = await tool.execute({
|
|
461
468
|
path: filePath,
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
469
|
+
replacements: [
|
|
470
|
+
{
|
|
471
|
+
search_content: 'const foo = 1; const bar = 2;',
|
|
472
|
+
replace_content: 'const foo = 9; const bar = 9;',
|
|
473
|
+
},
|
|
474
|
+
],
|
|
465
475
|
});
|
|
466
476
|
const parsed = JSON.parse(result);
|
|
467
477
|
t.false(parsed.output[0].success);
|
|
@@ -477,14 +487,14 @@ test.serial('execute does not match substrings in relaxed mode', async (t) => {
|
|
|
477
487
|
const absPath = path.join(dir, filePath);
|
|
478
488
|
const originalContent = ' formatted text ';
|
|
479
489
|
await fs.writeFile(absPath, originalContent);
|
|
480
|
-
// Search for "format" with different whitespace to trigger relaxed mode
|
|
481
|
-
// Relaxed mode should NOT match because it compares entire trimmed lines,
|
|
482
|
-
// and "format" (trimmed) !== "formatted text" (trimmed)
|
|
483
490
|
const result = await tool.execute({
|
|
484
491
|
path: filePath,
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
492
|
+
replacements: [
|
|
493
|
+
{
|
|
494
|
+
search_content: ' format ',
|
|
495
|
+
replace_content: 'replacement',
|
|
496
|
+
},
|
|
497
|
+
],
|
|
488
498
|
});
|
|
489
499
|
const parsed = JSON.parse(result);
|
|
490
500
|
t.false(parsed.output[0].success);
|
|
@@ -492,22 +502,20 @@ test.serial('execute does not match substrings in relaxed mode', async (t) => {
|
|
|
492
502
|
t.is(unchanged, originalContent);
|
|
493
503
|
});
|
|
494
504
|
});
|
|
495
|
-
// ============================================================================
|
|
496
|
-
// EOL Normalization Tests
|
|
497
|
-
// ============================================================================
|
|
498
505
|
test.serial('execute normalizes CRLF search content to match LF file', async (t) => {
|
|
499
506
|
await withTempDir(async (dir) => {
|
|
500
507
|
const tool = createTool();
|
|
501
508
|
const filePath = 'lf-file.txt';
|
|
502
509
|
const absPath = path.join(dir, filePath);
|
|
503
|
-
// File has LF line endings
|
|
504
510
|
await fs.writeFile(absPath, 'line one\nline two\nline three');
|
|
505
|
-
// Search content has CRLF line endings
|
|
506
511
|
const result = await tool.execute({
|
|
507
512
|
path: filePath,
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
513
|
+
replacements: [
|
|
514
|
+
{
|
|
515
|
+
search_content: 'line one\r\nline two',
|
|
516
|
+
replace_content: 'replaced',
|
|
517
|
+
},
|
|
518
|
+
],
|
|
511
519
|
});
|
|
512
520
|
const parsed = JSON.parse(result);
|
|
513
521
|
t.true(parsed.output[0].success);
|
|
@@ -520,19 +528,19 @@ test.serial('execute normalizes LF search content to match CRLF file', async (t)
|
|
|
520
528
|
const tool = createTool();
|
|
521
529
|
const filePath = 'crlf-file.txt';
|
|
522
530
|
const absPath = path.join(dir, filePath);
|
|
523
|
-
// File has CRLF line endings
|
|
524
531
|
await fs.writeFile(absPath, 'line one\r\nline two\r\nline three');
|
|
525
|
-
// Search content has LF line endings
|
|
526
532
|
const result = await tool.execute({
|
|
527
533
|
path: filePath,
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
534
|
+
replacements: [
|
|
535
|
+
{
|
|
536
|
+
search_content: 'line one\nline two',
|
|
537
|
+
replace_content: 'replaced',
|
|
538
|
+
},
|
|
539
|
+
],
|
|
531
540
|
});
|
|
532
541
|
const parsed = JSON.parse(result);
|
|
533
542
|
t.true(parsed.output[0].success);
|
|
534
543
|
const updated = await fs.readFile(absPath, 'utf8');
|
|
535
|
-
// Result should preserve the file's CRLF style
|
|
536
544
|
t.is(updated, 'replaced\r\nline three');
|
|
537
545
|
});
|
|
538
546
|
});
|
|
@@ -541,36 +549,36 @@ test.serial('execute preserves CRLF in replacement content when file uses CRLF',
|
|
|
541
549
|
const tool = createTool();
|
|
542
550
|
const filePath = 'crlf-file.txt';
|
|
543
551
|
const absPath = path.join(dir, filePath);
|
|
544
|
-
// File has CRLF line endings
|
|
545
552
|
await fs.writeFile(absPath, 'hello\r\nworld');
|
|
546
553
|
const result = await tool.execute({
|
|
547
554
|
path: filePath,
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
555
|
+
replacements: [
|
|
556
|
+
{
|
|
557
|
+
search_content: 'hello',
|
|
558
|
+
replace_content: 'new\nline',
|
|
559
|
+
},
|
|
560
|
+
],
|
|
551
561
|
});
|
|
552
562
|
const parsed = JSON.parse(result);
|
|
553
563
|
t.true(parsed.output[0].success);
|
|
554
564
|
const updated = await fs.readFile(absPath, 'utf8');
|
|
555
|
-
// Replacement should use file's CRLF style
|
|
556
565
|
t.is(updated, 'new\r\nline\r\nworld');
|
|
557
566
|
});
|
|
558
567
|
});
|
|
559
|
-
// ============================================================================
|
|
560
|
-
// Leading Filepath Comment Stripping Tests
|
|
561
|
-
// ============================================================================
|
|
562
568
|
test.serial('execute strips leading filepath comment from search content', async (t) => {
|
|
563
569
|
await withTempDir(async (dir) => {
|
|
564
570
|
const tool = createTool();
|
|
565
571
|
const filePath = 'sample.ts';
|
|
566
572
|
const absPath = path.join(dir, filePath);
|
|
567
573
|
await fs.writeFile(absPath, 'const x = 1;\nconst y = 2;');
|
|
568
|
-
// Search content has a leading filepath comment (common model behavior)
|
|
569
574
|
const result = await tool.execute({
|
|
570
575
|
path: filePath,
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
576
|
+
replacements: [
|
|
577
|
+
{
|
|
578
|
+
search_content: '// sample.ts\nconst x = 1;',
|
|
579
|
+
replace_content: 'const x = 42;',
|
|
580
|
+
},
|
|
581
|
+
],
|
|
574
582
|
});
|
|
575
583
|
const parsed = JSON.parse(result);
|
|
576
584
|
t.true(parsed.output[0].success);
|
|
@@ -586,9 +594,12 @@ test.serial('execute strips leading hash filepath comment from search content',
|
|
|
586
594
|
await fs.writeFile(absPath, 'x = 1\ny = 2');
|
|
587
595
|
const result = await tool.execute({
|
|
588
596
|
path: filePath,
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
597
|
+
replacements: [
|
|
598
|
+
{
|
|
599
|
+
search_content: '# script.py\nx = 1',
|
|
600
|
+
replace_content: 'x = 42',
|
|
601
|
+
},
|
|
602
|
+
],
|
|
592
603
|
});
|
|
593
604
|
const parsed = JSON.parse(result);
|
|
594
605
|
t.true(parsed.output[0].success);
|
|
@@ -602,12 +613,14 @@ test.serial('execute does not strip non-filepath leading comments', async (t) =>
|
|
|
602
613
|
const filePath = 'code.ts';
|
|
603
614
|
const absPath = path.join(dir, filePath);
|
|
604
615
|
await fs.writeFile(absPath, '// TODO: fix this\nconst x = 1;');
|
|
605
|
-
// This comment is not a filepath, so it should NOT be stripped
|
|
606
616
|
const result = await tool.execute({
|
|
607
617
|
path: filePath,
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
618
|
+
replacements: [
|
|
619
|
+
{
|
|
620
|
+
search_content: '// TODO: fix this\nconst x = 1;',
|
|
621
|
+
replace_content: 'const x = 42;',
|
|
622
|
+
},
|
|
623
|
+
],
|
|
611
624
|
});
|
|
612
625
|
const parsed = JSON.parse(result);
|
|
613
626
|
t.true(parsed.output[0].success);
|
|
@@ -615,9 +628,6 @@ test.serial('execute does not strip non-filepath leading comments', async (t) =>
|
|
|
615
628
|
t.is(updated, 'const x = 42;');
|
|
616
629
|
});
|
|
617
630
|
});
|
|
618
|
-
// ============================================================================
|
|
619
|
-
// Summarization Marker Detection Tests
|
|
620
|
-
// ============================================================================
|
|
621
631
|
test.serial('execute rejects search content with "Lines X-Y omitted" marker', async (t) => {
|
|
622
632
|
await withTempDir(async (dir) => {
|
|
623
633
|
const tool = createTool();
|
|
@@ -626,9 +636,12 @@ test.serial('execute rejects search content with "Lines X-Y omitted" marker', as
|
|
|
626
636
|
await fs.writeFile(absPath, 'line 1\nline 2\nline 3');
|
|
627
637
|
const result = await tool.execute({
|
|
628
638
|
path: filePath,
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
639
|
+
replacements: [
|
|
640
|
+
{
|
|
641
|
+
search_content: 'line 1\nLines 2-50 omitted\nline 3',
|
|
642
|
+
replace_content: 'replaced',
|
|
643
|
+
},
|
|
644
|
+
],
|
|
632
645
|
});
|
|
633
646
|
const parsed = JSON.parse(result);
|
|
634
647
|
t.false(parsed.output[0].success);
|
|
@@ -643,9 +656,12 @@ test.serial('execute rejects search content with ellipsis marker {…}', async (
|
|
|
643
656
|
await fs.writeFile(absPath, 'function foo() { /* code */ }');
|
|
644
657
|
const result = await tool.execute({
|
|
645
658
|
path: filePath,
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
659
|
+
replacements: [
|
|
660
|
+
{
|
|
661
|
+
search_content: 'function foo() {…}',
|
|
662
|
+
replace_content: 'replaced',
|
|
663
|
+
},
|
|
664
|
+
],
|
|
649
665
|
});
|
|
650
666
|
const parsed = JSON.parse(result);
|
|
651
667
|
t.false(parsed.output[0].success);
|
|
@@ -660,9 +676,12 @@ test.serial('execute rejects search content with /*...*/ marker', async (t) => {
|
|
|
660
676
|
await fs.writeFile(absPath, 'function foo() { return 1; }');
|
|
661
677
|
const result = await tool.execute({
|
|
662
678
|
path: filePath,
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
679
|
+
replacements: [
|
|
680
|
+
{
|
|
681
|
+
search_content: 'function foo() { /*...*/ }',
|
|
682
|
+
replace_content: 'replaced',
|
|
683
|
+
},
|
|
684
|
+
],
|
|
666
685
|
});
|
|
667
686
|
const parsed = JSON.parse(result);
|
|
668
687
|
t.false(parsed.output[0].success);
|
|
@@ -677,18 +696,56 @@ test.serial('execute rejects search content with // ... marker', async (t) => {
|
|
|
677
696
|
await fs.writeFile(absPath, 'const a = 1;\nconst b = 2;\nconst c = 3;');
|
|
678
697
|
const result = await tool.execute({
|
|
679
698
|
path: filePath,
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
699
|
+
replacements: [
|
|
700
|
+
{
|
|
701
|
+
search_content: 'const a = 1;\n// ...\nconst c = 3;',
|
|
702
|
+
replace_content: 'replaced',
|
|
703
|
+
},
|
|
704
|
+
],
|
|
683
705
|
});
|
|
684
706
|
const parsed = JSON.parse(result);
|
|
685
707
|
t.false(parsed.output[0].success);
|
|
686
708
|
t.true(parsed.output[0].error.includes('// ...'));
|
|
687
709
|
});
|
|
688
710
|
});
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
711
|
+
test.serial('execute rejects search content when search and replace content are identical', async (t) => {
|
|
712
|
+
await withTempDir(async (dir) => {
|
|
713
|
+
const tool = createTool();
|
|
714
|
+
const filePath = 'content.txt';
|
|
715
|
+
const absPath = path.join(dir, filePath);
|
|
716
|
+
await fs.writeFile(absPath, 'hello world');
|
|
717
|
+
const result = await tool.execute({
|
|
718
|
+
path: filePath,
|
|
719
|
+
replacements: [
|
|
720
|
+
{
|
|
721
|
+
search_content: 'hello',
|
|
722
|
+
replace_content: 'hello',
|
|
723
|
+
},
|
|
724
|
+
],
|
|
725
|
+
});
|
|
726
|
+
const parsed = JSON.parse(result);
|
|
727
|
+
t.false(parsed.output[0].success);
|
|
728
|
+
t.regex(parsed.output[0].error, /identical/i);
|
|
729
|
+
});
|
|
730
|
+
});
|
|
731
|
+
test.serial('needsApproval auto-approves when search and replace content are identical', async (t) => {
|
|
732
|
+
await withTempDir(async (dir) => {
|
|
733
|
+
const tool = createTool();
|
|
734
|
+
const filePath = 'content.txt';
|
|
735
|
+
const absPath = path.join(dir, filePath);
|
|
736
|
+
await fs.writeFile(absPath, 'hello world');
|
|
737
|
+
const result = await tool.needsApproval({
|
|
738
|
+
path: filePath,
|
|
739
|
+
replacements: [
|
|
740
|
+
{
|
|
741
|
+
search_content: 'hello',
|
|
742
|
+
replace_content: 'hello',
|
|
743
|
+
},
|
|
744
|
+
],
|
|
745
|
+
});
|
|
746
|
+
t.false(result);
|
|
747
|
+
});
|
|
748
|
+
});
|
|
692
749
|
test.serial('execute performs gap match: head <...> tail replaces entire region', async (t) => {
|
|
693
750
|
await withTempDir(async (dir) => {
|
|
694
751
|
const tool = createTool(createMockSettingsService({ 'tools.enableEditHealing': false }));
|
|
@@ -697,9 +754,12 @@ test.serial('execute performs gap match: head <...> tail replaces entire region'
|
|
|
697
754
|
await fs.writeFile(absPath, 'function foo() {\n const x = 1;\n const y = 2;\n const z = 3;\n return x + y + z;\n}\n');
|
|
698
755
|
const result = await tool.execute({
|
|
699
756
|
path: filePath,
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
757
|
+
replacements: [
|
|
758
|
+
{
|
|
759
|
+
search_content: 'function foo() {\n const x = 1;\n<...>\n return x + y + z;\n}',
|
|
760
|
+
replace_content: 'function foo() {\n return 6;\n}\n',
|
|
761
|
+
},
|
|
762
|
+
],
|
|
703
763
|
});
|
|
704
764
|
const parsed = JSON.parse(result);
|
|
705
765
|
t.true(parsed.output[0].success);
|
|
@@ -716,9 +776,12 @@ test.serial('execute performs gap match with single-line head and tail', async (
|
|
|
716
776
|
await fs.writeFile(absPath, 'start\nmiddle1\nmiddle2\nend\nafter');
|
|
717
777
|
const result = await tool.execute({
|
|
718
778
|
path: filePath,
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
779
|
+
replacements: [
|
|
780
|
+
{
|
|
781
|
+
search_content: 'start\n<...>\nend',
|
|
782
|
+
replace_content: 'replaced\n',
|
|
783
|
+
},
|
|
784
|
+
],
|
|
722
785
|
});
|
|
723
786
|
const parsed = JSON.parse(result);
|
|
724
787
|
t.true(parsed.output[0].success);
|
|
@@ -734,9 +797,12 @@ test.serial('execute performs gap match with adjacent head and tail (no actual g
|
|
|
734
797
|
await fs.writeFile(absPath, 'line1\nline2\nline3');
|
|
735
798
|
const result = await tool.execute({
|
|
736
799
|
path: filePath,
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
800
|
+
replacements: [
|
|
801
|
+
{
|
|
802
|
+
search_content: 'line1\n<...>\nline2',
|
|
803
|
+
replace_content: 'replaced\n',
|
|
804
|
+
},
|
|
805
|
+
],
|
|
740
806
|
});
|
|
741
807
|
const parsed = JSON.parse(result);
|
|
742
808
|
t.true(parsed.output[0].success);
|
|
@@ -752,9 +818,12 @@ test.serial('execute performs gap match with multiple <...> markers', async (t)
|
|
|
752
818
|
await fs.writeFile(absPath, 'function a() {\n body1;\n}\nfunction b() {\n body2;\n}\nfunction c() {\n body3;\n}\n');
|
|
753
819
|
const result = await tool.execute({
|
|
754
820
|
path: filePath,
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
821
|
+
replacements: [
|
|
822
|
+
{
|
|
823
|
+
search_content: 'function a() {\n<...>\nfunction b() {\n<...>\nfunction c() {\n body3;\n}',
|
|
824
|
+
replace_content: 'function abc() {\n combined;\n}\n',
|
|
825
|
+
},
|
|
826
|
+
],
|
|
758
827
|
});
|
|
759
828
|
const parsed = JSON.parse(result);
|
|
760
829
|
t.true(parsed.output[0].success);
|
|
@@ -770,9 +839,12 @@ test.serial('execute gap match fails when head is not found', async (t) => {
|
|
|
770
839
|
await fs.writeFile(absPath, 'line1\nline2\nline3');
|
|
771
840
|
const result = await tool.execute({
|
|
772
841
|
path: filePath,
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
842
|
+
replacements: [
|
|
843
|
+
{
|
|
844
|
+
search_content: 'nonexistent\n<...>\nline3',
|
|
845
|
+
replace_content: 'replaced',
|
|
846
|
+
},
|
|
847
|
+
],
|
|
776
848
|
});
|
|
777
849
|
const parsed = JSON.parse(result);
|
|
778
850
|
t.false(parsed.output[0].success);
|
|
@@ -786,32 +858,66 @@ test.serial('execute gap match fails when tail is not found after head', async (
|
|
|
786
858
|
await fs.writeFile(absPath, 'line1\nline2\nline3');
|
|
787
859
|
const result = await tool.execute({
|
|
788
860
|
path: filePath,
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
861
|
+
replacements: [
|
|
862
|
+
{
|
|
863
|
+
search_content: 'line1\n<...>\nnonexistent',
|
|
864
|
+
replace_content: 'replaced',
|
|
865
|
+
},
|
|
866
|
+
],
|
|
792
867
|
});
|
|
793
868
|
const parsed = JSON.parse(result);
|
|
794
869
|
t.false(parsed.output[0].success);
|
|
795
870
|
});
|
|
796
871
|
});
|
|
797
|
-
test.serial('execute
|
|
872
|
+
test.serial('execute does not invoke healing for a failed gap pattern and reports a head-anchor diagnostic', async (t) => {
|
|
798
873
|
await withTempDir(async (dir) => {
|
|
799
|
-
|
|
874
|
+
let healingCalls = 0;
|
|
875
|
+
const tool = createTool(createMockSettingsService({ 'tools.enableEditHealing': true }), async (params) => {
|
|
876
|
+
healingCalls++;
|
|
877
|
+
return { params, wasModified: false, confidence: 0 };
|
|
878
|
+
});
|
|
800
879
|
const filePath = 'content.txt';
|
|
801
|
-
|
|
802
|
-
await fs.writeFile(absPath, 'start\nmiddle1\nend\nstart\nmiddle2\nend\n');
|
|
880
|
+
await fs.writeFile(path.join(dir, filePath), 'line1\nline2\nline3');
|
|
803
881
|
const result = await tool.execute({
|
|
804
882
|
path: filePath,
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
883
|
+
replacements: [
|
|
884
|
+
{
|
|
885
|
+
search_content: 'nonexistent\n<...>\nline3',
|
|
886
|
+
replace_content: 'replaced',
|
|
887
|
+
},
|
|
888
|
+
],
|
|
808
889
|
});
|
|
809
890
|
const parsed = JSON.parse(result);
|
|
810
891
|
t.false(parsed.output[0].success);
|
|
811
|
-
t.
|
|
892
|
+
t.is(healingCalls, 0);
|
|
893
|
+
t.regex(parsed.output[0].error, /head anchor/i);
|
|
894
|
+
t.regex(parsed.output[0].error, /not auto-healed/i);
|
|
812
895
|
});
|
|
813
896
|
});
|
|
814
|
-
test.serial('execute gap
|
|
897
|
+
test.serial('execute gap diagnostic identifies the anchor that failed after the head matched', async (t) => {
|
|
898
|
+
await withTempDir(async (dir) => {
|
|
899
|
+
const tool = createTool(createMockSettingsService({ 'tools.enableEditHealing': true }), async (params) => ({
|
|
900
|
+
params,
|
|
901
|
+
wasModified: false,
|
|
902
|
+
confidence: 0,
|
|
903
|
+
}));
|
|
904
|
+
const filePath = 'content.txt';
|
|
905
|
+
await fs.writeFile(path.join(dir, filePath), 'line1\nline2\nline3');
|
|
906
|
+
const result = await tool.execute({
|
|
907
|
+
path: filePath,
|
|
908
|
+
replacements: [
|
|
909
|
+
{
|
|
910
|
+
search_content: 'line1\n<...>\nnonexistent',
|
|
911
|
+
replace_content: 'replaced',
|
|
912
|
+
},
|
|
913
|
+
],
|
|
914
|
+
});
|
|
915
|
+
const parsed = JSON.parse(result);
|
|
916
|
+
t.false(parsed.output[0].success);
|
|
917
|
+
t.regex(parsed.output[0].error, /not found after/i);
|
|
918
|
+
});
|
|
919
|
+
});
|
|
920
|
+
test.serial('execute gap match rejects multiple matches', async (t) => {
|
|
815
921
|
await withTempDir(async (dir) => {
|
|
816
922
|
const tool = createTool(createMockSettingsService({ 'tools.enableEditHealing': false }));
|
|
817
923
|
const filePath = 'content.txt';
|
|
@@ -819,14 +925,16 @@ test.serial('execute gap match replaces all when replace_all is true', async (t)
|
|
|
819
925
|
await fs.writeFile(absPath, 'start\nmiddle1\nend\nstart\nmiddle2\nend\n');
|
|
820
926
|
const result = await tool.execute({
|
|
821
927
|
path: filePath,
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
928
|
+
replacements: [
|
|
929
|
+
{
|
|
930
|
+
search_content: 'start\n<...>\nend',
|
|
931
|
+
replace_content: 'replaced',
|
|
932
|
+
},
|
|
933
|
+
],
|
|
825
934
|
});
|
|
826
935
|
const parsed = JSON.parse(result);
|
|
827
|
-
t.
|
|
828
|
-
|
|
829
|
-
t.is(updated, 'replaced\nreplaced\n');
|
|
936
|
+
t.false(parsed.output[0].success);
|
|
937
|
+
t.true(parsed.output[0].error.includes('gap'));
|
|
830
938
|
});
|
|
831
939
|
});
|
|
832
940
|
test.serial('execute gap match works with relaxed whitespace matching in segments', async (t) => {
|
|
@@ -837,9 +945,12 @@ test.serial('execute gap match works with relaxed whitespace matching in segment
|
|
|
837
945
|
await fs.writeFile(absPath, ' function foo() {\n const x = 1;\n const y = 2;\n return x;\n }\n');
|
|
838
946
|
const result = await tool.execute({
|
|
839
947
|
path: filePath,
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
948
|
+
replacements: [
|
|
949
|
+
{
|
|
950
|
+
search_content: 'function foo() {\n<...>\nreturn x;\n}',
|
|
951
|
+
replace_content: 'function foo() {\n return 42;\n}\n',
|
|
952
|
+
},
|
|
953
|
+
],
|
|
843
954
|
});
|
|
844
955
|
const parsed = JSON.parse(result);
|
|
845
956
|
t.true(parsed.output[0].success);
|
|
@@ -847,19 +958,21 @@ test.serial('execute gap match works with relaxed whitespace matching in segment
|
|
|
847
958
|
t.is(updated, 'function foo() {\n return 42;\n}\n');
|
|
848
959
|
});
|
|
849
960
|
});
|
|
850
|
-
test.serial('needsApproval handles gap match
|
|
961
|
+
test.serial('needsApproval handles gap match', async (t) => {
|
|
851
962
|
await withTempDir(async (dir) => {
|
|
852
|
-
const tool = createTool(createMockSettingsService(
|
|
963
|
+
const tool = createTool(createMockSettingsService());
|
|
853
964
|
const filePath = 'content.txt';
|
|
854
965
|
const absPath = path.join(dir, filePath);
|
|
855
966
|
await fs.writeFile(absPath, 'start\nmiddle\nend\n');
|
|
856
967
|
const result = await tool.needsApproval({
|
|
857
968
|
path: filePath,
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
969
|
+
replacements: [
|
|
970
|
+
{
|
|
971
|
+
search_content: 'start\n<...>\nend',
|
|
972
|
+
replace_content: 'replaced',
|
|
973
|
+
},
|
|
974
|
+
],
|
|
861
975
|
});
|
|
862
|
-
// In edit mode with a unique gap match, should auto-approve
|
|
863
976
|
t.false(result);
|
|
864
977
|
});
|
|
865
978
|
});
|