Python port of Claude Code agent harness — tools, commands, task orchestration, and CLI entrypoint via oh-my-codex
triggers
["how do I run clawd-code","how do I use the Python port of Claude Code","clawd-code CLI commands","how do I add a tool to clawd-code","how does the agent harness work in clawd-code","how do I extend clawd-code with new commands","how do I run the parity audit in clawd-code","how do I verify the Python workspace in clawd-code"]
clawd-code is an independent Python rewrite of the Claude Code agent harness, built from scratch for educational purposes. It captures the architectural patterns of Claude Code — tool wiring, command dispatch, task orchestration, and agent runtime context — in clean Python, without copying any proprietary TypeScript source.
The project is orchestrated end-to-end using oh-my-codex (OmX), a workflow layer on top of OpenAI Codex. It is not affiliated with or endorsed by Anthropic.
Installation
# Clone the repository
git clone https://github.com/instructkr/clawd-code.git
cd clawd-code
# (Optional but recommended) Create a virtual environment
python3 -m venv .venv
source .venv/bin/activate
# Install dependencies (if a requirements.txt or pyproject.toml is present)
pip install -r requirements.txt
pip install -e .
# or
No API keys are needed for the manifest/summary/CLI commands. If you extend the query engine to call a live model, set your key via environment variable:
Tasks wrap a unit of agent work — a goal, a set of tools, and a result:
from dataclasses import dataclass, field
from typing importList, Optional, Any@dataclassclassTaskResult:
success: bool
output: Any
error: Optional[str] = None@dataclassclassTask:
goal: str
tools: List[str] = field(default_factory=list) # tool names available
context: dict = field(default_factory=dict)
result: Optional[TaskResult] = Nonedefrun(self, dispatcher) -> TaskResult:
"""
dispatcher: callable(tool_name, **kwargs) -> Any
Implement your agent loop here.
"""try:
# Minimal stub: just report goal received
output = f"Task received: {self.goal}"self.result = TaskResult(success=True, output=output)
except Exception as e:
self.result = TaskResult(success=False, output=None, error=str(e))
returnself.result
# Usagefrom src.tools import dispatch_tool
task = Task(
goal="Read README.md and summarize it",
tools=["read_file"],
context={"working_dir": "."},
)
result = task.run(dispatcher=dispatch_tool)
print(result.output)
Query Engine (src/query_engine.py)
The query engine renders a porting summary from the active manifest:
from src.port_manifest import build_manifest
from src.query_engine import render_summary
manifest = build_manifest()
summary = render_summary(manifest)
print(summary)
You can also invoke it from the CLI:
python3 -m src.main summary
Port Manifest (src/port_manifest.py)
Build and inspect the current workspace manifest programmatically:
from src.port_manifest import build_manifest
manifest = build_manifest()
for subsystem in manifest.subsystems:
print(f"[{subsystem.name}]")
for module in subsystem.modules:
print(f" {module.name}: {module.status}")
print("Backlog:", manifest.backlog)
Adding a New Tool
Define a handler function in src/tools.py.
Create a Tool dataclass instance.
Register it in TOOL_REGISTRY.
Write a test in tests/.
# src/tools.pydeflist_dir_handler(path: str):
import os
return os.listdir(path)
LIST_DIR_TOOL = Tool(
name="list_dir",
description="List files in a directory.",
parameters={"path": {"type": "string"}},
handler=list_dir_handler,
)
TOOL_REGISTRY["list_dir"] = LIST_DIR_TOOL
# Run all tests with verbose output
python3 -m unittest discover -s tests -v
# Run a specific test file
python3 -m unittest tests.test_tools -v
Example test pattern:
# tests/test_tools.pyimport unittest
from src.tools import dispatch_tool
import tempfile, os
classTestReadFileTool(unittest.TestCase):
deftest_read_file(self):
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
f.write("hello clawd")
path = f.name
try:
result = dispatch_tool("read_file", path=path)
self.assertEqual(result, "hello clawd")
finally:
os.unlink(path)
if __name__ == "__main__":
unittest.main()
Parity Audit
When a local ignored archive of the original snapshot is present, run:
python3 -m src.main parity-audit
This compares the current Python workspace surface against the archived root-entry file surface, subsystem names, and command/tool inventories, reporting gaps.
Common Patterns
Chaining tools in a task loop
from src.tools import dispatch_tool
from src.task import Task
task = Task(
goal="Read and list files",
tools=["read_file", "list_dir"],
context={"working_dir": "."},
)
# Manual tool chain (before full agent loop is implemented)
files = dispatch_tool("list_dir", path=".")
for fname in files[:3]:
content = dispatch_tool("read_file", path=fname)
print(f"--- {fname} ---\n{content[:200]}")
Using the manifest in automation
from src.port_manifest import build_manifest
defunported_modules():
manifest = build_manifest()
stubs = []
for sub in manifest.subsystems:
for mod in sub.modules:
if mod.status != "ported":
stubs.append((sub.name, mod.name, mod.status))
return stubs
for subsystem, module, status in unported_modules():
print(f"{subsystem}/{module} → {status}")
Troubleshooting
Symptom
Fix
ModuleNotFoundError: src
Run commands from the repo root, not inside src/
NotImplementedError: Tool 'x' has no handler
The tool is registered but the Python handler hasn't been written yet — implement handler in tools.py
parity-audit does nothing
The local ignored archive must be present at the expected path; see port_manifest.py for the expected location
Tests not discovered
Ensure test files are named test_*.py and located in tests/