Self-contained AI config sync for Codex, Cursor, Gemini, and Claude Code.
Project description
ai-sync
ai-sync synchronizes shared AI tooling artifacts into a project-local setup for Codex, Cursor, Gemini, and Claude Code.
The current workflow is:
- Install
ai-synconce on the machine. - Write a project
.ai-sync.yamlor.ai-sync.local.yaml. - Run
ai-sync plan. - Run
ai-sync apply [planfile].
License: PolyForm Noncommercial 1.0.0.
Install
End users
pipx install ai-sync
Local development
poetry sync --with dev
Poetry is configured to create an in-project .venv/, which is what the just tasks use.
Web UI development
For full hot reload during UI work, run the backend and frontend in separate terminals:
just ui-dev-api
just ui-dev-web
This starts:
- the FastAPI API with Python auto-reload on
http://127.0.0.1:8321 - the Vite frontend with HMR on
http://127.0.0.1:5173
If you want to keep using the packaged static UI instead of Vite HMR, use:
just build-ui-watch
Optional task runner:
brew install just
just install
just test
Machine bootstrap
install is the only machine-level setup step. It writes ~/.ai-sync/config.toml for auth/bootstrap settings such as the 1Password account identifier used by the CLI.
ai-sync install --op-account-identifier example.1password.com
The --op-account-identifier value must be a 1Password sign-in address such as example.1password.com or a 1Password user ID from op account list.
You can also authenticate with OP_SERVICE_ACCOUNT_TOKEN.
Project workflow
1. Write .ai-sync.yaml or .ai-sync.local.yaml
Projects declare their config sources explicitly and select resources by fully scoped ids.
Example:
sources:
company:
source: github.com/acme/company-ai-sync
version: v1.4.0
frontend:
source: ../frontend-ai-sync
agents:
- company/senior-software-engineer
skills:
- company/code-review
- frontend/react-review
commands:
- company/session-summary
rules:
- company/commit-conventions
mcp_servers:
- company/context7
settings:
mode: normal
subagents: true
Notes:
.ai-sync.yamlis the shared project manifest..ai-sync.local.yamlis an optional local override. If it exists,ai-syncignores.ai-sync.yamlentirely and uses the local file as the only project manifest.- Remote sources must be pinned with
version. - Local path sources are allowed, but they are less portable than pinned remote sources.
- If your local SSH setup needs a different Git host than the shared manifest uses, prefer a local Git URL rewrite over checking machine-specific hosts into
.ai-sync.yaml. For example:
git config url."git@example-git-host:example-org/".insteadOf "git@github.com:example-org/"
- Every selected resource must be scoped as
<sourceAlias>/<resourceId>.
2. Run plan
plan resolves sources under the project, validates the selection, computes planned actions, and saves a plan artifact.
ai-sync plan
By default, the saved plan goes to .ai-sync/last-plan.yaml.
You can also choose an explicit output path:
ai-sync plan --out my-plan.yaml
3. Run apply
Use a reviewed plan:
ai-sync apply .ai-sync/last-plan.yaml
Or let ai-sync compute a fresh plan and execute it immediately:
ai-sync apply
The reviewed path is:
ai-sync plan- review the output / saved plan
ai-sync apply <planfile>
Source repo layout
A source repo is a catalog of reusable artifacts:
<source>/
├── agents/
│ └── <artifact-id>/
│ ├── artifact.yaml
│ ├── prompt.md
│ └── files/... # optional reserved bundle assets
├── skills/
│ └── <artifact-id>/
│ ├── artifact.yaml
│ ├── prompt.md
│ └── files/...
├── commands/
│ └── <relative-path>/
│ ├── artifact.yaml
│ ├── prompt.md
│ └── files/... # optional reserved bundle assets
├── rules/
│ └── <artifact-id>/
│ ├── artifact.yaml
│ ├── prompt.md
│ └── files/... # optional reserved bundle assets
└── mcp_servers/
└── <server-id>/
└── artifact.yaml
Resource ids
- Agents come from
agents/<name>/artifact.yamlplusagents/<name>/prompt.mdand are referenced as<alias>/<name>. - Skills come from
skills/<name>/artifact.yamlplusskills/<name>/prompt.mdand are referenced as<alias>/<name>. - Commands come from
commands/**/<name>/artifact.yamlplus siblingprompt.mdand are referenced as<alias>/<relative-path>. - Rules come from
rules/<name>/artifact.yamlplusrules/<name>/prompt.mdand are referenced as<alias>/<name>. - MCP servers come from
mcp_servers/<server-id>/artifact.yamland are referenced as<alias>/<server-id>. - MCP-only: rendered subprocess
envis synthesized from the server's declareddependencies.enventries after runtime resolution (optional per-entryinject_asrenames the subprocess variable while keeping a unique dependency key for merging).
Bundle artifact format
Every artifact bundle uses the same entry-file convention:
<bundle>/
├── artifact.yaml
├── prompt.md # prompt-bearing bundles only
└── files/... # optional bundled assets
For prompts, skills, commands, and rules, artifact.yaml stores metadata only. The markdown body lives in sibling prompt.md:
<bundle>/
├── artifact.yaml
└── prompt.md
Example command bundle:
description: Session summary command
Summarize the current session.
Notes:
- For prompts and rules, default ids are derived from the bundle directory name, not from the literal filename
artifact.yaml. - Skills are authored from
artifact.yamlplusprompt.md;ai-syncgenerates the client-facingSKILL.mdduring sync. - Skill assets live under
skills/<name>/files/...in the source repo and are written to the client skill root without thefiles/prefix (for examplefiles/scripts/tool.pybecomesscripts/tool.py). - Non-skill prompt-bearing bundles may also reserve
files/in the source repo, butai-syncdoes not sync those assets to client outputs yet. - To migrate older inline
prompt:bundles, usemigration/scripts/migrate_to_split_prompt_bundles.py.
Artifact dependencies
Each selected artifact can declare dependencies in artifact.yaml under the dependencies block.
Two dependency kinds are supported: env (environment variables and secrets) and binaries (version-checked executables on PATH).
Example:
dependencies:
env:
PUBLIC_CLIENT_ID: abc123
GITHUB_PAT:
local: {}
description: Personal GitHub PAT
CONTEXT7_API_KEY:
secret:
provider: op
ref: op://Example Vault/AI Tools/CONTEXT7_API_KEY
binaries:
- name: npx
version:
require: ~10.0.0
- name: gh
version:
require: ^2.0.0
get_cmd: gh --version
Env rules
- only dependencies from selected artifacts are resolved
secret.providercurrently supportsoponly- unresolved declared local vars emit warnings; MCP rendering uses empty placeholders for those vars so plan/apply can still complete (set values in
.env.ai-syncfor working MCP servers) .env.ai-syncis generated only when selected dependencies includelocalentries- secret dependencies are never written to
.env.ai-sync - for MCP servers,
ai-syncrenders subprocessenvfromdependencies.env; useinject_aswhen several servers must expose the same subprocess name (for exampleSTRIPE_SECRET_KEY) but need distinct dependency keys and secret refs; avoid top-level MCPenvunless you need${VAR}interpolation outsidedependencies.env
Binary rules
- binary requirements are collected from all selected artifact kinds (agents, skills, commands, rules, MCP servers)
- identical declarations (same name + version constraint + get_cmd) across artifacts are deduplicated
- conflicting declarations (same name, different version or get_cmd) raise a collision error
- version constraints use
~X.Y.Z(compatible within minor) or^X.Y.Z(compatible within major) - each binary is checked by running
[name] --versionunlessget_cmdoverrides the command
Project-local outputs
ai-sync manages project-local files such as:
.codex/*.cursor/*.gemini/*.claude/*.mcp.jsonCLAUDE.md.env.ai-sync.ai-sync/rules/.ai-sync/state/.ai-sync/sources/.ai-sync/last-plan.yaml
It does not modify machine-global client config under ~/.codex, ~/.cursor, ~/.gemini, or ~/.claude.
When rules are selected, ai-sync writes rule files to .ai-sync/rules/ and maintains a small managed link block in AGENTS.md instead of replacing the whole file.
You should usually cover these paths and .ai-sync.local.yaml with .gitignore, but ai-sync no longer blocks plan or apply when they are not ignored.
Reliability rules
- Remote sources must be pinned.
- If two selected resources would write the same non-composable output, planning fails.
- Saved plans are invalidated when the project config or resolved source fingerprints change.
Commands
ai-sync install
ai-sync plan
ai-sync apply [planfile]
ai-sync doctor
ai-sync uninstall [--apply]
Architecture
Dependency injection
- The composition root lives in
ai_sync.di, with providers declared inAppContainer. - The only runtime entrypoint is
cli.main(), which callsbootstrap_runtime()to resolveCommandHandlersService. - Command flows (
install,plan,apply,doctor,uninstall) are implemented on service classes underai_sync.services. - Adapter boundaries (
filesystem,process runner,state store) isolate side effects from orchestration logic.
Universal artifact pipeline
All artifact kinds flow through one shared pipeline:
select → load → prepare (pre-runtime) → resolve RuntimeEnv → prepare (post-runtime) → collect artifacts → resolve ApplySpecs → apply & reconcile
ArtifactPreparationServiceorchestrates preparation in two explicit phases: pre-runtime (validation, env dependency collection) and post-runtime (interpolation, rendering) afterRuntimeEnvis resolved.- The shared
PreparedArtifactsboundary carries per-kind payloads (e.g.PreparedMcpServer) so downstream collectors, requirement checks, and plan builders all receive the same context. - Kind-specific logic (MCP validation, env interpolation, rendered
envsynthesis) lives inMcpPreparationService, called as a preparation hook inside the shared pipeline.
Apply contract
Every artifact resolves into a sequence of ApplySpec, a union of two concrete types:
WriteSpec— managed file writes (text markers, structured JSON/TOML/YAML keys).EffectSpec— managed side effects (pre-commit hook install/remove, file permission changes).
Both types are planned, confirmed, executed, and reversed through the same state-backed reconciliation engine in ManagedOutputService and StateStore.
State management
- Managed state is persisted in
.ai-sync/state/state.jsonwith an explicitSTATE_VERSION. - Write baselines and effect baselines are tracked separately so
uninstallcan restore prior state for both files and side effects. - Incompatible state from an older pipeline version is rejected with an actionable error directing the user to
ai-sync uninstall --applybefore reapplying.
Testing
just test
DI-heavy tests should prefer provider overrides over module monkeypatching:
- Build a fresh container via
create_container(). - Override collaborators with
container.override_providers(...). - Resolve the service under test from the container after overrides are applied.
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-1.8.5.tar.gz.
File metadata
- Download URL: ai_sync-1.8.5.tar.gz
- Upload date:
- Size: 295.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fc4abd08fc5ce7bb0dea2e742361c55956bb65f0eb43050c20766cbdbe8b37ba
|
|
| MD5 |
90fe9c9f4c8c8d82637c97e9e98a7e23
|
|
| BLAKE2b-256 |
0c82823fbff58a8b85c790144d8e4781f91f45672ef24159e16a7bcc5c6a70cd
|
Provenance
The following attestation bundles were made for ai_sync-1.8.5.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-1.8.5.tar.gz -
Subject digest:
fc4abd08fc5ce7bb0dea2e742361c55956bb65f0eb43050c20766cbdbe8b37ba - Sigstore transparency entry: 1252520027
- Sigstore integration time:
-
Permalink:
Ephasme/ai-sync@f034bf2c13c948c3ad633380266ccb89729a699a -
Branch / Tag:
refs/tags/v1.8.5 - Owner: https://github.com/Ephasme
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f034bf2c13c948c3ad633380266ccb89729a699a -
Trigger Event:
push
-
Statement type:
File details
Details for the file ai_sync-1.8.5-py3-none-any.whl.
File metadata
- Download URL: ai_sync-1.8.5-py3-none-any.whl
- Upload date:
- Size: 337.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3f81a6d69490de697fe00ddda1f40b45f81d73f87aecccf0d8367d8195666615
|
|
| MD5 |
754695fad12078aa2340bd9374478cc2
|
|
| BLAKE2b-256 |
93d27df4e6ffeeb2d9ed55bb3c0d2c1724a4f719132640322f01bd6558a70d98
|
Provenance
The following attestation bundles were made for ai_sync-1.8.5-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-1.8.5-py3-none-any.whl -
Subject digest:
3f81a6d69490de697fe00ddda1f40b45f81d73f87aecccf0d8367d8195666615 - Sigstore transparency entry: 1252520048
- Sigstore integration time:
-
Permalink:
Ephasme/ai-sync@f034bf2c13c948c3ad633380266ccb89729a699a -
Branch / Tag:
refs/tags/v1.8.5 - Owner: https://github.com/Ephasme
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f034bf2c13c948c3ad633380266ccb89729a699a -
Trigger Event:
push
-
Statement type: