cowork-flow 0.0.37 → 0.0.39

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.
Files changed (111) hide show
  1. package/README.md +27 -14
  2. package/package.json +1 -1
  3. package/src/commands/init.js +39 -28
  4. package/src/commands/sync.js +2 -2
  5. package/src/lib/asset-plan.js +127 -0
  6. package/src/lib/copy-template.js +101 -141
  7. package/src/lib/host-assets.js +231 -0
  8. package/src/lib/plan-applier.js +329 -0
  9. package/src/lib/platforms.js +15 -76
  10. package/src/lib/skill-registry.js +503 -0
  11. package/template/.claude/agents/cowork-check.md +10 -1
  12. package/template/.claude/agents/cowork-implement.md +11 -2
  13. package/template/.claude/hooks/inject-workflow-state.py +51 -254
  14. package/template/.codex/agents/cowork-check.toml +12 -3
  15. package/template/.codex/agents/cowork-implement.toml +11 -2
  16. package/template/.codex/agents/default.toml +3 -2
  17. package/template/.codex/agents/explorer.toml +3 -2
  18. package/template/.codex/agents/worker.toml +2 -1
  19. package/template/.codex/hooks/inject-workflow-state.py +46 -265
  20. package/template/.cowork-flow/.version +1 -1
  21. package/template/.cowork-flow/scripts/application/__init__.py +1 -0
  22. package/template/.cowork-flow/scripts/application/batch_execution.py +743 -0
  23. package/template/.cowork-flow/scripts/application/runtime_context_service.py +365 -0
  24. package/template/.cowork-flow/scripts/application/task_archive.py +220 -0
  25. package/template/.cowork-flow/scripts/application/task_context.py +765 -0
  26. package/template/.cowork-flow/scripts/application/task_creation.py +201 -0
  27. package/template/.cowork-flow/scripts/application/task_lifecycle.py +532 -0
  28. package/template/.cowork-flow/scripts/application/task_tree.py +179 -0
  29. package/template/.cowork-flow/scripts/commands/doctor.py +78 -57
  30. package/template/.cowork-flow/scripts/commands/party_mode_v2.py +671 -425
  31. package/template/.cowork-flow/scripts/commands/subagent.py +38 -26
  32. package/template/.cowork-flow/scripts/commands/task.py +535 -1953
  33. package/template/.cowork-flow/scripts/commands/task_archive_commands.py +195 -0
  34. package/template/.cowork-flow/scripts/commands/task_context_commands.py +240 -0
  35. package/template/.cowork-flow/scripts/commands/task_create_command.py +164 -0
  36. package/template/.cowork-flow/scripts/commands/task_navigation.py +407 -0
  37. package/template/.cowork-flow/scripts/commands/task_parser.py +364 -0
  38. package/template/.cowork-flow/scripts/commands/task_support.py +76 -0
  39. package/template/.cowork-flow/scripts/commands/task_tree_commands.py +160 -0
  40. package/template/.cowork-flow/scripts/common/core/execution_context.py +161 -85
  41. package/template/.cowork-flow/scripts/common/core/host_manifest.py +393 -0
  42. package/template/.cowork-flow/scripts/common/core/skill_registry.py +568 -0
  43. package/template/.cowork-flow/scripts/common/gates/coding_standards.py +58 -7
  44. package/template/.cowork-flow/scripts/common/gates/gates.py +323 -46
  45. package/template/.cowork-flow/scripts/common/gates/models.py +196 -0
  46. package/template/.cowork-flow/scripts/common/gates/registry.py +79 -0
  47. package/template/.cowork-flow/scripts/common/gates/tdd_evidence.py +13 -13
  48. package/template/.cowork-flow/scripts/common/gates/validate_coding_standards.py +104 -62
  49. package/template/.cowork-flow/scripts/common/gates/validate_implementation.py +39 -22
  50. package/template/.cowork-flow/scripts/common/gates/validate_rules.py +223 -40
  51. package/template/.cowork-flow/scripts/common/git/git_context.py +216 -182
  52. package/template/.cowork-flow/scripts/common/host/__init__.py +1 -0
  53. package/template/.cowork-flow/scripts/common/host/workflow_state_hook.py +303 -0
  54. package/template/.cowork-flow/scripts/common/storage/__init__.py +1 -0
  55. package/template/.cowork-flow/scripts/common/storage/operation_log.py +72 -0
  56. package/template/.cowork-flow/scripts/common/storage/state_store.py +279 -0
  57. package/template/.cowork-flow/scripts/common/storage/unit_of_work.py +214 -0
  58. package/template/.cowork-flow/scripts/common/task/active_task.py +93 -135
  59. package/template/.cowork-flow/scripts/common/task/batch_mode.py +33 -120
  60. package/template/.cowork-flow/scripts/common/task/decision_review.py +85 -0
  61. package/template/.cowork-flow/scripts/common/task/readiness.py +9 -15
  62. package/template/.cowork-flow/scripts/common/task/task_repository.py +156 -0
  63. package/template/.cowork-flow/scripts/run.py +24 -0
  64. package/template/.cowork-flow/spec/contracts/decision-anchor.md +8 -2
  65. package/template/.cowork-flow/spec/contracts/subagent-dispatch.md +2 -4
  66. package/template/.cowork-flow/spec/contracts/workflow-state-templates.md +1 -1
  67. package/template/.cowork-flow/spec/protocols/decision-review.md +28 -0
  68. package/template/.cowork-flow/spec/protocols/review.md +34 -0
  69. package/template/.cowork-flow/spec/protocols/spec-maintenance.md +16 -0
  70. package/template/.cowork-flow/spec/protocols/tdd.md +30 -0
  71. package/template/.cowork-flow/spec/references/definition-of-done.md +4 -4
  72. package/template/.cowork-flow/spec/references/orchestration-patterns.md +1 -1
  73. package/template/.cowork-flow/spec/runtime/contract-registry.json +25 -0
  74. package/template/.cowork-flow/spec/runtime/host-assets.json +214 -0
  75. package/template/.cowork-flow/spec/runtime/index.md +8 -0
  76. package/template/.cowork-flow/spec/runtime/rules.json +61 -10
  77. package/template/.cowork-flow/spec/runtime/skill-registry.json +397 -0
  78. package/template/.cowork-flow/spec/schemas/host-assets.schema.json +173 -0
  79. package/template/.cowork-flow/spec/schemas/rules.schema.json +10 -1
  80. package/template/.cowork-flow/spec/schemas/skill-registry.schema.json +202 -0
  81. package/template/.cowork-flow/workflow.md +18 -13
  82. package/template/.opencode/agents/cowork-check.md +11 -0
  83. package/template/.opencode/agents/cowork-implement.md +16 -1
  84. package/template/.zcode/.zcode-plugin/plugin.json +1 -1
  85. package/template/.zcode/hooks/inject-context.js +3 -85
  86. package/template/.zcode/hooks/inject-context.selfcheck.mjs +6 -6
  87. package/template/.zcode/scaffold/.cowork-flow/spec/contracts/decision-anchor.md +8 -2
  88. package/template/.zcode/scaffold/.cowork-flow/spec/contracts/subagent-dispatch.md +2 -4
  89. package/template/.zcode/scaffold/.cowork-flow/spec/runtime/rules.json +61 -10
  90. package/template/.zcode/scaffold/.cowork-flow/spec/schemas/rules.schema.json +10 -1
  91. package/template/.zcode/scaffold/.cowork-flow/workflow.md +18 -13
  92. package/template/.zcode/scaffold/AGENTS.md +18 -8
  93. package/template/AGENTS.md +1 -1
  94. package/template/skills/batch-mode/SKILL.md +140 -76
  95. package/template/skills/brainstorming/SKILL.md +2 -2
  96. package/template/skills/cowork-flow/SKILL.md +42 -0
  97. package/template/skills/party-mode/SHARED-BOUNDARIES.md +1 -1
  98. package/template/skills/party-mode/SKILL.md +48 -119
  99. package/template/.cowork-flow/scripts/common/gates/spec_validator.py +0 -53
  100. package/template/.cowork-flow/scripts/common/gates/validate_jsonl.py +0 -32
  101. package/template/skills/before-dev/SKILL.md +0 -41
  102. package/template/skills/check/SKILL.md +0 -53
  103. package/template/skills/check/simplification-guide.md +0 -47
  104. package/template/skills/continue/SKILL.md +0 -29
  105. package/template/skills/doubt-review/SKILL.md +0 -118
  106. package/template/skills/finish-work/SKILL.md +0 -32
  107. package/template/skills/party-mode-v2/SKILL.md +0 -75
  108. package/template/skills/start/SKILL.md +0 -78
  109. package/template/skills/tdd/SKILL.md +0 -74
  110. package/template/skills/update-spec/SKILL.md +0 -39
  111. package/template/skills/using-cowork-flow/SKILL.md +0 -80
@@ -50,7 +50,7 @@ class GitSnapshot:
50
50
  class ActiveTaskSnapshot:
51
51
  path: str | None
52
52
  data: dict | None
53
- has_prd: bool
53
+ has_decision_anchor: bool
54
54
 
55
55
 
56
56
  def _run_git_command(args: list[str], cwd: Path | None = None) -> tuple[int, str, str]:
@@ -179,7 +179,7 @@ def _get_active_task_snapshot(repo_root: Path) -> ActiveTaskSnapshot:
179
179
  """Collect active-task metadata once for text and JSON renderers."""
180
180
  active_task = get_active_task(repo_root).task_path
181
181
  if not active_task:
182
- return ActiveTaskSnapshot(path=None, data=None, has_prd=False)
182
+ return ActiveTaskSnapshot(path=None, data=None, has_decision_anchor=False)
183
183
 
184
184
  active_task_dir = repo_root / active_task
185
185
  task_json_path = active_task_dir / FILE_TASK_JSON
@@ -189,7 +189,7 @@ def _get_active_task_snapshot(repo_root: Path) -> ActiveTaskSnapshot:
189
189
  return ActiveTaskSnapshot(
190
190
  path=active_task,
191
191
  data=data,
192
- has_prd=(active_task_dir / "decision-anchor.md").is_file(),
192
+ has_decision_anchor=(active_task_dir / "decision-anchor.md").is_file(),
193
193
  )
194
194
 
195
195
 
@@ -270,7 +270,7 @@ def _build_resume_checklist(
270
270
  active_task = snapshot.path
271
271
  commands.append(f"./{DIR_WORKFLOW}/run task list-context {active_task}")
272
272
 
273
- if snapshot.has_prd:
273
+ if snapshot.has_decision_anchor:
274
274
  read_files.append(f"{active_task}/decision-anchor.md")
275
275
 
276
276
  read_files.extend(_task_plan_references(repo_root, snapshot))
@@ -314,11 +314,11 @@ def _append_resume_checklist(
314
314
  return
315
315
 
316
316
  active_task = snapshot.path
317
- prd_path = f"{active_task}/decision-anchor.md"
318
- if prd_path in read_files:
319
- lines.append(f"- Read active task PRD: {prd_path}")
317
+ decision_anchor_path = f"{active_task}/decision-anchor.md"
318
+ if decision_anchor_path in read_files:
319
+ lines.append(f"- Read active task decision-anchor: {decision_anchor_path}")
320
320
  else:
321
- lines.append(f"- Active task PRD missing: {prd_path}")
321
+ lines.append(f"- Active task decision-anchor missing: {decision_anchor_path}")
322
322
 
323
323
  lines.append(f"- List task context before reading details: {commands[1]}")
324
324
 
@@ -338,7 +338,7 @@ def _append_active_task(
338
338
  snapshot: ActiveTaskSnapshot,
339
339
  include_created: bool = False,
340
340
  include_description: bool = False,
341
- include_prd_hint: bool = False,
341
+ include_decision_anchor_hint: bool = False,
342
342
  ) -> None:
343
343
  """Append the ACTIVE TASK section to text output."""
344
344
  lines.append("## ACTIVE TASK")
@@ -360,7 +360,7 @@ def _append_active_task(
360
360
  if description:
361
361
  lines.append(f"Description: {description}")
362
362
 
363
- if include_prd_hint and snapshot.has_prd:
363
+ if include_decision_anchor_hint and snapshot.has_decision_anchor:
364
364
  lines.append("")
365
365
  lines.append("[!] This task has decision-anchor.md - read it for task details")
366
366
  lines.append("")
@@ -404,29 +404,88 @@ def _children_progress(children: list[str], statuses: dict[str, str]) -> str:
404
404
  return f" [{_children_done_count(children, statuses)}/{len(children)} done]"
405
405
 
406
406
 
407
- # =============================================================================
408
- # JSON Output
409
- # =============================================================================
407
+ def _append_active_tasks(lines: list[str], tasks_dir: Path) -> None:
408
+ """Append active task hierarchy to text output."""
409
+ lines.append("## ACTIVE TASKS")
410
+ task_count = 0
410
411
 
412
+ all_task_data = _load_task_context_by_dir(tasks_dir)
413
+ all_task_statuses = {
414
+ dir_name: data["status"]
415
+ for dir_name, data in all_task_data.items()
416
+ }
411
417
 
412
- def get_context_json(repo_root: Path | None = None) -> dict:
413
- """Get context as a dictionary.
418
+ def _print_task_tree(name: str, indent: int = 0) -> None:
419
+ nonlocal task_count
420
+ info = all_task_data[name]
421
+ progress = _children_progress(info["children"], all_task_statuses)
422
+ prefix = " " * indent
423
+ lines.append(f"{prefix}- {name}/ ({info['status']}){progress} @{info['assignee']}")
424
+ task_count += 1
425
+ for child in info["children"]:
426
+ if child in all_task_data:
427
+ _print_task_tree(child, indent + 1)
414
428
 
415
- Args:
416
- repo_root: Repository root path. Defaults to auto-detected.
429
+ for dir_name in sorted(all_task_data.keys()):
430
+ if not all_task_data[dir_name]["parent"]:
431
+ _print_task_tree(dir_name)
417
432
 
418
- Returns:
419
- Context dictionary.
420
- """
421
- if repo_root is None:
422
- repo_root = get_repo_root()
433
+ if task_count == 0:
434
+ lines.append("(no active tasks)")
435
+ lines.append(f"Total: {task_count} active task(s)")
436
+ lines.append("")
423
437
 
424
- developer = get_developer(repo_root)
425
- tasks_dir = get_tasks_dir(repo_root)
438
+
439
+ def _append_my_tasks(lines: list[str], developer: str, tasks_dir: Path) -> None:
440
+ """Append tasks assigned to the current developer."""
441
+ lines.append("## MY TASKS (Assigned to me)")
442
+ my_task_count = 0
443
+ all_task_data = _load_task_context_by_dir(tasks_dir)
444
+ all_task_statuses = _task_statuses(all_task_data)
445
+
446
+ for dir_name in sorted(all_task_data.keys()):
447
+ info = all_task_data[dir_name]
448
+ assignee = info["my_assignee"]
449
+ status = info["my_status"]
450
+
451
+ if assignee == developer and status != "done":
452
+ progress = _children_progress(info["children"], all_task_statuses)
453
+ lines.append(f"- [{info['priority']}] {info['title']} ({status}){progress}")
454
+ my_task_count += 1
455
+
456
+ if my_task_count == 0:
457
+ lines.append("(no tasks assigned to you)")
458
+ lines.append("")
459
+
460
+
461
+ def _append_journal_file(lines: list[str], repo_root: Path, developer: str) -> None:
462
+ """Append journal file information to text output."""
463
+ lines.append("## JOURNAL FILE")
426
464
  journal_file = get_active_journal_file(repo_root)
427
- git_snapshot = _get_git_snapshot(repo_root)
428
- active_task_snapshot = _get_active_task_snapshot(repo_root)
465
+ if journal_file:
466
+ journal_lines = count_lines(journal_file)
467
+ relative = f"{DIR_WORKFLOW}/{DIR_WORKSPACE}/{developer}/{journal_file.name}"
468
+ lines.append(f"Active file: {relative}")
469
+ lines.append(f"Line count: {journal_lines} / 2000")
470
+ if journal_lines > 1800:
471
+ lines.append("[!] WARNING: Approaching 2000 line limit!")
472
+ else:
473
+ lines.append("No journal file found")
474
+ lines.append("")
475
+
476
+
477
+ def _append_paths(lines: list[str], developer: str) -> None:
478
+ """Append standard cowork-flow paths."""
479
+ lines.append("## PATHS")
480
+ lines.append(f"Workspace: {DIR_WORKFLOW}/{DIR_WORKSPACE}/{developer}/")
481
+ lines.append(f"Tasks: {DIR_WORKFLOW}/{DIR_TASKS}/")
482
+ lines.append(f"Spec: {DIR_WORKFLOW}/{DIR_SPEC}/")
483
+ lines.append("")
484
+
429
485
 
486
+ def _context_journal_json(repo_root: Path, developer: str | None) -> dict:
487
+ """Build journal context for JSON output."""
488
+ journal_file = get_active_journal_file(repo_root)
430
489
  journal_lines = 0
431
490
  journal_relative = ""
432
491
  if journal_file and developer:
@@ -434,8 +493,15 @@ def get_context_json(repo_root: Path | None = None) -> dict:
434
493
  journal_relative = (
435
494
  f"{DIR_WORKFLOW}/{DIR_WORKSPACE}/{developer}/{journal_file.name}"
436
495
  )
496
+ return {
497
+ "file": journal_relative,
498
+ "lines": journal_lines,
499
+ "nearLimit": journal_lines > 1800,
500
+ }
437
501
 
438
- # Tasks
502
+
503
+ def _context_tasks_json(tasks_dir: Path) -> list[dict]:
504
+ """Build active task list for JSON output."""
439
505
  tasks = []
440
506
  for dir_name, data in _load_task_json_by_dir(tasks_dir, sort=False).items():
441
507
  tasks.append(
@@ -447,19 +513,39 @@ def get_context_json(repo_root: Path | None = None) -> dict:
447
513
  "parent": data.get("parent"),
448
514
  }
449
515
  )
516
+ return tasks
517
+
518
+
519
+ # =============================================================================
520
+ # JSON Output
521
+ # =============================================================================
522
+
523
+
524
+ def get_context_json(repo_root: Path | None = None) -> dict:
525
+ """Get context as a dictionary.
526
+
527
+ Args:
528
+ repo_root: Repository root path. Defaults to auto-detected.
529
+
530
+ Returns:
531
+ Context dictionary.
532
+ """
533
+ if repo_root is None:
534
+ repo_root = get_repo_root()
535
+
536
+ developer = get_developer(repo_root)
537
+ tasks_dir = get_tasks_dir(repo_root)
538
+ git_snapshot = _get_git_snapshot(repo_root)
539
+ active_task_snapshot = _get_active_task_snapshot(repo_root)
450
540
 
451
541
  return {
452
542
  "developer": developer or "",
453
543
  "git": _git_json(git_snapshot),
454
544
  "tasks": {
455
- "active": tasks,
545
+ "active": _context_tasks_json(tasks_dir),
456
546
  "directory": f"{DIR_WORKFLOW}/{DIR_TASKS}",
457
547
  },
458
- "journal": {
459
- "file": journal_relative,
460
- "lines": journal_lines,
461
- "nearLimit": journal_lines > 1800,
462
- },
548
+ "journal": _context_journal_json(repo_root, developer),
463
549
  "resumeChecklist": _build_resume_checklist(repo_root, active_task_snapshot),
464
550
  }
465
551
 
@@ -479,37 +565,32 @@ def output_json(repo_root: Path | None = None) -> None:
479
565
  # =============================================================================
480
566
 
481
567
 
482
- def get_context_text(repo_root: Path | None = None) -> str:
483
- """Get context as formatted text.
484
-
485
- Args:
486
- repo_root: Repository root path. Defaults to auto-detected.
487
-
488
- Returns:
489
- Formatted text output.
490
- """
491
- if repo_root is None:
492
- repo_root = get_repo_root()
493
-
494
- lines = []
495
- lines.append("========================================")
496
- lines.append("SESSION CONTEXT")
497
- lines.append("========================================")
498
- lines.append("")
568
+ def _new_context_lines(title: str) -> list[str]:
569
+ return [
570
+ "========================================",
571
+ title,
572
+ "========================================",
573
+ "",
574
+ ]
499
575
 
500
- developer = get_developer(repo_root)
501
576
 
502
- # Developer section
577
+ def _append_developer(lines: list[str], developer: str | None) -> bool:
503
578
  lines.append("## DEVELOPER")
504
579
  if not developer:
505
580
  lines.append(
506
581
  f"ERROR: Not initialized. Run: ./{DIR_WORKFLOW}/run init-developer <name>"
507
582
  )
508
- return "\n".join(lines)
509
-
583
+ return False
510
584
  lines.append(f"Name: {developer}")
511
585
  lines.append("")
586
+ return True
512
587
 
588
+
589
+ def _append_default_context_sections(
590
+ lines: list[str],
591
+ repo_root: Path,
592
+ developer: str,
593
+ ) -> None:
513
594
  git_snapshot = _get_git_snapshot(repo_root)
514
595
  _append_git_status(lines, git_snapshot, repo_root)
515
596
  _append_recent_commits(lines, git_snapshot)
@@ -519,81 +600,35 @@ def get_context_text(repo_root: Path | None = None) -> str:
519
600
  active_task_snapshot,
520
601
  include_created=True,
521
602
  include_description=True,
522
- include_prd_hint=True,
603
+ include_decision_anchor_hint=True,
523
604
  )
524
605
  _append_resume_checklist(lines, repo_root, active_task_snapshot)
525
606
 
526
- # Active tasks
527
- lines.append("## ACTIVE TASKS")
528
607
  tasks_dir = get_tasks_dir(repo_root)
529
- task_count = 0
608
+ _append_active_tasks(lines, tasks_dir)
609
+ _append_my_tasks(lines, developer, tasks_dir)
610
+ _append_journal_file(lines, repo_root, developer)
611
+ _append_paths(lines, developer)
530
612
 
531
- all_task_data = _load_task_context_by_dir(tasks_dir)
532
- all_task_statuses = {
533
- dir_name: data["status"]
534
- for dir_name, data in all_task_data.items()
535
- }
536
613
 
537
- def _print_task_tree(name: str, indent: int = 0) -> None:
538
- nonlocal task_count
539
- info = all_task_data[name]
540
- progress = _children_progress(info["children"], all_task_statuses)
541
- prefix = " " * indent
542
- lines.append(f"{prefix}- {name}/ ({info['status']}){progress} @{info['assignee']}")
543
- task_count += 1
544
- for child in info["children"]:
545
- if child in all_task_data:
546
- _print_task_tree(child, indent + 1)
547
-
548
- for dir_name in sorted(all_task_data.keys()):
549
- if not all_task_data[dir_name]["parent"]:
550
- _print_task_tree(dir_name)
551
-
552
- if task_count == 0:
553
- lines.append("(no active tasks)")
554
- lines.append(f"Total: {task_count} active task(s)")
555
- lines.append("")
556
-
557
- # My tasks
558
- lines.append("## MY TASKS (Assigned to me)")
559
- my_task_count = 0
560
-
561
- for dir_name in sorted(all_task_data.keys()):
562
- info = all_task_data[dir_name]
563
- assignee = info["my_assignee"]
564
- status = info["my_status"]
565
-
566
- if assignee == developer and status != "done":
567
- children_list = info["children"]
568
- progress = _children_progress(children_list, all_task_statuses)
569
- lines.append(f"- [{info['priority']}] {info['title']} ({status}){progress}")
570
- my_task_count += 1
614
+ def get_context_text(repo_root: Path | None = None) -> str:
615
+ """Get context as formatted text.
571
616
 
572
- if my_task_count == 0:
573
- lines.append("(no tasks assigned to you)")
574
- lines.append("")
617
+ Args:
618
+ repo_root: Repository root path. Defaults to auto-detected.
575
619
 
576
- # Journal file
577
- lines.append("## JOURNAL FILE")
578
- journal_file = get_active_journal_file(repo_root)
579
- if journal_file:
580
- journal_lines = count_lines(journal_file)
581
- relative = f"{DIR_WORKFLOW}/{DIR_WORKSPACE}/{developer}/{journal_file.name}"
582
- lines.append(f"Active file: {relative}")
583
- lines.append(f"Line count: {journal_lines} / 2000")
584
- if journal_lines > 1800:
585
- lines.append("[!] WARNING: Approaching 2000 line limit!")
586
- else:
587
- lines.append("No journal file found")
588
- lines.append("")
620
+ Returns:
621
+ Formatted text output.
622
+ """
623
+ if repo_root is None:
624
+ repo_root = get_repo_root()
589
625
 
590
- # Paths
591
- lines.append("## PATHS")
592
- lines.append(f"Workspace: {DIR_WORKFLOW}/{DIR_WORKSPACE}/{developer}/")
593
- lines.append(f"Tasks: {DIR_WORKFLOW}/{DIR_TASKS}/")
594
- lines.append(f"Spec: {DIR_WORKFLOW}/{DIR_SPEC}/")
595
- lines.append("")
626
+ lines = _new_context_lines("SESSION CONTEXT")
627
+ developer = get_developer(repo_root)
628
+ if not _append_developer(lines, developer):
629
+ return "\n".join(lines)
596
630
 
631
+ _append_default_context_sections(lines, repo_root, developer)
597
632
  lines.append("========================================")
598
633
 
599
634
  return "\n".join(lines)
@@ -608,100 +643,73 @@ def get_context_record_json(repo_root: Path | None = None) -> dict:
608
643
  repo_root = get_repo_root()
609
644
 
610
645
  developer = get_developer(repo_root)
611
- tasks_dir = get_tasks_dir(repo_root)
612
-
613
646
  git_snapshot = _get_git_snapshot(
614
647
  repo_root,
615
648
  include_empty_commit_message=False,
616
649
  )
617
650
 
618
- # My tasks
619
- my_tasks = []
620
- task_data_by_dir = _load_task_json_by_dir(tasks_dir)
621
- all_task_statuses = _task_statuses(task_data_by_dir)
622
-
623
- for dir_name, data in task_data_by_dir.items():
624
- if data.get("assignee") == developer:
625
- children_list = data.get("children", [])
626
- done = _children_done_count(children_list, all_task_statuses)
627
- my_tasks.append({
628
- "dir": dir_name,
629
- "title": data.get("title") or data.get("name") or "unknown",
630
- "status": data.get("status", "unknown"),
631
- "priority": data.get("priority", "P2"),
632
- "children": children_list,
633
- "childrenDone": done,
634
- "parent": data.get("parent"),
635
- "meta": data.get("meta", {}),
636
- })
637
-
638
651
  active_task_snapshot = _get_active_task_snapshot(repo_root)
639
652
 
640
653
  return {
641
654
  "developer": developer or "",
642
655
  "git": _git_json(git_snapshot),
643
- "myTasks": my_tasks,
656
+ "myTasks": _record_my_tasks_json(get_tasks_dir(repo_root), developer),
644
657
  "activeTask": _active_task_json(active_task_snapshot),
645
658
  "resumeChecklist": _build_resume_checklist(repo_root, active_task_snapshot),
646
659
  }
647
660
 
648
661
 
649
- def get_context_text_record(repo_root: Path | None = None) -> str:
650
- """Get context as formatted text for session recording mode.
651
-
652
- Focused output: MY ACTIVE TASKS first (with [!!!] emphasis),
653
- then GIT STATUS, RECENT COMMITS, ACTIVE TASK.
654
-
655
- Args:
656
- repo_root: Repository root path. Defaults to auto-detected.
657
-
658
- Returns:
659
- Formatted text output for session recording.
660
- """
661
- if repo_root is None:
662
- repo_root = get_repo_root()
663
-
664
- lines: list[str] = []
665
- lines.append("========================================")
666
- lines.append("SESSION CONTEXT (RECORD MODE)")
667
- lines.append("========================================")
668
- lines.append("")
662
+ def _record_my_tasks_json(tasks_dir: Path, developer: str | None) -> list[dict]:
663
+ """Build record-mode task list for JSON output."""
664
+ my_tasks = []
665
+ task_data_by_dir = _load_task_json_by_dir(tasks_dir)
666
+ all_task_statuses = _task_statuses(task_data_by_dir)
667
+ for dir_name, data in task_data_by_dir.items():
668
+ if data.get("assignee") != developer:
669
+ continue
670
+ children_list = data.get("children", [])
671
+ my_tasks.append({
672
+ "dir": dir_name,
673
+ "title": data.get("title") or data.get("name") or "unknown",
674
+ "status": data.get("status", "unknown"),
675
+ "priority": data.get("priority", "P2"),
676
+ "children": children_list,
677
+ "childrenDone": _children_done_count(children_list, all_task_statuses),
678
+ "parent": data.get("parent"),
679
+ "meta": data.get("meta", {}),
680
+ })
681
+ return my_tasks
669
682
 
670
- developer = get_developer(repo_root)
671
- if not developer:
672
- lines.append(
673
- f"ERROR: Not initialized. Run: ./{DIR_WORKFLOW}/run init-developer <name>"
674
- )
675
- return "\n".join(lines)
676
683
 
677
- # MY ACTIVE TASKS - first and prominent
684
+ def _append_record_my_tasks(
685
+ lines: list[str],
686
+ developer: str,
687
+ tasks_dir: Path,
688
+ ) -> None:
689
+ """Append prominent record-mode tasks assigned to the developer."""
678
690
  lines.append(f"## [!!!] MY ACTIVE TASKS (Assigned to {developer})")
679
691
  lines.append("[!] Review whether any should be archived before recording this session.")
680
692
  lines.append("")
681
693
 
682
- tasks_dir = get_tasks_dir(repo_root)
683
694
  my_task_count = 0
684
-
685
- # Collect task data for children progress
686
695
  task_data_by_dir = _load_task_json_by_dir(tasks_dir)
687
696
  all_task_statuses = _task_statuses(task_data_by_dir)
688
-
689
697
  for dir_name, data in task_data_by_dir.items():
690
- assignee = data.get("assignee", "")
698
+ if data.get("assignee", "") != developer:
699
+ continue
700
+ title = data.get("title") or data.get("name") or "unknown"
701
+ priority = data.get("priority", "P2")
691
702
  status = data.get("status", "planning")
692
-
693
- if assignee == developer:
694
- title = data.get("title") or data.get("name") or "unknown"
695
- priority = data.get("priority", "P2")
696
- children_list = data.get("children", [])
697
- progress = _children_progress(children_list, all_task_statuses)
698
- lines.append(f"- [{priority}] {title} ({status}){progress} - {dir_name}")
699
- my_task_count += 1
703
+ progress = _children_progress(data.get("children", []), all_task_statuses)
704
+ lines.append(f"- [{priority}] {title} ({status}){progress} - {dir_name}")
705
+ my_task_count += 1
700
706
 
701
707
  if my_task_count == 0:
702
708
  lines.append("(no active tasks assigned to you)")
703
709
  lines.append("")
704
710
 
711
+
712
+ def _append_record_context_sections(lines: list[str], repo_root: Path) -> None:
705
713
  git_snapshot = _get_git_snapshot(repo_root)
706
714
  _append_git_status(lines, git_snapshot, repo_root)
707
715
  _append_recent_commits(lines, git_snapshot)
@@ -709,6 +717,32 @@ def get_context_text_record(repo_root: Path | None = None) -> str:
709
717
  _append_active_task(lines, active_task_snapshot)
710
718
  _append_resume_checklist(lines, repo_root, active_task_snapshot)
711
719
 
720
+
721
+ def get_context_text_record(repo_root: Path | None = None) -> str:
722
+ """Get context as formatted text for session recording mode.
723
+
724
+ Focused output: MY ACTIVE TASKS first (with [!!!] emphasis),
725
+ then GIT STATUS, RECENT COMMITS, ACTIVE TASK.
726
+
727
+ Args:
728
+ repo_root: Repository root path. Defaults to auto-detected.
729
+
730
+ Returns:
731
+ Formatted text output for session recording.
732
+ """
733
+ if repo_root is None:
734
+ repo_root = get_repo_root()
735
+
736
+ lines = _new_context_lines("SESSION CONTEXT (RECORD MODE)")
737
+ developer = get_developer(repo_root)
738
+ if not developer:
739
+ lines.append(
740
+ f"ERROR: Not initialized. Run: ./{DIR_WORKFLOW}/run init-developer <name>"
741
+ )
742
+ return "\n".join(lines)
743
+
744
+ _append_record_my_tasks(lines, developer, get_tasks_dir(repo_root))
745
+ _append_record_context_sections(lines, repo_root)
712
746
  lines.append("========================================")
713
747
 
714
748
  return "\n".join(lines)
@@ -0,0 +1 @@
1
+ """Shared host adapter runtime."""