diff --git a/src/components/SkillsFlowSection.tsx b/src/components/SkillsFlowSection.tsx
index 4f7094c..d776fab 100644
--- a/src/components/SkillsFlowSection.tsx
+++ b/src/components/SkillsFlowSection.tsx
@@ -1,5 +1,6 @@
+import { useState } from "react";
import { motion } from "framer-motion";
-import { GitFork, Zap, MessageSquare, Wrench, ArrowDown } from "lucide-react";
+import { GitFork, Zap, MessageSquare, Wrench, ArrowDown, Copy, Check } from "lucide-react";
const features = [
{
@@ -71,6 +72,14 @@ error_handling:
channel: "#eng-reviews"`;
export function SkillsFlowSection() {
+ const [copied, setCopied] = useState(false);
+
+ const handleCopy = () => {
+ navigator.clipboard.writeText(yamlCode);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ };
+
return (
@@ -100,6 +109,13 @@ export function SkillsFlowSection() {
workflow
+
+ {copied ? : }
+
{yamlCode}
diff --git a/src/components/SkillsSection.tsx b/src/components/SkillsSection.tsx
index 78f78c6..8c57940 100644
--- a/src/components/SkillsSection.tsx
+++ b/src/components/SkillsSection.tsx
@@ -1,5 +1,6 @@
+import { useState } from "react";
import { motion } from "framer-motion";
-import { Zap, Search, Download, List, Folder } from "lucide-react";
+import { Zap, Search, Download, List, Folder, Copy, Check } from "lucide-react";
const discoveryPaths = [
{ path: "/skills/", source: "Agent-local", priority: 1 },
@@ -10,13 +11,47 @@ const discoveryPaths = [
];
const skillCommands = [
- { icon: Search, cmd: "gitagent skills search \"code review\"", desc: "Search SkillsMP or GitHub" },
- { icon: Download, cmd: "gitagent skills install code-review --global", desc: "Install to global or local" },
- { icon: List, cmd: "gitagent skills list", desc: "List discovered skills" },
- { icon: Zap, cmd: "gitagent skills info code-review", desc: "Inspect skill metadata" },
+ { icon: Search, cmd: "opengap skills search \"code review\"", desc: "Search SkillsMP or GitHub" },
+ { icon: Download, cmd: "opengap skills install code-review --global", desc: "Install to global or local" },
+ { icon: List, cmd: "opengap skills list", desc: "List discovered skills" },
+ { icon: Zap, cmd: "opengap skills info code-review", desc: "Inspect skill metadata" },
];
+const skillMdContent = `---
+name: code-review
+description: Thorough code reviews
+license: MIT
+compatibility: ">=0.4.0"
+allowed-tools: Read Edit Grep Glob Bash
+metadata:
+ author: "Jane Doe"
+ version: "1.0.0"
+ category: "developer-tools"
+---
+
+# Instructions
+
+Review the code for:
+1. Security vulnerabilities
+2. Performance issues
+3. Code style consistency`;
+
export function SkillsSection() {
+ const [copiedSkill, setCopiedSkill] = useState(false);
+ const [copiedCmd, setCopiedCmd] = useState(null);
+
+ const handleSkillCopy = () => {
+ navigator.clipboard.writeText(skillMdContent);
+ setCopiedSkill(true);
+ setTimeout(() => setCopiedSkill(false), 2000);
+ };
+
+ const handleCmdCopy = (cmd: string) => {
+ navigator.clipboard.writeText(cmd);
+ setCopiedCmd(cmd);
+ setTimeout(() => setCopiedCmd(null), 2000);
+ };
+
return (
@@ -43,25 +78,21 @@ export function SkillsSection() {
viewport={{ once: true }}
>
SKILL.md Format
-
-
{`---
-name: code-review
-description: Thorough code reviews
-license: MIT
-compatibility: ">=0.1.0"
-allowed-tools: Read Edit Grep Glob Bash
-metadata:
- author: "Jane Doe"
- version: "1.0.0"
- category: "developer-tools"
----
-
-# Instructions
-
-Review the code for:
-1. Security vulnerabilities
-2. Performance issues
-3. Code style consistency`}
+
+
+
+
+
+ SKILL.md
+
+ {copiedSkill ? : }
+
+
+
{skillMdContent}
Discovery Priority
@@ -97,6 +128,13 @@ Review the code for:
{s.desc}
+ handleCmdCopy(s.cmd)}
+ className="ml-auto text-muted-foreground/30 hover:text-foreground transition-colors shrink-0"
+ aria-label="Copy command"
+ >
+ {copiedCmd === s.cmd ? : }
+
$ {s.cmd}
diff --git a/src/components/WhySection.tsx b/src/components/WhySection.tsx
index 99fbc97..02d9191 100644
--- a/src/components/WhySection.tsx
+++ b/src/components/WhySection.tsx
@@ -10,7 +10,7 @@ const features: { label: string; desc: string; icon: LucideIcon }[] = [
},
{
label: "Framework-Agnostic",
- desc: "Works with Claude Code, OpenAI, CrewAI, LangChain, and more. Define once, export anywhere.",
+ desc: "Works with Claude Code, OpenAI, CrewAI, Gemini, and more. Define once, export anywhere.",
icon: Blocks,
},
{
@@ -35,7 +35,7 @@ export function WhySection() {
viewport={{ once: true }}
className="mb-12"
>
-
Why GitAgent: The Git-Native AI Agent Standard
+
Why OpenGAP: The Git-Native AI Agent Standard
Everything your agent needs, defined in files you already know how to manage.
diff --git a/src/components/gitAgent/CodeBlock.tsx b/src/components/gitAgent/CodeBlock.tsx
new file mode 100644
index 0000000..68005b7
--- /dev/null
+++ b/src/components/gitAgent/CodeBlock.tsx
@@ -0,0 +1,60 @@
+import { useState } from "react";
+import { Copy, Check } from "lucide-react";
+import { track } from "@/lib/analytics";
+
+interface CodeBlockProps {
+ code: string;
+ filename?: string;
+ trackEvent?: string;
+ codeClassName?: string;
+ className?: string;
+}
+
+export function CodeBlock({
+ code,
+ filename,
+ trackEvent,
+ codeClassName,
+ className,
+}: CodeBlockProps) {
+ const [copied, setCopied] = useState(false);
+
+ const handleCopy = () => {
+ navigator.clipboard.writeText(code);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ if (trackEvent) track(trackEvent);
+ };
+
+ return (
+
+
+
+
+
+ {filename && (
+ {filename}
+ )}
+
+ {copied ? (
+
+ ) : (
+
+ )}
+
+
+
+ {code}
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentArchitecture.tsx b/src/components/gitAgent/GitAgentArchitecture.tsx
new file mode 100644
index 0000000..10f9fc1
--- /dev/null
+++ b/src/components/gitAgent/GitAgentArchitecture.tsx
@@ -0,0 +1,262 @@
+import { useState } from "react";
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const directoryTree = `my-agent/
+├── agent.yaml # Model, tools, runtime config
+├── SOUL.md # Agent identity & personality
+├── RULES.md # Behavioral rules & constraints
+├── DUTIES.md # Role-specific responsibilities
+├── memory/
+│ └── MEMORY.md # Git-committed agent memory
+├── tools/
+│ └── *.yaml # Declarative tool definitions
+├── skills/
+│ └──
/
+│ ├── SKILL.md # Skill instructions (YAML frontmatter)
+│ └── scripts/ # Skill scripts
+├── workflows/
+│ └── *.yaml|*.md # Multi-step workflow definitions
+├── agents/
+│ └── / # Sub-agent definitions
+├── plugins/
+│ └── / # Local plugins (plugin.yaml + tools/hooks/skills)
+├── hooks/
+│ └── hooks.yaml # Lifecycle hook scripts
+├── knowledge/
+│ └── index.yaml # Knowledge base entries
+├── config/
+│ ├── default.yaml # Default environment config
+│ └── .yaml # Environment overrides
+├── examples/
+│ └── *.md # Few-shot examples
+└── compliance/
+ └── *.yaml # Compliance & audit config`;
+
+const identityFiles = [
+ {
+ name: "SOUL.md",
+ desc: "Agent personality, identity, core values",
+ },
+ {
+ name: "RULES.md",
+ desc: "Behavioral constraints and rules",
+ },
+ {
+ name: "DUTIES.md",
+ desc: "Job responsibilities and tasks",
+ },
+ {
+ name: "AGENTS.md",
+ desc: "Sub-agent relationships and delegation rules",
+ },
+];
+
+const yamlTabs = [
+ {
+ label: "Minimal",
+ code: `spec_version: "0.4.0"
+name: my-agent
+version: 1.0.0
+description: An agent that does things
+
+model:
+ preferred: "anthropic:claude-sonnet-4-6"
+ fallback: ["openai:gpt-4o"]
+ constraints:
+ temperature: 0.7
+ max_tokens: 4096
+
+tools: [cli, read, write, memory]
+
+runtime:
+ max_turns: 50
+ timeout: 120`,
+ },
+ {
+ label: "Full (with compliance)",
+ code: `spec_version: "0.4.0"
+name: my-agent
+version: 1.0.0
+description: A description of what this agent does
+
+model:
+ preferred: "anthropic:claude-sonnet-4-6"
+ fallback:
+ - "openai:gpt-4o"
+ - "google:gemini-2.0-flash-001"
+ constraints:
+ temperature: 0.7
+ max_tokens: 4096
+
+tools:
+ - cli
+ - read
+ - write
+ - memory
+ - capture_photo
+ - task_tracker
+ - skill_learner
+
+skills:
+ - code-review
+ - deployment
+
+runtime:
+ max_turns: 50
+ timeout: 300
+
+extends: https://github.com/user/parent-agent.git
+
+dependencies:
+ - name: shared-skills
+ source: https://github.com/team/shared-skills
+ version: main
+ mount: deps/shared
+
+agents:
+ researcher:
+ model: "anthropic:claude-haiku-4-5-20251001"
+ tools: [read, cli]
+
+delegation:
+ mode: auto
+
+plugins:
+ my-plugin:
+ enabled: true
+ config:
+ api_key: "\${MY_PLUGIN_KEY}"
+
+compliance:
+ risk_level: high
+ human_in_the_loop: true
+ data_classification: "confidential"
+ regulatory_frameworks: [SOX, GLBA]
+ recordkeeping:
+ audit_logging: true
+ retention_days: 2555
+ review:
+ required_approvers: 2
+ auto_review: false
+
+serve:
+ port: 8080
+ allowed_tools: [lookup_account, get_policy]
+ constraints:
+ temperature: 0
+ max_tokens: 4000`,
+ },
+];
+
+export function GitAgentArchitecture() {
+ const [activeTab, setActiveTab] = useState(0);
+
+ return (
+
+
+ {/* Section heading */}
+
+
+ Agent Directory Structure
+
+
+ Every agent is a Git repository. These files define its identity, behavior, and capabilities.
+
+
+
+ {/* A. Directory Tree */}
+
+
+ Directory Layout
+
+
+
+
+ {/* B. Identity Files — 2×2 grid */}
+
+
+ Identity Files
+
+
+ {identityFiles.map((file, i) => (
+
+
+
+ {file.name}
+
+
+
+ {file.desc}
+
+
+ ))}
+
+
+
+ {/* C. Tabbed agent.yaml */}
+
+
+ agent.yaml Schema
+
+
+ {/* Tabs */}
+
+ {yamlTabs.map((tab, i) => (
+ setActiveTab(i)}
+ className={`px-3 sm:px-4 py-2 sm:py-2.5 text-xs font-body transition-colors relative ${
+ activeTab === i
+ ? "text-foreground"
+ : "text-muted-foreground hover:text-foreground"
+ }`}
+ >
+ {tab.label}
+ {activeTab === i && (
+
+ )}
+
+ ))}
+
+
+ {/* Code block */}
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentArchitectureSection.tsx b/src/components/gitAgent/GitAgentArchitectureSection.tsx
new file mode 100644
index 0000000..c3ba524
--- /dev/null
+++ b/src/components/gitAgent/GitAgentArchitectureSection.tsx
@@ -0,0 +1,202 @@
+import { useState } from "react";
+import { motion } from "framer-motion";
+
+const dirTree = `~/assistant/ # Agent root (git repo)
+├── agent.yaml # Agent manifest
+├── SOUL.md # Agent identity
+├── RULES.md # Behavior rules (optional)
+├── DUTIES.md # Responsibilities (optional)
+├── .env # API keys (gitignored)
+├── workspace/ # Output directory
+├── memory/
+│ ├── MEMORY.md # Primary memory
+│ ├── mood.md # Mood tracking
+│ ├── photos/ # Captured moments
+│ ├── journal/ # Session reflections
+│ └── archive/ # Archived entries
+├── skills/
+│ └── skill-name/
+│ └── SKILL.md
+├── workflows/ # SkillFlows + .md workflows
+├── schedules/ # Cron jobs
+├── hooks/
+│ ├── hooks.yaml
+│ └── validate.sh # example name — any .sh filename works
+├── tools/ # Custom declarative tools
+├── plugins/ # Installed plugins
+├── config/
+│ ├── default.yaml
+│ └── production.yaml
+├── knowledge/ # Knowledge base
+├── compliance/ # Compliance config
+└── .gitagent/ # Internal state (gitignored)
+ ├── state.json
+ ├── audit.jsonl
+ └── learning/`;
+
+const tabs = [
+ {
+ label: "agent.yaml",
+ code: `spec_version: "0.4.0"
+name: my-agent
+version: 1.0.0
+description: An agent that does things
+
+model:
+ preferred: "anthropic:claude-sonnet-4-6"
+ fallback: ["openai:gpt-4o", "google:gemini-2.0-flash-001"]
+ constraints:
+ temperature: 0.7
+ max_tokens: 4096
+
+tools: [cli, read, write, memory]
+
+runtime:
+ max_turns: 50
+ timeout: 300
+
+# Optional
+extends: "https://github.com/org/base-agent.git"
+skills: [code-review, deploy]
+delegation:
+ mode: auto
+compliance:
+ risk_level: high
+ human_in_the_loop: true
+ recordkeeping:
+ audit_logging: true`,
+ },
+ {
+ label: "SOUL.md",
+ code: `# Soul
+
+## Core Identity
+I am a focused, reliable autonomous coding agent.
+I analyze, implement, test, and document — with
+minimal interruption to the developer.
+
+## Communication Style
+Direct and concise. I report what I did and why,
+not a running commentary on my process.
+
+## Values & Principles
+- Correctness over speed
+- Every change is reviewable
+- No silent mutations to files I wasn't asked to touch
+- Ask before deleting or overwriting
+
+## Domain Expertise
+- Software architecture and code quality
+- Security review (OWASP Top 10)
+- Performance optimization
+- Git workflows and code review`,
+ },
+ {
+ label: "RULES.md",
+ code: `# Rules
+
+1. **Read before modifying** — always read a file
+ before editing it.
+
+2. **No destructive commands** — never run
+ \`rm -rf\`, \`git reset --hard\`, or \`DROP TABLE\`
+ without explicit confirmation.
+
+3. **No secrets in memory** — never write API keys,
+ passwords, or tokens to MEMORY.md.
+
+4. **Stay in scope** — only touch files that are
+ directly relevant to the current task.
+
+5. **Report errors honestly** — if something fails,
+ report the exact error; do not fabricate results.`,
+ },
+];
+
+export function GitAgentArchitectureSection() {
+ const [active, setActive] = useState(0);
+
+ return (
+
+
+
+
+ 04 — Architecture
+
+
+ Architecture
+
+
+ Everything your agent needs, defined in plain files you already know how to manage.
+
+
+
+
+ {/* Directory tree */}
+
+
+ Directory Structure
+
+
+
+
+
+
+ ~/assistant/
+
+
+ {dirTree}
+
+
+
+
+ {/* Tabbed config files */}
+
+
+ Core Files
+
+ {/* Tabs */}
+
+ {tabs.map((t, i) => (
+ setActive(i)}
+ className={`px-3 py-2 text-xs font-body transition-colors relative ${
+ active === i ? "text-foreground" : "text-muted-foreground hover:text-foreground"
+ }`}
+ >
+ {t.label}
+ {active === i && (
+
+ )}
+
+ ))}
+
+
+
+ {tabs[active].code}
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentCLI.tsx b/src/components/gitAgent/GitAgentCLI.tsx
new file mode 100644
index 0000000..6c1ac38
--- /dev/null
+++ b/src/components/gitAgent/GitAgentCLI.tsx
@@ -0,0 +1,159 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const cliFlags = [
+ { flag: "--dir ", short: "-d", desc: "Agent directory", default: "current working directory" },
+ { flag: "--model ", short: "-m", desc: "Override model from agent.yaml", default: "from agent.yaml" },
+ { flag: "--prompt \"text\"", short: "-p", desc: "Run a single prompt (non-interactive)", default: "—" },
+ { flag: "--env ", short: "-e", desc: "Load config/.yaml environment overrides", default: "default" },
+ { flag: "--voice", short: "-v", desc: "Start Web UI + voice server at localhost:3333", default: "—" },
+ { flag: "--sandbox", short: "-s", desc: "Run agent inside an E2B cloud VM", default: "false" },
+ { flag: "--sandbox-repo ", short: "—", desc: "Clone a repo into the sandbox", default: "—" },
+ { flag: "--sandbox-token ", short: "—", desc: "Git token for cloning (falls back to GITHUB_TOKEN / GIT_TOKEN)", default: "—" },
+ { flag: "--repo ", short: "—", desc: "Work on a remote git repo locally", default: "—" },
+ { flag: "--pat ", short: "—", desc: "Personal access token for --repo", default: "—" },
+ { flag: "--session ", short: "—", desc: "Session branch name for --repo mode", default: "auto-generated" },
+];
+
+const replCommands = [
+ { cmd: "/quit or /exit", desc: "Exit the session" },
+ { cmd: "/memory", desc: "View the current memory file" },
+ { cmd: "/skills", desc: "List all installed skills" },
+ { cmd: "/learned", desc: "Show learned skills with confidence, usage, and success stats" },
+ { cmd: "/tasks", desc: "Show currently active tasks" },
+ { cmd: "/plugins", desc: "List loaded plugins and what they provide" },
+ { cmd: "/skill:", desc: "Execute a specific skill (e.g. /skill:deploy)" },
+];
+
+const pluginCliCode = `gitagent plugin install https://github.com/user/plugin-repo
+gitagent plugin install ./local/path --name my-plugin --force
+gitagent plugin list --dir ~/assistant
+gitagent plugin enable my-plugin --dir ~/assistant
+gitagent plugin disable my-plugin --dir ~/assistant
+gitagent plugin remove my-plugin --dir ~/assistant
+gitagent plugin init my-plugin --dir ~/assistant`;
+
+export function GitAgentCLI() {
+ return (
+
+
+
+ CLI
+
+ The primary way to run GitAgent. Starts an interactive REPL in your terminal.
+
+
+
+ {/* Basic usage */}
+
+
+ Basic Usage
+
+
+
+
+
+ {/* Left: CLI Flags */}
+
+
+ All Flags
+
+
+ {cliFlags.map((row, i) => (
+
+
+
+
+ {row.flag}
+
+ {row.short !== "—" && (
+
+ {row.short}
+
+ )}
+
+
+
+ {row.desc}
+
+
+ default: {row.default}
+
+
+
+
+ ))}
+
+
+
+ {/* Right: REPL Commands + Plugin CLI */}
+
+
+
+ REPL Commands
+
+
+ {replCommands.map((row, i) => (
+
+
+
+ {row.cmd}
+
+
—
+
+ {row.desc}
+
+
+
+ ))}
+
+
+
+
+
+ Plugin CLI
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentCompliance.tsx b/src/components/gitAgent/GitAgentCompliance.tsx
new file mode 100644
index 0000000..6ce062f
--- /dev/null
+++ b/src/components/gitAgent/GitAgentCompliance.tsx
@@ -0,0 +1,229 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const riskLevels = [
+ { level: "low", label: "Low", desc: "Minimal — standard logging", color: "text-green-600", bg: "bg-green-500/10" },
+ { level: "medium", label: "Medium", desc: "Audit logging recommended", color: "text-yellow-600", bg: "bg-yellow-500/10" },
+ {
+ level: "high",
+ label: "High",
+ desc: "HITL required, audit logging, compliance artifacts",
+ color: "text-orange-600",
+ bg: "bg-orange-500/10",
+ },
+ {
+ level: "critical",
+ label: "Critical",
+ desc: "Kill switch required, immutable logs, quarterly validation — prints a compliance error at startup if audit_logging missing (execution still continues)",
+ color: "text-destructive",
+ bg: "bg-destructive/10",
+ },
+];
+
+const complianceYaml = `compliance:
+ risk_level: critical # low | medium | high | critical
+ human_in_the_loop: true
+ data_classification: "PCI-DSS"
+ regulatory_frameworks: [SOX, GLBA, OCC]
+ recordkeeping:
+ audit_logging: true
+ retention_days: 2555 # 7 years for banking
+ review:
+ required_approvers: 2
+ auto_review: false`;
+
+const validationRules = [
+ {
+ rule: "high_risk_hitl",
+ condition: "High/critical risk without human_in_the_loop",
+ severity: "warning",
+ },
+ {
+ rule: "critical_audit",
+ condition: "Critical risk without audit_logging",
+ severity: "error",
+ },
+ {
+ rule: "regulatory_recordkeeping",
+ condition: "Regulatory frameworks without recordkeeping",
+ severity: "warning",
+ },
+ {
+ rule: "high_risk_review",
+ condition: "High/critical risk without review config",
+ severity: "warning",
+ },
+ {
+ rule: "audit_retention",
+ condition: "Audit logging without retention_days",
+ severity: "warning",
+ },
+];
+
+const auditLogJson = `{"timestamp":"2026-01-15T14:23:45Z","session_id":"uuid","event":"session_start"}
+{"timestamp":"2026-01-15T14:23:46Z","session_id":"uuid","event":"tool_use","tool":"cli","args":{"command":"ls"}}
+{"timestamp":"2026-01-15T14:23:47Z","session_id":"uuid","event":"tool_result","tool":"cli","result":"file.txt"}
+{"timestamp":"2026-01-15T14:23:48Z","session_id":"uuid","event":"response"}
+{"timestamp":"2026-01-15T14:23:49Z","session_id":"uuid","event":"session_end"}`;
+
+export function GitAgentCompliance() {
+ return (
+
+
+
+ Compliance
+
+ If your agent handles sensitive data — financial records, PII, regulated workflows — GitAgent can enforce audit logging, human-in-the-loop approval, and regulatory recordkeeping automatically.
+
+
+ Set risk_level in agent.yaml and GitAgent validates your config at startup. Missing a required setting? It prints a warning before the agent runs.
+
+
+
+
+ {/* A. Risk Levels */}
+
+
+ Risk Levels
+
+
+ {riskLevels.map((r, i) => (
+
+
+
+ {r.label}
+
+
{r.desc}
+
+
+ ))}
+
+
+
+ {/* B. Compliance config */}
+
+
+ Compliance Config (agent.yaml)
+
+
+
+
+
+ {/* C. Validation Rules */}
+
+
+ What gets validated at startup
+
+ GitAgent checks your compliance config when the agent loads and warns you about gaps before any session starts.
+
+
+ {validationRules.map((r, i) => (
+
+
+
+ {r.rule}
+
+ {r.condition}
+
+ {r.severity}
+
+
+
+ ))}
+
+
+
+
+ {/* D. Regulatory Frameworks */}
+
+
+ Supported Regulatory Frameworks
+
+
+ {[
+ { name: "SOX", desc: "Sarbanes-Oxley financial recordkeeping" },
+ { name: "GLBA", desc: "Gramm-Leach-Bliley Act financial privacy" },
+ { name: "OCC", desc: "Office of the Comptroller of the Currency" },
+ { name: "GDPR", desc: "General Data Protection Regulation" },
+ { name: "SOC2", desc: "Service Organization Control 2" },
+ { name: "FINRA", desc: "Financial Industry Regulatory Authority" },
+ ].map((f, i) => (
+
+ {f.name}
+ {f.desc}
+
+ ))}
+
+
+
+ {/* E. Audit Log format */}
+
+
+ Audit Log Format
+
+
+
+ Logged to{" "}
+ .gitagent/audit.jsonl when{" "}
+ audit_logging: true
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentCookbookCodeReview.tsx b/src/components/gitAgent/GitAgentCookbookCodeReview.tsx
new file mode 100644
index 0000000..490706d
--- /dev/null
+++ b/src/components/gitAgent/GitAgentCookbookCodeReview.tsx
@@ -0,0 +1,238 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const part1Setup = `git clone https://github.com/your-org/code-review-agent
+cd code-review-agent`;
+
+const agentYaml = `spec_version: "0.4.0"
+name: code-review-agent
+version: 1.0.0
+description: An agent that reviews pull request diffs across security, correctness, and performance
+
+model:
+ preferred: "anthropic:claude-sonnet-4-6"
+ fallback: []
+
+tools:
+ - cli
+ - read
+ - write
+
+runtime:
+ max_turns: 15
+ timeout: 120`;
+
+const soulMd = `# Soul
+
+## Core Identity
+I am a senior software engineer specialising in code review.
+I review diffs thoroughly across three dimensions: security, correctness, and performance.
+
+## How I review
+- Security — OWASP Top 10: SQL injection, XSS, hardcoded secrets, missing auth
+- Correctness — logic bugs, off-by-one errors, unhandled nulls, race conditions
+- Performance — N+1 queries, blocking operations, missing indexes
+
+## Output format
+For each finding:
+**[CRITICAL|WARNING|INFO]** File:Line — What is wrong — How to fix it
+
+If a dimension has no findings, write "No issues found."
+Be specific. No vague feedback.`;
+
+const rulesMd = `- Never push or commit to the target repo
+- Never modify source files in the target repo
+- Never run destructive commands (rm -rf, git reset --hard)
+- Only clone, checkout, and diff — do not modify the target repo
+- Always review all three dimensions: security, correctness, performance
+- Always use the exact finding format specified
+- If the diff is empty, say so and stop`;
+
+const part1Push = `git add .
+git commit -m "init code review agent"
+git push`;
+
+const part2Setup = `mkdir pr-reviewer && cd pr-reviewer
+npm init -y
+npm install @open-gitagent/opengap`;
+
+const packageJson = `{
+ "type": "module",
+ "dependencies": {
+ "@open-gitagent/opengap": "^2.0.0"
+ }
+}`;
+
+const envVars = `export ANTHROPIC_API_KEY=your_anthropic_key
+export GITHUB_TOKEN=your_pat`;
+
+const reviewTs = `import { query } from "@open-gitagent/opengap";
+import { writeFileSync } from "fs";
+
+const prUrl = process.argv[2];
+
+if (!prUrl) {
+ console.error("Usage: npx tsx review.ts ");
+ process.exit(1);
+}
+
+let report = "";
+
+async function main() {
+ console.log(\`Reviewing PR: \${prUrl}\`);
+
+ for await (const msg of query({
+ repo: {
+ url: "https://github.com/your-org/code-review-agent",
+ token: process.env.GITHUB_TOKEN!,
+ dir: "/tmp/code-review-agent",
+ },
+ prompt: \`
+ Review this pull request: \${prUrl}
+
+ Steps:
+ 1. Use gh pr checkout to clone and checkout the PR branch
+ 2. Run git diff main...HEAD to get the diff
+ 3. Review the diff across three dimensions:
+
+ ## 1. Security (OWASP Top 10)
+ - SQL injection, XSS, command injection
+ - Hardcoded secrets or credentials
+ - Missing authentication/authorization checks
+
+ ## 2. Correctness
+ - Logic bugs and off-by-one errors
+ - Unhandled edge cases and null/undefined
+ - Missing error handling
+
+ ## 3. Performance
+ - N+1 queries
+ - Blocking operations on the main thread
+ - Missing indexes or caching opportunities
+
+ For each finding:
+ **[CRITICAL|WARNING|INFO]** File:Line — What is wrong — How to fix it
+
+ If a dimension has no findings, write "No issues found."
+ \`,
+ maxTurns: 15,
+ })) {
+ if (msg.type === "delta") process.stdout.write(msg.content);
+ if (msg.type === "assistant") report += msg.content;
+ if (msg.type === "system" && msg.subtype === "error")
+ console.log(\`\\n[ERROR] \${msg.content}\`);
+ }
+
+ writeFileSync("review-report.md", report);
+ console.log("\\n\\nReport saved to review-report.md");
+}
+
+main();`;
+
+const part4Run = `npx tsx review.ts https://github.com/org/repo/pull/123`;
+
+const steps = [
+ { step: "1", desc: "PR URL passed as command line argument" },
+ { step: "2", desc: "SDK clones code-review-agent from GitHub into /tmp/code-review-agent" },
+ { step: "3", desc: "Loads agent.yaml + SOUL.md + RULES.md" },
+ { step: "4", desc: "Agent uses cli tool to run gh pr checkout on the target PR" },
+ { step: "5", desc: "Agent runs git diff main...HEAD itself to get the diff" },
+ { step: "6", desc: "Agent reviews diff across all three dimensions" },
+ { step: "7", desc: "Findings stream to terminal in real time" },
+ { step: "8", desc: "Full report written to review-report.md" },
+];
+
+export function GitAgentCookbookCodeReview() {
+ return (
+
+
+
+ {/* Header */}
+
+ SDK / Cookbooks /
+ Code Review on a Pull Request
+
+ Pass any GitHub PR URL and the agent clones the repo, checks out the branch, runs the diff itself,
+ and produces a structured review across security, correctness, and performance — saved to{" "}
+ review-report.md.
+
+
+
+ {/* Part 1 */}
+
+ Part 1 — Build the Code Review Agent Repo
+
+ Create a new repo on GitHub called code-review-agent, clone it locally:
+
+
+
+
+ {/* Part 2 */}
+
+ Part 2 — SDK Project Setup
+
+
+
+
Update package.json:
+
+
+
+
Set environment variables:
+
+
+
+
+
+ {/* Part 3 */}
+
+ Part 3 — Create review.ts
+
+
+
+
+
+ {/* Part 4 */}
+
+ Part 4 — Run it
+
+
+
+
+
+ {/* Step by step */}
+
+ What happens step by step
+
+ {steps.map((s, i) => (
+
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentCookbookCustomTool.tsx b/src/components/gitAgent/GitAgentCookbookCustomTool.tsx
new file mode 100644
index 0000000..f6484e1
--- /dev/null
+++ b/src/components/gitAgent/GitAgentCookbookCustomTool.tsx
@@ -0,0 +1,301 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const part3Setup = `git clone https://github.com/your-username/custom-tool-agent
+cd custom-tool-agent`;
+
+const agentYaml = `spec_version: "0.4.0"
+name: custom-tool-agent
+version: 1.0.0
+description: An agent that looks up GitHub issues and sends Discord alerts
+
+model:
+ preferred: "anthropic:claude-sonnet-4-6"
+ fallback: []
+
+tools:
+ - read
+ - write
+
+runtime:
+ max_turns: 15
+ timeout: 120`;
+
+const soulMd = `# Soul
+
+## Core Identity
+I am a project management assistant. I look up GitHub issues and send Discord alerts when action is needed.
+
+## What I do
+- Look up GitHub issues by number
+- Check their status and assignee
+- Send Discord messages when issues need attention
+
+## Principles
+- Only send Discord messages when there is a clear reason
+- Be concise — one message covering all flagged issues
+- Never spam the channel`;
+
+const rulesMd = `- Never look up issues that were not explicitly mentioned
+- Always check assignee before sending an alert
+- Send only one Discord message per run
+- Never close or modify issues`;
+
+const part3Push = `git add .
+git commit -m "init custom tool agent"
+git push`;
+
+const part4Setup = `mkdir custom-tool-sdk && cd custom-tool-sdk
+npm init -y
+npm install @open-gitagent/opengap`;
+
+const packageJson = `{
+ "type": "module",
+ "dependencies": {
+ "@open-gitagent/opengap": "^2.0.0"
+ }
+}`;
+
+const envVars = `export ANTHROPIC_API_KEY=your_anthropic_key
+export GITHUB_TOKEN=your_pat
+export GITHUB_REPO=your-username/test-issues
+export DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/xxx/yyy`;
+
+const indexTs = `import { query, tool } from "@open-gitagent/opengap";
+
+const lookupGithubIssue = tool(
+ "lookup_github_issue",
+ "Look up a GitHub issue by number and return its title, state, and assignee",
+ {
+ properties: {
+ issue_number: { type: "number", description: "Issue number e.g. 1" },
+ },
+ required: ["issue_number"],
+ },
+ async ({ issue_number }: { issue_number: number }) => {
+ const res = await fetch(
+ \`https://api.github.com/repos/\${process.env.GITHUB_REPO}/issues/\${issue_number}\`,
+ {
+ headers: {
+ Authorization: \`Bearer \${process.env.GITHUB_TOKEN}\`,
+ Accept: "application/vnd.github+json",
+ },
+ }
+ );
+ const data = await res.json();
+ return JSON.stringify({
+ number: data.number,
+ title: data.title,
+ state: data.state,
+ assignee: data.assignee?.login ?? "Unassigned",
+ created_at: data.created_at,
+ body: data.body?.slice(0, 500) ?? "",
+ });
+ },
+);
+
+const sendDiscordMessage = tool(
+ "send_discord_message",
+ "Send a message to a Discord channel via webhook",
+ {
+ properties: {
+ message: { type: "string", description: "Message text to send" },
+ },
+ required: ["message"],
+ },
+ async ({ message }: { message: string }) => {
+ await fetch(process.env.DISCORD_WEBHOOK_URL!, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ content: message }),
+ });
+ return "Message sent to Discord";
+ },
+);
+
+async function main() {
+ for await (const msg of query({
+ repo: {
+ url: "https://github.com/your-username/custom-tool-agent",
+ token: process.env.GITHUB_TOKEN!,
+ dir: "/tmp/custom-tool-agent",
+ },
+ prompt: \`
+ Look up GitHub issues #1, #2, and #3
+ in the repo \${process.env.GITHUB_REPO}.
+ For each one check if it is open and unassigned.
+ If any are open and unassigned, draft a Discord message
+ flagging them and send it.
+ \`,
+ tools: [lookupGithubIssue, sendDiscordMessage],
+ maxTurns: 15,
+ })) {
+ if (msg.type === "delta") process.stdout.write(msg.content);
+ if (msg.type === "system" && msg.subtype === "error")
+ console.log(\`\\n[ERROR] \${msg.content}\`);
+ }
+}
+
+main();`;
+
+const part6Run = `npx tsx index.ts`;
+
+const issueRows = [
+ { number: "#1", title: "Fix login page not redirecting after auth", assignee: "none" },
+ { number: "#2", title: "API returns 500 on empty payload", assignee: "yourself" },
+ { number: "#3", title: "Update dependencies to latest version", assignee: "none" },
+];
+
+const steps = [
+ { step: "1", desc: "SDK clones custom-tool-agent from GitHub into /tmp/custom-tool-agent" },
+ { step: "2", desc: "Loads agent.yaml + SOUL.md + RULES.md" },
+ { step: "3", desc: "Agent calls lookup_github_issue three times — one per issue" },
+ { step: "4", desc: "Checks state and assignee for each" },
+ { step: "5", desc: "Issues #1 and #3 are open + unassigned → flagged" },
+ { step: "6", desc: "Issue #2 is assigned → skipped" },
+ { step: "7", desc: "Agent calls send_discord_message with a summary of flagged issues" },
+ { step: "8", desc: "Discord message appears in your #alerts channel" },
+];
+
+export function GitAgentCookbookCustomTool() {
+ return (
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentCookbookMultiAgentHandoff.tsx b/src/components/gitAgent/GitAgentCookbookMultiAgentHandoff.tsx
new file mode 100644
index 0000000..0f656db
--- /dev/null
+++ b/src/components/gitAgent/GitAgentCookbookMultiAgentHandoff.tsx
@@ -0,0 +1,385 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const part1Setup = `git clone https://github.com/your-username/buggy-code
+cd buggy-code && mkdir src`;
+
+const authJs = `async function loginUser(username, password) {
+ try {
+ const query = "SELECT * FROM users WHERE username = '" + username + "'";
+ const user = db.execute(query);
+ return user;
+ } catch (e) {
+ // silently swallowed
+ }
+}
+
+async function fetchUserData(userId) {
+ const data = fetch(\`/api/users/\${userId}\`); // promise not awaited
+ return data;
+}
+
+function updateProfile(req) {
+ const name = req.body.name; // no validation
+ db.execute("UPDATE users SET name = '" + name + "'");
+}`;
+
+const paymentsJs = `async function processPayment(amount, cardNumber) {
+ try {
+ const result = await chargeCard(cardNumber, amount);
+ return result;
+ } catch (err) {
+ // error swallowed
+ }
+}
+
+async function getTransaction(id) {
+ const tx = fetch(\`/api/transactions/\${id}\`); // not awaited
+ return tx;
+}
+
+function createOrder(req) {
+ const price = req.body.price; // no validation
+ db.execute("INSERT INTO orders VALUES ('" + price + "')");
+}`;
+
+const part1Push = `git add . && git commit -m "add buggy code" && git push`;
+
+const auditorSetup = `git clone https://github.com/your-username/auditor-agent
+cd auditor-agent`;
+
+const auditorYaml = `spec_version: "0.4.0"
+name: auditor-agent
+version: 1.0.0
+description: Scans codebases and produces structured findings
+
+model:
+ preferred: "anthropic:claude-sonnet-4-6"
+ fallback: []
+
+tools:
+ - cli
+ - read
+
+runtime:
+ max_turns: 20
+ timeout: 120`;
+
+const auditorSoul = `# Soul
+
+## Core Identity
+I am a code auditor. I scan codebases and produce structured findings.
+
+## What I look for
+- Empty catch blocks silently swallowing errors
+- Promises not awaited
+- User input used without validation
+- Database queries built with string concatenation
+
+## Output format
+For each finding output exactly:
+FILE:
+LINE:
+TYPE:
+CODE:
+WHY:
+---`;
+
+const auditorRules = `- Never modify any files
+- Always use the exact finding format — no variations
+- Scan every file in src/ without skipping
+- If no issues found in a file, move on silently`;
+
+const auditorPush = `git add . && git commit -m "init auditor agent" && git push`;
+
+const writerSetup = `git clone https://github.com/your-username/writer-agent
+cd writer-agent`;
+
+const writerYaml = `spec_version: "0.4.0"
+name: writer-agent
+version: 1.0.0
+description: Takes audit findings and writes a polished GitHub issue
+
+model:
+ preferred: "anthropic:claude-sonnet-4-6"
+ fallback: []
+
+tools:
+ - read
+ - write
+
+runtime:
+ max_turns: 5
+ timeout: 120`;
+
+const writerSoul = `# Soul
+
+## Core Identity
+I am a tech lead. I take raw audit findings and write clear, actionable GitHub issues.
+
+## What I produce
+- A clear specific title
+- A 2-3 sentence problem summary explaining production risk
+- A table: File | Line | Type | Risk Level (Critical/High/Medium)
+- A "How to fix" section with one concrete example per issue type
+- A checklist of acceptance criteria for the PR that fixes this
+
+## Style
+- GitHub Flavored Markdown throughout
+- Concise and direct — no filler text
+- Risk levels: SQL injection = Critical, unvalidated input = High, unhandled errors = High, unawaited promises = Medium`;
+
+const writerRules = `- Never add findings that were not in the input
+- Always include all four sections
+- Always use GitHub Flavored Markdown
+- Never modify source files`;
+
+const writerPush = `git add . && git commit -m "init writer agent" && git push`;
+
+const part4Setup = `mkdir multi-agent-sdk && cd multi-agent-sdk
+npm init -y
+npm install @open-gitagent/opengap`;
+
+const packageJson = `{
+ "type": "module",
+ "dependencies": {
+ "@open-gitagent/opengap": "^2.0.0"
+ }
+}`;
+
+const envVars = `export ANTHROPIC_API_KEY=your_anthropic_key
+export GITHUB_TOKEN=your_pat`;
+
+const indexTs = `import { query } from "@open-gitagent/opengap";
+
+async function main() {
+ console.log("Agent 1 — Scanning codebase for issues...\\n");
+
+ let findings = "";
+ let fileCount = 0;
+
+ for await (const msg of query({
+ repo: {
+ url: "https://github.com/your-username/auditor-agent",
+ token: process.env.GITHUB_TOKEN!,
+ dir: "/tmp/auditor-agent",
+ },
+ prompt: \`
+ Clone https://github.com/your-username/buggy-code
+ into /tmp/buggy-code using the cli tool.
+
+ Then scan every file in /tmp/buggy-code/src/ and find:
+ 1. Errors silently swallowed in empty catch blocks
+ 2. Promises not awaited
+ 3. User input used without validation
+ 4. Database queries built with string concatenation
+
+ For each finding output exactly:
+ FILE:
+ LINE:
+ TYPE:
+ CODE:
+ WHY:
+ ---
+ \`,
+ maxTurns: 20,
+ })) {
+ if (msg.type === "delta") process.stdout.write(msg.content);
+ if (msg.type === "assistant") findings = msg.content;
+ if (msg.type === "tool_use" && msg.toolName === "read") fileCount++;
+ if (msg.type === "system" && msg.subtype === "error")
+ console.log(\`\\n[ERROR] \${msg.content}\`);
+ }
+
+ console.log(\`\\n\\nAgent 1 done. Scanned \${fileCount} files.\`);
+ console.log("\\nAgent 2 — Writing GitHub issue...\\n");
+
+ for await (const msg of query({
+ repo: {
+ url: "https://github.com/your-username/writer-agent",
+ token: process.env.GITHUB_TOKEN!,
+ dir: "/tmp/writer-agent",
+ },
+ prompt: \`
+ Write a well-structured GitHub issue based on these audit findings:
+
+ - A clear, specific title
+ - A 2-3 sentence problem summary explaining the risk to production
+ - A table with columns: File | Line | Type | Risk Level (Critical/High/Medium)
+ - A "How to fix" section with one concrete example fix for each type
+ - A checklist of acceptance criteria for the PR that fixes this
+
+ Use GitHub Flavored Markdown throughout.
+
+ Findings:
+ \${findings}
+ \`,
+ maxTurns: 5,
+ constraints: { temperature: 0.2 },
+ })) {
+ if (msg.type === "delta") process.stdout.write(msg.content);
+ if (msg.type === "system" && msg.subtype === "error")
+ console.log(\`\\n[ERROR] \${msg.content}\`);
+ }
+}
+
+main();`;
+
+const part6Run = `npx tsx index.ts`;
+
+const steps = [
+ { step: "1", desc: "SDK clones auditor-agent into /tmp/auditor-agent" },
+ { step: "2", desc: "Agent 1 uses cli to clone buggy-code into /tmp/buggy-code" },
+ { step: "3", desc: "Agent 1 scans every file in src/ using read tool" },
+ { step: "4", desc: "Agent 1 outputs structured findings — one block per issue" },
+ { step: "5", desc: "findings captures Agent 1's full output" },
+ { step: "6", desc: "SDK clones writer-agent into /tmp/writer-agent" },
+ { step: "7", desc: "Agent 2 receives the findings and writes a polished GitHub issue" },
+ { step: "8", desc: "GitHub issue markdown streams to terminal — ready to paste" },
+];
+
+export function GitAgentCookbookMultiAgentHandoff() {
+ return (
+
+
+
+ {/* Header */}
+
+ SDK / Cookbooks /
+ Multi-Agent Handoff
+
+ Runs two specialized agents back to back. Agent 1 is a code auditor — it clones a repo, scans
+ every file in src/, and produces structured findings.
+ Agent 2 is a tech lead writer — it takes those findings and produces a polished GitHub issue with
+ a risk table, fix guide, and acceptance criteria. Each agent stays focused on one job.
+
+
+
+ {/* Part 1 */}
+
+ Part 1 — Create a Target Repo with Code Issues
+
+ Create a repo called buggy-code on GitHub, clone it locally:
+
+
+
+
+
Create src/auth.js:
+
+
+
+
Create src/payments.js:
+
+
+
+
+
+
+ {/* Part 2 */}
+
+ Part 2 — Build the Auditor Agent Repo
+
+ Create a repo called auditor-agent on GitHub, clone it locally:
+
+
+
+
+ {/* Part 3 */}
+
+ Part 3 — Build the Writer Agent Repo
+
+ Create a repo called writer-agent on GitHub, clone it locally:
+
+
+
+
+ {/* Part 4 */}
+
+ Part 4 — SDK Project Setup
+
+
+
+
Update package.json:
+
+
+
+
Set environment variables:
+
+
+
+
+
+ {/* Part 5 */}
+
+ Part 5 — Create index.ts
+
+
+
+
+
+ {/* Part 6 */}
+
+ Part 6 — Run it
+
+
+
+
+
+ {/* Step by step */}
+
+ What happens step by step
+
+ {steps.map((s, i) => (
+
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentCookbookRefactorRepo.tsx b/src/components/gitAgent/GitAgentCookbookRefactorRepo.tsx
new file mode 100644
index 0000000..4eb2723
--- /dev/null
+++ b/src/components/gitAgent/GitAgentCookbookRefactorRepo.tsx
@@ -0,0 +1,227 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const part1Setup = `git clone https://github.com/your-org/refactor-agent
+cd refactor-agent`;
+
+const agentYaml = `spec_version: "0.4.0"
+name: refactor-agent
+version: 1.0.0
+description: An agent that refactors codebases without changing behavior
+
+model:
+ preferred: "anthropic:claude-sonnet-4-6"
+ fallback: []
+
+tools:
+ - cli
+ - read
+ - write
+
+runtime:
+ max_turns: 50
+ timeout: 120`;
+
+const soulMd = `# Soul
+
+## Core Identity
+I am an expert refactoring agent. I improve code quality without changing behavior.
+
+## What I do
+- Replace callbacks with async/await
+- Add input validation on public functions
+- Extract magic strings to named constants
+- Remove dead code and unused imports
+- Add JSDoc comments to exported functions
+
+## Approach
+- Work through files one at a time
+- After each file, confirm what changed
+- Never change what the code does, only how it looks`;
+
+const rulesMd = `- Never modify test files
+- Never run destructive commands
+- Never delete code without understanding it first
+- Never commit secrets or API keys
+- Always confirm changes before moving to next file`;
+
+const part1Push = `git add .
+git commit -m "init refactor agent"
+git push`;
+
+const part2Setup = `mkdir refactor-sdk && cd refactor-sdk
+npm init -y
+npm install @open-gitagent/opengap`;
+
+const packageJson = `{
+ "type": "module",
+ "dependencies": {
+ "@open-gitagent/opengap": "^2.0.0"
+ }
+}`;
+
+const indexTs = `import { query } from "@open-gitagent/opengap";
+
+async function main() {
+ const result = query({
+ repo: {
+ url: "https://github.com/your-org/refactor-agent",
+ token: process.env.GITHUB_TOKEN!,
+ dir: "/tmp/refactor-agent",
+ },
+ prompt: \`
+ Clone https://github.com/your-org/target-repo using the cli tool
+ into /tmp/target-repo.
+
+ Then refactor the entire codebase:
+ 1. Replace all callback-style async code with async/await
+ 2. Add input validation on every public-facing function
+ 3. Extract all magic strings and numbers into named constants
+ 4. Remove any dead code or unused imports
+ 5. Add JSDoc comments to every exported function
+
+ Work through files one at a time.
+ After each file, confirm what changed.
+ Do not modify test files.
+ \`,
+ maxTurns: 50,
+ hooks: {
+ preToolUse: async (ctx) => {
+ if (ctx.toolName === "cli" && ctx.args.command?.includes("rm -rf"))
+ return { action: "block", reason: "Destructive commands blocked" };
+ return { action: "allow" };
+ },
+ fileChanged: async (ctx) => {
+ console.log(\`Modified: \${ctx.path}\`);
+ },
+ },
+ });
+
+ const changedFiles: string[] = [];
+
+ for await (const msg of result) {
+ if (msg.type === "delta") process.stdout.write(msg.content);
+ if (msg.type === "system" && msg.subtype === "error")
+ console.log(\`\\n[ERROR] \${msg.content}\`);
+ if (msg.type === "tool_use" && msg.toolName === "write")
+ changedFiles.push(msg.args.path);
+ if (msg.type === "system" && msg.subtype === "session_end") {
+ console.log(\`\\n\\nDone. \${changedFiles.length} files modified:\`);
+ changedFiles.forEach(f => console.log(\` - \${f}\`));
+ }
+ }
+
+ const costs = result.costs();
+ console.log(\`\\nTotal cost: $\${costs.totalCostUsd.toFixed(4)}\`);
+}
+
+main();`;
+
+const part4Run = `export ANTHROPIC_API_KEY=your_key
+export GITHUB_TOKEN=your_pat
+npx tsx index.ts`;
+
+const steps = [
+ { step: "1", desc: "SDK clones refactor-agent into /tmp/refactor-agent" },
+ { step: "2", desc: "Loads agent.yaml + SOUL.md + RULES.md from it" },
+ { step: "3", desc: "Agent uses cli tool to clone the target repo" },
+ { step: "4", desc: "Agent reads files one by one using read tool" },
+ { step: "5", desc: "Agent rewrites each file using write tool" },
+ { step: "6", desc: "fileChanged hook logs every modified file" },
+ { step: "7", desc: "Session ends — prints file list and total cost" },
+];
+
+export function GitAgentCookbookRefactorRepo() {
+ return (
+
+
+
+ {/* Header */}
+
+ SDK / Cookbooks /
+ Refactor a Repo
+
+ You build a dedicated refactoring agent and store it on GitHub. The SDK pulls that agent
+ and runs it against any target codebase you specify in the prompt.
+
+
+
+ {/* Part 1 */}
+
+ Part 1 — Build the Refactoring Agent Repo
+
+ Create a new repo on GitHub called refactor-agent, clone it locally:
+
+
+
+
+ {/* Part 2 */}
+
+ Part 2 — SDK Project Setup
+ Create a new folder and install dependencies:
+
+
+
+
Update package.json:
+
+
+
+
+
+ {/* Part 3 */}
+
+ Part 3 — Write index.ts
+
+
+
+
+
+ {/* Part 4 */}
+
+ Part 4 — Run it
+
+
+
+
+
+ {/* Step by step */}
+
+ What happens step by step
+
+ {steps.map((s, i) => (
+
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentCookbookScheduledCron.tsx b/src/components/gitAgent/GitAgentCookbookScheduledCron.tsx
new file mode 100644
index 0000000..625f225
--- /dev/null
+++ b/src/components/gitAgent/GitAgentCookbookScheduledCron.tsx
@@ -0,0 +1,290 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const part1Setup = `git clone https://github.com/your-username/standup-target
+cd standup-target`;
+
+const part1Commits = `echo "# App" > README.md
+git add . && git commit -m "feat: initial project setup"
+
+mkdir src
+echo "export const login = () => {}" > src/auth.js
+git add . && git commit -m "feat: add login handler"
+
+echo "export const pay = () => {}" > src/payments.js
+git add . && git commit -m "fix: payment null check"`;
+
+const part1Push = `git push`;
+
+const part2Setup = `git clone https://github.com/your-username/standup-agent
+cd standup-agent`;
+
+const agentYaml = `spec_version: "0.4.0"
+name: standup-agent
+version: 1.0.0
+description: Writes a daily standup digest from git activity
+
+model:
+ preferred: "anthropic:claude-sonnet-4-6"
+ fallback: []
+
+tools:
+ - cli
+ - read
+
+runtime:
+ max_turns: 10
+ timeout: 120`;
+
+const soulMd = `# Soul
+
+## Core Identity
+I am a standup digest writer. I look at what changed in a repo over the last 24 hours and summarize it for an engineering team.
+
+## What I produce
+A short Discord message with three sections:
+- **Yesterday** — commits that shipped
+- **Blocked** — PRs with no review activity in over 2 days
+- **Today** — PRs that are approved and ready to merge
+
+## Style
+- Discord markdown: **bold**, bullet points
+- Under 20 lines total
+- No preamble — just the message
+- If nothing to report in a section, write "Nothing to report"`;
+
+const rulesMd = `- Never modify any files in the target repo
+- Always run git log with --since='24 hours ago' — do not change the time window
+- Always run gh pr list to check PR status
+- Output only the Discord message — no commentary before or after
+- Use Discord markdown, not GitHub markdown`;
+
+const part2Push = `git add . && git commit -m "init standup agent" && git push`;
+
+const part4Setup = `mkdir standup-sdk && cd standup-sdk
+npm init -y
+npm install @open-gitagent/opengap node-cron
+npm install -D tsx`;
+
+const packageJson = `{
+ "type": "module",
+ "dependencies": {
+ "@open-gitagent/opengap": "^2.0.0",
+ "node-cron": "^4.2.1"
+ }
+}`;
+
+const envVars = `export ANTHROPIC_API_KEY=your_anthropic_key
+export GITHUB_TOKEN=your_pat
+export DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...`;
+
+const indexTs = `import { query } from "@open-gitagent/opengap";
+import cron from "node-cron";
+
+async function runStandup() {
+ console.log(\`[\${new Date().toISOString()}] Running standup agent...\`);
+
+ let summary = "";
+
+ for await (const msg of query({
+ repo: {
+ url: "https://github.com/your-username/standup-agent",
+ token: process.env.GITHUB_TOKEN!,
+ dir: "/tmp/standup-agent",
+ },
+ prompt: \`
+ Write a standup digest for the engineering team.
+
+ Steps:
+ 1. Clone https://github.com/your-username/standup-target into /tmp/standup-target
+ using cli: git clone https://github.com/your-username/standup-target /tmp/standup-target
+ If the directory already exists, run: git -C /tmp/standup-target pull
+ 2. Run: git -C /tmp/standup-target log --oneline --since='24 hours ago'
+ 3. Run: gh pr list --repo your-username/standup-target --json title,author,reviewDecision,updatedAt
+
+ Then write a Discord message with these three sections:
+ **Yesterday** — commits that shipped in the last 24 hours
+ **Blocked** — PRs with no review activity in over 2 days
+ **Today** — PRs that are approved and ready to merge
+
+ Keep it under 20 lines. Use Discord markdown (**bold**, bullet points).
+ Output only the Discord message — no preamble.
+ \`,
+ maxTurns: 10,
+ })) {
+ if (msg.type === "delta") process.stdout.write(msg.content);
+ if (msg.type === "assistant") summary = msg.content;
+ if (msg.type === "system" && msg.subtype === "error")
+ console.log(\`\\n[ERROR] \${msg.content}\`);
+ }
+
+ if (!summary) {
+ console.error("\\nAgent returned no output, skipping Discord post");
+ return;
+ }
+
+ const res = await fetch(process.env.DISCORD_WEBHOOK_URL!, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ content: summary }),
+ });
+
+ if (res.ok) {
+ console.log("\\nStandup posted to Discord");
+ } else {
+ console.error(\`\\nDiscord post failed: \${res.status}\`);
+ }
+}
+
+// Run once immediately for testing
+runStandup();
+
+// Then schedule for weekdays at 9 AM
+cron.schedule("0 9 * * 1-5", runStandup);
+console.log("Standup agent running. Scheduled for 9 AM weekdays.");`;
+
+const part6Run = `npx tsx index.ts`;
+
+const steps = [
+ { step: "1", desc: "SDK clones standup-agent into /tmp/standup-agent" },
+ { step: "2", desc: "Agent uses cli to clone standup-target into /tmp/standup-target" },
+ { step: "3", desc: "Agent runs git log --since='24 hours ago' on the target repo" },
+ { step: "4", desc: "Agent runs gh pr list to get open PR status" },
+ { step: "5", desc: "Agent writes the Discord message with Yesterday / Blocked / Today sections" },
+ { step: "6", desc: "SDK posts the message to Discord via webhook" },
+ { step: "7", desc: "Process stays alive and fires again every weekday at 9 AM" },
+];
+
+export function GitAgentCookbookScheduledCron() {
+ return (
+
+
+
+ {/* Header */}
+
+ SDK / Cookbooks /
+ Scheduled Cron Task
+
+ Runs a standup agent every weekday at 9 AM. It clones your target repo, checks commits from the
+ last 24 hours and open PRs, writes a digest, and posts it to a Discord channel automatically.
+ The SDK handles scheduling via node-cron — no external infrastructure needed.
+
+
+
+ {/* Part 1 */}
+
+ Part 1 — Create a Target Repo with Git History
+
+ Create a repo called standup-target on GitHub, clone it locally:
+
+
+
+
+
+ Create some files and commits so the agent has history to report:
+
+
+
+
+
+
+
+ {/* Part 2 */}
+
+ Part 2 — Build the Standup Agent Repo
+
+ Create a repo called standup-agent on GitHub, clone it locally:
+
+
+
+
+ {/* Part 3 */}
+
+ Part 3 — Discord Webhook Setup
+
+ {[
+ "Open your Discord server → right-click a channel → Edit Channel",
+ "Go to Integrations → Webhooks → New Webhook",
+ "Name it Standup Bot, click Copy Webhook URL",
+ "Save it — you'll use it as DISCORD_WEBHOOK_URL",
+ ].map((item, i) => (
+
+ —
+ {item}
+
+ ))}
+
+
+
+ {/* Part 4 */}
+
+ Part 4 — SDK Project Setup
+
+
+
+
Update package.json:
+
+
+
+
Set environment variables:
+
+
+
+
+
+ {/* Part 5 */}
+
+ Part 5 — Create index.ts
+
+
+
+
+
+ {/* Part 6 */}
+
+ Part 6 — Run it
+
+
+
+
+
+ {/* Step by step */}
+
+ What happens step by step
+
+ {steps.map((s, i) => (
+
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentCookbookSummarizeEmails.tsx b/src/components/gitAgent/GitAgentCookbookSummarizeEmails.tsx
new file mode 100644
index 0000000..36a4140
--- /dev/null
+++ b/src/components/gitAgent/GitAgentCookbookSummarizeEmails.tsx
@@ -0,0 +1,317 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const part1Setup = `git clone https://github.com/your-org/email-agent
+cd email-agent`;
+
+const agentYaml = `spec_version: "0.4.0"
+name: email-agent
+version: 1.0.0
+description: An agent that summarizes unread emails by urgency
+
+model:
+ preferred: "anthropic:claude-sonnet-4-6"
+ fallback: []
+
+tools:
+ - read
+ - write
+
+runtime:
+ max_turns: 15
+ timeout: 120`;
+
+const soulMd = `# Soul
+
+## Core Identity
+I am an email assistant. I read emails and produce clear, prioritized summaries.
+
+## What I do
+- Fetch unread emails using available tools
+- Group them by urgency: action needed today, can wait, FYI only
+- For urgent emails, extract the specific action required
+- Keep summaries scannable — bullet points, no long paragraphs
+
+## Principles
+- Never send, delete, or modify emails
+- Only read`;
+
+const rulesMd = `- Never send emails
+- Never delete emails
+- Never mark emails as read unless explicitly asked
+- Only use the tools provided`;
+
+const part1Push = `git add .
+git commit -m "init email agent"
+git push`;
+
+const getTokenScript = `import { google } from "googleapis";
+import * as readline from "readline";
+
+const auth = new google.auth.OAuth2(
+ "YOUR_CLIENT_ID",
+ "YOUR_CLIENT_SECRET",
+ "urn:ietf:wg:oauth:2.0:oob"
+);
+
+const url = auth.generateAuthUrl({
+ access_type: "offline",
+ scope: ["https://www.googleapis.com/auth/gmail.readonly"],
+});
+
+console.log("Open this URL in your browser:\\n", url);
+
+const rl = readline.createInterface({ input: process.stdin });
+rl.question("\\nPaste the code here: ", async (code) => {
+ const { tokens } = await auth.getToken(code);
+ console.log("\\nYour refresh token:", tokens.refresh_token);
+ rl.close();
+});`;
+
+const runGetToken = `node get-token.mjs`;
+
+const part3Setup = `mkdir email-summarizer && cd email-summarizer
+npm init -y
+npm install @open-gitagent/opengap googleapis`;
+
+const packageJson = `{
+ "type": "module",
+ "dependencies": {
+ "@open-gitagent/opengap": "^2.0.0",
+ "googleapis": "latest"
+ }
+}`;
+
+const envVars = `export ANTHROPIC_API_KEY=your_anthropic_key
+export GITHUB_TOKEN=your_pat
+export GMAIL_CLIENT_ID=your_client_id
+export GMAIL_CLIENT_SECRET=your_client_secret
+export GMAIL_REFRESH_TOKEN=your_refresh_token`;
+
+const indexTs = `import { query, tool } from "@open-gitagent/opengap";
+import { google } from "googleapis";
+
+const auth = new google.auth.OAuth2(
+ process.env.GMAIL_CLIENT_ID,
+ process.env.GMAIL_CLIENT_SECRET,
+);
+auth.setCredentials({ refresh_token: process.env.GMAIL_REFRESH_TOKEN });
+const gmail = google.gmail({ version: "v1", auth });
+
+const listEmails = tool(
+ "list_unread_emails",
+ "List unread emails with subject, sender, date and snippet",
+ {
+ properties: {
+ limit: { type: "number", description: "Number of emails to fetch (default 20)" },
+ from: { type: "string", description: "Filter by sender domain e.g. @company.com" },
+ },
+ required: [],
+ },
+ async ({ limit = 20, from = "" }) => {
+ const q = \`is:unread\${from ? \` from:\${from}\` : ""}\`;
+ const list = await gmail.users.messages.list({ userId: "me", q, maxResults: limit });
+ const messages = await Promise.all(
+ (list.data.messages ?? []).map(async (m) => {
+ const msg = await gmail.users.messages.get({
+ userId: "me", id: m.id!, format: "metadata",
+ metadataHeaders: ["From", "Subject", "Date"],
+ });
+ const headers = msg.data.payload?.headers ?? [];
+ return {
+ id: m.id,
+ from: headers.find(h => h.name === "From")?.value,
+ subject: headers.find(h => h.name === "Subject")?.value,
+ date: headers.find(h => h.name === "Date")?.value,
+ snippet: msg.data.snippet,
+ };
+ })
+ );
+ return JSON.stringify(messages);
+ },
+);
+
+const getEmailBody = tool(
+ "get_email_body",
+ "Get the full body of a specific email by ID",
+ {
+ properties: {
+ id: { type: "string", description: "Email message ID" },
+ },
+ required: ["id"],
+ },
+ async ({ id }: { id: string }) => {
+ const msg = await gmail.users.messages.get({ userId: "me", id, format: "full" });
+ const body = msg.data.payload?.parts?.[0]?.body?.data ?? msg.data.payload?.body?.data ?? "";
+ return Buffer.from(body, "base64").toString("utf-8").slice(0, 5000);
+ },
+);
+
+async function main() {
+ for await (const msg of query({
+ repo: {
+ url: "https://github.com/your-org/email-agent",
+ token: process.env.GITHUB_TOKEN!,
+ dir: "/tmp/email-agent",
+ },
+ prompt: \`
+ Fetch my last 20 unread emails.
+ Group them into three buckets:
+ - Needs action today
+ - Can wait until tomorrow
+ - FYI only / no action needed
+
+ For each email in "Needs action today", read the full body
+ and extract the specific action required.
+ Keep the summary scannable — bullet points, no long paragraphs.
+ \`,
+ tools: [listEmails, getEmailBody],
+ maxTurns: 15,
+ })) {
+ if (msg.type === "delta") process.stdout.write(msg.content);
+ if (msg.type === "system" && msg.subtype === "error")
+ console.log(\`\\n[ERROR] \${msg.content}\`);
+ }
+}
+
+main();`;
+
+const part5Run = `npx tsx index.ts`;
+
+const steps = [
+ { step: "1", desc: "SDK clones email-agent from GitHub into /tmp/email-agent" },
+ { step: "2", desc: "Loads agent.yaml + SOUL.md + RULES.md" },
+ { step: "3", desc: "Agent calls list_unread_emails — fetches 20 emails with subject, sender, snippet" },
+ { step: "4", desc: "For urgent emails, calls get_email_body to read full content" },
+ { step: "5", desc: "Produces grouped summary printed to terminal" },
+];
+
+export function GitAgentCookbookSummarizeEmails() {
+ return (
+
+
+
+ {/* Header */}
+
+ SDK / Cookbooks /
+ Summarize Unread Emails
+
+ Connects to Gmail via two custom tools, fetches your unread emails, and produces a prioritized
+ summary grouped by urgency — with specific action items called out for emails that need attention today.
+
+
+
+ {/* Part 1 */}
+
+ Part 1 — Build the Email Agent Repo
+
+ Create a new repo on GitHub called email-agent, clone it locally:
+
+
+
+
+ {/* Part 2 */}
+
+ Part 2 — Get Gmail API Credentials
+
+ {[
+ { step: "Step 1 — Create a Google Cloud project", items: ["Go to console.cloud.google.com", "Click New Project → name it email-agent → Create"] },
+ { step: "Step 2 — Enable Gmail API", items: ["Go to APIs & Services → Library", "Search Gmail API → Enable"] },
+ { step: "Step 3 — Create OAuth2 credentials", items: ["Go to APIs & Services → Credentials", "Click Create Credentials → OAuth client ID", "Configure consent screen: User type: External, Add your Gmail as a test user", "Application type: Desktop app → Create", "Copy Client ID and Client Secret"] },
+ ].map((s, i) => (
+
+
{s.step}
+
+ {s.items.map((item, j) => (
+
+ —
+ {item}
+
+ ))}
+
+
+ ))}
+
+
+
Step 4 — Get refresh token
+
+ Create get-token.mjs:
+
+
+
+
+
+
Copy the refresh token it prints.
+
+
+
+
+ {/* Part 3 */}
+
+ Part 3 — SDK Project Setup
+
+
+
+
Update package.json:
+
+
+
+
Set environment variables:
+
+
+
+
+
+ {/* Part 4 */}
+
+ Part 4 — Create index.ts
+
+
+
+
+
+ {/* Part 5 */}
+
+ Part 5 — Run it
+
+
+
+
+
+ {/* Step by step */}
+
+ What happens step by step
+
+ {steps.map((s, i) => (
+
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentEnvVars.tsx b/src/components/gitAgent/GitAgentEnvVars.tsx
new file mode 100644
index 0000000..3031c23
--- /dev/null
+++ b/src/components/gitAgent/GitAgentEnvVars.tsx
@@ -0,0 +1,104 @@
+import { useState } from "react";
+import { motion } from "framer-motion";
+
+const envVars = [
+ { name: "ANTHROPIC_API_KEY", default: "—", desc: "API key for Anthropic Claude models" },
+ { name: "OPENAI_API_KEY", default: "—", desc: "API key for OpenAI models (also used for Lyzr)" },
+ { name: "GOOGLE_API_KEY", default: "—", desc: "API key for the google: text model provider" },
+ { name: "GEMINI_API_KEY", default: "—", desc: "API key for Gemini Live voice mode" },
+ { name: "GROQ_API_KEY", default: "—", desc: "API key for Groq" },
+ { name: "XAI_API_KEY", default: "—", desc: "API key for xAI Grok models" },
+ { name: "MISTRAL_API_KEY", default: "—", desc: "API key for Mistral models" },
+ { name: "OPENROUTER_API_KEY", default: "—", desc: "API key for OpenRouter (multi-provider proxy)" },
+ { name: "CEREBRAS_API_KEY", default: "—", desc: "API key for Cerebras" },
+ { name: "DEEPSEEK_API_KEY", default: "—", desc: "API key for DeepSeek" },
+ { name: "LYZR_API_KEY", default: "—", desc: "Lyzr AI Studio API key" },
+ { name: "GITHUB_TOKEN", default: "—", desc: "GitHub PAT for --repo mode (also falls back to GIT_TOKEN)" },
+ { name: "GIT_TOKEN", default: "—", desc: "Alternative PAT fallback for --repo mode" },
+ { name: "E2B_API_KEY", default: "—", desc: "API token read by the E2B SDK when using --sandbox" },
+ { name: "COMPOSIO_API_KEY", default: "—", desc: "Composio integrations API key" },
+ { name: "TELEGRAM_BOT_TOKEN", default: "—", desc: "Telegram bot token from @BotFather" },
+ { name: "GITAGENT_MODEL_BASE_URL", default: "—", desc: "Custom OpenAI-compatible base URL" },
+ { name: "GITAGENT_PASSWORD", default: "—", desc: "Password for web UI authentication" },
+];
+
+export function GitAgentEnvVars() {
+ const [filter, setFilter] = useState("");
+
+ const filtered = envVars.filter(
+ (v) =>
+ v.name.toLowerCase().includes(filter.toLowerCase()) ||
+ v.desc.toLowerCase().includes(filter.toLowerCase())
+ );
+
+ return (
+
+
+
+ Environment Variables
+
+ All supported environment variables and their defaults.
+
+
+
+ {/* Search input */}
+
+ setFilter(e.target.value)}
+ className="border border-border rounded px-3 py-2 text-sm font-body w-full bg-background text-foreground placeholder:text-muted-foreground/50 outline-none focus:border-primary/50 transition-colors"
+ />
+
+
+ {/* Env var list */}
+
+ {filtered.length === 0 && (
+
+ No environment variables match your search.
+
+ )}
+ {filtered.map((v, i) => (
+
+
+
+ {v.name}
+
+
+ {v.default}
+
+
+ {v.desc}
+
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentFeaturesSection.tsx b/src/components/gitAgent/GitAgentFeaturesSection.tsx
new file mode 100644
index 0000000..2d5e4ac
--- /dev/null
+++ b/src/components/gitAgent/GitAgentFeaturesSection.tsx
@@ -0,0 +1,96 @@
+import { motion } from "framer-motion";
+import { Mic, Monitor, Cpu, Brain, Zap, Puzzle, GitBranch, ShieldCheck, Activity } from "lucide-react";
+
+const features = [
+ {
+ icon: ShieldCheck,
+ title: "Compliance & Audit",
+ desc: "Built-in risk tiers, human-in-the-loop enforcement, and regulatory support for SOX, GLBA, SOC2, and GDPR. Your compliance team reviews the config like any code PR — no black-box policies.",
+ },
+ {
+ icon: GitBranch,
+ title: "SkillFlows",
+ desc: "Chain tasks into reliable, repeatable workflows in plain YAML. Approval gates pause execution and wait for sign-off via Telegram or WhatsApp — so humans stay in control of critical steps.",
+ },
+ {
+ icon: Brain,
+ title: "Git-Native Memory",
+ desc: "The agent remembers context across sessions — and every memory write is a git commit you can audit, diff, or revert. No opaque data stores, no vendor lock-in on your agent's history.",
+ },
+ {
+ icon: Zap,
+ title: "Skills & Auto-Learning",
+ desc: "Completed tasks are crystallized into reusable skills automatically. The agent builds its own capability library over time — every skill is version-controlled and inspectable.",
+ },
+ {
+ icon: Mic,
+ title: "Voice & Camera",
+ desc: "Real-time bidirectional voice via OpenAI Realtime API or Gemini Live. Camera input lets your agent see what you see — meet your team on voice, not just text.",
+ },
+ {
+ icon: Cpu,
+ title: "12+ LLM Providers",
+ desc: "Switch between Anthropic, OpenAI, Google, Groq, Mistral, and more by changing one line. Prevents model vendor lock-in — your agent definition stays yours regardless of who runs the models.",
+ },
+ {
+ icon: Monitor,
+ title: "Web UI",
+ desc: "Full browser interface at localhost:3333 with tabs for Chat, Skills, Integrations, Communication, SkillFlows, Scheduler, and Settings. No CLI required for day-to-day use.",
+ },
+ {
+ icon: Puzzle,
+ title: "Plugin System",
+ desc: "Extend with tools, hooks, skills, and memory layers via plugin.yaml. Install from git URLs or local paths — composable by design, auditable by default.",
+ },
+ {
+ icon: Activity,
+ title: "OpenTelemetry",
+ desc: "Built-in observability — spans, tool execution traces, and session cost in USD. Point it at your existing OTEL collector and it works; leave it unset for zero overhead.",
+ },
+];
+
+export function GitAgentFeaturesSection() {
+ return (
+
+
+
+
+ 05 — Features
+
+
+ Features
+
+
+ Production-ready out of the box — auditable for compliance, composable for engineering.
+
+
+
+
+ {features.map((f, i) => (
+
+
+
+ {f.title}
+
+
+ {f.desc}
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentHero.tsx b/src/components/gitAgent/GitAgentHero.tsx
new file mode 100644
index 0000000..f97b51e
--- /dev/null
+++ b/src/components/gitAgent/GitAgentHero.tsx
@@ -0,0 +1,49 @@
+import { motion } from "framer-motion";
+
+export function GitAgentHero() {
+ return (
+
+
+
+
+ Documentation
+
+
+ GitAgent
+
+
+ A universal git-native multimodal always-learning AI agent. Your agent IS a git repo.
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentHeroSection.tsx b/src/components/gitAgent/GitAgentHeroSection.tsx
new file mode 100644
index 0000000..3211a20
--- /dev/null
+++ b/src/components/gitAgent/GitAgentHeroSection.tsx
@@ -0,0 +1,276 @@
+import { useState } from "react";
+import { motion } from "framer-motion";
+import { Copy, Check, BookOpen, Github, ArrowRight, Folder, FileText, Brain, Settings, Zap, Wrench, Database, GitFork, ShieldCheck } from "lucide-react";
+import { track } from "@/lib/analytics";
+
+const INSTALL_CMD = `npm install -g @open-gitagent/opengap`;
+
+type TreeItem = { name: string; indent: number; icon: React.ReactNode; tag?: string };
+
+const tree: TreeItem[] = [
+ { name: "~/assistant/", indent: 0, icon: },
+ { name: "agent.yaml", indent: 1, icon: , tag: "manifest" },
+ { name: "SOUL.md", indent: 1, icon: , tag: "identity" },
+ { name: "RULES.md", indent: 1, icon: },
+ { name: "memory/", indent: 1, icon: },
+ { name: "MEMORY.md", indent: 2, icon: },
+ { name: "skills/", indent: 1, icon: },
+ { name: "tools/", indent: 1, icon: },
+ { name: "hooks/", indent: 1, icon: },
+ { name: "workflows/", indent: 1, icon: },
+ { name: "compliance/", indent: 1, icon: },
+];
+
+export function GitAgentHeroSection() {
+ const [copied, setCopied] = useState(false);
+
+ const handleCopy = () => {
+ navigator.clipboard.writeText(INSTALL_CMD);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ track("gitagent_install_copied");
+ };
+
+ return (
+
+
+
+
+
+ {/* Left */}
+
+ {/* OpenGAP badge */}
+
+
+ OpenGAP
+ —
+ compliant
+ ↗
+
+
+
+ Git Agent
+
+
+
+ A git-native agent harness — identity, memory, rules, and skills live as plain files in a repo.
+
+
+ Full audit trail, human-in-the-loop controls, and zero vendor lock-in. Your agent config is code — version-controlled, reviewable, and reversible.
+
+
+ {/* Meta badges */}
+
+ {[
+ { label: "node ≥ 20", dot: "bg-green-500" },
+ { label: "Agent Harness", dot: null },
+ { label: "MIT License", dot: null },
+ { label: "TypeScript 5.7", dot: null },
+ { label: "v1.5.0", dot: null },
+ ].map((b) => (
+
+ {b.dot && }
+ {b.label}
+
+ ))}
+
+
+ {/* SDK — primary */}
+
+
+ SDK — Build with AI Agents
+
+
+ Integrate agents into your app, automate workflows, run agents from code.
+
+
+
+
+
+
+ bash
+
+
+
+ $
+ {INSTALL_CMD}
+
+
+ {copied ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+ {/* Personal Assistant — secondary */}
+
+
+ Personal Assistant
+
+
+ Run a local AI agent with memory, voice, and web UI.
+
+
+
+
+
+
+ bash
+
+
+
+ $
+ curl -fsSL https://raw.githubusercontent.com/open-gitagent/gitagent/main/install.sh | bash
+
+ { navigator.clipboard.writeText("curl -fsSL https://raw.githubusercontent.com/open-gitagent/gitagent/main/install.sh | bash"); }}
+ className="text-muted-foreground/50 hover:text-foreground transition-colors shrink-0 ml-2"
+ aria-label="Copy curl command"
+ >
+
+
+
+
+
+
+ {/* CTAs */}
+
+
+ Get Started
+
+
+
+ Read Docs
+
+
+
+ GitHub
+
+
+
+
+ {/* Right — directory tree + agents-as-repos */}
+
+
+
+
+
+
+ your agent repo
+
+
+
+ {tree.map((item, i) => (
+
+ {item.icon}
+
+ {item.name}
+
+ {item.tag && (
+
+ {item.tag}
+
+ )}
+
+ ))}
+
+
+
+ {[
+ { cmd: "git fork org/base-agent", desc: "inherit personality + rules" },
+ { cmd: "git checkout -b feature/research-mode", desc: "experiment safely" },
+ { cmd: "git log memory/MEMORY.md", desc: "audit memory history" },
+ { cmd: "git diff SOUL.md", desc: "track personality changes" },
+ { cmd: "git revert HEAD~3", desc: "undo last 3 agent changes" },
+ { cmd: "git checkout v1.2.0", desc: "roll back to stable agent" },
+ ].map((line) => (
+
+ $
+ {line.cmd}
+ # {line.desc}
+
+ ))}
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentHooks.tsx b/src/components/gitAgent/GitAgentHooks.tsx
new file mode 100644
index 0000000..798a6a9
--- /dev/null
+++ b/src/components/gitAgent/GitAgentHooks.tsx
@@ -0,0 +1,245 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const hookEvents = [
+ {
+ event: "on_session_start",
+ when: "Before agent runs",
+ canBlock: true,
+ canModify: false,
+ },
+ {
+ event: "pre_tool_use",
+ when: "Before each tool call",
+ canBlock: true,
+ canModify: true,
+ },
+ {
+ event: "post_tool_failure",
+ when: "After a tool errors",
+ canBlock: false,
+ canModify: false,
+ },
+ {
+ event: "pre_query",
+ when: "Before LLM call",
+ canBlock: true,
+ canModify: false,
+ },
+ {
+ event: "post_response",
+ when: "After LLM responds",
+ canBlock: false,
+ canModify: false,
+ },
+ {
+ event: "file_changed",
+ when: "After file write",
+ canBlock: false,
+ canModify: false,
+ },
+ {
+ event: "on_error",
+ when: "On agent error",
+ canBlock: false,
+ canModify: false,
+ },
+];
+
+const hooksYaml = `hooks:
+ on_session_start:
+ - script:check-auth.sh
+ description: "Verify user authorization"
+
+ pre_tool_use:
+ - script:validate-command.sh
+ description: "Block dangerous CLI commands"
+
+ post_tool_failure:
+ - script:notify-error.sh
+
+ post_response:
+ - script:log-response.sh
+
+ pre_query:
+ - script:rate-limit.sh
+
+ file_changed:
+ - script:track-changes.sh
+
+ on_error:
+ - script:incident-report.sh`;
+
+const hookInput = `{
+ "event": "pre_tool_use",
+ "session_id": "uuid",
+ "tool": "cli",
+ "args": {"command": "rm -rf /"}
+}`;
+
+const hookOutput = `{"action": "allow"}
+{"action": "block", "reason": "Destructive command blocked"}
+{"action": "modify", "args": {"command": "echo safe"}}`;
+
+const sdkHooks = `for await (const msg of query({
+ prompt: "Deploy the service",
+ hooks: {
+ preToolUse: async (ctx) => {
+ if (ctx.toolName === "cli" && ctx.args.command?.includes("rm -rf"))
+ return { action: "block", reason: "Destructive command blocked" };
+ if (ctx.toolName === "write" && !ctx.args.path.startsWith("/safe/"))
+ return { action: "modify", args: { ...ctx.args, path: \`/safe/\${ctx.args.path}\` } };
+ return { action: "allow" };
+ },
+ onError: async (ctx) => {
+ console.error(\`Agent error: \${ctx.error}\`);
+ },
+ },
+})) { /* ... */ }`;
+
+function YesBadge() {
+ return (
+
+ Yes
+
+ );
+}
+
+function NoBadge() {
+ return (
+
+ No
+
+ );
+}
+
+export function GitAgentHooks() {
+ return (
+
+
+ {/* Section heading */}
+
+
+ Hooks
+
+
+ Lifecycle hooks let you intercept, block, or modify agent behavior at every stage —
+ via shell scripts or programmatic SDK callbacks.
+
+
+
+ {/* A. Hook Events */}
+
+
+ Hook Events
+
+
+ {/* Table header */}
+
+
+
+ {["Event", "When", "Can Block", "Can Modify"].map((col) => (
+
+ {col}
+
+ ))}
+
+
+ {hookEvents.map((row, i) => (
+
+
+ {row.event}
+
+
+ {row.when}
+
+ {row.canBlock ? : }
+ {row.canModify ? : }
+
+ ))}
+
+
+
+
+ {/* B. hooks.yaml config */}
+
+
+ hooks.yaml Config
+
+
+
+
+ {/* C. Hook Script Format — two code-blocks side by side */}
+
+
+ Hook Script Format
+
+
+ {/* Input */}
+
+
+ stdin (JSON input)
+
+
+
+
+ {/* Output */}
+
+
+ stdout (output options)
+
+
+
+
+
+
+ {/* D. Programmatic Hooks (SDK) */}
+
+
+ Programmatic Hooks (SDK)
+
+
+
+ SDK hooks run before script hooks — if an SDK hook blocks, the script hook is never called. Both can run independently when the SDK hook allows.
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentInstallSection.tsx b/src/components/gitAgent/GitAgentInstallSection.tsx
new file mode 100644
index 0000000..78ad5e3
--- /dev/null
+++ b/src/components/gitAgent/GitAgentInstallSection.tsx
@@ -0,0 +1,81 @@
+import { motion } from "framer-motion";
+import { ArrowRight } from "lucide-react";
+
+export function GitAgentInstallSection() {
+ return (
+
+
+
+
+ 09 — Get Started
+
+
+ Choose your path
+
+
+ Pick the mode that fits how you want to use GitAgent.
+
+
+
+
+ {/* SDK — primary */}
+
+
+
+ SDK
+
+
+ Build with AI Agents
+
+
+ Integrate agents into your app, automate workflows, and run agents from code.
+
+
+
+
+
+ {/* Personal Assistant */}
+
+
+
+ Personal Assistant
+
+
+ Run as Personal Agent Locally
+
+
+ Run a local AI agent with memory, voice, and web UI.
+
+
+
+ Personal Assistant Quick Start
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentIntegrations.tsx b/src/components/gitAgent/GitAgentIntegrations.tsx
new file mode 100644
index 0000000..02740ac
--- /dev/null
+++ b/src/components/gitAgent/GitAgentIntegrations.tsx
@@ -0,0 +1,157 @@
+import { motion } from "framer-motion";
+import { Globe, MessageCircle, Smartphone, Phone } from "lucide-react";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const composioCode = `# Set up Composio
+export COMPOSIO_API_KEY=your-key
+gitagent --voice --dir ~/assistant
+
+# Then in the Integrations tab, connect Gmail, Slack, GitHub, etc.
+# The agent will have access to those services as tools automatically`;
+
+const twilioWebhookCode = `Webhook URL: https://your-server:3333/api/phone/webhook`;
+
+export function GitAgentIntegrations() {
+ return (
+
+
+
+ Integrations
+
+ Connect GitAgent to external services via Composio, Telegram, WhatsApp, and Twilio.
+
+
+
+
+ {/* A. Composio card */}
+
+
+
+ Composio
+
+ 200+ integrations
+
+
+
+
+ Requires
+ COMPOSIO_API_KEY
+
+
+ Enables 200+ integrations: Gmail, Calendar, Slack, GitHub, Notion, Linear, Salesforce, and more.
+
+
+ Configure in the Integrations tab of the web UI.
+
+
+ GitAgent tools map directly to Composio actions.
+
+
+
+
+ {/* B. Telegram card */}
+
+
+
+ Telegram
+
+
+
+ Requires
+ TELEGRAM_BOT_TOKEN
+
+
+ {[
+ "Create bot via @BotFather → Enter token in Communication tab",
+ "Configure allowed users for access control",
+ "Files generated by the agent are auto-sent to Telegram",
+ "Used for approval gates in SkillFlows",
+ ].map((item, i) => (
+
+ ))}
+
+
+
+
+ {/* C. WhatsApp card */}
+
+
+
+ WhatsApp
+
+
+ {[
+ "Uses Baileys library — no phone number API needed",
+ "Connect via QR code in Communication tab",
+ "Session persists across restarts",
+ "Auto-responds to messages from your number",
+ ].map((item, i) => (
+
+ ))}
+
+
+
+ {/* D. Phone / SMS (Twilio) card */}
+
+
+
+
Phone / SMS (Twilio)
+
+
+
+ Configure this webhook URL in your Twilio console:
+
+
+
+
+
+
+ {/* E. Composio example */}
+
+
+ Composio Quick Start
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentIntegrationsSection.tsx b/src/components/gitAgent/GitAgentIntegrationsSection.tsx
new file mode 100644
index 0000000..e667a00
--- /dev/null
+++ b/src/components/gitAgent/GitAgentIntegrationsSection.tsx
@@ -0,0 +1,127 @@
+import { motion } from "framer-motion";
+import { Plug, MessageCircle, Phone, Smartphone } from "lucide-react";
+
+const integrations = [
+ {
+ icon: Plug,
+ name: "Composio",
+ tag: "200+ apps",
+ requirement: "COMPOSIO_API_KEY",
+ color: "text-blue-500",
+ desc: "Connect Gmail, Google Calendar, Slack, GitHub, Notion, Jira, and 200+ more services via the Composio toolkit. One key unlocks everything.",
+ features: ["Gmail & Calendar", "Slack & GitHub", "Notion & Jira", "CRM & payment tools"],
+ setup: `COMPOSIO_API_KEY=your_key
+# That's it — no agent.yaml changes needed.
+# Connect services in the Web UI Integrations tab.`,
+ },
+ {
+ icon: MessageCircle,
+ name: "Telegram",
+ tag: "BotFather",
+ requirement: "TELEGRAM_BOT_TOKEN",
+ color: "text-primary",
+ desc: "Create a bot via BotFather, add the token, and your agent is instantly reachable from Telegram — text, voice messages, and file sharing.",
+ features: ["Text & voice messages", "File sharing", "Group chat support", "Webhook or polling"],
+ setup: `TELEGRAM_BOT_TOKEN=your_token
+# That's it — no agent.yaml changes needed.
+# Configure allowed users via TELEGRAM_ALLOWED_USERS.`,
+ },
+ {
+ icon: Smartphone,
+ name: "WhatsApp",
+ tag: "Baileys QR",
+ requirement: "QR scan (no API key)",
+ color: "text-green-500",
+ desc: "Uses the Baileys library for direct WhatsApp connection. Scan a QR code from the Web UI Communication tab — no Meta API account needed.",
+ features: ["QR-based pairing", "No Meta API required", "Text & media", "Multi-device support"],
+ setup: `# No API key required
+# Go to Web UI → Communication tab
+# Scan QR code with WhatsApp`,
+ },
+ {
+ icon: Phone,
+ name: "Phone / SMS",
+ tag: "Twilio",
+ requirement: "Twilio phone number + webhook URL",
+ color: "text-orange-500",
+ desc: "Twilio webhook integration for inbound SMS and voice calls. Configure the webhook URL to your running agent and it handles phone interactions.",
+ features: ["Inbound SMS", "Voice call handling", "Webhook-based", "Twilio phone number"],
+ setup: `# In Twilio console:
+# Phone Numbers → your number → Messaging
+# Webhook URL: https://your-server:3333/api/phone/webhook`,
+ },
+];
+
+export function GitAgentIntegrationsSection() {
+ return (
+
+
+
+
+ 08 — Integrations
+
+
+ Integrations
+
+
+ Connect external services and reach your agent across every channel.
+
+
+
+
+ {integrations.map((item, i) => (
+
+ {/* Header */}
+
+
+ {item.name}
+
+ {item.tag}
+
+
+
+ {/* Requirement */}
+
+ Requires
+ {item.requirement}
+
+
+ {/* Description */}
+
+ {item.desc}
+
+
+ {/* Feature tags */}
+
+ {item.features.map((f) => (
+
+ {f}
+
+ ))}
+
+
+ {/* Setup snippet */}
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentInterfaces.tsx b/src/components/gitAgent/GitAgentInterfaces.tsx
new file mode 100644
index 0000000..5eb2497
--- /dev/null
+++ b/src/components/gitAgent/GitAgentInterfaces.tsx
@@ -0,0 +1,155 @@
+import { motion } from "framer-motion";
+import { Terminal, Globe, MessageSquare, Code2 } from "lucide-react";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const sdkCode = `import { query } from "@open-gitagent/opengap";\n\nfor await (const msg of query({\n prompt: "Summarise yesterday's tasks",\n dir: "./my-agent",\n})) {\n if (msg.type === "assistant") console.log(msg.content);\n}`;
+
+const paItems = [
+ {
+ icon: Terminal,
+ label: "CLI",
+ desc: "Interactive terminal REPL — launch an agent session from your shell.",
+ href: "/docs/cli",
+ },
+ {
+ icon: Globe,
+ label: "Web & Voice",
+ desc: "Full browser UI with chat, skills, scheduler, and real-time voice.",
+ href: "/docs/webui",
+ },
+ {
+ icon: MessageSquare,
+ label: "Messaging",
+ desc: "Connect to Telegram, WhatsApp, or phone via Twilio.",
+ href: "/docs/messaging",
+ },
+];
+
+export function GitAgentInterfaces() {
+ return (
+
+
+
+
+ 03 — Interfaces
+
+
+ Ways to Interact
+
+
+ GitAgent meets you where you work — terminal, browser, chat app, or embedded in your own code.
+
+
+
+
+
+ Building an app? {" "}
+ The SDK is the production entry point.{" "}
+
+ Start with the SDK Quickstart →
+ {" "}
+ before reading anything else on this page.
+
+
+
+
+ {/* SDK card */}
+
+
+
+
+
+
+
+ SDK
+ Embed in Node.js
+
+
+ The production entry point. Import GitAgent directly into your Node.js application and call query() to send prompts programmatically.
+
+
+
+
+
+
+
+ {[
+ { label: "SDK Quickstart", href: "/docs/quickstart/sdk" },
+ { label: "Full API", href: "/docs/sdk" },
+ { label: "SDK Cookbooks", href: "/docs/sdk/cookbooks/refactor-repo" },
+ ].map((link) => (
+
+ {link.label} →
+
+ ))}
+
+
+
+ {/* Personal Assistant card */}
+
+
+
+
+
+
+
+ Personal Assistant
+
+
+ Run GitAgent as a local assistant with multiple ways to interact — terminal, browser, or messaging apps.
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentMemory.tsx b/src/components/gitAgent/GitAgentMemory.tsx
new file mode 100644
index 0000000..ac98bf1
--- /dev/null
+++ b/src/components/gitAgent/GitAgentMemory.tsx
@@ -0,0 +1,228 @@
+import { motion } from "framer-motion";
+import { SmilePlus, Camera, BookOpen, Brain } from "lucide-react";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const layersYaml = `# memory/memory.yaml
+layers:
+ - name: working
+ path: memory/MEMORY.md
+ max_lines: 1000
+ format: markdown
+ - name: technical
+ path: memory/technical.md
+ max_lines: 500
+ format: markdown
+
+archive_policy:
+ max_entries: 500
+ compress_after: 30d`;
+
+const memoryTree = `memory/
+├── MEMORY.md # Primary memory (versioned)
+├── mood.md # Per-session mood log
+├── photos/ # Camera captures
+│ └── 2025-08-28-moment.jpg
+├── journal/ # Session reflections
+│ └── 2025-08-28.md
+└── archive/ # Auto-archived old entries
+ └── 2025-07-archive.md`;
+
+const memoryCommandsCode = `# View current memory in REPL
+/memory
+
+# Full git history of every memory write
+git log --oneline memory/MEMORY.md
+
+# Diff what changed last session
+git diff HEAD~1 memory/MEMORY.md
+
+# Undo a specific memory commit
+git revert a3f2e91
+
+# Read yesterday's mood log
+git show HEAD:memory/mood.md
+
+# Save something to memory via SDK
+query({ prompt: "Remember that the API base URL is https://api.example.com", dir: "./my-agent" })`;
+
+const additionalFeatures = [
+ {
+ icon: SmilePlus,
+ label: "Mood Log",
+ path: "memory/mood.md",
+ desc: "Session mood tracking (happy, frustrated, curious, excited, calm)",
+ },
+ {
+ icon: Camera,
+ label: "Photos",
+ path: "memory/photos/",
+ desc: "Captured memorable moments with INDEX.md",
+ },
+ {
+ icon: BookOpen,
+ label: "Journal",
+ path: "memory/journal/.md",
+ desc: "Auto-generated session reflections",
+ },
+ {
+ icon: Brain,
+ label: "Learning",
+ path: ".gitagent/learning/",
+ desc: "Task history and learned skills (JSON)",
+ },
+];
+
+export function GitAgentMemory() {
+ return (
+
+
+
+ Memory
+
+ Every memory write is a git commit. That means you can diff what changed last session, revert a bad memory write, or audit the full history — with standard git commands.
+
+
+
+ {/* A. Overview card */}
+
+
+
+ memory/MEMORY.md is the primary file. It's loaded automatically into every conversation.
+
+
+
+
+
+ {/* B. Directory tree */}
+
+
+ Directory Structure
+
+
+
+
+ {/* C. Memory Layers config */}
+
+
+ Memory Layers
+
+
+
+ When a layer exceeds max_lines, old entries are
+ auto-archived to{" "}
+ memory/archive/<YYYY-MM>.md
+
+
+
+
+ {/* C. Additional Memory Features */}
+
+
+ Other Memory Files
+
+ GitAgent also writes to a few other files automatically:
+
+ {additionalFeatures.map((f, i) => {
+ const Icon = f.icon;
+ return (
+
+
+
+ {f.label}
+
+ {f.path}
+ {f.desc}
+
+ );
+ })}
+
+
+
+ {/* E. Memory commands */}
+
+
+ Memory Commands
+
+
+
+
+ {/* F. Rolling Back Memory */}
+
+
+ Rolling Back Memory
+
+
+
+ Use git revert rather than{" "}
+ git reset --hard — revert preserves the audit trail while reset destroys it.
+
+
+
+ {/* G. Browsing the Archive */}
+
+
+ Browsing the Archive
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentMemorySection.tsx b/src/components/gitAgent/GitAgentMemorySection.tsx
new file mode 100644
index 0000000..8162771
--- /dev/null
+++ b/src/components/gitAgent/GitAgentMemorySection.tsx
@@ -0,0 +1,75 @@
+import { motion } from "framer-motion";
+import { Archive, Camera, BookOpen, Zap } from "lucide-react";
+
+const memoryFeatures = [
+ {
+ icon: Archive,
+ title: "Auto-Archiving",
+ desc: "When a layer exceeds max_lines, oldest entries are moved to memory/archive/.md — keeping MEMORY.md fast without losing history.",
+ color: "text-blue-500",
+ },
+ {
+ icon: BookOpen,
+ title: "Mood & Journal",
+ desc: "Per-session mood log (mood.md) and voice session reflection journal in memory/journal/ — both committed to git.",
+ color: "text-orange-500",
+ },
+ {
+ icon: Camera,
+ title: "Photos & Moments",
+ desc: "Voice mode captures webcam frames during memorable moments and stores them in memory/photos/ with a git commit.",
+ color: "text-purple-500",
+ },
+ {
+ icon: Zap,
+ title: "Learning",
+ desc: "Completed tasks are crystallized into reusable skills in .gitagent/learning/ — the agent improves over time.",
+ color: "text-primary",
+ },
+];
+
+export function GitAgentMemorySection() {
+ return (
+
+
+
+
+ 07 — Memory
+
+
+ Git-Native Memory
+
+
+ Every memory write is a git commit. Memory lives in plain Markdown files — readable, diffable, and rollback-able at any time.
+
+
+
+
+ {memoryFeatures.map((f, i) => (
+
+
+
+ {f.title}
+
+
+ {f.desc}
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentMessaging.tsx b/src/components/gitAgent/GitAgentMessaging.tsx
new file mode 100644
index 0000000..5d6d169
--- /dev/null
+++ b/src/components/gitAgent/GitAgentMessaging.tsx
@@ -0,0 +1,127 @@
+import { motion } from "framer-motion";
+import { Send, Smartphone, Phone } from "lucide-react";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+export function GitAgentMessaging() {
+ return (
+
+
+
+ Messaging
+
+ Connect your agent to Telegram, WhatsApp, and phone — all through the same running instance.
+
+
+
+
+
+ {/* Telegram */}
+
+
+
+ Set TELEGRAM_BOT_TOKEN and the agent automatically becomes a Telegram bot. No extra config needed.
+
+
+
+
+
+
+ {/* WhatsApp */}
+
+
+
+ WhatsApp has no env var toggle. Set it up once through the Web UI — credentials persist automatically after that.
+
+
+ {[
+ <>Start gitagent --voice>,
+ <>Open localhost:3333 → go to Integrations → scan the QR code>,
+ <>Credentials are saved to .gitagent/whatsapp-auth/creds.json>,
+ <>On every subsequent start, WhatsApp auto-reconnects if that file exists>,
+ ].map((step, i) => (
+
+
+ {i + 1}
+
+ {step}
+
+ ))}
+
+
+
+ {/* Phone (Twilio) */}
+
+
+
+
+ Phone
+ via Twilio
+
+
+
+ GitAgent does not read TWILIO_ACCOUNT_SID or TWILIO_AUTH_TOKEN. Setup is done entirely in the Twilio Console.
+
+
+ {[
+ <>Start gitagent --voice>,
+ <>In the Twilio Console, set your webhook URL to your server's phone endpoint>,
+ <>Incoming calls are routed to the agent automatically>,
+ ].map((step, i) => (
+
+
+ {i + 1}
+
+ {step}
+
+ ))}
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentModels.tsx b/src/components/gitAgent/GitAgentModels.tsx
new file mode 100644
index 0000000..2dac3cc
--- /dev/null
+++ b/src/components/gitAgent/GitAgentModels.tsx
@@ -0,0 +1,275 @@
+import { useState } from "react";
+import { motion } from "framer-motion";
+import { Copy, Check } from "lucide-react";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const providers = [
+ { name: "Anthropic", example: "anthropic:claude-sonnet-4-6", envVar: "ANTHROPIC_API_KEY" },
+ { name: "OpenAI", example: "openai:gpt-4o", envVar: "OPENAI_API_KEY" },
+ { name: "Google", example: "google:gemini-2.0-flash-001", envVar: "GOOGLE_API_KEY" },
+ { name: "Groq", example: "groq:llama-3.3-70b-versatile", envVar: "GROQ_API_KEY" },
+ { name: "xAI", example: "xai:grok-2-1212", envVar: "XAI_API_KEY" },
+ { name: "Mistral", example: "mistral:mistral-large-latest", envVar: "MISTRAL_API_KEY" },
+ { name: "OpenRouter", example: "openrouter:anthropic/claude-3.5-sonnet", envVar: "OPENROUTER_API_KEY" },
+ { name: "Cerebras", example: "cerebras:llama3.1-70b", envVar: "CEREBRAS_API_KEY" },
+ { name: "DeepSeek", example: "deepseek:deepseek-chat", envVar: "DEEPSEEK_API_KEY" },
+ { name: "Amazon Bedrock", example: "amazon-bedrock:anthropic.claude-3-sonnet", envVar: "AWS credentials" },
+ { name: "Google Vertex", example: "google-vertex:gemini-2.5-flash", envVar: "GCP ADC" },
+ { name: "Azure OpenAI", example: "azure-openai-responses:gpt-4o", envVar: "AZURE_OPENAI_API_KEY" },
+];
+
+const resolutionSteps = [
+ {
+ step: 1,
+ label: "Environment config model_override",
+ detail: "from config/.yaml",
+ },
+ {
+ step: 2,
+ label: "CLI flag --model provider:model-id",
+ detail: "passed at runtime",
+ },
+ {
+ step: 3,
+ label: "agent.yaml model.preferred",
+ detail: "defined in the agent repo",
+ },
+];
+
+const customEndpointCode = `# Inline URL
+gitagent --model "ollama:llama3@http://localhost:11434/v1" --voice --dir ~/assistant
+
+# Environment variable
+export GITAGENT_MODEL_BASE_URL=http://localhost:11434/v1
+gitagent --model "ollama:llama3" --voice --dir ~/assistant
+
+# In agent.yaml
+# model:
+# preferred: "custom:my-model@https://my-proxy.com/v1"`;
+
+const compatibleEndpoints = [
+ { name: "Ollama", port: "11434" },
+ { name: "LM Studio", port: "1234" },
+ { name: "vLLM", port: "8000" },
+ { name: "LiteLLM", port: "4000" },
+ { name: "Lyzr AI Studio", port: "" },
+ { name: "Any OpenAI-compatible proxy", port: "" },
+];
+
+const lyzrSdkCode = `import { query } from "@open-gitagent/opengap";
+
+const result = query({
+ prompt: "Hello! What can you help me with?",
+ dir: "/path/to/agent",
+ model: \`lyzr:\${LYZR_AGENT_ID}@https://agent-prod.studio.lyzr.ai/v4\`,
+ constraints: { temperature: 0.7, maxTokens: 2000 },
+});
+for await (const msg of result) {
+ if (msg.type === "assistant") console.log(msg.content);
+}`;
+
+const lyzrOptions = [
+ { label: "Via installer (easiest)", desc: "Run the interactive installer and select Lyzr when prompted" },
+ { label: "Via CLI flag", desc: `gitagent --model "lyzr:@https://agent-prod.studio.lyzr.ai/v4" --dir ~/assistant` },
+ { label: "Via SDK", desc: "Use the query() function with a lyzr: model string (see example below)" },
+];
+
+export function GitAgentModels() {
+ const [copiedEndpoint, setCopiedEndpoint] = useState(false);
+
+ const handleCopyEndpoint = () => {
+ navigator.clipboard.writeText(customEndpointCode);
+ setCopiedEndpoint(true);
+ setTimeout(() => setCopiedEndpoint(false), 2000);
+ };
+ return (
+
+
+ {/* Section heading */}
+
+
+ Models & Providers
+
+
+ 12 providers out of the box. Custom endpoints and OpenAI-compatible proxies supported.
+
+
+
+ {/* A. Providers grid — 3 cols on lg */}
+
+
+ Supported Providers
+
+
+ {providers.map((p, i) => (
+
+
+
+ {p.name}
+
+
+ {p.envVar}
+
+
+
+ {p.example}
+
+
+ ))}
+
+
+
+ {/* B. Model resolution order */}
+
+
+ Model Resolution Order
+
+
+ {resolutionSteps.map((s, i) => (
+
+
+ {s.step}
+
+
+
+ {s.label}
+
+
+ — {s.detail}
+
+
+
+ ))}
+
+
+
+ {/* C. Custom / OpenAI-Compatible Endpoints */}
+
+
+ Custom / OpenAI-Compatible Endpoints
+
+
+
+
+
+
+ terminal
+
+ {copiedEndpoint ? : }
+
+
+
+ {customEndpointCode.split("\n").map((line, i) => (
+
+ {line}
+
+ ))}
+
+
+
+ {compatibleEndpoints.map((ep) => (
+
+ {ep.name}
+ {ep.port && (
+ :{ep.port}
+ )}
+
+ ))}
+
+
+
+ {/* D. Lyzr Integration */}
+
+
+
+
+ Lyzr AI Studio Integration
+
+
+ Connect to Lyzr's managed agent infrastructure. Your agent runs on Lyzr's servers with full observability.
+
+
+
+ {lyzrOptions.map((opt, i) => (
+
+
+ {opt.label}
+
+ {opt.label === "Via CLI flag" ? (
+
+ {opt.desc}
+
+ ) : (
+
+ {opt.desc}
+
+ )}
+
+ ))}
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentModelsSection.tsx b/src/components/gitAgent/GitAgentModelsSection.tsx
new file mode 100644
index 0000000..e53432c
--- /dev/null
+++ b/src/components/gitAgent/GitAgentModelsSection.tsx
@@ -0,0 +1,64 @@
+import { motion } from "framer-motion";
+
+const providers = [
+ { name: "Anthropic", format: "anthropic:", envKey: "ANTHROPIC_API_KEY" },
+ { name: "OpenAI", format: "openai:", envKey: "OPENAI_API_KEY" },
+ { name: "Google", format: "google:", envKey: "GEMINI_API_KEY" },
+ { name: "Groq", format: "groq:", envKey: "GROQ_API_KEY" },
+ { name: "xAI", format: "xai:", envKey: "XAI_API_KEY" },
+ { name: "Mistral", format: "mistral:", envKey: "MISTRAL_API_KEY" },
+ { name: "OpenRouter", format: "openrouter:", envKey: "OPENROUTER_API_KEY" },
+ { name: "Cerebras", format: "cerebras:", envKey: "CEREBRAS_API_KEY" },
+ { name: "DeepSeek", format: "deepseek:", envKey: "DEEPSEEK_API_KEY" },
+ { name: "AWS Bedrock", format: "amazon-bedrock:", envKey: "AWS_ACCESS_KEY_ID" },
+ { name: "Google Vertex", format: "google-vertex:", envKey: "GOOGLE_APPLICATION_CREDENTIALS" },
+ { name: "Azure OpenAI", format: "azure-openai-responses:", envKey: "AZURE_OPENAI_API_KEY" },
+];
+
+export function GitAgentModelsSection() {
+ return (
+
+
+
+
+ 06 — Models
+
+
+ 12+ Model Providers
+
+
+ Switch providers by changing one env var. Any OpenAI-compatible endpoint works too.
+
+
+
+
+ {providers.map((p, i) => (
+
+
+ {p.name}
+
+
+ {p.format}
+
+
+ {p.envKey}
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentNavbar.tsx b/src/components/gitAgent/GitAgentNavbar.tsx
new file mode 100644
index 0000000..c0a902d
--- /dev/null
+++ b/src/components/gitAgent/GitAgentNavbar.tsx
@@ -0,0 +1,191 @@
+import { useState, useRef, useEffect } from "react";
+import { Menu, X, Search, Github } from "lucide-react";
+import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
+import { sidebarGroups } from "@/components/gitAgent/GitAgentSidebar";
+
+const allItems = sidebarGroups.flatMap((g) =>
+ g.items.map((item) => ({ ...item, group: g.label, slug: g.slug }))
+);
+
+interface GitAgentNavbarProps {
+ variant?: "overview" | "docs" | "subpage";
+}
+
+export function GitAgentNavbar({ variant = "docs" }: GitAgentNavbarProps) {
+ const [sheetOpen, setSheetOpen] = useState(false);
+ const [query, setQuery] = useState("");
+ const [searchOpen, setSearchOpen] = useState(false);
+ const searchRef = useRef(null);
+
+ useEffect(() => {
+ function handleClickOutside(e: MouseEvent) {
+ if (searchRef.current && !searchRef.current.contains(e.target as Node)) {
+ setSearchOpen(false);
+ }
+ }
+ document.addEventListener("mousedown", handleClickOutside);
+ return () => document.removeEventListener("mousedown", handleClickOutside);
+ }, []);
+
+ const filtered = query
+ ? allItems.filter((item) =>
+ item.label.toLowerCase().includes(query.toLowerCase()) ||
+ item.group.toLowerCase().includes(query.toLowerCase())
+ )
+ : allItems;
+
+ return (
+
+
+ {/* Left: back + Logo + version badge */}
+
+ {(variant === "subpage" || variant === "docs") && (
+
+ ←
+
+ )}
+
+ Git Agent
+
+
+ v1.5.0
+
+
+
+ {/* Middle: search — docs only, desktop */}
+ {variant === "docs" && (
+
+
+
+ setQuery(e.target.value)}
+ onFocus={() => setSearchOpen(true)}
+ placeholder="Search docs…"
+ className="text-xs font-body bg-transparent outline-none text-foreground placeholder:text-muted-foreground/40 w-full"
+ />
+ {query && (
+ setQuery("")}
+ className="text-muted-foreground/50 hover:text-foreground transition-colors shrink-0"
+ aria-label="Clear search"
+ >
+
+
+ )}
+
+
+ {searchOpen && (
+
+ {filtered.length === 0 ? (
+
No results
+ ) : (
+
+ )}
+
+ )}
+
+ )}
+
+ {/* Right — desktop */}
+
+
+ {/* Mobile right */}
+
+ {variant === "docs" ? (
+
+
+
+ {sheetOpen ? : }
+
+
+
+
+ Git Agent
+ docs
+
+
+ {sidebarGroups.map((group, groupIndex) => (
+
+ ))}
+
+
+
+
+ ) : (
+
+ GitHub
+
+ )}
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentOverview.tsx b/src/components/gitAgent/GitAgentOverview.tsx
new file mode 100644
index 0000000..fae7072
--- /dev/null
+++ b/src/components/gitAgent/GitAgentOverview.tsx
@@ -0,0 +1,219 @@
+import { motion } from "framer-motion";
+
+const repoPhilosophyItems = [
+ {
+ file: "agent.yaml",
+ desc: "model, tools, runtime config",
+ dot: "bg-primary",
+ },
+ {
+ file: "SOUL.md",
+ desc: "personality and identity",
+ dot: "bg-primary/70",
+ },
+ {
+ file: "RULES.md",
+ desc: "behavioral constraints",
+ dot: "bg-foreground/40",
+ },
+ {
+ file: "memory/",
+ desc: "git-committed memory with full history",
+ dot: "bg-primary/50",
+ },
+ {
+ file: "tools/ + skills/ + hooks/",
+ desc: "all version-controlled",
+ dot: "bg-foreground/30",
+ },
+];
+
+const comparisonRows = [
+ {
+ dimension: "Primary purpose",
+ gitagent: "General-purpose AI agent framework",
+ openclaw: "General-purpose life/work assistant",
+ },
+ {
+ dimension: "Security model",
+ gitagent: "Git-native (all changes tracked, reversible), auditable",
+ openclaw:
+ "Auth disabled by default, plaintext credentials, vulnerable skill marketplace",
+ },
+ {
+ dimension: "Voice mode",
+ gitagent:
+ "Real-time bidirectional with OpenAI Realtime API, camera input",
+ openclaw: "TTS/STT via ElevenLabs, no real-time bidirectional",
+ },
+ {
+ dimension: "Skills",
+ gitagent:
+ "Curated marketplace, agent creates its own skills, SkillFlow",
+ openclaw: "13,700+ community skills (~20% flagged malicious)",
+ },
+ {
+ dimension: "Memory",
+ gitagent: "Structured git-committed memory with RL + archival",
+ openclaw: "Markdown diary entries",
+ },
+ {
+ dimension: "Multi-channel",
+ gitagent: "Voice UI, Telegram, WhatsApp",
+ openclaw: "20+ channels",
+ },
+ {
+ dimension: "Architecture",
+ gitagent: "Single focused process, SDK for embedding",
+ openclaw: "Gateway + multiple services",
+ },
+];
+
+export function GitAgentOverview() {
+ return (
+
+ {/* Brief intro */}
+
+
+ Overview
+
+
+ GitAgent is a git-native, multimodal AI agent harness built in TypeScript. Your agent lives entirely inside a git repository — identity, memory, rules, tools, and skills are all plain files you can read, diff, and roll back.
+ Install with one command, configure in plain text, and run it anywhere Node.js runs.
+
+
+
+ {/* Why GitAgent heading */}
+
+
+ Why GitAgent
+
+
+ Agents as repos — your agent IS a git repository.
+
+
+
+ {/* Two-column: repo philosophy + narrower scope note */}
+
+ {/* Left: repo philosophy */}
+
+
+ Your agent repo
+
+
+ {repoPhilosophyItems.map((item) => (
+
+
+
+ {item.file}
+ — {item.desc}
+
+
+ ))}
+
+
+ Fork an agent. Branch a personality.{" "}
+ git log your agent's memory.{" "}
+ diff its rules. This is agents as repos.
+
+
+
+ {/* Right: scope note */}
+
+
+ Design philosophy
+
+
+ GitAgent is narrower in scope but deeper in execution.
+
+
+ Rather than being a general-purpose life assistant, GitAgent focuses entirely on
+ being the best autonomous coding and project agent possible. Every design decision
+ optimizes for reliability, security, and developer trust.
+
+
+ {[
+ "All agent state is version-controlled",
+ "No silent credential storage",
+ "Auditable by design",
+ "Composable skills via SkillFlow",
+ ].map((point) => (
+
+
+ {point}
+
+ ))}
+
+
+
+
+ {/* Comparison table */}
+
+
+ {/* Table header */}
+
+ {["Dimension", "GitAgent", "OpenClaw"].map((col) => (
+
+ {col}
+
+ ))}
+
+
+ {/* Table rows */}
+ {comparisonRows.map((row, i) => (
+
+
+ {row.dimension}
+
+
+ {row.gitagent}
+
+
+ {row.openclaw}
+
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentPlugins.tsx b/src/components/gitAgent/GitAgentPlugins.tsx
new file mode 100644
index 0000000..cc7d786
--- /dev/null
+++ b/src/components/gitAgent/GitAgentPlugins.tsx
@@ -0,0 +1,180 @@
+import { motion } from "framer-motion";
+import { Wrench, Zap, MessageSquare, GitBranch, Database } from "lucide-react";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const manifestYaml = `id: my-plugin
+name: My Plugin
+version: 1.0.0
+description: What this plugin does
+author: Your Name
+license: MIT
+engine: ">=1.0.0"
+
+provides:
+ tools: true
+ skills: true
+ prompt: prompt.md
+ hooks:
+ pre_tool_use:
+ - script: hooks/validate.sh # any filename — hooks.yaml points to it`;
+
+const structureCode = `plugins/my-plugin/
+ plugin.yaml # manifest
+ prompt.md # appended to system prompt
+ tools/
+ my-tool.yaml # declarative tools
+ skills/
+ my-skill/
+ SKILL.md
+ hooks/
+ validate.sh # example — any filename, referenced from hooks.yaml`;
+
+const managementCode = `# Install from git URL
+gitagent plugin install https://github.com/user/plugin
+# Install from local path
+gitagent plugin install ./path/to/plugin
+# List all plugins
+gitagent plugin list
+# Enable / disable
+gitagent plugin enable my-plugin
+gitagent plugin disable my-plugin
+# Remove
+gitagent plugin remove my-plugin
+# Scaffold new plugin
+gitagent plugin init my-plugin`;
+
+const agentYamlConfig = `plugins:
+ my-plugin:
+ enabled: true
+ config:
+ api_key: "your-api-key"
+ timeout: 30`;
+
+const sdkCode = `import type { GitagentPluginApi } from "@open-gitagent/opengap";
+
+export function register(api: GitagentPluginApi) {
+ api.registerTool(myTool);
+ api.registerHook("pre_tool_use", async (ctx) => ({ action: "allow" }));
+ api.addPrompt("Additional context for the agent...");
+}`;
+
+const provisions = [
+ { icon: Wrench, label: "Tools", desc: "Expose new typed tools to the agent" },
+ { icon: Zap, label: "Skills", desc: "Add reusable skill definitions" },
+ { icon: MessageSquare, label: "System Prompt Injection", desc: "Append context via prompt.md" },
+ { icon: GitBranch, label: "Lifecycle Hooks", desc: "React to tool use and session events" },
+ { icon: Database, label: "Memory Layers", desc: "Add scoped memory files" },
+];
+
+export function GitAgentPlugins() {
+ return (
+
+
+
+ Plugin System
+
+ Extend GitAgent with installable plugins that provide tools, skills, hooks, and memory layers.
+
+
+
+
+ {/* A. Plugin Manifest */}
+
+
+ Plugin Manifest
+
+
+
+
+ {/* B. Plugin Structure */}
+
+
+ Plugin Structure
+
+
+
+ {/* D. agent.yaml plugin config */}
+
+ agent.yaml Plugin Config
+
+
+
+ Config values can reference environment variables using{" "}
+ {"${VAR_NAME}"} syntax — API keys are never hardcoded.
+
+
+
+
+ {/* C. Plugin Management CLI */}
+
+
+ Plugin Management CLI
+
+
+
+
+ {/* E. Programmatic Plugin (SDK) */}
+
+
+ Programmatic Plugin (SDK)
+
+
+
+
+ {/* F. What plugins can provide */}
+
+
+ What Plugins Can Provide
+
+
+ {provisions.map((p, i) => {
+ const Icon = p.icon;
+ return (
+
+
+ {p.label}
+ {p.desc}
+
+ );
+ })}
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentQuickStart.tsx b/src/components/gitAgent/GitAgentQuickStart.tsx
new file mode 100644
index 0000000..629f984
--- /dev/null
+++ b/src/components/gitAgent/GitAgentQuickStart.tsx
@@ -0,0 +1,117 @@
+import { motion } from "framer-motion";
+import { Terminal, Code2, ArrowRight } from "lucide-react";
+
+const paths = [
+ {
+ icon: Code2,
+ title: "SDK / Embed",
+ desc: "Import GitAgent as a Node.js library and call query() from your own application or script.",
+ bullets: ["Zero-config repo analysis", "Streaming async iterator", "Composable tools & hooks"],
+ href: "/docs/quickstart/sdk",
+ cta: "View SDK quickstart",
+ },
+ {
+ icon: Terminal,
+ title: "Personal Assistant",
+ desc: "Install GitAgent as a global CLI and run it as your daily coding companion — voice, text, or web UI.",
+ bullets: ["Interactive install script", "Voice + text interface", "Auto-scaffolds agent.yaml & SOUL.md"],
+ href: "/docs/quickstart/personal-assistant",
+ cta: "Get started",
+ },
+];
+
+const comparison = [
+ { aspect: "Audience", pa: "End users / developers", sdk: "Application developers" },
+ { aspect: "Install", pa: "npm install -g", sdk: "npm install (local dep)" },
+ { aspect: "Entry point", pa: "gitagent CLI", sdk: "query() function" },
+ { aspect: "Interface", pa: "Voice, Web UI, REPL", sdk: "Streaming async iterator" },
+ { aspect: "Agent config", pa: "agent.yaml + SOUL.md", sdk: "Optional — works without" },
+];
+
+export function GitAgentQuickStart() {
+ return (
+
+
+
+ Quick Start
+
+ Two entry points depending on how you want to use GitAgent. Pick the one that matches your goal.
+
+
+
+ {/* Two-card chooser */}
+
+ {paths.map((p, i) => (
+
+
+
+
+ {p.desc}
+
+
+ {p.bullets.map((b) => (
+
+
+ {b}
+
+ ))}
+
+
+ {p.cta}
+
+
+
+
+ ))}
+
+
+ {/* Comparison table */}
+
+
+ Which should I use?
+
+
+
+
+
+ Aspect
+ SDK / Embed
+ Personal Assistant
+
+
+
+ {comparison.map((row, i) => (
+
+ {row.aspect}
+ {row.sdk}
+ {row.pa}
+
+ ))}
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentQuickStartPersonalAssistant.tsx b/src/components/gitAgent/GitAgentQuickStartPersonalAssistant.tsx
new file mode 100644
index 0000000..62f07a1
--- /dev/null
+++ b/src/components/gitAgent/GitAgentQuickStartPersonalAssistant.tsx
@@ -0,0 +1,249 @@
+import { motion } from "framer-motion";
+import { Info } from "lucide-react";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const installMethods = [
+ {
+ title: "Interactive installer (recommended)",
+ code: `curl -fsSL https://raw.githubusercontent.com/open-gitagent/gitagent/main/install.sh | bash`,
+ desc: "Installs globally via npm, walks through API key setup, launches web UI at http://localhost:3333",
+ },
+ {
+ title: "Manual install",
+ code: `npm install -g @open-gitagent/opengap
+mkdir ~/assistant && cd ~/assistant && git init
+gitagent --voice --dir .`,
+ desc: "",
+ },
+];
+
+const setupModes = [
+ {
+ name: "Install with LYZR",
+ desc: "Easiest — uses Lyzr AI Studio cloud",
+ keys: "LYZR_API_KEY",
+ },
+ {
+ name: "Voice + Text",
+ desc: "Real-time voice + text chat",
+ keys: "OPENAI_API_KEY (or GEMINI_API_KEY for --voice gemini)",
+ },
+ {
+ name: "Text Only",
+ desc: "Terminal REPL, no voice or web UI",
+ keys: "ANTHROPIC_API_KEY",
+ },
+ {
+ name: "Advanced Setup",
+ desc: "Choose voice adapter, model, port, integrations",
+ keys: "varies",
+ },
+];
+
+const quickExamples = [
+ {
+ comment: "# Single-shot query",
+ code: `gitagent --dir ~/my-project "Build a REST API for user management"`,
+ },
+ {
+ comment: "# With voice UI",
+ code: `gitagent --voice --dir ~/assistant`,
+ },
+ {
+ comment: "# Local repo mode — clone, fix, commit to session branch",
+ code: `gitagent --repo https://github.com/org/repo --pat ghp_xxx "Fix the login bug"`,
+ },
+];
+
+export function GitAgentQuickStartPersonalAssistant() {
+ return (
+
+
+ {/* Section heading */}
+
+
+ Quick Start
+ {" / "}
+
+
+ Personal Assistant
+
+
+ Install GitAgent as a global CLI and run it as your daily coding companion — voice, text, or web UI.
+
+
+
+ {/* Videos */}
+
+
+
+
+
+ {/* A. Install methods */}
+
+
+ Installation
+
+
+ {installMethods.map((method, i) => (
+
+
+ {method.title}
+
+
+ {method.desc && (
+
+ {method.desc}
+
+ )}
+
+ ))}
+
+
+
+ {/* B. Setup Modes — 2×2 grid */}
+
+
+ Setup Modes
+
+
+ {setupModes.map((mode, i) => (
+
+
+
+ {mode.name}
+
+
+ {mode.desc}
+
+
+ {mode.keys}
+
+
+
+ ))}
+
+
+
+ {/* C. First run note */}
+
+
+
+
+
+ Auto-scaffold on first run:
+
+ gitagent --dir ~/my-project 'Explain this project'
+ {" "}
+ auto-creates agent.yaml,{" "}
+ SOUL.md, and{" "}
+ memory/ in the target directory.
+
+
+
+
+
+ {/* D. Quick examples */}
+
+
+ Quick Examples
+
+
+
+
+
+
+
+ terminal
+
+
+
+ {quickExamples.map((ex, i) => (
+
+
+ {ex.comment}
+
+
+ {ex.code}
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentQuickStartSDK.tsx b/src/components/gitAgent/GitAgentQuickStartSDK.tsx
new file mode 100644
index 0000000..e88f8f7
--- /dev/null
+++ b/src/components/gitAgent/GitAgentQuickStartSDK.tsx
@@ -0,0 +1,133 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const installCode = `npm install @open-gitagent/opengap`;
+
+const pathWithRepo = `import { query } from "@open-gitagent/opengap";
+
+for await (const msg of query({
+ prompt: "Summarise the open pull requests",
+ repo: "https://github.com/open-gitagent/opengap",
+})) {
+ if (msg.type === "assistant") console.log(msg.content);
+}`;
+
+const pathWithoutRepo = `import { query } from "@open-gitagent/opengap";
+
+for await (const msg of query({
+ prompt: "Refactor the auth module in src/auth.ts",
+ model: "anthropic:claude-sonnet-4-6",
+ dir: "./my-project",
+})) {
+ if (msg.type === "assistant") console.log(msg.content);
+}`;
+
+const nextSteps = [
+ { label: "SDK reference", href: "/docs/sdk" },
+];
+
+export function GitAgentQuickStartSDK() {
+ return (
+
+
+ {/* Breadcrumb + heading */}
+
+
+ Quick Start
+ {" / "}
+
+ SDK / Embed
+
+ Import GitAgent as a Node.js library and call{" "}
+ query() from your own application or script.
+
+
+
+ {/* Install */}
+
+
+ Install
+
+
+
+
+ {/* Two code paths side by side */}
+
+
+ Two ways to call query()
+
+
+ {/* Path 1 */}
+
+ Path 1 — With repo URL
+
+
+ Clones the repo, reads{" "}
+ agent.yaml,{" "}
+ SOUL.md, and skills automatically. Zero local setup.
+
+
+
+ {/* Path 2 */}
+
+ Path 2 — Without repo
+
+
+ No agent directory required. Points at a local working directory — ideal for embedding in an existing codebase or CI pipeline.
+
+
+
+
+
+ {/* Next steps */}
+
+
+ Next steps
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentSDK.tsx b/src/components/gitAgent/GitAgentSDK.tsx
new file mode 100644
index 0000000..076b34c
--- /dev/null
+++ b/src/components/gitAgent/GitAgentSDK.tsx
@@ -0,0 +1,397 @@
+import { useState } from "react";
+import { motion } from "framer-motion";
+import { Copy, Check } from "lucide-react";
+import { track } from "@/lib/analytics";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const tabs = ["query()", "tool()", "buildTool()", "hooks"] as const;
+type Tab = (typeof tabs)[number];
+
+const codeMap: Record = {
+ "query()": `import { query } from "@open-gitagent/opengap";
+
+for await (const msg of query({
+ prompt: "Refactor the auth module",
+ dir: "/path/to/agent",
+ model: "anthropic:claude-sonnet-4-6",
+})) {
+ switch (msg.type) {
+ case "delta": // streaming text chunk
+ process.stdout.write(msg.content); break;
+ case "assistant": // complete response
+ console.log(\`\\nTokens: \${msg.usage?.totalTokens}\`); break;
+ case "tool_use": // tool invocation
+ console.log(\`Tool: \${msg.toolName}(\${JSON.stringify(msg.args)})\`); break;
+ case "tool_result": // tool output
+ console.log(\`Result: \${msg.content}\`); break;
+ case "system": // lifecycle events & errors
+ console.log(\`[\${msg.subtype}] \${msg.content}\`); break;
+ }
+}`,
+ "tool()": `import { query, tool } from "@open-gitagent/opengap";
+
+const search = tool(
+ "search_docs",
+ "Search the documentation",
+ {
+ properties: {
+ query: { type: "string", description: "Search query" },
+ limit: { type: "number", description: "Max results" },
+ },
+ required: ["query"],
+ },
+ async (args) => {
+ const results = await mySearchEngine(args.query, args.limit ?? 10);
+ return { text: JSON.stringify(results), details: { count: results.length } };
+ },
+);
+
+for await (const msg of query({ prompt: "Find auth docs", tools: [search] })) {
+ // agent can now call search_docs
+}`,
+ "buildTool()": `import { buildTool } from "@open-gitagent/opengap";
+
+const myTool = buildTool({
+ name: "search_docs",
+ description: "Search documentation",
+ parameters: {
+ properties: { query: { type: "string" } },
+ required: ["query"],
+ },
+ execute: async (args) => {
+ return "Results: ...";
+ },
+ metadata: {
+ isConcurrencySafe: true, // safe to run in parallel
+ isReadOnly: true, // no side effects
+ maxResultSizeChars: 20000, // truncate large results
+ },
+});`,
+ hooks: `const result = query({
+ prompt: "Deploy to production",
+ dir: "/path/to/agent",
+ hooks: {
+ onSessionStart: async (ctx) => ({ action: "allow" }),
+ preToolUse: async (ctx) => {
+ if (ctx.toolName === "cli" && ctx.args.command.includes("deploy")) {
+ return { action: "block", reason: "Manual approval required" };
+ }
+ return { action: "allow" };
+ },
+ postToolFailure: async (ctx) => {
+ console.error(\`Tool \${ctx.toolName} failed: \${ctx.error}\`);
+ },
+ preQuery: async (ctx) => {
+ console.log(\`Sending prompt to LLM: \${ctx.sessionId}\`);
+ return { action: "allow" };
+ },
+ postResponse: async (ctx) => {
+ console.log(\`Session \${ctx.sessionId} responded\`);
+ },
+ fileChanged: async (ctx) => {
+ console.log(\`File changed: \${ctx.path}\`);
+ },
+ onError: async (ctx) => {
+ console.error(\`Error in \${ctx.sessionId}: \${ctx.error}\`);
+ },
+ },
+});`,
+};
+
+const pathWithRepo = `import { query } from "@open-gitagent/opengap";
+
+for await (const msg of query({
+ repo: "https://github.com/open-gitagent/opengap",
+ prompt: "Summarise the open pull requests",
+})) {
+ if (msg.type === "assistant") console.log(msg.content);
+}`;
+
+const pathWithoutRepo = `import { query } from "@open-gitagent/opengap";
+
+for await (const msg of query({
+ prompt: "Refactor the auth module in src/auth.ts",
+ model: "anthropic:claude-sonnet-4-6",
+ dir: "./my-project",
+})) {
+ if (msg.type === "assistant") console.log(msg.content);
+}`;
+
+const queryOptions = [
+ { name: "prompt", type: "string | AsyncIterable", desc: "User prompt or multi-turn stream", required: true },
+ { name: "dir", type: "string", desc: "Agent directory (default: cwd)" },
+ { name: "model", type: "string", desc: '"provider:model-id"' },
+ { name: "env", type: "string", desc: "Environment config (config/.yaml)" },
+ { name: "systemPrompt", type: "string", desc: "Override discovered system prompt" },
+ { name: "systemPromptSuffix", type: "string", desc: "Append to discovered system prompt" },
+ { name: "tools", type: "GCToolDefinition[]", desc: "Additional tools" },
+ { name: "replaceBuiltinTools", type: "boolean", desc: "Skip cli/read/write/memory" },
+ { name: "allowedTools", type: "string[]", desc: "Tool name allowlist" },
+ { name: "disallowedTools", type: "string[]", desc: "Tool name denylist" },
+ { name: "maxTurns", type: "number", desc: "Max agent turns" },
+ { name: "abortController", type: "AbortController", desc: "Cancellation signal" },
+ { name: "constraints", type: "object", desc: "temperature, maxTokens, topP, topK" },
+ { name: "hooks", type: "object", desc: "onSessionStart, preToolUse, postToolFailure, preQuery, postResponse, fileChanged, onError lifecycle hooks" },
+ { name: "repo", type: "object", desc: "Work on a remote git repo — clone, run agent, auto-commit changes to session branch" },
+ { name: "sandbox", type: "SandboxOptions | boolean", desc: "Run agent inside an E2B cloud VM (true uses defaults)" },
+ { name: "sessionId", type: "string", desc: "Tag or resume a specific session" },
+] as const;
+
+const messageTypes = [
+ { type: "delta", desc: "Streaming text/thinking chunk", fields: "deltaType, content" },
+ { type: "assistant", desc: "Complete LLM response", fields: "content, model, usage, stopReason" },
+ { type: "tool_use", desc: "Tool invocation", fields: "toolName, args, toolCallId" },
+ { type: "tool_result", desc: "Tool output", fields: "toolName, content, isError, toolCallId" },
+ { type: "system", desc: "Lifecycle events", fields: "subtype, content, metadata" },
+ { type: "user", desc: "User message (multi-turn)", fields: "content" },
+];
+
+export function GitAgentSDK() {
+ const [activeTab, setActiveTab] = useState("query()");
+ const [copied, setCopied] = useState(false);
+
+ const handleCopy = () => {
+ navigator.clipboard.writeText(codeMap[activeTab]);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ track("gitagent_sdk_code_copied");
+ };
+
+ return (
+
+
+
+ SDK
+
+ Programmatic access to GitAgent via query(),{" "}
+ tool(), and{" "}
+ buildTool().
+
+
+
+ {/* Starting points */}
+
+
+ Pick your starting point
+
+
+ Two paths — both use the same query() function.
+ Full API reference below.
+
+
+
+
Path 1 — With an OpenGAP repo URL
+
+
+ GitAgent clones the repo, reads agent.yaml +{" "}
+ SOUL.md + skills. Zero config — the repo is your agent.
+
+
+
+
Path 2 — Without a repo
+
+
+ Pure SDK — point at an existing directory. No agent repo needed. Ideal for embedding in an app you already have.
+
+
+
+
+
+ {/* A. API Reference heading + Tabbed code display */}
+
+
+ API Reference
+
+
+ {/* Tab bar */}
+
+
+
+
+
+ {tabs.map((tab) => (
+ setActiveTab(tab)}
+ className={`relative px-3 py-1 text-xs font-body rounded transition-colors ${
+ activeTab === tab
+ ? "text-foreground"
+ : "text-muted-foreground hover:text-foreground/70"
+ }`}
+ >
+ {activeTab === tab && (
+
+ )}
+ {tab}
+
+ ))}
+
+
+ {copied ? : }
+
+
+
+
+ {codeMap[activeTab]}
+
+
+
+
+
+ {/* B. QueryOptions */}
+
+ QueryOptions
+ Parameters accepted by query()
+
+ {queryOptions.map((opt, i) => (
+
+
+
+ {opt.name}
+ {"required" in opt && opt.required && (
+ required
+ )}
+ {opt.type}
+
+
{opt.desc}
+
+
+ ))}
+
+
+
+ {/* C. Message Types */}
+
+ Message Types
+ Emitted by the query() async iterator
+
+ {messageTypes.map((m, i) => (
+
+
+
+ {m.type}
+ {m.fields}
+
+
{m.desc}
+
+
+ ))}
+
+
+
+ {/* Cost tracking */}
+
+
+ Cost Tracking
+
+
+
+
+ {/* SDK Cookbooks */}
+
+ SDK Cookbooks
+
+ Production-ready examples showing query() and buildTool() in real scenarios.
+
+
+ {[
+ { label: "Refactor a Repo", desc: "Clone a repo and apply AI-driven refactors with auto-commit", href: "/docs/sdk/cookbooks/refactor-repo" },
+ { label: "Summarize Emails", desc: "Fetch Gmail via custom tool and produce a prioritized digest", href: "/docs/sdk/cookbooks/summarize-emails" },
+ { label: "Code Review PR", desc: "Review a git diff across security, correctness, and performance", href: "/docs/sdk/cookbooks/code-review" },
+ { label: "Custom Tool", desc: "Build Jira + Slack tools with buildTool() and wire them to an agent", href: "/docs/sdk/cookbooks/custom-tool" },
+ { label: "Multi-Agent Handoff", desc: "Chain an auditor agent into a GitHub issue writer agent", href: "/docs/sdk/cookbooks/multi-agent-handoff" },
+ { label: "Scheduled Cron", desc: "Run a standup digest agent every weekday at 9 AM via node-cron", href: "/docs/sdk/cookbooks/scheduled-cron" },
+ ].map((item, i) => (
+
+
+
{item.label} →
+
{item.desc}
+
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentSchedules.tsx b/src/components/gitAgent/GitAgentSchedules.tsx
new file mode 100644
index 0000000..a304966
--- /dev/null
+++ b/src/components/gitAgent/GitAgentSchedules.tsx
@@ -0,0 +1,152 @@
+import { motion } from "framer-motion";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const repeatScheduleYaml = `# schedules/daily-standup.yaml (repeat)
+id: daily-standup
+prompt: "Summarize git commits from the last 24 hours and list open tasks"
+cron: "0 9 * * 1-5"
+mode: repeat
+enabled: true`;
+
+const onceScheduleYaml = `# schedules/quarterly-review.yaml (one-time)
+id: quarterly-review
+prompt: "Generate Q1 performance report"
+mode: once
+runAt: "2026-04-01T09:00:00Z"
+enabled: true`;
+
+const cronPatterns = [
+ { pattern: "0 9 * * 1-5", desc: "Weekdays at 9 AM" },
+ { pattern: "0 9 * * 1", desc: "Every Monday at 9 AM" },
+ { pattern: "0 9 1 * *", desc: "First of month at 9 AM" },
+ { pattern: "0 9 1 */3 *", desc: "Quarterly" },
+ { pattern: "*/30 * * * *", desc: "Every 30 minutes" },
+ { pattern: "0 0 * * *", desc: "Daily at midnight" },
+];
+
+export function GitAgentSchedules() {
+ return (
+
+
+
+ Schedules
+
+ Automate agent runs on a cron schedule or as one-time tasks.
+
+
+
+ {/* A. Modes */}
+
+
+ Schedule Modes
+
+
+ {[
+ { mode: "repeat", how: "cron expression", when: "Run on a recurring schedule (daily, weekly, etc.)" },
+ { mode: "once", how: "runAt ISO datetime", when: "Run exactly one time at a specific date/time" },
+ ].map((row) => (
+
+ {row.mode}
+ {row.how}
+ {row.when}
+
+ ))}
+
+
+ Default is repeat if mode is not specified.
+
+
+
+ {/* B. Schedule definitions */}
+
+
+ Schedule Definitions
+
+
+
+
+
+
+
+
+ {/* B. Cron Patterns */}
+
+
+ Common Cron Patterns
+
+
+ {cronPatterns.map((row, i) => (
+
+
+
+ {row.pattern}
+
+ |
+ {row.desc}
+
+
+ ))}
+
+
+
+ {/* C. UI Management card */}
+
+
+ Web UI Management
+
+
+
+ The Scheduler tab in the web UI lets you manage schedules without editing YAML files. Schedule files live in schedules/ — version-controlled alongside everything else.
+
+
+ {[
+ "Create new schedules with a visual form",
+ "Edit existing schedule prompts and timing",
+ "Enable or disable schedules with a toggle",
+ "Trigger a schedule immediately for testing",
+ "Delete schedules you no longer need",
+ ].map((item, i) => (
+
+ ))}
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentSecurity.tsx b/src/components/gitAgent/GitAgentSecurity.tsx
new file mode 100644
index 0000000..1e7945f
--- /dev/null
+++ b/src/components/gitAgent/GitAgentSecurity.tsx
@@ -0,0 +1,211 @@
+import { motion } from "framer-motion";
+import { Shield } from "lucide-react";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const passwordBashCode = `# Basic auth
+GITAGENT_PASSWORD=mysecret gitagent --voice --dir ~/assistant
+
+# With custom username (defaults to "admin")
+GITAGENT_USERNAME=alice GITAGENT_PASSWORD=mysecret gitagent --voice --dir ~/assistant`;
+
+const sandboxCode = `# Run agent in an E2B cloud sandbox
+gitagent --sandbox --dir ~/assistant
+
+# Clone a remote repo into the sandbox
+gitagent --sandbox --sandbox-repo https://github.com/user/repo --dir ~/assistant`;
+
+const passwordFeatures = [
+ "All HTTP routes show a login page instead of the UI",
+ "WebSocket connections are rejected without valid auth cookie",
+ "/health endpoint remains open (for load balancers)",
+ "Cookie: HttpOnly, SameSite=Strict, 24-hour expiry",
+ "Token is SHA-256 hash (password never stored in cookie)",
+ "GITAGENT_USERNAME sets the login username (defaults to \"admin\")",
+];
+
+const bestPractices = [
+ { title: "Use HTTPS in production", desc: "Via nginx, Caddy, or Cloudflare Tunnel" },
+ { title: "Set GITAGENT_PASSWORD", desc: "When exposing to a network" },
+ { title: "Use --sandbox for untrusted code", desc: "Runs the agent in an isolated E2B cloud VM" },
+ { title: "Enable audit logging", desc: "For compliance and incident review" },
+];
+
+const sandboxPoints = [
+ {
+ title: "Cloud VM isolation",
+ desc: "Agent runs inside an E2B cloud sandbox — fully isolated from your local machine.",
+ },
+ {
+ title: "Filesystem isolation",
+ desc: "The sandbox has its own filesystem. Your host files are not accessible unless explicitly mounted.",
+ },
+ {
+ title: "Remote repo support",
+ desc: "Use --sandbox-repo to clone a repository directly into the sandbox environment.",
+ },
+ {
+ title: "API token required",
+ desc: "Set E2B_API_KEY in your environment — the E2B SDK reads it directly. --sandbox-token is a Git token for cloning the repository (falls back to GITHUB_TOKEN / GIT_TOKEN).",
+ },
+];
+
+export function GitAgentSecurity() {
+ return (
+
+
+
+ Security
+
+ Password protection, best practices, and cloud sandboxing via E2B.
+
+
+
+
+ {/* A. Password Protection */}
+
+
+ Password Protection
+
+
+
+ {passwordFeatures.map((f, i) => (
+
+ —
+ {f}
+
+ ))}
+
+
+
+ {/* B. Best Practices */}
+
+
+ Best Practices
+
+
+ {bestPractices.map((p, i) => (
+
+
+
+ ))}
+
+
+
+
+ {/* C. HTTPS Setup */}
+
+
+ HTTPS Setup
+
+
+
+
nginx reverse proxy
+
+
+
+
Cloudflare Tunnel (zero-config)
+
+
+ Never expose port 3333 directly — always proxy via nginx, Caddy, or Cloudflare Tunnel in production.
+
+
+
+
+
+ {/* D. E2B Sandboxing */}
+
+
+
+
E2B Cloud Sandbox
+
+
+ Run the agent in an isolated E2B cloud VM via the --sandbox flag
+
+
+
+ {sandboxPoints.map((p, i) => (
+
+
+
+ ))}
+
+
+
+ Quick Start
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentSidebar.tsx b/src/components/gitAgent/GitAgentSidebar.tsx
new file mode 100644
index 0000000..71ac967
--- /dev/null
+++ b/src/components/gitAgent/GitAgentSidebar.tsx
@@ -0,0 +1,125 @@
+export const sidebarGroups = [
+ {
+ label: "Getting Started",
+ slug: "getting-started",
+ items: [
+ { id: "overview", label: "Overview" },
+ { id: "interfaces", label: "Ways to Interact" },
+ { id: "architecture", label: "Architecture" },
+ ],
+ },
+ {
+ label: "SDK",
+ slug: "sdk",
+ items: [
+ { id: "quickstart/sdk", label: "Quick Start" },
+ { id: "sdk", label: "SDK Reference" },
+ { id: "utilities", label: "Utilities" },
+ { id: "telemetry", label: "Telemetry" },
+ { id: "cookbooks-divider", label: "SDK Cookbooks", divider: true },
+ { id: "sdk/cookbooks/refactor-repo", label: "Refactor a Repo", depth: 1 },
+ { id: "sdk/cookbooks/summarize-emails", label: "Summarize Emails", depth: 1 },
+ { id: "sdk/cookbooks/code-review", label: "Code Review PR", depth: 1 },
+ { id: "sdk/cookbooks/custom-tool", label: "Custom Tool", depth: 1 },
+ { id: "sdk/cookbooks/multi-agent-handoff", label: "Multi-Agent Handoff", depth: 1 },
+ { id: "sdk/cookbooks/scheduled-cron", label: "Scheduled Cron", depth: 1 },
+ ],
+ },
+ {
+ label: "Personal Assistant",
+ slug: "personal-assistant",
+ items: [
+ { id: "quickstart/personal-assistant", label: "Quick Start" },
+ { id: "cli", label: "CLI" },
+ { id: "webui", label: "Web & Voice" },
+ { id: "messaging", label: "Messaging" },
+ ],
+ },
+ {
+ label: "Configuration",
+ slug: "configuration",
+ items: [
+ { id: "models", label: "Models & Providers" },
+ { id: "env", label: "Environment Variables" },
+ ],
+ },
+ {
+ label: "Capabilities",
+ slug: "capabilities",
+ items: [
+ { id: "tools", label: "Tools" },
+ { id: "skills", label: "Skills" },
+ { id: "workflows", label: "Workflows" },
+ { id: "hooks", label: "Hooks" },
+ { id: "plugins", label: "Plugins" },
+ ],
+ },
+ {
+ label: "Data & Integrations",
+ slug: "data",
+ items: [
+ { id: "memory", label: "Memory System" },
+ { id: "schedules", label: "Schedules & Cron" },
+ { id: "integrations", label: "Integrations" },
+ ],
+ },
+ {
+ label: "Enterprise",
+ slug: "enterprise",
+ items: [
+ { id: "compliance", label: "Compliance & Audit" },
+ { id: "security", label: "Security" },
+ ],
+ },
+];
+
+export function GitAgentSidebar({ activeSection }: { activeSection: string }) {
+ return (
+
+
+
+ Contents
+
+
+ {sidebarGroups.map((group, groupIndex) => (
+
+
+ {group.label}
+
+
+ {group.items.map((s) => {
+ if ("divider" in s && s.divider) {
+ return (
+
+
+ {s.label}
+
+
+ );
+ }
+ const isActive = "href" in s && s.href
+ ? s.href === `/docs/${activeSection}`
+ : activeSection === s.id;
+ return (
+
+
+ {s.label}
+
+
+ );
+ })}
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentSkills.tsx b/src/components/gitAgent/GitAgentSkills.tsx
new file mode 100644
index 0000000..3a736c4
--- /dev/null
+++ b/src/components/gitAgent/GitAgentSkills.tsx
@@ -0,0 +1,213 @@
+import { motion } from "framer-motion";
+import { ArrowDown, Folder, FileText } from "lucide-react";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const skillMdExample = `---
+name: code-review
+description: Review code for bugs, style, and security issues
+---
+
+# Code Review
+
+## Instructions
+
+1. Read the specified file(s) using the read tool
+2. Analyze for:
+ - Bugs and logic errors
+ - Security vulnerabilities (OWASP top 10)
+ - Code style and readability
+ - Performance issues
+3. Write a review report to workspace/review.md
+
+## Output Format
+
+For each issue found:
+- **File**: path
+- **Line**: number
+- **Severity**: critical / warning / info
+- **Description**: what's wrong
+- **Fix**: suggested change`;
+
+const invokeExample = `# In REPL
+/skill:code-review Review the auth module
+
+# In voice/text
+"Use the code-review skill on src/auth.ts"
+
+# Via SDK
+query({ prompt: "/skill:code-review Review src/auth.ts", dir: "./my-agent" })`;
+
+const learningSteps = [
+ {
+ num: "01",
+ title: "Task begins",
+ desc: "task_tracker begins tracking a task",
+ },
+ {
+ num: "02",
+ title: "Task completes",
+ desc: "Agent completes the task successfully",
+ },
+ {
+ num: "03",
+ title: "Evaluation",
+ desc: "skill_learner evaluates if the approach is worth saving",
+ },
+ {
+ num: "04",
+ title: "Crystallization",
+ desc: "If the task passes worthiness checks, crystallizes it as a new skill",
+ },
+ {
+ num: "05",
+ title: "Reuse",
+ desc: "Future tasks search for matching skills",
+ },
+ {
+ num: "06",
+ title: "Feedback loop",
+ desc: "Confidence adjusts based on success/failure outcomes",
+ },
+];
+
+export function GitAgentSkills() {
+ return (
+
+
+ {/* Section heading */}
+
+
+ Skills
+
+
+ Skills are reusable task modules defined in Markdown with YAML frontmatter. The agent
+ learns new skills automatically and crystallizes effective patterns.
+
+
+
+ {/* A. SKILL.md format */}
+
+
+ SKILL.md Format
+
+
+ Auto-generated fields added by the skill learner:
+ "\nlearned_at: ""\nusage_count: 0\nsuccess_count: 0\nfailure_count: 0\nnegative_examples: []`}
+ filename="auto-generated frontmatter"
+ />
+
+
+ {/* B. Invoking Skills */}
+
+
+ Invoking Skills
+
+
+
+
+ {/* C. Skill Learning Workflow */}
+
+
+ Skill Learning Workflow
+
+
+ {learningSteps.map((step, i) => (
+
+
+
+
+ {step.num}
+
+
+
+ {step.title}
+
+
+ {step.desc}
+
+
+
+
+ {i < learningSteps.length - 1 && (
+
+ )}
+
+ ))}
+
+
+
+ {/* D. Skill Directory Structure */}
+
+
+ Skill Directory Structure
+
+
+
+ {[
+ { indent: 0, icon: Folder, name: "skills/", type: "dir" },
+ { indent: 1, icon: Folder, name: "code-review/", type: "dir" },
+ { indent: 2, icon: FileText, name: "SKILL.md", type: "file", note: "skill instructions + frontmatter" },
+ { indent: 2, icon: Folder, name: "scripts/", type: "dir" },
+ { indent: 3, icon: FileText, name: "lint.sh", type: "file", note: "helper scripts" },
+ ].map((item) => {
+ const Icon = item.icon;
+ return (
+
+
+ {item.name}
+ {item.note && (
+
+ ← {item.note}
+
+ )}
+
+ );
+ })}
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentTools.tsx b/src/components/gitAgent/GitAgentTools.tsx
new file mode 100644
index 0000000..f5101ca
--- /dev/null
+++ b/src/components/gitAgent/GitAgentTools.tsx
@@ -0,0 +1,259 @@
+import { motion } from "framer-motion";
+import { Terminal, FileText, FilePlus, Brain, Camera, ListChecks, BookOpen } from "lucide-react";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const builtinTools = [
+ {
+ icon: Terminal,
+ name: "cli",
+ desc: "Execute shell commands",
+ concurrent: false,
+ readOnly: false,
+ },
+ {
+ icon: FileText,
+ name: "read",
+ desc: "Read file contents with pagination",
+ concurrent: true,
+ readOnly: true,
+ },
+ {
+ icon: FilePlus,
+ name: "write",
+ desc: "Create/write files (auto-creates dirs)",
+ concurrent: false,
+ readOnly: false,
+ },
+ {
+ icon: FileText,
+ name: "edit",
+ desc: "Edit existing file contents (find and replace)",
+ concurrent: false,
+ readOnly: false,
+ },
+ {
+ icon: Brain,
+ name: "memory",
+ desc: "Load/save git-committed memory (auto-archives)",
+ concurrent: false,
+ readOnly: false,
+ },
+ {
+ icon: Camera,
+ name: "capture_photo",
+ desc: "Capture camera frame as photo",
+ concurrent: false,
+ readOnly: false,
+ },
+ {
+ icon: ListChecks,
+ name: "task_tracker",
+ desc: "Track task progress, search skills",
+ concurrent: false,
+ readOnly: false,
+ },
+ {
+ icon: BookOpen,
+ name: "skill_learner",
+ desc: "Save/evaluate learned skills with confidence",
+ concurrent: false,
+ readOnly: false,
+ },
+];
+
+const toolDetails = [
+ {
+ name: "cli",
+ rows: [
+ { key: "Timeout", value: "120s (configurable)" },
+ { key: "Output", value: "stdout + stderr (truncated to ~100 KB)" },
+ ],
+ },
+ {
+ name: "read",
+ rows: [
+ { key: "Encoding", value: "utf-8 (binary files return a placeholder message)" },
+ { key: "Partial reads", value: "line offsets (offset = start line, limit = number of lines)" },
+ ],
+ },
+ {
+ name: "write",
+ rows: [
+ { key: "Directories", value: "Auto-creates parent directories" },
+ ],
+ },
+ {
+ name: "memory",
+ rows: [
+ { key: "load", value: "Returns MEMORY.md contents" },
+ { key: "save", value: "Appends + git commits" },
+ { key: "Archive", value: "Auto-archives when max_lines exceeded to memory/archive/.md" },
+ ],
+ },
+];
+
+const declarativeYaml = `# tools/lookup-account.yaml
+name: lookup-account
+description: Look up account details by customer ID
+input_schema:
+ properties:
+ customer_id:
+ type: string
+ description: The customer ID
+ required: [customer_id]
+implementation:
+ script: scripts/lookup.sh
+ runtime: sh`;
+
+export function GitAgentTools() {
+ return (
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentUtilities.tsx b/src/components/gitAgent/GitAgentUtilities.tsx
new file mode 100644
index 0000000..10df021
--- /dev/null
+++ b/src/components/gitAgent/GitAgentUtilities.tsx
@@ -0,0 +1,308 @@
+import { motion } from "framer-motion";
+import { Activity } from "lucide-react";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const compactionCode = `import {
+ estimateTokens,
+ estimateMessageTokens,
+ needsCompaction,
+ truncateToolResults,
+ buildCompactPrompt
+} from "@open-gitagent/opengap";
+
+// Estimate tokens in a string
+const tokens = estimateTokens("Hello world"); // ~3
+
+// Check if compaction needed (triggers at 75% of context window)
+// tokenEstimate is also returned and useful for logging
+const { needed, ratio, tokenEstimate } = needsCompaction(messages, 200000);
+if (needed) console.log(\`Context at \${(ratio * 100).toFixed(0)}% — compaction needed\`);
+
+// Truncate oversized tool results (keeps first + last half)
+const trimmed = truncateToolResults(messages, 10000);
+
+// Build a summarization prompt for the LLM
+const prompt = buildCompactPrompt(messages);`;
+
+const costTrackingCode = `import { query } from "@open-gitagent/opengap";
+
+const result = query({ prompt: "...", dir: "..." });
+
+for await (const msg of result) { /* ... */ }
+
+const costs = result.costs();
+// {
+// startTime: 1716825600000,
+// totalCostUsd: 0.05,
+// totalInputTokens: 5000,
+// totalOutputTokens: 2000,
+// totalRequests: 3,
+// modelUsage: {
+// "anthropic:claude-sonnet-4-6": {
+// inputTokens: 5000,
+// outputTokens: 2000,
+// cacheReadTokens: 0,
+// cacheWriteTokens: 0,
+// totalTokens: 7000,
+// requests: 3,
+// costUsd: 0.05
+// }
+// }
+// }
+console.log(\`Session cost: $\${costs.totalCostUsd.toFixed(4)}\`);`;
+
+const compactionUtils = [
+ { name: "estimateTokens", desc: "Fast approximation: chars/4" },
+ { name: "needsCompaction", desc: "Triggers at 75% of model context window" },
+ { name: "truncateToolResults", desc: "Keeps first and last half of large results" },
+ { name: "buildCompactPrompt", desc: "Generates a prompt asking the LLM to summarize the conversation" },
+];
+
+const otelEnvVars = [
+ { key: "OTEL_EXPORTER_OTLP_ENDPOINT", desc: "OTLP/HTTP collector URL (e.g. http://localhost:4318). When set, telemetry is auto-enabled.", default: "unset = off" },
+ { key: "GITAGENT_OTEL_ENABLED", desc: "Set to false to force-disable telemetry even when endpoint is set.", default: "auto" },
+ { key: "OTEL_SERVICE_NAME", desc: "service.name resource attribute.", default: "gitagent" },
+ { key: "OTEL_SERVICE_VERSION", desc: "service.version resource attribute.", default: "unset" },
+ { key: "OTEL_EXPORTER_OTLP_HEADERS", desc: "Comma-separated key=value pairs, no quotes (e.g. Authorization=Bearer xyz,x-tenant=abc).", default: "unset" },
+ { key: "OTEL_TRACES_EXPORTER", desc: "Set to console to print spans to stdout — no collector needed.", default: "unset" },
+];
+
+const otelSpans = [
+ { name: "gitagent.agent.session", kind: "INTERNAL", attrs: "gitagent.entry, gitagent.cost_usd, gitagent.session.duration_ms" },
+ { name: "gitagent.tool.execute", kind: "INTERNAL", attrs: "tool.name, tool.call_id, tool.status, tool.error_message" },
+ { name: "gen_ai.chat", kind: "CLIENT", attrs: "gen_ai.system, gen_ai.request.model, gen_ai.usage.input_tokens, gen_ai.usage.output_tokens, gitagent.cost_usd" },
+ { name: "HTTP …", kind: "CLIENT", attrs: "URL, status code, duration (auto via instrumentation-undici)" },
+];
+
+const otelMetrics = [
+ { name: "gitagent.tool.calls", type: "counter", desc: "Tool executions, labelled by tool.name" },
+ { name: "gitagent.tool.duration_ms", type: "histogram", desc: "Tool execution duration" },
+ { name: "gitagent.session.duration_ms", type: "histogram", desc: "Session duration" },
+ { name: "gitagent.session.cost_usd", type: "counter", desc: "Cumulative session cost in USD" },
+ { name: "gen_ai.client.token.usage", type: "counter", desc: "Token usage by model and token type" },
+ { name: "gen_ai.client.operation.duration", type: "histogram", desc: "LLM call duration" },
+];
+
+const jaegerSnippet = `docker run -d --name jaeger \\
+ -p 16686:16686 -p 4318:4318 \\
+ jaegertracing/all-in-one:latest
+
+OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 gitagent
+# Open http://localhost:16686`;
+
+export function GitAgentUtilities() {
+ return (
+
+
+
+ Utilities
+
+ Context compaction helpers and cost tracking for programmatic use.
+
+
+
+ {/* A. Context Compaction */}
+
+
+ Context Compaction
+
+
+
+ {compactionUtils.map((u, i) => (
+
+
+ {u.name}
+
+ {u.desc}
+
+ ))}
+
+
+
+ {/* B. Cost Tracking */}
+
+
+ Cost Tracking
+
+
+
+
+
+ );
+}
+
+export function GitAgentTelemetry() {
+ return (
+
+
+
+
+
+ Set OTEL_EXPORTER_OTLP_ENDPOINT and telemetry is on. Leave unset for zero overhead.
+
+
+
+
+ {/* Env vars */}
+
+
+ Environment Variables
+
+
+ {otelEnvVars.map((v, i) => (
+
+
+
+ {v.key}
+
+
+
{v.desc}
+
default: {v.default}
+
+
+
+ ))}
+
+
+
+
+ {/* Spans */}
+
+
+ Spans Emitted
+
+
+ {otelSpans.map((s, i) => (
+
+
+
+ {s.name}
+ {s.kind}
+
+
{s.attrs}
+
+
+ ))}
+
+
+
+ {/* Metrics */}
+
+
+ Metrics Emitted
+
+
+ {otelMetrics.map((m, i) => (
+
+
+
+ {m.name}
+ {m.type}
+
+
{m.desc}
+
+
+ ))}
+
+
+
+
+ {/* Jaeger quickstart */}
+
+
+ Jaeger Quickstart
+
+
+
+
+ {/* Console Output */}
+
+
+ Console Output (No Collector)
+
+
+
+ Prints spans to stdout — no collector needed. Useful for local debugging.
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentVoiceSection.tsx b/src/components/gitAgent/GitAgentVoiceSection.tsx
new file mode 100644
index 0000000..5eed9c1
--- /dev/null
+++ b/src/components/gitAgent/GitAgentVoiceSection.tsx
@@ -0,0 +1,119 @@
+import { motion } from "framer-motion";
+import { Mic, Monitor } from "lucide-react";
+
+const voiceAdapters = [
+ {
+ name: "OpenAI Realtime",
+ model: "gpt-realtime-2025-08-28",
+ tag: "default",
+ requires: "OPENAI_API_KEY",
+ },
+ {
+ name: "Gemini Live",
+ model: "gemini-2.0-flash",
+ tag: "free tier",
+ requires: "GEMINI_API_KEY",
+ },
+];
+
+const webUiTabs = [
+ { name: "Chat", desc: "Voice controls, camera, agent vitals, file system viewer" },
+ { name: "Skills", desc: "Browse and install skills from the marketplace" },
+ { name: "Integrations", desc: "Connect Composio services (Gmail, Calendar, Slack, GitHub)" },
+ { name: "Communication", desc: "Telegram bot setup, WhatsApp connection, phone/SMS webhook" },
+ { name: "SkillFlows", desc: "Visual workflow builder — chain skills into multi-step flows" },
+ { name: "Scheduler", desc: "Create cron jobs — run prompts on a recurring schedule" },
+ { name: "Settings", desc: "Model selection, API keys, custom base URL — saves to .env and agent.yaml" },
+];
+
+
+export function GitAgentVoiceSection() {
+ return (
+
+
+
+
+ 04 — Voice & Web UI
+
+
+ Voice Mode & Web UI
+
+
+ Real-time voice, camera, and a full browser interface at{" "}
+ http://localhost:3333
+
+
+
+
+ {/* Left: voice adapters */}
+
+
+ Voice Adapters
+
+
+ {voiceAdapters.map((v, i) => (
+
+
+ {v.name}
+
+ {v.tag}
+
+
+
+
+ Model
+ {v.model}
+
+
+ Requires
+ {v.requires}
+
+
+
+ ))}
+
+
+
+
+ {/* Right: Web UI tabs */}
+
+
+ Web UI Tabs
+
+
+ {webUiTabs.map((tab, i) => (
+
+
+ {tab.name}
+
+
+ {tab.desc}
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentWebUI.tsx b/src/components/gitAgent/GitAgentWebUI.tsx
new file mode 100644
index 0000000..ead38ac
--- /dev/null
+++ b/src/components/gitAgent/GitAgentWebUI.tsx
@@ -0,0 +1,311 @@
+import { motion } from "framer-motion";
+import {
+ MessageSquare,
+ Zap,
+ Globe,
+ Radio,
+ GitBranch,
+ Clock,
+ Settings,
+ Camera,
+ Mic,
+} from "lucide-react";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const webUITabs = [
+ {
+ icon: MessageSquare,
+ label: "Chat",
+ desc: "Real-time conversation, voice controls, camera, file system viewer",
+ },
+ {
+ icon: Zap,
+ label: "Skills",
+ desc: "Browse and install skills from the marketplace",
+ },
+ {
+ icon: Globe,
+ label: "Integrations",
+ desc: "Connect Composio services (Gmail, Calendar, Slack, GitHub)",
+ },
+ {
+ icon: Radio,
+ label: "Communication",
+ desc: "Telegram bot setup, WhatsApp connection, phone/SMS webhook",
+ },
+ {
+ icon: GitBranch,
+ label: "SkillFlows",
+ desc: "Visual workflow builder — chain skills into multi-step flows",
+ },
+ {
+ icon: Clock,
+ label: "Scheduler",
+ desc: "Create cron jobs — run prompts on a schedule",
+ },
+ {
+ icon: Settings,
+ label: "Settings",
+ desc: "Model selection, API keys, custom base URL — saves to .env and agent.yaml",
+ },
+];
+
+
+const voiceModes = [
+ {
+ provider: "OpenAI Realtime (default)",
+ model: "gpt-realtime-2025-08-28",
+ features: [
+ "Real-time audio streaming over WebSocket",
+ "Supports image input (camera frames)",
+ ],
+ requires: "OPENAI_API_KEY",
+ command: "OPENAI_API_KEY=your_key gitagent --voice --dir ~/assistant",
+ badge: "Default",
+ badgeColor: "bg-primary/15 text-primary",
+ },
+ {
+ provider: "Gemini Live",
+ model: "models/gemini-2.5-flash-native-audio-preview",
+ features: ["Alternative voice provider", "Free tier available"],
+ requires: "GEMINI_API_KEY",
+ command: "GEMINI_API_KEY=your_key gitagent --voice gemini --dir ~/assistant",
+ badge: "Free tier",
+ badgeColor: "bg-green-500/10 text-green-600",
+ },
+];
+
+export function GitAgentWebUI() {
+ return (
+
+
+ {/* Section heading */}
+
+
+ Web UI
+
+
+ A full-featured browser interface served at{" "}
+ http://localhost:3333
+ {" "}— chat, skills, integrations, and voice all in one place.
+
+
+
+ {/* A. Auth / startup */}
+
+
+ Starting the Server
+
+
+
+
+ Auth behaviour
+
+
+ {[
+ "Port is always 3333 — no env var to change it",
+ "All HTTP routes show a login page when GITAGENT_PASSWORD is set",
+ "WebSocket connections are rejected without a valid auth cookie",
+ "/health always stays open (for load balancers)",
+ "Cookie: HttpOnly, SameSite=Strict, 24-hour expiry, SHA-256 token",
+ ].map((item) => (
+
+
+ {item}
+
+ ))}
+
+
+
+
+ {/* B. Web UI Tabs */}
+
+
+ Interface Tabs
+
+
+ {webUITabs.map((tab, i) => {
+ const Icon = tab.icon;
+ return (
+
+
+
+
+ {tab.label}
+
+
+
+ {tab.desc}
+
+
+ );
+ })}
+
+
+
+ {/* C. Voice Mode */}
+
+
+ Voice Mode
+
+
+ {voiceModes.map((mode, i) => (
+
+
+
+
+
+
+ {mode.provider}
+
+
+
+ {mode.badge}
+
+
+
+
+ Model
+
+
+ {mode.model}
+
+
+
+ {mode.features.map((f) => (
+
+
+ {f}
+
+ ))}
+
+
+
+ Requires
+
+
+ {mode.requires}
+
+
+
+
+
+ ))}
+
+
+
+ {/* D. Camera Features + E. Text-Only Fallback — side by side */}
+
+ {/* D. Camera Features */}
+
+
+
+
+
+ Camera Input
+
+
+
+ {[
+ "Front/back camera toggle (mobile)",
+ "Captures frames every 2 seconds as JPEG",
+ "Frames injected into conversation as images",
+ 'Auto-captures on "memorable moments" (laughter, excitement)',
+ ].map((item) => (
+
+
+ {item}
+
+ ))}
+
+
+
+
+ {/* E. Text-Only Fallback */}
+
+
+
+ Text-Only Fallback
+
+
+ No voice API key?
+
+
+ GitAgent still starts the web UI server but with voice disabled. Text input routes
+ directly to the agent via{" "}
+ query().
+
+
+
+ Web UI runs at{" "}
+ http://localhost:3333
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentWhySection.tsx b/src/components/gitAgent/GitAgentWhySection.tsx
new file mode 100644
index 0000000..e5b919a
--- /dev/null
+++ b/src/components/gitAgent/GitAgentWhySection.tsx
@@ -0,0 +1,95 @@
+import { motion } from "framer-motion";
+import { GitBranch, Brain, ExternalLink, ShieldCheck } from "lucide-react";
+
+const pillars = [
+ {
+ icon: GitBranch,
+ label: "Full Auditability",
+ desc: "Every agent decision, memory write, and rule change is a git commit. Know exactly what your agent did, when, and why — full audit trail out of the box, no extra tooling needed.",
+ },
+ {
+ icon: ShieldCheck,
+ label: "Compliance & Control",
+ desc: "Human-in-the-loop enforcement, risk tiers, and regulatory support (SOX, GLBA, GDPR, FINRA) baked into the agent manifest. Your compliance team can review the config like any code PR.",
+ },
+ {
+ icon: Brain,
+ label: "Always Learning",
+ desc: "The agent continuously improves from completed tasks, building a library of reusable skills over time. Every learned skill is inspectable, versioned, and reversible — no black-box retraining.",
+ },
+ {
+ icon: ExternalLink,
+ label: "No Vendor Lock-in",
+ desc: "Built on the OpenGAP open standard. Switch between Claude, OpenAI, Gemini, or any provider by changing one line. Your agent definition lives in git — not a vendor's cloud.",
+ link: "/opengap",
+ linkLabel: "About OpenGAP →",
+ },
+];
+
+export function GitAgentWhySection() {
+ return (
+
+
+
+
+ 02 — Why GitAgent
+
+
+ Why GitAgent
+
+
+
+
+
+ Most agent frameworks scatter configuration across your application and lock your agent inside a framework.{" "}
+ GitAgent flips this — your agent IS the git repo.
+
+
+ Identity, memory, rules, tools, and skills are plain files you already know how to manage. Fork it, branch it, diff it, roll it back — everything a developer already does with code, now applied to agents.
+
+
+
+
+ {pillars.map((p, i) => (
+
+
+
+ {p.desc}
+
+ {p.link && (
+
+ {p.linkLabel}
+
+ )}
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/src/components/gitAgent/GitAgentWorkflows.tsx b/src/components/gitAgent/GitAgentWorkflows.tsx
new file mode 100644
index 0000000..faf74ba
--- /dev/null
+++ b/src/components/gitAgent/GitAgentWorkflows.tsx
@@ -0,0 +1,176 @@
+import { motion } from "framer-motion";
+import { ShieldCheck, ArrowRight, Clock, Users, ListOrdered, MessageSquare } from "lucide-react";
+import { CodeBlock } from "@/components/gitAgent/CodeBlock";
+
+const basicWorkflow = `# workflows/cleanup.md
+---
+name: cleanup
+description: Clean up temporary files
+---
+
+# Cleanup Workflow
+Remove temp files and rebuild.`;
+
+const skillFlowYaml = `# workflows/data-pipeline.yaml
+name: data-pipeline
+description: Process data through validation, transformation, and storage
+steps:
+ - skill: validate-input
+ prompt: "Validate the CSV data format"
+
+ - skill: __approval_gate__
+ prompt: "Data validation complete. Approve to continue?"
+ channel: telegram
+
+ - skill: transform-data
+ prompt: "Transform to required schema"
+
+ - skill: save-to-database
+ prompt: "Store results"`;
+
+const skillFlowFeatures = [
+ {
+ icon: ListOrdered,
+ title: "Deterministic Execution",
+ desc: "Skills run in declared order, not LLM discretion",
+ },
+ {
+ icon: ShieldCheck,
+ title: "Approval Gates",
+ desc: "Pause and require human approval via Telegram/WhatsApp",
+ },
+ {
+ icon: ArrowRight,
+ title: "Multi-step",
+ desc: "Chain multiple skills into a single automated pipeline",
+ },
+ {
+ icon: MessageSquare,
+ title: "Prompt Override",
+ desc: "Add per-step instructions with the prompt: field",
+ },
+];
+
+export function GitAgentWorkflows() {
+ return (
+
+
+ {/* Section heading */}
+
+
+ Workflows
+
+
+ Two workflow formats — human-readable Markdown procedures and executable YAML
+ SkillFlows with built-in approval gates.
+
+
+
+ {/* A + B. Basic Workflow + SkillFlow — side by side */}
+
+ {/* A. Basic Workflow */}
+
+
+ Basic Workflow
+
+
+
+ Reference workflow (.md) — human-readable procedure, agent follows as instructions
+
+
+
+ {/* B. SkillFlow */}
+
+
+ SkillFlow (executable YAML)
+
+
+
+ SkillFlow (.yaml) — deterministic, multi-step, executable pipeline
+
+
+
+
+ {/* C. Approval Gates */}
+
+
+
+
+
+ Approval Gates
+
+
+ Steps with{" "}
+ skill: __approval_gate__
+ {" "}pause execution and send an approval request via the specified channel (
+ telegram or{" "}
+ whatsapp). The user has 5
+ minutes to approve or the step times out. Useful for data-modifying operations,
+ deployments, or any critical path requiring human sign-off.{" "}
+ Note: if neither Telegram nor WhatsApp is configured, the gate auto-approves and execution continues.
+
+
+
+
+
+ {/* D. SkillFlow Features */}
+
+
+ SkillFlow Features
+
+
+ {skillFlowFeatures.map((feat, i) => {
+ const Icon = feat.icon;
+ return (
+
+
+
+
+ {feat.title}
+
+
+ {feat.desc}
+
+
+
+ );
+ })}
+
+
+
+
+ );
+}
diff --git a/src/components/opengap/OpenGAPNavbar.tsx b/src/components/opengap/OpenGAPNavbar.tsx
new file mode 100644
index 0000000..c1a9c64
--- /dev/null
+++ b/src/components/opengap/OpenGAPNavbar.tsx
@@ -0,0 +1,181 @@
+import { useState, useRef, useEffect } from "react";
+import { Menu, X, Search } from "lucide-react";
+import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
+import { opengapSidebarGroups } from "@/components/opengap/OpenGAPSidebar";
+
+const allItems = opengapSidebarGroups.flatMap((g) =>
+ g.items.map((item) => ({ ...item, group: g.label, slug: g.slug }))
+);
+
+interface OpenGAPNavbarProps {
+ variant?: "overview" | "docs";
+}
+
+export function OpenGAPNavbar({ variant = "docs" }: OpenGAPNavbarProps) {
+ const [sheetOpen, setSheetOpen] = useState(false);
+ const [query, setQuery] = useState("");
+ const [searchOpen, setSearchOpen] = useState(false);
+ const searchRef = useRef(null);
+
+ useEffect(() => {
+ function handleClickOutside(e: MouseEvent) {
+ if (searchRef.current && !searchRef.current.contains(e.target as Node)) {
+ setSearchOpen(false);
+ }
+ }
+ document.addEventListener("mousedown", handleClickOutside);
+ return () => document.removeEventListener("mousedown", handleClickOutside);
+ }, []);
+
+ const filtered = query
+ ? allItems.filter((item) =>
+ item.label.toLowerCase().includes(query.toLowerCase()) ||
+ item.group.toLowerCase().includes(query.toLowerCase())
+ )
+ : allItems;
+
+ return (
+
+
+ {/* Left: back + Logo */}
+
+
+ {/* Middle: search — docs variant only, desktop */}
+ {variant === "docs" && (
+
+
+
+ setQuery(e.target.value)}
+ onFocus={() => setSearchOpen(true)}
+ placeholder="Search…"
+ className="text-xs font-body bg-transparent outline-none text-foreground placeholder:text-muted-foreground/40 w-full"
+ />
+ {query && (
+ setQuery("")}
+ className="text-muted-foreground/50 hover:text-foreground transition-colors shrink-0"
+ aria-label="Clear search"
+ >
+
+
+ )}
+
+
+ {searchOpen && (
+
+ {filtered.length === 0 ? (
+
No results
+ ) : (
+
+ )}
+
+ )}
+
+ )}
+
+ {/* Right — desktop */}
+
+
+ {/* Mobile right */}
+
+ {variant === "docs" ? (
+
+
+
+ {sheetOpen ? : }
+
+
+
+
+ Git Agent
+
+
+ {opengapSidebarGroups.map((group, groupIndex) => (
+
+ ))}
+
+
+
+
+ ) : (
+
+ GitHub
+
+ )}
+
+
+
+ );
+}
diff --git a/src/components/opengap/OpenGAPSidebar.tsx b/src/components/opengap/OpenGAPSidebar.tsx
new file mode 100644
index 0000000..a9408fa
--- /dev/null
+++ b/src/components/opengap/OpenGAPSidebar.tsx
@@ -0,0 +1,83 @@
+export const opengapSidebarGroups = [
+ {
+ label: "Getting Started",
+ slug: "getting-started",
+ items: [
+ { id: "overview", label: "Overview" },
+ { id: "quickstart", label: "Quick Start" },
+ ],
+ },
+ {
+ label: "Concepts",
+ slug: "concepts",
+ items: [
+ { id: "why", label: "Why OpenGAP" },
+ { id: "how-it-works", label: "How It Works" },
+ ],
+ },
+ {
+ label: "Features",
+ slug: "features",
+ items: [
+ { id: "cli", label: "CLI" },
+ { id: "export", label: "Export" },
+ { id: "adapters", label: "Adapters" },
+ ],
+ },
+ {
+ label: "Skills",
+ slug: "skills",
+ items: [
+ { id: "skills", label: "Skills" },
+ { id: "skillflow", label: "SkillFlow" },
+ ],
+ },
+ {
+ label: "Enterprise",
+ slug: "enterprise",
+ items: [
+ { id: "compliance", label: "Compliance" },
+ { id: "faq", label: "FAQ" },
+ ],
+ },
+];
+
+export function OpenGAPSidebar({ activeSection }: { activeSection: string }) {
+ return (
+
+
+
+ Contents
+
+
+ {opengapSidebarGroups.map((group, groupIndex) => (
+
+
+ {group.label}
+
+
+ {group.items.map((s) => {
+ const isActive = activeSection === s.id;
+ return (
+
+
+ {s.label}
+
+
+ );
+ })}
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/index.css b/src/index.css
index 587915d..d9468ee 100644
--- a/src/index.css
+++ b/src/index.css
@@ -117,6 +117,22 @@
opacity: 0.4;
}
+ /* Dark hero override — all CSS-variable-based classes auto-adapt */
+ .hero-dark {
+ background-color: hsl(240 28% 5%);
+ --foreground: 220 15% 92%;
+ --card: 240 22% 9%;
+ --card-foreground: 220 15% 92%;
+ --primary: 258 75% 72%;
+ --primary-foreground: 240 28% 5%;
+ --muted: 240 18% 14%;
+ --muted-foreground: 220 10% 54%;
+ --accent: 240 18% 14%;
+ --accent-foreground: 220 15% 88%;
+ --border: 240 18% 20%;
+ --ring: 258 75% 72%;
+ }
+
/* Sketch border effect */
.sketch-border {
border: 1.5px solid hsl(var(--foreground) / 0.25);
@@ -171,3 +187,4 @@
transform: translateY(-5%) scaleY(1);
}
}
+
diff --git a/src/pages/GitAgentDocsPage.tsx b/src/pages/GitAgentDocsPage.tsx
new file mode 100644
index 0000000..4ddb76d
--- /dev/null
+++ b/src/pages/GitAgentDocsPage.tsx
@@ -0,0 +1,152 @@
+import { useEffect } from "react";
+import { useParams } from "react-router-dom";
+import { ChevronUp, ChevronLeft, ChevronRight } from "lucide-react";
+import { GitAgentNavbar } from "@/components/gitAgent/GitAgentNavbar";
+import { GitAgentSidebar } from "@/components/gitAgent/GitAgentSidebar";
+import { GitAgentOverview } from "@/components/gitAgent/GitAgentOverview";
+import { GitAgentArchitecture } from "@/components/gitAgent/GitAgentArchitecture";
+import { GitAgentInterfaces } from "@/components/gitAgent/GitAgentInterfaces";
+import { GitAgentMessaging } from "@/components/gitAgent/GitAgentMessaging";
+import { GitAgentQuickStart } from "@/components/gitAgent/GitAgentQuickStart";
+import { GitAgentQuickStartPersonalAssistant } from "@/components/gitAgent/GitAgentQuickStartPersonalAssistant";
+import { GitAgentQuickStartSDK } from "@/components/gitAgent/GitAgentQuickStartSDK";
+import { GitAgentCookbookRefactorRepo } from "@/components/gitAgent/GitAgentCookbookRefactorRepo";
+import { GitAgentCookbookSummarizeEmails } from "@/components/gitAgent/GitAgentCookbookSummarizeEmails";
+import { GitAgentCookbookCodeReview } from "@/components/gitAgent/GitAgentCookbookCodeReview";
+import { GitAgentCookbookCustomTool } from "@/components/gitAgent/GitAgentCookbookCustomTool";
+import { GitAgentCookbookMultiAgentHandoff } from "@/components/gitAgent/GitAgentCookbookMultiAgentHandoff";
+import { GitAgentCookbookScheduledCron } from "@/components/gitAgent/GitAgentCookbookScheduledCron";
+import { GitAgentCLI } from "@/components/gitAgent/GitAgentCLI";
+import { GitAgentModels } from "@/components/gitAgent/GitAgentModels";
+import { GitAgentWebUI } from "@/components/gitAgent/GitAgentWebUI";
+import { GitAgentTools } from "@/components/gitAgent/GitAgentTools";
+import { GitAgentSkills } from "@/components/gitAgent/GitAgentSkills";
+import { GitAgentWorkflows } from "@/components/gitAgent/GitAgentWorkflows";
+import { GitAgentHooks } from "@/components/gitAgent/GitAgentHooks";
+import { GitAgentPlugins } from "@/components/gitAgent/GitAgentPlugins";
+import { GitAgentMemory } from "@/components/gitAgent/GitAgentMemory";
+import { GitAgentSchedules } from "@/components/gitAgent/GitAgentSchedules";
+import { GitAgentIntegrations } from "@/components/gitAgent/GitAgentIntegrations";
+import { GitAgentCompliance } from "@/components/gitAgent/GitAgentCompliance";
+import { GitAgentSDK } from "@/components/gitAgent/GitAgentSDK";
+import { GitAgentUtilities, GitAgentTelemetry } from "@/components/gitAgent/GitAgentUtilities";
+import { GitAgentSecurity } from "@/components/gitAgent/GitAgentSecurity";
+import { GitAgentEnvVars } from "@/components/gitAgent/GitAgentEnvVars";
+import { sidebarGroups } from "@/components/gitAgent/GitAgentSidebar";
+import { Footer } from "@/components/Footer";
+import { useState } from "react";
+
+const SECTION_COMPONENTS: Record = {
+ overview: GitAgentOverview,
+ quickstart: GitAgentQuickStart,
+ interfaces: GitAgentInterfaces,
+ architecture: GitAgentArchitecture,
+ cli: GitAgentCLI,
+ models: GitAgentModels,
+ env: GitAgentEnvVars,
+ webui: GitAgentWebUI,
+ messaging: GitAgentMessaging,
+ tools: GitAgentTools,
+ skills: GitAgentSkills,
+ workflows: GitAgentWorkflows,
+ hooks: GitAgentHooks,
+ plugins: GitAgentPlugins,
+ memory: GitAgentMemory,
+ schedules: GitAgentSchedules,
+ integrations: GitAgentIntegrations,
+ compliance: GitAgentCompliance,
+ security: GitAgentSecurity,
+ sdk: GitAgentSDK,
+ utilities: GitAgentUtilities,
+ telemetry: GitAgentTelemetry,
+ "quickstart/personal-assistant": GitAgentQuickStartPersonalAssistant,
+ "quickstart/sdk": GitAgentQuickStartSDK,
+ "sdk/cookbooks/refactor-repo": GitAgentCookbookRefactorRepo,
+ "sdk/cookbooks/summarize-emails": GitAgentCookbookSummarizeEmails,
+ "sdk/cookbooks/code-review": GitAgentCookbookCodeReview,
+ "sdk/cookbooks/custom-tool": GitAgentCookbookCustomTool,
+ "sdk/cookbooks/multi-agent-handoff": GitAgentCookbookMultiAgentHandoff,
+ "sdk/cookbooks/scheduled-cron": GitAgentCookbookScheduledCron,
+};
+
+const ALL_ITEMS = sidebarGroups.flatMap((g) => g.items).filter((item) => !("divider" in item && item.divider));
+
+const GitAgentDocsPage = () => {
+ const { section = "overview", subsection, page } = useParams<{ section: string; subsection?: string; page?: string }>();
+ const sectionKey = page ? `${section}/${subsection}/${page}` : subsection ? `${section}/${subsection}` : section;
+ const [showBackToTop, setShowBackToTop] = useState(false);
+
+ const currentIndex = ALL_ITEMS.findIndex((item) => item.id === sectionKey);
+ const prevItem = currentIndex > 0 ? ALL_ITEMS[currentIndex - 1] : null;
+ const nextItem = currentIndex < ALL_ITEMS.length - 1 ? ALL_ITEMS[currentIndex + 1] : null;
+
+ const SectionComponent = SECTION_COMPONENTS[sectionKey] ?? GitAgentOverview;
+ const currentLabel = ALL_ITEMS.find((item) => item.id === sectionKey)?.label ?? "Docs";
+
+ useEffect(() => {
+ document.title = `GitAgent Docs — ${currentLabel}`;
+ window.scrollTo(0, 0);
+ }, [sectionKey, currentLabel]);
+
+ useEffect(() => {
+ const handleScroll = () => setShowBackToTop(window.scrollY > 400);
+ window.addEventListener("scroll", handleScroll, { passive: true });
+ return () => window.removeEventListener("scroll", handleScroll);
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+ {/* Prev / Next navigation */}
+
+
+
+
+
+
+ {showBackToTop && (
+
window.scrollTo({ top: 0, behavior: "smooth" })}
+ className="fixed bottom-6 right-6 z-50 p-2 rounded-md sketch-border bg-background text-muted-foreground hover:text-foreground transition-colors"
+ aria-label="Back to top"
+ >
+
+
+ )}
+
+ );
+};
+
+export default GitAgentDocsPage;
diff --git a/src/pages/GitAgentPage.tsx b/src/pages/GitAgentPage.tsx
new file mode 100644
index 0000000..c2fd7b4
--- /dev/null
+++ b/src/pages/GitAgentPage.tsx
@@ -0,0 +1,121 @@
+import { useEffect, useState } from "react";
+import { ChevronUp } from "lucide-react";
+import { GitAgentNavbar } from "@/components/gitAgent/GitAgentNavbar";
+import { GitAgentHeroSection } from "@/components/gitAgent/GitAgentHeroSection";
+import { GitAgentWhySection } from "@/components/gitAgent/GitAgentWhySection";
+import { GitAgentArchitectureSection } from "@/components/gitAgent/GitAgentArchitectureSection";
+import { GitAgentInterfaces } from "@/components/gitAgent/GitAgentInterfaces";
+import { GitAgentFeaturesSection } from "@/components/gitAgent/GitAgentFeaturesSection";
+import { GitAgentModelsSection } from "@/components/gitAgent/GitAgentModelsSection";
+import { GitAgentMemorySection } from "@/components/gitAgent/GitAgentMemorySection";
+import { GitAgentIntegrationsSection } from "@/components/gitAgent/GitAgentIntegrationsSection";
+import { GitAgentInstallSection } from "@/components/gitAgent/GitAgentInstallSection";
+import { PatternsSection } from "@/components/PatternsSection";
+import { Footer } from "@/components/Footer";
+
+const overviewSections = [
+ { id: "patterns", label: "Patterns" },
+ { id: "why-gitagent", label: "Why" },
+ { id: "interfaces", label: "Interfaces" },
+ { id: "architecture", label: "Architecture" },
+ { id: "features", label: "Features" },
+ { id: "models", label: "Models" },
+ { id: "memory", label: "Memory" },
+ { id: "integrations", label: "Integrations" },
+ { id: "install", label: "Install" },
+];
+
+const GitAgentPage = () => {
+ const [activeSection, setActiveSection] = useState("");
+ const [showPageNav, setShowPageNav] = useState(false);
+ const [showBackToTop, setShowBackToTop] = useState(false);
+
+ useEffect(() => {
+ document.title = "GitAgent — Git-Native AI Agent";
+ }, []);
+
+ useEffect(() => {
+ const handleScroll = () => {
+ const scrollY = window.scrollY;
+ setShowPageNav(scrollY > 300);
+ setShowBackToTop(scrollY > 400);
+ };
+ window.addEventListener("scroll", handleScroll, { passive: true });
+ return () => window.removeEventListener("scroll", handleScroll);
+ }, []);
+
+ useEffect(() => {
+ const observers: IntersectionObserver[] = [];
+ overviewSections.forEach(({ id }) => {
+ const el = document.getElementById(id);
+ if (!el) return;
+ const observer = new IntersectionObserver(
+ ([entry]) => {
+ if (entry.isIntersecting) setActiveSection(id);
+ },
+ { rootMargin: "-20% 0px -70% 0px", threshold: 0 }
+ );
+ observer.observe(el);
+ observers.push(observer);
+ });
+ return () => observers.forEach((o) => o.disconnect());
+ }, []);
+
+ return (
+
+
+
+
+ {/* Sticky secondary page-nav */}
+ {showPageNav && (
+
+
+
+ {overviewSections.map(({ id, label }) => (
+
+ {label}
+
+ ))}
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Back to top */}
+ {showBackToTop && (
+
window.scrollTo({ top: 0, behavior: "smooth" })}
+ className="fixed bottom-6 right-6 z-50 p-2 rounded-md sketch-border bg-background text-muted-foreground hover:text-foreground transition-colors"
+ aria-label="Back to top"
+ >
+
+
+ )}
+
+ );
+};
+
+export default GitAgentPage;
diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx
index c7bc4b1..4a93a14 100644
--- a/src/pages/Index.tsx
+++ b/src/pages/Index.tsx
@@ -1,4 +1,4 @@
-import { Navbar } from "@/components/Navbar";
+import { GitAgentNavbar } from "@/components/gitAgent/GitAgentNavbar";
import { HeroSection } from "@/components/HeroSection";
import { WhySection } from "@/components/WhySection";
import { PatternsSection } from "@/components/PatternsSection";
@@ -34,7 +34,7 @@ const Index = () => {
return (
-
+
{/* spacer for fixed announcement strip */}
;
+import { QuickStartSection } from "@/components/QuickStartSection";
+import { WhySection } from "@/components/WhySection";
+import { HowItWorksSection } from "@/components/HowItWorksSection";
+import { CLISection } from "@/components/CLISection";
+import { ExportSection } from "@/components/ExportSection";
+import { AdaptersSection } from "@/components/AdaptersSection";
+import { SkillsSection } from "@/components/SkillsSection";
+import { SkillsFlowSection } from "@/components/SkillsFlowSection";
+import { ComplianceSection } from "@/components/ComplianceSection";
+import { FAQSection } from "@/components/FAQSection";
+import { Footer } from "@/components/Footer";
+
+const SECTION_COMPONENTS: Record = {
+ overview: HeroSectionDocs,
+ quickstart: QuickStartSection,
+ why: WhySection,
+ "how-it-works": HowItWorksSection,
+ cli: CLISection,
+ export: ExportSection,
+ adapters: AdaptersSection,
+ skills: SkillsSection,
+ skillflow: SkillsFlowSection,
+ compliance: ComplianceSection,
+ faq: FAQSection,
+};
+
+const ALL_ITEMS = opengapSidebarGroups.flatMap((g) => g.items);
+
+const OpenGAPDocsPage = () => {
+ const { section = "overview" } = useParams<{ section: string }>();
+ const [showBackToTop, setShowBackToTop] = useState(false);
+
+ const currentIndex = ALL_ITEMS.findIndex((item) => item.id === section);
+ const prevItem = currentIndex > 0 ? ALL_ITEMS[currentIndex - 1] : null;
+ const nextItem = currentIndex < ALL_ITEMS.length - 1 ? ALL_ITEMS[currentIndex + 1] : null;
+
+ const SectionComponent = SECTION_COMPONENTS[section] ?? HeroSection;
+ const currentLabel = ALL_ITEMS.find((item) => item.id === section)?.label ?? "Overview";
+
+ useEffect(() => {
+ document.title = `OpenGAP — ${currentLabel}`;
+ window.scrollTo(0, 0);
+ }, [section, currentLabel]);
+
+ useEffect(() => {
+ const handleScroll = () => setShowBackToTop(window.scrollY > 400);
+ window.addEventListener("scroll", handleScroll, { passive: true });
+ return () => window.removeEventListener("scroll", handleScroll);
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+ {showBackToTop && (
+
window.scrollTo({ top: 0, behavior: "smooth" })}
+ className="fixed bottom-6 right-6 z-50 p-2 rounded-md sketch-border bg-background text-muted-foreground hover:text-foreground transition-colors"
+ aria-label="Back to top"
+ >
+
+
+ )}
+
+ );
+};
+
+export default OpenGAPDocsPage;