cowork-flow 0.0.48 → 0.0.49
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/README.md +16 -1
- package/package.json +6 -3
- package/src/cli.js +6 -0
- package/src/commands/source-refresh.js +40 -0
- package/src/lib/copy-template.js +130 -1
- package/src/lib/host-assets.js +216 -71
- package/template/.codex/hooks.json +1 -1
- package/template/.cowork-flow/.version +1 -1
- package/template/.cowork-flow/scripts/adapters/cli/task.py +0 -2
- package/template/.cowork-flow/scripts/adapters/cli/task_archive_commands.py +12 -0
- package/template/.cowork-flow/scripts/adapters/cli/task_lifecycle_commands.py +18 -133
- package/template/.cowork-flow/scripts/adapters/cli/task_navigation.py +82 -1
- package/template/.cowork-flow/scripts/adapters/host/host_manifest.py +98 -114
- package/template/.cowork-flow/scripts/infra/skill_manifest.py +23 -3
- package/template/.cowork-flow/scripts/infra/storage/operation_log.py +40 -0
- package/template/.cowork-flow/scripts/infra/storage/state_store.py +356 -1
- package/template/.cowork-flow/scripts/services/context_discovery.py +156 -0
- package/template/.cowork-flow/scripts/services/context_jsonl.py +119 -0
- package/template/.cowork-flow/scripts/services/context_paths.py +186 -0
- package/template/.cowork-flow/scripts/services/lifecycle_checks.py +23 -4
- package/template/.cowork-flow/scripts/services/lifecycle_policy.py +128 -0
- package/template/.cowork-flow/scripts/services/plan_binding.py +146 -0
- package/template/.cowork-flow/scripts/services/task_archive.py +100 -14
- package/template/.cowork-flow/scripts/services/task_context.py +49 -479
- package/template/.cowork-flow/scripts/services/task_creation.py +31 -27
- package/template/.cowork-flow/scripts/services/task_lifecycle.py +155 -18
- package/template/.cowork-flow/spec/contracts/error-output-as-data.md +8 -0
- package/template/.cowork-flow/spec/runtime/index.md +1 -1
- package/template/.cowork-flow/spec/schemas/host-assets.schema.json +32 -6
- package/template/.zcode/.zcode-plugin/plugin.json +1 -1
- package/template/skills/batch-execution/scripts/batch_execution.py +111 -629
- package/template/skills/batch-execution/scripts/batch_recovery.py +94 -0
- package/template/skills/batch-execution/scripts/batch_state_machine.py +201 -0
- package/template/skills/batch-execution/scripts/batch_verification.py +326 -0
- package/template/skills/cowork-flow/SKILL.md +3 -0
- package/template/skills/party-mode/scripts/party_actions.py +95 -0
- package/template/skills/party-mode/scripts/party_mode_v2.py +129 -205
- package/template/skills/party-mode/scripts/party_reports.py +177 -0
- package/template/skills/party-mode/scripts/party_state_machine.py +189 -0
- package/template/skills/runtime-health/scripts/doctor.py +204 -15
- package/template/skills/task-review/scripts/review_check.py +135 -2
- package/template/skills/test-first/SKILL.md +18 -3
- package/template/skills/test-first/manifest.json +8 -4
|
@@ -38,8 +38,6 @@ from adapters.cli.task_context_commands import (
|
|
|
38
38
|
from adapters.cli.task_create_command import cmd_create, ensure_tasks_dir
|
|
39
39
|
from adapters.cli import task_navigation
|
|
40
40
|
from adapters.cli.task_lifecycle_commands import (
|
|
41
|
-
_optional_readiness_blockers,
|
|
42
|
-
_task_start_blockers,
|
|
43
41
|
cmd_complete,
|
|
44
42
|
cmd_current,
|
|
45
43
|
cmd_finish,
|
|
@@ -94,6 +94,17 @@ def _archive_error_message(task_name: str, error: TaskArchiveError) -> str:
|
|
|
94
94
|
return f"Failed to archive task: {error.detail}"
|
|
95
95
|
|
|
96
96
|
|
|
97
|
+
def _print_rollback_issues(error: TaskArchiveError) -> None:
|
|
98
|
+
if not error.rollback_issues:
|
|
99
|
+
return
|
|
100
|
+
print("Rollback issues:", file=sys.stderr)
|
|
101
|
+
for issue in error.rollback_issues:
|
|
102
|
+
print(
|
|
103
|
+
f"- {issue.stage}: {issue.path} ? {issue.detail}",
|
|
104
|
+
file=sys.stderr,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
97
108
|
def _archive_task(repo_root, task_name: str, task_dir):
|
|
98
109
|
try:
|
|
99
110
|
return TaskArchiveService(repo_root).archive(
|
|
@@ -103,6 +114,7 @@ def _archive_task(repo_root, task_name: str, task_dir):
|
|
|
103
114
|
except TaskArchiveError as error:
|
|
104
115
|
message = _archive_error_message(task_name, error)
|
|
105
116
|
print(colored(f"Error: {message}", Colors.RED), file=sys.stderr)
|
|
117
|
+
_print_rollback_issues(error)
|
|
106
118
|
return None
|
|
107
119
|
|
|
108
120
|
|
|
@@ -6,7 +6,6 @@ from __future__ import annotations
|
|
|
6
6
|
|
|
7
7
|
import argparse
|
|
8
8
|
import sys
|
|
9
|
-
from datetime import datetime
|
|
10
9
|
from pathlib import Path
|
|
11
10
|
|
|
12
11
|
if __package__:
|
|
@@ -14,13 +13,7 @@ if __package__:
|
|
|
14
13
|
else:
|
|
15
14
|
import _bootstrap # noqa: F401
|
|
16
15
|
|
|
17
|
-
from
|
|
18
|
-
from services.task_context import TaskContextService
|
|
19
|
-
from services.task_lifecycle import (
|
|
20
|
-
LifecyclePreflightFailure,
|
|
21
|
-
LifecycleResult,
|
|
22
|
-
TaskLifecycleService,
|
|
23
|
-
)
|
|
16
|
+
from services.task_lifecycle import LifecycleResult, TaskLifecycleService
|
|
24
17
|
from adapters.cli.task_support import (
|
|
25
18
|
Colors,
|
|
26
19
|
colored,
|
|
@@ -33,13 +26,7 @@ from infra.paths import (
|
|
|
33
26
|
FILE_TASK_JSON,
|
|
34
27
|
get_repo_root,
|
|
35
28
|
)
|
|
36
|
-
from runtime.session_state import clear_active_task, get_active_task
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def _allow_spec_file_modifications(repo_root: Path, execution_context) -> bool:
|
|
40
|
-
if execution_context.is_worker or execution_context.is_subagent:
|
|
41
|
-
return False
|
|
42
|
-
return is_main_session(repo_root)
|
|
29
|
+
from runtime.session_state import clear_active_task, get_active_task
|
|
43
30
|
|
|
44
31
|
|
|
45
32
|
def _report_check_block(title: str, blockers: tuple[str, ...]) -> int:
|
|
@@ -116,102 +103,8 @@ def _resolve_status_task_dir(
|
|
|
116
103
|
return task_dir
|
|
117
104
|
|
|
118
105
|
|
|
119
|
-
def _task_start_blockers(task_dir: Path) -> list[str]:
|
|
120
|
-
return list(
|
|
121
|
-
TaskContextService(get_repo_root(task_dir)).start_blockers(task_dir)
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
def _task_context_validation_issues(
|
|
126
|
-
task_dir: Path,
|
|
127
|
-
repo_root: Path,
|
|
128
|
-
quiet: bool = False,
|
|
129
|
-
) -> list[str]:
|
|
130
|
-
del quiet
|
|
131
|
-
return list(
|
|
132
|
-
TaskContextService(repo_root).validation_issue_summaries(task_dir)
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def _refresh_task_artifact_placeholders(
|
|
137
|
-
task_dir: Path,
|
|
138
|
-
repo_root: Path,
|
|
139
|
-
) -> list[str]:
|
|
140
|
-
service = TaskContextService(repo_root)
|
|
141
|
-
service.ensure_task_artifact_placeholders(task_dir)
|
|
142
|
-
return _task_context_validation_issues(task_dir, repo_root)
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
def _optional_readiness_blockers(
|
|
146
|
-
repo_root: Path,
|
|
147
|
-
task_dir: Path,
|
|
148
|
-
) -> list[str]:
|
|
149
|
-
try:
|
|
150
|
-
from services.readiness import task_readiness_blockers
|
|
151
|
-
except Exception:
|
|
152
|
-
return []
|
|
153
|
-
try:
|
|
154
|
-
blockers = task_readiness_blockers(repo_root, task_dir)
|
|
155
|
-
except Exception:
|
|
156
|
-
return [
|
|
157
|
-
"readiness check failed; run task next <dir> --validate and inspect "
|
|
158
|
-
"task readiness artifacts"
|
|
159
|
-
]
|
|
160
|
-
return [str(blocker) for blocker in blockers if str(blocker).strip()]
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
def _start_preflight(
|
|
164
|
-
task_dir: Path,
|
|
165
|
-
repo_root: Path,
|
|
166
|
-
) -> LifecyclePreflightFailure | None:
|
|
167
|
-
blockers = _task_start_blockers(task_dir)
|
|
168
|
-
if blockers:
|
|
169
|
-
return LifecyclePreflightFailure(
|
|
170
|
-
code="TASK-START-001",
|
|
171
|
-
title="Task is not ready to start yet",
|
|
172
|
-
blockers=tuple(blockers),
|
|
173
|
-
hint=(
|
|
174
|
-
"write decision-anchor.md and required context JSONL files, "
|
|
175
|
-
"then retry `task next <dir> --run`"
|
|
176
|
-
),
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
validation_issues = _task_context_validation_issues(
|
|
180
|
-
task_dir,
|
|
181
|
-
repo_root,
|
|
182
|
-
)
|
|
183
|
-
if validation_issues:
|
|
184
|
-
validation_issues = _refresh_task_artifact_placeholders(task_dir, repo_root)
|
|
185
|
-
if validation_issues:
|
|
186
|
-
return LifecyclePreflightFailure(
|
|
187
|
-
code="TASK-CONTEXT-001",
|
|
188
|
-
title="Task context validation failed",
|
|
189
|
-
blockers=tuple(validation_issues),
|
|
190
|
-
hint=(
|
|
191
|
-
"run ./.cowork-flow/run task next <dir> --validate and fix the "
|
|
192
|
-
f"reported issues. {PLANNED_FILE_HINT}"
|
|
193
|
-
),
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
readiness_blockers = _optional_readiness_blockers(
|
|
197
|
-
repo_root,
|
|
198
|
-
task_dir,
|
|
199
|
-
)
|
|
200
|
-
if readiness_blockers:
|
|
201
|
-
return LifecyclePreflightFailure(
|
|
202
|
-
code="TASK-READINESS-001",
|
|
203
|
-
title="Task readiness failed",
|
|
204
|
-
blockers=tuple(readiness_blockers),
|
|
205
|
-
hint=(
|
|
206
|
-
"run ./.cowork-flow/run task next <dir> and complete the "
|
|
207
|
-
"reported readiness artifacts"
|
|
208
|
-
),
|
|
209
|
-
)
|
|
210
|
-
return None
|
|
211
|
-
|
|
212
|
-
|
|
213
106
|
def _report_lifecycle_preflight(
|
|
214
|
-
result:
|
|
107
|
+
result: LifecycleResult,
|
|
215
108
|
) -> int:
|
|
216
109
|
print(colored(f"Error: {result.title}", Colors.RED), file=sys.stderr)
|
|
217
110
|
for blocker in result.blockers:
|
|
@@ -264,11 +157,11 @@ def _resolve_start_task(
|
|
|
264
157
|
def _run_auto_start(
|
|
265
158
|
args: argparse.Namespace,
|
|
266
159
|
full_path: Path,
|
|
267
|
-
|
|
160
|
+
service: TaskLifecycleService,
|
|
268
161
|
) -> int:
|
|
269
|
-
|
|
270
|
-
if
|
|
271
|
-
return _report_lifecycle_preflight(
|
|
162
|
+
readiness = service.start_readiness(full_path)
|
|
163
|
+
if not readiness.ok:
|
|
164
|
+
return _report_lifecycle_preflight(readiness)
|
|
272
165
|
from adapters.cli.batch_mode import run_batch_entry
|
|
273
166
|
|
|
274
167
|
return run_batch_entry(get_repo_root(), full_path, args)
|
|
@@ -276,9 +169,7 @@ def _run_auto_start(
|
|
|
276
169
|
|
|
277
170
|
def _report_start_failure(
|
|
278
171
|
result: LifecycleResult,
|
|
279
|
-
service: TaskLifecycleService,
|
|
280
172
|
) -> int:
|
|
281
|
-
del service
|
|
282
173
|
if result.title:
|
|
283
174
|
return _report_lifecycle_preflight(result)
|
|
284
175
|
if result.code == "LIFECYCLE-TRANSITION-001":
|
|
@@ -354,21 +245,17 @@ def cmd_start(args: argparse.Namespace) -> int:
|
|
|
354
245
|
if full_path is None:
|
|
355
246
|
return 1
|
|
356
247
|
|
|
357
|
-
|
|
358
|
-
task_dir: Path,
|
|
359
|
-
) -> LifecyclePreflightFailure | None:
|
|
360
|
-
return _start_preflight(task_dir, repo_root)
|
|
361
|
-
|
|
248
|
+
service = TaskLifecycleService(repo_root)
|
|
362
249
|
if getattr(args, "auto", False):
|
|
363
|
-
return _run_auto_start(args, full_path,
|
|
250
|
+
return _run_auto_start(args, full_path, service)
|
|
364
251
|
|
|
365
|
-
|
|
366
|
-
result = service.start(full_path, preflight=preflight)
|
|
252
|
+
result = service.start(full_path)
|
|
367
253
|
if not result.ok:
|
|
368
|
-
return _report_start_failure(result
|
|
254
|
+
return _report_start_failure(result)
|
|
369
255
|
|
|
370
256
|
_report_start_success(result, repo_root, full_path)
|
|
371
|
-
|
|
257
|
+
if "after_start" in getattr(result, "emitted_events", ()):
|
|
258
|
+
_run_hooks("after_start", full_path / FILE_TASK_JSON, repo_root)
|
|
372
259
|
return 0
|
|
373
260
|
|
|
374
261
|
|
|
@@ -383,10 +270,7 @@ def cmd_review(args: argparse.Namespace) -> int:
|
|
|
383
270
|
service = TaskLifecycleService(repo_root)
|
|
384
271
|
result = service.review(
|
|
385
272
|
task_dir,
|
|
386
|
-
|
|
387
|
-
repo_root,
|
|
388
|
-
execution_context,
|
|
389
|
-
),
|
|
273
|
+
execution_context=execution_context,
|
|
390
274
|
)
|
|
391
275
|
if not result.ok:
|
|
392
276
|
if result.code == "LIFECYCLE-TRANSITION-001":
|
|
@@ -415,6 +299,7 @@ def cmd_review(args: argparse.Namespace) -> int:
|
|
|
415
299
|
def cmd_complete(args: argparse.Namespace) -> int:
|
|
416
300
|
"""Mark a task completed after final check."""
|
|
417
301
|
repo_root = get_repo_root()
|
|
302
|
+
execution_context = execution_context_from_namespace(args)
|
|
418
303
|
task_dir = _resolve_status_task_dir(args, repo_root)
|
|
419
304
|
if task_dir is None:
|
|
420
305
|
return 1
|
|
@@ -422,8 +307,7 @@ def cmd_complete(args: argparse.Namespace) -> int:
|
|
|
422
307
|
service = TaskLifecycleService(repo_root)
|
|
423
308
|
result = service.complete(
|
|
424
309
|
task_dir,
|
|
425
|
-
|
|
426
|
-
allow_spec_file_modifications=is_main_session(repo_root),
|
|
310
|
+
execution_context=execution_context,
|
|
427
311
|
)
|
|
428
312
|
if not result.ok:
|
|
429
313
|
if result.code == "LIFECYCLE-TRANSITION-001":
|
|
@@ -475,7 +359,8 @@ def cmd_finish(args: argparse.Namespace) -> int:
|
|
|
475
359
|
Colors.GREEN,
|
|
476
360
|
)
|
|
477
361
|
)
|
|
478
|
-
if task_json_path.is_file()
|
|
362
|
+
emitted_events = ("after_finish",) if task_json_path.is_file() else ()
|
|
363
|
+
if "after_finish" in emitted_events:
|
|
479
364
|
_run_hooks("after_finish", task_json_path, repo_root)
|
|
480
365
|
return 0
|
|
481
366
|
|
|
@@ -165,6 +165,76 @@ def _attach_recovery(payload: dict[str, object], repo_root: Path) -> dict[str, o
|
|
|
165
165
|
return payload
|
|
166
166
|
|
|
167
167
|
|
|
168
|
+
def _add_read_first_entry(
|
|
169
|
+
entries: list[dict[str, str]],
|
|
170
|
+
seen: set[str],
|
|
171
|
+
file_path: str,
|
|
172
|
+
reason: str,
|
|
173
|
+
) -> None:
|
|
174
|
+
normalized = file_path.replace("\\", "/").strip()
|
|
175
|
+
if not normalized or normalized in seen:
|
|
176
|
+
return
|
|
177
|
+
seen.add(normalized)
|
|
178
|
+
entry = {"file": normalized}
|
|
179
|
+
if reason.strip():
|
|
180
|
+
entry["reason"] = reason.strip()
|
|
181
|
+
entries.append(entry)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _attach_implement_read_first(
|
|
185
|
+
payload: dict[str, object],
|
|
186
|
+
repo_root: Path,
|
|
187
|
+
task_path: str | None,
|
|
188
|
+
) -> dict[str, object]:
|
|
189
|
+
action = payload.get("action")
|
|
190
|
+
if not isinstance(action, dict) or action.get("id") != "implement_change":
|
|
191
|
+
return payload
|
|
192
|
+
if not task_path:
|
|
193
|
+
return payload
|
|
194
|
+
|
|
195
|
+
task_dir = repo_root / task_path
|
|
196
|
+
if not task_dir.is_dir():
|
|
197
|
+
return payload
|
|
198
|
+
|
|
199
|
+
entries: list[dict[str, str]] = []
|
|
200
|
+
seen: set[str] = set()
|
|
201
|
+
task_prefix = task_path.rstrip("/")
|
|
202
|
+
_add_read_first_entry(
|
|
203
|
+
entries,
|
|
204
|
+
seen,
|
|
205
|
+
f"{task_prefix}/decision-anchor.md",
|
|
206
|
+
"Task decision anchor",
|
|
207
|
+
)
|
|
208
|
+
try:
|
|
209
|
+
task_data = TaskRepository(repo_root).load(task_dir)
|
|
210
|
+
except TaskRepositoryError:
|
|
211
|
+
task_data = {}
|
|
212
|
+
meta = task_data.get("meta") if isinstance(task_data, dict) else None
|
|
213
|
+
plan_file = meta.get("planFile") if isinstance(meta, dict) else None
|
|
214
|
+
if isinstance(plan_file, str):
|
|
215
|
+
_add_read_first_entry(entries, seen, plan_file, "Linked implementation plan")
|
|
216
|
+
_add_read_first_entry(
|
|
217
|
+
entries,
|
|
218
|
+
seen,
|
|
219
|
+
f"{task_prefix}/implement.jsonl",
|
|
220
|
+
"Implementation context index",
|
|
221
|
+
)
|
|
222
|
+
for context_entry in TaskContextService(repo_root).entries(task_dir, "implement"):
|
|
223
|
+
file_value = context_entry.get("file")
|
|
224
|
+
if not isinstance(file_value, str):
|
|
225
|
+
continue
|
|
226
|
+
reason = context_entry.get("reason")
|
|
227
|
+
_add_read_first_entry(
|
|
228
|
+
entries,
|
|
229
|
+
seen,
|
|
230
|
+
file_value,
|
|
231
|
+
reason if isinstance(reason, str) else "Implementation context",
|
|
232
|
+
)
|
|
233
|
+
if entries:
|
|
234
|
+
payload["readFirst"] = entries
|
|
235
|
+
return payload
|
|
236
|
+
|
|
237
|
+
|
|
168
238
|
def _print_recovery(payload: dict[str, object]) -> None:
|
|
169
239
|
recovery = payload.get("recovery")
|
|
170
240
|
if not isinstance(recovery, dict):
|
|
@@ -206,20 +276,24 @@ def build_navigation_payload(
|
|
|
206
276
|
blockers: list[str],
|
|
207
277
|
active_target: bool,
|
|
208
278
|
task_path: str | None = None,
|
|
279
|
+
repo_root: Path | None = None,
|
|
209
280
|
) -> dict[str, object]:
|
|
210
281
|
intent = getattr(args, "intent", None) or _default_intent(
|
|
211
282
|
status,
|
|
212
283
|
blockers,
|
|
213
284
|
active_target,
|
|
214
285
|
)
|
|
215
|
-
|
|
286
|
+
root = repo_root if repo_root is not None else get_repo_root()
|
|
287
|
+
payload = route_request(
|
|
216
288
|
status=status,
|
|
217
289
|
intent=intent,
|
|
218
290
|
context=_routing_context(args),
|
|
219
291
|
blockers=blockers,
|
|
220
292
|
active_target=active_target,
|
|
221
293
|
task_path=task_path,
|
|
294
|
+
repo_root=root,
|
|
222
295
|
)
|
|
296
|
+
return _attach_implement_read_first(payload, root, task_path)
|
|
223
297
|
|
|
224
298
|
|
|
225
299
|
def _print_json_route(
|
|
@@ -229,6 +303,7 @@ def _print_json_route(
|
|
|
229
303
|
blockers: list[str],
|
|
230
304
|
active_target: bool,
|
|
231
305
|
task_path: str | None = None,
|
|
306
|
+
repo_root: Path | None = None,
|
|
232
307
|
) -> None:
|
|
233
308
|
payload = build_navigation_payload(
|
|
234
309
|
args=args,
|
|
@@ -236,6 +311,7 @@ def _print_json_route(
|
|
|
236
311
|
blockers=blockers,
|
|
237
312
|
active_target=active_target,
|
|
238
313
|
task_path=task_path,
|
|
314
|
+
repo_root=repo_root,
|
|
239
315
|
)
|
|
240
316
|
print(json.dumps(payload, ensure_ascii=False, sort_keys=False))
|
|
241
317
|
|
|
@@ -274,6 +350,7 @@ def _navigation_target(args, repo_root: Path, structured: bool):
|
|
|
274
350
|
blockers=[],
|
|
275
351
|
active_target=False,
|
|
276
352
|
task_path=None,
|
|
353
|
+
repo_root=repo_root,
|
|
277
354
|
)
|
|
278
355
|
_attach_recovery(payload, repo_root)
|
|
279
356
|
print(json.dumps(payload, ensure_ascii=False, sort_keys=False))
|
|
@@ -286,6 +363,7 @@ def _navigation_target(args, repo_root: Path, structured: bool):
|
|
|
286
363
|
task_path=None,
|
|
287
364
|
blockers=[],
|
|
288
365
|
active_target=False,
|
|
366
|
+
repo_root=repo_root,
|
|
289
367
|
)
|
|
290
368
|
_attach_recovery(payload, repo_root)
|
|
291
369
|
_print_text_payload(payload)
|
|
@@ -302,6 +380,7 @@ def _print_stale_route(args, task_path: str, source: str, active_target: bool) -
|
|
|
302
380
|
blockers=blockers,
|
|
303
381
|
active_target=active_target,
|
|
304
382
|
task_path=task_path,
|
|
383
|
+
repo_root=get_repo_root(),
|
|
305
384
|
)
|
|
306
385
|
return
|
|
307
386
|
print("Status: stale")
|
|
@@ -332,6 +411,7 @@ def _print_text_route(
|
|
|
332
411
|
task_path=task_path,
|
|
333
412
|
blockers=blockers,
|
|
334
413
|
active_target=active_target,
|
|
414
|
+
repo_root=get_repo_root(),
|
|
335
415
|
)
|
|
336
416
|
_print_text_payload(payload)
|
|
337
417
|
|
|
@@ -361,6 +441,7 @@ def cmd_next(args) -> int:
|
|
|
361
441
|
blockers=blockers,
|
|
362
442
|
active_target=active_target,
|
|
363
443
|
task_path=task_path,
|
|
444
|
+
repo_root=repo_root,
|
|
364
445
|
)
|
|
365
446
|
return 0
|
|
366
447
|
|