git-repokit 0.9.11__tar.gz → 0.9.12__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 (220) hide show
  1. {git_repokit-0.9.11/git_repokit.egg-info → git_repokit-0.9.12}/PKG-INFO +1 -3
  2. {git_repokit-0.9.11 → git_repokit-0.9.12}/README.md +0 -2
  3. {git_repokit-0.9.11 → git_repokit-0.9.12/git_repokit.egg-info}/PKG-INFO +1 -3
  4. {git_repokit-0.9.11 → git_repokit-0.9.12}/git_repokit.egg-info/SOURCES.txt +1 -0
  5. {git_repokit-0.9.11 → git_repokit-0.9.12}/pyproject.toml +1 -1
  6. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/__version__.py +1 -1
  7. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/branch_utils.py +4 -2
  8. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/common_kit.py +42 -5
  9. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/guardrails.py +1 -1
  10. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/hooks/pre-commit-template +2 -2
  11. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/repo_manager.py +81 -77
  12. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/hooks/pre-commit.template +2 -2
  13. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_layers.py +39 -1
  14. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_new_default.py +11 -0
  15. git_repokit-0.9.12/tests/test_private_branch_create.py +338 -0
  16. {git_repokit-0.9.11 → git_repokit-0.9.12}/LICENSE +0 -0
  17. {git_repokit-0.9.11 → git_repokit-0.9.12}/MANIFEST.in +0 -0
  18. {git_repokit-0.9.11 → git_repokit-0.9.12}/git_repokit.egg-info/dependency_links.txt +0 -0
  19. {git_repokit-0.9.11 → git_repokit-0.9.12}/git_repokit.egg-info/entry_points.txt +0 -0
  20. {git_repokit-0.9.11 → git_repokit-0.9.12}/git_repokit.egg-info/requires.txt +0 -0
  21. {git_repokit-0.9.11 → git_repokit-0.9.12}/git_repokit.egg-info/top_level.txt +0 -0
  22. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/__init__.py +0 -0
  23. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/__main__.py +0 -0
  24. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/_vendor/__init__.py +0 -0
  25. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/_vendor/ailib/__init__.py +0 -0
  26. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/_vendor/ailib/analyzer.py +0 -0
  27. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/_vendor/ailib/backend.py +0 -0
  28. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/_vendor/ailib/cache.py +0 -0
  29. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/_vendor/ailib/parsers.py +0 -0
  30. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/_vendor/ailib/transports/__init__.py +0 -0
  31. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/_vendor/ailib/transports/cli.py +0 -0
  32. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/_vendor/ailib/transports/openai_compat.py +0 -0
  33. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/_vendor/ailib/transports/prompt_file.py +0 -0
  34. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/_vendor/ailib/types.py +0 -0
  35. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/auth_integration.py +0 -0
  36. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/badges.py +0 -0
  37. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/cli.py +0 -0
  38. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/config.py +0 -0
  39. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/defaults.py +0 -0
  40. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/directory_analyzer.py +0 -0
  41. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/directory_profiles.py +0 -0
  42. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/git_config.py +0 -0
  43. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/github_create.py +0 -0
  44. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/github_setup.py +0 -0
  45. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/history_cleaner.py +0 -0
  46. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/history_protection.py +0 -0
  47. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/hooks/pre-commit-guardrails +0 -0
  48. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/layers.py +0 -0
  49. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/0bsd.txt +0 -0
  50. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/LICENSES.md +0 -0
  51. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/__init__.py +0 -0
  52. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/agpl-3.0.txt +0 -0
  53. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/all-rights-reserved.txt +0 -0
  54. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/apache-2.0.txt +0 -0
  55. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/bsd-2-clause.txt +0 -0
  56. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/bsd-3-clause.txt +0 -0
  57. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/bsl-1.0.txt +0 -0
  58. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/cc-by-4.0.txt +0 -0
  59. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/cc-by-sa-4.0.txt +0 -0
  60. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/cc0-1.0.txt +0 -0
  61. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/epl-2.0.txt +0 -0
  62. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/gpl-2.0.txt +0 -0
  63. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/gpl-3.0.txt +0 -0
  64. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/isc.txt +0 -0
  65. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/lgpl-2.1.txt +0 -0
  66. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/lgpl-3.0.txt +0 -0
  67. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/mit.txt +0 -0
  68. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/mpl-2.0.txt +0 -0
  69. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/proprietary-evaluation.txt +0 -0
  70. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/licenses/unlicense.txt +0 -0
  71. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/readme_blocks.py +0 -0
  72. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/remote_integration.py +0 -0
  73. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/template_engine.py +0 -0
  74. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/ai/claude/CLAUDE-nested.md.template +0 -0
  75. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/ai/claude/CLAUDE.md.template +0 -0
  76. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/ai/claude/instructions/step1_context_rebuilder.md +0 -0
  77. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/ai/claude/instructions/step2_dev_workflow_process.md +0 -0
  78. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/ai/claude/instructions/step3_context_bridge.md +0 -0
  79. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/common/CHANGELOG.md.template +0 -0
  80. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/common/CONTRIBUTING.md.template +0 -0
  81. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/common/README.md.template +0 -0
  82. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/common/gitattributes.template +0 -0
  83. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/common/gitignore.template +0 -0
  84. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/extras/FUNDING.yml.template +0 -0
  85. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/extras/ROADMAP.md.template +0 -0
  86. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/extras/dependabot.yml.template +0 -0
  87. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/extras/one-offs-README.md.template +0 -0
  88. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/extras/platform-support.md.template +0 -0
  89. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/extras/pull_request_template.md.template +0 -0
  90. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/extras/release-template.md.template +0 -0
  91. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/extras/settings.json.template +0 -0
  92. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/extras/stale.yml.template +0 -0
  93. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/github/CODEOWNERS.template +0 -0
  94. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/github/ISSUE_TEMPLATE/bug-report.md.template +0 -0
  95. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/github/ISSUE_TEMPLATE/feature-request.md.template +0 -0
  96. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/github/issues/notes.md.template +0 -0
  97. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/github/issues/roadmap.md.template +0 -0
  98. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/any/claude-plugin/.claude-plugin/marketplace.json +0 -0
  99. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/any/claude-plugin/plugin/.claude-plugin/plugin.json +0 -0
  100. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/any/claude-plugin/plugin/README.md +0 -0
  101. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/c-cpp/cmake-app/.github/workflows/ci-c-cpp.yml +0 -0
  102. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/c-cpp/cmake-app/.repokit-common.toml.rkappend +0 -0
  103. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/c-cpp/cmake-app/.vscode/launch.json +0 -0
  104. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/c-cpp/cmake-app/.vscode/tasks.json +0 -0
  105. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/c-cpp/cmake-app/CMakeLists.txt +0 -0
  106. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/c-cpp/cmake-app/CMakePresets.json +0 -0
  107. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/c-cpp/cmake-app/cmake/GenerateStamp.cmake +0 -0
  108. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/c-cpp/cmake-app/src/main.cpp +0 -0
  109. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/c-cpp/cmake-app/tests/test_version.cpp +0 -0
  110. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/c-cpp/cmake-app/vcpkg.json +0 -0
  111. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/c-cpp/minimal/.vscode/launch.json +0 -0
  112. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/c-cpp/minimal/Makefile +0 -0
  113. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/c-cpp/minimal/build.bat +0 -0
  114. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/c-cpp/minimal/src/__RK_SNAKE__.c +0 -0
  115. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/c-cpp/msbuild/.vscode/launch.json +0 -0
  116. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/c-cpp/msbuild/.vscode/tasks.json +0 -0
  117. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/c-cpp/msbuild/__RK_PROJECT__.sln +0 -0
  118. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/c-cpp/msbuild/__RK_PROJECT__.vcxproj +0 -0
  119. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/c-cpp/msbuild/src/main.cpp +0 -0
  120. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/js-ts/claude-plugin/.repokit-common.toml +0 -0
  121. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/js-ts/claude-plugin/.vscode/launch.json +0 -0
  122. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/js-ts/claude-plugin/plugin/hooks/hooks.json +0 -0
  123. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/js-ts/claude-plugin/plugin/hooks/scripts/session-start.mjs +0 -0
  124. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/js-ts/claude-plugin/plugin/tests/manifest.test.js +0 -0
  125. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/js-ts/node/.repokit-common.toml.rkappend +0 -0
  126. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/js-ts/node/package.json +0 -0
  127. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/js-ts/node-package/.github/workflows/release.yml +0 -0
  128. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/js-ts/node-package/package.json +0 -0
  129. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/js-ts/node-package/src/index.ts +0 -0
  130. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/js-ts/node-package/tests/index.test.ts +0 -0
  131. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/js-ts/script/.vscode/launch.json +0 -0
  132. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/js-ts/script/__RK_PROJECT__.js +0 -0
  133. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/js-ts/script/tests/version.test.js +0 -0
  134. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/js-ts/ts/package.json +0 -0
  135. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/js-ts/ts/tsconfig.json +0 -0
  136. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/claude-plugin/.github/workflows/ci-python.yml +0 -0
  137. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/claude-plugin/.repokit-common.toml +0 -0
  138. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/claude-plugin/.vscode/launch.json +0 -0
  139. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/claude-plugin/VERSION +0 -0
  140. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/claude-plugin/plugin/hooks/hooks.json +0 -0
  141. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/claude-plugin/plugin/hooks/scripts/run-hook.mjs +0 -0
  142. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/claude-plugin/plugin/hooks/scripts/session_start.py +0 -0
  143. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/claude-plugin/plugin/tests/test_manifest.py +0 -0
  144. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/pypi/.github/workflows/release.yml +0 -0
  145. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/pypi/.vscode/launch.json +0 -0
  146. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/pypi/__RK_SNAKE__/__init__.py +0 -0
  147. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/pypi/__RK_SNAKE__/__main__.py +0 -0
  148. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/pypi/__RK_SNAKE__/_version.py +0 -0
  149. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/pypi/__RK_SNAKE__/cli.py +0 -0
  150. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/pypi/pyproject.toml +0 -0
  151. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/pypi/tests/conftest.py +0 -0
  152. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/pypi/tests/test_version.py +0 -0
  153. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/script/.github/workflows/ci-python.yml +0 -0
  154. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/script/.repokit-common.toml +0 -0
  155. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/script/.vscode/launch.json +0 -0
  156. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/script/VERSION +0 -0
  157. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/script/__RK_PROJECT__.py +0 -0
  158. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/script/tests/test_version.py +0 -0
  159. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/setuppy/requirements.txt +0 -0
  160. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/archetypes/python/setuppy/setup.py +0 -0
  161. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/languages/c-cpp/.gitignore.rkappend +0 -0
  162. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/languages/c-cpp/.repokit-common.toml +0 -0
  163. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/languages/c-cpp/include/__RK_SNAKE__/version.h +0 -0
  164. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/languages/c-cpp/src/app.rc +0 -0
  165. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/languages/js-ts/.editorconfig +0 -0
  166. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/languages/js-ts/.github/workflows/ci-js-ts.yml +0 -0
  167. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/languages/js-ts/.gitignore.rkappend +0 -0
  168. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/languages/js-ts/.repokit-common.toml +0 -0
  169. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/languages/js-ts/.vscode/launch.json +0 -0
  170. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/languages/js-ts/VERSION +0 -0
  171. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/languages/python/.github/workflows/ci-python.yml +0 -0
  172. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/languages/python/.gitignore.rkappend +0 -0
  173. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/languages/python/.vscode/launch.json +0 -0
  174. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/layers/manifest.toml +0 -0
  175. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/vault/CLAUDE.md +0 -0
  176. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/vault/claude/_maps/MOC-project.md +0 -0
  177. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/vault/claude/_oracle/backlinks.md +0 -0
  178. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/vault/claude/_oracle/concepts.md +0 -0
  179. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/vault/claude/_oracle/manifest.md +0 -0
  180. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/vault/claude/_todo/ACTIVE-WORK.md +0 -0
  181. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/templates/vault/claude/_todo/OPEN-LOOPS.md +0 -0
  182. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/utils.py +0 -0
  183. {git_repokit-0.9.11 → git_repokit-0.9.12}/repokit/vault.py +0 -0
  184. {git_repokit-0.9.11 → git_repokit-0.9.12}/setup.cfg +0 -0
  185. {git_repokit-0.9.11 → git_repokit-0.9.12}/setup.py +0 -0
  186. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_badges.py +0 -0
  187. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_branch_strategies.py +0 -0
  188. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_cli_examples.py +0 -0
  189. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_cli_integration.py +0 -0
  190. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_core_functionality.py +0 -0
  191. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_deployment_scenarios.py +0 -0
  192. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_directory_profiles.py +0 -0
  193. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_docs_links.py +0 -0
  194. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_extras_cli.py +0 -0
  195. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_github_create.py +0 -0
  196. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_github_integration.py +0 -0
  197. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_github_owner.py +0 -0
  198. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_github_setup.py +0 -0
  199. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_guardrails.py +0 -0
  200. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_hermetic.py +0 -0
  201. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_hook_encoding.py +0 -0
  202. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_hook_patterns.py +0 -0
  203. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_layer_action_pins.py +0 -0
  204. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_layers_c_cpp.py +0 -0
  205. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_layers_claude_plugin.py +0 -0
  206. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_layers_cli.py +0 -0
  207. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_layers_compositions.py +0 -0
  208. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_layers_js_ts.py +0 -0
  209. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_layers_vscode.py +0 -0
  210. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_legacy_smoke.py +0 -0
  211. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_license_adopt.py +0 -0
  212. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_license_cli.py +0 -0
  213. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_licenses.py +0 -0
  214. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_packaging.py +0 -0
  215. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_readme_blocks.py +0 -0
  216. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_run_tests_driver.py +0 -0
  217. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_template_parity.py +0 -0
  218. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_utils.py +0 -0
  219. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_vault_bootstrap.py +0 -0
  220. {git_repokit-0.9.11 → git_repokit-0.9.12}/tests/test_vendored_ailib.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: git-repokit
3
- Version: 0.9.11
3
+ Version: 0.9.12
4
4
  Summary: Create standardized Git repositories: language presets, licenses, GitHub setup, and a private notes vault
5
5
  Author: Dustin Darcy
6
6
  License-Expression: MIT
@@ -157,8 +157,6 @@ git push
157
157
 
158
158
  `safe-merge` leaves out the private content (`private/`, `CLAUDE.md`, `revisions/`, ...), and the hooks refuse it on a public branch if it ever gets staged. `--github` pushes only the public branches. Other layouts: `simple` (private, dev, main), `gitflow`, `github-flow`, and your own with `--branches` and `--worktrees`; see [Branch strategies](#branch-strategies) below.
159
159
 
160
- > **Known issue in 0.9.11:** this create prints `fatal: ambiguous argument 'private'` once per public branch, and `dev/` and `github/` start with only the README and `.github/`. The loop above still brings the work across. The fix is tracked in [#16](https://github.com/DazzleTools/git-repokit/issues/16).
161
-
162
160
  ### Other common creates
163
161
 
164
162
  ```bash
@@ -116,8 +116,6 @@ git push
116
116
 
117
117
  `safe-merge` leaves out the private content (`private/`, `CLAUDE.md`, `revisions/`, ...), and the hooks refuse it on a public branch if it ever gets staged. `--github` pushes only the public branches. Other layouts: `simple` (private, dev, main), `gitflow`, `github-flow`, and your own with `--branches` and `--worktrees`; see [Branch strategies](#branch-strategies) below.
118
118
 
119
- > **Known issue in 0.9.11:** this create prints `fatal: ambiguous argument 'private'` once per public branch, and `dev/` and `github/` start with only the README and `.github/`. The loop above still brings the work across. The fix is tracked in [#16](https://github.com/DazzleTools/git-repokit/issues/16).
120
-
121
119
  ### Other common creates
122
120
 
123
121
  ```bash
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: git-repokit
3
- Version: 0.9.11
3
+ Version: 0.9.12
4
4
  Summary: Create standardized Git repositories: language presets, licenses, GitHub setup, and a private notes vault
5
5
  Author: Dustin Darcy
6
6
  License-Expression: MIT
@@ -157,8 +157,6 @@ git push
157
157
 
158
158
  `safe-merge` leaves out the private content (`private/`, `CLAUDE.md`, `revisions/`, ...), and the hooks refuse it on a public branch if it ever gets staged. `--github` pushes only the public branches. Other layouts: `simple` (private, dev, main), `gitflow`, `github-flow`, and your own with `--branches` and `--worktrees`; see [Branch strategies](#branch-strategies) below.
159
159
 
160
- > **Known issue in 0.9.11:** this create prints `fatal: ambiguous argument 'private'` once per public branch, and `dev/` and `github/` start with only the README and `.github/`. The loop above still brings the work across. The fix is tracked in [#16](https://github.com/DazzleTools/git-repokit/issues/16).
161
-
162
160
  ### Other common creates
163
161
 
164
162
  ```bash
@@ -209,6 +209,7 @@ tests/test_license_cli.py
209
209
  tests/test_licenses.py
210
210
  tests/test_new_default.py
211
211
  tests/test_packaging.py
212
+ tests/test_private_branch_create.py
212
213
  tests/test_readme_blocks.py
213
214
  tests/test_run_tests_driver.py
214
215
  tests/test_template_parity.py
@@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"
8
8
  # PyPI's trusted publisher is registered for this name (DazzleTools/git-repokit,
9
9
  # publish.yml, environment pypi); the command stays `repokit`
10
10
  name = "git-repokit"
11
- version = "0.9.11"
11
+ version = "0.9.12"
12
12
  description = "Create standardized Git repositories: language presets, licenses, GitHub setup, and a private notes vault"
13
13
  readme = "README.md"
14
14
  license = "MIT"
@@ -1,4 +1,4 @@
1
1
  """Version information for RepoKit."""
2
2
 
3
- __version__ = "0.9.11"
3
+ __version__ = "0.9.12"
4
4
  __version_info__ = tuple(int(part) for part in __version__.split("."))
@@ -154,10 +154,12 @@ class BranchContext:
154
154
  return self.branch_type == 'public'
155
155
 
156
156
  def get_staged_files(self) -> List[str]:
157
- """Get list of files staged for commit."""
157
+ """Files the commit adds or changes (0.9.12: deletions are left out --
158
+ removing a private file from a public branch is what the guard wants,
159
+ not something to refuse)."""
158
160
  try:
159
161
  result = subprocess.run(
160
- ['git', 'diff', '--cached', '--name-only'],
162
+ ['git', 'diff', '--cached', '--name-only', '--diff-filter=ACMR'],
161
163
  cwd=self.repo_path,
162
164
  capture_output=True,
163
165
  text=True,
@@ -131,23 +131,59 @@ def _toml_list(values: List[str]) -> str:
131
131
  _TABLE_HEADER = re.compile(r"^[ \t]*\[[ \t]*tool[ \t]*\.[ \t]*\"?repokit-common\"?[ \t]*\][ \t]*\r?\n", re.MULTILINE)
132
132
 
133
133
 
134
+ # Keys whose list only ever protects more as it grows: when the table already
135
+ # has one, the entries it lacks are added to it rather than the key being
136
+ # skipped (0.9.12). A layer's pyproject.toml ships its own private-patterns, and
137
+ # skipping the key dropped CLAUDE.md and the private dirs from the guard.
138
+ _UNION_KEYS = ("private-patterns",)
139
+ _TOML_STRING = re.compile(r'"((?:[^"\\]|\\.)*)"|\'([^\']*)\'')
140
+
141
+
142
+ def _union_into_list(table: str, key: str, values: List[str]) -> Optional[str]:
143
+ """`table` with the `values` its `key = [...]` list lacks appended inside
144
+ the brackets (a list written over several lines is kept as written).
145
+ None when the key is not a list or nothing is missing."""
146
+ found = re.search(rf"^([ \t]*{re.escape(key)}[ \t]*=[ \t]*\[)(.*?)(\])", table, re.MULTILINE | re.DOTALL)
147
+ if not found:
148
+ return None
149
+ inner = found.group(2)
150
+ present = {(m.group(1) if m.group(1) is not None else m.group(2)).replace('\\"', '"').replace("\\\\", "\\")
151
+ for m in _TOML_STRING.finditer(inner)}
152
+ missing = [v for v in values if v not in present]
153
+ if not missing:
154
+ return None
155
+ kept = inner.rstrip()
156
+ tail = inner[len(kept):]
157
+ sep = "" if not kept.strip() else (" " if kept.endswith(",") else ", ")
158
+ added = _toml_list(missing)[1:-1]
159
+ return table[: found.start(2)] + kept + sep + added + tail + table[found.end(2):]
160
+
161
+
134
162
  def _merge_into_table(text: str, settings: Dict[str, List[str]]) -> Optional[str]:
135
163
  """Add the keys the existing [tool.repokit-common] table lacks, right after
136
- its header; a key already present is never overwritten. Returns the new
137
- text, or None when nothing was missing."""
164
+ its header. A key already present is never overwritten, except that a
165
+ `_UNION_KEYS` list gains the entries it lacks. Returns the new text, or
166
+ None when nothing was missing."""
138
167
  header = _TABLE_HEADER.search(text)
139
168
  if not header:
140
169
  return None
141
170
  rest = text[header.end():]
142
171
  next_table = re.search(r"^[ \t]*\[", rest, re.MULTILINE) # the table ends at the next header
143
172
  table = rest[: next_table.start()] if next_table else rest
173
+ after = rest[len(table):]
144
174
  missing = [key for key in settings
145
175
  if not re.search(rf"^[ \t]*{re.escape(key)}[ \t]*=", table, re.MULTILINE)]
146
- if not missing:
176
+ grown = table
177
+ for key in _UNION_KEYS:
178
+ if key in settings and key not in missing:
179
+ unioned = _union_into_list(grown, key, settings[key])
180
+ if unioned is not None:
181
+ grown = unioned
182
+ if not missing and grown == table:
147
183
  return None
148
184
  nl = "\r\n" if "\r\n" in text else "\n"
149
185
  lines = "".join(f"{key} = {_toml_list(settings[key])}{nl}" for key in missing)
150
- return text[: header.end()] + lines + rest
186
+ return text[: header.end()] + lines + grown + after
151
187
 
152
188
 
153
189
  def write_settings(repo_root: str, settings: Dict[str, List[str]]) -> Optional[str]:
@@ -158,7 +194,8 @@ def write_settings(repo_root: str, settings: Dict[str, List[str]]) -> Optional[s
158
194
  (created with the header repokit-common requires, or merged the same way).
159
195
  A key already present is never overwritten: a code-type layer may ship
160
196
  the file with its own version-source, and the private-branch settings go
161
- in beside it rather than being dropped.
197
+ in beside it rather than being dropped. The one exception is
198
+ private-patterns, whose existing list gains the entries it lacks.
162
199
  """
163
200
  if not settings:
164
201
  return None
@@ -214,7 +214,7 @@ except ImportError:
214
214
 
215
215
  if branch not in ['private', 'local']:
216
216
  # Check for obvious private files
217
- result = subprocess.run(['git', 'diff', '--cached', '--name-only'],
217
+ result = subprocess.run(['git', 'diff', '--cached', '--name-only', '--diff-filter=ACMR'],
218
218
  capture_output=True, text=True)
219
219
  files = result.stdout.strip().split('\\n')
220
220
 
@@ -2,7 +2,7 @@
2
2
  # Pre-commit hook to prevent committing private content
3
3
 
4
4
  # Check for private directories
5
- for file in $(git diff --cached --name-only); do
5
+ for file in $(git diff --cached --name-only --diff-filter=ACMR); do
6
6
  if echo "$file" | grep -q -E "^private/|^convos/|^logs/|__private__"; then
7
7
  echo "ERROR: Attempting to commit private content: $file"
8
8
  echo "Private content should not be committed to the repository."
@@ -15,7 +15,7 @@ PRIVATE_PATTERNS=(
15
15
  $private_dirs_patterns
16
16
  )
17
17
 
18
- for file in $(git diff --cached --name-only); do
18
+ for file in $(git diff --cached --name-only --diff-filter=ACMR); do
19
19
  for pattern in "${PRIVATE_PATTERNS[@]}"; do
20
20
  if echo "$file" | grep -q -E "$pattern"; then
21
21
  echo "ERROR: Attempting to commit private content: $file"
@@ -239,9 +239,10 @@ class RepoManager:
239
239
  # Create local repository directory
240
240
  os.makedirs(self.repo_root, exist_ok=True)
241
241
 
242
- # Create github directory for worktree (legacy layout only)
243
- if self.github_root:
244
- os.makedirs(self.github_root, exist_ok=True)
242
+ # No <name>/github is made ahead of time (0.9.12): `git worktree add`
243
+ # creates the folder when main is checked out there, and a strategy
244
+ # with no worktree for main (minimal), or one that checks main out
245
+ # elsewhere (--branch-dir-main), must not get a stray github/ folder.
245
246
 
246
247
  # Create standard subdirectories
247
248
  standard_dirs = self.config.get(
@@ -1070,6 +1071,29 @@ class RepoManager:
1070
1071
  else:
1071
1072
  self.logger.info(f"Linked {path}/private to the private repository")
1072
1073
 
1074
+ def _worktree_of(self, branch: str) -> Optional[str]:
1075
+ """The folder where `branch` is checked out in a worktree other than
1076
+ repo_root itself, from `git worktree list --porcelain`; None when it
1077
+ has none (0.9.12)."""
1078
+ out = self.run_git(["worktree", "list", "--porcelain"], cwd=self.repo_root, check=False) or ""
1079
+ here = os.path.normcase(os.path.abspath(self.repo_root))
1080
+ path = None
1081
+ for line in out.splitlines():
1082
+ if line.startswith("worktree "):
1083
+ path = line[len("worktree "):].strip()
1084
+ elif line.strip() == f"branch refs/heads/{branch}" and path:
1085
+ if os.path.normcase(os.path.abspath(path)) != here:
1086
+ return path
1087
+ return None
1088
+
1089
+ @staticmethod
1090
+ def _branch_exists_in(repo: str, name: str) -> bool:
1091
+ """Whether a local branch named exactly `name` exists in `repo`
1092
+ (0.9.12: the old check matched by substring, so `dev` found `develop`)."""
1093
+ result = subprocess.run(["git", "show-ref", "--verify", "--quiet", f"refs/heads/{name}"],
1094
+ cwd=repo, capture_output=True, stdin=subprocess.DEVNULL)
1095
+ return result.returncode == 0
1096
+
1073
1097
  def _setup_github_integration(self) -> None:
1074
1098
  """Set up GitHub integration."""
1075
1099
  self.logger.info("Setting up GitHub integration")
@@ -1084,6 +1108,19 @@ class RepoManager:
1084
1108
  if not self.github_root:
1085
1109
  return
1086
1110
 
1111
+ # Run git only in the default branch's real worktree (0.9.12). The
1112
+ # folder is where `git worktree list` says the branch is checked out,
1113
+ # not an assumed <name>/github: with no worktree there, `git add` and
1114
+ # `git commit` walked up into whatever repository enclosed the
1115
+ # project and committed in it. No worktree for the default branch
1116
+ # means nothing to do -- the branch already carries .github, cut from
1117
+ # the private branch.
1118
+ main_worktree = self._worktree_of(self.config.get("default_branch", "main"))
1119
+ if not main_worktree:
1120
+ self.logger.info("No worktree for the default branch; its .github came with the branch")
1121
+ return
1122
+ self.github_root = main_worktree
1123
+
1087
1124
  # Only proceed if the GitHub worktree exists
1088
1125
  if not os.path.exists(self.github_root):
1089
1126
  self.logger.warning(
@@ -1222,7 +1259,7 @@ class RepoManager:
1222
1259
  # Pre-commit hook to prevent committing private content
1223
1260
 
1224
1261
  # Check for private directories
1225
- for file in $(git diff --cached --name-only); do
1262
+ for file in $(git diff --cached --name-only --diff-filter=ACMR); do
1226
1263
  if echo "$file" | grep -q -E "^private/|^convos/|^logs/|__private__"; then
1227
1264
  echo "ERROR: Attempting to commit private content: $file"
1228
1265
  echo "Private content should not be committed to the repository."
@@ -1427,6 +1464,10 @@ exit 0
1427
1464
  )
1428
1465
  private_branch = self.private_branch
1429
1466
 
1467
+ # A public branch that cannot be built, or fails verification, fails
1468
+ # the create (0.9.12): it used to be a warning and the create exited 0
1469
+ # with every public branch holding only the initial commit.
1470
+ failed = []
1430
1471
  for branch in branches:
1431
1472
  if branch != private_branch: # Skip private branch
1432
1473
  try:
@@ -1447,17 +1488,21 @@ exit 0
1447
1488
  self.logger.info(f"Successfully created and verified clean public branch '{branch}'")
1448
1489
  else:
1449
1490
  self.logger.error(f"Branch '{branch}' failed verification - contains private content!")
1450
-
1491
+ failed.append(branch)
1492
+
1451
1493
  if self.verbose >= 2:
1452
1494
  approach = "selective" if is_adoption else "standard"
1453
1495
  self.logger.debug(
1454
1496
  f"Created clean public branch '{branch}' using {approach} approach"
1455
1497
  )
1456
1498
  except Exception as e:
1457
- self.logger.warning(f"Failed to create clean public branch '{branch}': {str(e)}")
1499
+ self.logger.error(f"Failed to create clean public branch '{branch}': {str(e)}")
1500
+ failed.append(branch)
1458
1501
 
1459
1502
  # Switch back to private branch for continued development
1460
1503
  self.run_git(["checkout", private_branch], cwd=self.repo_root)
1504
+ if failed:
1505
+ raise RuntimeError(f"public branches not built: {', '.join(failed)}")
1461
1506
 
1462
1507
  def _get_working_directory_files(self) -> List[str]:
1463
1508
  """
@@ -1575,8 +1620,7 @@ exit 0
1575
1620
  current_branch = self.run_git(["rev-parse", "--abbrev-ref", "HEAD"], cwd=self.repo_root).strip()
1576
1621
 
1577
1622
  # Check if branch already exists
1578
- existing_branches = self.run_git(["branch", "--list"], cwd=self.repo_root).splitlines()
1579
- branch_exists = any(branch_name in branch.strip() for branch in existing_branches)
1623
+ branch_exists = self._branch_exists_in(self.repo_root, branch_name) # exact name (0.9.12)
1580
1624
 
1581
1625
  if branch_exists:
1582
1626
  self.logger.info(f"Branch '{branch_name}' already exists, will recreate with selective approach")
@@ -1660,17 +1704,22 @@ exit 0
1660
1704
  current_branch = self.run_git(["rev-parse", "--abbrev-ref", "HEAD"], cwd=self.repo_root).strip()
1661
1705
 
1662
1706
  # Check if branch already exists
1663
- existing_branches = self.run_git(["branch", "--list"], cwd=self.repo_root).splitlines()
1664
- branch_exists = any(branch_name in branch.strip() for branch in existing_branches)
1707
+ branch_exists = self._branch_exists_in(self.repo_root, branch_name) # exact name (0.9.12)
1665
1708
 
1709
+ # The source is always the BRANCH, never a folder of the same name:
1710
+ # a private/ folder sits beside the private branch, and a bare
1711
+ # `git reset --hard private` is refused as ambiguous (0.9.12;
1712
+ # djdarcy: "The only private we care about in the private-public
1713
+ # branch strategy is the "private" branch as the source").
1714
+ source_ref = f"refs/heads/{source_branch}"
1666
1715
  if branch_exists:
1667
1716
  # Branch exists, check it out and reset it
1668
1717
  self.logger.info(f"Branch '{branch_name}' already exists, resetting it")
1669
1718
  self.run_git(["checkout", branch_name], cwd=self.repo_root)
1670
- self.run_git(["reset", "--hard", source_branch], cwd=self.repo_root)
1719
+ self.run_git(["reset", "--hard", source_ref], cwd=self.repo_root)
1671
1720
  else:
1672
1721
  # Create the new branch from the source branch
1673
- self.run_git(["checkout", "-b", branch_name, source_branch], cwd=self.repo_root)
1722
+ self.run_git(["checkout", "-b", branch_name, source_ref], cwd=self.repo_root)
1674
1723
 
1675
1724
  # Use existing BranchContext with configuration to identify private content
1676
1725
  context = BranchContext(self.repo_root, self._get_branch_config())
@@ -2059,8 +2108,8 @@ exit 0
2059
2108
  else:
2060
2109
  # For branches not in worktrees, we can update them directly
2061
2110
  self.run_git(["checkout", branch], cwd=self.repo_root)
2062
- self.run_git(
2063
- ["reset", "--hard", private_branch], cwd=self.repo_root
2111
+ self.run_git( # the branch, never a folder of the same name (0.9.12)
2112
+ ["reset", "--hard", f"refs/heads/{private_branch}"], cwd=self.repo_root
2064
2113
  )
2065
2114
  self.logger.info(
2066
2115
  f"Updated branch '{branch}' from {private_branch}"
@@ -2366,12 +2415,14 @@ exit 0
2366
2415
 
2367
2416
  def _cleanup_sensitive_files(self, branch_context: Optional[str] = None, dry_run: bool = False) -> Dict[str, List[str]]:
2368
2417
  """
2369
- Branch-aware cleanup of sensitive files during repository adoption.
2370
-
2371
- This method performs intelligent cleanup based on branch context:
2372
- - For private branches: Only clean git index, preserve working directory
2373
- - For public branches: Clean both working directory and git index
2374
-
2418
+ Take sensitive files out of a branch's git index, leaving them on disk.
2419
+
2420
+ Called while a public branch is cut. It removes tracked files matching
2421
+ the sensitive patterns from the index (`git rm --cached`) and never
2422
+ deletes anything from the working folder, which in the private-branch
2423
+ model is the private checkout (0.9.12). The 'working_dir' list in the
2424
+ result is always empty; it is kept for callers that read it.
2425
+
2375
2426
  Args:
2376
2427
  branch_context: Branch we're cleaning for (e.g. 'private', 'dev', 'main')
2377
2428
  dry_run: If True, only report what would be cleaned
@@ -2411,65 +2462,18 @@ exit 0
2411
2462
  cleanup_logger.info(f"Is private branch: {is_private_branch}")
2412
2463
  cleanup_logger.info(f"Using {len(patterns_to_clean)} sensitive patterns: {patterns_to_clean}")
2413
2464
 
2414
- # For private branches, we preserve working directory files but clean git index
2415
- # For public branches, we clean both working directory and git index
2416
- clean_working_dir = not is_private_branch
2465
+ # The working folder is never cleaned, for any branch (0.9.12). In the
2466
+ # private-branch model a public branch is cut inside repo_root, which is
2467
+ # the PRIVATE checkout (<name>/local), so deleting matching files from
2468
+ # disk removed a person's untracked .env, logs and backups from their
2469
+ # own working folder. It never ran before 0.9.12 only because the reset
2470
+ # before it always failed. Taking files out of the public branch is the
2471
+ # index clean below, which leaves them on disk.
2472
+ clean_working_dir = False
2417
2473
  clean_git_index = True # Always clean git index
2418
-
2419
- cleanup_logger.info(f"Will clean working directory: {clean_working_dir}")
2420
- cleanup_logger.info(f"Will clean git index: {clean_git_index}")
2421
-
2422
- # Clean working directory files (only for public branches)
2423
- if clean_working_dir:
2424
- cleanup_logger.debug("Scanning working directory for sensitive files")
2425
- for root, dirs, files in os.walk(self.repo_root):
2426
- # Skip .git directory
2427
- if ".git" in root.split(os.sep):
2428
- continue
2429
-
2430
- for file in files:
2431
- file_path = os.path.join(root, file)
2432
- relative_path = os.path.relpath(file_path, self.repo_root)
2433
- if self._is_vendored(relative_path):
2434
- continue
2435
2474
 
2436
- # Check if file matches any sensitive pattern
2437
- should_remove = False
2438
- matching_pattern = None
2439
-
2440
- for pattern in patterns_to_clean:
2441
- # Cross-platform pattern matching using normalized paths
2442
- normalized_relative = str(Path(relative_path)).replace('\\', '/')
2443
- normalized_pattern = pattern.replace('\\', '/')
2444
-
2445
- # Check multiple match approaches for cross-platform compatibility
2446
- matches = [
2447
- fnmatch.fnmatch(normalized_relative, normalized_pattern), # Full path
2448
- fnmatch.fnmatch(file, normalized_pattern), # Filename only
2449
- fnmatch.fnmatch(str(Path(file)), normalized_pattern), # Filename as Path
2450
- ]
2451
-
2452
- if any(matches):
2453
- should_remove = True
2454
- matching_pattern = pattern
2455
- if self.verbose >= 3:
2456
- cleanup_logger.debug(f"Pattern '{pattern}' matched file '{relative_path}' (normalized: '{normalized_relative}')")
2457
- break
2458
-
2459
- if should_remove:
2460
- cleanup_results["working_dir"].append(relative_path)
2461
-
2462
- if dry_run:
2463
- cleanup_logger.info(f"Would remove (working dir): {relative_path} (pattern: {matching_pattern})")
2464
- else:
2465
- try:
2466
- os.remove(file_path)
2467
- cleanup_logger.debug(f"Removed (working dir): {relative_path} (pattern: {matching_pattern})")
2468
- except Exception as e:
2469
- cleanup_logger.warning(f"Failed to remove {relative_path}: {str(e)}")
2470
- else:
2471
- cleanup_logger.info("Skipping working directory cleanup (private branch - files preserved)")
2472
-
2475
+ cleanup_logger.info(f"Will clean git index: {clean_git_index} (the working folder is never touched)")
2476
+
2473
2477
  # Clean git index files (always done)
2474
2478
  if clean_git_index:
2475
2479
  cleanup_logger.debug("Scanning git index for sensitive files")
@@ -23,7 +23,7 @@ esac
23
23
 
24
24
  # Check for private content using BranchContext patterns
25
25
  has_private_content=0
26
- for file in $(git diff --cached --name-only); do
26
+ for file in $(git diff --cached --name-only --diff-filter=ACMR); do
27
27
  # Check against all private content patterns from BranchContext
28
28
  if echo "$file" | grep -q -E "$private_content_regex"; then
29
29
  if [ $has_private_content -eq 0 ]; then
@@ -50,7 +50,7 @@ if [ $has_private_content -eq 1 ]; then
50
50
  fi
51
51
 
52
52
  # Check for large files (>10MB)
53
- for file in $(git diff --cached --name-only); do
53
+ for file in $(git diff --cached --name-only --diff-filter=ACMR); do
54
54
  if [ -f "$file" ]; then
55
55
  size=$(du -k "$file" | awk '{print $1}')
56
56
  if [ "$size" -gt 10240 ]; then
@@ -504,7 +504,7 @@ def test_write_settings_merges_missing_keys_and_never_overwrites(tmp_path):
504
504
  assert common_kit.write_settings(str(tmp_path), {"private-patterns": ["CLAUDE.md"], "languages": ["NOT-THIS"]}) == ".repokit-common.toml"
505
505
  assert f.read_bytes() == (b'[tool.repokit-common]\r\nprivate-patterns = ["CLAUDE.md"]\r\n'
506
506
  b'version-source = "include/x/version.h"\r\nlanguages = ["c-cpp"]\r\n')
507
- assert common_kit.write_settings(str(tmp_path), {"private-patterns": ["other"]}) is None # present: untouched
507
+ assert common_kit.write_settings(str(tmp_path), {"languages": ["other"]}) is None # present: untouched
508
508
  py = tmp_path / "pyproject.toml"
509
509
  py.write_text('[project]\nname = "p"\n\n[tool.repokit-common]\nversion-source = "p/_version.py"\n\n[tool.other]\nx = 1\n')
510
510
  assert common_kit.write_settings(str(tmp_path), {"private-branches": ["private", "scratch"]}) == "pyproject.toml"
@@ -512,6 +512,44 @@ def test_write_settings_merges_missing_keys_and_never_overwrites(tmp_path):
512
512
  'version-source = "p/_version.py"\n\n[tool.other]\nx = 1\n')
513
513
 
514
514
 
515
+ def test_write_settings_adds_the_private_patterns_an_existing_list_lacks(tmp_path):
516
+ """0.9.12: a table that already has private-patterns (the pypi layer's
517
+ pyproject.toml ships one) keeps its entries and gains the ones the
518
+ private-branch model needs -- skipping the key dropped CLAUDE.md from
519
+ the guard on public branches. Other keys are still never overwritten."""
520
+ py = tmp_path / "pyproject.toml"
521
+ py.write_text('[tool.repokit-common]\nversion-source = "p/_version.py"\n'
522
+ 'private-patterns = ["private/", "local/", ".env"]\n\n[tool.other]\nx = ["CLAUDE.md"]\n')
523
+ settings = {"private-patterns": ["CLAUDE.md", "private/", "convos/"], "version-source": ["NOT-THIS"]}
524
+ assert common_kit.write_settings(str(tmp_path), settings) == "pyproject.toml"
525
+ assert py.read_text() == ('[tool.repokit-common]\nversion-source = "p/_version.py"\n'
526
+ 'private-patterns = ["private/", "local/", ".env", "CLAUDE.md", "convos/"]\n\n'
527
+ '[tool.other]\nx = ["CLAUDE.md"]\n')
528
+ assert common_kit.write_settings(str(tmp_path), settings) is None # nothing missing any more
529
+
530
+ toml = tmp_path / "sub"
531
+ toml.mkdir()
532
+ f = toml / ".repokit-common.toml"
533
+ f.write_bytes(b"[tool.repokit-common]\r\nprivate-patterns = [\r\n 'private/',\r\n]\r\n")
534
+ assert common_kit.write_settings(str(toml), {"private-patterns": ["CLAUDE.md"]}) == ".repokit-common.toml"
535
+ assert f.read_bytes() == b"[tool.repokit-common]\r\nprivate-patterns = [\r\n 'private/', \"CLAUDE.md\"\r\n]\r\n"
536
+
537
+
538
+ def test_write_settings_union_stays_in_its_own_list_and_table(tmp_path):
539
+ """0.9.12: the union reads single-quoted entries as present, adds only to
540
+ the private-patterns list (not to a list after it), and the table ends at
541
+ the next header -- a key of the same name in a later table neither counts
542
+ as present nor receives anything. Found by the 0.9.12 mutation sweep."""
543
+ py = tmp_path / "pyproject.toml"
544
+ py.write_text("[tool.repokit-common]\nprivate-patterns = ['private/', \"local/\"]\nlanguages = [\"python\"]\n\n"
545
+ "[tool.other]\nprivate-branches = [\"x\"]\n")
546
+ settings = {"private-patterns": ["private/", "CLAUDE.md"], "private-branches": ["private", "scratch"]}
547
+ assert common_kit.write_settings(str(tmp_path), settings) == "pyproject.toml"
548
+ assert py.read_text() == ("[tool.repokit-common]\nprivate-branches = [\"private\", \"scratch\"]\n"
549
+ "private-patterns = ['private/', \"local/\", \"CLAUDE.md\"]\nlanguages = [\"python\"]\n\n"
550
+ "[tool.other]\nprivate-branches = [\"x\"]\n")
551
+
552
+
515
553
  # --- U2.3: packaging ----------------------------------------------------------
516
554
 
517
555
  @pytest.mark.integration
@@ -114,6 +114,17 @@ class TestPrivateBranchModelWithCommon(RepoKitTestCase):
114
114
  allowed = commit_file(local, "CLAUDE.md")
115
115
  self.assertEqual(allowed.returncode, 0, allowed.stdout + allowed.stderr)
116
116
 
117
+ def test_claude_md_refused_on_main_of_a_python_project(self):
118
+ """0.9.12: a Python project's pyproject.toml ships its own private-patterns;
119
+ the private-branch entries must be added to that list, or CLAUDE.md
120
+ reaches main (committed from the github worktree)."""
121
+ self.assert_repokit_success(["create", "demo", "--branch-strategy", "simple", "--with-common",
122
+ "--language", "python", "--no-push"])
123
+ github = os.path.join(self.test_dir, "demo", "github")
124
+ self.assertIn('"CLAUDE.md"', Path(github, "pyproject.toml").read_text(encoding="utf-8"))
125
+ refused = commit_file(github, "CLAUDE.md")
126
+ self.assertNotEqual(refused.returncode, 0, refused.stdout + refused.stderr)
127
+
117
128
  def test_a_custom_private_branch_is_written_to_the_settings(self):
118
129
  self.assert_repokit_success(["create", "demo", "--branch-strategy", "simple",
119
130
  "--private-branch", "scratch", "--with-common", "--no-push"])