@xenonbyte/req-2-plan 0.5.1 → 0.5.2
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/package.json
CHANGED
|
@@ -49,9 +49,13 @@ from tools.workflow_cli.gates import (
|
|
|
49
49
|
check_execution_complete,
|
|
50
50
|
)
|
|
51
51
|
from tools.workflow_cli.output import (
|
|
52
|
+
COMPACT_DETAIL_LIMIT,
|
|
53
|
+
COMPACT_FILE_LIST_LIMIT,
|
|
54
|
+
compact_human_list,
|
|
52
55
|
format_success,
|
|
53
56
|
format_error,
|
|
54
57
|
format_gate_result,
|
|
58
|
+
is_json_mode,
|
|
55
59
|
print_and_exit,
|
|
56
60
|
EXIT_OK,
|
|
57
61
|
EXIT_CLI_ERR,
|
|
@@ -95,6 +99,53 @@ def _load_run(work_id: str, base_path: Path | None = None):
|
|
|
95
99
|
)
|
|
96
100
|
|
|
97
101
|
|
|
102
|
+
def _write_recovery_list(run_dir: Path, work_id: str, filename: str, items: list[str]) -> str | None:
|
|
103
|
+
logs_dir = run_dir / "logs"
|
|
104
|
+
if logs_dir.is_symlink():
|
|
105
|
+
return None
|
|
106
|
+
try:
|
|
107
|
+
logs_dir.mkdir(parents=True, exist_ok=True)
|
|
108
|
+
recovery_path = logs_dir / filename
|
|
109
|
+
atomic_write_text(recovery_path, "\n".join(items) + "\n")
|
|
110
|
+
except OSError:
|
|
111
|
+
return None
|
|
112
|
+
return f".req-to-plan/{work_id}/logs/{filename}"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _human_list_payload(
|
|
116
|
+
*,
|
|
117
|
+
run_dir: Path,
|
|
118
|
+
work_id: str,
|
|
119
|
+
label: str,
|
|
120
|
+
items: list,
|
|
121
|
+
limit: int,
|
|
122
|
+
recovery_filename: str,
|
|
123
|
+
recovery_items: list[str] | None = None,
|
|
124
|
+
) -> dict:
|
|
125
|
+
if is_json_mode() or len(items) <= limit:
|
|
126
|
+
return {label: items}
|
|
127
|
+
|
|
128
|
+
recovery_path = _write_recovery_list(
|
|
129
|
+
run_dir,
|
|
130
|
+
work_id,
|
|
131
|
+
recovery_filename,
|
|
132
|
+
recovery_items if recovery_items is not None else [str(item) for item in items],
|
|
133
|
+
)
|
|
134
|
+
if recovery_path is None:
|
|
135
|
+
return {label: items}
|
|
136
|
+
|
|
137
|
+
payload = compact_human_list(
|
|
138
|
+
label=label,
|
|
139
|
+
items=items,
|
|
140
|
+
limit=limit,
|
|
141
|
+
recovery_path=recovery_path,
|
|
142
|
+
)
|
|
143
|
+
payload[f"{label}_summary"] = (
|
|
144
|
+
f"{payload[f'{label}_shown']} shown, {payload[f'{label}_total']} total"
|
|
145
|
+
)
|
|
146
|
+
return payload
|
|
147
|
+
|
|
148
|
+
|
|
98
149
|
def _validate_work_id(raw: str) -> WorkId:
|
|
99
150
|
"""Parse WorkId or exit with CLI error."""
|
|
100
151
|
try:
|
|
@@ -334,6 +385,15 @@ def _cmd_run_start(args):
|
|
|
334
385
|
def _cmd_run_resume(args):
|
|
335
386
|
record, mgr, run_dir = _load_run(args.work_id, args.base_path)
|
|
336
387
|
rc = record.resume_context
|
|
388
|
+
reread_targets = list(rc.required_reread_targets)
|
|
389
|
+
reread_payload = _human_list_payload(
|
|
390
|
+
run_dir=run_dir,
|
|
391
|
+
work_id=str(record.work_id),
|
|
392
|
+
label="required_reread_targets",
|
|
393
|
+
items=reread_targets,
|
|
394
|
+
limit=COMPACT_FILE_LIST_LIMIT,
|
|
395
|
+
recovery_filename="run-resume-reread-targets.txt",
|
|
396
|
+
)
|
|
337
397
|
print_and_exit(
|
|
338
398
|
format_success(
|
|
339
399
|
{
|
|
@@ -344,6 +404,7 @@ def _cmd_run_resume(args):
|
|
|
344
404
|
"next_operation": rc.next_allowed_operation,
|
|
345
405
|
"active_item": rc.active_item,
|
|
346
406
|
"resume_reason": rc.resume_reason,
|
|
407
|
+
**reread_payload,
|
|
347
408
|
},
|
|
348
409
|
message="Resume context",
|
|
349
410
|
),
|
|
@@ -1223,6 +1284,22 @@ def _cmd_status_run(args):
|
|
|
1223
1284
|
for s in record.stale_artifacts
|
|
1224
1285
|
]
|
|
1225
1286
|
outstanding_stale = [aa.stage.value for aa in record.active_artifacts if aa.status == "stale"]
|
|
1287
|
+
approved_checkpoints = [cp.stage.value for cp in record.approved_checkpoints]
|
|
1288
|
+
approved_payload = _human_list_payload(
|
|
1289
|
+
run_dir=run_dir,
|
|
1290
|
+
work_id=str(record.work_id),
|
|
1291
|
+
label="approved_checkpoints",
|
|
1292
|
+
items=approved_checkpoints,
|
|
1293
|
+
limit=COMPACT_DETAIL_LIMIT,
|
|
1294
|
+
recovery_filename="status-run-approved-checkpoints.txt",
|
|
1295
|
+
recovery_items=[
|
|
1296
|
+
(
|
|
1297
|
+
f"{cp.stage.value}\t{cp.artifact}\tv{cp.version}\t"
|
|
1298
|
+
f"{cp.approved_at}\t{cp.downstream_authorization}\t{cp.bundle_id or ''}"
|
|
1299
|
+
)
|
|
1300
|
+
for cp in record.approved_checkpoints
|
|
1301
|
+
],
|
|
1302
|
+
)
|
|
1226
1303
|
|
|
1227
1304
|
print_and_exit(
|
|
1228
1305
|
format_success(
|
|
@@ -1237,7 +1314,7 @@ def _cmd_status_run(args):
|
|
|
1237
1314
|
"open_routes_detail": open_routes_detail,
|
|
1238
1315
|
"stale_artifacts": stale_artifacts,
|
|
1239
1316
|
"outstanding_stale": outstanding_stale,
|
|
1240
|
-
|
|
1317
|
+
**approved_payload,
|
|
1241
1318
|
},
|
|
1242
1319
|
message="Run status",
|
|
1243
1320
|
),
|
|
@@ -11,12 +11,38 @@ EXIT_REVIEW_REQ = 5 # forced subagent review required
|
|
|
11
11
|
EXIT_CONFLICT = 6 # state conflict (run already closed, etc.)
|
|
12
12
|
EXIT_NOT_FOUND = 7 # resource not found (run.md, artifact, etc.)
|
|
13
13
|
|
|
14
|
+
# Opt-in compact display limits. Default formatters remain uncapped.
|
|
15
|
+
COMPACT_DETAIL_LIMIT = 10
|
|
16
|
+
COMPACT_FILE_LIST_LIMIT = 15
|
|
17
|
+
|
|
14
18
|
|
|
15
19
|
def is_json_mode() -> bool:
|
|
16
20
|
"""Check if JSON output mode is enabled via R2P_JSON environment variable."""
|
|
17
21
|
return os.environ.get("R2P_JSON", "0") == "1"
|
|
18
22
|
|
|
19
23
|
|
|
24
|
+
def compact_human_list(
|
|
25
|
+
*,
|
|
26
|
+
label: str,
|
|
27
|
+
items: list,
|
|
28
|
+
limit: int,
|
|
29
|
+
recovery_path: str | None = None,
|
|
30
|
+
) -> dict:
|
|
31
|
+
"""Build an opt-in compact list payload without touching the filesystem."""
|
|
32
|
+
if limit < 0:
|
|
33
|
+
raise ValueError("limit must be non-negative")
|
|
34
|
+
|
|
35
|
+
visible_items = list(items[:limit])
|
|
36
|
+
result = {
|
|
37
|
+
label: visible_items,
|
|
38
|
+
f"{label}_shown": len(visible_items),
|
|
39
|
+
f"{label}_total": len(items),
|
|
40
|
+
}
|
|
41
|
+
if recovery_path:
|
|
42
|
+
result[f"{label}_full_list"] = recovery_path
|
|
43
|
+
return result
|
|
44
|
+
|
|
45
|
+
|
|
20
46
|
def format_success(data: dict, message: str = "") -> str:
|
|
21
47
|
"""Format a success response."""
|
|
22
48
|
if is_json_mode():
|
|
@@ -1 +1 @@
|
|
|
1
|
-
R2P_VERSION = "0.5.
|
|
1
|
+
R2P_VERSION = "0.5.2"
|
|
@@ -14,7 +14,7 @@ from pathlib import Path
|
|
|
14
14
|
|
|
15
15
|
from tools.workflow_cli.atomic import atomic_write_text
|
|
16
16
|
|
|
17
|
-
_WORKSPACE_GITIGNORE_LINES = ("/archive", "/.workflow-active")
|
|
17
|
+
_WORKSPACE_GITIGNORE_LINES = ("/archive", "/.workflow-active", "/*/logs/")
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def ensure_workspace_gitignore(base_path: Path) -> None:
|