Packages from the terminal
A package is a set of skills, rules, agents, commands, contexts and instructions for your coding agents. You install it once, pick which of its items are active, and Invariant writes them where each of your harnesses reads them. The app's Packages page does the same; see Packages and skills.
Set up a project
invariant init # the claude target
invariant init claude codex # several targets
init creates invariant.json (the project's manifest: targets, packages, active items) and the .invariant/ folder. Commit invariant.json. With --organization <name> it also registers the project in an organisation this machine is signed in to. --copy copies items into each target folder instead of linking them.
Targets are the harnesses you write files for: claude, codex, cursor, copilot, aider. Change them later without touching the packages:
invariant target # the configured targets
invariant target add cursor # add one, and write its files now
invariant target remove cursor
Which file each target gets is on Agents and harnesses.
Install a package
install downloads a package into the project's cache and activates all its items.
invariant install github:owner/repo # a GitHub repository, default branch
invariant install github:owner/repo#v1.2.0 # at a branch, tag or commit
invariant install skills:owner/repo # a skills.sh source, every skill it ships
invariant install skills:owner/repo/skill # the same source, only that skill active
invariant install # reinstall every package invariant.json lists
A skills.sh page URL is accepted as-is. From skills.sh, the whole repository is cached, so add can turn on its other skills later without a second download; the package is named @owner/repo. Skill folders are copied whole. Binary files and files over 256 KB are skipped and listed. Nothing is run at install time.
--no-add only caches the package. --sync (also on add and add-interactive) runs sync right after.
Choose what is active
invariant add my-package # every item
invariant add skills:my-package # every skill
invariant add skill:my-package/tdd rule:my-package/naming
invariant remove skill:my-package/tdd # deactivate; the package stays cached
invariant add-interactive # a checkbox tree of every installed item
Types can be written singular or plural: agent, skill, command, rule, context, instruction. add-interactive shows package, type and item rows; toggling a package or type row toggles its items (--packages gives one box per package).
uninstall <package> removes a package completely: its active items, its cache and its entry in invariant.json.
To see what you have:
invariant list # installed packages (--active for the active ones only)
invariant inspect # the active items of every package
invariant inspect my-package # every item of one package, active ones highlighted
invariant inspect --paths # package, type, installed name and path, tab-separated
Sync
invariant sync brings the project back in line with invariant.json. It rebuilds items that went missing, writes every active item for every target, removes what is no longer active, and regenerates the context files (CLAUDE.md, AGENTS.md…). Run it after a clone, after you change targets, or after editing invariant.json by hand.
invariant sync
invariant sync --target claude # one target only
invariant sync --check # report drift, write nothing; exits 1 when out of date
What sync does with your files:
- One copy. Every active agent, skill, command and rule is stored once, in
.agents/. Claude Code gets links to it; Codex, Cursor and Copilot read.agents/skills/directly. Contexts and instructions go into the context files. - Names carry their package. An item is installed as
<package>-<item>: thetddskill ofmy-packagelands in.agents/skills/my-package-tdd. Two packages can ship a skill of the same name. You keep addressing it asskill:my-package/tdd. - It deletes only what it can rebuild. Skills, agents, commands and rules you keep in
.agents/yourself, from no package, are local items: never deleted or overwritten, linked for Claude Code, and listed in every context file. - Hand edits are kept. A target file you edited is left alone, with a warning, until you sync with
--force. - Git stays clean. Generated files are listed, by exact path, in a marked block of
.gitignore.
For Claude Code, --claude-skills folder makes .claude/skills one link to .agents/skills, so a skill Claude writes there is shared with every harness; per-item keeps one link per skill. The choice is recorded in invariant.json.
Write a package
A package is a folder with one sub-folder per item type, and package metadata in its invariant.json:
my-package/
├── invariant.json
├── agents/planner.md
├── skills/tdd/SKILL.md # a skill is always a folder with a SKILL.md
├── commands/scaffold.md
├── rules/naming.md
├── contexts/overview.md
└── instructions/onboarding/ # any other item can also be a folder of .md files
├── steps.md
└── checklist.md
- agents: how the agent should behave for a kind of task.
- skills: a procedure the agent invokes on demand. Other files in the skill's folder (scripts, references, templates) travel with it. Skill folders may also sit at the package root.
- commands: commands for the harness.
- rules: must and must-not lists.
- contexts: background about the project, folded into the context files.
- instructions: procedures folded into the context files, read on every task.
The metadata sits beside the usual project keys:
{
"name": "@acme/my-package",
"version": "1.0.0",
"description": "TDD and naming rules for our services.",
"author": "Acme",
"license": "MIT",
"repository": "https://example.com/acme/my-package",
"items": {
"skills": [{ "name": "tdd", "description": "Test-first, red then green." }],
"rules": [{ "name": "naming", "description": "How we name files and classes." }]
}
}
You do not write items by hand:
invariant package create --name @acme/my-package --version 1.0.0 --author Acme
invariant package refresh # rescan the folders and update items
invariant package validate # declared but missing, or on disk but not declared
create scans the folder and writes the metadata; each item's description is the first line of its file. validate also warns when an item refers to another by a path that will read differently once installed under its <package>-<item> name. A folder can be both a project and a package: invariant init --hybrid.
To share a package, push the folder to a Git repository; others install it with invariant install github:owner/repo.