agent-cli 0.67.2__tar.gz → 0.68.1__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 (361) hide show
  1. {agent_cli-0.67.2 → agent_cli-0.68.1}/PKG-INFO +9 -2
  2. {agent_cli-0.67.2 → agent_cli-0.68.1}/README.md +8 -1
  3. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/core/deps.py +58 -27
  4. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/install/extras.py +38 -20
  5. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/cli.py +14 -14
  6. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/architecture/index.md +27 -10
  7. agent_cli-0.68.1/tests/test_requires_extras.py +285 -0
  8. {agent_cli-0.67.2 → agent_cli-0.68.1}/zensical.toml +1 -0
  9. agent_cli-0.67.2/tests/test_requires_extras.py +0 -63
  10. {agent_cli-0.67.2 → agent_cli-0.68.1}/.claude/skills/agent-cli-dev/SKILL.md +0 -0
  11. {agent_cli-0.67.2 → agent_cli-0.68.1}/.claude/skills/agent-cli-dev/examples.md +0 -0
  12. {agent_cli-0.67.2 → agent_cli-0.68.1}/.claude-plugin/README.md +0 -0
  13. {agent_cli-0.67.2 → agent_cli-0.68.1}/.claude-plugin/marketplace.json +0 -0
  14. {agent_cli-0.67.2 → agent_cli-0.68.1}/.claude-plugin/plugin.json +0 -0
  15. {agent_cli-0.67.2 → agent_cli-0.68.1}/.claude-plugin/skills/agent-cli-dev/SKILL.md +0 -0
  16. {agent_cli-0.67.2 → agent_cli-0.68.1}/.claude-plugin/skills/agent-cli-dev/examples.md +0 -0
  17. {agent_cli-0.67.2 → agent_cli-0.68.1}/.cursorrules +0 -0
  18. {agent_cli-0.67.2 → agent_cli-0.68.1}/.env.example +0 -0
  19. {agent_cli-0.67.2 → agent_cli-0.68.1}/.github/logo.svg +0 -0
  20. {agent_cli-0.67.2 → agent_cli-0.68.1}/.github/release-drafter.yml +0 -0
  21. {agent_cli-0.67.2 → agent_cli-0.68.1}/.github/renovate.json +0 -0
  22. {agent_cli-0.67.2 → agent_cli-0.68.1}/.github/scripts/check_extras_sync.py +0 -0
  23. {agent_cli-0.67.2 → agent_cli-0.68.1}/.github/scripts/sync_requirements.py +0 -0
  24. {agent_cli-0.67.2 → agent_cli-0.68.1}/.github/workflows/automerge.yml +0 -0
  25. {agent_cli-0.67.2 → agent_cli-0.68.1}/.github/workflows/docker.yml +0 -0
  26. {agent_cli-0.67.2 → agent_cli-0.68.1}/.github/workflows/docs.yml +0 -0
  27. {agent_cli-0.67.2 → agent_cli-0.68.1}/.github/workflows/markdown-code-runner.yml +0 -0
  28. {agent_cli-0.67.2 → agent_cli-0.68.1}/.github/workflows/pytest.yml +0 -0
  29. {agent_cli-0.67.2 → agent_cli-0.68.1}/.github/workflows/release-drafter.yml +0 -0
  30. {agent_cli-0.67.2 → agent_cli-0.68.1}/.github/workflows/release.yml +0 -0
  31. {agent_cli-0.67.2 → agent_cli-0.68.1}/.github/workflows/toc.yaml +0 -0
  32. {agent_cli-0.67.2 → agent_cli-0.68.1}/.gitignore +0 -0
  33. {agent_cli-0.67.2 → agent_cli-0.68.1}/.jscpd.json +0 -0
  34. {agent_cli-0.67.2 → agent_cli-0.68.1}/.pre-commit-config.yaml +0 -0
  35. {agent_cli-0.67.2 → agent_cli-0.68.1}/.prompts/docs-review.md +0 -0
  36. {agent_cli-0.67.2 → agent_cli-0.68.1}/.prompts/pr-review.md +0 -0
  37. {agent_cli-0.67.2 → agent_cli-0.68.1}/CLAUDE.md +0 -0
  38. {agent_cli-0.67.2 → agent_cli-0.68.1}/LICENSE +0 -0
  39. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/__init__.py +0 -0
  40. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/__main__.py +0 -0
  41. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/_extras.json +0 -0
  42. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/_requirements/.gitkeep +0 -0
  43. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/_requirements/audio.txt +0 -0
  44. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/_requirements/faster-whisper.txt +0 -0
  45. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/_requirements/kokoro.txt +0 -0
  46. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/_requirements/llm.txt +0 -0
  47. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/_requirements/memory.txt +0 -0
  48. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/_requirements/mlx-whisper.txt +0 -0
  49. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/_requirements/piper.txt +0 -0
  50. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/_requirements/rag.txt +0 -0
  51. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/_requirements/server.txt +0 -0
  52. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/_requirements/speed.txt +0 -0
  53. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/_requirements/vad.txt +0 -0
  54. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/_tools.py +0 -0
  55. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/agents/__init__.py +0 -0
  56. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/agents/_voice_agent_common.py +0 -0
  57. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/agents/assistant.py +0 -0
  58. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/agents/autocorrect.py +0 -0
  59. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/agents/chat.py +0 -0
  60. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/agents/memory/__init__.py +0 -0
  61. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/agents/memory/add.py +0 -0
  62. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/agents/memory/proxy.py +0 -0
  63. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/agents/rag_proxy.py +0 -0
  64. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/agents/speak.py +0 -0
  65. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/agents/transcribe.py +0 -0
  66. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/agents/transcribe_daemon.py +0 -0
  67. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/agents/voice_edit.py +0 -0
  68. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/api.py +0 -0
  69. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/cli.py +0 -0
  70. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/config.py +0 -0
  71. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/config_cmd.py +0 -0
  72. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/constants.py +0 -0
  73. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/core/__init__.py +0 -0
  74. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/core/audio.py +0 -0
  75. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/core/audio_format.py +0 -0
  76. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/core/chroma.py +0 -0
  77. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/core/openai_proxy.py +0 -0
  78. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/core/process.py +0 -0
  79. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/core/reranker.py +0 -0
  80. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/core/sse.py +0 -0
  81. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/core/transcription_logger.py +0 -0
  82. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/core/utils.py +0 -0
  83. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/core/vad.py +0 -0
  84. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/core/watch.py +0 -0
  85. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/__init__.py +0 -0
  86. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/cli.py +0 -0
  87. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/coding_agents/__init__.py +0 -0
  88. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/coding_agents/aider.py +0 -0
  89. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/coding_agents/base.py +0 -0
  90. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/coding_agents/claude.py +0 -0
  91. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/coding_agents/codex.py +0 -0
  92. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/coding_agents/continue_dev.py +0 -0
  93. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/coding_agents/copilot.py +0 -0
  94. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/coding_agents/cursor_agent.py +0 -0
  95. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/coding_agents/gemini.py +0 -0
  96. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/coding_agents/opencode.py +0 -0
  97. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/coding_agents/registry.py +0 -0
  98. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/editors/__init__.py +0 -0
  99. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/editors/base.py +0 -0
  100. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/editors/cursor.py +0 -0
  101. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/editors/emacs.py +0 -0
  102. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/editors/jetbrains.py +0 -0
  103. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/editors/nano.py +0 -0
  104. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/editors/neovim.py +0 -0
  105. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/editors/registry.py +0 -0
  106. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/editors/sublime.py +0 -0
  107. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/editors/vim.py +0 -0
  108. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/editors/vscode.py +0 -0
  109. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/editors/zed.py +0 -0
  110. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/project.py +0 -0
  111. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/registry.py +0 -0
  112. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/skill/SKILL.md +0 -0
  113. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/skill/examples.md +0 -0
  114. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/terminals/__init__.py +0 -0
  115. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/terminals/apple_terminal.py +0 -0
  116. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/terminals/base.py +0 -0
  117. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/terminals/gnome.py +0 -0
  118. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/terminals/iterm2.py +0 -0
  119. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/terminals/kitty.py +0 -0
  120. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/terminals/registry.py +0 -0
  121. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/terminals/tmux.py +0 -0
  122. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/terminals/warp.py +0 -0
  123. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/terminals/zellij.py +0 -0
  124. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/dev/worktree.py +0 -0
  125. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/docs_gen.py +0 -0
  126. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/example-config.toml +0 -0
  127. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/install/__init__.py +0 -0
  128. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/install/common.py +0 -0
  129. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/install/hotkeys.py +0 -0
  130. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/install/services.py +0 -0
  131. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/memory/__init__.py +0 -0
  132. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/memory/_files.py +0 -0
  133. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/memory/_filters.py +0 -0
  134. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/memory/_git.py +0 -0
  135. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/memory/_indexer.py +0 -0
  136. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/memory/_ingest.py +0 -0
  137. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/memory/_persistence.py +0 -0
  138. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/memory/_prompt.py +0 -0
  139. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/memory/_retrieval.py +0 -0
  140. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/memory/_store.py +0 -0
  141. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/memory/_streaming.py +0 -0
  142. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/memory/_tasks.py +0 -0
  143. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/memory/api.py +0 -0
  144. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/memory/client.py +0 -0
  145. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/memory/engine.py +0 -0
  146. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/memory/entities.py +0 -0
  147. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/memory/models.py +0 -0
  148. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/opts.py +0 -0
  149. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/py.typed +0 -0
  150. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/rag/__init__.py +0 -0
  151. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/rag/_indexer.py +0 -0
  152. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/rag/_indexing.py +0 -0
  153. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/rag/_prompt.py +0 -0
  154. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/rag/_retriever.py +0 -0
  155. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/rag/_store.py +0 -0
  156. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/rag/_utils.py +0 -0
  157. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/rag/api.py +0 -0
  158. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/rag/client.py +0 -0
  159. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/rag/engine.py +0 -0
  160. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/rag/models.py +0 -0
  161. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/.runtime/.gitkeep +0 -0
  162. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/__init__.py +0 -0
  163. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/check_plugin_skill_sync.py +0 -0
  164. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/linux-hotkeys/README.md +0 -0
  165. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/linux-hotkeys/toggle-autocorrect.sh +0 -0
  166. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/linux-hotkeys/toggle-transcription.sh +0 -0
  167. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/linux-hotkeys/toggle-voice-edit.sh +0 -0
  168. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/macos-hotkeys/README.md +0 -0
  169. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/macos-hotkeys/skhd-config-example +0 -0
  170. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/macos-hotkeys/toggle-autocorrect.sh +0 -0
  171. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/macos-hotkeys/toggle-transcription.sh +0 -0
  172. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/macos-hotkeys/toggle-voice-edit.sh +0 -0
  173. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/nvidia-asr-server/README.md +0 -0
  174. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/nvidia-asr-server/pyproject.toml +0 -0
  175. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/nvidia-asr-server/server.py +0 -0
  176. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/nvidia-asr-server/shell.nix +0 -0
  177. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/nvidia-asr-server/uv.lock +0 -0
  178. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/run-openwakeword.sh +0 -0
  179. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/run-piper-windows.ps1 +0 -0
  180. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/run-piper.sh +0 -0
  181. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/run-whisper-linux.sh +0 -0
  182. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/run-whisper-macos.sh +0 -0
  183. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/run-whisper-windows.ps1 +0 -0
  184. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/run-whisper.sh +0 -0
  185. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/run_faster_whisper_server.py +0 -0
  186. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/setup-linux-hotkeys.sh +0 -0
  187. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/setup-linux.sh +0 -0
  188. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/setup-macos-hotkeys.sh +0 -0
  189. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/setup-macos.sh +0 -0
  190. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/setup-windows.ps1 +0 -0
  191. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/start-all-services-windows.ps1 +0 -0
  192. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/start-all-services.sh +0 -0
  193. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/scripts/sync_extras.py +0 -0
  194. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/__init__.py +0 -0
  195. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/common.py +0 -0
  196. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/model_manager.py +0 -0
  197. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/model_registry.py +0 -0
  198. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/proxy/__init__.py +0 -0
  199. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/proxy/api.py +0 -0
  200. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/streaming.py +0 -0
  201. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/tts/__init__.py +0 -0
  202. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/tts/api.py +0 -0
  203. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/tts/backends/__init__.py +0 -0
  204. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/tts/backends/base.py +0 -0
  205. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/tts/backends/kokoro.py +0 -0
  206. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/tts/backends/piper.py +0 -0
  207. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/tts/model_manager.py +0 -0
  208. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/tts/model_registry.py +0 -0
  209. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/tts/wyoming_handler.py +0 -0
  210. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/whisper/__init__.py +0 -0
  211. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/whisper/api.py +0 -0
  212. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/whisper/backends/__init__.py +0 -0
  213. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/whisper/backends/base.py +0 -0
  214. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/whisper/backends/faster_whisper.py +0 -0
  215. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/whisper/backends/mlx.py +0 -0
  216. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/whisper/languages.py +0 -0
  217. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/whisper/model_manager.py +0 -0
  218. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/whisper/model_registry.py +0 -0
  219. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/server/whisper/wyoming_handler.py +0 -0
  220. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/services/__init__.py +0 -0
  221. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/services/_wyoming_utils.py +0 -0
  222. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/services/asr.py +0 -0
  223. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/services/llm.py +0 -0
  224. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/services/tts.py +0 -0
  225. {agent_cli-0.67.2 → agent_cli-0.68.1}/agent_cli/services/wake_word.py +0 -0
  226. {agent_cli-0.67.2 → agent_cli-0.68.1}/docker/docker-compose.yml +0 -0
  227. {agent_cli-0.67.2 → agent_cli-0.68.1}/docker/transcription-proxy.Dockerfile +0 -0
  228. {agent_cli-0.67.2 → agent_cli-0.68.1}/docker/tts.Dockerfile +0 -0
  229. {agent_cli-0.67.2 → agent_cli-0.68.1}/docker/whisper.Dockerfile +0 -0
  230. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/CNAME +0 -0
  231. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/architecture/memory.md +0 -0
  232. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/architecture/rag.md +0 -0
  233. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/assistant.md +0 -0
  234. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/autocorrect.md +0 -0
  235. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/chat.md +0 -0
  236. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/config.md +0 -0
  237. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/dev.md +0 -0
  238. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/index.md +0 -0
  239. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/install-extras.md +0 -0
  240. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/install-hotkeys.md +0 -0
  241. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/install-services.md +0 -0
  242. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/memory.md +0 -0
  243. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/rag-proxy.md +0 -0
  244. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/server/index.md +0 -0
  245. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/server/transcription-proxy.md +0 -0
  246. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/server/tts.md +0 -0
  247. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/server/whisper.md +0 -0
  248. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/speak.md +0 -0
  249. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/start-services.md +0 -0
  250. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/transcribe-daemon.md +0 -0
  251. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/transcribe.md +0 -0
  252. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/commands/voice-edit.md +0 -0
  253. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/configuration.md +0 -0
  254. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/getting-started.md +0 -0
  255. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/iOS_Shortcut_Guide.md +0 -0
  256. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/index.md +0 -0
  257. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/installation/docker.md +0 -0
  258. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/installation/index.md +0 -0
  259. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/installation/linux.md +0 -0
  260. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/installation/macos.md +0 -0
  261. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/installation/nixos.md +0 -0
  262. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/installation/windows.md +0 -0
  263. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/logo-clean.svg +0 -0
  264. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/overrides/partials/integrations/analytics/custom.html +0 -0
  265. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/run_markdown_code_runner.py +0 -0
  266. {agent_cli-0.67.2 → agent_cli-0.68.1}/docs/system-integration.md +0 -0
  267. {agent_cli-0.67.2 → agent_cli-0.68.1}/example.agent-cli-config.toml +0 -0
  268. {agent_cli-0.67.2 → agent_cli-0.68.1}/justfile +0 -0
  269. {agent_cli-0.67.2 → agent_cli-0.68.1}/pyproject.toml +0 -0
  270. {agent_cli-0.67.2 → agent_cli-0.68.1}/shell.nix +0 -0
  271. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/__init__.py +0 -0
  272. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/agents/__init__.py +0 -0
  273. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/agents/test_fix_my_text.py +0 -0
  274. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/agents/test_interactive.py +0 -0
  275. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/agents/test_interactive_extra.py +0 -0
  276. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/agents/test_memory_add.py +0 -0
  277. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/agents/test_speak.py +0 -0
  278. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/agents/test_speak_e2e.py +0 -0
  279. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/agents/test_transcribe.py +0 -0
  280. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/agents/test_transcribe_agent.py +0 -0
  281. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/agents/test_transcribe_daemon.py +0 -0
  282. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/agents/test_transcribe_e2e.py +0 -0
  283. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/agents/test_transcribe_recovery.py +0 -0
  284. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/agents/test_tts_common.py +0 -0
  285. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/agents/test_tts_common_extra.py +0 -0
  286. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/agents/test_voice_agent_common.py +0 -0
  287. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/agents/test_voice_edit.py +0 -0
  288. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/agents/test_voice_edit_e2e.py +0 -0
  289. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/agents/test_wake_word_assistant.py +0 -0
  290. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/conftest.py +0 -0
  291. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/core/__init__.py +0 -0
  292. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/core/test_audio_format.py +0 -0
  293. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/core/test_chroma.py +0 -0
  294. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/core/test_sse.py +0 -0
  295. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/core/test_vad.py +0 -0
  296. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/core/test_watch.py +0 -0
  297. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/dev/__init__.py +0 -0
  298. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/dev/test_cli.py +0 -0
  299. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/dev/test_coding_agents.py +0 -0
  300. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/dev/test_editors.py +0 -0
  301. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/dev/test_project.py +0 -0
  302. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/dev/test_terminals.py +0 -0
  303. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/dev/test_verification.py +0 -0
  304. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/dev/test_worktree.py +0 -0
  305. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/install/__init__.py +0 -0
  306. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/install/test_extras.py +0 -0
  307. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/memory/__init__.py +0 -0
  308. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/memory/test_api_health.py +0 -0
  309. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/memory/test_api_integration_liveish.py +0 -0
  310. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/memory/test_client.py +0 -0
  311. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/memory/test_engine.py +0 -0
  312. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/memory/test_files.py +0 -0
  313. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/memory/test_filters.py +0 -0
  314. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/memory/test_git_integration.py +0 -0
  315. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/memory/test_indexer.py +0 -0
  316. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/memory/test_memory_integration.py +0 -0
  317. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/memory/test_proxy_passthrough.py +0 -0
  318. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/memory/test_store.py +0 -0
  319. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/memory/test_utils.py +0 -0
  320. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/mocks/__init__.py +0 -0
  321. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/mocks/audio.py +0 -0
  322. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/mocks/llm.py +0 -0
  323. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/mocks/wyoming.py +0 -0
  324. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/rag/__init__.py +0 -0
  325. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/rag/test_api.py +0 -0
  326. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/rag/test_engine.py +0 -0
  327. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/rag/test_history.py +0 -0
  328. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/rag/test_indexer.py +0 -0
  329. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/rag/test_indexing.py +0 -0
  330. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/rag/test_rag_client.py +0 -0
  331. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/rag/test_rag_integration_liveish.py +0 -0
  332. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/rag/test_rag_proxy_passthrough.py +0 -0
  333. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/rag/test_retriever.py +0 -0
  334. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/rag/test_store.py +0 -0
  335. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/rag/test_utils.py +0 -0
  336. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_api.py +0 -0
  337. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_api_integration.py +0 -0
  338. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_asr.py +0 -0
  339. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_asr_recovery.py +0 -0
  340. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_audio_e2e.py +0 -0
  341. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_cli.py +0 -0
  342. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_config.py +0 -0
  343. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_config_cmd.py +0 -0
  344. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_docs_gen.py +0 -0
  345. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_env_vars.py +0 -0
  346. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_json_output.py +0 -0
  347. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_llm.py +0 -0
  348. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_llm_gemini.py +0 -0
  349. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_memory_tools.py +0 -0
  350. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_mlx_backend.py +0 -0
  351. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_process_manager.py +0 -0
  352. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_server_streaming.py +0 -0
  353. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_server_tts.py +0 -0
  354. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_server_whisper.py +0 -0
  355. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_services.py +0 -0
  356. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_tools.py +0 -0
  357. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_tts.py +0 -0
  358. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_utils.py +0 -0
  359. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_wake_word.py +0 -0
  360. {agent_cli-0.67.2 → agent_cli-0.68.1}/tests/test_wyoming_utils.py +0 -0
  361. {agent_cli-0.67.2 → agent_cli-0.68.1}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-cli
3
- Version: 0.67.2
3
+ Version: 0.68.1
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>
@@ -459,7 +459,14 @@ All necessary scripts are bundled with the package, so you can run these command
459
459
 
460
460
  #### Installing Optional Extras
461
461
 
462
- Some features require additional Python dependencies. Use `install-extras` to install them with pinned versions:
462
+ Some features require additional Python dependencies. By default, **agent-cli will auto-install missing extras** when you run a command that needs them. To disable this, set `AGENT_CLI_NO_AUTO_INSTALL=1` or add to your config file:
463
+
464
+ ```toml
465
+ [settings]
466
+ auto_install_extras = false
467
+ ```
468
+
469
+ You can also manually install extras with `install-extras`:
463
470
 
464
471
  ```bash
465
472
  # List available extras
@@ -377,7 +377,14 @@ All necessary scripts are bundled with the package, so you can run these command
377
377
 
378
378
  #### Installing Optional Extras
379
379
 
380
- Some features require additional Python dependencies. Use `install-extras` to install them with pinned versions:
380
+ Some features require additional Python dependencies. By default, **agent-cli will auto-install missing extras** when you run a command that needs them. To disable this, set `AGENT_CLI_NO_AUTO_INSTALL=1` or add to your config file:
381
+
382
+ ```toml
383
+ [settings]
384
+ auto_install_extras = false
385
+ ```
386
+
387
+ You can also manually install extras with `install-extras`:
381
388
 
382
389
  ```bash
383
390
  # List available extras
@@ -4,19 +4,29 @@ from __future__ import annotations
4
4
 
5
5
  import functools
6
6
  import json
7
+ import os
7
8
  from importlib.util import find_spec
8
9
  from pathlib import Path
9
10
  from typing import TYPE_CHECKING, TypeVar
10
11
 
11
12
  import typer
12
13
 
13
- from agent_cli.core.utils import print_error_message
14
+ from agent_cli.config import load_config
15
+ from agent_cli.core.utils import console, print_error_message
14
16
 
15
17
  if TYPE_CHECKING:
16
18
  from collections.abc import Callable
17
19
 
18
20
  F = TypeVar("F", bound="Callable[..., object]")
19
21
 
22
+
23
+ def _get_auto_install_setting() -> bool:
24
+ """Check if auto-install is enabled (default: True)."""
25
+ if os.environ.get("AGENT_CLI_NO_AUTO_INSTALL", "").lower() in ("1", "true", "yes"):
26
+ return False
27
+ return load_config().get("settings", {}).get("auto_install_extras", True)
28
+
29
+
20
30
  # Load extras from JSON file
21
31
  _EXTRAS_FILE = Path(__file__).parent.parent / "_extras.json"
22
32
  EXTRAS: dict[str, tuple[str, list[str]]] = {
@@ -44,7 +54,7 @@ def check_extra_installed(extra: str) -> bool:
44
54
  return any(check_extra_installed(e) for e in extra.split("|"))
45
55
 
46
56
  if extra not in EXTRAS:
47
- return True # Unknown extra, assume OK
57
+ return False # Unknown extra, trigger install attempt to surface error
48
58
  _, packages = EXTRAS[extra]
49
59
 
50
60
  # All packages must be installed
@@ -116,44 +126,65 @@ def get_combined_install_hint(extras: list[str]) -> str:
116
126
  return "\n".join(lines)
117
127
 
118
128
 
119
- def requires_extras(*extras: str) -> Callable[[F], F]:
120
- """Decorator to declare required extras for a command.
121
-
122
- When a required extra is missing, the decorator prints a helpful error
123
- message and exits with code 1.
124
-
125
- The decorator stores the required extras on the function for test validation.
129
+ def _try_auto_install(missing: list[str]) -> bool:
130
+ """Attempt to auto-install missing extras. Returns True if successful."""
131
+ from agent_cli.install.extras import install_extras_programmatic # noqa: PLC0415
132
+
133
+ # Flatten alternatives (e.g., "piper|kokoro" -> just pick the first one)
134
+ extras_to_install = []
135
+ for extra in missing:
136
+ if "|" in extra:
137
+ # For alternatives, install the first option
138
+ extras_to_install.append(extra.split("|")[0])
139
+ else:
140
+ extras_to_install.append(extra)
141
+
142
+ console.print(
143
+ f"[yellow]Auto-installing missing extras: {', '.join(extras_to_install)}[/]",
144
+ )
145
+ return install_extras_programmatic(extras_to_install)
146
+
147
+
148
+ def _check_and_install_extras(extras: tuple[str, ...]) -> list[str]:
149
+ """Check for missing extras and attempt auto-install. Returns list of still-missing."""
150
+ missing = [e for e in extras if not check_extra_installed(e)]
151
+ if not missing:
152
+ return []
153
+
154
+ if not _get_auto_install_setting():
155
+ print_error_message(get_combined_install_hint(missing))
156
+ return missing
157
+
158
+ if not _try_auto_install(missing):
159
+ print_error_message("Auto-install failed.\n" + get_combined_install_hint(missing))
160
+ return missing
161
+
162
+ console.print("[green]Installation complete![/]")
163
+ still_missing = [e for e in extras if not check_extra_installed(e)]
164
+ if still_missing:
165
+ print_error_message(
166
+ "Auto-install completed but some dependencies are still missing.\n"
167
+ + get_combined_install_hint(still_missing),
168
+ )
169
+ return still_missing
126
170
 
127
- Process management flags (--stop, --status, --toggle) skip the dependency
128
- check since they just manage running processes without using the actual
129
- dependencies.
130
171
 
131
- Example:
132
- @app.command("rag-proxy")
133
- @requires_extras("rag")
134
- def rag_proxy(...):
135
- ...
172
+ def requires_extras(*extras: str) -> Callable[[F], F]:
173
+ """Decorator to declare required extras for a command.
136
174
 
175
+ Auto-installs missing extras by default. Disable via AGENT_CLI_NO_AUTO_INSTALL=1
176
+ or config [settings] auto_install_extras = false.
137
177
  """
138
178
 
139
179
  def decorator(func: F) -> F:
140
- # Store extras on function for test introspection
141
180
  func._required_extras = extras # type: ignore[attr-defined]
142
181
 
143
182
  @functools.wraps(func)
144
183
  def wrapper(*args: object, **kwargs: object) -> object:
145
- # Skip dependency check for process management and info operations
146
- # These don't need the actual dependencies, just manage processes or list info
147
- if any(kwargs.get(flag) for flag in ("stop", "status", "toggle", "list_devices")):
148
- return func(*args, **kwargs)
149
-
150
- missing = [e for e in extras if not check_extra_installed(e)]
151
- if missing:
152
- print_error_message(get_combined_install_hint(missing))
184
+ if _check_and_install_extras(extras):
153
185
  raise typer.Exit(1)
154
186
  return func(*args, **kwargs)
155
187
 
156
- # Preserve the extras on wrapper too
157
188
  wrapper._required_extras = extras # type: ignore[attr-defined]
158
189
  return wrapper # type: ignore[return-value]
159
190
 
@@ -86,6 +86,38 @@ def _install_cmd() -> list[str]:
86
86
  return cmd
87
87
 
88
88
 
89
+ def _install_extras_impl(extras: list[str], *, quiet: bool = False) -> bool:
90
+ """Install extras. Returns True on success, False on failure."""
91
+ if _is_uv_tool_install():
92
+ current_extras = _get_current_uv_tool_extras()
93
+ new_extras = sorted(set(current_extras) | set(extras))
94
+ return _install_via_uv_tool(new_extras)
95
+
96
+ cmd = _install_cmd()
97
+ for extra in extras:
98
+ req_file = _requirements_path(extra)
99
+ if not quiet:
100
+ console.print(f"Installing [cyan]{extra}[/]...")
101
+ result = subprocess.run(
102
+ [*cmd, "-r", str(req_file)],
103
+ check=False,
104
+ capture_output=quiet,
105
+ )
106
+ if result.returncode != 0:
107
+ return False
108
+ return True
109
+
110
+
111
+ def install_extras_programmatic(extras: list[str], *, quiet: bool = False) -> bool:
112
+ """Install extras programmatically (for auto-install feature)."""
113
+ available = _available_extras()
114
+ valid = [e for e in extras if e in available]
115
+ invalid = [e for e in extras if e not in available]
116
+ if invalid:
117
+ console.print(f"[yellow]Unknown extras (skipped): {', '.join(invalid)}[/]")
118
+ return bool(valid) and _install_extras_impl(valid, quiet=quiet)
119
+
120
+
89
121
  @app.command("install-extras", rich_help_panel="Installation")
90
122
  def install_extras(
91
123
  extras: Annotated[list[str] | None, typer.Argument(help="Extras to install")] = None,
@@ -128,25 +160,11 @@ def install_extras(
128
160
  print_error_message(f"Unknown extras: {invalid}. Use --list to see available.")
129
161
  raise typer.Exit(1)
130
162
 
131
- # If running from uv tool install, reinstall with extras to persist them
163
+ if not _install_extras_impl(extras):
164
+ print_error_message("Failed to install extras")
165
+ raise typer.Exit(1)
166
+
132
167
  if _is_uv_tool_install():
133
- current_extras = _get_current_uv_tool_extras()
134
- new_extras = sorted(set(current_extras) | set(extras))
135
- if not _install_via_uv_tool(new_extras):
136
- print_error_message("Failed to reinstall via uv tool")
137
- raise typer.Exit(1)
138
168
  console.print("[green]Done! Extras will persist across uv tool upgrade.[/]")
139
- return
140
-
141
- # Standard pip/uv pip install for non-tool environments
142
- cmd = _install_cmd()
143
-
144
- for extra in extras:
145
- req_file = _requirements_path(extra)
146
- console.print(f"Installing [cyan]{extra}[/]...")
147
- result = subprocess.run([*cmd, "-r", str(req_file)], check=False)
148
- if result.returncode != 0:
149
- print_error_message(f"Failed to install '{extra}'")
150
- raise typer.Exit(1)
151
-
152
- console.print("[green]Done![/]")
169
+ else:
170
+ console.print("[green]Done![/]")
@@ -20,13 +20,13 @@ console = Console()
20
20
  err_console = Console(stderr=True)
21
21
  logger = logging.getLogger(__name__)
22
22
 
23
- # Check for optional dependencies
24
- HAS_UVICORN = find_spec("uvicorn") is not None
25
- HAS_FASTAPI = find_spec("fastapi") is not None
26
- HAS_FASTER_WHISPER = find_spec("faster_whisper") is not None
27
- HAS_MLX_WHISPER = find_spec("mlx_whisper") is not None
28
- HAS_PIPER = find_spec("piper") is not None
29
- HAS_KOKORO = find_spec("kokoro") is not None
23
+ # Check for optional dependencies at call time (not module load time)
24
+ # This is important because auto-install may install packages after the module is loaded
25
+
26
+
27
+ def _has(package: str) -> bool:
28
+ return find_spec(package) is not None
29
+
30
30
 
31
31
  app = typer.Typer(
32
32
  name="server",
@@ -48,7 +48,7 @@ def server_callback(ctx: typer.Context) -> None:
48
48
 
49
49
  def _check_server_deps() -> None:
50
50
  """Check that server dependencies are available."""
51
- if not HAS_UVICORN or not HAS_FASTAPI:
51
+ if not _has("uvicorn") or not _has("fastapi"):
52
52
  err_console.print(
53
53
  "[bold red]Error:[/bold red] Server dependencies not installed. "
54
54
  "Run: [cyan]pip install agent-cli\\[server][/cyan] "
@@ -62,7 +62,7 @@ def _check_tts_deps(backend: str = "auto") -> None:
62
62
  _check_server_deps()
63
63
 
64
64
  if backend == "kokoro":
65
- if not HAS_KOKORO:
65
+ if not _has("kokoro"):
66
66
  err_console.print(
67
67
  "[bold red]Error:[/bold red] Kokoro backend requires kokoro. "
68
68
  "Run: [cyan]pip install agent-cli\\[tts-kokoro][/cyan] "
@@ -72,7 +72,7 @@ def _check_tts_deps(backend: str = "auto") -> None:
72
72
  return
73
73
 
74
74
  if backend == "piper":
75
- if not HAS_PIPER:
75
+ if not _has("piper"):
76
76
  err_console.print(
77
77
  "[bold red]Error:[/bold red] Piper backend requires piper-tts. "
78
78
  "Run: [cyan]pip install agent-cli\\[tts][/cyan] "
@@ -82,7 +82,7 @@ def _check_tts_deps(backend: str = "auto") -> None:
82
82
  return
83
83
 
84
84
  # For auto, check if either is available
85
- if not HAS_PIPER and not HAS_KOKORO:
85
+ if not _has("piper") and not _has("kokoro"):
86
86
  err_console.print(
87
87
  "[bold red]Error:[/bold red] No TTS backend available. "
88
88
  "Run: [cyan]pip install agent-cli\\[tts][/cyan] for Piper "
@@ -136,7 +136,7 @@ def _check_whisper_deps(backend: str, *, download_only: bool = False) -> None:
136
136
  """Check that Whisper dependencies are available."""
137
137
  _check_server_deps()
138
138
  if download_only:
139
- if not HAS_FASTER_WHISPER:
139
+ if not _has("faster_whisper"):
140
140
  err_console.print(
141
141
  "[bold red]Error:[/bold red] faster-whisper is required for --download-only. "
142
142
  "Run: [cyan]pip install agent-cli\\[whisper][/cyan] "
@@ -146,7 +146,7 @@ def _check_whisper_deps(backend: str, *, download_only: bool = False) -> None:
146
146
  return
147
147
 
148
148
  if backend == "mlx":
149
- if not HAS_MLX_WHISPER:
149
+ if not _has("mlx_whisper"):
150
150
  err_console.print(
151
151
  "[bold red]Error:[/bold red] MLX Whisper backend requires mlx-whisper. "
152
152
  "Run: [cyan]pip install mlx-whisper[/cyan]",
@@ -154,7 +154,7 @@ def _check_whisper_deps(backend: str, *, download_only: bool = False) -> None:
154
154
  raise typer.Exit(1)
155
155
  return
156
156
 
157
- if not HAS_FASTER_WHISPER:
157
+ if not _has("faster_whisper"):
158
158
  err_console.print(
159
159
  "[bold red]Error:[/bold red] Whisper dependencies not installed. "
160
160
  "Run: [cyan]pip install agent-cli\\[whisper][/cyan] "
@@ -141,22 +141,39 @@ Usage: [rag-proxy command](../commands/rag-proxy.md).
141
141
 
142
142
  ## Dependencies
143
143
 
144
+ Agent CLI uses a modular dependency structure. The base package is lightweight, with features installed as optional extras.
145
+
144
146
  ### Core Dependencies
145
147
 
148
+ Always installed:
149
+
146
150
  - **typer** - CLI framework
147
- - **pydantic-ai-slim** - AI agent framework with tool support
148
- - **sounddevice** - Audio I/O
149
- - **pyperclip** - Clipboard access
151
+ - **pydantic** - Data validation
150
152
  - **rich** - Terminal formatting
151
- - **wyoming** - Protocol for local AI services
152
- - **openai** - OpenAI-compatible API client
153
- - **google-genai** - Google Gemini API client
153
+ - **pyperclip** - Clipboard access
154
+ - **httpx** - HTTP client
155
+
156
+ ### Provider Extras
157
+
158
+ Install with `agent-cli install-extras <name>` or `pip install agent-cli[name]`:
159
+
160
+ | Extra | Purpose | Key Packages |
161
+ |-------|---------|--------------|
162
+ | `audio` | Voice features | sounddevice, wyoming, numpy |
163
+ | `llm` | AI processing | pydantic-ai-slim (OpenAI, Gemini) |
164
+
165
+ ### Feature Extras
154
166
 
155
- ### Optional Dependencies
167
+ | Extra | Purpose | Key Packages |
168
+ |-------|---------|--------------|
169
+ | `vad` | Voice activity detection | silero-vad |
170
+ | `rag` | Document chat | chromadb, markitdown |
171
+ | `memory` | Long-term memory | chromadb |
172
+ | `server` | Local ASR/TTS servers | fastapi |
173
+ | `faster-whisper` | Whisper (CUDA/CPU) | faster-whisper |
174
+ | `mlx-whisper` | Whisper (Apple Silicon) | mlx-whisper |
156
175
 
157
- - **silero-vad** - Voice activity detection (for `transcribe-daemon`)
158
- - **chromadb** - Vector database (for RAG and memory)
159
- - **markitdown** - Document parsing (for RAG)
176
+ See [`install-extras`](../commands/install-extras.md) for the full list and installation instructions.
160
177
 
161
178
  ## Platform Support
162
179
 
@@ -0,0 +1,285 @@
1
+ """Tests for @requires_extras decorator functionality."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from unittest.mock import patch
7
+
8
+ import pytest
9
+ import typer
10
+
11
+ from agent_cli.core.deps import (
12
+ EXTRAS,
13
+ _check_and_install_extras,
14
+ _get_auto_install_setting,
15
+ _try_auto_install,
16
+ get_install_hint,
17
+ requires_extras,
18
+ )
19
+
20
+
21
+ class TestRequiresExtrasDecorator:
22
+ """Test the requires_extras decorator functionality."""
23
+
24
+ def test_decorator_stores_extras_on_function(self) -> None:
25
+ """The decorator should store required extras on the function."""
26
+
27
+ @requires_extras("audio", "llm")
28
+ def sample_command() -> str:
29
+ return "success"
30
+
31
+ assert hasattr(sample_command, "_required_extras")
32
+ assert sample_command._required_extras == ("audio", "llm")
33
+
34
+ def test_get_install_hint_with_pipe_syntax(self) -> None:
35
+ """Pipe syntax shows all alternatives in the hint."""
36
+ hint = get_install_hint("piper|kokoro")
37
+ assert "requires one of:" in hint
38
+ assert "'piper'" in hint
39
+ assert "'kokoro'" in hint
40
+ # Brackets are escaped for rich markup (\\[)
41
+ assert "agent-cli\\[piper]" in hint
42
+ assert "agent-cli\\[kokoro]" in hint
43
+
44
+
45
+ class TestExtrasMetadata:
46
+ """Test the _extras.json metadata is properly structured."""
47
+
48
+ def test_extras_dict_structure(self) -> None:
49
+ """EXTRAS dict should have proper structure."""
50
+ assert isinstance(EXTRAS, dict)
51
+ for name, value in EXTRAS.items():
52
+ assert isinstance(name, str)
53
+ assert isinstance(value, tuple)
54
+ assert len(value) == 2
55
+ desc, packages = value
56
+ assert isinstance(desc, str)
57
+ assert isinstance(packages, list)
58
+ assert all(isinstance(pkg, str) for pkg in packages)
59
+
60
+ def test_essential_extras_present(self) -> None:
61
+ """Essential extras should be defined."""
62
+ essential = ["audio", "llm", "rag", "memory", "vad"]
63
+ for extra in essential:
64
+ assert extra in EXTRAS, f"Missing essential extra: {extra}"
65
+
66
+
67
+ class TestAutoInstallSetting:
68
+ """Test the auto-install extras configuration."""
69
+
70
+ def test_default_is_enabled(self) -> None:
71
+ """Auto-install should be enabled by default."""
72
+ env = dict(os.environ)
73
+ env.pop("AGENT_CLI_NO_AUTO_INSTALL", None)
74
+ with (
75
+ patch.dict(os.environ, env, clear=True),
76
+ patch("agent_cli.core.deps.load_config", return_value={}),
77
+ ):
78
+ assert _get_auto_install_setting() is True
79
+
80
+ def test_env_var_disables(self) -> None:
81
+ """AGENT_CLI_NO_AUTO_INSTALL=1 should disable auto-install."""
82
+ with patch.dict(os.environ, {"AGENT_CLI_NO_AUTO_INSTALL": "1"}):
83
+ assert _get_auto_install_setting() is False
84
+
85
+ def test_env_var_true_disables(self) -> None:
86
+ """AGENT_CLI_NO_AUTO_INSTALL=true should disable auto-install."""
87
+ with patch.dict(os.environ, {"AGENT_CLI_NO_AUTO_INSTALL": "true"}):
88
+ assert _get_auto_install_setting() is False
89
+
90
+ def test_env_var_yes_disables(self) -> None:
91
+ """AGENT_CLI_NO_AUTO_INSTALL=yes should disable auto-install."""
92
+ with patch.dict(os.environ, {"AGENT_CLI_NO_AUTO_INSTALL": "yes"}):
93
+ assert _get_auto_install_setting() is False
94
+
95
+ def test_config_file_disables(self) -> None:
96
+ """Config [settings] section with auto_install_extras = false should disable."""
97
+ env = dict(os.environ)
98
+ env.pop("AGENT_CLI_NO_AUTO_INSTALL", None)
99
+ with (
100
+ patch.dict(os.environ, env, clear=True),
101
+ patch(
102
+ "agent_cli.core.deps.load_config",
103
+ return_value={"settings": {"auto_install_extras": False}},
104
+ ),
105
+ ):
106
+ assert _get_auto_install_setting() is False
107
+
108
+ def test_config_file_enables(self) -> None:
109
+ """Config [settings] section with auto_install_extras = true should enable."""
110
+ env = dict(os.environ)
111
+ env.pop("AGENT_CLI_NO_AUTO_INSTALL", None)
112
+ with (
113
+ patch.dict(os.environ, env, clear=True),
114
+ patch(
115
+ "agent_cli.core.deps.load_config",
116
+ return_value={"settings": {"auto_install_extras": True}},
117
+ ),
118
+ ):
119
+ assert _get_auto_install_setting() is True
120
+
121
+ def test_env_var_takes_precedence(self) -> None:
122
+ """Environment variable should take precedence over config file."""
123
+ with (
124
+ patch.dict(os.environ, {"AGENT_CLI_NO_AUTO_INSTALL": "1"}),
125
+ patch(
126
+ "agent_cli.core.deps.load_config",
127
+ return_value={"settings": {"auto_install_extras": True}},
128
+ ),
129
+ ):
130
+ assert _get_auto_install_setting() is False
131
+
132
+
133
+ class TestTryAutoInstall:
134
+ """Test the _try_auto_install function."""
135
+
136
+ def test_flattens_alternatives(self) -> None:
137
+ """Alternatives like 'piper|kokoro' should pick the first option."""
138
+ with patch(
139
+ "agent_cli.install.extras.install_extras_programmatic",
140
+ return_value=True,
141
+ ) as mock_install:
142
+ result = _try_auto_install(["audio", "piper|kokoro"])
143
+ assert result is True
144
+ mock_install.assert_called_once_with(["audio", "piper"])
145
+
146
+ def test_returns_install_result(self) -> None:
147
+ """Should return the result from install_extras_programmatic."""
148
+ with patch(
149
+ "agent_cli.install.extras.install_extras_programmatic",
150
+ return_value=False,
151
+ ):
152
+ assert _try_auto_install(["audio"]) is False
153
+
154
+ with patch(
155
+ "agent_cli.install.extras.install_extras_programmatic",
156
+ return_value=True,
157
+ ):
158
+ assert _try_auto_install(["audio"]) is True
159
+
160
+
161
+ class TestCheckAndInstallExtras:
162
+ """Test the _check_and_install_extras function."""
163
+
164
+ def test_returns_empty_when_all_installed(self) -> None:
165
+ """Should return empty list when all extras are already installed."""
166
+ with patch("agent_cli.core.deps.check_extra_installed", return_value=True):
167
+ result = _check_and_install_extras(("audio", "llm"))
168
+ assert result == []
169
+
170
+ def test_returns_missing_when_auto_install_disabled(self) -> None:
171
+ """Should return missing list without installing when disabled."""
172
+ with (
173
+ patch("agent_cli.core.deps.check_extra_installed", return_value=False),
174
+ patch("agent_cli.core.deps._get_auto_install_setting", return_value=False),
175
+ patch("agent_cli.core.deps.print_error_message") as mock_error,
176
+ ):
177
+ result = _check_and_install_extras(("fake-extra",))
178
+ assert result == ["fake-extra"]
179
+ mock_error.assert_called_once()
180
+
181
+ def test_returns_missing_when_install_fails(self) -> None:
182
+ """Should return missing list when auto-install fails."""
183
+ with (
184
+ patch("agent_cli.core.deps.check_extra_installed", return_value=False),
185
+ patch("agent_cli.core.deps._get_auto_install_setting", return_value=True),
186
+ patch("agent_cli.core.deps._try_auto_install", return_value=False),
187
+ patch("agent_cli.core.deps.print_error_message") as mock_error,
188
+ ):
189
+ result = _check_and_install_extras(("fake-extra",))
190
+ assert result == ["fake-extra"]
191
+ mock_error.assert_called_once()
192
+ assert "Auto-install failed" in mock_error.call_args[0][0]
193
+
194
+ def test_returns_empty_when_install_succeeds(self) -> None:
195
+ """Should return empty list when auto-install succeeds."""
196
+ check_results = iter([False, True]) # First call: missing, second: installed
197
+ with (
198
+ patch(
199
+ "agent_cli.core.deps.check_extra_installed",
200
+ side_effect=lambda _: next(check_results),
201
+ ),
202
+ patch("agent_cli.core.deps._get_auto_install_setting", return_value=True),
203
+ patch("agent_cli.core.deps._try_auto_install", return_value=True),
204
+ ):
205
+ result = _check_and_install_extras(("fake-extra",))
206
+ assert result == []
207
+
208
+ def test_returns_still_missing_after_partial_install(self) -> None:
209
+ """Should return still-missing extras after install completes."""
210
+ # First check: missing, install succeeds, second check: still missing
211
+ with (
212
+ patch("agent_cli.core.deps.check_extra_installed", return_value=False),
213
+ patch("agent_cli.core.deps._get_auto_install_setting", return_value=True),
214
+ patch("agent_cli.core.deps._try_auto_install", return_value=True),
215
+ patch("agent_cli.core.deps.print_error_message") as mock_error,
216
+ ):
217
+ result = _check_and_install_extras(("fake-extra",))
218
+ assert result == ["fake-extra"]
219
+ mock_error.assert_called_once()
220
+ assert "still missing" in mock_error.call_args[0][0]
221
+
222
+
223
+ class TestDecoratorIntegration:
224
+ """Test the requires_extras decorator end-to-end behavior."""
225
+
226
+ def test_calls_function_when_extras_installed(self) -> None:
227
+ """Decorated function should run when extras are installed."""
228
+ with patch("agent_cli.core.deps.check_extra_installed", return_value=True):
229
+
230
+ @requires_extras("audio")
231
+ def my_command() -> str:
232
+ return "success"
233
+
234
+ assert my_command() == "success"
235
+
236
+ def test_exits_when_extras_missing_and_auto_install_disabled(self) -> None:
237
+ """Should exit when extras missing and auto-install is disabled."""
238
+ with (
239
+ patch("agent_cli.core.deps.check_extra_installed", return_value=False),
240
+ patch("agent_cli.core.deps._get_auto_install_setting", return_value=False),
241
+ patch("agent_cli.core.deps.print_error_message"),
242
+ ):
243
+
244
+ @requires_extras("fake-extra")
245
+ def my_command() -> str:
246
+ return "success"
247
+
248
+ with pytest.raises(typer.Exit) as exc_info:
249
+ my_command()
250
+ assert exc_info.value.exit_code == 1
251
+
252
+ def test_auto_installs_and_runs_on_success(self) -> None:
253
+ """Should auto-install, then run the function on success."""
254
+ check_results = iter([False, True]) # Missing first, then installed
255
+ with (
256
+ patch(
257
+ "agent_cli.core.deps.check_extra_installed",
258
+ side_effect=lambda _: next(check_results),
259
+ ),
260
+ patch("agent_cli.core.deps._get_auto_install_setting", return_value=True),
261
+ patch("agent_cli.core.deps._try_auto_install", return_value=True),
262
+ ):
263
+
264
+ @requires_extras("fake-extra")
265
+ def my_command() -> str:
266
+ return "success"
267
+
268
+ assert my_command() == "success"
269
+
270
+ def test_exits_when_auto_install_fails(self) -> None:
271
+ """Should exit when auto-install fails."""
272
+ with (
273
+ patch("agent_cli.core.deps.check_extra_installed", return_value=False),
274
+ patch("agent_cli.core.deps._get_auto_install_setting", return_value=True),
275
+ patch("agent_cli.core.deps._try_auto_install", return_value=False),
276
+ patch("agent_cli.core.deps.print_error_message"),
277
+ ):
278
+
279
+ @requires_extras("fake-extra")
280
+ def my_command() -> str:
281
+ return "success"
282
+
283
+ with pytest.raises(typer.Exit) as exc_info:
284
+ my_command()
285
+ assert exc_info.value.exit_code == 1
@@ -49,6 +49,7 @@ nav = [
49
49
  ] },
50
50
  { "Setup" = [
51
51
  { "config" = "commands/config.md" },
52
+ { "install-extras" = "commands/install-extras.md" },
52
53
  { "install-services" = "commands/install-services.md" },
53
54
  { "install-hotkeys" = "commands/install-hotkeys.md" },
54
55
  { "start-services" = "commands/start-services.md" },