diff --git a/reports/context_budget.json b/reports/context_budget.json index e408ca1c..8519419f 100644 --- a/reports/context_budget.json +++ b/reports/context_budget.json @@ -6,9 +6,9 @@ "context_budget_tier": "production", "context_budget_limit": 1000, "skill_body_tokens": 824, - "other_text_tokens": 271901, + "other_text_tokens": 274311, "estimated_initial_load_tokens": 1000, - "estimated_total_text_tokens": 272725, + "estimated_total_text_tokens": 275135, "relevant_file_count": 165, "unused_resource_dirs": [], "quality_signal_points": 140, diff --git a/scripts/render_intent_dialogue.py b/scripts/render_intent_dialogue.py index 5af75295..dc165673 100644 --- a/scripts/render_intent_dialogue.py +++ b/scripts/render_intent_dialogue.py @@ -100,6 +100,26 @@ def build_questions(focus: str) -> list[dict]: return base +def build_opening_styles() -> list[dict]: + return [ + { + "label": "温柔陪伴型", + "best_when": "用户想法还散、还在试探,或者需要先被接住。", + "message": "我们先不急着把它说成一个很完整的 skill。你就像跟我聊天一样,先说说你最想让它以后稳稳接住哪类重复工作;如果它做得很理想,最后应该交回你一个什么结果。", + }, + { + "label": "专业教练型", + "best_when": "用户目标比较明确,希望被高效带着走。", + "message": "我们先把这件事讲清楚,再决定 skill 怎么设计。你先告诉我三件事:它要接住的重复任务是什么,别人通常会给它什么材料,最后你希望它交付什么结果。", + }, + { + "label": "共创伙伴型", + "best_when": "用户已经有一些想法,希望一起打磨,不想被填表。", + "message": "我们把这次当成一次共创。你先给我一个粗糙版本就行,我先帮你看它真正的核心任务是什么,再一起决定边界、结构和接下来最值的一步。", + }, + ] + + def build_summary(skill_dir: Path) -> dict: skill_text = (skill_dir / "SKILL.md").read_text(encoding="utf-8") frontmatter, body = parse_frontmatter(skill_text) @@ -108,6 +128,7 @@ def build_summary(skill_dir: Path) -> dict: title = extract_title(body, name.replace("-", " ").title()) focus = classify_focus(description) questions = build_questions(focus) + opening_styles = build_opening_styles() output = { "capability_sentence": f"{title} should turn a recurring request into a reliable reusable output without widening the boundary unnecessarily.", "required_capture": [ @@ -126,8 +147,20 @@ def build_summary(skill_dir: Path) -> dict: "title": title, "description": description, "focus": focus, - "opening_frame": "Let's shape the skill around the real work, the desired outcome, and the standards you care about before we start adding structure.", + "opening_frame": "Let's start from the real work, the result you care about, and the standards that matter here. We can make the structure clearer after that.", "reference_note": "If you already have examples you admire, bring them in. We will learn the pattern, not copy the source.", + "conversation_path": [ + "Start with the user's own words, not package vocabulary.", + "Reflect the job, output, and non-goals back in one clean sentence.", + "Only then offer a tiny scaffold if it would help the user move faster.", + ], + "opening_styles": opening_styles, + "optional_scaffold": [ + "The repeated job it should take over", + "The real inputs people will hand to it", + "The useful output it should hand back", + "What it should clearly refuse", + ], "questions": questions, "output": output, } @@ -143,20 +176,45 @@ def render_markdown(summary: dict) -> str: "", summary["opening_frame"], "", - "## Why Start Here", - "", - "Use this short dialogue before deep authoring. The goal is to learn the real job, output, exclusions, and constraints so the first package is small but accurate.", - "", - "## Current Anchor", - "", - f"- Title: `{summary['title']}`", - f"- Description: {summary['description']}", - f"- Focus: `{summary['focus']}`", - f"- Reference note: {summary['reference_note']}", - "", - "## Questions To Ask", + "## Opening Tone Options", "", ] + for item in summary["opening_styles"]: + lines.extend( + [ + f"### {item['label']}", + "", + f"- Best when: {item['best_when']}", + f"- Example: {item['message']}", + "", + ] + ) + lines.extend( + [ + "## Conversation Path", + "", + ] + ) + for idx, item in enumerate(summary["conversation_path"], start=1): + lines.append(f"{idx}. {item}") + lines.extend( + [ + "", + "## Why Start Here", + "", + "Use this short dialogue before deep authoring. The goal is to learn the real job, output, exclusions, and constraints so the first package is small but accurate.", + "", + "## Current Anchor", + "", + f"- Title: `{summary['title']}`", + f"- Description: {summary['description']}", + f"- Focus: `{summary['focus']}`", + f"- Reference note: {summary['reference_note']}", + "", + "## Questions To Ask", + "", + ] + ) for idx, item in enumerate(summary["questions"], start=1): lines.extend( [ @@ -171,8 +229,11 @@ def render_markdown(summary: dict) -> str: "", f"- Capability sentence: {summary['output']['capability_sentence']}", f"- Recommended first gate: `{summary['output']['recommended_first_gate']}`", + "- Tiny optional scaffold:", ] ) + for item in summary["optional_scaffold"]: + lines.append(f" - {item}") for item in summary["output"]["required_capture"]: lines.append(f"- Capture: `{item}`") return "\n".join(lines).strip() + "\n" diff --git a/scripts/render_iteration_directions.py b/scripts/render_iteration_directions.py index af0eb9ed..921b9107 100644 --- a/scripts/render_iteration_directions.py +++ b/scripts/render_iteration_directions.py @@ -37,6 +37,12 @@ def load_manifest(path: Path) -> dict: return json.loads(path.read_text(encoding="utf-8")) +def load_json(path: Path) -> dict: + if not path.exists(): + return {} + return json.loads(path.read_text(encoding="utf-8")) + + def extract_sections(body: str) -> dict[str, str]: sections: dict[str, list[str]] = {} current = "_preamble" @@ -69,6 +75,8 @@ def direction(priority: int, title: str, why: str, actions: list[str], unlocks: "why": why, "actions": actions, "unlocks": unlocks, + "do_now": "", + "wait_on": "", } @@ -77,6 +85,7 @@ def build_directions(skill_dir: Path) -> tuple[dict, list[dict]]: frontmatter, body = parse_frontmatter(skill_text) sections = extract_sections(body) manifest = load_manifest(skill_dir / "manifest.json") + benchmark = load_json(skill_dir / "reports" / "github-benchmark-scan.json") description = frontmatter.get("description", "") maturity = manifest.get("maturity_tier", "scaffold") @@ -109,6 +118,21 @@ def build_directions(skill_dir: Path) -> tuple[dict, list[dict]]: "Stronger execution quality without bloating the entrypoint.", ) ) + if benchmark.get("repositories"): + top_repo = benchmark["repositories"][0] + candidates.append( + direction( + 2, + "Borrow one proven pattern on purpose", + "You already have public benchmark objects. The next gain is to choose one pattern intentionally instead of absorbing everything loosely.", + [ + f"Read the strongest pattern from {top_repo.get('full_name', 'the top benchmark repo')}.", + "Decide whether to borrow method, structure, execution, or portability, but only one of them first.", + "Record what you will not borrow so the package stays light.", + ], + "A cleaner package shape with less accidental over-design.", + ) + ) if maturity == "scaffold": candidates.append( direction( @@ -169,11 +193,25 @@ def build_directions(skill_dir: Path) -> tuple[dict, list[dict]]: ) chosen = sorted(candidates, key=lambda item: item["priority"])[:3] + for index, item in enumerate(chosen, start=1): + item["do_now"] = ( + "Do this first." + if index == 1 + else "Do this after the first move lands cleanly." + ) + item["wait_on"] = ( + "Wait to add broader structure until this move clearly improves reliability." + if index == 1 + else "Wait until the package has evidence that this extra structure is justified." + ) summary = { "skill_name": frontmatter.get("name", skill_dir.name), "description": description, "maturity_tier": maturity, "selection_rule": "Pick the three smallest next steps that increase reliability more than they increase context cost.", + "recommended_now": chosen[0]["title"] if chosen else "", + "recommended_now_why": chosen[0]["why"] if chosen else "", + "defer_for_now": chosen[-1]["title"] if chosen else "", } return summary, chosen @@ -186,6 +224,9 @@ def render_markdown(summary: dict, directions: list[dict]) -> str: "", f"- Maturity tier: `{summary['maturity_tier']}`", f"- Selection rule: {summary['selection_rule']}", + f"- Start here: `{summary['recommended_now']}`", + f"- Why first: {summary['recommended_now_why']}", + f"- Defer for now: `{summary['defer_for_now']}`", "", "## Top 3 Next Moves", "", @@ -196,13 +237,15 @@ def render_markdown(summary: dict, directions: list[dict]) -> str: f"### {item['priority']}. {item['title']}", "", f"- Why now: {item['why']}", + f"- Timing: {item['do_now']}", "- Recommended actions:", ] ) for action in item["actions"]: lines.append(f" - {action}") - lines.append(f"- Unlocks: {item['unlocks']}") - lines.append("") + lines.append(f"- Unlocks: {item['unlocks']}") + lines.append(f"- Wait on: {item['wait_on']}") + lines.append("") return "\n".join(lines).strip() + "\n" diff --git a/scripts/render_review_viewer.py b/scripts/render_review_viewer.py index 0424c49c..3e81c4c4 100644 --- a/scripts/render_review_viewer.py +++ b/scripts/render_review_viewer.py @@ -43,6 +43,11 @@ def load_specific_promotion(skill_dir: Path) -> dict: return payload if isinstance(payload, dict) else {} +def load_benchmark_summary(skill_dir: Path) -> dict: + payload = load_json(skill_dir / "reports" / "github-benchmark-scan.json") + return payload if isinstance(payload, dict) else {} + + def ensure_report_inputs(skill_dir: Path) -> dict: overview_json = skill_dir / "reports" / "skill-overview.json" intent_json = skill_dir / "reports" / "intent-dialogue.json" @@ -62,6 +67,7 @@ def ensure_report_inputs(skill_dir: Path) -> dict: baseline = load_baseline_summary(skill_dir) compare = load_specific_compare(skill_dir) promotion = load_specific_promotion(skill_dir) + benchmark = load_benchmark_summary(skill_dir) return { "overview": overview, "intent": intent, @@ -71,6 +77,7 @@ def ensure_report_inputs(skill_dir: Path) -> dict: "baseline": baseline, "compare": compare, "promotion": promotion, + "benchmark": benchmark, } @@ -112,6 +119,19 @@ def compare_rows(compare: dict) -> list[dict]: return rows +def benchmark_cards(benchmark: dict) -> list[dict]: + cards = [] + for repo in benchmark.get("repositories", [])[:3]: + cards.append( + { + "name": repo.get("full_name", "Unknown repo"), + "borrow": repo.get("borrow", [])[:2], + "avoid": repo.get("avoid", [])[:1], + } + ) + return cards + + def render_html(report: dict) -> str: overview = report["overview"] intent = report["intent"] @@ -122,8 +142,10 @@ def render_html(report: dict) -> str: baseline = report.get("baseline", {}) compare = report.get("compare", {}) promotion = report.get("promotion", {}) + benchmark = report.get("benchmark", {}) architecture = architecture_steps(overview) compare_table_rows = compare_rows(compare) + benchmark_rows = benchmark_cards(benchmark) strength_items = "".join(f"
No promotion summary is attached to this package yet.
" + benchmark_html = "" + if benchmark_rows: + benchmark_html = "".join( + ( + "Borrow now
" + + ("No borrow cues recorded.
") + + "Avoid
" + + ("No avoid cues recorded.
") + + "No GitHub benchmark scan has been attached to this package yet.
" + return f""" @@ -414,6 +453,21 @@ def render_html(report: dict) -> str: +Borrow now
" + + ("None yet.
") + + "Avoid
" + + ("None yet.
") + + "Use this section when a first-time reader needs the shape and intent quickly, before reading the full package.
+If a GitHub benchmark scan exists, surface the strongest borrow and avoid cues here instead of hiding them in raw report files.
+No benchmark scan recorded yet. Run the GitHub benchmark scan first.