@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
File without changes
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import Optional
5
+
6
+
7
+ class CloudProvider(ABC):
8
+ name: str = ""
9
+
10
+ @abstractmethod
11
+ async def deploy(self, project_path: str, version: Optional[str] = None) -> str:
12
+ ...
13
+
14
+ @abstractmethod
15
+ async def status(self) -> str:
16
+ ...
17
+
18
+ @abstractmethod
19
+ async def rollback(self, version: str) -> str:
20
+ ...
@@ -0,0 +1,69 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from pathlib import Path
5
+ from typing import Optional
6
+
7
+ from onecode.codebase.chunker import CodeChunk
8
+ from onecode.config import CodebaseConfig
9
+ from onecode.codebase.indexer import CodebaseIndexer
10
+ from onecode.codebase.retriever import CodebaseRetriever
11
+ from onecode.codebase.storage import CodebaseStorage
12
+ from onecode.codebase.tools import CodebaseSearchTool
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ _MAX_INJECT_TOKENS = 2000
17
+
18
+
19
+ class CodebaseEngine:
20
+ def __init__(self, project_dir: Path, config: Optional[CodebaseConfig] = None):
21
+ self.project_dir = project_dir.resolve()
22
+ self.config = config or CodebaseConfig()
23
+ self.storage = CodebaseStorage(project_dir)
24
+ self.indexer = CodebaseIndexer(project_dir, self.config)
25
+ self.retriever = CodebaseRetriever(project_dir, self.config)
26
+ self._indexed = False
27
+
28
+ async def ensure_indexed(self, force: bool = False) -> None:
29
+ if self._indexed and not force:
30
+ return
31
+ result = await self.indexer.index(force=force)
32
+ self._indexed = True
33
+ self.retriever.mark_dirty()
34
+ return result
35
+
36
+ async def retrieve(self, query: str, top_k: Optional[int] = None) -> list[CodeChunk]:
37
+ await self.ensure_indexed()
38
+ return await self.retriever.retrieve(query, top_k=top_k)
39
+
40
+ def format_context(self, chunks: list[CodeChunk], max_tokens: int = _MAX_INJECT_TOKENS) -> str:
41
+ if not chunks:
42
+ return ""
43
+ parts: list[str] = []
44
+ token_budget = max_tokens
45
+ for c in chunks:
46
+ header = f"<chunk file=\"{c.file_path}\" lines=\"{c.start_line}-{c.end_line}\">"
47
+ footer = "</chunk>"
48
+ chunk_text = f"{header}\n{c.content}\n{footer}"
49
+ estimated_tokens = len(chunk_text) // 4 + 1
50
+ if estimated_tokens > token_budget:
51
+ if not parts:
52
+ trimmed = c.content[:token_budget * 4]
53
+ chunk_text = f"{header}\n{trimmed}\n{footer}"
54
+ parts.append(chunk_text)
55
+ break
56
+ parts.append(chunk_text)
57
+ token_budget -= estimated_tokens
58
+ return "<codebase_context>\n" + "\n".join(parts) + "\n</codebase_context>"
59
+
60
+
61
+ __all__ = [
62
+ "CodebaseEngine",
63
+ "CodebaseConfig",
64
+ "CodeChunk",
65
+ "CodebaseIndexer",
66
+ "CodebaseRetriever",
67
+ "CodebaseStorage",
68
+ "CodebaseSearchTool",
69
+ ]
@@ -0,0 +1,130 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+ from typing import Optional
7
+
8
+
9
+ _LANG_MAP: dict[str, str] = {
10
+ ".py": "python",
11
+ ".js": "javascript",
12
+ ".ts": "typescript",
13
+ ".tsx": "typescriptreact",
14
+ ".jsx": "javascriptreact",
15
+ ".go": "go",
16
+ ".rs": "rust",
17
+ ".java": "java",
18
+ ".rb": "ruby",
19
+ ".php": "php",
20
+ ".c": "c",
21
+ ".cpp": "cpp",
22
+ ".h": "c",
23
+ ".hpp": "cpp",
24
+ ".swift": "swift",
25
+ ".kt": "kotlin",
26
+ ".scala": "scala",
27
+ ".sql": "sql",
28
+ ".yaml": "yaml",
29
+ ".yml": "yaml",
30
+ ".json": "json",
31
+ ".toml": "toml",
32
+ ".md": "markdown",
33
+ ".css": "css",
34
+ ".scss": "scss",
35
+ ".html": "html",
36
+ ".svelte": "svelte",
37
+ ".vue": "vue",
38
+ ".sh": "shell",
39
+ ".bash": "shell",
40
+ ".zsh": "shell",
41
+ ".fish": "shell",
42
+ ".tf": "terraform",
43
+ ".tfvars": "terraform",
44
+ }
45
+
46
+
47
+ @dataclass
48
+ class CodeChunk:
49
+ file_path: str
50
+ start_line: int
51
+ end_line: int
52
+ content: str
53
+ language: str = ""
54
+
55
+ @property
56
+ def identifier(self) -> str:
57
+ return f"{self.file_path}:{self.start_line}-{self.end_line}"
58
+
59
+
60
+ def detect_language(file_path: str) -> str:
61
+ ext = os.path.splitext(file_path)[1].lower()
62
+ return _LANG_MAP.get(ext, "")
63
+
64
+
65
+ def chunk_file(file_path: Path, strategy: str = "line",
66
+ chunk_lines: int = 50, overlap: int = 10) -> list[CodeChunk]:
67
+ if strategy == "line":
68
+ return _chunk_by_lines(file_path, chunk_lines, overlap)
69
+ if strategy == "ast":
70
+ return _chunk_by_lines(file_path, chunk_lines, overlap)
71
+ return _chunk_by_lines(file_path, chunk_lines, overlap)
72
+
73
+
74
+ def _chunk_by_lines(file_path: Path, chunk_lines: int, overlap: int) -> list[CodeChunk]:
75
+ try:
76
+ text = file_path.read_text(encoding="utf-8")
77
+ except Exception:
78
+ return []
79
+
80
+ lines = text.splitlines(keepends=True)
81
+ if not lines:
82
+ return []
83
+
84
+ language = detect_language(str(file_path))
85
+ step = max(chunk_lines - overlap, 1)
86
+ chunks: list[CodeChunk] = []
87
+
88
+ for start in range(0, len(lines), step):
89
+ end = min(start + chunk_lines, len(lines))
90
+ content = "".join(lines[start:end]).rstrip()
91
+ if not content.strip():
92
+ continue
93
+ chunks.append(CodeChunk(
94
+ file_path=str(file_path),
95
+ start_line=start + 1,
96
+ end_line=end,
97
+ content=content,
98
+ language=language,
99
+ ))
100
+ if end == len(lines):
101
+ break
102
+
103
+ return chunks
104
+
105
+
106
+ def chunk_text(text: str, file_path: str, chunk_lines: int = 50, overlap: int = 10) -> list[CodeChunk]:
107
+ lines = text.splitlines(keepends=True)
108
+ if not lines:
109
+ return []
110
+
111
+ language = detect_language(file_path)
112
+ step = max(chunk_lines - overlap, 1)
113
+ chunks: list[CodeChunk] = []
114
+
115
+ for start in range(0, len(lines), step):
116
+ end = min(start + chunk_lines, len(lines))
117
+ content = "".join(lines[start:end]).rstrip()
118
+ if not content.strip():
119
+ continue
120
+ chunks.append(CodeChunk(
121
+ file_path=file_path,
122
+ start_line=start + 1,
123
+ end_line=end,
124
+ content=content,
125
+ language=language,
126
+ ))
127
+ if end == len(lines):
128
+ break
129
+
130
+ return chunks
@@ -0,0 +1,136 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import os
5
+ import time
6
+ from dataclasses import dataclass, field
7
+ from pathlib import Path
8
+ from typing import Optional
9
+
10
+ from pathspec import PathSpec
11
+ from pathspec.patterns import GitWildMatchPattern
12
+
13
+ from onecode.codebase.chunker import chunk_file
14
+ from onecode.config import CodebaseConfig
15
+ from onecode.codebase.storage import CodebaseStorage
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ @dataclass
21
+ class IndexResult:
22
+ total_files: int = 0
23
+ indexed_files: int = 0
24
+ skipped_files: int = 0
25
+ failed_files: int = 0
26
+ total_chunks: int = 0
27
+ duration_ms: float = 0.0
28
+ errors: list[str] = field(default_factory=list)
29
+
30
+
31
+ class CodebaseIndexer:
32
+ def __init__(self, project_dir: Path, config: CodebaseConfig):
33
+ self.project_dir = project_dir.resolve()
34
+ self.config = config
35
+ self.storage = CodebaseStorage(project_dir)
36
+ self._spec = self._build_spec()
37
+
38
+ def _build_spec(self) -> Optional[PathSpec]:
39
+ patterns = self.config.exclude_patterns
40
+ if not patterns:
41
+ return None
42
+ return PathSpec.from_lines(GitWildMatchPattern, patterns)
43
+
44
+ def walk_files(self) -> list[Path]:
45
+ include_exts = {ext.lower() for ext in self.config.include_extensions}
46
+ files: list[Path] = []
47
+ for root, dirs, filenames in os.walk(self.project_dir):
48
+ root_path = Path(root)
49
+ rel_root = root_path.relative_to(self.project_dir)
50
+ dirs[:] = [
51
+ d for d in dirs
52
+ if not self._is_excluded(str(rel_root / d))
53
+ ]
54
+ for fname in filenames:
55
+ fpath = root_path / fname
56
+ rel = fpath.relative_to(self.project_dir)
57
+ if self._is_excluded(str(rel)):
58
+ continue
59
+ if fpath.suffix.lower() in include_exts:
60
+ files.append(fpath)
61
+ return files
62
+
63
+ def _is_excluded(self, rel_path: str) -> bool:
64
+ if self._spec is None:
65
+ return False
66
+ return self._spec.match_file(rel_path)
67
+
68
+ def needs_reindex(self, file_path: Path) -> bool:
69
+ if not file_path.exists():
70
+ return True
71
+ stored_mtime = self.storage.get_file_mtime(str(file_path))
72
+ actual_mtime = file_path.stat().st_mtime
73
+ return actual_mtime > stored_mtime
74
+
75
+ async def index(self, force: bool = False) -> IndexResult:
76
+ result = IndexResult()
77
+ start = time.monotonic()
78
+
79
+ if force:
80
+ self.storage.clear_all()
81
+
82
+ files = self.walk_files()
83
+ result.total_files = len(files)
84
+
85
+ for fpath in files:
86
+ if not force and not self.needs_reindex(fpath):
87
+ result.skipped_files += 1
88
+ continue
89
+
90
+ try:
91
+ mtime = fpath.stat().st_mtime
92
+ chunks = chunk_file(
93
+ fpath,
94
+ strategy=self.config.chunk_strategy,
95
+ chunk_lines=self.config.chunk_lines,
96
+ overlap=self.config.chunk_overlap,
97
+ )
98
+ if chunks:
99
+ self.storage.save_chunks(chunks, mtime=mtime)
100
+ result.indexed_files += 1
101
+ result.total_chunks += len(chunks)
102
+ except Exception as e:
103
+ logger.warning("Failed to index %s: %s", fpath, e)
104
+ result.failed_files += 1
105
+ result.errors.append(f"{fpath}: {e}")
106
+
107
+ result.duration_ms = (time.monotonic() - start) * 1000
108
+ logger.info(
109
+ "Indexed %d files (%d skipped, %d failed) → %d chunks in %.0fms",
110
+ result.indexed_files, result.skipped_files, result.failed_files,
111
+ result.total_chunks, result.duration_ms,
112
+ )
113
+ return result
114
+
115
+ async def index_file(self, file_path: Path) -> None:
116
+ if not file_path.exists():
117
+ self.storage.remove_file(str(file_path))
118
+ return
119
+ include_exts = {ext.lower() for ext in self.config.include_extensions}
120
+ if file_path.suffix.lower() not in include_exts:
121
+ self.storage.remove_file(str(file_path))
122
+ return
123
+ try:
124
+ mtime = file_path.stat().st_mtime
125
+ chunks = chunk_file(
126
+ file_path,
127
+ strategy=self.config.chunk_strategy,
128
+ chunk_lines=self.config.chunk_lines,
129
+ overlap=self.config.chunk_overlap,
130
+ )
131
+ if chunks:
132
+ self.storage.save_chunks(chunks, mtime=mtime)
133
+ else:
134
+ self.storage.remove_file(str(file_path))
135
+ except Exception as e:
136
+ logger.warning("Failed to index %s: %s", file_path, e)
@@ -0,0 +1,192 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import logging
5
+ import math
6
+ from typing import Optional
7
+
8
+ import httpx
9
+
10
+ from onecode.codebase.chunker import CodeChunk
11
+ from onecode.config import CodebaseConfig
12
+ from onecode.codebase.storage import CodebaseStorage
13
+ from onecode.memory.recall import BM25
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ def _cosine_similarity(a: list[float], b: list[float]) -> float:
19
+ dot = sum(x * y for x, y in zip(a, b))
20
+ norm_a = math.sqrt(sum(x * x for x in a))
21
+ norm_b = math.sqrt(sum(y * y for y in b))
22
+ if norm_a == 0 or norm_b == 0:
23
+ return 0.0
24
+ return dot / (norm_a * norm_b)
25
+
26
+
27
+ class CodebaseRetriever:
28
+ def __init__(self, project_dir, config: CodebaseConfig):
29
+ self.storage = CodebaseStorage(project_dir)
30
+ self.config = config
31
+ self._bm25: Optional[BM25] = None
32
+ self._chunks: list[CodeChunk] = []
33
+ self._dirty = True
34
+
35
+ def _ensure_indexed(self) -> None:
36
+ if not self._dirty:
37
+ return
38
+ self._chunks = self.storage.get_all_chunks()
39
+ if self._chunks:
40
+ texts = [c.content for c in self._chunks]
41
+ self._bm25 = BM25()
42
+ self._bm25.index(texts)
43
+ else:
44
+ self._bm25 = None
45
+ self._dirty = False
46
+ logger.debug("BM25 index rebuilt with %d chunks", len(self._chunks))
47
+
48
+ def mark_dirty(self) -> None:
49
+ self._dirty = True
50
+
51
+ async def retrieve(self, query: str, top_k: Optional[int] = None) -> list[CodeChunk]:
52
+ k = top_k or self.config.top_k
53
+ if not query.strip():
54
+ return []
55
+ retriever_type = self.config.retriever
56
+ if retriever_type == "bm25":
57
+ return self._retrieve_bm25(query, k)
58
+ elif retriever_type == "embedding":
59
+ return await self._retrieve_embedding(query, k) or self._retrieve_bm25(query, k)
60
+ elif retriever_type == "hybrid":
61
+ return await self._retrieve_hybrid(query, k)
62
+ return self._retrieve_bm25(query, k)
63
+
64
+ def _retrieve_bm25(self, query: str, top_k: int) -> list[CodeChunk]:
65
+ self._ensure_indexed()
66
+ if not self._bm25 or not self._chunks:
67
+ return []
68
+ results = self._bm25.search(query, top_k=top_k)
69
+ scored: list[tuple[CodeChunk, float]] = []
70
+ for idx, score in results:
71
+ if 0 <= idx < len(self._chunks):
72
+ scored.append((self._chunks[idx], score))
73
+ scored.sort(key=lambda x: x[1], reverse=True)
74
+ return [c for c, _ in scored[:top_k]]
75
+
76
+ async def _retrieve_embedding(self, query: str, top_k: int) -> Optional[list[CodeChunk]]:
77
+ self._ensure_indexed()
78
+ if not self._chunks:
79
+ return None
80
+
81
+ query_emb = await _get_embedding(query, self.config)
82
+ if query_emb is None:
83
+ return None
84
+
85
+ embs = await _get_batch_embeddings([c.content[:1000] for c in self._chunks], self.config)
86
+ if not embs or len(embs) != len(self._chunks):
87
+ return None
88
+
89
+ scored = []
90
+ for i, emb in enumerate(embs):
91
+ if emb is not None:
92
+ score = _cosine_similarity(query_emb, emb)
93
+ scored.append((self._chunks[i], score))
94
+
95
+ scored.sort(key=lambda x: x[1], reverse=True)
96
+ return [c for c, _ in scored[:top_k]]
97
+
98
+ async def _retrieve_hybrid(self, query: str, top_k: int) -> list[CodeChunk]:
99
+ bm25_chunks = self._retrieve_bm25(query, top_k * 2)
100
+ emb_chunks = await self._retrieve_embedding(query, top_k * 2) or []
101
+ seen: set[str] = set()
102
+ merged: list[CodeChunk] = []
103
+ for chunk in bm25_chunks + emb_chunks:
104
+ key = chunk.identifier
105
+ if key not in seen:
106
+ seen.add(key)
107
+ merged.append(chunk)
108
+ return merged[:top_k]
109
+
110
+
111
+ EMBEDDING_CACHE: dict[str, list[float]] = {}
112
+ BATCH_CACHE: dict[int, Optional[list[float]]] = {}
113
+
114
+
115
+ async def _get_embedding(text: str, config: CodebaseConfig) -> Optional[list[float]]:
116
+ if text in EMBEDDING_CACHE:
117
+ return EMBEDDING_CACHE[text]
118
+ try:
119
+ from onecode.config import load_config
120
+ cfg = load_config()
121
+ provider_name = config.embedding_provider or cfg.default_provider
122
+ pcfg = cfg.providers.get(provider_name)
123
+ if not pcfg:
124
+ return None
125
+ if provider_name == "openai":
126
+ emb = await _openai_embed(text, pcfg.api_key or "", pcfg.endpoint or "")
127
+ elif provider_name == "ollama":
128
+ emb = await _ollama_embed(text, pcfg.endpoint or "http://localhost:11434")
129
+ else:
130
+ emb = await _openai_embed(text, pcfg.api_key or "", pcfg.endpoint or "")
131
+ if emb is not None:
132
+ EMBEDDING_CACHE[text] = emb
133
+ return emb
134
+ except Exception as e:
135
+ logger.warning("Embedding failed: %s", e)
136
+ return None
137
+
138
+
139
+ async def _get_batch_embeddings(texts: list[str], config: CodebaseConfig) -> list[Optional[list[float]]]:
140
+ global BATCH_CACHE
141
+ key = hash(tuple(texts))
142
+ if key in BATCH_CACHE:
143
+ return BATCH_CACHE[key]
144
+
145
+ from onecode.config import load_config
146
+ cfg = load_config()
147
+ provider_name = config.embedding_provider or cfg.default_provider
148
+ pcfg = cfg.providers.get(provider_name)
149
+
150
+ if provider_name == "ollama" and pcfg:
151
+ embs = await _ollama_batch_embed(texts, pcfg.endpoint or "http://localhost:11434")
152
+ else:
153
+ embs = []
154
+ for t in texts:
155
+ emb = await _get_embedding(t, config)
156
+ embs.append(emb)
157
+
158
+ BATCH_CACHE[key] = embs
159
+ return embs
160
+
161
+
162
+ async def _openai_embed(text: str, api_key: str, endpoint: str) -> Optional[list[float]]:
163
+ url = f"{endpoint.rstrip('/')}/embeddings"
164
+ async with httpx.AsyncClient(timeout=30) as client:
165
+ resp = await client.post(
166
+ url,
167
+ headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
168
+ json={"input": text, "model": "text-embedding-3-small"},
169
+ )
170
+ data = resp.json()
171
+ emb = data.get("data", [{}])[0].get("embedding")
172
+ return emb if emb else None
173
+
174
+
175
+ async def _ollama_embed(text: str, endpoint: str) -> Optional[list[float]]:
176
+ url = f"{endpoint.rstrip('/')}/api/embeddings"
177
+ async with httpx.AsyncClient(timeout=30) as client:
178
+ resp = await client.post(
179
+ url,
180
+ json={"model": "nomic-embed-text", "prompt": text},
181
+ )
182
+ data = resp.json()
183
+ emb = data.get("embedding")
184
+ return emb if emb else None
185
+
186
+
187
+ async def _ollama_batch_embed(texts: list[str], endpoint: str) -> list[Optional[list[float]]]:
188
+ embs = []
189
+ for t in texts:
190
+ emb = await _ollama_embed(t, endpoint)
191
+ embs.append(emb)
192
+ return embs
@@ -0,0 +1,130 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from pathlib import Path
5
+ from typing import Optional
6
+
7
+ from sqlalchemy import Column, String, Text, Integer, Float, create_engine, Index
8
+ from sqlalchemy.orm import declarative_base, sessionmaker, Session
9
+
10
+ from onecode.codebase.chunker import CodeChunk
11
+
12
+ Base = declarative_base()
13
+
14
+
15
+ class ChunkRecord(Base):
16
+ __tablename__ = "chunks"
17
+
18
+ id = Column(Integer, primary_key=True, autoincrement=True)
19
+ file_path = Column(String(1024), nullable=False, index=True)
20
+ start_line = Column(Integer, nullable=False)
21
+ end_line = Column(Integer, nullable=False)
22
+ content = Column(Text, nullable=False)
23
+ language = Column(String(64), default="")
24
+ file_mtime = Column(Float, default=0.0)
25
+
26
+ __table_args__ = (
27
+ Index("idx_file_path", "file_path"),
28
+ Index("idx_language", "language"),
29
+ )
30
+
31
+
32
+ class CodebaseStorage:
33
+ def __init__(self, project_dir: Path):
34
+ db_dir = project_dir / ".cdh" / "codebase"
35
+ db_dir.mkdir(parents=True, exist_ok=True)
36
+ self.db_path = db_dir / "index.db"
37
+ self.engine = create_engine(f"sqlite:///{self.db_path}", echo=False)
38
+ Base.metadata.create_all(self.engine)
39
+ self.Session = sessionmaker(bind=self.engine)
40
+
41
+ def save_chunks(self, chunks: list[CodeChunk], mtime: float = 0.0) -> None:
42
+ if not chunks:
43
+ return
44
+ file_path = chunks[0].file_path
45
+ with self.Session() as s:
46
+ s.query(ChunkRecord).filter_by(file_path=file_path).delete()
47
+ for c in chunks:
48
+ s.add(ChunkRecord(
49
+ file_path=c.file_path,
50
+ start_line=c.start_line,
51
+ end_line=c.end_line,
52
+ content=c.content,
53
+ language=c.language,
54
+ file_mtime=mtime,
55
+ ))
56
+ s.commit()
57
+
58
+ def remove_file(self, file_path: str) -> None:
59
+ with self.Session() as s:
60
+ s.query(ChunkRecord).filter_by(file_path=file_path).delete()
61
+ s.commit()
62
+
63
+ def remove_directory(self, dir_path: str) -> None:
64
+ prefix = dir_path.rstrip("/") + "/"
65
+ with self.Session() as s:
66
+ s.query(ChunkRecord).filter(
67
+ ChunkRecord.file_path.like(f"{prefix}%")
68
+ ).delete(synchronize_session=False)
69
+ s.commit()
70
+
71
+ def get_file_mtime(self, file_path: str) -> float:
72
+ with self.Session() as s:
73
+ record = (
74
+ s.query(ChunkRecord.file_mtime)
75
+ .filter_by(file_path=file_path)
76
+ .order_by(ChunkRecord.file_mtime.desc())
77
+ .first()
78
+ )
79
+ return record[0] if record else 0.0
80
+
81
+ def get_indexed_files(self) -> set[str]:
82
+ with self.Session() as s:
83
+ rows = s.query(ChunkRecord.file_path).distinct().all()
84
+ return {row[0] for row in rows}
85
+
86
+ def get_all_chunks(self) -> list[CodeChunk]:
87
+ with self.Session() as s:
88
+ records = s.query(ChunkRecord).all()
89
+ return [
90
+ CodeChunk(
91
+ file_path=r.file_path,
92
+ start_line=r.start_line,
93
+ end_line=r.end_line,
94
+ content=r.content,
95
+ language=r.language,
96
+ )
97
+ for r in records
98
+ ]
99
+
100
+ def get_chunks_for_file(self, file_path: str) -> list[CodeChunk]:
101
+ with self.Session() as s:
102
+ records = (
103
+ s.query(ChunkRecord)
104
+ .filter_by(file_path=file_path)
105
+ .order_by(ChunkRecord.start_line)
106
+ .all()
107
+ )
108
+ return [
109
+ CodeChunk(
110
+ file_path=r.file_path,
111
+ start_line=r.start_line,
112
+ end_line=r.end_line,
113
+ content=r.content,
114
+ language=r.language,
115
+ )
116
+ for r in records
117
+ ]
118
+
119
+ def chunk_count(self) -> int:
120
+ with self.Session() as s:
121
+ return s.query(ChunkRecord).count()
122
+
123
+ def file_count(self) -> int:
124
+ with self.Session() as s:
125
+ return s.query(ChunkRecord.file_path).distinct().count()
126
+
127
+ def clear_all(self) -> None:
128
+ with self.Session() as s:
129
+ s.query(ChunkRecord).delete()
130
+ s.commit()