Compare commits
4 Commits
73d5c261b1
...
816f036abe
| Author | SHA1 | Date |
|---|---|---|
|
|
816f036abe | |
|
|
625cabbd63 | |
|
|
2554c879e4 | |
|
|
6774a9d4aa |
|
|
@ -1,29 +1,31 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from datetime import datetime
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
PLAN_PREFIX = "[PLAN]"
|
PLAN_STATUS_START = "<!-- plan-status:start -->"
|
||||||
PLAN_SECTION_HEADER = "## Plan 状态记录"
|
PLAN_STATUS_END = "<!-- plan-status:end -->"
|
||||||
PLAN_FILE_RE = re.compile(r"^(\d{4}-\d{2}-\d{2})-.+\.md$")
|
PLAN_FILE_RE = re.compile(r"^(\d{4}-\d{2}-\d{2})-.+\.md$")
|
||||||
VALID_STATUSES = {"in-progress", "done", "blocked"}
|
PLAN_LINE_RE = re.compile(
|
||||||
|
r"^- \[(?P<check>[ xX])\] `(?P<plan>[^`]+)` (?P<status>done|blocked|pending)(?:: (?P<note>.*))?$"
|
||||||
|
)
|
||||||
|
VALID_STATUSES = {"done", "blocked", "pending"}
|
||||||
|
|
||||||
|
|
||||||
def usage() -> str:
|
def usage() -> str:
|
||||||
return (
|
return (
|
||||||
"Usage:\\n"
|
"Usage:\n"
|
||||||
" python scripts/plan_progress.py select -plans <dir> -progress <file>\\n"
|
" python scripts/plan_progress.py select -plans <dir> -progress <file>\n"
|
||||||
" python scripts/plan_progress.py record -plan <path> -status <status> -progress <file> [-note <text>]\\n"
|
" python scripts/plan_progress.py record -plan <path> -status <status> -progress <file> [-note <text>]\n"
|
||||||
" python scripts/plan_progress.py -h\\n"
|
" python scripts/plan_progress.py -h\n"
|
||||||
"Options:\\n"
|
"Options:\n"
|
||||||
" -plans DIR\\n"
|
" -plans DIR\n"
|
||||||
" -plan PATH\\n"
|
" -plan PATH\n"
|
||||||
" -status in-progress|done|blocked\\n"
|
" -status done|blocked|pending\n"
|
||||||
" -progress FILE\\n"
|
" -progress FILE\n"
|
||||||
" -note TEXT\\n"
|
" -note TEXT\n"
|
||||||
" -h, -help Show this help.\\n"
|
" -h, -help Show this help.\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -43,102 +45,123 @@ def parse_flags(args: list[str]) -> dict[str, str]:
|
||||||
return flags
|
return flags
|
||||||
|
|
||||||
|
|
||||||
def normalize_plan_key(plan_value: str, cwd: Path) -> str:
|
def normalize_plan_key(plan_value: str) -> str:
|
||||||
try:
|
raw = plan_value.strip().replace("\\", "/")
|
||||||
return Path(plan_value).resolve().relative_to(cwd.resolve()).as_posix()
|
raw = raw.lstrip("./")
|
||||||
except ValueError:
|
if raw.startswith("docs/plans/"):
|
||||||
return Path(plan_value).as_posix()
|
return raw[len("docs/plans/") :]
|
||||||
|
marker = "/docs/plans/"
|
||||||
|
if marker in raw:
|
||||||
|
return raw.split(marker, 1)[1]
|
||||||
|
return raw
|
||||||
|
|
||||||
|
|
||||||
def load_plan_records(progress_path: Path, cwd: Path) -> dict[str, str]:
|
def render_plan_line(plan_key: str, status: str, note: Optional[str]) -> str:
|
||||||
if not progress_path.exists():
|
checked = "x" if status == "done" else " "
|
||||||
return {}
|
if status == "blocked":
|
||||||
text = progress_path.read_text(encoding="utf-8")
|
suffix = "blocked"
|
||||||
records: dict[str, str] = {}
|
if note:
|
||||||
for line in text.splitlines():
|
suffix += f": {note}"
|
||||||
if not line.startswith(PLAN_PREFIX):
|
elif status == "pending":
|
||||||
continue
|
suffix = "pending"
|
||||||
payload = line[len(PLAN_PREFIX) :].strip()
|
else:
|
||||||
if not payload:
|
suffix = "done"
|
||||||
continue
|
return f"- [{checked}] `{plan_key}` {suffix}"
|
||||||
segments = [seg.strip() for seg in payload.split("|")]
|
|
||||||
if not segments:
|
|
||||||
continue
|
|
||||||
plan_path = segments[0]
|
|
||||||
status = None
|
|
||||||
for seg in segments[1:]:
|
|
||||||
if "=" not in seg:
|
|
||||||
continue
|
|
||||||
key, value = seg.split("=", 1)
|
|
||||||
if key.strip() == "status":
|
|
||||||
status = value.strip()
|
|
||||||
if not plan_path or status is None:
|
|
||||||
continue
|
|
||||||
records[normalize_plan_key(plan_path, cwd)] = status
|
|
||||||
return records
|
|
||||||
|
|
||||||
|
|
||||||
def list_plan_files(plans_dir: Path, cwd: Path) -> list[tuple[str, Path, str]]:
|
|
||||||
entries: list[tuple[str, Path, str]] = []
|
|
||||||
for path in plans_dir.iterdir():
|
|
||||||
if not path.is_file():
|
|
||||||
continue
|
|
||||||
match = PLAN_FILE_RE.match(path.name)
|
|
||||||
if not match:
|
|
||||||
continue
|
|
||||||
date_value = match.group(1)
|
|
||||||
try:
|
|
||||||
rel = path.resolve().relative_to(cwd.resolve()).as_posix()
|
|
||||||
except ValueError:
|
|
||||||
rel = path.as_posix()
|
|
||||||
entries.append((date_value, path, rel))
|
|
||||||
return entries
|
|
||||||
|
|
||||||
|
|
||||||
def select_plan(plans_dir: Path, progress_path: Path) -> tuple[int, str]:
|
|
||||||
cwd = Path.cwd()
|
|
||||||
if not plans_dir.is_dir():
|
|
||||||
return 2, f"ERROR: plans dir not found: {plans_dir}"
|
|
||||||
plans = list_plan_files(plans_dir, cwd)
|
|
||||||
if not plans:
|
|
||||||
return 2, "ERROR: no plan files found"
|
|
||||||
|
|
||||||
records = load_plan_records(progress_path, cwd)
|
|
||||||
|
|
||||||
in_progress = [item for item in plans if records.get(item[2]) == "in-progress"]
|
|
||||||
if in_progress:
|
|
||||||
in_progress.sort(key=lambda item: (item[0], item[2]))
|
|
||||||
return 0, in_progress[-1][2]
|
|
||||||
|
|
||||||
pending = [
|
|
||||||
item
|
|
||||||
for item in plans
|
|
||||||
if records.get(item[2]) not in ("done", "blocked")
|
|
||||||
]
|
|
||||||
if not pending:
|
|
||||||
return 2, "ERROR: no pending plans"
|
|
||||||
|
|
||||||
pending.sort(key=lambda item: (item[0], item[2]))
|
|
||||||
return 0, pending[-1][2]
|
|
||||||
|
|
||||||
|
|
||||||
def ensure_plan_section(text: str) -> str:
|
|
||||||
if PLAN_SECTION_HEADER in text:
|
|
||||||
return text
|
|
||||||
suffix = text
|
|
||||||
if suffix and not suffix.endswith("\n"):
|
|
||||||
suffix += "\n"
|
|
||||||
if suffix:
|
|
||||||
suffix += "\n"
|
|
||||||
suffix += PLAN_SECTION_HEADER + "\n"
|
|
||||||
return suffix
|
|
||||||
|
|
||||||
|
|
||||||
def normalize_note(note: str) -> str:
|
def normalize_note(note: str) -> str:
|
||||||
cleaned = note.replace("\n", " ").replace("|", " ").strip()
|
cleaned = note.replace("\n", " ").replace("\r", " ").replace("`", "'").strip()
|
||||||
return cleaned
|
return cleaned
|
||||||
|
|
||||||
|
|
||||||
|
def list_plan_files(plans_dir: Path) -> list[str]:
|
||||||
|
entries: list[str] = []
|
||||||
|
for path in plans_dir.iterdir():
|
||||||
|
if not path.is_file():
|
||||||
|
continue
|
||||||
|
if not PLAN_FILE_RE.match(path.name):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
rel = path.resolve().relative_to(plans_dir.resolve()).as_posix()
|
||||||
|
except ValueError:
|
||||||
|
rel = path.name
|
||||||
|
entries.append(rel)
|
||||||
|
return sorted(entries)
|
||||||
|
|
||||||
|
|
||||||
|
def find_block(lines: list[str]) -> Optional[tuple[int, int]]:
|
||||||
|
start_idx = None
|
||||||
|
for idx, line in enumerate(lines):
|
||||||
|
if line.strip() == PLAN_STATUS_START:
|
||||||
|
start_idx = idx
|
||||||
|
break
|
||||||
|
if start_idx is None:
|
||||||
|
return None
|
||||||
|
for idx in range(start_idx + 1, len(lines)):
|
||||||
|
if lines[idx].strip() == PLAN_STATUS_END:
|
||||||
|
return start_idx, idx
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def parse_entries(lines: list[str], start_idx: int, end_idx: int) -> list[tuple[str, str, Optional[str], int]]:
|
||||||
|
entries: list[tuple[str, str, Optional[str], int]] = []
|
||||||
|
for idx in range(start_idx + 1, end_idx):
|
||||||
|
line = lines[idx].strip()
|
||||||
|
match = PLAN_LINE_RE.match(line)
|
||||||
|
if not match:
|
||||||
|
continue
|
||||||
|
plan_key = normalize_plan_key(match.group("plan"))
|
||||||
|
status = match.group("status")
|
||||||
|
note = match.group("note")
|
||||||
|
entries.append((plan_key, status, note, idx))
|
||||||
|
return entries
|
||||||
|
|
||||||
|
|
||||||
|
def render_progress_lines(plans: list[str]) -> list[str]:
|
||||||
|
lines = ["# Plan 状态", "", PLAN_STATUS_START]
|
||||||
|
for plan_key in plans:
|
||||||
|
lines.append(render_plan_line(plan_key, "pending", None))
|
||||||
|
lines.append(PLAN_STATUS_END)
|
||||||
|
return lines
|
||||||
|
|
||||||
|
|
||||||
|
def select_plan(plans_dir: Path, progress_path: Path) -> tuple[int, str]:
|
||||||
|
if not plans_dir.is_dir():
|
||||||
|
return 2, f"ERROR: plans dir not found: {plans_dir}"
|
||||||
|
plan_keys = list_plan_files(plans_dir)
|
||||||
|
if not plan_keys:
|
||||||
|
return 2, "ERROR: no plan files found"
|
||||||
|
|
||||||
|
progress_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
if progress_path.exists():
|
||||||
|
lines = progress_path.read_text(encoding="utf-8").splitlines()
|
||||||
|
else:
|
||||||
|
lines = []
|
||||||
|
|
||||||
|
block = find_block(lines)
|
||||||
|
if not block:
|
||||||
|
lines = render_progress_lines(plan_keys)
|
||||||
|
progress_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||||
|
return 0, (plans_dir / plan_keys[0]).as_posix()
|
||||||
|
|
||||||
|
start_idx, end_idx = block
|
||||||
|
entries = parse_entries(lines, start_idx, end_idx)
|
||||||
|
existing = {plan for plan, _, _, _ in entries}
|
||||||
|
missing = [plan for plan in plan_keys if plan not in existing]
|
||||||
|
if missing:
|
||||||
|
insert_lines = [render_plan_line(plan, "pending", None) for plan in missing]
|
||||||
|
lines[end_idx:end_idx] = insert_lines
|
||||||
|
end_idx += len(insert_lines)
|
||||||
|
progress_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||||
|
entries = parse_entries(lines, start_idx, end_idx)
|
||||||
|
|
||||||
|
for plan_key, status, _, _ in entries:
|
||||||
|
if status == "pending":
|
||||||
|
return 0, (plans_dir / plan_key).as_posix()
|
||||||
|
|
||||||
|
return 2, "ERROR: no pending plans"
|
||||||
|
|
||||||
|
|
||||||
def record_status(plan: str, status: str, progress_path: Path, note: Optional[str]) -> tuple[int, str]:
|
def record_status(plan: str, status: str, progress_path: Path, note: Optional[str]) -> tuple[int, str]:
|
||||||
if status not in VALID_STATUSES:
|
if status not in VALID_STATUSES:
|
||||||
return 2, f"ERROR: invalid status: {status}"
|
return 2, f"ERROR: invalid status: {status}"
|
||||||
|
|
@ -147,24 +170,38 @@ def record_status(plan: str, status: str, progress_path: Path, note: Optional[st
|
||||||
|
|
||||||
progress_path.parent.mkdir(parents=True, exist_ok=True)
|
progress_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
if progress_path.exists():
|
if progress_path.exists():
|
||||||
text = progress_path.read_text(encoding="utf-8")
|
lines = progress_path.read_text(encoding="utf-8").splitlines()
|
||||||
else:
|
else:
|
||||||
text = "# 开发进度追踪\n"
|
lines = []
|
||||||
|
|
||||||
text = ensure_plan_section(text)
|
plan_key = normalize_plan_key(plan)
|
||||||
if not text.endswith("\n"):
|
block = find_block(lines)
|
||||||
text += "\n"
|
if not block:
|
||||||
|
lines = render_progress_lines([plan_key])
|
||||||
|
block = find_block(lines)
|
||||||
|
if not block:
|
||||||
|
return 2, "ERROR: failed to create plan status block"
|
||||||
|
|
||||||
date_value = datetime.now().strftime("%Y-%m-%d")
|
start_idx, end_idx = block
|
||||||
plan_path = Path(plan).as_posix()
|
entries = parse_entries(lines, start_idx, end_idx)
|
||||||
line = f"{PLAN_PREFIX} {plan_path} | status={status} | date={date_value}"
|
|
||||||
if note:
|
rendered_note = None
|
||||||
cleaned = normalize_note(note)
|
if status == "blocked" and note:
|
||||||
if cleaned:
|
rendered_note = normalize_note(note)
|
||||||
line += f" | note={cleaned}"
|
|
||||||
text += line + "\n"
|
updated_line = render_plan_line(plan_key, status, rendered_note)
|
||||||
progress_path.write_text(text, encoding="utf-8")
|
updated = False
|
||||||
return 0, line
|
for entry_plan, _, _, idx in entries:
|
||||||
|
if entry_plan == plan_key:
|
||||||
|
lines[idx] = updated_line
|
||||||
|
updated = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not updated:
|
||||||
|
lines[end_idx:end_idx] = [updated_line]
|
||||||
|
|
||||||
|
progress_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||||
|
return 0, updated_line
|
||||||
|
|
||||||
|
|
||||||
def main(argv: list[str]) -> int:
|
def main(argv: list[str]) -> int:
|
||||||
|
|
@ -222,4 +259,4 @@ def main(argv: list[str]) -> int:
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sys.exit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@
|
||||||
### 工作流程
|
### 工作流程
|
||||||
|
|
||||||
- [docs/prompts/coding/clarify.md](docs/prompts/coding/clarify.md) - 需求澄清
|
- [docs/prompts/coding/clarify.md](docs/prompts/coding/clarify.md) - 需求澄清
|
||||||
- [docs/prompts/coding/verify.md](docs/prompts/coding/verify.md) - 验证检查
|
- [docs/prompts/coding/review.md](docs/prompts/coding/review.md) - 复盘总结
|
||||||
- [docs/prompts/system/agent-behavior.md](docs/prompts/system/agent-behavior.md) - AI 行为规范
|
- [docs/prompts/system/agent-behavior.md](docs/prompts/system/agent-behavior.md) - 工作模式参考
|
||||||
<!-- playbook:templates:end -->
|
<!-- playbook:templates:end -->
|
||||||
|
|
||||||
<!-- playbook:framework:end -->
|
<!-- playbook:framework:end -->
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# AGENT_RULES
|
# AGENT_RULES
|
||||||
|
|
||||||
目的:为本仓库提供稳定的执行流程。
|
目的:为本仓库提供稳定的执行流程与行为规范。
|
||||||
|
|
||||||
## 优先级
|
## 优先级
|
||||||
|
|
||||||
|
|
@ -14,6 +14,35 @@
|
||||||
- 不得在代码/日志/注释中写入明文密钥、密码、Token
|
- 不得在代码/日志/注释中写入明文密钥、密码、Token
|
||||||
- 修改鉴权/权限逻辑必须说明动机与风险
|
- 修改鉴权/权限逻辑必须说明动机与风险
|
||||||
- 不确定是否敏感时按敏感信息处理
|
- 不确定是否敏感时按敏感信息处理
|
||||||
|
- 执行修改文件系统的命令前,必须解释目的和潜在影响
|
||||||
|
|
||||||
|
## 行为准则
|
||||||
|
|
||||||
|
### 项目适应
|
||||||
|
|
||||||
|
- **模仿项目风格**:优先分析周围代码和配置,遵循现有约定
|
||||||
|
- **不假设可用性**:不假设库或框架可用,先验证再使用
|
||||||
|
- **完整完成请求**:不遗漏用户要求的任何部分
|
||||||
|
|
||||||
|
### 技术态度
|
||||||
|
|
||||||
|
- **准确性优先**:技术准确性优先于迎合用户
|
||||||
|
- **诚实纠正**:发现用户理解有误时,礼貌纠正
|
||||||
|
- **先查后答**:不确定时先调查再回答
|
||||||
|
|
||||||
|
### 避免过度工程
|
||||||
|
|
||||||
|
- **只做要求的**:不主动添加未要求的功能或重构
|
||||||
|
- **不过度抽象**:不为一次性操作创建工具函数
|
||||||
|
- **不为未来设计**:不为假设的未来需求设计
|
||||||
|
|
||||||
|
## 沟通原则
|
||||||
|
|
||||||
|
- **简洁直接**:专业、直接、简洁,避免对话填充词
|
||||||
|
- **拒绝时提供替代**:无法满足请求时,简洁说明并提供替代方案
|
||||||
|
- **不给时间估算**:专注任务本身,让用户自己判断时间
|
||||||
|
- **代码块标注语言**:输出代码时标注语言类型
|
||||||
|
- **不使用 emoji**:除非用户明确要求
|
||||||
|
|
||||||
## 上下文加载(每次会话开始)
|
## 上下文加载(每次会话开始)
|
||||||
|
|
||||||
|
|
@ -30,23 +59,47 @@
|
||||||
|
|
||||||
**目的**:让 AI 快速理解项目全貌,避免重复解释。
|
**目的**:让 AI 快速理解项目全貌,避免重复解释。
|
||||||
|
|
||||||
|
## 规划与执行分工
|
||||||
|
|
||||||
|
| 阶段 | 工具 | 产出 | 留痕 |
|
||||||
|
| ------------ | ---------------------- | ----------------- | -------------------- |
|
||||||
|
| 头脑风暴 | `$brainstorming` skill | 设计思路 | 无 |
|
||||||
|
| 生成计划 | `$writing-plans` skill | `docs/plans/*.md` | 无 |
|
||||||
|
| **执行计划** | **主循环** | 代码/配置变更 | **plan_progress.py** |
|
||||||
|
|
||||||
|
> **重要**:第三方 skills 不记录操作状态,执行必须通过主循环完成。
|
||||||
|
|
||||||
## 主循环
|
## 主循环
|
||||||
|
|
||||||
|
**触发词**:
|
||||||
|
|
||||||
|
| 触发词 | 模式 | 说明 |
|
||||||
|
| --------------------------------------- | ---------- | ---------------------- |
|
||||||
|
| `执行主循环`、`继续执行`、`下一个 Plan` | 常规模式 | 遇确认场景可询问用户 |
|
||||||
|
| `自动执行所有 Plan` | 无交互模式 | 不询问,按规则自动处理 |
|
||||||
|
|
||||||
0. 选择 Plan:
|
0. 选择 Plan:
|
||||||
- 运行 `python {{PLAYBOOK_SCRIPTS}}/plan_progress.py select -plans docs/plans -progress memory-bank/progress.md`
|
- 运行 `python {{PLAYBOOK_SCRIPTS}}/plan_progress.py select -plans docs/plans -progress memory-bank/progress.md`
|
||||||
- 如无可执行 Plan,说明情况并询问用户下一步(新增 Plan/切换任务/结束)
|
- 如无可执行 Plan,跳到步骤 4
|
||||||
1. 标记开始:
|
1. 阅读 Plan:
|
||||||
- `python {{PLAYBOOK_SCRIPTS}}/plan_progress.py record -plan <plan> -status in-progress -progress memory-bank/progress.md`
|
|
||||||
2. 阅读 Plan:
|
|
||||||
- 理解目标、子任务与验证标准
|
- 理解目标、子任务与验证标准
|
||||||
3. 逐步执行:
|
2. 逐步执行:
|
||||||
- 按顺序执行子任务
|
- 按顺序执行子任务
|
||||||
- 每步完成后进行必要验证(测试/日志/diff)
|
- 每步完成后进行必要验证(测试/日志/diff)
|
||||||
- 遇到阻塞立即记录并停止
|
- 遇到歧义/风险/决策点:
|
||||||
4. 记录结果(写入 `memory-bank/progress.md`):
|
- 常规模式:记录到回复中,可询问用户
|
||||||
|
- 无交互模式:按「需要确认的场景」规则自动处理
|
||||||
|
- 遇到阻塞:记录原因,跳到步骤 3 标记 blocked
|
||||||
|
- **安全红线阻塞**(发现明文密钥等):立即停止,不继续后续 Plan
|
||||||
|
3. 记录结果:
|
||||||
- 完成:`python {{PLAYBOOK_SCRIPTS}}/plan_progress.py record -plan <plan> -status done -progress memory-bank/progress.md`
|
- 完成:`python {{PLAYBOOK_SCRIPTS}}/plan_progress.py record -plan <plan> -status done -progress memory-bank/progress.md`
|
||||||
- 阻塞:`python {{PLAYBOOK_SCRIPTS}}/plan_progress.py record -plan <plan> -status blocked -progress memory-bank/progress.md -note <原因>`
|
- 阻塞:`python {{PLAYBOOK_SCRIPTS}}/plan_progress.py record -plan <plan> -status blocked -progress memory-bank/progress.md -note <原因>`
|
||||||
5. 如存在歧义/风险/决策点,在回复中明确提出,并视需要记录到 `memory-bank/decisions.md`
|
- 回到步骤 0 继续下一个 Plan
|
||||||
|
4. 汇总报告(所有 Plan 处理完毕后):
|
||||||
|
- 列出已完成的 Plan
|
||||||
|
- 列出阻塞的 Plan 及原因
|
||||||
|
- 列出待确认的歧义/风险/决策点
|
||||||
|
- 如需记录重要决策,写入 `memory-bank/decisions.md`
|
||||||
|
|
||||||
## Plan 规则
|
## Plan 规则
|
||||||
|
|
||||||
|
|
@ -64,20 +117,37 @@
|
||||||
|
|
||||||
## 执行约束
|
## 执行约束
|
||||||
|
|
||||||
### 代码修改约束
|
### 代码修改
|
||||||
|
|
||||||
- **必须先读文件再修改**:不读文件就提议修改是禁止的
|
- **必须先读文件再修改**:不读文件就提议修改是禁止的
|
||||||
- **必须运行测试验证**:相关测试必须通过
|
- **必须运行测试验证**:相关测试必须通过
|
||||||
- **遵循换行规则**:遵循 `.gitattributes` 规则
|
- **遵循换行规则**:遵循 `.gitattributes` 规则
|
||||||
|
- **命名一致性**:遵循项目现有的命名风格
|
||||||
|
- **最小改动原则**:只修改必要的部分,不顺手重构
|
||||||
|
|
||||||
### 决策记录约束
|
### 决策记录
|
||||||
|
|
||||||
- **重要决策**:记录到 `memory-bank/decisions.md`(ADR 格式)
|
- **重要决策**:记录到 `memory-bank/decisions.md`(ADR 格式)
|
||||||
- **待确认事项**:在回复中列出并等待确认
|
- **待确认事项**:在回复中列出并等待确认
|
||||||
- **进度留痕**:通过 `{{PLAYBOOK_SCRIPTS}}/plan_progress.py` 写入 `memory-bank/progress.md`,该文件为 Plan 状态唯一权威
|
- **进度留痕**:通过 `{{PLAYBOOK_SCRIPTS}}/plan_progress.py` 维护 `memory-bank/progress.md` 的 Plan 状态块(唯一权威)
|
||||||
|
|
||||||
|
### Git 操作
|
||||||
|
|
||||||
|
- **不使用 --amend**:除非用户明确要求,总是创建新提交
|
||||||
|
- **不使用 --force**:特别是推送到 main/master,如用户要求必须警告风险
|
||||||
|
- **不跳过 hooks**:不使用 `--no-verify`
|
||||||
|
|
||||||
|
## 工具使用
|
||||||
|
|
||||||
|
- **并行执行**:独立的工具调用尽可能并行执行
|
||||||
|
- **遵循 schema**:严格遵循工具参数定义
|
||||||
|
- **避免循环**:避免重复调用同一工具获取相同信息
|
||||||
|
- **优先专用工具**:文件操作用 Read/Edit/Write,搜索用 Grep/Glob
|
||||||
|
|
||||||
## 需要确认的场景
|
## 需要确认的场景
|
||||||
|
|
||||||
|
**常规模式**(可交互):
|
||||||
|
|
||||||
- 需求不明确或存在多种可行方案
|
- 需求不明确或存在多种可行方案
|
||||||
- 需要行为/兼容性取舍
|
- 需要行为/兼容性取舍
|
||||||
- 风险或约束冲突
|
- 风险或约束冲突
|
||||||
|
|
@ -85,6 +155,21 @@
|
||||||
- **性能权衡**:需要在性能和可维护性之间选择
|
- **性能权衡**:需要在性能和可维护性之间选择
|
||||||
- **兼容性问题**:可能破坏现有用户代码
|
- **兼容性问题**:可能破坏现有用户代码
|
||||||
|
|
||||||
|
**无交互模式**(自动处理):
|
||||||
|
|
||||||
|
| 场景 | 处理方式 |
|
||||||
|
| -------------------------- | ---------------------------------- |
|
||||||
|
| 安全红线 | 立即停止,不继续后续 Plan |
|
||||||
|
| 架构变更/兼容性/破坏性修改 | 标记 blocked,跳到下一个 Plan |
|
||||||
|
| 多种可行方案 | 选择最保守方案,记录选择理由到报告 |
|
||||||
|
| 歧义/风险/决策点 | 记录到报告,继续执行 |
|
||||||
|
|
||||||
|
**可以不确认**(两种模式通用):
|
||||||
|
|
||||||
|
- 明显的 bug 修复
|
||||||
|
- 符合现有模式的小改动
|
||||||
|
- 测试用例补充
|
||||||
|
|
||||||
## 验证清单
|
## 验证清单
|
||||||
|
|
||||||
每个 Plan 完成后,必须验证:
|
每个 Plan 完成后,必须验证:
|
||||||
|
|
@ -93,7 +178,7 @@
|
||||||
- [ ] 相关测试通过(如有测试且未被豁免)
|
- [ ] 相关测试通过(如有测试且未被豁免)
|
||||||
- [ ] 换行符正确
|
- [ ] 换行符正确
|
||||||
- [ ] 无语法错误
|
- [ ] 无语法错误
|
||||||
- [ ] 已更新 `memory-bank/progress.md`
|
- [ ] 已通过 `plan_progress.py` 记录 Plan 状态
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,109 +1,43 @@
|
||||||
# 架构设计
|
# 架构设计
|
||||||
|
|
||||||
|
<!--
|
||||||
|
填写指南:
|
||||||
|
- 【必填】:项目启动前必须填写
|
||||||
|
- 【可选】:按需填写,可随项目发展补充
|
||||||
|
- 小项目可只填核心模块,架构图可后补
|
||||||
|
-->
|
||||||
|
|
||||||
## 整体架构
|
## 整体架构
|
||||||
|
|
||||||
|
<!-- 【可选】项目成熟后补充 -->
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
{{ARCHITECTURE_DIAGRAM}}
|
||||||
│ {{LAYER_1}} │
|
|
||||||
└─────────────────────┬───────────────────────────────────────┘
|
|
||||||
│
|
|
||||||
↓
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ {{LAYER_2}} │
|
|
||||||
└─────────────────────┬───────────────────────────────────────┘
|
|
||||||
│
|
|
||||||
↓
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ {{LAYER_3}} │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 核心模块
|
## 核心模块
|
||||||
|
|
||||||
### 1. {{MODULE_1}}
|
<!-- 【必填】至少列出主要模块 -->
|
||||||
|
|
||||||
|
### {{MODULE_1}}
|
||||||
|
|
||||||
**职责**:{{MODULE_1_DESC}}
|
**职责**:{{MODULE_1_DESC}}
|
||||||
|
|
||||||
**主要组件**:
|
|
||||||
|
|
||||||
- {{COMPONENT_1}}
|
|
||||||
- {{COMPONENT_2}}
|
|
||||||
|
|
||||||
**核心方法**:
|
|
||||||
|
|
||||||
- {{METHOD_1}}
|
|
||||||
- {{METHOD_2}}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. {{MODULE_2}}
|
|
||||||
|
|
||||||
**职责**:{{MODULE_2_DESC}}
|
|
||||||
|
|
||||||
**主要组件**:
|
|
||||||
|
|
||||||
- {{COMPONENT_3}}
|
|
||||||
- {{COMPONENT_4}}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. {{MODULE_3}}
|
|
||||||
|
|
||||||
**职责**:{{MODULE_3_DESC}}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 设计模式
|
|
||||||
|
|
||||||
### {{PATTERN_1}}
|
|
||||||
|
|
||||||
**应用**:{{PATTERN_1_USAGE}}
|
|
||||||
|
|
||||||
**目的**:{{PATTERN_1_PURPOSE}}
|
|
||||||
|
|
||||||
**优点**:
|
|
||||||
|
|
||||||
- {{PATTERN_1_ADVANTAGE_1}}
|
|
||||||
- {{PATTERN_1_ADVANTAGE_2}}
|
|
||||||
|
|
||||||
### {{PATTERN_2}}
|
|
||||||
|
|
||||||
**应用**:{{PATTERN_2_USAGE}}
|
|
||||||
|
|
||||||
**目的**:{{PATTERN_2_PURPOSE}}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 关键约束
|
## 关键约束
|
||||||
|
|
||||||
### 1. {{CONSTRAINT_CATEGORY_1}}
|
<!-- 【可选】 -->
|
||||||
|
|
||||||
- {{CONSTRAINT_1}}
|
- {{CONSTRAINT_1}}
|
||||||
- {{CONSTRAINT_2}}
|
|
||||||
|
|
||||||
### 2. {{CONSTRAINT_CATEGORY_2}}
|
|
||||||
|
|
||||||
- {{CONSTRAINT_3}}
|
|
||||||
- {{CONSTRAINT_4}}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 扩展点
|
## 扩展点
|
||||||
|
|
||||||
### 1. {{EXTENSION_1}}
|
<!-- 【可选】大项目建议填写 -->
|
||||||
|
|
||||||
|
### {{EXTENSION_1}}
|
||||||
|
|
||||||
**步骤**:
|
**步骤**:
|
||||||
|
|
||||||
1. {{STEP_1}}
|
1. {{STEP_1}}
|
||||||
2. {{STEP_2}}
|
|
||||||
3. {{STEP_3}}
|
|
||||||
|
|
||||||
### 2. {{EXTENSION_2}}
|
|
||||||
|
|
||||||
**步骤**:
|
|
||||||
|
|
||||||
1. {{STEP_4}}
|
|
||||||
2. {{STEP_5}}
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,11 @@
|
||||||
# 架构决策记录
|
# 架构决策记录
|
||||||
|
|
||||||
本文档记录项目中的重要架构决策,使用 ADR (Architecture Decision Record) 格式。
|
<!--
|
||||||
|
填写指南:
|
||||||
---
|
- 本文件记录重要架构决策,使用 ADR 格式
|
||||||
|
- 初始可为空,遇到重要决策时由 AI 或人工添加
|
||||||
## ADR-001: {{DECISION_1_TITLE}}
|
- 每个决策使用下方模板
|
||||||
|
-->
|
||||||
**日期**: {{DATE}}
|
|
||||||
**状态**: 已采纳
|
|
||||||
|
|
||||||
### 决策
|
|
||||||
|
|
||||||
{{DECISION_1_CONTENT}}
|
|
||||||
|
|
||||||
### 理由
|
|
||||||
|
|
||||||
{{DECISION_1_REASON}}
|
|
||||||
|
|
||||||
### 影响
|
|
||||||
|
|
||||||
{{DECISION_1_IMPACT}}
|
|
||||||
|
|
||||||
### 实施细节
|
|
||||||
|
|
||||||
{{DECISION_1_IMPLEMENTATION}}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ADR-002: {{DECISION_2_TITLE}}
|
|
||||||
|
|
||||||
**日期**: {{DATE}}
|
|
||||||
**状态**: 已采纳
|
|
||||||
|
|
||||||
### 决策
|
|
||||||
|
|
||||||
{{DECISION_2_CONTENT}}
|
|
||||||
|
|
||||||
### 理由
|
|
||||||
|
|
||||||
{{DECISION_2_REASON}}
|
|
||||||
|
|
||||||
### 影响
|
|
||||||
|
|
||||||
{{DECISION_2_IMPACT}}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ADR 模板
|
## ADR 模板
|
||||||
|
|
||||||
|
|
@ -65,10 +26,6 @@
|
||||||
### 影响
|
### 影响
|
||||||
|
|
||||||
对项目的影响
|
对项目的影响
|
||||||
|
|
||||||
### 替代方案(可选)
|
|
||||||
|
|
||||||
考虑过但未采纳的方案
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,4 @@
|
||||||
# 开发进度追踪
|
# Plan 状态
|
||||||
|
|
||||||
## 已知问题
|
<!-- plan-status:start -->
|
||||||
|
<!-- plan-status:end -->
|
||||||
<!-- 记录已知但暂不解决的问题 -->
|
|
||||||
|
|
||||||
#### {{ISSUE_CATEGORY_1}}
|
|
||||||
|
|
||||||
- {{ISSUE_1}}
|
|
||||||
- **临时方案**:{{WORKAROUND_1}}
|
|
||||||
- **长期方案**:{{SOLUTION_1}}
|
|
||||||
|
|
||||||
## 里程碑
|
|
||||||
|
|
||||||
#### M1: {{MILESTONE_1}}(目标:{{TARGET_DATE_1}})
|
|
||||||
|
|
||||||
- [ ] {{MILESTONE_1_TASK_1}}
|
|
||||||
- [ ] {{MILESTONE_1_TASK_2}}
|
|
||||||
|
|
||||||
#### M2: {{MILESTONE_2}}(目标:{{TARGET_DATE_2}})
|
|
||||||
|
|
||||||
- [ ] {{MILESTONE_2_TASK_1}}
|
|
||||||
- [ ] {{MILESTONE_2_TASK_2}}
|
|
||||||
|
|
||||||
## Plan 状态记录
|
|
||||||
|
|
||||||
<!-- 由 plan_progress.py 自动管理,请勿手动编辑此节内容 -->
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**最后更新**:{{DATE}}
|
|
||||||
|
|
|
||||||
|
|
@ -1,65 +1,47 @@
|
||||||
# {{PROJECT_NAME}} 项目简介
|
# {{PROJECT_NAME}} 项目简介
|
||||||
|
|
||||||
|
<!--
|
||||||
|
填写指南:
|
||||||
|
- 【必填】:项目启动前必须填写
|
||||||
|
- 【可选】:按需填写,可随项目发展补充
|
||||||
|
- 未填写的占位符保持原样或删除整行
|
||||||
|
-->
|
||||||
|
|
||||||
## 项目定位
|
## 项目定位
|
||||||
|
|
||||||
|
<!-- 【必填】 -->
|
||||||
|
|
||||||
**核心目标**:{{PROJECT_GOAL}}
|
**核心目标**:{{PROJECT_GOAL}}
|
||||||
|
|
||||||
**一句话描述**:{{PROJECT_DESCRIPTION}}
|
**一句话描述**:{{PROJECT_DESCRIPTION}}
|
||||||
|
|
||||||
## 为什么做这个项目?
|
|
||||||
|
|
||||||
### 问题
|
|
||||||
|
|
||||||
- {{PROBLEM_1}}
|
|
||||||
- {{PROBLEM_2}}
|
|
||||||
- {{PROBLEM_3}}
|
|
||||||
|
|
||||||
### 解决方案
|
|
||||||
|
|
||||||
- {{SOLUTION_1}}
|
|
||||||
- {{SOLUTION_2}}
|
|
||||||
- {{SOLUTION_3}}
|
|
||||||
|
|
||||||
## 项目边界
|
## 项目边界
|
||||||
|
|
||||||
|
<!-- 【必填】至少填写"做什么" -->
|
||||||
|
|
||||||
### 做什么
|
### 做什么
|
||||||
|
|
||||||
- {{DO_1}}
|
- {{DO_1}}
|
||||||
- {{DO_2}}
|
|
||||||
- {{DO_3}}
|
|
||||||
|
|
||||||
### 不做什么
|
### 不做什么
|
||||||
|
|
||||||
|
<!-- 【可选】 -->
|
||||||
|
|
||||||
- {{DONT_1}}
|
- {{DONT_1}}
|
||||||
- {{DONT_2}}
|
|
||||||
- {{DONT_3}}
|
|
||||||
|
|
||||||
### 约束条件
|
### 约束条件
|
||||||
|
|
||||||
|
<!-- 【可选】 -->
|
||||||
|
|
||||||
- {{CONSTRAINT_1}}
|
- {{CONSTRAINT_1}}
|
||||||
- {{CONSTRAINT_2}}
|
|
||||||
- {{CONSTRAINT_3}}
|
|
||||||
|
|
||||||
## 核心概念
|
## 核心概念
|
||||||
|
|
||||||
<!-- 根据项目需要填写核心概念 -->
|
<!-- 【可选】项目特有的术语或概念 -->
|
||||||
|
|
||||||
## 技术栈
|
|
||||||
|
|
||||||
- **主语言**:{{MAIN_LANGUAGE}}
|
|
||||||
- **外部依赖**:{{DEPENDENCIES}}
|
|
||||||
- **测试环境**:{{TEST_ENV}}
|
|
||||||
|
|
||||||
## 参考资料
|
## 参考资料
|
||||||
|
|
||||||
- {{REFERENCE_1}}
|
<!-- 【可选】 -->
|
||||||
- {{REFERENCE_2}}
|
|
||||||
|
|
||||||
## 当前状态
|
|
||||||
|
|
||||||
- {{STATUS_1}}
|
|
||||||
- {{STATUS_2}}
|
|
||||||
- {{STATUS_3}}
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,117 +1,63 @@
|
||||||
# 技术栈与工具链
|
# 技术栈与工具链
|
||||||
|
|
||||||
|
<!--
|
||||||
|
填写指南:
|
||||||
|
- 【必填】:项目启动前必须填写
|
||||||
|
- 【可选】:按需填写,可随项目发展补充
|
||||||
|
- 未填写的占位符保持原样或删除整行
|
||||||
|
-->
|
||||||
|
|
||||||
## 核心技术
|
## 核心技术
|
||||||
|
|
||||||
### 主语言:{{MAIN_LANGUAGE}}
|
<!-- 【必填】 -->
|
||||||
|
|
||||||
**文件类型**:
|
**主语言**:{{MAIN_LANGUAGE}}
|
||||||
|
|
||||||
- {{FILE_TYPE_1}}
|
**文件类型**:{{FILE_TYPES}}
|
||||||
- {{FILE_TYPE_2}}
|
|
||||||
|
|
||||||
**特点**:
|
|
||||||
|
|
||||||
- {{FEATURE_1}}
|
|
||||||
- {{FEATURE_2}}
|
|
||||||
- {{FEATURE_3}}
|
|
||||||
|
|
||||||
**运行方式**:
|
|
||||||
|
|
||||||
- {{RUN_METHOD_1}}
|
|
||||||
- {{RUN_METHOD_2}}
|
|
||||||
|
|
||||||
## 项目结构
|
## 项目结构
|
||||||
|
|
||||||
|
<!-- 【必填】 -->
|
||||||
|
|
||||||
```text
|
```text
|
||||||
{{PROJECT_NAME}}/
|
{{PROJECT_NAME}}/
|
||||||
├── {{DIR_1}}/ # {{DIR_1_DESC}}
|
├── {{DIR_1}}/ # {{DIR_1_DESC}}
|
||||||
├── {{DIR_2}}/ # {{DIR_2_DESC}}
|
|
||||||
├── {{DIR_3}}/ # {{DIR_3_DESC}}
|
|
||||||
└── memory-bank/ # 项目上下文
|
└── memory-bank/ # 项目上下文
|
||||||
```
|
```
|
||||||
|
|
||||||
## 开发环境
|
## 开发环境
|
||||||
|
|
||||||
### {{ENV_1}}
|
<!-- 【必填】至少填写运行测试命令 -->
|
||||||
|
|
||||||
**必需工具**:
|
**必需工具**:
|
||||||
|
|
||||||
- {{TOOL_1}}
|
- {{TOOL_1}}
|
||||||
- {{TOOL_2}}
|
|
||||||
|
|
||||||
**运行测试**:
|
**运行测试**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
{{TEST_CMD_1}}
|
{{TEST_CMD}}
|
||||||
```
|
```
|
||||||
|
|
||||||
### {{ENV_2}}(如有)
|
|
||||||
|
|
||||||
**必需工具**:
|
|
||||||
|
|
||||||
- {{TOOL_3}}
|
|
||||||
- {{TOOL_4}}
|
|
||||||
|
|
||||||
## 版本控制
|
|
||||||
|
|
||||||
### Git 配置
|
|
||||||
|
|
||||||
**换行规则**(`.gitattributes`):
|
|
||||||
|
|
||||||
- 遵循 `.gitattributes` 文件定义
|
|
||||||
|
|
||||||
**忽略规则**(`.gitignore`):
|
|
||||||
|
|
||||||
- 以 `.gitignore` 实际内容为准
|
|
||||||
|
|
||||||
### 分支策略
|
|
||||||
|
|
||||||
- `master`/`main`:主分支(稳定版本)
|
|
||||||
- 功能分支:按需创建
|
|
||||||
|
|
||||||
## 测试策略
|
|
||||||
|
|
||||||
### 测试类型
|
|
||||||
|
|
||||||
- {{TEST_TYPE_1}}
|
|
||||||
- {{TEST_TYPE_2}}
|
|
||||||
|
|
||||||
### 验证标准
|
|
||||||
|
|
||||||
**测试通过条件**:
|
|
||||||
|
|
||||||
1. {{PASS_CONDITION_1}}
|
|
||||||
2. {{PASS_CONDITION_2}}
|
|
||||||
3. {{PASS_CONDITION_3}}
|
|
||||||
|
|
||||||
**常见失败原因**:
|
|
||||||
|
|
||||||
- {{FAIL_REASON_1}}
|
|
||||||
- {{FAIL_REASON_2}}
|
|
||||||
|
|
||||||
## 依赖管理
|
## 依赖管理
|
||||||
|
|
||||||
### 外部依赖
|
<!-- 【可选】 -->
|
||||||
|
|
||||||
|
**外部依赖**:
|
||||||
|
|
||||||
- {{EXTERNAL_DEP_1}}
|
- {{EXTERNAL_DEP_1}}
|
||||||
- {{EXTERNAL_DEP_2}}
|
|
||||||
|
|
||||||
### 内部依赖
|
## 测试策略
|
||||||
|
|
||||||
- {{INTERNAL_DEP_1}}
|
<!-- 【可选】大项目建议填写 -->
|
||||||
- {{INTERNAL_DEP_2}}
|
|
||||||
|
|
||||||
## 性能考虑
|
**测试类型**:
|
||||||
|
|
||||||
### 当前瓶颈
|
- {{TEST_TYPE_1}}
|
||||||
|
|
||||||
- {{BOTTLENECK_1}}
|
**验证标准**:
|
||||||
- {{BOTTLENECK_2}}
|
|
||||||
|
|
||||||
### 优化方向
|
- {{PASS_CONDITION_1}}
|
||||||
|
|
||||||
- {{OPTIMIZATION_1}}
|
|
||||||
- {{OPTIMIZATION_2}}
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,47 @@
|
||||||
# 提示词库
|
# 提示词库
|
||||||
|
|
||||||
本目录包含 AI 代理的工作流程模板和参考文档。
|
本目录包含 AI 代理的工作流程参考模板。
|
||||||
|
|
||||||
## 目录结构
|
## 目录结构
|
||||||
|
|
||||||
```text
|
```text
|
||||||
prompts/
|
prompts/
|
||||||
├── README.md # 本文件
|
├── README.md # 本文件
|
||||||
├── system/ # 系统级规范
|
├── system/
|
||||||
│ └── agent-behavior.md
|
│ └── agent-behavior.md # 工作模式参考
|
||||||
├── coding/ # 编码流程
|
├── coding/
|
||||||
│ ├── clarify.md # 需求澄清模板
|
│ ├── clarify.md # 需求澄清模板
|
||||||
│ └── verify.md # 验证检查清单
|
│ └── review.md # 复盘总结模板
|
||||||
└── user/ # 用户快捷命令
|
└── meta/
|
||||||
└── quick-test.md # 快速测试命令
|
└── prompt-generator.md # 元提示词生成器
|
||||||
```
|
```
|
||||||
|
|
||||||
## 使用方式
|
## 使用方式
|
||||||
|
|
||||||
### AI 代理
|
| 模板 | 触发场景 |
|
||||||
|
| ----------------------- | ------------------------------ |
|
||||||
|
| **agent-behavior.md** | 切换工作模式(探索/开发/调试) |
|
||||||
|
| **clarify.md** | 需求不明确时澄清 |
|
||||||
|
| **review.md** | Plan 完成后复盘总结 |
|
||||||
|
| **prompt-generator.md** | 创建新的专用提示词 |
|
||||||
|
|
||||||
- 新会话时读取 `system/agent-behavior.md`
|
## 工作流程
|
||||||
- 需要澄清需求时参考 `coding/clarify.md`
|
|
||||||
- 完成任务前检查 `coding/verify.md`
|
|
||||||
|
|
||||||
### 用户
|
```
|
||||||
|
需求不清 → clarify.md
|
||||||
|
↓
|
||||||
|
头脑风暴 → $brainstorming skill
|
||||||
|
↓
|
||||||
|
生成计划 → $writing-plans skill → docs/plans/*.md
|
||||||
|
↓
|
||||||
|
执行计划 → AGENT_RULES 主循环(留痕)
|
||||||
|
↓
|
||||||
|
完成复盘 → review.md
|
||||||
|
↓
|
||||||
|
沉淀提示词 → prompt-generator.md(可选)
|
||||||
|
```
|
||||||
|
|
||||||
- 使用 `user/quick-test.md` 中的命令快速执行测试
|
> **核心规则在 `AGENT_RULES.md`**,第三方 skills 负责规划,主循环负责执行和留痕。
|
||||||
|
|
||||||
## 文档说明
|
|
||||||
|
|
||||||
| 文件 | 用途 |
|
|
||||||
| -------------------------- | ------------------------------- |
|
|
||||||
| `system/agent-behavior.md` | AI 行为规范、工作模式、禁止行为 |
|
|
||||||
| `coding/clarify.md` | 需求澄清步骤和问题模板 |
|
|
||||||
| `coding/verify.md` | 代码、测试、文档验证清单 |
|
|
||||||
| `user/quick-test.md` | 常用测试命令参考 |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
# 需求澄清模板
|
# 需求澄清模板
|
||||||
|
|
||||||
|
<!--
|
||||||
|
按需使用:当需求不明确或存在歧义时参考本模板。
|
||||||
|
Vibe-coding 场景下可跳过,直接开始实现。
|
||||||
|
-->
|
||||||
|
|
||||||
## 何时使用
|
## 何时使用
|
||||||
|
|
||||||
- 需求描述不明确
|
- 需求描述不明确
|
||||||
|
|
@ -10,119 +15,37 @@
|
||||||
|
|
||||||
## 澄清步骤
|
## 澄清步骤
|
||||||
|
|
||||||
### 1. 理解当前需求
|
### 1. 复述需求
|
||||||
|
|
||||||
**复述需求**:
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
我理解你的需求是:[用自己的话复述]
|
我理解你的需求是:[用自己的话复述]
|
||||||
```
|
```
|
||||||
|
|
||||||
**识别歧义点**:
|
### 2. 识别歧义
|
||||||
|
|
||||||
- 歧义 1:[描述不明确的地方]
|
- 歧义 1:[描述不明确的地方]
|
||||||
- 歧义 2:[可能有多种理解的地方]
|
- 歧义 2:[可能有多种理解的地方]
|
||||||
|
|
||||||
---
|
### 3. 提出问题
|
||||||
|
|
||||||
### 2. 提出澄清问题
|
|
||||||
|
|
||||||
**问题模板**:
|
|
||||||
|
|
||||||
> 只问阻塞问题,最多 1–2 个;优先给出选项让用户选择。
|
> 只问阻塞问题,最多 1–2 个;优先给出选项让用户选择。
|
||||||
|
|
||||||
#### 功能范围
|
|
||||||
|
|
||||||
- 这个功能是否包括 [场景 A]?
|
- 这个功能是否包括 [场景 A]?
|
||||||
- 是否需要支持 [边界情况 B]?
|
|
||||||
- 优先级如何?必须有 vs 可选
|
|
||||||
|
|
||||||
#### 行为细节
|
|
||||||
|
|
||||||
- 当 [条件 X] 时,应该 [行为 Y] 还是 [行为 Z]?
|
- 当 [条件 X] 时,应该 [行为 Y] 还是 [行为 Z]?
|
||||||
- 如果 [异常情况],如何处理?
|
|
||||||
- 是否需要与 [现有功能] 保持一致?
|
|
||||||
|
|
||||||
#### 技术约束
|
### 4. 提供选项
|
||||||
|
|
||||||
- 是否有性能要求?
|
|
||||||
- 是否有兼容性要求?
|
|
||||||
- 是否有安全要求?
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. 提供选项
|
|
||||||
|
|
||||||
**选项格式**:
|
|
||||||
|
|
||||||
**选项 A**:[方案描述]
|
**选项 A**:[方案描述]
|
||||||
|
|
||||||
- 优点:[列出优点]
|
- 优点:...
|
||||||
- 缺点:[列出缺点]
|
- 缺点:...
|
||||||
- 适用场景:[什么情况下选这个]
|
|
||||||
|
|
||||||
**选项 B**:[方案描述]
|
**选项 B**:[方案描述]
|
||||||
|
|
||||||
- 优点:[列出优点]
|
- 优点:...
|
||||||
- 缺点:[列出缺点]
|
- 缺点:...
|
||||||
- 适用场景:[什么情况下选这个]
|
|
||||||
|
|
||||||
**推荐**:[推荐哪个选项,为什么]
|
**推荐**:[推荐哪个,为什么]
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 4. 确认理解
|
|
||||||
|
|
||||||
**确认清单**:
|
|
||||||
|
|
||||||
- [ ] 功能范围明确
|
|
||||||
- [ ] 行为细节清晰
|
|
||||||
- [ ] 技术约束已知
|
|
||||||
- [ ] 优先级确定
|
|
||||||
- [ ] 验收标准明确
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 示例
|
|
||||||
|
|
||||||
### 需求
|
|
||||||
|
|
||||||
```text
|
|
||||||
实现 XXX 功能
|
|
||||||
```
|
|
||||||
|
|
||||||
### 澄清过程
|
|
||||||
|
|
||||||
**复述需求**:
|
|
||||||
|
|
||||||
```text
|
|
||||||
我理解你的需求是:为 YYY 添加 XXX 功能,
|
|
||||||
用于 ZZZ。
|
|
||||||
```
|
|
||||||
|
|
||||||
**识别歧义点**:
|
|
||||||
|
|
||||||
- 歧义 1:XXX 是只读还是可写?
|
|
||||||
- 歧义 2:是否需要支持所有场景?
|
|
||||||
|
|
||||||
**澄清问题**:
|
|
||||||
|
|
||||||
- 是否需要支持 [场景 A]?
|
|
||||||
- 当 [条件 X] 时,应该如何处理?
|
|
||||||
|
|
||||||
**提供选项**:
|
|
||||||
|
|
||||||
**选项 A**:完整实现
|
|
||||||
|
|
||||||
- 优点:功能完整
|
|
||||||
- 缺点:开发周期长
|
|
||||||
|
|
||||||
**选项 B**:核心功能
|
|
||||||
|
|
||||||
- 优点:快速交付
|
|
||||||
- 缺点:功能有限
|
|
||||||
|
|
||||||
**推荐**:选项 A,因为 [原因]。
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
# 复盘模板
|
||||||
|
|
||||||
|
<!--
|
||||||
|
用途:Plan 或阶段完成后的回顾总结
|
||||||
|
触发:主循环汇总报告时、阶段性工作完成时
|
||||||
|
-->
|
||||||
|
|
||||||
|
## 何时使用
|
||||||
|
|
||||||
|
- 一批 Plan 执行完毕后
|
||||||
|
- 阶段性工作告一段落
|
||||||
|
- 遇到重大阻塞需要总结
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 复盘格式
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# 复盘: [日期/阶段名称]
|
||||||
|
|
||||||
|
## 完成情况
|
||||||
|
|
||||||
|
### 已完成
|
||||||
|
- [x] Plan 1: 简述
|
||||||
|
- [x] Plan 2: 简述
|
||||||
|
|
||||||
|
### 阻塞
|
||||||
|
- [ ] Plan 3: 阻塞原因
|
||||||
|
|
||||||
|
### 跳过
|
||||||
|
- [ ] Plan 4: 跳过原因
|
||||||
|
|
||||||
|
## 关键发现
|
||||||
|
|
||||||
|
### 做得好的
|
||||||
|
- 发现1
|
||||||
|
- 发现2
|
||||||
|
|
||||||
|
### 待改进
|
||||||
|
- 问题1 → 建议改进方式
|
||||||
|
- 问题2 → 建议改进方式
|
||||||
|
|
||||||
|
## 决策记录
|
||||||
|
|
||||||
|
| 决策 | 理由 | 影响 |
|
||||||
|
|------|------|------|
|
||||||
|
| 决策1 | 为什么 | 影响范围 |
|
||||||
|
|
||||||
|
## 下一步
|
||||||
|
|
||||||
|
- [ ] 待处理事项1
|
||||||
|
- [ ] 待处理事项2
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 复盘原则
|
||||||
|
|
||||||
|
- **客观记录**:如实记录完成/阻塞/跳过
|
||||||
|
- **提取经验**:总结做得好的和待改进的
|
||||||
|
- **决策留痕**:重要决策记录到 decisions.md
|
||||||
|
- **明确下一步**:列出后续待处理事项
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**最后更新**:{{DATE}}
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
# 验证检查清单
|
|
||||||
|
|
||||||
## 代码修改验证
|
|
||||||
|
|
||||||
### 语法检查
|
|
||||||
|
|
||||||
- [ ] 代码可正常运行(无语法错误)
|
|
||||||
- [ ] 无未定义的变量或函数
|
|
||||||
- [ ] 依赖引用正确
|
|
||||||
|
|
||||||
### 风格检查
|
|
||||||
|
|
||||||
- [ ] 命名符合规范
|
|
||||||
- [ ] 缩进正确
|
|
||||||
- [ ] 换行符正确(遵循 .gitattributes)
|
|
||||||
- [ ] 无冗余注释
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 测试验证
|
|
||||||
|
|
||||||
### 单元测试
|
|
||||||
|
|
||||||
- [ ] 相关测试脚本存在
|
|
||||||
- [ ] 测试可正常运行
|
|
||||||
- [ ] 测试通过(无失败)
|
|
||||||
|
|
||||||
### 回归测试
|
|
||||||
|
|
||||||
- [ ] 现有测试仍然通过
|
|
||||||
- [ ] 未破坏其他功能
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 文档验证
|
|
||||||
|
|
||||||
### 代码文档
|
|
||||||
|
|
||||||
- [ ] 复杂逻辑有注释说明
|
|
||||||
- [ ] 公开 API 有使用示例(如需)
|
|
||||||
|
|
||||||
### 项目文档
|
|
||||||
|
|
||||||
- [ ] `memory-bank/progress.md` 已更新
|
|
||||||
- [ ] 重要决策记录到 `memory-bank/decisions.md`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Git 验证
|
|
||||||
|
|
||||||
### 提交前检查
|
|
||||||
|
|
||||||
- [ ] 只包含相关修改(无无关文件)
|
|
||||||
- [ ] 提交信息清晰
|
|
||||||
- [ ] 无临时文件或调试代码
|
|
||||||
|
|
||||||
### 分支检查
|
|
||||||
|
|
||||||
- [ ] 在正确的分支上工作
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 性能验证(如果涉及)
|
|
||||||
|
|
||||||
### 性能测试
|
|
||||||
|
|
||||||
- [ ] 处理时间可接受
|
|
||||||
- [ ] 内存使用正常
|
|
||||||
- [ ] 无明显性能退化
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 安全验证(如果涉及)
|
|
||||||
|
|
||||||
### 安全检查
|
|
||||||
|
|
||||||
- [ ] 无注入风险
|
|
||||||
- [ ] 敏感信息已脱敏
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 快速检查清单(最小集)
|
|
||||||
|
|
||||||
**每次修改必须检查**:
|
|
||||||
|
|
||||||
- [ ] 代码可运行(无语法错误)
|
|
||||||
- [ ] 相关测试通过
|
|
||||||
- [ ] 换行符正确
|
|
||||||
- [ ] `memory-bank/progress.md` 已更新
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**最后更新**:{{DATE}}
|
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
# 提示词生成器(元提示词)
|
||||||
|
|
||||||
|
<!--
|
||||||
|
用途:根据场景自动生成专用提示词
|
||||||
|
原理:α-prompts(生成)+ Ω-prompts(优化)递归循环
|
||||||
|
-->
|
||||||
|
|
||||||
|
## 何时使用
|
||||||
|
|
||||||
|
- 需要为新场景创建专用提示词
|
||||||
|
- 现有提示词不满足特定需求
|
||||||
|
- 需要批量生成同类提示词
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 生成流程(α循环)
|
||||||
|
|
||||||
|
### 1. 分析场景
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
**场景名称**:[名称]
|
||||||
|
**目标用户**:[AI/人类/两者]
|
||||||
|
**触发条件**:[何时使用这个提示词]
|
||||||
|
**预期输出**:[使用后应该产出什么]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 提取约束
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
**必须做**:
|
||||||
|
- 约束1
|
||||||
|
- 约束2
|
||||||
|
|
||||||
|
**禁止做**:
|
||||||
|
- 禁止1
|
||||||
|
- 禁止2
|
||||||
|
|
||||||
|
**边界条件**:
|
||||||
|
- 边界1
|
||||||
|
- 边界2
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 生成草稿
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# [提示词标题]
|
||||||
|
|
||||||
|
<!--
|
||||||
|
用途:[一句话描述]
|
||||||
|
触发:[触发条件]
|
||||||
|
-->
|
||||||
|
|
||||||
|
## 何时使用
|
||||||
|
|
||||||
|
- 场景1
|
||||||
|
- 场景2
|
||||||
|
|
||||||
|
## [核心内容]
|
||||||
|
|
||||||
|
[根据场景填充]
|
||||||
|
|
||||||
|
## [约束/原则]
|
||||||
|
|
||||||
|
- 约束1
|
||||||
|
- 约束2
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**最后更新**:{{DATE}}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 优化流程(Ω循环)
|
||||||
|
|
||||||
|
### 1. 评估维度
|
||||||
|
|
||||||
|
| 维度 | 问题 |
|
||||||
|
| ---------- | ---------------------- |
|
||||||
|
| **清晰度** | 指令是否明确无歧义? |
|
||||||
|
| **完整度** | 是否覆盖所有必要场景? |
|
||||||
|
| **简洁度** | 是否有冗余内容可删除? |
|
||||||
|
| **可操作** | AI 能否直接执行? |
|
||||||
|
|
||||||
|
### 2. 迭代优化
|
||||||
|
|
||||||
|
```
|
||||||
|
草稿 → 评估 → 修改 → 再评估 → ... → 定稿
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 验证测试
|
||||||
|
|
||||||
|
- 用实际场景测试提示词效果
|
||||||
|
- 收集反馈,持续迭代
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 提示词模板库
|
||||||
|
|
||||||
|
### 标准结构
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# [标题]
|
||||||
|
|
||||||
|
<!--
|
||||||
|
用途:
|
||||||
|
触发:
|
||||||
|
-->
|
||||||
|
|
||||||
|
## 何时使用
|
||||||
|
## [核心内容]
|
||||||
|
## [约束/原则]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**最后更新**:{{DATE}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 命名规范
|
||||||
|
|
||||||
|
- 文件名:`[动词]-[对象].template.md`
|
||||||
|
- 示例:`clarify-requirement.template.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**最后更新**:{{DATE}}
|
||||||
|
|
@ -1,18 +1,19 @@
|
||||||
# AI 代理行为规范
|
# 工作模式参考
|
||||||
|
|
||||||
## 工作模式
|
<!--
|
||||||
|
本文件定义三种工作模式,供 AI 根据任务类型选择。
|
||||||
|
核心规则(安全红线、验证清单等)见 AGENT_RULES.md。
|
||||||
|
-->
|
||||||
|
|
||||||
### 模式 1: 探索模式(Explore)
|
## 模式 1: 探索模式(Explore)
|
||||||
|
|
||||||
**目的**:理解代码库、分析问题、收集信息
|
**目的**:理解代码库、分析问题、收集信息
|
||||||
|
|
||||||
**行为规范**:
|
**行为**:
|
||||||
|
|
||||||
- 使用搜索工具探索代码
|
- 使用搜索工具探索代码
|
||||||
- 输出分析报告和发现
|
- 输出分析报告和发现
|
||||||
- 提出问题和建议
|
|
||||||
- 不修改任何代码
|
- 不修改任何代码
|
||||||
- 不运行测试(除非明确要求)
|
|
||||||
|
|
||||||
**适用场景**:
|
**适用场景**:
|
||||||
|
|
||||||
|
|
@ -22,18 +23,15 @@
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 模式 2: 开发模式(Develop)
|
## 模式 2: 开发模式(Develop)
|
||||||
|
|
||||||
**目的**:实现功能、修复 bug、重构代码
|
**目的**:实现功能、修复 bug、重构代码
|
||||||
|
|
||||||
**行为规范**:
|
**行为**:
|
||||||
|
|
||||||
- 先读取相关文件,理解现有逻辑
|
- 先读取相关文件,理解现有逻辑
|
||||||
- 进行精确修改
|
- 进行精确修改
|
||||||
- 修改后运行对应测试验证
|
- 修改后运行测试验证
|
||||||
- 更新 `memory-bank/progress.md`
|
|
||||||
- 不读文件就提议修改
|
|
||||||
- 不跳过测试直接提交
|
|
||||||
|
|
||||||
**适用场景**:
|
**适用场景**:
|
||||||
|
|
||||||
|
|
@ -43,16 +41,15 @@
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 模式 3: 调试模式(Debug)
|
## 模式 3: 调试模式(Debug)
|
||||||
|
|
||||||
**目的**:诊断问题、对比差异、验证行为
|
**目的**:诊断问题、对比差异、验证行为
|
||||||
|
|
||||||
**行为规范**:
|
**行为**:
|
||||||
|
|
||||||
- 收集相关日志和输出
|
- 收集相关日志和输出
|
||||||
- 分析差异原因
|
- 分析差异原因
|
||||||
- 记录待确认事项并在回复中提出,或直接修复
|
- 修复后重新验证
|
||||||
- 重新验证
|
|
||||||
|
|
||||||
**适用场景**:
|
**适用场景**:
|
||||||
|
|
||||||
|
|
@ -62,135 +59,4 @@
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 代码风格要求
|
|
||||||
|
|
||||||
### 通用规范
|
|
||||||
|
|
||||||
**命名规范**:
|
|
||||||
|
|
||||||
- 遵循项目现有的命名风格
|
|
||||||
- 保持一致性
|
|
||||||
|
|
||||||
**缩进**:
|
|
||||||
|
|
||||||
- 遵循项目现有的缩进风格
|
|
||||||
|
|
||||||
**换行**:
|
|
||||||
|
|
||||||
- 遵循 `.gitattributes` 规则
|
|
||||||
|
|
||||||
**注释**:
|
|
||||||
|
|
||||||
- 只在逻辑不自明时添加注释
|
|
||||||
- 不添加冗余注释
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 禁止行为清单
|
|
||||||
|
|
||||||
### 代码修改
|
|
||||||
|
|
||||||
- **不读文件就提议修改**
|
|
||||||
|
|
||||||
- 必须先读取文件
|
|
||||||
- 理解现有逻辑后再提出修改建议
|
|
||||||
|
|
||||||
- **破坏现有架构**
|
|
||||||
|
|
||||||
- 不随意移动目录结构
|
|
||||||
- 不随意重构核心模块
|
|
||||||
|
|
||||||
- **随意改动换行符**
|
|
||||||
- 遵循 `.gitattributes` 规则
|
|
||||||
- 不混用 LF 和 CRLF
|
|
||||||
|
|
||||||
### 测试流程
|
|
||||||
|
|
||||||
- **跳过测试直接提交**
|
|
||||||
- 修改后必须运行相关测试
|
|
||||||
- 测试失败必须分析原因
|
|
||||||
|
|
||||||
### Git 操作
|
|
||||||
|
|
||||||
- **使用 `git commit --amend`**
|
|
||||||
|
|
||||||
- 除非用户明确要求
|
|
||||||
- 总是创建新提交
|
|
||||||
|
|
||||||
- **使用 `git push --force`**
|
|
||||||
|
|
||||||
- 特别是推送到 main/master 分支
|
|
||||||
- 如果用户要求,必须警告风险
|
|
||||||
|
|
||||||
- **跳过 hooks**
|
|
||||||
- 不使用 `--no-verify`
|
|
||||||
|
|
||||||
### 过度工程
|
|
||||||
|
|
||||||
- **添加未要求的功能**
|
|
||||||
|
|
||||||
- 只做用户要求的修改
|
|
||||||
- 不主动重构周边代码
|
|
||||||
|
|
||||||
- **添加不必要的注释**
|
|
||||||
|
|
||||||
- 不给自明的代码添加注释
|
|
||||||
|
|
||||||
- **过度抽象**
|
|
||||||
- 不为一次性操作创建工具函数
|
|
||||||
- 不为假设的未来需求设计
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 决策原则
|
|
||||||
|
|
||||||
### 何时需要确认
|
|
||||||
|
|
||||||
**必须确认**:
|
|
||||||
|
|
||||||
- 需求有歧义,存在多种理解
|
|
||||||
- 有多个技术方案,需要权衡
|
|
||||||
- 可能破坏兼容性
|
|
||||||
- 涉及架构变更
|
|
||||||
|
|
||||||
**可以不确认**:
|
|
||||||
|
|
||||||
- 明显的 bug 修复
|
|
||||||
- 符合现有模式的小改动
|
|
||||||
- 测试用例补充
|
|
||||||
|
|
||||||
### 何时记录到 decisions.md
|
|
||||||
|
|
||||||
**必须记录**(ADR 格式):
|
|
||||||
|
|
||||||
- 影响多个模块的架构决策
|
|
||||||
- 技术栈选择
|
|
||||||
- 设计模式选择
|
|
||||||
- 重要的约束条件
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 沟通原则
|
|
||||||
|
|
||||||
### 输出风格
|
|
||||||
|
|
||||||
- 简洁明确,避免冗长
|
|
||||||
- 使用纯文本结构化输出,必要时用 Markdown 代码块
|
|
||||||
- 代码块标注语言
|
|
||||||
- 不使用 emoji(除非用户明确要求)
|
|
||||||
- 不使用过度的赞美或验证
|
|
||||||
|
|
||||||
### 技术准确性
|
|
||||||
|
|
||||||
- 优先技术准确性,而非迎合用户
|
|
||||||
- 发现用户理解有误时,礼貌纠正
|
|
||||||
- 不确定时,先调查再回答
|
|
||||||
|
|
||||||
### 时间估算
|
|
||||||
|
|
||||||
- 不给出时间估算
|
|
||||||
- 专注于任务本身,让用户自己判断时间
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**最后更新**:{{DATE}}
|
**最后更新**:{{DATE}}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ def run_cli(*args, cwd=None):
|
||||||
|
|
||||||
|
|
||||||
class PlanProgressCliTests(unittest.TestCase):
|
class PlanProgressCliTests(unittest.TestCase):
|
||||||
def test_select_prefers_in_progress(self):
|
def test_select_seeds_progress_when_missing(self):
|
||||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
root = Path(tmp_dir)
|
root = Path(tmp_dir)
|
||||||
plans_dir = root / "docs" / "plans"
|
plans_dir = root / "docs" / "plans"
|
||||||
|
|
@ -26,13 +26,6 @@ class PlanProgressCliTests(unittest.TestCase):
|
||||||
(plans_dir / "2026-01-01-old.md").write_text("old", encoding="utf-8")
|
(plans_dir / "2026-01-01-old.md").write_text("old", encoding="utf-8")
|
||||||
(plans_dir / "2026-01-02-new.md").write_text("new", encoding="utf-8")
|
(plans_dir / "2026-01-02-new.md").write_text("new", encoding="utf-8")
|
||||||
|
|
||||||
progress = root / "memory-bank" / "progress.md"
|
|
||||||
progress.parent.mkdir(parents=True)
|
|
||||||
progress.write_text(
|
|
||||||
"[PLAN] docs/plans/2026-01-01-old.md | status=in-progress | date=2026-01-03\n",
|
|
||||||
encoding="utf-8",
|
|
||||||
)
|
|
||||||
|
|
||||||
result = run_cli(
|
result = run_cli(
|
||||||
"select",
|
"select",
|
||||||
"-plans",
|
"-plans",
|
||||||
|
|
@ -42,10 +35,17 @@ class PlanProgressCliTests(unittest.TestCase):
|
||||||
cwd=root,
|
cwd=root,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(result.returncode, 0)
|
self.assertEqual(result.returncode, 0, msg=result.stderr)
|
||||||
self.assertEqual(result.stdout.strip(), "docs/plans/2026-01-01-old.md")
|
self.assertEqual(result.stdout.strip(), "docs/plans/2026-01-01-old.md")
|
||||||
|
|
||||||
def test_select_skips_done_and_blocked(self):
|
progress = root / "memory-bank" / "progress.md"
|
||||||
|
text = progress.read_text(encoding="utf-8")
|
||||||
|
self.assertIn("<!-- plan-status:start -->", text)
|
||||||
|
self.assertIn("<!-- plan-status:end -->", text)
|
||||||
|
self.assertIn("`2026-01-01-old.md` pending", text)
|
||||||
|
self.assertIn("`2026-01-02-new.md` pending", text)
|
||||||
|
|
||||||
|
def test_select_returns_first_pending_in_order(self):
|
||||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
root = Path(tmp_dir)
|
root = Path(tmp_dir)
|
||||||
plans_dir = root / "docs" / "plans"
|
plans_dir = root / "docs" / "plans"
|
||||||
|
|
@ -58,8 +58,12 @@ class PlanProgressCliTests(unittest.TestCase):
|
||||||
progress.write_text(
|
progress.write_text(
|
||||||
"\n".join(
|
"\n".join(
|
||||||
[
|
[
|
||||||
"[PLAN] docs/plans/2026-01-02-b.md | status=done | date=2026-01-03",
|
"# Plan 状态",
|
||||||
"[PLAN] docs/plans/2026-01-01-a.md | status=blocked | date=2026-01-03",
|
"",
|
||||||
|
"<!-- plan-status:start -->",
|
||||||
|
"- [ ] `2026-01-02-b.md` pending",
|
||||||
|
"- [ ] `2026-01-01-a.md` pending",
|
||||||
|
"<!-- plan-status:end -->",
|
||||||
"",
|
"",
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
|
@ -75,13 +79,27 @@ class PlanProgressCliTests(unittest.TestCase):
|
||||||
cwd=root,
|
cwd=root,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertNotEqual(result.returncode, 0)
|
self.assertEqual(result.returncode, 0, msg=result.stderr)
|
||||||
self.assertIn("no pending plans", (result.stdout + result.stderr).lower())
|
self.assertEqual(result.stdout.strip(), "docs/plans/2026-01-02-b.md")
|
||||||
|
|
||||||
def test_record_creates_section(self):
|
def test_record_updates_line(self):
|
||||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
root = Path(tmp_dir)
|
root = Path(tmp_dir)
|
||||||
progress = root / "memory-bank" / "progress.md"
|
progress = root / "memory-bank" / "progress.md"
|
||||||
|
progress.parent.mkdir(parents=True)
|
||||||
|
progress.write_text(
|
||||||
|
"\n".join(
|
||||||
|
[
|
||||||
|
"# Plan 状态",
|
||||||
|
"",
|
||||||
|
"<!-- plan-status:start -->",
|
||||||
|
"- [ ] `2026-01-03-demo.md` pending",
|
||||||
|
"<!-- plan-status:end -->",
|
||||||
|
"",
|
||||||
|
]
|
||||||
|
),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
result = run_cli(
|
result = run_cli(
|
||||||
"record",
|
"record",
|
||||||
|
|
@ -91,15 +109,13 @@ class PlanProgressCliTests(unittest.TestCase):
|
||||||
"done",
|
"done",
|
||||||
"-progress",
|
"-progress",
|
||||||
"memory-bank/progress.md",
|
"memory-bank/progress.md",
|
||||||
"-note",
|
|
||||||
"done",
|
|
||||||
cwd=root,
|
cwd=root,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(result.returncode, 0)
|
self.assertEqual(result.returncode, 0, msg=result.stderr)
|
||||||
text = progress.read_text(encoding="utf-8")
|
text = progress.read_text(encoding="utf-8")
|
||||||
self.assertIn("## Plan 状态记录", text)
|
self.assertIn("- [x] `2026-01-03-demo.md` done", text)
|
||||||
self.assertIn("status=done", text)
|
self.assertEqual(text.count("2026-01-03-demo.md"), 1)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
SCRIPT = ROOT / "scripts" / "playbook.py"
|
||||||
|
|
||||||
|
|
||||||
|
def run_cli(*args):
|
||||||
|
return subprocess.run(
|
||||||
|
[sys.executable, str(SCRIPT), *args],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SyncDirectoryActionsTests(unittest.TestCase):
|
||||||
|
def test_sync_memory_bank_adds_missing_files_without_deleting_custom(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
|
root = Path(tmp_dir)
|
||||||
|
memory_bank = root / "memory-bank"
|
||||||
|
memory_bank.mkdir(parents=True)
|
||||||
|
custom = memory_bank / "custom.md"
|
||||||
|
custom.write_text("custom", encoding="utf-8")
|
||||||
|
|
||||||
|
config_body = f"""
|
||||||
|
[playbook]
|
||||||
|
project_root = "{tmp_dir}"
|
||||||
|
|
||||||
|
[sync_memory_bank]
|
||||||
|
project_name = "Demo"
|
||||||
|
"""
|
||||||
|
config_path = root / "playbook.toml"
|
||||||
|
config_path.write_text(config_body, encoding="utf-8")
|
||||||
|
|
||||||
|
result = run_cli("-config", str(config_path))
|
||||||
|
self.assertEqual(result.returncode, 0, msg=result.stderr)
|
||||||
|
|
||||||
|
self.assertTrue(custom.exists())
|
||||||
|
self.assertTrue((memory_bank / "project-brief.md").is_file())
|
||||||
|
|
||||||
|
def test_sync_prompts_adds_missing_files_without_deleting_custom(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
|
root = Path(tmp_dir)
|
||||||
|
prompts = root / "docs" / "prompts"
|
||||||
|
prompts.mkdir(parents=True)
|
||||||
|
custom = prompts / "custom.md"
|
||||||
|
custom.write_text("custom", encoding="utf-8")
|
||||||
|
|
||||||
|
config_body = f"""
|
||||||
|
[playbook]
|
||||||
|
project_root = "{tmp_dir}"
|
||||||
|
|
||||||
|
[sync_prompts]
|
||||||
|
"""
|
||||||
|
config_path = root / "playbook.toml"
|
||||||
|
config_path.write_text(config_body, encoding="utf-8")
|
||||||
|
|
||||||
|
result = run_cli("-config", str(config_path))
|
||||||
|
self.assertEqual(result.returncode, 0, msg=result.stderr)
|
||||||
|
|
||||||
|
self.assertTrue(custom.exists())
|
||||||
|
self.assertTrue((prompts / "system" / "agent-behavior.md").is_file())
|
||||||
|
|
||||||
|
def test_sync_memory_bank_force_overwrites_template_files_only(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
|
root = Path(tmp_dir)
|
||||||
|
memory_bank = root / "memory-bank"
|
||||||
|
memory_bank.mkdir(parents=True)
|
||||||
|
custom = memory_bank / "custom.md"
|
||||||
|
custom.write_text("custom", encoding="utf-8")
|
||||||
|
|
||||||
|
brief = memory_bank / "project-brief.md"
|
||||||
|
brief.write_text("OLD", encoding="utf-8")
|
||||||
|
|
||||||
|
config_body = f"""
|
||||||
|
[playbook]
|
||||||
|
project_root = "{tmp_dir}"
|
||||||
|
|
||||||
|
[sync_memory_bank]
|
||||||
|
project_name = "Demo"
|
||||||
|
force = true
|
||||||
|
no_backup = true
|
||||||
|
"""
|
||||||
|
config_path = root / "playbook.toml"
|
||||||
|
config_path.write_text(config_body, encoding="utf-8")
|
||||||
|
|
||||||
|
result = run_cli("-config", str(config_path))
|
||||||
|
self.assertEqual(result.returncode, 0, msg=result.stderr)
|
||||||
|
|
||||||
|
self.assertTrue(custom.exists())
|
||||||
|
self.assertNotIn("OLD", brief.read_text(encoding="utf-8"))
|
||||||
|
backups = list(memory_bank.glob("project-brief.md.bak.*"))
|
||||||
|
self.assertEqual(backups, [])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
SCRIPT = ROOT / "scripts" / "playbook.py"
|
||||||
|
|
||||||
|
|
||||||
|
def run_cli(*args):
|
||||||
|
return subprocess.run(
|
||||||
|
[sys.executable, str(SCRIPT), *args],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class VendorSnapshotTemplatesTests(unittest.TestCase):
|
||||||
|
def test_vendor_includes_core_templates(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
|
config_body = f"""
|
||||||
|
[playbook]
|
||||||
|
project_root = "{tmp_dir}"
|
||||||
|
|
||||||
|
[vendor]
|
||||||
|
langs = ["tsl"]
|
||||||
|
"""
|
||||||
|
config_path = Path(tmp_dir) / "playbook.toml"
|
||||||
|
config_path.write_text(config_body, encoding="utf-8")
|
||||||
|
|
||||||
|
result = run_cli("-config", str(config_path))
|
||||||
|
self.assertEqual(result.returncode, 0, msg=result.stderr)
|
||||||
|
|
||||||
|
snapshot = Path(tmp_dir) / "docs/standards/playbook"
|
||||||
|
self.assertTrue((snapshot / "templates/AGENTS.template.md").is_file())
|
||||||
|
self.assertTrue((snapshot / "templates/AGENT_RULES.template.md").is_file())
|
||||||
|
self.assertTrue((snapshot / "templates/README.md").is_file())
|
||||||
|
self.assertTrue((snapshot / "templates/memory-bank").is_dir())
|
||||||
|
self.assertTrue((snapshot / "templates/prompts").is_dir())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Loading…
Reference in New Issue