agent-cli 0.80.0__tar.gz → 0.82.0__tar.gz
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.
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.pre-commit-config.yaml +1 -1
- {agent_cli-0.80.0 → agent_cli-0.82.0}/PKG-INFO +1 -1
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_tools.py +1 -1
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/chat.py +2 -2
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/transcribe_live.py +1 -1
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/config.py +21 -5
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/deps.py +1 -1
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/watch.py +1 -1
- agent_cli-0.82.0/agent_cli/dev/_branch_name.py +371 -0
- agent_cli-0.82.0/agent_cli/dev/_output.py +37 -0
- agent_cli-0.82.0/agent_cli/dev/cleanup.py +112 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/cli.py +243 -606
- agent_cli-0.82.0/agent_cli/dev/launch.py +295 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/worktree.py +2 -2
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/docs_gen.py +1 -1
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/install/service_config.py +1 -1
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/_indexer.py +6 -2
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/_indexing.py +17 -3
- agent_cli-0.82.0/agent_cli/rag/_utils.py +461 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/engine.py +3 -4
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/nvidia-asr-server/server.py +2 -2
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/backends/transformers.py +1 -1
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/dev.md +12 -1
- {agent_cli-0.80.0 → agent_cli-0.82.0}/pyproject.toml +4 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/core/test_watch.py +1 -1
- agent_cli-0.82.0/tests/dev/test_cli.py +958 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/dev/test_worktree.py +7 -7
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_engine.py +36 -3
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_indexer.py +9 -3
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_utils.py +172 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_api_integration.py +1 -1
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_asr_recovery.py +3 -3
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_config.py +53 -0
- agent_cli-0.80.0/agent_cli/rag/_utils.py +0 -218
- agent_cli-0.80.0/tests/dev/test_cli.py +0 -496
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.claude/skills/agent-cli-dev/SKILL.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.claude/skills/agent-cli-dev/examples.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.claude-plugin/README.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.claude-plugin/marketplace.json +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.claude-plugin/plugin.json +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.claude-plugin/skills/agent-cli-dev/SKILL.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.claude-plugin/skills/agent-cli-dev/examples.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.cursorrules +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.dockerignore +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.env.example +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/logo.svg +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/release-drafter.yml +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/renovate.json +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/scripts/check_extras_sync.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/scripts/check_plugin_skill_sync.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/scripts/sync_extras.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/scripts/sync_requirements.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/workflows/automerge.yml +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/workflows/docker.yml +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/workflows/docs.yml +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/workflows/markdown-code-runner.yml +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/workflows/pytest.yml +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/workflows/release-drafter.yml +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/workflows/release.yml +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/workflows/toc.yaml +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.gitignore +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.jscpd.json +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.prompts/docs-review.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/.prompts/pr-review.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/CLAUDE.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/LICENSE +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/README.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/__main__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_extras.json +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/.gitkeep +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/audio.txt +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/faster-whisper.txt +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/kokoro.txt +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/llm.txt +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/memory.txt +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/mlx-whisper.txt +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/piper.txt +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/rag.txt +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/server.txt +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/speed.txt +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/vad.txt +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/whisper-transformers.txt +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/wyoming.txt +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/_voice_agent_common.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/assistant.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/autocorrect.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/memory/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/memory/add.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/memory/proxy.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/rag_proxy.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/speak.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/transcribe.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/voice_edit.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/api.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/cli.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/config_cmd.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/constants.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/audio.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/audio_format.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/chroma.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/openai_proxy.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/process.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/reranker.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/sse.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/transcription_logger.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/utils.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/vad.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/daemon/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/daemon/cli.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/aider.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/base.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/claude.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/codex.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/continue_dev.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/copilot.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/cursor_agent.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/gemini.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/opencode.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/registry.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/base.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/cursor.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/emacs.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/jetbrains.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/nano.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/neovim.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/registry.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/sublime.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/vim.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/vscode.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/zed.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/project.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/registry.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/skill/SKILL.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/skill/examples.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/apple_terminal.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/base.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/gnome.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/iterm2.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/kitty.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/registry.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/tmux.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/warp.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/zellij.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/example-config.toml +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/install/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/install/common.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/install/extras.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/install/hotkeys.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/install/launchd.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/install/services.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/install/systemd.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_files.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_filters.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_git.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_indexer.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_ingest.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_persistence.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_prompt.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_retrieval.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_store.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_streaming.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_tasks.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/api.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/client.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/engine.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/entities.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/models.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/opts.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/py.typed +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/_prompt.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/_retriever.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/_store.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/api.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/client.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/models.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/.runtime/.gitkeep +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/linux-hotkeys/README.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/linux-hotkeys/toggle-autocorrect.sh +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/linux-hotkeys/toggle-transcription.sh +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/linux-hotkeys/toggle-voice-edit.sh +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/macos-hotkeys/README.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/macos-hotkeys/skhd-config-example +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/macos-hotkeys/toggle-autocorrect.sh +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/macos-hotkeys/toggle-transcription.sh +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/macos-hotkeys/toggle-voice-edit.sh +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/nvidia-asr-server/README.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/nvidia-asr-server/pyproject.toml +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/nvidia-asr-server/shell.nix +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/nvidia-asr-server/uv.lock +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/run-openwakeword.sh +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/setup-linux-hotkeys.sh +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/setup-linux.sh +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/setup-macos-hotkeys.sh +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/setup-macos.sh +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/setup-windows.ps1 +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/start-all-services-windows.ps1 +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/start-all-services.sh +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/cli.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/common.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/model_manager.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/model_registry.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/proxy/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/proxy/api.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/streaming.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/tts/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/tts/api.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/tts/backends/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/tts/backends/base.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/tts/backends/kokoro.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/tts/backends/piper.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/tts/model_manager.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/tts/model_registry.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/tts/wyoming_handler.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/api.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/backends/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/backends/base.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/backends/faster_whisper.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/backends/mlx.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/languages.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/model_manager.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/model_registry.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/wyoming_handler.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/services/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/services/_wyoming_utils.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/services/asr.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/services/llm.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/services/tts.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/services/wake_word.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docker/docker-compose.yml +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docker/memory-proxy.Dockerfile +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docker/rag-proxy.Dockerfile +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docker/transcribe-proxy.Dockerfile +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docker/tts.Dockerfile +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docker/whisper.Dockerfile +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/CNAME +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/architecture/index.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/architecture/memory.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/architecture/rag.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/assistant.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/autocorrect.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/chat.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/config.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/daemon.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/index.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/install-extras.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/install-hotkeys.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/install-services.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/memory.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/rag-proxy.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/server/index.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/server/transcribe-proxy.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/server/tts.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/server/whisper.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/speak.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/start-services.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/transcribe-live.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/transcribe.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/voice-edit.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/configuration.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/getting-started.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/iOS_Shortcut_Guide.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/index.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/installation/docker.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/installation/index.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/installation/linux.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/installation/macos.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/installation/nixos.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/installation/windows.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/logo-clean.svg +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/overrides/partials/integrations/analytics/custom.html +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/run_markdown_code_runner.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/system-integration.md +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/example.agent-cli-config.toml +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/justfile +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/shell.nix +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_fix_my_text.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_interactive.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_interactive_extra.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_memory_add.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_speak.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_speak_e2e.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_transcribe.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_transcribe_agent.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_transcribe_e2e.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_transcribe_live.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_transcribe_recovery.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_tts_common.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_tts_common_extra.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_voice_agent_common.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_voice_edit.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_voice_edit_e2e.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_wake_word_assistant.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/conftest.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/core/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/core/test_audio_format.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/core/test_chroma.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/core/test_sse.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/core/test_vad.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/dev/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/dev/test_coding_agents.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/dev/test_editors.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/dev/test_project.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/dev/test_terminals.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/dev/test_verification.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/install/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/install/test_extras.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_api_health.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_api_integration_liveish.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_client.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_engine.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_files.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_filters.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_git_integration.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_indexer.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_memory_integration.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_proxy_passthrough.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_store.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_utils.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/mocks/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/mocks/audio.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/mocks/llm.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/mocks/wyoming.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/__init__.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_api.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_history.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_indexing.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_rag_client.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_rag_integration_liveish.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_rag_proxy_passthrough.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_retriever.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_store.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_api.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_asr.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_audio_e2e.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_cli.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_config_cmd.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_daemon.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_docs_gen.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_env_vars.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_json_output.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_llm.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_llm_gemini.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_memory_tools.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_mlx_backend.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_process_manager.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_requires_extras.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_server_streaming.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_server_tts.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_server_whisper.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_services.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_tools.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_transformers_backend.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_tts.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_utils.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_wake_word.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_wyoming_utils.py +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/uv.lock +0 -0
- {agent_cli-0.80.0 → agent_cli-0.82.0}/zensical.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.82.0
|
|
4
4
|
Summary: A suite of AI-powered command-line tools for text correction, audio transcription, and voice assistance.
|
|
5
5
|
Project-URL: Homepage, https://github.com/basnijholt/agent-cli
|
|
6
6
|
Author-email: Bas Nijholt <bas@nijho.lt>
|
|
@@ -130,7 +130,7 @@ def execute_code(code: str) -> str:
|
|
|
130
130
|
except subprocess.CalledProcessError as e:
|
|
131
131
|
return f"Error executing code: {e.stderr}"
|
|
132
132
|
except FileNotFoundError:
|
|
133
|
-
return f"Error: Command not found: {code.split()[0]}"
|
|
133
|
+
return f"Error: Command not found: {code.split(maxsplit=1)[0]}"
|
|
134
134
|
|
|
135
135
|
|
|
136
136
|
def add_memory(content: str, category: str = "general", tags: str = "") -> str:
|
|
@@ -272,7 +272,7 @@ async def _handle_conversation_turn(
|
|
|
272
272
|
|
|
273
273
|
# 6. Save history
|
|
274
274
|
if history_cfg.history_dir:
|
|
275
|
-
history_path = Path(history_cfg.history_dir).expanduser()
|
|
275
|
+
history_path = Path(history_cfg.history_dir).expanduser() # noqa: ASYNC240
|
|
276
276
|
history_path.mkdir(parents=True, exist_ok=True)
|
|
277
277
|
# Share the history directory with the memory tools
|
|
278
278
|
os.environ["AGENT_CLI_HISTORY_DIR"] = str(history_path)
|
|
@@ -335,7 +335,7 @@ async def _async_main(
|
|
|
335
335
|
# Load conversation history
|
|
336
336
|
conversation_history = []
|
|
337
337
|
if history_cfg.history_dir:
|
|
338
|
-
history_path = Path(history_cfg.history_dir).expanduser()
|
|
338
|
+
history_path = Path(history_cfg.history_dir).expanduser() # noqa: ASYNC240
|
|
339
339
|
history_path.mkdir(parents=True, exist_ok=True)
|
|
340
340
|
# Share the history directory with the memory tools
|
|
341
341
|
os.environ["AGENT_CLI_HISTORY_DIR"] = str(history_path)
|
|
@@ -188,7 +188,7 @@ async def _process_segment( # noqa: PLR0912
|
|
|
188
188
|
if cfg.clipboard:
|
|
189
189
|
import pyperclip # noqa: PLC0415
|
|
190
190
|
|
|
191
|
-
text_to_copy = processed
|
|
191
|
+
text_to_copy = processed or transcript
|
|
192
192
|
pyperclip.copy(text_to_copy)
|
|
193
193
|
|
|
194
194
|
# Log
|
|
@@ -264,6 +264,9 @@ class Dev(BaseModel):
|
|
|
264
264
|
|
|
265
265
|
default_agent: str | None = None
|
|
266
266
|
default_editor: str | None = None
|
|
267
|
+
branch_name_mode: Literal["random", "auto", "ai"] = "random"
|
|
268
|
+
branch_name_agent: Literal["claude", "codex", "gemini"] | None = None
|
|
269
|
+
branch_name_timeout: float = 20.0 # seconds
|
|
267
270
|
agent_args: dict[str, list[str]] | None = (
|
|
268
271
|
None # Per-agent args, e.g. {"claude": ["--dangerously-skip-permissions"]}
|
|
269
272
|
)
|
|
@@ -319,17 +322,30 @@ def normalize_provider_defaults(cfg: dict[str, Any]) -> dict[str, Any]:
|
|
|
319
322
|
return normalized
|
|
320
323
|
|
|
321
324
|
|
|
322
|
-
def _replace_dashed_keys(cfg:
|
|
323
|
-
|
|
325
|
+
def _replace_dashed_keys(cfg: Any) -> Any:
|
|
326
|
+
"""Recursively replace dashed keys in dictionaries."""
|
|
327
|
+
if isinstance(cfg, dict):
|
|
328
|
+
return {k.replace("-", "_"): _replace_dashed_keys(v) for k, v in cfg.items()}
|
|
329
|
+
return cfg
|
|
324
330
|
|
|
325
331
|
|
|
326
332
|
def _flatten_nested_sections(cfg: dict[str, Any], prefix: str = "") -> dict[str, Any]:
|
|
327
|
-
"""Flatten nested TOML sections
|
|
328
|
-
|
|
333
|
+
"""Flatten nested TOML sections while preserving scalar parent options.
|
|
334
|
+
|
|
335
|
+
Example:
|
|
336
|
+
{"a": {"x": 1, "b": {"y": 2}}} -> {"a": {"x": 1}, "a.b": {"y": 2}}
|
|
337
|
+
{"a": {"b": {"x": 1}}} -> {"a.b": {"x": 1}}
|
|
338
|
+
|
|
339
|
+
"""
|
|
340
|
+
result: dict[str, Any] = {}
|
|
329
341
|
for key, value in cfg.items():
|
|
330
342
|
full_key = f"{prefix}.{key}" if prefix else key
|
|
331
343
|
if isinstance(value, dict) and any(isinstance(v, dict) for v in value.values()):
|
|
332
|
-
|
|
344
|
+
scalar_items = {k: v for k, v in value.items() if not isinstance(v, dict)}
|
|
345
|
+
if scalar_items:
|
|
346
|
+
result[full_key] = scalar_items
|
|
347
|
+
dict_items = {k: v for k, v in value.items() if isinstance(v, dict)}
|
|
348
|
+
result.update(_flatten_nested_sections(dict_items, full_key))
|
|
333
349
|
else:
|
|
334
350
|
result[full_key] = value
|
|
335
351
|
return result
|
|
@@ -61,7 +61,7 @@ EXTRAS: dict[str, tuple[str, list[str]]] = {
|
|
|
61
61
|
|
|
62
62
|
def _check_package_installed(pkg: str) -> bool:
|
|
63
63
|
"""Check if a single package is installed."""
|
|
64
|
-
top_module = pkg.split(".")[0]
|
|
64
|
+
top_module = pkg.split(".", maxsplit=1)[0]
|
|
65
65
|
try:
|
|
66
66
|
return find_spec(top_module) is not None
|
|
67
67
|
except (ValueError, ModuleNotFoundError):
|
|
@@ -53,7 +53,7 @@ async def watch_directory(
|
|
|
53
53
|
async for changes in awatch(root):
|
|
54
54
|
for change_type, file_path_str in changes:
|
|
55
55
|
path = Path(file_path_str)
|
|
56
|
-
if path.is_dir():
|
|
56
|
+
if path.is_dir(): # noqa: ASYNC240
|
|
57
57
|
continue
|
|
58
58
|
|
|
59
59
|
if should_skip is not None and should_skip(path, root):
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
"""Branch name generation for dev worktrees."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import random
|
|
7
|
+
import re
|
|
8
|
+
import shutil
|
|
9
|
+
import subprocess
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
from agent_cli.core.utils import err_console
|
|
13
|
+
|
|
14
|
+
from . import worktree
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from collections.abc import Callable
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
AGENTS: tuple[str, ...] = ("claude", "codex", "gemini")
|
|
21
|
+
|
|
22
|
+
_ADJECTIVES = [
|
|
23
|
+
"happy",
|
|
24
|
+
"clever",
|
|
25
|
+
"swift",
|
|
26
|
+
"bright",
|
|
27
|
+
"calm",
|
|
28
|
+
"eager",
|
|
29
|
+
"fancy",
|
|
30
|
+
"gentle",
|
|
31
|
+
"jolly",
|
|
32
|
+
"keen",
|
|
33
|
+
"lively",
|
|
34
|
+
"merry",
|
|
35
|
+
"nice",
|
|
36
|
+
"proud",
|
|
37
|
+
"quick",
|
|
38
|
+
"sharp",
|
|
39
|
+
"smart",
|
|
40
|
+
"sunny",
|
|
41
|
+
"witty",
|
|
42
|
+
"zesty",
|
|
43
|
+
"bold",
|
|
44
|
+
"cool",
|
|
45
|
+
"fresh",
|
|
46
|
+
"grand",
|
|
47
|
+
]
|
|
48
|
+
_NOUNS = [
|
|
49
|
+
"fox",
|
|
50
|
+
"owl",
|
|
51
|
+
"bear",
|
|
52
|
+
"wolf",
|
|
53
|
+
"hawk",
|
|
54
|
+
"lion",
|
|
55
|
+
"tiger",
|
|
56
|
+
"eagle",
|
|
57
|
+
"falcon",
|
|
58
|
+
"otter",
|
|
59
|
+
"panda",
|
|
60
|
+
"raven",
|
|
61
|
+
"shark",
|
|
62
|
+
"whale",
|
|
63
|
+
"zebra",
|
|
64
|
+
"bison",
|
|
65
|
+
"crane",
|
|
66
|
+
"dolphin",
|
|
67
|
+
"gecko",
|
|
68
|
+
"heron",
|
|
69
|
+
"koala",
|
|
70
|
+
"lemur",
|
|
71
|
+
"moose",
|
|
72
|
+
"newt",
|
|
73
|
+
"oriole",
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
_MAX_BRANCH_NAME_LEN = 80
|
|
77
|
+
_MAX_BRANCH_TASK_LEN = 1200
|
|
78
|
+
_CLAUDE_BRANCH_SCHEMA = json.dumps(
|
|
79
|
+
{
|
|
80
|
+
"type": "object",
|
|
81
|
+
"properties": {
|
|
82
|
+
"branch": {
|
|
83
|
+
"type": "string",
|
|
84
|
+
"pattern": r"^[a-z0-9][a-z0-9._/-]{1,79}$",
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
"required": ["branch"],
|
|
88
|
+
"additionalProperties": False,
|
|
89
|
+
},
|
|
90
|
+
separators=(",", ":"),
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _branch_exists_in_repo(repo_root: Path, branch_name: str) -> bool:
|
|
95
|
+
"""Check whether a branch already exists locally or on origin."""
|
|
96
|
+
return any(worktree.check_branch_exists(branch_name, repo_root))
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _ensure_unique_branch_name(
|
|
100
|
+
base_name: str,
|
|
101
|
+
existing_branches: set[str] | None = None,
|
|
102
|
+
*,
|
|
103
|
+
repo_root: Path | None = None,
|
|
104
|
+
) -> str:
|
|
105
|
+
"""Add a numeric suffix when a branch name collides."""
|
|
106
|
+
existing = existing_branches or set()
|
|
107
|
+
|
|
108
|
+
def is_available(candidate: str) -> bool:
|
|
109
|
+
if candidate in existing:
|
|
110
|
+
return False
|
|
111
|
+
return repo_root is None or not _branch_exists_in_repo(repo_root, candidate)
|
|
112
|
+
|
|
113
|
+
if is_available(base_name):
|
|
114
|
+
return base_name
|
|
115
|
+
|
|
116
|
+
for i in range(2, 100):
|
|
117
|
+
candidate = f"{base_name}-{i}"
|
|
118
|
+
if is_available(candidate):
|
|
119
|
+
return candidate
|
|
120
|
+
|
|
121
|
+
for _ in range(20):
|
|
122
|
+
candidate = f"{base_name}-{random.randint(100, 999)}" # noqa: S311
|
|
123
|
+
if is_available(candidate):
|
|
124
|
+
return candidate
|
|
125
|
+
|
|
126
|
+
# Last resort: large range, unchecked (98 sequential + 20 random exhausted)
|
|
127
|
+
return f"{base_name}-{random.randint(1000, 9999)}" # noqa: S311
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _parse_json_lines(output: str) -> list[dict[str, object]]:
|
|
131
|
+
"""Parse JSONL output and ignore non-JSON lines."""
|
|
132
|
+
parsed: list[dict[str, object]] = []
|
|
133
|
+
for raw_line in output.splitlines():
|
|
134
|
+
stripped_line = raw_line.strip()
|
|
135
|
+
if not stripped_line:
|
|
136
|
+
continue
|
|
137
|
+
try:
|
|
138
|
+
item = json.loads(stripped_line)
|
|
139
|
+
except json.JSONDecodeError:
|
|
140
|
+
continue
|
|
141
|
+
if isinstance(item, dict):
|
|
142
|
+
parsed.append(item)
|
|
143
|
+
return parsed
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _extract_branch_from_claude_output(output: str) -> str | None:
|
|
147
|
+
"""Extract branch name from `claude -p --output-format json` output."""
|
|
148
|
+
for event in reversed(_parse_json_lines(output)):
|
|
149
|
+
structured = event.get("structured_output")
|
|
150
|
+
if isinstance(structured, dict):
|
|
151
|
+
branch = structured.get("branch")
|
|
152
|
+
if isinstance(branch, str) and branch.strip():
|
|
153
|
+
return branch
|
|
154
|
+
result = event.get("result")
|
|
155
|
+
if isinstance(result, str) and result.strip():
|
|
156
|
+
return result
|
|
157
|
+
return None
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _extract_branch_from_codex_output(output: str) -> str | None:
|
|
161
|
+
"""Extract branch name from `codex exec --json` output."""
|
|
162
|
+
branch: str | None = None
|
|
163
|
+
for event in _parse_json_lines(output):
|
|
164
|
+
if event.get("type") != "item.completed":
|
|
165
|
+
continue
|
|
166
|
+
item = event.get("item")
|
|
167
|
+
if not isinstance(item, dict):
|
|
168
|
+
continue
|
|
169
|
+
if item.get("type") != "agent_message":
|
|
170
|
+
continue
|
|
171
|
+
text = item.get("text")
|
|
172
|
+
if isinstance(text, str) and text.strip():
|
|
173
|
+
branch = text
|
|
174
|
+
return branch
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _extract_branch_from_gemini_output(output: str) -> str | None:
|
|
178
|
+
"""Extract branch name from `gemini -p -o json` output."""
|
|
179
|
+
for raw_line in output.splitlines():
|
|
180
|
+
stripped = raw_line.strip()
|
|
181
|
+
if not stripped:
|
|
182
|
+
continue
|
|
183
|
+
try:
|
|
184
|
+
item = json.loads(stripped)
|
|
185
|
+
except json.JSONDecodeError:
|
|
186
|
+
continue
|
|
187
|
+
if isinstance(item, dict):
|
|
188
|
+
response = item.get("response")
|
|
189
|
+
if isinstance(response, str) and response.strip():
|
|
190
|
+
return response
|
|
191
|
+
return None
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _normalize_ai_branch_candidate(candidate: str, repo_root: Path) -> str | None:
|
|
195
|
+
"""Normalize model output into a safe branch slug."""
|
|
196
|
+
lines = [line.strip() for line in candidate.replace("`", "").splitlines() if line.strip()]
|
|
197
|
+
if not lines:
|
|
198
|
+
return None
|
|
199
|
+
|
|
200
|
+
branch = lines[0].strip().strip("'\"")
|
|
201
|
+
branch = re.sub(r"^(branch|name)\s*:\s*", "", branch, flags=re.IGNORECASE)
|
|
202
|
+
branch = branch.lower()
|
|
203
|
+
branch = re.sub(r"\s+", "-", branch)
|
|
204
|
+
branch = re.sub(r"[^a-z0-9._/-]", "-", branch)
|
|
205
|
+
branch = re.sub(r"/{2,}", "/", branch)
|
|
206
|
+
branch = re.sub(r"-{2,}", "-", branch)
|
|
207
|
+
branch = branch.strip("./-")
|
|
208
|
+
if len(branch) > _MAX_BRANCH_NAME_LEN:
|
|
209
|
+
branch = branch[:_MAX_BRANCH_NAME_LEN].rstrip("./-")
|
|
210
|
+
if not branch:
|
|
211
|
+
return None
|
|
212
|
+
|
|
213
|
+
try:
|
|
214
|
+
result = subprocess.run(
|
|
215
|
+
["git", "check-ref-format", "--branch", branch], # noqa: S607
|
|
216
|
+
cwd=repo_root,
|
|
217
|
+
check=False,
|
|
218
|
+
capture_output=True,
|
|
219
|
+
text=True,
|
|
220
|
+
)
|
|
221
|
+
except OSError:
|
|
222
|
+
return None
|
|
223
|
+
return branch if result.returncode == 0 else None
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _build_branch_naming_prompt(
|
|
227
|
+
repo_root: Path,
|
|
228
|
+
prompt: str | None,
|
|
229
|
+
from_ref: str | None,
|
|
230
|
+
) -> str:
|
|
231
|
+
"""Build a constrained prompt for branch name generation."""
|
|
232
|
+
task = (prompt or "").strip()
|
|
233
|
+
if not task:
|
|
234
|
+
task = "General maintenance task."
|
|
235
|
+
if len(task) > _MAX_BRANCH_TASK_LEN:
|
|
236
|
+
task = task[:_MAX_BRANCH_TASK_LEN] + "..."
|
|
237
|
+
|
|
238
|
+
base_ref = from_ref or "default branch"
|
|
239
|
+
return (
|
|
240
|
+
"Generate exactly one git branch name.\n"
|
|
241
|
+
"Return only the branch name and nothing else.\n"
|
|
242
|
+
"Do not use tools, do not inspect files, and do not ask follow-up questions.\n"
|
|
243
|
+
"Rules:\n"
|
|
244
|
+
"- lowercase ascii only\n"
|
|
245
|
+
"- allowed characters: a-z 0-9 / - _ .\n"
|
|
246
|
+
"- no spaces, no backticks, no explanation\n"
|
|
247
|
+
"- max 80 characters\n"
|
|
248
|
+
f"Repository: {repo_root.name}\n"
|
|
249
|
+
f"Base ref: {base_ref}\n"
|
|
250
|
+
f"Task: {task}\n"
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _generate_branch_name_with_agent(
|
|
255
|
+
agent_name: str,
|
|
256
|
+
repo_root: Path,
|
|
257
|
+
prompt: str | None,
|
|
258
|
+
from_ref: str | None,
|
|
259
|
+
timeout_seconds: float,
|
|
260
|
+
) -> str | None:
|
|
261
|
+
"""Run a headless agent to generate a branch name."""
|
|
262
|
+
naming_prompt = _build_branch_naming_prompt(repo_root, prompt, from_ref)
|
|
263
|
+
|
|
264
|
+
agent_commands: dict[str, tuple[list[str], Callable[[str], str | None]]] = {
|
|
265
|
+
"claude": (
|
|
266
|
+
[
|
|
267
|
+
"claude",
|
|
268
|
+
"-p",
|
|
269
|
+
"--output-format",
|
|
270
|
+
"json",
|
|
271
|
+
"--permission-mode",
|
|
272
|
+
"plan",
|
|
273
|
+
"--no-session-persistence",
|
|
274
|
+
"--json-schema",
|
|
275
|
+
_CLAUDE_BRANCH_SCHEMA,
|
|
276
|
+
naming_prompt,
|
|
277
|
+
],
|
|
278
|
+
_extract_branch_from_claude_output,
|
|
279
|
+
),
|
|
280
|
+
"codex": (
|
|
281
|
+
[
|
|
282
|
+
"codex",
|
|
283
|
+
"-a",
|
|
284
|
+
"never",
|
|
285
|
+
"exec",
|
|
286
|
+
"-s",
|
|
287
|
+
"read-only",
|
|
288
|
+
"--json",
|
|
289
|
+
naming_prompt,
|
|
290
|
+
],
|
|
291
|
+
_extract_branch_from_codex_output,
|
|
292
|
+
),
|
|
293
|
+
"gemini": (
|
|
294
|
+
["gemini", "-p", naming_prompt, "-o", "json"],
|
|
295
|
+
_extract_branch_from_gemini_output,
|
|
296
|
+
),
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
entry = agent_commands.get(agent_name)
|
|
300
|
+
if entry is None:
|
|
301
|
+
return None
|
|
302
|
+
command, extractor = entry
|
|
303
|
+
|
|
304
|
+
try:
|
|
305
|
+
result = subprocess.run(
|
|
306
|
+
command,
|
|
307
|
+
check=False,
|
|
308
|
+
capture_output=True,
|
|
309
|
+
text=True,
|
|
310
|
+
timeout=timeout_seconds,
|
|
311
|
+
cwd=repo_root,
|
|
312
|
+
)
|
|
313
|
+
except (OSError, subprocess.TimeoutExpired):
|
|
314
|
+
return None
|
|
315
|
+
if result.returncode != 0:
|
|
316
|
+
return None
|
|
317
|
+
|
|
318
|
+
raw_branch = extractor(result.stdout)
|
|
319
|
+
if not raw_branch:
|
|
320
|
+
return None
|
|
321
|
+
return _normalize_ai_branch_candidate(raw_branch, repo_root)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def generate_ai_branch_name(
|
|
325
|
+
repo_root: Path,
|
|
326
|
+
existing_branches: set[str],
|
|
327
|
+
prompt: str | None,
|
|
328
|
+
from_ref: str | None,
|
|
329
|
+
preferred_agent: str | None,
|
|
330
|
+
timeout_seconds: float,
|
|
331
|
+
) -> str | None:
|
|
332
|
+
"""Generate an AI branch name, trying available agents in order."""
|
|
333
|
+
if preferred_agent:
|
|
334
|
+
agent = preferred_agent.lower().strip()
|
|
335
|
+
if agent not in AGENTS or shutil.which(agent) is None:
|
|
336
|
+
return None
|
|
337
|
+
agents = [agent]
|
|
338
|
+
else:
|
|
339
|
+
agents = [a for a in AGENTS if shutil.which(a)]
|
|
340
|
+
|
|
341
|
+
for agent_name in agents:
|
|
342
|
+
with err_console.status(f"Generating branch name with {agent_name}..."):
|
|
343
|
+
branch = _generate_branch_name_with_agent(
|
|
344
|
+
agent_name,
|
|
345
|
+
repo_root,
|
|
346
|
+
prompt,
|
|
347
|
+
from_ref,
|
|
348
|
+
timeout_seconds,
|
|
349
|
+
)
|
|
350
|
+
if branch:
|
|
351
|
+
return _ensure_unique_branch_name(
|
|
352
|
+
branch,
|
|
353
|
+
existing_branches,
|
|
354
|
+
repo_root=repo_root,
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
return None
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def generate_random_branch_name(
|
|
361
|
+
existing_branches: set[str] | None = None,
|
|
362
|
+
*,
|
|
363
|
+
repo_root: Path | None = None,
|
|
364
|
+
) -> str:
|
|
365
|
+
"""Generate a unique random branch name like 'clever-fox'.
|
|
366
|
+
|
|
367
|
+
If the name already exists, adds a numeric suffix (clever-fox-2).
|
|
368
|
+
"""
|
|
369
|
+
existing = existing_branches or set()
|
|
370
|
+
base = f"{random.choice(_ADJECTIVES)}-{random.choice(_NOUNS)}" # noqa: S311
|
|
371
|
+
return _ensure_unique_branch_name(base, existing, repo_root=repo_root)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Console output helpers for the dev module."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import NoReturn
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
from agent_cli.core.utils import console, err_console
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def error(msg: str) -> NoReturn:
|
|
13
|
+
"""Print an error message and exit."""
|
|
14
|
+
err_console.print(f"[bold red]Error:[/bold red] {msg}")
|
|
15
|
+
raise typer.Exit(1)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def success(msg: str) -> None:
|
|
19
|
+
"""Print a success message."""
|
|
20
|
+
console.print(f"[bold green]✓[/bold green] {msg}")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def info(msg: str) -> None:
|
|
24
|
+
"""Print an info message, with special styling for commands."""
|
|
25
|
+
# Style commands (messages starting with "Running: ")
|
|
26
|
+
if msg.startswith("Running: "):
|
|
27
|
+
cmd = msg[9:] # Remove "Running: " prefix
|
|
28
|
+
# Escape brackets to prevent Rich from interpreting them as markup
|
|
29
|
+
cmd = cmd.replace("[", r"\[")
|
|
30
|
+
console.print(f"[dim]→[/dim] Running: [bold cyan]{cmd}[/bold cyan]")
|
|
31
|
+
else:
|
|
32
|
+
console.print(f"[dim]→[/dim] {msg}")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def warn(msg: str) -> None:
|
|
36
|
+
"""Print a warning message."""
|
|
37
|
+
console.print(f"[yellow]Warning:[/yellow] {msg}")
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""Worktree cleanup operations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import subprocess
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
from . import worktree
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def find_worktrees_with_no_commits(repo_root: Path) -> list[worktree.WorktreeInfo]:
|
|
16
|
+
"""Find worktrees whose branches have no commits ahead of the default branch."""
|
|
17
|
+
worktrees_list = worktree.list_worktrees()
|
|
18
|
+
default_branch = worktree.get_default_branch(repo_root)
|
|
19
|
+
to_remove: list[worktree.WorktreeInfo] = []
|
|
20
|
+
|
|
21
|
+
for wt in worktrees_list:
|
|
22
|
+
if wt.is_main or not wt.branch:
|
|
23
|
+
continue
|
|
24
|
+
|
|
25
|
+
# Check if branch has any commits ahead of default branch
|
|
26
|
+
result = subprocess.run(
|
|
27
|
+
["git", "rev-list", f"{default_branch}..{wt.branch}", "--count"], # noqa: S607
|
|
28
|
+
capture_output=True,
|
|
29
|
+
text=True,
|
|
30
|
+
cwd=repo_root,
|
|
31
|
+
check=False,
|
|
32
|
+
)
|
|
33
|
+
if result.returncode == 0 and result.stdout.strip() == "0":
|
|
34
|
+
to_remove.append(wt)
|
|
35
|
+
|
|
36
|
+
return to_remove
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def find_worktrees_with_merged_prs(
|
|
40
|
+
repo_root: Path,
|
|
41
|
+
) -> list[tuple[worktree.WorktreeInfo, str]]:
|
|
42
|
+
"""Find worktrees whose PRs have been merged on GitHub.
|
|
43
|
+
|
|
44
|
+
Returns a list of tuples containing (worktree_info, pr_url).
|
|
45
|
+
"""
|
|
46
|
+
worktrees_list = worktree.list_worktrees()
|
|
47
|
+
to_remove: list[tuple[worktree.WorktreeInfo, str]] = []
|
|
48
|
+
|
|
49
|
+
for wt in worktrees_list:
|
|
50
|
+
if wt.is_main or not wt.branch:
|
|
51
|
+
continue
|
|
52
|
+
|
|
53
|
+
# Check if PR for this branch is merged
|
|
54
|
+
result = subprocess.run(
|
|
55
|
+
["gh", "pr", "list", "--head", wt.branch, "--state", "merged", "--json", "number,url"], # noqa: S607
|
|
56
|
+
capture_output=True,
|
|
57
|
+
text=True,
|
|
58
|
+
cwd=repo_root,
|
|
59
|
+
check=False,
|
|
60
|
+
)
|
|
61
|
+
if result.returncode == 0 and result.stdout.strip() not in ("", "[]"):
|
|
62
|
+
prs = json.loads(result.stdout)
|
|
63
|
+
pr_url = prs[0]["url"] if prs else ""
|
|
64
|
+
to_remove.append((wt, pr_url))
|
|
65
|
+
|
|
66
|
+
return to_remove
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def check_gh_available() -> tuple[bool, str]:
|
|
70
|
+
"""Check if GitHub CLI is available and authenticated.
|
|
71
|
+
|
|
72
|
+
Returns (ok, error_message).
|
|
73
|
+
"""
|
|
74
|
+
gh_version = subprocess.run(
|
|
75
|
+
["gh", "--version"], # noqa: S607
|
|
76
|
+
capture_output=True,
|
|
77
|
+
check=False,
|
|
78
|
+
)
|
|
79
|
+
if gh_version.returncode != 0:
|
|
80
|
+
return False, "GitHub CLI (gh) not found. Install from: https://cli.github.com/"
|
|
81
|
+
|
|
82
|
+
gh_auth = subprocess.run(
|
|
83
|
+
["gh", "auth", "status"], # noqa: S607
|
|
84
|
+
capture_output=True,
|
|
85
|
+
check=False,
|
|
86
|
+
)
|
|
87
|
+
if gh_auth.returncode != 0:
|
|
88
|
+
return False, "Not authenticated with GitHub. Run: gh auth login"
|
|
89
|
+
|
|
90
|
+
return True, ""
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def remove_worktrees(
|
|
94
|
+
worktrees_to_remove: list[worktree.WorktreeInfo],
|
|
95
|
+
repo_root: Path,
|
|
96
|
+
*,
|
|
97
|
+
force: bool = False,
|
|
98
|
+
) -> list[tuple[str, bool, str | None]]:
|
|
99
|
+
"""Remove a list of worktrees.
|
|
100
|
+
|
|
101
|
+
Returns list of (branch_name, success, error_message) tuples.
|
|
102
|
+
"""
|
|
103
|
+
results: list[tuple[str, bool, str | None]] = []
|
|
104
|
+
for wt in worktrees_to_remove:
|
|
105
|
+
success, error = worktree.remove_worktree(
|
|
106
|
+
wt.path,
|
|
107
|
+
force=force,
|
|
108
|
+
delete_branch=True,
|
|
109
|
+
repo_path=repo_root,
|
|
110
|
+
)
|
|
111
|
+
results.append((wt.branch or wt.path.name, success, error))
|
|
112
|
+
return results
|