Skip to main content
PortRuntime is the top-level orchestrator for Claw Code. It tokenizes incoming prompts, scores them against the ported command and tool surfaces, assembles a RuntimeSession, and drives multi-turn execution loops — all without calling the upstream Claude Code binary.
from src.runtime import PortRuntime

runtime = PortRuntime()

route_prompt

def route_prompt(self, prompt: str, limit: int = 5) -> list[RoutedMatch]
Tokenizes prompt and scores every ported command and tool against those tokens. Returns up to limit matches sorted by relevance. Selection logic
  1. Score all commands and all tools independently against the prompt tokens.
  2. Pick the single highest-scoring command (if any) and the single highest-scoring tool (if any) first.
  3. Fill remaining slots (up to limit) from the leftover matches, ordered by score descending, then alphabetically.
prompt
string
required
The user-facing prompt to route. Punctuation (/, -) is normalised to spaces before tokenising.
limit
number
default:"5"
Maximum number of RoutedMatch entries to return.
Returns list[RoutedMatch]
kind
string
required
Either "command" or "tool".
name
string
required
The ported module name (e.g. "BashTool", "compact").
source_hint
string
required
Path or identifier in the upstream Claude Code source that this module mirrors.
score
number
required
Number of prompt tokens that matched this module’s name, source_hint, or responsibility string.
from src.runtime import PortRuntime

runtime = PortRuntime()
matches = runtime.route_prompt("read a python file", limit=3)

for m in matches:
    print(f"[{m.kind}] {m.name}  score={m.score}  hint={m.source_hint}")
# [tool] FileReadTool  score=2  hint=tools/FileReadTool/FileReadTool.tsx
# [command] read  score=1  hint=commands/read/index.ts

bootstrap_session

def bootstrap_session(self, prompt: str, limit: int = 5) -> RuntimeSession
Runs a full single-turn session: builds context, runs workspace setup, routes the prompt, executes matched commands and tools, submits one turn to the query engine, and persists the session to disk.
prompt
string
required
The prompt that drives routing and the initial engine turn.
limit
number
default:"5"
Forwarded to route_prompt.
Returns RuntimeSession
prompt
string
The original prompt string.
context
PortContext
Workspace context snapshot (Python file count, archive availability, etc.).
setup
WorkspaceSetup
Detected runtime environment (Python version, platform, test command, startup steps).
setup_report
SetupReport
Full structured output from the workspace setup run.
system_init_message
string
The system-initialisation message passed to the engine (mirrors Claude Code’s system_init).
history
HistoryLog
Structured log of every runtime phase (context, registry, routing, execution, turn, session store).
routed_matches
list[RoutedMatch]
Matches returned by route_prompt.
turn_result
TurnResult
Result of the single QueryEnginePort.submit_message call.
command_execution_messages
tuple[str, ...]
Output messages from each matched command’s .execute(prompt) call.
tool_execution_messages
tuple[str, ...]
Output messages from each matched tool’s .execute(prompt) call.
stream_events
tuple[dict, ...]
All event dicts yielded by stream_submit_message for this turn.
persisted_session_path
string
Absolute path of the .json file written by persist_session().

RuntimeSession.as_markdown()

def as_markdown(self) -> str
Renders the entire session — context, setup, system init, routed matches, command/tool execution logs, stream events, turn output, and full history — as a single Markdown document. Useful for debugging and audit trails.
from src.runtime import PortRuntime

runtime = PortRuntime()
session = runtime.bootstrap_session("list open todo items")

print(session.as_markdown())
# # Runtime Session
# Prompt: list open todo items
# ...

run_turn_loop

def run_turn_loop(
    self,
    prompt: str,
    limit: int = 5,
    max_turns: int = 3,
    structured_output: bool = False,
) -> list[TurnResult]
Routes the prompt once, then drives QueryEnginePort through up to max_turns consecutive turns. Each turn after the first appends [turn N] to the prompt string so the engine can distinguish continuation turns. Early exit: if any TurnResult.stop_reason is not "completed", the loop stops immediately and returns the results collected so far.
prompt
string
required
The base prompt. Routing is performed once; the same matched commands and tools are reused across all turns.
limit
number
default:"5"
Forwarded to route_prompt.
max_turns
number
default:"3"
Upper bound on the number of engine turns to execute.
structured_output
boolean
default:"false"
When True, each turn’s output is serialised as JSON via QueryEngineConfig.structured_output.
Returns list[TurnResult] Each entry is a TurnResult from a single engine turn. See QueryEnginePort.submit_message for the full field reference.
from src.runtime import PortRuntime

runtime = PortRuntime()
results = runtime.run_turn_loop("refactor auth module", max_turns=4)

for i, r in enumerate(results):
    print(f"turn {i+1}: stop_reason={r.stop_reason}  tokens={r.usage.input_tokens}in/{r.usage.output_tokens}out")
run_turn_loop creates a fresh QueryEnginePort from the current workspace for every call. It does not resume a previously persisted session. Use QueryEnginePort.from_saved_session directly if you need to continue an existing session.

Permission inference

PortRuntime automatically gates bash-adjacent tools during bootstrap_session. Any matched tool whose name contains "bash" (case-insensitive) receives a PermissionDenial with the reason:
"destructive shell execution remains gated in the Python port"
These denials are passed to the engine as denied_tools and appear in TurnResult.permission_denials. To apply custom permission rules, construct a ToolPermissionContext and call QueryEnginePort.submit_message directly instead of using bootstrap_session. See ToolPermissionContext for details.

Build docs developers (and LLMs) love