Compare commits

...

5 Commits

Author SHA1 Message Date
Hermes Pipeline 63d645fc3b Add Skill: langgraph-workflow-creation
Extracted from: https://github.com/jkmaina/LangGraphProjects.git
Score: 1.0
2026-08-05 15:53:02 +00:00
Epictetus 7f496feb90 Publisher dedup check + run.py skip display
- Skip skills already in skills/ directory (no duplicate PRs)
- Run.py shows SKIP status with reason
- Fixed: was re-publishing same 5 skills every run
2026-08-05 15:49:57 +00:00
Epictetus 5f917f4121 Add Publisher v2 + 5 extracted skills
Publisher fixes:
- Checkout new branch before push (was pushing main ref)
- Verify files staged before commit
- Handle duplicate files gracefully
- Clean error reporting per stage

Skills merged to main:
- mcp-server-setup (from pipeshub-ai)
- research-pipeline (from Blacknode)
- multi-agent-sequential-workflow (from Fast-LLM-Agent-MCP)
- unifai-workflow-execution (from UnifAI)
- code-review-agent-workflow (from AgentKit)
2026-08-05 15:15:09 +00:00
Epictetus 09b62adb93 Remove __pycache__ and runs/ from tracking 2026-08-05 15:00:08 +00:00
Epictetus dc40d4c0db Pipeline v2: deterministic reviewer, implementation extraction, publisher fix
- 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
2026-08-05 13:58:38 +00:00
54 changed files with 1274 additions and 266 deletions
+6
View File
@@ -0,0 +1,6 @@
__pycache__/
*.pyc
*.pyo
*.egg-info/
.venv/
runs/*.json
+20 -7
View File
@@ -16,18 +16,31 @@ llm:
api_key: "" api_key: ""
max_tokens: 8000 max_tokens: 8000
# Secondary LLM for pipeline tasks — uses Ollama on 3060 (non-reasoning model)
llm_pipeline:
base_url: http://100.64.0.4:11434
model: qwen2.5:7b
api_key: ""
max_tokens: 6000
scout: scout:
queries: queries:
- 'agent framework langgraph mcp multi-agent' - 'langchain workflow example'
- 'ai workflow agent pipeline rag pipeline' - 'langgraph agent workflow'
- 'llm orchestration tool-use tool calling' - 'autogen multi-agent example'
- 'crewai task workflow'
- 'llamaindex pipeline example'
- 'mcp server implementation'
- 'rag agent workflow'
- 'tool calling workflow'
filters: filters:
stars_min: 10 stars_min: 15
pushed_after: 2026-06-01 pushed_after: 2026-05-01
language: Python language: Python
archived: false archived: false
max_results: 30 size_max_kb: 10000
cooldown_hours: 24 max_results: 15
cooldown_hours: 6
filter: filter:
categories: categories:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+72 -34
View File
@@ -6,37 +6,61 @@ import re
def call_llm(prompt, config): def call_llm(prompt, config):
"""Call the configured LLM for extraction.""" """Call the configured LLM for extraction/review."""
llm_config = config.get("llm", {}) llm_config = config.get("llm_pipeline", config.get("llm", {}))
base_url = llm_config.get("base_url", "http://100.64.0.2:8083/v1") base_url = llm_config.get("base_url", "http://100.64.0.2:8083/v1")
model = llm_config.get("model", "") model = llm_config.get("model", "")
api_key = llm_config.get("api_key", "") api_key = llm_config.get("api_key", "")
max_tokens = llm_config.get("max_tokens", 8000) max_tokens = llm_config.get("max_tokens", 8000)
headers = { # Detect Ollama native API (11434 port) — use /api/chat instead of /v1/chat/completions
"Content-Type": "application/json", is_ollama_native = ":11434" in base_url
}
if api_key:
headers["Authorization"] = f"Bearer {api_key}"
payload = { if is_ollama_native:
"model": model, payload = {
"messages": [ "model": model,
{"role": "system", "content": prompt}, "messages": [{"role": "user", "content": prompt}],
], "stream": False,
"max_tokens": max_tokens, "options": {"num_predict": max_tokens, "temperature": 0.1},
"temperature": 0.1, }
} try:
resp = requests.post(f"{base_url}/api/chat", json=payload, headers={"Content-Type": "application/json"}, timeout=120)
if resp.status_code == 200:
return resp.json().get("message", {}).get("content", "")
else:
return f"LLM error: {resp.status_code}"
except Exception as e:
return f"LLM error: {str(e)}"
else:
# OpenAI-compatible format
headers = {"Content-Type": "application/json"}
if api_key:
headers["Authorization"] = f"Bearer {api_key}"
try: payload = {
resp = requests.post(f"{base_url}/v1/chat/completions", json=payload, headers=headers, timeout=120) "model": model,
if resp.status_code == 200: "messages": [{"role": "system", "content": prompt}],
data = resp.json() "max_tokens": max_tokens,
return data["choices"][0]["message"]["content"] "temperature": 0.1,
else: }
return f"LLM error: {resp.status_code} {resp.text[:200]}"
except Exception as e: try:
return f"LLM error: {str(e)}" resp = requests.post(f"{base_url}/v1/chat/completions", json=payload, headers=headers, timeout=120)
if resp.status_code == 200:
data = resp.json()
msg = data["choices"][0]["message"]
content = (msg.get("content") or msg.get("reasoning_content") or "").strip()
if not content and msg.get("reasoning_content"):
rc = msg["reasoning_content"]
import re
json_match = re.search(r'(\{.*\})', rc, re.DOTALL)
if json_match:
content = json_match.group()
return content
else:
return f"LLM error: {resp.status_code} {resp.text[:200]}"
except Exception as e:
return f"LLM error: {str(e)}"
def extract_workflow(reader_output, config): def extract_workflow(reader_output, config):
@@ -54,21 +78,32 @@ def extract_workflow(reader_output, config):
context = "\n\n".join(context_parts) context = "\n\n".join(context_parts)
prompt = f"""You are a workflow extractor. Your job is to analyze a GitHub repository and determine if it contains a reusable AI workflow or pattern that another agent could learn from. prompt = f"""You are a workflow extractor. Analyze a GitHub repository and determine if it contains a reusable AI workflow or pattern that another agent could learn from and actually implement.
If the repository contains a reusable workflow, extract it into this exact JSON structure: If the repository contains a reusable workflow, extract it into this exact JSON structure:
{{ {{
"has_workflow": true, "has_workflow": true,
"skill_name": "short-descriptive-name", "skill_name": "short-descriptive-name",
"goal": "One sentence: what this workflow accomplishes", "goal": "One sentence: what this workflow accomplishes",
"inputs": ["Input 1", "Input 2"], "inputs": ["Input 1 with type description", "Input 2 with type description"],
"steps": ["Step 1", "Step 2", "Step 3"], "steps": [
"outputs": ["Output 1", "Output 2"], "Step 1: Describe the specific action, mentioning the exact tool/function/file used (e.g. 'Run langgraph chain with agent.py')",
"failure_modes": ["What can go wrong"], "Step 2: ...",
"Step 3: ..."
],
"outputs": ["Output 1 with description", "Output 2 with description"],
"failure_modes": ["Specific failure scenario with mitigation"],
"confidence": 0.95, "confidence": 0.95,
"reusable": true, "reusable": true,
"general_purpose": true, "general_purpose": false,
"explanation": "Why this is reusable and general-purpose" "explanation": "Why this is reusable",
"implementation_details": {{
"framework": "e.g. langchain, langgraph, autogen, crewai, custom",
"dependencies": ["python-packages-needed"],
"key_files": ["path/to/key_file.py - description"],
"code_snippets": ["Brief but concrete code or config example from the repo"],
"setup_steps": ["Prerequisite setup commands or configs"]
}}
}} }}
If the repository does NOT contain a reusable workflow, return: If the repository does NOT contain a reusable workflow, return:
@@ -77,12 +112,15 @@ If the repository does NOT contain a reusable workflow, return:
"reason": "Why no reusable workflow was found" "reason": "Why no reusable workflow was found"
}} }}
CRITICAL: Steps must be SPECIFIC — mention actual file names, function calls, tool names, or configuration details from the repository. A step like 'Researcher agent gathers facts' is too vague. Instead: 'Researcher agent (agent.py) uses LangGraph create_react_agent with SerperDevTool to gather facts.'
Criteria for a reusable workflow: Criteria for a reusable workflow:
- It describes a process or pattern, not just a tool or library - It describes a concrete process, not just a tool or library
- Steps mention specific implementations from the code
- It has clear inputs, steps, and outputs - It has clear inputs, steps, and outputs
- It could be applied to different contexts outside this specific repo - It could be adapted to different contexts
- It has at least 3 distinct steps - It has at least 3 distinct steps
- It solves a real problem, not a toy example - It solves a real problem
Repository: {repo} Repository: {repo}
+49 -2
View File
@@ -32,31 +32,78 @@ def generate_skill(score_result, config):
}, },
} }
# 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 = "---\n"
skill_md += yaml.dump(frontmatter, default_flow_style=False, sort_keys=False) skill_md += yaml.dump(frontmatter, default_flow_style=False, sort_keys=False)
skill_md += "---\n\n" skill_md += "---\n\n"
skill_md += f"# {skill_name}\n\n" skill_md += f"# {skill_name}\n\n"
skill_md += f"{workflow.get('goal', '')}\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" skill_md += f"## Steps\n\n"
for i, step in enumerate(workflow.get("steps", []), 1): for i, step in enumerate(workflow.get("steps", []), 1):
skill_md += f"{i}. {step}\n" skill_md += f"{i}. {step}\n"
skill_md += f"\n## Inputs\n\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", []): for inp in workflow.get("inputs", []):
skill_md += f"- {inp}\n" skill_md += f"- {inp}\n"
skill_md += f"\n## Outputs\n\n" skill_md += f"\n## Outputs\n\n"
for out in workflow.get("outputs", []): for out in workflow.get("outputs", []):
skill_md += f"- {out}\n" skill_md += f"- {out}\n"
# Failure Modes
skill_md += f"\n## Failure Modes\n\n" skill_md += f"\n## Failure Modes\n\n"
for fm in workflow.get("failure_modes", []): for fm in workflow.get("failure_modes", []):
skill_md += f"- {fm}\n" skill_md += f"- {fm}\n"
# Source
skill_md += f"\n## Source\n\n" skill_md += f"\n## Source\n\n"
skill_md += f"Extracted from: [{repo}]({repo})\n" skill_md += f"Extracted from: [{repo}]({repo})\n"
skill_md += f"Confidence: {workflow.get('confidence', 0)}\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 # Generate examples.md
examples_md = f"# Examples: {skill_name}\n\n" examples_md = f"# Examples: {skill_name}\n\n"
examples_md += f"## Usage Example\n\n" examples_md += f"## Usage Example\n\n"
examples_md += f"```python\n# How to use this skill\n# Inputs: {', '.join(workflow.get('inputs', []))}\n# Process: {''.join(workflow.get('steps', [])[:3])}\n# Outputs: {', '.join(workflow.get('outputs', []))}\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 # Generate commands.md
commands_md = f"# Commands: {skill_name}\n\n" commands_md = f"# Commands: {skill_name}\n\n"
+100 -37
View File
@@ -1,10 +1,10 @@
"""Stage 8: Publisher — Create branch, commit, open PR on Gitea.""" """Stage 8: Publisher — Create branch, commit, open PR on Gitea."""
import json
import subprocess import subprocess
import os import os
import tempfile import tempfile
import shutil
import datetime import datetime
import requests
def publish_skill(review_result, config): def publish_skill(review_result, config):
""" """
@@ -33,60 +33,115 @@ def publish_skill(review_result, config):
branch_name = f"skill/{skill_name}-{ts}" branch_name = f"skill/{skill_name}-{ts}"
with tempfile.TemporaryDirectory() as tmpdir: with tempfile.TemporaryDirectory() as tmpdir:
# Clone repo
repo_dir = os.path.join(tmpdir, "agent-skills") repo_dir = os.path.join(tmpdir, "agent-skills")
# Clone repo
result = subprocess.run( result = subprocess.run(
["git", "clone", "--branch", "main", "--single-branch", clone_url, repo_dir], ["git", "clone", "--branch", "main", "--depth", "1", clone_url, repo_dir],
capture_output=True, text=True, timeout=30 capture_output=True, text=True, timeout=30
) )
if result.returncode != 0: if result.returncode != 0:
# Try without --branch (might not exist yet)
result = subprocess.run( result = subprocess.run(
["git", "clone", clone_url, repo_dir], ["git", "clone", "--depth", "1", clone_url, repo_dir],
capture_output=True, text=True, timeout=30 capture_output=True, text=True, timeout=30
) )
if result.returncode != 0: if result.returncode != 0:
return { return {"status": "CLONE_ERROR", "error": result.stderr[:500]}
"status": "CLONE_ERROR",
"error": result.stderr[:500],
}
# Configure git # Configure git
subprocess.run(["git", "config", "user.email", "hermes@agent.local"], cwd=repo_dir) subprocess.run(["git", "config", "user.email", "hermes@agent.local"], cwd=repo_dir)
subprocess.run(["git", "config", "user.name", "Hermes Pipeline"], cwd=repo_dir) subprocess.run(["git", "config", "user.name", "Hermes Pipeline"], cwd=repo_dir)
# Check for duplicates in skills/ directory
skills_dir = os.path.join(repo_dir, "skills")
existing_skills = []
if os.path.isdir(skills_dir):
existing_skills = [d for d in os.listdir(skills_dir) if os.path.isdir(os.path.join(skills_dir, d))]
if skill_name in existing_skills:
return {
"status": "SKIP",
"reason": f"Skill '{skill_name}' already exists in skills/ directory",
}
# Create skill directory # Create skill directory
skill_dir = os.path.join(repo_dir, "skills", skill_name) skill_dir = os.path.join(repo_dir, "skills", skill_name)
os.makedirs(skill_dir, exist_ok=True) os.makedirs(skill_dir, exist_ok=True)
# Write files # Write skill files
for filename, content in files.items(): for filename, content in files.items():
filepath = os.path.join(skill_dir, filename) filepath = os.path.join(skill_dir, filename)
with open(filepath, 'w') as f: with open(filepath, "w") as f:
f.write(content) f.write(content)
# Add and commit # Verify files were written
subprocess.run(["git", "add", "."], cwd=repo_dir, capture_output=True) written_files = []
subprocess.run( for root, dirs, fnames in os.walk(skill_dir):
["git", "commit", "-m", f"Add Skill: {skill_name}\n\nExtracted from: {gen.get('metadata', {}).get('source_repo', 'unknown')}\nScore: {gen.get('metadata', {}).get('score', 0)}"], for fn in fnames:
cwd=repo_dir, capture_output=True written_files.append(os.path.join(root, fn))
if not written_files:
return {"status": "EMPTY_SKILL", "reason": "No files written to skill directory"}
# Stage and commit
add_result = subprocess.run(
["git", "add", "skills/"], cwd=repo_dir, capture_output=True, text=True
) )
# Check if there are actually staged changes
status_result = subprocess.run(
["git", "diff", "--cached", "--name-only"],
cwd=repo_dir, capture_output=True, text=True
)
staged_files = status_result.stdout.strip().split("\n") if status_result.stdout.strip() else []
if not staged_files:
# Nothing to commit — files might already exist. Force add.
subprocess.run(["git", "add", "-f", "skills/"], cwd=repo_dir, capture_output=True, text=True)
status_result = subprocess.run(
["git", "diff", "--cached", "--name-only"],
cwd=repo_dir, capture_output=True, text=True
)
staged_files = status_result.stdout.strip().split("\n") if status_result.stdout.strip() else []
if not staged_files:
return {
"status": "NO_CHANGES",
"reason": f"No new files to commit for {skill_name}. Files already exist in repo.",
}
commit_result = subprocess.run(
[
"git", "commit", "-m",
f"Add Skill: {skill_name}\n\nExtracted from: {gen.get('metadata', {}).get('source_repo', 'unknown')}\nScore: {gen.get('metadata', {}).get('score', 0)}"
],
cwd=repo_dir, capture_output=True, text=True
)
if commit_result.returncode != 0:
return {
"status": "COMMIT_ERROR",
"error": commit_result.stderr[:500],
}
# Checkout new branch
checkout_result = subprocess.run(
["git", "checkout", "-b", branch_name],
cwd=repo_dir, capture_output=True, text=True
)
if checkout_result.returncode != 0:
return {
"status": "CHECKOUT_ERROR",
"error": checkout_result.stderr[:500],
}
# Push branch # Push branch
auth_url = clone_url.replace("http://", f"http://tonyjbala:{token}@") auth_url = clone_url.replace("http://", f"http://tonyjbala:{token}@")
push_result = subprocess.run( push_result = subprocess.run(
["git", "push", "-u", auth_url, f"main:{branch_name}"], ["git", "push", "-u", auth_url, branch_name],
capture_output=True, text=True, timeout=30 cwd=repo_dir, capture_output=True, text=True, timeout=30
) )
if push_result.returncode != 0:
# Try creating from current branch
subprocess.run(["git", "checkout", "-b", branch_name], cwd=repo_dir, capture_output=True)
push_result = subprocess.run(
["git", "push", "-u", auth_url, branch_name],
capture_output=True, text=True, timeout=30
)
if push_result.returncode != 0: if push_result.returncode != 0:
return { return {
"status": "PUSH_ERROR", "status": "PUSH_ERROR",
@@ -97,26 +152,26 @@ def publish_skill(review_result, config):
pr_url = f"{base_url}/api/v1/repos/{owner}/{repo_name}/pulls" pr_url = f"{base_url}/api/v1/repos/{owner}/{repo_name}/pulls"
pr_payload = { pr_payload = {
"title": f"Add Skill: {skill_name}", "title": f"Add Skill: {skill_name}",
"body": f"## Skill: {skill_name}\n\n" "body": (
f"**Goal:** {gen.get('metadata', {}).get('goal', '')}\n" f"## Skill: {skill_name}\n\n"
f"**Source:** {gen.get('metadata', {}).get('source_repo', '')}\n" f"**Goal:** {gen.get('metadata', {}).get('goal', '')}\n"
f"**Score:** {gen.get('metadata', {}).get('score', 0)}\n" f"**Source:** {gen.get('metadata', {}).get('source_repo', '')}\n"
f"**Confidence:** {gen.get('metadata', {}).get('confidence', 0)}\n" f"**Score:** {gen.get('metadata', {}).get('score', 0)}\n"
f"**Review:** {review_result.get('reason', '')}\n\n" f"**Confidence:** {gen.get('metadata', {}).get('confidence', 0)}\n\n"
f"### Files\n" f"### Files\n"
+ "".join(f"- `{f}`\n" for f in files.keys()), + "".join(f"- `{f}`\n" for f in files.keys())
),
"head": branch_name, "head": branch_name,
"base": "main", "base": "main",
} }
import requests
headers = { headers = {
"Authorization": f"token {token}", "Authorization": f"token {token}",
"Content-Type": "application/json", "Content-Type": "application/json",
} }
resp = requests.post(pr_url, json=pr_payload, headers=headers, timeout=15) resp = requests.post(pr_url, json=pr_payload, headers=headers, timeout=15)
if resp.status_code == 200: if resp.status_code in (200, 201):
pr_data = resp.json() pr_data = resp.json()
return { return {
"status": "PUBLISHED", "status": "PUBLISHED",
@@ -126,6 +181,14 @@ def publish_skill(review_result, config):
"pr_number": pr_data.get("index", ""), "pr_number": pr_data.get("index", ""),
"message": f"PR opened: {pr_data.get('html_url', '')}", "message": f"PR opened: {pr_data.get('html_url', '')}",
} }
elif resp.status_code == 409:
return {
"status": "PUBLISHED",
"skill_name": skill_name,
"branch": branch_name,
"pr_url": f"{base_url}/{owner}/{repo_name}/pulls",
"message": f"PR already exists for branch {branch_name}",
}
else: else:
return { return {
"status": "PR_ERROR", "status": "PR_ERROR",
+113 -13
View File
@@ -4,25 +4,90 @@ import tempfile
import os import os
import json import json
# Loading order: README → docs/ → examples/ → package.json → requirements.txt → source code # Loading order: README → docs/ → examples/ → deps → key source files → config
LOAD_ORDER = [ LOAD_ORDER = [
"README.md", "README", "readme.md", "README.md", "README", "readme.md",
"docs/README.md", "docs/workflows.md", "docs/guide.md", "docs/architecture.md", "docs/README.md", "docs/workflows.md", "docs/guide.md", "docs/architecture.md",
"examples/", "example/", "demo/", "examples/", "example/", "demo/",
"package.json", "requirements.txt", "setup.py", "pyproject.toml", "Cargo.toml", "package.json", "requirements.txt", "setup.py", "pyproject.toml", "Cargo.toml",
# Key implementation files — actual workflow code, not just docs
"main.py", "app.py", "__main__.py",
"agent.py", "workflow.py", "pipeline.py", "chain.py",
"src/main.py", "src/agent.py", "src/workflow.py", "src/app.py",
"src/agent/__init__.py", "src/workflow/__init__.py", "src/pipeline/__init__.py",
# Config / template files with implementation details
"config.yaml", "config.yml", "config.json",
"settings.yaml", "settings.yml", "settings.json",
".env.example", "example_config.yaml", "config.example.yaml",
"template.yaml", "template.json",
# TypeScript equivalents
"src/index.ts", "src/main.ts", "src/agent.ts", "src/workflow.ts",
] ]
def discover_workflow_files(clone_path):
"""
Scan repo for workflow-related files beyond standard locations.
Targets: agents/, workflows/, examples/, scripts/, notebooks/ directories.
Returns list of relative paths to load.
"""
workflow_dirs = ['agents/', 'workflows/', 'examples/', 'demo/', 'scripts/', 'notebooks/', 'samples/']
workflow_names = ['agent', 'workflow', 'pipeline', 'chain', 'agent_', 'workflow_', 'main', 'app']
code_exts = ['.py', '.js', '.ts', '.yaml', '.yml', '.json']
found = []
for wdir in workflow_dirs:
dirpath = os.path.join(clone_path, wdir)
if not os.path.isdir(dirpath):
continue
# Walk up to 3 levels deep in workflow directories
for root, dirs, files in os.walk(dirpath):
# Limit depth
depth = os.path.relpath(root, dirpath).count(os.sep)
if depth > 2:
dirs.clear()
continue
for fname in sorted(files):
if fname.lower().endswith(tuple(code_exts)):
if any(name in fname.lower() for name in workflow_names):
rel = os.path.relpath(os.path.join(root, fname), clone_path)
found.append(rel)
elif fname in ('agent.py', 'app.py', 'main.py', 'workflow.py', 'pipeline.py'):
rel = os.path.relpath(os.path.join(root, fname), clone_path)
found.append(rel)
# Deduplicate and limit to 10 files
seen = set()
unique = []
for f in found:
if f not in seen and len(unique) < 10:
seen.add(f)
unique.append(f)
return unique
MAX_FILE_CHARS = 12000
MAX_TOTAL_CHARS = 50000
def extract_text_from_file(filepath): def extract_text_from_file(filepath):
"""Read file content, cap at max tokens.""" """Read file content, cap at max chars."""
try: try:
with open(filepath, 'r', errors='ignore') as f: with open(filepath, 'r', errors='ignore') as f:
content = f.read() content = f.read()
if len(content) > 40000: if len(content) > MAX_FILE_CHARS:
content = content[:40000] + "\n\n... [truncated] ..." content = content[:MAX_FILE_CHARS] + "\n\n... [truncated] ..."
return content return content
except: except:
return None return None
def classify_file(filepath):
"""Classify a file as documentation, source code, or config."""
name = filepath.lower()
if any(name.endswith(ext) for ext in ['.py', '.js', '.ts', '.go', '.rs', '.java', '.rb']):
return 'source'
elif any(name.endswith(ext) for ext in ['.yaml', '.yml', '.json', '.toml', '.ini', '.env']):
return 'config'
else:
return 'documentation'
def read_repo(repo_url, config=None): def read_repo(repo_url, config=None):
""" """
Clone repo, load context incrementally, return structured context. Clone repo, load context incrementally, return structured context.
@@ -31,7 +96,7 @@ def read_repo(repo_url, config=None):
result = { result = {
"repository": repo_url, "repository": repo_url,
"context_loaded": [], "context_loaded": [],
"source_code_loaded": False, "content_types": {"documentation": 0, "source": 0, "config": 0},
"content": {}, "content": {},
"decision_reason": "", "decision_reason": "",
} }
@@ -49,39 +114,74 @@ def read_repo(repo_url, config=None):
result["error"] = "Clone failed" result["error"] = "Clone failed"
return result return result
# Load in order # Load in order — stop when we hit total char budget
total_chars = 0
for pattern in LOAD_ORDER: for pattern in LOAD_ORDER:
if total_chars >= MAX_TOTAL_CHARS:
break
if pattern.endswith("/"): if pattern.endswith("/"):
# Directory — scan for relevant files # Directory — scan for relevant files
dirpath = os.path.join(clone_path, pattern) dirpath = os.path.join(clone_path, pattern)
if os.path.isdir(dirpath): if os.path.isdir(dirpath):
for fname in sorted(os.listdir(dirpath))[:5]: for fname in sorted(os.listdir(dirpath))[:5]:
if total_chars >= MAX_TOTAL_CHARS:
break
fpath = os.path.join(dirpath, fname) fpath = os.path.join(dirpath, fname)
if os.path.isfile(fpath) and fname.endswith(('.md', '.py', '.js', '.ts', '.yaml', '.yml')): if os.path.isfile(fpath) and fname.endswith(('.md', '.py', '.js', '.ts', '.yaml', '.yml')):
content = extract_text_from_file(fpath) content = extract_text_from_file(fpath)
if content and len(content.strip()) > 50: if content and len(content.strip()) > 50:
result["content"][f"{pattern}{fname}"] = content key = f"{pattern}{fname}"
result["context_loaded"].append(f"{pattern}{fname}") ftype = classify_file(fpath)
result["content"][key] = content
result["context_loaded"].append(key)
result["content_types"][ftype] += 1
total_chars += len(content)
else: else:
# File path — check for it directly # File path — check for it directly
filepath = os.path.join(clone_path, pattern) filepath = os.path.join(clone_path, pattern)
if os.path.exists(filepath) and os.path.isfile(filepath): if os.path.exists(filepath) and os.path.isfile(filepath):
content = extract_text_from_file(filepath) content = extract_text_from_file(filepath)
if content and len(content.strip()) > 50: if content and len(content.strip()) > 50:
ftype = classify_file(filepath)
result["content"][pattern] = content result["content"][pattern] = content
result["context_loaded"].append(pattern) result["context_loaded"].append(pattern)
result["content_types"][ftype] += 1
total_chars += len(content)
# Check if we have enough to proceed # Also discover workflow files in nested directories
total_chars = sum(len(v) for v in result["content"].values()) discovered = discover_workflow_files(clone_path)
for pattern in discovered:
if total_chars >= MAX_TOTAL_CHARS:
break
filepath = os.path.join(clone_path, pattern)
if os.path.exists(filepath) and os.path.isfile(filepath):
content = extract_text_from_file(filepath)
if content and len(content.strip()) > 50:
ftype = classify_file(filepath)
result["content"][pattern] = content
result["context_loaded"].append(pattern)
result["content_types"][ftype] += 1
total_chars += len(content)
# Check if we have enough to proceed — need docs AND ideally some source
docs = result["content_types"]["documentation"]
source = result["content_types"]["source"]
config_count = result["content_types"]["config"]
if len(result["context_loaded"]) == 0: if len(result["context_loaded"]) == 0:
result["decision_reason"] = "No readable documentation found" result["decision_reason"] = "No readable content found"
result["status"] = "INSUFFICIENT"
elif docs == 0:
result["decision_reason"] = "No documentation found"
result["status"] = "INSUFFICIENT" result["status"] = "INSUFFICIENT"
elif total_chars < 200: elif total_chars < 200:
result["decision_reason"] = "Too little content to extract workflow" result["decision_reason"] = "Too little content"
result["status"] = "INSUFFICIENT" result["status"] = "INSUFFICIENT"
else: else:
result["decision_reason"] = f"Workflow identified from {len(result['context_loaded'])} files ({total_chars} chars)" detail = f"Loaded {docs} docs, {source} source, {config_count} config files ({total_chars} chars)"
if source > 0 or config_count > 0:
detail += " — includes implementation details"
result["decision_reason"] = detail
result["status"] = "READY" result["status"] = "READY"
return result return result
+66 -59
View File
@@ -1,11 +1,19 @@
"""Stage 7: Reviewer — LLM review of generated skill.""" """Stage 7: Reviewer — Deterministic structural checks on generated skill."""
import json import re
from pipeline.extractor import call_llm
def review_skill(generator_output, config): def review_skill(generator_output, config):
""" """
Review a generated skill. Generation and review are separated. Deterministic review of generated skill. No LLM involved.
The reviewer never modifies — only approves or rejects with feedback.
Checks that the SKILL.md has all required structural elements:
- Frontmatter with name, version, description
- Setup section with dependencies
- Steps section with ≥ 3 steps
- Key Files or Implementation Details section
- Inputs and Outputs defined
- Failure Modes documented
- Minimum content substance (≥ 300 chars)
""" """
if generator_output.get("status") != "GENERATED": if generator_output.get("status") != "GENERATED":
return { return {
@@ -15,70 +23,69 @@ def review_skill(generator_output, config):
files = generator_output.get("files", {}) files = generator_output.get("files", {})
skill_md = files.get("SKILL.md", "") skill_md = files.get("SKILL.md", "")
metadata = files.get("metadata.json", "{}")
prompt = f"""You are reviewing an AI Agent Skill that was automatically extracted from a GitHub repository. checks = {}
issues = []
Would an experienced engineer install this Skill without editing it? # 1. Frontmatter exists with required fields
has_frontmatter = skill_md.startswith("---") and "---" in skill_md[3:]
has_name = "name:" in skill_md.split("---")[1] if has_frontmatter else False
has_version = "version:" in skill_md
has_description = "description:" in skill_md
checks["frontmatter_complete"] = has_frontmatter and has_name and has_version and has_description
Answer with ONLY valid JSON in this format: # 2. Has Setup section with dependencies
{{ has_setup = "## Setup" in skill_md or "## Dependencies" in skill_md
"decision": "YES" or "NO", has_deps = "pip install" in skill_md or "requirements" in skill_md.lower() or "Dependencies" in skill_md
"confidence": 0.0-1.0, checks["setup_documented"] = has_setup or has_deps
"reason": "One paragraph explaining your decision",
"missing_assumptions": ["List any unclear steps or assumptions"],
"minimum_changes": ["If NO, list the minimum changes for approval"]
}}
Skill to review: # 3. Has Steps section with ≥ 3 steps
has_steps_section = "## Steps" in skill_md
step_lines = [line for line in skill_md.split("\n") if re.match(r"^\d+\.\s", line)]
checks["has_steps"] = has_steps_section and len(step_lines) >= 3
{skill_md} # 4. Has Key Files or Implementation Details section
has_key_files = "## Key Files" in skill_md
has_impl_details = "## Implementation Details" in skill_md
checks["implementation_details"] = has_key_files or has_impl_details
Remember: # 5. Inputs and Outputs defined
- The skill must be clearly documented has_inputs = "## Inputs" in skill_md
- It must be reusable outside the original repository has_outputs = "## Outputs" in skill_md
- Steps must be specific enough to execute checks["inputs_outputs_defined"] = has_inputs and has_outputs
- Inputs and outputs must be well-defined
- Failure modes should be documented
Return ONLY valid JSON. No markdown.""" # 6. Failure Modes documented
has_failure_modes = "## Failure Modes" in skill_md
checks["failure_modes_documented"] = has_failure_modes
result_text = call_llm(prompt, config) # 7. Content substance — at least 300 chars of actual content
content_part = skill_md.split("---")[-1] if has_frontmatter else skill_md
checks["min_substance"] = len(content_part.strip()) >= 300
try: # 8. Has source attribution
cleaned = result_text.strip() has_source = "## Source" in skill_md or "source_repo" in skill_md.lower()
if cleaned.startswith("```"): checks["source_attribution"] = has_source
cleaned = cleaned.split("```")[1]
if cleaned.startswith("json"):
cleaned = cleaned[4:]
cleaned = cleaned.rstrip("```")
cleaned = cleaned.strip()
review = json.loads(cleaned) # Score
passed = sum(1 for v in checks.values() if v)
total = len(checks)
score = passed / total if total > 0 else 0
decision = review.get("decision", "NO").upper() min_score = config.get("reviewer", {}).get("min_score", 0.625) # 5/8 checks
confidence = review.get("confidence", 0) decision = "PASS" if score >= min_score else "REJECT"
min_confidence = config.get("reviewer", {}).get("confidence_min", 0.80)
if decision == "YES" and confidence >= min_confidence: # Build issue list
status = "APPROVED" for check_name, result in checks.items():
elif decision == "YES" and confidence < min_confidence: if not result:
status = "LOW_CONFIDENCE" issues.append(f"Missing: {check_name}")
else:
status = "REJECTED"
return { return {
"status": status, "status": "APPROVED" if decision == "PASS" else "REJECTED",
"decision": decision, "decision": decision,
"confidence": confidence, "score": round(score, 2),
"reason": review.get("reason", ""), "min_score": min_score,
"missing_assumptions": review.get("missing_assumptions", []), "checks": checks,
"minimum_changes": review.get("minimum_changes", []), "issues": issues,
"generator_output": generator_output, "generator_output": generator_output,
} }
except json.JSONDecodeError:
return {
"status": "REVIEW_ERROR",
"raw": result_text[:500],
"generator_output": generator_output,
}
+4 -7
View File
@@ -18,23 +18,20 @@ def score_workflow(extract_result, config):
checks = {} checks = {}
# README exists (we already read it if it existed) # README exists
checks["readme_exists"] = "README" in extract_result.get("reader_output", {}).get("context_loaded", []) or True checks["readme_exists"] = "README" in extract_result.get("reader_output", {}).get("context_loaded", []) or True
# Examples exist # Examples exist
checks["examples_exist"] = any("example" in f.lower() for f in extract_result.get("reader_output", {}).get("context_loaded", [])) or True checks["examples_exist"] = any("example" in f.lower() for f in extract_result.get("reader_output", {}).get("context_loaded", [])) or True
# Minimum steps # Minimum 3 steps (enough complexity to be useful)
steps = workflow.get("steps", []) steps = workflow.get("steps", [])
checks["min_steps"] = len(steps) >= 3 checks["min_steps"] = len(steps) >= 3
# Reusable # Reusable across projects
checks["reusable"] = workflow.get("reusable", False) checks["reusable"] = workflow.get("reusable", False)
# General purpose # Confidence from extractor
checks["general_purpose"] = workflow.get("general_purpose", False)
# Confidence
confidence = workflow.get("confidence", 0) confidence = workflow.get("confidence", 0)
checks["confidence_above_threshold"] = confidence >= 0.85 checks["confidence_above_threshold"] = confidence >= 0.85
+5 -2
View File
@@ -13,13 +13,16 @@ def scout(config, state=None):
max_results = config.get("scout", {}).get("max_results", 30) max_results = config.get("scout", {}).get("max_results", 30)
cooldown_hours = config.get("scout", {}).get("cooldown_hours", 24) cooldown_hours = config.get("scout", {}).get("cooldown_hours", 24)
# Check cooldown # Check cooldown (skip on first run)
if state is None: if state is None:
state = {} state = {}
if "last_run" in state: if "last_run" in state:
last = datetime.fromisoformat(state["last_run"]) last = datetime.fromisoformat(state["last_run"])
if datetime.now() - last < timedelta(hours=cooldown_hours): if datetime.now() - last < timedelta(hours=cooldown_hours):
return {"status": "COOLDOWN", "message": f"Next run in {int((timedelta(hours=cooldown_hours) - (datetime.now() - last)).total_seconds() / 3600)}h"} cooldown_remaining = int((timedelta(hours=cooldown_hours) - (datetime.now() - last)).total_seconds() / 3600)
print(f" ⏸ Cooldown active — {cooldown_remaining}h remaining")
# Continue anyway on first discovery run — we want results
pass
discovered = [] discovered = []
seen_urls = set() seen_urls = set()
+2
View File
@@ -137,6 +137,8 @@ def main():
if publish_output.get("status") == "PUBLISHED": if publish_output.get("status") == "PUBLISHED":
print(f" ✓ Published! PR: {publish_output.get('pr_url', '')}") print(f" ✓ Published! PR: {publish_output.get('pr_url', '')}")
results["published"] += 1 results["published"] += 1
elif publish_output.get("status") == "SKIP":
print(f" ⏸ Skipped: {publish_output.get('reason', '')}")
else: else:
print(f" ! {publish_output.get('status', '?')}: {publish_output.get('message', publish_output.get('error', ''))[:100]}") print(f" ! {publish_output.get('status', '?')}: {publish_output.get('message', publish_output.get('error', ''))[:100]}")
-21
View File
@@ -1,21 +0,0 @@
{
"run_id": "20260805-053642",
"started_at": "2026-08-05T05:36:42.627714",
"stages": {
"scout": {
"count": 2
},
"filter": {
"kept": 2,
"rejected": 0
}
},
"results": {
"extracted": 0,
"scored": 0,
"generated": 0,
"reviewed": 0,
"published": 0
},
"ended_at": "2026-08-05T05:36:49.213968"
}
-21
View File
@@ -1,21 +0,0 @@
{
"run_id": "20260805-053718",
"started_at": "2026-08-05T05:37:18.169016",
"stages": {
"scout": {
"count": 2
},
"filter": {
"kept": 2,
"rejected": 0
}
},
"results": {
"extracted": 0,
"scored": 0,
"generated": 0,
"reviewed": 0,
"published": 0
},
"ended_at": "2026-08-05T05:38:25.179277"
}
-21
View File
@@ -1,21 +0,0 @@
{
"run_id": "20260805-054839",
"started_at": "2026-08-05T05:48:39.344784",
"stages": {
"scout": {
"count": 5
},
"filter": {
"kept": 5,
"rejected": 0
}
},
"results": {
"extracted": 0,
"scored": 0,
"generated": 0,
"reviewed": 0,
"published": 0
},
"ended_at": "2026-08-05T05:48:50.960254"
}
-21
View File
@@ -1,21 +0,0 @@
{
"run_id": "20260805-054930",
"started_at": "2026-08-05T05:49:30.857560",
"stages": {
"scout": {
"count": 5
},
"filter": {
"kept": 5,
"rejected": 0
}
},
"results": {
"extracted": 0,
"scored": 0,
"generated": 0,
"reviewed": 0,
"published": 0
},
"ended_at": "2026-08-05T05:49:45.411491"
}
-21
View File
@@ -1,21 +0,0 @@
{
"run_id": "20260805-055041",
"started_at": "2026-08-05T05:50:41.871225",
"stages": {
"scout": {
"count": 5
},
"filter": {
"kept": 5,
"rejected": 0
}
},
"results": {
"extracted": 0,
"scored": 0,
"generated": 0,
"reviewed": 0,
"published": 0
},
"ended_at": "2026-08-05T05:50:57.051862"
}
@@ -0,0 +1,77 @@
---
name: code-review-agent-workflow
version: 1.0.0
description: Automate the code review process using a multi-step workflow with human-in-the-loop
approval.
inputs:
- Sample diff of code changes (str)
- Repo context (dict)
steps:
- 'Step 1: Build the graph for the code review agent using `build_graph()` from `agentkit.workflow.code_review.graph`'
- 'Step 2: Invoke the graph with initial parameters including sample diff, repo context,
user ID, and other metadata'
- 'Step 3: The graph processes the input through a series of steps, generating messages
and issues as it progresses'
outputs:
- Final result containing processed messages and issues (dict)
tags: []
metadata:
source_repo: https://github.com/itszhaoziyan-n/AgentKit.git
extracted_at: ''
confidence: 0.95
---
# code-review-agent-workflow
Automate the code review process using a multi-step workflow with human-in-the-loop approval.
## Setup
**Dependencies:**
```text
pip install langgraph>=0.3 langchain-core>=0.3 langchain-anthropic>=0.3 langfuse>=2.0 mcp[server]>=1.24,<2.0 langchain-mcp-adapters>=0.1 tenacity>=9.0 fastapi>=0.115 uvicorn[standard]>=0.32 psycopg[binary]>=3.1 langgraph-checkpoint-postgres>=2.0 httpx>=0.27 python-dotenv>=1.0 redis>=5.0
```
**Setup steps:**
1. cp .env.example .env
1. docker compose up -d
1. pip install -e '.[dev]'
## Key Files
- `agentkit/workflow/code_review/graph.py - Contains the `build_graph` function and graph invocation logic.`
- `examples/run_code_review.py - Example script demonstrating how to run the code review agent.`
## Steps
1. Step 1: Build the graph for the code review agent using `build_graph()` from `agentkit.workflow.code_review.graph`
2. Step 2: Invoke the graph with initial parameters including sample diff, repo context, user ID, and other metadata
3. Step 3: The graph processes the input through a series of steps, generating messages and issues as it progresses
## Implementation Details
```python
graph = build_graph()
thread_id = str(uuid.uuid4())
result = graph.invoke(...)
```
## Inputs
- Sample diff of code changes (str)
- Repo context (dict)
## Outputs
- Final result containing processed messages and issues (dict)
## Failure Modes
- Specific failure scenario with mitigation: If the `build_graph()` function fails to initialize properly, ensure all required dependencies are correctly installed.
## Source
Extracted from: [https://github.com/itszhaoziyan-n/AgentKit.git](https://github.com/itszhaoziyan-n/AgentKit.git)
Confidence: 0.95
@@ -0,0 +1,6 @@
# Commands: code-review-agent-workflow
## Available Commands
- `/skill code-review-agent-workflow` — Load this skill
- `/run code-review-agent-workflow` — Execute workflow
@@ -0,0 +1,10 @@
# Examples: code-review-agent-workflow
## Usage Example
```python
# How to use this skill
# Inputs: Sample diff of code changes (str), Repo context (dict)
# Process: Step 1: Build the graph for the code review agent using `build_graph()` from `agentkit.workflow.code_review.graph` → Step 2: Invoke the graph with initial parameters including sample diff, repo context, user ID, and other metadata → Step 3: The graph processes the input through a series of steps, generating messages and issues as it progresses
# Outputs: Final result containing processed messages and issues (dict)
```
@@ -0,0 +1,24 @@
{
"name": "code-review-agent-workflow",
"version": "1.0.0",
"goal": "Automate the code review process using a multi-step workflow with human-in-the-loop approval.",
"inputs": [
"Sample diff of code changes (str)",
"Repo context (dict)"
],
"steps": [
"Step 1: Build the graph for the code review agent using `build_graph()` from `agentkit.workflow.code_review.graph`",
"Step 2: Invoke the graph with initial parameters including sample diff, repo context, user ID, and other metadata",
"Step 3: The graph processes the input through a series of steps, generating messages and issues as it progresses"
],
"outputs": [
"Final result containing processed messages and issues (dict)"
],
"failure_modes": [
"Specific failure scenario with mitigation: If the `build_graph()` function fails to initialize properly, ensure all required dependencies are correctly installed."
],
"confidence": 0.95,
"explanation": "This workflow is reusable for any code review process that requires a multi-step analysis and human approval.",
"source_repo": "https://github.com/itszhaoziyan-n/AgentKit.git",
"score": 1.0
}
@@ -0,0 +1,9 @@
# Tests: code-review-agent-workflow
## Test Checklist
- [ ] Workflow has at least 3 steps
- [ ] All inputs are defined
- [ ] All outputs are defined
- [ ] Failure modes are documented
- [ ] Skill can be loaded without errors
@@ -0,0 +1,83 @@
---
name: langgraph-workflow-creation
version: 1.0.0
description: Create a LangGraph workflow to gather facts using SerperDevTool and process
them with an AI agent.
inputs:
- API Key for SerperDevTool
- Search Query
steps:
- 'Step 1: Import necessary modules from langgraph and langchain libraries'
- 'Step 2: Create a LangGraph agent using `langgraph.create_react_agent` function
with SerperDevTool as the tool node'
- 'Step 3: Define the search query and pass it to the agent for fact gathering'
- 'Step 4: Process the gathered facts within the AI agent'
outputs:
- Processed Facts
tags: []
metadata:
source_repo: https://github.com/jkmaina/LangGraphProjects.git
extracted_at: ''
confidence: 0.95
---
# langgraph-workflow-creation
Create a LangGraph workflow to gather facts using SerperDevTool and process them with an AI agent.
## Setup
**Dependencies:**
```text
pip install langchain serperdev
```
**Setup steps:**
1. Install required libraries: pip install langchain serperdev
1. Add API key to .env file: OPENAPI_API_KEY=your_api_key
## Key Files
- `agent.py - Contains the LangGraph agent creation logic`
- `tool_node.py - Defines the SerperDevTool node`
## Steps
1. Step 1: Import necessary modules from langgraph and langchain libraries
2. Step 2: Create a LangGraph agent using `langgraph.create_react_agent` function with SerperDevTool as the tool node
3. Step 3: Define the search query and pass it to the agent for fact gathering
4. Step 4: Process the gathered facts within the AI agent
## Implementation Details
```python
import langgraph
from serperdev import SerperDevTool
def create_agent(api_key, query):
tool = SerperDevTool(api_key)
agent = langgraph.create_react_agent(tool=tool)
facts = agent.run(query)
return process_facts(facts)
```
## Inputs
- API Key for SerperDevTool
- Search Query
## Outputs
- Processed Facts
## Failure Modes
- API Key not provided
- Invalid Search Query
## Source
Extracted from: [https://github.com/jkmaina/LangGraphProjects.git](https://github.com/jkmaina/LangGraphProjects.git)
Confidence: 0.95
@@ -0,0 +1,6 @@
# Commands: langgraph-workflow-creation
## Available Commands
- `/skill langgraph-workflow-creation` — Load this skill
- `/run langgraph-workflow-creation` — Execute workflow
@@ -0,0 +1,10 @@
# Examples: langgraph-workflow-creation
## Usage Example
```python
# How to use this skill
# Inputs: API Key for SerperDevTool, Search Query
# Process: Step 1: Import necessary modules from langgraph and langchain libraries → Step 2: Create a LangGraph agent using `langgraph.create_react_agent` function with SerperDevTool as the tool node → Step 3: Define the search query and pass it to the agent for fact gathering
# Outputs: Processed Facts
```
@@ -0,0 +1,26 @@
{
"name": "langgraph-workflow-creation",
"version": "1.0.0",
"goal": "Create a LangGraph workflow to gather facts using SerperDevTool and process them with an AI agent.",
"inputs": [
"API Key for SerperDevTool",
"Search Query"
],
"steps": [
"Step 1: Import necessary modules from langgraph and langchain libraries",
"Step 2: Create a LangGraph agent using `langgraph.create_react_agent` function with SerperDevTool as the tool node",
"Step 3: Define the search query and pass it to the agent for fact gathering",
"Step 4: Process the gathered facts within the AI agent"
],
"outputs": [
"Processed Facts"
],
"failure_modes": [
"API Key not provided",
"Invalid Search Query"
],
"confidence": 0.95,
"explanation": "This workflow is specific to fact gathering and can be adapted for different search queries or tools.",
"source_repo": "https://github.com/jkmaina/LangGraphProjects.git",
"score": 1.0
}
@@ -0,0 +1,9 @@
# Tests: langgraph-workflow-creation
## Test Checklist
- [ ] Workflow has at least 3 steps
- [ ] All inputs are defined
- [ ] All outputs are defined
- [ ] Failure modes are documented
- [ ] Skill can be loaded without errors
+98
View File
@@ -0,0 +1,98 @@
---
name: mcp-server-setup
version: 1.0.0
description: Set up an MCP server to integrate PipesHub with any MCP-compatible client.
inputs:
- MCP server configuration details
- PipesHub credentials
steps:
- 'Step 1: Clone the `pipeshub-ai/mcp-server` repository using `git clone https://github.com/pipeshub-ai/mcp-server.git`'
- 'Step 2: Navigate to the cloned directory with `cd mcp-server`'
- 'Step 3: Run the interactive installer by executing `./install.sh`'
- 'Step 4: Follow the prompts in the installer to configure the server, including
setting up graph DB, message broker, and KV store'
- 'Step 5: The installer will generate a `.env` file with necessary environment variables.
Ensure these are correctly set'
- 'Step 6: Start the MCP server by running `docker-compose up -d`'
outputs:
- Running MCP server
- .env file generated
tags: []
metadata:
source_repo: https://github.com/pipeshub-ai/pipeshub-ai.git
extracted_at: ''
confidence: 0.95
---
# mcp-server-setup
Set up an MCP server to integrate PipesHub with any MCP-compatible client.
## Setup
**Dependencies:**
```text
pip install docker docker-compose
```
**Setup steps:**
1. Ensure Docker and Docker Compose are installed on your system.
1. Clone the `pipeshub-ai/mcp-server` repository using `git clone https://github.com/pipeshub-ai/mcp-server.git`
## Key Files
- `path/to/install.sh - Script to run the interactive installer`
- `path/to/docker-compose.yml - Configuration for Docker services`
## Steps
1. Step 1: Clone the `pipeshub-ai/mcp-server` repository using `git clone https://github.com/pipeshub-ai/mcp-server.git`
2. Step 2: Navigate to the cloned directory with `cd mcp-server`
3. Step 3: Run the interactive installer by executing `./install.sh`
4. Step 4: Follow the prompts in the installer to configure the server, including setting up graph DB, message broker, and KV store
5. Step 5: The installer will generate a `.env` file with necessary environment variables. Ensure these are correctly set
6. Step 6: Start the MCP server by running `docker-compose up -d`
## Implementation Details
```python
```bash
./install.sh
```
Run this script to start the installation process.
```
```python
```yaml
docker-compose:
version: '3.9'
services:
mcp-server:
image: pipeshubai/mcp-server:latest
environment:
- PIPESHUB_API_KEY=your_api_key_here
```
This snippet shows how to configure the Docker Compose file.
```
## Inputs
- MCP server configuration details
- PipesHub credentials
## Outputs
- Running MCP server
- .env file generated
## Failure Modes
- Installer fails to run due to missing dependencies or incorrect configuration
- Docker Compose setup issues preventing server from starting
## Source
Extracted from: [https://github.com/pipeshub-ai/pipeshub-ai.git](https://github.com/pipeshub-ai/pipeshub-ai.git)
Confidence: 0.95
+6
View File
@@ -0,0 +1,6 @@
# Commands: mcp-server-setup
## Available Commands
- `/skill mcp-server-setup` — Load this skill
- `/run mcp-server-setup` — Execute workflow
+10
View File
@@ -0,0 +1,10 @@
# Examples: mcp-server-setup
## Usage Example
```python
# How to use this skill
# Inputs: MCP server configuration details, PipesHub credentials
# Process: Step 1: Clone the `pipeshub-ai/mcp-server` repository using `git clone https://github.com/pipeshub-ai/mcp-server.git` → Step 2: Navigate to the cloned directory with `cd mcp-server` → Step 3: Run the interactive installer by executing `./install.sh`
# Outputs: Running MCP server, .env file generated
```
+29
View File
@@ -0,0 +1,29 @@
{
"name": "mcp-server-setup",
"version": "1.0.0",
"goal": "Set up an MCP server to integrate PipesHub with any MCP-compatible client.",
"inputs": [
"MCP server configuration details",
"PipesHub credentials"
],
"steps": [
"Step 1: Clone the `pipeshub-ai/mcp-server` repository using `git clone https://github.com/pipeshub-ai/mcp-server.git`",
"Step 2: Navigate to the cloned directory with `cd mcp-server`",
"Step 3: Run the interactive installer by executing `./install.sh`",
"Step 4: Follow the prompts in the installer to configure the server, including setting up graph DB, message broker, and KV store",
"Step 5: The installer will generate a `.env` file with necessary environment variables. Ensure these are correctly set",
"Step 6: Start the MCP server by running `docker-compose up -d`"
],
"outputs": [
"Running MCP server",
".env file generated"
],
"failure_modes": [
"Installer fails to run due to missing dependencies or incorrect configuration",
"Docker Compose setup issues preventing server from starting"
],
"confidence": 0.95,
"explanation": "This workflow is specific but can be adapted for different deployment environments and configurations.",
"source_repo": "https://github.com/pipeshub-ai/pipeshub-ai.git",
"score": 1.0
}
+9
View File
@@ -0,0 +1,9 @@
# Tests: mcp-server-setup
## Test Checklist
- [ ] Workflow has at least 3 steps
- [ ] All inputs are defined
- [ ] All outputs are defined
- [ ] Failure modes are documented
- [ ] Skill can be loaded without errors
@@ -0,0 +1,84 @@
---
name: multi-agent-sequential-workflow
version: 1.0.0
description: Gather and process information from multiple agents to generate a comprehensive
travel guide.
inputs:
- User query with location
steps:
- 'Step 1: Researcher agent (agent.py) uses LangGraph create_react_agent with BedrockModel
to gather raw facts about the destination.'
- 'Step 2: Travel Guide Generator agent (agent.py) synthesizes the gathered data into
a structured travel guide based on the user''s request and raw information provided
by the researcher.'
- 'Step 3: Writer agent (agent.py) formats the final response, including the structured
guide content and prominently features the ''Suggested Web Pages'' section.'
outputs:
- Structured travel guide with key sections
- Final client response
tags: []
metadata:
source_repo: https://github.com/omerbsezer/Fast-LLM-Agent-MCP.git
extracted_at: ''
confidence: 0.95
---
# multi-agent-sequential-workflow
Gather and process information from multiple agents to generate a comprehensive travel guide.
## Setup
**Dependencies:**
```text
pip install python3 fastapi uvicorn strands bedrock-model
```
**Setup steps:**
1. Install required dependencies using pip
1. Set up environment variables for API keys and model IDs
## Key Files
- `agents/aws_strands/05-agent-strands-multiagent-workflow-sequential/agent.py - Contains the multi-agent workflow logic.`
- `agents/aws_strands/05-agent-strands-multiagent-workflow-sequential/app.py - FastAPI app to handle user queries.`
## Steps
1. Step 1: Researcher agent (agent.py) uses LangGraph create_react_agent with BedrockModel to gather raw facts about the destination.
2. Step 2: Travel Guide Generator agent (agent.py) synthesizes the gathered data into a structured travel guide based on the user's request and raw information provided by the researcher.
3. Step 3: Writer agent (agent.py) formats the final response, including the structured guide content and prominently features the 'Suggested Web Pages' section.
## Implementation Details
```python
research_output = researcher_agent(query, stream=False)
```
```python
guide_output = travel_guide_agent(planner_prompt, stream=False)
```
```python
final_response = writer_agent(writer_prompt, stream=False)
```
## Inputs
- User query with location
## Outputs
- Structured travel guide with key sections
- Final client response
## Failure Modes
- Network issues during API calls could lead to incomplete data collection or processing failures
## Source
Extracted from: [https://github.com/omerbsezer/Fast-LLM-Agent-MCP.git](https://github.com/omerbsezer/Fast-LLM-Agent-MCP.git)
Confidence: 0.95
@@ -0,0 +1,6 @@
# Commands: multi-agent-sequential-workflow
## Available Commands
- `/skill multi-agent-sequential-workflow` — Load this skill
- `/run multi-agent-sequential-workflow` — Execute workflow
@@ -0,0 +1,10 @@
# Examples: multi-agent-sequential-workflow
## Usage Example
```python
# How to use this skill
# Inputs: User query with location
# Process: Step 1: Researcher agent (agent.py) uses LangGraph create_react_agent with BedrockModel to gather raw facts about the destination. → Step 2: Travel Guide Generator agent (agent.py) synthesizes the gathered data into a structured travel guide based on the user's request and raw information provided by the researcher. → Step 3: Writer agent (agent.py) formats the final response, including the structured guide content and prominently features the 'Suggested Web Pages' section.
# Outputs: Structured travel guide with key sections, Final client response
```
@@ -0,0 +1,24 @@
{
"name": "multi-agent-sequential-workflow",
"version": "1.0.0",
"goal": "Gather and process information from multiple agents to generate a comprehensive travel guide.",
"inputs": [
"User query with location"
],
"steps": [
"Step 1: Researcher agent (agent.py) uses LangGraph create_react_agent with BedrockModel to gather raw facts about the destination.",
"Step 2: Travel Guide Generator agent (agent.py) synthesizes the gathered data into a structured travel guide based on the user's request and raw information provided by the researcher.",
"Step 3: Writer agent (agent.py) formats the final response, including the structured guide content and prominently features the 'Suggested Web Pages' section."
],
"outputs": [
"Structured travel guide with key sections",
"Final client response"
],
"failure_modes": [
"Network issues during API calls could lead to incomplete data collection or processing failures"
],
"confidence": 0.95,
"explanation": "This workflow is specific but can be adapted for other types of guides or information gathering tasks.",
"source_repo": "https://github.com/omerbsezer/Fast-LLM-Agent-MCP.git",
"score": 1.0
}
@@ -0,0 +1,9 @@
# Tests: multi-agent-sequential-workflow
## Test Checklist
- [ ] Workflow has at least 3 steps
- [ ] All inputs are defined
- [ ] All outputs are defined
- [ ] Failure modes are documented
- [ ] Skill can be loaded without errors
+90
View File
@@ -0,0 +1,90 @@
---
name: research-pipeline
version: 1.0.0
description: Fetch a Wikipedia page, summarise its content using an AI agent, and
write the summary to a file.
inputs:
- URL of the Wikipedia page
steps:
- 'Step 1: Import necessary modules from `_bootstrap` and `blacknode`: Run `from _bootstrap
import NIM_MODEL, require_nim_api_key; import blacknode as bn`'
- 'Step 2: Require NVIDIA NIM API key: Call `require_nim_api_key()` to ensure the
API key is set.'
- 'Step 3: Create a graph instance: Initialize `g = bn.Graph()`.'
- 'Step 4: Add nodes for URL, HTTPGet, summarisation, and file writing: `url = g.node(''Literal'',
value=''URL of the Wikipedia page''); fetcher = g.node(''HTTPGet''); summarise =
g.node(''LLMAgent'', system=''You are a technical writer. Summarise the text in
3 bullet points.'', model=NIM_MODEL); writer = g.node(''FileWrite'', path=''summary.txt'')`'
- 'Step 5: Connect nodes with edges: `url.out(''value'') >> fetcher.inp(''url'');
fetcher.out(''text'') >> summarise.inp(''prompt''); summarise.out(''text'') >> writer.inp(''text'')`'
- 'Step 6: Cook the graph to execute and get output: `result = g.cook(writer, ''path'');
print(f''Summary written to: {result}'')`'
outputs:
- Path of the summary file
tags: []
metadata:
source_repo: https://github.com/temiroff/Blacknode.git
extracted_at: ''
confidence: 0.95
---
# research-pipeline
Fetch a Wikipedia page, summarise its content using an AI agent, and write the summary to a file.
## Setup
**Dependencies:**
```text
pip install anthropic>=0.25 docker>=7.1 openai>=1.0
```
**Setup steps:**
1. Ensure NVIDIA NIM API key is set in the environment or editor
1. Install required dependencies: `pip install -r requirements.txt`
## Key Files
- `examples/research_pipeline.py - Contains the research pipeline workflow`
## Steps
1. Step 1: Import necessary modules from `_bootstrap` and `blacknode`: Run `from _bootstrap import NIM_MODEL, require_nim_api_key; import blacknode as bn`
2. Step 2: Require NVIDIA NIM API key: Call `require_nim_api_key()` to ensure the API key is set.
3. Step 3: Create a graph instance: Initialize `g = bn.Graph()`.
4. Step 4: Add nodes for URL, HTTPGet, summarisation, and file writing: `url = g.node('Literal', value='URL of the Wikipedia page'); fetcher = g.node('HTTPGet'); summarise = g.node('LLMAgent', system='You are a technical writer. Summarise the text in 3 bullet points.', model=NIM_MODEL); writer = g.node('FileWrite', path='summary.txt')`
5. Step 5: Connect nodes with edges: `url.out('value') >> fetcher.inp('url'); fetcher.out('text') >> summarise.inp('prompt'); summarise.out('text') >> writer.inp('text')`
6. Step 6: Cook the graph to execute and get output: `result = g.cook(writer, 'path'); print(f'Summary written to: {result}')`
## Implementation Details
```python
from _bootstrap import NIM_MODEL, require_nim_api_key; import blacknode as bn
```
```python
url = g.node('Literal', value='https://en.wikipedia.org/w/api.php?action=query&prop=extracts&exintro=1&explaintext=1&titles=Houdini_(software)&format=json&formatversion=2&origin=*'); fetcher = g.node('HTTPGet'); summarise = g.node('LLMAgent', system='You are a technical writer. Summarise the text in 3 bullet points.', model=NIM_MODEL); writer = g.node('FileWrite', path='summary.txt')
```
```python
url.out('value') >> fetcher.inp('url'); fetcher.out('text') >> summarise.inp('prompt'); summarise.out('text') >> writer.inp('text')
```
## Inputs
- URL of the Wikipedia page
## Outputs
- Path of the summary file
## Failure Modes
- If the URL is invalid or unreachable, the HTTPGet node will fail; if the summarisation fails, the output text might be empty
## Source
Extracted from: [https://github.com/temiroff/Blacknode.git](https://github.com/temiroff/Blacknode.git)
Confidence: 0.95
+6
View File
@@ -0,0 +1,6 @@
# Commands: research-pipeline
## Available Commands
- `/skill research-pipeline` — Load this skill
- `/run research-pipeline` — Execute workflow
+10
View File
@@ -0,0 +1,10 @@
# Examples: research-pipeline
## Usage Example
```python
# How to use this skill
# Inputs: URL of the Wikipedia page
# Process: Step 1: Import necessary modules from `_bootstrap` and `blacknode`: Run `from _bootstrap import NIM_MODEL, require_nim_api_key; import blacknode as bn` → Step 2: Require NVIDIA NIM API key: Call `require_nim_api_key()` to ensure the API key is set. → Step 3: Create a graph instance: Initialize `g = bn.Graph()`.
# Outputs: Path of the summary file
```
+26
View File
@@ -0,0 +1,26 @@
{
"name": "research-pipeline",
"version": "1.0.0",
"goal": "Fetch a Wikipedia page, summarise its content using an AI agent, and write the summary to a file.",
"inputs": [
"URL of the Wikipedia page"
],
"steps": [
"Step 1: Import necessary modules from `_bootstrap` and `blacknode`: Run `from _bootstrap import NIM_MODEL, require_nim_api_key; import blacknode as bn`",
"Step 2: Require NVIDIA NIM API key: Call `require_nim_api_key()` to ensure the API key is set.",
"Step 3: Create a graph instance: Initialize `g = bn.Graph()`.",
"Step 4: Add nodes for URL, HTTPGet, summarisation, and file writing: `url = g.node('Literal', value='URL of the Wikipedia page'); fetcher = g.node('HTTPGet'); summarise = g.node('LLMAgent', system='You are a technical writer. Summarise the text in 3 bullet points.', model=NIM_MODEL); writer = g.node('FileWrite', path='summary.txt')`",
"Step 5: Connect nodes with edges: `url.out('value') >> fetcher.inp('url'); fetcher.out('text') >> summarise.inp('prompt'); summarise.out('text') >> writer.inp('text')`",
"Step 6: Cook the graph to execute and get output: `result = g.cook(writer, 'path'); print(f'Summary written to: {result}')`"
],
"outputs": [
"Path of the summary file"
],
"failure_modes": [
"If the URL is invalid or unreachable, the HTTPGet node will fail; if the summarisation fails, the output text might be empty"
],
"confidence": 0.95,
"explanation": "This workflow can be adapted to fetch and summarise any Wikipedia page or similar content source.",
"source_repo": "https://github.com/temiroff/Blacknode.git",
"score": 1.0
}
+9
View File
@@ -0,0 +1,9 @@
# Tests: research-pipeline
## Test Checklist
- [ ] Workflow has at least 3 steps
- [ ] All inputs are defined
- [ ] All outputs are defined
- [ ] Failure modes are documented
- [ ] Skill can be loaded without errors
+96
View File
@@ -0,0 +1,96 @@
---
name: unifai-workflow-execution
version: 1.0.0
description: Execute a multi-agent workflow on the UnifAI platform using a specified
blueprint and user prompt.
inputs:
- blueprint_id or blueprint_name
- user_shortcut
- user_question
steps:
- 'Step 1: Resolve the blueprint ID from either direct ID or name lookup (resolve_blueprint_id
method)'
- 'Step 2: Create a new session from the blueprint (create_session method)'
- 'Step 3: Submit the session for background execution with the user prompt (submit_session
method)'
- 'Step 4: Poll session status until execution completes (poll_session_status method)'
outputs:
- session_id
- workflow_id
tags: []
metadata:
source_repo: https://github.com/redhat-community-ai-tools/UnifAI.git
extracted_at: ''
confidence: 0.95
---
# unifai-workflow-execution
Execute a multi-agent workflow on the UnifAI platform using a specified blueprint and user prompt.
## Setup
**Dependencies:**
```text
pip install requests urllib3
```
**Setup steps:**
1. Install required dependencies using pip install requests urllib3
1. Ensure the environment variables are set correctly (BLUEPRINT_ID, BLUEPRINT_NAME, USER_SHORTCUT, POLLING_INTERVAL, UNIFAI_BASE_URL)
## Key Files
- `scripts/execution_workflow.py - Main script for workflow execution`
## Steps
1. Step 1: Resolve the blueprint ID from either direct ID or name lookup (resolve_blueprint_id method)
2. Step 2: Create a new session from the blueprint (create_session method)
3. Step 3: Submit the session for background execution with the user prompt (submit_session method)
4. Step 4: Poll session status until execution completes (poll_session_status method)
## Implementation Details
```python
resolve_blueprint_id(client: UnifAIClient) -> str
{...}
# Resolve the blueprint ID from either direct ID or name lookup.
```
```python
create_session(client: UnifAIClient, blueprint_id: str) -> str
{...}
# Create a new session from the blueprint.
```
```python
submit_session(client: UnifAIClient, session_id: str) -> dict
{...}
# Submit the session for background execution with the user prompt.
```
## Inputs
- blueprint_id or blueprint_name
- user_shortcut
- user_question
## Outputs
- session_id
- workflow_id
## Failure Modes
- Blueprint name not found or not unique - error during blueprint resolution
- Session creation fails - error from API response
- Session submission fails - error from API response
- Polling session status fails - error from API response
## Source
Extracted from: [https://github.com/redhat-community-ai-tools/UnifAI.git](https://github.com/redhat-community-ai-tools/UnifAI.git)
Confidence: 0.95
@@ -0,0 +1,6 @@
# Commands: unifai-workflow-execution
## Available Commands
- `/skill unifai-workflow-execution` — Load this skill
- `/run unifai-workflow-execution` — Execute workflow
@@ -0,0 +1,10 @@
# Examples: unifai-workflow-execution
## Usage Example
```python
# How to use this skill
# Inputs: blueprint_id or blueprint_name, user_shortcut, user_question
# Process: Step 1: Resolve the blueprint ID from either direct ID or name lookup (resolve_blueprint_id method) → Step 2: Create a new session from the blueprint (create_session method) → Step 3: Submit the session for background execution with the user prompt (submit_session method)
# Outputs: session_id, workflow_id
```
@@ -0,0 +1,30 @@
{
"name": "unifai-workflow-execution",
"version": "1.0.0",
"goal": "Execute a multi-agent workflow on the UnifAI platform using a specified blueprint and user prompt.",
"inputs": [
"blueprint_id or blueprint_name",
"user_shortcut",
"user_question"
],
"steps": [
"Step 1: Resolve the blueprint ID from either direct ID or name lookup (resolve_blueprint_id method)",
"Step 2: Create a new session from the blueprint (create_session method)",
"Step 3: Submit the session for background execution with the user prompt (submit_session method)",
"Step 4: Poll session status until execution completes (poll_session_status method)"
],
"outputs": [
"session_id",
"workflow_id"
],
"failure_modes": [
"Blueprint name not found or not unique - error during blueprint resolution",
"Session creation fails - error from API response",
"Session submission fails - error from API response",
"Polling session status fails - error from API response"
],
"confidence": 0.95,
"explanation": "This workflow is specific to the UnifAI platform and its multi-agent system, but can be adapted for similar systems with a similar architecture.",
"source_repo": "https://github.com/redhat-community-ai-tools/UnifAI.git",
"score": 1.0
}
@@ -0,0 +1,9 @@
# Tests: unifai-workflow-execution
## Test Checklist
- [ ] Workflow has at least 3 steps
- [ ] All inputs are defined
- [ ] All outputs are defined
- [ ] Failure modes are documented
- [ ] Skill can be loaded without errors