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.
- apps/__init__.py +1 -0
- apps/audit/__init__.py +1 -0
- apps/audit/admin.py +6 -0
- apps/audit/apps.py +8 -0
- apps/audit/migrations/0001_initial.py +35 -0
- apps/audit/migrations/__init__.py +0 -0
- apps/audit/models.py +22 -0
- apps/harness/__init__.py +1 -0
- apps/harness/admin.py +6 -0
- apps/harness/apps.py +8 -0
- apps/harness/migrations/0001_initial.py +35 -0
- apps/harness/migrations/__init__.py +0 -0
- apps/harness/models.py +22 -0
- apps/harness/templatetags/__init__.py +1 -0
- apps/integrations/__init__.py +1 -0
- apps/integrations/admin.py +6 -0
- apps/integrations/apps.py +8 -0
- apps/integrations/migrations/0001_initial.py +29 -0
- apps/integrations/migrations/__init__.py +0 -0
- apps/integrations/models.py +16 -0
- apps/integrations/services.py +31 -0
- apps/mcp/__init__.py +1 -0
- apps/mcp/admin.py +20 -0
- apps/mcp/apps.py +8 -0
- apps/mcp/migrations/0001_initial.py +168 -0
- apps/mcp/migrations/__init__.py +0 -0
- apps/mcp/models.py +154 -0
- apps/mcp/services.py +327 -0
- apps/orders/__init__.py +1 -0
- apps/orders/admin.py +6 -0
- apps/orders/apps.py +8 -0
- apps/orders/migrations/0001_initial.py +79 -0
- apps/orders/migrations/__init__.py +0 -0
- apps/orders/models.py +66 -0
- apps/orders/services.py +107 -0
- apps/policy/__init__.py +1 -0
- apps/policy/admin.py +6 -0
- apps/policy/apps.py +8 -0
- apps/policy/migrations/0001_initial.py +75 -0
- apps/policy/migrations/__init__.py +0 -0
- apps/policy/models.py +61 -0
- apps/policy/services.py +110 -0
- apps/portfolio/__init__.py +1 -0
- apps/portfolio/admin.py +6 -0
- apps/portfolio/apps.py +8 -0
- apps/portfolio/migrations/0001_initial.py +67 -0
- apps/portfolio/migrations/__init__.py +0 -0
- apps/portfolio/models.py +53 -0
- apps/research/__init__.py +1 -0
- apps/research/admin.py +1 -0
- apps/research/apps.py +8 -0
- apps/research/migrations/__init__.py +0 -0
- apps/research/models.py +1 -0
- apps/workflows/__init__.py +1 -0
- apps/workflows/admin.py +6 -0
- apps/workflows/apps.py +8 -0
- apps/workflows/migrations/0001_initial.py +51 -0
- apps/workflows/migrations/__init__.py +0 -0
- apps/workflows/models.py +44 -0
- tradingcodex-0.1.0.dist-info/METADATA +337 -0
- tradingcodex-0.1.0.dist-info/RECORD +254 -0
- tradingcodex-0.1.0.dist-info/WHEEL +5 -0
- tradingcodex-0.1.0.dist-info/entry_points.txt +2 -0
- tradingcodex-0.1.0.dist-info/licenses/LICENSE +202 -0
- tradingcodex-0.1.0.dist-info/licenses/NOTICE +24 -0
- tradingcodex-0.1.0.dist-info/top_level.txt +4 -0
- tradingcodex_cli/__init__.py +1 -0
- tradingcodex_cli/__main__.py +124 -0
- tradingcodex_cli/commands/__init__.py +2 -0
- tradingcodex_cli/commands/bootstrap.py +157 -0
- tradingcodex_cli/commands/db.py +36 -0
- tradingcodex_cli/commands/doctor.py +230 -0
- tradingcodex_cli/commands/mcp.py +186 -0
- tradingcodex_cli/commands/orders.py +89 -0
- tradingcodex_cli/commands/policy.py +21 -0
- tradingcodex_cli/commands/profile.py +110 -0
- tradingcodex_cli/commands/research.py +76 -0
- tradingcodex_cli/commands/skills.py +93 -0
- tradingcodex_cli/commands/strategies.py +67 -0
- tradingcodex_cli/commands/subagents.py +106 -0
- tradingcodex_cli/commands/utils.py +134 -0
- tradingcodex_cli/commands/workspaces.py +53 -0
- tradingcodex_cli/generator.py +234 -0
- tradingcodex_cli/mcp_stdio.py +26 -0
- tradingcodex_cli/service_autostart.py +127 -0
- tradingcodex_service/__init__.py +5 -0
- tradingcodex_service/admin.py +1 -0
- tradingcodex_service/api.py +486 -0
- tradingcodex_service/application/__init__.py +2 -0
- tradingcodex_service/application/agents.py +1470 -0
- tradingcodex_service/application/audit.py +71 -0
- tradingcodex_service/application/common.py +88 -0
- tradingcodex_service/application/components.py +299 -0
- tradingcodex_service/application/harness.py +747 -0
- tradingcodex_service/application/markdown_preview.py +179 -0
- tradingcodex_service/application/orders.py +404 -0
- tradingcodex_service/application/policy.py +150 -0
- tradingcodex_service/application/portfolio.py +166 -0
- tradingcodex_service/application/research.py +356 -0
- tradingcodex_service/application/runtime.py +321 -0
- tradingcodex_service/asgi.py +6 -0
- tradingcodex_service/mcp_http.py +59 -0
- tradingcodex_service/mcp_runtime.py +565 -0
- tradingcodex_service/settings.py +91 -0
- tradingcodex_service/templates/web/activity.html +24 -0
- tradingcodex_service/templates/web/agent_skills.html +111 -0
- tradingcodex_service/templates/web/agents.html +121 -0
- tradingcodex_service/templates/web/base.html +150 -0
- tradingcodex_service/templates/web/dashboard.html +109 -0
- tradingcodex_service/templates/web/fragments/role_inspector.html +81 -0
- tradingcodex_service/templates/web/fragments/starter_prompt.html +24 -0
- tradingcodex_service/templates/web/fragments/topology_canvas.html +85 -0
- tradingcodex_service/templates/web/harness.html +87 -0
- tradingcodex_service/templates/web/mcp_router.html +250 -0
- tradingcodex_service/templates/web/orders.html +68 -0
- tradingcodex_service/templates/web/policy.html +81 -0
- tradingcodex_service/templates/web/portfolio.html +52 -0
- tradingcodex_service/templates/web/research.html +73 -0
- tradingcodex_service/templates/web/starter_prompt.html +40 -0
- tradingcodex_service/templates/web/strategies.html +74 -0
- tradingcodex_service/urls.py +42 -0
- tradingcodex_service/version.py +1 -0
- tradingcodex_service/web.py +885 -0
- tradingcodex_service/wsgi.py +6 -0
- workspace_templates/__init__.py +1 -0
- workspace_templates/modules/audit/files/.tradingcodex/audit/README.md +5 -0
- workspace_templates/modules/audit/files/trading/audit/.gitkeep +1 -0
- workspace_templates/modules/audit/module.json +16 -0
- workspace_templates/modules/codex-base/files/.codex/config.toml +272 -0
- workspace_templates/modules/codex-base/files/.codex/hooks/tradingcodex_hook.py +173 -0
- workspace_templates/modules/codex-base/files/.codex/hooks.json +105 -0
- workspace_templates/modules/codex-base/files/.codex/prompts/base_instructions/head-manager.md +129 -0
- workspace_templates/modules/codex-base/files/.codex/rules/tradingcodex.rules +50 -0
- workspace_templates/modules/codex-base/files/.tradingcodex/capabilities.yaml +56 -0
- workspace_templates/modules/codex-base/files/.tradingcodex/cli.py +16 -0
- workspace_templates/modules/codex-base/files/.tradingcodex/config.yaml +53 -0
- workspace_templates/modules/codex-base/files/.tradingcodex/policies/policy-bindings.yaml +16 -0
- workspace_templates/modules/codex-base/files/.tradingcodex/policies/principals.yaml +17 -0
- workspace_templates/modules/codex-base/files/.tradingcodex/policies/roles.yaml +27 -0
- workspace_templates/modules/codex-base/files/AGENTS.md +56 -0
- workspace_templates/modules/codex-base/files/pyproject.toml +13 -0
- workspace_templates/modules/codex-base/files/tcx +35 -0
- workspace_templates/modules/codex-base/module.json +17 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/policies/access-policies.yaml +39 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/policies/restricted-list.yaml +6 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/approval_receipt.schema.json +14 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/audit_event.schema.json +11 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/evidence_pack.schema.json +16 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/execution_result.schema.json +12 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/fundamental_report.schema.json +15 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/news_report.schema.json +15 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/order_intent.schema.json +30 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/portfolio_review.schema.json +15 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/postmortem_report.schema.json +14 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/risk_report.schema.json +15 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/technical_report.schema.json +15 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/thesis.schema.json +15 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/valuation.schema.json +15 -0
- workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/scripts/validate-order-intent.py +15 -0
- workspace_templates/modules/enforcement-guardrails/module.json +19 -0
- workspace_templates/modules/fixed-subagents/files/.codex/agents/execution-operator.toml +71 -0
- workspace_templates/modules/fixed-subagents/files/.codex/agents/fundamental-analyst.toml +62 -0
- workspace_templates/modules/fixed-subagents/files/.codex/agents/instrument-analyst.toml +64 -0
- workspace_templates/modules/fixed-subagents/files/.codex/agents/macro-analyst.toml +64 -0
- workspace_templates/modules/fixed-subagents/files/.codex/agents/news-analyst.toml +63 -0
- workspace_templates/modules/fixed-subagents/files/.codex/agents/portfolio-manager.toml +62 -0
- workspace_templates/modules/fixed-subagents/files/.codex/agents/risk-manager.toml +65 -0
- workspace_templates/modules/fixed-subagents/files/.codex/agents/technical-analyst.toml +63 -0
- workspace_templates/modules/fixed-subagents/files/.codex/agents/valuation-analyst.toml +59 -0
- workspace_templates/modules/fixed-subagents/files/.tradingcodex/mainagent/head-manager.yaml +66 -0
- workspace_templates/modules/fixed-subagents/files/.tradingcodex/mainagent/skill-change-proposals/.gitkeep +1 -0
- workspace_templates/modules/fixed-subagents/files/.tradingcodex/mainagent/subagent-registry.yaml +56 -0
- workspace_templates/modules/fixed-subagents/module.json +23 -0
- workspace_templates/modules/guidance-guardrails/files/.tradingcodex/guidance/guardrails.md +17 -0
- workspace_templates/modules/guidance-guardrails/files/.tradingcodex/guidance/task-quality-checklist.md +37 -0
- workspace_templates/modules/guidance-guardrails/module.json +18 -0
- workspace_templates/modules/information-barriers/files/.tradingcodex/policies/information-barriers.yaml +211 -0
- workspace_templates/modules/information-barriers/files/.tradingcodex/secrets.md +9 -0
- workspace_templates/modules/information-barriers/files/trading/approvals/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/market-data/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/orders/approved/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/orders/draft/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/orders/executed/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/orders/rejected/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/portfolio/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/fundamental/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/instrument/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/macro/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/news/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/policy/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/portfolio/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/postmortem/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/risk/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/technical/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/reports/valuation/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/files/trading/research/.gitkeep +1 -0
- workspace_templates/modules/information-barriers/module.json +19 -0
- workspace_templates/modules/paper-trading/files/.tradingcodex/mcp/adapters/paper-trading.py +4 -0
- workspace_templates/modules/paper-trading/module.json +16 -0
- workspace_templates/modules/postmortem/files/.tradingcodex/workflows/postmortem.yaml +12 -0
- workspace_templates/modules/postmortem/module.json +16 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/investment-workflow-map/SKILL.md +106 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/investment-workflow-map/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/manage-optional-skills/SKILL.md +101 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/manage-optional-skills/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/manage-subagents/SKILL.md +140 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/manage-subagents/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/orchestrate-workflow/SKILL.md +140 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/orchestrate-workflow/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/postmortem/SKILL.md +31 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/postmortem/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/scenario-quality-gates/SKILL.md +138 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/scenario-quality-gates/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/strategy-creator/SKILL.md +109 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/strategy-creator/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/synthesize-decision/SKILL.md +54 -0
- workspace_templates/modules/repo-skills/files/.agents/skills/synthesize-decision/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/execution-operator/execute-paper-order/SKILL.md +35 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/execution-operator/execute-paper-order/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/fundamental-analyst/fundamental-analysis/SKILL.md +46 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/fundamental-analyst/fundamental-analysis/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/instrument-analyst/instrument-analysis/SKILL.md +40 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/instrument-analyst/instrument-analysis/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/macro-analyst/macro-analysis/SKILL.md +40 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/macro-analyst/macro-analysis/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/news-analyst/news-analysis/SKILL.md +43 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/news-analyst/news-analysis/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/create-order-intent/SKILL.md +46 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/create-order-intent/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/portfolio-review/SKILL.md +44 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/portfolio-review/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/approve-order/SKILL.md +38 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/approve-order/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/policy-review/SKILL.md +43 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/policy-review/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/review-risk/SKILL.md +45 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/review-risk/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/collect-evidence/SKILL.md +46 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/collect-evidence/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/external-data-source-gate/SKILL.md +66 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/external-data-source-gate/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/technical-analyst/technical-analysis/SKILL.md +43 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/technical-analyst/technical-analysis/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/valuation-analyst/valuation-review/SKILL.md +47 -0
- workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/valuation-analyst/valuation-review/agents/openai.yaml +6 -0
- workspace_templates/modules/repo-skills/module.json +37 -0
- workspace_templates/modules/stub-execution/files/.tradingcodex/mcp/adapters/stub-execution.py +4 -0
- workspace_templates/modules/stub-execution/module.json +15 -0
- workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/adapters/live-adapter.contract.md +25 -0
- workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/enforcer/README.md +5 -0
- workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/gateway/README.md +8 -0
- workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/server.py +27 -0
- workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/smoke-call.py +18 -0
- workspace_templates/modules/tradingcodex-mcp/module.json +24 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from tradingcodex_service.application.research import (
|
|
6
|
+
create_research_artifact,
|
|
7
|
+
export_research_artifact_md,
|
|
8
|
+
get_research_artifact,
|
|
9
|
+
append_research_artifact_version,
|
|
10
|
+
list_research_artifacts,
|
|
11
|
+
search_research_artifacts,
|
|
12
|
+
)
|
|
13
|
+
from tradingcodex_cli.commands.utils import _option_value, print_json
|
|
14
|
+
|
|
15
|
+
def research(root: Path, argv: list[str]) -> None:
|
|
16
|
+
sub = argv[0] if argv else "list"
|
|
17
|
+
args = argv[1:]
|
|
18
|
+
if sub == "create":
|
|
19
|
+
markdown_file = _option_value(args, "--markdown-file") or _option_value(args, "--file")
|
|
20
|
+
if not markdown_file:
|
|
21
|
+
raise ValueError("Usage: tcx research create --markdown-file <file.md> [--id <id>] [--title <title>] [--source-as-of <date>]")
|
|
22
|
+
payload = {
|
|
23
|
+
"artifact_id": _option_value(args, "--id"),
|
|
24
|
+
"artifact_type": _option_value(args, "--type") or "research_memo",
|
|
25
|
+
"universe": _option_value(args, "--universe") or "public_equity",
|
|
26
|
+
"workflow_type": _option_value(args, "--workflow-type") or "",
|
|
27
|
+
"symbol": _option_value(args, "--symbol") or "",
|
|
28
|
+
"title": _option_value(args, "--title"),
|
|
29
|
+
"markdown_path": markdown_file,
|
|
30
|
+
"source_as_of": _option_value(args, "--source-as-of") or "",
|
|
31
|
+
"readiness_label": _option_value(args, "--readiness") or "",
|
|
32
|
+
"created_by": _option_value(args, "--created-by") or "head-manager",
|
|
33
|
+
"export_path": _option_value(args, "--export-path"),
|
|
34
|
+
}
|
|
35
|
+
print_json(create_research_artifact(root, payload))
|
|
36
|
+
return
|
|
37
|
+
if sub == "append":
|
|
38
|
+
artifact_id = args[0] if args and not args[0].startswith("--") else _option_value(args, "--id")
|
|
39
|
+
markdown_file = _option_value(args, "--markdown-file") or _option_value(args, "--file")
|
|
40
|
+
if not artifact_id or not markdown_file:
|
|
41
|
+
raise ValueError("Usage: tcx research append <artifact-id> --markdown-file <file.md> [--source-as-of <date>]")
|
|
42
|
+
print_json(append_research_artifact_version(root, {
|
|
43
|
+
"artifact_id": artifact_id,
|
|
44
|
+
"markdown_path": markdown_file,
|
|
45
|
+
"source_as_of": _option_value(args, "--source-as-of") or "",
|
|
46
|
+
"created_by": _option_value(args, "--created-by") or "head-manager",
|
|
47
|
+
"export_path": _option_value(args, "--export-path"),
|
|
48
|
+
}))
|
|
49
|
+
return
|
|
50
|
+
if sub == "get":
|
|
51
|
+
artifact_id = args[0] if args and not args[0].startswith("--") else _option_value(args, "--id")
|
|
52
|
+
if not artifact_id:
|
|
53
|
+
raise ValueError("Usage: tcx research get <artifact-id>")
|
|
54
|
+
print_json(get_research_artifact(root, {"artifact_id": artifact_id}))
|
|
55
|
+
return
|
|
56
|
+
if sub == "list":
|
|
57
|
+
print_json(list_research_artifacts(root, {
|
|
58
|
+
"artifact_type": _option_value(args, "--type"),
|
|
59
|
+
"universe": _option_value(args, "--universe"),
|
|
60
|
+
"symbol": _option_value(args, "--symbol"),
|
|
61
|
+
"limit": _option_value(args, "--limit") or 50,
|
|
62
|
+
}))
|
|
63
|
+
return
|
|
64
|
+
if sub == "search":
|
|
65
|
+
query = " ".join(args).strip()
|
|
66
|
+
if not query:
|
|
67
|
+
raise ValueError("Usage: tcx research search <query>")
|
|
68
|
+
print_json(search_research_artifacts(root, {"query": query}))
|
|
69
|
+
return
|
|
70
|
+
if sub == "export":
|
|
71
|
+
artifact_id = args[0] if args and not args[0].startswith("--") else _option_value(args, "--id")
|
|
72
|
+
if not artifact_id:
|
|
73
|
+
raise ValueError("Usage: tcx research export <artifact-id> [--export-path <file.md>]")
|
|
74
|
+
print_json(export_research_artifact_md(root, {"artifact_id": artifact_id, "export_path": _option_value(args, "--export-path")}))
|
|
75
|
+
return
|
|
76
|
+
raise ValueError(f"Unknown research command: {sub}")
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from tradingcodex_cli.commands.utils import _option_value, apply_skill_proposal, list_skills, print_json, write_skill_proposal
|
|
6
|
+
from tradingcodex_service.application.agents import (
|
|
7
|
+
create_or_update_optional_skill,
|
|
8
|
+
delete_optional_skill,
|
|
9
|
+
get_optional_skill_record,
|
|
10
|
+
read_optional_skill_records,
|
|
11
|
+
set_optional_skill_status,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
def skills(root: Path, argv: list[str]) -> None:
|
|
15
|
+
sub = argv[0] if argv else "list"
|
|
16
|
+
args = argv[1:]
|
|
17
|
+
if sub == "optional":
|
|
18
|
+
optional_skills(root, args)
|
|
19
|
+
return
|
|
20
|
+
if sub == "list":
|
|
21
|
+
for skill in list_skills(root, include_internal="--all" in args):
|
|
22
|
+
print(skill)
|
|
23
|
+
return
|
|
24
|
+
if sub == "inspect":
|
|
25
|
+
name = args[0] if args else ""
|
|
26
|
+
from tradingcodex_service.application.agents import build_projection_state
|
|
27
|
+
|
|
28
|
+
skill = build_projection_state(root)["skills"].get(name)
|
|
29
|
+
if not skill or not skill.get("source_file"):
|
|
30
|
+
raise ValueError(f"Unknown skill: {name}")
|
|
31
|
+
print((root / str(skill["source_file"])).read_text(encoding="utf-8"))
|
|
32
|
+
return
|
|
33
|
+
if sub in {"propose-add", "propose-update"}:
|
|
34
|
+
target = _option_value(args, "--to")
|
|
35
|
+
skill = _option_value(args, "--skill")
|
|
36
|
+
if not target or not skill:
|
|
37
|
+
raise ValueError(f"Usage: tcx skills {sub} --to <agent> --skill <skill>")
|
|
38
|
+
print_json(write_skill_proposal(root, sub.replace("propose-", ""), target, skill))
|
|
39
|
+
return
|
|
40
|
+
if sub == "apply-proposal":
|
|
41
|
+
proposal_path = Path(args[0]) if args else None
|
|
42
|
+
if not proposal_path:
|
|
43
|
+
raise ValueError("Usage: tcx skills apply-proposal <proposal.yaml> [--approved-by <principal>]")
|
|
44
|
+
apply_skill_proposal(root, proposal_path if proposal_path.is_absolute() else root / proposal_path, _option_value(args, "--approved-by"))
|
|
45
|
+
return
|
|
46
|
+
raise ValueError(f"Unknown skills command: {sub}")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def optional_skills(root: Path, argv: list[str]) -> None:
|
|
50
|
+
sub = argv[0] if argv else "list"
|
|
51
|
+
args = argv[1:]
|
|
52
|
+
role = _option_value(args, "--role") or _option_value(args, "--to")
|
|
53
|
+
if sub == "list":
|
|
54
|
+
for record in read_optional_skill_records(root, role=role, include_archived="--active" not in args):
|
|
55
|
+
print(f"{record['role']}:{record['name']}")
|
|
56
|
+
return
|
|
57
|
+
if sub == "inspect":
|
|
58
|
+
name = args[0] if args and not args[0].startswith("--") else ""
|
|
59
|
+
if not role or not name:
|
|
60
|
+
raise ValueError("Usage: tcx skills optional inspect <name> --role <agent>")
|
|
61
|
+
record = get_optional_skill_record(root, role, name)
|
|
62
|
+
print((root / str(record["source_file"])).read_text(encoding="utf-8"))
|
|
63
|
+
return
|
|
64
|
+
if sub in {"create", "update"}:
|
|
65
|
+
name = args[0] if args and not args[0].startswith("--") else ""
|
|
66
|
+
if not role or not name:
|
|
67
|
+
raise ValueError(f"Usage: tcx skills optional {sub} <name> --role <agent> [--description <text>] [--body-file <path>]")
|
|
68
|
+
body = _body_arg(root, args)
|
|
69
|
+
status = "active" if "--active" in args else (_option_value(args, "--status") or "draft")
|
|
70
|
+
print_json(create_or_update_optional_skill(root, role, name, description=_option_value(args, "--description") or "", body=body, status=status, actor="local-cli"))
|
|
71
|
+
return
|
|
72
|
+
if sub in {"activate", "archive"}:
|
|
73
|
+
name = args[0] if args else ""
|
|
74
|
+
if not role or not name:
|
|
75
|
+
raise ValueError(f"Usage: tcx skills optional {sub} <name> --role <agent>")
|
|
76
|
+
print_json(set_optional_skill_status(root, role, name, "active" if sub == "activate" else "archived", actor="local-cli"))
|
|
77
|
+
return
|
|
78
|
+
if sub == "delete":
|
|
79
|
+
name = args[0] if args else ""
|
|
80
|
+
if not role or not name:
|
|
81
|
+
raise ValueError("Usage: tcx skills optional delete <name> --role <agent> [--force]")
|
|
82
|
+
print_json(delete_optional_skill(root, role, name, force="--force" in args, actor="local-cli"))
|
|
83
|
+
return
|
|
84
|
+
raise ValueError(f"Unknown optional skills command: {sub}")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _body_arg(root: Path, args: list[str]) -> str:
|
|
88
|
+
body_file = _option_value(args, "--body-file")
|
|
89
|
+
if body_file:
|
|
90
|
+
path = Path(body_file)
|
|
91
|
+
path = path if path.is_absolute() else root / path
|
|
92
|
+
return path.read_text(encoding="utf-8")
|
|
93
|
+
return _option_value(args, "--body") or ""
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from tradingcodex_cli.commands.utils import _option_value, print_json
|
|
6
|
+
from tradingcodex_service.application.agents import (
|
|
7
|
+
create_or_update_strategy_skill,
|
|
8
|
+
delete_strategy_skill,
|
|
9
|
+
get_strategy_skill_record,
|
|
10
|
+
read_strategy_skill_records,
|
|
11
|
+
set_strategy_skill_status,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def strategies(root: Path, argv: list[str]) -> None:
|
|
16
|
+
sub = argv[0] if argv else "list"
|
|
17
|
+
args = argv[1:]
|
|
18
|
+
if sub == "list":
|
|
19
|
+
for record in read_strategy_skill_records(root, active_only="--active" in args):
|
|
20
|
+
print(record["name"])
|
|
21
|
+
return
|
|
22
|
+
if sub == "inspect":
|
|
23
|
+
name = args[0] if args else ""
|
|
24
|
+
if not name:
|
|
25
|
+
raise ValueError("Usage: tcx strategies inspect <name>")
|
|
26
|
+
record = get_strategy_skill_record(root, name)
|
|
27
|
+
print((root / str(record["source_file"])).read_text(encoding="utf-8"))
|
|
28
|
+
return
|
|
29
|
+
if sub in {"create", "update"}:
|
|
30
|
+
name = args[0] if args and not args[0].startswith("--") else ""
|
|
31
|
+
if not name:
|
|
32
|
+
raise ValueError(f"Usage: tcx strategies {sub} <name> [--description <text>] [--body-file <path>]")
|
|
33
|
+
status = "active" if "--active" in args else (_option_value(args, "--status") or "draft")
|
|
34
|
+
print_json(
|
|
35
|
+
create_or_update_strategy_skill(
|
|
36
|
+
root,
|
|
37
|
+
name,
|
|
38
|
+
description=_option_value(args, "--description") or "",
|
|
39
|
+
body=_body_arg(root, args),
|
|
40
|
+
language=_option_value(args, "--language") or "unknown",
|
|
41
|
+
status=status,
|
|
42
|
+
actor="local-cli",
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
return
|
|
46
|
+
if sub in {"activate", "archive"}:
|
|
47
|
+
name = args[0] if args else ""
|
|
48
|
+
if not name:
|
|
49
|
+
raise ValueError(f"Usage: tcx strategies {sub} <name>")
|
|
50
|
+
print_json(set_strategy_skill_status(root, name, "active" if sub == "activate" else "archived", actor="local-cli"))
|
|
51
|
+
return
|
|
52
|
+
if sub == "delete":
|
|
53
|
+
name = args[0] if args else ""
|
|
54
|
+
if not name:
|
|
55
|
+
raise ValueError("Usage: tcx strategies delete <name> [--force]")
|
|
56
|
+
print_json(delete_strategy_skill(root, name, force="--force" in args, actor="local-cli"))
|
|
57
|
+
return
|
|
58
|
+
raise ValueError(f"Unknown strategies command: {sub}")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _body_arg(root: Path, args: list[str]) -> str:
|
|
62
|
+
body_file = _option_value(args, "--body-file")
|
|
63
|
+
if body_file:
|
|
64
|
+
path = Path(body_file)
|
|
65
|
+
path = path if path.is_absolute() else root / path
|
|
66
|
+
return path.read_text(encoding="utf-8")
|
|
67
|
+
return _option_value(args, "--body") or ""
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from tradingcodex_service.application.agents import (
|
|
7
|
+
AGENT_SPECS,
|
|
8
|
+
diff_agent_configuration,
|
|
9
|
+
inspect_agent_configuration,
|
|
10
|
+
project_agent_configuration,
|
|
11
|
+
EXPECTED_SUBAGENTS,
|
|
12
|
+
)
|
|
13
|
+
from tradingcodex_service.application.harness import build_subagent_starter_prompt
|
|
14
|
+
from tradingcodex_cli.commands.utils import (
|
|
15
|
+
_option_value,
|
|
16
|
+
_parse_agent_list,
|
|
17
|
+
list_skills,
|
|
18
|
+
list_subagents,
|
|
19
|
+
print_json,
|
|
20
|
+
read_subagent_state,
|
|
21
|
+
read_thread_policy,
|
|
22
|
+
skills_for_role,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
def subagents(root: Path, argv: list[str]) -> None:
|
|
26
|
+
sub = argv[0] if argv else "list"
|
|
27
|
+
args = argv[1:]
|
|
28
|
+
if sub == "list":
|
|
29
|
+
for agent in list_subagents(root):
|
|
30
|
+
print(f"{agent['name']}\t{agent['description']}")
|
|
31
|
+
return
|
|
32
|
+
if sub == "prompt":
|
|
33
|
+
request = " ".join(args).strip()
|
|
34
|
+
if not request:
|
|
35
|
+
raise ValueError("Usage: tcx subagents prompt <investment request>")
|
|
36
|
+
print(build_subagent_starter_prompt(request))
|
|
37
|
+
return
|
|
38
|
+
if sub == "status":
|
|
39
|
+
agents = list_subagents(root)
|
|
40
|
+
print_json({
|
|
41
|
+
"expected_count": len(EXPECTED_SUBAGENTS),
|
|
42
|
+
"installed_count": len(agents),
|
|
43
|
+
"fixed_roster_ok": len(agents) == len(EXPECTED_SUBAGENTS),
|
|
44
|
+
"skills_installed": len(list_skills(root)),
|
|
45
|
+
"thread_policy": read_thread_policy(root),
|
|
46
|
+
"agents": agents,
|
|
47
|
+
})
|
|
48
|
+
return
|
|
49
|
+
if sub == "state":
|
|
50
|
+
print_json(read_subagent_state(root, _option_value(args, "--run")))
|
|
51
|
+
return
|
|
52
|
+
if sub == "inspect":
|
|
53
|
+
role = args[0] if args else ""
|
|
54
|
+
if not role:
|
|
55
|
+
raise ValueError("Usage: tcx subagents inspect <role>")
|
|
56
|
+
print_json(inspect_agent_configuration(root, role))
|
|
57
|
+
return
|
|
58
|
+
if sub == "diff":
|
|
59
|
+
role = args[0] if args and not args[0].startswith("--") else _option_value(args, "--role")
|
|
60
|
+
if not role:
|
|
61
|
+
raise ValueError("Usage: tcx subagents diff <role>")
|
|
62
|
+
print_json(diff_agent_configuration(root, role))
|
|
63
|
+
return
|
|
64
|
+
if sub == "project":
|
|
65
|
+
role = _option_value(args, "--role")
|
|
66
|
+
proposal = _option_value(args, "--proposal")
|
|
67
|
+
applied_by = _option_value(args, "--applied-by") or "local-cli"
|
|
68
|
+
result = project_agent_configuration(
|
|
69
|
+
root,
|
|
70
|
+
role=role,
|
|
71
|
+
proposal_path=(Path(proposal) if proposal else None),
|
|
72
|
+
applied_by=applied_by,
|
|
73
|
+
)
|
|
74
|
+
print_json({"status": "projected", "projection_hash": result["projection_hash"], "manifest": ".tradingcodex/generated/projection-manifest.json"})
|
|
75
|
+
return
|
|
76
|
+
if sub == "plan":
|
|
77
|
+
installed = list_subagents(root)
|
|
78
|
+
requested = [agent["name"] for agent in installed] if "--all" in args else _parse_agent_list(args)
|
|
79
|
+
if not requested:
|
|
80
|
+
raise ValueError("Usage: tcx subagents plan <agent...>|--all")
|
|
81
|
+
installed_names = {agent["name"] for agent in installed}
|
|
82
|
+
unknown = [agent for agent in requested if agent not in installed_names]
|
|
83
|
+
thread_policy = read_thread_policy(root)
|
|
84
|
+
size = max(1, int(thread_policy["max_parallel_subagents"]))
|
|
85
|
+
batches = [{"batch": i + 1, "agents": requested[i:i + size]} for i in range(0, len(requested), size)]
|
|
86
|
+
print_json({
|
|
87
|
+
"requested_count": len(requested),
|
|
88
|
+
"requested_agents": requested,
|
|
89
|
+
"all_fixed_roster": "--all" in args,
|
|
90
|
+
"unknown_agents": unknown,
|
|
91
|
+
"thread_policy": thread_policy,
|
|
92
|
+
"parallel_spawn_ok": not unknown and len(batches) == 1,
|
|
93
|
+
"required_batches": len(batches),
|
|
94
|
+
"batches": batches,
|
|
95
|
+
"recommendation": "spawn requested subagents in one batch" if len(batches) == 1 else "spawn each batch sequentially and hand off artifacts before starting the next batch",
|
|
96
|
+
})
|
|
97
|
+
if unknown:
|
|
98
|
+
sys.exit(1)
|
|
99
|
+
return
|
|
100
|
+
if sub == "skills":
|
|
101
|
+
role = args[0] if args else ""
|
|
102
|
+
if role not in AGENT_SPECS:
|
|
103
|
+
raise ValueError(f"Unknown subagent or role: {role}")
|
|
104
|
+
print_json({"agent": role, "skills": skills_for_role(root, role)})
|
|
105
|
+
return
|
|
106
|
+
raise ValueError(f"Unknown subagents command: {sub}")
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from tradingcodex_service.application.agents import (
|
|
8
|
+
build_projection_state,
|
|
9
|
+
list_user_visible_skills,
|
|
10
|
+
project_agent_configuration,
|
|
11
|
+
skills_for_role as file_native_skills_for_role,
|
|
12
|
+
write_skill_proposal_file,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
def list_subagents(root: Path) -> list[dict[str, str]]:
|
|
16
|
+
agents = []
|
|
17
|
+
for path in sorted((root / ".codex" / "agents").glob("*.toml")):
|
|
18
|
+
text = path.read_text(encoding="utf-8")
|
|
19
|
+
name = _toml_string(text, "name") or path.stem
|
|
20
|
+
agents.append({"name": name, "runtime_label": name, "description": _toml_string(text, "description") or ""})
|
|
21
|
+
return agents
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def list_skills(root: Path, include_internal: bool = True) -> list[str]:
|
|
25
|
+
if include_internal:
|
|
26
|
+
return sorted(build_projection_state(root)["skills"])
|
|
27
|
+
return list_user_visible_skills(root)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def read_thread_policy(root: Path) -> dict[str, Any]:
|
|
31
|
+
config = _safe_read(root / ".codex" / "config.toml")
|
|
32
|
+
tc_config = _safe_read(root / ".tradingcodex" / "config.yaml")
|
|
33
|
+
max_threads = int(_regex(config, r"^max_threads\s*=\s*(\d+)", "1"))
|
|
34
|
+
max_depth = int(_regex(config, r"^max_depth\s*=\s*(\d+)", "1"))
|
|
35
|
+
reserved = int(_regex(tc_config, r"^\s*reserved_threads:\s*(\d+)", "0"))
|
|
36
|
+
return {"max_threads": max_threads, "max_depth": max_depth, "reserved_threads": reserved, "max_parallel_subagents": max(1, max_threads - reserved), "overflow_strategy": _regex(tc_config, r"^\s*overflow_strategy:\s*([A-Za-z0-9_-]+)", "batch_queue")}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def read_subagent_state(root: Path, run_id: str | None) -> dict[str, Any]:
|
|
40
|
+
state = _read_json(root / ".tradingcodex" / "mainagent" / "subagent-session-state.json", {"updated_at": None, "active": {}, "completed": [], "events": []})
|
|
41
|
+
if not run_id:
|
|
42
|
+
return {"run_filter": None, **state}
|
|
43
|
+
return {
|
|
44
|
+
"run_filter": run_id,
|
|
45
|
+
"updated_at": state.get("updated_at"),
|
|
46
|
+
"active": {role: record for role, record in state.get("active", {}).items() if record.get("run_id") == run_id},
|
|
47
|
+
"completed": [record for record in state.get("completed", []) if record.get("run_id") == run_id],
|
|
48
|
+
"events": [record for record in state.get("events", []) if record.get("run_id") == run_id],
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def skills_for_role(root: Path, role: str) -> list[str]:
|
|
53
|
+
return file_native_skills_for_role(root, role)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def write_skill_proposal(root: Path, type_: str, target: str, skill: str) -> dict[str, Any]:
|
|
57
|
+
return write_skill_proposal_file(root, type_, target, skill)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def apply_skill_proposal(root: Path, proposal_path: Path, approved_by: str | None) -> None:
|
|
61
|
+
result = project_agent_configuration(root, proposal_path=proposal_path, applied_by=approved_by or "local-cli")
|
|
62
|
+
print_json({"status": "applied", "proposal_path": proposal_path.relative_to(root).as_posix(), "projection_hash": result["projection_hash"]})
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def path_check(root: Path, layer: str, name: str, rel: str, codex_native: bool) -> dict[str, Any]:
|
|
66
|
+
ok = (root / rel).exists()
|
|
67
|
+
return {"layer": layer, "name": name, "ok": ok, "codexNative": codex_native, "detail": "found" if ok else "missing"}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def text_check(root: Path, layer: str, name: str, rel: str, pattern: str, codex_native: bool) -> dict[str, Any]:
|
|
71
|
+
ok = pattern in _safe_read(root / rel)
|
|
72
|
+
return {"layer": layer, "name": name, "ok": ok, "codexNative": codex_native, "detail": f"contains {pattern}" if ok else f"missing {pattern}"}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def classify_artifact_path(rel: str) -> str:
|
|
76
|
+
if rel.startswith("trading/research/"):
|
|
77
|
+
return "evidence_pack"
|
|
78
|
+
if "order_intent" in rel:
|
|
79
|
+
return "order_intent"
|
|
80
|
+
if "approval_receipt" in rel:
|
|
81
|
+
return "approval_receipt"
|
|
82
|
+
if rel.startswith("trading/reports/"):
|
|
83
|
+
return "report"
|
|
84
|
+
return "artifact"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _option_value(args: list[str], name: str) -> str | None:
|
|
88
|
+
try:
|
|
89
|
+
return args[args.index(name) + 1]
|
|
90
|
+
except Exception:
|
|
91
|
+
return None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _parse_agent_list(args: list[str]) -> list[str]:
|
|
95
|
+
return [item.strip() for arg in args for item in arg.split(",") if item.strip()]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _toml_string(text: str, key: str) -> str | None:
|
|
99
|
+
for line in text.splitlines():
|
|
100
|
+
if line.startswith(f"{key} = "):
|
|
101
|
+
return line.split('"')[1]
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _safe_read(path: Path) -> str:
|
|
106
|
+
try:
|
|
107
|
+
return path.read_text(encoding="utf-8")
|
|
108
|
+
except Exception:
|
|
109
|
+
return ""
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _read_json(path: Path, default: Any) -> Any:
|
|
113
|
+
try:
|
|
114
|
+
return json.loads(path.read_text(encoding="utf-8"))
|
|
115
|
+
except Exception:
|
|
116
|
+
return default
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _regex(text: str, pattern: str, default: str) -> str:
|
|
120
|
+
import re
|
|
121
|
+
|
|
122
|
+
match = re.search(pattern, text, flags=re.M)
|
|
123
|
+
return match.group(1) if match else default
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _yaml_value(text: str, key: str) -> str | None:
|
|
127
|
+
for line in text.splitlines():
|
|
128
|
+
if line.startswith(f"{key}:"):
|
|
129
|
+
return line.split(":", 1)[1].strip()
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def print_json(value: Any) -> None:
|
|
134
|
+
print(json.dumps(value, indent=2, ensure_ascii=False, default=str))
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from tradingcodex_cli.commands.utils import print_json
|
|
6
|
+
from tradingcodex_service.application.runtime import (
|
|
7
|
+
ensure_runtime_database,
|
|
8
|
+
ensure_workspace_manifest,
|
|
9
|
+
persist_workspace_context_if_available,
|
|
10
|
+
tradingcodex_db_path,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def workspace(root: Path, argv: list[str]) -> None:
|
|
15
|
+
sub = argv[0] if argv else "status"
|
|
16
|
+
if sub == "status":
|
|
17
|
+
manifest = ensure_workspace_manifest(root)
|
|
18
|
+
ensure_runtime_database(root)
|
|
19
|
+
context = persist_workspace_context_if_available(root)
|
|
20
|
+
print_json({
|
|
21
|
+
"status": "ok",
|
|
22
|
+
"workspace_name": manifest["project_name"],
|
|
23
|
+
"workspace_id": manifest["workspace_id"],
|
|
24
|
+
"active_profile": manifest["active_profile"],
|
|
25
|
+
"db_path": str(tradingcodex_db_path()),
|
|
26
|
+
"mcp_scope": manifest["mcp_scope"],
|
|
27
|
+
"execution_mode": manifest["execution_mode"],
|
|
28
|
+
"workspace_context": context,
|
|
29
|
+
"db_canonical": True,
|
|
30
|
+
})
|
|
31
|
+
return
|
|
32
|
+
if sub == "list":
|
|
33
|
+
ensure_runtime_database(root)
|
|
34
|
+
from apps.harness.models import WorkspaceContext
|
|
35
|
+
|
|
36
|
+
print_json({
|
|
37
|
+
"db_path": str(tradingcodex_db_path()),
|
|
38
|
+
"workspaces": [
|
|
39
|
+
{
|
|
40
|
+
"workspace_id": item.workspace_id,
|
|
41
|
+
"project_name": item.project_name,
|
|
42
|
+
"path": item.path,
|
|
43
|
+
"git_remote": item.git_remote,
|
|
44
|
+
"git_branch": item.git_branch,
|
|
45
|
+
"active_profile": item.active_profile,
|
|
46
|
+
"last_seen_at": item.last_seen_at.isoformat(),
|
|
47
|
+
}
|
|
48
|
+
for item in WorkspaceContext.objects.all()[:200]
|
|
49
|
+
],
|
|
50
|
+
"db_canonical": True,
|
|
51
|
+
})
|
|
52
|
+
return
|
|
53
|
+
raise ValueError("Usage: tcx workspace status|list")
|