repomatic.registry module¶
Declarative registry of all components managed by the init subcommand.
Every resource the init subcommand can create, sync, or merge is declared
here as a Component subclass instance in the COMPONENTS tuple.
Each component carries all its metadata: what kind it is, whether it is
selected by default, which files it manages, and any per-file properties like
repo-scope gating or config keys.
All derived constants (ALL_COMPONENTS, REUSABLE_WORKFLOWS,
SKILL_PHASES, etc.) are computed from this single registry at the bottom of
this module.
- repomatic.registry.GITHUB_YAML_PATTERNS: tuple[str, ...] = ('.github/workflows/*.yaml', '.github/workflows/*.yml', '.github/actions/**/*.yaml', '.github/actions/**/*.yml')¶
Globs matching every workflow and composite-action file of a repository.
Rooted at the repository root rather than at
.github/, so the same patterns work against the current directory and against an arbitrary target tree. Both.ymland.yamlare listed because GitHub accepts either, whatever this project’s own long-extension convention prefers: a downstream repository is free to have picked the short one.Shared by
sync_ops._workflow_and_action_files, which reads the pins to bump, andinit_project._highest_upstream_pin, which reads them to floor a new pin. The two must agree on which files carry a pin, orinitwould floor against a filesync-workflow-pinsnever bumps.
- class repomatic.registry.InitDefault(*values)[source]¶
Bases:
EnumHow
inittreats the component when no explicit CLI args are given.- INCLUDE = 1¶
Included by default (like changelog or workflows).
- EXCLUDE = 2¶
In default set but excluded unless explicitly included (e.g., labels, skills).
- AUTO = 3¶
Auto-included only for matching repos (e.g., awesome-template).
- EXPLICIT = 4¶
Only included when explicitly requested (e.g., tool configs).
- class repomatic.registry.SyncMode(*values)[source]¶
Bases:
EnumHow a
ToolConfigComponentbehaves when the section already exists.- BOOTSTRAP = 1¶
Insert once, skip if section already exists (e.g., ruff, pytest).
- ONGOING = 2¶
Replace template content on every sync, preserving local additions (e.g., bumpversion).
- class repomatic.registry.RepoScope(*values)[source]¶
Bases:
EnumWhich repository types a component or file entry applies to.
The classification has three axes: whether the repo is an
awesome-*list, whether it carries a PEP 621pyproject.toml, and whether that project is a distributable package. The first is mutually exclusive with the other two (awesome repos are content lists, not Python projects), so a single scope value suffices.The Python axis is deliberately split in two.
PYTHON_ONLYcovers anything that needs Python code to be useful;PACKAGE_ONLYcovers only what needs something to publish. A uv virtual project ([tool.uv] package = false) sits between the two: it locks dependencies and runs tests, but never ships a release. Collapsing the pair would hand every blog and docs site a PyPI publish action and a release workflow it can never run.Scope restrictions are defaults: they apply during bare
repomatic initbut are bypassed when components are explicitly named on the CLI or covered by[tool.repomatic] include.- ALL = 1¶
Included in every repository type.
- AWESOME_ONLY = 2¶
Only for
awesome-*repositories.
- PYTHON_ONLY = 3¶
Only for Python projects (PEP 621
[project].namepresent).Use for anything a uv virtual project still wants: dependency locking, coverage config, test tooling.
- PACKAGE_ONLY = 4¶
Only for Python projects that build a distributable package.
Strictly narrower than
PYTHON_ONLY, excluding uv virtual projects. Use for the release lane: publishing, tagging, changelog upkeep.
- matches(is_awesome, is_python, is_package)[source]¶
Whether this scope applies to the given repository traits.
- Parameters:
is_awesome (
bool) –Trueforawesome-*repositories.is_python (
bool) –Truefor repositories whosepyproject.tomldeclares a PEP 621[project].name, perrepomatic.pyproject.is_python_project().is_package (
bool) –Truewhen that project is also distributable, perrepomatic.pyproject.is_python_package(). Always implies is_python.
- Return type:
- class repomatic.registry.FileEntry(source, target='', file_id='', scope=RepoScope.ALL, config_key='', config_default=False, reusable=True, phase='', tree=False)[source]¶
Bases:
objectA single file managed within a component.
- target: str = ''¶
Relative output path in the target repository. Defaults to
source(root-level file).
- file_id: str = ''¶
Identifier for file-level
--include/--exclude. Defaults to the filename portion oftarget.
- config_default: bool = False¶
Value assumed when
config_keyis absent from config.Falsemeans opt-in (excluded unless enabled),Truemeans opt-out (included unless disabled).
- tree: bool = False¶
Whether
sourceandtargetname directories, not files.A tree entry is copied wholesale, so a skill can ship
scripts/,references/andassets/alongside itsSKILL.mdexactly as the Agent Skills spec describes, with no per-file registration.Caution
Under
repomatic/data/a tree’s directories must be real and only its leaves may be symlinks back into the authoritative tree.uv_buildrefuses a symlinked directory in package data (Is a directory (os error 21)) and fails the whole wheel, while symlinked files are dereferenced into it normally.
- class repomatic.registry.Component(name, description, init_default=InitDefault.INCLUDE, scope=RepoScope.ALL, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='')[source]¶
Bases:
objectBase class for all init components.
- init_default: InitDefault = 1¶
How
inittreats this component when no explicit CLI selection is made.
- scope: RepoScope = 1¶
Which repository types get this component. Checked at the component level during auto-exclusion, complementing the file-level
FileEntry.scope.
- config_default: bool = True¶
Value assumed when
config_keyis absent from config.Truemeans opt-out (included unless disabled).
- keep_unmodified: bool = False¶
Preserve files on disk even when identical to the bundled default. When
False, unmodified copies are flagged for cleanup by--delete-unmodified.
- ephemeral: bool = False¶
Whether this component’s files are inputs regenerated on demand rather than repository content.
Every consumer of an ephemeral component dumps it right before reading it, so a copy in the working tree is never the one that gets used. Bare
repomatic inittherefore skips these components, and [tool.repomatic] include cannot opt into materializing them: only naming the component explicitly on the CLI (repomatic init labels) writes its files out, which is howsync-labelsstageslabels.tomlinto a temporary directory to hand tolabelmaker, leaving the working tree untouched.
- location_field: str = ''¶
Configfield holding this component’s destination, when the user can move it.Set for every component whose destination is configurable: the directories
subagentsandskillswrite into, and the single filepluginmerges into. Declared targets are built against the default location, so a repo that overrode it needs each target rebased onto the configured one.resolve_target()performs that rebase, and leaving this empty means the targets are fixed (.github/workflows/is GitHub’s, not ours to move).
- is_enabled(config)[source]¶
Whether this component is enabled by the given
Configobject.See
_config_enabled()for the resolution rule.
- resolve_target(target, config)[source]¶
Rebase a declared target path onto this component’s configured location.
A no-op unless
location_fieldis set and the resolved config actually moves the destination, so every caller can route every target through this method instead of testing the component name first.Handles both shapes a location may take. A directory location rebases the path under it; a file location (
plugin) is the path, so it is replaced outright. Matching only the directory shape would leave a moved file reported at its default path, and stale-file detection would then hunt for an orphan the repository never wrote there.- Parameters:
target (
str) – A path as declared on aFileEntry(or aRemovedAssettombstone), relative to the repository root and expressed against the default location.
- Return type:
- Returns:
The target rebased onto the configured location, or target unchanged.
- class repomatic.registry.BundledComponent(name, description, init_default=InitDefault.INCLUDE, scope=RepoScope.ALL, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='')[source]¶
Bases:
ComponentFiles copied from
repomatic/data/to a target path.
- class repomatic.registry.WorkflowComponent(name, description, init_default=InitDefault.INCLUDE, scope=RepoScope.ALL, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='')[source]¶
Bases:
ComponentThin-caller generation and header sync.
- class repomatic.registry.ToolConfigComponent(name, description, init_default=InitDefault.INCLUDE, scope=RepoScope.ALL, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', source_file='', tool_section='', sync_mode=SyncMode.BOOTSTRAP, preserved_keys=(), graft_identity_keys=(), overlay=False)[source]¶
Bases:
ComponentMerged into
pyproject.toml.Note
Nothing here declares where the section lands.
initappends it to the[tool]table, thenformat-pyprojectmoves it:pyproject-fmtsorts[tool.*]by its own known-tool order, which no per-component hint can override. This class used to carryinsert_after/insert_beforetuples for the purpose; they were read by no code and are gone.- sync_mode: SyncMode = 1¶
How this config behaves when the section already exists.
BOOTSTRAP: insert once, skip if the section is present.ONGOING: re-derive the section from the template on every sync while preserving local additions: keys the template omits, extra items in shared arrays, and extra keys in shared nested tables. The template wins on shared scalars;preserved_keysflips that for named top-level keys.
- preserved_keys: tuple[str, ...] = ()¶
Top-level keys whose existing values survive an ongoing sync.
Only meaningful when
sync_modeisONGOING. During replacement, these keys keep their value from the existing config rather than being overwritten by the template placeholder.
- graft_identity_keys: tuple[str, ...] = ()¶
Keys that identify the “slot” of an array-of-tables entry during a graft.
Only meaningful when
sync_modeisONGOING. When set, a local array-of-tables entry that shares its identity tuple (the values of these keys) with a template entry is treated as a stale copy of that canonical entry: the template wins and the local entry is dropped rather than appended as a duplicate. Local entries whose identity matches no template entry are genuinely local and survive. Leave empty to fall back to a plain union-by-value, which cannot tell an evolved canonical entry apart from a new local one.For
bumpversion, the slot is(filename | glob | key_path, replace):filename/glob/key_pathname the target file andreplacenames what the entry writes there, so a stale entry whosesearchpattern evolved (e.g. gaining a regex anchor) still maps to the same slot.
- overlay: bool = False¶
Treat the template as a partial section owning only its own keys.
Only meaningful when
sync_modeisONGOING. The default rebuild-and-graft sync rebuilds the whole section from the template and grafts local additions after it, so template keys always land first. That is wrong for a section the project mostly owns and a formatter reorders:[tool.uv], whose keyspyproject-fmtsorts into a fixed schema order. Emitting the owned keys as a leading block would lose topyproject-fmton the next format pass and churn an endless sync PR.With
overlayset, an ongoing sync instead updates only the template’s top-level keys in place within the existing section (the template value wins), preserving the existing key order and leaving every other key untouched. The merged section is therefore already apyproject-fmtfixpoint. A repo missing an owned key has it appended;pyproject-fmtcanonicalizes that one position once, after which steady-state syncs are no-ops.
- class repomatic.registry.TemplateComponent(name, description, init_default=InitDefault.INCLUDE, scope=RepoScope.ALL, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='')[source]¶
Bases:
ComponentDirectory tree (awesome-template).
- class repomatic.registry.GeneratedComponent(name, description, init_default=InitDefault.INCLUDE, scope=RepoScope.ALL, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', target='')[source]¶
Bases:
ComponentProduced from code (changelog).
Unlike bundled components, generated components have no
filestuple. Thetargetfield records the output path so the auto-exclusion logic can detect stale copies on disk.
- class repomatic.registry.RemovedAsset(component, target, removed_in, hashes=(), owned_dir='', successor='')[source]¶
Bases:
objectAn asset repomatic once shipped and has since dropped.
Note
Stale-file detection in
initonly inspects files still listed inCOMPONENTS. An asset removed from the registry (a renamed or consolidated skill, a retired workflow) becomes invisible to it, so downstream repos accumulate one orphan per upstream removal. EachRemovedAssetis a tombstone that letsinitfind and prune those orphans.initfinds an on-disk orphan and decides whether to prune it with one of two gates, depending on the component:Content-gated (skills, agents, config files): the file is deleted only when its normalized content matches one of
hashes(a version repomatic shipped), proving it is an untouched copy.Fingerprint-gated (workflows): thin-callers are parameterized per repo (version pin,
paths:filters), so they carry no fixed content. The file is deleted only when it is a repomatic-lineage thin-caller for this workflow (itsuses:line references an upstream slug, seeUPSTREAM_REPO_SLUGS) with no extra downstream jobs.
Either way, a locally modified orphan is reported for manual review, never deleted. When
targetis already gone but the asset shipped as a folder, an emptyowned_dirleft behind is pruned on its own: it carries nothing anyone could lose.- target: str¶
Relative output path the asset occupied, in default-location form (like
.claude/skills/repomatic-release/SKILL.mdor.github/workflows/label-sponsors.yaml).Build skill and subagent targets with
_skill_target/_subagent_targetso they match the live registry: theskills.locationandsubagents.locationoverrides are re-applied at detection time. Workflow targets are literal (.github/workflows/is fixed by GitHub).
- removed_in: str¶
Bare package version that first stopped shipping the asset (like
6.21.0). Surfaced in the prune report.
- hashes: tuple[str, ...] = ()¶
Content gate for skills and agents: the hex SHA-256 of every distinct normalized content repomatic shipped for this asset (content.rstrip() + “n”`, exactly as``init` writes it to disk). An on-disk file whose content hashes to any of these is an untouched copy of some released version and is safe to delete. Listing one hash per distinct released revision (not just the last) means a downstream repo that synced an older version is still recognized and pruned rather than flagged for review.
Empty for workflows, which are fingerprint-gated by their
uses:line instead (see the class docstring).
- owned_dir: str = ''¶
Directory the asset had to itself, in default-location form (like
.claude/skills/repomatic-release), for an asset shipped as a folder.A skill is a folder, so deleting its
SKILL.mdby any route other thaninit(a handrm, a repomatic old enough to unlink the file alone) leaves the folder behind, empty.targetno longer exists, so the tombstone never fires again and the fossil outlives every laterinit. Declaring the folder gives detection a second thing to look for. Empty for an asset that shipped as a lone file in a shared directory (a subagent, a workflow), whose parent must never be swept.
- repomatic.registry.WORKFLOW_TARGET_ROOT = '.github/workflows'¶
Directory GitHub reads workflow files from. Not configurable.
- repomatic.registry.INSTALL_GUIDE_PATH = 'docs/install.md'¶
Install guide the release freeze pins download URLs in.
Shared by
PrepareRelease, which rewrites those URLs, andcheck_install_guide_downloads(), which verifies the release they name actually carries the files.
- repomatic.registry.SKILL_FILENAME = 'SKILL.md'¶
Name the Agent Skills spec reserves for a skill’s entry point.
- repomatic.registry.SKILL_SOURCE_ROOT = 'skills'¶
Directory under
repomatic/data/holding one folder per bundled skill.
- repomatic.registry.COMPONENTS: tuple[Component, ...] = (BundledComponent(name='labels', description='Label definitions for labelmaker (labels.toml)', init_default=<InitDefault.EXCLUDE: 2>, scope=<RepoScope.ALL: 1>, files=(FileEntry(source='labels.toml', target='labels.toml', file_id='labels.toml', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False),), config_key='', config_default=True, keep_unmodified=False, ephemeral=True, location_field=''), BundledComponent(name='publish-pypi-action', description='Composite action that publishes to PyPI via Trusted Publishing (.github/actions/publish-pypi/)', init_default=<InitDefault.INCLUDE: 1>, scope=<RepoScope.PACKAGE_ONLY: 4>, files=(FileEntry(source='action-publish-pypi.yaml', target='.github/actions/publish-pypi/action.yaml', file_id='action.yaml', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False),), config_key='', config_default=True, keep_unmodified=True, ephemeral=False, location_field=''), BundledComponent(name='subagents', description='Agent subagent definitions (.claude/agents/)', init_default=<InitDefault.EXCLUDE: 2>, scope=<RepoScope.ALL: 1>, files=(FileEntry(source='agent-grunt-qa.md', target='.claude/agents/grunt-qa.md', file_id='grunt-qa', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='agent-qa-engineer.md', target='.claude/agents/qa-engineer.md', file_id='qa-engineer', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='agent-sphinx-docs.md', target='.claude/agents/sphinx-docs.md', file_id='sphinx-docs', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False)), config_key='', config_default=True, keep_unmodified=True, ephemeral=False, location_field='subagents_location'), BundledComponent(name='skills', description='Claude Code skill definitions (.claude/skills/)', init_default=<InitDefault.EXCLUDE: 2>, scope=<RepoScope.ALL: 1>, files=(FileEntry(source='skills/av-false-positive', target='.claude/skills/av-false-positive', file_id='av-false-positive', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Release', tree=True), FileEntry(source='skills/awesome-triage', target='.claude/skills/awesome-triage', file_id='awesome-triage', scope=<RepoScope.AWESOME_ONLY: 2>, config_key='', config_default=False, reusable=True, phase='Maintenance', tree=True), FileEntry(source='skills/babysit-ci', target='.claude/skills/babysit-ci', file_id='babysit-ci', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Quality', tree=True), FileEntry(source='skills/benchmark-update', target='.claude/skills/benchmark-update', file_id='benchmark-update', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Development', tree=True), FileEntry(source='skills/brand-assets', target='.claude/skills/brand-assets', file_id='brand-assets', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Development', tree=True), FileEntry(source='skills/file-bug-report', target='.claude/skills/file-bug-report', file_id='file-bug-report', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Maintenance', tree=True), FileEntry(source='skills/github-housekeeping', target='.claude/skills/github-housekeeping', file_id='github-housekeeping', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Maintenance', tree=True), FileEntry(source='skills/repomatic-audit', target='.claude/skills/repomatic-audit', file_id='repomatic-audit', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Maintenance', tree=True), FileEntry(source='skills/repomatic-changelog', target='.claude/skills/repomatic-changelog', file_id='repomatic-changelog', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Release', tree=True), FileEntry(source='skills/repomatic-deps', target='.claude/skills/repomatic-deps', file_id='repomatic-deps', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Development', tree=True), FileEntry(source='skills/repomatic-init', target='.claude/skills/repomatic-init', file_id='repomatic-init', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Setup', tree=True), FileEntry(source='skills/repomatic-ship', target='.claude/skills/repomatic-ship', file_id='repomatic-ship', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Release', tree=True), FileEntry(source='skills/repomatic-test-matrix', target='.claude/skills/repomatic-test-matrix', file_id='repomatic-test-matrix', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Quality', tree=True), FileEntry(source='skills/repomatic-topics', target='.claude/skills/repomatic-topics', file_id='repomatic-topics', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Development', tree=True), FileEntry(source='skills/sphinx-docs-sync', target='.claude/skills/sphinx-docs-sync', file_id='sphinx-docs-sync', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Maintenance', tree=True), FileEntry(source='skills/translation-sync', target='.claude/skills/translation-sync', file_id='translation-sync', scope=<RepoScope.AWESOME_ONLY: 2>, config_key='', config_default=False, reusable=True, phase='Maintenance', tree=True), FileEntry(source='skills/upstream-audit', target='.claude/skills/upstream-audit', file_id='upstream-audit', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Maintenance', tree=True)), config_key='', config_default=True, keep_unmodified=True, ephemeral=False, location_field='skills_location'), WorkflowComponent(name='workflows', description='Thin-caller workflow files', init_default=<InitDefault.INCLUDE: 1>, scope=<RepoScope.ALL: 1>, files=(FileEntry(source='autofix.yaml', target='.github/workflows/autofix.yaml', file_id='autofix.yaml', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='autolock.yaml', target='.github/workflows/autolock.yaml', file_id='autolock.yaml', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='cancel-runs.yaml', target='.github/workflows/cancel-runs.yaml', file_id='cancel-runs.yaml', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='changelog.yaml', target='.github/workflows/changelog.yaml', file_id='changelog.yaml', scope=<RepoScope.PACKAGE_ONLY: 4>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='debug.yaml', target='.github/workflows/debug.yaml', file_id='debug.yaml', scope=<RepoScope.PYTHON_ONLY: 3>, config_key='debug.sync', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='docs.yaml', target='.github/workflows/docs.yaml', file_id='docs.yaml', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='labels.yaml', target='.github/workflows/labels.yaml', file_id='labels.yaml', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='lint.yaml', target='.github/workflows/lint.yaml', file_id='lint.yaml', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='metrics.yaml', target='.github/workflows/metrics.yaml', file_id='metrics.yaml', scope=<RepoScope.ALL: 1>, config_key='metrics.sync', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='_release-engine.yaml', target='.github/workflows/release.yaml', file_id='release.yaml', scope=<RepoScope.PACKAGE_ONLY: 4>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='tests.yaml', target='.github/workflows/tests.yaml', file_id='tests.yaml', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=False, phase='', tree=False), FileEntry(source='unsubscribe.yaml', target='.github/workflows/unsubscribe.yaml', file_id='unsubscribe.yaml', scope=<RepoScope.ALL: 1>, config_key='notification.unsubscribe', config_default=False, reusable=True, phase='', tree=False)), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field=''), TemplateComponent(name='awesome-template', description='Boilerplate for awesome-* repositories', init_default=<InitDefault.AUTO: 3>, scope=<RepoScope.ALL: 1>, files=(), config_key='awesome-template.sync', config_default=True, keep_unmodified=False, ephemeral=False, location_field=''), GeneratedComponent(name='changelog', description='Minimal changelog.md', init_default=<InitDefault.INCLUDE: 1>, scope=<RepoScope.PACKAGE_ONLY: 4>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', target='changelog.md'), GeneratedComponent(name='plugin', description='Claude Code plugin marketplace wiring (.claude/settings.json)', init_default=<InitDefault.EXCLUDE: 2>, scope=<RepoScope.ALL: 1>, files=(), config_key='', config_default=True, keep_unmodified=True, ephemeral=False, location_field='settings_location', target='.claude/settings.json'), ToolConfigComponent(name='uv', description='uv resolver pin and dependency cooldown policy', init_default=<InitDefault.EXPLICIT: 4>, scope=<RepoScope.ALL: 1>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', source_file='uv.toml', tool_section='tool.uv', sync_mode=<SyncMode.ONGOING: 2>, preserved_keys=(), graft_identity_keys=(), overlay=True), ToolConfigComponent(name='lychee', description='Lychee link checker configuration', init_default=<InitDefault.INCLUDE: 1>, scope=<RepoScope.AWESOME_ONLY: 2>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', source_file='lychee.toml', tool_section='tool.lychee', sync_mode=<SyncMode.ONGOING: 2>, preserved_keys=(), graft_identity_keys=(), overlay=False), ToolConfigComponent(name='ruff', description='Ruff linter/formatter configuration', init_default=<InitDefault.EXPLICIT: 4>, scope=<RepoScope.ALL: 1>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', source_file='ruff.toml', tool_section='tool.ruff', sync_mode=<SyncMode.BOOTSTRAP: 1>, preserved_keys=(), graft_identity_keys=(), overlay=False), ToolConfigComponent(name='pytest', description='Pytest test configuration', init_default=<InitDefault.EXPLICIT: 4>, scope=<RepoScope.ALL: 1>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', source_file='pytest.toml', tool_section='tool.pytest', sync_mode=<SyncMode.BOOTSTRAP: 1>, preserved_keys=(), graft_identity_keys=(), overlay=False), ToolConfigComponent(name='coverage', description='Coverage.py measurement and reporting configuration', init_default=<InitDefault.EXPLICIT: 4>, scope=<RepoScope.ALL: 1>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', source_file='coverage.toml', tool_section='tool.coverage', sync_mode=<SyncMode.BOOTSTRAP: 1>, preserved_keys=(), graft_identity_keys=(), overlay=False), ToolConfigComponent(name='mypy', description='Mypy type checking configuration', init_default=<InitDefault.EXPLICIT: 4>, scope=<RepoScope.ALL: 1>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', source_file='mypy.toml', tool_section='tool.mypy', sync_mode=<SyncMode.BOOTSTRAP: 1>, preserved_keys=(), graft_identity_keys=(), overlay=False), ToolConfigComponent(name='mdformat', description='mdformat Markdown formatter configuration', init_default=<InitDefault.EXPLICIT: 4>, scope=<RepoScope.ALL: 1>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', source_file='mdformat.toml', tool_section='tool.mdformat', sync_mode=<SyncMode.BOOTSTRAP: 1>, preserved_keys=(), graft_identity_keys=(), overlay=False), ToolConfigComponent(name='bumpversion', description='bump-my-version configuration', init_default=<InitDefault.EXPLICIT: 4>, scope=<RepoScope.ALL: 1>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', source_file='bumpversion.toml', tool_section='tool.bumpversion', sync_mode=<SyncMode.ONGOING: 2>, preserved_keys=('current_version',), graft_identity_keys=('filename', 'glob', 'key_path', 'replace'), overlay=False), ToolConfigComponent(name='typos', description='Typos spell checker configuration', init_default=<InitDefault.EXPLICIT: 4>, scope=<RepoScope.ALL: 1>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', source_file='typos.toml', tool_section='tool.typos', sync_mode=<SyncMode.ONGOING: 2>, preserved_keys=(), graft_identity_keys=(), overlay=False))¶
The component registry.
Single source of truth for all resources managed by the
initsubcommand. Every component declares its kind, selection default, file entries, and behavioral flags. All derived constants are computed from this tuple.
- repomatic.registry.COMPONENTS_BY_NAME: dict[str, Component] = {'awesome-template': TemplateComponent(name='awesome-template', description='Boilerplate for awesome-* repositories', init_default=<InitDefault.AUTO: 3>, scope=<RepoScope.ALL: 1>, files=(), config_key='awesome-template.sync', config_default=True, keep_unmodified=False, ephemeral=False, location_field=''), 'bumpversion': ToolConfigComponent(name='bumpversion', description='bump-my-version configuration', init_default=<InitDefault.EXPLICIT: 4>, scope=<RepoScope.ALL: 1>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', source_file='bumpversion.toml', tool_section='tool.bumpversion', sync_mode=<SyncMode.ONGOING: 2>, preserved_keys=('current_version',), graft_identity_keys=('filename', 'glob', 'key_path', 'replace'), overlay=False), 'changelog': GeneratedComponent(name='changelog', description='Minimal changelog.md', init_default=<InitDefault.INCLUDE: 1>, scope=<RepoScope.PACKAGE_ONLY: 4>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', target='changelog.md'), 'coverage': ToolConfigComponent(name='coverage', description='Coverage.py measurement and reporting configuration', init_default=<InitDefault.EXPLICIT: 4>, scope=<RepoScope.ALL: 1>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', source_file='coverage.toml', tool_section='tool.coverage', sync_mode=<SyncMode.BOOTSTRAP: 1>, preserved_keys=(), graft_identity_keys=(), overlay=False), 'labels': BundledComponent(name='labels', description='Label definitions for labelmaker (labels.toml)', init_default=<InitDefault.EXCLUDE: 2>, scope=<RepoScope.ALL: 1>, files=(FileEntry(source='labels.toml', target='labels.toml', file_id='labels.toml', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False),), config_key='', config_default=True, keep_unmodified=False, ephemeral=True, location_field=''), 'lychee': ToolConfigComponent(name='lychee', description='Lychee link checker configuration', init_default=<InitDefault.INCLUDE: 1>, scope=<RepoScope.AWESOME_ONLY: 2>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', source_file='lychee.toml', tool_section='tool.lychee', sync_mode=<SyncMode.ONGOING: 2>, preserved_keys=(), graft_identity_keys=(), overlay=False), 'mdformat': ToolConfigComponent(name='mdformat', description='mdformat Markdown formatter configuration', init_default=<InitDefault.EXPLICIT: 4>, scope=<RepoScope.ALL: 1>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', source_file='mdformat.toml', tool_section='tool.mdformat', sync_mode=<SyncMode.BOOTSTRAP: 1>, preserved_keys=(), graft_identity_keys=(), overlay=False), 'mypy': ToolConfigComponent(name='mypy', description='Mypy type checking configuration', init_default=<InitDefault.EXPLICIT: 4>, scope=<RepoScope.ALL: 1>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', source_file='mypy.toml', tool_section='tool.mypy', sync_mode=<SyncMode.BOOTSTRAP: 1>, preserved_keys=(), graft_identity_keys=(), overlay=False), 'plugin': GeneratedComponent(name='plugin', description='Claude Code plugin marketplace wiring (.claude/settings.json)', init_default=<InitDefault.EXCLUDE: 2>, scope=<RepoScope.ALL: 1>, files=(), config_key='', config_default=True, keep_unmodified=True, ephemeral=False, location_field='settings_location', target='.claude/settings.json'), 'publish-pypi-action': BundledComponent(name='publish-pypi-action', description='Composite action that publishes to PyPI via Trusted Publishing (.github/actions/publish-pypi/)', init_default=<InitDefault.INCLUDE: 1>, scope=<RepoScope.PACKAGE_ONLY: 4>, files=(FileEntry(source='action-publish-pypi.yaml', target='.github/actions/publish-pypi/action.yaml', file_id='action.yaml', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False),), config_key='', config_default=True, keep_unmodified=True, ephemeral=False, location_field=''), 'pytest': ToolConfigComponent(name='pytest', description='Pytest test configuration', init_default=<InitDefault.EXPLICIT: 4>, scope=<RepoScope.ALL: 1>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', source_file='pytest.toml', tool_section='tool.pytest', sync_mode=<SyncMode.BOOTSTRAP: 1>, preserved_keys=(), graft_identity_keys=(), overlay=False), 'ruff': ToolConfigComponent(name='ruff', description='Ruff linter/formatter configuration', init_default=<InitDefault.EXPLICIT: 4>, scope=<RepoScope.ALL: 1>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', source_file='ruff.toml', tool_section='tool.ruff', sync_mode=<SyncMode.BOOTSTRAP: 1>, preserved_keys=(), graft_identity_keys=(), overlay=False), 'skills': BundledComponent(name='skills', description='Claude Code skill definitions (.claude/skills/)', init_default=<InitDefault.EXCLUDE: 2>, scope=<RepoScope.ALL: 1>, files=(FileEntry(source='skills/av-false-positive', target='.claude/skills/av-false-positive', file_id='av-false-positive', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Release', tree=True), FileEntry(source='skills/awesome-triage', target='.claude/skills/awesome-triage', file_id='awesome-triage', scope=<RepoScope.AWESOME_ONLY: 2>, config_key='', config_default=False, reusable=True, phase='Maintenance', tree=True), FileEntry(source='skills/babysit-ci', target='.claude/skills/babysit-ci', file_id='babysit-ci', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Quality', tree=True), FileEntry(source='skills/benchmark-update', target='.claude/skills/benchmark-update', file_id='benchmark-update', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Development', tree=True), FileEntry(source='skills/brand-assets', target='.claude/skills/brand-assets', file_id='brand-assets', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Development', tree=True), FileEntry(source='skills/file-bug-report', target='.claude/skills/file-bug-report', file_id='file-bug-report', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Maintenance', tree=True), FileEntry(source='skills/github-housekeeping', target='.claude/skills/github-housekeeping', file_id='github-housekeeping', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Maintenance', tree=True), FileEntry(source='skills/repomatic-audit', target='.claude/skills/repomatic-audit', file_id='repomatic-audit', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Maintenance', tree=True), FileEntry(source='skills/repomatic-changelog', target='.claude/skills/repomatic-changelog', file_id='repomatic-changelog', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Release', tree=True), FileEntry(source='skills/repomatic-deps', target='.claude/skills/repomatic-deps', file_id='repomatic-deps', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Development', tree=True), FileEntry(source='skills/repomatic-init', target='.claude/skills/repomatic-init', file_id='repomatic-init', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Setup', tree=True), FileEntry(source='skills/repomatic-ship', target='.claude/skills/repomatic-ship', file_id='repomatic-ship', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Release', tree=True), FileEntry(source='skills/repomatic-test-matrix', target='.claude/skills/repomatic-test-matrix', file_id='repomatic-test-matrix', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Quality', tree=True), FileEntry(source='skills/repomatic-topics', target='.claude/skills/repomatic-topics', file_id='repomatic-topics', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Development', tree=True), FileEntry(source='skills/sphinx-docs-sync', target='.claude/skills/sphinx-docs-sync', file_id='sphinx-docs-sync', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Maintenance', tree=True), FileEntry(source='skills/translation-sync', target='.claude/skills/translation-sync', file_id='translation-sync', scope=<RepoScope.AWESOME_ONLY: 2>, config_key='', config_default=False, reusable=True, phase='Maintenance', tree=True), FileEntry(source='skills/upstream-audit', target='.claude/skills/upstream-audit', file_id='upstream-audit', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='Maintenance', tree=True)), config_key='', config_default=True, keep_unmodified=True, ephemeral=False, location_field='skills_location'), 'subagents': BundledComponent(name='subagents', description='Agent subagent definitions (.claude/agents/)', init_default=<InitDefault.EXCLUDE: 2>, scope=<RepoScope.ALL: 1>, files=(FileEntry(source='agent-grunt-qa.md', target='.claude/agents/grunt-qa.md', file_id='grunt-qa', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='agent-qa-engineer.md', target='.claude/agents/qa-engineer.md', file_id='qa-engineer', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='agent-sphinx-docs.md', target='.claude/agents/sphinx-docs.md', file_id='sphinx-docs', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False)), config_key='', config_default=True, keep_unmodified=True, ephemeral=False, location_field='subagents_location'), 'typos': ToolConfigComponent(name='typos', description='Typos spell checker configuration', init_default=<InitDefault.EXPLICIT: 4>, scope=<RepoScope.ALL: 1>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', source_file='typos.toml', tool_section='tool.typos', sync_mode=<SyncMode.ONGOING: 2>, preserved_keys=(), graft_identity_keys=(), overlay=False), 'uv': ToolConfigComponent(name='uv', description='uv resolver pin and dependency cooldown policy', init_default=<InitDefault.EXPLICIT: 4>, scope=<RepoScope.ALL: 1>, files=(), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='', source_file='uv.toml', tool_section='tool.uv', sync_mode=<SyncMode.ONGOING: 2>, preserved_keys=(), graft_identity_keys=(), overlay=True), 'workflows': WorkflowComponent(name='workflows', description='Thin-caller workflow files', init_default=<InitDefault.INCLUDE: 1>, scope=<RepoScope.ALL: 1>, files=(FileEntry(source='autofix.yaml', target='.github/workflows/autofix.yaml', file_id='autofix.yaml', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='autolock.yaml', target='.github/workflows/autolock.yaml', file_id='autolock.yaml', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='cancel-runs.yaml', target='.github/workflows/cancel-runs.yaml', file_id='cancel-runs.yaml', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='changelog.yaml', target='.github/workflows/changelog.yaml', file_id='changelog.yaml', scope=<RepoScope.PACKAGE_ONLY: 4>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='debug.yaml', target='.github/workflows/debug.yaml', file_id='debug.yaml', scope=<RepoScope.PYTHON_ONLY: 3>, config_key='debug.sync', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='docs.yaml', target='.github/workflows/docs.yaml', file_id='docs.yaml', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='labels.yaml', target='.github/workflows/labels.yaml', file_id='labels.yaml', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='lint.yaml', target='.github/workflows/lint.yaml', file_id='lint.yaml', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='metrics.yaml', target='.github/workflows/metrics.yaml', file_id='metrics.yaml', scope=<RepoScope.ALL: 1>, config_key='metrics.sync', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='_release-engine.yaml', target='.github/workflows/release.yaml', file_id='release.yaml', scope=<RepoScope.PACKAGE_ONLY: 4>, config_key='', config_default=False, reusable=True, phase='', tree=False), FileEntry(source='tests.yaml', target='.github/workflows/tests.yaml', file_id='tests.yaml', scope=<RepoScope.ALL: 1>, config_key='', config_default=False, reusable=False, phase='', tree=False), FileEntry(source='unsubscribe.yaml', target='.github/workflows/unsubscribe.yaml', file_id='unsubscribe.yaml', scope=<RepoScope.ALL: 1>, config_key='notification.unsubscribe', config_default=False, reusable=True, phase='', tree=False)), config_key='', config_default=True, keep_unmodified=False, ephemeral=False, location_field='')}¶
Index for O(1) component lookup by name.
- repomatic.registry.REMOVED_ASSETS: tuple[RemovedAsset, ...] = (RemovedAsset(component='codecov', target='.github/codecov.yaml', removed_in='7.8.0.dev0', hashes=('e8e96bfead62334599f4ec4c0448f2376352629789a70a76ae6fc3746ff7057b',), owned_dir='', successor='coverage is now gated by pytest --cov-fail-under'), RemovedAsset(component='labels', target='.github/labeller-content-based.yaml', removed_in='7.11.0.dev0', hashes=('1f3e670c0b4c6687a8920fb3738a15fb82b8639b7825d81f76c55bc5784cdb08', 'adf62c78c539229d34d4d2518a9af7f39df44d599c60852784b0faa47a6defa9', '5cf481b4aec2bf98a4056757f41ef5fc50f808dbd7c8a43f1dea0b224ecb7f1f', '8a047d53d5449ea0b53517e2f63e126360050127342084b7a705f34fb735d818'), owned_dir='', successor='rules now live in repomatic.labels.DEFAULT_CONTENT_RULES'), RemovedAsset(component='labels', target='.github/labeller-file-based.yaml', removed_in='7.11.0.dev0', hashes=('9dc0948e23a3a83d2cec5f11e400c75992fb1ce326eb6c5811c1fc3bfe258b31', 'b216d370e4d2c6118f46d9bb2eacaf91392e6a6267a4f4857f44a698422cc860', '9a4feeb49c37ee7eba1d13957d26aaaa867c791ec12be8cd4197e7526bfbf963'), owned_dir='', successor='rules now live in repomatic.labels.DEFAULT_FILE_RULES'), RemovedAsset(component='skills', target='.claude/skills/gha-changelog/SKILL.md', removed_in='6.0.0', hashes=('2c178a58e1106f08aa6e540cd022eff12c4e954942ec5d794282c7b640adf768',), owned_dir='.claude/skills/gha-changelog', successor='renamed to repomatic-changelog'), RemovedAsset(component='skills', target='.claude/skills/gha-deps/SKILL.md', removed_in='6.0.0', hashes=('d0bcb44f81335f4aabcadb82085f5048be12db252fc0a1f8c6bda8d9e5292efd',), owned_dir='.claude/skills/gha-deps', successor='renamed to repomatic-deps'), RemovedAsset(component='skills', target='.claude/skills/gha-init/SKILL.md', removed_in='6.0.0', hashes=('0f4f23f424c73774dd6253d9cb547e7a1d52ed64266c93b5b7271f4bee492a25',), owned_dir='.claude/skills/gha-init', successor='renamed to repomatic-init'), RemovedAsset(component='skills', target='.claude/skills/gha-lint/SKILL.md', removed_in='6.0.0', hashes=('7079f4d79c6347b03b4788de97db2e1839006b606e9dbacbfeb51e9cca04db20',), owned_dir='.claude/skills/gha-lint', successor='now handled by lint.yaml on every push'), RemovedAsset(component='skills', target='.claude/skills/gha-metadata/SKILL.md', removed_in='6.0.0', hashes=('74c6f7d3574236d20aa7011b92f174abd2f8fdda162131e7f61851dfee7145fa',), owned_dir='.claude/skills/gha-metadata', successor='now handled by the repomatic metadata CLI command'), RemovedAsset(component='skills', target='.claude/skills/gha-release/SKILL.md', removed_in='6.0.0', hashes=('99a466bc4d377bb056c5696de8f0eae2b025b34505ac951d504bee55a42bdd1c',), owned_dir='.claude/skills/gha-release', successor='replaced by repomatic-ship'), RemovedAsset(component='skills', target='.claude/skills/gha-sync/SKILL.md', removed_in='6.0.0', hashes=('f856f143db3f0ad37adb6c80b89c33efa5112e1307927ff3331f82857a71fef4',), owned_dir='.claude/skills/gha-sync', successor='now handled by autofix.yaml on every push'), RemovedAsset(component='skills', target='.claude/skills/gha-test/SKILL.md', removed_in='6.0.0', hashes=('4a00dac78e0ca3c598c2a3ae6e649f354f73e754c5aaea531d8409f1eff23434',), owned_dir='.claude/skills/gha-test', successor='now handled by tests.yaml on every push'), RemovedAsset(component='skills', target='.claude/skills/repokit-changelog/SKILL.md', removed_in='6.0.1', hashes=('6e176d9d0090afb9d9a10035e4c6721fff8fac4a1c313010fc04a7ab631be399',), owned_dir='.claude/skills/repokit-changelog', successor='renamed to repomatic-changelog'), RemovedAsset(component='skills', target='.claude/skills/repokit-deps/SKILL.md', removed_in='6.0.1', hashes=('577687ae8481cc67b992497ee0de9fb38c0f26cd20a9b907a4bf78f834803cc0',), owned_dir='.claude/skills/repokit-deps', successor='renamed to repomatic-deps'), RemovedAsset(component='skills', target='.claude/skills/repokit-init/SKILL.md', removed_in='6.0.1', hashes=('c68a9108ead81c4bb5b33912770155f6a587188ca72c8ba8d08f7283fdcad281',), owned_dir='.claude/skills/repokit-init', successor='renamed to repomatic-init'), RemovedAsset(component='skills', target='.claude/skills/repokit-lint/SKILL.md', removed_in='6.0.1', hashes=('1c05f0fb8c5ff8eed38ac02af2fff016e931fdf8866fd93a3fc6c61f84d4df52',), owned_dir='.claude/skills/repokit-lint', successor='now handled by lint.yaml on every push'), RemovedAsset(component='skills', target='.claude/skills/repokit-metadata/SKILL.md', removed_in='6.0.1', hashes=('0322f70cdd8e53d03fce2befbf904be1f0dc5596b79e41557ce8ec788a202cff',), owned_dir='.claude/skills/repokit-metadata', successor='now handled by the repomatic metadata CLI command'), RemovedAsset(component='skills', target='.claude/skills/repokit-release/SKILL.md', removed_in='6.0.1', hashes=('a6ceb0394f084f481765bb834f275af0cb1cf58a9383059358ceec50ea87b93a',), owned_dir='.claude/skills/repokit-release', successor='replaced by repomatic-ship'), RemovedAsset(component='skills', target='.claude/skills/repokit-sync/SKILL.md', removed_in='6.0.1', hashes=('412811337a541b6c4518e588240ce2cb13f3f476bcd311f32edcf04394e17ade',), owned_dir='.claude/skills/repokit-sync', successor='now handled by autofix.yaml on every push'), RemovedAsset(component='skills', target='.claude/skills/repokit-test/SKILL.md', removed_in='6.0.1', hashes=('63f0b532f379aa4400eea5a6284c3004ddc09749c8f476f4ea5a5e8ce3c4716f',), owned_dir='.claude/skills/repokit-test', successor='now handled by tests.yaml on every push'), RemovedAsset(component='skills', target='.claude/skills/repomatic-lint/SKILL.md', removed_in='6.21.0', hashes=('11131553c99adb7daf880b6b19b84e4d4573eedbe7b951092aa7d4a1f9357aab', 'd72cada008b46db93eff0b7a167f1f57346c528ec317fca73857205895fb1395', '058b9cc3248cd1d537d8fbf7a0c1133e3107c6ed405859457e88625b9301d3d8', '7ec6520cba0a14af07ed1bb4e2f0388109ac8db0509ca92ffa0829cf2967bd11'), owned_dir='.claude/skills/repomatic-lint', successor='now handled by lint.yaml on every push'), RemovedAsset(component='skills', target='.claude/skills/repomatic-metadata/SKILL.md', removed_in='6.3.0', hashes=('e94ba4246c0bf56b8dfb6a7e4d3ea2e9521c000e8322130b1746e7a54d3f260b', '58c6eec756177f445893366960464c2d5872de994a692399440df0eb30b11e35'), owned_dir='.claude/skills/repomatic-metadata', successor='now handled by the repomatic metadata CLI command'), RemovedAsset(component='skills', target='.claude/skills/repomatic-release/SKILL.md', removed_in='6.21.0', hashes=('0ecfa8ff5d55b33394d83bce76d39015450403124ff63e131fea14adf685c00b', '8546a42c1ea44b2a4fa0ed1bc49f71eaf8be3b5656a323ee93957ea1fdb0bb38', '778783f3ef6093d9892a4772fc312747155b399e18ba33f416fa9b138897b43d', 'b076cae374b3104f50996cf8b92eae6f53ec9546d3b0fab2c033c90cb1e8a107', '8e93d723827042e90acbe22d038516400bcd743bf39f3fb45a65c115008a97d0'), owned_dir='.claude/skills/repomatic-release', successor='replaced by repomatic-ship'), RemovedAsset(component='skills', target='.claude/skills/repomatic-sync/SKILL.md', removed_in='6.21.0', hashes=('3b36a8b4fc76282c280f6cc19fdc24aa826db8a81ee91a66737b24cb921c84d9', '1460738708f7e878c17ef578a7fad14710962a5fd7e7789f3bc08ae6bc49247b', '54a2b2aa40799c05d666295ee0a1f4d65946605c5397a006185123e4c2e9f1d0', '771d4e15efab4739fb00a7c1ba20495e063025842beb2e54d84207e1410f40a1', '687c7f9cae7271ee56f4d35b754325ba7a2c3b13537eee057679cc160e39471e', 'ceaf3141599850847ee51b2e4f85c76a4cae130a01b2a4fd820dd3b5c0dd0dc0', '91add2c0b7686f64f810bb86fa70c3ac99d3940b37ba6fbe57c01a4d427cc902'), owned_dir='.claude/skills/repomatic-sync', successor='now handled by autofix.yaml on every push'), RemovedAsset(component='skills', target='.claude/skills/repomatic-test/SKILL.md', removed_in='6.21.0', hashes=('8bc5f054507b369f9be34dd4a34183e00b6a8e0186c34d4deb385032e6682e1a', 'cb987bfe342c2d00ea1a6226585238f19bc5a351a678124f7e6225d5c6122c2c', '17bae80a4b98518b6037518ad340a60d117d35a4fa26725fa2ab685ebd23e8dd'), owned_dir='.claude/skills/repomatic-test', successor='now handled by tests.yaml on every push'), RemovedAsset(component='workflows', target='.github/workflows/label-sponsors.yaml', removed_in='4.25.0', hashes=(), owned_dir='', successor='merged into labels.yaml'), RemovedAsset(component='workflows', target='.github/workflows/labeller-content-based.yaml', removed_in='4.25.0', hashes=(), owned_dir='', successor='merged into labels.yaml'), RemovedAsset(component='workflows', target='.github/workflows/labeller-file-based.yaml', removed_in='4.25.0', hashes=(), owned_dir='', successor='merged into labels.yaml'), RemovedAsset(component='workflows', target='.github/workflows/renovate.yaml', removed_in='7.0.0.dev0', hashes=(), owned_dir='', successor='replaced by self-hosted sync-tool-versions, sync-action-pins, and sync-workflow-pins'))¶
Tombstones for assets repomatic has dropped (see
RemovedAsset).initprunes orphaned copies of these from downstream repos. Ordered by(component, target).When you drop a bundled asset from
COMPONENTS, add an entry here so the removal propagates downstream on the nextinitinstead of leaving an orphan. List one hash per distinct content the asset shipped across its released lifetime, collected from the release tags where its data file existed:import hashlib, subprocess src = "repomatic/data/skill-repomatic-release.md" # the dropped data file tags = subprocess.run( ["git", "tag", "--list", "v*"], capture_output=True, text=True, check=True ).stdout.split() hashes = {} for tag in tags: blob = subprocess.run( ["git", "show", f"{tag}:{src}"], capture_output=True, text=True, encoding="UTF-8", ) if blob.returncode == 0: normalized = blob.stdout.rstrip() + "\n" hashes.setdefault(hashlib.sha256(normalized.encode("UTF-8")).hexdigest(), tag) print(tuple(hashes)) # distinct contents, in first-shipped order
Removed workflows are fingerprint-gated, not hashed: omit
hashesand give the workflow’s downstream path astarget(.github/workflows/{name}).
- repomatic.registry.DEFAULT_REPO: str = 'kdeldycke/repomatic'¶
Default upstream repository for reusable workflows.
- repomatic.registry.is_awesome_repo(name)[source]¶
Whether a repository name marks an
awesome-*curated list.The one spelling of the prefix test: repository-trait detection, the broken-links label choice and the awesome-template auto-inclusion all classify on it.
- Return type:
- repomatic.registry.package_of(repo)[source]¶
The package name an
owner/reposlug implies: its repository half.The one spelling of that derivation, shared by every check and rewriter that maps a
--upstream-repovalue onto the inlinepackage==X.Y.Zpin it governs.- Return type:
- repomatic.registry.UPSTREAM_PACKAGE: str = 'repomatic'¶
Distribution name of the upstream toolkit, derived from
DEFAULT_REPO.The freeze, cooldown-exemption, and lint code that handles the
uses:refs and the inline self-pin all key on this name: deriving it here keeps the writer/checker pairs in lockstep and makes a rename a one-line change.
- repomatic.registry.UPSTREAM_REPO_SLUGS: tuple[str, ...] = ('kdeldycke/repomatic', 'kdeldycke/repokit', 'kdeldycke/workflows')¶
Upstream repository slugs across the project’s renames, current first.
A downstream thin-caller’s
uses:line references whichever slug was current when it was generated. Workflow-tombstone detection matches against all of them (current first, since most callers are recent) so an orphaned thin-caller is recognized regardless of which era set it up.
- repomatic.registry.UPSTREAM_SOURCE_GLOB: str = 'repomatic/**'¶
Path glob for the upstream source directory in canonical workflows.
Canonical workflow
paths:filters use this glob to match source code changes. In downstream repos, this is replaced with the project’s own source directory.
- repomatic.registry.UPSTREAM_SOURCE_PREFIX: str = 'repomatic/'¶
Path prefix for upstream-specific files in canonical workflows.
Paths starting with this prefix (but not matching
UPSTREAM_SOURCE_GLOB) are dropped in downstream thin callers because they reference files that only exist in the upstream repository (likerepomatic/data/labels.toml).
- repomatic.registry.SKILL_PHASE_ORDER: tuple[str, ...] = ('Setup', 'Development', 'Quality', 'Maintenance', 'Release')¶
Canonical display order for lifecycle phases in
list-skillsoutput.
- repomatic.registry.SKILL_LIST_HEADER_DEFS: tuple[tuple[str, str], ...] = (('Phase', 'phase'), ('Skill', 'skill'), ('Description', 'description'))¶
Column definitions for the
repomatic list-skillstable.Lives beside
skill_catalog(), whose triples these columns render, so the two cannot drift apart. The columns mirror the hand-maintained roster ofdocs/agent-skills.md, which the page renders this command right above.
- repomatic.registry.ALL_COMPONENTS: dict[str, str] = {'awesome-template': 'Boilerplate for awesome-* repositories', 'bumpversion': 'bump-my-version configuration', 'changelog': 'Minimal changelog.md', 'coverage': 'Coverage.py measurement and reporting configuration', 'labels': 'Label definitions for labelmaker (labels.toml)', 'lychee': 'Lychee link checker configuration', 'mdformat': 'mdformat Markdown formatter configuration', 'mypy': 'Mypy type checking configuration', 'plugin': 'Claude Code plugin marketplace wiring (.claude/settings.json)', 'publish-pypi-action': 'Composite action that publishes to PyPI via Trusted Publishing (.github/actions/publish-pypi/)', 'pytest': 'Pytest test configuration', 'ruff': 'Ruff linter/formatter configuration', 'skills': 'Claude Code skill definitions (.claude/skills/)', 'subagents': 'Agent subagent definitions (.claude/agents/)', 'typos': 'Typos spell checker configuration', 'uv': 'uv resolver pin and dependency cooldown policy', 'workflows': 'Thin-caller workflow files'}¶
All available init components.
- repomatic.registry.EPHEMERAL_TARGETS: frozenset[str] = frozenset({'labels.toml'})¶
Target paths belonging to
Component.ephemeralcomponents.Written only when the component is named explicitly on the CLI, and never worth committing: whatever reads them regenerates them first.
inituses this to keep its closing “commit the generated files” advice off a run that produced nothing but scratch output.
- repomatic.registry.BUNDLED_VERBATIM_TARGETS: frozenset[str] = frozenset({'.claude/agents/grunt-qa.md', '.claude/agents/qa-engineer.md', '.claude/agents/sphinx-docs.md', '.claude/skills/av-false-positive', '.claude/skills/awesome-triage', '.claude/skills/babysit-ci', '.claude/skills/benchmark-update', '.claude/skills/brand-assets', '.claude/skills/file-bug-report', '.claude/skills/github-housekeeping', '.claude/skills/repomatic-audit', '.claude/skills/repomatic-changelog', '.claude/skills/repomatic-deps', '.claude/skills/repomatic-init', '.claude/skills/repomatic-ship', '.claude/skills/repomatic-test-matrix', '.claude/skills/repomatic-topics', '.claude/skills/sphinx-docs-sync', '.claude/skills/translation-sync', '.claude/skills/upstream-audit', '.github/actions/publish-pypi/action.yaml', 'labels.toml'})¶
Target paths
repomatic initwrites verbatim from arepomatic/data/template.Every
BundledComponentcopies its bundled source byte-for-byte to the target, so downstream the file’s content (including any SHA-pinneduses:ref) is owned byrepomatic init.sync-action-pinsandsync-workflow-pinsskip these paths for the same reason they skipUPSTREAM_REPO_SLUGS: a pin the nextsync-repomaticoverwrites turns the two pull requests into a ping-pong, the bump PR and the init-revert PR chasing each other. The skip lifts inside the source repo, where each bundled source is a symlink to its in-tree target and the pin is a normal source-of-truth ref (seerepomatic.sync_ops._pinnable_files). Generated workflows (WorkflowComponent) are deliberately absent: they carry only upstream-slug refs (already skipped) and may host downstream-authored extra jobs whose third-party pins the bumpers should keep current.
- repomatic.registry.REUSABLE_WORKFLOWS: tuple[str, ...] = ('autofix.yaml', 'autolock.yaml', 'cancel-runs.yaml', 'changelog.yaml', 'debug.yaml', 'docs.yaml', 'labels.yaml', 'lint.yaml', 'metrics.yaml', 'release.yaml', 'unsubscribe.yaml')¶
Workflow filenames that support
workflow_calltriggers.
- repomatic.registry.NON_REUSABLE_WORKFLOWS: frozenset[str] = frozenset({'tests.yaml'})¶
Workflows without
workflow_callthat cannot be used as thin callers.
- repomatic.registry.ALL_WORKFLOW_FILES: tuple[str, ...] = ('autofix.yaml', 'autolock.yaml', 'cancel-runs.yaml', 'changelog.yaml', 'debug.yaml', 'docs.yaml', 'labels.yaml', 'lint.yaml', 'metrics.yaml', 'release.yaml', 'tests.yaml', 'unsubscribe.yaml')¶
All workflow filenames (reusable and non-reusable).
- repomatic.registry.WORKFLOW_SOURCES: dict[str, str] = {'autofix.yaml': 'autofix.yaml', 'autolock.yaml': 'autolock.yaml', 'cancel-runs.yaml': 'cancel-runs.yaml', 'changelog.yaml': 'changelog.yaml', 'debug.yaml': 'debug.yaml', 'docs.yaml': 'docs.yaml', 'labels.yaml': 'labels.yaml', 'lint.yaml': 'lint.yaml', 'metrics.yaml': 'metrics.yaml', 'release.yaml': '_release-engine.yaml', 'tests.yaml': 'tests.yaml', 'unsubscribe.yaml': 'unsubscribe.yaml'}¶
Maps each workflow’s downstream file_id to its bundled source filename.
For most workflows source == file_id. The release entry is the exception: its downstream artifact is
release.yaml, whose backing reusable engine is_release-engine.yaml(the lane the generic “is this a reusable workflow” tests inspect). The full set of reusable lanes the generatedrelease.yamlcalls isRELEASE_ENGINE_WORKFLOWS.
- repomatic.registry.RELEASE_ENGINE_WORKFLOWS: tuple[str, ...] = ('_release-build.yaml', '_release-engine.yaml')¶
Reusable workflows the generated
release.yamlreferences but thatrepomatic initnever materializes downstream.The
workflowscomponent deploys a generatedrelease.yaml(not a thin delegation): itsbuildjob calls_release-build.yamland itsreleasejob calls_release-engine.yaml, each via{repo}/.github/workflows/<lane>@<tag>resolved from this repo at the release tag rather than copied into the downstream tree. These lanes live in.github/workflows/here (and at every release tag) but are notFileEntrytargets and never appear inALL_WORKFLOW_FILES.The release entry’s
FileEntrystill records_release-engine.yamlas itssource(seeWORKFLOW_SOURCES) so the generic backing-reusable tests and a downstreamrepomatic lintcan read it viaget_data_contentto check the engine lane forwards its secrets;_release-build.yamlis not bundled because nothing reads it at runtime (the build lane declares no secrets). Naming both lanes here lets stale-file detection and the data-symlink rules treat them as a group instead of special-casing each by hand.
- repomatic.registry.SELF_MAINTENANCE_WORKFLOWS: frozenset[str] = frozenset({'self-maintenance.yaml'})¶
Workflows that maintain this package’s own source and never ship downstream.
Unlike
RELEASE_ENGINE_WORKFLOWS, which downstream repos still reach remotely through auses:ref at a release tag, these are invisible outside this repository: they are notFileEntrytargets, carry norepomatic/data/symlink, and nothing resolves them at runtime. That is what lets their jobs drop thegithub.repository == 'kdeldycke/repomatic'guard every in-autofix.yamlupstream-only step needs, and pick a schedule without spending downstream CI.A workflow belongs here when its write domain is a path that exists only in this repository (
repomatic/tooling/tool_registry.pyand friends). A workflow that merely behaves differently upstream does not: it still ships, so it still needs the runtime guard.
- repomatic.registry.SKILL_PHASES: dict[str, str] = {'av-false-positive': 'Release', 'awesome-triage': 'Maintenance', 'babysit-ci': 'Quality', 'benchmark-update': 'Development', 'brand-assets': 'Development', 'file-bug-report': 'Maintenance', 'github-housekeeping': 'Maintenance', 'repomatic-audit': 'Maintenance', 'repomatic-changelog': 'Release', 'repomatic-deps': 'Development', 'repomatic-init': 'Setup', 'repomatic-ship': 'Release', 'repomatic-test-matrix': 'Quality', 'repomatic-topics': 'Development', 'sphinx-docs-sync': 'Maintenance', 'translation-sync': 'Maintenance', 'upstream-audit': 'Maintenance'}¶
Maps skill names to lifecycle phases for display grouping.
- repomatic.registry.skill_catalog()[source]¶
Read every bundled skill’s display metadata off its frontmatter.
- Return type:
- Returns:
One
(phase, name, description)tuple per bundled skill, in registry order, with the description’s trailing period stripped for table display. Phases are keyed by the registryfile_id, not the frontmatter name, so a skill renamed in frontmatter still lands in its phase.
- repomatic.registry.FILE_SELECTOR_COMPONENTS: tuple[str, ...] = ('labels', 'publish-pypi-action', 'subagents', 'skills', 'workflows')¶
Components that support file-level
component/fileselectors.
- repomatic.registry.COMPONENT_HELP_TABLE: str = ' labels Label definitions for labelmaker (labels.toml)\n publish-pypi-action Composite action that publishes to PyPI via Trusted Publishing (.github/actions/publish-pypi/)\n subagents Agent subagent definitions (.claude/agents/)\n skills Claude Code skill definitions (.claude/skills/)\n workflows Thin-caller workflow files\n awesome-template Boilerplate for awesome-* repositories\n changelog Minimal changelog.md\n plugin Claude Code plugin marketplace wiring (.claude/settings.json)\n uv uv resolver pin and dependency cooldown policy\n lychee Lychee link checker configuration\n ruff Ruff linter/formatter configuration\n pytest Pytest test configuration\n coverage Coverage.py measurement and reporting configuration\n mypy Mypy type checking configuration\n mdformat mdformat Markdown formatter configuration\n bumpversion bump-my-version configuration\n typos Typos spell checker configuration'¶
Formatted component table for CLI help text.
- repomatic.registry.valid_file_ids(component)[source]¶
Return valid file identifiers for a component.
Components with file entries report their declared
file_idvalues. Returns an empty set for components without file-level selection (e.g., changelog, tool configs).
- repomatic.registry.excluded_rel_path(component, file_id)[source]¶
Map a component and file identifier to its relative output path.
Returns
Nonewhen the identifier cannot be resolved (e.g., for tool config components that have no file-level exclusion support).
- repomatic.registry.parse_component_entries(entries, *, context='entry')[source]¶
Parse component entries into full-component and file-level sets.
Bare names (no
/) must be component names fromALL_COMPONENTS. Qualifiedcomponent/identifierentries target individual files. RaisesValueErroron unknown entries.Used by both the
excludeconfig path and the CLI positional selection, with context controlling error message wording.