diff --git a/plugins/README.md b/plugins/README.md index cf4a21ecc5..6afcb7e94d 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -12,6 +12,7 @@ Learn more in the [official plugins documentation](https://docs.claude.com/en/do | Name | Description | Contents | |------|-------------|----------| +| [agents-md](./agents-md/) | Loads `AGENTS.md` files into context at session start, making Claude Code compatible with the cross-tool agent instruction standard used by Cursor, Codex, and Amp ([#6235](https://github.com/anthropics/claude-code/issues/6235)) | **Hook:** SessionStart - Injects `AGENTS.md` content as `additionalContext`, mirroring `CLAUDE.md` discovery | | [agent-sdk-dev](./agent-sdk-dev/) | Development kit for working with the Claude Agent SDK | **Command:** `/new-sdk-app` - Interactive setup for new Agent SDK projects
**Agents:** `agent-sdk-verifier-py`, `agent-sdk-verifier-ts` - Validate SDK applications against best practices | | [claude-opus-4-5-migration](./claude-opus-4-5-migration/) | Migrate code and prompts from Sonnet 4.x and Opus 4.1 to Opus 4.5 | **Skill:** `claude-opus-4-5-migration` - Automated migration of model strings, beta headers, and prompt adjustments | | [code-review](./code-review/) | Automated PR code review using multiple specialized agents with confidence-based scoring to filter false positives | **Command:** `/code-review` - Automated PR review workflow
**Agents:** 5 parallel Sonnet agents for CLAUDE.md compliance, bug detection, historical context, PR history, and code comments | @@ -25,6 +26,7 @@ Learn more in the [official plugins documentation](https://docs.claude.com/en/do | [pr-review-toolkit](./pr-review-toolkit/) | Comprehensive PR review agents specializing in comments, tests, error handling, type design, code quality, and code simplification | **Command:** `/pr-review-toolkit:review-pr` - Run with optional review aspects (comments, tests, errors, types, code, simplify, all)
**Agents:** `comment-analyzer`, `pr-test-analyzer`, `silent-failure-hunter`, `type-design-analyzer`, `code-reviewer`, `code-simplifier` | | [ralph-wiggum](./ralph-wiggum/) | Interactive self-referential AI loops for iterative development. Claude works on the same task repeatedly until completion | **Commands:** `/ralph-loop`, `/cancel-ralph` - Start/stop autonomous iteration loops
**Hook:** Stop - Intercepts exit attempts to continue iteration | | [security-guidance](./security-guidance/) | Security reminder hook that warns about potential security issues when editing files | **Hook:** PreToolUse - Monitors 9 security patterns including command injection, XSS, eval usage, dangerous HTML, pickle deserialization, and os.system calls | +| [tmp-cwd-cleanup](./tmp-cwd-cleanup/) | Cleans up orphaned `/tmp/claude-*-cwd` files on session exit, working around a Bash tool memory leak ([#8856](https://github.com/anthropics/claude-code/issues/8856)) | **Hook:** Stop - Deletes accumulated working-directory tracking files owned by the current user | ## Installation diff --git a/plugins/agents-md/.claude-plugin/plugin.json b/plugins/agents-md/.claude-plugin/plugin.json new file mode 100644 index 0000000000..39b9f66ca3 --- /dev/null +++ b/plugins/agents-md/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "agents-md", + "version": "1.0.0", + "description": "Automatically loads AGENTS.md files into context at session start, making Claude Code compatible with cross-tool agent instruction files used by Cursor, Codex, Amp, and others", + "author": { + "name": "Claude Code Community", + "email": "" + } +} diff --git a/plugins/agents-md/README.md b/plugins/agents-md/README.md new file mode 100644 index 0000000000..ed1d2164e8 --- /dev/null +++ b/plugins/agents-md/README.md @@ -0,0 +1,64 @@ +# agents-md + +Automatically loads `AGENTS.md` files into Claude Code's context at session start, making it compatible with the cross-tool agent instruction standard used by Cursor, OpenAI Codex, Amp, and others. + +**Fixes:** [#6235 – Feature Request: Support AGENTS.md](https://github.com/anthropics/claude-code/issues/6235) + +## The problem + +Claude Code reads `CLAUDE.md` (and `CLAUDE.local.md`) from the project directory tree to understand project-specific instructions. Most other AI coding tools use `AGENTS.md` for the same purpose — this is coalescing into a [cross-tool standard](https://agents.md/). + +Without this plugin, teams that use both Claude Code and other tools must either: +- Maintain two separate instruction files (`CLAUDE.md` and `AGENTS.md`) in sync, or +- Symlink one to the other (fragile on Windows, confusing for contributors) + +## What this plugin does + +Registers a `SessionStart` hook that mirrors Claude Code's own `CLAUDE.md` discovery logic: + +1. Walks up the directory tree from the project root, collecting every `AGENTS.md` found. +2. Also checks `~/.claude/AGENTS.md` for user-level instructions. +3. Injects all found content as `additionalContext` — the same mechanism used internally for `CLAUDE.md`. + +The file lookup order (lowest → highest priority) matches `CLAUDE.md` precedence: + +``` +~/.claude/AGENTS.md ← user-level (lowest priority) +/path/to/repo/AGENTS.md ← repo root +/path/to/repo/subdir/AGENTS.md ← subdirectory (highest priority) +``` + +## Installation + +Install via the `/plugin` command inside Claude Code: + +``` +/plugin install agents-md +``` + +Or add it manually to your `.claude/settings.json`: + +```json +{ + "plugins": ["agents-md"] +} +``` + +## Usage + +Just drop an `AGENTS.md` in your project root (the same place you'd put `CLAUDE.md`) and start a Claude Code session. The file is loaded automatically — no configuration needed. + +``` +my-project/ +├── AGENTS.md ← loaded by this plugin (and by Cursor, Codex, Amp, …) +├── CLAUDE.md ← loaded natively by Claude Code +└── src/ +``` + +Both files are loaded; `CLAUDE.md` instructions take precedence where they overlap. + +## Hook + +| Event | Effect | +|-------|--------| +| `SessionStart` | Finds and injects `AGENTS.md` file(s) as `additionalContext` | diff --git a/plugins/agents-md/hooks/hooks.json b/plugins/agents-md/hooks/hooks.json new file mode 100644 index 0000000000..97be8b3f13 --- /dev/null +++ b/plugins/agents-md/hooks/hooks.json @@ -0,0 +1,15 @@ +{ + "description": "Loads AGENTS.md files into context at session start, mirroring how Claude Code handles CLAUDE.md", + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/load_agents_md.py" + } + ] + } + ] + } +} diff --git a/plugins/agents-md/hooks/load_agents_md.py b/plugins/agents-md/hooks/load_agents_md.py new file mode 100644 index 0000000000..32f8720724 --- /dev/null +++ b/plugins/agents-md/hooks/load_agents_md.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +"""SessionStart hook: inject AGENTS.md files as additionalContext. + +Claude Code natively reads CLAUDE.md (and CLAUDE.local.md) from the project +tree and the user's ~/.claude/ directory. Many other AI coding tools — Cursor, +OpenAI Codex, Amp, and others — use AGENTS.md for the same purpose, creating +a cross-tool standard. + +This hook mirrors Claude Code's own CLAUDE.md discovery logic: + 1. Walk up from the project root, collecting every AGENTS.md found. + 2. Also check ~/.claude/AGENTS.md for user-level instructions. + +All found files are concatenated and injected via additionalContext so Claude +sees them exactly as if they had been written in CLAUDE.md. + +See: https://github.com/anthropics/claude-code/issues/6235 + https://agents.md/ +""" + +import json +import os +import sys +from pathlib import Path + + +FILENAME = "AGENTS.md" +# Limit how many directory levels we climb (same spirit as CLAUDE.md behaviour). +MAX_DEPTH = 20 + + +def find_agents_md_files(start_dir: Path) -> list[Path]: + """Walk upward from start_dir collecting AGENTS.md files (innermost first).""" + found: list[Path] = [] + current = start_dir.resolve() + + for _ in range(MAX_DEPTH): + candidate = current / FILENAME + if candidate.is_file(): + found.append(candidate) + parent = current.parent + if parent == current: + break # reached filesystem root + current = parent + + return found + + +def main() -> None: + # Consume stdin (required by hook protocol). + try: + hook_input = json.load(sys.stdin) + except Exception: + hook_input = {} + + # Determine the project root. The hook runs with cwd set to the project + # directory; fall back to CLAUDE_PROJECT_ROOT if provided. + project_root_env = os.environ.get("CLAUDE_PROJECT_ROOT", "") + start_dir = Path(project_root_env) if project_root_env else Path.cwd() + + # 1. Collect project-tree AGENTS.md files (closest → furthest from root). + project_files = find_agents_md_files(start_dir) + + # 2. User-level AGENTS.md at ~/.claude/AGENTS.md (lowest priority). + user_file = Path.home() / ".claude" / FILENAME + user_files = [user_file] if user_file.is_file() else [] + + # Final order: user-level first (lowest priority), then project files + # from outermost → innermost (matching how CLAUDE.md precedence works). + all_files = user_files + list(reversed(project_files)) + + if not all_files: + # Nothing to inject — exit silently. + sys.exit(0) + + # Read and concatenate. + sections: list[str] = [] + for path in all_files: + try: + content = path.read_text(encoding="utf-8").strip() + if content: + sections.append( + f"\n{content}" + ) + except OSError: + pass # Unreadable file — skip silently. + + if not sections: + sys.exit(0) + + combined = "\n\n".join(sections) + context = ( + "The following instructions come from AGENTS.md file(s) found in this " + "project. AGENTS.md is a cross-tool standard (used by Cursor, Codex, " + "Amp, and others) equivalent to CLAUDE.md. Treat these instructions " + "with the same weight as CLAUDE.md:\n\n" + combined + ) + + output = { + "hookSpecificOutput": { + "hookEventName": "SessionStart", + "additionalContext": context, + } + } + print(json.dumps(output)) + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/plugins/tmp-cwd-cleanup/.claude-plugin/plugin.json b/plugins/tmp-cwd-cleanup/.claude-plugin/plugin.json new file mode 100644 index 0000000000..a59c5ce297 --- /dev/null +++ b/plugins/tmp-cwd-cleanup/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "tmp-cwd-cleanup", + "version": "1.0.0", + "description": "Cleans up orphaned /tmp/claude-*-cwd working directory tracking files on session stop, working around a memory leak in the Bash tool (issue #8856)", + "author": { + "name": "Claude Code Community", + "email": "" + } +} diff --git a/plugins/tmp-cwd-cleanup/README.md b/plugins/tmp-cwd-cleanup/README.md new file mode 100644 index 0000000000..851a95c7cf --- /dev/null +++ b/plugins/tmp-cwd-cleanup/README.md @@ -0,0 +1,41 @@ +# tmp-cwd-cleanup + +A workaround plugin for [issue #8856](https://github.com/anthropics/claude-code/issues/8856): the Bash tool creates a `/tmp/claude--cwd` file after every shell command to track the working directory, but never deletes it. On active systems these files accumulate into the thousands and eventually slow down `/tmp` lookups. + +## What it does + +Registers a **Stop hook** that deletes all `/tmp/claude-*-cwd` files owned by the current user when the Claude Code session ends. + +``` +/tmp/claude-02a6-cwd (22 bytes — deleted on exit) +/tmp/claude-1f3b-cwd (22 bytes — deleted on exit) +... +``` + +## Installation + +Install via the `/plugin` command inside Claude Code: + +``` +/plugin install tmp-cwd-cleanup +``` + +Or add it manually to your `.claude/settings.json`: + +```json +{ + "plugins": ["tmp-cwd-cleanup"] +} +``` + +## Notes + +- Only removes files owned by the **current user** (safe on shared systems). +- Exits `0` so it never blocks session teardown. +- This plugin is a stopgap; the proper fix is to call `unlinkSync` in the Bash tool implementation immediately after reading the cwd file ([upstream tracking issue #8856](https://github.com/anthropics/claude-code/issues/8856)). + +## Hook + +| Event | Matcher | Effect | +|-------|---------|--------| +| `Stop` | _(all)_ | Deletes `/tmp/claude-*-cwd` files owned by current user | diff --git a/plugins/tmp-cwd-cleanup/hooks/cleanup_cwd_files.py b/plugins/tmp-cwd-cleanup/hooks/cleanup_cwd_files.py new file mode 100644 index 0000000000..5284541176 --- /dev/null +++ b/plugins/tmp-cwd-cleanup/hooks/cleanup_cwd_files.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +"""Stop hook: clean up orphaned /tmp/claude-*-cwd files. + +The Bash tool writes a small temp file to /tmp/claude--cwd after each +command to capture the resulting working directory, but never deletes it. +On active systems these files accumulate into the thousands and eventually +slow down /tmp lookups (see issue #8856). + +This hook runs on session Stop and removes any such files owned by the +current user, providing a clean-up path until the upstream fix lands. +""" + +import glob +import json +import os +import sys + + +def main() -> None: + # Consume stdin (required by the hook protocol) but we don't need it. + try: + json.load(sys.stdin) + except Exception: + pass + + uid = os.getuid() if hasattr(os, "getuid") else None # no-op on Windows + removed = 0 + errors = 0 + + for path in glob.glob("/tmp/claude-*-cwd"): + try: + # Only remove files owned by the current user (safety check). + if uid is not None and os.stat(path).st_uid != uid: + continue + if os.path.isfile(path): + os.unlink(path) + removed += 1 + except OSError: + errors += 1 + + # Exit 0 so the Stop event is never blocked. + # Optionally surface a brief summary via systemMessage. + if removed > 0: + result = {"systemMessage": f"tmp-cwd-cleanup: removed {removed} orphaned file(s)."} + if errors: + result["systemMessage"] += f" ({errors} error(s) skipped)" + print(json.dumps(result)) + + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/plugins/tmp-cwd-cleanup/hooks/hooks.json b/plugins/tmp-cwd-cleanup/hooks/hooks.json new file mode 100644 index 0000000000..d3e4d53bc4 --- /dev/null +++ b/plugins/tmp-cwd-cleanup/hooks/hooks.json @@ -0,0 +1,15 @@ +{ + "description": "Cleans up orphaned /tmp/claude-*-cwd working directory tracking files when the session stops", + "hooks": { + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/cleanup_cwd_files.py" + } + ] + } + ] + } +}