ace-git-copilot 0.4.0__tar.gz → 0.4.2__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 (134) hide show
  1. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/PKG-INFO +1 -1
  2. ace_git_copilot-0.4.2/ace/__init__.py +1 -0
  3. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/history_analyzer.py +2 -1
  4. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/cli.py +75 -23
  5. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/pyproject.toml +1 -1
  6. ace_git_copilot-0.4.2/tests/test_interceptor.py +42 -0
  7. ace_git_copilot-0.4.0/ace/__init__.py +0 -1
  8. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/AGENTS.md +0 -0
  9. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/BRIEFING.md +0 -0
  10. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/ORIGINAL_REQUEST.md +0 -0
  11. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/e2e_testing_track/BRIEFING.md +0 -0
  12. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/e2e_testing_track/ORIGINAL_REQUEST.md +0 -0
  13. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/e2e_testing_track/SCOPE.md +0 -0
  14. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/e2e_testing_track/progress.md +0 -0
  15. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/explorer_init/BRIEFING.md +0 -0
  16. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/explorer_init/ORIGINAL_REQUEST.md +0 -0
  17. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/explorer_init/emojis_list.txt +0 -0
  18. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/explorer_init/find_unused_modules.py +0 -0
  19. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/explorer_init/handoff.md +0 -0
  20. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/explorer_init/measure_lazy_startup.py +0 -0
  21. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/explorer_init/measure_startup.py +0 -0
  22. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/explorer_init/profile_imports.py +0 -0
  23. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/explorer_init/progress.md +0 -0
  24. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/explorer_init/run_importtime.py +0 -0
  25. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/explorer_init/search_banner.py +0 -0
  26. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/explorer_init/search_emojis.py +0 -0
  27. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/explorer_init/search_git_usages.py +0 -0
  28. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/explorer_init/search_usages.py +0 -0
  29. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/explorer_init/test_import_profiler.py +0 -0
  30. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/explorer_init/test_mocked_sys.py +0 -0
  31. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/handoff.md +0 -0
  32. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/implementation_track/BRIEFING.md +0 -0
  33. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/implementation_track/ORIGINAL_REQUEST.md +0 -0
  34. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/implementation_track/explorer_initial_report.md +0 -0
  35. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/implementation_track/progress.md +0 -0
  36. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/orchestrator/.gitkeep +0 -0
  37. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/orchestrator/BRIEFING.md +0 -0
  38. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/orchestrator/ORIGINAL_REQUEST.md +0 -0
  39. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/orchestrator/PROJECT.md +0 -0
  40. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/orchestrator/progress.md +0 -0
  41. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/teamwork_preview_explorer_e2e_explore/BRIEFING.md +0 -0
  42. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/teamwork_preview_explorer_e2e_explore/ORIGINAL_REQUEST.md +0 -0
  43. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/teamwork_preview_explorer_e2e_explore/handoff.md +0 -0
  44. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/teamwork_preview_explorer_e2e_explore/progress.md +0 -0
  45. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/worker_e2e_testing/BRIEFING.md +0 -0
  46. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/worker_e2e_testing/ORIGINAL_REQUEST.md +0 -0
  47. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/worker_e2e_testing/progress.md +0 -0
  48. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/worker_m1_startup/BRIEFING.md +0 -0
  49. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/worker_m1_startup/ORIGINAL_REQUEST.md +0 -0
  50. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.agents/worker_m1_startup/progress.md +0 -0
  51. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.env.example +0 -0
  52. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.github/workflows/tests.yml +0 -0
  53. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/.gitignore +0 -0
  54. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/CODE_OF_CONDUCT.md +0 -0
  55. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/CONTRIBUTING.md +0 -0
  56. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/LICENSE +0 -0
  57. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/PROJECT.md +0 -0
  58. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/README.md +0 -0
  59. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/SECURITY.md +0 -0
  60. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/SUPPORT.md +0 -0
  61. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/TEST_INFRA.md +0 -0
  62. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/TEST_READY.md +0 -0
  63. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/__main__.py +0 -0
  64. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/changelog_generator.py +0 -0
  65. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/code_reviewer.py +0 -0
  66. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/commit_generator.py +0 -0
  67. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/conflict_resolver.py +0 -0
  68. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/gitignore_generator.py +0 -0
  69. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/intent_parser.py +0 -0
  70. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/llm_factory.py +0 -0
  71. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/pr_drafter.py +0 -0
  72. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/prompts/blame.py +0 -0
  73. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/prompts/changelog.py +0 -0
  74. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/prompts/commit.py +0 -0
  75. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/prompts/conflict.py +0 -0
  76. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/prompts/doctor.py +0 -0
  77. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/prompts/explain.py +0 -0
  78. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/prompts/ignore.py +0 -0
  79. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/prompts/intent.py +0 -0
  80. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/prompts/pr.py +0 -0
  81. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/prompts/rebase.py +0 -0
  82. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/prompts/review.py +0 -0
  83. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/prompts/search.py +0 -0
  84. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/prompts/standup.py +0 -0
  85. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/prompts/undo.py +0 -0
  86. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ai/rebase_helper.py +0 -0
  87. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/core/config.py +0 -0
  88. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/core/context.py +0 -0
  89. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/core/diagnostics.py +0 -0
  90. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/core/git_ops.py +0 -0
  91. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/core/hooks.py +0 -0
  92. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/core/safety.py +0 -0
  93. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ui/banner.py +0 -0
  94. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ui/dashboard.py +0 -0
  95. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ui/display.py +0 -0
  96. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ui/prompts.py +0 -0
  97. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/ui/themes.py +0 -0
  98. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/utils/conflict_parser.py +0 -0
  99. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/utils/diff_parser.py +0 -0
  100. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/utils/json_utils.py +0 -0
  101. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/ace/utils/string_utils.py +0 -0
  102. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/conftest.py +0 -0
  103. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/e2e/conftest.py +0 -0
  104. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/e2e/test_tier1_features.py +0 -0
  105. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/e2e/test_tier2_boundaries.py +0 -0
  106. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/e2e/test_tier3_combinations.py +0 -0
  107. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/e2e/test_tier4_workloads.py +0 -0
  108. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_blame.py +0 -0
  109. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_changelog_generator.py +0 -0
  110. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_code_reviewer.py +0 -0
  111. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_config_atomic_write.py +0 -0
  112. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_conflict_resolver.py +0 -0
  113. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_conflict_resolver_backup.py +0 -0
  114. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_context_gitdir_detection.py +0 -0
  115. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_diagnostics.py +0 -0
  116. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_diff_trimmer.py +0 -0
  117. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_git_ops.py +0 -0
  118. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_git_ops_execute.py +0 -0
  119. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_help.py +0 -0
  120. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_history_analyzer.py +0 -0
  121. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_hooks.py +0 -0
  122. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_ignore.py +0 -0
  123. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_intent_parser.py +0 -0
  124. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_llm_factory.py +0 -0
  125. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_llm_factory_ollama.py +0 -0
  126. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_merge_state.py +0 -0
  127. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_pr_drafter.py +0 -0
  128. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_rebase_helper.py +0 -0
  129. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_safety.py +0 -0
  130. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_search.py +0 -0
  131. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_standup.py +0 -0
  132. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_string_utils.py +0 -0
  133. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_undo.py +0 -0
  134. {ace_git_copilot-0.4.0 → ace_git_copilot-0.4.2}/tests/test_workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ace-git-copilot
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
4
  Summary: AI-powered Git copilot — talk to Git in plain English
5
5
  Project-URL: Homepage, https://github.com/jachinsamuel/Ace
6
6
  Project-URL: Documentation, https://github.com/jachinsamuel/Ace#readme
@@ -0,0 +1 @@
1
+ __version__ = "0.4.2"
@@ -196,7 +196,8 @@ class HistoryAnalyzer:
196
196
 
197
197
  commit_lines = []
198
198
  for c in commits:
199
- commit_lines.append(f"- {c['hexsha'][:7]} - {c['summary']} (by {c['author']})")
199
+ repo_prefix = f"[{c['repo_name']}] " if "repo_name" in c else ""
200
+ commit_lines.append(f"- {repo_prefix}{c['hexsha'][:7]} - {c['summary']} (by {c['author']})")
200
201
  commit_list_text = "\n".join(commit_lines)
201
202
 
202
203
  from ace.ai.prompts.standup import STANDUP_SYSTEM_PROMPT, USER_PROMPT_TEMPLATE
@@ -84,16 +84,38 @@ def main(
84
84
  from ace.core.safety import SafetyChecker
85
85
  from ace.ui.prompts import confirm
86
86
 
87
- if ctx.invoked_subcommand is not None:
87
+ # Intercept subcommand if the user typed an English sentence (e.g. 'ace add and commit')
88
+ intercepted_query = None
89
+ if ctx.invoked_subcommand in ("add", "stage", "commit", "review", "explain", "resolve", "search"):
90
+ import sys
91
+ sub_cmd = ctx.invoked_subcommand
92
+ sub_idx = -1
93
+ for idx, arg in enumerate(sys.argv):
94
+ if arg == sub_cmd and idx > 0 and not sys.argv[idx - 1].startswith("-"):
95
+ sub_idx = idx
96
+ break
97
+ if sub_idx != -1:
98
+ sub_args = sys.argv[sub_idx + 1:]
99
+ nl_indicators = {"and", "with", "to", "the", "a", "new", "my", "all", "but", "yesterday", "last", "from", "for", "changed", "everything"}
100
+ if any(arg.lower() in nl_indicators for arg in sub_args):
101
+ query_parts = []
102
+ for arg in sys.argv[1:]:
103
+ if arg.startswith("-"):
104
+ continue
105
+ query_parts.append(arg)
106
+ intercepted_query = " ".join(query_parts)
107
+
108
+ if intercepted_query:
109
+ query = intercepted_query
110
+ elif ctx.invoked_subcommand is not None:
88
111
  # A subcommand was invoked, let it execute
89
112
  return
90
-
91
- if not ctx.args:
92
- # No query and no subcommand, Typer will show help
93
- console.print(ctx.get_help())
94
- raise typer.Exit()
95
-
96
- query = " ".join(ctx.args)
113
+ else:
114
+ if not ctx.args:
115
+ # No query and no subcommand, Typer will show help
116
+ console.print(ctx.get_help())
117
+ raise typer.Exit()
118
+ query = " ".join(ctx.args)
97
119
 
98
120
  # Initialize GitOps
99
121
  try:
@@ -1898,36 +1920,66 @@ def standup_cmd(
1898
1920
  from ace.ai.history_analyzer import HistoryAnalyzer
1899
1921
  from rich.markdown import Markdown
1900
1922
  from rich.panel import Panel
1923
+ import pathlib
1901
1924
 
1925
+ current_dir = pathlib.Path(".")
1926
+ repos_found = []
1927
+
1928
+ # 1. Try active repository in current folder
1902
1929
  try:
1903
1930
  git_ops = GitOps()
1904
- except NotAGitRepositoryError as e:
1905
- show_error_panel(str(e), "Git Error")
1906
- raise typer.Exit(code=1)
1907
-
1908
- # 1. Resolve author filter
1909
- author_name = None
1910
- if not all_authors:
1931
+ repos_found.append((current_dir.resolve().name, git_ops))
1932
+ except NotAGitRepositoryError:
1933
+ # Fallback: scan immediate subdirectories for sibling repositories
1911
1934
  try:
1912
- author_name = git_ops.repo.git.config("user.name")
1935
+ for child in sorted(current_dir.iterdir()):
1936
+ if child.is_dir() and ((child / ".git").exists() or (child / "gitdir").exists() or child.name.endswith(".git")):
1937
+ try:
1938
+ repos_found.append((child.name, GitOps(str(child))))
1939
+ except Exception:
1940
+ pass
1913
1941
  except Exception:
1914
1942
  pass
1915
1943
 
1916
- # 2. Get recent commits
1944
+ if not repos_found:
1945
+ show_error_panel("Not a git repository (or any of the parent directories).", "Git Error")
1946
+ raise typer.Exit(code=1)
1947
+
1948
+ # 2. Gather commits from all resolved repositories
1949
+ all_commits = []
1917
1950
  since_arg = f"{days} days ago"
1918
- commits = git_ops.get_log(since=since_arg, author=author_name)
1919
1951
 
1920
- if not commits:
1952
+ # We need a primary git_ops instance to initialize HistoryAnalyzer
1953
+ primary_git_ops = repos_found[0][1]
1954
+
1955
+ for repo_name, git_ops in repos_found:
1956
+ author_name = None
1957
+ if not all_authors:
1958
+ try:
1959
+ author_name = git_ops.repo.git.config("user.name")
1960
+ except Exception:
1961
+ pass
1962
+
1963
+ try:
1964
+ repo_commits = git_ops.get_log(since=since_arg, author=author_name)
1965
+ for c in repo_commits:
1966
+ c["repo_name"] = repo_name
1967
+ all_commits.append(c)
1968
+ except Exception:
1969
+ pass
1970
+
1971
+ if not all_commits:
1921
1972
  author_msg = "your" if not all_authors else "any"
1922
- print_warning(f"No recent commits found from {author_msg} author in the last {days} day(s).")
1973
+ scope_msg = "this repository" if len(repos_found) == 1 else f"{len(repos_found)} repositories in this directory"
1974
+ print_warning(f"No recent commits found from {author_msg} author in {scope_msg} in the last {days} day(s).")
1923
1975
  raise typer.Exit(code=0)
1924
1976
 
1925
- analyzer = HistoryAnalyzer(git_ops)
1977
+ analyzer = HistoryAnalyzer(primary_git_ops)
1926
1978
 
1927
1979
  try:
1928
1980
  get_llm(offline_override=offline)
1929
- with spinner(f"Analyzing {len(commits)} commits and generating standup..."):
1930
- standup_report = analyzer.generate_standup(commits, offline=offline)
1981
+ with spinner(f"Analyzing {len(all_commits)} commits across {len(repos_found)} repo(s) and generating standup..."):
1982
+ standup_report = analyzer.generate_standup(all_commits, offline=offline)
1931
1983
  except Exception as e:
1932
1984
  show_error_panel(f"Failed to generate standup: {e}", "AI Error")
1933
1985
  raise typer.Exit(code=1)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ace-git-copilot"
3
- version = "0.4.0"
3
+ version = "0.4.2"
4
4
  description = "AI-powered Git copilot — talk to Git in plain English"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -0,0 +1,42 @@
1
+ from unittest.mock import MagicMock, patch
2
+ import sys
3
+ from typer.testing import CliRunner
4
+ from ace.cli import app
5
+
6
+ runner = CliRunner()
7
+
8
+ @patch("ace.core.git_ops.GitOps")
9
+ @patch("ace.ai.llm_factory.get_llm")
10
+ @patch("ace.ai.intent_parser.IntentParser")
11
+ def test_subcommand_interceptor_nl(mock_parser_class, mock_get_llm, mock_git_ops_class):
12
+ # Setup mocks
13
+ mock_git_ops = MagicMock()
14
+ mock_git_ops_class.return_value = mock_git_ops
15
+
16
+ mock_parser = MagicMock()
17
+ mock_parser.parse_intent.return_value = {"commands": [], "explanation": "NL query matched"}
18
+ mock_parser_class.return_value = mock_parser
19
+
20
+ # We mock sys.argv to simulate running: ace add and commit
21
+ with patch.object(sys, "argv", ["ace", "add", "and", "commit"]):
22
+ # Run command without subcommand routing triggering
23
+ result = runner.invoke(app, ["add", "and", "commit"])
24
+
25
+ assert result.exit_code == 0
26
+ # The IntentParser should have been called with the combined string
27
+ mock_parser.parse_intent.assert_called_once_with("add and commit", offline=False)
28
+
29
+ @patch("ace.core.git_ops.GitOps")
30
+ def test_subcommand_interceptor_normal_subcmd(mock_git_ops_class):
31
+ # Setup mock to fail staging of a non-existent file
32
+ mock_git_ops = MagicMock()
33
+ mock_git_ops.execute.side_effect = Exception("failed to stage")
34
+ mock_git_ops_class.return_value = mock_git_ops
35
+
36
+ # Simulate: ace add file1.txt (no NL indicators)
37
+ with patch.object(sys, "argv", ["ace", "add", "file1.txt"]):
38
+ result = runner.invoke(app, ["add", "file1.txt"])
39
+
40
+ # Normal execution path: it routes to the add subcommand, tries to run git add, and fails
41
+ assert result.exit_code == 1
42
+ mock_git_ops.execute.assert_called_once_with("add file1.txt")
@@ -1 +0,0 @@
1
- __version__ = "0.4.0"
File without changes