tsugite-cli 0.3.3__tar.gz → 0.8.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 (386) hide show
  1. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/.github/copilot-instructions.md +8 -12
  2. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/.gitignore +11 -1
  3. tsugite_cli-0.8.2/AGENTS.md +354 -0
  4. tsugite_cli-0.8.2/CLAUDE.md +1 -0
  5. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/PKG-INFO +112 -7
  6. tsugite_cli-0.8.2/README.md +161 -0
  7. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/pyproject.toml +20 -7
  8. tsugite_cli-0.8.2/scripts/regenerate_schema.py +33 -0
  9. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/conftest.py +162 -37
  10. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/core/test_agent.py +175 -122
  11. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/core/test_agent_ui_events.py +70 -142
  12. tsugite_cli-0.8.2/tests/core/test_content_blocks.py +340 -0
  13. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/core/test_executor.py +248 -1
  14. tsugite_cli-0.8.2/tests/core/test_proxy.py +165 -0
  15. tsugite_cli-0.8.2/tests/core/test_sandbox.py +171 -0
  16. tsugite_cli-0.8.2/tests/core/test_subprocess_executor.py +353 -0
  17. tsugite_cli-0.8.2/tests/daemon/__init__.py +0 -0
  18. tsugite_cli-0.8.2/tests/daemon/test_http_adapter.py +252 -0
  19. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/events/test_event_consolidation.py +27 -62
  20. tsugite_cli-0.8.2/tests/test_agent_file_hot_loading.py +279 -0
  21. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_agent_inheritance.py +22 -138
  22. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_agent_parser.py +8 -179
  23. tsugite_cli-0.8.2/tests/test_agent_sessions.py +312 -0
  24. tsugite_cli-0.8.2/tests/test_agent_skills.py +610 -0
  25. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_agent_utils.py +27 -53
  26. tsugite_cli-0.8.2/tests/test_attachment_deduplication.py +281 -0
  27. tsugite_cli-0.8.2/tests/test_attachments.py +448 -0
  28. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_auto_context_handler.py +44 -46
  29. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_auto_discovery.py +10 -18
  30. tsugite_cli-0.8.2/tests/test_background_task_status.py +187 -0
  31. tsugite_cli-0.8.2/tests/test_background_tasks.py +529 -0
  32. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_builtin_agent_paths.py +5 -9
  33. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_builtin_agents.py +22 -89
  34. tsugite_cli-0.8.2/tests/test_cache_control.py +184 -0
  35. tsugite_cli-0.8.2/tests/test_chat_error_handling.py +65 -0
  36. tsugite_cli-0.8.2/tests/test_claude_code_attachments.py +245 -0
  37. tsugite_cli-0.8.2/tests/test_claude_code_provider.py +609 -0
  38. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_cli.py +102 -31
  39. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_cli_rendering.py +51 -28
  40. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_cli_subcommands.py +3 -19
  41. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_config.py +29 -13
  42. tsugite_cli-0.8.2/tests/test_continuation.py +453 -0
  43. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_custom_ui.py +0 -37
  44. tsugite_cli-0.8.2/tests/test_daemon_compaction_scheduler.py +208 -0
  45. tsugite_cli-0.8.2/tests/test_daemon_config.py +133 -0
  46. tsugite_cli-0.8.2/tests/test_daemon_history_persistence.py +514 -0
  47. tsugite_cli-0.8.2/tests/test_daemon_memory.py +465 -0
  48. tsugite_cli-0.8.2/tests/test_daemon_push.py +260 -0
  49. tsugite_cli-0.8.2/tests/test_daemon_scheduler.py +625 -0
  50. tsugite_cli-0.8.2/tests/test_daemon_session_isolation.py +265 -0
  51. tsugite_cli-0.8.2/tests/test_daemon_unified_sessions.py +350 -0
  52. tsugite_cli-0.8.2/tests/test_discord_progress.py +302 -0
  53. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_file_references.py +65 -48
  54. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_file_tools.py +216 -44
  55. tsugite_cli-0.8.2/tests/test_history.py +389 -0
  56. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_history_integration.py +140 -96
  57. tsugite_cli-0.8.2/tests/test_history_models.py +492 -0
  58. tsugite_cli-0.8.2/tests/test_history_performance.py +202 -0
  59. tsugite_cli-0.8.2/tests/test_history_tools.py +267 -0
  60. tsugite_cli-0.8.2/tests/test_hooks.py +951 -0
  61. tsugite_cli-0.8.2/tests/test_http_tools.py +223 -0
  62. tsugite_cli-0.8.2/tests/test_interaction_backends.py +246 -0
  63. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_interactive_context.py +98 -61
  64. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_jsonl_ui.py +2 -21
  65. tsugite_cli-0.8.2/tests/test_kvstore.py +120 -0
  66. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_list_agents_tool.py +23 -43
  67. tsugite_cli-0.8.2/tests/test_mcp_server.py +109 -0
  68. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_models.py +3 -3
  69. tsugite_cli-0.8.2/tests/test_multi_agent.py +162 -0
  70. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_multistep_agents.py +58 -383
  71. tsugite_cli-0.8.2/tests/test_orchestrator_heartbeat.py +228 -0
  72. tsugite_cli-0.8.2/tests/test_plugins.py +174 -0
  73. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_rendering_scenarios.py +3 -3
  74. tsugite_cli-0.8.2/tests/test_repl_commands.py +274 -0
  75. tsugite_cli-0.8.2/tests/test_repl_completer.py +105 -0
  76. tsugite_cli-0.8.2/tests/test_repl_handler.py +187 -0
  77. tsugite_cli-0.8.2/tests/test_run_if.py +92 -0
  78. tsugite_cli-0.8.2/tests/test_schedule_model_override.py +109 -0
  79. tsugite_cli-0.8.2/tests/test_scheduler_history_injection.py +172 -0
  80. tsugite_cli-0.8.2/tests/test_schema.py +206 -0
  81. tsugite_cli-0.8.2/tests/test_security_phase1.py +319 -0
  82. tsugite_cli-0.8.2/tests/test_send_message.py +58 -0
  83. tsugite_cli-0.8.2/tests/test_skill_discovery.py +403 -0
  84. tsugite_cli-0.8.2/tests/test_skill_tools.py +344 -0
  85. tsugite_cli-0.8.2/tests/test_stdin.py +204 -0
  86. tsugite_cli-0.8.2/tests/test_tmux_tools.py +397 -0
  87. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_tool_directives.py +5 -5
  88. tsugite_cli-0.8.2/tests/test_workspace_auto_continue.py +74 -0
  89. tsugite_cli-0.8.2/tests/test_workspace_cwd.py +370 -0
  90. tsugite_cli-0.8.2/tests/test_workspace_discovery.py +120 -0
  91. tsugite_cli-0.8.2/tsugite/__init__.py +19 -0
  92. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/agent_inheritance.py +45 -68
  93. tsugite_cli-0.8.2/tsugite/agent_preparation.py +413 -0
  94. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/agent_runner/__init__.py +8 -2
  95. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/agent_runner/helpers.py +37 -1
  96. tsugite_cli-0.8.2/tsugite/agent_runner/history_integration.py +270 -0
  97. tsugite_cli-0.8.2/tsugite/agent_runner/models.py +52 -0
  98. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/agent_runner/runner.py +426 -574
  99. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/agent_runner/validation.py +3 -4
  100. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/attachments/__init__.py +3 -3
  101. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/attachments/auto_context.py +42 -12
  102. tsugite_cli-0.8.2/tsugite/attachments/base.py +65 -0
  103. tsugite_cli-0.8.2/tsugite/attachments/file.py +151 -0
  104. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/attachments/storage.py +1 -1
  105. tsugite_cli-0.8.2/tsugite/attachments/url.py +147 -0
  106. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/attachments/youtube.py +12 -5
  107. tsugite_cli-0.8.2/tsugite/builtin_agents/.gitkeep +0 -0
  108. tsugite_cli-0.8.2/tsugite/builtin_agents/code_searcher.md +54 -0
  109. tsugite_cli-0.8.2/tsugite/builtin_agents/default.md +230 -0
  110. tsugite_cli-0.8.2/tsugite/builtin_agents/file_searcher.md +201 -0
  111. tsugite_cli-0.8.2/tsugite/builtin_agents/onboard.md +53 -0
  112. tsugite_cli-0.8.2/tsugite/builtin_skills/.gitkeep +0 -0
  113. tsugite_cli-0.8.2/tsugite/builtin_skills/codebase_exploration.md +650 -0
  114. tsugite_cli-0.8.2/tsugite/builtin_skills/python_math.md +202 -0
  115. tsugite_cli-0.8.2/tsugite/builtin_skills/response_patterns.md +95 -0
  116. tsugite_cli-0.8.2/tsugite/builtin_skills/scheduling.md +268 -0
  117. tsugite_cli-0.8.2/tsugite/builtin_skills/skill_authoring.md +342 -0
  118. tsugite_cli-0.8.2/tsugite/builtin_skills/tsugite_agent_basics.md +469 -0
  119. tsugite_cli-0.8.2/tsugite/builtin_skills/tsugite_jinja_reference.md +439 -0
  120. tsugite_cli-0.8.2/tsugite/builtin_skills/tsugite_skill_basics.md +574 -0
  121. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/cache.py +169 -3
  122. tsugite_cli-0.8.2/tsugite/cli/__init__.py +66 -0
  123. tsugite_cli-0.8.2/tsugite/cli/chat.py +101 -0
  124. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/cli/config.py +34 -60
  125. tsugite_cli-0.8.2/tsugite/cli/daemon.py +681 -0
  126. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/cli/helpers.py +152 -49
  127. tsugite_cli-0.8.2/tsugite/cli/history.py +205 -0
  128. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/cli/init.py +40 -59
  129. tsugite_cli-0.8.2/tsugite/cli/plugins.py +77 -0
  130. tsugite_cli-0.8.2/tsugite/cli/render.py +213 -0
  131. tsugite_cli-0.8.2/tsugite/cli/run.py +702 -0
  132. tsugite_cli-0.8.2/tsugite/cli/serve.py +41 -0
  133. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/cli/tools.py +4 -2
  134. tsugite_cli-0.8.2/tsugite/cli/validate.py +144 -0
  135. tsugite_cli-0.8.2/tsugite/cli/workspace.py +273 -0
  136. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/config.py +102 -66
  137. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/console.py +0 -5
  138. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/core/__init__.py +1 -2
  139. tsugite_cli-0.8.2/tsugite/core/agent.py +1294 -0
  140. tsugite_cli-0.8.2/tsugite/core/claude_code.py +253 -0
  141. tsugite_cli-0.8.2/tsugite/core/content_blocks.py +72 -0
  142. tsugite_cli-0.8.2/tsugite/core/executor.py +380 -0
  143. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/core/memory.py +10 -1
  144. tsugite_cli-0.8.2/tsugite/core/proxy.py +211 -0
  145. tsugite_cli-0.8.2/tsugite/core/sandbox.py +239 -0
  146. tsugite_cli-0.8.2/tsugite/core/subprocess_executor.py +721 -0
  147. tsugite_cli-0.8.2/tsugite/daemon/__init__.py +6 -0
  148. tsugite_cli-0.8.2/tsugite/daemon/adapters/__init__.py +5 -0
  149. tsugite_cli-0.8.2/tsugite/daemon/adapters/base.py +655 -0
  150. tsugite_cli-0.8.2/tsugite/daemon/adapters/discord.py +685 -0
  151. tsugite_cli-0.8.2/tsugite/daemon/adapters/http.py +1775 -0
  152. tsugite_cli-0.8.2/tsugite/daemon/adapters/scheduler_adapter.py +312 -0
  153. tsugite_cli-0.8.2/tsugite/daemon/compaction_scheduler.py +131 -0
  154. tsugite_cli-0.8.2/tsugite/daemon/config.py +180 -0
  155. tsugite_cli-0.8.2/tsugite/daemon/gateway.py +422 -0
  156. tsugite_cli-0.8.2/tsugite/daemon/memory.py +329 -0
  157. tsugite_cli-0.8.2/tsugite/daemon/push.py +99 -0
  158. tsugite_cli-0.8.2/tsugite/daemon/scheduler.py +455 -0
  159. tsugite_cli-0.8.2/tsugite/daemon/session_runner.py +185 -0
  160. tsugite_cli-0.8.2/tsugite/daemon/session_store.py +506 -0
  161. tsugite_cli-0.8.2/tsugite/daemon/web/css/responsive.css +182 -0
  162. tsugite_cli-0.8.2/tsugite/daemon/web/css/styles.css +216 -0
  163. tsugite_cli-0.8.2/tsugite/daemon/web/css/theme.css +47 -0
  164. tsugite_cli-0.8.2/tsugite/daemon/web/icons/icon-192.png +0 -0
  165. tsugite_cli-0.8.2/tsugite/daemon/web/icons/icon-512-maskable.png +0 -0
  166. tsugite_cli-0.8.2/tsugite/daemon/web/icons/icon-512.png +0 -0
  167. tsugite_cli-0.8.2/tsugite/daemon/web/icons/screenshot-narrow.png +0 -0
  168. tsugite_cli-0.8.2/tsugite/daemon/web/icons/screenshot-wide.png +0 -0
  169. tsugite_cli-0.8.2/tsugite/daemon/web/index.html +1046 -0
  170. tsugite_cli-0.8.2/tsugite/daemon/web/js/api.js +54 -0
  171. tsugite_cli-0.8.2/tsugite/daemon/web/js/app.js +103 -0
  172. tsugite_cli-0.8.2/tsugite/daemon/web/js/utils.js +84 -0
  173. tsugite_cli-0.8.2/tsugite/daemon/web/js/views/conversations.js +742 -0
  174. tsugite_cli-0.8.2/tsugite/daemon/web/js/views/dashboard.js +76 -0
  175. tsugite_cli-0.8.2/tsugite/daemon/web/js/views/file-editor.js +83 -0
  176. tsugite_cli-0.8.2/tsugite/daemon/web/js/views/kvstore.js +79 -0
  177. tsugite_cli-0.8.2/tsugite/daemon/web/js/views/schedules.js +185 -0
  178. tsugite_cli-0.8.2/tsugite/daemon/web/js/views/webhooks.js +71 -0
  179. tsugite_cli-0.8.2/tsugite/daemon/web/js/views/workspace.js +119 -0
  180. tsugite_cli-0.8.2/tsugite/daemon/web/manifest.json +18 -0
  181. tsugite_cli-0.8.2/tsugite/daemon/web/sw.js +27 -0
  182. tsugite_cli-0.8.2/tsugite/daemon/webhook_store.py +74 -0
  183. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/events/__init__.py +17 -5
  184. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/events/base.py +21 -12
  185. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/events/events.py +105 -35
  186. tsugite_cli-0.8.2/tsugite/events/helpers.py +81 -0
  187. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/exceptions.py +2 -9
  188. tsugite_cli-0.8.2/tsugite/history/__init__.py +52 -0
  189. tsugite_cli-0.8.2/tsugite/history/models.py +133 -0
  190. tsugite_cli-0.8.2/tsugite/history/reconstruction.py +348 -0
  191. tsugite_cli-0.8.2/tsugite/history/storage.py +547 -0
  192. tsugite_cli-0.8.2/tsugite/hooks.py +482 -0
  193. tsugite_cli-0.8.2/tsugite/interaction.py +79 -0
  194. tsugite_cli-0.8.2/tsugite/kvstore/__init__.py +45 -0
  195. tsugite_cli-0.8.2/tsugite/kvstore/backend.py +13 -0
  196. tsugite_cli-0.8.2/tsugite/kvstore/sqlite.py +83 -0
  197. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/mcp_client.py +2 -0
  198. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/mcp_config.py +1 -1
  199. tsugite_cli-0.8.2/tsugite/mcp_server.py +144 -0
  200. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/md_agents.py +39 -71
  201. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/models.py +26 -1
  202. tsugite_cli-0.8.2/tsugite/options.py +94 -0
  203. tsugite_cli-0.8.2/tsugite/plugins.py +102 -0
  204. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/renderer.py +67 -1
  205. tsugite_cli-0.8.2/tsugite/schemas/__init__.py +91 -0
  206. tsugite_cli-0.8.2/tsugite/schemas/agent.schema.json +224 -0
  207. tsugite_cli-0.8.2/tsugite/shell_tool_config.py +184 -0
  208. tsugite_cli-0.8.2/tsugite/skill_discovery.py +159 -0
  209. tsugite_cli-0.8.2/tsugite/templates/AGENTS.md +154 -0
  210. tsugite_cli-0.8.2/tsugite/templates/IDENTITY.md +23 -0
  211. tsugite_cli-0.8.2/tsugite/templates/MEMORY.md +23 -0
  212. tsugite_cli-0.8.2/tsugite/templates/USER.md +17 -0
  213. tsugite_cli-0.8.2/tsugite/templates/personas/casual-technical.md +26 -0
  214. tsugite_cli-0.8.2/tsugite/templates/personas/marvin.md +32 -0
  215. tsugite_cli-0.8.2/tsugite/templates/personas/minimal.md +9 -0
  216. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/tools/__init__.py +98 -28
  217. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/tools/agents.py +53 -13
  218. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/tools/fs.py +174 -62
  219. tsugite_cli-0.8.2/tsugite/tools/history.py +133 -0
  220. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/tools/http.py +58 -72
  221. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/tools/interactive.py +110 -42
  222. tsugite_cli-0.8.2/tsugite/tools/kv.py +86 -0
  223. tsugite_cli-0.8.2/tsugite/tools/notify.py +76 -0
  224. tsugite_cli-0.8.2/tsugite/tools/schedule.py +435 -0
  225. tsugite_cli-0.8.2/tsugite/tools/sessions.py +205 -0
  226. tsugite_cli-0.8.2/tsugite/tools/shell.py +54 -0
  227. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/tools/shell_tools.py +0 -1
  228. tsugite_cli-0.8.2/tsugite/tools/skills.py +284 -0
  229. tsugite_cli-0.8.2/tsugite/tools/tmux.py +311 -0
  230. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/ui/__init__.py +0 -13
  231. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/ui/base.py +101 -129
  232. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/ui/chat.py +121 -38
  233. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/ui/helpers.py +0 -28
  234. tsugite_cli-0.8.2/tsugite/ui/jsonl.py +175 -0
  235. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/ui/plain.py +26 -79
  236. tsugite_cli-0.8.2/tsugite/ui/repl_chat.py +300 -0
  237. tsugite_cli-0.8.2/tsugite/ui/repl_commands.py +388 -0
  238. tsugite_cli-0.8.2/tsugite/ui/repl_completer.py +201 -0
  239. tsugite_cli-0.8.2/tsugite/ui/repl_handler.py +411 -0
  240. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/ui_context.py +16 -2
  241. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/utils.py +61 -88
  242. tsugite_cli-0.8.2/tsugite/workspace/__init__.py +28 -0
  243. tsugite_cli-0.8.2/tsugite/workspace/context.py +37 -0
  244. tsugite_cli-0.8.2/tsugite/workspace/models.py +285 -0
  245. tsugite_cli-0.8.2/tsugite/workspace/session.py +174 -0
  246. tsugite_cli-0.8.2/tsugite/workspace/templates.py +59 -0
  247. tsugite_cli-0.8.2/uv.lock +2959 -0
  248. tsugite_cli-0.3.3/AGENTS.md +0 -1356
  249. tsugite_cli-0.3.3/CLAUDE.md +0 -1
  250. tsugite_cli-0.3.3/Dockerfile.runtime +0 -11
  251. tsugite_cli-0.3.3/README.md +0 -64
  252. tsugite_cli-0.3.3/benchmarks/README.md +0 -14
  253. tsugite_cli-0.3.3/benchmarks/basic/echo.md +0 -27
  254. tsugite_cli-0.3.3/benchmarks/basic/echo.test.md +0 -33
  255. tsugite_cli-0.3.3/benchmarks/basic/hello_world.md +0 -27
  256. tsugite_cli-0.3.3/benchmarks/basic/hello_world.test.md +0 -33
  257. tsugite_cli-0.3.3/benchmarks/basic/json_formatter.md +0 -38
  258. tsugite_cli-0.3.3/benchmarks/basic/json_formatter.test.md +0 -36
  259. tsugite_cli-0.3.3/benchmarks/basic/list_maker.md +0 -33
  260. tsugite_cli-0.3.3/benchmarks/basic/list_maker.test.md +0 -39
  261. tsugite_cli-0.3.3/benchmarks/basic/simple_math.md +0 -32
  262. tsugite_cli-0.3.3/benchmarks/basic/simple_math.test.md +0 -33
  263. tsugite_cli-0.3.3/benchmarks/complex/creative_writing.md +0 -35
  264. tsugite_cli-0.3.3/benchmarks/complex/creative_writing.test.md +0 -60
  265. tsugite_cli-0.3.3/benchmarks/tools/file_read_tool.md +0 -12
  266. tsugite_cli-0.3.3/benchmarks/tools/file_read_tool.test.md +0 -33
  267. tsugite_cli-0.3.3/benchmarks/tools/file_write_read_tool.md +0 -14
  268. tsugite_cli-0.3.3/benchmarks/tools/file_write_read_tool.test.md +0 -33
  269. tsugite_cli-0.3.3/benchmarks/tools/file_write_tool.md +0 -12
  270. tsugite_cli-0.3.3/benchmarks/tools/file_write_tool.test.md +0 -39
  271. tsugite_cli-0.3.3/benchmarks/tools/fixtures/config.json +0 -1
  272. tsugite_cli-0.3.3/benchmarks/tools/fixtures/data.txt +0 -3
  273. tsugite_cli-0.3.3/benchmarks/tools/fixtures/sample.txt +0 -2
  274. tsugite_cli-0.3.3/benchmarks/tools/shell_command_tool.md +0 -12
  275. tsugite_cli-0.3.3/benchmarks/tools/shell_command_tool.test.md +0 -32
  276. tsugite_cli-0.3.3/docs/examples/agents/morning_brief.md +0 -86
  277. tsugite_cli-0.3.3/docs/examples/agents/research_coordinator.md +0 -57
  278. tsugite_cli-0.3.3/docs/examples/agents/system_monitor.md +0 -54
  279. tsugite_cli-0.3.3/docs/examples/agents/web_searcher.md +0 -49
  280. tsugite_cli-0.3.3/docs/test_agents/prefresh_readme.md +0 -16
  281. tsugite_cli-0.3.3/docs/test_agents/template_test.md +0 -14
  282. tsugite_cli-0.3.3/docs/test_agents/test_agent.md +0 -15
  283. tsugite_cli-0.3.3/docs/test_agents/test_steps.md +0 -8
  284. tsugite_cli-0.3.3/tests/fixtures/agents/failing.md +0 -13
  285. tsugite_cli-0.3.3/tests/fixtures/agents/nested.md +0 -12
  286. tsugite_cli-0.3.3/tests/fixtures/agents/simple.md +0 -12
  287. tsugite_cli-0.3.3/tests/fixtures/agents/slow.md +0 -12
  288. tsugite_cli-0.3.3/tests/test_agent_attachments.py +0 -127
  289. tsugite_cli-0.3.3/tests/test_agent_composition.py +0 -189
  290. tsugite_cli-0.3.3/tests/test_attachment_deduplication.py +0 -228
  291. tsugite_cli-0.3.3/tests/test_attachment_resolution.py +0 -184
  292. tsugite_cli-0.3.3/tests/test_attachments.py +0 -196
  293. tsugite_cli-0.3.3/tests/test_benchmark_core.py +0 -301
  294. tsugite_cli-0.3.3/tests/test_benchmark_evaluators.py +0 -333
  295. tsugite_cli-0.3.3/tests/test_benchmark_metrics.py +0 -265
  296. tsugite_cli-0.3.3/tests/test_benchmark_reports.py +0 -347
  297. tsugite_cli-0.3.3/tests/test_cache_control.py +0 -157
  298. tsugite_cli-0.3.3/tests/test_continuation.py +0 -510
  299. tsugite_cli-0.3.3/tests/test_docker_delegation.py +0 -236
  300. tsugite_cli-0.3.3/tests/test_edit_strategies.py +0 -293
  301. tsugite_cli-0.3.3/tests/test_history.py +0 -637
  302. tsugite_cli-0.3.3/tests/test_history_models.py +0 -346
  303. tsugite_cli-0.3.3/tests/test_http_tools.py +0 -109
  304. tsugite_cli-0.3.3/tests/test_llm_evaluator.py +0 -344
  305. tsugite_cli-0.3.3/tests/test_stdin.py +0 -243
  306. tsugite_cli-0.3.3/tests/test_task_tracking.py +0 -362
  307. tsugite_cli-0.3.3/tests/test_textual_chat.py +0 -428
  308. tsugite_cli-0.3.3/tsugite/__init__.py +0 -6
  309. tsugite_cli-0.3.3/tsugite/agent_composition.py +0 -163
  310. tsugite_cli-0.3.3/tsugite/agent_preparation.py +0 -236
  311. tsugite_cli-0.3.3/tsugite/agent_runner/history_integration.py +0 -248
  312. tsugite_cli-0.3.3/tsugite/attachments/base.py +0 -34
  313. tsugite_cli-0.3.3/tsugite/attachments/file.py +0 -51
  314. tsugite_cli-0.3.3/tsugite/attachments/url.py +0 -59
  315. tsugite_cli-0.3.3/tsugite/benchmark/__init__.py +0 -62
  316. tsugite_cli-0.3.3/tsugite/benchmark/config.py +0 -183
  317. tsugite_cli-0.3.3/tsugite/benchmark/core.py +0 -292
  318. tsugite_cli-0.3.3/tsugite/benchmark/discovery.py +0 -377
  319. tsugite_cli-0.3.3/tsugite/benchmark/evaluators.py +0 -671
  320. tsugite_cli-0.3.3/tsugite/benchmark/execution.py +0 -657
  321. tsugite_cli-0.3.3/tsugite/benchmark/metrics.py +0 -204
  322. tsugite_cli-0.3.3/tsugite/benchmark/reports.py +0 -420
  323. tsugite_cli-0.3.3/tsugite/benchmark/utils.py +0 -288
  324. tsugite_cli-0.3.3/tsugite/builtin_agents/chat-assistant.md +0 -53
  325. tsugite_cli-0.3.3/tsugite/builtin_agents/default.md +0 -140
  326. tsugite_cli-0.3.3/tsugite/builtin_agents.py +0 -5
  327. tsugite_cli-0.3.3/tsugite/cli/__init__.py +0 -1042
  328. tsugite_cli-0.3.3/tsugite/cli/benchmark.py +0 -663
  329. tsugite_cli-0.3.3/tsugite/cli/history.py +0 -193
  330. tsugite_cli-0.3.3/tsugite/core/agent.py +0 -774
  331. tsugite_cli-0.3.3/tsugite/core/executor.py +0 -300
  332. tsugite_cli-0.3.3/tsugite/docker_cli.py +0 -270
  333. tsugite_cli-0.3.3/tsugite/history/__init__.py +0 -29
  334. tsugite_cli-0.3.3/tsugite/history/index.py +0 -210
  335. tsugite_cli-0.3.3/tsugite/history/models.py +0 -106
  336. tsugite_cli-0.3.3/tsugite/history/storage.py +0 -157
  337. tsugite_cli-0.3.3/tsugite/shell_tool_config.py +0 -265
  338. tsugite_cli-0.3.3/tsugite/templates/assistant.md +0 -14
  339. tsugite_cli-0.3.3/tsugite/tools/edit_strategies.py +0 -393
  340. tsugite_cli-0.3.3/tsugite/tools/shell.py +0 -129
  341. tsugite_cli-0.3.3/tsugite/tools/tasks.py +0 -339
  342. tsugite_cli-0.3.3/tsugite/ui/chat.tcss +0 -92
  343. tsugite_cli-0.3.3/tsugite/ui/chat_history.py +0 -286
  344. tsugite_cli-0.3.3/tsugite/ui/jsonl.py +0 -125
  345. tsugite_cli-0.3.3/tsugite/ui/live_template.py +0 -529
  346. tsugite_cli-0.3.3/tsugite/ui/textual_chat.py +0 -642
  347. tsugite_cli-0.3.3/tsugite/ui/textual_handler.py +0 -225
  348. tsugite_cli-0.3.3/tsugite/ui/widgets/__init__.py +0 -6
  349. tsugite_cli-0.3.3/tsugite/ui/widgets/base_scroll_log.py +0 -27
  350. tsugite_cli-0.3.3/tsugite/ui/widgets/message_list.py +0 -121
  351. tsugite_cli-0.3.3/tsugite/ui/widgets/thought_log.py +0 -80
  352. tsugite_cli-0.3.3/tsugite/xdg.py +0 -104
  353. tsugite_cli-0.3.3/uv.lock +0 -2767
  354. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/.github/workflows/ci.yml +0 -0
  355. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/.github/workflows/docker-publish.yml +0 -0
  356. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/.github/workflows/pypi-publish.yml +0 -0
  357. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/LICENSE +0 -0
  358. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/README.md +0 -0
  359. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/__init__.py +0 -0
  360. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/core/__init__.py +0 -0
  361. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/core/test_memory.py +0 -0
  362. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/core/test_tools.py +0 -0
  363. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/smoke_test.sh +0 -0
  364. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_agents_tool.py +0 -0
  365. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_chat_cli.py +0 -0
  366. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_cli_arguments.py +0 -0
  367. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_custom_shell_tools.py +0 -0
  368. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_error_display.py +0 -0
  369. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_interactive_tool.py +0 -0
  370. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_mcp_client.py +0 -0
  371. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_reasoning_models.py +0 -0
  372. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_renderer.py +0 -0
  373. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_retry_system.py +0 -0
  374. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_subagent_subprocess.py +0 -0
  375. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tests/test_tool_registry.py +0 -0
  376. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/agent_runner/metrics.py +0 -0
  377. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/agent_utils.py +0 -0
  378. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/attachments/inline.py +0 -0
  379. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/cli/agents.py +0 -0
  380. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/cli/attachments.py +0 -0
  381. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/cli/cache.py +0 -0
  382. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/cli/mcp.py +0 -0
  383. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/constants.py +0 -0
  384. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/core/tools.py +0 -0
  385. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/events/bus.py +0 -0
  386. {tsugite_cli-0.3.3 → tsugite_cli-0.8.2}/tsugite/tsugite.py +0 -0
@@ -7,19 +7,18 @@ Tsugite is a micro-agent CLI: agents are Markdown + YAML frontmatter, rendered v
7
7
  **Flow:** `Agent.md` (YAML + Jinja2) → `renderer.py` → `TsugiteAgent` → LiteLLM → Tools
8
8
 
9
9
  **Key Modules:**
10
- - `cli/__init__.py` - Typer CLI (run, chat, render, config, mcp, tools, agents, attachments, cache, benchmark)
10
+ - `cli/__init__.py` - Typer CLI (run, chat, render, config, mcp, 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
- - `ui/textual_chat.py` - Textual TUI for interactive chat
16
15
  - `agent_runner.py` - Execution orchestration, prefetch, tool wiring, multi-step
17
16
  - `core/agent.py` - LiteLLM agent loop + streaming
18
17
  - `renderer.py` - Jinja2 rendering + helpers (now, today, slugify, env, file_exists, read_text, is_file, is_dir)
19
18
  - `tools/` - Tool registry + implementations
20
19
  - `shell_tool_config.py` + `tools/shell_tools.py` - Custom shell command wrappers
21
20
  - `models.py` - Model string parsing + provider dispatch
22
- - `config.py` - Configuration (default_model, model_aliases, default_base_agent, chat_theme)
21
+ - `config.py` - Configuration (default_model, model_aliases, default_base_agent)
23
22
 
24
23
  **Development:**
25
24
  ```bash
@@ -42,8 +41,9 @@ uv run black . && uv run ruff check .
42
41
  File-based agents distributed with the package in `tsugite/builtin_agents/` directory.
43
42
 
44
43
  **Current Built-ins:**
45
- 1. `default.md` - Base agent with task tracking, spawn_agent, and file tools
46
- 2. `chat-assistant.md` - Chat agent with tools (read_file, write_file, list_files, web_search, fetch_text, run), text_mode enabled
44
+ 1. `default.md` - General-purpose agent with task tracking, spawn_agent, file tools, web search, memory, and shell execution
45
+ 2. `file_searcher.md` - Specialized agent for finding files and searching content
46
+ 3. `code_searcher.md` - AST-based structural code search using ast-grep
47
47
 
48
48
  **Location:** `tsugite/builtin_agents/{name}.md`
49
49
 
@@ -71,20 +71,17 @@ File-based agents distributed with the package in `tsugite/builtin_agents/` dire
71
71
 
72
72
  **Components:**
73
73
  - `chat.py:ChatManager` - Session management, history (max_history), turn execution
74
- - `ui/textual_chat.py:ChatApp` - Textual TUI
75
- - `ui/widgets/` - MessageList, ThoughtLog
74
+ - `ui/repl_chat.py` - REPL chat interface
76
75
 
77
- **Default Agent:** `chat-assistant`. Override with `.tsugite/chat-assistant.md`.
76
+ **Default Agent:** `default`. Override with `.tsugite/default.md`.
78
77
 
79
78
  **Flow:**
80
79
  1. User input → `ChatManager.run_turn()`
81
80
  2. Inject `chat_history` context (list of ChatTurn objects)
82
- 3. Call `agent_runner.run_agent()` with `force_text_mode=True`
81
+ 3. Call `agent_runner.run_agent()`
83
82
  4. Store result in ChatTurn (timestamp, messages, token_count, cost)
84
83
  5. Prune history if `> max_history`
85
84
 
86
- **Text Mode:** `text_mode: true` allows LLM responses without code blocks.
87
-
88
85
  ### Directives
89
86
 
90
87
  **Types:**
@@ -155,7 +152,6 @@ Chat:
155
152
  ---
156
153
  name: my_chat
157
154
  model: openai:gpt-4o
158
- text_mode: true
159
155
  tools: [read_file, web_search]
160
156
  ---
161
157
  {% if chat_history %}
@@ -166,4 +166,14 @@ cython_debug/
166
166
  .env
167
167
  benchmark_results/
168
168
  test_output/
169
- .claude/settings.local.json
169
+ .claude/settings.local.json
170
+
171
+
172
+ # TODO: temp - I need to clean up the docs
173
+ docs/
174
+ examples/
175
+ agents/
176
+ .claude/
177
+ .tsugite/
178
+ benchmarks/
179
+ #### TODO ^^^
@@ -0,0 +1,354 @@
1
+ # AGENTS.md
2
+
3
+ This file provides guidance to AI agents (tsugite, Claude Code, etc.) when working with code in this repository.
4
+
5
+ ## Commands
6
+
7
+ ### Development Setup
8
+ ```bash
9
+ # Install dependencies
10
+ uv sync --dev
11
+
12
+ # Run all tests
13
+ uv run pytest
14
+
15
+ # Run specific test
16
+ uv run pytest tests/test_file.py::test_function
17
+
18
+ # Run tests with coverage
19
+ uv run pytest --cov=tsugite --cov-report=html
20
+
21
+ # Lint and format
22
+ uv run black .
23
+ uv run ruff check .
24
+ uv run pylint tsugite
25
+ ```
26
+
27
+ ### Testing Agents
28
+ ```bash
29
+ # Run a basic agent (both `tsugite` and `tsu` aliases work)
30
+ uv run tsugite run examples/simple_variable_injection.md "test task"
31
+
32
+ # Run with specific model
33
+ uv run tsu run +agent "task" --model openai:gpt-4o-mini
34
+
35
+ # Preview agent rendering (see what LLM receives)
36
+ uv run tsu render agent.md "task"
37
+
38
+ # Validate agent schema
39
+ uv run tsu validate agents/*.md
40
+ ```
41
+
42
+ ### Schema Management
43
+ ```bash
44
+ # Regenerate JSON schema after modifying AgentConfig
45
+ uv run python scripts/regenerate_schema.py
46
+ ```
47
+
48
+ ## Architecture
49
+
50
+ Tsugite is an agentic CLI that executes AI agents defined as markdown files with YAML frontmatter. The architecture follows a pipeline pattern: parse → prepare → render → execute.
51
+
52
+ ### Core Execution Flow
53
+
54
+ 1. **CLI Entry** (`tsugite/cli/__init__.py`)
55
+ - Typer-based CLI with subcommands
56
+ - Main commands: `run` (single-shot), `chat` (interactive), `render` (preview)
57
+ - Additional: `daemon`, `workspace`, `init`, `validate`, `mcp`, `serve`, `agents`, `config`, `attachments`, `cache`, `tools`, `history`
58
+
59
+ 2. **Agent Resolution & Inheritance** (`tsugite/agent_inheritance.py`)
60
+ - Resolves agent names to file paths using search order:
61
+ - Workspace agents directory (if workspace provided)
62
+ - `.tsugite/{name}.md` (project-local)
63
+ - `agents/{name}.md` (project convention)
64
+ - `./{name}.md` (current directory)
65
+ - `builtin_agents/{name}.md` (package-provided)
66
+ - Global directories (`~/.config/tsugite/agents/`)
67
+ - Merges agent inheritance chains (scalars override, lists merge, dicts deep merge)
68
+
69
+ 3. **Agent Parsing** (`tsugite/md_agents.py`)
70
+ - Parses YAML frontmatter → `AgentConfig` (Pydantic model)
71
+ - Extracts markdown content
72
+ - Parses directives:
73
+ - `<!-- tsu:step -->` - Multi-step workflow stages
74
+ - `<!-- tsu:ignore -->` - Documentation blocks (stripped before LLM)
75
+ - `<!-- tsu:tool -->` - Execute tools during rendering
76
+
77
+ 4. **Agent Preparation** (`tsugite/agent_preparation.py`)
78
+ - **Unified preparation pipeline** (used by both `run` and `render` commands)
79
+ - Executes prefetch tools (frontmatter `prefetch` field)
80
+ - Executes tool directives (inline `<!-- tsu:tool -->`)
81
+ - Builds template context (tasks, variables, helpers)
82
+ - Renders Jinja2 templates
83
+ - Expands tool globs and categories
84
+ - Loads auto_load_skills
85
+ - Builds system prompt with tools and instructions
86
+
87
+ 5. **Template Rendering** (`tsugite/renderer.py`)
88
+ - Jinja2 environment with custom helpers:
89
+ - `{{ user_prompt }}`, `{{ tasks }}`, `{{ task_summary }}`
90
+ - `{{ now() }}`, `{{ today() }}`, `{{ yesterday() }}`, `{{ tomorrow() }}`
91
+ - `{{ slugify(text) }}`, `{{ cwd() }}`
92
+ - `{{ file_exists(path) }}`, `{{ is_file(path) }}`, `{{ is_dir(path) }}`, `{{ read_text(path) }}`
93
+ - Context also includes: `env` (os.environ), `datetime`, `timedelta`
94
+ - Supports step-scoped variables for multi-step agents
95
+ - Strips ignored sections before LLM execution
96
+
97
+ 6. **Agent Execution** (`tsugite/agent_runner/runner.py`)
98
+ - Orchestrates multi-step or single-step execution
99
+ - Manages step metrics (duration, success/failure)
100
+ - Handles looping steps (repeat_while/repeat_until)
101
+ - Supports continue_on_error and per-step timeouts
102
+ - Integrates with history system for conversation continuity
103
+
104
+ 7. **LLM Agent Loop** (`tsugite/core/agent.py`)
105
+ - `TsugiteAgent` - Custom agent using LiteLLM directly
106
+ - Think-Code-Observation loop (max_turns iterations)
107
+ - Supports reasoning models (o1, o3, Claude extended thinking)
108
+ - Direct control over model parameters (temperature, reasoning_effort)
109
+ - Code execution via `LocalExecutor`
110
+
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`)
117
+
118
+ 9. **Event System** (`tsugite/events/`)
119
+ - Event-driven architecture for UI decoupling
120
+ - 20 event types: execution, LLM, meta, progress, skills
121
+ - `EventBus` dispatches to multiple handlers
122
+ - Handlers: Rich console, plain text, JSONL, chat, REPL
123
+
124
+ 10. **History System** (`tsugite/history/`)
125
+ - JSONL-based conversation history (one `.jsonl` file per conversation)
126
+ - Stores turns (user prompts, assistant responses)
127
+ - Supports continuation (`--continue` flag, optional `--conversation-id`)
128
+ - Indexed by conversation_id, agent_name, machine_name
129
+
130
+ 11. **Configuration** (`tsugite/config.py`)
131
+ - XDG-compliant paths: `~/.config/tsugite/` or `$XDG_CONFIG_HOME/tsugite/`
132
+ - JSON config with model aliases, default settings
133
+ - Attachment storage (reusable context)
134
+ - MCP server registration
135
+ - Auto-context discovery (CLAUDE.md, AGENTS.md, CONTEXT.md)
136
+
137
+ ### Key Data Structures
138
+
139
+ **AgentConfig** (`md_agents.py:43`)
140
+ - Pydantic model for agent frontmatter
141
+ - Validates all fields (name, model, tools, max_turns, etc.)
142
+ - Schema exported to `tsugite/schemas/agent.schema.json`
143
+
144
+ **PreparedAgent** (`agent_preparation.py:17`)
145
+ - Dataclass containing everything for execution/display
146
+ - Ensures `render` shows exactly what `run` executes
147
+ - Contains: agent, config, system_message, user_message, tools, context
148
+
149
+ **Tool** (`core/tools.py`)
150
+ - Unified tool interface for LLM function calling
151
+ - Supports async/sync execution
152
+ - Parameters with JSON schema validation
153
+
154
+ **Agent** (`md_agents.py`)
155
+ - Parsed agent with content and step directives
156
+ - Steps contain: name, assign variable, temperature, timeout, continue_on_error, repeat conditions
157
+
158
+ ### Multi-Step Agents
159
+
160
+ Multi-step agents use `<!-- tsu:step -->` directives to create workflows:
161
+
162
+ ```markdown
163
+ <!-- tsu:step name="research" assign="findings" timeout="60" -->
164
+ Research topic
165
+
166
+ <!-- tsu:step name="summarize" assign="summary" repeat_while="has_pending_tasks" -->
167
+ Summarize findings
168
+ ```
169
+
170
+ **Step Execution:**
171
+ - Preamble (content before first step) rendered once, shared across steps
172
+ - Each step has isolated execution context
173
+ - Variables persist across steps (findings → summary)
174
+ - Supports timeouts, continue_on_error, repeat conditions
175
+ - Metrics tracked per step (duration, status, errors)
176
+
177
+ **Looping Steps:**
178
+ - `repeat_while` / `repeat_until` with Jinja2 conditions
179
+ - Helper conditions: `has_pending_tasks`, `all_tasks_complete`, etc.
180
+ - Safety: `max_iterations` (default 10) prevents infinite loops
181
+ - Loop context: `{{ iteration }}`, `{{ max_iterations }}`, `{{ is_looping_step }}`
182
+
183
+ ### Event-Driven UI
184
+
185
+ All UI output goes through the event system:
186
+
187
+ **Event Types** (events/base.py:9):
188
+ - Execution: TASK_START, STEP_START, CODE_EXECUTION, OBSERVATION, FINAL_ANSWER, ERROR
189
+ - LLM: LLM_MESSAGE, REASONING_CONTENT, REASONING_TOKENS
190
+ - Meta: COST_SUMMARY, STREAM_CHUNK, STREAM_COMPLETE, INFO, WARNING, DEBUG_MESSAGE
191
+ - Skills: SKILL_LOADED, SKILL_UNLOADED, SKILL_LOAD_FAILED
192
+ - Progress: STEP_PROGRESS, FILE_READ
193
+
194
+ **Handlers** (ui/):
195
+ - `base.py` - UIHandler interface
196
+ - `plain.py` - Plain text (no colors, copy-paste friendly)
197
+ - `jsonl.py` - JSONL protocol (for subprocess subagents)
198
+ - `chat.py`, `repl_handler.py` - Interactive chat/REPL modes
199
+
200
+ ### Agent Spawning (Multi-Agent)
201
+
202
+ Agents can spawn other agents using the `spawn_agent` tool:
203
+
204
+ ```bash
205
+ # Multi-agent mode: primary +helper1 +helper2
206
+ tsugite run +coordinator +researcher +writer "task"
207
+ ```
208
+
209
+ **Visibility control:**
210
+ - `visibility: public` (default) - Can be spawned by any agent
211
+ - `visibility: private` - Requires explicit allow list (multi-agent mode)
212
+ - `visibility: internal` - Helper agents, not meant to be public
213
+ - `spawnable: false` - Hard block, cannot be spawned (safety)
214
+
215
+ **Subagent context:**
216
+ - `{{ is_subagent }}` - Boolean
217
+ - `{{ parent_agent }}` - Parent agent name
218
+
219
+ ### Prompt Caching
220
+
221
+ Automatic caching for supported providers (OpenAI, Anthropic, Bedrock, Deepseek):
222
+
223
+ - Attachments sent as separate system content blocks with `cache_control` markers
224
+ - Skills also receive cache markers for reuse across turns
225
+ - Cache markers added automatically by `tsugite/core/agent.py`
226
+
227
+ ### Context Management (Daemon Mode)
228
+
229
+ When using Claude Code as the LLM provider for daemon mode, disable Claude Code's built-in auto-compaction so that tsugite's compaction logic (80% threshold) can manage context properly. If Claude Code compacts first, tsugite's compaction never triggers and session summaries are lost.
230
+
231
+ ### Multi-Modal Attachments
232
+
233
+ Tsugite supports vision (images), audio, and document understanding through the attachment system.
234
+
235
+ **Supported Content Types:**
236
+ - **Images**: JPEG, PNG, GIF, WebP, SVG, BMP, TIFF
237
+ - **Audio**: MP3, WAV, OGG, M4A, FLAC (base64 encoded)
238
+ - **Documents**: PDF, Word (DOCX/DOC), Excel (XLSX/XLS), PowerPoint (PPTX/PPT)
239
+ - **Text**: Plain text, HTML (converted to markdown), JSON, XML, etc.
240
+
241
+ **How It Works:**
242
+
243
+ 1. **URL Attachments** (Images/Documents):
244
+ - LiteLLM fetches the URL directly (no download overhead)
245
+ - Example: `tsu run -f https://example.com/chart.png "Describe this chart"`
246
+ - Automatically detected via HTTP HEAD request
247
+
248
+ 2. **Local File Attachments**:
249
+ - Images: Read as bytes, base64 encoded
250
+ - Documents: Read as bytes, base64 encoded
251
+ - Text: Read as UTF-8
252
+ - Example: `tsu run -f image.jpg "What's in this image?"`
253
+
254
+ 3. **LLM Integration**:
255
+ - Images: Sent as `image_url` content blocks (URL or data URI)
256
+ - Audio: Sent as `input_audio` content blocks (base64)
257
+ - Documents: Sent as `file` content blocks (URL or data URI)
258
+ - Text: Sent as `text` content blocks (wrapped in XML tags)
259
+
260
+ **Examples:**
261
+
262
+ ```bash
263
+ # Analyze an image from URL
264
+ tsu run -f https://example.com/chart.png "Describe this chart"
265
+
266
+ # Analyze a local image
267
+ tsu run -f screenshot.png "What error is shown?"
268
+
269
+ # Analyze a PDF
270
+ tsu run -f report.pdf "Summarize this report"
271
+
272
+ # Multiple attachments
273
+ tsu run -f image1.jpg -f image2.jpg "Compare these images"
274
+ ```
275
+
276
+ **Model Support:**
277
+ - Vision: Claude 3.5 Sonnet, GPT-4o, GPT-4 Turbo, Gemini Pro Vision, etc.
278
+ - Audio: GPT-4o Audio Preview
279
+ - Documents: Claude (Bedrock/API), GPT-4o, Gemini, Bedrock Anthropic models
280
+
281
+ **Implementation Details:**
282
+ - `Attachment` dataclass: `tsugite/attachments/base.py`
283
+ - URL handler: `tsugite/attachments/url.py` (detects content type via HEAD request)
284
+ - File handler: `tsugite/attachments/file.py` (detects type by extension)
285
+ - Auto-context handler: `tsugite/attachments/auto_context.py` (discovers CONTEXT.md, AGENTS.md, CLAUDE.md)
286
+ - YouTube handler: `tsugite/attachments/youtube.py` (fetches transcripts)
287
+ - LLM formatting: `tsugite/core/agent.py:_format_attachment()`
288
+
289
+ ## Development Patterns
290
+
291
+ ### Adding a New Built-in Agent
292
+
293
+ 1. Create `tsugite/builtin_agents/my_agent.md`
294
+ 2. Add frontmatter with `name`, `tools`, `description`
295
+ 3. Write agent template
296
+ 4. No code changes needed - auto-discovered
297
+ 5. Add tests in `tests/test_builtin_agents.py`
298
+
299
+ ### Adding a New Tool
300
+
301
+ 1. Create tool function in `tsugite/tools/`
302
+ 2. Register in tool registry (`tools/__init__.py`)
303
+ 3. Define parameters with JSON schema
304
+ 4. Add to category if applicable (`@fs`, `@http`, etc.)
305
+ 5. Add tests in `tests/test_*_tools.py`
306
+
307
+ ### Adding a New Event Type
308
+
309
+ 1. Add to `EventType` enum (`events/base.py`)
310
+ 2. Create event class in `events/events.py`
311
+ 3. Update UI handlers to handle new event
312
+ 4. Add tests in `tests/events/`
313
+
314
+ ### Modifying AgentConfig
315
+
316
+ 1. Update `AgentConfig` Pydantic model (`md_agents.py`)
317
+ 2. Regenerate schema: `uv run python scripts/regenerate_schema.py`
318
+ 3. Update documentation in CLAUDE.md (user guide section)
319
+ 4. Add validation tests in `tests/test_agent_parser.py`
320
+
321
+ ## Testing Strategy
322
+
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.
324
+ - **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.
326
+ - **Smoke tests**: `tests/smoke_test.sh` hits a real LLM API (requires `OPENAI_API_KEY`, not run in CI)
327
+ - **Fixtures**: `conftest.py` provides shared test data
328
+ - **Mocking**: Use `@pytest.fixture` for LLM responses
329
+ - **Async tests**: Mark with `@pytest.mark.asyncio`
330
+ - **Coverage target**: 80%+ (check with `--cov-report=html`)
331
+
332
+ ## Code Style
333
+
334
+ - Python 3.11+ (type hints on public APIs)
335
+ - Line length: 120 characters (Black)
336
+ - Import order: stdlib → third-party → local (blank lines between groups)
337
+ - Errors: `ValueError` for input validation, `RuntimeError` for execution failures
338
+ - Docstrings: Google style with `Args:` and `Returns:` sections
339
+ - Pydantic models: Use `extra="forbid"` to catch typos in YAML frontmatter
340
+
341
+ ## Common Pitfalls
342
+
343
+ 1. **Don't break render/run parity**: Changes to rendering must update BOTH `renderer.py` and `agent_preparation.py`
344
+ 2. **Don't emit print/console directly**: Use event system (`EventBus.emit()`)
345
+ 3. **Don't forget schema regen**: After modifying `AgentConfig`, regenerate schema
346
+ 4. **Don't hardcode paths**: Use XDG utilities (`get_xdg_config_path()`, etc.)
347
+ 5. **Don't use blocking IO in async**: Use `asyncio.to_thread()` for sync tools
348
+ 6. **Test both sync and async paths**: Many tools support both execution modes
349
+ 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
+
351
+ ## Code Review Policy
352
+
353
+ - **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.
354
+ - When making implementation plans, include a final "simplify/review" step that uses the code-simplifier agent on all modified files.
@@ -0,0 +1 @@
1
+ See [AGENTS.md](AGENTS.md) for project instructions.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tsugite-cli
3
- Version: 0.3.3
3
+ Version: 0.8.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
@@ -248,15 +248,23 @@ Requires-Dist: jinja2>=3.1
248
248
  Requires-Dist: litellm>=1.77.7
249
249
  Requires-Dist: mcp>=1.0
250
250
  Requires-Dist: nest-asyncio>=1.6.0
251
+ Requires-Dist: pathspec>=0.11.0
252
+ Requires-Dist: portalocker>=2.8.0
253
+ Requires-Dist: prompt-toolkit>=3.0.43
251
254
  Requires-Dist: pydantic>=2.12.3
252
255
  Requires-Dist: pyyaml>=6.0
253
256
  Requires-Dist: questionary>=2.1.1
254
257
  Requires-Dist: rich>=13.0
255
- Requires-Dist: ruff>=0.13.2
256
- Requires-Dist: textual-autocomplete>=4.0.6
257
- Requires-Dist: textual>=6.2.1
258
258
  Requires-Dist: typer>=0.12
259
+ Requires-Dist: tzlocal>=5.3.1
259
260
  Requires-Dist: youtube-transcript-api>=0.6.0
261
+ Provides-Extra: daemon
262
+ Requires-Dist: cronsim>=2.6; extra == 'daemon'
263
+ Requires-Dist: discord-py>=2.3.2; extra == 'daemon'
264
+ Requires-Dist: py-vapid>=1.9.0; extra == 'daemon'
265
+ Requires-Dist: pywebpush>=2.0.0; extra == 'daemon'
266
+ Requires-Dist: starlette>=0.36; extra == 'daemon'
267
+ Requires-Dist: uvicorn>=0.25; extra == 'daemon'
260
268
  Description-Content-Type: text/markdown
261
269
 
262
270
  # tsugite
@@ -265,12 +273,24 @@ Tsugite (Japanese: 継ぎ手, the art of joinery in woodworking) is a developer-
265
273
 
266
274
  Define AI agents as markdown files with YAML frontmatter. Chain multiple steps together, pass data between them, and use any LLM (OpenAI, Anthropic, Ollama, etc).
267
275
 
268
- ## Quick Start
276
+ ## Installation
269
277
 
270
278
  ```bash
271
- # Install
272
- uv sync --dev
279
+ # Recommended: Install with uv
280
+ uv tool install tsugite-cli
281
+
282
+ # Alternative: Install with pipx
283
+ pipx install tsugite-cli
284
+
285
+ # Or with pip
286
+ pip install tsugite-cli
287
+ ```
273
288
 
289
+ **Note:** The package name is `tsugite-cli`, but the command is `tsugite` (or `tsu` for short).
290
+
291
+ ## Quick Start
292
+
293
+ ```bash
274
294
  # Run an agent
275
295
  tsugite run examples/simple_variable_injection.md "test it"
276
296
 
@@ -322,4 +342,89 @@ tsugite history list
322
342
  tsugite history show CONV_ID
323
343
  ```
324
344
 
345
+ ## Sandbox
346
+
347
+ Agent code can run inside a [bubblewrap](https://github.com/containers/bubblewrap) sandbox with filesystem and network isolation.
348
+
349
+ ```bash
350
+ # Sandbox with specific domains allowed
351
+ tsu run +default "task" --sandbox --allow-domain "github.com" --allow-domain "*.openai.com"
352
+
353
+ # Full network isolation
354
+ tsu run +default "task" --sandbox --no-network
355
+ ```
356
+
357
+ **How it works:**
358
+
359
+ ```
360
+ CLI --sandbox --allow-domain "*.github.com"
361
+ └─ SubprocessExecutor writes harness script
362
+ └─ bwrap --unshare-pid --unshare-net --die-with-parent ...
363
+ └─ TCP↔UDS bridge (HTTP_PROXY → Unix socket)
364
+ └─ ConnectProxy filters CONNECT requests by domain:port
365
+ ```
366
+
367
+ **Default mounts:**
368
+
369
+ - Read-only: `/usr`, `/lib`, `/lib64`, `/bin`, `/sbin`, `/etc/ssl`, `/etc/resolv.conf`, CA certs, Python venv + `sys.path`
370
+ - Read-write: workspace directory, internal state directory
371
+ - Tmpfs: `/tmp` (fresh each run)
372
+
373
+ Extra bind mounts can be added programmatically via `SandboxConfig.extra_ro_binds` and `extra_rw_binds`, but these are not yet exposed through CLI flags or agent frontmatter.
374
+
375
+ **Network:**
376
+ - Network is namespace-isolated; outbound HTTP/HTTPS goes through a filtering CONNECT proxy
377
+ - Direct connections to bare IP addresses are always blocked
378
+ - `--no-network` skips the proxy entirely — no connectivity at all
379
+
380
+ **`--allow-domain` syntax:**
381
+
382
+ | Pattern | Allows |
383
+ |---|---|
384
+ | `github.com` | ports 80, 443 |
385
+ | `github.com:22` | port 22 only |
386
+ | `*.github.com:8080` | port 8080 on subdomains |
387
+ | `*:*` | all domains, all ports |
388
+
389
+ ## Hooks
390
+
391
+ Hooks fire shell commands at lifecycle points. Configure in `.tsugite/hooks.yaml`:
392
+
393
+ ```yaml
394
+ hooks:
395
+ post_tool:
396
+ - tools: [write_file]
397
+ run: git add {{ path }}
398
+ wait: true
399
+
400
+ pre_message:
401
+ - run: uridx search "{{ message }}" --limit 5
402
+ capture_as: rag_context
403
+ - run: cat memory/preferences.md
404
+ capture_as: user_preferences
405
+
406
+ pre_compact:
407
+ - run: ./scripts/extract-facts.sh {{ turns_file }}
408
+ wait: true
409
+
410
+ post_compact:
411
+ - run: echo "Compacted {{ turns_compacted }} turns"
412
+ ```
413
+
414
+ **Hook fields:** `run` (Jinja2 shell command), `tools` (tool filter, `post_tool` only), `match` (Jinja2 condition), `wait` (block until done), `capture_as` (capture stdout into a template variable, implies `wait`).
415
+
416
+ **Hook types:**
417
+ - **`post_tool`** — After successful tool calls. Context: `tool`, plus tool arguments.
418
+ - **`pre_message`** — Before agent execution. Context: `message`, `user_id`, `agent_name`. Use `capture_as` to inject results into agent templates as `{{ var_name }}`.
419
+ - **`pre_compact`** / **`post_compact`** — Around session compaction. Context: `conversation_id`, `user_id`, `agent_name`, `turns_file`, `turn_count`.
420
+
421
+ ## Development
422
+
423
+ ```bash
424
+ # Clone and install for development
425
+ git clone https://github.com/justyns/tsugite.git
426
+ cd tsugite
427
+ uv sync --dev
428
+ ```
429
+
325
430
  See `examples/` for working agents and `CLAUDE.md` for AI-generated documentation.