Skip to main content
Glama
volkangunay

agentclaim

by volkangunay

agentclaim

多个代理。一个工作树。Git 救不了你。

为并行 AI 编码代理提供文件所有权——让它们不再静默地互相覆盖。

npm node dependencies license github

npm i -g agentclaim && agentclaim init

问题

你同时运行两个、三个、五个编码代理。它们共享一个工作树

Git 是为在各自独立的克隆上工作、稍后再合并的人设计的。它完全不知道该如何处理 两个写入者在同一秒编辑同一个检出目录的情况。没有冲突标记、没有警告、没有合并—— 第二次写入直接获胜,第一次写入就消失了。

以下是同一个仓库里一个下午发生的三起真实事故。三起都发布到了生产环境。 没有一起产生过任何一条错误消息。

1. 暂存竞态

git add 对文件的快照就是它那一瞬间的样子

 session A            session B
 ─────────            ─────────
                      git add i18n.js Money.jsx   ← snapshots i18n.js v1
 write i18n.js v2
                      git commit                  ← commit contains i18n.js v1

这次提交把一个新的 Money.jsx旧的 i18n.js 一起发布了。页面 在生产环境中渲染出了原始的翻译键。Git 报告成功。后续的 修复提交又掉进了完全相同的竞态。

2. 破坏性还原

git checkout HEAD -- i18n.js api.demo.js   # session A tidies its tree
git commit -a                              # session B, two seconds later

会话 B 的工作被从磁盘上还原掉,然后被提交走了。悄无声息。

3. 说谎的门禁

部署脚本有一个脏工作树守卫。它检查的是部署时的工作树, 而不是提交时的竞态。门禁变绿了,提交是错的,部署 忠实地发布了错误的提交。


Related MCP server: asynkor

解决方案

写入变得更聪明。提交保持严格。

阻止每一个第二个写入者会是一块停车标志,而不是解决方案——而且一个阻止人们 需要做的工作的工具会被关掉。两个代理在同一个文件里,只有当它们触及同一个 区域时才是真正的冲突。

所以 agentclaim 不问"这个文件归谁?"它问的是**"自从我上次看它以来,什么变了?"** ——这是唯一能把另一个代理的编辑和你自己的编辑区分开来的问题。不同区域,两个代理 都能工作。同一行,其中一个停下来。

agentclaim: src/checkout.ts is also being edited by another agent — your edits do not overlap theirs.
  their lines: 12-19
  your lines:  84-91
Both edits are kept. You may not commit this file until they are done.

整文件 Write 也不是冲突——它会用 git merge-file 与另一个代理的工作做三方合并, 所以两边的编辑都能落地:

agentclaim: src/checkout.ts was merged, not overwritten.
Another agent had edited this file; your write has been combined with
their changes. Re-read the file before continuing — it now contains both.

重叠的行仍然不是停车标志

定向编辑是外科手术式的:只有当它的锚文本仍然存在于当前的文件中时,它才会生效。 这一个属性就完成了所有工作。

  • 锚还在 → 替换它不会丢掉另一个代理的编辑,因为它们的更改 按定义在文本的其他地方

  • 锚没了 → 工具自己拒绝,代理重新读取

无论哪种情况,结果都已经是正确的,所以阻止只会浪费一次往返,什么也买不到。 agentclaim 改为添加上下文:

agentclaim: heads up — another agent just changed the same lines of src/checkout.ts.
  their lines: 12-19
  your lines:  14-16

Your edit still applies cleanly on top of their version. This is what
they changed, in case it affects what you were about to do:
  @@ line 12-19 @@
  -  const total = items.length
  +  const total = items.reduce((n, i) => n + i.qty, 0)

Nothing is blocked. You may not commit this file until they are done.

真正会阻止代理的是什么

四件事,而且只有这四件:

被阻止的情况

原因

一个无法三方合并的整文件写入

没有正确的自动答案,两个版本中会有一个丢失。

本会话从未读取过的文件进行整文件写入

没有可合并的基准;这是盲目覆盖。

暂存或提交另一个代理正在积极编辑的文件

事故 #1:这就是一个代理把另一个代理做了一半的工作发布出去的方式。

在另一个会话活跃时,我们无法解析的、间接触及 git 的 git 命令evalsh -c

我们不会对 git reset --hard 瞎猜。

编辑路径上没有任何东西会被阻止。这就是重点:一个打断代理做普通工作的工具 会被关掉,然后它就什么都保护不了了。

提交是严格的部分

一旦两个活跃会话都碰过某个文件,任何一方都不能暂存或提交它——因为 这正是事故 #1 中一个代理把另一个代理做了一半的工作发布出去的方式。

如果这个保护没有出口,它就会变成死锁,所以它有三个出口:

出口

作用

agentclaim release <path>

"我在这里做完了。"只放弃的份额,不需要 --force,也不能用来偷走文件。另一个代理可以立即提交。

什么都不做

一个会话在最后一次编辑某个文件后的 touchTtlMinutes(默认 10)内会继续阻止其他人提交该文件。故意比 ttlMinutes 短:还活着不等于还在这个文件里工作,把这两者混为一谈正是把保护变成死锁的原因。

agentclaim release <path> --force

直接接管。粗暴的手段,需要时仍然可用。

会话结束或崩溃时会释放它们持有的所有东西,所以工作树永远不会被锁住。

没有服务器。没有守护进程。没有依赖。存储区是 .git/ 里的一个目录。


快速开始

npm i -g agentclaim
cd your-repo
agentclaim init

不想全局安装?npx agentclaim init 也可以——它会先把自己复制到 ~/.agentclaim/lib,因为钩子必须指向一个明天仍然存在的路径。

就这样。init 会配置 Claude Code 钩子并安装一个 git pre-commit 钩子 (会链接任何已有的钩子)。随时检查:

$ agentclaim status
SESSION          FILES  AGE   LAST SEEN
● money screen       3  6m    2s
  ai visibility      2  22m   14s

FILE                    HELD BY        AGE
web/src/Money.jsx       (you)          6m
web/src/i18n.jsx        (you)          6m
web/src/api.demo.js     ai visibility  22m

给你的会话起一个可读的名字,这样另一个代理的错误消息就有意义了:

agentclaim label "money screen"

init 会在你的机器上改变什么

三件事。没有别的。

什么

在哪里

撤销

钩子条目

.claude/settings.json(先备份)

agentclaim uninstall

一个 pre-commit 钩子

.git/hooks/(已有的钩子会被链接,绝不会被替换)

agentclaim uninstall

声明存储区

.git/agentclaim/——在 .git 内部,永远不会被提交

删除该目录

没有网络调用。没有遥测。没有后台进程。你的代码一行都不会被碰, git status 里也不会出现任何新东西。


当你独自一人时它什么都不做

如果在这个工作树里只有你一个活跃会话,每个门禁都会短路为允许。 不执行任何声明,不检查任何命令,没有什么需要绕过的。

这是故意的。一个人们无法通过的门禁比没有门禁更糟,因为他们会学会 禁用它,然后它就什么都保护不了了。agentclaim 只会在它存在的那个 确切场景里才有牙齿。

$ agentclaim doctor
...
1 live session(s) · 3 claim(s) · TTL 30m · mode block
single session -> gates inactive (no-op)

四道门禁

#

门禁

时机

阻止什么

1

写入

Write / Edit 之前

编辑路径上什么都不阻止——整文件写入会被合并,只有无法合并的才会停下来

2

Git

Bash 命令之前

git add -Agit commit -agit checkout -- xgit reset --hardgit stashgit clean 触及你不拥有的文件

3

提交真相

git commit 之后

快照竞态——与磁盘不匹配的提交内容

4

pre-commit

任何 git commit

属于其他人的已暂存文件,来自任何工具

门禁 3 是其他任何东西都抓不到的那个。它会用 git show <sha>:<path> 重新读取提交中的每个路径,并逐字节与磁盘上的文件比较:

agentclaim: ⚠ commit 045d1f7 does NOT match what is on disk:
  web/src/api.demo.js

This is the classic `git add` snapshot race: another session rewrote these
files after you staged them, so the commit captured stale content.
DO NOT DEPLOY. Fix it with:
  git add web/src/api.demo.js && git commit --amend --no-edit

它只报告由另一个会话持有的文件,所以普通的局部暂存 (git add x,继续编辑 x,提交)永远不会触发误报。


与任何代理兼容

三层集成,最强的在前。能用的都用上。

Claude Code——钩子(最强)

agentclaim init            # project-level  (.claude/settings.json)
agentclaim init --global   # every repo     (~/.claude/settings.json)

门禁在写入或命令之前运行。代理会把拒绝当作 反馈,然后自己选择另一个文件。

Cursor · Windsurf · Codex · Zed · Cline · 任何支持 MCP 的工具

agentclaim 自带一个 MCP 服务器,所以任何会说 MCP 的代理都可以加入同一个 所有权协议:

{
  "mcpServers": {
    "agentclaim": {
      "command": "agentclaim",
      "args": ["mcp"],
      "env": { "AGENTCLAIM_SESSION": "cursor-1", "AGENTCLAIM_AGENT": "cursor" }
    }
  }
}

暴露的工具:agentclaim_statusagentclaim_claimagentclaim_releaseagentclaim_checkagentclaim_verify_commit。描述会告诉模型 何时调用它们。

其他一切——git 钩子

agentclaim init 会安装一个 pre-commit 钩子,所以 aider、普通的 git commit、 你的 IDE 或 shell 脚本都会命中同样的检查。无需配置。

对于部署脚本和 CI,使用退出码门禁:

agentclaim check --staged --quiet || exit 1   # anyone else holding staged files?
agentclaim verify HEAD                        # did the commit capture disk?

命令

agentclaim init [--global]    wire up the hooks (Claude Code + git pre-commit)
agentclaim status             who holds what
agentclaim who <path>         owner of a single file
agentclaim claim <path...>    claim files            [--note "..."]
agentclaim release <path...>  "I am done here"       [--all] [--force to take over]
agentclaim check <path...>    gate for scripts, exit 0/1  [--staged] [--quiet]
agentclaim verify [rev]       compare commit content against disk  [--all]
agentclaim label "<name>"     give this session a readable name
agentclaim gc                 collect stale claims
agentclaim doctor             diagnose the installation
agentclaim uninstall          remove the hooks
agentclaim mcp                run as an MCP server

配置

仓库根目录下可选的 .agentclaim.json

{
  "ttlMinutes": 30,
  "touchTtlMinutes": 10,
  "mode": "block",
  "ignore": ["node_modules/**", "dist/**", "*.lock", "package-lock.json"]
}
  • ttlMinutes —— 一个会话在这个时长内没有活动就被视为已消失, 它的声明可以被接管。每次钩子调用都会刷新心跳,所以活跃的会话永远不会过期。

  • touchTtlMinutes —— 一个会话在最后一次编辑某个文件后,会继续阻止其他人 提交该文件的时长。故意比 ttlMinutes 短:还活着不等于还在这个文件里工作, 把这两者混为一谈正是把保护变成死锁的原因。

  • mode —— block(默认)、warn(报告但允许)、off

  • ignore —— 永远不会被声明。把生成的文件放在这里;如果锁文件和构建 产物被声明了,门禁会频繁触发,人们就会开始绕过它。


工作原理

.git/agentclaim/
  sessions/<id>.json   { sid, label, pid, started, seen, wt }
  claims/<hash>.json   { path, wt, sid, at, touchers }
  snap/<sid>/<hash>    what that session last saw on disk
  pending/<sid>/<hash> a merge computed before a write, applied right after it
  pass.json            short-lived identity token for the git hook
  • 存储位置git rev-parse --git-common-dir,因此仓库的每个工作树共享一个注册表。

  • 认领键包含工作树根,因为同一相对路径在两个工作树中是磁盘上的两个不同文件。独立的工作树永远不会互相阻塞——工作树是解决此问题的合理方案,而不是需要惩罚的对象。

  • 原子性就是 open(..., 'wx') —— O_EXCL。两个同时发出的认领,只有一个胜出,不存在竞态。

  • 活性基于 TTL。钩子在每次工具调用时触发,因此 seen 在几秒内保持新鲜;崩溃代理的认领可被回收,绝不会卡死仓库。

  • 区域推理使用 git diff --no-index -U0 将你会话的快照与磁盘上的文件进行比较,并使用 git merge-file 进行合并。一切都是 git 自身的语义——那些你已然信任的语义——无需添加任何依赖。

  • **合并由我们应用,而非注入。**钩子输出的结构有一个 updatedInput 字段,但没有任何可验证的东西表明它可以在不自动批准该调用的情况下被应用,如果对此做出错误假设,就会静默丢弃其他代理的工作。因此,合并会被暂存,并在工具运行后立即写入,只使用我们可控的机制。

  • 每次工具调用的成本是一个短生命周期的 node 进程:单独工作时约 ~45 ms,当门禁实际需要推理时约 ~83 ms(在包含 200 个文件的仓库上测得)。Node 启动占主导——读取后拍摄快照约增加 1 ms。

  • 无需维护。gc 在每次会话启动时运行,并清除已结束会话的认领、快照和待处理合并,因此 .git/ 中不会积累任何东西。


局限性

  • git commit --no-verify 会跳过 git 钩子层。Claude Code 层仍然会捕获它。

  • 没有钩子或 MCP 的代理在写入时是不可见的;它们在提交时会被捕获。

  • 命令行解析刻意不是完整的 shell 解析器。对于涉及 git 的 eval / sh -c / 反引号命令,agentclaim 仅在另一个会话处于活动状态时拒绝执行。

  • 区域共存需要知道你的会话上次看到了什么,因此它只适用于代理通过其工具读取或写入的文件。通过其他途径(shell 的 sed、外部编辑器)更改的文件对该推理是不可见的。

  • agentclaim 不理解含义。两次编辑可以在文本上相互独立,但合在一起在语义上不连贯;它会告诉你其他代理在那里,但判断权在你手上。

  • 一次干净的三方合并仍然可能在语义上是错误的,就像对人类来说一样。agentclaim 会告诉你文件已被合并,以便你在依赖它之前重新阅读。

  • 认领是按机器划分的。主机之间不会同步任何内容。


测试

npm test

32 项端到端检查。该测试套件重放了上述所有三个真实事件,证明该工具对独立会话完全无操作,并断言每个门禁都有通过和失败两种示例——一个无法捕获自身 bug 的门禁比没有门禁更糟糕,因为它会激发信任。


链接

许可证

MIT © Volkan Günay

A
license - permissive license
Not graded
quality - not tested
C
maintenance

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Coordination layer for AI coding agents working on the same codebase. Adds file locks, shared project memory, and cross-machine file sync so Claude Code, Cursor, Windsurf, and other MCP agents stop overwriting each other.
    50
    Apache 2.0
  • A
    license
    A
    quality
    B
    maintenance
    Enables multiple AI agents to collaborate on the same git repository by coordinating work via a shared claims branch, detecting file conflicts before they happen.
    9
    PolyForm Noncommercial 1.0.0

View all related MCP servers

Related MCP Connectors

  • The team layer for AI coding agents: shared contracts, collision alerts, E2EE sessions.

  • Coding agents from Claude Code, Cursor and Codex claim jobs and lock files on one shared board.

  • Coordinate multiple AI agents over MCP: atomic claims, leases, shared ledger, handoffs, tasks.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/volkangunay/agentclaim'

If you have feedback or need assistance with the MCP directory API, please join our Discord server