Other harnesses
This page covers assistant-adjacent tools that do not have their own page in this section: Codex, OpenCode, Oh My Pi, GitHub Copilot CLI, and tuicr. It stays at the configuration and rendering layer; Cross-harness subagents owns runtime discovery, review fan-out hierarchy, source paths, and design notes.
Use it to answer three questions: which repo source owns the deployed config, which wrapper runs before the native binary, and which runtime-owned fields are allowed to survive a merge.
Mental modelβ
| Area | Current rule |
|---|---|
| Codex and OpenCode | profile merging plus MCP injection |
| Codex launcher | interactive shells route codex through managed ~/bin/,codex |
| Copilot launcher wrappers | custom providers are BYOK environment variables only and wrapper commands exec ,copilot |
| Local provider adapters | per-wrapper loopback processes translate harness protocols while keeping upstream credentials local |
| Copilot MCP | generated as stdio type: "local", OAuth HTTP, or token-bridge stdio depending on the server block |
| Copilot memory | native SDK extension supplies context and worklog hooks |
| tuicr | single-sourced readonly review TUI config; labels are categories, not severity |
| secrets | runtime API keys come from pass, not committed tool config files |
Codex and OpenCodeβ
Config sourcesβ
| Tool | Config source |
|---|---|
| Codex | home/dot_codex/ |
| OpenCode | home/dot_config/opencode/ |
Codex and OpenCode use profile merging with MCP injection.
The interactive codex command routes through the managed ~/bin/,codex shim in interactive shells. The shim injects the local llama.cpp model catalog when needed and then falls through to the real Codex binary; hosted MCP auth needs no launch-time work because those servers run as ,mcp-token --bridge stdio bridges.
Codex profiles and approvalsβ
Codex policy settings are profile-specific.
| Profile | Policy |
|---|---|
| work interactive | managed-device requirements with approval_policy = "on-request", approvals_reviewer = "auto_review", and sandbox_mode = "workspace-write" |
| personal interactive | approval_policy = "never" with sandbox_mode = "danger-full-access" |
| read-only worker profiles | approval_policy = "untrusted" with sandbox_mode = "read-only" |
Repeated exact-command approvals can be captured by Codex execpolicy *.rules files under ~/.codex/rules/. Those rules should stay narrow because explicit allow rules also bypass sandboxing for the matched command prefix.
Repeated MCP tool approvals live as mcp_servers.<server>.tools.<tool>.approval_mode = "approve" in ~/.codex/config.toml. The Codex merge hook preserves those runtime-written approval overrides when it regenerates the managed MCP blocks.
scsi-main and scsi-local are generated with default_tools_approval_mode = "approve" so their read-analysis tools do not depend on the flaky MCP approval persistence path. Slack is only auto-approved for read/search tools (slack_read_* and slack_search_*), while send/create/update/schedule tools stay prompted/auto-reviewed.
Both interactive profiles pin service_tier = "default" so gpt-5.5 starts on standard routing instead of priority/legacy fast routing.
Codex reconciliationβ
Codex reconciliation rebuilds from the selected profile and generated MCP registry, then reattaches four explicitly runtime-owned buckets.
| Runtime-owned bucket | Rule |
|---|---|
| MCP approval overrides | valid values only |
[hooks.state.*].trusted_hash | reattached |
projects.*.trust_level | reattached when it is trusted or untrusted |
tui.model_availability_nux.* | counters in 0..4294967295 |
All unrelated live tables and invalid values are discarded. Matching source tables remain authoritative. Hook trust hashes are not baked into home/dot_codex/private_config.*.toml.
OpenCode Cloudflare providerβ
Personal OpenCode exposes Cloudflare.
| Provider | Env | Models |
|---|---|---|
cloudflare-workers-ai | CLOUDFLARE_WORKERS_AI_ACCOUNT_ID, CLOUDFLARE_WORKERS_AI_API_KEY | @cf/zai-org/glm-5.2, @cf/moonshotai/kimi-k2.7-code, minimax/m3 |
,opencode-cloudflare wraps that provider with Fish-completable model choices.
Provider wrappersβ
Codex Cloudflare wrapper:
| Wrapper | Target |
|---|---|
,codex-cloudflare | Cloudflare AI Gateway OpenAI Responses endpoint |
The wrapper uses the Gateway OpenAI provider with:
CLOUDFLARE_GATEWAY_ID(defaultdefault)CLOUDFLARE_API_TOKEN
It does not target Workers AI's @cf/... Chat Completions endpoint. Chat Completions wire mode is not a supported Codex target.
Other provider wrappers:
,codex-llama-cpp,codex-litellm,opencode-llama-cpp,claude-litellm,copilot-cloudflare,copilot-litellm,copilot-openrouter
Copilot custom providers are BYOK environment variables only, so its wrappers set those variables for OpenAI-compatible endpoints. The provider wrappers exec ,copilot rather than copilot to keep one stable entry point.
The three *-litellm wrappers use LITELLM_API_BASE / LITELLM_PROXY_KEY, falling back to the litellm/api/base and litellm/api/token pass entries when the environment is not already populated. ,claude-litellm removes a trailing /v1 because Claude's Anthropic SDK appends /v1/messages; it defaults Opus and subagents to llm-gateway/claude-opus-4-8[1m] and Sonnet to llm-gateway/claude-sonnet-5[1m]. The [1m] selector triggers Claude Code's BM(e) short-circuit that sets the model window to 1e6 for autocompact math; without it, llm-gateway/-prefixed ids miss the client registry and fall back to the ~200k default (autocompact fires at ~180k). Haiku 4.5 stays bare because the client registry has no recognized haiku[1m] selector. The wrapper also disables thinking, selects high effort, sets ANTHROPIC_AUTH_TOKEN, and clears ANTHROPIC_API_KEY. Each model or behavior default remains overridable through its corresponding environment variable. ,codex-litellm configures a per-invocation Responses provider and starts a loopback shim (~/lib/,codex-litellm/main.py), described next.
,codex-litellm launches Codex with a Codex-family client slug (gpt-5.2-codex, override CODEX_LITELLM_CLIENT_MODEL) and routes it through a loopback shim that rewrites /responses bodies before they reach the gateway. The client slug is deliberate: Codex only attaches its built-in tool set (exec_command, apply_patch, β¦) when the --model slug resolves to a Codex-family model, so launching with a gpt-5.6-* slug attaches zero tools and the model reports "no shell tool available". The shim rewrites the model field to the real gateway target, so the family slug never reaches the gateway. It also, on tool turns against the gpt-5.x azure_ai deployments (which bridge /responses down to chat-completions): sets drop_params: true when tool_choice is present; removes client_metadata; removes reasoning when tools are present; coerces type:"custom" tools (freeform apply_patch) into type:"function" tools taking a single input string; drops tool types the bridge rejects (namespace, tool_search, web_search); and reorders each function_call_output to immediately follow its function_call (Codex interleaves an assistant message between them, which detaches the tool output across the bridge). Each rewrite corresponds to a 400 the bridge would otherwise return.
Pick the upstream model with ,codex-litellm --target <tier|slug>: a bare tier (sol/terra/luna β gpt-5.6-<tier>), a bare id (gpt-5.2, gpt-5.2-codex, gpt-5.6-terra), or a full llm-gateway/... slug. It defaults to sol (override the default with CODEX_LITELLM_MODEL). gpt-5.5 is not offered because it has no /responses (or /chat/completions) deployment on the gateway. Because tool attachment is decided from the client slug before the shim runs, switching to a gpt-5.6-* entry in Codex's in-TUI /model picker silently drops tools β relaunch with --target instead of switching in the picker.
Because Codex's banner and /status print the client slug (gpt-5.2-codex), not the upstream, the wrapper names the provider after the target so provider: reveals it β e.g. provider: gateway-gpt-5-6-terra (dots become dashes because a -c model_providers.<id>.* key splits on the first dot). It also echoes one line before launch, e.g. ,codex-litellm β target llm-gateway/gpt-5.6-terra (client gpt-5.2-codex, effort high). The launch line scrolls off; the provider: line persists as the in-TUI signal of which model is actually answering.
gpt-5.2 and gpt-5.2-codex differ from the gpt-5.6-* deployments: Codex recognizes them as tool-capable client slugs directly, so the wrapper uses each as the client slug (the banner/picker show the true model) rather than the gpt-5.2-codex disguise it uses for gpt-5.6-*. The shim still rewrites the wire model to the fully-qualified llm-gateway/... id in every case β the gateway key's allowlist rejects a bare gpt-5.2 with 401 (surfacing in Codex as Reconnectingβ¦), while the client slug must stay bare for tool attachment. Per-target quirks the wrapper and shim handle: gpt-5.2-codex is a native Responses model that accepts xhigh effort (the wrapper defaults it to xhigh) but requires text.verbosity=medium and 400s on anything else (the shim forces medium for that target); gpt-5.2 is azure_ai-bridged and caps at high like the gpt-5.6-* tiers.
Reasoning effort defaults to high and is overridable with CODEX_LITELLM_EFFORT, a -c model_reasoning_effort="..." flag, or the in-TUI picker's effort suffix. The azure_ai deployments cap effort at high: they 400 on xhigh, max, and minimal, accepting only none/low/medium/high. The wrapper defaults to high so the picker/banner reflect that ceiling, and the shim clamps any xhigh/max down to high as a safety net (so selecting xhigh in the picker degrades to high rather than erroring). Note that on tool turns the shim removes reasoning entirely β the bridged chat-completions path rejects function tools and reasoning_effort together β so the effort setting affects only tool-free turns.
Select a LiteLLM model with ,copilot-litellm <llm-gateway/model> or ,copilot-litellm --model <llm-gateway/model>. The wrapper keeps the gateway slug as Copilot's wire model and maps known slugs such as llm-gateway/claude-opus-4-7 to Copilot's internal claude-opus-4.7 spelling for model metadata.
,copilot-litellm also starts a loopback shim (~/lib/,copilot-litellm/main.py) on an ephemeral 127.0.0.1 port and points COPILOT_PROVIDER_BASE_URL at it. The shim forwards every request to the LiteLLM gateway verbatim, except that on /chat/completions requests carrying tool_choice it merges tool_choice into allowed_openai_params before forwarding. Copilot's Background compaction sends tool_choice on every retry; without the shim, LiteLLM's Azure AI backend returns 400 UnsupportedParamsError and compaction loops until the session is unusable. The shim resolves this client-side; the wrapper's bash shell parents the shim and reaps it via trap EXIT when Copilot exits.
,copilot-openrouter routes anthropic/* and ~anthropic/* models through COPILOT_PROVIDER_TYPE=anthropic against https://openrouter.ai/api with no /v1 suffix. It does this instead of the default openai-type client against https://openrouter.ai/api/v1.
This matters because Copilot CLI's BYOK client only sets enableCacheControl: true and injects Anthropic cache_control breakpoints for COPILOT_PROVIDER_TYPE=anthropic. The default openai-type client always sends enableCacheControl: false, so prompt caching never activates for it regardless of what the upstream model supports.
Every other OpenRouter model, including GLM, DeepSeek, Qwen, Kimi, GPT, and Gemini, still uses the openai-type client. OpenRouter's public model-endpoints API currently reports supports_implicit_caching: false for all providers, so there is no caching benefit to gain there.
Setting COPILOT_PROVIDER_TYPE explicitly before invoking the wrapper always overrides this auto-detection.
Repo-owned Codex subscription adapterβ
,copilot-codex and ,claude-codex start one authenticated adapter on a random 127.0.0.1 port and stop it with the harness. The child receives only a random per-launch loopback token. The adapter reads the existing Codex ChatGPT OAuth state from ${CODEX_HOME:-~/.codex}/auth.json; it never copies the upstream access token into the Copilot or Claude environment.
| Wrapper | Harness protocol | Codex backend adaptation |
|---|---|---|
,copilot-codex | OpenAI Responses | forces the backend's required SSE mode, relays streaming callers, and assembles response.output_item.done for JSON callers |
,claude-codex | Anthropic Messages | translates messages, images, tools/tool results, structured output, usage, and streaming events to and from OpenAI Responses |
The adapter sends requests to the same ChatGPT Codex Responses backend used by the installed Codex CLI. If the backend rejects the current bearer with 401, the adapter asks codex app-server to run account/read with refreshToken: true, reloads auth.json, and retries once. Other failures are not retried.
Both wrappers accept --model <id> / -m <id> and --effort <level> / --reasoning-effort <level>. An explicit wrapper value overrides the model or reasoning effort emitted by the harness. Without --model, the wrapper reads the top-level model from the active Codex config.toml; without --effort, it preserves the harness-generated effort. Put -- before an underlying Claude or Copilot flag with the same name.
Both wrappers read the selected model's max_context_window from ${CODEX_HOME:-~/.codex}/models_cache.json, falling back to context_window. For GPT-5.4, GPT-5.5, and GPT-5.6 family IDs, Copilot receives a 128000 output allowance and the remaining context through COPILOT_PROVIDER_MAX_PROMPT_TOKENS, so its combined budget equals the Codex context limit. Other model IDs receive the Codex context as their prompt limit and keep Copilot's native output default. Claude Code receives the context as the auto-compact window and adds Claude's [1m] frontend capability marker when the window exceeds Claude's default 200k; the adapter still sends the original, unsuffixed model ID to Codex. If the model metadata is unavailable, each harness keeps its native default instead of guessing a larger window.
Claude token counting is a local byte-based estimate because the Codex backend does not expose an Anthropic token-count endpoint. Claude's required max_tokens field is not forwarded because the installed Codex request schema has no output-token-cap field. Opaque encrypted reasoning attached to a tool turn stays in bounded process memory only, keyed by the following tool call ID, so it can be restored on the tool-result turn without writing prompts or credentials to disk.
Repo-owned Copilot subscription adapterβ
,claude-copilot and ,codex-copilot start one authenticated adapter on a random 127.0.0.1 port and stop it with the harness. The adapter resolves the active GitHub credential with gh auth token, while the child receives only a random per-launch loopback token.
The adapter asks the installed Copilot CLI SDK for its entitlement-filtered live model catalog before launch and rejects unavailable models, models without a supported completion endpoint, unsupported effort values, or unsupported context tiers. Every Messages-, Responses-, or Chat-Completions-capable catalog model is selectable from either harness. ,claude-copilot defaults to claude-sonnet-5; ,codex-copilot defaults to gpt-5.3-codex with medium effort. Both accept --model / -m, --effort / --reasoning-effort, and --context default|long_context, with -- separating colliding native harness flags.
Native Claude Code Messages traffic and native Codex Responses traffic remain pass-through. For a cross-protocol selection, the loopback reuses the repo's tested subscription translation modules: Messages β Responses for Claude/GPT crossings, and either native harness protocol β Chat Completions for models such as Gemini. Tool calls, tool results, streaming terminal events, effort, and provider-owned opaque tool context are translated with the conversation. Opaque context remains in bounded process memory for the lifetime of the adapter; the GitHub bearer remains confined to that process.
Without --context, both wrappers use Copilot's default tier. When the selected model advertises long_context, --context long_context uses the tier's catalogued prompt allowance plus the model's output allowance, capped by its maximum context window. Models without that tier fail before the child starts. This matches Copilot CLI's --context contract.
Claude's selected Copilot context window becomes CLAUDE_CODE_AUTO_COMPACT_WINDOW. Windows above Claude's default 200k also add Claude's [1m] frontend capability marker; the loopback strips that marker before sending the exact catalog model ID to Copilot. The loopback removes only Copilot-incompatible Anthropic beta values, currently Claude Code's advisor-tool-2026-03-01, while preserving the rest of a native Messages request. Codex receives a provider-owned /v1/models projection of the same live catalog so every translated model retains its prompt limit, reasoning levels, shell, and freeform patch tool metadata instead of falling back.
The GitHub bearer is added only on the adapter's upstream request together with Copilot's copilot-developer-cli integration ID. A 401 refreshes the value from gh auth token and retries once; other failures pass through without retry. Interactive interrupts propagate to the native harness, while adapter shutdown suppresses follow-up SIGINT delivery so cleanup does not emit a Python traceback.
Repo-owned Vertex adapterβ
,codex-vertex, ,copilot-vertex, and ,claude-vertex start one authenticated adapter on a random 127.0.0.1 port and stop it with the harness. The adapter uses the configured Google Cloud project and refreshes gcloud auth print-access-token credentials behind the local protocol boundary; no Google bearer or project credential is written to generated config.
The three local frontends map to the two Vertex transports:
| Wrapper | Local protocol | Vertex transport |
|---|---|---|
,codex-vertex | OpenAI Responses | Gemini OpenAI Chat Completions or Claude publisher rawPredict/streamRawPredict |
,copilot-vertex | OpenAI Chat Completions | Gemini OpenAI Chat Completions or Claude publisher rawPredict/streamRawPredict |
,claude-vertex | Anthropic Messages | Gemini OpenAI Chat Completions or Claude publisher rawPredict/streamRawPredict |
The canonical provider_models entries in home/.chezmoidata/ai_models.yaml own the four allowed IDs, backend wire IDs, token limits, and effort matrix. home/dot_config/vertex-adapter/readonly_models.json.tmpl renders that data for the deployed core in ~/lib/,vertex-adapter/. For Codex, the launcher also renders an owner-only, per-session model_catalog_json from the same registry and removes it on exit; the loopback /v1/models route exposes the equivalent Codex schema so these non-OpenAI IDs use their declared context, effort, shell, and freeform apply_patch metadata instead of fallback metadata.
All wrappers use gemini-3.6-flash unless --model/-m selects gemini-3.1-pro-preview, claude-opus-4-6, or claude-opus-4-7. --thinking enables the model's declared default, --effort selects a supported level, and --no-thinking is accepted only for the Claude models. Gemini 3.6 Flash's closest low-reasoning mode is --effort minimal; Gemini 3.1 Pro cannot disable thinking.
Streaming text and parallel function/custom tools are translated incrementally. Gemini tool-call thought signatures and Claude signed thinking blocks that cannot cross another protocol directly are stored by call ID in owner-only runtime state under ${XDG_STATE_HOME:-~/.local/state}/vertex-adapter/; no user prompts, credentials, or general conversation transcript are added to that store.
Cursor is intentionally absent: Cursor Agent has no custom model-provider/base-URL route, so a ,cursor-vertex command would not make Cursor use Vertex. Native vlaude also remains unchanged for direct Claude Code β Vertex Claude use.
Claude Code and Cloudflareβ
Claude Code is not wired to Cloudflare. The Codex subscription adapter above is provider-specific and does not create a general Claude-to-Cloudflare path, so there is still no ,claude-cloudflare wrapper.
Codex hosted MCP token bridgesβ
inject_mcp_into_codex_toml.py emits slack and scsi-main as ,mcp-token <source> --bridge --url <url> command servers, not inline secrets or env-var contracts.
The bridge injects a freshly selected bearer per request, rotating through cursor's refresh grant behind the seam, so a Codex session outlives any single token.
Oh My Piβ
| Surface | Source | Target |
|---|---|---|
| Agent config | home/dot_omp/private_agent/config.yml.tmpl | ~/.omp/agent/config.yml |
| MCP servers | mcp_servers.yaml via generate_mcp_configs.py omp | ~/.omp/agent/mcp.json |
| Shared skills | symlink_skills β ~/.agents/skills | ~/.omp/agent/skills |
| Runtime hooks | extensions/ | ~/.omp/agent/extensions/ |
Managed configurationβ
config.yml.tmpl is the complete declarative OMP contract. Its isWork branch keeps work on GitHub Copilot and makes the personal profile use OMP's native Codex subscription provider. omp config list --json reports the effective typed settings; inspect all model-role pins together with omp config get modelRoles, not with dotted child keys.
| Setting | Work value | Personal value |
|---|---|---|
modelRoles.default | github-copilot/gpt-5.3-codex:high | openai-codex/gpt-5.5:medium |
modelRoles.smol | github-copilot/claude-sonnet-5:medium | openai-codex/gpt-5.6-terra:medium |
modelRoles.slow, modelRoles.plan, modelRoles.advisor | github-copilot/gpt-5.5:medium | openai-codex/gpt-5.5:medium |
modelProviderOrder | github-copilot, openrouter, anthropic, openai | openai-codex, then the work providers |
advisor.enabled, advisor.subagents, advisor.syncBacklog, advisor.immuneTurns | true, true, 1, 0 | true, true, 1, 0 |
defaultThinkingLevel | medium | medium |
memory.backend | off | off |
autolearn.enabled, autolearn.autoContinue | false, false | false, false |
dev.autoqaConsent | granted | granted |
skills.enabled, skills.enableSkillCommands | true, true | true, true |
task.isolation.mode, task.enableEffort, task.enableLsp, task.maxRecursionDepth | auto, true, true, 2 | auto, true, true, 2 |
retry.enabled, retry.maxRetries | true, 5 | true, 5 |
symbolPreset, theme.dark, setupVersion | nerd, titanium, 1 | nerd, titanium, 1 |
These native defaults are distinct from the explicit model_tier_map.omp selections used by repo-managed custom agent profiles.
The OMP advisor is enabled for primary turns and spawned agents. It uses the configured modelRoles.advisor model to inject actionable notes as <advisory> context. syncBacklog: 1 pauses the primary for up to 30 seconds at every pending advisor review, waiting for the backlog to clear before continuing. immuneTurns: 0 keeps every later concern or blocker eligible for steering instead of downgrading it to a non-interrupting aside. These settings make advisor recommendations reach the primary promptly; OMP still marks them as guidance="weigh, don't blindly obey", so it does not mechanically enforce compliance.
AutoQA consent is source-managed as granted, so OMP records and uploads concise xd://report_issue tool-grievance reports without prompting again.
OMP receives a native mcp.json generated from the shared registry. scsi-main and slack run as ,mcp-token --bridge stdio servers, so fresh cursor-minted bearer tokens are injected per request; scsi-local remains a direct stdio server.
GitHub Copilot CLIβ
Source and installβ
| Surface | Path |
|---|---|
| Source | home/private_dot_copilot/ |
| Target | ~/.copilot/ |
| Install | Homebrew cask copilot-cli, binary copilot |
| Brewfile | brews/shared/39-applications-casks.brewfile |
The cask auto-generates fish/zsh/bash completions for the native binary. Comma wrappers track their Fish completions under home/dot_config/fish/completions/.
Copilot uses shared SOP, skills, session context, and worklog hooks. The Copilot-specific SDK adapter lives under home/private_dot_copilot/.
Rendered surfacesβ
| Surface | Source | Target |
|---|---|---|
| SOP / instructions | symlink_copilot-instructions.md β ~/AGENTS.md | ~/.copilot/copilot-instructions.md |
| Skills | symlink_skills β ~/.agents/skills | ~/.copilot/skills |
| Custom agents | exact_agents/ | ~/.copilot/agents/ |
| MCP servers | mcp_servers.yaml via generate_mcp_configs.py copilot | ~/.copilot/mcp-config.json |
| Agent-memory extension | exact_extensions/exact_agent-memory/readonly_extension.mjs | ~/.copilot/extensions/agent-memory/extension.mjs |
| Settings | settings.json | ~/.copilot/settings.json |
Instructions, skills, and agentsβ
Instructions and skills are symlinks, not copies. Copilot reads $HOME/.copilot/copilot-instructions.md as its global SOP and ~/.copilot/skills/<name>/SKILL.md for skills.
The explicit ~/.copilot/skills symlink is required, and the Copilot path does not depend on ~/.claude/ agents or skills.
The managed custom agents are thin .agent.md profiles that point back to the shared review skill. settings.json owns the exact agent roster plus effort/context policy, while profile frontmatter owns registry-rendered model selection.
Internal worker profiles are model-invocable but not user-invocable. disable-model-invocation: false keeps them available to session.tasks.startAgent, while user-invocable: false keeps them out of direct /agent selection.
Copilot MCP modesβ
The copilot transform in generate_mcp_configs.py emits three MCP server shapes.
| Server shape | Generated form |
|---|---|
| stdio servers | type: "local" |
| OAuth HTTP servers | type: "http" with oauthClientId + auth.redirectPort + oauthScopes |
| token-bridge HTTP servers | type: "local" running ,mcp-token <source> --bridge --url <url> when the copilot block carries tokenBridge |
Because Copilot cannot run the SCSI/Slack browser OAuth flows itself, both scsi-main and slack carry a copilot tokenBridge source and reach the hosted endpoint through a local stdio bridge that injects a bearer token minted by cursor-cli per request. scsi-local has no OAuth, so it is emitted as a type: "local" stdio server with local pass Elasticsearch credentials. Copilot's generated mcp-config.json therefore carries scsi-main, scsi-local, and slack plus its built-in servers; the built-in github-mcp-server is Copilot-provided and not emitted.
The rendered config carries no Authorization values, so chezmoi apply owns it entirely. ,copilot passes through to the real binary except for bare --resume, which selects a local session before launching --session-id=<id> to avoid Copilot 1.0.73's MCP startup race. The full OAuth-exception rationale, synchronous rotation grant, and opaque-token liveness probe are owned by MCP servers. The bearer-free ~/.copilot/mcp-config.json is written 0600 under a 0700 ~/.copilot/ directory.
Copilot settings reconciliationβ
Copilot owns ~/.copilot/settings.json and rewrites it at runtime, including chosen model, allowedUrls, and config.json migration.
The typed reconciler recursively preserves live keys absent from the baseline and lets declared values win. It replaces subagents.agents exactly with the seven declared agents, which removes stale agent names and persisted per-agent model/effort/context overrides while preserving unrelated runtime preferences.
The target is in .chezmoiignore.
Copilot agent memoryβ
A live probe of Copilot 1.0.68 showed that its JSON command hooks run from ~/.copilot/hooks/*.json, but their SessionStart stdout is not ingested as context.
The active context path is the agent-memory extension. It registers onSessionStart, onPostToolUse, and onPostToolUseFailure, translates Copilot's camelCase SDK payloads to the shared snake_case script contract, and returns SDK additionalContext.
The command-hook file and its legacy checksum row are cleaned up by the apply hook. Copilot has no shell-gate hooks; PR review anchor verification is instruction-owned by the review/GitHub skills.
tuicr (review TUI)β
tuicr is a terminal UI for code review, not an LLM harness. Its config is single-sourced and read-only.
| Surface | Path | Target |
|---|---|---|
| Config | home/dot_config/tuicr/readonly_config.toml | ~/.config/tuicr/config.toml |
The config defines the review comment types (issue, suggestion, question, nit, praise) that tuicr exports as [LABEL] prefixes in the markdown an agent consumes.
These are actionable categories, not severity. Severity (CRITICAL/HIGH/MEDIUM/LOW) stays internal per the ~/AGENTS.md review SOP and is intentionally not encoded here, so tuicr labels and the review skill's severity model do not collide.
Secretsβ
Some API keys are loaded into the shell from pass in home/dot_config/fish/readonly_config.fish.tmpl. That means your password-store is part of the runtime wiring for AI tools.
echo "${OPENAI_API_KEY:+set}"
echo "${ANTHROPIC_API_KEY:+set}"
echo "${GEMINI_API_KEY:+set}"
Do not commit literal secrets into tool config files; keep them in pass and load at runtime. See Security and secrets.