repomatic 7.5.0__tar.gz → 7.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. {repomatic-7.5.0 → repomatic-7.6.0}/PKG-INFO +3 -3
  2. {repomatic-7.5.0 → repomatic-7.6.0}/pyproject.toml +7 -5
  3. {repomatic-7.5.0 → repomatic-7.6.0}/pyproject.toml.orig +15 -9
  4. {repomatic-7.5.0 → repomatic-7.6.0}/readme.md +1 -1
  5. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/__init__.py +2 -2
  6. repomatic-7.6.0/repomatic/awesome_toc.py +277 -0
  7. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/cli.py +110 -23
  8. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/config.py +31 -3
  9. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/_release-engine.yaml +29 -22
  10. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/action-publish-pypi.yaml +1 -1
  11. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/autofix.yaml +62 -74
  12. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/cancel-runs.yaml +2 -2
  13. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/changelog.yaml +27 -18
  14. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/debug.yaml +3 -3
  15. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/docs.yaml +6 -6
  16. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/labels.yaml +10 -10
  17. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/lint.yaml +16 -16
  18. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/release.yaml +37 -1
  19. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/tests.yaml +5 -5
  20. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/unsubscribe.yaml +2 -2
  21. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/github/issue.py +82 -10
  22. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/github/workflow_sync.py +169 -19
  23. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/init_project.py +168 -29
  24. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/labels.py +32 -10
  25. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/lint_repo.py +417 -1
  26. repomatic-7.6.0/repomatic/plugin.py +339 -0
  27. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/prepare_release.py +46 -0
  28. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/pyproject.py +6 -1
  29. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/registry.py +61 -0
  30. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/sync_ops.py +18 -16
  31. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/tool_registry.py +34 -10
  32. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/version_sync.py +33 -8
  33. {repomatic-7.5.0 → repomatic-7.6.0}/license +0 -0
  34. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/__main__.py +0 -0
  35. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/binaries_page.py +0 -0
  36. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/binary.py +0 -0
  37. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/broken_links.py +0 -0
  38. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/bundle.py +0 -0
  39. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/cache.py +0 -0
  40. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/changelog.py +0 -0
  41. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/checksums.py +0 -0
  42. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/compat.py +0 -0
  43. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/__init__.py +0 -0
  44. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/agent-grunt-qa.md +0 -0
  45. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/agent-qa-engineer.md +0 -0
  46. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/agent-sphinx-docs.md +0 -0
  47. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/autolock.yaml +0 -0
  48. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/awesome_template/.github/ISSUE_TEMPLATE/config.yml +0 -0
  49. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/awesome_template/.github/ISSUE_TEMPLATE/new-link.yaml +0 -0
  50. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/awesome_template/.github/code-of-conduct.md +0 -0
  51. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/awesome_template/.github/contributing.md +0 -0
  52. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/awesome_template/.github/contributing.zh.md +0 -0
  53. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/awesome_template/.github/funding.yml +0 -0
  54. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/awesome_template/.github/pull_request_template.md +0 -0
  55. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/awesome_template/__init__.py +0 -0
  56. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/awesome_template/license +0 -0
  57. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/bumpversion.toml +0 -0
  58. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/codecov.yaml +0 -0
  59. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/labeller-content-based.yaml +0 -0
  60. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/labeller-file-based.yaml +0 -0
  61. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/labels.toml +0 -0
  62. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/lychee.toml +0 -0
  63. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/mdformat.toml +0 -0
  64. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/mypy.toml +0 -0
  65. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/pytest.toml +0 -0
  66. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/ruff.toml +0 -0
  67. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/skills/av-false-positive/SKILL.md +0 -0
  68. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/skills/awesome-triage/SKILL.md +0 -0
  69. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/skills/babysit-ci/SKILL.md +0 -0
  70. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/skills/benchmark-update/SKILL.md +0 -0
  71. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/skills/brand-assets/SKILL.md +0 -0
  72. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/skills/file-bug-report/SKILL.md +0 -0
  73. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/skills/repomatic-audit/SKILL.md +0 -0
  74. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/skills/repomatic-changelog/SKILL.md +0 -0
  75. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/skills/repomatic-deps/SKILL.md +0 -0
  76. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/skills/repomatic-init/SKILL.md +0 -0
  77. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/skills/repomatic-ship/SKILL.md +0 -0
  78. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/skills/repomatic-topics/SKILL.md +0 -0
  79. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/skills/sphinx-docs-sync/SKILL.md +0 -0
  80. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/skills/translation-sync/SKILL.md +0 -0
  81. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/skills/upstream-audit/SKILL.md +0 -0
  82. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/typos.toml +0 -0
  83. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/uv.toml +0 -0
  84. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/yamllint.yaml +0 -0
  85. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/data/zizmor.yaml +0 -0
  86. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/dep_graph.py +0 -0
  87. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/dep_report.py +0 -0
  88. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/dep_sources.py +0 -0
  89. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/docs.py +0 -0
  90. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/frontmatter.py +0 -0
  91. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/git_ops.py +0 -0
  92. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/github/__init__.py +0 -0
  93. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/github/actions.py +0 -0
  94. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/github/dev_release.py +0 -0
  95. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/github/gh.py +0 -0
  96. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/github/matrix.py +0 -0
  97. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/github/pr_body.py +0 -0
  98. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/github/release_sync.py +0 -0
  99. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/github/releases.py +0 -0
  100. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/github/sponsor.py +0 -0
  101. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/github/status.py +0 -0
  102. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/github/token.py +0 -0
  103. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/github/unsubscribe.py +0 -0
  104. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/gitignore.py +0 -0
  105. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/http.py +0 -0
  106. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/humanize.py +0 -0
  107. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/images.py +0 -0
  108. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/mailmap.py +0 -0
  109. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/matrix_axes.py +0 -0
  110. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/metadata.py +0 -0
  111. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/npm.py +0 -0
  112. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/py.typed +0 -0
  113. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/pypi.py +0 -0
  114. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/setup_guide.py +0 -0
  115. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/__init__.py +0 -0
  116. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/available-admonition.md +0 -0
  117. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/broken-links-issue.md +0 -0
  118. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/bump-version.md +0 -0
  119. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/detect-squash-merge.md +0 -0
  120. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/development-warning.md +0 -0
  121. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/fix-changelog.md +0 -0
  122. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/fix-typos.md +0 -0
  123. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/fix-vulnerable-deps.md +0 -0
  124. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/format-images.md +0 -0
  125. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/format-json.md +0 -0
  126. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/format-markdown.md +0 -0
  127. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/format-pyproject.md +0 -0
  128. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/format-python.md +0 -0
  129. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/format-shell.md +0 -0
  130. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/generated-footer.md +0 -0
  131. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/github-releases.md +0 -0
  132. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/immutable-releases.md +0 -0
  133. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/pr-metadata.md.noformat +0 -0
  134. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/prepare-release.md.noformat +0 -0
  135. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/refresh-tip.md +0 -0
  136. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/release-notes.md +0 -0
  137. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/release-sync-report.md +0 -0
  138. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/setup-guide-branch-ruleset.md +0 -0
  139. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/setup-guide-dependabot.md +0 -0
  140. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/setup-guide-fork-pr-approval.md +0 -0
  141. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/setup-guide-notifications-pat.md +0 -0
  142. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/setup-guide-pages-source.md +0 -0
  143. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/setup-guide-pypi-trusted-publisher.md +0 -0
  144. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/setup-guide-sha-pinning-required.md +0 -0
  145. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/setup-guide-token.md +0 -0
  146. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/setup-guide-verify.md +0 -0
  147. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/setup-guide-virustotal.md +0 -0
  148. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/setup-guide.md +0 -0
  149. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/sync-action-pins.md +0 -0
  150. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/sync-bumpversion.md +0 -0
  151. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/sync-dep-sources.md +0 -0
  152. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/sync-gitignore.md +0 -0
  153. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/sync-mailmap.md +0 -0
  154. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/sync-repomatic.md +0 -0
  155. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/sync-tool-versions.md +0 -0
  156. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/sync-uv-lock.md +0 -0
  157. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/sync-workflow-pins.md +0 -0
  158. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/unavailable-admonition.md +0 -0
  159. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/unsubscribe-phase1.md +0 -0
  160. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/unsubscribe-phase2.md +0 -0
  161. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/update-dep-graph.md +0 -0
  162. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/update-docs.md +0 -0
  163. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/templates/yanked-admonition.md +0 -0
  164. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/tool_runner.py +0 -0
  165. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/tool_runner_page.py +0 -0
  166. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/uv.py +0 -0
  167. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/virustotal.py +0 -0
  168. {repomatic-7.5.0 → repomatic-7.6.0}/repomatic/vulnerable_deps.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: repomatic
3
- Version: 7.5.0
3
+ Version: 7.6.0
4
4
  Summary: 🏭 Automate repository maintenance, releases, and CI/CD workflows
5
5
  Keywords: changelog-formatter,dependabot-alternative,github-actions,github-labels,github-workflow,gitignore,mailmap,mypy,nuitka,python-automation,release-automation,repository-automation,repository-management,reusable-workflows,software-factory,sphinx-doc,typo,uv,workflow-reusable
6
6
  Author: Kevin Deldycke
@@ -59,7 +59,7 @@ Requires-Dist: wcmatch>=10
59
59
  Requires-Python: >=3.10
60
60
  Project-URL: Changelog, https://github.com/kdeldycke/repomatic/blob/main/changelog.md
61
61
  Project-URL: Documentation, https://kdeldycke.github.io/repomatic
62
- Project-URL: Download, https://github.com/kdeldycke/repomatic/releases/tag/v7.5.0
62
+ Project-URL: Download, https://github.com/kdeldycke/repomatic/releases/tag/v7.6.0
63
63
  Project-URL: Funding, https://github.com/sponsors/kdeldycke
64
64
  Project-URL: Homepage, https://github.com/kdeldycke/repomatic
65
65
  Project-URL: Issues, https://github.com/kdeldycke/repomatic/issues
@@ -145,7 +145,7 @@ See the **[full documentation](https://kdeldycke.github.io/repomatic/)** for:
145
145
  - [CLI parameters](https://kdeldycke.github.io/repomatic/cli.html)
146
146
  - [Reusable workflow reference](https://kdeldycke.github.io/repomatic/workflows.html) (all 14 workflows with job descriptions)
147
147
  - [Security practices and token setup](https://kdeldycke.github.io/repomatic/security.html)
148
- - [Claude Code skills](https://kdeldycke.github.io/repomatic/skills.html)
148
+ - [Claude Code skills](https://kdeldycke.github.io/repomatic/skills.html) and [agents](https://kdeldycke.github.io/repomatic/agents.html), also installable as a [Claude Code plugin](https://kdeldycke.github.io/repomatic/plugin.html)
149
149
  - [API reference](https://kdeldycke.github.io/repomatic/repomatic.html)
150
150
  - [Project history](https://kdeldycke.github.io/repomatic/history.html)
151
151
 
@@ -4,7 +4,7 @@ requires = ["uv-build>=0.8"]
4
4
 
5
5
  [project]
6
6
  name = "repomatic"
7
- version = "7.5.0"
7
+ version = "7.6.0"
8
8
  description = "🏭 Automate repository maintenance, releases, and CI/CD workflows"
9
9
  readme = "readme.md"
10
10
  keywords = [
@@ -92,7 +92,7 @@ email = "kevin@deldycke.com"
92
92
  [project.urls]
93
93
  Changelog = "https://github.com/kdeldycke/repomatic/blob/main/changelog.md"
94
94
  Documentation = "https://kdeldycke.github.io/repomatic"
95
- Download = "https://github.com/kdeldycke/repomatic/releases/tag/v7.5.0"
95
+ Download = "https://github.com/kdeldycke/repomatic/releases/tag/v7.6.0"
96
96
  Funding = "https://github.com/sponsors/kdeldycke"
97
97
  Homepage = "https://github.com/kdeldycke/repomatic"
98
98
  Issues = "https://github.com/kdeldycke/repomatic/issues"
@@ -146,7 +146,7 @@ module-root = ""
146
146
  [tool.nuitka]
147
147
  copyright = "Kevin Deldycke <kevin@deldycke.com> and contributors. Distributed under GPL-2.0-or-later license."
148
148
  file-description = "🏭 Automate repository maintenance, releases, and CI/CD workflows"
149
- file-version = "7.5.0"
149
+ file-version = "7.6.0"
150
150
  include-data-dir = [
151
151
  "repomatic/data/awesome_template=repomatic/data/awesome_template",
152
152
  "repomatic/data/skills=repomatic/data/skills",
@@ -156,7 +156,7 @@ include-package-data = ["click_extra"]
156
156
  linux-icon = "docs/assets/icon.png"
157
157
  macos-app-icon = "docs/assets/icon.icns"
158
158
  product-name = "Repomatic"
159
- product-version = "7.5.0"
159
+ product-version = "7.6.0"
160
160
  windows-icon-from-ico = "docs/assets/icon.ico"
161
161
 
162
162
  [tool.ruff]
@@ -205,6 +205,7 @@ Typescript = "TypeScript"
205
205
 
206
206
  [tool.typos.default.extend-words]
207
207
  astroid = "astroid"
208
+ Contribuer = "Contribuer"
208
209
  oject = "oject"
209
210
  PNGs = "PNGs"
210
211
  sur = "sur"
@@ -247,7 +248,7 @@ source = ["repomatic"]
247
248
  precision = 2
248
249
 
249
250
  [tool.bumpversion]
250
- current_version = "7.5.0"
251
+ current_version = "7.6.0"
251
252
  parse = '(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\.dev(?P<dev>\d+))?'
252
253
  serialize = [
253
254
  "{major}.{minor}.{patch}.dev{dev}",
@@ -332,6 +333,7 @@ pypi-package-history = [
332
333
  "gha-utils",
333
334
  "repokit",
334
335
  ]
336
+ release-assets = ["repomatic-plugin.zip"]
335
337
 
336
338
  [tool.repomatic.changelog]
337
339
  archive-location = "./docs/changelog-archive.md"
@@ -12,7 +12,7 @@ requires = [ "uv-build>=0.8" ]
12
12
  [project]
13
13
  # Docs: https://packaging.python.org/en/latest/guides/writing-pyproject-toml/
14
14
  name = "repomatic"
15
- version = "7.5.0"
15
+ version = "7.6.0"
16
16
  description = "🏭 Automate repository maintenance, releases, and CI/CD workflows"
17
17
  readme = "readme.md"
18
18
  keywords = [
@@ -93,8 +93,8 @@ dependencies = [
93
93
  "boltons>=25",
94
94
  # click-extra 8.8.0 is the floor: repomatic delegates to the utilities absorbed
95
95
  # there — `format_size` (byte sizes in the image and cache reports), the
96
- # `parse_duration`/`parse_friendly_duration`/`parse_iso8601_duration` family
97
- # (uv cooldown spans in `uv.py`/`version_sync.py`), `prep_path`/`is_stdout`
96
+ # `parse_friendly_duration`/`parse_iso8601_duration` pair (uv cooldown spans
97
+ # in `uv.py`/`version_sync.py`), `prep_path`/`is_stdout`/`STDOUT_SENTINEL`
98
98
  # (CLI `--output` handling), and `config_table_to_flags` (tool flag
99
99
  # translation). Earlier floors remain in play: `binaries_page` imports
100
100
  # `click_extra.blocks` (`replace_region`, 8.7.1) to splice the binaries page
@@ -145,7 +145,7 @@ dependencies = [
145
145
  ]
146
146
  urls.Changelog = "https://github.com/kdeldycke/repomatic/blob/main/changelog.md"
147
147
  urls.Documentation = "https://kdeldycke.github.io/repomatic"
148
- urls.Download = "https://github.com/kdeldycke/repomatic/releases/tag/v7.5.0"
148
+ urls.Download = "https://github.com/kdeldycke/repomatic/releases/tag/v7.6.0"
149
149
  urls.Funding = "https://github.com/sponsors/kdeldycke"
150
150
  urls.Homepage = "https://github.com/kdeldycke/repomatic"
151
151
  urls.Issues = "https://github.com/kdeldycke/repomatic/issues"
@@ -250,7 +250,7 @@ copyright = "Kevin Deldycke <kevin@deldycke.com> and contributors. Distributed u
250
250
  file-description = "🏭 Automate repository maintenance, releases, and CI/CD workflows"
251
251
  # Numeric only: Nuitka rejects PEP 440 .devN suffixes. Kept in sync with the
252
252
  # package version by the dedicated [tool.bumpversion] rule that strips the suffix.
253
- file-version = "7.5.0"
253
+ file-version = "7.6.0"
254
254
  include-data-dir = [
255
255
  "repomatic/data/awesome_template=repomatic/data/awesome_template",
256
256
  "repomatic/data/skills=repomatic/data/skills",
@@ -269,7 +269,7 @@ include-package-data = [ "click_extra" ]
269
269
  linux-icon = "docs/assets/icon.png"
270
270
  macos-app-icon = "docs/assets/icon.icns"
271
271
  product-name = "Repomatic"
272
- product-version = "7.5.0"
272
+ product-version = "7.6.0"
273
273
  windows-icon-from-ico = "docs/assets/icon.ico"
274
274
 
275
275
  [tool.ruff]
@@ -309,8 +309,9 @@ output-prefer-rule-codes = true
309
309
 
310
310
  [tool.typos]
311
311
  default.extend-identifiers = { Github = "GitHub", IOS = "iOS", Javascript = "JavaScript", MacOS = "macOS", PNGs = "PNGs", PyPi = "PyPI", Typescript = "TypeScript" }
312
- # False positives.
313
- default.extend-words = { astroid = "astroid", oject = "oject", PNGs = "PNGs", sur = "sur" }
312
+ # False positives. "Contribuer" is the French for "Contributing", part of the
313
+ # translated-readme fixture in tests/test_awesome_toc.py.
314
+ default.extend-words = { astroid = "astroid", Contribuer = "Contribuer", oject = "oject", PNGs = "PNGs", sur = "sur" }
314
315
  # Skip content between <!-- typos:off --> and <!-- typos:on --> markers.
315
316
  default.extend-ignore-re = [
316
317
  "(?s)<!-- typos:off -->.*?<!-- typos:on -->",
@@ -394,7 +395,7 @@ run.source = [ "repomatic" ]
394
395
  report.precision = 2
395
396
 
396
397
  [tool.bumpversion]
397
- current_version = "7.5.0"
398
+ current_version = "7.6.0"
398
399
  # Parse versions with an optional .devN suffix (PEP 440).
399
400
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\.dev(?P<dev>\\d+))?"
400
401
  serialize = [
@@ -517,3 +518,8 @@ exclude = [ "workflows" ]
517
518
  # the HTML docs site stay in lockstep.
518
519
  manpages.script = "repomatic.cli:repomatic"
519
520
  pypi-package-history = [ "gha-utils", "repokit" ]
521
+ # Claude Code plugin archive packed by release.yaml's pack-plugin job and
522
+ # attached to every GitHub release by the engine's extra-assets job. The
523
+ # marketplace at .claude-plugin/marketplace.json installs the plugin straight
524
+ # from this asset, so a release missing it breaks `/plugin install`.
525
+ release-assets = [ "repomatic-plugin.zip" ]
@@ -77,7 +77,7 @@ See the **[full documentation](https://kdeldycke.github.io/repomatic/)** for:
77
77
  - [CLI parameters](https://kdeldycke.github.io/repomatic/cli.html)
78
78
  - [Reusable workflow reference](https://kdeldycke.github.io/repomatic/workflows.html) (all 14 workflows with job descriptions)
79
79
  - [Security practices and token setup](https://kdeldycke.github.io/repomatic/security.html)
80
- - [Claude Code skills](https://kdeldycke.github.io/repomatic/skills.html)
80
+ - [Claude Code skills](https://kdeldycke.github.io/repomatic/skills.html) and [agents](https://kdeldycke.github.io/repomatic/agents.html), also installable as a [Claude Code plugin](https://kdeldycke.github.io/repomatic/plugin.html)
81
81
  - [API reference](https://kdeldycke.github.io/repomatic/repomatic.html)
82
82
  - [Project history](https://kdeldycke.github.io/repomatic/history.html)
83
83
 
@@ -17,5 +17,5 @@
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
- __version__ = "7.5.0"
21
- __git_tag_sha__ = "ef51d27ea435bf5368112791288130134bd02142"
20
+ __version__ = "7.6.0"
21
+ __git_tag_sha__ = "07e3ef8ae5685c7dfc4af43001b6fc67501396b1"
@@ -0,0 +1,277 @@
1
+ # Copyright Kevin Deldycke <kevin@deldycke.com> and contributors.
2
+ #
3
+ # This program is Free Software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU General Public License
5
+ # as published by the Free Software Foundation; either version 2
6
+ # of the License, or (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program; if not, write to the Free Software
15
+ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16
+
17
+ """Remove the table-of-contents entries awesome-lint forbids.
18
+
19
+ Backs the `fix-awesome-toc` command, which runs on `awesome-*` repositories
20
+ right after `repomatic run mdformat` regenerates the ToC of every readme.
21
+
22
+ ```{note}
23
+ This is the one operation that has no job, PR branch or template of its own,
24
+ against the rule in `claude.md` § Naming conventions for automated operations.
25
+ It corrects what `format-markdown` just wrote, so it has to share that job's
26
+ working tree: given its own job, the two would land in separate PRs and undo
27
+ each other on every push, `format-markdown` re-adding the entries this command
28
+ had removed.
29
+ ```
30
+
31
+ `mdformat-toc` lists every heading in range and offers no exclusion mechanism
32
+ of its own, so the entries have to be deleted afterwards. Upstream tracks the
33
+ feature in [hukkin/mdformat-toc#17](https://github.com/hukkin/mdformat-toc/issues/17)
34
+ and [hukkin/mdformat-toc#20](https://github.com/hukkin/mdformat-toc/pull/20);
35
+ this module can go once either lands and grows a way to express the exclusion
36
+ in the ToC marker.
37
+ """
38
+
39
+ from __future__ import annotations
40
+
41
+ import logging
42
+ import re
43
+ from pathlib import Path
44
+
45
+ TYPE_CHECKING = False
46
+ if TYPE_CHECKING:
47
+ from collections.abc import Iterable, Iterator, Sequence
48
+
49
+ FORBIDDEN_HEADINGS: tuple[str, ...] = (
50
+ "Contents",
51
+ "Contributing",
52
+ "Footnotes",
53
+ "Related Lists",
54
+ )
55
+ """Headings awesome-lint refuses to see listed in the table of contents.
56
+
57
+ Mirrors the roster in
58
+ [awesome-lint's `toc.js`](https://github.com/sindresorhus/awesome-lint/blob/v2.2.2/rules/toc.js#L14-L18),
59
+ plus the heading owning the ToC itself (`Contents`), whose entry trips
60
+ `remark-lint:awesome-toc`:
61
+
62
+ ```
63
+ ✖ 26:1 ToC item "Contents" does not match corresponding heading "Meta"
64
+ ```
65
+
66
+ These are the English names, the only ones awesome-lint knows. A translated
67
+ readme names the same sections in its own language, which is why matching on
68
+ this roster alone is not enough: see {func}`forbidden_headings_for`.
69
+ """
70
+
71
+ README_RE = re.compile(r"^readme(\.[^.]+)?\.md$")
72
+ """Match `readme.md` and every `readme.{lang}.md` translation beside it.
73
+
74
+ Only the repository root is scanned. The `find ./` this replaced walked the
75
+ whole tree, which on a checkout carrying a `node_modules/` directory would
76
+ have reached a few hundred vendored readmes.
77
+ """
78
+
79
+ REFERENCE_README = "readme.md"
80
+ """The English readme, whose heading positions every translation is mapped onto."""
81
+
82
+ HEADING_RE = re.compile(r"^\#{1,6}[ \t]+(?P<text>.+?)[ \t]*\#*[ \t]*$", re.MULTILINE)
83
+ """Match an ATX heading and capture its text, closing sequence excluded."""
84
+
85
+ TOC_ENTRY_RE = re.compile(r"^[ \t]*- \[(?P<text>.+)\]\(#[^)]*\)$")
86
+ """Match one `mdformat-toc` list entry and capture its link text."""
87
+
88
+ TOC_START_RE = re.compile(r"^<!--\s*mdformat-toc\s+start\b.*-->$", re.IGNORECASE)
89
+ """Match the opening marker of an `mdformat-toc` block."""
90
+
91
+ TOC_END_RE = re.compile(r"^<!--\s*mdformat-toc\s+end\s*-->$", re.IGNORECASE)
92
+ """Match the closing marker of an `mdformat-toc` block."""
93
+
94
+ FENCE_RE = re.compile(r"^[ \t]*(?P<fence>`{3,}|~{3,})")
95
+ """Match the delimiter of a fenced code block."""
96
+
97
+
98
+ def _strip_fenced_blocks(content: str) -> str:
99
+ """Blank out fenced code blocks so their `#` lines are not read as headings.
100
+
101
+ Replaces the content of every fence with empty lines, keeping the line
102
+ count intact so positions elsewhere are unaffected.
103
+
104
+ :param content: The full Markdown document.
105
+ :return: The document with fenced blocks blanked out.
106
+ """
107
+ lines = content.splitlines()
108
+ kept: list[str] = []
109
+ fence: str | None = None
110
+ for line in lines:
111
+ match = FENCE_RE.match(line)
112
+ if fence is None:
113
+ if match:
114
+ fence = match.group("fence")[0]
115
+ kept.append("")
116
+ continue
117
+ elif match and match.group("fence")[0] == fence:
118
+ fence = None
119
+ kept.append("")
120
+ continue
121
+ kept.append("" if fence else line)
122
+ return "\n".join(kept)
123
+
124
+
125
+ def headings(content: str) -> list[str]:
126
+ """List the ATX heading texts of a Markdown document, in document order.
127
+
128
+ :param content: The full Markdown document.
129
+ :return: Every heading text, code fences excluded.
130
+ """
131
+ return [m.group("text") for m in HEADING_RE.finditer(_strip_fenced_blocks(content))]
132
+
133
+
134
+ def forbidden_headings_for(
135
+ content: str,
136
+ reference_headings: Sequence[str] | None = None,
137
+ ) -> set[str]:
138
+ """Resolve which heading texts must not appear in *content*'s ToC.
139
+
140
+ Always includes the English {data}`FORBIDDEN_HEADINGS` that awesome-lint
141
+ knows, since a translation routinely leaves some of them untranslated.
142
+
143
+ On top of that, when *reference_headings* is given and the document has the
144
+ same number of headings, the heading occupying each forbidden position in
145
+ the reference is forbidden here too. That positional mapping is what carries
146
+ the rule across languages: repomatic cannot know that `贡献` translates
147
+ `Contributing`, but it can see that both sit at the same index of a readme
148
+ and its translation. Headings survive formatting untouched, so the mapping
149
+ holds even against an already-stripped reference.
150
+
151
+ :param content: The Markdown document to resolve the roster for.
152
+ :param reference_headings: Headings of {data}`REFERENCE_README`, if any.
153
+ :return: The heading texts whose ToC entry must be deleted.
154
+ """
155
+ forbidden = set(FORBIDDEN_HEADINGS)
156
+ if not reference_headings:
157
+ return forbidden
158
+
159
+ own_headings = headings(content)
160
+ if len(own_headings) != len(reference_headings):
161
+ logging.warning(
162
+ f"Heading count differs from {REFERENCE_README} "
163
+ f"({len(own_headings)} vs {len(reference_headings)}): "
164
+ "falling back to English heading names only."
165
+ )
166
+ return forbidden
167
+
168
+ forbidden.update(
169
+ own_headings[index]
170
+ for index, heading in enumerate(reference_headings)
171
+ if heading in FORBIDDEN_HEADINGS
172
+ )
173
+ return forbidden
174
+
175
+
176
+ def _toc_bounds(lines: Sequence[str]) -> tuple[int, int] | None:
177
+ """Locate the `mdformat-toc` block in *lines*.
178
+
179
+ :param lines: The document split into lines.
180
+ :return: The start and end marker indices, or `None` when either is absent.
181
+ """
182
+ start = end = None
183
+ for index, line in enumerate(lines):
184
+ if start is None:
185
+ if TOC_START_RE.match(line):
186
+ start = index
187
+ elif TOC_END_RE.match(line):
188
+ end = index
189
+ break
190
+ if start is None or end is None:
191
+ return None
192
+ return start, end
193
+
194
+
195
+ def strip_toc_entries(content: str, forbidden: set[str]) -> tuple[str, list[str]]:
196
+ """Delete the ToC entries of *content* whose link text is *forbidden*.
197
+
198
+ Only the `mdformat-toc` block is touched: a list item elsewhere in the
199
+ document that happens to link the same heading is left alone.
200
+
201
+ :param content: The full Markdown document.
202
+ :param forbidden: Heading texts whose entry must go.
203
+ :return: The updated document and the link texts that were deleted.
204
+ """
205
+ lines = content.splitlines(keepends=True)
206
+ bounds = _toc_bounds([line.rstrip("\r\n") for line in lines])
207
+ if bounds is None:
208
+ return content, []
209
+ start, end = bounds
210
+
211
+ kept: list[str] = []
212
+ removed: list[str] = []
213
+ for index, line in enumerate(lines):
214
+ if start < index < end:
215
+ match = TOC_ENTRY_RE.match(line.rstrip("\r\n"))
216
+ if match and match.group("text") in forbidden:
217
+ removed.append(match.group("text"))
218
+ continue
219
+ kept.append(line)
220
+ return "".join(kept), removed
221
+
222
+
223
+ def fix_awesome_toc(root: Path | None = None) -> dict[Path, list[str]]:
224
+ """Strip the forbidden ToC entries from every readme under *root*.
225
+
226
+ Reads {data}`REFERENCE_README` first so its heading positions can be mapped
227
+ onto each translation, then rewrites every readme that changed. Idempotent:
228
+ a second run finds nothing left to delete.
229
+
230
+ :param root: Directory holding the readmes. Defaults to the current one.
231
+ :return: The deleted entries, keyed by the readme they came from.
232
+ """
233
+ root = Path.cwd() if root is None else root
234
+
235
+ reference = root / REFERENCE_README
236
+ reference_headings: list[str] | None = None
237
+ if reference.is_file():
238
+ reference_headings = headings(reference.read_text(encoding="utf-8"))
239
+ else:
240
+ logging.warning(
241
+ f"No {REFERENCE_README} in {root}: translations can only be matched "
242
+ "on English heading names."
243
+ )
244
+
245
+ report: dict[Path, list[str]] = {}
246
+ for readme in _readmes(root):
247
+ content = readme.read_text(encoding="utf-8")
248
+ forbidden = forbidden_headings_for(content, reference_headings)
249
+ updated, removed = strip_toc_entries(content, forbidden)
250
+ if not removed:
251
+ logging.debug(f"No forbidden ToC entry in {readme}.")
252
+ continue
253
+ readme.write_text(updated, encoding="utf-8")
254
+ logging.info(f"Removed {len(removed)} ToC entries from {readme}.")
255
+ report[readme] = removed
256
+ return report
257
+
258
+
259
+ def _readmes(root: Path) -> Iterator[Path]:
260
+ """Yield the readmes of *root*, the reference first.
261
+
262
+ The reference goes first so its own entries are gone before a translation
263
+ is compared against it, which keeps a partial failure from leaving the
264
+ English readme dirtier than its translations.
265
+
266
+ :param root: Directory to scan.
267
+ :return: Each matching readme path, sorted after the reference.
268
+ """
269
+ matches: Iterable[Path] = sorted(
270
+ path for path in root.iterdir() if path.is_file() and README_RE.match(path.name)
271
+ )
272
+ for path in matches:
273
+ if path.name == REFERENCE_README:
274
+ yield path
275
+ for path in matches:
276
+ if path.name != REFERENCE_README:
277
+ yield path