Development
July 17, 2026 · View on GitHub
Build from source
Source builds are primarily for development and local testing.
make build
make up
The compose stack passes PUID / PGID by default (1000:1000). To match the container agent user/group to your host user:
PUID=$(id -u) PGID=$(id -g) make up
The default compose container name is pibox:
docker exec -u agent -it pibox bash
cd /workspace && pi
Build targets
Run build/package commands from the repo root:
make build-piclaw # full build: vendor bundle + web assets + TypeScript
make vendor # rebuild vendored assets
make lint # ESLint
make test # full test suite
make ci-fast # canonical fast CI guardrails + web build
make local-install # pack and install piclaw globally (no restart)
make restart # restart piclaw via the detected service manager
CI workflow map
For the current end-to-end GitHub Actions flow (triggers, job dependencies, and step-level map), see:
Testing
The implementation lives under runtime/, so direct Bun test runs should target that subtree. Sequential mode is recommended for SQLite safety:
cd runtime && bun test --max-concurrency=1
Focused integration notes
Earendil 0.80.10 runtime
Piclaw's Pi runtime packages are sourced from @earendil-works/* and are pinned together at 0.80.10. The runtime uses upstream model/auth services, catalogs, pricing, native max thinking, compaction estimation, Anthropic signature handling, and configurable shellPath behavior while keeping a small set of Piclaw-owned compatibility layers.
| Compatibility layer | Why Piclaw still owns it | Focused coverage |
|---|---|---|
runtime/src/extensions/azure-openai-api.ts and runtime/extensions/integrations/azure-openai.ts | Azure transport, deployment mapping, session correlation, replay sanitation, context budgeting, and throttle feedback | runtime/test/extensions/azure-openai-*.test.ts |
runtime/src/extensions/github-copilot-dynamic-models.ts | Merge authenticated live Copilot models into Earendil's static registry without collapsing native max into xhigh | runtime/test/extensions/github-copilot-dynamic-models.test.ts |
runtime/src/agent-control/ and runtime/web/src/components/settings/models.ts | Preserve native xhigh/max as separate levels while retaining compatibility with old custom { xhigh: "max" } metadata | runtime/test/agent-control/*.test.ts |
runtime/src/tools/tracked-bash.ts | Keep Piclaw's process tracking and keychain injection while honoring Pi's configured shellPath | runtime/test/tools/tracked-bash.test.ts |
runtime/src/extensions/mcp-timeout-patch.ts | Preserve Piclaw's outer PICLAW_MCP_TOOL_TIMEOUT_MS timeout/abort guard around tools registered by the packaged adapter | runtime/test/extensions/mcp-timeout-patch.test.ts |
runtime/src/agent-pool/cache-stats.ts | Add diagnostic cache re-billing to the shared session statistics | runtime/test/agent-pool/cache-stats.test.ts |
runtime/vendor-manifests/ plus generated *.meta.json | Keep browser dependencies reproducible and auditable | runtime/test/scripts/runtime-vendors.test.ts |
The compatibility layers fail conservatively: dynamic model discovery falls back to the static catalog, the MCP wrapper leaves unrelated tools untouched and can be disabled independently, and statistics omit cache re-billing when provider usage data is insufficient.
Useful focused validation from the repository root:
bun test \
runtime/test/agent-pool/cache-stats.test.ts \
runtime/test/extensions/mcp-timeout-patch.test.ts \
runtime/test/extensions/github-copilot-dynamic-models.test.ts \
runtime/test/extensions/azure-openai-api.test.ts \
runtime/test/extensions/azure-openai-retry-after.test.ts \
runtime/test/extensions/azure-openai-routing.test.ts
User/operator behavior is documented in configuration.md, tools-and-skills.md, mcp.md, azure-openai-extension.md, and vendored-widget-libraries.md.
MCP adapter
PiClaw bundles pi-mcp-adapter as a normal package dependency and loads it as a packaged session extension from node_modules/.
Relevant files when working on MCP integration:
package.json/bun.lockruntime/src/agent-pool/session.tsdocs/mcp.mdskel/.mcp.json.exampleskel/.pi/mcp.json.exampleskel/.pi/skills/mcp-adapter/SKILL.md
Piclaw uses pi-mcp-adapter 2.11.0, which forwards abort signals and applies its configured requestTimeoutMs to protocol requests. Piclaw independently wraps MCP tools after their session_start registration so existing PICLAW_MCP_TOOL_TIMEOUT_MS behavior remains stable across adapter upgrades.
Focused regression tests:
PICLAW_DB_IN_MEMORY=1 bun test \
runtime/test/agent-pool/mcp-adapter-bundled.test.ts \
runtime/test/extensions/mcp-timeout-patch.test.ts
Azure OpenAI image commands
Recent Azure OpenAI work hardened the shared helper resolution path, improved image-output formatting, and added transparent PNG support to /image.
Relevant files:
runtime/extensions/integrations/azure-openai.tsruntime/src/extensions/azure-openai-api.tsruntime/test/extensions/azure-openai-api.test.tsruntime/test/extensions/azure-openai-image-output.test.tsdocs/azure/azure-openai-extension.md
Focused regression tests:
bun test \
runtime/test/extensions/azure-openai-api.test.ts \
runtime/test/extensions/azure-openai-image-output.test.ts
Notes:
/image --transparentrequests transparent PNG output on the Azure OpenAI image path./fluxstill rejects transparent background requests.- Successful image runs format results as workspace-backed inline images plus file listings rather than raw download links.
Azure OpenAI / Foundry harness
Use the standalone harness when you need provider-level evidence without reloading the running Piclaw process.
Relevant files:
runtime/scripts/azure-openai-harness.tsruntime/extensions/experimental/azure-openai.harness.tsruntime/extensions/integrations/azure-openai.tsruntime/src/extensions/azure-openai-api.tsdocs/azure/azure-openai-extension.md
Typical commands:
bun run scripts/azure-openai-harness.ts --list
bun run scripts/azure-openai-harness.ts --models gpt-5-3-codex,gpt-5-4 --cases json,tool,history --tool-rounds 2 --history-turns 3
AOAI_EXPERIMENT_AZURE_CLIENT_REQUEST_ID=1 bun run scripts/azure-openai-harness.ts --providers azure-openai --models gpt-5-3-codex --cases json,tool,history --tool-rounds 2 --history-turns 3
Notes:
- the harness bundles to
/workspace/piclaw/.tmp/azure-openai.harness.bundle.mjsso Bun resolves this repo's dependencies correctly - the live Azure extension aligns
prompt_cache_key,session_id, andx-client-request-idfrom the active session id on the Azure Responses path - the harness checks those correlation fields automatically and fails if they drift
- the harness also fails if replayed request payloads still contain leaked
partialJsonscratch buffers - historical
0.67.2live-provider evidence forgpt-5-3-codexandgpt-5-4is recorded in azure-openai-extension.md; deterministic0.80.10behavior is covered by the focused tests in the Earendil section above AOAI_EXPERIMENT_AZURE_CLIENT_REQUEST_ID=1remains available for the optionalx-ms-client-request-idexperiment
Workspace search / reindex UI
Recent workspace explorer changes added an index-status surface and manual reindex control on top of the existing FTS search/indexing pipeline.
Relevant files:
runtime/src/workspace-search.tsruntime/src/extensions/workspace-search.tsruntime/src/channels/web/handlers/workspace.tsruntime/src/channels/web/workspace/service.tsruntime/web/src/components/workspace-explorer.ts
Focused regression tests:
PICLAW_DB_IN_MEMORY=1 bun test \
runtime/test/channels/web/http-dispatch-workspace.test.ts \
runtime/test/channels/web/http-route-classification.test.ts \
runtime/test/channels/web/workspace-service.test.ts \
runtime/test/workspace-search.test.ts
OOBE local container + Playwright smoke
A realistic OOBE browser pass can be run against a local Docker container rather than only against the in-process dedicated web test instance.
Default command:
bun run test:oobe:local-container
The script:
- ensures Playwright Chromium is available
- builds a local image (
piclaw-oobe-test:local) unless skipped - mounts the repo's current
runtime/web/static/distinto the container so web-bundle changes can be validated against the latest local build without requiring a fresh image for every UI-only tweak - starts a temporary local Piclaw container on a random localhost port
- runs Playwright against the live web UI
- writes screenshots, DOM dumps, state captures, and container logs under
artifacts/oobe-local-container/ - validates:
- provider-missing OOBE panel copy
/logincompose prefill- dismiss persistence after reload
- provider-ready OOBE state
/modelcompose prefill- ready-state completion persistence after reload
Useful flags/env:
# Skip the image rebuild when only the web bundle changed locally.
# Rebuild the local web assets first so the mounted dist is current.
cd runtime && bun run build:web && cd ..
PICLAW_OOBE_TEST_SKIP_BUILD=1 bun run test:oobe:local-container
PICLAW_OOBE_TEST_IMAGE=pibox:latest bun run test:oobe:local-container
PICLAW_OOBE_TEST_HEADLESS=0 bun run test:oobe:local-container
Implementation surface:
runtime/scripts/playwright/oobe-local-container.ts
Notes:
- The generated
artifacts/oobe-local-container/files are local smoke-test artefacts, not release payloads. - Clean them up before tagging if you do not intend to keep the latest repro bundle around.
Editor file conflict detection
The editor pane polls GET /workspace/stat?path=<file> every 5s while the tab is focused and shows a conflict resolution bar when the on-disk mtime advances past the last known mtime. The same FileConflictMonitor can be reused by pane-style editors, and add-ons can vendor the same pattern for specialized editors such as kanban-editor.
Relevant files:
runtime/extensions/viewers/editor/editor-extension.tsruntime/web/src/panes/file-conflict-monitor.ts
Recovery and resilience
Blank-turn detection, compaction stall bounding, the recovery chip, and the held-failed-run retry/skip model are documented in architecture.md under "Per-chat turn lifecycle and failure model", "Recovery chip", "Blank turn detection", and "Compaction stall guard".
Relevant files:
runtime/src/agent-pool/blank-turn-detection.tsruntime/src/agent-pool/prompt-utils.ts—waitForSessionIdle, session idle defaultsruntime/src/agent-pool/automatic-recovery.ts— auto-recovery classification / retry policyruntime/src/channels/web/handlers/agent.ts— web turn finalization, held-failure behavior, retry/skip resolution pointsruntime/src/channels/web/runtime/chat-run-control.ts— explicit retry/skip cursor helpersruntime/src/db/chat-cursors.ts—beginChatRun, inflight rollback, failed-run storage, rollback-with-errorruntime/src/extensions/smart-compaction.ts— working-indicator UI hooks
Focused regression tests:
bun test \
runtime/test/db/chat-cursors.test.ts \
runtime/test/channels/web/runtime/chat-run-control.test.ts \
runtime/test/channels/web/recovery.test.ts \
runtime/test/channels/web/web-channel-recovery-state.test.ts \
runtime/test/channels/web/web-channel.test.ts
Layout
See architecture.md for the full source layout and module boundaries.
Skill and extension development
New skills go in .pi/skills/<name>/SKILL.md (workspace-local) or
skel/.pi/skills/<name>/ (shipped with the skel for new installs).
New internal tools register through the extension API:
pi.registerTool({ name, description, parameters, execute });
For visual artifacts, always load and follow:
/workspace/.pi/skills/visual-artifact-generator/SKILL.md/workspace/.pi/skills/visual-design/SKILL.md
Use the mermaid-fixup.js helper for any artifact that renders Mermaid diagrams.
Adding new HTTP endpoints
New GET /agent/* or POST /agent/* endpoints follow this chain:
runtime/src/channels/web/http/dispatch-agent.ts— register the routeruntime/src/channels/web/endpoints/channel-endpoint-facade-service.ts— add handler methodruntime/src/channels/web/core/web-channel-http-surface-service.ts— delegate from surfaceruntime/src/channels/web/core/web-channel-contracts.ts— declare the interfaceruntime/src/channels/web/core/web-channel-prototype.ts— bind the prototype method
See runtime/src/channels/web/agent/agent-commands.ts (GET /agent/commands)
as the canonical simple example.
Documentation updates
When shipping new features, update:
docs/tools-and-skills.md— if new tools, skills, or slash commands are addeddocs/architecture.md— if new endpoints or subsystems are addeddocs/configuration.md— if new environment variables or config keys are addedREADME.md— if the feature merits a bullet in the Why/Feature overviewdocs/vendored-widget-libraries.md— if new vendored libraries or fonts are added