@scemoon/cdh 1.0.0

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 (528) hide show
  1. package/ai-dlc-skill/SKILL.md +75 -0
  2. package/ai-dlc-skill/agents/deliver-agent.md +24 -0
  3. package/ai-dlc-skill/agents/master.md +44 -0
  4. package/ai-dlc-skill/agents/plan-agent.md +23 -0
  5. package/ai-dlc-skill/agents/understand-agent.md +23 -0
  6. package/ai-dlc-skill/agents/verify-agent.md +27 -0
  7. package/ai-dlc-skill/architecture/project-structure.md +78 -0
  8. package/ai-dlc-skill/brownfield/README.md +30 -0
  9. package/ai-dlc-skill/brownfield/generate-context.sh +64 -0
  10. package/ai-dlc-skill/brownfield/scripts/deps.sh +27 -0
  11. package/ai-dlc-skill/brownfield/scripts/discover.sh +34 -0
  12. package/ai-dlc-skill/brownfield/scripts/extract-api.sh +45 -0
  13. package/ai-dlc-skill/components/backend.md +34 -0
  14. package/ai-dlc-skill/components/desktop.md +30 -0
  15. package/ai-dlc-skill/components/mya.md +29 -0
  16. package/ai-dlc-skill/components/native.md +30 -0
  17. package/ai-dlc-skill/components/tta.md +29 -0
  18. package/ai-dlc-skill/components/web.md +31 -0
  19. package/ai-dlc-skill/components/wxa.md +29 -0
  20. package/ai-dlc-skill/contracts/README.md +25 -0
  21. package/ai-dlc-skill/core/adaptive-flow.md +36 -0
  22. package/ai-dlc-skill/core/security.md +29 -0
  23. package/ai-dlc-skill/cross-tool/cline/export.sh +31 -0
  24. package/ai-dlc-skill/cross-tool/copilot/export.sh +47 -0
  25. package/ai-dlc-skill/cross-tool/cursor/export.sh +32 -0
  26. package/ai-dlc-skill/cross-tool/export.sh +22 -0
  27. package/ai-dlc-skill/cross-tool/onecode/config.yaml.tpl +17 -0
  28. package/ai-dlc-skill/cross-tool/onecode/export-skills.sh +31 -0
  29. package/ai-dlc-skill/cross-tool/onecode/export.sh +48 -0
  30. package/ai-dlc-skill/phases/deliver/entry.md +26 -0
  31. package/ai-dlc-skill/phases/deliver/lifecycle.md +185 -0
  32. package/ai-dlc-skill/phases/deliver/prompt.md +40 -0
  33. package/ai-dlc-skill/phases/deliver/rules.md +61 -0
  34. package/ai-dlc-skill/phases/plan/entry.md +25 -0
  35. package/ai-dlc-skill/phases/plan/lifecycle.md +204 -0
  36. package/ai-dlc-skill/phases/plan/prompt.md +38 -0
  37. package/ai-dlc-skill/phases/plan/rules.md +25 -0
  38. package/ai-dlc-skill/phases/understand/entry.md +26 -0
  39. package/ai-dlc-skill/phases/understand/lifecycle.md +184 -0
  40. package/ai-dlc-skill/phases/understand/prompt.md +41 -0
  41. package/ai-dlc-skill/phases/understand/rules.md +37 -0
  42. package/ai-dlc-skill/phases/verify/entry.md +26 -0
  43. package/ai-dlc-skill/phases/verify/lifecycle.md +192 -0
  44. package/ai-dlc-skill/phases/verify/prompt.md +43 -0
  45. package/ai-dlc-skill/phases/verify/rules.md +73 -0
  46. package/ai-dlc-skill/practices/bdd.md +91 -0
  47. package/ai-dlc-skill/practices/sdd.md +84 -0
  48. package/ai-dlc-skill/practices/tdd.md +95 -0
  49. package/ai-dlc-skill/providers/README.md +114 -0
  50. package/ai-dlc-skill/providers/aliyun/deployment.yaml +66 -0
  51. package/ai-dlc-skill/providers/aliyun/preview.yaml +84 -0
  52. package/ai-dlc-skill/providers/aliyun/provider.yaml +160 -0
  53. package/ai-dlc-skill/providers/tcb/deployment.yaml +69 -0
  54. package/ai-dlc-skill/providers/tcb/preview.yaml +83 -0
  55. package/ai-dlc-skill/providers/tcb/provider.yaml +172 -0
  56. package/ai-dlc-skill/requirements.md +140 -0
  57. package/ai-dlc-skill/skill.yaml +147 -0
  58. package/ai-dlc-skill/templates/integration/CHANGELOG.md +41 -0
  59. package/ai-dlc-skill/templates/integration/asyncapi.yaml +43 -0
  60. package/ai-dlc-skill/templates/integration/contract-diff.md +63 -0
  61. package/ai-dlc-skill/templates/integration/contract-spec.md +49 -0
  62. package/ai-dlc-skill/templates/integration/openapi.yaml +18 -0
  63. package/ai-dlc-skill/templates/integration/runtime-contract.yaml +36 -0
  64. package/ai-dlc-skill/templates/plan/design.md +49 -0
  65. package/ai-dlc-skill/templates/plan/task-list.md +32 -0
  66. package/ai-dlc-skill/templates/project/README.md +127 -0
  67. package/ai-dlc-skill/templates/project/template.md +119 -0
  68. package/ai-dlc-skill/templates/understand/feature.feature +23 -0
  69. package/ai-dlc-skill/templates/understand/intent.md +29 -0
  70. package/ai-dlc-skill/templates/understand/spec-delta.md +47 -0
  71. package/ai-dlc-skill/templates/verify/test-unit.py +27 -0
  72. package/ai-dlc-skill/walkthrough/collect.sh +50 -0
  73. package/ai-dlc-skill/walkthrough/template.md +54 -0
  74. package/ai-dlc-skill/workflows/ai-dlc.yaml +110 -0
  75. package/ai-dlc-skill/workflows/deployment.yaml +199 -0
  76. package/cdh/__init__.py +0 -0
  77. package/cdh/__main__.py +4 -0
  78. package/cdh/__pycache__/__init__.cpython-313.pyc +0 -0
  79. package/cdh/__pycache__/__init__.cpython-314.pyc +0 -0
  80. package/cdh/__pycache__/cli.cpython-314.pyc +0 -0
  81. package/cdh/__pycache__/scaffold.cpython-313.pyc +0 -0
  82. package/cdh/__pycache__/scaffold.cpython-314.pyc +0 -0
  83. package/cdh/cli.py +361 -0
  84. package/cdh/scaffold.py +312 -0
  85. package/cli.js +2 -0
  86. package/onecode/__init__.py +1 -0
  87. package/onecode/__main__.py +4 -0
  88. package/onecode/__pycache__/__init__.cpython-314.pyc +0 -0
  89. package/onecode/__pycache__/cli.cpython-314.pyc +0 -0
  90. package/onecode/__pycache__/commands.cpython-314.pyc +0 -0
  91. package/onecode/__pycache__/config.cpython-314.pyc +0 -0
  92. package/onecode/__pycache__/config_screen.cpython-314.pyc +0 -0
  93. package/onecode/__pycache__/mock_data.cpython-314.pyc +0 -0
  94. package/onecode/agent/__init__.py +0 -0
  95. package/onecode/agent/__pycache__/__init__.cpython-314.pyc +0 -0
  96. package/onecode/agent/__pycache__/cdh_agent_acp.cpython-313.pyc +0 -0
  97. package/onecode/agent/__pycache__/cdh_agent_acp.cpython-314.pyc +0 -0
  98. package/onecode/agent/__pycache__/cdh_loader.cpython-314.pyc +0 -0
  99. package/onecode/agent/__pycache__/context.cpython-314.pyc +0 -0
  100. package/onecode/agent/__pycache__/engine.cpython-314.pyc +0 -0
  101. package/onecode/agent/__pycache__/harness_skill.cpython-314.pyc +0 -0
  102. package/onecode/agent/__pycache__/hooks.cpython-314.pyc +0 -0
  103. package/onecode/agent/__pycache__/onecode_agent_acp.cpython-314.pyc +0 -0
  104. package/onecode/agent/__pycache__/permissions.cpython-314.pyc +0 -0
  105. package/onecode/agent/__pycache__/permissions_store.cpython-314.pyc +0 -0
  106. package/onecode/agent/__pycache__/session.cpython-314.pyc +0 -0
  107. package/onecode/agent/agents/__init__.py +29 -0
  108. package/onecode/agent/agents/__pycache__/__init__.cpython-314.pyc +0 -0
  109. package/onecode/agent/agents/__pycache__/types.cpython-314.pyc +0 -0
  110. package/onecode/agent/agents/types.py +662 -0
  111. package/onecode/agent/attachments.py +113 -0
  112. package/onecode/agent/cdh_loader.py +262 -0
  113. package/onecode/agent/context.py +338 -0
  114. package/onecode/agent/engine.py +1894 -0
  115. package/onecode/agent/hooks.py +86 -0
  116. package/onecode/agent/onecode_agent_acp.py +2203 -0
  117. package/onecode/agent/permissions.py +151 -0
  118. package/onecode/agent/permissions_store.py +62 -0
  119. package/onecode/agent/session.py +186 -0
  120. package/onecode/agent/snapshot.py +145 -0
  121. package/onecode/agent/tools/__init__.py +17 -0
  122. package/onecode/agent/tools/__pycache__/__init__.cpython-314.pyc +0 -0
  123. package/onecode/agent/tools/__pycache__/agent_tools.cpython-314.pyc +0 -0
  124. package/onecode/agent/tools/__pycache__/apply_patch_tool.cpython-314.pyc +0 -0
  125. package/onecode/agent/tools/__pycache__/bash_tool.cpython-314.pyc +0 -0
  126. package/onecode/agent/tools/__pycache__/communication_tools.cpython-314.pyc +0 -0
  127. package/onecode/agent/tools/__pycache__/config_tool.cpython-314.pyc +0 -0
  128. package/onecode/agent/tools/__pycache__/cron_tools.cpython-314.pyc +0 -0
  129. package/onecode/agent/tools/__pycache__/errors.cpython-314.pyc +0 -0
  130. package/onecode/agent/tools/__pycache__/file_ops.cpython-314.pyc +0 -0
  131. package/onecode/agent/tools/__pycache__/file_tools.cpython-314.pyc +0 -0
  132. package/onecode/agent/tools/__pycache__/git_tools.cpython-314.pyc +0 -0
  133. package/onecode/agent/tools/__pycache__/lsp_tools.cpython-314.pyc +0 -0
  134. package/onecode/agent/tools/__pycache__/mcp_tools.cpython-314.pyc +0 -0
  135. package/onecode/agent/tools/__pycache__/permission_handler.cpython-314.pyc +0 -0
  136. package/onecode/agent/tools/__pycache__/permissions.cpython-314.pyc +0 -0
  137. package/onecode/agent/tools/__pycache__/protocol.cpython-314.pyc +0 -0
  138. package/onecode/agent/tools/__pycache__/registry.cpython-314.pyc +0 -0
  139. package/onecode/agent/tools/__pycache__/sandbox.cpython-314.pyc +0 -0
  140. package/onecode/agent/tools/__pycache__/schema_validation.cpython-314.pyc +0 -0
  141. package/onecode/agent/tools/__pycache__/skill_tools.cpython-314.pyc +0 -0
  142. package/onecode/agent/tools/__pycache__/task_tools.cpython-314.pyc +0 -0
  143. package/onecode/agent/tools/__pycache__/web_tools.cpython-314.pyc +0 -0
  144. package/onecode/agent/tools/agent_tools.py +134 -0
  145. package/onecode/agent/tools/apply_patch_tool.py +144 -0
  146. package/onecode/agent/tools/bash_tool.py +91 -0
  147. package/onecode/agent/tools/communication_tools.py +149 -0
  148. package/onecode/agent/tools/config_tool.py +130 -0
  149. package/onecode/agent/tools/cron_tools.py +179 -0
  150. package/onecode/agent/tools/errors.py +17 -0
  151. package/onecode/agent/tools/file_ops.py +187 -0
  152. package/onecode/agent/tools/file_tools.py +226 -0
  153. package/onecode/agent/tools/git_tools.py +88 -0
  154. package/onecode/agent/tools/lsp_tools.py +404 -0
  155. package/onecode/agent/tools/mcp_tools.py +129 -0
  156. package/onecode/agent/tools/permission_handler.py +46 -0
  157. package/onecode/agent/tools/permissions.py +77 -0
  158. package/onecode/agent/tools/protocol.py +20 -0
  159. package/onecode/agent/tools/registry.py +134 -0
  160. package/onecode/agent/tools/sandbox.py +264 -0
  161. package/onecode/agent/tools/schema_validation.py +100 -0
  162. package/onecode/agent/tools/skill_tools.py +52 -0
  163. package/onecode/agent/tools/task_tools.py +249 -0
  164. package/onecode/agent/tools/web_tools.py +170 -0
  165. package/onecode/builtin_skills/git/SKILL.md +64 -0
  166. package/onecode/builtin_skills/git/skill.yaml +7 -0
  167. package/onecode/builtin_skills/shell/SKILL.md +58 -0
  168. package/onecode/builtin_skills/shell/skill.yaml +8 -0
  169. package/onecode/cli.py +755 -0
  170. package/onecode/cloud/__init__.py +0 -0
  171. package/onecode/cloud/base.py +20 -0
  172. package/onecode/codebase/__init__.py +69 -0
  173. package/onecode/codebase/__pycache__/__init__.cpython-314.pyc +0 -0
  174. package/onecode/codebase/__pycache__/chunker.cpython-314.pyc +0 -0
  175. package/onecode/codebase/__pycache__/config.cpython-314.pyc +0 -0
  176. package/onecode/codebase/__pycache__/indexer.cpython-314.pyc +0 -0
  177. package/onecode/codebase/__pycache__/retriever.cpython-314.pyc +0 -0
  178. package/onecode/codebase/__pycache__/storage.cpython-314.pyc +0 -0
  179. package/onecode/codebase/__pycache__/tools.cpython-314.pyc +0 -0
  180. package/onecode/codebase/chunker.py +130 -0
  181. package/onecode/codebase/indexer.py +136 -0
  182. package/onecode/codebase/retriever.py +192 -0
  183. package/onecode/codebase/storage.py +130 -0
  184. package/onecode/codebase/tools.py +59 -0
  185. package/onecode/commands.py +276 -0
  186. package/onecode/config.py +285 -0
  187. package/onecode/config_screen.py +594 -0
  188. package/onecode/cron/__init__.py +3 -0
  189. package/onecode/lsp/__init__.py +3 -0
  190. package/onecode/mcp/__init__.py +4 -0
  191. package/onecode/mcp/__pycache__/__init__.cpython-314.pyc +0 -0
  192. package/onecode/mcp/__pycache__/client.cpython-314.pyc +0 -0
  193. package/onecode/mcp/__pycache__/manager.cpython-314.pyc +0 -0
  194. package/onecode/mcp/client.py +193 -0
  195. package/onecode/mcp/manager.py +191 -0
  196. package/onecode/memory/__init__.py +95 -0
  197. package/onecode/memory/__pycache__/__init__.cpython-314.pyc +0 -0
  198. package/onecode/memory/__pycache__/backend.cpython-314.pyc +0 -0
  199. package/onecode/memory/__pycache__/offload.cpython-314.pyc +0 -0
  200. package/onecode/memory/__pycache__/pyramid.cpython-314.pyc +0 -0
  201. package/onecode/memory/__pycache__/recall.cpython-314.pyc +0 -0
  202. package/onecode/memory/__pycache__/symbolic.cpython-314.pyc +0 -0
  203. package/onecode/memory/backend.py +185 -0
  204. package/onecode/memory/offload.py +114 -0
  205. package/onecode/memory/pyramid.py +202 -0
  206. package/onecode/memory/recall.py +168 -0
  207. package/onecode/memory/symbolic.py +164 -0
  208. package/onecode/models/__init__.py +4 -0
  209. package/onecode/models/__pycache__/__init__.cpython-314.pyc +0 -0
  210. package/onecode/models/__pycache__/errors.cpython-314.pyc +0 -0
  211. package/onecode/models/__pycache__/messages.cpython-314.pyc +0 -0
  212. package/onecode/models/__pycache__/provider.cpython-314.pyc +0 -0
  213. package/onecode/models/__pycache__/registry.cpython-314.pyc +0 -0
  214. package/onecode/models/errors.py +94 -0
  215. package/onecode/models/messages.py +662 -0
  216. package/onecode/models/provider.py +450 -0
  217. package/onecode/models/providers/__init__.py +17 -0
  218. package/onecode/models/providers/__pycache__/__init__.cpython-314.pyc +0 -0
  219. package/onecode/models/providers/__pycache__/anthropic_provider.cpython-314.pyc +0 -0
  220. package/onecode/models/providers/__pycache__/deepseek_provider.cpython-314.pyc +0 -0
  221. package/onecode/models/providers/__pycache__/glm_provider.cpython-314.pyc +0 -0
  222. package/onecode/models/providers/__pycache__/minimax_provider.cpython-314.pyc +0 -0
  223. package/onecode/models/providers/__pycache__/minimaxi_provider.cpython-314.pyc +0 -0
  224. package/onecode/models/providers/__pycache__/mock_provider.cpython-314.pyc +0 -0
  225. package/onecode/models/providers/__pycache__/ollama_provider.cpython-314.pyc +0 -0
  226. package/onecode/models/providers/__pycache__/openai_provider.cpython-314.pyc +0 -0
  227. package/onecode/models/providers/anthropic_provider.py +224 -0
  228. package/onecode/models/providers/deepseek_provider.py +210 -0
  229. package/onecode/models/providers/glm_provider.py +154 -0
  230. package/onecode/models/providers/minimax_provider.py +154 -0
  231. package/onecode/models/providers/minimaxi_provider.py +363 -0
  232. package/onecode/models/providers/ollama_provider.py +96 -0
  233. package/onecode/models/providers/openai_provider.py +233 -0
  234. package/onecode/models/registry.py +212 -0
  235. package/onecode/server/__init__.py +3 -0
  236. package/onecode/server/app.py +224 -0
  237. package/onecode/skills/__init__.py +11 -0
  238. package/onecode/skills/__pycache__/__init__.cpython-314.pyc +0 -0
  239. package/onecode/skills/__pycache__/argument_substitution.cpython-314.pyc +0 -0
  240. package/onecode/skills/__pycache__/create.cpython-314.pyc +0 -0
  241. package/onecode/skills/__pycache__/frontmatter.cpython-314.pyc +0 -0
  242. package/onecode/skills/__pycache__/loader.cpython-314.pyc +0 -0
  243. package/onecode/skills/__pycache__/manager.cpython-314.pyc +0 -0
  244. package/onecode/skills/__pycache__/model.cpython-314.pyc +0 -0
  245. package/onecode/skills/argument_substitution.py +39 -0
  246. package/onecode/skills/create.py +56 -0
  247. package/onecode/skills/frontmatter.py +33 -0
  248. package/onecode/skills/loader.py +157 -0
  249. package/onecode/skills/manager.py +75 -0
  250. package/onecode/skills/model.py +43 -0
  251. package/onecode/storage/__init__.py +0 -0
  252. package/onecode/storage/activity.py +64 -0
  253. package/onecode/storage/project.py +54 -0
  254. package/onecode/storage/session.py +113 -0
  255. package/onecode/tasks/__init__.py +26 -0
  256. package/onecode/tasks/manager.py +219 -0
  257. package/onecode/tasks/models.py +240 -0
  258. package/onecode/trace/__init__.py +0 -0
  259. package/onecode/trace/tracer.py +174 -0
  260. package/onecode/utils/__init__.py +0 -0
  261. package/onecode/utils/helpers.py +20 -0
  262. package/onecode-cli.js +2 -0
  263. package/package.json +32 -0
  264. package/pyproject.toml +60 -0
  265. package/run.js +86 -0
  266. package/tui/__init__.py +46 -0
  267. package/tui/__main__.py +4 -0
  268. package/tui/__pycache__/__init__.cpython-314.pyc +0 -0
  269. package/tui/__pycache__/_loop.cpython-314.pyc +0 -0
  270. package/tui/__pycache__/_path_fuzzy_search.cpython-314.pyc +0 -0
  271. package/tui/__pycache__/_path_match.cpython-314.pyc +0 -0
  272. package/tui/__pycache__/agent.cpython-314.pyc +0 -0
  273. package/tui/__pycache__/agent_schema.cpython-314.pyc +0 -0
  274. package/tui/__pycache__/agents.cpython-314.pyc +0 -0
  275. package/tui/__pycache__/answer.cpython-314.pyc +0 -0
  276. package/tui/__pycache__/app.cpython-314.pyc +0 -0
  277. package/tui/__pycache__/atomic.cpython-314.pyc +0 -0
  278. package/tui/__pycache__/complete.cpython-314.pyc +0 -0
  279. package/tui/__pycache__/constants.cpython-314.pyc +0 -0
  280. package/tui/__pycache__/conversation_markdown.cpython-314.pyc +0 -0
  281. package/tui/__pycache__/danger.cpython-314.pyc +0 -0
  282. package/tui/__pycache__/db.cpython-314.pyc +0 -0
  283. package/tui/__pycache__/dec.cpython-314.pyc +0 -0
  284. package/tui/__pycache__/directory.cpython-314.pyc +0 -0
  285. package/tui/__pycache__/directory_suggester.cpython-314.pyc +0 -0
  286. package/tui/__pycache__/directory_watcher.cpython-314.pyc +0 -0
  287. package/tui/__pycache__/format_path.cpython-314.pyc +0 -0
  288. package/tui/__pycache__/fuzzy.cpython-314.pyc +0 -0
  289. package/tui/__pycache__/fuzzy_index.cpython-314.pyc +0 -0
  290. package/tui/__pycache__/history.cpython-314.pyc +0 -0
  291. package/tui/__pycache__/jsonrpc.cpython-314.pyc +0 -0
  292. package/tui/__pycache__/menus.cpython-314.pyc +0 -0
  293. package/tui/__pycache__/message_log.cpython-314.pyc +0 -0
  294. package/tui/__pycache__/messages.cpython-314.pyc +0 -0
  295. package/tui/__pycache__/path_complete.cpython-314.pyc +0 -0
  296. package/tui/__pycache__/path_filter.cpython-314.pyc +0 -0
  297. package/tui/__pycache__/paths.cpython-314.pyc +0 -0
  298. package/tui/__pycache__/pill.cpython-314.pyc +0 -0
  299. package/tui/__pycache__/platform_commands.cpython-314.pyc +0 -0
  300. package/tui/__pycache__/protocol.cpython-314.pyc +0 -0
  301. package/tui/__pycache__/session_tracker.cpython-314.pyc +0 -0
  302. package/tui/__pycache__/settings.cpython-314.pyc +0 -0
  303. package/tui/__pycache__/settings_schema.cpython-314.pyc +0 -0
  304. package/tui/__pycache__/shell.cpython-314.pyc +0 -0
  305. package/tui/__pycache__/shell_read.cpython-314.pyc +0 -0
  306. package/tui/__pycache__/slash_command.cpython-314.pyc +0 -0
  307. package/tui/__pycache__/version.cpython-314.pyc +0 -0
  308. package/tui/_loop.py +86 -0
  309. package/tui/_path_fuzzy_search.py +48 -0
  310. package/tui/_path_match.py +141 -0
  311. package/tui/about.py +95 -0
  312. package/tui/acp/__pycache__/agent.cpython-314.pyc +0 -0
  313. package/tui/acp/__pycache__/api.cpython-314.pyc +0 -0
  314. package/tui/acp/__pycache__/encode_tool_call_id.cpython-314.pyc +0 -0
  315. package/tui/acp/__pycache__/messages.cpython-314.pyc +0 -0
  316. package/tui/acp/__pycache__/prompt.cpython-314.pyc +0 -0
  317. package/tui/acp/__pycache__/protocol.cpython-314.pyc +0 -0
  318. package/tui/acp/agent.py +886 -0
  319. package/tui/acp/api.py +55 -0
  320. package/tui/acp/encode_tool_call_id.py +12 -0
  321. package/tui/acp/messages.py +190 -0
  322. package/tui/acp/prompt.py +110 -0
  323. package/tui/acp/protocol.py +462 -0
  324. package/tui/agent.py +70 -0
  325. package/tui/agent_schema.py +76 -0
  326. package/tui/agents.py +45 -0
  327. package/tui/ansi/__init__.py +1 -0
  328. package/tui/ansi/__pycache__/__init__.cpython-314.pyc +0 -0
  329. package/tui/ansi/__pycache__/_ansi.cpython-314.pyc +0 -0
  330. package/tui/ansi/__pycache__/_ansi_colors.cpython-314.pyc +0 -0
  331. package/tui/ansi/__pycache__/_control_codes.cpython-314.pyc +0 -0
  332. package/tui/ansi/__pycache__/_keys.cpython-314.pyc +0 -0
  333. package/tui/ansi/__pycache__/_sgr_styles.cpython-314.pyc +0 -0
  334. package/tui/ansi/__pycache__/_stream_parser.cpython-314.pyc +0 -0
  335. package/tui/ansi/_ansi.py +1689 -0
  336. package/tui/ansi/_ansi_colors.py +264 -0
  337. package/tui/ansi/_control_codes.py +37 -0
  338. package/tui/ansi/_keys.py +251 -0
  339. package/tui/ansi/_sgr_styles.py +64 -0
  340. package/tui/ansi/_stream_parser.py +418 -0
  341. package/tui/answer.py +12 -0
  342. package/tui/app.py +1236 -0
  343. package/tui/atomic.py +37 -0
  344. package/tui/cli.py +313 -0
  345. package/tui/code_analyze.py +27 -0
  346. package/tui/complete.py +38 -0
  347. package/tui/constants.py +61 -0
  348. package/tui/conversation_markdown.py +30 -0
  349. package/tui/danger.py +372 -0
  350. package/tui/data/agents/ampcode.com.toml +51 -0
  351. package/tui/data/agents/augmentcode.com.toml +40 -0
  352. package/tui/data/agents/cdh.cloud-dev-harness.toml +22 -0
  353. package/tui/data/agents/claude.com.toml +41 -0
  354. package/tui/data/agents/copilot.github.com.toml +29 -0
  355. package/tui/data/agents/crowai.dev.toml +35 -0
  356. package/tui/data/agents/cursor.com.toml +38 -0
  357. package/tui/data/agents/deepagents.langchain.com +33 -0
  358. package/tui/data/agents/docker.com.toml +59 -0
  359. package/tui/data/agents/geminicli.com.toml +28 -0
  360. package/tui/data/agents/goose.ai.toml +51 -0
  361. package/tui/data/agents/inference.huggingface.co.toml +41 -0
  362. package/tui/data/agents/kimi.com.toml +35 -0
  363. package/tui/data/agents/kiro.dev.toml +42 -0
  364. package/tui/data/agents/openai.com.toml +53 -0
  365. package/tui/data/agents/openclaw.ai.toml +37 -0
  366. package/tui/data/agents/opencode.ai.toml +39 -0
  367. package/tui/data/agents/openhands.dev.toml +45 -0
  368. package/tui/data/agents/stakpak.dev.toml +61 -0
  369. package/tui/data/agents/vibe.mistral.ai.toml +30 -0
  370. package/tui/data/agents/vtcode.dev.toml +62 -0
  371. package/tui/data/images/frog.png +0 -0
  372. package/tui/data/sounds/question.wav +0 -0
  373. package/tui/data/sounds/turn-over.wav +0 -0
  374. package/tui/db.py +190 -0
  375. package/tui/dec.py +332 -0
  376. package/tui/directory.py +234 -0
  377. package/tui/directory_suggester.py +93 -0
  378. package/tui/directory_watcher.py +197 -0
  379. package/tui/format_path.py +25 -0
  380. package/tui/fuzzy.py +140 -0
  381. package/tui/fuzzy_index.py +275 -0
  382. package/tui/gist.py +33 -0
  383. package/tui/history.py +138 -0
  384. package/tui/jsonrpc.py +578 -0
  385. package/tui/menus.py +14 -0
  386. package/tui/message_log.py +69 -0
  387. package/tui/messages.py +139 -0
  388. package/tui/option_content.py +51 -0
  389. package/tui/os.py +0 -0
  390. package/tui/path_complete.py +217 -0
  391. package/tui/path_filter.py +124 -0
  392. package/tui/paths.py +71 -0
  393. package/tui/pill.py +23 -0
  394. package/tui/platform_commands.py +52 -0
  395. package/tui/prompt/__pycache__/extract.cpython-314.pyc +0 -0
  396. package/tui/prompt/__pycache__/resource.cpython-314.pyc +0 -0
  397. package/tui/prompt/extract.py +19 -0
  398. package/tui/prompt/resource.py +68 -0
  399. package/tui/protocol.py +28 -0
  400. package/tui/screens/__init__.py +0 -0
  401. package/tui/screens/__pycache__/__init__.cpython-314.pyc +0 -0
  402. package/tui/screens/__pycache__/log.cpython-314.pyc +0 -0
  403. package/tui/screens/__pycache__/main.cpython-314.pyc +0 -0
  404. package/tui/screens/__pycache__/projects_screen.cpython-314.pyc +0 -0
  405. package/tui/screens/__pycache__/sessions.cpython-314.pyc +0 -0
  406. package/tui/screens/__pycache__/settings.cpython-314.pyc +0 -0
  407. package/tui/screens/__pycache__/store.cpython-314.pyc +0 -0
  408. package/tui/screens/action_modal.py +94 -0
  409. package/tui/screens/agent_modal.py +175 -0
  410. package/tui/screens/command_edit_modal.py +74 -0
  411. package/tui/screens/log.py +553 -0
  412. package/tui/screens/main.py +411 -0
  413. package/tui/screens/main.tcss +655 -0
  414. package/tui/screens/permissions.py +404 -0
  415. package/tui/screens/permissions.tcss +72 -0
  416. package/tui/screens/projects.tcss +66 -0
  417. package/tui/screens/projects_app.py +319 -0
  418. package/tui/screens/projects_screen.py +142 -0
  419. package/tui/screens/session_resume_modal.py +172 -0
  420. package/tui/screens/session_resume_modal.tcss +35 -0
  421. package/tui/screens/sessions.py +166 -0
  422. package/tui/screens/sessions.tcss +119 -0
  423. package/tui/screens/settings.py +254 -0
  424. package/tui/screens/settings.tcss +101 -0
  425. package/tui/screens/store.py +500 -0
  426. package/tui/screens/store.tcss +274 -0
  427. package/tui/screens/tool_content_screen.py +59 -0
  428. package/tui/session_tracker.py +120 -0
  429. package/tui/settings.py +353 -0
  430. package/tui/settings_schema.py +431 -0
  431. package/tui/shell.py +304 -0
  432. package/tui/shell_read.py +42 -0
  433. package/tui/slash_command.py +34 -0
  434. package/tui/tui.tcss +246 -0
  435. package/tui/twitter.py +38 -0
  436. package/tui/version.py +80 -0
  437. package/tui/visuals/__init__.py +0 -0
  438. package/tui/visuals/__pycache__/__init__.cpython-314.pyc +0 -0
  439. package/tui/visuals/__pycache__/columns.cpython-314.pyc +0 -0
  440. package/tui/visuals/columns.py +273 -0
  441. package/tui/widgets/__init__.py +0 -0
  442. package/tui/widgets/__pycache__/__init__.cpython-314.pyc +0 -0
  443. package/tui/widgets/__pycache__/acp_content.cpython-314.pyc +0 -0
  444. package/tui/widgets/__pycache__/agent_response.cpython-314.pyc +0 -0
  445. package/tui/widgets/__pycache__/agent_thought.cpython-314.pyc +0 -0
  446. package/tui/widgets/__pycache__/ask_user.cpython-314.pyc +0 -0
  447. package/tui/widgets/__pycache__/condensed_path.cpython-314.pyc +0 -0
  448. package/tui/widgets/__pycache__/context_stats.cpython-314.pyc +0 -0
  449. package/tui/widgets/__pycache__/conversation.cpython-313.pyc +0 -0
  450. package/tui/widgets/__pycache__/conversation.cpython-314.pyc +0 -0
  451. package/tui/widgets/__pycache__/diff_view.cpython-314.pyc +0 -0
  452. package/tui/widgets/__pycache__/directory_input.cpython-314.pyc +0 -0
  453. package/tui/widgets/__pycache__/flash.cpython-314.pyc +0 -0
  454. package/tui/widgets/__pycache__/future_text.cpython-314.pyc +0 -0
  455. package/tui/widgets/__pycache__/grid_select.cpython-314.pyc +0 -0
  456. package/tui/widgets/__pycache__/highlighted_textarea.cpython-314.pyc +0 -0
  457. package/tui/widgets/__pycache__/mandelbrot.cpython-314.pyc +0 -0
  458. package/tui/widgets/__pycache__/markdown_note.cpython-314.pyc +0 -0
  459. package/tui/widgets/__pycache__/menu.cpython-314.pyc +0 -0
  460. package/tui/widgets/__pycache__/modified_files.cpython-314.pyc +0 -0
  461. package/tui/widgets/__pycache__/non_selectable_label.cpython-314.pyc +0 -0
  462. package/tui/widgets/__pycache__/note.cpython-314.pyc +0 -0
  463. package/tui/widgets/__pycache__/path_search.cpython-314.pyc +0 -0
  464. package/tui/widgets/__pycache__/plan.cpython-314.pyc +0 -0
  465. package/tui/widgets/__pycache__/project_directory_tree.cpython-314.pyc +0 -0
  466. package/tui/widgets/__pycache__/project_grid_select.cpython-314.pyc +0 -0
  467. package/tui/widgets/__pycache__/project_summary.cpython-314.pyc +0 -0
  468. package/tui/widgets/__pycache__/prompt.cpython-314.pyc +0 -0
  469. package/tui/widgets/__pycache__/question.cpython-314.pyc +0 -0
  470. package/tui/widgets/__pycache__/session_grid_select.cpython-314.pyc +0 -0
  471. package/tui/widgets/__pycache__/session_summary.cpython-314.pyc +0 -0
  472. package/tui/widgets/__pycache__/session_tabs.cpython-314.pyc +0 -0
  473. package/tui/widgets/__pycache__/shell_result.cpython-314.pyc +0 -0
  474. package/tui/widgets/__pycache__/shell_terminal.cpython-314.pyc +0 -0
  475. package/tui/widgets/__pycache__/side_bar.cpython-314.pyc +0 -0
  476. package/tui/widgets/__pycache__/slash_complete.cpython-314.pyc +0 -0
  477. package/tui/widgets/__pycache__/strike_text.cpython-314.pyc +0 -0
  478. package/tui/widgets/__pycache__/subagent.cpython-314.pyc +0 -0
  479. package/tui/widgets/__pycache__/terminal.cpython-313.pyc +0 -0
  480. package/tui/widgets/__pycache__/terminal.cpython-314.pyc +0 -0
  481. package/tui/widgets/__pycache__/throbber.cpython-314.pyc +0 -0
  482. package/tui/widgets/__pycache__/tool_call.cpython-313.pyc +0 -0
  483. package/tui/widgets/__pycache__/tool_call.cpython-314.pyc +0 -0
  484. package/tui/widgets/__pycache__/user_input.cpython-314.pyc +0 -0
  485. package/tui/widgets/acp_content.py +70 -0
  486. package/tui/widgets/agent_response.py +77 -0
  487. package/tui/widgets/agent_thought.py +168 -0
  488. package/tui/widgets/ask_user.py +264 -0
  489. package/tui/widgets/command_pane.py +223 -0
  490. package/tui/widgets/condensed_path.py +115 -0
  491. package/tui/widgets/context_stats.py +66 -0
  492. package/tui/widgets/conversation.py +2441 -0
  493. package/tui/widgets/danger_warning.py +65 -0
  494. package/tui/widgets/diff_view.py +59 -0
  495. package/tui/widgets/directory_input.py +8 -0
  496. package/tui/widgets/flash.py +81 -0
  497. package/tui/widgets/future_text.py +126 -0
  498. package/tui/widgets/grid_select.py +234 -0
  499. package/tui/widgets/highlighted_textarea.py +180 -0
  500. package/tui/widgets/mandelbrot.py +294 -0
  501. package/tui/widgets/markdown_note.py +13 -0
  502. package/tui/widgets/menu.py +147 -0
  503. package/tui/widgets/modified_files.py +106 -0
  504. package/tui/widgets/non_selectable_label.py +5 -0
  505. package/tui/widgets/note.py +18 -0
  506. package/tui/widgets/path_search.py +492 -0
  507. package/tui/widgets/plan.py +263 -0
  508. package/tui/widgets/project_directory_tree.py +90 -0
  509. package/tui/widgets/project_grid_select.py +35 -0
  510. package/tui/widgets/project_summary.py +33 -0
  511. package/tui/widgets/prompt.py +795 -0
  512. package/tui/widgets/question.py +360 -0
  513. package/tui/widgets/session_grid_select.py +224 -0
  514. package/tui/widgets/session_summary.py +115 -0
  515. package/tui/widgets/session_tabs.py +188 -0
  516. package/tui/widgets/shell_result.py +35 -0
  517. package/tui/widgets/shell_terminal.py +40 -0
  518. package/tui/widgets/side_bar.py +114 -0
  519. package/tui/widgets/slash_complete.py +235 -0
  520. package/tui/widgets/strike_text.py +70 -0
  521. package/tui/widgets/subagent.py +87 -0
  522. package/tui/widgets/terminal.py +577 -0
  523. package/tui/widgets/terminal_tool.py +351 -0
  524. package/tui/widgets/throbber.py +100 -0
  525. package/tui/widgets/tool_call.py +368 -0
  526. package/tui/widgets/user_input.py +23 -0
  527. package/tui/widgets/version.py +5 -0
  528. package/tui/widgets/welcome.py +31 -0
@@ -0,0 +1,500 @@
1
+ import asyncio
2
+ from contextlib import suppress
3
+ from dataclasses import dataclass
4
+ from itertools import zip_longest
5
+ from pathlib import Path
6
+ from random import shuffle
7
+
8
+ from textual.binding import Binding
9
+ from textual.screen import Screen
10
+ from textual import events
11
+ from textual import work
12
+ from textual import getters
13
+ from textual import on
14
+ from textual.app import ComposeResult
15
+ from textual.content import Content
16
+ from textual.css.query import NoMatches
17
+ from textual.message import Message
18
+ from textual.reactive import reactive
19
+ from textual import containers
20
+ from textual import widgets
21
+
22
+ from tui.app import A2TUIApp
23
+ from tui.format_path import format_path
24
+ from tui import messages
25
+ from tui.pill import pill
26
+ from tui.widgets.directory_input import DirectoryInput
27
+ from tui.widgets.mandelbrot import Mandelbrot
28
+ from tui.widgets.condensed_path import CondensedPath
29
+ from tui.widgets.grid_select import GridSelect
30
+ from tui.agent_schema import Agent
31
+ from tui.agents import read_agents
32
+
33
+
34
+ @dataclass
35
+ class ChangeDirectory(Message):
36
+ path: str
37
+
38
+
39
+ class DirectoryDisplay(containers.HorizontalGroup):
40
+
41
+ BINDINGS = [("escape", "dismiss", "Dismiss")]
42
+
43
+ DEFAULT_CSS = """
44
+ DirectoryDisplay {
45
+ CondensedPath { display: block; }
46
+ DirectoryInput { display: none; }
47
+ &.-edit {
48
+ CondensedPath { display: none}
49
+ DirectoryInput { display: block; }
50
+ }
51
+ }
52
+ """
53
+
54
+ project_dir: reactive[Path] = reactive(Path)
55
+ path = reactive("")
56
+ edit = reactive(False, toggle_class="-edit")
57
+
58
+ directory_input = getters.query_one(DirectoryInput)
59
+ condensed_path = getters.query_one(CondensedPath)
60
+
61
+ def __init__(self, project_dir: Path) -> None:
62
+ super().__init__()
63
+ self.project_dir = project_dir
64
+ self.path = format_path(project_dir, directory=True)
65
+
66
+ def watch_project_dir(self, path: Path) -> None:
67
+ self.path = format_path(path, directory=True)
68
+
69
+ def focus(self, scroll_visible=True) -> "Self":
70
+ self.edit = True
71
+ self.directory_input.focus(scroll_visible=scroll_visible)
72
+ return self
73
+
74
+ @on(events.Click, "CondensedPath")
75
+ def on_click(self) -> None:
76
+ self.edit = True
77
+ self.directory_input.focus()
78
+
79
+ @on(events.DescendantBlur)
80
+ def on_blur(self):
81
+ self.action_dismiss()
82
+
83
+ @on(widgets.Input.Submitted)
84
+ def on_input_submitted(self, event: widgets.Input.Submitted) -> None:
85
+ path = Path(event.value).expanduser().resolve()
86
+ self.edit = False
87
+ if not path.is_dir():
88
+ self.notify(
89
+ f"Unable to change directory to {str(path)!r}",
90
+ title="Change directory",
91
+ severity="error",
92
+ )
93
+ return
94
+ self.condensed_path.path = format_path(path, directory=True)
95
+ self.post_message(ChangeDirectory(str(path)))
96
+
97
+ def action_dismiss(self) -> None:
98
+ self.edit = False
99
+ self.directory_input.value = self.path
100
+
101
+ def watch_edit(self, edit: bool) -> None:
102
+ if not edit and self.directory_input.has_focus:
103
+ self.directory_input.blur()
104
+
105
+ def compose(self) -> ComposeResult:
106
+ yield widgets.Label("📁 ")
107
+ yield CondensedPath(self.path, directory=True).data_bind(
108
+ path=DirectoryDisplay.path
109
+ ).with_tooltip("Project directory for new agent sessions (click to edit)")
110
+ yield DirectoryInput(self.path, select_on_focus=True, compact=True).data_bind(
111
+ value=DirectoryDisplay.path
112
+ )
113
+
114
+
115
+ class AgentItem(containers.VerticalGroup):
116
+ """An entry in the Agent grid select."""
117
+
118
+ def __init__(self, agent: Agent) -> None:
119
+ self._agent = agent
120
+ super().__init__()
121
+
122
+ @property
123
+ def agent(self) -> Agent:
124
+ return self._agent
125
+
126
+ def compose(self) -> ComposeResult:
127
+ agent = self._agent
128
+ with containers.Grid():
129
+ yield widgets.Label(agent["name"], id="name")
130
+ tag = pill(agent.get("type", "agent"), "$primary-muted 50%", "$text-primary")
131
+ yield widgets.Label(tag, id="type")
132
+ yield widgets.Label(agent.get("author_name", ""), id="author")
133
+ yield widgets.Static(agent.get("description", ""), id="description")
134
+
135
+
136
+ class LauncherItem(containers.VerticalGroup):
137
+ """An entry in the Launcher grid select."""
138
+
139
+ def __init__(self, digit: str, agent: Agent) -> None:
140
+ self._digit = digit
141
+ self._agent = agent
142
+ super().__init__()
143
+
144
+ @property
145
+ def agent(self) -> Agent:
146
+ return self._agent
147
+
148
+ def compose(self) -> ComposeResult:
149
+ agent = self._agent
150
+ with containers.HorizontalGroup():
151
+ if self._digit:
152
+ yield widgets.Label(self._digit, classes="digit")
153
+ with containers.VerticalGroup():
154
+ yield widgets.Label(agent["name"], id="name")
155
+ yield widgets.Label(agent.get("author_name", ""), id="author")
156
+ yield widgets.Static(agent.get("description", ""), id="description")
157
+
158
+
159
+ class Launcher(containers.VerticalGroup):
160
+ app = getters.app(A2TUIApp)
161
+ grid_select = getters.query_one("#launcher-grid-select", GridSelect)
162
+ DIGITS = "123456789ABCDEF"
163
+
164
+ def __init__(
165
+ self,
166
+ agents: dict[str, Agent],
167
+ *,
168
+ name: str | None = None,
169
+ id: str | None = None,
170
+ classes: str | None = None,
171
+ ) -> None:
172
+ self._agents = agents
173
+ super().__init__(name=name, id=id, classes=classes)
174
+
175
+ @property
176
+ def highlighted(self) -> int | None:
177
+ return self.grid_select.highlighted
178
+
179
+ @highlighted.setter
180
+ def highlighted(self, value: int) -> None:
181
+ self.grid_select.highlighted = value
182
+
183
+ def focus(self, scroll_visible: bool = True) -> "Self":
184
+ try:
185
+ self.grid_select.focus(scroll_visible=scroll_visible)
186
+ except NoMatches:
187
+ pass
188
+ return self
189
+
190
+ def compose(self) -> ComposeResult:
191
+ launcher_agents = list(
192
+ dict.fromkeys(
193
+ identity
194
+ for identity in self.app.settings.get(
195
+ "launcher.agents", str
196
+ ).splitlines()
197
+ if identity.strip()
198
+ )
199
+ )
200
+ agents = self._agents
201
+ self.set_class(not launcher_agents, "-empty")
202
+ if launcher_agents:
203
+ with GridSelect(
204
+ id="launcher-grid-select", min_column_width=32, max_column_width=32
205
+ ):
206
+ for digit, identity in zip_longest(self.DIGITS, launcher_agents):
207
+ if identity is None:
208
+ break
209
+ if identity in agents:
210
+ yield LauncherItem(digit or "", agents[identity])
211
+
212
+ if not launcher_agents:
213
+ yield widgets.Label("Choose your fighter below!", classes="no-agents")
214
+
215
+ def launch_highlighted(self) -> None:
216
+ self.grid_select.action_select()
217
+
218
+
219
+ class Container(containers.VerticalScroll):
220
+ BINDING_GROUP_TITLE = "View"
221
+
222
+ def allow_focus(self) -> bool:
223
+ """Only allow focus when we can scroll."""
224
+ return super().allow_focus() and self.show_vertical_scrollbar
225
+
226
+
227
+ class StoreScreen(Screen):
228
+ BINDING_GROUP_TITLE = "Screen"
229
+ CSS_PATH = "store.tcss"
230
+ FOCUS_GROUP = Binding.Group("Focus")
231
+
232
+ BINDINGS = [
233
+ Binding(
234
+ "tab",
235
+ "app.focus_next",
236
+ "Focus Next",
237
+ group=FOCUS_GROUP,
238
+ ),
239
+ Binding(
240
+ "shift+tab",
241
+ "app.focus_previous",
242
+ "Focus Previous",
243
+ group=FOCUS_GROUP,
244
+ ),
245
+ Binding(
246
+ "null",
247
+ "quick_launch",
248
+ "Quick launch",
249
+ key_display="1-9 a-f",
250
+ ),
251
+ Binding("ctrl+r", "resume", "Resume", tooltip="Resume a previous session"),
252
+ Binding(
253
+ "ctrl+d",
254
+ "directory",
255
+ "Directory",
256
+ tooltip="Change project directory",
257
+ ),
258
+ Binding("escape", "dismiss", "Dismiss"),
259
+ ]
260
+
261
+ agents_view = getters.query_one("#agents-view", GridSelect)
262
+ launcher = getters.query_one("#launcher", Launcher)
263
+ container = getters.query_one("#container", Container)
264
+
265
+ project_dir: reactive[Path] = reactive(Path)
266
+
267
+ app = getters.app(A2TUIApp)
268
+
269
+ @dataclass
270
+ class OpenAgentDetails(Message):
271
+ identity: str
272
+
273
+ def __init__(
274
+ self, name: str | None = None, id: str | None = None, classes: str | None = None
275
+ ):
276
+ self._agents: dict[str, Agent] = {}
277
+ super().__init__(name=name, id=id, classes=classes)
278
+ self.project_dir = self.app.project_dir
279
+
280
+ @property
281
+ def agents(self) -> dict[str, Agent]:
282
+ return self._agents
283
+
284
+ def compose(self) -> ComposeResult:
285
+ with containers.VerticalGroup(id="title-container"):
286
+ with containers.Grid(id="title-grid"):
287
+ yield Mandelbrot()
288
+ yield widgets.Label(self.get_info(), id="info")
289
+ yield DirectoryDisplay(self.project_dir).data_bind(
290
+ project_dir=StoreScreen.project_dir
291
+ )
292
+ yield Container(id="container", can_focus=False)
293
+ yield widgets.Footer()
294
+
295
+ def get_info(self) -> Content:
296
+ content = Content.assemble(
297
+ Content.from_markup("[bold]🐸 A2TUI[/]\n"),
298
+ Content.from_markup("[dim]Agent store - choose your fighter[/]"),
299
+ )
300
+ return content
301
+
302
+
303
+
304
+ def compose_agents(self) -> ComposeResult:
305
+ agents = self._agents
306
+
307
+ yield Launcher(agents, id="launcher")
308
+
309
+ ordered_agents = sorted(
310
+ agents.values(), key=lambda agent: agent.get("name", "").casefold()
311
+ )
312
+
313
+ recommended_agents = [
314
+ agent for agent in ordered_agents if agent.get("recommended", False)
315
+ ]
316
+ shuffle(recommended_agents)
317
+ if recommended_agents:
318
+ with containers.VerticalGroup(id="sponsored-agents", classes="recommended"):
319
+ yield widgets.Static(
320
+ "[$text-warning u]Recommended[/] [$text-secondary 100% i]Best of the bunch",
321
+ classes="heading",
322
+ )
323
+ with GridSelect(classes="agents-picker", min_column_width=40):
324
+ for agent in recommended_agents:
325
+ yield AgentItem(agent)
326
+
327
+ chat_bots = [
328
+ agent for agent in ordered_agents if agent.get("type") in {"chat", "assistant"}
329
+ ]
330
+ if chat_bots:
331
+ yield widgets.Static(
332
+ "[$text-warning u]Chat & Assistants[/]",
333
+ classes="heading",
334
+ )
335
+ with containers.VerticalGroup():
336
+ with GridSelect(classes="agents-picker", min_column_width=40):
337
+ for agent in chat_bots:
338
+ yield AgentItem(agent)
339
+
340
+ coding_agents = [agent for agent in ordered_agents if agent.get("type") == "coding"]
341
+ if coding_agents:
342
+ yield widgets.Static(
343
+ "[$text-warning u]Coding agents[/]",
344
+ classes="heading",
345
+ )
346
+ with containers.VerticalGroup():
347
+ with GridSelect(classes="agents-picker", min_column_width=40):
348
+ for agent in coding_agents:
349
+ yield AgentItem(agent)
350
+
351
+ def move_focus(self, direction: int) -> None:
352
+ if isinstance(self.focused, GridSelect):
353
+ focus_chain = list(self.query(GridSelect))
354
+ if self.focused in focus_chain:
355
+ index = focus_chain.index(self.focused)
356
+ new_focus = focus_chain[(index + direction) % len(focus_chain)]
357
+ if direction == -1:
358
+ new_focus.highlight_last()
359
+ else:
360
+ new_focus.highlight_first()
361
+ new_focus.focus(scroll_visible=False)
362
+
363
+ @on(GridSelect.LeaveUp)
364
+ def on_grid_select_leave_up(self, event: GridSelect.LeaveUp):
365
+ event.stop()
366
+ self.move_focus(-1)
367
+
368
+ @on(GridSelect.LeaveDown)
369
+ def on_grid_select_leave_down(self, event: GridSelect.LeaveUp):
370
+ event.stop()
371
+ self.move_focus(+1)
372
+
373
+ @on(GridSelect.Selected, ".agents-picker")
374
+ @work
375
+ async def on_grid_select_selected(self, event: GridSelect.Selected):
376
+ if not isinstance(event.widget, AgentItem):
377
+ return
378
+ assert isinstance(event.widget, AgentItem)
379
+ from tui.screens.agent_modal import AgentModal
380
+
381
+ modal_response = await self.app.push_screen_wait(AgentModal(event.widget.agent))
382
+ await self.app.save_settings()
383
+ if modal_response == "launch":
384
+ self.post_message(messages.LaunchAgent(event.widget.agent["identity"]))
385
+
386
+ @on(OpenAgentDetails)
387
+ @work
388
+ async def open_agent_detail(self, message: OpenAgentDetails) -> None:
389
+ from tui.screens.agent_modal import AgentModal
390
+
391
+ try:
392
+ agent = self._agents[message.identity]
393
+ except KeyError:
394
+ return
395
+ modal_response = await self.app.push_screen_wait(AgentModal(agent))
396
+ await self.app.save_settings()
397
+ if modal_response == "launch":
398
+ self.post_message(messages.LaunchAgent(agent["identity"]))
399
+
400
+ @on(GridSelect.Selected, "#launcher GridSelect")
401
+ @work
402
+ async def on_launcher_selected(self, event: GridSelect.Selected):
403
+ launcher_item = event.widget
404
+ assert isinstance(launcher_item, LauncherItem)
405
+
406
+ from tui.screens.agent_modal import AgentModal
407
+
408
+ modal_response = await self.app.push_screen_wait(
409
+ AgentModal(launcher_item.agent)
410
+ )
411
+ await self.app.save_settings()
412
+ if modal_response == "launch":
413
+ self.post_message(messages.LaunchAgent(launcher_item.agent["identity"]))
414
+
415
+ @on(ChangeDirectory)
416
+ def on_change_directory(self, event: ChangeDirectory) -> None:
417
+ self.project_dir = Path(event.path)
418
+ self.app.project_dir = self.project_dir
419
+
420
+ @work
421
+ async def on_mount(self) -> None:
422
+ self.app.settings_changed_signal.subscribe(self, self.setting_updated)
423
+ if not self.app.settings.get("launcher.agents", str).strip():
424
+ self.app.settings.set("launcher.agents", "cdh.cloud-dev-harness")
425
+ await self.app.save_settings()
426
+ try:
427
+ self._agents = await read_agents()
428
+ except Exception as error:
429
+ self.notify(
430
+ f"Failed to read agents data ({error})",
431
+ title="Agents data",
432
+ severity="error",
433
+ )
434
+ else:
435
+ await self.container.mount_compose(self.compose_agents())
436
+ with suppress(NoMatches):
437
+ first_grid = self.container.query(GridSelect).first()
438
+ first_grid.focus(scroll_visible=False)
439
+
440
+ def setting_updated(self, setting: tuple[str, object]) -> None:
441
+ key, value = setting
442
+ if key == "launcher.agents":
443
+ try:
444
+ launcher = self.launcher
445
+ except NoMatches:
446
+ return
447
+
448
+ async def _recompose():
449
+ await launcher.recompose()
450
+ try:
451
+ self.query(GridSelect).first().focus()
452
+ except Exception:
453
+ pass
454
+
455
+ asyncio.create_task(_recompose())
456
+
457
+ def on_key(self, event: events.Key) -> None:
458
+ if event.character is None:
459
+ return
460
+ LAUNCHER_KEYS = "123456789abcdef"
461
+
462
+ if event.character in LAUNCHER_KEYS:
463
+ launch_item_offset = LAUNCHER_KEYS.find(event.character)
464
+ try:
465
+ item = self.launcher.grid_select.children[launch_item_offset]
466
+ except (IndexError, NoMatches):
467
+ self.notify(
468
+ f"No agent on key [b]{LAUNCHER_KEYS[launch_item_offset]}",
469
+ title="Quick launch",
470
+ severity="error",
471
+ )
472
+ self.app.bell()
473
+ return
474
+ if not isinstance(item, LauncherItem):
475
+ return
476
+ self.post_message(messages.LaunchAgent(item.agent["identity"]))
477
+
478
+ def action_quick_launch(self) -> None:
479
+ try:
480
+ self.launcher.focus()
481
+ except NoMatches:
482
+ self.notify("Launcher not ready yet", severity="warning")
483
+
484
+ @work
485
+ async def action_resume(self) -> None:
486
+ from tui.screens.session_resume_modal import SessionResumeModal
487
+
488
+ session = await self.app.push_screen_wait(SessionResumeModal())
489
+ if session is not None:
490
+ self.post_message(
491
+ messages.LaunchAgent(
492
+ session["agent_identity"],
493
+ session["agent_session_id"],
494
+ pk=session["id"],
495
+ )
496
+ )
497
+
498
+ async def action_directory(self) -> None:
499
+ if (directory_display := self.query_one_optional(DirectoryDisplay)) is not None:
500
+ directory_display.focus()