diff --git a/README.md b/README.md index 41fb13c..e115da6 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ nix/home.nix # home-manager module for NixOS machines flake.nix # exposes homeModules.default ``` -Skills are portable: only `name`+`description` frontmatter is required by any of the tools; Claude-only fields (`user-invocable`, `args`) are ignored elsewhere. Claude Code reads them from `~/.claude/skills`, Codex and Pi from `~/.agents/skills`, and opencode auto-loads both — so the two links cover all four. Cross-skill refs use root-relative paths (`tracker-common/COMMON.md`), so they resolve under either root. +Skills are portable: only `name`+`description` frontmatter is required by any of the tools; Claude-only fields (`user-invocable`, `args`) are ignored elsewhere. Claude Code reads them from `~/.claude/skills`, Pi from `~/.agents/skills`, Codex from `~/.codex/skills` (and only there — `~/.agents/skills` is invisible to it), and opencode auto-loads the first two. Cross-skill refs use root-relative paths (`tracker-common/COMMON.md`), so they resolve under either root. Context files differ: Claude Code and Codex support `@file` imports, so their entry files import the shared fragments by path. Pi and opencode do not, so each gets a single `AGENTS.md` generated by concatenating the same fragments — on NixOS the home-manager module builds it in the store, elsewhere `bin/link.sh` writes it (idempotent; set `MACHINE=name` to pick a `claude-md/machines/` profile, default is `default`). @@ -30,7 +30,7 @@ git clone https://git.naps.pt/yolo/agent-skills.git ~/tea/agent-skills ~/tea/agent-skills/bin/link.sh ``` -Symlinks each skill into `~/.claude/skills/` and `~/.agents/skills/`, commands into `~/.claude/commands/` and `~/.config/opencode/commands/`, hooks into `~/.claude/hooks/`, `claude-md/` fragments into `~/.claude/`, and generates `~/.pi/agent/AGENTS.md` and `~/.config/opencode/AGENTS.md` from the fragments. Idempotent; any pre-existing real dir (or non-generated AGENTS.md) is moved to `~/.agent-skills-backup/` (outside the discovery path, so it isn't picked up as a duplicate skill). Re-run after adding a skill. +Symlinks each skill into `~/.claude/skills/`, `~/.agents/skills/` and `~/.codex/skills/`, commands into `~/.claude/commands/` and `~/.config/opencode/commands/`, hooks into `~/.claude/hooks/`, `claude-md/` fragments into `~/.claude/`, and generates `~/.pi/agent/AGENTS.md` and `~/.config/opencode/AGENTS.md` from the fragments. Idempotent; any pre-existing real dir (or non-generated AGENTS.md) is moved to `~/.agent-skills-backup/` (outside the discovery path, so it isn't picked up as a duplicate skill). Re-run after adding a skill. Hooks still need one manual step: the `settings.json` snippet in `hooks/README.md`. Entry files are linked automatically — `entry/CLAUDE.md` and `entry/codex-AGENTS.md` hold the machine-local sections and `@import` the shared fragments, so both tools read the same rules with no copy and no drift. diff --git a/bin/link.sh b/bin/link.sh index d22bba8..ef8f589 100755 --- a/bin/link.sh +++ b/bin/link.sh @@ -7,10 +7,13 @@ set -euo pipefail REPO="$(cd "$(dirname "$0")/.." && pwd)" # targets: agent config skill roots. Claude Code reads ~/.claude/skills, -# Codex and Pi read ~/.agents/skills, opencode auto-loads both dirs. -# All four consume the same SKILL.md dirs. +# Pi reads ~/.agents/skills, opencode auto-loads both dirs, and Codex reads +# $CODEX_HOME/skills and nothing else -- ~/.agents/skills is invisible to it, +# which is how review sessions ended up reporting an unavailable review-pr +# skill. All four consume the same SKILL.md dirs. CLAUDE_SKILLS="$HOME/.claude/skills" -CODEX_SKILLS="$HOME/.agents/skills" +AGENTS_SKILLS="$HOME/.agents/skills" +CODEX_SKILLS="$HOME/.codex/skills" CLAUDE_CMDS="$HOME/.claude/commands" # commands are Claude-only; Codex ignores OPENCODE_CMDS="${XDG_CONFIG_HOME:-$HOME/.config}/opencode/commands" CLAUDE_HOOKS="$HOME/.claude/hooks" # hooks are Claude-only @@ -62,11 +65,12 @@ gen() { # gen — writes a generated (concatenated) file GEN_MARK="" -mkdir -p "$CLAUDE_SKILLS" "$CODEX_SKILLS" "$CLAUDE_CMDS" "$CLAUDE_HOOKS" "$CLAUDE_SCRIPTS" "$CLAUDE_RULES" "$CODEX_HOME" "$PI_HOME" "$OPENCODE_CMDS" +mkdir -p "$CLAUDE_SKILLS" "$AGENTS_SKILLS" "$CODEX_SKILLS" "$CLAUDE_CMDS" "$CLAUDE_HOOKS" "$CLAUDE_SCRIPTS" "$CLAUDE_RULES" "$CODEX_HOME" "$PI_HOME" "$OPENCODE_CMDS" for d in "$REPO"/skills/*/; do name="$(basename "$d")" link "$d" "$CLAUDE_SKILLS/$name" + link "$d" "$AGENTS_SKILLS/$name" link "$d" "$CODEX_SKILLS/$name" done diff --git a/nix/home.nix b/nix/home.nix index 813ca9d..1190340 100644 --- a/nix/home.nix +++ b/nix/home.nix @@ -40,6 +40,22 @@ let repo = cfg.repoPath; mkEnable = what: lib.mkEnableOption "the ${what} user unit"; + + # Codex reads $CODEX_HOME/skills and nothing else -- ~/.agents/skills reaches + # pi and opencode but not codex, which is why codex review sessions reported + # review-pr as an unavailable skill. One symlink per skill dir rather than + # recursive=true: codex skips any skill whose SKILL.md is itself a symlink, + # which is exactly what a per-file link produces. Per-skill also leaves + # codex's own ~/.codex/skills/.system in place, which a whole-dir link would + # replace. + skillNames = builtins.attrNames ( + lib.filterAttrs (_: type: type == "directory") (builtins.readDir "${agent-skills}/skills") + ); + codexSkills = lib.listToAttrs ( + map (name: lib.nameValuePair ".codex/skills/${name}" { + source = "${agent-skills}/skills/${name}"; + }) skillNames + ); in { options.programs.agentSkills = { @@ -71,7 +87,7 @@ in weekReview.enable = mkEnable "weekly review timer"; }; - config.home.file = { + config.home.file = codexSkills // { ".claude/skills" = { source = "${agent-skills}/skills"; recursive = true;