Self-contained AI config sync for Codex, Cursor, and Gemini.
Project description
ai-sync
A self-contained local config store for Codex, Cursor, and Gemini CLI. Define agents, skills, MCP servers, and client settings once in ~/.ai-sync/, then sync them to every client with one command.
License: PolyForm Noncommercial 1.0.0 (non-commercial use only).
Overview
This repo provides:
- The ai-sync CLI – Manage
~/.ai-sync/and run syncs - Agents – Sub-agents derived from prompts with per-client metadata
- Skills – Agent Skills (SKILL.md) mirrored to all clients
- Rules/commands – Prompt snippets/shortcuts mirrored to each client’s equivalent feature
- MCP servers – Model Context Protocol servers with centralized config and secrets
- Client configuration – Generic settings (subagents, mode) derived into client-specific configs
- OAuth token portability – Manual copy of client OAuth caches across machines (automated capture/restore planned)
All syncing is idempotent: identical targets cause no writes; changed targets overwrite in place.
Install
End users (recommended)
pipx install ai-sync
Local development
poetry sync --with dev
Optional (task runner):
brew install just
just install
just test
just release 0.1.4
Quick start
ai-sync setup --op-account NAME
ai-sync import --repo /path/to/config-repo # optional
ai-sync sync
Prerequisites
- Python 3.11+
- Codex, Cursor, and/or Gemini CLI installed
- 1Password Desktop app (for
OP_ACCOUNT) orOP_SERVICE_ACCOUNT_TOKENfor service accounts - For MCP stdio servers: Node.js (
npx), uv (uvxfor workspace-mcp),pip install mcp-server-fetchfor fetch
Project structure
This repo ships the sync tool. Runtime data lives in ~/.ai-sync/.
Repo:
.
├── src/
│ └── ai_sync/ # Python: ai-sync
│ └── .client-versions.json # Supported client versions (packaged)
├── tests/
├── pyproject.toml
└── README.md
Runtime:
~/.ai-sync/
├── config.toml # op_account, secret_provider
├── .env.tpl # MCP secrets (op:// refs resolved via 1Password)
├── config/
│ ├── prompts/
│ ├── skills/
│ ├── rules/
│ ├── mcp-servers.yaml
│ └── client-settings.yaml
└── cache/
Import repo layout:
<repo>/
├── prompts/
├── skills/
├── mcp-servers.yaml
├── client-settings.yaml
├── .env.tpl # optional
└── rules/ # optional
ai-sync
Usage
ai-sync setup --op-account NAME
ai-sync import --repo /path/to/config-repo # optional
ai-sync sync
Running ai-sync with no subcommand defaults to sync.
Other commands: ai-sync setup, ai-sync import, ai-sync doctor.
Sync options
| Option | Description |
|---|---|
| (none) | Full sync: agents → skills → rules → MCP servers → client config |
--force |
Update the packaged client version lock (dev-only), then sync |
--no-interactive |
Skip interactive prompts |
--plain |
Plain output (implies --no-interactive) |
--override / --override-json |
Override manifest leaf values (e.g. /servers/context7/enabled=false) |
Sync order
- Agents – From
~/.ai-sync/config/prompts/*.md→~/.codex/agents/,~/.cursor/agents/,~/.gemini/agents/ - Skills – From
~/.ai-sync/config/skills/*/→~/.codex/skills/,~/.cursor/skills/,~/.gemini/skills/ - Rules – From
~/.ai-sync/config/rules/→ client rule/command locations - MCP servers – From
~/.ai-sync/config/mcp-servers.yaml→ client MCP configs, MCP instructions - Client config – From
~/.ai-sync/config/client-settings.yaml→ approval policy, sandbox, features
Sync strategy
- Agents, skills, rules, client config: Files overwritten if they exist. Untracked agents/skills/rules (not in
~/.ai-sync/config/prompts/,~/.ai-sync/config/skills/, or~/.ai-sync/config/rules/) are left alone. - Client config: Deep-merge with existing; ai-tools keys overwrite on conflict.
- MCP servers: Merged with existing; managed servers updated, user-added servers preserved.
Agents
Source structure
Each agent lives in ~/.ai-sync/config/prompts/:
~/.ai-sync/config/prompts/
├── <agent_name>.md # Prompt content (required)
└── <agent_name>.metadata.yaml # Optional metadata
Metadata schema (~/.ai-sync/config/prompts/<name>.metadata.yaml)
Metadata is generic (client-agnostic). The sync script adapts it per client.
| Key | Description | Default |
|---|---|---|
slug |
Agent ID (kebab-case) | Derived from filename |
name |
Display name | From filename |
description |
Short description | Extracted from prompt |
Untracked agents (in client but not in ~/.ai-sync/config/prompts/) are left alone.
Target layout per client
| Client | Target |
|---|---|
| Codex | ~/.codex/agents/<slug>/prompt.md + config.toml |
| Cursor | ~/.cursor/agents/<slug>.md (frontmatter) |
| Gemini | ~/.gemini/agents/<slug>.md (frontmatter) |
Skills
Skills are directories under ~/.ai-sync/config/skills/ with a SKILL.md file. The sync script mirrors each skill to all three clients.
Structure
~/.ai-sync/config/skills/
└── <skill-name>/
├── SKILL.md # Required
├── reference.md # Optional
├── examples.md # Optional
└── scripts/ # Optional; copied if present
Paths containing .venv, node_modules, __pycache__, .git, or .DS_Store are skipped. Untracked skills (in client but not in ~/.ai-sync/config/skills/) are left alone.
Targets
~/.codex/skills/<skill-name>/~/.cursor/skills/<skill-name>/~/.gemini/skills/<skill-name>/
MCP servers
Manifest (~/.ai-sync/config/mcp-servers.yaml)
servers:
<server_id>:
method: stdio | http | sse
command: npx
args: ["-y", "@modelcontextprotocol/server-xxx"]
enabled: true
clients: [codex, cursor, gemini] # Optional; default: all
timeout_seconds: 60 # Optional; startup/tool timeout (seconds)
trust: true # Optional; Cursor/Gemini: auto-approve tools
STDIO servers – command, args; env vars use "${VAR}" refs resolved from ~/.ai-sync/.env.tpl.
HTTP/SSE servers – url, httpUrl; optional bearer_token_env_var for Codex.
HTTP with OAuth – httpUrl + oauth.enabled: true; clientId/clientSecret from ~/.ai-sync/.env.tpl via "${VAR}":
google-maps-grounding-lite:
method: http
httpUrl: https://mapstools.googleapis.com/mcp
oauth:
enabled: true
clientId: "${GOOGLE_MAPS_GROUNDING_LITE_CLIENT_ID}"
clientSecret: "${GOOGLE_MAPS_GROUNDING_LITE_CLIENT_SECRET}"
Configured servers
| Server | Method | Description |
|---|---|---|
| context7 | stdio | Documentation lookup (@upstash/context7-mcp) |
| fetch | stdio | URL fetch → markdown (python -m mcp_server_fetch) |
| playwright | stdio | Browser control (@playwright/mcp) |
| exa | http | Search API |
| google-workspace-perso | stdio | Gmail, Calendar, Drive (personal @gmail.com, separate OAuth) |
| google-workspace-pro | stdio | Gmail, Calendar, Drive (work @sherpas.com, separate OAuth) |
| google-maps-grounding-lite | http | Maps grounding (OAuth) |
Secrets (~/.ai-sync/.env.tpl)
All MCP secrets live in ~/.ai-sync/.env.tpl. Use op:// references for 1Password:
CONTEXT7_API_KEY=op://Private/AI Tools Secrets/CONTEXT7_API_KEY
EXA_API_KEY=op://Private/AI Tools Secrets/EXA_API_KEY
GOOGLE_OAUTH_CLIENT_ID_PERSO=op://Private/AI Tools Secrets/GOOGLE_OAUTH_CLIENT_ID_PERSO
...
In mcp-servers.yaml, reference them with "${VAR_NAME}" in each server's env or oauth block. The sync script resolves these at runtime via 1Password (requires OP_ACCOUNT or OP_SERVICE_ACCOUNT_TOKEN).
Requirements: 1Password Desktop app (for OP_ACCOUNT) or OP_SERVICE_ACCOUNT_TOKEN (service account).
Client targets
| Client | Target | Strategy |
|---|---|---|
| Codex | ~/.codex/config.toml [mcp_servers.<id>] |
Merge |
| Codex | ~/.codex/mcp.env |
Bearer token exports (source before running Codex) |
| Cursor | ~/.cursor/mcp.json |
Overwrite mcpServers |
| Gemini | ~/.gemini/settings.json mcpServers |
Deep-merge |
OAuth token portability
OAuth tokens are stored per client (e.g. ~/.gemini/mcp-oauth-tokens.json). To move them between machines, copy the client token files manually via a secure channel (rsync, 1Password, etc.). Automated capture/restore is planned.
Client configuration
Single YAML definition → derived into Codex, Gemini, and Cursor.
Schema (~/.ai-sync/config/client-settings.yaml)
| Key | Values | Description |
|---|---|---|
experimental |
true | false |
Enable experimental/preview features; suppress experimental warnings where supported |
subagents |
true | false |
Enable multi-agents, sub-agents, child-prompts, AGENTS.md |
mode |
strict | normal | yolo |
Approval / restriction mode (default: normal) |
tools.sandbox |
true | false |
Gemini only. When false, allow MCP tools filesystem access (uvx, etc.) |
Mode semantics
| Mode | Meaning |
|---|---|
strict |
Most restrictive: read-only where supported; approval required for actions |
normal |
More permissive; allow reads/writes while still requiring approval for destructive actions where supported |
yolo |
No approval prompts, no restrictions (full access) |
Client mapping (from official docs)
| Generic | Codex | Gemini | Cursor |
|---|---|---|---|
| subagents: true | features.multi_agent, features.child_agents_md |
experimental.enableAgents |
— |
| experimental: true | suppress_unstable_features_warning |
experimental.plan |
— |
| tools.sandbox: false | — | tools.sandbox |
— |
| mode: strict | approval_policy=on-request, sandbox_mode=read-only |
general.defaultApprovalMode=plan, tools.sandbox=true |
permissions: {allow:[], deny:[]} |
| mode: normal | approval_policy=untrusted, sandbox_mode=danger-full-access |
general.defaultApprovalMode=auto_edit, tools.sandbox=false |
allow: [Shell(*), Read(*), Write(*), WebFetch(*), Mcp(*:*)] |
| mode: yolo | approval_policy=never, sandbox_mode=danger-full-access |
general.defaultApprovalMode=yolo, tools.sandbox=false |
allow: [Shell(*), Read(*), Write(*), WebFetch(*), Mcp(*:*)] |
Client targets
| Client | Target | Strategy |
|---|---|---|
| Codex | ~/.codex/config.toml |
Deep-merge with existing; ai-tools keys overwrite. No backup. |
| Gemini | ~/.gemini/settings.json |
Deep-merge with existing; ai-tools keys overwrite. No backup. |
| Cursor | ~/.cursor/cli-config.json |
Deep-merge with existing; ai-tools keys overwrite. No backup. |
Dependencies
This project uses Poetry for dependency management and packaging:
poetry sync --with dev
Dependencies: pyyaml>=6.0, tomli>=2.0, tomli-w>=1.0, and others (see pyproject.toml).
Testing
poetry run pytest
Packaging & Release
This project is published to PyPI as ai-sync.
Release checklist
- Update README if anything changed in CLI behavior or setup.
- Run the release:
just release X.Y.Z
This runs ./scripts/release_checks.sh, poetry lock, bumps the version, runs tests, commits, tags, and pushes.
- GitHub Actions runs tests, builds artifacts, publishes to PyPI, and creates a GitHub Release.
Notes
- Use
pipx install ai-syncfor end users. - Keep
ai-syncas the only supported CLI name.
.gitignore
If you maintain a separate config repo for ai-sync import, consider ignoring:
config/mcp-servers.yamlconfig/client-settings.yaml.env.tplknowledge-base/*.env, Python bytecode, virtual envs,.pytest_cache/,node_modules/,.DS_Store, etc.
Workflow summary
New machine setup
- Clone repo (tooling only)
poetry sync --with devai-sync setup --op-account NAME(or setOP_SERVICE_ACCOUNT_TOKEN)ai-sync import --repo /path/to/config-repo(optional)- Ensure
~/.ai-sync/.env.tplhas correct 1Password refs ai-sync sync- For Codex HTTP MCP servers:
source ~/.codex/mcp.envin shell profile
Adding an MCP server
- Edit
~/.ai-sync/config/mcp-servers.yaml - Add required vars to
~/.ai-sync/.env.tpl(useop://refs for secrets) - Run
ai-sync sync
Adding an agent
- Add
~/.ai-sync/config/prompts/<name>.md - (Optional) Add
~/.ai-sync/config/prompts/<name>.metadata.yaml - Run
ai-sync sync
Adding a skill
- Create
~/.ai-sync/config/skills/<skill-name>/SKILL.md - Run
ai-sync sync
Changing client mode
- Edit
~/.ai-sync/config/client-settings.yaml(subagents,mode) - Run
ai-sync sync
References
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ai_sync-0.1.11.tar.gz.
File metadata
- Download URL: ai_sync-0.1.11.tar.gz
- Upload date:
- Size: 43.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
810ed9ec322ca8eacdbd289f7c42a3a1f9e07fbc6f9ce8ab288c0829f7e32017
|
|
| MD5 |
35703789b15cbad6f4d1e52717ce4153
|
|
| BLAKE2b-256 |
c9672f49ea6b0083a0013e775323315a14919496b27cb5f8bcb757d85c259940
|
Provenance
The following attestation bundles were made for ai_sync-0.1.11.tar.gz:
Publisher:
release.yml on Ephasme/ai-sync
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ai_sync-0.1.11.tar.gz -
Subject digest:
810ed9ec322ca8eacdbd289f7c42a3a1f9e07fbc6f9ce8ab288c0829f7e32017 - Sigstore transparency entry: 1003476610
- Sigstore integration time:
-
Permalink:
Ephasme/ai-sync@7069742969e431c931c21881a45f1215b2d8929f -
Branch / Tag:
refs/tags/v0.1.11 - Owner: https://github.com/Ephasme
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7069742969e431c931c21881a45f1215b2d8929f -
Trigger Event:
push
-
Statement type:
File details
Details for the file ai_sync-0.1.11-py3-none-any.whl.
File metadata
- Download URL: ai_sync-0.1.11-py3-none-any.whl
- Upload date:
- Size: 54.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
911121921f9407d21f0d10d772f385b3921c197d33be4392285bcf502d195b8a
|
|
| MD5 |
763c0f0e41fd202db90eccc2abc8c6e1
|
|
| BLAKE2b-256 |
f4dae930aaa3a8bc13d67f5b7bf6f3eb4e559640cb81801f9ffbc7ecb00b4bdd
|
Provenance
The following attestation bundles were made for ai_sync-0.1.11-py3-none-any.whl:
Publisher:
release.yml on Ephasme/ai-sync
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ai_sync-0.1.11-py3-none-any.whl -
Subject digest:
911121921f9407d21f0d10d772f385b3921c197d33be4392285bcf502d195b8a - Sigstore transparency entry: 1003476629
- Sigstore integration time:
-
Permalink:
Ephasme/ai-sync@7069742969e431c931c21881a45f1215b2d8929f -
Branch / Tag:
refs/tags/v0.1.11 - Owner: https://github.com/Ephasme
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7069742969e431c931c21881a45f1215b2d8929f -
Trigger Event:
push
-
Statement type: