From dibs
Claim, check, or release a vendor-neutral directory lock before mutating a repo another agent might touch.
How this skill is triggered — by the user, by Claude, or both
Slash command
/dibs:dibsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Calling dibs on a directory: a small, vendor-neutral, cross-platform lock so
Calling dibs on a directory: a small, vendor-neutral, cross-platform lock so only one coding agent occupies a working directory at a time. A Claude and a Codex contend for the same lock through the same on-disk artifact, so neither needs to know about the other's hooks.
The lock is a file created with an atomic exclusive create
(open(O_CREAT|O_EXCL), the node wx flag) at a deterministic path keyed by
the target directory's realpath: ${LAICLUSE_HOME:-$HOME/.laicluse}/locks/<sha256-of-realpath>.lock.
The record holds the realpath, holder pid, agent, session id, hostname, a
nonce, and an acquired-at timestamp. Liveness is pid-based
(process.kill(pid, 0) on the same host), not a heartbeat: a lock whose holder
process is gone is taken over by the next claimer, while a live holder is
refused. No external binary, no flock, no native dependency.
The CLI lives in this plugin's bin/. Resolve the plugin root, then call it:
resolve_dibs_root() {
if [ -n "${DIBS_BIN:-}" ]; then dirname "$(dirname "$DIBS_BIN")"; return 0; fi
if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then printf '%s\n' "$CLAUDE_PLUGIN_ROOT"; return 0; fi
if command -v codex >/dev/null 2>&1; then
codex plugin list | awk '$1 == "dibs@laicluse-agent-fieldkit" { print $NF; found=1; exit } END { exit found ? 0 : 1 }'
return $?
fi
return 1
}
ROOT="$(resolve_dibs_root)" || { echo "dibs plugin root not found" >&2; exit 1; }
DIBS="${DIBS_BIN:-$ROOT/bin/dibs}"
node "$DIBS" claim <dir> [--pid <n>] [--agent <name>] [--session <id>] [--max-age-hours <n>] [--json]
node "$DIBS" release <dir> [--pid <n>] [--json]
node "$DIBS" check <dir> [--max-age-hours <n>] [--json]
held-by-self), or takes over a
stale lock whose holder is dead (took-over-stale). It exits non-zero and
names the holder (refused: held by <agent> (pid <pid>) since <acquired-at>)
when a live holder exists.free or the holder plus its liveness and staleness.--pid is the pid that must stay alive for the lock to count as live. Record
the long-lived holder (the agent or session process), not the ephemeral process
that runs dibs. It defaults to the calling process's parent pid. --json
prints the full result record for machine consumers.
The lock only helps if it is acquired before mutation, so the two reliable acquisition points are:
bonsai claims the lock for a worktree it hands out
(it consumes this one implementation; there is no second lock anywhere). The
git-native commit hook in the branch-worktree-discipline order remains the
backstop for agents without a reliable pre-mutation hook.--max-age-hours cap bound the
residual risk; v1 accepts it rather than adding a native process-start probe.EPERM), so dibs never breaks a lock it cannot prove dead; that lock
clears only via --max-age-hours. Within one user on one machine, the agent
case, liveness is exact.index.lock.npx claudepluginhub epologee/laicluse-agent-fieldkit --plugin dibsGuides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Enforces test-driven development: write failing test first, then minimal code to pass. Use when implementing features or bugfixes.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.