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.
- package/.claude/CLAUDE.md +1065 -48
- package/.claude/agents/dev-implementer.md +195 -0
- package/.claude/commands/{flow-archive.md → flow/archive.md} +46 -11
- package/.claude/commands/flow/context.md +150 -0
- package/.claude/commands/flow/delta.md +245 -0
- package/.claude/commands/{flow-dev.md → flow/dev.md} +112 -11
- package/.claude/commands/flow/init.md +45 -0
- package/.claude/commands/flow/quality.md +159 -0
- package/.claude/commands/flow/spec.md +186 -0
- package/.claude/commands/flow/workspace.md +146 -0
- package/.claude/commands/{cancel-ralph.md → util/cancel-ralph.md} +1 -0
- package/.claude/config/quality-gates.yml +305 -0
- package/.claude/docs/guides/TEAM_MODE_GUIDE.md +313 -0
- package/.claude/docs/templates/DELTA_SPEC_TEMPLATE.md +91 -0
- package/.claude/docs/templates/DESIGN_DECISIONS_TEMPLATE.md +151 -0
- package/.claude/docs/templates/JOURNAL_TEMPLATE.md +75 -0
- package/.claude/docs/templates/_shared/CLAUDE.md +36 -0
- package/.claude/docs/templates/_shared/CONSTITUTION_CHECK.md +125 -0
- package/.claude/docs/templates/_shared/VALIDATION_CHECKLIST.md +187 -0
- package/.claude/docs/templates/_shared/YAML_FRONTMATTER.md +164 -0
- package/.claude/docs/templates/context/dev.jsonl.template +6 -0
- package/.claude/docs/templates/context/epic.jsonl.template +5 -0
- package/.claude/docs/templates/context/prd.jsonl.template +4 -0
- package/.claude/docs/templates/context/research.jsonl.template +4 -0
- package/.claude/docs/templates/context/review.jsonl.template +5 -0
- package/.claude/docs/templates/context/tech.jsonl.template +5 -0
- package/.claude/hooks/CLAUDE.md +342 -0
- package/.claude/hooks/inject-agent-context.ts +480 -0
- package/.claude/hooks/inject-skill-context.ts +359 -0
- package/.claude/hooks/ralph-loop.ts +931 -0
- package/.claude/hooks/task-completed-hook.ts +593 -0
- package/.claude/hooks/teammate-idle-hook.ts +690 -0
- package/.claude/hooks/types/team-types.d.ts +238 -0
- package/.claude/rules/devflow-conventions.md +82 -9
- package/.claude/scripts/archive-requirement.sh +44 -1
- package/.claude/scripts/common.sh +670 -3
- package/.claude/scripts/delta-parser.ts +527 -0
- package/.claude/scripts/detect-file-conflicts.sh +151 -0
- package/.claude/scripts/flow-context-add.sh +134 -0
- package/.claude/scripts/flow-context-init.sh +133 -0
- package/.claude/scripts/flow-context-validate.sh +144 -0
- package/.claude/scripts/flow-delta-apply.sh +297 -0
- package/.claude/scripts/flow-delta-archive.sh +71 -0
- package/.claude/scripts/flow-delta-create.sh +202 -0
- package/.claude/scripts/flow-delta-list.sh +142 -0
- package/.claude/scripts/flow-delta-status.sh +235 -0
- package/.claude/scripts/flow-quality-full.sh +184 -0
- package/.claude/scripts/flow-quality-quick.sh +64 -0
- package/.claude/scripts/flow-workspace-init.sh +117 -0
- package/.claude/scripts/flow-workspace-record.sh +164 -0
- package/.claude/scripts/flow-workspace-start.sh +88 -0
- package/.claude/scripts/get-workflow-status.sh +415 -0
- package/.claude/scripts/parse-task-dependencies.js +334 -0
- package/.claude/scripts/record-quality-error.sh +165 -0
- package/.claude/scripts/run-quality-gates.sh +242 -0
- package/.claude/scripts/team-dev-init.sh +319 -0
- package/.claude/scripts/team-state-recovery.sh +229 -0
- package/.claude/scripts/workflow-status.ts +433 -0
- package/.claude/settings.json +19 -0
- package/.claude/skills/cc-devflow-orchestrator/SKILL.md +85 -200
- package/.claude/skills/domain/using-git-worktrees/SKILL.md +252 -0
- package/.claude/skills/domain/using-git-worktrees/assets/SHELL_ALIASES.md +133 -0
- package/.claude/skills/domain/using-git-worktrees/context.jsonl +4 -0
- package/.claude/skills/domain/using-git-worktrees/scripts/worktree-cleanup.sh +218 -0
- package/.claude/skills/domain/using-git-worktrees/scripts/worktree-create.sh +232 -0
- package/.claude/skills/domain/using-git-worktrees/scripts/worktree-list.sh +130 -0
- package/.claude/skills/domain/using-git-worktrees/scripts/worktree-status.sh +140 -0
- package/.claude/skills/domain/using-git-worktrees/scripts/worktree-switch.sh +70 -0
- package/.claude/skills/skill-rules.json +72 -1
- package/.claude/skills/utility/journey-checker/SKILL.md +199 -0
- package/.claude/skills/utility/journey-checker/pressure-scenarios.md +164 -0
- package/.claude/skills/utility/skill-creator/LICENSE.txt +202 -0
- package/.claude/skills/utility/skill-creator/SKILL.md +356 -0
- package/.claude/skills/utility/skill-creator/references/output-patterns.md +82 -0
- package/.claude/skills/utility/skill-creator/references/workflows.md +28 -0
- package/.claude/skills/utility/skill-creator/scripts/init_skill.py +303 -0
- package/.claude/skills/utility/skill-creator/scripts/package_skill.py +110 -0
- package/.claude/skills/utility/skill-creator/scripts/quick_validate.py +95 -0
- package/.claude/skills/workflow/flow-dev/CLAUDE.md +78 -0
- package/.claude/skills/workflow/flow-dev/SKILL.md +96 -0
- package/.claude/skills/workflow/flow-dev/assets/IMPLEMENTATION_PLAN_TEMPLATE.md +71 -0
- package/.claude/skills/workflow/flow-dev/context.jsonl +8 -0
- package/.claude/skills/workflow/flow-dev/dev-implementer.jsonl +8 -0
- package/.claude/skills/workflow/flow-dev/scripts/entry-gate.sh +116 -0
- package/.claude/skills/workflow/flow-dev/scripts/exit-gate.sh +101 -0
- package/.claude/skills/workflow/flow-dev/scripts/task-orchestrator.sh +106 -0
- package/.claude/skills/workflow/flow-fix/SKILL.md +105 -0
- package/.claude/skills/workflow/flow-fix/context.jsonl +6 -0
- package/.claude/skills/workflow/flow-fix/references/bug-analyzer.md +381 -0
- package/.claude/skills/workflow/flow-init/SKILL.md +211 -0
- package/.claude/skills/workflow/flow-init/assets/BRAINSTORM_TEMPLATE.md +148 -0
- package/.claude/skills/workflow/flow-init/assets/INIT_FLOW_TEMPLATE.md +198 -0
- package/.claude/skills/workflow/flow-init/assets/RESEARCH_TEMPLATE.md +276 -0
- package/.claude/skills/workflow/flow-init/context.jsonl +5 -0
- package/.claude/skills/workflow/flow-init/references/flow-researcher.md +132 -0
- package/.claude/skills/workflow/flow-init/scripts/check-prerequisites.sh +232 -0
- package/.claude/skills/workflow/flow-init/scripts/consolidate-research.sh +182 -0
- package/.claude/skills/workflow/flow-init/scripts/create-requirement.sh +515 -0
- package/.claude/skills/workflow/flow-init/scripts/generate-research-tasks.sh +157 -0
- package/.claude/skills/workflow/flow-init/scripts/populate-research-tasks.sh +284 -0
- package/.claude/skills/workflow/flow-init/scripts/validate-research.sh +332 -0
- package/.claude/skills/workflow/flow-quality/SKILL.md +94 -0
- package/.claude/skills/workflow/flow-quality/context.jsonl +6 -0
- package/.claude/skills/workflow/flow-quality/references/code-quality-reviewer.md +205 -0
- package/.claude/skills/workflow/flow-quality/references/qa-tester.md +313 -0
- package/.claude/skills/workflow/flow-quality/references/security-reviewer.md +314 -0
- package/.claude/skills/workflow/flow-quality/references/spec-reviewer.md +221 -0
- package/.claude/skills/workflow/flow-release/SKILL.md +126 -0
- package/.claude/skills/workflow/flow-release/context.jsonl +7 -0
- package/.claude/skills/workflow/flow-release/references/release-manager.md +295 -0
- package/.claude/skills/workflow/flow-spec/CLAUDE.md +103 -0
- package/.claude/skills/workflow/flow-spec/SKILL.md +545 -0
- package/.claude/skills/workflow/flow-spec/context.jsonl +7 -0
- package/.claude/skills/workflow/flow-spec/scripts/entry-gate.sh +194 -0
- package/.claude/skills/workflow/flow-spec/scripts/exit-gate.sh +244 -0
- package/.claude/skills/workflow/flow-spec/scripts/parallel-orchestrator.sh +205 -0
- package/.claude/skills/workflow/flow-spec/scripts/team-communication.sh +353 -0
- package/.claude/skills/workflow/flow-spec/scripts/team-init.sh +195 -0
- package/.claude/skills/workflow/flow-spec/scripts/test-team-mode.sh +496 -0
- package/.claude/skills/workflow/flow-spec/team-config.json +165 -0
- package/.claude/skills/workflow.yaml +417 -0
- package/CHANGELOG.md +254 -0
- package/README.md +193 -33
- package/README.zh-CN.md +206 -46
- package/lib/compiler/CLAUDE.md +77 -46
- package/lib/compiler/__tests__/multi-module-emitters.test.js +508 -0
- package/lib/compiler/context-expander.js +179 -0
- package/lib/compiler/emitters/antigravity-emitter.js +195 -5
- package/lib/compiler/emitters/base-emitter.js +217 -2
- package/lib/compiler/emitters/codex-emitter.js +200 -4
- package/lib/compiler/emitters/cursor-emitter.js +307 -3
- package/lib/compiler/emitters/qwen-emitter.js +196 -4
- package/lib/compiler/index.js +197 -2
- package/lib/compiler/platforms.js +270 -21
- package/package.json +1 -1
- package/.claude/commands/flow-epic.md +0 -183
- package/.claude/commands/flow-init.md +0 -370
- package/.claude/commands/flow-prd.md +0 -144
- package/.claude/commands/flow-qa.md +0 -93
- package/.claude/commands/flow-review.md +0 -257
- package/.claude/commands/flow-tech.md +0 -142
- package/.claude/commands/flow-ui.md +0 -189
- package/.claude/skills/file-header-guardian/SKILL.md +0 -56
- package/.claude/skills/skill-developer/ADVANCED.md +0 -197
- package/.claude/skills/skill-developer/HOOK_MECHANISMS.md +0 -306
- package/.claude/skills/skill-developer/PATTERNS_LIBRARY.md +0 -152
- package/.claude/skills/skill-developer/SKILL.md +0 -426
- package/.claude/skills/skill-developer/SKILL_RULES_REFERENCE.md +0 -315
- package/.claude/skills/skill-developer/TRIGGER_TYPES.md +0 -305
- package/.claude/skills/skill-developer/TROUBLESHOOTING.md +0 -514
- package/.claude/skills/writing-skills/SKILL.md +0 -655
- package/.claude/skills/writing-skills/anthropic-best-practices.md +0 -1150
- package/.claude/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +0 -189
- package/.claude/skills/writing-skills/graphviz-conventions.dot +0 -172
- package/.claude/skills/writing-skills/persuasion-principles.md +0 -187
- package/.claude/skills/writing-skills/render-graphs.js +0 -168
- package/.claude/skills/writing-skills/testing-skills-with-subagents.md +0 -384
- package/.claude/tsc-cache/795ba6e3-b98a-423b-bab2-51aa62812569/affected-repos.txt +0 -1
- package/.claude/tsc-cache/ae335694-be5a-4ba4-a1a0-b676c09a7906/affected-repos.txt +0 -1
- /package/.claude/commands/{core-architecture.md → core/architecture.md} +0 -0
- /package/.claude/commands/{core-guidelines.md → core/guidelines.md} +0 -0
- /package/.claude/commands/{core-roadmap.md → core/roadmap.md} +0 -0
- /package/.claude/commands/{core-style.md → core/style.md} +0 -0
- /package/.claude/commands/{flow-checklist.md → flow/checklist.md} +0 -0
- /package/.claude/commands/{flow-clarify.md → flow/clarify.md} +0 -0
- /package/.claude/commands/{flow-constitution.md → flow/constitution.md} +0 -0
- /package/.claude/commands/{flow-fix.md → flow/fix.md} +0 -0
- /package/.claude/commands/{flow-ideate.md → flow/ideate.md} +0 -0
- /package/.claude/commands/{flow-new.md → flow/new.md} +0 -0
- /package/.claude/commands/{flow-release.md → flow/release.md} +0 -0
- /package/.claude/commands/{flow-restart.md → flow/restart.md} +0 -0
- /package/.claude/commands/{flow-status.md → flow/status.md} +0 -0
- /package/.claude/commands/{flow-update.md → flow/update.md} +0 -0
- /package/.claude/commands/{flow-upgrade.md → flow/upgrade.md} +0 -0
- /package/.claude/commands/{flow-verify.md → flow/verify.md} +0 -0
- /package/.claude/commands/{code-review-high.md → util/code-review.md} +0 -0
- /package/.claude/commands/{git-commit.md → util/git-commit.md} +0 -0
- /package/.claude/commands/{problem-analyzer.md → util/problem-analyzer.md} +0 -0
- /package/.claude/skills/{flow-attention-refresh → domain/attention-refresh}/SKILL.md +0 -0
- /package/.claude/skills/{flow-brainstorming → domain/brainstorming}/SKILL.md +0 -0
- /package/.claude/skills/{flow-debugging → domain/debugging}/SKILL.md +0 -0
- /package/.claude/skills/{flow-finishing-branch → domain/finishing-branch}/SKILL.md +0 -0
- /package/.claude/skills/{flow-receiving-review → domain/receiving-review}/SKILL.md +0 -0
- /package/.claude/skills/{flow-tdd → domain/tdd}/SKILL.md +0 -0
- /package/.claude/skills/{verification-before-completion → domain/verification}/SKILL.md +0 -0
- /package/.claude/skills/{constitution-guardian → guardrail/constitution-guardian}/SKILL.md +0 -0
- /package/.claude/skills/{devflow-tdd-enforcer → guardrail/tdd-enforcer}/SKILL.md +0 -0
- /package/.claude/skills/{devflow-constitution-quick-ref → utility/constitution-quick-ref}/SKILL.md +0 -0
- /package/.claude/skills/{devflow-file-standards → utility/file-standards}/SKILL.md +0 -0
- /package/.claude/skills/{fractal-docs-generator → utility/fractal-docs}/SKILL.md +0 -0
- /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
|
-
#
|
|
49
|
-
if git rev-parse --
|
|
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
|
+
|