Skip to content

Commit 68a0313

Browse files
authored
enh: skills (#5)
* enh: skills * chore: code sandbox * ci: add * ci * bump * bump
1 parent d020672 commit 68a0313

22 files changed

Lines changed: 397 additions & 25 deletions

.github/workflows/build.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Build
2+
3+
on:
4+
push:
5+
branches: [ main, develop ]
6+
pull_request:
7+
branches: [ main, develop ]
8+
workflow_dispatch:
9+
10+
jobs:
11+
python-build:
12+
name: Python Build & Test (${{ matrix.python-version }})
13+
strategy:
14+
matrix:
15+
python-version: ['3.11', '3.12']
16+
uses: ./.github/workflows/reusable-python.yml
17+
with:
18+
python-version: ${{ matrix.python-version }}
19+
install-extras: test,typing
20+
extra-packages: dbus-python keyring secretstorage
21+
run-tests: true
22+
test-command: pytest tests/ --cov=agent_codemode --cov-report term-missing
23+
run-mypy: true
24+
mypy-target: agent_codemode
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: Py Code Style
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- develop
8+
paths:
9+
- 'agent_codemode/**/*.py'
10+
- 'tests/**/*.py'
11+
- 'pyproject.toml'
12+
- 'setup.py'
13+
- '.pre-commit-config.yaml'
14+
- 'ruff.toml'
15+
- '.ruff.toml'
16+
- '.github/workflows/py-code-style.yml'
17+
- '.github/workflows/reusable-python.yml'
18+
pull_request:
19+
branches:
20+
- main
21+
- develop
22+
paths:
23+
- 'agent_codemode/**/*.py'
24+
- 'tests/**/*.py'
25+
- 'pyproject.toml'
26+
- 'setup.py'
27+
- '.pre-commit-config.yaml'
28+
- 'ruff.toml'
29+
- '.ruff.toml'
30+
- '.github/workflows/py-code-style.yml'
31+
- '.github/workflows/reusable-python.yml'
32+
workflow_dispatch:
33+
34+
concurrency:
35+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
36+
cancel-in-progress: true
37+
38+
jobs:
39+
check-code-style:
40+
uses: ./.github/workflows/reusable-python.yml
41+
with:
42+
python-version: '3.11'
43+
extra-packages: pre-commit
44+
run-pre-commit: true

.github/workflows/py-tests.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: Py Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- 'agent_codemode/**'
9+
- 'tests/**'
10+
- 'pyproject.toml'
11+
- 'setup.py'
12+
- 'requirements*.txt'
13+
- '**/*.py'
14+
- '.github/workflows/py-tests.yml'
15+
- '.github/workflows/reusable-python.yml'
16+
pull_request:
17+
branches:
18+
- main
19+
paths:
20+
- 'agent_codemode/**'
21+
- 'tests/**'
22+
- 'pyproject.toml'
23+
- 'setup.py'
24+
- 'requirements*.txt'
25+
- '**/*.py'
26+
- '.github/workflows/py-tests.yml'
27+
- '.github/workflows/reusable-python.yml'
28+
29+
workflow_dispatch:
30+
31+
jobs:
32+
tests:
33+
strategy:
34+
max-parallel: 1
35+
matrix:
36+
python-version: ['3.10', '3.11', '3.12', '3.13']
37+
uses: ./.github/workflows/reusable-python.yml
38+
with:
39+
python-version: ${{ matrix.python-version }}
40+
install-extras: test
41+
extra-packages: dbus-python keyring secretstorage
42+
run-tests: true
43+
test-command: pytest tests/ --cov=agent_codemode --cov-report term-missing

.github/workflows/py-typing.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: Py Type Checking
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- develop
8+
paths:
9+
- 'agent_codemode/**/*.py'
10+
- 'tests/**/*.py'
11+
- 'pyproject.toml'
12+
- 'setup.py'
13+
- 'mypy.ini'
14+
- '.mypy.ini'
15+
- '.github/workflows/py-typing.yml'
16+
- '.github/workflows/reusable-python.yml'
17+
pull_request:
18+
branches:
19+
- main
20+
- develop
21+
paths:
22+
- 'agent_codemode/**/*.py'
23+
- 'tests/**/*.py'
24+
- 'pyproject.toml'
25+
- 'setup.py'
26+
- 'mypy.ini'
27+
- '.mypy.ini'
28+
- '.github/workflows/py-typing.yml'
29+
- '.github/workflows/reusable-python.yml'
30+
workflow_dispatch:
31+
32+
concurrency:
33+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
34+
cancel-in-progress: true
35+
36+
jobs:
37+
type-check:
38+
uses: ./.github/workflows/reusable-python.yml
39+
with:
40+
python-version: '3.12'
41+
install-extras: typing
42+
extra-packages: types-requests
43+
run-mypy: true
44+
mypy-target: agent_codemode
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
name: Reusable Python Workflow
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
python-version:
7+
description: Python version to use
8+
required: false
9+
type: string
10+
default: '3.11'
11+
install-system-deps:
12+
description: Install Linux system dependencies and unlock keyring
13+
required: false
14+
type: boolean
15+
default: true
16+
install-extras:
17+
description: Optional extras to install from pyproject (for example test,typing)
18+
required: false
19+
type: string
20+
default: ''
21+
extra-packages:
22+
description: Optional extra packages to install with uv pip
23+
required: false
24+
type: string
25+
default: ''
26+
run-tests:
27+
description: Run tests
28+
required: false
29+
type: boolean
30+
default: false
31+
test-command:
32+
description: Test command to run when run-tests=true
33+
required: false
34+
type: string
35+
default: 'pytest -q'
36+
run-mypy:
37+
description: Run mypy
38+
required: false
39+
type: boolean
40+
default: false
41+
mypy-target:
42+
description: Package/module path to type-check
43+
required: false
44+
type: string
45+
default: ''
46+
run-pre-commit:
47+
description: Run pre-commit
48+
required: false
49+
type: boolean
50+
default: false
51+
52+
jobs:
53+
python-checks:
54+
runs-on: ubuntu-latest
55+
56+
steps:
57+
- name: Install system dependencies
58+
if: inputs.install-system-deps
59+
run: |
60+
sudo apt-get update
61+
sudo apt-get install -y libdbus-1-3 libdbus-1-dev libglib2.0-dev
62+
63+
- name: Checkout
64+
uses: actions/checkout@v6
65+
66+
- name: Setup UV
67+
uses: astral-sh/setup-uv@v7
68+
with:
69+
version: latest
70+
python-version: ${{ inputs.python-version }}
71+
activate-environment: true
72+
73+
- name: Install dependencies
74+
run: |
75+
uv pip install .
76+
if [[ -n "${{ inputs.install-extras }}" ]]; then
77+
uv pip install ".[${{ inputs.install-extras }}]"
78+
fi
79+
if [[ -n "${{ inputs.extra-packages }}" ]]; then
80+
uv pip install ${{ inputs.extra-packages }}
81+
fi
82+
83+
- name: Unlock keyring
84+
if: inputs.install-system-deps
85+
uses: t1m0thyj/unlock-keyring@v1
86+
87+
- name: Configure git to use https
88+
run: git config --global hub.protocol https
89+
90+
- name: Run tests
91+
if: inputs.run-tests
92+
run: ${{ inputs.test-command }}
93+
94+
- name: Run mypy
95+
if: inputs.run-mypy
96+
run: mypy ${{ inputs.mypy-target }}
97+
98+
- name: Run pre-commit
99+
if: inputs.run-pre-commit
100+
run: pre-commit run --all-files

README.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ Same task, same MCP server — Code Mode uses significantly fewer tokens by comp
6363
|--------|-------------|
6464
| `allow_direct_tool_calls` | When `False` (default), `call_tool` is hidden; all execution flows through `execute_code` |
6565
| `max_tool_calls` | Safety cap limiting tool invocations per `execute_code` run |
66-
| `sandbox_variant` | Sandbox type for code execution (default: `"local-eval"`) |
66+
| `sandbox_variant` | Sandbox type for code execution (default: `"eval"`) |
6767
| `workspace_path` | Working directory for sandbox execution |
6868
| `generated_path` | Path where tool bindings are generated |
6969
| `skills_path` | Path for saved skills |
@@ -461,6 +461,29 @@ When running in a sandbox, state can persist between `execute_code` calls within
461461
- [Advanced Tool Use](https://www.anthropic.com/engineering/advanced-tool-use) - Anthropic
462462
- [Programmatic MCP Prototype](https://github.com/domdomegg/programmatic-mcp-prototype)
463463

464+
## CI Workflows
465+
466+
This repository uses a reusable GitHub Actions workflow at `.github/workflows/reusable-python.yml`.
467+
468+
The following workflows call it:
469+
470+
- `.github/workflows/build.yml`
471+
- `.github/workflows/py-tests.yml`
472+
- `.github/workflows/py-code-style.yml`
473+
- `.github/workflows/py-typing.yml`
474+
475+
Reusable workflow inputs:
476+
477+
- `python-version`: Python version to run.
478+
- `install-system-deps`: Install Linux dependencies and unlock keyring.
479+
- `install-extras`: Extras from `pyproject.toml` (for example `test,typing`).
480+
- `extra-packages`: Additional packages installed with `uv pip install`.
481+
- `run-tests`: Enable test execution.
482+
- `test-command`: Command used for tests.
483+
- `run-mypy`: Enable mypy.
484+
- `mypy-target`: Package or module passed to mypy.
485+
- `run-pre-commit`: Enable pre-commit checks.
486+
464487
## License
465488

466489
BSD 3-Clause License

agent_codemode/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44

55
"""Agent Codemode."""
66

7-
__version__ = "0.1.1"
7+
__version__ = "0.1.3"

agent_codemode/composition/executor.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ def set_skills_metadata(self, metadata: list[dict[str, Any]]) -> None:
133133
self._skills_metadata = metadata
134134

135135
def _is_local_eval_sandbox(self) -> bool:
136-
"""Check if the sandbox is a local-eval type (has in-memory namespaces).
136+
"""Check if the sandbox is a eval type (has in-memory namespaces).
137137
138138
This checks the actual sandbox instance, not the config, to handle
139139
cases where an external sandbox is passed that differs from config.
@@ -202,7 +202,7 @@ async def _setup_sandbox_environment(self) -> None:
202202
# Use /tmp so 'from generated.mcp...' works (files are at /tmp/generated/)
203203
sandbox_generated_path = "/tmp"
204204
else:
205-
# For local-eval, use the parent directory so 'from generated.mcp...' works
205+
# For eval, use the parent directory so 'from generated.mcp...' works
206206
# The generated_path might be './generated', we need its parent on sys.path
207207
sandbox_generated_path = str(generated_path.parent)
208208

@@ -705,7 +705,7 @@ def generate_skills_in_sandbox(self) -> None:
705705
skill scripts **directly** in the Jupyter kernel, reading the script
706706
files from the shared ``skills_path`` on disk. This avoids the
707707
HTTP proxy round-trip (call_tool → MCP proxy → agent-runtimes →
708-
local-eval fallback) which caused blocking and deadlocks.
708+
eval fallback) which caused blocking and deadlocks.
709709
710710
The skills_path is the same between the agent-runtimes process and
711711
the Jupyter runtime (shared filesystem or mount).
@@ -717,7 +717,7 @@ def generate_skills_in_sandbox(self) -> None:
717717
if self._sandbox is None or not self._skills_metadata:
718718
return
719719

720-
# Skip for local-eval sandboxes (they use the on-disk generated files)
720+
# Skip for eval sandboxes (they use the on-disk generated files)
721721
if self._is_local_eval_sandbox():
722722
return
723723

@@ -1032,7 +1032,7 @@ async def execute(
10321032

10331033
# Get the generated path for sys.path setup
10341034
# For remote sandboxes, use /tmp so 'from generated.mcp...' works (files at /tmp/generated/)
1035-
# For local-eval, use parent of generated_path so 'from generated.mcp...' works
1035+
# For eval, use parent of generated_path so 'from generated.mcp...' works
10361036
# Use actual sandbox type detection, not config
10371037
is_local_eval = self._is_local_eval_sandbox()
10381038
if not is_local_eval:
@@ -1086,7 +1086,7 @@ async def execute(
10861086
'''
10871087
# Branch based on actual sandbox type (already computed above)
10881088
if is_local_eval:
1089-
# For local-eval, we can access _namespaces directly
1089+
# For eval, we can access _namespaces directly
10901090
return await self._execute_local_eval(code, setup_code, timeout)
10911091
else:
10921092
# For Jupyter/remote sandboxes, use run_code()
@@ -1099,7 +1099,7 @@ async def _execute_local_eval(
10991099
setup_code: str,
11001100
timeout: Optional[float] = None,
11011101
) -> ExecutionResult:
1102-
"""Execute code in local-eval sandbox with direct namespace access."""
1102+
"""Execute code in eval sandbox with direct namespace access."""
11031103
import sys
11041104
import io
11051105
import time

agent_codemode/toolset.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ class CodemodeToolset(AbstractToolset):
100100

101101
registry: ToolRegistry | None = None
102102
config: CodeModeConfig = field(default_factory=CodeModeConfig)
103-
sandbox: Any | None = None # Optional pre-configured sandbox (e.g., LocalEvalSandbox)
103+
sandbox: Any | None = None # Optional pre-configured sandbox (e.g., EvalSandbox)
104104
allow_direct_tool_calls: bool | None = None
105105
allow_discovery_tools: bool = True
106106
tool_reranker: Callable[[list, str, Optional[str]], Awaitable[list]] | None = None

agent_codemode/types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ class CodeModeConfig(BaseModel):
223223
workspace_path: str = "./workspace"
224224
skills_path: str = "./skills"
225225
generated_path: str = "./generated"
226-
sandbox_variant: str = "local-eval"
226+
sandbox_variant: str = "eval"
227227
sandbox_image: str | None = None
228228
allow_direct_tool_calls: bool = False
229229
max_tool_calls: int | None = None

0 commit comments

Comments
 (0)