tsugite-cli 0.12.2__tar.gz → 0.13.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (402) hide show
  1. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/.github/copilot-instructions.md +3 -5
  2. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/AGENTS.md +108 -15
  3. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/PKG-INFO +4 -6
  4. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/README.md +3 -4
  5. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/docs/agents.md +70 -2
  6. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/pyproject.toml +1 -2
  7. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/scripts/update_model_registry.py +11 -0
  8. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/README.md +12 -16
  9. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/conftest.py +39 -36
  10. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/core/test_agent.py +148 -73
  11. tsugite_cli-0.13.0/tests/core/test_agent_event_loop.py +310 -0
  12. tsugite_cli-0.13.0/tests/core/test_agent_parser_triple_quotes.py +70 -0
  13. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/core/test_agent_ui_events.py +91 -27
  14. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/core/test_content_blocks.py +12 -12
  15. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/core/test_executor.py +133 -25
  16. tsugite_cli-0.13.0/tests/core/test_executor_escape_tripwire.py +35 -0
  17. tsugite_cli-0.13.0/tests/core/test_state.py +85 -0
  18. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/core/test_subprocess_executor.py +40 -11
  19. tsugite_cli-0.13.0/tests/daemon/test_chat_reentrancy.py +399 -0
  20. tsugite_cli-0.13.0/tests/daemon/test_compaction_command.py +247 -0
  21. tsugite_cli-0.13.0/tests/daemon/test_event_broadcast_scope.py +55 -0
  22. tsugite_cli-0.13.0/tests/daemon/test_final_answer_delivery.py +75 -0
  23. tsugite_cli-0.13.0/tests/daemon/test_http_adapter.py +797 -0
  24. tsugite_cli-0.13.0/tests/daemon/test_message_context_topic.py +207 -0
  25. tsugite_cli-0.13.0/tests/daemon/test_prompt_too_long_retry.py +144 -0
  26. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/daemon/test_session_metadata_api.py +28 -0
  27. tsugite_cli-0.13.0/tests/daemon/test_session_pinning_api.py +284 -0
  28. tsugite_cli-0.13.0/tests/daemon/test_session_store_pinning.py +194 -0
  29. tsugite_cli-0.13.0/tests/daemon/test_skill_trigger_loading.py +154 -0
  30. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/e2e/test_history_code_rendering.py +103 -7
  31. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/e2e/test_markdown_rendering.py +12 -31
  32. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/e2e/test_message_actions.py +26 -33
  33. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/e2e/test_scroll_behavior.py +6 -12
  34. tsugite_cli-0.13.0/tests/integration/test_concurrent_workspace_cwd.py +88 -0
  35. tsugite_cli-0.13.0/tests/integration/test_nested_session_spawn.py +103 -0
  36. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/smoke_test.sh +3 -3
  37. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_agent_inheritance.py +54 -20
  38. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_agent_sessions.py +200 -0
  39. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_agent_skills.py +10 -30
  40. tsugite_cli-0.13.0/tests/test_anthropic_extended_thinking.py +198 -0
  41. tsugite_cli-0.13.0/tests/test_attachments.py +1062 -0
  42. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_background_tasks.py +103 -0
  43. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_builtin_agent_paths.py +1 -1
  44. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_builtin_agents.py +1 -1
  45. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_claude_code_attachments.py +20 -0
  46. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_claude_code_provider.py +216 -0
  47. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_cli.py +47 -6
  48. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_cli_rendering.py +3 -4
  49. tsugite_cli-0.13.0/tests/test_cli_skills.py +85 -0
  50. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_cli_subcommands.py +0 -30
  51. tsugite_cli-0.13.0/tests/test_compaction_context_preservation.py +239 -0
  52. tsugite_cli-0.13.0/tests/test_compaction_event.py +113 -0
  53. tsugite_cli-0.13.0/tests/test_compaction_progress.py +162 -0
  54. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_daemon_unified_sessions.py +11 -24
  55. tsugite_cli-0.13.0/tests/test_event_storage.py +153 -0
  56. tsugite_cli-0.13.0/tests/test_events_to_messages.py +156 -0
  57. tsugite_cli-0.13.0/tests/test_fs_tool_workspace.py +89 -0
  58. tsugite_cli-0.13.0/tests/test_history_migrate.py +274 -0
  59. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_hooks.py +18 -0
  60. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_http_tools.py +62 -16
  61. tsugite_cli-0.13.0/tests/test_interaction_backend_isolation.py +191 -0
  62. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_interactive_context.py +0 -1
  63. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_jsonl_ui.py +41 -0
  64. tsugite_cli-0.13.0/tests/test_live_event_recording.py +130 -0
  65. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_md_agents_json.py +1 -3
  66. tsugite_cli-0.13.0/tests/test_memory_sanitize.py +290 -0
  67. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_model_registry.py +11 -0
  68. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_multistep_agents.py +102 -0
  69. tsugite_cli-0.13.0/tests/test_multistep_retry_side_effects.py +108 -0
  70. tsugite_cli-0.13.0/tests/test_post_compaction_counters.py +257 -0
  71. tsugite_cli-0.13.0/tests/test_provider_turn_heartbeat.py +142 -0
  72. tsugite_cli-0.13.0/tests/test_reasoning_models.py +127 -0
  73. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_renderer.py +59 -0
  74. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_rendering_scenarios.py +103 -0
  75. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_scheduler_history_injection.py +10 -15
  76. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_schema.py +4 -3
  77. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_session_metadata.py +34 -0
  78. tsugite_cli-0.13.0/tests/test_shell_tool_cwd.py +82 -0
  79. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_skill_discovery.py +172 -4
  80. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_skill_tools.py +103 -0
  81. tsugite_cli-0.13.0/tests/test_step_render_framework_flags.py +108 -0
  82. tsugite_cli-0.13.0/tests/test_unified_event_log.py +185 -0
  83. tsugite_cli-0.13.0/tests/test_user_agent.py +100 -0
  84. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/agent_inheritance.py +16 -20
  85. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/agent_preparation.py +323 -38
  86. tsugite_cli-0.13.0/tsugite/agent_runner/history_integration.py +245 -0
  87. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/agent_runner/models.py +1 -0
  88. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/agent_runner/runner.py +224 -78
  89. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/attachments/base.py +16 -1
  90. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/attachments/url.py +6 -5
  91. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/builtin_agents/default.md +42 -20
  92. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/builtin_agents/file_searcher.md +1 -1
  93. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/builtin_agents/onboard.md +1 -1
  94. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/builtin_skills/tsugite-agent-basics/SKILL.md +22 -6
  95. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/cli/__init__.py +2 -4
  96. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/cli/helpers.py +28 -2
  97. tsugite_cli-0.13.0/tsugite/cli/history.py +715 -0
  98. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/cli/render.py +6 -3
  99. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/cli/run.py +43 -21
  100. tsugite_cli-0.13.0/tsugite/cli/skills.py +77 -0
  101. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/config.py +1 -1
  102. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/core/agent.py +453 -366
  103. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/core/claude_code.py +26 -3
  104. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/core/executor.py +162 -94
  105. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/core/memory.py +3 -0
  106. tsugite_cli-0.13.0/tsugite/core/state.py +76 -0
  107. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/core/subprocess_executor.py +142 -110
  108. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/core/tools.py +0 -1
  109. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/adapters/base.py +284 -168
  110. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/adapters/http.py +425 -285
  111. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/adapters/scheduler_adapter.py +38 -31
  112. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/commands.py +7 -5
  113. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/compaction_scheduler.py +10 -2
  114. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/gateway.py +9 -2
  115. tsugite_cli-0.13.0/tsugite/daemon/memory.py +507 -0
  116. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/session_runner.py +29 -1
  117. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/session_store.py +328 -40
  118. tsugite_cli-0.13.0/tsugite/daemon/web/css/console.css +1704 -0
  119. tsugite_cli-0.13.0/tsugite/daemon/web/css/responsive.css +110 -0
  120. tsugite_cli-0.13.0/tsugite/daemon/web/css/styles.css +239 -0
  121. tsugite_cli-0.13.0/tsugite/daemon/web/css/theme.css +61 -0
  122. tsugite_cli-0.13.0/tsugite/daemon/web/index.html +1753 -0
  123. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/js/api.js +10 -2
  124. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/js/app.js +32 -0
  125. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/js/utils.js +20 -0
  126. tsugite_cli-0.13.0/tsugite/daemon/web/js/views/conversation/event_types.js +71 -0
  127. tsugite_cli-0.13.0/tsugite/daemon/web/js/views/conversation/history.js +412 -0
  128. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/js/views/conversation/input.js +1 -1
  129. tsugite_cli-0.13.0/tsugite/daemon/web/js/views/conversation/sessions.js +457 -0
  130. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/js/views/conversation/streaming.js +110 -47
  131. tsugite_cli-0.13.0/tsugite/daemon/web/js/views/conversations.js +644 -0
  132. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/js/views/file-editor.js +3 -0
  133. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/js/views/usage.js +2 -0
  134. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/manifest.json +1 -1
  135. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/events/__init__.py +2 -0
  136. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/events/base.py +3 -0
  137. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/events/events.py +10 -0
  138. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/exceptions.py +22 -0
  139. tsugite_cli-0.13.0/tsugite/history/__init__.py +24 -0
  140. tsugite_cli-0.13.0/tsugite/history/models.py +46 -0
  141. tsugite_cli-0.13.0/tsugite/history/reconstruction.py +136 -0
  142. tsugite_cli-0.13.0/tsugite/history/storage.py +224 -0
  143. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/hooks.py +20 -1
  144. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/md_agents.py +84 -7
  145. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/models.py +59 -6
  146. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/options.py +1 -3
  147. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/providers/anthropic.py +39 -14
  148. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/providers/base.py +2 -0
  149. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/providers/claude_code.py +18 -7
  150. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/providers/model_registry.py +5 -0
  151. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/providers/openai_compat.py +12 -8
  152. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/renderer.py +16 -0
  153. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/schemas/__init__.py +12 -9
  154. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/schemas/agent.schema.json +80 -24
  155. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/skill_discovery.py +92 -24
  156. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/tools/agents.py +27 -17
  157. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/tools/fs.py +8 -7
  158. tsugite_cli-0.13.0/tsugite/tools/history.py +240 -0
  159. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/tools/http.py +25 -16
  160. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/tools/interactive.py +8 -10
  161. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/tools/secrets.py +10 -0
  162. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/tools/shell.py +11 -2
  163. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/tools/skills.py +64 -2
  164. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/tools/tmux.py +4 -0
  165. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/ui/jsonl.py +23 -2
  166. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/ui/repl_commands.py +23 -21
  167. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/user_agent.py +28 -3
  168. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/utils.py +32 -1
  169. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/workspace/models.py +10 -0
  170. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/workspace/session.py +9 -9
  171. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/uv.lock +647 -882
  172. tsugite_cli-0.12.2/tests/daemon/test_http_adapter.py +0 -534
  173. tsugite_cli-0.12.2/tests/test_attachments.py +0 -448
  174. tsugite_cli-0.12.2/tests/test_cache_control.py +0 -181
  175. tsugite_cli-0.12.2/tests/test_continuation.py +0 -453
  176. tsugite_cli-0.12.2/tests/test_daemon_history_persistence.py +0 -514
  177. tsugite_cli-0.12.2/tests/test_daemon_memory.py +0 -481
  178. tsugite_cli-0.12.2/tests/test_daemon_session_isolation.py +0 -265
  179. tsugite_cli-0.12.2/tests/test_history.py +0 -414
  180. tsugite_cli-0.12.2/tests/test_history_integration.py +0 -715
  181. tsugite_cli-0.12.2/tests/test_history_models.py +0 -517
  182. tsugite_cli-0.12.2/tests/test_history_performance.py +0 -202
  183. tsugite_cli-0.12.2/tests/test_history_tools.py +0 -286
  184. tsugite_cli-0.12.2/tests/test_mcp_client.py +0 -378
  185. tsugite_cli-0.12.2/tests/test_mcp_server.py +0 -109
  186. tsugite_cli-0.12.2/tests/test_reasoning_models.py +0 -62
  187. tsugite_cli-0.12.2/tsugite/agent_runner/history_integration.py +0 -306
  188. tsugite_cli-0.12.2/tsugite/cli/history.py +0 -413
  189. tsugite_cli-0.12.2/tsugite/cli/mcp.py +0 -193
  190. tsugite_cli-0.12.2/tsugite/cli/serve.py +0 -41
  191. tsugite_cli-0.12.2/tsugite/daemon/memory.py +0 -333
  192. tsugite_cli-0.12.2/tsugite/daemon/web/css/responsive.css +0 -191
  193. tsugite_cli-0.12.2/tsugite/daemon/web/css/styles.css +0 -387
  194. tsugite_cli-0.12.2/tsugite/daemon/web/css/theme.css +0 -47
  195. tsugite_cli-0.12.2/tsugite/daemon/web/index.html +0 -1344
  196. tsugite_cli-0.12.2/tsugite/daemon/web/js/views/conversation/history.js +0 -184
  197. tsugite_cli-0.12.2/tsugite/daemon/web/js/views/conversation/sessions.js +0 -207
  198. tsugite_cli-0.12.2/tsugite/daemon/web/js/views/conversations.js +0 -345
  199. tsugite_cli-0.12.2/tsugite/history/__init__.py +0 -54
  200. tsugite_cli-0.12.2/tsugite/history/models.py +0 -147
  201. tsugite_cli-0.12.2/tsugite/history/reconstruction.py +0 -348
  202. tsugite_cli-0.12.2/tsugite/history/storage.py +0 -619
  203. tsugite_cli-0.12.2/tsugite/mcp_client.py +0 -221
  204. tsugite_cli-0.12.2/tsugite/mcp_config.py +0 -174
  205. tsugite_cli-0.12.2/tsugite/mcp_server.py +0 -144
  206. tsugite_cli-0.12.2/tsugite/tools/history.py +0 -190
  207. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/.github/workflows/ci.yml +0 -0
  208. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/.github/workflows/docker-publish.yml +0 -0
  209. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/.github/workflows/pypi-publish.yml +0 -0
  210. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/.gitignore +0 -0
  211. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/CLAUDE.md +0 -0
  212. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/LICENSE +0 -0
  213. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/docs/plugin-hooks.md +0 -0
  214. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/docs/secrets.md +0 -0
  215. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/docs/test_agents/prefresh_readme.md +0 -0
  216. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/docs/test_agents/template_test.md +0 -0
  217. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/docs/test_agents/test_agent.md +0 -0
  218. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/docs/test_agents/test_steps.md +0 -0
  219. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/mise.toml +0 -0
  220. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/scripts/backfill_usage.py +0 -0
  221. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/scripts/regenerate_schema.py +0 -0
  222. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/__init__.py +0 -0
  223. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/core/__init__.py +0 -0
  224. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/core/test_agent_context_tokens.py +0 -0
  225. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/core/test_memory.py +0 -0
  226. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/core/test_proxy.py +0 -0
  227. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/core/test_sandbox.py +0 -0
  228. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/core/test_tools.py +0 -0
  229. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/daemon/__init__.py +0 -0
  230. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/e2e/__init__.py +0 -0
  231. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/e2e/conftest.py +0 -0
  232. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/e2e/test_auth.py +0 -0
  233. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/e2e/test_chat.py +0 -0
  234. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/e2e/test_context_bar.py +0 -0
  235. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/e2e/test_draft_persistence.py +0 -0
  236. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/e2e/test_history.py +0 -0
  237. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/e2e/test_page_load.py +0 -0
  238. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/e2e/test_prompt_inspector.py +0 -0
  239. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/e2e/test_sessions.py +0 -0
  240. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/e2e/test_sidebar_redesign.py +0 -0
  241. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/e2e/test_sse_metadata.py +0 -0
  242. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/events/test_event_consolidation.py +0 -0
  243. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_agent_file_hot_loading.py +0 -0
  244. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_agent_parser.py +0 -0
  245. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_agent_utils.py +0 -0
  246. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_agents_tool.py +0 -0
  247. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_attachment_deduplication.py +0 -0
  248. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_auto_context_handler.py +0 -0
  249. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_auto_discovery.py +0 -0
  250. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_background_task_status.py +0 -0
  251. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_chat_cli.py +0 -0
  252. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_chat_error_handling.py +0 -0
  253. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_cli_arguments.py +0 -0
  254. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_completion_callbacks.py +0 -0
  255. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_config.py +0 -0
  256. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_custom_shell_tools.py +0 -0
  257. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_custom_ui.py +0 -0
  258. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_daemon_auth.py +0 -0
  259. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_daemon_compaction_scheduler.py +0 -0
  260. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_daemon_config.py +0 -0
  261. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_daemon_push.py +0 -0
  262. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_daemon_scheduler.py +0 -0
  263. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_discord_progress.py +0 -0
  264. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_error_display.py +0 -0
  265. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_file_references.py +0 -0
  266. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_file_tools.py +0 -0
  267. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_interaction_backends.py +0 -0
  268. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_interactive_tool.py +0 -0
  269. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_list_agents_tool.py +0 -0
  270. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_models.py +0 -0
  271. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_multi_agent.py +0 -0
  272. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_orchestrator_heartbeat.py +0 -0
  273. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_plugins.py +0 -0
  274. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_prompt_snapshot.py +0 -0
  275. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_provider_registry.py +0 -0
  276. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_repl_commands.py +0 -0
  277. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_repl_completer.py +0 -0
  278. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_repl_handler.py +0 -0
  279. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_retry_system.py +0 -0
  280. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_run_if.py +0 -0
  281. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_schedule_model_override.py +0 -0
  282. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_secret_access_event.py +0 -0
  283. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_secrets.py +0 -0
  284. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_security_phase1.py +0 -0
  285. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_send_message.py +0 -0
  286. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_session_orchestrator_tools.py +0 -0
  287. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_stdin.py +0 -0
  288. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_subagent_subprocess.py +0 -0
  289. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_tmux_tools.py +0 -0
  290. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_tool_directives.py +0 -0
  291. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_tool_registry.py +0 -0
  292. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_workspace_auto_continue.py +0 -0
  293. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_workspace_cwd.py +0 -0
  294. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tests/test_workspace_discovery.py +0 -0
  295. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/__init__.py +0 -0
  296. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/agent_runner/__init__.py +0 -0
  297. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/agent_runner/helpers.py +0 -0
  298. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/agent_runner/metrics.py +0 -0
  299. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/agent_runner/validation.py +0 -0
  300. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/agent_utils.py +0 -0
  301. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/attachments/__init__.py +0 -0
  302. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/attachments/auto_context.py +0 -0
  303. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/attachments/file.py +0 -0
  304. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/attachments/inline.py +0 -0
  305. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/attachments/storage.py +0 -0
  306. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/attachments/youtube.py +0 -0
  307. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/builtin_agents/.gitkeep +0 -0
  308. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/builtin_agents/code_searcher.md +0 -0
  309. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/builtin_skills/.gitkeep +0 -0
  310. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/builtin_skills/codebase-exploration/SKILL.md +0 -0
  311. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/builtin_skills/python-math/SKILL.md +0 -0
  312. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/builtin_skills/response-patterns/SKILL.md +0 -0
  313. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/builtin_skills/scheduling/SKILL.md +0 -0
  314. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/builtin_skills/skill-authoring/SKILL.md +0 -0
  315. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/builtin_skills/tsugite-jinja-reference/SKILL.md +0 -0
  316. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/builtin_skills/tsugite-skill-basics/SKILL.md +0 -0
  317. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/cache.py +0 -0
  318. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/cli/agents.py +0 -0
  319. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/cli/attachments.py +0 -0
  320. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/cli/cache.py +0 -0
  321. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/cli/chat.py +0 -0
  322. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/cli/config.py +0 -0
  323. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/cli/daemon.py +0 -0
  324. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/cli/init.py +0 -0
  325. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/cli/models.py +0 -0
  326. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/cli/plugins.py +0 -0
  327. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/cli/secrets.py +0 -0
  328. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/cli/tools.py +0 -0
  329. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/cli/usage.py +0 -0
  330. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/cli/validate.py +0 -0
  331. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/cli/workspace.py +0 -0
  332. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/console.py +0 -0
  333. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/constants.py +0 -0
  334. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/core/__init__.py +0 -0
  335. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/core/content_blocks.py +0 -0
  336. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/core/proxy.py +0 -0
  337. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/core/sandbox.py +0 -0
  338. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/__init__.py +0 -0
  339. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/adapters/__init__.py +0 -0
  340. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/adapters/discord.py +0 -0
  341. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/auth.py +0 -0
  342. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/config.py +0 -0
  343. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/push.py +0 -0
  344. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/scheduler.py +0 -0
  345. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/icons/icon-192.png +0 -0
  346. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/icons/icon-512-maskable.png +0 -0
  347. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/icons/icon-512.png +0 -0
  348. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/icons/screenshot-narrow.png +0 -0
  349. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/icons/screenshot-wide.png +0 -0
  350. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/js/vendor/marked.LICENSE.md +0 -0
  351. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/js/vendor/marked.esm.min.js +0 -0
  352. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/js/views/conversation/attachments.js +0 -0
  353. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/js/views/schedules.js +0 -0
  354. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/js/views/webhooks.js +0 -0
  355. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/js/views/workspace.js +0 -0
  356. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/web/sw.js +0 -0
  357. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/daemon/webhook_store.py +0 -0
  358. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/events/bus.py +0 -0
  359. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/events/helpers.py +0 -0
  360. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/interaction.py +0 -0
  361. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/plugins.py +0 -0
  362. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/providers/__init__.py +0 -0
  363. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/providers/model_cache.py +0 -0
  364. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/providers/ollama.py +0 -0
  365. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/providers/openrouter.py +0 -0
  366. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/secrets/__init__.py +0 -0
  367. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/secrets/backend.py +0 -0
  368. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/secrets/env.py +0 -0
  369. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/secrets/exec.py +0 -0
  370. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/secrets/file.py +0 -0
  371. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/secrets/masking.py +0 -0
  372. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/secrets/registry.py +0 -0
  373. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/secrets/sqlite.py +0 -0
  374. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/shell_tool_config.py +0 -0
  375. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/templates/AGENTS.md +0 -0
  376. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/templates/IDENTITY.md +0 -0
  377. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/templates/MEMORY.md +0 -0
  378. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/templates/USER.md +0 -0
  379. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/templates/personas/casual-technical.md +0 -0
  380. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/templates/personas/marvin.md +0 -0
  381. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/templates/personas/minimal.md +0 -0
  382. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/tools/__init__.py +0 -0
  383. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/tools/notify.py +0 -0
  384. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/tools/schedule.py +0 -0
  385. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/tools/scratchpad.py +0 -0
  386. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/tools/sessions.py +0 -0
  387. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/tools/shell_tools.py +0 -0
  388. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/tsugite.py +0 -0
  389. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/ui/__init__.py +0 -0
  390. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/ui/base.py +0 -0
  391. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/ui/chat.py +0 -0
  392. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/ui/helpers.py +0 -0
  393. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/ui/plain.py +0 -0
  394. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/ui/repl_chat.py +0 -0
  395. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/ui/repl_completer.py +0 -0
  396. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/ui/repl_handler.py +0 -0
  397. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/ui_context.py +0 -0
  398. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/usage/__init__.py +0 -0
  399. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/usage/store.py +0 -0
  400. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/workspace/__init__.py +0 -0
  401. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/workspace/context.py +0 -0
  402. {tsugite_cli-0.12.2 → tsugite_cli-0.13.0}/tsugite/workspace/templates.py +0 -0
@@ -4,16 +4,16 @@ Tsugite is a micro-agent CLI: agents are Markdown + YAML frontmatter, rendered v
4
4
 
5
5
  ### Architecture
6
6
 
7
- **Flow:** `Agent.md` (YAML + Jinja2) → `renderer.py` → `TsugiteAgent` → LiteLLM → Tools
7
+ **Flow:** `Agent.md` (YAML + Jinja2) → `renderer.py` → `TsugiteAgent` → Provider → Tools
8
8
 
9
9
  **Key Modules:**
10
- - `cli/__init__.py` - Typer CLI (run, chat, render, config, mcp, tools, agents, attachments, cache)
10
+ - `cli/__init__.py` - Typer CLI (run, chat, render, config, tools, agents, attachments, cache)
11
11
  - `md_agents.py` - Parse frontmatter + agent resolution + directives
12
12
  - `builtin_agents/` - File-based built-in agent definitions (default.md, chat-assistant.md)
13
13
  - `agent_inheritance.py` - Agent resolution pipeline + inheritance chain
14
14
  - `chat.py` - Chat session management with history
15
15
  - `agent_runner.py` - Execution orchestration, prefetch, tool wiring, multi-step
16
- - `core/agent.py` - LiteLLM agent loop + streaming
16
+ - `core/agent.py` - agent loop + streaming
17
17
  - `renderer.py` - Jinja2 rendering + helpers (now, today, slugify, env, file_exists, read_text, is_file, is_dir)
18
18
  - `tools/` - Tool registry + implementations
19
19
  - `shell_tool_config.py` + `tools/shell_tools.py` - Custom shell command wrappers
@@ -62,7 +62,6 @@ File-based agents distributed with the package in `tsugite/builtin_agents/` dire
62
62
  **Merge rules:**
63
63
  - Scalars (model, max_steps): Child overwrites
64
64
  - Lists (tools): Merge + deduplicate
65
- - Dicts (mcp_servers): Merge, child keys override
66
65
  - Strings (instructions): Concatenate with `\n\n`
67
66
 
68
67
  **Opt-out:** `extends: none` skips all inheritance.
@@ -236,7 +235,6 @@ assert tool is not None
236
235
 
237
236
  **Safety:**
238
237
  - Shell tool guard: Never relax dangerous patterns blocklist
239
- - MCP: Requires `--trust-mcp-code` flag
240
238
  - File ops: Use `Path.resolve()` to prevent traversal
241
239
 
242
240
  **Testing:** Test-first workflow. Add minimal failing test, run target, widen to full suite before merge.
@@ -54,7 +54,7 @@ Tsugite is an agentic CLI that executes AI agents defined as markdown files with
54
54
  1. **CLI Entry** (`tsugite/cli/__init__.py`)
55
55
  - Typer-based CLI with subcommands
56
56
  - Main commands: `run` (single-shot), `chat` (interactive), `render` (preview)
57
- - Additional: `daemon`, `workspace`, `init`, `validate`, `mcp`, `serve`, `agents`, `config`, `attachments`, `cache`, `tools`, `history`
57
+ - Additional: `daemon`, `workspace`, `init`, `validate`, `agents`, `config`, `attachments`, `cache`, `tools`, `history`
58
58
 
59
59
  2. **Agent Resolution & Inheritance** (`tsugite/agent_inheritance.py`)
60
60
  - Resolves agent names to file paths using search order:
@@ -102,22 +102,20 @@ Tsugite is an agentic CLI that executes AI agents defined as markdown files with
102
102
  - Integrates with history system for conversation continuity
103
103
 
104
104
  7. **LLM Agent Loop** (`tsugite/core/agent.py`)
105
- - `TsugiteAgent` - Custom agent using LiteLLM directly
105
+ - `TsugiteAgent` - Custom agent using tsugite's provider abstraction
106
106
  - Think-Code-Observation loop (max_turns iterations)
107
107
  - Supports reasoning models (o1, o3, Claude extended thinking)
108
108
  - Direct control over model parameters (temperature, reasoning_effort)
109
109
  - Code execution via `LocalExecutor`
110
110
 
111
111
  8. **Tool System** (`tsugite/tools/`)
112
- - Tool registry with built-in tools (fs, http, shell, tasks, agents, memory, skills, history, interactive)
113
- - Category system: `@fs`, `@http`, `@shell`, `@tasks`, `@agents`, `@memory`, `@skills`, `@history`, `@interactive`
114
- - Custom shell tools (config-based command wrappers)
115
- - MCP integration (`tsugite/mcp_client.py`)
116
- - Tool expansion supports globs (`*_search`) and exclusions (`-delete_file`)
112
+ - Tool registry with built-in tools (fs, http, shell, agents, skills, history, interactive) plus optional categories (notify, schedule, scratchpad, sessions, secrets, tmux — gated by `_OPTIONAL_CATEGORIES` in `tools/__init__.py`)
113
+ - Custom shell tools (config-based command wrappers in `tools/shell_tools.py`)
114
+ - Tool expansion supports globs (`*_search`), categories (`@fs`), and exclusions (`-delete_file`)
117
115
 
118
116
  9. **Event System** (`tsugite/events/`)
119
117
  - Event-driven architecture for UI decoupling
120
- - 20 event types: execution, LLM, meta, progress, skills
118
+ - 28 event types: execution, LLM, meta, progress, skills
121
119
  - `EventBus` dispatches to multiple handlers
122
120
  - Handlers: Rich console, plain text, JSONL, chat, REPL
123
121
 
@@ -131,17 +129,16 @@ Tsugite is an agentic CLI that executes AI agents defined as markdown files with
131
129
  - XDG-compliant paths: `~/.config/tsugite/` or `$XDG_CONFIG_HOME/tsugite/`
132
130
  - JSON config with model aliases, default settings
133
131
  - Attachment storage (reusable context)
134
- - MCP server registration
135
132
  - Auto-context discovery (CLAUDE.md, AGENTS.md, CONTEXT.md)
136
133
 
137
134
  ### Key Data Structures
138
135
 
139
- **AgentConfig** (`md_agents.py:43`)
136
+ **AgentConfig** (`md_agents.py:36`)
140
137
  - Pydantic model for agent frontmatter
141
138
  - Validates all fields (name, model, tools, max_turns, etc.)
142
139
  - Schema exported to `tsugite/schemas/agent.schema.json`
143
140
 
144
- **PreparedAgent** (`agent_preparation.py:17`)
141
+ **PreparedAgent** (`agent_preparation.py:69`)
145
142
  - Dataclass containing everything for execution/display
146
143
  - Ensures `render` shows exactly what `run` executes
147
144
  - Contains: agent, config, system_message, user_message, tools, context
@@ -241,7 +238,7 @@ Tsugite supports vision (images), audio, and document understanding through the
241
238
  **How It Works:**
242
239
 
243
240
  1. **URL Attachments** (Images/Documents):
244
- - LiteLLM fetches the URL directly (no download overhead)
241
+ - The provider fetches the URL directly (no download overhead — support varies by provider and content type; see `tsugite/attachments/url.py`)
245
242
  - Example: `tsu run -f https://example.com/chart.png "Describe this chart"`
246
243
  - Automatically detected via HTTP HEAD request
247
244
 
@@ -286,6 +283,63 @@ tsu run -f image1.jpg -f image2.jpg "Compare these images"
286
283
  - YouTube handler: `tsugite/attachments/youtube.py` (fetches transcripts)
287
284
  - LLM formatting: `tsugite/core/agent.py:_format_attachment()`
288
285
 
286
+ #### Frontmatter Attachment Specs
287
+
288
+ The `attachments:` field accepts either plain string paths (legacy) or dict-form `AttachmentSpec` entries. Both forms can be mixed in the same list.
289
+
290
+ **String form** (existing): rendered through Jinja, then loaded as a single file. Prefix with `-` to remove a workspace default (e.g., `-MEMORY.md`).
291
+
292
+ ```yaml
293
+ attachments:
294
+ - MEMORY.md
295
+ - "{{ today() }}.md"
296
+ - -SKIP.md # remove a workspace default named SKIP.md
297
+ ```
298
+
299
+ **Dict form** (`AttachmentSpec`): adds `assign:`, `mode: index`, glob expansion, and other knobs.
300
+
301
+ ```yaml
302
+ attachments:
303
+ - path: MEMORY.md
304
+ assign: memory_content # bind file content as Jinja variable
305
+
306
+ - path: USER.md
307
+ assign: user_prefs
308
+ attach: false # bind variable but don't inject into prompt
309
+
310
+ - path: "memory/topics/*.md" # glob - one Attachment per matched file
311
+ assign: topic_files # bound as list[dict] of {path, content}
312
+
313
+ - path: "memory/topics/*.md"
314
+ mode: index # render path+heading list, not full content
315
+ name: topic_index # explicit attachment name (default derives from glob)
316
+ assign: topics # bound as list[dict] of {path, heading, size_bytes, mtime}
317
+ index_format: first_heading # path_only | first_line | first_heading | frontmatter
318
+ max_entries: 50 # cap, warns if exceeded
319
+ ```
320
+
321
+ **Variable shapes** (when `assign:` is set):
322
+
323
+ | Spec | Shape |
324
+ |---|---|
325
+ | `mode: full`, single concrete file | `str` (file content) |
326
+ | `mode: full`, glob | `list[dict]` of `{path, content}` (binaries skipped) |
327
+ | `mode: index` | `list[dict]` of `{path, heading, size_bytes, mtime}` |
328
+ | Single file, missing | `None` |
329
+ | Empty glob | `[]` |
330
+
331
+ **Constraints (validated at parse time):**
332
+ - `assign:` must be a valid Python identifier.
333
+ - Two specs can't share the same `assign` value.
334
+ - `attach: false` requires `assign:` (otherwise the attachment loads for nothing).
335
+ - `path:` cannot start with `-` (use string form for removal).
336
+
337
+ **Index mode rendering** wraps the formatted index in an existing `<attachment>` tag with a `mode="index"` attribute, e.g. `<attachment name="topic_index" mode="index">...</attachment>`. Agents are expected to call `read_file(path=...)` to fetch individual entries on demand.
338
+
339
+ **Collisions:** if an `assign:` name shadows a built-in or prefetch variable, the assignment wins and a warning is logged.
340
+
341
+ See `examples/attachment_assign_demo.md` and `examples/attachment_index_demo.md` for runnable references.
342
+
289
343
  ## Development Patterns
290
344
 
291
345
  ### Adding a New Built-in Agent
@@ -315,14 +369,31 @@ tsu run -f image1.jpg -f image2.jpg "Compare these images"
315
369
 
316
370
  1. Update `AgentConfig` Pydantic model (`md_agents.py`)
317
371
  2. Regenerate schema: `uv run python scripts/regenerate_schema.py`
318
- 3. Update documentation in CLAUDE.md (user guide section)
372
+ 3. Update relevant documentation in `AGENTS.md` and any user-facing doc under `docs/`
319
373
  4. Add validation tests in `tests/test_agent_parser.py`
320
374
 
321
375
  ## Testing Strategy
322
376
 
323
- - **TDD preferred**: Write tests first when building new features. Tests don't need to be elaborate — simple tests that verify the expected behavior are enough. This creates a fast feedback loop and catches integration issues early (e.g., contextvar propagation, tool registration) before manual testing.
377
+ ### TDD is the default
378
+
379
+ For any non-trivial bug fix or feature, write a failing test **first**. Exceptions: typo fixes, docs, pure refactors, and changes the user has explicitly scoped as exploratory.
380
+
381
+ The workflow:
382
+
383
+ 1. **Reproduce before fixing.** Write the smallest test that demonstrates the symptom as reported. Run it. If it passes on master, the hypothesis is wrong — do not write a fix. Investigate further, ask the user for more detail, or add logging.
384
+ 2. **Implement the fix.** Keep the change minimal. Do not refactor or add adjacent cleanup in the same diff.
385
+ 3. **Verify the test is load-bearing.** Temp-revert the production change and re-run. The test must go red again. If it still passes, the test isn't actually exercising the fix — rework it.
386
+ 4. **Cover adjacent cases before declaring done.** When touching a parser, protocol boundary, or state machine, enumerate at least two adjacent input shapes and add tests for them. Narrow tests let bugs sneak in through the cases you didn't think about.
387
+ 5. **Run the local regression suite.** At minimum, the test files next to the modules you changed. Faster than running everything, catches silent breakage of existing tests.
388
+ 6. **Honest uncertainty.** If a hypothesis doesn't reproduce, say so. Don't ship a "probably-this" fix with no failing test to anchor it — that's how placebo fixes get merged.
389
+
390
+ Reproducing tests don't need to be elaborate. A 10-line test that flips red→green is worth more than a 100-line one that nobody understands.
391
+
392
+ ### Test layers
393
+
324
394
  - **Unit tests**: Individual functions and classes
325
- - **Pipeline tests**: Mock only `TsugiteAgent`/`litellm.acompletion` but exercise the full pipeline (parsing → rendering → preparation → tool expansion → execution). Most tests in the suite are this style.
395
+ - **Pipeline tests**: Mock only `TsugiteAgent` / the provider's `acompletion` but exercise the full pipeline (parsing → rendering → preparation → tool expansion → execution). Most tests in the suite are this style.
396
+ - **Integration tests**: Live under `tests/integration/` (not collected by default — see `pyproject.toml` `norecursedirs`). Run explicitly with `uv run pytest tests/integration/`. Good for concurrency, cwd, and daemon-wiring tests that need real threading or a real workspace.
326
397
  - **Smoke tests**: `tests/smoke_test.sh` hits a real LLM API (requires `OPENAI_API_KEY`, not run in CI)
327
398
  - **Fixtures**: `conftest.py` provides shared test data
328
399
  - **Mocking**: Use `@pytest.fixture` for LLM responses
@@ -348,6 +419,28 @@ tsu run -f image1.jpg -f image2.jpg "Compare these images"
348
419
  6. **Test both sync and async paths**: Many tools support both execution modes
349
420
  7. **Don't embed prompts in adapters/code**: Use context variables + conditional blocks in `default.md` instead. Add new context vars in `_build_agent_context()` (base adapter) and default them in `agent_preparation.py`, then use `{% if var %}` in the agent template. This keeps all prompt content in one place and leverages the existing rendering pipeline.
350
421
 
422
+ ## Web UI
423
+
424
+ The daemon's web UI lives at `tsugite/daemon/web/`.
425
+
426
+ ### Stack
427
+
428
+ - **Alpine.js 3** loaded from CDN. No build step. No React. Don't port this to a framework - the design handoff explicitly said match visuals, not internal structure.
429
+ - **Starlette** serves `index.html` plus `/static/{css,js,icons}` and the `/api/*` routes (`tsugite/daemon/adapters/http.py`).
430
+ - CSS lives in three files. Read all three before adding rules: `theme.css` (Catppuccin tokens), `console.css` (Console redesign - the real stylesheet), `styles.css` (legacy modals/auth/forms; ~120 lines, pruned aggressively after the redesign).
431
+ - View modules under `js/views/`: `conversations.js` (orchestrator) + `conversation/{sessions,history,streaming,input,attachments,event_types}.js` mixins, plus `workspace.js`, `schedules.js`, `webhooks.js`, `usage.js`, `file-editor.js`. Shared helpers: `js/api.js` (REST + SSE), `js/utils.js` (markdown, formatters, toast).
432
+
433
+ ### Theme tokens
434
+
435
+ - Use **bare token names** - `var(--base)`, `var(--mantle)`, `var(--crust)`, `var(--surface0/1/2)`, `var(--text)`, `var(--lavender)`, `var(--blue)`, `var(--peach)`, etc. The legacy `--ctp-*` aliases were removed.
436
+ - **Visual depth is `crust < mantle < base`** (darker → lighter on Frappé/Mocha/Macchiato). For dark-on-dark panels, panels go `var(--mantle)` and inner cards `var(--surface0)` or `var(--crust)`. Don't put a brighter (`--bg`/`--base`) header over a `--mantle` panel - it inverts the depth on dark themes.
437
+ - Never hardcode hex colors. They won't theme-switch. Even in JS palette objects (e.g. `PI_COLORS` in `conversations.js`), use `var(--pink)` strings - they get embedded into `:style="background: ..."` and resolve through the CSS cascade.
438
+ - `<meta name="theme-color">` is synced to the active theme's `--crust` via an `Alpine.effect` in `app.js` so the PWA status bar / mobile chrome track the theme.
439
+
440
+ ### Verification
441
+
442
+ Browser verification on UI changes is mandatory. The smoketest recipe - daemon spinup, Playwright wiring, and the post-redesign DOM selectors - is captured in the agent's project memory file `project_daemon_ui_smoketest.md`. There's also a `project_webui_console_hotzones.md` memory listing the load-bearing assumptions in the new UI (pulse signal, mid-turn reload guards, theme depth, PWA resume, mobile back-buttons) that bit during the redesign and should be re-read before touching session lifecycle, theme tokens, or PWA behaviour.
443
+
351
444
  ## Code Review Policy
352
445
 
353
446
  - **Automatically run the `code-simplifier:code-simplifier` agent** (via the Task tool) after implementing any changes, without waiting for the user to ask. This catches duplication, unnecessary complexity, and keeps the codebase DRY.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tsugite-cli
3
- Version: 0.12.2
3
+ Version: 0.13.0
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
@@ -246,7 +246,6 @@ Requires-Dist: ddgs>=9.6.0
246
246
  Requires-Dist: html2text>=2020.1.16
247
247
  Requires-Dist: httpx>=0.25
248
248
  Requires-Dist: jinja2>=3.1
249
- Requires-Dist: mcp>=1.0
250
249
  Requires-Dist: nest-asyncio>=1.5.0
251
250
  Requires-Dist: pathspec>=0.11.0
252
251
  Requires-Dist: portalocker>=2.8.0
@@ -334,8 +333,8 @@ tsu run +default "summarize the files in this directory"
334
333
  # Run an agent file directly
335
334
  tsu run my-agent.md "do the thing"
336
335
 
337
- # Start the web UI
338
- tsu serve
336
+ # Start the daemon (web UI, Discord/Telegram bots)
337
+ tsu daemon
339
338
  ```
340
339
 
341
340
  ## Features
@@ -347,7 +346,6 @@ tsu serve
347
346
  - **Skills** directory-based knowledge modules (mostly) following the [agentskills.io](https://agentskills.io/) SKILL.md format
348
347
  - **Hooks** that fire shell commands on lifecycle events (post-tool, pre-message, pre/post-compact)
349
348
  - **Sandbox** (linux only) via bubblewrap with filesystem and network isolation
350
- - **MCP** integration for connecting to MCP servers
351
349
 
352
350
  ## Agents in more detail
353
351
 
@@ -395,7 +393,7 @@ All paths follow [XDG Base Directory](https://specifications.freedesktop.org/bas
395
393
 
396
394
  | Path | Default | Contents |
397
395
  |----------------------------------------|--------------------------------------|----------------------------------------------|
398
- | `$XDG_CONFIG_HOME/tsugite/` | `~/.config/tsugite/` | `config.json`, `mcp.json`, `daemon.yaml` |
396
+ | `$XDG_CONFIG_HOME/tsugite/` | `~/.config/tsugite/` | `config.json`, `daemon.yaml` |
399
397
  | `$XDG_DATA_HOME/tsugite/history/` | `~/.local/share/tsugite/history/` | Session history (JSONL per session) |
400
398
  | `$XDG_DATA_HOME/tsugite/daemon/` | `~/.local/share/tsugite/daemon/` | Daemon state |
401
399
  | `$XDG_DATA_HOME/tsugite/secrets/` | `~/.local/share/tsugite/secrets/` | Encrypted secrets (`secrets.db`) |
@@ -63,8 +63,8 @@ tsu run +default "summarize the files in this directory"
63
63
  # Run an agent file directly
64
64
  tsu run my-agent.md "do the thing"
65
65
 
66
- # Start the web UI
67
- tsu serve
66
+ # Start the daemon (web UI, Discord/Telegram bots)
67
+ tsu daemon
68
68
  ```
69
69
 
70
70
  ## Features
@@ -76,7 +76,6 @@ tsu serve
76
76
  - **Skills** directory-based knowledge modules (mostly) following the [agentskills.io](https://agentskills.io/) SKILL.md format
77
77
  - **Hooks** that fire shell commands on lifecycle events (post-tool, pre-message, pre/post-compact)
78
78
  - **Sandbox** (linux only) via bubblewrap with filesystem and network isolation
79
- - **MCP** integration for connecting to MCP servers
80
79
 
81
80
  ## Agents in more detail
82
81
 
@@ -124,7 +123,7 @@ All paths follow [XDG Base Directory](https://specifications.freedesktop.org/bas
124
123
 
125
124
  | Path | Default | Contents |
126
125
  |----------------------------------------|--------------------------------------|----------------------------------------------|
127
- | `$XDG_CONFIG_HOME/tsugite/` | `~/.config/tsugite/` | `config.json`, `mcp.json`, `daemon.yaml` |
126
+ | `$XDG_CONFIG_HOME/tsugite/` | `~/.config/tsugite/` | `config.json`, `daemon.yaml` |
128
127
  | `$XDG_DATA_HOME/tsugite/history/` | `~/.local/share/tsugite/history/` | Session history (JSONL per session) |
129
128
  | `$XDG_DATA_HOME/tsugite/daemon/` | `~/.local/share/tsugite/daemon/` | Daemon state |
130
129
  | `$XDG_DATA_HOME/tsugite/secrets/` | `~/.local/share/tsugite/secrets/` | Encrypted secrets (`secrets.db`) |
@@ -46,7 +46,6 @@ All fields are optional except `name`.
46
46
  | `auto_load_skills` | list | `[]` | Skills to load automatically at startup |
47
47
  | `skill_paths` | list | `[]` | Extra directories to search for skills |
48
48
  | `prefetch` | list | `[]` | Tools to run before execution, results available in templates |
49
- | `mcp_servers` | dict | `{}` | MCP server connections |
50
49
  | `custom_tools` | list | `[]` | Inline tool definitions |
51
50
  | `reasoning_effort` | str | none | For reasoning models: `low`, `medium`, `high` |
52
51
  | `disable_history` | bool | `false` | Don't save this agent's sessions |
@@ -186,7 +185,57 @@ attachments:
186
185
  - "{{ WORKSPACE_DIR }}/notes.md"
187
186
  ```
188
187
 
189
- Supports text files, PDFs, images, and URLs. Jinja2 works in attachment paths. All attachments are injected as context.
188
+ Supports text files, PDFs, images, and URLs. Jinja2 works in attachment paths. All attachments are injected as context. Prefix with `-` to remove a workspace default (e.g. `-MEMORY.md`).
189
+
190
+ ### Attachment specs (dict form)
191
+
192
+ Use the dict form to bind attachment content to a Jinja variable, render an on-demand index instead of full content, or expand globs:
193
+
194
+ ```yaml
195
+ attachments:
196
+ # Bind file content as a variable usable in body/instructions templates
197
+ - path: MEMORY.md
198
+ assign: memory_content
199
+
200
+ # Bind without injecting (the LLM doesn't see it; the template does)
201
+ - path: USER.md
202
+ assign: user_prefs
203
+ attach: false
204
+
205
+ # Glob - one attachment per matched file; assign binds list[dict] of {path, content}
206
+ - path: "notes/*.md"
207
+ assign: notes
208
+
209
+ # Index mode - emits a single <attachment mode="index"> with path+heading bullets,
210
+ # no full file content. Agents read individual entries via read_file().
211
+ - path: "memory/topics/*.md"
212
+ mode: index
213
+ name: topic_index # optional override; default derives from glob
214
+ assign: topics # list[dict] of {path, heading, size_bytes, mtime}
215
+ index_format: first_heading # path_only | first_line | first_heading | frontmatter
216
+ max_entries: 50
217
+ ```
218
+
219
+ Variable shapes when `assign:` is set:
220
+
221
+ | Spec | Variable shape |
222
+ |---|---|
223
+ | `mode: full`, single concrete file | `str` (file content) |
224
+ | `mode: full`, glob | `list[dict]` of `{path, content}` (binaries skipped) |
225
+ | `mode: index` | `list[dict]` of `{path, heading, size_bytes, mtime}` |
226
+ | Single file, missing | `None` |
227
+ | Empty glob | `[]` |
228
+
229
+ Validation rules (enforced at parse time):
230
+
231
+ - `assign:` must be a valid Python identifier.
232
+ - Two specs cannot share the same `assign:` value.
233
+ - `attach: false` requires `assign:`.
234
+ - `path:` cannot start with `-` (use string form for removal).
235
+
236
+ Collisions between an `assign:` name and a built-in/prefetch variable resolve in favor of the attachment binding, with a warning logged.
237
+
238
+ See `examples/attachment_assign_demo.md` and `examples/attachment_index_demo.md` for full working examples.
190
239
 
191
240
  ## Multi-step agents
192
241
 
@@ -227,6 +276,7 @@ The variable `findings` is available in Python.
227
276
  | `repeat_while` | str | Jinja2 expression, loop while truthy |
228
277
  | `repeat_until` | str | Jinja2 expression, loop until truthy |
229
278
  | `max_iterations` | int | Loop safety limit (default 10) |
279
+ | `agent` | str | Path to agent file; runs the step via `spawn_agent` instead of inline |
230
280
 
231
281
  ### Variable passing
232
282
 
@@ -235,6 +285,24 @@ When a step has `assign="var_name"`, the result is available in later steps:
235
285
  - In Jinja2 templates: `{{ var_name }}`
236
286
  - In Python code execution: just use `var_name` directly
237
287
 
288
+ ### Step context isolation
289
+
290
+ Each `tsu:step` already runs as an independent agent invocation: a fresh system prompt, no LLM-level conversation history from prior steps. The only thing that flows between steps is variables captured with `assign=`, substituted into later steps via Jinja.
291
+
292
+ If you need a step to run with a fully separate identity (its own model defaults, attachments, skills, instructions), use `agent="path/to/other.md"`. The step content (after Jinja rendering) becomes the spawned agent's prompt. The spawned agent does not see the parent's `step_context` — only the rendered prompt — so any data the spawned agent needs must already be substituted in.
293
+
294
+ ```markdown
295
+ <!-- tsu:step name="implement" assign="diff" -->
296
+ Write a fix for: {{ user_prompt }}. Return only the diff.
297
+
298
+ <!-- tsu:step name="review" agent="agents/code-reviewer.md" assign="verdict" -->
299
+ Review this diff:
300
+
301
+ {{ diff }}
302
+ ```
303
+
304
+ Path resolution follows the standard agent search order (workspace, project-local `agents/`, builtin agents, global config). Unresolvable `agent=` paths fail before any step runs, so a typo in step 2 won't waste step 1's work.
305
+
238
306
  ## Other directives
239
307
 
240
308
  ### tsu:tool
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tsugite-cli"
3
- version = "0.12.2"
3
+ version = "0.13.0"
4
4
  description = "Micro-agent runner for task automation using markdown definitions"
5
5
  authors = [{ name = "Justyn Shull" }]
6
6
  requires-python = ">=3.11"
@@ -13,7 +13,6 @@ dependencies = [
13
13
  "pyyaml>=6.0",
14
14
  "httpx>=0.25",
15
15
  "rich>=13.0",
16
- "mcp>=1.0",
17
16
  "ddgs>=9.6.0",
18
17
  "questionary>=2.1.1",
19
18
  "youtube-transcript-api>=0.6.0",
@@ -51,6 +51,13 @@ SKIP_PREFIXES = (
51
51
  # Reasoning model name patterns (for supports_reasoning flag)
52
52
  REASONING_PATTERNS = re.compile(r"^(o1|o3|o4)(-mini|-preview|-pro)?")
53
53
 
54
+ # Reasoning models that accept the reasoning_effort parameter.
55
+ # Excludes o1-mini (unsupported by OpenAI).
56
+ EFFORT_LEVELS_PATTERNS = re.compile(r"^(o1(-pro)?|o3(-mini|-pro)?|o4(-mini|-pro)?)(-\d{4}-\d{2}-\d{2})?$")
57
+
58
+ # Anthropic models that support extended thinking (Claude 4+ families).
59
+ ANTHROPIC_THINKING_PATTERNS = re.compile(r"^claude-(opus|sonnet|haiku)-4")
60
+
54
61
 
55
62
  def fetch_litellm_data() -> dict:
56
63
  print(f"Fetching {LITELLM_URL}...")
@@ -96,6 +103,10 @@ def entry_to_model_info(key: str, entry: dict, provider: str) -> str:
96
103
  parts.append("supports_vision=True")
97
104
  if reasoning:
98
105
  parts.append("supports_reasoning=True")
106
+ if EFFORT_LEVELS_PATTERNS.match(key):
107
+ parts.append('supported_effort_levels=["low", "medium", "high"]')
108
+ elif provider == "anthropic" and ANTHROPIC_THINKING_PATTERNS.match(key):
109
+ parts.append('supported_effort_levels=["low", "medium", "high", "max"]')
99
110
 
100
111
  return f"ModelInfo({', '.join(parts)})"
101
112
 
@@ -26,7 +26,7 @@ uv run pytest --cov=tsugite --cov-report=html
26
26
 
27
27
  ### Smoke Tests
28
28
 
29
- **Smoke tests are NOT run automatically** to avoid API costs. They test real LiteLLM integration with actual API calls.
29
+ **Smoke tests are NOT run automatically** to avoid API costs. They test real provider integration with actual API calls.
30
30
 
31
31
  #### Prerequisites
32
32
 
@@ -42,7 +42,7 @@ bash tests/smoke_test.sh
42
42
 
43
43
  #### What Smoke Tests Verify
44
44
 
45
- 1. **Real LiteLLM integration** - Tests actual API calls, not mocks
45
+ 1. **Real provider integration** - Tests actual API calls, not mocks
46
46
  2. **Async context detection** - Catches issues like missing asyncio imports
47
47
  3. **Tool integration** - Verifies tools work in real execution
48
48
  4. **New agents** - Tests recently added agents end-to-end
@@ -50,7 +50,7 @@ bash tests/smoke_test.sh
50
50
  #### When to Run Smoke Tests
51
51
 
52
52
  - Before releases
53
- - After upgrading LiteLLM
53
+ - After upgrading provider dependencies
54
54
  - After major refactors to agent execution
55
55
  - When debugging integration issues
56
56
  - After adding new agents
@@ -61,7 +61,7 @@ bash tests/smoke_test.sh
61
61
 
62
62
  - **`core/`** - Tests for core agent implementation (TsugiteAgent, executor, tools)
63
63
  - **`test_*.py`** - Feature-specific tests (CLI, rendering, parsing, etc.)
64
- - All unit tests mock LiteLLM to avoid API calls
64
+ - All unit tests mock the provider to avoid API calls
65
65
 
66
66
  ### Integration Tests
67
67
 
@@ -69,24 +69,20 @@ Currently implemented as smoke tests (see above).
69
69
 
70
70
  ## Writing Tests
71
71
 
72
- ### Mocking LiteLLM
72
+ ### Mocking the provider
73
73
 
74
- When writing unit tests that use `TsugiteAgent`:
74
+ When writing unit tests that use `TsugiteAgent`, use the `mock_provider` and
75
+ `mock_completion_response` fixtures from `tests/conftest.py`:
75
76
 
76
77
  ```python
77
- from unittest.mock import AsyncMock, patch
78
-
79
78
  @pytest.mark.asyncio
80
- async def test_agent_example(mock_litellm_response):
81
- with patch("tsugite.core.agent.litellm") as mock_litellm:
82
- mock_litellm.acompletion = AsyncMock(
83
- return_value=mock_litellm_response("test response")
84
- )
79
+ async def test_agent_example(mock_provider, mock_completion_response):
80
+ mock_provider.acompletion.return_value = mock_completion_response("test response")
85
81
 
86
- agent = TsugiteAgent(...)
87
- result = await agent.run("test task")
82
+ agent = TsugiteAgent(...)
83
+ result = await agent.run("test task")
88
84
 
89
- assert result == expected
85
+ assert result == expected
90
86
  ```
91
87
 
92
88
  ### Testing Agents
@@ -50,17 +50,46 @@ def clear_agent_context():
50
50
  This prevents test pollution where one test sets current_agent
51
51
  and affects another test running in the same worker.
52
52
  """
53
- from tsugite.agent_runner.helpers import clear_allowed_agents, clear_current_agent
53
+ from tsugite.agent_runner.helpers import clear_allowed_agents, clear_current_agent, set_allowed_secrets
54
54
 
55
- # Clear before test
56
- clear_current_agent()
57
- clear_allowed_agents()
55
+ def _reset():
56
+ clear_current_agent()
57
+ clear_allowed_agents()
58
+ set_allowed_secrets(None)
58
59
 
60
+ _reset()
59
61
  yield
62
+ _reset()
60
63
 
61
- # Clear after test
62
- clear_current_agent()
63
- clear_allowed_agents()
64
+
65
+ @pytest.fixture
66
+ def secret_backend(tmp_path):
67
+ """File-backed secrets rooted under tmp_path. Wires the backend, registers a
68
+ test agent, clears the masking registry, and yields the backend so callers
69
+ can populate via backend.set(name, value)."""
70
+ from tsugite.agent_runner.helpers import set_current_agent
71
+ from tsugite.secrets import set_backend
72
+ from tsugite.secrets.file import FileSecretBackend
73
+ from tsugite.secrets.registry import get_registry
74
+
75
+ backend = FileSecretBackend({"path": str(tmp_path / "secrets")})
76
+ set_backend(backend)
77
+ set_current_agent("test-agent")
78
+ get_registry().clear()
79
+ return backend
80
+
81
+
82
+ @pytest.fixture(autouse=True)
83
+ def reset_workspace_dir_cv():
84
+ """Clear the workspace ContextVar between tests so that state from one test
85
+ doesn't leak into the next (the CV persists across sync tests otherwise)."""
86
+ from tsugite.cli.helpers import _workspace_dir_cv, set_workspace_dir
87
+
88
+ token = set_workspace_dir(None)
89
+ try:
90
+ yield
91
+ finally:
92
+ _workspace_dir_cv.reset(token)
64
93
 
65
94
 
66
95
  @pytest.fixture
@@ -287,12 +316,12 @@ def spawn_agent_tool(reset_tool_registry, request):
287
316
  def interactive_tools(reset_tool_registry):
288
317
  """Register interactive tools for testing."""
289
318
  from tsugite.tools import tool
290
- from tsugite.tools.interactive import ask_user, ask_user_batch, final_answer, react_to_message, send_message
319
+ from tsugite.tools.interactive import ask_user, ask_user_batch, react_to_message, return_value, send_message
291
320
 
292
321
  # Re-register the tools after registry reset
293
322
  tool(ask_user)
294
323
  tool(ask_user_batch)
295
- tool(final_answer)
324
+ tool(return_value)
296
325
  tool(send_message)
297
326
  tool(react_to_message)
298
327
 
@@ -403,33 +432,7 @@ tools: {tools_str}"""
403
432
 
404
433
 
405
434
  @pytest.fixture
406
- def mcp_config_factory():
407
- """Factory for creating MCP server configurations."""
408
-
409
- def _create_config(server_type="stdio", name="test-server", **kwargs):
410
- """Create an MCP server config dict.
411
-
412
- Args:
413
- server_type: "stdio" or "http"
414
- name: Server name
415
- **kwargs: Additional config fields (command, args, env, url, etc)
416
- """
417
- if server_type == "stdio":
418
- return {
419
- "command": kwargs.get("command", "npx"),
420
- "args": kwargs.get("args", ["-y", "test-server"]),
421
- "env": kwargs.get("env", {}),
422
- }
423
- elif server_type == "http":
424
- return {"url": kwargs.get("url", "http://localhost:8000/mcp"), "type": "http"}
425
- else:
426
- raise ValueError(f"Unknown server type: {server_type}")
427
-
428
- return _create_config
429
-
430
-
431
- @pytest.fixture
432
- def mock_litellm_response():
435
+ def mock_completion_response():
433
436
  """Create a mock provider CompletionResponse for agent tests.
434
437
 
435
438
  This fixture provides a factory function that creates mock LLM responses