You have a renovate.json in 30 repos. A platform team owns the canonical version. Six months later, half those repos are running a config from March that nobody updated. One team added a custom rule. Another deleted a section by accident. Nobody knows which version is "right" anymore.
This is config drift. By the time anyone looks, the configs have diverged.
What avanti does
avanti is a CLI tool that pulls local files from a declarative YAML spec. You describe what each file should contain and where it comes from. Then you run avanti pull.
It has two commands:
-
avanti diff— shows a colored git-diff-style preview of what would change, exits1if changes exist -
avanti pull— fetches everything, shows the diff, and asks for confirmation before writing
No daemon, no server, no agent. Just a spec file and two commands.
Sources
A src value can be an HTTP URL, a local path, a GitHub file, a GitLab file, a shell command, or inline raw content. You pick per entry.
files:
# HTTP
- src: https://example.com/base-config.yml
target: base-config.yml
# Local path
- src: ~/shared/scripts/deploy.sh
target: scripts/deploy.sh
mode: '0755'
# GitHub
- src:
github:
repo: org/standards
file: eslint.config.js
ref: main
# Shell command
- src:
exec: aws ssm get-parameter --name /app/db-config --with-decryption --query Parameter.Value --output text
target: config/db.json
mode: '0600'
# Inline content
- src:
raw: |
# THIS FILE IS MANAGED — run `avanti pull` to update
target: header.txt
You can also combine multiple sources into one file by passing src as a list. They're fetched in order and joined with a newline.
Variables
Define reusable values once, reference them everywhere with $name. Environment variables use $env:NAME.
variables:
standards_ref: v2.4.1
region: eu-west-1
files:
- src:
github:
repo: org/standards
file: renovate.json
ref: $standards_ref
- src:
github:
repo: org/infra
file: k8s/deployment-template.yaml
ref: $env:DEPLOY_VERSION
target: k8s/deployment.yaml
replace:
- from: '{REGION}'
to: $region
- from: '{ENV}'
to: $env:ENVIRONMENT
Bump standards_ref in one place. Run avanti diff. See every file that will change before you touch anything.
Post-processing
Each entry supports a replace list for string or regex substitutions, and a post field to pipe content through a shell script. Both run after fetching, before writing.
- src: https://example.com/template.yml
target: config.yml
replace:
- from: '{EMAIL}'
to: $email
- from: /\d+\.\d+\.\d+/
to: $env:APP_VERSION
post: yq e '.metadata.name = "my-app"' -
Use case 1: A global CLAUDE.md your whole team actually shares
Per-project CLAUDE.md files should contain project-specific context. But company-wide coding standards, team conventions, and security guidelines don't belong there — they belong in a global ~/.claude/CLAUDE.md that every developer on the team carries. The problem is keeping that file in sync across 20 machines.
avanti solves this by assembling the global file from central sources. Each developer has a personal .avanti.yml (or the team ships one via onboarding). Running avanti pull rebuilds the file from the current canonical versions.
variables:
team: backend
oncall_channel: '#backend-oncall'
files:
- src:
- raw: |
# AI Assistant Guidelines
<!-- THIS FILE IS MANAGED — run `avanti pull` to update -->
- gitlab:
project: platform/ai-standards
file: teams/backend-rules.md
ref: main
- github:
repo: org/shared-prompts
file: company-standards.md
ref: main
- raw: |
## Team Context
Team: $team
Oncall: $oncall_channel
target: ~/.claude/CLAUDE.md
The platform team updates backend-rules.md once. Every developer who runs avanti pull gets it. No Slack message asking which version is current, no six-month-old guidelines silently telling the AI the wrong thing.
Use case 2: Pinned shared tooling configs
variables:
standards_ref: v2.4.1
files:
- src:
github:
repo: org/standards
file: renovate.json
ref: $standards_ref
- src:
github:
repo: org/standards
file: eslint.config.js
ref: $standards_ref
- src:
github:
repo: org/standards
file: tsconfig.base.json
ref: $standards_ref
All files pinned to v2.4.1. When the platform team cuts v2.5.0, projects bump one variable, diff, and apply. No manual file hunting.
Use case 3: Drift detection in CI
files:
- src:
- raw: |
# THIS FILE IS MANAGED — run `avanti pull` to update
- github:
repo: org/ci-templates
file: workflows/security-scan.yml
ref: main
target: .github/workflows/security-scan.yml
Add avanti diff as a CI step. If a developer edited the managed file by hand, the pipeline fails. The fix is to run avanti pull, not to argue about whose edit was right.
Atomic writes
avanti stages all files to a temp directory first. If any source fails, nothing gets written. You either get a full successful update or nothing changes. No half-applied state.
Working directory and path safety
All src and target paths resolve relative to the working directory, not the config file's location. This lets you use one shared config across many projects:
for dir in services/*/; do
avanti -c shared/avanti.yml -w "$dir" pull --yes
done
Target paths cannot escape the working directory. A target of ../../etc/passwd is an error. Absolute targets are only allowed when the working directory is /.
Install
npm install -g @udondan/avanti
Or run without installing:
npx @udondan/avanti --help
Drop a .avanti.yml in your project root, define your files, and run avanti diff to see what it would do.
The config format is in the README. The source is on GitHub at udondan/scync. Issues and PRs are open.
If you've handled this differently, I'm curious what you're doing.
Top comments (0)