git-repokit 0.9.10__py3-none-any.whl

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 (174) hide show
  1. git_repokit-0.9.10.dist-info/METADATA +422 -0
  2. git_repokit-0.9.10.dist-info/RECORD +174 -0
  3. git_repokit-0.9.10.dist-info/WHEEL +5 -0
  4. git_repokit-0.9.10.dist-info/entry_points.txt +2 -0
  5. git_repokit-0.9.10.dist-info/licenses/LICENSE +9 -0
  6. git_repokit-0.9.10.dist-info/top_level.txt +1 -0
  7. repokit/__init__.py +8 -0
  8. repokit/__main__.py +12 -0
  9. repokit/__version__.py +4 -0
  10. repokit/_vendor/__init__.py +0 -0
  11. repokit/_vendor/ailib/__init__.py +1 -0
  12. repokit/_vendor/ailib/analyzer.py +177 -0
  13. repokit/_vendor/ailib/backend.py +95 -0
  14. repokit/_vendor/ailib/cache.py +97 -0
  15. repokit/_vendor/ailib/parsers.py +52 -0
  16. repokit/_vendor/ailib/transports/__init__.py +16 -0
  17. repokit/_vendor/ailib/transports/cli.py +312 -0
  18. repokit/_vendor/ailib/transports/openai_compat.py +383 -0
  19. repokit/_vendor/ailib/transports/prompt_file.py +41 -0
  20. repokit/_vendor/ailib/types.py +224 -0
  21. repokit/auth_integration.py +244 -0
  22. repokit/badges.py +183 -0
  23. repokit/branch_utils.py +364 -0
  24. repokit/cli.py +2997 -0
  25. repokit/common_kit.py +202 -0
  26. repokit/config.py +686 -0
  27. repokit/defaults.py +220 -0
  28. repokit/directory_analyzer.py +1244 -0
  29. repokit/directory_profiles.py +423 -0
  30. repokit/git_config.py +370 -0
  31. repokit/github_create.py +142 -0
  32. repokit/github_setup.py +248 -0
  33. repokit/guardrails.py +707 -0
  34. repokit/history_cleaner.py +449 -0
  35. repokit/history_protection.py +434 -0
  36. repokit/hooks/pre-commit-guardrails +122 -0
  37. repokit/hooks/pre-commit-template +28 -0
  38. repokit/layers.py +510 -0
  39. repokit/licenses/0bsd.txt +14 -0
  40. repokit/licenses/LICENSES.md +27 -0
  41. repokit/licenses/__init__.py +142 -0
  42. repokit/licenses/agpl-3.0.txt +661 -0
  43. repokit/licenses/all-rights-reserved.txt +10 -0
  44. repokit/licenses/apache-2.0.txt +201 -0
  45. repokit/licenses/bsd-2-clause.txt +24 -0
  46. repokit/licenses/bsd-3-clause.txt +28 -0
  47. repokit/licenses/bsl-1.0.txt +23 -0
  48. repokit/licenses/cc-by-4.0.txt +395 -0
  49. repokit/licenses/cc-by-sa-4.0.txt +427 -0
  50. repokit/licenses/cc0-1.0.txt +121 -0
  51. repokit/licenses/epl-2.0.txt +277 -0
  52. repokit/licenses/gpl-2.0.txt +339 -0
  53. repokit/licenses/gpl-3.0.txt +674 -0
  54. repokit/licenses/isc.txt +15 -0
  55. repokit/licenses/lgpl-2.1.txt +504 -0
  56. repokit/licenses/lgpl-3.0.txt +165 -0
  57. repokit/licenses/mit.txt +21 -0
  58. repokit/licenses/mpl-2.0.txt +373 -0
  59. repokit/licenses/unlicense.txt +24 -0
  60. repokit/readme_blocks.py +112 -0
  61. repokit/remote_integration.py +627 -0
  62. repokit/repo_manager.py +2796 -0
  63. repokit/template_engine.py +391 -0
  64. repokit/templates/ai/claude/CLAUDE-nested.md.template +25 -0
  65. repokit/templates/ai/claude/CLAUDE.md.template +98 -0
  66. repokit/templates/ai/claude/instructions/step1_context_rebuilder.md +33 -0
  67. repokit/templates/ai/claude/instructions/step2_dev_workflow_process.md +67 -0
  68. repokit/templates/ai/claude/instructions/step3_context_bridge.md +52 -0
  69. repokit/templates/common/CHANGELOG.md.template +15 -0
  70. repokit/templates/common/CONTRIBUTING.md.template +31 -0
  71. repokit/templates/common/README.md.template +30 -0
  72. repokit/templates/common/gitattributes.template +17 -0
  73. repokit/templates/common/gitignore.template +141 -0
  74. repokit/templates/extras/FUNDING.yml.template +13 -0
  75. repokit/templates/extras/ROADMAP.md.template +17 -0
  76. repokit/templates/extras/dependabot.yml.template +22 -0
  77. repokit/templates/extras/one-offs-README.md.template +28 -0
  78. repokit/templates/extras/platform-support.md.template +11 -0
  79. repokit/templates/extras/pull_request_template.md.template +25 -0
  80. repokit/templates/extras/release-template.md.template +34 -0
  81. repokit/templates/extras/settings.json.template +11 -0
  82. repokit/templates/extras/stale.yml.template +14 -0
  83. repokit/templates/github/CODEOWNERS.template +11 -0
  84. repokit/templates/github/ISSUE_TEMPLATE/bug-report.md.template +29 -0
  85. repokit/templates/github/ISSUE_TEMPLATE/feature-request.md.template +19 -0
  86. repokit/templates/github/issues/notes.md.template +18 -0
  87. repokit/templates/github/issues/roadmap.md.template +15 -0
  88. repokit/templates/hooks/pre-commit.template +64 -0
  89. repokit/templates/layers/archetypes/any/claude-plugin/.claude-plugin/marketplace.json +20 -0
  90. repokit/templates/layers/archetypes/any/claude-plugin/plugin/.claude-plugin/plugin.json +15 -0
  91. repokit/templates/layers/archetypes/any/claude-plugin/plugin/README.md +28 -0
  92. repokit/templates/layers/archetypes/c-cpp/cmake-app/.github/workflows/ci-c-cpp.yml +24 -0
  93. repokit/templates/layers/archetypes/c-cpp/cmake-app/.repokit-common.toml.rkappend +4 -0
  94. repokit/templates/layers/archetypes/c-cpp/cmake-app/.vscode/launch.json +29 -0
  95. repokit/templates/layers/archetypes/c-cpp/cmake-app/.vscode/tasks.json +26 -0
  96. repokit/templates/layers/archetypes/c-cpp/cmake-app/CMakeLists.txt +47 -0
  97. repokit/templates/layers/archetypes/c-cpp/cmake-app/CMakePresets.json +23 -0
  98. repokit/templates/layers/archetypes/c-cpp/cmake-app/cmake/GenerateStamp.cmake +45 -0
  99. repokit/templates/layers/archetypes/c-cpp/cmake-app/src/main.cpp +27 -0
  100. repokit/templates/layers/archetypes/c-cpp/cmake-app/tests/test_version.cpp +20 -0
  101. repokit/templates/layers/archetypes/c-cpp/cmake-app/vcpkg.json +5 -0
  102. repokit/templates/layers/archetypes/c-cpp/minimal/.vscode/launch.json +27 -0
  103. repokit/templates/layers/archetypes/c-cpp/minimal/Makefile +10 -0
  104. repokit/templates/layers/archetypes/c-cpp/minimal/build.bat +7 -0
  105. repokit/templates/layers/archetypes/c-cpp/minimal/src/__RK_SNAKE__.c +26 -0
  106. repokit/templates/layers/archetypes/c-cpp/msbuild/.vscode/launch.json +17 -0
  107. repokit/templates/layers/archetypes/c-cpp/msbuild/.vscode/tasks.json +20 -0
  108. repokit/templates/layers/archetypes/c-cpp/msbuild/__RK_PROJECT__.sln +19 -0
  109. repokit/templates/layers/archetypes/c-cpp/msbuild/__RK_PROJECT__.vcxproj +41 -0
  110. repokit/templates/layers/archetypes/c-cpp/msbuild/src/main.cpp +26 -0
  111. repokit/templates/layers/archetypes/js-ts/claude-plugin/.repokit-common.toml +28 -0
  112. repokit/templates/layers/archetypes/js-ts/claude-plugin/.vscode/launch.json +34 -0
  113. repokit/templates/layers/archetypes/js-ts/claude-plugin/plugin/hooks/hooks.json +15 -0
  114. repokit/templates/layers/archetypes/js-ts/claude-plugin/plugin/hooks/scripts/session-start.mjs +23 -0
  115. repokit/templates/layers/archetypes/js-ts/claude-plugin/plugin/tests/manifest.test.js +30 -0
  116. repokit/templates/layers/archetypes/js-ts/node/.repokit-common.toml.rkappend +6 -0
  117. repokit/templates/layers/archetypes/js-ts/node/package.json +13 -0
  118. repokit/templates/layers/archetypes/js-ts/node-package/.github/workflows/release.yml +39 -0
  119. repokit/templates/layers/archetypes/js-ts/node-package/package.json +32 -0
  120. repokit/templates/layers/archetypes/js-ts/node-package/src/index.ts +13 -0
  121. repokit/templates/layers/archetypes/js-ts/node-package/tests/index.test.ts +11 -0
  122. repokit/templates/layers/archetypes/js-ts/script/.vscode/launch.json +34 -0
  123. repokit/templates/layers/archetypes/js-ts/script/__RK_PROJECT__.js +37 -0
  124. repokit/templates/layers/archetypes/js-ts/script/tests/version.test.js +26 -0
  125. repokit/templates/layers/archetypes/js-ts/ts/package.json +18 -0
  126. repokit/templates/layers/archetypes/js-ts/ts/tsconfig.json +15 -0
  127. repokit/templates/layers/archetypes/python/claude-plugin/.github/workflows/ci-python.yml +44 -0
  128. repokit/templates/layers/archetypes/python/claude-plugin/.repokit-common.toml +21 -0
  129. repokit/templates/layers/archetypes/python/claude-plugin/.vscode/launch.json +33 -0
  130. repokit/templates/layers/archetypes/python/claude-plugin/VERSION +1 -0
  131. repokit/templates/layers/archetypes/python/claude-plugin/plugin/hooks/hooks.json +15 -0
  132. repokit/templates/layers/archetypes/python/claude-plugin/plugin/hooks/scripts/run-hook.mjs +42 -0
  133. repokit/templates/layers/archetypes/python/claude-plugin/plugin/hooks/scripts/session_start.py +31 -0
  134. repokit/templates/layers/archetypes/python/claude-plugin/plugin/tests/test_manifest.py +42 -0
  135. repokit/templates/layers/archetypes/python/pypi/.github/workflows/release.yml +63 -0
  136. repokit/templates/layers/archetypes/python/pypi/.vscode/launch.json +43 -0
  137. repokit/templates/layers/archetypes/python/pypi/__RK_SNAKE__/__init__.py +5 -0
  138. repokit/templates/layers/archetypes/python/pypi/__RK_SNAKE__/__main__.py +6 -0
  139. repokit/templates/layers/archetypes/python/pypi/__RK_SNAKE__/_version.py +96 -0
  140. repokit/templates/layers/archetypes/python/pypi/__RK_SNAKE__/cli.py +18 -0
  141. repokit/templates/layers/archetypes/python/pypi/pyproject.toml +71 -0
  142. repokit/templates/layers/archetypes/python/pypi/tests/conftest.py +11 -0
  143. repokit/templates/layers/archetypes/python/pypi/tests/test_version.py +50 -0
  144. repokit/templates/layers/archetypes/python/script/.github/workflows/ci-python.yml +43 -0
  145. repokit/templates/layers/archetypes/python/script/.repokit-common.toml +14 -0
  146. repokit/templates/layers/archetypes/python/script/.vscode/launch.json +33 -0
  147. repokit/templates/layers/archetypes/python/script/VERSION +1 -0
  148. repokit/templates/layers/archetypes/python/script/__RK_PROJECT__.py +40 -0
  149. repokit/templates/layers/archetypes/python/script/tests/test_version.py +25 -0
  150. repokit/templates/layers/archetypes/python/setuppy/requirements.txt +8 -0
  151. repokit/templates/layers/archetypes/python/setuppy/setup.py +23 -0
  152. repokit/templates/layers/languages/c-cpp/.gitignore.rkappend +20 -0
  153. repokit/templates/layers/languages/c-cpp/.repokit-common.toml +7 -0
  154. repokit/templates/layers/languages/c-cpp/include/__RK_SNAKE__/version.h +20 -0
  155. repokit/templates/layers/languages/c-cpp/src/app.rc +25 -0
  156. repokit/templates/layers/languages/js-ts/.editorconfig +15 -0
  157. repokit/templates/layers/languages/js-ts/.github/workflows/ci-js-ts.yml +41 -0
  158. repokit/templates/layers/languages/js-ts/.gitignore.rkappend +13 -0
  159. repokit/templates/layers/languages/js-ts/.repokit-common.toml +19 -0
  160. repokit/templates/layers/languages/js-ts/.vscode/launch.json +24 -0
  161. repokit/templates/layers/languages/js-ts/VERSION +1 -0
  162. repokit/templates/layers/languages/python/.github/workflows/ci-python.yml +76 -0
  163. repokit/templates/layers/languages/python/.gitignore.rkappend +15 -0
  164. repokit/templates/layers/languages/python/.vscode/launch.json +33 -0
  165. repokit/templates/layers/manifest.toml +302 -0
  166. repokit/templates/vault/CLAUDE.md +29 -0
  167. repokit/templates/vault/claude/_maps/MOC-project.md +15 -0
  168. repokit/templates/vault/claude/_oracle/backlinks.md +3 -0
  169. repokit/templates/vault/claude/_oracle/concepts.md +12 -0
  170. repokit/templates/vault/claude/_oracle/manifest.md +10 -0
  171. repokit/templates/vault/claude/_todo/ACTIVE-WORK.md +18 -0
  172. repokit/templates/vault/claude/_todo/OPEN-LOOPS.md +14 -0
  173. repokit/utils.py +316 -0
  174. repokit/vault.py +248 -0
@@ -0,0 +1,364 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Branch utilities for RepoKit.
4
+
5
+ Provides branch detection and validation for private content protection.
6
+ """
7
+
8
+ import subprocess
9
+ import os
10
+ from typing import Set, List, Dict, Optional, Tuple
11
+
12
+ # Lines RepoKit writes into .git/info/exclude for either branch type. Anything
13
+ # else in the file belongs to the user (or another tool) and is kept.
14
+ _REPOKIT_EXCLUDE_LINES = {
15
+ "CLAUDE.md", "private/claude/", "private/docs/", "private/notes/", "private/temp/",
16
+ "revisions/", "test-runs/", "test_runs/",
17
+ "# AI integration files", "# Development conversation logs",
18
+ "# Private development documentation", "# Development notes and temporary files",
19
+ "# Revision history (private branch only)", "# Test artifacts", "# Test artifacts (not needed in git)",
20
+ "# Private branches can contain all content",
21
+ "# These files are tracked in 'private' branch but ignored in public branches",
22
+ }
23
+
24
+
25
+ def _write_excludes(exclude_file: str, excludes: List[str]) -> None:
26
+ """Write RepoKit's branch excludes, keeping every line that is not RepoKit's.
27
+
28
+ The file used to be overwritten, which silently dropped the user's own
29
+ excludes (and git's template comments) each time branch excludes were set up.
30
+ """
31
+ kept: List[str] = []
32
+ if os.path.exists(exclude_file):
33
+ with open(exclude_file, encoding="utf-8", errors="replace") as f:
34
+ for line in f.read().splitlines():
35
+ stripped = line.strip()
36
+ if stripped.startswith("# RepoKit:") or stripped in _REPOKIT_EXCLUDE_LINES:
37
+ continue
38
+ kept.append(line)
39
+ while kept and not kept[-1].strip():
40
+ kept.pop()
41
+ while kept and not kept[0].strip():
42
+ kept.pop(0)
43
+ with open(exclude_file, "w", encoding="utf-8") as f:
44
+ f.write("\n".join(kept + ([""] if kept else []) + excludes) + "\n")
45
+
46
+
47
+ class BranchContext:
48
+ """Manages branch context and security rules with configurable patterns."""
49
+
50
+ def __init__(self, repo_path: str = '.', config: Optional[Dict] = None):
51
+ """
52
+ Initialize branch context for given repository.
53
+
54
+ Args:
55
+ repo_path: Path to the repository
56
+ config: Optional configuration dictionary to override default patterns
57
+ """
58
+ self.repo_path = os.path.abspath(repo_path)
59
+ self._current_branch = None
60
+ self._branch_type = None
61
+
62
+ # Load configuration or use defaults
63
+ config = config or {}
64
+
65
+ # Configure private branches (can contain private content)
66
+ self.PRIVATE_BRANCHES = set(config.get('private_branches', ['private', 'local']))
67
+
68
+ # Configure public branches (must not contain private content)
69
+ self.PUBLIC_BRANCHES = set(config.get('public_branches',
70
+ ['main', 'master', 'dev', 'test', 'staging', 'live', 'prod', 'production']))
71
+
72
+ # Configure private content patterns
73
+ self.PRIVATE_PATTERNS = set(config.get('private_patterns', {
74
+ 'CLAUDE.md',
75
+ 'private/',
76
+ 'convos/',
77
+ 'logs/',
78
+ 'credentials/',
79
+ 'secrets/',
80
+ '.env.local',
81
+ '.env.private',
82
+ '**/__private__*',
83
+ '**/private_*',
84
+ }))
85
+
86
+ # Configure exclude patterns for public branches
87
+ self.EXCLUDE_FROM_PUBLIC = set(config.get('exclude_from_public', {
88
+ 'CLAUDE.md',
89
+ 'private/claude/',
90
+ 'private/docs/',
91
+ 'private/notes/',
92
+ 'private/temp/',
93
+ 'revisions/',
94
+ 'test-runs/',
95
+ 'test_runs/',
96
+ }))
97
+
98
+ @property
99
+ def current_branch(self) -> str:
100
+ """Get current branch name."""
101
+ if self._current_branch is None:
102
+ self._current_branch = self._get_current_branch()
103
+ return self._current_branch
104
+
105
+ @property
106
+ def branch_type(self) -> str:
107
+ """Get branch type: 'private', 'public', or 'unknown'."""
108
+ if self._branch_type is None:
109
+ self._branch_type = self._determine_branch_type()
110
+ return self._branch_type
111
+
112
+ def _get_current_branch(self) -> str:
113
+ """Get current git branch name."""
114
+ try:
115
+ result = subprocess.run(
116
+ ['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
117
+ cwd=self.repo_path,
118
+ capture_output=True,
119
+ text=True,
120
+ check=True
121
+ )
122
+ return result.stdout.strip()
123
+ except subprocess.CalledProcessError:
124
+ return 'unknown'
125
+
126
+ def _determine_branch_type(self) -> str:
127
+ """Determine if current branch is private, public, or unknown."""
128
+ branch = self.current_branch.lower()
129
+
130
+ # Check explicit private branches
131
+ if branch in self.PRIVATE_BRANCHES:
132
+ return 'private'
133
+
134
+ # Check explicit public branches
135
+ if branch in self.PUBLIC_BRANCHES:
136
+ return 'public'
137
+
138
+ # Check patterns
139
+ if branch.startswith('private/') or branch.startswith('local/'):
140
+ return 'private'
141
+
142
+ if branch.startswith('feature/') or branch.startswith('feat/'):
143
+ return 'private' # Feature branches can have private content
144
+
145
+ # Default to public for safety
146
+ return 'public'
147
+
148
+ def is_private_branch(self) -> bool:
149
+ """Check if current branch allows private content."""
150
+ return self.branch_type == 'private'
151
+
152
+ def is_public_branch(self) -> bool:
153
+ """Check if current branch must not have private content."""
154
+ return self.branch_type == 'public'
155
+
156
+ def get_staged_files(self) -> List[str]:
157
+ """Get list of files staged for commit."""
158
+ try:
159
+ result = subprocess.run(
160
+ ['git', 'diff', '--cached', '--name-only'],
161
+ cwd=self.repo_path,
162
+ capture_output=True,
163
+ text=True,
164
+ check=True
165
+ )
166
+ return [f.strip() for f in result.stdout.strip().split('\n') if f.strip()]
167
+ except subprocess.CalledProcessError:
168
+ return []
169
+
170
+ def check_private_files(self, files: List[str]) -> List[Tuple[str, str]]:
171
+ """
172
+ Check if files contain private content.
173
+
174
+ Returns list of (file, reason) tuples for files that are private.
175
+ """
176
+ private_files = []
177
+
178
+ for file in files:
179
+ # Check exact matches
180
+ if file in self.PRIVATE_PATTERNS:
181
+ private_files.append((file, "Exact match to private pattern"))
182
+ continue
183
+
184
+ # Check directory prefixes
185
+ for pattern in self.PRIVATE_PATTERNS:
186
+ if pattern.endswith('/') and file.startswith(pattern):
187
+ private_files.append((file, f"Inside private directory: {pattern}"))
188
+ break
189
+
190
+ # Check file patterns
191
+ if 'private' in file.lower() or '__private__' in file:
192
+ private_files.append((file, "Contains 'private' in path"))
193
+
194
+ # Check specific files that should be excluded
195
+ for exclude in self.EXCLUDE_FROM_PUBLIC:
196
+ if file == exclude or file.startswith(exclude):
197
+ private_files.append((file, f"Matches exclude pattern: {exclude}"))
198
+ break
199
+
200
+ return private_files
201
+
202
+ def validate_commit(self) -> Tuple[bool, List[str]]:
203
+ """
204
+ Validate if current commit is safe for the branch.
205
+
206
+ Returns (is_valid, error_messages).
207
+ """
208
+ # Check for override mechanisms
209
+ if self._check_override():
210
+ return True, []
211
+
212
+ if self.is_private_branch():
213
+ # Private branches can have any content
214
+ return True, []
215
+
216
+ # Public branch - check for private content
217
+ staged_files = self.get_staged_files()
218
+ private_files = self.check_private_files(staged_files)
219
+
220
+ if private_files:
221
+ errors = [
222
+ f"ERROR: Attempting to commit private files to public branch '{self.current_branch}':",
223
+ ""
224
+ ]
225
+ for file, reason in private_files:
226
+ errors.append(f" - {file}: {reason}")
227
+
228
+ errors.extend([
229
+ "",
230
+ "These files should only exist in private branches.",
231
+ "To fix this:",
232
+ " 1. Switch to private branch: git checkout private",
233
+ " 2. Or unstage these files: git reset HEAD <file>",
234
+ " 3. Or remove from working directory: rm <file>",
235
+ " 4. Or override with: REPOKIT_OVERRIDE=true git commit ...",
236
+ " 5. Or add '[override]' to commit message",
237
+ ])
238
+ return False, errors
239
+
240
+ return True, []
241
+
242
+ def _check_override(self) -> bool:
243
+ """Check if guardrails should be overridden."""
244
+ # Environment variable override
245
+ if os.environ.get('REPOKIT_OVERRIDE', '').lower() in ('true', '1', 'yes'):
246
+ print("🔓 REPOKIT_OVERRIDE detected - bypassing guardrails")
247
+ return True
248
+
249
+ # Check commit message for override flag
250
+ try:
251
+ result = subprocess.run(
252
+ ['git', 'log', '--format=%B', '-n', '1'],
253
+ cwd=self.repo_path,
254
+ capture_output=True,
255
+ text=True,
256
+ check=False
257
+ )
258
+
259
+ # Also check if we're in the middle of a commit (check COMMIT_EDITMSG)
260
+ commit_msg_file = os.path.join(self.repo_path, '.git', 'COMMIT_EDITMSG')
261
+ commit_message = ""
262
+
263
+ if os.path.exists(commit_msg_file):
264
+ with open(commit_msg_file, 'r') as f:
265
+ commit_message = f.read().strip()
266
+ elif result.returncode == 0:
267
+ commit_message = result.stdout.strip()
268
+
269
+ if '[override]' in commit_message.lower() or '[repokit-override]' in commit_message.lower():
270
+ print("🔓 Override flag detected in commit message - bypassing guardrails")
271
+ return True
272
+
273
+ except Exception:
274
+ pass # If we can't check commit message, continue with normal validation
275
+
276
+ return False
277
+
278
+ def get_merge_excludes(self, source_branch: str, target_branch: str) -> Set[str]:
279
+ """Get list of files to exclude when merging between branches."""
280
+ # If merging from private to public, exclude private files
281
+ if source_branch in self.PRIVATE_BRANCHES and target_branch in self.PUBLIC_BRANCHES:
282
+ return self.EXCLUDE_FROM_PUBLIC
283
+
284
+ return set()
285
+
286
+ def setup_branch_excludes(self) -> None:
287
+ """Set up .git/info/exclude for current branch."""
288
+ exclude_file = os.path.join(self.repo_path, '.git', 'info', 'exclude')
289
+
290
+ if self.is_public_branch():
291
+ # Add private patterns to exclude file
292
+ excludes = [
293
+ "# RepoKit: Branch-conditional excludes for public branch",
294
+ "# These files are tracked in 'private' branch but ignored in public branches",
295
+ "",
296
+ "# AI integration files",
297
+ "CLAUDE.md",
298
+ "",
299
+ "# Development conversation logs",
300
+ "private/claude/",
301
+ "",
302
+ "# Private development documentation",
303
+ "private/docs/",
304
+ "",
305
+ "# Development notes and temporary files",
306
+ "private/notes/",
307
+ "private/temp/",
308
+ "",
309
+ "# Revision history (private branch only)",
310
+ "revisions/",
311
+ "",
312
+ "# Test artifacts",
313
+ "test-runs/",
314
+ "test_runs/",
315
+ ]
316
+
317
+ _write_excludes(exclude_file, excludes)
318
+
319
+ print(f"✓ Set up branch excludes for public branch '{self.current_branch}'")
320
+ else:
321
+ # Private branch - minimal excludes
322
+ excludes = [
323
+ "# RepoKit: Minimal excludes for private branch",
324
+ "# Private branches can contain all content",
325
+ "",
326
+ "# Test artifacts (not needed in git)",
327
+ "test-runs/",
328
+ "test_runs/",
329
+ ]
330
+
331
+ _write_excludes(exclude_file, excludes)
332
+
333
+ print(f"✓ Set up branch excludes for private branch '{self.current_branch}'")
334
+
335
+
336
+ def check_branch_safety() -> bool:
337
+ """Quick check if current operations are safe."""
338
+ context = BranchContext()
339
+ is_valid, errors = context.validate_commit()
340
+
341
+ if not is_valid:
342
+ for error in errors:
343
+ print(error)
344
+ return False
345
+
346
+ return True
347
+
348
+
349
+ if __name__ == "__main__":
350
+ # Quick test when run directly
351
+ context = BranchContext()
352
+ print(f"Current branch: {context.current_branch}")
353
+ print(f"Branch type: {context.branch_type}")
354
+ print(f"Is private: {context.is_private_branch()}")
355
+ print(f"Is public: {context.is_public_branch()}")
356
+
357
+ staged = context.get_staged_files()
358
+ if staged:
359
+ print(f"\nStaged files: {staged}")
360
+ private = context.check_private_files(staged)
361
+ if private:
362
+ print("\nPrivate files detected:")
363
+ for file, reason in private:
364
+ print(f" - {file}: {reason}")