Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Showing 1 changed file with 201 additions and 3 deletions.
| @@ -55,7 +55,7 @@ would break your Claude Code login. | ||
| 55 | 55 | Useful flags: `-model`, `-effort low|medium|high|xhigh|max`, `-max-turns`, |
| 56 | 56 | `-resume latest`, `-no-stream`, `-no-cache`, `-no-auto-compact`, `-v`, `-yolo`, |
| 57 | 57 | `-no-tui`, `-no-skills`, `-no-agents`, `-no-web`, `-no-hooks`, |
| 58 | - `-no-diagnostics`, `-no-mcp`, `-no-claude-code-auth`. | |
| 58 | + `-no-diagnostics`, `-no-mcp`, `-no-claude-code-auth`, `-trust-project`. | |
| 59 | 59 | |
| 60 | 60 | ### Flags and thresholds |
| 61 | 61 | |
| @@ -884,6 +884,7 @@ rather than the project's. | ||
| 884 | 884 | | Path | What | |
| 885 | 885 | | --- | --- | |
| 886 | 886 | | `~/.config/codey/config.json` | the answers, written by the wizard and `/settings`; [hooks](#hooks) go here by hand | |
| 887 | + | `~/.config/codey/trust.json` | which checkouts' [Claude Code files](#trusting-a-project) you trusted, and which version of them | | |
| 887 | 888 | | `<repo>/.codey/sessions/` | saved conversations, which is what `-resume` and `/resume` read | |
| 888 | 889 | | `<repo>/.codey/events.jsonl` | the event log METRICS.md is built from | |
| 889 | 890 | | `<repo>/.codey/history` | what has been typed at the prompt here, which `↑` walks back through | |
| @@ -942,6 +943,114 @@ The whole thing is `↑↓` and `enter`, `esc` to go back. Animations collapse t | ||
| 942 | 943 | their final frame under `NO_COLOR`, a `dumb` terminal, or a window under 60×20, |
| 943 | 944 | and any key skips the one playing. |
| 944 | 945 | |
| 946 | + ### Claude Code's settings | |
| 947 | + | |
| 948 | + If you already use Claude Code, a project set up for it should not need setting | |
| 949 | + up a second time. Codey reads three kinds of thing from Claude Code's files — | |
| 950 | + [its MCP servers](#claude-codes-servers), its permission rules, and its | |
| 951 | + CLAUDE.md files — and none of it is copied anywhere or written back: change it | |
| 952 | + in Claude Code and the next start here sees the change. | |
| 953 | + | |
| 954 | + **Permissions.** The `permissions` block of three settings files, in the order | |
| 955 | + Claude Code reads them: | |
| 956 | + | |
| 957 | + | File | Whose | `deny` / `ask` | `allow` | `defaultMode` | | |
| 958 | + | --- | --- | --- | --- | --- | | |
| 959 | + | `~/.claude/settings.json` | yours, every project | honoured | honoured | `acceptEdits` only | | |
| 960 | + | `.claude/settings.json` | the repository's, committed | honoured | only once [trusted](#trusting-a-project) | ignored | | |
| 961 | + | `.claude/settings.local.json` | yours, this project, git-ignored | honoured | honoured | `acceptEdits` only | | |
| 962 | + | |
| 963 | + The rule underneath that table is who wrote the file. A deny or an ask rule can | |
| 964 | + only make the agent do less or ask more, and nobody comes to harm from that, so | |
| 965 | + a repository may add as many as it likes. An allow rule is the opposite: it is | |
| 966 | + a decision that something runs without you being asked, and a decision written | |
| 967 | + into a repository was made by whoever wrote the repository. The shared file's | |
| 968 | + allow rules therefore wait for the same trust question as its `.mcp.json`, and | |
| 969 | + until you answer it they are listed and ignored. A `settings.local.json` that | |
| 970 | + git turns out to be tracking arrived with the clone, whatever its name says, | |
| 971 | + and is treated as shared. | |
| 972 | + | |
| 973 | + The rules act before anything else in the approval policy. A deny holds in | |
| 974 | + every mode, `-yolo` included — that is what writing it down is for — and it | |
| 975 | + holds for the read-only tools too, since `Read(.env)` is a rule about reading. | |
| 976 | + An ask rule puts the call to you even when the mode would have let it through, | |
| 977 | + and even when you said "always this tool" earlier in the session. An allow rule | |
| 978 | + lets a call through without a prompt. Between them deny beats ask beats allow, | |
| 979 | + as in Claude Code, so no allow rule anywhere argues a call past a deny. The | |
| 980 | + denial the model reads names the rule and the file, so it can tell you which | |
| 981 | + line to change instead of retrying. `/allowed` lists every rule under the file | |
| 982 | + it came from, and says which are in force and why the others are not. | |
| 983 | + | |
| 984 | + Claude Code's names map onto Codey's: `Bash` is `bash`, `Read` is `read_file` | |
| 985 | + (and, as best it can, `glob` and `grep`), `Edit` is both `edit_file` and | |
| 986 | + `write_file`, `MultiEdit` is `edit_file`, `Write` is `write_file`, `Glob`, | |
| 987 | + `Grep`, `WebFetch`, `WebSearch` and `TodoWrite` are the obvious ones, and | |
| 988 | + `mcp__server` and `mcp__server__tool` are unchanged. A rule naming a tool Codey | |
| 989 | + does not have, or written in a form it does not understand, is reported at | |
| 990 | + startup and takes no part. Paths follow Claude Code's conventions: `//path` is | |
| 991 | + absolute, `~/path` is under your home, `/path` is under the project root, and | |
| 992 | + anything else is relative to it, with a bare name such as `.env` matching at | |
| 993 | + any depth the way `.gitignore` would. Links are followed, so a rule about | |
| 994 | + `.env` also catches a symlink to it. | |
| 995 | + | |
| 996 | + `Bash(npm run test:*)` is matched one simple command at a time, using the same | |
| 997 | + parser as the approval prompt. An allow rule covers `npm run test && rm -rf ~` | |
| 998 | + only if something also allows `rm`; a deny rule catches the line if it catches | |
| 999 | + any part of it. A command the parser cannot see through — `$(...)`, backticks, | |
| 1000 | + `bash -c`, a variable in command position — never matches an allow rule, and | |
| 1001 | + where a deny rule cannot be ruled out for it, the call is asked about rather | |
| 1002 | + than run. So is anything that does more than its words say: `PATH=. git | |
| 1003 | + status`, or `echo hi > ~/.bashrc`, is not covered by an allow rule for `git` or | |
| 1004 | + `echo`. None of this makes shell allowlisting a security boundary — see | |
| 1005 | + [Safety](#safety) — but it does stop the commonest way a prefix rule says yes to | |
| 1006 | + something nobody wrote down. | |
| 1007 | + | |
| 1008 | + `defaultMode: "acceptEdits"` in a file of yours lets file edits through, as | |
| 1009 | + Claude Code's mode of that name does. `default` is the default. The other modes | |
| 1010 | + are ignored with a notice, from every file: `bypassPermissions` in particular, | |
| 1011 | + because in Codey the way to stop being asked is `-yolo`, typed on the command | |
| 1012 | + line for one run, and neither a file of yours nor a repository's gets to say it | |
| 1013 | + for you. | |
| 1014 | + | |
| 1015 | + **CLAUDE.md.** The files Claude Code loads into every conversation are loaded | |
| 1016 | + into this one's system prompt, at startup, in Claude Code's order: | |
| 1017 | + `~/.claude/CLAUDE.md`, then — from the top of the project down to the working | |
| 1018 | + root — each directory's `CLAUDE.md`, `.claude/CLAUDE.md` and `CLAUDE.local.md`. | |
| 1019 | + The top is the nearest directory with a `.git` in it, or your home directory if | |
| 1020 | + there is none on the way; a root that is in neither is a project of one | |
| 1021 | + directory, because walking up to `/` would read whatever happened to be lying | |
| 1022 | + in `/tmp`. Later files are more specific and the prompt says they win. | |
| 1023 | + `@path/to/file` imports are followed, relative to the file they appear in and up | |
| 1024 | + to five deep, skipping anything in a code block. A project's files may import | |
| 1025 | + only from the project, and that is checked where symlinks lead: a repository's | |
| 1026 | + CLAUDE.md that could import `@~/.aws/credentials` would be a repository reading | |
| 1027 | + your credentials into a conversation that is then sent to the model. Your own | |
| 1028 | + `~/.claude/CLAUDE.md` and `CLAUDE.local.md` may import from your home | |
| 1029 | + directory. The whole thing is read once per session, so the cached prompt | |
| 1030 | + prefix stays the same from request to request; restart to pick up an edit. | |
| 1031 | + `AGENTS.md` is not loaded this way, but the agent is still told to read one if | |
| 1032 | + the repository has it, as before, and a CLAUDE.md that says `@AGENTS.md` brings | |
| 1033 | + it in. | |
| 1034 | + | |
| 1035 | + **What is not carried over**, so nobody has to find out the hard way: | |
| 1036 | + | |
| 1037 | + - Hooks in Claude Code's format (`PreToolUse`, `PostToolUse` and the rest). | |
| 1038 | + Codey's [hooks](#hooks) are a different thing — they watch, they cannot | |
| 1039 | + veto — and a script written to block a tool call would silently block | |
| 1040 | + nothing here. | |
| 1041 | + - `env`, `model`, `apiKeyHelper`, `statusLine`, `outputStyle`, `cleanupPeriodDays` | |
| 1042 | + and the other non-permission settings. Codey has its own config for the ones | |
| 1043 | + it has an equivalent of. | |
| 1044 | + - `permissions.additionalDirectories`: the file tools keep the working root as | |
| 1045 | + their only root. | |
| 1046 | + - Claude Code's own record of which `.mcp.json` servers you approved | |
| 1047 | + (`enabledMcpjsonServers`, `enableAllProjectMcpServers`). It approves by name, | |
| 1048 | + so a server whose command changed under the same name would still count; | |
| 1049 | + Codey asks its own question about the exact definitions instead. | |
| 1050 | + - Managed (enterprise) settings, subdirectory CLAUDE.md files loaded on demand, | |
| 1051 | + and `.claude/rules/`. | |
| 1052 | + - Remote MCP servers, which Codey's client does not speak. | |
| 1053 | + | |
| 945 | 1054 | ## Tools |
| 946 | 1055 | |
| 947 | 1056 | | Tool | Approval | Notes | |
| @@ -1191,7 +1300,85 @@ Only tools are used: not resources, prompts or sampling, and no HTTP | ||
| 1191 | 1300 | transport. Pictures a tool returns are described to the model rather than |
| 1192 | 1301 | shown. A server's directory is fixed when it starts, so after `/worktree` it |
| 1193 | 1302 | is still serving the root the session began in. `-no-mcp` starts none of them |
| 1194 | - for one run. | |
| 1303 | + for one run — Codey's own and Claude Code's alike. | |
| 1304 | + | |
| 1305 | + ### Claude Code's servers | |
| 1306 | + | |
| 1307 | + The servers Claude Code already knows about are started as well, read from | |
| 1308 | + where Claude Code keeps them rather than copied into Codey's config: | |
| 1309 | + | |
| 1310 | + | Where | Whose | Started | | |
| 1311 | + | --- | --- | --- | | |
| 1312 | + | `~/.claude.json`, under `projects["<root>"].mcpServers` | yours, for this project — what `claude mcp add` writes | always | | |
| 1313 | + | `.mcp.json` at the working root | the repository's, committed | once you [trust it](#trusting-a-project) | | |
| 1314 | + | `~/.claude.json`, top-level `mcpServers` | yours, for every project — `claude mcp add -s user` | always | | |
| 1315 | + | |
| 1316 | + The shape is the one already shown above, `{"mcpServers": {name: {command, | |
| 1317 | + args, env}}}`, with `"type": "stdio"` accepted and meaning the same thing. A | |
| 1318 | + server with `"type": "http"` or `"sse"`, or with a `url`, is a remote server; | |
| 1319 | + Codey's client speaks only stdio, so it is reported at startup and skipped. | |
| 1320 | + `${VAR}` and `${VAR:-default}` are expanded in the command, the arguments and | |
| 1321 | + the env values, as Claude Code expands them — so a `.mcp.json` can say | |
| 1322 | + `"${DB_URL}"` and leave the secret in your environment. A variable that is | |
| 1323 | + unset and has no default skips that server with a notice naming it, rather | |
| 1324 | + than starting it with a hole in its command line. | |
| 1325 | + | |
| 1326 | + When two places define a server of the same name, Codey's own `mcp_servers` | |
| 1327 | + win, and after that the table is read top to bottom: your per-project servers, | |
| 1328 | + then the repository's, then your global ones. That is Claude Code's own order, | |
| 1329 | + so a name means the same program in both. Each clash is reported once, naming | |
| 1330 | + the one that won, because a server that quietly turned out to be a different | |
| 1331 | + program is not something anyone thinks to debug. | |
| 1332 | + | |
| 1333 | + #### Trusting a project | |
| 1334 | + | |
| 1335 | + A `.mcp.json` comes from the repository. Starting a server listed there means | |
| 1336 | + running a command — with your environment, your credentials, your network — | |
| 1337 | + that whoever wrote the repository chose, before the agent has done anything | |
| 1338 | + and outside every approval prompt. So, like Claude Code, Codey asks first. The | |
| 1339 | + first interactive start in a checkout whose `.mcp.json` or shared | |
| 1340 | + `.claude/settings.json` [allow rules](#claude-codes-settings) would grant | |
| 1341 | + something prints, before the TUI opens: | |
| 1342 | + | |
| 1343 | + ``` | |
| 1344 | + Claude Code's configuration for /home/you/src/thing would: | |
| 1345 | + | |
| 1346 | + start these MCP servers, from .mcp.json: | |
| 1347 | + db: npx -y @acme/db-mcp --url ${DB_URL} (env: DB_TOKEN) | |
| 1348 | + run these without asking, from .claude/settings.json: | |
| 1349 | + Bash(make:*) | |
| 1350 | + | |
| 1351 | + These files come from the repository, not from you: trusting them lets it | |
| 1352 | + choose commands this machine runs. Your answer is remembered until they change. | |
| 1353 | + Trust them for this checkout? [y/N] | |
| 1354 | + ``` | |
| 1355 | + | |
| 1356 | + One question covers both files, because it is one question: do you trust what | |
| 1357 | + this repository says about itself. The full command line is shown because that | |
| 1358 | + is the thing being agreed to. The answer — yes or no — is remembered for this | |
| 1359 | + checkout against a fingerprint of exactly those definitions, as written, before | |
| 1360 | + any `${VAR}` is expanded. A new server, a changed argument or one more allow | |
| 1361 | + rule is a different fingerprint, and the question comes back; nothing else | |
| 1362 | + does. A refusal is remembered too, so saying no once is not a question at every | |
| 1363 | + start. | |
| 1364 | + | |
| 1365 | + The record is kept in `~/.config/codey/trust.json`, not in the project's | |
| 1366 | + `.codey/` directory with the rest of the per-repository state. That is the | |
| 1367 | + point of it: anything inside the working tree can arrive with a clone, and a | |
| 1368 | + repository that shipped its own record of being trusted would be approving its | |
| 1369 | + own servers. The fingerprint would be no defence there — whoever wrote the | |
| 1370 | + `.mcp.json` can compute it. Only a file the repository cannot reach can say what | |
| 1371 | + you decided. | |
| 1372 | + | |
| 1373 | + Where nobody can be asked — `-p`, `codey agent stdio`, a piped stdin — an | |
| 1374 | + undecided project's servers are not started and its allow rules are ignored, | |
| 1375 | + with a line on stderr saying so. A checkout you have already trusted needs | |
| 1376 | + nothing more. `-trust-project` says yes for one run without asking and without | |
| 1377 | + recording anything, which is what a CI job against a repository you control | |
| 1378 | + wants. Servers in `~/.claude.json` never ask: you put them there, as you put | |
| 1379 | + Codey's own in its config. Once started, every server's tool calls go through | |
| 1380 | + the approval prompts like any other, unless an allow rule such as `mcp__db` | |
| 1381 | + says otherwise. | |
| 1195 | 1382 | |
| 1196 | 1383 | ## Hooks |
| 1197 | 1384 | |
| @@ -1441,6 +1628,14 @@ interpreter with `-c` cannot be analysed at all, so it is marked opaque and | ||
| 1441 | 1628 | allowlists for shell commands do not work; this exists to make the prompt |
| 1442 | 1629 | specific, not to make the decision for you. |
| 1443 | 1630 | |
| 1631 | + [Claude Code's permission rules](#claude-codes-settings) sit in front of all | |
| 1632 | + of that. Its deny rules hold in every mode, `-yolo` included, which makes them | |
| 1633 | + the one part of this layer you can lean on: a call they refuse does not run. | |
| 1634 | + Its allow rules are matched with the same parser, one simple command at a | |
| 1635 | + time, and an opaque command matches none of them — but the warning above | |
| 1636 | + applies to them with full force. `Bash(find:*)` allows `find . -exec rm {} +`, | |
| 1637 | + and no parser will tell you otherwise. | |
| 1638 | + | |
| 1444 | 1639 | Hooks sit outside all three layers, deliberately: a command in `"hooks"` runs |
| 1445 | 1640 | on every event it is attached to, without a prompt, because you put it in your |
| 1446 | 1641 | own config rather than the agent asking for it. The path jail is what keeps the |
| @@ -1449,7 +1644,9 @@ working root you would sensibly point Codey at — but the flip side is that | ||
| 1449 | 1644 | anything able to write that file has arranged to run commands here. `-no-hooks` |
| 1450 | 1645 | is the switch that says no for one run. MCP servers are the same bargain: each |
| 1451 | 1646 | is a program started with your environment when the session opens, and only |
| 1452 | - its tool calls go through the prompts. `-no-mcp` is theirs. | |
| 1647 | + its tool calls go through the prompts. `-no-mcp` is theirs. The one kind of | |
| 1648 | + server that does not come from a file you wrote — a repository's `.mcp.json` — | |
| 1649 | + waits for you to [trust it](#trusting-a-project) first. | |
| 1453 | 1650 | |
| 1454 | 1651 | **A sandbox.** This is the layer that actually contains a mistake, and the |
| 1455 | 1652 | Codey cannot provide it for you. The honest recipe on Linux: |
| @@ -1489,6 +1686,7 @@ internal/websearch the search command, the URL jail, HTML to text | ||
| 1489 | 1686 | internal/mcp MCP client: starts the configured servers, offers their tools |
| 1490 | 1687 | internal/config saved defaults: model, effort, approvals, web search, theme, diagnostics |
| 1491 | 1688 | internal/approval allow / deny / ask, with shell analysis |
| 1689 | + internal/ccsettings Claude Code's MCP servers, permission rules and CLAUDE.md, and who may grant what | |
| 1492 | 1690 | internal/ask questions the agent puts to you mid-task, and the tool for it |
| 1493 | 1691 | internal/hooks the lifecycle, published: fan-out, and the config's commands |
| 1494 | 1692 | internal/herdr reporting agent state to the herdr pane Codey is running in |