Skip to content

fix: repair skills symlinks broken by Windows git + auto-activate brave-search (#156)#169

Open
1aifanatic wants to merge 1 commit into
stephengpope:mainfrom
1aifanatic:fix/156-skills-symlink-repair
Open

fix: repair skills symlinks broken by Windows git + auto-activate brave-search (#156)#169
1aifanatic wants to merge 1 commit into
stephengpope:mainfrom
1aifanatic:fix/156-skills-symlink-repair

Conversation

@1aifanatic

Copy link
Copy Markdown

Summary

Fixes #156Agent does not use tools (Brave Search, Date Tool) despite configuration

Two root causes identified and fixed:

  • Skills symlinks become text files on Windows git (core.symlinks=false): git checks out mode-120000 symlink entries as plain text files. bin/cli.js init() copied these into the user's project, then the symlink-creation guard saw fs.existsSync() === true for the text file and silently skipped creating the real symlink. Those text files were committed as mode 100644. When the Docker agent cloned the repo on Linux, .pi/skills was a regular file — causing the Pi agent to crash: ln: failed to create symbolic link '/job/.pi/skills/brave-search': No such file or directory

  • BRAVE_API_KEY doesn't auto-activate brave-search: Users set AGENT_BRAVE_API_KEY as a GitHub secret expecting the skill to work, but only the symlink skills/active/brave-search activates it. Without running the setup wizard, the symlink never gets created.

Changes

bin/cli.js

  • Add isTextFileSymlink(filePath, expectedTarget) — uses fs.lstatSync to detect a text-file symlink artifact (Windows git) vs a real symlink or directory
  • In init(), call isTextFileSymlink before each createDirLink call; remove the text-file artifact if found so the real symlink is always created

templates/docker/pi-coding-agent-job/entrypoint.sh

templates/docker/claude-code-job/entrypoint.sh

  • Add repair_dir_symlink function: runs after git clone, detects text-file artifacts and replaces them with proper ln -sf symlinks; also creates any symlinks that are missing entirely
  • Auto-activate brave-search skill when BRAVE_API_KEY is present in the environment but skills/active/brave-search symlink is absent (BRAVE_API_KEY is injected from AGENT_BRAVE_API_KEY GitHub secret via the existing SECRETS eval pipeline)

Test Plan

  • isTextFileSymlink unit-tested across 6 cases: exact match, wrong content, directory, missing file, trailing newline, real symlink — all pass
  • Full init() flow simulated in Node.js: text-file artifacts replaced by real symlinks end-to-end
  • Bash syntax validated: bash -n passes on both entrypoints
  • repair_dir_symlink logic verified: only removes files whose content exactly matches the expected symlink target — unrelated files are left untouched
  • brave-search auto-activation: activates when key present, skips when key absent

Backward Compatibility

  • Projects already using proper symlinks (Linux/Mac init): isTextFileSymlink returns false → no change in behavior
  • repair_dir_symlink in entrypoints: no-ops on valid symlinks ([ -L ] check passes, function returns immediately)

🤖 Generated with Claude Code

…ve-search

Fixes stephengpope#156

## Root Cause

On Windows hosts with `git core.symlinks=false`, git checks out
mode-120000 symlink entries (e.g. `.pi/skills`, `skills/active/browser-tools`)
as plain text files whose content is the symlink target path.

`bin/cli.js init()` copied these text files into the user's project via
`fs.copyFileSync`, then its symlink-creation guard used `fs.existsSync()`
which returned true for the text file — so the real symlink was silently
skipped. The text files were committed to git as mode 100644. When the
Docker agent later cloned the repo on Linux, `.pi/skills` was a text file,
not a directory, causing the Pi agent to crash:

    ln: failed to create symbolic link '/job/.pi/skills/brave-search':
    No such file or directory

The second issue: users set `USE_BRAVE_SEARCH=true` expecting it to enable
the skill, but that env var is not processed anywhere. Brave Search is
activated by a `skills/active/brave-search` symlink; if only `BRAVE_API_KEY`
is set (via `AGENT_BRAVE_API_KEY` GitHub secret) the skill stayed inactive.

## Fix

**bin/cli.js** — Add `isTextFileSymlink(filePath, expectedTarget)` that uses
`lstatSync` to distinguish a real symlink from a text-file artifact. Before
each `createDirLink()` call in `init()`, remove any text-file artifact so the
symlink is always created correctly regardless of host OS.

**entrypoint.sh (both pi-coding-agent-job and claude-code-job)** — Add a
`repair_dir_symlink` function that runs after `git clone` inside the Docker
container. It detects text-file artifacts (Windows git) and replaces them
with proper symlinks, and creates any missing symlinks. Also auto-activates
the brave-search skill when `BRAVE_API_KEY` is present in the environment
but the `skills/active/brave-search` symlink is missing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@1aifanatic 1aifanatic requested a review from stephengpope as a code owner March 3, 2026 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Agent does not use tools (Brave Search, Date Tool) despite configuration

1 participant