AI Rules Sync (ais) is a CLI tool designed to synchronize agent rules from a centralized Git repository to local projects using symbolic links. It supports Cursor rules, Cursor commands, Cursor skills, Cursor subagents, Copilot instructions, Claude Code rules/skills/subagents/CLAUDE.md, Trae rules/skills, OpenCode agents/skills/commands/tools, Codex rules/skills/AGENTS.md, Gemini CLI commands/skills/agents/GEMINI.md, Windsurf rules/skills, Cline rules/skills, and universal AGENTS.md support, keeping projects up-to-date across teams.
A key feature is User Mode (--user / -u): use $HOME as project root to manage AI config files in ~/.claude/, ~/.gemini/, ~/.codex/, ~/.config/opencode/, etc. Entries are tracked in ~/.config/ai-rules-sync/user.json (or a user-configured custom path for dotfiles integration) and gitignore management is skipped automatically.
- Rules Repository: A Git repository containing rule definitions in official tool paths (
.cursor/rules/,.cursor/commands/,.cursor/skills/,.cursor/agents/,.github/instructions/,.claude/skills/,.claude/agents/,.claude/(for CLAUDE.md),.trae/rules/,.trae/skills/,.opencode/agents/,.opencode/skills/,.opencode/commands/,.opencode/tools/,.codex/rules/,.codex/(for AGENTS.md),.agents/skills/,.gemini/commands/,.gemini/skills/,.gemini/agents/,.gemini/(for GEMINI.md),.windsurf/rules/,.windsurf/skills/,.clinerules/,.cline/skills/,agents-md/). - Symbolic Links: Entries are linked from the local cache of the repo to project directories, avoiding file duplication and drift.
- Dependency Tracking: Uses
ai-rules-sync.jsonto track project dependencies (Cursor rules/commands/skills/subagents, Copilot instructions, Claude Code rules/skills/subagents/CLAUDE.md, Trae rules/skills, OpenCode agents/skills/commands/tools, Codex rules/skills/AGENTS.md, Gemini CLI commands/skills/agents/GEMINI.md, Windsurf rules/skills, Cline rules/skills, universal AGENTS.md). - Privacy: Supports private/local entries via
ai-rules-sync.local.jsonand.git/info/exclude. - User Mode:
--user/-uflag on add/remove/install commands. SetsprojectPath = $HOME, stores dependencies in~/.config/ai-rules-sync/user.json, skips gitignore management. Enablesais user installto restore all user-scope symlinks on a new machine. - User Config Path: Configurable via
ais config user set <path>for dotfiles integration (e.g.~/dotfiles/ai-rules-sync/user.json).
- Language: TypeScript (Node.js).
- CLI Framework: Commander.js.
- Config: Stored in
~/.config/ai-rules-sync/config.json(global) and project roots. - Git Operations: Uses
execato run git commands; stores repos in~/.config/ai-rules-sync/repos/. - Symlink Layer: All symlink creation/deletion goes through
linkany(viaDotfileManager.doLink/doUnlink), providing atomic operations. No barefs.ensureSymlinkcalls in dotany. - Plugin Architecture: Modular adapter system for different AI tools.
- Modular CLI: Declarative command registration using adapters.
src/
├── dotfile/ # Generic dotfile abstraction library (tool-agnostic)
│ ├── types.ts # All interfaces: SourceResolver, ManifestStore, etc.
│ ├── manager.ts # DotfileManager — all symlink ops via linkany (atomic)
│ ├── composer.ts # DotfileComposer — multi-manager apply/status
│ ├── sources/filesystem.ts # FileSystemSource (local dir, stow basis)
│ ├── manifest/json.ts # JsonManifest (generic JSON, optional namespace)
│ └── index.ts # dotfile.create() / dotfile.compose() entry point
├── plugin/ # ai-rules-sync plugin implementations
│ ├── git-repo-source.ts # GitRepoSource (RepoConfig | RepoResolverFn | null)
│ └── ai-rules-sync-manifest.ts # AiRulesSyncManifest (over ai-rules-sync.json)
├── adapters/ # Plugin architecture for different AI tools
│ ├── types.ts # SyncAdapter interface
│ ├── base.ts # createBaseAdapter factory function
│ ├── index.ts # Registry and helper functions
│ ├── cursor-rules.ts # Cursor rules adapter
│ ├── cursor-commands.ts # Cursor commands adapter
│ ├── cursor-skills.ts # Cursor skills adapter
│ ├── cursor-agents.ts # Cursor agents adapter
│ ├── copilot-instructions.ts # Copilot instructions adapter
│ ├── claude-skills.ts # Claude skills adapter
│ ├── claude-agents.ts # Claude agents adapter
│ ├── claude-rules.ts # Claude rules adapter (.claude/rules/)
│ ├── claude-md.ts # Claude CLAUDE.md adapter (.claude/, global mode)
│ ├── trae-rules.ts # Trae rules adapter
│ ├── trae-skills.ts # Trae skills adapter
│ ├── opencode-agents.ts # OpenCode agents adapter (file mode)
│ ├── opencode-skills.ts # OpenCode skills adapter (directory mode)
│ ├── opencode-commands.ts # OpenCode commands adapter (file mode)
│ ├── opencode-tools.ts # OpenCode tools adapter (file mode)
│ ├── codex-rules.ts # Codex rules adapter (file mode)
│ ├── codex-skills.ts # Codex skills adapter (directory mode)
│ ├── gemini-commands.ts # Gemini CLI commands adapter (file mode)
│ ├── gemini-skills.ts # Gemini CLI skills adapter (directory mode)
│ ├── gemini-agents.ts # Gemini CLI agents adapter (file mode)
│ ├── windsurf-rules.ts # Windsurf rules adapter (file mode)
│ ├── windsurf-skills.ts # Windsurf skills adapter (directory mode)
│ ├── cline-rules.ts # Cline rules adapter (file mode)
│ ├── cline-skills.ts # Cline skills adapter (directory mode)
│ └── agents-md.ts # Universal AGENTS.md adapter (file mode)
├── cli/ # CLI registration layer
│ └── register.ts # Declarative command registration (registerAdapterCommands)
├── commands/ # Command handlers
│ ├── handlers.ts # Generic add/remove/import handlers
│ ├── helpers.ts # Helper functions (getTargetRepo, parseConfigEntry, etc.)
│ ├── install.ts # Generic install function
│ ├── add-all.ts # Discover and install all entries from repository
│ ├── lifecycle.ts # check/update/init lifecycle commands
│ └── index.ts # Module exports
├── completion/ # Shell completion
│ └── scripts.ts # Shell completion scripts (bash, zsh, fish)
├── config.ts # Global config management
├── git.ts # Git operations
├── index.ts # CLI entry point (~560 lines)
├── link.ts # Re-exports from sync-engine
├── project-config.ts # Project config management
├── sync-engine.ts # Core linkEntry/unlinkEntry/importEntry functions
└── utils.ts # Utility functions
Additional root-level tooling:
docs/supported-tools.json- Single source of truth for Supported Tools table rowsscripts/sync-supported-tools.mjs- Regenerates README/README_ZH tool matrix from manifest
The sync engine uses a plugin-based architecture with unified operations:
SyncAdapter Interface:
interface SyncAdapter {
// Core properties
name: string; // e.g. "cursor-rules"
tool: string; // e.g. "cursor"
subtype: string; // e.g. "rules", "commands"
configPath: [string, string]; // e.g. ['cursor', 'rules']
defaultSourceDir: string; // e.g. ".cursor/rules"
targetDir: string; // e.g. ".cursor/rules"
mode: 'directory' | 'file' | 'hybrid'; // NEW: hybrid supports both
fileSuffixes?: string[]; // For file mode
hybridFileSuffixes?: string[]; // NEW: For hybrid mode
// Optional resolution hooks
resolveSource?(...): Promise<ResolvedSource>;
resolveTargetName?(...): string;
// Unified operations (provided by createBaseAdapter)
addDependency(projectPath, name, repoUrl, alias?, isLocal?): Promise<void>;
removeDependency(projectPath, alias): Promise<{removedFrom}>;
link(options): Promise<LinkResult>;
unlink(projectPath, alias): Promise<void>;
}Key Benefits:
- Unified Interface: All adapters provide the same add/remove/link/unlink operations
- No Hardcoding: configPath allows generic functions to work with any adapter
- Automatic Routing: findAdapterForAlias() finds the right adapter based on where alias is configured
- Reduced Duplication: Single generic handler for all add/remove/install/import operations
Adapter Modes:
- directory: Links entire directories (skills, agents)
- file: Links individual files with suffix resolution (commands)
- hybrid: Links both files and directories (cursor-rules with
.mdc/.mdsupport)
Helper Functions (base.ts):
// For single-suffix file adapters (e.g., cursor-commands, trae-rules)
createSingleSuffixResolver(suffix: string, entityName: string)
// For hybrid adapters supporting multiple suffixes (e.g., cursor-rules)
createMultiSuffixResolver(suffixes: string[], entityName: string)
// Ensures target name has proper suffix
createSuffixAwareTargetResolver(suffixes: string[])Example Simplified Adapter (cursor-commands.ts):
export const cursorCommandsAdapter = createBaseAdapter({
name: 'cursor-commands',
tool: 'cursor',
subtype: 'commands',
configPath: ['cursor', 'commands'],
defaultSourceDir: '.cursor/commands',
targetDir: '.cursor/commands',
mode: 'file',
fileSuffixes: ['.md'],
resolveSource: createSingleSuffixResolver('.md', 'Command'),
resolveTargetName: createSuffixAwareTargetResolver(['.md'])
});The CLI uses a declarative registration approach:
Declarative Command Registration (src/cli/register.ts):
interface RegisterCommandsOptions {
adapter: SyncAdapter;
parentCommand: Command;
programOpts: () => { target?: string };
}
function registerAdapterCommands(options: RegisterCommandsOptions): void {
// Automatically registers: add, remove, install, import subcommands
}Generic Command Handlers (src/commands/handlers.ts):
// All handlers work with any adapter
async function handleAdd(adapter, ctx, name, alias?): Promise<AddResult>
async function handleRemove(adapter, projectPath, alias, isUser?, options?): Promise<RemoveResult>
async function handleImport(adapter, ctx, name, options): Promise<void>Generic Install Function (src/commands/install.ts):
// Replaces 7 duplicate install functions with one generic function
async function installEntriesForAdapter(adapter, projectPath): Promise<void>
async function installEntriesForTool(adapters[], projectPath): Promise<void>Repository Lifecycle Commands (src/commands/lifecycle.ts):
checkRepositories(options): collects repo URLs from project/user config and compares local HEAD vs upstream (ahead/behind) usinggit fetch+git rev-list.updateRepositories(options): updates repos (git pullviacloneOrUpdateRepo) and reapplies links from config.initRulesRepository(options): scaffoldsai-rules-sync.jsonwith adapter-drivensourceDirdefaults and creates default source directories. Supports--only <tools...>and--exclude <tools...>to selectively initialize specific tools.
Helper Functions (src/commands/helpers.ts):
getTargetRepo(options): Resolve target repository from options or configinferDefaultMode(projectPath): Auto-detect cursor/copilot mode from configparseConfigEntry(key, value): Parse config entry to extract repoUrl, entryName, aliasresolveCopilotAliasFromConfig(input, keys): Resolve copilot alias with suffix handling
src/sync-engine.ts:
// Link from repo to project
async function linkEntry(adapter, options): Promise<LinkResult>
// Unlink from project
async function unlinkEntry(adapter, projectPath, alias): Promise<void>
// Import from project to repo (copy, commit, then create symlink)
async function importEntry(adapter, options): Promise<{imported, sourceName, targetName}>SourceDirConfig (for rules repos):
interface SourceDirConfig {
cursor?: {
rules?: string; // Default: ".cursor/rules"
commands?: string; // Default: ".cursor/commands"
skills?: string; // Default: ".cursor/skills"
agents?: string; // Default: ".cursor/agents"
};
copilot?: {
instructions?: string; // Default: ".github/instructions"
};
claude?: {
skills?: string; // Default: ".claude/skills"
agents?: string; // Default: ".claude/agents"
rules?: string; // Default: ".claude/rules"
md?: string; // Default: ".claude"
};
trae?: {
rules?: string; // Default: ".trae/rules"
skills?: string; // Default: ".trae/skills"
};
opencode?: {
agents?: string; // Default: ".opencode/agents"
skills?: string; // Default: ".opencode/skills"
commands?: string; // Default: ".opencode/commands"
tools?: string; // Default: ".opencode/tools"
};
codex?: {
rules?: string; // Default: ".codex/rules"
skills?: string; // Default: ".agents/skills"
};
gemini?: {
commands?: string; // Default: ".gemini/commands"
skills?: string; // Default: ".gemini/skills"
agents?: string; // Default: ".gemini/agents"
};
warp?: {
skills?: string; // Default: ".agents/skills"
};
windsurf?: {
rules?: string; // Default: ".windsurf/rules"
skills?: string; // Default: ".windsurf/skills"
};
cline?: {
rules?: string; // Default: ".clinerules"
skills?: string; // Default: ".cline/skills"
};
agentsMd?: {
file?: string; // Default: "." (repository root)
};
}ProjectConfig (unified configuration):
interface ProjectConfig {
// For rules repos: global path prefix
rootPath?: string;
// For rules repos: source directory configuration
sourceDir?: SourceDirConfig;
// For projects: dependency records
cursor?: {
rules?: Record<string, RuleEntry>;
commands?: Record<string, RuleEntry>;
skills?: Record<string, RuleEntry>;
agents?: Record<string, RuleEntry>;
};
copilot?: {
instructions?: Record<string, RuleEntry>;
};
claude?: {
skills?: Record<string, RuleEntry>;
agents?: Record<string, RuleEntry>;
rules?: Record<string, RuleEntry>;
md?: Record<string, RuleEntry>;
};
trae?: {
rules?: Record<string, RuleEntry>;
skills?: Record<string, RuleEntry>;
};
opencode?: {
agents?: Record<string, RuleEntry>;
skills?: Record<string, RuleEntry>;
commands?: Record<string, RuleEntry>;
tools?: Record<string, RuleEntry>;
};
codex?: {
rules?: Record<string, RuleEntry>;
skills?: Record<string, RuleEntry>;
};
gemini?: {
commands?: Record<string, RuleEntry>;
skills?: Record<string, RuleEntry>;
agents?: Record<string, RuleEntry>;
};
warp?: {
skills?: Record<string, RuleEntry>;
};
windsurf?: {
rules?: Record<string, RuleEntry>;
skills?: Record<string, RuleEntry>;
};
cline?: {
rules?: Record<string, RuleEntry>;
skills?: Record<string, RuleEntry>;
};
// Universal AGENTS.md support (tool-agnostic)
agentsMd?: Record<string, RuleEntry>;
}getAdapter(tool, subtype): Get adapter by tool/subtype, throws if not foundgetDefaultAdapter(tool): Get the first adapter for a toolgetToolAdapters(tool): Get all adapters for a toolfindAdapterForAlias(config, alias): Find which adapter manages a specific aliasaddDependencyGeneric(projectPath, configPath, name, repoUrl, alias?, isLocal?): Generic dependency addremoveDependencyGeneric(projectPath, configPath, alias): Generic dependency remove
- Use:
ais use <url|name>- Configure or switch the active rules repository. - List:
ais ls(alias:ais list) - Show configured repositories and the active one. - Status:
ais status- Show repository availability and project config summary. - Search:
ais search [query]- Search available entries in the current repository. - Git Proxy:
ais git <args...>- Run git commands directly in the active rules repository context. - Script-friendly output: Query commands support
--json(list/ls,status,search,config repo list/ls,config repo show). - Safe previews:
--dry-runsupported for destructive commands (remove/rm,import).
- Syntax:
ais cursor add <rule_name> [alias]orais cursor rules add <rule_name> [alias] - Links
<repo>/.cursor/rules/<rule_name>to.cursor/rules/<alias>. - Mode: Hybrid - supports both files (
.mdc,.md) and directories. - Options:
-t <repo>,--local(-l)
- Syntax:
ais cursor commands add <command_name> [alias] - Links
<repo>/.cursor/commands/<command_name>to.cursor/commands/<alias>. - Supports
.mdfiles.
- Syntax:
ais copilot add <name> [alias] - Links
<repo>/.github/instructions/<name>to.github/instructions/<alias>. - Supports
.mdand.instructions.mdsuffixes with conflict detection.
- Syntax:
ais cursor skills add <skillName> [alias] - Links
<repo>/.cursor/skills/<skillName>to.cursor/skills/<alias>. - Directory-based synchronization.
- Syntax:
ais cursor agents add <agentName> [alias] - Links
<repo>/.cursor/agents/<agentName>to.cursor/agents/<alias>. - Directory-based synchronization for Cursor subagents (Markdown files with YAML frontmatter).
- Syntax:
ais claude skills add <skillName> [alias] - Links
<repo>/.claude/skills/<skillName>to.claude/skills/<alias>. - Directory-based synchronization.
- Syntax:
ais claude agents add <agentName> [alias] - Links
<repo>/.claude/agents/<agentName>to.claude/agents/<alias>. - Directory-based synchronization.
- Syntax:
ais claude rules add <ruleName> [alias] - Links
<repo>/.claude/rules/<ruleName>to.claude/rules/<alias>. - File-based synchronization for Claude Code project rules.
- Supports
.mdsuffix.
- Syntax:
ais claude md add <name> [alias] - Links
<repo>/.claude/<name>.mdto.claude/<name>.md. - File-based synchronization for CLAUDE.md-style configuration files.
- Supports
.mdsuffix; resolvesCLAUDE→CLAUDE.mdautomatically. - User Mode:
ais claude md add CLAUDE --userlinks to~/.claude/CLAUDE.md. - sourceDir object format: Use
common/AGENTS.mdas CLAUDE.md source (one file, multiple targets):Enables syncing the same file to both AGENTS.md (via agentsMd) and CLAUDE.md (via claude-md).{ "sourceDir": { "claude": { "md": { "dir": "common", "sourceFile": "AGENTS.md", "targetFile": "CLAUDE.md" } } } }
- Syntax:
ais trae rules add <ruleName> [alias] - Links
<repo>/.trae/rules/<ruleName>to.trae/rules/<alias>. - File-based synchronization for Trae AI rules.
- Syntax:
ais trae skills add <skillName> [alias] - Links
<repo>/.trae/skills/<skillName>to.trae/skills/<alias>. - Directory-based synchronization for Trae AI skills.
- Syntax:
ais agents-md add <name> [alias] - Links AGENTS.md from repository to
AGENTS.md(project root). - Tool-agnostic: Follows the agents.md standard making agent definitions available to any AI coding tool that supports this format.
- Flexible path resolution supports multiple patterns:
- Root level:
.,AGENTS,AGENTS.md→repo/AGENTS.md - Directory:
frontend→repo/frontend/AGENTS.md - Nested path:
docs/team→repo/docs/team/AGENTS.md - Explicit file:
backend/AGENTS.md→repo/backend/AGENTS.md
- Root level:
- File-based synchronization with
.mdsuffix. - Config structure: Uses flat
agentsMdrecord (not nested like other tools)
OpenCode AI is supported with four different entry types:
- Syntax:
ais opencode agents add <agentName> [alias] - Links
<repo>/.opencode/agents/<agentName>to.opencode/agents/<alias>. - File-based synchronization with
.mdsuffix for OpenCode AI agents.
- Syntax:
ais opencode skills add <skillName> [alias] - Links
<repo>/.opencode/skills/<skillName>to.opencode/skills/<alias>. - Directory-based synchronization for OpenCode AI skills (SKILL.md inside).
- Syntax:
ais opencode commands add <commandName> [alias] - Links
<repo>/.opencode/commands/<commandName>to.opencode/commands/<alias>. - File-based synchronization with
.mdsuffix for OpenCode AI commands.
- Syntax:
ais opencode tools add <toolName> [alias] - Links
<repo>/.opencode/tools/<toolName>to.opencode/tools/<alias>. - File-based synchronization with
.ts/.jssuffixes for OpenCode AI tools.
OpenAI Codex is supported with two entry types:
- Syntax:
ais codex rules add <ruleName> [alias] - Links
<repo>/.codex/rules/<ruleName>to.codex/rules/<alias>. - File-based synchronization with
.rulessuffix for Codex rules (Starlark syntax). - Purpose: Control which commands can run outside sandbox.
- Syntax:
ais codex skills add <skillName> [alias] - Links
<repo>/.agents/skills/<skillName>to.agents/skills/<alias>. - Directory-based synchronization for Codex skills (SKILL.md inside).
- Note: Uses non-standard
.agents/skillsdirectory (not.codex/skills) per Codex documentation.
Gemini CLI (https://geminicli.com/) is supported with three entry types:
- Syntax:
ais gemini commands add <commandName> [alias] - Links
<repo>/.gemini/commands/<commandName>to.gemini/commands/<alias>. - File-based synchronization with
.tomlsuffix for Gemini CLI commands. - Purpose: Reusable prompts with argument substitution.
- Syntax:
ais gemini skills add <skillName> [alias] - Links
<repo>/.gemini/skills/<skillName>to.gemini/skills/<alias>. - Directory-based synchronization for Gemini CLI skills (SKILL.md inside).
- Purpose: Specialized expertise for specific tasks.
- Syntax:
ais gemini agents add <agentName> [alias] - Links
<repo>/.gemini/agents/<agentName>to.gemini/agents/<alias>. - File-based synchronization with
.mdsuffix for Gemini CLI subagents (Markdown with YAML frontmatter). - Purpose: Specialized subagents with defined capabilities.
- Syntax:
ais windsurf add <ruleName> [alias] - Links
<repo>/.windsurf/rules/<ruleName>to.windsurf/rules/<alias>. - File-based synchronization with
.mdsuffix for Windsurf workspace rules.
- Syntax:
ais windsurf skills add <skillName> [alias] - Links
<repo>/.windsurf/skills/<skillName>to.windsurf/skills/<alias>. - Directory-based synchronization for Windsurf Cascade skills (
SKILL.mdinside each skill folder).
- Syntax:
ais cline add <ruleName> [alias] - Links
<repo>/.clinerules/<ruleName>to.clinerules/<alias>. - File-based synchronization with
.md/.txtsuffixes for Cline rules.
- Syntax:
ais cline skills add <skillName> [alias] - Links
<repo>/.cline/skills/<skillName>to.cline/skills/<alias>. - Directory-based synchronization for Cline skills (
SKILL.mdinside each skill folder).
- Syntax:
ais import <tool> <subtype> <name>orais <tool> <subtype> import <name> - Copies entry from project to rules repository, commits, and creates symlink back.
- Options:
-m, --message <message>: Custom commit message-f, --force: Overwrite if entry exists in repo-p, --push: Push to remote after commit-l, --local: Add to local config
- Examples:
ais import cursor rules my-rule ais cursor rules import my-rule --push ais import copilot instructions my-instruction -m "Add new instruction"
ais cursor install- Install all Cursor rules, commands, skills, and agents.ais copilot install- Install all Copilot instructions.ais claude install- Install all Claude Code skills, agents, rules, and CLAUDE.md files.ais trae install- Install all Trae rules and skills.ais opencode install- Install all OpenCode agents, skills, commands, and tools.ais codex install- Install all Codex rules and skills.ais gemini install- Install all Gemini CLI commands, skills, and subagents.ais windsurf install- Install all Windsurf rules and skills.ais cline install- Install all Cline rules and skills.ais agents-md install- Install AGENTS.md files.ais install- Install everything (smart dispatch).ais install --user/ais user install- Install all user-scope AI config files from~/.config/ai-rules-sync/user.json.
The add-all command automatically discovers and installs all available entries from the rules repository by scanning the filesystem, unlike install which reads from config files.
Core Implementation (src/commands/add-all.ts):
discoverEntriesForAdapter()- Scans repository directory for a single adapterdiscoverAllEntries()- Discovers across all/filtered adaptersinstallDiscoveredEntries()- Batch installs discovered entrieshandleAddAll()- Main orchestrator
Command Hierarchy:
- Top-level:
ais add-all- All tools, supports--toolsand--adaptersfilters - Tool-level:
ais cursor add-all,ais copilot add-all, etc. - Filters to specific tool - Subtype-level:
ais cursor rules add-all,ais cursor commands add-all, etc. - Filters to specific adapter
Discovery Algorithm:
- Get source directory from repo config using
getRepoSourceConfig()andgetSourceDir() - Scan filesystem with
fs.readdir()filtering hidden files (starting with.) - Apply adapter mode filters:
- file mode: Include files matching
fileSuffixes(e.g.,.md) - directory mode: Include only directories
- hybrid mode: Include files matching
hybridFileSuffixesAND directories
- file mode: Include files matching
- Extract entry names by stripping suffixes for files
- Check existing config to mark already-configured entries
Options:
--dry-run- Preview without making changes--force- Overwrite existing entries (re-link and update config)--interactive- Prompt for each entry using readline--local- Store inai-rules-sync.local.json--skip-existing- Skip entries already in config (default behavior)--quiet- Minimal output--tools <tools>- Filter by comma-separated tool names (top-level only)--adapters <adapters>- Filter by comma-separated adapter names (top-level only)
Examples:
# Preview all Cursor rules
ais cursor rules add-all --dry-run
# Install all Cursor entries (rules, commands, skills, agents)
ais cursor add-all
# Install all entries from all tools
ais add-all
# Install only from specific tools
ais add-all --tools cursor,copilot
# Interactive confirmation
ais cursor add-all --interactive
# Install as private entries
ais cursor rules add-all --localOutput Format:
Discovering entries from repository...
cursor-rules: 5 entries
cursor-commands: 3 entries
Total: 8 entries discovered
Installing entries:
[1/8] cursor-rules/react → .cursor/rules/react ✓
[2/8] cursor-rules/testing → .cursor/rules/testing ✓
[3/8] cursor-commands/deploy → .cursor/commands/deploy ⊘ (already configured)
...
Summary:
Installed: 7
Skipped: 1 (already configured)
Errors: 0
Integration:
- Uses existing adapter system and
addDependency()/link()methods - Works with all adapter modes (file/directory/hybrid)
- Respects repository source directory configuration
- Compatible with
ais install(discovered entries are added to config)
Problem: Third-party rules repositories may not have ai-rules-sync.json or use non-standard directory structures (e.g., rules/cursor/ instead of .cursor/rules).
Solution: 4-layer priority system for custom source directory configuration:
CLI Parameters > Global Config > Repository Config > Adapter Defaults
Implementation (src/cli/source-dir-parser.ts, src/commands/config.ts):
- CLI parameter parsing with context-aware simple format and explicit dot notation
- Global config persistence in
~/.config/ai-rules-sync/config.json - Enhanced
getSourceDir()withglobalOverrideparameter - Config management commands for persistent configuration
CLI Parameter Formats:
# Simple format (context-aware - when tool/subtype is clear)
ais cursor rules add-all -s custom/rules
ais cursor add-all -s rules=custom/rules -s commands=custom/cmds
# Dot notation (explicit - requires full tool.subtype)
ais add-all -s cursor.rules=custom/rules -s cursor.commands=custom/cmds
# Multiple overrides (can repeat -s flag)
ais add-all \
-s cursor.rules=rules/cursor \
-s cursor.commands=commands \
-s claude.skills=claude/skillsGlobal Configuration Commands:
# Set custom source directory (persistent)
ais config repo set-source <repoName> <tool.subtype> <path>
ais config repo set-source third-party cursor.rules custom/rules
# View repository configuration
ais config repo show <repoName>
# Clear source directory
ais config repo clear-source <repoName> [tool.subtype]
# List all repositories
ais config repo listConfiguration Structure:
// Global config (~/.config/ai-rules-sync/config.json)
{
"currentRepo": "third-party-rules",
"repos": {
"third-party-rules": {
"name": "third-party-rules",
"url": "https://github.com/someone/rules",
"path": "/Users/user/.config/ai-rules-sync/repos/third-party-rules",
"sourceDir": {
"cursor": {
"rules": "rules/cursor",
"commands": "commands/cursor"
},
"claude": {
"skills": "claude/skills"
}
}
}
}
}sourceDir value formats:
- String (legacy):
"claude": { "md": ".claude" }— source directory path - Object (file mode):
"claude": { "md": { "dir": "common", "sourceFile": "AGENTS.md", "targetFile": "CLAUDE.md" } }— use a different source file and target filename (e.g. mapcommon/AGENTS.md→.claude/CLAUDE.md) - Object (directory mode):
"cursor": { "rules": { "dir": "common", "sourceDir": "shared-rules", "targetName": "cursor-rules" } }— use a different source subdirectory and target symlink name (e.g. mapcommon/shared-rules→.cursor/rules/cursor-rules)
Wildcard (*) fallback:
- Use
*as tool key to define shared config when tool-specific config is missing - Example:
"*": { "skills": "common/skills" }— cursor.skills, copilot.skills, etc. all resolve tocommon/skillswhen not explicitly defined - Applies to both
sourceDir(rules repo) and dependency records (project config) - Tool-specific config takes precedence; remove falls back to
*when entry is in wildcard config
Priority Resolution in getSourceDir():
- CLI override (highest priority) -
-sparameter, no rootPath prefix - Global config - From
RepoConfig.sourceDir, no rootPath prefix - Repository config - From repo's
ai-rules-sync.json, with rootPath prefix - Adapter default (lowest priority) - Hardcoded in adapter definition
Use Cases:
- One-time exploration: Use CLI parameter to test repositories
- Regular use: Configure once with
config repo set-source, use normally - Override: CLI parameter overrides even global config
add-all Integration:
All add-all commands support -s/--source-dir option:
- Top-level:
ais add-all -s tool.subtype=path - Tool-level:
ais cursor add-all -s rules=path - Subtype-level:
ais cursor rules add-all -s custom/rules
Files Involved:
src/config.ts- ExtendedRepoConfigwithsourceDirfieldsrc/project-config.ts- EnhancedgetSourceDir()with 4-layer prioritysrc/commands/add-all.ts- AddedsourceDirOverridesparametersrc/cli/source-dir-parser.ts- Parameter parsing logicsrc/commands/config.ts- Config management commandssrc/cli/register.ts- Added-soption to adapter commandssrc/index.ts- Added-soption and config commands
Example Workflow:
# Discover what a third-party repo has (preview)
ais cursor rules add-all -s custom/rules --dry-run
# Configure for regular use
ais config repo set-source my-third-party cursor.rules custom/rules
ais config repo set-source my-third-party cursor.commands custom/commands
# Use normally (sourceDir automatically applied)
ais cursor add-all
# Override temporarily
ais cursor rules add-all -s experimental/rulesUser Mode allows managing personal AI config files (~/.claude/CLAUDE.md, ~/.cursor/rules/, etc.) with version control and cross-machine sync. Aligns with Claude Code's official terminology: user scope = ~/.claude/, project scope = .claude/.
How it works:
--user/-uflag setsprojectPath = $HOMEand stores dependencies in~/.config/ai-rules-sync/user.jsoninstead of a project'sai-rules-sync.json- Gitignore management is skipped automatically (home dir is not a git repo)
- Symlinks are created at absolute paths (e.g.,
~/.claude/CLAUDE.md)
Commands:
# Add entries to user config
ais claude md add CLAUDE --user # ~/.claude/CLAUDE.md
ais cursor rules add my-style --user # ~/.cursor/rules/my-style.mdc
ais claude rules add general --user # ~/.claude/rules/general.md
# Install all user entries (restore on new machine)
ais user install
ais install --user # equivalent
# Remove from user config
ais claude md remove CLAUDE --userUser Config Path Management:
By default, user-scope dependencies are stored in ~/.config/ai-rules-sync/user.json. For dotfiles integration (to track user.json in git), the path can be customized:
# View current user config path
ais config user show
# Set custom path (e.g., inside dotfiles git repo)
ais config user set ~/dotfiles/ai-rules-sync/user.json
# Reset to default
ais config user resetMulti-machine Workflow:
# Machine A: initial setup
ais use git@github.com:me/my-rules.git
ais config user set ~/dotfiles/ai-rules-sync/user.json
ais claude md add CLAUDE --user
ais cursor rules add my-style --user
# Machine B: restore all user-scope symlinks
ais use git@github.com:me/my-rules.git
ais config user set ~/dotfiles/ai-rules-sync/user.json # if using dotfiles
ais user installuser.json format — same as ai-rules-sync.json:
{
"claude": {
"md": { "CLAUDE": "https://github.com/me/my-rules.git" },
"rules": { "general": "https://github.com/me/my-rules.git" }
},
"cursor": {
"rules": { "my-style": "https://github.com/me/my-rules.git" }
}
}Migration: On first use after upgrading, AIS automatically renames global.json → user.json and globalConfigPath → userConfigPath in config.json.
Config Fields:
Config.userConfigPath?: string- Custom path foruser.json(stored inconfig.json; replaces deprecatedglobalConfigPath)SyncOptions.skipIgnore?: boolean- Skip gitignore management (set automatically in user mode)
Functions:
getUserConfigPath()- Returns custom or default user.json path (replacesgetGlobalConfigPath())getUserProjectConfig()- Reads user.json asProjectConfig(replacesgetGlobalProjectConfig())saveUserProjectConfig()- Writes to user.json (replacessaveGlobalProjectConfig())addUserDependency()- Adds entry to user.json (inproject-config.ts; replacesaddGlobalDependency())removeUserDependency()- Removes entry from user.json (inproject-config.ts; replacesremoveGlobalDependency())installUserEntriesForAdapter()- Installs all user entries for one adapter (replacesinstallGlobalEntriesForAdapter())installAllUserEntries()- Installs user entries for all adapters (replacesinstallAllGlobalEntries())
Rules Repository Config (ai-rules-sync.json in the rules repo):
{
"rootPath": "src",
"sourceDir": {
"cursor": {
"rules": ".cursor/rules",
"commands": ".cursor/commands",
"skills": ".cursor/skills",
"agents": ".cursor/agents"
},
"copilot": {
"instructions": ".github/instructions"
},
"claude": {
"skills": ".claude/skills",
"agents": ".claude/agents",
"rules": ".claude/rules",
"md": ".claude"
},
"trae": {
"rules": ".trae/rules",
"skills": ".trae/skills"
},
"opencode": {
"agents": ".opencode/agents",
"skills": ".opencode/skills",
"commands": ".opencode/commands",
"tools": ".opencode/tools"
},
"codex": {
"rules": ".codex/rules",
"skills": ".agents/skills"
},
"gemini": {
"commands": ".gemini/commands",
"skills": ".gemini/skills",
"agents": ".gemini/agents"
},
"agentsMd": {
"file": "."
}
}
}Project Config (ai-rules-sync.json in user projects):
{
"cursor": {
"rules": { "react": "https://..." },
"commands": { "deploy-docs": "https://..." },
"skills": { "code-review": "https://..." },
"agents": { "code-analyzer": "https://..." }
},
"copilot": {
"instructions": { "general": "https://..." }
},
"claude": {
"skills": { "my-skill": "https://..." },
"agents": { "debugger": "https://..." },
"rules": { "general": "https://..." },
"md": { "CLAUDE": "https://..." }
},
"trae": {
"rules": { "project-rules": "https://..." },
"skills": { "new-adapter": "https://..." }
},
"opencode": {
"agents": { "code-reviewer": "https://..." },
"skills": { "refactor-helper": "https://..." },
"commands": { "build-optimizer": "https://..." },
"tools": { "project-analyzer": "https://..." }
},
"codex": {
"rules": { "sandbox-rules": "https://..." },
"skills": { "code-assistant": "https://..." }
},
"gemini": {
"commands": { "deploy-command": "https://..." },
"skills": { "code-review-skill": "https://..." },
"agents": { "refactor-agent": "https://..." }
},
"agentsMd": {
"root": {
"url": "https://...",
"rule": "AGENTS.md"
},
"frontend": {
"url": "https://...",
"rule": "frontend/AGENTS.md"
}
}
}ai-rules-sync.local.json: Private dependencies (merged, takes precedence).- Config files: Only
ai-rules-sync.jsonandai-rules-sync.local.jsonare supported.
- Auto-Install: On first run, AIS prompts to install shell completion automatically.
- Manual Install:
ais completion install- Installs completion to shell config file. - Script Output:
ais completion [bash|zsh|fish]- Outputs raw completion script. - Detection: Automatically detects shell type from
$SHELLenvironment variable. - Generation Model: Completion scripts are generated from shared metadata in
src/completion/scripts.ts(bash/zsh/fish stay in sync).
- Single Source: Supported tools matrix is defined in
docs/supported-tools.json. - Sync Command:
npm run docs:sync-toolsregenerates table blocks in bothREADME.mdandREADME_ZH.md. - Update Boundaries: Table content is replaced between
<!-- SUPPORTED_TOOLS_TABLE:START -->and<!-- SUPPORTED_TOOLS_TABLE:END -->.
| Adapter | Tool | Subtype | Mode | Source Dir | File Suffixes | Reference |
|---|---|---|---|---|---|---|
| cursor-rules | cursor | rules | hybrid | .cursor/rules | .mdc, .md | Cursor Rules |
| cursor-commands | cursor | commands | file | .cursor/commands | .md | Cursor Commands |
| cursor-skills | cursor | skills | directory | .cursor/skills | - | Cursor Skills |
| cursor-agents | cursor | subagents | directory | .cursor/agents | - | Cursor subagents |
| copilot-instructions | copilot | instructions | file | .github/instructions | .instructions.md, .md | Copilot Instructions |
| claude-skills | claude | skills | directory | .claude/skills | - | Claude Code Skills |
| claude-agents | claude | subagents | directory | .claude/agents | - | Claude Code Subagents |
| Adapter | Tool | Subtype | Mode | Source Dir | Target Dir (project) | User-level Target |
| --------- | ------ | --------- | ------ | ------------ | ---------------------- | ------------------- |
| cursor-rules | cursor | rules | hybrid | .cursor/rules | .cursor/rules | - |
| cursor-commands | cursor | commands | file | .cursor/commands | .cursor/commands | - |
| cursor-skills | cursor | skills | directory | .cursor/skills | .cursor/skills | - |
| cursor-agents | cursor | subagents | directory | .cursor/agents | .cursor/agents | - |
| copilot-instructions | copilot | instructions | file | .github/instructions | .github/instructions | - |
| claude-skills | claude | skills | directory | .claude/skills | .claude/skills | - |
| claude-agents | claude | subagents | directory | .claude/agents | .claude/agents | - |
| claude-rules | claude | rules | file | .claude/rules | .claude/rules | - |
| claude-md | claude | md | file | .claude | .claude | - |
| trae-rules | trae | rules | file | .trae/rules | .trae/rules | - |
| trae-skills | trae | skills | directory | .trae/skills | .trae/skills | - |
| agents-md | agents-md | file | file | . (root) | . | - |
| opencode-agents | opencode | agents | file | .opencode/agents | .opencode/agents | .config/opencode/agents |
| opencode-skills | opencode | skills | directory | .opencode/skills | .opencode/skills | .config/opencode/skills |
| opencode-commands | opencode | commands | file | .opencode/commands | .opencode/commands | .config/opencode/commands |
| opencode-tools | opencode | tools | file | .opencode/tools | .opencode/tools | .config/opencode/tools |
| codex-rules | codex | rules | file | .codex/rules | .codex/rules | - |
| codex-skills | codex | skills | directory | .agents/skills | .agents/skills | - |
| codex-md | codex | md | file | .codex | .codex | - |
| gemini-commands | gemini | commands | file | .gemini/commands | .gemini/commands | - |
| gemini-skills | gemini | skills | directory | .gemini/skills | .gemini/skills | - |
| gemini-agents | gemini | agents | file | .gemini/agents | .gemini/agents | - |
| gemini-md | gemini | md | file | .gemini | .gemini | - |
| windsurf-rules | windsurf | rules | file | .windsurf/rules | .windsurf/rules | - |
| windsurf-skills | windsurf | skills | directory | .windsurf/skills | .windsurf/skills | - |
| cline-rules | cline | rules | file | .clinerules | .clinerules | - |
| cline-skills | cline | skills | directory | .cline/skills | .cline/skills | - |
User-level Target column: When
--useris used, symlinks land at~/<userTargetDir>instead of~/<targetDir>. A-means the tool has no dedicated XDG/user path — user mode just uses~/<targetDir>(which for claude-md means~/.claude/CLAUDE.md, for gemini-md means~/.gemini/GEMINI.md, etc.).
- TypeScript: Strict mode enabled.
- Testing: Vitest for unit tests.
- Style: Functional programming style preferred.
- Adding New AI Tools:
- Create adapter in
src/adapters/<tool>.ts(usecreateBaseAdapter; adduserTargetDirif tool has a different XDG user-level path) - Register in
src/adapters/index.ts(constructor +findAdapterForAlias) - Add CLI commands in
src/index.tsusingregisterAdapterCommands() - Update
ProjectConfig,SourceDirConfig,RepoSourceConfig,mergeCombined, andgetSourceDirinsrc/project-config.ts
- Create adapter in
userTargetDir: Set this on an adapter when the tool's official user-level filesystem path differs from its project-level path (e.g., OpenCode uses~/.config/opencode/not~/.opencode/). Leave unset when~/<targetDir>is already the correct user path.
- Use directory mode for tools that organize entries as folders (skills, agents)
- Use file mode for tools with single files and consistent suffix (commands with
.md) - Use hybrid mode when entries can be either files or directories (cursor-rules)
Enables using the same file (e.g. common/AGENTS.md) for both AGENTS.md and CLAUDE.md sync.
Problem Solved:
- Rules repo had
common/AGENTS.mdthat should sync to project root asAGENTS.md(via agentsMd) and to.claude/CLAUDE.md(via claude-md) - Previously required duplicate files or manual symlinks
New sourceDir Object Format (file mode):
"claude": {
"md": { "dir": "common", "sourceFile": "AGENTS.md", "targetFile": "CLAUDE.md" }
}dir: Source directory (replaces default.claude)sourceFile: Filename to use as source (e.g.AGENTS.mdinstead ofCLAUDE.md)targetFile: Symlink filename in target (e.g.CLAUDE.md)
Extended for directory/hybrid mode (2026-03):
"cursor": {
"rules": { "dir": "common", "sourceDir": "shared-rules", "targetName": "cursor-rules" }
}sourceDir: Source subdirectory to use (e.g.common/shared-rulesinstead ofcommon/<name>)targetName: Symlink directory name in target (e.g.cursor-rules)
Implementation:
src/project-config.ts- AddedSourceDirValuetype,getSourceFileOverride(),getTargetFileOverride(),getSourceDirOverride(),getTargetNameOverride(), extendedbuildRepoSourceFromNestedStringsandgetSourceDir()for object formatsrc/adapters/claude-md.ts- CustomresolveSourcethat usessourceFileOverridewhen setsrc/adapters/base.ts-createMultiSuffixResolveracceptssourceDirOverridefor hybrid/directory modesrc/adapters/types.ts- ExtendedresolveSourcesignature with optionaloptions.sourceFileOverrideandoptions.sourceDirOverridesrc/dotany/types.ts- AddedtargetNametoResolvedSourcefor target overridesrc/plugin/git-repo-source.ts- Pass overrides to resolveSource, support sourceDirOverride/targetNameOverride for directory-only adapterssrc/sync-engine.ts- Pass overrides to adapter.resolveSource, use targetFileOverride/targetNameOverride for target namesrc/dotany/manager.ts- Useresolved.targetNamewhen present
- Added Linux-style aliases while keeping backward compatibility:
ais list→ais lsais remove→ais rm- Adapter subcommands also support
rm(for exampleais cursor rules rm react).
- Added query commands:
ais statusfor repository/project snapshot outputais search [query]to discover available entries in the active repository
- Added machine-readable query output with
--json:ais list/ls --jsonais status --jsonais search --jsonais config repo list/ls --jsonais config repo show <repo> --json
- Added
--dry-runsupport for destructive operations:- remove/rm commands
- import commands
- Updated shell completion metadata to include aliases and new query commands across bash/zsh/fish.
- Added Windsurf support: rules (
.windsurf/rules,.md) and skills (.windsurf/skills) with full CLI/completion integration - Added Cline support: rules (
.clinerules,.md/.txt) and skills (.cline/skills) with full CLI/completion integration - Refactored
project-configsource directory resolution to table-driven logic for both legacy-string detection andgetSourceDir() - Refactored shell completion generation to metadata-driven script builders for bash/zsh/fish
- Added reusable adapter contract test helper (
src/__tests__/helpers/adapter-contract.ts) to reduce duplicated adapter tests - Added single-source Supported Tools manifest (
docs/supported-tools.json) and sync script (npm run docs:sync-tools) for README parity - Added User Mode (
--user/-u): manage personal AI config files (~/.claude/CLAUDE.md, etc.) with version control;ais user installrestores all symlinks on new machines - Added claude-md adapter: sync CLAUDE.md-style files;
ais claude md add CLAUDE --user - Added User Config Path:
ais config user set <path>for dotfiles integration - Added Gemini CLI support: commands (
.toml), skills (directory), subagents (.md) - Added OpenAI Codex support: rules (
.rules, Starlark), skills (.agents/skills/) - Standardized user-scope flags on
--user/-u(legacy--global/-galiases removed)
Added full user-level support for Gemini CLI and Codex, fixed OpenCode XDG paths.
Problem Solved:
--usermode setprojectPath = $HOMEbut used the sametargetDirfor all adapters — causing OpenCode user-level files to land in~/.opencode/...instead of the official XDG path~/.config/opencode/...- No adapter existed for
~/.gemini/GEMINI.md(Gemini CLI's user-level context file) - No adapter existed for
~/.codex/AGENTS.md(Codex CLI's user-level instructions file) getSourceDir()had no branch forgeminiorcodex.md, returning the adapter default instead of any repo-configured path
Features Implemented:
-
userTargetDiron Adapter Interface:- New optional field
userTargetDir?: stringonSyncAdapterandAdapterConfig - When
options.skipIgnore === true(user mode) AND adapter hasuserTargetDir,sync-engine.tsuses it as the symlink destination instead oftargetDir - OpenCode adapters now carry
userTargetDirpointing to XDG paths
- New optional field
-
OpenCode XDG Path Fix:
opencode-commands:userTargetDir = '.config/opencode/commands'→~/.config/opencode/commands/opencode-agents:userTargetDir = '.config/opencode/agents'→~/.config/opencode/agents/opencode-skills:userTargetDir = '.config/opencode/skills'→~/.config/opencode/skills/opencode-tools:userTargetDir = '.config/opencode/tools'→~/.config/opencode/tools/
-
gemini-md Adapter (new):
- Manages
~/.gemini/GEMINI.md(or project-level.gemini/GEMINI.md) - File mode,
.mdsuffix; resolvesGEMINI→GEMINI.md - CLI:
ais gemini md [add|remove|install|import] - User mode:
ais gemini md add GEMINI --user→ symlink at~/.gemini/GEMINI.md
- Manages
-
codex-md Adapter (new):
- Manages
~/.codex/AGENTS.md(or project-level.codex/AGENTS.md) - File mode,
.mdsuffix; resolvesAGENTS→AGENTS.md - CLI:
ais codex md [add|remove|install|import] - User mode:
ais codex md add AGENTS --user→ symlink at~/.codex/AGENTS.md
- Manages
-
Configuration Updates:
ProjectConfig.gemininow includesmd?: Record<string, RuleEntry>ProjectConfig.codexnow includesmd?: Record<string, RuleEntry>- Same additions to
SourceDirConfigandRepoSourceConfig mergeCombined()now mergesgemini.md,codex.md, and the previously missinggemini.*fieldsgetSourceDir()now handlesgemini.*(all subtypes) andcodex.mdfindAdapterForAlias()updated forgemini.mdandcodex.md
-
_complete/userCmdUpdates:gemini-mdandcodex-mdadded to shell completion type listais userdescription now mentions~/.gemini/GEMINI.mdand~/.codex/AGENTS.md
Implementation:
src/adapters/types.ts— AddeduserTargetDir?: stringtoSyncAdaptersrc/adapters/base.ts— AddeduserTargetDir?: stringtoAdapterConfig, pass-through increateBaseAdaptersrc/sync-engine.ts— Applyadapter.userTargetDirwhenoptions.skipIgnore === truesrc/adapters/opencode-{commands,agents,skills,tools}.ts— AddeduserTargetDirsrc/adapters/gemini-md.ts— New adaptersrc/adapters/codex-md.ts— New adaptersrc/adapters/index.ts— Registered new adapters, updatedfindAdapterForAliassrc/project-config.ts— Extended types,mergeCombined,getSourceDirsrc/index.ts— Addedais gemini mdandais codex mdsubcommands
Files Changed: 9 modified + 2 new adapters, all tests passing (269/269)
Added User Mode for managing personal AI config files (~/.claude/CLAUDE.md, etc.):
Problem Solved:
- Personal AI config files like
~/.claude/CLAUDE.mdhad no version control or cross-machine sync - Each machine required manual setup of global AI tool configurations
Features Implemented:
-
User Mode (
--user/-uflag):- All add/remove/install commands accept
--userflag - Sets
projectPath = $HOMEautomatically - Stores dependencies in
~/.config/ai-rules-sync/user.json - Skips gitignore management (home dir isn't a git repo)
- All add/remove/install commands accept
-
claude-md Adapter:
- New adapter for CLAUDE.md-style files (
.claude/<name>.md) - File mode with
.mdsuffix; resolvesCLAUDE→CLAUDE.md - CLI:
ais claude md [add|remove|install|import] - User mode usage:
ais claude md add CLAUDE --user
- New adapter for CLAUDE.md-style files (
-
One-click User Install:
ais user install/ais install --user- Reads all entries from
user.jsonand recreates symlinks (perfect for new machine setup)
-
User Config Path Management:
ais config user show- View current user.json pathais config user set <path>- Set custom path (for dotfiles integration)ais config user reset- Reset to default path- Stored as
userConfigPathin~/.config/ai-rules-sync/config.json
-
claude-rules Adapter (formalized):
- Adapter for
.claude/rules/files (.mdsuffix) - CLI:
ais claude rules [add|remove|install|import]
- Adapter for
-
skipIgnorein SyncOptions:- New optional field prevents gitignore management in user mode
- Set automatically when
--useris used
-
Automatic Migration:
- On first use, auto-renames
global.json→user.jsonin the config directory - Auto-renames
globalConfigPath→userConfigPathinconfig.json
- On first use, auto-renames
Implementation:
src/adapters/claude-md.ts- New claude-md adaptersrc/config.ts- AddeduserConfigPath,getUserConfigPath(),getUserProjectConfig(),saveUserProjectConfig()(replacingglobal*equivalents)src/project-config.ts- Addedclaude.mdandclaude.rulesto config interfaces; addedaddUserDependency(),removeUserDependency()src/adapters/types.ts- AddedskipIgnore?: booleantoSyncOptionssrc/sync-engine.ts- RespectskipIgnoreinlinkEntry()src/adapters/index.ts- RegisteredclaudeMdAdaptersrc/commands/handlers.ts- Addeduser?andskipIgnore?toCommandContext; user path forhandleAdd/handleRemovesrc/commands/install.ts- AddedinstallUserEntriesForAdapter(),installAllUserEntries()src/commands/config.ts- AddedhandleUserConfigShow/Set/Reset()src/cli/register.ts- Added-u, --userflag to add/remove/install commandssrc/index.ts- Addedais claude mdsubgroup,ais user install,ais config usercommands
Files Changed: 11 modified/new, all tests passing (206/206)
Added complete support for OpenAI Codex project-level rules and skills:
Problem Solved:
- Teams using Codex needed to share rules and skills across projects
- No centralized way to distribute Codex configurations
- Manual copying led to drift and inconsistency
Features Implemented:
-
Codex Rules Adapter:
- File mode with
.rulessuffix - Source:
.codex/rules/ - Syntax:
ais codex rules add <ruleName> [alias] - Purpose: Control which commands can run outside sandbox using Starlark syntax
- File mode with
-
Codex Skills Adapter:
- Directory mode (SKILL.md + optional scripts/assets)
- Source:
.agents/skills/(non-standard location per Codex docs) - Syntax:
ais codex skills add <skillName> [alias] - Purpose: Task-specific capabilities that extend Codex
-
CLI Commands:
ais codex install- Install all Codex rules and skillsais codex add-all- Discover and add all entries from repositoryais codex import <name>- Import from project to repository (auto-detects subtype)ais codex rules [add|remove|install|import]- Rules managementais codex skills [add|remove|install|import]- Skills management
-
Configuration Support:
- Extended
SourceDirConfigwithcodex.rulesandcodex.skills - Extended
ProjectConfigwithcodex.rulesandcodex.skillsrecords - Full support for custom source directories via
-soption - Backward compatible with existing configs
- Extended
-
Shell Completion:
- Added Codex to bash, zsh, and fish completion scripts
- Dynamic completion for rule/skill names via
ais _complete codex-rules|codex-skills - Context-aware completions for all subcommands
-
Mode Detection:
- Added 'codex' to
DefaultModetype ais installsmart dispatch includes Codex- Auto-detect Codex-only projects
- Added 'codex' to
Implementation:
src/adapters/codex-rules.ts- Rules adapter (file mode,.rulessuffix)src/adapters/codex-skills.ts- Skills adapter (directory mode,.agents/skills)src/adapters/index.ts- Registered adapters and added tofindAdapterForAliassrc/project-config.ts- Extended configuration interfaces and helperssrc/commands/helpers.ts- Added 'codex' mode type and inferencesrc/index.ts- Full CLI command hierarchy with install/add-all/importsrc/completion/scripts.ts- Shell completion for all three shellstests/codex-adapters.test.ts- Complete test coverage (9 tests)
Files Changed: 9 new/modified, all tests passing (126/126)
Benefits:
- Centralized Codex configuration management
- Team-wide consistency for sandbox rules
- Easy sharing of skills across projects
- Follows same pattern as other supported tools
Added 4-layer priority system for custom source directory configuration:
Problem Solved:
- Third-party repositories without
ai-rules-sync.jsonwere unusable - Repositories with non-standard directory structures (e.g.,
rules/cursor/instead of.cursor/rules) required forking - No way to override source directories without modifying the repository
Solution - 4-Layer Priority:
CLI Parameters > Global Config > Repository Config > Adapter Defaults
Features Implemented:
-
CLI Parameters (
-s/--source-dir):- Simple format:
ais cursor rules add-all -s custom/rules - Dot notation:
ais add-all -s cursor.rules=custom/rules - Supports multiple
-sflags - Context-aware parsing (infers tool/subtype from command)
- Simple format:
-
Global Configuration Commands:
ais config repo set-source <name> <tool.subtype> <path>- Persist custom sourceDirais config repo show <name>- View repository configurationais config repo clear-source <name> [tool.subtype]- Remove custom sourceDirais config repo list- List all repositories with sourceDir
-
Enhanced Architecture:
- Extended
RepoConfiginterface withsourceDir?: SourceDirConfig - Enhanced
getSourceDir()withglobalOverrideparameter (4-layer priority logic) - All
add-allcommands support-soption (top-level, tool-level, subtype-level) - Custom parser with simple and dot notation format support
- Extended
-
Configuration Persistence:
- Global config stored in
~/.config/ai-rules-sync/config.json - Per-repository
sourceDirconfiguration - CLI overrides have highest priority (temporary)
- Global config persists across sessions
- Global config stored in
Use Cases:
- Exploration:
ais cursor rules add-all -s custom/rules --dry-run - Persistent:
ais config repo set-source my-repo cursor.rules custom/rules - Override: CLI parameter overrides saved configuration
Files Changed:
src/config.ts- AddedsourceDirtoRepoConfiginterfacesrc/project-config.ts- EnhancedgetSourceDir()with priority logicsrc/commands/add-all.ts- AddedsourceDirOverridesparameter throughoutsrc/cli/source-dir-parser.ts- New parameter parsing modulesrc/commands/config.ts- New config management commandssrc/cli/register.ts- Added-soption to all adapter commandssrc/index.ts- Added-sto all add-all commands + config command groupREADME.md- Added comprehensive documentation section
Benefits:
- Works with any repository (no ai-rules-sync.json required)
- Flexible (CLI for quick tests, config for persistent use)
- Non-destructive (doesn't modify third-party repositories)
- User-friendly (smart context detection in simple format)
Changed global configuration location to follow XDG Base Directory specification:
- Old:
~/.ai-rules-sync/ - New:
~/.config/ai-rules-sync/
Impact:
- Global config file:
~/.config/ai-rules-sync/config.json - Repository cache:
~/.config/ai-rules-sync/repos/ - No automatic migration provided - users must manually move files if needed
- Aligns with Linux/macOS standards for configuration file placement
Files Changed:
src/config.ts- UpdatedCONFIG_DIRconstanttests/config.test.ts- Updated test fixtures- Documentation updated to reflect new paths
Added --only and --exclude options to ais init for selective tool initialization:
Problem Solved:
ais initgenerated config and directories for all 14+ supported tools, which is overwhelming for users who only use a few tools.
Features Implemented:
-
--only <tools...>: Only include specified tools in the generated config and directories.- Example:
ais init --only cursor claude— only initializes Cursor and Claude sections.
- Example:
-
--exclude <tools...>: Exclude specified tools from initialization.- Example:
ais init --exclude trae opencode gemini— initializes everything except those tools.
- Example:
-
Mutual exclusivity:
--onlytakes precedence if both are provided. -
Scope: Both options affect
sourceDirconfig generation and source directory creation.
Implementation:
src/commands/lifecycle.ts— Addedonly/excludetoInitOptions; addedgetFilteredAdapters()helper; updatedinitRulesRepository()andbuildTemplateSourceDirConfig()to use filtered adapters.src/index.ts— Added--onlyand--excludeCLI options to theinitcommand.src/__tests__/lifecycle-init.test.ts— Added 3 new tests for--only,--exclude, and directory filtering.
Files Changed: 3 modified, all tests passing
Added end-to-end release automation via GitHub Actions:
-
release.yml:- Triggered on push to
main(andworkflow_dispatch) - Uses
changesets/actionto create/update release PRs and publish npm automatically - Publishes downstream outputs (
published,published_version) for dependent jobs
- Triggered on push to
-
update-homebrew.yml(refactored):- Converted to reusable workflow (
workflow_call) and manual rerun (workflow_dispatch) - Accepts explicit
versioninput (no registry race on version lookup) - Computes npm tarball SHA256 and updates
Formula/ais.rbin this repository
- Converted to reusable workflow (
Distribution Impact:
- Homebrew tap uses explicit repository URL:
brew tap lbb00/ai-rules-sync https://github.com/lbb00/ai-rules-syncbrew install ais
- npm remains the source artifact for Homebrew formula updates
Added complete support for OpenCode AI (https://opencode.ai) with 5 component types:
- Rules (
.opencode/rules/) - File mode with.mdsuffix - Agents (
.opencode/agents/) - Directory mode - Skills (
.opencode/skills/) - Directory mode - Commands (
.opencode/commands/) - Directory mode - Custom-tools (
.opencode/custom-tools/) - Directory mode
Implementation:
- Created 5 new adapters following existing patterns
- Extended
ProjectConfigandSourceDirConfiginterfaces - Added CLI commands:
ais opencode [rules|agents|skills|commands|custom-tools] [add|remove|install|import] - Updated shell completion scripts (bash, zsh, fish)
- Added
_completecommand support for all OpenCode types - Updated mode inference to recognize OpenCode projects
Files Changed:
src/adapters/opencode-rules.ts- New adapter (file mode, .md)src/adapters/opencode-agents.ts- New adapter (directory mode)src/adapters/opencode-skills.ts- New adapter (directory mode)src/adapters/opencode-commands.ts- New adapter (directory mode)src/adapters/opencode-custom-tools.ts- New adapter (directory mode)src/adapters/index.ts- Registered all 5 OpenCode adapterssrc/project-config.ts- Extended configuration interfacessrc/commands/helpers.ts- Added 'opencode' mode typesrc/index.ts- Added OpenCode CLI commands and completionsrc/completion/scripts.ts- Added OpenCode to all shell completionsREADME.md- Documented OpenCode supportKNOWLEDGE_BASE.md- Updated architecture and feature documentation
Fixed OpenCode adapter modes to match official OpenCode documentation:
- Removed
opencode-rulesadapter (OpenCode doesn't have a rules type) - Fixed
opencode-agents- Changed from directory to file mode with.mdsuffix - Fixed
opencode-commands- Changed from directory to file mode with.mdsuffix - Renamed
opencode-custom-tools→opencode-tools- Changed to file mode with.ts/.jssuffixes - Kept
opencode-skills- Remains directory mode (contains SKILL.md inside)
Added Universal AGENTS.md Support:
- New adapter:
agents-md- Tool-agnostic support for the agents.md standard - Syncs AGENTS.md files from repository to project root
- Makes agent definitions available to any AI coding tool supporting the agents.md format
- Mode: File mode with
.mdsuffix - Target: Project root (
.)
Configuration Changes:
- Removed
opencode.rulesfrom all config interfaces - Renamed
opencode['custom-tools']→opencode.tools - Added new top-level
agentsMdconfiguration section - Updated
SourceDirConfigandProjectConfiginterfaces
CLI Changes:
- Added new top-level command group:
ais agents-md [add|remove|install|import] - Removed
ais opencode rulessubcommand - Changed
ais opencode custom-tools→ais opencode tools - Updated all error messages and help text
Implementation:
src/adapters/agents-md.ts- New universal adaptersrc/adapters/opencode-rules.ts- Deletedsrc/adapters/opencode-custom-tools.ts- Renamed toopencode-tools.tssrc/adapters/opencode-agents.ts- Fixed to file modesrc/adapters/opencode-commands.ts- Fixed to file modesrc/adapters/opencode-tools.ts- Updated to file mode with .ts/.jssrc/adapters/index.ts- Updated registrysrc/project-config.ts- Updated all config interfacessrc/commands/helpers.ts- Added 'agents-md' modesrc/index.ts- Updated CLI commands and completionsrc/completion/scripts.ts- Updated all shell completionsREADME.md&README_ZH.md- Documented changes with reference linksKNOWLEDGE_BASE.md- Updated architecture documentation
Reference Links Added: All documentation now includes links to official tool documentation for easy reference.
Redesigned agents-md adapter for flexible AGENTS.md location:
Problem Solved:
- Old adapter required AGENTS.md files in fixed
agents-md/directory - Didn't align with agents.md standard allowing files anywhere in repo
- Limited name resolution didn't support path-based lookup
New Design:
- Changed default source directory:
agents-md→.(repository root) - Flexible path resolution supports 4 patterns:
- Explicit file path:
frontend/AGENTS.md→repo/frontend/AGENTS.md - Directory path:
docs/team→repo/docs/team/AGENTS.md(auto-appends /AGENTS.md) - Simple name:
frontend→ triesrepo/frontend/AGENTS.md, thenrepo/AGENTS.md - Root level:
.orAGENTS→repo/AGENTS.md
- Explicit file path:
- Case insensitive: Supports both
AGENTS.mdandagents.mdvariants - Custom config management: Uses flat
agentsMdstructure instead of nested like other tools
Configuration Changes:
// Old (rules repo)
{ "sourceDir": { "agentsMd": { "file": "agents-md" } } }
// New (rules repo) - supports any directory
{ "sourceDir": { "agentsMd": { "file": "." } } }
// Project config - flat structure with flexible paths
{
"agentsMd": {
"root": { "url": "...", "rule": "AGENTS.md" },
"frontend": { "url": "...", "rule": "frontend/AGENTS.md" },
"platform": { "url": "...", "rule": "docs/teams/platform/AGENTS.md" }
}
}Implementation Details:
- Custom
resolveSourcewith multi-pattern path resolution - Custom
addDependency/removeDependencyfor flat config structure - Fixed alias handling in
handleAddto prioritize user-provided aliases - Validates that only AGENTS.md files are supported (rejects other .md files)
Migration:
- No breaking changes: Old configs continue to work
- Existing
agents-md/AGENTS.mdwill still be found - Users can gradually migrate to new flexible structure
- New projects can organize AGENTS.md files by directory (frontend/, backend/, etc.)
Files Changed:
src/adapters/agents-md.ts- Complete rewrite with flexible resolutionsrc/project-config.ts- Updated default comment and config handlingsrc/commands/handlers.ts- Fixed alias handling for all adaptersKNOWLEDGE_BASE.md,README.md,README_ZH.md- Documentation updates
Added support for custom target directories per entry:
Feature Overview:
- Users can now specify custom target directories for each synced entry
- Allows flexible organization beyond default tool directories
- Perfect for documentation projects, monorepos, and custom team structures
- Supports aliasing same rule to multiple locations
Implementation:
- Entry-level configuration: Each config entry can specify
targetDirfield - CLI option: Added
-d, --target-dir <dir>to alladdcommands - Priority resolution: options.targetDir > config entry targetDir > adapter default
- Conflict detection: Prevents overwriting when adding same rule to different locations without alias
- Suffix handling: Properly resolves file suffixes when removing aliased entries
Configuration Examples:
{
"cursor": {
"rules": {
// Default target directory (.cursor/rules/)
"standard-rule": "https://github.com/company/rules",
// Custom target directory
"docs-rule": {
"url": "https://github.com/company/rules",
"targetDir": "docs/ai/rules"
},
// Same rule to multiple locations (requires alias)
"frontend-auth": {
"url": "https://github.com/company/rules",
"rule": "auth-rules",
"targetDir": "packages/frontend/.cursor/rules"
},
"backend-auth": {
"url": "https://github.com/company/rules",
"rule": "auth-rules",
"targetDir": "packages/backend/.cursor/rules"
}
}
}
}CLI Usage:
# Add rule to custom directory
ais cursor add my-rule -d docs/ai/rules
# Add same rule to multiple locations (requires alias)
ais cursor add auth-rules frontend-auth -d packages/frontend/.cursor/rules
ais cursor add auth-rules backend-auth -d packages/backend/.cursor/rules
# Remove specific location (uses alias as config key)
ais cursor remove frontend-auth
# Install respects custom targetDir from config
ais cursor installKey Behaviors:
- No alias needed for first-time adds or when targeting different source files
- Alias required when adding same source file to different location (prevents config key conflicts)
- Backward compatible: Entries without
targetDiruse adapter default - Config format: Uses simple string when no custom targetDir; object format when specified
- Install support:
installcommand reads and respectstargetDirfrom config
Files Changed:
src/project-config.ts- ExtendedRuleEntrytype, addedgetTargetDir()andgetEntryConfig()src/sync-engine.ts- Dynamic target directory resolution inlinkEntry(),unlinkEntry(),importEntry()src/adapters/types.ts- ExtendedSyncOptionsandaddDependencysignaturesrc/adapters/base.ts- PasstargetDirthrough to config functionssrc/adapters/agents-md.ts- Updated customaddDependency()fortargetDirsupportsrc/commands/handlers.ts- AddedAddOptionswith conflict detectionsrc/commands/install.ts- Extract and passtargetDirduring installationsrc/cli/register.ts- Added-d, --target-diroption to adapter commandssrc/index.ts- Added-d, --target-dirto hardcoded cursor/copilot commands- All tests passing (105/105)
Added complete support for Gemini CLI (https://geminicli.com/) with three entry types:
Problem Solved:
- Teams using Gemini CLI needed to share commands, skills, and agents across projects
- No centralized way to distribute Gemini CLI configurations
- Manual copying led to drift and inconsistency
Features Implemented:
-
Gemini Commands Adapter:
- File mode with
.tomlsuffix (native Gemini CLI format) - Source:
.gemini/commands/ - Syntax:
ais gemini commands add <commandName> [alias] - Purpose: Reusable prompts with argument substitution
- File mode with
-
Gemini Skills Adapter:
- Directory mode (SKILL.md + optional assets)
- Source:
.gemini/skills/ - Syntax:
ais gemini skills add <skillName> [alias] - Purpose: Specialized expertise for specific tasks
-
Gemini Agents Adapter:
- File mode with
.mdsuffix (Markdown with YAML frontmatter) - Source:
.gemini/agents/ - Syntax:
ais gemini agents add <agentName> [alias] - Purpose: Specialized agents with defined capabilities
- File mode with
-
CLI Commands:
ais gemini install- Install all Gemini commands, skills, and agentsais gemini add-all- Discover and add all entries from repositoryais gemini import <name>- Import from project to repository (auto-detects subtype)ais gemini commands [add|remove|install|import]- Commands managementais gemini skills [add|remove|install|import]- Skills managementais gemini agents [add|remove|install|import]- Agents management
-
Configuration Support:
- Extended
SourceDirConfigwithgemini.commands,gemini.skills, andgemini.agents - Extended
ProjectConfigwithgemini.commands,gemini.skills, andgemini.agentsrecords - Full support for custom source directories via
-soption - Backward compatible with existing configs
- Extended
-
Shell Completion:
- Added Gemini CLI to bash, zsh, and fish completion scripts
- Dynamic completion for command/skill/agent names via
ais _complete gemini-commands|gemini-skills|gemini-agents - Context-aware completions for all subcommands
-
Mode Detection:
- Added 'gemini' to
DefaultModetype ais installsmart dispatch includes Gemini CLI- Auto-detect Gemini-only projects
- Added 'gemini' to
Implementation:
src/adapters/gemini-commands.ts- Commands adapter (file mode,.tomlsuffix)src/adapters/gemini-skills.ts- Skills adapter (directory mode)src/adapters/gemini-agents.ts- Agents adapter (file mode,.mdsuffix)src/adapters/index.ts- Registered adapters and added tofindAdapterForAliassrc/project-config.ts- Extended configuration interfaces and helperssrc/commands/helpers.ts- Added 'gemini' mode type and inferencesrc/index.ts- Full CLI command hierarchy with install/add-all/importsrc/completion/scripts.ts- Shell completion for all three shellssrc/__tests__/gemini-commands.test.ts- Commands adapter tests (5 tests)src/__tests__/gemini-skills.test.ts- Skills adapter tests (5 tests)src/__tests__/gemini-agents.test.ts- Agents adapter tests (5 tests)README.md,README_ZH.md- Documentation updates
Files Changed: 15 new/modified, all tests passing (166/166)
Benefits:
- Centralized Gemini CLI configuration management
- Team-wide consistency for commands, skills, and agents
- Easy sharing of configurations across projects
- Native TOML support for commands
- Follows same pattern as other supported tools (Cursor, OpenCode, Codex)