ayechat-dev 0.36.8.20260126091750__tar.gz → 0.36.9.20260201141756__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 (154) hide show
  1. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/.github/workflows/test-windows-installer.yml +1 -1
  2. {ayechat_dev-0.36.8.20260126091750/src/ayechat_dev.egg-info → ayechat_dev-0.36.9.20260201141756}/PKG-INFO +1 -1
  3. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/pyproject.toml +1 -1
  4. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/controller/repl.py +42 -1
  5. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/plugins/shell_executor.py +5 -3
  6. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/presenter/repl_ui.py +1 -1
  7. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756/src/ayechat_dev.egg-info}/PKG-INFO +1 -1
  8. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_repl.py +235 -0
  9. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_shell_executor_plugin.py +1 -1
  10. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  11. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  12. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  13. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/.github/dependabot.yml +0 -0
  14. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/.github/workflows/build-windows-installer.yml +0 -0
  15. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/.github/workflows/message-releases-to-discord.yml +0 -0
  16. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/.github/workflows/pylint.yml +0 -0
  17. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/.github/workflows/python-publish-dev.yml +0 -0
  18. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/.github/workflows/python-publish.yml +0 -0
  19. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/.github/workflows/python-testing.yml +0 -0
  20. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/.github/workflows/test-homebrew.yml +0 -0
  21. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/.github/workflows/test-nix-github.yml +0 -0
  22. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/.github/workflows/test-nix.yml +0 -0
  23. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/.github/workflows/update-homebrew.yml +0 -0
  24. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/.gitignore +0 -0
  25. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/.pylintrc +0 -0
  26. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/BUILD.md +0 -0
  27. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/DISCLAIMER +0 -0
  28. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/Formula/aye-chat.rb +0 -0
  29. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/LICENSE +0 -0
  30. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/README.md +0 -0
  31. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/assets/aye-chat.ico +0 -0
  32. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/aye-chat.spec +0 -0
  33. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/ayechat.nix +0 -0
  34. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/flake.lock +0 -0
  35. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/flake.nix +0 -0
  36. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/installer.iss +0 -0
  37. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/publish_pypi.sh +0 -0
  38. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/requirements.txt +0 -0
  39. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/run_tests.cmd +0 -0
  40. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/run_tests.sh +0 -0
  41. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/setup.cfg +0 -0
  42. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/.gitignore +0 -0
  43. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/__init__.py +0 -0
  44. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/__main__.py +0 -0
  45. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/__main_chat__.py +0 -0
  46. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/controller/__init__.py +0 -0
  47. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/controller/command_handlers.py +0 -0
  48. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/controller/commands.py +0 -0
  49. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/controller/llm_handler.py +0 -0
  50. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/controller/llm_invoker.py +0 -0
  51. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/controller/plugin_manager.py +0 -0
  52. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/controller/tutorial.py +0 -0
  53. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/controller/util.py +0 -0
  54. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/__init__.py +0 -0
  55. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/api.py +0 -0
  56. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/ast_chunker.py +0 -0
  57. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/auth.py +0 -0
  58. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/config.py +0 -0
  59. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/download_plugins.py +0 -0
  60. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/file_processor.py +0 -0
  61. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/ignore_patterns.py +0 -0
  62. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/index_manager/__init__.py +0 -0
  63. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/index_manager/index_manager.py +0 -0
  64. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/index_manager/index_manager_executor.py +0 -0
  65. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/index_manager/index_manager_file_ops.py +0 -0
  66. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/index_manager/index_manager_state.py +0 -0
  67. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/index_manager/index_manager_utils.py +0 -0
  68. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/json_extractor.py +0 -0
  69. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/models.py +0 -0
  70. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/offline_llm_manager.py +0 -0
  71. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/onnx_manager.py +0 -0
  72. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/snapshot/__init__.py +0 -0
  73. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/snapshot/base.py +0 -0
  74. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/snapshot/file_backend.py +0 -0
  75. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/snapshot/git_ref_backend.py +0 -0
  76. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/source_collector.py +0 -0
  77. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/telemetry.py +0 -0
  78. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/vector_db.py +0 -0
  79. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/version_checker.py +0 -0
  80. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/model/write_validator.py +0 -0
  81. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/plugins/__init__.py +0 -0
  82. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/plugins/at_file_completer.py +0 -0
  83. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/plugins/auto_detect_mask.py +0 -0
  84. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/plugins/completer.py +0 -0
  85. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/plugins/local_model.py +0 -0
  86. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/plugins/offline_llm.py +0 -0
  87. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/plugins/plugin_base.py +0 -0
  88. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/plugins/slash_completer.py +0 -0
  89. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/presenter/__init__.py +0 -0
  90. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/presenter/cli_ui.py +0 -0
  91. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/presenter/diff_presenter.py +0 -0
  92. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/presenter/streaming_ui.py +0 -0
  93. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/aye/presenter/ui_utils.py +0 -0
  94. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/ayechat_dev.egg-info/SOURCES.txt +0 -0
  95. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/ayechat_dev.egg-info/dependency_links.txt +0 -0
  96. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/ayechat_dev.egg-info/entry_points.txt +0 -0
  97. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/ayechat_dev.egg-info/requires.txt +0 -0
  98. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/src/ayechat_dev.egg-info/top_level.txt +0 -0
  99. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/.gitignore +0 -0
  100. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/config/unittest-env.sh +0 -0
  101. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/e2e/test_chat_workflow.py +0 -0
  102. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_api.py +0 -0
  103. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_ast_chunker.py +0 -0
  104. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_at_file_completer.py +0 -0
  105. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_auth.py +0 -0
  106. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_auth_uat_1.py +0 -0
  107. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_auto_detect_mask.py +0 -0
  108. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_chromadb_corruption_recovery.py +0 -0
  109. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_cli.py +0 -0
  110. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_command_handlers.py +0 -0
  111. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_commands.py +0 -0
  112. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_completer_plugin.py +0 -0
  113. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_config.py +0 -0
  114. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_diff_presenter.py +0 -0
  115. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_download_plugins.py +0 -0
  116. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_file_processor.py +0 -0
  117. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_git_ref_backend.py +0 -0
  118. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_index_manager.py +0 -0
  119. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_index_manager_executor.py +0 -0
  120. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_index_manager_more.py +0 -0
  121. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_index_manager_state.py +0 -0
  122. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_index_manager_utils.py +0 -0
  123. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_llm_handler.py +0 -0
  124. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_llm_invoker.py +0 -0
  125. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_local_model_plugin.py +0 -0
  126. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_offline_llm.py +0 -0
  127. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_offline_llm_manager.py +0 -0
  128. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_onnx_manager.py +0 -0
  129. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_plugin_base.py +0 -0
  130. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_plugin_manager.py +0 -0
  131. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_presenter.py +0 -0
  132. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_rag_context_retrieval.py +0 -0
  133. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_service.py +0 -0
  134. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_slash_completer.py +0 -0
  135. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_snapshot.py +0 -0
  136. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_source_collector.py +0 -0
  137. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_streaming_ui.py +0 -0
  138. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_telemetry.py +0 -0
  139. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_tutorial.py +0 -0
  140. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_ui_utils.py +0 -0
  141. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_util.py +0 -0
  142. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_vector_db.py +0 -0
  143. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_version_checker.py +0 -0
  144. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/test_write_validator.py +0 -0
  145. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/ua/api_tests.md +0 -0
  146. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/ua/auth_tests.md +0 -0
  147. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/ua/demo_tests.md +0 -0
  148. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/ua/download_plugins_tests.md +0 -0
  149. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/ua/plugin_tests.md +0 -0
  150. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/ua/service_tests.md +0 -0
  151. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/ua/snapshot_tests.md +0 -0
  152. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/ua/source_collector_tests.md +0 -0
  153. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/tests/ua/ui_tests.md +0 -0
  154. {ayechat_dev-0.36.8.20260126091750 → ayechat_dev-0.36.9.20260201141756}/version_info.txt +0 -0
@@ -45,7 +45,7 @@ jobs:
45
45
  Write-Host "Using workflow run ID: $runId"
46
46
 
47
47
  - name: Download installer artifact
48
- uses: actions/download-artifact@v4
48
+ uses: actions/download-artifact@v7
49
49
  with:
50
50
  pattern: aye-chat-installer-*
51
51
  run-id: ${{ steps.get-run-id.outputs.RUN_ID }}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ayechat-dev
3
- Version: 0.36.8.20260126091750
3
+ Version: 0.36.9.20260201141756
4
4
  Summary: Aye Chat: Terminal-first AI Code Generator
5
5
  Author-email: "Acrotron, Inc." <info@acrotron.com>
6
6
  License: MIT
@@ -8,7 +8,7 @@ description = "Aye Chat: Terminal-first AI Code Generator"
8
8
  readme = "README.md"
9
9
  requires-python = ">=3.10, <3.14"
10
10
  dependencies = [ "typer>=0.20.0", "httpx>=0.28.1", "keyring>=25.7.0", "prompt-toolkit>=3.0.52", "pathspec>=0.12.1", "chromadb>=1.3.5", "rapidfuzz",]
11
- version = "0.36.8.20260126091750"
11
+ version = "0.36.9.20260201141756"
12
12
  [[project.authors]]
13
13
  name = "Acrotron, Inc."
14
14
  email = "info@acrotron.com"
@@ -1,7 +1,7 @@
1
1
  import os
2
2
  import json
3
3
  from pathlib import Path
4
- from typing import Optional, Any
4
+ from typing import Optional, Any, List
5
5
  import shlex
6
6
  import threading
7
7
  import glob
@@ -252,6 +252,34 @@ def create_prompt_session(completer: Any, completion_style: str = "readline") ->
252
252
  )
253
253
 
254
254
 
255
+ def _execute_forced_shell_command(command: str, args: List[str], conf: Any) -> None:
256
+ """Execute a shell command with force flag (bypasses command validation).
257
+
258
+ Used when user prefixes input with '!' to force shell execution.
259
+
260
+ Args:
261
+ command: The command to execute
262
+ args: List of arguments to pass to the command
263
+ conf: Configuration object with plugin_manager
264
+ """
265
+ telemetry.record_command(command, has_args=len(args) > 0, prefix=_CMD_PREFIX)
266
+ shell_response = conf.plugin_manager.handle_command(
267
+ "execute_shell_command",
268
+ {"command": command, "args": args, "force": True}
269
+ )
270
+ if shell_response is not None:
271
+ if "stdout" in shell_response or "stderr" in shell_response:
272
+ if shell_response.get("stdout", "").strip():
273
+ rprint(shell_response["stdout"])
274
+ if shell_response.get("stderr", "").strip():
275
+ rprint(f"[yellow]{shell_response['stderr']}[/]")
276
+ if "error" in shell_response:
277
+ rprint(f"[red]Error:[/] {shell_response['error']}")
278
+ elif "message" in shell_response:
279
+ rprint(shell_response["message"])
280
+ else:
281
+ rprint(f"[red]Error:[/] Failed to execute shell command")
282
+
255
283
 
256
284
  def chat_repl(conf: Any) -> None:
257
285
  is_first_run = run_first_time_tutorial_if_needed()
@@ -327,6 +355,14 @@ def chat_repl(conf: Any) -> None:
327
355
  chat_id = new_chat_id
328
356
  continue
329
357
 
358
+ # Check for '!' prefix - force shell execution
359
+ force_shell = False
360
+ if prompt.strip().startswith('!'):
361
+ force_shell = True
362
+ prompt = prompt.strip()[1:] # Remove the '!'
363
+ if not prompt.strip():
364
+ continue # Nothing after the '!', skip
365
+
330
366
  if not prompt.strip():
331
367
  continue
332
368
  tokens = shlex.split(prompt.strip(), posix=False)
@@ -340,6 +376,11 @@ def chat_repl(conf: Any) -> None:
340
376
 
341
377
  original_first, lowered_first = tokens[0], tokens[0].lower()
342
378
 
379
+ # If force_shell is True, execute as shell command directly and skip all other checks
380
+ if force_shell:
381
+ _execute_forced_shell_command(original_first, tokens[1:], conf)
382
+ continue
383
+
343
384
  # Normalize slash-prefixed commands: /restore -> restore, /model -> model, etc.
344
385
  if lowered_first.startswith('/'):
345
386
  lowered_first = lowered_first[1:] # Remove leading slash
@@ -10,7 +10,7 @@ from rich import print as rprint
10
10
 
11
11
  class ShellExecutorPlugin(Plugin):
12
12
  name = "shell_executor"
13
- version = "1.0.2" # Fixed Windows localization bug in command detection
13
+ version = "1.0.3" # Added force parameter for ! prefix execution
14
14
  premium = "free"
15
15
 
16
16
  # Known interactive commands that require a TTY (add more as needed)
@@ -143,15 +143,17 @@ class ShellExecutorPlugin(Plugin):
143
143
  "returncode": e.returncode
144
144
  }
145
145
  except FileNotFoundError:
146
- return None # Command not found
146
+ return {"error": f"Command not found: {command}", "stdout": "", "stderr": "", "returncode": 127}
147
147
 
148
148
  def on_command(self, command_name: str, params: Dict[str, Any]) -> Optional[Dict[str, Any]]:
149
149
  """Handle shell command execution through plugin system."""
150
150
  if command_name == "execute_shell_command":
151
151
  command = params.get("command", "")
152
152
  args = params.get("args", [])
153
+ force = params.get("force", False)
153
154
 
154
- if not self._is_valid_command(command):
155
+ # If force is False, validate the command exists first
156
+ if not force and not self._is_valid_command(command):
155
157
  return None # Command not found or not executable
156
158
 
157
159
  full_cmd_str = self._build_full_cmd(command, args)
@@ -87,7 +87,7 @@ def print_help_message():
87
87
  commands = [
88
88
  # Some commands are intentionally undocumented: keep them as such.
89
89
  ("@filename", "Include a file in your prompt inline (e.g., \"explain @main.py\"). Supports wildcards (e.g., @*.py, @src/*.js)."),
90
- ("blog <intent>", "Generate a technical deep-dive blog post derived from the current chat session and write it to blog.md."),
90
+ ("!command", "Force shell execution (e.g., \"!echo hello\")."),
91
91
  ("model", "Select a different model. Selection will persist between sessions."),
92
92
  (r"verbose \[on|off]", "Toggle verbose mode to increase or decrease chattiness (on/off, persists between sessions)"),
93
93
  (r"completion \[readline|multi]", "Switch auto-completion style (readline or multi, persists between sessions)"),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ayechat-dev
3
- Version: 0.36.8.20260126091750
3
+ Version: 0.36.9.20260201141756
4
4
  Summary: Aye Chat: Terminal-first AI Code Generator
5
5
  Author-email: "Acrotron, Inc." <info@acrotron.com>
6
6
  License: MIT
@@ -332,6 +332,241 @@ class TestRepl(TestCase):
332
332
  mock_process.assert_called_once()
333
333
 
334
334
 
335
+ class TestExecuteForcedShellCommand(TestCase):
336
+ """Tests for _execute_forced_shell_command function."""
337
+
338
+ def setUp(self):
339
+ repl.telemetry.reset()
340
+ repl.telemetry.set_enabled(False)
341
+
342
+ @patch("aye.controller.repl.rprint")
343
+ @patch("aye.controller.repl.telemetry.record_command")
344
+ def test_executes_with_force_flag_and_prints_stdout(self, mock_record, mock_rprint):
345
+ """Test that command executes with force=True and stdout is printed."""
346
+ plugin_manager = MagicMock()
347
+ plugin_manager.handle_command.return_value = {
348
+ "stdout": "file1.txt\nfile2.txt",
349
+ "stderr": "",
350
+ }
351
+ conf = SimpleNamespace(plugin_manager=plugin_manager)
352
+
353
+ repl._execute_forced_shell_command("ls", ["-la"], conf)
354
+
355
+ plugin_manager.handle_command.assert_called_once_with(
356
+ "execute_shell_command",
357
+ {"command": "ls", "args": ["-la"], "force": True}
358
+ )
359
+ mock_rprint.assert_called_once_with("file1.txt\nfile2.txt")
360
+
361
+ @patch("aye.controller.repl.rprint")
362
+ @patch("aye.controller.repl.telemetry.record_command")
363
+ def test_prints_stderr_with_yellow_formatting(self, mock_record, mock_rprint):
364
+ """Test that stderr is printed with yellow formatting."""
365
+ plugin_manager = MagicMock()
366
+ plugin_manager.handle_command.return_value = {
367
+ "stdout": "",
368
+ "stderr": "warning: something happened",
369
+ }
370
+ conf = SimpleNamespace(plugin_manager=plugin_manager)
371
+
372
+ repl._execute_forced_shell_command("cmd", [], conf)
373
+
374
+ mock_rprint.assert_called_once_with("[yellow]warning: something happened[/]")
375
+
376
+ @patch("aye.controller.repl.rprint")
377
+ @patch("aye.controller.repl.telemetry.record_command")
378
+ def test_prints_both_stdout_and_stderr(self, mock_record, mock_rprint):
379
+ """Test that both stdout and stderr are printed when present."""
380
+ plugin_manager = MagicMock()
381
+ plugin_manager.handle_command.return_value = {
382
+ "stdout": "output here",
383
+ "stderr": "warning here",
384
+ }
385
+ conf = SimpleNamespace(plugin_manager=plugin_manager)
386
+
387
+ repl._execute_forced_shell_command("cmd", ["arg1"], conf)
388
+
389
+ assert mock_rprint.call_count == 2
390
+ mock_rprint.assert_any_call("output here")
391
+ mock_rprint.assert_any_call("[yellow]warning here[/]")
392
+
393
+ @patch("aye.controller.repl.rprint")
394
+ @patch("aye.controller.repl.telemetry.record_command")
395
+ def test_prints_error_with_red_formatting(self, mock_record, mock_rprint):
396
+ """Test that error in response is printed with red formatting."""
397
+ plugin_manager = MagicMock()
398
+ plugin_manager.handle_command.return_value = {
399
+ "stdout": "partial output",
400
+ "stderr": "",
401
+ "error": "Command failed with exit code 1",
402
+ }
403
+ conf = SimpleNamespace(plugin_manager=plugin_manager)
404
+
405
+ repl._execute_forced_shell_command("failing_cmd", [], conf)
406
+
407
+ assert mock_rprint.call_count == 2
408
+ mock_rprint.assert_any_call("partial output")
409
+ mock_rprint.assert_any_call("[red]Error:[/] Command failed with exit code 1")
410
+
411
+ @patch("aye.controller.repl.rprint")
412
+ @patch("aye.controller.repl.telemetry.record_command")
413
+ def test_prints_message_for_interactive_commands(self, mock_record, mock_rprint):
414
+ """Test that message-only responses (e.g., interactive commands) are printed."""
415
+ plugin_manager = MagicMock()
416
+ plugin_manager.handle_command.return_value = {
417
+ "message": "Interactive command 'vim' completed (exit code: 0)."
418
+ }
419
+ conf = SimpleNamespace(plugin_manager=plugin_manager)
420
+
421
+ repl._execute_forced_shell_command("vim", ["file.txt"], conf)
422
+
423
+ mock_rprint.assert_called_once_with(
424
+ "Interactive command 'vim' completed (exit code: 0)."
425
+ )
426
+
427
+ @patch("aye.controller.repl.rprint")
428
+ @patch("aye.controller.repl.telemetry.record_command")
429
+ def test_prints_error_when_plugin_returns_none(self, mock_record, mock_rprint):
430
+ """Test that error is printed when plugin returns None."""
431
+ plugin_manager = MagicMock()
432
+ plugin_manager.handle_command.return_value = None
433
+ conf = SimpleNamespace(plugin_manager=plugin_manager)
434
+
435
+ repl._execute_forced_shell_command("nonexistent", [], conf)
436
+
437
+ mock_rprint.assert_called_once_with("[red]Error:[/] Failed to execute shell command")
438
+
439
+ @patch("aye.controller.repl.rprint")
440
+ @patch("aye.controller.repl.telemetry.record_command")
441
+ def test_records_telemetry_with_args(self, mock_record, mock_rprint):
442
+ """Test that telemetry is recorded with has_args=True when args present."""
443
+ plugin_manager = MagicMock()
444
+ plugin_manager.handle_command.return_value = {"stdout": "ok", "stderr": ""}
445
+ conf = SimpleNamespace(plugin_manager=plugin_manager)
446
+
447
+ repl._execute_forced_shell_command("git", ["status", "-s"], conf)
448
+
449
+ mock_record.assert_called_once_with("git", has_args=True, prefix="cmd:")
450
+
451
+ @patch("aye.controller.repl.rprint")
452
+ @patch("aye.controller.repl.telemetry.record_command")
453
+ def test_records_telemetry_without_args(self, mock_record, mock_rprint):
454
+ """Test that telemetry is recorded with has_args=False when no args."""
455
+ plugin_manager = MagicMock()
456
+ plugin_manager.handle_command.return_value = {"stdout": "ok", "stderr": ""}
457
+ conf = SimpleNamespace(plugin_manager=plugin_manager)
458
+
459
+ repl._execute_forced_shell_command("pwd", [], conf)
460
+
461
+ mock_record.assert_called_once_with("pwd", has_args=False, prefix="cmd:")
462
+
463
+ @patch("aye.controller.repl.rprint")
464
+ @patch("aye.controller.repl.telemetry.record_command")
465
+ def test_does_not_print_empty_stdout_or_stderr(self, mock_record, mock_rprint):
466
+ """Test that empty stdout/stderr strings don't result in prints."""
467
+ plugin_manager = MagicMock()
468
+ plugin_manager.handle_command.return_value = {
469
+ "stdout": " ", # whitespace only
470
+ "stderr": "",
471
+ }
472
+ conf = SimpleNamespace(plugin_manager=plugin_manager)
473
+
474
+ repl._execute_forced_shell_command("cmd", [], conf)
475
+
476
+ mock_rprint.assert_not_called()
477
+
478
+ @patch("aye.controller.repl.rprint")
479
+ @patch("aye.controller.repl.telemetry.record_command")
480
+ def test_handles_stdout_stderr_and_error_together(self, mock_record, mock_rprint):
481
+ """Test all three outputs printed when all present."""
482
+ plugin_manager = MagicMock()
483
+ plugin_manager.handle_command.return_value = {
484
+ "stdout": "some output",
485
+ "stderr": "some warning",
486
+ "error": "but it failed",
487
+ }
488
+ conf = SimpleNamespace(plugin_manager=plugin_manager)
489
+
490
+ repl._execute_forced_shell_command("cmd", ["arg"], conf)
491
+
492
+ assert mock_rprint.call_count == 3
493
+ mock_rprint.assert_any_call("some output")
494
+ mock_rprint.assert_any_call("[yellow]some warning[/]")
495
+ mock_rprint.assert_any_call("[red]Error:[/] but it failed")
496
+
497
+
498
+ def test_chat_repl_force_shell_prefix_executes_via_forced_function():
499
+ """Test that ! prefix triggers _execute_forced_shell_command in REPL."""
500
+ with (
501
+ patch("aye.controller.repl.PromptSession") as mock_session_cls,
502
+ patch("aye.controller.repl.run_first_time_tutorial_if_needed", return_value=False),
503
+ patch("aye.controller.repl._prompt_for_telemetry_consent_if_needed", return_value=False),
504
+ patch("aye.controller.repl.print_startup_header"),
505
+ patch("aye.controller.repl.print_prompt", return_value="> "),
506
+ patch("aye.controller.repl.Path") as mock_path,
507
+ patch("aye.controller.repl._execute_forced_shell_command") as mock_forced,
508
+ patch("aye.controller.repl.collect_and_send_feedback"),
509
+ ):
510
+ session = MagicMock()
511
+ session.prompt.side_effect = ["!mycommand arg1 arg2", "exit"]
512
+ mock_session_cls.return_value = session
513
+
514
+ _setup_mock_chat_id_path(mock_path)
515
+
516
+ plugin_manager = MagicMock()
517
+ plugin_manager.handle_command.side_effect = [{"completer": None}]
518
+
519
+ conf = SimpleNamespace(
520
+ root=Path.cwd(),
521
+ file_mask="*.py",
522
+ plugin_manager=plugin_manager,
523
+ index_manager=None,
524
+ verbose=False,
525
+ selected_model="model",
526
+ use_rag=True,
527
+ )
528
+
529
+ repl.chat_repl(conf)
530
+
531
+ mock_forced.assert_called_once_with("mycommand", ["arg1", "arg2"], conf)
532
+
533
+
534
+ def test_chat_repl_force_shell_empty_after_bang_skips():
535
+ """Test that '!' alone is skipped without error."""
536
+ with (
537
+ patch("aye.controller.repl.PromptSession") as mock_session_cls,
538
+ patch("aye.controller.repl.run_first_time_tutorial_if_needed", return_value=False),
539
+ patch("aye.controller.repl._prompt_for_telemetry_consent_if_needed", return_value=False),
540
+ patch("aye.controller.repl.print_startup_header"),
541
+ patch("aye.controller.repl.print_prompt", return_value="> "),
542
+ patch("aye.controller.repl.Path") as mock_path,
543
+ patch("aye.controller.repl._execute_forced_shell_command") as mock_forced,
544
+ patch("aye.controller.repl.collect_and_send_feedback"),
545
+ ):
546
+ session = MagicMock()
547
+ session.prompt.side_effect = ["!", "! ", "exit"]
548
+ mock_session_cls.return_value = session
549
+
550
+ _setup_mock_chat_id_path(mock_path)
551
+
552
+ plugin_manager = MagicMock()
553
+ plugin_manager.handle_command.side_effect = [{"completer": None}]
554
+
555
+ conf = SimpleNamespace(
556
+ root=Path.cwd(),
557
+ file_mask="*.py",
558
+ plugin_manager=plugin_manager,
559
+ index_manager=None,
560
+ verbose=False,
561
+ selected_model="model",
562
+ use_rag=True,
563
+ )
564
+
565
+ repl.chat_repl(conf)
566
+
567
+ mock_forced.assert_not_called()
568
+
569
+
335
570
  def test_create_key_bindings_registers_two_enter_bindings_and_handlers_work():
336
571
  bindings = repl.create_key_bindings()
337
572
  enter_bindings = bindings.get_bindings_for_keys((Keys.Enter,))
@@ -153,7 +153,7 @@ class TestShellExecutorPlugin(TestCase):
153
153
  @patch('subprocess.run', side_effect=FileNotFoundError)
154
154
  def test_execute_non_interactive_file_not_found(self, mock_run):
155
155
  result = self.plugin._execute_non_interactive('badcmd', [])
156
- self.assertIsNone(result)
156
+ self.assertEqual(result, {'error': 'Command not found: badcmd', 'stdout': '', 'stderr': '', 'returncode': 127})
157
157
 
158
158
  @patch.object(ShellExecutorPlugin, '_is_valid_command', return_value=True)
159
159
  @patch.object(ShellExecutorPlugin, '_execute_non_interactive')