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,32 @@
1
+ {
2
+ "name": "__RK_PROJECT__",
3
+ "version": "0.1.0",
4
+ "description": "__RK_DESCRIPTION__",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=20"
8
+ },
9
+ "main": "./dist/src/index.js",
10
+ "types": "./dist/src/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/src/index.d.ts",
14
+ "import": "./dist/src/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist/src"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsc -p .",
22
+ "typecheck": "tsc --noEmit -p .",
23
+ "test": "tsc -p . && node --test",
24
+ "prepublishOnly": "npm run build"
25
+ },
26
+ "devDependencies": {
27
+ "typescript": "^5.6.0"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ }
32
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * __RK_DESCRIPTION__
3
+ *
4
+ * The package's public surface. Everything exported here is what
5
+ * `import ... from "__RK_PROJECT__"` gives a consumer.
6
+ */
7
+
8
+ export const name = "__RK_PROJECT__";
9
+
10
+ /** A first function to replace with the package's work. */
11
+ export function greet(who = "world"): string {
12
+ return `Hello, ${who}, from ${name}.`;
13
+ }
@@ -0,0 +1,11 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { greet, name } from "../src/index.js";
4
+
5
+ test("the package names itself", () => {
6
+ assert.equal(name, "__RK_PROJECT__");
7
+ });
8
+
9
+ test("greet greets", () => {
10
+ assert.equal(greet("tests"), `Hello, tests, from ${name}.`);
11
+ });
@@ -0,0 +1,34 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "name": "__RK_PROJECT__.js --version",
6
+ "type": "node",
7
+ "request": "launch",
8
+ "program": "${workspaceFolder}/__RK_PROJECT__.js",
9
+ "args": ["--version"],
10
+ "cwd": "${workspaceFolder}",
11
+ "console": "integratedTerminal",
12
+ "skipFiles": ["<node_internals>/**"]
13
+ },
14
+ {
15
+ "name": "node: current file",
16
+ "type": "node",
17
+ "request": "launch",
18
+ "program": "${file}",
19
+ "cwd": "${workspaceFolder}",
20
+ "console": "integratedTerminal",
21
+ "skipFiles": ["<node_internals>/**"]
22
+ },
23
+ {
24
+ "name": "npm test",
25
+ "type": "node",
26
+ "request": "launch",
27
+ "runtimeExecutable": "npm",
28
+ "runtimeArgs": ["test"],
29
+ "cwd": "${workspaceFolder}",
30
+ "console": "integratedTerminal",
31
+ "skipFiles": ["<node_internals>/**"]
32
+ }
33
+ ]
34
+ }
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ // __RK_DESCRIPTION__
3
+ //
4
+ // A plain script: one file, run with node, no build step and no dependencies.
5
+ // The version lives in the VERSION file beside this module (git-repokit-common's
6
+ // version tool keeps it, package.json, the CHANGELOG and the tags in step).
7
+
8
+ import { readFileSync } from "node:fs";
9
+ import { dirname, join } from "node:path";
10
+ import { fileURLToPath } from "node:url";
11
+
12
+ const HERE = dirname(fileURLToPath(import.meta.url));
13
+
14
+ export function readVersion() {
15
+ try {
16
+ return readFileSync(join(HERE, "VERSION"), "utf8").trim() || "0.0.0+unknown";
17
+ } catch {
18
+ return "0.0.0+unknown";
19
+ }
20
+ }
21
+
22
+ export function main(argv = process.argv.slice(2)) {
23
+ if (argv.includes("--version") || argv.includes("-V")) {
24
+ console.log(readVersion());
25
+ return 0;
26
+ }
27
+ if (argv.includes("--help") || argv.includes("-h")) {
28
+ console.log("usage: __RK_PROJECT__ [--version] [--help]\n\n__RK_DESCRIPTION__");
29
+ return 0;
30
+ }
31
+ console.log("Hello from __RK_PROJECT__. Replace this with the script's work.");
32
+ return 0;
33
+ }
34
+
35
+ if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
36
+ process.exit(main());
37
+ }
@@ -0,0 +1,26 @@
1
+ // The script answers --version with the VERSION file's content, and --help exits 0.
2
+ import { test } from "node:test";
3
+ import assert from "node:assert/strict";
4
+ import { spawnSync } from "node:child_process";
5
+ import { readFileSync } from "node:fs";
6
+ import { dirname, join } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+
9
+ const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
10
+ const SCRIPT = join(ROOT, "__RK_PROJECT__.js");
11
+
12
+ function run(...args) {
13
+ return spawnSync(process.execPath, [SCRIPT, ...args], { cwd: ROOT, encoding: "utf8" });
14
+ }
15
+
16
+ test("--version matches the VERSION file", () => {
17
+ const r = run("--version");
18
+ assert.equal(r.status, 0, r.stderr);
19
+ assert.equal(r.stdout.trim(), readFileSync(join(ROOT, "VERSION"), "utf8").trim());
20
+ });
21
+
22
+ test("--help exits cleanly and names the script", () => {
23
+ const r = run("--help");
24
+ assert.equal(r.status, 0, r.stderr);
25
+ assert.match(r.stdout, /__RK_PROJECT__/);
26
+ });
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "__RK_PROJECT__",
3
+ "version": "0.1.0",
4
+ "description": "__RK_DESCRIPTION__",
5
+ "private": true,
6
+ "type": "module",
7
+ "engines": {
8
+ "node": ">=20"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc -p .",
12
+ "typecheck": "tsc --noEmit -p .",
13
+ "test": "tsc -p . && node --test"
14
+ },
15
+ "devDependencies": {
16
+ "typescript": "^5.6.0"
17
+ }
18
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "declaration": true,
10
+ "sourceMap": true,
11
+ "rootDir": ".",
12
+ "outDir": "dist"
13
+ },
14
+ "include": ["src", "tests"]
15
+ }
@@ -0,0 +1,44 @@
1
+ name: CI (Claude Code plugin, Python hooks)
2
+
3
+ # The plugin has no package to install: every hook script must compile, the
4
+ # node launcher must parse, and the plugin's tests run with pytest.
5
+
6
+ on:
7
+ push:
8
+ branches: [ main ]
9
+ pull_request:
10
+ branches: [ main ]
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: ${{ matrix.os }}
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ os: [ubuntu-latest, windows-latest]
19
+ python-version: ['3.10', '3.12']
20
+
21
+ steps:
22
+ - uses: actions/checkout@v7
23
+
24
+ - name: Set up Python ${{ matrix.python-version }}
25
+ uses: actions/setup-python@v7
26
+ with:
27
+ python-version: ${{ matrix.python-version }}
28
+
29
+ - name: Set up Node
30
+ uses: actions/setup-node@v7
31
+ with:
32
+ node-version: '22'
33
+
34
+ - name: Compile the hooks and check the launcher
35
+ shell: bash
36
+ run: |
37
+ python -m py_compile plugin/hooks/scripts/*.py
38
+ node --check plugin/hooks/scripts/run-hook.mjs
39
+
40
+ - name: Run the plugin's tests
41
+ shell: bash
42
+ run: |
43
+ python -m pip install --upgrade pip pytest
44
+ python -m pytest plugin/tests -v
@@ -0,0 +1,21 @@
1
+ # git-repokit-common settings for __RK_PROJECT__, a Claude Code plugin with Python hooks.
2
+ #
3
+ # The plugin lives in plugin/ with its own README for the marketplace; this
4
+ # root holds the repository's tooling. The version lives in VERSION; the
5
+ # vendored tool at scripts/repokit-common/ keeps the plugin manifest in step.
6
+ # "human" tags (v0.1.0) match the version Claude Code reads from
7
+ # plugin/.claude-plugin/plugin.json. A push runs the plugin's own tests.
8
+
9
+ [tool.repokit-common]
10
+ version-source = "VERSION"
11
+ changelog = "CHANGELOG.md"
12
+ repo-url = "https://github.com/__RK_ORG__/__RK_PROJECT__"
13
+ tag-prefix = "v"
14
+ tag-format = "human"
15
+ private-patterns = ["private/", "local/", ".env"]
16
+ test-command = "python -m pytest plugin/tests -q"
17
+
18
+ [[tool.repokit-common.extra-targets]]
19
+ path = "plugin/.claude-plugin/plugin.json"
20
+ type = "json"
21
+ field = "version"
@@ -0,0 +1,33 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "name": "Hook: session_start.py (type the event JSON, then Ctrl+Z Enter / Ctrl+D)",
6
+ "type": "debugpy",
7
+ "request": "launch",
8
+ "program": "${workspaceFolder}/plugin/hooks/scripts/session_start.py",
9
+ "cwd": "${workspaceFolder}",
10
+ "env": { "__RK_MACRO___DEBUG": "1" },
11
+ "console": "integratedTerminal",
12
+ "justMyCode": false
13
+ },
14
+ {
15
+ "name": "pytest: plugin tests",
16
+ "type": "debugpy",
17
+ "request": "launch",
18
+ "module": "pytest",
19
+ "args": ["plugin/tests", "-q"],
20
+ "cwd": "${workspaceFolder}",
21
+ "console": "integratedTerminal",
22
+ "justMyCode": false
23
+ },
24
+ {
25
+ "name": "Python: current file",
26
+ "type": "debugpy",
27
+ "request": "launch",
28
+ "program": "${file}",
29
+ "console": "integratedTerminal",
30
+ "justMyCode": false
31
+ }
32
+ ]
33
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/scripts/run-hook.mjs\" session_start.py",
9
+ "timeout": 10
10
+ }
11
+ ]
12
+ }
13
+ ]
14
+ }
15
+ }
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ // Launch one of this plugin's Python hooks: node run-hook.mjs <script.py>
3
+ //
4
+ // Claude Code always has node (it is a node app), but `python3 || python` in
5
+ // hooks.json depends on the shell the hook runs under, which differs by
6
+ // platform. This launcher finds a Python 3 the platform's way round, runs the
7
+ // named script beside it with the event passed through on stdin, and returns
8
+ // its exit code. A missing Python or script is reported on stderr and exits 0,
9
+ // so a broken hook never blocks the session. Modelled on
10
+ // claude-session-logger's run-hook.mjs; the script name is an argument here,
11
+ // so one launcher serves every hook.
12
+
13
+ import { spawnSync } from "node:child_process";
14
+ import { existsSync } from "node:fs";
15
+ import { dirname, join } from "node:path";
16
+ import { fileURLToPath } from "node:url";
17
+
18
+ const HERE = dirname(fileURLToPath(import.meta.url));
19
+ const NAME = "__RK_PROJECT__";
20
+ const script = join(HERE, process.argv[2] || "session_start.py");
21
+
22
+ function findPython() {
23
+ const candidates = process.platform === "win32" ? ["python", "python3", "py"] : ["python3", "python"];
24
+ for (const cmd of candidates) {
25
+ const r = spawnSync(cmd, ["--version"], { stdio: "pipe", timeout: 5000 });
26
+ const out = `${r.stdout || ""}${r.stderr || ""}`;
27
+ if (r.status === 0 && out.includes("Python 3")) return cmd;
28
+ }
29
+ return null;
30
+ }
31
+
32
+ const python = findPython();
33
+ if (!python) {
34
+ process.stderr.write(`[${NAME}] Python 3 not found; install it and put python3 (or python) on PATH.\n`);
35
+ process.exit(0);
36
+ }
37
+ if (!existsSync(script)) {
38
+ process.stderr.write(`[${NAME}] hook script not found: ${script}\n`);
39
+ process.exit(0);
40
+ }
41
+ const r = spawnSync(python, [script], { stdio: "inherit", timeout: 60000 });
42
+ process.exit(r.status || 0);
@@ -0,0 +1,31 @@
1
+ """SessionStart hook for __RK_PROJECT__.
2
+
3
+ Claude Code runs this (through run-hook.mjs) when a session starts and passes
4
+ the event as JSON on stdin. Anything printed to stdout as JSON with
5
+ "additionalContext" reaches the session; an empty object is a no-op. Replace
6
+ the body with the plugin's work; keep it quick (the hooks.json timeout is 10
7
+ seconds).
8
+ """
9
+ import json
10
+ import os
11
+ import sys
12
+
13
+
14
+ def main() -> int:
15
+ try:
16
+ raw = sys.stdin.read()
17
+ event = json.loads(raw) if raw.strip() else {}
18
+ except (ValueError, OSError):
19
+ event = {}
20
+ output = {}
21
+ if os.environ.get("__RK_MACRO___DEBUG"):
22
+ output["additionalContext"] = (
23
+ f"__RK_PROJECT__ saw a {event.get('hook_event_name', 'SessionStart')} event "
24
+ f"(source: {event.get('source', 'unknown')})."
25
+ )
26
+ sys.stdout.write(json.dumps(output))
27
+ return 0
28
+
29
+
30
+ if __name__ == "__main__":
31
+ sys.exit(main())
@@ -0,0 +1,42 @@
1
+ """The plugin's manifests parse, agree with VERSION, and the hook answers --
2
+ directly and through the node launcher Claude Code runs."""
3
+ import json
4
+ import shutil
5
+ import subprocess
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ import pytest
10
+
11
+ PLUGIN = Path(__file__).resolve().parents[1]
12
+ ROOT = PLUGIN.parent
13
+ SCRIPTS = PLUGIN / "hooks" / "scripts"
14
+ EVENT = json.dumps({"hook_event_name": "SessionStart", "source": "startup"})
15
+
16
+
17
+ def test_plugin_json_names_the_plugin_and_carries_version():
18
+ manifest = json.loads((PLUGIN / ".claude-plugin" / "plugin.json").read_text(encoding="utf-8"))
19
+ assert manifest["name"] == "__RK_PROJECT__"
20
+ assert manifest["version"] == (ROOT / "VERSION").read_text(encoding="utf-8").strip()
21
+
22
+
23
+ def test_marketplace_lists_this_plugin_from_plugin_folder():
24
+ market = json.loads((ROOT / ".claude-plugin" / "marketplace.json").read_text(encoding="utf-8"))
25
+ assert any(p["source"] == "./plugin" and p["name"] == "__RK_PROJECT__" for p in market["plugins"])
26
+
27
+
28
+ def test_the_session_start_hook_returns_json():
29
+ hooks = json.loads((PLUGIN / "hooks" / "hooks.json").read_text(encoding="utf-8"))
30
+ assert "SessionStart" in hooks["hooks"]
31
+ r = subprocess.run([sys.executable, str(SCRIPTS / "session_start.py")], input=EVENT,
32
+ capture_output=True, text=True, timeout=30)
33
+ assert r.returncode == 0, r.stderr
34
+ json.loads(r.stdout)
35
+
36
+
37
+ @pytest.mark.skipif(shutil.which("node") is None, reason="node is not on PATH")
38
+ def test_the_node_launcher_runs_the_python_hook():
39
+ r = subprocess.run(["node", str(SCRIPTS / "run-hook.mjs"), "session_start.py"], input=EVENT,
40
+ capture_output=True, text=True, timeout=60)
41
+ assert r.returncode == 0, r.stderr
42
+ json.loads(r.stdout)
@@ -0,0 +1,63 @@
1
+ name: Publish to PyPI
2
+
3
+ # Triggered when a GitHub Release is PUBLISHED -- not on tag push. This makes
4
+ # "publish a release" the single deliberate ship action: force-moving or
5
+ # re-pushing a tag (e.g. amending a released commit) never re-fires PyPI, so
6
+ # we don't get spurious "400 File already exists" failures. The release event
7
+ # checks out the release's tag, so the right version is built.
8
+ on:
9
+ release:
10
+ types: [published]
11
+
12
+ permissions:
13
+ id-token: write
14
+ contents: read
15
+
16
+ jobs:
17
+ build:
18
+ # Skip on the template repo itself (no pyproject.toml until instantiated)
19
+ if: github.repository != 'DazzleTools/git-repokit-template'
20
+ runs-on: ubuntu-latest
21
+
22
+ steps:
23
+ - uses: actions/checkout@v7
24
+
25
+ - name: Set up Python
26
+ uses: actions/setup-python@v7
27
+ with:
28
+ python-version: '3.11'
29
+
30
+ - name: Install build dependencies
31
+ run: |
32
+ python -m pip install --upgrade pip
33
+ pip install build
34
+
35
+ - name: Build package
36
+ run: python -m build
37
+
38
+ - name: Check package
39
+ run: |
40
+ pip install twine
41
+ twine check dist/*
42
+
43
+ - uses: actions/upload-artifact@v7
44
+ with:
45
+ name: dist
46
+ path: dist/
47
+
48
+ publish:
49
+ needs: build
50
+ runs-on: ubuntu-latest
51
+ environment: pypi
52
+
53
+ permissions:
54
+ id-token: write
55
+
56
+ steps:
57
+ - uses: actions/download-artifact@v8
58
+ with:
59
+ name: dist
60
+ path: dist/
61
+
62
+ - name: Publish to PyPI
63
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,43 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "name": "__RK_PROJECT__ --version",
6
+ "type": "debugpy",
7
+ "request": "launch",
8
+ "module": "__RK_SNAKE__",
9
+ "args": ["--version"],
10
+ "cwd": "${workspaceFolder}",
11
+ "console": "integratedTerminal",
12
+ "justMyCode": false
13
+ },
14
+ {
15
+ "name": "Python: current file",
16
+ "type": "debugpy",
17
+ "request": "launch",
18
+ "program": "${file}",
19
+ "console": "integratedTerminal",
20
+ "justMyCode": false
21
+ },
22
+ {
23
+ "name": "pytest: tests",
24
+ "type": "debugpy",
25
+ "request": "launch",
26
+ "module": "pytest",
27
+ "args": ["tests", "-q"],
28
+ "cwd": "${workspaceFolder}",
29
+ "console": "integratedTerminal",
30
+ "justMyCode": false
31
+ },
32
+ {
33
+ "name": "pytest: current file",
34
+ "type": "debugpy",
35
+ "request": "launch",
36
+ "module": "pytest",
37
+ "args": ["${file}", "-q"],
38
+ "cwd": "${workspaceFolder}",
39
+ "console": "integratedTerminal",
40
+ "justMyCode": false
41
+ }
42
+ ]
43
+ }
@@ -0,0 +1,5 @@
1
+ """__RK_PROJECT__ - __RK_DESCRIPTION__"""
2
+
3
+ from ._version import __version__, __app_name__, PIP_VERSION
4
+
5
+ __all__ = ["__version__", "__app_name__", "PIP_VERSION"]
@@ -0,0 +1,6 @@
1
+ """Allow running as: python -m __RK_SNAKE__"""
2
+
3
+ from .cli import main
4
+
5
+ if __name__ == "__main__":
6
+ raise SystemExit(main())
@@ -0,0 +1,96 @@
1
+ """
2
+ Version information for __RK_PROJECT__.
3
+
4
+ This file is the canonical source for version numbers.
5
+ The __version__ string is automatically updated by git hooks
6
+ with build metadata (branch, build number, date, commit hash).
7
+
8
+ Format: MAJOR.MINOR.PATCH[-PHASE]_BRANCH_BUILD-YYYYMMDD-COMMITHASH
9
+ Example: 0.1.0_main_1-20260101-a1b2c3d4
10
+
11
+ To sync versions: python scripts/repokit-common/sync-versions.py
12
+ To bump version: python scripts/repokit-common/sync-versions.py --bump patch
13
+
14
+ Version levels:
15
+ PROJECT_PHASE: Global project maturity (prealpha -> alpha -> beta -> stable).
16
+ Changes rarely, when the overall project hits a threshold.
17
+ PHASE: Per-MINOR feature set maturity (alpha -> beta -> "" for stable).
18
+ Drops when a MINOR's feature set is complete.
19
+ """
20
+
21
+ # Version components - edit these for version bumps
22
+ MAJOR = 0
23
+ MINOR = 1
24
+ PATCH = 0
25
+ PHASE = "" # Per-MINOR feature set: "" (stable), "alpha", "beta", "rc1", etc.
26
+ PRE_RELEASE_NUM = 1 # PEP 440 pre-release number (e.g., a1, b2)
27
+
28
+ # Project-level phase (independent of version phase)
29
+ PROJECT_PHASE = "" # "prealpha", "alpha", "beta", "stable", or ""
30
+
31
+ # Auto-updated by git hooks - do not edit manually
32
+ __version__ = "0.1.0"
33
+ __app_name__ = "__RK_PROJECT__"
34
+
35
+
36
+ def get_version():
37
+ """Return the full version string including branch and build info."""
38
+ return __version__
39
+
40
+
41
+ def get_base_version():
42
+ """Return the semantic version string (MAJOR.MINOR.PATCH[-PHASE])."""
43
+ if "_" in __version__:
44
+ return __version__.split("_")[0]
45
+ base = f"{MAJOR}.{MINOR}.{PATCH}"
46
+ if PHASE:
47
+ base = f"{base}-{PHASE}"
48
+ return base
49
+
50
+
51
+ def get_display_version():
52
+ """Return a human-friendly version string with project phase.
53
+
54
+ Example: 'PREALPHA 0.1.0-alpha' or 'BETA 0.5.1' or '1.0.0'
55
+ """
56
+ base = get_base_version()
57
+ if PROJECT_PHASE and PROJECT_PHASE != "stable":
58
+ return f"{PROJECT_PHASE.upper()} {base}"
59
+ return base
60
+
61
+
62
+ def get_pip_version():
63
+ """
64
+ Return PEP 440 compliant version for pip/setuptools.
65
+
66
+ Converts our version format to PEP 440:
67
+ - Main branch: 0.1.0_main_3-20260404-hash -> 0.1.0
68
+ - Dev branch: 0.1.0_dev_3-20260404-hash -> 0.1.0.dev3
69
+ - Alpha: 0.1.0-alpha_main_3 -> 0.1.0a1
70
+ """
71
+ base = f"{MAJOR}.{MINOR}.{PATCH}"
72
+
73
+ # Map phase to PEP 440 pre-release segment
74
+ phase_map = {"alpha": f"a{PRE_RELEASE_NUM}", "beta": f"b{PRE_RELEASE_NUM}"}
75
+ if PHASE:
76
+ base += phase_map.get(PHASE, PHASE)
77
+
78
+ if "_" not in __version__:
79
+ return base
80
+
81
+ parts = __version__.split("_")
82
+ branch = parts[1] if len(parts) > 1 else "unknown"
83
+
84
+ if branch == "main":
85
+ return base
86
+ else:
87
+ build_info = "_".join(parts[2:]) if len(parts) > 2 else ""
88
+ build_num = build_info.split("-")[0] if "-" in build_info else "0"
89
+ return f"{base}.dev{build_num}"
90
+
91
+
92
+ # For convenience in imports
93
+ VERSION = get_version()
94
+ BASE_VERSION = get_base_version()
95
+ PIP_VERSION = get_pip_version()
96
+ DISPLAY_VERSION = get_display_version()
@@ -0,0 +1,18 @@
1
+ """Command-line entry point for __RK_PROJECT__ (the `__RK_PROJECT__` command)."""
2
+
3
+ import argparse
4
+
5
+ from ._version import __app_name__, get_display_version
6
+
7
+
8
+ def build_parser() -> argparse.ArgumentParser:
9
+ parser = argparse.ArgumentParser(prog=__app_name__, description="__RK_DESCRIPTION__")
10
+ parser.add_argument("--version", action="version", version=f"{__app_name__} {get_display_version()}")
11
+ return parser
12
+
13
+
14
+ def main(argv=None) -> int:
15
+ args = build_parser().parse_args(argv)
16
+ del args # nothing to do yet: replace this with the tool's work
17
+ print(f"{__app_name__} {get_display_version()}")
18
+ return 0