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,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
|