---
title: "Works with"
description: "「Works with」：yokoten 使用指南的一章。"
canonical: https://yokoten.app/zh-tw/docs/guide/works-with
lang: en
source: https://github.com/yokoten-labs/yokoten/blob/v0.1.2/docs/guide.md
ref: "v0.1.2"
---

> For AI agents: the documentation index is at https://yokoten.app/llms.txt. To set up yokoten in a project, follow https://yokoten.app/agents.md.

> 這一頁還沒有繁體中文版，下面是英文原文。

# Works with

yokoten guards nine AI coding agents with the same rules. This table is the one list of
them and how well each is covered. Each "yes" below is a real run: the agent tried
`git push --tags` in a throwaway repo, and the guard stopped it. "Not run end to end"
means the agent is listed, but its guard is not verified end to end yet. Docs are not
proof. The table is generated from the runtime contract in `src/agents/runtimes/runtime.ts`
and the dated runs. A test fails when the two differ.

| Agent | Hook file (`init` writes it) | How it blocks | Capabilities (rewrite · stop · trust · fails · role) | Verified | Caveats |
|---|---|---|---|---|---|
| Claude Code | `.claude/settings.json`: PreToolUse, Stop and SubagentStop | exit 2, the reason on stderr | yes · yes · yes · closed · yes | yes, 2026-10-19: headless, on DeepSeek `deepseek-flash` | none |
| GitHub Copilot CLI | the same `.claude/settings.json`: Copilot reads it | exit 2, and the reason as JSON on stdout | no · yes · yes · open · yes | yes, 2026-10-19: Copilot CLI 1.0.98, `copilot -p`, model `gpt-6-luna` (Auto) | Runs repo hooks only after you trust the folder: run `yokoten trust` there. Skips them without a word otherwise. A hook that times out lets the call go on |
| Antigravity CLI (`agy`) | `.agents/hooks.json`: the `yokoten` entry, PreToolUse | `{"decision": "deny"}` on stdout | no · no · no · closed · yes | yes, 2026-10-19: agy 1.4.2, `agy -p`, model `gemini-3.8-flash-high` | No stop check yet. Without `.yokoten/`, every call is refused: agy fails closed. The lead runs a worker's done-when check |
| Codex CLI | `.codex/hooks.json`: PreToolUse and Stop | exit 2, the reason on stderr | no · yes · yes · closed · yes | yes, 2026-10-19: Codex 0.163.0, `codex exec`, on DeepSeek `deepseek-flash` | Runs the hooks only after you trust the folder, and then both hooks: run `yokoten trust` there. `codex exec` skips an untrusted hook without a word |
| Muse Code | `.muse/hooks.json`: PreToolUse and Stop | exit 2, the reason on stderr | no · yes · yes · open · no | yes, 2026-10-19: Muse Code 1.4.10, `muse exec --trust-workspace`, model `muse-spark-1.3-contributor` | Runs the hooks only after you trust the folder: run `yokoten trust`. Muse passes no `YOKOTEN_ROLE` to a hook, so mark a worker's worktree with `yokoten worker mark <dir>`. It is not known to pass `YOKOTEN_RELEASE_APPROVAL` either, so release outside Muse. A hook that fails or times out lets the call go on |
| DeepSeek Harness (`dsh`) | `.dsh/hooks.json`: PreToolUse, and `.dsh/yokoten.patch.yml` to load it | exit 2, the reason on stderr | no · no · no · open · yes | yes, 2026-10-20: dsh 0.2.1-rc.2, `dsh headless --patch .dsh/yokoten.patch.yml`, default DeepSeek model | Runs hooks only through a bridge you add to each profile: see [DeepSeek Harness](https://yokoten.app/zh-tw/docs/guide/works-with.md#deepseek-harness). Without it, or without the patch, dsh runs no hooks and says nothing. Not guarded on Windows yet (#332). No stop check: the lead runs a worker's done-when check |
| Grok CLI (`grok`) | `.grok/hooks/yokoten.json`: PreToolUse and Stop | `permissionDecision: "deny"` on stdout, and exit 2 | yes · yes · yes · closed · yes | yes, 2026-10-20: Grok CLI 1.0.52, `grok -p --trust --always-approve`, default model | Runs project hooks only after you trust the folder: run `yokoten trust` there. Skips them without a word otherwise. Also runs the hooks in `.claude/settings.json`, so the guard may run twice for one call, on purpose |
| Gemini CLI | `.gemini/settings.json`: BeforeTool and AfterAgent | exit 2, the reason on stderr | no · yes · yes · closed · no | not run end to end: personal sign-in retired 2026-06-18; needs a paid API key or an org license | Google's agent for individuals is now Antigravity CLI |
| OpenCode | `.opencode/plugins/yokoten.ts`: OpenCode 2 permission `evaluate`, and OpenCode 1 `tool.execute.before` | OpenCode 2 sets `effect` to deny; OpenCode 1 throws the reason | no · no · no · closed · no | yes, 2026-10-23: OpenCode 2.1.4, `opencode-go/deepseek-v4.1-flash`, the plugin as `init` writes it; 2026-10-25: the guard stopped a command from `opencode-go/glm-5.3-flash` (GLM-5.3 Flash, by Z.ai) | No stop check. A guard that fails or times out refuses the call. So does a call that is not read-only, when no command or path can be read. A subagent call was not part of that run |
| Any other agent | none | the rules in `AGENTS.md`, and the commit check, which stops a bad commit whoever made it | — | | |

`install` writes the user hooks: the guide at the start of each session, and the command
guard. They go in `~/.claude/settings.json`, and in `~/.codex/hooks.json` and
`~/.gemini/settings.json` when those folders exist. Muse Code gets both, in
`~/.config/muse/settings.json` (`$XDG_CONFIG_HOME/muse/settings.json` when that is set),
when its config folder exists. Grok CLI gets only the command guard,
in `~/.grok/hooks/yokoten.json`, when `~/.grok` exists: it shows a session-start hook's
output to you, not to the agent. Antigravity CLI gets only the command guard, in
`~/.gemini/config/hooks.json`, when `~/.gemini/config` exists. Copilot CLI gets it in
`~/.copilot/hooks/yokoten.json` when `~/.copilot` exists. Neither has a stop check yet:
agy's Stop event has no re-entry signal, and Copilot lets a timed-out hook go on.
OpenCode gets the command guard in
`~/.config/opencode/plugins/yokoten.ts` when `~/.config/opencode` exists. `doctor` counts every folder as trusted when
Grok's trust gate is off: `GROK_FOLDER_TRUST=0`, or `[folder_trust] enabled = false` in
`~/.grok/config.toml`. In Codex, the guard checks the files
of an `apply_patch`, also one run through the shell.
When `CLAUDE_CONFIG_DIR`, `CODEX_HOME` or `XDG_CONFIG_HOME` is a full path, `install` writes in that folder instead, where the tool reads.

Each agent runs a project hook only after you trust it:

- **Claude Code** runs project hooks in a folder you trust.
- **Copilot CLI** runs them in a folder you trust. In `copilot -p`, set
  `COPILOT_ALLOW_ALL=true`: that exact value trusts the folder. `--allow-all-tools` alone
  does not, and then the hooks do not run.
- **Antigravity CLI** ran `.agents/hooks.json` in `agy -p`, in a folder not on its trust
  list. It runs the hook with the `.agents` folder as its working folder. Guides say
  `--dangerously-skip-permissions` skips the hooks. In agy 1.4.2 the hook still ran with it.
  Don't count on either.
- **Codex** runs project hooks only in a trusted project. Then approve each hook in
  `/hooks`, or run `yokoten trust` in the folder. Codex remembers the command, so a
  changed command must be approved again; `yokoten trust` writes its new hash.
- **Muse Code** runs `.muse/hooks.json` only in a trusted workspace, and skips it without a
  word otherwise. In `muse exec`, `--trust-workspace` trusts the folder for that run.
  It also reads the project's skills in `.agents/skills` only then. Outside a desk, Muse
  Code can read ~/.claude/CLAUDE.md and send that file to Muse's model. Run Muse in a desk
  to keep your home folder hidden. `doctor` says so when that file is there. When a desk
  can still see the file, `doctor` says the home folder is visible and the desk needs
  attention.
- **Grok CLI** runs `.grok/hooks/` only in a trusted folder, and skips it without a word
  otherwise. Run `yokoten trust` in the folder, or type `/hooks-trust` in a session there.
  `yokoten trust` writes Grok's own trust file, for this folder only. Grok also runs the hooks in `.claude/settings.json`,
  so a call may be checked twice. That is on purpose. Grok can skip its own hook: in a folder
  it does not trust, or when a repo commits an empty hook file. The Claude Code hook then
  still guards the call. The second check costs about 0.1 s.
- **Gemini CLI** runs project hooks only in a trusted folder. Start Gemini at the repo
  root: it reads `.gemini/settings.json` from the folder it starts in, and nowhere else.
  It warns once before it runs a new or changed hook.
- **OpenCode** loads the plugin in `.opencode/plugins/` when it starts. OpenCode 2 denies in the permission hook. OpenCode 1 still blocks on a throw. The lead ran this end to end on 2026-10-23, in a scratch repo with OpenCode 2.1.4 and `opencode-go/deepseek-v4.1-flash`; a subagent call was not part of that run.

`init` prints the one step that trusts the folder for each agent that has one. `doctor` then reads
each agent's own trust store, read-only, and notes a folder it does not trust yet:

| Agent | Trust store | Counts as trusted |
|---|---|---|
| Copilot CLI | `trustedFolders` in `~/.copilot/config.json` (or `$COPILOT_HOME`) | the folder, or a folder above it |
| Codex | `~/.codex/config.toml` (or `$CODEX_HOME`): `[projects]` and `[hooks.state]` | the repo folder exactly, and each hook as it is now |
| Muse Code | `~/.config/muse/trust.json` (or `$XDG_CONFIG_HOME/muse/trust.json`) | the repo folder exactly |
| Grok CLI | `~/.grok/trusted_folders.toml` (or `$GROK_HOME`): `[folders."<folder>"]` with `trusted = true` | the repo folder exactly |

Doctor checks an agent only when its home folder is there (`~/.copilot`, `~/.codex`, `~/.config/muse`, `~/.grok`).
When that folder has no store, or one doctor can't read, it says "can't check", never ok.

A worker is a session with `YOKOTEN_ROLE=worker`. In Codex, a subagent whose
`agent_type` names a worker is a worker too. Copilot CLI and Antigravity CLI name no
subagent role in a hook call, nor does DeepSeek Harness: `YOKOTEN_ROLE` decides. Gemini CLI's strict environment
sanitization removes `YOKOTEN_ROLE` from the hooks. Then yokoten can't tell a worker in
Gemini CLI: every session counts as the lead, and the lead-only guard does not stop a
worker's edit. Muse Code passes a hook only a short list of variables, and `YOKOTEN_ROLE` is not
one of them. Grok CLI passes `YOKOTEN_ROLE` to its hooks, and a subagent whose
`subagentType` names a worker is a worker too.

So the lead also marks each worker's worktree: `yokoten worker mark <dir>` (#555). The guard
then treats every agent in that worktree as a worker, in Gemini CLI and Muse Code too. The
mark is a file in the worktree's own git folder (`.git/worktrees/<name>/yokoten-worker`).
The guard finds it with no git process. The mark only adds the worker role: with the mark,
`YOKOTEN_ROLE=lead` is still a worker. The tool guard, the stop check, the commit check and
the session start all read it. It fails closed: a `.git` file the guard can't read (a mode it
can't open, a FIFO, no `gitdir:` line), or a git folder it names that is gone or can't be
read, makes the session a worker. A refusal that comes from the mark says so, and names the
main checkout, where the lead works.

The guard refuses a worker's plain ways to change the mark: a command that writes, moves,
removes, links or changes the mode of a file in a git folder, or redirects into one. A glob
counts when it can match `.git` or the mark (`chmod 000 .gi?`). So do git with `GIT_DIR` or
`--git-dir`, `git worktree` other than `list`, and an edit inside a git folder, by real path.
A read that names `.git` passes (`grep --exclude-dir=.git`, `find . -path ./.git -prune`).
This reads text, like the rest of the guard, so a worker that builds a command in pieces
gets past it. The real lock is the server and the credentials: branch protection and code
owners, and a worker that holds no push or bot token.

A worker commits and reports; the lead pushes and posts after review. For a worker the
guard refuses `git push`, `git send-pack`, `git subtree push`, a git alias (`git config
alias.*`, `git -c alias.*`), `yokoten bot` in any form, `gh pr`
`create|merge|edit|close|comment|review|ready|reopen`, and `gh api` with a write method or
fields (a GraphQL query that reads passes). The main checkout is never marked, so the lead
there is not affected. In a marked worktree the lead counts as a worker too. `git worktree
remove <dir>` removes the mark with the worktree. Without a mark, an agent that drops
`YOKOTEN_ROLE` counts as the lead.

Cancelling or deleting an Actions run is refused for the lead and for a worker.
The guard stops `gh run cancel`, `gh run delete`, and the same call through `gh api`
or `yokoten bot`. `gh run rerun`, `view`, `list` and `watch` still pass. Ask the
owner, who can cancel or delete the run on the Actions page.
A `repositories/<id>` path is the same call. A path with `.` or `..` is read after those are resolved.
The route words match in any case, so `Actions` and `Cancel` are the same call.
A cluster of short options in `gh api` is refused, unless it is one option with its value joined.
A method or a path taken from the shell is refused when it can name a run.
A `gh run` subcommand taken from the shell is refused.
`gh` must name one of its own commands. `gh extension install`, `upgrade`, `exec` and `browse` are refused.
`exec` runs an installed extension. `browse` can install one.
A non-GET call is refused when the path after `repos/` or `repositories/` is only a variable.
`gh config set aliases` is refused. `gh alias list` and `gh extension list` still pass.
`gh alias set` and `gh alias import` are refused for both roles. Ask the owner to add an alias by hand.

No agent, lead or worker, gets gh's token (#659, #668). With it, `curl` or a script could
call GitHub directly, around every rule above. gh and `yokoten bot` use the token without
showing it. So for every agent the guard refuses:

- gh's commands that print it: `gh auth token`, `gh auth status --show-token` (`-t`),
  `gh auth git-credential` and `gh config get oauth_token`.
- The places a login is kept: `git credential fill`, a credential helper's `get`
  (`git credential-manager get`), `git -c credential.…`, `~/.git-credentials`, gh's
  `hosts.yml`, `security find-…-password -w` and `secret-tool lookup`.
- The token's variables: `$GH_TOKEN`, `$GITHUB_TOKEN`, `$GH_ENTERPRISE_TOKEN` and
  `$GITHUB_ENTERPRISE_TOKEN`, also as `$env:GH_TOKEN` (PowerShell) and `%GH_TOKEN%` (cmd).
  So are `node`, `python`, `jq` and other programs that run code when the command names one.
  To write the name in a post, use a body file; to search for it, leave out the `$`.
- `GIT_TRACE_REDACT=0`, which makes a git trace print the token.
- For the bot's git, `-c` and git settings in the environment (`GIT_CONFIG_*`). The bot also
  drops the pager, editor, browser, ssh and askpass programs and the trace and debug
  settings from its environment, and pages through `cat`.

`gh auth status`, `gh pr view` and `curl https://example.com` still pass.

`gh workflow disable` is refused for every agent too: a workflow that is off runs no
checks on any pull request. Only the owner turns one off.

The guard stops a worker's shell writes to lead-only files as well as its edits (#402). It
reads redirects (`>`, `>>`, `>|`, `&>`), `sed -i`, `perl -i`, `tee`, `cp`, `mv`,
`install`, `ln`, `dd of=`, `truncate`, `rm`, and git's `checkout`, `restore`, `rm`, `mv`
and `apply`. A redirect counts with its file in quotes or glued to the word before it
(`echo x>"AGENTS.md"`). For `git apply`, the guard reads the patch to see its files, only
from a plain file under 1 MB; any other patch file stops a worker. A `git commit` that
would commit a lead-only file is stopped too: the commit check knows the role only from
`YOKOTEN_ROLE`, and a Claude Code subagent such as `yokoten:worker` has none. Reads such
as `cat` and `grep` pass. As for edits, this holds only in a repo that uses yokoten (one
with a `yokoten.json`). A path under `.worktrees/<issue>/` reads from that worktree, as
if the session ran there: `.worktrees/571-x/AGENTS.md` is still lead-only (#571).
When the brief asks for a change to a lead-only file, the lead
starts the worker with `YOKOTEN_LEAD_ONLY_OK=AGENTS.md,.github/workflows/ci.yml`: paths
from the repo root, comma-separated, an entry with `*` read as a glob. `*` alone grants
every lead-only file. The guard and the commit check let those paths through. A subagent
shares the lead's environment, so give such a task to a separate session. At session
start, `doctor` tells a session with `YOKOTEN_ROLE=worker` "this session is a worker:
lead-only files are guarded". A separate worker session that does not hear it has no role
set. A subagent starts no session, so it hears nothing.

## Sandboxes

`init` writes each agent's sandbox for the project. `doctor` checks it in the
`sandbox` step. Start the worker in its worktree, so the sandbox covers it.

| Agent | What `init` writes | Why |
|---|---|---|
| Claude Code | `.claude/settings.json`: `sandbox.enabled` is `true` | Shell commands stay in the worktree. New network hosts ask first |
| Codex | `.codex/config.toml`: `sandbox_mode` is `workspace-write`, `approval_policy` is `on-request`, `network_access` is `false` | Writes stay in the worktree. Network stays off. Codex asks before it goes beyond |
| Gemini | `.gemini/settings.json`: `security.disableYoloMode` is `true` | YOLO mode, which approves everything, stays off |
| Grok | `.grok/sandbox.toml`: `[profiles.yokoten]` builds on `workspace` | The `yokoten` profile keeps writes in the worktree. Workers run with `grok --sandbox yokoten` |

A value you set stays. `doctor` names what differs, with one fix.

Muse Code, DeepSeek Harness, Antigravity and Copilot have no project-writable sandbox, so `init` writes none for them. Muse has no project settings file for one. DeepSeek Harness and Antigravity keep only hooks in the project, and Copilot's own sandbox is user-level. OpenCode gets a plugin, not a sandbox file.

## DeepSeek Harness

DeepSeek Harness (`dsh`) runs Claude Code command hooks through a bridge plugin. The
bridge is not in the dsh package. `init` writes the hook and a launch patch, and prints
the rest. It installs nothing, and it leaves `~/.dsh` alone.

1. Add the bridge and its peer to each profile you use (`tui`, `headless`, `web`).
   `dsh plugin add` needs `pnpm` on your PATH. Without the peer, the bridge loads no hooks.

   ```sh
   dsh plugin --profile tui add @deepseek-ai/dsh-hooks-claude-code@0.2.1-rc.2
   dsh plugin --profile tui add @deepseek-ai/dsh-hook-protocol@0.2.1-rc.2
   ```

2. Start dsh at the repo root, with the patch right after the profile name:

   ```sh
   dsh tui --patch .dsh/yokoten.patch.yml
   dsh headless --patch .dsh/yokoten.patch.yml --json "<task>"
   ```

   The patch names `.dsh/hooks.json` by a relative path, which dsh reads from the folder
   it starts in. Started anywhere else, or without the patch, dsh runs no hooks.
   yokoten owns `.dsh/yokoten.patch.yml`, and `init` rewrites it. Put overlays of your own
   in another file.

dsh matches tool names exactly, in lowercase: the hook's matcher is `bash|pwsh|edit|write`.
`pwsh` is dsh's shell on Windows. It is in the matcher for later, but Windows is not
guarded yet. There dsh runs the hook itself in PowerShell, which can't run yokoten's
POSIX shell command. The bridge takes that failure as "go ahead", so every call goes
through. On Windows `doctor` says so, and `init` does too (#332).
There is no stop check: the bridge's Stop event has no loop guard. A worker on DeepSeek
Harness can report a task as done with failing checks. The lead runs the done-when
command (or the project's checks when the brief has none). Antigravity CLI has no stop
check either; the lead runs the check there too. `doctor` checks both files, and looks in
`${DSH_HOME:-~/.dsh}/profiles` for the two packages. Without a dsh home it says it can't
check. The bridge fails open without a word, so files in place prove nothing. Only a real
deny proves the guard runs: ask dsh to run `git push --tags` in a throwaway repo, and see
it refused.

## Claude Code or Codex on DeepSeek

The guard sits in the agent, not in the model. So Claude Code and Codex keep the same
rules on a DeepSeek model. Both runs above used `deepseek-flash`.

Claude Code, through DeepSeek's Anthropic-compatible API:

```sh
export CLAUDE_CONFIG_DIR=~/.config/claude-deepseek   # a config folder with no claude.ai login
export ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic
export ANTHROPIC_API_KEY=<your DeepSeek key>
export ANTHROPIC_MODEL=deepseek-flash
claude
```

Keep the separate `CLAUDE_CONFIG_DIR`. With a claude.ai login in the config folder, Claude
Code sends that login's token, not the key. DeepSeek answers 401 each time, and Claude
Code retries without a word until it times out. It looks like a hang. Do not use
`--bare` either: it skips the hooks, so the guard does not run.

Codex, through DeepSeek's OpenAI-compatible API. Put this in `$CODEX_HOME/config.toml`
(default `~/.codex/config.toml`), and set `DEEPSEEK_API_KEY`:

```toml
model = "deepseek-flash"
model_provider = "deepseek"

[model_providers.deepseek]
name = "DeepSeek"
base_url = "https://api.deepseek.com"
env_key = "DEEPSEEK_API_KEY"
wire_api = "responses"
```

Codex 0.163.0 refuses `wire_api = "chat"`. It warns that it has no metadata for
`deepseek-flash`, and runs anyway.

---

上一章: [Four layers](https://yokoten.app/zh-tw/docs/guide/four-layers.md)
下一章: [Install and commands](https://yokoten.app/zh-tw/docs/guide/install-and-commands.md)
