@blxzer/cursor-trellis 0.3.3 → 0.3.5
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/CHANGELOG.md +6 -0
- package/README.md +0 -13
- package/README.zh-CN.md +2 -1
- package/bin/cstl.js +1 -1
- package/bin/smart-search.js +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +14 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/campaign/canvas-render.d.ts +68 -0
- package/dist/commands/campaign/canvas-render.d.ts.map +1 -0
- package/dist/commands/campaign/canvas-render.js +414 -0
- package/dist/commands/campaign/canvas-render.js.map +1 -0
- package/dist/commands/campaign/compose.d.ts +15 -0
- package/dist/commands/campaign/compose.d.ts.map +1 -0
- package/dist/commands/campaign/compose.js +110 -0
- package/dist/commands/campaign/compose.js.map +1 -0
- package/dist/commands/campaign/index.d.ts +13 -0
- package/dist/commands/campaign/index.d.ts.map +1 -0
- package/dist/commands/campaign/index.js +134 -0
- package/dist/commands/campaign/index.js.map +1 -0
- package/dist/commands/campaign/kind-map.d.ts +6 -0
- package/dist/commands/campaign/kind-map.d.ts.map +1 -0
- package/dist/commands/campaign/kind-map.js +21 -0
- package/dist/commands/campaign/kind-map.js.map +1 -0
- package/dist/commands/campaign/mcp-server.d.ts +6 -0
- package/dist/commands/campaign/mcp-server.d.ts.map +1 -0
- package/dist/commands/campaign/mcp-server.js +167 -0
- package/dist/commands/campaign/mcp-server.js.map +1 -0
- package/dist/commands/campaign/render.d.ts +5 -0
- package/dist/commands/campaign/render.d.ts.map +1 -0
- package/dist/commands/campaign/render.js +103 -0
- package/dist/commands/campaign/render.js.map +1 -0
- package/dist/commands/campaign/trellis-load.d.ts +13 -0
- package/dist/commands/campaign/trellis-load.d.ts.map +1 -0
- package/dist/commands/campaign/trellis-load.js +122 -0
- package/dist/commands/campaign/trellis-load.js.map +1 -0
- package/dist/commands/campaign/types.d.ts +61 -0
- package/dist/commands/campaign/types.d.ts.map +1 -0
- package/dist/commands/campaign/types.js +3 -0
- package/dist/commands/campaign/types.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +11 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/rpc/client.d.ts +51 -0
- package/dist/commands/rpc/client.d.ts.map +1 -0
- package/dist/commands/rpc/client.js +147 -0
- package/dist/commands/rpc/client.js.map +1 -0
- package/dist/commands/rpc/index.d.ts +10 -0
- package/dist/commands/rpc/index.d.ts.map +1 -0
- package/dist/commands/rpc/index.js +157 -0
- package/dist/commands/rpc/index.js.map +1 -0
- package/dist/commands/rpc/serve.d.ts +23 -0
- package/dist/commands/rpc/serve.d.ts.map +1 -0
- package/dist/commands/rpc/serve.js +60 -0
- package/dist/commands/rpc/serve.js.map +1 -0
- package/dist/commands/rpc/smoke.d.ts +16 -0
- package/dist/commands/rpc/smoke.d.ts.map +1 -0
- package/dist/commands/rpc/smoke.js +92 -0
- package/dist/commands/rpc/smoke.js.map +1 -0
- package/dist/commands/sdk/index.d.ts +9 -0
- package/dist/commands/sdk/index.d.ts.map +1 -0
- package/dist/commands/sdk/index.js +58 -0
- package/dist/commands/sdk/index.js.map +1 -0
- package/dist/commands/sdk/run.d.ts +42 -0
- package/dist/commands/sdk/run.d.ts.map +1 -0
- package/dist/commands/sdk/run.js +245 -0
- package/dist/commands/sdk/run.js.map +1 -0
- package/dist/commands/sdk/status.d.ts +14 -0
- package/dist/commands/sdk/status.d.ts.map +1 -0
- package/dist/commands/sdk/status.js +69 -0
- package/dist/commands/sdk/status.js.map +1 -0
- package/dist/migrations/manifests/0.3.2.json +16 -0
- package/dist/migrations/manifests/0.3.3.json +9 -0
- package/dist/migrations/manifests/0.3.4.json +9 -0
- package/dist/templates/common/bundled-skills/cstl-meta/references/platform-files/hooks-and-settings.md +2 -1
- package/dist/templates/common/bundled-skills/smart-search-cli/SKILL.md +35 -29
- package/dist/templates/common/bundled-skills/smart-search-cli/references/cli-contract.md +11 -25
- package/dist/templates/cursor/fixtures/expected-rules.d.ts.map +1 -1
- package/dist/templates/cursor/fixtures/expected-rules.js +5 -0
- package/dist/templates/cursor/fixtures/expected-rules.js.map +1 -1
- package/dist/templates/cursor/hooks.json +7 -0
- package/dist/templates/cursor/rules/cstl-session-rename.mdc +31 -0
- package/dist/templates/cursor/rules/retrieval-routing.mdc +1 -1
- package/dist/templates/markdown/agents.md +2 -0
- package/dist/templates/markdown/spec/guides/retrieval-daily-guide.md.txt +14 -0
- package/dist/templates/shared-hooks/index.d.ts +1 -1
- package/dist/templates/shared-hooks/index.d.ts.map +1 -1
- package/dist/templates/shared-hooks/index.js +1 -0
- package/dist/templates/shared-hooks/index.js.map +1 -1
- package/dist/templates/shared-hooks/rename-session-for-task.py +231 -0
- package/dist/templates/trellis/scripts/common/codebase_retrieval_router.py +14 -0
- package/dist/templates/trellis/scripts/common/parent_orchestration.py +341 -2
- package/dist/templates/trellis/scripts/common/task_map.py +638 -614
- package/dist/templates/trellis/scripts/common/task_store.py +47 -1
- package/dist/templates/trellis/scripts/task.py +32 -0
- package/dist/templates/trellis/tasks/templates/release-execution/design.md +60 -60
- package/dist/utils/codebase-retrieval-router.d.ts.map +1 -1
- package/dist/utils/codebase-retrieval-router.js +14 -0
- package/dist/utils/codebase-retrieval-router.js.map +1 -1
- package/dist/utils/cursor-sdk-gate.d.ts +10 -0
- package/dist/utils/cursor-sdk-gate.d.ts.map +1 -0
- package/dist/utils/cursor-sdk-gate.js +32 -0
- package/dist/utils/cursor-sdk-gate.js.map +1 -0
- package/dist/utils/project-capabilities.d.ts +30 -3
- package/dist/utils/project-capabilities.d.ts.map +1 -1
- package/dist/utils/project-capabilities.js +182 -11
- package/dist/utils/project-capabilities.js.map +1 -1
- package/dist/utils/readiness.d.ts.map +1 -1
- package/dist/utils/readiness.js +26 -0
- package/dist/utils/readiness.js.map +1 -1
- package/package.json +4 -3
|
@@ -289,6 +289,317 @@ def build_child_prompt(
|
|
|
289
289
|
return "\n".join(lines), errors
|
|
290
290
|
|
|
291
291
|
|
|
292
|
+
|
|
293
|
+
def _children_by_id(data: dict) -> dict[str, dict]:
|
|
294
|
+
return {
|
|
295
|
+
item.get("id"): item
|
|
296
|
+
for item in data.get("children", [])
|
|
297
|
+
if isinstance(item, dict) and isinstance(item.get("id"), str)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _normalize_stages(data: dict) -> tuple[list[dict], list[str], bool]:
|
|
302
|
+
"""Return (stages, errors, used_legacy_default)."""
|
|
303
|
+
errors: list[str] = []
|
|
304
|
+
children = _children_by_id(data)
|
|
305
|
+
raw_stages = data.get("stages") or []
|
|
306
|
+
if not isinstance(raw_stages, list):
|
|
307
|
+
return [], ["task-map stages must be a list"], False
|
|
308
|
+
|
|
309
|
+
if not raw_stages:
|
|
310
|
+
units = list(children.keys())
|
|
311
|
+
return (
|
|
312
|
+
[{"id": "default", "title": "All children (no stages declared)", "units": units}],
|
|
313
|
+
[],
|
|
314
|
+
True,
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
stages: list[dict] = []
|
|
318
|
+
seen_units: set[str] = set()
|
|
319
|
+
for idx, stage in enumerate(raw_stages):
|
|
320
|
+
if not isinstance(stage, dict):
|
|
321
|
+
errors.append(f"stages[{idx}] must be a mapping")
|
|
322
|
+
continue
|
|
323
|
+
stage_id = stage.get("id")
|
|
324
|
+
if not isinstance(stage_id, str) or not stage_id.strip():
|
|
325
|
+
errors.append(f"stages[{idx}] missing id")
|
|
326
|
+
continue
|
|
327
|
+
units = stage.get("units") or []
|
|
328
|
+
if not isinstance(units, list):
|
|
329
|
+
errors.append(f"stage `{stage_id}` units must be a list")
|
|
330
|
+
continue
|
|
331
|
+
norm_units: list[str] = []
|
|
332
|
+
for unit in units:
|
|
333
|
+
if not isinstance(unit, str) or not unit.strip():
|
|
334
|
+
errors.append(f"stage `{stage_id}` has invalid unit entry")
|
|
335
|
+
continue
|
|
336
|
+
if unit not in children:
|
|
337
|
+
errors.append(f"stage `{stage_id}` unit `{unit}` not in children[]")
|
|
338
|
+
continue
|
|
339
|
+
if unit in seen_units:
|
|
340
|
+
errors.append(f"unit `{unit}` appears in multiple stages")
|
|
341
|
+
continue
|
|
342
|
+
seen_units.add(unit)
|
|
343
|
+
norm_units.append(unit)
|
|
344
|
+
stages.append(
|
|
345
|
+
{
|
|
346
|
+
"id": stage_id,
|
|
347
|
+
"title": stage.get("title") if isinstance(stage.get("title"), str) else "",
|
|
348
|
+
"units": norm_units,
|
|
349
|
+
}
|
|
350
|
+
)
|
|
351
|
+
return stages, errors, False
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def _unit_readiness(
|
|
355
|
+
unit_id: str,
|
|
356
|
+
children_by_id: dict[str, dict],
|
|
357
|
+
) -> tuple[str, list[str]]:
|
|
358
|
+
entry = children_by_id.get(unit_id) or {}
|
|
359
|
+
blocked = _unmet_dependencies(entry, children_by_id)
|
|
360
|
+
return ("ready" if not blocked else "blocked", blocked)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def build_publish_pack(
|
|
364
|
+
parent_dir: Path,
|
|
365
|
+
*,
|
|
366
|
+
stage_id: str | None = None,
|
|
367
|
+
mode: str = "inline",
|
|
368
|
+
dry_run: bool = False,
|
|
369
|
+
) -> tuple[str | None, list[str]]:
|
|
370
|
+
"""Build campaign PACK index + per-unit prompts under child-prompts/."""
|
|
371
|
+
errors: list[str] = []
|
|
372
|
+
repo_root = get_repo_root()
|
|
373
|
+
parent_rel = _repo_rel(parent_dir, repo_root)
|
|
374
|
+
|
|
375
|
+
if not (parent_dir / "task.json").is_file():
|
|
376
|
+
return None, [f"parent task.json missing: {parent_dir}"]
|
|
377
|
+
|
|
378
|
+
data, _ = load_task_map(parent_dir)
|
|
379
|
+
if data is None:
|
|
380
|
+
return None, ["parent task-map.md missing or invalid"]
|
|
381
|
+
|
|
382
|
+
stages, stage_errors, legacy = _normalize_stages(data)
|
|
383
|
+
errors.extend(stage_errors)
|
|
384
|
+
if errors:
|
|
385
|
+
return None, errors
|
|
386
|
+
|
|
387
|
+
if stage_id:
|
|
388
|
+
stages = [s for s in stages if s.get("id") == stage_id]
|
|
389
|
+
if not stages:
|
|
390
|
+
return None, [f"unknown stage id: {stage_id}"]
|
|
391
|
+
|
|
392
|
+
children_by_id = _children_by_id(data)
|
|
393
|
+
prompts_dir = parent_dir / "child-prompts"
|
|
394
|
+
ready_rows: list[tuple[str, str, str]] = []
|
|
395
|
+
blocked_rows: list[tuple[str, str, list[str]]] = []
|
|
396
|
+
|
|
397
|
+
pack_lines = [
|
|
398
|
+
f"# Campaign PACK — `{parent_dir.name}`",
|
|
399
|
+
"",
|
|
400
|
+
f"- Parent: `{parent_rel}`",
|
|
401
|
+
f"- Generated: {_utc_now()}",
|
|
402
|
+
f"- Mode: `{mode}`",
|
|
403
|
+
"- Path rule: every worker must `task.py select <explicit-child-path>`; "
|
|
404
|
+
"do not rely on selected_task inheritance across windows.",
|
|
405
|
+
"",
|
|
406
|
+
"## Manual window path (PACK)",
|
|
407
|
+
"",
|
|
408
|
+
"1. Run `publish-pack` (this file) on the Parent.",
|
|
409
|
+
"2. For each **ready** unit: IDE **New Chat** or Agents Window → paste `child-prompts/<child>.md`.",
|
|
410
|
+
"3. In that session run `python ./.cstl/scripts/task.py select <child-path>`.",
|
|
411
|
+
"4. Parent retains `review-child` / `integrate-child`. PACK is not RUN auto-dispatch.",
|
|
412
|
+
"",
|
|
413
|
+
"## Stages",
|
|
414
|
+
"",
|
|
415
|
+
]
|
|
416
|
+
if legacy:
|
|
417
|
+
pack_lines.append(
|
|
418
|
+
"> Warning: no `stages:` in task-map; using implicit `default` stage "
|
|
419
|
+
"with all children. Declare `stages:` for HYBRID campaign packs."
|
|
420
|
+
)
|
|
421
|
+
pack_lines.append("")
|
|
422
|
+
|
|
423
|
+
written: list[str] = []
|
|
424
|
+
for stage in stages:
|
|
425
|
+
sid = stage["id"]
|
|
426
|
+
title = stage.get("title") or ""
|
|
427
|
+
heading = f"### `{sid}`" + (f" — {title}" if title else "")
|
|
428
|
+
pack_lines.append(heading)
|
|
429
|
+
pack_lines.append("")
|
|
430
|
+
for unit in stage.get("units") or []:
|
|
431
|
+
readiness, blocked = _unit_readiness(unit, children_by_id)
|
|
432
|
+
child_dir = parent_dir.parent / unit
|
|
433
|
+
child_rel = _repo_rel(child_dir, repo_root)
|
|
434
|
+
state = (children_by_id.get(unit) or {}).get("state", "?")
|
|
435
|
+
if readiness == "ready":
|
|
436
|
+
prompt, prompt_errors = build_child_prompt(
|
|
437
|
+
parent_dir,
|
|
438
|
+
child_dir,
|
|
439
|
+
include_artifacts=False,
|
|
440
|
+
mode=mode,
|
|
441
|
+
)
|
|
442
|
+
if prompt_errors or not prompt:
|
|
443
|
+
errors.extend(prompt_errors or [f"could not build prompt for `{unit}`"])
|
|
444
|
+
continue
|
|
445
|
+
out_name = f"{unit}.md"
|
|
446
|
+
out_rel = f"{parent_rel}/child-prompts/{out_name}"
|
|
447
|
+
if not dry_run:
|
|
448
|
+
prompts_dir.mkdir(parents=True, exist_ok=True)
|
|
449
|
+
(prompts_dir / out_name).write_text(prompt + "\n", encoding="utf-8")
|
|
450
|
+
written.append(out_rel)
|
|
451
|
+
ready_rows.append((sid, unit, out_rel))
|
|
452
|
+
pack_lines.append(
|
|
453
|
+
f"- `{unit}` — `{state}` — **ready** — prompt: `child-prompts/{out_name}` "
|
|
454
|
+
f"(select `{child_rel}`)"
|
|
455
|
+
)
|
|
456
|
+
else:
|
|
457
|
+
blocked_rows.append((sid, unit, blocked))
|
|
458
|
+
stub = "\n".join(
|
|
459
|
+
[
|
|
460
|
+
f"# BLOCKED — `{unit}`",
|
|
461
|
+
"",
|
|
462
|
+
f"- Parent: `{parent_rel}`",
|
|
463
|
+
f"- Child: `{child_rel}`",
|
|
464
|
+
f"- Stage: `{sid}`",
|
|
465
|
+
"- Do not start implementation until dependencies reach "
|
|
466
|
+
"`integrated` or `cancelled`.",
|
|
467
|
+
"",
|
|
468
|
+
"Unmet dependencies:",
|
|
469
|
+
*[f"- {item}" for item in blocked],
|
|
470
|
+
"",
|
|
471
|
+
]
|
|
472
|
+
)
|
|
473
|
+
out_name = f"{unit}.md"
|
|
474
|
+
out_rel = f"{parent_rel}/child-prompts/{out_name}"
|
|
475
|
+
if not dry_run:
|
|
476
|
+
prompts_dir.mkdir(parents=True, exist_ok=True)
|
|
477
|
+
(prompts_dir / out_name).write_text(stub, encoding="utf-8")
|
|
478
|
+
written.append(out_rel)
|
|
479
|
+
pack_lines.append(
|
|
480
|
+
f"- `{unit}` — `{state}` — **blocked** — {', '.join(blocked)}"
|
|
481
|
+
)
|
|
482
|
+
pack_lines.append("")
|
|
483
|
+
|
|
484
|
+
pack_lines.extend(["## Ready", ""])
|
|
485
|
+
if ready_rows:
|
|
486
|
+
for sid, unit, path in ready_rows:
|
|
487
|
+
pack_lines.append(f"- `{sid}` / `{unit}` → `{path}`")
|
|
488
|
+
else:
|
|
489
|
+
pack_lines.append("(none)")
|
|
490
|
+
pack_lines.extend(["", "## Blocked", ""])
|
|
491
|
+
if blocked_rows:
|
|
492
|
+
for sid, unit, deps in blocked_rows:
|
|
493
|
+
pack_lines.append(f"- `{sid}` / `{unit}` — {', '.join(deps)}")
|
|
494
|
+
else:
|
|
495
|
+
pack_lines.append("(none)")
|
|
496
|
+
pack_lines.append("")
|
|
497
|
+
|
|
498
|
+
pack_body = "\n".join(pack_lines)
|
|
499
|
+
pack_rel = f"{parent_rel}/child-prompts/PACK.md"
|
|
500
|
+
if not dry_run:
|
|
501
|
+
prompts_dir.mkdir(parents=True, exist_ok=True)
|
|
502
|
+
(prompts_dir / "PACK.md").write_text(pack_body + "\n", encoding="utf-8")
|
|
503
|
+
written.append(pack_rel)
|
|
504
|
+
|
|
505
|
+
if errors:
|
|
506
|
+
return None, errors
|
|
507
|
+
|
|
508
|
+
summary = [
|
|
509
|
+
f"# publish-pack — `{parent_dir.name}`",
|
|
510
|
+
"",
|
|
511
|
+
f"- Parent: `{parent_rel}`",
|
|
512
|
+
f"- dry_run: {dry_run}",
|
|
513
|
+
f"- legacy_no_stages: {legacy}",
|
|
514
|
+
f"- ready: {len(ready_rows)}",
|
|
515
|
+
f"- blocked: {len(blocked_rows)}",
|
|
516
|
+
f"- pack_index: `{pack_rel}`",
|
|
517
|
+
"",
|
|
518
|
+
]
|
|
519
|
+
if written:
|
|
520
|
+
summary.append("## Written")
|
|
521
|
+
summary.append("")
|
|
522
|
+
for path in written:
|
|
523
|
+
summary.append(f"- `{path}`")
|
|
524
|
+
summary.append("")
|
|
525
|
+
elif dry_run:
|
|
526
|
+
summary.append("(dry-run: no files written)")
|
|
527
|
+
summary.append("")
|
|
528
|
+
summary.append(pack_body)
|
|
529
|
+
return "\n".join(summary), []
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
def build_parent_status_dict(parent_dir: Path) -> dict | None:
|
|
533
|
+
"""Machine-readable parent orchestration snapshot (campaign UI / MCP)."""
|
|
534
|
+
repo_root = get_repo_root()
|
|
535
|
+
parent_rel = _repo_rel(parent_dir, repo_root)
|
|
536
|
+
data, _ = load_task_map(parent_dir)
|
|
537
|
+
if data is None:
|
|
538
|
+
return None
|
|
539
|
+
|
|
540
|
+
children_by_id = _children_by_id(data)
|
|
541
|
+
stages, stage_errors, legacy = _normalize_stages(data)
|
|
542
|
+
|
|
543
|
+
stage_rows: list[dict] = []
|
|
544
|
+
for stage in stages:
|
|
545
|
+
units: list[dict] = []
|
|
546
|
+
for unit in stage.get("units") or []:
|
|
547
|
+
entry = children_by_id.get(unit) or {}
|
|
548
|
+
readiness, blocked = _unit_readiness(unit, children_by_id)
|
|
549
|
+
units.append(
|
|
550
|
+
{
|
|
551
|
+
"id": unit,
|
|
552
|
+
"state": entry.get("state", "?"),
|
|
553
|
+
"readiness": readiness,
|
|
554
|
+
"blockedBy": blocked,
|
|
555
|
+
}
|
|
556
|
+
)
|
|
557
|
+
stage_rows.append(
|
|
558
|
+
{
|
|
559
|
+
"id": stage["id"],
|
|
560
|
+
"title": stage.get("title") or "",
|
|
561
|
+
"units": units,
|
|
562
|
+
}
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
children_rows: list[dict] = []
|
|
566
|
+
for child in data.get("children") or []:
|
|
567
|
+
if not isinstance(child, dict):
|
|
568
|
+
continue
|
|
569
|
+
cid = child.get("id")
|
|
570
|
+
if not isinstance(cid, str):
|
|
571
|
+
continue
|
|
572
|
+
child_dir = parent_dir.parent / cid
|
|
573
|
+
children_rows.append(
|
|
574
|
+
{
|
|
575
|
+
"id": cid,
|
|
576
|
+
"state": child.get("state", "?"),
|
|
577
|
+
"dependsOn": child.get("depends_on") or [],
|
|
578
|
+
"touches": child.get("touches") or [],
|
|
579
|
+
"isolation": child.get("isolation"),
|
|
580
|
+
"branch": child.get("branch"),
|
|
581
|
+
"worktreePath": child.get("worktree_path"),
|
|
582
|
+
"evidence": child.get("evidence"),
|
|
583
|
+
"ref": child.get("ref"),
|
|
584
|
+
"verifyMd": (child_dir / "verify.md").is_file(),
|
|
585
|
+
"handoffMd": (child_dir / "handoff.md").is_file(),
|
|
586
|
+
}
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
return {
|
|
590
|
+
"id": parent_dir.name,
|
|
591
|
+
"path": parent_rel,
|
|
592
|
+
"contractEpoch": data.get("contract_epoch"),
|
|
593
|
+
"executionTopology": data.get("execution_topology"),
|
|
594
|
+
"mergeLimit": data.get("merge_limit"),
|
|
595
|
+
"stages": stage_rows,
|
|
596
|
+
"children": children_rows,
|
|
597
|
+
"integrationQueue": data.get("integration_queue") or [],
|
|
598
|
+
"stageErrors": stage_errors,
|
|
599
|
+
"legacyStages": legacy,
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
|
|
292
603
|
def build_parent_status(parent_dir: Path) -> str:
|
|
293
604
|
"""Render a parent task-map status summary for the reviewer."""
|
|
294
605
|
repo_root = get_repo_root()
|
|
@@ -305,10 +616,37 @@ def build_parent_status(parent_dir: Path) -> str:
|
|
|
305
616
|
f"- execution_topology: {data.get('execution_topology', '?')}",
|
|
306
617
|
f"- merge_limit: {data.get('merge_limit', '?')}",
|
|
307
618
|
"",
|
|
308
|
-
"## Children",
|
|
309
|
-
"",
|
|
310
619
|
]
|
|
311
620
|
|
|
621
|
+
children_by_id = _children_by_id(data)
|
|
622
|
+
stages, stage_errors, legacy = _normalize_stages(data)
|
|
623
|
+
lines.append("## Stages")
|
|
624
|
+
lines.append("")
|
|
625
|
+
if stage_errors:
|
|
626
|
+
for err in stage_errors:
|
|
627
|
+
lines.append(f"- error: {err}")
|
|
628
|
+
lines.append("")
|
|
629
|
+
elif legacy:
|
|
630
|
+
lines.append("stages: (none declared — implicit `default` for publish-pack)")
|
|
631
|
+
lines.append("")
|
|
632
|
+
else:
|
|
633
|
+
for stage in stages:
|
|
634
|
+
sid = stage["id"]
|
|
635
|
+
title = stage.get("title") or ""
|
|
636
|
+
heading = f"### `{sid}`" + (f" — {title}" if title else "")
|
|
637
|
+
lines.append(heading)
|
|
638
|
+
for unit in stage.get("units") or []:
|
|
639
|
+
entry = children_by_id.get(unit) or {}
|
|
640
|
+
state = entry.get("state", "?")
|
|
641
|
+
readiness, blocked = _unit_readiness(unit, children_by_id)
|
|
642
|
+
if readiness == "ready":
|
|
643
|
+
lines.append(f"- `{unit}` — `{state}` — ready")
|
|
644
|
+
else:
|
|
645
|
+
lines.append(f"- `{unit}` — `{state}` — blocked ({', '.join(blocked)})")
|
|
646
|
+
lines.append("")
|
|
647
|
+
|
|
648
|
+
lines.extend(["## Children", ""])
|
|
649
|
+
|
|
312
650
|
children = data.get("children") or []
|
|
313
651
|
if not children:
|
|
314
652
|
lines.append("(no children in task-map)")
|
|
@@ -354,6 +692,7 @@ def build_parent_status(parent_dir: Path) -> str:
|
|
|
354
692
|
lines.append("")
|
|
355
693
|
lines.append("```bash")
|
|
356
694
|
lines.append(f"python ./.cstl/scripts/task.py parent-status {parent_rel}")
|
|
695
|
+
lines.append(f"python ./.cstl/scripts/task.py publish-pack {parent_rel}")
|
|
357
696
|
lines.append(f"python ./.cstl/scripts/task.py generate-child-prompt {parent_rel} <child> --mode subagent")
|
|
358
697
|
lines.append(f"python ./.cstl/scripts/task.py generate-child-prompt {parent_rel} <child> --mode inline")
|
|
359
698
|
lines.append(f"python ./.cstl/scripts/task.py review-child {parent_rel} <child> --check")
|