This file contains durable instructions for coding agents working in this repository. Keep it short, current, and focused on rules that should affect day-to-day code changes. Move long runbooks, dated audits, generated examples, and machine-specific workflows into docs.
Clyde is a Go CLI and daemon for raw provider artifact reading plus adapter, MITM, ingress, logging, MCP, and transcript export.
Core surfaces:
- A first-party
clydeCLI that owns Clyde-specific conversation, export, logs, daemon, MITM, and MCP commands. - A raw conversation index that reads Claude, Codex, Cursor, and Zed artifacts without writing provider files.
- An MCP server for conversation listing, retrieval, context windows, search, analysis, and export.
- An OpenAI-compatible adapter under
internal/adapter/. - A MITM capture proxy under
internal/mitm/. - Provider auth behind generic adapter boundaries, with provider-specific credential readers injected by the daemon.
Clyde must not own, create, resume, rename, isolate, compact, wrap, or present provider sessions. Users should run raw claude and raw codex directly for interactive work.
Prefer code and tests over this file for exact behavior.
- Use
cmd/clyde/main.gofor current CLI routing. - Use
internal/conversation/for raw provider artifact indexing, loading, search, and export boundaries. - Use
docs/conversations.mdfor durable conversation model, compaction segment, and export selector terminology. - Use
internal/mcpserver/for MCP tool names and argument contracts. - Use
internal/config/for supported config file formats and fields. - Use
internal/daemon/for daemon startup, adapter/MITM listener ownership, reload, and background conversation-index refresh. - Use
internal/adapter/for adapter, Cursor, Codex, Anthropic, model routing, and request-shape details. - Use
internal/mitm/plusinternal/providers/*/mitmcontrib/for MITM provider registration, route claims, identity facets, and capture extensions. - Use
docs/logging/andinternal/slogger/for logging, sink, inventory, and correlation contracts. - Use
internal/contextsvc/anddocs/context/overview.mdfor the ConversationContext gRPC service that returns recent conversation turns. - Use
docs/cursor.mdfor the empirical reasons behind Cursor-specific adapter rules, anddocs/cursor/stores.mdfor the Cursor conversation stores and how they are read. - Use
docs/reorient/overview.mdfor the two-tier reorient-after-compaction delivery and the MITM summary-injection hook.
Do not add stale snapshots of command tables, schemas, request payloads, local machine setup, or dated audits to this file. Add links or brief pointers instead.
Search for an existing implementation before writing one. A format this repository emits, parses, or owns has exactly one home, and a second implementation drifts from the first the moment either changes.
Search by behavior rather than by name. A primitive is named for the concern that produced it, not the concern you are working in, so it will not surface under the word you searched for. Search the format's literal markers, key prefixes, or field names instead, and read the package that emits a value before writing anything that consumes it.
When an existing primitive is close but not sufficient, extend it in place or say plainly why it cannot serve. Do not fork it. Adding a second parser, registry, or drain needs its reason stated in the pull request rather than left for a reviewer to find.
- Conversation IDs are stable derived IDs:
claude:<provider-session-id>,codex:<thread-id>,cursor:<native-id>,zed:<thread-id>, orartifact:<path-hash>when the provider ID is missing. - Clyde reads provider-owned artifacts only. It must not mutate any provider's transcript files, settings, metadata, or runtime state.
- Cursor and Zed keep conversations in SQLite databases their running application owns. Open those read-only and never write, migrate, or checkpoint them. Cursor holds conversations in more than one store; see
docs/cursor/stores.md. - Startup must load the last completed conversation cache quickly and refresh in a debounced background worker.
- Refreshes must be idempotent and keyed by provider, artifact path, size, and mtime.
- Stale cache data is acceptable while a refresh is running.
- CLI and MCP operations must use
conversation_idterminology. Do not add session-name aliases.
- Running
clydewith no arguments must show help. - Unknown arguments must fail through Cobra/help. Do not forward unknown arguments to provider CLIs.
- Each conversation operation is declared once in
internal/clispecand rendered onto both the terminal command and the MCP tool, with the terminal commands grouped under theconversationparent. Add or change an operation there, not in two places. - The CLI and MCP operation sets stay aligned structurally.
internal/clispec/alignment_test.goreads the one registry and fails if an operation, parameter, or required flag exists on one surface but not the other, so there is no separate list to maintain here.
Preserve zero-bind-gap daemon reload semantics when changing internal/daemon/, internal/adapter/, or internal/mitm/.
- Reload must re-exec the current daemon binary and inherit daemon-owned listener file descriptors.
- Reload must reject listener address changes that require a full restart.
- A reload child must not initiate another reload until it owns the daemon process lock.
- After child readiness, the old generation must stop accepting public traffic and drain or close existing traffic according to the daemon implementation.
- Existing long-lived HTTP, CONNECT, websocket, MCP, and gRPC streams may stay on the old process until graceful drain completes.
A config edit no longer always re-execs. The watcher classifies the change (config.ClassifyConfigChange) and either applies it in process, waits for quiet then reloads, rebinds, or asks for a restart. See docs/reload-and-hot-apply.md.
Keep detailed reload behavior in daemon code comments, tests, or dedicated docs rather than expanding this file.
Any subsystem that owns long-lived state crossing reload, shutdown, or force-close boundaries MUST register that state with internal/livetrack. This includes HTTP connections, MITM CONNECT tunnels, gRPC streams, provider websockets, SSE readers, MCP stdio handlers, file watchers, async workers, and capture-file flocks.
Forbidden in subsystems that have adopted livetrack:
- Bare
http.Server.Shutdownwithout a paired registry drain. - Bare context cancellation as the sole shutdown mechanism for long-lived goroutines that hold OS resources.
- Goroutine fanout without registry registration.
- Per-subsystem hand-rolled equivalents of
WaitForIdle,ActiveCount, orCloseAll.
Registration is now enforced by the API, not by convention: livetrack.New, Registry.Drain, and Registry.DrainWith are unexported, so the only way to construct a registry is livetrack.Attach (which binds it to the daemon lifecycle group) and the only way to drain is Group.Quiesce. See docs/reload-and-hot-apply.md.
The motivating empirical case for long-lived Cursor backend keepalives is documented in docs/cursor.md.
This repository is pre-alpha. Prefer strict type safety over loose compatibility.
- Do not introduce
any,interface{},map[string]any,[]any, or equivalent open-ended payloads in production Go code. - Do not use empty marker structs such as
struct{}or empty JSON payloads such as{}to represent protocol messages, request params, response params, config sections, or domain state. - Wire, config, RPC, logging, and domain payloads must use named structs, typed fields, typed slices, typed maps, and explicit enum-like types where applicable.
- Provider identities should use typed enums at generic boundaries.
- If upstream data is a union, model supported variants explicitly and reject or ignore unsupported variants intentionally at the boundary.
- If JSON must remain partially opaque for an external contract, isolate that opacity at the smallest edge with a named type and a comment citing the contract.
- Tests should assert concrete typed shapes and should not build fixtures with loose maps when production code has or should have concrete types.
Existing loose types are technical debt, not precedent. When touching a loose surface, either replace it with enumerated types in the same change or leave a narrow follow-up note if the refactor is larger than the active task.
Write tests alongside behavior changes when practical. Cover success and error paths, keep tests independent, and use descriptive test names.
If any step of a make target fails, fix the underlying code, test, configuration, or documentation honestly and truthfully.
- Do not turn off, skip, weaken, delete, silence, baseline, or otherwise circumvent a failing
makestep to make the target pass. - Do not add
|| true, ignore exit codes, narrow target scopes, raise thresholds, lower coverage expectations, or remove checks unless the user explicitly asks for that exact policy change. - The fix must be a real code or test correction that addresses the failure while preserving the intent of the check.
- If the failure appears to be caused by an external outage, missing local credential, or unavailable toolchain, report that blocker with the exact command and error instead of bypassing the step.
For changes that affect the OpenAI-compatible adapter, Cursor BYOK ingress, SSE rendering, thinking blocks, tool calls, file reads, or provider request builders, unit tests are necessary but may not be sufficient.
Use the real Cursor client for final verification when the rendered chat output or actual SSE bytes matter. Keep prompts read-only and include a unique probe id. Build, install, and reload the daemon before the probe. Operator-specific automation belongs in a separate runbook, not in this file.
Use docs/testing/overview.md for the live daemon test harness and its parallel-run port overrides.
The adapter is a safety boundary. For model aliases, effort tiers, context budgets, request shaping, and provider-specific behavior, prefer config-driven and typed resolver paths over hard-coded facts.
The adapter HTTP listener serves three route families. They share the listener, the auth pipeline, and the error boundary, but each carries its own envelope shape and its own production status. Do not conflate route families.
| Route family | Paths | Status |
|---|---|---|
| OpenAI-compatible | /v1/chat/completions, /v1/responses, /v1/models, /v1/completions |
Production. Cursor BYOK and any OpenAI-SDK-compatible client. |
| Native Anthropic | /v1/messages, /v1/messages/count_tokens |
Code shipped, untested in production. Proposed for cross-provider use. |
| Health | /healthz, / |
Ops only. |
The MITM proxy is a separate surface, not an adapter route. It runs on its own per-client listeners under internal/mitm/, one loopback port per traffic type, acts as a forward proxy for arbitrary HTTPS targets, and is not subject to the adapter error boundary.
- Do not add new hard-coded model facts unless the task explicitly requires it and the follow-up toward config-driven behavior is documented.
- Preserve adapter-side preflight for known context-window overflows.
- Do not log raw prompts, request bodies, response bodies, tokens, credentials, cookies, API keys, or personal data unless an explicit local debugging policy enables sanitized or raw body logging.
- Reasoning round-trip is per-provider, configured under
[adapter.<provider>.reasoning]. - Cross-provider thinking replay must inject foreign provider reasoning as plain text instead of recreating native signed or encrypted thinking blocks.
Every adapter HTTP response with a non-2xx status MUST go through the adapter error boundary so the calling client receives a parsable, route-correct envelope with the chosen message preserved in error.message.
- Handlers return a typed adapter error from the generic adapter.
- Route-family renderers live in provider packages and own their envelope shape.
- Pre-headers errors and mid-stream errors both go through the boundary's typed entry points.
- Upstream failures classify into a typed upstream-code class and flow through the route-family-specific upstream-error mapper.
OpenAI-compatible route family rule:
- Every non-2xx upstream MUST be returned as HTTP 400 +
invalid_request_error+ a typedupstream_*code. - Do not preserve upstream 5xx or 429 status codes on the response.
- Do not map upstream rate-limits to OpenAI
rate_limit_error.
The empirical Cursor reason for this rule is in docs/cursor.md.
Use structured log/slog logging for production diagnostics. Prefer context-aware logging when a context.Context exists.
- Log meaningful lifecycle boundaries, external calls, state mutations, retries, fallbacks, completions, and failures.
- Include fields that make events queryable, such as
component,subcomponent,request_id,trace_id,span_id,parent_span_id,conversation_id,model,duration_ms,attempt,count,path,status, anderr. - Use explicit concern loggers from
internal/sloggerat subsystem boundaries when possible. - Propagate the correlation context through HTTP, gRPC, daemon jobs, provider requests, MCP handlers, and CLI command contexts.
- Do not invent unrelated trace ids in lower-level helpers when a caller context exists.
- Keep hot-path detail at
Debug, and keep healthy steady-state requests to a small number ofInfoevents. - Do not use
fmt.Print,fmt.Println,fmt.Printf, or standard-librarylog.Printfor operational logging.fmt.Fprint*is acceptable for intentional user-facing command output.
Default log paths are under $XDG_STATE_HOME/clyde; when XDG_STATE_HOME is unset, use ~/.local/state/clyde.
- Main daemon log:
clyde-daemon.jsonl. - Main CLI log:
clyde-cli.jsonl. - Concern logs:
logs/<concern-path>.jsonl. - Dedicated Codex sidecar log:
codex.jsonl, unlessCLYDE_CODEX_LOG_PATHoverrides it. - MITM capture store:
mitm/capture.db(SQLite; full decoded request/response bodies plus per-request metadata, one row per exchange with a side table for bodies; each row is tagged byclient(the listener id, oradapter.<provider>for in-code BYOK egress) and an autoclassifiedconcern). - MITM wire legs:
logs/providers/mitm/wire.jsonlvia theproviders.mitm.wireconcern; join to capture.db rows onrequest_id/trace_id. - macOS LaunchAgent stderr/stdout fallback:
daemon.log.
Useful concern roots include adapter.http, adapter.chat, adapter.models, adapter.providers, providers.claude, providers.codex, providers.cursor, providers.zed, providers.mitm, daemon.rpc, daemon.workers, process.daemon, and mcp.server.
For local adapter, MITM, test server, and example upstream addresses, use localhost wherever the consumer accepts hostnames. When a literal bind or URL host is required, use IPv6 loopback [::1].
Do not introduce 127.0.0.1, 0.0.0.0, wildcard binds, LAN addresses, or public listener defaults unless the user explicitly asks for an externally reachable service and the security implications are handled in the same change.
Store keys and tokens in environment variables or file references only. Reference sensitive data by variable name or file path in output, logs, tests, and docs.
No agent creates, rewrites, restructures, or regenerates documentation unless the user explicitly directs that specific change. This covers everything under docs/, this file, and any other Markdown doc. Docs here are durable and human-owned, so an unrequested rewrite is a regression even when it reads as an improvement. Correcting a typo or a broken link in a file you are already editing for a code change is fine; reshaping, condensing, expanding, or regenerating a doc is not, without an explicit instruction.
Keep AGENTS.md durable and concise.
- Prefer small files with one clear responsibility.
- Keep one entity or logical responsibility per file when practical.
- Name files for the responsibility they own.
- Prefer full domain names in identifiers over abbreviations.
- Wrap errors with operation context and the relevant identifier or backend.
- Add package doc comments, exported type comments, and comments for non-obvious exported fields.
- Do not add long JSON examples, full shell scripts, generated schemas, dated audit tables, local workstation facts, or historical incident notes.
- Move runbooks to
docs/, and point to them from this file only when agents need to know they exist. - Move task lists and stale audit findings to issues or dedicated planning docs.
- When behavior changes, update the code, tests, and closest specific documentation. Update this file only when the durable agent rule changes.