agent-cli 0.80.0__tar.gz → 0.81.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.
Files changed (368) hide show
  1. {agent_cli-0.80.0 → agent_cli-0.81.0}/.pre-commit-config.yaml +1 -1
  2. {agent_cli-0.80.0 → agent_cli-0.81.0}/PKG-INFO +1 -1
  3. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/_tools.py +1 -1
  4. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/agents/transcribe_live.py +1 -1
  5. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/config.py +19 -4
  6. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/core/deps.py +1 -1
  7. agent_cli-0.81.0/agent_cli/dev/_branch_name.py +371 -0
  8. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/cli.py +74 -86
  9. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/worktree.py +2 -2
  10. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/install/service_config.py +1 -1
  11. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/dev.md +12 -1
  12. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/dev/test_cli.py +469 -6
  13. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/dev/test_worktree.py +7 -7
  14. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_config.py +22 -0
  15. {agent_cli-0.80.0 → agent_cli-0.81.0}/.claude/skills/agent-cli-dev/SKILL.md +0 -0
  16. {agent_cli-0.80.0 → agent_cli-0.81.0}/.claude/skills/agent-cli-dev/examples.md +0 -0
  17. {agent_cli-0.80.0 → agent_cli-0.81.0}/.claude-plugin/README.md +0 -0
  18. {agent_cli-0.80.0 → agent_cli-0.81.0}/.claude-plugin/marketplace.json +0 -0
  19. {agent_cli-0.80.0 → agent_cli-0.81.0}/.claude-plugin/plugin.json +0 -0
  20. {agent_cli-0.80.0 → agent_cli-0.81.0}/.claude-plugin/skills/agent-cli-dev/SKILL.md +0 -0
  21. {agent_cli-0.80.0 → agent_cli-0.81.0}/.claude-plugin/skills/agent-cli-dev/examples.md +0 -0
  22. {agent_cli-0.80.0 → agent_cli-0.81.0}/.cursorrules +0 -0
  23. {agent_cli-0.80.0 → agent_cli-0.81.0}/.dockerignore +0 -0
  24. {agent_cli-0.80.0 → agent_cli-0.81.0}/.env.example +0 -0
  25. {agent_cli-0.80.0 → agent_cli-0.81.0}/.github/logo.svg +0 -0
  26. {agent_cli-0.80.0 → agent_cli-0.81.0}/.github/release-drafter.yml +0 -0
  27. {agent_cli-0.80.0 → agent_cli-0.81.0}/.github/renovate.json +0 -0
  28. {agent_cli-0.80.0 → agent_cli-0.81.0}/.github/scripts/check_extras_sync.py +0 -0
  29. {agent_cli-0.80.0 → agent_cli-0.81.0}/.github/scripts/check_plugin_skill_sync.py +0 -0
  30. {agent_cli-0.80.0 → agent_cli-0.81.0}/.github/scripts/sync_extras.py +0 -0
  31. {agent_cli-0.80.0 → agent_cli-0.81.0}/.github/scripts/sync_requirements.py +0 -0
  32. {agent_cli-0.80.0 → agent_cli-0.81.0}/.github/workflows/automerge.yml +0 -0
  33. {agent_cli-0.80.0 → agent_cli-0.81.0}/.github/workflows/docker.yml +0 -0
  34. {agent_cli-0.80.0 → agent_cli-0.81.0}/.github/workflows/docs.yml +0 -0
  35. {agent_cli-0.80.0 → agent_cli-0.81.0}/.github/workflows/markdown-code-runner.yml +0 -0
  36. {agent_cli-0.80.0 → agent_cli-0.81.0}/.github/workflows/pytest.yml +0 -0
  37. {agent_cli-0.80.0 → agent_cli-0.81.0}/.github/workflows/release-drafter.yml +0 -0
  38. {agent_cli-0.80.0 → agent_cli-0.81.0}/.github/workflows/release.yml +0 -0
  39. {agent_cli-0.80.0 → agent_cli-0.81.0}/.github/workflows/toc.yaml +0 -0
  40. {agent_cli-0.80.0 → agent_cli-0.81.0}/.gitignore +0 -0
  41. {agent_cli-0.80.0 → agent_cli-0.81.0}/.jscpd.json +0 -0
  42. {agent_cli-0.80.0 → agent_cli-0.81.0}/.prompts/docs-review.md +0 -0
  43. {agent_cli-0.80.0 → agent_cli-0.81.0}/.prompts/pr-review.md +0 -0
  44. {agent_cli-0.80.0 → agent_cli-0.81.0}/CLAUDE.md +0 -0
  45. {agent_cli-0.80.0 → agent_cli-0.81.0}/LICENSE +0 -0
  46. {agent_cli-0.80.0 → agent_cli-0.81.0}/README.md +0 -0
  47. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/__init__.py +0 -0
  48. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/__main__.py +0 -0
  49. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/_extras.json +0 -0
  50. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/_requirements/.gitkeep +0 -0
  51. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/_requirements/audio.txt +0 -0
  52. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/_requirements/faster-whisper.txt +0 -0
  53. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/_requirements/kokoro.txt +0 -0
  54. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/_requirements/llm.txt +0 -0
  55. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/_requirements/memory.txt +0 -0
  56. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/_requirements/mlx-whisper.txt +0 -0
  57. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/_requirements/piper.txt +0 -0
  58. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/_requirements/rag.txt +0 -0
  59. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/_requirements/server.txt +0 -0
  60. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/_requirements/speed.txt +0 -0
  61. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/_requirements/vad.txt +0 -0
  62. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/_requirements/whisper-transformers.txt +0 -0
  63. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/_requirements/wyoming.txt +0 -0
  64. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/agents/__init__.py +0 -0
  65. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/agents/_voice_agent_common.py +0 -0
  66. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/agents/assistant.py +0 -0
  67. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/agents/autocorrect.py +0 -0
  68. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/agents/chat.py +0 -0
  69. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/agents/memory/__init__.py +0 -0
  70. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/agents/memory/add.py +0 -0
  71. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/agents/memory/proxy.py +0 -0
  72. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/agents/rag_proxy.py +0 -0
  73. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/agents/speak.py +0 -0
  74. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/agents/transcribe.py +0 -0
  75. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/agents/voice_edit.py +0 -0
  76. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/api.py +0 -0
  77. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/cli.py +0 -0
  78. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/config_cmd.py +0 -0
  79. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/constants.py +0 -0
  80. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/core/__init__.py +0 -0
  81. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/core/audio.py +0 -0
  82. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/core/audio_format.py +0 -0
  83. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/core/chroma.py +0 -0
  84. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/core/openai_proxy.py +0 -0
  85. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/core/process.py +0 -0
  86. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/core/reranker.py +0 -0
  87. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/core/sse.py +0 -0
  88. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/core/transcription_logger.py +0 -0
  89. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/core/utils.py +0 -0
  90. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/core/vad.py +0 -0
  91. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/core/watch.py +0 -0
  92. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/daemon/__init__.py +0 -0
  93. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/daemon/cli.py +0 -0
  94. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/__init__.py +0 -0
  95. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/coding_agents/__init__.py +0 -0
  96. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/coding_agents/aider.py +0 -0
  97. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/coding_agents/base.py +0 -0
  98. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/coding_agents/claude.py +0 -0
  99. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/coding_agents/codex.py +0 -0
  100. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/coding_agents/continue_dev.py +0 -0
  101. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/coding_agents/copilot.py +0 -0
  102. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/coding_agents/cursor_agent.py +0 -0
  103. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/coding_agents/gemini.py +0 -0
  104. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/coding_agents/opencode.py +0 -0
  105. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/coding_agents/registry.py +0 -0
  106. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/editors/__init__.py +0 -0
  107. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/editors/base.py +0 -0
  108. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/editors/cursor.py +0 -0
  109. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/editors/emacs.py +0 -0
  110. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/editors/jetbrains.py +0 -0
  111. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/editors/nano.py +0 -0
  112. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/editors/neovim.py +0 -0
  113. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/editors/registry.py +0 -0
  114. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/editors/sublime.py +0 -0
  115. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/editors/vim.py +0 -0
  116. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/editors/vscode.py +0 -0
  117. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/editors/zed.py +0 -0
  118. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/project.py +0 -0
  119. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/registry.py +0 -0
  120. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/skill/SKILL.md +0 -0
  121. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/skill/examples.md +0 -0
  122. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/terminals/__init__.py +0 -0
  123. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/terminals/apple_terminal.py +0 -0
  124. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/terminals/base.py +0 -0
  125. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/terminals/gnome.py +0 -0
  126. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/terminals/iterm2.py +0 -0
  127. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/terminals/kitty.py +0 -0
  128. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/terminals/registry.py +0 -0
  129. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/terminals/tmux.py +0 -0
  130. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/terminals/warp.py +0 -0
  131. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/dev/terminals/zellij.py +0 -0
  132. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/docs_gen.py +0 -0
  133. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/example-config.toml +0 -0
  134. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/install/__init__.py +0 -0
  135. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/install/common.py +0 -0
  136. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/install/extras.py +0 -0
  137. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/install/hotkeys.py +0 -0
  138. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/install/launchd.py +0 -0
  139. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/install/services.py +0 -0
  140. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/install/systemd.py +0 -0
  141. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/memory/__init__.py +0 -0
  142. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/memory/_files.py +0 -0
  143. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/memory/_filters.py +0 -0
  144. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/memory/_git.py +0 -0
  145. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/memory/_indexer.py +0 -0
  146. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/memory/_ingest.py +0 -0
  147. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/memory/_persistence.py +0 -0
  148. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/memory/_prompt.py +0 -0
  149. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/memory/_retrieval.py +0 -0
  150. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/memory/_store.py +0 -0
  151. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/memory/_streaming.py +0 -0
  152. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/memory/_tasks.py +0 -0
  153. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/memory/api.py +0 -0
  154. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/memory/client.py +0 -0
  155. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/memory/engine.py +0 -0
  156. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/memory/entities.py +0 -0
  157. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/memory/models.py +0 -0
  158. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/opts.py +0 -0
  159. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/py.typed +0 -0
  160. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/rag/__init__.py +0 -0
  161. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/rag/_indexer.py +0 -0
  162. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/rag/_indexing.py +0 -0
  163. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/rag/_prompt.py +0 -0
  164. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/rag/_retriever.py +0 -0
  165. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/rag/_store.py +0 -0
  166. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/rag/_utils.py +0 -0
  167. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/rag/api.py +0 -0
  168. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/rag/client.py +0 -0
  169. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/rag/engine.py +0 -0
  170. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/rag/models.py +0 -0
  171. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/.runtime/.gitkeep +0 -0
  172. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/__init__.py +0 -0
  173. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/linux-hotkeys/README.md +0 -0
  174. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/linux-hotkeys/toggle-autocorrect.sh +0 -0
  175. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/linux-hotkeys/toggle-transcription.sh +0 -0
  176. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/linux-hotkeys/toggle-voice-edit.sh +0 -0
  177. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/macos-hotkeys/README.md +0 -0
  178. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/macos-hotkeys/skhd-config-example +0 -0
  179. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/macos-hotkeys/toggle-autocorrect.sh +0 -0
  180. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/macos-hotkeys/toggle-transcription.sh +0 -0
  181. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/macos-hotkeys/toggle-voice-edit.sh +0 -0
  182. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/nvidia-asr-server/README.md +0 -0
  183. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/nvidia-asr-server/pyproject.toml +0 -0
  184. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/nvidia-asr-server/server.py +0 -0
  185. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/nvidia-asr-server/shell.nix +0 -0
  186. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/nvidia-asr-server/uv.lock +0 -0
  187. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/run-openwakeword.sh +0 -0
  188. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/setup-linux-hotkeys.sh +0 -0
  189. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/setup-linux.sh +0 -0
  190. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/setup-macos-hotkeys.sh +0 -0
  191. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/setup-macos.sh +0 -0
  192. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/setup-windows.ps1 +0 -0
  193. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/start-all-services-windows.ps1 +0 -0
  194. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/scripts/start-all-services.sh +0 -0
  195. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/__init__.py +0 -0
  196. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/cli.py +0 -0
  197. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/common.py +0 -0
  198. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/model_manager.py +0 -0
  199. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/model_registry.py +0 -0
  200. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/proxy/__init__.py +0 -0
  201. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/proxy/api.py +0 -0
  202. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/streaming.py +0 -0
  203. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/tts/__init__.py +0 -0
  204. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/tts/api.py +0 -0
  205. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/tts/backends/__init__.py +0 -0
  206. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/tts/backends/base.py +0 -0
  207. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/tts/backends/kokoro.py +0 -0
  208. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/tts/backends/piper.py +0 -0
  209. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/tts/model_manager.py +0 -0
  210. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/tts/model_registry.py +0 -0
  211. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/tts/wyoming_handler.py +0 -0
  212. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/whisper/__init__.py +0 -0
  213. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/whisper/api.py +0 -0
  214. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/whisper/backends/__init__.py +0 -0
  215. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/whisper/backends/base.py +0 -0
  216. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/whisper/backends/faster_whisper.py +0 -0
  217. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/whisper/backends/mlx.py +0 -0
  218. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/whisper/backends/transformers.py +0 -0
  219. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/whisper/languages.py +0 -0
  220. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/whisper/model_manager.py +0 -0
  221. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/whisper/model_registry.py +0 -0
  222. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/server/whisper/wyoming_handler.py +0 -0
  223. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/services/__init__.py +0 -0
  224. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/services/_wyoming_utils.py +0 -0
  225. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/services/asr.py +0 -0
  226. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/services/llm.py +0 -0
  227. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/services/tts.py +0 -0
  228. {agent_cli-0.80.0 → agent_cli-0.81.0}/agent_cli/services/wake_word.py +0 -0
  229. {agent_cli-0.80.0 → agent_cli-0.81.0}/docker/docker-compose.yml +0 -0
  230. {agent_cli-0.80.0 → agent_cli-0.81.0}/docker/memory-proxy.Dockerfile +0 -0
  231. {agent_cli-0.80.0 → agent_cli-0.81.0}/docker/rag-proxy.Dockerfile +0 -0
  232. {agent_cli-0.80.0 → agent_cli-0.81.0}/docker/transcribe-proxy.Dockerfile +0 -0
  233. {agent_cli-0.80.0 → agent_cli-0.81.0}/docker/tts.Dockerfile +0 -0
  234. {agent_cli-0.80.0 → agent_cli-0.81.0}/docker/whisper.Dockerfile +0 -0
  235. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/CNAME +0 -0
  236. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/architecture/index.md +0 -0
  237. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/architecture/memory.md +0 -0
  238. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/architecture/rag.md +0 -0
  239. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/assistant.md +0 -0
  240. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/autocorrect.md +0 -0
  241. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/chat.md +0 -0
  242. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/config.md +0 -0
  243. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/daemon.md +0 -0
  244. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/index.md +0 -0
  245. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/install-extras.md +0 -0
  246. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/install-hotkeys.md +0 -0
  247. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/install-services.md +0 -0
  248. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/memory.md +0 -0
  249. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/rag-proxy.md +0 -0
  250. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/server/index.md +0 -0
  251. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/server/transcribe-proxy.md +0 -0
  252. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/server/tts.md +0 -0
  253. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/server/whisper.md +0 -0
  254. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/speak.md +0 -0
  255. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/start-services.md +0 -0
  256. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/transcribe-live.md +0 -0
  257. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/transcribe.md +0 -0
  258. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/commands/voice-edit.md +0 -0
  259. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/configuration.md +0 -0
  260. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/getting-started.md +0 -0
  261. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/iOS_Shortcut_Guide.md +0 -0
  262. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/index.md +0 -0
  263. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/installation/docker.md +0 -0
  264. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/installation/index.md +0 -0
  265. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/installation/linux.md +0 -0
  266. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/installation/macos.md +0 -0
  267. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/installation/nixos.md +0 -0
  268. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/installation/windows.md +0 -0
  269. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/logo-clean.svg +0 -0
  270. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/overrides/partials/integrations/analytics/custom.html +0 -0
  271. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/run_markdown_code_runner.py +0 -0
  272. {agent_cli-0.80.0 → agent_cli-0.81.0}/docs/system-integration.md +0 -0
  273. {agent_cli-0.80.0 → agent_cli-0.81.0}/example.agent-cli-config.toml +0 -0
  274. {agent_cli-0.80.0 → agent_cli-0.81.0}/justfile +0 -0
  275. {agent_cli-0.80.0 → agent_cli-0.81.0}/pyproject.toml +0 -0
  276. {agent_cli-0.80.0 → agent_cli-0.81.0}/shell.nix +0 -0
  277. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/__init__.py +0 -0
  278. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/agents/__init__.py +0 -0
  279. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/agents/test_fix_my_text.py +0 -0
  280. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/agents/test_interactive.py +0 -0
  281. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/agents/test_interactive_extra.py +0 -0
  282. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/agents/test_memory_add.py +0 -0
  283. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/agents/test_speak.py +0 -0
  284. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/agents/test_speak_e2e.py +0 -0
  285. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/agents/test_transcribe.py +0 -0
  286. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/agents/test_transcribe_agent.py +0 -0
  287. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/agents/test_transcribe_e2e.py +0 -0
  288. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/agents/test_transcribe_live.py +0 -0
  289. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/agents/test_transcribe_recovery.py +0 -0
  290. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/agents/test_tts_common.py +0 -0
  291. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/agents/test_tts_common_extra.py +0 -0
  292. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/agents/test_voice_agent_common.py +0 -0
  293. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/agents/test_voice_edit.py +0 -0
  294. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/agents/test_voice_edit_e2e.py +0 -0
  295. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/agents/test_wake_word_assistant.py +0 -0
  296. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/conftest.py +0 -0
  297. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/core/__init__.py +0 -0
  298. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/core/test_audio_format.py +0 -0
  299. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/core/test_chroma.py +0 -0
  300. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/core/test_sse.py +0 -0
  301. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/core/test_vad.py +0 -0
  302. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/core/test_watch.py +0 -0
  303. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/dev/__init__.py +0 -0
  304. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/dev/test_coding_agents.py +0 -0
  305. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/dev/test_editors.py +0 -0
  306. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/dev/test_project.py +0 -0
  307. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/dev/test_terminals.py +0 -0
  308. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/dev/test_verification.py +0 -0
  309. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/install/__init__.py +0 -0
  310. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/install/test_extras.py +0 -0
  311. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/memory/__init__.py +0 -0
  312. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/memory/test_api_health.py +0 -0
  313. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/memory/test_api_integration_liveish.py +0 -0
  314. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/memory/test_client.py +0 -0
  315. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/memory/test_engine.py +0 -0
  316. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/memory/test_files.py +0 -0
  317. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/memory/test_filters.py +0 -0
  318. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/memory/test_git_integration.py +0 -0
  319. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/memory/test_indexer.py +0 -0
  320. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/memory/test_memory_integration.py +0 -0
  321. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/memory/test_proxy_passthrough.py +0 -0
  322. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/memory/test_store.py +0 -0
  323. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/memory/test_utils.py +0 -0
  324. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/mocks/__init__.py +0 -0
  325. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/mocks/audio.py +0 -0
  326. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/mocks/llm.py +0 -0
  327. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/mocks/wyoming.py +0 -0
  328. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/rag/__init__.py +0 -0
  329. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/rag/test_api.py +0 -0
  330. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/rag/test_engine.py +0 -0
  331. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/rag/test_history.py +0 -0
  332. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/rag/test_indexer.py +0 -0
  333. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/rag/test_indexing.py +0 -0
  334. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/rag/test_rag_client.py +0 -0
  335. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/rag/test_rag_integration_liveish.py +0 -0
  336. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/rag/test_rag_proxy_passthrough.py +0 -0
  337. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/rag/test_retriever.py +0 -0
  338. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/rag/test_store.py +0 -0
  339. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/rag/test_utils.py +0 -0
  340. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_api.py +0 -0
  341. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_api_integration.py +0 -0
  342. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_asr.py +0 -0
  343. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_asr_recovery.py +0 -0
  344. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_audio_e2e.py +0 -0
  345. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_cli.py +0 -0
  346. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_config_cmd.py +0 -0
  347. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_daemon.py +0 -0
  348. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_docs_gen.py +0 -0
  349. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_env_vars.py +0 -0
  350. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_json_output.py +0 -0
  351. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_llm.py +0 -0
  352. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_llm_gemini.py +0 -0
  353. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_memory_tools.py +0 -0
  354. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_mlx_backend.py +0 -0
  355. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_process_manager.py +0 -0
  356. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_requires_extras.py +0 -0
  357. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_server_streaming.py +0 -0
  358. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_server_tts.py +0 -0
  359. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_server_whisper.py +0 -0
  360. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_services.py +0 -0
  361. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_tools.py +0 -0
  362. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_transformers_backend.py +0 -0
  363. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_tts.py +0 -0
  364. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_utils.py +0 -0
  365. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_wake_word.py +0 -0
  366. {agent_cli-0.80.0 → agent_cli-0.81.0}/tests/test_wyoming_utils.py +0 -0
  367. {agent_cli-0.80.0 → agent_cli-0.81.0}/uv.lock +0 -0
  368. {agent_cli-0.80.0 → agent_cli-0.81.0}/zensical.toml +0 -0
@@ -8,7 +8,7 @@ repos:
8
8
  - id: end-of-file-fixer
9
9
  - id: mixed-line-ending
10
10
  - repo: https://github.com/astral-sh/ruff-pre-commit
11
- rev: "v0.14.14"
11
+ rev: "v0.15.0"
12
12
  hooks:
13
13
  - id: ruff
14
14
  args: ["--fix"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-cli
3
- Version: 0.80.0
3
+ Version: 0.81.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:
@@ -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 if processed else transcript
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,16 +322,28 @@ 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: dict[str, Any]) -> dict[str, Any]:
323
- return {k.replace("-", "_"): v for k, v in cfg.items()}
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: {"a": {"b": {"x": 1}}} -> {"a.b": {"x": 1}}."""
328
- result = {}
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()):
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
332
347
  result.update(_flatten_nested_sections(value, full_key))
333
348
  else:
334
349
  result[full_key] = value
@@ -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):
@@ -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)
@@ -4,13 +4,12 @@ from __future__ import annotations
4
4
 
5
5
  import json
6
6
  import os
7
- import random
8
7
  import shlex
9
8
  import shutil
10
9
  import subprocess
11
10
  import tempfile
12
11
  from pathlib import Path
13
- from typing import TYPE_CHECKING, Annotated, NoReturn
12
+ from typing import TYPE_CHECKING, Annotated, Literal, NoReturn
14
13
 
15
14
  import typer
16
15
  from rich.panel import Panel
@@ -22,85 +21,11 @@ from agent_cli.config import load_config
22
21
  from agent_cli.core.process import set_process_title
23
22
  from agent_cli.core.utils import console, err_console
24
23
 
25
- # Word lists for generating random branch names (like Docker container names)
26
- _ADJECTIVES = [
27
- "happy",
28
- "clever",
29
- "swift",
30
- "bright",
31
- "calm",
32
- "eager",
33
- "fancy",
34
- "gentle",
35
- "jolly",
36
- "keen",
37
- "lively",
38
- "merry",
39
- "nice",
40
- "proud",
41
- "quick",
42
- "sharp",
43
- "smart",
44
- "sunny",
45
- "witty",
46
- "zesty",
47
- "bold",
48
- "cool",
49
- "fresh",
50
- "grand",
51
- ]
52
- _NOUNS = [
53
- "fox",
54
- "owl",
55
- "bear",
56
- "wolf",
57
- "hawk",
58
- "lion",
59
- "tiger",
60
- "eagle",
61
- "falcon",
62
- "otter",
63
- "panda",
64
- "raven",
65
- "shark",
66
- "whale",
67
- "zebra",
68
- "bison",
69
- "crane",
70
- "dolphin",
71
- "gecko",
72
- "heron",
73
- "koala",
74
- "lemur",
75
- "moose",
76
- "newt",
77
- "oriole",
78
- ]
79
-
80
-
81
- def _generate_branch_name(existing_branches: set[str] | None = None) -> str:
82
- """Generate a unique random branch name like 'clever-fox'.
83
-
84
- If the name already exists, adds a numeric suffix (clever-fox-2).
85
- """
86
- existing = existing_branches or set()
87
- base = f"{random.choice(_ADJECTIVES)}-{random.choice(_NOUNS)}" # noqa: S311
88
-
89
- if base not in existing:
90
- return base
91
-
92
- # Add numeric suffix to avoid collision
93
- for i in range(2, 100):
94
- candidate = f"{base}-{i}"
95
- if candidate not in existing:
96
- return candidate
97
-
98
- # Fallback: add random digits
99
- return f"{base}-{random.randint(100, 999)}" # noqa: S311
100
-
101
-
102
- from . import coding_agents, editors, terminals, worktree # noqa: E402
103
- from .project import ( # noqa: E402
24
+ from . import coding_agents, editors, terminals, worktree
25
+ from ._branch_name import AGENTS as _BRANCH_NAME_AGENTS
26
+ from ._branch_name import generate_ai_branch_name as _generate_ai_branch_name
27
+ from ._branch_name import generate_random_branch_name as _generate_branch_name
28
+ from .project import (
104
29
  copy_env_files,
105
30
  detect_project_type,
106
31
  is_direnv_available,
@@ -156,6 +81,17 @@ def dev_callback(
156
81
  has its own branch, allowing parallel development without stashing changes.
157
82
  """
158
83
  set_config_defaults(ctx, config_file)
84
+
85
+ # The [dev] section config is intended for `dev new` options.
86
+ # Click expects subcommand defaults under ctx.default_map["new"].
87
+ if isinstance(ctx.default_map, dict):
88
+ flat_defaults = {k: v for k, v in ctx.default_map.items() if not isinstance(v, dict)}
89
+ nested_defaults = {k: dict(v) for k, v in ctx.default_map.items() if isinstance(v, dict)}
90
+
91
+ if flat_defaults:
92
+ nested_defaults["new"] = {**flat_defaults, **nested_defaults.get("new", {})}
93
+ ctx.default_map = nested_defaults
94
+
159
95
  if ctx.invoked_subcommand is not None:
160
96
  set_process_title(f"dev-{ctx.invoked_subcommand}")
161
97
 
@@ -311,7 +247,7 @@ def _get_config_agent_env() -> dict[str, dict[str, str]] | None:
311
247
  agent_name = key[len(prefix) :]
312
248
  result[agent_name] = value
313
249
 
314
- return result if result else None
250
+ return result or None
315
251
 
316
252
 
317
253
  def _get_agent_env(agent: CodingAgent) -> dict[str, str]:
@@ -350,7 +286,7 @@ def _merge_agent_args(
350
286
  if cli_args:
351
287
  result.extend(cli_args)
352
288
 
353
- return result if result else None
289
+ return result or None
354
290
 
355
291
 
356
292
  def _is_ssh_session() -> bool:
@@ -488,7 +424,7 @@ def new( # noqa: C901, PLR0912, PLR0915
488
424
  branch: Annotated[
489
425
  str | None,
490
426
  typer.Argument(
491
- help="Branch name for the worktree. If omitted, generates a random name like 'clever-fox'",
427
+ help="Branch name for the worktree. If omitted, auto-generates one (random by default, AI with --branch-name-mode)",
492
428
  ),
493
429
  ] = None,
494
430
  from_ref: Annotated[
@@ -558,6 +494,29 @@ def new( # noqa: C901, PLR0912, PLR0915
558
494
  help="Run 'git fetch' before creating the worktree to ensure refs are up-to-date",
559
495
  ),
560
496
  ] = True,
497
+ branch_name_mode: Annotated[
498
+ Literal["random", "auto", "ai"],
499
+ typer.Option(
500
+ "--branch-name-mode",
501
+ case_sensitive=False,
502
+ help="How to auto-name branches when BRANCH is omitted: random (default), auto (AI only when --prompt/--prompt-file is set), or ai (always try AI first)",
503
+ ),
504
+ ] = "random",
505
+ branch_name_agent: Annotated[
506
+ str | None,
507
+ typer.Option(
508
+ "--branch-name-agent",
509
+ help="Headless agent for AI branch naming: claude, codex, or gemini. If omitted, uses --with-agent when supported, otherwise tries available agents in that order",
510
+ ),
511
+ ] = None,
512
+ branch_name_timeout: Annotated[
513
+ float,
514
+ typer.Option(
515
+ "--branch-name-timeout",
516
+ min=1.0,
517
+ help="Timeout in seconds for AI branch naming command",
518
+ ),
519
+ ] = 20.0,
561
520
  direnv: Annotated[
562
521
  bool | None,
563
522
  typer.Option(
@@ -619,6 +578,7 @@ def new( # noqa: C901, PLR0912, PLR0915
619
578
  - `dev new feature-x -a` — Create + start Claude/detected agent
620
579
  - `dev new feature-x -e -a` — Create + open editor + start agent
621
580
  - `dev new -a --prompt "Fix auth bug"` — Auto-named branch + agent with task
581
+ - `dev new --branch-name-mode ai -a --prompt "Refactor auth flow"` — AI-generated branch name
622
582
  - `dev new hotfix --from v1.2.3` — Branch from a tag instead of main
623
583
  - `dev new feature-x --from origin/develop` — Branch from develop instead
624
584
  - `dev new feature-x --with-agent aider --with-editor cursor` — Specific tools
@@ -637,8 +597,36 @@ def new( # noqa: C901, PLR0912, PLR0915
637
597
  if branch is None:
638
598
  # Get existing branches to avoid collisions
639
599
  existing = {wt.branch for wt in worktree.list_worktrees() if wt.branch}
640
- branch = _generate_branch_name(existing)
641
- _info(f"Generated branch name: {branch}")
600
+ # In auto mode, only use AI naming when we have task context.
601
+ if branch_name_mode == "auto" and not prompt:
602
+ use_ai = False
603
+ else:
604
+ use_ai = branch_name_mode != "random"
605
+
606
+ if not use_ai:
607
+ branch = _generate_branch_name(existing, repo_root=repo_root)
608
+ _info(f"Generated branch name: {branch}")
609
+ else:
610
+ effective_branch_name_agent = branch_name_agent
611
+ if effective_branch_name_agent is None and agent_name:
612
+ candidate = agent_name.lower().strip()
613
+ if candidate in _BRANCH_NAME_AGENTS:
614
+ effective_branch_name_agent = candidate
615
+
616
+ branch = _generate_ai_branch_name(
617
+ repo_root,
618
+ existing,
619
+ prompt,
620
+ from_ref,
621
+ effective_branch_name_agent,
622
+ branch_name_timeout,
623
+ )
624
+ if branch:
625
+ _info(f"AI-generated branch name: {branch}")
626
+ else:
627
+ _warn("Could not generate branch name with AI. Falling back to random naming.")
628
+ branch = _generate_branch_name(existing, repo_root=repo_root)
629
+ _info(f"Generated branch name: {branch}")
642
630
 
643
631
  # Create the worktree
644
632
  _info(f"Creating worktree for branch '{branch}'...")