govrail 0.1.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.
- govrail-0.1.0/PKG-INFO +65 -0
- govrail-0.1.0/README.md +52 -0
- govrail-0.1.0/gov/__init__.py +7 -0
- govrail-0.1.0/gov/__main__.py +4 -0
- govrail-0.1.0/gov/archive_notes.py +29 -0
- govrail-0.1.0/gov/change_scope.py +62 -0
- govrail-0.1.0/gov/cli.py +214 -0
- govrail-0.1.0/gov/gates.py +327 -0
- govrail-0.1.0/gov/self_test.py +201 -0
- govrail-0.1.0/gov/templates/__init__.py +0 -0
- govrail-0.1.0/gov/templates/gates.json +24 -0
- govrail-0.1.0/gov/templates/notes-README.md +62 -0
- govrail-0.1.0/gov/templates/rules.md +51 -0
- govrail-0.1.0/gov/verify_notes.py +51 -0
- govrail-0.1.0/gov/verify_translation_pairing.py +147 -0
- govrail-0.1.0/govrail.egg-info/PKG-INFO +65 -0
- govrail-0.1.0/govrail.egg-info/SOURCES.txt +20 -0
- govrail-0.1.0/govrail.egg-info/dependency_links.txt +1 -0
- govrail-0.1.0/govrail.egg-info/entry_points.txt +2 -0
- govrail-0.1.0/govrail.egg-info/top_level.txt +1 -0
- govrail-0.1.0/pyproject.toml +31 -0
- govrail-0.1.0/setup.cfg +4 -0
govrail-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: govrail
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A language-agnostic governance plane for agent-driven development: gates and notes, delivered by the gov CLI.
|
|
5
|
+
Author: govrail
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: agent,governance,gates,notes,development
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# govrail
|
|
15
|
+
|
|
16
|
+
English | [中文](README.zh.md)
|
|
17
|
+
|
|
18
|
+
A language-agnostic governance plane for agent-driven development: coding
|
|
19
|
+
agents work fast in parallel while machines — not vigilance — hold the quality
|
|
20
|
+
line. The only runtime dependency is Python 3.
|
|
21
|
+
|
|
22
|
+
The plane ships two mechanisms: **gates** (any promise a command can check
|
|
23
|
+
becomes a mechanical check) and **notes** (every non-trivial change records the
|
|
24
|
+
decision, what it beat, and the consequences). Bilingual pairing keeps the
|
|
25
|
+
external-presentation docs in sync.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
pip install . # or: uv tool install . / pipx install .
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This puts the `gov` CLI on your PATH (stdlib only — no third-party
|
|
34
|
+
dependencies). It has one subcommand per action:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
gov init --project <path> # inject the plane into an existing project
|
|
38
|
+
gov uninstall --project <path> # reverse it exactly
|
|
39
|
+
gov run --mode all # run the project's gate DAG
|
|
40
|
+
gov self-test # prove every governance gate can reject
|
|
41
|
+
gov verify-pairing --write # re-confirm a bilingual pair after editing one side
|
|
42
|
+
gov change-scope --base <ref> # smallest sufficient check set for a diff
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`init` is non-invasive and idempotent: it creates `.gov/rules.md`, adds
|
|
46
|
+
`gates.json` and the notes README only when missing, appends one reference line
|
|
47
|
+
to AGENTS.md, and never overwrites the project's own files. `uninstall` reverses
|
|
48
|
+
it exactly.
|
|
49
|
+
|
|
50
|
+
## What is inside
|
|
51
|
+
|
|
52
|
+
- `gov/` — the Python package: `gates` (the DAG runner over `gates.json`),
|
|
53
|
+
`verify_notes` (three required sections), `verify_translation_pairing`
|
|
54
|
+
(git blob hashes), `change_scope`, `self_test`, `archive_notes`.
|
|
55
|
+
- `gov/templates/` — the rules, default `gates.json`, and notes format that
|
|
56
|
+
`gov init` injects into a project.
|
|
57
|
+
- `.gov/rules.md` — the single source of truth for the rules.
|
|
58
|
+
- `.agents/notes/` — the decision-record format and lifecycle.
|
|
59
|
+
|
|
60
|
+
## Origin
|
|
61
|
+
|
|
62
|
+
The mechanisms are distilled from the DeepSeek Harness repository, whose
|
|
63
|
+
gates-over-prose axiom shaped this template. Kept: the governance plane. Left to
|
|
64
|
+
you: the product plane. The locked design decisions live in
|
|
65
|
+
[docs/decisions.md](docs/decisions.md).
|
govrail-0.1.0/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# govrail
|
|
2
|
+
|
|
3
|
+
English | [中文](README.zh.md)
|
|
4
|
+
|
|
5
|
+
A language-agnostic governance plane for agent-driven development: coding
|
|
6
|
+
agents work fast in parallel while machines — not vigilance — hold the quality
|
|
7
|
+
line. The only runtime dependency is Python 3.
|
|
8
|
+
|
|
9
|
+
The plane ships two mechanisms: **gates** (any promise a command can check
|
|
10
|
+
becomes a mechanical check) and **notes** (every non-trivial change records the
|
|
11
|
+
decision, what it beat, and the consequences). Bilingual pairing keeps the
|
|
12
|
+
external-presentation docs in sync.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
pip install . # or: uv tool install . / pipx install .
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
This puts the `gov` CLI on your PATH (stdlib only — no third-party
|
|
21
|
+
dependencies). It has one subcommand per action:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
gov init --project <path> # inject the plane into an existing project
|
|
25
|
+
gov uninstall --project <path> # reverse it exactly
|
|
26
|
+
gov run --mode all # run the project's gate DAG
|
|
27
|
+
gov self-test # prove every governance gate can reject
|
|
28
|
+
gov verify-pairing --write # re-confirm a bilingual pair after editing one side
|
|
29
|
+
gov change-scope --base <ref> # smallest sufficient check set for a diff
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`init` is non-invasive and idempotent: it creates `.gov/rules.md`, adds
|
|
33
|
+
`gates.json` and the notes README only when missing, appends one reference line
|
|
34
|
+
to AGENTS.md, and never overwrites the project's own files. `uninstall` reverses
|
|
35
|
+
it exactly.
|
|
36
|
+
|
|
37
|
+
## What is inside
|
|
38
|
+
|
|
39
|
+
- `gov/` — the Python package: `gates` (the DAG runner over `gates.json`),
|
|
40
|
+
`verify_notes` (three required sections), `verify_translation_pairing`
|
|
41
|
+
(git blob hashes), `change_scope`, `self_test`, `archive_notes`.
|
|
42
|
+
- `gov/templates/` — the rules, default `gates.json`, and notes format that
|
|
43
|
+
`gov init` injects into a project.
|
|
44
|
+
- `.gov/rules.md` — the single source of truth for the rules.
|
|
45
|
+
- `.agents/notes/` — the decision-record format and lifecycle.
|
|
46
|
+
|
|
47
|
+
## Origin
|
|
48
|
+
|
|
49
|
+
The mechanisms are distilled from the DeepSeek Harness repository, whose
|
|
50
|
+
gates-over-prose axiom shaped this template. Kept: the governance plane. Left to
|
|
51
|
+
you: the product plane. The locked design decisions live in
|
|
52
|
+
[docs/decisions.md](docs/decisions.md).
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""govrail — a language-agnostic governance plane for agent-driven development.
|
|
2
|
+
|
|
3
|
+
The plane ships two mechanisms: gates (mechanical checks) and notes (decision
|
|
4
|
+
records), delivered by the ``gov`` CLI. The only runtime dependency is Python 3.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Seal the archived-notes manifest: recompute sha256 for every archived note.
|
|
3
|
+
|
|
4
|
+
Archived notes are frozen (D5); the manifest pins each file's content hash so
|
|
5
|
+
any later edit is detectable. Run this after moving a note into ``archived/``.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import hashlib
|
|
10
|
+
import json
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
ARCHIVED = Path(".agents/notes/archived")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def main() -> int:
|
|
17
|
+
files: dict[str, dict[str, str]] = {}
|
|
18
|
+
for p in sorted(ARCHIVED.rglob("*.md")):
|
|
19
|
+
rel = str(p.relative_to(ARCHIVED))
|
|
20
|
+
files[rel] = {"sha256": hashlib.sha256(p.read_bytes()).hexdigest()}
|
|
21
|
+
(ARCHIVED / "manifest.json").write_text(
|
|
22
|
+
json.dumps({"files": files}, indent=2) + "\n", encoding="utf-8"
|
|
23
|
+
)
|
|
24
|
+
print(f"archive_notes: sealed {len(files)} file(s)")
|
|
25
|
+
return 0
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
if __name__ == "__main__":
|
|
29
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Report what a change touches, and suggest the smallest gate set.
|
|
3
|
+
|
|
4
|
+
This is the "check only what changed" hint (rule 9). It does not run gates;
|
|
5
|
+
it maps the touched surfaces to the gates that cover them so a developer
|
|
6
|
+
picks the smallest sufficient set instead of reflexively running everything.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
SURFACE_GATES = {
|
|
15
|
+
"governance": ["self-test"],
|
|
16
|
+
"notes": ["notes"],
|
|
17
|
+
"docs": ["pairing", "links"],
|
|
18
|
+
"config": ["self-test"],
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _classify(path: str) -> str:
|
|
23
|
+
if path.startswith(".agents/notes/"):
|
|
24
|
+
return "notes"
|
|
25
|
+
if path.endswith(".md"):
|
|
26
|
+
return "docs"
|
|
27
|
+
if path in ("gates.json",) or path.startswith(("gates.py", "verify_", "self_test", "gov.py", "change_scope")):
|
|
28
|
+
return "governance"
|
|
29
|
+
return "code"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def main(argv: list[str] | None = None) -> int:
|
|
33
|
+
parser = argparse.ArgumentParser(description="Report touched surfaces since a base ref.")
|
|
34
|
+
parser.add_argument("--base", default="HEAD~1", help="git ref to diff against")
|
|
35
|
+
args = parser.parse_args(argv)
|
|
36
|
+
try:
|
|
37
|
+
proc = subprocess.run(
|
|
38
|
+
["git", "diff", "--name-only", args.base],
|
|
39
|
+
capture_output=True,
|
|
40
|
+
text=True,
|
|
41
|
+
check=True,
|
|
42
|
+
)
|
|
43
|
+
except subprocess.CalledProcessError as e:
|
|
44
|
+
print(f"change_scope: git diff failed: {e.stderr.strip()}", file=sys.stderr)
|
|
45
|
+
return 2
|
|
46
|
+
files = [f for f in proc.stdout.splitlines() if f]
|
|
47
|
+
if not files:
|
|
48
|
+
print(f"change_scope: no changes since {args.base}")
|
|
49
|
+
return 0
|
|
50
|
+
|
|
51
|
+
surfaces = sorted({_classify(f) for f in files})
|
|
52
|
+
gates = sorted({g for s in surfaces for g in SURFACE_GATES.get(s, [])})
|
|
53
|
+
print(f"touched surfaces: {', '.join(surfaces)}")
|
|
54
|
+
for s in surfaces:
|
|
55
|
+
changed = [f for f in files if _classify(f) == s]
|
|
56
|
+
print(f" {s}: {len(changed)} file(s)")
|
|
57
|
+
print(f"suggested gates: {', '.join(gates) or 'code gates (project toolchain)'}")
|
|
58
|
+
return 0
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
62
|
+
raise SystemExit(main())
|
govrail-0.1.0/gov/cli.py
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""gov — install, uninstall, run, and self-test the governance plane.
|
|
3
|
+
|
|
4
|
+
Subcommands delegate to the modules in this package; ``init`` injects the
|
|
5
|
+
templates shipped as package data, and ``uninstall`` reverses it exactly via
|
|
6
|
+
the ``.gov/manifest.json`` it wrote.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import shutil
|
|
12
|
+
import sys
|
|
13
|
+
from importlib.resources import files
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from . import archive_notes, change_scope, gates, self_test
|
|
17
|
+
from . import verify_notes, verify_translation_pairing
|
|
18
|
+
from . import __version__
|
|
19
|
+
|
|
20
|
+
TEMPLATES = files("gov.templates")
|
|
21
|
+
REFERENCE_MARKER = "<!-- gov:rules -->"
|
|
22
|
+
REFERENCE_LINE = (
|
|
23
|
+
f"{REFERENCE_MARKER} Read .gov/rules.md and follow it before starting work."
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _copy(source, dest: Path) -> None:
|
|
28
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
29
|
+
with source.open("rb") as f:
|
|
30
|
+
dest.write_bytes(f.read())
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _remove_empty_dirs(root: Path) -> None:
|
|
34
|
+
"""Remove empty parent dirs, deepest first, stopping at the first non-empty."""
|
|
35
|
+
p = root
|
|
36
|
+
while p != p.parent:
|
|
37
|
+
try:
|
|
38
|
+
p.rmdir()
|
|
39
|
+
except OSError:
|
|
40
|
+
break
|
|
41
|
+
p = p.parent
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def init(project: Path) -> int:
|
|
45
|
+
project = project.resolve()
|
|
46
|
+
if not project.is_dir():
|
|
47
|
+
print(f"init: {project} is not a directory", file=sys.stderr)
|
|
48
|
+
return 2
|
|
49
|
+
if (project / ".gov" / "manifest.json").exists():
|
|
50
|
+
print(f"init: {project} is already initialized")
|
|
51
|
+
return 0
|
|
52
|
+
|
|
53
|
+
gov_dir = project / ".gov"
|
|
54
|
+
created: list[str] = []
|
|
55
|
+
|
|
56
|
+
_copy(TEMPLATES.joinpath("rules.md"), gov_dir / "rules.md")
|
|
57
|
+
|
|
58
|
+
if not (project / "gates.json").exists():
|
|
59
|
+
_copy(TEMPLATES.joinpath("gates.json"), project / "gates.json")
|
|
60
|
+
created.append("gates.json")
|
|
61
|
+
|
|
62
|
+
notes_readme = project / ".agents" / "notes" / "README.md"
|
|
63
|
+
if not notes_readme.exists():
|
|
64
|
+
_copy(TEMPLATES.joinpath("notes-README.md"), notes_readme)
|
|
65
|
+
created.append(".agents/notes/README.md")
|
|
66
|
+
|
|
67
|
+
ag = project / "AGENTS.md"
|
|
68
|
+
if ag.exists():
|
|
69
|
+
text = ag.read_text(encoding="utf-8")
|
|
70
|
+
if REFERENCE_MARKER not in text:
|
|
71
|
+
if text and not text.endswith("\n"):
|
|
72
|
+
text += "\n"
|
|
73
|
+
ag.write_text(text + REFERENCE_LINE + "\n", encoding="utf-8")
|
|
74
|
+
else:
|
|
75
|
+
ag.write_text(REFERENCE_LINE + "\n", encoding="utf-8")
|
|
76
|
+
|
|
77
|
+
(gov_dir / "manifest.json").write_text(
|
|
78
|
+
json.dumps({"version": "0.1.0", "created": created}, indent=2) + "\n",
|
|
79
|
+
encoding="utf-8",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
print(f"init: initialized {project}")
|
|
83
|
+
print(" .gov/rules.md (rules)")
|
|
84
|
+
if created:
|
|
85
|
+
print(" " + ", ".join(created) + " (created; project had none)")
|
|
86
|
+
print(" AGENTS.md reference line")
|
|
87
|
+
return 0
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def uninstall(project: Path) -> int:
|
|
91
|
+
project = project.resolve()
|
|
92
|
+
manifest = project / ".gov" / "manifest.json"
|
|
93
|
+
if not manifest.exists():
|
|
94
|
+
print(f"uninstall: {project} is not initialized", file=sys.stderr)
|
|
95
|
+
return 2
|
|
96
|
+
try:
|
|
97
|
+
data = json.loads(manifest.read_text(encoding="utf-8"))
|
|
98
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
99
|
+
print(f"uninstall: corrupt manifest {manifest}: {e}", file=sys.stderr)
|
|
100
|
+
return 2
|
|
101
|
+
|
|
102
|
+
ag = project / "AGENTS.md"
|
|
103
|
+
if ag.exists():
|
|
104
|
+
kept = [line for line in ag.read_text(encoding="utf-8").splitlines()
|
|
105
|
+
if REFERENCE_MARKER not in line]
|
|
106
|
+
while kept and kept[-1] == "":
|
|
107
|
+
kept.pop()
|
|
108
|
+
if kept:
|
|
109
|
+
ag.write_text("\n".join(kept) + "\n", encoding="utf-8")
|
|
110
|
+
else:
|
|
111
|
+
ag.unlink()
|
|
112
|
+
|
|
113
|
+
for rel in data.get("created", []):
|
|
114
|
+
p = project / rel
|
|
115
|
+
if p.exists():
|
|
116
|
+
p.unlink()
|
|
117
|
+
_remove_empty_dirs(p.parent)
|
|
118
|
+
|
|
119
|
+
shutil.rmtree(project / ".gov", ignore_errors=True)
|
|
120
|
+
print(f"uninstall: removed governance from {project}")
|
|
121
|
+
return 0
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
_COMMANDS = {
|
|
125
|
+
"init": "inject the plane into a project",
|
|
126
|
+
"uninstall": "reverse init",
|
|
127
|
+
"run": "run the project's gate DAG (args forwarded to gates.py)",
|
|
128
|
+
"self-test": "run governance rejection cases",
|
|
129
|
+
"verify-notes": "check note format",
|
|
130
|
+
"verify-pairing": "check bilingual pairing (e.g. --write)",
|
|
131
|
+
"change-scope": "report touched surfaces (e.g. --base <ref>)",
|
|
132
|
+
"archive-notes": "seal the archived-notes manifest",
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _usage() -> None:
|
|
137
|
+
print("usage: gov <command> [args]", file=sys.stderr)
|
|
138
|
+
print("commands:", file=sys.stderr)
|
|
139
|
+
for name, help_text in _COMMANDS.items():
|
|
140
|
+
print(f" {name:<16} {help_text}", file=sys.stderr)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
_HELP_FLAGS = ("-h", "--help", "help")
|
|
144
|
+
_VERSION_FLAGS = ("-v", "--version", "version")
|
|
145
|
+
# Commands whose args are NOT forwarded to an argparse parser: they must
|
|
146
|
+
# intercept help/version themselves so a trailing flag never runs the action.
|
|
147
|
+
_NO_FORWARD = ("init", "uninstall", "self-test", "verify-notes", "archive-notes")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _init_uninstall_args(args: list[str], what: str) -> Path | None:
|
|
151
|
+
"""Parse the only supported flag ``--project <dir>``; reject the rest."""
|
|
152
|
+
project = "."
|
|
153
|
+
i = 0
|
|
154
|
+
while i < len(args):
|
|
155
|
+
a = args[i]
|
|
156
|
+
if a == "--project":
|
|
157
|
+
if i + 1 >= len(args):
|
|
158
|
+
print(f"gov {what}: --project requires a directory", file=sys.stderr)
|
|
159
|
+
return None
|
|
160
|
+
project = args[i + 1]
|
|
161
|
+
i += 2
|
|
162
|
+
else:
|
|
163
|
+
print(f"gov {what}: unexpected argument '{a}'", file=sys.stderr)
|
|
164
|
+
_usage()
|
|
165
|
+
return None
|
|
166
|
+
return Path(project)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def main(argv: list[str] | None = None) -> int:
|
|
170
|
+
argv = list(sys.argv[1:] if argv is None else argv)
|
|
171
|
+
if not argv:
|
|
172
|
+
_usage()
|
|
173
|
+
return 2
|
|
174
|
+
cmd, rest = argv[0], argv[1:]
|
|
175
|
+
|
|
176
|
+
if cmd in _HELP_FLAGS:
|
|
177
|
+
_usage()
|
|
178
|
+
return 0
|
|
179
|
+
if cmd in _VERSION_FLAGS:
|
|
180
|
+
print(f"gov {__version__}")
|
|
181
|
+
return 0
|
|
182
|
+
# Subcommand-level help/version: never execute the action as a side effect.
|
|
183
|
+
if cmd in _NO_FORWARD:
|
|
184
|
+
if any(a in _HELP_FLAGS for a in rest):
|
|
185
|
+
_usage()
|
|
186
|
+
return 0
|
|
187
|
+
if any(a in _VERSION_FLAGS for a in rest):
|
|
188
|
+
print(f"gov {__version__}")
|
|
189
|
+
return 0
|
|
190
|
+
if cmd == "init":
|
|
191
|
+
project = _init_uninstall_args(rest, "init")
|
|
192
|
+
return 2 if project is None else init(project)
|
|
193
|
+
if cmd == "uninstall":
|
|
194
|
+
project = _init_uninstall_args(rest, "uninstall")
|
|
195
|
+
return 2 if project is None else uninstall(project)
|
|
196
|
+
if cmd == "run":
|
|
197
|
+
return gates.main(rest)
|
|
198
|
+
if cmd == "self-test":
|
|
199
|
+
return self_test.main()
|
|
200
|
+
if cmd == "verify-notes":
|
|
201
|
+
return verify_notes.main()
|
|
202
|
+
if cmd == "verify-pairing":
|
|
203
|
+
return verify_translation_pairing.main(rest)
|
|
204
|
+
if cmd == "change-scope":
|
|
205
|
+
return change_scope.main(rest)
|
|
206
|
+
if cmd == "archive-notes":
|
|
207
|
+
return archive_notes.main()
|
|
208
|
+
print(f"gov: unknown command '{cmd}'", file=sys.stderr)
|
|
209
|
+
_usage()
|
|
210
|
+
return 2
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
if __name__ == "__main__":
|
|
214
|
+
raise SystemExit(main())
|