@martinmqz/agent-guidance-sync
v0.1.2
Published
Deterministically sync one canonical set of coding-agent guidance to AGENTS.md, CLAUDE.md, Cursor rules, and GitHub Copilot instructions.
Downloads
639
Maintainers
Readme
agent-guidance-sync
Deterministically sync one canonical set of coding-agent guidance to
AGENTS.md, CLAUDE.md, Cursor rules, and GitHub Copilot instructions.
This project is a local, one-way guidance compiler. It does not generate guidance with AI and it is not an MCP server.
Status
The current milestone supports safe initialization, repository-wide guidance, always-activated rules, path-activated Cursor and GitHub Copilot rules, optional directory-scoped AGENTS and Claude guidance, non-mutating sync previews, and machine-readable output.
Usage
Initialize the current Git repository, edit the new canonical guide, then sync and check the generated files:
npx @martinmqz/[email protected] init
# Edit .agents/guide.md
npx @martinmqz/[email protected] sync
npx @martinmqz/[email protected] checkinit reuses the nearest existing canonical source, otherwise finds the nearest
Git repository root from the current directory. When there is no Git repository,
it initializes the current directory. It creates
missing .agents/guide.md and .agents/config.yaml files, never overwrites an
existing source, and leaves output generation to an explicit sync.
Preview the exact sync plan without staging, publishing, or deleting files:
npx @martinmqz/agent-guidance-sync sync --dry-run
npx @martinmqz/agent-guidance-sync sync --force --dry-run--dry-run is available only on sync and honors --adopt and --force.
It exits 0 when the plan is safe to apply, including when changes are pending,
and exits 1 when unmanaged or unsafe targets block the plan.
JSON output
Pass --json to init, sync, or check to emit one JSON document. Normal
operational results are written to stdout, including out-of-sync and blocked
results that exit 1. Usage and runtime errors are written to stderr. JSON
mode preserves the command's normal exit codes:
0: the command succeeded; a dry run may still report pending changes.1: guidance is out of sync, a sync is blocked, or guidance could not be safely processed.2: command-line usage is invalid.
Every document has schemaVersion: 1, command, ok, and status. Command
statuses are stable within that schema:
| Command | Status values |
| --- | --- |
| init | initialized, unchanged |
| sync | synced, changes-planned, unchanged, blocked |
| check | in-sync, out-of-sync |
| Error | error |
sync and check include a deterministic plan. Each plan item exposes only
action, repository-relative path, and an optional reason; generated
contents and filesystem identity data are never serialized. Actions are
unchanged, create, update, adopt, replace, delete, conflict, or
unsafe. For example:
{"schemaVersion":1,"command":"sync","ok":true,"status":"changes-planned","root":"/repo","dryRun":true,"takeover":"none","plan":[{"action":"create","path":"AGENTS.md"}]}init --json still initializes the project. Use sync --dry-run --json for a
machine-readable, non-mutating sync preview.
Canonical format
.agents/config.yaml uses a deliberately strict, versioned YAML subset:
version: 1
adapters:
agents: true
claude: true
cursor: true
copilot: trueAll adapter keys are required. Disabling an adapter removes only outputs with
an exact agent-guidance-sync ownership marker. The Claude adapter requires the
AGENTS adapter because CLAUDE.md imports AGENTS.md.
Cursor and Copilot also accept rules-only, which generates path rules but
omits that adapter's repository-wide copy. With nested: true, it also omits
path-rule copies already represented by nested AGENTS.md files. This is useful
when the client reads native guidance: Cursor supports nested AGENTS.md,
and VS Code requires chat.useAgentsMdFile and chat.useNestedAgentsMdFiles.
Existing owned duplicate copies are removed; unmanaged root files are preserved.
Keep copilot: true when you need the root .github/copilot-instructions.md
and all path instructions for GitHub.com consumers. For example:
version: 1
adapters:
agents: true
claude: true
cursor: rules-only
copilot: true
nested: truenested is optional and defaults to false. Existing boolean adapter
configurations retain their behavior.
Rules live in lowercase kebab-case .agents/rules/**/*.md paths. An
always-activated rule is inlined into repository-wide outputs:
---
description: Shared testing guidance
activation: always
---
# Testing
Use the repository's existing test commands.A path-activated rule uses portable repository-relative globs:
---
description: React component guidance
activation: path
paths:
- "apps/web/**/*.tsx"
- "packages/ui/**/*.tsx"
---
# React Components
Prefer observable behavior over implementation details.Every rule body must begin with a level-one Markdown heading. Path-activated rules require at least one of the Cursor or GitHub Copilot adapters, unless every path rule can be represented by enabled nested guidance as described below.
Descriptions and paths may be unquoted, single-quoted, or JSON-style
double-quoted strings. Paths must use the indented list form shown above and
must not be absolute, negated, comma-separated, contain backslashes, ..
segments, Windows-invalid literal characters, or segments ending in a dot or
space. Windows reserved device-name segments are also rejected. Plain values
using YAML comments, mappings, collections, anchors, aliases, tags, or block
syntax must be quoted when a literal string is intended.
Only regular files with lowercase kebab-case .md names are treated as rules;
other .md spellings are rejected so guidance cannot disappear silently.
Auxiliary regular files such as .DS_Store, .gitkeep, README.md, and editor
swap files are ignored. Hidden auxiliary directories are ignored without being
traversed; symlinks and non-regular entries remain unsafe. A
leading UTF-8 byte-order mark is stripped from the guide, config, and rule files
before parsing or generation.
Nested directory guidance
With nested: true and agents: true, a rule with a single literal directory
followed by /** also generates <directory>/AGENTS.md. If claude: true, it
generates <directory>/CLAUDE.md with the same rule bodies inlined, without YAML
frontmatter or imports. For example, Scripts/** and
MileagePosting/AlaskaAir.MileagePosting.Cores/** each map to their own directory.
Rules for the same directory are combined in canonical rule-path order.
The directory must already exist, and every directory component must match its
on-disk spelling exactly, including case and Unicode spelling. Correct the rule
or explicitly create/rename the directory before syncing. The tool never creates
scope directories. Existing nested filenames must likewise match AGENTS.md or
CLAUDE.md exactly. These checks keep markers and inventory paths portable when
the repository moves between filesystems.
Patterns such as src/**/*.ts, wildcard directories, and rules spanning multiple
directories retain only their Cursor/Copilot output. A shared literal prefix
does not make a file-specific rule apply to every descendant, so the tool never
broadens these patterns into directory instructions. Rules for reserved .git,
.agents, and node_modules directories, or either reserved agent-guidance/
scoped-rule namespace, also retain only Cursor/Copilot output. Overlapping
generated target paths are rejected.
Commit the generated .agents/nested-outputs.json inventory alongside nested
files. It lets check and sync find obsolete nested outputs without scanning
the repository. Removing a rule, disabling an adapter, or removing nested: true
cleans up inventory-listed files only when their exact ownership markers still
match. Unmanaged or unsafe obsolete targets block cleanup even with --force.
For an unmanaged obsolete file, restore its generated contents from version
control, or move/remove that file explicitly before retrying. If the inventory
is invalid, restore the inventory itself from a known-good generated copy;
takeover flags cannot establish safe cleanup ownership. A UTF-8 BOM in the
inventory is accepted.
Before publishing new nested files, synchronization records both old and new
destinations in the inventory. It removes obsolete entries only after cleanup
succeeds, so a failed sync remains recoverable even if rules change before retry.
After an explicit directory rename, older inventory spellings can migrate owned
files when filesystem identities prove they refer to the same directory entry.
The tool preserves neighboring files and directories. Do not delete the inventory
manually: without it, old nested outputs cannot be discovered for cleanup.
No inventory is emitted when there are no nested outputs, including when the
AGENTS adapter is disabled. An obsolete owned inventory is removed after cleanup.
Nested files use the same default conflict, --adopt, and --force behavior as
root guidance. Replacing existing pointer stubs therefore requires explicitly
adopting an identical body or replacing them with sync --force after reviewing
the dry run. Client support varies: Cursor supports nested AGENTS.md,
Claude loads nested CLAUDE.md on demand,
and VS Code's nested AGENTS.md support requires its experimental setting.
The package installs the agent-guidance executable and generates:
| Agent | Generated target |
| --- | --- |
| Codex and compatible agents | AGENTS.md |
| Claude Code | CLAUDE.md |
| Optional directory guidance | <directory>/AGENTS.md, <directory>/CLAUDE.md |
| Cursor | .cursor/rules/agent-guidance.mdc |
| Cursor path rules | .cursor/rules/agent-guidance/**/*.mdc |
| GitHub Copilot | .github/copilot-instructions.md |
| Copilot path rules | .github/instructions/agent-guidance/**/*.instructions.md |
Run the command from the repository root or any descendant directory. The
nearest ancestor containing .agents/guide.md is used. Discovery never climbs
past a nested Git repository boundary.
Existing files
Generated files carry an exact, target-specific ownership marker at an
adapter-defined header position. By default, sync updates only missing or
already-owned files and refuses to make any changes when an unmanaged target
exists.
agent-guidance sync --adoptclaims an unmanaged file only when its payload already matches the generated payload exactly.agent-guidance sync --forcereplaces differing unmanaged regular files.- Symlinks and non-regular targets detected during planning, staging, or commit are not followed, removed, or replaced, including in force mode. Missing targets use no-clobber publication so a late-created path cannot be replaced.
- The two
agent-guidance/scoped-rule directories are reserved generated namespaces. Obsolete files are removed only when their exact marker proves ownership; unmanaged or unsafe entries block the entire sync, including with--force.
All created or updated output is staged and flushed before publication.
Existing files use a same-directory atomic rename; missing files use an atomic
hard-link publication. The filesystem must support hard links for missing-file
publication; the CLI does not fall back to a partially visible copy that would
weaken its atomic no-clobber guarantee. Obsolete files are identity-checked
again immediately before removal. The multi-file commit is not transactional
if the operating system fails during publication, and the tool does not claim
protection from a hostile process that mutates an existing destination inside
the final filesystem-syscall window. After any failed sync, rerun check before
retrying. check uses the same plan as sync and never changes files.
Final validation is sequential and does not create an atomic snapshot across
the separate canonical and generated paths. A non-cooperating process can
modify an earlier path after its last validation read but before another path
is checked or sync returns. Environments that permit concurrent writers must
serialize them externally and run check after sync.
Generated-file comparisons treat CRLF and LF checkouts as equivalent while new
output is emitted with LF. This avoids fresh Windows checkouts reporting drift
solely because Git applied core.autocrlf.
Migrating older prototypes
Versions through 0.1.1 generated Cursor globs as JSON arrays. Version 0.1.2
emits the bare comma-separated format Cursor expects.
An ordinary sync migrates old owned files without --force; obsolete files
using the old format remain eligible for safe cleanup.
Repositories that already have .agents/guide.md from an earlier release can
rerun agent-guidance init. It adds the missing .agents/config.yaml without
reading or overwriting the existing guide.
Repositories using .agents/AGENTS.md and Cursor-shaped canonical .mdc rules
must convert them to .agents/guide.md and the vendor-neutral rule format
above before syncing. Legacy scoped output files live outside this package's
reserved namespaces and are deliberately preserved; remove them explicitly
only after the new generated files pass check.
Development
npm testThe implementation has no runtime dependencies and requires Node.js 20 or newer. The synchronous library API uses process working-directory pinning to preserve no-follow filesystem guarantees and therefore must run on the main Node.js thread rather than inside a worker thread.
License
MIT
