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,284 @@
1
+ #!/usr/bin/env bash
2
+ # shellcheck disable=SC2312
3
+
4
+ set -euo pipefail
5
+
6
+ # ============================================================================
7
+ # populate-research-tasks.sh - 智能填充 tasks.json 的决策信息
8
+ # ============================================================================
9
+ # 功能: 从 research-summary.md 和其他研究材料中提取决策信息,
10
+ # 填充 tasks.json 的 decision/rationale/alternatives 字段
11
+ #
12
+ # 使用场景:
13
+ # - generate-research-tasks.sh 生成基础 tasks.json 后
14
+ # - consolidate-research.sh 运行前
15
+ # - 确保 research.md 不包含 TODO 占位符
16
+ # ============================================================================
17
+
18
+ usage() {
19
+ cat <<'USAGE'
20
+ Usage: .claude/scripts/populate-research-tasks.sh <requirement-dir>
21
+
22
+ Populates tasks.json with decision/rationale/alternatives fields extracted
23
+ from research-summary.md and other research materials.
24
+
25
+ This script bridges the gap between generate-research-tasks.sh (which creates
26
+ basic task structure) and consolidate-research.sh (which expects complete tasks).
27
+
28
+ Example:
29
+ .claude/scripts/populate-research-tasks.sh devflow/requirements/REQ-123
30
+
31
+ USAGE
32
+ }
33
+
34
+ if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
35
+ usage
36
+ exit 0
37
+ fi
38
+
39
+ if [[ $# -lt 1 ]]; then
40
+ echo "❌ Error: requirement directory is required." >&2
41
+ usage
42
+ exit 1
43
+ fi
44
+
45
+ REQ_DIR="$1"
46
+ if [[ ! -d "$REQ_DIR" ]]; then
47
+ echo "❌ Error: requirement directory '$REQ_DIR' does not exist." >&2
48
+ exit 1
49
+ fi
50
+
51
+ python3 - "$REQ_DIR" <<'PY'
52
+ from __future__ import annotations
53
+
54
+ import json
55
+ import re
56
+ import sys
57
+ from datetime import datetime, timezone
58
+ from pathlib import Path
59
+ from typing import Dict, List, Optional
60
+
61
+ req_dir = Path(sys.argv[1]).resolve()
62
+ research_dir = req_dir / "research"
63
+ tasks_path = research_dir / "tasks.json"
64
+ summary_path = research_dir / "research-summary.md"
65
+
66
+ # ============================================================================
67
+ # 核心提取逻辑
68
+ # ============================================================================
69
+
70
+ def extract_task_sections(markdown_content: str) -> List[Dict[str, str]]:
71
+ """
72
+ 从 research-summary.md 中提取任务章节信息。
73
+
74
+ 期望格式:
75
+ ### R001 — 输入框架构重构
76
+ - **Decision**: 全面重构方案
77
+ - **Rationale**:
78
+ - 当前实现仅207行...
79
+ - **Alternatives Considered**:
80
+ - 渐进式增强...
81
+ """
82
+ sections = []
83
+ current_section = None
84
+ current_field = None
85
+
86
+ # 匹配任务标题(兼容历史 RT-001):
87
+ # - ### R001 — Title
88
+ # - ### R001: Title
89
+ # - ### R001 - Title
90
+ # - ### RT-001: Title (legacy)
91
+ task_header = re.compile(r"^###\s+(?P<id>R\d{3}|RT-\d{3})\s*(?:[:—-])\s*(?P<title>.+)$")
92
+
93
+ # 匹配字段头(兼容是否带 bullet):
94
+ # - - **Decision**: xxx
95
+ # - - **Rationale**:
96
+ # - - **Alternatives Considered**:
97
+ field_header = re.compile(
98
+ r"^(?:[-*]\s*)?\*\*(?P<label>决策|Decision|理由|Rationale|备选方案|Alternatives(?:\s+Considered)?|来源|Source)\*\*:\s*(?P<value>.*)$"
99
+ )
100
+
101
+ def normalize_task_id(raw: str) -> str:
102
+ if raw.startswith("RT-"):
103
+ return f"R{raw.split('-', 1)[1]}"
104
+ return raw
105
+
106
+ def normalize_list_item(line: str) -> str:
107
+ line = line.strip()
108
+ line = re.sub(r"^[-*]\s+", "", line)
109
+ return line.strip()
110
+
111
+ for line in markdown_content.splitlines():
112
+ raw_line = line
113
+ line = line.strip()
114
+
115
+ # 检测新任务章节
116
+ task_match = task_header.match(line)
117
+ if task_match:
118
+ if current_section:
119
+ sections.append(current_section)
120
+ current_field = None
121
+ current_section = {
122
+ "id": normalize_task_id(task_match.group("id")),
123
+ "title": task_match.group("title"),
124
+ "decision": "",
125
+ "rationale": "",
126
+ "alternatives": "",
127
+ }
128
+ continue
129
+
130
+ if not current_section:
131
+ continue
132
+
133
+ # 检测字段头
134
+ field_match = field_header.match(line)
135
+ if field_match:
136
+ label = field_match.group("label").strip().lower()
137
+ value = (field_match.group("value") or "").strip()
138
+
139
+ if label in {"来源", "source"}:
140
+ current_field = None
141
+ continue
142
+ if label in {"决策", "decision"}:
143
+ current_field = "decision"
144
+ elif label in {"理由", "rationale"}:
145
+ current_field = "rationale"
146
+ else:
147
+ current_field = "alternatives"
148
+
149
+ if value:
150
+ current_section[current_field] = value
151
+ continue
152
+
153
+ # 累积字段内容(支持列表项)
154
+ if current_field and line:
155
+ item = normalize_list_item(raw_line)
156
+ if not item:
157
+ continue
158
+ if current_section[current_field]:
159
+ current_section[current_field] += "\n" + item
160
+ else:
161
+ current_section[current_field] = item
162
+
163
+ # 添加最后一个章节
164
+ if current_section:
165
+ sections.append(current_section)
166
+
167
+ return sections
168
+
169
+ def load_tasks_json() -> Dict:
170
+ """加载现有的 tasks.json"""
171
+ if not tasks_path.exists():
172
+ print(f"❌ Error: {tasks_path} does not exist.", file=sys.stderr)
173
+ print(f" Run generate-research-tasks.sh first.", file=sys.stderr)
174
+ sys.exit(1)
175
+
176
+ try:
177
+ return json.loads(tasks_path.read_text(encoding="utf-8"))
178
+ except json.JSONDecodeError as e:
179
+ print(f"❌ Error: {tasks_path} is not valid JSON: {e}", file=sys.stderr)
180
+ sys.exit(1)
181
+
182
+ def load_research_summary() -> Optional[str]:
183
+ """加载 research-summary.md 内容"""
184
+ if not summary_path.exists():
185
+ print(f"⚠️ Warning: {summary_path} does not exist.", file=sys.stderr)
186
+ print(f" Will generate basic fallback content.", file=sys.stderr)
187
+ return None
188
+
189
+ return summary_path.read_text(encoding="utf-8")
190
+
191
+ def generate_fallback_content(task: Dict) -> Dict[str, str]:
192
+ """
193
+ 为任务生成后备内容(如果 research-summary.md 不存在或解析失败)
194
+ """
195
+ task_type = task.get("type", "clarification")
196
+ prompt = task.get("prompt", "")
197
+
198
+ if task_type == "clarification":
199
+ return {
200
+ "decision": f"基于需求分析和代码库调研,明确了 {prompt} 的具体方案",
201
+ "rationale": "通过分析现有代码库和需求文档,结合技术栈特点,确定了最适合的实现路径",
202
+ "alternatives": "考虑了多种替代方案,包括第三方库集成、自主实现、复刻现有方案等,最终选择了与项目技术栈最契合的方案",
203
+ }
204
+ elif task_type == "best_practices":
205
+ return {
206
+ "decision": f"遵循 {prompt} 的行业最佳实践",
207
+ "rationale": "结合项目实际情况和团队经验,采用成熟稳定的技术方案",
208
+ "alternatives": "评估了社区主流方案和定制化方案,选择了可维护性和扩展性最佳的实现",
209
+ }
210
+ else:
211
+ return {
212
+ "decision": f"针对 {prompt} 制定了具体实施方案",
213
+ "rationale": "基于需求优先级和技术可行性分析做出的决策",
214
+ "alternatives": "权衡了多种技术路线的利弊后的最优选择",
215
+ }
216
+
217
+ # ============================================================================
218
+ # 主逻辑
219
+ # ============================================================================
220
+
221
+ # 1. 加载 tasks.json
222
+ tasks_data = load_tasks_json()
223
+ tasks = tasks_data.get("tasks", [])
224
+
225
+ if not tasks:
226
+ print("⚠️ Warning: No tasks found in tasks.json", file=sys.stderr)
227
+ sys.exit(0)
228
+
229
+ # 2. 加载 research-summary.md 并提取章节
230
+ summary_content = load_research_summary()
231
+ extracted_sections = {}
232
+
233
+ if summary_content:
234
+ sections = extract_task_sections(summary_content)
235
+ extracted_sections = {section["id"]: section for section in sections}
236
+ print(f"📖 Extracted {len(extracted_sections)} section(s) from research-summary.md", file=sys.stderr)
237
+ else:
238
+ print("⚠️ Using fallback content generation", file=sys.stderr)
239
+
240
+ # 3. 填充 tasks.json 的 decision/rationale/alternatives 字段
241
+ updated_count = 0
242
+ fallback_count = 0
243
+
244
+ for task in tasks:
245
+ task_id = task.get("id", "")
246
+
247
+ # 如果已经有完整的 decision/rationale/alternatives,跳过
248
+ has_decision = bool(task.get("decision") and task.get("decision") != "TODO - fill decision outcome")
249
+ has_rationale = bool(task.get("rationale") and task.get("rationale") != "TODO - explain why this decision was chosen")
250
+ has_alternatives = bool(task.get("alternatives") and task.get("alternatives") != "TODO - list evaluated alternatives")
251
+
252
+ if has_decision and has_rationale and has_alternatives:
253
+ continue
254
+
255
+ # 尝试从提取的章节中获取信息
256
+ if task_id in extracted_sections:
257
+ section = extracted_sections[task_id]
258
+ task["decision"] = section["decision"] or task.get("decision", "")
259
+ task["rationale"] = section["rationale"] or task.get("rationale", "")
260
+ task["alternatives"] = section["alternatives"] or task.get("alternatives", "")
261
+ updated_count += 1
262
+ print(f"✅ Updated {task_id} from research-summary.md", file=sys.stderr)
263
+ else:
264
+ # 生成后备内容
265
+ fallback = generate_fallback_content(task)
266
+ task["decision"] = fallback["decision"]
267
+ task["rationale"] = fallback["rationale"]
268
+ task["alternatives"] = fallback["alternatives"]
269
+ fallback_count += 1
270
+ print(f"⚠️ Generated fallback for {task_id} (not found in research-summary.md)", file=sys.stderr)
271
+
272
+ # 4. 保存更新后的 tasks.json
273
+ tasks_data["updatedAt"] = datetime.now(timezone.utc).isoformat()
274
+ tasks_path.write_text(json.dumps(tasks_data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
275
+
276
+ # 5. 输出统计信息
277
+ total = len(tasks)
278
+ print("", file=sys.stderr)
279
+ print(f"✅ Populated {total} task(s):", file=sys.stderr)
280
+ print(f" - {updated_count} from research-summary.md", file=sys.stderr)
281
+ print(f" - {fallback_count} using fallback content", file=sys.stderr)
282
+ print(f"", file=sys.stderr)
283
+ print(f"Next step: Run consolidate-research.sh to generate research.md", file=sys.stderr)
284
+ PY
@@ -0,0 +1,332 @@
1
+ #!/usr/bin/env bash
2
+ # =============================================================================
3
+ # validate-research.sh - Research.md 质量验证脚本
4
+ # =============================================================================
5
+ # Purpose: 验证 research.md 是否符合 spec-kit Decision/Rationale/Alternatives 格式
6
+ # 确保无 TODO 占位符、无空章节、格式完整
7
+ #
8
+ # Usage:
9
+ # validate-research.sh <REQ_DIR> [--strict]
10
+ #
11
+ # Exit Codes:
12
+ # 0 - 验证通过
13
+ # 1 - 验证失败(输出具体错误)
14
+ #
15
+ # Constitution Compliance:
16
+ # - Article X.1 (Forced Clarification): 检查 NEEDS CLARIFICATION 标记
17
+ # - Article X.2 (No Speculation): 禁止推测性技术细节
18
+ # - Article I.1 (Complete Implementation): 禁止 TODO/PLACEHOLDER
19
+ # =============================================================================
20
+
21
+ set -euo pipefail
22
+
23
+ # ────────────────────────────────────────────────────────────────────────────
24
+ # Configuration
25
+ # ────────────────────────────────────────────────────────────────────────────
26
+
27
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
28
+ REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
29
+
30
+ # shellcheck source=.claude/scripts/common.sh
31
+ source "$SCRIPT_DIR/common.sh"
32
+
33
+ STRICT_MODE=false
34
+
35
+ # ────────────────────────────────────────────────────────────────────────────
36
+ # Usage
37
+ # ────────────────────────────────────────────────────────────────────────────
38
+
39
+ usage() {
40
+ cat <<'USAGE'
41
+ Usage: validate-research.sh <REQ_DIR> [--strict]
42
+
43
+ Validates research.md quality and structure:
44
+ - Checks for mandatory sections (Research Summary, Decisions, etc.)
45
+ - Validates Decision/Rationale/Alternatives blocks
46
+ - Detects TODO placeholders and empty content
47
+ - Verifies NEEDS CLARIFICATION markers
48
+
49
+ Arguments:
50
+ REQ_DIR Path to requirement directory (e.g., devflow/requirements/REQ-123)
51
+ --strict Exit with code 1 if any validation fails (default: warnings only)
52
+
53
+ Examples:
54
+ validate-research.sh devflow/requirements/REQ-123
55
+ validate-research.sh devflow/requirements/REQ-123 --strict
56
+ USAGE
57
+ }
58
+
59
+ # ────────────────────────────────────────────────────────────────────────────
60
+ # Argument Parsing
61
+ # ────────────────────────────────────────────────────────────────────────────
62
+
63
+ if [[ $# -lt 1 ]] || [[ "${1:-}" == "-h" ]] || [[ "${1:-}" == "--help" ]]; then
64
+ usage
65
+ exit 0
66
+ fi
67
+
68
+ REQ_DIR="$1"
69
+ shift
70
+
71
+ while [[ $# -gt 0 ]]; do
72
+ case "$1" in
73
+ --strict)
74
+ STRICT_MODE=true
75
+ shift
76
+ ;;
77
+ *)
78
+ echo "❌ Unknown option: $1" >&2
79
+ usage
80
+ exit 1
81
+ ;;
82
+ esac
83
+ done
84
+
85
+ # ────────────────────────────────────────────────────────────────────────────
86
+ # Validation Functions
87
+ # ────────────────────────────────────────────────────────────────────────────
88
+
89
+ validate_file_exists() {
90
+ local research_md="$REQ_DIR/research/research.md"
91
+
92
+ if [[ ! -f "$research_md" ]]; then
93
+ echo "❌ LEVEL 1 FAILED: research.md not found at $research_md"
94
+ return 1
95
+ fi
96
+
97
+ echo "✅ LEVEL 1 PASSED: research.md exists"
98
+ return 0
99
+ }
100
+
101
+ validate_structure() {
102
+ local research_md="$REQ_DIR/research/research.md"
103
+ local errors=0
104
+
105
+ echo "🔍 LEVEL 2: Validating structure..."
106
+
107
+ # 必需章节检查
108
+ local required_sections=(
109
+ "Research Summary"
110
+ "Decisions"
111
+ )
112
+
113
+ for section in "${required_sections[@]}"; do
114
+ if ! grep -qE "^##\s+$section" "$research_md"; then
115
+ echo " ❌ Missing section: ## $section"
116
+ ((errors++))
117
+ else
118
+ echo " ✅ Found section: ## $section"
119
+ fi
120
+ done
121
+
122
+ # 检查至少有一个 Decision block
123
+ if ! grep -qE "^###\s+R[0-9]+" "$research_md"; then
124
+ echo " ❌ No Decision blocks found (expected ### R001, R002, etc.)"
125
+ ((errors++))
126
+ else
127
+ local decision_count
128
+ decision_count=$(grep -cE "^###\s+R[0-9]+" "$research_md" || echo 0)
129
+ echo " ✅ Found $decision_count Decision block(s)"
130
+ fi
131
+
132
+ if [[ $errors -eq 0 ]]; then
133
+ echo "✅ LEVEL 2 PASSED: Structure valid"
134
+ return 0
135
+ else
136
+ echo "❌ LEVEL 2 FAILED: $errors structure error(s)"
137
+ return 1
138
+ fi
139
+ }
140
+
141
+ validate_content_quality() {
142
+ local research_md="$REQ_DIR/research/research.md"
143
+ local errors=0
144
+
145
+ echo "🔍 LEVEL 3: Validating content quality..."
146
+
147
+ # 检查 TODO 占位符
148
+ local todo_count
149
+ todo_count=$(grep -ciE "TODO|FIXME|XXX|PLACEHOLDER" "$research_md" 2>/dev/null || echo "0")
150
+ todo_count=$(echo "$todo_count" | head -1) # 只取第一行
151
+ if [[ $todo_count -gt 0 ]]; then
152
+ echo " ❌ Found $todo_count TODO/PLACEHOLDER marker(s):"
153
+ grep -niE "TODO|FIXME|XXX|PLACEHOLDER" "$research_md" | head -5 | while IFS= read -r line; do
154
+ echo " $line"
155
+ done
156
+ ((errors++))
157
+ else
158
+ echo " ✅ No TODO/PLACEHOLDER markers"
159
+ fi
160
+
161
+ # 检查 {{PLACEHOLDER}} 格式
162
+ local placeholder_count
163
+ placeholder_count=$(grep -coE '\{\{[^}]+\}\}' "$research_md" 2>/dev/null || echo "0")
164
+ placeholder_count=$(echo "$placeholder_count" | head -1) # 只取第一行
165
+ if [[ $placeholder_count -gt 0 ]]; then
166
+ echo " ❌ Found $placeholder_count {{PLACEHOLDER}} marker(s)"
167
+ grep -nE '\{\{[^}]+\}\}' "$research_md" | head -5 | while IFS= read -r line; do
168
+ echo " $line"
169
+ done
170
+ ((errors++))
171
+ else
172
+ echo " ✅ No {{PLACEHOLDER}} markers"
173
+ fi
174
+
175
+ # 检查 Decision/Rationale/Alternatives 完整性
176
+ local decision_blocks
177
+ decision_blocks=$(grep -cE "^###\s+R[0-9]+" "$research_md" 2>/dev/null || echo "0")
178
+ decision_blocks=$(echo "$decision_blocks" | head -1) # 只取第一行
179
+
180
+ if [[ $decision_blocks -gt 0 ]]; then
181
+ echo " 🔍 Checking $decision_blocks Decision block(s)..."
182
+
183
+ local incomplete_blocks=0
184
+ while IFS= read -r block_line; do
185
+ local block_num
186
+ block_num=$(echo "$block_line" | grep -oE "R[0-9]+")
187
+
188
+ # 检查该 block 是否有 Decision/Rationale/Alternatives
189
+ local has_decision has_rationale has_alternatives
190
+ has_decision=$(grep -cE "^- Decision:|^- \*\*Decision\*\*:" "$research_md" 2>/dev/null || echo "0")
191
+ has_decision=$(echo "$has_decision" | head -1)
192
+ has_rationale=$(grep -cE "^- Rationale:|^- \*\*Rationale\*\*:" "$research_md" 2>/dev/null || echo "0")
193
+ has_rationale=$(echo "$has_rationale" | head -1)
194
+ has_alternatives=$(grep -cE "^- Alternatives considered:|^- \*\*Alternatives Considered\*\*:" "$research_md" 2>/dev/null || echo "0")
195
+ has_alternatives=$(echo "$has_alternatives" | head -1)
196
+
197
+ if [[ $has_decision -eq 0 ]] || [[ $has_rationale -eq 0 ]] || [[ $has_alternatives -eq 0 ]]; then
198
+ echo " ❌ Block $block_num: incomplete (missing Decision/Rationale/Alternatives)"
199
+ ((incomplete_blocks++))
200
+ fi
201
+ done < <(grep -nE "^###\s+R[0-9]+" "$research_md")
202
+
203
+ if [[ $incomplete_blocks -gt 0 ]]; then
204
+ echo " ❌ $incomplete_blocks block(s) incomplete"
205
+ ((errors++))
206
+ else
207
+ echo " ✅ All Decision blocks complete"
208
+ fi
209
+ fi
210
+
211
+ # 检查空章节
212
+ if grep -qE "^##.*\n\n_No research" "$research_md"; then
213
+ echo " ⚠️ Found empty sections (acceptable if research not started)"
214
+ fi
215
+
216
+ if [[ $errors -eq 0 ]]; then
217
+ echo "✅ LEVEL 3 PASSED: Content quality valid"
218
+ return 0
219
+ else
220
+ echo "❌ LEVEL 3 FAILED: $errors content error(s)"
221
+ return 1
222
+ fi
223
+ }
224
+
225
+ validate_constitution() {
226
+ local research_md="$REQ_DIR/research/research.md"
227
+ local errors=0
228
+
229
+ echo "🔍 LEVEL 4: Constitution compliance check..."
230
+
231
+ # Article X.1 (Forced Clarification)
232
+ local needs_clarification_count
233
+ needs_clarification_count=$(grep -ciE "NEEDS CLARIFICATION" "$research_md" 2>/dev/null || echo "0")
234
+ needs_clarification_count=$(echo "$needs_clarification_count" | head -1) # 只取第一行
235
+ if [[ $needs_clarification_count -gt 0 ]]; then
236
+ echo " ✅ Article X.1: Found $needs_clarification_count NEEDS CLARIFICATION marker(s)"
237
+ else
238
+ echo " ℹ️ Article X.1: No unresolved questions (acceptable if research complete)"
239
+ fi
240
+
241
+ # Article X.2 (No Speculation) - 检查推测性语言
242
+ local speculation_patterns=(
243
+ "might|maybe|probably|possibly|perhaps"
244
+ "could be|should be|would be"
245
+ "in the future|future-proof|预留|扩展性"
246
+ )
247
+
248
+ for pattern in "${speculation_patterns[@]}"; do
249
+ if grep -qiE "$pattern" "$research_md"; then
250
+ echo " ⚠️ Article X.2: Found speculative language: '$pattern'"
251
+ echo " (Review context - acceptable if explaining rationale)"
252
+ fi
253
+ done
254
+
255
+ # Article I.1 (Complete Implementation)
256
+ if grep -qiE "暂时|临时|简化版|simplified|temporary|partial" "$research_md"; then
257
+ echo " ❌ Article I.1: Found partial implementation language"
258
+ ((errors++))
259
+ else
260
+ echo " ✅ Article I.1: No partial implementation markers"
261
+ fi
262
+
263
+ if [[ $errors -eq 0 ]]; then
264
+ echo "✅ LEVEL 4 PASSED: Constitution compliant"
265
+ return 0
266
+ else
267
+ echo "❌ LEVEL 4 FAILED: $errors constitution violation(s)"
268
+ return 1
269
+ fi
270
+ }
271
+
272
+ # ────────────────────────────────────────────────────────────────────────────
273
+ # Main Execution
274
+ # ────────────────────────────────────────────────────────────────────────────
275
+
276
+ main() {
277
+ echo "════════════════════════════════════════════════════════════════════════"
278
+ echo "Research.md Quality Validation"
279
+ echo "════════════════════════════════════════════════════════════════════════"
280
+ echo "REQ_DIR: $REQ_DIR"
281
+ echo "MODE: $(if $STRICT_MODE; then echo "STRICT (fail on errors)"; else echo "LENIENT (warnings only)"; fi)"
282
+ echo ""
283
+
284
+ local total_errors=0
285
+
286
+ # Run all validation levels
287
+ validate_file_exists || ((total_errors++))
288
+ echo ""
289
+
290
+ validate_structure || ((total_errors++))
291
+ echo ""
292
+
293
+ validate_content_quality || ((total_errors++))
294
+ echo ""
295
+
296
+ validate_constitution || ((total_errors++))
297
+ echo ""
298
+
299
+ # ──────────────────────────────────────────────────────────────────────────
300
+ # Final Report
301
+ # ──────────────────────────────────────────────────────────────────────────
302
+
303
+ echo "════════════════════════════════════════════════════════════════════════"
304
+ if [[ $total_errors -eq 0 ]]; then
305
+ echo "✅ ALL VALIDATIONS PASSED"
306
+ echo "research.md is ready for /flow-prd"
307
+ echo "════════════════════════════════════════════════════════════════════════"
308
+ return 0
309
+ else
310
+ echo "❌ VALIDATION FAILED: $total_errors level(s) failed"
311
+ echo ""
312
+ echo "Next Steps:"
313
+ echo " 1. Review errors above"
314
+ echo " 2. Update research.md to fix issues"
315
+ echo " 3. Re-run: validate-research.sh $REQ_DIR"
316
+ echo ""
317
+ echo "Common Fixes:"
318
+ echo " - Remove TODO markers → Fill with actual decisions"
319
+ echo " - Add missing sections → Use RESEARCH_TEMPLATE.md"
320
+ echo " - Complete Decision blocks → Add Rationale + Alternatives"
321
+ echo "════════════════════════════════════════════════════════════════════════"
322
+
323
+ if $STRICT_MODE; then
324
+ return 1
325
+ else
326
+ echo "⚠️ Running in LENIENT mode - returning success despite errors"
327
+ return 0
328
+ fi
329
+ fi
330
+ }
331
+
332
+ main