Skip to content

init/update can't install agents when .claude/agents is a symlink, so argent-environment-inspector never lands (but the rule still references it) #701

Description

@lukeharvey

Summary. If .claude/agents is a symlink to a directory elsewhere in the repo, the agent install step fails and argent-environment-inspector.md is never written. Everything else installs fine, so it's easy to miss — but rules/argent.md then points at a subagent that doesn't exist.

Why the symlink layout. Agent definitions — skills, subagents, rules — are increasingly the same content across harnesses, but every harness wants them under its own vendor directory. Keeping the canonical copy in a neutral .agents/ and symlinking each harness's expected path at it means one definition, edited in one place, with no duplication or drift between copies. It's the same motivation behind AGENTS.md as a cross-tool file, applied to skills and subagents. Claude Code supports this directly: .claude/rules/ explicitly documents symlinks ("The .claude/rules/ directory supports symlinks, so you can maintain a shared set of rules and link them into multiple projects"), and .claude/skills / .claude/agents symlinks resolve fine for loading — Claude Code picks up the argent skills through exactly this symlink today.

So the ask is just that argent's writer tolerate what the host tool already tolerates: treat a symlinked .claude/agents as a valid destination rather than a thing to overwrite.

Repro

# canonical, harness-neutral definitions in .agents/ — each harness's path symlinked at it
mkdir -p .agents/agents .agents/skills
ln -s ../.agents/agents .claude/agents
ln -s ../.agents/skills .claude/skills

argent update   # or init

Error

Could not copy agents to /Users/me/repo/.claude/agents: Error: Cannot overwrite non-directory
/Users/me/repo/.claude/agents with directory
/path/to/node/lib/node_modules/@swmansion/argent/agents

Root cause. The agents step looks like a single recursive directory copy onto .claude/agents. Node's fs.cp refuses to overwrite a non-directory (the symlink) with a directory. Isolated repro on Node v24.18.0:

await fs.promises.cp('src', 'link', { recursive: true });
// ERR_FS_CP_DIR_TO_NON_DIR
// Cannot overwrite non-directory with directory: cp returned EISDIR

Resolving the destination first works:

const dest = await fs.promises.realpath('link');
await fs.promises.cp('src', dest, { recursive: true });  // ok

Why skills aren't affected. Skills are written per-file from each skillPath in skills-lock.json. Writing through a symlink into its target is fine — it's only replacing the symlink itself with a directory that fails. So skills install correctly into the symlinked location and only agents break.

Impact. rules/argent.md tells the agent to use argent-environment-inspector in two places (<subagents> and <react_native_detection>, the latter saying to run it "first before proceeding"), but it isn't installed. <subagents> has a fallback — "Never call gather-workspace-data yourself - do only if subagent is not available" — so behaviour degrades rather than breaking, which is also why the failed copy is easy to overlook. The install summary reports success apart from the one warning line.

Suggested fix. realpath the destination before the copy (or write agents file-by-file the way skills already are). Either would make symlinked .claude/ layouts work.

Workaround. Copy the files into the real directory by hand:

ARGENT=$(dirname $(dirname $(readlink -f $(which argent))))/agents   # package agents dir
cp $ARGENT/argent-environment-inspector.md .agents/agents/
mkdir -p .agents/agents/references
cp $ARGENT/references/quality-control-checklist.md .agents/agents/references/

The warning still prints on every update, but the agent is there and resolves through the symlink.

Env. argent 0.18.0 · Node v24.18.0 · macOS 15 (Darwin 25.5.0) · Claude Code 2.1.220 · global install on PATH.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions