tsugite-cli 0.14.0__tar.gz → 0.14.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 (396) hide show
  1. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/PKG-INFO +14 -1
  2. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/README.md +12 -0
  3. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/pyproject.toml +15 -2
  4. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/agent_inheritance.py +3 -0
  5. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/agent_preparation.py +13 -0
  6. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/agent_runner/exec_directives.py +1 -1
  7. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/builtin_agents/default.md +9 -3
  8. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/builtin_skills/tsugite-jinja-reference/SKILL.md +2 -1
  9. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/chat.py +3 -13
  10. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/plugins.py +8 -6
  11. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/run.py +26 -20
  12. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/workspace.py +0 -1
  13. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/core/executor.py +60 -12
  14. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/adapters/base.py +52 -20
  15. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/adapters/http.py +146 -132
  16. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/commands.py +13 -10
  17. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/compaction_scheduler.py +5 -4
  18. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/scheduler.py +26 -11
  19. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/session_runner.py +1 -1
  20. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/session_store.py +191 -139
  21. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/css/console.css +4 -4
  22. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/css/theme.css +11 -5
  23. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/index.html +53 -25
  24. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/js/app.js +23 -0
  25. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/js/views/conversation/history.js +37 -19
  26. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/js/views/conversation/input.js +1 -1
  27. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/js/views/conversation/sessions.js +86 -29
  28. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/js/views/conversation/streaming.js +51 -24
  29. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/js/views/conversations.js +174 -80
  30. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/events/base.py +0 -1
  31. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/events/bus.py +3 -1
  32. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/events/events.py +2 -0
  33. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/events/helpers.py +17 -2
  34. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/history/models.py +2 -1
  35. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/md_agents.py +2 -0
  36. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/options.py +1 -0
  37. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/renderer.py +8 -1
  38. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/schemas/agent.schema.json +12 -0
  39. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/tools/agents.py +44 -43
  40. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/tools/skills.py +0 -2
  41. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/ui/__init__.py +11 -0
  42. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/ui/chat.py +5 -73
  43. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/ui/helpers.py +25 -2
  44. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/ui/jsonl.py +8 -2
  45. tsugite_cli-0.14.2/tsugite/ui/live.py +150 -0
  46. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/ui/plain.py +8 -4
  47. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/ui/repl_chat.py +0 -8
  48. tsugite_cli-0.14.0/.github/copilot-instructions.md +0 -252
  49. tsugite_cli-0.14.0/.github/workflows/ci.yml +0 -74
  50. tsugite_cli-0.14.0/.github/workflows/docker-publish.yml +0 -60
  51. tsugite_cli-0.14.0/.github/workflows/pypi-publish.yml +0 -49
  52. tsugite_cli-0.14.0/CLAUDE.md +0 -1
  53. tsugite_cli-0.14.0/Dockerfile +0 -19
  54. tsugite_cli-0.14.0/cliff.toml +0 -44
  55. tsugite_cli-0.14.0/docs/agents.md +0 -341
  56. tsugite_cli-0.14.0/docs/plugin-events.md +0 -131
  57. tsugite_cli-0.14.0/docs/plugin-hooks.md +0 -110
  58. tsugite_cli-0.14.0/docs/plugin-tools.md +0 -109
  59. tsugite_cli-0.14.0/docs/plugins.md +0 -139
  60. tsugite_cli-0.14.0/docs/release_notes/v0.13.0.md +0 -51
  61. tsugite_cli-0.14.0/docs/release_notes/v0.14.0.md +0 -39
  62. tsugite_cli-0.14.0/docs/secrets.md +0 -136
  63. tsugite_cli-0.14.0/docs/test_agents/prefresh_readme.md +0 -16
  64. tsugite_cli-0.14.0/docs/test_agents/template_test.md +0 -14
  65. tsugite_cli-0.14.0/docs/test_agents/test_agent.md +0 -15
  66. tsugite_cli-0.14.0/docs/test_agents/test_steps.md +0 -8
  67. tsugite_cli-0.14.0/examples/tsugite-example-plugin/pyproject.toml +0 -17
  68. tsugite_cli-0.14.0/examples/tsugite-example-plugin/tsugite_example_plugin/__init__.py +0 -41
  69. tsugite_cli-0.14.0/mise.toml +0 -22
  70. tsugite_cli-0.14.0/plugins/tsugite-tmux/pyproject.toml +0 -16
  71. tsugite_cli-0.14.0/plugins/tsugite-tmux/tests/test_tmux_tools.py +0 -376
  72. tsugite_cli-0.14.0/plugins/tsugite-tmux/tsugite_tmux/__init__.py +0 -292
  73. tsugite_cli-0.14.0/scripts/backfill_usage.py +0 -98
  74. tsugite_cli-0.14.0/scripts/bump_version.py +0 -90
  75. tsugite_cli-0.14.0/scripts/regenerate_schema.py +0 -33
  76. tsugite_cli-0.14.0/scripts/update_model_registry.py +0 -200
  77. tsugite_cli-0.14.0/tests/README.md +0 -123
  78. tsugite_cli-0.14.0/tests/__init__.py +0 -1
  79. tsugite_cli-0.14.0/tests/conftest.py +0 -573
  80. tsugite_cli-0.14.0/tests/core/__init__.py +0 -1
  81. tsugite_cli-0.14.0/tests/core/test_agent.py +0 -884
  82. tsugite_cli-0.14.0/tests/core/test_agent_context_tokens.py +0 -188
  83. tsugite_cli-0.14.0/tests/core/test_agent_event_loop.py +0 -310
  84. tsugite_cli-0.14.0/tests/core/test_agent_parser_triple_quotes.py +0 -70
  85. tsugite_cli-0.14.0/tests/core/test_agent_ui_events.py +0 -485
  86. tsugite_cli-0.14.0/tests/core/test_content_blocks.py +0 -269
  87. tsugite_cli-0.14.0/tests/core/test_executor.py +0 -668
  88. tsugite_cli-0.14.0/tests/core/test_executor_escape_tripwire.py +0 -35
  89. tsugite_cli-0.14.0/tests/core/test_memory.py +0 -165
  90. tsugite_cli-0.14.0/tests/core/test_proxy.py +0 -165
  91. tsugite_cli-0.14.0/tests/core/test_sandbox.py +0 -171
  92. tsugite_cli-0.14.0/tests/core/test_state.py +0 -85
  93. tsugite_cli-0.14.0/tests/core/test_subprocess_executor.py +0 -382
  94. tsugite_cli-0.14.0/tests/core/test_tools.py +0 -372
  95. tsugite_cli-0.14.0/tests/daemon/__init__.py +0 -0
  96. tsugite_cli-0.14.0/tests/daemon/test_chat_reentrancy.py +0 -399
  97. tsugite_cli-0.14.0/tests/daemon/test_collect_events.py +0 -106
  98. tsugite_cli-0.14.0/tests/daemon/test_compaction_command.py +0 -247
  99. tsugite_cli-0.14.0/tests/daemon/test_event_broadcast_scope.py +0 -64
  100. tsugite_cli-0.14.0/tests/daemon/test_final_answer_delivery.py +0 -75
  101. tsugite_cli-0.14.0/tests/daemon/test_http_adapter.py +0 -797
  102. tsugite_cli-0.14.0/tests/daemon/test_message_context_session_started.py +0 -152
  103. tsugite_cli-0.14.0/tests/daemon/test_message_context_topic.py +0 -207
  104. tsugite_cli-0.14.0/tests/daemon/test_prompt_too_long_retry.py +0 -239
  105. tsugite_cli-0.14.0/tests/daemon/test_session_metadata_api.py +0 -249
  106. tsugite_cli-0.14.0/tests/daemon/test_session_pinning_api.py +0 -284
  107. tsugite_cli-0.14.0/tests/daemon/test_session_primary_api.py +0 -151
  108. tsugite_cli-0.14.0/tests/daemon/test_session_store_named_route.py +0 -91
  109. tsugite_cli-0.14.0/tests/daemon/test_session_store_pinning.py +0 -194
  110. tsugite_cli-0.14.0/tests/daemon/test_session_store_primary.py +0 -182
  111. tsugite_cli-0.14.0/tests/daemon/test_skill_trigger_loading.py +0 -154
  112. tsugite_cli-0.14.0/tests/e2e/__init__.py +0 -0
  113. tsugite_cli-0.14.0/tests/e2e/conftest.py +0 -162
  114. tsugite_cli-0.14.0/tests/e2e/test_auth.py +0 -27
  115. tsugite_cli-0.14.0/tests/e2e/test_chat.py +0 -69
  116. tsugite_cli-0.14.0/tests/e2e/test_context_bar.py +0 -30
  117. tsugite_cli-0.14.0/tests/e2e/test_draft_persistence.py +0 -115
  118. tsugite_cli-0.14.0/tests/e2e/test_history.py +0 -148
  119. tsugite_cli-0.14.0/tests/e2e/test_history_code_rendering.py +0 -329
  120. tsugite_cli-0.14.0/tests/e2e/test_markdown_rendering.py +0 -186
  121. tsugite_cli-0.14.0/tests/e2e/test_message_actions.py +0 -253
  122. tsugite_cli-0.14.0/tests/e2e/test_page_load.py +0 -35
  123. tsugite_cli-0.14.0/tests/e2e/test_prompt_inspector.py +0 -53
  124. tsugite_cli-0.14.0/tests/e2e/test_scroll_behavior.py +0 -137
  125. tsugite_cli-0.14.0/tests/e2e/test_sessions.py +0 -41
  126. tsugite_cli-0.14.0/tests/e2e/test_sidebar_redesign.py +0 -73
  127. tsugite_cli-0.14.0/tests/e2e/test_sse_metadata.py +0 -31
  128. tsugite_cli-0.14.0/tests/events/test_event_consolidation.py +0 -206
  129. tsugite_cli-0.14.0/tests/events/test_filtered_subscriptions.py +0 -261
  130. tsugite_cli-0.14.0/tests/integration/test_concurrent_workspace_cwd.py +0 -88
  131. tsugite_cli-0.14.0/tests/integration/test_exec_directive_e2e.py +0 -37
  132. tsugite_cli-0.14.0/tests/integration/test_nested_session_spawn.py +0 -103
  133. tsugite_cli-0.14.0/tests/smoke_test.sh +0 -87
  134. tsugite_cli-0.14.0/tests/test_agent_file_hot_loading.py +0 -279
  135. tsugite_cli-0.14.0/tests/test_agent_inheritance.py +0 -485
  136. tsugite_cli-0.14.0/tests/test_agent_parser.py +0 -330
  137. tsugite_cli-0.14.0/tests/test_agent_preparation.py +0 -107
  138. tsugite_cli-0.14.0/tests/test_agent_sessions.py +0 -510
  139. tsugite_cli-0.14.0/tests/test_agent_skills.py +0 -1019
  140. tsugite_cli-0.14.0/tests/test_agent_utils.py +0 -220
  141. tsugite_cli-0.14.0/tests/test_agents_tool.py +0 -219
  142. tsugite_cli-0.14.0/tests/test_anthropic_extended_thinking.py +0 -198
  143. tsugite_cli-0.14.0/tests/test_attachment_deduplication.py +0 -281
  144. tsugite_cli-0.14.0/tests/test_attachments.py +0 -1074
  145. tsugite_cli-0.14.0/tests/test_auto_context_handler.py +0 -429
  146. tsugite_cli-0.14.0/tests/test_auto_discovery.py +0 -223
  147. tsugite_cli-0.14.0/tests/test_background_task_status.py +0 -187
  148. tsugite_cli-0.14.0/tests/test_background_tasks.py +0 -722
  149. tsugite_cli-0.14.0/tests/test_builtin_agent_paths.py +0 -164
  150. tsugite_cli-0.14.0/tests/test_builtin_agents.py +0 -229
  151. tsugite_cli-0.14.0/tests/test_chat_cli.py +0 -186
  152. tsugite_cli-0.14.0/tests/test_chat_error_handling.py +0 -65
  153. tsugite_cli-0.14.0/tests/test_claude_code_attachments.py +0 -103
  154. tsugite_cli-0.14.0/tests/test_claude_code_provider.py +0 -1167
  155. tsugite_cli-0.14.0/tests/test_cli.py +0 -873
  156. tsugite_cli-0.14.0/tests/test_cli_arguments.py +0 -132
  157. tsugite_cli-0.14.0/tests/test_cli_rendering.py +0 -387
  158. tsugite_cli-0.14.0/tests/test_cli_skills.py +0 -85
  159. tsugite_cli-0.14.0/tests/test_cli_subcommands.py +0 -159
  160. tsugite_cli-0.14.0/tests/test_compaction_context_preservation.py +0 -233
  161. tsugite_cli-0.14.0/tests/test_compaction_event.py +0 -160
  162. tsugite_cli-0.14.0/tests/test_compaction_progress.py +0 -162
  163. tsugite_cli-0.14.0/tests/test_completion_callbacks.py +0 -479
  164. tsugite_cli-0.14.0/tests/test_config.py +0 -157
  165. tsugite_cli-0.14.0/tests/test_custom_shell_tools.py +0 -340
  166. tsugite_cli-0.14.0/tests/test_custom_ui.py +0 -338
  167. tsugite_cli-0.14.0/tests/test_daemon_auth.py +0 -254
  168. tsugite_cli-0.14.0/tests/test_daemon_compaction_scheduler.py +0 -191
  169. tsugite_cli-0.14.0/tests/test_daemon_config.py +0 -133
  170. tsugite_cli-0.14.0/tests/test_daemon_push.py +0 -260
  171. tsugite_cli-0.14.0/tests/test_daemon_scheduler.py +0 -625
  172. tsugite_cli-0.14.0/tests/test_daemon_unified_sessions.py +0 -602
  173. tsugite_cli-0.14.0/tests/test_discord_progress.py +0 -529
  174. tsugite_cli-0.14.0/tests/test_error_display.py +0 -85
  175. tsugite_cli-0.14.0/tests/test_event_storage.py +0 -153
  176. tsugite_cli-0.14.0/tests/test_events_to_messages.py +0 -217
  177. tsugite_cli-0.14.0/tests/test_exceptions.py +0 -17
  178. tsugite_cli-0.14.0/tests/test_exec_directives.py +0 -309
  179. tsugite_cli-0.14.0/tests/test_file_references.py +0 -266
  180. tsugite_cli-0.14.0/tests/test_file_tools.py +0 -954
  181. tsugite_cli-0.14.0/tests/test_fs_tool_workspace.py +0 -89
  182. tsugite_cli-0.14.0/tests/test_handle_message_flush.py +0 -216
  183. tsugite_cli-0.14.0/tests/test_history_migrate.py +0 -274
  184. tsugite_cli-0.14.0/tests/test_hooks.py +0 -1213
  185. tsugite_cli-0.14.0/tests/test_http_tools.py +0 -397
  186. tsugite_cli-0.14.0/tests/test_interaction_backend_isolation.py +0 -186
  187. tsugite_cli-0.14.0/tests/test_interaction_backends.py +0 -246
  188. tsugite_cli-0.14.0/tests/test_interactive_context.py +0 -311
  189. tsugite_cli-0.14.0/tests/test_interactive_tool.py +0 -483
  190. tsugite_cli-0.14.0/tests/test_jsonl_ui.py +0 -203
  191. tsugite_cli-0.14.0/tests/test_list_agents_tool.py +0 -232
  192. tsugite_cli-0.14.0/tests/test_live_event_recording.py +0 -130
  193. tsugite_cli-0.14.0/tests/test_md_agents_json.py +0 -97
  194. tsugite_cli-0.14.0/tests/test_memory_sanitize.py +0 -288
  195. tsugite_cli-0.14.0/tests/test_model_registry.py +0 -37
  196. tsugite_cli-0.14.0/tests/test_models.py +0 -79
  197. tsugite_cli-0.14.0/tests/test_multi_agent.py +0 -162
  198. tsugite_cli-0.14.0/tests/test_multistep_agents.py +0 -972
  199. tsugite_cli-0.14.0/tests/test_multistep_retry_side_effects.py +0 -108
  200. tsugite_cli-0.14.0/tests/test_orchestrator_heartbeat.py +0 -228
  201. tsugite_cli-0.14.0/tests/test_plugins.py +0 -442
  202. tsugite_cli-0.14.0/tests/test_post_compaction_counters.py +0 -255
  203. tsugite_cli-0.14.0/tests/test_prompt_snapshot.py +0 -127
  204. tsugite_cli-0.14.0/tests/test_provider_registry.py +0 -53
  205. tsugite_cli-0.14.0/tests/test_provider_turn_heartbeat.py +0 -142
  206. tsugite_cli-0.14.0/tests/test_reasoning_models.py +0 -127
  207. tsugite_cli-0.14.0/tests/test_renderer.py +0 -760
  208. tsugite_cli-0.14.0/tests/test_rendering_scenarios.py +0 -490
  209. tsugite_cli-0.14.0/tests/test_repl_commands.py +0 -274
  210. tsugite_cli-0.14.0/tests/test_repl_completer.py +0 -105
  211. tsugite_cli-0.14.0/tests/test_repl_handler.py +0 -187
  212. tsugite_cli-0.14.0/tests/test_retry_system.py +0 -176
  213. tsugite_cli-0.14.0/tests/test_run_if.py +0 -92
  214. tsugite_cli-0.14.0/tests/test_schedule_model_override.py +0 -148
  215. tsugite_cli-0.14.0/tests/test_scheduler_history_injection.py +0 -363
  216. tsugite_cli-0.14.0/tests/test_schema.py +0 -207
  217. tsugite_cli-0.14.0/tests/test_secret_access_event.py +0 -69
  218. tsugite_cli-0.14.0/tests/test_secrets.py +0 -487
  219. tsugite_cli-0.14.0/tests/test_security_phase1.py +0 -369
  220. tsugite_cli-0.14.0/tests/test_send_message.py +0 -58
  221. tsugite_cli-0.14.0/tests/test_session_metadata.py +0 -304
  222. tsugite_cli-0.14.0/tests/test_session_orchestrator_tools.py +0 -152
  223. tsugite_cli-0.14.0/tests/test_session_progress_cache.py +0 -139
  224. tsugite_cli-0.14.0/tests/test_shell_tool_cwd.py +0 -82
  225. tsugite_cli-0.14.0/tests/test_skill_discovery.py +0 -691
  226. tsugite_cli-0.14.0/tests/test_skill_tools.py +0 -444
  227. tsugite_cli-0.14.0/tests/test_stdin.py +0 -204
  228. tsugite_cli-0.14.0/tests/test_step_render_framework_flags.py +0 -108
  229. tsugite_cli-0.14.0/tests/test_subagent_subprocess.py +0 -119
  230. tsugite_cli-0.14.0/tests/test_time_tool.py +0 -47
  231. tsugite_cli-0.14.0/tests/test_tool_directives.py +0 -433
  232. tsugite_cli-0.14.0/tests/test_tool_registry.py +0 -433
  233. tsugite_cli-0.14.0/tests/test_unified_event_log.py +0 -185
  234. tsugite_cli-0.14.0/tests/test_user_agent.py +0 -99
  235. tsugite_cli-0.14.0/tests/test_workspace_auto_continue.py +0 -74
  236. tsugite_cli-0.14.0/tests/test_workspace_cwd.py +0 -370
  237. tsugite_cli-0.14.0/tests/test_workspace_discovery.py +0 -120
  238. tsugite_cli-0.14.0/uv.lock +0 -2628
  239. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/.gitignore +0 -0
  240. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/AGENTS.md +0 -0
  241. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/CONTRIBUTING.md +0 -0
  242. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/LICENSE +0 -0
  243. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/__init__.py +0 -0
  244. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/agent_runner/__init__.py +2 -2
  245. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/agent_runner/exec_runner.py +0 -0
  246. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/agent_runner/helpers.py +0 -0
  247. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/agent_runner/history_integration.py +0 -0
  248. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/agent_runner/metrics.py +0 -0
  249. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/agent_runner/models.py +0 -0
  250. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/agent_runner/runner.py +0 -0
  251. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/agent_runner/validation.py +0 -0
  252. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/agent_utils.py +0 -0
  253. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/attachments/__init__.py +0 -0
  254. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/attachments/auto_context.py +0 -0
  255. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/attachments/base.py +0 -0
  256. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/attachments/file.py +0 -0
  257. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/attachments/inline.py +0 -0
  258. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/attachments/storage.py +0 -0
  259. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/attachments/url.py +0 -0
  260. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/attachments/youtube.py +0 -0
  261. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/builtin_agents/.gitkeep +0 -0
  262. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/builtin_agents/code_searcher.md +0 -0
  263. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/builtin_agents/file_searcher.md +0 -0
  264. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/builtin_agents/onboard.md +0 -0
  265. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/builtin_skills/.gitkeep +0 -0
  266. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/builtin_skills/codebase-exploration/SKILL.md +0 -0
  267. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/builtin_skills/python-math/SKILL.md +0 -0
  268. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/builtin_skills/response-patterns/SKILL.md +0 -0
  269. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/builtin_skills/scheduling/SKILL.md +0 -0
  270. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/builtin_skills/skill-authoring/SKILL.md +0 -0
  271. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/builtin_skills/tsugite-agent-basics/SKILL.md +0 -0
  272. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/builtin_skills/tsugite-skill-basics/SKILL.md +0 -0
  273. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cache.py +0 -0
  274. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/__init__.py +0 -0
  275. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/agents.py +0 -0
  276. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/attachments.py +0 -0
  277. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/cache.py +0 -0
  278. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/config.py +0 -0
  279. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/daemon.py +0 -0
  280. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/helpers.py +0 -0
  281. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/history.py +0 -0
  282. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/init.py +0 -0
  283. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/models.py +0 -0
  284. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/render.py +0 -0
  285. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/secrets.py +0 -0
  286. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/skills.py +0 -0
  287. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/tools.py +0 -0
  288. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/usage.py +0 -0
  289. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/cli/validate.py +0 -0
  290. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/config.py +0 -0
  291. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/console.py +0 -0
  292. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/constants.py +0 -0
  293. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/core/__init__.py +0 -0
  294. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/core/agent.py +0 -0
  295. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/core/claude_code.py +0 -0
  296. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/core/content_blocks.py +0 -0
  297. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/core/memory.py +0 -0
  298. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/core/proxy.py +0 -0
  299. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/core/sandbox.py +0 -0
  300. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/core/state.py +0 -0
  301. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/core/subprocess_executor.py +0 -0
  302. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/core/tools.py +0 -0
  303. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/__init__.py +0 -0
  304. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/adapters/__init__.py +0 -0
  305. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/adapters/discord.py +0 -0
  306. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/adapters/scheduler_adapter.py +0 -0
  307. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/auth.py +0 -0
  308. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/config.py +0 -0
  309. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/gateway.py +0 -0
  310. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/memory.py +0 -0
  311. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/push.py +0 -0
  312. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/css/responsive.css +0 -0
  313. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/css/styles.css +0 -0
  314. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/icons/icon-192.png +0 -0
  315. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/icons/icon-512-maskable.png +0 -0
  316. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/icons/icon-512.png +0 -0
  317. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/icons/screenshot-narrow.png +0 -0
  318. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/icons/screenshot-wide.png +0 -0
  319. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/js/api.js +0 -0
  320. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/js/utils.js +0 -0
  321. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/js/vendor/marked.LICENSE.md +0 -0
  322. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/js/vendor/marked.esm.min.js +0 -0
  323. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/js/views/conversation/attachments.js +0 -0
  324. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/js/views/conversation/event_types.js +0 -0
  325. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/js/views/file-editor.js +0 -0
  326. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/js/views/schedules.js +0 -0
  327. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/js/views/usage.js +0 -0
  328. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/js/views/webhooks.js +0 -0
  329. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/js/views/workspace.js +0 -0
  330. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/manifest.json +0 -0
  331. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/web/sw.js +0 -0
  332. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/daemon/webhook_store.py +0 -0
  333. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/events/__init__.py +0 -0
  334. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/exceptions.py +0 -0
  335. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/history/__init__.py +0 -0
  336. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/history/reconstruction.py +0 -0
  337. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/history/storage.py +0 -0
  338. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/hooks.py +0 -0
  339. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/interaction.py +0 -0
  340. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/models.py +0 -0
  341. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/plugins.py +0 -0
  342. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/providers/__init__.py +0 -0
  343. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/providers/anthropic.py +0 -0
  344. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/providers/base.py +0 -0
  345. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/providers/claude_code.py +0 -0
  346. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/providers/model_cache.py +0 -0
  347. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/providers/model_registry.py +0 -0
  348. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/providers/ollama.py +0 -0
  349. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/providers/openai_compat.py +0 -0
  350. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/providers/openrouter.py +0 -0
  351. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/schemas/__init__.py +0 -0
  352. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/secrets/__init__.py +0 -0
  353. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/secrets/backend.py +0 -0
  354. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/secrets/env.py +0 -0
  355. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/secrets/exec.py +0 -0
  356. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/secrets/file.py +0 -0
  357. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/secrets/masking.py +0 -0
  358. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/secrets/registry.py +0 -0
  359. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/secrets/sqlite.py +0 -0
  360. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/shell_tool_config.py +0 -0
  361. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/skill_discovery.py +0 -0
  362. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/templates/AGENTS.md +0 -0
  363. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/templates/IDENTITY.md +0 -0
  364. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/templates/MEMORY.md +0 -0
  365. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/templates/USER.md +0 -0
  366. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/templates/personas/casual-technical.md +0 -0
  367. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/templates/personas/marvin.md +0 -0
  368. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/templates/personas/minimal.md +0 -0
  369. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/tools/__init__.py +0 -0
  370. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/tools/fs.py +0 -0
  371. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/tools/history.py +0 -0
  372. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/tools/http.py +0 -0
  373. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/tools/interactive.py +0 -0
  374. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/tools/notify.py +0 -0
  375. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/tools/schedule.py +0 -0
  376. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/tools/scratchpad.py +0 -0
  377. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/tools/secrets.py +0 -0
  378. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/tools/sessions.py +0 -0
  379. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/tools/shell.py +0 -0
  380. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/tools/shell_tools.py +0 -0
  381. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/tools/time.py +0 -0
  382. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/tsugite.py +0 -0
  383. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/ui/base.py +0 -0
  384. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/ui/repl_commands.py +0 -0
  385. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/ui/repl_completer.py +0 -0
  386. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/ui/repl_handler.py +0 -0
  387. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/ui_context.py +0 -0
  388. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/usage/__init__.py +0 -0
  389. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/usage/store.py +0 -0
  390. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/user_agent.py +0 -0
  391. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/utils.py +0 -0
  392. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/workspace/__init__.py +0 -0
  393. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/workspace/context.py +0 -0
  394. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/workspace/models.py +0 -0
  395. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/workspace/session.py +0 -0
  396. {tsugite_cli-0.14.0 → tsugite_cli-0.14.2}/tsugite/workspace/templates.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tsugite-cli
3
- Version: 0.14.0
3
+ Version: 0.14.2
4
4
  Summary: Micro-agent runner for task automation using markdown definitions
5
5
  Author: Justyn Shull
6
6
  License: GNU AFFERO GENERAL PUBLIC LICENSE
@@ -263,6 +263,7 @@ Provides-Extra: daemon
263
263
  Requires-Dist: cronsim>=2.6; extra == 'daemon'
264
264
  Requires-Dist: discord-py>=2.3.2; extra == 'daemon'
265
265
  Requires-Dist: py-vapid>=1.9.0; extra == 'daemon'
266
+ Requires-Dist: python-multipart>=0.0.9; extra == 'daemon'
266
267
  Requires-Dist: pywebpush>=2.0.0; extra == 'daemon'
267
268
  Requires-Dist: starlette>=0.36; extra == 'daemon'
268
269
  Requires-Dist: uvicorn>=0.25; extra == 'daemon'
@@ -337,6 +338,18 @@ tsu run my-agent.md "do the thing"
337
338
  tsu daemon
338
339
  ```
339
340
 
341
+ ### Output modes
342
+
343
+ `tsu run` keeps its terminal output plain by default so logs are copy-pasteable and behave
344
+ in nested tmux / non-Rich-friendly shells. Pick a richer or quieter mode when you need it:
345
+
346
+ | Mode | When to use |
347
+ | --- | --- |
348
+ | **Default (plain)** | Everyday interactive runs and piped output (`tsu run ... \| less`). |
349
+ | `--ui live` | Long-running interactive runs where a persistent footer showing turn / current tool / tokens / cost / elapsed time is useful. Falls back to plain if stdout is not a TTY or `NO_COLOR` is set. |
350
+ | `--headless` | CI/scripts: result on stdout, no progress chrome. Combine with `--verbose` for stderr trace. |
351
+ | `--plain` | Force plain explicitly (same as the default; useful when overriding configs/aliases). |
352
+
340
353
  ## Features
341
354
 
342
355
  - **Multi-step workflows** with `<!-- tsu:step -->` to chain steps and pass data between them
@@ -67,6 +67,18 @@ tsu run my-agent.md "do the thing"
67
67
  tsu daemon
68
68
  ```
69
69
 
70
+ ### Output modes
71
+
72
+ `tsu run` keeps its terminal output plain by default so logs are copy-pasteable and behave
73
+ in nested tmux / non-Rich-friendly shells. Pick a richer or quieter mode when you need it:
74
+
75
+ | Mode | When to use |
76
+ | --- | --- |
77
+ | **Default (plain)** | Everyday interactive runs and piped output (`tsu run ... \| less`). |
78
+ | `--ui live` | Long-running interactive runs where a persistent footer showing turn / current tool / tokens / cost / elapsed time is useful. Falls back to plain if stdout is not a TTY or `NO_COLOR` is set. |
79
+ | `--headless` | CI/scripts: result on stdout, no progress chrome. Combine with `--verbose` for stderr trace. |
80
+ | `--plain` | Force plain explicitly (same as the default; useful when overriding configs/aliases). |
81
+
70
82
  ## Features
71
83
 
72
84
  - **Multi-step workflows** with `<!-- tsu:step -->` to chain steps and pass data between them
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tsugite-cli"
3
- version = "0.14.0"
3
+ version = "0.14.2"
4
4
  description = "Micro-agent runner for task automation using markdown definitions"
5
5
  authors = [{ name = "Justyn Shull" }]
6
6
  requires-python = ">=3.11"
@@ -34,6 +34,7 @@ daemon = [
34
34
  "cronsim>=2.6",
35
35
  "starlette>=0.36",
36
36
  "uvicorn>=0.25",
37
+ "python-multipart>=0.0.9",
37
38
  "pywebpush>=2.0.0",
38
39
  "py-vapid>=1.9.0",
39
40
  ]
@@ -49,12 +50,23 @@ build-backend = "hatchling.build"
49
50
  [tool.hatch.build.targets.wheel]
50
51
  packages = ["tsugite"]
51
52
 
53
+ [tool.hatch.build.targets.sdist]
54
+ include = [
55
+ "/tsugite",
56
+ "/README.md",
57
+ "/LICENSE",
58
+ "/CONTRIBUTING.md",
59
+ "/AGENTS.md",
60
+ "/pyproject.toml",
61
+ ]
62
+
52
63
  [tool.uv.workspace]
53
64
  members = ["plugins/*"]
54
65
 
55
66
  [tool.uv.sources]
56
67
  tsugite-cli = { workspace = true }
57
68
  tsugite-tmux = { workspace = true }
69
+ tsugite-acp = { workspace = true }
58
70
 
59
71
  [tool.ruff]
60
72
  target-version = "py311"
@@ -73,7 +85,7 @@ line-length = 120
73
85
 
74
86
  [tool.pytest.ini_options]
75
87
  testpaths = ["tests", "plugins"]
76
- addopts = "-n auto --dist loadscope --cov=tsugite --cov-report=term-missing --cov-report=html --cov-report=xml"
88
+ addopts = "-n auto --dist loadfile --cov=tsugite --cov-report=term-missing --cov-report=html --cov-report=xml"
77
89
  asyncio_mode = "strict"
78
90
  asyncio_default_fixture_loop_scope = "function"
79
91
  filterwarnings = ["ignore::RuntimeWarning"]
@@ -129,4 +141,5 @@ dev = [
129
141
  "ruff>=0.1",
130
142
  "vulture>=2.14",
131
143
  "tsugite-tmux",
144
+ "tsugite-acp",
132
145
  ]
@@ -281,6 +281,7 @@ def merge_scalar_fields(parent, child) -> Dict[str, Any]:
281
281
  "model": child.model if child.model else parent.model,
282
282
  "max_turns": child.max_turns if child.max_turns != 5 else parent.max_turns,
283
283
  "reasoning_effort": child.reasoning_effort if child.reasoning_effort else parent.reasoning_effort,
284
+ "auto_load_agent_list": child.auto_load_agent_list or parent.auto_load_agent_list,
284
285
  }
285
286
 
286
287
 
@@ -321,6 +322,7 @@ def merge_list_fields(parent, child) -> Dict[str, List]:
321
322
  merged_tools = _merge_dedup(parent.tools, child.tools)
322
323
  merged_attachments = _merge_attachments(parent.attachments, child.attachments)
323
324
  merged_skills = _merge_dedup(parent.auto_load_skills, child.auto_load_skills)
325
+ merged_auto_load_agents = _merge_dedup(parent.auto_load_agents, child.auto_load_agents)
324
326
 
325
327
  # Custom tools - deduplicate by "name" field (child overrides parent)
326
328
  custom_tool_dict = {}
@@ -333,6 +335,7 @@ def merge_list_fields(parent, child) -> Dict[str, List]:
333
335
  "prefetch": (parent.prefetch or []) + (child.prefetch or []),
334
336
  "custom_tools": list(custom_tool_dict.values()),
335
337
  "auto_load_skills": merged_skills,
338
+ "auto_load_agents": merged_auto_load_agents,
336
339
  }
337
340
 
338
341
 
@@ -547,6 +547,19 @@ class AgentPreparer:
547
547
  # Silently continue if prefetch fails
548
548
  prefetch_context = {}
549
549
 
550
+ # Step 1b: Opt-in <available_agents> injection. Default agents call
551
+ # list_available_agents() on demand instead of carrying the full list.
552
+ if "available_agents" not in prefetch_context and (
553
+ agent_config.auto_load_agent_list or agent_config.auto_load_agents
554
+ ):
555
+ from tsugite.tools.agents import discover_agents, format_agents_markdown
556
+
557
+ agents = discover_agents()
558
+ if agent_config.auto_load_agents:
559
+ wanted = set(agent_config.auto_load_agents)
560
+ agents = [a for a in agents if a["name"] in wanted]
561
+ prefetch_context["available_agents"] = format_agents_markdown(agents)
562
+
550
563
  # Step 2: Execute tool directives (unless skip_tool_directives=True for render)
551
564
  if skip_tool_directives:
552
565
  modified_content = agent.content
@@ -7,7 +7,7 @@ execution note, and returns the modified content plus a context dict of assigned
7
7
 
8
8
  from typing import Any, Dict, List, Optional, Tuple
9
9
 
10
- from tsugite.agent_runner.exec_runner import run_python_block
10
+ from tsugite.agent_runner.exec_runner import ExecBlockResult, run_python_block
11
11
  from tsugite.md_agents import ExecDirective, extract_exec_directives
12
12
 
13
13
 
@@ -15,6 +15,7 @@ attachments:
15
15
  max_turns: 10
16
16
  tools:
17
17
  - spawn_agent
18
+ - list_available_agents
18
19
  - read_file
19
20
  - list_files
20
21
  - write_file
@@ -36,9 +37,6 @@ tools:
36
37
  auto_load_skills:
37
38
  - response-patterns
38
39
  prefetch:
39
- - tool: list_agents
40
- args: {}
41
- assign: available_agents
42
40
  - tool: get_skills_for_template
43
41
  args: {}
44
42
  assign: available_skills
@@ -54,6 +52,14 @@ instructions: |
54
52
  Before starting work, check the `<available_skills>` section and load one if it matches.
55
53
  Call `load_skill("skill_name")` and wait for the next turn before proceeding.
56
54
 
55
+ ## Delegating to Sub-Agents
56
+
57
+ The list of available sub-agents is not auto-attached to your context. When a task
58
+ clearly benefits from a specialized agent (and isn't something you can handle inline),
59
+ call `list_available_agents()` to discover them, then `spawn_agent(path, prompt)` to
60
+ delegate. Skip both for simple tasks or single-file edits - most work doesn't need
61
+ delegation.
62
+
57
63
  ## Guidelines
58
64
 
59
65
  - Be concise and direct in your responses
@@ -76,10 +76,11 @@ Injected via `AgentRenderer.env.globals`:
76
76
  ```
77
77
 
78
78
  ### Environment Access
79
- - `env` → Dictionary of environment variables (`os.environ`)
79
+ - `env` → Dictionary of environment variables (`os.environ`). Also callable as `env(key, default)`.
80
80
  ```jinja2
81
81
  Debug mode: {{ env.get("DEBUG", "false") }}
82
82
  API endpoint: {{ env["API_URL"] }}
83
+ With default: {{ env("DEBUG", "false") }}
83
84
  ```
84
85
 
85
86
  ## Control Patterns
@@ -60,12 +60,9 @@ def chat(
60
60
  workspace_attachments = _build_workspace_attachments(resolved_workspace)
61
61
 
62
62
  with workspace_directory_context(resolved_workspace, root, console) as path_context:
63
- # Handle conversation resume
64
- resume_turns = None
65
-
66
63
  if continue_ is not None:
67
64
  from tsugite.agent_runner.history_integration import get_latest_conversation
68
- from tsugite.history import get_history_dir, get_turns
65
+ from tsugite.history import get_history_dir
69
66
 
70
67
  if continue_ == "" or continue_.lower() == "latest":
71
68
  history_opts.continue_id = get_latest_conversation()
@@ -77,16 +74,10 @@ def chat(
77
74
  history_opts.continue_id = continue_
78
75
  console.print(f"[cyan]Resuming conversation: {history_opts.continue_id}[/cyan]")
79
76
 
80
- try:
81
- session_path = get_history_dir() / f"{history_opts.continue_id}.jsonl"
82
- resume_turns = get_turns(session_path)
83
- console.print(f"[cyan]Loaded {len(resume_turns)} previous turns[/cyan]")
84
- except FileNotFoundError:
77
+ session_path = get_history_dir() / f"{history_opts.continue_id}.jsonl"
78
+ if not session_path.exists():
85
79
  console.print(f"[red]Conversation not found: {history_opts.continue_id}[/red]")
86
80
  raise typer.Exit(1)
87
- except Exception as e:
88
- console.print(f"[red]Failed to load conversation: {e}[/red]")
89
- raise typer.Exit(1)
90
81
 
91
82
  agent_to_load = agent if agent else "default"
92
83
  _, primary_agent_path, _ = load_and_validate_agent(agent_to_load, console)
@@ -100,7 +91,6 @@ def chat(
100
91
  agent_path=primary_agent_path,
101
92
  exec_options=exec_opts,
102
93
  history_options=history_opts,
103
- resume_turns=resume_turns,
104
94
  path_context=path_context,
105
95
  workspace_attachments=workspace_attachments,
106
96
  )
@@ -156,15 +156,15 @@ def test_hello_custom():
156
156
 
157
157
  def _patch_root_pyproject(root_pyproject: Path, dist_name: str) -> None:
158
158
  text = root_pyproject.read_text()
159
- sources_line = f'{dist_name} = {{ workspace = true }}\n'
159
+ sources_line = f"{dist_name} = {{ workspace = true }}\n"
160
160
  dev_dep_line = f' "{dist_name}",\n'
161
161
 
162
162
  if dist_name in text:
163
163
  return # idempotent
164
164
 
165
165
  new_text = re.sub(
166
- r'(\[tool\.uv\.sources\][^\[]*?tsugite-cli = \{ workspace = true \}\n)',
167
- rf'\1{sources_line}',
166
+ r"(\[tool\.uv\.sources\][^\[]*?tsugite-cli = \{ workspace = true \}\n)",
167
+ rf"\1{sources_line}",
168
168
  text,
169
169
  count=1,
170
170
  )
@@ -172,8 +172,8 @@ def _patch_root_pyproject(root_pyproject: Path, dist_name: str) -> None:
172
172
  raise typer.BadParameter("Could not locate [tool.uv.sources] block in root pyproject.toml")
173
173
 
174
174
  new_text2 = re.sub(
175
- r'(\[dependency-groups\]\s*\ndev\s*=\s*\[(?:[^\]]|\n)*?)\n\]',
176
- rf'\1\n{dev_dep_line.rstrip()}\n]',
175
+ r"(\[dependency-groups\]\s*\ndev\s*=\s*\[(?:[^\]]|\n)*?)\n\]",
176
+ rf"\1\n{dev_dep_line.rstrip()}\n]",
177
177
  new_text,
178
178
  count=1,
179
179
  )
@@ -185,7 +185,9 @@ def _patch_root_pyproject(root_pyproject: Path, dist_name: str) -> None:
185
185
 
186
186
  @plugin_app.command("create")
187
187
  def plugin_create(
188
- name: str = typer.Argument(help="Plugin short name (lowercase, alphanumeric + hyphens). E.g. 'discord' creates tsugite-discord."),
188
+ name: str = typer.Argument(
189
+ help="Plugin short name (lowercase, alphanumeric + hyphens). E.g. 'discord' creates tsugite-discord."
190
+ ),
189
191
  dry_run: bool = typer.Option(False, "--dry-run", help="Print planned changes without writing"),
190
192
  ):
191
193
  """Scaffold a new workspace plugin under plugins/tsugite-<name>/."""
@@ -1,6 +1,5 @@
1
1
  """CLI run command - execute agents."""
2
2
 
3
- import sys
4
3
  from pathlib import Path
5
4
  from typing import Any, Dict, List, Optional, Tuple
6
5
 
@@ -97,16 +96,18 @@ def _resolve_ui_mode(ui_mode: Optional[str], ui_opts: UIOptions, console: Consol
97
96
  console.print("[red]Error: --ui cannot be used with --plain or --headless[/red]")
98
97
  raise typer.Exit(1)
99
98
 
100
- ui_modes = {"plain", "headless"}
99
+ ui_modes = {"plain", "headless", "live"}
101
100
  ui_lower = ui_mode.lower()
102
101
  if ui_lower not in ui_modes:
103
- console.print(f"[red]Error: Invalid UI mode '{ui_mode}'. Choose from: {', '.join(ui_modes)}[/red]")
102
+ console.print(f"[red]Error: Invalid UI mode '{ui_mode}'. Choose from: {', '.join(sorted(ui_modes))}[/red]")
104
103
  raise typer.Exit(1)
105
104
 
106
105
  if ui_lower == "plain":
107
106
  ui_opts.plain = True
108
107
  elif ui_lower == "headless":
109
108
  ui_opts.headless = True
109
+ elif ui_lower == "live":
110
+ ui_opts.live = True
110
111
 
111
112
  return ui_opts
112
113
 
@@ -169,7 +170,14 @@ def _execute_agent_with_ui(
169
170
  use_plain_output: bool,
170
171
  console: Console,
171
172
  ):
172
- """Execute agent with appropriate UI mode."""
173
+ """Execute agent with appropriate UI mode.
174
+
175
+ Dispatch order:
176
+ 1. --headless / --final-only -> custom_agent_ui with silent progress
177
+ 2. --ui live (on a real TTY) -> three-region LiveUIHandler
178
+ 3. fall-through default -> plain logger (covers no flags, --plain, piped/NO_COLOR,
179
+ and --ui live degraded to plain by should_use_plain_output)
180
+ """
173
181
  from tsugite.ui import create_plain_logger, custom_agent_ui
174
182
 
175
183
  if ui_opts.headless or ui_opts.final_only:
@@ -188,22 +196,16 @@ def _execute_agent_with_ui(
188
196
  executor_kwargs["custom_logger"] = custom_logger
189
197
  return executor(**executor_kwargs)
190
198
 
191
- if use_plain_output:
192
- custom_logger = create_plain_logger()
193
- with custom_logger.ui_handler.progress_context():
199
+ if ui_opts.live and not use_plain_output:
200
+ from tsugite.ui import create_live_logger
201
+
202
+ custom_logger = create_live_logger(show_reasoning=ui_opts.show_reasoning)
203
+ with custom_logger.ui_handler.live_context():
194
204
  executor_kwargs["custom_logger"] = custom_logger
195
205
  return executor(**executor_kwargs)
196
206
 
197
- default_console = Console(file=sys.stderr, force_terminal=True, no_color=ui_opts.no_color)
198
- with custom_agent_ui(
199
- console=default_console,
200
- show_code=not ui_opts.non_interactive,
201
- show_observations=not ui_opts.non_interactive,
202
- show_progress=not ui_opts.no_color,
203
- show_llm_messages=ui_opts.show_reasoning,
204
- show_panels=False,
205
- show_debug_messages=ui_opts.verbose,
206
- ) as custom_logger:
207
+ custom_logger = create_plain_logger(show_reasoning=ui_opts.show_reasoning)
208
+ with custom_logger.ui_handler.progress_context():
207
209
  executor_kwargs["custom_logger"] = custom_logger
208
210
  return executor(**executor_kwargs)
209
211
 
@@ -246,7 +248,11 @@ def run(
246
248
  ),
247
249
  root: Optional[str] = typer.Option(None, "--root", help="Working directory"),
248
250
  model: Optional[str] = typer.Option(None, "--model", "-m", help="Override agent model"),
249
- ui: Optional[str] = typer.Option(None, "--ui", help="UI mode: plain, headless, or live (default: minimal)"),
251
+ ui: Optional[str] = typer.Option(
252
+ None,
253
+ "--ui",
254
+ help="UI mode: plain, headless, or live. Default: plain (auto-degrades when piped or NO_COLOR is set)",
255
+ ),
250
256
  non_interactive: bool = typer.Option(False, "--non-interactive", help="Run without interactive prompts"),
251
257
  history_dir: Optional[str] = typer.Option(None, "--history-dir", help="Directory to store history files"),
252
258
  no_color: bool = typer.Option(False, "--no-color", help="Disable ANSI colors"),
@@ -407,8 +413,8 @@ def run(
407
413
  import os
408
414
 
409
415
  if subagent_mode:
410
- if ui_opts.plain or ui_opts.headless:
411
- console.print("[red]Error: --subagent-mode cannot be combined with --plain or --headless[/red]")
416
+ if ui_opts.plain or ui_opts.headless or ui_opts.live:
417
+ console.print("[red]Error: --subagent-mode cannot be combined with --plain, --headless, or --ui live[/red]")
412
418
  raise typer.Exit(1)
413
419
 
414
420
  ui_opts.non_interactive = True
@@ -142,7 +142,6 @@ def onboard_workspace(
142
142
  agent_path=agent_path,
143
143
  exec_options=exec_opts,
144
144
  history_options=history_opts,
145
- resume_turns=None,
146
145
  path_context=path_context,
147
146
  workspace_attachments=workspace_attachments,
148
147
  )
@@ -8,18 +8,47 @@ injected `state` object persist across turns.
8
8
  """
9
9
 
10
10
  import ast
11
+ import contextlib
11
12
  import io
13
+ import os
12
14
  import pprint
13
15
  import sys
16
+ import threading
14
17
  from dataclasses import dataclass, field
15
18
  from pathlib import Path
16
- from typing import Any, Callable, Dict, List, Optional
19
+ from typing import Any, Callable, Dict, Iterator, List, Optional
17
20
 
18
21
  from tsugite.core.state import load_state, save_state
19
22
  from tsugite.exceptions import StateSerializationError
20
23
 
21
24
  PPRINT_WIDTH = 100
22
25
 
26
+ # CWD is process-global. Concurrent execute() calls in different threads must
27
+ # serialize their chdir+exec+restore window; the lock is held across the whole
28
+ # user-code exec(), so heavy parallel LocalExecutor turns serialize end-to-end.
29
+ # Accepted trade-off: silent disagreement between raw `os.getcwd()` and tool
30
+ # path resolution is worse than the serialization. `spawn_session` returns
31
+ # immediately so a parent holding the lock doesn't starve a nested child.
32
+ _chdir_lock = threading.Lock()
33
+
34
+
35
+ @contextlib.contextmanager
36
+ def _locked_chdir(target: Optional[Path]) -> Iterator[None]:
37
+ """chdir to target under `_chdir_lock`; restore CWD and release on exit.
38
+
39
+ No-op when target is None.
40
+ """
41
+ if target is None:
42
+ yield
43
+ return
44
+ with _chdir_lock:
45
+ previous = os.getcwd()
46
+ os.chdir(str(target))
47
+ try:
48
+ yield
49
+ finally:
50
+ os.chdir(previous)
51
+
23
52
 
24
53
  def _looks_html_escaped(source: str) -> bool:
25
54
  """True if `source` is HTML-entity-escaped XML (observation content leaked into exec)."""
@@ -203,6 +232,11 @@ class LocalExecutor:
203
232
  self._sticky_injections: Dict[str, Any] = {}
204
233
  self._content_blocks: Dict[str, str] = {}
205
234
 
235
+ target = workspace_dir
236
+ if target is None and path_context is not None:
237
+ target = path_context.effective_cwd or path_context.workspace_dir
238
+ self._chdir_target_resolved: Optional[Path] = Path(target).resolve() if target is not None else None
239
+
206
240
  self.namespace: Dict[str, Any] = self._build_turn_namespace()
207
241
 
208
242
  def _build_turn_namespace(self) -> Dict[str, Any]:
@@ -317,11 +351,24 @@ class LocalExecutor:
317
351
  return pprint.pformat(value, width=PPRINT_WIDTH, compact=False)
318
352
  return repr(value)
319
353
 
354
+ def _workspace_chdir_target(self) -> Optional[Path]:
355
+ """Target for the per-execute chdir, or None if CWD already matches (or no workspace)."""
356
+ target = self._chdir_target_resolved
357
+ if target is None:
358
+ return None
359
+ try:
360
+ if target == Path.cwd().resolve():
361
+ return None
362
+ except FileNotFoundError:
363
+ pass
364
+ return target
365
+
320
366
  async def execute(self, code: str) -> ExecutionResult:
321
367
  """Execute code using exec().
322
368
 
323
369
  Automatically displays the value of the last expression (REPL-like behavior).
324
- CWD is managed at CLI level - no directory changes here.
370
+ When a workspace is bound, chdir to it under a process-wide lock so raw
371
+ Python file APIs (os.getcwd, open, Path.cwd) resolve against the workspace.
325
372
 
326
373
  Args:
327
374
  code: Python code to execute
@@ -367,19 +414,20 @@ class LocalExecutor:
367
414
  sys.stdout = stdout_capture
368
415
  sys.stderr = stderr_capture
369
416
 
370
- setup_code, last_expr = self._split_code_for_last_expr(code)
417
+ with _locked_chdir(self._workspace_chdir_target()):
418
+ setup_code, last_expr = self._split_code_for_last_expr(code)
371
419
 
372
- if last_expr:
373
- if setup_code.strip():
374
- exec(setup_code, self.namespace)
420
+ if last_expr:
421
+ if setup_code.strip():
422
+ exec(setup_code, self.namespace)
375
423
 
376
- result = eval(last_expr, self.namespace)
424
+ result = eval(last_expr, self.namespace)
377
425
 
378
- if result is not None:
379
- formatted = self._format_value(result)
380
- print(formatted)
381
- else:
382
- exec(code, self.namespace)
426
+ if result is not None:
427
+ formatted = self._format_value(result)
428
+ print(formatted)
429
+ else:
430
+ exec(code, self.namespace)
383
431
 
384
432
  except Exception as e:
385
433
  exec_error = f"{type(e).__name__}: {str(e)}"