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.
Files changed (373) hide show
  1. {agent_cli-0.80.0 → agent_cli-0.82.0}/.pre-commit-config.yaml +1 -1
  2. {agent_cli-0.80.0 → agent_cli-0.82.0}/PKG-INFO +1 -1
  3. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_tools.py +1 -1
  4. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/chat.py +2 -2
  5. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/transcribe_live.py +1 -1
  6. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/config.py +21 -5
  7. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/deps.py +1 -1
  8. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/watch.py +1 -1
  9. agent_cli-0.82.0/agent_cli/dev/_branch_name.py +371 -0
  10. agent_cli-0.82.0/agent_cli/dev/_output.py +37 -0
  11. agent_cli-0.82.0/agent_cli/dev/cleanup.py +112 -0
  12. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/cli.py +243 -606
  13. agent_cli-0.82.0/agent_cli/dev/launch.py +295 -0
  14. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/worktree.py +2 -2
  15. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/docs_gen.py +1 -1
  16. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/install/service_config.py +1 -1
  17. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/_indexer.py +6 -2
  18. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/_indexing.py +17 -3
  19. agent_cli-0.82.0/agent_cli/rag/_utils.py +461 -0
  20. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/engine.py +3 -4
  21. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/nvidia-asr-server/server.py +2 -2
  22. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/backends/transformers.py +1 -1
  23. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/dev.md +12 -1
  24. {agent_cli-0.80.0 → agent_cli-0.82.0}/pyproject.toml +4 -0
  25. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/core/test_watch.py +1 -1
  26. agent_cli-0.82.0/tests/dev/test_cli.py +958 -0
  27. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/dev/test_worktree.py +7 -7
  28. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_engine.py +36 -3
  29. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_indexer.py +9 -3
  30. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_utils.py +172 -0
  31. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_api_integration.py +1 -1
  32. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_asr_recovery.py +3 -3
  33. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_config.py +53 -0
  34. agent_cli-0.80.0/agent_cli/rag/_utils.py +0 -218
  35. agent_cli-0.80.0/tests/dev/test_cli.py +0 -496
  36. {agent_cli-0.80.0 → agent_cli-0.82.0}/.claude/skills/agent-cli-dev/SKILL.md +0 -0
  37. {agent_cli-0.80.0 → agent_cli-0.82.0}/.claude/skills/agent-cli-dev/examples.md +0 -0
  38. {agent_cli-0.80.0 → agent_cli-0.82.0}/.claude-plugin/README.md +0 -0
  39. {agent_cli-0.80.0 → agent_cli-0.82.0}/.claude-plugin/marketplace.json +0 -0
  40. {agent_cli-0.80.0 → agent_cli-0.82.0}/.claude-plugin/plugin.json +0 -0
  41. {agent_cli-0.80.0 → agent_cli-0.82.0}/.claude-plugin/skills/agent-cli-dev/SKILL.md +0 -0
  42. {agent_cli-0.80.0 → agent_cli-0.82.0}/.claude-plugin/skills/agent-cli-dev/examples.md +0 -0
  43. {agent_cli-0.80.0 → agent_cli-0.82.0}/.cursorrules +0 -0
  44. {agent_cli-0.80.0 → agent_cli-0.82.0}/.dockerignore +0 -0
  45. {agent_cli-0.80.0 → agent_cli-0.82.0}/.env.example +0 -0
  46. {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/logo.svg +0 -0
  47. {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/release-drafter.yml +0 -0
  48. {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/renovate.json +0 -0
  49. {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/scripts/check_extras_sync.py +0 -0
  50. {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/scripts/check_plugin_skill_sync.py +0 -0
  51. {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/scripts/sync_extras.py +0 -0
  52. {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/scripts/sync_requirements.py +0 -0
  53. {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/workflows/automerge.yml +0 -0
  54. {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/workflows/docker.yml +0 -0
  55. {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/workflows/docs.yml +0 -0
  56. {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/workflows/markdown-code-runner.yml +0 -0
  57. {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/workflows/pytest.yml +0 -0
  58. {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/workflows/release-drafter.yml +0 -0
  59. {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/workflows/release.yml +0 -0
  60. {agent_cli-0.80.0 → agent_cli-0.82.0}/.github/workflows/toc.yaml +0 -0
  61. {agent_cli-0.80.0 → agent_cli-0.82.0}/.gitignore +0 -0
  62. {agent_cli-0.80.0 → agent_cli-0.82.0}/.jscpd.json +0 -0
  63. {agent_cli-0.80.0 → agent_cli-0.82.0}/.prompts/docs-review.md +0 -0
  64. {agent_cli-0.80.0 → agent_cli-0.82.0}/.prompts/pr-review.md +0 -0
  65. {agent_cli-0.80.0 → agent_cli-0.82.0}/CLAUDE.md +0 -0
  66. {agent_cli-0.80.0 → agent_cli-0.82.0}/LICENSE +0 -0
  67. {agent_cli-0.80.0 → agent_cli-0.82.0}/README.md +0 -0
  68. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/__init__.py +0 -0
  69. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/__main__.py +0 -0
  70. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_extras.json +0 -0
  71. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/.gitkeep +0 -0
  72. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/audio.txt +0 -0
  73. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/faster-whisper.txt +0 -0
  74. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/kokoro.txt +0 -0
  75. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/llm.txt +0 -0
  76. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/memory.txt +0 -0
  77. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/mlx-whisper.txt +0 -0
  78. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/piper.txt +0 -0
  79. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/rag.txt +0 -0
  80. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/server.txt +0 -0
  81. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/speed.txt +0 -0
  82. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/vad.txt +0 -0
  83. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/whisper-transformers.txt +0 -0
  84. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/_requirements/wyoming.txt +0 -0
  85. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/__init__.py +0 -0
  86. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/_voice_agent_common.py +0 -0
  87. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/assistant.py +0 -0
  88. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/autocorrect.py +0 -0
  89. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/memory/__init__.py +0 -0
  90. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/memory/add.py +0 -0
  91. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/memory/proxy.py +0 -0
  92. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/rag_proxy.py +0 -0
  93. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/speak.py +0 -0
  94. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/transcribe.py +0 -0
  95. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/agents/voice_edit.py +0 -0
  96. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/api.py +0 -0
  97. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/cli.py +0 -0
  98. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/config_cmd.py +0 -0
  99. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/constants.py +0 -0
  100. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/__init__.py +0 -0
  101. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/audio.py +0 -0
  102. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/audio_format.py +0 -0
  103. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/chroma.py +0 -0
  104. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/openai_proxy.py +0 -0
  105. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/process.py +0 -0
  106. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/reranker.py +0 -0
  107. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/sse.py +0 -0
  108. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/transcription_logger.py +0 -0
  109. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/utils.py +0 -0
  110. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/core/vad.py +0 -0
  111. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/daemon/__init__.py +0 -0
  112. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/daemon/cli.py +0 -0
  113. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/__init__.py +0 -0
  114. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/__init__.py +0 -0
  115. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/aider.py +0 -0
  116. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/base.py +0 -0
  117. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/claude.py +0 -0
  118. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/codex.py +0 -0
  119. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/continue_dev.py +0 -0
  120. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/copilot.py +0 -0
  121. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/cursor_agent.py +0 -0
  122. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/gemini.py +0 -0
  123. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/opencode.py +0 -0
  124. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/coding_agents/registry.py +0 -0
  125. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/__init__.py +0 -0
  126. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/base.py +0 -0
  127. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/cursor.py +0 -0
  128. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/emacs.py +0 -0
  129. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/jetbrains.py +0 -0
  130. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/nano.py +0 -0
  131. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/neovim.py +0 -0
  132. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/registry.py +0 -0
  133. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/sublime.py +0 -0
  134. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/vim.py +0 -0
  135. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/vscode.py +0 -0
  136. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/editors/zed.py +0 -0
  137. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/project.py +0 -0
  138. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/registry.py +0 -0
  139. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/skill/SKILL.md +0 -0
  140. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/skill/examples.md +0 -0
  141. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/__init__.py +0 -0
  142. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/apple_terminal.py +0 -0
  143. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/base.py +0 -0
  144. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/gnome.py +0 -0
  145. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/iterm2.py +0 -0
  146. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/kitty.py +0 -0
  147. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/registry.py +0 -0
  148. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/tmux.py +0 -0
  149. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/warp.py +0 -0
  150. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/dev/terminals/zellij.py +0 -0
  151. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/example-config.toml +0 -0
  152. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/install/__init__.py +0 -0
  153. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/install/common.py +0 -0
  154. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/install/extras.py +0 -0
  155. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/install/hotkeys.py +0 -0
  156. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/install/launchd.py +0 -0
  157. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/install/services.py +0 -0
  158. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/install/systemd.py +0 -0
  159. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/__init__.py +0 -0
  160. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_files.py +0 -0
  161. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_filters.py +0 -0
  162. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_git.py +0 -0
  163. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_indexer.py +0 -0
  164. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_ingest.py +0 -0
  165. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_persistence.py +0 -0
  166. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_prompt.py +0 -0
  167. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_retrieval.py +0 -0
  168. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_store.py +0 -0
  169. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_streaming.py +0 -0
  170. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/_tasks.py +0 -0
  171. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/api.py +0 -0
  172. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/client.py +0 -0
  173. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/engine.py +0 -0
  174. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/entities.py +0 -0
  175. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/memory/models.py +0 -0
  176. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/opts.py +0 -0
  177. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/py.typed +0 -0
  178. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/__init__.py +0 -0
  179. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/_prompt.py +0 -0
  180. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/_retriever.py +0 -0
  181. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/_store.py +0 -0
  182. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/api.py +0 -0
  183. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/client.py +0 -0
  184. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/rag/models.py +0 -0
  185. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/.runtime/.gitkeep +0 -0
  186. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/__init__.py +0 -0
  187. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/linux-hotkeys/README.md +0 -0
  188. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/linux-hotkeys/toggle-autocorrect.sh +0 -0
  189. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/linux-hotkeys/toggle-transcription.sh +0 -0
  190. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/linux-hotkeys/toggle-voice-edit.sh +0 -0
  191. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/macos-hotkeys/README.md +0 -0
  192. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/macos-hotkeys/skhd-config-example +0 -0
  193. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/macos-hotkeys/toggle-autocorrect.sh +0 -0
  194. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/macos-hotkeys/toggle-transcription.sh +0 -0
  195. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/macos-hotkeys/toggle-voice-edit.sh +0 -0
  196. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/nvidia-asr-server/README.md +0 -0
  197. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/nvidia-asr-server/pyproject.toml +0 -0
  198. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/nvidia-asr-server/shell.nix +0 -0
  199. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/nvidia-asr-server/uv.lock +0 -0
  200. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/run-openwakeword.sh +0 -0
  201. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/setup-linux-hotkeys.sh +0 -0
  202. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/setup-linux.sh +0 -0
  203. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/setup-macos-hotkeys.sh +0 -0
  204. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/setup-macos.sh +0 -0
  205. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/setup-windows.ps1 +0 -0
  206. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/start-all-services-windows.ps1 +0 -0
  207. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/scripts/start-all-services.sh +0 -0
  208. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/__init__.py +0 -0
  209. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/cli.py +0 -0
  210. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/common.py +0 -0
  211. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/model_manager.py +0 -0
  212. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/model_registry.py +0 -0
  213. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/proxy/__init__.py +0 -0
  214. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/proxy/api.py +0 -0
  215. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/streaming.py +0 -0
  216. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/tts/__init__.py +0 -0
  217. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/tts/api.py +0 -0
  218. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/tts/backends/__init__.py +0 -0
  219. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/tts/backends/base.py +0 -0
  220. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/tts/backends/kokoro.py +0 -0
  221. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/tts/backends/piper.py +0 -0
  222. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/tts/model_manager.py +0 -0
  223. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/tts/model_registry.py +0 -0
  224. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/tts/wyoming_handler.py +0 -0
  225. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/__init__.py +0 -0
  226. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/api.py +0 -0
  227. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/backends/__init__.py +0 -0
  228. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/backends/base.py +0 -0
  229. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/backends/faster_whisper.py +0 -0
  230. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/backends/mlx.py +0 -0
  231. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/languages.py +0 -0
  232. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/model_manager.py +0 -0
  233. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/model_registry.py +0 -0
  234. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/server/whisper/wyoming_handler.py +0 -0
  235. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/services/__init__.py +0 -0
  236. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/services/_wyoming_utils.py +0 -0
  237. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/services/asr.py +0 -0
  238. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/services/llm.py +0 -0
  239. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/services/tts.py +0 -0
  240. {agent_cli-0.80.0 → agent_cli-0.82.0}/agent_cli/services/wake_word.py +0 -0
  241. {agent_cli-0.80.0 → agent_cli-0.82.0}/docker/docker-compose.yml +0 -0
  242. {agent_cli-0.80.0 → agent_cli-0.82.0}/docker/memory-proxy.Dockerfile +0 -0
  243. {agent_cli-0.80.0 → agent_cli-0.82.0}/docker/rag-proxy.Dockerfile +0 -0
  244. {agent_cli-0.80.0 → agent_cli-0.82.0}/docker/transcribe-proxy.Dockerfile +0 -0
  245. {agent_cli-0.80.0 → agent_cli-0.82.0}/docker/tts.Dockerfile +0 -0
  246. {agent_cli-0.80.0 → agent_cli-0.82.0}/docker/whisper.Dockerfile +0 -0
  247. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/CNAME +0 -0
  248. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/architecture/index.md +0 -0
  249. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/architecture/memory.md +0 -0
  250. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/architecture/rag.md +0 -0
  251. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/assistant.md +0 -0
  252. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/autocorrect.md +0 -0
  253. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/chat.md +0 -0
  254. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/config.md +0 -0
  255. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/daemon.md +0 -0
  256. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/index.md +0 -0
  257. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/install-extras.md +0 -0
  258. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/install-hotkeys.md +0 -0
  259. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/install-services.md +0 -0
  260. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/memory.md +0 -0
  261. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/rag-proxy.md +0 -0
  262. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/server/index.md +0 -0
  263. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/server/transcribe-proxy.md +0 -0
  264. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/server/tts.md +0 -0
  265. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/server/whisper.md +0 -0
  266. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/speak.md +0 -0
  267. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/start-services.md +0 -0
  268. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/transcribe-live.md +0 -0
  269. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/transcribe.md +0 -0
  270. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/commands/voice-edit.md +0 -0
  271. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/configuration.md +0 -0
  272. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/getting-started.md +0 -0
  273. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/iOS_Shortcut_Guide.md +0 -0
  274. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/index.md +0 -0
  275. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/installation/docker.md +0 -0
  276. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/installation/index.md +0 -0
  277. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/installation/linux.md +0 -0
  278. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/installation/macos.md +0 -0
  279. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/installation/nixos.md +0 -0
  280. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/installation/windows.md +0 -0
  281. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/logo-clean.svg +0 -0
  282. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/overrides/partials/integrations/analytics/custom.html +0 -0
  283. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/run_markdown_code_runner.py +0 -0
  284. {agent_cli-0.80.0 → agent_cli-0.82.0}/docs/system-integration.md +0 -0
  285. {agent_cli-0.80.0 → agent_cli-0.82.0}/example.agent-cli-config.toml +0 -0
  286. {agent_cli-0.80.0 → agent_cli-0.82.0}/justfile +0 -0
  287. {agent_cli-0.80.0 → agent_cli-0.82.0}/shell.nix +0 -0
  288. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/__init__.py +0 -0
  289. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/__init__.py +0 -0
  290. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_fix_my_text.py +0 -0
  291. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_interactive.py +0 -0
  292. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_interactive_extra.py +0 -0
  293. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_memory_add.py +0 -0
  294. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_speak.py +0 -0
  295. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_speak_e2e.py +0 -0
  296. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_transcribe.py +0 -0
  297. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_transcribe_agent.py +0 -0
  298. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_transcribe_e2e.py +0 -0
  299. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_transcribe_live.py +0 -0
  300. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_transcribe_recovery.py +0 -0
  301. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_tts_common.py +0 -0
  302. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_tts_common_extra.py +0 -0
  303. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_voice_agent_common.py +0 -0
  304. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_voice_edit.py +0 -0
  305. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_voice_edit_e2e.py +0 -0
  306. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/agents/test_wake_word_assistant.py +0 -0
  307. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/conftest.py +0 -0
  308. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/core/__init__.py +0 -0
  309. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/core/test_audio_format.py +0 -0
  310. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/core/test_chroma.py +0 -0
  311. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/core/test_sse.py +0 -0
  312. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/core/test_vad.py +0 -0
  313. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/dev/__init__.py +0 -0
  314. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/dev/test_coding_agents.py +0 -0
  315. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/dev/test_editors.py +0 -0
  316. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/dev/test_project.py +0 -0
  317. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/dev/test_terminals.py +0 -0
  318. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/dev/test_verification.py +0 -0
  319. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/install/__init__.py +0 -0
  320. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/install/test_extras.py +0 -0
  321. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/__init__.py +0 -0
  322. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_api_health.py +0 -0
  323. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_api_integration_liveish.py +0 -0
  324. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_client.py +0 -0
  325. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_engine.py +0 -0
  326. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_files.py +0 -0
  327. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_filters.py +0 -0
  328. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_git_integration.py +0 -0
  329. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_indexer.py +0 -0
  330. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_memory_integration.py +0 -0
  331. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_proxy_passthrough.py +0 -0
  332. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_store.py +0 -0
  333. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/memory/test_utils.py +0 -0
  334. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/mocks/__init__.py +0 -0
  335. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/mocks/audio.py +0 -0
  336. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/mocks/llm.py +0 -0
  337. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/mocks/wyoming.py +0 -0
  338. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/__init__.py +0 -0
  339. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_api.py +0 -0
  340. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_history.py +0 -0
  341. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_indexing.py +0 -0
  342. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_rag_client.py +0 -0
  343. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_rag_integration_liveish.py +0 -0
  344. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_rag_proxy_passthrough.py +0 -0
  345. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_retriever.py +0 -0
  346. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/rag/test_store.py +0 -0
  347. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_api.py +0 -0
  348. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_asr.py +0 -0
  349. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_audio_e2e.py +0 -0
  350. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_cli.py +0 -0
  351. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_config_cmd.py +0 -0
  352. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_daemon.py +0 -0
  353. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_docs_gen.py +0 -0
  354. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_env_vars.py +0 -0
  355. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_json_output.py +0 -0
  356. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_llm.py +0 -0
  357. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_llm_gemini.py +0 -0
  358. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_memory_tools.py +0 -0
  359. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_mlx_backend.py +0 -0
  360. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_process_manager.py +0 -0
  361. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_requires_extras.py +0 -0
  362. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_server_streaming.py +0 -0
  363. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_server_tts.py +0 -0
  364. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_server_whisper.py +0 -0
  365. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_services.py +0 -0
  366. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_tools.py +0 -0
  367. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_transformers_backend.py +0 -0
  368. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_tts.py +0 -0
  369. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_utils.py +0 -0
  370. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_wake_word.py +0 -0
  371. {agent_cli-0.80.0 → agent_cli-0.82.0}/tests/test_wyoming_utils.py +0 -0
  372. {agent_cli-0.80.0 → agent_cli-0.82.0}/uv.lock +0 -0
  373. {agent_cli-0.80.0 → agent_cli-0.82.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.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 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,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: 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()):
332
- result.update(_flatten_nested_sections(value, full_key))
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