proofstep-cli 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.
- proofstep_cli/__init__.py +8 -0
- proofstep_cli/calibration.py +318 -0
- proofstep_cli/calibration_store.py +215 -0
- proofstep_cli/commands/__init__.py +1 -0
- proofstep_cli/main.py +562 -0
- proofstep_cli/publish.py +541 -0
- proofstep_cli/py.typed +0 -0
- proofstep_cli/registry.py +234 -0
- proofstep_cli/render/__init__.py +1 -0
- proofstep_cli/render/calibration.py +127 -0
- proofstep_cli/render/markdown.py +304 -0
- proofstep_cli/render/report.py +193 -0
- proofstep_cli/render/terminal.py +287 -0
- proofstep_cli/runner.py +374 -0
- proofstep_cli/suite/__init__.py +1 -0
- proofstep_cli/suite/loader.py +377 -0
- proofstep_cli/suite/schema.py +343 -0
- proofstep_cli-0.1.0.dist-info/METADATA +58 -0
- proofstep_cli-0.1.0.dist-info/RECORD +21 -0
- proofstep_cli-0.1.0.dist-info/WHEEL +4 -0
- proofstep_cli-0.1.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
"""The suite YAML schema.
|
|
2
|
+
|
|
3
|
+
Versioned by `apiVersion` from the first release. Suite files live in users' repos
|
|
4
|
+
and must not break on upgrade, which costs nothing to promise now and is expensive
|
|
5
|
+
to retrofit later (docs/OPEN_QUESTIONS.md Q11).
|
|
6
|
+
|
|
7
|
+
`extra="forbid"` throughout: a typo'd key that is silently ignored produces a suite
|
|
8
|
+
which looks configured and is not. `capture_args: true` misspelled as `capture_arg`
|
|
9
|
+
should be an error, not a shrug.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import difflib
|
|
15
|
+
from typing import Any, Literal
|
|
16
|
+
|
|
17
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
18
|
+
|
|
19
|
+
API_VERSION = "proofstep.dev/v1"
|
|
20
|
+
|
|
21
|
+
ReportFormat = Literal["terminal", "json"]
|
|
22
|
+
|
|
23
|
+
EVALUATOR_TYPES = (
|
|
24
|
+
"exact_match",
|
|
25
|
+
"json_schema",
|
|
26
|
+
"regex",
|
|
27
|
+
"contains",
|
|
28
|
+
"length",
|
|
29
|
+
"numeric_range",
|
|
30
|
+
"set_comparison",
|
|
31
|
+
"llm_judge",
|
|
32
|
+
"trajectory",
|
|
33
|
+
"operational",
|
|
34
|
+
"classification",
|
|
35
|
+
"ranking",
|
|
36
|
+
"calibration",
|
|
37
|
+
"discrimination",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class DatasetRef(BaseModel):
|
|
42
|
+
model_config = ConfigDict(extra="forbid")
|
|
43
|
+
|
|
44
|
+
name: str | None = None
|
|
45
|
+
version: str = "latest-locked"
|
|
46
|
+
path: str | None = Field(
|
|
47
|
+
default=None, description="Local JSONL/CSV file; mutually exclusive with `name`"
|
|
48
|
+
)
|
|
49
|
+
split: str | None = None
|
|
50
|
+
limit: int | None = Field(default=None, ge=1)
|
|
51
|
+
|
|
52
|
+
@model_validator(mode="after")
|
|
53
|
+
def _one_source(self) -> DatasetRef:
|
|
54
|
+
if bool(self.name) == bool(self.path):
|
|
55
|
+
msg = "dataset needs exactly one of `name` (server) or `path` (local file)"
|
|
56
|
+
raise ValueError(msg)
|
|
57
|
+
return self
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def is_local(self) -> bool:
|
|
61
|
+
return self.path is not None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class TaskRef(BaseModel):
|
|
65
|
+
model_config = ConfigDict(extra="forbid")
|
|
66
|
+
|
|
67
|
+
entrypoint: str = Field(pattern=r"^[\w.]+:[\w.]+$", description="module:function")
|
|
68
|
+
timeout_s: float = Field(default=120.0, gt=0)
|
|
69
|
+
retries: int = Field(default=2, ge=0)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class Execution(BaseModel):
|
|
73
|
+
model_config = ConfigDict(extra="forbid")
|
|
74
|
+
|
|
75
|
+
concurrency: int = Field(default=8, ge=1, le=256)
|
|
76
|
+
judge_concurrency: int = Field(default=4, ge=1, le=64)
|
|
77
|
+
max_error_rate: float = Field(default=0.10, ge=0, le=1)
|
|
78
|
+
seed: int = 42
|
|
79
|
+
slice_by: list[str] = Field(default_factory=list)
|
|
80
|
+
max_cost: float | None = Field(default=None, gt=0)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class Scale(BaseModel):
|
|
84
|
+
model_config = ConfigDict(extra="forbid")
|
|
85
|
+
|
|
86
|
+
min: int = 1
|
|
87
|
+
max: int = 5
|
|
88
|
+
normalize: bool = True
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class Calibration(BaseModel):
|
|
92
|
+
"""One judge's calibration: which labelled set, and what it has to achieve.
|
|
93
|
+
|
|
94
|
+
Thresholds live with the evaluator rather than with the gate because they are a
|
|
95
|
+
property of the measurement — an unsubscribe judge needs a tighter false-pass rate
|
|
96
|
+
than a tone judge, whatever gate set happens to reference it. Whether falling short
|
|
97
|
+
*blocks* is the gate set's decision (`calibration.require`).
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
model_config = ConfigDict(extra="forbid")
|
|
101
|
+
|
|
102
|
+
#: Path to the labelled JSONL, relative to the suite file.
|
|
103
|
+
dataset: str
|
|
104
|
+
version: str = "latest-locked"
|
|
105
|
+
#: Labels that count as "passing", which is what makes the false-pass and false-fail
|
|
106
|
+
#: rates computable. Without it those two numbers are unmeasured, and they are the
|
|
107
|
+
#: ones that matter most.
|
|
108
|
+
passing_labels: list[str] = Field(default_factory=list)
|
|
109
|
+
min_agreement: float | None = Field(default=None, ge=0, le=1)
|
|
110
|
+
min_kappa: float | None = Field(default=None, ge=-1, le=1)
|
|
111
|
+
max_false_pass_rate: float | None = Field(default=None, ge=0, le=1)
|
|
112
|
+
max_false_fail_rate: float | None = Field(default=None, ge=0, le=1)
|
|
113
|
+
min_examples: int | None = Field(default=None, ge=1)
|
|
114
|
+
min_per_class: int | None = Field(default=None, ge=1)
|
|
115
|
+
allow_position_bias: bool = False
|
|
116
|
+
required: bool = False
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class CalibrationPolicy(BaseModel):
|
|
120
|
+
"""Suite-level calibration settings.
|
|
121
|
+
|
|
122
|
+
`require` is the enforcement switch: `false` means an uncalibrated gated judge only
|
|
123
|
+
warns, `true` means it fails the run, and a mapping overrides the thresholds that
|
|
124
|
+
apply when an evaluator does not state its own.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
model_config = ConfigDict(extra="forbid")
|
|
128
|
+
|
|
129
|
+
#: Where calibration records live, relative to the suite file. They are committed to
|
|
130
|
+
#: git so `require` works in CI with no server — see calibration_store.py.
|
|
131
|
+
directory: str = "calibrations"
|
|
132
|
+
require: bool | dict[str, Any] = False
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class EvaluatorSpec(BaseModel):
|
|
136
|
+
model_config = ConfigDict(extra="forbid")
|
|
137
|
+
|
|
138
|
+
name: str = Field(pattern=r"^[a-z][a-z0-9_]*$")
|
|
139
|
+
type: str
|
|
140
|
+
|
|
141
|
+
# Deterministic
|
|
142
|
+
field: str | None = None
|
|
143
|
+
expected_field: str | None = None
|
|
144
|
+
normalize: Literal["none", "case", "whitespace", "punctuation", "all"] = "none"
|
|
145
|
+
schema_: dict[str, Any] | None = Field(default=None, alias="schema")
|
|
146
|
+
schema_path: str | None = None
|
|
147
|
+
allow: list[str] = Field(default_factory=list)
|
|
148
|
+
deny: list[str] = Field(default_factory=list)
|
|
149
|
+
substrings: list[str] = Field(default_factory=list)
|
|
150
|
+
mode: str | None = None
|
|
151
|
+
case_sensitive: bool = True
|
|
152
|
+
unit: Literal["chars", "words"] = "chars"
|
|
153
|
+
minimum: float | None = None
|
|
154
|
+
maximum: float | None = None
|
|
155
|
+
inclusive: bool = True
|
|
156
|
+
|
|
157
|
+
# Judge
|
|
158
|
+
rubric: str | None = None
|
|
159
|
+
rubric_path: str | None = None
|
|
160
|
+
model: str | None = None
|
|
161
|
+
temperature: float = 0.0
|
|
162
|
+
seed: int | None = 42
|
|
163
|
+
inputs: list[str] = Field(default_factory=list)
|
|
164
|
+
labels: list[str] = Field(default_factory=list)
|
|
165
|
+
#: Which of `labels` count as a pass, turning a classify judge's label into a gateable rate.
|
|
166
|
+
#: Declared on the evaluator rather than only under `calibration`, because it changes what the
|
|
167
|
+
#: judge scores and not merely how it is calibrated.
|
|
168
|
+
passing_labels: list[str] = Field(default_factory=list)
|
|
169
|
+
scale: Scale = Field(default_factory=Scale)
|
|
170
|
+
votes: int = 1
|
|
171
|
+
timeout_s: float = 60.0
|
|
172
|
+
max_retries: int = 2
|
|
173
|
+
calibration: Calibration | None = None
|
|
174
|
+
|
|
175
|
+
# Trajectory
|
|
176
|
+
policy: str | None = None
|
|
177
|
+
|
|
178
|
+
# Statistical / operational
|
|
179
|
+
prediction_field: str | None = None
|
|
180
|
+
label_field: str | None = None
|
|
181
|
+
averaging: Literal["macro", "micro", "weighted"] = "macro"
|
|
182
|
+
k: int = 10
|
|
183
|
+
ranking_field: str | None = None
|
|
184
|
+
relevant_field: str | None = None
|
|
185
|
+
confidence_field: str | None = None
|
|
186
|
+
#: A boolean output field saying whether the prediction was right, for cases where
|
|
187
|
+
#: correctness is not a prediction/label comparison.
|
|
188
|
+
correct_field: str | None = None
|
|
189
|
+
percentiles: list[int] = Field(default_factory=lambda: [50, 95, 99])
|
|
190
|
+
|
|
191
|
+
@model_validator(mode="after")
|
|
192
|
+
def _known_type(self) -> EvaluatorSpec:
|
|
193
|
+
if self.type not in EVALUATOR_TYPES:
|
|
194
|
+
closest = _closest(self.type, EVALUATOR_TYPES)
|
|
195
|
+
hint = f" Did you mean {closest!r}?" if closest else ""
|
|
196
|
+
msg = (
|
|
197
|
+
f"evaluator {self.name!r} has unknown type {self.type!r}.{hint} "
|
|
198
|
+
f"Valid types: {', '.join(sorted(EVALUATOR_TYPES))}"
|
|
199
|
+
)
|
|
200
|
+
raise ValueError(msg)
|
|
201
|
+
return self
|
|
202
|
+
|
|
203
|
+
@model_validator(mode="after")
|
|
204
|
+
def _judges_declare_inputs(self) -> EvaluatorSpec:
|
|
205
|
+
if self.type != "llm_judge":
|
|
206
|
+
return self
|
|
207
|
+
if not self.inputs:
|
|
208
|
+
# A judge handed the whole example can read `expected` and grade against
|
|
209
|
+
# the answer key. Enumerating inputs is the only thing that prevents it.
|
|
210
|
+
msg = (
|
|
211
|
+
f"judge {self.name!r} must declare `inputs`: without it the judge can see "
|
|
212
|
+
"`expected` and grade against the answer key"
|
|
213
|
+
)
|
|
214
|
+
raise ValueError(msg)
|
|
215
|
+
if not self.rubric and not self.rubric_path:
|
|
216
|
+
msg = f"judge {self.name!r} needs `rubric` or `rubric_path`"
|
|
217
|
+
raise ValueError(msg)
|
|
218
|
+
if not self.model:
|
|
219
|
+
# Unpinned means the provider can change the model underneath you and
|
|
220
|
+
# invalidate every historical number without any signal.
|
|
221
|
+
msg = (
|
|
222
|
+
f"judge {self.name!r} must pin a `model`: an unpinned judge silently "
|
|
223
|
+
"invalidates every historical comparison when the provider updates it"
|
|
224
|
+
)
|
|
225
|
+
raise ValueError(msg)
|
|
226
|
+
return self
|
|
227
|
+
|
|
228
|
+
@model_validator(mode="after")
|
|
229
|
+
def _trajectory_needs_a_policy(self) -> EvaluatorSpec:
|
|
230
|
+
if self.type == "trajectory" and not self.policy:
|
|
231
|
+
msg = f"trajectory evaluator {self.name!r} needs a `policy` path"
|
|
232
|
+
raise ValueError(msg)
|
|
233
|
+
return self
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
class GateSpec(BaseModel):
|
|
237
|
+
model_config = ConfigDict(extra="forbid")
|
|
238
|
+
|
|
239
|
+
minimum: float | None = None
|
|
240
|
+
maximum: float | None = None
|
|
241
|
+
max_regression: float | None = None
|
|
242
|
+
max_relative_regression: float | None = None
|
|
243
|
+
blocking: bool = True
|
|
244
|
+
slice: dict[str, str] | None = None
|
|
245
|
+
require_baseline: bool = False
|
|
246
|
+
#: Alpha for a paired significance test. With it, a regression must be both bigger than the
|
|
247
|
+
#: threshold *and* distinguishable from noise before it fails the build — because at the sample
|
|
248
|
+
#: sizes eval suites actually run at, most measured regressions are neither.
|
|
249
|
+
significance: float | None = Field(default=None, ge=0.0, le=1.0)
|
|
250
|
+
#: Report ERROR when the run was too small to have detected the regression this gate guards.
|
|
251
|
+
#: A green check from a test that could never have failed is worse than no check.
|
|
252
|
+
require_power: bool = False
|
|
253
|
+
|
|
254
|
+
@model_validator(mode="after")
|
|
255
|
+
def _has_a_condition(self) -> GateSpec:
|
|
256
|
+
if all(
|
|
257
|
+
v is None
|
|
258
|
+
for v in (
|
|
259
|
+
self.minimum,
|
|
260
|
+
self.maximum,
|
|
261
|
+
self.max_regression,
|
|
262
|
+
self.max_relative_regression,
|
|
263
|
+
)
|
|
264
|
+
):
|
|
265
|
+
msg = "declares no condition; a gate that cannot fail gives false assurance"
|
|
266
|
+
raise ValueError(msg)
|
|
267
|
+
return self
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
class BaselineSpec(BaseModel):
|
|
271
|
+
model_config = ConfigDict(extra="forbid")
|
|
272
|
+
|
|
273
|
+
strategy: Literal["latest_on_branch", "run_id", "none"] = "latest_on_branch"
|
|
274
|
+
branch: str = "main"
|
|
275
|
+
run_id: str | None = None
|
|
276
|
+
require_dataset_match: bool = True
|
|
277
|
+
|
|
278
|
+
@model_validator(mode="after")
|
|
279
|
+
def _run_id_present(self) -> BaselineSpec:
|
|
280
|
+
if self.strategy == "run_id" and not self.run_id:
|
|
281
|
+
msg = "baseline strategy 'run_id' needs a `run_id`"
|
|
282
|
+
raise ValueError(msg)
|
|
283
|
+
return self
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
class ReportSpec(BaseModel):
|
|
287
|
+
model_config = ConfigDict(extra="forbid")
|
|
288
|
+
|
|
289
|
+
formats: list[ReportFormat] = Field(
|
|
290
|
+
default_factory=lambda: ["terminal", "json"] # type: ignore[arg-type]
|
|
291
|
+
)
|
|
292
|
+
output: str = "proofstep-report.json"
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
class Suite(BaseModel):
|
|
296
|
+
model_config = ConfigDict(extra="forbid", populate_by_name=True)
|
|
297
|
+
|
|
298
|
+
apiVersion: str = API_VERSION # noqa: N815 — YAML field name
|
|
299
|
+
kind: Literal["EvalSuite"] = "EvalSuite"
|
|
300
|
+
name: str = Field(pattern=r"^[a-z][a-z0-9-]*$")
|
|
301
|
+
description: str | None = None
|
|
302
|
+
extends: str | None = None
|
|
303
|
+
|
|
304
|
+
dataset: DatasetRef
|
|
305
|
+
task: TaskRef | None = None
|
|
306
|
+
configuration: dict[str, Any] = Field(default_factory=dict)
|
|
307
|
+
execution: Execution = Field(default_factory=Execution)
|
|
308
|
+
evaluators: list[EvaluatorSpec] = Field(min_length=1)
|
|
309
|
+
gates: dict[str, GateSpec] = Field(default_factory=dict)
|
|
310
|
+
calibration: CalibrationPolicy = Field(default_factory=CalibrationPolicy)
|
|
311
|
+
baseline: BaselineSpec = Field(default_factory=BaselineSpec)
|
|
312
|
+
report: ReportSpec = Field(default_factory=ReportSpec)
|
|
313
|
+
|
|
314
|
+
@model_validator(mode="after")
|
|
315
|
+
def _supported_api_version(self) -> Suite:
|
|
316
|
+
if self.apiVersion != API_VERSION:
|
|
317
|
+
msg = (
|
|
318
|
+
f"unsupported apiVersion {self.apiVersion!r}; this CLI understands {API_VERSION!r}"
|
|
319
|
+
)
|
|
320
|
+
raise ValueError(msg)
|
|
321
|
+
return self
|
|
322
|
+
|
|
323
|
+
@model_validator(mode="after")
|
|
324
|
+
def _unique_evaluator_names(self) -> Suite:
|
|
325
|
+
seen: set[str] = set()
|
|
326
|
+
for evaluator in self.evaluators:
|
|
327
|
+
if evaluator.name in seen:
|
|
328
|
+
msg = f"duplicate evaluator name {evaluator.name!r}"
|
|
329
|
+
raise ValueError(msg)
|
|
330
|
+
seen.add(evaluator.name)
|
|
331
|
+
return self
|
|
332
|
+
|
|
333
|
+
@property
|
|
334
|
+
def judge_ratio(self) -> float:
|
|
335
|
+
if not self.evaluators:
|
|
336
|
+
return 0.0
|
|
337
|
+
judges = sum(1 for e in self.evaluators if e.type == "llm_judge")
|
|
338
|
+
return judges / len(self.evaluators)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def _closest(value: str, candidates: tuple[str, ...]) -> str | None:
|
|
342
|
+
matches = difflib.get_close_matches(value, candidates, n=1, cutoff=0.6)
|
|
343
|
+
return matches[0] if matches else None
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: proofstep-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Proofstep command-line interface — run suites, gate CI, compare experiments
|
|
5
|
+
Project-URL: Homepage, https://github.com/IlaKhan17/proofstep
|
|
6
|
+
Project-URL: Documentation, https://github.com/IlaKhan17/proofstep/tree/main/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/IlaKhan17/proofstep
|
|
8
|
+
Project-URL: Issues, https://github.com/IlaKhan17/proofstep/issues
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
16
|
+
Classifier: Topic :: Software Development :: Testing
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Requires-Dist: httpx>=0.27
|
|
20
|
+
Requires-Dist: proofstep
|
|
21
|
+
Requires-Dist: proofstep-core
|
|
22
|
+
Requires-Dist: proofstep-trajectory
|
|
23
|
+
Requires-Dist: proofstep-types
|
|
24
|
+
Requires-Dist: pyyaml>=6.0
|
|
25
|
+
Requires-Dist: rich>=13.9
|
|
26
|
+
Requires-Dist: typer>=0.15
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# proofstep-cli
|
|
30
|
+
|
|
31
|
+
**The `proofstep` command** — part of [Proofstep](https://github.com/IlaKhan17/proofstep), the CI gate for AI
|
|
32
|
+
agents that knows the difference between a regression and a bad day.
|
|
33
|
+
|
|
34
|
+
Run an evaluation suite, apply its gates, and exit non-zero when a protected metric regresses.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
proofstep eval evals/suites/reply-intent.yaml
|
|
38
|
+
echo $? # 0 merge · 1 a blocking gate failed · 2 execution error · 3 the suite is wrong
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The exit code is the contract. Everything else — the terminal table, the JSON report, the
|
|
42
|
+
pull-request comment — exists to explain it.
|
|
43
|
+
|
|
44
|
+
Gates can be statistically honest rather than just thresholded:
|
|
45
|
+
|
|
46
|
+
```yaml
|
|
47
|
+
gates:
|
|
48
|
+
intent_accuracy:
|
|
49
|
+
max_regression: 0.02
|
|
50
|
+
significance: 0.05 # only fail if the drop is distinguishable from noise
|
|
51
|
+
require_power: true # and ERROR if this run could never have detected it
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Documentation
|
|
55
|
+
|
|
56
|
+
Full documentation lives in the [repository](https://github.com/IlaKhan17/proofstep/tree/main/docs).
|
|
57
|
+
|
|
58
|
+
Apache-2.0.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
proofstep_cli/__init__.py,sha256=kZDCFB-6Wudptp_a3AK2xCFx-s2muRDAL_oxo_J6BsI,341
|
|
2
|
+
proofstep_cli/calibration.py,sha256=3_iQ7aNBY3o0yYOe2ejS8xfvel_tfFgovoOm6AEoiMc,11572
|
|
3
|
+
proofstep_cli/calibration_store.py,sha256=wjxopf2nAl1wKOXAfkivjD6u2E7PhIoTmLloFfZoDHw,7946
|
|
4
|
+
proofstep_cli/main.py,sha256=4wtRYvoWWpO8TWOC9E4pvtiDX5JtzoQzymHuW5I5rjQ,21821
|
|
5
|
+
proofstep_cli/publish.py,sha256=5XZ-eg40uoUXYXOx5qCzT0nfIi2KofT7f2XmJTwb-Qk,22955
|
|
6
|
+
proofstep_cli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
proofstep_cli/registry.py,sha256=8X8IL8U_8tTCthI5pycy_IUxgGpddTK649S9XerLcyk,7951
|
|
8
|
+
proofstep_cli/runner.py,sha256=8MYXZTXCxFyXObE2mWZEBLzgD_IBvyB4qVrkpj92UgY,12651
|
|
9
|
+
proofstep_cli/commands/__init__.py,sha256=QN-uD3qaiBO7K7E4QEfRPnoh3rvXcIe89veVtfE8c38,23
|
|
10
|
+
proofstep_cli/render/__init__.py,sha256=bmQ3dGBiHqSPwumamEebbTyZSdWgUDms_jPjo1MBB0Y,24
|
|
11
|
+
proofstep_cli/render/calibration.py,sha256=r9Zidu3Dw4AMe7aOj24DayDGJmiQJnQIKvZhudPXa8o,5108
|
|
12
|
+
proofstep_cli/render/markdown.py,sha256=Y58yri1ynyD47vE0VTAgMBVqdIYyzVMjrZ1rSnRBYlk,10651
|
|
13
|
+
proofstep_cli/render/report.py,sha256=44BKaBO5Li42t1XzEGYqnXPoKCIgThGipyUyPBlw5Jc,6708
|
|
14
|
+
proofstep_cli/render/terminal.py,sha256=Ax2PBj1gtWExN6yS5IQpo8BFUy_-uQSYmZDrv65BNGI,10343
|
|
15
|
+
proofstep_cli/suite/__init__.py,sha256=CEqbhLYlg7SJoIKNYdV4W5m7MrwQa21mG7OhK5TGNAM,27
|
|
16
|
+
proofstep_cli/suite/loader.py,sha256=llGUMwSt5LEB__hz3Pv7SpmHt1d0Qqj1cXGSrRi3VN4,14050
|
|
17
|
+
proofstep_cli/suite/schema.py,sha256=p6iL4Mgz9baqqjVz9TtpHGgLgDtb3RrmbOBlw1Ji0Ow,12459
|
|
18
|
+
proofstep_cli-0.1.0.dist-info/METADATA,sha256=3ueFoVrFRD0nygOEwLm5EcbgvjoJEy71zcDrHOe1nmQ,2148
|
|
19
|
+
proofstep_cli-0.1.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
20
|
+
proofstep_cli-0.1.0.dist-info/entry_points.txt,sha256=kvjBHqD1IOyphRKMklvkz_Yrhe4VxUY5bZkuK35yrNY,53
|
|
21
|
+
proofstep_cli-0.1.0.dist-info/RECORD,,
|