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,334 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * [INPUT]: 依赖 TASKS.md 文件格式 (Phase/[P]/[US*]/checkbox 标记)
4
+ * [OUTPUT]: 对外提供 TaskInfo, ParallelGroup, parseTasks(), getParallelGroups()
5
+ * [POS]: scripts 的任务依赖解析器,被 Team Hook 调用以确定可并行任务
6
+ * [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const fs = require('fs');
12
+
13
+ // ============================================================================
14
+ // Regex Patterns
15
+ // ============================================================================
16
+
17
+ // Phase header: ## Phase 1: Setup, ## Phase 2: Tests First (TDD) ⚠️
18
+ const PHASE_HEADER_REGEX = /^##\s+Phase\s+(\d+):\s*(.+?)(?:\s+\[([^\]]+)\])?$/;
19
+
20
+ // Task line: - [x] **T001** [P] [US1] Title `file.ts`
21
+ // or: - [ ] T001 [P1] [US2] Title
22
+ const TASK_LINE_REGEX = /^-\s+\[([ xX])\]\s+(?:\*\*)?([Tt]\d+)(?:\*\*)?\s+(.*)$/;
23
+
24
+ // Markers within task line
25
+ const PARALLEL_MARKER = /\[P\]/;
26
+ const USER_STORY_MARKER = /\[US(\d+)\]/;
27
+ const PRIORITY_MARKER = /\[P(\d+)\]/;
28
+ const FILE_PATH_MARKER = /`([^`]+)`/;
29
+
30
+ // ============================================================================
31
+ // Utilities
32
+ // ============================================================================
33
+
34
+ /**
35
+ * Normalize line endings to \n
36
+ * @param {string} content
37
+ * @returns {string}
38
+ */
39
+ function normalizeLineEndings(content) {
40
+ return content.replace(/\r\n?/g, '\n');
41
+ }
42
+
43
+ // ============================================================================
44
+ // Parser Functions
45
+ // ============================================================================
46
+
47
+ /**
48
+ * @typedef {Object} TaskInfo
49
+ * @property {string} id - Task ID (T001, T002, etc.)
50
+ * @property {string} title - Task title without markers
51
+ * @property {boolean} parallel - true if [P] marker present
52
+ * @property {string} userStory - US1, US2, etc. (empty if none)
53
+ * @property {number} phase - Phase number (1, 2, 3, etc.)
54
+ * @property {string} filePath - File path from backticks (empty if none)
55
+ * @property {boolean} completed - true if [x], false if [ ]
56
+ * @property {string} priority - P1, P2, P3 (empty if none)
57
+ * @property {string} rawLine - Original line for debugging
58
+ */
59
+
60
+ /**
61
+ * @typedef {Object} ParallelGroup
62
+ * @property {number} phase - Phase number
63
+ * @property {string} userStory - User story identifier
64
+ * @property {TaskInfo[]} tasks - Tasks in this group
65
+ */
66
+
67
+ /**
68
+ * @typedef {Object} ParseResult
69
+ * @property {TaskInfo[]} tasks - All parsed tasks
70
+ * @property {ParallelGroup[]} parallelGroups - Grouped parallel tasks
71
+ * @property {number[]} phases - List of phase numbers
72
+ * @property {Object} stats - Statistics
73
+ * @property {number} stats.total - Total tasks
74
+ * @property {number} stats.completed - Completed tasks
75
+ * @property {number} stats.pending - Pending tasks
76
+ * @property {number} stats.parallelizable - Parallelizable pending tasks
77
+ */
78
+
79
+ /**
80
+ * Parse a single task line and extract TaskInfo
81
+ * @param {string} line - The line to parse
82
+ * @param {number} currentPhase - Current phase number
83
+ * @param {string} currentUserStory - Current user story from phase header
84
+ * @returns {TaskInfo|null}
85
+ */
86
+ function parseTaskLine(line, currentPhase, currentUserStory) {
87
+ const match = line.match(TASK_LINE_REGEX);
88
+ if (!match) return null;
89
+
90
+ const [, checkboxState, taskId, rest] = match;
91
+ const completed = checkboxState.toLowerCase() === 'x';
92
+
93
+ // Extract markers from rest of line
94
+ const isParallel = PARALLEL_MARKER.test(rest);
95
+
96
+ const userStoryMatch = rest.match(USER_STORY_MARKER);
97
+ const userStory = userStoryMatch ? `US${userStoryMatch[1]}` : currentUserStory;
98
+
99
+ const priorityMatch = rest.match(PRIORITY_MARKER);
100
+ const priority = priorityMatch ? `P${priorityMatch[1]}` : '';
101
+
102
+ const filePathMatch = rest.match(FILE_PATH_MARKER);
103
+ const filePath = filePathMatch ? filePathMatch[1] : '';
104
+
105
+ // Clean title: remove all markers and file path
106
+ let title = rest
107
+ .replace(PARALLEL_MARKER, '')
108
+ .replace(USER_STORY_MARKER, '')
109
+ .replace(PRIORITY_MARKER, '')
110
+ .replace(FILE_PATH_MARKER, '')
111
+ .replace(/\[Setup\]/gi, '')
112
+ .replace(/\*\*/g, '')
113
+ .trim();
114
+
115
+ // Remove leading/trailing punctuation and whitespace
116
+ title = title.replace(/^[\s\-:]+|[\s\-:]+$/g, '').trim();
117
+
118
+ // Clean up multiple spaces left after removing markers
119
+ title = title.replace(/\s{2,}/g, ' ').trim();
120
+
121
+ return {
122
+ id: taskId.toUpperCase(),
123
+ title,
124
+ parallel: isParallel,
125
+ userStory,
126
+ phase: currentPhase,
127
+ filePath,
128
+ completed,
129
+ priority,
130
+ rawLine: line
131
+ };
132
+ }
133
+
134
+ /**
135
+ * Parse TASKS.md content and extract all tasks
136
+ * @param {string} content - TASKS.md file content
137
+ * @returns {TaskInfo[]}
138
+ */
139
+ function parseTasks(content) {
140
+ const lines = normalizeLineEndings(content).split('\n');
141
+ const tasks = [];
142
+
143
+ let currentPhase = 0;
144
+ let currentUserStory = '';
145
+
146
+ for (const line of lines) {
147
+ // Check for phase header
148
+ const phaseMatch = line.match(PHASE_HEADER_REGEX);
149
+ if (phaseMatch) {
150
+ currentPhase = parseInt(phaseMatch[1], 10);
151
+ // Extract user story from phase header if present
152
+ const headerUserStory = phaseMatch[3];
153
+ if (headerUserStory) {
154
+ const usMatch = headerUserStory.match(/US(\d+)/);
155
+ currentUserStory = usMatch ? `US${usMatch[1]}` : '';
156
+ } else {
157
+ currentUserStory = '';
158
+ }
159
+ continue;
160
+ }
161
+
162
+ // Check for task line
163
+ const task = parseTaskLine(line, currentPhase, currentUserStory);
164
+ if (task) {
165
+ tasks.push(task);
166
+ }
167
+ }
168
+
169
+ return tasks;
170
+ }
171
+
172
+ /**
173
+ * Group tasks into parallel execution groups
174
+ * Tasks in the same phase with [P] marker can run in parallel
175
+ * @param {TaskInfo[]} tasks - All tasks
176
+ * @returns {ParallelGroup[]}
177
+ */
178
+ function getParallelGroups(tasks) {
179
+ const groups = [];
180
+ const groupMap = new Map();
181
+
182
+ // Group by phase + userStory
183
+ for (const task of tasks) {
184
+ if (task.completed) continue; // Skip completed tasks
185
+
186
+ const key = `${task.phase}-${task.userStory}`;
187
+ if (!groupMap.has(key)) {
188
+ groupMap.set(key, []);
189
+ }
190
+ groupMap.get(key).push(task);
191
+ }
192
+
193
+ // Convert to ParallelGroup array
194
+ for (const [key, groupTasks] of groupMap) {
195
+ const [phaseStr, userStory] = key.split('-');
196
+ const phase = parseInt(phaseStr, 10);
197
+
198
+ // Only include groups with parallel tasks
199
+ const parallelTasks = groupTasks.filter(t => t.parallel);
200
+ if (parallelTasks.length > 0) {
201
+ groups.push({
202
+ phase,
203
+ userStory: userStory || '',
204
+ tasks: parallelTasks
205
+ });
206
+ }
207
+ }
208
+
209
+ // Sort by phase
210
+ groups.sort((a, b) => a.phase - b.phase);
211
+
212
+ return groups;
213
+ }
214
+
215
+ /**
216
+ * Get next available tasks (not completed, not blocked by phase dependencies)
217
+ * @param {TaskInfo[]} tasks - All tasks
218
+ * @returns {TaskInfo[]}
219
+ */
220
+ function getNextAvailableTasks(tasks) {
221
+ const pendingTasks = tasks.filter(t => !t.completed);
222
+ if (pendingTasks.length === 0) return [];
223
+
224
+ // Find minimum phase with pending tasks
225
+ const minPhase = Math.min(...pendingTasks.map(t => t.phase));
226
+
227
+ // Return all pending tasks in that phase
228
+ return pendingTasks.filter(t => t.phase === minPhase);
229
+ }
230
+
231
+ /**
232
+ * Full parse with statistics
233
+ * @param {string} content - TASKS.md file content
234
+ * @returns {ParseResult}
235
+ */
236
+ function parseTasksWithStats(content) {
237
+ const tasks = parseTasks(content);
238
+ const parallelGroups = getParallelGroups(tasks);
239
+
240
+ const phases = [...new Set(tasks.map(t => t.phase))].sort((a, b) => a - b);
241
+
242
+ const completed = tasks.filter(t => t.completed).length;
243
+ const pending = tasks.filter(t => !t.completed).length;
244
+ const parallelizable = tasks.filter(t => t.parallel && !t.completed).length;
245
+
246
+ return {
247
+ tasks,
248
+ parallelGroups,
249
+ phases,
250
+ stats: {
251
+ total: tasks.length,
252
+ completed,
253
+ pending,
254
+ parallelizable
255
+ }
256
+ };
257
+ }
258
+
259
+ // ============================================================================
260
+ // Exports
261
+ // ============================================================================
262
+
263
+ module.exports = {
264
+ parseTasks,
265
+ parseTaskLine,
266
+ getParallelGroups,
267
+ getNextAvailableTasks,
268
+ parseTasksWithStats
269
+ };
270
+
271
+ // ============================================================================
272
+ // CLI Interface
273
+ // ============================================================================
274
+
275
+ if (require.main === module) {
276
+ const args = process.argv.slice(2);
277
+
278
+ if (args.length < 1) {
279
+ console.error('Usage: parse-task-dependencies.js <command> [args]');
280
+ console.error('Commands:');
281
+ console.error(' parse <tasks-file> Parse TASKS.md and output JSON');
282
+ console.error(' groups <tasks-file> Get parallel groups');
283
+ console.error(' next <tasks-file> Get next available tasks');
284
+ console.error(' stats <tasks-file> Get task statistics');
285
+ process.exit(1);
286
+ }
287
+
288
+ const command = args[0];
289
+ const tasksFile = args[1];
290
+
291
+ if (!tasksFile) {
292
+ console.error('Error: tasks-file required');
293
+ process.exit(1);
294
+ }
295
+
296
+ if (!fs.existsSync(tasksFile)) {
297
+ console.error(`Error: File not found: ${tasksFile}`);
298
+ process.exit(1);
299
+ }
300
+
301
+ const content = fs.readFileSync(tasksFile, 'utf-8');
302
+
303
+ switch (command) {
304
+ case 'parse': {
305
+ const result = parseTasksWithStats(content);
306
+ console.log(JSON.stringify(result, null, 2));
307
+ break;
308
+ }
309
+
310
+ case 'groups': {
311
+ const tasks = parseTasks(content);
312
+ const groups = getParallelGroups(tasks);
313
+ console.log(JSON.stringify(groups, null, 2));
314
+ break;
315
+ }
316
+
317
+ case 'next': {
318
+ const tasks = parseTasks(content);
319
+ const nextTasks = getNextAvailableTasks(tasks);
320
+ console.log(JSON.stringify(nextTasks, null, 2));
321
+ break;
322
+ }
323
+
324
+ case 'stats': {
325
+ const result = parseTasksWithStats(content);
326
+ console.log(JSON.stringify(result.stats, null, 2));
327
+ break;
328
+ }
329
+
330
+ default:
331
+ console.error(`Unknown command: ${command}`);
332
+ process.exit(1);
333
+ }
334
+ }
@@ -0,0 +1,165 @@
1
+ #!/bin/bash
2
+ # [INPUT]: 依赖 ERROR_LOG.md 模板
3
+ # [OUTPUT]: 追加错误记录到 ERROR_LOG.md
4
+ # [POS]: scripts 的错误记录脚本,被 ralph-stop-hook.sh 调用
5
+ # [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
6
+
7
+ set -e
8
+
9
+ # ============================================================================
10
+ # Usage
11
+ # ============================================================================
12
+
13
+ usage() {
14
+ cat << EOF
15
+ Usage: record-quality-error.sh <phase> <error_type> <error_message> [--task <task_id>]
16
+
17
+ Record a quality gate error to ERROR_LOG.md.
18
+
19
+ Arguments:
20
+ phase Phase name (flow-dev, flow-review, etc.)
21
+ error_type Error type (Test Failure, Build Error, Lint Error, etc.)
22
+ error_message Full error message
23
+
24
+ Options:
25
+ --task Associated task ID (e.g., T015)
26
+ --root-cause Root cause analysis (optional)
27
+ --resolution Resolution applied (optional)
28
+
29
+ Examples:
30
+ record-quality-error.sh flow-dev "Test Failure" "Expected 5 but got 3" --task T015
31
+ EOF
32
+ exit 1
33
+ }
34
+
35
+ # ============================================================================
36
+ # Parse Arguments
37
+ # ============================================================================
38
+
39
+ PHASE=""
40
+ ERROR_TYPE=""
41
+ ERROR_MESSAGE=""
42
+ TASK_ID=""
43
+ ROOT_CAUSE=""
44
+ RESOLUTION=""
45
+
46
+ while [[ $# -gt 0 ]]; do
47
+ case $1 in
48
+ --task)
49
+ TASK_ID="$2"
50
+ shift 2
51
+ ;;
52
+ --root-cause)
53
+ ROOT_CAUSE="$2"
54
+ shift 2
55
+ ;;
56
+ --resolution)
57
+ RESOLUTION="$2"
58
+ shift 2
59
+ ;;
60
+ --help|-h)
61
+ usage
62
+ ;;
63
+ *)
64
+ if [[ -z "$PHASE" ]]; then
65
+ PHASE="$1"
66
+ elif [[ -z "$ERROR_TYPE" ]]; then
67
+ ERROR_TYPE="$1"
68
+ elif [[ -z "$ERROR_MESSAGE" ]]; then
69
+ ERROR_MESSAGE="$1"
70
+ fi
71
+ shift
72
+ ;;
73
+ esac
74
+ done
75
+
76
+ if [[ -z "$PHASE" || -z "$ERROR_TYPE" || -z "$ERROR_MESSAGE" ]]; then
77
+ echo "Error: phase, error_type, and error_message are required"
78
+ usage
79
+ fi
80
+
81
+ # ============================================================================
82
+ # Detect REQ-ID and Error Log Path
83
+ # ============================================================================
84
+
85
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
86
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
87
+
88
+ # Try environment variable first
89
+ REQ_ID="${DEVFLOW_REQ_ID:-}"
90
+
91
+ # Try git branch
92
+ if [[ -z "$REQ_ID" ]]; then
93
+ BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
94
+ REQ_ID=$(echo "$BRANCH" | grep -oE 'REQ-[0-9]+' | head -1 || echo "")
95
+ fi
96
+
97
+ if [[ -z "$REQ_ID" ]]; then
98
+ echo "Warning: Could not detect REQ-ID. Recording to project-level ERROR_LOG.md"
99
+ ERROR_LOG="$PROJECT_ROOT/ERROR_LOG.md"
100
+ else
101
+ ERROR_LOG="$PROJECT_ROOT/devflow/requirements/$REQ_ID/ERROR_LOG.md"
102
+ fi
103
+
104
+ # ============================================================================
105
+ # Generate Error Entry
106
+ # ============================================================================
107
+
108
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
109
+ DATE_DISPLAY=$(date +"%Y-%m-%d %H:%M")
110
+
111
+ # Count existing errors to generate error number
112
+ if [[ -f "$ERROR_LOG" ]]; then
113
+ ERROR_COUNT=$(grep -c "^## \[" "$ERROR_LOG" 2>/dev/null || echo "0")
114
+ else
115
+ ERROR_COUNT=0
116
+ fi
117
+ ERROR_NUM=$((ERROR_COUNT + 1))
118
+ ERROR_ID=$(printf "E%03d" $ERROR_NUM)
119
+
120
+ # Build phase info
121
+ PHASE_INFO="$PHASE"
122
+ if [[ -n "$TASK_ID" ]]; then
123
+ PHASE_INFO="$PHASE / $TASK_ID"
124
+ fi
125
+
126
+ # Create error entry
127
+ ERROR_ENTRY="## [$DATE_DISPLAY] $ERROR_ID: Quality Gate Failure
128
+
129
+ **Phase**: $PHASE_INFO
130
+ **Error Type**: $ERROR_TYPE
131
+ **Error Message**:
132
+ \`\`\`
133
+ $ERROR_MESSAGE
134
+ \`\`\`
135
+ **Root Cause**: ${ROOT_CAUSE:-[Pending analysis]}
136
+ **Resolution**: ${RESOLUTION:-[Pending]}
137
+ **Prevention**: [Optional]
138
+
139
+ ---
140
+ "
141
+
142
+ # ============================================================================
143
+ # Write to Error Log
144
+ # ============================================================================
145
+
146
+ # Create file with header if it doesn't exist
147
+ if [[ ! -f "$ERROR_LOG" ]]; then
148
+ cat > "$ERROR_LOG" << 'EOF'
149
+ # Error Log
150
+
151
+ > **[PROTOCOL]**: 变更时更新此头部,然后检查 CLAUDE.md
152
+
153
+ This file records errors encountered during development for learning and prevention.
154
+
155
+ ---
156
+
157
+ EOF
158
+ fi
159
+
160
+ # Append error entry
161
+ echo "$ERROR_ENTRY" >> "$ERROR_LOG"
162
+
163
+ echo "✅ Error recorded to $ERROR_LOG"
164
+ echo " ID: $ERROR_ID"
165
+ echo " Type: $ERROR_TYPE"