aru-code 0.55.0__tar.gz → 0.56.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. {aru_code-0.55.0/aru_code.egg-info → aru_code-0.56.0}/PKG-INFO +1 -1
  2. aru_code-0.56.0/aru/__init__.py +1 -0
  3. {aru_code-0.55.0 → aru_code-0.56.0}/aru/display.py +6 -3
  4. {aru_code-0.55.0 → aru_code-0.56.0/aru_code.egg-info}/PKG-INFO +1 -1
  5. {aru_code-0.55.0 → aru_code-0.56.0}/pyproject.toml +1 -1
  6. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_permission_flow.py +34 -0
  7. aru_code-0.55.0/aru/__init__.py +0 -1
  8. {aru_code-0.55.0 → aru_code-0.56.0}/LICENSE +0 -0
  9. {aru_code-0.55.0 → aru_code-0.56.0}/README.md +0 -0
  10. {aru_code-0.55.0 → aru_code-0.56.0}/aru/_debug/__init__.py +0 -0
  11. {aru_code-0.55.0 → aru_code-0.56.0}/aru/_debug/analyze_trace.py +0 -0
  12. {aru_code-0.55.0 → aru_code-0.56.0}/aru/_debug/loop_tracer.py +0 -0
  13. {aru_code-0.55.0 → aru_code-0.56.0}/aru/agent_factory.py +0 -0
  14. {aru_code-0.55.0 → aru_code-0.56.0}/aru/agents/__init__.py +0 -0
  15. {aru_code-0.55.0 → aru_code-0.56.0}/aru/agents/base.py +0 -0
  16. {aru_code-0.55.0 → aru_code-0.56.0}/aru/agents/catalog.py +0 -0
  17. {aru_code-0.55.0 → aru_code-0.56.0}/aru/agents/planner.py +0 -0
  18. {aru_code-0.55.0 → aru_code-0.56.0}/aru/cache_patch.py +0 -0
  19. {aru_code-0.55.0 → aru_code-0.56.0}/aru/checkpoints.py +0 -0
  20. {aru_code-0.55.0 → aru_code-0.56.0}/aru/cli.py +0 -0
  21. {aru_code-0.55.0 → aru_code-0.56.0}/aru/commands.py +0 -0
  22. {aru_code-0.55.0 → aru_code-0.56.0}/aru/completers.py +0 -0
  23. {aru_code-0.55.0 → aru_code-0.56.0}/aru/config.py +0 -0
  24. {aru_code-0.55.0 → aru_code-0.56.0}/aru/context.py +0 -0
  25. {aru_code-0.55.0 → aru_code-0.56.0}/aru/doom_loop.py +0 -0
  26. {aru_code-0.55.0 → aru_code-0.56.0}/aru/events.py +0 -0
  27. {aru_code-0.55.0 → aru_code-0.56.0}/aru/format/__init__.py +0 -0
  28. {aru_code-0.55.0 → aru_code-0.56.0}/aru/format/manager.py +0 -0
  29. {aru_code-0.55.0 → aru_code-0.56.0}/aru/format/runner.py +0 -0
  30. {aru_code-0.55.0 → aru_code-0.56.0}/aru/history_blocks.py +0 -0
  31. {aru_code-0.55.0 → aru_code-0.56.0}/aru/lsp/__init__.py +0 -0
  32. {aru_code-0.55.0 → aru_code-0.56.0}/aru/lsp/client.py +0 -0
  33. {aru_code-0.55.0 → aru_code-0.56.0}/aru/lsp/manager.py +0 -0
  34. {aru_code-0.55.0 → aru_code-0.56.0}/aru/lsp/protocol.py +0 -0
  35. {aru_code-0.55.0 → aru_code-0.56.0}/aru/memory/__init__.py +0 -0
  36. {aru_code-0.55.0 → aru_code-0.56.0}/aru/memory/extractor.py +0 -0
  37. {aru_code-0.55.0 → aru_code-0.56.0}/aru/memory/loader.py +0 -0
  38. {aru_code-0.55.0 → aru_code-0.56.0}/aru/memory/store.py +0 -0
  39. {aru_code-0.55.0 → aru_code-0.56.0}/aru/permissions.py +0 -0
  40. {aru_code-0.55.0 → aru_code-0.56.0}/aru/plugin_cache.py +0 -0
  41. {aru_code-0.55.0 → aru_code-0.56.0}/aru/plugins/__init__.py +0 -0
  42. {aru_code-0.55.0 → aru_code-0.56.0}/aru/plugins/custom_tools.py +0 -0
  43. {aru_code-0.55.0 → aru_code-0.56.0}/aru/plugins/hooks.py +0 -0
  44. {aru_code-0.55.0 → aru_code-0.56.0}/aru/plugins/manager.py +0 -0
  45. {aru_code-0.55.0 → aru_code-0.56.0}/aru/plugins/tool_api.py +0 -0
  46. {aru_code-0.55.0 → aru_code-0.56.0}/aru/providers.py +0 -0
  47. {aru_code-0.55.0 → aru_code-0.56.0}/aru/runner.py +0 -0
  48. {aru_code-0.55.0 → aru_code-0.56.0}/aru/runtime.py +0 -0
  49. {aru_code-0.55.0 → aru_code-0.56.0}/aru/select.py +0 -0
  50. {aru_code-0.55.0 → aru_code-0.56.0}/aru/session.py +0 -0
  51. {aru_code-0.55.0 → aru_code-0.56.0}/aru/sinks.py +0 -0
  52. {aru_code-0.55.0 → aru_code-0.56.0}/aru/streaming.py +0 -0
  53. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tool_policy.py +0 -0
  54. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/__init__.py +0 -0
  55. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/_diff.py +0 -0
  56. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/_shared.py +0 -0
  57. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/apply_patch.py +0 -0
  58. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/apply_patch_prompt.txt +0 -0
  59. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/ast_tools.py +0 -0
  60. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/codebase.py +0 -0
  61. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/delegate.py +0 -0
  62. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/delegate_prompt.txt +0 -0
  63. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/file_ops.py +0 -0
  64. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/gitignore.py +0 -0
  65. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/lsp.py +0 -0
  66. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/mcp_client.py +0 -0
  67. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/memory_tool.py +0 -0
  68. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/plan_mode.py +0 -0
  69. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/ranker.py +0 -0
  70. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/registry.py +0 -0
  71. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/search.py +0 -0
  72. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/shell.py +0 -0
  73. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/skill.py +0 -0
  74. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/tasklist.py +0 -0
  75. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/web.py +0 -0
  76. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tools/worktree.py +0 -0
  77. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/__init__.py +0 -0
  78. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/app.py +0 -0
  79. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/log_bridge.py +0 -0
  80. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/notifications.py +0 -0
  81. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/sanitize.py +0 -0
  82. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/screens/__init__.py +0 -0
  83. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/screens/choice.py +0 -0
  84. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/screens/confirm.py +0 -0
  85. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/screens/keymap.py +0 -0
  86. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/screens/search.py +0 -0
  87. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/screens/session_picker.py +0 -0
  88. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/screens/text_input.py +0 -0
  89. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/sinks.py +0 -0
  90. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/slash_bridge.py +0 -0
  91. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/themes.py +0 -0
  92. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/ui.py +0 -0
  93. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/widgets/__init__.py +0 -0
  94. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/widgets/chat.py +0 -0
  95. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/widgets/completer.py +0 -0
  96. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/widgets/context_pane.py +0 -0
  97. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/widgets/file_link.py +0 -0
  98. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/widgets/header.py +0 -0
  99. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/widgets/inline_choice.py +0 -0
  100. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/widgets/loaded_pane.py +0 -0
  101. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/widgets/prompt_area.py +0 -0
  102. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/widgets/prompt_queue.py +0 -0
  103. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/widgets/status.py +0 -0
  104. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/widgets/subagent_panel.py +0 -0
  105. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/widgets/tasklist_panel.py +0 -0
  106. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/widgets/thinking.py +0 -0
  107. {aru_code-0.55.0 → aru_code-0.56.0}/aru/tui/widgets/tools.py +0 -0
  108. {aru_code-0.55.0 → aru_code-0.56.0}/aru/ui.py +0 -0
  109. {aru_code-0.55.0 → aru_code-0.56.0}/aru_code.egg-info/SOURCES.txt +0 -0
  110. {aru_code-0.55.0 → aru_code-0.56.0}/aru_code.egg-info/dependency_links.txt +0 -0
  111. {aru_code-0.55.0 → aru_code-0.56.0}/aru_code.egg-info/entry_points.txt +0 -0
  112. {aru_code-0.55.0 → aru_code-0.56.0}/aru_code.egg-info/requires.txt +0 -0
  113. {aru_code-0.55.0 → aru_code-0.56.0}/aru_code.egg-info/top_level.txt +0 -0
  114. {aru_code-0.55.0 → aru_code-0.56.0}/setup.cfg +0 -0
  115. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_agents_base.py +0 -0
  116. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_agents_md_coverage.py +0 -0
  117. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_apply_patch.py +0 -0
  118. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_async_tool_permission.py +0 -0
  119. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_cache_patch_metrics.py +0 -0
  120. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_cache_patch_stop_reason.py +0 -0
  121. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_catalog.py +0 -0
  122. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_chat_scrollable.py +0 -0
  123. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_checkpoints.py +0 -0
  124. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_cli.py +0 -0
  125. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_cli_advanced.py +0 -0
  126. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_cli_base.py +0 -0
  127. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_cli_completers.py +0 -0
  128. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_cli_new.py +0 -0
  129. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_cli_run_cli.py +0 -0
  130. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_cli_session.py +0 -0
  131. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_cli_shell.py +0 -0
  132. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_codebase.py +0 -0
  133. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_confabulation_regression.py +0 -0
  134. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_config.py +0 -0
  135. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_context.py +0 -0
  136. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_context_pane.py +0 -0
  137. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_cwd_awareness.py +0 -0
  138. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_delegate.py +0 -0
  139. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_doom_loop.py +0 -0
  140. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_events_backward_compat.py +0 -0
  141. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_events_schema.py +0 -0
  142. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_fork_ctx_concurrency.py +0 -0
  143. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_format.py +0 -0
  144. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_gitignore.py +0 -0
  145. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_guardrails_scenarios.py +0 -0
  146. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_invoke_skill.py +0 -0
  147. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_invoked_skills.py +0 -0
  148. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_loaded_pane_path.py +0 -0
  149. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_lsp.py +0 -0
  150. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_lsp_rename.py +0 -0
  151. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_main.py +0 -0
  152. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_markdown_to_text.py +0 -0
  153. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_mcp_client.py +0 -0
  154. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_mcp_health.py +0 -0
  155. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_memory.py +0 -0
  156. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_memory_tool.py +0 -0
  157. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_microcompact.py +0 -0
  158. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_permission_timeout_suspension.py +0 -0
  159. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_permissions.py +0 -0
  160. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_plan_mode_refactor.py +0 -0
  161. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_plugin_cache.py +0 -0
  162. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_plugin_errors.py +0 -0
  163. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_plugin_hooks_v2.py +0 -0
  164. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_plugins.py +0 -0
  165. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_providers.py +0 -0
  166. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_ranker.py +0 -0
  167. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_reasoning.py +0 -0
  168. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_runner_interrupt.py +0 -0
  169. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_runner_recovery.py +0 -0
  170. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_runtime.py +0 -0
  171. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_select.py +0 -0
  172. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_session_free_cost.py +0 -0
  173. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_skill_disallowed_tools.py +0 -0
  174. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_status_breakdown.py +0 -0
  175. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_status_cost.py +0 -0
  176. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_streaming_sink.py +0 -0
  177. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_subagent_tool_events.py +0 -0
  178. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tasklist.py +0 -0
  179. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_thread_tool_timeout.py +0 -0
  180. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tool_policy.py +0 -0
  181. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_truncation_marker.py +0 -0
  182. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_app_boot.py +0 -0
  183. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_bindings.py +0 -0
  184. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_bus_flow.py +0 -0
  185. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_chat.py +0 -0
  186. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_chat_adversarial.py +0 -0
  187. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_completer.py +0 -0
  188. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_completer_dynamic.py +0 -0
  189. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_copy.py +0 -0
  190. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_error_display.py +0 -0
  191. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_file_link.py +0 -0
  192. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_input_behaviour.py +0 -0
  193. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_layer12_recovery.py +0 -0
  194. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_layer13_recovery.py +0 -0
  195. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_mention_expand.py +0 -0
  196. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_modals.py +0 -0
  197. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_mode_cycle.py +0 -0
  198. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_native_selection.py +0 -0
  199. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_plan_task_render.py +0 -0
  200. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_prompt_queue.py +0 -0
  201. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_shell_bang.py +0 -0
  202. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_sidebar_toggle.py +0 -0
  203. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_slash_bridge.py +0 -0
  204. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_slash_model.py +0 -0
  205. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_snapshot_smoke.py +0 -0
  206. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_subagent_panel.py +0 -0
  207. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_theme.py +0 -0
  208. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_thinking_and_boot.py +0 -0
  209. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_tui_widgets_visual.py +0 -0
  210. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_ui_adapter.py +0 -0
  211. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_worktree.py +0 -0
  212. {aru_code-0.55.0 → aru_code-0.56.0}/tests/test_worktree_session_restore.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aru-code
3
- Version: 0.55.0
3
+ Version: 0.56.0
4
4
  Summary: A Claude Code clone built with Agno agents
5
5
  Author-email: Estevao <estevaofon@gmail.com>
6
6
  License-Expression: MIT
@@ -0,0 +1 @@
1
+ __version__ = "0.56.0"
@@ -301,13 +301,16 @@ class ToolTracker:
301
301
  self._completed: list[tuple[str, float]] = [] # (label, duration)
302
302
 
303
303
  def start(self, tool_id: str, label: str):
304
- self._active[tool_id] = (label, time.monotonic())
304
+ # perf_counter, not monotonic: on Windows monotonic() has ~15.6ms
305
+ # resolution, so a sub-tick tool call would report a 0.0 duration.
306
+ # perf_counter is monotonic too but high-resolution (~100ns).
307
+ self._active[tool_id] = (label, time.perf_counter())
305
308
 
306
309
  def complete(self, tool_id: str) -> tuple[str, float] | None:
307
310
  entry = self._active.pop(tool_id, None)
308
311
  if entry:
309
312
  label, start = entry
310
- duration = time.monotonic() - start
313
+ duration = time.perf_counter() - start
311
314
  self._completed.append((label, duration))
312
315
  return label, duration
313
316
  return None
@@ -315,7 +318,7 @@ class ToolTracker:
315
318
  @property
316
319
  def active_labels(self) -> list[tuple[str, float]]:
317
320
  """Return (label, elapsed_seconds) for each active tool."""
318
- now = time.monotonic()
321
+ now = time.perf_counter()
319
322
  return [(label, now - start) for label, start in self._active.values()]
320
323
 
321
324
  def pop_completed(self) -> list[tuple[str, float]]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aru-code
3
- Version: 0.55.0
3
+ Version: 0.56.0
4
4
  Summary: A Claude Code clone built with Agno agents
5
5
  Author-email: Estevao <estevaofon@gmail.com>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "aru-code"
7
- version = "0.55.0"
7
+ version = "0.56.0"
8
8
  description = "A Claude Code clone built with Agno agents"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -9,6 +9,33 @@ import pytest
9
9
  pytest.importorskip("textual")
10
10
 
11
11
 
12
+ async def _wait_for_inline_focus(app, pilot, *, max_iter: int = 50):
13
+ """Block until the InlineChoicePrompt's OptionList owns focus.
14
+
15
+ ``query(InlineChoicePrompt)`` returns the widget as soon as it is in
16
+ the DOM, but the OptionList only gains focus (and its default
17
+ highlight) inside ``InlineChoicePrompt.on_mount`` — a message
18
+ dispatched *after* mount completes. Pressing Enter / arrow keys
19
+ before that lands sends the key nowhere useful: ``OptionSelected``
20
+ never fires and the worker thread blocks until its timeout. In
21
+ isolation the gap is sub-tick so the press always lands; under a
22
+ loaded suite the mount lifecycle slips behind detection and the test
23
+ flakes with a TimeoutError. Waiting for focus closes the race.
24
+ """
25
+ from textual.widgets import OptionList
26
+
27
+ from aru.tui.widgets.inline_choice import InlineChoicePrompt
28
+
29
+ for _ in range(max_iter):
30
+ prompts = list(app.query(InlineChoicePrompt))
31
+ if prompts:
32
+ opts = list(prompts[0].query(OptionList))
33
+ if opts and app.focused is opts[0] and opts[0].highlighted is not None:
34
+ return opts[0]
35
+ await pilot.pause(0.05)
36
+ raise AssertionError("InlineChoicePrompt OptionList never took focus")
37
+
38
+
12
39
  @pytest.mark.asyncio
13
40
  async def test_tui_ask_choice_from_worker_resolves_via_modal():
14
41
  """TuiUI.ask_choice invoked from a worker thread returns modal result.
@@ -106,6 +133,9 @@ async def test_ask_choice_with_details_uses_inline_prompt_not_modal():
106
133
  "expected InlineChoicePrompt in ChatPane"
107
134
  )
108
135
  # Press Enter — OptionList focuses on mount, default=0 highlighted.
136
+ # Wait for that focus to actually land first; querying the prompt
137
+ # only proves it is in the DOM (see _wait_for_inline_focus).
138
+ await _wait_for_inline_focus(app, pilot)
109
139
  await pilot.press("enter")
110
140
  await asyncio.wait_for(task, timeout=5.0)
111
141
  assert holder["choice"] == 0
@@ -156,6 +186,7 @@ async def test_inline_prompt_hides_input_bar_and_restores_on_answer():
156
186
  assert inp.has_class("-hidden"), (
157
187
  "input should be hidden while InlineChoicePrompt is mounted"
158
188
  )
189
+ await _wait_for_inline_focus(app, pilot)
159
190
  await pilot.press("enter")
160
191
  await asyncio.wait_for(task, timeout=5.0)
161
192
  # After the user answers, the input bar is restored.
@@ -199,6 +230,7 @@ async def test_ask_choice_inline_esc_cancels_with_cancel_value():
199
230
  await pilot.pause(0.05)
200
231
  if list(app.query_one(ChatPane).query(InlineChoicePrompt)):
201
232
  break
233
+ await _wait_for_inline_focus(app, pilot)
202
234
  await pilot.press("escape")
203
235
  await asyncio.wait_for(task, timeout=5.0)
204
236
  assert holder["choice"] == 99
@@ -257,6 +289,7 @@ async def test_auto_accept_inline_choice_updates_status_pane_mode():
257
289
  if list(chat.query(InlineChoicePrompt)):
258
290
  break
259
291
  # Option index 1 = "Yes, and auto-accept edits".
292
+ await _wait_for_inline_focus(app, pilot)
260
293
  await pilot.press("down")
261
294
  await pilot.press("enter")
262
295
  await asyncio.wait_for(task, timeout=5.0)
@@ -320,6 +353,7 @@ async def test_thinking_spinner_hidden_while_prompt_open():
320
353
  "spinner must be hidden while the permission prompt is open"
321
354
  )
322
355
 
356
+ await _wait_for_inline_focus(app, pilot)
323
357
  await pilot.press("enter")
324
358
  await asyncio.wait_for(task, timeout=5.0)
325
359
  for _ in range(20):
@@ -1 +0,0 @@
1
- __version__ = "0.55.0"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes