template_press 3.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.4
2
+ Name: template_press
3
+ Version: 3.0.0
4
+ Summary: Standalone rebrand utility — press a new identity onto an external target repo
5
+ Author: Steve Morin
6
+ Author-email: Steve Morin <steve.morin@gmail.com>
7
+ License-Expression: MIT
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
+ Requires-Python: >=3.12
17
+ Project-URL: Homepage, https://github.com/smorinlabs/template-press
18
+ Project-URL: Repository, https://github.com/smorinlabs/template-press
19
+ Project-URL: Documentation, https://template-press.readthedocs.io/en/latest/
20
+ Project-URL: Changelog, https://github.com/smorinlabs/template-press/blob/main/CHANGELOG.md
21
+ Project-URL: Issues, https://github.com/smorinlabs/template-press/issues
22
+ Description-Content-Type: text/markdown
23
+
24
+ <!-- ITM-081 — badges. Single horizontal row (decided round 23). -->
25
+ [![PyPI version](https://img.shields.io/pypi/v/template-press.svg)](https://pypi.org/project/template-press/)
26
+ [![Python versions](https://img.shields.io/pypi/pyversions/template-press.svg)](https://pypi.org/project/template-press/)
27
+ [![CI](https://github.com/smorinlabs/template-press/actions/workflows/ci.yml/badge.svg)](https://github.com/smorinlabs/template-press/actions/workflows/ci.yml)
28
+ [![codecov](https://codecov.io/gh/smorinlabs/template-press/branch/main/graph/badge.svg)](https://codecov.io/gh/smorinlabs/template-press)
29
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
30
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
31
+ [![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
32
+ [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://www.conventionalcommits.org/)
33
+
34
+ # template-press
35
+
36
+ **Press a new identity onto an existing repository.** template-press is a
37
+ standalone command-line utility: you point it at a target repo, it discovers
38
+ and *validates* that repo's current identity, rewrites every occurrence to a
39
+ new identity, and — only after verifying that none of the old identity
40
+ survives — writes a receipt. A wrong or partial run fails loudly instead of
41
+ silently corrupting the target.
42
+
43
+ It is **not** a project template you fork; it is a tool you run against other
44
+ repos, one at a time. The rebrand engine is pure standard library, so the
45
+ installed package has **zero runtime dependencies**.
46
+
47
+ ## Install
48
+
49
+ ```bash
50
+ uvx --from template-press press --help # run without installing
51
+ # or
52
+ pip install template-press
53
+ ```
54
+
55
+ ## Usage
56
+
57
+ ```bash
58
+ # preview the plan (writes nothing)
59
+ press rebrand --target ../my-repo --config press-answers.toml --dry-run
60
+
61
+ # apply, verify, and write a receipt
62
+ press rebrand --target ../my-repo --config press-answers.toml
63
+ ```
64
+
65
+ `press-answers.toml` describes the **destination** identity:
66
+
67
+ ```toml
68
+ [answers]
69
+ package_name = "new_pkg"
70
+ repo_name = "new-repo"
71
+ app_name = "newcli"
72
+ author = "Jane Dev"
73
+ email = "jane@example.com"
74
+ owner = "janedev"
75
+ ```
76
+
77
+ Keep your filled-in `press-answers.toml` out of the target (it is transient
78
+ operator input, not committed state). A repo may commit a
79
+ `press/press-answers.example.toml` placeholder to advertise the field
80
+ shape — copy it, fill it, and pass it via `--config`. It has the same
81
+ `[answers]` fields shown above.
82
+
83
+ The target's **current** identity is read from a committed
84
+ `<target>/press/press-source.toml`. On a first run, `--dry-run` prints a proposed
85
+ source-config from discovery; review it and re-run with `--accept-discovery`
86
+ to write and use it.
87
+
88
+ ## How it stays safe
89
+
90
+ - **Config-first identity, discovery as a guard.** The committed source-config
91
+ is authoritative; discovery cross-checks it against the target and *refuses
92
+ to run on a mismatch*, so the press never rewrites the wrong repo.
93
+ - **Verify-then-mark.** After rewriting, a no-leak scan confirms no source
94
+ identity remains. Any leftover ⇒ **exit 1 and no receipt** — a partial
95
+ rebrand is a loud failure, not a false success.
96
+ - **Boundary-safe replacement.** Short tokens (`press`, an owner like `go`)
97
+ match only at word boundaries, so ordinary prose (`compress`, `ongoing`) is
98
+ never touched.
99
+
100
+ ## Exit codes
101
+
102
+ The exit code is the contract:
103
+
104
+ | Code | Meaning |
105
+ |------|---------|
106
+ | `0` | Verified — rebrand complete, no source identity remains, receipt written. |
107
+ | `1` | Leaks found after applying. No receipt; restore with `git -C <target> checkout . && git clean -fd`. |
108
+ | `2` | Precondition/config error (missing target, dirty tree, identity mismatch, existing receipt without `--force`). Nothing written. |
109
+
110
+ `--dry-run` exits `0` after printing the plan without touching the target (no receipt).
111
+
112
+ ## Documentation
113
+
114
+ - Full docs: <https://template-press.readthedocs.io/>
115
+ - Design contract: [`docs/design/0006-external-target-model.md`](docs/design/0006-external-target-model.md)
116
+ - Agent runbooks: [`press-target`](.claude/skills/press-target/SKILL.md) (drive a
117
+ press) and [`rebrand-matrix`](.claude/skills/rebrand-matrix/SKILL.md) (the
118
+ R1/R2/R3 acceptance matrix)
119
+
120
+ ## Contributing
121
+
122
+ See [`AGENTS.md`](AGENTS.md) for the canonical toolchain and verification flow
123
+ (`just setup` → `just check` → commit) and
124
+ [`.github/CONTRIBUTING.md`](.github/CONTRIBUTING.md) for the human contributor
125
+ guide.
126
+
127
+ ## License
128
+
129
+ MIT — see [`LICENSE`](LICENSE).
@@ -0,0 +1,106 @@
1
+ <!-- ITM-081 — badges. Single horizontal row (decided round 23). -->
2
+ [![PyPI version](https://img.shields.io/pypi/v/template-press.svg)](https://pypi.org/project/template-press/)
3
+ [![Python versions](https://img.shields.io/pypi/pyversions/template-press.svg)](https://pypi.org/project/template-press/)
4
+ [![CI](https://github.com/smorinlabs/template-press/actions/workflows/ci.yml/badge.svg)](https://github.com/smorinlabs/template-press/actions/workflows/ci.yml)
5
+ [![codecov](https://codecov.io/gh/smorinlabs/template-press/branch/main/graph/badge.svg)](https://codecov.io/gh/smorinlabs/template-press)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
8
+ [![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
9
+ [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://www.conventionalcommits.org/)
10
+
11
+ # template-press
12
+
13
+ **Press a new identity onto an existing repository.** template-press is a
14
+ standalone command-line utility: you point it at a target repo, it discovers
15
+ and *validates* that repo's current identity, rewrites every occurrence to a
16
+ new identity, and — only after verifying that none of the old identity
17
+ survives — writes a receipt. A wrong or partial run fails loudly instead of
18
+ silently corrupting the target.
19
+
20
+ It is **not** a project template you fork; it is a tool you run against other
21
+ repos, one at a time. The rebrand engine is pure standard library, so the
22
+ installed package has **zero runtime dependencies**.
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ uvx --from template-press press --help # run without installing
28
+ # or
29
+ pip install template-press
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ ```bash
35
+ # preview the plan (writes nothing)
36
+ press rebrand --target ../my-repo --config press-answers.toml --dry-run
37
+
38
+ # apply, verify, and write a receipt
39
+ press rebrand --target ../my-repo --config press-answers.toml
40
+ ```
41
+
42
+ `press-answers.toml` describes the **destination** identity:
43
+
44
+ ```toml
45
+ [answers]
46
+ package_name = "new_pkg"
47
+ repo_name = "new-repo"
48
+ app_name = "newcli"
49
+ author = "Jane Dev"
50
+ email = "jane@example.com"
51
+ owner = "janedev"
52
+ ```
53
+
54
+ Keep your filled-in `press-answers.toml` out of the target (it is transient
55
+ operator input, not committed state). A repo may commit a
56
+ `press/press-answers.example.toml` placeholder to advertise the field
57
+ shape — copy it, fill it, and pass it via `--config`. It has the same
58
+ `[answers]` fields shown above.
59
+
60
+ The target's **current** identity is read from a committed
61
+ `<target>/press/press-source.toml`. On a first run, `--dry-run` prints a proposed
62
+ source-config from discovery; review it and re-run with `--accept-discovery`
63
+ to write and use it.
64
+
65
+ ## How it stays safe
66
+
67
+ - **Config-first identity, discovery as a guard.** The committed source-config
68
+ is authoritative; discovery cross-checks it against the target and *refuses
69
+ to run on a mismatch*, so the press never rewrites the wrong repo.
70
+ - **Verify-then-mark.** After rewriting, a no-leak scan confirms no source
71
+ identity remains. Any leftover ⇒ **exit 1 and no receipt** — a partial
72
+ rebrand is a loud failure, not a false success.
73
+ - **Boundary-safe replacement.** Short tokens (`press`, an owner like `go`)
74
+ match only at word boundaries, so ordinary prose (`compress`, `ongoing`) is
75
+ never touched.
76
+
77
+ ## Exit codes
78
+
79
+ The exit code is the contract:
80
+
81
+ | Code | Meaning |
82
+ |------|---------|
83
+ | `0` | Verified — rebrand complete, no source identity remains, receipt written. |
84
+ | `1` | Leaks found after applying. No receipt; restore with `git -C <target> checkout . && git clean -fd`. |
85
+ | `2` | Precondition/config error (missing target, dirty tree, identity mismatch, existing receipt without `--force`). Nothing written. |
86
+
87
+ `--dry-run` exits `0` after printing the plan without touching the target (no receipt).
88
+
89
+ ## Documentation
90
+
91
+ - Full docs: <https://template-press.readthedocs.io/>
92
+ - Design contract: [`docs/design/0006-external-target-model.md`](docs/design/0006-external-target-model.md)
93
+ - Agent runbooks: [`press-target`](.claude/skills/press-target/SKILL.md) (drive a
94
+ press) and [`rebrand-matrix`](.claude/skills/rebrand-matrix/SKILL.md) (the
95
+ R1/R2/R3 acceptance matrix)
96
+
97
+ ## Contributing
98
+
99
+ See [`AGENTS.md`](AGENTS.md) for the canonical toolchain and verification flow
100
+ (`just setup` → `just check` → commit) and
101
+ [`.github/CONTRIBUTING.md`](.github/CONTRIBUTING.md) for the human contributor
102
+ guide.
103
+
104
+ ## License
105
+
106
+ MIT — see [`LICENSE`](LICENSE).
@@ -0,0 +1,225 @@
1
+ # Copyright (c) 2026, Steve Morin
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ # this software and associated documentation files (the "Software"), to deal in
5
+ # the Software without restriction, including without limitation the rights to
6
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ # the Software, and to permit persons to whom the Software is furnished to do so,
8
+ # subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all
11
+ # copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+
20
+ [project]
21
+ name = "template_press"
22
+ # ITM-070 — static version (ADR-06 cutover). release-please bumps this.
23
+ version = "3.0.0"
24
+ description = "Standalone rebrand utility — press a new identity onto an external target repo"
25
+ readme = "README.md"
26
+ requires-python = ">=3.12"
27
+ license = "MIT" # PEP 639 license expression (ITM-070, round-19).
28
+ authors = [{ name = "Steve Morin", email = "steve.morin@gmail.com" }]
29
+ classifiers = [
30
+ # ITM-070 — round-19. py3.12+ only (per ITM-033 floor); MIT classifier
31
+ # intentionally omitted (PEP 639 license expression above is the single
32
+ # source of truth — duplication would drift).
33
+ "Development Status :: 4 - Beta",
34
+ "Intended Audience :: Developers",
35
+ "Operating System :: OS Independent",
36
+ "Programming Language :: Python :: 3",
37
+ "Programming Language :: Python :: 3.12",
38
+ "Programming Language :: Python :: 3.13",
39
+ "Programming Language :: Python :: 3 :: Only",
40
+ "Topic :: Software Development :: Libraries :: Python Modules",
41
+ ]
42
+ # The rebrand engine is pure standard library (tomllib, re, dataclasses,
43
+ # subprocess) — the shipped utility has ZERO runtime dependencies.
44
+ dependencies = []
45
+
46
+ # ITM-063 — PEP 735 dependency-groups (round-13 decision). Replaces
47
+ # [project.optional-dependencies] dev+docs. Install via `uv sync --group <name>`.
48
+ [dependency-groups]
49
+ dev = [
50
+ "pytest>=9.1.1",
51
+ "pytest-cov>=7.1.0",
52
+ "ty>=0.0.56", # per ADR-03 (pre-1.0; bump as needed)
53
+ "ruff>=0.15.20",
54
+ "cogapp",
55
+ # WL-001 — hook/CI tools pinned via uv.lock (run with `uv run`, not floating
56
+ # `uvx`), so local hooks and CI use the same version and upgrades arrive as
57
+ # reviewable dependabot PRs instead of surprise breakage.
58
+ "bandit>=1.9.4",
59
+ "codespell>=2.4.2",
60
+ "editorconfig-checker>=3.6.1",
61
+ "yamllint>=1.38.0",
62
+ "pip-audit>=2.10.1", # WL-014 — `just audit` + scheduled dep-audit workflow
63
+ "twine>=6.2.0", # WL-004 — dist metadata check in the build-smoke CI job
64
+ "syrupy>=5.4.0", # WL-023 — CLI --help snapshot tests
65
+ ]
66
+
67
+ docs = [
68
+ "sphinx>=7.0.0",
69
+ "sphinx-rtd-theme",
70
+ "furo",
71
+ "myst-parser",
72
+ "sphinx-autodoc-typehints",
73
+ "cogapp",
74
+ "sphinx-copybutton",
75
+ "sphinx-autobuild",
76
+ "sphinxext-opengraph", # https://github.com/wpilibsuite/sphinxext-opengraph
77
+ ]
78
+
79
+
80
+ [project.scripts]
81
+ # Noun-verb CLI (design 0006 §2): press <verb>. `rebrand` is live; `provision`
82
+ # and `status` arrive with the M6 Provision phase.
83
+ press = "template_press.press_cli:main"
84
+ # Alias escape hatch (design 0004 §2): a legacy `press` exists on PyPI, so
85
+ # ship `tpress` as a collision-proof alias console script.
86
+ tpress = "template_press.press_cli:main"
87
+
88
+ # ITM-072 — package metadata URLs (round 19).
89
+ [project.urls]
90
+ Homepage = "https://github.com/smorinlabs/template-press"
91
+ Repository = "https://github.com/smorinlabs/template-press"
92
+ Documentation = "https://template-press.readthedocs.io/en/latest/"
93
+ Changelog = "https://github.com/smorinlabs/template-press/blob/main/CHANGELOG.md"
94
+ Issues = "https://github.com/smorinlabs/template-press/issues"
95
+
96
+ # ITM-073 — uv_build (ADR-06 cutover; replaces Hatchling + hatch-vcs).
97
+ [build-system]
98
+ requires = ["uv_build>=0.5,<1.0"]
99
+ build-backend = "uv_build"
100
+
101
+ # ITM-074 — uv_build module configuration.
102
+ # Template uses a flat layout (package at repo root, not src/) — must set
103
+ # module-root = "" so uv_build doesn't expect src/template_press/.
104
+ [tool.uv.build-backend]
105
+ module-name = "template_press"
106
+ # src/ layout (Decision 1) — package now lives at src/template_press/.
107
+ module-root = "src"
108
+
109
+ [tool.ruff]
110
+ # ITM-018 reconciled config (round-2). Merge: template's stricter rule
111
+ # selection + source's ignore rationale. target-version reflects ITM-033
112
+ # Python-floor bump.
113
+ target-version = "py312"
114
+ line-length = 88
115
+
116
+ lint.select = [
117
+ "E", # pycodestyle errors
118
+ "F", # pyflakes
119
+ "I", # isort
120
+ "B", # flake8-bugbear
121
+ "C4", # flake8-comprehensions
122
+ "UP", # pyupgrade
123
+ "N", # pep8-naming
124
+ "RUF", # Ruff-specific rules
125
+ "W", # pycodestyle warnings
126
+ "YTT", # flake8-2020
127
+ "S", # flake8-bandit
128
+ ]
129
+
130
+ # Source's ignore rationale (round-2): line-length handled by formatter;
131
+ # template-applicable subset adopted (B904/B007 omitted — source-specific
132
+ # patterns that don't apply here).
133
+ lint.ignore = [
134
+ "E501", # line too long (formatter owns this)
135
+ "W293", # whitespace on blank line (formatter owns trim_trailing)
136
+ ]
137
+
138
+ # Allow autofix behavior for specific rules
139
+ fix = true
140
+ lint.unfixable = [] # Rules that should not be fixed automatically
141
+
142
+ # Exclude files/folders
143
+ exclude = [
144
+ ".git",
145
+ ".venv",
146
+ "__pycache__",
147
+ "build",
148
+ "dist",
149
+ "docs/source/conf.py",
150
+ "src/template_press/_version.py",
151
+ "prototypes", # P0004 template-press engine prototypes (own lint config in template-press)
152
+ ]
153
+ # Per-file-ignores
154
+ [tool.ruff.lint.pycodestyle]
155
+ max-line-length = 88
156
+ [tool.ruff.lint.per-file-ignores]
157
+ "__init__.py" = ["F401"] # Ignore unused imports in __init__.py files
158
+ "tests/*" = ["S101", "S105", "S106"] # Ignore assert statements in tests
159
+ # S101: assert statements removed under -O — security risk if assert is the only gate.
160
+ # S105/S106: hardcoded password strings/variables in code.
161
+
162
+ # ITM-018 — explicit format config (round-2; mirrors source). Values match Ruff
163
+ # defaults but being explicit prevents silent drift if defaults change.
164
+ [tool.ruff.format]
165
+ quote-style = "double"
166
+ indent-style = "space"
167
+ line-ending = "auto"
168
+
169
+ # Pyright/Pylance (IDE) strict type checking — formerly pyrightconfig.json.
170
+ # CI typechecking runs ty (ADR-03); this section drives the VS Code Pyright
171
+ # extension and ad-hoc `pyright` runs.
172
+ [tool.pyright]
173
+ include = ["src/template_press", "tests"]
174
+ exclude = ["**/node_modules", "**/__pycache__"]
175
+ defineConstant = { DEBUG = true }
176
+ typeCheckingMode = "strict"
177
+ pythonVersion = "3.12"
178
+ useLibraryCodeForTypes = true
179
+ reportMissingImports = true
180
+ reportMissingTypeStubs = true
181
+ reportUnknownParameterType = true
182
+ reportUnknownArgumentType = true
183
+ reportUnknownLambdaType = true
184
+ reportUnknownVariableType = true
185
+ reportUnknownMemberType = true
186
+ reportMissingTypeArgument = true
187
+ reportInvalidTypeVarUse = true
188
+ reportUnnecessaryIsInstance = true
189
+ reportUnnecessaryCast = true
190
+ reportUnnecessaryComparison = true
191
+ reportDuplicateImport = true
192
+ reportPrivateUsage = "warning"
193
+ reportUnusedImport = true
194
+ reportUnusedVariable = true
195
+ reportUnusedFunction = true
196
+ reportUnusedClass = true
197
+
198
+ # ITM-027 — bandit config (round 3). Tests legitimately use `assert`
199
+ # (pytest convention) — exclude_dirs covers tests/.
200
+ [tool.bandit]
201
+ exclude_dirs = ["tests", ".venv", "build", "dist"]
202
+ skips = [] # No global skips; add with rationale only.
203
+
204
+ [tool.pytest.ini_options]
205
+ testpaths = ["tests"]
206
+ python_files = ["test_*.py"]
207
+ # Marker taxonomy (ITM-046, round-9 decision).
208
+ # `live` = requires external services; `slow` = takes >1s.
209
+ # Default `addopts` excludes both — run full suite with `pytest -m ""`.
210
+ markers = ["live: requires external services", "slow: takes >1s"]
211
+ addopts = "-m 'not live and not slow'"
212
+ strict_markers = true
213
+
214
+ # ITM-015 — codespell. US English baseline (default builtin="clear,rare"); empty
215
+ # ignore list — add terms only when intentional. Decided round 9.
216
+ [tool.codespell]
217
+ skip = ".git,.venv,build,dist,node_modules,uv.lock,bun.lock,*.lock,docs/_build,*.svg,*.cast,.ruff_cache,.pytest_cache,.mypy_cache,.ty_cache,*.log"
218
+ # *.log: raw run output, not prose.
219
+ ignore-words-list = "ans,porjects"
220
+ # ans: variable name used in prompt-handling code; codespell wants to "fix"
221
+ # it to "and". Whitelist to keep the short variable name.
222
+ # porjects: intentional typo — the did-you-mean example in cli/groups.py and
223
+ # its test ("press porjects" must suggest "projects").
224
+ check-filenames = true
225
+ check-hidden = false
@@ -0,0 +1,24 @@
1
+ # Copyright (c) 2025, Steve Morin
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ # this software and associated documentation files (the "Software"), to deal in
5
+ # the Software without restriction, including without limitation the rights to
6
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ # the Software, and to permit persons to whom the Software is furnished to do so,
8
+ # subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all
11
+ # copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+
20
+ """template-press — a standalone external-target rebrand utility."""
21
+
22
+ from importlib import metadata
23
+
24
+ __version__ = metadata.version("template_press")
@@ -0,0 +1,50 @@
1
+ """press — the template-press command line.
2
+
3
+ Noun-verb dispatcher (design 0006): `press rebrand --target …` presses an
4
+ identity onto an external target repo. `provision` and `status` are reserved
5
+ for the M6 Provision phase and currently exit 2 with a pointer.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import sys
11
+
12
+ from template_press import __version__
13
+ from template_press.rebrand import cli as rebrand_cli
14
+
15
+ _RESERVED = {"provision", "status"}
16
+
17
+ _USAGE = """\
18
+ usage: press <command> [options]
19
+
20
+ commands:
21
+ rebrand press an identity onto a target repo (press rebrand --help)
22
+ provision configure a target's features (coming in M6)
23
+ status report a target's provisioned state (coming in M6)
24
+ """
25
+
26
+
27
+ def main(argv: list[str] | None = None) -> int:
28
+ args = list(sys.argv[1:] if argv is None else argv)
29
+ if args and args[0] in ("-V", "--version"):
30
+ print(f"press {__version__}")
31
+ return 0
32
+ if not args or args[0] in ("-h", "--help"):
33
+ print(_USAGE)
34
+ return 0
35
+ verb, rest = args[0], args[1:]
36
+ if verb == "rebrand":
37
+ return rebrand_cli.main(rest)
38
+ if verb in _RESERVED:
39
+ print(
40
+ f"error: '{verb}' is part of the Provision phase and is not "
41
+ f"available yet (coming in M6).",
42
+ file=sys.stderr,
43
+ )
44
+ return 2
45
+ print(f"error: unknown command {verb!r}\n\n{_USAGE}", file=sys.stderr)
46
+ return 2
47
+
48
+
49
+ if __name__ == "__main__":
50
+ raise SystemExit(main())
File without changes