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
@@ -0,0 +1,229 @@
1
+ #!/usr/bin/env bash
2
+ # =============================================================================
3
+ # Team State Recovery Script
4
+ # =============================================================================
5
+ # [INPUT]: 依赖 common.sh 的 Team 函数
6
+ # [OUTPUT]: 恢复 Team 状态,支持断点续传
7
+ # [POS]: flow-dev 的状态恢复器,被 /flow:dev --resume 调用
8
+ # [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
9
+ # =============================================================================
10
+
11
+ set -euo pipefail
12
+
13
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
14
+ source "$SCRIPT_DIR/common.sh"
15
+
16
+ # =============================================================================
17
+ # State Recovery Functions
18
+ # =============================================================================
19
+
20
+ # Check if Team state can be recovered
21
+ # Args: $1 - repo root, $2 - requirement ID
22
+ can_recover_state() {
23
+ local repo_root="$1"
24
+ local req_id="$2"
25
+ local status_file="$repo_root/devflow/requirements/$req_id/orchestration_status.json"
26
+
27
+ if [[ ! -f "$status_file" ]]; then
28
+ echo "false"
29
+ return 0
30
+ fi
31
+
32
+ local team_mode
33
+ team_mode=$(jq -r '.team.mode // "none"' "$status_file")
34
+
35
+ if [[ "$team_mode" == "none" || "$team_mode" == "null" ]]; then
36
+ echo "false"
37
+ return 0
38
+ fi
39
+
40
+ echo "true"
41
+ }
42
+
43
+ # Get recovery summary
44
+ # Args: $1 - repo root, $2 - requirement ID
45
+ get_recovery_summary() {
46
+ local repo_root="$1"
47
+ local req_id="$2"
48
+ local status_file="$repo_root/devflow/requirements/$req_id/orchestration_status.json"
49
+
50
+ if [[ ! -f "$status_file" ]]; then
51
+ echo '{"canRecover": false, "reason": "Status file not found"}'
52
+ return 0
53
+ fi
54
+
55
+ local team_data
56
+ team_data=$(jq '{
57
+ canRecover: (.team.mode != null and .team.mode != "none"),
58
+ mode: .team.mode,
59
+ lead: .team.lead,
60
+ teammateCount: (.team.teammates | length),
61
+ completedTasks: [.team.teammates[].completedTasks | length] | add,
62
+ pendingTasks: (.team.taskAssignments | keys | length),
63
+ lastActiveAt: ([.team.teammates[].lastActiveAt] | max),
64
+ ralphLoop: {
65
+ globalIteration: .ralphLoop.globalIteration,
66
+ maxIterations: .ralphLoop.maxIterations
67
+ }
68
+ }' "$status_file")
69
+
70
+ echo "$team_data"
71
+ }
72
+
73
+ # Recover Team state
74
+ # Args: $1 - repo root, $2 - requirement ID
75
+ recover_state() {
76
+ local repo_root="$1"
77
+ local req_id="$2"
78
+ local status_file="$repo_root/devflow/requirements/$req_id/orchestration_status.json"
79
+
80
+ echo "Recovering Team state for $req_id..."
81
+
82
+ # Reset teammate statuses to idle
83
+ local updated
84
+ updated=$(jq '
85
+ .team.teammates = [.team.teammates[] | .status = "idle" | .currentTask = null]
86
+ ' "$status_file")
87
+
88
+ echo "$updated" > "$status_file"
89
+
90
+ # Get pending tasks
91
+ local pending_tasks
92
+ pending_tasks=$(get_unassigned_tasks "$repo_root" "$req_id")
93
+
94
+ echo "Recovery complete."
95
+ echo "Pending tasks: $pending_tasks"
96
+ }
97
+
98
+ # Create state snapshot
99
+ # Args: $1 - repo root, $2 - requirement ID
100
+ create_snapshot() {
101
+ local repo_root="$1"
102
+ local req_id="$2"
103
+ local status_file="$repo_root/devflow/requirements/$req_id/orchestration_status.json"
104
+ local snapshot_dir="$repo_root/devflow/requirements/$req_id/.snapshots"
105
+ local timestamp
106
+ timestamp=$(date +%Y%m%d_%H%M%S)
107
+
108
+ mkdir -p "$snapshot_dir"
109
+
110
+ if [[ -f "$status_file" ]]; then
111
+ cp "$status_file" "$snapshot_dir/orchestration_status_$timestamp.json"
112
+ echo "Snapshot created: $snapshot_dir/orchestration_status_$timestamp.json"
113
+ else
114
+ echo "No status file to snapshot"
115
+ fi
116
+ }
117
+
118
+ # Restore from snapshot
119
+ # Args: $1 - repo root, $2 - requirement ID, $3 - snapshot timestamp (optional)
120
+ restore_snapshot() {
121
+ local repo_root="$1"
122
+ local req_id="$2"
123
+ local timestamp="${3:-}"
124
+ local status_file="$repo_root/devflow/requirements/$req_id/orchestration_status.json"
125
+ local snapshot_dir="$repo_root/devflow/requirements/$req_id/.snapshots"
126
+
127
+ if [[ -z "$timestamp" ]]; then
128
+ # Get latest snapshot
129
+ local latest
130
+ latest=$(ls -t "$snapshot_dir"/orchestration_status_*.json 2>/dev/null | head -1)
131
+ if [[ -z "$latest" ]]; then
132
+ echo "No snapshots found"
133
+ return 1
134
+ fi
135
+ cp "$latest" "$status_file"
136
+ echo "Restored from: $latest"
137
+ else
138
+ local snapshot_file="$snapshot_dir/orchestration_status_$timestamp.json"
139
+ if [[ -f "$snapshot_file" ]]; then
140
+ cp "$snapshot_file" "$status_file"
141
+ echo "Restored from: $snapshot_file"
142
+ else
143
+ echo "Snapshot not found: $snapshot_file"
144
+ return 1
145
+ fi
146
+ fi
147
+ }
148
+
149
+ # List available snapshots
150
+ # Args: $1 - repo root, $2 - requirement ID
151
+ list_snapshots() {
152
+ local repo_root="$1"
153
+ local req_id="$2"
154
+ local snapshot_dir="$repo_root/devflow/requirements/$req_id/.snapshots"
155
+
156
+ if [[ ! -d "$snapshot_dir" ]]; then
157
+ echo "[]"
158
+ return 0
159
+ fi
160
+
161
+ local snapshots='[]'
162
+ for file in "$snapshot_dir"/orchestration_status_*.json; do
163
+ if [[ -f "$file" ]]; then
164
+ local timestamp
165
+ timestamp=$(basename "$file" | sed 's/orchestration_status_//' | sed 's/.json//')
166
+ snapshots=$(echo "$snapshots" | jq --arg ts "$timestamp" '. + [$ts]')
167
+ fi
168
+ done
169
+
170
+ echo "$snapshots"
171
+ }
172
+
173
+ # =============================================================================
174
+ # Main
175
+ # =============================================================================
176
+
177
+ main() {
178
+ local action="${1:-help}"
179
+
180
+ case "$action" in
181
+ check)
182
+ local repo_root="${2:-$(get_repo_root)}"
183
+ local req_id="${3:-$(get_current_req_id)}"
184
+ can_recover_state "$repo_root" "$req_id"
185
+ ;;
186
+ summary)
187
+ local repo_root="${2:-$(get_repo_root)}"
188
+ local req_id="${3:-$(get_current_req_id)}"
189
+ get_recovery_summary "$repo_root" "$req_id"
190
+ ;;
191
+ recover)
192
+ local repo_root="${2:-$(get_repo_root)}"
193
+ local req_id="${3:-$(get_current_req_id)}"
194
+ recover_state "$repo_root" "$req_id"
195
+ ;;
196
+ snapshot)
197
+ local repo_root="${2:-$(get_repo_root)}"
198
+ local req_id="${3:-$(get_current_req_id)}"
199
+ create_snapshot "$repo_root" "$req_id"
200
+ ;;
201
+ restore)
202
+ local repo_root="${2:-$(get_repo_root)}"
203
+ local req_id="${3:-$(get_current_req_id)}"
204
+ local timestamp="${4:-}"
205
+ restore_snapshot "$repo_root" "$req_id" "$timestamp"
206
+ ;;
207
+ list-snapshots)
208
+ local repo_root="${2:-$(get_repo_root)}"
209
+ local req_id="${3:-$(get_current_req_id)}"
210
+ list_snapshots "$repo_root" "$req_id"
211
+ ;;
212
+ help|*)
213
+ echo "Usage: $0 <action> [args...]"
214
+ echo ""
215
+ echo "Actions:"
216
+ echo " check [repo_root] [req_id] - Check if state can be recovered"
217
+ echo " summary [repo_root] [req_id] - Get recovery summary"
218
+ echo " recover [repo_root] [req_id] - Recover Team state"
219
+ echo " snapshot [repo_root] [req_id] - Create state snapshot"
220
+ echo " restore [repo_root] [req_id] [timestamp] - Restore from snapshot"
221
+ echo " list-snapshots [repo_root] [req_id] - List available snapshots"
222
+ ;;
223
+ esac
224
+ }
225
+
226
+ # Run if executed directly
227
+ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
228
+ main "$@"
229
+ fi
@@ -0,0 +1,433 @@
1
+ /**
2
+ * [INPUT]: 依赖 fs, path, yaml (js-yaml) 模块
3
+ * [OUTPUT]: 对外提供 WorkflowStatus 类型、isArtifactComplete, getNextActions, getWorkflowStatus 函数
4
+ * [POS]: .claude/scripts 的工作流状态检测工具,被 flow-* 命令和 hooks 消费
5
+ * [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
6
+ */
7
+
8
+ import * as fs from 'fs';
9
+ import * as path from 'path';
10
+ import * as yaml from 'js-yaml';
11
+ import { glob } from 'glob';
12
+
13
+ // =============================================================================
14
+ // 类型定义
15
+ // =============================================================================
16
+
17
+ /** 工件定义 */
18
+ export interface Artifact {
19
+ id: string;
20
+ generates: string;
21
+ requires: string[];
22
+ skill: string;
23
+ optional?: boolean;
24
+ glob?: boolean;
25
+ instruction?: string;
26
+ }
27
+
28
+ /** 工件状态 */
29
+ export type ArtifactStatus = 'complete' | 'ready' | 'blocked';
30
+
31
+ /** 工件状态详情 */
32
+ export interface ArtifactStatusDetail {
33
+ id: string;
34
+ status: ArtifactStatus;
35
+ generates: string;
36
+ resolvedPath: string;
37
+ skill: string;
38
+ optional: boolean;
39
+ blockedBy?: string[];
40
+ }
41
+
42
+ /** 工作流状态 */
43
+ export interface WorkflowStatus {
44
+ reqId: string;
45
+ complete: ArtifactStatusDetail[];
46
+ ready: ArtifactStatusDetail[];
47
+ optionalPending: ArtifactStatusDetail[];
48
+ blocked: ArtifactStatusDetail[];
49
+ progress: {
50
+ completed: number;
51
+ total: number;
52
+ percent: number;
53
+ };
54
+ nextAction?: {
55
+ artifactId: string;
56
+ skill: string;
57
+ command: string;
58
+ };
59
+ }
60
+
61
+ /** workflow.yaml 结构 */
62
+ interface WorkflowSchema {
63
+ name: string;
64
+ version: string;
65
+ description: string;
66
+ artifacts: Artifact[];
67
+ detection: {
68
+ method: string;
69
+ fallback: string;
70
+ glob_options?: {
71
+ ignore?: string[];
72
+ };
73
+ };
74
+ skills: Record<string, Array<{ id: string; path: string; description: string }>>;
75
+ context: {
76
+ global: Array<{ file: string; reason: string }>;
77
+ defaults: Record<string, Array<{ file: string; reason: string; optional?: boolean }>>;
78
+ };
79
+ deprecated: Array<{ id: string; replacement: string; message: string }>;
80
+ }
81
+
82
+ // =============================================================================
83
+ // 配置
84
+ // =============================================================================
85
+
86
+ const PROJECT_ROOT = path.resolve(__dirname, '../..');
87
+ const WORKFLOW_YAML_PATH = path.join(PROJECT_ROOT, '.claude/skills/workflow.yaml');
88
+
89
+ // =============================================================================
90
+ // 核心函数
91
+ // =============================================================================
92
+
93
+ /** 加载 workflow.yaml */
94
+ export function loadWorkflowSchema(): WorkflowSchema {
95
+ const content = fs.readFileSync(WORKFLOW_YAML_PATH, 'utf-8');
96
+ return yaml.load(content) as WorkflowSchema;
97
+ }
98
+
99
+ /** 替换路径中的占位符 */
100
+ export function resolvePath(pattern: string, reqId: string, bugId?: string): string {
101
+ let resolved = pattern.replace(/\{REQ\}/g, reqId);
102
+ if (bugId) {
103
+ resolved = resolved.replace(/\{BUG\}/g, bugId);
104
+ }
105
+ return resolved;
106
+ }
107
+
108
+ /** 检查文件/目录是否存在(支持 glob) */
109
+ export async function checkExists(pattern: string, isGlob: boolean): Promise<boolean> {
110
+ const fullPath = path.join(PROJECT_ROOT, pattern);
111
+
112
+ if (isGlob) {
113
+ // glob 模式:检查是否有匹配的文件
114
+ const matches = await glob(fullPath, { nodir: false });
115
+ return matches.length > 0;
116
+ } else {
117
+ // 精确路径:检查文件或目录存在
118
+ return fs.existsSync(fullPath);
119
+ }
120
+ }
121
+
122
+ /** 同步版本的检查存在性 */
123
+ export function checkExistsSync(pattern: string, isGlob: boolean): boolean {
124
+ const fullPath = path.join(PROJECT_ROOT, pattern);
125
+
126
+ if (isGlob) {
127
+ // 简化的 glob 检查:使用 fs.readdirSync
128
+ const dir = path.dirname(fullPath);
129
+ const filePattern = path.basename(fullPath);
130
+
131
+ if (!fs.existsSync(dir)) return false;
132
+
133
+ try {
134
+ const files = fs.readdirSync(dir);
135
+ // 简单的通配符匹配
136
+ const regex = new RegExp('^' + filePattern.replace(/\*/g, '.*') + '$');
137
+ return files.some(f => regex.test(f));
138
+ } catch {
139
+ return false;
140
+ }
141
+ } else {
142
+ return fs.existsSync(fullPath);
143
+ }
144
+ }
145
+
146
+ /** 检查工件是否完成 */
147
+ export function isArtifactComplete(artifact: Artifact, reqId: string): boolean {
148
+ const resolvedPath = resolvePath(artifact.generates, reqId);
149
+ return checkExistsSync(resolvedPath, artifact.glob ?? false);
150
+ }
151
+
152
+ /** 获取工件的依赖状态 */
153
+ export function getBlockingDependencies(
154
+ artifact: Artifact,
155
+ reqId: string,
156
+ schema: WorkflowSchema
157
+ ): string[] {
158
+ const blocking: string[] = [];
159
+
160
+ for (const depId of artifact.requires) {
161
+ const depArtifact = schema.artifacts.find(a => a.id === depId);
162
+ if (!depArtifact) continue;
163
+
164
+ if (!isArtifactComplete(depArtifact, reqId)) {
165
+ blocking.push(depId);
166
+ }
167
+ }
168
+
169
+ return blocking;
170
+ }
171
+
172
+ /** 获取工件状态 */
173
+ export function getArtifactStatus(
174
+ artifact: Artifact,
175
+ reqId: string,
176
+ schema: WorkflowSchema
177
+ ): ArtifactStatus {
178
+ if (isArtifactComplete(artifact, reqId)) {
179
+ return 'complete';
180
+ }
181
+
182
+ const blocking = getBlockingDependencies(artifact, reqId, schema);
183
+ if (blocking.length === 0) {
184
+ return 'ready';
185
+ }
186
+
187
+ return 'blocked';
188
+ }
189
+
190
+ /** 获取下一步可执行的工件 */
191
+ export function getNextActions(reqId: string): Artifact[] {
192
+ const schema = loadWorkflowSchema();
193
+ const ready: Artifact[] = [];
194
+
195
+ // 筛选需求级工件
196
+ const reqArtifacts = schema.artifacts.filter(a => a.generates.includes('{REQ}'));
197
+
198
+ for (const artifact of reqArtifacts) {
199
+ const status = getArtifactStatus(artifact, reqId, schema);
200
+
201
+ // 只返回非可选的 ready 工件
202
+ if (status === 'ready' && !artifact.optional) {
203
+ ready.push(artifact);
204
+ }
205
+ }
206
+
207
+ return ready;
208
+ }
209
+
210
+ /** 获取完整的工作流状态 */
211
+ export function getWorkflowStatus(reqId: string): WorkflowStatus {
212
+ const schema = loadWorkflowSchema();
213
+
214
+ const complete: ArtifactStatusDetail[] = [];
215
+ const ready: ArtifactStatusDetail[] = [];
216
+ const optionalPending: ArtifactStatusDetail[] = [];
217
+ const blocked: ArtifactStatusDetail[] = [];
218
+
219
+ // 筛选需求级工件
220
+ const reqArtifacts = schema.artifacts.filter(a => a.generates.includes('{REQ}'));
221
+
222
+ for (const artifact of reqArtifacts) {
223
+ const status = getArtifactStatus(artifact, reqId, schema);
224
+ const resolvedPath = resolvePath(artifact.generates, reqId);
225
+
226
+ const detail: ArtifactStatusDetail = {
227
+ id: artifact.id,
228
+ status,
229
+ generates: artifact.generates,
230
+ resolvedPath,
231
+ skill: artifact.skill,
232
+ optional: artifact.optional ?? false,
233
+ };
234
+
235
+ if (status === 'blocked') {
236
+ detail.blockedBy = getBlockingDependencies(artifact, reqId, schema);
237
+ }
238
+
239
+ switch (status) {
240
+ case 'complete':
241
+ complete.push(detail);
242
+ break;
243
+ case 'ready':
244
+ if (artifact.optional) {
245
+ optionalPending.push(detail);
246
+ } else {
247
+ ready.push(detail);
248
+ }
249
+ break;
250
+ case 'blocked':
251
+ blocked.push(detail);
252
+ break;
253
+ }
254
+ }
255
+
256
+ // 计算进度
257
+ const total = complete.length + ready.length + blocked.length;
258
+ const percent = total > 0 ? Math.round((complete.length / total) * 100) : 0;
259
+
260
+ // 确定下一步
261
+ let nextAction: WorkflowStatus['nextAction'];
262
+ if (ready.length > 0) {
263
+ const next = ready[0];
264
+ nextAction = {
265
+ artifactId: next.id,
266
+ skill: next.skill,
267
+ command: `/${next.skill} "${reqId}"`,
268
+ };
269
+ }
270
+
271
+ return {
272
+ reqId,
273
+ complete,
274
+ ready,
275
+ optionalPending,
276
+ blocked,
277
+ progress: {
278
+ completed: complete.length,
279
+ total,
280
+ percent,
281
+ },
282
+ nextAction,
283
+ };
284
+ }
285
+
286
+ /** 获取项目级工件状态 */
287
+ export function getProjectStatus(): {
288
+ complete: ArtifactStatusDetail[];
289
+ missing: ArtifactStatusDetail[];
290
+ } {
291
+ const schema = loadWorkflowSchema();
292
+
293
+ const complete: ArtifactStatusDetail[] = [];
294
+ const missing: ArtifactStatusDetail[] = [];
295
+
296
+ // 筛选项目级工件(不包含 {REQ} 或 {BUG})
297
+ const projectArtifacts = schema.artifacts.filter(
298
+ a => !a.generates.includes('{REQ}') && !a.generates.includes('{BUG}')
299
+ );
300
+
301
+ for (const artifact of projectArtifacts) {
302
+ const exists = checkExistsSync(artifact.generates, artifact.glob ?? false);
303
+
304
+ const detail: ArtifactStatusDetail = {
305
+ id: artifact.id,
306
+ status: exists ? 'complete' : 'ready',
307
+ generates: artifact.generates,
308
+ resolvedPath: artifact.generates,
309
+ skill: artifact.skill,
310
+ optional: artifact.optional ?? false,
311
+ };
312
+
313
+ if (exists) {
314
+ complete.push(detail);
315
+ } else {
316
+ missing.push(detail);
317
+ }
318
+ }
319
+
320
+ return { complete, missing };
321
+ }
322
+
323
+ /** 格式化输出工作流状态 */
324
+ export function formatWorkflowStatus(status: WorkflowStatus): string {
325
+ const lines: string[] = [];
326
+
327
+ lines.push(`\n=== 需求 ${status.reqId} 工作流状态 ===\n`);
328
+
329
+ if (status.complete.length > 0) {
330
+ lines.push(`已完成 (${status.complete.length}):`);
331
+ for (const a of status.complete) {
332
+ lines.push(` ✓ ${a.id} → ${a.resolvedPath}`);
333
+ }
334
+ lines.push('');
335
+ }
336
+
337
+ if (status.ready.length > 0) {
338
+ lines.push(`可执行 (${status.ready.length}):`);
339
+ for (const a of status.ready) {
340
+ lines.push(` → ${a.id} (skill: ${a.skill})`);
341
+ }
342
+ lines.push('');
343
+ }
344
+
345
+ if (status.optionalPending.length > 0) {
346
+ lines.push(`可选工件 (${status.optionalPending.length}):`);
347
+ for (const a of status.optionalPending) {
348
+ lines.push(` ○ ${a.id} (skill: ${a.skill})`);
349
+ }
350
+ lines.push('');
351
+ }
352
+
353
+ if (status.blocked.length > 0) {
354
+ lines.push(`阻塞中 (${status.blocked.length}):`);
355
+ for (const a of status.blocked) {
356
+ lines.push(` ✗ ${a.id} (等待: ${a.blockedBy?.join(', ')})`);
357
+ }
358
+ lines.push('');
359
+ }
360
+
361
+ lines.push(`进度: ${status.progress.completed}/${status.progress.total} (${status.progress.percent}%)`);
362
+
363
+ if (status.nextAction) {
364
+ lines.push('');
365
+ lines.push(`建议下一步: ${status.nextAction.command}`);
366
+ }
367
+
368
+ return lines.join('\n');
369
+ }
370
+
371
+ // =============================================================================
372
+ // CLI 入口
373
+ // =============================================================================
374
+
375
+ if (require.main === module) {
376
+ const args = process.argv.slice(2);
377
+
378
+ if (args.includes('--help') || args.includes('-h')) {
379
+ console.log(`
380
+ 用法: npx ts-node workflow-status.ts [REQ-ID] [选项]
381
+
382
+ 选项:
383
+ --json JSON 格式输出
384
+ --next 仅输出下一步可执行的工件
385
+ --project 检查项目级工件状态
386
+ -h, --help 显示帮助
387
+ `);
388
+ process.exit(0);
389
+ }
390
+
391
+ const isJson = args.includes('--json');
392
+ const isNext = args.includes('--next');
393
+ const isProject = args.includes('--project');
394
+ const reqId = args.find(a => a.startsWith('REQ-'));
395
+
396
+ if (isProject) {
397
+ const status = getProjectStatus();
398
+ if (isJson) {
399
+ console.log(JSON.stringify(status, null, 2));
400
+ } else {
401
+ console.log('\n=== 项目级工件状态 ===\n');
402
+ if (status.complete.length > 0) {
403
+ console.log(`已完成 (${status.complete.length}):`);
404
+ for (const a of status.complete) {
405
+ console.log(` ✓ ${a.id} → ${a.generates}`);
406
+ }
407
+ }
408
+ if (status.missing.length > 0) {
409
+ console.log(`\n缺失 (${status.missing.length}):`);
410
+ for (const a of status.missing) {
411
+ console.log(` ○ ${a.id} → ${a.generates} (skill: ${a.skill})`);
412
+ }
413
+ }
414
+ }
415
+ } else if (reqId) {
416
+ if (isNext) {
417
+ const actions = getNextActions(reqId);
418
+ for (const a of actions) {
419
+ console.log(`${a.id}:${a.skill}`);
420
+ }
421
+ } else {
422
+ const status = getWorkflowStatus(reqId);
423
+ if (isJson) {
424
+ console.log(JSON.stringify(status, null, 2));
425
+ } else {
426
+ console.log(formatWorkflowStatus(status));
427
+ }
428
+ }
429
+ } else {
430
+ console.error('错误: 需要指定 REQ-ID 或使用 --project');
431
+ process.exit(1);
432
+ }
433
+ }
@@ -60,6 +60,15 @@
60
60
  "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-tool-use-guardrail.sh"
61
61
  }
62
62
  ]
63
+ },
64
+ {
65
+ "matcher": "Task",
66
+ "hooks": [
67
+ {
68
+ "type": "command",
69
+ "command": "npx ts-node $CLAUDE_PROJECT_DIR/.claude/hooks/inject-agent-context.ts"
70
+ }
71
+ ]
63
72
  }
64
73
  ],
65
74
  "Stop": [
@@ -71,6 +80,16 @@
71
80
  }
72
81
  ]
73
82
  }
83
+ ],
84
+ "SubagentStop": [
85
+ {
86
+ "hooks": [
87
+ {
88
+ "type": "command",
89
+ "command": "npx ts-node $CLAUDE_PROJECT_DIR/.claude/hooks/ralph-loop.ts"
90
+ }
91
+ ]
92
+ }
74
93
  ]
75
94
  }
76
95
  }