techtree 0.1.0__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.
- techtree/__init__.py +35 -0
- techtree/__main__.py +14 -0
- techtree/canonical.py +239 -0
- techtree/catalog/__init__.py +25 -0
- techtree/catalog/repository.py +400 -0
- techtree/catalog/service.py +419 -0
- techtree/cli/__init__.py +1 -0
- techtree/cli/app.py +416 -0
- techtree/cli/commands/__init__.py +1 -0
- techtree/cli/commands/climb.py +1223 -0
- techtree/cli/commands/doctor.py +147 -0
- techtree/cli/commands/engine.py +207 -0
- techtree/cli/commands/proof.py +556 -0
- techtree/cli/commands/publish.py +447 -0
- techtree/cli/commands/release.py +303 -0
- techtree/cli/commands/run.py +1067 -0
- techtree/cli/commands/setup.py +181 -0
- techtree/cli/commands/skill.py +221 -0
- techtree/cli/commands/uplift.py +698 -0
- techtree/cli/commands/withdraw.py +212 -0
- techtree/cli/confirm.py +47 -0
- techtree/cli/context.py +96 -0
- techtree/cli/invoke.py +220 -0
- techtree/cli/output.py +280 -0
- techtree/constants.py +138 -0
- techtree/crypto.py +128 -0
- techtree/doctor/__init__.py +1 -0
- techtree/doctor/checks.py +675 -0
- techtree/doctor/execution_checks.py +435 -0
- techtree/doctor/service.py +326 -0
- techtree/drafts/__init__.py +32 -0
- techtree/drafts/source.py +146 -0
- techtree/drafts/store.py +992 -0
- techtree/engines/__init__.py +1 -0
- techtree/engines/bundle.py +251 -0
- techtree/engines/installer.py +679 -0
- techtree/engines/registry.py +235 -0
- techtree/engines/runner.py +170 -0
- techtree/errors.py +262 -0
- techtree/fs.py +234 -0
- techtree/harness.py +108 -0
- techtree/identity/__init__.py +41 -0
- techtree/identity/models.py +113 -0
- techtree/identity/service.py +199 -0
- techtree/identity/store.py +263 -0
- techtree/ids.py +85 -0
- techtree/manifests/__init__.py +39 -0
- techtree/manifests/builder.py +433 -0
- techtree/manifests/compare.py +376 -0
- techtree/models/__init__.py +282 -0
- techtree/models/base.py +201 -0
- techtree/models/campaign.py +484 -0
- techtree/models/catalog.py +227 -0
- techtree/models/cli.py +151 -0
- techtree/models/climb.py +254 -0
- techtree/models/data_policy.py +130 -0
- techtree/models/engine.py +156 -0
- techtree/models/episode_receipt.py +130 -0
- techtree/models/evaluation_backend.py +113 -0
- techtree/models/experiment.py +154 -0
- techtree/models/run.py +214 -0
- techtree/models/skill.py +156 -0
- techtree/models/uplift_report.py +158 -0
- techtree/models/validation.py +299 -0
- techtree/paths.py +116 -0
- techtree/presentation/__init__.py +31 -0
- techtree/presentation/build.py +1242 -0
- techtree/presentation/compact.py +246 -0
- techtree/presentation/evidence.py +169 -0
- techtree/presentation/models.py +358 -0
- techtree/presentation/rich.py +312 -0
- techtree/presentation/sanitize.py +156 -0
- techtree/publication/__init__.py +44 -0
- techtree/publication/address.py +180 -0
- techtree/publication/coordinates.py +26 -0
- techtree/publication/journal.py +212 -0
- techtree/publication/keccak.py +183 -0
- techtree/publication/models.py +209 -0
- techtree/publication/offer.py +35 -0
- techtree/publication/service.py +618 -0
- techtree/publication/transport.py +296 -0
- techtree/publication/verify.py +242 -0
- techtree/publication/withdraw.py +156 -0
- techtree/py.typed +0 -0
- techtree/receipts/__init__.py +52 -0
- techtree/receipts/bundle.py +578 -0
- techtree/receipts/compare.py +1065 -0
- techtree/receipts/episode.py +672 -0
- techtree/receipts/execution.py +630 -0
- techtree/receipts/observed.py +474 -0
- techtree/receipts/set.py +336 -0
- techtree/receipts/uplift.py +655 -0
- techtree/receipts/verify.py +1055 -0
- techtree/release/__init__.py +9 -0
- techtree/release/bootstrap.py +509 -0
- techtree/release/checks.py +376 -0
- techtree/release/document.py +125 -0
- techtree/release/generate.py +221 -0
- techtree/release/models.py +293 -0
- techtree/release/provenance.py +109 -0
- techtree/resources/catalog/campaigns/hello-world-climb.json +1 -0
- techtree/resources/catalog/catalog.json +32 -0
- techtree/resources/catalog/climbs/hello-world-climb.json +1 -0
- techtree/resources/catalog/data-policies/hello-world-climb.json +1 -0
- techtree/resources/catalog/taskset-validations/hello-world-climb.json +1 -0
- techtree/resources/catalog/validation-evidence/hello-world-climb.json +1 -0
- techtree/resources/engines/default/engine.json +20 -0
- techtree/resources/engines/default/packages/procedure-transfer-v1/procedure_transfer_v1/__init__.py +7 -0
- techtree/resources/engines/default/packages/procedure-transfer-v1/procedure_transfer_v1/algorithm.py +136 -0
- techtree/resources/engines/default/packages/procedure-transfer-v1/procedure_transfer_v1/dataset.py +156 -0
- techtree/resources/engines/default/packages/procedure-transfer-v1/procedure_transfer_v1/env.py +48 -0
- techtree/resources/engines/default/packages/procedure-transfer-v1/procedure_transfer_v1/taskset.py +163 -0
- techtree/resources/engines/default/packages/procedure-transfer-v1/pyproject.toml +13 -0
- techtree/resources/engines/default/pyproject.toml +23 -0
- techtree/resources/engines/default/tools/inspect_taskset.py +124 -0
- techtree/resources/engines/default/tools/normalize_eval_output.py +470 -0
- techtree/resources/engines/default/tools/normalize_validation.py +222 -0
- techtree/resources/engines/default/uv.lock +1758 -0
- techtree/resources/harness/hermes-agent-0.19.0.json +69 -0
- techtree/resources/release/build-provenance.json +4 -0
- techtree/resources/release/release-core.json +24 -0
- techtree/runs/__init__.py +31 -0
- techtree/runs/artifacts.py +750 -0
- techtree/runs/child_registry.py +228 -0
- techtree/runs/events.py +478 -0
- techtree/runs/executor.py +140 -0
- techtree/runs/fake.py +741 -0
- techtree/runs/launcher.py +253 -0
- techtree/runs/machine.py +489 -0
- techtree/runs/real.py +789 -0
- techtree/runs/service.py +616 -0
- techtree/runs/store.py +555 -0
- techtree/runs/validation.py +259 -0
- techtree/runs/variants.py +684 -0
- techtree/settings.py +143 -0
- techtree/skills/__init__.py +14 -0
- techtree/skills/archive.py +282 -0
- techtree/skills/policy.py +62 -0
- techtree/skills/scanner.py +394 -0
- techtree/skills/service.py +752 -0
- techtree/skills/starter.py +434 -0
- techtree/tasksets/__init__.py +1 -0
- techtree/tasksets/membership.py +269 -0
- techtree/tasksets/provider.py +207 -0
- techtree/tasksets/resolver.py +311 -0
- techtree/tasksets/service.py +484 -0
- techtree/tasksets/verifiers_cli.py +538 -0
- techtree/uplift/__init__.py +20 -0
- techtree/uplift/context.py +544 -0
- techtree/uplift/derive.py +203 -0
- techtree/uplift/public_tasks.py +151 -0
- techtree/uplift/service.py +719 -0
- techtree/uplift/source.py +160 -0
- techtree/verifiers/__init__.py +31 -0
- techtree/verifiers/budget.py +219 -0
- techtree/verifiers/child.py +633 -0
- techtree/verifiers/compiler.py +432 -0
- techtree/verifiers/config.py +365 -0
- techtree/verifiers/credentials.py +321 -0
- techtree/verifiers/image.py +126 -0
- techtree/verifiers/models.py +527 -0
- techtree/verifiers/outputs.py +368 -0
- techtree/verifiers/progress.py +192 -0
- techtree/verifiers/supervisor.py +341 -0
- techtree/verifiers/verify.py +782 -0
- techtree/version.py +39 -0
- techtree/worker/__init__.py +18 -0
- techtree/worker/execute.py +487 -0
- techtree/worker/main.py +57 -0
- techtree-0.1.0.dist-info/METADATA +344 -0
- techtree-0.1.0.dist-info/RECORD +174 -0
- techtree-0.1.0.dist-info/WHEEL +4 -0
- techtree-0.1.0.dist-info/entry_points.txt +3 -0
- techtree-0.1.0.dist-info/licenses/LICENSE +21 -0
techtree/cli/app.py
ADDED
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
"""The Typer application. Spec section 12.4.
|
|
2
|
+
|
|
3
|
+
This module wires and nothing else. It declares the global options, builds the
|
|
4
|
+
:class:`~techtree.cli.context.CliContext` once in the root callback, registers
|
|
5
|
+
the command groups, and closes the last gap in the error boundary: a typed
|
|
6
|
+
failure raised while the context is still being built still owes the caller one
|
|
7
|
+
envelope and one documented exit code.
|
|
8
|
+
|
|
9
|
+
``climb``, ``skill``, ``run``, ``engine``, ``proof``, ``release`` and
|
|
10
|
+
``uplift`` are registered with their real names even where this build
|
|
11
|
+
implements only some of what each will eventually hold. ``publish`` and
|
|
12
|
+
``withdraw`` are commands rather than groups, because each is one thing a
|
|
13
|
+
person does and neither has a second member to hold. A name
|
|
14
|
+
that exists and answers ``not_implemented`` is discoverable and scriptable; a
|
|
15
|
+
name that does not exist yet is indistinguishable from a typo. The reserved
|
|
16
|
+
namespaces — ``program``, ``blueprint``, ``forge``, ``verify``, ``trace``,
|
|
17
|
+
``lab`` — get no group at all, because an empty group in ``--help`` is a
|
|
18
|
+
promise about a shape that has not been decided. ``uplift`` left that list when
|
|
19
|
+
spec section 7.21's improvement commands landed and it stopped being a shape
|
|
20
|
+
nobody had decided.
|
|
21
|
+
|
|
22
|
+
Global options are accepted anywhere on the command line. ``techtree --json
|
|
23
|
+
doctor`` and ``techtree doctor --json`` mean the same thing, because a caller
|
|
24
|
+
scripting the boundary should not have to know that one of those flags belongs
|
|
25
|
+
to a group and the other to a command.
|
|
26
|
+
|
|
27
|
+
Argument parsing happens before any of this. A malformed command line is
|
|
28
|
+
reported by the parser with exit code 2, and a bare ``techtree`` prints help;
|
|
29
|
+
neither reaches a command, and the one-envelope rule covers every invocation
|
|
30
|
+
that does.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from __future__ import annotations
|
|
34
|
+
|
|
35
|
+
import os
|
|
36
|
+
import sys
|
|
37
|
+
from collections.abc import Mapping, Sequence
|
|
38
|
+
from pathlib import Path
|
|
39
|
+
from typing import Annotated, Any, NoReturn
|
|
40
|
+
|
|
41
|
+
import typer
|
|
42
|
+
from typer.core import TyperGroup
|
|
43
|
+
|
|
44
|
+
from techtree.cli.commands.climb import (
|
|
45
|
+
list_climbs_command,
|
|
46
|
+
prepare_climb_command,
|
|
47
|
+
show_climb_command,
|
|
48
|
+
start_climb_command,
|
|
49
|
+
)
|
|
50
|
+
from techtree.cli.commands.doctor import doctor_command
|
|
51
|
+
from techtree.cli.commands.engine import (
|
|
52
|
+
install_engine_command,
|
|
53
|
+
status_engine_command,
|
|
54
|
+
verify_engine_command,
|
|
55
|
+
)
|
|
56
|
+
from techtree.cli.commands.proof import verify_proof_command
|
|
57
|
+
from techtree.cli.commands.publish import publish_run_command
|
|
58
|
+
from techtree.cli.commands.release import (
|
|
59
|
+
info_release_command,
|
|
60
|
+
verify_release_command,
|
|
61
|
+
)
|
|
62
|
+
from techtree.cli.commands.run import (
|
|
63
|
+
cancel_run_command,
|
|
64
|
+
logs_run_command,
|
|
65
|
+
result_run_command,
|
|
66
|
+
status_run_command,
|
|
67
|
+
)
|
|
68
|
+
from techtree.cli.commands.setup import setup_command
|
|
69
|
+
from techtree.cli.commands.skill import starter_skill_command
|
|
70
|
+
from techtree.cli.commands.uplift import (
|
|
71
|
+
context_uplift_command,
|
|
72
|
+
prepare_uplift_command,
|
|
73
|
+
skill_source_uplift_command,
|
|
74
|
+
start_uplift_command,
|
|
75
|
+
)
|
|
76
|
+
from techtree.cli.commands.withdraw import withdraw_run_command
|
|
77
|
+
from techtree.cli.context import build_cli_context
|
|
78
|
+
from techtree.cli.invoke import emit_boundary_failure, failure_envelope
|
|
79
|
+
from techtree.cli.output import write_envelope
|
|
80
|
+
from techtree.errors import (
|
|
81
|
+
TechtreeError,
|
|
82
|
+
UsageError,
|
|
83
|
+
exit_code_for,
|
|
84
|
+
stable_exception_message,
|
|
85
|
+
)
|
|
86
|
+
from techtree.version import package_version
|
|
87
|
+
|
|
88
|
+
__all__ = ["create_app", "main", "root_callback"]
|
|
89
|
+
|
|
90
|
+
#: Namespaces that belong to Techtree but are not part of this product surface
|
|
91
|
+
#: yet. Listed so the choice is visible in code review rather than implied by
|
|
92
|
+
#: absence, and asserted by the CLI contract tests.
|
|
93
|
+
RESERVED_NAMESPACES: tuple[str, ...] = (
|
|
94
|
+
"program",
|
|
95
|
+
"blueprint",
|
|
96
|
+
"forge",
|
|
97
|
+
"verify",
|
|
98
|
+
"uplift",
|
|
99
|
+
"trace",
|
|
100
|
+
"lab",
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
#: Global switches, and the one global option that takes a value. Both lists
|
|
105
|
+
#: exist so a global option can be recognized wherever it appears.
|
|
106
|
+
GLOBAL_FLAGS: frozenset[str] = frozenset(
|
|
107
|
+
{"--json", "--no-color", "--no-input", "--debug", "--version"}
|
|
108
|
+
)
|
|
109
|
+
GLOBAL_VALUE_OPTIONS: frozenset[str] = frozenset({"--home"})
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def hoist_global_options(args: Sequence[str]) -> list[str]:
|
|
113
|
+
"""Move global options to the front of the command line.
|
|
114
|
+
|
|
115
|
+
The parser binds an option to whichever command declares it, and the global
|
|
116
|
+
options are declared once, on the root. Moving them forward is what lets
|
|
117
|
+
them be written last, where a person naturally puts them. Everything after
|
|
118
|
+
a literal ``--`` is left exactly where it is.
|
|
119
|
+
"""
|
|
120
|
+
leading: list[str] = []
|
|
121
|
+
trailing: list[str] = []
|
|
122
|
+
index = 0
|
|
123
|
+
|
|
124
|
+
while index < len(args):
|
|
125
|
+
token = args[index]
|
|
126
|
+
if token == "--":
|
|
127
|
+
trailing.extend(args[index:])
|
|
128
|
+
break
|
|
129
|
+
if token in GLOBAL_FLAGS or any(
|
|
130
|
+
token.startswith(f"{name}=") for name in GLOBAL_VALUE_OPTIONS
|
|
131
|
+
):
|
|
132
|
+
leading.append(token)
|
|
133
|
+
elif token in GLOBAL_VALUE_OPTIONS and index + 1 < len(args):
|
|
134
|
+
leading.extend(args[index : index + 2])
|
|
135
|
+
index += 1
|
|
136
|
+
else:
|
|
137
|
+
trailing.append(token)
|
|
138
|
+
index += 1
|
|
139
|
+
|
|
140
|
+
return [*leading, *trailing]
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class TechtreeGroup(TyperGroup):
|
|
144
|
+
"""Root group that accepts global options anywhere on the command line."""
|
|
145
|
+
|
|
146
|
+
def parse_args(self, ctx: Any, args: list[str]) -> list[str]:
|
|
147
|
+
"""Hoist global options, then parse normally."""
|
|
148
|
+
return list(super().parse_args(ctx, hoist_global_options(args)))
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def version_callback(value: bool) -> None:
|
|
152
|
+
"""Print the package version and exit when ``--version`` is given."""
|
|
153
|
+
if not value:
|
|
154
|
+
return
|
|
155
|
+
typer.echo(package_version())
|
|
156
|
+
raise typer.Exit()
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def root_callback(
|
|
160
|
+
ctx: typer.Context,
|
|
161
|
+
home: Annotated[
|
|
162
|
+
Path | None,
|
|
163
|
+
typer.Option(
|
|
164
|
+
"--home",
|
|
165
|
+
help="Directory Techtree keeps its local state in.",
|
|
166
|
+
metavar="PATH",
|
|
167
|
+
),
|
|
168
|
+
] = None,
|
|
169
|
+
json_output: Annotated[
|
|
170
|
+
bool,
|
|
171
|
+
typer.Option("--json", help="Emit one JSON envelope instead of human output."),
|
|
172
|
+
] = False,
|
|
173
|
+
no_color: Annotated[
|
|
174
|
+
bool,
|
|
175
|
+
typer.Option("--no-color", help="Never colour human output."),
|
|
176
|
+
] = False,
|
|
177
|
+
no_input: Annotated[
|
|
178
|
+
bool,
|
|
179
|
+
typer.Option("--no-input", help="Never prompt; fail instead of asking."),
|
|
180
|
+
] = False,
|
|
181
|
+
debug: Annotated[
|
|
182
|
+
bool,
|
|
183
|
+
typer.Option("--debug", help="Write operational detail to stderr."),
|
|
184
|
+
] = False,
|
|
185
|
+
version: Annotated[
|
|
186
|
+
bool,
|
|
187
|
+
typer.Option(
|
|
188
|
+
"--version",
|
|
189
|
+
callback=version_callback,
|
|
190
|
+
is_eager=True,
|
|
191
|
+
help="Show the Techtree version and exit.",
|
|
192
|
+
),
|
|
193
|
+
] = False,
|
|
194
|
+
) -> None:
|
|
195
|
+
"""Techtree Climb command-line interface."""
|
|
196
|
+
if ctx.invoked_subcommand is None:
|
|
197
|
+
emit_boundary_failure(
|
|
198
|
+
json_output=json_output,
|
|
199
|
+
no_color=no_color,
|
|
200
|
+
command="techtree",
|
|
201
|
+
error=UsageError(
|
|
202
|
+
"no command was given; run `techtree --help` for the available "
|
|
203
|
+
"commands",
|
|
204
|
+
code="no_command",
|
|
205
|
+
),
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
ctx.obj = build_cli_context(
|
|
210
|
+
home=home,
|
|
211
|
+
json_output=json_output,
|
|
212
|
+
no_color=no_color,
|
|
213
|
+
no_input=no_input,
|
|
214
|
+
debug=debug,
|
|
215
|
+
)
|
|
216
|
+
except TechtreeError as error:
|
|
217
|
+
emit_boundary_failure(
|
|
218
|
+
json_output=json_output,
|
|
219
|
+
no_color=no_color,
|
|
220
|
+
command=ctx.invoked_subcommand,
|
|
221
|
+
error=error,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def create_app() -> typer.Typer:
|
|
226
|
+
"""Construct and return the application."""
|
|
227
|
+
app = typer.Typer(
|
|
228
|
+
name="techtree",
|
|
229
|
+
cls=TechtreeGroup,
|
|
230
|
+
help="Techtree Climb command-line interface.",
|
|
231
|
+
no_args_is_help=True,
|
|
232
|
+
add_completion=False,
|
|
233
|
+
pretty_exceptions_enable=False,
|
|
234
|
+
)
|
|
235
|
+
app.callback(invoke_without_command=True)(root_callback)
|
|
236
|
+
app.command("doctor", help="Check that this machine is ready to run a Climb.")(
|
|
237
|
+
doctor_command
|
|
238
|
+
)
|
|
239
|
+
app.command("setup", help="Prepare this machine to run a Climb.")(setup_command)
|
|
240
|
+
# Publishing and withdrawing are top-level commands rather than members of
|
|
241
|
+
# ``proof`` (decisions 0038's founder ruling of 2026-08-27). ``proof`` is
|
|
242
|
+
# where a person checks evidence offline; these two are the only commands in
|
|
243
|
+
# Techtree that send anything anywhere, and burying them one level down made
|
|
244
|
+
# them read as a variation on checking a proof.
|
|
245
|
+
app.command(
|
|
246
|
+
"publish",
|
|
247
|
+
help="Publish a verified run's proof to the public run log.",
|
|
248
|
+
)(publish_run_command)
|
|
249
|
+
app.command(
|
|
250
|
+
"withdraw",
|
|
251
|
+
help="Withdraw a published entry from the public run log.",
|
|
252
|
+
)(withdraw_run_command)
|
|
253
|
+
|
|
254
|
+
app.add_typer(_climb_app(), name="climb")
|
|
255
|
+
app.add_typer(_skill_app(), name="skill")
|
|
256
|
+
app.add_typer(_run_app(), name="run")
|
|
257
|
+
app.add_typer(_engine_app(), name="engine")
|
|
258
|
+
app.add_typer(_proof_app(), name="proof")
|
|
259
|
+
app.add_typer(_release_app(), name="release")
|
|
260
|
+
app.add_typer(_uplift_app(), name="uplift")
|
|
261
|
+
return app
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def main() -> None:
|
|
265
|
+
"""Run the application."""
|
|
266
|
+
try:
|
|
267
|
+
create_app()()
|
|
268
|
+
except TechtreeError as error:
|
|
269
|
+
_last_resort(error)
|
|
270
|
+
except Exception as unexpected:
|
|
271
|
+
_last_resort(
|
|
272
|
+
TechtreeError(
|
|
273
|
+
stable_exception_message(unexpected),
|
|
274
|
+
code="internal_error",
|
|
275
|
+
details={"exception_type": type(unexpected).__name__},
|
|
276
|
+
)
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
# ---------------------------------------------------------------------------
|
|
281
|
+
# Command groups
|
|
282
|
+
# ---------------------------------------------------------------------------
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def _climb_app() -> typer.Typer:
|
|
286
|
+
app = typer.Typer(help="Browse and enter Climbs.", no_args_is_help=True)
|
|
287
|
+
|
|
288
|
+
app.command("list", help="Show the Climbs available in this build.")(
|
|
289
|
+
list_climbs_command
|
|
290
|
+
)
|
|
291
|
+
app.command(
|
|
292
|
+
"show",
|
|
293
|
+
help="Show what a Climb measures, the data rights it carries, and "
|
|
294
|
+
"whether this machine can run it.",
|
|
295
|
+
)(show_climb_command)
|
|
296
|
+
|
|
297
|
+
app.command("prepare", help="Prepare a skill for submission to a Climb.")(
|
|
298
|
+
prepare_climb_command
|
|
299
|
+
)
|
|
300
|
+
app.command("start", help="Start a prepared submission running.")(
|
|
301
|
+
start_climb_command
|
|
302
|
+
)
|
|
303
|
+
return app
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _run_app() -> typer.Typer:
|
|
307
|
+
app = typer.Typer(help="Follow and control your runs.", no_args_is_help=True)
|
|
308
|
+
|
|
309
|
+
app.command("status", help="Show how a run is progressing.")(status_run_command)
|
|
310
|
+
app.command("logs", help="Show the log output of a run.")(logs_run_command)
|
|
311
|
+
app.command("cancel", help="Stop a run that is still in progress.")(
|
|
312
|
+
cancel_run_command
|
|
313
|
+
)
|
|
314
|
+
app.command("result", help="Show the finished report for a run.")(
|
|
315
|
+
result_run_command
|
|
316
|
+
)
|
|
317
|
+
return app
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def _skill_app() -> typer.Typer:
|
|
321
|
+
app = typer.Typer(help="Obtain the Skills a release names.", no_args_is_help=True)
|
|
322
|
+
|
|
323
|
+
app.command(
|
|
324
|
+
"starter",
|
|
325
|
+
help="Put the starter Skill this release pins on this machine.",
|
|
326
|
+
)(starter_skill_command)
|
|
327
|
+
return app
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def _uplift_app() -> typer.Typer:
|
|
331
|
+
app = typer.Typer(help="Improve a Skill from a finished run.", no_args_is_help=True)
|
|
332
|
+
|
|
333
|
+
app.command(
|
|
334
|
+
"context",
|
|
335
|
+
help="Export the sanitized improvement context for a finished run.",
|
|
336
|
+
)(context_uplift_command)
|
|
337
|
+
app.command(
|
|
338
|
+
"skill-source",
|
|
339
|
+
help="Show the verified text of the Skill a finished run measured.",
|
|
340
|
+
)(skill_source_uplift_command)
|
|
341
|
+
app.command(
|
|
342
|
+
"prepare",
|
|
343
|
+
help="Prepare a comparison between a run's Skill and a revision of it.",
|
|
344
|
+
)(prepare_uplift_command)
|
|
345
|
+
app.command("start", help="Start a prepared Skill-against-Skill comparison.")(
|
|
346
|
+
start_uplift_command
|
|
347
|
+
)
|
|
348
|
+
return app
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def _proof_app() -> typer.Typer:
|
|
352
|
+
app = typer.Typer(help="Check local proofs.", no_args_is_help=True)
|
|
353
|
+
|
|
354
|
+
app.command(
|
|
355
|
+
"verify",
|
|
356
|
+
help="Check a local proof offline, from the bytes the run stored.",
|
|
357
|
+
)(verify_proof_command)
|
|
358
|
+
return app
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def _release_app() -> typer.Typer:
|
|
362
|
+
app = typer.Typer(
|
|
363
|
+
help="Show and check the release this build belongs to.",
|
|
364
|
+
no_args_is_help=True,
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
app.command("info", help="Show the release coordinates this build carries.")(
|
|
368
|
+
info_release_command
|
|
369
|
+
)
|
|
370
|
+
app.command(
|
|
371
|
+
"verify",
|
|
372
|
+
help="Check that every release coordinate still names what it claims.",
|
|
373
|
+
)(verify_release_command)
|
|
374
|
+
return app
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def _engine_app() -> typer.Typer:
|
|
378
|
+
app = typer.Typer(
|
|
379
|
+
help="Install and check the evaluation engine.", no_args_is_help=True
|
|
380
|
+
)
|
|
381
|
+
app.command("install", help="Install the evaluation engine on this machine.")(
|
|
382
|
+
install_engine_command
|
|
383
|
+
)
|
|
384
|
+
app.command("status", help="Show which evaluation engine is installed and in use.")(
|
|
385
|
+
status_engine_command
|
|
386
|
+
)
|
|
387
|
+
app.command("verify", help="Confirm the installed evaluation engine is intact.")(
|
|
388
|
+
verify_engine_command
|
|
389
|
+
)
|
|
390
|
+
return app
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
# ---------------------------------------------------------------------------
|
|
394
|
+
# Last-resort boundary
|
|
395
|
+
# ---------------------------------------------------------------------------
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def _last_resort(error: TechtreeError) -> NoReturn:
|
|
399
|
+
"""Report a failure that escaped every command's own boundary.
|
|
400
|
+
|
|
401
|
+
Nothing here can consult a ``CliContext`` — the failure may be the reason
|
|
402
|
+
there is not one — so the output mode is read straight off the command line
|
|
403
|
+
and the environment. This path is for defects; every ordinary failure is
|
|
404
|
+
handled where the context exists.
|
|
405
|
+
"""
|
|
406
|
+
write_envelope(
|
|
407
|
+
failure_envelope(command="techtree", error=error),
|
|
408
|
+
json_output=_machine_mode(sys.argv[1:], os.environ),
|
|
409
|
+
no_color=True,
|
|
410
|
+
)
|
|
411
|
+
raise SystemExit(exit_code_for(error))
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def _machine_mode(argv: Sequence[str], environ: Mapping[str, str]) -> bool:
|
|
415
|
+
"""Decide the output mode without a parsed command line."""
|
|
416
|
+
return "--json" in argv or environ.get("TECHTREE_OUTPUT_MODE") == "json"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Command functions. Each one is plumbing: parse, delegate, emit, exit."""
|