assurance-cli 0.6.0__tar.gz → 0.6.2__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.
- assurance_cli-0.6.2/PKG-INFO +89 -0
- assurance_cli-0.6.2/README.md +56 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/assurance_cli/cli.py +62 -18
- assurance_cli-0.6.2/assurance_cli.egg-info/PKG-INFO +89 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/pyproject.toml +1 -1
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/tests/test_cli.py +38 -2
- assurance_cli-0.6.0/PKG-INFO +0 -364
- assurance_cli-0.6.0/README.md +0 -331
- assurance_cli-0.6.0/assurance_cli.egg-info/PKG-INFO +0 -364
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/LICENSE +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/assurance_cli/__init__.py +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/assurance_cli/__main__.py +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/assurance_cli/baseline.py +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/assurance_cli/drift.py +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/assurance_cli/gather.py +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/assurance_cli/paths.py +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/assurance_cli/pin.py +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/assurance_cli/profile.py +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/assurance_cli/py.typed +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/assurance_cli/setdiff.py +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/assurance_cli.egg-info/SOURCES.txt +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/assurance_cli.egg-info/dependency_links.txt +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/assurance_cli.egg-info/entry_points.txt +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/assurance_cli.egg-info/requires.txt +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/assurance_cli.egg-info/top_level.txt +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/setup.cfg +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/tests/test_drift.py +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/tests/test_gapped_series.py +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/tests/test_installed_wheel.py +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/tests/test_pin.py +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/tests/test_read_only.py +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/tests/test_readme_blocks.py +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/tests/test_setdiff.py +0 -0
- {assurance_cli-0.6.0 → assurance_cli-0.6.2}/tests/test_version.py +0 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: assurance-cli
|
|
3
|
+
Version: 0.6.2
|
|
4
|
+
Summary: CLI for folder assurance checks — coverage, staleness, and baselines.
|
|
5
|
+
Author-email: "I-Ops Operations Intelligence, LLC" <hello@i-ops.dev>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://i-ops.dev
|
|
8
|
+
Project-URL: Source, https://github.com/i-ops-hq/assurance
|
|
9
|
+
Keywords: cli,ai-agents,governance,auditing,assurance,reproducibility
|
|
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.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: assurance-core>=0.14.0
|
|
23
|
+
Requires-Dist: openpyxl>=3.1
|
|
24
|
+
Provides-Extra: mcp
|
|
25
|
+
Requires-Dist: mcp<3,>=1.27.0; extra == "mcp"
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
28
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
29
|
+
Requires-Dist: types-openpyxl; extra == "dev"
|
|
30
|
+
Provides-Extra: deps
|
|
31
|
+
Requires-Dist: assurance-deps>=0.2.4; extra == "deps"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# assurance-cli
|
|
35
|
+
|
|
36
|
+
[](https://pypi.org/project/assurance-cli/)
|
|
37
|
+
[](https://github.com/i-ops-hq/assurance/actions/workflows/tests.yml)
|
|
38
|
+
[](https://pypi.org/project/assurance-cli/)
|
|
39
|
+
[](https://github.com/i-ops-hq/assurance/blob/main/packages/cli/LICENSE)
|
|
40
|
+
|
|
41
|
+
Did the job cover everything it was supposed to cover? Arithmetic, not models.
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install assurance-cli
|
|
47
|
+
# or: pip install assurance # every tool
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Quick start
|
|
51
|
+
|
|
52
|
+
`assurance --help` lists: `init`, `check`, `diff`, `pin`, `drift`, `deps`, `budget`, `authority`, `audit`.
|
|
53
|
+
`audit`, `budget`, `authority`, and `deps` forward to their packages when installed.
|
|
54
|
+
|
|
55
|
+
Point `check` at a folder of dated files (real output from a temp folder with Jan, Feb, Apr CSVs):
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
$ assurance check reports
|
|
59
|
+
3 of 4 months from 2026-01 to 2026-04 in reports — not in this folder: March 2026 — Range inferred from filenames: earliest 2026-01, latest 2026-04 — uneven spacing, so monthly was read from the names rather than detected. Override with --expect / --from / --to.
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
With `--fail-on-gap` that same line exits `1`.
|
|
63
|
+
|
|
64
|
+
`diff` compares any two sets of keys (no folder required). `pin` snapshots MCP tool definitions and detects drift. `drift` watches a binary outcome stream.
|
|
65
|
+
|
|
66
|
+
## What it checks
|
|
67
|
+
|
|
68
|
+
- Dated or numbered series in a folder (`check`) — months, weeks, days, numbered runs
|
|
69
|
+
- Coverage over any two key sets (`diff`)
|
|
70
|
+
- MCP tool-description drift since a pin (`pin`)
|
|
71
|
+
- Shift in a binary outcome stream (`drift`)
|
|
72
|
+
- Forwards: session audit, run budget, authority review, dependency install hooks
|
|
73
|
+
|
|
74
|
+
## In CI
|
|
75
|
+
|
|
76
|
+
| exit | means |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `0` | checked; no gap (or not asked to fail on one) |
|
|
79
|
+
| `1` | gap / unexpected key / stale pin / nothing it could check |
|
|
80
|
+
| `2` | could not run (bad path, missing sibling package, bad JSON) |
|
|
81
|
+
|
|
82
|
+
## Limits
|
|
83
|
+
|
|
84
|
+
- **Does not invent the expected set.** `diff` takes your declaration; `check` derives one and prints how.
|
|
85
|
+
- **No network, no telemetry, no keys.**
|
|
86
|
+
- **A folder it cannot parse exits 1**, not 0 — silence is not a pass.
|
|
87
|
+
- Skipped tool directories (`.git`, `node_modules`, …) are named, not folded into “files not opened” as if they were content.
|
|
88
|
+
|
|
89
|
+
See the [root README](https://github.com/i-ops-hq/assurance#readme) and [CHANGELOG.md](CHANGELOG.md).
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# assurance-cli
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/assurance-cli/)
|
|
4
|
+
[](https://github.com/i-ops-hq/assurance/actions/workflows/tests.yml)
|
|
5
|
+
[](https://pypi.org/project/assurance-cli/)
|
|
6
|
+
[](https://github.com/i-ops-hq/assurance/blob/main/packages/cli/LICENSE)
|
|
7
|
+
|
|
8
|
+
Did the job cover everything it was supposed to cover? Arithmetic, not models.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install assurance-cli
|
|
14
|
+
# or: pip install assurance # every tool
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
`assurance --help` lists: `init`, `check`, `diff`, `pin`, `drift`, `deps`, `budget`, `authority`, `audit`.
|
|
20
|
+
`audit`, `budget`, `authority`, and `deps` forward to their packages when installed.
|
|
21
|
+
|
|
22
|
+
Point `check` at a folder of dated files (real output from a temp folder with Jan, Feb, Apr CSVs):
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
$ assurance check reports
|
|
26
|
+
3 of 4 months from 2026-01 to 2026-04 in reports — not in this folder: March 2026 — Range inferred from filenames: earliest 2026-01, latest 2026-04 — uneven spacing, so monthly was read from the names rather than detected. Override with --expect / --from / --to.
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
With `--fail-on-gap` that same line exits `1`.
|
|
30
|
+
|
|
31
|
+
`diff` compares any two sets of keys (no folder required). `pin` snapshots MCP tool definitions and detects drift. `drift` watches a binary outcome stream.
|
|
32
|
+
|
|
33
|
+
## What it checks
|
|
34
|
+
|
|
35
|
+
- Dated or numbered series in a folder (`check`) — months, weeks, days, numbered runs
|
|
36
|
+
- Coverage over any two key sets (`diff`)
|
|
37
|
+
- MCP tool-description drift since a pin (`pin`)
|
|
38
|
+
- Shift in a binary outcome stream (`drift`)
|
|
39
|
+
- Forwards: session audit, run budget, authority review, dependency install hooks
|
|
40
|
+
|
|
41
|
+
## In CI
|
|
42
|
+
|
|
43
|
+
| exit | means |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `0` | checked; no gap (or not asked to fail on one) |
|
|
46
|
+
| `1` | gap / unexpected key / stale pin / nothing it could check |
|
|
47
|
+
| `2` | could not run (bad path, missing sibling package, bad JSON) |
|
|
48
|
+
|
|
49
|
+
## Limits
|
|
50
|
+
|
|
51
|
+
- **Does not invent the expected set.** `diff` takes your declaration; `check` derives one and prints how.
|
|
52
|
+
- **No network, no telemetry, no keys.**
|
|
53
|
+
- **A folder it cannot parse exits 1**, not 0 — silence is not a pass.
|
|
54
|
+
- Skipped tool directories (`.git`, `node_modules`, …) are named, not folded into “files not opened” as if they were content.
|
|
55
|
+
|
|
56
|
+
See the [root README](https://github.com/i-ops-hq/assurance#readme) and [CHANGELOG.md](CHANGELOG.md).
|
|
@@ -25,23 +25,65 @@ FORWARDED: dict[str, tuple[str, str]] = {
|
|
|
25
25
|
"budget": ("assurance_budget.cli", "pip install assurance-budget"),
|
|
26
26
|
"authority": ("assurance_authority.cli", "pip install assurance-authority"),
|
|
27
27
|
"audit": ("assurance_budget.session_cli", "pip install assurance-budget"),
|
|
28
|
+
"hook": ("assurance_budget.hook_setup", "pip install assurance-budget"),
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
_START_HERE = """\
|
|
31
|
-
assurance —
|
|
32
|
+
assurance — your AI agent says it's done. This tells you what it didn't check.
|
|
33
|
+
No model, no network, no account.
|
|
32
34
|
|
|
33
|
-
assurance
|
|
34
|
-
assurance
|
|
35
|
+
assurance audit what the Claude Code session in this folder did, and skipped
|
|
36
|
+
assurance audit --demo the same report on a bundled sample session
|
|
37
|
+
assurance hook install run the audit after every Claude Code turn (remove undoes it)
|
|
38
|
+
assurance diff --expected A --found B was everything that should have been read, read?
|
|
35
39
|
assurance pin --save | --check did an MCP server change a tool after you approved it?
|
|
36
|
-
assurance deps
|
|
37
|
-
assurance budget runs.jsonl where an agent run's budget went
|
|
40
|
+
assurance deps package.json what an install will execute, read without running it
|
|
41
|
+
assurance budget runs.jsonl where an agent run's budget went, and where it looped
|
|
38
42
|
assurance authority --example may a task proceed for the person who asked?
|
|
39
|
-
assurance
|
|
43
|
+
assurance check [FOLDER] a folder of dated or numbered files: what is absent
|
|
40
44
|
|
|
45
|
+
Run it after every Claude Code session: https://github.com/i-ops-hq/assurance#run-it-after-every-session
|
|
41
46
|
assurance <command> --help for more.
|
|
42
47
|
"""
|
|
43
48
|
|
|
44
49
|
|
|
50
|
+
#: Every distribution the `assurance` command can reach, in the order `--version` names them.
|
|
51
|
+
_DISTRIBUTIONS = (
|
|
52
|
+
("assurance-cli", "cli"),
|
|
53
|
+
("assurance-budget", "budget"),
|
|
54
|
+
("assurance-deps", "deps"),
|
|
55
|
+
("assurance-authority", "authority"),
|
|
56
|
+
("assurance-mcp", "mcp"),
|
|
57
|
+
("assurance-core", "core"),
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _version_line() -> str:
|
|
62
|
+
"""`assurance 0.1.2 (cli 0.6.1, budget 0.2.2, …)` — every installed part, so a bug report
|
|
63
|
+
names what actually ran. `pip install assurance` is one version number over several packages;
|
|
64
|
+
printing only the cli's used to say 0.6.0 to someone who had installed 0.1.1."""
|
|
65
|
+
parts = []
|
|
66
|
+
for dist, short in _DISTRIBUTIONS:
|
|
67
|
+
try:
|
|
68
|
+
parts.append(f"{short} {importlib.metadata.version(dist)}")
|
|
69
|
+
except importlib.metadata.PackageNotFoundError:
|
|
70
|
+
continue
|
|
71
|
+
try:
|
|
72
|
+
front = f"assurance {importlib.metadata.version('assurance')}"
|
|
73
|
+
except importlib.metadata.PackageNotFoundError:
|
|
74
|
+
front = "assurance"
|
|
75
|
+
return f"{front} ({', '.join(parts)})" if parts else front
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class _PrintVersion(argparse.Action):
|
|
79
|
+
"""argparse's own version action rewraps the text to the terminal width, splitting a version
|
|
80
|
+
number across lines. This prints the line as it is."""
|
|
81
|
+
|
|
82
|
+
def __call__(self, parser: argparse.ArgumentParser, namespace: object, values: object, option_string: str | None = None) -> None:
|
|
83
|
+
print(_version_line())
|
|
84
|
+
parser.exit()
|
|
85
|
+
|
|
86
|
+
|
|
45
87
|
def main(argv: list[str] | None = None) -> int:
|
|
46
88
|
"""Command-line entry for assurance checks."""
|
|
47
89
|
# `deps` is forwarded whole, before argparse sees any of it. argparse claims `--help` for the
|
|
@@ -58,17 +100,23 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
58
100
|
parser = argparse.ArgumentParser(
|
|
59
101
|
prog="assurance",
|
|
60
102
|
description=(
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"special case for a folder of dated or numbered files."
|
|
103
|
+
"Your AI agent says it's done. Assurance tells you what it didn't check. Every answer "
|
|
104
|
+
"is arithmetic over what happened; no model decides anything."
|
|
64
105
|
),
|
|
65
106
|
)
|
|
66
|
-
parser.add_argument(
|
|
67
|
-
"--version",
|
|
68
|
-
action="version",
|
|
69
|
-
version=f"%(prog)s {importlib.metadata.version('assurance-cli')}",
|
|
70
|
-
)
|
|
107
|
+
parser.add_argument("--version", action=_PrintVersion, nargs=0, help="Show every installed part's version")
|
|
71
108
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
109
|
+
# First, because it is the one most people install this for. `main` forwards it before
|
|
110
|
+
# argparse runs; it is listed here so `assurance --help` shows it.
|
|
111
|
+
sub.add_parser(
|
|
112
|
+
"audit", add_help=False,
|
|
113
|
+
help="What the Claude Code session in this folder did, and what it skipped (--demo for a sample)",
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
sub.add_parser(
|
|
117
|
+
"hook", add_help=False,
|
|
118
|
+
help="Run the audit after every Claude Code turn: install, remove, status",
|
|
119
|
+
)
|
|
72
120
|
|
|
73
121
|
init_parser = sub.add_parser("init", help="Write .assurance.json baseline")
|
|
74
122
|
init_parser.add_argument("folder", help="Folder to baseline")
|
|
@@ -192,10 +240,6 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
192
240
|
"authority", add_help=False,
|
|
193
241
|
help="Whether a task may proceed for the person who asked (assurance-authority)",
|
|
194
242
|
)
|
|
195
|
-
sub.add_parser(
|
|
196
|
-
"audit", add_help=False,
|
|
197
|
-
help="What a Claude Code session did, and what could not be classified (assurance-budget)",
|
|
198
|
-
)
|
|
199
243
|
|
|
200
244
|
args = parser.parse_args(argv)
|
|
201
245
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: assurance-cli
|
|
3
|
+
Version: 0.6.2
|
|
4
|
+
Summary: CLI for folder assurance checks — coverage, staleness, and baselines.
|
|
5
|
+
Author-email: "I-Ops Operations Intelligence, LLC" <hello@i-ops.dev>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://i-ops.dev
|
|
8
|
+
Project-URL: Source, https://github.com/i-ops-hq/assurance
|
|
9
|
+
Keywords: cli,ai-agents,governance,auditing,assurance,reproducibility
|
|
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.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: assurance-core>=0.14.0
|
|
23
|
+
Requires-Dist: openpyxl>=3.1
|
|
24
|
+
Provides-Extra: mcp
|
|
25
|
+
Requires-Dist: mcp<3,>=1.27.0; extra == "mcp"
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
28
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
29
|
+
Requires-Dist: types-openpyxl; extra == "dev"
|
|
30
|
+
Provides-Extra: deps
|
|
31
|
+
Requires-Dist: assurance-deps>=0.2.4; extra == "deps"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# assurance-cli
|
|
35
|
+
|
|
36
|
+
[](https://pypi.org/project/assurance-cli/)
|
|
37
|
+
[](https://github.com/i-ops-hq/assurance/actions/workflows/tests.yml)
|
|
38
|
+
[](https://pypi.org/project/assurance-cli/)
|
|
39
|
+
[](https://github.com/i-ops-hq/assurance/blob/main/packages/cli/LICENSE)
|
|
40
|
+
|
|
41
|
+
Did the job cover everything it was supposed to cover? Arithmetic, not models.
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install assurance-cli
|
|
47
|
+
# or: pip install assurance # every tool
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Quick start
|
|
51
|
+
|
|
52
|
+
`assurance --help` lists: `init`, `check`, `diff`, `pin`, `drift`, `deps`, `budget`, `authority`, `audit`.
|
|
53
|
+
`audit`, `budget`, `authority`, and `deps` forward to their packages when installed.
|
|
54
|
+
|
|
55
|
+
Point `check` at a folder of dated files (real output from a temp folder with Jan, Feb, Apr CSVs):
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
$ assurance check reports
|
|
59
|
+
3 of 4 months from 2026-01 to 2026-04 in reports — not in this folder: March 2026 — Range inferred from filenames: earliest 2026-01, latest 2026-04 — uneven spacing, so monthly was read from the names rather than detected. Override with --expect / --from / --to.
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
With `--fail-on-gap` that same line exits `1`.
|
|
63
|
+
|
|
64
|
+
`diff` compares any two sets of keys (no folder required). `pin` snapshots MCP tool definitions and detects drift. `drift` watches a binary outcome stream.
|
|
65
|
+
|
|
66
|
+
## What it checks
|
|
67
|
+
|
|
68
|
+
- Dated or numbered series in a folder (`check`) — months, weeks, days, numbered runs
|
|
69
|
+
- Coverage over any two key sets (`diff`)
|
|
70
|
+
- MCP tool-description drift since a pin (`pin`)
|
|
71
|
+
- Shift in a binary outcome stream (`drift`)
|
|
72
|
+
- Forwards: session audit, run budget, authority review, dependency install hooks
|
|
73
|
+
|
|
74
|
+
## In CI
|
|
75
|
+
|
|
76
|
+
| exit | means |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `0` | checked; no gap (or not asked to fail on one) |
|
|
79
|
+
| `1` | gap / unexpected key / stale pin / nothing it could check |
|
|
80
|
+
| `2` | could not run (bad path, missing sibling package, bad JSON) |
|
|
81
|
+
|
|
82
|
+
## Limits
|
|
83
|
+
|
|
84
|
+
- **Does not invent the expected set.** `diff` takes your declaration; `check` derives one and prints how.
|
|
85
|
+
- **No network, no telemetry, no keys.**
|
|
86
|
+
- **A folder it cannot parse exits 1**, not 0 — silence is not a pass.
|
|
87
|
+
- Skipped tool directories (`.git`, `node_modules`, …) are named, not folded into “files not opened” as if they were content.
|
|
88
|
+
|
|
89
|
+
See the [root README](https://github.com/i-ops-hq/assurance#readme) and [CHANGELOG.md](CHANGELOG.md).
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import importlib.metadata
|
|
6
|
+
|
|
5
7
|
import csv
|
|
6
8
|
import json
|
|
7
9
|
from pathlib import Path
|
|
@@ -838,15 +840,49 @@ def test_help_lists_every_forwarded_command(capsys) -> None:
|
|
|
838
840
|
with pytest.raises(SystemExit):
|
|
839
841
|
main(["--help"])
|
|
840
842
|
out = capsys.readouterr().out
|
|
841
|
-
for name in ("deps", "budget", "authority"):
|
|
843
|
+
for name in ("deps", "budget", "authority", "audit", "hook"):
|
|
842
844
|
assert name in out
|
|
843
845
|
|
|
844
846
|
|
|
847
|
+
def test_the_hook_subcommand_reaches_the_installer(tmp_path: Path, capsys, monkeypatch) -> None:
|
|
848
|
+
"""`assurance hook` is forwarded whole to assurance-budget, like `audit`."""
|
|
849
|
+
pytest.importorskip("assurance_budget.hook_setup")
|
|
850
|
+
monkeypatch.setenv("CLAUDE_CONFIG_DIR", str(tmp_path / "claude"))
|
|
851
|
+
monkeypatch.setenv("XDG_STATE_HOME", str(tmp_path / "state"))
|
|
852
|
+
monkeypatch.setenv("LOCALAPPDATA", str(tmp_path / "state")) # where backups go on Windows
|
|
853
|
+
monkeypatch.chdir(tmp_path)
|
|
854
|
+
assert main(["hook", "install", "--yes", "--command", "uvx assurance@9.9.9 audit --hook --nudge"]) == 0
|
|
855
|
+
assert "uvx assurance@9.9.9 audit --hook --nudge" in (tmp_path / "claude" / "settings.json").read_text()
|
|
856
|
+
assert main(["hook", "status"]) in (0, 1)
|
|
857
|
+
assert "uvx assurance@9.9.9 audit --hook --nudge" in capsys.readouterr().out
|
|
858
|
+
assert main(["hook", "remove", "--yes"]) == 0
|
|
859
|
+
assert not (tmp_path / "claude" / "settings.json").exists()
|
|
860
|
+
|
|
861
|
+
|
|
845
862
|
def test_version_flag(capsys) -> None:
|
|
846
863
|
with pytest.raises(SystemExit) as exc:
|
|
847
864
|
main(["--version"])
|
|
848
865
|
assert exc.value.code == 0
|
|
849
|
-
|
|
866
|
+
out = capsys.readouterr().out
|
|
867
|
+
assert out.startswith("assurance ")
|
|
868
|
+
# Every installed part is named, on one unwrapped line: `pip install assurance` is one version
|
|
869
|
+
# over several packages, and printing only the cli's told a 0.1.1 user "0.6.0".
|
|
870
|
+
assert out.count("\n") == 1
|
|
871
|
+
assert f"cli {importlib.metadata.version('assurance-cli')}" in out
|
|
872
|
+
assert f"core {importlib.metadata.version('assurance-core')}" in out
|
|
873
|
+
|
|
874
|
+
|
|
875
|
+
def test_help_lists_audit_first(capsys) -> None:
|
|
876
|
+
with pytest.raises(SystemExit):
|
|
877
|
+
main(["--help"])
|
|
878
|
+
usage = capsys.readouterr().out.splitlines()[1]
|
|
879
|
+
assert usage.strip().startswith("{audit,")
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
def test_start_here_leads_with_audit_and_the_demo(capsys) -> None:
|
|
883
|
+
assert main([]) == 0
|
|
884
|
+
listed = [line.split()[1] for line in capsys.readouterr().out.splitlines() if line.startswith(" assurance ")]
|
|
885
|
+
assert listed[:2] == ["audit", "audit"]
|
|
850
886
|
|
|
851
887
|
|
|
852
888
|
def test_budget_version_flag(capsys) -> None:
|