From 58118b93e4b89ff11fad65dd169ce47af8ea43dd Mon Sep 17 00:00:00 2001 From: anx4758 Date: Tue, 24 Feb 2026 19:46:37 +0800 Subject: [PATCH 1/4] feat: default pipeline to handbook workflow and add codegen switch --- README.md | 5 ++ SKILL.md | 16 ++-- scripts/benchmark_pipeline.py | 2 + scripts/run_pipeline.py | 162 ++++++++++++++++++++++++++++------ 4 files changed, 152 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 7a2330c..e5c50f8 100644 --- a/README.md +++ b/README.md @@ -51,8 +51,13 @@ node bin/stylekit-style-prompts-skill.js uninstall --target /tmp/stylekit-skill- python3 scripts/audit_style_rule_conflicts.py --format text python3 scripts/smoke_test.py python3 scripts/run_pipeline.py --query "高端科技SaaS财务后台,玻璃质感,强调可读性" --stack nextjs --format json +python3 scripts/run_pipeline.py --workflow codegen --query "高端科技SaaS财务后台,玻璃质感,强调可读性" --stack nextjs --format json ``` +说明: +- 默认是 `--workflow manual`(手册/知识库模式):输出设计简报 + 手册化建议,不强制走 prompt QA。 +- 若要生成并严格审查 prompt,请显式加 `--workflow codegen`。 + ## 3) 回归门禁 ```bash diff --git a/SKILL.md b/SKILL.md index 5d2908a..281b6d4 100644 --- a/SKILL.md +++ b/SKILL.md @@ -11,29 +11,33 @@ Generate better-looking frontend output by combining StyleKit style identity, ac ## Quick One-shot Command -Run full flow in one command: +Run handbook mode in one command (default): `python scripts/run_pipeline.py --query "" --stack nextjs --format json` +Run prompt-generation mode with QA gate: + +`python scripts/run_pipeline.py --workflow codegen --query "" --stack nextjs --format json` + Force multi-style blend: -`python scripts/run_pipeline.py --query "" --stack nextjs --blend-mode on --format json` +`python scripts/run_pipeline.py --workflow codegen --query "" --stack nextjs --blend-mode on --format json` Run targeted refinement (polish/debug/contrast/layout/component-fill): -`python scripts/run_pipeline.py --query "" --stack nextjs --refine-mode debug --format json` +`python scripts/run_pipeline.py --workflow codegen --query "" --stack nextjs --refine-mode debug --format json` Run with screenshot/Figma reference constraints: -`python scripts/run_pipeline.py --query "" --stack nextjs --reference-type screenshot --reference-notes "" --format json` +`python scripts/run_pipeline.py --workflow codegen --query "" --stack nextjs --reference-type screenshot --reference-notes "" --format json` Run with structured reference payload: -`python scripts/run_pipeline.py --query "" --stack nextjs --reference-type screenshot --reference-file refs/screen-analysis.json --format json` +`python scripts/run_pipeline.py --workflow codegen --query "" --stack nextjs --reference-type screenshot --reference-file refs/screen-analysis.json --format json` Run strict schema mode for reference payload: -`python scripts/run_pipeline.py --query "" --stack nextjs --reference-type screenshot --reference-file refs/screen-analysis.json --strict-reference-schema --format json` +`python scripts/run_pipeline.py --workflow codegen --query "" --stack nextjs --reference-type screenshot --reference-file refs/screen-analysis.json --strict-reference-schema --format json` Benchmark current quality: diff --git a/scripts/benchmark_pipeline.py b/scripts/benchmark_pipeline.py index 11cdd8b..43cd7cb 100644 --- a/scripts/benchmark_pipeline.py +++ b/scripts/benchmark_pipeline.py @@ -123,6 +123,8 @@ def run_case(py: str, query: str, stack: str, blend_mode: str, bucket: str, refi cmd = [ py, str(PIPELINE_SCRIPT), + "--workflow", + "codegen", "--query", query, "--stack", diff --git a/scripts/run_pipeline.py b/scripts/run_pipeline.py index e661c6d..d6b32bf 100644 --- a/scripts/run_pipeline.py +++ b/scripts/run_pipeline.py @@ -17,6 +17,60 @@ BRIEF_SCRIPT = SCRIPT_DIR / "generate_brief.py" QA_SCRIPT = SCRIPT_DIR / "qa_prompt.py" +PRODUCT_HINTS = { + "dashboard": ["dashboard", "admin", "panel", "console", "后台", "仪表盘", "控制台"], + "landing-page": ["landing", "hero", "marketing", "homepage", "落地页", "首页", "营销"], + "ecommerce": ["shop", "store", "ecommerce", "checkout", "商品", "电商", "购物", "支付"], + "docs": ["docs", "documentation", "guide", "manual", "文档", "说明", "手册"], + "portfolio": ["portfolio", "case study", "作品集", "案例"], +} + + +def infer_product_type(query: str) -> str: + text = (query or "").lower() + for product_type, keywords in PRODUCT_HINTS.items(): + if any(keyword in text for keyword in keywords): + return product_type + return "general-web-product" + + +def build_manual_assistant( + *, + query: str, + stack: str, + selected_style: str | None, + search_payload: dict[str, Any], + brief_payload: dict[str, Any], +) -> dict[str, Any]: + design_brief = brief_payload.get("design_brief", {}) or {} + design_intent = design_brief.get("design_intent", {}) or {} + style_choice = design_brief.get("style_choice", {}) or {} + + return { + "purpose": "Use this as a frontend design handbook context before generating code.", + "product_profile": { + "query": query, + "inferred_product_type": infer_product_type(query), + "stack": stack, + "purpose": design_intent.get("purpose"), + "audience": design_intent.get("audience"), + "tone": design_intent.get("tone"), + }, + "style_recommendation": { + "selected_style": selected_style, + "primary": style_choice.get("primary", {}), + "alternatives": style_choice.get("alternatives", []), + "top_candidates": search_payload.get("candidates", [])[:5], + }, + "implementation_handbook": { + "component_guidelines": design_brief.get("component_guidelines", []), + "interaction_rules": design_brief.get("interaction_rules", []), + "a11y_baseline": design_brief.get("a11y_baseline", []), + "anti_pattern_blacklist": design_brief.get("anti_pattern_blacklist", [])[:8], + "validation_tests": design_brief.get("validation_tests", []), + }, + } + def run_json_command(cmd: list[str]) -> dict[str, Any]: proc = subprocess.run(cmd, capture_output=True, text=True) @@ -48,6 +102,8 @@ def to_markdown(payload: dict[str, Any]) -> str: lines = [ "# StyleKit Pipeline Result", f"- Query: {payload['query']}", + f"- Workflow: {payload.get('workflow')}", + f"- Mode: {payload.get('mode')}", f"- Stack: {payload['stack']}", f"- Selected style: {payload['selected_style']}", f"- QA status: {payload['status']}", @@ -88,18 +144,41 @@ def to_markdown(payload: dict[str, Any]) -> str: ] ) + manual = payload.get("manual_assistant", {}) or {} + profile = manual.get("product_profile", {}) or {} + handbook = manual.get("implementation_handbook", {}) or {} + if manual: + lines.extend( + [ + "", + "## Handbook Snapshot", + f"- Inferred product type: {profile.get('inferred_product_type', '')}", + f"- Purpose: {profile.get('purpose', '')}", + f"- Audience: {profile.get('audience', '')}", + f"- Tone: {profile.get('tone', '')}", + f"- Component guidelines: {len(handbook.get('component_guidelines', []))}", + f"- Interaction rules: {len(handbook.get('interaction_rules', []))}", + ] + ) + return "\n".join(lines) def main() -> None: parser = argparse.ArgumentParser(description="Run StyleKit full pipeline") parser.add_argument("--query", required=True, help="User requirement") + parser.add_argument( + "--workflow", + default="manual", + choices=["manual", "codegen"], + help="manual = handbook/knowledge mode, codegen = prompt-generation mode", + ) parser.add_argument("--stack", default="html-tailwind", choices=["html-tailwind", "react", "nextjs", "vue", "svelte", "tailwind-v4"]) parser.add_argument("--style", help="Force style slug") parser.add_argument("--style-type", choices=["visual", "layout", "animation"]) parser.add_argument("--top", type=int, default=5) - parser.add_argument("--mode", default="brief+prompt", choices=["brief-only", "brief+prompt"]) - parser.add_argument("--blend-mode", default="auto", choices=["off", "auto", "on"]) + parser.add_argument("--mode", default=None, choices=["brief-only", "brief+prompt"]) + parser.add_argument("--blend-mode", default=None, choices=["off", "auto", "on"]) parser.add_argument( "--refine-mode", default="new", @@ -115,6 +194,10 @@ def main() -> None: args = parser.parse_args() py = sys.executable + resolved_mode = args.mode or ("brief-only" if args.workflow == "manual" else "brief+prompt") + resolved_blend_mode = args.blend_mode or ("off" if args.workflow == "manual" else "auto") + if args.style and args.blend_mode is None: + resolved_blend_mode = "off" search_cmd = [py, str(SEARCH_SCRIPT), "--query", args.query, "--top", str(args.top), "--format", "json"] if args.style_type: @@ -129,9 +212,9 @@ def main() -> None: "--stack", args.stack, "--mode", - args.mode, + resolved_mode, "--blend-mode", - args.blend_mode, + resolved_blend_mode, "--refine-mode", args.refine_mode, "--reference-type", @@ -159,44 +242,69 @@ def main() -> None: brief_payload.get("design_brief", {}).get("input_context", {}).get("reference_payload_present") ) - qa_input_text = brief_payload.get("hard_prompt") or brief_payload.get("soft_prompt") - if not qa_input_text: - qa_input_text = json.dumps(brief_payload, ensure_ascii=False) + qa_payload: dict[str, Any] + if resolved_mode == "brief+prompt": + qa_input_text = brief_payload.get("hard_prompt") or brief_payload.get("soft_prompt") + if not qa_input_text: + qa_input_text = json.dumps(brief_payload, ensure_ascii=False) - with tempfile.NamedTemporaryFile("w", encoding="utf-8", suffix=".txt", delete=False) as tmp: - tmp.write(qa_input_text) - tmp_path = tmp.name + with tempfile.NamedTemporaryFile("w", encoding="utf-8", suffix=".txt", delete=False) as tmp: + tmp.write(qa_input_text) + tmp_path = tmp.name - try: - qa_cmd = [py, str(QA_SCRIPT), "--input", tmp_path, "--min-ai-rules", str(args.min_ai_rules)] - expected_lang = brief_payload.get("language") - if expected_lang in {"en", "zh"}: - qa_cmd.extend(["--lang", expected_lang]) - qa_cmd.extend(["--require-refine-mode", args.refine_mode]) - qa_cmd.extend(["--require-reference-type", resolved_reference_type]) - if reference_payload_present: - qa_cmd.append("--require-reference-signals") - if selected_style: - qa_cmd.extend(["--style", selected_style]) - qa_payload = run_json_command(qa_cmd) - finally: try: - os.remove(tmp_path) - except OSError: - pass + qa_cmd = [py, str(QA_SCRIPT), "--input", tmp_path, "--min-ai-rules", str(args.min_ai_rules)] + expected_lang = brief_payload.get("language") + if expected_lang in {"en", "zh"}: + qa_cmd.extend(["--lang", expected_lang]) + qa_cmd.extend(["--require-refine-mode", args.refine_mode]) + qa_cmd.extend(["--require-reference-type", resolved_reference_type]) + if reference_payload_present: + qa_cmd.append("--require-reference-signals") + if selected_style: + qa_cmd.extend(["--style", selected_style]) + qa_payload = run_json_command(qa_cmd) + finally: + try: + os.remove(tmp_path) + except OSError: + pass + else: + qa_payload = { + "status": "pass", + "violations": [], + "warnings": [ + { + "code": "QA_SKIPPED_BRIEF_ONLY", + "message": "Prompt QA skipped in handbook mode (brief-only).", + } + ], + "checks": [], + } + + manual_assistant = build_manual_assistant( + query=args.query, + stack=args.stack, + selected_style=selected_style, + search_payload=search_payload, + brief_payload=brief_payload, + ) output = { "status": qa_payload.get("status"), + "workflow": args.workflow, + "mode": resolved_mode, "query": args.query, "stack": args.stack, "style_type_filter": args.style_type, - "blend_mode": args.blend_mode, + "blend_mode": resolved_blend_mode, "refine_mode": args.refine_mode, "reference_type": resolved_reference_type, "strict_reference_schema": args.strict_reference_schema, "selected_style": selected_style, "candidates": search_payload.get("candidates", []), "result": brief_payload, + "manual_assistant": manual_assistant, "quality_gate": qa_payload, } From fe7aea629c71573568096e7b5365cd98e0970f0e Mon Sep 17 00:00:00 2001 From: anx4758 Date: Tue, 24 Feb 2026 19:54:44 +0800 Subject: [PATCH 2/4] chore: bump skill package version to 0.1.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1db07ca..33d8ae5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@anxforever/stylekit-style-prompts-skill", - "version": "0.1.0", + "version": "0.1.1", "description": "Standalone StyleKit style-prompts skill installer for Codex and Claude.", "license": "MIT", "type": "commonjs", From fe89e62355d830dfaf79659c1aceb0c497887d06 Mon Sep 17 00:00:00 2001 From: anx4758 Date: Tue, 24 Feb 2026 20:04:04 +0800 Subject: [PATCH 3/4] feat: add novice decision-assistant flow for style selection --- README.md | 1 + SKILL.md | 10 +++ scripts/run_pipeline.py | 161 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 171 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e5c50f8..c907320 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ python3 scripts/run_pipeline.py --workflow codegen --query "高端科技SaaS财 说明: - 默认是 `--workflow manual`(手册/知识库模式):输出设计简报 + 手册化建议,不强制走 prompt QA。 - 若要生成并严格审查 prompt,请显式加 `--workflow codegen`。 +- 在 manual 模式下,会额外输出 `manual_assistant.decision_assistant`,包含:候选风格卡片、给新手的引导问题、以及用户选定风格后的下一步命令模板。 ## 3) 回归门禁 diff --git a/SKILL.md b/SKILL.md index 281b6d4..113f96d 100644 --- a/SKILL.md +++ b/SKILL.md @@ -79,6 +79,16 @@ CI one-command gate: `python scripts/generate_brief.py --query "" --stack nextjs --mode brief+prompt --reference-file refs/screen-analysis.json --strict-reference-schema` 9. If quality gate fails, run audit + fix workflow. +## Workflow 1.5: Novice Decision Assist (Recommended) + +1. User only provides a high-level goal (example: "I want to build a blog"). +2. Run handbook mode: + `python scripts/run_pipeline.py --query "" --stack nextjs --format json` +3. Read `manual_assistant.decision_assistant.recommended_style_options` and explain 3-4 options with trade-offs. +4. Ask `manual_assistant.decision_assistant.decision_questions` to help user pick direction. +5. After user selects one option, run codegen mode with forced style: + `python scripts/run_pipeline.py --workflow codegen --query "" --stack nextjs --style --blend-mode off --format json` + ## Workflow 2: Existing Prompt -> Quality Audit -> Fix Suggestions 1. Audit prompt text: diff --git a/scripts/run_pipeline.py b/scripts/run_pipeline.py index d6b32bf..405a7d2 100644 --- a/scripts/run_pipeline.py +++ b/scripts/run_pipeline.py @@ -19,12 +19,29 @@ PRODUCT_HINTS = { "dashboard": ["dashboard", "admin", "panel", "console", "后台", "仪表盘", "控制台"], + "blog": ["blog", "article", "post", "editorial", "博客", "文章", "专栏", "内容站"], "landing-page": ["landing", "hero", "marketing", "homepage", "落地页", "首页", "营销"], "ecommerce": ["shop", "store", "ecommerce", "checkout", "商品", "电商", "购物", "支付"], "docs": ["docs", "documentation", "guide", "manual", "文档", "说明", "手册"], "portfolio": ["portfolio", "case study", "作品集", "案例"], } +NOVICE_HINTS = [ + "一窍不通", + "不会", + "不懂", + "小白", + "新手", + "不确定", + "不知道", + "just started", + "beginner", + "new to frontend", + "no idea", +] + +OPTION_IDS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + def infer_product_type(query: str) -> str: text = (query or "").lower() @@ -34,6 +51,129 @@ def infer_product_type(query: str) -> str: return "general-web-product" +def infer_user_confidence(query: str) -> str: + text = (query or "").lower() + if any(keyword in text for keyword in NOVICE_HINTS): + return "low" + return "medium" + + +def is_zh(text: str) -> bool: + return any("\u4e00" <= ch <= "\u9fff" for ch in text or "") + + +def style_complexity_label(candidate: dict[str, Any], zh: bool) -> str: + tags = {str(tag).lower() for tag in (candidate.get("preview", {}) or {}).get("tags", [])} + style_type = str(candidate.get("styleType", "")).lower() + if "expressive" in tags or "high-contrast" in tags: + return "进阶" if zh else "advanced" + if style_type == "layout" or "minimal" in tags or "modern" in tags: + return "入门友好" if zh else "beginner-friendly" + return "标准" if zh else "standard" + + +def style_risk_note(candidate: dict[str, Any], product_type: str, zh: bool) -> str: + tags = {str(tag).lower() for tag in (candidate.get("preview", {}) or {}).get("tags", [])} + style_type = str(candidate.get("styleType", "")).lower() + if product_type in {"docs", "dashboard"} and ("expressive" in tags or "high-contrast" in tags): + return "视觉冲击较强,可能影响信息可读性。" if zh else "Very expressive; can reduce dense-information readability." + if product_type in {"landing-page", "ecommerce"} and style_type == "layout": + return "这是布局型风格,建议后续再配一个视觉风格。" if zh else "This is layout-first; pair with a visual style in the next step." + return "风险较低,适合作为默认起点。" if zh else "Low implementation risk; good default starting point." + + +def build_style_options(search_payload: dict[str, Any], product_type: str, zh: bool, top: int = 4) -> list[dict[str, Any]]: + candidates = search_payload.get("candidates", [])[:top] + options: list[dict[str, Any]] = [] + for idx, candidate in enumerate(candidates): + option_id = OPTION_IDS[idx] if idx < len(OPTION_IDS) else f"OPT{idx + 1}" + options.append( + { + "option_id": option_id, + "slug": candidate.get("slug"), + "name": candidate.get("name"), + "nameEn": candidate.get("nameEn"), + "styleType": candidate.get("styleType"), + "reason": candidate.get("reason_summary"), + "complexity": style_complexity_label(candidate, zh), + "risk_note": style_risk_note(candidate, product_type, zh), + "keywords": (candidate.get("preview", {}) or {}).get("keywords", [])[:6], + "tags": (candidate.get("preview", {}) or {}).get("tags", [])[:4], + } + ) + return options + + +def build_decision_questions(product_type: str, zh: bool) -> list[dict[str, Any]]: + if zh: + questions = [ + { + "id": "visual_intensity", + "question": "你希望页面更偏哪种视觉强度?", + "choices": ["克制耐看", "平衡现代", "强烈个性"], + }, + { + "id": "content_density", + "question": "博客内容展示更偏向哪种?", + "choices": ["长文阅读优先", "图文平衡", "视觉封面优先"], + }, + { + "id": "interaction_level", + "question": "你希望动效强度如何?", + "choices": ["几乎无动效", "适中动效", "明显动效"], + }, + ] + if product_type in {"docs", "dashboard"}: + questions.append( + { + "id": "readability_priority", + "question": "是否把可读性/信息效率放在最高优先级?", + "choices": ["是,优先可读性", "平衡", "不是,视觉更重要"], + } + ) + return questions + + questions = [ + { + "id": "visual_intensity", + "question": "How strong should the visual style be?", + "choices": ["Calm and clean", "Balanced modern", "Bold and expressive"], + }, + { + "id": "content_density", + "question": "How should blog content be presented?", + "choices": ["Long-form reading first", "Balanced text and visuals", "Visual-first covers"], + }, + { + "id": "interaction_level", + "question": "How much motion should be used?", + "choices": ["Minimal motion", "Moderate motion", "Noticeable motion"], + }, + ] + if product_type in {"docs", "dashboard"}: + questions.append( + { + "id": "readability_priority", + "question": "Should readability and information efficiency be the highest priority?", + "choices": ["Yes", "Balanced", "No, visual impact first"], + } + ) + return questions + + +def build_next_step_templates(style_options: list[dict[str, Any]], query: str, stack: str, zh: bool) -> dict[str, Any]: + selected = style_options[0]["slug"] if style_options else "" + if zh: + return { + "after_user_selects_style": f'python scripts/run_pipeline.py --workflow codegen --query "{query}" --stack {stack} --style {selected} --blend-mode off --format json', + "assistant_script": "先展示 3-4 个风格选项并解释差异,用户选择后再进入代码生成。", + } + return { + "after_user_selects_style": f'python scripts/run_pipeline.py --workflow codegen --query "{query}" --stack {stack} --style {selected} --blend-mode off --format json', + "assistant_script": "Present 3-4 style options with trade-offs, then generate code after user selection.", + } + + def build_manual_assistant( *, query: str, @@ -45,17 +185,28 @@ def build_manual_assistant( design_brief = brief_payload.get("design_brief", {}) or {} design_intent = design_brief.get("design_intent", {}) or {} style_choice = design_brief.get("style_choice", {}) or {} + zh = is_zh(query) + product_type = infer_product_type(query) + style_options = build_style_options(search_payload, product_type=product_type, zh=zh) + decision_questions = build_decision_questions(product_type=product_type, zh=zh) + next_step_templates = build_next_step_templates(style_options=style_options, query=query, stack=stack, zh=zh) return { "purpose": "Use this as a frontend design handbook context before generating code.", "product_profile": { "query": query, - "inferred_product_type": infer_product_type(query), + "inferred_product_type": product_type, + "user_confidence": infer_user_confidence(query), "stack": stack, "purpose": design_intent.get("purpose"), "audience": design_intent.get("audience"), "tone": design_intent.get("tone"), }, + "decision_assistant": { + "recommended_style_options": style_options, + "decision_questions": decision_questions, + "next_step_templates": next_step_templates, + }, "style_recommendation": { "selected_style": selected_style, "primary": style_choice.get("primary", {}), @@ -147,6 +298,7 @@ def to_markdown(payload: dict[str, Any]) -> str: manual = payload.get("manual_assistant", {}) or {} profile = manual.get("product_profile", {}) or {} handbook = manual.get("implementation_handbook", {}) or {} + decision = manual.get("decision_assistant", {}) or {} if manual: lines.extend( [ @@ -160,6 +312,13 @@ def to_markdown(payload: dict[str, Any]) -> str: f"- Interaction rules: {len(handbook.get('interaction_rules', []))}", ] ) + options = decision.get("recommended_style_options", [])[:4] + if options: + lines.extend(["", "## Style Options"]) + for option in options: + lines.append( + f"- [{option.get('option_id')}] `{option.get('slug')}` / {option.get('nameEn')} - {option.get('complexity')} - {option.get('risk_note')}" + ) return "\n".join(lines) From 862710adfb23adc10e4edc09c2fbb7bc28a9227b Mon Sep 17 00:00:00 2001 From: anx4758 Date: Tue, 24 Feb 2026 20:15:42 +0800 Subject: [PATCH 4/4] feat: add CC decision conversation template for novice users --- README.md | 1 + SKILL.md | 2 + .../cc-decision-conversation-template.md | 52 +++++++++++++++++++ scripts/run_pipeline.py | 41 +++++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 references/cc-decision-conversation-template.md diff --git a/README.md b/README.md index c907320..29e0016 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ python3 scripts/run_pipeline.py --workflow codegen --query "高端科技SaaS财 - 默认是 `--workflow manual`(手册/知识库模式):输出设计简报 + 手册化建议,不强制走 prompt QA。 - 若要生成并严格审查 prompt,请显式加 `--workflow codegen`。 - 在 manual 模式下,会额外输出 `manual_assistant.decision_assistant`,包含:候选风格卡片、给新手的引导问题、以及用户选定风格后的下一步命令模板。 +- 可直接复用对话模板:`references/cc-decision-conversation-template.md`。 ## 3) 回归门禁 diff --git a/SKILL.md b/SKILL.md index 113f96d..43f6147 100644 --- a/SKILL.md +++ b/SKILL.md @@ -88,6 +88,7 @@ CI one-command gate: 4. Ask `manual_assistant.decision_assistant.decision_questions` to help user pick direction. 5. After user selects one option, run codegen mode with forced style: `python scripts/run_pipeline.py --workflow codegen --query "" --stack nextjs --style --blend-mode off --format json` +6. Follow `references/cc-decision-conversation-template.md` for a turn-by-turn assistant script. ## Workflow 2: Existing Prompt -> Quality Audit -> Fix Suggestions @@ -163,6 +164,7 @@ If stack is unknown, fallback to framework-agnostic Tailwind semantics. - `references/frontend-design-principles.md`: distinctiveness and anti-generic design heuristics. - `references/design-system-patterns.md`: token hierarchy and component architecture. - `references/accessibility-gate.md`: WCAG + mobile touch baseline for prompt quality. +- `references/cc-decision-conversation-template.md`: assistant dialogue template for novice user decision flow. - `scripts/refresh-style-prompts.sh`: rebuild style dataset from local repo. - `scripts/search_stylekit.py`: query -> ranked style candidates. - `scripts/generate_brief.py`: query -> design brief + prompts. diff --git a/references/cc-decision-conversation-template.md b/references/cc-decision-conversation-template.md new file mode 100644 index 0000000..3c84630 --- /dev/null +++ b/references/cc-decision-conversation-template.md @@ -0,0 +1,52 @@ +# CC Decision Conversation Template + +Use this when the user only has a high-level idea (for example: "I want a blog"). + +## Goal + +Help non-frontend users make design decisions first, then generate implementation. + +## Phase 1: Clarify Product Context + +- Assistant prompt: + - "I will help narrow direction first. Who is the audience, and is the primary goal reading, conversion, or brand expression?" +- Expected output: + - product type + - target audience + - top 1-2 business goals + +## Phase 2: Present Style Choices + +- Input source: + - `manual_assistant.decision_assistant.recommended_style_options` +- Assistant prompt: + - "Here are 3-4 style options with complexity and risk. Pick A/B/C/D, and I will generate with that style." +- Explain each option in plain language: + - visual tone + - implementation complexity + - readability/risk trade-off + +## Phase 3: Ask Guiding Questions + +- Input source: + - `manual_assistant.decision_assistant.decision_questions` +- Ask 2-3 questions max: + - visual intensity + - content density preference + - motion level + +## Phase 4: Lock Style and Generate + +- After user picks a style option, run: + - `python scripts/run_pipeline.py --workflow codegen --query "" --stack --style --blend-mode off --format json` +- Then generate implementation with: + - homepage + - detail page (for blog: post page) + - list page (for blog: article list page) + +## Output Rules for Assistant + +- Never force a style before user selects. +- Explain style differences in non-technical language first. +- Preserve user language (Chinese in -> Chinese out, English in -> English out). +- Keep decision loop short: propose options -> ask questions -> confirm -> generate. diff --git a/scripts/run_pipeline.py b/scripts/run_pipeline.py index 405a7d2..ec81239 100644 --- a/scripts/run_pipeline.py +++ b/scripts/run_pipeline.py @@ -174,6 +174,45 @@ def build_next_step_templates(style_options: list[dict[str, Any]], query: str, s } +def build_cc_conversation_flow(zh: bool) -> dict[str, Any]: + if zh: + return { + "phase_1_opening": { + "goal": "确认产品目标与用户场景", + "assistant_template": "我先帮你把方向收敛。你这个产品主要给谁用?核心目标是阅读、转化,还是展示品牌?", + "expected_user_input": ["产品类型", "目标用户", "主要任务"], + }, + "phase_2_style_decision": { + "goal": "给用户可理解的 3-4 个风格选项并解释差异", + "assistant_template": "我给你 4 个可选风格,每个都附上难度和风险。你选 A/B/C/D,我再按这个风格生成页面。", + "expected_user_input": ["选项ID", "偏好强度", "是否优先可读性"], + }, + "phase_3_delivery": { + "goal": "锁定风格并进入实现", + "assistant_template": "已锁定风格。我会按该风格输出首页、文章页、列表页,并保持统一视觉语言。", + "expected_user_input": ["技术栈", "是否深色模式", "是否需要多语言"], + }, + } + + return { + "phase_1_opening": { + "goal": "Clarify product goal and audience context", + "assistant_template": "Let me narrow the direction first. Who is this for, and is the primary goal reading, conversion, or brand expression?", + "expected_user_input": ["product type", "target audience", "primary task"], + }, + "phase_2_style_decision": { + "goal": "Offer 3-4 understandable style options with trade-offs", + "assistant_template": "I will give you 4 style options with complexity and risk notes. Pick A/B/C/D, then I will generate with that style.", + "expected_user_input": ["option id", "visual intensity preference", "readability priority"], + }, + "phase_3_delivery": { + "goal": "Lock style and move to implementation", + "assistant_template": "Style locked. I will generate homepage, post page, and listing page with consistent visual language.", + "expected_user_input": ["tech stack", "dark mode needed", "multilingual needed"], + }, + } + + def build_manual_assistant( *, query: str, @@ -190,6 +229,7 @@ def build_manual_assistant( style_options = build_style_options(search_payload, product_type=product_type, zh=zh) decision_questions = build_decision_questions(product_type=product_type, zh=zh) next_step_templates = build_next_step_templates(style_options=style_options, query=query, stack=stack, zh=zh) + conversation_flow = build_cc_conversation_flow(zh=zh) return { "purpose": "Use this as a frontend design handbook context before generating code.", @@ -206,6 +246,7 @@ def build_manual_assistant( "recommended_style_options": style_options, "decision_questions": decision_questions, "next_step_templates": next_step_templates, + "cc_conversation_flow": conversation_flow, }, "style_recommendation": { "selected_style": selected_style,