refactor: both tools import the same fragments; scope code comments by path

Codex reads ~/.codex/AGENTS.md, not ~/.agents/AGENTS.md, so the file
generated there last commit was inert. Both tools support @path imports,
which removes the generate-and-drift step entirely.

code-comments.md moves to ~/.claude/rules/ with paths frontmatter, so its
24 lines load only when a source file is read.
This commit is contained in:
naps62
2026-08-01 14:08:44 +00:00
parent 36372f85b2
commit ccfb5192f8
6 changed files with 44 additions and 42 deletions
+2 -2
View File
@@ -27,7 +27,7 @@ git clone https://git.naps.pt/yolo/agent-skills.git ~/tea/yolo/agent-skills
Symlinks each skill into `~/.claude/skills/` and `~/.agents/skills/`, commands into `~/.claude/commands/`, hooks into `~/.claude/hooks/`, `claude-md/` fragments into `~/.claude/`. Idempotent; any pre-existing real dir 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 and fragments need one manual step each: the `settings.json` snippet in `hooks/README.md`, and `@writing.md` + `@operating.md` import lines in your `~/.claude/CLAUDE.md`. Codex needs no step — `link.sh` generates `~/.agents/AGENTS.md` from both fragments (re-run it after editing them).
Hooks and fragments need one manual step each: the `settings.json` snippet in `hooks/README.md`, and `@writing.md` + `@operating.md` import lines in your `~/.claude/CLAUDE.md` and `~/.codex/AGENTS.md`. Both tools support `@path`, so they import the same fragments — no copy, no drift.
### NixOS machine (home-manager)
@@ -83,7 +83,7 @@ Two enforcers in `hooks/`:
| enforcer | scope |
|----------|-------|
| `comms-lint.py` | GitHub issues/PRs/review comments — 150 words (hard cap 300), evidence in `<details>`, no reviewer-addressing opener, plain diction |
| `comment-lint.py` | code comments — 1-3 lines, volume and purpose, not wording |
| `comment-lint.py` | code comments — 1-3 lines, volume and purpose, not wording. The rule is path-scoped (`~/.claude/rules/`), so it loads only when Claude reads a source file. |
Written in the voice it asks for, and leads with a worked example of each.
Prompt style leaks into output style, and examples steer harder than
+13 -13
View File
@@ -13,7 +13,8 @@ CODEX_SKILLS="$HOME/.agents/skills"
CLAUDE_CMDS="$HOME/.claude/commands" # commands are Claude-only; Codex ignores
CLAUDE_HOOKS="$HOME/.claude/hooks" # hooks are Claude-only
CLAUDE_HOME="$HOME/.claude" # CLAUDE.md fragments, pulled in via @name.md
AGENTS_HOME="$HOME/.agents" # Codex global config root
CLAUDE_RULES="$HOME/.claude/rules" # path-scoped rules
CODEX_HOME="$HOME/.codex" # Codex global config root
# Per-FILE links, never a whole-dir link: ~/.claude/hooks and ~/.claude itself hold
# machine-local files this repo does not own, and a dir symlink would hide them.
@@ -33,7 +34,7 @@ link() { # link <src> <dst>
echo "linked $dst -> $src"
}
mkdir -p "$CLAUDE_SKILLS" "$CODEX_SKILLS" "$CLAUDE_CMDS" "$CLAUDE_HOOKS" "$AGENTS_HOME"
mkdir -p "$CLAUDE_SKILLS" "$CODEX_SKILLS" "$CLAUDE_CMDS" "$CLAUDE_HOOKS" "$CLAUDE_RULES" "$CODEX_HOME"
for d in "$REPO"/skills/*/; do
name="$(basename "$d")"
@@ -51,22 +52,21 @@ for f in "$REPO"/hooks/*.py "$REPO"/hooks/*.sh; do
link "$f" "$CLAUDE_HOOKS/$(basename "$f")"
done
# code-comments.md is path-scoped and belongs in rules/, not here — linking it
# into ~/.claude/ as well would load it unconditionally and defeat the scoping.
for f in "$REPO"/claude-md/*.md; do
[ -e "$f" ] || continue
[ "$(basename "$f")" = "code-comments.md" ] && continue
link "$f" "$CLAUDE_HOME/$(basename "$f")"
done
# Codex reads ~/.agents/AGENTS.md and has no @import, so the fragments are
# concatenated into a real file. Re-run this script after editing claude-md/.
rm -f "$AGENTS_HOME/AGENTS.md"
{
echo "<!-- generated by agent-skills/bin/link.sh from claude-md/ — do not edit -->"
echo
cat "$REPO/claude-md/operating.md"
echo
cat "$REPO/claude-md/writing.md"
} > "$AGENTS_HOME/AGENTS.md"
echo "generated $AGENTS_HOME/AGENTS.md"
# Path-scoped rules load only when Claude reads a matching file.
link "$REPO/claude-md/code-comments.md" "$CLAUDE_RULES/code-comments.md"
rm -f "$CLAUDE_HOME/code-comments.md" "$HOME/.agents/AGENTS.md"
# Codex reads ~/.codex/AGENTS.md and supports @path imports, same as CLAUDE.md,
# so both tools import the identical fragments — no copy, no drift.
mkdir -p "$CODEX_HOME"
echo "done."
echo "hooks still need wiring in ~/.claude/settings.json — see hooks/README.md"
+24
View File
@@ -0,0 +1,24 @@
---
paths:
- "**/*.{ts,tsx,js,jsx,mjs,cjs,py,rs,go,sol,sh,bash,zsh,c,h,cc,cpp,hpp,java,kt,rb,lua,zig,nix,swift,php,cs,ex}"
---
## Code comments
Budget: 1-3 lines. Write one only when an agent with the repo and thirty
seconds of grep would still get it wrong. That means one of:
- a landmine no test catches
- a fact not in the repo (deployed bytecode, chain quirk, library bug)
- a unit or epoch the type cannot carry (wei, ms, 18-dp)
- a MUST or MUST NOT a caller can break
Good:
> `// MUST read lastGoodPrice before fetchPrice — same eth_call state.`
Everything else: delete. Design rationale and rejected alternatives go in
`docs/`; leave a path. History is in git. No emphasis; ALL-CAPS only for
MUST / NEVER.
Deleting a comment is cheap and reversible. When unsure, delete.
-20
View File
@@ -81,26 +81,6 @@ Commits: Conventional Commits, subject ≤50 chars, body only when the
"why" is not obvious. Review comments: one line per finding — location,
problem, fix.
### Code comments
Budget: 1-3 lines. Write one only when an agent with the repo and thirty
seconds of grep would still get it wrong. That means one of:
- a landmine no test catches
- a fact not in the repo (deployed bytecode, chain quirk, library bug)
- a unit or epoch the type cannot carry (wei, ms, 18-dp)
- a MUST or MUST NOT a caller can break
Good:
> `// MUST read lastGoodPrice before fetchPrice — same eth_call state.`
Everything else: delete. Design rationale and rejected alternatives go in
`docs/`; leave a path. History is in git. No emphasis; ALL-CAPS only for
MUST / NEVER.
Deleting a comment is cheap and reversible. When unsure, delete.
### Where this does not apply
Security warnings, irreversible-action confirmations, and legal or
+2 -2
View File
@@ -2,7 +2,7 @@
"""PostToolUse nudge for code comments: Write / Edit / MultiEdit.
Lints ONLY the newly-added text against the "Code comments" contract in
~/.claude/writing.md, so legacy files are not re-flagged on every touch.
~/.claude/rules/code-comments.md, so legacy files are not re-flagged on every touch.
Exit 0 = silent. Exit 2 = stderr goes back to Claude as feedback; the edit is
already applied, so this is a revise-it nudge, not a block.
@@ -157,7 +157,7 @@ def main():
name = os.path.basename(path)
if problems:
lines = [f"Comment contract (~/.claude/writing.md) — {name}:", ""]
lines = [f"Comment contract (~/.claude/rules/code-comments.md) — {name}:", ""]
lines += [f" - {p}" for p in problems + notes]
lines += ["", "Trim what you just wrote, or say why it stays."]
print("\n".join(lines), file=sys.stderr)
+3 -5
View File
@@ -21,11 +21,9 @@
".claude/writing.md".source = "${agent-skills}/claude-md/writing.md";
".claude/operating.md".source = "${agent-skills}/claude-md/operating.md";
# Codex has no @import, so the fragments are concatenated into one file.
".agents/AGENTS.md".text = builtins.concatStringsSep "\n" [
(builtins.readFile "${agent-skills}/claude-md/operating.md")
(builtins.readFile "${agent-skills}/claude-md/writing.md")
];
# Path-scoped: loads only when Claude reads a matching source file.
".claude/rules/code-comments.md".source = "${agent-skills}/claude-md/code-comments.md";
};
}
# Hook wiring lives in ~/.claude/settings.json, which this module does not own.