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.
- git_repokit-0.9.10.dist-info/METADATA +422 -0
- git_repokit-0.9.10.dist-info/RECORD +174 -0
- git_repokit-0.9.10.dist-info/WHEEL +5 -0
- git_repokit-0.9.10.dist-info/entry_points.txt +2 -0
- git_repokit-0.9.10.dist-info/licenses/LICENSE +9 -0
- git_repokit-0.9.10.dist-info/top_level.txt +1 -0
- repokit/__init__.py +8 -0
- repokit/__main__.py +12 -0
- repokit/__version__.py +4 -0
- repokit/_vendor/__init__.py +0 -0
- repokit/_vendor/ailib/__init__.py +1 -0
- repokit/_vendor/ailib/analyzer.py +177 -0
- repokit/_vendor/ailib/backend.py +95 -0
- repokit/_vendor/ailib/cache.py +97 -0
- repokit/_vendor/ailib/parsers.py +52 -0
- repokit/_vendor/ailib/transports/__init__.py +16 -0
- repokit/_vendor/ailib/transports/cli.py +312 -0
- repokit/_vendor/ailib/transports/openai_compat.py +383 -0
- repokit/_vendor/ailib/transports/prompt_file.py +41 -0
- repokit/_vendor/ailib/types.py +224 -0
- repokit/auth_integration.py +244 -0
- repokit/badges.py +183 -0
- repokit/branch_utils.py +364 -0
- repokit/cli.py +2997 -0
- repokit/common_kit.py +202 -0
- repokit/config.py +686 -0
- repokit/defaults.py +220 -0
- repokit/directory_analyzer.py +1244 -0
- repokit/directory_profiles.py +423 -0
- repokit/git_config.py +370 -0
- repokit/github_create.py +142 -0
- repokit/github_setup.py +248 -0
- repokit/guardrails.py +707 -0
- repokit/history_cleaner.py +449 -0
- repokit/history_protection.py +434 -0
- repokit/hooks/pre-commit-guardrails +122 -0
- repokit/hooks/pre-commit-template +28 -0
- repokit/layers.py +510 -0
- repokit/licenses/0bsd.txt +14 -0
- repokit/licenses/LICENSES.md +27 -0
- repokit/licenses/__init__.py +142 -0
- repokit/licenses/agpl-3.0.txt +661 -0
- repokit/licenses/all-rights-reserved.txt +10 -0
- repokit/licenses/apache-2.0.txt +201 -0
- repokit/licenses/bsd-2-clause.txt +24 -0
- repokit/licenses/bsd-3-clause.txt +28 -0
- repokit/licenses/bsl-1.0.txt +23 -0
- repokit/licenses/cc-by-4.0.txt +395 -0
- repokit/licenses/cc-by-sa-4.0.txt +427 -0
- repokit/licenses/cc0-1.0.txt +121 -0
- repokit/licenses/epl-2.0.txt +277 -0
- repokit/licenses/gpl-2.0.txt +339 -0
- repokit/licenses/gpl-3.0.txt +674 -0
- repokit/licenses/isc.txt +15 -0
- repokit/licenses/lgpl-2.1.txt +504 -0
- repokit/licenses/lgpl-3.0.txt +165 -0
- repokit/licenses/mit.txt +21 -0
- repokit/licenses/mpl-2.0.txt +373 -0
- repokit/licenses/unlicense.txt +24 -0
- repokit/readme_blocks.py +112 -0
- repokit/remote_integration.py +627 -0
- repokit/repo_manager.py +2796 -0
- repokit/template_engine.py +391 -0
- repokit/templates/ai/claude/CLAUDE-nested.md.template +25 -0
- repokit/templates/ai/claude/CLAUDE.md.template +98 -0
- repokit/templates/ai/claude/instructions/step1_context_rebuilder.md +33 -0
- repokit/templates/ai/claude/instructions/step2_dev_workflow_process.md +67 -0
- repokit/templates/ai/claude/instructions/step3_context_bridge.md +52 -0
- repokit/templates/common/CHANGELOG.md.template +15 -0
- repokit/templates/common/CONTRIBUTING.md.template +31 -0
- repokit/templates/common/README.md.template +30 -0
- repokit/templates/common/gitattributes.template +17 -0
- repokit/templates/common/gitignore.template +141 -0
- repokit/templates/extras/FUNDING.yml.template +13 -0
- repokit/templates/extras/ROADMAP.md.template +17 -0
- repokit/templates/extras/dependabot.yml.template +22 -0
- repokit/templates/extras/one-offs-README.md.template +28 -0
- repokit/templates/extras/platform-support.md.template +11 -0
- repokit/templates/extras/pull_request_template.md.template +25 -0
- repokit/templates/extras/release-template.md.template +34 -0
- repokit/templates/extras/settings.json.template +11 -0
- repokit/templates/extras/stale.yml.template +14 -0
- repokit/templates/github/CODEOWNERS.template +11 -0
- repokit/templates/github/ISSUE_TEMPLATE/bug-report.md.template +29 -0
- repokit/templates/github/ISSUE_TEMPLATE/feature-request.md.template +19 -0
- repokit/templates/github/issues/notes.md.template +18 -0
- repokit/templates/github/issues/roadmap.md.template +15 -0
- repokit/templates/hooks/pre-commit.template +64 -0
- repokit/templates/layers/archetypes/any/claude-plugin/.claude-plugin/marketplace.json +20 -0
- repokit/templates/layers/archetypes/any/claude-plugin/plugin/.claude-plugin/plugin.json +15 -0
- repokit/templates/layers/archetypes/any/claude-plugin/plugin/README.md +28 -0
- repokit/templates/layers/archetypes/c-cpp/cmake-app/.github/workflows/ci-c-cpp.yml +24 -0
- repokit/templates/layers/archetypes/c-cpp/cmake-app/.repokit-common.toml.rkappend +4 -0
- repokit/templates/layers/archetypes/c-cpp/cmake-app/.vscode/launch.json +29 -0
- repokit/templates/layers/archetypes/c-cpp/cmake-app/.vscode/tasks.json +26 -0
- repokit/templates/layers/archetypes/c-cpp/cmake-app/CMakeLists.txt +47 -0
- repokit/templates/layers/archetypes/c-cpp/cmake-app/CMakePresets.json +23 -0
- repokit/templates/layers/archetypes/c-cpp/cmake-app/cmake/GenerateStamp.cmake +45 -0
- repokit/templates/layers/archetypes/c-cpp/cmake-app/src/main.cpp +27 -0
- repokit/templates/layers/archetypes/c-cpp/cmake-app/tests/test_version.cpp +20 -0
- repokit/templates/layers/archetypes/c-cpp/cmake-app/vcpkg.json +5 -0
- repokit/templates/layers/archetypes/c-cpp/minimal/.vscode/launch.json +27 -0
- repokit/templates/layers/archetypes/c-cpp/minimal/Makefile +10 -0
- repokit/templates/layers/archetypes/c-cpp/minimal/build.bat +7 -0
- repokit/templates/layers/archetypes/c-cpp/minimal/src/__RK_SNAKE__.c +26 -0
- repokit/templates/layers/archetypes/c-cpp/msbuild/.vscode/launch.json +17 -0
- repokit/templates/layers/archetypes/c-cpp/msbuild/.vscode/tasks.json +20 -0
- repokit/templates/layers/archetypes/c-cpp/msbuild/__RK_PROJECT__.sln +19 -0
- repokit/templates/layers/archetypes/c-cpp/msbuild/__RK_PROJECT__.vcxproj +41 -0
- repokit/templates/layers/archetypes/c-cpp/msbuild/src/main.cpp +26 -0
- repokit/templates/layers/archetypes/js-ts/claude-plugin/.repokit-common.toml +28 -0
- repokit/templates/layers/archetypes/js-ts/claude-plugin/.vscode/launch.json +34 -0
- repokit/templates/layers/archetypes/js-ts/claude-plugin/plugin/hooks/hooks.json +15 -0
- repokit/templates/layers/archetypes/js-ts/claude-plugin/plugin/hooks/scripts/session-start.mjs +23 -0
- repokit/templates/layers/archetypes/js-ts/claude-plugin/plugin/tests/manifest.test.js +30 -0
- repokit/templates/layers/archetypes/js-ts/node/.repokit-common.toml.rkappend +6 -0
- repokit/templates/layers/archetypes/js-ts/node/package.json +13 -0
- repokit/templates/layers/archetypes/js-ts/node-package/.github/workflows/release.yml +39 -0
- repokit/templates/layers/archetypes/js-ts/node-package/package.json +32 -0
- repokit/templates/layers/archetypes/js-ts/node-package/src/index.ts +13 -0
- repokit/templates/layers/archetypes/js-ts/node-package/tests/index.test.ts +11 -0
- repokit/templates/layers/archetypes/js-ts/script/.vscode/launch.json +34 -0
- repokit/templates/layers/archetypes/js-ts/script/__RK_PROJECT__.js +37 -0
- repokit/templates/layers/archetypes/js-ts/script/tests/version.test.js +26 -0
- repokit/templates/layers/archetypes/js-ts/ts/package.json +18 -0
- repokit/templates/layers/archetypes/js-ts/ts/tsconfig.json +15 -0
- repokit/templates/layers/archetypes/python/claude-plugin/.github/workflows/ci-python.yml +44 -0
- repokit/templates/layers/archetypes/python/claude-plugin/.repokit-common.toml +21 -0
- repokit/templates/layers/archetypes/python/claude-plugin/.vscode/launch.json +33 -0
- repokit/templates/layers/archetypes/python/claude-plugin/VERSION +1 -0
- repokit/templates/layers/archetypes/python/claude-plugin/plugin/hooks/hooks.json +15 -0
- repokit/templates/layers/archetypes/python/claude-plugin/plugin/hooks/scripts/run-hook.mjs +42 -0
- repokit/templates/layers/archetypes/python/claude-plugin/plugin/hooks/scripts/session_start.py +31 -0
- repokit/templates/layers/archetypes/python/claude-plugin/plugin/tests/test_manifest.py +42 -0
- repokit/templates/layers/archetypes/python/pypi/.github/workflows/release.yml +63 -0
- repokit/templates/layers/archetypes/python/pypi/.vscode/launch.json +43 -0
- repokit/templates/layers/archetypes/python/pypi/__RK_SNAKE__/__init__.py +5 -0
- repokit/templates/layers/archetypes/python/pypi/__RK_SNAKE__/__main__.py +6 -0
- repokit/templates/layers/archetypes/python/pypi/__RK_SNAKE__/_version.py +96 -0
- repokit/templates/layers/archetypes/python/pypi/__RK_SNAKE__/cli.py +18 -0
- repokit/templates/layers/archetypes/python/pypi/pyproject.toml +71 -0
- repokit/templates/layers/archetypes/python/pypi/tests/conftest.py +11 -0
- repokit/templates/layers/archetypes/python/pypi/tests/test_version.py +50 -0
- repokit/templates/layers/archetypes/python/script/.github/workflows/ci-python.yml +43 -0
- repokit/templates/layers/archetypes/python/script/.repokit-common.toml +14 -0
- repokit/templates/layers/archetypes/python/script/.vscode/launch.json +33 -0
- repokit/templates/layers/archetypes/python/script/VERSION +1 -0
- repokit/templates/layers/archetypes/python/script/__RK_PROJECT__.py +40 -0
- repokit/templates/layers/archetypes/python/script/tests/test_version.py +25 -0
- repokit/templates/layers/archetypes/python/setuppy/requirements.txt +8 -0
- repokit/templates/layers/archetypes/python/setuppy/setup.py +23 -0
- repokit/templates/layers/languages/c-cpp/.gitignore.rkappend +20 -0
- repokit/templates/layers/languages/c-cpp/.repokit-common.toml +7 -0
- repokit/templates/layers/languages/c-cpp/include/__RK_SNAKE__/version.h +20 -0
- repokit/templates/layers/languages/c-cpp/src/app.rc +25 -0
- repokit/templates/layers/languages/js-ts/.editorconfig +15 -0
- repokit/templates/layers/languages/js-ts/.github/workflows/ci-js-ts.yml +41 -0
- repokit/templates/layers/languages/js-ts/.gitignore.rkappend +13 -0
- repokit/templates/layers/languages/js-ts/.repokit-common.toml +19 -0
- repokit/templates/layers/languages/js-ts/.vscode/launch.json +24 -0
- repokit/templates/layers/languages/js-ts/VERSION +1 -0
- repokit/templates/layers/languages/python/.github/workflows/ci-python.yml +76 -0
- repokit/templates/layers/languages/python/.gitignore.rkappend +15 -0
- repokit/templates/layers/languages/python/.vscode/launch.json +33 -0
- repokit/templates/layers/manifest.toml +302 -0
- repokit/templates/vault/CLAUDE.md +29 -0
- repokit/templates/vault/claude/_maps/MOC-project.md +15 -0
- repokit/templates/vault/claude/_oracle/backlinks.md +3 -0
- repokit/templates/vault/claude/_oracle/concepts.md +12 -0
- repokit/templates/vault/claude/_oracle/manifest.md +10 -0
- repokit/templates/vault/claude/_todo/ACTIVE-WORK.md +18 -0
- repokit/templates/vault/claude/_todo/OPEN-LOOPS.md +14 -0
- repokit/utils.py +316 -0
- repokit/vault.py +248 -0
repokit/layers.py
ADDED
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
"""Code-type template layers: the second of git-repokit's two template systems.
|
|
2
|
+
|
|
3
|
+
The structure system (repo_manager._generate_template_files) writes the files
|
|
4
|
+
every project gets, from *.template files with $var placeholders. This module
|
|
5
|
+
lays the code type on top: a manifest-driven walk over layer folders under
|
|
6
|
+
repokit/templates/layers/ -- a language's layer, then an archetype's -- where
|
|
7
|
+
a later file replaces an earlier one, a file named <name>.rkappend is appended
|
|
8
|
+
to <name> (including a file the structure system already wrote), __RK_NAME__
|
|
9
|
+
tokens are replaced in file names and text contents, and workflow files are
|
|
10
|
+
copied byte for byte (a token inside one is an error, because GitHub Actions'
|
|
11
|
+
own ${{ }} lives there). Both systems render through one TemplateEngine and
|
|
12
|
+
one context (template_engine.token_context), so a project has one set of names.
|
|
13
|
+
|
|
14
|
+
Ported from the proof in git-repokit-common,
|
|
15
|
+
tests/one-offs/thinking/poc-templates-c-cpp/assemble.py (8/8 arms on
|
|
16
|
+
2026-10-07); design: the Phase 2 v2 analysis of 2026-10-09 in the vault.
|
|
17
|
+
|
|
18
|
+
The manifest (repokit/templates/layers/manifest.toml):
|
|
19
|
+
|
|
20
|
+
[languages.python]
|
|
21
|
+
layers = ["languages/python"]
|
|
22
|
+
default = "python/setuppy" # the archetype a bare --language gets
|
|
23
|
+
|
|
24
|
+
[languages.javascript]
|
|
25
|
+
stub = true # named, announced, not built yet
|
|
26
|
+
description = "planned as the js-ts layer"
|
|
27
|
+
|
|
28
|
+
[archetypes."python/setuppy"]
|
|
29
|
+
languages = ["python"] # implied when --archetype names it
|
|
30
|
+
layers = ["archetypes/python/setuppy"]
|
|
31
|
+
description = "setup.py and requirements.txt (git-repokit's output before 0.7)"
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
import difflib
|
|
35
|
+
import os
|
|
36
|
+
import re
|
|
37
|
+
from dataclasses import dataclass, field
|
|
38
|
+
from pathlib import Path
|
|
39
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
40
|
+
|
|
41
|
+
from .template_engine import TemplateEngine, RK_TOKEN
|
|
42
|
+
|
|
43
|
+
MANIFEST = "manifest.toml"
|
|
44
|
+
APPEND = ".rkappend"
|
|
45
|
+
WORKFLOWS = ".github/workflows/"
|
|
46
|
+
GENERIC = "generic" # no code-type layers at all
|
|
47
|
+
LAYERS_DIR = "layers" # under the templates directory
|
|
48
|
+
ARCHETYPES_DIR = "archetypes"
|
|
49
|
+
NOT_A_PRESET = "assembled from layers; not a tested preset"
|
|
50
|
+
UNRANKED = 99 # a layer folder with no [layers] entry sorts last, in the order given
|
|
51
|
+
# Files only the primary language writes in a multi-language project (0.9.2):
|
|
52
|
+
# VS Code reads one of each, and merging two languages' JSON is out of scope.
|
|
53
|
+
PRIMARY_ONLY = (".vscode/launch.json", ".vscode/tasks.json")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class LayerError(Exception):
|
|
57
|
+
"""A language or archetype that cannot be assembled: unknown, a stub, or a
|
|
58
|
+
broken layer tree. The message is for the person; create stops before
|
|
59
|
+
`git init` when it is raised."""
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class Resolution:
|
|
64
|
+
layers: List[str] = field(default_factory=list) # layer folders in apply order
|
|
65
|
+
languages: List[str] = field(default_factory=list)
|
|
66
|
+
archetype: Optional[str] = None # the preset the request equals, or None
|
|
67
|
+
tokens: List[str] = field(default_factory=list) # what --with asked for, as given
|
|
68
|
+
names: List[str] = field(default_factory=list) # qualified layer names in apply order
|
|
69
|
+
notes: List[str] = field(default_factory=list) # e.g. NOT_A_PRESET
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _load_toml(path: Path) -> Dict[str, Any]:
|
|
73
|
+
try:
|
|
74
|
+
import tomllib
|
|
75
|
+
except ImportError: # Python < 3.11
|
|
76
|
+
try:
|
|
77
|
+
import tomli as tomllib # type: ignore
|
|
78
|
+
except ImportError:
|
|
79
|
+
raise LayerError(f"{path} needs a TOML parser: Python 3.11+ or the tomli package") from None
|
|
80
|
+
try:
|
|
81
|
+
with open(path, "rb") as f:
|
|
82
|
+
return tomllib.load(f)
|
|
83
|
+
except (tomllib.TOMLDecodeError, OSError) as e:
|
|
84
|
+
raise LayerError(f"cannot read {path}: {e}") from None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def layers_root(engine: Optional[TemplateEngine] = None) -> Optional[Path]:
|
|
88
|
+
"""The layers folder of the templates directory the engine found, or None."""
|
|
89
|
+
engine = engine or TemplateEngine()
|
|
90
|
+
if not engine.templates_dir:
|
|
91
|
+
return None
|
|
92
|
+
root = Path(engine.templates_dir) / LAYERS_DIR
|
|
93
|
+
return root if (root / MANIFEST).is_file() else None
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def load_manifest(root: Path) -> Dict[str, Any]:
|
|
97
|
+
data = _load_toml(root / MANIFEST)
|
|
98
|
+
data.setdefault("languages", {})
|
|
99
|
+
data.setdefault("archetypes", {})
|
|
100
|
+
data.setdefault("layers", {})
|
|
101
|
+
data.setdefault("facets", {})
|
|
102
|
+
return data
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def known_languages(manifest: Dict[str, Any]) -> List[str]:
|
|
106
|
+
"""Every language the manifest names (stubs included), plus generic. A
|
|
107
|
+
pseudo-language (`pseudo = true`, such as `any`, which holds layer files
|
|
108
|
+
shared by several languages) is not a language a project can have."""
|
|
109
|
+
return [GENERIC] + sorted(k for k, v in manifest["languages"].items() if not v.get("pseudo"))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def is_pseudo(manifest: Dict[str, Any], name: str) -> bool:
|
|
113
|
+
return bool(manifest["languages"].get(name, {}).get("pseudo"))
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def known_archetypes(manifest: Dict[str, Any]) -> List[str]:
|
|
117
|
+
"""The presets: what --with <lang>/<name> expands to."""
|
|
118
|
+
return sorted(manifest["archetypes"])
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def known_layers(manifest: Dict[str, Any]) -> List[str]:
|
|
122
|
+
"""Every [layers] entry, qualified (stubs included)."""
|
|
123
|
+
return sorted(manifest["layers"])
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def layer_folder(manifest: Dict[str, Any], key: str) -> str:
|
|
127
|
+
"""The folder a [layers] entry stands for: archetypes/<lang>/<name>, by convention."""
|
|
128
|
+
return f"{ARCHETYPES_DIR}/{key}"
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def facet_rank(manifest: Dict[str, Any], facet: Optional[str]) -> int:
|
|
132
|
+
f = manifest["facets"].get(facet or "", {})
|
|
133
|
+
return f.get("rank", UNRANKED)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def facet_exclusive(manifest: Dict[str, Any], facet: Optional[str]) -> bool:
|
|
137
|
+
return bool(manifest["facets"].get(facet or "", {}).get("exclusive", False))
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def parse_languages(value) -> List[str]:
|
|
141
|
+
"""A list, or a comma-separated string, or a list of either -> names in
|
|
142
|
+
the order given, without repeats or blanks."""
|
|
143
|
+
items: List[str] = []
|
|
144
|
+
for part in (value if isinstance(value, (list, tuple)) else [value or ""]):
|
|
145
|
+
for name in str(part).split(","):
|
|
146
|
+
name = name.strip()
|
|
147
|
+
if name and name not in items:
|
|
148
|
+
items.append(name)
|
|
149
|
+
return items
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _suggest(token: str, choices: List[str]) -> str:
|
|
153
|
+
close = difflib.get_close_matches(token, choices, n=3, cutoff=0.6)
|
|
154
|
+
return f"; did you mean {', '.join(close)}?" if close else ""
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _check_language(manifest: Dict[str, Any], name: str) -> None:
|
|
158
|
+
entry = manifest["languages"].get(name)
|
|
159
|
+
if entry is None:
|
|
160
|
+
raise LayerError(f"unknown language {name!r}; known: {', '.join(known_languages(manifest))}")
|
|
161
|
+
if entry.get("stub"):
|
|
162
|
+
raise LayerError(f"language {name} is not built yet: {entry.get('description', '')}".rstrip(": "))
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _preset_layers(manifest: Dict[str, Any], key: str) -> List[Tuple[str, Optional[str]]]:
|
|
166
|
+
"""A preset's (folder, qualified layer name or None) pairs, after the stub check."""
|
|
167
|
+
preset = manifest["archetypes"][key]
|
|
168
|
+
if preset.get("stub"):
|
|
169
|
+
raise LayerError(f"archetype {key} is not built yet: {preset.get('description', '')}".rstrip(": "))
|
|
170
|
+
out = []
|
|
171
|
+
for folder in preset.get("layers", []):
|
|
172
|
+
name = folder[len(ARCHETYPES_DIR) + 1:] if folder.startswith(ARCHETYPES_DIR + "/") else None
|
|
173
|
+
out.append((folder, name if name in manifest["layers"] else None))
|
|
174
|
+
return out
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _layer(manifest: Dict[str, Any], key: str) -> Tuple[str, str]:
|
|
178
|
+
entry = manifest["layers"][key]
|
|
179
|
+
if entry.get("stub"):
|
|
180
|
+
raise LayerError(f"layer {key} is not built yet: {entry.get('description', '')}".rstrip(": "))
|
|
181
|
+
return layer_folder(manifest, key), key
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def resolve(manifest: Dict[str, Any], languages, with_tokens=None, _recognise: bool = True) -> Resolution:
|
|
185
|
+
"""The ordered layers for these languages and these --with tokens.
|
|
186
|
+
|
|
187
|
+
The eight rules (Phase 3 design, D2): tokens are split on commas and
|
|
188
|
+
lower-cased; a preset (an [archetypes] key) expands to its layers; a bare
|
|
189
|
+
name is looked up across layers and presets, narrowed by the languages
|
|
190
|
+
listed or already implied, and refused with every qualified form when it
|
|
191
|
+
stays ambiguous; every layer implies its language and whatever its entry
|
|
192
|
+
`implies`, transitively; two layers in one exclusive facet of one language
|
|
193
|
+
are refused naming the pair and the facet; layers are ordered by facet
|
|
194
|
+
rank, then by the order given, after the languages' own layers; a list
|
|
195
|
+
that equals a preset's expansion is reported as that preset, any other
|
|
196
|
+
list carries the NOT_A_PRESET note; with no tokens the FIRST language's
|
|
197
|
+
default preset applies (the first language is the project's primary one);
|
|
198
|
+
stubs refuse with their description; an unknown name suggests close ones.
|
|
199
|
+
generic means no layers.
|
|
200
|
+
"""
|
|
201
|
+
table = manifest["languages"]
|
|
202
|
+
presets = manifest["archetypes"]
|
|
203
|
+
layer_table = manifest["layers"]
|
|
204
|
+
langs = [name for name in parse_languages(languages) if name != GENERIC]
|
|
205
|
+
tokens: List[str] = []
|
|
206
|
+
for t in parse_languages(with_tokens):
|
|
207
|
+
if t.lower() not in tokens:
|
|
208
|
+
tokens.append(t.lower())
|
|
209
|
+
for name in langs:
|
|
210
|
+
_check_language(manifest, name)
|
|
211
|
+
if is_pseudo(manifest, name):
|
|
212
|
+
raise LayerError(f"{name!r} holds layers shared by several languages; it is not a project language")
|
|
213
|
+
res = Resolution(languages=langs, tokens=list(tokens))
|
|
214
|
+
|
|
215
|
+
chosen: List[Tuple[str, Optional[str]]] = [] # (folder, qualified name or None), in the order given
|
|
216
|
+
presets_used: List[str] = []
|
|
217
|
+
|
|
218
|
+
def add_language(name: str) -> None:
|
|
219
|
+
if name not in langs:
|
|
220
|
+
_check_language(manifest, name)
|
|
221
|
+
if is_pseudo(manifest, name):
|
|
222
|
+
return # a shared layer's folder prefix, never a project language
|
|
223
|
+
langs.append(name)
|
|
224
|
+
|
|
225
|
+
def add_preset(key: str) -> None:
|
|
226
|
+
presets_used.append(key)
|
|
227
|
+
for lang_name in presets[key].get("languages", []) or [key.split("/")[0]]:
|
|
228
|
+
add_language(lang_name)
|
|
229
|
+
for pair in _preset_layers(manifest, key):
|
|
230
|
+
if pair not in chosen:
|
|
231
|
+
chosen.append(pair)
|
|
232
|
+
|
|
233
|
+
def add_layer(key: str) -> None:
|
|
234
|
+
pair = _layer(manifest, key)
|
|
235
|
+
add_language(key.split("/")[0])
|
|
236
|
+
if pair not in chosen:
|
|
237
|
+
chosen.append(pair)
|
|
238
|
+
|
|
239
|
+
# Pass 1: presets and qualified names; bare names that are unique anywhere.
|
|
240
|
+
# Pass 2: the bare names left, narrowed by the languages now known.
|
|
241
|
+
deferred: List[str] = []
|
|
242
|
+
for tok in tokens:
|
|
243
|
+
if tok in presets:
|
|
244
|
+
add_preset(tok)
|
|
245
|
+
elif "/" in tok:
|
|
246
|
+
if tok in layer_table:
|
|
247
|
+
add_layer(tok)
|
|
248
|
+
else:
|
|
249
|
+
known = sorted(set(presets) | set(layer_table))
|
|
250
|
+
raise LayerError(f"unknown archetype {tok!r}; known: {', '.join(known) or 'none'}{_suggest(tok, known)}")
|
|
251
|
+
else:
|
|
252
|
+
deferred.append(tok)
|
|
253
|
+
for tok in deferred:
|
|
254
|
+
candidates = _bare_candidates(manifest, tok)
|
|
255
|
+
if not candidates:
|
|
256
|
+
bare = sorted({k.split('/', 1)[1] for k in list(presets) + list(layer_table) if '/' in k})
|
|
257
|
+
raise LayerError(f"unknown name {tok!r}; known: {', '.join(bare) or 'none'}{_suggest(tok, bare + sorted(set(presets) | set(layer_table)))}")
|
|
258
|
+
if len(candidates) > 1:
|
|
259
|
+
narrowed = [c for c in candidates if c.split("/")[0] in langs]
|
|
260
|
+
# With no language named at all, a candidate marked `primary` in the
|
|
261
|
+
# manifest settles a bare name (js-ts is the primary shape of a Claude
|
|
262
|
+
# plugin; the Python one is supported, not the default -- djdarcy,
|
|
263
|
+
# 2026-10-22). A named language that does not narrow stays ambiguous.
|
|
264
|
+
primary = [c for c in candidates if (presets.get(c) or layer_table.get(c) or {}).get("primary")]
|
|
265
|
+
if len(narrowed) == 1:
|
|
266
|
+
candidates = narrowed
|
|
267
|
+
elif not langs and len(primary) == 1:
|
|
268
|
+
candidates = primary
|
|
269
|
+
else:
|
|
270
|
+
raise LayerError(f"{tok!r} is ambiguous: {' or '.join(candidates)}")
|
|
271
|
+
key = candidates[0]
|
|
272
|
+
if key in presets:
|
|
273
|
+
add_preset(key)
|
|
274
|
+
else:
|
|
275
|
+
add_layer(key)
|
|
276
|
+
|
|
277
|
+
# Rule 4: imply, transitively (a layer implies its language; its entry may imply more).
|
|
278
|
+
i = 0
|
|
279
|
+
while i < len(chosen):
|
|
280
|
+
folder, name = chosen[i]
|
|
281
|
+
if name:
|
|
282
|
+
lang_name = name.split("/")[0]
|
|
283
|
+
for imp in layer_table[name].get("implies", []):
|
|
284
|
+
target = imp if "/" in imp else f"{lang_name}/{imp}"
|
|
285
|
+
if target in table: # a language
|
|
286
|
+
add_language(target)
|
|
287
|
+
elif target in layer_table:
|
|
288
|
+
pair = _layer(manifest, target)
|
|
289
|
+
if pair not in chosen:
|
|
290
|
+
chosen.append(pair)
|
|
291
|
+
else:
|
|
292
|
+
raise LayerError(f"layer {name} implies {imp!r}, which the manifest does not define")
|
|
293
|
+
i += 1
|
|
294
|
+
|
|
295
|
+
# Rule 5: exclusivity, per language and facet.
|
|
296
|
+
seen: Dict[Tuple[str, str], str] = {}
|
|
297
|
+
for folder, name in chosen:
|
|
298
|
+
if not name:
|
|
299
|
+
continue
|
|
300
|
+
facet = layer_table[name].get("facet")
|
|
301
|
+
if facet and facet_exclusive(manifest, facet):
|
|
302
|
+
slot = (name.split("/")[0], facet)
|
|
303
|
+
other = seen.get(slot)
|
|
304
|
+
if other and other != name:
|
|
305
|
+
a, b = other.split("/", 1)[1], name.split("/", 1)[1]
|
|
306
|
+
raise LayerError(f"'{a}' and '{b}' are both a {facet} for {slot[0]}; pick one")
|
|
307
|
+
seen[slot] = name
|
|
308
|
+
|
|
309
|
+
# Rule 7 (default) when nothing was asked.
|
|
310
|
+
if not tokens:
|
|
311
|
+
for name in langs[:1]: # the primary language's default only
|
|
312
|
+
default = table[name].get("default")
|
|
313
|
+
if default:
|
|
314
|
+
entry = presets.get(default)
|
|
315
|
+
if entry is None or entry.get("stub"):
|
|
316
|
+
raise LayerError(f"language {name} names default archetype {default!r}, which is {'unknown' if entry is None else 'a stub'}")
|
|
317
|
+
add_preset(default)
|
|
318
|
+
res.archetype = default
|
|
319
|
+
|
|
320
|
+
# Rule 6: order -- the languages' own layers first (in the order given), then by facet rank, then as given.
|
|
321
|
+
order: List[Tuple[str, Optional[str]]] = []
|
|
322
|
+
for name in langs:
|
|
323
|
+
for folder in table[name].get("layers", []):
|
|
324
|
+
order.append((folder, name))
|
|
325
|
+
ranked = sorted(enumerate(chosen), key=lambda ic: (facet_rank(manifest, layer_table.get(ic[1][1] or "", {}).get("facet")), ic[0]))
|
|
326
|
+
order += [pair for _, pair in ranked]
|
|
327
|
+
for folder, name in order:
|
|
328
|
+
if folder not in res.layers:
|
|
329
|
+
res.layers.append(folder)
|
|
330
|
+
res.names.append(name or folder)
|
|
331
|
+
res.languages = langs
|
|
332
|
+
|
|
333
|
+
# Rule 8: recognise a preset -- the WHOLE request must equal the preset's
|
|
334
|
+
# expansion, languages included (`--language python --with minimal` is a
|
|
335
|
+
# two-language composition no preset vouches for, even though `minimal`
|
|
336
|
+
# alone is one).
|
|
337
|
+
if tokens and _recognise:
|
|
338
|
+
for key in presets_used + [k for k in presets if k not in presets_used]:
|
|
339
|
+
if presets[key].get("stub"):
|
|
340
|
+
continue
|
|
341
|
+
try:
|
|
342
|
+
expansion = resolve(manifest, [], [key], _recognise=False).layers
|
|
343
|
+
except LayerError:
|
|
344
|
+
continue
|
|
345
|
+
if expansion == res.layers:
|
|
346
|
+
res.archetype = key
|
|
347
|
+
break
|
|
348
|
+
if res.archetype is None:
|
|
349
|
+
res.notes.append(NOT_A_PRESET)
|
|
350
|
+
|
|
351
|
+
# A language with a default preset that ended up with no layer of its own
|
|
352
|
+
# (the second of two languages and no --with, or a --with naming only the
|
|
353
|
+
# other language's layers) is said out loud rather than left bare
|
|
354
|
+
# (Q261022-02, answered 2026-10-22: the person refines; no second default).
|
|
355
|
+
for name in langs:
|
|
356
|
+
default = table[name].get("default")
|
|
357
|
+
if default and not any(f.startswith(f"{ARCHETYPES_DIR}/{name}/") for f in res.layers):
|
|
358
|
+
res.notes.append(f"{name} got no preset of its own; add --with {name}/<name> (see `repokit list {name}`)")
|
|
359
|
+
return res
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def catalogue_lines(manifest: Dict[str, Any], language: Optional[str] = None) -> List[str]:
|
|
363
|
+
"""`repokit list [LANG]`: per language, the presets (name = layers; planned
|
|
364
|
+
ones marked), then each facet with its choices and one-of / any-of."""
|
|
365
|
+
table = manifest["languages"]
|
|
366
|
+
if language and language not in table:
|
|
367
|
+
raise LayerError(f"unknown language {language!r}; known: {', '.join(known_languages(manifest))}")
|
|
368
|
+
facets = sorted(manifest["facets"].items(), key=lambda kv: kv[1].get("rank", UNRANKED))
|
|
369
|
+
lines: List[str] = []
|
|
370
|
+
for lang in sorted(table):
|
|
371
|
+
if language and lang != language:
|
|
372
|
+
continue
|
|
373
|
+
entry = table[lang]
|
|
374
|
+
if entry.get("pseudo"):
|
|
375
|
+
continue
|
|
376
|
+
head = f"{lang}:" + (f" (planned: {entry.get('description', '')})" if entry.get("stub") else "")
|
|
377
|
+
lines.append(head)
|
|
378
|
+
default = entry.get("default")
|
|
379
|
+
for key in sorted(k for k in manifest["archetypes"] if k.startswith(lang + "/")):
|
|
380
|
+
preset = manifest["archetypes"][key]
|
|
381
|
+
if preset.get("stub"):
|
|
382
|
+
lines.append(f" {key:28} (planned: {preset.get('description', '')})")
|
|
383
|
+
else:
|
|
384
|
+
parts = [f.split("/", 2)[-1] for f in preset.get("layers", [])]
|
|
385
|
+
mark = " <- default" if key == default else ""
|
|
386
|
+
lines.append(f" {key:28} = {', '.join(parts)}{mark}")
|
|
387
|
+
for facet, spec in facets:
|
|
388
|
+
if facet == "language":
|
|
389
|
+
continue
|
|
390
|
+
names = []
|
|
391
|
+
for key, l_entry in manifest["layers"].items():
|
|
392
|
+
if key.startswith(lang + "/") and l_entry.get("facet") == facet:
|
|
393
|
+
names.append(key.split("/", 1)[1] + (" (planned)" if l_entry.get("stub") else ""))
|
|
394
|
+
if names:
|
|
395
|
+
pick = "one of" if spec.get("exclusive") else "any of"
|
|
396
|
+
lines.append(f" {facet:10} {pick}: {', '.join(sorted(names))}")
|
|
397
|
+
return lines
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def _bare_candidates(manifest: Dict[str, Any], tok: str) -> List[str]:
|
|
401
|
+
"""Qualified keys whose bare name is tok: one per language, a preset
|
|
402
|
+
preferred over a layer of the same name (the preset is the tested one)."""
|
|
403
|
+
by_lang: Dict[str, str] = {}
|
|
404
|
+
for key in manifest["layers"]:
|
|
405
|
+
lang, name = key.split("/", 1)
|
|
406
|
+
if name == tok and not is_pseudo(manifest, lang):
|
|
407
|
+
by_lang.setdefault(lang, key)
|
|
408
|
+
for key in manifest["archetypes"]:
|
|
409
|
+
lang, name = key.split("/", 1)
|
|
410
|
+
if name == tok:
|
|
411
|
+
by_lang[lang] = key
|
|
412
|
+
return [by_lang[l] for l in sorted(by_lang)]
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def _is_text(data: bytes) -> bool:
|
|
416
|
+
return b"\0" not in data[:8192]
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def layer_language(folder: str) -> Optional[str]:
|
|
420
|
+
"""The language a layer folder belongs to: languages/<lang> or archetypes/<lang>/<name>."""
|
|
421
|
+
parts = folder.split("/")
|
|
422
|
+
return parts[1] if len(parts) >= 2 and parts[0] in ("languages", ARCHETYPES_DIR) else None
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def assemble(root: Path, layers: List[str], dest: str, context: Dict[str, Any],
|
|
426
|
+
engine: Optional[TemplateEngine] = None, preserve_existing: bool = False,
|
|
427
|
+
logger=None, primary_language: Optional[str] = None,
|
|
428
|
+
skipped: Optional[List[str]] = None) -> List[str]:
|
|
429
|
+
"""Lay the layers down under ``dest``. Returns the files written or appended,
|
|
430
|
+
as dest-relative POSIX paths. ``context`` is token_context(...).
|
|
431
|
+
|
|
432
|
+
With ``primary_language`` set (a project of two or more languages), the
|
|
433
|
+
files in PRIMARY_ONLY come from that language's layers and the shared
|
|
434
|
+
`any` ones only: VS Code reads one launch.json, and merging two languages'
|
|
435
|
+
debug configurations is out of scope (0.9.2). Each file left out is
|
|
436
|
+
reported in ``skipped`` as "<language>: <path>"."""
|
|
437
|
+
engine = engine or TemplateEngine()
|
|
438
|
+
files: Dict[str, bytes] = {} # template-relative path (tokens unresolved) -> content
|
|
439
|
+
appends: Dict[str, bytes] = {} # appends whose target no layer provides: a file already on disk
|
|
440
|
+
|
|
441
|
+
for layer in layers:
|
|
442
|
+
base = root / layer
|
|
443
|
+
if not base.is_dir():
|
|
444
|
+
raise LayerError(f"layer folder missing: {layer} (under {root})")
|
|
445
|
+
lang = layer_language(layer)
|
|
446
|
+
for src in sorted(p for p in base.rglob("*") if p.is_file()):
|
|
447
|
+
# Bytecode beside the template sources (a syntax check compiled
|
|
448
|
+
# them) is not part of any layer, and carries the tokens inside.
|
|
449
|
+
if "__pycache__" in src.parts or src.suffix in (".pyc", ".pyo"):
|
|
450
|
+
continue
|
|
451
|
+
rel = src.relative_to(base).as_posix()
|
|
452
|
+
if primary_language and rel in PRIMARY_ONLY and lang not in (primary_language, "any", None):
|
|
453
|
+
if skipped is not None and f"{lang}: {rel}" not in skipped:
|
|
454
|
+
skipped.append(f"{lang}: {rel}")
|
|
455
|
+
continue
|
|
456
|
+
data = src.read_bytes()
|
|
457
|
+
if rel.endswith(APPEND):
|
|
458
|
+
target = rel[: -len(APPEND)]
|
|
459
|
+
if target in files:
|
|
460
|
+
files[target] = _joined(files[target], data)
|
|
461
|
+
else:
|
|
462
|
+
appends[target] = _joined(appends.get(target, b""), data) if target in appends else data
|
|
463
|
+
else:
|
|
464
|
+
files[rel] = data # a later layer's file replaces an earlier one
|
|
465
|
+
|
|
466
|
+
def out_path(rel: str) -> str:
|
|
467
|
+
resolved = engine.render_bytes(rel.encode("utf-8"), context, dollar=False).decode("utf-8")
|
|
468
|
+
if ".." in Path(resolved).parts:
|
|
469
|
+
raise LayerError(f"a layer path escapes the project: {rel}")
|
|
470
|
+
return resolved
|
|
471
|
+
|
|
472
|
+
written: List[str] = []
|
|
473
|
+
for rel, data in files.items():
|
|
474
|
+
is_workflow = rel.startswith(WORKFLOWS)
|
|
475
|
+
if is_workflow and RK_TOKEN.search(data.decode("latin-1")):
|
|
476
|
+
raise LayerError(f"token in workflow file {rel}: workflow files must stay placeholder-free")
|
|
477
|
+
target = out_path(rel)
|
|
478
|
+
full = os.path.join(dest, target)
|
|
479
|
+
if preserve_existing and os.path.exists(full):
|
|
480
|
+
if logger:
|
|
481
|
+
logger.info(f"Preserving existing file: {target}")
|
|
482
|
+
continue
|
|
483
|
+
if not is_workflow and _is_text(data):
|
|
484
|
+
data = engine.render_bytes(data, context, dollar=False)
|
|
485
|
+
os.makedirs(os.path.dirname(full) or dest, exist_ok=True)
|
|
486
|
+
with open(full, "wb") as f:
|
|
487
|
+
f.write(data)
|
|
488
|
+
written.append(target)
|
|
489
|
+
|
|
490
|
+
for rel, data in appends.items():
|
|
491
|
+
target = out_path(rel)
|
|
492
|
+
full = os.path.join(dest, target)
|
|
493
|
+
if preserve_existing and os.path.exists(full):
|
|
494
|
+
if logger:
|
|
495
|
+
logger.info(f"Preserving existing file, not appending: {target}")
|
|
496
|
+
continue
|
|
497
|
+
data = engine.render_bytes(data, context, dollar=False) if _is_text(data) else data
|
|
498
|
+
os.makedirs(os.path.dirname(full) or dest, exist_ok=True)
|
|
499
|
+
existing = open(full, "rb").read() if os.path.exists(full) else b""
|
|
500
|
+
with open(full, "wb") as f:
|
|
501
|
+
f.write(_joined(existing, data))
|
|
502
|
+
written.append(target)
|
|
503
|
+
return written
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
def _joined(head: bytes, tail: bytes) -> bytes:
|
|
507
|
+
"""head + tail, with a newline between when head has none at its end."""
|
|
508
|
+
if head and not head.endswith((b"\n", b"\r\n")):
|
|
509
|
+
head += b"\n"
|
|
510
|
+
return head + tail
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
BSD Zero Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) __RK_YEAR__ __RK_HOLDER__
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted.
|
|
7
|
+
|
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
9
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
10
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
11
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
12
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
13
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
14
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# The licence catalogue: where each text came from, and its fingerprint
|
|
2
|
+
|
|
3
|
+
Source: `github/choosealicense.com`, `_licenses/<id>.txt`, commit `f66c5dd01866041688edd87f658b6d5ac3304f1b` (branch gh-pages, fetched 2026-10-23 by `tests/one-offs/thinking/p4-fetch-licenses/fetch.py`). Each file's YAML front matter was stripped and the placeholders `[year]`, `[yyyy]`, `[fullname]`, `[name of copyright owner]` became `__RK_YEAR__` and `__RK_HOLDER__`; nothing else changed. `all-rights-reserved.txt` is authored here, not fetched. The "or-later" variants (`gpl-3.0-or-later`, `lgpl-3.0-or-later`, `agpl-3.0-or-later`) are not files: `repokit.licenses` renders them from the base text with a notice line, because the licence text is identical and only the project's notice says "or later".
|
|
4
|
+
|
|
5
|
+
`tests/test_licenses.py` checks every shipped file against its shipped sha256; a changed file without a new row here fails.
|
|
6
|
+
|
|
7
|
+
| id | SPDX | title | sha256 as received | sha256 as shipped | tokens |
|
|
8
|
+
|---|---|---|---|---|---|
|
|
9
|
+
| 0bsd | 0BSD | BSD Zero Clause License | 749c160d5a8290da254f5fd02a510c6a234df5aea2f4f0711f309f2d9a821899 | e41dbf5f8aa40ed502ea4ab4023de21795d556d9a341734f2d24872dc4973d0c | holder, year |
|
|
10
|
+
| agpl-3.0 | AGPL-3.0 | GNU Affero General Public License v3.0 | 2a1fda78234b71502e2cf8035021b5592f5eba7e8107228aca4d95bba645e397 | 8486a10c4393cee1c25392769ddd3b2d6c242d6ec7928e1414efff7dfb2f07ef | none |
|
|
11
|
+
| apache-2.0 | Apache-2.0 | Apache License 2.0 | 609e2cb599f84aaa41d8ef29d8fdb04d164fab22e8d9292ca34a599d0f56a338 | 7ab3d1eca77d69cf60cb1e56379fa9a619b6fc300155904e1f8539ee6155e9c4 | holder, year |
|
|
12
|
+
| bsd-2-clause | BSD-2-Clause | BSD 2-Clause "Simplified" License | d8237d165c176416a8ab938bf7e120d8f345098163fcec9017c5652f467fecaf | fc29f42f5b8468afa3811a721ec1dd5c629f725e04785597ff4b7219887fe98e | holder, year |
|
|
13
|
+
| bsd-3-clause | BSD-3-Clause | BSD 3-Clause "New" or "Revised" License | 732e653d6b57d83ee9bd21ff53c6dd1eb332628cefd05df4b99d523727dff664 | f5d1fc68fbc06c286a11125154784b0b21fab066a1574a29f928f7c564ec8461 | holder, year |
|
|
14
|
+
| bsl-1.0 | BSL-1.0 | Boost Software License 1.0 | f8f6e066934a2365c12debd413b413caa0152791c87c0b16a12c8a65c3b92f9a | c9bff75738922193e67fa726fa225535870d2aa1059f91452c411736284ad566 | none |
|
|
15
|
+
| cc-by-4.0 | CC-BY-4.0 | Creative Commons Attribution 4.0 International | 736763bf08829e36266b8a3bebcc041ac68575424e976490671a7e6b43a525cf | f5b745ef98087f531e719ee8ca6a96809444573ecc7173c6fa68eaad39b3cc3f | none |
|
|
16
|
+
| cc-by-sa-4.0 | CC-BY-SA-4.0 | Creative Commons Attribution Share Alike 4.0 International | 8f85091fc554652d297119db8a092d8fc3d942ef3d768bd674decf48067a151f | 2003829cf643d61b00a76088dd31bb11b8029546f24c56b39fd492adb9281483 | none |
|
|
17
|
+
| cc0-1.0 | CC0-1.0 | Creative Commons Zero v1.0 Universal | db8278dfdfc1f73224f9f3762922c378344ca0443d36800baa61b3aaf31ef0e2 | a2010f343487d3f7618affe54f789f5487602331c0a8d03f49e9a7c547cf0499 | none |
|
|
18
|
+
| epl-2.0 | EPL-2.0 | Eclipse Public License 2.0 | 93c73b1678467af856e43dc4e7cc346991ab187b8da09b0e1a0cb010f6401ccd | 8c349f80764d0648e645f41ef23772a70c995a0924b5235f735f4a3d09df127c | none |
|
|
19
|
+
| gpl-2.0 | GPL-2.0 | GNU General Public License v2.0 | b7c21f2a2d0a1558458adbd90b9380d2514cebf970d12f26f84f862571c30ee2 | 8177f97513213526df2cf6184d8ff986c675afb514d4e68a404010521b880643 | none |
|
|
20
|
+
| gpl-3.0 | GPL-3.0 | GNU General Public License v3.0 | e9c13804a52b8aea2e30033764d1b19f98369bae58028d9d1eb6b84e42b888bc | 3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986 | none |
|
|
21
|
+
| isc | ISC | ISC License | 8ea92c9e801b11e1303173c3ce3fc566bbe1ede96190aaffbc5a0c97712146c8 | 491a83f2c57434db8e97565ec680a129960783ef2c5c10d3c1b9734bf0b4ee79 | holder, year |
|
|
22
|
+
| lgpl-2.1 | LGPL-2.1 | GNU Lesser General Public License v2.1 | 0094ff7d2b4451d2231cd0c20b2798574c29bc81ef740d719a9909aabd86d633 | 20c17d8b8c48a600800dfd14f95d5cb9ff47066a9641ddeab48dc54aec96e331 | none |
|
|
23
|
+
| lgpl-3.0 | LGPL-3.0 | GNU Lesser General Public License v3.0 | 0b2e8a7c7a977908fa0bfaa2e2a7a54e3fd73676662e9fedae264db2916e0135 | e3a994d82e644b03a792a930f574002658412f62407f5fee083f2555c5f23118 | none |
|
|
24
|
+
| mit | MIT | MIT License | e6919db13cacb3300f918d1048d996d0f4506e1f110b7caa21f31ea7ca3ef5b8 | e3a0a9c39448343918a8bcbbf2dc5a0bce37cd0a937045ef6eeb869653fc0ec7 | holder, year |
|
|
25
|
+
| mpl-2.0 | MPL-2.0 | Mozilla Public License 2.0 | 0e03b8171b7b830a6a416b403eced17d39baed5b3a60563971f8b27aaa4f29e6 | 3f3d9e0024b1921b067d6f7f88deb4a60cbe7a78e76c64e3f1d7fc3b779b9d04 | none |
|
|
26
|
+
| unlicense | Unlicense | The Unlicense | ddc2e070df0bb25ce951b0954d9c1b5d22b17f150d58eac66bda653d044bb5ed | 6b0382b16279f26ff69014300541967a356a666eb0b91b422f6862f6b7dad17e | none |
|
|
27
|
+
| all-rights-reserved | LicenseRef-AllRightsReserved | All rights reserved (authored here) | - | 1025ba5f1f4d5a5583b50ea511aa986aa557b1a417ef92f52fe0696b4e5565c9 | holder, year |
|