connectonion 0.6.1__py3-none-any.whl → 0.6.3__py3-none-any.whl
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.
- connectonion/__init__.py +46 -9
- connectonion/cli/__init__.py +11 -1
- connectonion/cli/browser_agent/__init__.py +11 -1
- connectonion/cli/browser_agent/browser.py +95 -142
- connectonion/cli/browser_agent/element_finder.py +147 -0
- connectonion/cli/browser_agent/highlight_screenshot.py +182 -0
- connectonion/cli/browser_agent/prompt.md +188 -105
- connectonion/cli/browser_agent/prompts/element_matcher.md +59 -0
- connectonion/cli/browser_agent/prompts/form_filler.md +19 -0
- connectonion/cli/browser_agent/prompts/scroll_strategy.md +36 -0
- connectonion/cli/browser_agent/scripts/extract_elements.js +126 -0
- connectonion/cli/browser_agent/scroll.py +145 -0
- connectonion/cli/co_ai/__init__.py +6 -0
- connectonion/cli/co_ai/agent.py +87 -0
- connectonion/cli/co_ai/agents/__init__.py +5 -0
- connectonion/cli/co_ai/agents/registry.py +57 -0
- connectonion/cli/co_ai/commands/__init__.py +45 -0
- connectonion/cli/co_ai/commands/compact.py +173 -0
- connectonion/cli/co_ai/commands/cost.py +77 -0
- connectonion/cli/co_ai/commands/export.py +60 -0
- connectonion/cli/co_ai/commands/help.py +80 -0
- connectonion/cli/co_ai/commands/init.py +101 -0
- connectonion/cli/co_ai/commands/sessions.py +55 -0
- connectonion/cli/co_ai/commands/tasks.py +63 -0
- connectonion/cli/co_ai/commands/undo.py +103 -0
- connectonion/cli/co_ai/context.py +127 -0
- connectonion/cli/co_ai/main.py +52 -0
- connectonion/cli/co_ai/plugins/__init__.py +6 -0
- connectonion/cli/co_ai/plugins/reminder.py +76 -0
- connectonion/cli/co_ai/plugins/shell_approval.py +105 -0
- connectonion/cli/co_ai/prompts/agents/explore.md +79 -0
- connectonion/cli/co_ai/prompts/agents/plan.md +60 -0
- connectonion/cli/co_ai/prompts/assembler.py +303 -0
- connectonion/cli/{docs/co-vibecoding-principles-docs-contexts-all-in-one.md → co_ai/prompts/connectonion/README.md} +26 -0
- connectonion/cli/co_ai/prompts/connectonion/api.md +457 -0
- connectonion/cli/co_ai/prompts/connectonion/cli/README.md +805 -0
- connectonion/cli/co_ai/prompts/connectonion/cli/auth.md +46 -0
- connectonion/cli/co_ai/prompts/connectonion/cli/browser.md +235 -0
- connectonion/cli/co_ai/prompts/connectonion/cli/copy.md +184 -0
- connectonion/cli/co_ai/prompts/connectonion/cli/create.md +335 -0
- connectonion/cli/co_ai/prompts/connectonion/cli/init.md +431 -0
- connectonion/cli/co_ai/prompts/connectonion/co-directory-structure.md +214 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/agent.md +1078 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/events.md +816 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/llm_do.md +256 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/max_iterations.md +362 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/models.md +641 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/plugins.md +100 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/prompts.md +122 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/tools.md +512 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/transcribe.md +156 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/trust.md +291 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/README.md +18 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/auto_debug.md +1026 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/console.md +129 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/eval-format.md +178 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/eval.md +230 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/exceptions.md +307 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/log.md +117 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/xray.md +215 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/001-choosing-input-method.md +202 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/002-choosing-llm-function-name.md +202 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/003-choosing-trust-keyword.md +141 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/004-cli-create-flow.md +117 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/005-designing-agent-network-protocol.md +503 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/006-agent-address-format.md +305 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/007-authentication-backend-design.md +240 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/008-naming-is-hard.md +228 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/009-why-connect-function.md +167 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/010-cli-ux-progressive-disclosure.md +176 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/011-global-config-identity-management.md +357 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/012-tool-execution-separation.md +259 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/013-debug-and-logging-design.md +253 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/014-hook-system-design.md +510 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/015-interactive-auto-debug-design.md +837 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/016-why-no-zero-knowledge-proofs.md +358 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/017-session-logging-and-eval-format.md +120 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/018-event-api-naming.md +274 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/019-agent-lifecycle-design.md +655 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/020-trust-system-and-network-architecture.md +503 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/021-task-storage-jsonl-design.md +496 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/022-raw-asgi-implementation.md +273 -0
- connectonion/cli/co_ai/prompts/connectonion/examples/agent_reasoning.md +62 -0
- connectonion/cli/co_ai/prompts/connectonion/examples/atomic_tools.md +24 -0
- connectonion/cli/co_ai/prompts/connectonion/examples/load_guide.md +18 -0
- connectonion/cli/co_ai/prompts/connectonion/examples.md +0 -0
- connectonion/cli/co_ai/prompts/connectonion/hook-system-options.md +364 -0
- connectonion/cli/co_ai/prompts/connectonion/index.md +162 -0
- connectonion/cli/co_ai/prompts/connectonion/integrations/README.md +12 -0
- connectonion/cli/co_ai/prompts/connectonion/integrations/auth.md +450 -0
- connectonion/cli/co_ai/prompts/connectonion/integrations/google.md +431 -0
- connectonion/cli/co_ai/prompts/connectonion/integrations/microsoft.md +370 -0
- connectonion/cli/co_ai/prompts/connectonion/network/README.md +14 -0
- connectonion/cli/co_ai/prompts/connectonion/network/connect.md +543 -0
- connectonion/cli/co_ai/prompts/connectonion/network/connection.md +538 -0
- connectonion/cli/co_ai/prompts/connectonion/network/deploy.md +123 -0
- connectonion/cli/co_ai/prompts/connectonion/network/host.md +1049 -0
- connectonion/cli/co_ai/prompts/connectonion/network/protocol/agent-relay-protocol.md +495 -0
- connectonion/cli/co_ai/prompts/connectonion/network/protocol/announce-message.md +115 -0
- connectonion/cli/co_ai/prompts/connectonion/principles.md +124 -0
- connectonion/cli/co_ai/prompts/connectonion/quickstart.md +261 -0
- connectonion/cli/co_ai/prompts/connectonion/roadmap.md +81 -0
- connectonion/cli/co_ai/prompts/connectonion/templates/README.md +77 -0
- connectonion/cli/co_ai/prompts/connectonion/templates/meta-agent.md +152 -0
- connectonion/cli/co_ai/prompts/connectonion/templates/minimal.md +105 -0
- connectonion/cli/co_ai/prompts/connectonion/templates/playwright.md +130 -0
- connectonion/cli/co_ai/prompts/connectonion/templates/web-research.md +144 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/README.md +95 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/chat.md +181 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/divider.md +63 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/dropdown.md +83 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/footer.md +44 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/fuzzy.md +68 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/input.md +84 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/keys.md +77 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/pick.md +71 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/providers.md +89 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/status_bar.md +67 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/README.md +156 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/calendar_plugin.md +68 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/eval.md +89 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/gmail_plugin.md +68 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/image_result_formatter.md +74 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/re_act.md +86 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/shell_approval.md +69 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/README.md +81 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/diff_writer.md +138 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/get_emails.md +499 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/gmail.md +135 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/google_calendar.md +106 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/memory.md +486 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/microsoft_calendar.md +106 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/outlook.md +120 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/send_email.md +403 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/shell.md +95 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/slash_command.md +96 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/terminal.md +97 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/todo_list.md +252 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/web_fetch.md +130 -0
- connectonion/cli/co_ai/prompts/connectonion/vibe-coding-guide.md +97 -0
- connectonion/cli/co_ai/prompts/connectonion/windows-support.md +258 -0
- connectonion/cli/co_ai/prompts/main.md +247 -0
- connectonion/cli/co_ai/prompts/reminders/plan_mode.md +34 -0
- connectonion/cli/co_ai/prompts/summarization.md +55 -0
- connectonion/cli/co_ai/prompts/tools/ask_user.md +61 -0
- connectonion/cli/co_ai/prompts/tools/background.md +57 -0
- connectonion/cli/co_ai/prompts/tools/edit.md +90 -0
- connectonion/cli/co_ai/prompts/tools/glob.md +52 -0
- connectonion/cli/co_ai/prompts/tools/grep.md +55 -0
- connectonion/cli/co_ai/prompts/tools/plan_mode.md +80 -0
- connectonion/cli/co_ai/prompts/tools/read.md +40 -0
- connectonion/cli/co_ai/prompts/tools/shell.md +67 -0
- connectonion/cli/co_ai/prompts/tools/task.md +51 -0
- connectonion/cli/co_ai/prompts/tools/todo.md +139 -0
- connectonion/cli/co_ai/prompts/tools/write.md +47 -0
- connectonion/cli/co_ai/prompts/workflow.md +89 -0
- connectonion/cli/co_ai/reminders.py +159 -0
- connectonion/cli/co_ai/sessions.py +110 -0
- connectonion/cli/co_ai/skills/__init__.py +37 -0
- connectonion/cli/co_ai/skills/builtin/commit/SKILL.md +63 -0
- connectonion/cli/co_ai/skills/builtin/review-pr/SKILL.md +76 -0
- connectonion/cli/co_ai/skills/loader.py +166 -0
- connectonion/cli/co_ai/skills/tool.py +46 -0
- connectonion/cli/co_ai/tools/__init__.py +92 -0
- connectonion/cli/co_ai/tools/ask_user.py +35 -0
- connectonion/cli/co_ai/tools/background.py +201 -0
- connectonion/cli/co_ai/tools/diff_writer.py +291 -0
- connectonion/cli/co_ai/tools/edit.py +89 -0
- connectonion/cli/co_ai/tools/glob.py +84 -0
- connectonion/cli/co_ai/tools/grep.py +158 -0
- connectonion/cli/co_ai/tools/load_guide.py +23 -0
- connectonion/cli/co_ai/tools/multi_edit.py +116 -0
- connectonion/cli/co_ai/tools/plan_mode.py +172 -0
- connectonion/cli/co_ai/tools/read.py +67 -0
- connectonion/cli/co_ai/tools/task.py +59 -0
- connectonion/cli/co_ai/tools/todo_list.py +159 -0
- connectonion/cli/co_ai/tools/write.py +126 -0
- connectonion/cli/commands/__init__.py +11 -1
- connectonion/cli/commands/ai_commands.py +34 -0
- connectonion/cli/commands/copy_commands.py +55 -6
- connectonion/cli/commands/create.py +20 -17
- connectonion/cli/commands/init.py +19 -22
- connectonion/cli/commands/project_cmd_lib.py +15 -0
- connectonion/cli/main.py +11 -0
- connectonion/console.py +15 -1
- connectonion/core/__init__.py +10 -1
- connectonion/core/agent.py +37 -16
- connectonion/core/exceptions.py +74 -0
- connectonion/core/llm.py +54 -6
- connectonion/core/tool_executor.py +32 -31
- connectonion/core/tool_factory.py +47 -10
- connectonion/debug/__init__.py +10 -1
- connectonion/debug/debug_explainer/__init__.py +10 -1
- connectonion/debug/execution_analyzer/__init__.py +10 -1
- connectonion/debug/execution_analyzer/execution_analysis.py +5 -2
- connectonion/debug/runtime_inspector/__init__.py +10 -1
- connectonion/docs/.package-ignore +6 -0
- connectonion/docs/README.md +2036 -0
- connectonion/docs/api.md +457 -0
- connectonion/docs/archive/001-ai-agent-is-just-prompt-plus-function.md +249 -0
- connectonion/docs/archive/README.md +53 -0
- connectonion/docs/archive/archive/consolidation-plan.md +72 -0
- connectonion/docs/archive/archive/core-principles-extracted.md +239 -0
- connectonion/docs/archive/archive/master-principles.md +222 -0
- connectonion/docs/archive/archive/principles.md +293 -0
- connectonion/docs/archive/archive/simplicity-principles.md +221 -0
- connectonion/docs/archive/attack-defense-insights.md +410 -0
- connectonion/docs/archive/business-model.md +305 -0
- connectonion/docs/archive/core-principles-unified.md +190 -0
- connectonion/docs/archive/discussion-journey.md +178 -0
- connectonion/docs/archive/economic-analysis.md +323 -0
- connectonion/docs/archive/features/01-share-and-find.md +256 -0
- connectonion/docs/archive/features/02-agent-authentication.md +93 -0
- connectonion/docs/archive/features/03-test-before-trust.md +71 -0
- connectonion/docs/archive/features/06-reliability-and-offline.md +197 -0
- connectonion/docs/archive/features/README.md +46 -0
- connectonion/docs/archive/features-roadmap.md +247 -0
- connectonion/docs/archive/mcp-comparison-insights.md +215 -0
- connectonion/docs/archive/migration-strategy.md +571 -0
- connectonion/docs/archive/mini-whitepaper.md +293 -0
- connectonion/docs/archive/network-protocol.md +394 -0
- connectonion/docs/archive/semantic-revolution.md +367 -0
- connectonion/docs/archive/technical-architecture.md +453 -0
- connectonion/docs/archive/the-semantic-insight.md +207 -0
- connectonion/docs/archive/threat-model.md +164 -0
- connectonion/docs/cli/README.md +805 -0
- connectonion/docs/cli/auth.md +46 -0
- connectonion/docs/cli/browser.md +235 -0
- connectonion/docs/cli/copy.md +232 -0
- connectonion/docs/cli/create.md +335 -0
- connectonion/docs/cli/init.md +431 -0
- connectonion/docs/co-directory-structure.md +214 -0
- connectonion/docs/concepts/agent.md +1078 -0
- connectonion/docs/concepts/events.md +699 -0
- connectonion/docs/concepts/llm_do.md +256 -0
- connectonion/docs/concepts/max_iterations.md +362 -0
- connectonion/docs/concepts/models.md +641 -0
- connectonion/docs/concepts/plugins.md +100 -0
- connectonion/docs/concepts/prompts.md +122 -0
- connectonion/docs/concepts/session.md +428 -0
- connectonion/docs/concepts/tools.md +512 -0
- connectonion/docs/concepts/transcribe.md +156 -0
- connectonion/docs/concepts/trust.md +291 -0
- connectonion/docs/connectonion.md +1256 -0
- connectonion/docs/debug/README.md +18 -0
- connectonion/docs/debug/auto_debug.md +1026 -0
- connectonion/docs/debug/console.md +129 -0
- connectonion/docs/debug/eval-format.md +178 -0
- connectonion/docs/debug/eval.md +230 -0
- connectonion/docs/debug/exceptions.md +307 -0
- connectonion/docs/debug/log.md +117 -0
- connectonion/docs/debug/xray.md +215 -0
- connectonion/docs/design-decisions/001-choosing-input-method.md +202 -0
- connectonion/docs/design-decisions/002-choosing-llm-function-name.md +202 -0
- connectonion/docs/design-decisions/003-choosing-trust-keyword.md +141 -0
- connectonion/docs/design-decisions/004-cli-create-flow.md +117 -0
- connectonion/docs/design-decisions/005-designing-agent-network-protocol.md +503 -0
- connectonion/docs/design-decisions/006-agent-address-format.md +305 -0
- connectonion/docs/design-decisions/007-authentication-backend-design.md +240 -0
- connectonion/docs/design-decisions/008-naming-is-hard.md +228 -0
- connectonion/docs/design-decisions/009-why-connect-function.md +167 -0
- connectonion/docs/design-decisions/010-cli-ux-progressive-disclosure.md +176 -0
- connectonion/docs/design-decisions/011-global-config-identity-management.md +357 -0
- connectonion/docs/design-decisions/012-tool-execution-separation.md +259 -0
- connectonion/docs/design-decisions/013-debug-and-logging-design.md +253 -0
- connectonion/docs/design-decisions/014-hook-system-design.md +510 -0
- connectonion/docs/design-decisions/015-interactive-auto-debug-design.md +837 -0
- connectonion/docs/design-decisions/016-why-no-zero-knowledge-proofs.md +358 -0
- connectonion/docs/design-decisions/017-session-logging-and-eval-format.md +120 -0
- connectonion/docs/design-decisions/018-event-api-naming.md +274 -0
- connectonion/docs/design-decisions/019-agent-lifecycle-design.md +655 -0
- connectonion/docs/design-decisions/020-trust-system-and-network-architecture.md +503 -0
- connectonion/docs/design-decisions/021-task-storage-jsonl-design.md +496 -0
- connectonion/docs/design-decisions/022-raw-asgi-implementation.md +273 -0
- connectonion/docs/examples.md +0 -0
- connectonion/docs/hook-system-options.md +364 -0
- connectonion/docs/integrations/README.md +12 -0
- connectonion/docs/integrations/auth.md +450 -0
- connectonion/docs/integrations/google.md +431 -0
- connectonion/docs/integrations/microsoft.md +370 -0
- connectonion/docs/network/README.md +14 -0
- connectonion/docs/network/connect.md +629 -0
- connectonion/docs/network/deploy.md +124 -0
- connectonion/docs/network/host.md +1087 -0
- connectonion/docs/network/io.md +538 -0
- connectonion/docs/network/protocol/agent-relay-protocol.md +495 -0
- connectonion/docs/network/protocol/announce-message.md +115 -0
- connectonion/docs/principles.md +124 -0
- connectonion/docs/quickstart.md +261 -0
- connectonion/docs/roadmap.md +81 -0
- connectonion/docs/templates/README.md +77 -0
- connectonion/docs/templates/meta-agent.md +152 -0
- connectonion/docs/templates/minimal.md +105 -0
- connectonion/docs/templates/playwright.md +130 -0
- connectonion/docs/templates/web-research.md +144 -0
- connectonion/docs/tui/README.md +95 -0
- connectonion/docs/tui/chat.md +181 -0
- connectonion/docs/tui/divider.md +63 -0
- connectonion/docs/tui/dropdown.md +83 -0
- connectonion/docs/tui/footer.md +44 -0
- connectonion/docs/tui/fuzzy.md +68 -0
- connectonion/docs/tui/input.md +84 -0
- connectonion/docs/tui/keys.md +77 -0
- connectonion/docs/tui/pick.md +71 -0
- connectonion/docs/tui/providers.md +89 -0
- connectonion/docs/tui/status_bar.md +67 -0
- connectonion/docs/useful_plugins/README.md +160 -0
- connectonion/docs/useful_plugins/calendar_plugin.md +68 -0
- connectonion/docs/useful_plugins/eval.md +89 -0
- connectonion/docs/useful_plugins/gmail_plugin.md +68 -0
- connectonion/docs/useful_plugins/image_result_formatter.md +74 -0
- connectonion/docs/useful_plugins/re_act.md +86 -0
- connectonion/docs/useful_plugins/shell_approval.md +69 -0
- connectonion/docs/useful_plugins/system_reminder.md +210 -0
- connectonion/docs/useful_prompts/README.md +127 -0
- connectonion/docs/useful_prompts/coding_agent.md +214 -0
- connectonion/docs/useful_tools/README.md +81 -0
- connectonion/docs/useful_tools/ask_user.md +103 -0
- connectonion/docs/useful_tools/diff_writer.md +158 -0
- connectonion/docs/useful_tools/get_emails.md +519 -0
- connectonion/docs/useful_tools/gmail.md +155 -0
- connectonion/docs/useful_tools/google_calendar.md +126 -0
- connectonion/docs/useful_tools/memory.md +506 -0
- connectonion/docs/useful_tools/microsoft_calendar.md +126 -0
- connectonion/docs/useful_tools/outlook.md +140 -0
- connectonion/docs/useful_tools/send_email.md +423 -0
- connectonion/docs/useful_tools/shell.md +115 -0
- connectonion/docs/useful_tools/slash_command.md +116 -0
- connectonion/docs/useful_tools/terminal.md +115 -0
- connectonion/docs/useful_tools/todo_list.md +272 -0
- connectonion/docs/useful_tools/web_fetch.md +150 -0
- connectonion/docs/vibe-coding-guide.md +97 -0
- connectonion/docs/windows-support.md +258 -0
- connectonion/logger.py +3 -3
- connectonion/network/__init__.py +19 -6
- connectonion/network/asgi/__init__.py +81 -0
- connectonion/network/asgi/http.py +205 -0
- connectonion/network/asgi/websocket.py +217 -0
- connectonion/network/connect.py +232 -185
- connectonion/network/host/__init__.py +59 -0
- connectonion/network/host/auth.py +191 -0
- connectonion/network/host/routes.py +135 -0
- connectonion/network/host/server.py +289 -0
- connectonion/network/host/session.py +78 -0
- connectonion/network/io/__init__.py +21 -0
- connectonion/network/{connection.py → io/base.py} +17 -42
- connectonion/network/io/websocket.py +55 -0
- connectonion/network/relay.py +37 -16
- connectonion/network/trust/__init__.py +30 -0
- connectonion/network/trust/factory.py +138 -0
- connectonion/network/{trust_agents.py → trust/prompts.py} +3 -3
- connectonion/network/{trust_functions.py → trust/tools.py} +2 -2
- connectonion/prompt_files/__init__.py +11 -1
- connectonion/prompt_files/react_acknowledge.md +26 -0
- connectonion/prompts.py +10 -1
- connectonion/tui/chat.py +10 -1
- connectonion/tui/divider.py +10 -1
- connectonion/tui/dropdown.py +10 -1
- connectonion/tui/footer.py +8 -0
- connectonion/tui/fuzzy.py +11 -1
- connectonion/tui/input.py +118 -70
- connectonion/tui/keys.py +133 -6
- connectonion/tui/providers.py +11 -1
- connectonion/tui/status_bar.py +10 -1
- connectonion/useful_events_handlers/__init__.py +8 -0
- connectonion/useful_events_handlers/reflect.py +19 -4
- connectonion/useful_plugins/__init__.py +2 -1
- connectonion/useful_plugins/eval.py +2 -2
- connectonion/useful_plugins/gmail_plugin.py +3 -3
- connectonion/useful_plugins/image_result_formatter.py +3 -3
- connectonion/useful_plugins/re_act.py +114 -28
- connectonion/useful_plugins/shell_approval.py +2 -2
- connectonion/useful_plugins/system_reminder.py +103 -0
- connectonion/useful_plugins/ui_stream.py +18 -133
- connectonion/useful_prompts/README.md +61 -0
- connectonion/useful_prompts/__init__.py +45 -0
- connectonion/useful_prompts/coding_agent/README.md +106 -0
- connectonion/useful_prompts/coding_agent/assembler.py +123 -0
- connectonion/useful_prompts/coding_agent/prompts/main.md +227 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/ask_user.md +61 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/background.md +57 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/edit.md +90 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/glob.md +52 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/grep.md +55 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/plan_mode.md +80 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/read.md +40 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/shell.md +67 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/task.md +51 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/todo.md +139 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/write.md +48 -0
- connectonion/useful_prompts/system-reminders/security-warning.md +14 -0
- connectonion/useful_prompts/system-reminders/test-reminder.md +11 -0
- connectonion/useful_tools/__init__.py +31 -4
- connectonion/useful_tools/ask_user.py +35 -0
- connectonion/useful_tools/bash.py +69 -0
- connectonion/useful_tools/diff_writer.py +186 -94
- connectonion/useful_tools/edit.py +102 -0
- connectonion/useful_tools/glob_files.py +97 -0
- connectonion/useful_tools/grep_files.py +171 -0
- connectonion/useful_tools/multi_edit.py +116 -0
- connectonion/useful_tools/read_file.py +73 -0
- connectonion/useful_tools/shell.py +50 -45
- connectonion/useful_tools/write_file.py +129 -0
- {connectonion-0.6.1.dist-info → connectonion-0.6.3.dist-info}/METADATA +10 -3
- connectonion-0.6.3.dist-info/RECORD +469 -0
- connectonion/cli/browser_agent/scroll_strategies.py +0 -276
- connectonion/network/asgi.py +0 -407
- connectonion/network/host.py +0 -616
- connectonion/network/trust.py +0 -166
- connectonion-0.6.1.dist-info/RECORD +0 -123
- /connectonion/cli/{docs → co_ai/prompts/connectonion}/connectonion.md +0 -0
- {connectonion-0.6.1.dist-info → connectonion-0.6.3.dist-info}/WHEEL +0 -0
- {connectonion-0.6.1.dist-info → connectonion-0.6.3.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bash tool for executing terminal commands (Unix/Mac only).
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
from connectonion import Agent, bash
|
|
6
|
+
|
|
7
|
+
agent = Agent("coder", tools=[bash])
|
|
8
|
+
|
|
9
|
+
# Agent can now use:
|
|
10
|
+
# - bash(command) - Execute bash command, returns output
|
|
11
|
+
# - bash(command, cwd="/path") - Execute in specific directory
|
|
12
|
+
# - bash(command, timeout=60) - Execute with custom timeout
|
|
13
|
+
|
|
14
|
+
Note: This tool is for Unix/Mac systems. For cross-platform usage, use Shell class instead.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import subprocess
|
|
18
|
+
import platform
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def bash(command: str, cwd: str = ".", timeout: int = 120) -> str:
|
|
22
|
+
"""Execute a bash command, returns output (Unix/Mac only).
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
command: Bash command to execute (e.g., "ls -la", "git status")
|
|
26
|
+
cwd: Working directory (default: current directory)
|
|
27
|
+
timeout: Seconds before timeout (default: 120, max: 600)
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Command output (stdout + stderr)
|
|
31
|
+
"""
|
|
32
|
+
# Check platform
|
|
33
|
+
if platform.system() == "Windows":
|
|
34
|
+
return "Error: bash tool is for Unix/Mac only. Use Shell class for Windows."
|
|
35
|
+
|
|
36
|
+
# Cap timeout at 10 minutes
|
|
37
|
+
timeout = min(timeout, 600)
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
result = subprocess.run(
|
|
41
|
+
command,
|
|
42
|
+
shell=True,
|
|
43
|
+
executable="/bin/bash",
|
|
44
|
+
capture_output=True,
|
|
45
|
+
text=True,
|
|
46
|
+
cwd=cwd,
|
|
47
|
+
timeout=timeout
|
|
48
|
+
)
|
|
49
|
+
except subprocess.TimeoutExpired:
|
|
50
|
+
return f"Error: Command timed out after {timeout} seconds"
|
|
51
|
+
except FileNotFoundError:
|
|
52
|
+
return "Error: /bin/bash not found. This tool requires bash shell."
|
|
53
|
+
|
|
54
|
+
parts = []
|
|
55
|
+
if result.stdout:
|
|
56
|
+
parts.append(result.stdout.rstrip())
|
|
57
|
+
if result.stderr:
|
|
58
|
+
parts.append(f"STDERR:\n{result.stderr.rstrip()}")
|
|
59
|
+
if result.returncode != 0:
|
|
60
|
+
parts.append(f"\nExit code: {result.returncode}")
|
|
61
|
+
|
|
62
|
+
output = "\n".join(parts) if parts else "(no output)"
|
|
63
|
+
|
|
64
|
+
# Truncate large outputs
|
|
65
|
+
max_chars = 10000
|
|
66
|
+
if len(output) > max_chars:
|
|
67
|
+
output = output[:max_chars] + f"\n... (truncated, {len(output):,} total chars)"
|
|
68
|
+
|
|
69
|
+
return output
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Purpose:
|
|
2
|
+
Purpose: Web-based file writing tool with Claude Code-style permission modes
|
|
3
3
|
LLM-Note:
|
|
4
|
-
Dependencies: imports from [difflib, pathlib,
|
|
5
|
-
Data flow: Agent calls DiffWriter.write(path, content)
|
|
6
|
-
State/Effects: reads and writes files on filesystem |
|
|
7
|
-
Integration: exposes DiffWriter class with write(path, content), diff(path, content), read(path) | used as agent tool via Agent(tools=[DiffWriter()])
|
|
8
|
-
|
|
9
|
-
Errors: returns error string if file unreadable | returns "Cancelled" if user rejects | no exceptions raised
|
|
4
|
+
Dependencies: imports from [difflib, pathlib, typing] | imported by [useful_tools/__init__.py, __init__.py] | tested by [tests/unit/test_diff_writer.py]
|
|
5
|
+
Data flow: Agent calls DiffWriter.write(path, content) -> show diff via io -> ask approval via io -> write file -> return status
|
|
6
|
+
State/Effects: reads and writes files on filesystem | sends diff_preview and ask_user events via io | requires io channel for approval
|
|
7
|
+
Integration: exposes DiffWriter class with write(path, content), diff(path, content), read(path) | used as agent tool via Agent(tools=[DiffWriter()])
|
|
8
|
+
Errors: returns error string if file unreadable | returns user feedback on rejection | no exceptions raised
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
Permission Modes (like Claude Code's Shift+Tab cycle):
|
|
11
|
+
- normal: Prompt for every edit (default)
|
|
12
|
+
- auto: Auto-approve all edits without prompting
|
|
13
|
+
- plan: Read-only mode, no writes allowed
|
|
12
14
|
|
|
13
15
|
Usage:
|
|
14
16
|
from connectonion import Agent, DiffWriter
|
|
15
17
|
|
|
16
|
-
writer = DiffWriter()
|
|
18
|
+
writer = DiffWriter() # mode="normal" by default
|
|
17
19
|
agent = Agent("coder", tools=[writer])
|
|
18
20
|
|
|
19
21
|
# Agent can now use:
|
|
@@ -24,25 +26,36 @@ Usage:
|
|
|
24
26
|
|
|
25
27
|
import difflib
|
|
26
28
|
from pathlib import Path
|
|
29
|
+
from typing import Optional, Tuple
|
|
27
30
|
|
|
28
|
-
from rich.console import Console
|
|
29
|
-
from rich.panel import Panel
|
|
30
|
-
from rich.text import Text
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
# Permission modes (like Claude Code's Shift+Tab cycle)
|
|
33
|
+
MODE_NORMAL = "normal" # Prompt for every edit
|
|
34
|
+
MODE_AUTO = "auto" # Auto-approve edits
|
|
35
|
+
MODE_PLAN = "plan" # Read-only, no writes
|
|
33
36
|
|
|
34
37
|
|
|
35
38
|
class DiffWriter:
|
|
36
|
-
"""File writer with
|
|
39
|
+
"""File writer with Claude Code-style permission modes (web mode).
|
|
37
40
|
|
|
38
|
-
|
|
41
|
+
Requires io channel for approval prompts. Without io, falls back to auto-approve.
|
|
42
|
+
|
|
43
|
+
Usage:
|
|
44
|
+
writer = DiffWriter(mode="normal") # Prompt for every edit
|
|
45
|
+
writer = DiffWriter(mode="auto") # Auto-approve all edits
|
|
46
|
+
writer = DiffWriter(mode="plan") # Read-only, preview only
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, mode: str = MODE_NORMAL, preview_limit: int = 2000):
|
|
39
50
|
"""Initialize DiffWriter.
|
|
40
51
|
|
|
41
52
|
Args:
|
|
42
|
-
|
|
53
|
+
mode: Permission mode - "normal" (prompt), "auto" (auto-approve), "plan" (read-only)
|
|
54
|
+
preview_limit: Max chars to include in diff preview
|
|
43
55
|
"""
|
|
44
|
-
self.
|
|
45
|
-
self.
|
|
56
|
+
self.mode = mode
|
|
57
|
+
self.preview_limit = preview_limit
|
|
58
|
+
self.io = None # Set by agent's _sync_tool_io event handler
|
|
46
59
|
|
|
47
60
|
def write(self, path: str, content: str) -> str:
|
|
48
61
|
"""Write content to a file with diff display and approval.
|
|
@@ -52,31 +65,42 @@ class DiffWriter:
|
|
|
52
65
|
content: Content to write
|
|
53
66
|
|
|
54
67
|
Returns:
|
|
55
|
-
Success message, rejection message, or
|
|
68
|
+
Success message, rejection message with feedback, or plan mode preview
|
|
56
69
|
"""
|
|
57
70
|
file_path = Path(path)
|
|
71
|
+
file_exists = file_path.exists()
|
|
58
72
|
|
|
59
|
-
|
|
73
|
+
# Plan mode = read-only, just show what would happen
|
|
74
|
+
if self.mode == MODE_PLAN:
|
|
60
75
|
diff_text = self._generate_diff(path, content)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
76
|
+
preview = self._build_preview(diff_text, content, file_exists)
|
|
77
|
+
return f"[Plan mode] Would write {len(content)} bytes to {path}\n\nPreview:\n{preview[:500]}"
|
|
78
|
+
|
|
79
|
+
# Generate diff for display
|
|
80
|
+
diff_text = self._generate_diff(path, content)
|
|
81
|
+
preview = self._build_preview(diff_text, content, file_exists)
|
|
82
|
+
preview, truncated = self._truncate_preview(preview)
|
|
65
83
|
|
|
66
|
-
|
|
84
|
+
# Send diff preview to UI (best-effort, doesn't block)
|
|
85
|
+
self._send_preview(path, preview, truncated, file_exists)
|
|
86
|
+
|
|
87
|
+
# Check approval based on mode
|
|
88
|
+
if self.mode == MODE_NORMAL:
|
|
89
|
+
choice = self._ask_approval(path, preview, truncated)
|
|
67
90
|
|
|
68
91
|
if choice == "reject":
|
|
69
|
-
self.
|
|
70
|
-
self._console.print("[bold yellow]What should the agent do instead?[/]")
|
|
71
|
-
feedback = input("> ")
|
|
92
|
+
feedback = self._ask_feedback(path)
|
|
72
93
|
return f"User rejected changes to {path}. Feedback: {feedback}"
|
|
73
94
|
|
|
74
95
|
if choice == "approve_all":
|
|
75
|
-
self.
|
|
96
|
+
self.mode = MODE_AUTO # Switch to auto mode for rest of session
|
|
76
97
|
|
|
98
|
+
# Auto mode or approved: write file
|
|
77
99
|
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
78
|
-
file_path.write_text(content, encoding=
|
|
79
|
-
|
|
100
|
+
file_path.write_text(content, encoding="utf-8")
|
|
101
|
+
|
|
102
|
+
mode_note = "" if self.mode == MODE_NORMAL else f" [{self.mode} mode]"
|
|
103
|
+
return f"Wrote {len(content)} bytes to {path}{mode_note}"
|
|
80
104
|
|
|
81
105
|
def diff(self, path: str, content: str) -> str:
|
|
82
106
|
"""Show diff without writing (preview mode).
|
|
@@ -90,7 +114,6 @@ class DiffWriter:
|
|
|
90
114
|
"""
|
|
91
115
|
diff_text = self._generate_diff(path, content)
|
|
92
116
|
if diff_text:
|
|
93
|
-
self._display_diff(diff_text, path)
|
|
94
117
|
return diff_text
|
|
95
118
|
return f"No changes to {path}"
|
|
96
119
|
|
|
@@ -106,37 +129,126 @@ class DiffWriter:
|
|
|
106
129
|
file_path = Path(path)
|
|
107
130
|
if not file_path.exists():
|
|
108
131
|
return f"Error: File {path} not found"
|
|
109
|
-
return file_path.read_text(encoding=
|
|
132
|
+
return file_path.read_text(encoding="utf-8")
|
|
133
|
+
|
|
134
|
+
# =========================================================================
|
|
135
|
+
# IO Communication (Web Mode)
|
|
136
|
+
# =========================================================================
|
|
110
137
|
|
|
111
|
-
def
|
|
112
|
-
"""
|
|
138
|
+
def _get_io(self):
|
|
139
|
+
"""Return active io channel if available."""
|
|
140
|
+
return self.io
|
|
141
|
+
|
|
142
|
+
def _send_preview(self, path: str, preview: str, truncated: bool, file_exists: bool) -> None:
|
|
143
|
+
"""Send diff preview event to UI client.
|
|
144
|
+
|
|
145
|
+
This is informational - UI can render a nice diff view.
|
|
146
|
+
Does not block or wait for response.
|
|
147
|
+
"""
|
|
148
|
+
io = self._get_io()
|
|
149
|
+
if not io:
|
|
150
|
+
return
|
|
151
|
+
io.send({
|
|
152
|
+
"type": "diff_preview",
|
|
153
|
+
"path": path,
|
|
154
|
+
"preview": preview,
|
|
155
|
+
"truncated": truncated,
|
|
156
|
+
"file_exists": file_exists,
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
def _ask_approval(self, path: str, preview: str, truncated: bool) -> str:
|
|
160
|
+
"""Ask user for approval via io channel.
|
|
113
161
|
|
|
114
162
|
Returns:
|
|
115
|
-
|
|
163
|
+
"approve" - Apply this change
|
|
164
|
+
"approve_all" - Apply and switch to auto mode
|
|
165
|
+
"reject" - Reject and ask for feedback
|
|
116
166
|
"""
|
|
117
|
-
self.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
167
|
+
io = self._get_io()
|
|
168
|
+
if not io:
|
|
169
|
+
# No io channel = auto-approve (for non-web usage)
|
|
170
|
+
return "approve"
|
|
171
|
+
|
|
172
|
+
question = f"Apply changes to {path}?"
|
|
173
|
+
if truncated:
|
|
174
|
+
question += " (preview truncated)"
|
|
175
|
+
|
|
176
|
+
response = self._ask_user(
|
|
177
|
+
question,
|
|
178
|
+
options=[
|
|
179
|
+
"Yes, apply this change",
|
|
180
|
+
"Yes to all (auto-approve)",
|
|
181
|
+
"No, reject and give feedback",
|
|
182
|
+
],
|
|
128
183
|
)
|
|
129
|
-
|
|
184
|
+
|
|
185
|
+
# Parse response
|
|
186
|
+
if response == "Yes to all (auto-approve)":
|
|
187
|
+
return "approve_all"
|
|
188
|
+
if response == "Yes, apply this change":
|
|
189
|
+
return "approve"
|
|
190
|
+
|
|
191
|
+
# Handle free-text responses
|
|
192
|
+
if response:
|
|
193
|
+
lowered = response.strip().lower()
|
|
194
|
+
if "yes to all" in lowered or lowered == "auto":
|
|
195
|
+
return "approve_all"
|
|
196
|
+
if lowered.startswith("yes") or lowered == "approve":
|
|
197
|
+
return "approve"
|
|
198
|
+
|
|
199
|
+
return "reject"
|
|
200
|
+
|
|
201
|
+
def _ask_feedback(self, path: str) -> str:
|
|
202
|
+
"""Ask user for feedback when changes are rejected."""
|
|
203
|
+
feedback = self._ask_user(f"What should the agent do instead for {path}?")
|
|
204
|
+
return feedback or "No feedback provided"
|
|
205
|
+
|
|
206
|
+
def _ask_user(self, question: str, options: Optional[list] = None) -> str:
|
|
207
|
+
"""Send ask_user event and block until user responds.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
question: Question to display
|
|
211
|
+
options: List of option strings (buttons in UI)
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
User's answer string
|
|
215
|
+
"""
|
|
216
|
+
io = self._get_io()
|
|
217
|
+
if not io:
|
|
218
|
+
return ""
|
|
219
|
+
|
|
220
|
+
# Send ask_user event
|
|
221
|
+
io.send({
|
|
222
|
+
"type": "ask_user",
|
|
223
|
+
"question": question,
|
|
224
|
+
"options": options,
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
# Block waiting for response
|
|
228
|
+
response = io.receive()
|
|
229
|
+
|
|
230
|
+
# Handle connection closed
|
|
231
|
+
if response.get("type") == "io_closed":
|
|
232
|
+
return ""
|
|
233
|
+
|
|
234
|
+
# Extract answer
|
|
235
|
+
answer = response.get("answer", "")
|
|
236
|
+
if isinstance(answer, list):
|
|
237
|
+
return ", ".join([str(a) for a in answer])
|
|
238
|
+
return str(answer) if answer is not None else ""
|
|
239
|
+
|
|
240
|
+
# =========================================================================
|
|
241
|
+
# Diff Generation
|
|
242
|
+
# =========================================================================
|
|
130
243
|
|
|
131
244
|
def _generate_diff(self, path: str, new_content: str) -> str:
|
|
132
245
|
"""Generate unified diff between existing file and new content."""
|
|
133
246
|
file_path = Path(path)
|
|
134
247
|
|
|
135
|
-
if file_path.exists():
|
|
136
|
-
|
|
137
|
-
else:
|
|
138
|
-
return "" # New file, no diff
|
|
248
|
+
if not file_path.exists():
|
|
249
|
+
return "" # New file, no diff to show
|
|
139
250
|
|
|
251
|
+
original_lines = file_path.read_text(encoding="utf-8").splitlines(keepends=True)
|
|
140
252
|
new_lines = new_content.splitlines(keepends=True)
|
|
141
253
|
|
|
142
254
|
diff = difflib.unified_diff(
|
|
@@ -146,47 +258,27 @@ class DiffWriter:
|
|
|
146
258
|
tofile=f"b/{path}",
|
|
147
259
|
)
|
|
148
260
|
|
|
149
|
-
return
|
|
150
|
-
|
|
151
|
-
def _display_diff(self, diff_text: str, path: str):
|
|
152
|
-
"""Display colorized diff."""
|
|
153
|
-
styled = Text()
|
|
154
|
-
|
|
155
|
-
for line in diff_text.splitlines():
|
|
156
|
-
if line.startswith('+++') or line.startswith('---'):
|
|
157
|
-
styled.append(line + '\n', style="bold white")
|
|
158
|
-
elif line.startswith('@@'):
|
|
159
|
-
styled.append(line + '\n', style="cyan")
|
|
160
|
-
elif line.startswith('+'):
|
|
161
|
-
styled.append(line + '\n', style="green")
|
|
162
|
-
elif line.startswith('-'):
|
|
163
|
-
styled.append(line + '\n', style="red")
|
|
164
|
-
else:
|
|
165
|
-
styled.append(line + '\n', style="dim")
|
|
166
|
-
|
|
167
|
-
panel = Panel(
|
|
168
|
-
styled,
|
|
169
|
-
title=f"[bold yellow]Changes to {path}[/]",
|
|
170
|
-
border_style="yellow",
|
|
171
|
-
padding=(0, 1)
|
|
172
|
-
)
|
|
173
|
-
self._console.print(panel)
|
|
174
|
-
|
|
175
|
-
def _display_new_file(self, path: str, content: str):
|
|
176
|
-
"""Display preview of new file."""
|
|
177
|
-
styled = Text()
|
|
261
|
+
return "".join(diff)
|
|
178
262
|
|
|
263
|
+
def _build_preview(self, diff_text: str, content: str, file_exists: bool) -> str:
|
|
264
|
+
"""Build human-readable preview for approval dialog."""
|
|
265
|
+
if diff_text:
|
|
266
|
+
return diff_text
|
|
267
|
+
if file_exists:
|
|
268
|
+
return "(no changes)"
|
|
269
|
+
return self._new_file_preview(content)
|
|
270
|
+
|
|
271
|
+
def _truncate_preview(self, preview: str) -> Tuple[str, bool]:
|
|
272
|
+
"""Truncate preview to configured limit."""
|
|
273
|
+
if len(preview) <= self.preview_limit:
|
|
274
|
+
return preview, False
|
|
275
|
+
return preview[: self.preview_limit] + "\n...(truncated)", True
|
|
276
|
+
|
|
277
|
+
def _new_file_preview(self, content: str, max_lines: int = 50) -> str:
|
|
278
|
+
"""Create preview for new file (no existing file to diff against)."""
|
|
179
279
|
lines = content.splitlines()
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
panel = Panel(
|
|
187
|
-
styled,
|
|
188
|
-
title=f"[bold yellow]New file: {path}[/]",
|
|
189
|
-
border_style="yellow",
|
|
190
|
-
padding=(0, 1)
|
|
191
|
-
)
|
|
192
|
-
self._console.print(panel)
|
|
280
|
+
preview_lines = lines[:max_lines]
|
|
281
|
+
preview = "\n".join([f"+ {line}" for line in preview_lines])
|
|
282
|
+
if len(lines) > max_lines:
|
|
283
|
+
preview += f"\n... ({len(lines) - max_lines} more lines)"
|
|
284
|
+
return preview
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Purpose: Edit tool for precise string replacement (Claude Code-style)
|
|
3
|
+
LLM-Note:
|
|
4
|
+
Dependencies: imports from [pathlib] | imported by [useful_tools/__init__]
|
|
5
|
+
Data flow: Agent calls edit(file_path, old_string, new_string) -> validates -> replaces -> writes
|
|
6
|
+
State/Effects: reads and writes file on filesystem
|
|
7
|
+
Integration: exposes edit(file_path, old_string, new_string) function | used as agent tool
|
|
8
|
+
Errors: returns error if file not found | returns error if old_string not found | returns error if old_string not unique
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
edit("app.py", "def foo():", "def bar():")
|
|
12
|
+
edit("config.json", '"debug": false', '"debug": true')
|
|
13
|
+
edit("README.md", "v1.0", "v2.0", replace_all=True)
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def edit(
|
|
20
|
+
file_path: str,
|
|
21
|
+
old_string: str,
|
|
22
|
+
new_string: str,
|
|
23
|
+
replace_all: bool = False,
|
|
24
|
+
) -> str:
|
|
25
|
+
"""
|
|
26
|
+
Replace a string in a file with precise matching.
|
|
27
|
+
|
|
28
|
+
More token-efficient than rewriting entire files. Use for small, targeted changes.
|
|
29
|
+
The old_string must exist in the file (and be unique unless replace_all=True).
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
file_path: Path to the file to edit
|
|
33
|
+
old_string: Exact string to replace (must be unique in file)
|
|
34
|
+
new_string: String to replace with
|
|
35
|
+
replace_all: If True, replace all occurrences; if False, old_string must be unique
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Success message or error description
|
|
39
|
+
|
|
40
|
+
Examples:
|
|
41
|
+
edit("app.py", "def foo():", "def bar():")
|
|
42
|
+
edit("config.json", '"debug": false', '"debug": true')
|
|
43
|
+
edit("README.md", "v1.0", "v2.0", replace_all=True)
|
|
44
|
+
"""
|
|
45
|
+
path = Path(file_path)
|
|
46
|
+
|
|
47
|
+
if not path.exists():
|
|
48
|
+
return f"Error: File '{file_path}' does not exist"
|
|
49
|
+
|
|
50
|
+
if not path.is_file():
|
|
51
|
+
return f"Error: '{file_path}' is not a file"
|
|
52
|
+
|
|
53
|
+
content = path.read_text(encoding="utf-8")
|
|
54
|
+
|
|
55
|
+
# Check if old_string exists
|
|
56
|
+
count = content.count(old_string)
|
|
57
|
+
|
|
58
|
+
if count == 0:
|
|
59
|
+
# Try to help debug: show similar strings
|
|
60
|
+
lines_with_similar = []
|
|
61
|
+
for i, line in enumerate(content.splitlines(), 1):
|
|
62
|
+
# Check if any significant part of old_string is in the line
|
|
63
|
+
if len(old_string) > 10:
|
|
64
|
+
# For longer strings, check first 20 chars
|
|
65
|
+
if old_string[:20] in line or old_string[-20:] in line:
|
|
66
|
+
lines_with_similar.append(f" Line {i}: {line[:80]}")
|
|
67
|
+
elif old_string.strip() in line:
|
|
68
|
+
lines_with_similar.append(f" Line {i}: {line[:80]}")
|
|
69
|
+
|
|
70
|
+
msg = f"Error: String not found in '{file_path}'"
|
|
71
|
+
if lines_with_similar:
|
|
72
|
+
msg += f"\n\nSimilar content found:\n" + "\n".join(lines_with_similar[:5])
|
|
73
|
+
return msg
|
|
74
|
+
|
|
75
|
+
if count > 1 and not replace_all:
|
|
76
|
+
# Show where the duplicates are
|
|
77
|
+
lines_with_match = []
|
|
78
|
+
for i, line in enumerate(content.splitlines(), 1):
|
|
79
|
+
if old_string in line:
|
|
80
|
+
lines_with_match.append(f" Line {i}: {line[:80]}")
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
f"Error: String appears {count} times in '{file_path}'. "
|
|
84
|
+
f"Use replace_all=True to replace all, or provide more context to make it unique.\n\n"
|
|
85
|
+
f"Found at:\n" + "\n".join(lines_with_match[:10])
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Perform replacement
|
|
89
|
+
if replace_all:
|
|
90
|
+
new_content = content.replace(old_string, new_string)
|
|
91
|
+
replaced_count = count
|
|
92
|
+
else:
|
|
93
|
+
new_content = content.replace(old_string, new_string, 1)
|
|
94
|
+
replaced_count = 1
|
|
95
|
+
|
|
96
|
+
# Write back
|
|
97
|
+
path.write_text(new_content, encoding="utf-8")
|
|
98
|
+
|
|
99
|
+
if replace_all and replaced_count > 1:
|
|
100
|
+
return f"Replaced {replaced_count} occurrences in '{file_path}'"
|
|
101
|
+
else:
|
|
102
|
+
return f"Successfully edited '{file_path}'"
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Purpose: Glob tool for file pattern matching
|
|
3
|
+
LLM-Note:
|
|
4
|
+
Dependencies: imports from [pathlib, typing] | imported by [useful_tools/__init__, grep]
|
|
5
|
+
Data flow: Agent calls glob(pattern) -> searches directory -> returns matching paths
|
|
6
|
+
State/Effects: reads filesystem (no writes)
|
|
7
|
+
Integration: exposes glob(pattern, path) function | used as agent tool | shared IGNORE_DIRS constant
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
glob("**/*.py") # All Python files
|
|
11
|
+
glob("src/**/*.tsx") # All TSX files in src/
|
|
12
|
+
glob("**/test_*.py") # All test files
|
|
13
|
+
glob("*.md", "docs") # Markdown files in docs/
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Optional
|
|
18
|
+
|
|
19
|
+
IGNORE_DIRS = {
|
|
20
|
+
".git",
|
|
21
|
+
"node_modules",
|
|
22
|
+
"__pycache__",
|
|
23
|
+
".venv",
|
|
24
|
+
"venv",
|
|
25
|
+
".env",
|
|
26
|
+
"dist",
|
|
27
|
+
"build",
|
|
28
|
+
".next",
|
|
29
|
+
".nuxt",
|
|
30
|
+
"target",
|
|
31
|
+
".idea",
|
|
32
|
+
".vscode",
|
|
33
|
+
"*.egg-info",
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def glob(pattern: str, path: Optional[str] = None) -> str:
|
|
38
|
+
"""
|
|
39
|
+
Search for files matching a glob pattern.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
pattern: Glob pattern (e.g., "**/*.py", "src/**/*.ts")
|
|
43
|
+
path: Directory to search in (default: current directory)
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Matching file paths, one per line, sorted by modification time (newest first)
|
|
47
|
+
|
|
48
|
+
Examples:
|
|
49
|
+
glob("**/*.py") # All Python files
|
|
50
|
+
glob("src/**/*.tsx") # All TSX files in src/
|
|
51
|
+
glob("**/test_*.py") # All test files
|
|
52
|
+
glob("*.md", "docs") # Markdown files in docs/
|
|
53
|
+
"""
|
|
54
|
+
base = Path(path) if path else Path.cwd()
|
|
55
|
+
|
|
56
|
+
if not base.exists():
|
|
57
|
+
return f"Error: Path '{base}' does not exist"
|
|
58
|
+
|
|
59
|
+
if not base.is_dir():
|
|
60
|
+
return f"Error: Path '{base}' is not a directory"
|
|
61
|
+
|
|
62
|
+
matches = []
|
|
63
|
+
for p in base.glob(pattern):
|
|
64
|
+
if p.is_file() and not _should_ignore(p):
|
|
65
|
+
matches.append(p)
|
|
66
|
+
|
|
67
|
+
if not matches:
|
|
68
|
+
return f"No files found matching '{pattern}'"
|
|
69
|
+
|
|
70
|
+
# Sort by modification time (newest first)
|
|
71
|
+
matches.sort(key=lambda p: p.stat().st_mtime, reverse=True)
|
|
72
|
+
|
|
73
|
+
# Format output
|
|
74
|
+
results = []
|
|
75
|
+
for p in matches[:100]: # Limit to 100 results
|
|
76
|
+
rel_path = p.relative_to(base) if path else p.relative_to(Path.cwd())
|
|
77
|
+
results.append(str(rel_path))
|
|
78
|
+
|
|
79
|
+
output = "\n".join(results)
|
|
80
|
+
|
|
81
|
+
if len(matches) > 100:
|
|
82
|
+
output += f"\n\n... and {len(matches) - 100} more files"
|
|
83
|
+
|
|
84
|
+
return output
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _should_ignore(path: Path) -> bool:
|
|
88
|
+
"""Check if path should be ignored."""
|
|
89
|
+
parts = path.parts
|
|
90
|
+
for part in parts:
|
|
91
|
+
if part in IGNORE_DIRS:
|
|
92
|
+
return True
|
|
93
|
+
# Handle patterns like *.egg-info
|
|
94
|
+
for ignore in IGNORE_DIRS:
|
|
95
|
+
if "*" in ignore and Path(part).match(ignore):
|
|
96
|
+
return True
|
|
97
|
+
return False
|