bot0 Skills System Architecture
The skills system enables bot0 to decompose complex, multi-step user workflows into modular, reusable, testable automation units. Skills are the primary mechanism through which bot0 evolves from a conversational assistant into a persistent automation platform.
Core Concepts
What is a Skill?
A skill is a self-contained, executable unit of automation. Every skill — whether it fetches a YouTube transcript or orchestrates a 10-step pipeline — has the same uniform structure:
<skill-name>/
├── SKILL.md # Manifest — inputs, outputs, instructions, sequence
├── plan.md # How and why the skill was built this way
├── benchmarks.json # Unit tests + performance data (grows over time)
├── sub-skills.json # References to other skills (flat, by skill ID)
└── <executables> # Scripts, configs, templates (*.ts, *.py, *.sh, etc.)
Skills reference other skills by ID. There is no nesting — all skills live at the same level in the vault. Composition happens through sub-skills.json references.
Design Principles
-
Modularize aggressively — Break workflows into the smallest reusable pieces. "Get YouTube transcript from URL" is one skill. "Analyze video comments" is another. Composition happens through references.
-
Uniform structure — Every skill has the same file layout regardless of complexity. An orchestrator that coordinates 5 sub-skills looks the same as a leaf skill that runs a single script.
-
Cost-efficiency hierarchy — When building a skill step, the agent must prioritize approaches in this order:
- APIs first — Deterministic, fast, cheap. Research whether an API exists before trying anything else.
- Deterministic code — Scripts, CLI tools, parsers. No LLM needed for structured data transformation.
- Web fetch — HTTP requests to public pages, RSS feeds, sitemaps.
- dom0 — Browser automation via Chrome DevTools Protocol. Deterministic DOM workflows with element refs.
- cmd0 — Desktop automation via visual grounding. Last resort, highest cost per execution.
-
Agentic only when necessary — Use bot0 agent reasoning for analysis, synthesis, and decisions. Never for data fetching that a script could do.
-
Improvement notes — When a skill fails in production, the agent documents the issue (diagnosis + suggested fix) so the user can address it later in skill builder mode.
Skill Building Lifecycle
The Three Stages
Every skill goes through a three-stage cycle: Plan → Build → Test. The cycle repeats when the user wants to improve a skill after testing. The user finalizes and publishes only when satisfied.
┌─────────────────────────────────────────────────────────────────────────────┐
│ SKILL BUILDING LIFECYCLE │
│ │
│ ┌──────────────────────────┐ │
│ │ SKILL BUILDER SESSION │ │
│ │ (single bot0 session) │ │
│ └──────────┬───────────────┘ │
│ │ │
│ ┌───────────────────────┼───────────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ │ │ │ │ │ │
│ │ 1. PLAN │─────────►│ 2. BUILD │─────────►│ 3. TEST │ │
│ │ │ │ │ │ │ │
│ │ Research │ │ Implement│ │ Opens TEST │ │
│ │ Decompose│ │ sub-skills│ │ WINDOW │ │
│ │ Document │ │ (parallel)│ │ (separate │ │
│ │ │ │ │ │ session) │ │
│ └──────────┘ └──────────┘ └──────┬───────┘ │
│ ▲ │ │
│ │ │ │
│ │ ┌───────────────────┐ │ │
│ │ │ │ │ │
│ └─────────┤ User feedback │◄──────────────┘ │
│ (iterate) │ "too slow" │ User jumps back to │
│ │ "wrong format" │ skill builder session │
│ │ "add caching" │ │
│ └─────────┬─────────┘ │
│ │ │
│ satisfied? │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ PUBLISH │ │
│ │ to vault │ │
│ └──────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Desktop Mode Controls (Updated UX)
The desktop interaction model is split into two controls:
- Primary mode cycle (Shift+Tab): only cycles
ask→auto→plan→ask - Skill Builder toggle (button next to mode controls): independently turns
skill_builderon/off
This replaces the previous pattern where skill_builder was part of the Shift+Tab cycle.
This is now the terminal interaction contract.
When Skill Builder is ON:
- A skill builder session is active
- The step-based workflow runs: Plan → Build → Test
- The user can still use
ask/autopermission behavior - The user can still use
planagent behavior for planning-first turns inside skill builder
When Skill Builder is OFF:
- Session returns to normal non-skill-builder behavior
- Skill plan panel/workflow state is hidden unless re-enabled
Stage 1: Plan
The agent enters skill_builder mode and has full access to all tools. It can run code, search the web, test API endpoints, inspect files — whatever it needs to research the best approach.
What happens:
- User describes a complex task (e.g., "YouTube analytics pipeline for 50+ creators")
- User enables the Skill Builder toggle
- Agent enters
skill_buildermode and initializes the step-based workflow - User can still switch between
ask/autoandplanbehavior while in skill builder - Agent researches approaches (web search, API docs, npm/pypi, existing skills)
- Agent decomposes the task into modular sub-skills
- For each sub-skill, agent documents in the plan:
- What the skill does (single responsibility)
- Which approach to use (API → code → web fetch → dom0 → cmd0)
- Variable inputs with types and defaults
- Expected output structure
- Authentication/credentials needed
- Whether a similar skill already exists and can be reused or forked
- Agent may ask the user clarifying questions (tone, audience, format, etc.)
- Agent presents the full decomposition for user approval before building
The plan is the contract. The agent documents everything it discovers — API rate limits, authentication quirks, edge cases — so the build stage can execute without re-researching.
Stage 2: Build
The agent implements each sub-skill based on the plan. Independent sub-skills are built in parallel using the task tool to spawn sub-agents.
What happens:
- Agent identifies which sub-skills are independent (no data dependencies)
- Agent spawns parallel sub-agents via the
tasktool:task("Build youtube-transcript skill per plan section 2.1") task("Build youtube-comments skill per plan section 2.2") task("Build youtube-thumbnails skill per plan section 2.3") - Each sub-agent:
- Creates the skill directory with uniform structure
- Writes executable code (TypeScript, Python, shell scripts)
- Creates the SKILL.md manifest with typed inputs/outputs
- Writes initial unit tests into benchmarks.json
- Runs the tests to populate baseline benchmark data
- Dependent sub-skills build sequentially after their dependencies complete
- The orchestrator SKILL.md is created last, referencing all sub-skills
- Agent updates the plan with build results and any deviations
Parallel build via task tool:
┌─────────────────────────────────────────────────────────────────────────────┐
│ PARALLEL SUB-SKILL BUILDING │
│ │
│ Skill Builder Agent (parent) │
│ │ │
│ ├── Analyzes plan, identifies 3 independent sub-skills │
│ │ │
│ ├── task("Build youtube-transcript") ──► Sub-agent A │
│ │ ├── Write get-transcript.ts │
│ │ ├── Write SKILL.md │
│ │ ├── Write benchmarks.json │
│ │ └── Run initial tests │
│ │ │
│ ├── task("Build youtube-comments") ──► Sub-agent B (parallel) │
│ │ ├── Write fetch-comments.ts │
│ │ ├── Write SKILL.md │
│ │ └── ... │
│ │ │
│ ├── task("Build youtube-thumbnails") ──► Sub-agent C (parallel) │
│ │ └── ... │
│ │ │
│ │ ◄── All 3 complete ────────────────────────────────────── │
│ │ │
│ ├── Build content-analyzer (depends on transcript/comments output) │
│ │ │
│ └── Build orchestrator SKILL.md (references all sub-skills) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Resource Contention: dom0 & cmd0 in Parallel Builds
Code-only sub-skills (API clients, scripts, parsers) run safely in parallel — they don't share physical resources. But dom0 (browser automation) and cmd0 (desktop automation) control shared hardware: the browser, the mouse, the screen. Two sub-agents clicking on different tabs or moving the mouse simultaneously would produce chaos.
Solution: Resource semaphores in tool middleware.
The daemon's existing resourceManager pattern (used for file edit locks) is extended with resource-class semaphores that transparently gate tool execution:
┌─────────────────────────────────────────────────────────────────────────────┐
│ RESOURCE SEMAPHORE MODEL │
│ │
│ Resource Class Concurrency Reason │
│ ────────────── ─────────── ────── │
│ code / bash unlimited No shared physical resource │
│ file read/write per-file lock Existing resourceManager pattern │
│ dom0 semaphore(1) One browser interaction at a time │
│ cmd0 semaphore(1) One mouse/keyboard at a time │
│ ground semaphore(1) Tied to cmd0 cycle (screenshot→ │
│ ground→click) │
│ │
│ How it works: │
│ │
│ Sub-agent A: bash("dom0 click @d1") │
│ │ │
│ ▼ │
│ Tool middleware checks resource class → "dom0" │
│ │ │
│ ▼ │
│ resourceManager.acquire("dom0") │
│ │ │
│ ├── Lock free? → Execute immediately │
│ │ │
│ └── Lock held by Sub-agent B? → Wait in queue │
│ │ │
│ │ Sub-agent B finishes dom0 command │
│ │ resourceManager.release("dom0") │
│ │ │
│ └── Sub-agent A proceeds │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Key properties:
- Transparent — Sub-agents don't know about contention. They call
bash dom0 click @d1normally. The middleware handles queueing. - Per-command granularity — The lock is held only for the duration of a single tool call, not for an entire sub-agent's lifetime. Sub-agent A can do a dom0 snapshot, release the lock, do some code work, then re-acquire for a click.
- Correct by default — Worst case is sequential execution (safe). Code-only sub-skills are never blocked.
- Extensible — dom0 can later be relaxed to per-tab locks (tab isolation) for read-only operations like
dom0 snapshotanddom0 get-text, while keeping write operations (dom0 click,dom0 type) under a global lock.
Implementation in tool middleware:
The middleware inspects the tool name and arguments to classify the resource:
function getResourceClass(toolName: string, input: unknown): string | null { if (toolName !== 'bash') return null; const cmd = (input as { command?: string })?.command ?? ''; if (cmd.startsWith('dom0 ')) return 'dom0'; if (cmd.startsWith('cmd0 ')) return 'cmd0'; return null; // No resource lock needed }
When the middleware detects a dom0/cmd0 command, it acquires the corresponding semaphore before executing and releases it in a finally block — the same pattern used by acquireFileEdit().
cmd0 is always exclusive — it controls the physical mouse, keyboard, and screen. There is no safe way to parallelize it. Two agents moving the mouse at the same time is undefined behavior.
dom0 has future optimization potential — because the Chrome extension talks to each tab's DevTools session independently, read-only operations (snapshot, get-text, get-attr) on different tabs could theoretically run in parallel. This would require upgrading from semaphore(1) to per-tab locks with a brief global focus lock for operations that require the tab to be active (typing, key presses). This optimization is deferred — the global semaphore is correct and sufficient for Phase 1.5.
Stage 3: Test
The user tests the skill in a separate test window — a dedicated bot0 session with the skill pre-loaded.
What happens:
- Agent completes the build and writes all skill files
- User (or agent) opens a test window:
- Button in the skill plan panel: "Test Skill"
- Or slash command:
/test-skill youtube-creator-analytics
- A new bot0 session opens with the skill already loaded
- User submits a test command (e.g., "Run this on these 3 creators for the last 2 days")
- The agent executes the skill end-to-end, streaming results
- Benchmarks are recorded automatically: duration, cost, token usage, success/failure
- User observes the output and decides:
- Satisfied → close test window, return to skill builder session, publish
- Needs improvement → provide feedback, return to skill builder session, iterate
Test Window UX Flow:
┌─────────────────────────────────────────────────────────────────────────────┐
│ TEST WINDOW FLOW │
│ │
│ Skill Builder Session Test Window │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ │ "Test Skill" │ │ │
│ │ Plan & Build done │ ────────────────►│ New bot0 session │ │
│ │ │ │ Skill pre-loaded │ │
│ │ [Test Skill] btn │ │ │ │
│ │ │ │ User: "Run this │ │
│ │ (waiting for user │ │ on channels X,Y,Z" │ │
│ │ to return) │ │ │ │
│ │ │ │ Agent executes │ │
│ │ │ │ skill end-to-end │ │
│ │ │ │ │ │
│ │ │ │ benchmarks.json │ │
│ │ │ User returns │ updated with run │ │
│ │ │ ◄────────────────│ data │ │
│ │ │ │ │ │
│ │ If feedback: │ └─────────────────────┘ │
│ │ → Re-enter Plan │ │
│ │ → Iterate │ │
│ │ │ │
│ │ If satisfied: │ │
│ │ → Publish │ │
│ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
What the test window provides:
- A clean session — no skill builder context polluting the test
- The skill is invoked exactly as it would be in production
- Benchmarks are real (not simulated) — actual API calls, actual costs
- User can run multiple test scenarios and see all results in benchmarks.json
- The benchmarks.json in the test window is the same file as in the skill directory
Skill File Format
Uniform Structure
Every skill has the same directory layout:
<skill-name>/
├── SKILL.md # Manifest — the contract
├── plan.md # Build rationale and research notes
├── benchmarks.json # Unit tests + performance data
├── sub-skills.json # References to other skills (empty for leaf skills)
└── <executables> # Any number of script/config files
SKILL.md — The Manifest
SKILL.md defines what the skill does, what it needs, what it produces, and how to execute it.
--- name: youtube-transcript description: "Extract transcript/captions from a YouTube video URL" version: 1 # Variable inputs — what the caller must/can provide inputs: - name: url type: string required: true description: "YouTube video URL (e.g., https://youtube.com/watch?v=XXXXX)" - name: language type: string required: false default: "en" description: "Preferred caption language code" - name: include_timestamps type: boolean required: false default: false description: "Whether to include timestamps in output" # Expected output — what the caller gets back outputs: - name: transcript type: string description: "Plain text transcript of the video" - name: duration_seconds type: number description: "Video duration in seconds" - name: caption_source type: string description: "'manual' or 'auto-generated'" # Execution metadata auth: [] tools_used: ["bash"] model: null estimated_cost: "$0.00" estimated_duration: "5s" --- # youtube-transcript Extract transcripts from YouTube videos using yt-dlp (no API key needed). ## Prerequisites - `yt-dlp` installed (`pip install yt-dlp`) ## Instructions Execute the following steps in order: ### Step 1: Validate input URL Verify the URL matches YouTube format. Extract video ID. ### Step 2: Fetch captions Run `get-transcript.ts` with the video URL: ```bash npx tsx get-transcript.ts --url "${url}" --lang "${language}"
Step 3: Format output
If include_timestamps is false, strip timestamp prefixes from each line.
Return the transcript text, duration, and caption source type.
**Key elements:**
- `inputs` — Typed, required/optional, with defaults. The agent knows exactly what to pass.
- `outputs` — Typed with descriptions. The agent knows exactly what to expect back.
- `Instructions` — Ordered sequence of steps. The executing agent follows these literally.
### SKILL.md for an Orchestrator
An orchestrator skill references sub-skills and defines the execution sequence:
```yaml
---
name: youtube-creator-analytics
description: "Analyze YouTube creators, extract data from recent videos, generate a blog post"
version: 1
inputs:
- name: creator_urls
type: string[]
required: true
description: "List of YouTube channel URLs"
- name: days
type: number
required: false
default: 7
description: "Lookback period in days"
- name: tone
type: string
required: false
default: "professional"
description: "Writing tone for the blog post (professional, casual, technical)"
- name: output_dir
type: string
required: false
default: "./output"
description: "Directory to write output files"
outputs:
- name: blog_post
type: string
description: "Markdown blog post content"
- name: analytics_summary
type: object
description: "JSON object with per-creator analytics"
auth: ["youtube_api_key"]
tools_used: ["bash", "task"]
estimated_cost: "$2.50"
estimated_duration: "15 min"
---
# youtube-creator-analytics
## Instructions
### Step 1: Get recent videos for each creator
For each URL in `creator_urls`, execute sub-skill `youtube-channel-videos` with:
- `channel_url`: the creator URL
- `since_days`: `days` input value
Collect all video IDs and metadata.
### Step 2: Extract data (parallel)
For each video, run these sub-skills **in parallel** (they are independent):
- `youtube-transcript` with `url`: video URL
- `youtube-comments` with `video_id`: video ID, `max_comments`: 50
- `youtube-thumbnails` with `video_id`: video ID
### Step 3: Analyze content
Execute sub-skill `content-analyzer` with:
- `transcripts`: all transcripts from Step 2
- `comments`: all comments from Step 2
- `creator_metadata`: video metadata from Step 1
### Step 4: Generate blog post
Execute sub-skill `blog-post-writer` with:
- `analysis`: output from Step 3
- `tone`: `tone` input value
- `thumbnails`: thumbnail paths from Step 2
Write output to `output_dir`.
sub-skills.json
A flat mapping of sub-skill references. Skills never nest — they reference by ID.
{ "sub_skills": [ { "skill_id": "youtube-channel-videos", "role": "Fetch list of recent videos for a channel" }, { "skill_id": "youtube-transcript", "role": "Extract transcript from a single video" }, { "skill_id": "youtube-comments", "role": "Fetch top comments from a single video" }, { "skill_id": "youtube-thumbnails", "role": "Download thumbnail image for a single video" }, { "skill_id": "content-analyzer", "role": "Analyze transcripts and comments for trends" }, { "skill_id": "blog-post-writer", "role": "Generate a blog post from analysis" } ] }
A leaf skill (no sub-skills) has an empty array:
{ "sub_skills": [] }
Sub-skills can themselves reference other sub-skills. This creates a directed acyclic graph (DAG), not a tree:
youtube-creator-analytics
├── youtube-channel-videos
├── youtube-transcript
├── youtube-comments
├── youtube-thumbnails
├── content-analyzer
│ └── (references: text-summarizer) ← reused from another workflow
└── blog-post-writer
└── (references: text-summarizer) ← same skill, shared
benchmarks.json
Benchmarks serve as both unit tests and performance tracking. They grow over time — every test run and every production execution adds data. The local file keeps test definitions + the last 20 runs + aggregates. Full execution history is synced to the ctx0_skills table in Supabase for cross-daemon analytics.
{ "skill": "youtube-transcript", "version": 1, "tests": [ { "id": "test-001", "name": "Standard English video", "description": "Fetch transcript from a video with manual English captions", "inputs": { "url": "https://youtube.com/watch?v=dQw4w9WgXcQ", "language": "en", "include_timestamps": false }, "expected": { "success": true, "output_contains": ["never gonna give you up"], "caption_source": "manual", "max_duration_ms": 10000 } }, { "id": "test-002", "name": "Auto-generated captions", "description": "Video with only auto-generated captions", "inputs": { "url": "https://youtube.com/watch?v=example456", "language": "en" }, "expected": { "success": true, "caption_source": "auto-generated", "max_duration_ms": 15000 } }, { "id": "test-003", "name": "No captions available", "description": "Video without any captions — should fail gracefully", "inputs": { "url": "https://youtube.com/watch?v=nocaptions789" }, "expected": { "success": false, "error_contains": "no captions" } } ], "runs": [ { "run_id": "run-001", "test_id": "test-001", "timestamp": "2026-02-23T14:30:00Z", "source": "build", "daemon_id": "device-macbook", "duration_ms": 4200, "cost_usd": 0.00, "tokens": { "input": 0, "output": 0 }, "success": true, "output_size_bytes": 12400, "notes": "yt-dlp v2026.02.01, English manual captions" }, { "run_id": "run-002", "test_id": "test-001", "timestamp": "2026-02-24T10:00:00Z", "source": "test_window", "daemon_id": "device-macbook", "duration_ms": 3800, "cost_usd": 0.00, "tokens": { "input": 0, "output": 0 }, "success": true, "output_size_bytes": 12400 }, { "run_id": "run-003", "test_id": null, "timestamp": "2026-02-25T08:15:00Z", "source": "production", "daemon_id": "device-server", "duration_ms": 5100, "cost_usd": 0.00, "tokens": { "input": 0, "output": 0 }, "success": true, "triggered_by": "youtube-creator-analytics", "input_hash": "sha256:abc123" } ], "aggregates": { "total_runs": 3, "success_rate": 1.0, "avg_duration_ms": 4367, "avg_cost_usd": 0.00, "last_run": "2026-02-25T08:15:00Z" } }
How benchmarks grow:
- Build stage — Agent creates initial test cases and runs them.
source: "build". - Test window — User runs the skill with real inputs.
source: "test_window". - Production — Skill is executed in normal workflows.
source: "production".
Test expectations:
success— Did the skill complete without error?output_contains— Substring checks on output (lightweight assertions)max_duration_ms— Performance regression detectionerror_contains— For expected-failure test cases
plan.md
Documents the research and decisions from the Plan stage:
# youtube-transcript — Build Plan ## Research ### Approach evaluation 1. **YouTube Data API v3** — Provides captions via `captions.download` endpoint. Requires API key. Rate limited. Requires OAuth for non-public captions. Verdict: Overly complex for this use case. 2. **yt-dlp** — CLI tool, downloads auto-generated and manual subtitles. No API key needed. Widely used. Supports all languages. Verdict: Best approach. Free, reliable, no auth. 3. **youtube-transcript-api (Python)** — Python library wrapping YouTube's internal endpoint. No API key needed but less reliable than yt-dlp. Verdict: Backup option. ### Decision Use yt-dlp via shell command. Falls back gracefully when no captions exist. ## Implementation notes - yt-dlp outputs VTT format by default — parse and strip timestamps - Auto-generated captions include [Music] and [Applause] markers — strip these - Some videos have both manual and auto captions — prefer manual
Flat Skill Storage in Vault
All skills live at the same level. No nesting. Composition through references.
Local Filesystem
.bot0/skills/
├── youtube-transcript/
│ ├── SKILL.md
│ ├── plan.md
│ ├── benchmarks.json
│ ├── sub-skills.json (empty — leaf skill)
│ └── get-transcript.ts
│
├── youtube-comments/
│ ├── SKILL.md
│ ├── plan.md
│ ├── benchmarks.json
│ ├── sub-skills.json (empty — leaf skill)
│ └── fetch-comments.ts
│
├── youtube-thumbnails/
│ ├── SKILL.md
│ ├── plan.md
│ ├── benchmarks.json
│ ├── sub-skills.json (empty — leaf skill)
│ └── download-thumbnails.sh
│
├── youtube-channel-videos/
│ ├── SKILL.md
│ ├── plan.md
│ ├── benchmarks.json
│ ├── sub-skills.json (empty — leaf skill)
│ └── list-videos.ts
│
├── content-analyzer/
│ ├── SKILL.md
│ ├── plan.md
│ ├── benchmarks.json
│ ├── sub-skills.json (references: text-summarizer)
│ └── analyze-prompt.md
│
├── blog-post-writer/
│ ├── SKILL.md
│ ├── plan.md
│ ├── benchmarks.json
│ ├── sub-skills.json (references: text-summarizer)
│ └── template.md
│
├── text-summarizer/ (shared by content-analyzer and blog-post-writer)
│ ├── SKILL.md
│ ├── plan.md
│ ├── benchmarks.json
│ └── sub-skills.json (empty — leaf skill)
│
└── youtube-creator-analytics/ (orchestrator)
├── SKILL.md
├── plan.md
├── benchmarks.json
└── sub-skills.json (references all 6 sub-skills above)
Why Flat?
-
Reusability —
text-summarizeris used by bothcontent-analyzerandblog-post-writer. With nesting, it would be duplicated. Flat storage means single source of truth. -
Independent evolution — Each skill can be versioned, tested, and improved independently without affecting its parent.
-
Cross-workflow sharing — A
youtube-transcriptskill built for the analytics pipeline can be reused in a completely different workflow without extraction. -
Simple sync — Vault sync pushes flat skill directories. No recursive dependency resolution needed.
Vault & ctx0 Integration
Skills are first-class citizens in the ctx0 vault. They sync across all of a user's daemons via Supabase.
Storage Layers
┌─────────────────────────────────────────────────────────────────────────────┐
│ SKILL STORAGE LAYERS │
│ │
│ Layer 1: Local Filesystem │
│ ───────────────────────── │
│ .bot0/skills/<skill-name>/ ← Published skills (all same layout) │
│ .bot0/skill-plans/<timestamp>.md ← Skill builder session plans │
│ │
│ Layer 2: ctx0 Database (Supabase) │
│ ───────────────────────────────── │
│ ctx0_skills table ← Skill metadata, versioning, │
│ benchmark aggregates, notes │
│ ctx0_entries (entryType='skill') ← Vault entries for discovery │
│ │
│ Layer 3: ctx0 Storage Bucket │
│ ──────────────────────────── │
│ ctx0-vault/<user-id>/skills/<name>/ ← Skill artifacts (executables, etc.) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
ctx0_skills Table
CREATE TABLE ctx0_skills ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES users(id), -- Identity name TEXT NOT NULL, -- lowercase-hyphenated description TEXT NOT NULL, -- Human-readable trigger description version INTEGER NOT NULL DEFAULT 1, -- Incremented on each publish -- Content skill_md TEXT NOT NULL, -- Full SKILL.md content manifest JSONB NOT NULL DEFAULT '{}', -- Parsed frontmatter (inputs, outputs, auth, etc.) sub_skills JSONB DEFAULT '[]', -- Parsed sub-skills.json content -- Artifacts artifact_paths TEXT[], -- Relative paths to executable files storage_refs TEXT[], -- ctx0-vault bucket references -- Provenance source_session_id UUID, -- Session where skill was built source_daemon_id UUID REFERENCES ctx0_daemons(id), source_device_id TEXT, -- Benchmarks (aggregated from benchmarks.json) benchmarks JSONB DEFAULT '{}', -- Aggregated test/run data test_count INTEGER DEFAULT 0, -- Number of unit tests last_benchmark_at TIMESTAMPTZ, -- Execution stats (grows over time) total_executions INTEGER DEFAULT 0, total_cost_usd DOUBLE PRECISION DEFAULT 0, avg_duration_ms INTEGER, success_rate DOUBLE PRECISION, last_executed_at TIMESTAMPTZ, -- Improvement notes improvement_notes JSONB DEFAULT '[]', -- Agent-documented issues (see Skill Improvement Notes) -- Tags tags TEXT[], -- Status is_published BOOLEAN DEFAULT false, is_active BOOLEAN DEFAULT true, -- Timestamps created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(user_id, name, version) ); CREATE INDEX idx_skills_user ON ctx0_skills(user_id); CREATE INDEX idx_skills_name ON ctx0_skills(user_id, name); CREATE INDEX idx_skills_published ON ctx0_skills(user_id, is_published) WHERE is_published = true; CREATE INDEX idx_skills_tags ON ctx0_skills USING GIN(tags);
Sync Flow
┌─────────────────────────────────────────────────────────────────────────────┐
│ SKILL SYNC FLOW │
│ │
│ Daemon A (MacBook) — builds skill │
│ │ │
│ │ 1. User publishes skill │
│ │ 2. Daemon writes: │
│ │ • ctx0_skills row (metadata, SKILL.md, manifest, benchmarks) │
│ │ • ctx0-vault bucket (executable files) │
│ │ • ctx0_entries row (vault entry, entryType='skill') │
│ │ │
│ ▼ │
│ Supabase (source of truth) │
│ │ │
│ ▼ │
│ Daemon B (Server) Daemon C (Office PC) │
│ │ │ │
│ │ On skill discovery: │ On skill discovery: │
│ │ 1. Query ctx0_skills │ 1. Query ctx0_skills │
│ │ 2. Download artifacts │ 2. Download artifacts │
│ │ 3. Write to .bot0/skills/ │ 3. Write to .bot0/skills/ │
│ │ 4. Clear skill cache │ 4. Clear skill cache │
│ │ │ │
│ │ Skill available locally │ Skill available locally │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Vault Entry
Skills appear in the ctx0 vault under /skills/ (flat):
/skills/ (system folder, locked)
├── youtube-transcript/ (entryType: 'skill')
├── youtube-comments/
├── youtube-thumbnails/
├── youtube-channel-videos/
├── content-analyzer/
├── blog-post-writer/
├── text-summarizer/
└── youtube-creator-analytics/ (same level as its sub-skills)
Approach Selection Decision Tree
When the agent builds a skill step, it follows this decision tree to choose the cheapest, most reliable approach:
┌─────────────────────────────────────────────────────────────────────────────┐
│ APPROACH SELECTION DECISION TREE │
│ │
│ "I need to get YouTube transcripts" │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 1. Does an API exist? │ │
│ │ • Search docs, web, npm/pypi │ │
│ │ • Check for official SDK │ │
│ └──────────┬──────────────────────────┘ │
│ │ │
│ YES │ NO │
│ ▼ ▼ │
│ ┌──────┐ ┌─────────────────────────────────────┐ │
│ │ Use │ │ 2. Can deterministic code do it? │ │
│ │ API │ │ • CLI tools (yt-dlp, ffmpeg) │ │
│ │ $0 │ │ • Parsing, scraping structured │ │
│ └──────┘ │ • Data transformation │ │
│ └──────────┬──────────────────────────┘ │
│ │ │
│ YES │ NO │
│ ▼ ▼ │
│ ┌──────┐ ┌─────────────────────────────────────┐ │
│ │ Use │ │ 3. Can web fetch get the data? │ │
│ │ code │ │ • Public web pages │ │
│ │ $0 │ │ • RSS feeds, sitemaps │ │
│ └──────┘ │ • JSON endpoints │ │
│ └──────────┬──────────────────────────┘ │
│ │ │
│ YES │ NO │
│ ▼ ▼ │
│ ┌──────┐ ┌─────────────────────────────────────┐ │
│ │ Use │ │ 4. Can dom0 automate it? │ │
│ │ web │ │ • Browser workflow with refs │ │
│ │fetch │ │ • Login → navigate → extract │ │
│ │ $0 │ │ • Deterministic DOM interaction │ │
│ └──────┘ └──────────┬──────────────────────────┘ │
│ │ │
│ YES │ NO │
│ ▼ ▼ │
│ ┌──────┐ ┌─────────────────────────────┐ │
│ │ Use │ │ 5. Use cmd0 (last resort) │ │
│ │ dom0 │ │ • Visual grounding loop │ │
│ │ ~$0 │ │ • screenshot→ground→click│ │
│ └──────┘ │ • Cache for replay │ │
│ │ • Build custom CLI tool │ │
│ └─────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Use cmd0 │ │
│ │ ~$0.01+ │ │
│ └──────────┘ │
│ │
│ At any level: if the step requires ANALYSIS or DECISIONS, │
│ wrap it in an agentic step that uses bot0 with a targeted prompt. │
│ Keep the LLM call focused — pass only the data it needs. │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
When to Use Agentic Steps
Some skill steps require intelligence — analysis, synthesis, subjective decisions. These steps should:
- Be clearly marked as agentic in the plan and SKILL.md
- Receive only the data they need (not the entire pipeline context)
- Use the cheapest capable model (Haiku for simple classification, Sonnet for writing)
- Have their cost tracked separately in benchmarks
- Be isolated into their own sub-skill (determinism boundary)
Runtime Execution Model
When a skill is executed — either manually via /skill-name or programmatically via execute_skill — the daemon follows a specific execution flow.
How execute_skill Works
┌─────────────────────────────────────────────────────────────────────────────┐
│ SKILL EXECUTION FLOW │
│ │
│ execute_skill("youtube-transcript", { url: "...", language: "en" }) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 1. RESOLVE │ │
│ │ │ │
│ │ • Find skill in .bot0/skills/ │ │
│ │ • Parse SKILL.md frontmatter │ │
│ │ • Load sub-skills.json │ │
│ │ • Validate inputs against schema │ │
│ │ (required fields, types) │ │
│ └──────────────┬──────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 2. SPAWN SUB-AGENT │ │
│ │ │ │
│ │ Create a child agent (via task │ │
│ │ tool pattern) with: │ │
│ │ │ │
│ │ • System prompt = SKILL.md content │ │
│ │ + companion files from skill dir │ │
│ │ • Input variables injected as │ │
│ │ context: "Inputs: url=..., │ │
│ │ language=en" │ │
│ │ • Model override if specified │ │
│ │ • Tool access per skill manifest │ │
│ └──────────────┬──────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 3. EXECUTE │ │
│ │ │ │
│ │ Sub-agent follows the Instructions │ │
│ │ section of SKILL.md step by step: │ │
│ │ │ │
│ │ • Deterministic steps: runs scripts │ │
│ │ via bash with input variables │ │
│ │ • Sub-skill steps: calls │ │
│ │ execute_skill recursively │ │
│ │ • Agentic steps: uses LLM reasoning │ │
│ │ with the data collected so far │ │
│ │ │ │
│ │ Skill files are READ-ONLY — on │ │
│ │ failure, agent copies executable │ │
│ │ to /tmp, patches the copy, runs it │ │
│ │ │ │
│ │ Resource semaphores apply: dom0/ │ │
│ │ cmd0 commands acquire locks │ │
│ └──────────────┬──────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 4. COLLECT OUTPUT │ │
│ │ │ │
│ │ Sub-agent returns structured output │ │
│ │ matching the SKILL.md outputs spec │ │
│ │ │ │
│ │ Benchmark data recorded: │ │
│ │ • Duration, cost, tokens │ │
│ │ • Success/failure + error │ │
│ │ • Appended to benchmarks.json │ │
│ └─────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Input Passing
Inputs are injected into the sub-agent's context as structured data. The agent reads the SKILL.md Instructions and substitutes variable references:
SKILL.md says: "Run get-transcript.ts --url ${url} --lang ${language}"
Agent receives: inputs = { url: "https://...", language: "en" }
Agent executes: bash("npx tsx get-transcript.ts --url 'https://...' --lang 'en'")
For sub-skill invocations, the agent maps outputs from one step to inputs of the next:
Step 1 output: { video_ids: ["abc", "def"] }
Step 2 instruction: "For each video, execute youtube-transcript with url: video URL"
Agent: execute_skill("youtube-transcript", { url: "https://youtube.com/watch?v=abc" })
execute_skill("youtube-transcript", { url: "https://youtube.com/watch?v=def" })
Error Handling at the Orchestrator Level
When a sub-skill fails during orchestrated execution:
- Transient errors (rate limits, timeouts, network) — Retry with exponential backoff (max 3 retries)
- Permanent errors (auth failure, missing dependency, invalid input) — Log the error, skip the failing item, continue with remaining items if the orchestrator is processing a batch
- Critical failures (every item in a batch fails, required step produces no output) — Abort the pipeline, report which step failed and why
The orchestrator SKILL.md can specify failure behavior per step:
### Step 2: Extract data (parallel) For each video, run these sub-skills in parallel: - `youtube-transcript` — **on failure: skip video, continue** - `youtube-comments` — **on failure: skip video, continue** ### Step 3: Analyze content Execute `content-analyzer` — **on failure: abort pipeline**
Caching for Deterministic Skills
Deterministic skills (API calls, CLI tools, data transforms) can cache outputs by input hash:
- Before execution, compute
SHA-256(JSON.stringify(sortedInputs)) - Check
.bot0/skill-cache/<skill-name>/<hash>.json - If cache hit and not expired (configurable TTL, default 1 hour): return cached output
- If cache miss: execute normally, write output to cache
Cache is opt-in via SKILL.md frontmatter:
cache: enabled: true ttl: 3600 # seconds (default: 1 hour)
Agentic skills should not enable caching — their outputs depend on LLM reasoning which may vary.
Skill Improvement Notes
When a skill fails during execution, the agent doesn't try to auto-repair it. Instead, it documents the issue so the user can fix it later in skill builder mode. This is simpler than a full self-healing system and keeps the user in control.
How It Works
┌─────────────────────────────────────────────────────────────────────────────┐
│ SKILL IMPROVEMENT NOTES FLOW │
│ │
│ Agent executes skill │
│ │ │
│ │ Skill fails or produces poor results │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 1. WORK AROUND IT │ │
│ │ │ │
│ │ Agent tries to complete the user's │ │
│ │ task despite the skill failure: │ │
│ │ • Retry with different parameters │ │
│ │ • Fall back to manual approach │ │
│ │ • Skip the failing step if possible │ │
│ └──────────────┬──────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 2. DOCUMENT THE ISSUE │ │
│ │ │ │
│ │ Agent calls skill_improvement_note: │ │
│ │ │ │
│ │ • Which skill failed │ │
│ │ • Which step failed │ │
│ │ • Error message / stack trace │ │
│ │ • What inputs caused the failure │ │
│ │ • Agent's diagnosis of root cause │ │
│ │ • Suggested fix │ │
│ └──────────────┬──────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 3. USER REVIEWS LATER │ │
│ │ │ │
│ │ User sees notification badge on │ │
│ │ skill in the skills panel │ │
│ │ │ │
│ │ Options: │ │
│ │ • Open in skill builder with the │ │
│ │ improvement note pre-loaded │ │
│ │ • Dismiss the note │ │
│ │ • Mark as "won't fix" │ │
│ └─────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
skill_improvement_note Tool
A new tool available to agents during skill execution:
{ name: "skill_improvement_note", description: "Document an issue with a skill for the user to fix later", inputSchema: { type: "object", properties: { skill_name: { type: "string" }, step: { type: "string", description: "Which SKILL.md step failed" }, error: { type: "string", description: "Error message or description" }, diagnosis: { type: "string", description: "Root cause analysis" }, suggested_fix: { type: "string", description: "How to fix this" }, inputs: { type: "object", description: "Inputs that triggered the failure" } }, required: ["skill_name", "error", "diagnosis", "suggested_fix"] } }
Storage
Improvement notes are stored in ctx0_skills.improvement_notes (JSONB array) and synced across daemons:
interface SkillImprovementNote { id: string; timestamp: string; sessionId: string; daemonId: string; // What happened step?: string; error: string; inputs?: Record<string, unknown>; // Agent's analysis diagnosis: string; suggestedFix: string; // Status status: 'open' | 'dismissed' | 'resolved'; resolvedAt?: string; }
Example note:
{ "id": "note-001", "timestamp": "2026-02-25T08:15:00Z", "sessionId": "abc-123", "daemonId": "device-server", "step": "Step 2: Fetch captions", "error": "yt-dlp: ERROR: [youtube] abc123: Video unavailable. This video has been removed.", "inputs": { "url": "https://youtube.com/watch?v=abc123" }, "diagnosis": "The get-transcript.ts script does not handle deleted/private videos. It throws an unhandled yt-dlp error instead of returning a structured failure.", "suggestedFix": "Add error handling in get-transcript.ts: catch yt-dlp exit code 1, parse stderr for 'Video unavailable', return { success: false, error: 'video_unavailable' } instead of throwing.", "status": "open" }
This approach captures 80% of the value of full self-healing (the diagnosis and fix are documented) with 10% of the complexity (no forking, no patching, no PR review UI). The user stays in control and can address issues when convenient.
Full auto-repair (agent forks, fixes, tests, and submits PRs) can be built later as an evolution of this pattern, once improvement notes prove the diagnosis quality is reliable enough to trust automated fixes.
Skill Builder Agent Behavior
Modularization Heuristics
The agent follows these rules when deciding how to modularize:
-
Single-responsibility check: Each skill should do exactly one thing. If you can describe it with "and", split it.
-
Reusability check: Ask "Would this be useful in a completely different workflow?" If yes, extract it.
-
Input/output boundary: Each skill should have clear, typed inputs and outputs. No side effects beyond the declared outputs.
-
Determinism boundary: Separate deterministic steps (API calls, data transforms) from agentic steps (analysis, writing). Never mix them in one skill.
-
Auth boundary: Skills requiring different credentials should be separate.
-
Inline vs. extract: Keep orchestration logic (loops, conditionals, data routing) in the orchestrator SKILL.md instructions. Only extract reusable computation into sub-skills.
Research Before Building
Before implementing any skill step, the agent must:
-
Search for existing APIs:
web_search("YouTube transcript API 2026") web_search("npm youtube captions download") -
Check for existing skills:
List .bot0/skills/ for similar functionality Query ctx0_skills for matching names/tags -
Evaluate approach cost:
- API call: typically $0 (free tier) to $0.001
- CLI tool: $0
- dom0 workflow: ~$0 (no LLM cost, just time)
- cmd0 workflow: ~$0.01+ (grounding model call per interaction)
- LLM analysis: $0.001-$0.10 depending on model and context size
Question-Asking Protocol
The agent should ask questions rather than guess when it encounters:
- Ambiguous preferences: "What tone should the blog post use?"
- Missing context: "Which YouTube creators should be included?"
- Format decisions: "Should the blog post include embedded images or just links?"
- Authentication: "Do you have a YouTube Data API key, or should I use yt-dlp?"
Questions are asked via the ask_question tool, which presents options in the desktop UI.
Decomposition Example
User request:
"I have 50+ YouTube creators. I need analytics on each video — transcripts, thumbnails, comments. Then bundle the last 7 days and write a blog post for my site and newsletter."
Agent's Decomposition
┌─────────────────────────────────────────────────────────────────────────────┐
│ SKILL DECOMPOSITION │
│ │
│ All skills stored flat in .bot0/skills/ : │
│ │
│ youtube-creator-analytics (orchestrator) │
│ ├── sub-skills.json references: │
│ │ ├── youtube-channel-videos Approach: API (YouTube Data API) │
│ │ ├── youtube-transcript Approach: CLI (yt-dlp) │
│ │ ├── youtube-comments Approach: API (YouTube Data API) │
│ │ ├── youtube-thumbnails Approach: Code (curl) │
│ │ ├── content-analyzer Approach: Agentic (LLM) │
│ │ └── blog-post-writer Approach: Agentic (LLM) │
│ │ │
│ │ Each is an independent skill with its own: │
│ │ SKILL.md, plan.md, benchmarks.json, sub-skills.json │
│ │ │
│ Build parallelism: │
│ ├── PARALLEL: transcript, comments, thumbnails, channel-videos │
│ │ (no data dependencies between them) │
│ ├── SEQUENTIAL: content-analyzer (needs transcript + comments output) │
│ └── SEQUENTIAL: blog-post-writer (needs analysis output) │
│ │
│ NOT extracted as skills (kept inline in orchestrator): │
│ • Loop over creators — orchestration logic │
│ • Filter to last 7 days — date comparison │
│ • Send newsletter — depends on user's platform │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Phase 1: Skill Builder Mode (Implemented)
Phase 1 provides the interactive skill building experience within the desktop app.
What's Implemented
| Component | Status | Description |
|---|---|---|
skill_builder agent mode | Done | Custom system prompt with skill planning/build lifecycle tools |
skill_builder_enter tool | Done | Creates skill plan file with YAML frontmatter |
skill_builder_update_plan tool | Done | Documents steps, accumulates tools/auth metadata |
skill_builder_exit tool | Done | Cancel or trigger build |
compileSkill() | Done | Compiles plan → SKILL.md in .bot0/skills/ |
SkillPlanPanel | Done | Real-time side panel showing plan progress |
SkillBuildDialog | Done | Build mode and target daemon selection |
| Mode control split (UX update) | Done | Shift+Tab cycles only ask/auto/plan; Skill Builder is a separate toggle |
| Skill plan persistence | Done | Local JSON + cloud ctx0_session_plans |
| Skill plan restore on resume | Done | Reads file content, restores side panel |
| Resizable side panel | Done | 20-60% width, persisted to localStorage |
| Skill discovery & parsing | Done | Scans .bot0/skills/, caches with 5s TTL |
Skill loading via /skill-name | Done | Injects SKILL.md + companion files into context |
What Needs Enhancement (Phase 1.5)
| Component | Status | Description |
|---|---|---|
| Uniform skill structure | Planned | Migrate from single SKILL.md to full directory layout |
| Typed inputs/outputs in SKILL.md | Planned | Frontmatter inputs and outputs arrays |
| sub-skills.json generation | Planned | Compiler creates sub-skill references |
| benchmarks.json with unit tests | Planned | Agent creates test cases during build |
| Parallel sub-agent building | Planned | Task tool spawns concurrent builders |
Test window (/test-skill) | Planned | Separate session with skill pre-loaded |
| Plan stage lock | Planned | Agent stays in plan until user approves |
| Build stage from plan | Planned | Agent follows plan, no re-research |
Phase 2: Execution Tracking & Skill Management
2.1 Execution Tracking
Every skill execution records to benchmarks.json:
interface SkillRun { run_id: string; test_id: string | null; // If running a specific test case timestamp: string; source: 'build' | 'test_window' | 'production'; daemon_id: string; duration_ms: number; cost_usd: number; tokens: { input: number; output: number }; success: boolean; error?: string; triggered_by?: string; // Parent skill that invoked this input_hash?: string; // For deduplication }
Production runs flow to both local benchmarks.json and ctx0_skills aggregates in Supabase.
2.2 Model Overrides in SKILL.md
Skills can specify which model to use for agentic steps:
model: claude-haiku-4-5-20251001 model_override: true
When model_override: true, the skill executor uses the specified model regardless of the daemon's default.
2.3 execute_skill Tool
A new daemon tool that executes a published skill programmatically:
{ name: "execute_skill", description: "Execute a published skill by name with given inputs", inputSchema: { type: "object", properties: { name: { type: "string", description: "Skill name" }, inputs: { type: "object", description: "Typed input parameters" }, model: { type: "string", description: "Optional model override" } }, required: ["name"] } }
This enables orchestrator skills to invoke sub-skills programmatically with full benchmark tracking.
2.4 Execution History UI
The NetworkSidebar's "Skills" button opens a panel showing:
- List of published skills with execution stats (runs, cost, success rate)
- Pending improvement notes with notification badge
- Drill-down into individual executions with cost breakdown
- Benchmark trend charts (cost and duration over time)
- Quick actions: test, edit (opens skill builder), disable, delete
2.5 Skill Versioning
When a skill is re-built (including from improvement notes), the version increments:
ctx0_skills.versionincrements- Previous version remains accessible (rollback)
- Orchestrators can pin to a specific version or use
latest - Benchmark comparison across versions
Phase 3: Remote Skills & Network Execution
3.1 Remote Skill Building
A desktop connected to daemon A can initiate skill building on daemon B:
Desktop (local) ──hub:task_submit──► Daemon B (remote server)
│
├── Enter skill_builder mode
├── Build skill with server's tools
├── Stream plan updates back
│
◄──hub:task_progress── │ (real-time plan panel updates)
◄──hub:task_result─── │
3.2 Daemon Capabilities Announcements
Daemons announce their capabilities to enable intelligent skill routing:
interface DaemonCapabilities { deviceId: string; daemonId: string; platform: 'macos' | 'windows' | 'linux'; hasGpu: boolean; hasBrowser: boolean; hasDesktop: boolean; tools: string[]; software: Array<{ name: string; version?: string }>; skills: Array<{ name: string; version: number; successRate?: number }>; role: 'daily-driver' | 'agent'; isOnline: boolean; }
3.3 Cross-Machine Skill Execution
Orchestrators can route sub-skills to the best available daemon:
┌─────────────────────────────────────────────────────────────────────────────┐
│ CROSS-MACHINE SKILL EXECUTION │
│ │
│ Orchestrator (Daemon A — MacBook) │
│ │ │
│ ├── youtube-transcript → Execute locally (yt-dlp installed) │
│ ├── youtube-comments → Execute locally (API call, no deps) │
│ ├── content-analyzer → Route to Daemon B (GPU server, faster LLM) │
│ └── blog-post-writer → Execute locally (user preferences here) │
│ │
│ Routing decision factors: │
│ 1. Required tools/software available? │
│ 2. Target daemon online? │
│ 3. Role appropriate? (agent > daily-driver) │
│ 4. Skill needs local context (files, browser state)? │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
3.4 Auto-Optimizer
The system identifies optimization opportunities:
- Cost reduction: "This skill uses Sonnet but the task is simple — switch to Haiku?"
- Caching: "This skill fetched the same URL 10 times today — add caching?"
- Parallelization: "These 3 sub-skills are independent — run them in parallel?"
- Skill reuse: "You built
get-youtube-transcriptbutyoutube-transcriptalready exists"
Security Considerations
Published Skills Are Read-Only
Published skill files (everything inside .bot0/skills/<skill-name>/) are read-only during execution. The executing agent cannot modify them. This is enforced at the tool middleware level — any file edit targeting a published skill path is rejected with a message directing the agent to use skill_improvement_note instead.
Why read-only?
- Published skills are synced to Supabase and shared across daemons. A local edit would drift from the canonical version.
- Quick fixes during execution bypass the skill builder flow — no plan review, no testing, no version increment.
- The agent's "fix" might be a hack that papers over the real issue.
What the agent does when a skill executable fails:
┌─────────────────────────────────────────────────────────────────────────────┐
│ SKILL FAILURE DURING EXECUTION │
│ │
│ Agent runs get-transcript.ts from published skill │
│ │ │
│ │ Script throws: "yt-dlp: ERROR: Video unavailable" │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 1. READ the published file │ │
│ │ (read is allowed, edit is not) │ │
│ └──────────────┬──────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 2. COPY to temp location │ │
│ │ │ │
│ │ cp .bot0/skills/youtube- │ │
│ │ transcript/get-transcript.ts │ │
│ │ /tmp/get-transcript-fix.ts │ │
│ └──────────────┬──────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 3. EDIT the copy │ │
│ │ │ │
│ │ Add error handling for deleted │ │
│ │ videos in the temp copy │ │
│ └──────────────┬──────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 4. RUN the patched copy │ │
│ │ │ │
│ │ npx tsx /tmp/get-transcript- │ │
│ │ fix.ts --url "..." │ │
│ │ │ │
│ │ User's task completes │ │
│ └──────────────┬──────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 5. DOCUMENT via improvement note │ │
│ │ │ │
│ │ skill_improvement_note({ │ │
│ │ skill_name: "youtube- │ │
│ │ transcript", │ │
│ │ step: "Step 2: Fetch │ │
│ │ captions", │ │
│ │ error: "Unhandled yt-dlp │ │
│ │ error for deleted videos", │ │
│ │ diagnosis: "get-transcript.ts │ │
│ │ doesn't catch exit code 1", │ │
│ │ suggested_fix: "Add try/catch │ │
│ │ for yt-dlp subprocess..." │ │
│ │ }) │ │
│ └─────────────────────────────────────┘ │
│ │
│ Published skill untouched. User fixes it later in skill builder. │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Middleware enforcement:
function isPublishedSkillPath(filePath: string): boolean { const skillsDir = path.join(os.homedir(), '.bot0', 'skills'); return filePath.startsWith(skillsDir); } // In file edit middleware: if (isPublishedSkillPath(targetPath) && !isSkillBuilderMode()) { return "Cannot edit published skill files during execution. " + "Published skills are read-only. To work around the issue: " + "1) Copy the file to a temp location, 2) Edit and run the copy, " + "3) Use skill_improvement_note to document the fix needed."; }
Skill Execution Sandboxing
- Permission engine applies to all tool calls within skill execution
- In
askmode, each file write or shell command requires user approval - In
automode, dangerous commands are still flagged - Skills cannot escalate permissions beyond the daemon's configured mode
Credential Isolation
- Skills declare required auth in their manifest (
auth: ["youtube_api_key"]) - Credentials are never stored in skill files — resolved at runtime via the proxy
- Sub-skills cannot access credentials they didn't declare
Remote Execution Trust
- Only daemons belonging to the same user can exchange skills
- Hub enforces cross-user isolation at the message routing level
- Device signatures verify daemon identity on every request
- Skills downloaded from Supabase are integrity-checked against stored hashes
Improvement Notes Safety
- Improvement notes are informational — they never auto-modify skill code
- Notes are visible to the user before any changes are made
- The user decides when and how to address each note
- Fixing a note goes through the normal skill builder flow (plan → build → test)
File & Component Reference
Daemon (packages/daemon/src/)
| File | Purpose |
|---|---|
agent/agents.ts | Agent definitions including skill_builder |
tools/skill-builder.ts | skill_builder_enter, _update_plan, _exit tools |
tools/skill-compiler.ts | compileSkill() — plan → skill directory |
tools/skill.ts | skill tool — loads SKILL.md into agent context |
tools/plan.ts | Plan state persistence (local + cloud) |
skill/discovery.ts | Scan directories for published skills |
skill/parse.ts | Parse SKILL.md frontmatter and content |
skill/types.ts | SkillInfo interface |
ipc/server.ts | handleBuildSkill — IPC bridge for compilation |
hub/client.ts | Remote skill building via Hub relay |
Desktop (packages/desktop/src/)
| File | Purpose |
|---|---|
components/Terminal/Terminal.tsx | Skill state management, mode controls (including planned Skill Builder toggle), and plan restore |
components/Terminal/SkillPlanPanel.tsx | Real-time side panel for skill plan viewing |
components/Terminal/SkillBuildDialog.tsx | Build mode and target daemon selection |
Database (packages/ctx0/src/)
| File | Purpose |
|---|---|
schema/skills.ts | ctx0_skills table definition (new) |
schema/sessions.ts | skill_name column on messages |
schema/index.ts | ctx0Tables array (add ctx0Skills) |
system-entries/folders.ts | /skills/ system folder |
types/entry.ts | 'skill' entry type |
Implementation Roadmap
Phase 1 — Skill Builder Mode (Done)
Interactive skill building with plan documentation, side panel, and single-skill compilation.
Phase 1.5 — Enhanced Skill Structure
| Task | Priority | Complexity |
|---|---|---|
| Uniform skill directory structure | High | Medium |
| Typed inputs/outputs in SKILL.md | High | Medium |
| sub-skills.json generation in compiler | High | Low |
| benchmarks.json with unit tests | High | Medium |
| Parallel sub-agent building via task tool | High | Medium |
Test window (/test-skill command) | High | High |
| Plan stage gate (user approval before build) | Medium | Low |
| plan.md generation from skill builder notes | Medium | Low |
Phase 2 — Execution & Management
| Task | Priority | Complexity |
|---|---|---|
ctx0_skills table + Drizzle schema | High | Medium |
| Skill publish flow (local → Supabase → vault) | High | High |
execute_skill tool | High | Medium |
| Production execution tracking in benchmarks.json | High | Medium |
skill_improvement_note tool | High | Medium |
| Improvement notes UI in skills panel | Medium | Medium |
| Model overrides in SKILL.md | Medium | Low |
| Skill management UI in NetworkSidebar | Medium | High |
| Skill versioning (increment, rollback, pin) | Medium | Medium |
| Benchmark trend charts | Low | Medium |
Phase 3 — Network & Optimization
| Task | Priority | Complexity |
|---|---|---|
| Remote skill building via Hub | High | High |
| Daemon capabilities announcements | High | Medium |
| Skill sync across daemons (publish → pull) | High | High |
| Cross-machine skill routing | Medium | High |
| Auto-optimizer suggestions | Low | High |
| Skill marketplace (Bytespace) | Low | Very High |
Related Documentation
- bot0 System Architecture — Core system design
- ctx0 Skills (DB-as-Skill) — Database query skills
- ctx0 System Architecture — Vault and entry system
- dom0 System Architecture — Browser automation
- bot0 Security — Security model and threat mitigation
- Bytespace Proxy — Credential proxy architecture
- bot0 Remote Daemon — Hub relay and remote tasks