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.
- package/README.md +27 -14
- package/package.json +1 -1
- package/src/commands/init.js +39 -28
- package/src/commands/sync.js +2 -2
- package/src/lib/asset-plan.js +127 -0
- package/src/lib/copy-template.js +101 -141
- package/src/lib/host-assets.js +231 -0
- package/src/lib/plan-applier.js +329 -0
- package/src/lib/platforms.js +15 -76
- package/src/lib/skill-registry.js +503 -0
- package/template/.claude/agents/cowork-check.md +10 -1
- package/template/.claude/agents/cowork-implement.md +11 -2
- package/template/.claude/hooks/inject-workflow-state.py +51 -254
- package/template/.codex/agents/cowork-check.toml +12 -3
- package/template/.codex/agents/cowork-implement.toml +11 -2
- package/template/.codex/agents/default.toml +3 -2
- package/template/.codex/agents/explorer.toml +3 -2
- package/template/.codex/agents/worker.toml +2 -1
- package/template/.codex/hooks/inject-workflow-state.py +46 -265
- package/template/.cowork-flow/.version +1 -1
- package/template/.cowork-flow/scripts/application/__init__.py +1 -0
- package/template/.cowork-flow/scripts/application/batch_execution.py +743 -0
- package/template/.cowork-flow/scripts/application/runtime_context_service.py +365 -0
- package/template/.cowork-flow/scripts/application/task_archive.py +220 -0
- package/template/.cowork-flow/scripts/application/task_context.py +765 -0
- package/template/.cowork-flow/scripts/application/task_creation.py +201 -0
- package/template/.cowork-flow/scripts/application/task_lifecycle.py +532 -0
- package/template/.cowork-flow/scripts/application/task_tree.py +179 -0
- package/template/.cowork-flow/scripts/commands/doctor.py +78 -57
- package/template/.cowork-flow/scripts/commands/party_mode_v2.py +671 -425
- package/template/.cowork-flow/scripts/commands/subagent.py +38 -26
- package/template/.cowork-flow/scripts/commands/task.py +535 -1953
- package/template/.cowork-flow/scripts/commands/task_archive_commands.py +195 -0
- package/template/.cowork-flow/scripts/commands/task_context_commands.py +240 -0
- package/template/.cowork-flow/scripts/commands/task_create_command.py +164 -0
- package/template/.cowork-flow/scripts/commands/task_navigation.py +407 -0
- package/template/.cowork-flow/scripts/commands/task_parser.py +364 -0
- package/template/.cowork-flow/scripts/commands/task_support.py +76 -0
- package/template/.cowork-flow/scripts/commands/task_tree_commands.py +160 -0
- package/template/.cowork-flow/scripts/common/core/execution_context.py +161 -85
- package/template/.cowork-flow/scripts/common/core/host_manifest.py +393 -0
- package/template/.cowork-flow/scripts/common/core/skill_registry.py +568 -0
- package/template/.cowork-flow/scripts/common/gates/coding_standards.py +58 -7
- package/template/.cowork-flow/scripts/common/gates/gates.py +323 -46
- package/template/.cowork-flow/scripts/common/gates/models.py +196 -0
- package/template/.cowork-flow/scripts/common/gates/registry.py +79 -0
- package/template/.cowork-flow/scripts/common/gates/tdd_evidence.py +13 -13
- package/template/.cowork-flow/scripts/common/gates/validate_coding_standards.py +104 -62
- package/template/.cowork-flow/scripts/common/gates/validate_implementation.py +39 -22
- package/template/.cowork-flow/scripts/common/gates/validate_rules.py +223 -40
- package/template/.cowork-flow/scripts/common/git/git_context.py +216 -182
- package/template/.cowork-flow/scripts/common/host/__init__.py +1 -0
- package/template/.cowork-flow/scripts/common/host/workflow_state_hook.py +303 -0
- package/template/.cowork-flow/scripts/common/storage/__init__.py +1 -0
- package/template/.cowork-flow/scripts/common/storage/operation_log.py +72 -0
- package/template/.cowork-flow/scripts/common/storage/state_store.py +279 -0
- package/template/.cowork-flow/scripts/common/storage/unit_of_work.py +214 -0
- package/template/.cowork-flow/scripts/common/task/active_task.py +93 -135
- package/template/.cowork-flow/scripts/common/task/batch_mode.py +33 -120
- package/template/.cowork-flow/scripts/common/task/decision_review.py +85 -0
- package/template/.cowork-flow/scripts/common/task/readiness.py +9 -15
- package/template/.cowork-flow/scripts/common/task/task_repository.py +156 -0
- package/template/.cowork-flow/scripts/run.py +24 -0
- package/template/.cowork-flow/spec/contracts/decision-anchor.md +8 -2
- package/template/.cowork-flow/spec/contracts/subagent-dispatch.md +2 -4
- package/template/.cowork-flow/spec/contracts/workflow-state-templates.md +1 -1
- package/template/.cowork-flow/spec/protocols/decision-review.md +28 -0
- package/template/.cowork-flow/spec/protocols/review.md +34 -0
- package/template/.cowork-flow/spec/protocols/spec-maintenance.md +16 -0
- package/template/.cowork-flow/spec/protocols/tdd.md +30 -0
- package/template/.cowork-flow/spec/references/definition-of-done.md +4 -4
- package/template/.cowork-flow/spec/references/orchestration-patterns.md +1 -1
- package/template/.cowork-flow/spec/runtime/contract-registry.json +25 -0
- package/template/.cowork-flow/spec/runtime/host-assets.json +214 -0
- package/template/.cowork-flow/spec/runtime/index.md +8 -0
- package/template/.cowork-flow/spec/runtime/rules.json +61 -10
- package/template/.cowork-flow/spec/runtime/skill-registry.json +397 -0
- package/template/.cowork-flow/spec/schemas/host-assets.schema.json +173 -0
- package/template/.cowork-flow/spec/schemas/rules.schema.json +10 -1
- package/template/.cowork-flow/spec/schemas/skill-registry.schema.json +202 -0
- package/template/.cowork-flow/workflow.md +18 -13
- package/template/.opencode/agents/cowork-check.md +11 -0
- package/template/.opencode/agents/cowork-implement.md +16 -1
- package/template/.zcode/.zcode-plugin/plugin.json +1 -1
- package/template/.zcode/hooks/inject-context.js +3 -85
- package/template/.zcode/hooks/inject-context.selfcheck.mjs +6 -6
- package/template/.zcode/scaffold/.cowork-flow/spec/contracts/decision-anchor.md +8 -2
- package/template/.zcode/scaffold/.cowork-flow/spec/contracts/subagent-dispatch.md +2 -4
- package/template/.zcode/scaffold/.cowork-flow/spec/runtime/rules.json +61 -10
- package/template/.zcode/scaffold/.cowork-flow/spec/schemas/rules.schema.json +10 -1
- package/template/.zcode/scaffold/.cowork-flow/workflow.md +18 -13
- package/template/.zcode/scaffold/AGENTS.md +18 -8
- package/template/AGENTS.md +1 -1
- package/template/skills/batch-mode/SKILL.md +140 -76
- package/template/skills/brainstorming/SKILL.md +2 -2
- package/template/skills/cowork-flow/SKILL.md +42 -0
- package/template/skills/party-mode/SHARED-BOUNDARIES.md +1 -1
- package/template/skills/party-mode/SKILL.md +48 -119
- package/template/.cowork-flow/scripts/common/gates/spec_validator.py +0 -53
- package/template/.cowork-flow/scripts/common/gates/validate_jsonl.py +0 -32
- package/template/skills/before-dev/SKILL.md +0 -41
- package/template/skills/check/SKILL.md +0 -53
- package/template/skills/check/simplification-guide.md +0 -47
- package/template/skills/continue/SKILL.md +0 -29
- package/template/skills/doubt-review/SKILL.md +0 -118
- package/template/skills/finish-work/SKILL.md +0 -32
- package/template/skills/party-mode-v2/SKILL.md +0 -75
- package/template/skills/start/SKILL.md +0 -78
- package/template/skills/tdd/SKILL.md +0 -74
- package/template/skills/update-spec/SKILL.md +0 -39
- 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
|
-
|
|
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,
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
318
|
-
if
|
|
319
|
-
lines.append(f"- Read active task
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
413
|
-
|
|
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
|
-
|
|
416
|
-
|
|
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
|
-
|
|
419
|
-
|
|
420
|
-
""
|
|
421
|
-
|
|
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
|
-
|
|
425
|
-
|
|
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
|
-
|
|
428
|
-
|
|
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
|
-
|
|
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":
|
|
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
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
538
|
-
|
|
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
|
-
|
|
573
|
-
|
|
574
|
-
lines.append("")
|
|
617
|
+
Args:
|
|
618
|
+
repo_root: Repository root path. Defaults to auto-detected.
|
|
575
619
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
if
|
|
580
|
-
|
|
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
|
-
|
|
591
|
-
|
|
592
|
-
lines
|
|
593
|
-
|
|
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":
|
|
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
|
|
650
|
-
"""
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
694
|
-
|
|
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."""
|