diff --git a/.gitignore b/.gitignore index f003499..6da79bf 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,6 @@ tests/tmp_iteration_directions/ tests/tmp_review_viewer/ tests/tmp_feedback/ tests/tmp_github_benchmark_scan/ +tests/tmp_intent_confidence/ +tests/tmp_reference_synthesis/ reports/release_snapshots/ diff --git a/Makefile b/Makefile index e9adb4c..e4e1699 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ PYTHON ?= python3 -.PHONY: eval eval-suite route-scorecard route-confusion-check description-optimization judge-blind-eval description-optimization-check promotion-check yao-cli-check skill-overview-check review-viewer-check feedback-check baseline-compare-check reference-scan-check github-benchmark-scan-check iteration-directions-check description-drift-history iteration-ledger results-panel regression-history context-reports portability-report portability-check failure-regression-check package-check package-failure-check snapshot-check validate lint governance-check resource-boundary-check quality-check test ci-test clean +.PHONY: eval eval-suite route-scorecard route-confusion-check description-optimization judge-blind-eval description-optimization-check promotion-check yao-cli-check skill-overview-check review-viewer-check feedback-check baseline-compare-check reference-scan-check github-benchmark-scan-check intent-confidence-check reference-synthesis-check iteration-directions-check description-drift-history iteration-ledger results-panel regression-history context-reports portability-report portability-check failure-regression-check package-check package-failure-check snapshot-check validate lint governance-check resource-boundary-check quality-check test ci-test clean eval: $(PYTHON) scripts/trigger_eval.py --description-file evals/improved_description.txt --cases evals/trigger_cases.json --baseline-description-file evals/baseline_description.txt @@ -47,6 +47,12 @@ reference-scan-check: github-benchmark-scan-check: $(PYTHON) tests/verify_github_benchmark_scan.py +intent-confidence-check: + $(PYTHON) tests/verify_intent_confidence.py + +reference-synthesis-check: + $(PYTHON) tests/verify_reference_synthesis.py + iteration-directions-check: $(PYTHON) tests/verify_iteration_directions.py @@ -98,10 +104,10 @@ resource-boundary-check: quality-check: $(PYTHON) tests/verify_quality_checks.py -test: eval eval-suite route-scorecard route-confusion-check description-optimization description-optimization-check promotion-check yao-cli-check skill-overview-check review-viewer-check feedback-check baseline-compare-check reference-scan-check github-benchmark-scan-check iteration-directions-check description-drift-history iteration-ledger regression-history context-reports portability-report portability-check failure-regression-check package-check package-failure-check snapshot-check validate lint governance-check resource-boundary-check quality-check +test: eval eval-suite route-scorecard route-confusion-check description-optimization description-optimization-check promotion-check yao-cli-check skill-overview-check review-viewer-check feedback-check baseline-compare-check reference-scan-check github-benchmark-scan-check intent-confidence-check reference-synthesis-check iteration-directions-check description-drift-history iteration-ledger regression-history context-reports portability-report portability-check failure-regression-check package-check package-failure-check snapshot-check validate lint governance-check resource-boundary-check quality-check ci-test: $(PYTHON) scripts/ci_test.py clean: - rm -rf dist tests/tmp tests/tmp_snapshot tests/tmp_cli tests/tmp_skill_overview tests/tmp_reference_scan tests/tmp_iteration_directions tests/tmp_review_viewer tests/tmp_feedback tests/tmp_github_benchmark_scan tests/tmp_baseline_compare.json tests/tmp_baseline_compare.md + rm -rf dist tests/tmp tests/tmp_snapshot tests/tmp_cli tests/tmp_skill_overview tests/tmp_reference_scan tests/tmp_iteration_directions tests/tmp_review_viewer tests/tmp_feedback tests/tmp_github_benchmark_scan tests/tmp_intent_confidence tests/tmp_reference_synthesis tests/tmp_baseline_compare.json tests/tmp_baseline_compare.md diff --git a/reports/context_budget.json b/reports/context_budget.json index 527fce3..1f6abf0 100644 --- a/reports/context_budget.json +++ b/reports/context_budget.json @@ -6,10 +6,10 @@ "context_budget_tier": "production", "context_budget_limit": 1000, "skill_body_tokens": 824, - "other_text_tokens": 277008, + "other_text_tokens": 291352, "estimated_initial_load_tokens": 1000, - "estimated_total_text_tokens": 277832, - "relevant_file_count": 167, + "estimated_total_text_tokens": 292176, + "relevant_file_count": 184, "unused_resource_dirs": [], "quality_signal_points": 140, "quality_density": 140.0 diff --git a/reports/intent-confidence.json b/reports/intent-confidence.json new file mode 100644 index 0000000..a6d252b --- /dev/null +++ b/reports/intent-confidence.json @@ -0,0 +1,79 @@ +{ + "score": 30, + "band": "low", + "gate_passed": false, + "strengths": [ + "The recurring job is concrete enough to anchor the package." + ], + "gaps": [ + { + "key": "real_inputs", + "label": "Real inputs are missing", + "reason": "Without real inputs, it is hard to choose assets, scripts, or examples.", + "severity": "high" + }, + { + "key": "primary_output", + "label": "Primary output is missing", + "reason": "The package does not yet know what it must hand back.", + "severity": "high" + }, + { + "key": "exclusions", + "label": "Near-neighbor exclusions are missing", + "reason": "The route may blur into nearby requests without an exclusion list.", + "severity": "high" + }, + { + "key": "constraints", + "label": "Constraints are missing", + "reason": "The package does not yet know which tradeoffs matter most.", + "severity": "high" + }, + { + "key": "standards", + "label": "Quality bar is implied, not explicit", + "reason": "The first evaluation target is still underspecified.", + "severity": "medium" + } + ], + "follow_up_questions": [ + { + "slot": "real_inputs", + "question": "What material will people actually hand to this skill in practice?", + "why": "Real input shape decides whether references, scripts, or examples are needed.", + "list": true, + "label": "Real inputs are missing", + "severity": "high" + }, + { + "slot": "primary_output", + "question": "What finished hand-back should this skill return so the next person can keep moving?", + "why": "The output is the anchor for package design and review.", + "list": false, + "label": "Primary output is missing", + "severity": "high" + }, + { + "slot": "exclusions", + "question": "What nearby requests should this skill clearly leave out so the boundary stays clean?", + "why": "Exclusions are the fastest route to better trigger quality.", + "list": true, + "label": "Near-neighbor exclusions are missing", + "severity": "high" + } + ], + "anchor_sentence": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "recommended_action": "Pause before deep authoring and close the highest-leverage gaps first.", + "context": { + "job": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "real_inputs": [], + "primary_output": "", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "exclusions": [], + "constraints": [], + "standards": [], + "correction": "", + "user_references": [] + } +} diff --git a/reports/intent-confidence.md b/reports/intent-confidence.md new file mode 100644 index 0000000..07c3a24 --- /dev/null +++ b/reports/intent-confidence.md @@ -0,0 +1,31 @@ +# Intent Confidence + +- Confidence score: `30/100` +- Confidence band: `low` +- Gate passed: `False` +- Recommended action: Pause before deep authoring and close the highest-leverage gaps first. + +## Current Reading + +Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse. + +## Strong Signals + +- The recurring job is concrete enough to anchor the package. + +## Gaps To Close + +- **Real inputs are missing** (`high`): Without real inputs, it is hard to choose assets, scripts, or examples. +- **Primary output is missing** (`high`): The package does not yet know what it must hand back. +- **Near-neighbor exclusions are missing** (`high`): The route may blur into nearby requests without an exclusion list. +- **Constraints are missing** (`high`): The package does not yet know which tradeoffs matter most. +- **Quality bar is implied, not explicit** (`medium`): The first evaluation target is still underspecified. + +## Follow-Up Questions + +- **What material will people actually hand to this skill in practice?** + - Why: Real input shape decides whether references, scripts, or examples are needed. +- **What finished hand-back should this skill return so the next person can keep moving?** + - Why: The output is the anchor for package design and review. +- **What nearby requests should this skill clearly leave out so the boundary stays clean?** + - Why: Exclusions are the fastest route to better trigger quality. diff --git a/reports/intent-context.json b/reports/intent-context.json new file mode 100644 index 0000000..8fc3e49 --- /dev/null +++ b/reports/intent-context.json @@ -0,0 +1,11 @@ +{ + "job": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "real_inputs": [], + "primary_output": "", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "exclusions": [], + "constraints": [], + "standards": [], + "correction": "", + "user_references": [] +} diff --git a/reports/reference-synthesis.json b/reports/reference-synthesis.json new file mode 100644 index 0000000..4de1d73 --- /dev/null +++ b/reports/reference-synthesis.json @@ -0,0 +1,69 @@ +{ + "skill_name": "yao-meta-skill", + "description": "Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse.", + "intent_confidence": { + "score": 30, + "band": "low", + "gate_passed": false + }, + "github_benchmarks": [], + "source_tracks": [ + { + "source_type": "official", + "name": "Official workflow product ergonomics", + "evidence_mode": "curated-pattern-track", + "matched_keywords": [ + "workflow" + ], + "borrow": "Borrow a first-time operator flow that explains itself before it asks for more structure.", + "avoid": "Do not mimic product polish that adds UI bulk without improving clarity.", + "why_relevant": "This track matches: workflow." + }, + { + "source_type": "research", + "name": "Hypothesis-test-learn loop", + "evidence_mode": "curated-pattern-track", + "matched_keywords": [ + "general fit" + ], + "borrow": "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "avoid": "Do not create experimental overhead that exceeds the skill's real risk tier.", + "why_relevant": "This track matches: general fit." + }, + { + "source_type": "principles", + "name": "Boundary-first design", + "evidence_mode": "curated-pattern-track", + "matched_keywords": [ + "general fit" + ], + "borrow": "Borrow the discipline of defining what the skill should not own before growing the package.", + "avoid": "Do not expand execution assets until route boundaries stay clean.", + "why_relevant": "This track matches: general fit." + } + ], + "synthesis": { + "borrow_now": [ + "Borrow a first-time operator flow that explains itself before it asks for more structure.", + "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "Borrow the discipline of defining what the skill should not own before growing the package." + ], + "avoid_now": [ + "Do not mimic product polish that adds UI bulk without improving clarity.", + "Do not create experimental overhead that exceeds the skill's real risk tier.", + "Do not expand execution assets until route boundaries stay clean." + ], + "quality_risers": [ + "Use GitHub repositories for concrete package and workflow patterns.", + "Use curated official or commercial tracks for entrypoint and operator ergonomics.", + "Use research tracks to justify the smallest evaluation loop that still catches regressions.", + "Use principle tracks to keep the package small, boundary-aware, and outcome-driven." + ], + "decision_prompt": "I pulled concrete GitHub benchmarks and layered them with curated official, research, and principle tracks. Do you want the next draft to borrow one or two of these patterns now, or keep the first pass lighter?", + "source_mix": { + "github_benchmarks": 0, + "curated_tracks": 3, + "user_references": 0 + } + } +} diff --git a/reports/reference-synthesis.md b/reports/reference-synthesis.md new file mode 100644 index 0000000..1829c9c --- /dev/null +++ b/reports/reference-synthesis.md @@ -0,0 +1,55 @@ +# Reference Synthesis + +Skill: `yao-meta-skill` +- Description: Create, refactor, evaluate, and package agent skills from workflows, prompts, transcripts, docs, or notes. Use when asked to create a skill, turn a repeated process into a reusable skill, improve an existing skill, add evals, or package a skill for team reuse. +- Intent confidence: `30/100` (`low`) + +## Live GitHub Benchmarks + +- No live GitHub benchmarks are attached yet. + +## Curated World-Class Pattern Tracks + +### Official workflow product ergonomics +- Type: `official` +- Evidence mode: `curated-pattern-track` +- Why relevant: This track matches: workflow. +- Borrow: Borrow a first-time operator flow that explains itself before it asks for more structure. +- Avoid: Do not mimic product polish that adds UI bulk without improving clarity. + +### Hypothesis-test-learn loop +- Type: `research` +- Evidence mode: `curated-pattern-track` +- Why relevant: This track matches: general fit. +- Borrow: Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed. +- Avoid: Do not create experimental overhead that exceeds the skill's real risk tier. + +### Boundary-first design +- Type: `principles` +- Evidence mode: `curated-pattern-track` +- Why relevant: This track matches: general fit. +- Borrow: Borrow the discipline of defining what the skill should not own before growing the package. +- Avoid: Do not expand execution assets until route boundaries stay clean. + +## Borrow Now + +- Borrow a first-time operator flow that explains itself before it asks for more structure. +- Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed. +- Borrow the discipline of defining what the skill should not own before growing the package. + +## Avoid Now + +- Do not mimic product polish that adds UI bulk without improving clarity. +- Do not create experimental overhead that exceeds the skill's real risk tier. +- Do not expand execution assets until route boundaries stay clean. + +## Quality Lift Thesis + +- Use GitHub repositories for concrete package and workflow patterns. +- Use curated official or commercial tracks for entrypoint and operator ergonomics. +- Use research tracks to justify the smallest evaluation loop that still catches regressions. +- Use principle tracks to keep the package small, boundary-aware, and outcome-driven. + +## Decision Prompt + +I pulled concrete GitHub benchmarks and layered them with curated official, research, and principle tracks. Do you want the next draft to borrow one or two of these patterns now, or keep the first pass lighter? diff --git a/scripts/init_skill.py b/scripts/init_skill.py index 1f5b634..de282b1 100644 --- a/scripts/init_skill.py +++ b/scripts/init_skill.py @@ -5,9 +5,11 @@ from datetime import date from pathlib import Path from github_benchmark_scan import run_github_benchmark_scan +from render_intent_confidence import render_intent_confidence from render_intent_dialogue import render_intent_dialogue from render_iteration_directions import render_iteration_directions from render_reference_scan import parse_reference, render_reference_scan +from render_reference_synthesis import render_reference_synthesis from render_review_viewer import render_review_viewer from render_skill_overview import render_skill_overview @@ -40,10 +42,12 @@ README_TEMPLATE = """# {title} 1. Load the skill through `SKILL.md`. 2. Start with `reports/intent-dialogue.md` to tighten the real job, outputs, exclusions, and the standards you care about. 3. Open `reports/reference-scan.md` to capture external benchmarks and any user-supplied references worth learning from. -4. Follow the workflow steps in `SKILL.md`. -5. Check `reports/skill-overview.html` if you want a fast visual explanation of the package. -6. Open `reports/review-viewer.html` for a compact visual review of the package. -7. Review `reports/iteration-directions.md` for the three most valuable next moves. +4. Review `reports/intent-confidence.md` to see whether the real job, inputs, outputs, and exclusions are clear enough yet. +5. Open `reports/reference-synthesis.md` to see the GitHub benchmarks plus curated official, research, and principle tracks in one place. +6. Follow the workflow steps in `SKILL.md`. +7. Check `reports/skill-overview.html` if you want a fast visual explanation of the package. +8. Open `reports/review-viewer.html` for a compact visual review of the package. +9. Review `reports/iteration-directions.md` for the three most valuable next moves. ## Package Map @@ -51,8 +55,10 @@ README_TEMPLATE = """# {title} - `agents/interface.yaml`: portable interface metadata - `manifest.json`: lifecycle and packaging metadata - `reports/intent-dialogue.md`: front-loaded discovery questions for better boundary design and clearer human alignment +- `reports/intent-confidence.md`: current clarity score, open gaps, and the next follow-up questions worth asking - `reports/github-benchmark-scan.md`: top public benchmark repositories, extracted patterns, and borrow or avoid notes - `reports/reference-scan.md`: benchmark notes from public references, user references, and local constraints +- `reports/reference-synthesis.md`: a combined view of GitHub benchmarks plus curated world-class pattern tracks - `reports/skill-overview.html`: visual overview report - `reports/review-viewer.html`: compact review page for architecture, usage, feedback, and next steps - `reports/iteration-directions.md`: the top three next iteration directions @@ -172,6 +178,7 @@ def initialize_skill( github_query: str | None = None, github_top_n: int = 3, github_fixture_dir: str | None = None, + intent_context: dict | None = None, ) -> dict: title = title or name.replace("-", " ").title() root = Path(output_dir).resolve() / name @@ -200,7 +207,7 @@ def initialize_skill( json.dumps(build_manifest(name, mode, archetype), ensure_ascii=False, indent=2), encoding="utf-8", ) - overview = render_skill_overview(root) + intent_confidence = render_intent_confidence(root, context=intent_context) intent_dialogue = render_intent_dialogue(root) benchmark_scan = None combined_external_references = [*(external_references or [])] @@ -216,17 +223,24 @@ def initialize_skill( root, dedupe_references([*combined_external_references, *(user_references or []), *(local_constraints or [])]), ) + reference_synthesis = render_reference_synthesis(root) + overview = render_skill_overview(root) iteration_directions = render_iteration_directions(root) review_viewer = render_review_viewer(root) artifacts = { "readme": str(root / "README.md"), "manifest": str(root / "manifest.json"), + "intent_confidence_md": intent_confidence["artifacts"]["markdown"], + "intent_confidence_json": intent_confidence["artifacts"]["json"], + "intent_context_json": intent_confidence["artifacts"]["context_json"], "skill_overview_html": overview["artifacts"]["html"], "skill_overview_json": overview["artifacts"]["json"], "intent_dialogue_md": intent_dialogue["artifacts"]["markdown"], "intent_dialogue_json": intent_dialogue["artifacts"]["json"], "reference_scan_md": reference_scan["artifacts"]["markdown"], "reference_scan_json": reference_scan["artifacts"]["json"], + "reference_synthesis_md": reference_synthesis["artifacts"]["markdown"], + "reference_synthesis_json": reference_synthesis["artifacts"]["json"], "iteration_directions_md": iteration_directions["artifacts"]["markdown"], "iteration_directions_json": iteration_directions["artifacts"]["json"], "review_viewer_html": review_viewer["artifacts"]["html"], @@ -241,6 +255,8 @@ def initialize_skill( "mode": mode, "archetype": archetype, "github_benchmark_scan": benchmark_scan, + "intent_confidence": intent_confidence["summary"], + "reference_synthesis": reference_synthesis["summary"], "artifacts": artifacts, } @@ -259,6 +275,13 @@ def main() -> None: parser.add_argument("--github-query") parser.add_argument("--github-top-n", type=int, default=3) parser.add_argument("--github-fixture-dir") + parser.add_argument("--intent-job") + parser.add_argument("--intent-real-input", action="append", default=[]) + parser.add_argument("--intent-primary-output") + parser.add_argument("--intent-exclusion", action="append", default=[]) + parser.add_argument("--intent-constraint", action="append", default=[]) + parser.add_argument("--intent-standard", action="append", default=[]) + parser.add_argument("--intent-correction", default="") args = parser.parse_args() result = initialize_skill( args.name, @@ -273,6 +296,17 @@ def main() -> None: github_query=args.github_query, github_top_n=args.github_top_n, github_fixture_dir=args.github_fixture_dir, + intent_context={ + "job": args.intent_job or args.description, + "real_inputs": args.intent_real_input, + "primary_output": args.intent_primary_output or "", + "description": args.description, + "exclusions": args.intent_exclusion, + "constraints": args.intent_constraint, + "standards": args.intent_standard, + "correction": args.intent_correction, + "user_references": [parse_reference(item, "user")["name"] for item in args.user_reference], + }, ) print(json.dumps(result, ensure_ascii=False, indent=2)) diff --git a/scripts/render_intent_confidence.py b/scripts/render_intent_confidence.py new file mode 100644 index 0000000..007b1cb --- /dev/null +++ b/scripts/render_intent_confidence.py @@ -0,0 +1,379 @@ +#!/usr/bin/env python3 +import argparse +import json +import re +from pathlib import Path +from typing import Any + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + + +GENERIC_PHRASES = { + "turn a repeated workflow into a reusable skill", + "a reusable skill package", + "describe what the skill does and when to use it", + "turn rough requests into a compact reusable demo skill", +} + +GENERIC_TOKENS = { + "workflow", + "skill", + "package", + "reusable", + "repeated", + "request", + "requests", + "task", + "tasks", + "work", + "job", +} + +FOLLOW_UP_LIBRARY = { + "job_specificity": { + "slot": "job", + "question": "If you say it plainly, what concrete repeated task should this skill own every time?", + "why": "A skill needs a real recurring job, not only a generic packaging goal.", + "list": False, + }, + "real_inputs": { + "slot": "real_inputs", + "question": "What material will people actually hand to this skill in practice?", + "why": "Real input shape decides whether references, scripts, or examples are needed.", + "list": True, + }, + "primary_output": { + "slot": "primary_output", + "question": "What finished hand-back should this skill return so the next person can keep moving?", + "why": "The output is the anchor for package design and review.", + "list": False, + }, + "exclusions": { + "slot": "exclusions", + "question": "What nearby requests should this skill clearly leave out so the boundary stays clean?", + "why": "Exclusions are the fastest route to better trigger quality.", + "list": True, + }, + "constraints": { + "slot": "constraints", + "question": "What constraints matter most here: privacy, naming, compatibility, portability, governance, or speed?", + "why": "Constraints decide how much structure and validation this skill really needs.", + "list": True, + }, + "standards": { + "slot": "standards", + "question": "What quality bar matters most here: consistency, auditability, tone, or delivery speed?", + "why": "Standards explain how to choose the first evaluation gate.", + "list": True, + }, +} + + +def parse_frontmatter(text: str) -> tuple[dict, str]: + lines = text.splitlines() + if not lines or lines[0].strip() != "---": + return {}, text + try: + end_index = lines[1:].index("---") + 1 + except ValueError: + return {}, text + frontmatter_text = "\n".join(lines[1:end_index]) + body = "\n".join(lines[end_index + 1 :]).lstrip() + if yaml is not None: + payload = yaml.safe_load(frontmatter_text) or {} + return payload if isinstance(payload, dict) else {}, body + data = {} + for line in frontmatter_text.splitlines(): + if ":" not in line: + continue + key, value = line.split(":", 1) + data[key.strip()] = value.strip().strip('"') + return data, body + + +def normalized_list(value: list[str] | str | None) -> list[str]: + if value is None: + return [] + if isinstance(value, str): + parts = [item.strip() for item in value.split(",")] + return [item for item in parts if item] + return [str(item).strip() for item in value if str(item).strip()] + + +def tokenize(text: str) -> list[str]: + return re.findall(r"[A-Za-z][A-Za-z0-9_-]{2,}", text.lower()) + + +def is_generic(text: str) -> bool: + compact = " ".join(text.lower().split()) + if not compact: + return True + if compact in GENERIC_PHRASES: + return True + tokens = tokenize(compact) + if len(tokens) <= 3: + return True + content_tokens = [token for token in tokens if token not in GENERIC_TOKENS] + return len(content_tokens) < 2 + + +def build_context_from_skill(skill_dir: Path) -> dict[str, Any]: + skill_text = (skill_dir / "SKILL.md").read_text(encoding="utf-8") + frontmatter, _ = parse_frontmatter(skill_text) + payload = load_json(skill_dir / "reports" / "intent-context.json") + if payload: + return payload + return { + "job": frontmatter.get("description", ""), + "real_inputs": [], + "primary_output": "", + "description": frontmatter.get("description", ""), + "exclusions": [], + "constraints": [], + "standards": [], + "correction": "", + "user_references": [], + } + + +def load_json(path: Path) -> dict[str, Any]: + if not path.exists(): + return {} + payload = json.loads(path.read_text(encoding="utf-8")) + return payload if isinstance(payload, dict) else {} + + +def assess_intent_confidence(context: dict[str, Any]) -> dict[str, Any]: + job = str(context.get("job", "")).strip() + primary_output = str(context.get("primary_output", "")).strip() + description = str(context.get("description", "")).strip() + real_inputs = normalized_list(context.get("real_inputs")) + exclusions = normalized_list(context.get("exclusions")) + constraints = normalized_list(context.get("constraints")) + standards = normalized_list(context.get("standards")) + user_references = normalized_list(context.get("user_references")) + correction = str(context.get("correction", "")).strip() + + score = 0 + strengths = [] + gaps = [] + + def add_gap(key: str, label: str, reason: str, severity: str = "high") -> None: + gaps.append({"key": key, "label": label, "reason": reason, "severity": severity}) + + if job and not is_generic(job): + score += 25 + strengths.append("The recurring job is concrete enough to anchor the package.") + elif job: + score += 10 + add_gap( + "job_specificity", + "Recurring job is still generic", + "The current job statement sounds more like a packaging goal than a concrete repeated task.", + ) + else: + add_gap("job_specificity", "Recurring job is missing", "The package has no clear job-to-be-done anchor yet.") + + if real_inputs: + score += 15 + strengths.append("Real input shape is explicit.") + else: + add_gap("real_inputs", "Real inputs are missing", "Without real inputs, it is hard to choose assets, scripts, or examples.") + + if primary_output and not is_generic(primary_output): + score += 20 + strengths.append("The hand-back output is concrete.") + elif primary_output: + score += 8 + add_gap( + "primary_output", + "Primary output is still generic", + "The current output does not yet say what a useful finished deliverable looks like.", + ) + else: + add_gap("primary_output", "Primary output is missing", "The package does not yet know what it must hand back.") + + if exclusions: + score += 15 + strengths.append("Boundary exclusions are already explicit.") + else: + add_gap("exclusions", "Near-neighbor exclusions are missing", "The route may blur into nearby requests without an exclusion list.") + + if constraints: + score += 10 + strengths.append("Operational constraints are visible.") + else: + add_gap("constraints", "Constraints are missing", "The package does not yet know which tradeoffs matter most.") + + if standards: + score += 5 + strengths.append("Quality standards are visible.") + else: + add_gap("standards", "Quality bar is implied, not explicit", "The first evaluation target is still underspecified.", "medium") + + if correction: + score += 5 + strengths.append("A correction loop already tightened the first reading.") + + if user_references: + score += 5 + strengths.append("Reference preferences are already available.") + + if description and not is_generic(description): + score += 5 + + score = min(score, 100) + if score >= 85: + band = "high" + elif score >= 70: + band = "medium" + else: + band = "low" + + gate_passed = score >= 70 and not any( + gap["key"] in {"job_specificity", "real_inputs", "primary_output"} and gap["severity"] == "high" + for gap in gaps + ) + follow_up_questions = [ + { + **FOLLOW_UP_LIBRARY[gap["key"]], + "label": gap["label"], + "severity": gap["severity"], + } + for gap in gaps + if gap["key"] in FOLLOW_UP_LIBRARY + ][:3] + + anchor_sentence = " ".join( + item + for item in [ + job or "Unclear recurring job.", + f"Primary output: {primary_output}." if primary_output else "", + f"Exclusions: {', '.join(exclusions)}." if exclusions else "", + ] + if item + ).strip() + + return { + "score": score, + "band": band, + "gate_passed": gate_passed, + "strengths": strengths[:5], + "gaps": gaps, + "follow_up_questions": follow_up_questions, + "anchor_sentence": anchor_sentence, + "recommended_action": ( + "Intent is clear enough to package the first routeable version." + if gate_passed + else "Pause before deep authoring and close the highest-leverage gaps first." + ), + "context": { + "job": job, + "real_inputs": real_inputs, + "primary_output": primary_output, + "description": description, + "exclusions": exclusions, + "constraints": constraints, + "standards": standards, + "correction": correction, + "user_references": user_references, + }, + } + + +def render_markdown(summary: dict[str, Any]) -> str: + lines = [ + "# Intent Confidence", + "", + f"- Confidence score: `{summary['score']}/100`", + f"- Confidence band: `{summary['band']}`", + f"- Gate passed: `{summary['gate_passed']}`", + f"- Recommended action: {summary['recommended_action']}", + "", + "## Current Reading", + "", + summary["anchor_sentence"] or "No clear anchor sentence yet.", + "", + "## Strong Signals", + "", + ] + if summary["strengths"]: + for item in summary["strengths"]: + lines.append(f"- {item}") + else: + lines.append("- No strong signals yet.") + + lines.extend(["", "## Gaps To Close", ""]) + if summary["gaps"]: + for gap in summary["gaps"]: + lines.append(f"- **{gap['label']}** (`{gap['severity']}`): {gap['reason']}") + else: + lines.append("- No major intent gaps detected.") + + lines.extend(["", "## Follow-Up Questions", ""]) + if summary["follow_up_questions"]: + for item in summary["follow_up_questions"]: + lines.append(f"- **{item['question']}**") + lines.append(f" - Why: {item['why']}") + else: + lines.append("- No extra follow-up questions required before the first package.") + return "\n".join(lines).strip() + "\n" + + +def render_intent_confidence( + skill_dir: Path, + context: dict[str, Any] | None = None, + output_md: Path | None = None, + output_json: Path | None = None, + context_json: Path | None = None, +) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + reports_dir = skill_dir / "reports" + reports_dir.mkdir(parents=True, exist_ok=True) + output_md = output_md or reports_dir / "intent-confidence.md" + output_json = output_json or reports_dir / "intent-confidence.json" + context_json = context_json or reports_dir / "intent-context.json" + + context_payload = context or build_context_from_skill(skill_dir) + summary = assess_intent_confidence(context_payload) + output_md.write_text(render_markdown(summary), encoding="utf-8") + output_json.write_text(json.dumps(summary, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + context_json.write_text(json.dumps(summary["context"], ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + return { + "ok": True, + "skill_dir": str(skill_dir), + "artifacts": { + "markdown": str(output_md), + "json": str(output_json), + "context_json": str(context_json), + }, + "summary": summary, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render an intent confidence report for a skill package.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--context-json") + parser.add_argument("--output-md") + parser.add_argument("--output-json") + args = parser.parse_args() + + context = None + if args.context_json: + context = load_json(Path(args.context_json).resolve()) + + result = render_intent_confidence( + Path(args.skill_dir), + context=context, + output_md=Path(args.output_md).resolve() if args.output_md else None, + output_json=Path(args.output_json).resolve() if args.output_json else None, + ) + print(json.dumps(result, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_reference_synthesis.py b/scripts/render_reference_synthesis.py new file mode 100644 index 0000000..40c84fb --- /dev/null +++ b/scripts/render_reference_synthesis.py @@ -0,0 +1,336 @@ +#!/usr/bin/env python3 +import argparse +import json +import re +from pathlib import Path +from typing import Any + +try: + import yaml +except ImportError: # pragma: no cover + yaml = None + + +CURATED_TRACKS = [ + { + "source_type": "official", + "name": "Official skill anatomy and context discipline", + "keywords": ["adapter", "portable", "metadata", "description", "references", "context", "entrypoint"], + "borrow": "Borrow progressive disclosure: keep the entrypoint lean and move depth into references or scripts.", + "avoid": "Do not let packaging or platform concerns swallow the core job boundary.", + }, + { + "source_type": "official", + "name": "Official workflow product ergonomics", + "keywords": ["quickstart", "review", "viewer", "feedback", "operator", "workflow", "guide"], + "borrow": "Borrow a first-time operator flow that explains itself before it asks for more structure.", + "avoid": "Do not mimic product polish that adds UI bulk without improving clarity.", + }, + { + "source_type": "research", + "name": "Hypothesis-test-learn loop", + "keywords": ["test", "benchmark", "baseline", "compare", "holdout", "optimize", "iteration"], + "borrow": "Borrow a small hypothesis-test-learn loop so the first revision is evidence-backed.", + "avoid": "Do not create experimental overhead that exceeds the skill's real risk tier.", + }, + { + "source_type": "research", + "name": "Human-in-the-loop verification", + "keywords": ["review", "audit", "govern", "incident", "compliance", "approval"], + "borrow": "Borrow a review checkpoint wherever trust matters more than raw speed.", + "avoid": "Do not force every skill through heavyweight review when the risk is low.", + }, + { + "source_type": "principles", + "name": "Boundary-first design", + "keywords": ["route", "trigger", "boundary", "exclude", "scope", "near-neighbor"], + "borrow": "Borrow the discipline of defining what the skill should not own before growing the package.", + "avoid": "Do not expand execution assets until route boundaries stay clean.", + }, + { + "source_type": "principles", + "name": "Minimum sufficient structure", + "keywords": ["lightweight", "lean", "minimal", "small", "context", "scaffold", "focus"], + "borrow": "Borrow the smallest structure that makes the skill reliable and explainable.", + "avoid": "Do not add files or gates that raise context cost faster than they raise trust.", + }, + { + "source_type": "principles", + "name": "Outcome-backwards design", + "keywords": ["output", "deliverable", "result", "handoff", "keep moving", "packet", "summary"], + "borrow": "Borrow the habit of designing from the required hand-back output backwards.", + "avoid": "Do not start with architecture terms before the deliverable is concrete.", + }, +] + + +def load_json(path: Path) -> dict[str, Any]: + if not path.exists(): + return {} + payload = json.loads(path.read_text(encoding="utf-8")) + return payload if isinstance(payload, dict) else {} + + +def parse_frontmatter(text: str) -> tuple[dict, str]: + lines = text.splitlines() + if not lines or lines[0].strip() != "---": + return {}, text + try: + end_index = lines[1:].index("---") + 1 + except ValueError: + return {}, text + frontmatter_text = "\n".join(lines[1:end_index]) + body = "\n".join(lines[end_index + 1 :]).lstrip() + if yaml is not None: + payload = yaml.safe_load(frontmatter_text) or {} + return payload if isinstance(payload, dict) else {}, body + data = {} + for line in frontmatter_text.splitlines(): + if ":" not in line: + continue + key, value = line.split(":", 1) + data[key.strip()] = value.strip().strip('"') + return data, body + + +def anchor_text(skill_dir: Path, benchmark: dict[str, Any], intent: dict[str, Any]) -> str: + skill_text = (skill_dir / "SKILL.md").read_text(encoding="utf-8") + frontmatter, _ = parse_frontmatter(skill_text) + pieces = [ + frontmatter.get("name", skill_dir.name), + frontmatter.get("description", ""), + benchmark.get("query", ""), + intent.get("anchor_sentence", ""), + ] + return " ".join(piece for piece in pieces if piece).lower() + + +def match_keywords(text: str, keywords: list[str]) -> list[str]: + hits = [] + for keyword in keywords: + if keyword in text: + hits.append(keyword) + return hits + + +def select_source_tracks(text: str) -> list[dict[str, Any]]: + grouped: dict[str, list[dict[str, Any]]] = {} + for track in CURATED_TRACKS: + matched = match_keywords(text, track["keywords"]) + score = len(matched) + payload = {**track, "matched_keywords": matched, "score": score} + grouped.setdefault(track["source_type"], []).append(payload) + + selected = [] + for source_type in ("official", "research", "principles"): + candidates = sorted(grouped.get(source_type, []), key=lambda item: item["score"], reverse=True) + chosen = candidates[0] if candidates else None + if chosen is None: + continue + if chosen["score"] == 0: + chosen = {**chosen, "matched_keywords": ["general fit"]} + selected.append( + { + "source_type": source_type, + "name": chosen["name"], + "evidence_mode": "curated-pattern-track", + "matched_keywords": chosen["matched_keywords"], + "borrow": chosen["borrow"], + "avoid": chosen["avoid"], + "why_relevant": ( + f"This track matches: {', '.join(chosen['matched_keywords'])}." + if chosen["matched_keywords"] + else "This track is the best general fit for the current skill shape." + ), + } + ) + return selected + + +def unique_items(items: list[str], limit: int) -> list[str]: + seen = set() + output = [] + for item in items: + if item in seen: + continue + seen.add(item) + output.append(item) + if len(output) == limit: + break + return output + + +def build_summary(skill_dir: Path) -> dict[str, Any]: + skill_text = (skill_dir / "SKILL.md").read_text(encoding="utf-8") + frontmatter, _ = parse_frontmatter(skill_text) + benchmark = load_json(skill_dir / "reports" / "github-benchmark-scan.json") + intent_payload = load_json(skill_dir / "reports" / "intent-confidence.json") + reference_scan = load_json(skill_dir / "reports" / "reference-scan.json") + + source_tracks = select_source_tracks(anchor_text(skill_dir, benchmark, intent_payload)) + github_repos = benchmark.get("repositories", [])[:3] + github_borrow = benchmark.get("cross_repo", {}).get("borrow", []) + github_avoid = benchmark.get("cross_repo", {}).get("avoid", []) + track_borrow = [track["borrow"] for track in source_tracks] + track_avoid = [track["avoid"] for track in source_tracks] + user_refs = reference_scan.get("user_references", []) + + borrow_now = unique_items( + [ + *track_borrow, + *github_borrow, + *[ref.get("borrow", "") for ref in user_refs], + ], + 5, + ) + avoid_now = unique_items( + [ + *track_avoid, + *github_avoid, + *[ref.get("avoid", "") for ref in user_refs], + ], + 5, + ) + quality_risers = unique_items( + [ + "Use GitHub repositories for concrete package and workflow patterns.", + "Use curated official or commercial tracks for entrypoint and operator ergonomics.", + "Use research tracks to justify the smallest evaluation loop that still catches regressions.", + "Use principle tracks to keep the package small, boundary-aware, and outcome-driven.", + ], + 4, + ) + + return { + "skill_name": frontmatter.get("name", skill_dir.name), + "description": frontmatter.get("description", "No description found."), + "intent_confidence": { + "score": intent_payload.get("score", 0), + "band": intent_payload.get("band", "low"), + "gate_passed": intent_payload.get("gate_passed", False), + }, + "github_benchmarks": [ + { + "name": repo.get("full_name"), + "url": repo.get("html_url"), + "stars": repo.get("stars"), + "borrow": repo.get("borrow", [])[:2], + } + for repo in github_repos + ], + "source_tracks": source_tracks, + "synthesis": { + "borrow_now": borrow_now, + "avoid_now": avoid_now, + "quality_risers": quality_risers, + "decision_prompt": ( + "I pulled concrete GitHub benchmarks and layered them with curated official, research, and principle tracks. " + "Do you want the next draft to borrow one or two of these patterns now, or keep the first pass lighter?" + ), + "source_mix": { + "github_benchmarks": len(github_repos), + "curated_tracks": len(source_tracks), + "user_references": len(user_refs), + }, + }, + } + + +def render_markdown(summary: dict[str, Any]) -> str: + lines = [ + "# Reference Synthesis", + "", + f"Skill: `{summary['skill_name']}`", + f"- Description: {summary['description']}", + f"- Intent confidence: `{summary['intent_confidence']['score']}/100` (`{summary['intent_confidence']['band']}`)", + "", + "## Live GitHub Benchmarks", + "", + ] + if summary["github_benchmarks"]: + for repo in summary["github_benchmarks"]: + lines.extend( + [ + f"### {repo['name']}", + f"- URL: {repo['url']}", + f"- Stars: `{repo['stars']}`", + ] + ) + for item in repo.get("borrow", []): + lines.append(f"- Borrow: {item}") + lines.append("") + else: + lines.append("- No live GitHub benchmarks are attached yet.") + lines.append("") + + lines.extend(["## Curated World-Class Pattern Tracks", ""]) + for track in summary["source_tracks"]: + lines.extend( + [ + f"### {track['name']}", + f"- Type: `{track['source_type']}`", + f"- Evidence mode: `{track['evidence_mode']}`", + f"- Why relevant: {track['why_relevant']}", + f"- Borrow: {track['borrow']}", + f"- Avoid: {track['avoid']}", + "", + ] + ) + + lines.extend(["## Borrow Now", ""]) + for item in summary["synthesis"]["borrow_now"]: + lines.append(f"- {item}") + + lines.extend(["", "## Avoid Now", ""]) + for item in summary["synthesis"]["avoid_now"]: + lines.append(f"- {item}") + + lines.extend(["", "## Quality Lift Thesis", ""]) + for item in summary["synthesis"]["quality_risers"]: + lines.append(f"- {item}") + + lines.extend(["", "## Decision Prompt", "", summary["synthesis"]["decision_prompt"], ""]) + return "\n".join(lines).strip() + "\n" + + +def render_reference_synthesis( + skill_dir: Path, + output_md: Path | None = None, + output_json: Path | None = None, +) -> dict[str, Any]: + skill_dir = skill_dir.resolve() + reports_dir = skill_dir / "reports" + reports_dir.mkdir(parents=True, exist_ok=True) + output_md = output_md or reports_dir / "reference-synthesis.md" + output_json = output_json or reports_dir / "reference-synthesis.json" + summary = build_summary(skill_dir) + output_md.write_text(render_markdown(summary), encoding="utf-8") + output_json.write_text(json.dumps(summary, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + return { + "ok": True, + "skill_dir": str(skill_dir), + "artifacts": { + "markdown": str(output_md), + "json": str(output_json), + }, + "summary": summary, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Render a multi-source reference synthesis report for a skill package.") + parser.add_argument("skill_dir", nargs="?", default=".") + parser.add_argument("--output-md") + parser.add_argument("--output-json") + args = parser.parse_args() + + result = render_reference_synthesis( + Path(args.skill_dir), + output_md=Path(args.output_md).resolve() if args.output_md else None, + output_json=Path(args.output_json).resolve() if args.output_json else None, + ) + print(json.dumps(result, ensure_ascii=False, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/scripts/render_review_viewer.py b/scripts/render_review_viewer.py index 6ee72a7..4c87bac 100644 --- a/scripts/render_review_viewer.py +++ b/scripts/render_review_viewer.py @@ -5,9 +5,11 @@ import json import re from pathlib import Path +from render_intent_confidence import render_intent_confidence from render_intent_dialogue import render_intent_dialogue from render_iteration_directions import render_iteration_directions from render_reference_scan import render_reference_scan +from render_reference_synthesis import render_reference_synthesis from render_skill_overview import render_skill_overview @@ -49,28 +51,41 @@ def load_benchmark_summary(skill_dir: Path) -> dict: return payload if isinstance(payload, dict) else {} +def load_reference_synthesis_summary(skill_dir: Path) -> dict: + payload = load_json(skill_dir / "reports" / "reference-synthesis.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_confidence_json = skill_dir / "reports" / "intent-confidence.json" intent_json = skill_dir / "reports" / "intent-dialogue.json" reference_json = skill_dir / "reports" / "reference-scan.json" + reference_synthesis_json = skill_dir / "reports" / "reference-synthesis.json" directions_json = skill_dir / "reports" / "iteration-directions.json" overview_payload = load_json(overview_json) if overview_json.exists() else {} + intent_confidence_payload = load_json(intent_confidence_json) if intent_confidence_json.exists() else {} intent_payload = load_json(intent_json) if intent_json.exists() else {} reference_payload = load_json(reference_json) if reference_json.exists() else {} + reference_synthesis_payload = load_json(reference_synthesis_json) if reference_synthesis_json.exists() else {} directions_payload = load_json(directions_json) if directions_json.exists() else {} - overview = overview_payload or render_skill_overview(skill_dir)["summary"] + intent_confidence = intent_confidence_payload or render_intent_confidence(skill_dir)["summary"] intent = intent_payload or render_intent_dialogue(skill_dir)["summary"] reference = reference_payload or render_reference_scan(skill_dir, [])["summary"] + reference_synthesis = reference_synthesis_payload or render_reference_synthesis(skill_dir)["summary"] + overview = overview_payload or render_skill_overview(skill_dir)["summary"] iteration = directions_payload.get("summary", {}) or render_iteration_directions(skill_dir)["summary"] feedback = load_feedback_summary(skill_dir) baseline = load_baseline_summary(skill_dir) compare = load_specific_compare(skill_dir) promotion = load_specific_promotion(skill_dir) benchmark = load_benchmark_summary(skill_dir) + reference_synthesis = load_reference_synthesis_summary(skill_dir) return { "overview": overview, + "intent_confidence": intent_confidence, "intent": intent, "reference": reference, "iteration": directions_payload if directions_payload else {"summary": iteration, "directions": []}, @@ -79,6 +94,7 @@ def ensure_report_inputs(skill_dir: Path) -> dict: "compare": compare, "promotion": promotion, "benchmark": benchmark, + "reference_synthesis": reference_synthesis, } @@ -133,6 +149,19 @@ def benchmark_cards(benchmark: dict) -> list[dict]: return cards +def synthesis_cards(reference_synthesis: dict) -> list[dict]: + cards = [] + for track in reference_synthesis.get("source_tracks", [])[:3]: + cards.append( + { + "name": track.get("name", "Unknown track"), + "borrow": [track.get("borrow", "")] if track.get("borrow") else [], + "avoid": [track.get("avoid", "")] if track.get("avoid") else [], + } + ) + return cards + + def split_sentences(text: str) -> list[str]: if not text: return [] @@ -197,6 +226,7 @@ def variant_diff_cards(compare: dict) -> list[dict]: def render_html(report: dict) -> str: overview = report["overview"] intent = report["intent"] + intent_confidence = report.get("intent_confidence", {}) reference = report["reference"] iteration = report["iteration"] directions = iteration.get("directions", [])[:3] @@ -205,9 +235,11 @@ def render_html(report: dict) -> str: compare = report.get("compare", {}) promotion = report.get("promotion", {}) benchmark = report.get("benchmark", {}) + reference_synthesis = report.get("reference_synthesis", {}) architecture = architecture_steps(overview) compare_table_rows = compare_rows(compare) benchmark_rows = benchmark_cards(benchmark) + synthesis_rows = synthesis_cards(reference_synthesis) variant_cards = variant_diff_cards(compare) strength_items = "".join(f"
No GitHub benchmark scan has been attached to this package yet.
" + synthesis_html = "" + if synthesis_rows: + synthesis_html = "".join( + ( + "Borrow now
" + + ("No borrow cues recorded.
") + + "Avoid
" + + ("No avoid cues recorded.
") + + "No multi-source synthesis has been generated yet.
" + variant_diff_html = "" if variant_cards: variant_diff_html = "".join( @@ -555,6 +604,7 @@ def render_html(report: dict) -> str: archetype: {html.escape(str(overview.get('metadata', {}).get('skill_archetype', 'scaffold')))} format: {html.escape(str(overview.get('metadata', {}).get('canonical_format', 'agent-skills')))} updated: {html.escape(str(overview.get('metadata', {}).get('updated_at', 'n/a')))} + intent confidence: {html.escape(str(intent_confidence.get('score', 'n/a')))} / 100 @@ -616,6 +666,18 @@ def render_html(report: dict) -> str: +{html.escape(reference_synthesis.get('synthesis', {}).get('decision_prompt', 'Run the reference synthesis after the benchmark scan to decide what to borrow next.'))}
+No benchmark scan recorded yet. Run the GitHub benchmark scan first.
These are the strongest cross-source patterns to borrow now after combining GitHub benchmarks with curated official, research, and principle tracks.
+