Repository navigation
Description
Description
Project-level skills in .claude/skills/ are not discovered when the directory is a symlink and the session runs in a git worktree sandbox (not the main repo).
Reproduction
Setup
- Main repo at
/Users/me/code/my-projectwith.claude/skills/as a symlink (e.g., to a git submodule) - OpenCode worktree sandbox at
~/.local/share/opencode/worktree/<id>/my-sandbox - The symlink resolves correctly —
ls .claude/skills/shows all skill directories opencode.jsonincludes"skills": { "paths": [".claude/skills"] }
Symlink structure
.claude/skills -> ../.pal-skills/.claude/skills (symlink to submodule)
.agents/skills -> ../.claude/skills (symlink chain)
Both resolve correctly on the filesystem. Python glob.glob() and Bun Glob.scan({ followSymlinks: true }) both find 23 SKILL.md files through the symlinks.
Expected
skill("my-project-skill") returns the skill content. Project skills appear in <available_skills>.
Actual
skill("my-project-skill") returns "not found". Only user-level skills (~/.claude/skills/, ~/.agents/skills/) and opencode-config skills (~/.config/opencode/skills/) are available.
Root Cause Analysis
I decompiled the binary and traced the issue through src/skill/index.ts and src/skill/discovery.ts:
1. Glob doesn't follow directory symlinks
Glob.scan is called with symlink: true, which toGlobOptions() maps to follow: true:
function toGlobOptions(options) {
return {
cwd: options.cwd,
absolute: options.absolute,
dot: options.dot,
follow: options.symlink ?? false, // symlink: true → follow: true
nodir: options.include !== "all"
};
}However, empirical testing with Bun shows:
// followSymlinks: true → 23 skills found
// Without followSymlinks → 0 skills foundThe npm glob package (bundled as Ze) uses follow, but the actual traversal may not follow directory symlinks — only file symlinks. When .claude/skills is a symlink to a directory, the glob pattern skills/**/SKILL.md finds nothing because it doesn't descend into the symlinked directory.
2. Per-sandbox skill isolation
The skill system creates per-session state via create(discovery, directory, worktree). The directory for a worktree sandbox is the sandbox path, not the main repo. Desktop app logs confirm:
- Skills scanned from the main repo path show up in WARN logs (duplicate skill names)
- But those skills are NOT available to the sandbox session
- The sandbox session's own scan finds 0 skills due to the symlink issue above
Evidence from logs
Desktop app log (~/Library/Logs/ai.opencode.desktop/):
WARN service=skill name=fastapi existing=.../pal-mono/.claude/skills/fastapi/SKILL.md
duplicate=.../pal-mono/.agents/skills/fastapi/SKILL.md
Skills ARE found when scanning from the main repo. But skill("fastapi") returns "not found" in the sandbox session.
Environment
- OpenCode Desktop v1.3.0 (macOS arm64)
- macOS Sequoia
- Git worktrees (not bare clones)
.claude/skillsis a git-tracked symlink (mode 120000) pointing to a submodule directory
Workaround
Replace symlinks with real directories (copy files instead of symlinking to submodule). This defeats the purpose of using a shared skills submodule.
Suggested Fix
- Ensure
Glob.scanfollows directory symlinks when traversing — either use Bun's nativefollowSymlinks: trueorfs.realpathSyncon the cwd before scanning - Consider sharing skill state across sandboxes within the same project, since they share the same
.claude/skills/content
Activity
EnocFlores commentedon Jun 12, 2026
+1 with a broader observation: this is not just a skill-discovery issue, it's the underlying
globtool itself that does not traverse symlinks, which affects any workflow that relies on a symlinked directory under the workspace root.Use case (similar setup, different content)
I keep Jira implementation docs in a centralized directory at
~/bare-repo/agent-resources/(outside any single worktree, shared across many.worktrees/*sandboxes for the same repo). Each worktree has a symlink at the root:This used to work fine with
glob. Sub-agents could enumerate docs withglob "agent-resources/**/*.md". As of recent versions:glob "agent-resources/**/*.md"Readandgrepwork via the same symlinkglob ".opencode/**/*.md"grep ... agent-resources/Read agent-resources/<file>.mdbash ls agent-resources/The source (
packages/opencode/src/tool/glob.ts) shows the tool delegates toripgrep.glob({ cwd, pattern, limit }), which inherits ripgrep's default of not following symlinks.Suggested resolutions (in order of effort)
followSymlinks: booleanparameter to theglobtool schema. Defaultfalsepreserves current behavior; users/agents opt in per-call.tool.glob.followSymlinksinopencode.json) for users with stable workflows like worktree sandboxes..ignoreif those symlink directories are re-included via!agent-resources/.Workaround I'm currently using
Updated my global
~/.config/opencode/AGENTS.mdto teach all agent sessions:Added
agent-resources/README.mdas a manual catalog and included it in the globalinstructionsarray so every session loads the index at startup. Workable, but it's a workaround for behavior that previously did not need one.github-actions commentedon Aug 12, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.