#!/usr/bin/env python3 """ pmassist-v3 session initializer 初始化 pmassist-v3 会话工作目录与基础文件 用法: python3 skills/pmassist-v3/scripts/init_session.py \ --path \ --doc prd|frd|dar \ --alias <简称> \ --title <标题> \ --desc <原始需求> \ [--enable-prototype] \ [--enable-acceptance] """ import argparse from pathlib import Path from datetime import datetime DOC_MAP = { "prd": "prd.md", "frd": "frd.md", "dar": "dar.md", } def now_ts(): return datetime.now().strftime("%Y-%m-%d %H:%M:%S") def read_template(doc_type: str, skill_dir: Path) -> str: ref_name = DOC_MAP[doc_type] ref_path = skill_dir / "references" / ref_name if ref_path.exists(): return ref_path.read_text(encoding="utf-8") return f"# {doc_type.upper()}\n\n> 模板缺失,请手动补充。\n" def read_acceptance_template(skill_dir: Path) -> str: ref_path = skill_dir / "references" / "acceptance_template.md" if ref_path.exists(): return ref_path.read_text(encoding="utf-8") return "# 验收标准(Acceptance Criteria)\n\n> 按 Given/When/Then 格式填写。\n" def write_file(path: Path, content: str, force: bool = False) -> bool: if path.exists() and not force: return False path.parent.mkdir(parents=True, exist_ok=True) path.write_text(content, encoding="utf-8") return True def main(): parser = argparse.ArgumentParser( description="Initialize pmassist-v3 session workspace" ) parser.add_argument("--path", required=True, help="Work directory path") parser.add_argument( "--doc", required=True, choices=["prd", "frd", "dar"], help="Document type" ) parser.add_argument("--alias", default="", help="Project alias (short name)") parser.add_argument("--title", default="", help="Document title") parser.add_argument("--desc", default="", help="Raw requirement description") parser.add_argument("--force", action="store_true", help="Overwrite existing files") parser.add_argument( "--enable-prototype", action="store_true", help="Enable prototype design phase", ) parser.add_argument( "--enable-acceptance", action="store_true", help="Pre-create acceptance.md for Given/When/Then AC output", ) args = parser.parse_args() workdir = Path(args.path).resolve() workdir.mkdir(parents=True, exist_ok=True) # Skill 根目录(脚本位于 scripts/ 下) skill_dir = Path(__file__).resolve().parent.parent # ── 基础目录 ────────────────────────────────────── base_dirs = ["materials", "rounds", "questions", "outputs"] # ── 原型目录(可选)────────────────────────────── proto_dirs = [ "materials/prototypes", "materials/prototypes/reference", "materials/prototypes/analysis", "prototypes", "prototypes/screenshots", "prototypes/webapp", ] dirs_to_create = base_dirs + (proto_dirs if args.enable_prototype else []) for d in dirs_to_create: (workdir / d).mkdir(parents=True, exist_ok=True) ts = now_ts() alias = args.alias or "" title = args.title or "" raw_desc = args.desc or "" # ── desc.md ────────────────────────────────────── desc_md = f"""# 需求描述 ## 元信息 - 创建时间: {ts} - 最后更新: {ts} - 文档类型: {args.doc.upper()} - 项目简称: {alias} - 标题: {title} ## 原始输入 {raw_desc if raw_desc else '[待补充原始需求]'} ## WWH 分析 ### What - 做什么 [待补充] ### Why - 为什么 [待补充] ### How - 怎么做 [待补充] """ # ── session.yaml ───────────────────────────────── proto_section = "" if args.enable_prototype: proto_section = """ prototype: enabled: true status: "proto_pending" proto_round: 0 tech_stack: [] outputs: [] unresolved_proto_questions: [] """ session_yaml = f"""project_alias: "{alias}" doc_type: "{args.doc}" title: "{title}" created_at: "{ts}" updated_at: "{ts}" round: 0 status: "init" # 功能需求统计(v3 新增) fr_count: 0 ac_count: 0 ac_coverage: "0/0" # 未解决问题 unresolved_questions: [] # 跳过标记 skip_flags: prototype: {'true' if not args.enable_prototype else 'false'} ac_batch: false diff_list: false last_output: "" materials: []{proto_section} """ # ── summary.md ─────────────────────────────────── summary_md = f"""# 会话摘要 - {ts} 初始化 pmassist-v3 会话 - 文档类型:{args.doc.upper()} - 项目:{alias or '(未设置)'} """ # ── decision_log.md ────────────────────────────── decision_log_md = """# 决策记录 | 时间 | 变更编号 | 事项 | 决策内容 | 影响 FR | 依据 | |---|---|---|---|---|---| """ # ── materials_index.md ─────────────────────────── materials_index_md = """# 资料索引 | ID | 标题 | 类型 | 来源/路径 | 摘要 | 日期 | |---|---|---|---|---|---| """ # ── 输出文档模板 ────────────────────────────────── output_template = read_template(args.doc, skill_dir) # ── acceptance.md(可选,PRD/FRD 推荐)────────── acceptance_template = read_acceptance_template(skill_dir) # ── 写文件 ──────────────────────────────────────── wrote = [] def wf(rel_path: str, content: str): if write_file(workdir / rel_path, content, args.force): wrote.append(rel_path) wf("desc.md", desc_md) wf("session.yaml", session_yaml) wf("summary.md", summary_md) wf("decision_log.md", decision_log_md) wf("materials_index.md", materials_index_md) wf(f"outputs/{DOC_MAP[args.doc]}", output_template) # 验收标准(按需或 PRD/FRD 默认启用) if args.enable_acceptance or args.doc in ("prd", "frd"): wf("outputs/acceptance.md", acceptance_template) if wrote: print("[OK] pmassist-v3 会话初始化完成,创建/更新文件:") for f in wrote: print(f" - {f}") print(f"\n工作目录:{workdir}") print(f"下一步:加载 pmassist-v3 skill,开始 Round 1 PDCA。") else: print("[SKIP] 无文件变更。使用 --force 覆盖已有文件。") if __name__ == "__main__": main()