feat: add intent dialogue and iteration directions

This commit is contained in:
yaojingang
2026-04-08 13:53:35 +08:00
parent 33c2c304c6
commit f19b64237c
18 changed files with 740 additions and 36 deletions
+1
View File
@@ -8,4 +8,5 @@ tests/tmp_cli/
tests/tmp_skill_overview/
tests/tmp_skill_preview/
tests/tmp_reference_scan/
tests/tmp_iteration_directions/
reports/release_snapshots/
+6 -3
View File
@@ -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 reference-scan-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 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 reference-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 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
@@ -35,6 +35,9 @@ skill-overview-check:
reference-scan-check:
$(PYTHON) tests/verify_reference_scan.py
iteration-directions-check:
$(PYTHON) tests/verify_iteration_directions.py
description-drift-history:
$(PYTHON) scripts/render_description_drift_history.py
@@ -83,7 +86,7 @@ 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 reference-scan-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 reference-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
clean:
rm -rf dist tests/tmp tests/tmp_snapshot
rm -rf dist tests/tmp tests/tmp_snapshot tests/tmp_cli tests/tmp_skill_overview tests/tmp_reference_scan tests/tmp_iteration_directions
+10 -3
View File
@@ -19,8 +19,10 @@ It turns rough workflows, transcripts, prompts, notes, and runbooks into reusabl
- a clear trigger surface
- a lean `SKILL.md`
- optional references, scripts, and evals
- a front-loaded intent dialogue before deep authoring
- a controlled benchmark scan before deep authoring
- a generated visual HTML overview for each newly initialized skill
- three high-value next iteration directions after the first package is created
- neutral source metadata plus client-specific adapters
- governance, promotion, and portability checks built into the default flow
@@ -47,18 +49,21 @@ Read it in 10 seconds:
## Quick Start
1. Describe the workflow, prompt set, or repeated task you want to turn into a skill.
2. Run a short reference scan with external benchmarks first, then use local files only for fit and privacy checks.
3. Use `yao-meta-skill` to generate or improve the package in scaffold, production, or library mode.
4. Run `context_sizer.py`, `resource_boundary_check.py`, `governance_check.py`, `trigger_eval.py`, and `cross_packager.py` as needed to validate and export the result.
2. Start with a short intent dialogue so the real job, outputs, exclusions, and constraints are explicit.
3. Run a short reference scan with external benchmarks first, then use local files only for fit and privacy checks.
4. Use `yao-meta-skill` to generate or improve the package in scaffold, production, or library mode.
5. Review the generated `reports/intent-dialogue.md`, `reports/skill-overview.html`, and `reports/iteration-directions.md` before adding more structure.
Or use the unified authoring CLI:
```bash
python3 scripts/yao.py init my-skill --description "Describe what the skill does."
python3 scripts/yao.py intent-dialogue my-skill
python3 scripts/yao.py reference-scan my-skill \
--external-reference "World Class Method::method::Borrow a tight evaluation loop.::Do not copy heavy process." \
--local-constraint "Current Library Naming::structure::Keep naming aligned with the local skill library.::Do not inherit private references."
python3 scripts/yao.py skill-report my-skill
python3 scripts/yao.py iteration-directions my-skill
python3 scripts/yao.py package . --platform generic --output-dir dist
```
@@ -201,9 +206,11 @@ The design logic is simple:
The repository now treats method as a first-class asset instead of scattered guidance.
- [Skill Engineering Method](references/skill-engineering-method.md)
- [Intent Dialogue](references/intent-dialogue.md)
- [Reference Scan Strategy](references/reference-scan.md)
- [Skill Archetypes](references/skill-archetypes.md)
- [Gate Selection](references/gate-selection.md)
- [Iteration Philosophy](references/iteration-philosophy.md)
- [Non-Skill Decision Tree](references/non-skill-decision-tree.md)
- [Regression Cause Taxonomy](references/regression-cause-taxonomy.md)
- [Human Review Template](references/human-review-template.md)
+6 -3
View File
@@ -28,12 +28,13 @@ Mode rules: [Operating Modes](references/operating-modes.md), [QA Ladder](refere
## Compact Workflow
1. Decide whether the request should become a skill, then choose the lightest archetype.
2. Run a short reference scan with external benchmark objects first, then use local files only for fit, privacy, and compatibility calibration.
3. Capture the recurring job, outputs, trigger phrases, and exclusions.
2. Run a short intent dialogue to capture the recurring job, outputs, trigger phrases, exclusions, and constraints.
3. Run a short reference scan with external benchmark objects first, then use local files only for fit, privacy, and compatibility calibration.
4. Write the `description` early, then test route quality before expanding the package.
5. Add only the folders and gates that earn their keep: `trigger_eval.py`, `optimize_description.py`, `judge_blind_eval.py`, `resource_boundary_check.py`, `governance_check.py`, `cross_packager.py`.
6. After the first package exists, surface the top three next iteration directions instead of expanding the skill in every direction at once.
Playbooks: [Skill Engineering Method](references/skill-engineering-method.md), [Reference Scan Strategy](references/reference-scan.md), [Skill Archetypes](references/skill-archetypes.md), [Gate Selection](references/gate-selection.md), [Non-Skill Decision Tree](references/non-skill-decision-tree.md), [Operating Modes](references/operating-modes.md), [Trigger And Eval Playbook](references/eval-playbook.md).
Playbooks: [Skill Engineering Method](references/skill-engineering-method.md), [Intent Dialogue](references/intent-dialogue.md), [Reference Scan Strategy](references/reference-scan.md), [Skill Archetypes](references/skill-archetypes.md), [Gate Selection](references/gate-selection.md), [Iteration Philosophy](references/iteration-philosophy.md), [Non-Skill Decision Tree](references/non-skill-decision-tree.md), [Operating Modes](references/operating-modes.md), [Trigger And Eval Playbook](references/eval-playbook.md).
## Output Contract
@@ -49,8 +50,10 @@ Unless the user asks otherwise, produce:
- [Skill Engineering Method](references/skill-engineering-method.md)
- [Reference Scan Strategy](references/reference-scan.md)
- [Intent Dialogue](references/intent-dialogue.md)
- [Skill Archetypes](references/skill-archetypes.md)
- [Gate Selection](references/gate-selection.md)
- [Iteration Philosophy](references/iteration-philosophy.md)
- [Non-Skill Decision Tree](references/non-skill-decision-tree.md)
- [Operating Modes](references/operating-modes.md)
- [Governance Model](references/governance.md)
+6 -3
View File
@@ -9,8 +9,10 @@ Il transforme des workflows bruts, des transcripts, des prompts, des notes et de
- une surface de déclenchement claire
- un `SKILL.md` léger
- des references, scripts et evals optionnels
- un court dialogue d'intention avant l'authoring approfondi
- un benchmark/reference scan contrôlé avant l'authoring profond
- un rapport HTML minimaliste en fond blanc généré automatiquement pour chaque nouveau skill
- trois directions d'itération à plus forte valeur après la première création
- des métadonnées sources neutres et des adaptateurs spécifiques au client
- des contrôles de gouvernance, de promotion et de portabilité intégrés au flux standard
@@ -37,9 +39,10 @@ Lecture en 10 secondes :
## Quick Start
1. Décrivez le workflow, l'ensemble de prompts ou la tâche répétée que vous voulez transformer en skill.
2. Lancez un court reference scan en privilégiant GitHub et les objets publics de référence ; les fichiers locaux ne servent ensuite qu'à l'ajustement, à la confidentialité et à la compatibilité.
3. Utilisez `yao-meta-skill` pour générer ou améliorer le paquet en mode scaffold, production ou library.
4. Chaque nouveau skill reçoit aussi `reports/skill-overview.html` et `reports/reference-scan.md`, pour comprendre rapidement son architecture, sa logique, son usage et ses références utiles.
2. Commencez par un court dialogue d'intention pour clarifier le vrai job, les sorties, les exclusions et les contraintes.
3. Lancez ensuite un short reference scan en privilégiant GitHub et les objets publics de référence ; les fichiers locaux ne servent ensuite qu'à l'ajustement, à la confidentialité et à la compatibilité.
4. Utilisez `yao-meta-skill` pour générer ou améliorer le paquet en mode scaffold, production ou library.
5. Chaque nouveau skill reçoit aussi `reports/intent-dialogue.md`, `reports/skill-overview.html`, `reports/reference-scan.md` et `reports/iteration-directions.md`.
## Results
+6 -3
View File
@@ -9,8 +9,10 @@
- 明確なトリガー面
- 軽量な `SKILL.md`
- 必要に応じた references、scripts、evals
- 深い authoring の前に行う短い intent dialogue
- 深い authoring の前に、制御された benchmark/reference scan を 1 回行う
- 新しい skill ごとに、白背景の簡潔な HTML overview を自動生成
- 初回作成後に自動で提示される 3 つの高価値な次の iteration direction
- 中立的なソースメタデータとクライアント別アダプタ
- ガバナンス、昇格判定、portability チェックを標準フローに内蔵
@@ -37,9 +39,10 @@ flowchart LR
## Quick Start
1. skill 化したい workflow、prompt 集合、または反復タスクを説明します。
2. まず短い reference scan を行い、GitHub や世界トップ級の公開対象を主参照にし、ローカル資産は適合確認と privacy 調整だけに使います。
3. `yao-meta-skill` を使って scaffold、production、library のいずれかのモードでパッケージを生成または改善します。
4. 新しく作成した skill には `reports/skill-overview.html``reports/reference-scan.md` が付き、構造、ロジック、使い方、参考対象をすぐ確認できます。
2. まず短い intent dialogue で、実際の job、outputs、boundary、constraints を明確にします。
3. 次に短い reference scan を行い、GitHub や世界トップ級の公開対象を主参照にし、ローカル資産は適合確認と privacy 調整だけに使います。
4. `yao-meta-skill` を使って scaffold、production、library のいずれかのモードでパッケージを生成または改善します。
5. 新しく作成した skill には `reports/intent-dialogue.md``reports/skill-overview.html``reports/reference-scan.md``reports/iteration-directions.md` が付きます。
## Results
+6 -3
View File
@@ -9,8 +9,10 @@
- понятной поверхностью срабатывания
- компактным `SKILL.md`
- необязательными references, scripts и evals
- коротким intent dialogue перед глубокой разработкой
- контролируемым benchmark/reference scan до глубокого authoring
- автоматически создаваемым минималистичным HTML-обзором на белом фоне для каждого нового skill
- тремя наиболее ценными направлениями следующей итерации после первого создания
- нейтральными исходными метаданными и клиентскими адаптерами
- встроенными проверками governance, promotion и portability в стандартном потоке
@@ -37,9 +39,10 @@ flowchart LR
## Quick Start
1. Опишите workflow, набор prompts или повторяющуюся задачу, которую хотите превратить в skill.
2. Сначала выполните короткий reference scan, где основными источниками будут GitHub и сильные публичные эталоны, а локальные файлы будут использоваться только для адаптации, приватности и совместимости.
3. Используйте `yao-meta-skill`, чтобы сгенерировать или улучшить пакет в режиме scaffold, production или library.
4. Каждый новый skill также получает `reports/skill-overview.html` и `reports/reference-scan.md`, чтобы быстро понять архитектуру, логику, способ использования и полезные ориентиры.
2. Сначала проведите короткий intent dialogue, чтобы уточнить реальную job-to-be-done, outputs, exclusions и constraints.
3. Затем выполните короткий reference scan, где основными источниками будут GitHub и сильные публичные эталоны, а локальные файлы будут использоваться только для адаптации, приватности и совместимости.
4. Используйте `yao-meta-skill`, чтобы сгенерировать или улучшить пакет в режиме scaffold, production или library.
5. Каждый новый skill также получает `reports/intent-dialogue.md`, `reports/skill-overview.html`, `reports/reference-scan.md` и `reports/iteration-directions.md`.
## Results
+6 -3
View File
@@ -9,8 +9,10 @@
- 清晰的触发面
- 精简的 `SKILL.md`
- 可选的 references、scripts 和 evals
- 深度起草前先做一轮意图对话,补齐真实任务、输出物和边界
- 深度起草前先做一轮受控的 benchmark/reference scan
- 新建 skill 时自动生成一份极简白底 HTML 可视化说明
- 首次建包后会自动给出 3 个最有价值的下一步迭代方向
- 中性的源元数据以及面向不同客户端的适配层
- 内建的治理、晋升和 portability 检查
@@ -37,9 +39,10 @@ flowchart LR
## Quick Start
1. 先描述你想沉淀成 skill 的 workflow、prompt 集合或重复任务。
2. 先做一轮简短的参考扫描,以 GitHub 和世界级公开对象为主来源,本地文件只做适配和隐私校准
3. 使用 `yao-meta-skill` 以 scaffold、production 或 library 模式生成或改进 skill 包
4. 新建 skill 后,会默认附带 `reports/skill-overview.html``reports/reference-scan.md`,方便理解架构、逻辑、使用方法和可借鉴对象
2. 先做一轮简短的意图对话,把真实任务、输出物、边界和约束说清楚
3. 再做一轮参考扫描,以 GitHub 和世界级公开对象为主来源,本地文件只做适配和隐私校准
4. 使用 `yao-meta-skill` 以 scaffold、production 或 library 模式生成或改进 skill 包
5. 新建 skill 后,会默认附带 `reports/intent-dialogue.md``reports/skill-overview.html``reports/reference-scan.md``reports/iteration-directions.md`,方便理解架构、逻辑、使用方法、可借鉴对象以及下一步最值的 3 个迭代方向。
## Results
+48
View File
@@ -0,0 +1,48 @@
# Intent Dialogue
Use a short intent dialogue before deep authoring so the first version of the skill is anchored in the real job rather than in a guessed prompt shape.
## Why This Step Exists
- raw workflow material is often incomplete, mixed, or ambiguous
- the wrong boundary chosen early is expensive to repair later
- good trigger design depends on knowing what should not route here
- execution assets should follow confirmed outputs, not assumptions
## What To Capture
Ask only the questions that change the package design.
1. What recurring job should this skill own?
2. What inputs will people actually hand to it?
3. What outputs should it produce every time?
4. What near-neighbor requests should stay out of scope?
5. What quality bar matters most: speed, consistency, auditability, portability, or governance?
6. What assets already exist: docs, scripts, templates, examples, or prior prompts?
7. What constraints matter: privacy, naming, local library fit, or target environments?
## Interview Rule
- prefer `5-7` sharp questions over a long discovery questionnaire
- ask boundary questions early
- ask output questions before architecture questions
- stop once the skill can be described clearly in one sentence
## Output
The dialogue should produce:
- one clear capability sentence
- a list of real inputs
- a list of required outputs
- a short exclusion list
- one recommended archetype
- one recommended first evaluation target
## Failure Pattern
Do not continue into full authoring when the dialogue still leaves these unresolved:
- whether the request is really reusable
- which near-neighbor requests should not trigger
- what concrete deliverable the skill must return
+30
View File
@@ -0,0 +1,30 @@
# Iteration Philosophy
The first skill package is a routeable baseline, not the final answer.
## Principle
Improve the skill by the smallest change that increases reliability more than it increases context cost.
## What Good Iteration Looks Like
- tighten boundary before adding prose
- improve description before enlarging the file tree
- add references only when they remove ambiguity
- add scripts only when deterministic logic repeats
- add gates only when risk justifies maintenance cost
- prefer one strong next step over five vague upgrades
## Default Priority Order
1. trigger clarity and exclusions
2. execution assets that remove repeated manual work
3. promotion, governance, and portability only when reuse justifies them
## First-Version Rule
After the initial package is created, always surface the three highest-value next iteration directions. This keeps the author focused on the best improvement path instead of expanding the skill in every direction at once.
## Anti-Pattern
Do not treat iteration as "make the package bigger." A larger package is only better when routing, execution, or governance becomes materially more reliable.
+36 -11
View File
@@ -5,11 +5,13 @@ This doctrine defines the default method for turning messy workflow material int
## Core Loop
1. Decide whether the request should become a skill at all.
2. Choose the smallest viable archetype.
3. Set one clear capability boundary.
4. Write and test the trigger description before expanding the body.
5. Add only the gates that match the risk.
6. Package and govern the skill only as far as real reuse demands.
2. Run a short intent dialogue to capture the real job, outputs, exclusions, and constraints.
3. Choose the smallest viable archetype.
4. Set one clear capability boundary.
5. Write and test the trigger description before expanding the body.
6. Add only the gates that match the risk.
7. Ship the first routeable package, then pick the three highest-value next iteration directions.
8. Package and govern the skill only as far as real reuse demands.
## Phase 1: Qualification
@@ -31,7 +33,19 @@ Reject skill creation when the request is only:
See [Non-Skill Decision Tree](non-skill-decision-tree.md).
## Phase 2: Archetype Selection
## Phase 2: Intent Dialogue
Before deep authoring, ask only the questions that change the package design.
- what recurring job should the skill own
- what real inputs will users hand to it
- what outputs must it produce
- what near-neighbor requests should stay out of scope
- what constraints matter: privacy, naming, portability, governance, or local fit
See [Intent Dialogue](intent-dialogue.md).
## Phase 3: Archetype Selection
Choose the lightest archetype that fits the job.
@@ -42,7 +56,7 @@ Choose the lightest archetype that fits the job.
See [Skill Archetypes](skill-archetypes.md).
## Phase 3: Boundary Design
## Phase 4: Boundary Design
Every skill should answer four questions clearly:
@@ -53,7 +67,7 @@ Every skill should answer four questions clearly:
Boundary work comes before polishing prose.
## Phase 4: Reference Scan
## Phase 5: Reference Scan
Run a short benchmark pass before deep authoring.
@@ -66,7 +80,7 @@ Run a short benchmark pass before deep authoring.
See [Reference Scan Strategy](reference-scan.md).
## Phase 5: Trigger-First Authoring
## Phase 6: Trigger-First Authoring
Author the frontmatter `description` before expanding the body.
@@ -84,7 +98,7 @@ Trigger quality is improved through:
- adversarial holdout
- route confusion
## Phase 6: Gate Selection
## Phase 7: Gate Selection
Add gates by risk, not by habit.
@@ -95,7 +109,18 @@ Add gates by risk, not by habit.
See [Gate Selection](gate-selection.md).
## Phase 7: Promotion
## Phase 8: First Iteration Philosophy
The first package is a routeable baseline, not the final answer.
- improve trigger and exclusions before growing prose
- add one execution asset before adding many documents
- surface the three highest-value next moves so authors do not expand in every direction at once
- prefer the smallest step that increases reliability more than context cost
See [Iteration Philosophy](iteration-philosophy.md).
## Phase 9: Promotion
A candidate route or package is promotable only when:
+18 -4
View File
@@ -4,6 +4,8 @@ import json
from datetime import date
from pathlib import Path
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_skill_overview import render_skill_overview
@@ -34,15 +36,19 @@ README_TEMPLATE = """# {title}
## How To Use
1. Load the skill through `SKILL.md`.
2. Follow the workflow steps in `SKILL.md`.
3. Check `reports/skill-overview.html` if you want a fast visual explanation of the package.
2. Start with `reports/intent-dialogue.md` to tighten the real job, outputs, and exclusions.
3. Follow the workflow steps in `SKILL.md`.
4. Check `reports/skill-overview.html` if you want a fast visual explanation of the package.
5. Review `reports/iteration-directions.md` for the three most valuable next moves.
## Package Map
- `SKILL.md`: trigger and workflow entrypoint
- `agents/interface.yaml`: portable interface metadata
- `manifest.json`: lifecycle and packaging metadata
- `reports/intent-dialogue.md`: front-loaded discovery questions for better boundary design
- `reports/skill-overview.html`: visual overview report
- `reports/iteration-directions.md`: the top three next iteration directions
"""
@@ -131,7 +137,9 @@ def main() -> None:
encoding="utf-8",
)
overview = render_skill_overview(root)
intent_dialogue = render_intent_dialogue(root)
reference_scan = render_reference_scan(root, [])
iteration_directions = render_iteration_directions(root)
print(
json.dumps(
{
@@ -140,8 +148,14 @@ def main() -> None:
"artifacts": {
"readme": str(root / "README.md"),
"manifest": str(root / "manifest.json"),
**overview["artifacts"],
**reference_scan["artifacts"],
"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"],
"iteration_directions_md": iteration_directions["artifacts"]["markdown"],
"iteration_directions_json": iteration_directions["artifacts"]["json"],
},
},
ensure_ascii=False,
+206
View File
@@ -0,0 +1,206 @@
#!/usr/bin/env python3
import argparse
import json
from pathlib import Path
try:
import yaml
except ImportError: # pragma: no cover
yaml = None
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 = "\n".join(lines[1:end_index])
body = "\n".join(lines[end_index + 1 :]).lstrip()
if yaml is not None:
payload = yaml.safe_load(frontmatter) or {}
return payload if isinstance(payload, dict) else {}, body
data = {}
for line in frontmatter.splitlines():
if ":" not in line:
continue
key, value = line.split(":", 1)
data[key.strip()] = value.strip().strip('"')
return data, body
def extract_title(body: str, fallback: str) -> str:
for line in body.splitlines():
if line.startswith("# "):
return line[2:].strip()
return fallback
def classify_focus(description: str) -> str:
lowered = description.lower()
if any(token in lowered for token in ["review", "audit", "incident", "risk", "govern"]):
return "quality-and-boundary"
if any(token in lowered for token in ["export", "package", "adapter", "client", "portable"]):
return "portability-and-contract"
if any(token in lowered for token in ["workflow", "coordinate", "orchestrate", "process"]):
return "execution-and-assets"
return "trigger-and-output"
def build_questions(focus: str) -> list[dict]:
base = [
{
"question": "What recurring job should this skill own every time?",
"why": "This defines the core capability sentence and keeps the package from drifting.",
},
{
"question": "What inputs will users actually hand to this skill?",
"why": "Input shape decides whether references, scripts, or templates are needed.",
},
{
"question": "What concrete outputs must the skill produce?",
"why": "Outputs should drive the package structure before extra guidance is added.",
},
{
"question": "Which near-neighbor requests should not trigger this skill?",
"why": "The exclusion list is the fastest route to better trigger quality.",
},
{
"question": "What constraints matter: privacy, naming, local fit, portability, or governance?",
"why": "Constraints decide how much structure, packaging, and review the skill actually needs.",
},
]
if focus == "quality-and-boundary":
base.append(
{
"question": "What failure would make this skill untrustworthy in practice?",
"why": "The answer usually reveals the first evaluation gate worth adding.",
}
)
elif focus == "portability-and-contract":
base.append(
{
"question": "Which environments or clients must be able to consume this skill?",
"why": "This sets the minimum metadata and degradation strategy.",
}
)
else:
base.append(
{
"question": "What repeated manual step should become a deterministic asset first?",
"why": "This usually reveals whether a script or reference should be created next.",
}
)
return base
def build_summary(skill_dir: Path) -> dict:
skill_text = (skill_dir / "SKILL.md").read_text(encoding="utf-8")
frontmatter, body = parse_frontmatter(skill_text)
name = frontmatter.get("name", skill_dir.name)
description = frontmatter.get("description", "No description found.")
title = extract_title(body, name.replace("-", " ").title())
focus = classify_focus(description)
questions = build_questions(focus)
output = {
"capability_sentence": f"{title} should turn a recurring request into a reliable reusable output without widening the boundary unnecessarily.",
"required_capture": [
"recurring job",
"real inputs",
"required outputs",
"exclusions",
"constraints",
"first evaluation target",
],
"recommended_first_gate": "trigger and boundary" if focus != "portability-and-contract" else "portability and contract",
}
return {
"skill_name": name,
"title": title,
"description": description,
"focus": focus,
"questions": questions,
"output": output,
}
def render_markdown(summary: dict) -> str:
lines = [
"# Intent Dialogue",
"",
f"Skill: `{summary['skill_name']}`",
"",
"## 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']}`",
"",
"## Questions To Ask",
"",
]
for idx, item in enumerate(summary["questions"], start=1):
lines.extend(
[
f"{idx}. {item['question']}",
f" Why: {item['why']}",
]
)
lines.extend(
[
"",
"## Capture Before Drafting",
"",
f"- Capability sentence: {summary['output']['capability_sentence']}",
f"- Recommended first gate: `{summary['output']['recommended_first_gate']}`",
]
)
for item in summary["output"]["required_capture"]:
lines.append(f"- Capture: `{item}`")
return "\n".join(lines).strip() + "\n"
def render_intent_dialogue(skill_dir: Path, output_md: Path | None = None, output_json: Path | None = None) -> dict:
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-dialogue.md"
output_json = output_json or reports_dir / "intent-dialogue.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), 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 an intent dialogue guide 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_intent_dialogue(
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()
+246
View File
@@ -0,0 +1,246 @@
#!/usr/bin/env python3
import argparse
import json
from pathlib import Path
try:
import yaml
except ImportError: # pragma: no cover
yaml = None
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 = "\n".join(lines[1:end_index])
body = "\n".join(lines[end_index + 1 :]).lstrip()
if yaml is not None:
payload = yaml.safe_load(frontmatter) or {}
return payload if isinstance(payload, dict) else {}, body
data = {}
for line in frontmatter.splitlines():
if ":" not in line:
continue
key, value = line.split(":", 1)
data[key.strip()] = value.strip().strip('"')
return data, body
def load_manifest(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"
sections[current] = []
for line in body.splitlines():
if line.startswith("## "):
current = line[3:].strip()
sections[current] = []
continue
sections[current].append(line)
return {name: "\n".join(lines).strip() for name, lines in sections.items()}
def section_text(sections: dict[str, str], *names: str) -> str:
lowered = {key.lower(): value for key, value in sections.items()}
for name in names:
if name.lower() in lowered:
return lowered[name.lower()]
return ""
def has_any_files(path: Path) -> bool:
return path.exists() and any(path.iterdir())
def direction(priority: int, title: str, why: str, actions: list[str], unlocks: str) -> dict:
return {
"priority": priority,
"title": title,
"why": why,
"actions": actions,
"unlocks": unlocks,
}
def build_directions(skill_dir: Path) -> tuple[dict, list[dict]]:
skill_text = (skill_dir / "SKILL.md").read_text(encoding="utf-8")
frontmatter, body = parse_frontmatter(skill_text)
sections = extract_sections(body)
manifest = load_manifest(skill_dir / "manifest.json")
description = frontmatter.get("description", "")
maturity = manifest.get("maturity_tier", "scaffold")
candidates: list[dict] = []
if "Do not use" not in body and "exclusion" not in body.lower():
candidates.append(
direction(
1,
"Tighten trigger and exclusions",
"The package needs clearer near-neighbor exclusions before it grows.",
[
"Add 3 to 5 should-trigger and should-not-trigger examples.",
"Refine the frontmatter description to name the recurring job and non-goals.",
"Run a first trigger evaluation pass before expanding the package.",
],
"Cleaner routing and fewer accidental activations.",
)
)
if not has_any_files(skill_dir / "references") or not has_any_files(skill_dir / "scripts"):
candidates.append(
direction(
2,
"Add the first execution asset",
"The package is still mostly prose. Add one asset that removes repeated manual work.",
[
"Move stable procedural guidance into references if users will need it repeatedly.",
"Create one deterministic helper script if a repeated step can be executed instead of described.",
"Keep the main SKILL.md compact and route-oriented.",
],
"Stronger execution quality without bloating the entrypoint.",
)
)
if maturity == "scaffold":
candidates.append(
direction(
3,
"Promote from scaffold to production-ready",
"The first version exists; the next gain usually comes from adding the smallest useful gates.",
[
"Decide whether this skill is personal, team-reused, or library-grade.",
"Add only the gates that match that risk level.",
"Record lifecycle metadata and review cadence once reuse becomes real.",
],
"A clearer path from exploratory package to maintained asset.",
)
)
workflow_section = section_text(sections, "Workflow", "Compact Workflow", "How To Use")
if workflow_section and len(workflow_section) < 120:
candidates.append(
direction(
4,
"Deepen operator guidance",
"The workflow is still thin and may not be repeatable by another operator.",
[
"Split the job into distinct phases or checkpoints.",
"Add success conditions and failure boundaries for each phase.",
"Link outputs to the step that produces them.",
],
"Better repeatability and easier human review.",
)
)
if "portable" in description.lower() or "package" in description.lower():
candidates.append(
direction(
5,
"Harden portability semantics",
"The skill already signals reuse across environments, so contract clarity matters early.",
[
"Confirm activation mode, execution context, and trust assumptions.",
"Add or review degradation strategy for non-native targets.",
"Package the skill once to verify adapter expectations.",
],
"Safer cross-environment reuse with less target drift.",
)
)
if len(candidates) < 3:
candidates.append(
direction(
len(candidates) + 10,
"Create an iteration evidence loop",
"The package should show what changed and why after the first draft.",
[
"Generate a visual overview and keep it aligned with the package.",
"Record reference scan choices and non-goals.",
"Capture the next iteration choice explicitly before adding more files.",
],
"A clearer path for the next author or reviewer.",
)
)
chosen = sorted(candidates, key=lambda item: item["priority"])[:3]
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.",
}
return summary, chosen
def render_markdown(summary: dict, directions: list[dict]) -> str:
lines = [
"# Iteration Directions",
"",
f"Skill: `{summary['skill_name']}`",
"",
f"- Maturity tier: `{summary['maturity_tier']}`",
f"- Selection rule: {summary['selection_rule']}",
"",
"## Top 3 Next Moves",
"",
]
for item in directions:
lines.extend(
[
f"### {item['priority']}. {item['title']}",
"",
f"- Why now: {item['why']}",
"- Recommended actions:",
]
)
for action in item["actions"]:
lines.append(f" - {action}")
lines.append(f"- Unlocks: {item['unlocks']}")
lines.append("")
return "\n".join(lines).strip() + "\n"
def render_iteration_directions(skill_dir: Path, output_md: Path | None = None, output_json: Path | None = None) -> dict:
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 / "iteration-directions.md"
output_json = output_json or reports_dir / "iteration-directions.json"
summary, directions = build_directions(skill_dir)
payload = {"summary": summary, "directions": directions}
output_md.write_text(render_markdown(summary, directions), encoding="utf-8")
output_json.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
return {
"ok": True,
"skill_dir": str(skill_dir),
"artifacts": {
"markdown": str(output_md),
"json": str(output_json),
},
**payload,
}
def main() -> None:
parser = argparse.ArgumentParser(description="Render the top three next iteration directions 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_iteration_directions(
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()
+42
View File
@@ -243,6 +243,30 @@ def command_reference_scan(args: argparse.Namespace) -> int:
return 0 if result["ok"] else 2
def command_intent_dialogue(args: argparse.Namespace) -> int:
skill_dir = str(Path(args.skill_dir).resolve())
cmd = [skill_dir]
if args.output_md:
cmd.extend(["--output-md", args.output_md])
if args.output_json:
cmd.extend(["--output-json", args.output_json])
result = run_script("render_intent_dialogue.py", cmd)
print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2))
return 0 if result["ok"] else 2
def command_iteration_directions(args: argparse.Namespace) -> int:
skill_dir = str(Path(args.skill_dir).resolve())
cmd = [skill_dir]
if args.output_md:
cmd.extend(["--output-md", args.output_md])
if args.output_json:
cmd.extend(["--output-json", args.output_json])
result = run_script("render_iteration_directions.py", cmd)
print(json.dumps(result["payload"] if result["payload"] is not None else result, ensure_ascii=False, indent=2))
return 0 if result["ok"] else 2
def command_review(args: argparse.Namespace) -> int:
target_name = resolve_promotion_target(args.target)
bundle_dir = ROOT / "reports" / "iteration_bundles" / target_name
@@ -464,6 +488,24 @@ def build_parser() -> argparse.ArgumentParser:
reference_scan_cmd.add_argument("--output-json")
reference_scan_cmd.set_defaults(func=command_reference_scan)
intent_dialogue_cmd = subparsers.add_parser(
"intent-dialogue",
help="Render a front-loaded intent dialogue guide for a skill package.",
)
intent_dialogue_cmd.add_argument("skill_dir", nargs="?", default=".")
intent_dialogue_cmd.add_argument("--output-md")
intent_dialogue_cmd.add_argument("--output-json")
intent_dialogue_cmd.set_defaults(func=command_intent_dialogue)
iteration_directions_cmd = subparsers.add_parser(
"iteration-directions",
help="Render the top three next iteration directions for a skill package.",
)
iteration_directions_cmd.add_argument("skill_dir", nargs="?", default=".")
iteration_directions_cmd.add_argument("--output-md")
iteration_directions_cmd.add_argument("--output-json")
iteration_directions_cmd.set_defaults(func=command_iteration_directions)
package_cmd = subparsers.add_parser("package", help="Export compatibility artifacts for selected targets.")
package_cmd.add_argument("skill_dir", nargs="?", default=".")
package_cmd.add_argument("--platform", action="append")
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env python3
import json
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
CLI = ROOT / "scripts" / "yao.py"
def run(*args: str) -> dict:
proc = subprocess.run(
[sys.executable, str(CLI), *args],
cwd=ROOT,
capture_output=True,
text=True,
check=True,
)
return json.loads(proc.stdout)
def main() -> None:
tmp_root = ROOT / "tests" / "tmp_iteration_directions"
if tmp_root.exists():
subprocess.run(["rm", "-rf", str(tmp_root)], check=True)
tmp_root.mkdir(parents=True, exist_ok=True)
init_payload = run(
"init",
"iteration-demo-skill",
"--description",
"Turn a repeated release checklist into a reusable reusable skill package.",
"--output-dir",
str(tmp_root),
)
created = Path(init_payload["root"])
report = run("iteration-directions", str(created))
assert report["summary"]["selection_rule"], report
assert len(report["directions"]) == 3, report
titles = {item["title"] for item in report["directions"]}
assert "Tighten trigger and exclusions" in titles or "Add the first execution asset" in titles, titles
print(json.dumps({"ok": True}, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()
+10
View File
@@ -44,8 +44,12 @@ def main() -> None:
created = tmp_root / "skill-overview-demo"
assert (created / "README.md").exists(), created
assert (created / "manifest.json").exists(), created
assert (created / "reports" / "intent-dialogue.md").exists(), created
assert (created / "reports" / "intent-dialogue.json").exists(), created
assert (created / "reports" / "skill-overview.html").exists(), created
assert (created / "reports" / "skill-overview.json").exists(), created
assert (created / "reports" / "iteration-directions.md").exists(), created
assert (created / "reports" / "iteration-directions.json").exists(), created
rerender_result = run("skill-report", str(created))
assert rerender_result["ok"], rerender_result
@@ -56,6 +60,12 @@ def main() -> None:
assert "Architecture" in report_html, report_html[:400]
assert "Why It Works" in report_html, report_html[:600]
intent_text = (created / "reports" / "intent-dialogue.md").read_text(encoding="utf-8")
assert "Questions To Ask" in intent_text, intent_text[:400]
directions_text = (created / "reports" / "iteration-directions.md").read_text(encoding="utf-8")
assert "Top 3 Next Moves" in directions_text, directions_text[:400]
print(json.dumps({"ok": True}, ensure_ascii=False, indent=2))
+10
View File
@@ -36,8 +36,10 @@ def main() -> None:
created = Path(init_result["payload"]["root"])
assert (created / "SKILL.md").exists(), created
assert (created / "README.md").exists(), created
assert (created / "reports" / "intent-dialogue.md").exists(), created
assert (created / "reports" / "skill-overview.html").exists(), created
assert (created / "reports" / "reference-scan.md").exists(), created
assert (created / "reports" / "iteration-directions.md").exists(), created
validate_result = run("validate", str(created))
assert validate_result["ok"], validate_result
@@ -58,6 +60,14 @@ def main() -> None:
assert reference_scan_result["ok"], reference_scan_result
assert reference_scan_result["payload"]["artifacts"]["markdown"].endswith("reports/reference-scan.md"), reference_scan_result
intent_result = run("intent-dialogue", str(created))
assert intent_result["ok"], intent_result
assert intent_result["payload"]["artifacts"]["markdown"].endswith("reports/intent-dialogue.md"), intent_result
directions_result = run("iteration-directions", str(created))
assert directions_result["ok"], directions_result
assert directions_result["payload"]["artifacts"]["markdown"].endswith("reports/iteration-directions.md"), directions_result
optimize_result = run("optimize-description", "--target", "root")
assert optimize_result["ok"], optimize_result
assert optimize_result["payload"]["winner"]["label"] == "Current", optimize_result