@scemoon/cdh 1.0.3 → 1.0.5

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 (294) hide show
  1. package/ai-dlc-skill/SKILL.md +4 -2
  2. package/ai-dlc-skill/agents/master.md +6 -0
  3. package/ai-dlc-skill/agents/plan-agent.md +2 -2
  4. package/ai-dlc-skill/agents/understand-agent.md +4 -4
  5. package/ai-dlc-skill/agents/verify-agent.md +1 -1
  6. package/ai-dlc-skill/architecture/project-structure.md +10 -10
  7. package/ai-dlc-skill/brownfield/README.md +2 -2
  8. package/ai-dlc-skill/brownfield/generate-context.sh +3 -3
  9. package/ai-dlc-skill/brownfield/scripts/extract-api.sh +5 -5
  10. package/ai-dlc-skill/components/backend.md +1 -1
  11. package/ai-dlc-skill/components/web.md +2 -2
  12. package/ai-dlc-skill/contracts/README.md +3 -3
  13. package/ai-dlc-skill/cross-tool/cline/export.sh +1 -1
  14. package/ai-dlc-skill/cross-tool/copilot/export.sh +1 -1
  15. package/ai-dlc-skill/cross-tool/onecode/export-skills.sh +7 -3
  16. package/ai-dlc-skill/cross-tool/onecode/export.sh +14 -8
  17. package/ai-dlc-skill/phases/deliver/lifecycle.md +6 -6
  18. package/ai-dlc-skill/phases/deliver/prompt.md +2 -2
  19. package/ai-dlc-skill/phases/deliver/rules.md +1 -1
  20. package/ai-dlc-skill/phases/plan/lifecycle.md +11 -11
  21. package/ai-dlc-skill/phases/plan/prompt.md +6 -6
  22. package/ai-dlc-skill/phases/plan/rules.md +1 -1
  23. package/ai-dlc-skill/phases/understand/entry.md +1 -1
  24. package/ai-dlc-skill/phases/understand/lifecycle.md +7 -7
  25. package/ai-dlc-skill/phases/understand/prompt.md +8 -8
  26. package/ai-dlc-skill/phases/understand/rules.md +5 -5
  27. package/ai-dlc-skill/phases/verify/lifecycle.md +24 -24
  28. package/ai-dlc-skill/phases/verify/prompt.md +8 -8
  29. package/ai-dlc-skill/phases/verify/rules.md +9 -9
  30. package/ai-dlc-skill/providers/README.md +2 -2
  31. package/ai-dlc-skill/providers/aliyun/preview.yaml +2 -2
  32. package/ai-dlc-skill/providers/tcb/preview.yaml +2 -2
  33. package/ai-dlc-skill/requirements.md +7 -7
  34. package/ai-dlc-skill/skill.yaml +4 -4
  35. package/ai-dlc-skill/templates/integration/CHANGELOG.md +2 -2
  36. package/ai-dlc-skill/templates/integration/contract-diff.md +7 -7
  37. package/ai-dlc-skill/templates/integration/contract-spec.md +7 -7
  38. package/ai-dlc-skill/templates/integration/runtime-contract.yaml +1 -1
  39. package/ai-dlc-skill/templates/project/README.md +18 -19
  40. package/ai-dlc-skill/templates/project/template.md +21 -21
  41. package/ai-dlc-skill/walkthrough/collect.sh +1 -1
  42. package/ai-dlc-skill/walkthrough/template.md +2 -2
  43. package/ai-dlc-skill/workflows/ai-dlc.yaml +12 -12
  44. package/ai-dlc-skill/workflows/deployment.yaml +4 -4
  45. package/cdh/__pycache__/cdh_mcp_injector.cpython-314.pyc +0 -0
  46. package/cdh/__pycache__/cdh_mcp_loader.cpython-314.pyc +0 -0
  47. package/cdh/__pycache__/cdh_mcp_manager.cpython-314.pyc +0 -0
  48. package/cdh/__pycache__/cdh_session_aggregator.cpython-314.pyc +0 -0
  49. package/cdh/__pycache__/cdh_skill_loader.cpython-314.pyc +0 -0
  50. package/cdh/__pycache__/cdh_skill_manager.cpython-314.pyc +0 -0
  51. package/cdh/__pycache__/cli.cpython-313.pyc +0 -0
  52. package/cdh/__pycache__/cli.cpython-314.pyc +0 -0
  53. package/cdh/__pycache__/scaffold.cpython-314.pyc +0 -0
  54. package/cdh/cdh_mcp_injector.py +115 -0
  55. package/cdh/cdh_mcp_loader.py +67 -0
  56. package/cdh/cdh_mcp_manager.py +73 -0
  57. package/cdh/cdh_session_aggregator.py +258 -0
  58. package/cdh/cdh_skill_manager.py +105 -0
  59. package/cdh/cli.py +14 -3
  60. package/cdh/scaffold.py +192 -41
  61. package/onecode/__init__.py +1 -1
  62. package/onecode/__pycache__/cli.cpython-314.pyc +0 -0
  63. package/onecode/__pycache__/commands.cpython-314.pyc +0 -0
  64. package/onecode/__pycache__/config.cpython-314.pyc +0 -0
  65. package/onecode/__pycache__/config_screen.cpython-314.pyc +0 -0
  66. package/onecode/__pycache__/migrate.cpython-314.pyc +0 -0
  67. package/onecode/agent/__pycache__/cdh_loader.cpython-314.pyc +0 -0
  68. package/onecode/agent/__pycache__/context.cpython-314.pyc +0 -0
  69. package/onecode/agent/__pycache__/engine.cpython-314.pyc +0 -0
  70. package/onecode/agent/__pycache__/hooks.cpython-314.pyc +0 -0
  71. package/onecode/agent/__pycache__/onecode_agent_acp.cpython-314.pyc +0 -0
  72. package/onecode/agent/__pycache__/permissions.cpython-314.pyc +0 -0
  73. package/onecode/agent/__pycache__/project_doc.cpython-314.pyc +0 -0
  74. package/onecode/agent/__pycache__/session.cpython-314.pyc +0 -0
  75. package/onecode/agent/agents/__pycache__/types.cpython-314.pyc +0 -0
  76. package/onecode/agent/agents/types.py +47 -164
  77. package/onecode/agent/attachments.py +0 -1
  78. package/onecode/agent/cdh_loader.py +17 -25
  79. package/onecode/agent/context.py +8 -3
  80. package/onecode/agent/engine.py +835 -220
  81. package/onecode/agent/hooks.py +1 -1
  82. package/onecode/agent/onecode_agent_acp.py +462 -49
  83. package/onecode/agent/onecode_agent_acp.py.bak +2447 -0
  84. package/onecode/agent/permissions.py +0 -1
  85. package/onecode/agent/project_doc.py +19 -0
  86. package/onecode/agent/session.py +17 -3
  87. package/onecode/agent/snapshot.py +3 -4
  88. package/onecode/agent/tools/__pycache__/agent_tools.cpython-314.pyc +0 -0
  89. package/onecode/agent/tools/__pycache__/apply_patch_tool.cpython-314.pyc +0 -0
  90. package/onecode/agent/tools/__pycache__/bash_tool.cpython-314.pyc +0 -0
  91. package/onecode/agent/tools/__pycache__/communication_tools.cpython-314.pyc +0 -0
  92. package/onecode/agent/tools/__pycache__/config_tool.cpython-314.pyc +0 -0
  93. package/onecode/agent/tools/__pycache__/cron_tools.cpython-314.pyc +0 -0
  94. package/onecode/agent/tools/__pycache__/file_ops.cpython-314.pyc +0 -0
  95. package/onecode/agent/tools/__pycache__/file_tools.cpython-314.pyc +0 -0
  96. package/onecode/agent/tools/__pycache__/git_tools.cpython-314.pyc +0 -0
  97. package/onecode/agent/tools/__pycache__/lsp_tools.cpython-314.pyc +0 -0
  98. package/onecode/agent/tools/__pycache__/mcp_tools.cpython-314.pyc +0 -0
  99. package/onecode/agent/tools/__pycache__/permission_handler.cpython-314.pyc +0 -0
  100. package/onecode/agent/tools/__pycache__/protocol.cpython-314.pyc +0 -0
  101. package/onecode/agent/tools/__pycache__/registry.cpython-314.pyc +0 -0
  102. package/onecode/agent/tools/__pycache__/sandbox.cpython-314.pyc +0 -0
  103. package/onecode/agent/tools/__pycache__/skill_tools.cpython-314.pyc +0 -0
  104. package/onecode/agent/tools/__pycache__/todo_tools.cpython-314.pyc +0 -0
  105. package/onecode/agent/tools/__pycache__/web_tools.cpython-314.pyc +0 -0
  106. package/onecode/agent/tools/agent_tools.py +4 -6
  107. package/onecode/agent/tools/apply_patch_tool.py +2 -8
  108. package/onecode/agent/tools/bash_tool.py +32 -12
  109. package/onecode/agent/tools/communication_tools.py +4 -5
  110. package/onecode/agent/tools/config_tool.py +3 -3
  111. package/onecode/agent/tools/cron_tools.py +4 -5
  112. package/onecode/agent/tools/file_ops.py +37 -3
  113. package/onecode/agent/tools/file_tools.py +10 -11
  114. package/onecode/agent/tools/git_tools.py +2 -2
  115. package/onecode/agent/tools/lsp_tools.py +2 -2
  116. package/onecode/agent/tools/mcp_tools.py +3 -5
  117. package/onecode/agent/tools/permission_handler.py +1 -1
  118. package/onecode/agent/tools/protocol.py +1 -1
  119. package/onecode/agent/tools/registry.py +69 -2
  120. package/onecode/agent/tools/sandbox.py +110 -16
  121. package/onecode/agent/tools/skill_tools.py +2 -2
  122. package/onecode/agent/tools/todo_tools.py +38 -19
  123. package/onecode/agent/tools/web_tools.py +6 -7
  124. package/onecode/builtin_skills/agent-browser/SKILL.md +212 -0
  125. package/onecode/builtin_skills/agent-browser/skill.yaml +8 -0
  126. package/onecode/builtin_skills/skill-creator/SKILL.md +106 -0
  127. package/onecode/builtin_skills/skill-creator/skill.yaml +8 -0
  128. package/onecode/cli.py +186 -6
  129. package/onecode/codebase/__pycache__/chunker.cpython-314.pyc +0 -0
  130. package/onecode/codebase/__pycache__/indexer.cpython-314.pyc +0 -0
  131. package/onecode/codebase/__pycache__/retriever.cpython-314.pyc +0 -0
  132. package/onecode/codebase/__pycache__/storage.cpython-314.pyc +0 -0
  133. package/onecode/codebase/chunker.py +0 -5
  134. package/onecode/codebase/indexer.py +14 -1
  135. package/onecode/codebase/retriever.py +13 -2
  136. package/onecode/codebase/storage.py +5 -4
  137. package/onecode/commands.py +81 -6
  138. package/onecode/config.py +41 -12
  139. package/onecode/config_screen.py +7 -3
  140. package/onecode/mcp/__pycache__/manager.cpython-314.pyc +0 -0
  141. package/onecode/mcp/manager.py +2 -3
  142. package/onecode/memory/__init__.py +7 -53
  143. package/onecode/memory/__pycache__/__init__.cpython-314.pyc +0 -0
  144. package/onecode/memory/__pycache__/backend.cpython-314.pyc +0 -0
  145. package/onecode/memory/__pycache__/offload.cpython-314.pyc +0 -0
  146. package/onecode/memory/__pycache__/pyramid.cpython-314.pyc +0 -0
  147. package/onecode/memory/__pycache__/recall.cpython-314.pyc +0 -0
  148. package/onecode/memory/backend.py +13 -39
  149. package/onecode/memory/pyramid.py +2 -71
  150. package/onecode/memory/recall.py +38 -3
  151. package/onecode/migrate.py +122 -0
  152. package/onecode/models/__pycache__/messages.cpython-314.pyc +0 -0
  153. package/onecode/models/__pycache__/provider.cpython-314.pyc +0 -0
  154. package/onecode/models/messages.py +58 -0
  155. package/onecode/models/provider.py +20 -2
  156. package/onecode/models/providers/__pycache__/anthropic_provider.cpython-314.pyc +0 -0
  157. package/onecode/models/providers/__pycache__/glm_provider.cpython-314.pyc +0 -0
  158. package/onecode/models/providers/__pycache__/minimax_provider.cpython-314.pyc +0 -0
  159. package/onecode/models/providers/__pycache__/minimaxi_provider.cpython-314.pyc +0 -0
  160. package/onecode/models/providers/__pycache__/ollama_provider.cpython-314.pyc +0 -0
  161. package/onecode/models/providers/anthropic_provider.py +59 -12
  162. package/onecode/models/providers/glm_provider.py +40 -0
  163. package/onecode/models/providers/minimax_provider.py +3 -0
  164. package/onecode/models/providers/minimaxi_provider.py +3 -0
  165. package/onecode/models/providers/ollama_provider.py +3 -0
  166. package/onecode/server/app.py +1 -1
  167. package/onecode/skills/__pycache__/argument_substitution.cpython-314.pyc +0 -0
  168. package/onecode/skills/__pycache__/bootstrap.cpython-314.pyc +0 -0
  169. package/onecode/skills/__pycache__/loader.cpython-314.pyc +0 -0
  170. package/onecode/skills/__pycache__/manager.cpython-314.pyc +0 -0
  171. package/onecode/skills/argument_substitution.py +0 -1
  172. package/onecode/skills/bootstrap.py +124 -0
  173. package/onecode/skills/loader.py +35 -53
  174. package/onecode/skills/manager.py +2 -2
  175. package/onecode/storage/activity.py +3 -4
  176. package/onecode/storage/session.py +3 -4
  177. package/onecode/tasks/manager.py +23 -10
  178. package/onecode/trace/tracer.py +2 -3
  179. package/package.json +1 -1
  180. package/pyproject.toml +5 -1
  181. package/tui/__pycache__/about.cpython-314.pyc +0 -0
  182. package/tui/__pycache__/agents.cpython-314.pyc +0 -0
  183. package/tui/__pycache__/app.cpython-313.pyc +0 -0
  184. package/tui/__pycache__/app.cpython-314.pyc +0 -0
  185. package/tui/__pycache__/danger.cpython-314.pyc +0 -0
  186. package/tui/__pycache__/messages.cpython-314.pyc +0 -0
  187. package/tui/__pycache__/path_complete.cpython-314.pyc +0 -0
  188. package/tui/__pycache__/paths.cpython-314.pyc +0 -0
  189. package/tui/__pycache__/session_tracker.cpython-314.pyc +0 -0
  190. package/tui/__pycache__/settings_schema.cpython-314.pyc +0 -0
  191. package/tui/acp/__pycache__/agent.cpython-314.pyc +0 -0
  192. package/tui/acp/__pycache__/api.cpython-314.pyc +0 -0
  193. package/tui/acp/__pycache__/messages.cpython-314.pyc +0 -0
  194. package/tui/acp/__pycache__/prompt.cpython-314.pyc +0 -0
  195. package/tui/acp/__pycache__/protocol.cpython-314.pyc +0 -0
  196. package/tui/acp/agent.py +125 -58
  197. package/tui/acp/api.py +12 -0
  198. package/tui/acp/messages.py +3 -1
  199. package/tui/acp/prompt.py +0 -57
  200. package/tui/acp/protocol.py +6 -0
  201. package/tui/agents.py +8 -7
  202. package/tui/app.py +11 -33
  203. package/tui/data/agents/ampcode.com.toml +1 -0
  204. package/tui/data/agents/augmentcode.com.toml +1 -0
  205. package/tui/data/agents/cdh.cloud-dev-harness.toml +3 -2
  206. package/tui/data/agents/crowai.dev.toml +1 -0
  207. package/tui/data/agents/deepagents.langchain.com +1 -0
  208. package/tui/data/agents/docker.com.toml +1 -0
  209. package/tui/data/agents/goose.ai.toml +1 -0
  210. package/tui/data/agents/inference.huggingface.co.toml +1 -1
  211. package/tui/data/agents/kiro.dev.toml +1 -0
  212. package/tui/data/agents/onecode.dev.toml +22 -0
  213. package/tui/data/agents/openclaw.ai.toml +1 -1
  214. package/tui/data/agents/openhands.dev.toml +1 -0
  215. package/tui/data/agents/stakpak.dev.toml +1 -0
  216. package/tui/data/agents/vibe.mistral.ai.toml +1 -0
  217. package/tui/data/agents/vtcode.dev.toml +1 -0
  218. package/tui/messages.py +2 -0
  219. package/tui/path_complete.py +1 -1
  220. package/tui/paths.py +16 -0
  221. package/tui/screens/__pycache__/component_picker.cpython-314.pyc +0 -0
  222. package/tui/screens/__pycache__/diff_screen.cpython-314.pyc +0 -0
  223. package/tui/screens/__pycache__/log.cpython-314.pyc +0 -0
  224. package/tui/screens/__pycache__/main.cpython-313.pyc +0 -0
  225. package/tui/screens/__pycache__/main.cpython-314.pyc +0 -0
  226. package/tui/screens/__pycache__/permissions.cpython-314.pyc +0 -0
  227. package/tui/screens/__pycache__/projects_app.cpython-314.pyc +0 -0
  228. package/tui/screens/__pycache__/projects_screen.cpython-314.pyc +0 -0
  229. package/tui/screens/__pycache__/sessions.cpython-314.pyc +0 -0
  230. package/tui/screens/__pycache__/store.cpython-314.pyc +0 -0
  231. package/tui/screens/__pycache__/tool_content_screen.cpython-314.pyc +0 -0
  232. package/tui/screens/component_picker.py +35 -15
  233. package/tui/screens/diff_screen.py +84 -0
  234. package/tui/screens/log.py +25 -25
  235. package/tui/screens/main.py +48 -26
  236. package/tui/screens/projects.tcss +5 -1
  237. package/tui/screens/projects_app.py +6 -6
  238. package/tui/screens/projects_screen.py +19 -39
  239. package/tui/screens/sessions.py +3 -5
  240. package/tui/screens/store.py +1 -1
  241. package/tui/session_tracker.py +3 -0
  242. package/tui/settings_schema.py +2 -2
  243. package/tui/visuals/__pycache__/columns.cpython-314.pyc +0 -0
  244. package/tui/visuals/columns.py +1 -2
  245. package/tui/widgets/__pycache__/agent_response.cpython-314.pyc +0 -0
  246. package/tui/widgets/__pycache__/agent_thought.cpython-314.pyc +0 -0
  247. package/tui/widgets/__pycache__/conversation.cpython-314.pyc +0 -0
  248. package/tui/widgets/__pycache__/diff_view.cpython-314.pyc +0 -0
  249. package/tui/widgets/__pycache__/load_more.cpython-314.pyc +0 -0
  250. package/tui/widgets/__pycache__/modified_files.cpython-314.pyc +0 -0
  251. package/tui/widgets/__pycache__/plan.cpython-314.pyc +0 -0
  252. package/tui/widgets/__pycache__/project_grid_select.cpython-314.pyc +0 -0
  253. package/tui/widgets/__pycache__/session_grid_select.cpython-314.pyc +0 -0
  254. package/tui/widgets/__pycache__/shell_result.cpython-314.pyc +0 -0
  255. package/tui/widgets/__pycache__/side_bar.cpython-314.pyc +0 -0
  256. package/tui/widgets/__pycache__/subagent.cpython-314.pyc +0 -0
  257. package/tui/widgets/__pycache__/subagent_screen.cpython-314.pyc +0 -0
  258. package/tui/widgets/__pycache__/terminal.cpython-314.pyc +0 -0
  259. package/tui/widgets/__pycache__/tool_call.cpython-314.pyc +0 -0
  260. package/tui/widgets/agent_response.py +0 -1
  261. package/tui/widgets/agent_thought.py +0 -1
  262. package/tui/widgets/conversation.py +157 -14
  263. package/tui/widgets/diff_view.py +10 -0
  264. package/tui/widgets/load_more.py +24 -0
  265. package/tui/widgets/modified_files.py +173 -105
  266. package/tui/widgets/plan.py +1 -1
  267. package/tui/widgets/project_grid_select.py +9 -2
  268. package/tui/widgets/session_grid_select.py +6 -0
  269. package/tui/widgets/side_bar.py +10 -0
  270. package/tui/widgets/subagent.py +92 -58
  271. package/tui/widgets/subagent_screen.py +638 -115
  272. package/tui/widgets/terminal.py +1 -2
  273. package/tui/widgets/terminal_tool.py +11 -2
  274. package/tui/widgets/tool_call.py +35 -2
  275. package/cdh/__pycache__/__init__.cpython-313.pyc +0 -0
  276. package/cdh/__pycache__/__main__.cpython-314.pyc +0 -0
  277. package/cdh/__pycache__/scaffold.cpython-313.pyc +0 -0
  278. package/onecode/__pycache__/mock_data.cpython-314.pyc +0 -0
  279. package/onecode/agent/__pycache__/cdh_agent_acp.cpython-313.pyc +0 -0
  280. package/onecode/agent/__pycache__/cdh_agent_acp.cpython-314.pyc +0 -0
  281. package/onecode/agent/__pycache__/harness_skill.cpython-314.pyc +0 -0
  282. package/onecode/agent/tools/__pycache__/task_tools.cpython-314.pyc +0 -0
  283. package/onecode/codebase/__pycache__/config.cpython-314.pyc +0 -0
  284. package/onecode/memory/offload.py +0 -114
  285. package/onecode/memory/symbolic.py +0 -164
  286. package/onecode/models/providers/__pycache__/mock_provider.cpython-314.pyc +0 -0
  287. package/onecode/tasks/__pycache__/__init__.cpython-314.pyc +0 -0
  288. package/onecode/tasks/__pycache__/manager.cpython-314.pyc +0 -0
  289. package/onecode/tasks/__pycache__/models.cpython-314.pyc +0 -0
  290. package/tui/__pycache__/cli.cpython-314.pyc +0 -0
  291. package/tui/screens/__pycache__/settings.cpython-314.pyc +0 -0
  292. package/tui/widgets/__pycache__/conversation.cpython-313.pyc +0 -0
  293. package/tui/widgets/__pycache__/terminal.cpython-313.pyc +0 -0
  294. package/tui/widgets/__pycache__/tool_call.cpython-313.pyc +0 -0
@@ -35,11 +35,11 @@ phases:
35
35
  - UND-001..006: Understand rules
36
36
  - Human review and approval
37
37
  artifacts:
38
- - requirements.md
39
- - openspec/changes/{id}/spec-delta.md
38
+ - aidlc/requirements.md
39
+ - aidlc/openspec/changes/{id}/spec-delta.md
40
40
  - apps/{component}/features/{domain}/{feature}.feature
41
- - features/cross-stack/{domain}/{feature}.feature
42
- - contracts/api|events/{name}.{yaml,graphql}
41
+ - aidlc/features/cross-stack/{domain}/{feature}.feature
42
+ - aidlc/contracts/api|events/{name}.{yaml,graphql}
43
43
 
44
44
  - name: plan
45
45
  label: "② Plan — 规划"
@@ -57,9 +57,9 @@ phases:
57
57
  - PLN-001..004: Plan rules
58
58
  - Human review and approval
59
59
  artifacts:
60
- - openspec/changes/{id}/design.md
61
- - openspec/changes/{id}/task-list.md
62
- - openspec/changes/{id}/contract-diff.md
60
+ - aidlc/openspec/changes/{id}/design.md
61
+ - aidlc/openspec/changes/{id}/task-list.md
62
+ - aidlc/openspec/changes/{id}/contract-diff.md
63
63
 
64
64
  - name: verify
65
65
  label: "③ Verify — 验证"
@@ -81,10 +81,10 @@ phases:
81
81
  - apps/{component}/tests/{unit,integration,e2e}/test_{feature}.py
82
82
  - apps/{component}/features/steps/test_{feature}_steps.py
83
83
  - apps/{component}/src/{module}/{feature}.py
84
- - tests/contract/test_{contract}.py
85
- - tests/cross-stack/test_{flow}.py
86
- - packages/shared/
87
- - openspec/changes/{id}/contract-diff.md
84
+ - aidlc/tests/contract/test_{contract}.py
85
+ - aidlc/tests/cross-stack/test_{flow}.py
86
+ - aidlc/packages/shared/
87
+ - aidlc/openspec/changes/{id}/contract-diff.md
88
88
 
89
89
  - name: deliver
90
90
  label: "④ Deliver — 交付"
@@ -105,6 +105,6 @@ phases:
105
105
  - STACK_URL / BACKEND_URL
106
106
  - Per-component e2e report
107
107
  - Cross-stack e2e report
108
- - openspec/changes/{id}/contract-diff.md
108
+ - aidlc/openspec/changes/{id}/contract-diff.md
109
109
  - Stack BVT report
110
110
  - Deploy log
@@ -107,7 +107,7 @@ stages:
107
107
  requires: [deploy_stack]
108
108
  when: affects contains tta
109
109
  - name: e2e_cross_stack
110
- command: pytest tests/cross-stack/ --stack-url ${STACK_URL}
110
+ command: pytest aidlc/tests/cross-stack/ --stack-url ${STACK_URL}
111
111
  requires: [e2e_backend, e2e_web, e2e_native, e2e_desktop, e2e_wxa, e2e_mya, e2e_tta]
112
112
  when: affects contains >= 2 components
113
113
 
@@ -119,7 +119,7 @@ stages:
119
119
  - name: smoke
120
120
  command: smoke-test ${STACK_URL}
121
121
  - name: cross_stack
122
- command: pytest tests/cross-stack/ --stack-url ${STACK_URL}
122
+ command: pytest aidlc/tests/cross-stack/ --stack-url ${STACK_URL}
123
123
 
124
124
  - name: production
125
125
  description: Stack production deploy with stack BVT
@@ -132,7 +132,7 @@ stages:
132
132
  - per_component_e2e_reports
133
133
  - cross_stack_e2e_report
134
134
  - staging_smoke_report
135
- - openspec/changes/{id}/contract-diff.md
135
+ - aidlc/openspec/changes/{id}/contract-diff.md
136
136
  - name: deploy_stack
137
137
  command: deploy_stack --env production
138
138
  - name: bvt
@@ -142,7 +142,7 @@ stages:
142
142
  command: curl -f ${PRODUCTION_URL}/health
143
143
  timeout: 30s
144
144
  - name: archive_contract_diff
145
- command: cp openspec/changes/{id}/contract-diff.md deploy-artifacts/
145
+ command: cp aidlc/openspec/changes/{id}/contract-diff.md aidlc/deploy-artifacts/
146
146
 
147
147
  rollback:
148
148
  triggers:
Binary file
@@ -0,0 +1,115 @@
1
+ """cdh platform MCP injector — injects platform MCPs into engines.
2
+
3
+ Strategies (per engine capability):
4
+ - env var: Set CDH_SHARED_MCP with JSON-serialized platform MCP configs
5
+ - CLI args: Inject via engine-specific CLI flags (--mcp-server name=url)
6
+ - config file: Write a temp config that the engine reads at startup
7
+
8
+ Name conflict: engine private MCPs win over cdh platform MCPs.
9
+ Engines that don't support injection get a silent fallback (INFO log).
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ import logging
16
+ import os
17
+ from pathlib import Path
18
+ from typing import Optional
19
+
20
+ import yaml
21
+
22
+ logger = logging.getLogger("cdh.mcp.injector")
23
+
24
+ CDH_SHARED_MCP_ENV = "CDH_SHARED_MCP"
25
+
26
+
27
+ class CdhMcpInjector:
28
+ SUPPORTED_ENGINES = {"onecode", "opencode"}
29
+
30
+ def __init__(self, platform_mcps_dir: Path | None = None):
31
+ from cdh.cdh_mcp_manager import CDH_PLATFORM_MCPS_DIR
32
+
33
+ self.platform_mcps_dir = platform_mcps_dir or CDH_PLATFORM_MCPS_DIR
34
+
35
+ def inject_env(self) -> None:
36
+ """Set CDH_SHARED_MCP env var with platform MCP configs."""
37
+ config_path = self.platform_mcps_dir / "mcps.yaml"
38
+ if not config_path.exists():
39
+ return
40
+ try:
41
+ data = yaml.safe_load(config_path.read_text()) or {}
42
+ os.environ[CDH_SHARED_MCP_ENV] = json.dumps(data)
43
+ logger.debug("Set %s with %d MCP servers", CDH_SHARED_MCP_ENV, len(data))
44
+ except Exception as e:
45
+ logger.warning("Failed to inject platform MCPs via env: %s", e)
46
+
47
+ def clear_env(self) -> None:
48
+ os.environ.pop(CDH_SHARED_MCP_ENV, None)
49
+
50
+ def get_shared_mcp_config(self) -> dict[str, dict]:
51
+ raw = os.environ.get(CDH_SHARED_MCP_ENV, "")
52
+ if not raw:
53
+ return {}
54
+ try:
55
+ return json.loads(raw)
56
+ except json.JSONDecodeError:
57
+ logger.warning("Invalid %s value, ignoring", CDH_SHARED_MCP_ENV)
58
+ return {}
59
+
60
+ def inject_for_engine(
61
+ self,
62
+ engine_name: str,
63
+ engine_mcps_path: Path | None = None,
64
+ ) -> Optional[str]:
65
+ """Inject platform MCPs for a specific engine.
66
+
67
+ Returns an info/warning message, or None if no injection needed.
68
+
69
+ For supported engines: sets CDH_SHARED_MCP env var.
70
+ For unsupported engines: logs a silent INFO fallback.
71
+ """
72
+ platform_config = self._load_platform_config()
73
+ if not platform_config:
74
+ return None
75
+
76
+ if engine_name not in self.SUPPORTED_ENGINES:
77
+ logger.info(
78
+ "Engine '%s' does not support cdh platform MCP injection. "
79
+ "Platform MCPs available via %s env var.",
80
+ engine_name,
81
+ CDH_SHARED_MCP_ENV,
82
+ )
83
+ return None
84
+
85
+ # Merge with engine private (engine wins)
86
+ merged = dict(platform_config)
87
+ if engine_mcps_path and engine_mcps_path.exists():
88
+ try:
89
+ engine_data = yaml.safe_load(engine_mcps_path.read_text()) or {}
90
+ engine_data = {
91
+ k: v for k, v in engine_data.items() if isinstance(v, dict)
92
+ }
93
+ except Exception:
94
+ engine_data = {}
95
+ merged.update(engine_data)
96
+
97
+ os.environ[CDH_SHARED_MCP_ENV] = json.dumps(merged)
98
+ logger.debug(
99
+ "Injected %d platform MCPs + %d engine MCPs via %s for '%s'",
100
+ len(platform_config),
101
+ len(engine_data) if engine_mcps_path and engine_mcps_path.exists() else 0,
102
+ CDH_SHARED_MCP_ENV,
103
+ engine_name,
104
+ )
105
+ return f"{len(platform_config)} platform MCP(s) injected for '{engine_name}'"
106
+
107
+ def _load_platform_config(self) -> dict[str, dict]:
108
+ config_path = self.platform_mcps_dir / "mcps.yaml"
109
+ if not config_path.exists():
110
+ return {}
111
+ try:
112
+ data = yaml.safe_load(config_path.read_text()) or {}
113
+ return {k: v for k, v in data.items() if isinstance(v, dict)}
114
+ except Exception:
115
+ return {}
@@ -0,0 +1,67 @@
1
+ """cdh platform MCP loader — merges platform MCPs with engine-private MCPs.
2
+
3
+ Discovery order:
4
+ 1. Read engine-private MCPs from engine's own config (e.g. ~/.onecode/mcps/mcps.yaml)
5
+ 2. Read cdh platform MCPs from ~/.cdh/mcps/mcps.yaml
6
+ 3. Merge: engine private wins on name conflict
7
+ 4. Return merged list for injection
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import logging
13
+ from pathlib import Path
14
+
15
+ import yaml
16
+
17
+ from cdh.cdh_mcp_manager import CDH_PLATFORM_MCPS_DIR
18
+
19
+ logger = logging.getLogger("cdh.mcp.loader")
20
+
21
+
22
+ class CdhMcpLoader:
23
+ def __init__(self, platform_mcps_dir: Path | None = None):
24
+ self.platform_mcps_dir = platform_mcps_dir or CDH_PLATFORM_MCPS_DIR
25
+ self._cache: dict[str, dict] | None = None
26
+
27
+ def load_platform(self) -> dict[str, dict]:
28
+ config_path = self.platform_mcps_dir / "mcps.yaml"
29
+ if not config_path.exists():
30
+ return {}
31
+ try:
32
+ data = yaml.safe_load(config_path.read_text()) or {}
33
+ return {k: v for k, v in data.items() if isinstance(v, dict)}
34
+ except Exception:
35
+ logger.warning("Failed to load platform MCP config from %s", config_path)
36
+ return {}
37
+
38
+ def get_merged(
39
+ self, engine_mcps_path: Path | None = None
40
+ ) -> list[dict]:
41
+ platform = self.load_platform()
42
+
43
+ if engine_mcps_path and engine_mcps_path.exists():
44
+ try:
45
+ engine_data = yaml.safe_load(engine_mcps_path.read_text()) or {}
46
+ engine_data = {k: v for k, v in engine_data.items() if isinstance(v, dict)}
47
+ except Exception:
48
+ engine_data = {}
49
+ else:
50
+ engine_data = {}
51
+
52
+ merged = dict(platform)
53
+ merged.update(engine_data)
54
+
55
+ return [
56
+ {"name": name, **cfg, "source": "engine" if name in engine_data else "platform"}
57
+ for name, cfg in merged.items()
58
+ ]
59
+
60
+ def get_platform_only(self) -> list[dict]:
61
+ return [
62
+ {"name": name, **cfg, "source": "platform"}
63
+ for name, cfg in self.load_platform().items()
64
+ ]
65
+
66
+ def invalidate_cache(self) -> None:
67
+ self._cache = None
@@ -0,0 +1,73 @@
1
+ """cdh platform MCP manager — operates ~/.cdh/mcps/.
2
+
3
+ This is the cdh platform equivalent of onecode.mcp.manager.MCPManager,
4
+ but operates strictly on ~/.cdh/mcps/ (cdh platform shared MCP pool).
5
+ Engines (onecode/opencode/claude) read platform MCPs via env var
6
+ injection at launch time (see cdh_mcp_injector.py).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import logging
12
+ from pathlib import Path
13
+ from typing import Optional
14
+
15
+ import yaml
16
+
17
+ logger = logging.getLogger("cdh.mcp")
18
+
19
+ CDH_PLATFORM_MCPS_DIR = Path.home() / ".cdh" / "mcps"
20
+
21
+
22
+ class CdhMcpManager:
23
+ def __init__(self, mcps_dir: Path | None = None):
24
+ self.mcps_dir = mcps_dir or CDH_PLATFORM_MCPS_DIR
25
+ self.mcps_dir.mkdir(parents=True, exist_ok=True)
26
+ self.config_path = self.mcps_dir / "mcps.yaml"
27
+ self._data: dict[str, dict] = {}
28
+ if self.config_path.exists():
29
+ try:
30
+ self._data = yaml.safe_load(self.config_path.read_text()) or {}
31
+ except Exception:
32
+ self._data = {}
33
+
34
+ def add(self, name: str, url: str, transport: str = "sse") -> None:
35
+ self._data[name] = {"url": url, "transport": transport, "enabled": True}
36
+ self._save()
37
+
38
+ def add_stdio(self, name: str, command: str, args: list[str] | None = None) -> None:
39
+ self._data[name] = {
40
+ "command": command,
41
+ "args": args or [],
42
+ "transport": "stdio",
43
+ "enabled": True,
44
+ }
45
+ self._save()
46
+
47
+ def list(self) -> list[dict]:
48
+ return [{"name": name, **cfg} for name, cfg in self._data.items()]
49
+
50
+ def get(self, name: str) -> Optional[dict]:
51
+ return self._data.get(name)
52
+
53
+ def enable(self, name: str, enabled: bool = True) -> Optional[str]:
54
+ if name not in self._data:
55
+ return f"MCP server '{name}' not found"
56
+ self._data[name]["enabled"] = enabled
57
+ self._save()
58
+ return None
59
+
60
+ def remove(self, name: str) -> Optional[str]:
61
+ if name not in self._data:
62
+ return f"MCP server '{name}' not found"
63
+ del self._data[name]
64
+ self._save()
65
+ return None
66
+
67
+ def to_dict(self) -> dict[str, dict]:
68
+ return dict(self._data)
69
+
70
+ def _save(self) -> None:
71
+ self.config_path.write_text(
72
+ yaml.dump(self._data, default_flow_style=False), encoding="utf-8"
73
+ )
@@ -0,0 +1,258 @@
1
+ """cdh platform session aggregator — cross-engine session registry.
2
+
3
+ Maintains a session registry at ~/.cdh/sessions/sessions.db (SQLite).
4
+ Provides a cross-engine view across all registered engines.
5
+ Engines register sessions via the aggregator API.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import logging
12
+ import sqlite3
13
+ from datetime import datetime, timezone
14
+ from pathlib import Path
15
+ from typing import Any, Optional
16
+
17
+ logger = logging.getLogger("cdh.session")
18
+
19
+ CDH_SESSIONS_DIR = Path.home() / ".cdh" / "sessions"
20
+
21
+
22
+ class CdhSessionAggregator:
23
+ def __init__(self, sessions_dir: Path | None = None):
24
+ self.sessions_dir = sessions_dir or CDH_SESSIONS_DIR
25
+ self.sessions_dir.mkdir(parents=True, exist_ok=True)
26
+ self.db_path = self.sessions_dir / "sessions.db"
27
+ self._init_db()
28
+
29
+ def _init_db(self) -> None:
30
+ try:
31
+ conn = sqlite3.connect(str(self.db_path))
32
+ conn.execute(
33
+ """
34
+ CREATE TABLE IF NOT EXISTS sessions (
35
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
36
+ engine TEXT NOT NULL,
37
+ engine_session_id TEXT NOT NULL,
38
+ project_name TEXT DEFAULT '',
39
+ agent TEXT NOT NULL,
40
+ title TEXT NOT NULL,
41
+ protocol TEXT DEFAULT 'acp',
42
+ prompt_count INTEGER DEFAULT 0,
43
+ created_at TEXT NOT NULL,
44
+ last_used TEXT NOT NULL,
45
+ meta_json TEXT DEFAULT '{}',
46
+ UNIQUE(engine, engine_session_id)
47
+ )
48
+ """
49
+ )
50
+ conn.commit()
51
+ conn.close()
52
+ except sqlite3.Error as e:
53
+ logger.error("Failed to init session db: %s", e)
54
+
55
+ def register(
56
+ self,
57
+ engine: str,
58
+ engine_session_id: str,
59
+ agent: str,
60
+ title: str,
61
+ project_name: str = "",
62
+ protocol: str = "acp",
63
+ meta: dict[str, Any] | None = None,
64
+ ) -> Optional[int]:
65
+ now = datetime.now(timezone.utc).isoformat()
66
+ try:
67
+ conn = sqlite3.connect(str(self.db_path))
68
+ conn.execute(
69
+ """
70
+ INSERT OR REPLACE INTO sessions
71
+ (engine, engine_session_id, project_name, agent, title, protocol,
72
+ prompt_count, created_at, last_used, meta_json)
73
+ VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?, ?)
74
+ """,
75
+ (
76
+ engine,
77
+ engine_session_id,
78
+ project_name,
79
+ agent,
80
+ title,
81
+ protocol,
82
+ now,
83
+ now,
84
+ json.dumps(meta or {}),
85
+ ),
86
+ )
87
+ conn.commit()
88
+ cursor = conn.execute("SELECT last_insert_rowid()")
89
+ row_id = cursor.fetchone()[0]
90
+ conn.close()
91
+ return row_id
92
+ except sqlite3.Error as e:
93
+ logger.error("Failed to register session: %s", e)
94
+ return None
95
+
96
+ def update_last_used(self, engine: str, engine_session_id: str) -> bool:
97
+ now = datetime.now(timezone.utc).isoformat()
98
+ try:
99
+ conn = sqlite3.connect(str(self.db_path))
100
+ conn.execute(
101
+ "UPDATE sessions SET last_used = ? WHERE engine = ? AND engine_session_id = ?",
102
+ (now, engine, engine_session_id),
103
+ )
104
+ conn.commit()
105
+ conn.close()
106
+ return True
107
+ except sqlite3.Error:
108
+ return False
109
+
110
+ def increment_prompt_count(self, engine: str, engine_session_id: str) -> bool:
111
+ try:
112
+ conn = sqlite3.connect(str(self.db_path))
113
+ conn.execute(
114
+ "UPDATE sessions SET prompt_count = prompt_count + 1 WHERE engine = ? AND engine_session_id = ?",
115
+ (engine, engine_session_id),
116
+ )
117
+ conn.commit()
118
+ conn.close()
119
+ return True
120
+ except sqlite3.Error:
121
+ return False
122
+
123
+ def get(self, session_id: int) -> Optional[dict]:
124
+ try:
125
+ conn = sqlite3.connect(str(self.db_path))
126
+ conn.row_factory = sqlite3.Row
127
+ cursor = conn.execute("SELECT * FROM sessions WHERE id = ?", (session_id,))
128
+ row = cursor.fetchone()
129
+ conn.close()
130
+ return dict(row) if row else None
131
+ except sqlite3.Error:
132
+ return None
133
+
134
+ def get_by_engine_id(self, engine: str, engine_session_id: str) -> Optional[dict]:
135
+ try:
136
+ conn = sqlite3.connect(str(self.db_path))
137
+ conn.row_factory = sqlite3.Row
138
+ cursor = conn.execute(
139
+ "SELECT * FROM sessions WHERE engine = ? AND engine_session_id = ?",
140
+ (engine, engine_session_id),
141
+ )
142
+ row = cursor.fetchone()
143
+ conn.close()
144
+ return dict(row) if row else None
145
+ except sqlite3.Error:
146
+ return None
147
+
148
+ def get_recent(self, max_results: int = 100) -> list[dict]:
149
+ try:
150
+ conn = sqlite3.connect(str(self.db_path))
151
+ conn.row_factory = sqlite3.Row
152
+ cursor = conn.execute(
153
+ "SELECT * FROM sessions ORDER BY last_used DESC LIMIT ?",
154
+ (max_results,),
155
+ )
156
+ rows = cursor.fetchall()
157
+ conn.close()
158
+ return [dict(r) for r in rows]
159
+ except sqlite3.Error:
160
+ return []
161
+
162
+ def get_by_engine(self, engine: str, max_results: int = 100) -> list[dict]:
163
+ try:
164
+ conn = sqlite3.connect(str(self.db_path))
165
+ conn.row_factory = sqlite3.Row
166
+ cursor = conn.execute(
167
+ "SELECT * FROM sessions WHERE engine = ? ORDER BY last_used DESC LIMIT ?",
168
+ (engine, max_results),
169
+ )
170
+ rows = cursor.fetchall()
171
+ conn.close()
172
+ return [dict(r) for r in rows]
173
+ except sqlite3.Error:
174
+ return []
175
+
176
+ def get_by_project(self, project_name: str, max_results: int = 100) -> list[dict]:
177
+ try:
178
+ conn = sqlite3.connect(str(self.db_path))
179
+ conn.row_factory = sqlite3.Row
180
+ cursor = conn.execute(
181
+ "SELECT * FROM sessions WHERE project_name = ? ORDER BY last_used DESC LIMIT ?",
182
+ (project_name, max_results),
183
+ )
184
+ rows = cursor.fetchall()
185
+ conn.close()
186
+ return [dict(r) for r in rows]
187
+ except sqlite3.Error:
188
+ return []
189
+
190
+ def delete(self, session_id: int) -> bool:
191
+ try:
192
+ conn = sqlite3.connect(str(self.db_path))
193
+ conn.execute("DELETE FROM sessions WHERE id = ?", (session_id,))
194
+ conn.commit()
195
+ conn.close()
196
+ return True
197
+ except sqlite3.Error:
198
+ return False
199
+
200
+ def count(self) -> int:
201
+ try:
202
+ conn = sqlite3.connect(str(self.db_path))
203
+ cursor = conn.execute("SELECT COUNT(*) FROM sessions")
204
+ count = cursor.fetchone()[0]
205
+ conn.close()
206
+ return count
207
+ except sqlite3.Error:
208
+ return 0
209
+
210
+ def list_engines(self) -> list[str]:
211
+ try:
212
+ conn = sqlite3.connect(str(self.db_path))
213
+ cursor = conn.execute("SELECT DISTINCT engine FROM sessions ORDER BY engine")
214
+ rows = cursor.fetchall()
215
+ conn.close()
216
+ return [r[0] for r in rows]
217
+ except sqlite3.Error:
218
+ return []
219
+
220
+ def import_from_tui_db(self, tui_db_path: Path) -> int:
221
+ """Import sessions from a tui.db into the aggregator.
222
+
223
+ Returns the number of sessions imported.
224
+ """
225
+ if not tui_db_path.exists():
226
+ logger.info("No tui.db found at %s", tui_db_path)
227
+ return 0
228
+
229
+ imported = 0
230
+ try:
231
+ tui_conn = sqlite3.connect(str(tui_db_path))
232
+ tui_conn.row_factory = sqlite3.Row
233
+ cursor = tui_conn.execute(
234
+ "SELECT * FROM sessions ORDER BY id"
235
+ )
236
+ rows = cursor.fetchall()
237
+ tui_conn.close()
238
+
239
+ for row in rows:
240
+ sess = dict(row)
241
+ engine = sess.get("agent_identity", "tui").lower()
242
+ engine_session_id = sess.get("agent_session_id", str(sess["id"]))
243
+ if self.get_by_engine_id(engine, engine_session_id) is None:
244
+ self.register(
245
+ engine=engine,
246
+ engine_session_id=engine_session_id,
247
+ agent=sess.get("agent", engine),
248
+ title=sess.get("title", f"Session {sess['id']}"),
249
+ protocol=sess.get("protocol", "acp"),
250
+ meta={"imported_from": str(tui_db_path), "original_id": sess["id"]},
251
+ )
252
+ imported += 1
253
+
254
+ logger.info("Imported %d sessions from %s", imported, tui_db_path)
255
+ except sqlite3.Error as e:
256
+ logger.error("Failed to import from tui.db: %s", e)
257
+
258
+ return imported