prizmkit 1.1.152 → 1.1.154
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.
- package/bundled/VERSION.json +3 -3
- package/bundled/dev-pipeline/README.md +100 -87
- package/bundled/dev-pipeline/assets/skill-subagent-integration.md +1 -1
- package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +14 -0
- package/bundled/dev-pipeline/prizmkit_runtime/cli.py +192 -110
- package/bundled/dev-pipeline/prizmkit_runtime/commands.py +146 -111
- package/bundled/dev-pipeline/prizmkit_runtime/daemon.py +3 -5
- package/bundled/dev-pipeline/prizmkit_runtime/interoperability.py +0 -1
- package/bundled/dev-pipeline/prizmkit_runtime/paths.py +0 -3
- package/bundled/dev-pipeline/prizmkit_runtime/reset.py +122 -15
- package/bundled/dev-pipeline/prizmkit_runtime/reset_preserve.py +1 -1
- package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +1 -1
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +19 -52
- package/bundled/dev-pipeline/prizmkit_runtime/status.py +106 -1
- package/bundled/dev-pipeline/scripts/init-bugfix-pipeline.py +2 -2
- package/bundled/dev-pipeline/scripts/parse-stream-progress.py +10 -12
- package/bundled/dev-pipeline/scripts/update-bug-status.py +196 -67
- package/bundled/dev-pipeline/scripts/update-checkpoint.py +16 -3
- package/bundled/dev-pipeline/scripts/update-feature-status.py +45 -117
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +45 -119
- package/bundled/dev-pipeline/scripts/utils.py +119 -0
- package/bundled/dev-pipeline/templates/bug-fix-list-schema.json +3 -2
- package/bundled/dev-pipeline/tests/test_auto_skip.py +111 -32
- package/bundled/dev-pipeline/tests/test_checkpoint_state.py +148 -12
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +0 -19
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +113 -188
- package/bundled/dev-pipeline/tests/test_recovery_workflow.py +211 -0
- package/bundled/dev-pipeline/tests/test_reset_modes.py +935 -0
- package/bundled/dev-pipeline/tests/test_reset_preserve.py +5 -5
- package/bundled/dev-pipeline/tests/test_unified_cli.py +544 -181
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/bug-planner/references/schema-validation.md +1 -1
- package/bundled/skills/bug-planner/scripts/validate-bug-list.py +1 -1
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +21 -13
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +22 -14
- package/bundled/skills/recovery-workflow/SKILL.md +7 -5
- package/bundled/skills/recovery-workflow/evals/evals.json +2 -2
- package/bundled/skills/recovery-workflow/references/detection.md +3 -3
- package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +1 -1
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +21 -13
- package/bundled/templates/project-memory-template.md +19 -11
- package/package.json +1 -1
- package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +0 -228
- package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +0 -767
|
@@ -9,136 +9,231 @@ from collections.abc import Sequence
|
|
|
9
9
|
from .commands import CommandResult, handle_diagnostics, handle_runtime_command
|
|
10
10
|
from .compat import UnsupportedPythonVersion, ensure_supported_python
|
|
11
11
|
from .paths import resolve_runtime_paths
|
|
12
|
-
from .runtime_helper import render_result, run_helper_command
|
|
13
12
|
|
|
14
13
|
DESCRIPTION = (
|
|
15
14
|
"PrizmKit canonical Python runtime CLI.\n"
|
|
16
|
-
"Runs pipeline, reset, daemon,
|
|
15
|
+
"Runs operation-first pipeline, status, reset, daemon, diagnostics, and helper commands."
|
|
17
16
|
)
|
|
18
17
|
|
|
18
|
+
_FAMILIES = ("feature", "bugfix", "refactor")
|
|
19
|
+
_GLOBAL_OPTIONS_WITH_VALUES = {"--project-root", "--pipeline-root"}
|
|
20
|
+
_LEAF_OPTIONS_WITH_VALUES = {
|
|
21
|
+
"--env",
|
|
22
|
+
"--features",
|
|
23
|
+
"--lines",
|
|
24
|
+
"-n",
|
|
25
|
+
"--max-retries",
|
|
26
|
+
"--max-infra-retries",
|
|
27
|
+
"--mode",
|
|
28
|
+
"--resume-phase",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _set_handler(
|
|
33
|
+
parser: argparse.ArgumentParser,
|
|
34
|
+
group: str,
|
|
35
|
+
action: str,
|
|
36
|
+
target: str | None = None,
|
|
37
|
+
) -> None:
|
|
38
|
+
parser.set_defaults(handler="runtime", group=group, action=action, target=target)
|
|
19
39
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
40
|
+
|
|
41
|
+
def _non_negative_int(value: str) -> int:
|
|
42
|
+
try:
|
|
43
|
+
parsed = int(value)
|
|
44
|
+
except ValueError as exc:
|
|
45
|
+
raise argparse.ArgumentTypeError("must be an integer") from exc
|
|
46
|
+
if parsed < 0:
|
|
47
|
+
raise argparse.ArgumentTypeError("must be non-negative")
|
|
48
|
+
return parsed
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _add_runner_arguments(parser: argparse.ArgumentParser, family: str) -> None:
|
|
52
|
+
parser.add_argument("operand", nargs="?", help="Optional task ID or family plan path.")
|
|
53
|
+
parser.add_argument("second_operand", nargs="?", help="Optional family plan path or task ID.")
|
|
54
|
+
if family == "feature":
|
|
55
|
+
parser.add_argument("--features", help="Feature ID, range, or comma-separated selection.")
|
|
56
|
+
parser.add_argument("--max-retries", type=_non_negative_int)
|
|
57
|
+
parser.add_argument("--max-infra-retries", type=_non_negative_int)
|
|
58
|
+
parser.add_argument("--mode")
|
|
59
|
+
parser.add_argument("--resume-phase")
|
|
60
|
+
parser.add_argument("--dry-run", action="store_true")
|
|
61
|
+
parser.add_argument("--clean", action="store_true")
|
|
62
|
+
parser.add_argument("--no-reset", action="store_true")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _add_run_subparsers(
|
|
66
|
+
subparsers: argparse._SubParsersAction[argparse.ArgumentParser],
|
|
67
|
+
) -> None:
|
|
68
|
+
run_parser = subparsers.add_parser("run", help="Run a foreground pipeline family")
|
|
69
|
+
family_subparsers = run_parser.add_subparsers(dest="run_family", required=True)
|
|
70
|
+
for family in _FAMILIES:
|
|
71
|
+
family_parser = family_subparsers.add_parser(family, help=f"Run the {family} pipeline")
|
|
72
|
+
_add_runner_arguments(family_parser, family)
|
|
73
|
+
_set_handler(family_parser, "run", "run", family)
|
|
26
74
|
|
|
27
75
|
|
|
28
|
-
def
|
|
29
|
-
|
|
76
|
+
def _add_status_subparsers(
|
|
77
|
+
subparsers: argparse._SubParsersAction[argparse.ArgumentParser],
|
|
78
|
+
) -> None:
|
|
79
|
+
status_parser = subparsers.add_parser("status", help="Inspect pipeline task state")
|
|
80
|
+
target_subparsers = status_parser.add_subparsers(dest="status_target", required=True)
|
|
81
|
+
for family in _FAMILIES:
|
|
82
|
+
family_parser = target_subparsers.add_parser(family, help=f"Show {family} status")
|
|
83
|
+
_add_runner_arguments(family_parser, family)
|
|
84
|
+
_set_handler(family_parser, "status", "status", family)
|
|
85
|
+
all_parser = target_subparsers.add_parser(
|
|
86
|
+
"all",
|
|
87
|
+
help="Show read-only status for every configured family",
|
|
88
|
+
)
|
|
89
|
+
_set_handler(all_parser, "status", "status", "all")
|
|
30
90
|
|
|
31
91
|
|
|
32
|
-
def
|
|
92
|
+
def _add_reset_subparsers(
|
|
93
|
+
subparsers: argparse._SubParsersAction[argparse.ArgumentParser],
|
|
94
|
+
) -> None:
|
|
95
|
+
reset_parser = subparsers.add_parser("reset", help="Apply one explicit reset mode")
|
|
96
|
+
family_subparsers = reset_parser.add_subparsers(dest="reset_family", required=True)
|
|
97
|
+
for family in _FAMILIES:
|
|
98
|
+
family_parser = family_subparsers.add_parser(family, help=f"Reset {family} task state")
|
|
99
|
+
family_parser.add_argument("operand", nargs="?", help="Task ID, range, or family plan path.")
|
|
100
|
+
family_parser.add_argument("second_operand", nargs="?", help="Optional family plan path.")
|
|
101
|
+
family_parser.add_argument("--state-only", action="store_true")
|
|
102
|
+
family_parser.add_argument("--fresh-checkout", action="store_true")
|
|
103
|
+
family_parser.add_argument("--clean", action="store_true")
|
|
104
|
+
family_parser.add_argument("--preserve-runtime", action="store_true")
|
|
105
|
+
family_parser.add_argument("--all", action="store_true")
|
|
106
|
+
family_parser.add_argument("--auto-skipped", action="store_true")
|
|
107
|
+
family_parser.add_argument("--failed", action="store_true")
|
|
108
|
+
family_parser.add_argument("--stalled", action="store_true")
|
|
109
|
+
_set_handler(family_parser, "reset", "reset", family)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _add_daemon_start_arguments(parser: argparse.ArgumentParser, family: str) -> None:
|
|
113
|
+
parser.add_argument("list_path", nargs="?", help="Optional family plan path.")
|
|
114
|
+
parser.add_argument("--env", help="Quoted KEY=VALUE daemon environment overrides.")
|
|
115
|
+
parser.add_argument("--mode", choices=("lite", "standard", "full"))
|
|
116
|
+
if family == "feature":
|
|
117
|
+
parser.add_argument("--features", help="Feature ID, range, or comma-separated selection.")
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _add_daemon_subparsers(
|
|
121
|
+
subparsers: argparse._SubParsersAction[argparse.ArgumentParser],
|
|
122
|
+
) -> None:
|
|
123
|
+
daemon_parser = subparsers.add_parser("daemon", help="Manage background pipeline processes")
|
|
124
|
+
family_subparsers = daemon_parser.add_subparsers(dest="daemon_family", required=True)
|
|
125
|
+
for family in _FAMILIES:
|
|
126
|
+
family_parser = family_subparsers.add_parser(family, help=f"Manage the {family} daemon")
|
|
127
|
+
action_subparsers = family_parser.add_subparsers(dest="daemon_action", required=True)
|
|
128
|
+
for action in ("start", "stop", "status", "logs", "restart"):
|
|
129
|
+
action_parser = action_subparsers.add_parser(action, help=f"{action.title()} the {family} daemon")
|
|
130
|
+
if action in {"start", "restart"}:
|
|
131
|
+
_add_daemon_start_arguments(action_parser, family)
|
|
132
|
+
elif action == "logs":
|
|
133
|
+
action_parser.add_argument("--lines", "-n", type=_non_negative_int, default=50)
|
|
134
|
+
action_parser.add_argument("--follow", "-f", action="store_true")
|
|
135
|
+
_set_handler(action_parser, "daemon", action, family)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _add_diagnostics_subparsers(
|
|
33
139
|
subparsers: argparse._SubParsersAction[argparse.ArgumentParser],
|
|
34
|
-
group: str,
|
|
35
|
-
actions: Sequence[str],
|
|
36
140
|
) -> None:
|
|
37
|
-
|
|
38
|
-
action_subparsers =
|
|
39
|
-
for action in
|
|
40
|
-
action_parser = action_subparsers.add_parser(action, help=f"
|
|
41
|
-
_add_passthrough_options(action_parser)
|
|
42
|
-
_set_handler(action_parser, group, action)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def _add_reset_subparsers(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
46
|
-
reset_parser = subparsers.add_parser("reset", help="Explicit reset commands")
|
|
47
|
-
reset_subparsers = reset_parser.add_subparsers(dest="reset_target", required=True)
|
|
48
|
-
for target in ("feature", "bugfix", "refactor"):
|
|
49
|
-
target_parser = reset_subparsers.add_parser(target, help=f"Run {target} reset")
|
|
50
|
-
_add_passthrough_options(target_parser)
|
|
51
|
-
_set_handler(target_parser, "reset", "reset", target)
|
|
52
|
-
describe_parser = reset_subparsers.add_parser("describe", help="Describe reset command boundaries")
|
|
53
|
-
_set_handler(describe_parser, "reset", "describe")
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def _add_daemon_subparsers(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
57
|
-
daemon_parser = subparsers.add_parser("daemon", help="Daemon lifecycle commands")
|
|
58
|
-
target_subparsers = daemon_parser.add_subparsers(dest="daemon_target", required=True)
|
|
59
|
-
for target in ("feature", "bugfix", "refactor"):
|
|
60
|
-
target_parser = target_subparsers.add_parser(target, help=f"{target} daemon commands")
|
|
61
|
-
action_subparsers = target_parser.add_subparsers(dest="daemon_action", required=True)
|
|
62
|
-
for action in ("start", "stop", "status", "logs", "restart", "describe"):
|
|
63
|
-
action_parser = action_subparsers.add_parser(action, help=f"Run daemon {target} {action}")
|
|
64
|
-
_add_passthrough_options(action_parser)
|
|
65
|
-
_set_handler(action_parser, "daemon", action, target)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def _add_status_subparsers(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
69
|
-
status_parser = subparsers.add_parser("status", help="Status commands")
|
|
70
|
-
status_subparsers = status_parser.add_subparsers(dest="status_target", required=True)
|
|
71
|
-
for target in ("feature", "bugfix", "refactor", "all"):
|
|
72
|
-
target_parser = status_subparsers.add_parser(target, help=f"Show {target} status")
|
|
73
|
-
_add_passthrough_options(target_parser)
|
|
74
|
-
_set_handler(target_parser, "status", "status", target)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def _add_diagnostics_subparsers(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
|
|
78
|
-
diagnostics_parser = subparsers.add_parser("diagnostics", help="Python runtime diagnostics")
|
|
79
|
-
diagnostics_subparsers = diagnostics_parser.add_subparsers(dest="diagnostics_action", required=True)
|
|
80
|
-
for action in ("paths", "entrypoints", "version"):
|
|
81
|
-
action_parser = diagnostics_subparsers.add_parser(action, help=f"Show {action} diagnostics")
|
|
141
|
+
diagnostics_parser = subparsers.add_parser("diagnostics", help="Inspect Python runtime configuration")
|
|
142
|
+
action_subparsers = diagnostics_parser.add_subparsers(dest="diagnostics_action", required=True)
|
|
143
|
+
for action in ("paths", "entrypoints", "version", "ai-cli"):
|
|
144
|
+
action_parser = action_subparsers.add_parser(action, help=f"Show {action} diagnostics")
|
|
82
145
|
action_parser.set_defaults(handler="diagnostics", action=action)
|
|
83
146
|
|
|
84
147
|
|
|
85
|
-
def _add_helper_subparser(
|
|
86
|
-
|
|
148
|
+
def _add_helper_subparser(
|
|
149
|
+
subparsers: argparse._SubParsersAction[argparse.ArgumentParser],
|
|
150
|
+
) -> None:
|
|
151
|
+
helper_parser = subparsers.add_parser("helper", help="Run low-level cross-platform helper commands")
|
|
152
|
+
helper_parser.add_argument("helper_command", nargs="?", help="Helper command namespace.")
|
|
87
153
|
helper_parser.add_argument(
|
|
88
|
-
"
|
|
154
|
+
"helper_args",
|
|
89
155
|
nargs=argparse.REMAINDER,
|
|
90
156
|
help="Arguments forwarded to prizmkit-runtime-helper.",
|
|
91
157
|
)
|
|
92
|
-
helper_parser
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
_PIPELINE_GROUPS = {"feature", "bugfix", "refactor", "recovery"}
|
|
96
|
-
_GLOBAL_OPTIONS_WITH_VALUES = {"--project-root", "--pipeline-root"}
|
|
158
|
+
_set_handler(helper_parser, "helper", "run")
|
|
97
159
|
|
|
98
160
|
|
|
99
|
-
def
|
|
100
|
-
"""
|
|
161
|
+
def _parser_argv(argv: Sequence[str]) -> list[str]:
|
|
162
|
+
"""Normalize interspersed leaf operands for argparse validation only."""
|
|
101
163
|
tokens = [str(arg) for arg in argv]
|
|
102
|
-
prefix: list[str] = []
|
|
103
164
|
index = 0
|
|
104
165
|
while index < len(tokens):
|
|
105
166
|
token = tokens[index]
|
|
106
167
|
if token in _GLOBAL_OPTIONS_WITH_VALUES:
|
|
107
|
-
|
|
108
|
-
index += 1
|
|
109
|
-
if index < len(tokens):
|
|
110
|
-
prefix.append(tokens[index])
|
|
111
|
-
index += 1
|
|
168
|
+
index += 2
|
|
112
169
|
continue
|
|
113
170
|
if any(token.startswith(f"{option}=") for option in _GLOBAL_OPTIONS_WITH_VALUES):
|
|
114
|
-
prefix.append(token)
|
|
115
171
|
index += 1
|
|
116
172
|
continue
|
|
117
173
|
break
|
|
174
|
+
if index >= len(tokens):
|
|
175
|
+
return tokens
|
|
118
176
|
|
|
177
|
+
group = tokens[index]
|
|
178
|
+
depth = {"run": 2, "status": 2, "reset": 2, "daemon": 3}.get(group)
|
|
179
|
+
if depth is None or len(tokens) <= index + depth:
|
|
180
|
+
return tokens
|
|
181
|
+
|
|
182
|
+
command_end = index + depth
|
|
183
|
+
operands: list[str] = []
|
|
184
|
+
options: list[str] = []
|
|
185
|
+
tail = tokens[command_end:]
|
|
186
|
+
tail_index = 0
|
|
187
|
+
while tail_index < len(tail):
|
|
188
|
+
token = tail[tail_index]
|
|
189
|
+
option_name = token.split("=", 1)[0]
|
|
190
|
+
if token.startswith("-"):
|
|
191
|
+
options.append(token)
|
|
192
|
+
if (
|
|
193
|
+
"=" not in token
|
|
194
|
+
and option_name in _LEAF_OPTIONS_WITH_VALUES
|
|
195
|
+
and tail_index + 1 < len(tail)
|
|
196
|
+
):
|
|
197
|
+
tail_index += 1
|
|
198
|
+
options.append(tail[tail_index])
|
|
199
|
+
else:
|
|
200
|
+
operands.append(token)
|
|
201
|
+
tail_index += 1
|
|
202
|
+
return [*tokens[:command_end], *operands, *options]
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _runtime_action_args(argv: Sequence[str]) -> tuple[str, ...]:
|
|
206
|
+
"""Return the original validated leaf arguments in their exact order."""
|
|
207
|
+
tokens = [str(arg) for arg in argv]
|
|
208
|
+
index = 0
|
|
209
|
+
while index < len(tokens):
|
|
210
|
+
token = tokens[index]
|
|
211
|
+
if token in _GLOBAL_OPTIONS_WITH_VALUES:
|
|
212
|
+
index += 2
|
|
213
|
+
continue
|
|
214
|
+
if any(token.startswith(f"{option}=") for option in _GLOBAL_OPTIONS_WITH_VALUES):
|
|
215
|
+
index += 1
|
|
216
|
+
continue
|
|
217
|
+
break
|
|
119
218
|
if index >= len(tokens):
|
|
120
|
-
return
|
|
219
|
+
return ()
|
|
121
220
|
|
|
122
221
|
group = tokens[index]
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
passthrough = tuple(tokens[command_end:])
|
|
135
|
-
if group == "diagnostics":
|
|
136
|
-
return tokens, ()
|
|
137
|
-
return prefix, passthrough
|
|
222
|
+
depth = {
|
|
223
|
+
"run": 2,
|
|
224
|
+
"status": 2,
|
|
225
|
+
"reset": 2,
|
|
226
|
+
"daemon": 3,
|
|
227
|
+
"diagnostics": 2,
|
|
228
|
+
"helper": 1,
|
|
229
|
+
}.get(group)
|
|
230
|
+
if depth is None:
|
|
231
|
+
return ()
|
|
232
|
+
return tuple(tokens[index + depth :])
|
|
138
233
|
|
|
139
234
|
|
|
140
235
|
def build_parser() -> argparse.ArgumentParser:
|
|
141
|
-
"""Build the standard-library argparse command tree."""
|
|
236
|
+
"""Build the standard-library operation-first argparse command tree."""
|
|
142
237
|
parser = argparse.ArgumentParser(
|
|
143
238
|
prog="prizmkit-runtime",
|
|
144
239
|
description=DESCRIPTION,
|
|
@@ -154,13 +249,10 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
154
249
|
)
|
|
155
250
|
subparsers = parser.add_subparsers(dest="command_group", required=True)
|
|
156
251
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
_add_action_subparsers(subparsers, "refactor", ("run", "status", "reset", "unskip", "describe"))
|
|
160
|
-
_add_action_subparsers(subparsers, "recovery", ("run", "detect", "describe"))
|
|
252
|
+
_add_run_subparsers(subparsers)
|
|
253
|
+
_add_status_subparsers(subparsers)
|
|
161
254
|
_add_reset_subparsers(subparsers)
|
|
162
255
|
_add_daemon_subparsers(subparsers)
|
|
163
|
-
_add_status_subparsers(subparsers)
|
|
164
256
|
_add_diagnostics_subparsers(subparsers)
|
|
165
257
|
_add_helper_subparser(subparsers)
|
|
166
258
|
return parser
|
|
@@ -174,14 +266,6 @@ def _dispatch(args: argparse.Namespace) -> CommandResult:
|
|
|
174
266
|
paths = _runtime_paths_from_args(args)
|
|
175
267
|
if args.handler == "diagnostics":
|
|
176
268
|
return handle_diagnostics(args.action, paths)
|
|
177
|
-
if args.handler == "helper":
|
|
178
|
-
helper_result = run_helper_command(tuple(getattr(args, "args", ()) or ()))
|
|
179
|
-
return CommandResult(
|
|
180
|
-
helper_result.exit_code,
|
|
181
|
-
"Python runtime helper",
|
|
182
|
-
"Cross-platform prompt runtime helper result.",
|
|
183
|
-
stdout=render_result(helper_result, json_output="--json" in tuple(getattr(args, "args", ()) or ())),
|
|
184
|
-
)
|
|
185
269
|
return handle_runtime_command(
|
|
186
270
|
args.group,
|
|
187
271
|
args.action,
|
|
@@ -192,7 +276,7 @@ def _dispatch(args: argparse.Namespace) -> CommandResult:
|
|
|
192
276
|
|
|
193
277
|
|
|
194
278
|
def main(argv: Sequence[str] | None = None) -> int:
|
|
195
|
-
"""Run the
|
|
279
|
+
"""Run the canonical operation-first runtime CLI."""
|
|
196
280
|
try:
|
|
197
281
|
ensure_supported_python()
|
|
198
282
|
except UnsupportedPythonVersion as exc:
|
|
@@ -201,10 +285,8 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
201
285
|
|
|
202
286
|
parser = build_parser()
|
|
203
287
|
parse_argv = list(sys.argv[1:] if argv is None else argv)
|
|
204
|
-
|
|
205
|
-
args =
|
|
206
|
-
if passthrough_args:
|
|
207
|
-
args.args = tuple(getattr(args, "args", ()) or ()) + passthrough_args
|
|
288
|
+
args = parser.parse_args(_parser_argv(parse_argv))
|
|
289
|
+
args.args = _runtime_action_args(parse_argv)
|
|
208
290
|
result = _dispatch(args)
|
|
209
291
|
if result.stderr:
|
|
210
292
|
print(result.stderr, file=sys.stderr)
|