@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
package/tui/shell.py ADDED
@@ -0,0 +1,304 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ from contextlib import suppress
5
+ import os
6
+ import asyncio
7
+ import codecs
8
+ import fcntl
9
+ import platform
10
+ import pty
11
+ import struct
12
+ import termios
13
+ from dataclasses import dataclass
14
+ from typing import TYPE_CHECKING
15
+
16
+ from textual import log
17
+ from textual.message import Message
18
+
19
+ from tui.shell_read import shell_read
20
+
21
+ from tui.widgets.terminal import Terminal
22
+
23
+ if TYPE_CHECKING:
24
+ from tui.widgets.conversation import Conversation
25
+
26
+ IS_MACOS = platform.system() == "Darwin"
27
+
28
+
29
+ def resize_pty(fd, cols, rows):
30
+ """Resize the pseudo-terminal"""
31
+ # Pack the dimensions into the format expected by TIOCSWINSZ
32
+ try:
33
+ size = struct.pack("HHHH", rows, cols, 0, 0)
34
+ fcntl.ioctl(fd, termios.TIOCSWINSZ, size)
35
+ except OSError:
36
+ # Possibly file descriptor closed
37
+ pass
38
+
39
+
40
+ @dataclass
41
+ class CurrentWorkingDirectoryChanged(Message):
42
+ """Current working directory has changed in shell."""
43
+
44
+ path: str
45
+
46
+
47
+ @dataclass
48
+ class ShellFinished(Message):
49
+ """The shell finished."""
50
+
51
+
52
+ class Shell:
53
+ """Responsible for shell interactions in Conversation."""
54
+
55
+ def __init__(
56
+ self,
57
+ conversation: Conversation,
58
+ working_directory: str,
59
+ shell="",
60
+ start="",
61
+ hide_start: bool = True,
62
+ ) -> None:
63
+ self.conversation = conversation
64
+ self.working_directory = working_directory
65
+
66
+ self.terminal: Terminal | None = None
67
+ self.new_log: bool = False
68
+ self.shell = shell or os.environ.get("SHELL", "sh")
69
+ self.shell_start = start
70
+ self.hide_start = hide_start
71
+ self.master: int | None = None
72
+ self._task: asyncio.Task | None = None
73
+ self._process: asyncio.subprocess.Process | None = None
74
+
75
+ self._finished: bool = False
76
+ self._ready_event: asyncio.Event = asyncio.Event()
77
+
78
+ self._hide_echo: set[bytes] = set()
79
+ """A set of byte strings to remove from output."""
80
+
81
+ self._hide_output = hide_start
82
+ """Hide all output."""
83
+
84
+ self._pid: int | None = None
85
+ """Shell process id"""
86
+
87
+ @property
88
+ def is_finished(self) -> bool:
89
+ return self._finished
90
+
91
+ def _is_busy(self) -> bool:
92
+ """Check if the shell is busy.
93
+
94
+ Called from a thread by `is_busy`.
95
+
96
+ Returns:
97
+ `True` if a command is running, or `False` if the shell is waiting for input.
98
+
99
+ """
100
+ if self._pid is None:
101
+ return False
102
+ import psutil
103
+
104
+ try:
105
+ shell_process = psutil.Process(self._pid)
106
+ children = shell_process.children(recursive=True)
107
+ except (psutil.NoSuchProcess, psutil.AccessDenied):
108
+ return False
109
+ else:
110
+ return bool(children)
111
+
112
+ async def is_busy(self) -> bool:
113
+ """Is there a process running in the shell?
114
+
115
+ Returns:
116
+ `True` if a command is running, or `False` if the shell is waiting for input.
117
+ """
118
+ return await asyncio.to_thread(self._is_busy)
119
+
120
+ async def wait_for_ready(self) -> None:
121
+ await self._ready_event.wait()
122
+
123
+ async def send(self, command: str, width: int, height: int) -> None:
124
+ await self._ready_event.wait()
125
+ if self.master is None:
126
+ print("TTY FD not set")
127
+ return
128
+
129
+ if self.terminal is not None:
130
+ self.terminal.finalize()
131
+ self.terminal = None
132
+
133
+ try:
134
+ await asyncio.to_thread(resize_pty, self.master, width, max(height, 1))
135
+ except OSError:
136
+ pass
137
+
138
+ get_pwd_command = f"{command};" + r'printf "\e]2025;$(pwd);\e\\"' + "\n"
139
+ await self.write(get_pwd_command, hide_echo=True)
140
+
141
+ async def send_input(self, text: str, paste: bool = False) -> None:
142
+ await self._ready_event.wait()
143
+ if self.master is None:
144
+ return
145
+ if paste and self.terminal is not None and self.terminal.state.bracketed_paste:
146
+ text = f"\x1b[200~{text}\x1b[201~"
147
+ await self.write(f"{text}\n", hide_echo=True)
148
+
149
+ def start(self) -> None:
150
+ assert self._task is None
151
+ self._task = asyncio.create_task(self.run(), name=repr(self))
152
+ log("shell starting")
153
+
154
+ async def interrupt(self) -> None:
155
+ """Interrupt the running command."""
156
+ await self.write(b"\x03")
157
+
158
+ def update_size(self, width: int, height: int) -> None:
159
+ """Update the size of the shell pty.
160
+
161
+ Args:
162
+ width: Desired width.
163
+ height: Desired height.
164
+ """
165
+ if self.master is None:
166
+ return
167
+ with suppress(OSError):
168
+ resize_pty(self.master, width, max(height, 1))
169
+
170
+ async def write(
171
+ self, text: str | bytes, hide_echo: bool = False, hide_output: bool = False
172
+ ) -> int:
173
+ if self.master is None:
174
+ return 0
175
+ text_bytes = text.encode("utf-8", "ignore") if isinstance(text, str) else text
176
+
177
+ if hide_echo:
178
+ for line in text_bytes.split(b"\n"):
179
+ if line:
180
+ self._hide_echo.add(line)
181
+ try:
182
+ result = await asyncio.to_thread(os.write, self.master, text_bytes)
183
+ except OSError:
184
+ return 0
185
+ self._hide_output = hide_output
186
+ return result
187
+
188
+ async def run(self) -> None:
189
+ current_directory = self.working_directory
190
+
191
+ master, slave = pty.openpty()
192
+ self.master = master
193
+
194
+ flags = fcntl.fcntl(master, fcntl.F_GETFL)
195
+ fcntl.fcntl(master, fcntl.F_SETFL, flags | os.O_NONBLOCK)
196
+
197
+ env = os.environ.copy()
198
+ env["FORCE_COLOR"] = "1"
199
+ env["TTY_COMPATIBLE"] = "1"
200
+ env["TERM"] = "xterm-256color"
201
+ env["COLORTERM"] = "truecolor"
202
+ env["TOAD"] = "1"
203
+ env["CLICOLOR"] = "1"
204
+
205
+ shell = self.shell
206
+
207
+ def setup_pty():
208
+ os.setsid()
209
+ fcntl.ioctl(slave, termios.TIOCSCTTY, 0)
210
+
211
+ try:
212
+ _process = await asyncio.create_subprocess_shell(
213
+ shell,
214
+ stdin=slave,
215
+ stdout=slave,
216
+ stderr=slave,
217
+ env=env,
218
+ cwd=current_directory,
219
+ preexec_fn=setup_pty,
220
+ )
221
+ except Exception as error:
222
+ self.conversation.notify(
223
+ f"Unable to start shell: {error}\n\nCheck your settings.",
224
+ title="Shell",
225
+ severity="error",
226
+ )
227
+ return
228
+ self._pid = _process.pid
229
+
230
+ os.close(slave)
231
+ BUFFER_SIZE = 64 * 1024
232
+ reader = asyncio.StreamReader(BUFFER_SIZE)
233
+ protocol = asyncio.StreamReaderProtocol(reader)
234
+
235
+ loop = asyncio.get_event_loop()
236
+ transport, _ = await loop.connect_read_pipe(
237
+ lambda: protocol, os.fdopen(master, "rb", 0)
238
+ )
239
+
240
+ self._ready_event.set()
241
+
242
+ if shell_start := self.shell_start.strip():
243
+ shell_start = self.shell_start.strip()
244
+ if not shell_start.endswith("\n"):
245
+ shell_start += "\n"
246
+ await self.write(shell_start, hide_echo=False, hide_output=self.hide_start)
247
+
248
+ unicode_decoder = codecs.getincrementaldecoder("utf-8")(errors="replace")
249
+
250
+ while True:
251
+ data = await shell_read(reader, BUFFER_SIZE)
252
+
253
+ for string_bytes in list(self._hide_echo):
254
+ remove_bytes = string_bytes
255
+ if remove_bytes in data:
256
+ remove_start = data.index(remove_bytes)
257
+ try:
258
+ next_line = data.index(b"\n", remove_start + len(remove_bytes))
259
+ except ValueError:
260
+ data = data.replace(remove_bytes, b"\x1b[2K")
261
+ else:
262
+ data = data[:remove_start] + b"\x1b[2K" + data[next_line + 1 :]
263
+
264
+ self._hide_echo.discard(string_bytes)
265
+
266
+ if line := unicode_decoder.decode(data, final=not data):
267
+ if self.terminal is None or self.terminal.is_finalized:
268
+ previous_state = (
269
+ None if self.terminal is None else self.terminal.state
270
+ )
271
+ self.terminal = await self.conversation.new_terminal()
272
+ # if previous_state is not None:
273
+ # self.terminal.set_state(previous_state)
274
+ self.terminal.set_write_to_stdin(self.write)
275
+
276
+ terminal_updated = await self.terminal.write(
277
+ line, hide_output=self._hide_output
278
+ )
279
+ if terminal_updated and not self.terminal.display:
280
+ if (
281
+ self.terminal.alternate_screen
282
+ or not self.terminal.state.scrollback_buffer.is_blank
283
+ ):
284
+ self.terminal.display = True
285
+ new_directory = self.terminal.current_directory
286
+ if new_directory and new_directory != current_directory:
287
+ current_directory = new_directory
288
+ self.conversation.post_message(
289
+ CurrentWorkingDirectoryChanged(current_directory)
290
+ )
291
+ if (
292
+ self.terminal is not None
293
+ and self.terminal.is_finalized
294
+ and self.terminal.state.scrollback_buffer.is_blank
295
+ ):
296
+ self.terminal.finalize()
297
+ self.terminal = None
298
+
299
+ if not data:
300
+ break
301
+
302
+ self.master = None
303
+ self._finished = True
304
+ self.conversation.post_message(ShellFinished())
@@ -0,0 +1,42 @@
1
+ import asyncio
2
+ from contextlib import suppress
3
+ from time import monotonic
4
+
5
+
6
+ async def shell_read(
7
+ reader: asyncio.StreamReader,
8
+ buffer_size: int,
9
+ *,
10
+ buffer_period: float | None = 1 / 100,
11
+ max_buffer_duration: float = 1 / 60,
12
+ ) -> bytes:
13
+ """Read data from a stream reader, with buffer logic to reduce the number of chunks.
14
+
15
+ Args:
16
+ reader: A reader instance.
17
+ buffer_size: Maximum buffer size.
18
+ buffer_period: Time in seconds where reads are batched, or `None` for no batching.
19
+ max_buffer_duration: Maximum time in seconds to buffer.
20
+
21
+ Returns:
22
+ Bytes read. May be empty on the last read.
23
+ """
24
+ try:
25
+ data = await reader.read(buffer_size)
26
+ except OSError:
27
+ data = b""
28
+ if data and buffer_period is not None:
29
+ buffer_time = monotonic() + max_buffer_duration
30
+ with suppress(asyncio.TimeoutError):
31
+ while len(data) < buffer_size and (time := monotonic()) < buffer_time:
32
+ async with asyncio.timeout(min(buffer_time - time, buffer_period)):
33
+ try:
34
+ if chunk := await reader.read(buffer_size - len(data)):
35
+ data += chunk
36
+ else:
37
+ break
38
+ except OSError as error:
39
+ print(repr(error))
40
+
41
+ break
42
+ return data
@@ -0,0 +1,34 @@
1
+ import rich.repr
2
+
3
+ from textual.content import Content
4
+
5
+
6
+ @rich.repr.auto
7
+ class SlashCommand:
8
+ """A record of a slash command."""
9
+
10
+ def __init__(self, command: str, help: str, hint: str | None = None) -> None:
11
+ """
12
+
13
+ Args:
14
+ command: The command name.
15
+ help: Description of command.
16
+ hint: Hint text (displayed as suggestion)
17
+ """
18
+ self.command = command
19
+ self.help = help
20
+ self.hint: str | None = hint
21
+
22
+ def __rich_repr__(self) -> rich.repr.Result:
23
+ yield self.command
24
+ yield "help", self.help
25
+ yield "hint", self.hint, None
26
+
27
+ def __str__(self) -> str:
28
+ return self.command
29
+
30
+ @property
31
+ def content(self) -> Content:
32
+ return Content.assemble(
33
+ (self.command, "$text-success"), "\t", (self.help, "dim")
34
+ )
package/tui/tui.tcss ADDED
@@ -0,0 +1,246 @@
1
+ .block {
2
+ margin: 1 1 1 0;
3
+ }
4
+
5
+
6
+ Markdown {
7
+ layout: stream;
8
+ }
9
+
10
+ LoadingIndicator {
11
+ background: transparent;
12
+ }
13
+
14
+ Throbber {
15
+ width: 100%;
16
+ height: 1;
17
+ layer: base;
18
+ visibility: hidden;
19
+ &.-busy {
20
+ visibility: visible;
21
+ }
22
+ }
23
+
24
+ .-hidden {
25
+ display: none;
26
+ }
27
+
28
+ App.-hide-footer {
29
+ #info-container {
30
+ margin: 0 !important;
31
+ }
32
+ Footer {
33
+ display: none;
34
+ }
35
+ }
36
+
37
+ App.-hide-footer.-compact-input {
38
+ #prompt-container {
39
+ margin: 0 0;
40
+ }
41
+ }
42
+
43
+
44
+ App.-hide-thoughts AgentThought {
45
+ display: none;
46
+ }
47
+
48
+ App.-hide-sidebar SideBar {
49
+ layer: sidebar;
50
+ offset-x: -100%;
51
+ &:focus-within {
52
+ offset-x: 0;
53
+ }
54
+ }
55
+
56
+ App.-hide-status-line StatusLine {
57
+ display: none;
58
+ }
59
+
60
+ App.-hide-agent-title AgentInfo {
61
+ display: none;
62
+ }
63
+
64
+ App.-hide-info-bar #info-container {
65
+ height: 1;
66
+ display: none;
67
+ }
68
+
69
+ App.-compact-input {
70
+ Prompt {
71
+ &.-mode-ask #prompt-container {
72
+ border: tall transparent !important;
73
+ &:focus-within {
74
+ border: tall $primary !important;
75
+ }
76
+ }
77
+ #prompt-container {
78
+ border: none;
79
+ &:focus-within {
80
+ border: none;
81
+ }
82
+ margin: 0 0 0 0;
83
+ }
84
+ #info-container {
85
+ margin: 0 0;
86
+ }
87
+ }
88
+ }
89
+
90
+ App.-show-sessions-bar {
91
+ SessionsTabs {
92
+ display: block;
93
+ }
94
+ Throbber {
95
+ offset-y: 1;
96
+ }
97
+ }
98
+
99
+ Checkbox {
100
+ # TODO: Bug in Textual?
101
+ text-overflow: clip;
102
+ }
103
+
104
+
105
+ Footer {
106
+ background: transparent;
107
+ .footer-key--key {
108
+ color: $text;
109
+ background: transparent;
110
+ # text-style: not bold;
111
+ padding: 0 1;
112
+ }
113
+
114
+ .footer-key--description {
115
+ padding: 0 1 0 0;
116
+ color: $text-muted;
117
+ background: $footer-description-background;
118
+ }
119
+
120
+ &:hover {
121
+ color: transparent;
122
+ background: transparent;
123
+ }
124
+
125
+ &.-disabled {
126
+ text-style: dim;
127
+ }
128
+
129
+ &.-compact {
130
+ .footer-key--key {
131
+ padding: 0;
132
+ }
133
+ .footer-key--description {
134
+ padding: 0 0 0 1;
135
+ }
136
+ }
137
+ FooterLabel {
138
+ color: $text-muted;
139
+ background: $footer-description-background;
140
+ }
141
+
142
+ FooterKey.-disabled {
143
+ .footer-key--key {
144
+ text-opacity: 0.6;
145
+ }
146
+ }
147
+ }
148
+
149
+ HelpPanel {
150
+
151
+ border-left: solid white 7%;
152
+ padding-right: 0;
153
+ # color: $text-secondary;
154
+
155
+ #widget-help {
156
+ # color: $text-secondary;
157
+ border: round $secondary;
158
+ background: transparent;
159
+ margin-top: 0;
160
+ padding: 0 0;
161
+ MarkdownBlock {
162
+ padding: 0 1;
163
+ }
164
+ MarkdownH2 {
165
+ margin: 0 0 1 0;
166
+ }
167
+ MarkdownBlock:last-child {
168
+ margin: 0 0 0 0;
169
+ }
170
+ }
171
+
172
+ KeyPanel {
173
+ # color: $text-secondary;
174
+ scrollbar-gutter: stable;
175
+ &> BindingsTable > .bindings-table--description {
176
+ # color: $text-secondary;
177
+ text-opacity:90%;
178
+ }
179
+ .bindings-table--key {
180
+ # color: $text-secondary;
181
+ color: $text;
182
+ }
183
+ &> BindingsTable > .bindings-table--header {
184
+ color: $primary;
185
+ text-style: underline;
186
+ }
187
+ }
188
+ }
189
+
190
+
191
+
192
+ Toast {
193
+ margin: 0 0;
194
+ padding: 0 1;
195
+ background: $background;
196
+ color: $text-muted;
197
+ .toast--title {
198
+ color: $text;
199
+ }
200
+ &.-information {
201
+ border: round $primary;
202
+ }
203
+ &.-warning {
204
+ border: round $warning;
205
+ }
206
+ &.-error {
207
+ border: round $error;
208
+ }
209
+ }
210
+
211
+
212
+ Loading {
213
+ color: $text-primary;
214
+ }
215
+
216
+ SessionsTabs {
217
+ layout: grid;
218
+ grid-columns: auto auto;
219
+ grid-size: 1 ;
220
+ overflow: scroll scroll;
221
+ scrollbar-size: 0 0;
222
+ width: 1fr;
223
+ display: none;
224
+
225
+ height: 2;
226
+ box-sizing: content-box;
227
+ Underline {
228
+ width: 1fr;
229
+ height: 1;
230
+ & > .underline--bar {
231
+ color: $block-cursor-background;
232
+ background: $foreground 10%;
233
+ }
234
+ }
235
+ #title-container {
236
+ width: auto;
237
+ Label {
238
+ text-style: dim;
239
+ pointer: pointer;
240
+ padding: 0 1;
241
+ &.-current {
242
+ text-style: not dim;
243
+ }
244
+ }
245
+ }
246
+ }
package/tui/twitter.py ADDED
@@ -0,0 +1,38 @@
1
+ import webbrowser
2
+ from urllib.parse import urlencode
3
+
4
+
5
+ def open_tweet_intent(
6
+ text: str,
7
+ *,
8
+ url: str | None = None,
9
+ hashtags: list[str] | None = None,
10
+ via: str | None = None,
11
+ in_reply_to: str | None = None,
12
+ ) -> str:
13
+ """Open a pre-filled Twitter/X tweet intent in the default browser.
14
+
15
+ Args:
16
+ text: The pre-filled tweet text.
17
+ url: An optional URL to append to the tweet.
18
+ hashtags: An optional list of hashtags to include (without '#').
19
+ via: An optional username to attribute the tweet to.
20
+ in_reply_to: An optional tweet ID to reply to.
21
+
22
+ Returns:
23
+ The full tweet intent URL that was opened.
24
+ """
25
+ params: dict[str, str] = {"text": text}
26
+
27
+ if url is not None:
28
+ params["url"] = url
29
+ if hashtags:
30
+ params["hashtags"] = ",".join(hashtags)
31
+ if via is not None:
32
+ params["via"] = via.lstrip("@")
33
+ if in_reply_to is not None:
34
+ params["in_reply_to"] = in_reply_to
35
+
36
+ tweet_url = f"https://twitter.com/intent/tweet?{urlencode(params)}"
37
+ webbrowser.open(tweet_url)
38
+ return tweet_url