Skip to content

Commit 3a7f2e9

Browse files
committed
Ship uvx installer bootstrap for alpha release
1 parent 13b0ebd commit 3a7f2e9

27 files changed

Lines changed: 265 additions & 96 deletions

File tree

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ include LICENSE
22
include NOTICE
33
include CONTRIBUTING.md
44
include TRADEMARKS.md
5+
include install.sh
56
recursive-include assets *
67
recursive-include docs *
78
recursive-include workspace_templates *

README.md

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,43 +32,37 @@ the durable service layer, and TradingCodex MCP is the execution boundary.
3232
## Quick Start
3333

3434
```bash
35-
uv python install 3.14
36-
uv tool install --python 3.14 tradingcodex
37-
uv tool update-shell
38-
export PATH="$HOME/.local/bin:$PATH"
39-
mkdir -p ~/tradingcodex-workspaces/apple-research
40-
cd ~/tradingcodex-workspaces/apple-research
41-
tcx init .
42-
./tcx doctor
35+
curl -fsSL https://raw.githubusercontent.com/monarchjuno/tradingcodex/main/install.sh | sh -s -- ~/tradingcodex-workspaces/apple-research
4336
```
4437

45-
Install the CLI as a user-level tool before creating a TradingCodex workspace.
46-
Do not clone this repository into the generated workspace. `tcx init .` expects
47-
the target directory to be empty, and the generated `./tcx` wrapper will remember
48-
the Python interpreter that created it.
38+
The command above bootstraps a clean TradingCodex workspace, runs `./tcx doctor`,
39+
and leaves the workspace ready to open in Codex. Do not clone this repository
40+
into the generated workspace. `tcx init` expects the target directory to be
41+
empty.
4942

50-
To install the current GitHub `main` source without cloning it into the
51-
workspace, replace the `uv tool install` line with:
43+
To bootstrap from the current GitHub `main` source without cloning it into the
44+
workspace:
5245

5346
```bash
54-
uv tool install --python 3.14 --force \
55-
"tradingcodex @ git+https://github.com/monarchjuno/tradingcodex.git@main"
47+
curl -fsSL https://raw.githubusercontent.com/monarchjuno/tradingcodex/main/install.sh | sh -s -- --from-github ~/tradingcodex-workspaces/apple-research
5648
```
5749

58-
For a one-shot bootstrap without installing `tcx` first, use `uvx`:
50+
The installer uses `uvx` for the bootstrap step. The direct equivalent is:
5951

6052
```bash
61-
uvx --python 3.14 --from tradingcodex tcx init .
53+
UV_NO_CACHE=1 uvx --isolated --refresh --python 3.14 --from tradingcodex tcx init ~/tradingcodex-workspaces/apple-research && cd ~/tradingcodex-workspaces/apple-research && ./tcx doctor
6254
```
6355

64-
`uv tool install` is preferred for normal use because it keeps the `tcx` command
65-
available after bootstrap.
56+
For repeated workspace creation, installing `tcx` as a user-level tool is still
57+
available:
6658

67-
For persistent Codex workspaces, do not point TradingCodex MCP at `uvx`.
68-
Generated `.codex/config.toml` records the Python interpreter that ran
69-
`tcx init`, and that MCP server also autostarts the experimental local Django
70-
dashboard service. A stable `uv tool install` environment avoids resolving the
71-
package again every time Codex starts MCP.
59+
```bash
60+
uv python install 3.14 && uv tool install --python 3.14 tradingcodex && uv tool update-shell
61+
```
62+
63+
Generated `.codex/config.toml` starts TradingCodex MCP with `uvx`, using the
64+
same package spec recorded at bootstrap time. That MCP startup also autostarts
65+
the experimental local Django dashboard service.
7266

7367
Start an orchestrated Codex workflow from the generated workspace:
7468

docs/core-concepts-and-rules.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,12 @@ Fail closed: if subagent dispatch is unavailable, the workflow waits. `head-mana
143143
trusted.
144144
- The generated TradingCodex MCP config sets
145145
`TRADINGCODEX_MCP_AUTOSTART_SERVICE=1`, so trusted Codex sessions start the
146-
MCP stdio bridge and the local Django dashboard service together. The
147-
autostart path must be idempotent, must not write non-MCP output to stdout,
148-
and must not be required for direct `./tcx mcp stdio` smoke checks.
149-
- Generated fixed-role subagent TOML files pin `model = "gpt-5.5"` and `reasoning_effort = "high"`; spawn by fixed role label so the role file supplies these runtime defaults.
146+
MCP stdio bridge and the local Django dashboard service together. The MCP
147+
command uses `uvx --from <package-spec> tcx mcp stdio`, and bootstrap records
148+
the package spec so GitHub-source installs do not silently fall back to PyPI.
149+
The autostart path must be idempotent, must not write non-MCP output to
150+
stdout, and must not be required for direct `./tcx mcp stdio` smoke checks.
151+
- Generated fixed-role subagent TOML files pin `model = "gpt-5.5"` and `model_reasoning_effort = "high"`; spawn by fixed role label so the role file supplies these runtime defaults.
150152
- The root `head-manager` MCP allowlist intentionally excludes
151153
`submit_approved_order`, `cancel_approved_order`, and approval creation.
152154
`risk-manager` owns approval receipt creation; `execution-operator` owns

docs/deployment.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ python3.14 -m venv /tmp/tcx-testpypi
133133
/tmp/tcx-testpypi/bin/pip install \
134134
--index-url https://test.pypi.org/simple/ \
135135
--extra-index-url https://pypi.org/simple/ \
136-
tradingcodex==0.1.0a3
136+
tradingcodex==0.1.0a4
137137
rm -rf /tmp/tcx-testpypi-smoke
138138
mkdir -p /tmp/tcx-testpypi-smoke
139139
cd /tmp/tcx-testpypi-smoke
@@ -162,19 +162,28 @@ After the PyPI workflow completes:
162162

163163
```bash
164164
python3.14 -m venv /tmp/tcx-pypi
165-
/tmp/tcx-pypi/bin/pip install tradingcodex==0.1.0a3
165+
/tmp/tcx-pypi/bin/pip install tradingcodex==0.1.0a4
166166
rm -rf /tmp/tcx-pypi-smoke
167167
mkdir -p /tmp/tcx-pypi-smoke
168168
cd /tmp/tcx-pypi-smoke
169169
/tmp/tcx-pypi/bin/tcx init .
170170
./tcx doctor
171171
```
172172

173+
Also verify the user-facing installer path:
174+
175+
```bash
176+
rm -rf /tmp/tcx-install-sh-smoke
177+
sh ./install.sh --no-doctor /tmp/tcx-install-sh-smoke
178+
cd /tmp/tcx-install-sh-smoke
179+
./tcx doctor
180+
```
181+
173182
## Versioning
174183

175184
Use PEP 440 versions:
176185

177-
- `0.1.0a1`, `0.1.0a2`, `0.1.0a3` for alpha releases
186+
- `0.1.0a1`, `0.1.0a2`, `0.1.0a3`, `0.1.0a4` for alpha releases
178187
- `0.1.0b1` for beta releases
179188
- `0.1.0rc1` for release candidates
180189
- `0.1.0` only after install, docs, DB migration, and generated workspace

docs/tradingcodex-prd.md

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,13 @@ Generated Codex workspaces also render a project-scoped
272272
OpenAI Codex MCP configuration shape: a stdio `command`, `args`, `enabled`,
273273
`env`, `enabled_tools`, `default_tools_approval_mode`, `startup_timeout_sec`,
274274
and `tool_timeout_sec`. Project-scoped Codex config applies only when the
275-
generated workspace is trusted by Codex. The TradingCodex MCP config sets
276-
`TRADINGCODEX_MCP_AUTOSTART_SERVICE=1`, so Codex MCP startup idempotently starts
277-
the local Django dashboard service at `127.0.0.1:8000` while keeping MCP stdio
278-
stdout clean. The root `head-manager` allowlist exposes research, audit,
275+
generated workspace is trusted by Codex. The generated TradingCodex MCP command
276+
uses `uvx --from <package-spec> tcx mcp stdio`, where the package spec is
277+
recorded during bootstrap so PyPI and GitHub-source installs keep the same MCP
278+
source. The TradingCodex MCP config sets `TRADINGCODEX_MCP_AUTOSTART_SERVICE=1`,
279+
so Codex MCP startup idempotently starts the local Django dashboard service at
280+
`127.0.0.1:8000` while keeping MCP stdio stdout clean. The root `head-manager`
281+
allowlist exposes research, audit,
279282
portfolio/status, and policy simulation tools, but excludes approval creation
280283
and execution submission. Role-scoped agent TOML files expose the narrower risky
281284
tools only to their owner roles: `risk-manager` can create approval receipts,
@@ -357,18 +360,19 @@ Codex-native bootstrap verification:
357360
role boundary.
358361
- `./tcx mcp stdio` `tools/list` verifies the TradingCodex MCP bridge and tool
359362
annotations, including `experimental = true` on execution tools.
360-
- Generated Codex MCP config starts the stdio MCP bridge and local dashboard
361-
service together; direct `./tcx mcp stdio` remains service-free unless
362-
`TRADINGCODEX_MCP_AUTOSTART_SERVICE=1` is set.
363-
- `codex mcp get tradingcodex` verifies Codex's own resolved MCP config only
364-
after the generated workspace is a trusted Codex project; otherwise Codex may
365-
show only user/global MCP servers.
363+
- Generated Codex MCP config starts the stdio MCP bridge through `uvx` and
364+
starts the local dashboard service together; direct `./tcx mcp stdio` remains
365+
service-free unless `TRADINGCODEX_MCP_AUTOSTART_SERVICE=1` is set.
366+
- `codex exec -C <workspace> --skip-git-repo-check ...` can verify that Codex
367+
CLI loads the generated project context. The management command
368+
`codex mcp list/get` may show only user/global MCP servers, even when a
369+
session uses project-scoped MCP config after workspace trust.
366370

367371
Generated workspaces contain:
368372

369373
- one root `head-manager`
370374
- nine fixed subagents
371-
- fixed subagents configured for `model = "gpt-5.5"` and `reasoning_effort = "high"`
375+
- fixed subagents configured for `model = "gpt-5.5"` and `model_reasoning_effort = "high"`
372376
- twenty-one repo skills
373377
- information-barrier policies
374378
- order/approval schemas

install.sh

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#!/usr/bin/env sh
2+
set -eu
3+
4+
PYTHON_VERSION="3.14"
5+
PACKAGE_SPEC="tradingcodex"
6+
WORKSPACE=""
7+
OVERWRITE="0"
8+
RUN_DOCTOR="1"
9+
10+
usage() {
11+
cat <<'USAGE'
12+
Usage:
13+
install.sh [options] <workspace>
14+
15+
Options:
16+
--from <package-spec> Install from a PyPI name, path, URL, or PEP 508 spec.
17+
--from-github Install from monarchjuno/tradingcodex main.
18+
--python <version> Python version for uvx. Default: 3.14.
19+
--overwrite Pass --overwrite to tcx init.
20+
--no-doctor Skip ./tcx doctor after bootstrap.
21+
-h, --help Show this help.
22+
23+
Examples:
24+
install.sh ~/tradingcodex-workspaces/apple-research
25+
install.sh --from-github ~/tradingcodex-workspaces/apple-research
26+
USAGE
27+
}
28+
29+
while [ "$#" -gt 0 ]; do
30+
case "$1" in
31+
--from)
32+
if [ "$#" -lt 2 ]; then
33+
echo "install.sh: --from requires a package spec" >&2
34+
exit 2
35+
fi
36+
PACKAGE_SPEC="$2"
37+
shift 2
38+
;;
39+
--from-github)
40+
PACKAGE_SPEC="tradingcodex @ git+https://github.com/monarchjuno/tradingcodex.git@main"
41+
shift
42+
;;
43+
--python)
44+
if [ "$#" -lt 2 ]; then
45+
echo "install.sh: --python requires a version" >&2
46+
exit 2
47+
fi
48+
PYTHON_VERSION="$2"
49+
shift 2
50+
;;
51+
--overwrite)
52+
OVERWRITE="1"
53+
shift
54+
;;
55+
--no-doctor)
56+
RUN_DOCTOR="0"
57+
shift
58+
;;
59+
-h|--help)
60+
usage
61+
exit 0
62+
;;
63+
--)
64+
shift
65+
break
66+
;;
67+
-*)
68+
echo "install.sh: unknown option: $1" >&2
69+
usage >&2
70+
exit 2
71+
;;
72+
*)
73+
if [ -n "$WORKSPACE" ]; then
74+
echo "install.sh: only one workspace path is supported" >&2
75+
usage >&2
76+
exit 2
77+
fi
78+
WORKSPACE="$1"
79+
shift
80+
;;
81+
esac
82+
done
83+
84+
if [ -z "$WORKSPACE" ] && [ "$#" -gt 0 ]; then
85+
WORKSPACE="$1"
86+
shift
87+
fi
88+
89+
if [ -z "$WORKSPACE" ]; then
90+
usage >&2
91+
exit 2
92+
fi
93+
94+
ensure_uvx() {
95+
if command -v uvx >/dev/null 2>&1; then
96+
return 0
97+
fi
98+
if command -v uv >/dev/null 2>&1; then
99+
return 0
100+
fi
101+
echo "install.sh: uv/uvx not found; installing uv into the user environment" >&2
102+
if command -v curl >/dev/null 2>&1; then
103+
curl -LsSf https://astral.sh/uv/install.sh | sh
104+
elif command -v wget >/dev/null 2>&1; then
105+
wget -qO- https://astral.sh/uv/install.sh | sh
106+
else
107+
echo "install.sh: install uv first: https://docs.astral.sh/uv/" >&2
108+
exit 127
109+
fi
110+
PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH"
111+
export PATH
112+
if ! command -v uvx >/dev/null 2>&1 && ! command -v uv >/dev/null 2>&1; then
113+
echo "install.sh: uv installation finished, but uvx is still not on PATH" >&2
114+
exit 127
115+
fi
116+
}
117+
118+
run_uvx() {
119+
if command -v uvx >/dev/null 2>&1; then
120+
uvx "$@"
121+
else
122+
uv tool run "$@"
123+
fi
124+
}
125+
126+
ensure_uvx
127+
128+
echo "install.sh: bootstrapping TradingCodex workspace: $WORKSPACE" >&2
129+
echo "install.sh: package spec: $PACKAGE_SPEC" >&2
130+
TRADINGCODEX_MCP_PACKAGE_SPEC="$PACKAGE_SPEC"
131+
export TRADINGCODEX_MCP_PACKAGE_SPEC
132+
UV_NO_CACHE=1
133+
export UV_NO_CACHE
134+
135+
if [ "$OVERWRITE" = "1" ]; then
136+
run_uvx --isolated --refresh --python "$PYTHON_VERSION" --from "$PACKAGE_SPEC" tcx init "$WORKSPACE" --overwrite
137+
else
138+
run_uvx --isolated --refresh --python "$PYTHON_VERSION" --from "$PACKAGE_SPEC" tcx init "$WORKSPACE"
139+
fi
140+
141+
if [ "$RUN_DOCTOR" = "1" ]; then
142+
(cd "$WORKSPACE" && ./tcx doctor)
143+
fi

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "tradingcodex"
7-
version = "0.1.0a3"
7+
version = "0.1.0a4"
88
description = "Codex-native trading harness with a Django service plane and MCP execution boundary."
99
readme = "README.md"
1010
requires-python = ">=3.14,<3.15"

tests/test_python_migration.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,12 @@ def test_python_generator_creates_workspace_contract(tmp_path: Path) -> None:
109109
stale_mcp_tool_names = {"evaluate_policy", "get_positions_snapshot", "write_audit_event"}
110110
root_config = tomllib.loads((workspace / ".codex" / "config.toml").read_text(encoding="utf-8"))
111111
root_mcp = root_config["mcp_servers"]["tradingcodex"]
112-
assert root_mcp["command"] == sys.executable
113-
assert root_mcp["args"] == [str(workspace / ".tradingcodex" / "mcp" / "server.py")]
112+
assert root_mcp["command"] == "uvx"
113+
assert root_mcp["args"] == ["--python", "3.14", "--from", "tradingcodex", "tcx", "mcp", "stdio"]
114114
assert root_mcp["enabled"] is True
115115
assert root_mcp["env"]["TRADINGCODEX_MCP_AUTOSTART_SERVICE"] == "1"
116116
assert root_mcp["env"]["TRADINGCODEX_SERVICE_ADDR"] == "127.0.0.1:8000"
117+
assert root_mcp["env"]["TRADINGCODEX_WORKSPACE_ROOT"] == str(workspace)
117118
assert set(root_mcp["enabled_tools"]).issubset(actual_mcp_tools)
118119
assert stale_mcp_tool_names.isdisjoint(root_mcp["enabled_tools"])
119120
assert "simulate_policy" in root_mcp["enabled_tools"]
@@ -124,10 +125,13 @@ def test_python_generator_creates_workspace_contract(tmp_path: Path) -> None:
124125
for agent_file in agent_files:
125126
agent_config = agent_file.read_text(encoding="utf-8")
126127
assert 'model = "gpt-5.5"' in agent_config
127-
assert 'reasoning_effort = "high"' in agent_config
128+
assert 'model_reasoning_effort = "high"' in agent_config
128129
agent_toml = tomllib.loads(agent_config)
129130
agent_mcp = agent_toml["mcp_servers"]["tradingcodex"]
131+
assert agent_mcp["command"] == "uvx"
132+
assert agent_mcp["args"] == ["--python", "3.14", "--from", "tradingcodex", "tcx", "mcp", "stdio"]
130133
assert agent_mcp["env"]["TRADINGCODEX_MCP_AUTOSTART_SERVICE"] == "1"
134+
assert agent_mcp["env"]["TRADINGCODEX_WORKSPACE_ROOT"] == str(workspace)
131135
configured_tools = set(agent_mcp.get("enabled_tools", [])) | set(agent_mcp.get("disabled_tools", []))
132136
assert configured_tools.issubset(actual_mcp_tools), agent_file
133137
assert stale_mcp_tool_names.isdisjoint(configured_tools), agent_file
@@ -185,7 +189,7 @@ def test_init_current_directory_and_overwrite_language(tmp_path: Path) -> None:
185189

186190
assert f"TradingCodex workspace created: {workspace.resolve()}" in result.stdout
187191
assert (workspace / "tcx").exists()
188-
assert json.loads((workspace / ".tradingcodex" / "generated" / "module-lock.json").read_text(encoding="utf-8"))["tradingcodex_version"] == "0.1.0a3"
192+
assert json.loads((workspace / ".tradingcodex" / "generated" / "module-lock.json").read_text(encoding="utf-8"))["tradingcodex_version"] == "0.1.0a4"
189193

190194
repeated = run([sys.executable, "-m", "tradingcodex_cli", "init", "."], workspace, expect_ok=False, env_extra=env_extra)
191195
assert "--overwrite" in repeated.stderr

tradingcodex_cli/generator.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import json
4+
import os
45
import shutil
56
import sys
67
from dataclasses import dataclass
@@ -50,7 +51,8 @@ def bootstrap_workspace(project_dir: Path | str, force: bool = False, dry_run: b
5051
"SOURCE_ROOT": str(repo_root()),
5152
"PYTHON_EXECUTABLE": sys.executable,
5253
"GENERATED_AT": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
53-
"TRADINGCODEX_VERSION": "0.1.0a3",
54+
"TRADINGCODEX_VERSION": "0.1.0a4",
55+
"TRADINGCODEX_MCP_PACKAGE_SPEC": os.environ.get("TRADINGCODEX_MCP_PACKAGE_SPEC", "tradingcodex"),
5456
"SUBAGENT_COUNT": str(len(subagents)),
5557
}
5658
result = {

tradingcodex_cli/workspace.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,9 @@ def mcp(root: Path, argv: list[str]) -> None:
221221
return
222222
if argv and argv[0] == "stdio":
223223
from tradingcodex_cli.mcp_stdio import run_stdio
224+
from tradingcodex_cli.service_autostart import maybe_autostart_service
224225

226+
maybe_autostart_service(root)
225227
run_stdio(root)
226228
return
227229
if argv and argv[0] in {"ledger", "calls"}:

0 commit comments

Comments
 (0)