| Crates.io | agentsmd |
| lib.rs | agentsmd |
| version | 0.0.2 |
| created_at | 2025-08-09 04:36:41.894267+00 |
| updated_at | 2025-08-09 06:05:49.980646+00 |
| description | Generate per-project AGENTS.md from templates |
| homepage | |
| repository | https://github.com/cortesi/agentsmd |
| max_upload_size | |
| id | 1787568 |
| size | 84,254 |
Generate per‑project AGENTS.md and CLAUDE.md files by combining a
project‑local template at <project-root>/.agents.md with a shared template at
~/.agents.md. Templates evaluate matchers against the target project (e.g.,
lang(rust)) to conditionally include blocks, letting you tailor the output to
your project.
.git/ or other VCS markers~/.agents.md (shared) and <project-root>/.agents.md (local)exists("**/*.rs") and env(CI)AGENTS.md (and optionally CLAUDE.md) to the project rootcargo install agentsmd
# Render for the current project and write AGENTS.md to the project root
agentsmd
# Render for the current project and write AGENTS.md to the project root
agentsmd --claude
# Render for a specific project path and write AGENTS.md to the project root
agentsmd /path/to/project
--template <path> Override template (defaults to ~/.agents.md)
--root <path> Force project root (skip detection)
--stdout Print instead of writing AGENTS.md
--diff Show a unified diff of pending changes, do not write
--quiet Suppress default diff output when writing changes
--claude Also write CLAUDE.md alongside AGENTS.md
--out <path> Override output file path (relative to project root if not absolute)
-V, --version Print version
-h, --help Help
The tool walks upward from the provided path (or current working directory) and picks the first match:
.git/, .hg/, or .svn/ (preferred)Cargo.lockIf neither is found, agents exits with a non‑zero status. The resolved root is where AGENTS.md is written.
Templates are just Markdown with HTML‑comment control tags, so they still
render cleanly if opened directly. We use comments so templates remain readable
and portable across viewers. The control syntax stays invisible in rendered
Markdown, diff‑friendly in git, and avoids executing arbitrary code - only
simple boolean checks (e.g., exists, env).
<!-- if exists("**/*.rs") -->
This project contains Rust sources.
<!-- endif -->
&&, ||, ! and parentheses.exists('src/**/{main,lib}.rs'), exists("Cargo.toml"),
exists(r"**/*.rs").Example:
<!-- if exists("package.json") && !exists("pnpm-lock.yaml") -->
Using npm or yarn (no pnpm lockfile found).
<!-- endif -->
exists(pattern): true if any non‑ignored file under the project root matches
the pattern. Matching uses the Rust ignore crate (gitignore semantics).
ignore/globset: *, ?, ** (recursive),
and {a,b} alternation..gitignore, .ignore, and .git/info/exclude — paths
ignored there are skipped and will not match.exists("**/*.rs")exists("Cargo.toml")exists('src/**/{main,lib}.rs')env(NAME): true if environment variable NAME is set and non‑empty in the current process environment.env(NAME=value): true if environment variable NAME exists and exactly equals value (string comparison).
env("MY FLAG"="on").env(CI)env(NODE_ENV=production)env(RUST_LOG=debug)lang(name): true if any non‑ignored file under the project root has a file
extension associated with the given programming language name. Language
lookup is powered by the languages
crate and is case‑insensitive.
lang(rust), lang("TypeScript"), lang(r"C++")| Element | Syntax | Notes |
|---|---|---|
| Conditional block | <!-- if EXPR --> … <!-- endif --> |
HTML‑comment control tags; blocks may nest; endif cannot have trailing content. |
| Operators | !, &&, ` |
|
Matcher: exists |
exists(PATTERN) |
Gitignore/globset pattern, relative to project root; matches files only; respects .gitignore, .ignore, and git excludes. |
Matcher: env (exists) |
env(NAME) |
True when env var is set and non‑empty; NAME may be quoted or bare. |
Matcher: env (equals) |
env(NAME=VALUE) |
True when env var exists and equals VALUE (string compare); NAME/VALUE may be quoted or raw. |
Matcher: lang |
lang(NAME) |
True when any file matches extensions for language NAME (case‑insensitive); unknown names are errors. |
| Strings | '...', "...", r"...", or bare token |
Quoted strings support \n, \r, \t, \\, \', \"; raw strings take contents verbatim; bare tokens end at whitespace or ). |
| Other comments | <!-- … --> |
Non‑control comments are preserved verbatim in output. |
| Parse errors | — | Unclosed if, stray endif, trailing characters in expressions, invalid glob patterns, and unknown languages cause a non‑zero exit. |
~/.agents.md (shared template)
<!-- if lang(rust) -->
run cargo clippy before finalising work and fix all warnings
<!-- endif -->
<project>/.agents.md (per‑project template)
# Project agentsmd
Project description
Rendered AGENTS.md (local then shared)
# Project Agents
Project description
run cargo clippy before finalising work and fix all warnings
endif, invalid expression, unknown
matcher): the process exits with a non‑zero status and does not write
output.<project-root>/.agents.md nor a shared
template is present/readable, agents exits with a non‑zero status.agents with the same inputs (shared template,
local .agents.md, project tree, and env) yields byte‑identical
AGENTS.md. Re‑running without changes results in no diff and no rewrite.Want to contribute? Have ideas or feature requests? Come tell us about it on Discord.