Keep your coding agents' skills and plugins deployed identically to Claude Code and Codex from one manifest of sources — your own skill repos and pinned third-party ones — and audit your projects so either agent sees the same instructions, skills, and hooks.
tack sync deploys skills and selected plugins from the manifest's sources,
tack status shows which skills and plugins are where, tack outdated and
tack update track upstream, tack add and tack remove edit the manifest,
tack doctor audits, and tack scaffold applies the fixes it suggests. A
path source with autocommit = true (and autopush) has its skill edits
committed, and pushed, whenever a command that changes things runs. A bare
tack opens a TUI over all of it: Skills, Plugins (each grouped by source,
and sortable), Sources and Doctor tabs, keyed 1 to 4, and a Settings
screen for the manifest, with every action previewed before it runs.
Install
tack runs on macOS and Linux and needs git. Install it one of two ways;
either gives you the tack command.
Homebrew
brew install chocs-cat/tap/tack
The formula lives in the chocs-cat/tap tap and brings its own Python.
PyPI
uv tool install tack-agents
This installs the tack-agents package from PyPI (tack is taken there)
into its own environment. It needs Python 3.11 or later;
pipx install tack-agents works the same way.
Use
tack # the TUI
tack add https://github.com/cloudflare/skills.git --skill wrangler
tack add https://github.com/anthropics/claude-plugins-official.git --plugin skill-creator
tack sync --dry-run # what would change
tack sync # fetch pinned sources, link skills into every harness
tack status # sources, pins, and where skills and plugins are deployed
tack outdated --diff # what upstream changed in the skills and plugins you deploy
tack update cloudflare # take it: move the pin, then sync
tack remove cloudflare # its table, pin, links, checkout and plugin clones
tack doctor # audit the harnesses and the projects under projects.roots
tack doctor --json # for scripts; exit 1 when there is an error or warning
tack scaffold hooks ~/Code/app --dry-run # the diff a doctor fix would make
Manifest and plugins
Sources live in ~/.config/tack/tack.toml (or $TACK_CONFIG/tack.toml).
Each [[source]] selects skills and, optionally, plugins:
| Source field | Default | Meaning |
|---|---|---|
name |
required | A unique source name. |
path / git |
required | Exactly one: a local directory or a git clone URL. |
ref |
remote's default branch | The branch or tag a git source follows. |
subdir |
skills |
The skills directory, relative to the source root. |
skills |
"*" |
All skills, or a list of names and { name = "a", harnesses = ["codex"] } tables. [] selects none and needs no skills directory, only the source root. |
plugins |
[] |
Plugins from the source's catalog: "*", or a list shaped like skills. Only claude-code and codex take plugins. |
harnesses |
every harness | Limit the whole source; a plugin may further limit its harnesses. |
autocommit / autopush |
false |
Commit a path source's skill edits when tack changes things, and optionally push them. |
For a source that supplies only plugins, which tack add … --plugin skill-creator writes:
[[source]]
name = "claude-plugins-official"
git = "https://github.com/anthropics/claude-plugins-official.git"
skills = []
plugins = ["skill-creator"]
The source's catalog is .claude-plugin/marketplace.json, or
.agents/plugins/marketplace.json if the first is absent. A plugin whose
files are inside the source is copied from it. One whose catalog entry
names another git repository and a commit (url, github or
git-subdir) is cloned into $XDG_DATA_HOME/tack/plugins/<source>/<plugin>/
(by default under ~/.local/share) and checked out at that commit, so the
source's pin pins it too: sync reports its clone and checkout, and an
update that moves the commit checks the clone out again. A clone with
local changes, or something else in its place, is a source problem (exit
1), left alone, and its plugin stays as it was; a clone that has gone
missing is cloned again. A dry run clones nothing, so it lists no copy or
install for a plugin not yet cloned.
sync copies selected plugins into a local marketplace named tack, then
registers it and installs <name>@tack through claude plugin and codex plugin. claude or codex is needed on PATH only when a selected plugin
targets that agent; a missing CLI or a failed command is an agent problem
(exit 1), and skills and the other agent's plugins still deploy. Changed
files are copied again, with Codex reinstalled; Claude Code loads tack's
copy in place. Deselected plugins are uninstalled, including through
tack remove.
tack add SOURCE --plugin P… selects plugins from the source's catalog,
each name once in the order given; without --skill it writes skills = [],
so the source deploys only those plugins and needs no skills directory.
add refuses (exit 2, nothing written) a --plugin the catalog doesn't
have (or a source with no catalog, or a broken one, with its error), one
sync couldn't deploy (a source tack can't pin, or files that can't be
read), giving the reason sync would, and one another source already
selects. A plugin from another repository is checked by its catalog entry
alone, even with a clone already there; the sync that follows clones it
and reports a clone or copy that fails (exit 1, the table written).
Without --plugin, refusing a source with no skills lists the plugins
--plugin would accept. A dry run of a git source doesn't clone it, so it
checks neither its skills nor its plugins. plugins = "*" is written by
hand.
tack sync --dry-run lists the commands as would run and writes nothing;
--json includes the command lines and harnesses in its changes. tack never
enables or disables plugins, and a source sync can't reach keeps its
existing plugins. A sync with no plugin selections, marketplace directory,
or recorded installs runs no agent CLI. Beyond add, plugin selections are
edited by hand. The TUI's Plugins tab shows each selected plugin's state in
each agent, grouped and sorted as Skills is but on its own; a plugin's detail
gives its source and pin, tack's copy, its version and description, and any
problem sync reports about it.
tack status shows each selected plugin's state in each agent it targets,
read through claude plugin list and codex plugin list (and their
marketplace list), which it runs only for an agent a selected plugin
targets; it changes nothing. A plugin is in the first of these that applies:
collision: two sources select the name; tack deploys neither.unavailable: the agent's CLI isn't onPATH, or listing its plugins fails.conflict: the agent has a marketplace namedtackthat isn't tack's.missing: not installed.disabled: installed, but turned off in the agent; tack leaves it off.stale: tack's copy, or Codex's, predates the plugin's files;syncrefreshes it.installed: installed from tack's marketplace, enabled, and current.
Plugins are read from their source as it is now: for a source whose own
state isn't ok, sync first holds it or checks out its pin.
tack status --json adds plugins, one object per selected plugin with
name, source, harnesses (agent → state), version, path (its
copy in tack's marketplace, or null) and repository: for a plugin from
another repository, its url, path (a git-subdir's, or null),
commit, clone (where tack keeps its clone) and the clone's state
(in the way, not cloned, local changes, off its commit or ok);
null for any other plugin. It also gives each source plugins, the names
it selects.
tack outdated compares a git source's selected plugins between its pin and
the tip, from its catalog at each end: each one modified (its catalog
entry changed, or a file under its directory), added or removed, with
its version at each end (plugins modified: a (1.0 -> 1.1)); the commits
it lists include those touching selected plugins; and --diff adds each
changed plugin's diff after the skills', its files and then its catalog
entry. For a plugin from another repository whose entry moved to another
commit of the same repository, --diff follows the entry's diff with the
repository's diff between the two commits (headed a/<plugin>@<commit>/),
fetched into the plugin's clone; outdated never clones one, checks one
out or moves it, so a plugin without a clone gets a line saying so (tack sync clones it), as does a fetch that fails. A catalog broken at either
end leaves the plugins uncompared and says why. --json gives each source plugins (name, change, and version
as {"from", "to"}) and plugins_error (null unless the catalog is
broken), and each commit plugins. A source that selects no plugins is
reported as before. tack update then takes the change: tack's copy is
refreshed and the plugin reinstalled in Codex.
tack doctor audits plugins too. It lists every installed agent's plugins
(the same two list commands, for each agent whose CLI is on PATH),
whether or not the manifest selects any; --projects-only runs neither.
A finding about an agent's plugins names the agent:
not-synced(warn): a selected plugin that ismissing,disabledorstalein an agent, or that an agent can't take (unavailable,conflict); a listed plugin the source's catalog lacks, one tack can't deploy, a broken catalog, a plugin whose files can't be read, so thatsynccan't copy it (at its directory, with the reasonsyncgives), or one whose clone is in the way or has local changes (at the clone); one of tack's plugins thatsyncwould uninstall.name-collision(error): two sources select the same plugin name.duplicate-plugin(warn): a plugin tack deploys to an agent is also installed there from another marketplace, so the agent loads both.unmanaged-plugin(info): a plugin installed from another marketplace, by hand or by another manager; selecting it from its source keeps it the same across agents and machines.
An agent's problem is a finding wherever sync reports it: a CLI that isn't
on PATH when a selected plugin targets that agent, a failing list
whenever sync would run that agent at all (a selected plugin targets it,
tack has recorded installs there, or tack's marketplace exists), and a
foreign tack marketplace when a selected plugin targets the agent or tack
has recorded installs there. Its finding names the plugins it stops that
have no finding of their own, or says only what is wrong with the agent.
Built-in harnesses need no table; [harness.claude-code] or
[harness.codex] appears only to change a setting. Their
ignore_marketplaces lists marketplaces whose plugins belong to the agent or
to another program, which doctor doesn't report as unmanaged; it adds to
the built-in list (Claude Code's builtin, inline, skills-dir and
synced; Codex's openai-bundled, openai-curated-remote and
openai-primary-runtime). Only these two harnesses take it:
[harness.claude-code]
ignore_marketplaces = ["company-tools"]
Agent skill
skills/tack/SKILL.md teaches coding agents to run
tack for you through its --json CLI. Deploy it with tack itself:
tack add https://github.com/chocs-cat/tack.git # a source named tack, linked into every harness
tack update tack # later: take in a newer version
Or copy skills/tack into your agents' skills directories.
Development
uv sync
uv run tack … # the checkout, not an installed copy
uv run pytest && uv run ruff check . && uv run ruff format --check . && uv run ty check
CONTRIBUTING.md has the commit convention and how releases are cut.
License
MIT; see LICENSE.
Metadata
Release files for tack-agents 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tack_agents-0.4.0.tar.gz | 350.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tack_agents-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 468.8 kB
Release files / tack_agents-0.4.0.tar.gz
| Download URL | tack_agents-0.4.0.tar.gz |
|---|---|
| Size | 350.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a5007bdf76b0631c0802f5f6806219876041e226c700848b93aff5e61b551135
|
|
BLAKE2b-256 checksum How to use checksums |
a4d8207fc0b0c5d3ab47118653adc3ac84598c27458fefb3f318d2b7a1435adf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.
Transparency logRelease files / tack_agents-0.4.0-py3-none-any.whl
| Download URL | tack_agents-0.4.0-py3-none-any.whl |
|---|---|
| Size | 118.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fc89d5fc20c60e2455030e829e3de220ce27da79a6102f5d27bdc190859f13da
|
|
BLAKE2b-256 checksum How to use checksums |
01fe13f7acf2eb785d13493362991ecd31e280fbf7b862f3709003a5f229651b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.
Transparency log