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,391 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Template engine for RepoKit.
4
+
5
+ Handles loading and rendering templates from the package's template directory.
6
+ """
7
+
8
+ import datetime
9
+ import json
10
+ import os
11
+ import logging
12
+ import re
13
+ import string
14
+ import uuid
15
+ import importlib.resources as resources
16
+ from pathlib import Path
17
+ from typing import Dict, Any, Optional, List, Union
18
+
19
+ # The second placeholder dialect: __RK_NAME__ tokens, used by the code-type
20
+ # layers (repokit/layers.py) in file contents AND file names, where a `$` is
21
+ # shell-hostile and collides with CMake's ${VAR}, Make's $$ and Actions' ${{ }}.
22
+ # The structure templates (*.template) keep string.Template's $var; both
23
+ # dialects read one context (token_context) so a project has one set of names.
24
+ RK_TOKEN = re.compile(r"__RK_([A-Z_]+?)__")
25
+
26
+
27
+ def token_context(context: Dict[str, Any], archetype: Optional[str] = None) -> Dict[str, Any]:
28
+ """The context plus the RK_* names the token dialect resolves.
29
+
30
+ Derived from the same values the $var templates use (project_name,
31
+ description, author, author_email, the GitHub user), after the proof's
32
+ assemble.py names(): PROJECT (my-tool), SNAKE (my_tool), MACRO (MY_TOOL),
33
+ PASCAL (MyTool), ORG, DESCRIPTION, ARCHETYPE, YEAR, DATE, and two GUIDs
34
+ for Visual Studio files. Call it once per project so every file shares
35
+ the GUIDs.
36
+ """
37
+ name = str(context.get("project_name") or "project")
38
+ words = [w for w in re.split(r"[-_\s]+", name) if w] or [name]
39
+ github = context.get("github")
40
+ # github_org is the repository's owner the structure templates use, so
41
+ # both dialects name the same owner (0.9.6)
42
+ org = (context.get("github_org")
43
+ or context.get("organization")
44
+ or (github.get("username") if isinstance(github, dict) else None)
45
+ or context.get("github_user") or "your-org")
46
+ today = datetime.date.today()
47
+ tokens = {
48
+ "RK_PROJECT": name,
49
+ "RK_SNAKE": "_".join(words).lower(),
50
+ "RK_MACRO": "_".join(words).upper(),
51
+ "RK_PASCAL": "".join(w[:1].upper() + w[1:] for w in words),
52
+ "RK_ORG": str(org),
53
+ "RK_DESCRIPTION": str(context.get("description") or ""),
54
+ "RK_ARCHETYPE": archetype or "",
55
+ "RK_YEAR": str(today.year),
56
+ "RK_DATE": today.isoformat(),
57
+ "RK_GUID_PROJECT": str(uuid.uuid4()).upper(),
58
+ "RK_GUID_SOLUTION": str(uuid.uuid4()).upper(),
59
+ "RK_AUTHOR": str(context.get("author") or context.get("author_name") or ""),
60
+ "RK_EMAIL": str(context.get("author_email") or ""),
61
+ # 0.9.6: the licence the create wrote and the topics as keywords (one
62
+ # form valid in a TOML or a JSON array). RK_LICENSE is a manifest's
63
+ # string (plugin.json); the pyproject lines are whole lines (0.9.8,
64
+ # PEP 639): `license = "<SPDX>"` only for a catalogue licence, and
65
+ # `license-files` whenever a LICENSE is written, else nothing.
66
+ "RK_LICENSE": str(context.get("license_expression") or "UNLICENSED"),
67
+ "RK_LICENSE_FIELD": (f'license = "{context["license_spdx"]}"' if context.get("license_spdx") else ""),
68
+ "RK_LICENSE_FILES": ('license-files = ["LICENSE"]' if context.get("license_file_written") else ""),
69
+ "RK_KEYWORDS": ", ".join(json.dumps(str(t)) for t in (context.get("topics") or [])),
70
+ }
71
+ return {**context, **tokens}
72
+
73
+
74
+ class TemplateEngine:
75
+ """
76
+ Template engine for loading and rendering templates.
77
+ """
78
+
79
+ def __init__(self, templates_dir: Optional[str] = None, verbose: int = 0):
80
+ """
81
+ Initialize the template engine.
82
+
83
+ Args:
84
+ templates_dir: Path to template directory (or use package's templates)
85
+ verbose: Verbosity level (0=normal, 1=info, 2=debug)
86
+ """
87
+ self.verbose = verbose
88
+
89
+ # Configure logging
90
+ self.logger = logging.getLogger("repokit.templates")
91
+
92
+ # Try multiple locations for templates
93
+ self.templates_dirs = []
94
+
95
+ # 1. Use provided template directory if specified
96
+ if templates_dir:
97
+ self.templates_dirs.append(templates_dir)
98
+
99
+ # 2. Try to find templates in the package via importlib.resources
100
+ try:
101
+ pkg_templates = resources.files("repokit").joinpath("templates")
102
+ # resources.as_file will give us a real filesystem path
103
+ with resources.as_file(pkg_templates) as tpl_path:
104
+ self.templates_dirs.append(str(tpl_path))
105
+ except (ImportError, FileNotFoundError):
106
+ # either stdlib importlib.resources is missing (very old Python)
107
+ # or the package isn’t installed with that resource
108
+ pass
109
+
110
+ # 3. Check current script directory
111
+ script_dir = os.path.dirname(os.path.abspath(__file__))
112
+ self.templates_dirs.append(os.path.join(script_dir, "templates"))
113
+
114
+ # 4. Check current working directory
115
+ cwd_templates = os.path.join(os.getcwd(), "repokit", "templates")
116
+ self.templates_dirs.append(cwd_templates)
117
+
118
+ # 5. Check parent directories (for development mode)
119
+ current_dir = os.getcwd()
120
+ for _ in range(3): # Check up to 3 levels up
121
+ parent_dir = os.path.dirname(current_dir)
122
+ if parent_dir == current_dir: # Reached root
123
+ break
124
+ current_dir = parent_dir
125
+ self.templates_dirs.append(
126
+ os.path.join(current_dir, "repokit", "templates")
127
+ )
128
+
129
+ # Find first valid template directory
130
+ self.templates_dir = None
131
+ for dir_path in self.templates_dirs:
132
+ if os.path.exists(dir_path) and os.path.isdir(dir_path):
133
+ self.templates_dir = dir_path
134
+ break
135
+
136
+ if self.templates_dir:
137
+ if self.verbose >= 1:
138
+ self.logger.info(f"Using templates from: {self.templates_dir}")
139
+ else:
140
+ self.logger.warning(
141
+ f"No template directory found. Tried: {self.templates_dirs}"
142
+ )
143
+ # Use fallback templates
144
+ self.templates_dir = None
145
+
146
+ # Fallback templates for critical files
147
+ self.fallback_templates = {
148
+ "common/README.md.template": """# $project_name\n\n$description\n""",
149
+ "common/gitignore.template": """# Ignore common files\n.DS_Store\n*.log\n""",
150
+ }
151
+
152
+ def get_template_path(
153
+ self, template_name: str, category: str = "common"
154
+ ) -> Optional[str]:
155
+ """
156
+ Get the path to a template file.
157
+
158
+ Args:
159
+ template_name: Name of the template file
160
+ category: Category of the template (common, github, languages/python, etc.)
161
+
162
+ Returns:
163
+ Path to the template file or None if not found
164
+ """
165
+ if not self.templates_dir:
166
+ return None
167
+
168
+ # Try direct path first (if template_name includes a category)
169
+ if "/" in template_name:
170
+ direct_path = os.path.join(self.templates_dir, template_name)
171
+ if os.path.exists(direct_path):
172
+ return direct_path
173
+
174
+ # Try with .template extension
175
+ if not template_name.endswith(".template"):
176
+ template_name = f"{template_name}.template"
177
+
178
+ # Look in multiple possible locations
179
+ possible_paths = []
180
+
181
+ # 1. Look in the specified category
182
+ possible_paths.append(os.path.join(self.templates_dir, category, template_name))
183
+
184
+ # 2. Try common category if not found and not already looking there
185
+ if category != "common":
186
+ possible_paths.append(
187
+ os.path.join(self.templates_dir, "common", template_name)
188
+ )
189
+
190
+ # 3. Try languages subdirectory
191
+ if "/" not in category and not category.startswith("languages/"):
192
+ possible_paths.append(
193
+ os.path.join(self.templates_dir, "languages", category, template_name)
194
+ )
195
+
196
+ # 4. Try root templates directory
197
+ possible_paths.append(os.path.join(self.templates_dir, template_name))
198
+
199
+ # Check all possible paths
200
+ for path in possible_paths:
201
+ if os.path.exists(path):
202
+ if self.verbose >= 2:
203
+ self.logger.debug(f"Found template at: {path}")
204
+ return path
205
+
206
+ # Log the paths we tried
207
+ if self.verbose >= 2:
208
+ self.logger.debug(f"Tried looking for template in: {possible_paths}")
209
+
210
+ return None
211
+
212
+ def load_template(
213
+ self, template_name: str, category: str = "common"
214
+ ) -> Optional[str]:
215
+ """
216
+ Load a template file.
217
+
218
+ Args:
219
+ template_name: Name of the template file
220
+ category: Category of the template (common, github, languages/python, etc.)
221
+
222
+ Returns:
223
+ Template content as string or None if not found
224
+ """
225
+ # First try to find the template file
226
+ template_path = self.get_template_path(template_name, category)
227
+ if template_path:
228
+ try:
229
+ with open(template_path, "r", encoding="utf-8") as f:
230
+ content = f.read()
231
+
232
+ if self.verbose >= 2:
233
+ self.logger.debug(f"Loaded template: {template_path}")
234
+ return content
235
+ except Exception as e:
236
+ self.logger.error(f"Error loading template {template_path}: {str(e)}")
237
+
238
+ # If not found, try fallback templates
239
+ fallback_key = f"{category}/{template_name}"
240
+ if not fallback_key.endswith(".template"):
241
+ fallback_key += ".template"
242
+
243
+ if fallback_key in self.fallback_templates:
244
+ if self.verbose >= 1:
245
+ self.logger.info(f"Using fallback template for: {fallback_key}")
246
+ return self.fallback_templates[fallback_key]
247
+
248
+ # Final fallback for specific template types
249
+ base_template_name = os.path.basename(template_name)
250
+ if base_template_name == "README.md.template":
251
+ return """# $project_name\n\n$description\n"""
252
+ elif base_template_name == "gitignore.template":
253
+ return """# Ignore patterns\n*.log\n.DS_Store\n"""
254
+
255
+ self.logger.warning(
256
+ f"Template not found: {template_name} in category {category}"
257
+ )
258
+ return None
259
+
260
+ def load_template_bytes(self, template_name: str, category: str = "common") -> Optional[bytes]:
261
+ """load_template, but the file's bytes (a fallback template is UTF-8)."""
262
+ template_path = self.get_template_path(template_name, category)
263
+ if template_path:
264
+ try:
265
+ with open(template_path, "rb") as f:
266
+ return f.read()
267
+ except OSError as e:
268
+ self.logger.error(f"Error loading template {template_path}: {str(e)}")
269
+ text = self.load_template(template_name, category)
270
+ return text.encode("utf-8") if text is not None else None
271
+
272
+ def render_bytes(self, data: bytes, context: Dict[str, Any], dollar: bool) -> bytes:
273
+ """Substitute placeholders in a file's bytes, changing nothing else.
274
+
275
+ latin-1 maps every byte to one character and back, so a BOM, CRLF
276
+ line endings and bytes outside UTF-8 all survive; the substituted
277
+ values are encoded as UTF-8 first. With ``dollar`` the $var dialect
278
+ (string.Template.safe_substitute: unknown and malformed placeholders
279
+ such as ${CMAKE_VAR}, $<GENEX>, ${{ github }} and $1 are left alone)
280
+ runs first; the __RK_NAME__ tokens are replaced in every case, and an
281
+ unknown token is left in place. Proven on both template populations
282
+ by tests/one-offs/thinking/poc-one-engine/poc_one_engine.py.
283
+ """
284
+ values = {k: str(v).encode("utf-8").decode("latin-1") for k, v in context.items()}
285
+ text = data.decode("latin-1")
286
+ if dollar:
287
+ text = string.Template(text).safe_substitute(values)
288
+ text = RK_TOKEN.sub(lambda m: values.get("RK_" + m.group(1), m.group(0)), text)
289
+ return text.encode("latin-1")
290
+
291
+ def render_template(self, template_content: str, context: Dict[str, Any]) -> str:
292
+ """
293
+ Render a template with the given context.
294
+
295
+ Args:
296
+ template_content: Template content as string
297
+ context: Dictionary of variables for template rendering
298
+
299
+ Returns:
300
+ Rendered template as string
301
+ """
302
+ try:
303
+ template = string.Template(template_content)
304
+ return template.safe_substitute(context)
305
+ except Exception as e:
306
+ self.logger.error(f"Error rendering template: {str(e)}")
307
+ # Return original content in case of error
308
+ return template_content
309
+
310
+ def render_template_to_file(
311
+ self,
312
+ template_name: str,
313
+ output_path: str,
314
+ context: Dict[str, Any],
315
+ category: str = "common",
316
+ preserve_existing: bool = False,
317
+ ) -> bool:
318
+ """
319
+ Load, render, and write a template to a file.
320
+
321
+ Args:
322
+ template_name: Name of the template file
323
+ output_path: Path to write the rendered template
324
+ context: Dictionary of variables for template rendering
325
+ category: Category of the template (common, github, languages/python, etc.)
326
+ preserve_existing: If True, don't overwrite existing files
327
+
328
+ Returns:
329
+ True if successful, False otherwise
330
+ """
331
+ # Check if file exists and should be preserved
332
+ if preserve_existing and os.path.exists(output_path):
333
+ self.logger.info(f"Preserving existing file: {output_path}")
334
+ return True
335
+
336
+ data = self.load_template_bytes(template_name, category)
337
+ if not data:
338
+ return False
339
+
340
+ # Bytes in, bytes out: the template's own line endings reach the file
341
+ # (the templates are pinned to LF by .gitattributes), where the text
342
+ # path used to write CRLF on Windows.
343
+ rendered = self.render_bytes(data, context, dollar=True)
344
+
345
+ try:
346
+ # Create directory if it doesn't exist
347
+ os.makedirs(os.path.dirname(output_path), exist_ok=True)
348
+
349
+ with open(output_path, "wb") as f:
350
+ f.write(rendered)
351
+
352
+ if self.verbose >= 2:
353
+ self.logger.debug(f"Rendered template {template_name} to {output_path}")
354
+ return True
355
+ except Exception as e:
356
+ self.logger.error(f"Error writing template to {output_path}: {str(e)}")
357
+ return False
358
+
359
+ def list_templates(self, category: str = None) -> List[str]:
360
+ """
361
+ List available templates.
362
+
363
+ Args:
364
+ category: Category to list templates from (or all if None)
365
+
366
+ Returns:
367
+ List of template names
368
+ """
369
+ templates = []
370
+
371
+ # Return fallback templates if no templates directory
372
+ if not self.templates_dir:
373
+ return list(self.fallback_templates.keys())
374
+
375
+ if category:
376
+ category_path = os.path.join(self.templates_dir, category)
377
+ if os.path.exists(category_path) and os.path.isdir(category_path):
378
+ for file in os.listdir(category_path):
379
+ if file.endswith(".template"):
380
+ templates.append(file)
381
+ else:
382
+ # List all templates
383
+ for root, _, files in os.walk(self.templates_dir):
384
+ for file in files:
385
+ if file.endswith(".template"):
386
+ rel_path = os.path.relpath(
387
+ os.path.join(root, file), self.templates_dir
388
+ )
389
+ templates.append(rel_path)
390
+
391
+ return templates
@@ -0,0 +1,25 @@
1
+ # CLAUDE.md -- $project_name
2
+
3
+ $description
4
+
5
+ Guidance for Claude Code sessions in this repository. It stays short: the project's notes, designs and boards live in the private vault.
6
+
7
+ ## Start here
8
+
9
+ - `private/CLAUDE.md` -- the vault: where the design documents, postmortems, notes and boards are, and how to write there.
10
+ - `README.md` -- what the project is, and how to install, run and test it.
11
+
12
+ ## Commands
13
+
14
+ Setup:
15
+
16
+ $setup_block
17
+
18
+ Tests:
19
+
20
+ $test_block
21
+
22
+ ## Rules
23
+
24
+ - `private/` is its own git repository, ignored by this one and never pushed. Commit vault files from inside it, by named path.
25
+ - Commit and push only when the person asks.
@@ -0,0 +1,98 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ **Project**: $project_name
8
+ **Description**: $description
9
+ **Language**: $language
10
+ **Created**: $date
11
+
12
+ ## Common Development Commands
13
+
14
+ ```bash
15
+ # Run tests
16
+ $test_command
17
+
18
+ # Lint/Format code
19
+ $lint_command
20
+
21
+ # Build/Run
22
+ $build_command
23
+ ```
24
+
25
+ ## Test Development Guidelines
26
+
27
+ ### Test Script Management
28
+ - **One-off test scripts**: Store in `tests/one-offs/` directory
29
+ - **Test runs**: Always use `test_runs/` directory (not test-runs)
30
+ - **Keep test artifacts**: Don't delete one-off scripts - they may be useful later
31
+
32
+ Example:
33
+ ```bash
34
+ # Create test script
35
+ mkdir -p tests/one-offs
36
+ vim tests/one-offs/test_feature.py
37
+
38
+ # Run tests that create artifacts
39
+ cd test_runs # Standardized location
40
+ python test_feature.py
41
+ ```
42
+
43
+ ## Architecture Notes
44
+
45
+ <!-- Add your project-specific architecture notes here -->
46
+
47
+ ## Private Branch Guidelines
48
+
49
+ ### Documentation Requirements
50
+
51
+ - **ALWAYS** document all work performed in timestamped files under `./private/claude/`
52
+ - Use filename format: `YYYY_MM_DD__HH_MM_SS__(TOPIC).md`
53
+ - Include all commands executed, their outputs, and summaries
54
+
55
+ ### Version Control Practices
56
+
57
+ - The `private` branch is LOCAL ONLY - never push to remote repositories
58
+ - Commit frequently to track all changes and edits
59
+ - Make minimal edits to preserve diff readability:
60
+ - Avoid unnecessary variable/function/class name changes
61
+ - Only rename when truly warranted for clarity or correctness
62
+ - Preserve existing code structure when possible
63
+
64
+ ### Private Content Structure
65
+
66
+ ```
67
+ private/
68
+ ├── claude/ # All Claude-assisted work documentation
69
+ │ ├── instructions/ # Core workflow instructions
70
+ │ └── YYYY_MM_DD__HH_MM_SS__(TOPIC).md
71
+ ├── convos/ # Conversation logs (protected from commits)
72
+ └── logs/ # System logs (protected from commits)
73
+ ```
74
+
75
+ ## The Dev Workflow Process
76
+
77
+ When tackling complex problems or making significant decisions, use **THE PROCESS** - a 5-stage systematic approach:
78
+
79
+ ### 🔁 The 5 Stages:
80
+
81
+ 1. **Problem Analysis** - Define and understand the full context
82
+ 2. **Conceptual Exploration** - Explore the nature and relationships
83
+ 3. **Brainstorming Solutions** - Generate and evaluate multiple approaches
84
+ 4. **Synthesis and Recommendation** - Combine best elements into optimal solution
85
+ 5. **Implementation Plan** - Create actionable roadmap
86
+
87
+ **When to use**: For any complex problem, design decision, bug investigation, or strategic choice.
88
+
89
+ ## Additional Instructions
90
+
91
+ Detailed workflow instructions are available in `./private/claude/instructions/`:
92
+ - Context rebuilding between sessions
93
+ - The full Dev Workflow Process
94
+ - Creating conversation summaries
95
+
96
+ ## Project-Specific Notes
97
+
98
+ <!-- Add any project-specific guidelines, patterns, or warnings here -->
@@ -0,0 +1,33 @@
1
+ # Context Rebuilder Instructions
2
+
3
+ When resuming work or switching between sessions, follow these steps to rebuild context:
4
+
5
+ ## 1. Initial Context Evaluation
6
+ - Review any provided summaries, notes, or partial conversations
7
+ - Identify the main topic/goal and current project status
8
+ - Note important artifacts, files, or references mentioned
9
+
10
+ ## 2. Reconstructing Full Context
11
+ - **Project Objectives**: Clearly restate what is being aimed for
12
+ - **Current Situation**: Summarize current phase, tasks in progress, recent developments
13
+ - **Prior Decisions**: State decisions made with their justification
14
+ - **Known Constraints**: Time, resources, technical limitations
15
+ - **Outstanding Issues**: Open questions, risks, or ambiguities
16
+
17
+ ## 3. Handling Ambiguous Context
18
+ If information is unclear, ask specific questions:
19
+ - "Could you clarify the main objective?"
20
+ - "What is the current status of [specific feature]?"
21
+ - "Are there any critical constraints I should know about?"
22
+
23
+ ## 4. Context Verification
24
+ Summarize the reconstructed context back to confirm accuracy:
25
+ - "Based on the provided information, here's my understanding..."
26
+ - "Please confirm or correct any misunderstandings"
27
+
28
+ ## 5. For Code Projects
29
+ When context is missing, ask for:
30
+ 1. Summary of previous conversation
31
+ 2. List of project files (`tree` or `ls` output)
32
+ 3. Key source files currently being worked on
33
+ 4. Any specific technical constraints or requirements
@@ -0,0 +1,67 @@
1
+ # The Dev Workflow Process (The Process)
2
+
3
+ A systematic 5-stage approach for tackling complex problems, making design decisions, and solving technical challenges.
4
+
5
+ ## 🔁 The 5 Stages
6
+
7
+ ### 1. Problem Analysis
8
+ > *"Clarify before solving. Understand before deciding."*
9
+
10
+ - Define the core problem with precision
11
+ - Break into sub-problems or dimensions
12
+ - Explore pros, cons, edge cases, and risks
13
+ - Consider short-term vs long-term implications
14
+ - Identify ambiguities and dependencies
15
+
16
+ ### 2. Conceptual Exploration
17
+ > *"Think about how the system works. What are the forces at play?"*
18
+
19
+ - Understand WHY the problem exists
20
+ - Explore mental models and analogies
21
+ - Consider different approach types
22
+ - Examine relationships between elements
23
+ - Think systemically about cause and effect
24
+
25
+ ### 3. Brainstorming Solutions
26
+ > *"Don't jump to the first good idea. Explore the option space."*
27
+
28
+ - Generate 3-5 distinct solutions
29
+ - For each: analyze pros, cons, trade-offs
30
+ - Evaluate feasibility, cost, and risks
31
+ - Consider hybrid approaches
32
+ - Think about edge cases each handles
33
+
34
+ ### 4. Synthesis and Recommendation
35
+ > *"Choose what's most balanced, strategic, and resilient."*
36
+
37
+ - Combine best elements from various solutions
38
+ - Select approach that best:
39
+ - Solves the core problem
40
+ - Handles edge cases
41
+ - Aligns with long-term goals
42
+ - Balances constraints
43
+ - Justify with evidence from analysis
44
+
45
+ ### 5. Implementation Plan
46
+ > *"Make it real. Turn insight into execution."*
47
+
48
+ - Define step-by-step roadmap
49
+ - Include milestones and checkpoints
50
+ - Identify tools and resources needed
51
+ - Plan for contingencies
52
+ - Define success criteria
53
+
54
+ ## When to Use The Process
55
+
56
+ **Use for:**
57
+ - Complex multi-faceted problems
58
+ - Important design decisions
59
+ - Performance optimization
60
+ - Architecture choices
61
+ - Debugging mysterious issues
62
+
63
+ **Maybe skip for:**
64
+ - Simple, well-defined tasks
65
+ - Following established patterns
66
+ - Time-critical emergencies (do quick version)
67
+ - Reversible, low-impact changes
@@ -0,0 +1,52 @@
1
+ # Context Bridge - Conversation Summaries
2
+
3
+ When approaching token limits or needing to transfer context between sessions, create a structured summary.
4
+
5
+ ## Summary Structure
6
+
7
+ ### 1. Topic Overview
8
+ - Detailed but concise summary of the conversation
9
+ - Main problem, context, and domain area
10
+
11
+ ### 2. Current Situation
12
+ - What we're working on right now
13
+ - Current step or phase
14
+ - Recent changes or decisions
15
+
16
+ ### 3. Key Artifacts
17
+ - Important files, functions, or systems
18
+ - Code snippets with context
19
+ - External tools or dependencies
20
+
21
+ ### 4. Unresolved Issues
22
+ - Open questions or bugs
23
+ - Assumptions to validate
24
+ - Known risks or concerns
25
+
26
+ ### 5. Next Steps
27
+ - Clear list of pending actions
28
+ - Priority order if applicable
29
+ - Dependencies between tasks
30
+
31
+ ### 6. Assumptions & Constraints
32
+ - Technical constraints
33
+ - Time or resource limits
34
+ - Design assumptions made
35
+
36
+ ### 7. Decisions Made
37
+ - What has been decided
38
+ - What has been ruled out
39
+ - Brief justification
40
+
41
+ ### 8. External References
42
+ - Links, documentation
43
+ - File paths
44
+ - Tool configurations
45
+
46
+ ## Usage Tips
47
+
48
+ - Make summaries copy-pasteable
49
+ - Include enough context for cold start
50
+ - Focus on actionable information
51
+ - Preserve important reasoning
52
+ - Note any time-sensitive items
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ All notable changes to $project_name are documented here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - $date
10
+
11
+ ### Added
12
+ - The project's structure, from git-repokit.
13
+
14
+ [Unreleased]: https://github.com/$github_org/$project_name/compare/v0.1.0...HEAD
15
+ [0.1.0]: https://github.com/$github_org/$project_name/releases/tag/v0.1.0