deepagents-cli 0.0.54__tar.gz → 0.0.55__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 (297) hide show
  1. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/CHANGELOG.md +6 -0
  2. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/PKG-INFO +1 -1
  3. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/_version.py +1 -1
  4. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/textual_adapter.py +15 -2
  5. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/messages.py +154 -20
  6. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/pyproject.toml +1 -1
  7. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_messages.py +116 -0
  8. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_textual_adapter.py +53 -0
  9. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/uv.lock +1 -1
  10. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/.gitignore +0 -0
  11. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/DEV.md +0 -0
  12. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/Makefile +0 -0
  13. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/README.md +0 -0
  14. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/THREAT_MODEL.md +0 -0
  15. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/__init__.py +0 -0
  16. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/__main__.py +0 -0
  17. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/_ask_user_types.py +0 -0
  18. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/_cli_context.py +0 -0
  19. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/_constants.py +0 -0
  20. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/_debug.py +0 -0
  21. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/_env_vars.py +0 -0
  22. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/_git.py +0 -0
  23. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/_server_config.py +0 -0
  24. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/_session_stats.py +0 -0
  25. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/_testing_models.py +0 -0
  26. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/_textual_patches.py +0 -0
  27. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/agent.py +0 -0
  28. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/app.py +0 -0
  29. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/app.tcss +0 -0
  30. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/ask_user.py +0 -0
  31. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/auth_store.py +0 -0
  32. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/built_in_skills/__init__.py +0 -0
  33. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/built_in_skills/remember/SKILL.md +0 -0
  34. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/built_in_skills/skill-creator/SKILL.md +0 -0
  35. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/built_in_skills/skill-creator/scripts/init_skill.py +0 -0
  36. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/built_in_skills/skill-creator/scripts/quick_validate.py +0 -0
  37. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/clipboard.py +0 -0
  38. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/command_registry.py +0 -0
  39. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/config.py +0 -0
  40. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/configurable_model.py +0 -0
  41. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/default_agent_prompt.md +0 -0
  42. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/deploy/__init__.py +0 -0
  43. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/deploy/bundler.py +0 -0
  44. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/deploy/commands.py +0 -0
  45. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/deploy/config.py +0 -0
  46. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/deploy/context_hub.py +0 -0
  47. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/deploy/frontend_dist/assets/anonymous-B9UzAXQd.js +0 -0
  48. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/deploy/frontend_dist/assets/clerk-5xHgyQyG.js +0 -0
  49. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/deploy/frontend_dist/assets/highlighted-body-OFNGDK62-rX-7qT8o.js +0 -0
  50. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/deploy/frontend_dist/assets/index-DM3gptpu.js +0 -0
  51. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/deploy/frontend_dist/assets/index-Ddy7F6KI.css +0 -0
  52. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/deploy/frontend_dist/assets/supabase-S6NACDgm.js +0 -0
  53. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/deploy/frontend_dist/index.html +0 -0
  54. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/deploy/frontend_dist/logo-dark.svg +0 -0
  55. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/deploy/frontend_dist/logo-light.svg +0 -0
  56. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/deploy/templates.py +0 -0
  57. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/editor.py +0 -0
  58. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/event_bus.py +0 -0
  59. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/extras_info.py +0 -0
  60. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/file_ops.py +0 -0
  61. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/formatting.py +0 -0
  62. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/hooks.py +0 -0
  63. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/input.py +0 -0
  64. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/integrations/__init__.py +0 -0
  65. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/integrations/sandbox_factory.py +0 -0
  66. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/integrations/sandbox_provider.py +0 -0
  67. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/iterm_cursor_guide.py +0 -0
  68. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/local_context.py +0 -0
  69. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/main.py +0 -0
  70. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/mcp_auth.py +0 -0
  71. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/mcp_commands.py +0 -0
  72. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/mcp_providers/__init__.py +0 -0
  73. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/mcp_providers/_registry.py +0 -0
  74. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/mcp_providers/base.py +0 -0
  75. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/mcp_providers/github.py +0 -0
  76. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/mcp_providers/slack.py +0 -0
  77. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/mcp_tools.py +0 -0
  78. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/mcp_trust.py +0 -0
  79. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/media_utils.py +0 -0
  80. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/model_config.py +0 -0
  81. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/non_interactive.py +0 -0
  82. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/notifications.py +0 -0
  83. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/offload.py +0 -0
  84. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/onboarding.py +0 -0
  85. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/output.py +0 -0
  86. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/project_utils.py +0 -0
  87. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/py.typed +0 -0
  88. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/remote_client.py +0 -0
  89. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/server.py +0 -0
  90. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/server_graph.py +0 -0
  91. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/server_manager.py +0 -0
  92. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/sessions.py +0 -0
  93. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/skills/__init__.py +0 -0
  94. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/skills/commands.py +0 -0
  95. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/skills/invocation.py +0 -0
  96. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/skills/load.py +0 -0
  97. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/state_migration.py +0 -0
  98. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/subagents.py +0 -0
  99. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/system_prompt.md +0 -0
  100. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/terminal_capabilities.py +0 -0
  101. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/theme.py +0 -0
  102. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/token_state.py +0 -0
  103. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/tool_display.py +0 -0
  104. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/tools.py +0 -0
  105. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/ui.py +0 -0
  106. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/unicode_security.py +0 -0
  107. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/update_check.py +0 -0
  108. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/__init__.py +0 -0
  109. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/_links.py +0 -0
  110. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/agent_selector.py +0 -0
  111. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/approval.py +0 -0
  112. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/ask_user.py +0 -0
  113. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/auth.py +0 -0
  114. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/autocomplete.py +0 -0
  115. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/chat_input.py +0 -0
  116. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/diff.py +0 -0
  117. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/history.py +0 -0
  118. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/launch_init.py +0 -0
  119. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/loading.py +0 -0
  120. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/mcp_viewer.py +0 -0
  121. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/message_store.py +0 -0
  122. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/model_selector.py +0 -0
  123. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/notification_center.py +0 -0
  124. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/notification_detail.py +0 -0
  125. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/notification_settings.py +0 -0
  126. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/status.py +0 -0
  127. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/theme_selector.py +0 -0
  128. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/thread_selector.py +0 -0
  129. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/tool_renderers.py +0 -0
  130. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/tool_widgets.py +0 -0
  131. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/update_available.py +0 -0
  132. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/update_progress.py +0 -0
  133. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/deepagents_cli/widgets/welcome.py +0 -0
  134. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/examples/deploy-content-writer/.env.example +0 -0
  135. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/examples/deploy-content-writer/skills/blog-post/SKILL.md +0 -0
  136. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/examples/deploy-content-writer/skills/social-media/SKILL.md +0 -0
  137. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/examples/deploy-content-writer/user/context.md +0 -0
  138. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/examples/deploy-content-writer/user/preferences.md +0 -0
  139. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/examples/skills/arxiv-search/SKILL.md +0 -0
  140. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/examples/skills/arxiv-search/arxiv_search.py +0 -0
  141. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/examples/skills/langgraph-docs/SKILL.md +0 -0
  142. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/examples/skills/skill-creator/SKILL.md +0 -0
  143. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/examples/skills/skill-creator/scripts/init_skill.py +0 -0
  144. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/examples/skills/skill-creator/scripts/quick_validate.py +0 -0
  145. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/examples/skills/web-research/SKILL.md +0 -0
  146. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/.nvmrc +0 -0
  147. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/index.html +0 -0
  148. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/package-lock.json +0 -0
  149. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/package.json +0 -0
  150. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/postcss.config.js +0 -0
  151. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/public/logo-dark.svg +0 -0
  152. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/public/logo-light.svg +0 -0
  153. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/App.tsx +0 -0
  154. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/ThemeProvider.tsx +0 -0
  155. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/auth/anonymous.tsx +0 -0
  156. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/auth/clerk.tsx +0 -0
  157. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/auth/loader.tsx +0 -0
  158. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/auth/supabase.tsx +0 -0
  159. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/auth/types.ts +0 -0
  160. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/components/AppHeader.tsx +0 -0
  161. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/components/FilePanels.tsx +0 -0
  162. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/components/MessageList.tsx +0 -0
  163. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/components/SubagentActivity.tsx +0 -0
  164. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/components/ThreadPicker.tsx +0 -0
  165. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/components/TodosPanel.tsx +0 -0
  166. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/components/ToolCallCard.tsx +0 -0
  167. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/components/toolcalls/FileToolCard.tsx +0 -0
  168. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/components/toolcalls/SearchCard.tsx +0 -0
  169. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/components/toolcalls/ThinkCard.tsx +0 -0
  170. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/components/toolcalls/TodosCard.tsx +0 -0
  171. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/components/toolcalls/index.ts +0 -0
  172. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/constants.ts +0 -0
  173. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/index.css +0 -0
  174. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/main.tsx +0 -0
  175. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/runtimeConfig.ts +0 -0
  176. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/types.ts +0 -0
  177. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/src/vite-env.d.ts +0 -0
  178. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/tsconfig.json +0 -0
  179. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/frontend/vite.config.ts +0 -0
  180. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/images/cli.png +0 -0
  181. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/scripts/check_imports.py +0 -0
  182. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/scripts/debug_server.sh +0 -0
  183. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/scripts/install.sh +0 -0
  184. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/README.md +0 -0
  185. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/integration_tests/__init__.py +0 -0
  186. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/integration_tests/benchmarks/__init__.py +0 -0
  187. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/integration_tests/benchmarks/test_codspeed_import_benchmarks.py +0 -0
  188. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/integration_tests/benchmarks/test_startup_benchmarks.py +0 -0
  189. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/integration_tests/conftest.py +0 -0
  190. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/integration_tests/test_acp_mode.py +0 -0
  191. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/integration_tests/test_compact_resume.py +0 -0
  192. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/integration_tests/test_context_hub.py +0 -0
  193. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/integration_tests/test_deploy_hub.py +0 -0
  194. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/integration_tests/test_sandbox_factory.py +0 -0
  195. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/integration_tests/test_sandbox_operations.py +0 -0
  196. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/__init__.py +0 -0
  197. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/conftest.py +0 -0
  198. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/deploy/__init__.py +0 -0
  199. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/deploy/test_bundler.py +0 -0
  200. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/deploy/test_commands.py +0 -0
  201. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/deploy/test_config.py +0 -0
  202. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/deploy/test_context_hub.py +0 -0
  203. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/deploy/test_frontend_bundle.py +0 -0
  204. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/deploy/test_frontend_config.py +0 -0
  205. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/skills/__init__.py +0 -0
  206. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/skills/test_commands.py +0 -0
  207. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/skills/test_load.py +0 -0
  208. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/skills/test_skills_json.py +0 -0
  209. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_agent.py +0 -0
  210. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_agent_friendly.py +0 -0
  211. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_agent_selector.py +0 -0
  212. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_app.py +0 -0
  213. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_approval.py +0 -0
  214. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_args.py +0 -0
  215. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_ask_user.py +0 -0
  216. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_ask_user_middleware.py +0 -0
  217. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_auth_store.py +0 -0
  218. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_auth_widgets.py +0 -0
  219. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_autocomplete.py +0 -0
  220. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_charset.py +0 -0
  221. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_chat_input.py +0 -0
  222. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_clipboard.py +0 -0
  223. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_command_registry.py +0 -0
  224. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_compact_tool.py +0 -0
  225. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_config.py +0 -0
  226. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_configurable_model.py +0 -0
  227. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_debug.py +0 -0
  228. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_editor.py +0 -0
  229. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_end_to_end.py +0 -0
  230. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_env_vars.py +0 -0
  231. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_event_bus.py +0 -0
  232. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_exception_handling.py +0 -0
  233. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_extras_info.py +0 -0
  234. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_file_ops.py +0 -0
  235. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_formatting.py +0 -0
  236. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_git.py +0 -0
  237. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_history.py +0 -0
  238. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_hooks.py +0 -0
  239. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_imports.py +0 -0
  240. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_input_parsing.py +0 -0
  241. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_iterm_cursor_guide.py +0 -0
  242. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_launch_init.py +0 -0
  243. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_links.py +0 -0
  244. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_loading.py +0 -0
  245. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_local_context.py +0 -0
  246. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_main.py +0 -0
  247. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_main_acp_mode.py +0 -0
  248. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_main_args.py +0 -0
  249. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_mcp_auth.py +0 -0
  250. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_mcp_commands.py +0 -0
  251. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_mcp_tools.py +0 -0
  252. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_mcp_trust.py +0 -0
  253. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_mcp_viewer.py +0 -0
  254. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_media_utils.py +0 -0
  255. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_message_store.py +0 -0
  256. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_model_config.py +0 -0
  257. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_model_selector.py +0 -0
  258. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_model_switch.py +0 -0
  259. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_non_interactive.py +0 -0
  260. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_notification_center.py +0 -0
  261. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_notification_detail.py +0 -0
  262. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_notifications.py +0 -0
  263. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_offload.py +0 -0
  264. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_offload_dict_messages.py +0 -0
  265. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_onboarding.py +0 -0
  266. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_output.py +0 -0
  267. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_reload.py +0 -0
  268. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_remote_client.py +0 -0
  269. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_sandbox_factory.py +0 -0
  270. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_server.py +0 -0
  271. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_server_config.py +0 -0
  272. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_server_graph.py +0 -0
  273. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_server_helpers.py +0 -0
  274. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_server_manager.py +0 -0
  275. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_session_stats.py +0 -0
  276. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_sessions.py +0 -0
  277. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_shell_allow_list.py +0 -0
  278. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_skill_invocation.py +0 -0
  279. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_startup_fast_paths.py +0 -0
  280. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_state_migration.py +0 -0
  281. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_status.py +0 -0
  282. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_subagents.py +0 -0
  283. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_terminal_capabilities.py +0 -0
  284. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_textual_patches.py +0 -0
  285. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_theme.py +0 -0
  286. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_thread_selector.py +0 -0
  287. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_token_tracker.py +0 -0
  288. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_tool_display.py +0 -0
  289. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_ui.py +0 -0
  290. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_unicode_security.py +0 -0
  291. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_update_available.py +0 -0
  292. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_update_check.py +0 -0
  293. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_update_progress.py +0 -0
  294. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_version.py +0 -0
  295. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/test_welcome.py +0 -0
  296. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/tools/__init__.py +0 -0
  297. {deepagents_cli-0.0.54 → deepagents_cli-0.0.55}/tests/unit_tests/tools/test_fetch_url.py +0 -0
@@ -1,5 +1,11 @@
1
1
  # Deep Agents CLI Changelog
2
2
 
3
+ ## [0.0.55](https://github.com/langchain-ai/deepagents/compare/deepagents-cli==0.0.54...deepagents-cli==0.0.55) (2026-05-11)
4
+
5
+ ### Bug Fixes
6
+
7
+ * Keep Thinking spinner during `edit_file` approval ([#3310](https://github.com/langchain-ai/deepagents/issues/3310)) ([d06133f](https://github.com/langchain-ai/deepagents/commit/d06133f8dd7cb924c82e46ece3e165d561ef7d9e))
8
+
3
9
  ## [0.0.54](https://github.com/langchain-ai/deepagents/compare/deepagents-cli==0.0.53...deepagents-cli==0.0.54) (2026-05-11)
4
10
 
5
11
  ### Features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepagents-cli
3
- Version: 0.0.54
3
+ Version: 0.0.55
4
4
  Summary: Terminal interface for Deep Agents - interactive AI agent with file operations, shell access, and sub-agent capabilities.
5
5
  Project-URL: Homepage, https://docs.langchain.com/oss/python/deepagents/overview
6
6
  Project-URL: Documentation, https://reference.langchain.com/python/deepagents/
@@ -1,6 +1,6 @@
1
1
  """Version information and lightweight constants for `deepagents-cli`."""
2
2
 
3
- __version__ = "0.0.54" # x-release-please-version
3
+ __version__ = "0.0.55" # x-release-please-version
4
4
 
5
5
  DOCS_URL = "https://docs.langchain.com/oss/python/deepagents/cli"
6
6
  """URL for `deepagents-cli` documentation."""
@@ -77,6 +77,9 @@ _hitl_adapter_cache: TypeAdapter | None = None
77
77
 
78
78
  _ASK_USER_UNSUPPORTED_ERROR = "ask_user not supported by this UI"
79
79
 
80
+ _TOOL_CALLS_KEEP_THINKING_SPINNER = frozenset({"edit_file"})
81
+ """Tool calls whose argument/approval phase can be long enough to need feedback."""
82
+
80
83
 
81
84
  def _get_hitl_request_adapter(hitl_request_type: type) -> TypeAdapter:
82
85
  """Return a cached `TypeAdapter(HITLRequest)`.
@@ -929,8 +932,16 @@ async def execute_task_textual(
929
932
  buffer_name, parsed_args, buffer_id
930
933
  )
931
934
 
932
- # Hide spinner before showing tool call
933
- if adapter._set_spinner:
935
+ keep_thinking_spinner = (
936
+ buffer_name in _TOOL_CALLS_KEEP_THINKING_SPINNER
937
+ )
938
+
939
+ # Hide spinner before showing most tool calls.
940
+ # `edit_file` can spend noticeable time between
941
+ # argument streaming, HITL interrupt delivery, and
942
+ # approval handling, so re-anchor Thinking below
943
+ # the row instead of leaving the UI visually idle.
944
+ if adapter._set_spinner and not keep_thinking_spinner:
934
945
  await adapter._set_spinner(None)
935
946
 
936
947
  # Mount tool call message
@@ -942,6 +953,8 @@ async def execute_task_textual(
942
953
  tool_msg = ToolCallMessage(buffer_name, parsed_args)
943
954
  await adapter._mount_message(tool_msg)
944
955
  adapter._current_tool_messages[buffer_id] = tool_msg
956
+ if adapter._set_spinner and keep_thinking_spinner:
957
+ await adapter._set_spinner("Thinking")
945
958
 
946
959
  tool_call_buffers.pop(buffer_key, None)
947
960
 
@@ -6,6 +6,7 @@ import ast
6
6
  import json
7
7
  import logging
8
8
  import re
9
+ import textwrap
9
10
  from dataclasses import dataclass
10
11
  from pathlib import Path
11
12
  from time import time
@@ -15,6 +16,7 @@ from textual import on
15
16
  from textual.containers import Vertical
16
17
  from textual.content import Content
17
18
  from textual.events import Click
19
+ from textual.message_pump import NoActiveAppError
18
20
  from textual.reactive import var
19
21
  from textual.widgets import Static
20
22
 
@@ -123,6 +125,8 @@ _MAX_INLINE_ARGS = 3
123
125
 
124
126
  # Truncation limits for display
125
127
  _MAX_TODO_CONTENT_LEN = 70
128
+ _DEFAULT_TODO_WRAP_WIDTH = 80
129
+ _TODO_WRAP_GUARD_COLUMNS = 4
126
130
  _MAX_WEB_CONTENT_LEN = 100
127
131
 
128
132
  # Tools that have their key info already in the header (no need for args line)
@@ -1063,6 +1067,8 @@ class ToolCallMessage(Vertical):
1063
1067
  """Toggle expansion of the tool's preview/full output."""
1064
1068
  if not self._output:
1065
1069
  return
1070
+ if not self._expanded and not self._has_expandable_output():
1071
+ return
1066
1072
  self._expanded = not self._expanded
1067
1073
  self._update_output_display()
1068
1074
 
@@ -1135,6 +1141,21 @@ class ToolCallMessage(Vertical):
1135
1141
  # Default: plain text (Content treats input as literal)
1136
1142
  return FormattedOutput(content=Content(output))
1137
1143
 
1144
+ def _has_expandable_output(self) -> bool:
1145
+ """Return whether collapsed output has hidden content to expand."""
1146
+ output = self._output.strip()
1147
+ if not output:
1148
+ return False
1149
+
1150
+ lines = output.split("\n")
1151
+ if len(lines) > self._PREVIEW_LINES or len(output) > self._PREVIEW_CHARS:
1152
+ return True
1153
+
1154
+ if self._tool_name == "write_todos":
1155
+ return self._format_output(output, is_preview=True).truncation is not None
1156
+
1157
+ return False
1158
+
1138
1159
  def _prefix_output(self, content: Content) -> Content: # noqa: PLR6301 # Grouped as method for widget cohesion
1139
1160
  """Prefix output with output marker and indent continuation lines.
1140
1161
 
@@ -1177,14 +1198,38 @@ class ToolCallMessage(Vertical):
1177
1198
  lines.extend([Content.assemble(" ", stats), Content("")])
1178
1199
 
1179
1200
  # Format each item
1180
- lines.extend(self._format_single_todo(item) for item in items[:max_items])
1201
+ lines.extend(
1202
+ self._format_single_todo(item, is_preview=is_preview)
1203
+ for item in items[:max_items]
1204
+ )
1181
1205
 
1182
1206
  truncation = None
1183
- if is_preview and len(items) > max_items:
1184
- truncation = f"{len(items) - max_items} more"
1207
+ if is_preview:
1208
+ hidden_items = len(items) - max_items
1209
+ if hidden_items > 0:
1210
+ truncation = f"{hidden_items} more"
1211
+ elif any(
1212
+ len(self._todo_text(item)) > _MAX_TODO_CONTENT_LEN
1213
+ for item in items[:max_items]
1214
+ ):
1215
+ truncation = "full todo text"
1185
1216
 
1186
1217
  return FormattedOutput(content=Content("\n").join(lines), truncation=truncation)
1187
1218
 
1219
+ @staticmethod
1220
+ def _todo_text(item: dict | str) -> str:
1221
+ """Return display text for a todo item.
1222
+
1223
+ Args:
1224
+ item: Todo item dictionary or plain string.
1225
+
1226
+ Returns:
1227
+ Todo content text.
1228
+ """
1229
+ if isinstance(item, dict):
1230
+ return str(item.get("content", str(item)))
1231
+ return str(item)
1232
+
1188
1233
  def _parse_todo_items(self, output: str) -> list | None: # noqa: PLR6301 # Grouped as method for widget cohesion
1189
1234
  """Parse todo items from output.
1190
1235
 
@@ -1227,37 +1272,113 @@ class ToolCallMessage(Vertical):
1227
1272
  parts.append(Content.styled(f"{completed} done", colors.success))
1228
1273
  return Content.styled(" | ", "dim").join(parts) if parts else Content("")
1229
1274
 
1230
- def _format_single_todo(self, item: dict | str) -> Content:
1275
+ def _todo_content_width(self, indent_width: int) -> int:
1276
+ """Return the todo content wrap width for the current widget size.
1277
+
1278
+ Args:
1279
+ indent_width: Display width before todo content starts.
1280
+
1281
+ Returns:
1282
+ Width available for todo content wrapping.
1283
+ """
1284
+ display_width = 0
1285
+ for widget in (self._full_widget, self._preview_widget, self):
1286
+ if widget and widget.is_mounted and widget.size.width > 0:
1287
+ display_width = widget.size.width
1288
+ break
1289
+
1290
+ if not display_width:
1291
+ try:
1292
+ display_width = self.app.size.width
1293
+ except NoActiveAppError:
1294
+ display_width = _DEFAULT_TODO_WRAP_WIDTH
1295
+
1296
+ output_prefix_width = len(get_glyphs().output_prefix) + 1
1297
+ available = (
1298
+ display_width
1299
+ - output_prefix_width
1300
+ - indent_width
1301
+ - _TODO_WRAP_GUARD_COLUMNS
1302
+ )
1303
+ return max(20, available)
1304
+
1305
+ def _format_todo_line(
1306
+ self,
1307
+ prefix: Content,
1308
+ text: str,
1309
+ *,
1310
+ is_preview: bool,
1311
+ text_style: str | None = None,
1312
+ ) -> Content:
1313
+ """Format a todo row, wrapping expanded content under the text column.
1314
+
1315
+ Args:
1316
+ prefix: Styled status prefix before todo content.
1317
+ text: Todo text to render.
1318
+ is_preview: Whether the compact preview is being rendered.
1319
+ text_style: Optional style for todo content.
1320
+
1321
+ Returns:
1322
+ Styled `Content` for one todo row.
1323
+ """
1324
+ if is_preview and len(text) > _MAX_TODO_CONTENT_LEN:
1325
+ text = text[: _MAX_TODO_CONTENT_LEN - 3] + "..."
1326
+
1327
+ if is_preview:
1328
+ content = Content.styled(text, text_style) if text_style else Content(text)
1329
+ return Content.assemble(prefix, content)
1330
+
1331
+ indent = " " * len(prefix.plain)
1332
+ wrapped = textwrap.wrap(
1333
+ text,
1334
+ width=self._todo_content_width(len(prefix.plain)),
1335
+ break_long_words=True,
1336
+ break_on_hyphens=False,
1337
+ ) or [""]
1338
+ parts: list[Content] = [prefix]
1339
+ for index, line in enumerate(wrapped):
1340
+ if index:
1341
+ parts.append(Content("\n" + indent))
1342
+ content = Content.styled(line, text_style) if text_style else Content(line)
1343
+ parts.append(content)
1344
+ return Content.assemble(*parts)
1345
+
1346
+ def _format_single_todo(self, item: dict | str, *, is_preview: bool) -> Content:
1231
1347
  """Format a single todo item.
1232
1348
 
1349
+ Args:
1350
+ item: Todo item dictionary or plain string.
1351
+ is_preview: Whether the compact preview is being rendered.
1352
+
1233
1353
  Returns:
1234
1354
  Styled `Content` with checkbox and status styling.
1235
1355
  """
1236
1356
  colors = theme.get_theme_colors(self)
1237
1357
  if isinstance(item, dict):
1238
- text = item.get("content", str(item))
1358
+ text = self._todo_text(item)
1239
1359
  status = item.get("status", "pending")
1240
1360
  else:
1241
- text = str(item)
1361
+ text = self._todo_text(item)
1242
1362
  status = "pending"
1243
1363
 
1244
- if len(text) > _MAX_TODO_CONTENT_LEN:
1245
- text = text[: _MAX_TODO_CONTENT_LEN - 3] + "..."
1246
-
1247
1364
  glyphs = get_glyphs()
1248
1365
  if status == "completed":
1249
- return Content.assemble(
1250
- Content.styled(f" {glyphs.checkmark} done", colors.success),
1251
- Content.styled(f" {text}", "dim"),
1366
+ return self._format_todo_line(
1367
+ Content.styled(f" {glyphs.checkmark} done ", colors.success),
1368
+ text,
1369
+ is_preview=is_preview,
1370
+ text_style="dim",
1252
1371
  )
1253
1372
  if status == "in_progress":
1254
- return Content.assemble(
1255
- Content.styled(f" {glyphs.circle_filled} active", colors.warning),
1256
- f" {text}",
1373
+ return self._format_todo_line(
1374
+ Content.styled(f" {glyphs.circle_filled} active ", colors.warning),
1375
+ text,
1376
+ is_preview=is_preview,
1257
1377
  )
1258
- return Content.assemble(
1259
- Content.styled(f" {glyphs.circle_empty} todo", "dim"),
1260
- f" {text}",
1378
+ return self._format_todo_line(
1379
+ Content.styled(f" {glyphs.circle_empty} todo ", "dim"),
1380
+ text,
1381
+ is_preview=is_preview,
1261
1382
  )
1262
1383
 
1263
1384
  def _format_ls_output( # noqa: PLR6301 # Grouped as method for widget cohesion
@@ -1574,11 +1695,24 @@ class ToolCallMessage(Vertical):
1574
1695
  self._hint_widget.display = True
1575
1696
  elif output_stripped:
1576
1697
  # Output fits in preview, show formatted
1577
- result = self._format_output(output_stripped, is_preview=False)
1698
+ is_tool_preview = self._tool_name == "write_todos"
1699
+ result = self._format_output(
1700
+ output_stripped,
1701
+ is_preview=is_tool_preview,
1702
+ )
1578
1703
  prefixed = self._prefix_output(result.content)
1579
1704
  self._preview_widget.update(prefixed)
1580
1705
  self._preview_widget.display = True
1581
- self._hint_widget.display = False
1706
+ if result.truncation:
1707
+ ellipsis = get_glyphs().ellipsis
1708
+ hint = Content.styled(
1709
+ f"{ellipsis} {result.truncation} — click or Ctrl+O to expand",
1710
+ "dim",
1711
+ )
1712
+ self._hint_widget.update(hint)
1713
+ self._hint_widget.display = True
1714
+ else:
1715
+ self._hint_widget.display = False
1582
1716
  else:
1583
1717
  self._preview_widget.display = False
1584
1718
  self._hint_widget.display = False
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "deepagents-cli"
7
- version = "0.0.54"
7
+ version = "0.0.55"
8
8
  description = "Terminal interface for Deep Agents - interactive AI agent with file operations, shell access, and sub-agent capabilities."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -394,6 +394,122 @@ class TestToolCallMessageMarkupSafety:
394
394
  assert msg.has_expandable_args is True
395
395
 
396
396
 
397
+ class TestToolCallMessageTodos:
398
+ """Tests for `write_todos` output formatting."""
399
+
400
+ def test_todo_preview_truncates_long_content(self) -> None:
401
+ """Collapsed todo preview should keep the compact character limit."""
402
+ long = "Implement " + "very detailed authentication flow " * 4
403
+ msg = ToolCallMessage("write_todos")
404
+
405
+ result = msg._format_todos_output(
406
+ repr([{"content": long, "status": "in_progress"}]),
407
+ is_preview=True,
408
+ )
409
+
410
+ assert result.content.plain.endswith("...")
411
+ assert long not in result.content.plain
412
+ assert result.truncation == "full todo text"
413
+
414
+ async def test_todo_collapsed_short_output_uses_preview_formatting(self) -> None:
415
+ """Collapsed todos should truncate even when raw output fits generically."""
416
+ from textual.app import App, ComposeResult
417
+
418
+ long = "Implement " + "very detailed authentication flow " * 3
419
+ assert len(long) > 70
420
+ output = repr([{"content": long, "status": "pending"}])
421
+ assert len(output) < ToolCallMessage._PREVIEW_CHARS
422
+
423
+ class _Harness(App[None]):
424
+ def __init__(self) -> None:
425
+ super().__init__()
426
+ self.msg = ToolCallMessage("write_todos")
427
+
428
+ def compose(self) -> ComposeResult:
429
+ yield self.msg
430
+
431
+ app = _Harness()
432
+ async with app.run_test() as pilot:
433
+ await pilot.pause()
434
+ app.msg.set_success(output)
435
+ await pilot.pause()
436
+
437
+ assert app.msg._preview_widget is not None
438
+ assert app.msg._hint_widget is not None
439
+ content = app.msg._preview_widget._Static__content # type: ignore[attr-defined]
440
+ assert isinstance(content, Content)
441
+ assert "..." in content.plain
442
+ assert long not in content.plain
443
+ assert app.msg._hint_widget.display is True
444
+
445
+ async def test_todo_short_fully_visible_output_does_not_expand(self) -> None:
446
+ """Clicking fully visible todo output should not show a collapse hint."""
447
+ from textual.app import App, ComposeResult
448
+
449
+ output = repr([{"content": "Write tests", "status": "pending"}])
450
+
451
+ class _Harness(App[None]):
452
+ def __init__(self) -> None:
453
+ super().__init__()
454
+ self.msg = ToolCallMessage("write_todos")
455
+
456
+ def compose(self) -> ComposeResult:
457
+ yield self.msg
458
+
459
+ app = _Harness()
460
+ async with app.run_test() as pilot:
461
+ await pilot.pause()
462
+ app.msg.set_success(output)
463
+ await pilot.pause()
464
+
465
+ assert app.msg._hint_widget is not None
466
+ assert app.msg._hint_widget.display is False
467
+
468
+ app.msg.toggle_output()
469
+ await pilot.pause()
470
+
471
+ assert app.msg._expanded is False
472
+ assert app.msg._hint_widget.display is False
473
+
474
+ def test_todo_expanded_shows_full_wrapped_content(self) -> None:
475
+ """Expanded todo output should wrap long content without truncating."""
476
+ long = (
477
+ "Implement the new authentication flow using OAuth2 with PKCE for "
478
+ "the CLI login command and preserve readable todo output"
479
+ )
480
+ msg = ToolCallMessage("write_todos")
481
+
482
+ result = msg._format_todos_output(
483
+ repr([{"content": long, "status": "in_progress"}]),
484
+ is_preview=False,
485
+ )
486
+ plain = result.content.plain
487
+
488
+ assert "..." not in plain
489
+ assert long.replace(" ", "") == plain.split("active ", 1)[1].replace(
490
+ "\n ",
491
+ "",
492
+ ).replace(" ", "")
493
+ assert "\n " in plain
494
+
495
+ def test_todo_expanded_continuation_aligns_content_column(self) -> None:
496
+ """Wrapped continuation lines should align under the todo text."""
497
+ long = "Write integration tests for " + "token refresh revocation " * 4
498
+ msg = ToolCallMessage("write_todos")
499
+
500
+ result = msg._format_todos_output(
501
+ repr([{"content": long, "status": "pending"}]),
502
+ is_preview=False,
503
+ )
504
+ lines = result.content.plain.splitlines()
505
+ todo_start = next(
506
+ index for index, line in enumerate(lines) if "todo " in line
507
+ )
508
+
509
+ assert len(lines) > todo_start + 1
510
+ assert lines[todo_start + 1].startswith(" ")
511
+
512
+
397
513
  class TestToolCallMessageExpandableArgs:
398
514
  """Tests for the `ask_user` expandable-arguments toggle."""
399
515
 
@@ -789,6 +789,59 @@ class TestExecuteTaskTextualParallelToolSpinner:
789
789
 
790
790
  assert statuses[-1] == "Thinking"
791
791
 
792
+ async def test_edit_file_tool_keeps_thinking_spinner_while_pending(self) -> None:
793
+ """`edit_file` should not leave a visual gap before approval/execution."""
794
+ statuses: list[str | None] = []
795
+
796
+ async def record_spinner(status: str | None) -> None:
797
+ await asyncio.sleep(0)
798
+ statuses.append(status)
799
+
800
+ chunks = [
801
+ (
802
+ (),
803
+ "messages",
804
+ (
805
+ _tool_call_message(
806
+ "edit_file",
807
+ {
808
+ "file_path": "example.py",
809
+ "old_string": "old",
810
+ "new_string": "new",
811
+ },
812
+ "tool-1",
813
+ ),
814
+ {},
815
+ ),
816
+ ),
817
+ (
818
+ (),
819
+ "messages",
820
+ (
821
+ ToolMessage(content="edited", tool_call_id="tool-1"),
822
+ {},
823
+ ),
824
+ ),
825
+ ]
826
+
827
+ adapter = TextualUIAdapter(
828
+ mount_message=_mock_mount,
829
+ update_status=_noop_status,
830
+ request_approval=_mock_approval,
831
+ set_spinner=record_spinner,
832
+ )
833
+
834
+ await execute_task_textual(
835
+ user_input="edit the file",
836
+ agent=_FakeAgent(chunks),
837
+ assistant_id="assistant",
838
+ session_state=SimpleNamespace(thread_id="thread-1", auto_approve=True),
839
+ adapter=adapter,
840
+ )
841
+
842
+ assert statuses[:2] == ["Thinking", "Thinking"]
843
+ assert None not in statuses
844
+
792
845
  async def test_spinner_with_three_parallel_tools_out_of_order(self) -> None:
793
846
  """Three parallel tools completed out of order; Thinking after all."""
794
847
  statuses: list[str | None] = []
@@ -1023,7 +1023,7 @@ wheels = [
1023
1023
 
1024
1024
  [[package]]
1025
1025
  name = "deepagents-cli"
1026
- version = "0.0.54"
1026
+ version = "0.0.55"
1027
1027
  source = { editable = "." }
1028
1028
  dependencies = [
1029
1029
  { name = "aiosqlite" },
File without changes