code-mower 0.5.0b5__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.
- code_mower/__init__.py +3 -0
- code_mower/adapters/__init__.py +39 -0
- code_mower/adapters/_base.py +148 -0
- code_mower/adapters/cursor_bugbot.py +97 -0
- code_mower/adapters/gitar.py +111 -0
- code_mower/adapters/greptile.py +191 -0
- code_mower/adapters/qodo.py +140 -0
- code_mower/antigravity_cli_audit_pr.py +241 -0
- code_mower/audit_handoff_log.py +345 -0
- code_mower/audit_labeler_lib.py +311 -0
- code_mower/audit_progress.py +203 -0
- code_mower/blind_review_artifacts.py +562 -0
- code_mower/blind_review_coordinator.py +276 -0
- code_mower/bootstrap.py +524 -0
- code_mower/builder_experiment.py +539 -0
- code_mower/calibration/__init__.py +181 -0
- code_mower/calibration/arms.py +267 -0
- code_mower/calibration/auto_discovery.py +348 -0
- code_mower/calibration/commands.py +192 -0
- code_mower/calibration/context_inputs.py +199 -0
- code_mower/calibration/corpus.py +91 -0
- code_mower/calibration/evidence.py +20 -0
- code_mower/calibration/evidence_report.py +360 -0
- code_mower/calibration/identity.py +27 -0
- code_mower/calibration/metrics.py +18 -0
- code_mower/calibration/overlap.py +103 -0
- code_mower/calibration/planning.py +309 -0
- code_mower/calibration/policy.py +207 -0
- code_mower/calibration/results.py +315 -0
- code_mower/calibration/run_results.py +147 -0
- code_mower/calibration/run_status.py +64 -0
- code_mower/calibration/runner.py +360 -0
- code_mower/calibration/truth.py +188 -0
- code_mower/calibration/value_report.py +142 -0
- code_mower/checks.py +402 -0
- code_mower/claude_audit_pr.py +1126 -0
- code_mower/claude_cli_bounce.py +303 -0
- code_mower/claude_cli_environment.py +73 -0
- code_mower/clear_stale.py +374 -0
- code_mower/cli.py +537 -0
- code_mower/cloud.py +674 -0
- code_mower/cloud_client/__init__.py +173 -0
- code_mower/cloud_client/bundle.py +155 -0
- code_mower/cloud_client/doctor.py +206 -0
- code_mower/cloud_client/dogfood.py +78 -0
- code_mower/cloud_client/endpoints.py +114 -0
- code_mower/cloud_client/errors.py +7 -0
- code_mower/cloud_client/events.py +278 -0
- code_mower/cloud_client/export.py +272 -0
- code_mower/cloud_client/git_metadata.py +46 -0
- code_mower/cloud_client/manifest.py +39 -0
- code_mower/cloud_client/operations.py +448 -0
- code_mower/cloud_client/reports.py +45 -0
- code_mower/cloud_client/setup.py +205 -0
- code_mower/cloud_client/upload.py +97 -0
- code_mower/code_mower_calibration.py +598 -0
- code_mower/code_mower_context_packs.py +591 -0
- code_mower/code_mower_merge.py +227 -0
- code_mower/code_mower_telemetry.py +561 -0
- code_mower/coderabbit_cli_audit_pr.py +526 -0
- code_mower/codex_audit_env_preflight.py +220 -0
- code_mower/codex_audit_pr.py +1738 -0
- code_mower/codex_audit_schema_smoke.py +160 -0
- code_mower/codex_audit_verdict.schema.json +44 -0
- code_mower/config.py +655 -0
- code_mower/doctor.py +161 -0
- code_mower/doctor_checks/__init__.py +104 -0
- code_mower/doctor_checks/cloud.py +129 -0
- code_mower/doctor_checks/common.py +215 -0
- code_mower/doctor_checks/github.py +128 -0
- code_mower/doctor_checks/github_actions.py +11 -0
- code_mower/doctor_checks/github_actions_cost.py +99 -0
- code_mower/doctor_checks/github_actions_cost_summary.py +111 -0
- code_mower/doctor_checks/github_actions_failure_annotations.py +27 -0
- code_mower/doctor_checks/github_actions_failure_models.py +47 -0
- code_mower/doctor_checks/github_actions_failure_scan.py +200 -0
- code_mower/doctor_checks/github_actions_failure_selection.py +63 -0
- code_mower/doctor_checks/github_actions_failures.py +103 -0
- code_mower/doctor_checks/github_actions_permissions.py +55 -0
- code_mower/doctor_checks/github_api.py +79 -0
- code_mower/doctor_checks/github_branch.py +56 -0
- code_mower/doctor_checks/github_config.py +25 -0
- code_mower/doctor_checks/github_provider.py +61 -0
- code_mower/doctor_checks/github_repo.py +120 -0
- code_mower/doctor_checks/groups.py +36 -0
- code_mower/doctor_checks/models.py +96 -0
- code_mower/doctor_checks/output.py +86 -0
- code_mower/doctor_checks/presets.py +64 -0
- code_mower/doctor_checks/privacy.py +20 -0
- code_mower/doctor_checks/provider_api_model.py +138 -0
- code_mower/doctor_checks/provider_api_model_openai.py +29 -0
- code_mower/doctor_checks/provider_api_model_profiles.py +137 -0
- code_mower/doctor_checks/provider_env.py +113 -0
- code_mower/doctor_checks/provider_env_required.py +56 -0
- code_mower/doctor_checks/provider_env_tokens.py +100 -0
- code_mower/doctor_checks/provider_local_cli.py +162 -0
- code_mower/doctor_checks/provider_local_cli_commands.py +47 -0
- code_mower/doctor_checks/provider_local_cli_probe_config.py +70 -0
- code_mower/doctor_checks/provider_probe.py +20 -0
- code_mower/doctor_checks/provider_probe_auth.py +52 -0
- code_mower/doctor_checks/provider_probe_evaluation.py +109 -0
- code_mower/doctor_checks/provider_probe_json.py +45 -0
- code_mower/doctor_checks/provider_probe_remediation.py +39 -0
- code_mower/doctor_checks/providers.py +159 -0
- code_mower/doctor_checks/registry.py +69 -0
- code_mower/doctor_checks/runner.py +188 -0
- code_mower/doctor_checks/runtime.py +89 -0
- code_mower/doctor_checks/runtime_github_auth.py +148 -0
- code_mower/gemini_cli_audit_pr.py +897 -0
- code_mower/hermes_cli_audit_pr.py +436 -0
- code_mower/init.py +888 -0
- code_mower/lane_configs/__init__.py +37 -0
- code_mower/lane_configs/aider.py +32 -0
- code_mower/lane_configs/antigravity_cli.py +35 -0
- code_mower/lane_configs/claude.py +35 -0
- code_mower/lane_configs/codex.py +32 -0
- code_mower/lane_configs/devin.py +33 -0
- code_mower/lane_configs/gemini_cli.py +35 -0
- code_mower/lane_configs/hermes_cli.py +35 -0
- code_mower/lane_configs/local_llm.py +31 -0
- code_mower/local_llm_audit_pr.py +1364 -0
- code_mower/local_llm_bakeoff.py +458 -0
- code_mower/local_llm_calibration.py +441 -0
- code_mower/local_llm_profiles.py +66 -0
- code_mower/migration.py +508 -0
- code_mower/migration_install.py +292 -0
- code_mower/migration_mirror.py +392 -0
- code_mower/migration_readiness.py +237 -0
- code_mower/migration_rehearsal.py +718 -0
- code_mower/next_steps.py +441 -0
- code_mower/package.py +673 -0
- code_mower/package_content.py +444 -0
- code_mower/package_manifest.py +452 -0
- code_mower/package_paths.py +53 -0
- code_mower/package_rendering.py +90 -0
- code_mower/package_static.py +585 -0
- code_mower/prompts.py +267 -0
- code_mower/provider_registry.py +469 -0
- code_mower/provider_runners/__init__.py +60 -0
- code_mower/provider_runners/comments.py +31 -0
- code_mower/provider_runners/git.py +46 -0
- code_mower/provider_runners/github_auth.py +61 -0
- code_mower/provider_runners/github_pr.py +120 -0
- code_mower/provider_runners/process.py +58 -0
- code_mower/provider_runners/repo_paths.py +23 -0
- code_mower/provider_runners/text_schema.py +41 -0
- code_mower/provider_runners/verdict_artifacts.py +103 -0
- code_mower/provider_runners/workspace.py +57 -0
- code_mower/release_readiness.py +549 -0
- code_mower/reviewer_metrics.py +389 -0
- code_mower/saas_reviewer_labeler.py +809 -0
- code_mower/secrets.py +89 -0
- code_mower/templates/builder-experiment.example.json +55 -0
- code_mower/templates/calibration-corpus.example.json +129 -0
- code_mower/templates/calibration-corpus.json +129 -0
- code_mower/templates/code-mower.example.yml +423 -0
- code_mower/templates/context-packs.example.json +150 -0
- code_mower/templates/lane_prompts/base-audit.md +22 -0
- code_mower/templates/lane_prompts/calibration-policy.md +21 -0
- code_mower/templates/lane_prompts/context-driven-quality.md +21 -0
- code_mower/templates/lane_prompts/docs-design.md +12 -0
- code_mower/templates/lane_prompts/generic-programming.md +21 -0
- code_mower/templates/lane_prompts/operability.md +22 -0
- code_mower/templates/lane_prompts/package-runtime.md +12 -0
- code_mower/templates/lane_prompts/security-threat-model.md +22 -0
- code_mower/templates/product-support/code_mower +216 -0
- code_mower/templates/product-support/code_mower_standalone_pin.env +7 -0
- code_mower/templates/product-support/code_mower_standalone_shadow.sh +151 -0
- code_mower/templates/product-support/run_claude_audit_pr.sh +32 -0
- code_mower/templates/product-support/run_codex_audit_pr.sh +32 -0
- code_mower/templates/product-support/safe_gh_comment.py +96 -0
- code_mower/templates/providers.yml +454 -0
- code_mower/templates/reviewer-spend.example.json +28 -0
- code_mower/templates/reviewer-value-report.example.md +20 -0
- code_mower/templates/workflows/private-standalone-shadow.yml.j2 +106 -0
- code_mower/templates/workflows/review-clear-stale.yml.j2 +83 -0
- code_mower/trailer_comment_labeler.py +207 -0
- code_mower/versioning.py +32 -0
- code_mower-0.5.0b5.dist-info/METADATA +302 -0
- code_mower-0.5.0b5.dist-info/RECORD +185 -0
- code_mower-0.5.0b5.dist-info/WHEEL +5 -0
- code_mower-0.5.0b5.dist-info/entry_points.txt +2 -0
- code_mower-0.5.0b5.dist-info/licenses/LICENSE +202 -0
- code_mower-0.5.0b5.dist-info/licenses/NOTICE +10 -0
- code_mower-0.5.0b5.dist-info/top_level.txt +1 -0
code_mower/prompts.py
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Load versioned Code Mower review prompt lenses."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import argparse
|
|
8
|
+
import json
|
|
9
|
+
import re
|
|
10
|
+
import subprocess
|
|
11
|
+
import sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, Iterable, Mapping
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
DEFAULT_REVIEW_LENSES = ("base-audit",)
|
|
17
|
+
SAFE_LENS_RE = re.compile(r"^[A-Za-z0-9_.-]+$")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def split_lenses(value: str | Iterable[str] | None) -> tuple[str, ...]:
|
|
21
|
+
"""Return normalized prompt lens ids from comma text or an iterable."""
|
|
22
|
+
|
|
23
|
+
if value is None:
|
|
24
|
+
return DEFAULT_REVIEW_LENSES
|
|
25
|
+
if isinstance(value, str):
|
|
26
|
+
lenses = tuple(item.strip() for item in value.split(",") if item.strip())
|
|
27
|
+
else:
|
|
28
|
+
lenses = tuple(str(item).strip() for item in value if str(item).strip())
|
|
29
|
+
return lenses or DEFAULT_REVIEW_LENSES
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def default_prompt_dirs() -> tuple[Path, ...]:
|
|
33
|
+
module_dir = Path(__file__).resolve().parent
|
|
34
|
+
return (
|
|
35
|
+
module_dir / "lane_prompts",
|
|
36
|
+
module_dir / "templates" / "lane_prompts",
|
|
37
|
+
module_dir.parent / "tools" / "lane_prompts",
|
|
38
|
+
Path.cwd() / "tools" / "lane_prompts",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def packaged_prompt_dir() -> Path | None:
|
|
43
|
+
module_dir = Path(__file__).resolve().parent
|
|
44
|
+
if module_dir.name == "tools":
|
|
45
|
+
return None
|
|
46
|
+
candidate = module_dir / "templates" / "lane_prompts"
|
|
47
|
+
return candidate if candidate.is_dir() else None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def resolve_prompt_dir(prompt_dir: str | Path | None = None) -> Path:
|
|
51
|
+
if prompt_dir is not None and str(prompt_dir).strip():
|
|
52
|
+
return Path(prompt_dir).expanduser()
|
|
53
|
+
|
|
54
|
+
env_dir = os.environ.get("CODE_MOWER_PROMPT_DIR")
|
|
55
|
+
if env_dir:
|
|
56
|
+
return Path(env_dir).expanduser()
|
|
57
|
+
|
|
58
|
+
for candidate in default_prompt_dirs():
|
|
59
|
+
if candidate.is_dir():
|
|
60
|
+
return candidate
|
|
61
|
+
return default_prompt_dirs()[0]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _read_lens(prompt_dir: Path, lens: str) -> str:
|
|
65
|
+
if not SAFE_LENS_RE.fullmatch(lens):
|
|
66
|
+
raise ValueError(
|
|
67
|
+
"review lens ids must match [A-Za-z0-9_.-]+: "
|
|
68
|
+
f"{lens!r}"
|
|
69
|
+
)
|
|
70
|
+
path = prompt_dir / f"{lens}.md"
|
|
71
|
+
if not path.is_file():
|
|
72
|
+
raise FileNotFoundError(f"review lens not found: {path}")
|
|
73
|
+
return path.read_text(encoding="utf-8").strip()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _lens_title(text: str, lens_id: str) -> str:
|
|
77
|
+
for line in text.splitlines():
|
|
78
|
+
stripped = line.strip()
|
|
79
|
+
if stripped.startswith("# "):
|
|
80
|
+
return stripped[2:].strip() or lens_id
|
|
81
|
+
return lens_id
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def list_prompt_lenses(prompt_dir: str | Path | None = None) -> list[dict[str, Any]]:
|
|
85
|
+
root = resolve_prompt_dir(prompt_dir)
|
|
86
|
+
if not root.is_dir():
|
|
87
|
+
raise FileNotFoundError(f"prompt directory not found: {root}")
|
|
88
|
+
lenses: list[dict[str, Any]] = []
|
|
89
|
+
for path in sorted(root.glob("*.md")):
|
|
90
|
+
lens_id = path.stem
|
|
91
|
+
if not SAFE_LENS_RE.fullmatch(lens_id):
|
|
92
|
+
continue
|
|
93
|
+
text = path.read_text(encoding="utf-8")
|
|
94
|
+
lenses.append(
|
|
95
|
+
{
|
|
96
|
+
"id": lens_id,
|
|
97
|
+
"title": _lens_title(text, lens_id),
|
|
98
|
+
"path": str(path),
|
|
99
|
+
"bytes": path.stat().st_size,
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
return lenses
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _read_git_lens(repo_root: Path, trusted_git_ref: str, lens: str) -> str | None:
|
|
106
|
+
if not SAFE_LENS_RE.fullmatch(lens):
|
|
107
|
+
raise ValueError(
|
|
108
|
+
"review lens ids must match [A-Za-z0-9_.-]+: "
|
|
109
|
+
f"{lens!r}"
|
|
110
|
+
)
|
|
111
|
+
result = subprocess.run(
|
|
112
|
+
[
|
|
113
|
+
"git",
|
|
114
|
+
"-C",
|
|
115
|
+
str(repo_root),
|
|
116
|
+
"show",
|
|
117
|
+
f"{trusted_git_ref}:tools/lane_prompts/{lens}.md",
|
|
118
|
+
],
|
|
119
|
+
text=True,
|
|
120
|
+
capture_output=True,
|
|
121
|
+
check=False,
|
|
122
|
+
)
|
|
123
|
+
if result.returncode != 0:
|
|
124
|
+
return None
|
|
125
|
+
return result.stdout.strip()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def load_review_prompt(
|
|
129
|
+
lenses: str | Iterable[str] | None = None,
|
|
130
|
+
*,
|
|
131
|
+
prompt_dir: str | Path | None = None,
|
|
132
|
+
trusted_git_ref: str | None = None,
|
|
133
|
+
repo_root: str | Path | None = None,
|
|
134
|
+
missing_ok: bool = False,
|
|
135
|
+
) -> str:
|
|
136
|
+
"""Load and concatenate one or more trusted review prompt lenses."""
|
|
137
|
+
|
|
138
|
+
explicit_prompt_dir = prompt_dir is not None or bool(os.environ.get("CODE_MOWER_PROMPT_DIR"))
|
|
139
|
+
sections = []
|
|
140
|
+
for lens in split_lenses(lenses):
|
|
141
|
+
text: str | None = None
|
|
142
|
+
if not explicit_prompt_dir and trusted_git_ref and repo_root is not None:
|
|
143
|
+
text = _read_git_lens(Path(repo_root), trusted_git_ref, lens)
|
|
144
|
+
if text is None:
|
|
145
|
+
package_dir = packaged_prompt_dir()
|
|
146
|
+
if package_dir is not None:
|
|
147
|
+
try:
|
|
148
|
+
text = _read_lens(package_dir, lens)
|
|
149
|
+
except FileNotFoundError:
|
|
150
|
+
text = None
|
|
151
|
+
if text is None and not missing_ok:
|
|
152
|
+
raise FileNotFoundError(
|
|
153
|
+
"review lens not found in trusted git ref: "
|
|
154
|
+
f"{trusted_git_ref}:tools/lane_prompts/{lens}.md"
|
|
155
|
+
)
|
|
156
|
+
else:
|
|
157
|
+
root = resolve_prompt_dir(prompt_dir)
|
|
158
|
+
try:
|
|
159
|
+
text = _read_lens(root, lens)
|
|
160
|
+
except FileNotFoundError:
|
|
161
|
+
if not missing_ok:
|
|
162
|
+
raise
|
|
163
|
+
text = None
|
|
164
|
+
if text is not None:
|
|
165
|
+
sections.append(f"## Review Lens: {lens}\n\n{text}")
|
|
166
|
+
if not sections and missing_ok:
|
|
167
|
+
return ""
|
|
168
|
+
return "\n\n".join(sections).strip() + "\n"
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def append_review_prompt(system_prompt: str, review_prompt: str) -> str:
|
|
172
|
+
"""Append trusted review doctrine without changing a prompt's opening line."""
|
|
173
|
+
|
|
174
|
+
if not review_prompt.strip():
|
|
175
|
+
return system_prompt
|
|
176
|
+
return (
|
|
177
|
+
system_prompt.rstrip()
|
|
178
|
+
+ "\n\n# Code Mower Review Doctrine\n\n"
|
|
179
|
+
+ review_prompt.strip()
|
|
180
|
+
+ "\n"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def render_prompt_lenses_text(lenses: Iterable[Mapping[str, Any]]) -> str:
|
|
185
|
+
lines = ["Code Mower prompt lenses", ""]
|
|
186
|
+
count = 0
|
|
187
|
+
for lens in lenses:
|
|
188
|
+
count += 1
|
|
189
|
+
lines.append(f"- {lens.get('id')}: {lens.get('title')}")
|
|
190
|
+
if count == 0:
|
|
191
|
+
lines.append("- none")
|
|
192
|
+
return "\n".join(lines) + "\n"
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def main(argv: list[str] | None = None) -> int:
|
|
196
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
197
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
198
|
+
|
|
199
|
+
list_parser = subparsers.add_parser("list")
|
|
200
|
+
list_parser.add_argument("--prompt-dir", default=None)
|
|
201
|
+
list_parser.add_argument("--json", action="store_true")
|
|
202
|
+
|
|
203
|
+
show_parser = subparsers.add_parser("show")
|
|
204
|
+
show_parser.add_argument("lenses", nargs="?", default=",".join(DEFAULT_REVIEW_LENSES))
|
|
205
|
+
show_parser.add_argument("--prompt-dir", default=None)
|
|
206
|
+
show_parser.add_argument("--json", action="store_true")
|
|
207
|
+
|
|
208
|
+
validate_parser = subparsers.add_parser("validate")
|
|
209
|
+
validate_parser.add_argument("--lenses", default=",".join(DEFAULT_REVIEW_LENSES))
|
|
210
|
+
validate_parser.add_argument("--prompt-dir", default=None)
|
|
211
|
+
validate_parser.add_argument("--json", action="store_true")
|
|
212
|
+
|
|
213
|
+
args = parser.parse_args(argv)
|
|
214
|
+
|
|
215
|
+
try:
|
|
216
|
+
if args.command == "list":
|
|
217
|
+
lenses = list_prompt_lenses(args.prompt_dir)
|
|
218
|
+
payload = {
|
|
219
|
+
"mode": "code-mower-prompt-lenses",
|
|
220
|
+
"prompt_dir": str(resolve_prompt_dir(args.prompt_dir)),
|
|
221
|
+
"lenses": lenses,
|
|
222
|
+
}
|
|
223
|
+
if args.json:
|
|
224
|
+
print(json.dumps(payload, indent=2, sort_keys=True))
|
|
225
|
+
else:
|
|
226
|
+
print(render_prompt_lenses_text(lenses), end="")
|
|
227
|
+
return 0
|
|
228
|
+
|
|
229
|
+
if args.command == "show":
|
|
230
|
+
selected = split_lenses(args.lenses)
|
|
231
|
+
prompt = load_review_prompt(selected, prompt_dir=args.prompt_dir)
|
|
232
|
+
payload = {
|
|
233
|
+
"mode": "code-mower-review-prompt",
|
|
234
|
+
"prompt_dir": str(resolve_prompt_dir(args.prompt_dir)),
|
|
235
|
+
"lenses": list(selected),
|
|
236
|
+
"prompt": prompt,
|
|
237
|
+
}
|
|
238
|
+
if args.json:
|
|
239
|
+
print(json.dumps(payload, indent=2, sort_keys=True))
|
|
240
|
+
else:
|
|
241
|
+
print(prompt, end="")
|
|
242
|
+
return 0
|
|
243
|
+
|
|
244
|
+
if args.command == "validate":
|
|
245
|
+
selected = split_lenses(args.lenses)
|
|
246
|
+
prompt = load_review_prompt(selected, prompt_dir=args.prompt_dir)
|
|
247
|
+
payload = {
|
|
248
|
+
"mode": "code-mower-prompt-validation",
|
|
249
|
+
"ok": True,
|
|
250
|
+
"prompt_dir": str(resolve_prompt_dir(args.prompt_dir)),
|
|
251
|
+
"lenses": list(selected),
|
|
252
|
+
"bytes": len(prompt.encode("utf-8")),
|
|
253
|
+
}
|
|
254
|
+
if args.json:
|
|
255
|
+
print(json.dumps(payload, indent=2, sort_keys=True))
|
|
256
|
+
else:
|
|
257
|
+
print("ok")
|
|
258
|
+
return 0
|
|
259
|
+
except (FileNotFoundError, ValueError) as exc:
|
|
260
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
261
|
+
return 1
|
|
262
|
+
|
|
263
|
+
raise AssertionError(f"unhandled prompt command: {args.command}")
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
if __name__ == "__main__":
|
|
267
|
+
raise SystemExit(main())
|