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
|
@@ -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"
|