dc40d4c0db
- Reader: discover workflow files in nested dirs (agents/, workflows/, examples/) - Reader: load source code, config, deps — not just docs - Extractor: prompt demands concrete implementation details (files, deps, code) - Scorer: removed general_purpose check (5/6 checks, score 1.0) - Generator: includes Setup, Key Files, Implementation Details sections - Reviewer: replaced LLM review with 8 deterministic structural checks - Publisher: handle 409 duplicate PR gracefully as success - 5 skills published as PRs #6-#10 on Gitea
151 lines
5.2 KiB
Python
151 lines
5.2 KiB
Python
"""Stage 6: Skill Generator — Transform workflow into standardized Hermes Skill."""
|
|
import yaml
|
|
import json
|
|
|
|
def generate_skill(score_result, config):
|
|
"""
|
|
Generate a standardized Hermes Skill package from an approved workflow.
|
|
"""
|
|
if score_result.get("decision") != "PASS":
|
|
return {
|
|
"status": "BLOCKED",
|
|
"reason": "Score check failed",
|
|
}
|
|
|
|
workflow = score_result.get("workflow", {})
|
|
skill_name = workflow.get("skill_name", "unknown").lower().replace(" ", "-").replace("_", "-")
|
|
repo = score_result.get("repository", "")
|
|
|
|
# Generate SKILL.md (frontmatter + markdown body)
|
|
frontmatter = {
|
|
"name": skill_name,
|
|
"version": "1.0.0",
|
|
"description": workflow.get("goal", ""),
|
|
"inputs": workflow.get("inputs", []),
|
|
"steps": workflow.get("steps", []),
|
|
"outputs": workflow.get("outputs", []),
|
|
"tags": [],
|
|
"metadata": {
|
|
"source_repo": repo,
|
|
"extracted_at": "",
|
|
"confidence": workflow.get("confidence", 0),
|
|
},
|
|
}
|
|
|
|
# Build SKILL.md with implementation details
|
|
impl = workflow.get("implementation_details", {})
|
|
framework = impl.get("framework", "")
|
|
dependencies = impl.get("dependencies", [])
|
|
key_files = impl.get("key_files", [])
|
|
code_snippets = impl.get("code_snippets", [])
|
|
setup_steps = impl.get("setup_steps", [])
|
|
|
|
skill_md = "---\n"
|
|
skill_md += yaml.dump(frontmatter, default_flow_style=False, sort_keys=False)
|
|
skill_md += "---\n\n"
|
|
skill_md += f"# {skill_name}\n\n"
|
|
skill_md += f"{workflow.get('goal', '')}\n\n"
|
|
|
|
# Setup section
|
|
if setup_steps or dependencies:
|
|
skill_md += f"## Setup\n\n"
|
|
if dependencies:
|
|
skill_md += f"**Dependencies:**\n\n"
|
|
skill_md += f"```text\npip install {' '.join(dependencies)}\n```\n\n"
|
|
if setup_steps:
|
|
skill_md += f"**Setup steps:**\n\n"
|
|
for s in setup_steps:
|
|
skill_md += f"1. {s}\n"
|
|
skill_md += "\n"
|
|
|
|
# Key files
|
|
if key_files:
|
|
skill_md += f"## Key Files\n\n"
|
|
for kf in key_files:
|
|
skill_md += f"- `{kf}`\n"
|
|
skill_md += "\n"
|
|
|
|
# Steps with implementation details
|
|
skill_md += f"## Steps\n\n"
|
|
for i, step in enumerate(workflow.get("steps", []), 1):
|
|
skill_md += f"{i}. {step}\n"
|
|
skill_md += "\n"
|
|
|
|
# Code examples
|
|
if code_snippets:
|
|
skill_md += f"## Implementation Details\n\n"
|
|
for snippet in code_snippets:
|
|
skill_md += f"```python\n{snippet}\n```\n\n"
|
|
|
|
# Inputs/Outputs
|
|
skill_md += f"## Inputs\n\n"
|
|
for inp in workflow.get("inputs", []):
|
|
skill_md += f"- {inp}\n"
|
|
skill_md += f"\n## Outputs\n\n"
|
|
for out in workflow.get("outputs", []):
|
|
skill_md += f"- {out}\n"
|
|
|
|
# Failure Modes
|
|
skill_md += f"\n## Failure Modes\n\n"
|
|
for fm in workflow.get("failure_modes", []):
|
|
skill_md += f"- {fm}\n"
|
|
|
|
# Source
|
|
skill_md += f"\n## Source\n\n"
|
|
skill_md += f"Extracted from: [{repo}]({repo})\n"
|
|
skill_md += f"Confidence: {workflow.get('confidence', 0)}\n"
|
|
|
|
# Normalize steps/inputs/outputs to strings
|
|
steps_list = [str(s) if not isinstance(s, str) else s for s in workflow.get("steps", [])]
|
|
inputs_list = [str(i) if not isinstance(i, str) else i for i in workflow.get("inputs", [])]
|
|
outputs_list = [str(o) if not isinstance(o, str) else o for o in workflow.get("outputs", [])]
|
|
|
|
# Generate examples.md
|
|
examples_md = f"# Examples: {skill_name}\n\n"
|
|
examples_md += f"## Usage Example\n\n"
|
|
examples_md += f"```python\n# How to use this skill\n# Inputs: {', '.join(inputs_list)}\n# Process: {' → '.join(steps_list[:3])}\n# Outputs: {', '.join(outputs_list)}\n```\n"
|
|
|
|
# Generate commands.md
|
|
commands_md = f"# Commands: {skill_name}\n\n"
|
|
commands_md += f"## Available Commands\n\n"
|
|
commands_md += f"- `/skill {skill_name}` — Load this skill\n"
|
|
commands_md += f"- `/run {skill_name}` — Execute workflow\n"
|
|
|
|
# Generate metadata.json
|
|
metadata = {
|
|
"name": skill_name,
|
|
"version": "1.0.0",
|
|
"goal": workflow.get("goal", ""),
|
|
"inputs": workflow.get("inputs", []),
|
|
"steps": workflow.get("steps", []),
|
|
"outputs": workflow.get("outputs", []),
|
|
"failure_modes": workflow.get("failure_modes", []),
|
|
"confidence": workflow.get("confidence", 0),
|
|
"explanation": workflow.get("explanation", ""),
|
|
"source_repo": repo,
|
|
"score": score_result.get("score", 0),
|
|
}
|
|
|
|
# Generate tests.md
|
|
tests_md = f"# Tests: {skill_name}\n\n"
|
|
tests_md += f"## Test Checklist\n\n"
|
|
tests_md += f"- [ ] Workflow has at least 3 steps\n"
|
|
tests_md += f"- [ ] All inputs are defined\n"
|
|
tests_md += f"- [ ] All outputs are defined\n"
|
|
tests_md += f"- [ ] Failure modes are documented\n"
|
|
tests_md += f"- [ ] Skill can be loaded without errors\n"
|
|
|
|
return {
|
|
"status": "GENERATED",
|
|
"skill_name": skill_name,
|
|
"repository": repo,
|
|
"files": {
|
|
"SKILL.md": skill_md,
|
|
"examples.md": examples_md,
|
|
"commands.md": commands_md,
|
|
"metadata.json": json.dumps(metadata, indent=2),
|
|
"tests.md": tests_md,
|
|
},
|
|
"metadata": metadata,
|
|
}
|