rulesync 43.0.0 → 43.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli/index.cjs +2 -2
- package/dist/cli/index.js +2 -2
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](https://github.com/dyoshikawa/rulesync/actions/workflows/ci.yml)
|
|
8
8
|
[](https://www.npmjs.com/package/rulesync)
|
|
9
9
|
[](https://www.npmjs.com/package/rulesync)
|
|
10
|
-
[](https://deepwiki.com/dyoshikawa/rulesync)
|
|
11
11
|
[](https://github.com/hesreallyhim/awesome-claude-code)
|
|
12
12
|
[](https://github.com/Piebald-AI/awesome-gemini-cli)
|
|
13
13
|
<a href="https://flatt.tech/oss/gmo/trampoline" target="_blank"><img src="https://flatt.tech/assets/images/badges/gmo-oss.svg" height="24px"/></a>
|
package/dist/cli/index.cjs
CHANGED
|
@@ -11413,7 +11413,7 @@ async function convertCommand(logger, options) {
|
|
|
11413
11413
|
const DOCS_CONTENT = {
|
|
11414
11414
|
"api/programmatic-api": "# Programmatic API\n\nRulesync can be used as a library in your Node.js/TypeScript projects. The `generate`, `importFromTool`, and `convertFromTool` functions are available as named exports.\n\n```typescript\nimport { convertFromTool, generate, importFromTool } from \"rulesync\";\n\n// Generate configurations\nconst result = await generate({\n targets: [\"claudecode\", \"cursor\"],\n features: [\"rules\", \"mcp\"],\n});\nconsole.log(`Generated ${result.rulesCount} rules, ${result.mcpCount} MCP configs`);\n\n// Import existing tool configurations into .rulesync/\nconst importResult = await importFromTool({\n target: \"claudecode\",\n features: [\"rules\", \"commands\"],\n});\nconsole.log(`Imported ${importResult.rulesCount} rules`);\n\n// Convert configurations between AI tools without writing intermediate .rulesync/ files\ntry {\n const convertResult = await convertFromTool({\n from: \"claudecode\",\n to: [\"cursor\", \"copilot\"],\n features: [\"rules\"],\n });\n console.log(`Converted ${convertResult.rulesCount} rule file(s)`);\n} catch (error) {\n // Thrown when `from` is empty, `to` is empty, `to` includes `from`,\n // a source file cannot be parsed, or write fails.\n console.error(\"convert failed:\", error);\n}\n```\n\n## `generate(options?)`\n\nGenerates configuration files for the specified targets and features.\n\n| Option | Type | Default | Description |\n| ------------------- | -------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `targets` | `ToolTarget[]` | from config file | Tools to generate configurations for |\n| `features` | `Feature[]` | from config file | Features to generate |\n| `outputRoots` | `string[]` | `[process.cwd()]` | Output root directories to generate files into |\n| `inputRoots` | `string[]` | `[<cwd>/.rulesync]` | Ordered list of rulesync source-tree directories (e.g. `.rulesync`, `.rulesync.local`). Each entry is a source tree itself — no `.rulesync/` join is applied. The first root is required; later roots are optional overlays and may be absent. Output still goes to each `outputRoots` entry; only the input source root is redirected. Later entries override earlier ones for the same relative source path. Cannot be combined with `inputRoot`. Mirrors the CLI's `--input-roots`. |\n| `inputRoot` | `string` | `process.cwd()` | **Deprecated.** PARENT directory of a `.rulesync/` source tree; kept as a backward-compatibility alias that expands internally to `inputRoots: [join(inputRoot, \".rulesync\")]`. Prefer `inputRoots` and point it directly at your source tree(s). Cannot be combined with `inputRoots`. Mirrors the CLI's `--input-root`. |\n| `configPath` | `string` | auto-detected | Path to `rulesync.jsonc` |\n| `verbose` | `boolean` | `false` | Enable verbose logging |\n| `silent` | `boolean` | `true` | Suppress all output |\n| `delete` | `boolean` | from config file | Delete existing files before generating |\n| `global` | `boolean` | `false` | Generate global (user scope) configurations |\n| `simulateCommands` | `boolean` | `false` | Generate simulated commands |\n| `simulateSubagents` | `boolean` | `false` | Generate simulated subagents |\n| `simulateSkills` | `boolean` | `false` | Generate simulated skills |\n| `dryRun` | `boolean` | `false` | Show changes without writing files |\n| `check` | `boolean` | `false` | Exit with code 1 if files are not up to date |\n\n> **Unreadable sources do not throw here.** Unlike the CLI, which exits\n> non-zero, `generate()` resolves and reports the problem on the result:\n> `sourceLoadFailed` is `true` and `sourceLoadFailedFeatures` names the features\n> whose `.rulesync/` source could not be read. Their counts are `0`, exactly like\n> a feature that had nothing to write, so check the flag rather than the counts\n> before treating a run as successful. (Those features also keep their existing\n> generated files: `delete` skips their orphan sweep.)\n\nWith `delete: true`, the result's `deletedPathsByFeature` lists what the orphan\nsweep deleted — or, with `dryRun` or `check`, would delete — per feature, as\n`{ path, kind }` entries (`kind` is `\"file\"` or `\"directory\"`, `path` is\nrelative to the output root and sorted). A feature that deleted nothing is\nabsent. `keyOperationsByFeature` lists, the same way but regardless of\n`delete`, the top-level keys\nRulesync owns outright in a shared configuration file (`.codex/config.toml`,\n`.claude/settings.json`, …) that a write added or changed (`action: \"write\"`)\nor removed (`action: \"delete\"`), as `{ action, path, key }` entries sorted by\npath and then key. The CLI's `--json` output turns these and the written paths\ninto a versioned `plan`; see [CLI Commands](../reference/cli-commands.md).\n\n## `importFromTool(options)`\n\nImports existing tool configurations into `.rulesync/` directory.\n\n| Option | Type | Default | Description |\n| ------------ | ------------ | ---------------- | ----------------------------------------- |\n| `target` | `ToolTarget` | (required) | Tool to import configurations from |\n| `features` | `Feature[]` | from config file | Features to import |\n| `configPath` | `string` | auto-detected | Path to `rulesync.jsonc` |\n| `verbose` | `boolean` | `false` | Enable verbose logging |\n| `silent` | `boolean` | `true` | Suppress all output |\n| `global` | `boolean` | `false` | Import global (user scope) configurations |\n\n## `convertFromTool(options)`\n\nConverts configuration files between AI tools without writing intermediate `.rulesync/` files to disk.\n\n| Option | Type | Default | Description |\n| ------------ | -------------- | ------------- | ------------------------------------------------------------------------------------------- |\n| `from` | `ToolTarget` | (required) | Source tool to convert configurations from |\n| `to` | `ToolTarget[]` | (required) | Destination tools to convert to |\n| `features` | `Feature[]` | `[\"*\"]` | Features to convert. Matches CLI behavior and overrides any `features` in `rulesync.jsonc`. |\n| `configPath` | `string` | auto-detected | Path to `rulesync.jsonc` |\n| `verbose` | `boolean` | `false` | Enable verbose logging |\n| `silent` | `boolean` | `true` | Suppress all output |\n| `global` | `boolean` | `false` | Convert global (user scope) configurations |\n| `dryRun` | `boolean` | `false` | Show changes without writing files |\n",
|
|
11415
11415
|
faq: "# FAQ\n\n## `rulesync generate` doesn't produce what I expect\n\nRun `rulesync doctor` first. It performs read-only diagnostics on `rulesync.jsonc` and `rulesync.local.jsonc` and reports problems the generator silently tolerates — most importantly misspelled or unknown configuration keys (the config schema is non-strict, so a typo like `\"target\"` instead of `\"targets\"` is otherwise ignored and generation quietly falls back to defaults). See the [Doctor Command](./reference/cli-commands.md#doctor-command) reference for the full list of checks.\n\n## The generated `.mcp.json` doesn't work properly in Claude Code\n\nYou can try adding the following to `.claude/settings.json` or `.claude/settings.local.json`:\n\n```diff\n{\n+ \"enableAllProjectMcpServers\": true\n}\n```\n\nAccording to [the documentation](https://code.claude.com/docs/en/settings), this means:\n\n> Automatically approve all MCP servers defined in project .mcp.json files\n\n## Google Antigravity doesn't load rules when `.agents` directories are in `.gitignore`\n\nGoogle Antigravity has a known limitation where it won't load rules, workflows, and skills if the `.agents/rules/`, `.agents/workflows/`, and `.agents/skills/` directories are listed in `.gitignore`, even with \"Agent Gitignore Access\" enabled.\n\n> **Note:** Antigravity 2.0 uses the plural `.agents/` directory by default (the `antigravity-ide` and `antigravity-cli` targets).\n\n**Workaround:** Instead of adding these directories to `.gitignore`, add them to `.git/info/exclude`. `rulesync gitignore` can do this for you when `gitignoreDestination` is set to `\"gitexclude\"` (for example in `rulesync.local.jsonc`, or only for the Antigravity targets — see [Configuration](./guide/configuration.md#per-feature-options)). Run it from the repository root. Because `.gitignore` takes precedence over the exclude file, also route every other target that writes the same paths (for example Codex CLI's `**/.agents/skills/`) to `\"gitexclude\"` — the root-level setting does this — or leave them out of `targets`; otherwise the shared entry is still written to `.gitignore`. To do it by hand:\n\n```bash\n# Remove from .gitignore (if present)\n# **/.agents/rules/\n# **/.agents/workflows/\n# **/.agents/skills/\n\n# Add to .git/info/exclude\necho \"**/.agents/rules/\" >> .git/info/exclude\necho \"**/.agents/workflows/\" >> .git/info/exclude\necho \"**/.agents/skills/\" >> .git/info/exclude\n```\n\n`.git/info/exclude` works like `.gitignore` but is local-only, so it won't affect Antigravity's ability to load the rules while still excluding these directories from Git.\n\nNote: `.git/info/exclude` can't be shared with your team since it's not committed to the repository.\n\n## Codex CLI denies SSH agent access, temp-dir writes, or reading its own config with a generated permissions profile\n\nThe `[permissions.rulesync]` profile that rulesync generates into `.codex/config.toml` extends Codex CLI's `:workspace` baseline. That baseline is deliberately conservative, so day-to-day development can still hit permission denials: `git push`/`git fetch` over SSH cannot reach the SSH agent socket, some build tools fail without a writable temp dir, and Codex may be blocked from reading its own `~/.codex` configuration.\n\nrulesync emits the `.git` write carve-out for you (`\".git/**\" = \"write\"` under `:workspace_roots`; opt out with the `codexcli.git_write_rules: false` override; it is left out when a broader workspace-relative deny or read-only rule such as `read: { \"**\": \"deny\" }` covers `.git`). The whole subtree — including `.git/config` — is writable, because everyday commands such as `git remote add`, `git push -u`, and local-scope `git config` write to the repository config; users who want stricter isolation can add their own `read` override (e.g. `read: { \".git/config\": \"allow\" }`) in the canonical permissions. Everything below, however, depends on your environment or workflow, so rulesync does not add it by default. Where to put each piece differs, because the two tables are managed differently:\n\n**Network settings: edit `.codex/config.toml` directly.** Network settings are out of rulesync's management scope by design, keeping you free to edit them. rulesync preserves user-authored network keys when it regenerates the file — `network.enabled` (as long as the profile carries no rulesync-managed allow domains) and unknown keys such as `dangerously_allow_all_unix_sockets` are carried forward verbatim, with a warning so they stay visible:\n\n```toml\n[permissions.rulesync.network]\nenabled = true\n# Simplest option: allow all unix sockets. Codex names this \"dangerously_*\"\n# because it is broad, but it avoids hardcoding an env-dependent socket path.\ndangerously_allow_all_unix_sockets = true\n\n# Stricter alternative: allow only the SSH agent socket.\n# Replace the path with the actual value of $SSH_AUTH_SOCK on your machine;\n# Codex does not expand environment variables in these keys.\n# [permissions.rulesync.network.unix_sockets]\n# \"/path/to/ssh-agent.sock\" = \"allow\"\n```\n\n**Filesystem entries: author them in `.rulesync/permissions.jsonc`, not in `config.toml`.** The profile's `filesystem` table is fully managed — hand-written entries there are replaced on the next `rulesync generate`. Add the rules to the canonical config instead (use the tool-scoped `codexcli.permission` block so they do not leak into other tools' outputs) and regenerate:\n\n```jsonc\n{\n \"permission\": {\n // ...your shared rules...\n },\n \"codexcli\": {\n \"permission\": {\n \"write\": {\n \".\": \"allow\",\n \".git/**\": \"allow\",\n \".agents/**\": \"allow\",\n \".codex/**\": \"allow\",\n \":root\": \"allow\",\n \":minimal\": \"allow\",\n \":tmpdir\": \"allow\",\n \":slash_tmp\": \"allow\",\n },\n \"read\": { \"~/.codex/**\": \"allow\", \"~/.codex/auth.json\": \"deny\" },\n },\n },\n}\n```\n\nNote that this example is intentionally permissive: the `\":root\"` + `\":minimal\"` write pair grants the sandbox full disk write access — see the trade-off in the entry list below for narrower alternatives.\n\nThis generates into the profile as `\".\" = \"write\"`, `\".git/**\" = \"write\"`, `\".agents/**\" = \"write\"`, and `\".codex/**\" = \"write\"` under `:workspace_roots`, plus `\":root\" = \"write\"`, `\":minimal\" = \"write\"`, `\":tmpdir\" = \"write\"`, `\":slash_tmp\" = \"write\"`, `\"~/.codex/**\" = \"read\"`, and `\"~/.codex/auth.json\" = \"deny\"`, and round-trips through `rulesync import` — with two exceptions. First, `\".git/**\" = \"write\"` matches rulesync's default carve-out exactly, so import skips it (it is re-added on every generate); if you later opt out with `codexcli.git_write_rules: false` after an import, re-author the `\".git/**\": \"allow\"` write rule in the canonical config. Second, `\":minimal\"` is never imported regardless of its value — rulesync treats it as its fixed `\"read\"` baseline — so after an import, re-author the `\":minimal\": \"allow\"` write rule as well or the next generate silently drops back to `\":minimal\" = \"read\"`. Note that a tool-scoped category replaces the shared one wholesale for Codex CLI: if your shared `permission` block already has `read`/`write` rules that should also apply to Codex CLI, repeat them inside `codexcli.permission`.\n\nWhat each entry does:\n\n- **Unix socket access**: `git push`/`git fetch` over SSH needs the agent socket. `dangerously_allow_all_unix_sockets = true` is the simple, environment-independent option; a per-socket `unix_sockets` allow entry with the resolved `$SSH_AUTH_SOCK` path is the stricter one.\n- **`.` / `.git/**` / `.agents/**` / `.codex/**` write**: the practical write set for the workspace itself. `\".\"` spells out the workspace-subtree write access the `:workspace` baseline already grants (a tool-scoped category replaces the shared block wholesale, so keeping it explicit avoids surprises), and `\".git/**\"` matches the carve-out rulesync emits by default anyway. `.agents/**` and `.codex/**` genuinely add access: Codex's `:workspace` baseline keeps `.git`, `.agents`, and `.codex` read-only inside workspace roots, so without these rules a Codex session cannot update agent files or its own project-level config — for example, running `rulesync generate` inside a session would be denied when writing `.agents/` or `.codex/` outputs. Trade-off: the baseline keeps those two directories read-only precisely so a sandboxed session cannot rewrite its own configuration — with `.codex/**` writable, a compromised or prompt-injected session could relax `.codex/config.toml` (approval policy, permission profiles, MCP servers) for its next run, and with `.agents/**` writable it could persist injected instructions into rule/skill files. Drop these two entries if your workflow does not need in-session writes there.\n- **`:root` / `:minimal` write**: package runners such as `npx {package}` unpack into the npm cache under the home directory (`~/.npm/_npx`), and many dev tools write to home-directory caches (`~/.cache`, `~/.local`, corepack/pnpm stores); the `:workspace` baseline denies these writes, which breaks the commands outright. `\":root\" = \"write\"` alone is not enough: rulesync emits `\":minimal\" = \"read\"` by default (the platform-default system paths needed for sandboxed command execution), and Codex treats that entry as narrowing the broader `:root` grant — the policy no longer qualifies for full disk write access, so writes that `:root` appears to allow can still be denied. Raise `\":minimal\"` to write alongside `\":root\"` to get the intended effect. Trade-off: the pair grants the sandbox full disk write access, including platform system paths — a compromised or prompt-injected session could then modify shell startup files, `PATH` binaries, or system configuration outside the workspace, effectively neutralizing the sandbox's write isolation. Prefer narrower home-directory patterns instead (e.g. `\"~/.npm/**\"`, `\"~/.cache/**\"`) unless you specifically need system-wide writes, at the cost of chasing each tool's cache path.\n- **`:tmpdir` / `:slash_tmp` write**: many build tools require a writable temp directory (`$TMPDIR` and `/tmp` respectively).\n- **`~/.codex/**` read with `auth.json` deny**: Codex can read its own configuration tree while your credentials stay protected. Tilde paths are expanded by Codex itself, so no manual `$HOME` resolution is needed.\n- **`glob_scan_max_depth`**: no need to add it — rulesync emits the Codex default (`8`) automatically whenever the generated workspace-root rules contain unbounded `**` patterns (the default `.git/**` carve-out, when emitted, already is one).\n\nSee the [Codex permissions reference](https://developers.openai.com/codex/permissions) for the full path and network syntax.\n\n## Generated rule files create noise in pull request diffs\n\nBecause many AI coding tools (Claude Code, Cursor, Copilot, Antigravity, etc.) need to read their rule files directly from the working tree, the files rulesync generates are intentionally not `.gitignore`d. On repositories with many targets, the generated files can dominate a pull request diff and make code review harder.\n\n**Workaround:** Add the generated paths to `.gitattributes` with the [`linguist-generated`](https://docs.github.com/en/repositories/working-with-files/managing-files/customizing-how-changed-files-appear-on-github#marking-files-as-generated) attribute. GitHub's PR UI will then collapse those files by default while still keeping them visible and loadable by the tools themselves.\n\nExample `.gitattributes` for a repo that uses `.agent/`, Claude Code, Cursor, and Copilot targets:\n\n```\n.agent/rules/** linguist-generated\n.agent/skills/** linguist-generated\n.agent/workflows/** linguist-generated\nCLAUDE.md linguist-generated\n.cursor/rules/** linguist-generated\n.github/copilot-instructions.md linguist-generated\n```\n\nAdjust the list to match the targets you have configured. These entries only affect how GitHub displays the files in diffs — they don't change how Git tracks them, and they don't interfere with the tools reading the rules.\n\n## How do I keep many repositories in sync with a shared source?\n\nRulesync stops at the repository boundary. A consumer repository declares its `sources` in `rulesync.jsonc`, pins what it resolved in `rulesync.lock`, and moves forward only when someone runs `rulesync install --update` there — the same model as an npm or Bun lockfile. Nothing in the tool schedules that run, watches the shared repository, or walks other clones, so with fifteen consumers the layer above the lockfile is yours to shape, and either of the two obvious shapes works:\n\n- **On demand.** Run `rulesync install --update && rulesync generate` in a repository when you want it to pick up the shared changes, review the diff, and commit the lockfile with the regenerated files.\n- **Scheduled.** A cron-triggered CI job that runs the same two commands and opens a pull request when the lockfile changed is an ordinary way to drive rulesync; nothing in the tool assumes the update is manual. Compare the lockfile with `resolvedAt` ignored (the `-I` flag needs Git 2.30 or newer): `--update` stamps a fresh timestamp on every source it re-resolves, so the file changes even when no `resolvedRef` did.\n\n ```bash\n rulesync install --update && rulesync generate\n git diff --quiet -I '\"resolvedAt\"' -- rulesync.lock rulesync-npm.lock.json || echo \"shared source moved: open a pull request\"\n ```\n\nKeep `rulesync doctor --strict && rulesync install --frozen && rulesync generate --check` in the consumer's CI either way; that is what guards a repository whose lockfile has fallen behind its own `rulesync.jsonc` or whose generated files have drifted, independent of how updates are triggered.\n\nTo see whether a repository is behind without changing anything, run `rulesync install --outdated` there. It re-resolves each declared source the same way `--update` does, compares the result with the lockfile, lists the sources that are outdated or not yet locked, and exits with code `1` when any is (`2` when a source cannot be resolved), without writing the lockfile or any other file. It is what a scheduled job can run first to decide whether an update is needed, and running it across clones shows which consumers are behind. See [Checking for Outdated Sources](./guide/declarative-sources.md#checking-for-outdated-sources) for the details.\n\nUpdating many repositories at once is a loop over clones that does, per repository, exactly what a single one does: skip a dirty working tree, run `rulesync install --update && rulesync generate`, run the CI guard, and commit. Rulesync does not orchestrate that loop, and the shared repository does not have to know who consumes it.\n\n## Is there a GUI or web dashboard for managing Rulesync?\n\nNo. Rulesync is a CLI and a library, and it does not ship a `rulesync ui` command or a dashboard. The `.rulesync/` directory and `rulesync.jsonc` are plain files meant to be reviewed in pull requests like any other source, and the rules, commands, subagents and skills in them are ordinary Markdown with frontmatter that any editor can open. Bundling a web UI would add a server, a frontend and their runtime dependencies to every install.\n\nA graphical front end can be built on top of Rulesync instead, from either of two existing entry points:\n\n- The [Programmatic API](./api/programmatic-api.md) exports `generate`, `importFromTool` and `convertFromTool`, so a desktop or web app can drive the same operations as the CLI from Node.js.\n- The [Rulesync MCP Server](./reference/mcp-server.md) lets an AI agent read, create, update and delete Rulesync files and run the CLI commands (`generate`, `import`, `convert`, `doctor`, `install` and the others; see [CLI parity](./reference/mcp-server.md#cli-parity)), which covers the \"browse, edit and regenerate\" part of a dashboard from inside your coding assistant.\n",
|
|
11416
|
-
"getting-started/installation": "# Installation\n\n## Package Managers\n\n```bash\nnpm install -g rulesync\n\n# And then\nrulesync --version\nrulesync --help\n```\n\n## Homebrew (macOS and Linux)\n\nrulesync ships a self-contained [Homebrew](https://brew.sh/) tap inside this\nrepository. Because the repository is not named `homebrew-rulesync`, you must use\nthe two-argument `brew tap <name> <url>` form to add it — the auto-tap shorthand\n`brew install dyoshikawa/rulesync/rulesync` cannot resolve it on its own:\n\n```bash\nbrew tap dyoshikawa/rulesync https://github.com/dyoshikawa/rulesync\nbrew install rulesync\n\n# And then\nrulesync --version\n```\n\nThe formula installs the prebuilt binary for your platform (macOS/Linux, arm64\nand x64), so it does not depend on a Node.js runtime. It is updated as part of\neach release. Homebrew does not support Windows; use npm or the\nsingle-binary download below there.\n\n## Single Binary\n\nDownload pre-built binaries from the [latest release](https://github.com/dyoshikawa/rulesync/releases/latest). These binaries are built using [Bun's single-file executable bundler](https://bun.sh/docs/bundler/executables).\n\n**Quick Install (Linux/macOS - No sudo required):**\n\n```bash\ncurl -fsSL https://github.com/dyoshikawa/rulesync/releases/latest/download/install.sh | bash\n```\n\nOptions:\n\n- Install specific version: `curl -fsSL https://github.com/dyoshikawa/rulesync/releases/latest/download/install.sh | bash -s -- v6.4.0`\n- Custom directory: `RULESYNC_HOME=~/.local
|
|
11416
|
+
"getting-started/installation": "# Installation\n\n## Package Managers\n\n```bash\nnpm install -g rulesync\n\n# And then\nrulesync --version\nrulesync --help\n```\n\n## Homebrew (macOS and Linux)\n\nrulesync ships a self-contained [Homebrew](https://brew.sh/) tap inside this\nrepository. Because the repository is not named `homebrew-rulesync`, you must use\nthe two-argument `brew tap <name> <url>` form to add it — the auto-tap shorthand\n`brew install dyoshikawa/rulesync/rulesync` cannot resolve it on its own:\n\n```bash\nbrew tap dyoshikawa/rulesync https://github.com/dyoshikawa/rulesync\nbrew install rulesync\n\n# And then\nrulesync --version\n```\n\nThe formula installs the prebuilt binary for your platform (macOS/Linux, arm64\nand x64), so it does not depend on a Node.js runtime. It is updated as part of\neach release. Homebrew does not support Windows; use npm or the\nsingle-binary download below there.\n\n## Single Binary\n\nDownload pre-built binaries from the [latest release](https://github.com/dyoshikawa/rulesync/releases/latest). These binaries are built using [Bun's single-file executable bundler](https://bun.sh/docs/bundler/executables).\n\n**Quick Install (Linux/macOS - No sudo required):**\n\n```bash\ncurl -fsSL https://github.com/dyoshikawa/rulesync/releases/latest/download/install.sh | bash\n```\n\nOptions:\n\n- Install specific version: `curl -fsSL https://github.com/dyoshikawa/rulesync/releases/latest/download/install.sh | bash -s -- v6.4.0`\n- Custom directory: `curl -fsSL https://github.com/dyoshikawa/rulesync/releases/latest/download/install.sh | RULESYNC_HOME=~/.local bash`\n\n**Uninstall:**\n\n```bash\ncurl -fsSL https://github.com/dyoshikawa/rulesync/releases/latest/download/uninstall.sh | bash\n```\n\nIt removes only the binary installed by `install.sh` (`~/.rulesync/bin/rulesync`), then deletes `~/.rulesync/bin` and `~/.rulesync` if they are left empty. If you installed to a custom directory, pass the same `RULESYNC_HOME`: `curl -fsSL https://github.com/dyoshikawa/rulesync/releases/latest/download/uninstall.sh | RULESYNC_HOME=~/.local bash`. The script does not edit your shell configuration, so remove the PATH entry you added yourself.\n\n::: details Manual installation (requires sudo)\n\n### Linux (x64)\n\n```bash\ncurl -L https://github.com/dyoshikawa/rulesync/releases/latest/download/rulesync-linux-x64 -o rulesync && \\\n chmod +x rulesync && \\\n sudo mv rulesync /usr/local/bin/\n```\n\n### Linux (ARM64)\n\n```bash\ncurl -L https://github.com/dyoshikawa/rulesync/releases/latest/download/rulesync-linux-arm64 -o rulesync && \\\n chmod +x rulesync && \\\n sudo mv rulesync /usr/local/bin/\n```\n\n### macOS (Apple Silicon)\n\n```bash\ncurl -L https://github.com/dyoshikawa/rulesync/releases/latest/download/rulesync-darwin-arm64 -o rulesync && \\\n chmod +x rulesync && \\\n sudo mv rulesync /usr/local/bin/\n```\n\n### macOS (Intel)\n\n```bash\ncurl -L https://github.com/dyoshikawa/rulesync/releases/latest/download/rulesync-darwin-x64 -o rulesync && \\\n chmod +x rulesync && \\\n sudo mv rulesync /usr/local/bin/\n```\n\n### Windows (x64)\n\n```powershell\nInvoke-WebRequest -Uri \"https://github.com/dyoshikawa/rulesync/releases/latest/download/rulesync-windows-x64.exe\" -OutFile \"rulesync.exe\"; `\n Move-Item rulesync.exe C:\\Windows\\System32\\\n```\n\nOr using curl (if available):\n\n```bash\ncurl -L https://github.com/dyoshikawa/rulesync/releases/latest/download/rulesync-windows-x64.exe -o rulesync.exe && \\\n mv rulesync.exe /path/to/your/bin/\n```\n\n### Verify checksums\n\n```bash\ncurl -L https://github.com/dyoshikawa/rulesync/releases/latest/download/SHA256SUMS -o SHA256SUMS\n\n# Linux/macOS\nsha256sum -c SHA256SUMS\n\n# Windows (PowerShell)\n# Download SHA256SUMS file first, then verify:\nGet-FileHash rulesync.exe -Algorithm SHA256 | ForEach-Object {\n $actual = $_.Hash.ToLower()\n $expected = (Get-Content SHA256SUMS | Select-String \"rulesync-windows-x64.exe\").ToString().Split()[0]\n if ($actual -eq $expected) { \"✓ Checksum verified\" } else { \"✗ Checksum mismatch\" }\n}\n```\n\n### Verify build provenance\n\nRelease binaries carry [GitHub Artifact Attestations](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations), so you can check that the file you downloaded really was built by this repository's release workflow. This needs the [GitHub CLI](https://cli.github.com/) v2.49.0 or later, which is where `gh attestation` was introduced, and a signed-in CLI (`gh auth login`) — verification queries the API even for a public repository.\n\n```bash\n# Linux/macOS — the path the steps above installed the binary to\ngh attestation verify /usr/local/bin/rulesync \\\n --repo dyoshikawa/rulesync \\\n --signer-workflow dyoshikawa/rulesync/.github/workflows/publish-assets.yml\n```\n\n```powershell\n# Windows\ngh attestation verify C:\\Windows\\System32\\rulesync.exe `\n --repo dyoshikawa/rulesync `\n --signer-workflow dyoshikawa/rulesync/.github/workflows/publish-assets.yml\n```\n\nPass the path you actually installed the binary to. The command identifies the file by its contents, not by its name, so renaming it during installation — which the steps above do — does not affect verification; a binary installed by `install.sh` or Homebrew is the same file and verifies the same way. `--repo` alone only proves the attestation came from this repository, so `--signer-workflow` is included to pin the workflow that signed it.\n\nThis covers the release binaries. The npm package carries npm's own provenance attestation instead, which is checked with `npm audit signatures` rather than `gh attestation verify`.\n\n:::\n",
|
|
11417
11417
|
"getting-started/quick-start": "# Quick Start\n\n## New Project\n\n```bash\n# Install rulesync globally\nnpm install -g rulesync\n\n# Create necessary directories, sample rule files, and configuration file\nrulesync init\n\n# Install official skills (recommended)\nrulesync fetch dyoshikawa/rulesync\n\n# Or add skill sources to rulesync.jsonc and run 'rulesync install' (see \"Declarative Skill Sources\")\n```\n\n## Existing AI Tool Configurations\n\nIf you already have AI tool configurations:\n\n```bash\n# Import existing files (to .rulesync/**/*)\nrulesync import --targets claudecode # From CLAUDE.md\nrulesync import --targets cursor # From .cursorrules\nrulesync import --targets copilot # From .github/copilot-instructions.md\nrulesync import --targets claudecode --features rules,mcp,commands,subagents\n\n# And more tool supports\n\n# Generate unified configurations with all features\nrulesync generate --targets \"*\" --features \"*\"\n```\n\n## Quick Commands\n\nFor a comprehensive list of all commands and options, see [CLI Commands](/reference/cli-commands).\n",
|
|
11418
11418
|
"guide/case-studies": "# Case Studies\n\nRulesync is trusted by leading companies and recognized by the industry:\n\n- **Anthropic Official Customer Story**: [Classmethod Inc. - Improving AI coding tool consistency with Rulesync](https://claude.com/customers/classmethod)\n- **Asoview Inc.**: [Adopting Rulesync for unified AI development rules](https://tech.asoview.co.jp/entry/2025/12/06/100000)\n- **KAKEHASHI Tech Blog**: [Building multilingual systems for the LLM era with a monorepo and a \"living specification\"](https://kakehashi-dev.hatenablog.com/entry/2025/12/08/110000)\n- **Cloudflare**: [Adopting Rulesync for AI coding assistant configuration](https://github.com/cloudflare/cloudflare-docs/pull/28232)\n- **Ripple**: [Migrating agent rule management to Rulesync](https://github.com/Ripple-TS/ripple/commit/114bcf791c957ab5d43fcc6369515b59b866ce80)\n- **VOICEVOX**: [Adding Rulesync to unify AI coding assistant rules](https://github.com/VOICEVOX/voicevox/pull/2918)\n- **Effect**: [Managing agent rules with Rulesync](https://github.com/Effect-TS/effect-smol/pull/986)\n- **AG Grid**: [Syncing shared AI rules via Rulesync](https://github.com/ag-grid/ag-grid/pull/13044)\n- **Red Hat Developer Hub**: [Adding Rulesync to synchronize AI Assistant rules](https://github.com/redhat-developer/rhdh/pull/3707)\n- **Zenn**: [Adding Rulesync configuration to zenn-editor](https://github.com/zenn-dev/zenn-editor/commit/3fa309d9d73d1cf14e376690f52a3504d49d5c1c)\n- **Octane**: [Generating agent rules with Rulesync](https://github.com/octanejs/octane/commit/3f26327262ea61c0f2de6169b243d0d0021f4fc2)\n- **Forum Virium Helsinki**: [Centralizing AI coding rules with Rulesync](https://github.com/ForumViriumHelsinki/.github/pull/27)\n- **YANET**: [Generating Claude Code and Codex subagents from one source with Rulesync](https://github.com/yanet-platform/yanet2/pull/1793)\n",
|
|
11419
11419
|
"guide/configuration": "# Configuration\n\nYou can configure Rulesync by creating a `rulesync.jsonc` file in the root of your project.\n\n## JSON Schema Support\n\nRulesync provides a JSON Schema for editor validation and autocompletion. Add the `$schema` property to your `rulesync.jsonc`:\n\n```jsonc\n// rulesync.jsonc\n{\n \"$schema\": \"https://github.com/dyoshikawa/rulesync/releases/latest/download/config-schema.json\",\n \"targets\": [\"claudecode\"],\n \"features\": [\"rules\"],\n}\n```\n\n## Configuration Options\n\nExample:\n\n```jsonc\n// rulesync.jsonc\n{\n \"$schema\": \"https://github.com/dyoshikawa/rulesync/releases/latest/download/config-schema.json\",\n\n // List of tools to generate configurations for. You can specify \"*\" to generate all tools.\n \"targets\": [\"cursor\", \"claudecode\", \"opencode\", \"codexcli\"],\n\n // Features to generate. You can specify \"*\" to generate all features.\n \"features\": [\"rules\", \"mcp\", \"commands\", \"subagents\", \"hooks\", \"permissions\"],\n\n // Output root directories to generate files into.\n // Basically, you can specify `[\".\"]` only.\n // However, for example, if your project is a monorepo and you have to launch the AI agent at each package directory, you can specify multiple output roots.\n \"outputRoots\": [\".\"],\n\n // Delete existing files before generating\n \"delete\": true,\n\n // Verbose output\n \"verbose\": false,\n\n // Silent mode - suppress all output (except errors)\n \"silent\": false,\n\n // Advanced options\n \"global\": false, // Generate for global(user scope) configuration files\n \"simulateCommands\": false, // Generate simulated commands\n \"simulateSubagents\": false, // Generate simulated subagents\n \"simulateSkills\": false, // Generate simulated skills\n\n // Keep hook handlers Rulesync did not write when regenerating a tool's hooks\n // file. By default the generated hooks replace the destination's hook list\n // wholesale, so a handler another tool (or a person) added by hand is lost on\n // the next `generate`. Turning this on keeps such handlers and appends the\n // generated ones. It applies to Claude Code, Codex CLI, Cursor and Qwen\n // Code, in both project and global scope; the Claude Code *plugin* bundle\n // always replaces, because Rulesync owns that directory outright. A kept\n // matcher group joins a generated one only when its other group-level keys\n // (such as Qwen Code's `sequential`) agree, so it never changes how its\n // handlers run. There is no CLI flag: this is a project policy, not a\n // per-invocation one.\n //\n // To stay able to retract a hook it did write, Rulesync records what it\n // generated in a `.rulesync-hooks-lock.json` next to each hooks file (e.g.\n // `.claude/.rulesync-hooks-lock.json`). Commit it alongside the generated\n // hooks. A handler listed there that the sources no longer define is removed;\n // anything else is kept. The lock is the only ownership history: Rulesync\n // never reconstructs it from the hooks file. The first run after opting in\n // has no lock yet, so it records only the hooks the sources define on that\n // run. A hook Rulesync wrote earlier is retracted later only if the sources\n // still define it on that first run; one already removed from the sources is\n // indistinguishable from a hand-written handler and is kept. The same holds\n // whenever the lock is missing, unreadable or from another lock version:\n // prior history is lost, so restore the matching lock or review and remove\n // the stale handlers by hand.\n \"preserveUnownedHooks\": false,\n\n // Derive `agentsmd.subprojectPath` from each non-root rule's `globs`, so a\n // rule with `globs: [\"packages/api/**/*\"]` is written as\n // `packages/api/AGENTS.md` (nested AGENTS.md) by the targets that nest,\n // instead of `.agents/memories/<rule>.md`. The directory is the leading\n // wildcard-free part every glob shares; a rule whose globs have none (e.g.\n // `[\"src/**/*.ts\", \"test/**/*.ts\"]`) or disagree silently keeps its default\n // placement. Only a rule that sets `agentsmd: { subprojectPath: \"auto\" }`\n // itself is warned about when nothing can be derived; that value also opts\n // a single rule in regardless of this option, and `agentsmd: {\n // subprojectPath: \"\" }` opts a single rule out. An explicit directory\n // always wins and `root: true` rules never nest.\n // Turning it on moves existing outputs, so run `generate --delete` once.\n \"deriveSubprojectPathFromGlobs\": false,\n\n // Naming for command files flattened for tools without subdirectory\n // command support (e.g. Cursor): \"basename\" (default) keeps only the\n // filename, so `pj/test.md` and `ops/test.md` collide and the last one\n // wins; \"path\" joins the directory segments into the filename\n // (`pj/test.md` -> `pj-test.md`), which reduces collisions but cannot\n // rule them out (a literal `pj-test.md` also maps to `pj-test.md`); the\n // collision warning still applies.\n // Tools that support subdirectories (e.g. Claude Code) are unaffected.\n // Note: switching from \"basename\" to \"path\" renames the generated files\n // (e.g. `test.md` -> `pj-test.md`); run `rulesync generate` with\n // `delete: true` (or `--delete`) once after switching, otherwise the\n // stale old flat-named files remain alongside the new ones.\n \"flattenedCommandNaming\": \"basename\",\n\n // Language the AI should answer in. Omit it to say nothing about language.\n // See the \"Response Language\" section for what each tool receives.\n // \"language\": \"ja\",\n\n // When true (default), `rulesync gitignore` only emits entries for the\n // tools listed in `targets`. Set to false to emit entries for all supported\n // tools regardless of `targets`.\n //\n // Note: Entries for `agentsmd` (AGENTS.md and related paths) are always\n // appended even when `gitignoreTargetsOnly` is true and `agentsmd` is\n // absent from `targets`. AGENTS.md is a de facto standard read by many AI\n // tools regardless of the target set, so its gitignore entries are emitted\n // unconditionally to prevent accidental commits of generated rule files.\n \"gitignoreTargetsOnly\": true,\n\n // Declarative rule and skill sources — installed via 'rulesync install'\n // See the \"Declarative Sources\" section for details.\n // \"sources\": [\n // { \"source\": \"owner/repo\" },\n // { \"source\": \"org/repo\", \"skills\": [\"specific-skill\"] },\n // { \"source\": \"org/standards\", \"rules\": [\"testing-guidelines\"] },\n // ],\n}\n```\n\n## Per-Target Features\n\nThe `targets` option accepts both an array and an object format. Use the\nobject format when you want to declare per-target feature configuration in\na single place — the object keys are the target tools, and each value\ncarries the features to generate for that tool:\n\n```jsonc\n// rulesync.jsonc\n{\n \"targets\": {\n \"claudecode\": [\"rules\", \"commands\"],\n \"cursor\": [\"rules\", \"mcp\"],\n \"copilot\": [\"rules\", \"subagents\"],\n },\n}\n```\n\nIn this example:\n\n- `claudecode` generates rules and commands\n- `cursor` generates rules and MCP configuration\n- `copilot` generates rules and subagents\n\n> **Important:** When `targets` is in object form, the top-level `features`\n> field must be omitted. Declaring both would double-define the target\n> set, so the config loader rejects that combination.\n\nYou can also use `*` (wildcard) inside a target's value to enable every\nfeature for that tool:\n\n```jsonc\n{\n \"targets\": {\n \"claudecode\": [\"*\"], // Generate all features for Claude Code\n \"cursor\": [\"rules\"], // Only rules for Cursor\n },\n}\n```\n\n### Per-feature options\n\nSome features accept additional configuration. To pass options through, use\nthe object form for a target's value instead of an array. Each feature key\nmaps to either `true`/`false` (enable/disable) or an options object.\n\n```jsonc\n{\n \"gitignoreDestination\": \"gitignore\",\n \"targets\": {\n \"claudecode\": {\n \"gitignoreDestination\": \"gitattributes\",\n \"rules\": { \"ruleDiscoveryMode\": \"explicit\" },\n \"ignore\": {\n \"fileMode\": \"local\",\n \"gitignoreDestination\": \"gitignore\",\n },\n },\n },\n}\n```\n\n`gitignoreDestination` controls where `rulesync gitignore` writes path entries.\nYou can set it:\n\n- at **root level** (`gitignoreDestination`)\n- at **tool level** (`targets.<tool>.gitignoreDestination`)\n- or at **tool × feature level**\n (`targets.<tool>.<feature>.gitignoreDestination`)\n\nAllowed values:\n\n- `\"gitignore\"` (default)\n- `\"gitattributes\"`\n- `\"gitexclude\"` — Git's repository-local, untracked exclude file\n (`$GIT_COMMON_DIR/info/exclude`, usually `.git/info/exclude`)\n\n`\"gitexclude\"` keeps the ignore rules out of tracked files, so it fits\n`rulesync.local.jsonc` when you want rulesync's outputs ignored only on your\nmachine:\n\n```jsonc\n// rulesync.local.jsonc (not committed to git)\n{\n \"gitignoreDestination\": \"gitexclude\",\n}\n```\n\nThe path is resolved through Git (`git rev-parse --git-path info/exclude`), so\nit works from linked worktrees, where `.git` is a file and the exclude file is\nshared through the common Git directory. `rulesync gitignore` fails with an\nerror when the project is not inside a Git repository. Entries keep their\nnegations (`!`) there, as in `.gitignore`, and the managed block is fenced with\nthe same `# Generated by Rulesync` / `# End of Rulesync` markers, so re-running\nthe command is idempotent. Only that fenced block is rewritten: every line\noutside it is left exactly as you wrote it, and the exclude file is never read\nor written unless at least one entry is routed to `\"gitexclude\"`. When the\nroot-level value is `\"gitexclude\"`, rulesync's own local files (such as\n`rulesync.local.jsonc`) are written there too; with `\"gitattributes\"` they\nalways stay in `.gitignore`.\n\nThings to keep in mind with `\"gitexclude\"`:\n\n- **Run it from the repository root.** The exclude file is shared by the whole\n repository and its patterns are matched from the repository root, so\n `rulesync gitignore` fails with an error when a project routed to\n `\"gitexclude\"` is not at the top level of its worktree (for example a nested\n package in a monorepo).\n- **`.gitignore` takes precedence over the exclude file.** Git consults\n `.gitignore` before `info/exclude`, so the exclude file cannot override it.\n Several targets share some paths (for example `**/.agents/skills/`); when one\n of them is routed to `\"gitexclude\"` and another still to `\"gitignore\"`, the\n shared entry is written to `.gitignore` as well and stays there. Route every\n target that shares the path to `\"gitexclude\"` (the root-level setting does\n this), or narrow `targets`.\n- **Switching away does not clean up.** When you change the destination back,\n the block already in the exclude file is left in place; delete it by hand.\n\nPriority is **more specific wins**:\n\n1. tool × feature level\n2. tool level\n3. root level\n4. default (`\"gitignore\"`)\n\nThe current per-feature options are:\n\n| Target | Feature | Option | Values | Default |\n| ------------ | -------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------- |\n| `claudecode` | `rules` | `ruleDiscoveryMode` | `\"none\"` / `\"explicit\"` | tool default |\n| any | `rules` | `includeLocalRoot` | `true` / `false` (when `false`, `localRoot` rules are skipped for this target) | `true` |\n| `claudecode` | `rules` | `includeRoot` | `true` / `false` (when `false`, project `CLAUDE.md` and `CLAUDE.local.md` are skipped; leftovers are removed only with `delete`) | `true` |\n| `claudecode` | `ignore` | `fileMode` | `\"shared\"` (settings.json) / `\"local\"` (settings.local.json) | `\"shared\"` |\n| `opencode` | `mcp` | `configShape` | `\"v1\"` (flat `mcp.<name>` map with `enabled`) / `\"v2\"` (OpenCode V2 `mcp.servers.<name>` envelope with `disabled`) | `\"v2\"` |\n| `opencodev1` | `mcp` | `configShape` | `\"v1\"` / `\"v2\"` (as for `opencode`; OpenCode 1.x reads both since v1.18.24) | `\"v1\"` |\n| any | any | `gitignoreDestination` | `\"gitignore\"` / `\"gitattributes\"` / `\"gitexclude\"` | `\"gitignore\"` |\n\nSee [`docs/reference/file-formats.md`](../reference/file-formats.md#where-ignore-patterns-are-written-per-tool)\nfor the rationale behind the Claude Code default and when to switch to\n`\"local\"`.\n\n## Response Language\n\nThe root `language` key steers the language the AI answers in. It accepts one of `en`, `ja`, `zh-CN`, `zh-TW`, `ko`, `fr`, `de`, `es`, `pt-BR`, `ru`, has no CLI flag, and can be overridden per developer from `rulesync.local.jsonc`. When it is omitted, Rulesync says nothing about language; `en` is therefore an explicit instruction, not the default.\n\n```jsonc\n// rulesync.jsonc\n{\n \"language\": \"ja\",\n}\n```\n\nWhat the `rules` feature generates from it depends on the tool:\n\n- **Claude Code** has a native `language` setting, so the key is written as `\"language\": \"japanese\"` into `.claude/settings.local.json` (project scope) or `~/.claude/settings.json` (global scope, since Claude Code reads no `~/.claude/settings.local.json`) and `CLAUDE.md` is left as it is. Only the `language` key is touched; everything else in the settings file is preserved, and the file is never created or modified while the key is unset. Removing `language` later does not retract the key already written: the settings file is shared with your own configuration, so Rulesync never deletes from it, and the value stays until you remove it by hand.\n- **Every other tool** gets the instruction appended to the generated root rule file — the file built from your `root: true` rule (`AGENTS.md`, `GEMINI.md`, `.github/copilot-instructions.md`, `.cursor/rules/overview.mdc`, and so on). Nested rules never carry it, and neither does a `localRoot: true` rule's separate personal file. For a tool that files every rule side by side (Cursor and the fixed-name targets) with more than one `root: true` rule, only the file built from the first root rule carries the block. The block is separated from your content by a thematic break:\n\n <!-- prettier-ignore -->\n ```md\n ---\n\n You must always answer in Japanese. On the other hand, reasoning (thinking) should be in English to improve token efficiency.\n ```\n\n `rulesync import` recognizes the block for every supported language and strips it (with a warning, since the instruction is not carried into `.rulesync/rules/` — the `language` key in `rulesync.jsonc` is what keeps it), so importing a generated file and generating again yields one block rather than two. `rulesync convert` re-adds the block to the destination's root file when `language` is set.\n\n## Local Configuration\n\nRulesync supports a local configuration file (`rulesync.local.jsonc`) for machine-specific or developer-specific settings. This file is automatically added to `.gitignore` by `rulesync gitignore` and should not be committed to the repository.\n\n**Configuration Priority** (highest to lowest):\n\n1. CLI options\n2. `rulesync.local.jsonc`\n3. `rulesync.jsonc`\n4. Default values\n\nExample usage:\n\n```jsonc\n// rulesync.local.jsonc (not committed to git)\n{\n \"$schema\": \"https://github.com/dyoshikawa/rulesync/releases/latest/download/config-schema.json\",\n // Override targets for local development\n \"targets\": [\"claudecode\"],\n // Enable verbose output for debugging\n \"verbose\": true,\n}\n```\n\n## User-Level Defaults for `rulesync init`\n\n`rulesync init` seeds the `targets` and `features` of a new `rulesync.jsonc` from `$XDG_CONFIG_HOME/rulesync/rulesync.jsonc` (`~/.config/rulesync/rulesync.jsonc` by default) when that file exists. It is a template for `init` only — it does not take part in the priority list above, and `generate` never reads it. See [Init Command](../reference/cli-commands.md#init-command).\n\n## Target Order and File Conflicts\n\nWhen multiple targets write to the same output file, **the last target in the array wins**. This is the \"last-wins\" behavior.\n\nFor example, both `agentsmd` and `opencode` generate `AGENTS.md`:\n\n```jsonc\n{\n // opencode wins because it comes last\n \"targets\": [\"agentsmd\", \"opencode\"],\n \"features\": [\"rules\"],\n}\n```\n\nIn this case:\n\n1. `agentsmd` generates `AGENTS.md` first\n2. `opencode` generates `AGENTS.md` second, overwriting the previous file\n\nIf you want `agentsmd`'s output instead, reverse the order:\n\n```jsonc\n{\n // agentsmd wins because it comes last\n \"targets\": [\"opencode\", \"agentsmd\"],\n \"features\": [\"rules\"],\n}\n```\n\nThe order matters most when a target that folds every rule into its root file shares that file with a target that keeps non-root rules in separate files. `codexcli` folds all rules (except directory-scoped ones, which it nests) into `AGENTS.md`, while `roo` and `zoocode` write only the root rule to `AGENTS.md` and put the rest under `.roo/rules/`. With `[\"codexcli\", \"zoocode\"]`, `zoocode` overwrites `AGENTS.md` with the root rule alone, so Codex CLI silently loses every folded non-root rule. `rulesync generate` warns when this happens; list the folding target last to keep the folded content:\n\n```jsonc\n{\n // codexcli wins AGENTS.md, so its folded rules survive;\n // zoocode still gets its own files under .roo/rules/\n \"targets\": [\"zoocode\", \"codexcli\"],\n \"features\": [\"rules\"],\n}\n```\n\nIn this order Roo Code and ZooCode see the non-root rules twice — folded into `AGENTS.md` and again under `.roo/rules/` — so prefer it when a complete `AGENTS.md` for Codex CLI matters more than that duplication.\n\nThe object form of `targets` follows the same rule using its key order.\n\n`rulesync generate --check` compares a shared root file against what `rulesync generate` without `--targets` leaves on disk, even when `--targets` narrows the check. Other options such as `--features` still apply. The file's owner is the last target in the config file that generates rules. A target in the check run counts when the run generates rules for it. Any other target counts when a run without `--targets` would generate rules for it, so one listed only for other features (for example `\"opencode\": [\"mcp\"]`) never owns it. With `\"targets\": [\"codexcli\", \"claudecode\", \"opencode\"]`, `rulesync generate --check --targets codexcli` therefore compares `AGENTS.md` against opencode's output. It passes after a full `rulesync generate` and fails once `AGENTS.md` is edited. It also fails after `rulesync generate --targets codexcli`, because that run overwrites `AGENTS.md` with codexcli's content, which the next full generate would replace. Run a full `rulesync generate` to fix it, or list the target you narrow to last.\n",
|
|
@@ -15542,7 +15542,7 @@ function wrapCommand$1({ name, errorCode, handler, getVersion, loggerFactory = c
|
|
|
15542
15542
|
};
|
|
15543
15543
|
}
|
|
15544
15544
|
// src/cli/program.ts
|
|
15545
|
-
const getVersion = () => "43.
|
|
15545
|
+
const getVersion = () => "43.1.0";
|
|
15546
15546
|
const FEATURES_HELP = `${require_import.ALL_FEATURES.join(",")}; ignore is deprecated, use permissions`;
|
|
15547
15547
|
function wrapCommand(name, errorCode, handler) {
|
|
15548
15548
|
return wrapCommand$1({
|
package/dist/cli/index.js
CHANGED
|
@@ -11410,7 +11410,7 @@ async function convertCommand(logger, options) {
|
|
|
11410
11410
|
const DOCS_CONTENT = {
|
|
11411
11411
|
"api/programmatic-api": "# Programmatic API\n\nRulesync can be used as a library in your Node.js/TypeScript projects. The `generate`, `importFromTool`, and `convertFromTool` functions are available as named exports.\n\n```typescript\nimport { convertFromTool, generate, importFromTool } from \"rulesync\";\n\n// Generate configurations\nconst result = await generate({\n targets: [\"claudecode\", \"cursor\"],\n features: [\"rules\", \"mcp\"],\n});\nconsole.log(`Generated ${result.rulesCount} rules, ${result.mcpCount} MCP configs`);\n\n// Import existing tool configurations into .rulesync/\nconst importResult = await importFromTool({\n target: \"claudecode\",\n features: [\"rules\", \"commands\"],\n});\nconsole.log(`Imported ${importResult.rulesCount} rules`);\n\n// Convert configurations between AI tools without writing intermediate .rulesync/ files\ntry {\n const convertResult = await convertFromTool({\n from: \"claudecode\",\n to: [\"cursor\", \"copilot\"],\n features: [\"rules\"],\n });\n console.log(`Converted ${convertResult.rulesCount} rule file(s)`);\n} catch (error) {\n // Thrown when `from` is empty, `to` is empty, `to` includes `from`,\n // a source file cannot be parsed, or write fails.\n console.error(\"convert failed:\", error);\n}\n```\n\n## `generate(options?)`\n\nGenerates configuration files for the specified targets and features.\n\n| Option | Type | Default | Description |\n| ------------------- | -------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `targets` | `ToolTarget[]` | from config file | Tools to generate configurations for |\n| `features` | `Feature[]` | from config file | Features to generate |\n| `outputRoots` | `string[]` | `[process.cwd()]` | Output root directories to generate files into |\n| `inputRoots` | `string[]` | `[<cwd>/.rulesync]` | Ordered list of rulesync source-tree directories (e.g. `.rulesync`, `.rulesync.local`). Each entry is a source tree itself — no `.rulesync/` join is applied. The first root is required; later roots are optional overlays and may be absent. Output still goes to each `outputRoots` entry; only the input source root is redirected. Later entries override earlier ones for the same relative source path. Cannot be combined with `inputRoot`. Mirrors the CLI's `--input-roots`. |\n| `inputRoot` | `string` | `process.cwd()` | **Deprecated.** PARENT directory of a `.rulesync/` source tree; kept as a backward-compatibility alias that expands internally to `inputRoots: [join(inputRoot, \".rulesync\")]`. Prefer `inputRoots` and point it directly at your source tree(s). Cannot be combined with `inputRoots`. Mirrors the CLI's `--input-root`. |\n| `configPath` | `string` | auto-detected | Path to `rulesync.jsonc` |\n| `verbose` | `boolean` | `false` | Enable verbose logging |\n| `silent` | `boolean` | `true` | Suppress all output |\n| `delete` | `boolean` | from config file | Delete existing files before generating |\n| `global` | `boolean` | `false` | Generate global (user scope) configurations |\n| `simulateCommands` | `boolean` | `false` | Generate simulated commands |\n| `simulateSubagents` | `boolean` | `false` | Generate simulated subagents |\n| `simulateSkills` | `boolean` | `false` | Generate simulated skills |\n| `dryRun` | `boolean` | `false` | Show changes without writing files |\n| `check` | `boolean` | `false` | Exit with code 1 if files are not up to date |\n\n> **Unreadable sources do not throw here.** Unlike the CLI, which exits\n> non-zero, `generate()` resolves and reports the problem on the result:\n> `sourceLoadFailed` is `true` and `sourceLoadFailedFeatures` names the features\n> whose `.rulesync/` source could not be read. Their counts are `0`, exactly like\n> a feature that had nothing to write, so check the flag rather than the counts\n> before treating a run as successful. (Those features also keep their existing\n> generated files: `delete` skips their orphan sweep.)\n\nWith `delete: true`, the result's `deletedPathsByFeature` lists what the orphan\nsweep deleted — or, with `dryRun` or `check`, would delete — per feature, as\n`{ path, kind }` entries (`kind` is `\"file\"` or `\"directory\"`, `path` is\nrelative to the output root and sorted). A feature that deleted nothing is\nabsent. `keyOperationsByFeature` lists, the same way but regardless of\n`delete`, the top-level keys\nRulesync owns outright in a shared configuration file (`.codex/config.toml`,\n`.claude/settings.json`, …) that a write added or changed (`action: \"write\"`)\nor removed (`action: \"delete\"`), as `{ action, path, key }` entries sorted by\npath and then key. The CLI's `--json` output turns these and the written paths\ninto a versioned `plan`; see [CLI Commands](../reference/cli-commands.md).\n\n## `importFromTool(options)`\n\nImports existing tool configurations into `.rulesync/` directory.\n\n| Option | Type | Default | Description |\n| ------------ | ------------ | ---------------- | ----------------------------------------- |\n| `target` | `ToolTarget` | (required) | Tool to import configurations from |\n| `features` | `Feature[]` | from config file | Features to import |\n| `configPath` | `string` | auto-detected | Path to `rulesync.jsonc` |\n| `verbose` | `boolean` | `false` | Enable verbose logging |\n| `silent` | `boolean` | `true` | Suppress all output |\n| `global` | `boolean` | `false` | Import global (user scope) configurations |\n\n## `convertFromTool(options)`\n\nConverts configuration files between AI tools without writing intermediate `.rulesync/` files to disk.\n\n| Option | Type | Default | Description |\n| ------------ | -------------- | ------------- | ------------------------------------------------------------------------------------------- |\n| `from` | `ToolTarget` | (required) | Source tool to convert configurations from |\n| `to` | `ToolTarget[]` | (required) | Destination tools to convert to |\n| `features` | `Feature[]` | `[\"*\"]` | Features to convert. Matches CLI behavior and overrides any `features` in `rulesync.jsonc`. |\n| `configPath` | `string` | auto-detected | Path to `rulesync.jsonc` |\n| `verbose` | `boolean` | `false` | Enable verbose logging |\n| `silent` | `boolean` | `true` | Suppress all output |\n| `global` | `boolean` | `false` | Convert global (user scope) configurations |\n| `dryRun` | `boolean` | `false` | Show changes without writing files |\n",
|
|
11412
11412
|
faq: "# FAQ\n\n## `rulesync generate` doesn't produce what I expect\n\nRun `rulesync doctor` first. It performs read-only diagnostics on `rulesync.jsonc` and `rulesync.local.jsonc` and reports problems the generator silently tolerates — most importantly misspelled or unknown configuration keys (the config schema is non-strict, so a typo like `\"target\"` instead of `\"targets\"` is otherwise ignored and generation quietly falls back to defaults). See the [Doctor Command](./reference/cli-commands.md#doctor-command) reference for the full list of checks.\n\n## The generated `.mcp.json` doesn't work properly in Claude Code\n\nYou can try adding the following to `.claude/settings.json` or `.claude/settings.local.json`:\n\n```diff\n{\n+ \"enableAllProjectMcpServers\": true\n}\n```\n\nAccording to [the documentation](https://code.claude.com/docs/en/settings), this means:\n\n> Automatically approve all MCP servers defined in project .mcp.json files\n\n## Google Antigravity doesn't load rules when `.agents` directories are in `.gitignore`\n\nGoogle Antigravity has a known limitation where it won't load rules, workflows, and skills if the `.agents/rules/`, `.agents/workflows/`, and `.agents/skills/` directories are listed in `.gitignore`, even with \"Agent Gitignore Access\" enabled.\n\n> **Note:** Antigravity 2.0 uses the plural `.agents/` directory by default (the `antigravity-ide` and `antigravity-cli` targets).\n\n**Workaround:** Instead of adding these directories to `.gitignore`, add them to `.git/info/exclude`. `rulesync gitignore` can do this for you when `gitignoreDestination` is set to `\"gitexclude\"` (for example in `rulesync.local.jsonc`, or only for the Antigravity targets — see [Configuration](./guide/configuration.md#per-feature-options)). Run it from the repository root. Because `.gitignore` takes precedence over the exclude file, also route every other target that writes the same paths (for example Codex CLI's `**/.agents/skills/`) to `\"gitexclude\"` — the root-level setting does this — or leave them out of `targets`; otherwise the shared entry is still written to `.gitignore`. To do it by hand:\n\n```bash\n# Remove from .gitignore (if present)\n# **/.agents/rules/\n# **/.agents/workflows/\n# **/.agents/skills/\n\n# Add to .git/info/exclude\necho \"**/.agents/rules/\" >> .git/info/exclude\necho \"**/.agents/workflows/\" >> .git/info/exclude\necho \"**/.agents/skills/\" >> .git/info/exclude\n```\n\n`.git/info/exclude` works like `.gitignore` but is local-only, so it won't affect Antigravity's ability to load the rules while still excluding these directories from Git.\n\nNote: `.git/info/exclude` can't be shared with your team since it's not committed to the repository.\n\n## Codex CLI denies SSH agent access, temp-dir writes, or reading its own config with a generated permissions profile\n\nThe `[permissions.rulesync]` profile that rulesync generates into `.codex/config.toml` extends Codex CLI's `:workspace` baseline. That baseline is deliberately conservative, so day-to-day development can still hit permission denials: `git push`/`git fetch` over SSH cannot reach the SSH agent socket, some build tools fail without a writable temp dir, and Codex may be blocked from reading its own `~/.codex` configuration.\n\nrulesync emits the `.git` write carve-out for you (`\".git/**\" = \"write\"` under `:workspace_roots`; opt out with the `codexcli.git_write_rules: false` override; it is left out when a broader workspace-relative deny or read-only rule such as `read: { \"**\": \"deny\" }` covers `.git`). The whole subtree — including `.git/config` — is writable, because everyday commands such as `git remote add`, `git push -u`, and local-scope `git config` write to the repository config; users who want stricter isolation can add their own `read` override (e.g. `read: { \".git/config\": \"allow\" }`) in the canonical permissions. Everything below, however, depends on your environment or workflow, so rulesync does not add it by default. Where to put each piece differs, because the two tables are managed differently:\n\n**Network settings: edit `.codex/config.toml` directly.** Network settings are out of rulesync's management scope by design, keeping you free to edit them. rulesync preserves user-authored network keys when it regenerates the file — `network.enabled` (as long as the profile carries no rulesync-managed allow domains) and unknown keys such as `dangerously_allow_all_unix_sockets` are carried forward verbatim, with a warning so they stay visible:\n\n```toml\n[permissions.rulesync.network]\nenabled = true\n# Simplest option: allow all unix sockets. Codex names this \"dangerously_*\"\n# because it is broad, but it avoids hardcoding an env-dependent socket path.\ndangerously_allow_all_unix_sockets = true\n\n# Stricter alternative: allow only the SSH agent socket.\n# Replace the path with the actual value of $SSH_AUTH_SOCK on your machine;\n# Codex does not expand environment variables in these keys.\n# [permissions.rulesync.network.unix_sockets]\n# \"/path/to/ssh-agent.sock\" = \"allow\"\n```\n\n**Filesystem entries: author them in `.rulesync/permissions.jsonc`, not in `config.toml`.** The profile's `filesystem` table is fully managed — hand-written entries there are replaced on the next `rulesync generate`. Add the rules to the canonical config instead (use the tool-scoped `codexcli.permission` block so they do not leak into other tools' outputs) and regenerate:\n\n```jsonc\n{\n \"permission\": {\n // ...your shared rules...\n },\n \"codexcli\": {\n \"permission\": {\n \"write\": {\n \".\": \"allow\",\n \".git/**\": \"allow\",\n \".agents/**\": \"allow\",\n \".codex/**\": \"allow\",\n \":root\": \"allow\",\n \":minimal\": \"allow\",\n \":tmpdir\": \"allow\",\n \":slash_tmp\": \"allow\",\n },\n \"read\": { \"~/.codex/**\": \"allow\", \"~/.codex/auth.json\": \"deny\" },\n },\n },\n}\n```\n\nNote that this example is intentionally permissive: the `\":root\"` + `\":minimal\"` write pair grants the sandbox full disk write access — see the trade-off in the entry list below for narrower alternatives.\n\nThis generates into the profile as `\".\" = \"write\"`, `\".git/**\" = \"write\"`, `\".agents/**\" = \"write\"`, and `\".codex/**\" = \"write\"` under `:workspace_roots`, plus `\":root\" = \"write\"`, `\":minimal\" = \"write\"`, `\":tmpdir\" = \"write\"`, `\":slash_tmp\" = \"write\"`, `\"~/.codex/**\" = \"read\"`, and `\"~/.codex/auth.json\" = \"deny\"`, and round-trips through `rulesync import` — with two exceptions. First, `\".git/**\" = \"write\"` matches rulesync's default carve-out exactly, so import skips it (it is re-added on every generate); if you later opt out with `codexcli.git_write_rules: false` after an import, re-author the `\".git/**\": \"allow\"` write rule in the canonical config. Second, `\":minimal\"` is never imported regardless of its value — rulesync treats it as its fixed `\"read\"` baseline — so after an import, re-author the `\":minimal\": \"allow\"` write rule as well or the next generate silently drops back to `\":minimal\" = \"read\"`. Note that a tool-scoped category replaces the shared one wholesale for Codex CLI: if your shared `permission` block already has `read`/`write` rules that should also apply to Codex CLI, repeat them inside `codexcli.permission`.\n\nWhat each entry does:\n\n- **Unix socket access**: `git push`/`git fetch` over SSH needs the agent socket. `dangerously_allow_all_unix_sockets = true` is the simple, environment-independent option; a per-socket `unix_sockets` allow entry with the resolved `$SSH_AUTH_SOCK` path is the stricter one.\n- **`.` / `.git/**` / `.agents/**` / `.codex/**` write**: the practical write set for the workspace itself. `\".\"` spells out the workspace-subtree write access the `:workspace` baseline already grants (a tool-scoped category replaces the shared block wholesale, so keeping it explicit avoids surprises), and `\".git/**\"` matches the carve-out rulesync emits by default anyway. `.agents/**` and `.codex/**` genuinely add access: Codex's `:workspace` baseline keeps `.git`, `.agents`, and `.codex` read-only inside workspace roots, so without these rules a Codex session cannot update agent files or its own project-level config — for example, running `rulesync generate` inside a session would be denied when writing `.agents/` or `.codex/` outputs. Trade-off: the baseline keeps those two directories read-only precisely so a sandboxed session cannot rewrite its own configuration — with `.codex/**` writable, a compromised or prompt-injected session could relax `.codex/config.toml` (approval policy, permission profiles, MCP servers) for its next run, and with `.agents/**` writable it could persist injected instructions into rule/skill files. Drop these two entries if your workflow does not need in-session writes there.\n- **`:root` / `:minimal` write**: package runners such as `npx {package}` unpack into the npm cache under the home directory (`~/.npm/_npx`), and many dev tools write to home-directory caches (`~/.cache`, `~/.local`, corepack/pnpm stores); the `:workspace` baseline denies these writes, which breaks the commands outright. `\":root\" = \"write\"` alone is not enough: rulesync emits `\":minimal\" = \"read\"` by default (the platform-default system paths needed for sandboxed command execution), and Codex treats that entry as narrowing the broader `:root` grant — the policy no longer qualifies for full disk write access, so writes that `:root` appears to allow can still be denied. Raise `\":minimal\"` to write alongside `\":root\"` to get the intended effect. Trade-off: the pair grants the sandbox full disk write access, including platform system paths — a compromised or prompt-injected session could then modify shell startup files, `PATH` binaries, or system configuration outside the workspace, effectively neutralizing the sandbox's write isolation. Prefer narrower home-directory patterns instead (e.g. `\"~/.npm/**\"`, `\"~/.cache/**\"`) unless you specifically need system-wide writes, at the cost of chasing each tool's cache path.\n- **`:tmpdir` / `:slash_tmp` write**: many build tools require a writable temp directory (`$TMPDIR` and `/tmp` respectively).\n- **`~/.codex/**` read with `auth.json` deny**: Codex can read its own configuration tree while your credentials stay protected. Tilde paths are expanded by Codex itself, so no manual `$HOME` resolution is needed.\n- **`glob_scan_max_depth`**: no need to add it — rulesync emits the Codex default (`8`) automatically whenever the generated workspace-root rules contain unbounded `**` patterns (the default `.git/**` carve-out, when emitted, already is one).\n\nSee the [Codex permissions reference](https://developers.openai.com/codex/permissions) for the full path and network syntax.\n\n## Generated rule files create noise in pull request diffs\n\nBecause many AI coding tools (Claude Code, Cursor, Copilot, Antigravity, etc.) need to read their rule files directly from the working tree, the files rulesync generates are intentionally not `.gitignore`d. On repositories with many targets, the generated files can dominate a pull request diff and make code review harder.\n\n**Workaround:** Add the generated paths to `.gitattributes` with the [`linguist-generated`](https://docs.github.com/en/repositories/working-with-files/managing-files/customizing-how-changed-files-appear-on-github#marking-files-as-generated) attribute. GitHub's PR UI will then collapse those files by default while still keeping them visible and loadable by the tools themselves.\n\nExample `.gitattributes` for a repo that uses `.agent/`, Claude Code, Cursor, and Copilot targets:\n\n```\n.agent/rules/** linguist-generated\n.agent/skills/** linguist-generated\n.agent/workflows/** linguist-generated\nCLAUDE.md linguist-generated\n.cursor/rules/** linguist-generated\n.github/copilot-instructions.md linguist-generated\n```\n\nAdjust the list to match the targets you have configured. These entries only affect how GitHub displays the files in diffs — they don't change how Git tracks them, and they don't interfere with the tools reading the rules.\n\n## How do I keep many repositories in sync with a shared source?\n\nRulesync stops at the repository boundary. A consumer repository declares its `sources` in `rulesync.jsonc`, pins what it resolved in `rulesync.lock`, and moves forward only when someone runs `rulesync install --update` there — the same model as an npm or Bun lockfile. Nothing in the tool schedules that run, watches the shared repository, or walks other clones, so with fifteen consumers the layer above the lockfile is yours to shape, and either of the two obvious shapes works:\n\n- **On demand.** Run `rulesync install --update && rulesync generate` in a repository when you want it to pick up the shared changes, review the diff, and commit the lockfile with the regenerated files.\n- **Scheduled.** A cron-triggered CI job that runs the same two commands and opens a pull request when the lockfile changed is an ordinary way to drive rulesync; nothing in the tool assumes the update is manual. Compare the lockfile with `resolvedAt` ignored (the `-I` flag needs Git 2.30 or newer): `--update` stamps a fresh timestamp on every source it re-resolves, so the file changes even when no `resolvedRef` did.\n\n ```bash\n rulesync install --update && rulesync generate\n git diff --quiet -I '\"resolvedAt\"' -- rulesync.lock rulesync-npm.lock.json || echo \"shared source moved: open a pull request\"\n ```\n\nKeep `rulesync doctor --strict && rulesync install --frozen && rulesync generate --check` in the consumer's CI either way; that is what guards a repository whose lockfile has fallen behind its own `rulesync.jsonc` or whose generated files have drifted, independent of how updates are triggered.\n\nTo see whether a repository is behind without changing anything, run `rulesync install --outdated` there. It re-resolves each declared source the same way `--update` does, compares the result with the lockfile, lists the sources that are outdated or not yet locked, and exits with code `1` when any is (`2` when a source cannot be resolved), without writing the lockfile or any other file. It is what a scheduled job can run first to decide whether an update is needed, and running it across clones shows which consumers are behind. See [Checking for Outdated Sources](./guide/declarative-sources.md#checking-for-outdated-sources) for the details.\n\nUpdating many repositories at once is a loop over clones that does, per repository, exactly what a single one does: skip a dirty working tree, run `rulesync install --update && rulesync generate`, run the CI guard, and commit. Rulesync does not orchestrate that loop, and the shared repository does not have to know who consumes it.\n\n## Is there a GUI or web dashboard for managing Rulesync?\n\nNo. Rulesync is a CLI and a library, and it does not ship a `rulesync ui` command or a dashboard. The `.rulesync/` directory and `rulesync.jsonc` are plain files meant to be reviewed in pull requests like any other source, and the rules, commands, subagents and skills in them are ordinary Markdown with frontmatter that any editor can open. Bundling a web UI would add a server, a frontend and their runtime dependencies to every install.\n\nA graphical front end can be built on top of Rulesync instead, from either of two existing entry points:\n\n- The [Programmatic API](./api/programmatic-api.md) exports `generate`, `importFromTool` and `convertFromTool`, so a desktop or web app can drive the same operations as the CLI from Node.js.\n- The [Rulesync MCP Server](./reference/mcp-server.md) lets an AI agent read, create, update and delete Rulesync files and run the CLI commands (`generate`, `import`, `convert`, `doctor`, `install` and the others; see [CLI parity](./reference/mcp-server.md#cli-parity)), which covers the \"browse, edit and regenerate\" part of a dashboard from inside your coding assistant.\n",
|
|
11413
|
-
"getting-started/installation": "# Installation\n\n## Package Managers\n\n```bash\nnpm install -g rulesync\n\n# And then\nrulesync --version\nrulesync --help\n```\n\n## Homebrew (macOS and Linux)\n\nrulesync ships a self-contained [Homebrew](https://brew.sh/) tap inside this\nrepository. Because the repository is not named `homebrew-rulesync`, you must use\nthe two-argument `brew tap <name> <url>` form to add it — the auto-tap shorthand\n`brew install dyoshikawa/rulesync/rulesync` cannot resolve it on its own:\n\n```bash\nbrew tap dyoshikawa/rulesync https://github.com/dyoshikawa/rulesync\nbrew install rulesync\n\n# And then\nrulesync --version\n```\n\nThe formula installs the prebuilt binary for your platform (macOS/Linux, arm64\nand x64), so it does not depend on a Node.js runtime. It is updated as part of\neach release. Homebrew does not support Windows; use npm or the\nsingle-binary download below there.\n\n## Single Binary\n\nDownload pre-built binaries from the [latest release](https://github.com/dyoshikawa/rulesync/releases/latest). These binaries are built using [Bun's single-file executable bundler](https://bun.sh/docs/bundler/executables).\n\n**Quick Install (Linux/macOS - No sudo required):**\n\n```bash\ncurl -fsSL https://github.com/dyoshikawa/rulesync/releases/latest/download/install.sh | bash\n```\n\nOptions:\n\n- Install specific version: `curl -fsSL https://github.com/dyoshikawa/rulesync/releases/latest/download/install.sh | bash -s -- v6.4.0`\n- Custom directory: `RULESYNC_HOME=~/.local
|
|
11413
|
+
"getting-started/installation": "# Installation\n\n## Package Managers\n\n```bash\nnpm install -g rulesync\n\n# And then\nrulesync --version\nrulesync --help\n```\n\n## Homebrew (macOS and Linux)\n\nrulesync ships a self-contained [Homebrew](https://brew.sh/) tap inside this\nrepository. Because the repository is not named `homebrew-rulesync`, you must use\nthe two-argument `brew tap <name> <url>` form to add it — the auto-tap shorthand\n`brew install dyoshikawa/rulesync/rulesync` cannot resolve it on its own:\n\n```bash\nbrew tap dyoshikawa/rulesync https://github.com/dyoshikawa/rulesync\nbrew install rulesync\n\n# And then\nrulesync --version\n```\n\nThe formula installs the prebuilt binary for your platform (macOS/Linux, arm64\nand x64), so it does not depend on a Node.js runtime. It is updated as part of\neach release. Homebrew does not support Windows; use npm or the\nsingle-binary download below there.\n\n## Single Binary\n\nDownload pre-built binaries from the [latest release](https://github.com/dyoshikawa/rulesync/releases/latest). These binaries are built using [Bun's single-file executable bundler](https://bun.sh/docs/bundler/executables).\n\n**Quick Install (Linux/macOS - No sudo required):**\n\n```bash\ncurl -fsSL https://github.com/dyoshikawa/rulesync/releases/latest/download/install.sh | bash\n```\n\nOptions:\n\n- Install specific version: `curl -fsSL https://github.com/dyoshikawa/rulesync/releases/latest/download/install.sh | bash -s -- v6.4.0`\n- Custom directory: `curl -fsSL https://github.com/dyoshikawa/rulesync/releases/latest/download/install.sh | RULESYNC_HOME=~/.local bash`\n\n**Uninstall:**\n\n```bash\ncurl -fsSL https://github.com/dyoshikawa/rulesync/releases/latest/download/uninstall.sh | bash\n```\n\nIt removes only the binary installed by `install.sh` (`~/.rulesync/bin/rulesync`), then deletes `~/.rulesync/bin` and `~/.rulesync` if they are left empty. If you installed to a custom directory, pass the same `RULESYNC_HOME`: `curl -fsSL https://github.com/dyoshikawa/rulesync/releases/latest/download/uninstall.sh | RULESYNC_HOME=~/.local bash`. The script does not edit your shell configuration, so remove the PATH entry you added yourself.\n\n::: details Manual installation (requires sudo)\n\n### Linux (x64)\n\n```bash\ncurl -L https://github.com/dyoshikawa/rulesync/releases/latest/download/rulesync-linux-x64 -o rulesync && \\\n chmod +x rulesync && \\\n sudo mv rulesync /usr/local/bin/\n```\n\n### Linux (ARM64)\n\n```bash\ncurl -L https://github.com/dyoshikawa/rulesync/releases/latest/download/rulesync-linux-arm64 -o rulesync && \\\n chmod +x rulesync && \\\n sudo mv rulesync /usr/local/bin/\n```\n\n### macOS (Apple Silicon)\n\n```bash\ncurl -L https://github.com/dyoshikawa/rulesync/releases/latest/download/rulesync-darwin-arm64 -o rulesync && \\\n chmod +x rulesync && \\\n sudo mv rulesync /usr/local/bin/\n```\n\n### macOS (Intel)\n\n```bash\ncurl -L https://github.com/dyoshikawa/rulesync/releases/latest/download/rulesync-darwin-x64 -o rulesync && \\\n chmod +x rulesync && \\\n sudo mv rulesync /usr/local/bin/\n```\n\n### Windows (x64)\n\n```powershell\nInvoke-WebRequest -Uri \"https://github.com/dyoshikawa/rulesync/releases/latest/download/rulesync-windows-x64.exe\" -OutFile \"rulesync.exe\"; `\n Move-Item rulesync.exe C:\\Windows\\System32\\\n```\n\nOr using curl (if available):\n\n```bash\ncurl -L https://github.com/dyoshikawa/rulesync/releases/latest/download/rulesync-windows-x64.exe -o rulesync.exe && \\\n mv rulesync.exe /path/to/your/bin/\n```\n\n### Verify checksums\n\n```bash\ncurl -L https://github.com/dyoshikawa/rulesync/releases/latest/download/SHA256SUMS -o SHA256SUMS\n\n# Linux/macOS\nsha256sum -c SHA256SUMS\n\n# Windows (PowerShell)\n# Download SHA256SUMS file first, then verify:\nGet-FileHash rulesync.exe -Algorithm SHA256 | ForEach-Object {\n $actual = $_.Hash.ToLower()\n $expected = (Get-Content SHA256SUMS | Select-String \"rulesync-windows-x64.exe\").ToString().Split()[0]\n if ($actual -eq $expected) { \"✓ Checksum verified\" } else { \"✗ Checksum mismatch\" }\n}\n```\n\n### Verify build provenance\n\nRelease binaries carry [GitHub Artifact Attestations](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations), so you can check that the file you downloaded really was built by this repository's release workflow. This needs the [GitHub CLI](https://cli.github.com/) v2.49.0 or later, which is where `gh attestation` was introduced, and a signed-in CLI (`gh auth login`) — verification queries the API even for a public repository.\n\n```bash\n# Linux/macOS — the path the steps above installed the binary to\ngh attestation verify /usr/local/bin/rulesync \\\n --repo dyoshikawa/rulesync \\\n --signer-workflow dyoshikawa/rulesync/.github/workflows/publish-assets.yml\n```\n\n```powershell\n# Windows\ngh attestation verify C:\\Windows\\System32\\rulesync.exe `\n --repo dyoshikawa/rulesync `\n --signer-workflow dyoshikawa/rulesync/.github/workflows/publish-assets.yml\n```\n\nPass the path you actually installed the binary to. The command identifies the file by its contents, not by its name, so renaming it during installation — which the steps above do — does not affect verification; a binary installed by `install.sh` or Homebrew is the same file and verifies the same way. `--repo` alone only proves the attestation came from this repository, so `--signer-workflow` is included to pin the workflow that signed it.\n\nThis covers the release binaries. The npm package carries npm's own provenance attestation instead, which is checked with `npm audit signatures` rather than `gh attestation verify`.\n\n:::\n",
|
|
11414
11414
|
"getting-started/quick-start": "# Quick Start\n\n## New Project\n\n```bash\n# Install rulesync globally\nnpm install -g rulesync\n\n# Create necessary directories, sample rule files, and configuration file\nrulesync init\n\n# Install official skills (recommended)\nrulesync fetch dyoshikawa/rulesync\n\n# Or add skill sources to rulesync.jsonc and run 'rulesync install' (see \"Declarative Skill Sources\")\n```\n\n## Existing AI Tool Configurations\n\nIf you already have AI tool configurations:\n\n```bash\n# Import existing files (to .rulesync/**/*)\nrulesync import --targets claudecode # From CLAUDE.md\nrulesync import --targets cursor # From .cursorrules\nrulesync import --targets copilot # From .github/copilot-instructions.md\nrulesync import --targets claudecode --features rules,mcp,commands,subagents\n\n# And more tool supports\n\n# Generate unified configurations with all features\nrulesync generate --targets \"*\" --features \"*\"\n```\n\n## Quick Commands\n\nFor a comprehensive list of all commands and options, see [CLI Commands](/reference/cli-commands).\n",
|
|
11415
11415
|
"guide/case-studies": "# Case Studies\n\nRulesync is trusted by leading companies and recognized by the industry:\n\n- **Anthropic Official Customer Story**: [Classmethod Inc. - Improving AI coding tool consistency with Rulesync](https://claude.com/customers/classmethod)\n- **Asoview Inc.**: [Adopting Rulesync for unified AI development rules](https://tech.asoview.co.jp/entry/2025/12/06/100000)\n- **KAKEHASHI Tech Blog**: [Building multilingual systems for the LLM era with a monorepo and a \"living specification\"](https://kakehashi-dev.hatenablog.com/entry/2025/12/08/110000)\n- **Cloudflare**: [Adopting Rulesync for AI coding assistant configuration](https://github.com/cloudflare/cloudflare-docs/pull/28232)\n- **Ripple**: [Migrating agent rule management to Rulesync](https://github.com/Ripple-TS/ripple/commit/114bcf791c957ab5d43fcc6369515b59b866ce80)\n- **VOICEVOX**: [Adding Rulesync to unify AI coding assistant rules](https://github.com/VOICEVOX/voicevox/pull/2918)\n- **Effect**: [Managing agent rules with Rulesync](https://github.com/Effect-TS/effect-smol/pull/986)\n- **AG Grid**: [Syncing shared AI rules via Rulesync](https://github.com/ag-grid/ag-grid/pull/13044)\n- **Red Hat Developer Hub**: [Adding Rulesync to synchronize AI Assistant rules](https://github.com/redhat-developer/rhdh/pull/3707)\n- **Zenn**: [Adding Rulesync configuration to zenn-editor](https://github.com/zenn-dev/zenn-editor/commit/3fa309d9d73d1cf14e376690f52a3504d49d5c1c)\n- **Octane**: [Generating agent rules with Rulesync](https://github.com/octanejs/octane/commit/3f26327262ea61c0f2de6169b243d0d0021f4fc2)\n- **Forum Virium Helsinki**: [Centralizing AI coding rules with Rulesync](https://github.com/ForumViriumHelsinki/.github/pull/27)\n- **YANET**: [Generating Claude Code and Codex subagents from one source with Rulesync](https://github.com/yanet-platform/yanet2/pull/1793)\n",
|
|
11416
11416
|
"guide/configuration": "# Configuration\n\nYou can configure Rulesync by creating a `rulesync.jsonc` file in the root of your project.\n\n## JSON Schema Support\n\nRulesync provides a JSON Schema for editor validation and autocompletion. Add the `$schema` property to your `rulesync.jsonc`:\n\n```jsonc\n// rulesync.jsonc\n{\n \"$schema\": \"https://github.com/dyoshikawa/rulesync/releases/latest/download/config-schema.json\",\n \"targets\": [\"claudecode\"],\n \"features\": [\"rules\"],\n}\n```\n\n## Configuration Options\n\nExample:\n\n```jsonc\n// rulesync.jsonc\n{\n \"$schema\": \"https://github.com/dyoshikawa/rulesync/releases/latest/download/config-schema.json\",\n\n // List of tools to generate configurations for. You can specify \"*\" to generate all tools.\n \"targets\": [\"cursor\", \"claudecode\", \"opencode\", \"codexcli\"],\n\n // Features to generate. You can specify \"*\" to generate all features.\n \"features\": [\"rules\", \"mcp\", \"commands\", \"subagents\", \"hooks\", \"permissions\"],\n\n // Output root directories to generate files into.\n // Basically, you can specify `[\".\"]` only.\n // However, for example, if your project is a monorepo and you have to launch the AI agent at each package directory, you can specify multiple output roots.\n \"outputRoots\": [\".\"],\n\n // Delete existing files before generating\n \"delete\": true,\n\n // Verbose output\n \"verbose\": false,\n\n // Silent mode - suppress all output (except errors)\n \"silent\": false,\n\n // Advanced options\n \"global\": false, // Generate for global(user scope) configuration files\n \"simulateCommands\": false, // Generate simulated commands\n \"simulateSubagents\": false, // Generate simulated subagents\n \"simulateSkills\": false, // Generate simulated skills\n\n // Keep hook handlers Rulesync did not write when regenerating a tool's hooks\n // file. By default the generated hooks replace the destination's hook list\n // wholesale, so a handler another tool (or a person) added by hand is lost on\n // the next `generate`. Turning this on keeps such handlers and appends the\n // generated ones. It applies to Claude Code, Codex CLI, Cursor and Qwen\n // Code, in both project and global scope; the Claude Code *plugin* bundle\n // always replaces, because Rulesync owns that directory outright. A kept\n // matcher group joins a generated one only when its other group-level keys\n // (such as Qwen Code's `sequential`) agree, so it never changes how its\n // handlers run. There is no CLI flag: this is a project policy, not a\n // per-invocation one.\n //\n // To stay able to retract a hook it did write, Rulesync records what it\n // generated in a `.rulesync-hooks-lock.json` next to each hooks file (e.g.\n // `.claude/.rulesync-hooks-lock.json`). Commit it alongside the generated\n // hooks. A handler listed there that the sources no longer define is removed;\n // anything else is kept. The lock is the only ownership history: Rulesync\n // never reconstructs it from the hooks file. The first run after opting in\n // has no lock yet, so it records only the hooks the sources define on that\n // run. A hook Rulesync wrote earlier is retracted later only if the sources\n // still define it on that first run; one already removed from the sources is\n // indistinguishable from a hand-written handler and is kept. The same holds\n // whenever the lock is missing, unreadable or from another lock version:\n // prior history is lost, so restore the matching lock or review and remove\n // the stale handlers by hand.\n \"preserveUnownedHooks\": false,\n\n // Derive `agentsmd.subprojectPath` from each non-root rule's `globs`, so a\n // rule with `globs: [\"packages/api/**/*\"]` is written as\n // `packages/api/AGENTS.md` (nested AGENTS.md) by the targets that nest,\n // instead of `.agents/memories/<rule>.md`. The directory is the leading\n // wildcard-free part every glob shares; a rule whose globs have none (e.g.\n // `[\"src/**/*.ts\", \"test/**/*.ts\"]`) or disagree silently keeps its default\n // placement. Only a rule that sets `agentsmd: { subprojectPath: \"auto\" }`\n // itself is warned about when nothing can be derived; that value also opts\n // a single rule in regardless of this option, and `agentsmd: {\n // subprojectPath: \"\" }` opts a single rule out. An explicit directory\n // always wins and `root: true` rules never nest.\n // Turning it on moves existing outputs, so run `generate --delete` once.\n \"deriveSubprojectPathFromGlobs\": false,\n\n // Naming for command files flattened for tools without subdirectory\n // command support (e.g. Cursor): \"basename\" (default) keeps only the\n // filename, so `pj/test.md` and `ops/test.md` collide and the last one\n // wins; \"path\" joins the directory segments into the filename\n // (`pj/test.md` -> `pj-test.md`), which reduces collisions but cannot\n // rule them out (a literal `pj-test.md` also maps to `pj-test.md`); the\n // collision warning still applies.\n // Tools that support subdirectories (e.g. Claude Code) are unaffected.\n // Note: switching from \"basename\" to \"path\" renames the generated files\n // (e.g. `test.md` -> `pj-test.md`); run `rulesync generate` with\n // `delete: true` (or `--delete`) once after switching, otherwise the\n // stale old flat-named files remain alongside the new ones.\n \"flattenedCommandNaming\": \"basename\",\n\n // Language the AI should answer in. Omit it to say nothing about language.\n // See the \"Response Language\" section for what each tool receives.\n // \"language\": \"ja\",\n\n // When true (default), `rulesync gitignore` only emits entries for the\n // tools listed in `targets`. Set to false to emit entries for all supported\n // tools regardless of `targets`.\n //\n // Note: Entries for `agentsmd` (AGENTS.md and related paths) are always\n // appended even when `gitignoreTargetsOnly` is true and `agentsmd` is\n // absent from `targets`. AGENTS.md is a de facto standard read by many AI\n // tools regardless of the target set, so its gitignore entries are emitted\n // unconditionally to prevent accidental commits of generated rule files.\n \"gitignoreTargetsOnly\": true,\n\n // Declarative rule and skill sources — installed via 'rulesync install'\n // See the \"Declarative Sources\" section for details.\n // \"sources\": [\n // { \"source\": \"owner/repo\" },\n // { \"source\": \"org/repo\", \"skills\": [\"specific-skill\"] },\n // { \"source\": \"org/standards\", \"rules\": [\"testing-guidelines\"] },\n // ],\n}\n```\n\n## Per-Target Features\n\nThe `targets` option accepts both an array and an object format. Use the\nobject format when you want to declare per-target feature configuration in\na single place — the object keys are the target tools, and each value\ncarries the features to generate for that tool:\n\n```jsonc\n// rulesync.jsonc\n{\n \"targets\": {\n \"claudecode\": [\"rules\", \"commands\"],\n \"cursor\": [\"rules\", \"mcp\"],\n \"copilot\": [\"rules\", \"subagents\"],\n },\n}\n```\n\nIn this example:\n\n- `claudecode` generates rules and commands\n- `cursor` generates rules and MCP configuration\n- `copilot` generates rules and subagents\n\n> **Important:** When `targets` is in object form, the top-level `features`\n> field must be omitted. Declaring both would double-define the target\n> set, so the config loader rejects that combination.\n\nYou can also use `*` (wildcard) inside a target's value to enable every\nfeature for that tool:\n\n```jsonc\n{\n \"targets\": {\n \"claudecode\": [\"*\"], // Generate all features for Claude Code\n \"cursor\": [\"rules\"], // Only rules for Cursor\n },\n}\n```\n\n### Per-feature options\n\nSome features accept additional configuration. To pass options through, use\nthe object form for a target's value instead of an array. Each feature key\nmaps to either `true`/`false` (enable/disable) or an options object.\n\n```jsonc\n{\n \"gitignoreDestination\": \"gitignore\",\n \"targets\": {\n \"claudecode\": {\n \"gitignoreDestination\": \"gitattributes\",\n \"rules\": { \"ruleDiscoveryMode\": \"explicit\" },\n \"ignore\": {\n \"fileMode\": \"local\",\n \"gitignoreDestination\": \"gitignore\",\n },\n },\n },\n}\n```\n\n`gitignoreDestination` controls where `rulesync gitignore` writes path entries.\nYou can set it:\n\n- at **root level** (`gitignoreDestination`)\n- at **tool level** (`targets.<tool>.gitignoreDestination`)\n- or at **tool × feature level**\n (`targets.<tool>.<feature>.gitignoreDestination`)\n\nAllowed values:\n\n- `\"gitignore\"` (default)\n- `\"gitattributes\"`\n- `\"gitexclude\"` — Git's repository-local, untracked exclude file\n (`$GIT_COMMON_DIR/info/exclude`, usually `.git/info/exclude`)\n\n`\"gitexclude\"` keeps the ignore rules out of tracked files, so it fits\n`rulesync.local.jsonc` when you want rulesync's outputs ignored only on your\nmachine:\n\n```jsonc\n// rulesync.local.jsonc (not committed to git)\n{\n \"gitignoreDestination\": \"gitexclude\",\n}\n```\n\nThe path is resolved through Git (`git rev-parse --git-path info/exclude`), so\nit works from linked worktrees, where `.git` is a file and the exclude file is\nshared through the common Git directory. `rulesync gitignore` fails with an\nerror when the project is not inside a Git repository. Entries keep their\nnegations (`!`) there, as in `.gitignore`, and the managed block is fenced with\nthe same `# Generated by Rulesync` / `# End of Rulesync` markers, so re-running\nthe command is idempotent. Only that fenced block is rewritten: every line\noutside it is left exactly as you wrote it, and the exclude file is never read\nor written unless at least one entry is routed to `\"gitexclude\"`. When the\nroot-level value is `\"gitexclude\"`, rulesync's own local files (such as\n`rulesync.local.jsonc`) are written there too; with `\"gitattributes\"` they\nalways stay in `.gitignore`.\n\nThings to keep in mind with `\"gitexclude\"`:\n\n- **Run it from the repository root.** The exclude file is shared by the whole\n repository and its patterns are matched from the repository root, so\n `rulesync gitignore` fails with an error when a project routed to\n `\"gitexclude\"` is not at the top level of its worktree (for example a nested\n package in a monorepo).\n- **`.gitignore` takes precedence over the exclude file.** Git consults\n `.gitignore` before `info/exclude`, so the exclude file cannot override it.\n Several targets share some paths (for example `**/.agents/skills/`); when one\n of them is routed to `\"gitexclude\"` and another still to `\"gitignore\"`, the\n shared entry is written to `.gitignore` as well and stays there. Route every\n target that shares the path to `\"gitexclude\"` (the root-level setting does\n this), or narrow `targets`.\n- **Switching away does not clean up.** When you change the destination back,\n the block already in the exclude file is left in place; delete it by hand.\n\nPriority is **more specific wins**:\n\n1. tool × feature level\n2. tool level\n3. root level\n4. default (`\"gitignore\"`)\n\nThe current per-feature options are:\n\n| Target | Feature | Option | Values | Default |\n| ------------ | -------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------- |\n| `claudecode` | `rules` | `ruleDiscoveryMode` | `\"none\"` / `\"explicit\"` | tool default |\n| any | `rules` | `includeLocalRoot` | `true` / `false` (when `false`, `localRoot` rules are skipped for this target) | `true` |\n| `claudecode` | `rules` | `includeRoot` | `true` / `false` (when `false`, project `CLAUDE.md` and `CLAUDE.local.md` are skipped; leftovers are removed only with `delete`) | `true` |\n| `claudecode` | `ignore` | `fileMode` | `\"shared\"` (settings.json) / `\"local\"` (settings.local.json) | `\"shared\"` |\n| `opencode` | `mcp` | `configShape` | `\"v1\"` (flat `mcp.<name>` map with `enabled`) / `\"v2\"` (OpenCode V2 `mcp.servers.<name>` envelope with `disabled`) | `\"v2\"` |\n| `opencodev1` | `mcp` | `configShape` | `\"v1\"` / `\"v2\"` (as for `opencode`; OpenCode 1.x reads both since v1.18.24) | `\"v1\"` |\n| any | any | `gitignoreDestination` | `\"gitignore\"` / `\"gitattributes\"` / `\"gitexclude\"` | `\"gitignore\"` |\n\nSee [`docs/reference/file-formats.md`](../reference/file-formats.md#where-ignore-patterns-are-written-per-tool)\nfor the rationale behind the Claude Code default and when to switch to\n`\"local\"`.\n\n## Response Language\n\nThe root `language` key steers the language the AI answers in. It accepts one of `en`, `ja`, `zh-CN`, `zh-TW`, `ko`, `fr`, `de`, `es`, `pt-BR`, `ru`, has no CLI flag, and can be overridden per developer from `rulesync.local.jsonc`. When it is omitted, Rulesync says nothing about language; `en` is therefore an explicit instruction, not the default.\n\n```jsonc\n// rulesync.jsonc\n{\n \"language\": \"ja\",\n}\n```\n\nWhat the `rules` feature generates from it depends on the tool:\n\n- **Claude Code** has a native `language` setting, so the key is written as `\"language\": \"japanese\"` into `.claude/settings.local.json` (project scope) or `~/.claude/settings.json` (global scope, since Claude Code reads no `~/.claude/settings.local.json`) and `CLAUDE.md` is left as it is. Only the `language` key is touched; everything else in the settings file is preserved, and the file is never created or modified while the key is unset. Removing `language` later does not retract the key already written: the settings file is shared with your own configuration, so Rulesync never deletes from it, and the value stays until you remove it by hand.\n- **Every other tool** gets the instruction appended to the generated root rule file — the file built from your `root: true` rule (`AGENTS.md`, `GEMINI.md`, `.github/copilot-instructions.md`, `.cursor/rules/overview.mdc`, and so on). Nested rules never carry it, and neither does a `localRoot: true` rule's separate personal file. For a tool that files every rule side by side (Cursor and the fixed-name targets) with more than one `root: true` rule, only the file built from the first root rule carries the block. The block is separated from your content by a thematic break:\n\n <!-- prettier-ignore -->\n ```md\n ---\n\n You must always answer in Japanese. On the other hand, reasoning (thinking) should be in English to improve token efficiency.\n ```\n\n `rulesync import` recognizes the block for every supported language and strips it (with a warning, since the instruction is not carried into `.rulesync/rules/` — the `language` key in `rulesync.jsonc` is what keeps it), so importing a generated file and generating again yields one block rather than two. `rulesync convert` re-adds the block to the destination's root file when `language` is set.\n\n## Local Configuration\n\nRulesync supports a local configuration file (`rulesync.local.jsonc`) for machine-specific or developer-specific settings. This file is automatically added to `.gitignore` by `rulesync gitignore` and should not be committed to the repository.\n\n**Configuration Priority** (highest to lowest):\n\n1. CLI options\n2. `rulesync.local.jsonc`\n3. `rulesync.jsonc`\n4. Default values\n\nExample usage:\n\n```jsonc\n// rulesync.local.jsonc (not committed to git)\n{\n \"$schema\": \"https://github.com/dyoshikawa/rulesync/releases/latest/download/config-schema.json\",\n // Override targets for local development\n \"targets\": [\"claudecode\"],\n // Enable verbose output for debugging\n \"verbose\": true,\n}\n```\n\n## User-Level Defaults for `rulesync init`\n\n`rulesync init` seeds the `targets` and `features` of a new `rulesync.jsonc` from `$XDG_CONFIG_HOME/rulesync/rulesync.jsonc` (`~/.config/rulesync/rulesync.jsonc` by default) when that file exists. It is a template for `init` only — it does not take part in the priority list above, and `generate` never reads it. See [Init Command](../reference/cli-commands.md#init-command).\n\n## Target Order and File Conflicts\n\nWhen multiple targets write to the same output file, **the last target in the array wins**. This is the \"last-wins\" behavior.\n\nFor example, both `agentsmd` and `opencode` generate `AGENTS.md`:\n\n```jsonc\n{\n // opencode wins because it comes last\n \"targets\": [\"agentsmd\", \"opencode\"],\n \"features\": [\"rules\"],\n}\n```\n\nIn this case:\n\n1. `agentsmd` generates `AGENTS.md` first\n2. `opencode` generates `AGENTS.md` second, overwriting the previous file\n\nIf you want `agentsmd`'s output instead, reverse the order:\n\n```jsonc\n{\n // agentsmd wins because it comes last\n \"targets\": [\"opencode\", \"agentsmd\"],\n \"features\": [\"rules\"],\n}\n```\n\nThe order matters most when a target that folds every rule into its root file shares that file with a target that keeps non-root rules in separate files. `codexcli` folds all rules (except directory-scoped ones, which it nests) into `AGENTS.md`, while `roo` and `zoocode` write only the root rule to `AGENTS.md` and put the rest under `.roo/rules/`. With `[\"codexcli\", \"zoocode\"]`, `zoocode` overwrites `AGENTS.md` with the root rule alone, so Codex CLI silently loses every folded non-root rule. `rulesync generate` warns when this happens; list the folding target last to keep the folded content:\n\n```jsonc\n{\n // codexcli wins AGENTS.md, so its folded rules survive;\n // zoocode still gets its own files under .roo/rules/\n \"targets\": [\"zoocode\", \"codexcli\"],\n \"features\": [\"rules\"],\n}\n```\n\nIn this order Roo Code and ZooCode see the non-root rules twice — folded into `AGENTS.md` and again under `.roo/rules/` — so prefer it when a complete `AGENTS.md` for Codex CLI matters more than that duplication.\n\nThe object form of `targets` follows the same rule using its key order.\n\n`rulesync generate --check` compares a shared root file against what `rulesync generate` without `--targets` leaves on disk, even when `--targets` narrows the check. Other options such as `--features` still apply. The file's owner is the last target in the config file that generates rules. A target in the check run counts when the run generates rules for it. Any other target counts when a run without `--targets` would generate rules for it, so one listed only for other features (for example `\"opencode\": [\"mcp\"]`) never owns it. With `\"targets\": [\"codexcli\", \"claudecode\", \"opencode\"]`, `rulesync generate --check --targets codexcli` therefore compares `AGENTS.md` against opencode's output. It passes after a full `rulesync generate` and fails once `AGENTS.md` is edited. It also fails after `rulesync generate --targets codexcli`, because that run overwrites `AGENTS.md` with codexcli's content, which the next full generate would replace. Run a full `rulesync generate` to fix it, or list the target you narrow to last.\n",
|
|
@@ -15539,7 +15539,7 @@ function wrapCommand$1({ name, errorCode, handler, getVersion, loggerFactory = c
|
|
|
15539
15539
|
};
|
|
15540
15540
|
}
|
|
15541
15541
|
// src/cli/program.ts
|
|
15542
|
-
const getVersion = () => "43.
|
|
15542
|
+
const getVersion = () => "43.1.0";
|
|
15543
15543
|
const FEATURES_HELP = `${ALL_FEATURES.join(",")}; ignore is deprecated, use permissions`;
|
|
15544
15544
|
function wrapCommand(name, errorCode, handler) {
|
|
15545
15545
|
return wrapCommand$1({
|