+ Two paths to bring your existing agents into OpenGAP format.
+
+
+
+
+
+
+ 1 — File System Agents
+
+
+ Agents that live as config files (Claude Code, Cursor, Gemini CLI, Codex, OpenCode).
+ opengap import reads their files directly and outputs OpenGAP format.
+
+
+
+
+
+ 2 — Code-Based Frameworks
+
+
+ Agents defined in code (LangGraph, CrewAI, AutoGen, etc.).
+ Follow the cookbook to manually map your framework's concepts to OpenGAP files.
+
+
+
+
+
+ {/* Path 1 */}
+
+
+
+
1 — File System Agents
+
+
+ {fsAgents.map((e, i) => )}
+
+
+
+ {/* Path 2 */}
+
+
+
+
2 — Code-Based Frameworks
+
+
+ {codeFrameworks.map((e, i) => )}
+
+
+
+
+
+ );
+}
diff --git a/src/components/opengap/OpenGAPSidebar.tsx b/src/components/opengap/OpenGAPSidebar.tsx
index a9408fa..1ec6844 100644
--- a/src/components/opengap/OpenGAPSidebar.tsx
+++ b/src/components/opengap/OpenGAPSidebar.tsx
@@ -20,6 +20,7 @@ export const opengapSidebarGroups = [
slug: "features",
items: [
{ id: "cli", label: "CLI" },
+ { id: "import", label: "Import" },
{ id: "export", label: "Export" },
{ id: "adapters", label: "Adapters" },
],
diff --git a/src/components/opengap/cookbook/CookbookAutoGen.tsx b/src/components/opengap/cookbook/CookbookAutoGen.tsx
new file mode 100644
index 0000000..f6884db
--- /dev/null
+++ b/src/components/opengap/cookbook/CookbookAutoGen.tsx
@@ -0,0 +1,247 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const projectStructure = `autogen-agentchat two-agent reflection pattern
+├── primary_agent ← AssistantAgent: drafts and refines responses
+└── critic_agent ← AssistantAgent: provides feedback until satisfied`;
+
+const agentPy = `# Based on microsoft/autogen AgentChat teams tutorial
+import asyncio
+from autogen_agentchat.agents import AssistantAgent
+from autogen_agentchat.conditions import TextMentionTermination
+from autogen_agentchat.teams import RoundRobinGroupChat
+from autogen_agentchat.ui import Console
+from autogen_ext.models.openai import OpenAIChatCompletionClient
+
+# Define a tool for the primary agent
+async def web_search(query: str) -> str:
+ """Find information on the web about a given topic."""
+ # real impl calls a search API
+ return f"Search results for: {query}"
+
+model_client = OpenAIChatCompletionClient(
+ model="gpt-4o-2024-08-06",
+)
+
+# Primary agent drafts responses using tools
+primary_agent = AssistantAgent(
+ name="primary",
+ model_client=model_client,
+ tools=[web_search],
+ system_message="""You are a helpful research assistant.
+Search the web to find accurate, up-to-date information before answering.
+Cite your sources. Revise your response based on the critic's feedback.""",
+)
+
+# Critic agent reviews and provides feedback
+critic_agent = AssistantAgent(
+ name="critic",
+ model_client=model_client,
+ system_message="""You are a constructive critic. Review the primary agent's response for:
+- Factual accuracy and source quality
+- Completeness and clarity
+- Any missing context or caveats
+
+Respond with 'APPROVE' once the response meets the quality bar.""",
+)
+
+# Stop when critic approves
+termination = TextMentionTermination("APPROVE")
+team = RoundRobinGroupChat(
+ [primary_agent, critic_agent],
+ termination_condition=termination,
+)
+
+async def main():
+ await Console(team.run_stream(
+ task="What are the key differences between LangGraph and AutoGen?"
+ ))
+
+asyncio.run(main())`;
+
+const agentYaml = `spec_version: 0.1.0
+name: primary
+version: 0.1.0
+description: Research assistant that searches the web and refines responses based on feedback
+model:
+ preferred: gpt-4o-2024-08-06
+tools:
+ - web-search
+agents:
+ critic:
+ description: Reviews research responses for accuracy, completeness, and clarity
+ delegation:
+ mode: auto`;
+
+const soulMd = `# Soul
+
+## Core Identity
+You are a helpful research assistant.
+
+## Purpose
+Find accurate, up-to-date information by searching the web before answering.
+Revise your response based on the critic's feedback.`;
+
+const rulesMd = `# Rules
+
+- Always search before answering factual questions
+- Always cite sources
+- Never fabricate URLs or citations`;
+
+const subCriticYaml = `spec_version: 0.1.0
+name: critic
+version: 0.1.0
+description: Reviews research responses for accuracy, completeness, and clarity
+model:
+ preferred: gpt-4o-2024-08-06`;
+
+const subCriticSoul = `# Soul
+
+## Core Identity
+You are a constructive critic reviewing research responses.
+
+## Review Criteria
+- Factual accuracy and source quality
+- Completeness and clarity
+- Missing context or caveats
+
+## Behavior
+Respond with 'APPROVE' once the response meets the quality bar.`;
+
+const toolWebSearch = `name: web-search
+description: Find information on the web about a given topic.
+input_schema:
+ type: object
+ properties:
+ query:
+ type: string
+ description: The search topic or question
+ required:
+ - query
+implementation:
+ type: script
+ path: tools/web_search.py
+ runtime: python3
+ timeout: 30`;
+
+const validateCmd = `opengap validate -d ./primary-agent-opengap
+opengap info -d ./primary-agent-opengap`;
+
+const mapping = [
+ ["AssistantAgent.system_message", "SOUL.md (split into identity vs rules by content)"],
+ ["AssistantAgent.name (kebab-case)", "agent.yaml → name"],
+ ["model_client model string", "agent.yaml → model.preferred"],
+ ["AssistantAgent.tools=[web_search] (function names)", "agent.yaml → tools[] + tools/.yaml"],
+ ["Each tool function docstring + typed args", "tools/.yaml description + input_schema"],
+ ["Second agent (critic_agent)", "agents/critic/agent.yaml + SOUL.md"],
+ ["RoundRobinGroupChat / team setup", "stays in framework — runtime orchestration"],
+ ["TextMentionTermination condition", "stays in framework — loop control"],
+];
+
+const steps = [
+ { step: "1", desc: "Take system_message from the primary AssistantAgent → SOUL.md. Split behavioral rules (search before answering, cite sources) from hard constraints (never fabricate) into RULES.md." },
+ { step: "2", desc: "Take the model string from OpenAIChatCompletionClient(model=...) → write to agent.yaml → model.preferred." },
+ { step: "3", desc: "For each function in AssistantAgent.tools=[], add a kebab-case name to agent.yaml → tools (web_search → web-search)." },
+ { step: "4", desc: "Create tools/.yaml for each tool — use the function docstring as description, typed parameters as input_schema." },
+ { step: "5", desc: "For the critic agent, create agents/critic/agent.yaml and SOUL.md using the same mapping. No tools needed since critic has no tools." },
+ { step: "6", desc: "RoundRobinGroupChat, TextMentionTermination, and team.run_stream() stay in the Python file — they are runtime orchestration with no OpenGAP equivalent." },
+];
+
+export function CookbookAutoGen() {
+ return (
+
+
+
+
+
OpenGAP / Cookbook /
+
AutoGen → OpenGAP
+
+ Based on microsoft/autogen AgentChat — a two-agent reflection pattern
+ with a primary research agent and a critic agent in a RoundRobinGroupChat.
+ Each AssistantAgent maps to its own OpenGAP directory;
+ the team setup and termination condition stay in the framework.
+
+
+
+ {/* Part 1 */}
+
+
Part 1 — The AutoGen project
+
Two-agent reflection: primary agent with web search + critic agent for quality control:
+
+
+
+
+
+
+ {/* Part 2 */}
+
+
Part 2 — What maps to OpenGAP
+
+
+ AutoGenOpenGAP
+
+ {mapping.map(([from, to], i) => (
+
+ {from}
+ {to}
+
+ ))}
+
+
+
+ {/* Part 3 */}
+
+
Part 3 — Create the OpenGAP files
+
+
+
Primary agent: agent.yaml:
+
+
+
+
Primary agent: SOUL.md — identity from system_message:
+
+
+
+
Primary agent: RULES.md — hard constraints from system_message:
+
+
+
+
tools/web-search.yaml:
+
+
+
+
Critic: agents/critic/agent.yaml:
+
+
+
+
Critic: agents/critic/SOUL.md:
+
+
+
+
+
+ {/* Part 4 */}
+
+
Part 4 — Validate
+
+
+
+ {/* Steps */}
+
+
What happens step by step
+
+ {steps.map((s, i) => (
+
+
+ {s.step}
+
{s.desc}
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/src/components/opengap/cookbook/CookbookClaudeCode.tsx b/src/components/opengap/cookbook/CookbookClaudeCode.tsx
new file mode 100644
index 0000000..268bd18
--- /dev/null
+++ b/src/components/opengap/cookbook/CookbookClaudeCode.tsx
@@ -0,0 +1,133 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const projectStructure = `my-project/
+├── CLAUDE.md ← identity, purpose, and rules
+├── memory/
+│ └── preferences.md ← persistent memory
+└── .claude/
+ └── settings.json ← model and tool config`;
+
+const claudeMd = `# CLAUDE.md (example)
+You are a senior TypeScript engineer. Help the user write clean,
+well-tested code using Vitest and pnpm.
+
+Always write tests before implementation (TDD).
+Never push directly to main — always use a feature branch.
+Prefer composition over inheritance.`;
+
+const agentYaml = `spec_version: 0.1.0
+name: my-engineer-agent
+version: 0.1.0
+description: Senior TypeScript engineer assistant
+model:
+ preferred: claude-sonnet-4-6`;
+
+const soulMd = `# Soul
+
+## Core Identity
+You are a senior TypeScript engineer.
+
+## Purpose
+Help the user write clean, well-tested code using Vitest and pnpm.`;
+
+const rulesMd = `# Rules
+
+- Always write tests before implementation (TDD)
+- Never push directly to main — always use a feature branch
+- Prefer composition over inheritance`;
+
+const skillMd = `# TDD Skill
+
+## What this skill does
+Enforces test-driven development — write the test first,
+then the implementation.
+
+## Steps
+1. Write a failing test for the desired behavior
+2. Write the minimum code to make it pass
+3. Refactor without breaking the test`;
+
+const mapping = [
+ ["CLAUDE.md — identity paragraph", "SOUL.md → Core Identity + Purpose"],
+ ["CLAUDE.md — behavioral rules", "RULES.md"],
+ ["CLAUDE.md — recurring workflows", "skills//SKILL.md"],
+ ["memory/preferences.md", "agent context (not imported — runtime state)"],
+ [".claude/settings.json → model", "agent.yaml → model.preferred"],
+];
+
+const steps = [
+ { step: "1", desc: "Open CLAUDE.md. Copy the identity paragraph (who you are, what you do) into SOUL.md under Core Identity and Purpose." },
+ { step: "2", desc: "Extract behavioral rules (\"always\", \"never\", \"prefer\") from CLAUDE.md into RULES.md as a bullet list." },
+ { step: "3", desc: "If CLAUDE.md describes a recurring workflow (TDD, PR review, etc.), create a skills//SKILL.md for it." },
+ { step: "4", desc: "Read .claude/settings.json for the model name and write it to agent.yaml → model.preferred." },
+ { step: "5", desc: "memory/ files are runtime state — they don't map to OpenGAP files. Leave them in place." },
+];
+
+export function CookbookClaudeCode() {
+ return (
+
+
+
+
+
OpenGAP / Cookbook /
+
Claude Code → OpenGAP
+
+ Claude Code stores agent identity in CLAUDE.md and runtime memory in memory/.
+ Converting to OpenGAP means splitting CLAUDE.md into SOUL.md (identity),
+ RULES.md (behavior), and optionally skills/ for recurring workflows.
+
+
+
+
+
Part 1 — The Claude Code project
+
Typical Claude Code workspace structure:
+
+
+
+
+
+
+
+
Part 2 — What maps to OpenGAP
+
+
+ Claude CodeOpenGAP
+
+ {mapping.map(([from, to], i) => (
+
+ {from}
+ {to}
+
+ ))}
+
+
+
+
+
Part 3 — Create the OpenGAP files
+
+
agent.yaml:
+
SOUL.md:
+
RULES.md:
+
skills/tdd/SKILL.md:
+
+
+
+
+
What happens step by step
+
+ {steps.map((s, i) => (
+
+
+ {s.step}
+
{s.desc}
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/src/components/opengap/cookbook/CookbookClaudeSDK.tsx b/src/components/opengap/cookbook/CookbookClaudeSDK.tsx
new file mode 100644
index 0000000..18f3969
--- /dev/null
+++ b/src/components/opengap/cookbook/CookbookClaudeSDK.tsx
@@ -0,0 +1,273 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const projectStructure = `anthropics/anthropic-cookbook/
+└── tool_use/
+ └── customer_service_agent.ipynb ← tools, system prompt, agent loop`;
+
+const agentPy = `# customer_service_agent.ipynb — key code cells
+import anthropic
+
+client = anthropic.Client()
+MODEL_NAME = "claude-opus-4-1"
+
+tools = [
+ {
+ "name": "get_customer_info",
+ "description": "Retrieves customer information based on their customer ID.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "customer_id": {
+ "type": "string",
+ "description": "The unique identifier for the customer.",
+ }
+ },
+ "required": ["customer_id"],
+ },
+ },
+ {
+ "name": "get_order_details",
+ "description": "Retrieves the details of a specific order based on the order ID.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "order_id": {
+ "type": "string",
+ "description": "The unique identifier for the order.",
+ }
+ },
+ "required": ["order_id"],
+ },
+ },
+ {
+ "name": "cancel_order",
+ "description": "Cancels an order based on the provided order ID.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "order_id": {
+ "type": "string",
+ "description": "The unique identifier for the order to be cancelled.",
+ }
+ },
+ "required": ["order_id"],
+ },
+ },
+]
+
+SYSTEM_PROMPT = """You are a customer service agent for an e-commerce platform.
+Your role is to assist customers with their inquiries and issues.
+You have access to tools to look up customer and order information,
+and to process cancellations. Always be polite and helpful."""
+
+def chatbot_interaction(user_message: str) -> str:
+ messages = [{"role": "user", "content": user_message}]
+ response = client.messages.create(
+ model=MODEL_NAME, max_tokens=4096, system=SYSTEM_PROMPT,
+ tools=tools, messages=messages,
+ )
+ while response.stop_reason == "tool_use":
+ tool_use = next(b for b in response.content if b.type == "tool_use")
+ tool_result = process_tool_call(tool_use.name, tool_use.input)
+ messages = [
+ {"role": "user", "content": user_message},
+ {"role": "assistant", "content": response.content},
+ {"role": "user", "content": [
+ {"type": "tool_result", "tool_use_id": tool_use.id, "content": str(tool_result)}
+ ]},
+ ]
+ response = client.messages.create(
+ model=MODEL_NAME, max_tokens=4096, system=SYSTEM_PROMPT,
+ tools=tools, messages=messages,
+ )
+ return next(b.text for b in response.content if hasattr(b, "text"))`;
+
+const agentYaml = `spec_version: 0.1.0
+name: customer-service-agent
+version: 0.1.0
+description: Customer service agent for an e-commerce platform
+model:
+ preferred: claude-opus-4-1
+tools:
+ - get-customer-info
+ - get-order-details
+ - cancel-order`;
+
+const soulMd = `# Soul
+
+## Core Identity
+You are a customer service agent for an e-commerce platform.
+
+## Purpose
+Assist customers with their inquiries and issues. Look up customer and
+order information, and process cancellations when requested.
+
+## Behavior
+- Always be polite and helpful
+- Use tools to look up accurate information before responding
+- Confirm order details before processing a cancellation`;
+
+const toolGetCustomer = `name: get-customer-info
+description: Retrieves customer information based on their customer ID.
+input_schema:
+ type: object
+ properties:
+ customer_id:
+ type: string
+ description: The unique identifier for the customer
+ required:
+ - customer_id
+implementation:
+ type: script
+ path: tools/get_customer_info.py
+ runtime: python3
+ timeout: 30`;
+
+const toolGetOrder = `name: get-order-details
+description: Retrieves the details of a specific order based on the order ID.
+input_schema:
+ type: object
+ properties:
+ order_id:
+ type: string
+ description: The unique identifier for the order
+ required:
+ - order_id
+implementation:
+ type: script
+ path: tools/get_order_details.py
+ runtime: python3
+ timeout: 30`;
+
+const toolCancelOrder = `name: cancel-order
+description: Cancels an order based on the provided order ID.
+input_schema:
+ type: object
+ properties:
+ order_id:
+ type: string
+ description: The unique identifier for the order to be cancelled
+ required:
+ - order_id
+implementation:
+ type: script
+ path: tools/cancel_order.py
+ runtime: python3
+ timeout: 30`;
+
+const validateCmd = `opengap validate -d ./customer-service-opengap
+opengap info -d ./customer-service-opengap`;
+
+const mapping = [
+ ["SYSTEM_PROMPT passed to messages.create(system=...)", "SOUL.md"],
+ ["MODEL_NAME ('claude-opus-4-1')", "agent.yaml → model.preferred"],
+ ["tools[].name (kebab-case)", "agent.yaml → tools[]"],
+ ["tools[].description + input_schema", "tools/.yaml"],
+ ["process_tool_call() dispatch function", "stays in framework — tool implementation"],
+ ["chatbot_interaction() loop / multi-turn logic", "stays in framework — runtime loop"],
+];
+
+const steps = [
+ { step: "1", desc: "Copy SYSTEM_PROMPT into SOUL.md. Keep identity, purpose, and behavioral rules together — the Claude SDK has a single flat prompt with no separate rules block." },
+ { step: "2", desc: "Take MODEL_NAME → write to agent.yaml → model.preferred (e.g. claude-opus-4-1)." },
+ { step: "3", desc: "For each entry in tools[], add a kebab-case name to agent.yaml → tools. get_customer_info → get-customer-info." },
+ { step: "4", desc: "Create tools/.yaml for each tool — copy the name (kebab-case), description, and input_schema directly from the tool dict." },
+ { step: "5", desc: "The process_tool_call() dispatch and the multi-turn chatbot_interaction() loop stay in the notebook — they are runtime execution, not agent identity." },
+ { step: "6", desc: "Run opengap validate to confirm the structure is correct." },
+];
+
+export function CookbookClaudeSDK() {
+ return (
+
+
+
+
+
OpenGAP / Cookbook /
+
Claude SDK → OpenGAP
+
+ Based on anthropics/anthropic-cookbook customer service agent — a multi-turn
+ agent with three tools (get_customer_info, get_order_details, cancel_order)
+ and a system prompt. The Anthropic SDK has no built-in agent format — everything lives as function arguments.
+ Converting to OpenGAP means pulling those values into files.
+
+
+
+ {/* Part 1 */}
+
+
Part 1 — The Claude SDK agent
+
E-commerce customer service with three tools and a multi-turn loop:
+
+
+
+
+
+
+ {/* Part 2 */}
+
+
Part 2 — What maps to OpenGAP
+
+
+ Claude SDKOpenGAP
+
+ {mapping.map(([from, to], i) => (
+
+ {from}
+ {to}
+
+ ))}
+
+
+
+ {/* Part 3 */}
+
+
Part 3 — Create the OpenGAP files
+
+
+
agent.yaml:
+
+
+
+
SOUL.md — from SYSTEM_PROMPT:
+
+
+
+
tools/get-customer-info.yaml:
+
+
+
+
tools/get-order-details.yaml:
+
+
+
+
tools/cancel-order.yaml:
+
+
+
+
+
+ {/* Part 4 */}
+
+
Part 4 — Validate
+
+
+
+ {/* Steps */}
+
+
What happens step by step
+
+ {steps.map((s, i) => (
+
+
+ {s.step}
+
{s.desc}
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/src/components/opengap/cookbook/CookbookCodex.tsx b/src/components/opengap/cookbook/CookbookCodex.tsx
new file mode 100644
index 0000000..1bc12db
--- /dev/null
+++ b/src/components/opengap/cookbook/CookbookCodex.tsx
@@ -0,0 +1,145 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const projectStructure = `my-project/
+└── AGENTS.md ← identity, rules, and tool declarations`;
+
+const agentsMd = `# AGENTS.md (example)
+You are a backend API developer specializing in Node.js and Express.
+Help users build production-ready REST APIs with proper validation and error handling.
+
+Always validate inputs using Zod schemas.
+Always return consistent JSON error responses.
+Never expose stack traces in production error responses.
+
+## Tools
+- run_tests: Run the test suite
+- lint_code: Lint the codebase with ESLint`;
+
+const agentYaml = `spec_version: 0.1.0
+name: backend-api-agent
+version: 0.1.0
+description: Backend API developer specializing in Node.js and Express
+model:
+ preferred: gpt-4o
+tools:
+ - run-tests
+ - lint-code`;
+
+const soulMd = `# Soul
+
+## Core Identity
+You are a backend API developer specializing in Node.js and Express.
+
+## Purpose
+Help users build production-ready REST APIs with proper validation and error handling.`;
+
+const rulesMd = `# Rules
+
+- Always validate inputs using Zod schemas
+- Always return consistent JSON error responses
+- Never expose stack traces in production error responses`;
+
+const toolRunTests = `name: run-tests
+description: Run the test suite for the project.
+input_schema:
+ type: object
+ properties: {}
+implementation:
+ type: script
+ path: tools/run_tests.py
+ runtime: python3
+ timeout: 60`;
+
+const toolLintCode = `name: lint-code
+description: Lint the codebase with ESLint and report issues.
+input_schema:
+ type: object
+ properties: {}
+implementation:
+ type: script
+ path: tools/lint_code.py
+ runtime: python3
+ timeout: 30`;
+
+const mapping = [
+ ["AGENTS.md — identity + purpose paragraph", "SOUL.md → Core Identity + Purpose"],
+ ["AGENTS.md — behavioral rules", "RULES.md"],
+ ["AGENTS.md → Tools section — each tool name (kebab-case)", "agent.yaml → tools[] + tools/.yaml"],
+];
+
+const steps = [
+ { step: "1", desc: "Open AGENTS.md. Copy the identity and purpose paragraph into SOUL.md." },
+ { step: "2", desc: "Extract rule lines into RULES.md as a bullet list." },
+ { step: "3", desc: "For each tool listed under ## Tools, add a kebab-case entry to agent.yaml → tools and create tools/.yaml." },
+ { step: "4", desc: "Run opengap validate to confirm the structure is correct." },
+];
+
+export function CookbookCodex() {
+ return (
+
+
+
+
+
OpenGAP / Cookbook /
+
Codex → OpenGAP
+
+ Codex stores agent configuration in AGENTS.md — identity, rules, and tool declarations in one file.
+ Converting to OpenGAP means splitting identity into SOUL.md, rules into
+ RULES.md, and each tool into its own tools/<name>.yaml.
+
+
+
+
+
Part 1 — The Codex project
+
Typical Codex workspace structure:
+
+
+
+
+
+
+
+
Part 2 — What maps to OpenGAP
+
+
+ CodexOpenGAP
+
+ {mapping.map(([from, to], i) => (
+
+ {from}
+ {to}
+
+ ))}
+
+
+
+
+
Part 3 — Create the OpenGAP files
+
+
agent.yaml:
+
SOUL.md:
+
RULES.md:
+
tools/run-tests.yaml:
+
tools/lint-code.yaml:
+
+
+
+
+
What happens step by step
+
+ {steps.map((s, i) => (
+
+
+ {s.step}
+
{s.desc}
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/src/components/opengap/cookbook/CookbookCrewAI.tsx b/src/components/opengap/cookbook/CookbookCrewAI.tsx
new file mode 100644
index 0000000..47b6942
--- /dev/null
+++ b/src/components/opengap/cookbook/CookbookCrewAI.tsx
@@ -0,0 +1,235 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const projectStructure = `stephenc222/example-crewai/
+├── financial_analysis.py ← Crew, Agent, Task definitions
+├── requirements.txt
+└── .env.example`;
+
+const financialPy = `# financial_analysis.py
+from langchain_community.tools.yahoo_finance_news import YahooFinanceNewsTool
+from crewai import Agent, Task, Crew
+from dotenv import load_dotenv
+
+load_dotenv()
+
+yahoo_finance_news_tool = YahooFinanceNewsTool()
+
+financial_analyst = Agent(
+ role='Financial Analyst',
+ goal='Analyze current financial news to identify market trends and investment opportunities',
+ backstory="""You are an experienced financial analyst adept at interpreting
+market data and news to forecast financial trends and advise on investment strategies.""",
+ verbose=True,
+ allow_delegation=False,
+ tools=[yahoo_finance_news_tool],
+)
+
+communications_specialist = Agent(
+ role='Corporate Communications Specialist',
+ goal='Communicate financial insights and market trends to company stakeholders',
+ backstory="""As a communications specialist in a corporate setting, your expertise lies in
+crafting clear and concise messages from complex financial data for stakeholders and the public.""",
+ verbose=True,
+ allow_delegation=True,
+)
+
+task1 = Task(
+ description="""Review the latest financial news using the Yahoo Finance News Tool.
+Identify key market trends and potential investment opportunities.""",
+ agent=financial_analyst,
+)
+
+task2 = Task(
+ description="""Based on the financial analyst's report, prepare a press release for the company.
+Highlight the identified market trends and investment opportunities.""",
+ agent=communications_specialist,
+)
+
+crew = Crew(
+ agents=[financial_analyst, communications_specialist],
+ tasks=[task1, task2],
+ verbose=2,
+)
+
+result = crew.kickoff()`;
+
+const agentYaml = `spec_version: 0.1.0
+name: financial-analyst
+version: 0.1.0
+description: Analyze financial news to identify market trends and investment opportunities
+model:
+ preferred: gpt-4o
+tools:
+ - yahoo-finance-news
+agents:
+ communications-specialist:
+ description: Communicate financial insights and market trends to company stakeholders
+ delegation:
+ mode: auto`;
+
+const soulMd = `# Soul
+
+## Core Identity
+Financial Analyst
+
+## Background
+You are an experienced financial analyst adept at interpreting market data
+and news to forecast financial trends and advise on investment strategies.
+
+## Purpose
+Analyze current financial news to identify market trends and investment opportunities.`;
+
+const subAgentYaml = `spec_version: 0.1.0
+name: communications-specialist
+version: 0.1.0
+description: Communicate financial insights and market trends to company stakeholders
+model:
+ preferred: gpt-4o`;
+
+const subSoulMd = `# Soul
+
+## Core Identity
+Corporate Communications Specialist
+
+## Background
+As a communications specialist in a corporate setting, your expertise lies in
+crafting clear and concise messages from complex financial data for stakeholders and the public.
+
+## Purpose
+Transform financial analysis into clear, stakeholder-ready communications.`;
+
+const toolYaml = `name: yahoo-finance-news
+description: Fetch the latest financial news from Yahoo Finance for a given ticker or topic.
+input_schema:
+ type: object
+ properties:
+ query:
+ type: string
+ description: Stock ticker or financial topic to search for
+ required:
+ - query
+implementation:
+ type: script
+ path: tools/yahoo_finance_news.py
+ runtime: python3
+ timeout: 30`;
+
+const validateCmd = `opengap validate -d ./financial-analyst-opengap
+opengap info -d ./financial-analyst-opengap`;
+
+const mapping = [
+ ["Agent.role", "SOUL.md → ## Core Identity"],
+ ["Agent.backstory", "SOUL.md → ## Background"],
+ ["Agent.goal", "SOUL.md → ## Purpose + agent.yaml → description"],
+ ["Agent.tools (tool names, kebab-case)", "agent.yaml → tools[]"],
+ ["Each tool → name + description + schema", "tools/.yaml"],
+ ["First/primary agent", "root agent.yaml + SOUL.md"],
+ ["Additional agents", "agents//agent.yaml + SOUL.md"],
+ ["Task definitions", "stays in framework — no OpenGAP equivalent"],
+ ["Crew / Process type", "stays in framework — runtime orchestration"],
+];
+
+const steps = [
+ { step: "1", desc: "Take the primary agent (financial_analyst) — its role, backstory, and goal map to SOUL.md ## Core Identity, ## Background, ## Purpose." },
+ { step: "2", desc: "Write agent.yaml: use role as name (kebab-case), goal as description, and llm as model.preferred (defaults to gpt-4o if not set)." },
+ { step: "3", desc: "For each tool in Agent.tools, add a kebab-case name to agent.yaml → tools. YahooFinanceNewsTool becomes yahoo-finance-news." },
+ { step: "4", desc: "Create tools/.yaml for each tool with name, description, and input_schema." },
+ { step: "5", desc: "For the second agent (communications_specialist), create agents/communications-specialist/agent.yaml and SOUL.md using the same mapping." },
+ { step: "6", desc: "Task descriptions and Crew stay in financial_analysis.py — they are runtime coordination with no OpenGAP equivalent." },
+];
+
+export function CookbookCrewAI() {
+ return (
+
+
+
+
+
OpenGAP / Cookbook /
+
CrewAI → OpenGAP
+
+ Based on stephenc222/example-crewai — a financial analysis crew with
+ a Financial Analyst agent and a Communications Specialist agent working in sequence.
+ CrewAI's Agent(role, goal, backstory) maps cleanly to OpenGAP's
+ SOUL.md sections; each agent becomes its own OpenGAP directory.
+
+
+
+ {/* Part 1 */}
+
+
Part 1 — The CrewAI project
+
Financial analysis crew with Yahoo Finance news tool:
+
+
+
+
+
+
+ {/* Part 2 */}
+
+
Part 2 — What maps to OpenGAP
+
+
+ CrewAIOpenGAP
+
+ {mapping.map(([from, to], i) => (
+
+ {from}
+ {to}
+
+ ))}
+
+
+
+ {/* Part 3 */}
+
+
Part 3 — Create the OpenGAP files
+
+
+
Root agent: agent.yaml (from financial_analyst):
+
+
+
+
Root agent: SOUL.md:
+
+
+
+
tools/yahoo-finance-news.yaml — one file per tool:
+
+ );
+}
diff --git a/src/components/opengap/cookbook/CookbookCursor.tsx b/src/components/opengap/cookbook/CookbookCursor.tsx
new file mode 100644
index 0000000..95f1ce6
--- /dev/null
+++ b/src/components/opengap/cookbook/CookbookCursor.tsx
@@ -0,0 +1,115 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const projectStructure = `my-project/
+├── .cursorrules ← identity and behavioral rules
+└── .cursor/
+ └── settings.json ← model config`;
+
+const cursorRules = `# .cursorrules (example)
+You are an expert React and TypeScript developer.
+Always use functional components and hooks.
+Prefer Tailwind CSS over custom CSS files.
+Never use class components.
+Always add PropTypes or TypeScript interfaces for props.
+Keep components small and focused on a single responsibility.`;
+
+const agentYaml = `spec_version: 0.1.0
+name: react-typescript-agent
+version: 0.1.0
+description: Expert React and TypeScript developer assistant
+model:
+ preferred: claude-sonnet-4-6`;
+
+const soulMd = `# Soul
+
+## Core Identity
+You are an expert React and TypeScript developer.`;
+
+const rulesMd = `# Rules
+
+- Always use functional components and hooks
+- Prefer Tailwind CSS over custom CSS files
+- Never use class components
+- Always add TypeScript interfaces for props
+- Keep components small and focused on a single responsibility`;
+
+const mapping = [
+ [".cursorrules — identity line", "SOUL.md → Core Identity"],
+ [".cursorrules — behavioral rules", "RULES.md"],
+ [".cursor/settings.json → model", "agent.yaml → model.preferred"],
+];
+
+const steps = [
+ { step: "1", desc: "Open .cursorrules. The first line is usually the identity — copy it into SOUL.md under Core Identity." },
+ { step: "2", desc: "Extract all rule lines (\"always\", \"never\", \"prefer\") into RULES.md as a bullet list." },
+ { step: "3", desc: "Check .cursor/settings.json for a model setting. Write it to agent.yaml → model.preferred." },
+ { step: "4", desc: "Run opengap validate to confirm the structure is correct." },
+];
+
+export function CookbookCursor() {
+ return (
+
+
+
+
+
OpenGAP / Cookbook /
+
Cursor → OpenGAP
+
+ Cursor stores agent behavior in .cursorrules — a flat file mixing identity and rules.
+ Converting to OpenGAP means splitting it into SOUL.md (who the agent is)
+ and RULES.md (how it behaves).
+
+
+
+
+
Part 1 — The Cursor project
+
Typical Cursor workspace structure:
+
+
+
+
+
+
+
+
Part 2 — What maps to OpenGAP
+
+
+ CursorOpenGAP
+
+ {mapping.map(([from, to], i) => (
+
+ {from}
+ {to}
+
+ ))}
+
+
+
+
+
Part 3 — Create the OpenGAP files
+
+
agent.yaml:
+
SOUL.md:
+
RULES.md:
+
+
+
+
+
What happens step by step
+
+ {steps.map((s, i) => (
+
+
+ {s.step}
+
{s.desc}
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/src/components/opengap/cookbook/CookbookGeminiCLI.tsx b/src/components/opengap/cookbook/CookbookGeminiCLI.tsx
new file mode 100644
index 0000000..bb94492
--- /dev/null
+++ b/src/components/opengap/cookbook/CookbookGeminiCLI.tsx
@@ -0,0 +1,114 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const projectStructure = `my-project/
+└── GEMINI.md ← identity, purpose, and behavioral rules`;
+
+const geminiMd = `# GEMINI.md (example)
+You are a data analysis assistant specializing in Python.
+Help users explore datasets, build visualizations, and write clean analysis scripts.
+
+Always explain your reasoning step by step before writing code.
+Use pandas and matplotlib unless the user specifies otherwise.
+Never modify the original dataset — always work on a copy.`;
+
+const agentYaml = `spec_version: 0.1.0
+name: data-analysis-agent
+version: 0.1.0
+description: Data analysis assistant specializing in Python
+model:
+ preferred: gemini-2.0-flash`;
+
+const soulMd = `# Soul
+
+## Core Identity
+You are a data analysis assistant specializing in Python.
+
+## Purpose
+Help users explore datasets, build visualizations, and write clean analysis scripts.`;
+
+const rulesMd = `# Rules
+
+- Always explain reasoning step by step before writing code
+- Use pandas and matplotlib unless the user specifies otherwise
+- Never modify the original dataset — always work on a copy`;
+
+const mapping = [
+ ["GEMINI.md — identity + purpose paragraph", "SOUL.md → Core Identity + Purpose"],
+ ["GEMINI.md — behavioral rules", "RULES.md"],
+ ["Gemini model (e.g. gemini-2.0-flash)", "agent.yaml → model.preferred"],
+];
+
+const steps = [
+ { step: "1", desc: "Open GEMINI.md. Copy the identity and purpose paragraph into SOUL.md." },
+ { step: "2", desc: "Extract rule lines (\"always\", \"never\", \"use X unless...\") into RULES.md." },
+ { step: "3", desc: "Set agent.yaml → model.preferred to the Gemini model being used (e.g. gemini-2.0-flash)." },
+ { step: "4", desc: "Run opengap validate to confirm the structure is correct." },
+];
+
+export function CookbookGeminiCLI() {
+ return (
+
+
+
+
+
OpenGAP / Cookbook /
+
Gemini CLI → OpenGAP
+
+ Gemini CLI stores agent configuration in GEMINI.md — a single markdown file
+ with identity and rules together. Converting to OpenGAP means separating identity into
+ SOUL.md and rules into RULES.md.
+
+
+
+
+
Part 1 — The Gemini CLI project
+
Typical Gemini CLI workspace structure:
+
+
+
+
+
+
+
+
Part 2 — What maps to OpenGAP
+
+
+ Gemini CLIOpenGAP
+
+ {mapping.map(([from, to], i) => (
+
+ {from}
+ {to}
+
+ ))}
+
+
+
+
+
Part 3 — Create the OpenGAP files
+
+
agent.yaml:
+
SOUL.md:
+
RULES.md:
+
+
+
+
+
What happens step by step
+
+ {steps.map((s, i) => (
+
+
+ {s.step}
+
{s.desc}
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/src/components/opengap/cookbook/CookbookGoogleADK.tsx b/src/components/opengap/cookbook/CookbookGoogleADK.tsx
new file mode 100644
index 0000000..e38a185
--- /dev/null
+++ b/src/components/opengap/cookbook/CookbookGoogleADK.tsx
@@ -0,0 +1,340 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const projectStructure = `google/adk-samples/python/agents/travel-concierge/
+├── travel_concierge/
+│ ├── agent.py ← root agent (coordinates sub-agents)
+│ ├── sub_agents/
+│ │ ├── inspiration/
+│ │ │ ├── agent.py ← place_agent + poi_agent + inspiration_agent
+│ │ │ └── prompt.py ← INSPIRATION_AGENT_INSTR
+│ │ ├── planning/agent.py
+│ │ ├── booking/agent.py
+│ │ └── ...
+│ ├── tools/places.py ← Google Maps toolset
+│ └── shared_libraries/types.py ← DestinationIdeas, POISuggestions schemas
+└── pyproject.toml`;
+
+const agentPy = `# travel_concierge/agent.py
+from google.adk.agents import Agent
+from travel_concierge import MODEL
+from travel_concierge.sub_agents.inspiration.agent import inspiration_agent
+from travel_concierge.sub_agents.planning.agent import planning_agent
+from travel_concierge.sub_agents.booking.agent import booking_agent
+
+root_agent = Agent(
+ name="travel_concierge",
+ model=MODEL, # e.g. "gemini-2.0-flash"
+ instruction="A Travel Concierge using the services of multiple sub-agents",
+ sub_agents=[
+ inspiration_agent,
+ planning_agent,
+ booking_agent,
+ # + pre_trip, in_trip, post_trip
+ ],
+)`;
+
+const inspirationPy = `# travel_concierge/sub_agents/inspiration/agent.py
+from google.adk.agents import Agent
+from google.adk.tools.agent_tool import AgentTool
+from travel_concierge.tools.places import get_places_toolset
+from travel_concierge.sub_agents.inspiration import prompt
+
+place_agent = Agent(
+ model=MODEL,
+ name="place_agent",
+ instruction=prompt.PLACE_AGENT_INSTR,
+ description="Suggests destinations given user preferences",
+)
+
+poi_agent = Agent(
+ model=MODEL,
+ name="poi_agent",
+ description="Suggests activities and points of interest for a destination",
+ instruction=prompt.POI_AGENT_INSTR,
+ tools=[get_places_toolset()], # Google Maps grounding
+)
+
+inspiration_agent = Agent(
+ model=MODEL,
+ name="inspiration_agent",
+ description="Travel inspiration: inspires users and discovers their next vacation",
+ instruction=prompt.INSPIRATION_AGENT_INSTR,
+ tools=[AgentTool(agent=place_agent), AgentTool(agent=poi_agent)],
+)`;
+
+const promptPy = `# travel_concierge/sub_agents/inspiration/prompt.py (excerpt)
+INSPIRATION_AGENT_INSTR = """
+You are a travel inspiration agent who helps users find their next dream vacation.
+Your role is to help the user identify a destination and activities they're interested in.
+
+- Call place_agent to recommend vacation destinations given vague ideas.
+- Call poi_agent to suggest points of interest once the user has a specific city in mind.
+- Avoid asking too many questions — when user says "inspire me", call place_agent immediately.
+- Transfer to planning_agent once the user wants a detailed itinerary or flight/hotel deals.
+
+Current user:
+ {user_profile}
+Current time: {_time}
+"""`;
+
+const agentYaml = `spec_version: 0.1.0
+name: travel-concierge
+version: 0.1.0
+description: A travel concierge that coordinates specialized sub-agents across the full trip lifecycle
+model:
+ preferred: gemini-2.0-flash
+agents:
+ inspiration-agent:
+ description: Inspires users and discovers their next vacation destination
+ delegation:
+ mode: auto
+ planning-agent:
+ description: Creates detailed itineraries and schedules
+ delegation:
+ mode: auto
+ booking-agent:
+ description: Books flights and hotels
+ delegation:
+ mode: auto
+ pre-trip-agent:
+ description: Prepares travellers before departure
+ delegation:
+ mode: auto
+ in-trip-agent:
+ description: Provides real-time support during the trip
+ delegation:
+ mode: auto
+ post-trip-agent:
+ description: Handles post-trip feedback and follow-up
+ delegation:
+ mode: auto`;
+
+const soulMd = `# Soul
+
+## Core Identity
+A Travel Concierge that coordinates multiple specialized sub-agents.
+
+## Purpose
+Help users plan their entire trip — from initial inspiration and destination
+discovery through booking, pre-trip preparation, and in-trip support.
+
+## Behavior
+- Delegate to specialist sub-agents rather than handling tasks directly
+- Hand off to inspiration_agent for destination ideas
+- Hand off to planning_agent for itineraries and schedules
+- Hand off to booking_agent for flights and hotels`;
+
+const subInspirationYaml = `spec_version: 0.1.0
+name: inspiration-agent
+version: 0.1.0
+description: Travel inspiration — inspires users and discovers their next vacation destination
+model:
+ preferred: gemini-2.0-flash
+tools:
+ - place-agent
+ - poi-agent
+ - google-places`;
+
+const subInspirationSoul = `# Soul
+
+## Core Identity
+You are a travel inspiration agent who helps users find their next dream vacation.
+
+## Purpose
+Help the user identify a destination and activities they're interested in.
+
+## Behavior
+- Call place_agent to recommend vacation destinations given vague ideas
+- Call poi_agent to suggest points of interest once a city is selected
+- When user says "inspire me", act immediately — don't ask questions first
+- Transfer to planning_agent once user wants a detailed itinerary`;
+
+const toolPlaceAgent = `name: place-agent
+description: Suggests vacation destinations given user preferences and vague ideas.
+input_schema:
+ type: object
+ properties:
+ inspiration_query:
+ type: string
+ description: User preferences or vague idea for a destination
+ required:
+ - inspiration_query
+implementation:
+ type: script
+ path: tools/place_agent.py
+ runtime: python3
+ timeout: 30`;
+
+const toolPoiAgent = `name: poi-agent
+description: Suggests activities and points of interest for a specific destination.
+input_schema:
+ type: object
+ properties:
+ destination:
+ type: string
+ description: The city or destination to find activities for
+ required:
+ - destination
+implementation:
+ type: script
+ path: tools/poi_agent.py
+ runtime: python3
+ timeout: 30`;
+
+const toolGooglePlaces = `name: google-places
+description: Search Google Maps for places, attractions, and local businesses near a location.
+input_schema:
+ type: object
+ properties:
+ query:
+ type: string
+ description: Search query for places or attractions
+ location:
+ type: string
+ description: The city or area to search within
+ required:
+ - query
+ - location
+implementation:
+ type: script
+ path: tools/google_places.py
+ runtime: python3
+ timeout: 30`;
+
+const validateCmd = `opengap validate -d ./travel-concierge-opengap
+opengap info -d ./travel-concierge-opengap`;
+
+const mapping = [
+ ["Agent(instruction=...)", "SOUL.md"],
+ ["Agent(name=...) — kebab-case", "agent.yaml → name"],
+ ["Agent(model=...) e.g. gemini-2.0-flash", "agent.yaml → model.preferred"],
+ ["Agent(description=...)", "agent.yaml → description"],
+ ["Agent(sub_agents=[...]) — each sub-agent", "agents//agent.yaml + SOUL.md"],
+ ["AgentTool(agent=X) — delegated sub-agents as tools", "agent.yaml → tools[] + tools/.yaml"],
+ ["FunctionTool / get_places_toolset()", "tools/.yaml"],
+ ["prompt.py instruction constants", "sub-agent SOUL.md"],
+];
+
+const steps = [
+ { step: "1", desc: "Start with the root agent. Copy Agent(instruction=...) into SOUL.md. Use Agent(name=...) as agent.yaml → name (kebab-case)." },
+ { step: "2", desc: "Take Agent(model=...) (the MODEL constant, e.g. gemini-2.0-flash) → write to agent.yaml → model.preferred." },
+ { step: "3", desc: "For each Agent in sub_agents=[], create a directory agents// with its own agent.yaml and SOUL.md using the same mapping." },
+ { step: "4", desc: "For each AgentTool(agent=X) used as a tool inside a sub-agent, add a kebab-case entry to that agent's tools[] and create tools/.yaml." },
+ { step: "5", desc: "For each FunctionTool or toolset (e.g. Google Maps grounding), create tools/.yaml with description and input_schema." },
+ { step: "6", desc: "Prompt constants in prompt.py become each sub-agent's SOUL.md. Template variables like {user_profile} can be noted in SOUL.md comments." },
+];
+
+export function CookbookGoogleADK() {
+ return (
+
+
+
+
+
OpenGAP / Cookbook /
+
Google ADK → OpenGAP
+
+ Based on google/adk-samples travel concierge — a root agent that coordinates
+ six specialized sub-agents (inspiration, planning, booking, pre-trip, in-trip, post-trip).
+ Each Agent maps to its own OpenGAP directory;
+ AgentTool sub-agents become tool entries.
+
+
+
+ {/* Part 1 */}
+
+
Part 1 — The Google ADK project
+
Multi-agent travel concierge with sub-agents across the trip lifecycle:
+
+
+
+
travel_concierge/agent.py — root agent:
+
+
+
+
sub_agents/inspiration/agent.py — example sub-agent:
+
+
+
+
sub_agents/inspiration/prompt.py:
+
+
+
+
+
+ {/* Part 2 */}
+
+
Part 2 — What maps to OpenGAP
+
+
+ Google ADKOpenGAP
+
+ {mapping.map(([from, to], i) => (
+
+ {from}
+ {to}
+
+ ))}
+
+
+
+ {/* Part 3 */}
+
+
Part 3 — Create the OpenGAP files
+
+
+
Root: agent.yaml:
+
+
+
+
Root: SOUL.md:
+
+
+
+
Sub-agent: agents/inspiration-agent/agent.yaml:
+
+
+
+
Sub-agent: agents/inspiration-agent/SOUL.md — from prompt.py:
+
+
+
+
Sub-agent tool: agents/inspiration-agent/tools/place-agent.yaml — AgentTool mapped to tool YAML:
Sub-agent tool: agents/inspiration-agent/tools/google-places.yaml — from get_places_toolset():
+
+
+
+
+
+ {/* Part 4 */}
+
+
Part 4 — Validate
+
+
+
+ {/* Steps */}
+
+
What happens step by step
+
+ {steps.map((s, i) => (
+
+
+ {s.step}
+
{s.desc}
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/src/components/opengap/cookbook/CookbookLangChain.tsx b/src/components/opengap/cookbook/CookbookLangChain.tsx
new file mode 100644
index 0000000..cabe534
--- /dev/null
+++ b/src/components/opengap/cookbook/CookbookLangChain.tsx
@@ -0,0 +1,232 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const projectStructure = `my-langchain-agent/
+├── agent.py ← AgentExecutor, prompt, model, tools
+├── tools.py ← @tool decorated functions
+└── requirements.txt`;
+
+const agentPy = `# agent.py
+from langchain.agents import AgentExecutor, create_tool_calling_agent
+from langchain_anthropic import ChatAnthropic
+from langchain_core.prompts import ChatPromptTemplate
+from tools import get_order_status, initiate_return
+
+SYSTEM_PROMPT = """You are a customer support agent for an e-commerce platform.
+Help customers with order status and return requests.
+Always verify the order ID before discussing order details.
+
+Rules:
+- Never share one customer's data with another
+- Always confirm before initiating a return"""
+
+prompt = ChatPromptTemplate.from_messages([
+ ("system", SYSTEM_PROMPT),
+ ("human", "{input}"),
+ ("placeholder", "{agent_scratchpad}"),
+])
+
+model = ChatAnthropic(model="claude-sonnet-4-6")
+tools = [get_order_status, initiate_return]
+
+agent = create_tool_calling_agent(model, tools, prompt)
+agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)`;
+
+const toolsPy = `# tools.py
+from langchain_core.tools import tool
+
+@tool
+def get_order_status(order_id: str) -> str:
+ """Get the current status of a customer order by order ID."""
+ # real impl queries your order database
+ return f"Order {order_id} is being processed."
+
+@tool
+def initiate_return(order_id: str, reason: str) -> str:
+ """Initiate a return request for an order.
+
+ Args:
+ order_id: The order to return.
+ reason: The reason for the return.
+ """
+ return f"Return initiated for order {order_id}. Reason: {reason}."`;
+
+const agentYaml = `spec_version: 0.1.0
+name: customer-support-agent
+version: 0.1.0
+description: Customer support agent for an e-commerce platform
+model:
+ preferred: claude-sonnet-4-6
+tools:
+ - get-order-status
+ - initiate-return`;
+
+const soulMd = `# Soul
+
+## Core Identity
+You are a customer support agent for an e-commerce platform.
+
+## Purpose
+Help customers with order status and return requests.
+Always verify the order ID before discussing order details.`;
+
+const rulesMd = `# Rules
+
+- Never share one customer's data with another
+- Always confirm before initiating a return`;
+
+const toolGetOrder = `name: get-order-status
+description: Get the current status of a customer order by order ID.
+input_schema:
+ type: object
+ properties:
+ order_id:
+ type: string
+ description: The order ID to look up
+ required:
+ - order_id
+implementation:
+ type: script
+ path: tools/get_order_status.py
+ runtime: python3
+ timeout: 30`;
+
+const toolReturn = `name: initiate-return
+description: Initiate a return request for an order.
+input_schema:
+ type: object
+ properties:
+ order_id:
+ type: string
+ description: The order to return
+ reason:
+ type: string
+ description: The reason for the return
+ required:
+ - order_id
+ - reason
+implementation:
+ type: script
+ path: tools/initiate_return.py
+ runtime: python3
+ timeout: 30`;
+
+const validateCmd = `opengap validate -d ./customer-support-opengap
+opengap info -d ./customer-support-opengap`;
+
+const mapping = [
+ ["SYSTEM_PROMPT — identity/purpose lines", "SOUL.md"],
+ ["SYSTEM_PROMPT — Rules: section", "RULES.md"],
+ ["ChatAnthropic(model=...)", "agent.yaml → model.preferred"],
+ ["@tool function names (kebab-case)", "agent.yaml → tools[]"],
+ ["@tool docstring + typed args", "tools/.yaml description + input_schema"],
+ ["Existing tools.py function", "tools/.yaml → implementation.path"],
+ ["AgentExecutor + ChatPromptTemplate", "stays in framework — runtime execution loop"],
+ ["agent_scratchpad placeholder", "stays in framework — internal reasoning trace"],
+];
+
+const steps = [
+ { step: "1", desc: "Split SYSTEM_PROMPT by content: who-you-are and purpose lines → SOUL.md. Hard rules (never, always) → RULES.md." },
+ { step: "2", desc: "Take the model string from ChatAnthropic(model=...) → write to agent.yaml → model.preferred." },
+ { step: "3", desc: "List each @tool function name → add to agent.yaml → tools as kebab-case (get_order_status → get-order-status)." },
+ { step: "4", desc: "Create tools/.yaml for each tool — copy the docstring as description, typed args as input_schema, and point implementation.path to the existing Python file." },
+ { step: "5", desc: "AgentExecutor, ChatPromptTemplate, and the agent_scratchpad placeholder stay in agent.py — they are LangChain runtime wiring." },
+ { step: "6", desc: "Run opengap validate to confirm the structure is correct." },
+];
+
+export function CookbookLangChain() {
+ return (
+
+
+
+
+
OpenGAP / Cookbook /
+
LangChain → OpenGAP
+
+ LangChain agents use AgentExecutor, a ChatPromptTemplate,
+ and @tool decorated functions. The system prompt is a plain string
+ inside the template. Converting to OpenGAP means pulling the prompt into files and declaring the tools —
+ the executor and template stay in your code.
+
+
+
+
+
Part 1 — The LangChain project
+
Customer support agent with two tools:
+
+
+
+
tools.py:
+
+
+
+
agent.py:
+
+
+
+
+
+
+
Part 2 — What maps to OpenGAP
+
+
+ LangChainOpenGAP
+
+ {mapping.map(([from, to], i) => (
+
+ {from}
+ {to}
+
+ ))}
+
+
+
+
+
Part 3 — Create the OpenGAP files
+
+
+
agent.yaml:
+
+
+
+
SOUL.md — identity lines from SYSTEM_PROMPT:
+
+
+
+
RULES.md — hard rules from SYSTEM_PROMPT:
+
+
+
+
tools/get-order-status.yaml:
+
+
+
+
tools/initiate-return.yaml:
+
+
+
+
+
+
+
Part 4 — Validate
+
+
+
+
+
What happens step by step
+
+ {steps.map((s, i) => (
+
+
+ {s.step}
+
{s.desc}
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/src/components/opengap/cookbook/CookbookLangGraph.tsx b/src/components/opengap/cookbook/CookbookLangGraph.tsx
new file mode 100644
index 0000000..8203318
--- /dev/null
+++ b/src/components/opengap/cookbook/CookbookLangGraph.tsx
@@ -0,0 +1,202 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const projectStructure = `langchain-ai/react-agent/
+├── src/react_agent/
+│ ├── graph.py ← StateGraph: nodes, edges, routing
+│ ├── tools.py ← TOOLS list (Tavily search)
+│ ├── prompts.py ← SYSTEM_PROMPT string
+│ ├── state.py ← State, InputState TypedDicts
+│ └── configuration.py← model + max_search_results config
+└── langgraph.json ← graph entry point for Studio`;
+
+const promptsPy = `# src/react_agent/prompts.py
+"""Default prompts used by the agent."""
+
+SYSTEM_PROMPT = """You are a helpful AI assistant.
+
+System time: {system_time}"""`;
+
+const graphPy = `# src/react_agent/graph.py (key excerpt)
+from react_agent.prompts import SYSTEM_PROMPT
+from react_agent.tools import TOOLS
+from react_agent.utils import load_chat_model
+
+async def call_model(state: State, runtime: Runtime[Context]):
+ model = load_chat_model(runtime.context.model).bind_tools(TOOLS)
+ system_message = runtime.context.system_prompt.format(
+ system_time=datetime.now(tz=UTC).isoformat()
+ )
+ response = await model.ainvoke(
+ [{"role": "system", "content": system_message}, *state.messages]
+ )
+ return {"messages": [response]}
+
+builder = StateGraph(State, input_schema=InputState, context_schema=Context)
+builder.add_node(call_model)
+builder.add_node("tools", ToolNode(TOOLS))
+builder.add_edge("__start__", "call_model")
+
+def route_model_output(state: State):
+ last = state.messages[-1]
+ return "__end__" if not last.tool_calls else "tools"
+
+builder.add_conditional_edges("call_model", route_model_output)
+builder.add_edge("tools", "call_model")
+graph = builder.compile(name="ReAct Agent")`;
+
+const toolsPy = `# src/react_agent/tools.py
+from langchain_community.tools.tavily_search import TavilySearchResults
+from langgraph.runtime import Runtime
+from react_agent.context import Context
+
+async def search(query: str, runtime: Runtime[Context]) -> list[dict]:
+ """Search the web using Tavily."""
+ max_results = runtime.context.max_search_results
+ wrapped = TavilySearchResults(max_results=max_results)
+ return await wrapped.ainvoke({"query": query})
+
+TOOLS = [search]`;
+
+const agentYaml = `spec_version: 0.1.0
+name: react-agent
+version: 0.1.0
+description: ReAct agent that searches the web to answer questions accurately
+model:
+ preferred: claude-sonnet-4-6
+tools:
+ - search`;
+
+const soulMd = `# Soul
+
+## Core Identity
+You are a helpful AI assistant.`;
+
+const toolYaml = `name: search
+description: Search the web using Tavily for up-to-date information.
+input_schema:
+ type: object
+ properties:
+ query:
+ type: string
+ description: The search query
+ required:
+ - query
+implementation:
+ type: script
+ path: tools/search.py
+ runtime: python3
+ timeout: 30`;
+
+const validateCmd = `opengap validate -d ./react-agent-opengap
+opengap info -d ./react-agent-opengap`;
+
+const mapping = [
+ ["SYSTEM_PROMPT in prompts.py", "SOUL.md"],
+ ["runtime.context.model (from configuration.py)", "agent.yaml → model.preferred"],
+ ["TOOLS list — function names", "agent.yaml → tools[]"],
+ ["Each tool function + docstring + typed args", "tools/.yaml"],
+ ["implementation.path → existing tools.py function", "tools/.yaml → implementation.path"],
+ ["StateGraph nodes + edges in graph.py", "stays in framework — runtime orchestration"],
+];
+
+const steps = [
+ { step: "1", desc: "Copy SYSTEM_PROMPT from prompts.py into SOUL.md." },
+ { step: "2", desc: "Take the model name from configuration.py → write to agent.yaml → model.preferred." },
+ { step: "3", desc: "For each function in the TOOLS list, add a kebab-case name to agent.yaml → tools (search stays search, web_search becomes web-search)." },
+ { step: "4", desc: "Create tools/.yaml for each tool — use the docstring as description, typed parameters as input_schema, and point implementation.path to your existing tool file." },
+ { step: "5", desc: "StateGraph, ToolNode, routing logic, and State TypedDicts stay in graph.py — they are runtime execution wiring with no OpenGAP equivalent." },
+ { step: "6", desc: "Run opengap validate to confirm the structure is correct." },
+];
+
+export function CookbookLangGraph() {
+ return (
+
+
+
+
+
OpenGAP / Cookbook /
+
LangGraph → OpenGAP
+
+ Based on langchain-ai/react-agent — the official LangGraph ReAct agent template.
+ The agent identity lives in prompts.py, tools are exported from tools.py,
+ and the graph wiring in graph.py stays in the framework.
+
+
+
+
+
Part 1 — The LangGraph project
+
ReAct agent with Tavily web search, designed for LangGraph Studio:
+
+
+
+
prompts.py:
+
+
+
+
tools.py:
+
+
+
+
graph.py excerpt:
+
+
+
+
+
+
+
Part 2 — What maps to OpenGAP
+
+
+ LangGraph (react-agent)OpenGAP
+
+ {mapping.map(([from, to], i) => (
+
+ {from}
+ {to}
+
+ ))}
+
+
+
+
+
Part 3 — Create the OpenGAP files
+
+
+
agent.yaml:
+
+
+
+
SOUL.md — from SYSTEM_PROMPT:
+
+
+
+
tools/search.yaml — the implementation.path points to your existing tool file:
+
+
+
+
+
+
+
Part 4 — Validate
+
+
+
+
+
What happens step by step
+
+ {steps.map((s, i) => (
+
+
+ {s.step}
+
{s.desc}
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/src/components/opengap/cookbook/CookbookOpenAIAgents.tsx b/src/components/opengap/cookbook/CookbookOpenAIAgents.tsx
new file mode 100644
index 0000000..017cba3
--- /dev/null
+++ b/src/components/opengap/cookbook/CookbookOpenAIAgents.tsx
@@ -0,0 +1,311 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const projectStructure = `openai/openai-agents-python/
+└── examples/customer_service/
+ └── main.py ← triage_agent, faq_agent, seat_booking_agent + tools`;
+
+const mainPy = `# examples/customer_service/main.py (key excerpt)
+from agents import Agent, function_tool, handoff, Runner
+from pydantic import BaseModel
+
+class AirlineAgentContext(BaseModel):
+ passenger_name: str | None = None
+ confirmation_number: str | None = None
+ seat_number: str | None = None
+ flight_number: str | None = None
+
+@function_tool(name_override="faq_lookup_tool",
+ description_override="Lookup frequently asked questions.")
+async def faq_lookup_tool(question: str) -> str:
+ question_lower = question.lower()
+ if any(k in question_lower for k in ["bag", "baggage", "luggage"]):
+ return "One bag allowed, under 50 lbs and 22x14x9 inches."
+ elif "seat" in question_lower:
+ return "120 seats: 22 business, 98 economy. Exit rows 4 and 16."
+ elif "wifi" in question_lower:
+ return "Free wifi — join Airline-Wifi"
+ return "I'm sorry, I don't know the answer to that question."
+
+@function_tool
+async def update_seat(
+ context: RunContextWrapper[AirlineAgentContext],
+ confirmation_number: str,
+ new_seat: str,
+) -> str:
+ """Update the seat for a given confirmation number."""
+ context.context.confirmation_number = confirmation_number
+ context.context.seat_number = new_seat
+ return f"Updated seat to {new_seat} for {confirmation_number}"
+
+faq_agent = Agent[AirlineAgentContext](
+ name="FAQ Agent",
+ handoff_description="Answers questions about the airline.",
+ instructions="""You are an FAQ agent. Use faq_lookup_tool to answer the customer's
+question. If you cannot answer it, transfer back to the triage agent.""",
+ tools=[faq_lookup_tool],
+)
+
+seat_booking_agent = Agent[AirlineAgentContext](
+ name="Seat Booking Agent",
+ handoff_description="Updates a seat on a flight.",
+ instructions="""You are a seat booking agent. Ask for the confirmation number,
+ask for the desired seat, then call update_seat.""",
+ tools=[update_seat],
+)
+
+triage_agent = Agent[AirlineAgentContext](
+ name="Triage Agent",
+ handoff_description="Routes customer requests to the appropriate agent.",
+ instructions="You are a helpful triaging agent. Delegate to FAQ or seat booking agents.",
+ handoffs=[
+ handoff(agent=faq_agent, tool_name_override="transfer_to_faq_agent"),
+ handoff(agent=seat_booking_agent, tool_name_override="transfer_to_seat_booking_agent"),
+ ],
+)`;
+
+const agentYaml = `spec_version: 0.1.0
+name: triage-agent
+version: 0.1.0
+description: Routes airline customer requests to the appropriate specialist agent
+model:
+ preferred: gpt-4o
+tools:
+ - transfer-to-faq-agent
+ - transfer-to-seat-booking-agent
+agents:
+ faq-agent:
+ description: Answers frequently asked questions about the airline
+ delegation:
+ mode: auto
+ seat-booking-agent:
+ description: Updates seat assignments for airline passengers
+ delegation:
+ mode: auto`;
+
+const soulMd = `# Soul
+
+## Core Identity
+You are a helpful triaging agent for an airline customer service system.
+
+## Purpose
+Evaluate customer requests and delegate to the FAQ agent or seat booking agent
+using the appropriate transfer tool.`;
+
+const subFaqYaml = `spec_version: 0.1.0
+name: faq-agent
+version: 0.1.0
+description: Answers frequently asked questions about the airline
+model:
+ preferred: gpt-4o
+tools:
+ - faq-lookup-tool`;
+
+const subFaqSoul = `# Soul
+
+## Core Identity
+You are an FAQ agent for an airline customer service system.
+
+## Behavior
+Use the faq_lookup_tool to answer each question. Do not rely on your own knowledge.
+If you cannot answer, transfer back to the triage agent.`;
+
+const subSeatYaml = `spec_version: 0.1.0
+name: seat-booking-agent
+version: 0.1.0
+description: Updates seat assignments for airline passengers
+model:
+ preferred: gpt-4o
+tools:
+ - update-seat`;
+
+const toolTransferFaq = `name: transfer-to-faq-agent
+description: Transfer the customer to the FAQ agent.
+input_schema:
+ type: object
+ properties: {}
+implementation:
+ type: script
+ path: tools/transfer_to_faq_agent.py
+ runtime: python3
+ timeout: 30`;
+
+const toolTransferSeat = `name: transfer-to-seat-booking-agent
+description: Transfer the customer to the seat booking agent.
+input_schema:
+ type: object
+ properties: {}
+implementation:
+ type: script
+ path: tools/transfer_to_seat_booking_agent.py
+ runtime: python3
+ timeout: 30`;
+
+const toolFaqLookup = `name: faq-lookup-tool
+description: Lookup frequently asked questions about the airline.
+input_schema:
+ type: object
+ properties:
+ question:
+ type: string
+ description: The customer's question
+ required:
+ - question
+implementation:
+ type: script
+ path: tools/faq_lookup_tool.py
+ runtime: python3
+ timeout: 30`;
+
+const toolUpdateSeat = `name: update-seat
+description: Update the seat for a given confirmation number.
+input_schema:
+ type: object
+ properties:
+ confirmation_number:
+ type: string
+ description: The booking confirmation number
+ new_seat:
+ type: string
+ description: The desired seat assignment
+ required:
+ - confirmation_number
+ - new_seat
+implementation:
+ type: script
+ path: tools/update_seat.py
+ runtime: python3
+ timeout: 30`;
+
+const validateCmd = `opengap validate -d ./triage-agent-opengap
+opengap info -d ./triage-agent-opengap`;
+
+const mapping = [
+ ["Agent.instructions", "SOUL.md"],
+ ["Agent.name (kebab-case)", "agent.yaml → name"],
+ ["Agent.model", "agent.yaml → model.preferred"],
+ ["@function_tool functions (kebab-case names)", "agent.yaml → tools[] + tools/.yaml"],
+ ["handoff(agent=X, tool_name_override=Y)", "agent.yaml → tools[] (transfer tools)"],
+ ["Each Agent in handoffs list", "agents//agent.yaml + SOUL.md"],
+ ["AirlineAgentContext Pydantic model", "stays in framework — runtime shared state"],
+ ["Runner.run() + conversation loop", "stays in framework — execution runner"],
+];
+
+const steps = [
+ { step: "1", desc: "Identify the root/entry agent (triage_agent). Its instructions → SOUL.md. Its name → agent.yaml name (kebab-case)." },
+ { step: "2", desc: "For each handoff, add a transfer tool entry to agent.yaml → tools (e.g. transfer_to_faq_agent → transfer-to-faq-agent) and create a minimal tools/.yaml." },
+ { step: "3", desc: "For each agent in handoffs (faq_agent, seat_booking_agent), create agents//agent.yaml + SOUL.md using the same mapping." },
+ { step: "4", desc: "For each @function_tool (faq_lookup_tool, update_seat), add to the relevant sub-agent's tools list and create tools/.yaml." },
+ { step: "5", desc: "AirlineAgentContext, RunContextWrapper, on_handoff hooks, and Runner.run() stay in main.py — runtime execution and state." },
+ { step: "6", desc: "Run opengap validate on each agent directory to confirm the structure is correct." },
+];
+
+export function CookbookOpenAIAgents() {
+ return (
+
+
+
+
+
OpenGAP / Cookbook /
+
OpenAI Agents SDK → OpenGAP
+
+ Based on openai/openai-agents-python customer service example — a triage agent
+ that routes airline customers to an FAQ agent or a seat booking agent via handoffs.
+ Each Agent maps to its own OpenGAP directory; handoffs become transfer tools.
+
+
+
+ {/* Part 1 */}
+
+
Part 1 — The OpenAI Agents SDK project
+
Airline customer service with triage + FAQ + seat booking agents:
+
+ );
+}
diff --git a/src/components/opengap/cookbook/CookbookOpenCode.tsx b/src/components/opengap/cookbook/CookbookOpenCode.tsx
new file mode 100644
index 0000000..922d606
--- /dev/null
+++ b/src/components/opengap/cookbook/CookbookOpenCode.tsx
@@ -0,0 +1,119 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const projectStructure = `my-project/
+└── .opencode/
+ └── config.json ← model, system prompt, and tool config`;
+
+const configJson = `// .opencode/config.json (example)
+{
+ "model": "gpt-4o",
+ "system": "You are a full-stack developer assistant. Help users build production-ready applications. Always consider security and performance. Prefer simple, readable solutions over clever ones.",
+ "tools": {
+ "bash": { "enabled": true },
+ "read": { "enabled": true },
+ "write": { "enabled": true }
+ }
+}`;
+
+const agentYaml = `spec_version: 0.1.0
+name: fullstack-dev-agent
+version: 0.1.0
+description: Full-stack developer assistant for production-ready applications
+model:
+ preferred: gpt-4o`;
+
+const soulMd = `# Soul
+
+## Core Identity
+You are a full-stack developer assistant.
+
+## Purpose
+Help users build production-ready applications.`;
+
+const rulesMd = `# Rules
+
+- Always consider security and performance
+- Prefer simple, readable solutions over clever ones`;
+
+const mapping = [
+ [".opencode/config.json → system", "SOUL.md + RULES.md"],
+ [".opencode/config.json → model", "agent.yaml → model.preferred"],
+ [".opencode/config.json → tools", "agent.yaml → tools[] (built-in tools stay in runtime)"],
+];
+
+const steps = [
+ { step: "1", desc: "Open .opencode/config.json. Read the system field — it contains both identity and rules mixed together." },
+ { step: "2", desc: "Split the system prompt: who the agent is and what it does → SOUL.md. Behavioral constraints → RULES.md." },
+ { step: "3", desc: "Copy the model value from config.json → agent.yaml → model.preferred." },
+ { step: "4", desc: "Built-in tools (bash, read, write) are runtime capabilities — they don't need tool YAML files." },
+ { step: "5", desc: "Run opengap validate to confirm the structure is correct." },
+];
+
+export function CookbookOpenCode() {
+ return (
+
+
+
+
+
OpenGAP / Cookbook /
+
OpenCode → OpenGAP
+
+ OpenCode stores agent configuration in .opencode/config.json — model, system prompt,
+ and tool toggles in a single JSON file. Converting to OpenGAP means extracting the system prompt
+ into SOUL.md and RULES.md, and
+ the model into agent.yaml.
+
))}
diff --git a/src/components/opengap/cookbook/CookbookLangGraph.tsx b/src/components/opengap/cookbook/CookbookLangGraph.tsx
index 8203318..395baa0 100644
--- a/src/components/opengap/cookbook/CookbookLangGraph.tsx
+++ b/src/components/opengap/cookbook/CookbookLangGraph.tsx
@@ -1,199 +1,910 @@
-import { motion } from "framer-motion";
+import { motion, AnimatePresence } from "framer-motion";
+import { ArrowRight, FileText, Shield, Workflow, Settings, CheckCircle2, ChevronDown, BookOpen, Target, Package } from "lucide-react";
+import { useState } from "react";
import { CodeBlock } from "@/components/gitAgent/CodeBlock";
-const projectStructure = `langchain-ai/react-agent/
-├── src/react_agent/
-│ ├── graph.py ← StateGraph: nodes, edges, routing
-│ ├── tools.py ← TOOLS list (Tavily search)
-│ ├── prompts.py ← SYSTEM_PROMPT string
-│ ├── state.py ← State, InputState TypedDicts
-│ └── configuration.py← model + max_search_results config
-└── langgraph.json ← graph entry point for Studio`;
+/* ═══════════════════ PART 1 — full LangGraph source ═══════════════════ */
-const promptsPy = `# src/react_agent/prompts.py
-"""Default prompts used by the agent."""
+const sourceTree = `react-agent-main/ (LangGraph)
+├── langgraph.json ← graph entry point
+├── pyproject.toml ← dependencies
+└── src/react_agent/
+ ├── graph.py ← StateGraph: nodes, edges, routing
+ ├── tools.py ← TOOLS list (Tavily search)
+ ├── prompts.py ← SYSTEM_PROMPT
+ ├── context.py ← model + max_search_results
+ └── state.py ← State / InputState (runtime)`;
+
+const srcPrompts = `"""Default prompts used by the agent."""
SYSTEM_PROMPT = """You are a helpful AI assistant.
System time: {system_time}"""`;
-const graphPy = `# src/react_agent/graph.py (key excerpt)
-from react_agent.prompts import SYSTEM_PROMPT
+const srcContext = `"""Define the configurable parameters for the agent."""
+
+from __future__ import annotations
+
+import os
+from dataclasses import dataclass, field, fields
+from typing import Annotated
+
+from . import prompts
+
+
+@dataclass(kw_only=True)
+class Context:
+ """The context for the agent."""
+
+ system_prompt: str = field(
+ default=prompts.SYSTEM_PROMPT,
+ metadata={"description": "The system prompt to use for the agent's interactions."},
+ )
+
+ model: Annotated[str, {"__template_metadata__": {"kind": "llm"}}] = field(
+ default="anthropic/claude-sonnet-4-5-20250929",
+ metadata={"description": "The language model. Form: provider/model-name."},
+ )
+
+ max_search_results: int = field(
+ default=10,
+ metadata={"description": "Max search results to return per query."},
+ )
+
+ def __post_init__(self) -> None:
+ """Fetch env vars for attributes that were not passed as args."""
+ for f in fields(self):
+ if not f.init:
+ continue
+ if getattr(self, f.name) == f.default:
+ setattr(self, f.name, os.environ.get(f.name.upper(), f.default))`;
+
+const srcTools = `"""Example tools — a basic Tavily search function."""
+
+from typing import Any, Callable, List, Optional, cast
+
+from langchain_tavily import TavilySearch
+from langgraph.runtime import get_runtime
+
+from react_agent.context import Context
+
+
+async def search(query: str) -> Optional[dict[str, Any]]:
+ """Search for general web results.
+
+ This function performs a search using the Tavily search engine, which is
+ designed to provide comprehensive, accurate, and trusted results. It's
+ particularly useful for answering questions about current events.
+ """
+ runtime = get_runtime(Context)
+ wrapped = TavilySearch(max_results=runtime.context.max_search_results)
+ return cast(dict[str, Any], await wrapped.ainvoke({"query": query}))
+
+
+TOOLS: List[Callable[..., Any]] = [search]`;
+
+const srcState = `"""Define the state structures for the agent."""
+
+from __future__ import annotations
+
+from dataclasses import dataclass, field
+from typing import Sequence
+
+from langchain_core.messages import AnyMessage
+from langgraph.graph import add_messages
+from langgraph.managed import IsLastStep
+from typing_extensions import Annotated
+
+
+@dataclass
+class InputState:
+ """A narrower interface to the outside world."""
+
+ messages: Annotated[Sequence[AnyMessage], add_messages] = field(
+ default_factory=list
+ )
+ # The \`add_messages\` reducer merges new messages with existing ones,
+ # updating by ID to maintain an "append-only" state.
+
+
+@dataclass
+class State(InputState):
+ """The complete state of the agent."""
+
+ is_last_step: IsLastStep = field(default=False)
+ # A 'managed' variable, controlled by the state machine — set to True when
+ # the step count reaches recursion_limit - 1.`;
+
+const srcGraph = `"""Define a custom Reasoning and Action agent."""
+
+from datetime import UTC, datetime
+from typing import Dict, List, Literal, cast
+
+from langchain_core.messages import AIMessage
+from langgraph.graph import StateGraph
+from langgraph.prebuilt import ToolNode
+from langgraph.runtime import Runtime
+
+from react_agent.context import Context
+from react_agent.state import InputState, State
from react_agent.tools import TOOLS
from react_agent.utils import load_chat_model
-async def call_model(state: State, runtime: Runtime[Context]):
+
+async def call_model(
+ state: State, runtime: Runtime[Context]
+) -> Dict[str, List[AIMessage]]:
+ """Call the LLM powering our agent."""
+ # Initialize the model with tool binding.
model = load_chat_model(runtime.context.model).bind_tools(TOOLS)
+
+ # Format the system prompt with the current time.
system_message = runtime.context.system_prompt.format(
system_time=datetime.now(tz=UTC).isoformat()
)
- response = await model.ainvoke(
- [{"role": "system", "content": system_message}, *state.messages]
+
+ response = cast(
+ AIMessage,
+ await model.ainvoke(
+ [{"role": "system", "content": system_message}, *state.messages]
+ ),
)
+
+ # Handle the case where it's the last step but the model still wants a tool.
+ if state.is_last_step and response.tool_calls:
+ return {"messages": [AIMessage(
+ id=response.id,
+ content="Sorry, I could not find an answer to your question in the "
+ "specified number of steps.",
+ )]}
+
return {"messages": [response]}
+
+# Build the graph
builder = StateGraph(State, input_schema=InputState, context_schema=Context)
+
+# The two nodes we cycle between
builder.add_node(call_model)
builder.add_node("tools", ToolNode(TOOLS))
+
+# Entrypoint
builder.add_edge("__start__", "call_model")
-def route_model_output(state: State):
- last = state.messages[-1]
- return "__end__" if not last.tool_calls else "tools"
+def route_model_output(state: State) -> Literal["__end__", "tools"]:
+ """Route to tools if the last message has tool calls, else finish."""
+ last_message = state.messages[-1]
+ if not isinstance(last_message, AIMessage):
+ raise ValueError(f"Expected AIMessage, got {type(last_message).__name__}")
+ if not last_message.tool_calls:
+ return "__end__"
+ return "tools"
+
+
+# After call_model, branch on route_model_output
builder.add_conditional_edges("call_model", route_model_output)
+
+# After tools, always loop back to the model
builder.add_edge("tools", "call_model")
+
+# Compile into an executable graph
graph = builder.compile(name="ReAct Agent")`;
-const toolsPy = `# src/react_agent/tools.py
-from langchain_community.tools.tavily_search import TavilySearchResults
-from langgraph.runtime import Runtime
-from react_agent.context import Context
+const srcLanggraphJson = `{
+ "$schema": "https://langgra.ph/schema.json",
+ "dependencies": ["."],
+ "graphs": {
+ "agent": "./src/react_agent/graph.py:graph"
+ },
+ "env": ".env"
+}`;
+
+/* ═══════════════════ PART 2 — paired mapping excerpts ═══════════════════ */
+
+const fromPrompts = `# prompts.py
+SYSTEM_PROMPT = """You are a helpful AI assistant.
+
+System time: {system_time}"""`;
+
+const toSoul = `# Soul
+
+## Core Identity
+You are a helpful AI assistant.
+
+I am a ReAct (Reasoning and Action) agent. I work by
+iteratively reasoning about user queries and executing
+actions — thinking step by step, choosing the right
+tools, observing results, and continuing until I can
+provide a complete and accurate answer.
-async def search(query: str, runtime: Runtime[Context]) -> list[dict]:
- """Search the web using Tavily."""
- max_results = runtime.context.max_search_results
- wrapped = TavilySearchResults(max_results=max_results)
+## Purpose
+Given a user query, I reason about what information or
+actions are needed, execute appropriate tools, observe
+results, and repeat until I can deliver a final answer.`;
+
+const fromGraphRules = `# graph.py (control logic)
+system_message = runtime.context.system_prompt.format(
+ system_time=datetime.now(tz=UTC).isoformat() # ← time-stamp each turn
+)
+
+if state.is_last_step and response.tool_calls: # ← step-limit guard
+ return {"messages": [AIMessage(
+ id=response.id,
+ content="Sorry, I could not find an answer to "
+ "your question in the specified number of steps.",
+ )]}`;
+
+const toRules = `# RULES.md
+## Must Always
+- Append the current system time (ISO 8601, UTC) to the
+ system prompt at the start of each turn
+- Continue the Reason → Act → Observe loop until a final
+ answer is reached or the step limit is hit
+- Return tool call results to the model before responding
+
+## Must Never
+- Fabricate search results or pretend to call a tool
+- Exceed the configured max reasoning steps; if the limit
+ is reached mid-tool-call, respond with: "Sorry, I could
+ not find an answer ... in the specified number of steps."
+- Ignore tool errors — always surface them to the model`;
+
+const fromGraphLoop = `# graph.py (the ReAct loop)
+builder = StateGraph(State, context_schema=Context)
+
+builder.add_node(call_model)
+builder.add_node("tools", ToolNode(TOOLS))
+builder.add_edge("__start__", "call_model")
+
+def route_model_output(state) -> Literal["__end__", "tools"]:
+ last = state.messages[-1]
+ if not last.tool_calls: # no tool call → done
+ return "__end__"
+ return "tools" # else run the tool
+
+builder.add_conditional_edges("call_model", route_model_output)
+builder.add_edge("tools", "call_model") # loop back`;
+
+const toSkill = `# skills/reason-and-act/SKILL.md
+---
+name: reason-and-act
+description: "Core ReAct reasoning loop: reason about a
+ user query, decide on an action, execute it, observe
+ the result, and repeat until a final answer is ready."
+allowed-tools: search
+---
+
+## Step 1: Reason → what's asked? what's missing?
+## Step 2: Act → call search if more info is needed
+## Step 3: Observe → enough to answer, or search again?
+## Step 4: Respond → compose the grounded final answer`;
+
+const fromContext = `# context.py + langgraph.json
+model: str = field(
+ default="anthropic/claude-sonnet-4-5-20250929",
+)
+max_search_results: int = field(default=10)
+# recursion_limit / step cap → runtime
+
+# langgraph.json
+"graphs": { "agent": "./src/react_agent/graph.py:graph" }`;
+
+const toAgentYaml = `# agent.yaml
+spec_version: "0.1.0"
+name: react-agent
+version: 1.0.0
+description: A ReAct (Reasoning and Action) agent that iteratively
+ reasons about user queries and executes tool actions.
+model:
+ preferred: anthropic:claude-sonnet-4-5-20250929
+ fallback:
+ - openai:gpt-4o
+runtime:
+ max_turns: 50
+ timeout: 300
+skills:
+ - reason-and-act
+tools:
+ - search`;
+
+const fromTools = `# tools.py
+from langchain_tavily import TavilySearch
+
+async def search(query: str) -> Optional[dict[str, Any]]:
+ """Search for general web results."""
+ runtime = get_runtime(Context)
+ wrapped = TavilySearch(
+ max_results=runtime.context.max_search_results)
return await wrapped.ainvoke({"query": query})
-TOOLS = [search]`;
+TOOLS: List[Callable[..., Any]] = [search]`;
-const agentYaml = `spec_version: 0.1.0
+const toToolFiles = `# tools/search.yaml (the contract)
+name: search
+input_schema:
+ type: object
+ properties:
+ query: { type: string }
+ required: [query]
+implementation:
+ type: script
+ path: search.py
+ runtime: python3
+
+# tools/search.py (the impl — reads stdin, writes stdout)
+if __name__ == "__main__":
+ data = json.loads(sys.stdin.read())
+ print(json.dumps(search(data.get("query", ""))))`;
+
+/* ═══════════════════ PART 3 — full OpenGAP output ═══════════════════ */
+
+const outputTree = `react-agent/ (OpenGAP)
+├── agent.yaml ← manifest: model, runtime, refs
+├── SOUL.md ← identity
+├── RULES.md ← guardrails
+├── skills/
+│ └── reason-and-act/
+│ └── SKILL.md ← the ReAct loop
+└── tools/
+ ├── search.yaml ← tool schema
+ └── search.py ← tool implementation`;
+
+const fullAgentYaml = `spec_version: "0.1.0"
name: react-agent
-version: 0.1.0
-description: ReAct agent that searches the web to answer questions accurately
+version: 1.0.0
+description: A ReAct (Reasoning and Action) agent that iteratively reasons about user queries and executes tool actions to provide accurate answers.
+
model:
- preferred: claude-sonnet-4-6
+ preferred: anthropic:claude-sonnet-4-5-20250929
+ fallback:
+ - openai:gpt-4o
+
+runtime:
+ max_turns: 50
+ timeout: 300
+
+skills:
+ - reason-and-act
+
tools:
- search`;
-const soulMd = `# Soul
+const fullSoul = `# Soul
## Core Identity
-You are a helpful AI assistant.`;
+You are a helpful AI assistant.
+
+I am a ReAct (Reasoning and Action) agent. I work by iteratively reasoning about user queries and executing actions — thinking through problems step by step, choosing the right tools, observing results, and continuing until I can provide a complete and accurate answer.
+
+## Purpose
+Given a user query, I reason about what information or actions are needed, execute appropriate tools, observe results, and repeat until I can deliver a final answer. I am designed for complex problem-solving tasks that may require multiple rounds of information gathering.
+
+My process:
+1. Take a user **query** as input
+2. Reason about the query and decide on an action
+3. Execute the chosen action using available tools
+4. Observe the result of the action
+5. Repeat steps 2–4 until I can provide a final answer
+
+## Communication Style
+Clear, helpful, and direct. I provide well-structured responses grounded in the information I have gathered. When I cannot find an answer within the allowed steps, I say so honestly.
+
+## Values & Principles
+- **Accuracy** — I search for and verify information before answering
+- **Transparency** — I am clear about what I found and how I reached my conclusions
+- **Completeness** — I iterate until I have a thorough answer, not just the first result
+- **Honesty** — If I cannot find an answer within the allotted steps, I say so rather than guessing
+
+## Domain Expertise
+- General web research and information retrieval
+- Answering questions about current events (via Tavily search)
+- Multi-step reasoning and tool use
+- ReAct loop reasoning: Thought → Action → Observation cycles
+
+## Collaboration Style
+I work autonomously through my ReAct loop without requiring confirmation for individual tool calls. I surface my final answer directly. If I exhaust the maximum number of reasoning steps without finding an answer, I inform the user honestly rather than fabricating a response.
+
+## Configuration
+The following aspects of my behavior can be customized at runtime:
+- **system_prompt**: Override my instructions entirely (default: "You are a helpful AI assistant.")
+- **model**: The underlying language model to use (default: anthropic/claude-sonnet-4-5-20250929, format: provider/model-name)
+- **max_search_results**: Maximum number of Tavily search results to return per query (default: 10)`;
+
+const fullRules = `# Rules
+
+## Must Always
+- Append the current system time (ISO 8601, UTC) to the system prompt at the start of each turn
+- Continue the Reason → Act → Observe loop until a final answer is reached or the step limit is hit
+- Return tool call results to the model before producing a final response
+- Use the \`search\` tool to look up current information rather than relying solely on training data
+
+## Must Never
+- Fabricate search results or pretend to have executed a tool call
+- Exceed the configured maximum number of reasoning steps (recursion limit); if the limit is reached while a tool call is still pending, respond with: "Sorry, I could not find an answer to your question in the specified number of steps."
+- Ignore tool errors — always surface them back to the model so it can reason about them
+
+## Output Constraints
+- Final responses are in natural language prose unless the user explicitly asks for a specific format
+- When the step limit is reached mid-tool-call, return the fallback message verbatim: "Sorry, I could not find an answer to your question in the specified number of steps."
+
+## Interaction Boundaries
+- Search queries are passed to the Tavily search engine; the number of results returned is bounded by \`max_search_results\` (default: 10)
+- Model selection is constrained to providers supported by LangChain's \`init_chat_model\` (anthropic, openai, etc.)`;
+
+const fullSkill = `---
+name: reason-and-act
+description: "Core ReAct reasoning loop: reason about a user query, decide on an action,
+ execute it using available tools, observe the result, and repeat until a final answer
+ can be given. Use for any user question that may require web search or multi-step
+ reasoning. Triggers on: any user query, question, research request, look up, find,
+ search, tell me about, what is, how does."
+allowed-tools: search
+metadata:
+ version: "1.0.0"
+ category: reasoning
+---
+
+# Reason and Act (ReAct Loop)
+
+Implements the ReAct (Reasoning and Action) pattern: iteratively reason about the user's query, choose and execute tools, observe results, and continue until a complete answer is ready.
+
+## Step 1: Reason
+Read the user's query and any prior conversation messages. Think through:
+- What is being asked?
+- What information is already available in context?
+- What additional information is needed?
+- Which tool (if any) should be called next?
+
+## Step 2: Act
+If additional information is needed, call the \`search\` tool with a precise search query derived from the user's request. The tool returns up to \`max_search_results\` web results (title, URL, snippet).
+
+If sufficient information is already available, skip to Step 4.
+
+## Step 3: Observe
+Read the tool results. Assess:
+- Did the search return relevant information?
+- Is the answer now complete, or is another search needed?
+- Are there follow-up queries that would improve the answer?
+
+If another iteration is needed, return to Step 1 with the updated context.
+
+## Step 4: Respond
+Once enough information has been gathered, compose a clear, accurate final answer for the user. Ground the answer in the search results collected.
+
+## Step Limit Handling
+If the maximum number of reasoning steps is reached while a tool call is still pending, do not fabricate a response. Return the following message verbatim:
+> "Sorry, I could not find an answer to your question in the specified number of steps."`;
+
+const fullToolYaml = `name: search
+description: Search for general web results using the Tavily search engine. Particularly useful for answering questions about current events. Returns comprehensive, accurate, and trusted results.
+version: 1.0.0
-const toolYaml = `name: search
-description: Search the web using Tavily for up-to-date information.
input_schema:
type: object
properties:
query:
type: string
- description: The search query
+ description: The search query string to look up on the web
required:
- query
+
+output_schema:
+ type: object
+ properties:
+ results:
+ type: array
+ description: List of search results from Tavily
+ items:
+ type: object
+ properties:
+ title:
+ type: string
+ description: Title of the search result
+ url:
+ type: string
+ description: URL of the search result
+ content:
+ type: string
+ description: Snippet or content excerpt from the result
+
implementation:
type: script
- path: tools/search.py
+ path: search.py
runtime: python3
- timeout: 30`;
-
-const validateCmd = `opengap validate -d ./react-agent-opengap
-opengap info -d ./react-agent-opengap`;
-
-const mapping = [
- ["SYSTEM_PROMPT in prompts.py", "SOUL.md"],
- ["runtime.context.model (from configuration.py)", "agent.yaml → model.preferred"],
- ["TOOLS list — function names", "agent.yaml → tools[]"],
- ["Each tool function + docstring + typed args", "tools/.yaml"],
- ["implementation.path → existing tools.py function", "tools/.yaml → implementation.path"],
- ["StateGraph nodes + edges in graph.py", "stays in framework — runtime orchestration"],
+ timeout: 30
+
+annotations:
+ read_only: true
+ idempotent: true
+ cost: low`;
+
+const fullToolPy = `"""Search tool implementation using Tavily search engine.
+
+This tool performs a web search using the Tavily search engine, which is designed
+to provide comprehensive, accurate, and trusted results. It is particularly useful
+for answering questions about current events.
+
+Environment variables required:
+ TAVILY_API_KEY: Your Tavily API key (https://app.tavily.com/sign-in)
+
+Optional environment variables:
+ MAX_SEARCH_RESULTS: Maximum number of results to return (default: 10)
+"""
+
+import json
+import os
+import sys
+from typing import Any, Optional
+
+# TRANSLATION NOTE: Original used langchain_tavily.TavilySearch via LangGraph runtime context
+# for max_results. Here MAX_SEARCH_RESULTS is read from environment variable with default of 10.
+
+
+def search(query: str) -> Optional[dict[str, Any]]:
+ """Search for general web results.
+
+ This function performs a search using the Tavily search engine, which is designed
+ to provide comprehensive, accurate, and trusted results. It's particularly useful
+ for answering questions about current events.
+
+ Args:
+ query: The search query string.
+
+ Returns:
+ A dictionary containing search results from Tavily.
+ """
+ try:
+ from tavily import TavilyClient
+ except ImportError:
+ raise ImportError(
+ "tavily-python is required. Install it with: pip install tavily-python"
+ )
+
+ api_key = os.environ.get("TAVILY_API_KEY")
+ if not api_key:
+ raise ValueError("TAVILY_API_KEY environment variable is not set")
+
+ max_results = int(os.environ.get("MAX_SEARCH_RESULTS", "10"))
+
+ client = TavilyClient(api_key=api_key)
+ response = client.search(query=query, max_results=max_results)
+ return response
+
+
+if __name__ == "__main__":
+ # Entry point for OpenGAP script execution
+ # Reads JSON input from stdin: {"query": "..."}
+ input_data = json.loads(sys.stdin.read())
+ query = input_data.get("query", "")
+ result = search(query)
+ print(json.dumps(result))`;
+
+const validateCmd = `$ opengap validate
+✓ agent.yaml valid (spec 0.1.0)
+✓ SOUL.md present
+✓ RULES.md present
+✓ skills/reason-and-act/SKILL.md valid frontmatter
+✓ tools/search.yaml schema ok → search.py
+ react-agent is ready.`;
+
+/* ───────────────────────── Building blocks ───────────────────────── */
+
+const buckets = [
+ { icon: FileText, title: "Identity", file: "SOUL.md", desc: "Who the agent is" },
+ { icon: Shield, title: "Rules", file: "RULES.md", desc: "Hard guardrails" },
+ { icon: Workflow, title: "Orchestration", file: "skills/", desc: "How it reasons" },
+ { icon: Settings, title: "Config & Tools", file: "agent.yaml · tools/", desc: "Model & capabilities" },
];
-const steps = [
- { step: "1", desc: "Copy SYSTEM_PROMPT from prompts.py into SOUL.md." },
- { step: "2", desc: "Take the model name from configuration.py → write to agent.yaml → model.preferred." },
- { step: "3", desc: "For each function in the TOOLS list, add a kebab-case name to agent.yaml → tools (search stays search, web_search becomes web-search)." },
- { step: "4", desc: "Create tools/.yaml for each tool — use the docstring as description, typed parameters as input_schema, and point implementation.path to your existing tool file." },
- { step: "5", desc: "StateGraph, ToolNode, routing logic, and State TypedDicts stay in graph.py — they are runtime execution wiring with no OpenGAP equivalent." },
- { step: "6", desc: "Run opengap validate to confirm the structure is correct." },
+const mapAtAGlance: [string, string][] = [
+ ["prompts.py", "SOUL.md"],
+ ["graph.py control logic", "RULES.md"],
+ ["graph.py StateGraph loop", "skills/reason-and-act/SKILL.md"],
+ ["context.py + langgraph.json", "agent.yaml"],
+ ["tools.py", "tools/search.yaml + search.py"],
];
+function PartHeader({ num, label, title, subtitle }: { num: string; label: string; title: string; subtitle: string }) {
+ return (
+
+
- Based on langchain-ai/react-agent — the official LangGraph ReAct agent template.
- The agent identity lives in prompts.py, tools are exported from tools.py,
- and the graph wiring in graph.py stays in the framework.
+
+ A step-by-step guide to converting a LangGraph agent into OpenGAP format by hand. We work through one real
+ project end to end — every file, the exact mapping, and the finished result — so you can follow the same
+ steps for your own agent.
-
-
Part 1 — The LangGraph project
-
ReAct agent with Tavily web search, designed for LangGraph Studio:
-
-
-
-
prompts.py:
-
+ {/* Example + its use case */}
+
+
+
+
+ The example
-
-
tools.py:
-
-
-
-
graph.py excerpt:
-
+
+ langchain-ai/react-agent — the canonical LangGraph
+ ReAct starter. A single graph that cycles between an LLM node and a tool node, with the
+ Tavily web-search tool wired in.
+
+
+
+
+ Use case: answer user questions — especially about
+ current events — by reasoning step by step: decide what to look up, search the web, observe the results,
+ and repeat until it can give a grounded answer
+ (Reason → Act → Observe → Respond).
+
+ state.py — State,{" "}
+ InputState, IsLastStep and the{" "}
+ add_messages reducer — has no OpenGAP file. Message accumulation, the
+ step counter, and graph execution are handled by the host runtime, not by you. You describe{" "}
+ what the agent does (identity, rules, skill, tools); the runtime owns how the loop runs.
+
+ From the agent directory, run opengap validate to confirm the
+ manifest, skill frontmatter, and tool schemas all resolve before you run the agent.
+
From 6d5ede787a4a374123ce2c8ca212f96f0b00fd8b Mon Sep 17 00:00:00 2001
From: Amogh Sunil
Date: Thu, 11 Jun 2026 18:24:01 +0530
Subject: [PATCH 3/8] rewrote all cookbooks in new three-part format
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Updated CrewAI, AutoGen, OpenAI Agents SDK, Claude SDK, Google ADK,
Claude Code, Cursor, Gemini CLI, Codex, and OpenCode cookbooks to use
PartHeader, CollapsibleCode, and ConversionStep components — matching
the LangGraph cookbook design with source files, mapping, and full output.
---
.../opengap/cookbook/CookbookAutoGen.tsx | 1107 ++++++++++++++---
.../opengap/cookbook/CookbookClaudeCode.tsx | 643 ++++++++--
.../opengap/cookbook/CookbookClaudeSDK.tsx | 956 ++++++++++++--
.../opengap/cookbook/CookbookCodex.tsx | 625 ++++++++--
.../opengap/cookbook/CookbookCrewAI.tsx | 978 +++++++++++++--
.../opengap/cookbook/CookbookCursor.tsx | 648 +++++++++-
.../opengap/cookbook/CookbookGeminiCLI.tsx | 474 ++++++-
.../opengap/cookbook/CookbookGoogleADK.tsx | 1050 +++++++++++++---
.../opengap/cookbook/CookbookOpenAIAgents.tsx | 1027 ++++++++++++---
.../opengap/cookbook/CookbookOpenCode.tsx | 479 ++++++-
10 files changed, 6887 insertions(+), 1100 deletions(-)
diff --git a/src/components/opengap/cookbook/CookbookAutoGen.tsx b/src/components/opengap/cookbook/CookbookAutoGen.tsx
index f6884db..05e04cb 100644
--- a/src/components/opengap/cookbook/CookbookAutoGen.tsx
+++ b/src/components/opengap/cookbook/CookbookAutoGen.tsx
@@ -1,244 +1,1033 @@
-import { motion } from "framer-motion";
+import { motion, AnimatePresence } from "framer-motion";
+import { ArrowRight, FileText, Shield, Workflow, Settings, CheckCircle2, ChevronDown, BookOpen, Target, Package } from "lucide-react";
+import { useState } from "react";
import { CodeBlock } from "@/components/gitAgent/CodeBlock";
-const projectStructure = `autogen-agentchat two-agent reflection pattern
-├── primary_agent ← AssistantAgent: drafts and refines responses
-└── critic_agent ← AssistantAgent: provides feedback until satisfied`;
+/* ═══════════════════ PART 1 — full AutoGen source ═══════════════════ */
-const agentPy = `# Based on microsoft/autogen AgentChat teams tutorial
+const sourceTree = `autogen-multi-agent/ (AutoGen AgentChat)
+├── agentchat_pipeline.py ← AssistantAgent + CodeExecutorAgent + RoundRobinGroupChat
+├── swarm_example.py ← Swarm with HandoffMessage between agents
+├── requirements.txt ← autogen-agentchat, autogen-ext[openai,docker]
+└── .env.example ← OPENAI_API_KEY`;
+
+const srcAgentChatPy = `# agentchat_pipeline.py
import asyncio
-from autogen_agentchat.agents import AssistantAgent
-from autogen_agentchat.conditions import TextMentionTermination
+from autogen_agentchat.agents import AssistantAgent, CodeExecutorAgent
+from autogen_agentchat.conditions import MaxMessageTermination, TextMentionTermination
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.ui import Console
+from autogen_core.tools import FunctionTool
+from autogen_ext.code_executors.local import LocalCommandLineCodeExecutor
from autogen_ext.models.openai import OpenAIChatCompletionClient
-# Define a tool for the primary agent
-async def web_search(query: str) -> str:
- """Find information on the web about a given topic."""
- # real impl calls a search API
- return f"Search results for: {query}"
+model_client = OpenAIChatCompletionClient(model="gpt-4o")
-model_client = OpenAIChatCompletionClient(
- model="gpt-4o-2024-08-06",
-)
+# Define a typed Python function — schema auto-generated from annotations
+async def get_stock_price(
+ ticker: str,
+ date: str, # "Date in YYYY-MM-DD format"
+) -> float:
+ """Get the stock price for a given ticker on a specific date."""
+ import random
+ return round(random.uniform(10, 500), 2)
-# Primary agent drafts responses using tools
-primary_agent = AssistantAgent(
- name="primary",
+tool = FunctionTool(get_stock_price, description="Fetch stock price for a ticker on a date.")
+
+# Primary LLM agent: drafts plans, calls tools, reflects on results
+assistant = AssistantAgent(
+ name="assistant",
model_client=model_client,
- tools=[web_search],
- system_message="""You are a helpful research assistant.
-Search the web to find accurate, up-to-date information before answering.
-Cite your sources. Revise your response based on the critic's feedback.""",
+ tools=[tool],
+ system_message="You are a helpful assistant. Analyse the task, write a plan, and use tools when needed.",
+ reflect_on_tool_use=True,
+ model_client_stream=True,
)
-# Critic agent reviews and provides feedback
-critic_agent = AssistantAgent(
- name="critic",
- model_client=model_client,
- system_message="""You are a constructive critic. Review the primary agent's response for:
-- Factual accuracy and source quality
-- Completeness and clarity
-- Any missing context or caveats
+# Code executor: extracts and runs code blocks from the conversation
+code_executor = LocalCommandLineCodeExecutor(work_dir="workspace", timeout=60)
-Respond with 'APPROVE' once the response meets the quality bar.""",
+code_executor_agent = CodeExecutorAgent(
+ name="code_executor",
+ code_executor=code_executor,
+ sources=["assistant"], # Only execute code produced by the assistant
)
-# Stop when critic approves
-termination = TextMentionTermination("APPROVE")
+# Terminate after 10 messages or when the assistant says "DONE"
+termination = MaxMessageTermination(10) | TextMentionTermination("DONE")
+
team = RoundRobinGroupChat(
- [primary_agent, critic_agent],
+ participants=[assistant, code_executor_agent],
+ termination_condition=termination,
+)
+
+async def main():
+ await Console(team.run_stream(task="Write and run a Python script that prints the Fibonacci sequence up to 100."))
+
+asyncio.run(main())`;
+
+const srcSwarmPy = `# swarm_example.py
+import asyncio
+from autogen_agentchat.agents import AssistantAgent
+from autogen_agentchat.conditions import HandoffTermination, MaxMessageTermination
+from autogen_agentchat.messages import HandoffMessage
+from autogen_agentchat.teams import Swarm
+from autogen_ext.models.openai import OpenAIChatCompletionClient
+
+model_client = OpenAIChatCompletionClient(model="gpt-4o")
+
+# Triage agent hands off to specialist based on task type
+triage_agent = AssistantAgent(
+ name="triage",
+ model_client=model_client,
+ handoffs=["specialist"],
+ system_message="""Assess the incoming task. If it requires deep domain expertise,
+hand it off to the specialist via HandoffMessage. Otherwise answer directly.""",
+)
+
+# Specialist agent handles delegated sub-tasks
+specialist_agent = AssistantAgent(
+ name="specialist",
+ model_client=model_client,
+ handoffs=["triage"],
+ system_message="""You are a domain specialist. Handle the delegated task thoroughly.
+Hand back to triage when done.""",
+)
+
+# Terminate when a handoff targets a non-participant, or after 20 messages
+termination = HandoffTermination(target="user") | MaxMessageTermination(20)
+
+swarm = Swarm(
+ participants=[triage_agent, specialist_agent],
termination_condition=termination,
)
async def main():
- await Console(team.run_stream(
- task="What are the key differences between LangGraph and AutoGen?"
- ))
+ result = await swarm.run(task="Analyse the Q3 earnings report and identify key risks.")
+ print(result.messages[-1].content)
asyncio.run(main())`;
-const agentYaml = `spec_version: 0.1.0
-name: primary
-version: 0.1.0
-description: Research assistant that searches the web and refines responses based on feedback
+/* ═══════════════════ PART 2 — paired mapping excerpts ═══════════════════ */
+
+const fromAgentDef = `# agentchat_pipeline.py
+assistant = AssistantAgent(
+ name="assistant",
+ model_client=model_client,
+ tools=[tool],
+ system_message="""You are a helpful assistant.
+Analyse the task, write a plan, and use tools
+when needed.""",
+ reflect_on_tool_use=True,
+ model_client_stream=True,
+)`;
+
+const toSoul = `# agents/assistant/SOUL.md
+
+## Core Identity
+I am the AssistantAgent — the primary LLM-backed
+participant in an AgentChat team. I receive messages,
+reason over the conversation history, call registered
+tools, optionally reflect on tool results, and return
+a TextMessage, ToolCallSummaryMessage, or HandoffMessage.
+
+## Purpose
+To be the intelligent, reasoning participant in a
+multi-agent team. I process task prompts, use tools
+to gather information or take actions, and produce
+well-reasoned responses.
+
+## Collaboration Style
+In a RoundRobinGroupChat I contribute on every turn.
+In a Swarm I delegate via HandoffMessage. I maintain
+my own message history between turns.`;
+
+const fromRulesCode = `# agentchat_pipeline.py (control constraints)
+# Must include termination condition — prevents infinite loops
+termination = MaxMessageTermination(10) \\
+ | TextMentionTermination("DONE")
+
+# tool + workbench are mutually exclusive
+# tools=[tool] — ok
+# workbench=wb — ok
+# both — runtime error
+
+# Each agent instance must be unique per team
+# Sharing across concurrent tasks is NOT safe`;
+
+const toRules = `# RULES.md
+## Must Always
+- Pass a system_message to AssistantAgent to define
+ its role and behavior before adding it to a team
+- Include a TerminationCondition when creating any
+ team to prevent infinite loops
+- Use the async API (await team.run(...)) — all
+ AgentChat APIs are async-first
+- Save and restore team state via save_state() /
+ load_state() for long-running workflows
+
+## Must Never
+- Hardcode API keys — source from environment vars
+- Use tools= and workbench= simultaneously on the
+ same AssistantAgent — they are mutually exclusive
+- Call team.run() after termination without calling
+ team.reset() first
+- Share a single agent instance across multiple
+ concurrent tasks`;
+
+const fromTeamOrch = `# agentchat_pipeline.py (the AgentChat loop)
+team = RoundRobinGroupChat(
+ participants=[assistant, code_executor_agent],
+ termination_condition=termination,
+)
+
+# Streaming run — emit messages as they arrive
+await Console(team.run_stream(task="..."))
+
+# swarm_example.py (handoff-based routing)
+swarm = Swarm(
+ participants=[triage_agent, specialist_agent],
+ termination_condition=HandoffTermination(target="user")
+ | MaxMessageTermination(20),
+)`;
+
+const toSkill = `# skills/assistant-agent/SKILL.md
+---
+name: assistant-agent
+description: "Configure and run an LLM-backed
+ AssistantAgent with tools, handoffs, and structured
+ output."
+allowed-tools: function-tool agent-tool team-tool
+ http-tool mcp-tool
+---
+
+## Step 1: Create the Model Client
+Instantiate OpenAIChatCompletionClient(model="gpt-4o")
+
+## Step 2: Define Tools
+Typed Python functions — schema auto-generated from
+type annotations + docstrings.
+
+## Step 3: Configure Handoffs (Optional)
+Handoff(target="agent", message="Delegating...")
+
+## Step 4: Construct and run the Agent
+AssistantAgent(name, model_client, system_message,
+ tools, reflect_on_tool_use, model_client_stream)`;
+
+const fromModelConfig = `# agentchat_pipeline.py
+from autogen_ext.models.openai import OpenAIChatCompletionClient
+
+model_client = OpenAIChatCompletionClient(
+ model="gpt-4o",
+)
+
+# Code executor agent uses the same model client
+assistant = AssistantAgent(
+ name="assistant",
+ model_client=model_client,
+ ...
+)`;
+
+const toAgentYaml = `# agent.yaml
+spec_version: "0.1.0"
+name: autogen-multi-agent-framework
+version: 1.0.0
+model:
+ preferred: openai:gpt-4o
+ fallback:
+ - openai:gpt-4o-mini
+skills:
+ - assistant-agent
+ - code-executor-agent
+ - society-of-mind
+ - message-filtering
+ - tool-use
+ - streaming-response
+tools:
+ - function-tool
+ - agent-tool
+ - team-tool
+ - http-tool
+ - mcp-tool
+ - code-execution
+agents:
+ assistant:
+ description: LLM-backed agent that generates
+ responses and calls tools
+ delegation:
+ mode: auto
+ code-executor:
+ description: Executes code blocks in a sandboxed
+ environment
+ delegation:
+ mode: explicit`;
+
+const fromToolDef = `# agentchat_pipeline.py
+from autogen_core.tools import FunctionTool
+
+async def get_stock_price(
+ ticker: str,
+ date: str,
+) -> float:
+ """Get the stock price for a given ticker
+ on a specific date."""
+ ...
+
+tool = FunctionTool(
+ get_stock_price,
+ description="Fetch stock price for a ticker.",
+)
+
+assistant = AssistantAgent(
+ name="assistant",
+ model_client=model_client,
+ tools=[tool],
+)`;
+
+const toToolFiles = `# tools/function-tool.yaml (the contract)
+name: function-tool
+input_schema:
+ type: object
+ properties:
+ function_name: { type: string }
+ description: { type: string }
+ strict: { type: boolean }
+ required: [function_name, description]
+implementation:
+ type: script
+ path: function-tool.py
+ runtime: python3
+
+# tools/function-tool.py (wraps FunctionTool)
+tool = FunctionTool(
+ func=get_stock_price,
+ description="Fetch stock price...",
+)
+result = await tool.run_json(
+ json.loads(sys.stdin.read()),
+ CancellationToken()
+)`;
+
+/* ═══════════════════ PART 3 — full OpenGAP output ═══════════════════ */
+
+const outputTree = `autogen-multi-agent-framework/ (OpenGAP)
+├── agent.yaml ← manifest: model, skills, sub-agents, tools
+├── SOUL.md ← framework identity
+├── RULES.md ← guardrails
+├── skills/
+│ ├── assistant-agent/SKILL.md
+│ ├── code-executor-agent/SKILL.md
+│ ├── society-of-mind/SKILL.md
+│ ├── message-filtering/SKILL.md
+│ ├── tool-use/SKILL.md
+│ └── streaming-response/SKILL.md
+├── agents/
+│ ├── user-proxy/agent.yaml
+│ ├── assistant/
+│ │ ├── agent.yaml
+│ │ └── SOUL.md
+│ ├── code-executor/agent.yaml
+│ ├── society-of-mind/agent.yaml
+│ └── message-filter/agent.yaml
+├── tools/
+│ ├── function-tool.yaml + function-tool.py
+│ ├── agent-tool.yaml + agent-tool.py
+│ ├── team-tool.yaml + team-tool.py
+│ ├── http-tool.yaml
+│ ├── mcp-tool.yaml
+│ └── code-execution.yaml + code-execution.py
+└── workflows/
+ ├── agentchat-pipeline.yaml
+ └── swarm-handoff.yaml`;
+
+const fullAgentYaml = `spec_version: "0.1.0"
+name: autogen-multi-agent-framework
+version: 1.0.0
+description: A multi-agent AI framework supporting role-based conversation teams with round-robin, selector, swarm, graph-flow, and magentic-one orchestration patterns.
+
model:
- preferred: gpt-4o-2024-08-06
+ preferred: openai:gpt-4o
+ fallback:
+ - openai:gpt-4o-mini
+
+runtime:
+ max_turns: 50
+ timeout: 300
+
+skills:
+ - assistant-agent
+ - code-executor-agent
+ - society-of-mind
+ - message-filtering
+ - tool-use
+ - streaming-response
+
tools:
- - web-search
+ - function-tool
+ - agent-tool
+ - team-tool
+ - http-tool
+ - mcp-tool
+ - code-execution
+
agents:
- critic:
- description: Reviews research responses for accuracy, completeness, and clarity
+ user-proxy:
+ description: Represents a human user in a chat session; handles user input and forwards to agents
+ delegation:
+ mode: explicit
+ assistant:
+ description: LLM-backed agent that generates responses and calls tools on behalf of the team
+ delegation:
+ mode: auto
+ code-executor:
+ description: Executes code blocks produced by other agents in a sandboxed environment
delegation:
- mode: auto`;
+ mode: explicit
+ society-of-mind:
+ description: Meta-agent that orchestrates an inner team and synthesises their output into a single response
+ delegation:
+ mode: auto
+ message-filter:
+ description: Wrapper agent that filters the message history before passing it to an inner agent
+ delegation:
+ mode: explicit
+
+delegation:
+ mode: auto
-const soulMd = `# Soul
+tags:
+ - multi-agent
+ - autogen
+ - agentchat
+ - conversation
+ - tool-use
+ - code-execution`;
+
+const fullSoul = `# Soul
## Core Identity
-You are a helpful research assistant.
+I am AutoGen — a Microsoft open-source framework for creating multi-agent AI applications that can act autonomously or work alongside humans. I embody a modular, conversation-centric architecture where multiple AI agents collaborate, delegate, and coordinate to solve complex tasks. My design separates agent identity (who), team orchestration (how agents take turns), tools (what agents can do), and termination logic (when to stop), making each concern independently composable.
+
+My primary packages are:
+- **autogen-core**: The foundational runtime, agent model, subscriptions, message routing, tool abstractions, code execution, and memory interfaces.
+- **autogen-agentchat**: High-level AgentChat API — AssistantAgent, UserProxyAgent, CodeExecutorAgent, SocietyOfMindAgent, MessageFilterAgent, teams (RoundRobinGroupChat, SelectorGroupChat, Swarm, MagenticOneGroupChat, GraphFlow), termination conditions, and message types.
+- **autogen-ext**: Pluggable extensions — OpenAI/Azure/Anthropic/Ollama/LlamaCpp model clients, Docker/local code executors, MCP tools, HTTP tools, GraphRAG, LangChain adapters.
## Purpose
-Find accurate, up-to-date information by searching the web before answering.
-Revise your response based on the critic's feedback.`;
+To enable developers to build and run multi-agent AI applications where specialized agents — each with their own system message, model, and tools — collaborate under a shared team orchestration strategy. Common use cases include: coding assistants, research pipelines, debate/review loops, automated QA workflows, and human-in-the-loop task execution.
-const rulesMd = `# Rules
+## Communication Style
+Structured and transparent. Agent responses are typed messages (TextMessage, HandoffMessage, ToolCallSummaryMessage, StructuredMessage). The framework emits trace logs and event logs for full observability. Streaming mode (model_client_stream=True) delivers incremental token chunks for real-time UIs.
-- Always search before answering factual questions
-- Always cite sources
-- Never fabricate URLs or citations`;
+## Values & Principles
+- **Composability** — every component (agent, team, tool, termination condition, model client) is independently swappable and serializable
+- **Semantic fidelity** — agents preserve their system_message exactly; no paraphrasing or summarizing of instructions
+- **Transparency** — full message history is shared across team participants; each turn is observable via event logging
+- **Async-first** — all agent and team APIs are async (run(), run_stream(), on_messages(), on_messages_stream())
+- **Safety** — code execution is sandboxed (Docker or local), human-in-the-loop is supported via UserProxyAgent and HandoffTermination
-const subCriticYaml = `spec_version: 0.1.0
-name: critic
-version: 0.1.0
-description: Reviews research responses for accuracy, completeness, and clarity
-model:
- preferred: gpt-4o-2024-08-06`;
+## Collaboration Style
+I operate as a framework, not a single agent. When a task is submitted to a team via team.run(task=...), I orchestrate agent turns according to the chosen team strategy, route messages, execute tools, and terminate when conditions are met. Human-in-the-loop is supported by including a UserProxyAgent in the team.`;
+
+const fullRules = `# Rules
+
+## Must Always
+- Pass a system_message to AssistantAgent to define its role and behavior before adding it to a team
+- Use typed Python functions with complete type annotations and docstrings as tools — the framework auto-generates the JSON schema from them
+- Include a TerminationCondition when creating any team to prevent infinite loops
+- Use the async API (await team.run(...)) — all AgentChat APIs are async-first
+- Save and restore team state via save_state() / load_state() for long-running or resumable workflows
+- Use CancellationToken for all operations that must support external interruption
+- Serialize agent and team configs via dump_component() for reproducibility and deployment
+
+## Must Never
+- Hardcode API keys — source them from environment variables (OPENAI_API_KEY, AZURE_OPENAI_API_KEY, etc.)
+- Share a single agent instance across multiple concurrent tasks or coroutines — agents are not thread-safe
+- Use tools= and workbench= simultaneously on the same AssistantAgent — they are mutually exclusive
+- Use Docker code execution (DockerCommandLineCodeExecutor) without verifying the Docker daemon is running
+- Create teams with duplicate participant names — participant names must be unique
+- Call team.run() after termination without calling team.reset() first — teams must be reset between runs
+
+## Output Constraints
+- Agent responses are typed: TextMessage for plain text, HandoffMessage for agent transfers, ToolCallSummaryMessage for tool results, StructuredMessage[T] for Pydantic-typed structured output
+- Tool return values must be strings or JSON-serializable objects; complex returns should be formatted as strings
+- When reflect_on_tool_use=True, the agent makes a second LLM call after tool execution to synthesize the final answer
-const subCriticSoul = `# Soul
+## Interaction Boundaries
+- CodeExecutorAgent only executes code produced by preceding agents in the same team — it does not generate code itself unless a model_client is also provided
+- Swarm teams only route via HandoffMessage — agents must explicitly declare handoffs to other participants
+- SelectorGroupChat uses an LLM call per turn to select the next speaker — this incurs additional token cost
+- SocietyOfMindAgent resets its inner team after every call — no state persists across calls to this agent`;
+
+const fullAssistantSoul = `# Soul
## Core Identity
-You are a constructive critic reviewing research responses.
+I am the AssistantAgent — the primary LLM-backed participant in an AgentChat team. I receive messages, reason over the conversation history, call registered tools (concurrently when multiple tool calls are returned), optionally reflect on tool results to synthesize a final answer, and return either a TextMessage, a ToolCallSummaryMessage, a StructuredMessage, or a HandoffMessage.
+
+My behavior is governed by a system_message provided at construction time. When no system_message is set, I behave as a general-purpose helpful assistant.
+
+## Purpose
+To be the intelligent, reasoning participant in a multi-agent team. I process task prompts, use tools to gather information or take actions, and produce well-reasoned responses. I delegate to other agents via handoffs when a subtask is better handled by a specialist.
+
+## Communication Style
+Clear, helpful, and precise. When tools are used, I summarize results unless reflect_on_tool_use=True, in which case I synthesize a coherent final answer.
+
+## Values & Principles
+- **Tool-augmented reasoning** — I use tools to extend my capabilities beyond pure LLM generation
+- **Context efficiency** — I respect model_context limits to stay within token budgets
+- **Handoff clarity** — I only trigger handoffs when explicitly configured and when the task warrants delegation
-## Review Criteria
-- Factual accuracy and source quality
-- Completeness and clarity
-- Missing context or caveats
+## Collaboration Style
+I am the most commonly used participant type. In a RoundRobinGroupChat I contribute on every turn. In a SelectorGroupChat I am selected by the LLM orchestrator. In a Swarm I delegate via HandoffMessage. I maintain my own message history between turns.`;
-## Behavior
-Respond with 'APPROVE' once the response meets the quality bar.`;
+const fullAssistantSkill = `---
+name: assistant-agent
+description: "Configure and run an LLM-backed AssistantAgent with tools, handoffs, and structured output. Use when you need to create an intelligent agent that can call tools, reflect on results, delegate to other agents, and produce typed responses. Triggers on: create assistant, add agent, configure agent, LLM agent, tool-calling agent, agent with handoffs."
+allowed-tools: function-tool agent-tool team-tool http-tool mcp-tool
+metadata:
+ version: "1.0.0"
+ category: multi-agent
+---
+
+# Assistant Agent
+
+Configure and operate an AssistantAgent from autogen_agentchat.agents.
+
+## Step 1: Create the Model Client
+Instantiate a model client from autogen_ext.models.openai:
+ model_client = OpenAIChatCompletionClient(model="gpt-4o")
+
+For Azure: use AzureOpenAIChatCompletionClient. For Anthropic, Ollama, or LlamaCpp, use the corresponding client from autogen_ext.models.
+
+## Step 2: Define Tools
+Create Python functions with full type annotations and docstrings. Pass them directly to the tools parameter — the framework generates JSON schemas automatically.
+
+For complex tools, subclass BaseTool from autogen_core.tools.
+
+## Step 3: Configure Handoffs (Optional)
+Define Handoff objects or pass target agent names as strings to enable the agent to delegate mid-conversation.
+
+## Step 4: Construct the Agent
+ agent = AssistantAgent(
+ name="assistant",
+ model_client=model_client,
+ system_message="You are a helpful assistant.",
+ tools=[...],
+ reflect_on_tool_use=True,
+ model_client_stream=True,
+ )
+
+## Step 5: Run or Stream
+ result = await agent.run(task="...")
+ async for message in agent.run_stream(task="..."): ...`;
+
+const fullFunctionToolYaml = `name: function-tool
+description: Wraps a plain Python function as a callable tool for AssistantAgent. The JSON schema is auto-generated from type annotations and docstrings. Supports both sync and async functions.
+version: 1.0.0
-const toolWebSearch = `name: web-search
-description: Find information on the web about a given topic.
input_schema:
type: object
properties:
- query:
+ function_name:
type: string
- description: The search topic or question
+ description: The name of the Python function to wrap as a tool
+ description:
+ type: string
+ description: Human-readable description of what the tool does (used in LLM prompts)
+ strict:
+ type: boolean
+ description: If true, only explicitly declared parameters are included in the schema
required:
- - query
+ - function_name
+ - description
+
+output_schema:
+ type: object
+ properties:
+ result:
+ type: string
+ description: The return value of the function, converted to a string
+
implementation:
type: script
- path: tools/web_search.py
+ path: function-tool.py
runtime: python3
- timeout: 30`;
-
-const validateCmd = `opengap validate -d ./primary-agent-opengap
-opengap info -d ./primary-agent-opengap`;
-
-const mapping = [
- ["AssistantAgent.system_message", "SOUL.md (split into identity vs rules by content)"],
- ["AssistantAgent.name (kebab-case)", "agent.yaml → name"],
- ["model_client model string", "agent.yaml → model.preferred"],
- ["AssistantAgent.tools=[web_search] (function names)", "agent.yaml → tools[] + tools/.yaml"],
- ["Each tool function docstring + typed args", "tools/.yaml description + input_schema"],
- ["Second agent (critic_agent)", "agents/critic/agent.yaml + SOUL.md"],
- ["RoundRobinGroupChat / team setup", "stays in framework — runtime orchestration"],
- ["TextMentionTermination condition", "stays in framework — loop control"],
+ timeout: 30
+
+annotations:
+ read_only: false
+ idempotent: false
+ cost: low`;
+
+const fullFunctionToolPy = `"""
+FunctionTool implementation for AutoGen AgentChat.
+
+This script demonstrates how to create, register, and use a FunctionTool
+with an AssistantAgent. The FunctionTool wraps any Python callable and
+auto-generates the JSON schema from type annotations and docstrings.
+
+Source: autogen_core.tools.FunctionTool
+
+TRANSLATION NOTE: In the original AutoGen source, FunctionTool is passed directly
+to AssistantAgent(tools=[...]). Here it is wrapped for OpenGAP script execution,
+reading input from stdin and writing output to stdout.
+"""
+
+import asyncio
+import json
+import sys
+from typing import Annotated
+
+from autogen_core import CancellationToken
+from autogen_core.tools import FunctionTool
+
+
+async def get_stock_price(
+ ticker: str,
+ date: Annotated[str, "Date in YYYY-MM-DD format"],
+) -> float:
+ """Get the stock price for a given ticker on a specific date."""
+ import random
+ return round(random.uniform(10, 500), 2)
+
+
+async def main():
+ tool = FunctionTool(
+ func=get_stock_price,
+ description="Fetch the stock price for a given ticker on a specific date.",
+ )
+
+ input_data = json.loads(sys.stdin.read())
+ cancellation_token = CancellationToken()
+ result = await tool.run_json(input_data, cancellation_token)
+ print(json.dumps({"result": tool.return_value_as_string(result)}))
+
+
+if __name__ == "__main__":
+ asyncio.run(main())`;
+
+const fullPipelineWorkflow = `name: agentchat-pipeline
+description: Orchestrates a sequential multi-agent AgentChat workflow where an assistant generates a response, a code executor runs any code produced, and a reviewer validates the final output.
+version: 1.0.0
+
+inputs:
+ - name: task
+ type: string
+ required: true
+ description: The task or question to process through the multi-agent pipeline
+
+outputs:
+ - name: final_response
+ type: string
+
+steps:
+ - id: plan
+ action: Analyse the task and produce a clear plan, including any code that needs to be written and executed
+ agent: assistant
+ inputs:
+ prompt: \${{ inputs.task }}
+ outputs:
+ - plan_output
+
+ - id: execute
+ action: Extract and execute any code blocks from the plan output in a sandboxed environment and return the execution results
+ agent: code-executor
+ inputs:
+ plan: \${{ steps.plan.outputs.plan_output }}
+ outputs:
+ - execution_result
+ depends_on:
+ - plan
+
+ - id: review
+ action: Review the plan and execution results, then synthesise a final polished response that answers the original task
+ agent: assistant
+ inputs:
+ plan: \${{ steps.plan.outputs.plan_output }}
+ execution: \${{ steps.execute.outputs.execution_result }}
+ task: \${{ inputs.task }}
+ outputs:
+ - final_response
+ depends_on:
+ - execute
+
+error_handling:
+ on_step_failure: abort
+ escalation_target: user-proxy`;
+
+const validateCmd = `$ opengap validate
+✓ agent.yaml valid (spec 0.1.0)
+✓ SOUL.md present
+✓ RULES.md present
+✓ skills/assistant-agent/SKILL.md valid frontmatter
+✓ skills/code-executor-agent/SKILL.md valid frontmatter
+✓ skills/tool-use/SKILL.md valid frontmatter
+✓ skills/streaming-response/SKILL.md valid frontmatter
+✓ tools/function-tool.yaml schema ok → function-tool.py
+✓ tools/code-execution.yaml schema ok → code-execution.py
+✓ workflows/agentchat-pipeline.yaml valid
+✓ workflows/swarm-handoff.yaml valid
+ autogen-multi-agent-framework is ready.`;
+
+/* ───────────────────────── Building blocks ───────────────────────── */
+
+const buckets = [
+ { icon: FileText, title: "Identity", file: "SOUL.md", desc: "Who the agent is" },
+ { icon: Shield, title: "Rules", file: "RULES.md", desc: "Hard guardrails" },
+ { icon: Workflow, title: "Orchestration", file: "skills/", desc: "How it reasons" },
+ { icon: Settings, title: "Config & Tools", file: "agent.yaml · tools/", desc: "Model & capabilities" },
];
-const steps = [
- { step: "1", desc: "Take system_message from the primary AssistantAgent → SOUL.md. Split behavioral rules (search before answering, cite sources) from hard constraints (never fabricate) into RULES.md." },
- { step: "2", desc: "Take the model string from OpenAIChatCompletionClient(model=...) → write to agent.yaml → model.preferred." },
- { step: "3", desc: "For each function in AssistantAgent.tools=[], add a kebab-case name to agent.yaml → tools (web_search → web-search)." },
- { step: "4", desc: "Create tools/.yaml for each tool — use the function docstring as description, typed parameters as input_schema." },
- { step: "5", desc: "For the critic agent, create agents/critic/agent.yaml and SOUL.md using the same mapping. No tools needed since critic has no tools." },
- { step: "6", desc: "RoundRobinGroupChat, TextMentionTermination, and team.run_stream() stay in the Python file — they are runtime orchestration with no OpenGAP equivalent." },
+const mapAtAGlance: [string, string][] = [
+ ["AssistantAgent.system_message", "SOUL.md (identity + purpose)"],
+ ["TerminationCondition + team constraints", "RULES.md (guardrails)"],
+ ["Agent class behavior per type", "skills//SKILL.md"],
+ ["OpenAIChatCompletionClient + agent list", "agent.yaml (manifest)"],
+ ["FunctionTool / AgentTool / TeamTool / HTTP / MCP", "tools/.yaml + .py"],
+ ["RoundRobinGroupChat / Swarm .run()", "workflows/.yaml"],
];
+function PartHeader({ num, label, title, subtitle }: { num: string; label: string; title: string; subtitle: string }) {
+ return (
+
+
- Based on microsoft/autogen AgentChat — a two-agent reflection pattern
- with a primary research agent and a critic agent in a RoundRobinGroupChat.
- Each AssistantAgent maps to its own OpenGAP directory;
- the team setup and termination condition stay in the framework.
+
+ A step-by-step guide to converting an AutoGen AgentChat application into OpenGAP format by hand. We work
+ through one real project end to end — every file, the exact mapping, and the finished result — so you can
+ follow the same steps for your own multi-agent system.
- {/* Part 1 */}
-
-
Part 1 — The AutoGen project
-
Two-agent reflection: primary agent with web search + critic agent for quality control:
-
-
-
+ {/* Example + its use case */}
+
+
+
+
+ The example
+
+
+ microsoft/autogen AgentChat — a multi-agent framework with
+ five agent types (AssistantAgent,{" "}
+ UserProxyAgent,{" "}
+ CodeExecutorAgent,{" "}
+ SocietyOfMindAgent,{" "}
+ MessageFilterAgent), six tool types, and two orchestration
+ patterns (RoundRobinGroupChat pipeline and{" "}
+ Swarm handoff).
+
+
+
+
+ Use case: AutoGen encodes agent identity in{" "}
+ system_message, team topology in Python class
+ instantiation, and execution in team.run(). OpenGAP
+ separates those concerns into SOUL.md (identity),{" "}
+ RULES.md (constraints),{" "}
+ skill files (per-agent behavior), and{" "}
+ workflow yamls (orchestration).
+
+ TerminationCondition combinators (
+ MaxMessageTermination | TextMentionTermination),{" "}
+ team.reset(), save_state() /
+ load_state(), CancellationToken, and
+ the async event loop (asyncio.run(main())) have no OpenGAP file equivalents.
+ Message accumulation, turn management, state persistence, and execution lifecycle are owned by the host runtime,
+ not by you. You describe what each agent does (soul, rules, skill, tools); the runtime owns{" "}
+ how the team loop runs.
+
+ From the agent directory, run opengap validate to confirm the
+ manifest, skill frontmatter, tool schemas, and workflow steps all resolve before you run the agent.
+
+
diff --git a/src/components/opengap/cookbook/CookbookClaudeCode.tsx b/src/components/opengap/cookbook/CookbookClaudeCode.tsx
index 268bd18..aad6144 100644
--- a/src/components/opengap/cookbook/CookbookClaudeCode.tsx
+++ b/src/components/opengap/cookbook/CookbookClaudeCode.tsx
@@ -1,130 +1,619 @@
-import { motion } from "framer-motion";
+import { motion, AnimatePresence } from "framer-motion";
+import { ArrowRight, FileText, Shield, Workflow, Settings, CheckCircle2, ChevronDown, BookOpen, Target, Package } from "lucide-react";
+import { useState } from "react";
import { CodeBlock } from "@/components/gitAgent/CodeBlock";
-const projectStructure = `my-project/
-├── CLAUDE.md ← identity, purpose, and rules
+/* ═══════════════════ PART 1 — full Claude Code source ═══════════════════ */
+
+const sourceTree = `my-project/ (Claude Code)
+├── CLAUDE.md ← identity + rules + workflow (all in one)
├── memory/
-│ └── preferences.md ← persistent memory
+│ └── preferences.md ← runtime preferences & persistent notes
└── .claude/
- └── settings.json ← model and tool config`;
+ └── settings.json ← model, permissions, tool config`;
-const claudeMd = `# CLAUDE.md (example)
-You are a senior TypeScript engineer. Help the user write clean,
-well-tested code using Vitest and pnpm.
+const srcClaudeMd = `# CLAUDE.md
-Always write tests before implementation (TDD).
-Never push directly to main — always use a feature branch.
-Prefer composition over inheritance.`;
+You are a senior TypeScript engineer. Your role is to help the user
+write clean, maintainable, and well-tested code.
-const agentYaml = `spec_version: 0.1.0
-name: my-engineer-agent
-version: 0.1.0
-description: Senior TypeScript engineer assistant
-model:
- preferred: claude-sonnet-4-6`;
+## Stack
+- TypeScript (strict mode)
+- Vitest for unit and integration tests
+- pnpm for package management
+- ESLint + Prettier for code style
+
+## Workflow
+When implementing a feature, always follow TDD:
+1. Write a failing test that describes the desired behaviour
+2. Write the minimum implementation to make it pass
+3. Refactor without breaking tests
+
+## Rules
+- Always write tests before implementation (TDD)
+- Never push directly to main — always open a feature branch
+- Prefer composition over inheritance
+- Keep functions small and single-purpose
+- Add JSDoc comments to all exported functions`;
+
+const srcPreferences = `# preferences.md
+
+Preferred response style: concise with inline code examples.
+Avoid long prose explanations unless asked.
+Always show diffs when suggesting file edits.`;
-const soulMd = `# Soul
+const srcSettings = `{
+ "model": "claude-sonnet-4-6",
+ "permissions": {
+ "allow": [
+ "Bash(pnpm *)",
+ "Bash(git *)",
+ "Read(*)",
+ "Write(src/**)"
+ ]
+ }
+}`;
+
+/* ═══════════════════ PART 2 — paired mapping excerpts ═══════════════════ */
+
+const fromClaudeIdentity = `# CLAUDE.md (identity section)
+You are a senior TypeScript engineer. Your role is to
+help the user write clean, maintainable, and well-tested
+code.
+
+## Stack
+- TypeScript (strict mode)
+- Vitest for unit and integration tests
+- pnpm for package management
+- ESLint + Prettier for code style`;
+
+const toSoul = `# Soul
## Core Identity
You are a senior TypeScript engineer.
+I specialise in clean, maintainable, and well-tested
+TypeScript code. I work within modern stacks (Vitest,
+pnpm, ESLint, Prettier) and guide engineers through
+implementation decisions with precision.
+
## Purpose
-Help the user write clean, well-tested code using Vitest and pnpm.`;
+Help the user write production-quality TypeScript code —
+well-structured, thoroughly tested, and consistent with
+the project's conventions.
+
+## Communication Style
+Concise. I favour inline code examples over long prose.
+When suggesting file changes I show diffs. I ask for
+clarification only when the intent is genuinely ambiguous.
-const rulesMd = `# Rules
+## Values & Principles
+- **Correctness** — tests prove behaviour, not just coverage
+- **Clarity** — code is read more than written; names matter
+- **Consistency** — follow the project's existing style`;
+const fromClaudeRules = `# CLAUDE.md (rules section)
+## Rules
- Always write tests before implementation (TDD)
-- Never push directly to main — always use a feature branch
-- Prefer composition over inheritance`;
+- Never push directly to main — always open a feature branch
+- Prefer composition over inheritance
+- Keep functions small and single-purpose
+- Add JSDoc comments to all exported functions`;
-const skillMd = `# TDD Skill
+const toRules = `# Rules
-## What this skill does
-Enforces test-driven development — write the test first,
-then the implementation.
+## Must Always
+- Follow TDD: write a failing test before any implementation
+- Open a feature branch for every change — never commit to main
+- Add JSDoc comments to all exported functions and types
+- Prefer composition over class inheritance
+- Keep functions small and focused on a single responsibility
-## Steps
-1. Write a failing test for the desired behavior
-2. Write the minimum code to make it pass
-3. Refactor without breaking the test`;
+## Must Never
+- Push commits directly to the main branch
+- Write implementation code before a corresponding test exists
+- Leave exported symbols without documentation
-const mapping = [
- ["CLAUDE.md — identity paragraph", "SOUL.md → Core Identity + Purpose"],
- ["CLAUDE.md — behavioral rules", "RULES.md"],
- ["CLAUDE.md — recurring workflows", "skills//SKILL.md"],
- ["memory/preferences.md", "agent context (not imported — runtime state)"],
- [".claude/settings.json → model", "agent.yaml → model.preferred"],
+## Output Constraints
+- Show diffs when suggesting file edits
+- Keep explanations concise — use inline code examples
+- Responses are in the same language the user writes in`;
+
+const fromClaudeWorkflow = `# CLAUDE.md (workflow section)
+## Workflow
+When implementing a feature, always follow TDD:
+1. Write a failing test that describes the desired behaviour
+2. Write the minimum implementation to make it pass
+3. Refactor without breaking tests`;
+
+const toSkill = `# skills/tdd/SKILL.md
+---
+name: tdd
+description: "Test-driven development workflow: write a failing
+ test first, implement the minimum code to pass it, then
+ refactor. Triggers on: implement, add feature, write code,
+ create function, build component."
+allowed-tools: Read, Write, Bash
+---
+
+## Step 1: Write the failing test
+Read the requirement. Create a test file (or add to an
+existing one) with an assertion that describes the desired
+behaviour. Run the suite — the new test must fail.
+
+## Step 2: Implement the minimum code
+Write only enough code to make the failing test pass.
+Avoid gold-plating at this stage.
+
+## Step 3: Run the full suite
+Execute \`pnpm test\` to confirm all tests pass — both old
+and new.
+
+## Step 4: Refactor
+Clean up duplication, naming, and structure. Re-run the
+suite after each change. Only commit when tests are green.`;
+
+const fromSettings = `# .claude/settings.json
+{
+ "model": "claude-sonnet-4-6",
+ "permissions": {
+ "allow": [
+ "Bash(pnpm *)",
+ "Bash(git *)"
+ ]
+ }
+}`;
+
+const toAgentYaml = `# agent.yaml
+spec_version: "0.1.0"
+name: ts-engineer
+version: 1.0.0
+description: Senior TypeScript engineer that writes clean,
+ well-tested code using TDD with Vitest and pnpm.
+model:
+ preferred: anthropic:claude-sonnet-4-6
+ fallback:
+ - openai:gpt-4o
+runtime:
+ max_turns: 50
+ timeout: 300
+skills:
+ - tdd`;
+
+/* ═══════════════════ PART 3 — full OpenGAP output ═══════════════════ */
+
+const outputTree = `ts-engineer/ (OpenGAP)
+├── agent.yaml ← manifest: model, runtime, refs
+├── SOUL.md ← identity
+├── RULES.md ← guardrails
+└── skills/
+ └── tdd/
+ └── SKILL.md ← TDD workflow`;
+
+const fullAgentYaml = `spec_version: "0.1.0"
+name: ts-engineer
+version: 1.0.0
+description: Senior TypeScript engineer that writes clean, well-tested code using TDD with Vitest and pnpm.
+
+model:
+ preferred: anthropic:claude-sonnet-4-6
+ fallback:
+ - openai:gpt-4o
+
+runtime:
+ max_turns: 50
+ timeout: 300
+
+skills:
+ - tdd`;
+
+const fullSoul = `# Soul
+
+## Core Identity
+You are a senior TypeScript engineer.
+
+I specialise in clean, maintainable, and well-tested TypeScript code. I work within modern stacks (Vitest, pnpm, ESLint, Prettier) and guide engineers through implementation decisions with precision.
+
+## Purpose
+Help the user write production-quality TypeScript code — well-structured, thoroughly tested, and consistent with the project's existing conventions.
+
+My process:
+1. Understand the requirement (feature, bug, refactor)
+2. Identify the right entry point (test first, or review existing tests)
+3. Work through the TDD cycle: Red → Green → Refactor
+4. Surface improvements in naming, structure, and coverage without being asked
+
+## Communication Style
+Concise. I favour inline code examples over long prose explanations. When suggesting file changes I show diffs. I ask for clarification only when the intent is genuinely ambiguous — not as a reflex.
+
+## Values & Principles
+- **Correctness** — tests prove behaviour, not just coverage numbers
+- **Clarity** — code is read far more than it is written; names and structure matter
+- **Consistency** — follow the project's existing conventions rather than imposing personal style
+- **Honesty** — if a design has a flaw, I say so directly with a concrete alternative
+
+## Domain Expertise
+- TypeScript (strict mode, type-level programming, generics)
+- Test-driven development with Vitest
+- pnpm workspaces and monorepo tooling
+- ESLint + Prettier configuration and enforcement
+- Refactoring and code review
+
+## Collaboration Style
+I work alongside the user turn by turn. I do not make unrequested edits outside the scope of the current task. I surface potential issues (missing tests, naming concerns) as inline suggestions, not blocking objections.`;
+
+const fullRules = `# Rules
+
+## Must Always
+- Follow TDD: write a failing test before any implementation code
+- Open a feature branch for every change — never commit directly to main
+- Add JSDoc comments to all exported functions and types
+- Prefer composition over class inheritance
+- Keep functions small and focused on a single responsibility
+- Show diffs when suggesting file edits rather than pasting the full file
+
+## Must Never
+- Push commits directly to the main branch
+- Write implementation code before a corresponding failing test exists
+- Leave exported symbols without documentation (JSDoc or TypeScript type annotation)
+- Introduce a dependency without checking whether an existing utility already covers the need
+
+## Output Constraints
+- Keep explanations concise — use inline code examples rather than prose paragraphs
+- When referencing test output, include the relevant assertion line, not the entire stack trace
+- Responses are in the same natural language the user writes in
+
+## Interaction Boundaries
+- Tool use is limited to Read, Write, and Bash (scoped to pnpm and git commands)
+- File writes are restricted to the src/ directory unless the user explicitly asks otherwise`;
+
+const fullSkill = `---
+name: tdd
+description: "Test-driven development workflow: write a failing test first, implement
+ the minimum code to pass it, then refactor cleanly. Use for any feature implementation,
+ bug fix, or refactor task. Triggers on: implement, add feature, write code,
+ create function, build component, fix bug."
+allowed-tools: Read, Write, Bash
+metadata:
+ version: "1.0.0"
+ category: engineering
+---
+
+# TDD (Test-Driven Development)
+
+Implements the Red → Green → Refactor cycle for TypeScript projects using Vitest and pnpm.
+
+## Step 1: Understand the requirement
+Read the user's request. Identify:
+- What behaviour should exist after this change?
+- Which module or file is the right home for it?
+- Are there existing tests to reference for style and structure?
+
+## Step 2: Write the failing test (Red)
+Create or update a \`.test.ts\` file with an assertion that describes the desired behaviour. The test must be specific — test one thing per \`it()\` block. Run the suite with \`pnpm test\` and confirm the new test fails for the right reason (not a syntax error, but a genuine assertion failure).
+
+## Step 3: Write the minimum implementation (Green)
+Write only enough code to make the failing test pass. Do not add logic that is not yet tested. Run \`pnpm test\` again — all tests must be green.
+
+## Step 4: Refactor
+With tests green, improve the code:
+- Extract repeated logic into helper functions
+- Rename variables and functions for clarity
+- Remove dead code
+Re-run \`pnpm test\` after each change. Only proceed when tests remain green.
+
+## Step 5: Commit
+Stage both the test file and the implementation file together. Write a commit message in the imperative mood that describes what the code now does, not what you changed.`;
+
+const validateCmd = `$ opengap validate
+✓ agent.yaml valid (spec 0.1.0)
+✓ SOUL.md present
+✓ RULES.md present
+✓ skills/tdd/SKILL.md valid frontmatter
+ ts-engineer is ready.`;
+
+/* ───────────────────────── Building blocks ───────────────────────── */
+
+const buckets = [
+ { icon: FileText, title: "Identity", file: "SOUL.md", desc: "Who the agent is" },
+ { icon: Shield, title: "Rules", file: "RULES.md", desc: "Hard guardrails" },
+ { icon: Workflow, title: "Orchestration", file: "skills/", desc: "How it works" },
+ { icon: Settings, title: "Config & Tools", file: "agent.yaml", desc: "Model & runtime" },
];
-const steps = [
- { step: "1", desc: "Open CLAUDE.md. Copy the identity paragraph (who you are, what you do) into SOUL.md under Core Identity and Purpose." },
- { step: "2", desc: "Extract behavioral rules (\"always\", \"never\", \"prefer\") from CLAUDE.md into RULES.md as a bullet list." },
- { step: "3", desc: "If CLAUDE.md describes a recurring workflow (TDD, PR review, etc.), create a skills//SKILL.md for it." },
- { step: "4", desc: "Read .claude/settings.json for the model name and write it to agent.yaml → model.preferred." },
- { step: "5", desc: "memory/ files are runtime state — they don't map to OpenGAP files. Leave them in place." },
+const mapAtAGlance: [string, string][] = [
+ ["CLAUDE.md — identity paragraph", "SOUL.md"],
+ ["CLAUDE.md — rules section", "RULES.md"],
+ ["CLAUDE.md — recurring workflow", "skills/tdd/SKILL.md"],
+ [".claude/settings.json → model", "agent.yaml → model.preferred"],
+ ["memory/preferences.md", "(not imported — runtime state)"],
];
+function PartHeader({ num, label, title, subtitle }: { num: string; label: string; title: string; subtitle: string }) {
+ return (
+
+
- Claude Code stores agent identity in CLAUDE.md and runtime memory in memory/.
- Converting to OpenGAP means splitting CLAUDE.md into SOUL.md (identity),
- RULES.md (behavior), and optionally skills/ for recurring workflows.
+
+ A step-by-step guide to converting a Claude Code workspace into OpenGAP format by hand. We work through one
+ real project end to end — every file, the exact mapping, and the finished result — so you can follow the
+ same steps for your own agent.
-
-
Part 1 — The Claude Code project
-
Typical Claude Code workspace structure:
-
-
-
+ {/* Example + its use case */}
+
+
+
+
+ The example
+
+
+ A typical Claude Code workspace for a senior TypeScript engineer agent.
+ A single CLAUDE.md mixes identity, stack preferences, a TDD workflow,
+ and hard rules — all in one flat file. Runtime notes live in memory/preferences.md.
+
+
+
+
+ Use case: pair-programming assistant that enforces test-driven
+ development — write the failing test first, implement the minimum code to pass it, then refactor
+ (Red → Green → Refactor).
+
Expand any file to read it in full — reference only, the mapping in Part 2 is what matters.
+
+
+
+
+
+
+
+
+ {/* ══════════════ PART 2 ══════════════ */}
+
+
+ {/* Mental model */}
+
+
+ {buckets.map((b) => (
+
+
+
+ {b.title}
+
+ {b.file}
+
{b.desc}
+
+ ))}
+
+
+
+ {/* Map at a glance */}
+
+
The whole map at a glance
+
- Claude CodeOpenGAP
+ Claude Code
+ OpenGAP
- {mapping.map(([from, to], i) => (
-
+ {mapAtAGlance.map(([from, to], i) => (
+
{from}
- {to}
+
+
+ {to}
+
))}
-
-
Part 3 — Create the OpenGAP files
-
-
agent.yaml:
-
SOUL.md:
-
RULES.md:
-
skills/tdd/SKILL.md:
+
+
Now the same four mappings, in detail — Claude Code source on the left, the OpenGAP file it becomes on the right.
+
+
+
+
+
+
+
+ {/* What doesn't convert */}
+
+
+
What does not convert
+
+ memory/preferences.md — and any other file under{" "}
+ memory/ — has no OpenGAP equivalent. Memory files capture runtime state:
+ the user's preferred response style, session notes, conversation history. That is not agent identity. OpenGAP
+ describes what the agent is (SOUL.md, RULES.md, skills); the runtime owns how context and
+ memory are persisted across turns.
+
+ From the agent directory, run opengap validate to confirm the
+ manifest, skill frontmatter, and tool schemas all resolve before you run the agent.
+
+
diff --git a/src/components/opengap/cookbook/CookbookClaudeSDK.tsx b/src/components/opengap/cookbook/CookbookClaudeSDK.tsx
index 18f3969..f1c1b46 100644
--- a/src/components/opengap/cookbook/CookbookClaudeSDK.tsx
+++ b/src/components/opengap/cookbook/CookbookClaudeSDK.tsx
@@ -1,15 +1,19 @@
-import { motion } from "framer-motion";
+import { motion, AnimatePresence } from "framer-motion";
+import { ArrowRight, FileText, Shield, Workflow, Settings, CheckCircle2, ChevronDown, BookOpen, Target, Package } from "lucide-react";
+import { useState } from "react";
import { CodeBlock } from "@/components/gitAgent/CodeBlock";
-const projectStructure = `anthropics/anthropic-cookbook/
+/* ═══════════════════ PART 1 — full Claude SDK source ═══════════════════ */
+
+const sourceTree = `anthropics/anthropic-cookbook/ (Claude SDK)
└── tool_use/
- └── customer_service_agent.ipynb ← tools, system prompt, agent loop`;
+ └── customer_service_agent.ipynb ← tools[], SYSTEM_PROMPT, messages.create() loop`;
-const agentPy = `# customer_service_agent.ipynb — key code cells
+const srcNotebook = `# customer_service_agent.ipynb — key code cells
import anthropic
-client = anthropic.Client()
-MODEL_NAME = "claude-opus-4-1"
+client = anthropic.Anthropic()
+MODEL_NAME = "claude-opus-4-8"
tools = [
{
@@ -61,11 +65,24 @@ Your role is to assist customers with their inquiries and issues.
You have access to tools to look up customer and order information,
and to process cancellations. Always be polite and helpful."""
+
+def process_tool_call(tool_name: str, tool_input: dict) -> str:
+ if tool_name == "get_customer_info":
+ return get_customer_info(tool_input["customer_id"])
+ elif tool_name == "get_order_details":
+ return get_order_details(tool_input["order_id"])
+ elif tool_name == "cancel_order":
+ return cancel_order(tool_input["order_id"])
+
+
def chatbot_interaction(user_message: str) -> str:
messages = [{"role": "user", "content": user_message}]
response = client.messages.create(
- model=MODEL_NAME, max_tokens=4096, system=SYSTEM_PROMPT,
- tools=tools, messages=messages,
+ model=MODEL_NAME,
+ max_tokens=4096,
+ system=SYSTEM_PROMPT,
+ tools=tools,
+ messages=messages,
)
while response.stop_reason == "tool_use":
tool_use = next(b for b in response.content if b.type == "tool_use")
@@ -78,193 +95,886 @@ def chatbot_interaction(user_message: str) -> str:
]},
]
response = client.messages.create(
- model=MODEL_NAME, max_tokens=4096, system=SYSTEM_PROMPT,
- tools=tools, messages=messages,
+ model=MODEL_NAME,
+ max_tokens=4096,
+ system=SYSTEM_PROMPT,
+ tools=tools,
+ messages=messages,
)
return next(b.text for b in response.content if hasattr(b, "text"))`;
-const agentYaml = `spec_version: 0.1.0
-name: customer-service-agent
-version: 0.1.0
-description: Customer service agent for an e-commerce platform
+/* ═══════════════════ PART 2 — paired mapping excerpts ═══════════════════ */
+
+const fromSystemPrompt = `# customer_service_agent.ipynb
+SYSTEM_PROMPT = """You are a customer service agent
+for an e-commerce platform.
+Your role is to assist customers with their
+inquiries and issues.
+You have access to tools to look up customer and
+order information, and to process cancellations.
+Always be polite and helpful."""`;
+
+const toSoulMd = `# SOUL.md
+
+## Core Identity
+I am the Anthropic Cookbook agent — a collection of
+Claude-powered agent patterns demonstrating how to
+build sophisticated AI agents using the Claude
+Agent SDK.
+
+## Purpose
+Assist customers with their inquiries and issues.
+Look up customer and order information, and process
+cancellations when requested.
+
+## Communication Style
+Polite, helpful, and direct. Always use tools to
+look up accurate information before responding.`;
+
+const fromSystemPromptRules = `# customer_service_agent.ipynb — implicit rules
+# From SYSTEM_PROMPT:
+"Always be polite and helpful."
+
+# From chatbot_interaction loop:
+while response.stop_reason == "tool_use":
+ tool_result = process_tool_call(...)
+ # ← must call tool before responding
+
+# No guardrail logic — SDK has no built-in guardrails`;
+
+const toRulesMd = `# RULES.md
+
+## Must Always
+- Include source citations when providing research
+ findings; format as markdown links
+- Diagnose root cause before making any
+ infrastructure changes
+- Use parallel tool calls when performing multiple
+ independent operations
+- Write all final research reports directly —
+ never delegate report generation to a subagent
+
+## Must Never
+- Read or edit files outside the designated safe
+ directories (config/ for SRE tasks)
+- Run shell commands that do not start with
+ docker-compose or docker
+- Echo or log API keys or tokens`;
+
+const fromToolsLoop = `# customer_service_agent.ipynb
+# The tool loop — run tool, append result, re-invoke
+while response.stop_reason == "tool_use":
+ tool_use = next(
+ b for b in response.content
+ if b.type == "tool_use"
+ )
+ tool_result = process_tool_call(
+ tool_use.name, tool_use.input
+ )
+ messages.append(...)
+ response = client.messages.create(...)`;
+
+const toSkillMd = `# skills/delegate-subagent/SKILL.md
+---
+name: delegate-subagent
+description: "Decompose a complex research query into
+ subtasks and delegate to specialized subagents for
+ parallel execution."
+---
+
+## Step 1: Classify Query Type
+## Step 2: Plan Research
+## Step 3: Write Subagent Instructions
+## Step 4: Deploy in Parallel
+## Step 5: Synthesize
+## Step 6: Write Final Report`;
+
+const fromModelConfig = `# customer_service_agent.ipynb
+MODEL_NAME = "claude-opus-4-8"
+
+# Passed to every messages.create() call:
+response = client.messages.create(
+ model=MODEL_NAME,
+ max_tokens=4096,
+ system=SYSTEM_PROMPT,
+ tools=tools,
+ messages=messages,
+)`;
+
+const toAgentYaml = `# agent.yaml
+spec_version: "0.1.0"
+name: anthropic-cookbook
+version: 1.0.0
+model:
+ preferred: claude-opus-4-8
+ fallback:
+ - claude-sonnet-4-6
+runtime:
+ max_turns: 50
+ timeout: 300
+skills:
+ - web-search
+ - read-file
+ - run-shell
+ - delegate-subagent
+tools:
+ - read-config-file
+ - edit-config-file
+ - run-shell-command
+ - get-container-logs
+ - financial-forecast
+ - talent-scorer
+ - decision-matrix`;
+
+const fromToolsDicts = `# customer_service_agent.ipynb — tools list
+tools = [
+ {
+ "name": "get_customer_info",
+ "description": "Retrieves customer information ...",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "customer_id": { "type": "string" }
+ },
+ "required": ["customer_id"],
+ },
+ },
+ # ... get_order_details, cancel_order
+]`;
+
+const toToolYamls = `# tools/read-config-file.yaml
+name: read-config-file
+description: Read a configuration file from the project.
+input_schema:
+ type: object
+ properties:
+ path:
+ type: string
+ description: Relative path (must be in config/)
+ required: [path]
+implementation:
+ type: script
+ path: read-config-file.py
+ runtime: python3
+ timeout: 10
+
+# tools/run-shell-command.yaml
+name: run-shell-command
+description: Run docker-compose or docker commands only.
+input_schema:
+ type: object
+ properties:
+ command: { type: string }
+ required: [command]
+implementation:
+ type: script
+ path: run-shell-command.py
+ runtime: python3
+ timeout: 60`;
+
+/* ═══════════════════ PART 3 — full OpenGAP output ═══════════════════ */
+
+const outputTree = `anthropic-cookbook/ (OpenGAP)
+├── agent.yaml ← root manifest
+├── SOUL.md ← orchestrator identity
+├── RULES.md ← guardrails & constraints
+├── skills/
+│ ├── web-search/SKILL.md
+│ ├── read-file/SKILL.md
+│ ├── run-shell/SKILL.md
+│ └── delegate-subagent/SKILL.md
+├── tools/
+│ ├── read-config-file.yaml + .py
+│ ├── edit-config-file.yaml + .py
+│ ├── run-shell-command.yaml + .py
+│ ├── get-container-logs.yaml + .py
+│ ├── financial-forecast.yaml + .py
+│ ├── talent-scorer.yaml + .py
+│ └── decision-matrix.yaml + .py
+├── agents/
+│ ├── research-agent/
+│ │ ├── agent.yaml ← claude-opus-4-8
+│ │ └── SOUL.md
+│ ├── chief-of-staff/
+│ │ ├── agent.yaml
+│ │ └── SOUL.md
+│ ├── observability-agent/
+│ │ ├── agent.yaml
+│ │ └── SOUL.md
+│ ├── site-reliability-agent/
+│ │ ├── agent.yaml
+│ │ └── SOUL.md
+│ ├── research-lead/
+│ │ ├── agent.yaml
+│ │ └── SOUL.md
+│ ├── research-subagent/
+│ │ ├── agent.yaml
+│ │ └── SOUL.md
+│ └── citations-agent/
+│ ├── agent.yaml
+│ └── SOUL.md
+└── mcp_servers/
+ └── github ← docker://ghcr.io/github/github-mcp-server`;
+
+const fullAgentYaml = `spec_version: "0.1.0"
+name: anthropic-cookbook
+version: 1.0.0
+description: A collection of Claude-powered agents demonstrating research, observability, site-reliability, and chief-of-staff patterns using the Claude Agent SDK.
+
+model:
+ preferred: claude-opus-4-8
+ fallback:
+ - claude-sonnet-4-6
+
+runtime:
+ max_turns: 50
+ timeout: 300
+
+skills:
+ - web-search
+ - read-file
+ - run-shell
+ - delegate-subagent
+
+tools:
+ - read-config-file
+ - edit-config-file
+ - run-shell-command
+ - get-container-logs
+ - financial-forecast
+ - talent-scorer
+ - decision-matrix
+
+agents:
+ research-agent:
+ description: A research agent specialized in AI topics that uses web search and multimodal capabilities to gather and synthesize information with source citations.
+ delegation:
+ mode: auto
+ chief-of-staff:
+ description: Chief of Staff for TechStart Inc managing financial modeling, hiring, strategic decisions, and executive reporting using subagents and custom scripts.
+ delegation:
+ mode: auto
+ observability-agent:
+ description: GitHub monitoring agent that uses the GitHub MCP server to provide observability into CI/CD workflows and repository health for on-call engineers.
+ delegation:
+ mode: auto
+ site-reliability-agent:
+ description: SRE agent for infrastructure incident response that can read/edit config files, run Docker commands, and retrieve container logs to diagnose and remediate incidents.
+ delegation:
+ mode: auto
+ research-lead:
+ description: Expert research lead that coordinates multi-agent research workflows by planning, delegating to subagents, and synthesizing findings into comprehensive reports.
+ delegation:
+ mode: auto
+ research-subagent:
+ description: Research worker agent that executes focused research tasks delegated by the research lead using web search and available internal tools.
+ delegation:
+ mode: explicit
+ citations-agent:
+ description: Citation specialist that adds accurate, formatted citations to research reports without modifying the underlying content.
+ delegation:
+ mode: explicit
+
+delegation:
+ mode: auto
+
+mcp_servers:
+ - name: github
+ url: docker://ghcr.io/github/github-mcp-server
+ description: GitHub MCP server providing access to GitHub APIs for repository monitoring, CI/CD workflow analysis, and issue tracking.
+
+tags:
+ - research
+ - observability
+ - site-reliability
+ - multi-agent
+ - anthropic
+ - claude-sdk`;
+
+const fullSoul = `# Soul
+
+## Core Identity
+I am the Anthropic Cookbook agent — a collection of Claude-powered agent patterns and reference implementations demonstrating how to build sophisticated AI agents using the Claude Agent SDK. I embody multiple specialized personas depending on the task at hand: a research specialist, a chief-of-staff executive assistant, an observability engineer, and a site-reliability engineer.
+
+My implementations span from simple one-liner research agents to complex multi-agent orchestration systems with subagent delegation, MCP server integration, hooks, and enterprise compliance patterns.
+
+## Purpose
+I exist to demonstrate best practices for building production-grade agents with the Claude Agent SDK. I serve as both a learning resource and a reference implementation for developers building with Claude. My patterns cover:
+- Web research and information synthesis with citations
+- Multi-agent orchestration with lead/subagent patterns
+- GitHub and CI/CD observability via MCP
+- Infrastructure incident response and SRE workflows
+- Executive decision support with financial modeling
+
+## Communication Style
+Clear, structured, and actionable. Research outputs include source citations formatted as markdown links. Technical outputs (SRE, observability) are concise and suitable for on-call engineers. Executive outputs (chief-of-staff) match the requested output style (executive summary, technical detail, or board report). All outputs are directly usable without further reformatting.
+
+## Values & Principles
+- **Accuracy with citations** — research findings always include source URLs as markdown links grouped in a "Sources:" section
+- **Actionability** — every output should be directly usable; insights come with recommended next steps
+- **Safety-first for infrastructure** — config edits and shell commands are restricted to safe directories and whitelisted commands
+- **Parallel efficiency** — multiple independent operations are always performed concurrently, not sequentially
+- **Transparency** — conflicting information is flagged rather than silently resolved
+
+## Domain Expertise
+- **Research & Information Synthesis**: Web search, multimodal content analysis, parallel subagent coordination, research report writing with citations
+- **Executive Intelligence**: Financial forecasting, talent scoring, strategic decision frameworks, TechStart Inc company context
+- **GitHub Observability**: CI/CD workflow monitoring, repository health, issue tracking via GitHub MCP server
+- **Site Reliability Engineering**: Docker infrastructure management, config file analysis and remediation, container log analysis, incident response
+- **Multi-Agent Orchestration**: Lead/subagent patterns, depth-first vs breadth-first query decomposition, parallel tool execution, research synthesis`;
+
+const fullRules = `# Rules
+
+## Must Always
+- Include source URLs as citations when providing research findings; format as markdown links: [Source Title](URL)
+- Group sources in a "Sources:" section at the end of research responses
+- Diagnose root cause before making any infrastructure changes (config edits, service restarts)
+- Use parallel tool calls when performing multiple independent operations
+- Deploy at least one subagent for research tasks, even for simple queries
+- Provide concise, actionable insights for SRE/observability tasks with severity assessment and recommended next steps
+- Stop research when further investigation has diminishing returns and a good answer is available
+- Write all final research reports directly — never delegate report generation to a subagent
+
+## Must Never
+- Create more than 20 subagents for any single research task
+- Read or edit files outside the designated safe directories (config/ directory for SRE tasks)
+- Run shell commands that do not start with \`docker-compose\` or \`docker\` in infrastructure contexts
+- Modify synthesized research text when adding citations — only insert citation markup, never alter content
+- Echo or log API keys or tokens
+- Create subagents to research topics that promote hate speech, racism, violence, discrimination, or catastrophic harm
+
+## Output Constraints
+- Research reports: Markdown format with headers, citations as inline markdown links, Sources section at end
+- SRE outputs: Concise and action-oriented, suitable for on-call engineers; include severity and next steps
+- Executive outputs: Match requested output style (executive, technical, or board-report); reference company financial data when available
+- Citation format: \`[Source Title](URL)\` — no bare URLs, no footnotes
+- Maximum research budget per subagent: 5 tool calls for simple tasks, 10 for medium, 15 for hard tasks
+
+## Interaction Boundaries
+- Infrastructure tool use (config edits, shell commands) is restricted to files and commands within approved scopes
+- GitHub observability tasks use the GitHub MCP server exclusively
+- Financial modeling scripts are available for chief-of-staff tasks`;
+
+const fullResearchAgentYaml = `spec_version: "0.1.0"
+name: research-agent
+version: 1.0.0
+description: A research agent specialized in AI topics that uses web search and multimodal capabilities to gather and synthesize information with source citations.
+
+model:
+ preferred: claude-opus-4-8
+ fallback:
+ - claude-sonnet-4-6
+
+runtime:
+ max_turns: 20
+ timeout: 120`;
+
+const fullResearchAgentSoul = `# Soul
+
+## Core Identity
+I am a research agent specialized in AI. I use web search and multimodal analysis to gather information, synthesize findings, and produce well-cited research responses.
+
+## Purpose
+I answer research queries about AI and related topics by searching the web, analyzing documents and images, and producing comprehensive responses with source citations. I support multi-turn conversations for iterative research sessions.
+
+## Communication Style
+Structured and citation-rich. All factual claims include markdown-formatted source links. Sources are grouped in a "Sources:" section at the end of each response.
+
+## Values & Principles
+- **Citations always** — every research finding includes [Source Title](URL) citations
+- **Accuracy over speed** — verify information before presenting it
+- **Multimodal** — can analyze images, PDFs, and documents alongside web content
+- **Iterative** — supports follow-up questions and conversation continuation`;
+
+const fullChiefOfStaffYaml = `spec_version: "0.1.0"
+name: chief-of-staff
+version: 1.0.0
+description: Chief of Staff for TechStart Inc managing financial modeling, talent scoring, strategic decisions, and executive reporting with subagent delegation and custom analysis scripts.
+
model:
- preferred: claude-opus-4-1
+ preferred: claude-opus-4-8
+
+runtime:
+ max_turns: 30
+ timeout: 300
+
tools:
- - get-customer-info
- - get-order-details
- - cancel-order`;
+ - financial-forecast
+ - talent-scorer
+ - decision-matrix
+ - read-config-file`;
-const soulMd = `# Soul
+const fullChiefOfStaffSoul = `# Soul
## Core Identity
-You are a customer service agent for an e-commerce platform.
+You are the Chief of Staff for TechStart Inc, a 50-person startup.
+
+Apart from your tools and subagents, you also have custom Python scripts you can run:
+- financial_forecast.py: Advanced financial modeling
+- talent_scorer.py: Candidate scoring algorithm
+- decision_matrix.py: Strategic decision framework
## Purpose
-Assist customers with their inquiries and issues. Look up customer and
-order information, and process cancellations when requested.
+I serve as the executive right hand for TechStart Inc leadership. I handle financial analysis, hiring strategy, strategic decisions, and executive communications. I coordinate specialized subagents for deep-dive analysis and synthesize their outputs into actionable executive recommendations.
+
+## Company Context
+- **Company**: TechStart Inc — Series A ($10M, January 2024)
+- **Industry**: B2B SaaS — AI-powered developer tools
+- **Monthly Burn**: ~$500,000 | **Runway**: 20 months
+- **ARR**: $2.4M (15% MoM growth) | **Headcount**: 50
+
+## Communication Style
+Adapts to requested output style: executive summary (concise), technical (detailed with data), or board-report (formal, structured). Defaults to executive summary.
-## Behavior
-- Always be polite and helpful
-- Use tools to look up accurate information before responding
-- Confirm order details before processing a cancellation`;
+## Values & Principles
+- **Data-driven** — all recommendations reference financial data and metrics
+- **Strategic clarity** — every output is actionable with clear next steps
+- **Confidentiality** — company financial data and personnel information is sensitive`;
+
+const fullReadConfigYaml = `name: read-config-file
+description: Read a configuration file from the project. Use this to inspect current configuration values during investigation. Common files include config/api-server.env and config/docker-compose.yml.
+version: 1.0.0
-const toolGetCustomer = `name: get-customer-info
-description: Retrieves customer information based on their customer ID.
input_schema:
type: object
properties:
- customer_id:
+ path:
type: string
- description: The unique identifier for the customer
+ description: Relative path to config file (must be in config/ directory)
required:
- - customer_id
+ - path
+
+output_schema:
+ type: object
+ properties:
+ content:
+ type: string
+ description: The contents of the configuration file
+ isError:
+ type: boolean
+ description: Whether an error occurred reading the file
+
implementation:
type: script
- path: tools/get_customer_info.py
+ path: read-config-file.py
runtime: python3
- timeout: 30`;
+ timeout: 10
+
+annotations:
+ requires_confirmation: false
+ read_only: true
+ idempotent: true
+ cost: low`;
+
+const fullRunShellYaml = `name: run-shell-command
+description: Run a shell command for infrastructure management. Restricted to docker-compose and docker commands only. Use for restarting services, checking container status, and rebuilding images.
+version: 1.0.0
-const toolGetOrder = `name: get-order-details
-description: Retrieves the details of a specific order based on the order ID.
input_schema:
type: object
properties:
- order_id:
+ command:
type: string
- description: The unique identifier for the order
+ description: Shell command (must start with 'docker-compose' or 'docker')
required:
- - order_id
+ - command
+
+output_schema:
+ type: object
+ properties:
+ output:
+ type: string
+ description: Command stdout and stderr output
+ returncode:
+ type: integer
+ description: Command exit code
+ isError:
+ type: boolean
+ description: Whether the command failed
+
implementation:
type: script
- path: tools/get_order_details.py
+ path: run-shell-command.py
runtime: python3
- timeout: 30`;
+ timeout: 60
+
+annotations:
+ requires_confirmation: true
+ read_only: false
+ idempotent: false
+ cost: medium`;
+
+const fullFinancialForecastYaml = `name: financial-forecast
+description: Run advanced financial modeling for TechStart Inc. Generates forecasts for burn rate, runway, and ARR projections based on current financial data.
+version: 1.0.0
-const toolCancelOrder = `name: cancel-order
-description: Cancels an order based on the provided order ID.
input_schema:
type: object
properties:
- order_id:
+ scenario:
type: string
- description: The unique identifier for the order to be cancelled
+ description: Forecast scenario to model (e.g., 'base', 'optimistic', 'conservative')
+ months:
+ type: integer
+ description: Number of months to forecast (default 12)
required:
- - order_id
+ - scenario
+
+output_schema:
+ type: object
+ properties:
+ forecast:
+ type: string
+ description: Financial forecast report in structured text format
+
implementation:
type: script
- path: tools/cancel_order.py
+ path: financial-forecast.py
runtime: python3
- timeout: 30`;
+ timeout: 30
-const validateCmd = `opengap validate -d ./customer-service-opengap
-opengap info -d ./customer-service-opengap`;
+annotations:
+ requires_confirmation: false
+ read_only: true
+ idempotent: true
+ cost: low`;
-const mapping = [
- ["SYSTEM_PROMPT passed to messages.create(system=...)", "SOUL.md"],
- ["MODEL_NAME ('claude-opus-4-1')", "agent.yaml → model.preferred"],
- ["tools[].name (kebab-case)", "agent.yaml → tools[]"],
- ["tools[].description + input_schema", "tools/.yaml"],
- ["process_tool_call() dispatch function", "stays in framework — tool implementation"],
- ["chatbot_interaction() loop / multi-turn logic", "stays in framework — runtime loop"],
+const validateCmd = `$ opengap validate
+✓ agent.yaml valid (spec 0.1.0)
+✓ SOUL.md present
+✓ RULES.md present
+✓ skills/delegate-subagent/SKILL.md valid frontmatter
+✓ tools/read-config-file.yaml schema ok → read-config-file.py
+✓ tools/run-shell-command.yaml schema ok → run-shell-command.py
+✓ tools/financial-forecast.yaml schema ok → financial-forecast.py
+✓ agents/research-agent/agent.yaml valid
+✓ agents/chief-of-staff/agent.yaml valid
+✓ agents/site-reliability-agent/agent.yaml valid
+✓ mcp_servers/github reachable
+ anthropic-cookbook is ready.`;
+
+/* ───────────────────────── Building blocks ───────────────────────── */
+
+const buckets = [
+ { icon: FileText, title: "Identity", file: "SOUL.md", desc: "Who the agent is" },
+ { icon: Shield, title: "Rules", file: "RULES.md", desc: "Hard guardrails" },
+ { icon: Workflow, title: "Orchestration", file: "skills/", desc: "How it researches & delegates" },
+ { icon: Settings, title: "Config & Tools", file: "agent.yaml · tools/", desc: "Model & capabilities" },
];
-const steps = [
- { step: "1", desc: "Copy SYSTEM_PROMPT into SOUL.md. Keep identity, purpose, and behavioral rules together — the Claude SDK has a single flat prompt with no separate rules block." },
- { step: "2", desc: "Take MODEL_NAME → write to agent.yaml → model.preferred (e.g. claude-opus-4-1)." },
- { step: "3", desc: "For each entry in tools[], add a kebab-case name to agent.yaml → tools. get_customer_info → get-customer-info." },
- { step: "4", desc: "Create tools/.yaml for each tool — copy the name (kebab-case), description, and input_schema directly from the tool dict." },
- { step: "5", desc: "The process_tool_call() dispatch and the multi-turn chatbot_interaction() loop stay in the notebook — they are runtime execution, not agent identity." },
- { step: "6", desc: "Run opengap validate to confirm the structure is correct." },
+const mapAtAGlance: [string, string][] = [
+ ["SYSTEM_PROMPT passed to messages.create(system=...)", "SOUL.md"],
+ ["Implicit behavioral rules in SYSTEM_PROMPT", "RULES.md"],
+ ["chatbot_interaction() tool loop + multi-turn logic", "skills/delegate-subagent/SKILL.md"],
+ ["MODEL_NAME + messages.create() config", "agent.yaml"],
+ ["tools[].name + input_schema dicts", "tools/.yaml + .py"],
];
+function PartHeader({ num, label, title, subtitle }: { num: string; label: string; title: string; subtitle: string }) {
+ return (
+
+
- Based on anthropics/anthropic-cookbook customer service agent — a multi-turn
- agent with three tools (get_customer_info, get_order_details, cancel_order)
- and a system prompt. The Anthropic SDK has no built-in agent format — everything lives as function arguments.
- Converting to OpenGAP means pulling those values into files.
+
+ A step-by-step guide to converting a Claude SDK (Anthropic) project into OpenGAP format by hand. We work through one real
+ project end to end — every file, the exact mapping, and the finished result — so you can follow the same
+ steps for your own agent.
- {/* Part 1 */}
-
-
Part 1 — The Claude SDK agent
-
E-commerce customer service with three tools and a multi-turn loop:
-
-
-
+ {/* Example + its use case */}
+
+
+
+
+ The example
+
+
+ anthropics/anthropic-cookbook — the canonical Claude SDK
+ agent cookbook. A Jupyter notebook that defines a customer service agent with a flat{" "}
+ tools[] list, a{" "}
+ SYSTEM_PROMPT string, and a manual{" "}
+ messages.create() loop — no agent class, no framework routing.
+
+
+
+
+ Use case: e-commerce customer service — the agent handles
+ customer inquiries by looking up customer info and order details, and processing cancellations. The v4 OpenGAP
+ output extends this into a full multi-agent system: research, chief-of-staff, observability, SRE, and
+ multi-agent research lead/subagent patterns — all with{" "}
+ claude-opus-4-8 as the preferred model.
+
+ process_tool_call() — the dispatch function that maps tool
+ names to Python implementations —,{" "}
+ chatbot_interaction() — the multi-turn message management loop —, and the
+ raw messages list accumulation have no OpenGAP files. Tool dispatch,
+ conversation history management, and the messages.create() call cycle are handled by the host runtime. You
+ describe what the agent does (identity, rules, skills, tools); the runtime owns{" "}
+ how the loop, message list, and tool dispatch execute.
+
+ From the agent directory, run opengap validate to confirm the
+ manifest, sub-agent configs, skill frontmatter, tool schemas, and MCP server references all resolve before you
+ run the agent.
+
+
diff --git a/src/components/opengap/cookbook/CookbookCodex.tsx b/src/components/opengap/cookbook/CookbookCodex.tsx
index 1bc12db..3ce76ff 100644
--- a/src/components/opengap/cookbook/CookbookCodex.tsx
+++ b/src/components/opengap/cookbook/CookbookCodex.tsx
@@ -1,142 +1,627 @@
-import { motion } from "framer-motion";
+import { motion, AnimatePresence } from "framer-motion";
+import { ArrowRight, FileText, Shield, Workflow, Settings, CheckCircle2, ChevronDown, BookOpen, Target, Package } from "lucide-react";
+import { useState } from "react";
import { CodeBlock } from "@/components/gitAgent/CodeBlock";
-const projectStructure = `my-project/
-└── AGENTS.md ← identity, rules, and tool declarations`;
+/* ═══════════════════ PART 1 — full Codex source ═══════════════════ */
-const agentsMd = `# AGENTS.md (example)
+const sourceTree = `my-project/ (Codex)
+└── AGENTS.md ← identity, rules, and tool declarations`;
+
+const srcAgentsMd = `# AGENTS.md
You are a backend API developer specializing in Node.js and Express.
-Help users build production-ready REST APIs with proper validation and error handling.
+Help users build production-ready REST APIs with proper validation
+and error handling.
Always validate inputs using Zod schemas.
Always return consistent JSON error responses.
Never expose stack traces in production error responses.
+Never return 200 for an error — use the correct HTTP status code.
+Log errors with context (route, method, status) before responding.
+
+## Tools
+- run_tests: Run the test suite
+- lint_code: Lint the codebase with ESLint`;
+
+/* ═══════════════════ PART 2 — paired mapping excerpts ═══════════════════ */
+
+const fromIdentity = `# AGENTS.md (identity paragraph)
+You are a backend API developer specializing
+in Node.js and Express.
+Help users build production-ready REST APIs
+with proper validation and error handling.`;
+
+const toSoul = `# Soul
+
+## Core Identity
+You are a backend API developer specializing
+in Node.js and Express.
+
+## Purpose
+Help users build production-ready REST APIs
+with proper validation and error handling.
+
+## Communication Style
+Direct and precise. I explain trade-offs
+before writing code and flag anything that
+would be unsafe in production.
+
+## Domain Expertise
+- Express.js REST API design
+- Input validation with Zod
+- Error handling and HTTP status codes
+- ESLint-clean, testable Node.js code`;
+
+const fromRules = `# AGENTS.md (rules section)
+Always validate inputs using Zod schemas.
+Always return consistent JSON error responses.
+Never expose stack traces in production
+ error responses.
+Never return 200 for an error — use the
+ correct HTTP status code.
+Log errors with context (route, method,
+ status) before responding.`;
+
+const toRules = `# RULES.md
+
+## Must Always
+- Validate all request inputs with Zod
+ schemas before processing
+- Return a consistent JSON error shape:
+ { error: { code, message } }
+- Use the correct HTTP status code for
+ every error response
+- Log errors with route, method, and status
+ before sending the response
+
+## Must Never
+- Expose raw stack traces or internal paths
+ in production error responses
+- Return HTTP 200 for an error condition`;
+const fromTools = `# AGENTS.md (## Tools section)
## Tools
- run_tests: Run the test suite
- lint_code: Lint the codebase with ESLint`;
-const agentYaml = `spec_version: 0.1.0
+const toToolFiles = `# agent.yaml (tools[] list)
+tools:
+ - run-tests
+ - lint-code
+
+# tools/run-tests.yaml (the contract)
+name: run-tests
+description: Run the project test suite.
+input_schema:
+ type: object
+ properties: {}
+implementation:
+ type: script
+ path: run_tests.sh
+ runtime: bash
+ timeout: 60
+
+# tools/lint-code.yaml
+name: lint-code
+description: Lint the codebase with ESLint.
+input_schema:
+ type: object
+ properties: {}
+implementation:
+ type: script
+ path: lint_code.sh
+ runtime: bash
+ timeout: 30`;
+
+const fromModel = `# Codex — model is set via environment or
+# API call; AGENTS.md has no model field.
+# e.g. OPENAI_MODEL=gpt-4o codex run`;
+
+const toAgentYaml = `# agent.yaml
+spec_version: "0.1.0"
+name: backend-api-agent
+version: 1.0.0
+description: Backend API developer for
+ production-ready Node.js / Express REST APIs.
+model:
+ preferred: openai:gpt-4o
+ fallback:
+ - anthropic:claude-sonnet-4-5-20250929
+runtime:
+ max_turns: 40
+ timeout: 180
+tools:
+ - run-tests
+ - lint-code`;
+
+/* ═══════════════════ PART 3 — full OpenGAP output ═══════════════════ */
+
+const outputTree = `my-project/ (OpenGAP)
+├── agent.yaml ← manifest: model, runtime, tool refs
+├── SOUL.md ← identity
+├── RULES.md ← guardrails
+└── tools/
+ ├── run-tests.yaml ← tool schema
+ ├── run_tests.sh ← tool implementation
+ ├── lint-code.yaml ← tool schema
+ └── lint_code.sh ← tool implementation`;
+
+const fullAgentYaml = `spec_version: "0.1.0"
name: backend-api-agent
-version: 0.1.0
-description: Backend API developer specializing in Node.js and Express
+version: 1.0.0
+description: Backend API developer specializing in Node.js and Express — production-ready REST APIs with proper validation, error handling, and clean code.
+
model:
- preferred: gpt-4o
+ preferred: openai:gpt-4o
+ fallback:
+ - anthropic:claude-sonnet-4-5-20250929
+
+runtime:
+ max_turns: 40
+ timeout: 180
+
tools:
- run-tests
- lint-code`;
-const soulMd = `# Soul
+const fullSoul = `# Soul
## Core Identity
You are a backend API developer specializing in Node.js and Express.
## Purpose
-Help users build production-ready REST APIs with proper validation and error handling.`;
+Help users build production-ready REST APIs with proper validation and error handling. I am designed for backend engineering conversations — designing routes, writing middleware, wiring validation, and producing clean, testable code.
-const rulesMd = `# Rules
+## Communication Style
+Direct and precise. I explain design trade-offs before writing code and always flag anything that would be unsafe or incorrect in a production environment. Code blocks are complete and runnable, not pseudocode.
-- Always validate inputs using Zod schemas
-- Always return consistent JSON error responses
-- Never expose stack traces in production error responses`;
+## Values & Principles
+- **Safety** — inputs are always validated; stack traces are never leaked
+- **Consistency** — error shapes and status codes follow a predictable contract
+- **Correctness** — HTTP semantics are respected (4xx for client errors, 5xx for server errors)
+- **Maintainability** — ESLint-clean code with clear separation of concerns
+
+## Domain Expertise
+- Express.js REST API design: routes, middleware, routers
+- Input validation with Zod: schema definition, parsing, error formatting
+- Error handling patterns: centralised error middleware, structured JSON error responses
+- HTTP status codes: 400, 401, 403, 404, 409, 422, 500 and when to use each
+- Testing Node.js APIs: Jest, Supertest, test isolation
+- ESLint configuration and lint-clean code
+
+## Collaboration Style
+I work interactively. I ask clarifying questions when requirements are ambiguous (which framework version? monorepo or single service?). For larger tasks I narrate the approach before writing code so the user can redirect early.`;
+
+const fullRules = `# Rules
+
+## Must Always
+- Validate all request inputs with Zod schemas before any processing logic runs
+- Return a consistent JSON error shape on every error: \`{ "error": { "code": "...", "message": "..." } }\`
+- Use the semantically correct HTTP status code for every response (400 Bad Request, 404 Not Found, 422 Unprocessable Entity, 500 Internal Server Error, etc.)
+- Log errors with context — at minimum: route, HTTP method, and response status — before sending the error response
+- Run \`lint-code\` on generated code before presenting it to the user when lint tooling is available
+
+## Must Never
+- Expose raw stack traces, file paths, or internal error messages in production error responses
+- Return HTTP 200 for an error condition — always use an appropriate 4xx or 5xx status
+- Skip input validation and trust caller-supplied data directly
+
+## Output Constraints
+- Code is Node.js / TypeScript, formatted for readability, with comments on non-obvious lines
+- Error middleware is always placed after all route definitions in Express
+
+## Interaction Boundaries
+- \`run-tests\` executes the project test suite; results are returned as stdout/stderr
+- \`lint-code\` runs ESLint on the codebase; lint errors are returned as structured output`;
+
+const fullRunTestsYaml = `name: run-tests
+description: Run the project test suite and return the results. Use after generating or modifying code to verify correctness.
+version: 1.0.0
-const toolRunTests = `name: run-tests
-description: Run the test suite for the project.
input_schema:
type: object
properties: {}
+ required: []
+
+output_schema:
+ type: object
+ properties:
+ stdout:
+ type: string
+ description: Test runner output (pass/fail summary)
+ stderr:
+ type: string
+ description: Error output from the test runner
+ exit_code:
+ type: integer
+ description: 0 = all tests passed, non-zero = failures
+
implementation:
type: script
- path: tools/run_tests.py
- runtime: python3
- timeout: 60`;
+ path: run_tests.sh
+ runtime: bash
+ timeout: 60
+
+annotations:
+ read_only: false
+ idempotent: true
+ cost: medium`;
+
+const fullRunTestsSh = `#!/usr/bin/env bash
+# run_tests.sh — execute the project test suite
+# Output: test runner stdout/stderr as JSON on stdout
+
+set -euo pipefail
+
+# Detect test runner (npm test, yarn test, or jest directly)
+if [ -f package.json ]; then
+ if command -v yarn &>/dev/null && [ -f yarn.lock ]; then
+ yarn test --ci 2>&1
+ else
+ npm test -- --ci 2>&1
+ fi
+else
+ echo '{"error": "No package.json found in working directory"}' >&2
+ exit 1
+fi`;
+
+const fullLintCodeYaml = `name: lint-code
+description: Lint the codebase with ESLint and report any issues. Use to verify generated code is lint-clean before presenting it.
+version: 1.0.0
-const toolLintCode = `name: lint-code
-description: Lint the codebase with ESLint and report issues.
input_schema:
type: object
properties: {}
+ required: []
+
+output_schema:
+ type: object
+ properties:
+ stdout:
+ type: string
+ description: ESLint output (file paths and rule violations)
+ exit_code:
+ type: integer
+ description: 0 = no lint errors, 1 = lint errors found
+
implementation:
type: script
- path: tools/lint_code.py
- runtime: python3
- timeout: 30`;
+ path: lint_code.sh
+ runtime: bash
+ timeout: 30
-const mapping = [
- ["AGENTS.md — identity + purpose paragraph", "SOUL.md → Core Identity + Purpose"],
- ["AGENTS.md — behavioral rules", "RULES.md"],
- ["AGENTS.md → Tools section — each tool name (kebab-case)", "agent.yaml → tools[] + tools/.yaml"],
+annotations:
+ read_only: true
+ idempotent: true
+ cost: low`;
+
+const fullLintCodeSh = `#!/usr/bin/env bash
+# lint_code.sh — run ESLint on the codebase
+
+set -euo pipefail
+
+if ! command -v npx &>/dev/null; then
+ echo '{"error": "npx not found — is Node.js installed?"}' >&2
+ exit 1
+fi
+
+npx eslint . --ext .js,.ts,.jsx,.tsx 2>&1`;
+
+const validateCmd = `$ opengap validate
+✓ agent.yaml valid (spec 0.1.0)
+✓ SOUL.md present
+✓ RULES.md present
+✓ tools/run-tests.yaml schema ok → run_tests.sh
+✓ tools/lint-code.yaml schema ok → lint_code.sh
+ backend-api-agent is ready.`;
+
+/* ───────────────────────── Building blocks ───────────────────────── */
+
+const buckets = [
+ { icon: FileText, title: "Identity", file: "SOUL.md", desc: "Who the agent is" },
+ { icon: Shield, title: "Rules", file: "RULES.md", desc: "Hard guardrails" },
+ { icon: Workflow, title: "Orchestration", file: "skills/", desc: "How it reasons (none here)" },
+ { icon: Settings, title: "Config & Tools", file: "agent.yaml · tools/", desc: "Model & capabilities" },
];
-const steps = [
- { step: "1", desc: "Open AGENTS.md. Copy the identity and purpose paragraph into SOUL.md." },
- { step: "2", desc: "Extract rule lines into RULES.md as a bullet list." },
- { step: "3", desc: "For each tool listed under ## Tools, add a kebab-case entry to agent.yaml → tools and create tools/.yaml." },
- { step: "4", desc: "Run opengap validate to confirm the structure is correct." },
+const mapAtAGlance: [string, string][] = [
+ ["AGENTS.md — identity paragraph", "SOUL.md"],
+ ["AGENTS.md — behavioral rules", "RULES.md"],
+ ["AGENTS.md — ## Tools list", "agent.yaml tools[] + tools/.yaml"],
+ ["Codex model (env / API)", "agent.yaml → model.preferred"],
];
+function PartHeader({ num, label, title, subtitle }: { num: string; label: string; title: string; subtitle: string }) {
+ return (
+
+
- Codex stores agent configuration in AGENTS.md — identity, rules, and tool declarations in one file.
- Converting to OpenGAP means splitting identity into SOUL.md, rules into
- RULES.md, and each tool into its own tools/<name>.yaml.
+
+ A step-by-step guide to converting a Codex agent into OpenGAP format by hand. Codex stores identity,
+ rules, and tool declarations together in a single AGENTS.md file.
+ We walk through one real project end to end — every file, the exact mapping, and the finished result.
-
-
Part 1 — The Codex project
-
Typical Codex workspace structure:
-
-
-
+ {/* Example + its use case */}
+
+
+
+
+ The example
+
+
+ An AGENTS.md-based backend API developer — a single markdown
+ file with an identity paragraph, behavioral rules, and a{" "}
+ ## Tools section listing{" "}
+ run_tests and{" "}
+ lint_code.
+
+
+
+
+ Use case: help users build production-ready
+ REST APIs in Node.js and Express — validating inputs with Zod, returning consistent JSON errors,
+ running the test suite, and linting generated code before handing it back.
+
Expand the file to read it in full — the mapping in Part 2 is what matters.
+
+
+
+
+
+
+ {/* ══════════════ PART 2 ══════════════ */}
+
+
+ {/* Mental model */}
+
+
+ {buckets.map((b) => (
+
+
+
+ {b.title}
+
+ {b.file}
+
{b.desc}
+
+ ))}
-
-
Part 2 — What maps to OpenGAP
-
+ {/* Map at a glance */}
+
+
The whole map at a glance
+
- CodexOpenGAP
+ Codex
+ OpenGAP
- {mapping.map(([from, to], i) => (
-
+ {mapAtAGlance.map(([from, to], i) => (
+
{from}
- {to}
+
+
+ {to}
+
))}
-
-
Part 3 — Create the OpenGAP files
-
-
agent.yaml:
-
SOUL.md:
-
RULES.md:
-
tools/run-tests.yaml:
-
tools/lint-code.yaml:
+
+
Now the same four mappings, in detail — Codex source on the left, the OpenGAP file it becomes on the right.
+
+
+
+
+
+
+
+ {/* What doesn't convert */}
+
+
+
What does not convert
+
+ Codex-specific shell execution context — Codex's built-in
+ sandboxed shell environment (network isolation, allowed-commands list, working-directory scope) has no
+ direct OpenGAP equivalent. The tools above call shell scripts, but sandbox policy must be configured at
+ the runtime / deployment level, not in the agent files. Any{" "}
+ allowedDomains or{" "}
+ disallowedCommands settings from Codex config
+ should be noted as comments in RULES.md for the
+ operator to enforce separately.
+
- Based on stephenc222/example-crewai — a financial analysis crew with
- a Financial Analyst agent and a Communications Specialist agent working in sequence.
- CrewAI's Agent(role, goal, backstory) maps cleanly to OpenGAP's
- SOUL.md sections; each agent becomes its own OpenGAP directory.
+
+ A step-by-step guide to converting a CrewAI multi-agent crew into OpenGAP format by hand. We work through one
+ real project end to end — every file, the exact mapping, and the finished result — so you can follow the same
+ steps for your own crew.
- {/* Part 1 */}
-
-
Part 1 — The CrewAI project
-
Financial analysis crew with Yahoo Finance news tool:
-
-
-
+ {/* Example + its use case */}
+
+
+
+
+ The example
+
+
+ example-crewai — a multi-domain crew with four specialized
+ agents across two workflows: a financial analysis crew (financial analyst + communications specialist) and a
+ talent search crew (recruitment specialist + HR communicator). Tools:{" "}
+ yahoo-finance-news and{" "}
+ google-jobs.
+
+
+
+
+ Use case: CrewAI runs agents in sequence via{" "}
+ Process.sequential — analyst first, communicator second.
+ OpenGAP replaces the Python orchestration with a declarative{" "}
+ workflow yaml, each agent's{" "}
+ role/backstory/goal with{" "}
+ SOUL.md, and the task descriptions with{" "}
+ skill files.
+
+ crew.kickoff(),{" "}
+ Process.sequential, and verbose=2 have no
+ OpenGAP file equivalents. The execution trigger, process type, and logging level are runtime concerns owned by the
+ host runtime — not by the agent. You describe what each agent does (soul, rules, skill, tools); the
+ runtime and workflow yaml own when and how the crew runs.
+
+ From the agent directory, run opengap validate to confirm the
+ manifest, skill frontmatter, tool schemas, and workflow steps all resolve before you run the agent.
+
+
diff --git a/src/components/opengap/cookbook/CookbookCursor.tsx b/src/components/opengap/cookbook/CookbookCursor.tsx
index 95f1ce6..f73dacd 100644
--- a/src/components/opengap/cookbook/CookbookCursor.tsx
+++ b/src/components/opengap/cookbook/CookbookCursor.tsx
@@ -1,112 +1,626 @@
-import { motion } from "framer-motion";
+import { motion, AnimatePresence } from "framer-motion";
+import { ArrowRight, FileText, Shield, Workflow, Settings, CheckCircle2, ChevronDown, BookOpen, Target, Package } from "lucide-react";
+import { useState } from "react";
import { CodeBlock } from "@/components/gitAgent/CodeBlock";
-const projectStructure = `my-project/
-├── .cursorrules ← identity and behavioral rules
+/* ═══════════════════ PART 1 — full Cursor source ═══════════════════ */
+
+const sourceTree = `my-project/ (Cursor)
+├── .cursorrules ← identity + rules (single flat file)
└── .cursor/
- └── settings.json ← model config`;
+ └── settings.json ← model and editor config`;
+
+const srcCursorRules = `# .cursorrules
+
+You are an expert React and TypeScript developer. Your role is to
+help build modern, accessible, and maintainable web applications.
+
+Always use functional components and React hooks — never class components.
+Prefer Tailwind CSS for styling; avoid custom CSS files unless unavoidable.
+Always define TypeScript interfaces for component props.
+Keep components small and focused on a single responsibility.
+Co-locate tests with components: Button.tsx → Button.test.tsx.
+Use React Query for server state; Zustand for client state.
+Never use \`any\` — always type explicitly or use \`unknown\`.
+Prefer named exports over default exports for components.`;
+
+const srcSettings = `{
+ "cursor.general.enableShadowWorkspace": true,
+ "cursor.chat.model": "claude-sonnet-4-6",
+ "cursor.chat.customApiKey": "",
+ "editor.formatOnSave": true,
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+}`;
+
+/* ═══════════════════ PART 2 — paired mapping excerpts ═══════════════════ */
+
+const fromCursorIdentity = `# .cursorrules (opening — identity)
+You are an expert React and TypeScript developer.
+Your role is to help build modern, accessible, and
+maintainable web applications.`;
+
+const toSoul = `# Soul
-const cursorRules = `# .cursorrules (example)
+## Core Identity
You are an expert React and TypeScript developer.
-Always use functional components and hooks.
-Prefer Tailwind CSS over custom CSS files.
-Never use class components.
-Always add PropTypes or TypeScript interfaces for props.
-Keep components small and focused on a single responsibility.`;
-
-const agentYaml = `spec_version: 0.1.0
-name: react-typescript-agent
-version: 0.1.0
-description: Expert React and TypeScript developer assistant
+
+I specialise in building modern, accessible, and
+maintainable web applications with React, TypeScript,
+and Tailwind CSS.
+
+## Purpose
+Help the user design and implement React components and
+application features that are well-typed, accessible,
+and easy to maintain.
+
+## Communication Style
+Direct. I show code over prose. When suggesting component
+changes I prefer showing the updated JSX rather than
+describing what to change. I flag accessibility issues
+as they arise, not as a separate audit step.
+
+## Values & Principles
+- **Type safety** — explicit types everywhere; no \`any\`
+- **Accessibility** — ARIA roles and semantic HTML by default
+- **Simplicity** — small, focused components over large ones`;
+
+const fromCursorRules = `# .cursorrules (rule lines)
+Always use functional components and React hooks —
+ never class components.
+Prefer Tailwind CSS for styling.
+Always define TypeScript interfaces for component props.
+Keep components small and focused on a single responsibility.
+Co-locate tests with components: Button.tsx → Button.test.tsx.
+Use React Query for server state; Zustand for client state.
+Never use \`any\` — always type explicitly or use \`unknown\`.
+Prefer named exports over default exports.`;
+
+const toRules = `# Rules
+
+## Must Always
+- Use functional components and React hooks — never class components
+- Define a TypeScript interface for every component's props
+- Co-locate test files with components: \`Button.tsx\` → \`Button.test.tsx\`
+- Use named exports for components (not default exports)
+- Add ARIA roles and labels to interactive elements
+- Use React Query for server state and Zustand for client state
+
+## Must Never
+- Write class components
+- Use the \`any\` type — use explicit types or \`unknown\` instead
+- Add custom CSS files when Tailwind classes cover the same need
+- Place tests in a separate \`__tests__\` directory away from the source
+
+## Output Constraints
+- Show updated JSX/TSX directly rather than describing changes in prose
+- Flag accessibility issues inline, not as a separate pass
+- Responses are in the same language the user writes in`;
+
+const fromCursorSkillHint = `# .cursorrules (implicit component workflow)
+Keep components small and focused on a single responsibility.
+Co-locate tests with components: Button.tsx → Button.test.tsx.
+Prefer named exports over default exports for components.`;
+
+const toSkill = `# skills/react-component/SKILL.md
+---
+name: react-component
+description: "Scaffold a React + TypeScript component following
+ project conventions: typed props interface, Tailwind styling,
+ co-located test, named export. Triggers on: create component,
+ add component, new component, build UI, make a button/card/modal."
+allowed-tools: Read, Write
+---
+
+## Step 1: Define the props interface
+Create a \`Props\` TypeScript interface. Every prop must
+have an explicit type — no \`any\`, no optional props without
+a clear default.
+
+## Step 2: Write the component
+Use a functional component with the props interface. Style
+with Tailwind classes. Add ARIA roles and labels for any
+interactive element.
+
+## Step 3: Add the named export
+Export the component by name at the bottom of the file:
+\`export { };\`
+
+## Step 4: Create the co-located test
+Create \`.test.tsx\` in the same directory. Write at
+least one test that renders the component and asserts its
+visible output.`;
+
+const fromSettings = `# .cursor/settings.json
+{
+ "cursor.chat.model": "claude-sonnet-4-6",
+ "editor.formatOnSave": true
+}`;
+
+const toAgentYaml = `# agent.yaml
+spec_version: "0.1.0"
+name: react-ts-developer
+version: 1.0.0
+description: Expert React and TypeScript developer that builds
+ modern, accessible, and maintainable web applications.
+model:
+ preferred: anthropic:claude-sonnet-4-6
+ fallback:
+ - openai:gpt-4o
+runtime:
+ max_turns: 50
+ timeout: 300
+skills:
+ - react-component`;
+
+/* ═══════════════════ PART 3 — full OpenGAP output ═══════════════════ */
+
+const outputTree = `react-ts-developer/ (OpenGAP)
+├── agent.yaml ← manifest: model, runtime, refs
+├── SOUL.md ← identity
+├── RULES.md ← guardrails
+└── skills/
+ └── react-component/
+ └── SKILL.md ← component scaffolding workflow`;
+
+const fullAgentYaml = `spec_version: "0.1.0"
+name: react-ts-developer
+version: 1.0.0
+description: Expert React and TypeScript developer that builds modern, accessible, and maintainable web applications.
+
model:
- preferred: claude-sonnet-4-6`;
+ preferred: anthropic:claude-sonnet-4-6
+ fallback:
+ - openai:gpt-4o
-const soulMd = `# Soul
+runtime:
+ max_turns: 50
+ timeout: 300
+
+skills:
+ - react-component`;
+
+const fullSoul = `# Soul
## Core Identity
-You are an expert React and TypeScript developer.`;
+You are an expert React and TypeScript developer.
-const rulesMd = `# Rules
+I specialise in building modern, accessible, and maintainable web applications with React, TypeScript, and Tailwind CSS.
-- Always use functional components and hooks
-- Prefer Tailwind CSS over custom CSS files
-- Never use class components
-- Always add TypeScript interfaces for props
-- Keep components small and focused on a single responsibility`;
+## Purpose
+Help the user design and implement React components and application features that are well-typed, accessible, and easy to maintain long-term.
-const mapping = [
- [".cursorrules — identity line", "SOUL.md → Core Identity"],
- [".cursorrules — behavioral rules", "RULES.md"],
- [".cursor/settings.json → model", "agent.yaml → model.preferred"],
+My process:
+1. Understand what the component or feature needs to do
+2. Define the props interface and data shape first
+3. Implement the component with Tailwind styling and proper ARIA attributes
+4. Write a co-located test that asserts visible behaviour
+5. Suggest refactors when existing components can absorb new requirements
+
+## Communication Style
+Direct. I show code over prose. When suggesting component changes I prefer showing the updated JSX/TSX rather than describing what to change. I flag accessibility issues as they arise, not as a separate audit step at the end.
+
+## Values & Principles
+- **Type safety** — explicit types everywhere; \`any\` is never acceptable; use \`unknown\` and narrow when needed
+- **Accessibility** — ARIA roles and semantic HTML are not optional extras; they are part of the component
+- **Simplicity** — small, focused components over large multi-concern ones
+- **Colocation** — tests, styles, and stories live next to the component they cover
+
+## Domain Expertise
+- React (functional components, hooks, Suspense, Error Boundaries)
+- TypeScript (strict mode, discriminated unions, generics, utility types)
+- Tailwind CSS (utility-first styling, responsive design, dark mode)
+- React Query for server state management
+- Zustand for client state management
+- Testing with Vitest and React Testing Library
+
+## Collaboration Style
+I work turn by turn with the user. I do not rewrite unrelated parts of the codebase. I surface potential improvements (missing ARIA, implicit \`any\`, large component) as inline comments rather than blocking the current request.`;
+
+const fullRules = `# Rules
+
+## Must Always
+- Use functional components and React hooks — never class components
+- Define a TypeScript interface for every component's props (no inline object types for props)
+- Co-locate test files with components: \`Button.tsx\` → \`Button.test.tsx\` in the same directory
+- Use named exports for components — not default exports
+- Add ARIA roles, labels, and keyboard handling to all interactive elements
+- Use React Query for server state (fetching, caching, mutations)
+- Use Zustand for client-side UI state that must survive re-renders
+
+## Must Never
+- Write class components or use \`React.Component\`
+- Use the \`any\` type — use explicit types, generics, or \`unknown\` with narrowing instead
+- Add custom CSS files when the same result is achievable with Tailwind utility classes
+- Place test files in a separate \`__tests__\` directory away from the source file they cover
+- Use default exports for React components
+
+## Output Constraints
+- Show updated JSX/TSX directly rather than describing what to change in prose
+- Flag accessibility issues inline as the component is written
+- When reviewing existing code, show a diff rather than the full rewritten file
+- Responses are in the same natural language the user writes in
+
+## Interaction Boundaries
+- Tool use is limited to Read and Write (scoped to src/)
+- Do not modify configuration files (vite.config.ts, tsconfig.json, tailwind.config.ts) unless explicitly asked`;
+
+const fullSkill = `---
+name: react-component
+description: "Scaffold a React + TypeScript component following project conventions: typed
+ props interface, Tailwind styling, co-located test file, named export. Use for any new
+ UI element. Triggers on: create component, add component, new component, build UI,
+ make a button/card/modal/form/table."
+allowed-tools: Read, Write
+metadata:
+ version: "1.0.0"
+ category: ui
+---
+
+# React Component (scaffold)
+
+Creates a React functional component with a typed props interface, Tailwind styling, ARIA attributes, and a co-located test file.
+
+## Step 1: Understand the requirement
+Read the user's request. Identify:
+- What does the component render?
+- What data does it receive (props)?
+- What events or interactions does it handle?
+- Are there existing components nearby that should be referenced for style conventions?
+
+## Step 2: Define the props interface
+Write a \`Props\` TypeScript interface. Rules:
+- Every prop must have an explicit type (no \`any\`)
+- Mark truly optional props with \`?\` and document the default
+- Use discriminated unions for mutually exclusive prop combinations
+
+## Step 3: Write the component
+Implement as a named functional component:
+\`\`\`tsx
+function Button({ label, onClick, disabled = false }: ButtonProps) {
+ return (
+
+ );
+}
+\`\`\`
+Use Tailwind classes for all styling. Add ARIA attributes to interactive elements.
+
+## Step 4: Export by name
+At the bottom of the file:
+\`\`\`ts
+export { Button };
+export type { ButtonProps };
+\`\`\`
+
+## Step 5: Create the co-located test
+Create \`.test.tsx\` in the same directory:
+\`\`\`tsx
+import { render, screen } from "@testing-library/react";
+import { Button } from "./Button";
+
+it("renders the label", () => {
+ render(