mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-31 16:33:34 +08:00
Skills installed by Codex, Cursor, Gemini CLI, or opencode live under `.agents/skills/` -- the convention documented at agentskills.io -- so users had to keep a duplicate copy in `.claude/skills/` before cc-haha could see them. The spec only constrains SKILL.md itself, which we already parse, so this is purely a directory-discovery gap. Scan both conventions at every scope: user (`~/.agents/skills`), project (every level up to the repo root), `--add-dir` roots, on-demand monorepo discovery, and the hot-reload watcher. skillRoots.ts is the single source of truth for those paths; the REST API now reports which convention a skill came from and the desktop list badges the `.agents` ones. `.claude` is always ordered first, so first-wins dedup keeps a user's own skills authoritative rather than letting an externally installed same-named skill shadow them. Name collisions are collapsed per scope, which covers copy-based sync -- symlink-based sync was already handled by the existing realpath dedup. Collisions across different scopes, such as a user skill and a project skill sharing a name, keep their existing behavior, and the shadowed file's identity is recorded so overlapping roots (CLAUDE_CONFIG_DIR pointing inside the project) cannot reintroduce it under the other scope. getProjectDirsUpToHome is untouched -- it is shared by commands, agents, output-styles, and workflows. Only its directory walk is extracted so the skill resolver reuses the same submodule/worktree stop boundary. Writes stay where they were: market installs and /skillify still target `~/.claude/skills`. `.agents` is anchored at $HOME rather than CLAUDE_CONFIG_DIR, since it is a space shared with other tools that read $HOME. Note that Bun snapshots os.homedir() at startup while Node re-reads $HOME per call, so the resolver reads the env var explicitly. Opt out with `disableAgentSkillsDirectory` in settings.json or CLAUDE_CODE_DISABLE_AGENT_SKILLS_DIR=1; no `.agents` root is produced at all then, so there is not even a failed stat. (cherry picked from commit ba9338f85d6beaba237753e42e823c87b59633d4)