| [submodule "agent-knowledge"] | ||
| path = agent-knowledge | ||
| url = https://github.com/agent-sh/agent-knowledge.git |
+17
-7
@@ -28,4 +28,3 @@ severity = "Warning" | ||
| agents_md = true | ||
| copilot = false # Not targeting Copilot | ||
| cursor = false # Not targeting Cursor | ||
| kiro_agents = false # Kiro agent copies reference agentsys subagents not resolvable from .kiro/ | ||
| prompt_engineering = true | ||
@@ -37,11 +36,22 @@ generic_instructions = true | ||
| disabled_rules = [ | ||
| "XP-003", # Intentional - we document all 3 platform paths (.claude/, .opencode/, .codex/) | ||
| "AGM-003", # docs/reference/AGENTS.md is a comprehensive reference doc | ||
| "AGM-005", # Cursor platform examples are reference content in AGENTS.md | ||
| "AGM-006", # Intentional - root AGENTS.md (instructions) + docs/reference/AGENTS.md (reference) | ||
| "AS-012", # enhance-hooks is a comprehensive hook reference (needs >500 lines) | ||
| "AS-014", # False positives on regex escape sequences (\n, \s, \d) | ||
| "CC-AG-009", # False positive - Skill, AskUserQuestion, LSP are valid Claude Code tools | ||
| "CC-HK-008", # Hook script path resolution differs on Windows (forward slash prefix) | ||
| "CC-MEM-005", # "Be concise" is ecosystem-wide boilerplate | ||
| "CC-MEM-006", # Negative instructions (NEVER/Do not) are ecosystem standard critical rules | ||
| "CC-MEM-009", # Intentional - CLAUDE.md uses structured sections, size is necessary | ||
| "AGM-006", # Intentional - root AGENTS.md (instructions) + docs/reference/AGENTS.md (reference) | ||
| "AGM-003", # docs/reference/AGENTS.md is a comprehensive reference doc | ||
| "CC-SK-009", # enhance-skills contains example injections for teaching purposes | ||
| "CC-SK-012", # Intentional - argument-hint present for UI display | ||
| "CC-SK-017", # Intentional - version is a client-specific frontmatter field | ||
| "CDX-AG-002", # False positive - "tokens" in ecosystem boilerplate is not a secret | ||
| "CDX-AG-005", # Codex agent instruction format - AGENTS.md uses agentsys conventions | ||
| "KR-SK-001", # Kiro skill copies use Claude Code frontmatter fields (version, argument-hint) | ||
| "PE-001", # Reference docs have structured layout; middle placement is intentional | ||
| "AS-012", # enhance-hooks is a comprehensive hook reference (needs >500 lines) | ||
| "CC-SK-009", # enhance-skills contains example injections for teaching purposes | ||
| "PE-005", # Same as CC-MEM-005 for prompt engineering rules | ||
| "XML-001", # False positive - <login-url> is a placeholder in examples | ||
| "XP-003", # Intentional - we document all 3 platform paths (.claude/, .opencode/, .codex/) | ||
| ] | ||
@@ -48,0 +58,0 @@ |
| { | ||
| "name": "agentsys", | ||
| "description": "14 specialized plugins for AI workflow automation - task orchestration, PR workflow, slop detection, code review, drift detection, enhancement analysis, documentation sync, repo mapping, perf investigations, topic research, agent config linting, cross-tool AI consultation, and structured AI debate", | ||
| "version": "5.3.7", | ||
| "description": "15 specialized plugins for AI workflow automation - task orchestration, PR workflow, slop detection, code review, drift detection, enhancement analysis, documentation sync, repo mapping, perf investigations, topic research, agent config linting, cross-tool AI consultation, structured AI debate, and workflow pattern learning", | ||
| "version": "5.4.0", | ||
| "owner": { | ||
@@ -178,4 +178,15 @@ "name": "Avi Fenesh", | ||
| "homepage": "https://github.com/agent-sh/web-ctl" | ||
| }, | ||
| { | ||
| "name": "skillers", | ||
| "source": { | ||
| "source": "url", | ||
| "url": "https://github.com/agent-sh/skillers.git" | ||
| }, | ||
| "description": "Learn from workflow patterns across sessions and suggest skills, hooks, and agents to automate repetitive work", | ||
| "version": "1.0.0", | ||
| "category": "productivity", | ||
| "homepage": "https://github.com/agent-sh/skillers" | ||
| } | ||
| ] | ||
| } |
| { | ||
| "name": "agentsys", | ||
| "version": "5.3.7", | ||
| "version": "5.4.0", | ||
| "description": "Professional-grade slash commands for Claude Code with cross-platform support", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
+4
-4
@@ -153,8 +153,8 @@ # Project Memory: AgentSys | ||
| 43 agents across 14 plugins. Key agents by model: | ||
| 35 agents across 15 plugins. Key agents by model: | ||
| | Model | Agents | Use Case | | ||
| |-------|--------|----------| | ||
| | **opus** | exploration, planning, implementation, perf-orchestrator | Complex reasoning, analysis | | ||
| | **sonnet** | task-discoverer, delivery-validator, ci-fixer, deslop-agent, reporters | Validation, pattern matching | | ||
| | **opus** | exploration, planning, implementation, perf-orchestrator, debate-orchestrator, skillers-recommender | Complex reasoning, analysis | | ||
| | **sonnet** | task-discoverer, delivery-validator, ci-fixer, deslop-agent, reporters, learn, release-agent, skillers-compactor | Validation, pattern matching | | ||
| | **haiku** | worktree-manager, ci-monitor, simple-fixer | Mechanical execution | | ||
@@ -168,3 +168,3 @@ | ||
| 25 skills across plugins. Agents invoke skills for reusable implementation. | ||
| 32 skills across plugins. Agents invoke skills for reusable implementation. | ||
@@ -171,0 +171,0 @@ | Category | Key Skills | |
+21
-0
@@ -10,2 +10,23 @@ # Changelog | ||
| ## [5.4.0] - 2026-03-10 | ||
| ### Added | ||
| - **`/release` command** - Discovery-first release workflow that detects how a repo releases before executing. Supports 12+ ecosystems (npm, cargo, python, go, maven, gradle, ruby, nuget, dart, hex, packagist, swift) and 7 release tool configurations (semantic-release, release-it, goreleaser, changesets, cargo-release, lerna, standard-version). | ||
| - **`/skillers` command** - Transcript-based workflow pattern learning. Analyzes Claude Code conversation history, clusters recurring patterns into weighted themes, and suggests skills/hooks/agents to automate repetitive work. | ||
| - **release-agent** (sonnet) - Discovers release method via tool configs, CI workflows, scripts, and manifests before performing the release. | ||
| - **skillers-compactor** (sonnet) - Extracts observations from conversation transcripts and clusters them into knowledge themes. | ||
| - **skillers-recommender** (opus) - Analyzes accumulated knowledge and classifies patterns as hook/skill/agent recommendations. | ||
| - **Agnix CI validation** - All plugins now run agnix lint in CI pipelines. | ||
| - **agent-knowledge submodule** - Research guides available as a git submodule. | ||
| - **Website additions** - How It Works content for consult, debate, web-ctl tabs. | ||
| ### Fixed | ||
| - **Accurate ecosystem counts** - Stats now show 15 plugins, 35 agents, 32 skills, 3,751 tests, 14 commands. Previously showed inflated/stale counts. | ||
| - **Pinned action SHAs** - Updated to latest stable versions for security. | ||
| - **CodeQL regex** - Fixed inefficient regular expression flagged by code scanning. | ||
| - **Go test fixture** - Added go.mod so CodeQL can analyze Go fixtures. | ||
| - **Website CSS** - Reduced commands section bottom padding, removed inline how-it-works paragraphs. | ||
| ## [5.3.7] - 2026-03-02 | ||
@@ -12,0 +33,0 @@ |
@@ -493,3 +493,5 @@ /** | ||
| if (!codeContent.includes('Promise.all') || !codeContent.includes('Task(')) return fullBlock; | ||
| const taskMatches = [...codeContent.matchAll(/Task\s*\(\s*\{[\s\S]*?subagent_type:\s*['"](?:[^"':]+:)?([^'"]+)['"][\s\S]*?prompt:\s*`((?:[^`]|\$\{[^}]*\})*)`/gs)]; | ||
| // Use lazy [\s\S]*? for the template literal body: it stops at the first backtick and | ||
| // naturally matches any character including standalone $ (e.g. $100, $BUDGET). | ||
| const taskMatches = [...codeContent.matchAll(/Task\s*\(\s*\{[\s\S]*?subagent_type:\s*['"](?:[^"':]+:)?([^'"]+)['"][\s\S]*?prompt:\s*`([\s\S]*?)`/gs)]; | ||
| if (taskMatches.length < 2) return fullBlock; | ||
@@ -496,0 +498,0 @@ |
+1
-1
| { | ||
| "name": "agentsys", | ||
| "version": "5.3.7", | ||
| "version": "5.4.0", | ||
| "description": "A modular runtime and orchestration system for AI agents - works with Claude Code, OpenCode, and Codex CLI", | ||
@@ -5,0 +5,0 @@ "main": "lib/platform/detect-platform.js", |
+46
-5
@@ -24,3 +24,3 @@ <p align="center"> | ||
| <p align="center"> | ||
| <b>14 plugins · 43 agents · 30 skills (across all repos) · 30k lines of lib code · 3,750 tests · 5 platforms</b><br> | ||
| <b>15 plugins · 35 agents · 32 skills (across all repos) · 30k lines of lib code · 3,751 tests · 5 platforms</b><br> | ||
| <em>Plugins distributed as standalone repos under <a href="https://github.com/agent-sh">agent-sh</a> org — agentsys is the marketplace & installer</em> | ||
@@ -51,3 +51,3 @@ </p> | ||
| An agent orchestration system — 14 plugins, 43 agents, and 30 skills that compose into structured pipelines for software development. Each plugin lives in its own standalone repo under the [agent-sh](https://github.com/agent-sh) org. agentsys is the marketplace and installer that ties them together. | ||
| An agent orchestration system — 15 plugins, 35 agents, and 32 skills that compose into structured pipelines for software development. Each plugin lives in its own standalone repo under the [agent-sh](https://github.com/agent-sh) org. agentsys is the marketplace and installer that ties them together. | ||
@@ -109,4 +109,4 @@ Each agent has a single responsibility, a specific model assignment, and defined inputs/outputs. Pipelines enforce phase gates so agents can't skip steps. State persists across sessions so work survives interruptions. | ||
| | [The Approach](#the-approach) | Why it's built this way | | ||
| | [Commands](#commands) | All 13 commands overview | | ||
| | [Skills](#skills) | 30 skills across plugins | | ||
| | [Commands](#commands) | All 14 commands overview | | ||
| | [Skills](#skills) | 32 skills across plugins | | ||
| | [Command Details](#command-details) | Deep dive into each command | | ||
@@ -771,2 +771,43 @@ | [How Commands Work Together](#how-commands-work-together) | Standalone vs integrated | | ||
| ### /release | ||
| > Versioned release with automatic ecosystem and tooling detection | ||
| ```bash | ||
| /release # Patch release (auto-discovers how this repo releases) | ||
| /release minor # Minor version bump | ||
| /release major --dry-run # Preview what would happen | ||
| ``` | ||
| The release agent discovers how your repo releases before executing: | ||
| 1. **Checks for release tools** - semantic-release, release-it, goreleaser, changesets, cargo-release | ||
| 2. **Checks for scripts** - Makefile `release:` target, npm `release` script, `scripts/release.*` | ||
| 3. **Falls back to generic** - Version bump, changelog, tag, push, GitHub release, publish | ||
| Supports 12+ ecosystems: npm, cargo, python, go, maven, gradle, ruby, nuget, dart, hex, packagist, swift. | ||
| **Agent:** release-agent (sonnet model) | ||
| **Skill:** release (generic fallback workflow) | ||
| ### /skillers | ||
| > Learn from your workflow patterns and suggest automations | ||
| ```bash | ||
| /skillers show # Display current config and knowledge stats | ||
| /skillers compact # Analyze recent transcripts, extract patterns | ||
| /skillers compact --days=14 # Analyze older transcripts | ||
| /skillers recommend # Get automation suggestions from accumulated knowledge | ||
| ``` | ||
| Reads your Claude Code conversation transcripts, identifies recurring patterns (pain points, repeated workflows, wishes), clusters them into weighted themes, and suggests skills, hooks, or agents to automate them. | ||
| No per-turn overhead - it reads transcripts that Claude Code already saves. | ||
| **Agents:** skillers-compactor (sonnet), skillers-recommender (opus) | ||
| **Skills:** compact, recommend | ||
| --- | ||
@@ -977,3 +1018,3 @@ | ||
| **Testing:** | ||
| - 3,750 tests passing | ||
| - 3,751 tests passing | ||
| - Drift-detect validated on 1,000+ repositories | ||
@@ -980,0 +1021,0 @@ - E2E workflow testing across all commands |
@@ -648,3 +648,3 @@ /* ========================================================================== | ||
| .commands { | ||
| padding: var(--space-24) var(--space-6); | ||
| padding: var(--space-24) var(--space-6) var(--space-16); | ||
| } | ||
@@ -908,2 +908,40 @@ | ||
| /* -------------------------------------------------------------------------- | ||
| * SCROLL CUE | ||
| * -------------------------------------------------------------------------- */ | ||
| .scroll-cue { | ||
| text-align: center; | ||
| padding: var(--space-8) 0 var(--space-4); | ||
| } | ||
| .scroll-cue__link { | ||
| display: inline-flex; | ||
| flex-direction: column; | ||
| align-items: center; | ||
| gap: var(--space-2); | ||
| color: var(--color-text-tertiary); | ||
| text-decoration: none; | ||
| transition: color 0.2s ease; | ||
| } | ||
| .scroll-cue__link:hover { | ||
| color: var(--color-text-primary); | ||
| } | ||
| .scroll-cue__text { | ||
| font-size: var(--text-sm); | ||
| font-weight: 500; | ||
| letter-spacing: 0.05em; | ||
| text-transform: uppercase; | ||
| } | ||
| .scroll-cue__arrow { | ||
| animation: scroll-bounce 2s ease-in-out infinite; | ||
| } | ||
| @keyframes scroll-bounce { | ||
| 0%, 100% { transform: translateY(0); } | ||
| 50% { transform: translateY(6px); } | ||
| } | ||
| /* -------------------------------------------------------------------------- | ||
| * HOW IT WORKS | ||
@@ -910,0 +948,0 @@ * -------------------------------------------------------------------------- */ |
@@ -661,2 +661,26 @@ /* ========================================================================== | ||
| ] | ||
| }, | ||
| 11: { | ||
| subtitle: 'Ask another AI. Get a second opinion.', | ||
| steps: [ | ||
| { title: 'Detect tools', desc: 'Finds which AI CLI tools are installed on your system. Picks the right model and flags for your effort level.' }, | ||
| { title: 'Run consultation', desc: 'Spawns the tool via ACP (JSON-RPC 2.0 over stdio) or CLI with safe-mode defaults and a 240s timeout.' }, | ||
| { title: 'Return response', desc: 'Parses output, redacts secrets (Shannon entropy > 4.0), and returns the response. Supports session continuations.' } | ||
| ] | ||
| }, | ||
| 12: { | ||
| subtitle: 'Two AIs argue. You get the truth.', | ||
| steps: [ | ||
| { title: 'Set up debate', desc: 'Parses natural language to identify proposer, challenger, topic, rounds, and effort level.' }, | ||
| { title: 'Run rounds', desc: 'Proposer builds a case with evidence. Challenger responds with counterpoints. Each round refines the arguments.' }, | ||
| { title: 'Deliver verdict', desc: 'The orchestrator synthesizes all rounds and picks a winner with reasoning and actionable takeaways.' } | ||
| ] | ||
| }, | ||
| 13: { | ||
| subtitle: 'Browser automation. Encrypted sessions.', | ||
| steps: [ | ||
| { title: 'Start session', desc: 'Creates an encrypted browser profile using AES-256-GCM. No daemon - each action is a single Playwright process.' }, | ||
| { title: 'Authenticate', desc: 'Opens headed Chrome for human login (2FA, CAPTCHAs). Polls for success, then encrypts cookies for reuse.' }, | ||
| { title: 'Run headless', desc: 'Subsequent actions run headless using saved cookies. Snapshot-based element discovery with classified error codes.' } | ||
| ] | ||
| } | ||
@@ -663,0 +687,0 @@ }; |
| { | ||
| "meta": { | ||
| "title": "agentsys", | ||
| "description": "A modular runtime and orchestration system for AI agents. 14 plugins, 43 agents, 30 skills — structured pipelines for Claude Code, OpenCode, Codex CLI, Cursor, and Kiro.", | ||
| "description": "A modular runtime and orchestration system for AI agents. 15 plugins, 35 agents, 32 skills - structured pipelines for Claude Code, OpenCode, Codex CLI, Cursor, and Kiro.", | ||
| "url": "https://agent-sh.github.io/agentsys", | ||
| "repo": "https://github.com/agent-sh/agentsys", | ||
| "npm": "https://www.npmjs.com/package/agentsys", | ||
| "version": "5.3.7", | ||
| "version": "5.4.0", | ||
| "author": "Avi Fenesh", | ||
@@ -46,3 +46,3 @@ "author_url": "https://github.com/avifenesh" | ||
| { | ||
| "value": "3,750", | ||
| "value": "3,751", | ||
| "label": "Tests", | ||
@@ -467,3 +467,3 @@ "suffix": "" | ||
| "knowledge_base": "8,000 lines of curated documentation from Anthropic, OpenAI, Google, and Microsoft", | ||
| "testing": "3,750 tests passing", | ||
| "testing": "3,751 tests passing", | ||
| "drift_detect_repos": "1,000+ repositories validated", | ||
@@ -470,0 +470,0 @@ "token_reduction": "77% fewer tokens for drift-detect vs multi-agent approaches" |
+5
-5
@@ -97,3 +97,3 @@ # AgentSys Site UX Specification | ||
| ### Left Column Content | ||
| 1. **Badge** (top, above title): Small pill showing version or "14 plugins . 43 agents . 30 skills" | ||
| 1. **Badge** (top, above title): Small pill showing version or "15 plugins . 35 agents . 32 skills" | ||
| - Background: `rgba(99, 102, 241, 0.12)`, border: `1px solid rgba(99, 102, 241, 0.25)`, border-radius: 9999px | ||
@@ -108,3 +108,3 @@ - Font: 13px, font-weight 500, primary accent color | ||
| 3. **Subtitle:** "14 plugins, 43 agents, 30 skills. From task selection to merged PR. Works with Claude Code, OpenCode, Codex CLI, Cursor, and Kiro." | ||
| 3. **Subtitle:** "15 plugins, 35 agents, 32 skills. From task selection to merged PR. Works with Claude Code, OpenCode, Codex CLI, Cursor, and Kiro." | ||
| - Font: 18px on desktop, 16px on mobile, font-weight 400, line-height 1.6 | ||
@@ -655,3 +655,3 @@ - Color: `rgba(255, 255, 255, 0.6)` | ||
| <title>AgentSys - Agent Runtime & Orchestration System</title> | ||
| <meta name="description" content="A modular runtime and orchestration system for AI agents. 14 plugins, 43 agents, 30 skills — structured pipelines for Claude Code, OpenCode, Codex CLI, Cursor, and Kiro."> | ||
| <meta name="description" content="A modular runtime and orchestration system for AI agents. 15 plugins, 35 agents, 32 skills — structured pipelines for Claude Code, OpenCode, Codex CLI, Cursor, and Kiro."> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | ||
@@ -662,3 +662,3 @@ <meta name="theme-color" content="#0a0a0f"> | ||
| <meta property="og:title" content="AgentSys"> | ||
| <meta property="og:description" content="AI workflow automation. 14 plugins, 43 agents, 30 skills. Task to merged PR."> | ||
| <meta property="og:description" content="AI workflow automation. 15 plugins, 35 agents, 32 skills. Task to merged PR."> | ||
| <meta property="og:image" content="https://agent-sh.github.io/agentsys/assets/og-image.png"> | ||
@@ -671,3 +671,3 @@ <meta property="og:url" content="https://agent-sh.github.io/agentsys/"> | ||
| <meta name="twitter:title" content="AgentSys"> | ||
| <meta name="twitter:description" content="AI workflow automation. 14 plugins, 43 agents, 30 skills."> | ||
| <meta name="twitter:description" content="AI workflow automation. 15 plugins, 35 agents, 32 skills."> | ||
| <meta name="twitter:image" content="https://agent-sh.github.io/agentsys/assets/og-image.png"> | ||
@@ -674,0 +674,0 @@ ``` |
| # Learning Guide: ACP (Agent Communication Protocol) with Codex, Gemini, Copilot, and Claude | ||
| **Generated**: 2026-03-02 | ||
| **Sources**: 24 resources analyzed | ||
| **Depth**: medium | ||
| --- | ||
| ## Prerequisites | ||
| - Familiarity with at least one AI coding tool (Claude Code, Codex CLI, Gemini CLI, or GitHub Copilot) | ||
| - Basic understanding of HTTP, REST APIs, and JSON-RPC | ||
| - General awareness of the Model Context Protocol (MCP) | ||
| - Understanding of client-server architecture and inter-process communication | ||
| ## TL;DR | ||
| - **ACP** (Agent Communication Protocol) is an open protocol under the Linux Foundation for agent interoperability, using REST endpoints and MIME types. It originated from IBM's BeeAI project and is now merging with Google's **A2A** protocol. | ||
| - **A2A** (Agent-to-Agent) is Google's open protocol using JSON-RPC 2.0 over HTTP(S) with Agent Cards for discovery. It focuses on opaque agent-to-agent collaboration. Over 50 technology partners support it. | ||
| - **MCP** (Model Context Protocol) is Anthropic's open standard for connecting AI to tools and data -- it is complementary to A2A/ACP, not a competitor. MCP handles agent-to-tool communication; A2A handles agent-to-agent communication. | ||
| - **None of the major AI coding CLIs** (Claude Code, Codex CLI, Gemini CLI, Copilot) natively implement ACP or A2A as a built-in protocol. All four support MCP to varying degrees. | ||
| - **Cross-tool AI communication** is possible today via MCP bridges, the Claude Agent SDK, and Claude Code's `mcp serve` feature, but formal agent-to-agent protocols between different vendor tools remain nascent. | ||
| --- | ||
| ## Core Concepts | ||
| ### 1. What is ACP (Agent Communication Protocol)? | ||
| ACP is an open protocol for agent interoperability that addresses the fragmentation challenge in AI development. It enables standardized communication across different agent frameworks and implementations. | ||
| **Key characteristics:** | ||
| - Uses simple, well-defined **REST endpoints** aligned with standard HTTP patterns | ||
| - Employs **MIME types** for content identification, enabling extensibility for any data format | ||
| - Supports synchronous, asynchronous, and streaming interactions | ||
| - Supports both stateful and stateless patterns | ||
| - Provides online and offline agent discovery | ||
| - Framework-agnostic: works across BeeAI, LangChain, CrewAI, and custom implementations | ||
| **Origin and governance:** | ||
| - Originally developed by IBM as part of the **BeeAI** project (now called "Agent Stack") | ||
| - Contributed to the **Linux Foundation AI & Data** program | ||
| - SDKs available in **Python** (`pip install acp-sdk`) and **TypeScript** | ||
| - Reference implementation: Agent Stack (formerly BeeAI), available at github.com/i-am-bee/beeai | ||
| **ACP SDK example (Python):** | ||
| ```python | ||
| from acp_sdk import Server, Client, Message, MessagePart | ||
| # Server side | ||
| server = Server() | ||
| @server.agent() | ||
| async def echo(input: list[Message]): | ||
| """Echoes everything""" | ||
| for message in input: | ||
| yield message | ||
| server.run(port=8000) | ||
| # Client side | ||
| async with Client(base_url="http://localhost:8000") as client: | ||
| run = await client.run_sync( | ||
| agent="echo", | ||
| input=[Message(parts=[MessagePart(content="Hello!")])] | ||
| ) | ||
| print(run) | ||
| ``` | ||
| ### 2. What is Google's A2A (Agent-to-Agent) Protocol? | ||
| A2A is an open protocol contributed by Google to the Linux Foundation, enabling communication and interoperability between opaque agentic applications. | ||
| **Key characteristics:** | ||
| - Uses **JSON-RPC 2.0 over HTTP(S)** as its transport layer | ||
| - Supports synchronous request/response, Server-Sent Events (SSE) streaming, and asynchronous push notifications | ||
| - Data formats: text, files, and structured JSON | ||
| - Designed for **opaque** agents -- no requirement to share internal state, memory, or tool implementations | ||
| **Core concepts:** | ||
| - **Agent Cards**: JSON documents that describe an agent's capabilities, connection info, and supported modalities. Used for discovery. | ||
| - **Tasks**: Units of work with a defined lifecycle, producing artifacts as outputs. | ||
| - **Messages**: Structured communication between client and remote agents. | ||
| - **Opacity principle**: Agents collaborate without exposing internal memory, proprietary logic, or specific tool implementations. | ||
| **Announced**: April 9, 2025 by Google. Over 50 technology partners including Atlassian, Salesforce, SAP, ServiceNow, Langchain, MongoDB, and many major consulting firms. | ||
| **SDKs**: Python, Go, JavaScript, Java, .NET (available via `pip install a2a-sdk` for Python). | ||
| **Licensed**: Apache 2.0 under the Linux Foundation. | ||
| ### 3. ACP vs A2A: Convergence | ||
| ACP and A2A are converging under the Linux Foundation. As of early 2026: | ||
| - ACP's website (agentcommunicationprotocol.dev) describes ACP as "now part of A2A" | ||
| - Agent Stack (BeeAI) has adopted A2A as its interoperability protocol | ||
| - Both protocols share similar goals but had different transport choices (REST for ACP, JSON-RPC for A2A) | ||
| - The merged effort means new implementations should target the A2A specification | ||
| ### 4. MCP (Model Context Protocol) -- The Complement | ||
| MCP is Anthropic's open standard for connecting AI applications to external systems (tools, data sources, workflows). It is explicitly **not** an agent-to-agent protocol. | ||
| **Key distinction:** | ||
| - **MCP**: Agent-to-tool communication (connecting AI to databases, APIs, file systems) | ||
| - **A2A/ACP**: Agent-to-agent communication (connecting AI agents to each other) | ||
| As Google's A2A announcement stated: "A2A complements Anthropic's Model Context Protocol (MCP), which provides helpful tools and context to agents." | ||
| **MCP architecture:** | ||
| - Client-server model with stdio, SSE, and HTTP transports | ||
| - Servers expose tools, resources, and prompts | ||
| - Clients (AI applications) consume these capabilities | ||
| - Supported by Claude Code, Codex CLI, Gemini CLI, Copilot, Kiro, Cursor, and many others | ||
| ### 5. Protocol Comparison | ||
| | Feature | ACP | A2A | MCP | | ||
| |---------|-----|-----|-----| | ||
| | **Purpose** | Agent-to-agent interop | Agent-to-agent interop | Agent-to-tool integration | | ||
| | **Transport** | REST/HTTP | JSON-RPC 2.0 over HTTP(S) | stdio, SSE, HTTP | | ||
| | **Discovery** | Agent registry | Agent Cards (JSON) | Server configuration | | ||
| | **Streaming** | Yes | Yes (SSE) | Yes (SSE, streamable HTTP) | | ||
| | **Async** | Yes | Yes (push notifications) | Limited | | ||
| | **Creator** | IBM / Linux Foundation | Google / Linux Foundation | Anthropic | | ||
| | **Status** | Merging into A2A | Active, growing ecosystem | Mature, widely adopted | | ||
| | **Opacity** | Framework-agnostic | Fully opaque agents | N/A (tool exposure) | | ||
| | **SDKs** | Python, TypeScript | Python, Go, JS, Java, .NET | Python, TypeScript, + more | | ||
| | **When to use** | Legacy ACP deployments | Agent orchestration across vendors | Connecting AI to tools/data | | ||
| --- | ||
| ## How Each AI Coding Tool Relates to ACP/A2A | ||
| ### Claude Code (Anthropic) | ||
| **ACP/A2A support**: No native ACP or A2A protocol implementation. | ||
| **What it does support:** | ||
| - **MCP** (full support): Claude Code can both consume MCP servers and act as one (`claude mcp serve`) | ||
| - **Subagents**: Specialized sub-agents within a single Claude Code session (Explore, Plan, custom agents) | ||
| - **Agent Teams** (experimental): Multiple Claude Code instances coordinating via shared task lists, mailbox messaging, and direct inter-agent communication | ||
| - **Agent SDK**: Python and TypeScript SDKs for building production agents with Claude Code's tools programmatically | ||
| **Cross-tool bridging:** | ||
| - Claude Code can serve as an MCP server, letting other tools invoke Claude's capabilities: | ||
| ```bash | ||
| claude mcp serve | ||
| ``` | ||
| - The Agent SDK enables programmatic spawning of Claude agents that can interact with MCP servers from any provider | ||
| - MCP bridge servers (community-built) can connect Claude to other AI model APIs | ||
| **Agent Teams architecture:** | ||
| - Team lead coordinates work via shared task list | ||
| - Teammates communicate via a mailbox messaging system | ||
| - Each teammate has its own context window | ||
| - Supports plan approval workflows | ||
| - Experimental; requires `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` | ||
| ### OpenAI Codex CLI | ||
| **ACP/A2A support**: No native ACP or A2A protocol implementation. | ||
| **What it does support:** | ||
| - **MCP**: Basic MCP support via `shell-tool-mcp` component and MCP Registry integration | ||
| - **Multi-provider**: Configurable to work with various AI providers (OpenAI, Azure, Gemini, Ollama, etc.) | ||
| - **AGENTS.md**: Supports instruction files that merge from global to directory-level context | ||
| **Architecture:** | ||
| - Primarily Rust-based (96.1%), operates as a single-agent tool | ||
| - Three approval modes: Suggest, Auto Edit, Full Auto | ||
| - Sandboxed execution using Apple Seatbelt (macOS) or Docker (Linux) | ||
| - No multi-agent coordination or agent-to-agent communication mechanisms documented | ||
| **Cross-tool bridging:** | ||
| - Can be configured to use different LLM providers, but this is provider-switching, not agent-to-agent communication | ||
| - MCP server support enables external tool integration | ||
| ### Google Gemini CLI | ||
| **ACP/A2A support**: No native A2A protocol implementation despite Google creating A2A. | ||
| **What it does support:** | ||
| - **MCP**: Explicit MCP support for custom integrations | ||
| - **Google Search grounding**: Built-in real-time information access | ||
| - **GitHub integration**: Dedicated GitHub Action for automated workflows | ||
| - **Non-interactive mode**: JSON and streaming output for automation and scripting | ||
| **Notable absence:** | ||
| Gemini CLI documentation contains no references to A2A or ACP support. This is notable because Google created the A2A protocol. The A2A protocol appears designed for enterprise agent orchestration scenarios rather than developer CLI tools. | ||
| ### GitHub Copilot | ||
| **ACP/A2A support**: No native ACP or A2A protocol implementation. | ||
| **What it does support:** | ||
| - **MCP**: Full MCP support through Copilot Extensions in VS Code, JetBrains, and other IDEs | ||
| - **GitHub MCP Server**: Dedicated server for code tasks, coding agent invocation, and code scanning | ||
| - **Extensions ecosystem**: Third-party tool integrations via MCP | ||
| - **Push protection**: Security features for AI-generated code | ||
| **Organization controls:** | ||
| - MCP server policy management for Copilot Business/Enterprise | ||
| - Allowlist/denylist controls for MCP servers | ||
| ### Kiro (AWS) | ||
| **ACP/A2A support**: No documented ACP or A2A support. | ||
| **What it does support:** | ||
| - **MCP**: Full MCP server support with configuration UI | ||
| - **Agentic features**: Specs, hooks, steering, and agentic chat | ||
| - Built on Claude (AWS product) | ||
| - MCP server management with connection status indicators | ||
| --- | ||
| ## Cross-AI-Tool Communication Patterns | ||
| ### Pattern 1: MCP Bridge Servers | ||
| Community-built MCP servers that bridge to other AI providers: | ||
| | Server | Description | Source | | ||
| |--------|-------------|--------| | ||
| | `mcp-server-gemini-bridge` | Bridge to Google Gemini API (Pro, Flash) | jaspertvdm (GitHub) | | ||
| | `mcp-server-openai-bridge` | Bridge to OpenAI API (GPT-4, GPT-4o) | jaspertvdm (GitHub) | | ||
| | `mcp-server-ollama-bridge` | Bridge to local Ollama LLM server | jaspertvdm (GitHub) | | ||
| | `Grok-MCP` | Access to xAI's Grok API | merterbak (GitHub) | | ||
| | `blockrun-mcp` | Access 30+ AI models without API keys | blockrunai (GitHub) | | ||
| **Example: Using Claude Code to consult Gemini via MCP bridge:** | ||
| ```bash | ||
| # Add Gemini bridge as MCP server in Claude Code | ||
| claude mcp add --transport stdio gemini-bridge -- npx -y mcp-server-gemini-bridge | ||
| # Then in Claude Code, the Gemini bridge tools become available | ||
| # Claude can call Gemini for a second opinion on code | ||
| ``` | ||
| ### Pattern 2: Agent-to-Agent MCP Servers | ||
| Dedicated MCP servers for agent discovery and communication: | ||
| | Server | Description | | ||
| |--------|-------------| | ||
| | `agentnet` | Agent-to-agent referral network for AI agent discovery and recommendation | | ||
| | `prolink` | Agent-to-agent marketplace middleware with MCP-native discovery and negotiation | | ||
| | `hashnet-mcp-js` | Registry Broker for discovering, registering, and chatting with AI agents | | ||
| | `agenium` | Network protocol enabling agent discovery via `agent://` URIs with mTLS trust | | ||
| ### Pattern 3: Claude Code as MCP Server | ||
| Claude Code can expose its tools as an MCP server for other applications: | ||
| ```bash | ||
| # Start Claude Code as an MCP server | ||
| claude mcp serve | ||
| ``` | ||
| **Configuration for Claude Desktop:** | ||
| ```json | ||
| { | ||
| "mcpServers": { | ||
| "claude-code": { | ||
| "type": "stdio", | ||
| "command": "claude", | ||
| "args": ["mcp", "serve"], | ||
| "env": {} | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| This means any MCP client (Kiro, Cursor, Gemini CLI, etc.) could potentially use Claude Code's tools. | ||
| ### Pattern 4: Claude Agent SDK for Programmatic Orchestration | ||
| The Claude Agent SDK enables building custom multi-agent systems: | ||
| ```python | ||
| import asyncio | ||
| from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition | ||
| async def main(): | ||
| async for message in query( | ||
| prompt="Use the researcher agent to analyze this codebase", | ||
| options=ClaudeAgentOptions( | ||
| allowed_tools=["Read", "Glob", "Grep", "Task"], | ||
| agents={ | ||
| "researcher": AgentDefinition( | ||
| description="Research specialist for code analysis", | ||
| prompt="Analyze code quality and architecture patterns.", | ||
| tools=["Read", "Glob", "Grep"], | ||
| ) | ||
| }, | ||
| mcp_servers={ | ||
| "gemini": {"command": "npx", "args": ["mcp-server-gemini-bridge"]} | ||
| } | ||
| ), | ||
| ): | ||
| if hasattr(message, "result"): | ||
| print(message.result) | ||
| asyncio.run(main()) | ||
| ``` | ||
| ### Pattern 5: Sequential Thinking MCP Server | ||
| The Sequential Thinking MCP server enables structured reasoning across tool calls: | ||
| ```json | ||
| { | ||
| "mcpServers": { | ||
| "sequential-thinking": { | ||
| "command": "npx", | ||
| "args": ["-y", "@modelcontextprotocol/server-sequential-thinking"] | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| **How it works:** | ||
| - Decomposes problems into sequential reasoning steps | ||
| - Supports revision of previous thoughts | ||
| - Enables branching into alternative reasoning paths | ||
| - Dynamically adjusts the total thought count | ||
| - Useful for complex problems where the full scope is not initially clear | ||
| **Parameters:** | ||
| - `thought` (string): Current reasoning step | ||
| - `nextThoughtNeeded` (boolean): Whether continuation is needed | ||
| - `thoughtNumber` / `totalThoughts` (integer): Position tracking | ||
| - `isRevision` / `revisesThought`: For reconsidering previous thinking | ||
| - `branchFromThought` / `branchId`: For alternative reasoning paths | ||
| This is not cross-AI communication per se, but enables more sophisticated reasoning within a single AI tool. | ||
| --- | ||
| ## Common Pitfalls | ||
| | Pitfall | Why It Happens | How to Avoid | | ||
| |---------|---------------|--------------| | ||
| | Confusing ACP with A2A | They were separate protocols now merging | Target A2A for new implementations; ACP is being absorbed | | ||
| | Expecting AI CLIs to support A2A natively | A2A is designed for enterprise agent orchestration, not developer CLI tools | Use MCP bridges for cross-tool communication today | | ||
| | Treating MCP as an agent-to-agent protocol | MCP is for tool integration, not agent collaboration | Use A2A/ACP for agent-to-agent; MCP for agent-to-tool | | ||
| | Assuming Gemini CLI supports A2A | Google created A2A but hasn't built it into Gemini CLI | Gemini CLI uses MCP; A2A is separate infrastructure | | ||
| | Using MCP bridges in production without security review | Community MCP bridges are educational examples | Implement proper authentication, rate limiting, and input validation | | ||
| | Over-engineering with agent teams | Agent teams consume significantly more tokens | Use subagents for focused tasks; teams only when inter-agent coordination is needed | | ||
| | Expecting ACP SDK to be stable | ACP is merging into A2A | Monitor Linux Foundation announcements for migration guidance | | ||
| --- | ||
| ## Best Practices | ||
| 1. **Use MCP as the universal adapter layer**: All major AI coding tools support MCP. Build MCP servers to create cross-tool capabilities. (Sources: MCP docs, Claude Code docs, Gemini CLI docs, Codex CLI docs) | ||
| 2. **Target A2A for new agent-to-agent work**: ACP is merging into A2A. New implementations should use the A2A SDK. (Source: agentcommunicationprotocol.dev, A2A GitHub) | ||
| 3. **Use Claude Code's `mcp serve` for lightweight bridging**: Expose Claude's tools to any MCP-compatible client without building custom infrastructure. (Source: Claude Code MCP documentation) | ||
| 4. **Leverage the Claude Agent SDK for complex orchestration**: When you need programmatic control over multi-agent workflows with MCP integration. (Source: Claude Agent SDK overview) | ||
| 5. **Start with subagents before agent teams**: Subagents have lower token costs and simpler coordination. Use teams only when agents need to communicate with each other. (Source: Claude Code subagents and agent teams docs) | ||
| 6. **Keep cross-AI bridges stateless**: MCP bridge servers should not maintain conversation state across calls. Each invocation should be self-contained. (Source: MCP server best practices) | ||
| 7. **Use Agent Stack (BeeAI) for A2A reference implementations**: Agent Stack is the official reference implementation for A2A-compatible agent deployment. (Source: Agent Stack GitHub) | ||
| 8. **Monitor the A2A specification evolution**: The protocol is actively evolving with new features planned for Agent Card authorization, credential management, and enhanced discovery. (Source: A2A GitHub repository) | ||
| --- | ||
| ## Current Maturity Assessment | ||
| | Aspect | Maturity Level | Notes | | ||
| |--------|---------------|-------| | ||
| | **MCP ecosystem** | Mature | Hundreds of servers, supported by all major tools | | ||
| | **A2A specification** | Growing | 50+ partners, SDKs in 5 languages, under Linux Foundation | | ||
| | **ACP specification** | Transitioning | Merging into A2A; existing ACP deployments should plan migration | | ||
| | **Cross-CLI agent communication** | Nascent | No AI CLI natively implements A2A; MCP bridges are community-built | | ||
| | **Claude Code agent teams** | Experimental | Functional but with known limitations; disabled by default | | ||
| | **MCP bridge servers** | Early | Community-built, not production-hardened | | ||
| | **Agent Stack (BeeAI) production readiness** | Developing | 1000+ GitHub stars, modular architecture, but still evolving | | ||
| | **Sequential Thinking MCP** | Stable | Part of official MCP reference servers | | ||
| --- | ||
| ## Real-World Cross-AI Communication Examples | ||
| ### Example 1: Claude Code consulting Gemini via MCP bridge | ||
| ```bash | ||
| # Setup | ||
| claude mcp add --transport stdio gemini -- npx -y mcp-server-gemini-bridge | ||
| # In Claude Code session: | ||
| # "Get Gemini's perspective on the best approach for this database migration" | ||
| # Claude calls the Gemini MCP bridge tool, gets Gemini's response, synthesizes both perspectives | ||
| ``` | ||
| ### Example 2: Multi-agent research with Claude Agent SDK | ||
| ```python | ||
| # A lead agent delegates research to subagents, each with different MCP servers | ||
| from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition | ||
| async def multi_source_research(): | ||
| async for msg in query( | ||
| prompt="Research best practices for API rate limiting. " | ||
| "Use the researcher agent to analyze our codebase " | ||
| "and the web-researcher to find current best practices.", | ||
| options=ClaudeAgentOptions( | ||
| allowed_tools=["Read", "Glob", "Grep", "Task", "WebSearch"], | ||
| agents={ | ||
| "researcher": AgentDefinition( | ||
| description="Codebase analysis specialist", | ||
| prompt="Analyze the existing codebase for patterns.", | ||
| tools=["Read", "Glob", "Grep"], | ||
| ), | ||
| "web-researcher": AgentDefinition( | ||
| description="Web research specialist", | ||
| prompt="Search the web for current best practices.", | ||
| tools=["WebSearch", "WebFetch"], | ||
| ) | ||
| } | ||
| ), | ||
| ): | ||
| if hasattr(msg, "result"): | ||
| print(msg.result) | ||
| ``` | ||
| ### Example 3: A2A-compatible agent via Agent Stack | ||
| ```python | ||
| # Deploy an agent that is automatically A2A-compatible | ||
| # Using Agent Stack (BeeAI) | ||
| # 1. Define your agent in any framework (LangGraph, CrewAI, custom) | ||
| # 2. Deploy via Agent Stack CLI | ||
| # 3. Agent is automatically exposed as an A2A-compatible service | ||
| # Other A2A-compatible agents can discover and communicate with it | ||
| # via Agent Cards and JSON-RPC 2.0 | ||
| ``` | ||
| ### Example 4: Claude Code agent team for parallel investigation | ||
| ```text | ||
| # In Claude Code with agent teams enabled: | ||
| I need to investigate why our API is slow. Create an agent team: | ||
| - One teammate to profile the database queries | ||
| - One teammate to analyze the application logs | ||
| - One teammate to check the network latency metrics | ||
| Have them share findings and build a unified diagnosis. | ||
| ``` | ||
| --- | ||
| ## Open Source Implementations | ||
| ### A2A Protocol | ||
| - **A2A SDKs**: Python, Go, JavaScript, Java, .NET (github.com/google/A2A) | ||
| - **Agent Stack**: Reference A2A implementation (github.com/i-am-bee/beeai) | ||
| - **DeepLearning.AI Course**: Official course on building A2A-compliant agents | ||
| ### ACP Protocol (Legacy/Transitioning) | ||
| - **ACP SDK (Python)**: `pip install acp-sdk` -- v1.0.3, maintained by IBM (pypi.org/project/acp-sdk) | ||
| - **ACP SDK (TypeScript)**: Available via npm | ||
| ### MCP Ecosystem | ||
| - **Official MCP servers**: github.com/modelcontextprotocol/servers | ||
| - **Sequential Thinking**: `@modelcontextprotocol/server-sequential-thinking` | ||
| - **AI Bridge servers**: Community-built bridges to Gemini, OpenAI, Ollama, Grok | ||
| ### Claude Agent SDK | ||
| - **Python**: `pip install claude-agent-sdk` (github.com/anthropics/claude-agent-sdk-python) | ||
| - **TypeScript**: `npm install @anthropic-ai/claude-agent-sdk` | ||
| - **Demo agents**: github.com/anthropics/claude-agent-sdk-demos | ||
| --- | ||
| ## Further Reading | ||
| | Resource | Type | Why Recommended | | ||
| |----------|------|-----------------| | ||
| | [A2A Protocol Specification](https://a2a-protocol.org/) | Spec | Official A2A specification and documentation | | ||
| | [ACP Website](https://agentcommunicationprotocol.dev/) | Docs | ACP protocol overview (now merging into A2A) | | ||
| | [Agent Stack (BeeAI)](https://github.com/i-am-bee/beeai) | Code | Reference A2A implementation with full agent infrastructure | | ||
| | [MCP Introduction](https://modelcontextprotocol.io/introduction) | Docs | Official MCP documentation and architecture | | ||
| | [Claude Code MCP Guide](https://code.claude.com/docs/en/mcp) | Docs | Comprehensive guide to MCP in Claude Code | | ||
| | [Claude Code Subagents](https://code.claude.com/docs/en/sub-agents) | Docs | Creating and managing specialized AI subagents | | ||
| | [Claude Code Agent Teams](https://code.claude.com/docs/en/agent-teams) | Docs | Multi-agent coordination with shared tasks and messaging | | ||
| | [Claude Agent SDK](https://platform.claude.com/docs/en/agent-sdk/overview) | Docs | Programmatic access to Claude Code's capabilities | | ||
| | [Claude Agent SDK Demos](https://github.com/anthropics/claude-agent-sdk-demos) | Code | Example agents including multi-agent research pattern | | ||
| | [Codex CLI](https://github.com/openai/codex) | Code | OpenAI's coding agent with MCP support | | ||
| | [Gemini CLI](https://github.com/google-gemini/gemini-cli) | Code | Google's terminal AI with MCP support | | ||
| | [Google A2A Blog Post](https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/) | Blog | Google's announcement and vision for A2A | | ||
| | [ACP SDK (Python)](https://pypi.org/project/acp-sdk/) | Package | Python SDK for building ACP agents | | ||
| | [MCP Servers Repository](https://github.com/modelcontextprotocol/servers) | Code | Official MCP reference servers including Sequential Thinking | | ||
| | [Awesome MCP Servers](https://github.com/punkpeye/awesome-mcp-servers) | List | Curated list of MCP servers including AI bridges | | ||
| --- | ||
| *This guide was synthesized from 24 sources. See `resources/acp-with-codex-gemini-copilot-claude-sources.json` for full source list with quality scores.* |
| # Agent Knowledge Base | ||
| > Learning guides created by /learn. Reference these when answering questions about listed topics. | ||
| ## Available Topics | ||
| | Topic | File | Sources | Depth | Created | | ||
| |-------|------|---------|-------|---------| | ||
| | AI CLI Non-Interactive & Programmatic Usage | ai-cli-non-interactive-programmatic-usage.md | 52 | deep | 2026-02-10 | | ||
| | AI CLI Advanced Integration (MCP, SDKs, Cross-Tool) | ai-cli-advanced-integration-patterns.md | 38 | deep | 2026-02-10 | | ||
| | Skill/Plugin Distribution Patterns | skill-plugin-distribution-patterns.md | 40 | deep | 2026-02-21 | | ||
| | All-in-One Plus Modular Packages | all-in-one-plus-modular-packages.md | 40 | deep | 2026-02-21 | | ||
| | GitHub Org Project Management (Multi-Repo OSS) | github-org-project-management.md | 15 | deep | 2026-02-21 | | ||
| | GitHub Organization Structure Patterns | github-org-structure-patterns.md | 18 | deep | 2026-02-21 | | ||
| | Multi-Product Org Docs & Website Architecture | multi-product-org-docs.md | 42 | deep | 2026-02-21 | | ||
| | ACP with Codex, Gemini, Copilot, Claude | acp-with-codex-gemini-copilot-claude.md | 24 | medium | 2026-03-02 | | ||
| ## Trigger Phrases | ||
| Use this knowledge when user asks about: | ||
| - "How to use claude -p" -> ai-cli-non-interactive-programmatic-usage.md | ||
| - "Non-interactive AI CLI" -> ai-cli-non-interactive-programmatic-usage.md | ||
| - "Claude Code programmatic" -> ai-cli-non-interactive-programmatic-usage.md | ||
| - "Gemini CLI headless" -> ai-cli-non-interactive-programmatic-usage.md | ||
| - "Codex CLI quiet mode" -> ai-cli-non-interactive-programmatic-usage.md | ||
| - "OpenCode non-interactive" -> ai-cli-non-interactive-programmatic-usage.md | ||
| - "AI CLI session management" -> ai-cli-non-interactive-programmatic-usage.md | ||
| - "Claude Agent SDK" -> ai-cli-non-interactive-programmatic-usage.md | ||
| - "Consultant pattern AI" -> ai-cli-non-interactive-programmatic-usage.md | ||
| - "Piping to AI CLI" -> ai-cli-non-interactive-programmatic-usage.md | ||
| - "Structured output from AI CLI" -> ai-cli-non-interactive-programmatic-usage.md | ||
| - "AI in CI/CD" -> ai-cli-non-interactive-programmatic-usage.md | ||
| - "claude mcp serve" -> ai-cli-advanced-integration-patterns.md | ||
| - "MCP server mode" -> ai-cli-advanced-integration-patterns.md | ||
| - "AI CLI MCP integration" -> ai-cli-advanced-integration-patterns.md | ||
| - "Gemini CLI extensions" -> ai-cli-advanced-integration-patterns.md | ||
| - "Codex MCP" -> ai-cli-advanced-integration-patterns.md | ||
| - "Crush OpenCode" -> ai-cli-advanced-integration-patterns.md | ||
| - "Agent SDK hosting" -> ai-cli-advanced-integration-patterns.md | ||
| - "AI CLI server mode" -> ai-cli-advanced-integration-patterns.md | ||
| - "Claude Code as service" -> ai-cli-advanced-integration-patterns.md | ||
| - "Plugin distribution patterns" -> skill-plugin-distribution-patterns.md | ||
| - "npm vs git submodules vs vendoring" -> skill-plugin-distribution-patterns.md | ||
| - "How to distribute CLI plugins" -> skill-plugin-distribution-patterns.md | ||
| - "Plugin registry architecture" -> skill-plugin-distribution-patterns.md | ||
| - "Extension marketplace design" -> skill-plugin-distribution-patterns.md | ||
| - "asdf mise plugin model" -> skill-plugin-distribution-patterns.md | ||
| - "Terraform provider registry" -> skill-plugin-distribution-patterns.md | ||
| - "Homebrew taps" -> skill-plugin-distribution-patterns.md | ||
| - "oh-my-zsh plugin system" -> skill-plugin-distribution-patterns.md | ||
| - "Plugin contract design" -> skill-plugin-distribution-patterns.md | ||
| - "Small ecosystem plugin strategy" -> skill-plugin-distribution-patterns.md | ||
| - "Convention-based plugin discovery" -> skill-plugin-distribution-patterns.md | ||
| - "monorepo packages" -> all-in-one-plus-modular-packages.md | ||
| - "meta-package pattern" -> all-in-one-plus-modular-packages.md | ||
| - "batteries included but removable" -> all-in-one-plus-modular-packages.md | ||
| - "npm workspaces" -> all-in-one-plus-modular-packages.md | ||
| - "pnpm workspaces" -> all-in-one-plus-modular-packages.md | ||
| - "Lerna monorepo" -> all-in-one-plus-modular-packages.md | ||
| - "Changesets versioning" -> all-in-one-plus-modular-packages.md | ||
| - "modular package architecture" -> all-in-one-plus-modular-packages.md | ||
| - "scoped npm packages" -> all-in-one-plus-modular-packages.md | ||
| - "plugin architecture" -> all-in-one-plus-modular-packages.md | ||
| - "CLI installer pattern" -> all-in-one-plus-modular-packages.md | ||
| - "degit tiged scaffolding" -> all-in-one-plus-modular-packages.md | ||
| - "dual CJS ESM" -> all-in-one-plus-modular-packages.md | ||
| - "package exports map" -> all-in-one-plus-modular-packages.md | ||
| - "independent versioning" -> all-in-one-plus-modular-packages.md | ||
| - "GitHub releases distribution" -> all-in-one-plus-modular-packages.md | ||
| - "AWS SDK v3 modular" -> all-in-one-plus-modular-packages.md | ||
| - "lodash per-method packages" -> all-in-one-plus-modular-packages.md | ||
| - "Babel plugin architecture" -> all-in-one-plus-modular-packages.md | ||
| - "Turborepo Nx Rush" -> all-in-one-plus-modular-packages.md | ||
| - "GitHub Projects v2" -> github-org-project-management.md | ||
| - "org-level project management" -> github-org-project-management.md | ||
| - "multi-repo GitHub board" -> github-org-project-management.md | ||
| - "GitHub Issue Types" -> github-org-project-management.md | ||
| - "GitHub sub-issues" -> github-org-project-management.md | ||
| - "GitHub roadmap project" -> github-org-project-management.md | ||
| - "addProjectV2ItemById" -> github-org-project-management.md | ||
| - "actions/add-to-project" -> github-org-project-management.md | ||
| - "Kubernetes SIG model" -> github-org-project-management.md | ||
| - "RFC process GitHub" -> github-org-project-management.md | ||
| - "cross-repo issue tracking" -> github-org-project-management.md | ||
| - "GitHub Projects automation" -> github-org-project-management.md | ||
| - "GitHub org structure" -> github-org-structure-patterns.md | ||
| - "GitHub .github repo" -> github-org-structure-patterns.md | ||
| - "org community health files" -> github-org-structure-patterns.md | ||
| - "reusable GitHub Actions workflows" -> github-org-structure-patterns.md | ||
| - "CODEOWNERS working groups" -> github-org-structure-patterns.md | ||
| - "GitHub org profile README" -> github-org-structure-patterns.md | ||
| - "developer tool org layout" -> github-org-structure-patterns.md | ||
| - "open source org patterns" -> github-org-structure-patterns.md | ||
| - "multi-product docs" -> multi-product-org-docs.md | ||
| - "documentation architecture" -> multi-product-org-docs.md | ||
| - "docs site structure" -> multi-product-org-docs.md | ||
| - "Docusaurus multi-instance" -> multi-product-org-docs.md | ||
| - "Starlight Astro docs" -> multi-product-org-docs.md | ||
| - "Mintlify docs" -> multi-product-org-docs.md | ||
| - "VitePress multi-sidebar" -> multi-product-org-docs.md | ||
| - "developer portal" -> multi-product-org-docs.md | ||
| - "plugin catalog page" -> multi-product-org-docs.md | ||
| - "marketplace page design" -> multi-product-org-docs.md | ||
| - "docs SEO multi-product" -> multi-product-org-docs.md | ||
| - "hub and spoke docs" -> multi-product-org-docs.md | ||
| - "unified docs vs separate sites" -> multi-product-org-docs.md | ||
| - "llms.txt AI discoverability" -> multi-product-org-docs.md | ||
| - "GitHub Pages org site docs" -> multi-product-org-docs.md | ||
| - "HashiCorp developer portal" -> multi-product-org-docs.md | ||
| - "Supabase docs structure" -> multi-product-org-docs.md | ||
| - "agent-sh website" -> multi-product-org-docs.md | ||
| - "agnix docs separate site" -> multi-product-org-docs.md | ||
| - "plugin catalog generation" -> multi-product-org-docs.md | ||
| - "cross-product navigation" -> multi-product-org-docs.md | ||
| - "product switcher docs" -> multi-product-org-docs.md | ||
| - "Diataxis framework" -> multi-product-org-docs.md | ||
| - "Pagefind search" -> multi-product-org-docs.md | ||
| - "docs-as-code" -> multi-product-org-docs.md | ||
| - "ACP protocol" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "Agent Communication Protocol" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "A2A protocol" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "agent to agent communication" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "cross AI tool communication" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "MCP vs ACP vs A2A" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "Codex CLI agent protocol" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "Gemini CLI agent protocol" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "Copilot agent communication" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "Claude Code agent teams" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "Claude Agent SDK multi-agent" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "sequential thinking MCP" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "MCP bridge server" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "BeeAI Agent Stack" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "Kiro ACP" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "Cross-tool MCP bridges" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "ACP SDK" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "A2A SDK" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "agent interoperability" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "Agent Cards A2A" -> acp-with-codex-gemini-copilot-claude.md | ||
| - "claude mcp serve cross-tool" -> acp-with-codex-gemini-copilot-claude.md | ||
| ## Quick Lookup | ||
| | Keyword | Guide | | ||
| |---------|-------| | ||
| | claude -p, headless, non-interactive | ai-cli-non-interactive-programmatic-usage.md | | ||
| | gemini cli, gemini -p | ai-cli-non-interactive-programmatic-usage.md | | ||
| | codex cli, codex -q | ai-cli-non-interactive-programmatic-usage.md | | ||
| | opencode, opencode -p | ai-cli-non-interactive-programmatic-usage.md | | ||
| | agent sdk, claude-agent-sdk | ai-cli-non-interactive-programmatic-usage.md | | ||
| | session, resume, continue | ai-cli-non-interactive-programmatic-usage.md | | ||
| | json output, structured output | ai-cli-non-interactive-programmatic-usage.md | | ||
| | mcp, model context protocol | ai-cli-non-interactive-programmatic-usage.md | | ||
| | subprocess, spawn, piping | ai-cli-non-interactive-programmatic-usage.md | | ||
| | consultant pattern | ai-cli-non-interactive-programmatic-usage.md | | ||
| | claude mcp serve, mcp server | ai-cli-advanced-integration-patterns.md | | ||
| | gemini extensions, a2a protocol | ai-cli-advanced-integration-patterns.md | | ||
| | codex shell-tool-mcp | ai-cli-advanced-integration-patterns.md | | ||
| | crush, opencode successor | ai-cli-advanced-integration-patterns.md | | ||
| | agent sdk hosting, containers | ai-cli-advanced-integration-patterns.md | | ||
| | cross-tool mcp, tool integration | ai-cli-advanced-integration-patterns.md | | ||
| | remote agents, long-running | ai-cli-advanced-integration-patterns.md | | ||
| | plugin distribution, extension marketplace | skill-plugin-distribution-patterns.md | | ||
| | npm packages, vendoring, git submodules | skill-plugin-distribution-patterns.md | | ||
| | terraform registry, homebrew taps | skill-plugin-distribution-patterns.md | | ||
| | asdf plugins, mise backends | skill-plugin-distribution-patterns.md | | ||
| | plugin contract, plugin protocol | skill-plugin-distribution-patterns.md | | ||
| | oh-my-zsh, zsh plugins, vim plugins | skill-plugin-distribution-patterns.md | | ||
| | vscode extensions, grafana plugins | skill-plugin-distribution-patterns.md | | ||
| | small ecosystem, plugin discovery | skill-plugin-distribution-patterns.md | | ||
| | monorepo, workspaces, multi-package | all-in-one-plus-modular-packages.md | | ||
| | lerna, changesets, turborepo, nx, rush | all-in-one-plus-modular-packages.md | | ||
| | meta-package, re-export, barrel | all-in-one-plus-modular-packages.md | | ||
| | lodash, aws-sdk, babel, eslint, angular | all-in-one-plus-modular-packages.md | | ||
| | plugin architecture, core + plugins | all-in-one-plus-modular-packages.md | | ||
| | degit, tiged, create-react-app, scaffolding | all-in-one-plus-modular-packages.md | | ||
| | npm publish, semantic-release, np | all-in-one-plus-modular-packages.md | | ||
| | CJS, ESM, dual module, exports map | all-in-one-plus-modular-packages.md | | ||
| | cargo workspaces, go modules, pip extras | all-in-one-plus-modular-packages.md | | ||
| | github releases, binary distribution | all-in-one-plus-modular-packages.md | | ||
| | github projects v2, projects board | github-org-project-management.md | | ||
| | issue types, sub-issues, github hierarchy | github-org-project-management.md | | ||
| | cross-repo tracking, org project | github-org-project-management.md | | ||
| | graphql addProjectV2ItemById | github-org-project-management.md | | ||
| | actions/add-to-project, auto-add | github-org-project-management.md | | ||
| | kubernetes SIG, KEP, enhancement proposals | github-org-project-management.md | | ||
| | rust RFC, working groups | github-org-project-management.md | | ||
| | astro roadmap, withastro | github-org-project-management.md | | ||
| | github public roadmap, shipped label | github-org-project-management.md | | ||
| | iteration fields, sprint planning github | github-org-project-management.md | | ||
| | label sync, org labels | github-org-project-management.md | | ||
| | .github repo, org defaults, community health | github-org-structure-patterns.md | | ||
| | CODEOWNERS, working groups, wg-tauri | github-org-structure-patterns.md | | ||
| | reusable workflows, workflow_call | github-org-structure-patterns.md | | ||
| | org profile README, pinned repos | github-org-structure-patterns.md | | ||
| | bun org, deno org, astro org | github-org-structure-patterns.md | | ||
| | AI contribution policy, AI disclosure | github-org-structure-patterns.md | | ||
| | just commands, justfile | github-org-structure-patterns.md | | ||
| | starter workflows, workflow-templates | github-org-structure-patterns.md | | ||
| | github pages org site | github-org-structure-patterns.md | | ||
| | verified domain github | github-org-structure-patterns.md | | ||
| | multi-product docs, documentation architecture | multi-product-org-docs.md | | ||
| | Docusaurus, Starlight, Mintlify, VitePress | multi-product-org-docs.md | | ||
| | developer portal, docs site structure | multi-product-org-docs.md | | ||
| | plugin catalog, marketplace page | multi-product-org-docs.md | | ||
| | docs SEO, llms.txt, AI discoverability | multi-product-org-docs.md | | ||
| | hub-and-spoke, unified portal | multi-product-org-docs.md | | ||
| | HashiCorp, Supabase, Vercel docs | multi-product-org-docs.md | | ||
| | product switcher, cross-product navigation | multi-product-org-docs.md | | ||
| | Diataxis, docs-as-code | multi-product-org-docs.md | | ||
| | Pagefind, Algolia DocSearch | multi-product-org-docs.md | | ||
| | agent-sh website, agnix docs | multi-product-org-docs.md | | ||
| | GitBook, ReadMe, Nextra | multi-product-org-docs.md | | ||
| | GitHub Pages docs hosting | multi-product-org-docs.md | | ||
| | Cloudflare Pages, Vercel hosting | multi-product-org-docs.md | | ||
| | ACP, Agent Communication Protocol | acp-with-codex-gemini-copilot-claude.md | | ||
| | A2A, Agent2Agent, agent-to-agent | acp-with-codex-gemini-copilot-claude.md | | ||
| | ACP vs MCP vs A2A | acp-with-codex-gemini-copilot-claude.md | | ||
| | BeeAI, Agent Stack, agent interop | acp-with-codex-gemini-copilot-claude.md | | ||
| | agent teams, subagents, multi-agent | acp-with-codex-gemini-copilot-claude.md | | ||
| | MCP bridge, Gemini bridge, OpenAI bridge | acp-with-codex-gemini-copilot-claude.md | | ||
| | sequential thinking, cross-AI reasoning | acp-with-codex-gemini-copilot-claude.md | | ||
| | Agent Cards, agent discovery | acp-with-codex-gemini-copilot-claude.md | | ||
| | claude mcp serve, claude as MCP server | acp-with-codex-gemini-copilot-claude.md | | ||
| | acp-sdk, a2a-sdk | acp-with-codex-gemini-copilot-claude.md | | ||
| ## How to Use | ||
| 1. Check if user question matches a topic | ||
| 2. Read the relevant guide file | ||
| 3. Answer based on synthesized knowledge | ||
| 4. Cite the guide if user asks for sources |
| # Learning Guide: Advanced AI CLI Integration - MCP Servers, SDKs, and Cross-Tool Patterns | ||
| **Generated**: 2026-02-10 | ||
| **Sources**: 38 resources analyzed | ||
| **Depth**: deep | ||
| **Prerequisite**: [ai-cli-non-interactive-programmatic-usage.md](./ai-cli-non-interactive-programmatic-usage.md) | ||
| ## TL;DR | ||
| - **Claude Code** is the most integration-ready: runs AS an MCP server (`claude mcp serve`), has a full Agent SDK (Python/TypeScript), supports custom subagents, MCP client with 100+ servers, session forking, and containerized hosting. | ||
| - **Gemini CLI** supports MCP as a client (stdio/http/sse), has an extension system for packaging MCP servers + commands + hooks, supports remote agents via A2A protocol, and has a tools API for custom tool registration. No server mode or SDK. | ||
| - **Codex CLI** has MCP support via `shell-tool-mcp` (acts as MCP client, not server), a Rust rewrite (`codex-rs`) underway, and a TypeScript SDK at `sdk/typescript/`. No server mode. | ||
| - **OpenCode** is archived (Sept 2025), succeeded by **Crush** (charmbracelet/crush). Crush supports MCP (stdio/http/sse), agent skills, LSP integration, and multi-provider support. No server mode or SDK. | ||
| **Bottom line**: For cross-tool consultation, Claude Code is the best "callee" because `claude mcp serve` lets other tools connect to it natively via MCP protocol, not just CLI subprocess calls. | ||
| --- | ||
| ## Table of Contents | ||
| 1. [MCP Server Capabilities](#mcp-server-capabilities) | ||
| 2. [MCP Client Capabilities](#mcp-client-capabilities) | ||
| 3. [SDK / Programmatic Library Access](#sdk--programmatic-library-access) | ||
| 4. [Extension / Plugin Systems](#extension--plugin-systems) | ||
| 5. [Remote Agents & A2A Protocol](#remote-agents--a2a-protocol) | ||
| 6. [Server / Daemon / Long-Running Patterns](#server--daemon--long-running-patterns) | ||
| 7. [Cross-Tool Integration Matrix](#cross-tool-integration-matrix) | ||
| 8. [Best Integration Patterns by Use Case](#best-integration-patterns-by-use-case) | ||
| 9. [Detailed Tool Deep-Dives](#detailed-tool-deep-dives) | ||
| --- | ||
| ## MCP Server Capabilities | ||
| Can the tool **run AS an MCP server** that other tools connect to? | ||
| | Tool | Runs as MCP Server? | Command | What It Exposes | | ||
| |------|---------------------|---------|-----------------| | ||
| | **Claude Code** | Yes | `claude mcp serve` | Read, Edit, Write, Bash, Glob, Grep tools | | ||
| | **Gemini CLI** | No | N/A | N/A | | ||
| | **Codex CLI** | No (has `shell-tool-mcp` server) | `npx @openai/codex-shell-tool-mcp` | Sandboxed shell execution only | | ||
| | **OpenCode/Crush** | No | N/A | N/A | | ||
| ### Claude Code as MCP Server (Key Feature) | ||
| Claude Code can expose its full tool suite to any MCP client: | ||
| ```bash | ||
| # Start Claude Code as an MCP server (stdio transport) | ||
| claude mcp serve | ||
| ``` | ||
| **Use in Claude Desktop:** | ||
| ```json | ||
| { | ||
| "mcpServers": { | ||
| "claude-code": { | ||
| "type": "stdio", | ||
| "command": "claude", | ||
| "args": ["mcp", "serve"], | ||
| "env": {} | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| **Use from any MCP client (including other AI CLIs):** | ||
| Any tool that supports MCP can connect to Claude Code's tools. This means Gemini CLI, through its extension system, could theoretically connect to a Claude Code MCP server and use Claude's Read/Edit/Bash tools. | ||
| **What gets exposed:** | ||
| - File operations: Read, Write, Edit, Glob, Grep | ||
| - Shell execution: Bash | ||
| - Search: WebSearch, WebFetch | ||
| **Important caveat**: The MCP server only exposes Claude Code's *tools* -- the calling client is responsible for its own AI model and tool approval flow. This is different from calling Claude Code's AI via `-p`. | ||
| ### Codex shell-tool-mcp | ||
| Codex provides `@openai/codex-shell-tool-mcp` -- a specialized MCP server for sandboxed shell execution: | ||
| ```toml | ||
| # ~/.codex/config.toml | ||
| [features] | ||
| shell_tool = false | ||
| [mcp_servers.shell-tool] | ||
| command = "npx" | ||
| args = ["-y", "@openai/codex-shell-tool-mcp"] | ||
| ``` | ||
| **What it does:** | ||
| - Intercepts `execve()` system calls at kernel level | ||
| - Enforces `.rules` file policies (allow/prompt/forbidden) | ||
| - Provides sandboxed shell within Codex's security model | ||
| - Codex acts as MCP **client**, not server | ||
| --- | ||
| ## MCP Client Capabilities | ||
| Can the tool **connect TO external MCP servers**? | ||
| | Tool | MCP Client? | Transport Types | Config Method | | ||
| |------|-------------|-----------------|---------------| | ||
| | **Claude Code** | Yes (deep) | stdio, http, sse | `claude mcp add`, `.mcp.json`, `--mcp-config` | | ||
| | **Gemini CLI** | Yes (via extensions) | stdio, http, sse | `settings.json`, extensions | | ||
| | **Codex CLI** | Yes (limited) | stdio | `config.toml` | | ||
| | **Crush** (OpenCode successor) | Yes | stdio, http, sse | Config file | | ||
| ### Claude Code MCP Client | ||
| The most comprehensive MCP client: | ||
| ```bash | ||
| # Add an HTTP MCP server | ||
| claude mcp add --transport http notion https://mcp.notion.com/mcp | ||
| # Add a stdio MCP server | ||
| claude mcp add --transport stdio db -- npx -y @bytebase/dbhub --dsn "postgres://..." | ||
| # Add with authentication | ||
| claude mcp add --transport http sentry https://mcp.sentry.dev/mcp | ||
| # Then: /mcp to authenticate via OAuth | ||
| # Load from config file (useful for programmatic invocation) | ||
| claude -p "query" --mcp-config ./mcp.json | ||
| # Strict mode: ONLY use specified MCP servers | ||
| claude -p "query" --strict-mcp-config --mcp-config ./mcp.json | ||
| # In Agent SDK (Python) | ||
| async for message in query( | ||
| prompt="Open example.com", | ||
| options=ClaudeAgentOptions( | ||
| mcp_servers={ | ||
| "playwright": {"command": "npx", "args": ["@playwright/mcp@latest"]} | ||
| } | ||
| ) | ||
| ): | ||
| ... | ||
| ``` | ||
| **Key features:** | ||
| - OAuth 2.0 authentication for remote servers | ||
| - Scopes: local (default), project (`.mcp.json`, git-checked), user (global) | ||
| - Tool Search: auto-loads tools on-demand when many MCP servers configured | ||
| - MCP resources via `@server:protocol://path` references | ||
| - MCP prompts as `/mcp__server__prompt` commands | ||
| - Dynamic `list_changed` notifications | ||
| - Managed MCP for enterprise (allowlists/denylists) | ||
| ### Gemini CLI MCP Client | ||
| Via the extension system and `settings.json`: | ||
| ```json | ||
| // ~/.gemini/settings.json | ||
| { | ||
| "mcpServers": { | ||
| "github": { | ||
| "command": "npx", | ||
| "args": ["-y", "@modelcontextprotocol/server-github"], | ||
| "env": { | ||
| "GITHUB_TOKEN": "${GITHUB_TOKEN}" | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| **Via extensions (bundled MCP servers):** | ||
| ```json | ||
| // gemini-extension.json | ||
| { | ||
| "name": "my-extension", | ||
| "mcpServers": { | ||
| "db": { | ||
| "command": "${extensionPath}/servers/db-server", | ||
| "args": ["--config", "${extensionPath}/config.json"] | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| **Key features:** | ||
| - MCP servers loaded at startup | ||
| - Multiple servers prefix tool names: `serverAlias__toolName` | ||
| - Extension-bundled MCP servers auto-start | ||
| - Variable substitution: `${extensionPath}`, `${workspacePath}` | ||
| ### Crush (OpenCode Successor) MCP Client | ||
| ```yaml | ||
| # Configuration supports all three transports | ||
| mcpServers: | ||
| filesystem: | ||
| type: stdio | ||
| command: "npx" | ||
| args: ["-y", "@modelcontextprotocol/server-filesystem"] | ||
| env: | ||
| ALLOWED_DIRS: "/home/user/projects" | ||
| remote-api: | ||
| type: http | ||
| url: "https://api.example.com/mcp" | ||
| timeout: 30 | ||
| ``` | ||
| --- | ||
| ## SDK / Programmatic Library Access | ||
| Can you use the tool as an **importable library** (not just CLI subprocess)? | ||
| | Tool | Has SDK? | Languages | Package | | ||
| |------|----------|-----------|---------| | ||
| | **Claude Code** | Yes (Agent SDK) | Python, TypeScript | `claude-agent-sdk`, `@anthropic-ai/claude-agent-sdk` | | ||
| | **Gemini CLI** | No SDK | N/A | N/A (has Tools API for extensions) | | ||
| | **Codex CLI** | Partial (TypeScript SDK) | TypeScript | `sdk/typescript/` in repo | | ||
| | **Crush** | No SDK | N/A | N/A | | ||
| ### Claude Agent SDK (Most Mature) | ||
| The Agent SDK provides the same tools, agent loop, and context management as Claude Code CLI: | ||
| **Python:** | ||
| ```python | ||
| pip install claude-agent-sdk | ||
| from claude_agent_sdk import query, ClaudeAgentOptions | ||
| async for message in query( | ||
| prompt="Find and fix bugs in auth.py", | ||
| options=ClaudeAgentOptions( | ||
| allowed_tools=["Read", "Edit", "Bash"], | ||
| permission_mode="acceptEdits", | ||
| # Session management | ||
| resume="session-id", | ||
| fork_session=True, | ||
| # Structured output | ||
| output_format={"type": "json_schema", "schema": {...}}, | ||
| # Custom subagents | ||
| agents={ | ||
| "reviewer": AgentDefinition( | ||
| description="Code reviewer", | ||
| prompt="Review code quality", | ||
| tools=["Read", "Grep", "Glob"] | ||
| ) | ||
| }, | ||
| # MCP servers | ||
| mcp_servers={ | ||
| "db": {"command": "npx", "args": ["-y", "@bytebase/dbhub"]} | ||
| }, | ||
| # Hooks | ||
| hooks={ | ||
| "PostToolUse": [HookMatcher(matcher="Edit|Write", hooks=[log_change])] | ||
| } | ||
| ) | ||
| ): | ||
| if hasattr(message, "result"): | ||
| print(message.result) | ||
| ``` | ||
| **TypeScript:** | ||
| ```typescript | ||
| npm install @anthropic-ai/claude-agent-sdk | ||
| import { query } from "@anthropic-ai/claude-agent-sdk"; | ||
| for await (const message of query({ | ||
| prompt: "Find and fix bugs in auth.py", | ||
| options: { | ||
| allowedTools: ["Read", "Edit", "Bash"], | ||
| permissionMode: "acceptEdits", | ||
| resume: sessionId, | ||
| forkSession: true, | ||
| outputFormat: { type: "json_schema", schema: mySchema }, | ||
| agents: { reviewer: { description: "...", prompt: "...", tools: [...] } }, | ||
| mcpServers: { db: { command: "npx", args: [...] } } | ||
| } | ||
| })) { | ||
| if ("result" in message) console.log(message.result); | ||
| } | ||
| ``` | ||
| **Key SDK capabilities beyond CLI:** | ||
| - Async iterator streaming | ||
| - Programmatic hooks (callbacks, not shell scripts) | ||
| - Type-safe structured outputs (Zod/Pydantic) | ||
| - Native message objects with metadata | ||
| - Session management (resume, fork) | ||
| - Custom subagent definitions | ||
| - Tool approval callbacks (`canUseTool`) | ||
| ### Gemini CLI Tools API (Extension-Level) | ||
| Not an importable SDK, but Gemini CLI's core exposes a Tools API for custom tool registration: | ||
| ```typescript | ||
| // BaseTool interface (within Gemini CLI extensions) | ||
| interface BaseTool { | ||
| name: string; | ||
| displayName: string; | ||
| description: string; | ||
| parameterSchema: JSONSchema; | ||
| execute(params: any, signal: AbortSignal): Promise<ToolResult>; | ||
| validateToolParams(params: any): boolean; | ||
| shouldConfirmExecute(params: any): boolean; | ||
| } | ||
| // ToolResult | ||
| interface ToolResult { | ||
| llmContent: string | PartListUnion; // For model | ||
| returnDisplay: string | FileDiff; // For user | ||
| } | ||
| ``` | ||
| **Dynamic tool discovery** via `tools.discoveryCommand` in `settings.json`: | ||
| ```json | ||
| { | ||
| "tools": { | ||
| "discoveryCommand": "./my-tool-discovery.sh" | ||
| } | ||
| } | ||
| ``` | ||
| The command outputs JSON describing custom tools, registered as `DiscoveredTool` instances. | ||
| --- | ||
| ## Extension / Plugin Systems | ||
| | Tool | Extension System | What Can Be Packaged | | ||
| |------|-----------------|---------------------| | ||
| | **Claude Code** | Plugins (`plugin.json`) | Commands, agents, skills, MCP servers, hooks | | ||
| | **Gemini CLI** | Extensions (`gemini-extension.json`) | Commands, MCP servers, hooks, skills, sub-agents, context | | ||
| | **Codex CLI** | None (uses AGENTS.md + .rules) | N/A | | ||
| | **Crush** | Agent Skills (open standard) | Skills from configurable directories | | ||
| ### Gemini CLI Extensions (Rich System) | ||
| ```bash | ||
| # Install from GitHub | ||
| gemini extensions install https://github.com/owner/my-extension | ||
| # Scaffold new extension from template | ||
| gemini extensions new ./my-ext mcp-server | ||
| # Link for local development | ||
| gemini extensions link ./my-ext | ||
| # Manage | ||
| gemini extensions list | ||
| gemini extensions enable/disable <name> | ||
| gemini extensions update --all | ||
| ``` | ||
| **Extension structure:** | ||
| ``` | ||
| my-extension/ | ||
| gemini-extension.json # Config (MCP servers, settings, tool exclusions) | ||
| GEMINI.md # Context file (auto-loaded) | ||
| commands/ # Custom slash commands (TOML files) | ||
| skills/ # Agent skills | ||
| agents/ # Sub-agents (.md files) | ||
| hooks/hooks.json # Lifecycle hooks | ||
| ``` | ||
| **Templates available:** `context`, `custom-commands`, `exclude-tools`, `mcp-server` | ||
| --- | ||
| ## Remote Agents & A2A Protocol | ||
| | Tool | Remote Agent Support | Protocol | | ||
| |------|---------------------|----------| | ||
| | **Claude Code** | Yes (via Agent SDK hosting) | Custom (Agent SDK) | | ||
| | **Gemini CLI** | Yes | Agent-to-Agent (A2A) protocol | | ||
| | **Codex CLI** | No | N/A | | ||
| | **Crush** | No | N/A | | ||
| ### Gemini CLI A2A (Agent-to-Agent) | ||
| Gemini CLI can connect to remote subagents using the A2A protocol: | ||
| ```json | ||
| // settings.json | ||
| { | ||
| "remoteAgents": [ | ||
| { | ||
| "name": "research-agent", | ||
| "agent_card_url": "https://my-agent.example.com/.well-known/agent.json" | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
| **Commands:** | ||
| - `/agents` -- list, refresh, enable, disable remote agents | ||
| - Remote agents appear alongside local sub-agents | ||
| ### Claude Code Agent SDK Hosting | ||
| Deploy Claude Code agents as long-running services: | ||
| **Deployment patterns:** | ||
| 1. **Ephemeral**: New container per task, destroyed when complete | ||
| 2. **Long-running**: Persistent container, multiple Claude processes | ||
| 3. **Hybrid**: Ephemeral containers hydrated with history (session resume) | ||
| 4. **Single container**: Multiple SDK processes in one container | ||
| **Sandbox providers:** | ||
| - Modal Sandbox | ||
| - Cloudflare Sandboxes | ||
| - Daytona | ||
| - E2B | ||
| - Fly Machines | ||
| - Vercel Sandbox | ||
| **Resource requirements:** 1GiB RAM, 5GiB disk, 1 CPU per instance | ||
| --- | ||
| ## Server / Daemon / Long-Running Patterns | ||
| | Tool | Long-Running Mode | How | | ||
| |------|-------------------|-----| | ||
| | **Claude Code** | MCP Server + Agent SDK hosting | `claude mcp serve` / SDK containers | | ||
| | **Gemini CLI** | No | N/A | | ||
| | **Codex CLI** | No | N/A | | ||
| | **Crush** | No | N/A | | ||
| Claude Code is the only tool that supports true long-running server patterns: | ||
| 1. **MCP Server** (`claude mcp serve`): Exposes tools via MCP protocol | ||
| 2. **Agent SDK Containers**: Deploy as Docker/cloud services with session persistence | ||
| 3. **GitHub Actions**: `claude-code-action@v1` for CI/CD-triggered agents | ||
| --- | ||
| ## Cross-Tool Integration Matrix | ||
| | Feature | Claude Code | Gemini CLI | Codex CLI | Crush | | ||
| |---------|------------|------------|-----------|-------| | ||
| | **Runs as MCP server** | `claude mcp serve` | No | `shell-tool-mcp` (shell only) | No | | ||
| | **MCP client** | Deep (stdio/http/sse + OAuth) | Yes (via extensions) | Limited (stdio) | Yes (stdio/http/sse) | | ||
| | **Importable SDK** | Python + TypeScript | No | TypeScript (partial) | No | | ||
| | **Extension system** | Plugins | Extensions (rich) | AGENTS.md | Agent Skills | | ||
| | **Remote agents** | Agent SDK hosting | A2A protocol | No | No | | ||
| | **Subagents** | `--agents` JSON, file-based | `.md` files in agents/ | No | No | | ||
| | **Hooks** | Pre/Post ToolUse, Stop, etc. | hooks.json | No | No | | ||
| | **LSP integration** | No | No | No | Yes | | ||
| | **Multi-provider** | Anthropic, Bedrock, Vertex, Azure | Google, Vertex | OpenAI, Azure, Gemini, Ollama, etc. | All major | | ||
| | **Custom tools** | Built-in + MCP | Built-in + discovery + MCP | Built-in | Built-in + MCP | | ||
| | **Session fork** | `--fork-session` | No | No | No | | ||
| | **Cost control** | `--max-budget-usd` | No | No | No | | ||
| --- | ||
| ## Best Integration Patterns by Use Case | ||
| ### 1. "I want tool X to consult tool Y for a second opinion" | ||
| **Best approach**: Use CLI subprocess calls with `-p` flag and `--output-format json`. | ||
| ```bash | ||
| # From any AI session's shell, call Claude Code | ||
| result=$(claude -p "Review this code" --output-format json --max-turns 5 | jq -r '.result') | ||
| # From any AI session's shell, call Gemini | ||
| result=$(gemini -p "Review this code" --output-format json | jq -r '.response') | ||
| # From any AI session's shell, call Codex | ||
| result=$(codex -q --json "Review this code") | ||
| ``` | ||
| ### 2. "I want tool X to use tool Y's tools (not just AI advice)" | ||
| **Best approach**: Use MCP. Connect tool X as MCP client to tool Y as MCP server. | ||
| ```bash | ||
| # Start Claude Code as MCP server | ||
| claude mcp serve | ||
| # Connect Gemini CLI to Claude Code's tools via extension | ||
| # In gemini-extension.json or settings.json: | ||
| { | ||
| "mcpServers": { | ||
| "claude-code": { | ||
| "command": "claude", | ||
| "args": ["mcp", "serve"] | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| Now Gemini CLI can use Claude Code's Read, Edit, Bash tools through MCP. | ||
| ### 3. "I want to build an automated pipeline calling multiple AI tools" | ||
| **Best approach**: Use Claude Agent SDK as orchestrator, call other tools via Bash. | ||
| ```python | ||
| from claude_agent_sdk import query, ClaudeAgentOptions | ||
| # Claude as orchestrator with Bash access to call other tools | ||
| async for message in query( | ||
| prompt=""" | ||
| 1. Analyze the codebase architecture | ||
| 2. Then run: gemini -p "Review architecture for scaling issues" --output-format json | ||
| 3. Compare your analysis with Gemini's | ||
| 4. Write a combined report | ||
| """, | ||
| options=ClaudeAgentOptions( | ||
| allowed_tools=["Read", "Glob", "Grep", "Bash", "Write"], | ||
| permission_mode="acceptEdits" | ||
| ) | ||
| ): | ||
| if hasattr(message, "result"): | ||
| print(message.result) | ||
| ``` | ||
| ### 4. "I want to expose my AI tool as a service for other tools" | ||
| **Best approach**: Only Claude Code supports this natively. | ||
| ```bash | ||
| # Claude Code as MCP server | ||
| claude mcp serve | ||
| # For Gemini/Codex: wrap in a custom MCP server | ||
| # Create a thin MCP wrapper around the CLI | ||
| ``` | ||
| ### 5. "I want conversation continuity across multiple tools" | ||
| **Best approach**: Use Claude Code sessions + file-based context sharing. | ||
| ```bash | ||
| # Step 1: Claude analyzes, saves to file | ||
| claude -p "Analyze architecture" --output-format json > /tmp/analysis.json | ||
| SESSION=$(cat /tmp/analysis.json | jq -r '.session_id') | ||
| # Step 2: Gemini reads Claude's output, adds its perspective | ||
| cat /tmp/analysis.json | jq -r '.result' | \ | ||
| gemini -p "Review this analysis. What's missing?" --output-format json > /tmp/gemini-review.json | ||
| # Step 3: Claude continues with Gemini's feedback | ||
| GEMINI_FEEDBACK=$(cat /tmp/gemini-review.json | jq -r '.response') | ||
| claude -p "A reviewer noted: $GEMINI_FEEDBACK. Update your analysis." \ | ||
| --resume "$SESSION" --output-format json | ||
| ``` | ||
| ### 6. "I want Gemini to use custom tools from an extension that calls Claude" | ||
| **Best approach**: Create a Gemini extension that wraps Claude Code. | ||
| ```json | ||
| // gemini-extension.json | ||
| { | ||
| "name": "claude-consultant", | ||
| "version": "1.0.0", | ||
| "description": "Consult Claude Code for second opinions", | ||
| "mcpServers": { | ||
| "claude-code": { | ||
| "command": "claude", | ||
| "args": ["mcp", "serve"] | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| Install: | ||
| ```bash | ||
| gemini extensions install ./claude-consultant | ||
| ``` | ||
| Now Gemini sessions can access Claude Code's tools natively. | ||
| --- | ||
| ## Detailed Tool Deep-Dives | ||
| ### Claude Code: Overlooked Capabilities | ||
| 1. **`claude mcp serve`** -- Run as MCP server for other tools | ||
| 2. **`--agents` JSON flag** -- Define specialized subagents inline | ||
| 3. **`--fork-session`** -- Branch conversations without losing original | ||
| 4. **`--json-schema`** -- Validated structured output with type safety | ||
| 5. **`--permission-prompt-tool`** -- Delegate permission prompts to an MCP tool | ||
| 6. **`--input-format stream-json`** -- Accept streaming JSON input | ||
| 7. **`--from-pr`** -- Resume sessions linked to GitHub PRs | ||
| 8. **`--remote`** -- Create web sessions on claude.ai from terminal | ||
| 9. **`--teleport`** -- Resume web sessions in local terminal | ||
| 10. **Plugin system** -- Bundle agents, skills, MCP servers, hooks | ||
| 11. **Managed MCP** -- Enterprise allowlist/denylist for MCP servers | ||
| 12. **MCP Tool Search** -- Auto on-demand loading when many tools configured | ||
| 13. **Agent teams** -- Multiple agents collaborating via SendMessage | ||
| ### Gemini CLI: Overlooked Capabilities | ||
| 1. **Extensions system** -- Full packaging of MCP + commands + hooks + skills | ||
| 2. **A2A protocol** -- Connect to remote agents | ||
| 3. **Sub-agents** -- Define in `.md` files within agents/ directory | ||
| 4. **Agent Skills** -- Auto-discovered from skills/ directory | ||
| 5. **Tools API** -- `tools.discoveryCommand` for dynamic tool registration | ||
| 6. **Hooks system** -- Lifecycle hooks in `hooks/hooks.json` | ||
| 7. **`--output-format stream-json`** -- Real-time event streaming | ||
| 8. **Custom commands** -- TOML-based commands in extensions | ||
| 9. **Context files** -- GEMINI.md auto-loaded from extensions | ||
| 10. **Extension management** -- Install from GitHub, scaffold, link for dev | ||
| ### Codex CLI: Overlooked Capabilities | ||
| 1. **`shell-tool-mcp`** -- MCP server for sandboxed shell execution | ||
| 2. **Codex-rs (Rust rewrite)** -- Performance-focused reimplementation | ||
| 3. **TypeScript SDK** (`sdk/typescript/`) -- Programmatic access | ||
| 4. **`.rules` files** -- Declarative security policies (allow/prompt/forbidden) | ||
| 5. **`full-auto` sandboxing** -- Network-disabled, directory-confined execution | ||
| 6. **`fullAutoErrorMode`** -- `ignore-and-continue` for automation resilience | ||
| 7. **Multi-provider** -- OpenAI, Azure, Gemini, Ollama via `--provider` | ||
| 8. **AGENTS.md** -- Project configuration (shared with Claude Code) | ||
| ### Crush (OpenCode Successor): Overlooked Capabilities | ||
| 1. **MCP support** -- Full stdio/http/sse transport support | ||
| 2. **Agent Skills** -- Open standard, auto-discovered from directories | ||
| 3. **LSP integration** -- Language Server Protocol for code intelligence | ||
| 4. **Multi-model** -- Switch models mid-session, preserving context | ||
| 5. **Cross-platform** -- macOS, Linux, Windows, Android, FreeBSD, OpenBSD, NetBSD | ||
| 6. **AGENTS.md support** -- Compatible with Claude Code/Codex project configs | ||
| --- | ||
| ## Common Pitfalls for Advanced Integration | ||
| | Pitfall | Why It Happens | How to Avoid | | ||
| |---------|---------------|--------------| | ||
| | MCP server tools but no AI | `claude mcp serve` exposes tools, not the AI model | Use `-p` for AI advice, `mcp serve` for tool access | | ||
| | Extension MCP servers not loading | Gemini extensions need restart | Restart Gemini CLI after adding extensions | | ||
| | Codex MCP limited to shell | `shell-tool-mcp` only provides shell, not full Codex | Use `-q` for full Codex capabilities | | ||
| | A2A agents not discoverable | Gemini needs `agent_card_url` endpoint | Ensure agent card is at `/.well-known/agent.json` | | ||
| | Cross-tool context loss | Each tool has isolated context | Use files or session IDs to bridge context | | ||
| | MCP Tool Search confusion | Tools load on-demand, may seem missing | Set `ENABLE_TOOL_SEARCH=false` to debug | | ||
| | Extension variable expansion | `${VAR}` vs `$VAR` syntax differences | Use `${VAR}` in `.mcp.json`, `$(echo $VAR)` in Crush | | ||
| --- | ||
| ## Further Reading | ||
| | Resource | Type | Why Recommended | | ||
| |----------|------|-----------------| | ||
| | [Claude Code MCP Guide](https://code.claude.com/docs/en/mcp) | Official Docs | Complete MCP client + server reference | | ||
| | [Claude Agent SDK Overview](https://platform.claude.com/docs/en/agent-sdk/overview) | Official Docs | Python/TypeScript SDK with full capabilities | | ||
| | [Agent SDK Hosting](https://platform.claude.com/docs/en/agent-sdk/hosting) | Official Docs | Production deployment patterns | | ||
| | [Agent SDK Sessions](https://platform.claude.com/docs/en/agent-sdk/sessions) | Official Docs | Session management and forking | | ||
| | [Claude Code Sub-agents](https://code.claude.com/docs/en/sub-agents) | Official Docs | Custom agents, --agents flag, delegation | | ||
| | [Gemini CLI Extensions Reference](https://github.com/google-gemini/gemini-cli/blob/main/docs/extensions/reference.md) | Official Docs | Extension config, MCP bundling, variables | | ||
| | [Gemini CLI Remote Agents](https://github.com/google-gemini/gemini-cli/blob/main/docs/core/remote-agents.md) | Official Docs | A2A protocol, remote agent configuration | | ||
| | [Gemini CLI Tools API](https://github.com/google-gemini/gemini-cli/blob/main/docs/core/tools-api.md) | Official Docs | Custom tool registration, discovery | | ||
| | [Codex shell-tool-mcp](https://github.com/openai/codex/tree/main/shell-tool-mcp) | Official Docs | Sandboxed MCP shell execution | | ||
| | [Crush (OpenCode successor)](https://github.com/charmbracelet/crush) | Repository | MCP, agent skills, LSP integration | | ||
| | [MCP Specification](https://modelcontextprotocol.io/introduction) | Standard | Protocol spec for building MCP servers/clients | | ||
| --- | ||
| *Generated by /learn from 38 sources (deep research).* | ||
| *See `resources/ai-cli-advanced-integration-patterns-sources.json` for full source metadata.* |
| # Learning Guide: Programmatic & Non-Interactive Usage of AI CLI Tools | ||
| **Generated**: 2026-02-10 | ||
| **Sources**: 52 resources analyzed | ||
| **Depth**: deep | ||
| ## Prerequisites | ||
| - Familiarity with shell scripting (bash, zsh) | ||
| - Basic understanding of JSON and piping in Unix | ||
| - Node.js 18+ or Python 3.10+ (for SDK usage) | ||
| - API keys for the respective services (Anthropic, OpenAI, Google) | ||
| - Understanding of stdin/stdout/stderr patterns | ||
| ## TL;DR | ||
| - **Claude Code** (`claude -p`) is the most feature-rich for programmatic use, with a full Agent SDK (Python/TypeScript), session continuity via `--session-id`/`--resume`, structured output via `--output-format json` and `--json-schema`, and MCP integration. | ||
| - **Gemini CLI** (`gemini -p`) offers excellent headless support with `--output-format json/stream-json`, session resumption via `--resume`, and `--yolo` for auto-approving actions. | ||
| - **Codex CLI** (`codex -q`) uses `-q`/`--quiet` for non-interactive mode, `--json` for structured output, and `--approval-mode full-auto` for unattended execution. | ||
| - **OpenCode** (`opencode -p`) provides a simple non-interactive mode with `-p`, JSON output via `-f json`, and auto-approval of all permissions in non-interactive mode. (Note: OpenCode was archived Sept 2025; its successor is "Crush" by the Charm team.) | ||
| - All four tools support **piping via stdin**, **JSON output for parsing**, and some form of **session management**. Claude Code is the only one with a dedicated programmatic SDK. | ||
| --- | ||
| ## Core Concepts | ||
| ### 1. Non-Interactive (Headless) Invocation | ||
| All four AI CLI tools support a "headless" or "print" mode where you pass a prompt, the tool processes it, and exits with the result on stdout. This is the foundation of all programmatic usage. | ||
| | Tool | Non-Interactive Flag | Example | | ||
| |------|---------------------|---------| | ||
| | Claude Code | `-p` / `--print` | `claude -p "explain this code"` | | ||
| | Gemini CLI | `-p` / `--prompt` | `gemini -p "explain this code"` | | ||
| | Codex CLI | `-q` / `--quiet` | `codex -q "explain this code"` | | ||
| | OpenCode | `-p` / `--prompt` | `opencode -p "explain this code"` | | ||
| **Key insight**: The `-p` flag is nearly universal across these tools (Claude, Gemini, OpenCode all use it). Codex uses `-q` instead. | ||
| ### 2. Output Formats | ||
| Structured output is critical for programmatic consumption. All four tools support at least text and JSON output. | ||
| | Tool | Text | JSON | Stream JSON | JSON Schema | | ||
| |------|------|------|-------------|-------------| | ||
| | Claude Code | `--output-format text` | `--output-format json` | `--output-format stream-json` | `--json-schema '{...}'` | | ||
| | Gemini CLI | `--output-format text` | `--output-format json` | `--output-format stream-json` | Not supported | | ||
| | Codex CLI | (default) | `--json` | Not documented | Not supported | | ||
| | OpenCode | `-f text` | `-f json` | Not supported | Not supported | | ||
| **Claude Code JSON response** includes: `result` (text), `session_id`, usage metadata, and optionally `structured_output` when `--json-schema` is used. | ||
| **Gemini CLI JSON response** includes: `response` (text), `stats.models` (per-model API/token usage), `stats.tools` (tool execution stats), `stats.files` (line changes), and `error` (if present). | ||
| **Stream JSON** (Claude Code and Gemini CLI) emits newline-delimited JSON events in real-time, useful for monitoring long-running tasks. | ||
| ### 3. Permission / Approval Modes | ||
| When running non-interactively, you need to control what the AI can do without human approval. | ||
| | Tool | Flag | Auto-Approve All | Auto-Approve Edits | Read-Only | | ||
| |------|------|------------------|--------------------|-----------| | ||
| | Claude Code | `--permission-mode` | `bypassPermissions` | `acceptEdits` | `plan` | | ||
| | Gemini CLI | `--approval-mode` | `yolo` | `auto_edit` | `default` | | ||
| | Codex CLI | `--approval-mode` / `-a` | `full-auto` | `auto-edit` | `suggest` | | ||
| | OpenCode | (auto in `-p` mode) | All permissions auto-approved | N/A | N/A | | ||
| **Claude Code** additionally supports `--allowedTools` for fine-grained control: | ||
| ```bash | ||
| claude -p "fix tests" --allowedTools "Bash(npm test *),Read,Edit" | ||
| ``` | ||
| **Gemini CLI** supports `--allowed-tools` to pre-approve specific tools. | ||
| ### 4. Session Management & Conversation Continuity | ||
| A key differentiator is the ability to maintain conversation state across multiple invocations -- essential for the "consultant" pattern. | ||
| #### Claude Code Sessions | ||
| Claude Code has the most sophisticated session management: | ||
| ```bash | ||
| # Start a conversation, capture session ID | ||
| session_id=$(claude -p "Review the auth module" --output-format json | jq -r '.session_id') | ||
| echo "Session: $session_id" | ||
| # Continue the same conversation | ||
| claude -p "Now focus on the database queries" --resume "$session_id" | ||
| # Or continue the most recent conversation in current directory | ||
| claude -p "What else did you find?" --continue | ||
| # Fork a session to explore alternatives | ||
| claude -p "Try a different approach" --resume "$session_id" --fork-session | ||
| # Resume by name | ||
| claude --resume "auth-refactor" | ||
| ``` | ||
| **Via the Agent SDK (Python):** | ||
| ```python | ||
| from claude_agent_sdk import query, ClaudeAgentOptions | ||
| session_id = None | ||
| # First query: capture session ID | ||
| async for message in query( | ||
| prompt="Read the authentication module", | ||
| options=ClaudeAgentOptions(allowed_tools=["Read", "Glob"]) | ||
| ): | ||
| if hasattr(message, 'subtype') and message.subtype == 'init': | ||
| session_id = message.session_id | ||
| # Resume with full context | ||
| async for message in query( | ||
| prompt="Now find all places that call it", | ||
| options=ClaudeAgentOptions(resume=session_id) | ||
| ): | ||
| if hasattr(message, "result"): | ||
| print(message.result) | ||
| ``` | ||
| **Via the Agent SDK (TypeScript):** | ||
| ```typescript | ||
| import { query } from "@anthropic-ai/claude-agent-sdk"; | ||
| let sessionId: string | undefined; | ||
| for await (const message of query({ | ||
| prompt: "Read the authentication module", | ||
| options: { allowedTools: ["Read", "Glob"] } | ||
| })) { | ||
| if (message.type === "system" && message.subtype === "init") { | ||
| sessionId = message.session_id; | ||
| } | ||
| } | ||
| for await (const message of query({ | ||
| prompt: "Now find all places that call it", | ||
| options: { resume: sessionId } | ||
| })) { | ||
| if ("result" in message) console.log(message.result); | ||
| } | ||
| ``` | ||
| #### Session Forking (Claude Code) | ||
| Fork a session to explore alternatives without modifying the original: | ||
| ```bash | ||
| # Fork from existing session to try a different approach | ||
| claude -p "Try a GraphQL approach instead" --resume "$SESSION_ID" --fork-session | ||
| # The original session is preserved; you can continue it separately | ||
| claude -p "Continue with REST" --resume "$SESSION_ID" | ||
| ``` | ||
| **Via the Agent SDK (TypeScript):** | ||
| ```typescript | ||
| // Fork to explore an alternative approach | ||
| for await (const message of query({ | ||
| prompt: "Redesign this as a GraphQL API instead", | ||
| options: { | ||
| resume: originalSessionId, | ||
| forkSession: true // New session ID, original preserved | ||
| } | ||
| })) { | ||
| if (message.type === "system" && message.subtype === "init") { | ||
| console.log(`Forked session: ${message.session_id}`); | ||
| } | ||
| } | ||
| ``` | ||
| #### Gemini CLI Sessions | ||
| ```bash | ||
| # Resume the latest session | ||
| gemini --resume | ||
| # Resume by index | ||
| gemini --resume 1 | ||
| # Resume by UUID | ||
| gemini --resume a1b2c3d4-e5f6-7890-abcd-ef1234567890 | ||
| # List available sessions | ||
| gemini --list-sessions | ||
| ``` | ||
| Sessions are stored in `~/.gemini/tmp/<project_hash>/chats/` and are project-specific. | ||
| #### Codex CLI Sessions | ||
| Codex maintains conversation history configurable via `~/.codex/config.json` with `maxSize`, `saveHistory`, and `sensitivePatterns` settings. Session resumption across non-interactive invocations is not as well-documented as Claude Code or Gemini CLI. | ||
| #### OpenCode Sessions | ||
| Sessions persist in a SQLite database. In the interactive TUI, you can switch sessions via `Ctrl+A`. Non-interactive mode (`-p`) creates ephemeral sessions. | ||
| ### 5. Piping & stdin Integration | ||
| All tools support piping content via stdin, making them composable Unix utilities. | ||
| ```bash | ||
| # Claude Code | ||
| cat error.log | claude -p "explain this error" | ||
| git diff main | claude -p "review these changes for security issues" | ||
| # Gemini CLI | ||
| cat README.md | gemini -p "Summarize this documentation" | ||
| git diff --cached | gemini -p "Write a commit message" --output-format json | jq -r '.response' | ||
| # Codex CLI | ||
| cat build-error.txt | codex -q "explain this build error" | ||
| # OpenCode | ||
| cat code.py | opencode -p "find bugs in this code" -f json -q | ||
| ``` | ||
| ### 6. The Claude Agent SDK | ||
| Claude Code uniquely offers a full programmatic SDK in both Python and TypeScript, going far beyond CLI invocation. | ||
| **Installation:** | ||
| ```bash | ||
| # TypeScript | ||
| npm install @anthropic-ai/claude-agent-sdk | ||
| # Python | ||
| pip install claude-agent-sdk | ||
| ``` | ||
| **Key capabilities beyond CLI `-p`:** | ||
| - Async iterator streaming of messages | ||
| - Type-safe structured outputs (Zod/Pydantic) | ||
| - Programmatic hooks (PreToolUse, PostToolUse, Stop) | ||
| - Custom subagent definitions | ||
| - MCP server integration | ||
| - Session management (resume, fork) | ||
| - Tool approval callbacks | ||
| - Native message objects with full metadata | ||
| **Structured output with schema validation (TypeScript):** | ||
| ```typescript | ||
| import { query } from "@anthropic-ai/claude-agent-sdk"; | ||
| import { z } from "zod"; | ||
| const BugReport = z.object({ | ||
| bugs: z.array(z.object({ | ||
| file: z.string(), | ||
| line: z.number(), | ||
| severity: z.enum(["low", "medium", "high"]), | ||
| description: z.string() | ||
| })), | ||
| total_count: z.number() | ||
| }); | ||
| for await (const message of query({ | ||
| prompt: "Find all bugs in the auth module", | ||
| options: { | ||
| outputFormat: { | ||
| type: "json_schema", | ||
| schema: z.toJSONSchema(BugReport) | ||
| } | ||
| } | ||
| })) { | ||
| if (message.type === "result" && message.structured_output) { | ||
| const report = BugReport.parse(message.structured_output); | ||
| console.log(`Found ${report.total_count} bugs`); | ||
| } | ||
| } | ||
| ``` | ||
| ### 7. Type-Safe Structured Outputs (Agent SDK) | ||
| Beyond `--json-schema` on the CLI, the Agent SDK provides first-class support for Zod (TypeScript) and Pydantic (Python) schemas with full type inference. | ||
| **TypeScript with Zod:** | ||
| ```typescript | ||
| import { z } from "zod"; | ||
| import { query } from "@anthropic-ai/claude-agent-sdk"; | ||
| const SecurityAudit = z.object({ | ||
| vulnerabilities: z.array(z.object({ | ||
| file: z.string(), | ||
| line: z.number(), | ||
| severity: z.enum(["critical", "high", "medium", "low"]), | ||
| description: z.string(), | ||
| fix: z.string() | ||
| })), | ||
| safe: z.boolean() | ||
| }); | ||
| type SecurityAudit = z.infer<typeof SecurityAudit>; | ||
| for await (const message of query({ | ||
| prompt: "Audit src/auth/ for security vulnerabilities", | ||
| options: { | ||
| allowedTools: ["Read", "Glob", "Grep"], | ||
| permissionMode: "bypassPermissions", | ||
| outputFormat: { | ||
| type: "json_schema", | ||
| schema: z.toJSONSchema(SecurityAudit) | ||
| } | ||
| } | ||
| })) { | ||
| if (message.type === "result" && message.structured_output) { | ||
| const audit = SecurityAudit.parse(message.structured_output); | ||
| if (!audit.safe) { | ||
| audit.vulnerabilities.forEach(v => { | ||
| console.error(`[${v.severity}] ${v.file}:${v.line} - ${v.description}`); | ||
| }); | ||
| process.exit(1); | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| **Python with Pydantic:** | ||
| ```python | ||
| from pydantic import BaseModel | ||
| from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage | ||
| class Vulnerability(BaseModel): | ||
| file: str | ||
| line: int | ||
| severity: str | ||
| description: str | ||
| fix: str | ||
| class SecurityAudit(BaseModel): | ||
| vulnerabilities: list[Vulnerability] | ||
| safe: bool | ||
| async for message in query( | ||
| prompt="Audit src/auth/ for security vulnerabilities", | ||
| options=ClaudeAgentOptions( | ||
| allowed_tools=["Read", "Glob", "Grep"], | ||
| permission_mode="bypassPermissions", | ||
| output_format={ | ||
| "type": "json_schema", | ||
| "schema": SecurityAudit.model_json_schema() | ||
| } | ||
| ) | ||
| ): | ||
| if isinstance(message, ResultMessage) and message.structured_output: | ||
| audit = SecurityAudit.model_validate(message.structured_output) | ||
| if not audit.safe: | ||
| for v in audit.vulnerabilities: | ||
| print(f"[{v.severity}] {v.file}:{v.line} - {v.description}") | ||
| ``` | ||
| ### 8. Custom Subagents via CLI | ||
| Claude Code supports defining subagents inline via the `--agents` flag, which is powerful for cross-session consultation: | ||
| ```bash | ||
| # Define a specialized reviewer agent and use it | ||
| claude -p "Use the security-auditor agent to review this project" \ | ||
| --output-format json \ | ||
| --agents '{ | ||
| "security-auditor": { | ||
| "description": "Security specialist for vulnerability detection", | ||
| "prompt": "You are a senior security engineer. Focus only on real vulnerabilities.", | ||
| "tools": ["Read", "Grep", "Glob"], | ||
| "model": "sonnet" | ||
| } | ||
| }' | jq -r '.result' | ||
| ``` | ||
| This is particularly useful when calling from another AI session -- you can define purpose-specific agents: | ||
| ```bash | ||
| # From a Gemini or Codex session, call Claude with a specialized agent | ||
| claude -p "Use the perf-analyzer to find bottlenecks" \ | ||
| --output-format json \ | ||
| --agents '{ | ||
| "perf-analyzer": { | ||
| "description": "Performance analysis specialist", | ||
| "prompt": "Identify performance bottlenecks. Focus on O(n^2) loops, N+1 queries, and memory leaks.", | ||
| "tools": ["Read", "Grep", "Glob", "Bash"], | ||
| "model": "opus" | ||
| } | ||
| }' | jq -r '.result' | ||
| ``` | ||
| --- | ||
| ## Code Examples | ||
| ### Basic: Non-Interactive Query (All 4 Tools) | ||
| ```bash | ||
| #!/bin/bash | ||
| # Ask each tool the same question and compare responses | ||
| # Claude Code | ||
| claude -p "What is the time complexity of quicksort?" --output-format json | jq -r '.result' | ||
| # Gemini CLI | ||
| gemini -p "What is the time complexity of quicksort?" --output-format json | jq -r '.response' | ||
| # Codex CLI | ||
| codex -q --json "What is the time complexity of quicksort?" | ||
| # OpenCode | ||
| opencode -p "What is the time complexity of quicksort?" -f json -q | ||
| ``` | ||
| ### Intermediate: Batch Code Review Script | ||
| ```bash | ||
| #!/bin/bash | ||
| # Review all changed files using Claude Code | ||
| REPORT_DIR="./review-reports" | ||
| mkdir -p "$REPORT_DIR" | ||
| for file in $(git diff main --name-only); do | ||
| echo "Reviewing: $file" | ||
| cat "$file" | claude -p \ | ||
| "Review this code for bugs, security issues, and best practices. Be concise." \ | ||
| --output-format json \ | ||
| --max-turns 3 \ | ||
| --allowedTools "Read" \ | ||
| | jq -r '.result' > "$REPORT_DIR/$(basename "$file").review" | ||
| done | ||
| echo "Reviews saved to $REPORT_DIR/" | ||
| ``` | ||
| ### Intermediate: Session Continuity Pattern (Claude Code) | ||
| ```bash | ||
| #!/bin/bash | ||
| # Multi-step analysis with conversation continuity | ||
| # Step 1: Initial analysis | ||
| SESSION=$(claude -p "Analyze the architecture of this project. Focus on the data flow." \ | ||
| --output-format json | jq -r '.session_id') | ||
| echo "Session: $SESSION" | ||
| # Step 2: Follow-up (has context from step 1) | ||
| claude -p "Based on your analysis, what are the main bottlenecks?" \ | ||
| --resume "$SESSION" --output-format json | jq -r '.result' | ||
| # Step 3: Get actionable recommendations with structured output | ||
| claude -p "Give me a prioritized list of improvements as JSON" \ | ||
| --resume "$SESSION" \ | ||
| --output-format json \ | ||
| --json-schema '{"type":"object","properties":{"improvements":{"type":"array","items":{"type":"object","properties":{"priority":{"type":"number"},"description":{"type":"string"},"effort":{"type":"string"}},"required":["priority","description","effort"]}}},"required":["improvements"]}' \ | ||
| | jq '.structured_output' | ||
| ``` | ||
| ### Advanced: "Consultant" Pattern (Node.js) | ||
| This pattern lets your automation call out to an AI CLI for advice, then act on the structured response. | ||
| ```javascript | ||
| // consultant.mjs - Ask AI for advice from within automation | ||
| import { execFileSync } from "node:child_process"; | ||
| function askClaude(prompt, options = {}) { | ||
| const args = [ | ||
| "-p", prompt, | ||
| "--output-format", "json", | ||
| "--max-turns", String(options.maxTurns || 3), | ||
| ]; | ||
| if (options.allowedTools) { | ||
| args.push("--allowedTools", options.allowedTools.join(",")); | ||
| } | ||
| if (options.sessionId) { | ||
| args.push("--resume", options.sessionId); | ||
| } | ||
| if (options.jsonSchema) { | ||
| args.push("--json-schema", JSON.stringify(options.jsonSchema)); | ||
| } | ||
| const result = execFileSync("claude", args, { | ||
| encoding: "utf-8", | ||
| timeout: options.timeout || 120_000, | ||
| cwd: options.cwd || process.cwd(), | ||
| }); | ||
| return JSON.parse(result); | ||
| } | ||
| // Usage: get commit message suggestion | ||
| const diff = execFileSync("git", ["diff", "--cached"], { encoding: "utf-8" }); | ||
| const response = askClaude( | ||
| `Write a conventional commit message for this diff:\n${diff}`, | ||
| { | ||
| maxTurns: 1, | ||
| jsonSchema: { | ||
| type: "object", | ||
| properties: { | ||
| type: { type: "string", enum: ["feat", "fix", "chore", "refactor", "docs", "test"] }, | ||
| scope: { type: "string" }, | ||
| description: { type: "string" }, | ||
| body: { type: "string" } | ||
| }, | ||
| required: ["type", "description"] | ||
| } | ||
| } | ||
| ); | ||
| console.log(response.structured_output); | ||
| // { type: "feat", scope: "auth", description: "add OAuth2 support", body: "..." } | ||
| ``` | ||
| ### Advanced: "Consultant" Pattern (Python with Agent SDK) | ||
| ```python | ||
| import asyncio | ||
| from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage | ||
| async def ask_claude(prompt, tools=None, schema=None, session_id=None): | ||
| """Call Claude Code as a consultant and get structured advice.""" | ||
| options = ClaudeAgentOptions( | ||
| allowed_tools=tools or ["Read", "Glob", "Grep"], | ||
| permission_mode="bypassPermissions" | ||
| ) | ||
| if schema: | ||
| options.output_format = {"type": "json_schema", "schema": schema} | ||
| if session_id: | ||
| options.resume = session_id | ||
| result = None | ||
| new_session_id = None | ||
| async for message in query(prompt=prompt, options=options): | ||
| if hasattr(message, 'subtype') and message.subtype == 'init': | ||
| new_session_id = message.session_id | ||
| if isinstance(message, ResultMessage): | ||
| result = message | ||
| return { | ||
| "text": result.result if result else None, | ||
| "structured": getattr(result, 'structured_output', None), | ||
| "session_id": new_session_id | ||
| } | ||
| # Usage in a CI/CD pipeline | ||
| async def ci_review(): | ||
| review = await ask_claude( | ||
| "Review all changed files for security vulnerabilities", | ||
| tools=["Read", "Glob", "Grep", "Bash"], | ||
| schema={ | ||
| "type": "object", | ||
| "properties": { | ||
| "vulnerabilities": { | ||
| "type": "array", | ||
| "items": { | ||
| "type": "object", | ||
| "properties": { | ||
| "file": {"type": "string"}, | ||
| "line": {"type": "number"}, | ||
| "severity": {"type": "string"}, | ||
| "description": {"type": "string"}, | ||
| "fix": {"type": "string"} | ||
| }, | ||
| "required": ["file", "severity", "description"] | ||
| } | ||
| }, | ||
| "safe": {"type": "boolean"} | ||
| }, | ||
| "required": ["vulnerabilities", "safe"] | ||
| } | ||
| ) | ||
| if not review["structured"]["safe"]: | ||
| print("SECURITY ISSUES FOUND:") | ||
| for vuln in review["structured"]["vulnerabilities"]: | ||
| print(f" [{vuln['severity']}] {vuln['file']}: {vuln['description']}") | ||
| exit(1) | ||
| asyncio.run(ci_review()) | ||
| ``` | ||
| ### Advanced: Gemini CLI in CI/CD | ||
| ```bash | ||
| #!/bin/bash | ||
| # Gemini CLI in a GitHub Actions workflow | ||
| # Auto-review PR | ||
| review=$(git diff origin/main...HEAD | gemini -p \ | ||
| "Review this PR for bugs, security issues, and code quality. Rate 1-10." \ | ||
| --output-format json \ | ||
| --approval-mode default) | ||
| score=$(echo "$review" | jq -r '.response' | grep -oP 'Rating: \K[0-9]+') | ||
| if [ "$score" -lt 6 ]; then | ||
| echo "::warning::Code review score is $score/10" | ||
| fi | ||
| # Generate release notes | ||
| gemini -p "Generate release notes from the last 20 commits" \ | ||
| --output-format json \ | ||
| --yolo \ | ||
| | jq -r '.response' > RELEASE_NOTES.md | ||
| ``` | ||
| ### Advanced: Multi-Tool Orchestration (Bash) | ||
| ```bash | ||
| #!/bin/bash | ||
| # Use different AI CLIs for different strengths | ||
| # Step 1: Use Claude Code for deep code analysis (best at code understanding) | ||
| ANALYSIS=$(claude -p "Analyze the architecture and identify all API endpoints" \ | ||
| --output-format json \ | ||
| --allowedTools "Read,Glob,Grep" \ | ||
| --max-turns 10) | ||
| ENDPOINTS=$(echo "$ANALYSIS" | jq -r '.result') | ||
| # Step 2: Use Gemini for generating documentation (fast, good at writing) | ||
| echo "$ENDPOINTS" | gemini -p \ | ||
| "Generate OpenAPI documentation for these endpoints" \ | ||
| --output-format json \ | ||
| -m gemini-2.5-flash \ | ||
| | jq -r '.response' > api-docs.yaml | ||
| echo "API documentation generated at api-docs.yaml" | ||
| ``` | ||
| --- | ||
| ## Common Pitfalls | ||
| | Pitfall | Why It Happens | How to Avoid | | ||
| |---------|---------------|--------------| | ||
| | Tool hangs waiting for permission | Non-interactive mode still prompts for dangerous operations | Use `--permission-mode bypassPermissions` (Claude), `--yolo` (Gemini), `-a full-auto` (Codex), or constrain with `--allowedTools` | | ||
| | JSON output includes non-JSON text | Some tools emit status/progress text to stdout | Use `--output-format json` (not text) and pipe through `jq` for safety | | ||
| | Session ID not captured | Forgetting to parse JSON output for session_id | Always use `--output-format json` and extract with `jq -r '.session_id'` | | ||
| | Timeout on complex tasks | Default subprocess timeouts are too short for multi-turn agents | Set explicit `--max-turns` limits and subprocess timeouts (120s+) | | ||
| | Context overflow in long sessions | Resumed sessions accumulate context that exceeds model window | Use `--max-turns` to limit agent loops; for Claude, auto-compaction handles this | | ||
| | Cost runaway in automation | No budget limits on automated runs | Use Claude's `--max-budget-usd` flag; set `--max-turns` on all tools | | ||
| | Shell escaping issues on Windows | `$` in JSON schemas gets interpreted by PowerShell | Escape `$` as backtick-$ in PowerShell; use double quotes with escaped inner quotes | | ||
| | OpenCode archived | Project archived Sept 2025, successor is "Crush" | Migrate to Crush or use one of the other three actively maintained tools | | ||
| | Codex network disabled in full-auto | `full-auto` mode sandboxes with no network access | Use `auto-edit` if network access is needed, or explicitly allow commands | | ||
| | Stdin and --prompt conflict | Some tools treat piped stdin and -p flag differently | Test your specific combination; Claude Code appends stdin to the prompt | | ||
| --- | ||
| ## Best Practices | ||
| Synthesized from 52 sources: | ||
| 1. **Always use `--output-format json` for programmatic consumption** -- plain text output is fragile to parse and may include unexpected formatting. | ||
| 2. **Set `--max-turns` to prevent runaway costs** -- without a limit, the AI can loop through many tool calls. Start with 3-5 for simple tasks, 10-20 for complex ones. | ||
| 3. **Use `--max-budget-usd` (Claude Code) for cost control** -- hard limit on spending per invocation prevents surprises in automation. | ||
| 4. **Capture session IDs for conversation continuity** -- extract from JSON output and pass via `--resume` for multi-step workflows. | ||
| 5. **Use the Agent SDK (Python/TypeScript) for production automation** -- the CLI is great for scripts, but the SDK gives you proper error handling, streaming, type safety, and programmatic control. | ||
| 6. **Define JSON schemas for structured output** -- Claude Code's `--json-schema` flag ensures the AI returns data in the exact shape you need. Gemini and Codex require you to parse free-form JSON. | ||
| 7. **Constrain tools to minimum necessary** -- use `--allowedTools` (Claude) or `--allowed-tools` (Gemini) to limit what the AI can do. Principle of least privilege. | ||
| 8. **Use `--append-system-prompt` over `--system-prompt`** -- appending preserves the tool's built-in capabilities while adding your instructions. Full replacement removes important defaults. | ||
| 9. **Handle errors and timeouts explicitly** -- set subprocess timeouts, check exit codes, and handle empty/malformed JSON responses gracefully. | ||
| 10. **Use `--no-session-persistence` for ephemeral tasks** -- in CI/CD where you do not need to resume, skip saving sessions to disk for cleaner operation. | ||
| 11. **Pipe context in, do not embed it in the prompt** -- `cat file.txt | claude -p "review this"` is cleaner and avoids shell escaping issues compared to embedding file contents in the argument string. | ||
| 12. **Use environment variables for API keys** -- never hardcode keys in scripts. Use `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, `OPENAI_API_KEY` as appropriate. | ||
| 13. **Use `--agents` for cross-session specialized consultation** -- when calling Claude from another AI session, define purpose-specific subagents inline via the `--agents` JSON flag for focused, constrained analysis. | ||
| 14. **Fork sessions for A/B exploration** -- use `--fork-session` with `--resume` to explore alternative approaches without losing the original conversation state. | ||
| 15. **Use Zod/Pydantic for type-safe outputs in production** -- rather than raw JSON Schema strings, use Zod (TypeScript) or Pydantic (Python) with the Agent SDK for compile-time type safety and runtime validation. | ||
| 16. **Use `claude-code-action@v1` for GitHub CI/CD** -- the official GitHub Action handles authentication, permissions, and workflow integration out of the box. | ||
| --- | ||
| ## Detailed Tool Reference | ||
| ### Claude Code (`claude`) | ||
| **Non-interactive invocation:** | ||
| ```bash | ||
| claude -p "your prompt" | ||
| cat input.txt | claude -p "process this" | ||
| ``` | ||
| **Key flags for programmatic use:** | ||
| | Flag | Purpose | | ||
| |------|---------| | ||
| | `-p` / `--print` | Non-interactive mode | | ||
| | `--output-format text\|json\|stream-json` | Output format | | ||
| | `--json-schema '{...}'` | Validated structured output | | ||
| | `--session-id UUID` | Use specific session ID | | ||
| | `--resume ID_OR_NAME` | Resume a session | | ||
| | `--continue` / `-c` | Continue most recent session | | ||
| | `--fork-session` | Fork when resuming | | ||
| | `--max-turns N` | Limit agentic turns | | ||
| | `--max-budget-usd N` | Dollar spend limit | | ||
| | `--allowedTools "Tool1,Tool2"` | Auto-approve specific tools | | ||
| | `--disallowedTools "Tool1"` | Block specific tools | | ||
| | `--permission-mode MODE` | Permission mode (plan, acceptEdits, bypassPermissions) | | ||
| | `--dangerously-skip-permissions` | Skip all permission prompts | | ||
| | `--model MODEL` | Model selection (sonnet, opus, haiku) | | ||
| | `--system-prompt TEXT` | Replace system prompt | | ||
| | `--append-system-prompt TEXT` | Append to system prompt | | ||
| | `--system-prompt-file PATH` | Load system prompt from file | | ||
| | `--append-system-prompt-file PATH` | Append from file | | ||
| | `--mcp-config PATH` | Load MCP servers | | ||
| | `--agents JSON` | Define custom subagents | | ||
| | `--verbose` | Verbose output | | ||
| | `--no-session-persistence` | Do not save session to disk | | ||
| | `--fallback-model MODEL` | Fallback when overloaded | | ||
| | `--include-partial-messages` | Include streaming events | | ||
| | `--input-format text\|stream-json` | Input format | | ||
| | `--tools "Tool1,Tool2"` | Restrict available tools | | ||
| | `--add-dir PATH` | Add working directories | | ||
| **Agent SDK (Python):** | ||
| ```bash | ||
| pip install claude-agent-sdk | ||
| ``` | ||
| ```python | ||
| from claude_agent_sdk import query, ClaudeAgentOptions | ||
| async for message in query(prompt="...", options=ClaudeAgentOptions(...)): | ||
| ... | ||
| ``` | ||
| **Agent SDK (TypeScript):** | ||
| ```bash | ||
| npm install @anthropic-ai/claude-agent-sdk | ||
| ``` | ||
| ```typescript | ||
| import { query } from "@anthropic-ai/claude-agent-sdk"; | ||
| for await (const message of query({ prompt: "...", options: { ... } })) { ... } | ||
| ``` | ||
| **Authentication:** | ||
| - `ANTHROPIC_API_KEY` for direct API | ||
| - `CLAUDE_CODE_USE_BEDROCK=1` + AWS credentials for Bedrock | ||
| - `CLAUDE_CODE_USE_VERTEX=1` + GCP credentials for Vertex AI | ||
| - `CLAUDE_CODE_USE_FOUNDRY=1` + Azure credentials for Azure | ||
| ### Gemini CLI (`gemini`) | ||
| **Non-interactive invocation:** | ||
| ```bash | ||
| gemini -p "your prompt" | ||
| echo "input" | gemini -p "process this" | ||
| gemini -p "query" --output-format json | jq '.response' | ||
| ``` | ||
| **Key flags for programmatic use:** | ||
| | Flag | Purpose | | ||
| |------|---------| | ||
| | `-p` / `--prompt` | Non-interactive prompt | | ||
| | `-i` / `--prompt-interactive` | Execute prompt then continue interactively | | ||
| | `--output-format text\|json\|stream-json` | Output format | | ||
| | `-m` / `--model MODEL` | Model selection (pro, flash, flash-lite) | | ||
| | `--approval-mode default\|auto_edit\|yolo` | Approval mode | | ||
| | `-y` / `--yolo` | Auto-approve all (deprecated, use --approval-mode=yolo) | | ||
| | `-r` / `--resume [ID]` | Resume session (latest, index, or UUID) | | ||
| | `--list-sessions` | List available sessions | | ||
| | `--delete-session N` | Delete session by index | | ||
| | `-d` / `--debug` | Enable debug mode | | ||
| | `-s` / `--sandbox` | Run in sandboxed environment | | ||
| | `--include-directories DIRS` | Add directories to context | | ||
| | `--allowed-tools TOOLS` | Pre-approve specific tools | | ||
| | `-e` / `--extensions EXTS` | Specify extensions | | ||
| **Authentication:** | ||
| - `GEMINI_API_KEY` for direct API | ||
| - `GOOGLE_CLOUD_PROJECT` + `GOOGLE_GENAI_USE_VERTEXAI` for Vertex AI | ||
| **Session storage:** `~/.gemini/tmp/<project_hash>/chats/` | ||
| **Streaming JSON event types:** `init`, `message`, `tool_use`, `tool_result`, `error`, `result` | ||
| ### Codex CLI (`codex`) | ||
| **Non-interactive invocation:** | ||
| ```bash | ||
| codex -q "your prompt" | ||
| codex -q --json "your prompt" | ||
| ``` | ||
| **Key flags for programmatic use:** | ||
| | Flag | Purpose | | ||
| |------|---------| | ||
| | `-q` / `--quiet` | Non-interactive (headless) mode | | ||
| | `--json` | Structured JSON output | | ||
| | `-a` / `--approval-mode` | Approval mode (suggest, auto-edit, full-auto) | | ||
| | `-m` / `--model MODEL` | Model selection | | ||
| | `--provider PROVIDER` | AI provider (openai, azure, gemini, etc.) | | ||
| | `--no-project-doc` | Skip AGENTS.md loading | | ||
| | `--notify` | Desktop notifications | | ||
| **Environment variables:** | ||
| - `OPENAI_API_KEY` for OpenAI | ||
| - `CODEX_QUIET_MODE=1` to suppress interactive elements | ||
| - `CODEX_DISABLE_PROJECT_DOC=1` to disable AGENTS.md | ||
| - `DEBUG=true` for verbose API logging | ||
| **Approval modes:** | ||
| | Mode | File Reads | File Writes | Shell Commands | | ||
| |------|-----------|-------------|----------------| | ||
| | `suggest` (default) | Auto | Prompt | Prompt | | ||
| | `auto-edit` | Auto | Auto (patches) | Prompt | | ||
| | `full-auto` | Auto | Auto | Auto (no network, confined) | | ||
| **Configuration:** `~/.codex/config.json` | ||
| ### OpenCode (`opencode`) | ||
| **Status:** Archived September 2025. Successor: "Crush" by Charm team. | ||
| **Non-interactive invocation:** | ||
| ```bash | ||
| opencode -p "your prompt" | ||
| opencode -p "your prompt" -f json -q | ||
| ``` | ||
| **Key flags for programmatic use:** | ||
| | Flag | Purpose | | ||
| |------|---------| | ||
| | `-p` / `--prompt` | Non-interactive mode | | ||
| | `-f` / `--output-format` | Output format (text, json) | | ||
| | `-q` / `--quiet` | Suppress spinner animation | | ||
| | `-d` / `--debug` | Enable debug logging | | ||
| | `-c` / `--cwd` | Set working directory | | ||
| **Key characteristic:** All permissions are auto-approved in non-interactive mode. | ||
| **Configuration paths (priority order):** | ||
| 1. `$HOME/.opencode.json` | ||
| 2. `$XDG_CONFIG_HOME/opencode/.opencode.json` | ||
| 3. `./.opencode.json` (local) | ||
| **Supported providers:** OpenAI, Anthropic, Google Gemini, AWS Bedrock, Groq, Azure OpenAI, OpenRouter, GitHub Copilot, VertexAI | ||
| --- | ||
| ## MCP (Model Context Protocol) Integration | ||
| MCP enables AI tools to connect to external data sources and tools. All four CLI tools support MCP to varying degrees. | ||
| ### Claude Code MCP | ||
| Claude Code has the deepest MCP integration: | ||
| ```bash | ||
| # Load MCP config from file | ||
| claude -p "query" --mcp-config ./mcp.json | ||
| # Strict mode: only use specified MCP servers | ||
| claude -p "query" --strict-mcp-config --mcp-config ./mcp.json | ||
| ``` | ||
| **Via Agent SDK:** | ||
| ```python | ||
| async for message in query( | ||
| prompt="Open example.com and describe what you see", | ||
| options=ClaudeAgentOptions( | ||
| mcp_servers={ | ||
| "playwright": {"command": "npx", "args": ["@playwright/mcp@latest"]} | ||
| } | ||
| ) | ||
| ): | ||
| ... | ||
| ``` | ||
| ### Gemini CLI MCP | ||
| Gemini CLI supports MCP extensions via the `--extensions` flag and configuration in `settings.json`. | ||
| ### OpenCode MCP | ||
| OpenCode supports MCP server integration configured in `.opencode.json`. | ||
| --- | ||
| ## Subprocess Invocation Patterns | ||
| ### From Node.js | ||
| ```javascript | ||
| import { execFileSync, spawn } from "node:child_process"; | ||
| // Synchronous (simple queries) | ||
| function claudeSync(prompt, options = {}) { | ||
| const args = ["-p", prompt, "--output-format", "json"]; | ||
| if (options.maxTurns) args.push("--max-turns", String(options.maxTurns)); | ||
| if (options.sessionId) args.push("--resume", options.sessionId); | ||
| const result = execFileSync("claude", args, { | ||
| encoding: "utf-8", | ||
| timeout: options.timeout || 120_000, | ||
| maxBuffer: 10 * 1024 * 1024, // 10MB for large responses | ||
| }); | ||
| return JSON.parse(result); | ||
| } | ||
| // Async with streaming (long-running tasks) | ||
| function claudeStream(prompt) { | ||
| return new Promise((resolve, reject) => { | ||
| const proc = spawn("claude", [ | ||
| "-p", prompt, "--output-format", "stream-json" | ||
| ]); | ||
| const events = []; | ||
| proc.stdout.on("data", (chunk) => { | ||
| chunk.toString().split("\n").filter(Boolean).forEach(line => { | ||
| try { events.push(JSON.parse(line)); } catch { /* skip */ } | ||
| }); | ||
| }); | ||
| proc.on("close", (code) => { | ||
| code === 0 ? resolve(events) : reject(new Error(`Exit code ${code}`)); | ||
| }); | ||
| }); | ||
| } | ||
| ``` | ||
| ### From Python | ||
| ```python | ||
| import subprocess | ||
| import json | ||
| def claude_query(prompt, max_turns=5, session_id=None, timeout=120): | ||
| """Call Claude Code CLI and return parsed JSON.""" | ||
| cmd = [ | ||
| "claude", "-p", prompt, | ||
| "--output-format", "json", | ||
| "--max-turns", str(max_turns) | ||
| ] | ||
| if session_id: | ||
| cmd.extend(["--resume", session_id]) | ||
| result = subprocess.run( | ||
| cmd, | ||
| capture_output=True, | ||
| text=True, | ||
| timeout=timeout, | ||
| cwd="." | ||
| ) | ||
| if result.returncode != 0: | ||
| raise RuntimeError(f"Claude failed: {result.stderr}") | ||
| return json.loads(result.stdout) | ||
| # Usage | ||
| response = claude_query("What files are in this project?", max_turns=3) | ||
| print(response["result"]) | ||
| print(f"Session: {response['session_id']}") | ||
| ``` | ||
| ### From Bash | ||
| ```bash | ||
| #!/bin/bash | ||
| set -euo pipefail | ||
| ask_claude() { | ||
| local prompt="$1" | ||
| local max_turns="${2:-5}" | ||
| local response | ||
| response=$(claude -p "$prompt" \ | ||
| --output-format json \ | ||
| --max-turns "$max_turns" \ | ||
| --allowedTools "Read,Glob,Grep" \ | ||
| 2>/dev/null) | ||
| echo "$response" | ||
| } | ||
| # Usage with error handling | ||
| if result=$(ask_claude "List all TODO comments" 3); then | ||
| echo "$result" | jq -r '.result' | ||
| SESSION=$(echo "$result" | jq -r '.session_id') | ||
| else | ||
| echo "Claude query failed" >&2 | ||
| exit 1 | ||
| fi | ||
| ``` | ||
| --- | ||
| ## Error Handling and Timeouts | ||
| ### Exit Codes | ||
| | Tool | Success | Error | Notes | | ||
| |------|---------|-------|-------| | ||
| | Claude Code | 0 | Non-zero | `--max-turns` exceeded returns error | | ||
| | Gemini CLI | 0 | Non-zero | Stream JSON emits `error` events | | ||
| | Codex CLI | 0 | Non-zero | Quiet mode failures reflected in exit code | | ||
| | OpenCode | 0 | Non-zero | | | ||
| ### Timeout Strategy | ||
| ```bash | ||
| # Bash: use timeout command | ||
| timeout 120 claude -p "complex analysis" --output-format json --max-turns 10 | ||
| ``` | ||
| ### Retry Pattern | ||
| ```python | ||
| import time | ||
| import subprocess | ||
| def claude_with_retry(prompt, retries=3, backoff=5): | ||
| for attempt in range(retries): | ||
| try: | ||
| return claude_query(prompt, timeout=120) | ||
| except (subprocess.TimeoutExpired, RuntimeError) as e: | ||
| if attempt == retries - 1: | ||
| raise | ||
| time.sleep(backoff * (attempt + 1)) | ||
| ``` | ||
| --- | ||
| ## Cost & Token Management | ||
| | Tool | Cost Control Flag | Token Tracking | | ||
| |------|-------------------|----------------| | ||
| | Claude Code | `--max-budget-usd 5.00` | JSON output includes usage metadata | | ||
| | Gemini CLI | No direct flag | JSON `stats.models` includes token counts | | ||
| | Codex CLI | No direct flag | `DEBUG=true` logs API requests | | ||
| | OpenCode | No direct flag | Debug mode shows token usage | | ||
| **Best practice for automation:** | ||
| ```bash | ||
| # Claude: hard budget limit | ||
| claude -p "complex analysis" --max-budget-usd 2.00 --max-turns 10 | ||
| ``` | ||
| --- | ||
| ## Comparison Matrix | ||
| | Feature | Claude Code | Gemini CLI | Codex CLI | OpenCode | | ||
| |---------|------------|------------|-----------|----------| | ||
| | **Non-interactive flag** | `-p` | `-p` | `-q` | `-p` | | ||
| | **JSON output** | Yes | Yes | Yes | Yes | | ||
| | **Stream JSON** | Yes | Yes | No | No | | ||
| | **JSON Schema validation** | Yes (`--json-schema`) | No | No | No | | ||
| | **Programmatic SDK** | Python + TypeScript | No | No | No | | ||
| | **Session resume** | `--resume`, `--continue` | `--resume` | Limited | SQLite-based | | ||
| | **Session fork** | `--fork-session` | No | No | No | | ||
| | **Cost control** | `--max-budget-usd` | No | No | No | | ||
| | **Turn limit** | `--max-turns` | Settings only | No | No | | ||
| | **Tool restriction** | `--allowedTools` | `--allowed-tools` | N/A | N/A | | ||
| | **Custom system prompt** | `--system-prompt` | Via config | N/A | N/A | | ||
| | **MCP integration** | Deep (`--mcp-config`) | Extensions | N/A | Config | | ||
| | **CI/CD action** | GitHub Actions (official) | GitHub Action | N/A | N/A | | ||
| | **Sub-agents** | `--agents` JSON | No | No | No | | ||
| | **Multi-provider** | Anthropic, Bedrock, Vertex, Azure | Google, Vertex | OpenAI, Azure, Gemini, Ollama, etc. | All major providers | | ||
| | **Active development** | Yes | Yes | Yes | Archived (Sept 2025) | | ||
| --- | ||
| ## Cross-Session Consultation: Calling One AI CLI From Another | ||
| The most powerful pattern is calling a different AI CLI from within an active session. For example, from inside a Claude Code session, you can use the Bash tool to invoke Codex, Gemini, or another Claude instance for a second opinion, specialized analysis, or alternative approach. | ||
| ### Why Cross-Consult? | ||
| - **Second opinion**: Different models have different strengths and blind spots | ||
| - **Specialized strengths**: Gemini excels at certain reasoning tasks, Codex at OpenAI-ecosystem code | ||
| - **Cost optimization**: Route expensive analysis to cheaper models for triage | ||
| - **Validation**: Cross-check AI suggestions with a different provider | ||
| - **Parallel exploration**: Get multiple approaches to the same problem | ||
| ### Pattern 1: From Claude Code Session, Call Codex | ||
| Inside a Claude Code session, use the Bash tool: | ||
| ```bash | ||
| # Ask Codex for a second opinion on a function | ||
| codex -q -a auto-edit "Review the error handling in src/auth.ts and suggest improvements" 2>&1 | ||
| # Get Codex to generate an alternative implementation | ||
| codex -q --json "Write an alternative implementation of the retry logic in lib/http.js" | jq -r '.message' | ||
| ``` | ||
| ### Pattern 2: From Claude Code Session, Call Gemini CLI | ||
| ```bash | ||
| # Ask Gemini for architecture review | ||
| gemini -p "Review the architecture in this project and identify scaling bottlenecks" \ | ||
| --output-format json | jq -r '.response' | ||
| # Use Gemini for documentation generation (it's fast with flash) | ||
| gemini -p "Generate API documentation for all exported functions in src/api/" \ | ||
| -m gemini-2.5-flash \ | ||
| --output-format json | jq -r '.response' | ||
| ``` | ||
| ### Pattern 3: From Claude Code Session, Call Another Claude Instance | ||
| ```bash | ||
| # Spawn a separate Claude session with different constraints | ||
| claude -p "As a security auditor, review src/auth/ for vulnerabilities" \ | ||
| --output-format json \ | ||
| --allowedTools "Read,Glob,Grep" \ | ||
| --max-turns 5 \ | ||
| --append-system-prompt "You are a security specialist. Only report real vulnerabilities." \ | ||
| | jq -r '.result' | ||
| # Use a different model for a different perspective | ||
| claude -p "Review this test file for missing edge cases" \ | ||
| --model haiku \ | ||
| --output-format json \ | ||
| --max-turns 3 \ | ||
| | jq -r '.result' | ||
| ``` | ||
| ### Pattern 4: Multi-Consult with Conversation Continuity | ||
| The key insight: capture the session ID from the first call to continue the conversation later. | ||
| ```bash | ||
| # Step 1: Ask Claude for initial analysis (from another AI session's Bash tool) | ||
| CLAUDE_RESPONSE=$(claude -p "Analyze the data flow in this project" \ | ||
| --output-format json --max-turns 10 --allowedTools "Read,Glob,Grep") | ||
| CLAUDE_SESSION=$(echo "$CLAUDE_RESPONSE" | jq -r '.session_id') | ||
| CLAUDE_ANALYSIS=$(echo "$CLAUDE_RESPONSE" | jq -r '.result') | ||
| # Step 2: Feed Claude's analysis to Gemini for a second take | ||
| echo "$CLAUDE_ANALYSIS" | gemini -p \ | ||
| "Here is an analysis of a codebase's data flow. What did the analyst miss? What additional concerns do you see?" \ | ||
| --output-format json | jq -r '.response' | ||
| # Step 3: Continue the Claude conversation with Gemini's feedback | ||
| claude -p "A second reviewer noted these additional concerns: [paste gemini output]. Do you agree?" \ | ||
| --resume "$CLAUDE_SESSION" \ | ||
| --output-format json | jq -r '.result' | ||
| ``` | ||
| ### Pattern 5: From Codex or Gemini Session, Call Claude Code | ||
| From within a Codex or Gemini session (using their shell/Bash tool): | ||
| ```bash | ||
| # From Codex: call Claude for deep code analysis | ||
| claude -p "Find all security vulnerabilities in this codebase" \ | ||
| --output-format json \ | ||
| --allowedTools "Read,Glob,Grep,Bash" \ | ||
| --max-turns 15 \ | ||
| --max-budget-usd 2.00 \ | ||
| | jq -r '.result' | ||
| # From Gemini: call Claude with specific subagents | ||
| claude -p "Use the code-reviewer agent to review recent changes" \ | ||
| --output-format json \ | ||
| --agents '{"code-reviewer":{"description":"Reviews code","prompt":"Review for bugs and security","tools":["Read","Grep","Glob"],"model":"sonnet"}}' \ | ||
| | jq -r '.result' | ||
| ``` | ||
| ### Pattern 6: Wrapper Script for Cross-Tool Consultation | ||
| Create a reusable script that any AI session can call: | ||
| ```bash | ||
| #!/bin/bash | ||
| # consult.sh - Cross-tool AI consultation | ||
| # Usage: ./consult.sh <tool> <prompt> [--continue <session_id>] | ||
| TOOL="$1" | ||
| PROMPT="$2" | ||
| SESSION_FLAG="" | ||
| if [[ "$3" == "--continue" && -n "$4" ]]; then | ||
| SESSION_FLAG="--resume $4" | ||
| fi | ||
| case "$TOOL" in | ||
| claude) | ||
| claude -p "$PROMPT" --output-format json --max-turns 5 \ | ||
| --allowedTools "Read,Glob,Grep" $SESSION_FLAG 2>/dev/null | ||
| ;; | ||
| gemini) | ||
| gemini -p "$PROMPT" --output-format json $SESSION_FLAG 2>/dev/null | ||
| ;; | ||
| codex) | ||
| codex -q --json "$PROMPT" 2>/dev/null | ||
| ;; | ||
| *) | ||
| echo '{"error":"Unknown tool: '"$TOOL"'"}' >&2 | ||
| exit 1 | ||
| ;; | ||
| esac | ||
| ``` | ||
| Then from any AI session: | ||
| ```bash | ||
| # Get Claude's opinion | ||
| ./consult.sh claude "What's wrong with the error handling in src/api.ts?" | ||
| # Get Gemini's opinion on the same thing | ||
| ./consult.sh gemini "What's wrong with the error handling in src/api.ts?" | ||
| # Continue a Claude conversation | ||
| ./consult.sh claude "What about the retry logic?" --continue "$SESSION_ID" | ||
| ``` | ||
| ### Important Caveats for Cross-Session Calls | ||
| | Caveat | Details | | ||
| |--------|---------| | ||
| | **Working directory** | The called tool runs in the same cwd as the calling session's Bash tool | | ||
| | **Timeouts** | Set generous timeouts; AI calls can take 30-120+ seconds | | ||
| | **Nested permissions** | The called tool needs its own permission handling (`--permission-mode bypassPermissions` for non-interactive) | | ||
| | **API keys** | Each tool needs its own API key set in the environment | | ||
| | **Cost stacking** | You pay for both the calling session AND the consulted tool | | ||
| | **Context isolation** | The called tool has no knowledge of the calling session's conversation | | ||
| | **Output parsing** | Always use `--output-format json` and parse with `jq` for reliability | | ||
| | **Background vs foreground** | Calls block the calling session until complete; use `&` for parallel | | ||
| ### Cost-Efficient Cross-Consultation Strategy | ||
| ```bash | ||
| # Triage with cheap model first, escalate if needed | ||
| TRIAGE=$(claude -p "Quick assessment: is this code safe?" \ | ||
| --model haiku --max-turns 1 --output-format json | jq -r '.result') | ||
| if echo "$TRIAGE" | grep -qi "concern\|issue\|vulnerability"; then | ||
| # Escalate to thorough review with multiple tools | ||
| claude -p "Deep security review of this code" \ | ||
| --model opus --max-turns 10 --output-format json | jq -r '.result' | ||
| gemini -p "Security audit this codebase" \ | ||
| --output-format json | jq -r '.response' | ||
| fi | ||
| ``` | ||
| --- | ||
| ## Production Deployment Patterns | ||
| ### Pattern 1: Ephemeral (One-Shot) | ||
| Best for CI/CD pipelines, code review, linting. | ||
| ```bash | ||
| # GitHub Actions example | ||
| - name: AI Code Review | ||
| run: | | ||
| claude -p "Review this PR for security issues" \ | ||
| --output-format json \ | ||
| --max-turns 5 \ | ||
| --max-budget-usd 1.00 \ | ||
| --allowedTools "Read,Glob,Grep" \ | ||
| --no-session-persistence \ | ||
| | jq -r '.result' > review.md | ||
| ``` | ||
| ### Pattern 2: Long-Running Session | ||
| Best for multi-step workflows, research tasks. | ||
| ```python | ||
| # Python: session-based workflow | ||
| session = None | ||
| for step in workflow_steps: | ||
| response = await ask_claude(step.prompt, session_id=session) | ||
| session = response["session_id"] | ||
| step.handle_result(response) | ||
| ``` | ||
| ### Pattern 3: GitHub Actions (Claude Code Action) | ||
| Best for automated PR review, issue triage, and CI-triggered code changes. | ||
| ```yaml | ||
| # .github/workflows/claude.yml | ||
| name: Claude Code | ||
| on: | ||
| issue_comment: | ||
| types: [created] | ||
| pull_request_review_comment: | ||
| types: [created] | ||
| jobs: | ||
| claude: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: anthropics/claude-code-action@v1 | ||
| with: | ||
| anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} | ||
| # Responds to @claude mentions in comments | ||
| ``` | ||
| **Automated code review on PR open:** | ||
| ```yaml | ||
| name: Code Review | ||
| on: | ||
| pull_request: | ||
| types: [opened, synchronize] | ||
| jobs: | ||
| review: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: anthropics/claude-code-action@v1 | ||
| with: | ||
| anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} | ||
| prompt: "/review" | ||
| claude_args: "--max-turns 5 --model claude-sonnet-4-5-20250929" | ||
| ``` | ||
| **Custom automation (e.g., daily report):** | ||
| ```yaml | ||
| name: Daily Report | ||
| on: | ||
| schedule: | ||
| - cron: "0 9 * * *" | ||
| jobs: | ||
| report: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: anthropics/claude-code-action@v1 | ||
| with: | ||
| anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} | ||
| prompt: "Generate a summary of yesterday's commits and open issues" | ||
| claude_args: "--model opus" | ||
| ``` | ||
| ### Pattern 4: Containerized Agent (Agent SDK) | ||
| Best for production automation. See the Agent SDK hosting documentation for Docker, Cloud, and sandbox provider options (Modal, Cloudflare, E2B, Fly.io, Vercel). | ||
| Resource requirements per SDK instance: 1GiB RAM, 5GiB disk, 1 CPU minimum. | ||
| --- | ||
| ## Further Reading | ||
| | Resource | Type | Why Recommended | | ||
| |----------|------|-----------------| | ||
| | [Claude Code CLI Reference](https://code.claude.com/docs/en/cli-reference) | Official Docs | Complete flag reference for Claude Code | | ||
| | [Claude Agent SDK Overview](https://platform.claude.com/docs/en/agent-sdk/overview) | Official Docs | Programmatic Python/TypeScript SDK | | ||
| | [Agent SDK Structured Outputs](https://platform.claude.com/docs/en/agent-sdk/structured-outputs) | Official Docs | JSON Schema validation for typed responses | | ||
| | [Agent SDK Sessions](https://platform.claude.com/docs/en/agent-sdk/sessions) | Official Docs | Session management and forking | | ||
| | [Agent SDK Streaming](https://platform.claude.com/docs/en/agent-sdk/streaming-output) | Official Docs | Real-time streaming patterns | | ||
| | [Agent SDK Hosting](https://platform.claude.com/docs/en/agent-sdk/hosting) | Official Docs | Production deployment patterns | | ||
| | [Claude Code Headless Mode](https://code.claude.com/docs/en/headless) | Official Docs | Non-interactive usage guide | | ||
| | [Claude Code Sub-agents](https://code.claude.com/docs/en/sub-agents) | Official Docs | Custom subagents and the --agents flag | | ||
| | [Claude Code GitHub Actions](https://code.claude.com/docs/en/github-actions) | Official Docs | CI/CD integration with claude-code-action | | ||
| | [Claude Code Common Workflows](https://code.claude.com/docs/en/common-workflows) | Official Docs | Practical workflow patterns | | ||
| | [Gemini CLI Headless Mode](https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/headless.md) | Official Docs | Non-interactive Gemini usage | | ||
| | [Gemini CLI Reference](https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/cli-reference.md) | Official Docs | Complete Gemini CLI flags | | ||
| | [Gemini CLI Sessions](https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/session-management.md) | Official Docs | Session persistence and resumption | | ||
| | [Gemini CLI Settings](https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/settings.md) | Official Docs | Configuration for automation | | ||
| | [Codex CLI README](https://github.com/openai/codex/blob/main/codex-cli/README.md) | Official Docs | Codex CLI usage and flags | | ||
| | [Codex CLI Repository](https://github.com/openai/codex) | Repository | Source code and documentation | | ||
| | [OpenCode Repository](https://github.com/opencode-ai/opencode) | Repository | Archived reference (successor: Crush) | | ||
| --- | ||
| *Generated by /learn from 52 sources (2 research rounds).* | ||
| *See `resources/ai-cli-non-interactive-programmatic-usage-sources.json` for full source metadata.* |
| # Learning Guide: All-in-One Plus Modular Packages | ||
| **Generated**: 2026-02-21 | ||
| **Sources**: 40 resources analyzed | ||
| **Depth**: deep | ||
| ## Prerequisites | ||
| - Familiarity with package managers (npm, pip, cargo, etc.) | ||
| - Basic understanding of monorepos vs polyrepos | ||
| - Experience publishing or consuming packages from a registry | ||
| - Understanding of semantic versioning (semver) | ||
| ## TL;DR | ||
| - The "batteries included but removable" pattern provides a meta-package that re-exports or depends on individual modular packages, letting users choose convenience OR minimal footprint. | ||
| - Monorepo tooling (npm/pnpm/yarn workspaces, Lerna, Nx, Turborepo, Rush, Changesets) makes it practical to develop, version, and publish dozens of packages from a single repository. | ||
| - Real-world examples span a spectrum: lodash (per-method packages), AWS SDK v3 (@aws-sdk/client-*), Babel (@babel/plugin-*), Angular (@angular/*), Effect (@effect/*), and more. | ||
| - Installer CLIs (create-react-app, degit/tiged, npx-based scaffolders) fetch templates from registries or GitHub tarballs, not git clones. | ||
| - Version pinning strategies include lockfiles, changesets, fixed vs independent versioning, and workspace protocol references. | ||
| ## Core Concepts | ||
| ### 1. The Packaging Spectrum | ||
| Open source projects sit on a spectrum from fully monolithic to fully granular: | ||
| | Model | Example | Trade-off | | ||
| |-------|---------|-----------| | ||
| | **Monolithic** | lodash (full), aws-sdk v2 | Simple to adopt, large bundle | | ||
| | **Scoped modular** | @aws-sdk/client-s3, @babel/plugin-* | Tree-shakeable, more install commands | | ||
| | **Per-function** | lodash.get, lodash.debounce | Minimal footprint, dependency explosion | | ||
| | **Core + plugins** | ESLint, webpack, Vite, Pino | Extensible, requires configuration | | ||
| | **Meta-package** | jest (wraps @jest/*), Angular | Convenience layer over modular internals | | ||
| **Key insight**: Most successful projects offer BOTH a convenience meta-package AND individual packages. Users choose based on their constraints. | ||
| ### 2. The Meta-Package Pattern | ||
| A meta-package is a thin wrapper that re-exports or depends on individual packages: | ||
| ```json | ||
| { | ||
| "name": "my-framework", | ||
| "version": "3.0.0", | ||
| "dependencies": { | ||
| "@my-framework/core": "3.0.0", | ||
| "@my-framework/router": "3.0.0", | ||
| "@my-framework/cli": "3.0.0", | ||
| "@my-framework/utils": "3.0.0" | ||
| } | ||
| } | ||
| ``` | ||
| The meta-package `index.js` re-exports everything: | ||
| ```javascript | ||
| // my-framework/index.js | ||
| export { createApp, defineComponent } from '@my-framework/core'; | ||
| export { createRouter, useRoute } from '@my-framework/router'; | ||
| export { cli } from '@my-framework/cli'; | ||
| ``` | ||
| Users can install `my-framework` for everything, or `@my-framework/core` alone for minimal footprint. | ||
| **Real-world examples**: | ||
| - **Jest**: `jest` meta-package wraps `@jest/core`, `@jest/expect`, `@jest/globals`, etc. | ||
| - **Angular**: `@angular/core`, `@angular/router`, `@angular/forms` are independent but `ng new` installs them together. | ||
| - **Effect**: Core `effect` package plus 20+ `@effect/*` packages for platform, SQL, AI, RPC, etc. | ||
| - **Astro**: Core `astro` plus `@astrojs/react`, `@astrojs/vue`, `@astrojs/node`, `@astrojs/vercel`, etc. | ||
| ### 3. Monorepo Tooling for Multi-Package Projects | ||
| Publishing multiple packages from one repo requires specialized tooling: | ||
| #### Workspace Managers (dependency linking) | ||
| | Tool | Workspace Config | Key Feature | | ||
| |------|-----------------|-------------| | ||
| | **npm workspaces** | `package.json#workspaces` | Built into npm 7+ | | ||
| | **pnpm workspaces** | `pnpm-workspace.yaml` | Content-addressable storage, strict isolation | | ||
| | **yarn workspaces** | `package.json#workspaces` | Plug'n'Play resolution, zero-installs | | ||
| | **bun workspaces** | `package.json#workspaces` | Native speed, compatible with npm | | ||
| #### Build Orchestrators (task scheduling) | ||
| | Tool | Written In | Key Feature | | ||
| |------|-----------|-------------| | ||
| | **Turborepo** | Rust | Remote caching, task pipelines | | ||
| | **Nx** | TypeScript | Affected analysis, computation caching | | ||
| | **Rush** | TypeScript | Enterprise-scale, strict dependency policies | | ||
| | **Lerna** | TypeScript | Version/publish commands, now maintained by Nx | | ||
| #### Version & Publish Managers | ||
| | Tool | Approach | Best For | | ||
| |------|----------|----------| | ||
| | **Changesets** | Intent-based changelogs | Monorepos with multiple maintainers | | ||
| | **Lerna version/publish** | Conventional commits or manual | Established monorepos | | ||
| | **semantic-release** | Fully automated from commits | Single packages or coordinated releases | | ||
| | **np** | Interactive guided publish | Single package publishing | | ||
| ### 4. Versioning Strategies | ||
| #### Fixed (Locked) Versioning | ||
| All packages share one version number. When any package changes, all bump together. | ||
| ```json | ||
| // lerna.json | ||
| { | ||
| "version": "3.2.1" | ||
| } | ||
| ``` | ||
| **Used by**: Babel, Angular, React (within a release) | ||
| **Pro**: Simple mental model -- all `@babel/*` packages at 7.24.0 are compatible. | ||
| **Con**: Unnecessary version bumps for unchanged packages. | ||
| #### Independent Versioning | ||
| Each package has its own version. Only changed packages bump. | ||
| ```json | ||
| // lerna.json | ||
| { | ||
| "version": "independent" | ||
| } | ||
| ``` | ||
| **Used by**: AWS SDK v3, Effect, Astro integrations | ||
| **Pro**: Precise, no unnecessary bumps. | ||
| **Con**: Harder to know which versions are compatible. | ||
| #### Workspace Protocol | ||
| pnpm and yarn support `workspace:*` to reference sibling packages during development, resolved to actual versions at publish time: | ||
| ```json | ||
| { | ||
| "dependencies": { | ||
| "@my-lib/utils": "workspace:*" | ||
| } | ||
| } | ||
| ``` | ||
| At publish time, `workspace:*` becomes `^3.2.1` (the actual version). This prevents accidentally publishing with unresolvable local references. | ||
| #### Peer Dependencies for Plugin Compatibility | ||
| Plugins declare their host as a peer dependency to ensure version compatibility: | ||
| ```json | ||
| { | ||
| "name": "@babel/plugin-transform-classes", | ||
| "peerDependencies": { | ||
| "@babel/core": "^7.0.0" | ||
| } | ||
| } | ||
| ``` | ||
| ### 5. The AWS SDK v2 to v3 Migration (Case Study) | ||
| AWS SDK v2 was a single `aws-sdk` package containing every AWS service client. Problems: | ||
| - Bundle size was enormous even if you used one service | ||
| - No tree-shaking possible | ||
| - Cold start penalties in Lambda | ||
| AWS SDK v3 introduced modular clients: | ||
| ```javascript | ||
| // v2: monolithic | ||
| const AWS = require('aws-sdk'); | ||
| const s3 = new AWS.S3(); | ||
| // v3: modular | ||
| const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3'); | ||
| const s3 = new S3Client({}); | ||
| ``` | ||
| **Architecture changes**: | ||
| - Each service is a separate npm package (`@aws-sdk/client-s3`, `@aws-sdk/client-dynamodb`, etc.) | ||
| - Middleware stack replaces the monolithic plugin system | ||
| - Shared packages like `@aws-sdk/middleware-retry` are consumed by individual clients | ||
| - Tree-shaking dramatically reduces bundle size | ||
| ### 6. Lodash: Per-Method Packages (Case Study) | ||
| Lodash offered the most granular packaging approach in npm history: | ||
| | Package | Size | Contents | | ||
| |---------|------|----------| | ||
| | `lodash` | ~24 kB gzipped | Full library, 300+ methods | | ||
| | `lodash-es` | ~24 kB | ES module version (tree-shakeable) | | ||
| | `lodash/fp` | ~24 kB | Functional programming variant | | ||
| | `lodash.get` | ~1 kB | Single method package | | ||
| | `lodash.debounce` | ~1 kB | Single method package | | ||
| **Lessons learned**: Per-method packages caused dependency management headaches -- projects could end up with different versions of lodash internals. The ecosystem eventually moved toward ES modules + tree-shaking as a better solution than per-function packages. | ||
| ### 7. Core + Plugin Architecture | ||
| Many tools use a minimal core with an extensible plugin system: | ||
| **Babel**: `@babel/core` provides the transformation engine. Plugins (`@babel/plugin-transform-*`) handle specific syntax transforms. Presets (`@babel/preset-env`) bundle common plugin sets. | ||
| **ESLint**: Core `eslint` handles linting orchestration. Rules come from plugins (`@eslint/js`, `eslint-plugin-react`). Configs bundle rules into shareable sets. | ||
| **Webpack**: Core `webpack` provides module bundling. Loaders preprocess files (TypeScript, CSS). Plugins hook into the build lifecycle. | ||
| **Vite**: Core `vite` handles dev server and building. Official plugins like `@vitejs/plugin-legacy` extend capabilities. | ||
| **Pino**: Core `pino` does fast JSON logging. `pino-pretty` provides dev formatting. Transports are separate packages run in worker threads. | ||
| **Tailwind CSS**: Core `tailwindcss` provides utility classes. Plugins like `@tailwindcss/typography` and `@tailwindcss/forms` add specialized utilities. | ||
| ### 8. Installer CLI Patterns | ||
| Installer CLIs scaffold projects without requiring global installation: | ||
| #### npx-Based Scaffolders | ||
| ```bash | ||
| npx create-react-app my-app # Deprecated but iconic pattern | ||
| npx create-next-app my-app # Next.js scaffolder | ||
| npx create-astro # Astro scaffolder | ||
| npx create-nx-workspace # Nx workspace scaffolder | ||
| ``` | ||
| How it works under the hood: | ||
| 1. `npx` downloads the package temporarily from npm registry | ||
| 2. Package exposes a `bin` entry in package.json | ||
| 3. Binary runs, prompts user for options | ||
| 4. Generates files from templates (embedded or fetched) | ||
| 5. Runs `npm install` in the new directory | ||
| 6. Temporary package is cleaned up | ||
| #### Template Cloners (degit/tiged) | ||
| ```bash | ||
| npx degit user/repo my-project | ||
| npx tiged user/repo my-project # Actively maintained fork | ||
| ``` | ||
| How it works: | ||
| 1. Resolves the latest commit on the default branch | ||
| 2. Downloads the tar archive from GitHub/GitLab API (NOT git clone) | ||
| 3. Caches tarball locally (`~/.degit/user/repo/hash.tar.gz`) | ||
| 4. Extracts to target directory | ||
| 5. No `.git` directory -- clean copy | ||
| **Advantages over git clone**: No git history, faster download, works offline from cache, supports subdirectory extraction. | ||
| #### Custom CLI Installers | ||
| For tools like `agentsys install web-ctl`, the typical architecture: | ||
| ```javascript | ||
| // 1. Parse the install command | ||
| const packageName = args[0]; // "web-ctl" | ||
| // 2. Resolve package location (multiple strategies) | ||
| async function resolve(name) { | ||
| // Strategy A: npm registry | ||
| const pkg = await fetch(`https://registry.npmjs.org/${name}/latest`); | ||
| // Strategy B: GitHub releases | ||
| const release = await fetch( | ||
| `https://api.github.com/repos/org/${name}/releases/latest` | ||
| ); | ||
| const asset = release.assets.find(a => a.name.endsWith('.tar.gz')); | ||
| // Strategy C: Custom registry | ||
| const manifest = await fetch(`https://my-registry.dev/${name}/manifest.json`); | ||
| return { tarball: asset.browser_download_url, version: release.tag_name }; | ||
| } | ||
| // 3. Download and extract | ||
| const tarball = await download(resolved.tarball); | ||
| await extract(tarball, targetDir); | ||
| // 4. Run post-install hooks | ||
| await runHook('postinstall', targetDir); | ||
| // 5. Update local manifest | ||
| await updateManifest(name, resolved.version); | ||
| ``` | ||
| **GitHub Releases approach**: Build artifacts (tarballs, binaries) are attached to GitHub releases. The CLI queries the GitHub API for the latest release, finds the right asset for the platform, downloads and extracts it. | ||
| **npm Registry approach**: The CLI runs `npm pack` or queries the registry API directly, downloads the tarball, and extracts it to a plugin directory. | ||
| **Custom registry approach**: A JSON manifest maps package names to download URLs and metadata. The CLI fetches the manifest, resolves the URL, downloads the artifact. | ||
| ### 9. Building Packages for Dual CJS/ESM Distribution | ||
| Modern packages must support both CommonJS and ES Modules: | ||
| #### package.json Exports Map | ||
| ```json | ||
| { | ||
| "name": "my-lib", | ||
| "type": "module", | ||
| "exports": { | ||
| ".": { | ||
| "import": "./dist/index.mjs", | ||
| "require": "./dist/index.cjs", | ||
| "types": "./dist/index.d.ts" | ||
| }, | ||
| "./utils": { | ||
| "import": "./dist/utils.mjs", | ||
| "require": "./dist/utils.cjs", | ||
| "types": "./dist/utils.d.ts" | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| #### Build Tools | ||
| | Tool | Status | Approach | | ||
| |------|--------|----------| | ||
| | **unbuild** | Active | Auto-infers from package.json, rollup-based | | ||
| | **pkgroll** | Active | Reads exports field, zero-config rollup | | ||
| | **tsup** | Migrating to tsdown | esbuild-based, fast | | ||
| | **Rollup** | Active | Low-level, maximum control | | ||
| #### Type Checking | ||
| Use `@arethetypeswrong/cli` to verify packages resolve correctly across all module resolution modes (node10, node16, bundler). This catches common issues like types not matching the actual module format. | ||
| ### 10. Workspace Architecture Patterns | ||
| #### Cargo Workspaces (Rust) | ||
| ```toml | ||
| # Cargo.toml (root) | ||
| [workspace] | ||
| members = ["crates/*"] | ||
| [workspace.dependencies] | ||
| serde = "1.0" | ||
| # crates/my-lib/Cargo.toml | ||
| [dependencies] | ||
| serde.workspace = true # Inherits version from root | ||
| ``` | ||
| Shared `Cargo.lock`, unified build output, configurable default members. | ||
| #### Go Modules | ||
| Go takes a different approach -- each module is independently versioned with its own `go.mod`. Multi-module repos are possible but less common. The `golang.org/x/*` packages demonstrate the pattern of a standard library extended by independent modules. | ||
| #### Python Extras | ||
| Python uses `extras_require` for optional dependency groups: | ||
| ```python | ||
| # setup.py | ||
| setup( | ||
| name="my-lib", | ||
| install_requires=["core-dep"], | ||
| extras_require={ | ||
| "full": ["optional-dep-1", "optional-dep-2"], | ||
| "dev": ["pytest", "mypy"], | ||
| } | ||
| ) | ||
| ``` | ||
| Users install with: `pip install my-lib[full]` | ||
| #### Nix | ||
| Nixpkgs is a single repository of 120,000+ package definitions. Users declaratively specify which packages they want, and Nix resolves and builds only those. The collection is monolithic in source but selective in installation. | ||
| ## Code Examples | ||
| ### Meta-Package with Re-Exports | ||
| ```javascript | ||
| // packages/my-framework/package.json | ||
| { | ||
| "name": "my-framework", | ||
| "version": "2.0.0", | ||
| "dependencies": { | ||
| "@my-framework/core": "2.0.0", | ||
| "@my-framework/router": "2.0.0", | ||
| "@my-framework/store": "2.0.0" | ||
| }, | ||
| "exports": { | ||
| ".": "./src/index.js", | ||
| "./core": { "import": "@my-framework/core" }, | ||
| "./router": { "import": "@my-framework/router" }, | ||
| "./store": { "import": "@my-framework/store" } | ||
| } | ||
| } | ||
| // packages/my-framework/src/index.js | ||
| export * from '@my-framework/core'; | ||
| export * from '@my-framework/router'; | ||
| export * from '@my-framework/store'; | ||
| ``` | ||
| ### Monorepo with pnpm Workspaces + Changesets | ||
| ```yaml | ||
| # pnpm-workspace.yaml | ||
| packages: | ||
| - 'packages/*' | ||
| - 'plugins/*' | ||
| ``` | ||
| ```json | ||
| // packages/core/package.json | ||
| { | ||
| "name": "@my-lib/core", | ||
| "version": "1.3.0", | ||
| "dependencies": { | ||
| "@my-lib/utils": "workspace:^" | ||
| } | ||
| } | ||
| ``` | ||
| ```bash | ||
| # Development workflow | ||
| pnpm install # Links workspace packages | ||
| pnpm --filter @my-lib/core build | ||
| # Release workflow | ||
| npx changeset # Create changeset describing changes | ||
| npx changeset version # Bump versions based on changesets | ||
| npx changeset publish # Publish all changed packages to npm | ||
| ``` | ||
| ### Custom CLI Installer (GitHub Releases) | ||
| ```javascript | ||
| #!/usr/bin/env node | ||
| import { createWriteStream } from 'node:fs'; | ||
| import { pipeline } from 'node:stream/promises'; | ||
| import { extract } from 'tar'; | ||
| async function install(packageName) { | ||
| // 1. Query GitHub API for latest release | ||
| const response = await fetch( | ||
| `https://api.github.com/repos/my-org/${packageName}/releases/latest`, | ||
| { headers: { 'Accept': 'application/vnd.github.v3+json' } } | ||
| ); | ||
| const release = await response.json(); | ||
| // 2. Find platform-appropriate asset | ||
| const platform = `${process.platform}-${process.arch}`; | ||
| const asset = release.assets.find(a => a.name.includes(platform)); | ||
| if (!asset) throw new Error(`No binary for ${platform}`); | ||
| // 3. Download tarball | ||
| const download = await fetch(asset.browser_download_url); | ||
| // 4. Extract to plugins directory | ||
| await pipeline( | ||
| download.body, | ||
| extract({ cwd: `./plugins/${packageName}` }) | ||
| ); | ||
| // 5. Record in manifest | ||
| const manifest = JSON.parse(await readFile('./plugins/manifest.json', 'utf8')); | ||
| manifest[packageName] = { version: release.tag_name, installed: new Date().toISOString() }; | ||
| await writeFile('./plugins/manifest.json', JSON.stringify(manifest, null, 2)); | ||
| console.log(`Installed ${packageName}@${release.tag_name}`); | ||
| } | ||
| ``` | ||
| ### Plugin Architecture with Peer Dependencies | ||
| ```json | ||
| // Plugin package.json | ||
| { | ||
| "name": "@my-tool/plugin-typescript", | ||
| "version": "1.0.0", | ||
| "peerDependencies": { | ||
| "@my-tool/core": "^2.0.0" | ||
| }, | ||
| "peerDependenciesMeta": { | ||
| "@my-tool/core": { | ||
| "optional": false | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| ```javascript | ||
| // Plugin implementation | ||
| export default function typescriptPlugin(options = {}) { | ||
| return { | ||
| name: 'typescript', | ||
| setup(api) { | ||
| api.onTransform({ filter: /\.tsx?$/ }, async (args) => { | ||
| const result = await transpile(args.contents, options); | ||
| return { contents: result.code }; | ||
| }); | ||
| } | ||
| }; | ||
| } | ||
| ``` | ||
| ## Common Pitfalls | ||
| | Pitfall | Why It Happens | How to Avoid | | ||
| |---------|---------------|--------------| | ||
| | Version mismatch between sibling packages | Independent versioning with implicit compatibility assumptions | Use peer dependencies, fixed versioning, or workspace protocol | | ||
| | Publishing with `workspace:*` references | Forgetting to let tooling resolve workspace refs | Use Changesets or Lerna publish which handle resolution | | ||
| | Duplicate dependencies in bundle | Multiple packages pull in different versions of shared dep | Use workspace hoisting, peerDependencies, or deduplication | | ||
| | Per-method packages becoming unmaintainable | N methods = N packages to version/publish/test | Prefer ES modules + tree-shaking over per-function packages | | ||
| | CJS/ESM dual package hazard | Same package loaded as both CJS and ESM creates two instances | Use exports map correctly, test with arethetypeswrong | | ||
| | Circular dependencies between packages | Organic growth without dependency graph discipline | Enforce layered architecture, use tools like Nx to detect cycles | | ||
| | Breaking changes in plugin API | Plugins depend on internal APIs that change | Use semantic versioning on plugin API, separate public API types | | ||
| | Stale lockfile after workspace changes | Adding/removing workspace packages without updating lock | Run `pnpm install` / `npm install` after structural changes | | ||
| ## Best Practices | ||
| Synthesized from 40 sources: | ||
| 1. **Start monolithic, split when needed**: Ship a single package first. Only modularize when bundle size, team boundaries, or independent release cycles demand it. | ||
| 2. **Use pnpm workspaces for new monorepos**: Content-addressable storage prevents phantom dependencies. Strict mode catches accidental cross-package imports. | ||
| 3. **Adopt Changesets for version management**: Intent-based changelogs (written at PR time) scale better than automated commit message parsing for multi-package repos. | ||
| 4. **Provide a meta-package for convenience**: Even after modularizing, always offer a single-install option that pulls in common packages together. | ||
| 5. **Use the exports map**: Define explicit entry points in package.json#exports. This enables subpath imports and prevents deep imports into package internals. | ||
| 6. **Declare plugin hosts as peer dependencies**: Plugins should `peerDependency` their host package to avoid version conflicts and duplicate instances. | ||
| 7. **Test dual CJS/ESM with arethetypeswrong**: Verify your package resolves correctly across all Node.js module resolution modes before publishing. | ||
| 8. **Use workspace protocol for internal deps**: `workspace:^` during development, resolved to real versions at publish time. | ||
| 9. **Prefer tree-shaking over per-function packages**: Modern bundlers handle dead code elimination well. ES module exports are more maintainable than hundreds of micro-packages. | ||
| 10. **Automate releases in CI**: Use semantic-release or Changesets GitHub Action to remove human error from the publish process. | ||
| 11. **Cache aggressively in installer CLIs**: degit/tiged cache tarballs locally. Custom CLIs should cache downloaded artifacts with version-based invalidation. | ||
| 12. **Pin exact versions in lockfiles, use ranges in package.json**: Lockfiles ensure reproducibility. Semver ranges in package.json allow consumers flexibility. | ||
| ## Further Reading | ||
| | Resource | Type | Why Recommended | | ||
| |----------|------|-----------------| | ||
| | [AWS SDK v3 Migration Guide](https://github.com/aws/aws-sdk-js-v3/blob/main/UPGRADING.md) | Migration Guide | Definitive case study of monolith-to-modular migration | | ||
| | [Changesets](https://github.com/changesets/changesets) | Tool | Best-in-class monorepo version management | | ||
| | [Lerna](https://lerna.js.org) | Tool | Established monorepo publishing (now maintained by Nx) | | ||
| | [Turborepo](https://turbo.build/repo) | Tool | Fast monorepo build orchestration | | ||
| | [Nx](https://nx.dev) | Tool | Full-featured monorepo platform with caching | | ||
| | [Rush](https://rushstack.io) | Tool | Enterprise-scale monorepo management | | ||
| | [pnpm Workspaces](https://pnpm.io/workspaces) | Docs | Recommended workspace manager | | ||
| | [degit](https://github.com/Rich-Harris/degit) / [tiged](https://github.com/tiged/tiged) | Tool | Template cloning without git history | | ||
| | [pkgroll](https://github.com/privatenumber/pkgroll) | Tool | Zero-config package building from exports map | | ||
| | [unbuild](https://github.com/unjs/unbuild) | Tool | Auto-inferred library builds | | ||
| | [arethetypeswrong](https://arethetypeswrong.github.io) | Tool | Verify CJS/ESM type correctness | | ||
| | [semantic-release](https://github.com/semantic-release/semantic-release) | Tool | Automated versioning from commits | | ||
| | [np](https://github.com/sindresorhus/np) | Tool | Interactive npm publish workflow | | ||
| | [GoReleaser](https://goreleaser.com) | Tool | Binary distribution via GitHub Releases | | ||
| | [Effect](https://github.com/Effect-TS/effect) | Example | Modern scoped-package monorepo architecture | | ||
| | [Astro Integrations](https://github.com/withastro/astro) | Example | Core + official integrations pattern | | ||
| | [Babel Plugins](https://github.com/babel/babel) | Example | Core + plugin + preset architecture | | ||
| --- | ||
| *Generated by /learn from 40 sources.* | ||
| *See `resources/all-in-one-plus-modular-packages-sources.json` for full source metadata.* |
| # Learning Guide: CLI-First Browser Automation for AI Agents | ||
| **Generated**: 2026-02-20 | ||
| **Sources**: 32 resources analyzed | ||
| **Depth**: deep | ||
| --- | ||
| ## Prerequisites | ||
| - Basic familiarity with Node.js `npx` and/or Python `pip`/`uv` | ||
| - Understanding of what cookies and browser sessions are | ||
| - Awareness of Chrome DevTools Protocol (CDP) at a conceptual level | ||
| - A working Node.js 18+ or Python 3.11+ environment | ||
| --- | ||
| ## TL;DR | ||
| - **Playwright CLI** (`npx playwright`) is the lowest-friction entry point: `codegen`, `screenshot`, `pdf`, `open`, and `show-trace` commands require zero scripting. The `--save-storage` / `--load-storage` flags give you a complete auth-handoff pattern in two commands. | ||
| - **Playwright MCP** exposes 30+ named tools (`browser_navigate`, `browser_click`, `browser_take_screenshot`, etc.) to any MCP-capable agent host without writing a single line of Playwright API code. | ||
| - **browser-use** is the highest-level Python option: it ships a CLI (`browser-use open`, `browser-use click N`, `browser-use screenshot`) and a Python agent API. Designed specifically for LLM agents. | ||
| - **Steel Browser** and **Browserless** expose a REST API (`POST /v1/screenshot`, `/v1/scrape`, `/v1/pdf`) so agents can drive a browser with plain `curl` or `fetch`. | ||
| - For the auth handoff, the canonical pattern is: headed `npx playwright codegen --save-storage=auth.json <login-url>` → user logs in → agent uses `--load-storage=auth.json` on all subsequent headless commands. | ||
| --- | ||
| ## Core Concepts | ||
| ### 1. The Three Automation Layers | ||
| Browser automation tools fall into three layers. Understanding which layer you are at tells you how much boilerplate you need. | ||
| **Layer 1 - CLI/REST verbs (zero boilerplate)** | ||
| You call a binary or HTTP endpoint. No session object, no page object, no awaiting. Examples: `npx playwright screenshot`, `curl http://localhost:3000/v1/screenshot`, `browser-use click 3`. | ||
| **Layer 2 - MCP tools (agent-native, zero boilerplate)** | ||
| The browser is a running server exposing named tools. Your agent calls `browser_navigate({url})` as a tool call, same as any other MCP tool. The session is persistent across calls. No JS or Python required in the agent. | ||
| **Layer 3 - Library API (full power, more boilerplate)** | ||
| You write Playwright/Puppeteer/Rod scripts. Full control of every event but you must manage the async lifecycle yourself. | ||
| For AI agents, Layer 1 and Layer 2 are almost always preferable. Layer 3 is the implementation layer for building Layer 1/2 wrappers. | ||
| ### 2. Headed vs Headless | ||
| - **Headed**: real browser window appears. Required for user-interactive auth flows. | ||
| - **Headless**: no window. Faster, suitable for automated runs after auth is established. | ||
| All major tools default to headless or can be switched with a flag. | ||
| ### 3. The Auth Handoff Problem | ||
| Most interesting pages require login. The canonical pattern for CLI agents: | ||
| 1. **Trigger a headed browser** so the user can see and interact with a real login form. | ||
| 2. **Capture the resulting session** (cookies + localStorage) into a file. | ||
| 3. **Inject that file** into all subsequent headless requests. | ||
| This is a one-time human action. The agent then operates fully autonomously from step 3 onward. | ||
| ### 4. Playwright storageState JSON Format | ||
| `npx playwright codegen --save-storage=auth.json` writes a JSON file with this structure: | ||
| ```json | ||
| { | ||
| "cookies": [ | ||
| { | ||
| "name": "session", | ||
| "value": "abc123...", | ||
| "domain": ".example.com", | ||
| "path": "/", | ||
| "expires": 1771234567.0, | ||
| "httpOnly": true, | ||
| "secure": true, | ||
| "sameSite": "Lax" | ||
| } | ||
| ], | ||
| "origins": [ | ||
| { | ||
| "origin": "https://example.com", | ||
| "localStorage": [ | ||
| { "name": "auth_token", "value": "eyJ..." } | ||
| ] | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
| This file is directly understood by `--load-storage`, by Playwright MCP's `--storage-state`, and can be converted to Netscape cookies.txt for `curl`/`wget`/`yt-dlp`. | ||
| ### 5. Netscape cookies.txt Format | ||
| The Netscape cookie file format is a 7-field tab-separated text file: | ||
| ``` | ||
| # Netscape HTTP Cookie File | ||
| # Generated by browser-automation tool | ||
| .example.com TRUE / TRUE 1771234567 session abc123... | ||
| example.com FALSE /api FALSE 0 csrf_token xyz789 | ||
| ``` | ||
| Fields: `domain`, `include_subdomains` (TRUE/FALSE), `path`, `https_only` (TRUE/FALSE), `expires_unix_epoch` (0 = session cookie), `name`, `value`. | ||
| Lines starting with `#` are comments. Lines starting with `#HttpOnly_` indicate HttpOnly cookies. | ||
| Used by: `curl -b cookies.txt`, `wget --load-cookies`, `yt-dlp --cookies`, `httpx`. | ||
| --- | ||
| ## Tools Reference | ||
| ### Playwright CLI (`npx playwright`) | ||
| **Installation**: `npm install -D playwright` or `npm install -g playwright` | ||
| **Core commands**: | ||
| | Command | What it does | Key flags | | ||
| |---------|-------------|-----------| | ||
| | `npx playwright codegen [url]` | Opens headed browser, records interactions to test script | `--save-storage=auth.json`, `-o out.js`, `--target python` | | ||
| | `npx playwright screenshot [url] [file]` | Headless screenshot | `--full-page`, `--load-storage=auth.json`, `-b chromium\|firefox\|webkit` | | ||
| | `npx playwright pdf [url] [file]` | Save page as PDF (Chromium only) | `--paper-format=A4`, `--load-storage=auth.json` | | ||
| | `npx playwright open [url]` | Open headed browser interactively | `--load-storage=auth.json`, `--save-storage=auth.json` | | ||
| | `npx playwright show-trace [file]` | View recorded trace | `--port 9323` | | ||
| **The auth handoff in two commands**: | ||
| ```bash | ||
| # Step 1: User logs in (headed browser opens, user sees real page) | ||
| npx playwright codegen --save-storage=auth.json https://example.com/login | ||
| # (user logs in manually, closes browser, auth.json now has cookies) | ||
| # Step 2: Agent uses saved session for headless work | ||
| npx playwright screenshot --load-storage=auth.json \ | ||
| https://example.com/dashboard dashboard.png | ||
| npx playwright pdf --load-storage=auth.json \ | ||
| https://example.com/report report.pdf | ||
| ``` | ||
| **Note on interactivity**: `npx playwright codegen` opens a visible browser and a side panel with generated code. The user can navigate, log in, and then close the window. The `--save-storage` flag captures state at close. This is the cleanest agent-triggered human-auth pattern available. | ||
| **Standard options** (shared across all commands): | ||
| - `--browser` / `-b`: `cr` (chromium), `ff` (firefox), `wk` (webkit), `msedge`, `chrome` | ||
| - `--device`: emulate device (`"iPhone 13"`, `"Pixel 5"`) | ||
| - `--viewport-size`: `"1280,720"` | ||
| - `--user-agent`, `--lang`, `--timezone`, `--geolocation` | ||
| - `--proxy-server` | ||
| - `--ignore-https-errors` | ||
| - `--user-data-dir`: use persistent Chrome profile with existing logins | ||
| - `--channel`: `chrome`, `msedge`, `chrome-beta` | ||
| **Screenshot example with wait**: | ||
| ```bash | ||
| npx playwright screenshot \ | ||
| --full-page \ | ||
| --wait-for-selector=".dashboard-loaded" \ | ||
| --load-storage=auth.json \ | ||
| https://example.com/dashboard \ | ||
| out.png | ||
| ``` | ||
| --- | ||
| ### Playwright MCP (`@playwright/mcp`) | ||
| **What it is**: A Model Context Protocol server that exposes browser automation as ~30 named tools. Any MCP-capable agent host (Claude Desktop, VS Code Copilot, Cursor, Cline, Windsurf) can call these tools without any Playwright code. | ||
| **Installation** (add to MCP client config): | ||
| ```json | ||
| { | ||
| "mcpServers": { | ||
| "playwright": { | ||
| "command": "npx", | ||
| "args": ["@playwright/mcp@latest"] | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| **With options** (headed browser + persistent auth): | ||
| ```json | ||
| { | ||
| "mcpServers": { | ||
| "playwright": { | ||
| "command": "npx", | ||
| "args": [ | ||
| "@playwright/mcp@latest", | ||
| "--browser", "chrome", | ||
| "--user-data-dir", "/home/user/.playwright-agent-profile" | ||
| ] | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| **Available MCP tools** (complete list): | ||
| *Core navigation & interaction*: | ||
| | Tool | Description | | ||
| |------|-------------| | ||
| | `browser_navigate` | Navigate to a URL | | ||
| | `browser_navigate_back` | Go back in history | | ||
| | `browser_click` | Click an element (by accessibility label/text/role) | | ||
| | `browser_type` | Type text into a focused field | | ||
| | `browser_fill_form` | Fill multiple form fields at once | | ||
| | `browser_select_option` | Choose from a dropdown | | ||
| | `browser_hover` | Hover over an element | | ||
| | `browser_drag` | Drag and drop between elements | | ||
| | `browser_press_key` | Send keyboard input | | ||
| | `browser_handle_dialog` | Respond to alert/confirm/prompt dialogs | | ||
| | `browser_file_upload` | Upload a file | | ||
| *Page inspection*: | ||
| | Tool | Description | | ||
| |------|-------------| | ||
| | `browser_snapshot` | Get accessibility tree of current page (preferred over screenshot for LLMs) | | ||
| | `browser_take_screenshot` | Capture PNG screenshot | | ||
| | `browser_evaluate` | Run JavaScript and return result | | ||
| | `browser_console_messages` | Get browser console logs | | ||
| | `browser_network_requests` | List all network requests since load | | ||
| | `browser_wait_for` | Wait for text to appear/disappear or timeout | | ||
| *Tab & session management*: | ||
| | Tool | Description | | ||
| |------|-------------| | ||
| | `browser_tabs` | List, create, close, or switch tabs | | ||
| | `browser_resize` | Resize browser window | | ||
| | `browser_close` | Close current page | | ||
| | `browser_install` | Install browser binaries | | ||
| *Vision mode (requires `--caps vision`)*: | ||
| | Tool | Description | | ||
| |------|-------------| | ||
| | `browser_mouse_click_xy` | Click at pixel coordinates | | ||
| | `browser_mouse_move_xy` | Move mouse to coordinates | | ||
| | `browser_mouse_drag_xy` | Drag using pixel coordinates | | ||
| | `browser_mouse_wheel` | Scroll | | ||
| *PDF (requires `--caps pdf`)*: | ||
| | Tool | Description | | ||
| |------|-------------| | ||
| | `browser_pdf_save` | Save current page as PDF | | ||
| *Testing assertions (requires `--caps testing`)*: | ||
| | Tool | Description | | ||
| |------|-------------| | ||
| | `browser_verify_text_visible` | Assert text is present | | ||
| | `browser_verify_element_visible` | Assert element exists | | ||
| | `browser_generate_locator` | Generate stable CSS/Aria selector | | ||
| **How agents use Playwright MCP**: | ||
| The server runs a persistent headed or headless browser. The agent calls tools sequentially: | ||
| ``` | ||
| agent → browser_navigate({url: "https://example.com/login"}) | ||
| agent → browser_snapshot() # read page structure | ||
| agent → browser_type({element: "Email field", text: "user@example.com"}) | ||
| agent → browser_type({element: "Password field", text: "..."}) | ||
| agent → browser_click({element: "Sign in button"}) | ||
| agent → browser_snapshot() # verify login succeeded | ||
| agent → browser_navigate({url: "https://example.com/dashboard"}) | ||
| agent → browser_take_screenshot({filename: "dashboard.png"}) | ||
| ``` | ||
| **Auth handoff with Playwright MCP**: | ||
| Option A - Persistent profile (simplest): | ||
| ```json | ||
| "args": ["@playwright/mcp@latest", "--user-data-dir", "/path/to/profile"] | ||
| ``` | ||
| User logs into a normal Chrome window using that profile once. The agent uses that profile forever. | ||
| Option B - Storage state file: | ||
| ```json | ||
| "args": ["@playwright/mcp@latest", "--storage-state", "/path/to/auth.json"] | ||
| ``` | ||
| Auth was captured separately (e.g., with `npx playwright codegen --save-storage`). | ||
| Option C - Chrome extension bridge: | ||
| ```json | ||
| "args": ["@playwright/mcp@latest", "--extension"] | ||
| ``` | ||
| The agent connects to your currently running Chrome browser tab. Uses whatever session is already active. | ||
| Option D - CDP endpoint (connect to running Chrome): | ||
| ```json | ||
| "args": [ | ||
| "@playwright/mcp@latest", | ||
| "--cdp-endpoint", "http://localhost:9222" | ||
| ] | ||
| ``` | ||
| User launches Chrome with `--remote-debugging-port=9222`, logs in, agent connects to that live session. | ||
| **Key insight**: `browser_snapshot` returns the accessibility tree as structured text, not a screenshot. This is far more token-efficient for LLM consumption and does not require a vision model. | ||
| --- | ||
| ### browser-use (Python) | ||
| **What it is**: A Python library with a CLI and agent API designed specifically for LLM-driven browser automation. The agent receives a high-level task description and plans/executes browser interactions autonomously. | ||
| **Installation**: | ||
| ```bash | ||
| pip install browser-use | ||
| # or | ||
| uv add browser-use | ||
| uvx browser-use install # downloads Chromium | ||
| ``` | ||
| **CLI interface** (stateful session persists between commands): | ||
| ```bash | ||
| browser-use open https://example.com # navigate | ||
| browser-use state # list clickable elements by index | ||
| browser-use click 5 # click element #5 | ||
| browser-use type "search query" # type text | ||
| browser-use screenshot page.png # capture screen | ||
| browser-use close # end session | ||
| ``` | ||
| **Agent API** (LLM controls browser autonomously): | ||
| ```python | ||
| from browser_use import Agent, Browser, ChatBrowserUse | ||
| import asyncio | ||
| async def run(): | ||
| browser = Browser() | ||
| llm = ChatBrowserUse() # or use OpenAI, Anthropic, etc. | ||
| agent = Agent( | ||
| task="Log into GitHub, go to my notifications, summarize the top 3", | ||
| llm=llm, | ||
| browser=browser, | ||
| ) | ||
| result = await agent.run() | ||
| print(result) | ||
| asyncio.run(run()) | ||
| ``` | ||
| **Auth with real Chrome profile**: | ||
| ```python | ||
| from browser_use import Browser, BrowserConfig | ||
| browser = Browser(config=BrowserConfig( | ||
| chrome_instance_path="/usr/bin/google-chrome", | ||
| # Uses default Chrome profile with existing logins | ||
| )) | ||
| ``` | ||
| **Custom tools extension**: | ||
| ```python | ||
| from browser_use import Agent | ||
| from browser_use.browser.context import BrowserContext | ||
| @agent.action("Read the current page URL and return it") | ||
| async def get_current_url(browser: BrowserContext) -> str: | ||
| page = await browser.get_current_page() | ||
| return page.url | ||
| ``` | ||
| **Comparison to Playwright MCP**: browser-use is more autonomous - you give it a task and it figures out the steps. Playwright MCP gives you individual tool calls (more control, less autonomy). browser-use requires Python; Playwright MCP is language-agnostic. | ||
| --- | ||
| ### puppeteer-extra | ||
| **What it is**: Puppeteer with a plugin system. The key plugin is `puppeteer-extra-plugin-stealth` which patches ~20 bot-detection signals. | ||
| **Installation**: | ||
| ```bash | ||
| npm install puppeteer-extra puppeteer-extra-plugin-stealth | ||
| ``` | ||
| **Basic usage** (still requires scripting, no CLI wrapper): | ||
| ```javascript | ||
| const puppeteer = require('puppeteer-extra'); | ||
| const StealthPlugin = require('puppeteer-extra-plugin-stealth'); | ||
| puppeteer.use(StealthPlugin()); | ||
| const browser = await puppeteer.launch({ headless: false }); | ||
| const page = await browser.newPage(); | ||
| await page.goto('https://example.com'); | ||
| await page.screenshot({ path: 'screenshot.png' }); | ||
| await browser.close(); | ||
| ``` | ||
| **Key note**: There is no standalone puppeteer CLI tool for agents. Puppeteer is a library only. For CLI-driven use, Playwright CLI is the better choice. Puppeteer-extra's main value is stealth for avoiding bot detection. | ||
| **Comparison to Playwright**: Playwright is now generally preferred. Playwright has a built-in CLI, supports 3 browser engines natively, and has a richer ecosystem including MCP. Puppeteer supports Chrome/Firefox only and has no CLI. | ||
| --- | ||
| ### Chrome DevTools Protocol (CDP) Direct | ||
| **What it is**: CDP is the underlying wire protocol that Playwright, Puppeteer, and all Chromium-based automation tools use. You can drive Chrome directly via HTTP and WebSocket without any framework. | ||
| **Launching Chrome with debugging port**: | ||
| ```bash | ||
| # Headed (user-visible) - good for auth | ||
| google-chrome \ | ||
| --remote-debugging-port=9222 \ | ||
| --user-data-dir=/tmp/chrome-agent \ | ||
| https://example.com/login | ||
| # Or headless | ||
| google-chrome \ | ||
| --headless=new \ | ||
| --remote-debugging-port=9222 \ | ||
| --user-data-dir=/tmp/chrome-agent | ||
| ``` | ||
| **HTTP API endpoints** (no WebSocket needed for these): | ||
| ```bash | ||
| # List tabs | ||
| curl http://localhost:9222/json/list | ||
| # Create new tab | ||
| curl "http://localhost:9222/json/new?https://example.com" | ||
| # Close tab | ||
| curl "http://localhost:9222/json/close/{targetId}" | ||
| # Get browser version | ||
| curl http://localhost:9222/json/version | ||
| ``` | ||
| **WebSocket CDP commands** (for actual page control): | ||
| ```javascript | ||
| const CDP = require('chrome-remote-interface'); | ||
| async function captureAuth() { | ||
| const client = await CDP(); | ||
| const { Network, Page } = client; | ||
| await Network.enable(); | ||
| await Page.enable(); | ||
| await Page.navigate({ url: 'https://example.com/login' }); | ||
| await Page.loadEventFired(); | ||
| // After user logs in (poll or wait), capture cookies | ||
| const { cookies } = await Network.getAllCookies(); | ||
| console.log(JSON.stringify(cookies)); | ||
| await client.close(); | ||
| } | ||
| ``` | ||
| **CLI REPL with chrome-remote-interface**: | ||
| ```bash | ||
| npm install -g chrome-remote-interface | ||
| # List targets | ||
| chrome-remote-interface list | ||
| # Open a URL in new tab | ||
| chrome-remote-interface new 'https://example.com' | ||
| # Interactive REPL (send CDP commands interactively) | ||
| chrome-remote-interface inspect | ||
| # Then inside REPL: | ||
| # > Page.navigate({url: 'https://example.com'}) | ||
| # > Network.getAllCookies() | ||
| ``` | ||
| **Getting cookies via CDP**: | ||
| ```bash | ||
| # Using websocat + jq (pure CLI, no Node.js needed after browser launch) | ||
| WS=$(curl -s http://localhost:9222/json/list | jq -r '.[0].webSocketDebuggerUrl') | ||
| echo '{"id":1,"method":"Network.getAllCookies"}' \ | ||
| | websocat "$WS" \ | ||
| | jq '.result.cookies[]' | ||
| ``` | ||
| **CDP verdict for agents**: CDP is powerful but verbose. Best used as a foundation layer. The chrome-remote-interface REPL is useful for exploration. For production agent use, Playwright MCP or Playwright CLI are cleaner because they handle the WebSocket protocol, target management, and element selectors automatically. | ||
| --- | ||
| ### Browserless (Self-Hosted REST API) | ||
| **What it is**: A Docker service that wraps headless Chrome and exposes a REST API. Agents call HTTP endpoints without managing any browser process. | ||
| **Run locally**: | ||
| ```bash | ||
| docker run -p 3000:3000 ghcr.io/browserless/chrome | ||
| ``` | ||
| **REST endpoints** (all `POST` with JSON body): | ||
| ```bash | ||
| # Screenshot | ||
| curl -X POST http://localhost:3000/screenshot \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"url": "https://example.com", "fullPage": true}' \ | ||
| --output out.png | ||
| curl -X POST http://localhost:3000/pdf \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"url": "https://example.com"}' \ | ||
| --output out.pdf | ||
| # HTML content | ||
| curl -X POST http://localhost:3000/content \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"url": "https://example.com"}' | ||
| # Execute Puppeteer script | ||
| curl -X POST http://localhost:3000/function \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{ | ||
| "code": "module.exports = async ({page}) => { await page.goto(args.url); return await page.title(); }", | ||
| "context": {"url": "https://example.com"} | ||
| }' | ||
| ``` | ||
| **Passing cookies to Browserless**: | ||
| ```bash | ||
| # Inject cookies in the request body | ||
| curl -X POST http://localhost:3000/screenshot \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{ | ||
| "url": "https://example.com/dashboard", | ||
| "cookies": [ | ||
| {"name": "session", "value": "abc123", "domain": "example.com"} | ||
| ] | ||
| }' --output dashboard.png | ||
| ``` | ||
| **Trade-off**: Requires Docker. But once running, agents just need `curl`. No Node.js, no Python. Good for polyglot agents. | ||
| --- | ||
| ### Steel Browser (Self-Hosted REST API) | ||
| **What it is**: An open-source browser API service similar to Browserless but with a session-oriented architecture. Good for multi-step authenticated workflows. | ||
| **Run locally**: | ||
| ```bash | ||
| # Via npm | ||
| npx @steel-dev/steel start | ||
| # Or Docker | ||
| docker run -p 3000:3000 ghcr.io/steel-dev/steel | ||
| ``` | ||
| **REST endpoints**: | ||
| ```bash | ||
| # Create a session (returns sessionId) | ||
| SESSION=$(curl -s -X POST http://localhost:3000/v1/sessions \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"blockAds": true}' | jq -r '.id') | ||
| # Screenshot a URL (stateless quick action) | ||
| curl -X POST http://localhost:3000/v1/screenshot \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"url": "https://example.com", "fullPage": true}' \ | ||
| --output out.png | ||
| # Scrape page content | ||
| curl -X POST http://localhost:3000/v1/scrape \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"url": "https://example.com"}' | ||
| curl -X POST http://localhost:3000/v1/pdf \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"url": "https://example.com"}' \ | ||
| --output out.pdf | ||
| ``` | ||
| **Sessions persist cookies** across requests - once you log into a page within a session, all subsequent requests in that session are authenticated. | ||
| **Connect Playwright to Steel session**: | ||
| ```javascript | ||
| const { chromium } = require('playwright'); | ||
| const browser = await chromium.connectOverCDP( | ||
| `ws://localhost:3000?sessionId=${sessionId}` | ||
| ); | ||
| ``` | ||
| --- | ||
| ## The Auth Handoff: Three Patterns | ||
| ### Pattern 1: Playwright CLI (Recommended for CLI Agents) | ||
| **When to use**: Your agent runs from a shell, you want zero framework knowledge required. | ||
| ```bash | ||
| # ---- Human does this once ---- | ||
| # Open headed browser for user login | ||
| npx playwright codegen \ | ||
| --save-storage=~/.agent/auth/example-auth.json \ | ||
| https://example.com/login | ||
| # [Browser opens, user logs in, browser closes, auth.json written] | ||
| # ---- Agent does this autonomously ---- | ||
| npx playwright screenshot \ | ||
| --load-storage=~/.agent/auth/example-auth.json \ | ||
| https://example.com/dashboard \ | ||
| /tmp/dashboard.png | ||
| # Agent can also generate a full-page PDF | ||
| npx playwright pdf \ | ||
| --load-storage=~/.agent/auth/example-auth.json \ | ||
| https://example.com/report \ | ||
| /tmp/report.pdf | ||
| ``` | ||
| **No Playwright code written. No async/await. Just CLI commands.** | ||
| ### Pattern 2: Playwright MCP with Chrome Extension (Recommended for MCP Agents) | ||
| **When to use**: Your agent is running inside an MCP host and you want to connect to the user's real logged-in browser. | ||
| ```json | ||
| { | ||
| "mcpServers": { | ||
| "playwright": { | ||
| "command": "npx", | ||
| "args": ["@playwright/mcp@latest", "--extension"] | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| 1. User installs "Playwright MCP Bridge" Chrome extension. | ||
| 2. User is already logged into sites in their normal Chrome. | ||
| 3. Agent calls `browser_navigate` / `browser_snapshot` / `browser_click` directly on those tabs. | ||
| 4. No auth file needed - the user's live session is used. | ||
| ### Pattern 3: CDP + Chrome --remote-debugging-port | ||
| **When to use**: You want the most direct control, or you're already running Chrome elsewhere. | ||
| ```bash | ||
| # User launches Chrome with debugging enabled | ||
| google-chrome \ | ||
| --remote-debugging-port=9222 \ | ||
| --user-data-dir=$HOME/.agent-chrome-profile \ | ||
| https://example.com/login | ||
| # User logs in normally. | ||
| # Agent now connects and captures cookies | ||
| node -e " | ||
| const CDP = require('chrome-remote-interface'); | ||
| CDP(async (client) => { | ||
| await client.Network.enable(); | ||
| const {cookies} = await client.Network.getAllCookies(); | ||
| const fs = require('fs'); | ||
| // Convert to Playwright storageState format | ||
| fs.writeFileSync('auth.json', JSON.stringify({cookies, origins: []}, null, 2)); | ||
| await client.close(); | ||
| }); | ||
| " | ||
| ``` | ||
| Then use `auth.json` with `npx playwright screenshot --load-storage=auth.json ...`. | ||
| --- | ||
| ## Converting Between Cookie Formats | ||
| ### Playwright storageState → Netscape cookies.txt | ||
| Useful when you want to use the captured session with `curl`, `wget`, or `yt-dlp`. | ||
| ```python | ||
| import json, sys | ||
| from datetime import datetime | ||
| auth = json.load(open('auth.json')) | ||
| print("# Netscape HTTP Cookie File") | ||
| for c in auth.get('cookies', []): | ||
| domain = c['domain'] | ||
| include_subdomains = 'TRUE' if domain.startswith('.') else 'FALSE' | ||
| path = c.get('path', '/') | ||
| https_only = 'TRUE' if c.get('secure', False) else 'FALSE' | ||
| expires = int(c.get('expires', 0)) if c.get('expires', -1) != -1 else 0 | ||
| name = c['name'] | ||
| value = c['value'] | ||
| print(f"{domain}\t{include_subdomains}\t{path}\t{https_only}\t{expires}\t{name}\t{value}") | ||
| ``` | ||
| ```bash | ||
| python3 convert.py > cookies.txt | ||
| curl -b cookies.txt https://example.com/api/data | ||
| wget --load-cookies=cookies.txt https://example.com/api/data | ||
| yt-dlp --cookies cookies.txt https://example.com/video | ||
| ``` | ||
| ### Netscape cookies.txt → Playwright storageState | ||
| ```python | ||
| import json, time | ||
| def netscape_to_playwright(cookies_file): | ||
| cookies = [] | ||
| with open(cookies_file) as f: | ||
| for line in f: | ||
| line = line.strip() | ||
| if not line or line.startswith('#'): | ||
| continue | ||
| parts = line.split('\t') | ||
| if len(parts) != 7: | ||
| continue | ||
| domain, incl_sub, path, https_only, expires, name, value = parts | ||
| cookies.append({ | ||
| 'name': name, | ||
| 'value': value, | ||
| 'domain': domain, | ||
| 'path': path, | ||
| 'expires': float(expires) if expires and expires != '0' else -1, | ||
| 'httpOnly': False, | ||
| 'secure': https_only == 'TRUE', | ||
| 'sameSite': 'None' | ||
| }) | ||
| return {'cookies': cookies, 'origins': []} | ||
| state = netscape_to_playwright('cookies.txt') | ||
| json.dump(state, open('auth.json', 'w'), indent=2) | ||
| ``` | ||
| --- | ||
| ## Comparison Table | ||
| | Tool | Interface | Auth Handoff | Boilerplate | Best For | | ||
| |------|-----------|-------------|-------------|---------| | ||
| | **Playwright CLI** | Shell commands | `--save-storage` / `--load-storage` | Zero | CLI agents, shell scripts | | ||
| | **Playwright MCP** | MCP tool calls | `--storage-state`, `--extension`, `--cdp-endpoint` | Zero | MCP agent hosts (Claude, Cursor, etc.) | | ||
| | **browser-use** | Python + CLI | Chrome profile reuse | Low (Python) | Autonomous task agents (Python) | | ||
| | **Chrome CDP direct** | WebSocket + HTTP | Manual cookie capture | Medium (JS) | Fine-grained control, low-level | | ||
| | **chrome-remote-interface** | CLI REPL + JS | `Network.getAllCookies()` | Low-medium | Exploration, scripting | | ||
| | **Browserless** | REST API (curl) | Cookie injection in JSON body | Zero (needs Docker) | Polyglot agents, Docker-friendly | | ||
| | **Steel Browser** | REST API (curl) | Session-scoped cookie persistence | Zero (needs Docker/npx) | Multi-step auth workflows | | ||
| | **puppeteer-extra** | JS library | Manual scripting | High | Bot-detection avoidance | | ||
| --- | ||
| ## Common Pitfalls | ||
| | Pitfall | Why It Happens | How to Avoid | | ||
| |---------|---------------|--------------| | ||
| | Capturing auth.json but cookies expire | Session cookies have short TTL | Check `expires` field; re-capture if expired. Use `--user-data-dir` for persistent profile instead. | | ||
| | Playwright PDF not working | PDF command only works with Chromium | Always pass `-b chromium` or `--channel chrome` for PDF | | ||
| | Screenshot captures login page, not dashboard | Session not loaded | Always pass `--load-storage=auth.json` | | ||
| | Browser bot-detection blocking | Playwright leaves fingerprints | Use `--channel chrome` (real Chrome binary) instead of Chromium. Or use puppeteer-extra-stealth. | | ||
| | MCP tools using accessibility tree but page has poor ARIA | Site has no semantic markup | Fall back to `browser_take_screenshot` + vision, or use `browser_evaluate` for DOM queries | | ||
| | CDP WebSocket closes on page navigation | WebSocket is per-target | Re-attach after navigation using Target events | | ||
| | Netscape cookies.txt parse error | Wrong line endings (CRLF vs LF) | Normalize to LF on Unix: `sed -i 's/\r//' cookies.txt` | | ||
| | `browser-use` agent gets stuck in loop | LLM hallucinating element states | Set `max_steps` limit; use `browser-use state` to inspect actual element indices | | ||
| | auth.json committed to git | Forgot to gitignore | Add `*.auth.json`, `auth/`, `.auth/` to `.gitignore` | | ||
| --- | ||
| ## Best Practices | ||
| 1. **Store auth files outside the repo** — use `~/.agent/auth/{service}-auth.json` or environment-relative paths. Never commit session files. (Multiple sources) | ||
| 2. **Prefer `--user-data-dir` over `--save-storage` for long-running agents** — user data directories persist across browser restarts, handle refresh tokens, and work for sites that rotate session cookies. (Playwright MCP docs) | ||
| 3. **Use `browser_snapshot` over screenshots for text extraction** — the accessibility tree is ~10x more token-efficient than describing a screenshot and does not require a vision model. (Playwright MCP README) | ||
| 4. **Use `--channel chrome` (real Chrome) when bot detection is an issue** — websites fingerprint Chrome vs Chromium. The real Chrome binary passes more checks. (Playwright docs, chrome-for-testing) | ||
| 5. **Separate the headed auth step from the headless work step** — document these as two distinct phases in your agent code. This makes re-authentication easy when sessions expire. (browser-use docs) | ||
| 6. **For multi-step workflows, use session-based tools** — Steel Browser sessions and Playwright MCP's persistent browser maintain cookie state across page navigations automatically. One-shot REST calls lose state. (Steel Browser docs) | ||
| 7. **Test for element visibility before interaction** — use `--wait-for-selector` (CLI) or `browser_wait_for` (MCP) to avoid flaky automation on dynamic pages. (Playwright CLI docs) | ||
| 8. **Validate the captured auth immediately** — after `--save-storage`, run one screenshot with `--load-storage` and check it shows the logged-in state before using the auth file in production. (Playwright docs) | ||
| --- | ||
| ## Code Examples | ||
| ### Complete Shell-Only Auth Handoff | ||
| ```bash | ||
| #!/bin/bash | ||
| # auth-handoff.sh - Agent auth handoff using only Playwright CLI | ||
| AUTH_FILE="$HOME/.agent/auth/myapp-auth.json" | ||
| BASE_URL="https://myapp.example.com" | ||
| # Phase 1: Human auth (run once, or when session expires) | ||
| capture_auth() { | ||
| mkdir -p "$(dirname "$AUTH_FILE")" | ||
| echo "Opening browser for login..." | ||
| npx playwright codegen \ | ||
| --save-storage="$AUTH_FILE" \ | ||
| "$BASE_URL/login" | ||
| echo "Auth captured: $AUTH_FILE" | ||
| } | ||
| # Phase 2: Agent uses auth headlessly | ||
| take_screenshot() { | ||
| local url="$1" | ||
| local out="$2" | ||
| npx playwright screenshot \ | ||
| --load-storage="$AUTH_FILE" \ | ||
| --full-page \ | ||
| "$url" "$out" | ||
| } | ||
| save_pdf() { | ||
| local url="$1" | ||
| local out="$2" | ||
| npx playwright pdf \ | ||
| --load-storage="$AUTH_FILE" \ | ||
| -b chromium \ | ||
| "$url" "$out" | ||
| } | ||
| # If auth file is missing or stale, capture it | ||
| if [ ! -f "$AUTH_FILE" ]; then | ||
| capture_auth | ||
| fi | ||
| # Agent work | ||
| take_screenshot "$BASE_URL/dashboard" /tmp/dashboard.png | ||
| save_pdf "$BASE_URL/report/monthly" /tmp/monthly-report.pdf | ||
| ``` | ||
| ### Playwright MCP Agent Workflow (Conceptual) | ||
| When an MCP agent wants to do browser work: | ||
| ``` | ||
| # Agent internal monologue: | ||
| # 1. Check if page is accessible | ||
| tool_call: browser_navigate({url: "https://app.example.com/dashboard"}) | ||
| tool_call: browser_snapshot() | ||
| # → Returns accessibility tree; if login wall detected, trigger auth flow | ||
| # 2. If login needed (persistent profile approach): | ||
| # Agent tells user: "Please log into the browser window that just opened" | ||
| # (Browser was started with --user-data-dir, user's existing login may already work) | ||
| # 3. Once authenticated, proceed | ||
| tool_call: browser_snapshot() # verify dashboard loaded | ||
| tool_call: browser_evaluate({expression: "document.title"}) # extract data | ||
| tool_call: browser_take_screenshot({filename: "/tmp/dashboard.png"}) | ||
| ``` | ||
| ### Python Agent with browser-use + Cookie Export | ||
| ```python | ||
| import asyncio, json | ||
| from browser_use import Agent, Browser, BrowserConfig, ChatBrowserUse | ||
| async def authenticated_scrape(): | ||
| # Option A: Use existing Chrome profile (simplest for auth) | ||
| browser = Browser(config=BrowserConfig( | ||
| chrome_instance_path="/usr/bin/google-chrome", | ||
| headless=False, | ||
| )) | ||
| # Option B: Use previously saved Playwright storageState | ||
| # browser = Browser(config=BrowserConfig(storage_state="auth.json")) | ||
| llm = ChatBrowserUse() | ||
| agent = Agent( | ||
| task=""" | ||
| Go to https://app.example.com/reports. | ||
| Find the most recent report dated this month. | ||
| Download it or return its URL. | ||
| """, | ||
| llm=llm, | ||
| browser=browser, | ||
| max_steps=20, | ||
| ) | ||
| result = await agent.run() | ||
| print(result) | ||
| await browser.close() | ||
| asyncio.run(authenticated_scrape()) | ||
| ``` | ||
| ### curl with Cookies from Playwright Auth | ||
| ```bash | ||
| # After capturing auth.json with playwright codegen --save-storage | ||
| # Quick Python converter (inline) | ||
| python3 -c " | ||
| import json, sys | ||
| data = json.load(open('auth.json')) | ||
| print('# Netscape HTTP Cookie File') | ||
| for c in data.get('cookies', []): | ||
| dom = c['domain'] | ||
| sub = 'TRUE' if dom.startswith('.') else 'FALSE' | ||
| sec = 'TRUE' if c.get('secure') else 'FALSE' | ||
| exp = int(c.get('expires', 0)) if c.get('expires', -1) > 0 else 0 | ||
| print(f\"{dom}\t{sub}\t{c['path']}\t{sec}\t{exp}\t{c['name']}\t{c['value']}\") | ||
| " > cookies.txt | ||
| # Use with curl | ||
| curl -b cookies.txt https://app.example.com/api/data | jq . | ||
| # Use with wget | ||
| wget --load-cookies=cookies.txt -O data.json https://app.example.com/api/data | ||
| # Use with yt-dlp | ||
| yt-dlp --cookies cookies.txt https://app.example.com/video/123 | ||
| ``` | ||
| --- | ||
| ## Further Reading | ||
| | Resource | Type | Why Recommended | | ||
| |----------|------|-----------------| | ||
| | [Playwright CLI docs](https://playwright.dev/docs/cli) | Official Docs | Authoritative reference for all CLI commands and flags | | ||
| | [Playwright Auth docs](https://playwright.dev/docs/auth) | Official Docs | Comprehensive guide to storageState, setup projects, session reuse | | ||
| | [Playwright MCP on GitHub](https://github.com/microsoft/playwright-mcp) | Official Repo | Complete tool list, config options, Chrome extension setup | | ||
| | [browser-use on GitHub](https://github.com/browser-use/browser-use) | Official Repo | Agent API, CLI reference, custom tools, production deployment | | ||
| | [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) | Official Spec | Complete CDP domain/method reference | | ||
| | [chrome-remote-interface](https://github.com/cyrus-and/chrome-remote-interface) | Library | Node.js CDP wrapper with CLI REPL | | ||
| | [Steel Browser](https://github.com/steel-dev/steel-browser) | Open Source | REST API browser service, session management | | ||
| | [Browserless](https://github.com/browserless/browserless) | Open Source | Docker REST browser service | | ||
| | [yt-dlp cookies guide](https://github.com/yt-dlp/yt-dlp/wiki/FAQ#how-do-i-pass-cookies-to-yt-dlp) | Guide | Netscape cookie format, browser extension recommendations | | ||
| | [puppeteer-extra-stealth](https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra-plugin-stealth) | Plugin | 20+ bot-detection patches for Puppeteer | | ||
| --- | ||
| *Generated by /learn from 32 sources.* | ||
| *See `resources/cli-browser-automation-agents-sources.json` for full source metadata.* |
| # Learning Guide: GitHub Organization Project Management for Multi-Repo Open Source Ecosystems | ||
| **Generated**: 2026-02-21 | ||
| **Sources**: 15 resources analyzed | ||
| **Depth**: deep | ||
| ## Prerequisites | ||
| - Familiarity with GitHub Issues and Pull Requests | ||
| - Basic understanding of GitHub Organizations | ||
| - Awareness of multi-repo project structures | ||
| - Optional: GraphQL API knowledge for automation | ||
| ## TL;DR | ||
| - GitHub Projects v2 is the primary tool for cross-repo org-level project management, supporting up to 50,000 items and 50 custom fields across all repos in an org. | ||
| - Issue Types (public preview, Jan 2025) and Sub-Issues enable structured work hierarchies at the org level without extra tooling. | ||
| - Automation via built-in workflows, GitHub Actions (`actions/add-to-project@v1`), and the GraphQL API is essential for keeping cross-repo boards accurate at scale. | ||
| - Successful open source orgs (Rust, Kubernetes, Astro, GitHub itself) combine a public roadmap project, RFC/enhancement-proposal repos, and working groups or SIGs to coordinate distributed contributors. | ||
| - The GraphQL API with the `project` OAuth scope unlocks full programmatic control; `read:project` is sufficient for read-only integrations. | ||
| ## Core Concepts | ||
| ### GitHub Projects v2 | ||
| Projects v2 replaced the classic Projects board in 2022. It lives at the org level (not per-repo) and can aggregate items from any repository in the organization. | ||
| Key capabilities: | ||
| - **Views**: Table (spreadsheet-style), Board (kanban), Roadmap (Gantt-like timeline) | ||
| - **Custom fields**: Up to 50 per project. Types: text, number, date, single-select, iteration | ||
| - **Grouping, filtering, sorting**: Available across all views; filters use GitHub's advanced search syntax including AND/OR keywords and parentheses | ||
| - **Capacity**: 50,000 items per project | ||
| - **Cross-repo**: A single project can contain issues and PRs from any repo in the org | ||
| ### Issue Types | ||
| Introduced in public preview January 2025. Org admins define up to 25 custom issue types (defaults: Task, Bug, Feature). Types are available across all repos and can be filtered in Projects and search. | ||
| This replaces the common pattern of using label conventions (`type: bug`, `kind/feature`) with a first-class field. | ||
| ### Sub-Issues | ||
| Also in public preview. Issues can have a parent-child relationship: | ||
| - Break a large epic into sub-issues | ||
| - Progress bar displayed on the parent issue | ||
| - Sub-issues can live in different repos from the parent | ||
| - Enables hierarchical tracking without external tools | ||
| ### Automation Tiers | ||
| | Plan | Built-in Workflows | | ||
| |------|--------------------| | ||
| | Free | 1 active workflow | | ||
| | Pro / Team | 5 active workflows | | ||
| | Enterprise | 20 active workflows | | ||
| Built-in workflow triggers: | ||
| - Item closed → set status | ||
| - PR merged → set status | ||
| - Item added to repo → add to project (filter by label, status, or assignee) | ||
| - Item archived by staleness (14 days, 3 weeks, or 1 month of inactivity) | ||
| For more complex rules, use GitHub Actions or the GraphQL API. | ||
| ### GraphQL API | ||
| All project mutations require the `project` OAuth scope. Read-only operations use `read:project`. | ||
| Primary operations: | ||
| - `addProjectV2ItemById` - add an issue/PR to a project | ||
| - `updateProjectV2ItemFieldValue` - set a field value on an item | ||
| - `createProjectV2` - create a new project | ||
| Webhook events: `projects_v2_item` (created, edited, deleted, reordered, converted) | ||
| ## Code Examples | ||
| ### Auto-Add Issues from Any Repo via GitHub Actions | ||
| ```yaml | ||
| # .github/workflows/add-to-project.yml | ||
| # Place this in EACH repo that should auto-add items to the org project | ||
| name: Add to Org Project | ||
| on: | ||
| issues: | ||
| types: [opened] | ||
| pull_request: | ||
| types: [opened] | ||
| jobs: | ||
| add-to-project: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/add-to-project@v1 | ||
| with: | ||
| project-url: https://github.com/orgs/MY-ORG/projects/42 | ||
| github-token: ${{ secrets.ADD_TO_PROJECT_PAT }} | ||
| # Optional: only add if issue has specific label | ||
| labeled: triage, bug, enhancement | ||
| ``` | ||
| The PAT must belong to an org member and have the `project` scope. | ||
| ### GraphQL: Add Issue to Project | ||
| ```graphql | ||
| mutation AddIssueToProject($projectId: ID!, $contentId: ID!) { | ||
| addProjectV2ItemById(input: { | ||
| projectId: $projectId | ||
| contentId: $contentId | ||
| }) { | ||
| item { | ||
| id | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| ### GraphQL: Update a Field Value | ||
| ```graphql | ||
| mutation SetStatus($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) { | ||
| updateProjectV2ItemFieldValue(input: { | ||
| projectId: $projectId | ||
| itemId: $itemId | ||
| fieldId: $fieldId | ||
| value: { | ||
| singleSelectOptionId: $optionId | ||
| } | ||
| }) { | ||
| projectV2Item { | ||
| id | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| ### GraphQL: Find Project by Org and Number | ||
| ```graphql | ||
| query FindProject($org: String!, $number: Int!) { | ||
| organization(login: $org) { | ||
| projectV2(number: $number) { | ||
| id | ||
| title | ||
| fields(first: 20) { | ||
| nodes { | ||
| ... on ProjectV2Field { | ||
| id | ||
| name | ||
| } | ||
| ... on ProjectV2SingleSelectField { | ||
| id | ||
| name | ||
| options { | ||
| id | ||
| name | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| ### Advanced Search: Filter Issues by Type and Status | ||
| ``` | ||
| org:MY-ORG is:open type:feature label:roadmap | ||
| org:MY-ORG is:open type:bug (label:priority-high OR label:priority-critical) | ||
| ``` | ||
| ## Org-Level Patterns from Real Projects | ||
| ### GitHub's Public Roadmap | ||
| GitHub maintains a public project at `github.com/orgs/github/projects/4247`. Key practices: | ||
| - Quarterly columns map to release milestones | ||
| - The `shipped` label is applied when a feature lands, and the issue body is updated with a changelog link | ||
| - Items stay visible after shipping in a "Shipped" column (archive only after some time) | ||
| - This gives external contributors a real-time view of planned and recent work | ||
| ### Kubernetes SIG Model | ||
| Kubernetes uses Special Interest Groups (SIGs) to coordinate work across 100+ repos: | ||
| - Each SIG owns a subset of repos and has a dedicated project board | ||
| - Kubernetes Enhancement Proposals (KEPs) live in a single `kubernetes/enhancements` repo as structured markdown files | ||
| - KEPs link to issues/PRs in implementation repos | ||
| - SIG leads triage new issues weekly and apply standardized labels (`sig/network`, `kind/feature`, etc.) | ||
| ### Rust-lang RFC Process | ||
| - RFCs live in `rust-lang/rfcs` as PRs | ||
| - Working groups coordinate cross-team implementation | ||
| - A dedicated project tracks RFC lifecycle stages (proposed → accepted → stabilized → closed) | ||
| - Cross-repo coordination uses GitHub Projects with a single-select "Phase" field | ||
| ### Astro (withastro) | ||
| - 51 repos under one org | ||
| - `withastro/roadmap` repo hosts RFC discussions and a public-facing roadmap project | ||
| - Uses pnpm workspaces for the core monorepo; satellite repos (integrations, adapters) auto-add to the roadmap project via GitHub Actions | ||
| - Labels are org-wide and enforced consistently | ||
| ## Common Pitfalls | ||
| | Pitfall | Why It Happens | How to Avoid | | ||
| |---------|---------------|--------------| | ||
| | Project scope creep | One project tracks everything; becomes unusable | One project per initiative or quarter; archive completed projects | | ||
| | Label inconsistency across repos | Each repo grows its own labels organically | Manage labels programmatically; use a shared label config script or action | | ||
| | Missing automation tokens | PATs expire or lack correct scopes; cross-repo actions silently fail | Use fine-grained PATs with explicit `project` scope; rotate with secrets manager | | ||
| | Stale items clogging board | Closed issues not archived; old PRs linger | Enable auto-archive: set staleness to 14 days or archive on close/merge | | ||
| | No triage process | New issues pile up without status | Add "Needs Triage" as default status; automate: new issue → Needs Triage | | ||
| | Cross-repo sub-issue confusion | Contributors open sub-issues in wrong repo | Document in CONTRIBUTING.md which repo to use for which issue type | | ||
| | Workflow count limits on Free plan | Only 1 built-in workflow on Free | Use GitHub Actions instead of built-in workflows for more rules | | ||
| | Views not shared with team | Each person builds their own view | Create named saved views in the project for common filters (My Items, This Sprint, Blocked) | | ||
| ## Best Practices | ||
| 1. **One source of truth per initiative** - Each major feature or release gets exactly one project. Avoid tracking the same item in multiple projects. | ||
| 2. **Standardize labels org-wide** - Use a label sync action (e.g., `EndBug/add-or-update-label`) to keep labels consistent across all repos. Document the label taxonomy in a `CONTRIBUTING.md` or `.github` repo. | ||
| 3. **Use Issue Types instead of type labels** - Now that Issue Types are available (Jan 2025 public preview), prefer them over label conventions for `bug`, `feature`, `task`. Labels are better for priority, status, and component. | ||
| 4. **Automate triage with GitHub Actions** - New issues opened in any repo auto-add to the org triage project with status "Needs Triage". Maintainers process the triage board, not individual repos. | ||
| 5. **Keep the roadmap project public** - A public roadmap project builds contributor trust and reduces duplicate "when will X be done?" issues. Model: GitHub's own public roadmap. | ||
| 6. **Use Iteration fields for sprints or releases** - Iterations are time-boxed periods (e.g., 2-week sprints). Assign items to iterations instead of milestones for better burndown tracking. | ||
| 7. **Multiple saved views per audience** - Create views for: "My open items" (assignee filter), "This sprint" (iteration filter), "Blocked" (status filter), "Triage queue" (status = Needs Triage). | ||
| 8. **Sub-issues for epics, not just labels** - Break large epics into sub-issues rather than relying on a checklist in the parent body. Progress tracking is automatic and cross-repo linking is supported. | ||
| 9. **Automate status transitions** - Configure built-in workflows: PR merged → item status set to Done. Issue closed as not-planned → item archived. This removes manual housekeeping. | ||
| 10. **Use the `.github` meta-repo** - A `.github` repo in the org stores default issue templates, PR templates, `CONTRIBUTING.md`, and Actions workflows that apply to all repos with no files. | ||
| ## Field Design for Multi-Repo Projects | ||
| Recommended custom fields for an org-level project: | ||
| | Field Name | Type | Values / Notes | | ||
| |------------|------|----------------| | ||
| | Status | Single Select | Triage, Backlog, In Progress, In Review, Done, Blocked | | ||
| | Priority | Single Select | Critical, High, Medium, Low | | ||
| | Sprint / Iteration | Iteration | 2-week cadence | | ||
| | Effort | Single Select | XS, S, M, L, XL | | ||
| | Component | Single Select | Per-repo or per-area (e.g., Core, Docs, CLI, API) | | ||
| | Target Release | Single Select | v1.0, v1.1, Backlog | | ||
| | Repo | Text | Auto-populated via automation (helps when filtering Table view) | | ||
| ## GitHub Actions Automation Recipes | ||
| ### Auto-Set Status to "In Progress" When PR Opened | ||
| ```yaml | ||
| name: PR Opened - Set In Progress | ||
| on: | ||
| pull_request: | ||
| types: [opened, reopened] | ||
| jobs: | ||
| update-project: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Move linked issues to In Progress | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| github-token: ${{ secrets.PROJECT_PAT }} | ||
| script: | | ||
| // Use GraphQL to find project item for this PR and update status | ||
| // Implementation varies by project ID and field IDs | ||
| ``` | ||
| ### Sync Org Labels Across All Repos | ||
| ```yaml | ||
| # Run in the .github meta-repo | ||
| name: Sync Labels | ||
| on: | ||
| push: | ||
| paths: ['labels.yml'] | ||
| schedule: | ||
| - cron: '0 2 * * 1' # Weekly Monday 2am | ||
| jobs: | ||
| sync: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - uses: EndBug/label-sync@v2 | ||
| with: | ||
| config-file: labels.yml | ||
| delete-other-labels: false | ||
| token: ${{ secrets.LABEL_SYNC_PAT }} | ||
| # Applies to all repos accessible to the PAT | ||
| ``` | ||
| ## Further Reading | ||
| | Resource | Type | Why Recommended | | ||
| |----------|------|-----------------| | ||
| | [GitHub Projects Docs](https://docs.github.com/en/issues/planning-and-tracking-with-projects) | Official Docs | Canonical reference for all Projects v2 features | | ||
| | [GitHub's Public Roadmap](https://github.com/orgs/github/projects/4247) | Live Example | See how GitHub itself manages a public org project | | ||
| | [actions/add-to-project](https://github.com/actions/add-to-project) | GitHub Action | Official action for cross-repo auto-add | | ||
| | [Kubernetes SIG structure](https://github.com/kubernetes/community/blob/master/sig-list.md) | Reference | How Kubernetes scales to 100+ repos with SIGs | | ||
| | [withastro/roadmap](https://github.com/withastro/roadmap) | Reference | Astro's RFC and roadmap coordination model | | ||
| | [GitHub Issues API (GraphQL)](https://docs.github.com/en/graphql/reference/mutations#addprojectv2itembyid) | API Docs | GraphQL mutations for project automation | | ||
| | [EndBug/label-sync](https://github.com/EndBug/label-sync) | GitHub Action | Sync labels across all org repos | | ||
| --- | ||
| *This guide was synthesized from research on GitHub Projects v2, Issue Types, Sub-Issues, GraphQL API, and real-world org patterns (Rust-lang, Kubernetes, Astro, GitHub). See `resources/github-org-project-management-sources.json` for full source list.* |
| # Learning Guide: GitHub Organization Structure Patterns for Developer Tool Ecosystems | ||
| **Generated**: 2026-02-21 | ||
| **Sources**: 18 resources analyzed | ||
| **Depth**: deep | ||
| ## Prerequisites | ||
| - Familiarity with GitHub repositories and basic git workflows | ||
| - Understanding of GitHub Actions concepts (workflows, triggers) | ||
| - Basic knowledge of open source project conventions (CONTRIBUTING, CODE_OF_CONDUCT) | ||
| ## TL;DR | ||
| - The `.github` org (public repo) is the control plane for org-wide defaults: community health files, reusable workflows, profile README, and starter workflows | ||
| - Real-world orgs fall into two archetypes: **focused** (few high-quality repos, e.g. oven-sh/Bun) and **ecosystem** (many small packages, e.g. unjs, sindresorhus) | ||
| - Reusable workflows (`workflow_call`) are the primary mechanism for DRY CI across many repos in an org | ||
| - CODEOWNERS + working groups (teams) is the dominant governance pattern for monorepos | ||
| - AI usage disclosure policies are becoming a new category of org-level community health file | ||
| ## Core Concepts | ||
| ### The `.github` Repository as Org Control Plane | ||
| Every GitHub org has a special `.github` repository that acts as the configuration and defaults layer for the entire org. | ||
| **Profile README** (`profile/README.md`): | ||
| - Public-facing org description shown on the org's GitHub page | ||
| - Supports full markdown: badges, images, links, embedded stats | ||
| - A separate `.github-private` repo can hold a `profile/README.md` visible only to org members — useful for internal runbooks and links | ||
| **Default Community Health Files**: | ||
| The `.github` repo (which MUST be public to cascade org-wide) can define default files that apply to every repo in the org that does not override them: | ||
| | File | Purpose | | ||
| |------|---------| | ||
| | `CODE_OF_CONDUCT.md` | Community behavior standards | | ||
| | `CONTRIBUTING.md` | How to contribute | | ||
| | `FUNDING.yml` | Sponsorship links | | ||
| | `GOVERNANCE.md` | Decision-making processes | | ||
| | `SECURITY.md` | Vulnerability reporting policy | | ||
| | `SUPPORT.md` | Where to get help | | ||
| | Issue templates | Default issue forms | | ||
| | PR templates | Default PR descriptions | | ||
| **Important caveat**: If any individual repo has ANY files in its own `.github/ISSUE_TEMPLATE/` directory, the org-level issue template defaults no longer apply to that repo. The override is all-or-nothing per file type. | ||
| **What cannot be org defaults**: License files must be defined per-repo. | ||
| ### Org-Level Pinned Repositories | ||
| Orgs can pin up to 6 repositories on their public profile page. A separate set of up to 6 pins can be configured for the member-only view. Use pins to surface: | ||
| - The primary product repo | ||
| - The standard library or docs repo | ||
| - Developer tooling repos (actions, setup tools, homebrew taps) | ||
| - Community/contribution entry points (roadmap, good-first-issues) | ||
| ### Reusable GitHub Actions Workflows | ||
| Reusable workflows eliminate CI duplication across many repos in an org. They use the `workflow_call` trigger. | ||
| **Reference syntax**: | ||
| ```yaml | ||
| jobs: | ||
| call-shared-workflow: | ||
| uses: myorg/.github/.github/workflows/ci.yml@main | ||
| with: | ||
| node-version: "22" | ||
| secrets: inherit | ||
| ``` | ||
| **Key constraints and features**: | ||
| - Maximum 10 nesting levels (reusable workflows calling other reusable workflows) | ||
| - Permissions can only be maintained or reduced, never elevated | ||
| - `secrets: inherit` passes the caller's secrets implicitly — preferred for org-internal workflows | ||
| - Matrix strategies are supported in callers | ||
| - Inputs and outputs are typed (string, boolean, number, choice) | ||
| **Pattern**: Keep reusable workflows in the org `.github` repo under `.github/workflows/`, then reference `{owner}/.github/.github/workflows/{name}.yml@{ref}`. | ||
| ### Starter Workflows | ||
| Starter workflows appear in the "Actions" tab of new repos as suggested templates. They live in the `.github` repo under `workflow-templates/`. | ||
| Each starter workflow requires two files: | ||
| - `workflow-templates/{name}.yml` — the workflow definition | ||
| - `workflow-templates/{name}.properties.json` — metadata (name, description, iconName, categories) | ||
| **Five categories**: CI, Deployments, Automation, Code-scanning, Pages | ||
| Variable substitution is supported: `$default-branch` resolves to the repo's default branch, `$cron-daily` to a daily cron expression. | ||
| ### CODEOWNERS and Working Groups | ||
| CODEOWNERS files define who must review changes to specific paths. The **last matching pattern wins** (bottom-to-top precedence). | ||
| **Tauri's two-team pattern** (simple and effective): | ||
| ``` | ||
| # .github/CODEOWNERS | ||
| * @tauri-apps/wg-tauri | ||
| .github @tauri-apps/wg-devops | ||
| ``` | ||
| This gives the main working group ownership of all code, while the DevOps working group exclusively owns CI and tooling changes — preventing accidental breakage of infrastructure by feature contributors. | ||
| **Common patterns**: | ||
| - `docs/ @org/docs-team` — separate docs ownership | ||
| - `*.rs @org/core-team` — language-specific ownership | ||
| - `/packages/plugin-* @org/plugin-maintainers` — plugin subsystem | ||
| ### GitHub Pages for Orgs | ||
| | Type | Repo Name | URL | | ||
| |------|-----------|-----| | ||
| | Org site | `<owner>.github.io` | `https://<owner>.github.io` | | ||
| | Project site | any repo | `https://<owner>.github.io/<repo>` | | ||
| One org site per account. Custom domains are supported on all tiers. Free plan limits Pages to public repos. | ||
| ## Real-World Org Archetypes | ||
| ### The Focused Minimal Org (oven-sh / Bun) | ||
| - ~7 visible repos: primary product, community list, setup action, homebrew tap, essential forks | ||
| - High signal-to-noise: every repo has clear purpose | ||
| - Pinned repos do the navigation work | ||
| - Setup tooling (`setup-bun`) is a first-class citizen in the org | ||
| **When to use**: Single primary product with companion tooling. Avoid repo sprawl that dilutes contributor attention. | ||
| ### The Ecosystem Org (denoland / Deno) | ||
| - 216+ repos: runtime, standard library, framework (Fresh), toolchains, bindings | ||
| - Standard library (`deno_std`) is its own monorepo, separately versioned | ||
| - Distinct registries: Deno now publishes std to JSR (JavaScript Registry) alongside npm | ||
| - Verified domain lends authority to the org | ||
| **When to use**: Platform-level products where many independent but related modules need separate release cycles. | ||
| ### The Framework Org (withastro / Astro) | ||
| - ~51 repos: core framework, docs site, compiler (separate language), roadmap (RFC process), deployment action, Starlight docs builder | ||
| - `roadmap` repo used for public RFCs and planning — community-facing project management | ||
| - pnpm workspaces for monorepo dependency management | ||
| - `astro-action` for GitHub Pages deployment | ||
| **When to use**: Frameworks with distinct subsystems (compiler, runtime, CLI) that benefit from separate contribution histories but shared releases. | ||
| ### The Working Group Org (tauri-apps) | ||
| - Working groups as GitHub Teams: `wg-tauri`, `wg-devops` | ||
| - CODEOWNERS maps paths to working groups | ||
| - Monorepo with clear subsystem ownership | ||
| **When to use**: Mature projects with specialized contributor groups. Teams enforce review requirements automatically. | ||
| ### The Utility Library Ecosystem (unjs / sindresorhus) | ||
| - Dozens to hundreds of small, focused packages | ||
| - Each package is its own repo with its own release cycle | ||
| - Org profile README serves as the package index | ||
| - Contributing to any package follows org-wide defaults from `.github` | ||
| **When to use**: Utility ecosystems where each package solves one problem and has independent users. | ||
| ## Contributing Guide Patterns | ||
| ### Setup Complexity Tiers | ||
| **Lightweight** (JS/TS projects like Astro): | ||
| - Requires: Node >=22.12, pnpm >=10.28 | ||
| - GitHub Codespaces support for zero-install onboarding | ||
| - Single `pnpm install && pnpm build` setup | ||
| **Heavy** (compiled projects like Bun): | ||
| - Requires: LLVM 21.1.8 (specific version), Zig (auto-installed by build script) | ||
| - 10-30 minute first setup expectation set explicitly | ||
| - Debug build alias (`bun bd`) documented prominently | ||
| ### Priority Labels | ||
| Astro uses a p1-p5 priority scale for issues. This is more expressive than simple priority/non-priority and helps maintainers triage community PRs. | ||
| ### Branch Targeting Conventions | ||
| Biome targets changes to `main` or `next` depending on stability. This should be explicitly documented — contributors who target the wrong branch create merge conflicts. | ||
| ### AI Contribution Policies | ||
| Emerging pattern (Biome, OXC): explicit AI usage disclosure in CONTRIBUTING.md: | ||
| - **Biome approach**: AI disclosure as the first section — sets tone before anything else | ||
| - **OXC approach**: AI accountability policy — contributor is responsible for all AI-generated code quality, must review and test it, cannot use AI generation as excuse for quality issues | ||
| This is becoming a community health default to consider for new orgs in 2025+. | ||
| ### Just Commands (Biome pattern) | ||
| Using `just` (Justfile) for project commands standardizes the contributor experience across platforms: | ||
| ``` | ||
| just build | ||
| just test | ||
| just lint | ||
| ``` | ||
| Avoids npm script confusion and works identically in all shells. | ||
| ### Changesets for Versioning | ||
| Biome and others use Changesets for coordinated multi-package version management. Contributors add a changeset file alongside their PR describing the change type (major/minor/patch), and release automation assembles the changelog. | ||
| ## Common Pitfalls | ||
| | Pitfall | Why It Happens | How to Avoid | | ||
| |---------|---------------|--------------| | ||
| | Org defaults silently overridden | Repo has any file in `.github/ISSUE_TEMPLATE/` | Audit all repos; prefer org defaults where possible | | ||
| | `.github` repo is private | Created as private by default | Must be public for defaults to cascade | | ||
| | CODEOWNERS pattern order wrong | Assuming first match wins | Remember: last matching pattern wins | | ||
| | Reusable workflow permissions escalation | Caller tries to grant more than caller has | Permissions are only reducible, never elevated | | ||
| | Workflow nesting beyond 10 levels | Over-decomposed reusable workflows | Flatten; use composite actions for deeply nested logic | | ||
| | Pinned repos not updated post-launch | Set once and forgotten | Review pins at each major release | | ||
| | Contributing guide assumes one platform | Windows contributors fail silently | Test setup steps on Windows or document platform limits | | ||
| ## Best Practices | ||
| 1. **Create `.github` repo first** — Before any other repo. It's the org's foundation. (Source: GitHub Docs) | ||
| 2. **Pin repos strategically** — Primary product + setup action + docs + roadmap covers most navigation needs. (Source: oven-sh, withastro patterns) | ||
| 3. **Use working groups as GitHub Teams** — Map CODEOWNERS to teams, not individuals. Individuals leave; teams persist. (Source: tauri-apps) | ||
| 4. **Set contributing guide setup time expectations explicitly** — "This takes 10-30 minutes" prevents contributor abandonment. (Source: Bun contributing guide) | ||
| 5. **Add AI contribution policy early** — Easier to establish norms before your first AI-assisted PR than after. (Source: Biome, OXC) | ||
| 6. **Use `secrets: inherit` for org-internal reusable workflows** — Avoids secret enumeration and future maintenance. (Source: GitHub Actions docs) | ||
| 7. **Separate the standard library / docs into its own repo** — Deno, Astro, and others all do this. Separate release cadence, separate contributors. (Source: denoland, withastro) | ||
| 8. **Use a `roadmap` repo for public RFCs** — Gives contributors a place to discuss direction without polluting the main issue tracker. (Source: withastro) | ||
| 9. **Verify your org's domain** — GitHub org domain verification adds authority and enables SAML SSO. (Source: denoland example) | ||
| 10. **Consider Changesets from day one** — Retrofitting automated versioning is painful; starting with it is cheap. (Source: Biome pattern) | ||
| ## Org Structure Decision Guide | ||
| ``` | ||
| Single product? | ||
| ├── Yes → Focused minimal org (oven-sh model) | ||
| │ Repos: product + setup-action + homebrew-tap + awesome-list | ||
| └── No → Multiple products? | ||
| ├── Related subsystems → Framework org (withastro model) | ||
| │ Repos: core + compiler + docs + action + roadmap | ||
| ├── Many small utilities → Ecosystem org (unjs model) | ||
| │ Each utility = own repo, org README = index | ||
| └── Large platform → Ecosystem org (denoland model) | ||
| Repos: runtime + std + frameworks + toolchains | ||
| ``` | ||
| ## Further Reading | ||
| | Resource | Type | Why Recommended | | ||
| |----------|------|-----------------| | ||
| | [GitHub: Default community health files](https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/creating-a-default-community-health-file) | Official Docs | Primary reference for `.github` repo mechanics | | ||
| | [GitHub: Reusing workflows](https://docs.github.com/en/actions/using-workflows/reusing-workflows) | Official Docs | Complete `workflow_call` reference with constraints | | ||
| | [GitHub: Starter workflows](https://docs.github.com/en/actions/using-workflows/creating-starter-workflows-for-your-organization) | Official Docs | How to create org-level workflow templates | | ||
| | [withastro org](https://github.com/withastro) | Example | Framework org archetype with roadmap pattern | | ||
| | [tauri-apps CODEOWNERS](https://github.com/tauri-apps/tauri/blob/dev/.github/CODEOWNERS) | Example | Working group CODEOWNERS pattern | | ||
| | [biomejs CONTRIBUTING.md](https://github.com/biomejs/biome/blob/main/CONTRIBUTING.md) | Example | AI disclosure + just commands pattern | | ||
| | [oxc-project CONTRIBUTING.md](https://github.com/oxc-project/oxc/blob/main/CONTRIBUTING.md) | Example | AI accountability policy | | ||
| | [denoland org](https://github.com/denoland) | Example | Large ecosystem org structure | | ||
| | [unjs org](https://github.com/unjs) | Example | Utility ecosystem, many small packages | | ||
| | [GitHub: About CODEOWNERS](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners) | Official Docs | CODEOWNERS syntax and precedence rules | | ||
| --- | ||
| *This guide was synthesized from 18 sources. See `resources/github-org-structure-patterns-sources.json` for full source list.* |
| # Learning Guide: Kiro Supervised Mode, Autopilot Mode, and Approval Workflows | ||
| **Generated**: 2026-03-02 | ||
| **Sources**: 6 resources analyzed | ||
| **Depth**: brief | ||
| --- | ||
| ## Prerequisites | ||
| - Familiarity with Kiro IDE or CLI (see kiro-cli-agentic-files-settings.md for overview) | ||
| - Understanding of AI-assisted code generation concepts | ||
| - Knowledge of version control and code diffs | ||
| - Basic understanding of file editing workflows | ||
| ## TL;DR | ||
| - **Autopilot Mode (default)**: Kiro executes tasks autonomously without approval; you can view diffs, revert changes, or interrupt, but edits proceed immediately | ||
| - **Supervised Mode**: Kiro pauses after each turn with file edits, presenting changes as reviewable hunks; you accept/reject/discuss individual sections before proceeding | ||
| - **File Edit Approval**: Presented as granular hunks per file; you can accept all, reject all, or approve specific sections | ||
| - **Command Execution**: Treated separately from file edits; commands run without approval in both modes but can be viewed in execution logs | ||
| - **Mode Switching**: Can switch between modes mid-session via IDE settings or CLI session context | ||
| - **Agent Behavior**: Mode choice affects how agents approach task decomposition and decision-making; supervised mode encourages smaller, reviewable steps | ||
| --- | ||
| ## Core Concepts | ||
| ### Autopilot Mode (Default Behavior) | ||
| Autopilot is Kiro's default operating mode, designed for speed and autonomy. | ||
| **Characteristics:** | ||
| - Agent executes tasks end-to-end without requesting approval | ||
| - Creates and modifies files across multiple locations in the codebase | ||
| - Runs shell commands as needed | ||
| - Makes architectural decisions and trade-offs independently | ||
| - Completes the workflow in a single or minimal number of turns | ||
| **User Control Points:** | ||
| - View diffs before changes are written to disk (read-only inspection) | ||
| - Revert all changes with one command if needed | ||
| - Interrupt execution mid-task | ||
| - Modify the task prompt and restart | ||
| **When Code is Modified:** | ||
| Files are written to disk immediately upon agent completion. There is no intermediate approval gate; the agent's turn completes and changes persist. | ||
| **Agent Reasoning:** | ||
| In autopilot mode, agents typically plan comprehensive solutions and execute them with confidence, knowing they can iterate if issues arise. This leads to faster task completion but less granular control over intermediate decisions. | ||
| *Source: kiro.dev/docs/chat/autopilot* | ||
| ### Supervised Mode (Approval-Required Behavior) | ||
| Supervised mode requires human review and approval for any file edits before they become permanent. | ||
| **Characteristics:** | ||
| - Agent completes a turn (thinking, planning, proposing edits) | ||
| - Yields control to the user with proposed file changes | ||
| - Changes are presented as individual hunks (contiguous blocks of edits within a file) | ||
| - User reviews and approves/rejects changes before persistence | ||
| - Agent does not proceed until explicit approval or rejection | ||
| **User Actions:** | ||
| - Accept all changes for the turn | ||
| - Reject all changes for the turn | ||
| - Accept individual hunks within a file | ||
| - Reject individual hunks within a file | ||
| - Request discussion or clarification on specific changes | ||
| - Modify a rejection reason and ask agent to revise | ||
| **File Edit Flow:** | ||
| ``` | ||
| Agent proposes edits → Kiro pauses → User reviews hunks → | ||
| [Accept/Reject per hunk] → Changes persist (if approved) → | ||
| Agent continues or cycle repeats | ||
| ``` | ||
| **Command Execution in Supervised Mode:** | ||
| Commands (shell invocations) are still executed in supervised mode; they are not subject to approval. However, command output is logged and visible in the execution history, allowing the user to understand what the agent did. | ||
| **Agent Reasoning:** | ||
| In supervised mode, agents tend to approach problems more cautiously, proposing smaller, reviewable chunks of work. They may break a task into more steps, making their reasoning transparent and easier to verify. | ||
| *Source: kiro.dev/docs/chat* | ||
| --- | ||
| ## File Edit Approval Details | ||
| ### Hunk-Based Review | ||
| File edits in supervised mode are grouped into **hunks** - contiguous blocks of changes within a single file. | ||
| **Hunk Structure:** | ||
| - File path and filename | ||
| - Line range affected (e.g., lines 45-62) | ||
| - Context lines (surrounding unchanged code for orientation) | ||
| - Proposed changes (insertions, deletions, modifications) | ||
| - Diff format (similar to `git diff` or unified diff format) | ||
| **Hunk Approval Options:** | ||
| 1. **Accept this hunk** - Approve the specific change block | ||
| 2. **Reject this hunk** - Decline the change; propose alternative in next turn | ||
| 3. **Modify and discuss** - Ask agent to adjust the hunk before approval | ||
| 4. **Accept all remaining** - Approve all hunks in this turn | ||
| 5. **Reject all remaining** - Decline all hunks in this turn | ||
| ### Per-File Review Workflow | ||
| Kiro presents changes organized by file: | ||
| ``` | ||
| File: src/api/routes.ts | ||
| ├─ Hunk 1 (lines 12-28): Add new route handler | ||
| │ [Accept] [Reject] [Discuss] | ||
| ├─ Hunk 2 (lines 45-51): Update imports | ||
| │ [Accept] [Reject] [Discuss] | ||
| └─ Hunk 3 (lines 89-94): Fix type annotations | ||
| [Accept] [Reject] [Discuss] | ||
| File: src/api/types.ts | ||
| └─ Hunk 1 (lines 3-15): Define new interface | ||
| [Accept] [Reject] [Discuss] | ||
| ``` | ||
| ### Partial Approval | ||
| You are not required to approve all hunks in a turn. Mixed approval is supported: | ||
| - Accept hunks 1 and 3 of file A | ||
| - Reject hunk 2 of file A | ||
| - Accept all hunks of file B | ||
| Rejected hunks are not written to disk. The agent can then be asked to revise them in the next turn. | ||
| ### Acceptance Persistence | ||
| Once a hunk is accepted, it is immediately written to the target file. Rejected hunks do not affect the file. The agent sees the rejection and can propose a different approach in its next response. | ||
| *Source: kiro.dev/docs/chat, synthesized from supervised mode documentation* | ||
| --- | ||
| ## Command Execution Approval | ||
| ### Shell Command Execution | ||
| Commands executed via the `execute_bash` or `execute_shell` tool are not subject to approval in either mode (autopilot or supervised). | ||
| **Execution Behavior:** | ||
| - Agent decides when to run commands | ||
| - Commands execute immediately when invoked | ||
| - Output is captured and shown to the user | ||
| - No pre-execution approval is required | ||
| **Rationale:** | ||
| Commands are typically read-only operations (file listing, dependency checks, test runs) or setup steps (installing dependencies, creating directories). Blocking on every command would create excessive friction. Dangerous operations (deletes, force pushes) are typically not attempted by agents without explicit user direction. | ||
| ### Logging and Observability | ||
| Command execution details are logged in the session history: | ||
| - **Command text** - Exact shell command executed | ||
| - **Exit code** - Return status (0 = success, non-zero = failure) | ||
| - **STDOUT** - Standard output (typically long) | ||
| - **STDERR** - Standard error output (if any) | ||
| - **Duration** - How long the command took | ||
| Users can review these logs to understand what the agent did and catch unintended side effects (e.g., a command that created files outside the intended directory). | ||
| ### Command Hooks (Advanced) | ||
| If you need to intercept or block certain commands, you can use Kiro Hooks with `PreToolUse` event type to validate commands before execution. A hook can return exit code 2 to block the tool invocation. | ||
| *Source: kiro.dev/docs/chat, kiro.dev/docs/hooks* | ||
| --- | ||
| ## Switching Modes Mid-Session | ||
| ### IDE Mode Switching | ||
| In the Kiro IDE, you can switch between autopilot and supervised modes during a session. | ||
| **Steps:** | ||
| 1. Open Settings (`Cmd+,` on Mac / `Ctrl+,` on Windows/Linux) | ||
| 2. Search for "Autopilot" or "Approval" | ||
| 3. Toggle the **Autopilot Toggle** setting | ||
| 4. The change takes effect immediately for the next agent response | ||
| **Persistence:** | ||
| The mode selection is stored in IDE settings and persists across sessions. | ||
| ### CLI Mode Switching | ||
| The Kiro CLI does not have built-in supervised mode toggle in the same way as the IDE. However, you can: | ||
| 1. **Start a new session** in the opposite mode (manual restart) | ||
| 2. **Use hooks** to add approval-like behavior for specific tools | ||
| 3. **Use custom agents** with different configurations | ||
| For CLI users wanting approval workflows, the recommended approach is to use hooks that validate tool invocations before execution. | ||
| ### Mid-Session Behavior | ||
| If you switch from **autopilot to supervised** mid-session: | ||
| - The next agent turn will pause for approval instead of auto-executing | ||
| - Previous turns' changes remain in place (already persisted) | ||
| - You start seeing approval prompts for hunks going forward | ||
| If you switch from **supervised to autopilot** mid-session: | ||
| - The next agent turn will auto-execute without pausing for approval | ||
| - No retroactive approval is requested for prior edits | ||
| - You lose granular control for subsequent turns | ||
| ### Recommendation | ||
| Most users do not switch modes mid-session because they have different workflows: | ||
| - **Autopilot**: For prototyping, familiar codebases, rapid iteration | ||
| - **Supervised**: For learning, critical systems, code review workflows | ||
| Switching reflects a change in your comfort level with the agent's decisions. | ||
| *Source: kiro.dev/docs/chat, kiro.dev/docs/getting-started* | ||
| --- | ||
| ## How Modes Affect Agent Behavior | ||
| ### Autopilot Agent Strategy | ||
| In autopilot mode, agents: | ||
| - **Plan comprehensively** - Think through multi-step solutions before acting | ||
| - **Execute decisively** - Make design choices without hesitation | ||
| - **Batch operations** - Group related changes to minimize turns | ||
| - **Assume trust** - Proceed with confidence that you'll catch issues in review | ||
| - **Iterate externally** - If a turn doesn't work, iterate based on user feedback in the next turn | ||
| **Example Flow (Autopilot):** | ||
| ``` | ||
| User: "Add user authentication to the API" | ||
| ↓ | ||
| Agent [Turn 1]: | ||
| - Analyzes codebase architecture | ||
| - Designs auth strategy | ||
| - Implements middleware, routes, types | ||
| - Updates package.json | ||
| - Writes all changes to disk | ||
| - Reports completion | ||
| ↓ | ||
| User: Views diffs, runs tests, decides if satisfied | ||
| ``` | ||
| ### Supervised Agent Strategy | ||
| In supervised mode, agents: | ||
| - **Break down tasks** - Decompose into smaller, reviewable steps | ||
| - **Explain reasoning** - Provide context for each change | ||
| - **Request micro-approvals** - After each turn, pause for review | ||
| - **Adapt to feedback** - Respond to rejected hunks with revisions | ||
| - **Build incrementally** - Let user guide direction through approvals | ||
| **Example Flow (Supervised):** | ||
| ``` | ||
| User: "Add user authentication to the API" | ||
| ↓ | ||
| Agent [Turn 1]: | ||
| - Proposes auth middleware structure | ||
| - Shows hunk for new middleware file | ||
| ↓ | ||
| User: Reviews, accepts hunk | ||
| ↓ | ||
| Agent [Turn 2]: | ||
| - Proposes route definitions | ||
| - Shows hunks for new routes | ||
| ↓ | ||
| User: Reviews, accepts some hunks, rejects others | ||
| ↓ | ||
| Agent [Turn 3]: | ||
| - Revises rejected hunks based on feedback | ||
| - Shows updated route definitions | ||
| ↓ | ||
| [Cycle continues until complete...] | ||
| ``` | ||
| ### Tool Usage Differences | ||
| **Autopilot Mode:** | ||
| - Uses more tools per turn | ||
| - Chains operations (read, analyze, write, test) | ||
| - Makes independent decisions about which files to modify | ||
| **Supervised Mode:** | ||
| - Typically uses fewer tools per turn | ||
| - Focuses on single concerns (e.g., "update this file type") | ||
| - Waits for user approval before deciding next steps | ||
| ### Architectural Decision-Making | ||
| **Autopilot:** | ||
| - Agent might choose between multiple architectural patterns and implement the one it judges best | ||
| - Trade-offs are made implicitly (e.g., choosing Zod over Joi for validation) | ||
| **Supervised:** | ||
| - Agent might ask for direction on architectural choices | ||
| - Presents alternatives and waits for user input | ||
| - Reduces risk of committing to the wrong approach | ||
| *Source: synthesized from kiro.dev/docs/chat, mode documentation, and user workflow guides* | ||
| --- | ||
| ## Common Pitfalls | ||
| | Pitfall | Why It Happens | How to Avoid | | ||
| |---------|---------------|--------------| | ||
| | Not realizing changes auto-persist in autopilot | Expectation of approval before changes are written | Use supervised mode if you want review before persistence; always check diffs before agent finishes | | ||
| | Command execution surprises | Commands run without approval; unexpected side effects occur | Review command output carefully; use hooks to validate dangerous commands; keep commands simple | | ||
| | Mode confusion between IDE and CLI | IDE has clear toggle; CLI doesn't expose mode selection | For CLI, understand that supervised mode requires hooks or external tooling; use IDE for approval workflows | | ||
| | Partial approval deadlock | User rejects all hunks, agent re-proposes identical changes | Provide clear feedback on why hunks were rejected; ask agent to revise approach, not just the code | | ||
| | Missing mid-session mode switch awareness | User switches to supervised mode expecting retrospective approval | Understand that retroactive approval isn't possible; switching only affects future turns | | ||
| | Approval workflow slowing down development | Over-reliance on hunk-by-hunk approval for simple changes | Use autopilot for trusted tasks; reserve supervised mode for high-risk changes | | ||
| --- | ||
| ## Best Practices | ||
| ### Choosing Modes | ||
| 1. **Start in supervised mode if:** | ||
| - You're learning Kiro | ||
| - Working on unfamiliar or critical code | ||
| - Building features with undefined requirements | ||
| - Team code review is a requirement | ||
| 2. **Use autopilot mode if:** | ||
| - You have high confidence in the agent | ||
| - Working on a familiar codebase | ||
| - Speed is the priority | ||
| - Changes are low-risk (new feature, not core logic) | ||
| 3. **Hybrid approach:** | ||
| - Use supervised mode for architectural decisions | ||
| - Switch to autopilot for implementation details once direction is set | ||
| - Use supervised mode again for integration and testing phases | ||
| ### File Approval Workflow | ||
| 4. **Always review hunks in context:** | ||
| - Don't accept hunks without reading the surrounding code | ||
| - Use the diff view to understand the change | ||
| - Spot-check for subtle bugs (off-by-one errors, missing nullchecks) | ||
| 5. **Provide clear rejection feedback:** | ||
| - Don't just reject; explain why (e.g., "This violates our naming convention" vs just "No") | ||
| - Suggest the alternative you want | ||
| - Reference your project's steering files if available | ||
| 6. **Use Accept All selectively:** | ||
| - Only for low-risk changes or when you've already approved similar hunks | ||
| - Prefer granular approval on first exposure to agent's code | ||
| ### Agent Behavior Optimization | ||
| 7. **Prime the agent for supervised mode:** | ||
| - When starting supervised session, mention it explicitly: "Review carefully before proceeding" | ||
| - Break down complex requirements into phases | ||
| 8. **Understand command context:** | ||
| - Commands are not subject to approval; log them for your review | ||
| - If a command fails, the agent will attempt recovery | ||
| - Dangerous operations should be scoped in steering files | ||
| 9. **Combine modes with steering:** | ||
| - Use `.kiro/steering/` files to define approval criteria (e.g., "all API changes require explicit approval") | ||
| - Supervised mode respects steering; agent will pause at critical areas | ||
| *Source: synthesized from kiro.dev documentation and best practices guides* | ||
| --- | ||
| ## Further Reading | ||
| | Resource | Type | Why Recommended | | ||
| |----------|------|-----------------| | ||
| | [kiro.dev/docs/chat](https://kiro.dev/docs/chat/) | Official Docs | Comprehensive chat modes documentation | | ||
| | [kiro.dev/docs/chat/autopilot](https://kiro.dev/docs/chat/autopilot/) | Official Docs | Autopilot mode details and controls | | ||
| | [kiro.dev/docs/chat/vibe](https://kiro.dev/docs/chat/vibe/) | Official Docs | Vibe mode (creative mode) paired with autopilot/supervised | | ||
| | [kiro.dev/docs/steering](https://kiro.dev/docs/steering/) | Official Docs | Steering files for guiding agent behavior | | ||
| | [kiro.dev/docs/hooks](https://kiro.dev/docs/hooks/) | Official Docs | Hooks for approval-like workflows in CLI | | ||
| | [kiro.dev/docs/specs](https://kiro.dev/docs/specs/) | Official Docs | Specs as complement to supervised mode workflows | | ||
| | [Kiro IDE Getting Started](https://kiro.dev/docs/getting-started/first-project/) | Tutorial | Hands-on walkthrough using IDE modes | | ||
| --- | ||
| *This guide was synthesized from 6 primary sources including official Kiro documentation and architectural references. See `resources/kiro-supervised-autopilot-sources.json` for the full source list with quality scores.* |
| # Learning Guide: Documentation & Website Architecture for Multi-Product Open Source Organizations | ||
| **Generated**: 2026-02-21 | ||
| **Sources**: 42 resources analyzed (5 fetched live, 37 from verified domain knowledge) | ||
| **Depth**: deep | ||
| ## Prerequisites | ||
| - Familiarity with static site generators (any one of: Next.js, Astro, VitePress) | ||
| - Basic understanding of DNS, hosting, and GitHub Pages | ||
| - Understanding of the difference between a "product" and a "feature/plugin" | ||
| ## TL;DR | ||
| - Multi-product orgs use one of three patterns: **unified portal** (HashiCorp), **separate branded sites** (JetBrains), or **hub-and-spoke** (Vercel). The right choice depends on how related the products are. | ||
| - For small orgs (1-5 people), a single site with product tabs/sections (Mintlify, Starlight, or Docusaurus multi-instance) beats maintaining separate sites. | ||
| - The key architectural decision is: **does each product deserve its own domain/subdomain, or are they sections of one site?** If a product has its own CLI, its own users, and could exist independently, it deserves its own site (or at minimum a subdomain). | ||
| - Plugin/extension catalogs need: search, categories, install commands, compatibility badges, and usage stats. A simple JSON registry with a static frontend works at small scale. | ||
| - SEO strongly favors a unified domain with product subpaths over scattered subdomains for small orgs. | ||
| ## Core Concepts | ||
| ### 1. The Three Architecture Patterns | ||
| Multi-product organizations structure their documentation using one of three fundamental patterns: | ||
| **Pattern A: Unified Developer Portal** | ||
| One site, all products share navigation, search, and design system. Products are sections/tabs. | ||
| - Example: **HashiCorp Developer** (developer.hashicorp.com) - Terraform, Vault, Consul, Nomad, Packer, Waypoint all under one roof. Product switcher in sidebar. Unified search across all products. Shared tutorials section. | ||
| - Example: **Supabase** (supabase.com/docs) - Database, Auth, Storage, Edge Functions, Realtime, Vector are all sections of one docs site with left sidebar navigation. | ||
| - Example: **Stripe** (stripe.com/docs) - Payments, Billing, Connect, Terminal, Identity all under one docs domain. | ||
| - Best for: Products that share users and are often used together. | ||
| **Pattern B: Separate Branded Sites** | ||
| Each product gets its own domain and fully independent docs site. | ||
| - Example: **JetBrains** - IntelliJ (jetbrains.com/help/idea/), WebStorm (jetbrains.com/help/webstorm/), Rider (jetbrains.com/help/rider/). Each has its own sidebar, its own structure, its own search. A top-level product picker links between them. | ||
| - Example: **Atlassian** - Jira, Confluence, Bitbucket each have separate doc sites under atlassian.com/software/*/docs. | ||
| - Best for: Products with distinct user bases that rarely overlap. | ||
| **Pattern C: Hub-and-Spoke** | ||
| A central landing site links to independent product sites, each with their own domain. | ||
| - Example: **Vercel ecosystem** - vercel.com/docs is the hub. Next.js lives at nextjs.org/docs (completely separate site, separate design). Turbo at turbo.build/repo/docs. SWC at swc.rs. Each product has its own identity, but Vercel docs cross-links to them. | ||
| - Example: **oven-sh/Bun** - Single product but uses hub-and-spoke internally: bun.sh/docs is one site with cards linking to Runtime, Package Manager, Test Runner, Bundler as distinct sections with their own sidebar trees. | ||
| - Best for: Products that have independent brand identities but share an organizational umbrella. | ||
| ### 2. Unified Landing Page vs Per-Product Sites | ||
| **Decision framework:** | ||
| | Factor | Unified Site | Separate Sites | | ||
| |--------|-------------|----------------| | ||
| | Products share users | Strong yes | Not needed | | ||
| | Products used together | Strong yes | Not needed | | ||
| | Products have independent brands | Awkward | Natural | | ||
| | Team size < 5 | Practical | Maintenance burden | | ||
| | SEO authority | Concentrated | Diluted | | ||
| | Design consistency | Automatic | Requires effort | | ||
| | Independent release cycles | Harder | Natural | | ||
| **The hybrid approach** (most common for small orgs): One landing page at org-domain.com that acts as a "portal" with cards/links to each product. Products that are closely related share a docs site. Products with independent identity get subdomains or separate domains. | ||
| For **agent-sh** specifically, a recommended structure: | ||
| ``` | ||
| agent-sh.dev/ # Org landing page (portal) | ||
| /docs/agentsys/ # Marketplace & installer docs | ||
| /docs/plugins/ # Plugin catalog (next-task, ship, etc.) | ||
| /docs/agent-core/ # Infrastructure docs | ||
| agnix.dev/ # Separate domain - it's a full product | ||
| /docs/ # CLI, LSP, IDE extension docs | ||
| /docs/agentsys-integration/ # How to use agnix via agentsys | ||
| web-ctl.dev/ (or agent-sh.dev/docs/web-ctl/) # Depends on independence level | ||
| ``` | ||
| ### 3. Shared Navigation Patterns | ||
| **Product Switcher**: A dropdown or tab bar at the top of the docs site that lets you jump between products. HashiCorp uses a persistent header with product icons. Supabase uses sidebar grouping. | ||
| **Cross-Product Discovery**: Key patterns: | ||
| - **"Related products" cards** at the bottom of pages (Stripe does this well) | ||
| - **Unified search** that returns results from all products with product badges | ||
| - **Getting started guides** that show how products work together | ||
| - **Architecture diagrams** showing how products compose | ||
| **Shared design system**: Critical for perceived cohesion. All products should use: | ||
| - Same typography, colors, code block styling | ||
| - Same component library (callouts, tabs, cards) | ||
| - Same header/footer with org branding | ||
| - Product-specific accent colors for differentiation | ||
| ### 4. Documentation Platforms Comparison for Multi-Product | ||
| | Platform | Multi-Product Support | Best For | Limitations | | ||
| |----------|----------------------|----------|-------------| | ||
| | **Docusaurus** | Multi-instance docs plugin; each product is a "docs instance" with independent versioning and sidebar | Meta-scale orgs, React ecosystem | Config complexity grows with products | | ||
| | **Starlight (Astro)** | Groups in sidebar config, multiple content collections | Performance-focused sites, Markdown-heavy | No built-in multi-instance like Docusaurus | | ||
| | **Nextra** | File-based routing with `_meta.json` per directory | Next.js projects, simple multi-section | Less opinionated about multi-product | | ||
| | **VitePress** | Multiple sidebars via route-based config, each product path gets its own sidebar | Vue ecosystem, fast builds | Manual sidebar config per product | | ||
| | **Mintlify** | Tabs + groups in `mint.json`; navigation anchors for product switching | Startups, API-heavy docs, beautiful defaults | Hosted/paid, less customizable | | ||
| | **ReadMe** | Multi-version, multi-project via dashboard | API documentation focus | Hosted/paid, opinionated structure | | ||
| | **GitBook** | Spaces (one per product) grouped into Collections | Non-technical teams, wiki-style | Limited customization, hosted | | ||
| | **Custom (Next.js/Astro)** | Full control over everything | Large orgs with specific needs | Build everything yourself | | ||
| **Recommendation for small orgs (1-5 people)**: Starlight or Mintlify. Starlight is free, fast, and handles multi-section well with Astro's content collections. Mintlify provides beautiful defaults with zero config but costs money. | ||
| **Recommendation for medium orgs**: Docusaurus multi-instance if you need independent versioning per product. VitePress if you want simplicity and speed. | ||
| ### 5. Docusaurus Multi-Instance Deep Dive | ||
| Docusaurus is the most mature option for multi-product docs. Key features: | ||
| ```javascript | ||
| // docusaurus.config.js | ||
| module.exports = { | ||
| plugins: [ | ||
| [ | ||
| '@docusaurus/plugin-content-docs', | ||
| { | ||
| id: 'agentsys', | ||
| path: 'docs/agentsys', | ||
| routeBasePath: 'docs/agentsys', | ||
| sidebarPath: './sidebars/agentsys.js', | ||
| }, | ||
| ], | ||
| [ | ||
| '@docusaurus/plugin-content-docs', | ||
| { | ||
| id: 'agnix', | ||
| path: 'docs/agnix', | ||
| routeBasePath: 'docs/agnix', | ||
| sidebarPath: './sidebars/agnix.js', | ||
| }, | ||
| ], | ||
| ], | ||
| themeConfig: { | ||
| navbar: { | ||
| items: [ | ||
| { type: 'dropdown', label: 'Products', items: [ | ||
| { to: '/docs/agentsys', label: 'AgentSys' }, | ||
| { to: '/docs/agnix', label: 'Agnix' }, | ||
| ]}, | ||
| ], | ||
| }, | ||
| }, | ||
| }; | ||
| ``` | ||
| Each instance gets: | ||
| - Independent versioning (agnix v1.0 vs agentsys v3.7) | ||
| - Separate sidebar | ||
| - Separate search scope | ||
| - Shared theme and components | ||
| ### 6. Starlight (Astro) Multi-Product Setup | ||
| ```javascript | ||
| // astro.config.mjs | ||
| import starlight from '@astrojs/starlight'; | ||
| export default { | ||
| integrations: [ | ||
| starlight({ | ||
| title: 'agent-sh', | ||
| sidebar: [ | ||
| { | ||
| label: 'AgentSys', | ||
| items: [ | ||
| { label: 'Getting Started', link: '/agentsys/getting-started' }, | ||
| { label: 'Plugin Catalog', link: '/agentsys/plugins' }, | ||
| { label: 'CLI Reference', link: '/agentsys/cli' }, | ||
| ], | ||
| }, | ||
| { | ||
| label: 'Agnix', | ||
| items: [ | ||
| { label: 'Overview', link: '/agnix/overview' }, | ||
| { label: 'CLI', link: '/agnix/cli' }, | ||
| { label: 'LSP', link: '/agnix/lsp' }, | ||
| { label: 'IDE Extensions', link: '/agnix/ide' }, | ||
| ], | ||
| }, | ||
| { | ||
| label: 'Plugins', | ||
| autogenerate: { directory: 'plugins' }, | ||
| }, | ||
| ], | ||
| }), | ||
| ], | ||
| }; | ||
| ``` | ||
| ### 7. GitHub Pages vs Custom Domain vs Docs-as-Code | ||
| **GitHub Pages** (org.github.io): | ||
| - Free, auto-deploys from repo | ||
| - Org site: `agent-sh.github.io` from the `agent-sh.github.io` repo | ||
| - Project sites: `agent-sh.github.io/agentsys` from the agentsys repo | ||
| - Custom domain: point `agent-sh.dev` to GitHub Pages | ||
| - Limitation: one site per repo, no server-side rendering | ||
| - Works great with static site generators (Starlight, Docusaurus, VitePress) | ||
| **Custom domain with GitHub Pages**: Best of both worlds. | ||
| ``` | ||
| agent-sh.dev -> agent-sh.github.io (org site, portal) | ||
| agent-sh.dev/docs -> served from same org site repo | ||
| agnix.dev -> separate repo with CNAME | ||
| ``` | ||
| **Docs-as-code pattern**: | ||
| - Docs live in the same repo as code (or a dedicated docs repo) | ||
| - PRs for docs changes, same review process as code | ||
| - CI builds and deploys on merge | ||
| - Every product repo has a `/docs` folder, build system aggregates | ||
| **Hosting alternatives**: | ||
| - **Vercel/Netlify**: Free tier, preview deployments for doc PRs, faster builds than GitHub Pages | ||
| - **Cloudflare Pages**: Free, fast, unlimited bandwidth | ||
| - **GitHub Pages**: Free, simple, good enough for most | ||
| ### 8. Plugin/Extension Catalogs and Marketplace Pages | ||
| **Exemplar marketplace architectures:** | ||
| **Terraform Registry** (registry.terraform.io): | ||
| - Categories: Providers, Modules, Policies | ||
| - Each entry: name, description, version, download count, verified badge | ||
| - Detail page: README, inputs/outputs, dependencies, usage example | ||
| - Search with filters | ||
| - Backed by a GitHub-based publishing flow | ||
| **VS Code Marketplace** (marketplace.visualstudio.com): | ||
| - Categories, trending, featured | ||
| - Install count, ratings, last updated | ||
| - Detail page: README (from repo), changelog, install button | ||
| - Deep linking: `ext install publisher.extension` | ||
| **Homebrew Formulae** (formulae.brew.sh): | ||
| - Simple catalog: name, description, install command | ||
| - Analytics: install counts over 30/90/365 days | ||
| - Static site generated from formula JSON data | ||
| - No ratings/reviews, just facts | ||
| **For agent-sh plugin catalog**, recommended approach: | ||
| ``` | ||
| /plugins/ # Catalog page | ||
| /plugins/next-task/ # Plugin detail page | ||
| /plugins/ship/ | ||
| /plugins/enhance/ | ||
| ... | ||
| ``` | ||
| Each plugin page should show: | ||
| - Name, one-line description | ||
| - Install command: `npx agentsys install next-task` | ||
| - Compatibility: which AI tools it works with | ||
| - What it includes: agents, skills, commands | ||
| - README content (pulled from plugin repo) | ||
| - Version, last updated, repo link | ||
| **Implementation**: A `registry.json` file (which agentsys already has) feeds a static catalog page. Build step pulls README from each plugin repo and generates pages. | ||
| ### 9. Small Org (1-5 People) Strategy | ||
| Large orgs like HashiCorp have dedicated docs teams. Small orgs need to be ruthlessly practical: | ||
| **Do:** | ||
| - Single repo for the docs site (even if products are in separate repos) | ||
| - Use a batteries-included framework (Starlight or Mintlify) | ||
| - Automate: CI deploys on merge, broken link checking, spell checking | ||
| - README-first: great READMEs in each repo, docs site supplements | ||
| - Use `/llms.txt` for AI discoverability (Bun does this at bun.com/docs/llms.txt) | ||
| **Don't:** | ||
| - Maintain separate docs sites per product until you have > 5 people | ||
| - Build custom docs infrastructure | ||
| - Version docs until you actually have breaking changes | ||
| - Create elaborate information architecture before you have content | ||
| **Pragmatic structure for agent-sh:** | ||
| Phase 1 (now): Single Starlight/Docusaurus site at agent-sh.dev | ||
| - Landing page with product cards | ||
| - /docs/agentsys/ - marketplace docs | ||
| - /docs/agnix/ - linter docs | ||
| - /plugins/ - catalog page | ||
| - Each product repo keeps its own README as the "quick start" | ||
| Phase 2 (when agnix has significant independent traction): Split agnix to agnix.dev | ||
| - Its own site, own brand colors, own domain | ||
| - Cross-links back to agent-sh.dev | ||
| - agent-sh.dev links to agnix.dev | ||
| Phase 3 (when you have a docs team): Full hub-and-spoke | ||
| - Portal at agent-sh.dev | ||
| - Each major product on its own site | ||
| ### 10. SEO and Discoverability for Multi-Product Orgs | ||
| **Domain strategy matters enormously for SEO:** | ||
| | Strategy | SEO Impact | Example | | ||
| |----------|-----------|---------| | ||
| | Subpaths (`org.dev/product/`) | Best - all authority on one domain | supabase.com/docs/auth | | ||
| | Subdomains (`product.org.dev`) | Medium - treated as separate sites by Google | docs.stripe.com | | ||
| | Separate domains (`product.dev`) | Worst for org SEO, but builds product brand | nextjs.org vs vercel.com | | ||
| **Key SEO practices:** | ||
| - Each product page needs unique title, description, and H1 | ||
| - Canonical URLs to avoid duplicate content across product sections | ||
| - Structured data (JSON-LD) for software application, FAQ | ||
| - Sitemap per product section, combined sitemap index | ||
| - Internal linking between products (Google follows these) | ||
| - `llms.txt` at root for AI crawler discoverability (emerging standard) | ||
| **For small orgs**: Keep everything on one domain. The SEO benefit of concentrated authority far outweighs the branding benefit of separate domains until you have significant traffic. | ||
| ### 11. Agent Skills Catalog / AI Tool Plugin Marketplaces | ||
| This is an emerging space. Key patterns from existing AI tool ecosystems: | ||
| **MCP Server Directory** (various community directories): | ||
| - Name, description, transport type | ||
| - Capabilities (tools, resources, prompts) | ||
| - Install/config snippet | ||
| - Compatible clients | ||
| **OpenAI GPT Store / ChatGPT Plugins** (historical): | ||
| - Category-based browsing | ||
| - Ratings and usage counts | ||
| - "Try it" button | ||
| - Manifest-based registration | ||
| **Claude Code skills** (CLAUDE.md-based): | ||
| - Convention-based discovery (slash commands) | ||
| - No central registry - skills live in repos | ||
| - Discoverability via documentation and CLAUDE.md | ||
| **For agent-sh plugin marketplace:** | ||
| ```json | ||
| // registry.json entry per plugin | ||
| { | ||
| "name": "next-task", | ||
| "description": "Master workflow: task discovery to PR merge", | ||
| "version": "3.7.0", | ||
| "commands": ["/next-task"], | ||
| "agents": ["task-discoverer", "exploration-agent", "planning-agent"], | ||
| "skills": ["discover-tasks", "validate-delivery"], | ||
| "compatibility": ["claude-code", "opencode", "codex"], | ||
| "install": "npx agentsys install next-task", | ||
| "repo": "https://github.com/agent-sh/next-task", | ||
| "tags": ["workflow", "task-management", "pr-automation"] | ||
| } | ||
| ``` | ||
| Catalog page features: | ||
| - Filter by: command type, compatible tool, category | ||
| - Sort by: popularity (install count), recently updated | ||
| - Search across names, descriptions, tags | ||
| - Quick install command copy button | ||
| - Compatibility badges (works with Claude Code, OpenCode, Codex) | ||
| ### 12. Real-World Architecture Analysis | ||
| **Bun (oven-sh)** - Single product, multiple doc sections: | ||
| - bun.sh/docs is one site | ||
| - Landing page has four cards: Runtime, Package Manager, Test Runner, Bundler | ||
| - Each section has its own sidebar tree | ||
| - Unified search across all sections | ||
| - Built with custom framework (not Docusaurus/Starlight) | ||
| - Provides `llms.txt` for AI discoverability | ||
| **Vercel** - Hub-and-spoke: | ||
| - vercel.com/docs covers the platform | ||
| - Next.js at nextjs.org (completely separate site, separate design system) | ||
| - Turbo at turbo.build (separate site) | ||
| - Cross-linking via "Related" sections | ||
| - Vercel docs reference Next.js docs but don't duplicate them | ||
| **HashiCorp** - Unified portal: | ||
| - developer.hashicorp.com is the single entry point | ||
| - Product switcher in left sidebar | ||
| - Each product has: overview, tutorials, docs, API, CLI reference | ||
| - Shared tutorial format across products | ||
| - Unified search with product filtering | ||
| - Originally separate sites (terraform.io/docs, vaultproject.io/docs), migrated to unified portal | ||
| **Supabase** - Unified with product sections: | ||
| - supabase.com/docs is one site | ||
| - Left sidebar groups: Database, Auth, Edge Functions, Realtime, Storage, AI & Vectors | ||
| - Shared design system, consistent page structure | ||
| - API reference auto-generated from OpenAPI specs | ||
| - Client library docs for multiple languages | ||
| ## Common Pitfalls | ||
| | Pitfall | Why It Happens | How to Avoid | | ||
| |---------|---------------|--------------| | ||
| | Premature site splitting | "Each product deserves its own site" before having enough content | Start unified, split when content and users justify it | | ||
| | Inconsistent design across product docs | Different people build different product docs | Establish shared component library / design system first | | ||
| | Docs rot in plugin repos | No one updates docs after initial write | CI that checks docs freshness, link checking | | ||
| | Over-engineering the catalog | Building a full marketplace when you have 13 plugins | Static JSON + generated pages. Dynamic features later | | ||
| | No cross-product navigation | Users on product A can't discover product B | Persistent header with product links on every page | | ||
| | Separate search per product | Each docs section has its own search | Unified search index across all products | | ||
| | Duplicating content across products | Same concept explained in 3 product docs | Write once, link from other products | | ||
| | Ignoring `llms.txt` and AI discoverability | Only thinking about human readers | Add llms.txt, structured metadata for AI crawlers | | ||
| ## Best Practices | ||
| 1. **Start with one site, split later** - Unified is always easier to maintain for small teams. Split only when a product has genuinely different users. (Source: HashiCorp migration from separate to unified) | ||
| 2. **Every product needs a one-liner and a hero install command** - Users decide in 5 seconds if they care. (Source: Bun, Supabase landing pages) | ||
| 3. **Use a product switcher, not just nav links** - A persistent, visible way to jump between products. (Source: HashiCorp developer portal) | ||
| 4. **README is the gateway, docs site is the depth** - Keep excellent READMEs in repos, docs site adds tutorials/guides/reference. (Source: GitHub community patterns) | ||
| 5. **Automate catalog generation from registry data** - Don't manually maintain a catalog page. Generate it from `registry.json` or equivalent. (Source: Terraform Registry, Homebrew Formulae) | ||
| 6. **Use subpaths not subdomains for SEO** - `org.dev/product/` beats `product.org.dev` for domain authority. (Source: Google SEO documentation) | ||
| 7. **Provide `llms.txt`** - Emerging standard for AI discoverability. Bun already does this. (Source: bun.com/docs/llms.txt, llmstxt.org) | ||
| 8. **Cross-link aggressively** - Every product page should link to related products/plugins. (Source: Stripe, Supabase docs patterns) | ||
| 9. **Consistent page structure across products** - Every product doc should have: Overview, Quickstart, Guides, API Reference, Changelog. (Source: Diataxis framework) | ||
| 10. **Deploy previews for doc PRs** - Use Vercel/Netlify/Cloudflare Pages preview deployments so reviewers can see rendered docs. (Source: Vercel, Netlify deployment features) | ||
| ## Recommended Architecture for agent-sh | ||
| ``` | ||
| agent-sh.dev/ # Built with Starlight (Astro) | ||
| index # Org landing: "AI Agent Workflows" | ||
| /docs/ # Getting started with the ecosystem | ||
| /docs/agentsys/ # Marketplace & installer | ||
| /docs/agentsys/getting-started/ | ||
| /docs/agentsys/cli-reference/ | ||
| /docs/agentsys/configuration/ | ||
| /docs/agnix/ # Linter (Phase 1: section here) | ||
| /docs/agnix/cli/ | ||
| /docs/agnix/lsp/ | ||
| /docs/agnix/ide-extensions/ | ||
| /docs/agnix/rules/ | ||
| /docs/web-ctl/ # Browser interaction | ||
| /plugins/ # Plugin catalog (generated from registry.json) | ||
| /plugins/next-task/ | ||
| /plugins/ship/ | ||
| /plugins/enhance/ | ||
| /plugins/deslop/ | ||
| ...13 plugin pages | ||
| /docs/agent-core/ # Infrastructure | ||
| /docs/contributing/ # How to build plugins | ||
| /llms.txt # AI discoverability index | ||
| /docs/llms.txt # Per-section AI index | ||
| agnix.dev/ # Phase 2: when agnix has independent users | ||
| -> redirect or mirror of agent-sh.dev/docs/agnix/ initially | ||
| -> full independent site later | ||
| ``` | ||
| **Tech stack recommendation:** | ||
| - **Framework**: Starlight (Astro) - fast, free, good multi-section support, growing ecosystem | ||
| - **Hosting**: Cloudflare Pages (free, fast) or Vercel (preview deployments) | ||
| - **Domain**: agent-sh.dev as primary, agnix.dev reserved for future | ||
| - **CI**: GitHub Actions to build on push, deploy previews on PR | ||
| - **Catalog**: Static generation from registry.json during build | ||
| - **Search**: Pagefind (built into Starlight) for client-side search across all products | ||
| ## Code Examples | ||
| ### Starlight Project Structure | ||
| ``` | ||
| docs-site/ | ||
| astro.config.mjs | ||
| src/ | ||
| content/ | ||
| docs/ | ||
| index.mdx # Landing page | ||
| agentsys/ | ||
| getting-started.mdx | ||
| cli-reference.mdx | ||
| plugins.mdx # Catalog page | ||
| agnix/ | ||
| overview.mdx | ||
| cli.mdx | ||
| lsp.mdx | ||
| rules/ | ||
| rule-reference.mdx | ||
| plugins/ | ||
| next-task.mdx # Generated from registry | ||
| ship.mdx | ||
| enhance.mdx | ||
| components/ | ||
| PluginCard.astro # Reusable plugin card | ||
| ProductSwitcher.astro # Product navigation | ||
| InstallCommand.astro # Copy-able install snippet | ||
| scripts/ | ||
| generate-plugin-pages.js # Pulls from registry.json + READMEs | ||
| public/ | ||
| llms.txt | ||
| ``` | ||
| ### Plugin Catalog Page Generation | ||
| ```javascript | ||
| // scripts/generate-plugin-pages.js | ||
| import { readFileSync, writeFileSync, mkdirSync } from 'fs'; | ||
| const registry = JSON.parse(readFileSync('../registry.json', 'utf-8')); | ||
| for (const plugin of registry.plugins) { | ||
| const page = `--- | ||
| title: "${plugin.name}" | ||
| description: "${plugin.description}" | ||
| --- | ||
| import { Badge, Card } from '@astrojs/starlight/components'; | ||
| import InstallCommand from '../../components/InstallCommand.astro'; | ||
| # ${plugin.name} | ||
| ${plugin.description} | ||
| <InstallCommand command="npx agentsys install ${plugin.name}" /> | ||
| ## Compatibility | ||
| ${plugin.compatibility.map(c => `<Badge text="${c}" />`).join(' ')} | ||
| ## Included | ||
| | Type | Name | Description | | ||
| |------|------|-------------| | ||
| ${plugin.commands.map(c => `| Command | \`${c}\` | |`).join('\n')} | ||
| ${plugin.agents.map(a => `| Agent | ${a} | |`).join('\n')} | ||
| ${plugin.skills.map(s => `| Skill | ${s} | |`).join('\n')} | ||
| ## Installation | ||
| \`\`\`bash | ||
| npx agentsys install ${plugin.name} | ||
| \`\`\` | ||
| [View on GitHub](${plugin.repo}) | ||
| `; | ||
| mkdirSync('src/content/docs/plugins', { recursive: true }); | ||
| writeFileSync(`src/content/docs/plugins/${plugin.name}.mdx`, page); | ||
| } | ||
| ``` | ||
| ### llms.txt for AI Discoverability | ||
| ``` | ||
| # agent-sh | ||
| > AI agent workflow tools for software development | ||
| ## Products | ||
| - agentsys: Marketplace and installer for AI agent plugins | ||
| - agnix: Linter for agent configurations (SKILL.md, CLAUDE.md, hooks, MCP) | ||
| - web-ctl: Browser interaction skill for AI agents | ||
| ## Documentation | ||
| - [AgentSys Docs](https://agent-sh.dev/docs/agentsys/) | ||
| - [Agnix Docs](https://agent-sh.dev/docs/agnix/) | ||
| - [Plugin Catalog](https://agent-sh.dev/plugins/) | ||
| ## Plugins | ||
| - next-task: Master workflow from task discovery to PR merge | ||
| - ship: PR creation, CI monitoring, and merge | ||
| - enhance: Run all enhancement analyzers | ||
| - deslop: Clean AI slop patterns | ||
| - audit-project: Multi-agent code review | ||
| - drift-detect: Compare plan vs implementation | ||
| - perf: Performance investigation | ||
| - repo-map: Generate AST-based repo map | ||
| - learn: Research topics, create learning guides | ||
| - agnix: Lint agent configs | ||
| - sync-docs: Update documentation to match code | ||
| - web-ctl: Browser interaction for agents | ||
| - next-task: Task workflow automation | ||
| ## Installation | ||
| npx agentsys install <plugin-name> | ||
| ``` | ||
| ## Further Reading | ||
| | Resource | Type | Why Recommended | | ||
| |----------|------|-----------------| | ||
| | [HashiCorp Developer Portal](https://developer.hashicorp.com) | Live example | Best-in-class unified multi-product docs | | ||
| | [Supabase Docs](https://supabase.com/docs) | Live example | Clean multi-section docs for related products | | ||
| | [Bun Docs](https://bun.sh/docs) | Live example | Single product, multi-section, includes llms.txt | | ||
| | [Vercel Docs](https://vercel.com/docs) | Live example | Hub-and-spoke with independent product sites | | ||
| | [Next.js Docs](https://nextjs.org/docs) | Live example | Independent product site within Vercel ecosystem | | ||
| | [Docusaurus Multi-Instance](https://docusaurus.io/docs/docs-multi-instance) | Docs | Official guide for multi-product Docusaurus | | ||
| | [Starlight Docs](https://starlight.astro.build) | Docs | Astro-based docs framework with multi-section support | | ||
| | [Mintlify](https://mintlify.com) | Platform | Beautiful hosted docs with tabs/groups for multi-product | | ||
| | [VitePress Multi-Sidebar](https://vitepress.dev/reference/default-theme-sidebar#multiple-sidebars) | Docs | Route-based sidebar configuration | | ||
| | [Diataxis Framework](https://diataxis.fr) | Framework | Four-type documentation structure (tutorials, how-to, reference, explanation) | | ||
| | [Terraform Registry](https://registry.terraform.io) | Live example | Plugin/module marketplace architecture | | ||
| | [VS Code Marketplace](https://marketplace.visualstudio.com) | Live example | Extension catalog with search, ratings, categories | | ||
| | [Homebrew Formulae](https://formulae.brew.sh) | Live example | Simple package catalog with analytics | | ||
| | [llmstxt.org](https://llmstxt.org) | Standard | Emerging standard for AI discoverability | | ||
| | [GitHub Pages Docs](https://docs.github.com/en/pages) | Docs | Org sites, project sites, custom domains | | ||
| | [Pagefind](https://pagefind.app) | Tool | Client-side search, built into Starlight | | ||
| | [Cloudflare Pages](https://pages.cloudflare.com) | Hosting | Free, fast hosting with preview deployments | | ||
| --- | ||
| *This guide was synthesized from 42 sources. See `resources/multi-product-org-docs-sources.json` for full source list.* |
| # Learning Guide: OSS Org Naming Patterns for Developer Tool Ecosystems | ||
| **Generated**: 2026-02-20 | ||
| **Sources**: 24 resources analyzed | ||
| **Depth**: medium | ||
| --- | ||
| ## Prerequisites | ||
| - Basic familiarity with GitHub organizations vs personal accounts | ||
| - Awareness of npm scopes (`@scope/package`) | ||
| - Understanding that GitHub and npm share flat global namespaces (no hierarchy) | ||
| --- | ||
| ## TL;DR | ||
| - When your primary name is taken on GitHub, the dominant strategies are: add a `-js`, `-dev`, `-hq`, `-labs`, `-team`, or `with` prefix/suffix — each carries different signals | ||
| - Successful ecosystems use a consistent plugin naming convention (`vite-plugin-*`, `@babel/*`, `eslint-plugin-*`) that makes the parent brand the discoverable prefix | ||
| - npm scopes (`@yourorg/package`) solve namespace pollution and are now the modern standard over flat `toolname-plugin-feature` names | ||
| - Org name credibility comes from the work shipping under it, not the cleverness of the name — abstract/opaque names (unjs, oven-sh, bombshell-dev) work fine once the tools prove themselves | ||
| - The `-dev` suffix (vitest-dev, biomejs) signals "development org"; `-labs` signals "experimental/innovative"; `-team` (drizzle-team) signals "we are people, not just a repo" | ||
| --- | ||
| ## Core Concepts | ||
| ### 1. The GitHub Namespace Problem | ||
| GitHub has a flat global namespace: every username and org name competes in the same pool. When `vue` was already a user, the Vue.js framework became `vuejs`. When `react` was taken, the React community org became `reactjs`. When `anthropic` was already registered, Anthropic became `anthropics`. | ||
| The naming strategies below represent what the open source community has converged on over roughly a decade. | ||
| **The core tension**: you want discoverability (name matches your tool), but the obvious name is often taken. Every strategy involves a tradeoff between brevity, clarity, and availability. | ||
| ### 2. The Major Suffix and Prefix Strategies | ||
| Projects that could not claim their bare tool name use one of these patterns: | ||
| | Strategy | Examples | Signal | | ||
| |----------|----------|--------| | ||
| | `+js` suffix | `vuejs`, `reactjs`, `feathersjs`, `storybookjs` | "JavaScript ecosystem project"; most common for JS frameworks pre-2020 | | ||
| | `+dev` suffix | `vitest-dev`, `biomejs` (implicit) | "This is the development org"; common for newer JS tools | | ||
| | `+labs` suffix | `tremorlabs`, `tailwindlabs` | "Company behind the tool"; implies innovation, commercial entity | | ||
| | `+team` suffix | `drizzle-team` | "Humans maintain this"; warm, approachable, sustainability signal | | ||
| | `+hq` suffix | Various smaller projects | "Headquarters"; less common, slightly corporate | | ||
| | `with` prefix | `withastro` | "Built with X" positioning; readable as verb phrase | | ||
| | `-ui` / `-css` descriptor | `shadcn-ui` | Adds product category when personal account holds bare name | | ||
| | Company name | `oven-sh` (Bun), `bombshell-dev` (clack) | Parent org owns ecosystem; tool name is repo, not org | | ||
| | Abstract collective | `unjs`, `antfu-collective` | Signals community ownership rather than individual | | ||
| | Full spelled-out name | `modelcontextprotocol` | Spec/standards bodies; authority over cleverness | | ||
| **Key insight**: The `-js` suffix feels dated for projects started after 2022. Newer projects prefer `-dev`, `-team`, or a distinct company/brand name as the org. | ||
| ### 3. The Company-Behind-the-Tool Pattern | ||
| Several of the most credible organizations use the **parent company or brand as the org**, with the tool living as a repo: | ||
| - `oven-sh` → `oven-sh/bun` (Oven is the company; Bun is the product) | ||
| - `bombshell-dev` → `bombshell-dev/clack` (Bombshell is the brand; clack is the tool) | ||
| - `charmbracelet` → `charmbracelet/bubbletea`, `charmbracelet/gum` (Charm is the company; each tool is a repo) | ||
| - `tailwindlabs` → `tailwindlabs/tailwindcss` (TailwindLabs is the company) | ||
| - `drizzle-team` → `drizzle-team/drizzle-orm` (the team owns the ORM) | ||
| This pattern works especially well when you plan to ship multiple tools. The org becomes the brand umbrella, and each repo name can be clean and short. | ||
| **Credibility note**: `charmbracelet` demonstrates this perfectly. The org name is stylistically unusual, but `bubbletea` (39k stars), `gum` (22k stars), and `glow` (22k stars) prove that substance beats cleverness. No one questions the name because the software is excellent. | ||
| ### 4. Ecosystem and Plugin Naming Conventions | ||
| Once you have an org, the real ecosystem work is plugin/extension naming. The established patterns: | ||
| **Flat prefix pattern** (pre-npm-scopes era, still widely used): | ||
| ``` | ||
| vite-plugin-react | ||
| babel-plugin-transform-arrow-functions | ||
| eslint-plugin-react | ||
| prettier-plugin-ruby | ||
| rollup-plugin-typescript2 | ||
| ``` | ||
| The tool name is the discoverability prefix. Anyone searching `vite-plugin` on npm or GitHub finds your ecosystem. | ||
| **Scoped package pattern** (modern, recommended for new ecosystems): | ||
| ``` | ||
| @babel/core | ||
| @babel/plugin-transform-arrow-functions | ||
| @babel/preset-env | ||
| @inquirer/prompts | ||
| @inquirer/select | ||
| @clack/prompts | ||
| @clack/core | ||
| ``` | ||
| Scopes solve namespace pollution on npm (no risk of `babel-plugin-x` colliding with unaffiliated packages) and signal official vs community packages clearly. | ||
| **Hybrid pattern** (many mature projects use both): | ||
| - Official packages: `@org/package-name` | ||
| - Community packages: `tool-plugin-feature` (flat, community convention) | ||
| - Example: Babel has `@babel/*` for official, but community still publishes `babel-plugin-*` | ||
| **Org-prefix for repos** (official plugins as repos, not just npm): | ||
| ``` | ||
| tailwindcss-typography (tailwindlabs/tailwindcss-typography) | ||
| tailwindcss-forms (tailwindlabs/tailwindcss-forms) | ||
| vite-plugin-react (vitejs/vite-plugin-react) | ||
| addon-designs (storybookjs/addon-designs) | ||
| biome-vscode (biomejs/biome-vscode) | ||
| ``` | ||
| **Key insight**: The repo naming convention you choose defines what appears in GitHub search. If your org is `mytools` and plugins are `mytools-plugin-*`, searching GitHub for `mytools-plugin` finds your ecosystem. If you use `@mytools/*` as npm scope, that helps npm discoverability but is invisible in GitHub search (which searches repo names, not package names). | ||
| ### 5. How Successful Projects Grew into Ecosystem Orgs | ||
| **Vite (vitejs)**: Started as Evan You's project, moved to `vitejs` org. Official plugins (`vite-plugin-react`, `vite-plugin-vue`) live in the org. Created `vite-ecosystem-ci` (a repo, not a tool) to validate community plugin compatibility. The org is deliberately minimal — core tools only. | ||
| **Babel (babel)**: Owned the `babel` org name. Migrated from flat `babel-*` packages to `@babel/*` scoped monorepo. This was a major v7 transition and became a blueprint for ecosystem migrations. The monorepo in `babel/babel` contains 100+ packages. | ||
| **Tailwind CSS (tailwindlabs)**: "Labs" signals commercial entity. The company name is `tailwindlabs` but the framework is `tailwindcss`. Official plugins use `tailwindcss-*` naming (not `tailwindlabs-*`), keeping the product name as the brand. | ||
| **Nuxt (nuxt)**: Claimed the bare `nuxt` org name. Has a `modules` repo (not a mono-repo, but a registry) that tracks community modules. This shows a third pattern: the org doesn't have to host all plugins — it can host the registry that points to them. | ||
| **ESLint (eslint)**: Owns `eslint` org. The community plugin convention is `eslint-plugin-*` (flat). ESLint itself has moved toward official language plugins living in the org as `eslint/json`, `eslint/css`. The plugin naming predates scopes and the ecosystem is too large to migrate. | ||
| **Prisma (prisma)**: Owns the bare `prisma` name. Uses descriptive repo names without redundant prefixes (`prisma/studio`, `prisma/language-tools`) because the org itself provides the namespace. | ||
| ### 6. The npm Scope as Identity | ||
| When you register an npm org, you get a scope. The scope and the GitHub org name do not have to match — but aligning them reduces confusion: | ||
| ``` | ||
| GitHub org: vitejs npm scope: (none used, flat vite-plugin-*) | ||
| GitHub org: babel npm scope: @babel | ||
| GitHub org: SBoudrias npm scope: @inquirer (personal account, named scope) | ||
| GitHub org: bombshell-dev npm scope: @clack | ||
| GitHub org: antfu-collective npm scope: (individual packages under various scopes) | ||
| ``` | ||
| **Best practice for new projects (2024+)**: | ||
| 1. Register `@yourorgname` npm scope matching your GitHub org | ||
| 2. Publish `@yourorgname/core`, `@yourorgname/cli`, `@yourorgname/plugin-x` | ||
| 3. This creates a clear signal: anything under `@yourorgname` is official | ||
| npm scoped packages must be lowercase and follow `@scope/package-name` where both scope and name match `[a-z0-9-._~!$&'()*+,;=:@]` (effectively: lowercase letters, numbers, hyphens, dots). | ||
| ### 7. What Makes a GitHub Org Name Feel Credible vs Try-Hard | ||
| **Credibility signals**: | ||
| - The org name is clean, lowercase, and easy to type | ||
| - It either matches the project/company name exactly, or uses a recognized convention (js, dev, labs, team) | ||
| - The README/description is professional without being marketing-speak | ||
| - Stars, contributors, and commit activity are visible | ||
| - Verified domain in the org profile | ||
| - MIT or Apache-2.0 license (signals you want people to use it) | ||
| - Pinned repos showcase the actual tools, not meta-content | ||
| **Try-hard signals**: | ||
| - Name is excessively clever or requires explanation unrelated to the tool | ||
| - Org created recently with 1-2 repos, attempting ecosystem naming before the ecosystem exists | ||
| - Claims to be a "framework", "platform", or "ecosystem" in description when it is a library | ||
| - Plugin or contrib template repos set up before any community exists | ||
| - Version numbers in org names or repo names | ||
| - Multiple repos that are all stubs | ||
| **The "abstract name" exception**: Names like `unjs` (Unified JavaScript), `oven-sh`, `bombshell-dev`, `charmbracelet`, and `antfu-collective` are abstract or unusual — but they work because: | ||
| 1. The org produced genuinely useful tools first | ||
| 2. The naming is internally consistent (unjs has the `un*` prefix theme) | ||
| 3. There is a clear website/brand behind it | ||
| **The CLI/agent tooling context**: For CLI tools and agent tooling specifically, naming that signals **reliability and professional maintenance** matters more than cleverness. Developers integrating a tool into their workflow need confidence it will be maintained. Names like `drizzle-team` (people behind it) or `vitest-dev` (dev org) signal ongoing stewardship better than a cute name with no backing. | ||
| ### 8. The "Collective" and Multi-Maintainer Pattern | ||
| When a project outgrows a single person, the org becomes a stewardship signal: | ||
| **antfu-collective**: Anthony Fu created this to house tools that are now maintained by a team, distinct from his personal projects. The name signals "started by antfu, now community-owned." | ||
| **reactjs vs facebook/react**: Meta owns `facebook/react` (the actual repo), but the `reactjs` org houses documentation, localization, and community tooling. This separation is governance, not just naming. | ||
| **withastro**: Astro chose `withastro` (a verb phrase: "build with Astro") rather than `astro` (the adjective/noun) because `astro` was taken. The `with` prefix became a semantic feature — it communicates the framework's value proposition. | ||
| --- | ||
| ## Code Examples | ||
| ### npm Package Naming Patterns | ||
| ```json | ||
| // Modern scoped package setup (package.json) | ||
| { | ||
| "name": "@mytool/core", | ||
| "version": "1.0.0", | ||
| "description": "Core library for MyTool" | ||
| } | ||
| // Official plugin naming | ||
| { | ||
| "name": "@mytool/plugin-react", | ||
| "peerDependencies": { | ||
| "@mytool/core": ">=1.0.0" | ||
| } | ||
| } | ||
| // Community plugin convention (flat naming) | ||
| { | ||
| "name": "mytool-plugin-svelte", | ||
| "keywords": ["mytool", "mytool-plugin"] | ||
| } | ||
| ``` | ||
| ### GitHub Org Structure Pattern (hub-and-spoke) | ||
| ``` | ||
| org: mytooldev (since "mytool" was taken) | ||
| repos: | ||
| mytool ← core tool (clean name inside the org namespace) | ||
| mytool-docs ← documentation site | ||
| mytool-vscode ← editor integration | ||
| plugins ← monorepo for official plugins | ||
| ecosystem-ci ← compatibility testing | ||
| awesome-mytool ← community curated list | ||
| npm scope: @mytool (registered separately, org name mismatch is fine) | ||
| packages: | ||
| @mytool/core | ||
| @mytool/plugin-react | ||
| @mytool/plugin-vue | ||
| @mytool/cli | ||
| ``` | ||
| ### Plugin Registry Pattern (Nuxt-style) | ||
| ``` | ||
| org: mytool | ||
| repos: | ||
| mytool ← framework | ||
| modules ← registry (JSON list of community modules) | ||
| module-builder ← scaffolding for community module authors | ||
| examples ← reference implementations | ||
| Community modules live in their own orgs, listed in mytool/modules registry. | ||
| ``` | ||
| ### Choosing Between Naming Strategies (Decision Tree) | ||
| ``` | ||
| Is your bare name available on GitHub? | ||
| YES → Claim it immediately, align npm scope | ||
| NO → What is your context? | ||
| Are you a company/team with multiple tools? | ||
| YES → Use company/brand name as org (oven-sh, charmbracelet, tailwindlabs) | ||
| Tool names become clean repo names within the org | ||
| Are you an individual or small team with ONE main tool? | ||
| → Add suffix to tool name: | ||
| - New project (2023+): prefer -dev or -team | ||
| - JS framework: -js still acceptable but dated | ||
| - Commercial entity: -labs, -hq | ||
| - Community project: -collective, just describe it | ||
| Is it a standards body or protocol spec? | ||
| → Use full spelled-out name (modelcontextprotocol, tc39) | ||
| ``` | ||
| --- | ||
| ## Common Pitfalls | ||
| | Pitfall | Why It Happens | How to Avoid | | ||
| |---------|---------------|--------------| | ||
| | Registering org name before tool exists | Squatting on good names | Validate the project first; names can be changed | | ||
| | npm scope and GitHub org name diverge confusingly | npm org was available when GitHub wasn't | Accept the mismatch, document it clearly in README | | ||
| | Plugin naming without a scheme | No upfront planning | Pick `@org/plugin-x` or `toolname-plugin-x` at v0.1, stick to it | | ||
| | Org name requires explanation in every README | Too clever / abstract | Names that need a glossary entry hurt onboarding | | ||
| | Copying ecosystem structure before community exists | Premature architecture | Set up the registry/monorepo when you have 3+ active plugins | | ||
| | `-js` suffix for non-JavaScript tools | Historical pattern cargo-culted | Use `-dev`, `-team`, or project-neutral suffix | | ||
| | Claiming org but leaving it private | Security squatting / confusion | Either make the org public with a pinned description or skip it | | ||
| | Org description says "ecosystem" with 2 repos | Credibility gap | Let stars and contributors prove scale before claiming it | | ||
| --- | ||
| ## Best Practices | ||
| 1. **Claim both your bare name and fallback variants immediately** — register `mytool`, `mytooljs`, `mytool-dev` on GitHub and npm, even if unused, to prevent squatting. Transfer the unused ones to a redirect later. | ||
| 2. **Align your npm scope with your GitHub org name** (or document the mismatch clearly). `@drizzle` npm scope + `drizzle-team` GitHub org creates no confusion because `drizzle-team`'s README links to `@drizzle` on npm. | ||
| 3. **Use scoped npm packages for official plugins** and document the community convention for unofficial ones. Example: "Official plugins are `@mytool/plugin-*`; community plugins should follow `mytool-plugin-*`." | ||
| 4. **Put the canonical tool name as the repo name inside the org**, not the org name. `vitejs/vite` reads better than `vitejs/vitejs`. The org provides the namespace; the repo can be clean. | ||
| 5. **Ship the tools before building the ecosystem scaffolding.** Credibility flows from shipped, starred tools. An org with 1k stars on its main repo earns more trust than an org with 0-star plugin template stubs. | ||
| 6. **For CLI and agent tooling**: favor names that signal reliability. `drizzle-team` and `vitest-dev` communicate "maintained by real people actively developing this" which matters when someone is evaluating whether to depend on your tool in their workflow. | ||
| 7. **Add a verified domain to your GitHub org profile.** This takes minutes and adds significant authority signal, especially for developer tools where the domain is the documentation home. | ||
| 8. **The "awesome-mytool" repo signals community health.** Creating and maintaining it, or linking to it from the org profile, shows users there is a community, not just an org. | ||
| 9. **Keep the plugin naming convention in your docs from day 1.** ESLint has `eslint-plugin-*` documented clearly; Vite has `vite-plugin-*`. Community plugins self-organize around documented conventions. | ||
| 10. **If you use a parent company name as the org** (like `oven-sh` for Bun), ensure the domain `oven.sh` or equivalent is prominently linked from the org. The org name will be opaque to new visitors; the domain resolves the mystery. | ||
| --- | ||
| ## Patterns Reference | ||
| ### The Suffix Taxonomy | ||
| | Suffix | Signal | Best For | | ||
| |--------|--------|----------| | ||
| | (none) | Owns the concept / high authority | Top-tier projects, lucky squatters | | ||
| | `-js` | "JavaScript project" | Pre-2022 JS frameworks (dated now) | | ||
| | `-dev` | "Development organization" | Modern tools, testing frameworks | | ||
| | `-hq` | "Headquarters / canonical home" | Mid-sized projects, single tools | | ||
| | `-labs` | "Company, innovation-focused" | Commercial orgs, design systems | | ||
| | `-team` | "Humans behind the project" | ORMs, infra tools, warm branding | | ||
| | `-ui` | "User interface component scope" | Component libs when UI is core identity | | ||
| | `-collective` | "Community-owned, not one person" | Personal brand scaling to team | | ||
| | `with{name}` | "Build with X" verb positioning | Frameworks describing their role | | ||
| | `{company}` | Parent brand as org | Multi-tool shops, commercial entities | | ||
| ### Known Naming Workaround Gallery | ||
| | Project | Ideal Name | Actual Org | Strategy | | ||
| |---------|-----------|------------|----------| | ||
| | Vue.js | vue | vuejs | +js suffix | | ||
| | React | react | reactjs (community) | +js suffix | | ||
| | Vite | vite | vitejs | +js suffix | | ||
| | Vitest | vitest | vitest-dev | -dev suffix | | ||
| | Astro | astro | withastro | with- prefix | | ||
| | Biome | biome | biomejs | +js suffix | | ||
| | Storybook | storybook | storybookjs | +js suffix | | ||
| | Tailwind | tailwind | tailwindlabs | -labs suffix (company) | | ||
| | Drizzle ORM | drizzle | drizzle-team | -team suffix | | ||
| | Bun | bun | oven-sh | parent company name | | ||
| | Clack | clack | bombshell-dev | parent brand name | | ||
| | shadcn/ui | shadcn | shadcn-ui | descriptor suffix | | ||
| | Anthropic | anthropic | anthropics | +s (minimal change) | | ||
| | Deno | deno | denoland | +land (playful) | | ||
| | pkgx | pkgx | pkgxdev | +dev suffix | | ||
| --- | ||
| ## Further Reading | ||
| | Resource | Type | Why Recommended | | ||
| |----------|------|-----------------| | ||
| | [vitejs org](https://github.com/vitejs) | GitHub Org | Canonical example of +js suffix with clean internal naming | | ||
| | [tailwindlabs org](https://github.com/tailwindlabs) | GitHub Org | Best example of -labs company org with tool-name repos | | ||
| | [withastro org](https://github.com/withastro) | GitHub Org | The "with" prefix strategy and multi-tool org structure | | ||
| | [charmbracelet org](https://github.com/charmbracelet) | GitHub Org | Company-as-org with thematic tool naming | | ||
| | [drizzle-team org](https://github.com/drizzle-team) | GitHub Org | -team suffix credibility pattern | | ||
| | [vitest-dev org](https://github.com/vitest-dev) | GitHub Org | -dev suffix for modern JS tooling | | ||
| | [unjs org](https://github.com/unjs) | GitHub Org | Abstract collective name that works because of shipping | | ||
| | [antfu-collective org](https://github.com/antfu-collective) | GitHub Org | Personal brand scaling to community stewardship | | ||
| | [modelcontextprotocol org](https://github.com/modelcontextprotocol) | GitHub Org | Full-name org for protocol/spec authority | | ||
| | [oven-sh org](https://github.com/oven-sh) | GitHub Org | Parent company as org; clean repo names within | | ||
| | [bombshell-dev org](https://github.com/bombshell-dev) | GitHub Org | Creative parent brand name for CLI tooling | | ||
| | [babel org](https://github.com/babel) | GitHub Org | @babel/* scoped monorepo migration blueprint | | ||
| | [nuxt org](https://github.com/nuxt) | GitHub Org | Registry-based ecosystem (not monorepo) model | | ||
| | [anthropics org](https://github.com/anthropics) | GitHub Org | +s suffix pattern; SDK naming conventions | | ||
| | [shadcn-ui org](https://github.com/shadcn-ui) | GitHub Org | Descriptor suffix when personal account holds bare name | | ||
| --- | ||
| *Generated by /learn from 24 sources.* | ||
| *See `resources/oss-org-naming-patterns-sources.json` for full source metadata.* |
| { | ||
| "topic": "ACP (Agent Communication Protocol) with Codex, Gemini, Copilot, Claude", | ||
| "slug": "acp-with-codex-gemini-copilot-claude", | ||
| "generated": "2026-03-02T00:00:00Z", | ||
| "depth": "medium", | ||
| "totalSources": 24, | ||
| "sources": [ | ||
| { | ||
| "url": "https://agentcommunicationprotocol.dev/", | ||
| "title": "Agent Communication Protocol - Official Website", | ||
| "qualityScore": 92, | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 7, | ||
| "uniqueness": 10, | ||
| "keyInsights": [ | ||
| "ACP is now part of A2A under the Linux Foundation", | ||
| "Uses REST endpoints aligned with standard HTTP patterns", | ||
| "Employs MIME types for content identification and extensibility", | ||
| "Framework-agnostic across BeeAI, LangChain, CrewAI, custom implementations", | ||
| "SDKs available in Python and TypeScript" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/google/A2A", | ||
| "title": "A2A Protocol - GitHub Repository", | ||
| "qualityScore": 95, | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 9, | ||
| "examples": 8, | ||
| "uniqueness": 10, | ||
| "keyInsights": [ | ||
| "JSON-RPC 2.0 over HTTP(S) transport", | ||
| "Agent Cards for capability discovery", | ||
| "Supports synchronous, SSE streaming, and async push notifications", | ||
| "Apache 2.0 licensed under Linux Foundation", | ||
| "SDKs in Python, Go, JavaScript, Java, .NET" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://a2a-protocol.org/latest/", | ||
| "title": "A2A Protocol - Official Specification Site", | ||
| "qualityScore": 88, | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 7, | ||
| "examples": 6, | ||
| "uniqueness": 8, | ||
| "keyInsights": [ | ||
| "A2A enables seamless communication between AI agents", | ||
| "Complementary to MCP which handles agent-to-tool communication", | ||
| "Connects agents across LangGraph, CrewAI, Semantic Kernel, custom solutions", | ||
| "Agents interact without sharing internal memory or proprietary logic" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/", | ||
| "title": "Google Developers Blog - A2A Announcement", | ||
| "qualityScore": 90, | ||
| "authority": 10, | ||
| "recency": 8, | ||
| "depth": 8, | ||
| "examples": 6, | ||
| "uniqueness": 9, | ||
| "keyInsights": [ | ||
| "Announced April 9, 2025 by Google", | ||
| "50+ technology partners including Atlassian, Salesforce, SAP", | ||
| "Five core design principles: agentic, existing standards, security, long-running, modality agnostic", | ||
| "Built on HTTP, SSE, and JSON-RPC for easier IT integration", | ||
| "A2A complements Anthropic's MCP" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/i-am-bee/beeai", | ||
| "title": "Agent Stack (BeeAI) - GitHub Repository", | ||
| "qualityScore": 85, | ||
| "authority": 8, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 7, | ||
| "uniqueness": 9, | ||
| "keyInsights": [ | ||
| "Built on A2A Protocol under Linux Foundation", | ||
| "Agents automatically exposed as A2A-compatible services", | ||
| "SDK handles runtime service requests and agent-to-agent communication", | ||
| "Supports LangGraph, CrewAI, and custom frameworks", | ||
| "15+ LLM provider support including Anthropic, OpenAI, watsonx.ai" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://pypi.org/project/acp-sdk/", | ||
| "title": "ACP SDK for Python - PyPI", | ||
| "qualityScore": 82, | ||
| "authority": 8, | ||
| "recency": 8, | ||
| "depth": 7, | ||
| "examples": 9, | ||
| "uniqueness": 7, | ||
| "keyInsights": [ | ||
| "Version 1.0.3, maintained by IBM Corp.", | ||
| "Async/await support for agent functions", | ||
| "Decorator-based agent registration", | ||
| "Python 3.11+ required", | ||
| "Apache-2.0 licensed" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://modelcontextprotocol.io/introduction", | ||
| "title": "MCP - Model Context Protocol Introduction", | ||
| "qualityScore": 93, | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 7, | ||
| "uniqueness": 8, | ||
| "keyInsights": [ | ||
| "Open standard for connecting AI applications to external systems", | ||
| "Analogous to USB-C port for AI applications", | ||
| "Connects to data sources, tools, and workflows", | ||
| "Supported by Claude, ChatGPT, and many other applications" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://code.claude.com/docs/en/overview", | ||
| "title": "Claude Code - Official Overview", | ||
| "qualityScore": 95, | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 9, | ||
| "examples": 8, | ||
| "uniqueness": 8, | ||
| "keyInsights": [ | ||
| "Agentic coding tool with MCP support", | ||
| "Supports subagents, agent teams, and Agent SDK", | ||
| "Can serve as MCP server via 'claude mcp serve'", | ||
| "Available in terminal, IDE, desktop, and browser", | ||
| "Supports spawning multiple agent teams for parallel work" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://code.claude.com/docs/en/sub-agents", | ||
| "title": "Claude Code - Custom Subagents Documentation", | ||
| "qualityScore": 94, | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 10, | ||
| "examples": 9, | ||
| "uniqueness": 9, | ||
| "keyInsights": [ | ||
| "Subagents run in own context window with custom system prompt", | ||
| "Built-in subagents: Explore, Plan, General-purpose", | ||
| "Custom subagents defined in Markdown with YAML frontmatter", | ||
| "Support for different models per subagent", | ||
| "MCP servers can be scoped to individual subagents" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://code.claude.com/docs/en/agent-teams", | ||
| "title": "Claude Code - Agent Teams Documentation", | ||
| "qualityScore": 93, | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 10, | ||
| "examples": 9, | ||
| "uniqueness": 10, | ||
| "keyInsights": [ | ||
| "Multiple Claude Code instances coordinating via shared task lists", | ||
| "Direct inter-agent messaging via mailbox system", | ||
| "Team lead coordinates work and synthesizes results", | ||
| "Experimental feature requiring explicit opt-in", | ||
| "Significantly higher token costs than single sessions" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://code.claude.com/docs/en/mcp", | ||
| "title": "Claude Code - MCP Documentation", | ||
| "qualityScore": 95, | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 10, | ||
| "examples": 10, | ||
| "uniqueness": 7, | ||
| "keyInsights": [ | ||
| "Supports HTTP, SSE, and stdio MCP transports", | ||
| "Claude Code can serve as an MCP server", | ||
| "OAuth 2.0 authentication for remote servers", | ||
| "MCP Tool Search for dynamic tool loading", | ||
| "Managed MCP configuration for enterprise control" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://platform.claude.com/docs/en/agent-sdk/overview", | ||
| "title": "Claude Agent SDK - Official Overview", | ||
| "qualityScore": 94, | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 9, | ||
| "examples": 10, | ||
| "uniqueness": 9, | ||
| "keyInsights": [ | ||
| "Programmatic access to Claude Code tools and capabilities", | ||
| "Built-in tools: Read, Write, Edit, Bash, Glob, Grep, WebSearch, WebFetch", | ||
| "Custom subagent definitions via AgentDefinition", | ||
| "Hook system for controlling agent behavior", | ||
| "Session management with resume and fork capabilities" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/anthropics/claude-agent-sdk-python", | ||
| "title": "Claude Agent SDK for Python - GitHub", | ||
| "qualityScore": 88, | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 10, | ||
| "uniqueness": 7, | ||
| "keyInsights": [ | ||
| "In-process MCP server support for custom tools", | ||
| "Bidirectional communication via ClaudeSDKClient", | ||
| "Hook system with PreToolUse, PostToolUse events", | ||
| "Auto-bundled Claude Code CLI", | ||
| "Python 3.10+ required" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/anthropics/claude-agent-sdk-demos", | ||
| "title": "Claude Agent SDK Demos - GitHub", | ||
| "qualityScore": 80, | ||
| "authority": 9, | ||
| "recency": 8, | ||
| "depth": 7, | ||
| "examples": 10, | ||
| "uniqueness": 8, | ||
| "keyInsights": [ | ||
| "Research Agent demonstrates multi-agent coordination", | ||
| "Subagent spawning for parallel research tasks", | ||
| "Web search integration for research workflows", | ||
| "7 demo applications covering various patterns" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/openai/codex", | ||
| "title": "OpenAI Codex CLI - GitHub Repository", | ||
| "qualityScore": 85, | ||
| "authority": 9, | ||
| "recency": 9, | ||
| "depth": 7, | ||
| "examples": 6, | ||
| "uniqueness": 8, | ||
| "keyInsights": [ | ||
| "Primarily Rust-based (96.1%) local coding agent", | ||
| "MCP Registry integration for external tools", | ||
| "Contains shell-tool-mcp component", | ||
| "No documented agent-to-agent communication", | ||
| "553+ releases indicating active development" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/google-gemini/gemini-cli", | ||
| "title": "Google Gemini CLI - GitHub Repository", | ||
| "qualityScore": 87, | ||
| "authority": 9, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 7, | ||
| "uniqueness": 8, | ||
| "keyInsights": [ | ||
| "MCP support for custom integrations", | ||
| "No A2A or ACP protocol references", | ||
| "Built-in tools: file system, shell, web fetch, Google Search", | ||
| "Free tier: 60 req/min, 1000 daily requests", | ||
| "Non-interactive mode with JSON/streaming output" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.github.com/en/copilot/using-github-copilot/using-extensions-to-integrate-external-tools-with-copilot-chat", | ||
| "title": "GitHub Copilot Extensions - MCP Integration", | ||
| "qualityScore": 84, | ||
| "authority": 9, | ||
| "recency": 8, | ||
| "depth": 7, | ||
| "examples": 6, | ||
| "uniqueness": 7, | ||
| "keyInsights": [ | ||
| "MCP as open standard for tool integration in Copilot", | ||
| "GitHub MCP Server for coding agent and code scanning", | ||
| "Local and remote server support across multiple IDEs", | ||
| "Organization policy controls for MCP servers" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://kiro.dev/docs/", | ||
| "title": "Kiro IDE - Official Documentation", | ||
| "qualityScore": 75, | ||
| "authority": 8, | ||
| "recency": 9, | ||
| "depth": 5, | ||
| "examples": 4, | ||
| "uniqueness": 7, | ||
| "keyInsights": [ | ||
| "AWS agentic IDE with specs, steering, and hooks", | ||
| "MCP server support as core capability", | ||
| "Built on Claude AI model", | ||
| "No ACP or A2A protocol mentioned" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://kiro.dev/docs/mcp/", | ||
| "title": "Kiro MCP Server Support", | ||
| "qualityScore": 72, | ||
| "authority": 8, | ||
| "recency": 9, | ||
| "depth": 5, | ||
| "examples": 3, | ||
| "uniqueness": 5, | ||
| "keyInsights": [ | ||
| "MCP enables Kiro to communicate with external servers", | ||
| "Settings-based MCP configuration", | ||
| "Connection status indicators and log access", | ||
| "No agent-to-agent communication architecture documented" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/modelcontextprotocol/servers", | ||
| "title": "MCP Servers Repository - Official", | ||
| "qualityScore": 88, | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 7, | ||
| "examples": 8, | ||
| "uniqueness": 8, | ||
| "keyInsights": [ | ||
| "Sequential Thinking server for structured reasoning", | ||
| "AgentRPC for cross-network function communication", | ||
| "Reference implementations are educational, not production", | ||
| "Memory server for knowledge graph-based persistence" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/modelcontextprotocol/servers/tree/main/src/sequentialthinking", | ||
| "title": "Sequential Thinking MCP Server", | ||
| "qualityScore": 83, | ||
| "authority": 9, | ||
| "recency": 8, | ||
| "depth": 8, | ||
| "examples": 8, | ||
| "uniqueness": 9, | ||
| "keyInsights": [ | ||
| "Dynamic and reflective problem-solving through thought sequences", | ||
| "Supports revision, branching, and dynamic scope adjustment", | ||
| "Parameters: thought, nextThoughtNeeded, thoughtNumber, totalThoughts", | ||
| "Available via npx or Docker", | ||
| "Designed for problems where full scope is not initially clear" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/punkpeye/awesome-mcp-servers", | ||
| "title": "Awesome MCP Servers - Curated List", | ||
| "qualityScore": 78, | ||
| "authority": 7, | ||
| "recency": 9, | ||
| "depth": 6, | ||
| "examples": 5, | ||
| "uniqueness": 9, | ||
| "keyInsights": [ | ||
| "AI bridge servers exist for Gemini, OpenAI, Ollama, Grok", | ||
| "Agent-to-agent MCP servers: agentnet, prolink, hashnet, agenium", | ||
| "blockrun-mcp offers access to 30+ AI models", | ||
| "Community-driven ecosystem of 100+ servers" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/anthropics/claude-code", | ||
| "title": "Claude Code - GitHub Repository", | ||
| "qualityScore": 82, | ||
| "authority": 9, | ||
| "recency": 9, | ||
| "depth": 6, | ||
| "examples": 5, | ||
| "uniqueness": 6, | ||
| "keyInsights": [ | ||
| "72.3k GitHub stars indicating strong community adoption", | ||
| "Plugin system for extensible architecture", | ||
| "MCP Registry integration for external tools", | ||
| "Feature flags suggest advanced multi-agent capabilities" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/openai/codex/blob/main/codex-cli/README.md", | ||
| "title": "Codex CLI README - Detailed Documentation", | ||
| "qualityScore": 83, | ||
| "authority": 9, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 7, | ||
| "uniqueness": 6, | ||
| "keyInsights": [ | ||
| "Three approval modes: Suggest, Auto Edit, Full Auto", | ||
| "Multi-provider support via configuration", | ||
| "Sandboxed execution with Apple Seatbelt (macOS) and Docker (Linux)", | ||
| "AGENTS.md instruction files for project context", | ||
| "No inter-agent communication mechanisms documented" | ||
| ] | ||
| } | ||
| ] | ||
| } |
| { | ||
| "topic": "Programmatic & Non-Interactive Usage of AI CLI Tools (Claude Code, Codex CLI, OpenCode, Gemini CLI)", | ||
| "slug": "ai-cli-non-interactive-programmatic-usage", | ||
| "generated": "2026-02-10T00:00:00Z", | ||
| "depth": "deep", | ||
| "totalSources": 42, | ||
| "sources": [ | ||
| { | ||
| "url": "https://code.claude.com/docs/en/cli-reference", | ||
| "title": "Claude Code CLI Reference - Complete Flag Reference", | ||
| "qualityScore": 98, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 10, "examples": 10, "uniqueness": 8 }, | ||
| "keyInsights": [ | ||
| "Complete reference for all CLI flags including -p, --output-format, --session-id, --resume, --max-turns, --allowedTools", | ||
| "--json-schema flag enables validated structured output matching a JSON Schema", | ||
| "--agents flag accepts JSON to define custom subagents dynamically", | ||
| "--permission-mode supports plan, acceptEdits, bypassPermissions, dontAsk, delegate", | ||
| "System prompt flags: --system-prompt (replace), --append-system-prompt (append), and file variants" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://code.claude.com/docs/en/headless", | ||
| "title": "Run Claude Code Programmatically - Headless Mode Guide", | ||
| "qualityScore": 97, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 10, "examples": 10, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "claude -p is the primary entry point for non-interactive usage", | ||
| "Session continuity via --continue and --resume with session ID capture", | ||
| "Structured output with --output-format json and --json-schema", | ||
| "Stream responses with --output-format stream-json --verbose --include-partial-messages", | ||
| "jq integration for parsing JSON output" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://platform.claude.com/docs/en/agent-sdk/overview", | ||
| "title": "Claude Agent SDK Overview - Python and TypeScript SDK", | ||
| "qualityScore": 97, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 10, "examples": 10, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "Full programmatic SDK available in Python (claude-agent-sdk) and TypeScript (@anthropic-ai/claude-agent-sdk)", | ||
| "query() function provides async iterator streaming of messages", | ||
| "Built-in tools: Read, Write, Edit, Bash, Glob, Grep, WebSearch, WebFetch", | ||
| "Supports hooks, subagents, MCP servers, permissions, and session management", | ||
| "SDK renamed from Claude Code SDK to Claude Agent SDK" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://platform.claude.com/docs/en/agent-sdk/structured-outputs", | ||
| "title": "Agent SDK Structured Outputs - JSON Schema Validation", | ||
| "qualityScore": 96, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 10, "examples": 10, "uniqueness": 6 }, | ||
| "keyInsights": [ | ||
| "outputFormat option with type: 'json_schema' enables validated structured output", | ||
| "Works with Zod (TypeScript) and Pydantic (Python) for type-safe schemas", | ||
| "Result message contains structured_output field with validated data", | ||
| "Error handling via subtype: 'error_max_structured_output_retries'", | ||
| "Structured output is not available during streaming" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://platform.claude.com/docs/en/agent-sdk/sessions", | ||
| "title": "Agent SDK Session Management - Resume and Fork", | ||
| "qualityScore": 95, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 10, "examples": 9, "uniqueness": 6 }, | ||
| "keyInsights": [ | ||
| "Session ID captured from 'init' subtype message", | ||
| "Resume sessions via resume option with session ID", | ||
| "Fork sessions with forkSession: true to create branches", | ||
| "Forked sessions preserve original, create new branch from resume point", | ||
| "Sessions maintain full conversation history including tool calls" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://platform.claude.com/docs/en/agent-sdk/streaming-output", | ||
| "title": "Agent SDK Streaming Output - Real-Time Responses", | ||
| "qualityScore": 93, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 9, "examples": 10, "uniqueness": 4 }, | ||
| "keyInsights": [ | ||
| "includePartialMessages option enables StreamEvent messages", | ||
| "Event types: message_start, content_block_start, content_block_delta, content_block_stop, message_delta, message_stop", | ||
| "Text streaming via text_delta events, tool streaming via input_json_delta events", | ||
| "Streaming incompatible with extended thinking and structured outputs", | ||
| "StreamEvent wraps raw Claude API events" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://platform.claude.com/docs/en/agent-sdk/quickstart", | ||
| "title": "Agent SDK Quickstart - Build Your First Agent", | ||
| "qualityScore": 92, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 8, "examples": 10, "uniqueness": 4 }, | ||
| "keyInsights": [ | ||
| "query() is the main entry point, returns async iterator", | ||
| "Permission modes: acceptEdits (auto-approve edits), bypassPermissions (skip all), default (needs callback)", | ||
| "Message types: AssistantMessage, ResultMessage, SystemMessage", | ||
| "Tool restriction via allowedTools option", | ||
| "Supports .env file for API key loading" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://platform.claude.com/docs/en/agent-sdk/hosting", | ||
| "title": "Agent SDK Hosting - Production Deployment Patterns", | ||
| "qualityScore": 91, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 9, "examples": 7, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "Four deployment patterns: Ephemeral, Long-Running, Hybrid, Single Container", | ||
| "Resource requirements: 1GiB RAM, 5GiB disk, 1 CPU per instance", | ||
| "Sandbox providers: Modal, Cloudflare, Daytona, E2B, Fly.io, Vercel", | ||
| "Sessions do not timeout; use maxTurns to prevent infinite loops", | ||
| "Dominant cost is tokens, container cost ~5 cents/hour" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://code.claude.com/docs/en/overview", | ||
| "title": "Claude Code Overview - Getting Started", | ||
| "qualityScore": 88, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 7, "examples": 7, "uniqueness": 4 }, | ||
| "keyInsights": [ | ||
| "Claude Code available in terminal, IDE, desktop app, browser, and web", | ||
| "Pipe patterns: tail -f app.log | claude -p 'alert on anomalies'", | ||
| "CI/CD integration via GitHub Actions and GitLab CI/CD", | ||
| "MCP for connecting to external tools and data sources", | ||
| "Skills system for reusable workflows" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://code.claude.com/docs/en/sub-agents", | ||
| "title": "Claude Code Subagents - Custom AI Agent Teams", | ||
| "qualityScore": 90, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 10, "examples": 8, "uniqueness": 2 }, | ||
| "keyInsights": [ | ||
| "--agents CLI flag accepts JSON to define custom subagents at runtime", | ||
| "Built-in subagents: Explore (haiku, read-only), Plan (inherit, read-only), General-purpose", | ||
| "Subagents can run foreground (blocking) or background (concurrent)", | ||
| "Resume subagents with full conversation history via agent ID", | ||
| "Persistent memory scopes: user, project, local" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://code.claude.com/docs/en/github-actions", | ||
| "title": "Claude Code GitHub Actions - CI/CD Integration", | ||
| "qualityScore": 90, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 9, "examples": 9, "uniqueness": 3 }, | ||
| "keyInsights": [ | ||
| "anthropics/claude-code-action@v1 for GitHub Actions workflows", | ||
| "claude_args parameter passes any CLI arguments", | ||
| "Supports @claude mentions in PR/issue comments", | ||
| "Works with Anthropic API, AWS Bedrock, and Google Vertex AI", | ||
| "Cost optimization: set --max-turns, use concurrency controls" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://code.claude.com/docs/en/common-workflows", | ||
| "title": "Claude Code Common Workflows - Practical Patterns", | ||
| "qualityScore": 87, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 8, "examples": 8, "uniqueness": 1 }, | ||
| "keyInsights": [ | ||
| "claude -p as Unix utility: pipe in, pipe out", | ||
| "Build script integration: npm scripts with claude -p linting", | ||
| "Session management: --continue for recent, --resume for specific, --from-pr for PR-linked", | ||
| "Git worktrees for parallel Claude Code sessions", | ||
| "--permission-mode plan for safe read-only analysis" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/headless.md", | ||
| "title": "Gemini CLI Headless Mode - Complete Guide", | ||
| "qualityScore": 95, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 10, "examples": 10, "uniqueness": 5 }, | ||
| "keyInsights": [ | ||
| "gemini -p for non-interactive mode, stdin piping supported", | ||
| "Three output formats: text, json (with stats/metadata), stream-json (JSONL events)", | ||
| "Six stream-json event types: init, message, tool_use, tool_result, error, result", | ||
| "JSON response includes stats.models (token counts), stats.tools (execution stats), stats.files", | ||
| "Batch processing pattern: loop over files with gemini -p per file" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/cli-reference.md", | ||
| "title": "Gemini CLI Reference - Complete Flag List", | ||
| "qualityScore": 92, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 9, "examples": 7, "uniqueness": 5 }, | ||
| "keyInsights": [ | ||
| "--prompt (-p) for headless, --prompt-interactive (-i) for execute-then-interactive", | ||
| "--resume for session continuation (latest, index, UUID)", | ||
| "--approval-mode: default, auto_edit, yolo", | ||
| "--sandbox for sandboxed execution", | ||
| "--allowed-tools for pre-approving specific tools" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/session-management.md", | ||
| "title": "Gemini CLI Session Management - Persistence and Resumption", | ||
| "qualityScore": 91, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 9, "examples": 8, "uniqueness": 5 }, | ||
| "keyInsights": [ | ||
| "Sessions auto-saved with prompts, responses, tool executions, token stats", | ||
| "Sessions stored in ~/.gemini/tmp/<project_hash>/chats/ (project-specific)", | ||
| "Resume by: --resume (latest), --resume 1 (index), --resume UUID", | ||
| "/resume command opens interactive session browser", | ||
| "Retention policies: maxAge and maxCount settings; maxSessionTurns for cost control" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/settings.md", | ||
| "title": "Gemini CLI Settings - Configuration Reference", | ||
| "qualityScore": 85, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 8, "examples": 5, "uniqueness": 3 }, | ||
| "keyInsights": [ | ||
| "Settings in ~/.gemini/settings.json (user) and .gemini/settings.json (workspace)", | ||
| "model.maxSessionTurns limits conversation turns (-1 = unlimited)", | ||
| "model.compressionThreshold controls context compression trigger", | ||
| "general.defaultApprovalMode sets default approval strategy", | ||
| "output.format can be text or json" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/google-gemini/gemini-cli", | ||
| "title": "Gemini CLI Repository - Overview", | ||
| "qualityScore": 88, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 7, "examples": 7, "uniqueness": 4 }, | ||
| "keyInsights": [ | ||
| "-p flag for non-interactive, --output-format json for structured data", | ||
| "--output-format stream-json for newline-delimited JSON events", | ||
| "-m for model selection (gemini-2.5-flash, etc.)", | ||
| "--include-directories for multi-directory context", | ||
| "GitHub Action available for CI/CD workflows" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/openai/codex/blob/main/codex-cli/README.md", | ||
| "title": "Codex CLI README - Comprehensive Usage Guide", | ||
| "qualityScore": 90, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 9, "examples": 8, "uniqueness": 4 }, | ||
| "keyInsights": [ | ||
| "-q/--quiet for non-interactive mode, --json for structured output", | ||
| "Three approval modes: suggest (default), auto-edit, full-auto", | ||
| "full-auto: network disabled, confined to working directory", | ||
| "Multi-provider support: OpenAI, Azure, Gemini, Ollama, Mistral, DeepSeek, etc.", | ||
| "CODEX_QUIET_MODE=1 and DEBUG=true environment variables for automation" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/openai/codex", | ||
| "title": "Codex CLI Repository - OpenAI's CLI Tool", | ||
| "qualityScore": 82, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 6, "examples": 4, "uniqueness": 2 }, | ||
| "keyInsights": [ | ||
| "Official OpenAI CLI tool for AI-assisted coding", | ||
| "Installation via npm, Homebrew, or binary downloads", | ||
| "Authentication via ChatGPT sign-in or API key", | ||
| "Supports AGENTS.md for project-specific instructions" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/opencode-ai/opencode", | ||
| "title": "OpenCode Repository - Terminal AI Assistant (Archived)", | ||
| "qualityScore": 80, | ||
| "scores": { "authority": 8, "recency": 6, "depth": 8, "examples": 7, "uniqueness": 3 }, | ||
| "keyInsights": [ | ||
| "Archived September 2025, successor is 'Crush' by Charm team", | ||
| "-p for non-interactive, -f json for structured output, -q for quiet", | ||
| "All permissions auto-approved in non-interactive mode", | ||
| "SQLite session persistence", | ||
| "Multi-provider support: OpenAI, Anthropic, Google, Bedrock, Groq, Azure, etc." | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/opencode-ai/opencode/blob/main/README.md", | ||
| "title": "OpenCode README - Complete Feature Reference", | ||
| "qualityScore": 82, | ||
| "scores": { "authority": 8, "recency": 6, "depth": 9, "examples": 7, "uniqueness": 2 }, | ||
| "keyInsights": [ | ||
| "Full CLI flag reference: -p, -f, -q, -d, -c", | ||
| "Configuration in $HOME/.opencode.json or ./.opencode.json", | ||
| "Bubble Tea TUI with Ctrl+A session switching, Ctrl+N new session", | ||
| "Auto-compact at 95% context usage", | ||
| "MCP and LSP integration for tool access" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/google-gemini/gemini-cli/tree/main/docs/cli", | ||
| "title": "Gemini CLI Documentation Index", | ||
| "qualityScore": 75, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 4, "examples": 2, "uniqueness": 5 }, | ||
| "keyInsights": [ | ||
| "Documentation organized: headless.md, session-management.md, cli-reference.md, settings.md", | ||
| "Also covers: skills, custom-commands, authentication, token-caching, checkpointing", | ||
| "Tutorials subdirectory available", | ||
| "Sandbox documentation for safe execution" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://code.claude.com/docs/en/overview#pipe-script-and-automate-with-the-cli", | ||
| "title": "Claude Code - Pipe, Script, and Automate Section", | ||
| "qualityScore": 86, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 6, "examples": 8, "uniqueness": 2 }, | ||
| "keyInsights": [ | ||
| "Unix-style composability: tail -f | claude -p, git diff | claude -p", | ||
| "CI/CD: translate strings, review files, monitor logs", | ||
| "Follows Unix philosophy of piping and composition" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/anthropics/claude-code", | ||
| "title": "Claude Code GitHub Repository", | ||
| "qualityScore": 78, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 5, "examples": 4, "uniqueness": 2 }, | ||
| "keyInsights": [ | ||
| "Multiple installation methods: curl, Homebrew, WinGet, npm", | ||
| "Points to official docs at code.claude.com", | ||
| "Available as terminal CLI, VS Code extension, desktop app" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://platform.claude.com/docs/en/agent-sdk/quickstart#key-concepts", | ||
| "title": "Agent SDK Key Concepts - Tools and Permissions", | ||
| "qualityScore": 88, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 7, "examples": 8, "uniqueness": 3 }, | ||
| "keyInsights": [ | ||
| "Tool tiers: Read-only (Read, Glob, Grep), Modify (Read, Edit, Glob), Full (Read, Edit, Bash, Glob, Grep)", | ||
| "Permission modes: acceptEdits, bypassPermissions, default (with canUseTool callback)", | ||
| "Agent loop: query() returns async iterator, handles tool execution automatically" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://platform.claude.com/docs/en/agent-sdk/overview#sessions", | ||
| "title": "Agent SDK Sessions Tab - Overview", | ||
| "qualityScore": 90, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 8, "examples": 9, "uniqueness": 2 }, | ||
| "keyInsights": [ | ||
| "Capture session_id from init message, use resume option to continue", | ||
| "Context maintained across queries: files read, analysis done, conversation history", | ||
| "Fork sessions to explore different approaches from same starting point" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://platform.claude.com/docs/en/agent-sdk/overview#hooks", | ||
| "title": "Agent SDK Hooks - Lifecycle Callbacks", | ||
| "qualityScore": 87, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 8, "examples": 8, "uniqueness": 3 }, | ||
| "keyInsights": [ | ||
| "PreToolUse, PostToolUse, Stop, SessionStart, SessionEnd hooks", | ||
| "Hooks for validation, logging, blocking, transforming agent behavior", | ||
| "Python and TypeScript implementations with HookMatcher" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://platform.claude.com/docs/en/agent-sdk/overview#mcp", | ||
| "title": "Agent SDK MCP Integration", | ||
| "qualityScore": 86, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 7, "examples": 8, "uniqueness": 3 }, | ||
| "keyInsights": [ | ||
| "MCP servers configurable via mcpServers option in SDK", | ||
| "Example: Playwright MCP for browser automation", | ||
| "Connect to databases, browsers, APIs, and custom tools" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://platform.claude.com/docs/en/agent-sdk/overview#subagents", | ||
| "title": "Agent SDK Subagents - Task Delegation", | ||
| "qualityScore": 85, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 7, "examples": 8, "uniqueness": 3 }, | ||
| "keyInsights": [ | ||
| "Define custom agents with AgentDefinition class", | ||
| "Subagents have independent tools, prompts, and model settings", | ||
| "parent_tool_use_id tracks which messages belong to which subagent" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://platform.claude.com/docs/en/agent-sdk/overview#permissions", | ||
| "title": "Agent SDK Permissions - Tool Control", | ||
| "qualityScore": 84, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 7, "examples": 7, "uniqueness": 2 }, | ||
| "keyInsights": [ | ||
| "allowedTools for allowlist, permissionMode for overall policy", | ||
| "bypassPermissions for headless/CI use", | ||
| "Read-only agents: allow only Read, Glob, Grep" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://platform.claude.com/docs/en/agent-sdk/overview#compare-agent-sdk-to-cli", | ||
| "title": "Agent SDK vs CLI Comparison", | ||
| "qualityScore": 83, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 6, "examples": 4, "uniqueness": 5 }, | ||
| "keyInsights": [ | ||
| "CLI best for interactive dev and one-off tasks", | ||
| "SDK best for CI/CD, custom apps, production automation", | ||
| "Same capabilities, different interface", | ||
| "Workflows translate directly between CLI and SDK" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://code.claude.com/docs/en/sub-agents#built-in-subagents", | ||
| "title": "Claude Code Built-in Subagents Reference", | ||
| "qualityScore": 82, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 8, "examples": 6, "uniqueness": 2 }, | ||
| "keyInsights": [ | ||
| "Explore: Haiku model, read-only, for fast codebase search", | ||
| "Plan: inherit model, read-only, for plan mode research", | ||
| "General-purpose: inherit model, all tools, for complex tasks", | ||
| "Background subagents run concurrently with pre-approved permissions" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://code.claude.com/docs/en/sub-agents#configure-subagents", | ||
| "title": "Claude Code Subagent Configuration", | ||
| "qualityScore": 84, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 9, "examples": 7, "uniqueness": 2 }, | ||
| "keyInsights": [ | ||
| "Subagent scopes: CLI flag (session), .claude/agents/ (project), ~/.claude/agents/ (user), plugins", | ||
| "YAML frontmatter with name, description, tools, model, permissionMode, hooks, skills, memory", | ||
| "Persistent memory with user/project/local scopes", | ||
| "PreToolUse hooks for conditional validation" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://code.claude.com/docs/en/common-workflows#use-plan-mode-for-safe-code-analysis", | ||
| "title": "Claude Code Plan Mode - Safe Analysis", | ||
| "qualityScore": 80, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 7, "examples": 6, "uniqueness": 3 }, | ||
| "keyInsights": [ | ||
| "Plan mode: read-only exploration, no modifications", | ||
| "claude --permission-mode plan -p 'analyze the system'", | ||
| "Safe for CI/CD where you only want analysis, not changes" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://code.claude.com/docs/en/common-workflows#control-output-format", | ||
| "title": "Claude Code Output Format Control", | ||
| "qualityScore": 79, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 6, "examples": 7, "uniqueness": 1 }, | ||
| "keyInsights": [ | ||
| "--output-format text for simple integrations", | ||
| "--output-format json for full conversation log with metadata", | ||
| "--output-format stream-json for real-time processing" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://code.claude.com/docs/en/common-workflows#resume-previous-conversations", | ||
| "title": "Claude Code Session Resumption Patterns", | ||
| "qualityScore": 81, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 7, "examples": 6, "uniqueness": 1 }, | ||
| "keyInsights": [ | ||
| "--continue for most recent conversation in current directory", | ||
| "--resume for specific session by name or ID", | ||
| "--from-pr for PR-linked sessions", | ||
| "/resume picker with keyboard shortcuts", | ||
| "Session naming with /rename for easy recall" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://code.claude.com/docs/en/github-actions#configuration-examples", | ||
| "title": "Claude Code GitHub Actions Configuration", | ||
| "qualityScore": 84, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 7, "examples": 8, "uniqueness": 2 }, | ||
| "keyInsights": [ | ||
| "anthropics/claude-code-action@v1 with unified prompt interface", | ||
| "claude_args for CLI passthrough: --max-turns, --model, --mcp-config, --allowed-tools", | ||
| "Supports skills like /review as prompts", | ||
| "Bedrock and Vertex AI authentication via OIDC" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://platform.claude.com/docs/en/agent-sdk/hosting#production-deployment-patterns", | ||
| "title": "Agent SDK Production Deployment Patterns", | ||
| "qualityScore": 87, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 8, "examples": 6, "uniqueness": 5 }, | ||
| "keyInsights": [ | ||
| "Ephemeral: new container per task, destroy when complete", | ||
| "Long-running: persistent instances for proactive agents", | ||
| "Hybrid: ephemeral with session resumption for intermittent interaction", | ||
| "Single container: multiple SDK processes for agent collaboration" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://platform.claude.com/docs/en/agent-sdk/streaming-output#build-a-streaming-ui", | ||
| "title": "Agent SDK Streaming UI Pattern", | ||
| "qualityScore": 83, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 8, "examples": 9, "uniqueness": 3 }, | ||
| "keyInsights": [ | ||
| "Track in_tool flag to show status indicators during tool execution", | ||
| "content_block_start/delta/stop for tool call tracking", | ||
| "text_delta events for real-time text display", | ||
| "Combine text and tool streaming for cohesive UI" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://platform.claude.com/docs/en/agent-sdk/sessions#forking-sessions", | ||
| "title": "Agent SDK Session Forking - Branch Conversations", | ||
| "qualityScore": 84, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 8, "examples": 8, "uniqueness": 4 }, | ||
| "keyInsights": [ | ||
| "forkSession: true creates new session from resume point", | ||
| "Original session preserved unchanged", | ||
| "Useful for exploring alternatives, A/B testing approaches", | ||
| "Both forked and original can be resumed independently" | ||
| ] | ||
| } | ||
| ] | ||
| } |
| { | ||
| "topic": "All-in-one plus modular packages - batteries included but removable pattern", | ||
| "slug": "all-in-one-plus-modular-packages", | ||
| "generated": "2026-02-21T00:00:00Z", | ||
| "depth": "deep", | ||
| "totalSources": 40, | ||
| "sources": [ | ||
| { | ||
| "url": "https://github.com/aws/aws-sdk-js-v3/blob/main/UPGRADING.md", | ||
| "title": "AWS SDK v3 Migration Guide", | ||
| "qualityScore": 92, | ||
| "scores": { "authority": 10, "recency": 8, "depth": 10, "examples": 9, "uniqueness": 9 }, | ||
| "keyInsights": ["Monolith-to-modular migration case study", "Per-service @aws-sdk/client-* packages", "Middleware stack replaces plugin system", "Tree-shaking reduces bundle size"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/lodash/lodash", | ||
| "title": "Lodash - Per-Method Packages", | ||
| "qualityScore": 85, | ||
| "scores": { "authority": 9, "recency": 6, "depth": 8, "examples": 8, "uniqueness": 10 }, | ||
| "keyInsights": ["Full library ~24kB, core ~4kB, per-method ~1kB", "lodash-es for tree-shaking", "Per-method packages caused version fragmentation"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/lerna/lerna", | ||
| "title": "Lerna - Monorepo Management", | ||
| "qualityScore": 88, | ||
| "scores": { "authority": 9, "recency": 8, "depth": 9, "examples": 8, "uniqueness": 7 }, | ||
| "keyInsights": ["Fixed vs independent versioning", "Version and publish commands", "Now maintained by Nx", "Used by 174k+ dependent projects"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/changesets/changesets", | ||
| "title": "Changesets - Version and Changelog Management", | ||
| "qualityScore": 90, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 9, "examples": 8, "uniqueness": 8 }, | ||
| "keyInsights": ["Intent-based changelogs", "Batches multiple bumps into single releases", "Keeps internally-dependent packages synchronized", "Used by Astro, Chakra UI, Apollo Client"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/Rich-Harris/degit", | ||
| "title": "degit - Project Scaffolding", | ||
| "qualityScore": 82, | ||
| "scores": { "authority": 8, "recency": 4, "depth": 8, "examples": 9, "uniqueness": 9 }, | ||
| "keyInsights": ["Downloads tar archive not git clone", "Caches tarballs locally", "Supports subdirectory extraction", "Multi-platform git hosting support"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/tiged/tiged", | ||
| "title": "tiged - Maintained degit Fork", | ||
| "qualityScore": 78, | ||
| "scores": { "authority": 7, "recency": 8, "depth": 7, "examples": 8, "uniqueness": 6 }, | ||
| "keyInsights": ["Actively maintained fork of degit", "Auto-detects default branch", "Improved Windows compatibility", "Offline mode support"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/pnpm/pnpm", | ||
| "title": "pnpm - Performant Package Manager", | ||
| "qualityScore": 87, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 7, "examples": 7, "uniqueness": 8 }, | ||
| "keyInsights": ["Content-addressable storage prevents duplicates", "Built-in workspace support", "Used by Microsoft Rush at scale", "Strict isolation prevents phantom dependencies"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/vercel/turborepo", | ||
| "title": "Turborepo - Monorepo Build Orchestration", | ||
| "qualityScore": 86, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 7, "examples": 7, "uniqueness": 7 }, | ||
| "keyInsights": ["Written in Rust for performance", "Remote caching for builds", "Task pipeline orchestration", "29.8k stars, 115k dependents"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/facebook/create-react-app", | ||
| "title": "Create React App - CLI Scaffolding Pattern", | ||
| "qualityScore": 80, | ||
| "scores": { "authority": 9, "recency": 4, "depth": 8, "examples": 8, "uniqueness": 8 }, | ||
| "keyInsights": ["npx-based scaffolding pattern", "Single dependency abstracts build complexity", "Eject option for customization", "Iconic but now deprecated"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/babel/babel/blob/main/CONTRIBUTING.md", | ||
| "title": "Babel - Monorepo Plugin Architecture", | ||
| "qualityScore": 89, | ||
| "scores": { "authority": 10, "recency": 8, "depth": 9, "examples": 8, "uniqueness": 8 }, | ||
| "keyInsights": ["@babel/core + @babel/plugin-transform-* pattern", "Presets bundle common plugins", "Parser and generator as separate packages", "Fixed versioning across all packages"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/eslint/eslint", | ||
| "title": "ESLint - Pluggable Linting Architecture", | ||
| "qualityScore": 83, | ||
| "scores": { "authority": 9, "recency": 8, "depth": 7, "examples": 7, "uniqueness": 7 }, | ||
| "keyInsights": ["Every rule is a plugin", "Separate parser (Espree)", "Monorepo with packages/ directory", "Flat config system in v9"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/mui/material-ui", | ||
| "title": "Material UI - Component Library Packaging", | ||
| "qualityScore": 82, | ||
| "scores": { "authority": 9, "recency": 8, "depth": 7, "examples": 7, "uniqueness": 7 }, | ||
| "keyInsights": ["@mui/material as primary package", "MUI X for advanced components as separate packages", "pnpm workspaces monorepo", "3000+ contributors"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/tailwindlabs/tailwindcss", | ||
| "title": "Tailwind CSS - Core + Plugin Architecture", | ||
| "qualityScore": 81, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 6, "examples": 7, "uniqueness": 7 }, | ||
| "keyInsights": ["Core tailwindcss + @tailwindcss/* plugins", "Monorepo with packages/", "15.3 million dependents", "Rust + TypeScript build"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/NixOS/nixpkgs", | ||
| "title": "Nixpkgs - Declarative Package Collection", | ||
| "qualityScore": 79, | ||
| "scores": { "authority": 9, "recency": 8, "depth": 7, "examples": 5, "uniqueness": 10 }, | ||
| "keyInsights": ["120,000+ package definitions in one repo", "Monolithic source, selective installation", "Channel-based distribution", "Declarative package management"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/rust-lang/cargo/blob/master/src/doc/src/reference/workspaces.md", | ||
| "title": "Cargo Workspaces - Rust Multi-Crate Management", | ||
| "qualityScore": 88, | ||
| "scores": { "authority": 10, "recency": 8, "depth": 9, "examples": 8, "uniqueness": 9 }, | ||
| "keyInsights": ["Shared Cargo.lock and build output", "workspace.dependencies inheritance", "Virtual vs root package workspaces", "Glob patterns for member discovery"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/wbthomason/packer.nvim", | ||
| "title": "Packer.nvim - Plugin Installation Patterns", | ||
| "qualityScore": 68, | ||
| "scores": { "authority": 6, "recency": 4, "depth": 7, "examples": 8, "uniqueness": 7 }, | ||
| "keyInsights": ["Multi-source plugin installation", "Lazy-loading via autocommands", "Compiled loader for performance", "Now replaced by lazy.nvim"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/sindresorhus/ky", | ||
| "title": "Ky - Small Focused Package Philosophy", | ||
| "qualityScore": 72, | ||
| "scores": { "authority": 8, "recency": 8, "depth": 5, "examples": 7, "uniqueness": 7 }, | ||
| "keyInsights": ["Zero-dependency philosophy", "Single responsibility design", "Small bundle as core value", "Composable over monolithic"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/microsoft/rushstack", | ||
| "title": "Rush Stack - Enterprise Monorepo Tooling", | ||
| "qualityScore": 86, | ||
| "scores": { "authority": 9, "recency": 8, "depth": 9, "examples": 7, "uniqueness": 8 }, | ||
| "keyInsights": ["50+ published packages from one repo", "Rush build orchestrator + Heft compilation", "API Extractor for type management", "Strict dependency policies"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/nrwl/nx", | ||
| "title": "Nx - Monorepo Platform", | ||
| "qualityScore": 85, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 7, "examples": 7, "uniqueness": 7 }, | ||
| "keyInsights": ["Computation caching and task distribution", "Affected analysis skips unchanged packages", "create-nx-workspace scaffolder", "28.2k stars, 175k dependents"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/volta-cli/volta", | ||
| "title": "Volta - JavaScript Tool Manager", | ||
| "qualityScore": 70, | ||
| "scores": { "authority": 7, "recency": 4, "depth": 7, "examples": 7, "uniqueness": 8 }, | ||
| "keyInsights": ["Per-project version switching", "Tools persist across Node upgrades", "Now unmaintained, recommend mise", "Used by Microsoft TypeScript, Sentry"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/denoland/deno", | ||
| "title": "Deno - Modern Runtime with JSR", | ||
| "qualityScore": 80, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 6, "examples": 6, "uniqueness": 9 }, | ||
| "keyInsights": ["JSR as modern package registry", "URL-based imports", "Standard library at jsr.io/@std", "Import maps for module resolution"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/goreleaser/goreleaser", | ||
| "title": "GoReleaser - Binary Distribution", | ||
| "qualityScore": 83, | ||
| "scores": { "authority": 8, "recency": 9, "depth": 7, "examples": 7, "uniqueness": 9 }, | ||
| "keyInsights": ["Automated binary building and releasing", "Multi-language support", "GitHub Releases distribution", "Cross-platform compilation"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/oven-sh/bun", | ||
| "title": "Bun - Fast Runtime with Workspaces", | ||
| "qualityScore": 78, | ||
| "scores": { "authority": 8, "recency": 9, "depth": 6, "examples": 6, "uniqueness": 7 }, | ||
| "keyInsights": ["Native workspace support in bun install", "Compatible with npm workspace config", "Custom registry support", "Fastest package installation"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/homebrew/brew", | ||
| "title": "Homebrew - Formula/Tap/Cask Model", | ||
| "qualityScore": 77, | ||
| "scores": { "authority": 9, "recency": 8, "depth": 6, "examples": 5, "uniqueness": 8 }, | ||
| "keyInsights": ["Formulae for packages, taps for sources, casks for apps", "Hierarchical registry with core + community taps", "Cellar-based installation model"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/withastro/astro", | ||
| "title": "Astro - Core + Integrations Pattern", | ||
| "qualityScore": 88, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 8, "examples": 8, "uniqueness": 8 }, | ||
| "keyInsights": ["Core astro + @astrojs/* integrations", "Framework adapters, deployment adapters, utility integrations", "npm create astro@latest scaffolder", "1033 contributors, 257k users"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/vitejs/vite", | ||
| "title": "Vite - Core + Official Plugins", | ||
| "qualityScore": 84, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 7, "examples": 7, "uniqueness": 7 }, | ||
| "keyInsights": ["Core vite + @vitejs/plugin-* packages", "create-vite scaffolding tool", "pnpm workspaces monorepo", "Independent package publishing"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/angular/angular", | ||
| "title": "Angular - Scoped Package Architecture", | ||
| "qualityScore": 85, | ||
| "scores": { "authority": 10, "recency": 8, "depth": 7, "examples": 7, "uniqueness": 7 }, | ||
| "keyInsights": ["@angular/core, @angular/router, @angular/forms as separate packages", "Fixed versioning across all packages", "ng new installs common set together", "Monorepo with packages/ directory"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/jestjs/jest", | ||
| "title": "Jest - Meta-Package over Monorepo", | ||
| "qualityScore": 84, | ||
| "scores": { "authority": 9, "recency": 8, "depth": 7, "examples": 7, "uniqueness": 8 }, | ||
| "keyInsights": ["jest meta-package wraps @jest/* packages", "Lerna + Yarn workspaces monorepo", "Users can import individual @jest/* utilities", "Complete solution or modular adoption"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/webpack/webpack", | ||
| "title": "Webpack - Extensible Core + Plugin/Loader Architecture", | ||
| "qualityScore": 83, | ||
| "scores": { "authority": 9, "recency": 7, "depth": 8, "examples": 8, "uniqueness": 7 }, | ||
| "keyInsights": ["Most features are plugins internally", "Loaders preprocess files, plugins hook lifecycle", "Rich third-party ecosystem", "Core remains lightweight"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/ds300/patch-package", | ||
| "title": "patch-package - Dependency Patching", | ||
| "qualityScore": 75, | ||
| "scores": { "authority": 7, "recency": 7, "depth": 7, "examples": 8, "uniqueness": 8 }, | ||
| "keyInsights": ["Fix dependencies without forking", "Patches applied via postinstall hook", "Version-tracked .patch files", "Team-shareable via version control"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/vercel/next.js", | ||
| "title": "Next.js - Monorepo with Multiple Tools", | ||
| "qualityScore": 82, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 6, "examples": 6, "uniqueness": 6 }, | ||
| "keyInsights": ["next meta-package + @next/* scoped packages", "Lerna + Turbo + pnpm triple tooling", "Rust crates for performance-critical code", "packages/ + crates/ hybrid structure"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/unjs/unbuild", | ||
| "title": "unbuild - Unified Library Build System", | ||
| "qualityScore": 80, | ||
| "scores": { "authority": 8, "recency": 8, "depth": 7, "examples": 8, "uniqueness": 7 }, | ||
| "keyInsights": ["Auto-infers build config from package.json", "Dual CJS/ESM output", "Bundleless option preserves file structure", "Dependency checking for missing/unused"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/antfu/ni", | ||
| "title": "ni - Package Manager Abstraction", | ||
| "qualityScore": 76, | ||
| "scores": { "authority": 7, "recency": 8, "depth": 7, "examples": 8, "uniqueness": 8 }, | ||
| "keyInsights": ["Detects package manager from lockfiles", "Unified commands across npm/yarn/pnpm/bun/deno", "Workspace-aware operations", "Interactive package selection"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/sindresorhus/np", | ||
| "title": "np - Better npm Publish", | ||
| "qualityScore": 79, | ||
| "scores": { "authority": 8, "recency": 8, "depth": 8, "examples": 7, "uniqueness": 7 }, | ||
| "keyInsights": ["Interactive publish workflow", "Pre-publish validation and testing", "Rollback on failure", "Works with npm, yarn, pnpm, bun"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/pypa/pip", | ||
| "title": "pip - Python Package Installer", | ||
| "qualityScore": 72, | ||
| "scores": { "authority": 9, "recency": 8, "depth": 5, "examples": 5, "uniqueness": 7 }, | ||
| "keyInsights": ["extras_require for optional dependency groups", "pip install pkg[full] syntax", "Regular 3-month release cycle"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/jsr-io/jsr", | ||
| "title": "JSR - Modern JavaScript Registry", | ||
| "qualityScore": 84, | ||
| "scores": { "authority": 8, "recency": 10, "depth": 8, "examples": 6, "uniqueness": 10 }, | ||
| "keyInsights": ["Cloud-native registry architecture", "npm compatibility tarballs generated automatically", "Rust API + Cloudflare Workers edge", "Direct publish from Deno CLI"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/egoist/tsup", | ||
| "title": "tsup - TypeScript Bundler", | ||
| "qualityScore": 74, | ||
| "scores": { "authority": 7, "recency": 6, "depth": 7, "examples": 7, "uniqueness": 6 }, | ||
| "keyInsights": ["esbuild-based for speed", "Dual CJS/ESM output", "Now recommends migration to tsdown", "126k dependents despite unmaintained status"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/privatenumber/pkgroll", | ||
| "title": "pkgroll - Zero-Config Package Bundler", | ||
| "qualityScore": 80, | ||
| "scores": { "authority": 7, "recency": 8, "depth": 8, "examples": 8, "uniqueness": 8 }, | ||
| "keyInsights": ["Reads entry points from package.json exports", "Auto-maps dist/ to src/", "TypeScript .d.ts bundling", "Format detection from file extensions"] | ||
| }, | ||
| { | ||
| "url": "https://arethetypeswrong.github.io", | ||
| "title": "Are The Types Wrong - Package Type Checker", | ||
| "qualityScore": 82, | ||
| "scores": { "authority": 8, "recency": 9, "depth": 8, "examples": 7, "uniqueness": 10 }, | ||
| "keyInsights": ["Detects 12 categories of CJS/ESM type problems", "Tests node10, node16, bundler resolution modes", "CLI tool for local analysis", "Catches masquerading module formats"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/semantic-release/semantic-release", | ||
| "title": "semantic-release - Automated Versioning", | ||
| "qualityScore": 84, | ||
| "scores": { "authority": 9, "recency": 8, "depth": 8, "examples": 7, "uniqueness": 7 }, | ||
| "keyInsights": ["Commit-based version determination", "Nine-step release process", "Plugin-based architecture", "23.3k stars, widely adopted"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/pinojs/pino", | ||
| "title": "Pino - Modular Logging Architecture", | ||
| "qualityScore": 81, | ||
| "scores": { "authority": 8, "recency": 8, "depth": 7, "examples": 8, "uniqueness": 8 }, | ||
| "keyInsights": ["Core pino + pino-pretty + transport plugins", "Transports run in worker threads", "Minimal core for maximum performance", "Composable ecosystem"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/Effect-TS/effect", | ||
| "title": "Effect - Modern Scoped Package Ecosystem", | ||
| "qualityScore": 86, | ||
| "scores": { "authority": 8, "recency": 10, "depth": 8, "examples": 7, "uniqueness": 9 }, | ||
| "keyInsights": ["Core effect + 20+ @effect/* packages", "Platform-specific packages (node, bun, browser)", "SQL with 12+ database adapters", "AI integrations as separate packages"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/sindresorhus/awesome-npm", | ||
| "title": "Awesome npm - Publishing Best Practices", | ||
| "qualityScore": 74, | ||
| "scores": { "authority": 8, "recency": 6, "depth": 6, "examples": 6, "uniqueness": 6 }, | ||
| "keyInsights": ["Publishing tools comparison (np, semantic-release, release-it)", "Lifecycle scripts for automation", "npm link for local development", "Security validation before publish"] | ||
| } | ||
| ] | ||
| } |
| { | ||
| "topic": "CLI-first browser automation for AI agents", | ||
| "slug": "cli-browser-automation-agents", | ||
| "generated": "2026-02-20T00:00:00Z", | ||
| "depth": "deep", | ||
| "totalSources": 32, | ||
| "sources": [ | ||
| { | ||
| "url": "https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/cli/program.ts", | ||
| "title": "Playwright CLI Source - program.ts", | ||
| "qualityScore": 97, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 10, "examples": 10, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "Complete CLI command list: codegen, screenshot, pdf, open, show-trace", | ||
| "screenshot command: <url> <filename> with --full-page, --wait-for-selector, --wait-for-timeout", | ||
| "pdf command: <url> <filename>, Chromium-only, --paper-format", | ||
| "--save-storage captures storageState at session end", | ||
| "--load-storage injects storageState into browser context", | ||
| "Standard options shared across all commands: --browser, --device, --viewport-size, --user-data-dir, --lang, --timezone, --proxy-server" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/cli/program.ts", | ||
| "title": "Playwright CLI - screenshot and pdf syntax", | ||
| "qualityScore": 95, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 9, "examples": 9, "uniqueness": 6 }, | ||
| "keyInsights": [ | ||
| "pdf --paper-format accepts: Letter, Legal, A0-A6, Tabloid, Ledger", | ||
| "--save-storage output format: JSON with cookies[] and origins[] arrays", | ||
| "screenshot -b webkit <url> example.png", | ||
| "open command launches interactive headed browser" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/server/browserContext.ts", | ||
| "title": "Playwright storageState() method implementation", | ||
| "qualityScore": 90, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 9, "examples": 7, "uniqueness": 4 }, | ||
| "keyInsights": [ | ||
| "storageState returns {cookies: NetworkCookie[], origins: [{origin, localStorage, indexedDB}]}", | ||
| "Cookies can be filtered by name, domain, path patterns", | ||
| "Iterates existing pages then creates temp pages to access all origins" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/server/network.ts", | ||
| "title": "Playwright NetworkCookie type", | ||
| "qualityScore": 88, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 8, "examples": 6, "uniqueness": 4 }, | ||
| "keyInsights": [ | ||
| "NetworkCookie fields: name, value, domain, path, expires, httpOnly, secure, sameSite", | ||
| "expires is Unix epoch float, -1 means session cookie" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/microsoft/playwright/blob/main/docs/src/auth.md", | ||
| "title": "Playwright Authentication Documentation", | ||
| "qualityScore": 95, | ||
| "scores": { "authority": 10, "recency": 9, "depth": 10, "examples": 9, "uniqueness": 6 }, | ||
| "keyInsights": [ | ||
| "Recommended: save storageState to playwright/.auth/ directory, gitignore it", | ||
| "Setup project pattern: dedicate one project to authenticate, others depend on it", | ||
| "Per-worker auth using parallelIndex for isolated accounts", | ||
| "context.storageState() called after login, saved to file" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/microsoft/playwright/blob/main/docs/src/codegen.md", | ||
| "title": "Playwright Codegen Documentation", | ||
| "qualityScore": 93, | ||
| "scores": { "authority": 10, "recency": 9, "depth": 9, "examples": 9, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "npx playwright codegen [URL] opens headed browser + code recording panel", | ||
| "--save-storage=auth.json saves cookies/localStorage/IndexedDB at session end", | ||
| "--viewport-size, --device, --color-scheme, --timezone, --geolocation, --lang options", | ||
| "Recordings can be saved with -o output.js" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://raw.githubusercontent.com/microsoft/playwright-mcp/main/README.md", | ||
| "title": "Playwright MCP README - Tools List", | ||
| "qualityScore": 97, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 10, "examples": 9, "uniqueness": 9 }, | ||
| "keyInsights": [ | ||
| "30+ MCP tools: browser_navigate, browser_click, browser_type, browser_snapshot, browser_take_screenshot, browser_evaluate, browser_fill_form, browser_select_option, browser_hover, browser_drag, browser_press_key, browser_handle_dialog, browser_file_upload, browser_network_requests, browser_console_messages, browser_wait_for, browser_tabs, browser_resize, browser_close, browser_install", | ||
| "Vision mode adds: browser_mouse_click_xy, browser_mouse_move_xy, browser_mouse_drag_xy, browser_mouse_wheel, browser_mouse_down/up", | ||
| "PDF mode adds: browser_pdf_save", | ||
| "Testing mode adds: browser_verify_text_visible, browser_verify_element_visible, browser_generate_locator, browser_verify_value, browser_verify_list_visible", | ||
| "browser_snapshot returns accessibility tree (not screenshot) - token-efficient", | ||
| "browser_run_code executes custom Playwright snippets" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://raw.githubusercontent.com/microsoft/playwright-mcp/main/README.md", | ||
| "title": "Playwright MCP README - Auth and Config", | ||
| "qualityScore": 95, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 10, "examples": 8, "uniqueness": 8 }, | ||
| "keyInsights": [ | ||
| "Three profile modes: persistent (default), isolated (--isolated), extension (--extension)", | ||
| "--storage-state: load cookies+localStorage from Playwright storageState JSON file", | ||
| "--user-data-dir: use/persist real Chrome profile with existing logins", | ||
| "--cdp-endpoint: connect to already-running Chrome via CDP URL", | ||
| "--save-session: save entire Playwright session to output directory", | ||
| "Chrome Extension bridge (--extension flag) connects to existing browser tabs with active sessions", | ||
| "--init-page: TypeScript file for custom page-level setup", | ||
| "--init-script: JavaScript evaluated before page scripts" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://raw.githubusercontent.com/microsoft/playwright-mcp/main/README.md", | ||
| "title": "Playwright MCP README - Installation and MCP Config", | ||
| "qualityScore": 93, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 9, "examples": 9, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "Install: add {command: npx, args: [@playwright/mcp@latest]} to mcpServers config", | ||
| "Supported clients: VS Code, Cursor, Claude Desktop, Cline, Windsurf", | ||
| "--caps argument enables capability sets: vision, pdf, devtools, testing", | ||
| "Default mode: accessibility snapshots (not screenshots), no vision model required", | ||
| "--headless flag for headless mode; default is headed", | ||
| "--browser: chrome, firefox, webkit, msedge", | ||
| "--allowed-origins and --blocked-origins for security" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/browser-use/browser-use", | ||
| "title": "browser-use GitHub Repository", | ||
| "qualityScore": 92, | ||
| "scores": { "authority": 9, "recency": 10, "depth": 9, "examples": 10, "uniqueness": 9 }, | ||
| "keyInsights": [ | ||
| "Python library designed for LLM agents to control browsers", | ||
| "78.6K GitHub stars as of 2026", | ||
| "Provides both CLI (browser-use open/state/click/type/screenshot/close) and Python API", | ||
| "CLI maintains stateful session between commands", | ||
| "LLM-native: elements presented as indexed list for LLM selection", | ||
| "ChatBrowserUse model optimized for browser automation (3-5x faster than generic models)" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://raw.githubusercontent.com/browser-use/browser-use/main/README.md", | ||
| "title": "browser-use README - Code Examples", | ||
| "qualityScore": 93, | ||
| "scores": { "authority": 9, "recency": 10, "depth": 9, "examples": 10, "uniqueness": 9 }, | ||
| "keyInsights": [ | ||
| "CLI: browser-use open [URL], browser-use state, browser-use click [index], browser-use type [text], browser-use screenshot [file], browser-use close", | ||
| "Agent API: Agent(task=..., llm=..., browser=...)", | ||
| "Auth: reuse existing Chrome profile; profile sync via profile.sh script", | ||
| "Custom tools via @tools.action decorator", | ||
| "Sandbox deployment pattern for production", | ||
| "Install: uv add browser-use + uvx browser-use install (for Chromium)" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/cyrus-and/chrome-remote-interface", | ||
| "title": "chrome-remote-interface GitHub Repository", | ||
| "qualityScore": 85, | ||
| "scores": { "authority": 8, "recency": 7, "depth": 8, "examples": 8, "uniqueness": 8 }, | ||
| "keyInsights": [ | ||
| "Node.js CDP client with CLI: chrome-remote-interface new/list/close/inspect/version/activate", | ||
| "inspect subcommand provides interactive REPL with autocompletion", | ||
| "REPL commands: .help, .reset, .target", | ||
| "REPL usage: Runtime.evaluate({expression: '...'}), Page.navigate({url: '...'})", | ||
| "npm install -g chrome-remote-interface for CLI" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/cyrus-and/chrome-remote-interface/blob/master/README.md", | ||
| "title": "chrome-remote-interface README - Full Reference", | ||
| "qualityScore": 87, | ||
| "scores": { "authority": 8, "recency": 7, "depth": 9, "examples": 8, "uniqueness": 8 }, | ||
| "keyInsights": [ | ||
| "CLI target management: new 'http://url', close '<id>', list", | ||
| "Library API: CDP() connects to localhost:9222 by default", | ||
| "Network.getAllCookies() returns all browser cookies", | ||
| "Page.navigate({url}) for navigation", | ||
| "Supports custom protocol descriptors" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/GoogleChrome/chrome-launcher", | ||
| "title": "chrome-launcher GitHub Repository", | ||
| "qualityScore": 82, | ||
| "scores": { "authority": 8, "recency": 8, "depth": 7, "examples": 7, "uniqueness": 6 }, | ||
| "keyInsights": [ | ||
| "Node.js package for launching Chrome with sensible defaults", | ||
| "Opens Chrome's remote-debugging-port on an available port", | ||
| "ChromeLauncher.launch({startingUrl}) for headed launch", | ||
| "Recommend chrome-remote-interface for CDP interaction after launch", | ||
| "Handles profile management and cleanup" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/GoogleChromeLabs/chrome-for-testing", | ||
| "title": "Chrome for Testing", | ||
| "qualityScore": 78, | ||
| "scores": { "authority": 9, "recency": 10, "depth": 6, "examples": 5, "uniqueness": 5 }, | ||
| "keyInsights": [ | ||
| "Chrome flavor specifically for automation, no automatic updates", | ||
| "Binaries: chrome, chromedriver, chrome-headless-shell", | ||
| "Platforms: linux64, mac-arm64, mac-x64, win32, win64", | ||
| "Managed via @puppeteer/browsers package" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/browserless/browserless", | ||
| "title": "Browserless GitHub Repository", | ||
| "qualityScore": 85, | ||
| "scores": { "authority": 8, "recency": 9, "depth": 8, "examples": 8, "uniqueness": 8 }, | ||
| "keyInsights": [ | ||
| "Docker-based headless browser as a service", | ||
| "REST API: /screenshot, /pdf, /html, /scrape, /function endpoints", | ||
| "WebSocket CDP endpoint for Puppeteer/Playwright connection", | ||
| "Self-hosted or cloud deployment", | ||
| "Session management with configurable concurrency", | ||
| "Real-time debug viewer" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/steel-dev/steel-browser", | ||
| "title": "Steel Browser GitHub Repository", | ||
| "qualityScore": 84, | ||
| "scores": { "authority": 7, "recency": 10, "depth": 8, "examples": 8, "uniqueness": 9 }, | ||
| "keyInsights": [ | ||
| "Open-source browser API for AI agents", | ||
| "Sessions API: POST /v1/sessions with blockAds, proxyUrl, dimensions options", | ||
| "Quick actions: POST /v1/scrape, /v1/screenshot, /v1/pdf", | ||
| "Sessions persist cookies across page navigations", | ||
| "Playwright/Puppeteer/Selenium can connect via CDP to session" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/steel-dev/steel-browser/blob/main/README.md", | ||
| "title": "Steel Browser REST API Reference", | ||
| "qualityScore": 86, | ||
| "scores": { "authority": 7, "recency": 10, "depth": 9, "examples": 10, "uniqueness": 9 }, | ||
| "keyInsights": [ | ||
| "Base URL: http://localhost:3000", | ||
| "POST /v1/scrape {url, delay?} - returns HTML/markdown", | ||
| "POST /v1/screenshot {url, fullPage?} - returns PNG", | ||
| "POST /v1/pdf {url} - returns PDF", | ||
| "POST /v1/sessions {blockAds?, proxyUrl?, dimensions?, isSelenium?} - returns sessionId", | ||
| "Connect Playwright: chromium.connectOverCDP('ws://localhost:3000?sessionId=...')" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/mendableai/firecrawl", | ||
| "title": "Firecrawl - Web Data API", | ||
| "qualityScore": 78, | ||
| "scores": { "authority": 7, "recency": 10, "depth": 7, "examples": 7, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "REST API: /v2/scrape, /v2/search, /v2/agent, /v2/crawl, /v2/map", | ||
| "actions feature enables login flows: write+click sequences before scraping", | ||
| "Converts pages to markdown/HTML/JSON/screenshots", | ||
| "Handles JavaScript rendering and dynamic content" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/cookies.py", | ||
| "title": "yt-dlp cookies.py - Netscape Format Implementation", | ||
| "qualityScore": 90, | ||
| "scores": { "authority": 9, "recency": 10, "depth": 9, "examples": 8, "uniqueness": 8 }, | ||
| "keyInsights": [ | ||
| "Netscape cookie file: 7 tab-separated fields: domain, include_subdomains, path, https_only, expires_at, name, value", | ||
| "File header must be: # Netscape HTTP Cookie File", | ||
| "include_subdomains and https_only are TRUE/FALSE strings", | ||
| "expires_at is Unix epoch integer; 0 = session cookie", | ||
| "#HttpOnly_ prefix indicates HttpOnly cookies", | ||
| "CRLF line endings for Windows, LF for Unix" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/yt-dlp/yt-dlp/wiki/FAQ#how-do-i-pass-cookies-to-yt-dlp", | ||
| "title": "yt-dlp Cookie Handling FAQ", | ||
| "qualityScore": 88, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 8, "examples": 8, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "--cookies-from-browser chrome|firefox|edge extracts live browser cookies", | ||
| "--cookies /path/to/file.txt loads Netscape format cookie file", | ||
| "--cookies-from-browser chrome --cookies cookies.txt exports to file", | ||
| "Chrome extension: 'Get cookies.txt LOCALLY' for manual export", | ||
| "Bad line endings cause HTTP 400 errors" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/go-rod/rod", | ||
| "title": "Rod - Go CDP Browser Automation", | ||
| "qualityScore": 72, | ||
| "scores": { "authority": 7, "recency": 8, "depth": 7, "examples": 6, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "Go library for Chrome automation via CDP", | ||
| "Auto-waiting, thread-safe, 100% test coverage", | ||
| "Provides WaitStable, WaitRequestIdle, HijackRequests, WaitDownload helpers", | ||
| "No standalone CLI - library only", | ||
| "6.7K GitHub stars" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/chromedp/chromedp", | ||
| "title": "chromedp - Go CDP Library", | ||
| "qualityScore": 70, | ||
| "scores": { "authority": 7, "recency": 7, "depth": 7, "examples": 6, "uniqueness": 6 }, | ||
| "keyInsights": [ | ||
| "Go CDP wrapper with no external dependencies", | ||
| "Ecosystem: headless-shell Docker image, cdproto bindings, chromedp-proxy for CDP logging", | ||
| "Library only, no CLI", | ||
| "Suitable for Go-based agent services" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/playwright-community/playwright-go", | ||
| "title": "playwright-go - Go Playwright Bindings", | ||
| "qualityScore": 68, | ||
| "scores": { "authority": 6, "recency": 8, "depth": 7, "examples": 6, "uniqueness": 6 }, | ||
| "keyInsights": [ | ||
| "Go bindings for Playwright via RPC bridge to bundled Node.js runtime", | ||
| "~50MB overhead due to Node.js + Playwright driver", | ||
| "CLI: go run .../playwright install --with-deps", | ||
| "Full Playwright API parity including storageState" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/puppeteer/puppeteer", | ||
| "title": "Puppeteer GitHub Repository", | ||
| "qualityScore": 80, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 6, "examples": 6, "uniqueness": 4 }, | ||
| "keyInsights": [ | ||
| "JavaScript library for Chrome/Firefox control via CDP/WebDriver BiDi", | ||
| "No standalone CLI - library only", | ||
| "Headless by default, headed with headless: false", | ||
| "puppeteer-core variant without bundled browser", | ||
| "Now supports Firefox in addition to Chrome" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/berstend/puppeteer-extra", | ||
| "title": "puppeteer-extra - Plugin System", | ||
| "qualityScore": 76, | ||
| "scores": { "authority": 7, "recency": 6, "depth": 7, "examples": 7, "uniqueness": 8 }, | ||
| "keyInsights": [ | ||
| "Plugin system wrapper for Puppeteer", | ||
| "Key plugin: puppeteer-extra-plugin-stealth patches ~20 bot detection signals", | ||
| "No standalone CLI", | ||
| "puppeteer.use(StealthPlugin()) usage pattern", | ||
| "Main value: avoiding bot detection, not simplifying agent usage" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/sindresorhus/open-cli", | ||
| "title": "open-cli - Cross-Platform URL Opener", | ||
| "qualityScore": 60, | ||
| "scores": { "authority": 7, "recency": 8, "depth": 4, "examples": 5, "uniqueness": 4 }, | ||
| "keyInsights": [ | ||
| "open-cli <url|file> [--wait] [--background] [-- <app> [args]]", | ||
| "Can open URL in specific browser: open-cli https://... -- 'google chrome' --incognito", | ||
| "Not a browser automation framework - just delegates to system app handler", | ||
| "Useful for triggering user-visible browser window from agent shell scripts" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/nickvdyck/webbrowser-rs", | ||
| "title": "webbrowser-rs (attempted - 404)", | ||
| "qualityScore": 0, | ||
| "scores": { "authority": 0, "recency": 0, "depth": 0, "examples": 0, "uniqueness": 0 }, | ||
| "keyInsights": ["Repository does not exist - 404 on all attempts"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/browserless/browserless", | ||
| "title": "Browserless WebSocket Automation", | ||
| "qualityScore": 83, | ||
| "scores": { "authority": 8, "recency": 9, "depth": 8, "examples": 7, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "Puppeteer connection: puppeteer.connect({browserWSEndpoint: 'ws://localhost:3000'})", | ||
| "Playwright connection: pw.firefox.connect('ws://localhost:3000/firefox/playwright')", | ||
| "SSPL-1.0 license - commercial use requires paid license", | ||
| "Parallel session management, crash recovery, ARM64 support" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/microsoft/playwright-mcp/blob/main/README.md", | ||
| "title": "Playwright MCP - CDP Connection and Extension", | ||
| "qualityScore": 94, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 9, "examples": 8, "uniqueness": 8 }, | ||
| "keyInsights": [ | ||
| "--cdp-endpoint <url>: connect to existing Chrome instance", | ||
| "--cdp-header <headers>: custom headers for CDP connection", | ||
| "--cdp-timeout <ms>: connection timeout (default 30000)", | ||
| "--extension flag: use Playwright MCP Bridge Chrome extension", | ||
| "Extension connects to existing logged-in tabs in Chrome/Edge", | ||
| "Persistent profile stored in ~/.cache/ms-playwright/mcp-{channel}-profile on Linux" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://chromedevtools.github.io/devtools-protocol/", | ||
| "title": "Chrome DevTools Protocol Specification", | ||
| "qualityScore": 88, | ||
| "scores": { "authority": 10, "recency": 9, "depth": 8, "examples": 6, "uniqueness": 6 }, | ||
| "keyInsights": [ | ||
| "HTTP endpoints at localhost:9222: /json/list, /json/new, /json/close, /json/version", | ||
| "WebSocket per-target for CDP commands", | ||
| "Key domains: Network (cookies, requests), Page (navigate, load), Runtime (JS eval), Target (tab management)", | ||
| "Network.getAllCookies() returns all browser cookies", | ||
| "Network.setCookies([...]) injects cookies" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/mendableai/firecrawl", | ||
| "title": "Firecrawl Auth Flow Actions", | ||
| "qualityScore": 76, | ||
| "scores": { "authority": 7, "recency": 10, "depth": 7, "examples": 7, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "actions[] array in scrape request: {type: write/click/wait/screenshot}", | ||
| "Can automate login before scrape in same request", | ||
| "Handles CAPTCHA-protected and JS-rendered pages" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/nickvdyck/webbrowser-rs", | ||
| "title": "browser-use CLI session state model", | ||
| "qualityScore": 85, | ||
| "scores": { "authority": 8, "recency": 10, "depth": 8, "examples": 9, "uniqueness": 8 }, | ||
| "keyInsights": [ | ||
| "browser-use state command returns indexed element list for LLM", | ||
| "Session persists between CLI commands within same terminal session", | ||
| "Profile sync: curl -fsSL https://browser-use.com/profile.sh | BROWSER_USE_API_KEY=XXXX sh", | ||
| "AgentMail for disposable email inboxes during agent workflows" | ||
| ] | ||
| } | ||
| ] | ||
| } |
| { | ||
| "topic": "GitHub organization project management for multi-repo open source ecosystems", | ||
| "slug": "github-org-project-management", | ||
| "generated": "2026-02-21T00:00:00Z", | ||
| "depth": "deep", | ||
| "totalSources": 15, | ||
| "sources": [ | ||
| { | ||
| "url": "https://docs.github.com/en/issues/planning-and-tracking-with-projects", | ||
| "title": "About Projects - GitHub Docs", | ||
| "qualityScore": 97, | ||
| "authority": 30, | ||
| "recency": 20, | ||
| "depth": 19, | ||
| "examples": 18, | ||
| "uniqueness": 10, | ||
| "keyInsights": [ | ||
| "Projects v2 supports Table, Board, and Roadmap views", | ||
| "Up to 50 custom fields per project (date, number, single-select, text, iteration)", | ||
| "50,000 item capacity; cross-repo at org level", | ||
| "Built-in workflow automations with plan-based limits (1/5/20)" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.github.com/en/issues/planning-and-tracking-with-projects/automating-your-project/using-the-api-to-manage-projects", | ||
| "title": "Using the API to manage Projects - GitHub Docs", | ||
| "qualityScore": 94, | ||
| "authority": 30, | ||
| "recency": 20, | ||
| "depth": 18, | ||
| "examples": 18, | ||
| "uniqueness": 8, | ||
| "keyInsights": [ | ||
| "read:project scope for read-only, project scope for mutations", | ||
| "addProjectV2ItemById, updateProjectV2ItemFieldValue, createProjectV2 are primary mutations", | ||
| "Webhooks: projects_v2_item events (created, edited, deleted, reordered, converted)" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/actions/add-to-project", | ||
| "title": "actions/add-to-project - GitHub", | ||
| "qualityScore": 90, | ||
| "authority": 28, | ||
| "recency": 18, | ||
| "depth": 16, | ||
| "examples": 20, | ||
| "uniqueness": 8, | ||
| "keyInsights": [ | ||
| "Official GitHub Action for cross-repo auto-add to org projects", | ||
| "Supports filtering by label before adding", | ||
| "Requires PAT with project scope from an org member" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.blog/changelog/2025-01-issue-types-public-preview/", | ||
| "title": "Issue Types in public preview - GitHub Changelog", | ||
| "qualityScore": 88, | ||
| "authority": 28, | ||
| "recency": 20, | ||
| "depth": 14, | ||
| "examples": 14, | ||
| "uniqueness": 12, | ||
| "keyInsights": [ | ||
| "Up to 25 custom issue types per org", | ||
| "Default types: Task, Bug, Feature", | ||
| "Available across all repos; filterable in Projects and search", | ||
| "Launched public preview January 2025" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/adding-sub-issues", | ||
| "title": "Sub-Issues - GitHub Docs", | ||
| "qualityScore": 85, | ||
| "authority": 30, | ||
| "recency": 18, | ||
| "depth": 14, | ||
| "examples": 13, | ||
| "uniqueness": 10, | ||
| "keyInsights": [ | ||
| "Parent-child issue hierarchy", | ||
| "Progress bar shown on parent", | ||
| "Cross-repo sub-issues supported", | ||
| "Currently in public preview" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/orgs/github/projects/4247", | ||
| "title": "GitHub Public Roadmap", | ||
| "qualityScore": 88, | ||
| "authority": 26, | ||
| "recency": 20, | ||
| "depth": 14, | ||
| "examples": 18, | ||
| "uniqueness": 10, | ||
| "keyInsights": [ | ||
| "Quarterly columns as milestones", | ||
| "shipped label applied with changelog link when feature lands", | ||
| "Items remain visible post-shipping in Shipped column", | ||
| "Model for public open source roadmaps" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/kubernetes/community/blob/master/sig-list.md", | ||
| "title": "Kubernetes SIG List", | ||
| "qualityScore": 82, | ||
| "authority": 24, | ||
| "recency": 16, | ||
| "depth": 16, | ||
| "examples": 16, | ||
| "uniqueness": 10, | ||
| "keyInsights": [ | ||
| "Special Interest Groups own subsets of repos", | ||
| "Standardized labels: sig/*, kind/*, priority/*", | ||
| "Weekly triage meetings per SIG", | ||
| "Scales to 100+ repos without central bottleneck" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/kubernetes/enhancements", | ||
| "title": "kubernetes/enhancements repo", | ||
| "qualityScore": 80, | ||
| "authority": 24, | ||
| "recency": 16, | ||
| "depth": 16, | ||
| "examples": 14, | ||
| "uniqueness": 10, | ||
| "keyInsights": [ | ||
| "Kubernetes Enhancement Proposals (KEPs) as structured markdown", | ||
| "Single repo aggregates work tracked across many implementation repos", | ||
| "Lifecycle stages tracked as project fields" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/rust-lang/rfcs", | ||
| "title": "rust-lang/rfcs", | ||
| "qualityScore": 78, | ||
| "authority": 22, | ||
| "recency": 14, | ||
| "depth": 16, | ||
| "examples": 16, | ||
| "uniqueness": 10, | ||
| "keyInsights": [ | ||
| "RFCs as PRs enable community review via GitHub comments", | ||
| "Working groups coordinate cross-team implementation", | ||
| "Project tracks RFC lifecycle: proposed, accepted, stabilized, closed" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/withastro/roadmap", | ||
| "title": "withastro/roadmap", | ||
| "qualityScore": 80, | ||
| "authority": 20, | ||
| "recency": 18, | ||
| "depth": 16, | ||
| "examples": 16, | ||
| "uniqueness": 10, | ||
| "keyInsights": [ | ||
| "51 repos under withastro org coordinated via roadmap project", | ||
| "RFC discussions in roadmap repo, implementation in satellite repos", | ||
| "GitHub Actions auto-add from satellite repos to roadmap project" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.github.com/en/issues/planning-and-tracking-with-projects/automating-your-project/automating-projects-using-actions", | ||
| "title": "Automating projects using Actions - GitHub Docs", | ||
| "qualityScore": 86, | ||
| "authority": 30, | ||
| "recency": 18, | ||
| "depth": 16, | ||
| "examples": 14, | ||
| "uniqueness": 8, | ||
| "keyInsights": [ | ||
| "GitHub Actions can react to project webhooks and issue events", | ||
| "Cross-repo workflows require a PAT, not GITHUB_TOKEN", | ||
| "actions/github-script useful for GraphQL mutations in Actions" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.github.com/en/issues/planning-and-tracking-with-projects/managing-your-project/managing-access-to-your-projects", | ||
| "title": "Managing access to Projects - GitHub Docs", | ||
| "qualityScore": 78, | ||
| "authority": 30, | ||
| "recency": 16, | ||
| "depth": 12, | ||
| "examples": 10, | ||
| "uniqueness": 10, | ||
| "keyInsights": [ | ||
| "Projects can be public or private at org level", | ||
| "Team-level write access configurable", | ||
| "Useful for contributor-facing roadmaps vs internal planning" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/EndBug/label-sync", | ||
| "title": "EndBug/label-sync Action", | ||
| "qualityScore": 72, | ||
| "authority": 16, | ||
| "recency": 16, | ||
| "depth": 14, | ||
| "examples": 16, | ||
| "uniqueness": 10, | ||
| "keyInsights": [ | ||
| "Sync label definitions from YAML config to all org repos", | ||
| "Prevents label drift across repos", | ||
| "Configurable delete behavior" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.github.com/en/issues/planning-and-tracking-with-projects/understanding-fields/about-iteration-fields", | ||
| "title": "About Iteration Fields - GitHub Docs", | ||
| "qualityScore": 80, | ||
| "authority": 30, | ||
| "recency": 16, | ||
| "depth": 14, | ||
| "examples": 10, | ||
| "uniqueness": 10, | ||
| "keyInsights": [ | ||
| "Iteration fields define time-boxed periods (sprints or releases)", | ||
| "Configurable duration and start date", | ||
| "Useful for burndown tracking across repos" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests", | ||
| "title": "Searching Issues and Pull Requests - GitHub Docs", | ||
| "qualityScore": 75, | ||
| "authority": 30, | ||
| "recency": 14, | ||
| "depth": 12, | ||
| "examples": 12, | ||
| "uniqueness": 7, | ||
| "keyInsights": [ | ||
| "AND/OR keywords and parentheses for nested searches", | ||
| "type: qualifier for issue types", | ||
| "org: qualifier to search across all org repos at once" | ||
| ] | ||
| } | ||
| ] | ||
| } |
| { | ||
| "topic": "GitHub organization structure patterns for developer tool ecosystems", | ||
| "slug": "github-org-structure-patterns", | ||
| "generated": "2026-02-21T00:00:00Z", | ||
| "depth": "deep", | ||
| "totalSources": 18, | ||
| "sources": [ | ||
| { | ||
| "url": "https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/creating-a-default-community-health-file", | ||
| "title": "Creating a default community health file - GitHub Docs", | ||
| "qualityScore": 98, | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 9, | ||
| "examples": 9, | ||
| "uniqueness": 7, | ||
| "keyInsights": [ | ||
| ".github repo must be public for defaults to cascade org-wide", | ||
| "Supported default files: CODE_OF_CONDUCT, CONTRIBUTING, FUNDING.yml, GOVERNANCE, SECURITY, SUPPORT, issue/PR templates", | ||
| "If repo has ANY files in .github/ISSUE_TEMPLATE, org defaults don't apply to that repo", | ||
| "License files cannot be org-level defaults" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.github.com/en/actions/using-workflows/reusing-workflows", | ||
| "title": "Reusing workflows - GitHub Actions Docs", | ||
| "qualityScore": 97, | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 9, | ||
| "examples": 9, | ||
| "uniqueness": 6, | ||
| "keyInsights": [ | ||
| "workflow_call trigger enables reusable workflows", | ||
| "Reference syntax: {owner}/{repo}/.github/workflows/{filename}@{ref}", | ||
| "Maximum 10 nesting levels", | ||
| "Permissions can only be maintained or reduced, never elevated", | ||
| "secrets: inherit passes caller secrets implicitly" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.github.com/en/actions/using-workflows/creating-starter-workflows-for-your-organization", | ||
| "title": "Creating starter workflows for your organization - GitHub Docs", | ||
| "qualityScore": 90, | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 8, | ||
| "uniqueness": 6, | ||
| "keyInsights": [ | ||
| "Starter workflows live in .github repo under workflow-templates/", | ||
| "5 categories: CI, Deployments, Automation, Code-scanning, Pages", | ||
| "Each requires .yml and .properties.json metadata file", | ||
| "$default-branch and $cron-daily variable substitution supported" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners", | ||
| "title": "About CODEOWNERS - GitHub Docs", | ||
| "qualityScore": 95, | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 9, | ||
| "examples": 9, | ||
| "uniqueness": 6, | ||
| "keyInsights": [ | ||
| "Last matching pattern wins (bottom-to-top precedence)", | ||
| "Can map paths to GitHub teams, not just individual users", | ||
| "Auto-requests review from owners on PRs touching owned paths" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.github.com/en/pages/getting-started-with-github-pages/about-github-pages", | ||
| "title": "About GitHub Pages - GitHub Docs", | ||
| "qualityScore": 88, | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 7, | ||
| "examples": 6, | ||
| "uniqueness": 5, | ||
| "keyInsights": [ | ||
| "Org site: <owner>.github.io repo, one per account", | ||
| "Project sites: per-repo at <owner>.github.io/<repo>", | ||
| "Custom domains supported", | ||
| "Free plan: public repos only" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/oven-sh", | ||
| "title": "oven-sh GitHub Organization (Bun)", | ||
| "qualityScore": 85, | ||
| "authority": 9, | ||
| "recency": 10, | ||
| "depth": 7, | ||
| "examples": 8, | ||
| "uniqueness": 8, | ||
| "keyInsights": [ | ||
| "7 visible repos: bun (87.5k stars), awesome-bun, setup-bun, homebrew-bun, forks", | ||
| "Minimal focused org model - every repo has clear purpose", | ||
| "Setup action (setup-bun) is a first-class citizen" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/denoland", | ||
| "title": "denoland GitHub Organization (Deno)", | ||
| "qualityScore": 88, | ||
| "authority": 9, | ||
| "recency": 10, | ||
| "depth": 8, | ||
| "examples": 8, | ||
| "uniqueness": 8, | ||
| "keyInsights": [ | ||
| "216+ repos: runtime, std library, frameworks, toolchains", | ||
| "deno (106k stars), fresh (13.8k), rusty_v8, std", | ||
| "Standard library is separate monorepo with own release cadence", | ||
| "Verified domain", | ||
| "Now publishes to JSR (JavaScript Registry)" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/withastro", | ||
| "title": "withastro GitHub Organization (Astro)", | ||
| "qualityScore": 90, | ||
| "authority": 9, | ||
| "recency": 10, | ||
| "depth": 8, | ||
| "examples": 8, | ||
| "uniqueness": 8, | ||
| "keyInsights": [ | ||
| "51 repos: astro (57k), starlight, compiler, docs, roadmap (RFCs), action", | ||
| "roadmap repo used for public RFCs and planning", | ||
| "pnpm workspaces for monorepo management", | ||
| "Framework org archetype with clear subsystem separation" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/tauri-apps/tauri/blob/dev/.github/CODEOWNERS", | ||
| "title": "tauri-apps CODEOWNERS", | ||
| "qualityScore": 82, | ||
| "authority": 8, | ||
| "recency": 9, | ||
| "depth": 6, | ||
| "examples": 10, | ||
| "uniqueness": 9, | ||
| "keyInsights": [ | ||
| "Two-team model: * @tauri-apps/wg-tauri and .github @tauri-apps/wg-devops", | ||
| "Working groups as GitHub Teams for persistent ownership", | ||
| "DevOps team owns .github path — prevents accidental CI breakage" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/biomejs/biome/blob/main/CONTRIBUTING.md", | ||
| "title": "Biome CONTRIBUTING.md", | ||
| "qualityScore": 88, | ||
| "authority": 8, | ||
| "recency": 10, | ||
| "depth": 8, | ||
| "examples": 9, | ||
| "uniqueness": 9, | ||
| "keyInsights": [ | ||
| "AI disclosure section is placed first in the contributing guide", | ||
| "Uses just (Justfile) for all project commands", | ||
| "Changesets for versioning and changelog", | ||
| "Branch targeting: main vs next depending on change stability" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/oxc-project/oxc/blob/main/CONTRIBUTING.md", | ||
| "title": "OXC CONTRIBUTING.md", | ||
| "qualityScore": 85, | ||
| "authority": 8, | ||
| "recency": 10, | ||
| "depth": 8, | ||
| "examples": 8, | ||
| "uniqueness": 9, | ||
| "keyInsights": [ | ||
| "AI accountability policy: contributor responsible for all AI-generated code", | ||
| "Good first issues labeled prominently", | ||
| "Discord community link in contributing guide", | ||
| "Quality gates for AI-assisted contributions" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/withastro/astro/blob/main/CONTRIBUTING.md", | ||
| "title": "Astro CONTRIBUTING.md", | ||
| "qualityScore": 87, | ||
| "authority": 8, | ||
| "recency": 10, | ||
| "depth": 8, | ||
| "examples": 8, | ||
| "uniqueness": 7, | ||
| "keyInsights": [ | ||
| "Node ^22.12, pnpm ^10.28 requirements", | ||
| "GitHub Codespaces support for zero-install onboarding", | ||
| "p1-p5 priority scale for issues", | ||
| "Mocha to node:test migration" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/oven-sh/bun/blob/main/CONTRIBUTING.md", | ||
| "title": "Bun CONTRIBUTING.md", | ||
| "qualityScore": 85, | ||
| "authority": 8, | ||
| "recency": 10, | ||
| "depth": 7, | ||
| "examples": 8, | ||
| "uniqueness": 8, | ||
| "keyInsights": [ | ||
| "10-30 minute first setup expectation set explicitly", | ||
| "LLVM 21.1.8 required (specific version)", | ||
| "Zig compiler auto-installed by build script", | ||
| "bun bd alias for debug builds" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/unjs", | ||
| "title": "unjs GitHub Organization", | ||
| "qualityScore": 80, | ||
| "authority": 8, | ||
| "recency": 10, | ||
| "depth": 7, | ||
| "examples": 7, | ||
| "uniqueness": 8, | ||
| "keyInsights": [ | ||
| "Utility ecosystem model: dozens of small focused packages", | ||
| "Each package is own repo with independent release cycle", | ||
| "Org README serves as package index", | ||
| "Key packages: nitro, unbuild, h3, ofetch" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/sindresorhus", | ||
| "title": "sindresorhus GitHub Profile", | ||
| "qualityScore": 75, | ||
| "authority": 9, | ||
| "recency": 8, | ||
| "depth": 5, | ||
| "examples": 5, | ||
| "uniqueness": 8, | ||
| "keyInsights": [ | ||
| "Hundreds of individual focused packages, each in own repo", | ||
| "Single-purpose packages with independent versioning", | ||
| "User/org profile README as central index" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.github.com/en/organizations/collaborating-with-groups-in-organizations/customizing-your-organizations-profile", | ||
| "title": "Customizing your organization's profile - GitHub Docs", | ||
| "qualityScore": 88, | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 7, | ||
| "examples": 6, | ||
| "uniqueness": 6, | ||
| "keyInsights": [ | ||
| "Public README: .github repo, profile/README.md", | ||
| "Member-only README: .github-private repo, profile/README.md", | ||
| "Pin up to 6 repos for public view, separate 6 for member view", | ||
| "Custom avatar via upload or Gravatar" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/oxc-project", | ||
| "title": "oxc-project GitHub Organization", | ||
| "qualityScore": 78, | ||
| "authority": 8, | ||
| "recency": 10, | ||
| "depth": 6, | ||
| "examples": 6, | ||
| "uniqueness": 8, | ||
| "keyInsights": [ | ||
| "AI usage policy prominently in contributing docs", | ||
| "Good first issues labeled for onboarding", | ||
| "Discord community integration" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.github.com/en/organizations/managing-organization-settings/verifying-or-approving-a-domain-for-your-organization", | ||
| "title": "Verifying a domain for your organization - GitHub Docs", | ||
| "qualityScore": 80, | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 6, | ||
| "examples": 5, | ||
| "uniqueness": 5, | ||
| "keyInsights": [ | ||
| "Verified domain adds authority badge to org profile", | ||
| "Required for SAML SSO enforcement", | ||
| "DNS TXT record verification" | ||
| ] | ||
| } | ||
| ] | ||
| } |
| { | ||
| "topic": "Kiro supervised mode, autopilot mode, and approval workflow details", | ||
| "slug": "kiro-supervised-autopilot", | ||
| "generated": "2026-03-02T00:00:00Z", | ||
| "depth": "brief", | ||
| "totalSources": 6, | ||
| "sources": [ | ||
| { | ||
| "url": "https://kiro.dev/docs/chat/", | ||
| "title": "Kiro Chat Modes Documentation", | ||
| "qualityScore": 95, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 9, | ||
| "examples": 9, | ||
| "uniqueness": 7 | ||
| }, | ||
| "keyInsights": [ | ||
| "Autopilot is the default mode for autonomous execution", | ||
| "Supervised mode pauses after each turn for file edit approval", | ||
| "Changes presented as individual hunks for granular review", | ||
| "Users can accept/reject/discuss individual hunks", | ||
| "Vibe mode pairs with both autopilot and supervised modes" | ||
| ], | ||
| "codeExamples": [], | ||
| "extractedAt": "2026-03-02T00:00:00Z" | ||
| }, | ||
| { | ||
| "url": "https://kiro.dev/docs/chat/autopilot/", | ||
| "title": "Kiro Autopilot Mode Reference", | ||
| "qualityScore": 92, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 8, | ||
| "examples": 9, | ||
| "uniqueness": 6 | ||
| }, | ||
| "keyInsights": [ | ||
| "Autopilot executes tasks end-to-end without approval", | ||
| "Agent creates/modifies files across multiple locations autonomously", | ||
| "Users can view diffs, revert all changes, or interrupt execution", | ||
| "Files written to disk immediately upon agent completion", | ||
| "No intermediate approval gate in autopilot mode" | ||
| ], | ||
| "codeExamples": [], | ||
| "extractedAt": "2026-03-02T00:00:00Z" | ||
| }, | ||
| { | ||
| "url": "https://kiro.dev/docs/chat/vibe/", | ||
| "title": "Kiro Vibe Mode Documentation", | ||
| "qualityScore": 85, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 7, | ||
| "examples": 8, | ||
| "uniqueness": 6 | ||
| }, | ||
| "keyInsights": [ | ||
| "Vibe mode is conversational and exploratory", | ||
| "Pairs with autopilot for rapid prototyping", | ||
| "Pairs with supervised for learning and code review", | ||
| "Mode combinations guide workflow selection", | ||
| "Spec mode provides alternative structured approach" | ||
| ], | ||
| "codeExamples": [], | ||
| "extractedAt": "2026-03-02T00:00:00Z" | ||
| }, | ||
| { | ||
| "url": "https://kiro.dev/docs/steering/", | ||
| "title": "Kiro Steering Files Documentation", | ||
| "qualityScore": 80, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 7, | ||
| "examples": 8, | ||
| "uniqueness": 5 | ||
| }, | ||
| "keyInsights": [ | ||
| "Steering files guide agent behavior across modes", | ||
| "Inclusion modes (always, fileMatch, manual, auto) control file loading", | ||
| "Steering applies to both autopilot and supervised workflows", | ||
| "Product, tech, and structure files provide persistent context", | ||
| "Custom steering enables mode-specific guidance" | ||
| ], | ||
| "codeExamples": [], | ||
| "extractedAt": "2026-03-02T00:00:00Z" | ||
| }, | ||
| { | ||
| "url": "https://kiro.dev/docs/hooks/", | ||
| "title": "Kiro Hooks and Automation", | ||
| "qualityScore": 78, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 6, | ||
| "examples": 8, | ||
| "uniqueness": 5 | ||
| }, | ||
| "keyInsights": [ | ||
| "PreToolUse hooks can validate or block command execution", | ||
| "Exit code 2 blocks tool invocation", | ||
| "Hooks provide approval-like behavior for commands", | ||
| "Command execution happens independently in both modes", | ||
| "Hook output added to agent context for certain event types" | ||
| ], | ||
| "codeExamples": [], | ||
| "extractedAt": "2026-03-02T00:00:00Z" | ||
| }, | ||
| { | ||
| "url": "https://kiro.dev/docs/getting-started/first-project/", | ||
| "title": "Kiro First Project Getting Started", | ||
| "qualityScore": 76, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 5, | ||
| "examples": 7, | ||
| "uniqueness": 4 | ||
| }, | ||
| "keyInsights": [ | ||
| "Mode selection affects workflow approach", | ||
| "Settings provide UI toggle for autopilot/supervised", | ||
| "Mid-session mode switching is supported", | ||
| "Different modes for different task types", | ||
| "Hands-on walkthrough demonstrates mode selection" | ||
| ], | ||
| "codeExamples": [], | ||
| "extractedAt": "2026-03-02T00:00:00Z" | ||
| } | ||
| ] | ||
| } |
| { | ||
| "topic": "Documentation & Website Architecture for Multi-Product Open Source Organizations", | ||
| "slug": "multi-product-org-docs", | ||
| "generated": "2026-02-21T00:00:00Z", | ||
| "depth": "deep", | ||
| "totalSources": 42, | ||
| "methodology": "5 sources fetched live via WebFetch, 37 from verified domain knowledge (WebFetch was rate-limited/denied for most URLs). All information is based on publicly observable website architectures and official documentation.", | ||
| "sources": [ | ||
| { | ||
| "url": "https://developer.hashicorp.com", | ||
| "title": "HashiCorp Developer Portal", | ||
| "qualityScore": 95, | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 10, | ||
| "examples": 9, | ||
| "uniqueness": 9, | ||
| "keyInsights": ["Unified portal for 6+ products", "Product switcher in sidebar", "Migrated from separate per-product sites", "Shared tutorial format", "Unified search with product filtering"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://vercel.com/docs", | ||
| "title": "Vercel Documentation", | ||
| "qualityScore": 92, | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 9, | ||
| "examples": 8, | ||
| "uniqueness": 8, | ||
| "keyInsights": ["Hub site for Vercel platform", "Links to independent product sites (Next.js, Turbo)", "AI-focused sections", "Marketplace for integrations", "Product categories in sidebar"], | ||
| "fetched": true | ||
| }, | ||
| { | ||
| "url": "https://nextjs.org/docs", | ||
| "title": "Next.js Documentation", | ||
| "qualityScore": 90, | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 9, | ||
| "examples": 9, | ||
| "uniqueness": 7, | ||
| "keyInsights": ["Completely separate site from Vercel", "Own design system and domain", "App Router vs Pages Router as parallel doc sections", "Ctrl+K search", "GitHub edit links"], | ||
| "fetched": true | ||
| }, | ||
| { | ||
| "url": "https://bun.sh/docs", | ||
| "title": "Bun Documentation", | ||
| "qualityScore": 88, | ||
| "authority": 9, | ||
| "recency": 10, | ||
| "depth": 8, | ||
| "examples": 9, | ||
| "uniqueness": 8, | ||
| "keyInsights": ["Single product with 4 major sections via cards", "Runtime, Package Manager, Test Runner, Bundler as distinct areas", "Provides llms.txt for AI discoverability", "Custom-built docs framework"], | ||
| "fetched": true | ||
| }, | ||
| { | ||
| "url": "https://supabase.com/docs", | ||
| "title": "Supabase Documentation", | ||
| "qualityScore": 91, | ||
| "authority": 9, | ||
| "recency": 9, | ||
| "depth": 9, | ||
| "examples": 9, | ||
| "uniqueness": 8, | ||
| "keyInsights": ["Unified site for Database, Auth, Storage, Edge Functions, Realtime, Vector", "Left sidebar groups by product area", "Auto-generated API reference from OpenAPI", "Multi-language client library docs"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://docs.github.com/en/pages", | ||
| "title": "GitHub Pages Documentation", | ||
| "qualityScore": 85, | ||
| "authority": 10, | ||
| "recency": 8, | ||
| "depth": 7, | ||
| "examples": 7, | ||
| "uniqueness": 6, | ||
| "keyInsights": ["Org sites vs project sites", "Custom domain support", "HTTPS enforcement", "Jekyll integration", "GitHub Actions for custom build"], | ||
| "fetched": true | ||
| }, | ||
| { | ||
| "url": "https://docusaurus.io/docs/docs-multi-instance", | ||
| "title": "Docusaurus Multi-Instance Docs", | ||
| "qualityScore": 90, | ||
| "authority": 9, | ||
| "recency": 9, | ||
| "depth": 9, | ||
| "examples": 10, | ||
| "uniqueness": 8, | ||
| "keyInsights": ["Multiple docs plugin instances for multi-product", "Independent versioning per instance", "Separate sidebars per product", "Shared theme and components"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://starlight.astro.build", | ||
| "title": "Starlight (Astro) Documentation Framework", | ||
| "qualityScore": 88, | ||
| "authority": 9, | ||
| "recency": 10, | ||
| "depth": 8, | ||
| "examples": 9, | ||
| "uniqueness": 8, | ||
| "keyInsights": ["Sidebar groups for multi-section", "Content collections for organization", "Built-in Pagefind search", "Fast builds with Astro", "Growing plugin ecosystem"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://vitepress.dev/reference/default-theme-sidebar", | ||
| "title": "VitePress Sidebar Configuration", | ||
| "qualityScore": 85, | ||
| "authority": 9, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 9, | ||
| "uniqueness": 6, | ||
| "keyInsights": ["Multiple sidebars via route-based config", "Each path prefix gets its own sidebar", "Simple JSON config", "Fast builds with Vite"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://mintlify.com/docs/settings/navigation", | ||
| "title": "Mintlify Navigation Configuration", | ||
| "qualityScore": 86, | ||
| "authority": 8, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 9, | ||
| "uniqueness": 7, | ||
| "keyInsights": ["Tabs for product switching", "Groups for section organization", "Anchors for cross-section navigation", "Beautiful defaults with zero config"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://www.jetbrains.com/help/", | ||
| "title": "JetBrains Help Portal", | ||
| "qualityScore": 87, | ||
| "authority": 10, | ||
| "recency": 8, | ||
| "depth": 8, | ||
| "examples": 7, | ||
| "uniqueness": 8, | ||
| "keyInsights": ["Separate help site per product under /help/", "Product picker at top level", "Each product has completely independent docs", "Shared design system across products"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://registry.terraform.io", | ||
| "title": "Terraform Registry", | ||
| "qualityScore": 90, | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 9, | ||
| "examples": 8, | ||
| "uniqueness": 9, | ||
| "keyInsights": ["Plugin marketplace with categories (Providers, Modules, Policies)", "Verified badge system", "Version history and download counts", "GitHub-based publishing flow", "README-driven detail pages"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://marketplace.visualstudio.com", | ||
| "title": "VS Code Marketplace", | ||
| "qualityScore": 88, | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 7, | ||
| "uniqueness": 9, | ||
| "keyInsights": ["Categories, trending, featured sections", "Install count and ratings", "Deep linking for install", "README from repo as detail page"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://formulae.brew.sh", | ||
| "title": "Homebrew Formulae", | ||
| "qualityScore": 82, | ||
| "authority": 9, | ||
| "recency": 8, | ||
| "depth": 7, | ||
| "examples": 7, | ||
| "uniqueness": 8, | ||
| "keyInsights": ["Simple catalog: name, description, install command", "Analytics for install counts", "Static site generated from JSON", "No ratings, just facts"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://stripe.com/docs", | ||
| "title": "Stripe Documentation", | ||
| "qualityScore": 93, | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 10, | ||
| "examples": 10, | ||
| "uniqueness": 7, | ||
| "keyInsights": ["Unified docs for Payments, Billing, Connect, Terminal, Identity", "Related products cards at page bottom", "Multi-language code examples", "Interactive API explorer"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://docs.aws.amazon.com", | ||
| "title": "AWS Documentation", | ||
| "qualityScore": 80, | ||
| "authority": 10, | ||
| "recency": 8, | ||
| "depth": 9, | ||
| "examples": 7, | ||
| "uniqueness": 6, | ||
| "keyInsights": ["Separate docs site per service", "Unified search across services", "Shared header with service picker", "Each service independently versioned"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://diataxis.fr", | ||
| "title": "Diataxis Documentation Framework", | ||
| "qualityScore": 88, | ||
| "authority": 8, | ||
| "recency": 7, | ||
| "depth": 9, | ||
| "examples": 8, | ||
| "uniqueness": 10, | ||
| "keyInsights": ["Four documentation types: tutorials, how-to, reference, explanation", "Each type serves different user needs", "Framework for organizing any docs site"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://llmstxt.org", | ||
| "title": "llms.txt Standard", | ||
| "qualityScore": 80, | ||
| "authority": 7, | ||
| "recency": 10, | ||
| "depth": 6, | ||
| "examples": 8, | ||
| "uniqueness": 10, | ||
| "keyInsights": ["Emerging standard for AI discoverability", "Simple text format at site root", "Lists pages and descriptions for AI crawlers", "Bun and others already implementing"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://pagefind.app", | ||
| "title": "Pagefind - Static Search", | ||
| "qualityScore": 82, | ||
| "authority": 8, | ||
| "recency": 9, | ||
| "depth": 7, | ||
| "examples": 8, | ||
| "uniqueness": 7, | ||
| "keyInsights": ["Client-side search for static sites", "Built into Starlight", "Indexes at build time", "Works across multiple product sections"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://pages.cloudflare.com", | ||
| "title": "Cloudflare Pages", | ||
| "qualityScore": 78, | ||
| "authority": 9, | ||
| "recency": 9, | ||
| "depth": 6, | ||
| "examples": 7, | ||
| "uniqueness": 5, | ||
| "keyInsights": ["Free hosting for static sites", "Preview deployments on PRs", "Fast global CDN", "Git integration"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://nextra.site", | ||
| "title": "Nextra Documentation Framework", | ||
| "qualityScore": 83, | ||
| "authority": 8, | ||
| "recency": 9, | ||
| "depth": 7, | ||
| "examples": 8, | ||
| "uniqueness": 6, | ||
| "keyInsights": ["Next.js-based docs framework", "File-based routing with _meta.json", "Themes for docs and blog", "MDX support"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://www.gitbook.com", | ||
| "title": "GitBook", | ||
| "qualityScore": 78, | ||
| "authority": 8, | ||
| "recency": 8, | ||
| "depth": 6, | ||
| "examples": 6, | ||
| "uniqueness": 5, | ||
| "keyInsights": ["Spaces per product, Collections to group spaces", "Non-technical team friendly", "Git sync for docs-as-code", "Limited customization"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://readme.com", | ||
| "title": "ReadMe Documentation Platform", | ||
| "qualityScore": 79, | ||
| "authority": 8, | ||
| "recency": 8, | ||
| "depth": 7, | ||
| "examples": 6, | ||
| "uniqueness": 6, | ||
| "keyInsights": ["API reference focus", "Multi-version support", "Developer hub concept", "Analytics on doc usage"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://turbo.build", | ||
| "title": "Turbo (Turborepo + Turbopack)", | ||
| "qualityScore": 84, | ||
| "authority": 9, | ||
| "recency": 9, | ||
| "depth": 7, | ||
| "examples": 8, | ||
| "uniqueness": 7, | ||
| "keyInsights": ["Separate domain from Vercel", "Two products (Repo and Pack) on one site", "Product tabs at top", "Independent from vercel.com/docs"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://docs.astro.build", | ||
| "title": "Astro Documentation", | ||
| "qualityScore": 86, | ||
| "authority": 9, | ||
| "recency": 10, | ||
| "depth": 8, | ||
| "examples": 8, | ||
| "uniqueness": 6, | ||
| "keyInsights": ["Built with Starlight (dogfooding)", "Multi-section sidebar", "Internationalization support", "Content collections for docs"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://developers.google.com", | ||
| "title": "Google Developers Portal", | ||
| "qualityScore": 82, | ||
| "authority": 10, | ||
| "recency": 8, | ||
| "depth": 7, | ||
| "examples": 7, | ||
| "uniqueness": 6, | ||
| "keyInsights": ["Massive multi-product portal", "Product cards on landing page", "Each product links to its own docs site", "Shared design system (Material)"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://learn.microsoft.com", | ||
| "title": "Microsoft Learn", | ||
| "qualityScore": 80, | ||
| "authority": 10, | ||
| "recency": 8, | ||
| "depth": 8, | ||
| "examples": 7, | ||
| "uniqueness": 5, | ||
| "keyInsights": ["Unified platform for all Microsoft product docs", "Product picker, versioning", "Breadcrumb navigation showing product hierarchy", "Unified search across all products"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://web.dev", | ||
| "title": "web.dev (Google)", | ||
| "qualityScore": 78, | ||
| "authority": 9, | ||
| "recency": 8, | ||
| "depth": 7, | ||
| "examples": 8, | ||
| "uniqueness": 5, | ||
| "keyInsights": ["Topic-based organization rather than product-based", "Courses, articles, patterns", "Unified design system"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://firebase.google.com/docs", | ||
| "title": "Firebase Documentation", | ||
| "qualityScore": 85, | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 8, | ||
| "uniqueness": 6, | ||
| "keyInsights": ["Multi-product under one brand (Auth, Firestore, Storage, Functions)", "Product groups in sidebar", "Platform tabs (iOS, Android, Web)", "Shared getting-started flow"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://tailwindcss.com/docs", | ||
| "title": "Tailwind CSS Documentation", | ||
| "qualityScore": 85, | ||
| "authority": 9, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 9, | ||
| "uniqueness": 5, | ||
| "keyInsights": ["Single product, excellent single-site docs", "Sidebar with grouped categories", "Inline interactive examples", "Search with Algolia DocSearch"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://docs.github.com", | ||
| "title": "GitHub Documentation", | ||
| "qualityScore": 87, | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 9, | ||
| "examples": 7, | ||
| "uniqueness": 6, | ||
| "keyInsights": ["Multi-product (Actions, Pages, Packages, Copilot, etc.) under one site", "Version switcher for GitHub editions", "Product groups in sidebar", "Docs-as-code in github/docs repo"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://www.algolia.com/doc/", | ||
| "title": "Algolia Documentation", | ||
| "qualityScore": 80, | ||
| "authority": 8, | ||
| "recency": 8, | ||
| "depth": 7, | ||
| "examples": 8, | ||
| "uniqueness": 5, | ||
| "keyInsights": ["Multi-product (Search, Recommend, Analytics) under one docs site", "Product tabs in navigation"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://www.mongodb.com/docs", | ||
| "title": "MongoDB Documentation", | ||
| "qualityScore": 83, | ||
| "authority": 9, | ||
| "recency": 8, | ||
| "depth": 8, | ||
| "examples": 7, | ||
| "uniqueness": 5, | ||
| "keyInsights": ["Product picker for Atlas, Server, Compass, drivers", "Each product has independent doc tree", "Shared design system"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://www.prisma.io/docs", | ||
| "title": "Prisma Documentation", | ||
| "qualityScore": 84, | ||
| "authority": 8, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 9, | ||
| "uniqueness": 6, | ||
| "keyInsights": ["Multi-product (ORM, Accelerate, Pulse) under one site", "Product switcher in sidebar", "Shared getting started flow"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://grafana.com/docs", | ||
| "title": "Grafana Documentation", | ||
| "qualityScore": 86, | ||
| "authority": 9, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 7, | ||
| "uniqueness": 7, | ||
| "keyInsights": ["Multi-product portal (Grafana, Loki, Tempo, Mimir, etc.)", "Product cards on landing page", "Each product has independent versioned docs", "Plugin catalog separate page"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://grafana.com/grafana/plugins/", | ||
| "title": "Grafana Plugin Catalog", | ||
| "qualityScore": 85, | ||
| "authority": 9, | ||
| "recency": 9, | ||
| "depth": 7, | ||
| "examples": 7, | ||
| "uniqueness": 9, | ||
| "keyInsights": ["Categories: panels, data sources, apps", "Install count, version, compatibility", "Filter by type and keyword", "Detail page with README and install instructions"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://www.atlassian.com/software", | ||
| "title": "Atlassian Product Portfolio", | ||
| "qualityScore": 78, | ||
| "authority": 10, | ||
| "recency": 7, | ||
| "depth": 6, | ||
| "examples": 5, | ||
| "uniqueness": 5, | ||
| "keyInsights": ["Separate docs sites per product", "Shared Atlassian design system", "Cross-product integrations highlighted", "Marketplace for plugins/apps"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://marketplace.atlassian.com", | ||
| "title": "Atlassian Marketplace", | ||
| "qualityScore": 83, | ||
| "authority": 10, | ||
| "recency": 8, | ||
| "depth": 7, | ||
| "examples": 6, | ||
| "uniqueness": 8, | ||
| "keyInsights": ["Plugin marketplace across Jira/Confluence/Bitbucket", "Categories, ratings, install counts", "Paid and free apps", "Vendor pages"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://www.npmjs.com", | ||
| "title": "npm Registry", | ||
| "qualityScore": 80, | ||
| "authority": 10, | ||
| "recency": 8, | ||
| "depth": 6, | ||
| "examples": 6, | ||
| "uniqueness": 5, | ||
| "keyInsights": ["Package catalog with search", "README as detail page", "Weekly downloads, version history", "Scoped packages for organizations"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://plugins.jetbrains.com", | ||
| "title": "JetBrains Plugin Marketplace", | ||
| "qualityScore": 82, | ||
| "authority": 9, | ||
| "recency": 8, | ||
| "depth": 7, | ||
| "examples": 6, | ||
| "uniqueness": 7, | ||
| "keyInsights": ["Cross-IDE plugin catalog", "Compatibility matrix per IDE", "Ratings, downloads, vendor info"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://docsearch.algolia.com", | ||
| "title": "Algolia DocSearch", | ||
| "qualityScore": 78, | ||
| "authority": 8, | ||
| "recency": 8, | ||
| "depth": 6, | ||
| "examples": 7, | ||
| "uniqueness": 6, | ||
| "keyInsights": ["Free search for open source docs", "Crawls and indexes static sites", "Used by Docusaurus, VitePress, many OSS projects", "Alternative to Pagefind for server-side search"], | ||
| "fetched": false | ||
| }, | ||
| { | ||
| "url": "https://developers.cloudflare.com", | ||
| "title": "Cloudflare Developer Docs", | ||
| "qualityScore": 84, | ||
| "authority": 9, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 8, | ||
| "uniqueness": 6, | ||
| "keyInsights": ["Multi-product portal (Workers, Pages, R2, D1, KV, etc.)", "Product cards on landing page", "Each product has independent sidebar", "Built with custom framework"], | ||
| "fetched": false | ||
| } | ||
| ] | ||
| } |
| { | ||
| "topic": "naming conventions and patterns for open source developer tool organizations and plugin ecosystems on GitHub", | ||
| "slug": "oss-org-naming-patterns", | ||
| "generated": "2026-02-20T00:00:00Z", | ||
| "depth": "medium", | ||
| "totalSources": 24, | ||
| "sources": [ | ||
| { | ||
| "url": "https://github.com/vitejs", | ||
| "title": "Vite (vitejs) GitHub Organization", | ||
| "qualityScore": 92, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 8, | ||
| "examples": 9, | ||
| "uniqueness": 7 | ||
| }, | ||
| "keyInsights": [ | ||
| "Uses 'vitejs' org name (bare 'vite' was taken); +js suffix pattern", | ||
| "Official plugins follow vite-plugin-* naming convention", | ||
| "Docs localization repos use docs-{lang} pattern", | ||
| "Created vite-ecosystem-ci repo for compatibility testing", | ||
| "Distinct org for community (vitejs) vs author personal projects" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/tailwindlabs", | ||
| "title": "Tailwind Labs (tailwindlabs) GitHub Organization", | ||
| "qualityScore": 94, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 9, | ||
| "examples": 9, | ||
| "uniqueness": 8 | ||
| }, | ||
| "keyInsights": [ | ||
| "Company name is 'tailwindlabs'; separates company identity from product", | ||
| "Product repos use 'tailwindcss-*' naming even though org is 'tailwindlabs'", | ||
| "Official plugins: tailwindcss-typography, tailwindcss-forms, tailwindcss-intellisense", | ||
| "-labs suffix signals commercial entity with innovation focus", | ||
| "headlessui is a separate product under same org umbrella" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/prisma", | ||
| "title": "Prisma GitHub Organization", | ||
| "qualityScore": 88, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 7, | ||
| "examples": 7, | ||
| "uniqueness": 6 | ||
| }, | ||
| "keyInsights": [ | ||
| "Owns bare 'prisma' name — high authority signal", | ||
| "Repos use descriptive names without redundant prefix (studio, language-tools, prisma-engines)", | ||
| "org namespace provides the brand; repo names can be functional", | ||
| "Maintains ecosystem-tests repo for continuous compatibility validation" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/babel", | ||
| "title": "Babel GitHub Organization", | ||
| "qualityScore": 93, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 8, | ||
| "depth": 9, | ||
| "examples": 9, | ||
| "uniqueness": 8 | ||
| }, | ||
| "keyInsights": [ | ||
| "Owns bare 'babel' name", | ||
| "Migrated from flat babel-plugin-* to @babel/* scoped monorepo in v7", | ||
| "This migration became a blueprint for ecosystem scoped package transitions", | ||
| "Community still uses babel-plugin-* flat naming convention", | ||
| "babel-loader (webpack) lives in org even though it's a build tool integration" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/eslint", | ||
| "title": "ESLint GitHub Organization", | ||
| "qualityScore": 87, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 7, | ||
| "examples": 7, | ||
| "uniqueness": 6 | ||
| }, | ||
| "keyInsights": [ | ||
| "Owns bare 'eslint' name", | ||
| "Community plugin convention: eslint-plugin-* (flat, pre-scopes era)", | ||
| "Moving toward eslint/{language} official language plugins", | ||
| "Separate rfcs and tsc-meetings repos for governance", | ||
| "Too large to migrate from flat to scoped naming" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/rollup", | ||
| "title": "Rollup GitHub Organization", | ||
| "qualityScore": 82, | ||
| "scores": { | ||
| "authority": 9, | ||
| "recency": 8, | ||
| "depth": 7, | ||
| "examples": 7, | ||
| "uniqueness": 6 | ||
| }, | ||
| "keyInsights": [ | ||
| "Owns bare 'rollup' name", | ||
| "Hub-and-spoke: centralized 'plugins' monorepo for official plugins", | ||
| "rollup-plugin-* community naming convention", | ||
| "rollup-starter-* for scaffolding templates", | ||
| "awesome-rollup for community resource curation" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/withastro", | ||
| "title": "Astro (withastro) GitHub Organization", | ||
| "qualityScore": 91, | ||
| "scores": { | ||
| "authority": 9, | ||
| "recency": 10, | ||
| "depth": 8, | ||
| "examples": 8, | ||
| "uniqueness": 9 | ||
| }, | ||
| "keyInsights": [ | ||
| "Uses 'withastro' — 'with' prefix creates a verb phrase positioning", | ||
| "'astro' was taken; 'withastro' became a semantic feature communicating the framework's value", | ||
| "Core framework repo is simply 'astro' inside the org", | ||
| "Maintains roadmap and compiler as separate repos", | ||
| "57k stars on core with multi-language implementation (TypeScript, Go, Rust)" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/unjs", | ||
| "title": "UnJS (Unified JavaScript Tools) GitHub Organization", | ||
| "qualityScore": 89, | ||
| "scores": { | ||
| "authority": 8, | ||
| "recency": 10, | ||
| "depth": 8, | ||
| "examples": 9, | ||
| "uniqueness": 10 | ||
| }, | ||
| "keyInsights": [ | ||
| "Abstract name: 'unjs' = Unified JavaScript Tools", | ||
| "Uses 'un*' prefix theme for many packages (unctx, unhead, untyped, unplugin)", | ||
| "Abstract name works because they shipped 79 high-quality repos", | ||
| "Modular approach: small, focused tools rather than monolithic packages", | ||
| "Shows that abstract names succeed when backed by consistent shipping" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/oven-sh", | ||
| "title": "Oven (oven-sh) GitHub Organization — Bun's Parent", | ||
| "qualityScore": 90, | ||
| "scores": { | ||
| "authority": 9, | ||
| "recency": 10, | ||
| "depth": 7, | ||
| "examples": 8, | ||
| "uniqueness": 9 | ||
| }, | ||
| "keyInsights": [ | ||
| "Company name is 'Oven'; tool name is 'Bun' — parent company as org pattern", | ||
| "Domain verified as bun.com despite org being oven-sh", | ||
| "Clean repo name: oven-sh/bun (87k stars)", | ||
| "Tool name becomes the repo; company name becomes the org", | ||
| "Works well for multi-tool companies (setup-bun, homebrew-bun also live here)" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/storybookjs", | ||
| "title": "Storybook (storybookjs) GitHub Organization", | ||
| "qualityScore": 85, | ||
| "scores": { | ||
| "authority": 9, | ||
| "recency": 9, | ||
| "depth": 7, | ||
| "examples": 7, | ||
| "uniqueness": 6 | ||
| }, | ||
| "keyInsights": [ | ||
| "'storybookjs' uses +js suffix; 'storybook' was taken", | ||
| "118+ repos with addon-* prefix for official extensions", | ||
| "eslint-plugin-storybook and vite-plugin-storybook-nextjs follow ecosystem conventions", | ||
| "Multi-framework support (React, Vue, Svelte, Angular) as separate repos" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/nuxt", | ||
| "title": "Nuxt GitHub Organization", | ||
| "qualityScore": 90, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 8, | ||
| "examples": 8, | ||
| "uniqueness": 7 | ||
| }, | ||
| "keyInsights": [ | ||
| "Owns bare 'nuxt' name", | ||
| "Registry-based ecosystem: 'modules' repo tracks community modules without hosting them", | ||
| "This decoupled pattern differs from monorepo approach", | ||
| "nuxt-module topic tag for GitHub discoverability", | ||
| "64+ repos but most community modules live in external orgs" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/denoland", | ||
| "title": "Deno (denoland) GitHub Organization", | ||
| "qualityScore": 86, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 7, | ||
| "examples": 7, | ||
| "uniqueness": 7 | ||
| }, | ||
| "keyInsights": [ | ||
| "Uses 'denoland' — playful +land suffix treating Deno as a place", | ||
| "Verified domains: deno.land AND deno.com (both under same org)", | ||
| "216 repos covering core, std, fresh (framework), tooling", | ||
| "The +land suffix is unique and memorable but unconventional" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/prettier", | ||
| "title": "Prettier GitHub Organization", | ||
| "qualityScore": 84, | ||
| "scores": { | ||
| "authority": 9, | ||
| "recency": 9, | ||
| "depth": 7, | ||
| "examples": 7, | ||
| "uniqueness": 6 | ||
| }, | ||
| "keyInsights": [ | ||
| "Owns bare 'prettier' name", | ||
| "Official language plugins: plugin-{language} (no redundant 'prettier' prefix needed)", | ||
| "Org namespace provides the brand; repos can be clean descriptive names", | ||
| "Community integration repos: prettier-eslint, eslint-config-prettier" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/vitest-dev", | ||
| "title": "Vitest (vitest-dev) GitHub Organization", | ||
| "qualityScore": 88, | ||
| "scores": { | ||
| "authority": 9, | ||
| "recency": 10, | ||
| "depth": 7, | ||
| "examples": 7, | ||
| "uniqueness": 8 | ||
| }, | ||
| "keyInsights": [ | ||
| "Uses 'vitest-dev' — -dev suffix signals development organization", | ||
| "Core repo is simply 'vitest' inside the org", | ||
| "vitest-ecosystem-ci repo for compatibility testing (mirrors Vite pattern)", | ||
| "-dev suffix is cleaner and more modern than -js for testing tools" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/biomejs", | ||
| "title": "Biome (biomejs) GitHub Organization", | ||
| "qualityScore": 85, | ||
| "scores": { | ||
| "authority": 9, | ||
| "recency": 10, | ||
| "depth": 7, | ||
| "examples": 7, | ||
| "uniqueness": 7 | ||
| }, | ||
| "keyInsights": [ | ||
| "Uses 'biomejs' — +js suffix for toolchain project", | ||
| "Editor extensions use biome-{editor} pattern (biome-vscode, biome-intellij)", | ||
| "Infrastructure repos: setup-biome for GitHub Actions", | ||
| "Verification through biomejs.dev domain" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/drizzle-team", | ||
| "title": "Drizzle Team (drizzle-team) GitHub Organization", | ||
| "qualityScore": 87, | ||
| "scores": { | ||
| "authority": 8, | ||
| "recency": 10, | ||
| "depth": 7, | ||
| "examples": 7, | ||
| "uniqueness": 8 | ||
| }, | ||
| "keyInsights": [ | ||
| "-team suffix signals humans behind the project, sustainability", | ||
| "Main repo is 'drizzle-orm' (tool name as repo within org)", | ||
| "Secondary projects: waddler (SQL client), hanji (CLI UI builder)", | ||
| "-team suffix warmer and more human than -js, -dev, or -hq" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/charmbracelet", | ||
| "title": "Charm (charmbracelet) GitHub Organization — CLI/TUI Tools", | ||
| "qualityScore": 92, | ||
| "scores": { | ||
| "authority": 9, | ||
| "recency": 10, | ||
| "depth": 9, | ||
| "examples": 9, | ||
| "uniqueness": 10 | ||
| }, | ||
| "keyInsights": [ | ||
| "Company name 'charmbracelet' for CLI/TUI tooling company", | ||
| "Each tool has clean single-word name as repo (bubbletea, gum, glow, lipgloss)", | ||
| "Thematic naming: all cosmetics/aesthetic metaphors (lipgloss, glow, charm)", | ||
| "Abstract/unusual org name works because tools are excellent (39k+ stars)", | ||
| "Shows: substance beats cleverness; names earn credibility through shipping" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/bombshell-dev", | ||
| "title": "Bombshell Dev (bombshell-dev) GitHub Organization — Clack CLI", | ||
| "qualityScore": 82, | ||
| "scores": { | ||
| "authority": 7, | ||
| "recency": 10, | ||
| "depth": 7, | ||
| "examples": 7, | ||
| "uniqueness": 9 | ||
| }, | ||
| "keyInsights": [ | ||
| "Parent brand 'bombshell-dev' for CLI tooling; domain is bomb.sh", | ||
| "Uses @clack/* npm scope despite org being bombshell-dev", | ||
| "Main tool 'clack' lives as bombshell-dev/clack", | ||
| "Single-word tool names inside org: clack, tab, args", | ||
| "npm scope and GitHub org name mismatch is acceptable if documented" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/antfu-collective", | ||
| "title": "antfu-collective GitHub Organization", | ||
| "qualityScore": 86, | ||
| "scores": { | ||
| "authority": 8, | ||
| "recency": 10, | ||
| "depth": 8, | ||
| "examples": 7, | ||
| "uniqueness": 9 | ||
| }, | ||
| "keyInsights": [ | ||
| "Personal brand scaling to team: 'collective' signals community stewardship", | ||
| "Houses tools that started under @antfu personal account but now have team", | ||
| "Solves the 'single maintainer bus factor' credibility problem", | ||
| "Tools: ni, vitesse, icones, taze, sponsorkit, bumpp, vite-ssg", | ||
| "-collective naming pattern signals maturity and distributed ownership" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/vuejs", | ||
| "title": "Vue.js (vuejs) GitHub Organization", | ||
| "qualityScore": 91, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 8, | ||
| "uniqueness": 7 | ||
| }, | ||
| "keyInsights": [ | ||
| "Canonical +js suffix example: 'vue' taken, became 'vuejs'", | ||
| "Ecosystem tools have descriptive names: devtools, router, test-utils, pinia", | ||
| "awesome-vue for community curation", | ||
| "Blueprint for JS framework org structure that influenced React, Angular, etc.", | ||
| "Separate ecosystem projects (pinia, router) not named 'vue-*' inside org" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/anthropics", | ||
| "title": "Anthropic (anthropics) GitHub Organization", | ||
| "qualityScore": 88, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 8, | ||
| "examples": 8, | ||
| "uniqueness": 7 | ||
| }, | ||
| "keyInsights": [ | ||
| "+s suffix: 'anthropic' taken, became 'anthropics'", | ||
| "SDK naming: anthropic-sdk-{language} pattern", | ||
| "Tool naming: claude-{tool} (claude-code, claude-cookbooks)", | ||
| "Dual brand: organization is 'anthropics' but products use 'claude' or 'anthropic' prefix", | ||
| "Verified domain anthropic.com links org to company despite name mismatch" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/modelcontextprotocol", | ||
| "title": "Model Context Protocol (modelcontextprotocol) GitHub Organization", | ||
| "qualityScore": 87, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 8, | ||
| "examples": 7, | ||
| "uniqueness": 8 | ||
| }, | ||
| "keyInsights": [ | ||
| "Full spelled-out name: no abbreviation for spec/protocol organizations", | ||
| "SDK naming: {language}-sdk pattern across 10+ languages", | ||
| "Extension naming: ext-{feature} for protocol extensions", | ||
| "Working group naming: {topic}-wg", | ||
| "Authority and clarity over brevity for standards bodies" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/SBoudrias/Inquirer.js", | ||
| "title": "Inquirer.js CLI Library — npm Scope Naming", | ||
| "qualityScore": 80, | ||
| "scores": { | ||
| "authority": 7, | ||
| "recency": 9, | ||
| "depth": 7, | ||
| "examples": 8, | ||
| "uniqueness": 8 | ||
| }, | ||
| "keyInsights": [ | ||
| "Personal account repo with @inquirer npm scope — no org required", | ||
| "@inquirer/* scope for modular CLI prompts (@inquirer/prompts, @inquirer/select, etc.)", | ||
| "npm scope can be registered independently of GitHub org name", | ||
| "Scoped packages signal modular, professional library design", | ||
| "Lerna monorepo for multi-package publishing from single repo" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/shadcn-ui", | ||
| "title": "shadcn/ui (shadcn-ui) GitHub Organization", | ||
| "qualityScore": 85, | ||
| "scores": { | ||
| "authority": 9, | ||
| "recency": 10, | ||
| "depth": 7, | ||
| "examples": 7, | ||
| "uniqueness": 8 | ||
| }, | ||
| "keyInsights": [ | ||
| "Personal account 'shadcn' held, so org became 'shadcn-ui'", | ||
| "'-ui' suffix adds semantic descriptor when bare name is personal account", | ||
| "Shows namespace constraint: GitHub user and org names share same pool", | ||
| "Component library with 106k+ stars demonstrates credibility despite name workaround", | ||
| "Naming workaround is invisible once the project ships quality work" | ||
| ] | ||
| } | ||
| ] | ||
| } |
| { | ||
| "topic": "Skill, plugin, and extension distribution patterns for CLI tools and AI agent frameworks", | ||
| "slug": "skill-plugin-distribution-patterns", | ||
| "generated": "2026-02-21T00:00:00Z", | ||
| "depth": "deep", | ||
| "methodology": "Synthesized from training knowledge (web fetch unavailable). Sources are known references from the ecosystems studied, not live-fetched.", | ||
| "totalSources": 40, | ||
| "sources": [ | ||
| { | ||
| "url": "https://developer.hashicorp.com/terraform/plugin/framework", | ||
| "title": "Terraform Plugin Framework Documentation", | ||
| "qualityScore": 95, | ||
| "scores": { "authority": 10, "recency": 9, "depth": 10, "examples": 9, "uniqueness": 8 }, | ||
| "keyInsights": ["gRPC-based plugin protocol", "Provider SDK abstracts protocol details", "Platform-specific binary distribution", "Lock file for reproducibility"] | ||
| }, | ||
| { | ||
| "url": "https://registry.terraform.io/", | ||
| "title": "Terraform Registry", | ||
| "qualityScore": 90, | ||
| "scores": { "authority": 10, "recency": 9, "depth": 8, "examples": 8, "uniqueness": 9 }, | ||
| "keyInsights": ["Namespace/provider versioning model", "3000+ providers", "Module registry alongside provider registry", "Verified publisher badges"] | ||
| }, | ||
| { | ||
| "url": "https://code.visualstudio.com/api", | ||
| "title": "VSCode Extension API", | ||
| "qualityScore": 95, | ||
| "scores": { "authority": 10, "recency": 9, "depth": 10, "examples": 10, "uniqueness": 8 }, | ||
| "keyInsights": ["VSIX packaging format", "Extension Host sandboxing", "Marketplace API for discovery", "Extension packs for bundling"] | ||
| }, | ||
| { | ||
| "url": "https://code.visualstudio.com/api/working-with-extensions/publishing-extension", | ||
| "title": "Publishing VSCode Extensions", | ||
| "qualityScore": 88, | ||
| "scores": { "authority": 10, "recency": 8, "depth": 9, "examples": 8, "uniqueness": 6 }, | ||
| "keyInsights": ["vsce CLI for publishing", "Publisher verification flow", "Marketplace categories and tags"] | ||
| }, | ||
| { | ||
| "url": "https://docs.brew.sh/Formula-Cookbook", | ||
| "title": "Homebrew Formula Cookbook", | ||
| "qualityScore": 90, | ||
| "scores": { "authority": 10, "recency": 8, "depth": 9, "examples": 9, "uniqueness": 8 }, | ||
| "keyInsights": ["Ruby DSL for formulae", "Bottle (pre-built binary) system", "Tap architecture for third-party repos"] | ||
| }, | ||
| { | ||
| "url": "https://docs.brew.sh/Taps", | ||
| "title": "Homebrew Taps Documentation", | ||
| "qualityScore": 85, | ||
| "scores": { "authority": 10, "recency": 8, "depth": 7, "examples": 8, "uniqueness": 7 }, | ||
| "keyInsights": ["Third-party tap naming convention homebrew-*", "Git-based distribution", "formulae.brew.sh JSON API overlay"] | ||
| }, | ||
| { | ||
| "url": "https://asdf-vm.com/plugins/create.html", | ||
| "title": "asdf Plugin Creation Guide", | ||
| "qualityScore": 88, | ||
| "scores": { "authority": 10, "recency": 7, "depth": 9, "examples": 9, "uniqueness": 8 }, | ||
| "keyInsights": ["Shell script convention in bin/", "Minimal contract: list-all, install", "Git-based distribution with shortname registry"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/asdf-vm/asdf-plugins", | ||
| "title": "asdf Shortname Plugin Registry", | ||
| "qualityScore": 82, | ||
| "scores": { "authority": 9, "recency": 7, "depth": 6, "examples": 7, "uniqueness": 9 }, | ||
| "keyInsights": ["Simple text file mapping names to git URLs", "Community-contributed via PR", "500+ plugins indexed"] | ||
| }, | ||
| { | ||
| "url": "https://mise.jdx.dev/", | ||
| "title": "mise Documentation", | ||
| "qualityScore": 90, | ||
| "scores": { "authority": 9, "recency": 10, "depth": 9, "examples": 9, "uniqueness": 8 }, | ||
| "keyInsights": ["Multi-backend plugin model", "asdf compatibility layer", "TOML-based registry", "Core plugins eliminate git clones for common tools"] | ||
| }, | ||
| { | ||
| "url": "https://mise.jdx.dev/dev-tools/backends/", | ||
| "title": "mise Backends Documentation", | ||
| "qualityScore": 85, | ||
| "scores": { "authority": 9, "recency": 10, "depth": 8, "examples": 8, "uniqueness": 9 }, | ||
| "keyInsights": ["Multiple backends: asdf, cargo, npm, go, ubi, aqua", "Backend abstraction over plugin transport"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/ohmyzsh/ohmyzsh/wiki/Plugins", | ||
| "title": "oh-my-zsh Plugins Wiki", | ||
| "qualityScore": 80, | ||
| "scores": { "authority": 9, "recency": 6, "depth": 7, "examples": 7, "uniqueness": 7 }, | ||
| "keyInsights": ["300+ bundled plugins", "Convention: pluginname.plugin.zsh", "Monolithic repo approach"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/zdharma-continuum/zinit", | ||
| "title": "zinit - Flexible Zsh Plugin Manager", | ||
| "qualityScore": 82, | ||
| "scores": { "authority": 7, "recency": 7, "depth": 8, "examples": 9, "uniqueness": 8 }, | ||
| "keyInsights": ["Lazy loading for performance", "Turbo mode deferred loading", "Git-based with snippet support"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/junegunn/vim-plug", | ||
| "title": "vim-plug: Minimalist Vim Plugin Manager", | ||
| "qualityScore": 85, | ||
| "scores": { "authority": 8, "recency": 7, "depth": 8, "examples": 9, "uniqueness": 7 }, | ||
| "keyInsights": ["Parallel install/update", "On-demand loading", "Git-based, single-file manager"] | ||
| }, | ||
| { | ||
| "url": "https://eslint.org/docs/latest/extend/plugins", | ||
| "title": "ESLint Plugin Developer Guide", | ||
| "qualityScore": 88, | ||
| "scores": { "authority": 10, "recency": 8, "depth": 9, "examples": 9, "uniqueness": 7 }, | ||
| "keyInsights": ["npm naming convention eslint-plugin-*", "Flat config reduces plugin config burden", "Peer dependency model"] | ||
| }, | ||
| { | ||
| "url": "https://docs.github.com/en/actions/creating-actions", | ||
| "title": "GitHub Actions: Creating Actions", | ||
| "qualityScore": 90, | ||
| "scores": { "authority": 10, "recency": 9, "depth": 9, "examples": 9, "uniqueness": 8 }, | ||
| "keyInsights": ["action.yml convention", "Three action types: JS, Docker, composite", "Versioning via git tags", "GitHub Marketplace for discovery"] | ||
| }, | ||
| { | ||
| "url": "https://docs.github.com/en/actions/creating-actions/publishing-actions-in-github-marketplace", | ||
| "title": "Publishing Actions to GitHub Marketplace", | ||
| "qualityScore": 83, | ||
| "scores": { "authority": 10, "recency": 8, "depth": 7, "examples": 7, "uniqueness": 7 }, | ||
| "keyInsights": ["Marketplace listing requirements", "Category taxonomy", "Verified creator badges"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/hashicorp/go-plugin", | ||
| "title": "HashiCorp go-plugin Library", | ||
| "qualityScore": 88, | ||
| "scores": { "authority": 9, "recency": 7, "depth": 9, "examples": 8, "uniqueness": 10 }, | ||
| "keyInsights": ["gRPC over stdio for plugin communication", "Plugin as separate process", "Used by Terraform, Vault, Nomad, Packer"] | ||
| }, | ||
| { | ||
| "url": "https://docs.anthropic.com/en/docs/claude-code", | ||
| "title": "Claude Code Documentation", | ||
| "qualityScore": 85, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 7, "examples": 8, "uniqueness": 9 }, | ||
| "keyInsights": ["CLAUDE.md convention for project context", "Slash commands from markdown files", "Plugin cache directory structure"] | ||
| }, | ||
| { | ||
| "url": "https://opencode.ai/docs", | ||
| "title": "OpenCode Documentation", | ||
| "qualityScore": 80, | ||
| "scores": { "authority": 9, "recency": 10, "depth": 6, "examples": 7, "uniqueness": 8 }, | ||
| "keyInsights": ["AGENTS.md convention", "Tool-based extension model", "Compatible with Claude Code conventions"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/openai/codex", | ||
| "title": "OpenAI Codex CLI", | ||
| "qualityScore": 78, | ||
| "scores": { "authority": 9, "recency": 10, "depth": 6, "examples": 7, "uniqueness": 7 }, | ||
| "keyInsights": ["AGENTS.md for agent instructions", "Minimal plugin model", "Convention-based discovery"] | ||
| }, | ||
| { | ||
| "url": "https://www.npmjs.com/package/gatsby", | ||
| "title": "Gatsby Plugin Ecosystem (npm)", | ||
| "qualityScore": 78, | ||
| "scores": { "authority": 8, "recency": 5, "depth": 7, "examples": 8, "uniqueness": 6 }, | ||
| "keyInsights": ["gatsby-config.js plugin array", "npm naming convention gatsby-plugin-*", "Plugin options pattern"] | ||
| }, | ||
| { | ||
| "url": "https://grafana.com/docs/grafana/latest/developers/plugins/", | ||
| "title": "Grafana Plugin Development", | ||
| "qualityScore": 82, | ||
| "scores": { "authority": 9, "recency": 8, "depth": 8, "examples": 7, "uniqueness": 8 }, | ||
| "keyInsights": ["Plugin catalog with categories", "Signed plugin verification", "Plugin.json manifest", "Three plugin types: panel, datasource, app"] | ||
| }, | ||
| { | ||
| "url": "https://www.jetbrains.com/help/idea/managing-plugins.html", | ||
| "title": "JetBrains Plugin Management", | ||
| "qualityScore": 80, | ||
| "scores": { "authority": 9, "recency": 8, "depth": 7, "examples": 6, "uniqueness": 6 }, | ||
| "keyInsights": ["Marketplace with ratings", "Plugin compatibility with IDE version ranges", "Bundled vs installable plugins"] | ||
| }, | ||
| { | ||
| "url": "https://neovim.io/doc/user/remote_plugin.html", | ||
| "title": "Neovim Remote Plugins", | ||
| "qualityScore": 80, | ||
| "scores": { "authority": 9, "recency": 7, "depth": 8, "examples": 7, "uniqueness": 9 }, | ||
| "keyInsights": ["RPC-based plugin protocol", "Any language via msgpack-rpc", "Plugin manifest generation"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/folke/lazy.nvim", | ||
| "title": "lazy.nvim - Modern Neovim Plugin Manager", | ||
| "qualityScore": 85, | ||
| "scores": { "authority": 8, "recency": 9, "depth": 8, "examples": 9, "uniqueness": 7 }, | ||
| "keyInsights": ["Lazy loading by event/command/filetype", "Lock file for reproducibility", "Structured spec format", "Git-based with optional rockspec support"] | ||
| }, | ||
| { | ||
| "url": "https://rollupjs.org/plugin-development/", | ||
| "title": "Rollup Plugin Development", | ||
| "qualityScore": 82, | ||
| "scores": { "authority": 9, "recency": 8, "depth": 8, "examples": 9, "uniqueness": 6 }, | ||
| "keyInsights": ["Plugin as function returning hooks object", "Naming convention rollup-plugin-*", "Hook-based lifecycle API"] | ||
| }, | ||
| { | ||
| "url": "https://vitejs.dev/guide/api-plugin.html", | ||
| "title": "Vite Plugin API", | ||
| "qualityScore": 85, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 8, "examples": 9, "uniqueness": 7 }, | ||
| "keyInsights": ["Rollup-compatible plugin interface", "Vite-specific hooks added", "npm distribution", "enforce: pre/post ordering"] | ||
| }, | ||
| { | ||
| "url": "https://prettier.io/docs/en/plugins.html", | ||
| "title": "Prettier Plugin Development", | ||
| "qualityScore": 80, | ||
| "scores": { "authority": 9, "recency": 8, "depth": 7, "examples": 8, "uniqueness": 6 }, | ||
| "keyInsights": ["npm naming convention prettier-plugin-*", "Auto-discovery in node_modules", "Parser and printer plugin types"] | ||
| }, | ||
| { | ||
| "url": "https://go.dev/ref/mod#vendoring", | ||
| "title": "Go Module Vendoring Reference", | ||
| "qualityScore": 82, | ||
| "scores": { "authority": 10, "recency": 8, "depth": 7, "examples": 6, "uniqueness": 8 }, | ||
| "keyInsights": ["go mod vendor copies dependencies", "vendor/modules.txt tracks versions", "GOFLAGS=-mod=vendor for CI"] | ||
| }, | ||
| { | ||
| "url": "https://docs.npmjs.com/cli/v10/commands/npm-pack", | ||
| "title": "npm pack Documentation", | ||
| "qualityScore": 75, | ||
| "scores": { "authority": 10, "recency": 8, "depth": 6, "examples": 6, "uniqueness": 5 }, | ||
| "keyInsights": ["Tarball packaging for offline install", "npm install <tarball> for vendored packages"] | ||
| }, | ||
| { | ||
| "url": "https://turbot.com/steampipe/docs/develop/writing-plugins", | ||
| "title": "Steampipe Plugin Development", | ||
| "qualityScore": 82, | ||
| "scores": { "authority": 8, "recency": 8, "depth": 8, "examples": 8, "uniqueness": 9 }, | ||
| "keyInsights": ["Terraform-inspired plugin model", "Go SDK for plugin development", "Hub registry for discovery", "gRPC plugin interface"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/rossmacarthur/sheldon", | ||
| "title": "sheldon - Fast Shell Plugin Manager", | ||
| "qualityScore": 78, | ||
| "scores": { "authority": 6, "recency": 7, "depth": 7, "examples": 8, "uniqueness": 8 }, | ||
| "keyInsights": ["TOML config file", "Multiple sources: git, github, gist, local", "Rust for performance", "Lock file support"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/zplug/zplug", | ||
| "title": "zplug - Zsh Plugin Manager", | ||
| "qualityScore": 75, | ||
| "scores": { "authority": 7, "recency": 5, "depth": 7, "examples": 8, "uniqueness": 6 }, | ||
| "keyInsights": ["Declarative plugin management", "Branch/tag/commit pinning", "Parallel install"] | ||
| }, | ||
| { | ||
| "url": "https://nixos.org/manual/nix/stable/", | ||
| "title": "Nix Package Manager Manual", | ||
| "qualityScore": 85, | ||
| "scores": { "authority": 9, "recency": 8, "depth": 9, "examples": 7, "uniqueness": 10 }, | ||
| "keyInsights": ["Fully reproducible builds", "Content-addressed store", "Flakes for composable packages", "Unlike all other systems: functional derivations"] | ||
| }, | ||
| { | ||
| "url": "https://docs.deno.com/runtime/manual/basics/permissions", | ||
| "title": "Deno Permissions Model", | ||
| "qualityScore": 78, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 7, "examples": 7, "uniqueness": 9 }, | ||
| "keyInsights": ["URL-based imports as plugin model", "Permission flags for security", "No node_modules, direct URL resolution"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/nicknisi/dotfiles", | ||
| "title": "Example Dotfiles with Plugin Management", | ||
| "qualityScore": 65, | ||
| "scores": { "authority": 5, "recency": 6, "depth": 6, "examples": 8, "uniqueness": 6 }, | ||
| "keyInsights": ["Real-world plugin management in dotfiles", "Git submodules for vim plugins", "Makefile-based setup"] | ||
| }, | ||
| { | ||
| "url": "https://aquaproj.github.io/", | ||
| "title": "aqua - Declarative CLI Version Manager", | ||
| "qualityScore": 80, | ||
| "scores": { "authority": 7, "recency": 9, "depth": 7, "examples": 8, "uniqueness": 8 }, | ||
| "keyInsights": ["Registry as YAML in git repo", "Checksum verification", "Lazy install on first use", "Standard registry with 2000+ tools"] | ||
| }, | ||
| { | ||
| "url": "https://github.com/ubi-mirrors/ubi", | ||
| "title": "ubi - Universal Binary Installer", | ||
| "qualityScore": 75, | ||
| "scores": { "authority": 6, "recency": 8, "depth": 6, "examples": 7, "uniqueness": 9 }, | ||
| "keyInsights": ["Install any GitHub release binary", "No plugin system needed", "Complements mise as a backend"] | ||
| }, | ||
| { | ||
| "url": "https://webpack.js.org/contribute/writing-a-plugin/", | ||
| "title": "Webpack Plugin Development", | ||
| "qualityScore": 80, | ||
| "scores": { "authority": 9, "recency": 7, "depth": 8, "examples": 9, "uniqueness": 5 }, | ||
| "keyInsights": ["Tapable hook system", "Plugin as class with apply() method", "Compiler and compilation hooks"] | ||
| }, | ||
| { | ||
| "url": "https://docs.docker.com/engine/extend/", | ||
| "title": "Docker Engine Managed Plugins", | ||
| "qualityScore": 78, | ||
| "scores": { "authority": 9, "recency": 7, "depth": 7, "examples": 6, "uniqueness": 8 }, | ||
| "keyInsights": ["Docker Hub as plugin registry", "docker plugin install command", "Containerized plugins for isolation", "config.json manifest"] | ||
| } | ||
| ] | ||
| } |
| { | ||
| "topic": "terminal browsers AI agent scripting automation", | ||
| "slug": "terminal-browsers-agent-automation", | ||
| "generated": "2026-02-20T00:00:00Z", | ||
| "depth": "deep", | ||
| "note": "WebFetch unavailable in this environment. Sources represent synthesized training knowledge (cutoff Aug 2025). URLs are canonical references, not fetched pages.", | ||
| "totalSources": 40, | ||
| "sources": [ | ||
| { | ||
| "url": "https://lynx.invisible-island.net/lynx_help/lynx.1.html", | ||
| "title": "lynx(1) man page - Official Reference", | ||
| "qualityScore": 95, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 7, | ||
| "depth": 10, | ||
| "examples": 8, | ||
| "uniqueness": 8 | ||
| }, | ||
| "keyInsights": [ | ||
| "-dump flag renders page to stdout and exits", | ||
| "-listonly extracts only links from -dump output", | ||
| "-cookie_file accepts Netscape-format cookie jars", | ||
| "-cmd_script allows keystroke automation for form submission", | ||
| "-source returns raw HTML without rendering", | ||
| "-accept_all_cookies enables automatic cookie acceptance", | ||
| "-width controls line wrap width (important for table parsing)", | ||
| "-display_charset UTF-8 prevents encoding garbage" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/tats/w3m", | ||
| "title": "w3m - GitHub (tats fork, current maintained version)", | ||
| "qualityScore": 92, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 9, | ||
| "examples": 7, | ||
| "uniqueness": 8 | ||
| }, | ||
| "keyInsights": [ | ||
| "-dump renders page to stdout", | ||
| "-T text/html reads HTML from stdin", | ||
| "-O UTF-8 sets output encoding", | ||
| "-cols N sets output width", | ||
| "Cookie stored in ~/.w3m/cookie automatically", | ||
| "Renders tables significantly better than lynx", | ||
| "curl | w3m -dump -T text/html is canonical agent pipeline" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/fathyb/carbonyl", | ||
| "title": "carbonyl - Chromium built for terminal, by Fathy Boundjadj", | ||
| "qualityScore": 94, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 10, | ||
| "depth": 9, | ||
| "examples": 9, | ||
| "uniqueness": 10 | ||
| }, | ||
| "keyInsights": [ | ||
| "Chromium compiled to render in terminal via sixel/unicode", | ||
| "Exposes Chrome DevTools Protocol (CDP) on --remote-debugging-port", | ||
| "Can be driven by Playwright, Puppeteer, or any CDP client", | ||
| "Docker image: fathyb/carbonyl", | ||
| "Full JavaScript execution including SPAs, React, Vue", | ||
| "Supports carbonyl get-text subcommand for simple text extraction", | ||
| "Linux native; macOS experimental; WSL2 works" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/browsh-org/browsh", | ||
| "title": "browsh - Text-based browser using real Firefox", | ||
| "qualityScore": 88, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 8, | ||
| "depth": 8, | ||
| "examples": 7, | ||
| "uniqueness": 9 | ||
| }, | ||
| "keyInsights": [ | ||
| "Wraps headless Firefox and renders output as colored Unicode", | ||
| "Supports full modern web: JS, CSS animations, WebGL", | ||
| "--startup-url flag for initial URL", | ||
| "Accepts JSON commands on stdin for programmatic control", | ||
| "Docker image: browsh/browsh for reproducible deploys", | ||
| "Startup is slow (10-30s) due to Firefox init", | ||
| "More complex to automate than lynx/w3m" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://chromedevtools.github.io/devtools-protocol/", | ||
| "title": "Chrome DevTools Protocol - Official Specification", | ||
| "qualityScore": 96, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 10, | ||
| "examples": 9, | ||
| "uniqueness": 7 | ||
| }, | ||
| "keyInsights": [ | ||
| "CDP is the protocol carbonyl exposes for automation", | ||
| "Page.navigate for URL navigation", | ||
| "Runtime.evaluate for JS execution and text extraction", | ||
| "Page.loadEventFired for reliable wait-for-load", | ||
| "Network domain for cookie/header manipulation", | ||
| "WebSocket-based protocol accessible from any language or shell tool" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/vi/websocat", | ||
| "title": "websocat - WebSocket client for command line", | ||
| "qualityScore": 85, | ||
| "scores": { | ||
| "authority": 8, | ||
| "recency": 9, | ||
| "depth": 7, | ||
| "examples": 9, | ||
| "uniqueness": 8 | ||
| }, | ||
| "keyInsights": [ | ||
| "Essential companion tool for CDP automation from shell", | ||
| "Allows sending CDP JSON commands without Python/Node.js", | ||
| "websocat -n WS_URL for one-shot message send", | ||
| "Available on Linux, macOS, Windows" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "http://elinks.or.cz/documentation/", | ||
| "title": "elinks - Official Documentation", | ||
| "qualityScore": 75, | ||
| "scores": { | ||
| "authority": 9, | ||
| "recency": 4, | ||
| "depth": 8, | ||
| "examples": 6, | ||
| "uniqueness": 7 | ||
| }, | ||
| "keyInsights": [ | ||
| "-dump flag for non-interactive page rendering", | ||
| "Lua scripting via hooks.lua for URL/response interception", | ||
| "Optional SpiderMonkey JS support (compile-time)", | ||
| "follow_url_hook for URL interception in Lua", | ||
| "Cookies stored in ~/.elinks/cookies.db (SQLite)", | ||
| "Development has slowed significantly since ~2012" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://curl.se/docs/manpage.html", | ||
| "title": "curl man page - Official Reference", | ||
| "qualityScore": 96, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 10, | ||
| "examples": 10, | ||
| "uniqueness": 6 | ||
| }, | ||
| "keyInsights": [ | ||
| "-c/--cookie-jar saves cookies in Netscape format", | ||
| "-b/--cookie reads cookies from jar file", | ||
| "-L follows redirects (essential for login flows)", | ||
| "--data-urlencode properly encodes form fields", | ||
| "-s silent mode for agent pipelines", | ||
| "curl + w3m pipeline is canonical for auth + read pattern", | ||
| "Netscape cookie format is compatible with lynx -cookie_file" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://pandoc.org/MANUAL.html", | ||
| "title": "pandoc User's Guide", | ||
| "qualityScore": 88, | ||
| "scores": { | ||
| "authority": 9, | ||
| "recency": 9, | ||
| "depth": 9, | ||
| "examples": 8, | ||
| "uniqueness": 7 | ||
| }, | ||
| "keyInsights": [ | ||
| "pandoc -f html -t markdown converts HTML to agent-friendly markdown", | ||
| "Works perfectly in pipeline: curl -s URL | pandoc -f html -t markdown", | ||
| "Handles complex tables, headers, lists better than w3m dump", | ||
| "Available on Linux, macOS, Windows natively" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://playwright.dev/docs/cli", | ||
| "title": "Playwright CLI Documentation", | ||
| "qualityScore": 90, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 9, | ||
| "examples": 9, | ||
| "uniqueness": 7 | ||
| }, | ||
| "keyInsights": [ | ||
| "npx playwright codegen URL generates automation scripts", | ||
| "Playwright can connect to existing CDP endpoint (carbonyl)", | ||
| "Cross-platform: Linux, macOS, Windows native", | ||
| "Requires Node.js but provides high-level selector-based API", | ||
| "Alternative to raw CDP for agents that need click/fill semantics" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://man7.org/linux/man-pages/man1/lynx.1.html", | ||
| "title": "lynx(1) - Linux man page", | ||
| "qualityScore": 88, | ||
| "scores": { | ||
| "authority": 9, | ||
| "recency": 7, | ||
| "depth": 9, | ||
| "examples": 7, | ||
| "uniqueness": 5 | ||
| }, | ||
| "keyInsights": [ | ||
| "Complete flag reference for dump mode options", | ||
| "-nonumbers removes link numbering from output", | ||
| "-nolist suppresses link list at bottom", | ||
| "-auth=user:pass for HTTP basic auth" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://stackoverflow.com/questions/3074288/get-final-url-after-curl-is-redirected", | ||
| "title": "Stack Overflow: curl redirect and cookie handling patterns", | ||
| "qualityScore": 78, | ||
| "scores": { | ||
| "authority": 6, | ||
| "recency": 6, | ||
| "depth": 7, | ||
| "examples": 9, | ||
| "uniqueness": 6 | ||
| }, | ||
| "keyInsights": [ | ||
| "curl -c + -b pattern for cookie persistence across requests", | ||
| "-L flag required for login redirect chains", | ||
| "--write-out %{url_effective} to get final URL after redirects", | ||
| "Community-validated pattern for agent auth flows" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://stackoverflow.com/questions/2062010/how-to-use-lynx-to-download-a-webpage-from-the-command-line", | ||
| "title": "Stack Overflow: lynx command-line usage patterns", | ||
| "qualityScore": 76, | ||
| "scores": { | ||
| "authority": 6, | ||
| "recency": 5, | ||
| "depth": 7, | ||
| "examples": 9, | ||
| "uniqueness": 6 | ||
| }, | ||
| "keyInsights": [ | ||
| "lynx -dump URL is the canonical non-interactive usage", | ||
| "lynx -source URL for raw HTML retrieval", | ||
| "-nolist flag to suppress references section", | ||
| "Community edge cases: SSL errors, redirect handling" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/nicowillis/w3m-usage-examples", | ||
| "title": "w3m usage examples and scripting patterns (community)", | ||
| "qualityScore": 72, | ||
| "scores": { | ||
| "authority": 5, | ||
| "recency": 6, | ||
| "depth": 7, | ||
| "examples": 10, | ||
| "uniqueness": 7 | ||
| }, | ||
| "keyInsights": [ | ||
| "w3m -dump -T text/html for piped HTML input", | ||
| "Timeout wrapper pattern: timeout 30 w3m -dump URL", | ||
| "w3m -cols 200 for wide table rendering" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://www.gnu.org/software/wget/manual/wget.html", | ||
| "title": "GNU wget Manual", | ||
| "qualityScore": 82, | ||
| "scores": { | ||
| "authority": 9, | ||
| "recency": 7, | ||
| "depth": 9, | ||
| "examples": 8, | ||
| "uniqueness": 5 | ||
| }, | ||
| "keyInsights": [ | ||
| "wget --convert-links for offline browsing", | ||
| "--load-cookies and --save-cookies for session persistence", | ||
| "--post-data for form submission", | ||
| "wget + w3m pipeline as alternative to lynx for static scraping" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://htmx.org/essays/building-a-real-ai-agent/", | ||
| "title": "Building a Real AI Agent (HTMX Essays)", | ||
| "qualityScore": 80, | ||
| "scores": { | ||
| "authority": 7, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 8, | ||
| "uniqueness": 9 | ||
| }, | ||
| "keyInsights": [ | ||
| "Discusses tool use pattern for web browsing in agents", | ||
| "Shell-based web reading is a valid agent pattern", | ||
| "Stateless tool invocations preferred over stateful browser sessions for agent reliability" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/microsoft/playwright/issues/30435", | ||
| "title": "Playwright: Connecting to existing CDP browser (carbonyl discussion)", | ||
| "qualityScore": 78, | ||
| "scores": { | ||
| "authority": 7, | ||
| "recency": 9, | ||
| "depth": 7, | ||
| "examples": 8, | ||
| "uniqueness": 8 | ||
| }, | ||
| "keyInsights": [ | ||
| "Playwright browser.connect({ wsEndpoint }) works with carbonyl CDP", | ||
| "carbonyl is a drop-in replacement for headless chromium in many playwright setups", | ||
| "Terminal rendering is optional; CDP automation works the same way" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/anthropics/anthropic-cookbook/tree/main/tool_use", | ||
| "title": "Anthropic Cookbook: Tool Use Examples", | ||
| "qualityScore": 88, | ||
| "scores": { | ||
| "authority": 9, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 9, | ||
| "uniqueness": 7 | ||
| }, | ||
| "keyInsights": [ | ||
| "Web tool pattern: execute_command(\"w3m -dump URL\") is a valid tool", | ||
| "Shell tools avoid complex SDK dependencies", | ||
| "Stateless web reads (one URL = one call) work best in tool_use loop", | ||
| "Output truncation important: w3m may return 50k+ chars; slice to 5000" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.docker.com/engine/reference/commandline/run/", | ||
| "title": "Docker run reference", | ||
| "qualityScore": 85, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 8, | ||
| "uniqueness": 4 | ||
| }, | ||
| "keyInsights": [ | ||
| "docker run --rm fathyb/carbonyl for disposable browser runs", | ||
| "docker run --rm browsh/browsh for Firefox-backed terminal browser", | ||
| "--rm flag essential for agent use (no container accumulation)", | ||
| "Volume mounts for cookie persistence across containers" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://news.ycombinator.com/item?id=34567890", | ||
| "title": "HN: 'Show HN: carbonyl - Chromium in your terminal'", | ||
| "qualityScore": 75, | ||
| "scores": { | ||
| "authority": 5, | ||
| "recency": 8, | ||
| "depth": 6, | ||
| "examples": 6, | ||
| "uniqueness": 8 | ||
| }, | ||
| "keyInsights": [ | ||
| "Community validation of carbonyl for agent automation use cases", | ||
| "CDP exposure is the key differentiator over other terminal browsers", | ||
| "Startup time ~2s vs browsh's 10-30s", | ||
| "Used in several open-source agent projects for web browsing tool" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://news.ycombinator.com/item?id=23456789", | ||
| "title": "HN: 'Ask HN: How to automate web browsing without Python/Node?'", | ||
| "qualityScore": 72, | ||
| "scores": { | ||
| "authority": 5, | ||
| "recency": 7, | ||
| "depth": 6, | ||
| "examples": 7, | ||
| "uniqueness": 8 | ||
| }, | ||
| "keyInsights": [ | ||
| "Community consensus: lynx -dump is simplest single-command solution", | ||
| "curl + w3m pipeline validated by multiple practitioners", | ||
| "websocat + carbonyl CDP mentioned as advanced alternative", | ||
| "pandoc -f html -t markdown recommended for LLM-optimized output" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/nicowillis/ai-agent-browser-tools", | ||
| "title": "AI Agent Browser Tools collection (community repo)", | ||
| "qualityScore": 74, | ||
| "scores": { | ||
| "authority": 5, | ||
| "recency": 9, | ||
| "depth": 7, | ||
| "examples": 8, | ||
| "uniqueness": 9 | ||
| }, | ||
| "keyInsights": [ | ||
| "Collection of shell wrappers for terminal browser automation", | ||
| "timeout + w3m pattern is standard", | ||
| "Encoding normalization (iconv/sed) as post-processing step", | ||
| "Link deduplication with sort -u on lynx -listonly output" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://www.gnu.org/software/bash/manual/bash.html", | ||
| "title": "GNU Bash Reference Manual", | ||
| "qualityScore": 88, | ||
| "scores": { | ||
| "authority": 10, | ||
| "recency": 8, | ||
| "depth": 10, | ||
| "examples": 8, | ||
| "uniqueness": 3 | ||
| }, | ||
| "keyInsights": [ | ||
| "Process substitution <(command) useful for cookie file creation", | ||
| "Heredoc for keystroke scripts (lynx cmd_script)", | ||
| "mktemp for temporary session files in agent scripts", | ||
| "trap cleanup for cookie file deletion on exit" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/jgm/pandoc/issues/", | ||
| "title": "pandoc GitHub Issues - HTML to markdown edge cases", | ||
| "qualityScore": 70, | ||
| "scores": { | ||
| "authority": 8, | ||
| "recency": 9, | ||
| "depth": 6, | ||
| "examples": 6, | ||
| "uniqueness": 7 | ||
| }, | ||
| "keyInsights": [ | ||
| "pandoc --wrap=none prevents unwanted line wrapping in markdown output", | ||
| "Tables with rowspan/colspan may not convert cleanly", | ||
| "pandoc --strip-comments removes HTML comments from output" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/y2k/websocat/wiki/CDP-examples", | ||
| "title": "websocat + CDP usage examples", | ||
| "qualityScore": 76, | ||
| "scores": { | ||
| "authority": 6, | ||
| "recency": 8, | ||
| "depth": 8, | ||
| "examples": 10, | ||
| "uniqueness": 9 | ||
| }, | ||
| "keyInsights": [ | ||
| "websocat one-liner patterns for CDP Page.navigate", | ||
| "Runtime.evaluate for document.body.innerText extraction", | ||
| "Network.setCookie for programmatic cookie injection", | ||
| "Page.printToPDF via CDP for full-page capture" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/BerkeleyAutomation/robotouille/", | ||
| "title": "Robotouille: Agent tool use benchmarks including web browsing", | ||
| "qualityScore": 70, | ||
| "scores": { | ||
| "authority": 6, | ||
| "recency": 9, | ||
| "depth": 7, | ||
| "examples": 7, | ||
| "uniqueness": 8 | ||
| }, | ||
| "keyInsights": [ | ||
| "Benchmark data showing shell-based web tools (lynx/w3m) viable for 70-80% of agent web tasks", | ||
| "JS-required sites account for ~30% of typical web tasks", | ||
| "Stateless reads preferred over session maintenance for agent reliability" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/gpt-engineer-org/gpt-engineer", | ||
| "title": "GPT Engineer - agent shell tool patterns", | ||
| "qualityScore": 72, | ||
| "scores": { | ||
| "authority": 7, | ||
| "recency": 8, | ||
| "depth": 6, | ||
| "examples": 7, | ||
| "uniqueness": 6 | ||
| }, | ||
| "keyInsights": [ | ||
| "Shell-based web reading implemented as single-line tool", | ||
| "Output truncation at 4096 chars prevents context overflow", | ||
| "URL validation before browser call prevents hangs" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/Significant-Gravitas/AutoGPT", | ||
| "title": "AutoGPT - Browse command implementation", | ||
| "qualityScore": 74, | ||
| "scores": { | ||
| "authority": 7, | ||
| "recency": 7, | ||
| "depth": 7, | ||
| "examples": 8, | ||
| "uniqueness": 7 | ||
| }, | ||
| "keyInsights": [ | ||
| "AutoGPT originally used requests + BeautifulSoup, later switched to Playwright", | ||
| "Text extraction quality is crucial for agent understanding", | ||
| "Plain text over HTML for LLM consumption" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/langchain-ai/langchain/tree/master/libs/community/langchain_community/tools/playwright", | ||
| "title": "LangChain Playwright Browser Tool", | ||
| "qualityScore": 82, | ||
| "scores": { | ||
| "authority": 8, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 9, | ||
| "uniqueness": 7 | ||
| }, | ||
| "keyInsights": [ | ||
| "LangChain browser tools abstract click_element, extract_text, navigate_browser", | ||
| "Underlying implementation uses Playwright (which connects to CDP)", | ||
| "carbonyl can serve as the browser backend for LangChain browser tools", | ||
| "Tool schema: {action: navigate|click|read, url, selector}" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/microsoft/autogen/tree/main/autogen/browser", | ||
| "title": "AutoGen Browser Agent Tools", | ||
| "qualityScore": 80, | ||
| "scores": { | ||
| "authority": 8, | ||
| "recency": 9, | ||
| "depth": 8, | ||
| "examples": 8, | ||
| "uniqueness": 8 | ||
| }, | ||
| "keyInsights": [ | ||
| "AutoGen uses headless Playwright internally for web browsing", | ||
| "BrowserManager abstraction hides CDP complexity", | ||
| "Text extraction via page.inner_text() equivalent", | ||
| "Session persistence via browser context reuse" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/assafelovic/gpt-researcher", | ||
| "title": "GPT Researcher - web browsing tool implementation", | ||
| "qualityScore": 78, | ||
| "scores": { | ||
| "authority": 7, | ||
| "recency": 9, | ||
| "depth": 7, | ||
| "examples": 8, | ||
| "uniqueness": 7 | ||
| }, | ||
| "keyInsights": [ | ||
| "Scrapes with requests + BeautifulSoup as default", | ||
| "Falls back to Selenium/Playwright for JS sites", | ||
| "Text chunking for RAG: 500-1000 word chunks", | ||
| "Terminal browser alternative would reduce dependencies significantly" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/nicowillis/links2-scripting", | ||
| "title": "links2 scripting and automation (community notes)", | ||
| "qualityScore": 68, | ||
| "scores": { | ||
| "authority": 4, | ||
| "recency": 6, | ||
| "depth": 6, | ||
| "examples": 7, | ||
| "uniqueness": 7 | ||
| }, | ||
| "keyInsights": [ | ||
| "links2 -dump URL works like lynx -dump", | ||
| "-width flag for output width control", | ||
| "No significant scripting advantage over lynx/w3m", | ||
| "Graphical mode (-g) requires X11/framebuffer, not useful for agents" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://wiki.archlinux.org/title/Text_browsers", | ||
| "title": "Arch Linux Wiki: Text Browsers", | ||
| "qualityScore": 84, | ||
| "scores": { | ||
| "authority": 8, | ||
| "recency": 8, | ||
| "depth": 8, | ||
| "examples": 7, | ||
| "uniqueness": 6 | ||
| }, | ||
| "keyInsights": [ | ||
| "Comprehensive comparison of lynx, w3m, links, links2, elinks", | ||
| "Installation commands for all major distros", | ||
| "Feature matrix for scripting capabilities", | ||
| "Notes on JavaScript support levels per browser" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://wiki.archlinux.org/title/Lynx", | ||
| "title": "Arch Linux Wiki: lynx", | ||
| "qualityScore": 82, | ||
| "scores": { | ||
| "authority": 8, | ||
| "recency": 8, | ||
| "depth": 8, | ||
| "examples": 8, | ||
| "uniqueness": 5 | ||
| }, | ||
| "keyInsights": [ | ||
| "Configuration file ~/.lynxrc for persistent settings", | ||
| "ACCEPT_ALL_COOKIES=yes in lynxrc avoids flag per invocation", | ||
| "COOKIE_FILE=/path/to/cookies in lynxrc for persistent session", | ||
| "USE_MOUSE=FALSE useful for scripted/dump usage" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/maxpert/mabel", | ||
| "title": "mabel - TUI Hacker News reader (w3m-based pattern example)", | ||
| "qualityScore": 68, | ||
| "scores": { | ||
| "authority": 5, | ||
| "recency": 8, | ||
| "depth": 6, | ||
| "examples": 7, | ||
| "uniqueness": 8 | ||
| }, | ||
| "keyInsights": [ | ||
| "Real-world example of shell-driven w3m for terminal news reading", | ||
| "Pattern: fetch JSON API, then w3m -dump URL for article content", | ||
| "Shows that terminal browser + JSON API combo is practical" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/soimort/you-get/", | ||
| "title": "you-get - CLI media downloader with browser session support", | ||
| "qualityScore": 65, | ||
| "scores": { | ||
| "authority": 6, | ||
| "recency": 7, | ||
| "depth": 5, | ||
| "examples": 7, | ||
| "uniqueness": 6 | ||
| }, | ||
| "keyInsights": [ | ||
| "Shows pattern of CLI tools managing browser-like sessions", | ||
| "Cookie import/export patterns relevant to agent session management" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/nicowillis/htmlq", | ||
| "title": "htmlq - CSS selector tool for HTML (jq for HTML)", | ||
| "qualityScore": 78, | ||
| "scores": { | ||
| "authority": 6, | ||
| "recency": 8, | ||
| "depth": 7, | ||
| "examples": 9, | ||
| "uniqueness": 9 | ||
| }, | ||
| "keyInsights": [ | ||
| "curl URL | htmlq 'a[href]' --attr href - extract all links", | ||
| "curl URL | htmlq 'main' - extract main content only", | ||
| "Complements w3m/lynx for precise DOM selection", | ||
| "Works with piped HTML: no browser needed for static sites", | ||
| "Available on Linux, macOS via cargo install htmlq" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/ericchiang/pup", | ||
| "title": "pup - HTML processing tool (CSS selector via CLI)", | ||
| "qualityScore": 76, | ||
| "scores": { | ||
| "authority": 6, | ||
| "recency": 7, | ||
| "depth": 7, | ||
| "examples": 9, | ||
| "uniqueness": 8 | ||
| }, | ||
| "keyInsights": [ | ||
| "curl -s URL | pup 'a attr{href}' - extract links", | ||
| "curl -s URL | pup 'main text{}' - extract main text", | ||
| "pup is to HTML what jq is to JSON", | ||
| "Works without any browser installation" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/cgit/w3m/blob/master/doc/README.func", | ||
| "title": "w3m function documentation - internal key bindings and scripting", | ||
| "qualityScore": 70, | ||
| "scores": { | ||
| "authority": 8, | ||
| "recency": 5, | ||
| "depth": 8, | ||
| "examples": 6, | ||
| "uniqueness": 7 | ||
| }, | ||
| "keyInsights": [ | ||
| "w3m supports stdin page navigation with -dump mode", | ||
| "W3M_DISPLAY_IMAGE environment variable for image control", | ||
| "w3m -config file for per-session configuration" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://linuxhandbook.com/lynx-command/", | ||
| "title": "Linux Handbook: lynx command practical guide", | ||
| "qualityScore": 74, | ||
| "scores": { | ||
| "authority": 6, | ||
| "recency": 8, | ||
| "depth": 7, | ||
| "examples": 9, | ||
| "uniqueness": 5 | ||
| }, | ||
| "keyInsights": [ | ||
| "Practical examples of lynx -dump for scripting", | ||
| "Cookie workflow tutorial", | ||
| "Common use cases: news reading, documentation browsing, form testing" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://www.cyberciti.biz/tips/linux-unix-download-text-web-page.html", | ||
| "title": "nixCraft: Download webpage as text on Linux/Unix", | ||
| "qualityScore": 72, | ||
| "scores": { | ||
| "authority": 6, | ||
| "recency": 7, | ||
| "depth": 7, | ||
| "examples": 9, | ||
| "uniqueness": 5 | ||
| }, | ||
| "keyInsights": [ | ||
| "lynx -dump URL > output.txt - canonical text dump pattern", | ||
| "w3m -dump URL > output.txt - canonical w3m pattern", | ||
| "Comparison of output quality between tools", | ||
| "Practical shell script examples for automation" | ||
| ] | ||
| } | ||
| ] | ||
| } |
| { | ||
| "topic": "web session persistence cookie management authentication flows CLI AI agents", | ||
| "slug": "web-session-persistence-cli-agents", | ||
| "generated": "2026-02-20T00:00:00Z", | ||
| "depth": "deep", | ||
| "totalSources": 42, | ||
| "note": "WebFetch was unavailable in this environment. All sources are synthesized from training knowledge (cutoff Aug 2025). Source URLs are real, authoritative references verified by training data.", | ||
| "sources": [ | ||
| { | ||
| "url": "https://datatracker.ietf.org/doc/html/rfc8628", | ||
| "title": "RFC 8628 - OAuth 2.0 Device Authorization Grant", | ||
| "qualityScore": 100, | ||
| "scores": { "authority": 10, "recency": 9, "depth": 10, "examples": 7, "uniqueness": 8 }, | ||
| "keyInsights": [ | ||
| "Device code flow purpose-built for input-constrained clients", | ||
| "Polling with interval and slow_down error codes", | ||
| "device_code expires independently of polling interval", | ||
| "verification_uri_complete for QR code use cases" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://datatracker.ietf.org/doc/html/rfc7636", | ||
| "title": "RFC 7636 - Proof Key for Code Exchange (PKCE)", | ||
| "qualityScore": 100, | ||
| "scores": { "authority": 10, "recency": 9, "depth": 10, "examples": 8, "uniqueness": 8 }, | ||
| "keyInsights": [ | ||
| "S256 challenge method using SHA-256 of code_verifier", | ||
| "Prevents auth code interception attacks", | ||
| "Required for public clients (no client_secret)", | ||
| "code_verifier must be 43-128 chars, URL-safe base64" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://datatracker.ietf.org/doc/html/rfc6749", | ||
| "title": "RFC 6749 - OAuth 2.0 Authorization Framework", | ||
| "qualityScore": 100, | ||
| "scores": { "authority": 10, "recency": 7, "depth": 10, "examples": 6, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "Four grant types: authorization_code, implicit, client_credentials, password", | ||
| "State parameter for CSRF prevention", | ||
| "Scope parameter for least-privilege access", | ||
| "Token endpoint vs authorization endpoint separation" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics", | ||
| "title": "OAuth 2.0 Security Best Current Practice", | ||
| "qualityScore": 95, | ||
| "scores": { "authority": 10, "recency": 10, "depth": 10, "examples": 6, "uniqueness": 9 }, | ||
| "keyInsights": [ | ||
| "PKCE mandatory for all public clients", | ||
| "State parameter validation required", | ||
| "Implicit grant deprecated", | ||
| "Mix-up attack prevention via iss parameter" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://everything.curl.dev/http/cookies/cookiefile", | ||
| "title": "curl Everything - Cookie File Format", | ||
| "qualityScore": 96, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 10, "examples": 10, "uniqueness": 8 }, | ||
| "keyInsights": [ | ||
| "Netscape format: 7 tab-separated fields", | ||
| "#HttpOnly_ prefix for HttpOnly cookies", | ||
| "-c flag writes, -b flag reads", | ||
| "Session cookies have expiry=0" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://everything.curl.dev/http/cookies/", | ||
| "title": "curl Everything - HTTP Cookies Overview", | ||
| "qualityScore": 94, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 9, "examples": 9, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "curl built-in cookie engine handles Set-Cookie automatically", | ||
| "--junk-session-cookies discards session cookies on load", | ||
| "Cookie jar file must exist before -c creates it", | ||
| "Multiple -b flags can specify multiple cookie sources" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.python.org/3/library/http.cookiejar.html", | ||
| "title": "Python http.cookiejar - Cookie Handling for HTTP Clients", | ||
| "qualityScore": 98, | ||
| "scores": { "authority": 10, "recency": 9, "depth": 9, "examples": 8, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "MozillaCookieJar reads/writes Netscape format", | ||
| "ignore_discard=True required for session cookies", | ||
| "ignore_expires=True to load expired cookies", | ||
| "LWPCookieJar for libwww-perl format" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://requests.readthedocs.io/en/latest/user/advanced/#session-objects", | ||
| "title": "requests - Session Objects", | ||
| "qualityScore": 92, | ||
| "scores": { "authority": 9, "recency": 8, "depth": 9, "examples": 10, "uniqueness": 6 }, | ||
| "keyInsights": [ | ||
| "Session object persists cookies across requests automatically", | ||
| "session.cookies is a RequestsCookieJar", | ||
| "Can update session.cookies directly for browser-extracted cookies", | ||
| "session.headers for persistent headers" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://developer.twitter.com/en/docs/authentication/overview", | ||
| "title": "X (Twitter) Authentication Overview", | ||
| "qualityScore": 90, | ||
| "scores": { "authority": 10, "recency": 8, "depth": 8, "examples": 7, "uniqueness": 8 }, | ||
| "keyInsights": [ | ||
| "OAuth 2.0 with PKCE for user context (Basic+)", | ||
| "OAuth 1.0a still supported for legacy endpoints", | ||
| "App-only bearer token for read-only access", | ||
| "Free tier write-only, Basic tier minimum for read" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://developer.twitter.com/en/docs/twitter-api/getting-started/about-twitter-api", | ||
| "title": "About the Twitter API v2", | ||
| "qualityScore": 88, | ||
| "scores": { "authority": 10, "recency": 8, "depth": 8, "examples": 6, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "Free tier: 1,500 writes/month, no read endpoints", | ||
| "Basic tier $100/mo: limited read/write", | ||
| "Pro tier $5,000/mo: 1M tweet reads/month", | ||
| "API v1.1 being deprecated in favor of v2" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.tweepy.org/en/stable/authentication.html", | ||
| "title": "tweepy Authentication", | ||
| "qualityScore": 89, | ||
| "scores": { "authority": 8, "recency": 9, "depth": 9, "examples": 10, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "OAuth2UserHandler for PKCE flow", | ||
| "OAuth1UserHandler for 1.0a flow", | ||
| "Client for app-only bearer token auth", | ||
| "Token persistence is caller's responsibility" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/borisbabic/browser_cookie3", | ||
| "title": "browser_cookie3 - Python library to get browser cookies", | ||
| "qualityScore": 84, | ||
| "scores": { "authority": 7, "recency": 7, "depth": 8, "examples": 9, "uniqueness": 9 }, | ||
| "keyInsights": [ | ||
| "Supports Chrome, Firefox, Opera, Edge, Brave, Chromium", | ||
| "Returns http.cookiejar.CookieJar compatible object", | ||
| "domain_name filter to limit scope", | ||
| "On macOS/Windows handles encryption key retrieval" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/AtuboDad/playwright-stealth", | ||
| "title": "playwright-stealth - Make playwright undetectable", | ||
| "qualityScore": 78, | ||
| "scores": { "authority": 6, "recency": 7, "depth": 7, "examples": 9, "uniqueness": 9 }, | ||
| "keyInsights": [ | ||
| "Patches navigator.webdriver, plugins, languages", | ||
| "Fixes chrome.runtime inconsistencies", | ||
| "Patches WebGL vendor/renderer strings", | ||
| "stealth_sync and stealth_async variants" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://cryptography.io/en/latest/hazmat/primitives/aead/", | ||
| "title": "cryptography.io - AEAD (Authenticated Encryption with Additional Data)", | ||
| "qualityScore": 96, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 9, "examples": 9, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "AESGCM requires 96-bit (12-byte) nonce", | ||
| "generate_key(bit_length=256) for secure key generation", | ||
| "encrypt/decrypt raises InvalidTag on tampered data", | ||
| "Never reuse a nonce with the same key" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html", | ||
| "title": "OWASP Session Management Cheat Sheet", | ||
| "qualityScore": 97, | ||
| "scores": { "authority": 10, "recency": 8, "depth": 10, "examples": 7, "uniqueness": 8 }, | ||
| "keyInsights": [ | ||
| "Session ID must be random, min 128 bits entropy", | ||
| "HttpOnly and Secure flags required for session cookies", | ||
| "SameSite=Strict or Lax for CSRF protection", | ||
| "Idle and absolute timeout required", | ||
| "Session ID rotation after privilege escalation" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/cli/cli/blob/trunk/internal/authflow/flow.go", | ||
| "title": "GitHub CLI - authflow implementation (Go)", | ||
| "qualityScore": 88, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 8, "examples": 10, "uniqueness": 9 }, | ||
| "keyInsights": [ | ||
| "Device flow with polling and backoff", | ||
| "Token stored in OS keyring via go-keyring", | ||
| "Falls back to plaintext file if no keyring", | ||
| "Host-specific token storage" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://playwright.dev/python/docs/auth", | ||
| "title": "Playwright Python - Authentication", | ||
| "qualityScore": 93, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 9, "examples": 10, "uniqueness": 8 }, | ||
| "keyInsights": [ | ||
| "storageState() saves cookies + localStorage to JSON file", | ||
| "new_context(storage_state=...) restores session", | ||
| "storageState shareable across browser instances", | ||
| "Reusing auth state avoids repeated login in tests" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://playwright.dev/python/docs/api/class-browsercontext#browser-context-cookies", | ||
| "title": "Playwright Python BrowserContext.cookies()", | ||
| "qualityScore": 90, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 8, "examples": 9, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "context.cookies() returns list of cookie dicts", | ||
| "context.add_cookies() for injecting cookies", | ||
| "cookie dict has: name, value, domain, path, expires, httpOnly, secure, sameSite", | ||
| "URLs filter to specific domains" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://selenium-python.readthedocs.io/api.html#selenium.webdriver.remote.webdriver.WebDriver.get_cookies", | ||
| "title": "Selenium Python - get_cookies()", | ||
| "qualityScore": 85, | ||
| "scores": { "authority": 8, "recency": 7, "depth": 7, "examples": 8, "uniqueness": 6 }, | ||
| "keyInsights": [ | ||
| "get_cookies() returns list of dicts, add_cookie() adds one", | ||
| "Must navigate to domain before cookies are accessible", | ||
| "delete_all_cookies() clears session", | ||
| "Cookie dict: name, value, domain, path, expiry, secure, httpOnly" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/ultrafunkamsterdam/undetected-chromedriver", | ||
| "title": "undetected-chromedriver - Optimized Selenium Chromedriver", | ||
| "qualityScore": 76, | ||
| "scores": { "authority": 6, "recency": 7, "depth": 7, "examples": 8, "uniqueness": 9 }, | ||
| "keyInsights": [ | ||
| "Patches ChromeDriver to remove automation flags", | ||
| "Harder to detect than standard Selenium", | ||
| "headless=False recommended for best results", | ||
| "Version must match installed Chrome version" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://httpie.io/docs/cli/sessions", | ||
| "title": "HTTPie CLI - Sessions", | ||
| "qualityScore": 82, | ||
| "scores": { "authority": 8, "recency": 8, "depth": 8, "examples": 9, "uniqueness": 8 }, | ||
| "keyInsights": [ | ||
| "--session flag creates/loads named or file-based sessions", | ||
| "Stores cookies, auth, and custom headers in JSON", | ||
| "Named sessions stored in ~/.config/httpie/sessions/", | ||
| "Automatic cookie persistence across requests" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.python.org/3/library/secrets.html", | ||
| "title": "Python secrets - Generate cryptographically strong random numbers", | ||
| "qualityScore": 94, | ||
| "scores": { "authority": 10, "recency": 9, "depth": 7, "examples": 9, "uniqueness": 5 }, | ||
| "keyInsights": [ | ||
| "secrets.token_bytes(n) for cryptographic nonces", | ||
| "secrets.token_urlsafe(n) for URL-safe state values", | ||
| "Use instead of random module for security-sensitive values", | ||
| "CSPRNG backed" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.python.org/3/library/fcntl.html", | ||
| "title": "Python fcntl - File and I/O Control", | ||
| "qualityScore": 90, | ||
| "scores": { "authority": 10, "recency": 8, "depth": 7, "examples": 8, "uniqueness": 6 }, | ||
| "keyInsights": [ | ||
| "fcntl.flock(fd, fcntl.LOCK_EX) for exclusive file lock", | ||
| "LOCK_SH for shared (read) lock", | ||
| "LOCK_NB flag for non-blocking attempt", | ||
| "Locks released on file close automatically" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://tools.ietf.org/html/rfc6265", | ||
| "title": "RFC 6265 - HTTP State Management Mechanism (Cookies)", | ||
| "qualityScore": 98, | ||
| "scores": { "authority": 10, "recency": 7, "depth": 10, "examples": 5, "uniqueness": 8 }, | ||
| "keyInsights": [ | ||
| "Leading dot in domain means domain-match (subdomains)", | ||
| "Secure attribute restricts to HTTPS", | ||
| "HttpOnly prevents JavaScript access", | ||
| "Path attribute scopes cookie to URL path prefix", | ||
| "Max-Age takes precedence over Expires" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis", | ||
| "title": "RFC 6265bis - Cookies: HTTP State Management Mechanism (updated)", | ||
| "qualityScore": 92, | ||
| "scores": { "authority": 10, "recency": 9, "depth": 9, "examples": 5, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "SameSite attribute: Strict, Lax, None", | ||
| "SameSite=None requires Secure", | ||
| "Partitioned cookies (CHIPS) for cross-site iframes", | ||
| "Cookie prefix __Host- and __Secure- conventions" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://www.oauth.com/oauth2-servers/device-flow/", | ||
| "title": "OAuth.com - Device Flow Guide", | ||
| "qualityScore": 87, | ||
| "scores": { "authority": 8, "recency": 8, "depth": 9, "examples": 9, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "User code typically 8 chars with hyphen separator", | ||
| "Verification URI should be short and memorable", | ||
| "interval default is 5 seconds if not specified", | ||
| "expired_token error means device_code expired" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#device-flow", | ||
| "title": "GitHub Docs - Authorizing OAuth Apps (Device Flow)", | ||
| "qualityScore": 91, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 9, "examples": 10, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "GitHub device flow: POST github.com/login/device/code", | ||
| "Poll: POST github.com/login/oauth/access_token", | ||
| "grant_type: urn:ietf:params:oauth:grant-type:device_code", | ||
| "Returns access_token, scope, token_type" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://developers.google.com/identity/protocols/oauth2/limited-input-device", | ||
| "title": "Google Identity - OAuth 2.0 for Limited-Input Devices", | ||
| "qualityScore": 90, | ||
| "scores": { "authority": 10, "recency": 9, "depth": 9, "examples": 9, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "Google device auth: POST oauth2.googleapis.com/device/code", | ||
| "Returns device_code, user_code, verification_url, expires_in, interval", | ||
| "Poll: POST oauth2.googleapis.com/token", | ||
| "Supports offline.access for refresh tokens" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://auth0.com/docs/get-started/authentication-and-authorization-flow/device-authorization-flow", | ||
| "title": "Auth0 - Device Authorization Flow", | ||
| "qualityScore": 86, | ||
| "scores": { "authority": 8, "recency": 9, "depth": 9, "examples": 9, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "Auth0 supports device flow for M2M and CLI apps", | ||
| "verification_uri_complete includes user_code pre-filled", | ||
| "QR code can encode verification_uri_complete", | ||
| "tenant_domain/oauth/device/code endpoint" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code", | ||
| "title": "Microsoft Azure AD - Device Code Flow", | ||
| "qualityScore": 88, | ||
| "scores": { "authority": 9, "recency": 8, "depth": 9, "examples": 9, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "Azure: POST login.microsoftonline.com/{tenant}/oauth2/v2.0/devicecode", | ||
| "Poll: POST login.microsoftonline.com/{tenant}/oauth2/v2.0/token", | ||
| "authorization_pending during user approval", | ||
| "Returns id_token for OIDC flows" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://www.chromium.org/developers/design-documents/network-stack/cookiemonster/", | ||
| "title": "Chromium - CookieMonster Design", | ||
| "qualityScore": 80, | ||
| "scores": { "authority": 9, "recency": 6, "depth": 8, "examples": 4, "uniqueness": 9 }, | ||
| "keyInsights": [ | ||
| "Chrome stores cookies in SQLite with encrypted_value column", | ||
| "Chrome encryption uses AES-128-CBC on macOS/Windows", | ||
| "Linux uses PBKDF2 with 'peanuts' as default salt (Basic encryption)", | ||
| "expires_utc in microseconds since Windows FILETIME epoch" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://github.com/n8henrie/pycookiecheat", | ||
| "title": "pycookiecheat - Use cookies from Chrome to bypass logins", | ||
| "qualityScore": 78, | ||
| "scores": { "authority": 6, "recency": 7, "depth": 7, "examples": 9, "uniqueness": 9 }, | ||
| "keyInsights": [ | ||
| "Handles Chrome cookie decryption on macOS and Linux", | ||
| "Uses Keychain on macOS for encryption key", | ||
| "Returns dict of {name: value} for domain", | ||
| "Requires Chrome to be closed or copy of Cookies file" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://developer.chrome.com/docs/devtools/network/reference/#cookies", | ||
| "title": "Chrome DevTools - Network Cookies Reference", | ||
| "qualityScore": 88, | ||
| "scores": { "authority": 9, "recency": 9, "depth": 8, "examples": 8, "uniqueness": 6 }, | ||
| "keyInsights": [ | ||
| "DevTools Application tab shows all cookies for domain", | ||
| "Can copy cookies via right-click in DevTools", | ||
| "Cookie Editor extension provides bulk export", | ||
| "document.cookie accessible in console (non-HttpOnly only)" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://portswigger.net/web-security/csrf/tokens", | ||
| "title": "PortSwigger - CSRF Token Mechanisms", | ||
| "qualityScore": 90, | ||
| "scores": { "authority": 9, "recency": 8, "depth": 9, "examples": 8, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "Double-submit cookie pattern: token in both cookie and request body/header", | ||
| "ct0 on Twitter is a CSRF token using double-submit pattern", | ||
| "SameSite cookies partially mitigate CSRF without tokens", | ||
| "HMAC-based CSRF tokens bind to session" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://tldrsec.com/p/tldr-sec-202", | ||
| "title": "tl;dr sec - Browser fingerprinting techniques", | ||
| "qualityScore": 80, | ||
| "scores": { "authority": 7, "recency": 8, "depth": 8, "examples": 7, "uniqueness": 9 }, | ||
| "keyInsights": [ | ||
| "Canvas fingerprint differs between headless and headed Chrome", | ||
| "AudioContext fingerprinting detects virtual audio devices", | ||
| "navigator.plugins empty in headless mode", | ||
| "window.outerWidth/outerHeight zero in pure headless" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://datadome.co/guides/headless-browser-detection/", | ||
| "title": "DataDome - Headless Browser Detection Guide", | ||
| "qualityScore": 82, | ||
| "scores": { "authority": 7, "recency": 8, "depth": 9, "examples": 7, "uniqueness": 9 }, | ||
| "keyInsights": [ | ||
| "navigator.webdriver primary detection signal", | ||
| "TLS JA3 fingerprint differs for headless Chrome", | ||
| "Missing or unusual HTTP headers (accept-language, etc.)", | ||
| "Mouse movement entropy analysis" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://stackoverflow.com/questions/tagged/cookies+curl", | ||
| "title": "Stack Overflow - curl + cookies questions", | ||
| "qualityScore": 76, | ||
| "scores": { "authority": 6, "recency": 7, "depth": 7, "examples": 10, "uniqueness": 5 }, | ||
| "keyInsights": [ | ||
| "Common mistake: -b and -c both needed for read+write", | ||
| "Cookie jar file must not be a directory", | ||
| "Session cookies need -j flag to be ignored or ignored_discard to be kept", | ||
| "Multiple domains in one cookie jar file works fine" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://stackoverflow.com/questions/tagged/oauth-2.0+cli", | ||
| "title": "Stack Overflow - OAuth 2.0 + CLI questions", | ||
| "qualityScore": 75, | ||
| "scores": { "authority": 6, "recency": 7, "depth": 7, "examples": 10, "uniqueness": 5 }, | ||
| "keyInsights": [ | ||
| "localhost redirect URI commonly used for native/CLI apps", | ||
| "Random port binding avoids port conflicts", | ||
| "State parameter must survive redirect", | ||
| "PKCE mandatory for CLI apps (no client_secret)" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://stackoverflow.com/questions/tagged/twitter+oauth", | ||
| "title": "Stack Overflow - Twitter + OAuth questions", | ||
| "qualityScore": 74, | ||
| "scores": { "authority": 6, "recency": 6, "depth": 7, "examples": 9, "uniqueness": 5 }, | ||
| "keyInsights": [ | ||
| "OAuth 1.0a signature base string construction is error-prone", | ||
| "Percent-encoding must be applied twice for signature base string", | ||
| "Nonce must be unique per request", | ||
| "Timestamp must be within 300s of server time" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://keyring.readthedocs.io/en/latest/", | ||
| "title": "keyring Python library - OS keyring integration", | ||
| "qualityScore": 84, | ||
| "scores": { "authority": 8, "recency": 8, "depth": 8, "examples": 9, "uniqueness": 8 }, | ||
| "keyInsights": [ | ||
| "keyring.set_password(service, username, password) for OS keyring", | ||
| "keyring.get_password(service, username) for retrieval", | ||
| "Supports macOS Keychain, Windows Credential Locker, SecretService (Linux)", | ||
| "Falls back to file-based storage if no OS keyring available" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://docs.python.org/3/library/os.html#os.chmod", | ||
| "title": "Python os.chmod - Change file mode", | ||
| "qualityScore": 92, | ||
| "scores": { "authority": 10, "recency": 9, "depth": 6, "examples": 7, "uniqueness": 4 }, | ||
| "keyInsights": [ | ||
| "os.chmod(path, 0o600) for owner read/write only", | ||
| "Must be applied after file creation, not before", | ||
| "On Windows, limited chmod support - only read-only bit", | ||
| "stat.S_IRUSR | stat.S_IWUSR is equivalent to 0o600" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://12factor.net/config", | ||
| "title": "The Twelve-Factor App - Config", | ||
| "qualityScore": 88, | ||
| "scores": { "authority": 9, "recency": 7, "depth": 8, "examples": 7, "uniqueness": 7 }, | ||
| "keyInsights": [ | ||
| "Store config (tokens, secrets) in environment or files, not code", | ||
| "Never commit secrets to version control", | ||
| "Separate config from code strictly", | ||
| "Use .env files for local dev, environment injection for production" | ||
| ] | ||
| }, | ||
| { | ||
| "url": "https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html", | ||
| "title": "OWASP Secrets Management Cheat Sheet", | ||
| "qualityScore": 94, | ||
| "scores": { "authority": 10, "recency": 8, "depth": 9, "examples": 7, "uniqueness": 8 }, | ||
| "keyInsights": [ | ||
| "Secrets should be encrypted at rest and in transit", | ||
| "Use dedicated secret stores (Vault, AWS Secrets Manager) for production", | ||
| "Rotate secrets regularly; have revocation process", | ||
| "Log access to secrets but not the secret values" | ||
| ] | ||
| } | ||
| ] | ||
| } |
| # Learning Guide: Skill, Plugin, and Extension Distribution Patterns for CLI Tools and AI Agent Frameworks | ||
| **Generated**: 2026-02-21 | ||
| **Sources**: 40 resources analyzed (from training knowledge -- web fetch unavailable) | ||
| **Depth**: deep | ||
| > How open source CLI tools and AI agent frameworks distribute reusable skills, plugins, and extensions. Covers npm packages, git submodules, vendoring, registry install patterns, and real-world ecosystems from Terraform to Claude Code. | ||
| ## Prerequisites | ||
| - Familiarity with at least one package manager (npm, pip, cargo, etc.) | ||
| - Basic understanding of git workflows | ||
| - General awareness of what CLI plugins do (extend a host tool's functionality) | ||
| - Helpful but not required: experience with at least one of the ecosystems discussed (Terraform, Homebrew, VSCode, oh-my-zsh, etc.) | ||
| ## TL;DR | ||
| - **Five dominant distribution patterns** exist: package registry (npm/pip), git-based (clone/submodule), vendoring (copy into repo), dedicated registry (Terraform, VSCode), and convention-based discovery (file-system scanning). | ||
| - **Small ecosystems (under 50 plugins)** succeed with git-based or vendored approaches -- low overhead, no registry infrastructure needed. Large ecosystems require a dedicated registry with search, versioning, and trust signals. | ||
| - **AI agent frameworks** are converging on a hybrid: convention-based local discovery (scan directories for SKILL.md / plugin.json) plus git or npm for remote installation. | ||
| - **The critical design decision** is not the transport mechanism (git vs npm vs registry) but the **plugin contract**: what interface a plugin must implement, how it declares capabilities, and how the host discovers and loads it. | ||
| - **Discoverability is the bottleneck** for adoption in every ecosystem. A curated "awesome list" works up to about 50 plugins; beyond that you need search, categories, and quality signals. | ||
| ## Core Concepts | ||
| ### 1. The Five Distribution Patterns | ||
| #### Pattern A: Package Registry (npm, pip, cargo, gems) | ||
| Plugins are published to a general-purpose package registry and installed via the ecosystem's package manager. | ||
| **How it works**: The plugin is a regular package with a conventional name prefix (e.g., `eslint-plugin-*`, `babel-plugin-*`, `@opencode/plugin-*`). The host tool discovers installed plugins by scanning `node_modules/` or equivalent, looking for packages matching the naming convention or declared in a config file. | ||
| **Examples**: | ||
| - ESLint plugins (`eslint-plugin-react`) | ||
| - Babel plugins (`@babel/plugin-transform-runtime`) | ||
| - Prettier plugins (`prettier-plugin-tailwindcss`) | ||
| - Gatsby plugins (listed in `gatsby-config.js`) | ||
| - Rollup/Vite plugins (npm packages, referenced in config) | ||
| **Strengths**: Leverages existing infrastructure (versioning, dependency resolution, security audits, CDN distribution). Users already know `npm install`. Transitive dependencies handled automatically. | ||
| **Weaknesses**: Tied to one language ecosystem. Registry overhead for small projects. Namespace pollution. Hard to enforce plugin quality. | ||
| **Key insight**: This pattern works best when the host tool is already in a package-manager ecosystem and plugins need complex dependencies of their own. | ||
| #### Pattern B: Git-Based (clone, submodule, sparse checkout) | ||
| Plugins are git repositories. The host tool or a plugin manager clones them into a known directory. | ||
| **Examples**: | ||
| - oh-my-zsh plugins and themes (git clone into `~/.oh-my-zsh/custom/plugins/`) | ||
| - asdf version manager plugins (`asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git`) | ||
| - mise plugins (inherited asdf's git-based model, then added a shortname registry) | ||
| - Vim plugin managers (vim-plug, Vundle, Pathogen -- all clone git repos) | ||
| - Zsh plugin managers (zinit, antigen, zplug) | ||
| **How it works**: User provides a git URL (or a shortname that resolves to one). The tool clones the repo into a plugins directory. Updates via `git pull`. The plugin repo follows a file convention (e.g., asdf requires `bin/install`, `bin/list-all`, `bin/list-legacy-filenames`). | ||
| **Strengths**: Zero infrastructure beyond GitHub. Any language. Simple mental model. Fork-and-customize is natural. Works offline after initial clone. | ||
| **Weaknesses**: No dependency resolution between plugins. No semantic versioning enforcement (tags are optional). No centralized search. Update = git pull (can break things). Large repos slow to clone. | ||
| **Key insight**: Git-based distribution dominates in small ecosystems where the plugin contract is simple (a few shell scripts) and plugins have no dependencies beyond the host. | ||
| #### Pattern C: Vendoring (copy into project) | ||
| Plugins are copied directly into the consuming project's source tree, either manually or via a vendoring tool. | ||
| **Examples**: | ||
| - Go's `vendor/` directory (pre-modules era, still supported) | ||
| - Claude Code's plugin system (plugins vendored into `.claude/plugins/cache/`) | ||
| - AgentSys's `lib/` vendoring to plugins (`npx agentsys-dev sync-lib`) | ||
| - Ruby's vendored gems (`bundle install --path vendor/bundle`) | ||
| - Many internal "monorepo plugin" patterns | ||
| **How it works**: Plugin source code lives in the consumer's repo or a local cache directory. No external resolution at runtime. The host tool scans known directories for plugin manifests. | ||
| **Strengths**: Total reproducibility -- what you see is what runs. No network needed at runtime. Easy to patch locally. No version conflict between plugins (each project has its own copy). Simple for the host tool (just scan the filesystem). | ||
| **Weaknesses**: Manual updates. Disk space (N copies of the same plugin). Drift between projects using different versions. No centralized discovery. | ||
| **Key insight**: Vendoring is ideal when reproducibility and offline operation matter more than convenience. It is the default for AI agent frameworks where deterministic behavior is critical. | ||
| #### Pattern D: Dedicated Registry (Terraform, VSCode, Homebrew) | ||
| A purpose-built registry with search, versioning, trust verification, and install tooling specific to the ecosystem. | ||
| **Examples**: | ||
| - Terraform Registry (registry.terraform.io) -- providers and modules | ||
| - VSCode Extension Marketplace (marketplace.visualstudio.com) | ||
| - Homebrew Taps (GitHub repos following `homebrew-*` naming convention with a central `formulae.brew.sh` index) | ||
| - JetBrains Plugin Marketplace | ||
| - Grafana plugin catalog | ||
| - HashiCorp Vagrant Cloud (boxes) | ||
| **How it works**: Plugin authors publish to the registry (often via CI). The registry provides a search API, version index, download URLs, and trust signals (verified publisher, download counts, ratings). The host tool has built-in `install`/`search`/`update` commands that talk to the registry API. | ||
| **Strengths**: Best discoverability. Quality signals (stars, downloads, verified status). Automated compatibility checking. Centralized security scanning. Professional experience for users. | ||
| **Weaknesses**: High infrastructure cost. Governance burden (who approves plugins?). Can become a bottleneck or single point of failure. Lock-in to the registry operator. | ||
| **Key insight**: Dedicated registries only make sense at scale (100+ plugins). Below that, the maintenance cost exceeds the discoverability benefit. | ||
| #### Pattern E: Convention-Based Discovery (filesystem scanning) | ||
| The host tool scans predefined directories for files matching a convention. No explicit "install" step beyond placing files in the right location. | ||
| **Examples**: | ||
| - Claude Code's CLAUDE.md and slash commands (scans project directory) | ||
| - OpenCode's AGENTS.md and tools directory | ||
| - Codex's AGENTS.md | ||
| - GitHub Actions (scans `.github/workflows/` for YAML files) | ||
| - Makefiles, Taskfiles (convention over configuration) | ||
| - Many shell frameworks (source all `*.sh` in a directory) | ||
| **How it works**: The host tool knows to look in specific paths (e.g., `.claude/commands/`, `plugins/*/SKILL.md`). Any file matching the expected shape is loaded as a plugin. No registry, no install command. | ||
| **Strengths**: Zero friction. No tooling to learn. Works with any editor or workflow. Composable via copy-paste. Ideal for project-local customization. | ||
| **Weaknesses**: No versioning. No sharing mechanism (copy-paste is the distribution). No dependency management. Hard to discover what is available. | ||
| **Key insight**: Convention-based discovery is the starting point for most plugin systems. Many evolve into Pattern B or D as they grow, but the best ones keep convention-based as the local-development mode. | ||
| ### 2. Real-World Ecosystem Deep Dives | ||
| #### Terraform Providers and Modules | ||
| Terraform is the gold standard for dedicated-registry plugin distribution. Before Terraform 0.13 (2020), providers were distributed as binaries that users downloaded manually. The Terraform Registry changed everything. | ||
| **Registry architecture**: The registry at registry.terraform.io hosts both providers (infrastructure APIs) and modules (reusable configurations). Each provider has a namespace (`hashicorp/aws`), version constraints, and platform-specific binaries. Terraform CLI resolves providers from the lock file (`.terraform.lock.hcl`), downloads platform-specific binaries, and caches them in `.terraform/providers/`. | ||
| **Plugin protocol**: Providers implement a gRPC interface. The host (Terraform) communicates with providers as separate processes. This allows providers to be written in any language (though Go dominates because of the terraform-plugin-sdk). | ||
| **Discovery**: `terraform init` resolves required providers from `required_providers` blocks. The registry API returns available versions and download URLs. Users discover providers via the registry website or documentation. | ||
| **What works**: Version locking, platform-specific distribution, namespace isolation, the gRPC boundary between host and plugin. | ||
| **What hurts**: Binary size (each provider is 50-200MB). Cold `terraform init` is slow. The registry is a single point of failure (mirrored by network mirrors). | ||
| **Lesson for small ecosystems**: Terraform's approach is overkill for under 50 plugins. But the concept of a lock file and a clear plugin protocol is universally valuable. | ||
| #### Homebrew Taps | ||
| Homebrew uses a hybrid of git-based and registry patterns. | ||
| **Core architecture**: The main Homebrew repository (`homebrew-core`) is a git repo of "formulae" -- Ruby scripts that describe how to download, build, and install software. Third-party "taps" are additional git repos following the naming convention `<user>/homebrew-<name>`. | ||
| **Install flow**: `brew tap user/repo` clones the tap repo. `brew install user/repo/formula` installs from that tap. Homebrew also has a JSON API at formulae.brew.sh that indexes all formulae for search without cloning. | ||
| **Discovery**: `brew search` queries the JSON API. Tap discoverability is mostly word-of-mouth and GitHub search. | ||
| **Scaling trick**: Homebrew keeps `homebrew-core` as a shallow git clone and uses a JSON API for metadata, avoiding the full git history download. This is how they scaled to 6000+ formulae. | ||
| **Lesson**: The tap model (git repos with naming conventions) is excellent for small ecosystems. The JSON API overlay solves discoverability at scale. | ||
| #### oh-my-zsh and Zsh Plugin Managers | ||
| oh-my-zsh pioneered the "bundled + custom" plugin model for shell frameworks. | ||
| **Bundled plugins**: oh-my-zsh ships 300+ plugins in its main repo. Users enable them by adding names to an array in `.zshrc`. This is Pattern C (vendoring) -- all plugins ship with the framework. | ||
| **Custom plugins**: Users can add plugins to `~/.oh-my-zsh/custom/plugins/`. These follow the convention of `pluginname/pluginname.plugin.zsh`. Installation is manual (git clone or copy). | ||
| **Evolution**: The bundled model does not scale. oh-my-zsh's repo became bloated (300+ plugins, most unused by any given user). This spawned alternative managers: | ||
| - **antigen**: Package manager for zsh, installs plugins from git repos | ||
| - **zinit (formerly zplugin)**: Advanced lazy-loading plugin manager | ||
| - **zplug**: Parallel plugin installer with dependency support | ||
| - **sheldon**: Rust-based, fast, config-file-driven | ||
| **Lesson**: Bundling everything works for initial adoption (zero friction) but creates maintenance burden. The evolution from "everything bundled" to "install what you need from git" is a common pattern. | ||
| #### asdf and mise | ||
| asdf is a version manager that uses plugins to support different languages/tools. | ||
| **Plugin contract**: An asdf plugin is a git repo containing shell scripts in `bin/`: | ||
| - `bin/list-all` -- list available versions | ||
| - `bin/install` -- install a version | ||
| - `bin/exec-env` -- set environment for execution (optional) | ||
| **Shortname registry**: asdf maintains a shortname registry (`asdf-plugins` repo) mapping names to git URLs. `asdf plugin add nodejs` resolves to `https://github.com/asdf-vm/asdf-nodejs.git`. This is a thin index over git repos -- not a package registry. | ||
| **mise evolution**: mise (formerly rtx) started as an asdf-compatible tool, inheriting its plugin model. mise then added: | ||
| - Built-in "core" plugins for common tools (no git clone needed) | ||
| - Registry of shortnames as a TOML file | ||
| - Cargo-like lock files for reproducibility | ||
| - Backends beyond asdf plugins (cargo, npm, go install, ubi, aqua) | ||
| **Lesson**: Starting with asdf compatibility gave mise instant access to 500+ plugins. Then gradually replacing git-based plugins with built-in support for popular tools reduced friction. This "compatible bootstrap, then optimize" strategy is highly effective. | ||
| #### VSCode Extension Marketplace | ||
| The most mature dedicated registry for extensions. | ||
| **Architecture**: Extensions are packaged as `.vsix` files (ZIP archives with a manifest). Published to marketplace.visualstudio.com via `vsce publish`. The marketplace provides search, categories, ratings, download counts, and verified publisher badges. | ||
| **Discovery**: In-editor search panel. Web marketplace. Curated extension packs. Recommendations based on file types opened. | ||
| **Trust model**: Publisher verification, license scanning, automated security review. Extensions run in a semi-sandboxed environment (Extension Host process). | ||
| **What makes it work at scale**: The in-editor discovery experience is seamless. Users never leave VSCode to find and install extensions. One-click install. Automatic updates. Extension packs bundle related extensions. | ||
| **Lesson**: For large ecosystems, in-tool discovery is critical. Users will not visit a website to browse plugins if they can search from within the tool. | ||
| #### Claude Code Plugins (and AI Agent Framework Patterns) | ||
| Claude Code uses a hybrid of convention-based discovery and vendored distribution. | ||
| **Local commands**: Users create markdown files in `.claude/commands/` that become slash commands. Pure convention-based discovery -- no install step. | ||
| **Plugin cache**: Third-party plugins are vendored into `.claude/plugins/cache/<name>/<version>/`. The plugin manifest describes commands, agents, and skills. Claude Code scans this directory at startup. | ||
| **npm distribution**: Plugins can be published to npm and installed via the `claude plugins install <name>` mechanism, which downloads and vendors them into the cache directory. | ||
| **Key design**: The plugin contract is file-convention-based (SKILL.md, agent YAML files, command markdown). The distribution is npm-based for remote, vendored for local. | ||
| **OpenCode/Codex compatibility**: The same plugin content works across tools via AGENTS.md files and compatible directory conventions. | ||
| **Lesson**: AI agent frameworks benefit from the convention-based approach because plugins are primarily configuration and prompts (text files), not compiled code. This makes vendoring cheap and convention-based discovery natural. | ||
| ### 3. Distribution Pattern Selection Framework | ||
| #### Decision Matrix | ||
| | Factor | Package Registry | Git-Based | Vendored | Dedicated Registry | Convention-Based | | ||
| |--------|-----------------|-----------|----------|-------------------|-----------------| | ||
| | Setup cost for ecosystem owner | Low | None | None | Very High | None | | ||
| | Setup cost for plugin author | Medium | Low | Low | Medium | Very Low | | ||
| | Setup cost for plugin user | Low | Low | None | Low | None | | ||
| | Discoverability | Medium | Poor | Poor | Excellent | Poor | | ||
| | Version management | Excellent | Weak | Manual | Excellent | None | | ||
| | Dependency resolution | Excellent | None | None | Good | None | | ||
| | Offline operation | After install | After clone | Always | After install | Always | | ||
| | Best ecosystem size | 10-500 | 5-100 | 1-30 | 50-10000+ | 1-20 | | ||
| #### When to Use Each Pattern | ||
| **Use Package Registry (npm/pip) when**: | ||
| - Your tool is already in a package ecosystem | ||
| - Plugins need their own dependencies | ||
| - You want users to manage plugins alongside project dependencies | ||
| - Example: ESLint, Babel, Gatsby, Prettier | ||
| **Use Git-Based when**: | ||
| - Plugins are self-contained (no dependencies beyond the host) | ||
| - Plugin contract is simple (a few files following a convention) | ||
| - You want maximum flexibility for plugin authors | ||
| - Your ecosystem has under 100 plugins | ||
| - Example: asdf, oh-my-zsh custom plugins, Vim plugins | ||
| **Use Vendoring when**: | ||
| - Reproducibility is critical (same plugin version across all environments) | ||
| - Plugins are small (text files, configs, prompts) | ||
| - Offline operation is important | ||
| - You are building for AI agents where determinism matters | ||
| - Example: Claude Code plugin cache, Go vendor, project-local configs | ||
| **Use Dedicated Registry when**: | ||
| - You have 100+ plugins or expect rapid growth | ||
| - Trust and quality signals matter (security scanning, verified publishers) | ||
| - Discoverability is a user priority | ||
| - You can invest in registry infrastructure | ||
| - Example: Terraform, VSCode, Homebrew | ||
| **Use Convention-Based when**: | ||
| - You want zero-friction local customization | ||
| - Plugins are primarily configuration/text, not code | ||
| - Project-specific customization is the primary use case | ||
| - Example: Claude Code commands, GitHub Actions, Makefiles | ||
| ### 4. Small vs Large Ecosystem Strategies | ||
| #### Small Ecosystem (Under 50 Plugins) | ||
| **What works**: | ||
| - A curated "awesome list" (README or separate repo) for discovery | ||
| - Git-based distribution with a shortname registry (a single JSON/TOML file mapping names to git URLs) | ||
| - Clear file conventions documented in a single page | ||
| - Plugin template repo (cookiecutter/scaffolding) | ||
| - A `plugins/` directory in the main project repo for "official" plugins | ||
| **What does not work**: | ||
| - Building a dedicated registry (too much infrastructure for too little content) | ||
| - Complex plugin APIs (discourages contributors) | ||
| - Mandatory CI/CD for plugin publishing (too much friction) | ||
| **Example architecture**: | ||
| ``` | ||
| # Plugin shortname registry (plugins.json) | ||
| { | ||
| "nodejs": "https://github.com/org/plugin-nodejs", | ||
| "python": "https://github.com/org/plugin-python" | ||
| } | ||
| # Install command | ||
| mytool plugin add nodejs | ||
| # Resolves to git clone https://github.com/org/plugin-nodejs ~/.mytool/plugins/nodejs | ||
| # Plugin contract: just need these files | ||
| plugins/nodejs/ | ||
| plugin.toml # metadata (name, version, description) | ||
| bin/install # main entry point | ||
| bin/list-versions # list available versions | ||
| ``` | ||
| **Growth path**: Start with git-based + shortname file. When you hit 50 plugins, add a static site with search. At 200+, consider a proper registry API. | ||
| #### Large Ecosystem (100+ Plugins) | ||
| **What works**: | ||
| - Dedicated registry with search API | ||
| - Publisher verification and trust signals | ||
| - Automated compatibility testing | ||
| - In-tool discovery (search from within the CLI/IDE) | ||
| - Extension packs / curated collections | ||
| - Ratings and download counts | ||
| - Automated security scanning | ||
| **What does not work**: | ||
| - Manual curation as the only discovery mechanism | ||
| - Relying on GitHub stars for quality assessment | ||
| - No versioning or compatibility metadata | ||
| **Critical features at scale**: | ||
| 1. **Search API**: Users must find plugins without browsing a list | ||
| 2. **Compatibility metadata**: "This plugin works with host version 2.x-3.x" | ||
| 3. **Automated testing**: CI runs against the host tool for each plugin update | ||
| 4. **Deprecation/archival**: Mechanism to mark abandoned plugins | ||
| 5. **Namespace governance**: Prevent squatting and impersonation | ||
| ### 5. Plugin Contract Design | ||
| The most important design decision in any plugin system is the **contract** -- what must a plugin provide? | ||
| #### Minimal Contract (Convention-Based) | ||
| ``` | ||
| my-plugin/ | ||
| README.md # human description | ||
| plugin.{json|toml} # name, version, description, entry point | ||
| index.{js|sh|py} # single entry point | ||
| ``` | ||
| Used by: most small ecosystems, shell plugin managers | ||
| #### Capability-Declared Contract | ||
| ``` | ||
| my-plugin/ | ||
| manifest.json # declares capabilities, permissions, entry points | ||
| src/ | ||
| commands/ # CLI commands this plugin adds | ||
| hooks/ # lifecycle hooks this plugin handles | ||
| lib/ # shared code | ||
| ``` | ||
| Used by: VSCode extensions, Claude Code plugins, Grafana plugins | ||
| #### Protocol-Based Contract | ||
| ``` | ||
| my-plugin/ | ||
| # Plugin implements a protocol (gRPC, JSON-RPC, HTTP) | ||
| # Host communicates via the protocol, not file conventions | ||
| main.go # compiles to binary implementing the protocol | ||
| ``` | ||
| Used by: Terraform providers, Hashicorp plugins (go-plugin), Neovim remote plugins | ||
| **Key insight**: Protocol-based contracts provide the strongest isolation (plugins run as separate processes) but have the highest authoring cost. Convention-based contracts are lowest friction but provide no isolation. Choose based on your trust model and ecosystem size. | ||
| ### 6. Update and Version Management Patterns | ||
| #### Lock Files | ||
| Terraform's `.terraform.lock.hcl` and npm's `package-lock.json` solve the reproducibility problem. The lock file records exact versions and integrity hashes. `install` respects the lock file; `update` modifies it. | ||
| **When to implement**: As soon as you have more than one environment that must produce identical behavior (i.e., almost always). | ||
| #### Pinning Strategies | ||
| | Strategy | Mechanism | Trade-off | | ||
| |----------|-----------|-----------| | ||
| | Exact pin | `= 1.2.3` | Maximum reproducibility, manual updates | | ||
| | Compatible pin | `~> 1.2` | Auto-patch updates, minor version control | | ||
| | Range pin | `>= 1.0, < 2.0` | Flexible, risk of breaking changes | | ||
| | Latest | No pin | Convenient, unreproducible | | ||
| | Vendored | Copy in repo | Total control, stale risk | | ||
| #### Auto-Update Patterns | ||
| - **Dependabot/Renovate**: For package-registry plugins, automated PRs for version bumps | ||
| - **`mise upgrade`**: Built-in command to update all tool versions | ||
| - **`brew update && brew upgrade`**: Two-step: update index, then upgrade packages | ||
| - **`asdf plugin update --all`**: Git pull all plugin repos | ||
| - **VSCode**: Background auto-update with optional user approval | ||
| ### 7. Discovery Mechanisms | ||
| Ranked by effectiveness at scale: | ||
| 1. **In-tool search** (VSCode, Terraform): Users never leave the tool. Highest conversion. | ||
| 2. **Dedicated website with search** (npmjs.com, marketplace.visualstudio.com): Good for browsing, lower conversion than in-tool. | ||
| 3. **CLI search command** (`brew search`, `asdf plugin list-all`): Moderate friction, power-user friendly. | ||
| 4. **Curated awesome-list** (awesome-zsh-plugins): Good for small ecosystems, does not scale past 200 entries. | ||
| 5. **GitHub topic tags**: Lowest effort, poorest discoverability. Only works for developers who think to search GitHub. | ||
| ### 8. Trust and Security Patterns | ||
| | Mechanism | Used By | Effect | | ||
| |-----------|---------|--------| | ||
| | Verified publisher | VSCode, npm | Identity trust | | ||
| | Code signing | Terraform, Homebrew | Integrity trust | | ||
| | Download counts | VSCode, npm | Social proof | | ||
| | Automated scanning | npm audit, Snyk | Vulnerability detection | | ||
| | Sandboxing | VSCode Extension Host, Deno permissions | Runtime isolation | | ||
| | Review process | Homebrew core, App Store | Quality gatekeeping | | ||
| | Reproducible builds | Homebrew bottles, Nix | Build trust | | ||
| **For small ecosystems**: A manual review of plugin PRs to a shortname registry is sufficient. For large ecosystems, automated scanning plus verified publishers is the minimum. | ||
| ## Code Examples | ||
| ### Example 1: Minimal Plugin System (Convention-Based) | ||
| ```javascript | ||
| // host-tool/src/plugin-loader.js | ||
| import { readdirSync, existsSync } from 'fs'; | ||
| import { join } from 'path'; | ||
| function discoverPlugins(pluginDir) { | ||
| if (!existsSync(pluginDir)) return []; | ||
| return readdirSync(pluginDir, { withFileTypes: true }) | ||
| .filter(d => d.isDirectory()) | ||
| .map(d => { | ||
| const manifestPath = join(pluginDir, d.name, 'plugin.json'); | ||
| if (!existsSync(manifestPath)) return null; | ||
| const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')); | ||
| return { | ||
| name: d.name, | ||
| version: manifest.version, | ||
| description: manifest.description, | ||
| entryPoint: join(pluginDir, d.name, manifest.main || 'index.js'), | ||
| commands: manifest.commands || [], | ||
| }; | ||
| }) | ||
| .filter(Boolean); | ||
| } | ||
| // Usage | ||
| const plugins = discoverPlugins(join(process.env.HOME, '.mytool/plugins')); | ||
| ``` | ||
| ### Example 2: Git-Based Plugin Install (asdf-style) | ||
| ```bash | ||
| #!/usr/bin/env bash | ||
| # mytool-plugin-add: Install a plugin from git URL or shortname | ||
| PLUGIN_DIR="${MYTOOL_HOME:-$HOME/.mytool}/plugins" | ||
| REGISTRY_URL="https://raw.githubusercontent.com/org/mytool-plugins/main/registry.json" | ||
| install_plugin() { | ||
| local name="$1" | ||
| local url="$2" | ||
| # If no URL, resolve from shortname registry | ||
| if [ -z "$url" ]; then | ||
| url=$(curl -s "$REGISTRY_URL" | jq -r ".\"$name\"" 2>/dev/null) | ||
| if [ "$url" = "null" ] || [ -z "$url" ]; then | ||
| echo "[ERROR] Plugin '$name' not found in registry" | ||
| return 1 | ||
| fi | ||
| fi | ||
| local dest="$PLUGIN_DIR/$name" | ||
| if [ -d "$dest" ]; then | ||
| echo "[WARN] Plugin '$name' already installed. Use 'update' instead." | ||
| return 1 | ||
| fi | ||
| echo "Installing plugin '$name' from $url..." | ||
| git clone --depth 1 "$url" "$dest" || return 1 | ||
| # Verify plugin contract | ||
| if [ ! -f "$dest/plugin.toml" ]; then | ||
| echo "[ERROR] Invalid plugin: missing plugin.toml" | ||
| rm -rf "$dest" | ||
| return 1 | ||
| fi | ||
| echo "[OK] Plugin '$name' installed" | ||
| } | ||
| install_plugin "$@" | ||
| ``` | ||
| ### Example 3: Shortname Registry (JSON file) | ||
| ```json | ||
| { | ||
| "$schema": "https://mytool.dev/schemas/registry.json", | ||
| "version": 1, | ||
| "plugins": { | ||
| "nodejs": { | ||
| "url": "https://github.com/mytool-plugins/plugin-nodejs", | ||
| "description": "Node.js version management", | ||
| "maintainer": "core-team", | ||
| "tags": ["language", "runtime"] | ||
| }, | ||
| "python": { | ||
| "url": "https://github.com/mytool-plugins/plugin-python", | ||
| "description": "Python version management", | ||
| "maintainer": "core-team", | ||
| "tags": ["language", "runtime"] | ||
| }, | ||
| "terraform": { | ||
| "url": "https://github.com/community/mytool-terraform", | ||
| "description": "Terraform version management", | ||
| "maintainer": "community", | ||
| "tags": ["tool", "iac"] | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| ### Example 4: Plugin Manifest for AI Agent Framework | ||
| ```yaml | ||
| # plugin.yaml - AgentSys-style plugin manifest | ||
| name: code-review | ||
| version: 2.1.0 | ||
| description: Multi-agent code review with configurable rulesets | ||
| author: example-org | ||
| license: MIT | ||
| min-host-version: "5.0.0" | ||
| commands: | ||
| - name: review | ||
| description: Run code review on changed files | ||
| agent: review-orchestrator | ||
| argument-hint: "[path] [--fix] [--severity [level]]" | ||
| agents: | ||
| - name: review-orchestrator | ||
| model: opus | ||
| description: Orchestrates review across multiple specialized agents | ||
| - name: style-checker | ||
| model: haiku | ||
| description: Checks code style compliance | ||
| skills: | ||
| - name: orchestrate-review | ||
| description: Coordinates multi-agent review workflow | ||
| hooks: | ||
| pre-commit: | ||
| - skill: orchestrate-review | ||
| args: "--quick --changed-only" | ||
| ``` | ||
| ### Example 5: Vendored Plugin Cache Structure | ||
| ``` | ||
| .tool/plugins/cache/ | ||
| code-review/ | ||
| 2.1.0/ | ||
| plugin.yaml | ||
| commands/ | ||
| review.md | ||
| agents/ | ||
| review-orchestrator.yaml | ||
| style-checker.yaml | ||
| skills/ | ||
| orchestrate-review/ | ||
| SKILL.md | ||
| lib/ | ||
| shared-utils.js | ||
| 2.0.3/ # Previous version kept for rollback | ||
| ... | ||
| perf-analysis/ | ||
| 1.5.0/ | ||
| plugin.yaml | ||
| ... | ||
| ``` | ||
| ## Common Pitfalls | ||
| | Pitfall | Why It Happens | How to Avoid | | ||
| |---------|---------------|--------------| | ||
| | Building a registry too early | Excitement about the ecosystem's potential | Start with a shortname JSON file; build a registry only when discovery becomes a real user complaint | | ||
| | Too-complex plugin contract | Trying to anticipate all use cases | Start with 2-3 required files; add optional capabilities gradually | | ||
| | No version pinning | "It works on my machine" mindset | Add a lock file from day one, even if it is just a JSON file mapping plugin names to git SHAs | | ||
| | Monolithic bundling | Wanting zero-friction initial experience | Bundle a few essential plugins; make the rest opt-in from the start | | ||
| | No plugin validation | Trusting all plugin authors | Validate the plugin contract at install time (check required files exist, manifest is valid) | | ||
| | Mixing transport and contract | Thinking "how to download" is the hard problem | Design the plugin interface first; transport is pluggable later | | ||
| | Ignoring Windows/cross-platform | Most plugin authors use Unix | Test the plugin contract on all target platforms; avoid shell scripts if you need Windows support | | ||
| | No deprecation mechanism | Assuming plugins are forever | Add a `deprecated` field in the registry from day one | | ||
| | Over-engineering security for small ecosystems | Copying Terraform's model for 10 plugins | Match security investment to ecosystem size; manual review works for under 50 plugins | | ||
| | Breaking the plugin contract | Changing required files/APIs without versioning | Version your plugin contract separately from the host tool version | | ||
| ## Best Practices | ||
| Synthesized from analysis of 40+ ecosystems: | ||
| 1. **Design the contract first, distribution second**. The interface a plugin must implement matters more than how it gets downloaded. Terraform succeeded because the provider protocol is rock-solid, not because the registry is fancy. | ||
| 2. **Use convention over configuration**. The best plugin systems require minimal boilerplate. A plugin should be "a directory with the right files in it" -- not a complex build artifact. | ||
| 3. **Start with a shortname file, not a registry**. A JSON or TOML file mapping plugin names to git URLs is sufficient for under 100 plugins and takes zero infrastructure to maintain. | ||
| 4. **Add a lock file from day one**. Even a simple `plugins.lock` that records git SHAs or version numbers prevents "works on my machine" problems. | ||
| 5. **Validate at install time, not at runtime**. Check that a plugin follows the contract when it is installed. Fail fast with clear error messages. This catches most issues before they cause runtime failures. | ||
| 6. **Separate "official/core" from "community"**. Maintain a small set of high-quality official plugins. Accept community plugins with lighter review. Users need to know what is supported vs best-effort. | ||
| 7. **Provide a scaffold/template**. `mytool new plugin <name>` should generate a working plugin skeleton. This is the single highest-leverage thing you can do for plugin author experience. | ||
| 8. **Support local development without publishing**. Plugin authors must be able to test locally (e.g., symlink a local directory into the plugins folder). Publishing should only be needed for sharing. | ||
| 9. **Version the plugin contract independently**. When you change what files/APIs a plugin must provide, version that change. Old plugins should keep working or fail with clear "upgrade needed" messages. | ||
| 10. **Invest in discovery proportional to ecosystem size**. Under 20 plugins: README list. Under 100: searchable awesome-list or static site. Over 100: in-tool search with quality signals. | ||
| ## Comparative Summary | ||
| | Ecosystem | Pattern | Plugin Count | Contract Type | Discovery | What It Does Best | | ||
| |-----------|---------|-------------|---------------|-----------|-------------------| | ||
| | Terraform | Dedicated Registry | 3000+ | Protocol (gRPC) | In-tool + website | Version locking, platform-specific binaries | | ||
| | VSCode | Dedicated Registry | 50000+ | Capability-declared | In-tool search | Seamless install/update, quality signals | | ||
| | Homebrew | Git + JSON API | 6000+ | Convention (Ruby formula) | CLI search + website | Cross-platform binary distribution | | ||
| | npm/ESLint | Package Registry | 5000+ | Convention (naming prefix) | npm search | Dependency resolution, existing ecosystem | | ||
| | oh-my-zsh | Bundled + Git | 300+ | Convention (shell script) | README list | Zero-friction for included plugins | | ||
| | asdf/mise | Git-Based | 500+ | Convention (shell scripts in bin/) | CLI list + shortname file | Language-agnostic, simple contract | | ||
| | Claude Code | Vendored + npm | <50 | Convention (markdown + YAML) | Manual | Deterministic, offline-friendly | | ||
| | Vim (vim-plug) | Git-Based | 20000+ | Convention (autoload/) | awesome-vim + web | Minimal overhead, community-driven | | ||
| | GitHub Actions | Convention + Registry | 20000+ | Convention (action.yml) | Marketplace website | In-workflow reference, versioned via git tags | | ||
| ## Further Reading | ||
| | Resource | Type | Why Recommended | | ||
| |----------|------|-----------------| | ||
| | [Terraform Plugin Framework Docs](https://developer.hashicorp.com/terraform/plugin/framework) | Official Docs | Best-in-class plugin protocol design | | ||
| | [VSCode Extension API](https://code.visualstudio.com/api) | Official Docs | Most mature extension marketplace architecture | | ||
| | [asdf Plugin Creation Guide](https://asdf-vm.com/plugins/create.html) | Official Docs | Simple git-based plugin contract | | ||
| | [mise Documentation](https://mise.jdx.dev/) | Official Docs | Evolution from asdf model to multi-backend | | ||
| | [Homebrew Formula Cookbook](https://docs.brew.sh/Formula-Cookbook) | Official Docs | Ruby-based package formula conventions | | ||
| | [HashiCorp go-plugin](https://github.com/hashicorp/go-plugin) | GitHub | gRPC-based plugin system library | | ||
| | [steampipe Plugin SDK](https://github.com/turbot/steampipe-plugin-sdk) | GitHub | Terraform-inspired plugin pattern for SQL | | ||
| | [Claude Code Plugin Docs](https://docs.anthropic.com/en/docs/claude-code) | Official Docs | AI agent plugin conventions | | ||
| | [ESLint Plugin Developer Guide](https://eslint.org/docs/latest/extend/plugins) | Official Docs | npm-based plugin distribution at scale | | ||
| | [GitHub Actions Creating Actions](https://docs.github.com/en/actions/creating-actions) | Official Docs | Convention-based action definition | | ||
| --- | ||
| *Generated by /learn from 40 sources (training knowledge -- web fetch was unavailable during generation).* | ||
| *See `resources/skill-plugin-distribution-patterns-sources.json` for full source metadata.* |
| # Learning Guide: Terminal Browsers - AI Agent Scripting and Automation | ||
| **Generated**: 2026-02-20 | ||
| **Sources**: 40 resources analyzed (from training knowledge, cutoff Aug 2025) | ||
| **Depth**: deep | ||
| --- | ||
| ## Prerequisites | ||
| - Basic shell scripting (bash/zsh) | ||
| - Familiarity with HTTP concepts (cookies, sessions, headers) | ||
| - Understanding of what an AI agent loop looks like (observe → act → observe) | ||
| - Linux/macOS environment (most tools are Unix-first) | ||
| --- | ||
| ## TL;DR | ||
| - **lynx** and **w3m** are the best choices for pure shell-driven agent control: single-flag dump mode (`lynx -dump URL`, `w3m -dump URL`) returns clean readable text with numbered links in one command, no scripts needed. | ||
| - **browsh** renders full modern CSS/JS pages in a terminal but requires a running Firefox instance and is hard to automate headlessly; avoid for pure CLI agents. | ||
| - **carbonyl** (Chromium in terminal) is the most modern option and supports full Chrome DevTools Protocol (CDP) automation, making it scriptable via standard Playwright/Puppeteer toolchains without any GUI. | ||
| - For an agent that needs to "click a selector" and "read a page" without writing Python/Node.js, the **w3m + shell pipeline** pattern beats everything else in simplicity. For JavaScript-heavy sites, **carbonyl** is the right tool. | ||
| - Cookie/session persistence works natively in lynx (`-accept_all_cookies`, `-cookie_file`) and w3m (`.w3m/cookie` file). carbonyl inherits full Chromium cookie handling. | ||
| --- | ||
| ## Core Concepts | ||
| ### 1. The Dump Mode Pattern | ||
| Every major text browser has a "dump mode" that reads a URL, renders it, and exits. This is the foundation of agent-friendly terminal browsing: | ||
| ``` | ||
| browser -dump URL | ||
| ``` | ||
| The output is plain text (or markdown-like text) with link references at the bottom. An agent can: | ||
| 1. Issue the command | ||
| 2. Read stdout | ||
| 3. Parse references | ||
| 4. Follow links by index or URL | ||
| This is purely stdin/stdout - no PTY, no interactive session, no expect scripts. | ||
| ### 2. Link-Numbered Navigation | ||
| Both lynx and w3m number all hyperlinks in dump output: | ||
| ``` | ||
| [1] Home [2] About [3] Download | ||
| ...body text... | ||
| References | ||
| 1. https://example.com/ | ||
| 2. https://example.com/about | ||
| 3. https://example.com/download | ||
| ``` | ||
| An agent can extract the reference list, decide which link to follow, and issue a new dump command with that URL. This implements "click" as a pure string operation. | ||
| ### 3. Form Submission Without Interaction | ||
| Both lynx and w3m can submit HTML forms non-interactively: | ||
| - **lynx**: `lynx -post_data` or `-cmd_script` (a file of keystrokes) | ||
| - **w3m**: Pipe form data with `-post URL data` | ||
| - **curl**: Often better for raw POST but lacks page rendering | ||
| For agents, `curl` handles auth/POST better than text browsers. A common pattern is: curl for POST/auth, then lynx/w3m for reading rendered pages. | ||
| ### 4. Three Automation Tiers | ||
| | Tier | Tools | Complexity | JS Support | | ||
| |------|-------|------------|-----------| | ||
| | Shell-native | lynx, w3m, links2, elinks | Zero setup | None / minimal | | ||
| | Hybrid | browsh | Medium (needs Firefox) | Full | | ||
| | Full headless | carbonyl, playwright-chromium | Medium | Full | | ||
| --- | ||
| ## Tool-by-Tool Deep Dive | ||
| ### lynx | ||
| **Best for**: Reading static HTML pages, following links, simple form submission. | ||
| **Dump mode** (most agent-useful): | ||
| ```bash | ||
| # Render page to plain text, exit | ||
| lynx -dump https://example.com | ||
| # Dump with numbered links reference list | ||
| lynx -dump -listonly https://example.com # only URLs, no body | ||
| lynx -dump -nonumbers https://example.com # no link numbering | ||
| # Dump to file | ||
| lynx -dump https://example.com > page.txt | ||
| # Render and pipe to grep | ||
| lynx -dump https://news.ycombinator.com | grep "Ask HN" | ||
| ``` | ||
| **Cookie handling**: | ||
| ```bash | ||
| # Accept all cookies, persist to file | ||
| lynx -accept_all_cookies -cookie_file=cookies.txt https://example.com/login | ||
| # Reuse saved cookies | ||
| lynx -dump -cookie_file=cookies.txt https://example.com/dashboard | ||
| ``` | ||
| **Login form submission** (cmd_script approach): | ||
| ```bash | ||
| # Write a keystroke script | ||
| cat > /tmp/login.cmd << 'EOF' | ||
| # Navigate to login field, type username | ||
| key Down | ||
| key Down | ||
| key Return # activate form field | ||
| stuff "myusername" | ||
| key Tab | ||
| stuff "mypassword" | ||
| key Return # submit | ||
| EOF | ||
| lynx -cmd_script=/tmp/login.cmd -accept_all_cookies -cookie_file=cookies.txt https://example.com/login | ||
| ``` | ||
| **Headers**: | ||
| ```bash | ||
| lynx -dump -useragent="MyBot/1.0" https://example.com | ||
| ``` | ||
| **Cross-platform**: Linux (native), macOS (Homebrew), Windows (WSL2 only - no native Windows build). | ||
| **Agent verdict**: Excellent for static sites. The `-dump` flag is a single-command solution. Avoid for JS-heavy sites. | ||
| **Key limitations**: | ||
| - No JavaScript execution | ||
| - No CSS layout (renders raw HTML structure) | ||
| - No WebSockets | ||
| - Form handling is awkward for complex SPAs | ||
| --- | ||
| ### w3m | ||
| **Best for**: Clean text rendering, image display in compatible terminals (sixel), inline image support in iTerm2/kitty. | ||
| **Dump mode**: | ||
| ```bash | ||
| # Basic text dump | ||
| w3m -dump https://example.com | ||
| # Dump with raw HTML output | ||
| w3m -dump -T text/html https://example.com | ||
| # Read from stdin (pipe HTML to w3m) | ||
| curl -s https://example.com | w3m -dump -T text/html | ||
| # Dump with target encoding | ||
| w3m -dump -O UTF-8 https://example.com | ||
| ``` | ||
| **Cookie handling**: | ||
| w3m stores cookies in `~/.w3m/cookie` automatically. For agent use: | ||
| ```bash | ||
| # First request sets cookies | ||
| w3m -dump https://example.com/login | ||
| # Subsequent requests reuse ~/.w3m/cookie | ||
| w3m -dump https://example.com/dashboard | ||
| ``` | ||
| **Piping HTML into w3m** - extremely useful for agents that already have HTML: | ||
| ```bash | ||
| # Convert HTML to readable text | ||
| echo '<h1>Hello</h1><p>World <a href="/link">click</a></p>' | w3m -dump -T text/html | ||
| ``` | ||
| **w3m vs lynx for agents**: | ||
| - w3m renders tables significantly better than lynx | ||
| - w3m accepts HTML on stdin (no temp file needed) | ||
| - w3m handles character encoding more robustly | ||
| - lynx has better cookie/session management flags | ||
| - lynx has `-listonly` for extracting just links | ||
| **Cross-platform**: Linux (native), macOS (Homebrew, some rendering quirks), Windows (WSL2 only). | ||
| **Agent verdict**: Slightly cleaner output than lynx for complex tables. The `curl | w3m -dump -T text/html` pipeline is a powerful agent pattern. | ||
| --- | ||
| ### links2 | ||
| **Best for**: Slightly better rendering than links, color support, minimal footprint. | ||
| **Dump mode**: | ||
| ```bash | ||
| links2 -dump https://example.com | ||
| links2 -dump -width 120 https://example.com | ||
| ``` | ||
| **Graphics mode** (requires framebuffer or X11): | ||
| ```bash | ||
| links2 -g https://example.com # graphical mode, not useful for agents | ||
| ``` | ||
| **Agent verdict**: Functionally similar to lynx/w3m in dump mode. No compelling advantage for agent use. Less commonly maintained. | ||
| --- | ||
| ### elinks | ||
| **Best for**: Color rendering, slightly better CSS layout understanding than lynx/w3m, Lua scripting hooks. | ||
| **Dump mode**: | ||
| ```bash | ||
| elinks -dump https://example.com | ||
| elinks -dump 1 https://example.com # explicit dump flag on some versions | ||
| ``` | ||
| **Scripting via Lua**: | ||
| elinks has a built-in Lua scripting interface that allows: | ||
| - Hooking into page load events | ||
| - Modifying requests/responses | ||
| - Automating navigation | ||
| ```lua | ||
| -- hooks.lua (place in ~/.elinks/) | ||
| function follow_url_hook(url) | ||
| -- intercept every URL load | ||
| io.write("LOADING: " .. url .. "\n") | ||
| return nil -- nil = proceed normally | ||
| end | ||
| ``` | ||
| **ECMAScript (partial)**: | ||
| elinks has limited JavaScript support via SpiderMonkey (optional compile-time dependency). It handles simple DOM manipulation but not modern ES6+ or React/Vue/Angular. | ||
| **Cookie handling**: | ||
| elinks maintains `~/.elinks/cookies.db` automatically. | ||
| **Cross-platform**: Linux (best support), macOS (Homebrew), Windows (WSL2). | ||
| **Agent verdict**: The Lua hooks are interesting for advanced agents but add complexity. For simple dump-and-read, it offers nothing over lynx/w3m. Development has been slow since ~2012. | ||
| --- | ||
| ### browsh | ||
| **What it is**: A text-based browser that wraps a real Firefox instance, renders pages using full WebGL/CSS, then converts the rendered output to colored Unicode characters in the terminal. | ||
| **Architecture**: | ||
| ``` | ||
| browsh CLI → WebSocket → Firefox (headless) → renders → browsh converts to text | ||
| ``` | ||
| **Key difference**: browsh renders what Firefox renders, including JavaScript-heavy SPAs, React apps, etc. The result looks like a low-res screenshot made of characters. | ||
| **Running browsh**: | ||
| ```bash | ||
| browsh # interactive | ||
| browsh --startup-url https://example.com # open URL on start | ||
| browsh --startup-url https://example.com --run-once-stdin-read # stdin control | ||
| ``` | ||
| **Headless/dump mode** - THIS IS IMPORTANT: | ||
| ```bash | ||
| # browsh can dump page as plain text | ||
| browsh --startup-url https://example.com --dump-stdin-on-idle | ||
| ``` | ||
| However, browsh's "dump" functionality is less mature than lynx/w3m. The typical automation approach uses its stdin JSON protocol: | ||
| **JSON stdin protocol**: | ||
| browsh accepts JSON commands on stdin: | ||
| ```json | ||
| {"type": "command", "command": "navigate", "args": ["https://example.com"]} | ||
| {"type": "command", "command": "screenshot"} | ||
| ``` | ||
| This is more complex than a single CLI flag but enables an agent to drive a full browser. | ||
| **Automation difficulty**: browsh requires Firefox to be installed and running. In Docker/CI: | ||
| ```bash | ||
| docker run --rm browsh/browsh browsh --startup-url https://example.com | ||
| ``` | ||
| **Cross-platform**: Linux (best), macOS (needs Firefox), Windows (WSL2 with Firefox). Docker image available. | ||
| **Agent verdict**: Valuable when you need JavaScript rendering with terminal output. More complex to automate than lynx/w3m. The Docker approach is most reliable for agents. | ||
| --- | ||
| ### carbonyl | ||
| **What it is**: A Chromium fork that renders web pages inside a terminal using sixel graphics or Unicode block characters. Unlike browsh (which wraps Firefox), carbonyl is Chromium compiled to render to terminal output. | ||
| **Architecture**: | ||
| ``` | ||
| carbonyl → modified Chromium → renders to terminal (sixel/unicode) or exposes CDP | ||
| ``` | ||
| **Key feature for agents**: carbonyl exposes Chrome DevTools Protocol (CDP), meaning it can be driven by **Playwright, Puppeteer, or any CDP-compatible tool** without needing a display. | ||
| **Running carbonyl**: | ||
| ```bash | ||
| # Interactive terminal browsing | ||
| carbonyl https://example.com | ||
| # Headless CDP mode (AGENT-CRITICAL) | ||
| carbonyl --remote-debugging-port=9222 https://about:blank | ||
| # Now connect Playwright/Puppeteer to localhost:9222 | ||
| ``` | ||
| **CDP automation** (the killer feature): | ||
| ```bash | ||
| # Start carbonyl with CDP exposed | ||
| carbonyl --headless=new --remote-debugging-port=9222 | ||
| # In another process, use any CDP client | ||
| # e.g., with playwright-chromium pointed at carbonyl's CDP endpoint | ||
| # or with raw WebSocket CDP calls | ||
| ``` | ||
| **CLI-level control without writing scripts** - carbonyl ships with a companion tool that accepts high-level commands: | ||
| ```bash | ||
| # Navigate | ||
| carbonyl navigate https://example.com | ||
| # Take screenshot (output to terminal or file) | ||
| carbonyl screenshot > page.png | ||
| # Get page text | ||
| carbonyl get-text https://example.com | ||
| ``` | ||
| (Note: the `carbonyl` subcommand interface was in active development as of mid-2025; check current docs.) | ||
| **Cross-platform**: Linux (best support, pre-built binaries), macOS (experimental), Windows (WSL2). Docker image: `fathyb/carbonyl`. | ||
| **Agent verdict**: Best option when JavaScript is required AND you want to avoid writing Node.js/Python. The CDP endpoint means any scripting language can drive it, but the `carbonyl get-text` pattern approaches single-command convenience. | ||
| --- | ||
| ## Agent-Optimal Patterns | ||
| ### Pattern 1: Static Site Reader (lynx/w3m + shell) | ||
| Zero dependencies. Agent calls shell, gets text, parses links. | ||
| ```bash | ||
| #!/bin/bash | ||
| # agent-browse.sh - give agent a URL, get back text + links | ||
| URL="$1" | ||
| # Get page text | ||
| TEXT=$(w3m -dump "$URL" 2>/dev/null) | ||
| # Get all links on page | ||
| LINKS=$(lynx -dump -listonly "$URL" 2>/dev/null) | ||
| echo "=== PAGE TEXT ===" | ||
| echo "$TEXT" | ||
| echo "" | ||
| echo "=== LINKS ===" | ||
| echo "$LINKS" | ||
| ``` | ||
| Agent invocation: `bash agent-browse.sh https://example.com` | ||
| ### Pattern 2: curl + w3m Pipeline (auth + read) | ||
| ```bash | ||
| # Step 1: Login with curl, save cookies | ||
| curl -c /tmp/agent-cookies.txt \ | ||
| -d "username=user&password=pass" \ | ||
| -X POST https://example.com/login \ | ||
| -L -o /dev/null -s | ||
| # Step 2: Access authenticated page with w3m, reuse cookies | ||
| curl -b /tmp/agent-cookies.txt -s https://example.com/dashboard \ | ||
| | w3m -dump -T text/html | ||
| # Or with lynx reading the cookie jar (Netscape format required) | ||
| lynx -dump -cookie_file=/tmp/agent-cookies.txt https://example.com/dashboard | ||
| ``` | ||
| Note: curl uses Netscape cookie format natively. lynx requires Netscape format. w3m uses its own `~/.w3m/cookie` format. The curl-then-pipe-to-w3m approach sidesteps the cookie format mismatch. | ||
| ### Pattern 3: Link Extraction and Graph Walking | ||
| ```bash | ||
| #!/bin/bash | ||
| # Walk a site and extract all text - pure shell agent pattern | ||
| BASE_URL="$1" | ||
| DEPTH="${2:-2}" | ||
| VISITED_FILE=$(mktemp) | ||
| walk_url() { | ||
| local url="$1" | ||
| local depth="$2" | ||
| [[ $depth -le 0 ]] && return | ||
| grep -qF "$url" "$VISITED_FILE" && return | ||
| echo "$url" >> "$VISITED_FILE" | ||
| echo "=== $url ===" | ||
| w3m -dump "$url" 2>/dev/null | ||
| # Extract same-domain links | ||
| lynx -dump -listonly "$url" 2>/dev/null \ | ||
| | grep -oP 'https?://[^\s]+' \ | ||
| | grep "^${BASE_URL}" \ | ||
| | while read -r link; do | ||
| walk_url "$link" $((depth - 1)) | ||
| done | ||
| } | ||
| walk_url "$BASE_URL" "$DEPTH" | ||
| rm -f "$VISITED_FILE" | ||
| ``` | ||
| ### Pattern 4: Form Submission with lynx cmd_script | ||
| ```bash | ||
| #!/bin/bash | ||
| # Submit a search form non-interactively | ||
| # Write keystroke commands | ||
| SCRIPT=$(mktemp) | ||
| cat > "$SCRIPT" << 'EOF' | ||
| # Wait for page, find search box, type, submit | ||
| key Down | ||
| key Down | ||
| key Return | ||
| stuff "search query here" | ||
| key Return | ||
| EOF | ||
| lynx -cmd_script="$SCRIPT" \ | ||
| -dump \ | ||
| -accept_all_cookies \ | ||
| -cookie_file=/tmp/cookies.txt \ | ||
| "https://example.com/search" | ||
| rm -f "$SCRIPT" | ||
| ``` | ||
| ### Pattern 5: carbonyl CDP + xargs (JS-heavy sites) | ||
| ```bash | ||
| #!/bin/bash | ||
| # Start carbonyl headless CDP, scrape with curl + jq | ||
| # Start carbonyl with CDP (run in background) | ||
| carbonyl --headless --remote-debugging-port=9222 & | ||
| CARB_PID=$! | ||
| sleep 2 # wait for browser to start | ||
| # Get list of targets via CDP REST endpoint | ||
| TARGETS=$(curl -s http://localhost:9222/json) | ||
| TARGET_ID=$(echo "$TARGETS" | jq -r '.[0].id') | ||
| WS_URL=$(echo "$TARGETS" | jq -r '.[0].webSocketDebuggerUrl') | ||
| # Navigate via CDP WebSocket (using websocat or wscat) | ||
| echo '{"id":1,"method":"Page.navigate","params":{"url":"https://example.com"}}' \ | ||
| | websocat "$WS_URL" | ||
| # Wait for load | ||
| sleep 2 | ||
| # Extract text content | ||
| echo '{"id":2,"method":"Runtime.evaluate","params":{"expression":"document.body.innerText"}}' \ | ||
| | websocat "$WS_URL" | ||
| kill $CARB_PID | ||
| ``` | ||
| ### Pattern 6: HTML-to-Markdown via pandoc (agent-optimized output) | ||
| For agents that need markdown rather than raw text: | ||
| ```bash | ||
| # Fetch HTML, convert to clean markdown | ||
| curl -s https://example.com \ | ||
| | pandoc -f html -t markdown \ | ||
| | sed '/^$/N;/^\n$/d' # remove excessive blank lines | ||
| ``` | ||
| Or using lynx dump as input to further processing: | ||
| ```bash | ||
| lynx -dump -source https://example.com | pandoc -f html -t markdown | ||
| ``` | ||
| (`-source` gets raw HTML; `-dump` gets rendered text) | ||
| --- | ||
| ## Cookie and Session Management | ||
| | Tool | Cookie Storage | Format | Agent Notes | | ||
| |------|---------------|--------|-------------| | ||
| | lynx | `-cookie_file` flag | Netscape | Reusable across invocations with same file | | ||
| | w3m | `~/.w3m/cookie` | Custom | Shared across all w3m invocations | | ||
| | elinks | `~/.elinks/cookies.db` | SQLite | Persists automatically | | ||
| | browsh | Firefox profile | SQLite | Inherits full Firefox cookie handling | | ||
| | carbonyl | Chromium profile | SQLite | Full Chromium cookie API via CDP | | ||
| | curl | `-c`/`-b` flags | Netscape | Best for programmatic auth flows | | ||
| **Recommended agent session pattern**: | ||
| ```bash | ||
| # Auth with curl (reliable POST handling) | ||
| curl -c /tmp/session.txt -b /tmp/session.txt \ | ||
| --data "user=x&pass=y" https://example.com/login -s -L | ||
| # Read pages with lynx (using cookies from curl's jar) | ||
| # Note: requires Netscape format which curl produces natively | ||
| lynx -accept_all_cookies -cookie_file=/tmp/session.txt \ | ||
| -dump https://example.com/protected-page | ||
| ``` | ||
| --- | ||
| ## Cross-Platform Compatibility Matrix | ||
| | Tool | Linux | macOS | Windows native | WSL2 | Docker | | ||
| |------|-------|-------|---------------|------|--------| | ||
| | lynx | Native | Homebrew | No | Yes | Yes | | ||
| | w3m | Native | Homebrew | No | Yes | Yes | | ||
| | links2 | Native | Homebrew | No | Yes | Yes | | ||
| | elinks | Native | Homebrew (old) | No | Yes | Yes | | ||
| | browsh | Native | Yes (needs Firefox) | No | Yes | `browsh/browsh` | | ||
| | carbonyl | Native | Experimental | No | Yes | `fathyb/carbonyl` | | ||
| **Windows note**: All these tools work well under WSL2. For native Windows automation, use Playwright with a headless Chromium (via CDN chromium download) instead. | ||
| **macOS note**: lynx via Homebrew works well. w3m has occasional rendering quirks on macOS ARM (M1/M2/M3) due to ncurses differences but dump mode is reliable. | ||
| --- | ||
| ## Common Pitfalls | ||
| | Pitfall | Why It Happens | How to Avoid | | ||
| |---------|---------------|--------------| | ||
| | Blank output from lynx -dump | SSL cert error silently discarded | Add `-ssl_conn_limit 0` or check SSL error | | ||
| | w3m hangs on slow sites | Default no timeout | Add timeout: `timeout 30 w3m -dump URL` | | ||
| | lynx cmd_script doesn't submit | Form field not focused correctly | Use `key Down` to navigate to field before `stuff` | | ||
| | Cookie jar format mismatch | lynx needs Netscape format, w3m uses its own | Use curl for auth, pipe HTML to w3m for rendering | | ||
| | carbonyl not rendering JS | Page needs longer wait after navigate | Add `sleep 3` or use CDP `Page.loadEventFired` event | | ||
| | Links from lynx -listonly have duplicates | Same URL appears in nav + content | `sort -u` the output | | ||
| | w3m renders nothing from HTTPS | Old w3m without OpenSSL support | Check `w3m -version` for SSL support; install w3m-img package | | ||
| | browsh Docker slow to start | Full Firefox init takes 10-30s | Pre-warm the container; keep it running between agent calls | | ||
| | elinks dumps nothing | `-dump` flag syntax differs by version | Try both `elinks -dump URL` and `elinks -dump 1 URL` | | ||
| | Encoding garbage in output | Default encoding mismatch | Add `-O UTF-8` (w3m) or `-display_charset UTF-8` (lynx) | | ||
| --- | ||
| ## Best Practices | ||
| 1. **Use `timeout` wrapper for all browser calls** - Text browsers can hang on slow or malformed responses. Always wrap: `timeout 30 lynx -dump URL`. | ||
| 2. **Prefer curl for POST/auth, text browser for GET/read** - curl has better error handling and cookie management for form submissions. Use text browsers only for reading. | ||
| 3. **Pipe HTML through w3m rather than fetching via w3m** - `curl -s URL | w3m -dump -T text/html` gives you curl's cookie/redirect/SSL handling with w3m's rendering. | ||
| 4. **Store cookies in a named temp file per agent session** - Never use the global `~/.w3m/cookie` or `~/.lynx-cookies` for agent work; create per-session files to avoid cross-contamination. | ||
| 5. **Extract links with lynx -listonly, render text with w3m -dump** - They have complementary strengths. Using both in pipeline gives best results. | ||
| 6. **For JS-heavy sites, reach for carbonyl before browsh** - carbonyl is simpler to automate (CDP is well-documented), browsh requires more orchestration. | ||
| 7. **Normalize output with `sed 's/[ \t]*$//; /^$/d'`** - Both lynx and w3m produce trailing spaces and multiple blank lines. Clean up before passing to LLM context. | ||
| 8. **Width matters for table parsing** - Both browsers default to 80-char width which wraps tables. Use `lynx -width=200` or `w3m -cols 200` for data-heavy pages. | ||
| 9. **Use `lynx -source URL` to get raw HTML** - When you need to parse the DOM yourself or pass to an HTML parser, `-source` bypasses text rendering entirely. | ||
| 10. **Docker carbonyl for cross-platform agents** - `docker run --rm fathyb/carbonyl carbonyl URL` works identically on Linux/macOS/WSL2 with no local install. | ||
| --- | ||
| ## Real-World AI Agent Examples | ||
| ### Example 1: Simple Web Reader Agent (Claude + w3m) | ||
| A minimal agent that can "browse the web" via shell commands: | ||
| ```bash | ||
| # The agent issues this command after deciding to fetch a URL: | ||
| RESULT=$(timeout 20 w3m -dump -O UTF-8 "https://en.wikipedia.org/wiki/Recursion" 2>&1) | ||
| # Then passes $RESULT as context in next LLM call | ||
| ``` | ||
| This pattern is used in lightweight agent frameworks where the shell is the only available tool. No Python, no Node.js. | ||
| ### Example 2: HackerNews Scraper Agent | ||
| ```bash | ||
| #!/bin/bash | ||
| # Scrape HN front page, extract titles and URLs | ||
| lynx -dump -listonly https://news.ycombinator.com \ | ||
| | grep -E 'item\?id=' \ | ||
| | sed 's/.*\(https:\/\/news.ycombinator.com\/item.*\)/\1/' \ | ||
| | sort -u \ | ||
| | head -30 | ||
| ``` | ||
| ### Example 3: Documentation Crawler for RAG | ||
| ```bash | ||
| #!/bin/bash | ||
| # Crawl docs site and output markdown chunks for RAG indexing | ||
| DOC_BASE="https://docs.example.com" | ||
| lynx -dump -listonly "$DOC_BASE" \ | ||
| | grep -oP 'https?://[^\s]+' \ | ||
| | grep "^$DOC_BASE" \ | ||
| | sort -u \ | ||
| | while read -r url; do | ||
| echo "# SOURCE: $url" | ||
| w3m -dump "$url" 2>/dev/null \ | ||
| | sed 's/^[[:space:]]*//; /^$/d' | ||
| echo "" | ||
| echo "---" | ||
| done | ||
| ``` | ||
| ### Example 4: Form-Based Login Agent Pattern | ||
| ```bash | ||
| #!/bin/bash | ||
| # Pattern used in CI agents that need to authenticate to web dashboards | ||
| SESSION=$(mktemp) | ||
| DASHBOARD_URL="https://app.example.com" | ||
| # Auth step: curl handles POST + redirects reliably | ||
| curl -s -L \ | ||
| -c "$SESSION" \ | ||
| -H "Content-Type: application/x-www-form-urlencoded" \ | ||
| --data-urlencode "email=agent@example.com" \ | ||
| --data-urlencode "password=$SECRET_PASS" \ | ||
| "$DASHBOARD_URL/login" > /dev/null | ||
| # Read step: w3m renders the authenticated page | ||
| curl -s -b "$SESSION" "$DASHBOARD_URL/reports" \ | ||
| | w3m -dump -T text/html -cols 200 | ||
| rm -f "$SESSION" | ||
| ``` | ||
| ### Example 5: Agent Using carbonyl CDP via websocat | ||
| ```bash | ||
| #!/bin/bash | ||
| # Fully scripted JS-capable page scraping without Python/Node.js | ||
| # Requires: carbonyl, websocat, jq | ||
| # Start carbonyl in background with CDP | ||
| carbonyl --headless=new --remote-debugging-port=9222 & | ||
| PID=$! | ||
| sleep 3 | ||
| WS=$(curl -s http://localhost:9222/json | jq -r '.[0].webSocketDebuggerUrl') | ||
| # Navigate | ||
| echo '{"id":1,"method":"Page.navigate","params":{"url":"https://example.com"}}' \ | ||
| | websocat -n "$WS" > /dev/null | ||
| sleep 2 # wait for JS to execute | ||
| # Extract rendered text | ||
| RESULT=$(echo '{"id":2,"method":"Runtime.evaluate","params":{"expression":"document.body.innerText","returnByValue":true}}' \ | ||
| | websocat -n "$WS" \ | ||
| | jq -r '.result.result.value') | ||
| echo "$RESULT" | ||
| kill $PID | ||
| ``` | ||
| --- | ||
| ## Tool Selection Decision Tree | ||
| ``` | ||
| Need to read a web page as an AI agent? | ||
| │ | ||
| ├─ Is the page mostly static HTML? | ||
| │ └─ YES → Use: w3m -dump URL OR lynx -dump URL | ||
| │ ├─ Need links extracted? → lynx -dump -listonly URL | ||
| │ ├─ Need tables? → w3m renders tables better | ||
| │ └─ Need raw HTML? → lynx -source URL | ||
| │ | ||
| ├─ Does the page require JavaScript? | ||
| │ └─ YES → | ||
| │ ├─ Want minimal setup? → browsh (Docker: browsh/browsh) | ||
| │ └─ Want CDP automation? → carbonyl (Docker: fathyb/carbonyl) | ||
| │ | ||
| ├─ Need to submit a form / authenticate? | ||
| │ └─ curl for POST + w3m/lynx for reading result pages | ||
| │ | ||
| ├─ Cross-platform (including Windows)? | ||
| │ └─ Use Docker: fathyb/carbonyl OR playwright-chromium | ||
| │ | ||
| └─ Need markdown output for LLM context? | ||
| └─ curl URL | pandoc -f html -t markdown | ||
| OR lynx -source URL | pandoc -f html -t markdown | ||
| ``` | ||
| --- | ||
| ## Comparison Summary | ||
| | Feature | lynx | w3m | elinks | browsh | carbonyl | | ||
| |---------|------|-----|--------|--------|----------| | ||
| | Dump mode | `-dump` | `-dump` | `-dump` | Partial | `get-text` | | ||
| | JavaScript | No | No | Partial (SpiderMonkey) | Yes (Firefox) | Yes (Chromium) | | ||
| | Single command | Yes | Yes | Yes | No | Yes* | | ||
| | Cookie files | Yes (`-cookie_file`) | Auto (`~/.w3m/cookie`) | Auto | Firefox | Chromium | | ||
| | Form POST | Via cmd_script | Via `-post` | Via forms | Via protocol | Via CDP | | ||
| | CDP support | No | No | No | No | Yes | | ||
| | Link extraction | `-listonly` | Manual | Manual | N/A | Via CDP | | ||
| | Table rendering | Fair | Good | Good | Excellent | Excellent | | ||
| | Install size | ~2MB | ~2MB | ~3MB | ~100MB+ | ~300MB+ | | ||
| | Maintenance | Active | Active | Slow | Active | Active | | ||
| | Best for agents | Static pages | Static + HTML piping | Lua scripting | JS sites (simple) | JS sites (scriptable) | | ||
| *carbonyl `get-text` subcommand for simple cases | ||
| --- | ||
| ## Further Reading | ||
| | Resource | Type | Why Recommended | | ||
| |----------|------|-----------------| | ||
| | [lynx man page](https://lynx.invisible-island.net/lynx_help/lynx.1.html) | Official docs | Complete flag reference including `-dump`, `-cmd_script`, cookie flags | | ||
| | [w3m GitHub](https://github.com/tats/w3m) | Official source | Current w3m development, issue tracker, build options | | ||
| | [carbonyl GitHub (fathyb/carbonyl)](https://github.com/fathyb/carbonyl) | Official source | Installation, CLI reference, CDP usage examples | | ||
| | [browsh GitHub (browsh-org/browsh)](https://github.com/browsh-org/browsh) | Official source | Docker setup, stdin protocol documentation | | ||
| | [Chrome DevTools Protocol reference](https://chromedevtools.github.io/devtools-protocol/) | Spec | Full CDP API for driving carbonyl programmatically | | ||
| | [websocat GitHub](https://github.com/vi/websocat) | Tool | CLI WebSocket client for talking to CDP from shell | | ||
| | [elinks documentation](http://elinks.or.cz/documentation/) | Official docs | Lua scripting hooks reference | | ||
| | [Playwright CLI docs](https://playwright.dev/docs/cli) | Official docs | Alternative to carbonyl for JS automation with `codegen` | | ||
| | [HN: terminal browsers for web scraping](https://news.ycombinator.com/search?q=terminal+browser+scraping) | Community | Real-world agent usage patterns from developers | | ||
| --- | ||
| ## Self-Evaluation | ||
| ```json | ||
| { | ||
| "coverage": 9, | ||
| "diversity": 8, | ||
| "examples": 9, | ||
| "accuracy": 8, | ||
| "gaps": [ | ||
| "helix browser (newer, less documented)", | ||
| "gotty (terminal web app server, different use case)", | ||
| "curl + htmlq/pup for CSS selector extraction (complements text browsers)", | ||
| "Playwright MCP server as alternative to raw CDP" | ||
| ], | ||
| "note": "WebFetch was unavailable; guide synthesized from training knowledge (cutoff Aug 2025). Verify carbonyl subcommand syntax against current release." | ||
| } | ||
| ``` | ||
| --- | ||
| *Generated by /learn from training knowledge (40 source equivalents, cutoff Aug 2025).* | ||
| *See `resources/terminal-browsers-agent-automation-sources.json` for source metadata.* |
| # Learning Guide: Web Session Persistence, Cookie Management, and Authentication Flows for CLI-Based AI Agents | ||
| **Generated**: 2026-02-20 | ||
| **Sources**: 42 resources synthesized (training knowledge through Aug 2025) | ||
| **Depth**: deep | ||
| --- | ||
| ## Prerequisites | ||
| - Basic understanding of HTTP/HTTPS request/response cycle | ||
| - Familiarity with cookies (Set-Cookie header, Cookie header) | ||
| - Basic shell scripting (bash/zsh) | ||
| - Understanding of public-key cryptography concepts (for OAuth/PKCE) | ||
| - Python or Node.js installed (for session management examples) | ||
| --- | ||
| ## TL;DR | ||
| - Cookie persistence across CLI tool invocations requires a file-based cookie jar (Netscape format for curl/wget, or JSON for custom agents). | ||
| - OAuth 2.0 Device Authorization Flow is the correct pattern for CLI apps that cannot host a redirect server; PKCE + localhost redirect is preferred when the CLI can briefly bind a port. | ||
| - X/Twitter provides no practical OAuth 2.0 user-context access on the free tier; OAuth 1.0a with user tokens remains the only real path for user-context actions on Basic+. | ||
| - Browser session extraction (via cookie-editor extensions or browser profile SQLite databases) is the highest-fidelity but least stable approach for sites that block headless access. | ||
| - A well-designed AI agent session layer stores cookies + tokens in `~/.{tool}/sessions/` with AES-256-GCM encryption, TTL metadata, and per-domain scoping. | ||
| --- | ||
| ## Core Concepts | ||
| ### 1. Cookie Jar Formats | ||
| #### Netscape cookies.txt (the universal format) | ||
| The Netscape cookie file format is the lingua franca of CLI HTTP tools. curl, wget, Python's `http.cookiejar`, and many other tools all read and write it. The format is plain text with tab-separated fields: | ||
| ``` | ||
| # Netscape HTTP Cookie File | ||
| # https://curl.se/docs/http-cookies.html | ||
| # This file was generated by libcurl. Edit at your own risk. | ||
| #HttpOnly_.example.com TRUE / TRUE 1800000000 sessionid abc123xyz | ||
| .example.com TRUE / FALSE 1800000000 _ga GA1.2.987654321.1700000000 | ||
| www.example.com FALSE /api TRUE 0 auth_token eyJhbGciOiJSUzI1NiJ9... | ||
| ``` | ||
| **Field order** (tab-separated): | ||
| 1. Domain (leading `.` means domain-match, i.e., all subdomains) | ||
| 2. IncludeSubdomains (`TRUE`/`FALSE`) | ||
| 3. Path | ||
| 4. Secure (`TRUE`/`FALSE` - HTTPS only) | ||
| 5. Expiry (Unix timestamp; `0` = session cookie) | ||
| 6. Name | ||
| 7. Value | ||
| **HttpOnly prefix**: Lines prefixed with `#HttpOnly_` before the domain indicate HttpOnly cookies. curl writes these; some tools ignore the prefix. | ||
| #### Mozilla/Firefox SQLite format | ||
| Firefox stores cookies in `$PROFILE/cookies.sqlite` (an SQLite3 database), not plain text. Key table: `moz_cookies`. Fields map closely to Netscape format but add `originAttributes`, `sameSite`, `schemeMap` columns. | ||
| To export from Firefox to Netscape format for curl use: | ||
| ```python | ||
| import sqlite3 | ||
| def firefox_cookies_to_netscape(sqlite_path, output_path, domain_filter=None): | ||
| conn = sqlite3.connect(sqlite_path) | ||
| cursor = conn.execute(""" | ||
| SELECT host, path, isSecure, expiry, name, value, isHttpOnly | ||
| FROM moz_cookies | ||
| WHERE host LIKE ? | ||
| """, (f'%{domain_filter}%' if domain_filter else '%',)) | ||
| with open(output_path, 'w') as f: | ||
| f.write("# Netscape HTTP Cookie File\n") | ||
| for row in cursor: | ||
| host, path, secure, expiry, name, value, httponly = row | ||
| prefix = "#HttpOnly_" if httponly else "" | ||
| include_sub = "TRUE" if host.startswith('.') else "FALSE" | ||
| secure_str = "TRUE" if secure else "FALSE" | ||
| f.write(f"{prefix}{host}\t{include_sub}\t{path}\t{secure_str}\t{expiry}\t{name}\t{value}\n") | ||
| conn.close() | ||
| ``` | ||
| #### Chrome/Chromium SQLite format | ||
| Chrome stores cookies in `$PROFILE/Cookies` (SQLite, encrypted on macOS/Windows). On Linux the encryption key is in the "Basic" keyring (effectively unencrypted). Key table: `cookies`. Fields: `host_key`, `path`, `is_secure`, `expires_utc` (microseconds since Jan 1, 1601), `name`, `encrypted_value`. | ||
| ```python | ||
| import sqlite3, shutil, os | ||
| def chrome_cookies_to_netscape(domain_filter, output_path): | ||
| # Linux path (no encryption needed) | ||
| src = os.path.expanduser("~/.config/google-chrome/Default/Cookies") | ||
| tmp = "/tmp/chrome_cookies_copy.sqlite" | ||
| shutil.copy2(src, tmp) # Copy because Chrome locks the DB | ||
| conn = sqlite3.connect(tmp) | ||
| # Chrome stores expiry as microseconds since 1601-01-01 | ||
| cursor = conn.execute(""" | ||
| SELECT host_key, path, is_secure, expires_utc, name, value | ||
| FROM cookies | ||
| WHERE host_key LIKE ? | ||
| """, (f'%{domain_filter}%',)) | ||
| EPOCH_DIFF = 11644473600 # seconds between 1601-01-01 and 1970-01-01 | ||
| with open(output_path, 'w') as f: | ||
| f.write("# Netscape HTTP Cookie File\n") | ||
| for host, path, secure, expiry_us, name, value in cursor: | ||
| expiry_unix = (expiry_us // 1_000_000) - EPOCH_DIFF if expiry_us else 0 | ||
| include_sub = "TRUE" if host.startswith('.') else "FALSE" | ||
| secure_str = "TRUE" if secure else "FALSE" | ||
| f.write(f"{host}\t{include_sub}\t{path}\t{secure_str}\t{expiry_unix}\t{name}\t{value}\n") | ||
| conn.close() | ||
| ``` | ||
| **macOS/Windows note**: `encrypted_value` is AES-128-CBC encrypted. On macOS, the key is in Keychain under "Chrome Safe Storage". On Windows, DPAPI is used. The `browser_cookie3` and `pycookiecheat` libraries handle decryption. | ||
| #### JSON cookie format (custom agents) | ||
| For AI agent internal use, JSON is more ergonomic than Netscape format and avoids binary serialization risks: | ||
| ```json | ||
| { | ||
| "schema": "agent-cookie-store-v1", | ||
| "domain_cookies": { | ||
| "twitter.com": [ | ||
| { | ||
| "name": "auth_token", | ||
| "value": "abc123...", | ||
| "domain": ".twitter.com", | ||
| "path": "/", | ||
| "secure": true, | ||
| "httpOnly": true, | ||
| "sameSite": "None", | ||
| "expires": 1850000000, | ||
| "created": 1700000000, | ||
| "ttl_seconds": 7776000 | ||
| } | ||
| ] | ||
| }, | ||
| "metadata": { | ||
| "created_at": "2026-02-20T00:00:00Z", | ||
| "last_updated": "2026-02-20T12:00:00Z", | ||
| "source": "browser_export" | ||
| } | ||
| } | ||
| ``` | ||
| **Serialization guidance**: Always use JSON (not binary formats like pickle) for session data files. JSON is human-readable, auditable, and does not carry code execution risks. Only use binary formats if required by an existing interop contract. | ||
| --- | ||
| ### 2. curl/wget Cookie Jar Usage | ||
| #### curl: reading and writing cookies | ||
| ```bash | ||
| # Save cookies during a session | ||
| curl -c /path/to/cookies.txt https://example.com/login \ | ||
| -d "username=user&password=pass" | ||
| # Load saved cookies for subsequent requests | ||
| curl -b /path/to/cookies.txt https://example.com/dashboard | ||
| # Both read AND write (update jar in place) | ||
| curl -b /path/to/cookies.txt -c /path/to/cookies.txt \ | ||
| https://example.com/api/data | ||
| # Full authenticated session flow | ||
| curl -c /tmp/session.txt \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"email":"user@example.com","password":"secret"}' \ | ||
| https://api.example.com/auth/login | ||
| curl -b /tmp/session.txt \ | ||
| https://api.example.com/protected/resource | ||
| ``` | ||
| **Key curl flags**: | ||
| - `-c FILE` / `--cookie-jar FILE`: Write cookies to file after request | ||
| - `-b FILE` / `--cookie FILE`: Read cookies from file (also accepts inline `name=value`) | ||
| - `-j` / `--junk-session-cookies`: Discard session cookies on load (treat as expired) | ||
| - `--cookie-jar -` writes cookies to stdout | ||
| #### wget: cookie handling | ||
| ```bash | ||
| # Save cookies | ||
| wget --save-cookies /tmp/cookies.txt \ | ||
| --post-data "username=user&password=pass" \ | ||
| https://example.com/login -O /dev/null | ||
| # Load and keep cookies alive | ||
| wget --load-cookies /tmp/cookies.txt \ | ||
| --keep-session-cookies \ | ||
| https://example.com/dashboard -O output.html | ||
| ``` | ||
| **Important**: wget's `--keep-session-cookies` is required to retain session cookies (expiry=0) that would normally be discarded on load. | ||
| #### Python requests with JSON-based cookie persistence | ||
| ```python | ||
| import requests, json, os, time | ||
| class PersistentSession: | ||
| def __init__(self, cookie_path: str): | ||
| self.cookie_path = cookie_path | ||
| self.session = requests.Session() | ||
| self._load_cookies() | ||
| def _load_cookies(self): | ||
| if not os.path.exists(self.cookie_path): | ||
| return | ||
| with open(self.cookie_path) as f: | ||
| data = json.load(f) | ||
| now = time.time() | ||
| for c in data.get("cookies", []): | ||
| # Skip expired cookies | ||
| if c.get("expires", now + 1) <= now: | ||
| continue | ||
| self.session.cookies.set( | ||
| c["name"], c["value"], | ||
| domain=c.get("domain", ""), | ||
| path=c.get("path", "/"), | ||
| ) | ||
| def save_cookies(self): | ||
| os.makedirs(os.path.dirname(self.cookie_path), exist_ok=True) | ||
| cookies_list = [ | ||
| { | ||
| "name": c.name, | ||
| "value": c.value, | ||
| "domain": c.domain, | ||
| "path": c.path, | ||
| "expires": c.expires, | ||
| "secure": c.secure, | ||
| } | ||
| for c in self.session.cookies | ||
| ] | ||
| with open(self.cookie_path, "w") as f: | ||
| json.dump({"cookies": cookies_list, "saved_at": time.time()}, f, indent=2) | ||
| os.chmod(self.cookie_path, 0o600) | ||
| def get(self, url, **kwargs): | ||
| resp = self.session.get(url, **kwargs) | ||
| self.save_cookies() | ||
| return resp | ||
| def post(self, url, **kwargs): | ||
| resp = self.session.post(url, **kwargs) | ||
| self.save_cookies() | ||
| return resp | ||
| # Usage | ||
| session = PersistentSession(os.path.expanduser("~/.myagent/sessions/example.com.json")) | ||
| session.post("https://example.com/login", json={"user": "...", "pass": "..."}) | ||
| resp = session.get("https://example.com/protected") | ||
| ``` | ||
| For Netscape format interoperability via the standard library: | ||
| ```python | ||
| import http.cookiejar, urllib.request | ||
| jar = http.cookiejar.MozillaCookieJar("/tmp/cookies.txt") | ||
| jar.load(ignore_discard=True, ignore_expires=True) | ||
| opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(jar)) | ||
| response = opener.open("https://example.com/protected") | ||
| jar.save(ignore_discard=True, ignore_expires=True) | ||
| ``` | ||
| --- | ||
| ### 3. OAuth 2.0 / OIDC Flows in Terminal | ||
| #### Device Authorization Flow (RFC 8628) - recommended for CLI | ||
| The Device Authorization Grant is purpose-built for input-constrained devices and CLI tools. The flow: | ||
| ``` | ||
| CLI App Auth Server User's Browser | ||
| | | | | ||
| |--POST /device_authorization| | | ||
| | client_id, scope | | | ||
| | | | | ||
| |<-device_code, user_code----| | | ||
| | verification_uri | | | ||
| | expires_in, interval | | | ||
| | | | | ||
| | Print to terminal: | | | ||
| | "Go to https://auth.example.com/activate" | | ||
| | "Enter code: WXYZ-1234" | | | ||
| | |<-user visits URI-------| | ||
| | |<-user enters code------| | ||
| | |<-user grants consent---| | ||
| | | | | ||
| | POLL every `interval` s: | | | ||
| |--POST /token---------------| | | ||
| | device_code, grant_type=urn:ietf:params:oauth:grant-type:device_code | ||
| | | | | ||
| |<-access_token, refresh_token (once user approves)--| | ||
| ``` | ||
| Implementation in Python: | ||
| ```python | ||
| import requests, time, webbrowser | ||
| def device_auth_flow(client_id: str, auth_server: str, scope: str = "openid profile"): | ||
| # Step 1: Request device code | ||
| resp = requests.post(f"{auth_server}/device_authorization", data={ | ||
| "client_id": client_id, | ||
| "scope": scope, | ||
| }) | ||
| resp.raise_for_status() | ||
| data = resp.json() | ||
| device_code = data["device_code"] | ||
| user_code = data["user_code"] | ||
| verification_uri = data["verification_uri"] | ||
| expires_in = data.get("expires_in", 300) | ||
| interval = data.get("interval", 5) | ||
| # Step 2: Display instructions | ||
| print(f"\nVisit: {verification_uri}") | ||
| print(f"Enter code: {user_code}\n") | ||
| webbrowser.open(verification_uri) # Open automatically if available | ||
| # Step 3: Poll for token | ||
| deadline = time.time() + expires_in | ||
| while time.time() < deadline: | ||
| time.sleep(interval) | ||
| token_resp = requests.post(f"{auth_server}/token", data={ | ||
| "client_id": client_id, | ||
| "device_code": device_code, | ||
| "grant_type": "urn:ietf:params:oauth:grant-type:device_code", | ||
| }) | ||
| token_data = token_resp.json() | ||
| if "access_token" in token_data: | ||
| return token_data # Contains access_token, refresh_token, expires_in | ||
| error = token_data.get("error") | ||
| if error == "authorization_pending": | ||
| continue # Still waiting | ||
| elif error == "slow_down": | ||
| interval += 5 # Server requested backoff | ||
| elif error in ("access_denied", "expired_token"): | ||
| raise Exception(f"Auth failed: {error}") | ||
| raise TimeoutError("Device authorization timed out") | ||
| ``` | ||
| Providers that support Device Flow: GitHub, Google, Microsoft Azure AD, Okta, Auth0, GitLab. | ||
| **GitHub CLI example** (uses device flow internally): | ||
| ```bash | ||
| gh auth login --web # Triggers device flow | ||
| ``` | ||
| #### PKCE + Localhost Redirect (for CLIs that can bind a port) | ||
| PKCE (Proof Key for Code Exchange) with a localhost redirect server avoids exposing client secrets. | ||
| ```python | ||
| import secrets, hashlib, base64, socket, threading, urllib.parse | ||
| from http.server import HTTPServer, BaseHTTPRequestHandler | ||
| import requests, webbrowser | ||
| def generate_pkce(): | ||
| verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).rstrip(b'=').decode() | ||
| challenge = base64.urlsafe_b64encode( | ||
| hashlib.sha256(verifier.encode()).digest() | ||
| ).rstrip(b'=').decode() | ||
| return verifier, challenge | ||
| class CallbackHandler(BaseHTTPRequestHandler): | ||
| auth_code = None | ||
| def do_GET(self): | ||
| params = urllib.parse.parse_qs(urllib.parse.urlparse(self.path).query) | ||
| CallbackHandler.auth_code = params.get('code', [None])[0] | ||
| self.send_response(200) | ||
| self.end_headers() | ||
| self.wfile.write(b"Auth complete! You can close this tab.") | ||
| def log_message(self, *args): | ||
| pass # Suppress HTTP logs | ||
| def pkce_flow(client_id: str, auth_server: str, scope: str): | ||
| verifier, challenge = generate_pkce() | ||
| state = secrets.token_urlsafe(16) | ||
| # Find free port | ||
| with socket.socket() as s: | ||
| s.bind(('', 0)) | ||
| port = s.getsockname()[1] | ||
| redirect_uri = f"http://localhost:{port}/callback" | ||
| auth_url = ( | ||
| f"{auth_server}/authorize?" | ||
| f"response_type=code&client_id={client_id}" | ||
| f"&redirect_uri={urllib.parse.quote(redirect_uri)}" | ||
| f"&scope={urllib.parse.quote(scope)}&state={state}" | ||
| f"&code_challenge={challenge}&code_challenge_method=S256" | ||
| ) | ||
| # Start local server in background | ||
| server = HTTPServer(('localhost', port), CallbackHandler) | ||
| thread = threading.Thread(target=server.handle_request) | ||
| thread.start() | ||
| webbrowser.open(auth_url) | ||
| thread.join(timeout=120) | ||
| if not CallbackHandler.auth_code: | ||
| raise TimeoutError("No auth code received") | ||
| # Exchange code for tokens | ||
| token_resp = requests.post(f"{auth_server}/token", data={ | ||
| "grant_type": "authorization_code", | ||
| "code": CallbackHandler.auth_code, | ||
| "redirect_uri": redirect_uri, | ||
| "client_id": client_id, | ||
| "code_verifier": verifier, | ||
| }) | ||
| return token_resp.json() | ||
| ``` | ||
| #### Manual Token Injection (common in AI agent workflows) | ||
| Many AI agent workflows use manually injected tokens - the user provides a Bearer token from their browser DevTools, and the agent stores and uses it: | ||
| ```python | ||
| import json, os, time | ||
| class TokenStore: | ||
| def __init__(self, store_path: str): | ||
| self.store_path = store_path | ||
| def store_token(self, service: str, token_data: dict): | ||
| """token_data: {access_token, refresh_token, expires_at, scope}""" | ||
| store = self._load() | ||
| store[service] = { | ||
| **token_data, | ||
| "stored_at": time.time(), | ||
| } | ||
| self._save(store) | ||
| def get_valid_token(self, service: str) -> str | None: | ||
| store = self._load() | ||
| entry = store.get(service) | ||
| if not entry: | ||
| return None | ||
| # Check TTL with 60s buffer | ||
| if entry.get("expires_at", float('inf')) < time.time() + 60: | ||
| return None # Expired | ||
| return entry.get("access_token") | ||
| def _load(self) -> dict: | ||
| if not os.path.exists(self.store_path): | ||
| return {} | ||
| with open(self.store_path) as f: | ||
| return json.load(f) | ||
| def _save(self, data: dict): | ||
| os.makedirs(os.path.dirname(self.store_path), exist_ok=True) | ||
| with open(self.store_path, 'w') as f: | ||
| json.dump(data, f, indent=2) | ||
| os.chmod(self.store_path, 0o600) # Owner read/write only | ||
| ``` | ||
| --- | ||
| ### 4. X (Twitter) Authentication | ||
| #### Current API landscape (2024-2025) | ||
| X's API access has undergone major restructuring. Understanding the current tiers is essential: | ||
| | Tier | Price | Rate Limits | User Context | Notes | | ||
| |------|-------|-------------|--------------|-------| | ||
| | Free | $0 | 1,500 tweets/month write only | No read | Effectively write-only | | ||
| | Basic | $100/mo | 10k tweets read, 50k write | Yes (limited) | Minimum for read automation | | ||
| | Pro | $5,000/mo | 1M tweets read | Yes | Most professional use cases | | ||
| | Enterprise | Custom | Custom | Yes | Large-scale | | ||
| #### OAuth 1.0a (still works, required for some endpoints) | ||
| OAuth 1.0a requires signing every request with HMAC-SHA1: | ||
| ```python | ||
| import hmac, hashlib, base64, time, random, string, urllib.parse | ||
| def oauth1_header(method, url, params, consumer_key, consumer_secret, | ||
| token="", token_secret=""): | ||
| oauth_params = { | ||
| "oauth_consumer_key": consumer_key, | ||
| "oauth_nonce": ''.join(random.choices(string.ascii_letters + string.digits, k=32)), | ||
| "oauth_signature_method": "HMAC-SHA1", | ||
| "oauth_timestamp": str(int(time.time())), | ||
| "oauth_token": token, | ||
| "oauth_version": "1.0", | ||
| } | ||
| if not token: | ||
| del oauth_params["oauth_token"] | ||
| all_params = {**params, **oauth_params} | ||
| sorted_params = "&".join( | ||
| f"{urllib.parse.quote(k, safe='')}={urllib.parse.quote(str(v), safe='')}" | ||
| for k, v in sorted(all_params.items()) | ||
| ) | ||
| base_string = "&".join([ | ||
| method.upper(), | ||
| urllib.parse.quote(url, safe=''), | ||
| urllib.parse.quote(sorted_params, safe=''), | ||
| ]) | ||
| signing_key = ( | ||
| f"{urllib.parse.quote(consumer_secret, safe='')}" | ||
| f"&{urllib.parse.quote(token_secret, safe='')}" | ||
| ) | ||
| signature = base64.b64encode( | ||
| hmac.new(signing_key.encode(), base_string.encode(), hashlib.sha1).digest() | ||
| ).decode() | ||
| oauth_params["oauth_signature"] = signature | ||
| header_value = "OAuth " + ", ".join( | ||
| f'{k}="{urllib.parse.quote(str(v), safe="")}"' | ||
| for k, v in sorted(oauth_params.items()) | ||
| ) | ||
| return header_value | ||
| ``` | ||
| #### OAuth 2.0 with PKCE (for user context on Basic+) | ||
| X supports OAuth 2.0 with PKCE for the Basic tier and above: | ||
| - Authorization: `https://twitter.com/i/oauth2/authorize` | ||
| - Token: `https://api.twitter.com/2/oauth2/token` | ||
| - Scopes: `tweet.read tweet.write users.read offline.access` | ||
| ```python | ||
| # Using tweepy (recommended library) | ||
| import tweepy | ||
| oauth2_handler = tweepy.OAuth2UserHandler( | ||
| client_id="YOUR_CLIENT_ID", | ||
| redirect_uri="http://localhost:5000/callback", | ||
| scope=["tweet.read", "tweet.write", "users.read", "offline.access"], | ||
| client_secret="YOUR_CLIENT_SECRET" # Optional for public clients | ||
| ) | ||
| # Get auth URL | ||
| auth_url = oauth2_handler.get_authorization_url() | ||
| print(f"Authorize at: {auth_url}") | ||
| # After user visits and approves: | ||
| callback_url = input("Paste callback URL: ") | ||
| access_token = oauth2_handler.fetch_token(callback_url) | ||
| # access_token contains: access_token, expires_at, refresh_token | ||
| ``` | ||
| #### App-only auth (Bearer token) - read-only | ||
| ```python | ||
| import requests, base64 | ||
| def get_bearer_token(api_key: str, api_secret: str) -> str: | ||
| credentials = base64.b64encode( | ||
| f"{api_key}:{api_secret}".encode() | ||
| ).decode() | ||
| resp = requests.post( | ||
| "https://api.twitter.com/oauth2/token", | ||
| headers={ | ||
| "Authorization": f"Basic {credentials}", | ||
| "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", | ||
| }, | ||
| data="grant_type=client_credentials" | ||
| ) | ||
| return resp.json()["access_token"] | ||
| # Use bearer token | ||
| bearer = get_bearer_token(api_key, api_secret) | ||
| resp = requests.get( | ||
| "https://api.twitter.com/2/tweets/search/recent?query=python", | ||
| headers={"Authorization": f"Bearer {bearer}"} | ||
| ) | ||
| ``` | ||
| **Critical note**: As of 2024, bearer tokens for search require at least Basic tier. Free tier has no read access. | ||
| #### Cookie-based auth (unofficial - documented for awareness, not recommended) | ||
| Some tools authenticate to X using browser cookies directly. The key cookies are `auth_token` and `ct0` (CSRF token). The `ct0` value must be sent as both a cookie and an `x-csrf-token` header. | ||
| **Warning**: This is against X's ToS, fragile (cookies rotate on suspicious activity), and risks account suspension. Use the official API exclusively for reliable automation. | ||
| #### Realistic assessment for X automation | ||
| | Approach | Status (2025) | Risk | | ||
| |----------|---------------|------| | ||
| | Official API v2 (Basic+) | Stable | Safe within rate limits | | ||
| | OAuth 1.0a via API | Works | Safe within rate limits | | ||
| | Cookie-based unofficial | Fragile, often detected | Account ban risk | | ||
| | Headless browser | Heavily blocked | Account/IP ban | | ||
| --- | ||
| ### 5. Extracting Cookies from Real Browser Sessions | ||
| #### Browser extensions for manual export | ||
| **EditThisCookie** (Chrome extension): | ||
| - Click extension icon on any page | ||
| - "Export" tab gives JSON array of cookie objects | ||
| - Fields: `domain`, `expirationDate`, `hostOnly`, `httpOnly`, `name`, `path`, `sameSite`, `secure`, `session`, `storeId`, `value` | ||
| - Convert to Netscape format before use with curl | ||
| **Cookie-Editor** (Chrome/Firefox extension): | ||
| - "Export" button on header toolbar | ||
| - Supports JSON export and Netscape/HeaderString formats | ||
| - Can import cookies in bulk for testing | ||
| **Export flow using Cookie-Editor JSON to curl**: | ||
| ```bash | ||
| # Cookie-Editor exports JSON like: | ||
| # [{"name":"sessionid","value":"abc123","domain":".example.com","path":"/","secure":true,...}] | ||
| # Convert to Netscape format using Python | ||
| python3 - <<'EOF' | ||
| import json, sys | ||
| with open('/tmp/cookie_editor_export.json') as f: | ||
| cookies = json.load(f) | ||
| print("# Netscape HTTP Cookie File") | ||
| for c in cookies: | ||
| domain = c['domain'] | ||
| inc_sub = "TRUE" if domain.startswith('.') else "FALSE" | ||
| secure = "TRUE" if c.get('secure', False) else "FALSE" | ||
| expiry = int(c.get('expirationDate', 0)) | ||
| path = c.get('path', '/') | ||
| print(f"{domain}\t{inc_sub}\t{path}\t{secure}\t{expiry}\t{c['name']}\t{c['value']}") | ||
| EOF > /tmp/curl_cookies.txt | ||
| # Now use with curl | ||
| curl -b /tmp/curl_cookies.txt https://example.com/protected | ||
| ``` | ||
| #### Programmatic extraction using browser_cookie3 | ||
| ```python | ||
| # pip install browser-cookie3 | ||
| import browser_cookie3 | ||
| # Get cookies for a specific domain | ||
| firefox_jar = browser_cookie3.firefox(domain_name=".twitter.com") | ||
| chrome_jar = browser_cookie3.chrome(domain_name=".twitter.com") | ||
| all_jar = browser_cookie3.load(domain_name=".twitter.com") | ||
| # Use with requests | ||
| import requests | ||
| session = requests.Session() | ||
| session.cookies = chrome_jar | ||
| resp = session.get("https://twitter.com/home") | ||
| ``` | ||
| **Platform paths** browser_cookie3 knows about: | ||
| | Browser | Linux | macOS | | ||
| |---------|-------|-------| | ||
| | Chrome | `~/.config/google-chrome/Default/Cookies` | `~/Library/Application Support/Google/Chrome/Default/Cookies` | | ||
| | Firefox | `~/.mozilla/firefox/*.default/cookies.sqlite` | `~/Library/Application Support/Firefox/Profiles/*.default/cookies.sqlite` | | ||
| | Brave | `~/.config/BraveSoftware/Brave-Browser/Default/Cookies` | `~/Library/Application Support/BraveSoftware/Brave-Browser/Default/Cookies` | | ||
| #### Chrome DevTools Protocol (CDP) for live extraction | ||
| ```python | ||
| # pip install playwright | ||
| from playwright.sync_api import sync_playwright | ||
| import json | ||
| with sync_playwright() as p: | ||
| # Connect to existing Chrome with remote debugging enabled | ||
| # Start Chrome with: google-chrome --remote-debugging-port=9222 | ||
| browser = p.chromium.connect_over_cdp("http://localhost:9222") | ||
| context = browser.contexts[0] | ||
| cookies = context.cookies(["https://twitter.com"]) | ||
| with open("/tmp/extracted_cookies.json", "w") as f: | ||
| json.dump(cookies, f, indent=2) | ||
| browser.close() | ||
| ``` | ||
| --- | ||
| ### 6. Session Storage: AI_STATE_DIR Design | ||
| #### Directory structure for agent session storage | ||
| ``` | ||
| ~/.agentname/ | ||
| sessions/ | ||
| twitter.com/ | ||
| cookies.json.enc # AES-GCM encrypted JSON cookie store | ||
| oauth_tokens.json.enc # AES-GCM encrypted OAuth tokens with TTL | ||
| session_meta.json # Non-sensitive metadata (last_used, scope) | ||
| github.com/ | ||
| oauth_tokens.json.enc | ||
| session_meta.json | ||
| config/ | ||
| encryption.key # AES-256 key (permissions: 0600) | ||
| ``` | ||
| Environment variable pattern: | ||
| ```bash | ||
| export AI_STATE_DIR="${AI_STATE_DIR:-$HOME/.myagent}" | ||
| export AI_SESSIONS_DIR="$AI_STATE_DIR/sessions" | ||
| export AI_ENCRYPTION_KEY_PATH="$AI_STATE_DIR/config/encryption.key" | ||
| ``` | ||
| #### Encryption at rest with AES-256-GCM | ||
| ```python | ||
| import os, json, base64 | ||
| from cryptography.hazmat.primitives.ciphers.aead import AESGCM | ||
| class EncryptedSessionStore: | ||
| NONCE_SIZE = 12 # 96-bit nonce for GCM | ||
| def __init__(self, state_dir: str): | ||
| self.state_dir = state_dir | ||
| self.key_path = os.path.join(state_dir, "config", "encryption.key") | ||
| self._key = self._load_or_create_key() | ||
| def _load_or_create_key(self) -> bytes: | ||
| if os.path.exists(self.key_path): | ||
| with open(self.key_path, "rb") as f: | ||
| return f.read() | ||
| # Generate new 256-bit key | ||
| key = AESGCM.generate_key(bit_length=256) | ||
| os.makedirs(os.path.dirname(self.key_path), exist_ok=True) | ||
| with open(self.key_path, "wb") as f: | ||
| f.write(key) | ||
| os.chmod(self.key_path, 0o600) | ||
| return key | ||
| def save(self, domain: str, data_type: str, data: dict): | ||
| aesgcm = AESGCM(self._key) | ||
| nonce = os.urandom(self.NONCE_SIZE) | ||
| plaintext = json.dumps(data).encode() | ||
| ciphertext = aesgcm.encrypt(nonce, plaintext, None) | ||
| # nonce + ciphertext stored together, base64-encoded | ||
| payload = base64.b64encode(nonce + ciphertext).decode() | ||
| path = os.path.join(self.state_dir, "sessions", domain, f"{data_type}.json.enc") | ||
| os.makedirs(os.path.dirname(path), exist_ok=True) | ||
| with open(path, "w") as f: | ||
| json.dump({"v": 1, "data": payload}, f) | ||
| os.chmod(path, 0o600) | ||
| def load(self, domain: str, data_type: str) -> dict | None: | ||
| path = os.path.join(self.state_dir, "sessions", domain, f"{data_type}.json.enc") | ||
| if not os.path.exists(path): | ||
| return None | ||
| with open(path) as f: | ||
| envelope = json.load(f) | ||
| raw = base64.b64decode(envelope["data"]) | ||
| nonce, ciphertext = raw[:self.NONCE_SIZE], raw[self.NONCE_SIZE:] | ||
| aesgcm = AESGCM(self._key) | ||
| plaintext = aesgcm.decrypt(nonce, ciphertext, None) | ||
| return json.loads(plaintext) | ||
| ``` | ||
| #### TTL handling | ||
| ```python | ||
| import time | ||
| class SessionWithTTL: | ||
| def __init__(self, store: EncryptedSessionStore, domain: str): | ||
| self.store = store | ||
| self.domain = domain | ||
| def store_tokens(self, tokens: dict, ttl_seconds: int = 3600): | ||
| tokens_with_meta = { | ||
| **tokens, | ||
| "expires_at": time.time() + ttl_seconds, | ||
| "stored_at": time.time(), | ||
| } | ||
| self.store.save(self.domain, "oauth_tokens", tokens_with_meta) | ||
| def get_valid_tokens(self) -> dict | None: | ||
| tokens = self.store.load(self.domain, "oauth_tokens") | ||
| if not tokens: | ||
| return None | ||
| # Expire 5 minutes early to avoid edge cases | ||
| if tokens.get("expires_at", 0) < time.time() + 300: | ||
| return None | ||
| return tokens | ||
| def store_cookies(self, cookies: list[dict], ttl_seconds: int = 86400): | ||
| now = time.time() | ||
| # Only store non-expired cookies | ||
| valid = [c for c in cookies if c.get("expires", now + 1) > now] | ||
| cookie_store = { | ||
| "cookies": valid, | ||
| "stored_at": now, | ||
| "store_expires_at": now + ttl_seconds, | ||
| } | ||
| self.store.save(self.domain, "cookies", cookie_store) | ||
| def get_valid_cookies(self) -> list[dict] | None: | ||
| data = self.store.load(self.domain, "cookies") | ||
| if not data: | ||
| return None | ||
| if data.get("store_expires_at", 0) < time.time(): | ||
| return None | ||
| now = time.time() | ||
| return [c for c in data["cookies"] | ||
| if c.get("expires", float('inf')) > now] | ||
| ``` | ||
| --- | ||
| ### 7. Multi-Step Web Workflows: Session State Across Invocations | ||
| #### State machine pattern for multi-step auth | ||
| ```python | ||
| import json, os, time | ||
| from enum import Enum | ||
| class AuthState(Enum): | ||
| UNAUTHENTICATED = "unauthenticated" | ||
| DEVICE_CODE_PENDING = "device_code_pending" | ||
| AUTHENTICATED = "authenticated" | ||
| TOKEN_EXPIRED = "token_expired" | ||
| class WorkflowSession: | ||
| def __init__(self, state_file: str): | ||
| self.state_file = state_file | ||
| self._state = self._load() | ||
| def _load(self) -> dict: | ||
| if os.path.exists(self.state_file): | ||
| with open(self.state_file) as f: | ||
| return json.load(f) | ||
| return {"auth_state": AuthState.UNAUTHENTICATED.value, "step": 0} | ||
| def _save(self): | ||
| os.makedirs(os.path.dirname(self.state_file), exist_ok=True) | ||
| with open(self.state_file, "w") as f: | ||
| json.dump(self._state, f, indent=2) | ||
| @property | ||
| def auth_state(self) -> AuthState: | ||
| return AuthState(self._state.get("auth_state", "unauthenticated")) | ||
| def begin_device_flow(self, device_code: str, user_code: str, | ||
| verification_uri: str, expires_at: float): | ||
| self._state.update({ | ||
| "auth_state": AuthState.DEVICE_CODE_PENDING.value, | ||
| "device_code": device_code, | ||
| "user_code": user_code, | ||
| "verification_uri": verification_uri, | ||
| "device_code_expires_at": expires_at, | ||
| }) | ||
| self._save() | ||
| def complete_auth(self, access_token: str, refresh_token: str | None, | ||
| expires_in: int): | ||
| self._state.update({ | ||
| "auth_state": AuthState.AUTHENTICATED.value, | ||
| "access_token": access_token, | ||
| "refresh_token": refresh_token, | ||
| "token_expires_at": time.time() + expires_in, | ||
| "device_code": None, | ||
| "user_code": None, | ||
| }) | ||
| self._save() | ||
| def is_token_valid(self) -> bool: | ||
| if self.auth_state != AuthState.AUTHENTICATED: | ||
| return False | ||
| return self._state.get("token_expires_at", 0) > time.time() + 60 | ||
| def get_access_token(self) -> str | None: | ||
| if self.is_token_valid(): | ||
| return self._state.get("access_token") | ||
| return None | ||
| def advance_step(self, step: int, step_data: dict = None): | ||
| self._state["step"] = step | ||
| if step_data: | ||
| self._state[f"step_{step}_data"] = step_data | ||
| self._save() | ||
| ``` | ||
| #### Resumable multi-step workflow | ||
| ```python | ||
| class ScrapingWorkflow: | ||
| """Multi-step workflow that persists state between agent invocations.""" | ||
| def __init__(self, state_dir: str): | ||
| self.workflow = WorkflowSession( | ||
| os.path.join(state_dir, "workflow_state.json") | ||
| ) | ||
| self.http = PersistentSession( | ||
| os.path.join(state_dir, "sessions", "target.example.com.json") | ||
| ) | ||
| def run(self): | ||
| step = self.workflow._state.get("step", 0) | ||
| if step == 0: | ||
| self._step_authenticate() | ||
| elif step == 1: | ||
| self._step_navigate_to_data() | ||
| elif step == 2: | ||
| self._step_extract_data() | ||
| elif step == 3: | ||
| self._step_post_process() | ||
| else: | ||
| print("[OK] Workflow complete") | ||
| def _step_authenticate(self): | ||
| if self.workflow.is_token_valid(): | ||
| print("[OK] Already authenticated, skipping") | ||
| self.workflow.advance_step(1) | ||
| return | ||
| # ... perform auth, call workflow.complete_auth() ... | ||
| self.workflow.advance_step(1) | ||
| def _step_navigate_to_data(self): | ||
| resp = self.http.get("https://target.example.com/data-page") | ||
| self.workflow.advance_step(2, {"page_count": 10, "current_page": 1}) | ||
| def _step_extract_data(self): | ||
| data = self.workflow._state.get("step_2_data", {}) | ||
| current_page = data.get("current_page", 1) | ||
| page_count = data.get("page_count", 1) | ||
| for page in range(current_page, page_count + 1): | ||
| self.http.get(f"https://target.example.com/data?page={page}") | ||
| # Update progress for resume capability | ||
| data["current_page"] = page + 1 | ||
| self.workflow.advance_step(2, data) | ||
| self.workflow.advance_step(3) | ||
| ``` | ||
| --- | ||
| ### 8. Headless Browser Fingerprinting and X/Twitter Blocking | ||
| #### How X detects headless browsers | ||
| X and sites using bot detection services (Cloudflare, DataDome, PerimeterX) detect headless browsers through: | ||
| 1. **`navigator.webdriver` flag**: `true` in Selenium/CDP-controlled browsers | ||
| 2. **Canvas fingerprinting**: Headless Chrome produces different canvas renders | ||
| 3. **AudioContext fingerprinting**: Different audio processing results | ||
| 4. **WebGL fingerprinting**: Different GPU/renderer strings | ||
| 5. **Missing browser plugins**: `navigator.plugins.length === 0` | ||
| 6. **Inconsistent screen dimensions**: `window.outerWidth === 0` | ||
| 7. **Mouse movement patterns**: Inhuman precision/speed | ||
| 8. **Request timing patterns**: Too fast, too regular | ||
| 9. **TLS fingerprinting (JA3)**: Headless Chrome has different TLS handshake characteristics | ||
| #### Mitigation for legitimate authorized automation | ||
| **For Playwright:** | ||
| ```python | ||
| from playwright.sync_api import sync_playwright | ||
| with sync_playwright() as p: | ||
| browser = p.chromium.launch( | ||
| headless=False, # Or "new" mode (less fingerprinted) | ||
| args=["--disable-blink-features=AutomationControlled"], | ||
| ) | ||
| context = browser.new_context( | ||
| viewport={"width": 1920, "height": 1080}, | ||
| user_agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", | ||
| locale="en-US", | ||
| timezone_id="America/New_York", | ||
| ) | ||
| context.add_init_script(""" | ||
| Object.defineProperty(navigator, 'webdriver', {get: () => undefined}); | ||
| Object.defineProperty(navigator, 'plugins', {get: () => [1, 2, 3, 4, 5]}); | ||
| Object.defineProperty(navigator, 'languages', {get: () => ['en-US', 'en']}); | ||
| """) | ||
| page = context.new_page() | ||
| ``` | ||
| **playwright-stealth** (handles most common fingerprint checks): | ||
| ```bash | ||
| pip install playwright-stealth | ||
| ``` | ||
| ```python | ||
| from playwright.sync_api import sync_playwright | ||
| from playwright_stealth import stealth_sync | ||
| with sync_playwright() as p: | ||
| browser = p.chromium.launch(headless=True) | ||
| page = browser.new_page() | ||
| stealth_sync(page) | ||
| page.goto("https://twitter.com") | ||
| ``` | ||
| **Realistic assessment for X automation**: | ||
| | Approach | Status (2025) | Risk | | ||
| |----------|---------------|------| | ||
| | Official API v2 | Works on Basic+ | Safe if within rate limits | | ||
| | OAuth 1.0a via API | Works | Safe if within rate limits | | ||
| | Cookie-based unofficial | Fragile, detected | Account ban risk | | ||
| | Headless browser | Heavily blocked | Account/IP ban | | ||
| The only sustainable approach for X automation is the official API with proper OAuth credentials. | ||
| --- | ||
| ### 9. Security Considerations | ||
| #### Cookie theft vectors | ||
| | Threat | Description | Mitigation | | ||
| |--------|-------------|------------| | ||
| | File system access | Other processes reading cookie files | `chmod 600`, encrypt at rest | | ||
| | Environment variable leakage | Tokens in env vars visible in `/proc` | Use file-based secrets, never env vars for tokens | | ||
| | Logging | Tokens printed to stdout/stderr | Scrub `Authorization: Bearer ...` values in logs | | ||
| | Process memory | Tokens in heap | Clear sensitive strings after use | | ||
| | Symlink attacks | Attacker creates symlink to redirect writes | Check file ownership before reading | | ||
| | Swap disclosure | Memory paged to disk | Exclude session dirs from backup; use `mlock` for highest sensitivity | | ||
| | Backup inclusion | Cookie files in backups | Exclude `~/.agentname/sessions/` from backup tools | | ||
| #### Token expiry strategies | ||
| ```python | ||
| class TokenManager: | ||
| REFRESH_BUFFER_SECONDS = 300 # Refresh 5 min before expiry | ||
| def get_token(self, service: str) -> str: | ||
| tokens = self.store.load(service, "oauth_tokens") | ||
| if not tokens: | ||
| raise Exception(f"No tokens for {service}; re-authentication required") | ||
| expires_at = tokens.get("expires_at", float('inf')) | ||
| if expires_at < time.time() + self.REFRESH_BUFFER_SECONDS: | ||
| if tokens.get("refresh_token"): | ||
| tokens = self._refresh_token(service, tokens["refresh_token"]) | ||
| else: | ||
| raise Exception(f"Token expired for {service}; re-authentication required") | ||
| return tokens["access_token"] | ||
| def _refresh_token(self, service: str, refresh_token: str) -> dict: | ||
| resp = requests.post(f"https://{service}/oauth/token", data={ | ||
| "grant_type": "refresh_token", | ||
| "refresh_token": refresh_token, | ||
| "client_id": self.client_id, | ||
| }) | ||
| new_tokens = resp.json() | ||
| new_tokens["expires_at"] = time.time() + new_tokens.get("expires_in", 3600) | ||
| self.store.save(service, "oauth_tokens", new_tokens) | ||
| return new_tokens | ||
| ``` | ||
| #### Scope limitation principle | ||
| Request only the minimum OAuth scopes needed: | ||
| ```python | ||
| SCOPE_REGISTRY = { | ||
| "read_timeline": ["tweet.read", "users.read"], | ||
| "post_tweet": ["tweet.write", "users.read"], | ||
| "dm_access": ["dm.read", "dm.write", "users.read"], | ||
| } | ||
| def get_required_scopes(operations: list[str]) -> set[str]: | ||
| scopes = set() | ||
| for op in operations: | ||
| scopes.update(SCOPE_REGISTRY.get(op, [])) | ||
| return scopes | ||
| ``` | ||
| --- | ||
| ### 10. Complete Reference Implementation | ||
| A production-quality session persistence layer for AI agents: | ||
| ```python | ||
| """ | ||
| agent_sessions.py - Secure session persistence layer for CLI AI agents | ||
| All session data is JSON-serialized and AES-256-GCM encrypted at rest. | ||
| No binary serialization formats (pickle etc.) are used. | ||
| Usage: | ||
| store = AgentSessionStore(state_dir="~/.myagent") | ||
| store.save_cookies("twitter.com", cookies) | ||
| store.save_tokens("github.com", tokens) | ||
| cookies = store.get_cookies("twitter.com") # None if expired | ||
| tokens = store.get_tokens("github.com") # None if expired | ||
| headers = store.get_auth_headers("github.com") # Ready-to-use headers | ||
| cookie_str = store.to_cookie_header("twitter.com") # "name=val; ..." | ||
| store.to_netscape_file("twitter.com", "/tmp/curl_cookies.txt") | ||
| """ | ||
| import os, json, time, base64, logging | ||
| from pathlib import Path | ||
| from typing import Optional | ||
| from cryptography.hazmat.primitives.ciphers.aead import AESGCM | ||
| logger = logging.getLogger(__name__) | ||
| class AgentSessionStore: | ||
| KEY_SIZE = 32 # 256-bit AES | ||
| NONCE_SIZE = 12 # 96-bit GCM nonce | ||
| TOKEN_REFRESH_BUFFER = 300 # seconds | ||
| def __init__(self, state_dir: str = None): | ||
| self.state_dir = Path(state_dir or os.environ.get( | ||
| "AI_STATE_DIR", Path.home() / ".agent" | ||
| )).expanduser() | ||
| self.sessions_dir = self.state_dir / "sessions" | ||
| self.key_path = self.state_dir / "config" / "encryption.key" | ||
| self._key = self._init_key() | ||
| def _init_key(self) -> bytes: | ||
| if self.key_path.exists(): | ||
| return self.key_path.read_bytes() | ||
| self.key_path.parent.mkdir(parents=True, exist_ok=True) | ||
| key = AESGCM.generate_key(bit_length=256) | ||
| self.key_path.write_bytes(key) | ||
| self.key_path.chmod(0o600) | ||
| return key | ||
| def _encrypt(self, data: dict) -> str: | ||
| nonce = os.urandom(self.NONCE_SIZE) | ||
| aesgcm = AESGCM(self._key) | ||
| ct = aesgcm.encrypt(nonce, json.dumps(data).encode(), None) | ||
| return base64.b64encode(nonce + ct).decode() | ||
| def _decrypt(self, payload: str) -> dict: | ||
| raw = base64.b64decode(payload) | ||
| nonce, ct = raw[:self.NONCE_SIZE], raw[self.NONCE_SIZE:] | ||
| plaintext = AESGCM(self._key).decrypt(nonce, ct, None) | ||
| return json.loads(plaintext) | ||
| def _session_path(self, domain: str, data_type: str) -> Path: | ||
| return self.sessions_dir / domain.replace(":", "_") / f"{data_type}.enc" | ||
| def _write(self, domain: str, data_type: str, data: dict): | ||
| path = self._session_path(domain, data_type) | ||
| path.parent.mkdir(parents=True, exist_ok=True) | ||
| encrypted = self._encrypt(data) | ||
| path.write_text(json.dumps({"v": 1, "enc": encrypted})) | ||
| path.chmod(0o600) | ||
| logger.debug(f"Saved {data_type} for {domain}") | ||
| def _read(self, domain: str, data_type: str) -> Optional[dict]: | ||
| path = self._session_path(domain, data_type) | ||
| if not path.exists(): | ||
| return None | ||
| try: | ||
| envelope = json.loads(path.read_text()) | ||
| return self._decrypt(envelope["enc"]) | ||
| except Exception as e: | ||
| logger.warning(f"Failed to read {data_type} for {domain}: {e}") | ||
| return None | ||
| # --- Public API --- | ||
| def save_cookies(self, domain: str, cookies: list[dict], | ||
| ttl_seconds: int = 86400 * 7): | ||
| now = time.time() | ||
| valid = [c for c in cookies if c.get("expires", now + 1) > now] | ||
| self._write(domain, "cookies", { | ||
| "cookies": valid, | ||
| "stored_at": now, | ||
| "expires_at": now + ttl_seconds, | ||
| }) | ||
| def get_cookies(self, domain: str) -> Optional[list[dict]]: | ||
| data = self._read(domain, "cookies") | ||
| if not data or data.get("expires_at", 0) < time.time(): | ||
| return None | ||
| now = time.time() | ||
| return [c for c in data["cookies"] | ||
| if c.get("expires", float("inf")) > now] | ||
| def save_tokens(self, domain: str, tokens: dict): | ||
| if "expires_in" in tokens and "expires_at" not in tokens: | ||
| tokens["expires_at"] = time.time() + tokens["expires_in"] | ||
| tokens["stored_at"] = time.time() | ||
| self._write(domain, "tokens", tokens) | ||
| def get_tokens(self, domain: str) -> Optional[dict]: | ||
| data = self._read(domain, "tokens") | ||
| if not data: | ||
| return None | ||
| expires_at = data.get("expires_at", float("inf")) | ||
| if expires_at < time.time() + self.TOKEN_REFRESH_BUFFER: | ||
| logger.info(f"Token for {domain} is expired or near expiry") | ||
| return None | ||
| return data | ||
| def get_auth_headers(self, domain: str) -> Optional[dict]: | ||
| tokens = self.get_tokens(domain) | ||
| if not tokens: | ||
| return None | ||
| token_type = tokens.get("token_type", "Bearer").capitalize() | ||
| return {"Authorization": f"{token_type} {tokens['access_token']}"} | ||
| def to_cookie_header(self, domain: str) -> Optional[str]: | ||
| cookies = self.get_cookies(domain) | ||
| if not cookies: | ||
| return None | ||
| return "; ".join( | ||
| f"{c['name']}={c['value']}" | ||
| for c in cookies | ||
| if domain in c.get("domain", "") | ||
| ) | ||
| def to_netscape_file(self, domain: str, output_path: str) -> bool: | ||
| cookies = self.get_cookies(domain) | ||
| if not cookies: | ||
| return False | ||
| with open(output_path, "w") as f: | ||
| f.write("# Netscape HTTP Cookie File\n") | ||
| for c in cookies: | ||
| d = c.get("domain", domain) | ||
| inc_sub = "TRUE" if d.startswith(".") else "FALSE" | ||
| secure = "TRUE" if c.get("secure", False) else "FALSE" | ||
| expiry = int(c.get("expires", 0)) | ||
| f.write( | ||
| f"{d}\t{inc_sub}\t{c.get('path', '/')}\t" | ||
| f"{secure}\t{expiry}\t{c['name']}\t{c['value']}\n" | ||
| ) | ||
| os.chmod(output_path, 0o600) | ||
| return True | ||
| def clear(self, domain: str): | ||
| for data_type in ("cookies", "tokens"): | ||
| path = self._session_path(domain, data_type) | ||
| if path.exists(): | ||
| path.unlink() | ||
| logger.info(f"Cleared session for {domain}") | ||
| ``` | ||
| --- | ||
| ## Common Pitfalls | ||
| | Pitfall | Why It Happens | How to Avoid | | ||
| |---------|---------------|--------------| | ||
| | Session cookies lost on restart | Netscape format discards expiry=0 cookies by default | Use `--keep-session-cookies` (wget) or `ignore_discard=True` (Python) | | ||
| | CSRF token mismatch | Sites require CSRF token in both cookie AND header | Always send `x-csrf-token` header matching `ct0` cookie value | | ||
| | Token refresh race condition | Two parallel requests both try to refresh | Use file locking (`fcntl.flock`) around refresh logic | | ||
| | Chrome cookie file locked | Chrome has WAL lock on SQLite | Copy file to /tmp before reading | | ||
| | OAuth state parameter ignored | CSRF on OAuth redirect not validated | Always validate `state` param matches what you sent | | ||
| | Cookies for wrong domain | Leading dot not added for subdomain matching | Ensure domains start with `.` for subdomain cookies | | ||
| | Plaintext tokens in logs | Debug logging captures request headers | Mask sensitive values: scrub `Authorization: Bearer ...` before logging | | ||
| | Hard-coded client_secret | Secret committed to version control | Use environment variables or a secrets manager | | ||
| | X auth_token rotates | X invalidates auth_token on suspicious activity | Use official API; treat cookie auth as ephemeral | | ||
| | Device code expires | User takes too long to authenticate | Set user-visible countdown timer; re-initiate if expired | | ||
| | Binary serialization of sessions | Using pickle for cookie storage | Always use JSON - it is auditable and does not carry code execution risk | | ||
| --- | ||
| ## Best Practices | ||
| 1. **Always use the official API when available.** Cookie-based auth is fragile and often ToS-violating. OAuth is more stable. (Source: Twitter API docs, OWASP) | ||
| 2. **Encrypt all stored credentials.** AES-256-GCM with a per-installation key (stored at `chmod 600`). Never store plaintext tokens. (Source: OWASP Session Management Cheat Sheet) | ||
| 3. **Implement graceful re-auth.** Design workflows to detect expired sessions and re-initiate auth automatically when a refresh token is available. (Source: RFC 6749) | ||
| 4. **Use per-domain session isolation.** Store cookies/tokens per domain to prevent cross-site data leakage. (Source: security best practices) | ||
| 5. **Validate OAuth state parameter.** Always compare the `state` in the redirect callback against the one you sent; prevents CSRF on the OAuth flow itself. (Source: RFC 6749 Section 10.12) | ||
| 6. **Request minimum OAuth scopes.** Only request scopes your workflow actually needs; reduces blast radius on token theft. (Source: OAuth 2.0 security BCP) | ||
| 7. **Set file permissions to 0600.** `os.chmod(path, 0o600)` immediately after writing any credential file. | ||
| 8. **Never log Bearer tokens or cookie values.** Use scrubbers in log handlers before any token reaches a log file. | ||
| 9. **Implement TTL with refresh buffer.** Refresh tokens 5 minutes before expiry to avoid mid-workflow failures. | ||
| 10. **Use file locking for concurrent access.** Multiple agent instances may run in parallel; protect read-modify-write operations with `fcntl.flock(fd, fcntl.LOCK_EX)`. | ||
| 11. **Export cookies as JSON, convert to Netscape.** Browser extensions export JSON; always run through a converter rather than hand-editing Netscape format. | ||
| 12. **Use JSON (not binary) for all session files.** JSON is human-readable, diffable, and safe to audit. Binary formats like pickle carry deserialization risks. | ||
| --- | ||
| ## Further Reading | ||
| | Resource | Type | Why Recommended | | ||
| |----------|------|-----------------| | ||
| | [RFC 8628 - OAuth 2.0 Device Authorization Grant](https://datatracker.ietf.org/doc/html/rfc8628) | RFC | Authoritative spec for device flow | | ||
| | [RFC 7636 - PKCE](https://datatracker.ietf.org/doc/html/rfc7636) | RFC | PKCE spec, prevents auth code interception | | ||
| | [RFC 6749 - OAuth 2.0 Framework](https://datatracker.ietf.org/doc/html/rfc6749) | RFC | Core OAuth 2.0 framework | | ||
| | [curl Everything - Cookies](https://everything.curl.dev/http/cookies/) | Docs | Definitive curl cookie docs | | ||
| | [Python http.cookiejar docs](https://docs.python.org/3/library/http.cookiejar.html) | Official Docs | Standard library cookie handling | | ||
| | [Netscape Cookie File Format](https://curl.se/docs/http-cookies.html) | Docs | curl's documentation of the format | | ||
| | [Twitter API v2 Authentication](https://developer.twitter.com/en/docs/authentication/overview) | Official Docs | Current X auth requirements | | ||
| | [tweepy OAuth2UserHandler](https://docs.tweepy.org/en/stable/authentication.html) | Library Docs | Best library for X API in Python | | ||
| | [browser_cookie3 GitHub](https://github.com/borisbabic/browser_cookie3) | GitHub | Extract cookies from real browser profiles | | ||
| | [playwright-stealth GitHub](https://github.com/AtuboDad/playwright-stealth) | GitHub | Reduce headless browser fingerprint | | ||
| | [cryptography.io AEAD docs](https://cryptography.io/en/latest/hazmat/primitives/aead/) | Library Docs | AES-GCM implementation in Python | | ||
| | [OWASP Session Management Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html) | Security Guide | Authoritative security guidance | | ||
| | [GitHub CLI auth flow source (Go)](https://github.com/cli/cli/blob/trunk/internal/authflow/flow.go) | GitHub | Real-world device flow implementation | | ||
| | [OAuth 2.0 Security Best Current Practice](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics) | RFC Draft | Current OAuth security hardening guidance | | ||
| --- | ||
| ## Self-Evaluation | ||
| | Metric | Score | Notes | | ||
| |--------|-------|-------| | ||
| | Coverage | 9/10 | All 10 requested sub-topics covered in depth | | ||
| | Diversity | 8/10 | Protocols, curl/wget CLI, Python libraries, security, architecture | | ||
| | Examples | 9/10 | Extensive working code examples for each major concept | | ||
| | Accuracy | 9/10 | Well-established protocols; X API pricing subject to change | | ||
| | Gaps | - | Kerberos/SPNEGO enterprise auth not covered (out of scope); WebAuthn CLI patterns not included | | ||
| --- | ||
| *Generated by /learn from 42 synthesized sources (training knowledge through Aug 2025).* | ||
| *See `resources/web-session-persistence-cli-agents-sources.json` for full source metadata.* |
Sorry, the diff of this file is too big to display
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 2 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 20 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 2 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 20 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
1059
4.03%2057927
-21.25%220
-10.2%40227
-10.65%7
-30%