@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/jsonrpc.py ADDED
@@ -0,0 +1,578 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import json
5
+ from asyncio import Future, get_running_loop
6
+ from dataclasses import dataclass
7
+ from functools import wraps
8
+ import inspect
9
+ from inspect import signature
10
+ from enum import IntEnum
11
+ import logging
12
+ from types import TracebackType
13
+ import weakref
14
+
15
+ import rich.repr
16
+ from typing import Callable, ParamSpec, TypeVar
17
+ from typeguard import check_type, CollectionCheckStrategy, TypeCheckError
18
+
19
+
20
+ type MethodType = Callable
21
+ type JSONValue = str | int | float | bool | None
22
+ type JSONType = dict[str, JSONType] | list[JSONType] | str | int | float | bool | None
23
+ type JSONObject = dict[str, JSONType]
24
+ type JSONList = list[JSONType]
25
+
26
+ log = logging.getLogger("jsonrpc")
27
+
28
+
29
+ def expose(name: str = "", prefix: str = ""):
30
+ """Expose a method."""
31
+
32
+ def expose_method[T: Callable](callable: T) -> T:
33
+ setattr(callable, "_jsonrpc_expose", f"{prefix}{name or callable.__name__}")
34
+ return callable
35
+
36
+ return expose_method
37
+
38
+
39
+ class NoDefault:
40
+ def __repr__(self) -> str:
41
+ return "NO_DEFAULT"
42
+
43
+
44
+ NO_DEFAULT = NoDefault()
45
+
46
+
47
+ class ErrorCode(IntEnum):
48
+ """JSONRPC error codes"""
49
+
50
+ # https://www.jsonrpc.org/specification
51
+ PARSE_ERROR = -32700
52
+ INVALID_REQUEST = -32600
53
+ METHOD_NOT_FOUND = -32601
54
+ INVALID_PARAMS = -32602
55
+ INTERNAL_ERROR = -32603
56
+
57
+
58
+ @dataclass
59
+ class Parameter:
60
+ type: type
61
+ default: JSONType | NoDefault
62
+
63
+
64
+ @dataclass
65
+ class Method:
66
+ name: str
67
+ callable: Callable
68
+ parameters: dict[str, Parameter]
69
+
70
+
71
+ @rich.repr.auto
72
+ class JSONRPCError(Exception):
73
+ """An error thrown by the JSONRPC system."""
74
+
75
+ CODE: ErrorCode = ErrorCode.INTERNAL_ERROR
76
+ """Default code to use (may be overridden in the constructor)."""
77
+
78
+ def __init__(
79
+ self, message: str, id: str | int | None = None, code: ErrorCode | None = None
80
+ ) -> None:
81
+ self.message = message
82
+ self.id = id
83
+ self.code = code if code is not None else self.CODE
84
+ super().__init__(message)
85
+
86
+ def __rich_repr__(self) -> rich.repr.Result:
87
+ yield self.message
88
+ yield "id", self.id
89
+ yield "code", self.code
90
+
91
+
92
+ class InvalidRequest(JSONRPCError):
93
+ CODE = ErrorCode.INVALID_REQUEST
94
+
95
+
96
+ class InvalidParams(JSONRPCError):
97
+ CODE = ErrorCode.INVALID_PARAMS
98
+
99
+
100
+ class MethodNotFound(JSONRPCError):
101
+ CODE = ErrorCode.METHOD_NOT_FOUND
102
+
103
+
104
+ class InternalError(JSONRPCError):
105
+ CODE = ErrorCode.INTERNAL_ERROR
106
+
107
+
108
+ @rich.repr.auto
109
+ class APIError(Exception):
110
+ def __init__(self, code: int, message: str, data: JSONType) -> None:
111
+ self.code = code
112
+ self.message = message
113
+ self.data = data
114
+ if data is None:
115
+ super().__init__(f"{message} ({code})")
116
+ else:
117
+ super().__init__(f"{message} ({code}); data={data!r}")
118
+
119
+
120
+ class Server:
121
+ def __init__(self) -> None:
122
+ self._methods: dict[str, Method] = {}
123
+
124
+ async def call(self, json: JSONObject | JSONList) -> JSONType:
125
+ if isinstance(json, dict):
126
+ # Single call
127
+ response = await self._dispatch_object(json)
128
+ else:
129
+ # Batch call
130
+ response = await self._dispatch_batch(json)
131
+ log.debug(f"OUT {response}")
132
+ return response
133
+
134
+ def expose_instance(self, instance: object) -> None:
135
+ """Add methods from the given instance."""
136
+ for method_name in dir(instance):
137
+ try:
138
+ method = getattr(instance, method_name)
139
+ except AttributeError:
140
+ continue
141
+ if (jsonrpc_expose := getattr(method, "_jsonrpc_expose", None)) is not None:
142
+ self.method(jsonrpc_expose)(method)
143
+
144
+ async def _dispatch_object(self, json: JSONObject) -> JSONType | None:
145
+ json_id = json.get("id")
146
+ if isinstance(json_id, (int, str)):
147
+ request_id = json_id
148
+ else:
149
+ request_id = None
150
+ try:
151
+ return await self._dispatch_object_call(request_id, json)
152
+ except JSONRPCError as error:
153
+ return {
154
+ "jsonrpc": "2.0",
155
+ "id": error.id,
156
+ "error": {
157
+ "code": int(error.code),
158
+ "message": error.message,
159
+ },
160
+ }
161
+ except Exception as error:
162
+ log.exception("Error dispatching JSONRPC request")
163
+ return {
164
+ "jsonrpc": "2.0",
165
+ "id": request_id,
166
+ "error": {
167
+ "code": int(ErrorCode.INTERNAL_ERROR),
168
+ "message": f"An error occurred handling your request: {error!r}",
169
+ },
170
+ }
171
+
172
+ async def _dispatch_object_call(
173
+ self, request_id: int | str | None, json: JSONObject
174
+ ) -> JSONType | None:
175
+ """Dispatch a JSONRPC call.
176
+
177
+ Args:
178
+ request_id: The request ID.
179
+ json: JSON object with the remote call information.
180
+
181
+ Returns:
182
+ Result encoding in a JSONRPC result object, or `None` if the call is a notification
183
+ and doesn't require a result.
184
+ """
185
+ if (jsonrpc := json.get("jsonrpc")) != "2.0":
186
+ raise InvalidRequest(
187
+ f"jsonrpc attribute should be '2.0'; found {jsonrpc!r}", id=request_id
188
+ )
189
+ if (method_name := json.get("method")) is None:
190
+ raise InvalidRequest(
191
+ "Invalid request; no value for 'method' given", id=request_id
192
+ )
193
+
194
+ if not isinstance(method_name, str):
195
+ raise InvalidRequest(
196
+ "Invalid request; 'method' should be a string type", id=request_id
197
+ )
198
+
199
+ if (method := self._methods.get(method_name)) is None:
200
+ raise MethodNotFound(
201
+ f"Method not found; {method_name!r} is not an exposed method",
202
+ id=request_id,
203
+ )
204
+
205
+ no_params: JSONList = []
206
+ params = json.get("params", no_params)
207
+
208
+ if not isinstance(params, (list, dict)):
209
+ raise InvalidRequest(
210
+ "Invalid request; 'params' attribute should be a list or an object"
211
+ )
212
+
213
+ arguments: dict[str, JSONType | Server | NoDefault] = {
214
+ name: parameter.default for name, parameter in method.parameters.items()
215
+ }
216
+
217
+ def validate(value: JSONType, parameter_type: type) -> None:
218
+ """Validate types."""
219
+ try:
220
+ check_type(
221
+ value,
222
+ parameter_type,
223
+ collection_check_strategy=CollectionCheckStrategy.ALL_ITEMS,
224
+ )
225
+ except TypeCheckError as error:
226
+ raise InvalidParams(
227
+ f"Parameter is not the expected type ({parameter_type}); {error}",
228
+ id=request_id,
229
+ )
230
+
231
+ if isinstance(params, list):
232
+ parameter_items = [
233
+ (name, parameter)
234
+ for name, parameter in method.parameters.items()
235
+ if not issubclass(parameter.type, Server)
236
+ ]
237
+ for (parameter_name, parameter), value in zip(parameter_items, params):
238
+ if issubclass(parameter.type, Server):
239
+ value = self
240
+ else:
241
+ validate(value, parameter.type)
242
+ arguments[parameter_name] = value
243
+ else:
244
+ for parameter_name, value in params.items():
245
+ if parameter := method.parameters.get(parameter_name):
246
+ validate(value, parameter.type)
247
+ arguments[parameter_name] = value
248
+
249
+ for name, parameter in method.parameters.items():
250
+ if inspect.isclass(parameter.type) and issubclass(parameter.type, Server):
251
+ arguments[name] = self
252
+
253
+ try:
254
+ call_result = method.callable(**arguments)
255
+ if inspect.isawaitable(call_result):
256
+ result = await call_result
257
+ else:
258
+ result = call_result
259
+ except JSONRPCError as error:
260
+ error.id = request_id
261
+ raise error
262
+ except Exception as error:
263
+ raise
264
+ # print("JSON error", repr(error))
265
+ # log.debug(f"Error in exposed JSONRPC method; {error}")
266
+ print("INTERNAL ERROR", error)
267
+ raise InternalError(str(error), id=request_id)
268
+
269
+ if request_id is None:
270
+ # Notification
271
+ return None
272
+
273
+ response_object = {"jsonrpc": "2.0", "result": result, "id": request_id}
274
+ return response_object
275
+
276
+ async def _dispatch_batch(self, json: JSONList) -> list[JSONType]:
277
+ batch_results: list[JSONType] = []
278
+ for request in json:
279
+ if not isinstance(request, dict):
280
+ continue
281
+ result = await self._dispatch_object(request)
282
+ if result is not None:
283
+ batch_results.append(result)
284
+ return batch_results
285
+
286
+ def process_callable(
287
+ self, callable: Callable[[MethodType], MethodType]
288
+ ) -> Callable[[MethodType], MethodType]:
289
+ return callable
290
+
291
+ def method[MethodT: Callable](
292
+ self,
293
+ name: str = "",
294
+ *,
295
+ prefix: str = "",
296
+ ) -> Callable[[MethodT], MethodT]:
297
+ """Decorator to expose a method via JSONRPC.
298
+
299
+ Args:
300
+ name: The name of the exposed method. Leave blank to auto-detect.
301
+ prefix: A prefix to be applied to the name.
302
+
303
+ Returns:
304
+ Decorator.
305
+ """
306
+
307
+ def expose_method[T: Callable](callable: T) -> T:
308
+ nonlocal name
309
+ if not name:
310
+ name = callable.__name__
311
+ name = f"{prefix}{name}"
312
+
313
+ parameters = {
314
+ name: Parameter(
315
+ (
316
+ eval(parameter.annotation)
317
+ if isinstance(parameter.annotation, str)
318
+ else parameter.annotation
319
+ ),
320
+ (
321
+ NO_DEFAULT
322
+ if parameter.default is inspect._empty
323
+ else parameter.default
324
+ ),
325
+ )
326
+ for name, parameter in signature(callable).parameters.items()
327
+ }
328
+ self._methods[name] = Method(name, callable, parameters)
329
+ return callable
330
+
331
+ return expose_method
332
+
333
+
334
+ @rich.repr.auto
335
+ class MethodCall[ReturnType]:
336
+ def __init__(
337
+ self, method: str, id: int | None, parameters: dict[str, JSONType]
338
+ ) -> None:
339
+ self.method = method
340
+ self.id = id
341
+ self.parameters = parameters
342
+ self.notification = False
343
+ self.future: Future[ReturnType] = get_running_loop().create_future()
344
+
345
+ def __rich_repr__(self) -> rich.repr.Result:
346
+ yield "method", self.method
347
+ yield "id", self.id, None
348
+ yield "parameters", self.parameters
349
+ yield "notification", self.notification, False
350
+
351
+ @property
352
+ def as_json_object(self) -> JSONType:
353
+ json: JSONType
354
+ if self.id is None:
355
+ json = {
356
+ "jsonrpc": "2.0",
357
+ "method": self.method,
358
+ "params": self.parameters,
359
+ }
360
+ else:
361
+ json = {
362
+ "jsonrpc": "2.0",
363
+ "method": self.method,
364
+ "params": self.parameters,
365
+ "id": self.id,
366
+ }
367
+ return json
368
+
369
+ async def wait(self, timeout: float | None = None) -> ReturnType | None:
370
+ if self.id is None:
371
+ return None
372
+ async with asyncio.timeout(timeout):
373
+ return await self.future
374
+
375
+
376
+ P = ParamSpec("P") # Captures parameter types
377
+ T = TypeVar("T") # Original return type
378
+
379
+
380
+ class Request:
381
+ def __init__(self, api: API, callback: Callable[[Request], None] | None) -> None:
382
+ self.api = api
383
+ self._calls: list[MethodCall] = []
384
+ self._callback = callback
385
+
386
+ def add_call(self, call: MethodCall) -> None:
387
+ self._calls.append(call)
388
+
389
+ def __enter__(self) -> Request:
390
+ self.api._requests.append(self)
391
+ return self
392
+
393
+ def __exit__(
394
+ self,
395
+ exc_type: type[BaseException] | None,
396
+ exc_val: type[BaseException],
397
+ exc_tb: TracebackType,
398
+ ) -> None:
399
+ self.api._requests.pop()
400
+ if self._callback is not None:
401
+ self._callback(self)
402
+
403
+ @property
404
+ def body(self) -> JSONType | None:
405
+ """The un-encoded JSON.
406
+
407
+ Returns:
408
+ The body, or `None` if no calls present on the request.
409
+ """
410
+ if not self._calls:
411
+ return None
412
+ calls = self._calls
413
+ if len(calls) == 1:
414
+ method_call = calls[0]
415
+ return method_call.as_json_object
416
+ else:
417
+ return [method_call.as_json_object for method_call in calls]
418
+
419
+ @property
420
+ def body_json(self) -> bytes:
421
+ """Dump the body as encoded json."""
422
+ body_json = json.dumps(self.body).encode("utf-8")
423
+ return body_json
424
+
425
+
426
+ class API:
427
+ def __init__(self) -> None:
428
+ self._request_id = 0
429
+ self._requests: list[Request] = []
430
+ self._calls: weakref.WeakValueDictionary[int, MethodCall] = (
431
+ weakref.WeakValueDictionary()
432
+ )
433
+
434
+ def request(self, callback: Callable[[Request], None] | None = None) -> Request:
435
+ """Create a Request context manager."""
436
+ request = Request(self, callback)
437
+ return request
438
+
439
+ def _process_method_response(self, response: JSONObject) -> None:
440
+ if (id := response.get("id")) is not None and isinstance(id, int):
441
+ if (method_call := self._calls.get(id)) is not None:
442
+ try:
443
+ result = response["result"]
444
+ except KeyError:
445
+ if (error := response.get("error")) is not None:
446
+ if isinstance(error, dict):
447
+ code = error.get("error", -1)
448
+ if not isinstance(code, int):
449
+ code = -1
450
+ message = str(error.get("message", "unknown error"))
451
+ data = error.get("data", None)
452
+ method_call.future.set_exception(
453
+ APIError(code, message, data)
454
+ )
455
+ else:
456
+ method_call.future.set_result(result)
457
+
458
+ def process_response(self, response: JSONType) -> None:
459
+ if isinstance(response, list):
460
+ for response_object in response:
461
+ if isinstance(response_object, dict):
462
+ self._process_method_response(response_object)
463
+ elif isinstance(response, dict):
464
+ self._process_method_response(response)
465
+
466
+ def method(
467
+ self, name: str = "", *, prefix: str = "", notification: bool = False
468
+ ) -> Callable[[Callable[P, T]], Callable[P, MethodCall[T]]]:
469
+ """Decorator to define a method.
470
+
471
+ Args:
472
+ name: Name of the method, or "" to auto-detect.
473
+ prefix: String to prefix the name.
474
+
475
+ Returns:
476
+ Decorator.
477
+ """
478
+
479
+ def decorator(func: Callable[P, T]) -> Callable[P, MethodCall[T]]:
480
+ nonlocal name
481
+ if not name:
482
+ name = func.__name__
483
+ name = f"{prefix}{name}"
484
+
485
+ @wraps(func)
486
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> MethodCall[T]:
487
+ parameters = signature(func).parameters
488
+ call_parameters = {}
489
+ for arg, parameter_name in zip(args, parameters):
490
+ call_parameters[parameter_name] = arg
491
+ for parameter_name, arg in kwargs:
492
+ call_parameters[parameter_name] = arg
493
+ if notification:
494
+ method_call = MethodCall(name, None, call_parameters)
495
+ else:
496
+ self._request_id += 1
497
+ method_call = MethodCall(name, self._request_id, call_parameters)
498
+ self._requests[-1].add_call(method_call)
499
+ if method_call.id is not None:
500
+ self._calls[method_call.id] = method_call
501
+ return method_call
502
+
503
+ return wrapper
504
+
505
+ return decorator
506
+
507
+ def notification(
508
+ self,
509
+ name: str = "",
510
+ *,
511
+ prefix: str = "",
512
+ ) -> Callable[[Callable[P, T]], Callable[P, MethodCall[T]]]:
513
+ return self.method(name, prefix=prefix, notification=True)
514
+
515
+
516
+ if __name__ == "__main__":
517
+ from rich import print
518
+
519
+ server = Server()
520
+
521
+ @server.method()
522
+ def hello(name: str) -> str:
523
+ return f"hello {name}"
524
+
525
+ @server.method()
526
+ def add(server: Server, a: int, b: int) -> int:
527
+ print("SERVER", server)
528
+ return a + b
529
+
530
+ # print("!", add(1, 2))
531
+ print(server._methods)
532
+
533
+ # print(
534
+ # server.call(
535
+ # [
536
+ # {
537
+ # "jsonrpc": "2.0",
538
+ # "method": "hello",
539
+ # "params": {"name": "Will"},
540
+ # "id": "1",
541
+ # },
542
+ # {
543
+ # "jsonrpc": "2.0",
544
+ # "method": "add",
545
+ # "params": {"a": 10, "b": 20},
546
+ # "id": "2",
547
+ # },
548
+ # {"jsonrpc": "2.0", "method": "alert", "params": {"message": "Alert!"}},
549
+ # ]
550
+ # )
551
+ # )
552
+
553
+ async def test_proxy():
554
+ api = API()
555
+
556
+ @api.method()
557
+ def add(a: int, b: int) -> int: ...
558
+
559
+ @api.method()
560
+ def greet(name: str) -> str: ...
561
+
562
+ @api.notification()
563
+ def alert(text: str) -> None: ...
564
+
565
+ with api.request() as request:
566
+ add(2, 4)
567
+ greeting = greet("Will")
568
+ alert("test")
569
+ # add(1, "not a number")
570
+
571
+ # greeting = await greeting.wait()
572
+ # print(greeting)
573
+
574
+ print(greeting)
575
+
576
+ print(request.body)
577
+
578
+ asyncio.run(test_proxy())
package/tui/menus.py ADDED
@@ -0,0 +1,14 @@
1
+ from typing import NamedTuple
2
+
3
+
4
+ class MenuItem(NamedTuple):
5
+ """An entry in a Menu."""
6
+
7
+ description: str
8
+ action: str | None
9
+ key: str | None = None
10
+
11
+
12
+ CONVERSATION_MENUS: dict[str, list[MenuItem]] = {
13
+ "fence": [MenuItem("Run this code", "run", "r")]
14
+ }
@@ -0,0 +1,69 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import re
5
+ from datetime import datetime, timezone
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from tui import paths
10
+
11
+
12
+ def sanitize_filename(name: str) -> str:
13
+ """Replace characters that are problematic in file names with underscores."""
14
+ return re.sub(r'[<>:"/\\|?*]', "_", name)
15
+
16
+
17
+ class MessageLog:
18
+ """Structured JSON message log for recording user input and agent output.
19
+
20
+ Writes one JSON line per event to a session-scoped log file for
21
+ diagnostic analysis (e.g. why the agent returned no response).
22
+ Logs are grouped by session_id — one file per session.
23
+ """
24
+
25
+ def __init__(self, session_id: str, agent_name: str, log_dir: Path | None = None) -> None:
26
+ self._session_id = session_id
27
+ self._agent_name = agent_name
28
+ dir = (log_dir or paths.get_log()) / "messages"
29
+ dir.mkdir(parents=True, exist_ok=True)
30
+ self._path = dir / f"{sanitize_filename(agent_name)}_{sanitize_filename(session_id)}.jsonl"
31
+ self._file: Any = None
32
+
33
+ def _write(self, event: str, data: dict[str, Any]) -> None:
34
+ record = {
35
+ "ts": datetime.now(timezone.utc).isoformat(),
36
+ "session_id": self._session_id,
37
+ "agent": self._agent_name,
38
+ "event": event,
39
+ **data,
40
+ }
41
+ if self._file is None:
42
+ self._file = self._path.open("at", buffering=1)
43
+ self._file.write(json.dumps(record, ensure_ascii=False) + "\n")
44
+
45
+ def user_input(self, text: str, turn: int) -> None:
46
+ self._write("user_input", {"text": text, "turn": turn})
47
+
48
+ def agent_output(self, text: str, turn: int) -> None:
49
+ self._write("agent_output", {"text": text, "turn": turn})
50
+
51
+ def agent_thought(self, text: str, turn: int) -> None:
52
+ self._write("agent_thought", {"text": text, "turn": turn})
53
+
54
+ def tool_call(self, name: str, tool_id: str, turn: int, **extra: Any) -> None:
55
+ self._write("tool_call", {"tool_name": name, "tool_id": tool_id, "turn": turn, **extra})
56
+
57
+ def tool_result(self, name: str, tool_id: str, turn: int, status: str = "success") -> None:
58
+ self._write("tool_result", {"tool_name": name, "tool_id": tool_id, "turn": turn, "status": status})
59
+
60
+ def turn_end(self, turn: int, stop_reason: str | None) -> None:
61
+ self._write("turn_end", {"turn": turn, "stop_reason": stop_reason})
62
+
63
+ def error(self, message: str, turn: int | None = None, **extra: Any) -> None:
64
+ self._write("error", {"message": message, "turn": turn, **extra})
65
+
66
+ def close(self) -> None:
67
+ if self._file is not None:
68
+ self._file.close()
69
+ self._file = None