tradingcodex 0.1.0__py3-none-any.whl

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 (254) hide show
  1. apps/__init__.py +1 -0
  2. apps/audit/__init__.py +1 -0
  3. apps/audit/admin.py +6 -0
  4. apps/audit/apps.py +8 -0
  5. apps/audit/migrations/0001_initial.py +35 -0
  6. apps/audit/migrations/__init__.py +0 -0
  7. apps/audit/models.py +22 -0
  8. apps/harness/__init__.py +1 -0
  9. apps/harness/admin.py +6 -0
  10. apps/harness/apps.py +8 -0
  11. apps/harness/migrations/0001_initial.py +35 -0
  12. apps/harness/migrations/__init__.py +0 -0
  13. apps/harness/models.py +22 -0
  14. apps/harness/templatetags/__init__.py +1 -0
  15. apps/integrations/__init__.py +1 -0
  16. apps/integrations/admin.py +6 -0
  17. apps/integrations/apps.py +8 -0
  18. apps/integrations/migrations/0001_initial.py +29 -0
  19. apps/integrations/migrations/__init__.py +0 -0
  20. apps/integrations/models.py +16 -0
  21. apps/integrations/services.py +31 -0
  22. apps/mcp/__init__.py +1 -0
  23. apps/mcp/admin.py +20 -0
  24. apps/mcp/apps.py +8 -0
  25. apps/mcp/migrations/0001_initial.py +168 -0
  26. apps/mcp/migrations/__init__.py +0 -0
  27. apps/mcp/models.py +154 -0
  28. apps/mcp/services.py +327 -0
  29. apps/orders/__init__.py +1 -0
  30. apps/orders/admin.py +6 -0
  31. apps/orders/apps.py +8 -0
  32. apps/orders/migrations/0001_initial.py +79 -0
  33. apps/orders/migrations/__init__.py +0 -0
  34. apps/orders/models.py +66 -0
  35. apps/orders/services.py +107 -0
  36. apps/policy/__init__.py +1 -0
  37. apps/policy/admin.py +6 -0
  38. apps/policy/apps.py +8 -0
  39. apps/policy/migrations/0001_initial.py +75 -0
  40. apps/policy/migrations/__init__.py +0 -0
  41. apps/policy/models.py +61 -0
  42. apps/policy/services.py +110 -0
  43. apps/portfolio/__init__.py +1 -0
  44. apps/portfolio/admin.py +6 -0
  45. apps/portfolio/apps.py +8 -0
  46. apps/portfolio/migrations/0001_initial.py +67 -0
  47. apps/portfolio/migrations/__init__.py +0 -0
  48. apps/portfolio/models.py +53 -0
  49. apps/research/__init__.py +1 -0
  50. apps/research/admin.py +1 -0
  51. apps/research/apps.py +8 -0
  52. apps/research/migrations/__init__.py +0 -0
  53. apps/research/models.py +1 -0
  54. apps/workflows/__init__.py +1 -0
  55. apps/workflows/admin.py +6 -0
  56. apps/workflows/apps.py +8 -0
  57. apps/workflows/migrations/0001_initial.py +51 -0
  58. apps/workflows/migrations/__init__.py +0 -0
  59. apps/workflows/models.py +44 -0
  60. tradingcodex-0.1.0.dist-info/METADATA +337 -0
  61. tradingcodex-0.1.0.dist-info/RECORD +254 -0
  62. tradingcodex-0.1.0.dist-info/WHEEL +5 -0
  63. tradingcodex-0.1.0.dist-info/entry_points.txt +2 -0
  64. tradingcodex-0.1.0.dist-info/licenses/LICENSE +202 -0
  65. tradingcodex-0.1.0.dist-info/licenses/NOTICE +24 -0
  66. tradingcodex-0.1.0.dist-info/top_level.txt +4 -0
  67. tradingcodex_cli/__init__.py +1 -0
  68. tradingcodex_cli/__main__.py +124 -0
  69. tradingcodex_cli/commands/__init__.py +2 -0
  70. tradingcodex_cli/commands/bootstrap.py +157 -0
  71. tradingcodex_cli/commands/db.py +36 -0
  72. tradingcodex_cli/commands/doctor.py +230 -0
  73. tradingcodex_cli/commands/mcp.py +186 -0
  74. tradingcodex_cli/commands/orders.py +89 -0
  75. tradingcodex_cli/commands/policy.py +21 -0
  76. tradingcodex_cli/commands/profile.py +110 -0
  77. tradingcodex_cli/commands/research.py +76 -0
  78. tradingcodex_cli/commands/skills.py +93 -0
  79. tradingcodex_cli/commands/strategies.py +67 -0
  80. tradingcodex_cli/commands/subagents.py +106 -0
  81. tradingcodex_cli/commands/utils.py +134 -0
  82. tradingcodex_cli/commands/workspaces.py +53 -0
  83. tradingcodex_cli/generator.py +234 -0
  84. tradingcodex_cli/mcp_stdio.py +26 -0
  85. tradingcodex_cli/service_autostart.py +127 -0
  86. tradingcodex_service/__init__.py +5 -0
  87. tradingcodex_service/admin.py +1 -0
  88. tradingcodex_service/api.py +486 -0
  89. tradingcodex_service/application/__init__.py +2 -0
  90. tradingcodex_service/application/agents.py +1470 -0
  91. tradingcodex_service/application/audit.py +71 -0
  92. tradingcodex_service/application/common.py +88 -0
  93. tradingcodex_service/application/components.py +299 -0
  94. tradingcodex_service/application/harness.py +747 -0
  95. tradingcodex_service/application/markdown_preview.py +179 -0
  96. tradingcodex_service/application/orders.py +404 -0
  97. tradingcodex_service/application/policy.py +150 -0
  98. tradingcodex_service/application/portfolio.py +166 -0
  99. tradingcodex_service/application/research.py +356 -0
  100. tradingcodex_service/application/runtime.py +321 -0
  101. tradingcodex_service/asgi.py +6 -0
  102. tradingcodex_service/mcp_http.py +59 -0
  103. tradingcodex_service/mcp_runtime.py +565 -0
  104. tradingcodex_service/settings.py +91 -0
  105. tradingcodex_service/templates/web/activity.html +24 -0
  106. tradingcodex_service/templates/web/agent_skills.html +111 -0
  107. tradingcodex_service/templates/web/agents.html +121 -0
  108. tradingcodex_service/templates/web/base.html +150 -0
  109. tradingcodex_service/templates/web/dashboard.html +109 -0
  110. tradingcodex_service/templates/web/fragments/role_inspector.html +81 -0
  111. tradingcodex_service/templates/web/fragments/starter_prompt.html +24 -0
  112. tradingcodex_service/templates/web/fragments/topology_canvas.html +85 -0
  113. tradingcodex_service/templates/web/harness.html +87 -0
  114. tradingcodex_service/templates/web/mcp_router.html +250 -0
  115. tradingcodex_service/templates/web/orders.html +68 -0
  116. tradingcodex_service/templates/web/policy.html +81 -0
  117. tradingcodex_service/templates/web/portfolio.html +52 -0
  118. tradingcodex_service/templates/web/research.html +73 -0
  119. tradingcodex_service/templates/web/starter_prompt.html +40 -0
  120. tradingcodex_service/templates/web/strategies.html +74 -0
  121. tradingcodex_service/urls.py +42 -0
  122. tradingcodex_service/version.py +1 -0
  123. tradingcodex_service/web.py +885 -0
  124. tradingcodex_service/wsgi.py +6 -0
  125. workspace_templates/__init__.py +1 -0
  126. workspace_templates/modules/audit/files/.tradingcodex/audit/README.md +5 -0
  127. workspace_templates/modules/audit/files/trading/audit/.gitkeep +1 -0
  128. workspace_templates/modules/audit/module.json +16 -0
  129. workspace_templates/modules/codex-base/files/.codex/config.toml +272 -0
  130. workspace_templates/modules/codex-base/files/.codex/hooks/tradingcodex_hook.py +173 -0
  131. workspace_templates/modules/codex-base/files/.codex/hooks.json +105 -0
  132. workspace_templates/modules/codex-base/files/.codex/prompts/base_instructions/head-manager.md +129 -0
  133. workspace_templates/modules/codex-base/files/.codex/rules/tradingcodex.rules +50 -0
  134. workspace_templates/modules/codex-base/files/.tradingcodex/capabilities.yaml +56 -0
  135. workspace_templates/modules/codex-base/files/.tradingcodex/cli.py +16 -0
  136. workspace_templates/modules/codex-base/files/.tradingcodex/config.yaml +53 -0
  137. workspace_templates/modules/codex-base/files/.tradingcodex/policies/policy-bindings.yaml +16 -0
  138. workspace_templates/modules/codex-base/files/.tradingcodex/policies/principals.yaml +17 -0
  139. workspace_templates/modules/codex-base/files/.tradingcodex/policies/roles.yaml +27 -0
  140. workspace_templates/modules/codex-base/files/AGENTS.md +56 -0
  141. workspace_templates/modules/codex-base/files/pyproject.toml +13 -0
  142. workspace_templates/modules/codex-base/files/tcx +35 -0
  143. workspace_templates/modules/codex-base/module.json +17 -0
  144. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/policies/access-policies.yaml +39 -0
  145. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/policies/restricted-list.yaml +6 -0
  146. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/approval_receipt.schema.json +14 -0
  147. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/audit_event.schema.json +11 -0
  148. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/evidence_pack.schema.json +16 -0
  149. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/execution_result.schema.json +12 -0
  150. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/fundamental_report.schema.json +15 -0
  151. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/news_report.schema.json +15 -0
  152. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/order_intent.schema.json +30 -0
  153. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/portfolio_review.schema.json +15 -0
  154. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/postmortem_report.schema.json +14 -0
  155. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/risk_report.schema.json +15 -0
  156. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/technical_report.schema.json +15 -0
  157. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/thesis.schema.json +15 -0
  158. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/valuation.schema.json +15 -0
  159. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/scripts/validate-order-intent.py +15 -0
  160. workspace_templates/modules/enforcement-guardrails/module.json +19 -0
  161. workspace_templates/modules/fixed-subagents/files/.codex/agents/execution-operator.toml +71 -0
  162. workspace_templates/modules/fixed-subagents/files/.codex/agents/fundamental-analyst.toml +62 -0
  163. workspace_templates/modules/fixed-subagents/files/.codex/agents/instrument-analyst.toml +64 -0
  164. workspace_templates/modules/fixed-subagents/files/.codex/agents/macro-analyst.toml +64 -0
  165. workspace_templates/modules/fixed-subagents/files/.codex/agents/news-analyst.toml +63 -0
  166. workspace_templates/modules/fixed-subagents/files/.codex/agents/portfolio-manager.toml +62 -0
  167. workspace_templates/modules/fixed-subagents/files/.codex/agents/risk-manager.toml +65 -0
  168. workspace_templates/modules/fixed-subagents/files/.codex/agents/technical-analyst.toml +63 -0
  169. workspace_templates/modules/fixed-subagents/files/.codex/agents/valuation-analyst.toml +59 -0
  170. workspace_templates/modules/fixed-subagents/files/.tradingcodex/mainagent/head-manager.yaml +66 -0
  171. workspace_templates/modules/fixed-subagents/files/.tradingcodex/mainagent/skill-change-proposals/.gitkeep +1 -0
  172. workspace_templates/modules/fixed-subagents/files/.tradingcodex/mainagent/subagent-registry.yaml +56 -0
  173. workspace_templates/modules/fixed-subagents/module.json +23 -0
  174. workspace_templates/modules/guidance-guardrails/files/.tradingcodex/guidance/guardrails.md +17 -0
  175. workspace_templates/modules/guidance-guardrails/files/.tradingcodex/guidance/task-quality-checklist.md +37 -0
  176. workspace_templates/modules/guidance-guardrails/module.json +18 -0
  177. workspace_templates/modules/information-barriers/files/.tradingcodex/policies/information-barriers.yaml +211 -0
  178. workspace_templates/modules/information-barriers/files/.tradingcodex/secrets.md +9 -0
  179. workspace_templates/modules/information-barriers/files/trading/approvals/.gitkeep +1 -0
  180. workspace_templates/modules/information-barriers/files/trading/market-data/.gitkeep +1 -0
  181. workspace_templates/modules/information-barriers/files/trading/orders/approved/.gitkeep +1 -0
  182. workspace_templates/modules/information-barriers/files/trading/orders/draft/.gitkeep +1 -0
  183. workspace_templates/modules/information-barriers/files/trading/orders/executed/.gitkeep +1 -0
  184. workspace_templates/modules/information-barriers/files/trading/orders/rejected/.gitkeep +1 -0
  185. workspace_templates/modules/information-barriers/files/trading/portfolio/.gitkeep +1 -0
  186. workspace_templates/modules/information-barriers/files/trading/reports/fundamental/.gitkeep +1 -0
  187. workspace_templates/modules/information-barriers/files/trading/reports/instrument/.gitkeep +1 -0
  188. workspace_templates/modules/information-barriers/files/trading/reports/macro/.gitkeep +1 -0
  189. workspace_templates/modules/information-barriers/files/trading/reports/news/.gitkeep +1 -0
  190. workspace_templates/modules/information-barriers/files/trading/reports/policy/.gitkeep +1 -0
  191. workspace_templates/modules/information-barriers/files/trading/reports/portfolio/.gitkeep +1 -0
  192. workspace_templates/modules/information-barriers/files/trading/reports/postmortem/.gitkeep +1 -0
  193. workspace_templates/modules/information-barriers/files/trading/reports/risk/.gitkeep +1 -0
  194. workspace_templates/modules/information-barriers/files/trading/reports/technical/.gitkeep +1 -0
  195. workspace_templates/modules/information-barriers/files/trading/reports/valuation/.gitkeep +1 -0
  196. workspace_templates/modules/information-barriers/files/trading/research/.gitkeep +1 -0
  197. workspace_templates/modules/information-barriers/module.json +19 -0
  198. workspace_templates/modules/paper-trading/files/.tradingcodex/mcp/adapters/paper-trading.py +4 -0
  199. workspace_templates/modules/paper-trading/module.json +16 -0
  200. workspace_templates/modules/postmortem/files/.tradingcodex/workflows/postmortem.yaml +12 -0
  201. workspace_templates/modules/postmortem/module.json +16 -0
  202. workspace_templates/modules/repo-skills/files/.agents/skills/investment-workflow-map/SKILL.md +106 -0
  203. workspace_templates/modules/repo-skills/files/.agents/skills/investment-workflow-map/agents/openai.yaml +6 -0
  204. workspace_templates/modules/repo-skills/files/.agents/skills/manage-optional-skills/SKILL.md +101 -0
  205. workspace_templates/modules/repo-skills/files/.agents/skills/manage-optional-skills/agents/openai.yaml +6 -0
  206. workspace_templates/modules/repo-skills/files/.agents/skills/manage-subagents/SKILL.md +140 -0
  207. workspace_templates/modules/repo-skills/files/.agents/skills/manage-subagents/agents/openai.yaml +6 -0
  208. workspace_templates/modules/repo-skills/files/.agents/skills/orchestrate-workflow/SKILL.md +140 -0
  209. workspace_templates/modules/repo-skills/files/.agents/skills/orchestrate-workflow/agents/openai.yaml +6 -0
  210. workspace_templates/modules/repo-skills/files/.agents/skills/postmortem/SKILL.md +31 -0
  211. workspace_templates/modules/repo-skills/files/.agents/skills/postmortem/agents/openai.yaml +6 -0
  212. workspace_templates/modules/repo-skills/files/.agents/skills/scenario-quality-gates/SKILL.md +138 -0
  213. workspace_templates/modules/repo-skills/files/.agents/skills/scenario-quality-gates/agents/openai.yaml +6 -0
  214. workspace_templates/modules/repo-skills/files/.agents/skills/strategy-creator/SKILL.md +109 -0
  215. workspace_templates/modules/repo-skills/files/.agents/skills/strategy-creator/agents/openai.yaml +6 -0
  216. workspace_templates/modules/repo-skills/files/.agents/skills/synthesize-decision/SKILL.md +54 -0
  217. workspace_templates/modules/repo-skills/files/.agents/skills/synthesize-decision/agents/openai.yaml +6 -0
  218. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/execution-operator/execute-paper-order/SKILL.md +35 -0
  219. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/execution-operator/execute-paper-order/agents/openai.yaml +6 -0
  220. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/fundamental-analyst/fundamental-analysis/SKILL.md +46 -0
  221. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/fundamental-analyst/fundamental-analysis/agents/openai.yaml +6 -0
  222. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/instrument-analyst/instrument-analysis/SKILL.md +40 -0
  223. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/instrument-analyst/instrument-analysis/agents/openai.yaml +6 -0
  224. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/macro-analyst/macro-analysis/SKILL.md +40 -0
  225. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/macro-analyst/macro-analysis/agents/openai.yaml +6 -0
  226. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/news-analyst/news-analysis/SKILL.md +43 -0
  227. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/news-analyst/news-analysis/agents/openai.yaml +6 -0
  228. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/create-order-intent/SKILL.md +46 -0
  229. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/create-order-intent/agents/openai.yaml +6 -0
  230. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/portfolio-review/SKILL.md +44 -0
  231. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/portfolio-review/agents/openai.yaml +6 -0
  232. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/approve-order/SKILL.md +38 -0
  233. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/approve-order/agents/openai.yaml +6 -0
  234. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/policy-review/SKILL.md +43 -0
  235. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/policy-review/agents/openai.yaml +6 -0
  236. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/review-risk/SKILL.md +45 -0
  237. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/review-risk/agents/openai.yaml +6 -0
  238. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/collect-evidence/SKILL.md +46 -0
  239. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/collect-evidence/agents/openai.yaml +6 -0
  240. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/external-data-source-gate/SKILL.md +66 -0
  241. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/external-data-source-gate/agents/openai.yaml +6 -0
  242. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/technical-analyst/technical-analysis/SKILL.md +43 -0
  243. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/technical-analyst/technical-analysis/agents/openai.yaml +6 -0
  244. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/valuation-analyst/valuation-review/SKILL.md +47 -0
  245. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/valuation-analyst/valuation-review/agents/openai.yaml +6 -0
  246. workspace_templates/modules/repo-skills/module.json +37 -0
  247. workspace_templates/modules/stub-execution/files/.tradingcodex/mcp/adapters/stub-execution.py +4 -0
  248. workspace_templates/modules/stub-execution/module.json +15 -0
  249. workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/adapters/live-adapter.contract.md +25 -0
  250. workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/enforcer/README.md +5 -0
  251. workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/gateway/README.md +8 -0
  252. workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/server.py +27 -0
  253. workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/smoke-call.py +18 -0
  254. workspace_templates/modules/tradingcodex-mcp/module.json +24 -0
@@ -0,0 +1,234 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ import shutil
6
+ import sys
7
+ import uuid
8
+ from dataclasses import dataclass
9
+ from datetime import datetime, timezone
10
+ from pathlib import Path
11
+ from typing import Any
12
+
13
+ from tradingcodex_service.version import TRADINGCODEX_VERSION
14
+ from tradingcodex_service.application.agents import project_agent_configuration
15
+ from tradingcodex_service.application.components import list_harness_components
16
+ from tradingcodex_service.application.runtime import ensure_workspace_manifest, read_workspace_manifest
17
+
18
+ DEFAULT_MODULE_IDS = [
19
+ "codex-base",
20
+ "fixed-subagents",
21
+ "repo-skills",
22
+ "guidance-guardrails",
23
+ "enforcement-guardrails",
24
+ "information-barriers",
25
+ "audit",
26
+ "tradingcodex-mcp",
27
+ "stub-execution",
28
+ "paper-trading",
29
+ "postmortem",
30
+ ]
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class Module:
35
+ id: str
36
+ description: str
37
+ dir: Path
38
+ manifest: dict[str, Any]
39
+
40
+
41
+ def repo_root() -> Path:
42
+ return Path(__file__).resolve().parent.parent
43
+
44
+
45
+ def templates_dir() -> Path:
46
+ return repo_root() / "workspace_templates"
47
+
48
+
49
+ def bootstrap_workspace(project_dir: Path | str, force: bool = False, dry_run: bool = False, module_ids: list[str] | None = None) -> dict[str, Any]:
50
+ target = Path(project_dir).resolve()
51
+ registry = load_module_registry(templates_dir())
52
+ modules = resolve_module_graph(registry, module_ids or DEFAULT_MODULE_IDS)
53
+ subagents = collect_template_subagent_names(modules)
54
+ existing_manifest = read_workspace_manifest(target)
55
+ workspace_id = str(existing_manifest.get("workspace_id") or f"tcxw_{uuid.uuid4().hex}")
56
+ context = {
57
+ "PROJECT_NAME": sanitize_project_name(target.name or "tradingcodex-workspace"),
58
+ "PROJECT_DIR": str(target),
59
+ "WORKSPACE_ID": workspace_id,
60
+ "SOURCE_ROOT": str(repo_root()),
61
+ "PYTHON_EXECUTABLE": sys.executable,
62
+ "GENERATED_AT": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
63
+ "TRADINGCODEX_VERSION": TRADINGCODEX_VERSION,
64
+ "TRADINGCODEX_MCP_PACKAGE_SPEC": os.environ.get("TRADINGCODEX_MCP_PACKAGE_SPEC", "tradingcodex"),
65
+ "SUBAGENT_COUNT": str(len(subagents)),
66
+ }
67
+ result = {
68
+ "targetDir": str(target),
69
+ "workspaceId": workspace_id,
70
+ "modules": [module.id for module in modules],
71
+ "capabilities": collect_capabilities(modules),
72
+ }
73
+ if dry_run:
74
+ return result
75
+ ensure_target_dir(target, force)
76
+ for module in modules:
77
+ files_dir = module.dir / "files"
78
+ if files_dir.exists():
79
+ copy_template_tree(files_dir, target, context)
80
+ ensure_workspace_manifest(target, project_name=context["PROJECT_NAME"], generated_at=context["GENERATED_AT"])
81
+ write_generated_indexes(target, modules, context)
82
+ project_agent_configuration(target, applied_by="bootstrap", generated_at=context["GENERATED_AT"])
83
+ return result
84
+
85
+
86
+ def load_module_registry(base_templates_dir: Path) -> dict[str, Module]:
87
+ modules_dir = base_templates_dir / "modules"
88
+ registry: dict[str, Module] = {}
89
+ for module_dir in sorted(path for path in modules_dir.iterdir() if path.is_dir()):
90
+ manifest_path = module_dir / "module.json"
91
+ if not manifest_path.exists():
92
+ continue
93
+ manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
94
+ module_id = manifest["id"]
95
+ if module_id != module_dir.name:
96
+ raise ValueError(f'Module id "{module_id}" does not match directory "{module_dir.name}"')
97
+ registry[module_id] = Module(module_id, manifest.get("description", ""), module_dir, manifest)
98
+ return registry
99
+
100
+
101
+ def resolve_module_graph(registry: dict[str, Module], requested_ids: list[str]) -> list[Module]:
102
+ resolved: list[Module] = []
103
+ seen: set[str] = set()
104
+ visiting: set[str] = set()
105
+
106
+ def visit(module_id: str, parent_id: str | None = None) -> None:
107
+ if module_id in seen:
108
+ return
109
+ if module_id in visiting:
110
+ raise ValueError(f'Circular module dependency detected at "{module_id}"')
111
+ if module_id not in registry:
112
+ suffix = f' required by "{parent_id}"' if parent_id else ""
113
+ raise ValueError(f'Unknown module "{module_id}"{suffix}')
114
+ visiting.add(module_id)
115
+ module = registry[module_id]
116
+ for dependency in module.manifest.get("requires", {}).get("modules", []):
117
+ visit(dependency, module_id)
118
+ visiting.remove(module_id)
119
+ seen.add(module_id)
120
+ resolved.append(module)
121
+
122
+ for module_id in requested_ids:
123
+ visit(module_id)
124
+ assert_no_conflicts(resolved)
125
+ return resolved
126
+
127
+
128
+ def collect_capabilities(modules: list[Module]) -> list[str]:
129
+ capabilities: set[str] = set()
130
+ for module in modules:
131
+ capabilities.update(module.manifest.get("provides", {}).get("capabilities", []))
132
+ return sorted(capabilities)
133
+
134
+
135
+ def ensure_target_dir(target: Path, force: bool) -> None:
136
+ target.mkdir(parents=True, exist_ok=True)
137
+ if not force and not target_has_only_bootstrap_files(target):
138
+ raise ValueError(f"Target directory already has files: {target}. Use an empty directory, a git-initialized empty directory, or pass --overwrite to update matching generated workspace paths.")
139
+
140
+
141
+ def target_has_only_bootstrap_files(target: Path) -> bool:
142
+ allowed_names = {".git", ".gitignore", ".gitattributes"}
143
+ return all(child.name in allowed_names for child in target.iterdir())
144
+
145
+
146
+ def copy_template_tree(source: Path, target: Path, context: dict[str, str]) -> None:
147
+ for item in source.iterdir():
148
+ if item.name in {"__pycache__", ".DS_Store"} or item.suffix in {".pyc", ".pyo"}:
149
+ continue
150
+ destination = target / item.name
151
+ if item.is_dir():
152
+ destination.mkdir(parents=True, exist_ok=True)
153
+ copy_template_tree(item, destination, context)
154
+ continue
155
+ if not item.is_file():
156
+ continue
157
+ destination.parent.mkdir(parents=True, exist_ok=True)
158
+ text = item.read_text(encoding="utf-8")
159
+ rendered = render_template(text, context)
160
+ destination.write_text(rendered, encoding="utf-8")
161
+ if rendered.startswith("#!"):
162
+ destination.chmod(0o755)
163
+
164
+
165
+ def write_generated_indexes(target: Path, modules: list[Module], context: dict[str, str]) -> None:
166
+ generated_dir = target / ".tradingcodex" / "generated"
167
+ generated_dir.mkdir(parents=True, exist_ok=True)
168
+ lock = {
169
+ "generated_at": context["GENERATED_AT"],
170
+ "tradingcodex_version": context["TRADINGCODEX_VERSION"],
171
+ "modules": [
172
+ {
173
+ "id": module.id,
174
+ "description": module.description,
175
+ "capabilities": module.manifest.get("provides", {}).get("capabilities", []),
176
+ }
177
+ for module in modules
178
+ ],
179
+ }
180
+ capability_index = {
181
+ "generated_at": context["GENERATED_AT"],
182
+ "capabilities": collect_capabilities(modules),
183
+ }
184
+ component_index = {
185
+ "generated_at": context["GENERATED_AT"],
186
+ "source": "tradingcodex_service.application.components",
187
+ "components": list_harness_components(),
188
+ }
189
+ (generated_dir / "module-lock.json").write_text(json.dumps(lock, indent=2) + "\n", encoding="utf-8")
190
+ (generated_dir / "capability-index.json").write_text(json.dumps(capability_index, indent=2) + "\n", encoding="utf-8")
191
+ (generated_dir / "component-index.json").write_text(json.dumps(component_index, indent=2) + "\n", encoding="utf-8")
192
+
193
+
194
+ def collect_template_subagent_names(modules: list[Module]) -> list[str]:
195
+ names: list[str] = []
196
+ for module in modules:
197
+ agents_dir = module.dir / "files" / ".codex" / "agents"
198
+ if not agents_dir.exists():
199
+ continue
200
+ for file_path in sorted(agents_dir.glob("*.toml")):
201
+ text = file_path.read_text(encoding="utf-8")
202
+ name = file_path.stem
203
+ for line in text.splitlines():
204
+ if line.startswith("name = "):
205
+ name = line.split('"')[1]
206
+ break
207
+ names.append(name)
208
+ return sorted(names)
209
+
210
+
211
+ def assert_no_conflicts(modules: list[Module]) -> None:
212
+ ids = {module.id for module in modules}
213
+ for module in modules:
214
+ for conflict in module.manifest.get("conflicts", []):
215
+ if conflict in ids:
216
+ raise ValueError(f'Module "{module.id}" conflicts with "{conflict}"')
217
+
218
+
219
+ def render_template(source: str, context: dict[str, str]) -> str:
220
+ rendered = source
221
+ for key, value in context.items():
222
+ rendered = rendered.replace(f"{{{{{key}}}}}", value)
223
+ return rendered
224
+
225
+
226
+ def sanitize_project_name(name: str) -> str:
227
+ cleaned = "".join(ch.lower() if ch.isalnum() or ch in "._-" else "-" for ch in name)
228
+ return cleaned.strip("-") or "tradingcodex-workspace"
229
+
230
+
231
+ def reset_tmp_generated_workspaces(repo: Path | None = None) -> None:
232
+ tmp = (repo or repo_root()) / "tmp"
233
+ for child in ["smoke", "dry-run-smoke", "non-empty-smoke", "scenario-quality", "external-data", "quality-scenarios-20"]:
234
+ shutil.rmtree(tmp / child, ignore_errors=True)
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ from tradingcodex_service.mcp_runtime import handle_mcp_rpc
8
+
9
+
10
+ def run_stdio(workspace_root: Path) -> None:
11
+ for line in sys.stdin:
12
+ if not line.strip():
13
+ continue
14
+ try:
15
+ message = json.loads(line)
16
+ except Exception as exc:
17
+ _write({"jsonrpc": "2.0", "id": None, "error": {"code": -32700, "message": str(exc)}})
18
+ continue
19
+ response = handle_mcp_rpc(workspace_root, message)
20
+ if response is not None:
21
+ _write(response)
22
+
23
+
24
+ def _write(message: dict) -> None:
25
+ sys.stdout.write(json.dumps(message, ensure_ascii=False) + "\n")
26
+ sys.stdout.flush()
@@ -0,0 +1,127 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import json
5
+ import socket
6
+ import subprocess
7
+ import sys
8
+ import time
9
+ import urllib.request
10
+ from pathlib import Path
11
+
12
+ from tradingcodex_service.application.runtime import tradingcodex_db_path, tradingcodex_file_lock, tradingcodex_state_dir
13
+ from tradingcodex_service.version import TRADINGCODEX_VERSION
14
+
15
+
16
+ DEFAULT_SERVICE_HOST = "127.0.0.1"
17
+ DEFAULT_SERVICE_PORT = 48267
18
+ DEFAULT_SERVICE_ADDR = f"{DEFAULT_SERVICE_HOST}:{DEFAULT_SERVICE_PORT}"
19
+
20
+
21
+ def maybe_autostart_service(workspace_root: Path, source_root: Path | None = None) -> bool:
22
+ if os.environ.get("TRADINGCODEX_MCP_AUTOSTART_SERVICE", "").lower() not in {"1", "true", "yes", "on"}:
23
+ return False
24
+ addr = os.environ.get("TRADINGCODEX_SERVICE_ADDR", DEFAULT_SERVICE_ADDR)
25
+ timeout = float(os.environ.get("TRADINGCODEX_MCP_AUTOSTART_TIMEOUT", "8"))
26
+ return ensure_service_up(workspace_root, addr=addr, source_root=source_root, timeout=timeout)
27
+
28
+
29
+ def ensure_service_up(workspace_root: Path, addr: str = DEFAULT_SERVICE_ADDR, source_root: Path | None = None, timeout: float = 8.0) -> bool:
30
+ host, port = _parse_addr(addr)
31
+ if _tcp_open(host, port):
32
+ _assert_compatible_service(host, port)
33
+ return False
34
+ with tradingcodex_file_lock(f"service-{host}-{port}"):
35
+ if _tcp_open(host, port):
36
+ _assert_compatible_service(host, port)
37
+ return False
38
+ _start_service(workspace_root, addr, source_root)
39
+ deadline = time.monotonic() + timeout
40
+ while time.monotonic() < deadline:
41
+ if _tcp_open(host, port) and _compatible_service(host, port):
42
+ return True
43
+ time.sleep(0.2)
44
+ _assert_compatible_service(host, port)
45
+ return False
46
+
47
+
48
+ def compatible_service_running(addr: str = DEFAULT_SERVICE_ADDR) -> bool:
49
+ host, port = _parse_addr(addr)
50
+ if not _tcp_open(host, port):
51
+ return False
52
+ _assert_compatible_service(host, port)
53
+ return True
54
+
55
+
56
+ def service_http_url(addr: str = DEFAULT_SERVICE_ADDR) -> str:
57
+ host, port = _parse_addr(addr)
58
+ return f"http://{host}:{port}/"
59
+
60
+
61
+ def _start_service(workspace_root: Path, addr: str, source_root: Path | None) -> None:
62
+ run_dir = tradingcodex_state_dir() / "run"
63
+ run_dir.mkdir(parents=True, exist_ok=True)
64
+ log_path = run_dir / "service.log"
65
+ env = os.environ.copy()
66
+ env.setdefault("DJANGO_SETTINGS_MODULE", "tradingcodex_service.settings")
67
+ env.setdefault("TRADINGCODEX_WORKSPACE_ROOT", str(workspace_root.resolve()))
68
+ if source_root:
69
+ current = env.get("PYTHONPATH")
70
+ env["PYTHONPATH"] = str(source_root.resolve()) + (f":{current}" if current else "")
71
+ with log_path.open("ab") as log_handle:
72
+ subprocess.Popen(
73
+ [sys.executable, "-m", "tradingcodex_cli", "service", "runserver", addr, "--noreload"],
74
+ cwd=workspace_root,
75
+ env=env,
76
+ stdin=subprocess.DEVNULL,
77
+ stdout=log_handle,
78
+ stderr=subprocess.STDOUT,
79
+ close_fds=True,
80
+ start_new_session=True,
81
+ )
82
+
83
+
84
+ def _parse_addr(addr: str) -> tuple[str, int]:
85
+ if ":" in addr:
86
+ host, port_text = addr.rsplit(":", 1)
87
+ return host or "127.0.0.1", int(port_text)
88
+ return "127.0.0.1", int(addr)
89
+
90
+
91
+ def _tcp_open(host: str, port: int) -> bool:
92
+ try:
93
+ with socket.create_connection((host, port), timeout=0.25):
94
+ return True
95
+ except OSError:
96
+ return False
97
+
98
+
99
+ def _compatible_service(host: str, port: int) -> bool:
100
+ try:
101
+ _assert_compatible_service(host, port)
102
+ return True
103
+ except Exception:
104
+ return False
105
+
106
+
107
+ def _assert_compatible_service(host: str, port: int) -> None:
108
+ health = _service_health(host, port)
109
+ if not health or health.get("service") != "tradingcodex":
110
+ raise RuntimeError(f"{host}:{port} is already in use by a non-TradingCodex service")
111
+ if health.get("version") != TRADINGCODEX_VERSION:
112
+ raise RuntimeError(f"TradingCodex service version mismatch: service={health.get('version')} package={TRADINGCODEX_VERSION}")
113
+ service_db = str(health.get("db_path") or "")
114
+ current_db = str(tradingcodex_db_path())
115
+ if service_db and service_db != current_db:
116
+ raise RuntimeError(f"TradingCodex service DB mismatch: service={service_db} package={current_db}")
117
+
118
+
119
+ def _service_health(host: str, port: int) -> dict:
120
+ url = f"http://{host}:{port}/api/health"
121
+ try:
122
+ with urllib.request.urlopen(url, timeout=0.5) as response:
123
+ payload = response.read().decode("utf-8")
124
+ data = json.loads(payload)
125
+ return data if isinstance(data, dict) else {}
126
+ except Exception:
127
+ return {}
@@ -0,0 +1,5 @@
1
+ from tradingcodex_service.version import TRADINGCODEX_VERSION
2
+
3
+ __all__ = ["__version__"]
4
+
5
+ __version__ = TRADINGCODEX_VERSION
@@ -0,0 +1 @@
1
+ """Use Django's default admin site without TradingCodex UI overrides."""