cc-devflow 2.4.6 → 4.1.0

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 (191) hide show
  1. package/.claude/CLAUDE.md +1065 -48
  2. package/.claude/agents/dev-implementer.md +195 -0
  3. package/.claude/commands/{flow-archive.md → flow/archive.md} +46 -11
  4. package/.claude/commands/flow/context.md +150 -0
  5. package/.claude/commands/flow/delta.md +245 -0
  6. package/.claude/commands/{flow-dev.md → flow/dev.md} +112 -11
  7. package/.claude/commands/flow/init.md +45 -0
  8. package/.claude/commands/flow/quality.md +159 -0
  9. package/.claude/commands/flow/spec.md +186 -0
  10. package/.claude/commands/flow/workspace.md +146 -0
  11. package/.claude/commands/{cancel-ralph.md → util/cancel-ralph.md} +1 -0
  12. package/.claude/config/quality-gates.yml +305 -0
  13. package/.claude/docs/guides/TEAM_MODE_GUIDE.md +313 -0
  14. package/.claude/docs/templates/DELTA_SPEC_TEMPLATE.md +91 -0
  15. package/.claude/docs/templates/DESIGN_DECISIONS_TEMPLATE.md +151 -0
  16. package/.claude/docs/templates/JOURNAL_TEMPLATE.md +75 -0
  17. package/.claude/docs/templates/_shared/CLAUDE.md +36 -0
  18. package/.claude/docs/templates/_shared/CONSTITUTION_CHECK.md +125 -0
  19. package/.claude/docs/templates/_shared/VALIDATION_CHECKLIST.md +187 -0
  20. package/.claude/docs/templates/_shared/YAML_FRONTMATTER.md +164 -0
  21. package/.claude/docs/templates/context/dev.jsonl.template +6 -0
  22. package/.claude/docs/templates/context/epic.jsonl.template +5 -0
  23. package/.claude/docs/templates/context/prd.jsonl.template +4 -0
  24. package/.claude/docs/templates/context/research.jsonl.template +4 -0
  25. package/.claude/docs/templates/context/review.jsonl.template +5 -0
  26. package/.claude/docs/templates/context/tech.jsonl.template +5 -0
  27. package/.claude/hooks/CLAUDE.md +342 -0
  28. package/.claude/hooks/inject-agent-context.ts +480 -0
  29. package/.claude/hooks/inject-skill-context.ts +359 -0
  30. package/.claude/hooks/ralph-loop.ts +931 -0
  31. package/.claude/hooks/task-completed-hook.ts +593 -0
  32. package/.claude/hooks/teammate-idle-hook.ts +690 -0
  33. package/.claude/hooks/types/team-types.d.ts +238 -0
  34. package/.claude/rules/devflow-conventions.md +82 -9
  35. package/.claude/scripts/archive-requirement.sh +44 -1
  36. package/.claude/scripts/common.sh +670 -3
  37. package/.claude/scripts/delta-parser.ts +527 -0
  38. package/.claude/scripts/detect-file-conflicts.sh +151 -0
  39. package/.claude/scripts/flow-context-add.sh +134 -0
  40. package/.claude/scripts/flow-context-init.sh +133 -0
  41. package/.claude/scripts/flow-context-validate.sh +144 -0
  42. package/.claude/scripts/flow-delta-apply.sh +297 -0
  43. package/.claude/scripts/flow-delta-archive.sh +71 -0
  44. package/.claude/scripts/flow-delta-create.sh +202 -0
  45. package/.claude/scripts/flow-delta-list.sh +142 -0
  46. package/.claude/scripts/flow-delta-status.sh +235 -0
  47. package/.claude/scripts/flow-quality-full.sh +184 -0
  48. package/.claude/scripts/flow-quality-quick.sh +64 -0
  49. package/.claude/scripts/flow-workspace-init.sh +117 -0
  50. package/.claude/scripts/flow-workspace-record.sh +164 -0
  51. package/.claude/scripts/flow-workspace-start.sh +88 -0
  52. package/.claude/scripts/get-workflow-status.sh +415 -0
  53. package/.claude/scripts/parse-task-dependencies.js +334 -0
  54. package/.claude/scripts/record-quality-error.sh +165 -0
  55. package/.claude/scripts/run-quality-gates.sh +242 -0
  56. package/.claude/scripts/team-dev-init.sh +319 -0
  57. package/.claude/scripts/team-state-recovery.sh +229 -0
  58. package/.claude/scripts/workflow-status.ts +433 -0
  59. package/.claude/settings.json +19 -0
  60. package/.claude/skills/cc-devflow-orchestrator/SKILL.md +85 -200
  61. package/.claude/skills/domain/using-git-worktrees/SKILL.md +252 -0
  62. package/.claude/skills/domain/using-git-worktrees/assets/SHELL_ALIASES.md +133 -0
  63. package/.claude/skills/domain/using-git-worktrees/context.jsonl +4 -0
  64. package/.claude/skills/domain/using-git-worktrees/scripts/worktree-cleanup.sh +218 -0
  65. package/.claude/skills/domain/using-git-worktrees/scripts/worktree-create.sh +232 -0
  66. package/.claude/skills/domain/using-git-worktrees/scripts/worktree-list.sh +130 -0
  67. package/.claude/skills/domain/using-git-worktrees/scripts/worktree-status.sh +140 -0
  68. package/.claude/skills/domain/using-git-worktrees/scripts/worktree-switch.sh +70 -0
  69. package/.claude/skills/skill-rules.json +72 -1
  70. package/.claude/skills/utility/journey-checker/SKILL.md +199 -0
  71. package/.claude/skills/utility/journey-checker/pressure-scenarios.md +164 -0
  72. package/.claude/skills/utility/skill-creator/LICENSE.txt +202 -0
  73. package/.claude/skills/utility/skill-creator/SKILL.md +356 -0
  74. package/.claude/skills/utility/skill-creator/references/output-patterns.md +82 -0
  75. package/.claude/skills/utility/skill-creator/references/workflows.md +28 -0
  76. package/.claude/skills/utility/skill-creator/scripts/init_skill.py +303 -0
  77. package/.claude/skills/utility/skill-creator/scripts/package_skill.py +110 -0
  78. package/.claude/skills/utility/skill-creator/scripts/quick_validate.py +95 -0
  79. package/.claude/skills/workflow/flow-dev/CLAUDE.md +78 -0
  80. package/.claude/skills/workflow/flow-dev/SKILL.md +96 -0
  81. package/.claude/skills/workflow/flow-dev/assets/IMPLEMENTATION_PLAN_TEMPLATE.md +71 -0
  82. package/.claude/skills/workflow/flow-dev/context.jsonl +8 -0
  83. package/.claude/skills/workflow/flow-dev/dev-implementer.jsonl +8 -0
  84. package/.claude/skills/workflow/flow-dev/scripts/entry-gate.sh +116 -0
  85. package/.claude/skills/workflow/flow-dev/scripts/exit-gate.sh +101 -0
  86. package/.claude/skills/workflow/flow-dev/scripts/task-orchestrator.sh +106 -0
  87. package/.claude/skills/workflow/flow-fix/SKILL.md +105 -0
  88. package/.claude/skills/workflow/flow-fix/context.jsonl +6 -0
  89. package/.claude/skills/workflow/flow-fix/references/bug-analyzer.md +381 -0
  90. package/.claude/skills/workflow/flow-init/SKILL.md +211 -0
  91. package/.claude/skills/workflow/flow-init/assets/BRAINSTORM_TEMPLATE.md +148 -0
  92. package/.claude/skills/workflow/flow-init/assets/INIT_FLOW_TEMPLATE.md +198 -0
  93. package/.claude/skills/workflow/flow-init/assets/RESEARCH_TEMPLATE.md +276 -0
  94. package/.claude/skills/workflow/flow-init/context.jsonl +5 -0
  95. package/.claude/skills/workflow/flow-init/references/flow-researcher.md +132 -0
  96. package/.claude/skills/workflow/flow-init/scripts/check-prerequisites.sh +232 -0
  97. package/.claude/skills/workflow/flow-init/scripts/consolidate-research.sh +182 -0
  98. package/.claude/skills/workflow/flow-init/scripts/create-requirement.sh +515 -0
  99. package/.claude/skills/workflow/flow-init/scripts/generate-research-tasks.sh +157 -0
  100. package/.claude/skills/workflow/flow-init/scripts/populate-research-tasks.sh +284 -0
  101. package/.claude/skills/workflow/flow-init/scripts/validate-research.sh +332 -0
  102. package/.claude/skills/workflow/flow-quality/SKILL.md +94 -0
  103. package/.claude/skills/workflow/flow-quality/context.jsonl +6 -0
  104. package/.claude/skills/workflow/flow-quality/references/code-quality-reviewer.md +205 -0
  105. package/.claude/skills/workflow/flow-quality/references/qa-tester.md +313 -0
  106. package/.claude/skills/workflow/flow-quality/references/security-reviewer.md +314 -0
  107. package/.claude/skills/workflow/flow-quality/references/spec-reviewer.md +221 -0
  108. package/.claude/skills/workflow/flow-release/SKILL.md +126 -0
  109. package/.claude/skills/workflow/flow-release/context.jsonl +7 -0
  110. package/.claude/skills/workflow/flow-release/references/release-manager.md +295 -0
  111. package/.claude/skills/workflow/flow-spec/CLAUDE.md +103 -0
  112. package/.claude/skills/workflow/flow-spec/SKILL.md +545 -0
  113. package/.claude/skills/workflow/flow-spec/context.jsonl +7 -0
  114. package/.claude/skills/workflow/flow-spec/scripts/entry-gate.sh +194 -0
  115. package/.claude/skills/workflow/flow-spec/scripts/exit-gate.sh +244 -0
  116. package/.claude/skills/workflow/flow-spec/scripts/parallel-orchestrator.sh +205 -0
  117. package/.claude/skills/workflow/flow-spec/scripts/team-communication.sh +353 -0
  118. package/.claude/skills/workflow/flow-spec/scripts/team-init.sh +195 -0
  119. package/.claude/skills/workflow/flow-spec/scripts/test-team-mode.sh +496 -0
  120. package/.claude/skills/workflow/flow-spec/team-config.json +165 -0
  121. package/.claude/skills/workflow.yaml +417 -0
  122. package/CHANGELOG.md +254 -0
  123. package/README.md +193 -33
  124. package/README.zh-CN.md +206 -46
  125. package/lib/compiler/CLAUDE.md +77 -46
  126. package/lib/compiler/__tests__/multi-module-emitters.test.js +508 -0
  127. package/lib/compiler/context-expander.js +179 -0
  128. package/lib/compiler/emitters/antigravity-emitter.js +195 -5
  129. package/lib/compiler/emitters/base-emitter.js +217 -2
  130. package/lib/compiler/emitters/codex-emitter.js +200 -4
  131. package/lib/compiler/emitters/cursor-emitter.js +307 -3
  132. package/lib/compiler/emitters/qwen-emitter.js +196 -4
  133. package/lib/compiler/index.js +197 -2
  134. package/lib/compiler/platforms.js +270 -21
  135. package/package.json +1 -1
  136. package/.claude/commands/flow-epic.md +0 -183
  137. package/.claude/commands/flow-init.md +0 -370
  138. package/.claude/commands/flow-prd.md +0 -144
  139. package/.claude/commands/flow-qa.md +0 -93
  140. package/.claude/commands/flow-review.md +0 -257
  141. package/.claude/commands/flow-tech.md +0 -142
  142. package/.claude/commands/flow-ui.md +0 -189
  143. package/.claude/skills/file-header-guardian/SKILL.md +0 -56
  144. package/.claude/skills/skill-developer/ADVANCED.md +0 -197
  145. package/.claude/skills/skill-developer/HOOK_MECHANISMS.md +0 -306
  146. package/.claude/skills/skill-developer/PATTERNS_LIBRARY.md +0 -152
  147. package/.claude/skills/skill-developer/SKILL.md +0 -426
  148. package/.claude/skills/skill-developer/SKILL_RULES_REFERENCE.md +0 -315
  149. package/.claude/skills/skill-developer/TRIGGER_TYPES.md +0 -305
  150. package/.claude/skills/skill-developer/TROUBLESHOOTING.md +0 -514
  151. package/.claude/skills/writing-skills/SKILL.md +0 -655
  152. package/.claude/skills/writing-skills/anthropic-best-practices.md +0 -1150
  153. package/.claude/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +0 -189
  154. package/.claude/skills/writing-skills/graphviz-conventions.dot +0 -172
  155. package/.claude/skills/writing-skills/persuasion-principles.md +0 -187
  156. package/.claude/skills/writing-skills/render-graphs.js +0 -168
  157. package/.claude/skills/writing-skills/testing-skills-with-subagents.md +0 -384
  158. package/.claude/tsc-cache/795ba6e3-b98a-423b-bab2-51aa62812569/affected-repos.txt +0 -1
  159. package/.claude/tsc-cache/ae335694-be5a-4ba4-a1a0-b676c09a7906/affected-repos.txt +0 -1
  160. /package/.claude/commands/{core-architecture.md → core/architecture.md} +0 -0
  161. /package/.claude/commands/{core-guidelines.md → core/guidelines.md} +0 -0
  162. /package/.claude/commands/{core-roadmap.md → core/roadmap.md} +0 -0
  163. /package/.claude/commands/{core-style.md → core/style.md} +0 -0
  164. /package/.claude/commands/{flow-checklist.md → flow/checklist.md} +0 -0
  165. /package/.claude/commands/{flow-clarify.md → flow/clarify.md} +0 -0
  166. /package/.claude/commands/{flow-constitution.md → flow/constitution.md} +0 -0
  167. /package/.claude/commands/{flow-fix.md → flow/fix.md} +0 -0
  168. /package/.claude/commands/{flow-ideate.md → flow/ideate.md} +0 -0
  169. /package/.claude/commands/{flow-new.md → flow/new.md} +0 -0
  170. /package/.claude/commands/{flow-release.md → flow/release.md} +0 -0
  171. /package/.claude/commands/{flow-restart.md → flow/restart.md} +0 -0
  172. /package/.claude/commands/{flow-status.md → flow/status.md} +0 -0
  173. /package/.claude/commands/{flow-update.md → flow/update.md} +0 -0
  174. /package/.claude/commands/{flow-upgrade.md → flow/upgrade.md} +0 -0
  175. /package/.claude/commands/{flow-verify.md → flow/verify.md} +0 -0
  176. /package/.claude/commands/{code-review-high.md → util/code-review.md} +0 -0
  177. /package/.claude/commands/{git-commit.md → util/git-commit.md} +0 -0
  178. /package/.claude/commands/{problem-analyzer.md → util/problem-analyzer.md} +0 -0
  179. /package/.claude/skills/{flow-attention-refresh → domain/attention-refresh}/SKILL.md +0 -0
  180. /package/.claude/skills/{flow-brainstorming → domain/brainstorming}/SKILL.md +0 -0
  181. /package/.claude/skills/{flow-debugging → domain/debugging}/SKILL.md +0 -0
  182. /package/.claude/skills/{flow-finishing-branch → domain/finishing-branch}/SKILL.md +0 -0
  183. /package/.claude/skills/{flow-receiving-review → domain/receiving-review}/SKILL.md +0 -0
  184. /package/.claude/skills/{flow-tdd → domain/tdd}/SKILL.md +0 -0
  185. /package/.claude/skills/{verification-before-completion → domain/verification}/SKILL.md +0 -0
  186. /package/.claude/skills/{constitution-guardian → guardrail/constitution-guardian}/SKILL.md +0 -0
  187. /package/.claude/skills/{devflow-tdd-enforcer → guardrail/tdd-enforcer}/SKILL.md +0 -0
  188. /package/.claude/skills/{devflow-constitution-quick-ref → utility/constitution-quick-ref}/SKILL.md +0 -0
  189. /package/.claude/skills/{devflow-file-standards → utility/file-standards}/SKILL.md +0 -0
  190. /package/.claude/skills/{fractal-docs-generator → utility/fractal-docs}/SKILL.md +0 -0
  191. /package/.claude/skills/{npm-release → utility/npm-release}/SKILL.md +0 -0
@@ -36,7 +36,7 @@ get_repo_root() {
36
36
  fi
37
37
  }
38
38
 
39
- # Get current requirement ID from branch or environment
39
+ # Get current requirement ID from worktree path, branch, or environment
40
40
  # Returns: REQ-XXX or BUG-XXX format
41
41
  get_current_req_id() {
42
42
  # First check if DEVFLOW_REQ_ID environment variable is set
@@ -45,8 +45,19 @@ get_current_req_id() {
45
45
  return
46
46
  fi
47
47
 
48
- # Then check git branch if available
49
- if git rev-parse --abbrev-ref HEAD >/dev/null 2>&1; then
48
+ # Check if in git repo
49
+ if git rev-parse --show-toplevel >/dev/null 2>&1; then
50
+ # Try to extract from worktree directory name first (e.g., cc-devflow-REQ-123)
51
+ local git_root
52
+ git_root=$(git rev-parse --show-toplevel)
53
+ local dir_name
54
+ dir_name=$(basename "$git_root")
55
+ if [[ "$dir_name" =~ -([A-Z]+-[0-9]+(-[0-9]+)?)$ ]]; then
56
+ echo "${BASH_REMATCH[1]}"
57
+ return
58
+ fi
59
+
60
+ # Then check git branch
50
61
  local branch=$(git rev-parse --abbrev-ref HEAD)
51
62
  # Extract REQ-XXX or BUG-XXX from branch name like feature/REQ-123-title or feature/REQ-20251006-001-title
52
63
  # Support formats: REQ-123, REQ-20251006-001, BUG-456, etc.
@@ -562,9 +573,665 @@ list_archived_reqs() {
562
573
  fi
563
574
  }
564
575
 
576
+ # Get archive summary for a requirement
577
+ # Args: $1 - repo root, $2 - requirement ID
578
+ # Returns: JSON summary of archived requirement
579
+ get_archive_summary() {
580
+ local repo_root="$1"
581
+ local req_id="$2"
582
+ local archived_path=$(find_archived_req "$repo_root" "$req_id")
583
+
584
+ if [[ -z "$archived_path" ]]; then
585
+ echo "{\"error\": \"not_found\"}"
586
+ return 1
587
+ fi
588
+
589
+ local status_file="$archived_path/orchestration_status.json"
590
+ if [[ -f "$status_file" ]]; then
591
+ jq '{
592
+ reqId: .reqId,
593
+ title: .title,
594
+ status: .status,
595
+ archivedReason: .archivedReason,
596
+ archivedAt: .archivedAt,
597
+ archiveLocation: .archiveLocation,
598
+ statusBeforeArchive: .statusBeforeArchive
599
+ }' "$status_file"
600
+ else
601
+ echo "{\"reqId\": \"$req_id\", \"archiveLocation\": \"$archived_path\"}"
602
+ fi
603
+ }
604
+
605
+ # Check if requirement has deltas to archive
606
+ # Args: $1 - requirement directory
607
+ # Returns: 0 if has deltas, 1 if not
608
+ has_deltas_to_archive() {
609
+ local req_dir="$1"
610
+ local deltas_dir="$req_dir/deltas"
611
+
612
+ if [[ -d "$deltas_dir" ]]; then
613
+ local count=$(find "$deltas_dir" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l)
614
+ [[ $count -gt 0 ]] && return 0
615
+ fi
616
+ return 1
617
+ }
618
+
619
+ # Get delta count for a requirement
620
+ # Args: $1 - requirement directory
621
+ # Returns: number of deltas
622
+ get_delta_count() {
623
+ local req_dir="$1"
624
+ local deltas_dir="$req_dir/deltas"
625
+
626
+ if [[ -d "$deltas_dir" ]]; then
627
+ find "$deltas_dir" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l | tr -d ' '
628
+ else
629
+ echo "0"
630
+ fi
631
+ }
632
+
633
+ # =============================================================================
634
+ # Git Worktree Functions (v4.3)
635
+ # =============================================================================
636
+
637
+ # Check if currently in a git worktree (not main repo)
638
+ # Returns: 0 if in worktree, 1 if in main repo or not in git
639
+ is_in_worktree() {
640
+ local git_dir
641
+ git_dir=$(git rev-parse --git-dir 2>/dev/null) || return 1
642
+
643
+ # If .git is a file (not directory), we're in a worktree
644
+ if [[ -f "$(git rev-parse --show-toplevel 2>/dev/null)/.git" ]]; then
645
+ return 0
646
+ fi
647
+ return 1
648
+ }
649
+
650
+ # Get the main repository path (works from any worktree)
651
+ # Returns: absolute path to main repository
652
+ get_main_repo_path() {
653
+ local git_root
654
+ git_root=$(git rev-parse --show-toplevel 2>/dev/null) || return 1
655
+
656
+ if is_in_worktree; then
657
+ # Read gitdir from .git file and extract main repo path
658
+ local gitdir_content
659
+ gitdir_content=$(cat "$git_root/.git" 2>/dev/null)
660
+ if [[ "$gitdir_content" =~ ^gitdir:\ (.+)$ ]]; then
661
+ local gitdir="${BASH_REMATCH[1]}"
662
+ # gitdir format: /path/to/main/.git/worktrees/name
663
+ echo "$gitdir" | sed 's|/.git/worktrees/.*||'
664
+ fi
665
+ else
666
+ echo "$git_root"
667
+ fi
668
+ }
669
+
670
+ # Get current worktree path
671
+ # Returns: absolute path to current worktree (or main repo if not in worktree)
672
+ get_worktree_path() {
673
+ git rev-parse --show-toplevel 2>/dev/null
674
+ }
675
+
676
+ # Get worktree directory for a specific REQ_ID
677
+ # Args: $1 - REQ_ID
678
+ # Returns: expected worktree path (may not exist)
679
+ get_worktree_dir_for_req() {
680
+ local req_id="$1"
681
+ local main_repo
682
+ main_repo=$(get_main_repo_path) || return 1
683
+
684
+ local repo_name
685
+ repo_name=$(basename "$main_repo")
686
+
687
+ echo "$(dirname "$main_repo")/${repo_name}-${req_id}"
688
+ }
689
+
690
+ # Check if a worktree exists for a specific REQ_ID
691
+ # Args: $1 - REQ_ID
692
+ # Returns: 0 if exists, 1 if not
693
+ worktree_exists_for_req() {
694
+ local req_id="$1"
695
+ local worktree_dir
696
+ worktree_dir=$(get_worktree_dir_for_req "$req_id")
697
+
698
+ [[ -d "$worktree_dir" ]]
699
+ }
700
+
701
+ # Extract REQ_ID from current worktree path or branch
702
+ # Returns: REQ_ID or empty string
703
+ get_req_id_from_worktree() {
704
+ local git_root
705
+ git_root=$(git rev-parse --show-toplevel 2>/dev/null) || return 1
706
+
707
+ # Try to extract from directory name first (e.g., cc-devflow-REQ-123)
708
+ local dir_name
709
+ dir_name=$(basename "$git_root")
710
+ if [[ "$dir_name" =~ -([A-Z]+-[0-9]+(-[0-9]+)?)$ ]]; then
711
+ echo "${BASH_REMATCH[1]}"
712
+ return 0
713
+ fi
714
+
715
+ # Fall back to branch name
716
+ local branch
717
+ branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
718
+ if [[ "$branch" =~ (REQ-[0-9]+(-[0-9]+)?|BUG-[0-9]+(-[0-9]+)?) ]]; then
719
+ echo "${BASH_REMATCH[1]}"
720
+ return 0
721
+ fi
722
+
723
+ echo ""
724
+ }
725
+
726
+ # List all worktrees with their REQ_IDs
727
+ # Output format: path|branch|req_id (one per line)
728
+ list_worktrees_with_req() {
729
+ local main_repo
730
+ main_repo=$(get_main_repo_path) || return 1
731
+
732
+ git -C "$main_repo" worktree list --porcelain | while IFS= read -r line; do
733
+ if [[ "$line" =~ ^worktree ]]; then
734
+ local path="${line#worktree }"
735
+ elif [[ "$line" =~ ^branch ]]; then
736
+ local branch="${line#branch refs/heads/}"
737
+ local req_id=""
738
+ local dir_name
739
+ dir_name=$(basename "$path")
740
+ if [[ "$dir_name" =~ -([A-Z]+-[0-9]+(-[0-9]+)?)$ ]]; then
741
+ req_id="${BASH_REMATCH[1]}"
742
+ elif [[ "$branch" =~ (REQ-[0-9]+(-[0-9]+)?|BUG-[0-9]+(-[0-9]+)?) ]]; then
743
+ req_id="${BASH_REMATCH[1]}"
744
+ fi
745
+ echo "${path}|${branch}|${req_id}"
746
+ elif [[ "$line" =~ ^detached ]]; then
747
+ local req_id=""
748
+ local dir_name
749
+ dir_name=$(basename "$path")
750
+ if [[ "$dir_name" =~ -([A-Z]+-[0-9]+(-[0-9]+)?)$ ]]; then
751
+ req_id="${BASH_REMATCH[1]}"
752
+ fi
753
+ echo "${path}|(detached)|${req_id}"
754
+ fi
755
+ done
756
+ }
757
+
565
758
  # Color output helpers
566
759
  color_red() { echo -e "\033[0;31m$1\033[0m"; }
567
760
  color_green() { echo -e "\033[0;32m$1\033[0m"; }
568
761
  color_yellow() { echo -e "\033[0;33m$1\033[0m"; }
569
762
  color_blue() { echo -e "\033[0;34m$1\033[0m"; }
570
763
  color_bold() { echo -e "\033[1m$1\033[0m"; }
764
+
765
+ # =============================================================================
766
+ # Claude Team Functions (v4.7)
767
+ # =============================================================================
768
+
769
+ # Check if Team mode is enabled for a requirement
770
+ # Args: $1 - repo root, $2 - requirement ID
771
+ # Returns: 0 if Team mode enabled, 1 if not
772
+ is_team_mode_enabled() {
773
+ local repo_root="$1"
774
+ local req_id="$2"
775
+ local status_file
776
+ status_file=$(get_req_dir "$repo_root" "$req_id")/orchestration_status.json
777
+
778
+ if [[ -f "$status_file" ]]; then
779
+ local team_mode
780
+ team_mode=$(jq -r '.team.mode // empty' "$status_file" 2>/dev/null)
781
+ [[ -n "$team_mode" ]] && return 0
782
+ fi
783
+ return 1
784
+ }
785
+
786
+ # Initialize Team state in orchestration_status.json
787
+ # Args: $1 - repo root, $2 - requirement ID, $3 - team mode (sequential|parallel), $4 - lead ID
788
+ init_team_state() {
789
+ local repo_root="$1"
790
+ local req_id="$2"
791
+ local mode="${3:-parallel}"
792
+ local lead="${4:-team-lead}"
793
+ local status_file
794
+ status_file=$(get_req_dir "$repo_root" "$req_id")/orchestration_status.json
795
+
796
+ if [[ ! -f "$status_file" ]]; then
797
+ echo "ERROR: orchestration_status.json not found" >&2
798
+ return 1
799
+ fi
800
+
801
+ local now
802
+ now=$(get_beijing_time_iso)
803
+
804
+ # Add team state using jq
805
+ jq --arg mode "$mode" --arg lead "$lead" --arg now "$now" '
806
+ .team = {
807
+ mode: $mode,
808
+ lead: $lead,
809
+ teammates: [],
810
+ taskAssignments: {},
811
+ createdAt: $now,
812
+ updatedAt: $now
813
+ } |
814
+ .ralphLoop = {
815
+ enabled: true,
816
+ teammates: {},
817
+ globalIteration: 0,
818
+ maxIterations: 10,
819
+ startedAt: $now
820
+ }
821
+ ' "$status_file" > "${status_file}.tmp" && mv "${status_file}.tmp" "$status_file"
822
+ }
823
+
824
+ # Add a teammate to Team state
825
+ # Args: $1 - repo root, $2 - requirement ID, $3 - teammate ID, $4 - role
826
+ add_teammate() {
827
+ local repo_root="$1"
828
+ local req_id="$2"
829
+ local teammate_id="$3"
830
+ local role="${4:-developer}"
831
+ local status_file
832
+ status_file=$(get_req_dir "$repo_root" "$req_id")/orchestration_status.json
833
+
834
+ if [[ ! -f "$status_file" ]]; then
835
+ echo "ERROR: orchestration_status.json not found" >&2
836
+ return 1
837
+ fi
838
+
839
+ local now
840
+ now=$(get_beijing_time_iso)
841
+
842
+ jq --arg id "$teammate_id" --arg role "$role" --arg now "$now" '
843
+ .team.teammates += [{
844
+ id: $id,
845
+ role: $role,
846
+ status: "idle",
847
+ currentTask: null,
848
+ completedTasks: [],
849
+ lastActiveAt: $now
850
+ }] |
851
+ .team.updatedAt = $now |
852
+ .ralphLoop.teammates[$id] = {
853
+ iteration: 0,
854
+ lastVerifyResult: "skipped"
855
+ }
856
+ ' "$status_file" > "${status_file}.tmp" && mv "${status_file}.tmp" "$status_file"
857
+ }
858
+
859
+ # Update teammate status
860
+ # Args: $1 - repo root, $2 - requirement ID, $3 - teammate ID, $4 - status, $5 - current task (optional)
861
+ update_teammate_status() {
862
+ local repo_root="$1"
863
+ local req_id="$2"
864
+ local teammate_id="$3"
865
+ local status="$4"
866
+ local current_task="${5:-null}"
867
+ local status_file
868
+ status_file=$(get_req_dir "$repo_root" "$req_id")/orchestration_status.json
869
+
870
+ if [[ ! -f "$status_file" ]]; then
871
+ echo "ERROR: orchestration_status.json not found" >&2
872
+ return 1
873
+ fi
874
+
875
+ local now
876
+ now=$(get_beijing_time_iso)
877
+
878
+ # Handle null vs string for current_task
879
+ if [[ "$current_task" == "null" ]]; then
880
+ jq --arg id "$teammate_id" --arg status "$status" --arg now "$now" '
881
+ (.team.teammates[] | select(.id == $id)) |= (
882
+ .status = $status |
883
+ .currentTask = null |
884
+ .lastActiveAt = $now
885
+ ) |
886
+ .team.updatedAt = $now
887
+ ' "$status_file" > "${status_file}.tmp" && mv "${status_file}.tmp" "$status_file"
888
+ else
889
+ jq --arg id "$teammate_id" --arg status "$status" --arg task "$current_task" --arg now "$now" '
890
+ (.team.teammates[] | select(.id == $id)) |= (
891
+ .status = $status |
892
+ .currentTask = $task |
893
+ .lastActiveAt = $now
894
+ ) |
895
+ .team.updatedAt = $now
896
+ ' "$status_file" > "${status_file}.tmp" && mv "${status_file}.tmp" "$status_file"
897
+ fi
898
+ }
899
+
900
+ # Mark task as completed by teammate
901
+ # Args: $1 - repo root, $2 - requirement ID, $3 - teammate ID, $4 - task ID
902
+ mark_teammate_task_complete() {
903
+ local repo_root="$1"
904
+ local req_id="$2"
905
+ local teammate_id="$3"
906
+ local task_id="$4"
907
+ local status_file
908
+ status_file=$(get_req_dir "$repo_root" "$req_id")/orchestration_status.json
909
+
910
+ if [[ ! -f "$status_file" ]]; then
911
+ echo "ERROR: orchestration_status.json not found" >&2
912
+ return 1
913
+ fi
914
+
915
+ local now
916
+ now=$(get_beijing_time_iso)
917
+
918
+ jq --arg id "$teammate_id" --arg task "$task_id" --arg now "$now" '
919
+ (.team.teammates[] | select(.id == $id)) |= (
920
+ .completedTasks += [$task] |
921
+ .currentTask = null |
922
+ .status = "idle" |
923
+ .lastActiveAt = $now
924
+ ) |
925
+ .team.updatedAt = $now
926
+ ' "$status_file" > "${status_file}.tmp" && mv "${status_file}.tmp" "$status_file"
927
+ }
928
+
929
+ # Assign task to teammate
930
+ # Args: $1 - repo root, $2 - requirement ID, $3 - task ID, $4 - teammate ID
931
+ assign_task_to_teammate() {
932
+ local repo_root="$1"
933
+ local req_id="$2"
934
+ local task_id="$3"
935
+ local teammate_id="$4"
936
+ local status_file
937
+ status_file=$(get_req_dir "$repo_root" "$req_id")/orchestration_status.json
938
+
939
+ if [[ ! -f "$status_file" ]]; then
940
+ echo "ERROR: orchestration_status.json not found" >&2
941
+ return 1
942
+ fi
943
+
944
+ local now
945
+ now=$(get_beijing_time_iso)
946
+
947
+ jq --arg task "$task_id" --arg id "$teammate_id" --arg now "$now" '
948
+ .team.taskAssignments[$task] = $id |
949
+ (.team.teammates[] | select(.id == $id)) |= (
950
+ .currentTask = $task |
951
+ .status = "working" |
952
+ .lastActiveAt = $now
953
+ ) |
954
+ .team.updatedAt = $now
955
+ ' "$status_file" > "${status_file}.tmp" && mv "${status_file}.tmp" "$status_file"
956
+ }
957
+
958
+ # Get teammate by ID
959
+ # Args: $1 - repo root, $2 - requirement ID, $3 - teammate ID
960
+ # Returns: JSON object of teammate state
961
+ get_teammate() {
962
+ local repo_root="$1"
963
+ local req_id="$2"
964
+ local teammate_id="$3"
965
+ local status_file
966
+ status_file=$(get_req_dir "$repo_root" "$req_id")/orchestration_status.json
967
+
968
+ if [[ ! -f "$status_file" ]]; then
969
+ echo "{}"
970
+ return 1
971
+ fi
972
+
973
+ jq --arg id "$teammate_id" '.team.teammates[] | select(.id == $id)' "$status_file" 2>/dev/null
974
+ }
975
+
976
+ # List all teammates
977
+ # Args: $1 - repo root, $2 - requirement ID
978
+ # Returns: JSON array of teammates
979
+ list_teammates() {
980
+ local repo_root="$1"
981
+ local req_id="$2"
982
+ local status_file
983
+ status_file=$(get_req_dir "$repo_root" "$req_id")/orchestration_status.json
984
+
985
+ if [[ ! -f "$status_file" ]]; then
986
+ echo "[]"
987
+ return 1
988
+ fi
989
+
990
+ jq '.team.teammates // []' "$status_file" 2>/dev/null
991
+ }
992
+
993
+ # Get unassigned tasks (tasks not in taskAssignments)
994
+ # Args: $1 - repo root, $2 - requirement ID
995
+ # Returns: List of unassigned task IDs
996
+ get_unassigned_tasks() {
997
+ local repo_root="$1"
998
+ local req_id="$2"
999
+ local req_dir
1000
+ req_dir=$(get_req_dir "$repo_root" "$req_id")
1001
+ local tasks_file="$req_dir/TASKS.md"
1002
+ local status_file="$req_dir/orchestration_status.json"
1003
+
1004
+ if [[ ! -f "$tasks_file" ]] || [[ ! -f "$status_file" ]]; then
1005
+ echo ""
1006
+ return 1
1007
+ fi
1008
+
1009
+ # Extract task IDs from TASKS.md (format: - [ ] **T001** ...)
1010
+ local all_tasks
1011
+ all_tasks=$(grep -oE '\*\*T[0-9]+\*\*' "$tasks_file" | sed 's/\*\*//g' | sort -u)
1012
+
1013
+ # Get assigned tasks from status file
1014
+ local assigned_tasks
1015
+ assigned_tasks=$(jq -r '.team.taskAssignments | keys[]' "$status_file" 2>/dev/null)
1016
+
1017
+ # Find unassigned tasks
1018
+ for task in $all_tasks; do
1019
+ if ! echo "$assigned_tasks" | grep -q "^${task}$"; then
1020
+ echo "$task"
1021
+ fi
1022
+ done
1023
+ }
1024
+
1025
+ # Update Ralph Loop state for teammate
1026
+ # Args: $1 - repo root, $2 - requirement ID, $3 - teammate ID, $4 - verify result (passed|failed)
1027
+ update_teammate_ralph_state() {
1028
+ local repo_root="$1"
1029
+ local req_id="$2"
1030
+ local teammate_id="$3"
1031
+ local verify_result="$4"
1032
+ local status_file
1033
+ status_file=$(get_req_dir "$repo_root" "$req_id")/orchestration_status.json
1034
+
1035
+ if [[ ! -f "$status_file" ]]; then
1036
+ echo "ERROR: orchestration_status.json not found" >&2
1037
+ return 1
1038
+ fi
1039
+
1040
+ local now
1041
+ now=$(get_beijing_time_iso)
1042
+
1043
+ jq --arg id "$teammate_id" --arg result "$verify_result" --arg now "$now" '
1044
+ .ralphLoop.teammates[$id].iteration += 1 |
1045
+ .ralphLoop.teammates[$id].lastVerifyResult = $result |
1046
+ .ralphLoop.teammates[$id].lastVerifyAt = $now |
1047
+ .ralphLoop.globalIteration += 1
1048
+ ' "$status_file" > "${status_file}.tmp" && mv "${status_file}.tmp" "$status_file"
1049
+ }
1050
+
1051
+ # Check if all teammates are idle or completed
1052
+ # Args: $1 - repo root, $2 - requirement ID
1053
+ # Returns: 0 if all idle/completed, 1 if any working
1054
+ all_teammates_idle() {
1055
+ local repo_root="$1"
1056
+ local req_id="$2"
1057
+ local status_file
1058
+ status_file=$(get_req_dir "$repo_root" "$req_id")/orchestration_status.json
1059
+
1060
+ if [[ ! -f "$status_file" ]]; then
1061
+ return 1
1062
+ fi
1063
+
1064
+ local working_count
1065
+ working_count=$(jq '[.team.teammates[] | select(.status == "working")] | length' "$status_file" 2>/dev/null)
1066
+
1067
+ [[ "$working_count" == "0" ]] && return 0
1068
+ return 1
1069
+ }
1070
+
1071
+ # Clean up Team state
1072
+ # Args: $1 - repo root, $2 - requirement ID
1073
+ cleanup_team_state() {
1074
+ local repo_root="$1"
1075
+ local req_id="$2"
1076
+ local status_file
1077
+ status_file=$(get_req_dir "$repo_root" "$req_id")/orchestration_status.json
1078
+
1079
+ if [[ ! -f "$status_file" ]]; then
1080
+ return 0
1081
+ fi
1082
+
1083
+ local now
1084
+ now=$(get_beijing_time_iso)
1085
+
1086
+ jq --arg now "$now" '
1087
+ del(.team) |
1088
+ del(.ralphLoop) |
1089
+ .updatedAt = $now
1090
+ ' "$status_file" > "${status_file}.tmp" && mv "${status_file}.tmp" "$status_file"
1091
+ }
1092
+
1093
+ # =============================================================================
1094
+ # Team Monitoring Functions (v4.7)
1095
+ # =============================================================================
1096
+
1097
+ # Get Team status summary
1098
+ # Args: $1 - repo root, $2 - requirement ID
1099
+ # Returns: JSON summary of Team status
1100
+ get_team_status_summary() {
1101
+ local repo_root="$1"
1102
+ local req_id="$2"
1103
+ local status_file
1104
+ status_file=$(get_req_dir "$repo_root" "$req_id")/orchestration_status.json
1105
+
1106
+ if [[ ! -f "$status_file" ]]; then
1107
+ echo '{"error": "Status file not found"}'
1108
+ return 1
1109
+ fi
1110
+
1111
+ jq '{
1112
+ mode: .team.mode,
1113
+ lead: .team.lead,
1114
+ teammateCount: (.team.teammates | length),
1115
+ workingCount: ([.team.teammates[] | select(.status == "working")] | length),
1116
+ idleCount: ([.team.teammates[] | select(.status == "idle")] | length),
1117
+ completedTaskCount: ([.team.teammates[].completedTasks | length] | add),
1118
+ assignedTaskCount: (.team.taskAssignments | keys | length),
1119
+ ralphLoop: {
1120
+ globalIteration: .ralphLoop.globalIteration,
1121
+ maxIterations: .ralphLoop.maxIterations
1122
+ },
1123
+ lastUpdated: .team.updatedAt
1124
+ }' "$status_file" 2>/dev/null
1125
+ }
1126
+
1127
+ # Check for timed out teammates
1128
+ # Args: $1 - repo root, $2 - requirement ID, $3 - timeout seconds (default 300)
1129
+ # Returns: JSON array of timed out teammates
1130
+ get_timed_out_teammates() {
1131
+ local repo_root="$1"
1132
+ local req_id="$2"
1133
+ local timeout_seconds="${3:-300}"
1134
+ local status_file
1135
+ status_file=$(get_req_dir "$repo_root" "$req_id")/orchestration_status.json
1136
+
1137
+ if [[ ! -f "$status_file" ]]; then
1138
+ echo '[]'
1139
+ return 1
1140
+ fi
1141
+
1142
+ local now_epoch
1143
+ now_epoch=$(date +%s)
1144
+
1145
+ jq --argjson now "$now_epoch" --argjson timeout "$timeout_seconds" '
1146
+ [.team.teammates[] |
1147
+ select(.status == "working") |
1148
+ select(
1149
+ ($now - ((.lastActiveAt | sub("\\.[0-9]+"; "") | sub("Z$"; "+00:00") | fromdateiso8601) // 0)) > $timeout
1150
+ )
1151
+ ]
1152
+ ' "$status_file" 2>/dev/null
1153
+ }
1154
+
1155
+ # Log Team event to EXECUTION_LOG.md
1156
+ # Args: $1 - repo root, $2 - requirement ID, $3 - event type, $4 - message
1157
+ log_team_event() {
1158
+ local repo_root="$1"
1159
+ local req_id="$2"
1160
+ local event_type="$3"
1161
+ local message="$4"
1162
+ local log_file
1163
+ log_file=$(get_req_dir "$repo_root" "$req_id")/EXECUTION_LOG.md
1164
+
1165
+ local timestamp
1166
+ timestamp=$(get_beijing_time_iso)
1167
+
1168
+ local icon
1169
+ case "$event_type" in
1170
+ info) icon="ℹ️" ;;
1171
+ warning) icon="⚠️" ;;
1172
+ error) icon="❌" ;;
1173
+ success) icon="✅" ;;
1174
+ *) icon="📝" ;;
1175
+ esac
1176
+
1177
+ local entry="
1178
+ ## [$timestamp] $icon Team Event: $event_type
1179
+
1180
+ $message
1181
+
1182
+ ---
1183
+ "
1184
+
1185
+ if [[ -f "$log_file" ]]; then
1186
+ echo "$entry" >> "$log_file"
1187
+ else
1188
+ echo "# Execution Log
1189
+
1190
+ $entry" > "$log_file"
1191
+ fi
1192
+ }
1193
+
1194
+ # Check Team health and log warnings
1195
+ # Args: $1 - repo root, $2 - requirement ID
1196
+ # Returns: 0 if healthy, 1 if issues found
1197
+ check_team_health() {
1198
+ local repo_root="$1"
1199
+ local req_id="$2"
1200
+ local status_file
1201
+ status_file=$(get_req_dir "$repo_root" "$req_id")/orchestration_status.json
1202
+
1203
+ if [[ ! -f "$status_file" ]]; then
1204
+ return 1
1205
+ fi
1206
+
1207
+ local issues=0
1208
+
1209
+ # Check for timed out teammates
1210
+ local timed_out
1211
+ timed_out=$(get_timed_out_teammates "$repo_root" "$req_id")
1212
+ local timed_out_count
1213
+ timed_out_count=$(echo "$timed_out" | jq 'length')
1214
+
1215
+ if [[ "$timed_out_count" -gt 0 ]]; then
1216
+ local teammate_ids
1217
+ teammate_ids=$(echo "$timed_out" | jq -r '.[].id' | tr '\n' ', ' | sed 's/,$//')
1218
+ log_team_event "$repo_root" "$req_id" "warning" "Timed out teammates: $teammate_ids"
1219
+ issues=$((issues + 1))
1220
+ fi
1221
+
1222
+ # Check Ralph Loop iteration limit
1223
+ local global_iter max_iter
1224
+ global_iter=$(jq '.ralphLoop.globalIteration // 0' "$status_file")
1225
+ max_iter=$(jq '.ralphLoop.maxIterations // 10' "$status_file")
1226
+
1227
+ if [[ "$global_iter" -ge "$max_iter" ]]; then
1228
+ log_team_event "$repo_root" "$req_id" "error" "Ralph Loop reached max iterations ($global_iter/$max_iter)"
1229
+ issues=$((issues + 1))
1230
+ elif [[ "$global_iter" -ge $((max_iter * 80 / 100)) ]]; then
1231
+ log_team_event "$repo_root" "$req_id" "warning" "Ralph Loop approaching limit ($global_iter/$max_iter)"
1232
+ fi
1233
+
1234
+ [[ "$issues" -eq 0 ]] && return 0
1235
+ return 1
1236
+ }
1237
+