{"owner":"obot-platform","repo":"nanobot","hasSkills":true,"totalSkillsCount":17,"totalTokensCount":15828,"categories":["claude-rule","anthropic-skill","subagent-persona"],"hasMcp":false,"mcpConfig":null,"found":["CLAUDE.md","docs/agents/schemas/agent.frontmatter.schema.json","docs/agents/schemas/mcp.schema.json","docs/agents/schemas/skill.frontmatter.schema.json","docs/agents/schemas/task.frontmatter.schema.json","docs/agents/schemas/task.step.frontmatter.schema.json","pkg/config/testdata/directory-with-readme/agents/README.md","pkg/servers/system/skills/browser-use.md","pkg/servers/system/skills/python-scripts.md","pkg/servers/system/skills/scheduled-tasks.md","pkg/servers/system/skills/workflows.md","pkg/servers/system/testdata/with-override/skills/workflows.md","pkg/servers/system/testdata/with-user-skills/skills/my-custom-skill.md","pkg/servers/system/testdata/with-user-skills/skills/user-skill.md","pkg/servers/workflows/testdata/with-workflows/workflows/another/SKILL.md","pkg/servers/workflows/testdata/with-workflows/workflows/no-description/SKILL.md","pkg/servers/workflows/testdata/with-workflows/workflows/test-workflow/SKILL.md"],"skills":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nNanobot is a standalone MCP (Model Context Protocol) host that enables building agents with MCP and MCP-UI. Unlike built-in MCP hosts in applications like VSCode, Claude, or ChatGPT, Nanobot is designed to be an open-source, deployable solution that combines MCP servers with LLMs to create agent experiences through external interfaces such as Obot. The project is written in Go.\n\n**Technology Stack:**\n- Go 1.26.0 with GORM (SQLite, MySQL, PostgreSQL) and goja (JavaScript runtime for hooks)\n\n## Build and Development Commands\n\n### Backend (Go)\n\n```bash\n# Build the nanobot binary\nmake\n\n# Run nanobot with a configuration file\n./bin/nanobot run ./nanobot.yaml\n\n# Run Go tests\ngo test ./...\n\n# Run a specific test\ngo test ./pkg/agents -run TestName\n\n# Format Go code\ngofmt -w .\n```\n\n## Architecture Overview\n\n### Backend Architecture\n\n**Core Components:**\n\n- **Runtime (`pkg/runtime/`)** - Main orchestration layer that initializes the system. Creates and wires together the LLM client, tool service, agents, and sampling components. Manages the overall execution environment.\n\n- **Agents (`pkg/agents/`)** - Agent execution engine that handles tool mapping, request population, and agent interactions. Responsible for running agents backed by LLMs with access to tools.\n\n- **Tools Service (`pkg/tools/`)** - Central registry for tools and agents. Handles tool discovery, mapping, and execution delegation. Manages MCP server connections.\n\n- **MCP Layer (`pkg/mcp/`)** - MCP protocol implementation including sessions, clients, servers, and wire protocols. Handles both stdio and HTTP transports. Key types:\n  - `Session` - Manages MCP session lifecycle and message routing\n  - `Client` - MCP client implementation for connecting to servers\n  - `Wire` - Protocol transport abstraction (stdio/HTTP)\n\n- **LLM Integration (`pkg/llm/`)** - Abstraction over different LLM providers (OpenAI, Anthropic). Routes requests to appropriate providers based on model names. Handles both completion and response APIs.\n\n- **Session Management (`pkg/session/`, `pkg/sessiondata/`)** - Manages user sessions, conversation state, and session-scoped data. Handles agent context, tool mappings, and resource mappings within sessions. Supports parent-child session relationships and database-backed OAuth token storage.\n\n- **Server Layer (`pkg/server/`)** - HTTP server handling MCP protocol over HTTP. Routes requests for initialize, tools/list, tools/call, prompts/*, resources/*, etc. Manages session creation and request routing.\n\n- **Built-in MCP Servers (`pkg/servers/`)** - Nanobot includes several built-in MCP servers:\n  - `agent/` - Exposes individual agents as MCP servers with chat capabilities\n  - `capabilities/` - Session initialization and capability management (workspace setup)\n  - `meta/` - Metadata and introspection tools (list_chats, update_chat, list_agents)\n  - `resources/` - Database-backed resource management (create_resource, delete_resource) with automatic mimetype detection\n  - `workspace/` - Workspace and session management (create/update/delete workspaces, session reading)\n\n- **Configuration (`pkg/config/`)** - YAML-based configuration loading and validation. Supports profiles, extends (inheritance), and environment variables. See `pkg/config/schema.yaml` for the complete schema.\n\n**Key Architectural Patterns:**\n\n- **Tool Mappings** - Tools from MCP servers are mapped to agent-accessible tools. The `BuildToolMappings` method creates this mapping by resolving tool references from agents and MCP servers.\n\n- **Hooks** - Lifecycle hooks for agents and MCP servers (config, request, response). Hooks are TypeScript/JavaScript functions that can modify configuration and messages. See `hooks.ts` for type definitions.\n\n- **Sandboxing** - MCP servers can run in Docker containers for isolation. The `pkg/mcp/sandbox/` handles containerization and port mapping.\n\nNanobot does not bundle a frontend. External UIs communicate through MCP endpoints, `/api/events`, and the browser proxy routes.\n\n## Configuration\n\nConfiguration is YAML-based. Key top-level sections:\n\n- `agents` - Define agents with their models, tools, instructions, and behaviors\n- `mcp-servers` - MCP server configurations (command, URL, Docker image, etc.)\n- `prompts` - Template definitions\n- `publish` - Defines what to expose when Nanobot itself acts as an MCP server\n- `env` - Environment variable definitions with descriptions and defaults\n- `auth` - Authentication configuration (OAuth, remote headers)\n- `profiles` - Configuration profiles for different environments\n- `extends` - Inherit from other configuration files\n\nExample minimal configuration:\n\n```yaml\nagents:\n  myagent:\n    name: My Agent\n    model: gpt-4\n    mcpServers: my-mcp-server\n\nmcpServers:\n  my-mcp-server:\n    url: https://example.com/mcp\n```\n\n## Important Go Packages\n\n- `pkg/types/` - Core type definitions shared across the system (Config, Agent, Message, ToolCall, etc.)\n- `pkg/complete/` - Utility package for handling option completion and merging\n- `pkg/expr/` - Expression evaluation for dynamic values in configurations\n- `pkg/schema/` - JSON Schema validation and manipulation\n- `pkg/supervise/` - Process supervision for running MCP server subprocesses\n- `pkg/sampling/` - Handles MCP sampling requests (LLM-in-the-loop)\n- `pkg/envvar/` - Environment variable handling with descriptions and defaults\n- `pkg/cmd/` - CLI command handling (routes from `main.go`)\n\n## Entry Points and Special Modes\n\n**Main Entry Point:** `main.go` routes commands to `pkg/cmd`\n\n**Special `_exec` Mode:** Nanobot can act as a daemon wrapper for MCP server subprocesses. When invoked with `_exec` as the first argument, it handles stdio piping and process lifecycle management for MCP servers. This enables Nanobot to supervise and manage MCP server processes.\n\n## Testing\n\nGo tests follow standard Go conventions:\n- Test files are named `*_test.go`\n- Run all tests: `go test ./...`\n- Run specific package tests: `go test ./pkg/agents`\n- Run specific test: `go test ./pkg/agents -run TestName`\n\n## Common Patterns\n\n### Adding a New Agent Hook\n\n1. Define TypeScript types in `hooks.ts` (root level)\n2. Update corresponding Go types in `pkg/types/hooks.go`\n3. Implement hook handling in `pkg/agents/` or relevant package\n4. Hook execution is managed through `pkg/mcp/hooks.go`\n\n### Adding a New Tool\n\n1. Tools come from MCP servers (external or built-in servers in `pkg/servers/`)\n2. Tool resolution happens in `pkg/tools/service.go`\n\n### Working with Sessions\n\n- Sessions are scoped to MCP connections\n- Use `mcp.SessionFromContext(ctx)` to get current session\n- Session state includes tool mappings, current agent, and custom attributes\n- Parent sessions can be accessed via `Session.Parent`\n- Root session can be accessed via `session.Root()`\n\n## MCP Protocol Notes\n\nNanobot supports both MCP standard and MCP-UI extensions:\n- Standard MCP: tools, prompts, resources, sampling\n- MCP-UI: Elicitations (user input prompts), progress notifications, structured UI elements\n\nWhen implementing MCP features, refer to:\n- MCP types in `pkg/mcp/types.go`\n- Message handling in `pkg/mcp/message.go`\n- Protocol reference at `https://modelcontextprotocol.io`\n\n## Code Style\n\n- Go: Follow standard Go conventions, use `gofmt`\n","docs/agents/schemas/agent.frontmatter.schema.json":"{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/agent.frontmatter.schema.json\",\n  \"title\": \"Nanobot Agent Front Matter\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"name\"],\n  \"properties\": {\n    \"name\": { \"type\": \"string\", \"minLength\": 1 },\n    \"description\": { \"type\": \"string\" },\n    \"metadata\": {\n      \"type\": \"object\",\n      \"description\": \"Free-form metadata.\",\n      \"additionalProperties\": true,\n      \"default\": {}\n    },\n\n    \"model\": { \"type\": \"string\" },\n    \"allowed_models\": {\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\" },\n      \"default\": []\n    },\n    \"temperature\": { \"type\": \"number\" },\n    \"max_tokens\": { \"type\": \"integer\", \"minimum\": 1 },\n\n    \"tools\": {\n      \"description\": \"Tools visible/available to the agent. If omitted, defaults to [\\\"inherit\\\"]. Built-ins are logical names (e.g. Read/Write). MCP tools are mcpServer/toolName.\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tool_approvals\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": {\n          \"description\": \"Default authorization behavior. Currently only 'approve' is supported.\",\n          \"type\": \"string\",\n          \"enum\": [\"approve\"],\n          \"default\": \"approve\"\n        },\n        \"rules\": {\n          \"description\": \"Ordered allow rules; first matching rule wins.\",\n          \"type\": \"array\",\n          \"items\": { \"$ref\": \"#/$defs/toolApprovalRule\" },\n          \"default\": []\n        }\n      }\n    },\n\n    \"skills\": {\n      \"description\": \"Skills visible/available to the agent. If omitted, defaults to [\\\"inherit\\\"]. Skill IDs are directory names.\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tasks\": {\n      \"description\": \"Tasks visible/available to the agent (task IDs are directory paths under .nanobot/tasks).\",\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\", \"minLength\": 1 },\n      \"default\": [\"inherit\"]\n    },\n\n    \"task_approvals\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": {\n          \"description\": \"Default task authorization behavior. Currently only 'approve' is supported.\",\n          \"type\": \"string\",\n          \"enum\": [\"approve\"],\n          \"default\": \"approve\"\n        },\n        \"rules\": {\n          \"description\": \"Ordered allow rules; first matching rule wins.\",\n          \"type\": \"array\",\n          \"items\": { \"$ref\": \"#/$defs/taskApprovalRule\" },\n          \"default\": []\n        }\n      }\n    }\n  },\n\n  \"$defs\": {\n    \"toolApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"tool\", \"allow\"],\n      \"properties\": {\n        \"tool\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Tool identifier (must match an entry in tools).\"\n        },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": {\n          \"description\": \"Optional matchers keyed by tool argument name.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"taskApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"task\", \"allow\"],\n      \"properties\": {\n        \"task\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Task identifier (must match an entry in tasks).\"\n        },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": {\n          \"description\": \"Optional matchers keyed by task input name.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"matcher\": {\n      \"description\": \"A value matcher for a tool argument key.\",\n      \"oneOf\": [\n        { \"$ref\": \"#/$defs/equalsMatcher\" },\n        { \"$ref\": \"#/$defs/inMatcher\" },\n        { \"$ref\": \"#/$defs/startsWithMatcher\" },\n        { \"$ref\": \"#/$defs/matchesMatcher\" },\n        { \"$ref\": \"#/$defs/containsMatcher\" },\n        { \"$ref\": \"#/$defs/containsAllMatcher\" },\n        { \"$ref\": \"#/$defs/anyOfMatcher\" },\n        { \"$ref\": \"#/$defs/allOfMatcher\" }\n      ]\n    },\n\n    \"equalsMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"equals\"],\n      \"properties\": {\n        \"equals\": {}\n      }\n    },\n\n    \"inMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"in\"],\n      \"properties\": {\n        \"in\": {\n          \"type\": \"array\",\n          \"items\": {}\n        }\n      }\n    },\n\n    \"startsWithMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"startsWith\"],\n      \"properties\": {\n        \"startsWith\": { \"type\": \"string\" }\n      }\n    },\n\n    \"matchesMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"matches\"],\n      \"properties\": {\n        \"matches\": {\n          \"type\": \"string\",\n          \"description\": \"Regex string.\"\n        }\n      }\n    },\n\n    \"containsMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"contains\"],\n      \"properties\": {\n        \"contains\": {}\n      }\n    },\n\n    \"containsAllMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"containsAll\"],\n      \"properties\": {\n        \"containsAll\": {\n          \"type\": \"array\",\n          \"items\": {}\n        }\n      }\n    },\n\n    \"anyOfMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"anyOf\"],\n      \"properties\": {\n        \"anyOf\": {\n          \"type\": \"array\",\n          \"minItems\": 1,\n          \"items\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"allOfMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"allOf\"],\n      \"properties\": {\n        \"allOf\": {\n          \"type\": \"array\",\n          \"minItems\": 1,\n          \"items\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    }\n  }\n}\n","docs/agents/schemas/mcp.schema.json":"{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/mcp.schema.json\",\n  \"title\": \"Nanobot MCP Configuration\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"mcpServers\"],\n  \"properties\": {\n    \"mcpServers\": {\n      \"description\": \"Map of MCP server name -> server configuration.\",\n      \"type\": \"object\",\n      \"additionalProperties\": { \"$ref\": \"#/$defs/mcpServerConfig\" },\n      \"default\": {}\n    },\n    \"mcpRegistries\": {\n      \"description\": \"Map of registry name -> registry configuration.\",\n      \"type\": \"object\",\n      \"additionalProperties\": { \"$ref\": \"#/$defs/mcpRegistryConfig\" },\n      \"default\": {}\n    }\n  },\n  \"$defs\": {\n    \"mcpServerConfig\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"command\"],\n      \"properties\": {\n        \"command\": {\n          \"description\": \"Executable used to start the MCP server.\",\n          \"type\": \"string\",\n          \"minLength\": 1\n        },\n        \"args\": {\n          \"description\": \"Arguments passed to the command.\",\n          \"type\": \"array\",\n          \"items\": { \"type\": \"string\" },\n          \"default\": []\n        },\n        \"env\": {\n          \"description\": \"Environment variables for the MCP server process.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"type\": \"string\" },\n          \"default\": {}\n        },\n        \"cwd\": {\n          \"description\": \"Optional working directory for the MCP server process.\",\n          \"type\": \"string\"\n        },\n        \"allow_tools\": {\n          \"description\": \"Optional allowlist of tool names exposed by this server (toolName portion of server/toolName). If omitted, all server tools are available to be authorized by agents.\",\n          \"type\": \"array\",\n          \"items\": { \"type\": \"string\", \"minLength\": 1 }\n        },\n        \"timeout_ms\": {\n          \"description\": \"Startup/connection timeout.\",\n          \"type\": \"integer\",\n          \"minimum\": 0,\n          \"default\": 30000\n        }\n      }\n    },\n    \"mcpRegistryConfig\": {\n      \"description\": \"A registry pointing at a standard MCP registry endpoint.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"url\"],\n      \"properties\": {\n        \"url\": {\n          \"description\": \"Base URL of the MCP registry.\",\n          \"type\": \"string\",\n          \"minLength\": 1\n        }\n      }\n    }\n  }\n}\n","docs/agents/schemas/skill.frontmatter.schema.json":"{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/skill.frontmatter.schema.json\",\n  \"title\": \"Nanobot Skill Front Matter (Agent Skills compatible)\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"name\", \"description\"],\n  \"properties\": {\n    \"name\": {\n      \"type\": \"string\",\n      \"minLength\": 1,\n      \"maxLength\": 64,\n      \"pattern\": \"^(?!-)(?!.*--)[a-z0-9-]+(?<!-)$\",\n      \"description\": \"Must be 1-64 chars, lowercase letters/numbers/hyphens, no leading/trailing hyphen, no consecutive hyphens. Must also match directory name (enforced outside JSON Schema).\"\n    },\n    \"description\": {\n      \"type\": \"string\",\n      \"minLength\": 1,\n      \"maxLength\": 1024,\n      \"description\": \"Describe what the skill does and when to use it.\"\n    },\n    \"license\": {\n      \"type\": \"string\",\n      \"description\": \"License name or reference to bundled license file.\"\n    },\n    \"compatibility\": {\n      \"type\": \"string\",\n      \"minLength\": 1,\n      \"maxLength\": 500,\n      \"description\": \"Environment requirements (product, packages, network access, etc.).\"\n    },\n    \"metadata\": {\n      \"type\": \"object\",\n      \"description\": \"Arbitrary key-value mapping.\",\n      \"additionalProperties\": {\n        \"oneOf\": [\n          { \"type\": \"string\" },\n          { \"type\": \"number\" },\n          { \"type\": \"integer\" },\n          { \"type\": \"boolean\" }\n        ]\n      },\n      \"default\": {}\n    },\n    \"allowed-tools\": {\n      \"type\": \"string\",\n      \"description\": \"Space-delimited list of pre-approved tools (experimental). E.g. 'Bash(git:*) Read'.\"\n    },\n\n    \"tools\": {\n      \"description\": \"Nanobot extension: tools visible/available while executing this skill. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tool_approvals\": {\n      \"description\": \"Nanobot extension: tool approval policy while executing this skill.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": {\n          \"description\": \"Default authorization behavior. Currently only 'approve' is supported.\",\n          \"type\": \"string\",\n          \"enum\": [\"approve\"],\n          \"default\": \"approve\"\n        },\n        \"rules\": {\n          \"description\": \"Ordered allow rules; first matching rule wins.\",\n          \"type\": \"array\",\n          \"items\": { \"$ref\": \"#/$defs/toolApprovalRule\" },\n          \"default\": []\n        }\n      }\n    },\n\n    \"skills\": {\n      \"description\": \"Nanobot extension: skills visible/available while executing this skill. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tasks\": {\n      \"description\": \"Nanobot extension: tasks visible/available while executing this skill (task IDs are directory paths under .nanobot/tasks).\",\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\", \"minLength\": 1 },\n      \"default\": [\"inherit\"]\n    },\n\n    \"task_approvals\": {\n      \"description\": \"Nanobot extension: task approval policy while executing this skill.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": {\n          \"description\": \"Default task authorization behavior. Currently only 'approve' is supported.\",\n          \"type\": \"string\",\n          \"enum\": [\"approve\"],\n          \"default\": \"approve\"\n        },\n        \"rules\": {\n          \"description\": \"Ordered allow rules; first matching rule wins.\",\n          \"type\": \"array\",\n          \"items\": { \"$ref\": \"#/$defs/taskApprovalRule\" },\n          \"default\": []\n        }\n      }\n    }\n  },\n\n  \"$defs\": {\n    \"toolApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"tool\", \"allow\"],\n      \"properties\": {\n        \"tool\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Tool identifier (must match an entry in tools).\"\n        },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": {\n          \"description\": \"Optional matchers keyed by tool argument name.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"taskApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"task\", \"allow\"],\n      \"properties\": {\n        \"task\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Task identifier (must match an entry in tasks).\"\n        },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": {\n          \"description\": \"Optional matchers keyed by task input name.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"matcher\": {\n      \"description\": \"A value matcher for a tool argument key.\",\n      \"oneOf\": [\n        { \"$ref\": \"#/$defs/equalsMatcher\" },\n        { \"$ref\": \"#/$defs/inMatcher\" },\n        { \"$ref\": \"#/$defs/startsWithMatcher\" },\n        { \"$ref\": \"#/$defs/matchesMatcher\" },\n        { \"$ref\": \"#/$defs/containsMatcher\" },\n        { \"$ref\": \"#/$defs/containsAllMatcher\" },\n        { \"$ref\": \"#/$defs/anyOfMatcher\" },\n        { \"$ref\": \"#/$defs/allOfMatcher\" }\n      ]\n    },\n\n    \"equalsMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"equals\"],\n      \"properties\": { \"equals\": {} }\n    },\n\n    \"inMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"in\"],\n      \"properties\": {\n        \"in\": {\n          \"type\": \"array\",\n          \"items\": {}\n        }\n      }\n    },\n\n    \"startsWithMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"startsWith\"],\n      \"properties\": { \"startsWith\": { \"type\": \"string\" } }\n    },\n\n    \"matchesMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"matches\"],\n      \"properties\": { \"matches\": { \"type\": \"string\" } }\n    },\n\n    \"containsMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"contains\"],\n      \"properties\": { \"contains\": {} }\n    },\n\n    \"containsAllMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"containsAll\"],\n      \"properties\": {\n        \"containsAll\": {\n          \"type\": \"array\",\n          \"items\": {}\n        }\n      }\n    },\n\n    \"anyOfMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"anyOf\"],\n      \"properties\": {\n        \"anyOf\": {\n          \"type\": \"array\",\n          \"minItems\": 1,\n          \"items\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"allOfMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"allOf\"],\n      \"properties\": {\n        \"allOf\": {\n          \"type\": \"array\",\n          \"minItems\": 1,\n          \"items\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    }\n  }\n}\n","docs/agents/schemas/task.frontmatter.schema.json":"{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/task.frontmatter.schema.json\",\n  \"title\": \"Nanobot Task Front Matter (TASK.md only)\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"name\"],\n  \"properties\": {\n    \"name\": { \"type\": \"string\", \"minLength\": 1 },\n    \"description\": { \"type\": \"string\" },\n    \"metadata\": {\n      \"type\": \"object\",\n      \"description\": \"Free-form metadata.\",\n      \"additionalProperties\": true,\n      \"default\": {}\n    },\n\n    \"agent\": {\n      \"description\": \"Agent ID to execute this task with.\",\n      \"type\": \"string\",\n      \"minLength\": 1\n    },\n\n    \"tools\": {\n      \"description\": \"Tools visible/available while executing this task. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tool_approvals\": {\n      \"description\": \"Tool approval policy while executing this task.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": { \"type\": \"string\", \"enum\": [\"approve\"], \"default\": \"approve\" },\n        \"rules\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/toolApprovalRule\" }, \"default\": [] }\n      }\n    },\n\n    \"tasks\": {\n      \"description\": \"Tasks visible/available while executing this task (task IDs are directory paths under .nanobot/tasks).\",\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\", \"minLength\": 1 },\n      \"default\": [\"inherit\"]\n    },\n\n    \"task_approvals\": {\n      \"description\": \"Task approval policy while executing this task.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": { \"type\": \"string\", \"enum\": [\"approve\"], \"default\": \"approve\" },\n        \"rules\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/taskApprovalRule\" }, \"default\": [] }\n      }\n    },\n\n    \"inputs\": {\n      \"description\": \"Task inputs (TASK.md only).\",\n      \"type\": \"array\",\n      \"items\": { \"$ref\": \"#/$defs/taskInput\" },\n      \"default\": []\n    },\n\n    \"next\": {\n      \"description\": \"Relative path to the next step markdown file within the same task directory.\",\n      \"type\": \"string\",\n      \"minLength\": 1\n    }\n  },\n\n  \"$defs\": {\n    \"taskInput\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"name\", \"description\"],\n      \"properties\": {\n        \"name\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Input name.\"\n        },\n        \"description\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Describe what the input means and expected format.\"\n        },\n        \"default\": {\n          \"type\": \"string\",\n          \"description\": \"Optional default value (string).\"\n        }\n      }\n    },\n\n    \"toolApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"tool\", \"allow\"],\n      \"properties\": {\n        \"tool\": { \"type\": \"string\", \"minLength\": 1 },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": { \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" } }\n      }\n    },\n\n    \"taskApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"task\", \"allow\"],\n      \"properties\": {\n        \"task\": { \"type\": \"string\", \"minLength\": 1 },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": { \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" } }\n      }\n    },\n\n    \"matcher\": {\n      \"oneOf\": [\n        { \"$ref\": \"#/$defs/equalsMatcher\" },\n        { \"$ref\": \"#/$defs/inMatcher\" },\n        { \"$ref\": \"#/$defs/startsWithMatcher\" },\n        { \"$ref\": \"#/$defs/matchesMatcher\" },\n        { \"$ref\": \"#/$defs/containsMatcher\" },\n        { \"$ref\": \"#/$defs/containsAllMatcher\" },\n        { \"$ref\": \"#/$defs/anyOfMatcher\" },\n        { \"$ref\": \"#/$defs/allOfMatcher\" }\n      ]\n    },\n\n    \"equalsMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"equals\"], \"properties\": { \"equals\": {} } },\n    \"inMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"in\"], \"properties\": { \"in\": { \"type\": \"array\", \"items\": {} } } },\n    \"startsWithMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"startsWith\"], \"properties\": { \"startsWith\": { \"type\": \"string\" } } },\n    \"matchesMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"matches\"], \"properties\": { \"matches\": { \"type\": \"string\" } } },\n    \"containsMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"contains\"], \"properties\": { \"contains\": {} } },\n    \"containsAllMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"containsAll\"], \"properties\": { \"containsAll\": { \"type\": \"array\", \"items\": {} } } },\n    \"anyOfMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"anyOf\"], \"properties\": { \"anyOf\": { \"type\": \"array\", \"minItems\": 1, \"items\": { \"$ref\": \"#/$defs/matcher\" } } } },\n    \"allOfMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"allOf\"], \"properties\": { \"allOf\": { \"type\": \"array\", \"minItems\": 1, \"items\": { \"$ref\": \"#/$defs/matcher\" } } } }\n  }\n}\n","docs/agents/schemas/task.step.frontmatter.schema.json":"{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/task.step.frontmatter.schema.json\",\n  \"title\": \"Nanobot Task Step Front Matter\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"name\"],\n  \"properties\": {\n    \"name\": { \"type\": \"string\", \"minLength\": 1 },\n    \"description\": { \"type\": \"string\" },\n    \"metadata\": {\n      \"type\": \"object\",\n      \"description\": \"Free-form metadata.\",\n      \"additionalProperties\": true,\n      \"default\": {}\n    },\n\n    \"agent\": {\n      \"description\": \"Agent ID to execute this step with.\",\n      \"type\": \"string\",\n      \"minLength\": 1\n    },\n\n    \"tools\": {\n      \"description\": \"Tools visible/available while executing this step. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tool_approvals\": {\n      \"description\": \"Tool approval policy while executing this step.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": { \"type\": \"string\", \"enum\": [\"approve\"], \"default\": \"approve\" },\n        \"rules\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/toolApprovalRule\" }, \"default\": [] }\n      }\n    },\n\n    \"skills\": {\n      \"description\": \"Skills visible/available while executing this step. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tasks\": {\n      \"description\": \"Tasks visible/available while executing this step (task IDs are directory paths under .nanobot/tasks).\",\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\", \"minLength\": 1 },\n      \"default\": [\"inherit\"]\n    },\n\n    \"next\": {\n      \"description\": \"Relative path to the next step markdown file within the same task directory.\",\n      \"type\": \"string\",\n      \"minLength\": 1\n    }\n  },\n\n  \"$defs\": {\n    \"toolApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"tool\", \"allow\"],\n      \"properties\": {\n        \"tool\": { \"type\": \"string\", \"minLength\": 1 },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": { \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" } }\n      }\n    },\n    \"taskApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"task\", \"allow\"],\n      \"properties\": {\n        \"task\": { \"type\": \"string\", \"minLength\": 1 },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": { \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" } }\n      }\n    },\n\n    \"matcher\": {\n      \"oneOf\": [\n        { \"$ref\": \"#/$defs/equalsMatcher\" },\n        { \"$ref\": \"#/$defs/inMatcher\" },\n        { \"$ref\": \"#/$defs/startsWithMatcher\" },\n        { \"$ref\": \"#/$defs/matchesMatcher\" },\n        { \"$ref\": \"#/$defs/containsMatcher\" },\n        { \"$ref\": \"#/$defs/containsAllMatcher\" },\n        { \"$ref\": \"#/$defs/anyOfMatcher\" },\n        { \"$ref\": \"#/$defs/allOfMatcher\" }\n      ]\n    },\n    \"equalsMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"equals\"], \"properties\": { \"equals\": {} } },\n    \"inMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"in\"], \"properties\": { \"in\": { \"type\": \"array\", \"items\": {} } } },\n    \"startsWithMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"startsWith\"], \"properties\": { \"startsWith\": { \"type\": \"string\" } } },\n    \"matchesMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"matches\"], \"properties\": { \"matches\": { \"type\": \"string\" } } },\n    \"containsMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"contains\"], \"properties\": { \"contains\": {} } },\n    \"containsAllMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"containsAll\"], \"properties\": { \"containsAll\": { \"type\": \"array\", \"items\": {} } } },\n    \"anyOfMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"anyOf\"], \"properties\": { \"anyOf\": { \"type\": \"array\", \"minItems\": 1, \"items\": { \"$ref\": \"#/$defs/matcher\" } } } },\n    \"allOfMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"allOf\"], \"properties\": { \"allOf\": { \"type\": \"array\", \"minItems\": 1, \"items\": { \"$ref\": \"#/$defs/matcher\" } } } }\n  }\n}\n","pkg/config/testdata/directory-with-readme/agents/README.md":"# README\n\nThis is documentation and should not be loaded as an agent.\n","pkg/servers/system/skills/browser-use.md":"---\nname: browser-use\ndescription: Automates browser interactions for web testing, form filling, screenshots, and data extraction. Use when the user needs to navigate websites, interact with web pages, fill forms, take screenshots, or extract information from web pages. IMPORTANT - If webfetch fails, try this tool instead.\n---\n\n# Browser Automation with browser-use CLI\n\nThe `browser-use` command provides fast, persistent browser automation. It maintains browser sessions across commands, enabling complex multi-step workflows.\n\n`browser-use` is already installed in the dedicated Nanobot agent image. Use the built-in CLI directly.\n\nWhen using this skill, default to `--browser chromium --headed` unless the user explicitly wants headless/background execution. Headed mode is the safer default because it keeps the browser visible in BrowserView and makes manual intervention possible.\n\n## Quick Start\n\n```bash\nbrowser-use --browser chromium --headed open https://example.com  # Navigate to URL\nbrowser-use state                              # Get page elements with indices\nbrowser-use click 5                            # Click element by index\nbrowser-use type \"Hello World\"                 # Type text\nbrowser-use screenshot                         # Take screenshot\nbrowser-use close                              # Close browser\n```\n\n## Core Workflow\n\n1. **Navigate**: `browser-use --browser chromium --headed open <url>` - Opens URL (starts browser if needed)\n2. **Inspect**: `browser-use state` - Returns clickable elements with indices\n3. **Interact**: Use indices from state to interact (`browser-use click 5`, `browser-use input 3 \"text\"`)\n4. **Verify**: `browser-use state` or `browser-use screenshot` to confirm actions\n5. **Repeat**: Browser stays open between commands\n\n## Browser Modes\n\n```bash\nbrowser-use --browser chromium --headed open <url>  # Recommended default: visible Chromium window\nbrowser-use --browser chromium open <url>      # Headless Chromium for explicit background runs\nbrowser-use --browser real open <url>          # User's Chrome with login sessions\n```\n\n- **chromium**: Fast, isolated, and should usually be run with `--headed`\n- **real**: Uses your Chrome with cookies, extensions, logged-in sessions\n\n## Browser Viewing with VNC\n\nThe VNC server is automatically started and ready to use. You can view and interact with the browser in real-time using VNC. This is **essential** for:\n\n- **Solving CAPTCHAs** - Many sites require human verification\n- **Debugging** - See exactly what the browser is doing\n- **User interaction** - Manual intervention when automation gets stuck\n\nThe VNC server provides:\n- X virtual framebuffer (Xvfb) on display :99\n- VNC server on port 5900\n- an internal WebSocket proxy on port 6080, exposed only through Nanobot's `/browser` path\n- a borderless, maximized browser window sized for the BrowserView pane by default\n\n### Accessing the Browser View\n\nThe Nanobot UI includes a browser viewer component that embeds VNC access. The backend proxies only the WebSocket transport; the standalone noVNC `vnc.html` page is not exposed.\n\n### Using with browser-use\n\n**Default to `--headed` mode. It is required when you need VNC viewing:**\n\n```bash\n# RECOMMENDED DEFAULT (visible in VNC viewer, user can interact)\nbrowser-use --browser chromium --headed open https://example.com\n\n# HEADLESS ONLY WHEN EXPLICITLY APPROPRIATE (faster, but user can't see it)\nbrowser-use --browser chromium open https://example.com\n```\n\nThe `--headed` flag makes the browser visible on the VNC display (:99).\n\n### CAPTCHA Solving Workflow\n\n**When a CAPTCHA is encountered:**\n\n1. **Detect the CAPTCHA** - Look for verification challenges, \"I'm not a robot\" checkboxes, or automation blocks\n2. **Inform the user immediately** - Tell them a CAPTCHA needs to be solved\n3. **Ensure VNC is running** - Verify the browser is running in `--headed` mode\n4. **Guide the user** - Tell them to open the BrowserView pane in the Nanobot UI\n5. **Wait for completion** - Pause automation until the user confirms they've solved the CAPTCHA\n6. **Continue** - Resume automation after manual intervention\n\n**Example:**\n```bash\n# You're automating a task and hit a CAPTCHA\nbrowser-use --browser chromium --headed open https://example.com\nbrowser-use state\n# → Detects CAPTCHA verification challenge\n\n# STOP and inform user:\n# \"I've encountered a CAPTCHA that needs human verification.\n#  Please open the BrowserView pane in the Nanobot UI\n#  and solve the CAPTCHA. Let me know when you're done.\"\n\n# Wait for user confirmation, then continue:\nbrowser-use state  # Verify CAPTCHA is solved\nbrowser-use click 5  # Continue with automation\n```\n\n**Important:** CAPTCHAs cannot be automated - they require human interaction. Always use `--headed` mode when you anticipate CAPTCHAs, and be prepared to pause and ask the user for help.\n\n## Commands\n\n### Navigation\n```bash\nbrowser-use open <url>                    # Navigate to URL\nbrowser-use back                          # Go back in history\nbrowser-use scroll down                   # Scroll down\nbrowser-use scroll up                     # Scroll up\n```\n\n### Page State\n```bash\nbrowser-use state                         # Get URL, title, and clickable elements\nbrowser-use screenshot                    # Take screenshot (outputs base64)\nbrowser-use screenshot path.png           # Save screenshot to file\nbrowser-use screenshot --full path.png    # Full page screenshot\n```\n\n### Interactions (use indices from `browser-use state`)\n```bash\nbrowser-use click <index>                 # Click element\nbrowser-use type \"text\"                   # Type text into focused element\nbrowser-use input <index> \"text\"          # Click element, then type text\nbrowser-use keys \"Enter\"                  # Send keyboard keys\nbrowser-use keys \"Control+a\"              # Send key combination\nbrowser-use select <index> \"option\"       # Select dropdown option\n```\n\n### Tab Management\n```bash\nbrowser-use switch <tab>                  # Switch to tab by index\nbrowser-use close-tab                     # Close current tab\nbrowser-use close-tab <tab>               # Close specific tab\n```\n\n### JavaScript & Data\n```bash\nbrowser-use eval \"document.title\"         # Execute JavaScript, return result\n```\n\n### Cookies\n```bash\nbrowser-use cookies get                   # Get all cookies\nbrowser-use cookies get --url <url>       # Get cookies for specific URL\nbrowser-use cookies set <name> <value>    # Set a cookie\nbrowser-use cookies set name val --domain .example.com --secure --http-only\nbrowser-use cookies clear                 # Clear all cookies\nbrowser-use cookies clear --url <url>     # Clear cookies for specific URL\nbrowser-use cookies export <file>         # Export all cookies to JSON file\nbrowser-use cookies export <file> --url <url>  # Export cookies for specific URL\nbrowser-use cookies import <file>         # Import cookies from JSON file\n```\n\n### Wait Conditions\n```bash\nbrowser-use wait selector \"h1\"            # Wait for element to be visible\nbrowser-use wait selector \".loading\" --state hidden  # Wait for element to disappear\nbrowser-use wait selector \"#btn\" --state attached    # Wait for element in DOM\nbrowser-use wait text \"Success\"           # Wait for text to appear\nbrowser-use wait selector \"h1\" --timeout 5000  # Custom timeout in ms\n```\n\n### Additional Interactions\n```bash\nbrowser-use hover <index>                 # Hover over element (triggers CSS :hover)\nbrowser-use dblclick <index>              # Double-click element\nbrowser-use rightclick <index>            # Right-click element (context menu)\n```\n\n### Information Retrieval\n```bash\nbrowser-use get title                     # Get page title\nbrowser-use get html                      # Get full page HTML\nbrowser-use get html --selector \"h1\"      # Get HTML of specific element\nbrowser-use get text <index>              # Get text content of element\nbrowser-use get value <index>             # Get value of input/textarea\nbrowser-use get attributes <index>        # Get all attributes of element\nbrowser-use get bbox <index>              # Get bounding box (x, y, width, height)\n```\n\n### Python Execution (Persistent Session)\n```bash\nbrowser-use python \"x = 42\"               # Set variable\nbrowser-use python \"print(x)\"             # Access variable (outputs: 42)\nbrowser-use python \"print(browser.url)\"   # Access browser object\nbrowser-use python --vars                 # Show defined variables\nbrowser-use python --reset                # Clear Python namespace\nbrowser-use python --file script.py       # Execute Python file\n```\n\nThe Python session maintains state across commands. The `browser` object provides:\n- `browser.url` - Current page URL\n- `browser.title` - Page title\n- `browser.goto(url)` - Navigate\n- `browser.click(index)` - Click element\n- `browser.type(text)` - Type text\n- `browser.screenshot(path)` - Take screenshot\n- `browser.scroll()` - Scroll page\n- `browser.html` - Get page HTML\n\n### Session Management\n```bash\nbrowser-use sessions                      # List active sessions\nbrowser-use close                         # Close current session\nbrowser-use close --all                   # Close all sessions\n```\n\n### Profile Management\n```bash\nbrowser-use profile list-local            # List local Chrome profiles\n```\n\n**Before opening a real browser (`--browser real`)**, always ask the user if they want to use a specific Chrome profile or no profile. Use `profile list-local` to show available profiles:\n\n```bash\nbrowser-use profile list-local\n# Output: Default: Person 1 (user@gmail.com)\n#         Profile 1: Work (work@company.com)\n\n# With a specific profile (has that profile's cookies/logins)\nbrowser-use --browser real --profile \"Profile 1\" open https://gmail.com\n\n# Without a profile (fresh browser, no existing logins)\nbrowser-use --browser real open https://gmail.com\n\n# Headless mode (no visible window) - useful for cookie export\nbrowser-use --browser real --profile \"Default\" cookies export /tmp/cookies.json\n```\n\nEach Chrome profile has its own cookies, history, and logged-in sessions. Choosing the right profile determines whether sites will be pre-authenticated.\n\n## Global Options\n\n| Option | Description |\n|--------|-------------|\n| `--session NAME` | Use named session (default: \"default\") |\n| `--browser MODE` | Browser mode: chromium, real |\n| `--headed` | Show browser window (chromium mode) |\n| `--profile NAME` | Chrome profile (real mode only) |\n| `--json` | Output as JSON |\n\n**Session behavior**: All commands without `--session` use the same \"default\" session. The browser stays open and is reused across commands. Use `--session NAME` to run multiple browsers in parallel.\n\n## Usage Notes\n\n- Always run `browser-use state` before interacting so you have current element indices.\n- Default to `--browser chromium --headed`.\n- Only omit `--headed` when the user explicitly wants headless/background execution or when visibility and manual takeover are clearly unnecessary.\n- Use `browser-use screenshot` or `browser-use state` to confirm each critical action.\n- Close the session with `browser-use close` when the browser task is complete.\n","pkg/servers/system/skills/python-scripts.md":"---\nname: python-scripts\ndescription: Write and execute Python scripts using uv for programmatic tasks.\n---\n\nUse Python when a step needs data transformation, API calls, calculations, or deterministic operations where code is more reliable than natural language.\n\n## How to Use\n\nWrite a script with inline dependencies and run it with `uv run`:\n\n```python\n#!/usr/bin/env python3\n# /// script\n# requires-python = \">=3.11\"\n# dependencies = [\n#     \"requests\",\n# ]\n# ///\n\nimport json\nimport requests\n\ndata = requests.get(\"https://api.example.com/data\").json()\nprint(json.dumps(data))\n```\n\n```bash\nuv run script.py\n```\n\nThe `# /// script` block declares dependencies - `uv` installs them automatically.\n\n## Output Conventions\n\n- **Structured data**: Print JSON to stdout\n- **Progress/debug info**: Print to stderr\n- **Errors**: Print to stderr and exit with non-zero code\n\n```python\nimport sys\nimport json\n\n# Output result\nprint(json.dumps({\"items\": [...], \"count\": 42}))\n\n# Debug info (won't pollute output)\nprint(\"Processed 42 items\", file=sys.stderr)\n```\n\n## When to Use Python vs LLM\n\n**Use Python for:** JSON manipulation, API calls with auth/pagination, calculations, file parsing, sorting/filtering, large datasets\n\n**Keep as LLM for:** Tasks requiring judgment, natural language generation, flexible interpretation\n","pkg/servers/system/skills/scheduled-tasks.md":"---\nname: scheduled-tasks\ndescription: Load this skill for ANY request that mentions scheduled tasks, recurring jobs, reminders, future runs, or time-based automation.\n---\n\n# Scheduled Tasks\n\nScheduled tasks are persistent task definitions managed through the `nanobot.tasks` tools. When a scheduled task fires, nanobot starts a new chat thread and runs the saved prompt there.\n\n## When to Use Scheduled Tasks\n\nUse scheduled tasks when work should happen later or recur on a schedule:\n- Daily, weekly, or monthly reports\n- Reminders or recurring checks\n- Periodic file updates, cleanups, or syncs\n\nIf the user wants the work done right now and not again later, do it directly instead of creating a scheduled task.\n\n## Scheduled Tasks vs Workflows\n\nDo not conflate scheduled tasks with workflows:\n- **Workflows** are reusable procedures stored under `workflows/`.\n- **Scheduled tasks** decide when nanobot should start a future chat thread.\n\nUse a workflow when the user wants a reusable multi-step procedure. Use a scheduled task when the user wants something to happen later or recur automatically.\n\nIf the user wants a workflow to run on a schedule, keep them separate:\n1. Create or update the workflow first.\n2. Create a scheduled task whose prompt tells nanobot to run that workflow.\n\n## Managing Existing Tasks\n\nBefore updating, deleting, or manually running an existing task, call `listScheduledTasks` unless the user already provided the exact `task:///...` URI.\n\nUse the task URI for all follow-up task operations:\n- `updateScheduledTask`\n- `deleteScheduledTask`\n- `startScheduledTask`\n\n## Timezone Rules\n\nWhen creating a scheduled task, always set the timezone explicitly.\n\n- If the user's current timezone is already known from context, use that by default unless they say otherwise.\n- If you do not know the user's timezone, collect it before creating the task.\n- Do not guess a timezone from a city, country, or language unless the user explicitly gave enough information to make that unambiguous.\n\nUnless the user asks for a different timezone, new scheduled tasks should use the user's current timezone.\n\n## Valid Schedule Shapes\n\nThe task tools only accept five-field cron expressions in these shapes:\n\n- **Daily:** minute hour `* * *`\n  - Example: `45 2 * * *` for every day at 02:45\n- **Weekly:** minute hour `* * day-of-week`\n  - Example: `20 23 * * 1,2,3,4` for Monday through Thursday at 23:20\n- **Monthly:** minute hour `day-of-month * *`\n  - Example: `45 2 22,24 * *` for the 22nd and 24th of every month at 02:45\n- **One-time date:** minute hour `day-of-month month *` with a matching expiration date\n  - Example: `45 2 26 3 *` with expiration `2026-03-26` for March 26 at 02:45\n\nDo not use unsupported cron shapes. In particular:\n- Do not use six-field cron expressions\n- Do not use schedules that combine both day-of-month and day-of-week\n- Do not use arbitrary cron patterns outside the daily, weekly, monthly, or one-time date forms above\n\n## Designing Good Task Prompts\n\nEach scheduled run starts a fresh chat thread. The stored prompt should be self-contained:\n- State exactly what to do\n- Name any files, outputs, or deliverables to produce\n- Mention any workflow to run by name if applicable\n- Avoid depending on transient context from the current conversation\n\nFor recurring actions that send messages or modify external systems, confirm the user's intent unless they have already clearly requested that recurring behavior.\n","pkg/servers/system/skills/workflows.md":"---\nname: workflows\ndescription: Load this skill for ANY request that mentions workflows, including listing, creating, running, editing, or discussing them.\n---\n\n# Workflows\n\nWorkflows are directories in `workflows/` that codify repeatable processes. Each workflow is a directory containing a `SKILL.md` file (with YAML frontmatter) and any supporting files (scripts, assets, etc.). Each step's output can be referenced by later steps using `{{Step Name}}`.\n\n## Discovering Workflows\n\nThere are two places workflows can exist:\n\n1. **Local workflows** — in the `workflows/` directory on the local filesystem. These are workflows the user has created or installed. List the `workflows/` directory to find these.\n2. **Shared/published workflows** — in the remote Obot registry. These are workflows other users have published (publicly or to the organization). Use `searchArtifacts` to find these.\n\n**When the user asks about \"shared workflows\", \"public workflows\", workflows from other users, or wants to discover/find/browse available workflows they haven't installed yet, ALWAYS use `searchArtifacts` to search the remote registry.** Do not just list the local `workflows/` directory — that only shows what is already installed locally.\n\nIf the user asks to \"list all workflows\", \"show my workflows\", \"which workflows are installed\" or similar without specifying shared/public, find workflows by using the Glob tool (never Bash with find or ls) with pattern `**/SKILL.md` in the `workflows/` directory (do NOT include `workflows/` in both the path and the pattern — that double-nests). If they ask to \"list shared workflows\" or \"find workflows\", use `searchArtifacts`.\n\n## When to Use Workflows\n\nWorkflows are for repeatable tasks. If it's a one-time thing, just execute it directly.\n\n## Tracking Progress\n\nUse TodoWrite during **both** workflow design and execution. The user sees your todo list — it's your primary way of keeping them informed.\n\n- **When designing:** Create todos for your design steps (gather requirements, identify inputs/edge cases, draft workflow, write file, present for review).\n- **When executing:** Create a todo for each workflow step and update status as you go. Add new todos if unexpected issues arise.\n\nKeep todo titles short and scannable. Update them as you progress — don't leave stale todos sitting around.\n\n## Workflow Format\n\nEach workflow is a directory under `workflows/` containing a `SKILL.md` file with:\n- **Frontmatter** (required): YAML frontmatter with `name` (lowercase-hyphenated slug that must match the directory name), `description` (always wrap in double quotes to avoid YAML parsing errors from colons or special characters), and optional `metadata` map.\n- **Inputs**: Optional parameters with defaults\n- **Steps**: Numbered steps with clear instructions\n- **Output**: Optional template for the final result\n\nWhen creating a new workflow, always include frontmatter with `name` (must match the directory name, lowercase with hyphens only), `description` (always in double quotes), and `metadata.createdAt` set to the current date/time in ISO 8601 format (e.g., `2026-01-15T09:00:00Z`). Use `bash` (e.g., `date -u +\"%Y-%m-%dT%H:%M:%SZ\"`) to get the current UTC time — do not guess or hardcode it.\n\n**IMPORTANT:** Always wrap the `description` value in double quotes in the YAML frontmatter. Unquoted descriptions containing colons (`:`) cause YAML parsing errors. For example, write `description: \"Analyze code: find and fix issues\"` not `description: Analyze code: find and fix issues`.\n\n**Name constraints:** lowercase letters, numbers, and hyphens only; no leading/trailing or consecutive hyphens; must match the directory name exactly.\n\n### Example: Simple Review\n\n```markdown\n---\nname: code-review\ndescription: \"Review code changes for quality issues.\"\nmetadata:\n  createdAt: \"2026-01-15T09:00:00Z\"\n---\n\n## Inputs\n\n- **target** (optional): Files to review. Default: `.`\n\n## Steps\n\n### 1. Find Changes\nFind all modified code files in {{input.target}}.\n\n### 2. Review Code\nReview these files for quality issues:\n{{Find Changes}}\n\nFocus on: error handling, edge cases, and readability.\n\n## Output\n\n{{Review Code}}\n```\n\n### Example: With Conditions\n\n```markdown\n---\nname: smart-fix\ndescription: \"Analyze an issue and apply a fix only if it's safe to do so.\"\nmetadata:\n  createdAt: \"2026-01-15T09:00:00Z\"\n---\n\n## Steps\n\n### 1. Analyze Issue\nAnalyze the reported issue and determine severity.\n\n### 2. Check Safety\nBased on this analysis, determine if an automated fix is safe:\n{{Analyze Issue}}\n\nEnd your response with SAFE or UNSAFE.\n\n### 3. Apply Fix\nApply the fix for: {{Analyze Issue}}\n\n**Condition:** {{Check Safety}} contains SAFE\n\n### 4. Create Manual Report\nCreate a report explaining why manual intervention is needed:\n{{Analyze Issue}}\n\n**Condition:** {{Check Safety}} contains UNSAFE\n```\n\n### Example: With Error Handling\n\n```markdown\n---\nname: deploy-with-rollback\ndescription: \"Deploy changes with automatic rollback on failure.\"\nmetadata:\n  createdAt: \"2026-01-15T09:00:00Z\"\n---\n\n## Inputs\n\n- **environment** (required): Target environment (staging, production)\n\n## Steps\n\n### 1. Run Tests\nRun the full test suite. Report any failures.\n\n### 2. Deploy\nDeploy to {{input.environment}}.\n\n**On error:** Rollback\n\n### 3. Verify\nVerify the deployment is healthy.\n\n**On error:** Rollback\n\n### 4. Rollback\nRoll back to the previous version and report what went wrong.\n\n## Output\n\nDeployment to {{input.environment}} complete.\n{{Verify}}\n```\n\n## Key Concepts\n\n**Variables:** Use `{{input.name}}` for inputs and `{{Step Name}}` for outputs from previous steps.\n\n**Conditions:** Add `**Condition:**` to make a step run only when the condition is true. Common patterns:\n- `{{Step Name}} contains X` - check if output contains text\n- `{{Step Name}} not empty` - check if output exists\n- `{{input.flag}} equals yes` - check exact value\n\n**Error handling:** Add `**On error:**` to control what happens when a step fails:\n- `stop` (default) - halt the workflow\n- `continue` - log the error and move to the next step\n- `Step Name` - jump to a specific step (useful for cleanup/rollback)\n\n**Output:** The `## Output` section defines the final result. If omitted, the last step's output is used.\n\n**File dependencies:** When a workflow depends on any files (scripts, templates, data files, configs, etc.), those files **must** be placed in the same directory as the workflow's `SKILL.md` file or in a subdirectory within it. Workflows are self-contained directories — files outside the workflow directory will not be available when the workflow is published, installed, or shared.\n\n## Designing Workflows\n\nWhen asked to create a workflow, DO NOT start writing it immediately. Follow this process:\n\n### Phase 1: Understand\n\n1. Use TodoWrite to create a plan for designing the workflow (e.g., \"Gather requirements\", \"Identify inputs and edge cases\", \"Draft workflow\", \"Write to file\", \"Present for review\").\n2. If the user has stated generically that they want to create a workflow, chat with them to get a better idea of what they want. Don't use the AskUserQuestion tool at this phase.\n3. Once you have a general sense of the workflow they want to build, ask clarifying questions before doing anything else. DO use the AskUserQuestion tool for these specific questions. You need to understand:\n    - What the workflow should accomplish end-to-end\n    - What the expected outcome is\n    - What inputs/variables are needed\n    - What external services or tools are involved\n    - How errors should be handled (stop? retry?)\n    - DON'T ask about execution frequency or external triggers because we don't yet support those features \n4. **Wait for answers. Do not proceed until you have enough information to design a good workflow.** Do not guess or assume — ask.\n\n### Phase 2: Draft & Save\n\n1. Once you understand the requirements, draft the workflow.\n2. Create the directory `workflows/<name>/` and write the workflow to `workflows/<name>/SKILL.md`. Place any supporting files (scripts, data, etc.) alongside `SKILL.md` in the same directory.\n3. Mark your design todos as complete.\n\n### Phase 3: Hand Off\n\n1. Present the workflow to the user with a brief summary of what each step does.\n2. Ask the user to review it. Offer to make changes.\n3. **Offer to execute the workflow — do NOT execute it automatically.** Say something like: \"Want me to run this now, or would you like to make changes first?\"\n\n**IMPORTANT:** Never skip Phase 1. Never jump straight to writing. Never auto-execute after designing.\n\nUse descriptive step names in title case (e.g., \"Fetch Issues\" not \"step1\") — these become variables for later steps.\n\nFor deterministic operations (data transformation, calculations), consider having steps use Python scripts (see `python-scripts` skill). For external services (Slack, Jira, databases), consider MCP servers (see `mcp-curl` skill).\n\n## Executing Workflows\n\nUsers may ask to run a workflow at any time — not just immediately after designing one. Workflows in `workflows/` are persistent, reusable artifacts. A user might say \"run the code-review workflow\" days or weeks after it was created. Treat execution as a standalone operation: load the file, collect inputs, and run it. Don't assume you have any prior context about the workflow beyond what's in the file itself.\n\n**IMPORTANT:** If the user asks you to *create* a workflow, do NOT auto-execute it after writing the file. Present it for review and offer to run it. But if the user asks you to *run* an existing workflow, go ahead — that's an explicit request.\n\n**IMPORTANT:** Installed workflows live on the local filesystem in the `workflows/` directory. When running a workflow, ALWAYS load it from `workflows/<name>/SKILL.md`. Do NOT use `searchArtifacts` to find workflows to run — that tool searches the remote Obot registry for published artifacts, not locally installed workflows.\n\nWhen the user asks you to run a workflow:\n\n1. Load the workflow from `workflows/<name>/SKILL.md`. If you're unsure which workflows are available, use glob pattern `**/SKILL.md` with path `workflows/` to find them.\n2. Use TodoWrite to create a todo for each workflow step before you begin. This is your execution plan — the user will follow along.\n3. **Present the execution plan to the user.** After creating the todos, present a brief summary of what will be executed and ask the user to confirm before proceeding. For example: \"I've planned the following steps: [list steps]. Does this look good to proceed?\"\n4. **Wait for user approval.** Do not begin execution until the user confirms.\n5. Collect any required inputs from the user. If inputs are missing and have no defaults, ask for them.\n6. Call `recordWorkflowRun` with the workflow URI (e.g. `workflow:///workflow-name`) to record the run in the current session.\n7. Execute each step in order:\n    - Check conditions before running conditional steps.\n    - Interpolate variables (`{{input.name}}` and `{{Step Name}}`).\n    - Follow error handling directives.\n    - Mark each todo complete/failed/skipped as you go.\n8. Provide running updates in normal chat messages as you work through each step. If errors occur, judgment calls are needed, or unexpected conditions arise, mention them in chat and add new todos as needed.\n\nComplete tasks fully, follow requested formats exactly, and provide specific analysis rather than vague summaries.\n\nAfter execution, present a structured run summary directly in chat:\n- A heading with the workflow name and a pass/fail indicator (e.g., ## Workflow Run: Code Review)\n- A tally of step outcomes (e.g., \"3 of 3 steps succeeded\" or \"2 of 3 steps succeeded, 1 failed\")\n- For failed steps, a brief explanation of what went wrong\n- A summary of any modifications made to external systems or artifacts produced\n- If the workflow defines an Output section, include that content last\n\nIf the workflow was aborted or no steps completed, still produce a summary explaining what happened and which steps were skipped.\n\n## Improving Workflows\n\nAfter executing a workflow, you can analyze the execution and propose improvements to the workflow file.\n\n**What to look for:**\n- Steps that needed retries or failed — unclear prompt? missing format spec?\n- Steps that produced poor output — missing context? too vague?\n- Steps that should be split into smaller steps\n- Missing error handling for steps that failed\n- Hardcoded values that should be inputs\n\n**Process:** Review which steps succeeded/failed/retried and what fixes were applied. For each issue, determine the concrete fix (rewrite prompt, add format spec, split step, add error handling, etc.). Present each proposed change with the issue, current text, and proposed text.\n\n**IMPORTANT:** Get user approval before editing the workflow file. Never apply changes automatically.\n\n**Example:**\n```\n## Proposed Changes to issue-triage.md\n\n### 1. Step: Analyze Issues\n**Issue:** Agent returned prose instead of JSON. Had to retry.\n**Change:** Add explicit format requirement.\n\nCurrent:\n> Analyze each issue and categorize by priority.\n> Return the results as JSON.\n\nProposed:\n> Analyze each issue and categorize by priority.\n>\n> Return as a JSON array:\n> [{\"number\": 1, \"title\": \"...\", \"priority\": \"High|Medium|Low\", \"reason\": \"...\"}]\n\nApply these changes? (yes/no/select specific)\n```\n\nDon't change things that worked fine or add complexity without a concrete problem to solve.\n\n## Publishing & Sharing Workflows\n\nYou can publish workflows to Obot's registry so other users can discover and install them.\n\n### Publishing\n\nTo publish a workflow, use the `publishArtifact` tool:\n\n1. Ensure the workflow exists in `workflows/<name>/` with a `SKILL.md` that has proper frontmatter (name, description). The workflow must pass format validation before publishing.\n2. Call `publishArtifact` with the workflow directory name. For example: `publishArtifact({ \"workflowName\": \"code-review\" })`.\n3. The tool bundles all files in the directory and uploads to Obot. The `SKILL.md` frontmatter is the source of truth for artifact metadata.\n4. The first publish creates version 1. Subsequent publishes of the same workflow create new versions (v2, v3, etc.).\n5. Version 1 starts owner-only unless its subjects are updated.\n6. Each later published version defaults to the previous version's sharing settings.\n7. To change who a specific published version is shared with, use `setArtifactSubjects({ \"id\": \"<artifact-id>\", \"version\": <n>, \"subjects\": [...] })`. If `version` is omitted, the latest version is updated.\n8. Supported subjects are:\n   - `{ \"type\": \"user\", \"id\": \"<user-id>\" }`\n   - `{ \"type\": \"group\", \"id\": \"<group-id>\" }`\n   - `{ \"type\": \"selector\", \"id\": \"*\" }` for all Obot users\n9. An empty `subjects` list makes that version owner-only again.\n10. To find valid user or group IDs before setting subjects, use `listSubjects({ \"type\": \"user\" | \"group\", \"query\": \"...\" })`. Leave `query` blank to list everything visible.\n\n### Searching the Registry\n\nTo find published or shared workflows from other users in the **remote Obot registry**, use `searchArtifacts`:\n- Search by keyword: `searchArtifacts({ \"query\": \"code review\", \"artifactType\": \"workflow\" })`\n- List all shared workflows: `searchArtifacts({ \"artifactType\": \"workflow\" })` (empty query returns all visible artifacts)\n- This is for discovering shared, public, or published workflows — not for finding workflows already on your local filesystem.\n- **When a user asks about \"shared workflows\", this is the tool to use.**\n\n### Installing\n\nTo install a published workflow, use `installArtifact`:\n- `installArtifact({ \"id\": \"<artifact-id>\" })` installs the latest version\n- `installArtifact({ \"id\": \"<artifact-id>\", \"version\": 2 })` installs a specific version\n- The workflow is automatically extracted into `workflows/<name>/` and is immediately available.\n- Installing overwrites any existing local workflow with the same name.\n","pkg/servers/system/testdata/with-override/skills/workflows.md":"---\nname: Custom Workflows\ndescription: My custom workflows skill that overrides the built-in\n---\n\n# Custom Workflows Skill\n\nThis overrides the built-in workflows skill.\n","pkg/servers/system/testdata/with-user-skills/skills/my-custom-skill.md":"---\nname: Custom Skill\ndescription: A custom skill\n---\n\n# My Custom Skill\n\nCustom content here.\n","pkg/servers/system/testdata/with-user-skills/skills/user-skill.md":"---\nname: User-Defined Skill\ndescription: A user-defined skill\n---\n\n# User Skill\n\nThis is a custom user skill.\n","pkg/servers/workflows/testdata/with-workflows/workflows/another/SKILL.md":"---\nname: another\ndescription: Another workflow for testing multiple workflow listing.\nmetadata:\n  createdAt: \"2026-01-16T10:30:00Z\"\n---\n\n## Steps\n\n### 1. only_step\nExecute the single step.\n\n---\n\n## Output\n\nDone.\n","pkg/servers/workflows/testdata/with-workflows/workflows/no-description/SKILL.md":"# No Workflow Header\n\nThis file doesn't have the standard workflow header format.\n","pkg/servers/workflows/testdata/with-workflows/workflows/test-workflow/SKILL.md":"---\nname: test-workflow\ndescription: This is a test workflow for unit testing purposes.\nlicense: MIT\ncompatibility: nanobot >= 0.1.0\nmetadata:\n  createdAt: \"2026-01-15T09:00:00Z\"\n  author: testuser\n---\n\n## Inputs\n\n- **input_name** (required): A test input parameter.\n\n## Steps\n\n### 1. first_step\nDo something useful.\n\n---\n\n### 2. second_step\nDo something else.\n\n---\n\n## Output\n\nThe workflow completed successfully.\n"},"files":{"CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nNanobot is a standalone MCP (Model Context Protocol) host that enables building agents with MCP and MCP-UI. Unlike built-in MCP hosts in applications like VSCode, Claude, or ChatGPT, Nanobot is designed to be an open-source, deployable solution that combines MCP servers with LLMs to create agent experiences through external interfaces such as Obot. The project is written in Go.\n\n**Technology Stack:**\n- Go 1.26.0 with GORM (SQLite, MySQL, PostgreSQL) and goja (JavaScript runtime for hooks)\n\n## Build and Development Commands\n\n### Backend (Go)\n\n```bash\n# Build the nanobot binary\nmake\n\n# Run nanobot with a configuration file\n./bin/nanobot run ./nanobot.yaml\n\n# Run Go tests\ngo test ./...\n\n# Run a specific test\ngo test ./pkg/agents -run TestName\n\n# Format Go code\ngofmt -w .\n```\n\n## Architecture Overview\n\n### Backend Architecture\n\n**Core Components:**\n\n- **Runtime (`pkg/runtime/`)** - Main orchestration layer that initializes the system. Creates and wires together the LLM client, tool service, agents, and sampling components. Manages the overall execution environment.\n\n- **Agents (`pkg/agents/`)** - Agent execution engine that handles tool mapping, request population, and agent interactions. Responsible for running agents backed by LLMs with access to tools.\n\n- **Tools Service (`pkg/tools/`)** - Central registry for tools and agents. Handles tool discovery, mapping, and execution delegation. Manages MCP server connections.\n\n- **MCP Layer (`pkg/mcp/`)** - MCP protocol implementation including sessions, clients, servers, and wire protocols. Handles both stdio and HTTP transports. Key types:\n  - `Session` - Manages MCP session lifecycle and message routing\n  - `Client` - MCP client implementation for connecting to servers\n  - `Wire` - Protocol transport abstraction (stdio/HTTP)\n\n- **LLM Integration (`pkg/llm/`)** - Abstraction over different LLM providers (OpenAI, Anthropic). Routes requests to appropriate providers based on model names. Handles both completion and response APIs.\n\n- **Session Management (`pkg/session/`, `pkg/sessiondata/`)** - Manages user sessions, conversation state, and session-scoped data. Handles agent context, tool mappings, and resource mappings within sessions. Supports parent-child session relationships and database-backed OAuth token storage.\n\n- **Server Layer (`pkg/server/`)** - HTTP server handling MCP protocol over HTTP. Routes requests for initialize, tools/list, tools/call, prompts/*, resources/*, etc. Manages session creation and request routing.\n\n- **Built-in MCP Servers (`pkg/servers/`)** - Nanobot includes several built-in MCP servers:\n  - `agent/` - Exposes individual agents as MCP servers with chat capabilities\n  - `capabilities/` - Session initialization and capability management (workspace setup)\n  - `meta/` - Metadata and introspection tools (list_chats, update_chat, list_agents)\n  - `resources/` - Database-backed resource management (create_resource, delete_resource) with automatic mimetype detection\n  - `workspace/` - Workspace and session management (create/update/delete workspaces, session reading)\n\n- **Configuration (`pkg/config/`)** - YAML-based configuration loading and validation. Supports profiles, extends (inheritance), and environment variables. See `pkg/config/schema.yaml` for the complete schema.\n\n**Key Architectural Patterns:**\n\n- **Tool Mappings** - Tools from MCP servers are mapped to agent-accessible tools. The `BuildToolMappings` method creates this mapping by resolving tool references from agents and MCP servers.\n\n- **Hooks** - Lifecycle hooks for agents and MCP servers (config, request, response). Hooks are TypeScript/JavaScript functions that can modify configuration and messages. See `hooks.ts` for type definitions.\n\n- **Sandboxing** - MCP servers can run in Docker containers for isolation. The `pkg/mcp/sandbox/` handles containerization and port mapping.\n\nNanobot does not bundle a frontend. External UIs communicate through MCP endpoints, `/api/events`, and the browser proxy routes.\n\n## Configuration\n\nConfiguration is YAML-based. Key top-level sections:\n\n- `agents` - Define agents with their models, tools, instructions, and behaviors\n- `mcp-servers` - MCP server configurations (command, URL, Docker image, etc.)\n- `prompts` - Template definitions\n- `publish` - Defines what to expose when Nanobot itself acts as an MCP server\n- `env` - Environment variable definitions with descriptions and defaults\n- `auth` - Authentication configuration (OAuth, remote headers)\n- `profiles` - Configuration profiles for different environments\n- `extends` - Inherit from other configuration files\n\nExample minimal configuration:\n\n```yaml\nagents:\n  myagent:\n    name: My Agent\n    model: gpt-4\n    mcpServers: my-mcp-server\n\nmcpServers:\n  my-mcp-server:\n    url: https://example.com/mcp\n```\n\n## Important Go Packages\n\n- `pkg/types/` - Core type definitions shared across the system (Config, Agent, Message, ToolCall, etc.)\n- `pkg/complete/` - Utility package for handling option completion and merging\n- `pkg/expr/` - Expression evaluation for dynamic values in configurations\n- `pkg/schema/` - JSON Schema validation and manipulation\n- `pkg/supervise/` - Process supervision for running MCP server subprocesses\n- `pkg/sampling/` - Handles MCP sampling requests (LLM-in-the-loop)\n- `pkg/envvar/` - Environment variable handling with descriptions and defaults\n- `pkg/cmd/` - CLI command handling (routes from `main.go`)\n\n## Entry Points and Special Modes\n\n**Main Entry Point:** `main.go` routes commands to `pkg/cmd`\n\n**Special `_exec` Mode:** Nanobot can act as a daemon wrapper for MCP server subprocesses. When invoked with `_exec` as the first argument, it handles stdio piping and process lifecycle management for MCP servers. This enables Nanobot to supervise and manage MCP server processes.\n\n## Testing\n\nGo tests follow standard Go conventions:\n- Test files are named `*_test.go`\n- Run all tests: `go test ./...`\n- Run specific package tests: `go test ./pkg/agents`\n- Run specific test: `go test ./pkg/agents -run TestName`\n\n## Common Patterns\n\n### Adding a New Agent Hook\n\n1. Define TypeScript types in `hooks.ts` (root level)\n2. Update corresponding Go types in `pkg/types/hooks.go`\n3. Implement hook handling in `pkg/agents/` or relevant package\n4. Hook execution is managed through `pkg/mcp/hooks.go`\n\n### Adding a New Tool\n\n1. Tools come from MCP servers (external or built-in servers in `pkg/servers/`)\n2. Tool resolution happens in `pkg/tools/service.go`\n\n### Working with Sessions\n\n- Sessions are scoped to MCP connections\n- Use `mcp.SessionFromContext(ctx)` to get current session\n- Session state includes tool mappings, current agent, and custom attributes\n- Parent sessions can be accessed via `Session.Parent`\n- Root session can be accessed via `session.Root()`\n\n## MCP Protocol Notes\n\nNanobot supports both MCP standard and MCP-UI extensions:\n- Standard MCP: tools, prompts, resources, sampling\n- MCP-UI: Elicitations (user input prompts), progress notifications, structured UI elements\n\nWhen implementing MCP features, refer to:\n- MCP types in `pkg/mcp/types.go`\n- Message handling in `pkg/mcp/message.go`\n- Protocol reference at `https://modelcontextprotocol.io`\n\n## Code Style\n\n- Go: Follow standard Go conventions, use `gofmt`\n","docs/agents/schemas/agent.frontmatter.schema.json":"{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/agent.frontmatter.schema.json\",\n  \"title\": \"Nanobot Agent Front Matter\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"name\"],\n  \"properties\": {\n    \"name\": { \"type\": \"string\", \"minLength\": 1 },\n    \"description\": { \"type\": \"string\" },\n    \"metadata\": {\n      \"type\": \"object\",\n      \"description\": \"Free-form metadata.\",\n      \"additionalProperties\": true,\n      \"default\": {}\n    },\n\n    \"model\": { \"type\": \"string\" },\n    \"allowed_models\": {\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\" },\n      \"default\": []\n    },\n    \"temperature\": { \"type\": \"number\" },\n    \"max_tokens\": { \"type\": \"integer\", \"minimum\": 1 },\n\n    \"tools\": {\n      \"description\": \"Tools visible/available to the agent. If omitted, defaults to [\\\"inherit\\\"]. Built-ins are logical names (e.g. Read/Write). MCP tools are mcpServer/toolName.\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tool_approvals\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": {\n          \"description\": \"Default authorization behavior. Currently only 'approve' is supported.\",\n          \"type\": \"string\",\n          \"enum\": [\"approve\"],\n          \"default\": \"approve\"\n        },\n        \"rules\": {\n          \"description\": \"Ordered allow rules; first matching rule wins.\",\n          \"type\": \"array\",\n          \"items\": { \"$ref\": \"#/$defs/toolApprovalRule\" },\n          \"default\": []\n        }\n      }\n    },\n\n    \"skills\": {\n      \"description\": \"Skills visible/available to the agent. If omitted, defaults to [\\\"inherit\\\"]. Skill IDs are directory names.\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tasks\": {\n      \"description\": \"Tasks visible/available to the agent (task IDs are directory paths under .nanobot/tasks).\",\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\", \"minLength\": 1 },\n      \"default\": [\"inherit\"]\n    },\n\n    \"task_approvals\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": {\n          \"description\": \"Default task authorization behavior. Currently only 'approve' is supported.\",\n          \"type\": \"string\",\n          \"enum\": [\"approve\"],\n          \"default\": \"approve\"\n        },\n        \"rules\": {\n          \"description\": \"Ordered allow rules; first matching rule wins.\",\n          \"type\": \"array\",\n          \"items\": { \"$ref\": \"#/$defs/taskApprovalRule\" },\n          \"default\": []\n        }\n      }\n    }\n  },\n\n  \"$defs\": {\n    \"toolApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"tool\", \"allow\"],\n      \"properties\": {\n        \"tool\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Tool identifier (must match an entry in tools).\"\n        },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": {\n          \"description\": \"Optional matchers keyed by tool argument name.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"taskApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"task\", \"allow\"],\n      \"properties\": {\n        \"task\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Task identifier (must match an entry in tasks).\"\n        },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": {\n          \"description\": \"Optional matchers keyed by task input name.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"matcher\": {\n      \"description\": \"A value matcher for a tool argument key.\",\n      \"oneOf\": [\n        { \"$ref\": \"#/$defs/equalsMatcher\" },\n        { \"$ref\": \"#/$defs/inMatcher\" },\n        { \"$ref\": \"#/$defs/startsWithMatcher\" },\n        { \"$ref\": \"#/$defs/matchesMatcher\" },\n        { \"$ref\": \"#/$defs/containsMatcher\" },\n        { \"$ref\": \"#/$defs/containsAllMatcher\" },\n        { \"$ref\": \"#/$defs/anyOfMatcher\" },\n        { \"$ref\": \"#/$defs/allOfMatcher\" }\n      ]\n    },\n\n    \"equalsMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"equals\"],\n      \"properties\": {\n        \"equals\": {}\n      }\n    },\n\n    \"inMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"in\"],\n      \"properties\": {\n        \"in\": {\n          \"type\": \"array\",\n          \"items\": {}\n        }\n      }\n    },\n\n    \"startsWithMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"startsWith\"],\n      \"properties\": {\n        \"startsWith\": { \"type\": \"string\" }\n      }\n    },\n\n    \"matchesMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"matches\"],\n      \"properties\": {\n        \"matches\": {\n          \"type\": \"string\",\n          \"description\": \"Regex string.\"\n        }\n      }\n    },\n\n    \"containsMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"contains\"],\n      \"properties\": {\n        \"contains\": {}\n      }\n    },\n\n    \"containsAllMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"containsAll\"],\n      \"properties\": {\n        \"containsAll\": {\n          \"type\": \"array\",\n          \"items\": {}\n        }\n      }\n    },\n\n    \"anyOfMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"anyOf\"],\n      \"properties\": {\n        \"anyOf\": {\n          \"type\": \"array\",\n          \"minItems\": 1,\n          \"items\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"allOfMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"allOf\"],\n      \"properties\": {\n        \"allOf\": {\n          \"type\": \"array\",\n          \"minItems\": 1,\n          \"items\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    }\n  }\n}\n","docs/agents/schemas/mcp.schema.json":"{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/mcp.schema.json\",\n  \"title\": \"Nanobot MCP Configuration\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"mcpServers\"],\n  \"properties\": {\n    \"mcpServers\": {\n      \"description\": \"Map of MCP server name -> server configuration.\",\n      \"type\": \"object\",\n      \"additionalProperties\": { \"$ref\": \"#/$defs/mcpServerConfig\" },\n      \"default\": {}\n    },\n    \"mcpRegistries\": {\n      \"description\": \"Map of registry name -> registry configuration.\",\n      \"type\": \"object\",\n      \"additionalProperties\": { \"$ref\": \"#/$defs/mcpRegistryConfig\" },\n      \"default\": {}\n    }\n  },\n  \"$defs\": {\n    \"mcpServerConfig\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"command\"],\n      \"properties\": {\n        \"command\": {\n          \"description\": \"Executable used to start the MCP server.\",\n          \"type\": \"string\",\n          \"minLength\": 1\n        },\n        \"args\": {\n          \"description\": \"Arguments passed to the command.\",\n          \"type\": \"array\",\n          \"items\": { \"type\": \"string\" },\n          \"default\": []\n        },\n        \"env\": {\n          \"description\": \"Environment variables for the MCP server process.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"type\": \"string\" },\n          \"default\": {}\n        },\n        \"cwd\": {\n          \"description\": \"Optional working directory for the MCP server process.\",\n          \"type\": \"string\"\n        },\n        \"allow_tools\": {\n          \"description\": \"Optional allowlist of tool names exposed by this server (toolName portion of server/toolName). If omitted, all server tools are available to be authorized by agents.\",\n          \"type\": \"array\",\n          \"items\": { \"type\": \"string\", \"minLength\": 1 }\n        },\n        \"timeout_ms\": {\n          \"description\": \"Startup/connection timeout.\",\n          \"type\": \"integer\",\n          \"minimum\": 0,\n          \"default\": 30000\n        }\n      }\n    },\n    \"mcpRegistryConfig\": {\n      \"description\": \"A registry pointing at a standard MCP registry endpoint.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"url\"],\n      \"properties\": {\n        \"url\": {\n          \"description\": \"Base URL of the MCP registry.\",\n          \"type\": \"string\",\n          \"minLength\": 1\n        }\n      }\n    }\n  }\n}\n","docs/agents/schemas/skill.frontmatter.schema.json":"{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/skill.frontmatter.schema.json\",\n  \"title\": \"Nanobot Skill Front Matter (Agent Skills compatible)\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"name\", \"description\"],\n  \"properties\": {\n    \"name\": {\n      \"type\": \"string\",\n      \"minLength\": 1,\n      \"maxLength\": 64,\n      \"pattern\": \"^(?!-)(?!.*--)[a-z0-9-]+(?<!-)$\",\n      \"description\": \"Must be 1-64 chars, lowercase letters/numbers/hyphens, no leading/trailing hyphen, no consecutive hyphens. Must also match directory name (enforced outside JSON Schema).\"\n    },\n    \"description\": {\n      \"type\": \"string\",\n      \"minLength\": 1,\n      \"maxLength\": 1024,\n      \"description\": \"Describe what the skill does and when to use it.\"\n    },\n    \"license\": {\n      \"type\": \"string\",\n      \"description\": \"License name or reference to bundled license file.\"\n    },\n    \"compatibility\": {\n      \"type\": \"string\",\n      \"minLength\": 1,\n      \"maxLength\": 500,\n      \"description\": \"Environment requirements (product, packages, network access, etc.).\"\n    },\n    \"metadata\": {\n      \"type\": \"object\",\n      \"description\": \"Arbitrary key-value mapping.\",\n      \"additionalProperties\": {\n        \"oneOf\": [\n          { \"type\": \"string\" },\n          { \"type\": \"number\" },\n          { \"type\": \"integer\" },\n          { \"type\": \"boolean\" }\n        ]\n      },\n      \"default\": {}\n    },\n    \"allowed-tools\": {\n      \"type\": \"string\",\n      \"description\": \"Space-delimited list of pre-approved tools (experimental). E.g. 'Bash(git:*) Read'.\"\n    },\n\n    \"tools\": {\n      \"description\": \"Nanobot extension: tools visible/available while executing this skill. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tool_approvals\": {\n      \"description\": \"Nanobot extension: tool approval policy while executing this skill.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": {\n          \"description\": \"Default authorization behavior. Currently only 'approve' is supported.\",\n          \"type\": \"string\",\n          \"enum\": [\"approve\"],\n          \"default\": \"approve\"\n        },\n        \"rules\": {\n          \"description\": \"Ordered allow rules; first matching rule wins.\",\n          \"type\": \"array\",\n          \"items\": { \"$ref\": \"#/$defs/toolApprovalRule\" },\n          \"default\": []\n        }\n      }\n    },\n\n    \"skills\": {\n      \"description\": \"Nanobot extension: skills visible/available while executing this skill. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tasks\": {\n      \"description\": \"Nanobot extension: tasks visible/available while executing this skill (task IDs are directory paths under .nanobot/tasks).\",\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\", \"minLength\": 1 },\n      \"default\": [\"inherit\"]\n    },\n\n    \"task_approvals\": {\n      \"description\": \"Nanobot extension: task approval policy while executing this skill.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": {\n          \"description\": \"Default task authorization behavior. Currently only 'approve' is supported.\",\n          \"type\": \"string\",\n          \"enum\": [\"approve\"],\n          \"default\": \"approve\"\n        },\n        \"rules\": {\n          \"description\": \"Ordered allow rules; first matching rule wins.\",\n          \"type\": \"array\",\n          \"items\": { \"$ref\": \"#/$defs/taskApprovalRule\" },\n          \"default\": []\n        }\n      }\n    }\n  },\n\n  \"$defs\": {\n    \"toolApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"tool\", \"allow\"],\n      \"properties\": {\n        \"tool\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Tool identifier (must match an entry in tools).\"\n        },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": {\n          \"description\": \"Optional matchers keyed by tool argument name.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"taskApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"task\", \"allow\"],\n      \"properties\": {\n        \"task\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Task identifier (must match an entry in tasks).\"\n        },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": {\n          \"description\": \"Optional matchers keyed by task input name.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"matcher\": {\n      \"description\": \"A value matcher for a tool argument key.\",\n      \"oneOf\": [\n        { \"$ref\": \"#/$defs/equalsMatcher\" },\n        { \"$ref\": \"#/$defs/inMatcher\" },\n        { \"$ref\": \"#/$defs/startsWithMatcher\" },\n        { \"$ref\": \"#/$defs/matchesMatcher\" },\n        { \"$ref\": \"#/$defs/containsMatcher\" },\n        { \"$ref\": \"#/$defs/containsAllMatcher\" },\n        { \"$ref\": \"#/$defs/anyOfMatcher\" },\n        { \"$ref\": \"#/$defs/allOfMatcher\" }\n      ]\n    },\n\n    \"equalsMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"equals\"],\n      \"properties\": { \"equals\": {} }\n    },\n\n    \"inMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"in\"],\n      \"properties\": {\n        \"in\": {\n          \"type\": \"array\",\n          \"items\": {}\n        }\n      }\n    },\n\n    \"startsWithMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"startsWith\"],\n      \"properties\": { \"startsWith\": { \"type\": \"string\" } }\n    },\n\n    \"matchesMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"matches\"],\n      \"properties\": { \"matches\": { \"type\": \"string\" } }\n    },\n\n    \"containsMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"contains\"],\n      \"properties\": { \"contains\": {} }\n    },\n\n    \"containsAllMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"containsAll\"],\n      \"properties\": {\n        \"containsAll\": {\n          \"type\": \"array\",\n          \"items\": {}\n        }\n      }\n    },\n\n    \"anyOfMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"anyOf\"],\n      \"properties\": {\n        \"anyOf\": {\n          \"type\": \"array\",\n          \"minItems\": 1,\n          \"items\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"allOfMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"allOf\"],\n      \"properties\": {\n        \"allOf\": {\n          \"type\": \"array\",\n          \"minItems\": 1,\n          \"items\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    }\n  }\n}\n","docs/agents/schemas/task.frontmatter.schema.json":"{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/task.frontmatter.schema.json\",\n  \"title\": \"Nanobot Task Front Matter (TASK.md only)\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"name\"],\n  \"properties\": {\n    \"name\": { \"type\": \"string\", \"minLength\": 1 },\n    \"description\": { \"type\": \"string\" },\n    \"metadata\": {\n      \"type\": \"object\",\n      \"description\": \"Free-form metadata.\",\n      \"additionalProperties\": true,\n      \"default\": {}\n    },\n\n    \"agent\": {\n      \"description\": \"Agent ID to execute this task with.\",\n      \"type\": \"string\",\n      \"minLength\": 1\n    },\n\n    \"tools\": {\n      \"description\": \"Tools visible/available while executing this task. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tool_approvals\": {\n      \"description\": \"Tool approval policy while executing this task.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": { \"type\": \"string\", \"enum\": [\"approve\"], \"default\": \"approve\" },\n        \"rules\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/toolApprovalRule\" }, \"default\": [] }\n      }\n    },\n\n    \"tasks\": {\n      \"description\": \"Tasks visible/available while executing this task (task IDs are directory paths under .nanobot/tasks).\",\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\", \"minLength\": 1 },\n      \"default\": [\"inherit\"]\n    },\n\n    \"task_approvals\": {\n      \"description\": \"Task approval policy while executing this task.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": { \"type\": \"string\", \"enum\": [\"approve\"], \"default\": \"approve\" },\n        \"rules\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/taskApprovalRule\" }, \"default\": [] }\n      }\n    },\n\n    \"inputs\": {\n      \"description\": \"Task inputs (TASK.md only).\",\n      \"type\": \"array\",\n      \"items\": { \"$ref\": \"#/$defs/taskInput\" },\n      \"default\": []\n    },\n\n    \"next\": {\n      \"description\": \"Relative path to the next step markdown file within the same task directory.\",\n      \"type\": \"string\",\n      \"minLength\": 1\n    }\n  },\n\n  \"$defs\": {\n    \"taskInput\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"name\", \"description\"],\n      \"properties\": {\n        \"name\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Input name.\"\n        },\n        \"description\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Describe what the input means and expected format.\"\n        },\n        \"default\": {\n          \"type\": \"string\",\n          \"description\": \"Optional default value (string).\"\n        }\n      }\n    },\n\n    \"toolApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"tool\", \"allow\"],\n      \"properties\": {\n        \"tool\": { \"type\": \"string\", \"minLength\": 1 },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": { \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" } }\n      }\n    },\n\n    \"taskApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"task\", \"allow\"],\n      \"properties\": {\n        \"task\": { \"type\": \"string\", \"minLength\": 1 },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": { \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" } }\n      }\n    },\n\n    \"matcher\": {\n      \"oneOf\": [\n        { \"$ref\": \"#/$defs/equalsMatcher\" },\n        { \"$ref\": \"#/$defs/inMatcher\" },\n        { \"$ref\": \"#/$defs/startsWithMatcher\" },\n        { \"$ref\": \"#/$defs/matchesMatcher\" },\n        { \"$ref\": \"#/$defs/containsMatcher\" },\n        { \"$ref\": \"#/$defs/containsAllMatcher\" },\n        { \"$ref\": \"#/$defs/anyOfMatcher\" },\n        { \"$ref\": \"#/$defs/allOfMatcher\" }\n      ]\n    },\n\n    \"equalsMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"equals\"], \"properties\": { \"equals\": {} } },\n    \"inMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"in\"], \"properties\": { \"in\": { \"type\": \"array\", \"items\": {} } } },\n    \"startsWithMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"startsWith\"], \"properties\": { \"startsWith\": { \"type\": \"string\" } } },\n    \"matchesMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"matches\"], \"properties\": { \"matches\": { \"type\": \"string\" } } },\n    \"containsMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"contains\"], \"properties\": { \"contains\": {} } },\n    \"containsAllMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"containsAll\"], \"properties\": { \"containsAll\": { \"type\": \"array\", \"items\": {} } } },\n    \"anyOfMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"anyOf\"], \"properties\": { \"anyOf\": { \"type\": \"array\", \"minItems\": 1, \"items\": { \"$ref\": \"#/$defs/matcher\" } } } },\n    \"allOfMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"allOf\"], \"properties\": { \"allOf\": { \"type\": \"array\", \"minItems\": 1, \"items\": { \"$ref\": \"#/$defs/matcher\" } } } }\n  }\n}\n","docs/agents/schemas/task.step.frontmatter.schema.json":"{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/task.step.frontmatter.schema.json\",\n  \"title\": \"Nanobot Task Step Front Matter\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"name\"],\n  \"properties\": {\n    \"name\": { \"type\": \"string\", \"minLength\": 1 },\n    \"description\": { \"type\": \"string\" },\n    \"metadata\": {\n      \"type\": \"object\",\n      \"description\": \"Free-form metadata.\",\n      \"additionalProperties\": true,\n      \"default\": {}\n    },\n\n    \"agent\": {\n      \"description\": \"Agent ID to execute this step with.\",\n      \"type\": \"string\",\n      \"minLength\": 1\n    },\n\n    \"tools\": {\n      \"description\": \"Tools visible/available while executing this step. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tool_approvals\": {\n      \"description\": \"Tool approval policy while executing this step.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": { \"type\": \"string\", \"enum\": [\"approve\"], \"default\": \"approve\" },\n        \"rules\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/toolApprovalRule\" }, \"default\": [] }\n      }\n    },\n\n    \"skills\": {\n      \"description\": \"Skills visible/available while executing this step. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tasks\": {\n      \"description\": \"Tasks visible/available while executing this step (task IDs are directory paths under .nanobot/tasks).\",\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\", \"minLength\": 1 },\n      \"default\": [\"inherit\"]\n    },\n\n    \"next\": {\n      \"description\": \"Relative path to the next step markdown file within the same task directory.\",\n      \"type\": \"string\",\n      \"minLength\": 1\n    }\n  },\n\n  \"$defs\": {\n    \"toolApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"tool\", \"allow\"],\n      \"properties\": {\n        \"tool\": { \"type\": \"string\", \"minLength\": 1 },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": { \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" } }\n      }\n    },\n    \"taskApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"task\", \"allow\"],\n      \"properties\": {\n        \"task\": { \"type\": \"string\", \"minLength\": 1 },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": { \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" } }\n      }\n    },\n\n    \"matcher\": {\n      \"oneOf\": [\n        { \"$ref\": \"#/$defs/equalsMatcher\" },\n        { \"$ref\": \"#/$defs/inMatcher\" },\n        { \"$ref\": \"#/$defs/startsWithMatcher\" },\n        { \"$ref\": \"#/$defs/matchesMatcher\" },\n        { \"$ref\": \"#/$defs/containsMatcher\" },\n        { \"$ref\": \"#/$defs/containsAllMatcher\" },\n        { \"$ref\": \"#/$defs/anyOfMatcher\" },\n        { \"$ref\": \"#/$defs/allOfMatcher\" }\n      ]\n    },\n    \"equalsMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"equals\"], \"properties\": { \"equals\": {} } },\n    \"inMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"in\"], \"properties\": { \"in\": { \"type\": \"array\", \"items\": {} } } },\n    \"startsWithMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"startsWith\"], \"properties\": { \"startsWith\": { \"type\": \"string\" } } },\n    \"matchesMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"matches\"], \"properties\": { \"matches\": { \"type\": \"string\" } } },\n    \"containsMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"contains\"], \"properties\": { \"contains\": {} } },\n    \"containsAllMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"containsAll\"], \"properties\": { \"containsAll\": { \"type\": \"array\", \"items\": {} } } },\n    \"anyOfMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"anyOf\"], \"properties\": { \"anyOf\": { \"type\": \"array\", \"minItems\": 1, \"items\": { \"$ref\": \"#/$defs/matcher\" } } } },\n    \"allOfMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"allOf\"], \"properties\": { \"allOf\": { \"type\": \"array\", \"minItems\": 1, \"items\": { \"$ref\": \"#/$defs/matcher\" } } } }\n  }\n}\n","pkg/config/testdata/directory-with-readme/agents/README.md":"# README\n\nThis is documentation and should not be loaded as an agent.\n","pkg/servers/system/skills/browser-use.md":"---\nname: browser-use\ndescription: Automates browser interactions for web testing, form filling, screenshots, and data extraction. Use when the user needs to navigate websites, interact with web pages, fill forms, take screenshots, or extract information from web pages. IMPORTANT - If webfetch fails, try this tool instead.\n---\n\n# Browser Automation with browser-use CLI\n\nThe `browser-use` command provides fast, persistent browser automation. It maintains browser sessions across commands, enabling complex multi-step workflows.\n\n`browser-use` is already installed in the dedicated Nanobot agent image. Use the built-in CLI directly.\n\nWhen using this skill, default to `--browser chromium --headed` unless the user explicitly wants headless/background execution. Headed mode is the safer default because it keeps the browser visible in BrowserView and makes manual intervention possible.\n\n## Quick Start\n\n```bash\nbrowser-use --browser chromium --headed open https://example.com  # Navigate to URL\nbrowser-use state                              # Get page elements with indices\nbrowser-use click 5                            # Click element by index\nbrowser-use type \"Hello World\"                 # Type text\nbrowser-use screenshot                         # Take screenshot\nbrowser-use close                              # Close browser\n```\n\n## Core Workflow\n\n1. **Navigate**: `browser-use --browser chromium --headed open <url>` - Opens URL (starts browser if needed)\n2. **Inspect**: `browser-use state` - Returns clickable elements with indices\n3. **Interact**: Use indices from state to interact (`browser-use click 5`, `browser-use input 3 \"text\"`)\n4. **Verify**: `browser-use state` or `browser-use screenshot` to confirm actions\n5. **Repeat**: Browser stays open between commands\n\n## Browser Modes\n\n```bash\nbrowser-use --browser chromium --headed open <url>  # Recommended default: visible Chromium window\nbrowser-use --browser chromium open <url>      # Headless Chromium for explicit background runs\nbrowser-use --browser real open <url>          # User's Chrome with login sessions\n```\n\n- **chromium**: Fast, isolated, and should usually be run with `--headed`\n- **real**: Uses your Chrome with cookies, extensions, logged-in sessions\n\n## Browser Viewing with VNC\n\nThe VNC server is automatically started and ready to use. You can view and interact with the browser in real-time using VNC. This is **essential** for:\n\n- **Solving CAPTCHAs** - Many sites require human verification\n- **Debugging** - See exactly what the browser is doing\n- **User interaction** - Manual intervention when automation gets stuck\n\nThe VNC server provides:\n- X virtual framebuffer (Xvfb) on display :99\n- VNC server on port 5900\n- an internal WebSocket proxy on port 6080, exposed only through Nanobot's `/browser` path\n- a borderless, maximized browser window sized for the BrowserView pane by default\n\n### Accessing the Browser View\n\nThe Nanobot UI includes a browser viewer component that embeds VNC access. The backend proxies only the WebSocket transport; the standalone noVNC `vnc.html` page is not exposed.\n\n### Using with browser-use\n\n**Default to `--headed` mode. It is required when you need VNC viewing:**\n\n```bash\n# RECOMMENDED DEFAULT (visible in VNC viewer, user can interact)\nbrowser-use --browser chromium --headed open https://example.com\n\n# HEADLESS ONLY WHEN EXPLICITLY APPROPRIATE (faster, but user can't see it)\nbrowser-use --browser chromium open https://example.com\n```\n\nThe `--headed` flag makes the browser visible on the VNC display (:99).\n\n### CAPTCHA Solving Workflow\n\n**When a CAPTCHA is encountered:**\n\n1. **Detect the CAPTCHA** - Look for verification challenges, \"I'm not a robot\" checkboxes, or automation blocks\n2. **Inform the user immediately** - Tell them a CAPTCHA needs to be solved\n3. **Ensure VNC is running** - Verify the browser is running in `--headed` mode\n4. **Guide the user** - Tell them to open the BrowserView pane in the Nanobot UI\n5. **Wait for completion** - Pause automation until the user confirms they've solved the CAPTCHA\n6. **Continue** - Resume automation after manual intervention\n\n**Example:**\n```bash\n# You're automating a task and hit a CAPTCHA\nbrowser-use --browser chromium --headed open https://example.com\nbrowser-use state\n# → Detects CAPTCHA verification challenge\n\n# STOP and inform user:\n# \"I've encountered a CAPTCHA that needs human verification.\n#  Please open the BrowserView pane in the Nanobot UI\n#  and solve the CAPTCHA. Let me know when you're done.\"\n\n# Wait for user confirmation, then continue:\nbrowser-use state  # Verify CAPTCHA is solved\nbrowser-use click 5  # Continue with automation\n```\n\n**Important:** CAPTCHAs cannot be automated - they require human interaction. Always use `--headed` mode when you anticipate CAPTCHAs, and be prepared to pause and ask the user for help.\n\n## Commands\n\n### Navigation\n```bash\nbrowser-use open <url>                    # Navigate to URL\nbrowser-use back                          # Go back in history\nbrowser-use scroll down                   # Scroll down\nbrowser-use scroll up                     # Scroll up\n```\n\n### Page State\n```bash\nbrowser-use state                         # Get URL, title, and clickable elements\nbrowser-use screenshot                    # Take screenshot (outputs base64)\nbrowser-use screenshot path.png           # Save screenshot to file\nbrowser-use screenshot --full path.png    # Full page screenshot\n```\n\n### Interactions (use indices from `browser-use state`)\n```bash\nbrowser-use click <index>                 # Click element\nbrowser-use type \"text\"                   # Type text into focused element\nbrowser-use input <index> \"text\"          # Click element, then type text\nbrowser-use keys \"Enter\"                  # Send keyboard keys\nbrowser-use keys \"Control+a\"              # Send key combination\nbrowser-use select <index> \"option\"       # Select dropdown option\n```\n\n### Tab Management\n```bash\nbrowser-use switch <tab>                  # Switch to tab by index\nbrowser-use close-tab                     # Close current tab\nbrowser-use close-tab <tab>               # Close specific tab\n```\n\n### JavaScript & Data\n```bash\nbrowser-use eval \"document.title\"         # Execute JavaScript, return result\n```\n\n### Cookies\n```bash\nbrowser-use cookies get                   # Get all cookies\nbrowser-use cookies get --url <url>       # Get cookies for specific URL\nbrowser-use cookies set <name> <value>    # Set a cookie\nbrowser-use cookies set name val --domain .example.com --secure --http-only\nbrowser-use cookies clear                 # Clear all cookies\nbrowser-use cookies clear --url <url>     # Clear cookies for specific URL\nbrowser-use cookies export <file>         # Export all cookies to JSON file\nbrowser-use cookies export <file> --url <url>  # Export cookies for specific URL\nbrowser-use cookies import <file>         # Import cookies from JSON file\n```\n\n### Wait Conditions\n```bash\nbrowser-use wait selector \"h1\"            # Wait for element to be visible\nbrowser-use wait selector \".loading\" --state hidden  # Wait for element to disappear\nbrowser-use wait selector \"#btn\" --state attached    # Wait for element in DOM\nbrowser-use wait text \"Success\"           # Wait for text to appear\nbrowser-use wait selector \"h1\" --timeout 5000  # Custom timeout in ms\n```\n\n### Additional Interactions\n```bash\nbrowser-use hover <index>                 # Hover over element (triggers CSS :hover)\nbrowser-use dblclick <index>              # Double-click element\nbrowser-use rightclick <index>            # Right-click element (context menu)\n```\n\n### Information Retrieval\n```bash\nbrowser-use get title                     # Get page title\nbrowser-use get html                      # Get full page HTML\nbrowser-use get html --selector \"h1\"      # Get HTML of specific element\nbrowser-use get text <index>              # Get text content of element\nbrowser-use get value <index>             # Get value of input/textarea\nbrowser-use get attributes <index>        # Get all attributes of element\nbrowser-use get bbox <index>              # Get bounding box (x, y, width, height)\n```\n\n### Python Execution (Persistent Session)\n```bash\nbrowser-use python \"x = 42\"               # Set variable\nbrowser-use python \"print(x)\"             # Access variable (outputs: 42)\nbrowser-use python \"print(browser.url)\"   # Access browser object\nbrowser-use python --vars                 # Show defined variables\nbrowser-use python --reset                # Clear Python namespace\nbrowser-use python --file script.py       # Execute Python file\n```\n\nThe Python session maintains state across commands. The `browser` object provides:\n- `browser.url` - Current page URL\n- `browser.title` - Page title\n- `browser.goto(url)` - Navigate\n- `browser.click(index)` - Click element\n- `browser.type(text)` - Type text\n- `browser.screenshot(path)` - Take screenshot\n- `browser.scroll()` - Scroll page\n- `browser.html` - Get page HTML\n\n### Session Management\n```bash\nbrowser-use sessions                      # List active sessions\nbrowser-use close                         # Close current session\nbrowser-use close --all                   # Close all sessions\n```\n\n### Profile Management\n```bash\nbrowser-use profile list-local            # List local Chrome profiles\n```\n\n**Before opening a real browser (`--browser real`)**, always ask the user if they want to use a specific Chrome profile or no profile. Use `profile list-local` to show available profiles:\n\n```bash\nbrowser-use profile list-local\n# Output: Default: Person 1 (user@gmail.com)\n#         Profile 1: Work (work@company.com)\n\n# With a specific profile (has that profile's cookies/logins)\nbrowser-use --browser real --profile \"Profile 1\" open https://gmail.com\n\n# Without a profile (fresh browser, no existing logins)\nbrowser-use --browser real open https://gmail.com\n\n# Headless mode (no visible window) - useful for cookie export\nbrowser-use --browser real --profile \"Default\" cookies export /tmp/cookies.json\n```\n\nEach Chrome profile has its own cookies, history, and logged-in sessions. Choosing the right profile determines whether sites will be pre-authenticated.\n\n## Global Options\n\n| Option | Description |\n|--------|-------------|\n| `--session NAME` | Use named session (default: \"default\") |\n| `--browser MODE` | Browser mode: chromium, real |\n| `--headed` | Show browser window (chromium mode) |\n| `--profile NAME` | Chrome profile (real mode only) |\n| `--json` | Output as JSON |\n\n**Session behavior**: All commands without `--session` use the same \"default\" session. The browser stays open and is reused across commands. Use `--session NAME` to run multiple browsers in parallel.\n\n## Usage Notes\n\n- Always run `browser-use state` before interacting so you have current element indices.\n- Default to `--browser chromium --headed`.\n- Only omit `--headed` when the user explicitly wants headless/background execution or when visibility and manual takeover are clearly unnecessary.\n- Use `browser-use screenshot` or `browser-use state` to confirm each critical action.\n- Close the session with `browser-use close` when the browser task is complete.\n","pkg/servers/system/skills/python-scripts.md":"---\nname: python-scripts\ndescription: Write and execute Python scripts using uv for programmatic tasks.\n---\n\nUse Python when a step needs data transformation, API calls, calculations, or deterministic operations where code is more reliable than natural language.\n\n## How to Use\n\nWrite a script with inline dependencies and run it with `uv run`:\n\n```python\n#!/usr/bin/env python3\n# /// script\n# requires-python = \">=3.11\"\n# dependencies = [\n#     \"requests\",\n# ]\n# ///\n\nimport json\nimport requests\n\ndata = requests.get(\"https://api.example.com/data\").json()\nprint(json.dumps(data))\n```\n\n```bash\nuv run script.py\n```\n\nThe `# /// script` block declares dependencies - `uv` installs them automatically.\n\n## Output Conventions\n\n- **Structured data**: Print JSON to stdout\n- **Progress/debug info**: Print to stderr\n- **Errors**: Print to stderr and exit with non-zero code\n\n```python\nimport sys\nimport json\n\n# Output result\nprint(json.dumps({\"items\": [...], \"count\": 42}))\n\n# Debug info (won't pollute output)\nprint(\"Processed 42 items\", file=sys.stderr)\n```\n\n## When to Use Python vs LLM\n\n**Use Python for:** JSON manipulation, API calls with auth/pagination, calculations, file parsing, sorting/filtering, large datasets\n\n**Keep as LLM for:** Tasks requiring judgment, natural language generation, flexible interpretation\n","pkg/servers/system/skills/scheduled-tasks.md":"---\nname: scheduled-tasks\ndescription: Load this skill for ANY request that mentions scheduled tasks, recurring jobs, reminders, future runs, or time-based automation.\n---\n\n# Scheduled Tasks\n\nScheduled tasks are persistent task definitions managed through the `nanobot.tasks` tools. When a scheduled task fires, nanobot starts a new chat thread and runs the saved prompt there.\n\n## When to Use Scheduled Tasks\n\nUse scheduled tasks when work should happen later or recur on a schedule:\n- Daily, weekly, or monthly reports\n- Reminders or recurring checks\n- Periodic file updates, cleanups, or syncs\n\nIf the user wants the work done right now and not again later, do it directly instead of creating a scheduled task.\n\n## Scheduled Tasks vs Workflows\n\nDo not conflate scheduled tasks with workflows:\n- **Workflows** are reusable procedures stored under `workflows/`.\n- **Scheduled tasks** decide when nanobot should start a future chat thread.\n\nUse a workflow when the user wants a reusable multi-step procedure. Use a scheduled task when the user wants something to happen later or recur automatically.\n\nIf the user wants a workflow to run on a schedule, keep them separate:\n1. Create or update the workflow first.\n2. Create a scheduled task whose prompt tells nanobot to run that workflow.\n\n## Managing Existing Tasks\n\nBefore updating, deleting, or manually running an existing task, call `listScheduledTasks` unless the user already provided the exact `task:///...` URI.\n\nUse the task URI for all follow-up task operations:\n- `updateScheduledTask`\n- `deleteScheduledTask`\n- `startScheduledTask`\n\n## Timezone Rules\n\nWhen creating a scheduled task, always set the timezone explicitly.\n\n- If the user's current timezone is already known from context, use that by default unless they say otherwise.\n- If you do not know the user's timezone, collect it before creating the task.\n- Do not guess a timezone from a city, country, or language unless the user explicitly gave enough information to make that unambiguous.\n\nUnless the user asks for a different timezone, new scheduled tasks should use the user's current timezone.\n\n## Valid Schedule Shapes\n\nThe task tools only accept five-field cron expressions in these shapes:\n\n- **Daily:** minute hour `* * *`\n  - Example: `45 2 * * *` for every day at 02:45\n- **Weekly:** minute hour `* * day-of-week`\n  - Example: `20 23 * * 1,2,3,4` for Monday through Thursday at 23:20\n- **Monthly:** minute hour `day-of-month * *`\n  - Example: `45 2 22,24 * *` for the 22nd and 24th of every month at 02:45\n- **One-time date:** minute hour `day-of-month month *` with a matching expiration date\n  - Example: `45 2 26 3 *` with expiration `2026-03-26` for March 26 at 02:45\n\nDo not use unsupported cron shapes. In particular:\n- Do not use six-field cron expressions\n- Do not use schedules that combine both day-of-month and day-of-week\n- Do not use arbitrary cron patterns outside the daily, weekly, monthly, or one-time date forms above\n\n## Designing Good Task Prompts\n\nEach scheduled run starts a fresh chat thread. The stored prompt should be self-contained:\n- State exactly what to do\n- Name any files, outputs, or deliverables to produce\n- Mention any workflow to run by name if applicable\n- Avoid depending on transient context from the current conversation\n\nFor recurring actions that send messages or modify external systems, confirm the user's intent unless they have already clearly requested that recurring behavior.\n","pkg/servers/system/skills/workflows.md":"---\nname: workflows\ndescription: Load this skill for ANY request that mentions workflows, including listing, creating, running, editing, or discussing them.\n---\n\n# Workflows\n\nWorkflows are directories in `workflows/` that codify repeatable processes. Each workflow is a directory containing a `SKILL.md` file (with YAML frontmatter) and any supporting files (scripts, assets, etc.). Each step's output can be referenced by later steps using `{{Step Name}}`.\n\n## Discovering Workflows\n\nThere are two places workflows can exist:\n\n1. **Local workflows** — in the `workflows/` directory on the local filesystem. These are workflows the user has created or installed. List the `workflows/` directory to find these.\n2. **Shared/published workflows** — in the remote Obot registry. These are workflows other users have published (publicly or to the organization). Use `searchArtifacts` to find these.\n\n**When the user asks about \"shared workflows\", \"public workflows\", workflows from other users, or wants to discover/find/browse available workflows they haven't installed yet, ALWAYS use `searchArtifacts` to search the remote registry.** Do not just list the local `workflows/` directory — that only shows what is already installed locally.\n\nIf the user asks to \"list all workflows\", \"show my workflows\", \"which workflows are installed\" or similar without specifying shared/public, find workflows by using the Glob tool (never Bash with find or ls) with pattern `**/SKILL.md` in the `workflows/` directory (do NOT include `workflows/` in both the path and the pattern — that double-nests). If they ask to \"list shared workflows\" or \"find workflows\", use `searchArtifacts`.\n\n## When to Use Workflows\n\nWorkflows are for repeatable tasks. If it's a one-time thing, just execute it directly.\n\n## Tracking Progress\n\nUse TodoWrite during **both** workflow design and execution. The user sees your todo list — it's your primary way of keeping them informed.\n\n- **When designing:** Create todos for your design steps (gather requirements, identify inputs/edge cases, draft workflow, write file, present for review).\n- **When executing:** Create a todo for each workflow step and update status as you go. Add new todos if unexpected issues arise.\n\nKeep todo titles short and scannable. Update them as you progress — don't leave stale todos sitting around.\n\n## Workflow Format\n\nEach workflow is a directory under `workflows/` containing a `SKILL.md` file with:\n- **Frontmatter** (required): YAML frontmatter with `name` (lowercase-hyphenated slug that must match the directory name), `description` (always wrap in double quotes to avoid YAML parsing errors from colons or special characters), and optional `metadata` map.\n- **Inputs**: Optional parameters with defaults\n- **Steps**: Numbered steps with clear instructions\n- **Output**: Optional template for the final result\n\nWhen creating a new workflow, always include frontmatter with `name` (must match the directory name, lowercase with hyphens only), `description` (always in double quotes), and `metadata.createdAt` set to the current date/time in ISO 8601 format (e.g., `2026-01-15T09:00:00Z`). Use `bash` (e.g., `date -u +\"%Y-%m-%dT%H:%M:%SZ\"`) to get the current UTC time — do not guess or hardcode it.\n\n**IMPORTANT:** Always wrap the `description` value in double quotes in the YAML frontmatter. Unquoted descriptions containing colons (`:`) cause YAML parsing errors. For example, write `description: \"Analyze code: find and fix issues\"` not `description: Analyze code: find and fix issues`.\n\n**Name constraints:** lowercase letters, numbers, and hyphens only; no leading/trailing or consecutive hyphens; must match the directory name exactly.\n\n### Example: Simple Review\n\n```markdown\n---\nname: code-review\ndescription: \"Review code changes for quality issues.\"\nmetadata:\n  createdAt: \"2026-01-15T09:00:00Z\"\n---\n\n## Inputs\n\n- **target** (optional): Files to review. Default: `.`\n\n## Steps\n\n### 1. Find Changes\nFind all modified code files in {{input.target}}.\n\n### 2. Review Code\nReview these files for quality issues:\n{{Find Changes}}\n\nFocus on: error handling, edge cases, and readability.\n\n## Output\n\n{{Review Code}}\n```\n\n### Example: With Conditions\n\n```markdown\n---\nname: smart-fix\ndescription: \"Analyze an issue and apply a fix only if it's safe to do so.\"\nmetadata:\n  createdAt: \"2026-01-15T09:00:00Z\"\n---\n\n## Steps\n\n### 1. Analyze Issue\nAnalyze the reported issue and determine severity.\n\n### 2. Check Safety\nBased on this analysis, determine if an automated fix is safe:\n{{Analyze Issue}}\n\nEnd your response with SAFE or UNSAFE.\n\n### 3. Apply Fix\nApply the fix for: {{Analyze Issue}}\n\n**Condition:** {{Check Safety}} contains SAFE\n\n### 4. Create Manual Report\nCreate a report explaining why manual intervention is needed:\n{{Analyze Issue}}\n\n**Condition:** {{Check Safety}} contains UNSAFE\n```\n\n### Example: With Error Handling\n\n```markdown\n---\nname: deploy-with-rollback\ndescription: \"Deploy changes with automatic rollback on failure.\"\nmetadata:\n  createdAt: \"2026-01-15T09:00:00Z\"\n---\n\n## Inputs\n\n- **environment** (required): Target environment (staging, production)\n\n## Steps\n\n### 1. Run Tests\nRun the full test suite. Report any failures.\n\n### 2. Deploy\nDeploy to {{input.environment}}.\n\n**On error:** Rollback\n\n### 3. Verify\nVerify the deployment is healthy.\n\n**On error:** Rollback\n\n### 4. Rollback\nRoll back to the previous version and report what went wrong.\n\n## Output\n\nDeployment to {{input.environment}} complete.\n{{Verify}}\n```\n\n## Key Concepts\n\n**Variables:** Use `{{input.name}}` for inputs and `{{Step Name}}` for outputs from previous steps.\n\n**Conditions:** Add `**Condition:**` to make a step run only when the condition is true. Common patterns:\n- `{{Step Name}} contains X` - check if output contains text\n- `{{Step Name}} not empty` - check if output exists\n- `{{input.flag}} equals yes` - check exact value\n\n**Error handling:** Add `**On error:**` to control what happens when a step fails:\n- `stop` (default) - halt the workflow\n- `continue` - log the error and move to the next step\n- `Step Name` - jump to a specific step (useful for cleanup/rollback)\n\n**Output:** The `## Output` section defines the final result. If omitted, the last step's output is used.\n\n**File dependencies:** When a workflow depends on any files (scripts, templates, data files, configs, etc.), those files **must** be placed in the same directory as the workflow's `SKILL.md` file or in a subdirectory within it. Workflows are self-contained directories — files outside the workflow directory will not be available when the workflow is published, installed, or shared.\n\n## Designing Workflows\n\nWhen asked to create a workflow, DO NOT start writing it immediately. Follow this process:\n\n### Phase 1: Understand\n\n1. Use TodoWrite to create a plan for designing the workflow (e.g., \"Gather requirements\", \"Identify inputs and edge cases\", \"Draft workflow\", \"Write to file\", \"Present for review\").\n2. If the user has stated generically that they want to create a workflow, chat with them to get a better idea of what they want. Don't use the AskUserQuestion tool at this phase.\n3. Once you have a general sense of the workflow they want to build, ask clarifying questions before doing anything else. DO use the AskUserQuestion tool for these specific questions. You need to understand:\n    - What the workflow should accomplish end-to-end\n    - What the expected outcome is\n    - What inputs/variables are needed\n    - What external services or tools are involved\n    - How errors should be handled (stop? retry?)\n    - DON'T ask about execution frequency or external triggers because we don't yet support those features \n4. **Wait for answers. Do not proceed until you have enough information to design a good workflow.** Do not guess or assume — ask.\n\n### Phase 2: Draft & Save\n\n1. Once you understand the requirements, draft the workflow.\n2. Create the directory `workflows/<name>/` and write the workflow to `workflows/<name>/SKILL.md`. Place any supporting files (scripts, data, etc.) alongside `SKILL.md` in the same directory.\n3. Mark your design todos as complete.\n\n### Phase 3: Hand Off\n\n1. Present the workflow to the user with a brief summary of what each step does.\n2. Ask the user to review it. Offer to make changes.\n3. **Offer to execute the workflow — do NOT execute it automatically.** Say something like: \"Want me to run this now, or would you like to make changes first?\"\n\n**IMPORTANT:** Never skip Phase 1. Never jump straight to writing. Never auto-execute after designing.\n\nUse descriptive step names in title case (e.g., \"Fetch Issues\" not \"step1\") — these become variables for later steps.\n\nFor deterministic operations (data transformation, calculations), consider having steps use Python scripts (see `python-scripts` skill). For external services (Slack, Jira, databases), consider MCP servers (see `mcp-curl` skill).\n\n## Executing Workflows\n\nUsers may ask to run a workflow at any time — not just immediately after designing one. Workflows in `workflows/` are persistent, reusable artifacts. A user might say \"run the code-review workflow\" days or weeks after it was created. Treat execution as a standalone operation: load the file, collect inputs, and run it. Don't assume you have any prior context about the workflow beyond what's in the file itself.\n\n**IMPORTANT:** If the user asks you to *create* a workflow, do NOT auto-execute it after writing the file. Present it for review and offer to run it. But if the user asks you to *run* an existing workflow, go ahead — that's an explicit request.\n\n**IMPORTANT:** Installed workflows live on the local filesystem in the `workflows/` directory. When running a workflow, ALWAYS load it from `workflows/<name>/SKILL.md`. Do NOT use `searchArtifacts` to find workflows to run — that tool searches the remote Obot registry for published artifacts, not locally installed workflows.\n\nWhen the user asks you to run a workflow:\n\n1. Load the workflow from `workflows/<name>/SKILL.md`. If you're unsure which workflows are available, use glob pattern `**/SKILL.md` with path `workflows/` to find them.\n2. Use TodoWrite to create a todo for each workflow step before you begin. This is your execution plan — the user will follow along.\n3. **Present the execution plan to the user.** After creating the todos, present a brief summary of what will be executed and ask the user to confirm before proceeding. For example: \"I've planned the following steps: [list steps]. Does this look good to proceed?\"\n4. **Wait for user approval.** Do not begin execution until the user confirms.\n5. Collect any required inputs from the user. If inputs are missing and have no defaults, ask for them.\n6. Call `recordWorkflowRun` with the workflow URI (e.g. `workflow:///workflow-name`) to record the run in the current session.\n7. Execute each step in order:\n    - Check conditions before running conditional steps.\n    - Interpolate variables (`{{input.name}}` and `{{Step Name}}`).\n    - Follow error handling directives.\n    - Mark each todo complete/failed/skipped as you go.\n8. Provide running updates in normal chat messages as you work through each step. If errors occur, judgment calls are needed, or unexpected conditions arise, mention them in chat and add new todos as needed.\n\nComplete tasks fully, follow requested formats exactly, and provide specific analysis rather than vague summaries.\n\nAfter execution, present a structured run summary directly in chat:\n- A heading with the workflow name and a pass/fail indicator (e.g., ## Workflow Run: Code Review)\n- A tally of step outcomes (e.g., \"3 of 3 steps succeeded\" or \"2 of 3 steps succeeded, 1 failed\")\n- For failed steps, a brief explanation of what went wrong\n- A summary of any modifications made to external systems or artifacts produced\n- If the workflow defines an Output section, include that content last\n\nIf the workflow was aborted or no steps completed, still produce a summary explaining what happened and which steps were skipped.\n\n## Improving Workflows\n\nAfter executing a workflow, you can analyze the execution and propose improvements to the workflow file.\n\n**What to look for:**\n- Steps that needed retries or failed — unclear prompt? missing format spec?\n- Steps that produced poor output — missing context? too vague?\n- Steps that should be split into smaller steps\n- Missing error handling for steps that failed\n- Hardcoded values that should be inputs\n\n**Process:** Review which steps succeeded/failed/retried and what fixes were applied. For each issue, determine the concrete fix (rewrite prompt, add format spec, split step, add error handling, etc.). Present each proposed change with the issue, current text, and proposed text.\n\n**IMPORTANT:** Get user approval before editing the workflow file. Never apply changes automatically.\n\n**Example:**\n```\n## Proposed Changes to issue-triage.md\n\n### 1. Step: Analyze Issues\n**Issue:** Agent returned prose instead of JSON. Had to retry.\n**Change:** Add explicit format requirement.\n\nCurrent:\n> Analyze each issue and categorize by priority.\n> Return the results as JSON.\n\nProposed:\n> Analyze each issue and categorize by priority.\n>\n> Return as a JSON array:\n> [{\"number\": 1, \"title\": \"...\", \"priority\": \"High|Medium|Low\", \"reason\": \"...\"}]\n\nApply these changes? (yes/no/select specific)\n```\n\nDon't change things that worked fine or add complexity without a concrete problem to solve.\n\n## Publishing & Sharing Workflows\n\nYou can publish workflows to Obot's registry so other users can discover and install them.\n\n### Publishing\n\nTo publish a workflow, use the `publishArtifact` tool:\n\n1. Ensure the workflow exists in `workflows/<name>/` with a `SKILL.md` that has proper frontmatter (name, description). The workflow must pass format validation before publishing.\n2. Call `publishArtifact` with the workflow directory name. For example: `publishArtifact({ \"workflowName\": \"code-review\" })`.\n3. The tool bundles all files in the directory and uploads to Obot. The `SKILL.md` frontmatter is the source of truth for artifact metadata.\n4. The first publish creates version 1. Subsequent publishes of the same workflow create new versions (v2, v3, etc.).\n5. Version 1 starts owner-only unless its subjects are updated.\n6. Each later published version defaults to the previous version's sharing settings.\n7. To change who a specific published version is shared with, use `setArtifactSubjects({ \"id\": \"<artifact-id>\", \"version\": <n>, \"subjects\": [...] })`. If `version` is omitted, the latest version is updated.\n8. Supported subjects are:\n   - `{ \"type\": \"user\", \"id\": \"<user-id>\" }`\n   - `{ \"type\": \"group\", \"id\": \"<group-id>\" }`\n   - `{ \"type\": \"selector\", \"id\": \"*\" }` for all Obot users\n9. An empty `subjects` list makes that version owner-only again.\n10. To find valid user or group IDs before setting subjects, use `listSubjects({ \"type\": \"user\" | \"group\", \"query\": \"...\" })`. Leave `query` blank to list everything visible.\n\n### Searching the Registry\n\nTo find published or shared workflows from other users in the **remote Obot registry**, use `searchArtifacts`:\n- Search by keyword: `searchArtifacts({ \"query\": \"code review\", \"artifactType\": \"workflow\" })`\n- List all shared workflows: `searchArtifacts({ \"artifactType\": \"workflow\" })` (empty query returns all visible artifacts)\n- This is for discovering shared, public, or published workflows — not for finding workflows already on your local filesystem.\n- **When a user asks about \"shared workflows\", this is the tool to use.**\n\n### Installing\n\nTo install a published workflow, use `installArtifact`:\n- `installArtifact({ \"id\": \"<artifact-id>\" })` installs the latest version\n- `installArtifact({ \"id\": \"<artifact-id>\", \"version\": 2 })` installs a specific version\n- The workflow is automatically extracted into `workflows/<name>/` and is immediately available.\n- Installing overwrites any existing local workflow with the same name.\n","pkg/servers/system/testdata/with-override/skills/workflows.md":"---\nname: Custom Workflows\ndescription: My custom workflows skill that overrides the built-in\n---\n\n# Custom Workflows Skill\n\nThis overrides the built-in workflows skill.\n","pkg/servers/system/testdata/with-user-skills/skills/my-custom-skill.md":"---\nname: Custom Skill\ndescription: A custom skill\n---\n\n# My Custom Skill\n\nCustom content here.\n","pkg/servers/system/testdata/with-user-skills/skills/user-skill.md":"---\nname: User-Defined Skill\ndescription: A user-defined skill\n---\n\n# User Skill\n\nThis is a custom user skill.\n","pkg/servers/workflows/testdata/with-workflows/workflows/another/SKILL.md":"---\nname: another\ndescription: Another workflow for testing multiple workflow listing.\nmetadata:\n  createdAt: \"2026-01-16T10:30:00Z\"\n---\n\n## Steps\n\n### 1. only_step\nExecute the single step.\n\n---\n\n## Output\n\nDone.\n","pkg/servers/workflows/testdata/with-workflows/workflows/no-description/SKILL.md":"# No Workflow Header\n\nThis file doesn't have the standard workflow header format.\n","pkg/servers/workflows/testdata/with-workflows/workflows/test-workflow/SKILL.md":"---\nname: test-workflow\ndescription: This is a test workflow for unit testing purposes.\nlicense: MIT\ncompatibility: nanobot >= 0.1.0\nmetadata:\n  createdAt: \"2026-01-15T09:00:00Z\"\n  author: testuser\n---\n\n## Inputs\n\n- **input_name** (required): A test input parameter.\n\n## Steps\n\n### 1. first_step\nDo something useful.\n\n---\n\n### 2. second_step\nDo something else.\n\n---\n\n## Output\n\nThe workflow completed successfully.\n"},"items":[{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/obot-platform/nanobot/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nNanobot is a standalone MCP (Model Context Protocol) host that enables building agents with MCP and MCP-UI. Unlike built-in MCP hosts in applications like VSCode, Claude, or ChatGPT, Nanobot is designed to be an open-source, deployable solution that combines MCP servers with LLMs to create agent experiences through external interfaces such as Obot. The project is written in Go.\n\n**Technology Stack:**\n- Go 1.26.0 with GORM (SQLite, MySQL, PostgreSQL) and goja (JavaScript runtime for hooks)\n\n## Build and Development Commands\n\n### Backend (Go)\n\n```bash\n# Build the nanobot binary\nmake\n\n# Run nanobot with a configuration file\n./bin/nanobot run ./nanobot.yaml\n\n# Run Go tests\ngo test ./...\n\n# Run a specific test\ngo test ./pkg/agents -run TestName\n\n# Format Go code\ngofmt -w .\n```\n\n## Architecture Overview\n\n### Backend Architecture\n\n**Core Components:**\n\n- **Runtime (`pkg/runtime/`)** - Main orchestration layer that initializes the system. Creates and wires together the LLM client, tool service, agents, and sampling components. Manages the overall execution environment.\n\n- **Agents (`pkg/agents/`)** - Agent execution engine that handles tool mapping, request population, and agent interactions. Responsible for running agents backed by LLMs with access to tools.\n\n- **Tools Service (`pkg/tools/`)** - Central registry for tools and agents. Handles tool discovery, mapping, and execution delegation. Manages MCP server connections.\n\n- **MCP Layer (`pkg/mcp/`)** - MCP protocol implementation including sessions, clients, servers, and wire protocols. Handles both stdio and HTTP transports. Key types:\n  - `Session` - Manages MCP session lifecycle and message routing\n  - `Client` - MCP client implementation for connecting to servers\n  - `Wire` - Protocol transport abstraction (stdio/HTTP)\n\n- **LLM Integration (`pkg/llm/`)** - Abstraction over different LLM providers (OpenAI, Anthropic). Routes requests to appropriate providers based on model names. Handles both completion and response APIs.\n\n- **Session Management (`pkg/session/`, `pkg/sessiondata/`)** - Manages user sessions, conversation state, and session-scoped data. Handles agent context, tool mappings, and resource mappings within sessions. Supports parent-child session relationships and database-backed OAuth token storage.\n\n- **Server Layer (`pkg/server/`)** - HTTP server handling MCP protocol over HTTP. Routes requests for initialize, tools/list, tools/call, prompts/*, resources/*, etc. Manages session creation and request routing.\n\n- **Built-in MCP Servers (`pkg/servers/`)** - Nanobot includes several built-in MCP servers:\n  - `agent/` - Exposes individual agents as MCP servers with chat capabilities\n  - `capabilities/` - Session initialization and capability management (workspace setup)\n  - `meta/` - Metadata and introspection tools (list_chats, update_chat, list_agents)\n  - `resources/` - Database-backed resource management (create_resource, delete_resource) with automatic mimetype detection\n  - `workspace/` - Workspace and session management (create/update/delete workspaces, session reading)\n\n- **Configuration (`pkg/config/`)** - YAML-based configuration loading and validation. Supports profiles, extends (inheritance), and environment variables. See `pkg/config/schema.yaml` for the complete schema.\n\n**Key Architectural Patterns:**\n\n- **Tool Mappings** - Tools from MCP servers are mapped to agent-accessible tools. The `BuildToolMappings` method creates this mapping by resolving tool references from agents and MCP servers.\n\n- **Hooks** - Lifecycle hooks for agents and MCP servers (config, request, response). Hooks are TypeScript/JavaScript functions that can modify configuration and messages. See `hooks.ts` for type definitions.\n\n- **Sandboxing** - MCP servers can run in Docker containers for isolation. The `pkg/mcp/sandbox/` handles containerization and port mapping.\n\nNanobot does not bundle a frontend. External UIs communicate through MCP endpoints, `/api/events`, and the browser proxy routes.\n\n## Configuration\n\nConfiguration is YAML-based. Key top-level sections:\n\n- `agents` - Define agents with their models, tools, instructions, and behaviors\n- `mcp-servers` - MCP server configurations (command, URL, Docker image, etc.)\n- `prompts` - Template definitions\n- `publish` - Defines what to expose when Nanobot itself acts as an MCP server\n- `env` - Environment variable definitions with descriptions and defaults\n- `auth` - Authentication configuration (OAuth, remote headers)\n- `profiles` - Configuration profiles for different environments\n- `extends` - Inherit from other configuration files\n\nExample minimal configuration:\n\n```yaml\nagents:\n  myagent:\n    name: My Agent\n    model: gpt-4\n    mcpServers: my-mcp-server\n\nmcpServers:\n  my-mcp-server:\n    url: https://example.com/mcp\n```\n\n## Important Go Packages\n\n- `pkg/types/` - Core type definitions shared across the system (Config, Agent, Message, ToolCall, etc.)\n- `pkg/complete/` - Utility package for handling option completion and merging\n- `pkg/expr/` - Expression evaluation for dynamic values in configurations\n- `pkg/schema/` - JSON Schema validation and manipulation\n- `pkg/supervise/` - Process supervision for running MCP server subprocesses\n- `pkg/sampling/` - Handles MCP sampling requests (LLM-in-the-loop)\n- `pkg/envvar/` - Environment variable handling with descriptions and defaults\n- `pkg/cmd/` - CLI command handling (routes from `main.go`)\n\n## Entry Points and Special Modes\n\n**Main Entry Point:** `main.go` routes commands to `pkg/cmd`\n\n**Special `_exec` Mode:** Nanobot can act as a daemon wrapper for MCP server subprocesses. When invoked with `_exec` as the first argument, it handles stdio piping and process lifecycle management for MCP servers. This enables Nanobot to supervise and manage MCP server processes.\n\n## Testing\n\nGo tests follow standard Go conventions:\n- Test files are named `*_test.go`\n- Run all tests: `go test ./...`\n- Run specific package tests: `go test ./pkg/agents`\n- Run specific test: `go test ./pkg/agents -run TestName`\n\n## Common Patterns\n\n### Adding a New Agent Hook\n\n1. Define TypeScript types in `hooks.ts` (root level)\n2. Update corresponding Go types in `pkg/types/hooks.go`\n3. Implement hook handling in `pkg/agents/` or relevant package\n4. Hook execution is managed through `pkg/mcp/hooks.go`\n\n### Adding a New Tool\n\n1. Tools come from MCP servers (external or built-in servers in `pkg/servers/`)\n2. Tool resolution happens in `pkg/tools/service.go`\n\n### Working with Sessions\n\n- Sessions are scoped to MCP connections\n- Use `mcp.SessionFromContext(ctx)` to get current session\n- Session state includes tool mappings, current agent, and custom attributes\n- Parent sessions can be accessed via `Session.Parent`\n- Root session can be accessed via `session.Root()`\n\n## MCP Protocol Notes\n\nNanobot supports both MCP standard and MCP-UI extensions:\n- Standard MCP: tools, prompts, resources, sampling\n- MCP-UI: Elicitations (user input prompts), progress notifications, structured UI elements\n\nWhen implementing MCP features, refer to:\n- MCP types in `pkg/mcp/types.go`\n- Message handling in `pkg/mcp/message.go`\n- Protocol reference at `https://modelcontextprotocol.io`\n\n## Code Style\n\n- Go: Follow standard Go conventions, use `gofmt`\n","isInternal":false,"tokens":1659,"sizeBytes":7403},{"name":"agent.frontmatter.schema.json","path":"docs/agents/schemas/agent.frontmatter.schema.json","rawUrl":"https://raw.githubusercontent.com/obot-platform/nanobot/HEAD/docs/agents/schemas/agent.frontmatter.schema.json","title":"Schemas Skill","category":"anthropic-skill","format":"json","content":"{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/agent.frontmatter.schema.json\",\n  \"title\": \"Nanobot Agent Front Matter\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"name\"],\n  \"properties\": {\n    \"name\": { \"type\": \"string\", \"minLength\": 1 },\n    \"description\": { \"type\": \"string\" },\n    \"metadata\": {\n      \"type\": \"object\",\n      \"description\": \"Free-form metadata.\",\n      \"additionalProperties\": true,\n      \"default\": {}\n    },\n\n    \"model\": { \"type\": \"string\" },\n    \"allowed_models\": {\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\" },\n      \"default\": []\n    },\n    \"temperature\": { \"type\": \"number\" },\n    \"max_tokens\": { \"type\": \"integer\", \"minimum\": 1 },\n\n    \"tools\": {\n      \"description\": \"Tools visible/available to the agent. If omitted, defaults to [\\\"inherit\\\"]. Built-ins are logical names (e.g. Read/Write). MCP tools are mcpServer/toolName.\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tool_approvals\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": {\n          \"description\": \"Default authorization behavior. Currently only 'approve' is supported.\",\n          \"type\": \"string\",\n          \"enum\": [\"approve\"],\n          \"default\": \"approve\"\n        },\n        \"rules\": {\n          \"description\": \"Ordered allow rules; first matching rule wins.\",\n          \"type\": \"array\",\n          \"items\": { \"$ref\": \"#/$defs/toolApprovalRule\" },\n          \"default\": []\n        }\n      }\n    },\n\n    \"skills\": {\n      \"description\": \"Skills visible/available to the agent. If omitted, defaults to [\\\"inherit\\\"]. Skill IDs are directory names.\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tasks\": {\n      \"description\": \"Tasks visible/available to the agent (task IDs are directory paths under .nanobot/tasks).\",\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\", \"minLength\": 1 },\n      \"default\": [\"inherit\"]\n    },\n\n    \"task_approvals\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": {\n          \"description\": \"Default task authorization behavior. Currently only 'approve' is supported.\",\n          \"type\": \"string\",\n          \"enum\": [\"approve\"],\n          \"default\": \"approve\"\n        },\n        \"rules\": {\n          \"description\": \"Ordered allow rules; first matching rule wins.\",\n          \"type\": \"array\",\n          \"items\": { \"$ref\": \"#/$defs/taskApprovalRule\" },\n          \"default\": []\n        }\n      }\n    }\n  },\n\n  \"$defs\": {\n    \"toolApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"tool\", \"allow\"],\n      \"properties\": {\n        \"tool\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Tool identifier (must match an entry in tools).\"\n        },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": {\n          \"description\": \"Optional matchers keyed by tool argument name.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"taskApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"task\", \"allow\"],\n      \"properties\": {\n        \"task\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Task identifier (must match an entry in tasks).\"\n        },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": {\n          \"description\": \"Optional matchers keyed by task input name.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"matcher\": {\n      \"description\": \"A value matcher for a tool argument key.\",\n      \"oneOf\": [\n        { \"$ref\": \"#/$defs/equalsMatcher\" },\n        { \"$ref\": \"#/$defs/inMatcher\" },\n        { \"$ref\": \"#/$defs/startsWithMatcher\" },\n        { \"$ref\": \"#/$defs/matchesMatcher\" },\n        { \"$ref\": \"#/$defs/containsMatcher\" },\n        { \"$ref\": \"#/$defs/containsAllMatcher\" },\n        { \"$ref\": \"#/$defs/anyOfMatcher\" },\n        { \"$ref\": \"#/$defs/allOfMatcher\" }\n      ]\n    },\n\n    \"equalsMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"equals\"],\n      \"properties\": {\n        \"equals\": {}\n      }\n    },\n\n    \"inMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"in\"],\n      \"properties\": {\n        \"in\": {\n          \"type\": \"array\",\n          \"items\": {}\n        }\n      }\n    },\n\n    \"startsWithMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"startsWith\"],\n      \"properties\": {\n        \"startsWith\": { \"type\": \"string\" }\n      }\n    },\n\n    \"matchesMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"matches\"],\n      \"properties\": {\n        \"matches\": {\n          \"type\": \"string\",\n          \"description\": \"Regex string.\"\n        }\n      }\n    },\n\n    \"containsMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"contains\"],\n      \"properties\": {\n        \"contains\": {}\n      }\n    },\n\n    \"containsAllMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"containsAll\"],\n      \"properties\": {\n        \"containsAll\": {\n          \"type\": \"array\",\n          \"items\": {}\n        }\n      }\n    },\n\n    \"anyOfMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"anyOf\"],\n      \"properties\": {\n        \"anyOf\": {\n          \"type\": \"array\",\n          \"minItems\": 1,\n          \"items\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"allOfMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"allOf\"],\n      \"properties\": {\n        \"allOf\": {\n          \"type\": \"array\",\n          \"minItems\": 1,\n          \"items\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    }\n  }\n}\n","frontmatter":{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://nanobot.dev/schemas/agent.frontmatter.schema.json","title":"Nanobot Agent Front Matter","type":"object","additionalProperties":false,"required":["name"],"properties":{"name":{"type":"string","minLength":1},"description":{"type":"string"},"metadata":{"type":"object","description":"Free-form metadata.","additionalProperties":true,"default":{}},"model":{"type":"string"},"allowed_models":{"type":"array","items":{"type":"string"},"default":[]},"temperature":{"type":"number"},"max_tokens":{"type":"integer","minimum":1},"tools":{"description":"Tools visible/available to the agent. If omitted, defaults to [\"inherit\"]. Built-ins are logical names (e.g. Read/Write). MCP tools are mcpServer/toolName.","type":"array","items":{"oneOf":[{"type":"string","enum":["inherit"]},{"type":"string","minLength":1}]}},"tool_approvals":{"type":"object","additionalProperties":false,"required":["default"],"properties":{"default":{"description":"Default authorization behavior. Currently only 'approve' is supported.","type":"string","enum":["approve"],"default":"approve"},"rules":{"description":"Ordered allow rules; first matching rule wins.","type":"array","items":{"$ref":"#/$defs/toolApprovalRule"},"default":[]}}},"skills":{"description":"Skills visible/available to the agent. If omitted, defaults to [\"inherit\"]. Skill IDs are directory names.","type":"array","items":{"oneOf":[{"type":"string","enum":["inherit"]},{"type":"string","minLength":1}]}},"tasks":{"description":"Tasks visible/available to the agent (task IDs are directory paths under .nanobot/tasks).","type":"array","items":{"type":"string","minLength":1},"default":["inherit"]},"task_approvals":{"type":"object","additionalProperties":false,"required":["default"],"properties":{"default":{"description":"Default task authorization behavior. Currently only 'approve' is supported.","type":"string","enum":["approve"],"default":"approve"},"rules":{"description":"Ordered allow rules; first matching rule wins.","type":"array","items":{"$ref":"#/$defs/taskApprovalRule"},"default":[]}}}},"$defs":{"toolApprovalRule":{"type":"object","additionalProperties":false,"required":["tool","allow"],"properties":{"tool":{"type":"string","minLength":1,"description":"Tool identifier (must match an entry in tools)."},"allow":{"type":"boolean"},"when":{"description":"Optional matchers keyed by tool argument name.","type":"object","additionalProperties":{"$ref":"#/$defs/matcher"}}}},"taskApprovalRule":{"type":"object","additionalProperties":false,"required":["task","allow"],"properties":{"task":{"type":"string","minLength":1,"description":"Task identifier (must match an entry in tasks)."},"allow":{"type":"boolean"},"when":{"description":"Optional matchers keyed by task input name.","type":"object","additionalProperties":{"$ref":"#/$defs/matcher"}}}},"matcher":{"description":"A value matcher for a tool argument key.","oneOf":[{"$ref":"#/$defs/equalsMatcher"},{"$ref":"#/$defs/inMatcher"},{"$ref":"#/$defs/startsWithMatcher"},{"$ref":"#/$defs/matchesMatcher"},{"$ref":"#/$defs/containsMatcher"},{"$ref":"#/$defs/containsAllMatcher"},{"$ref":"#/$defs/anyOfMatcher"},{"$ref":"#/$defs/allOfMatcher"}]},"equalsMatcher":{"type":"object","additionalProperties":false,"required":["equals"],"properties":{"equals":{}}},"inMatcher":{"type":"object","additionalProperties":false,"required":["in"],"properties":{"in":{"type":"array","items":{}}}},"startsWithMatcher":{"type":"object","additionalProperties":false,"required":["startsWith"],"properties":{"startsWith":{"type":"string"}}},"matchesMatcher":{"type":"object","additionalProperties":false,"required":["matches"],"properties":{"matches":{"type":"string","description":"Regex string."}}},"containsMatcher":{"type":"object","additionalProperties":false,"required":["contains"],"properties":{"contains":{}}},"containsAllMatcher":{"type":"object","additionalProperties":false,"required":["containsAll"],"properties":{"containsAll":{"type":"array","items":{}}}},"anyOfMatcher":{"type":"object","additionalProperties":false,"required":["anyOf"],"properties":{"anyOf":{"type":"array","minItems":1,"items":{"$ref":"#/$defs/matcher"}}}},"allOfMatcher":{"type":"object","additionalProperties":false,"required":["allOf"],"properties":{"allOf":{"type":"array","minItems":1,"items":{"$ref":"#/$defs/matcher"}}}}}},"isInternal":false,"tokens":1600,"sizeBytes":6336},{"name":"mcp.schema.json","path":"docs/agents/schemas/mcp.schema.json","rawUrl":"https://raw.githubusercontent.com/obot-platform/nanobot/HEAD/docs/agents/schemas/mcp.schema.json","title":"Schemas Skill","category":"anthropic-skill","format":"json","content":"{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/mcp.schema.json\",\n  \"title\": \"Nanobot MCP Configuration\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"mcpServers\"],\n  \"properties\": {\n    \"mcpServers\": {\n      \"description\": \"Map of MCP server name -> server configuration.\",\n      \"type\": \"object\",\n      \"additionalProperties\": { \"$ref\": \"#/$defs/mcpServerConfig\" },\n      \"default\": {}\n    },\n    \"mcpRegistries\": {\n      \"description\": \"Map of registry name -> registry configuration.\",\n      \"type\": \"object\",\n      \"additionalProperties\": { \"$ref\": \"#/$defs/mcpRegistryConfig\" },\n      \"default\": {}\n    }\n  },\n  \"$defs\": {\n    \"mcpServerConfig\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"command\"],\n      \"properties\": {\n        \"command\": {\n          \"description\": \"Executable used to start the MCP server.\",\n          \"type\": \"string\",\n          \"minLength\": 1\n        },\n        \"args\": {\n          \"description\": \"Arguments passed to the command.\",\n          \"type\": \"array\",\n          \"items\": { \"type\": \"string\" },\n          \"default\": []\n        },\n        \"env\": {\n          \"description\": \"Environment variables for the MCP server process.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"type\": \"string\" },\n          \"default\": {}\n        },\n        \"cwd\": {\n          \"description\": \"Optional working directory for the MCP server process.\",\n          \"type\": \"string\"\n        },\n        \"allow_tools\": {\n          \"description\": \"Optional allowlist of tool names exposed by this server (toolName portion of server/toolName). If omitted, all server tools are available to be authorized by agents.\",\n          \"type\": \"array\",\n          \"items\": { \"type\": \"string\", \"minLength\": 1 }\n        },\n        \"timeout_ms\": {\n          \"description\": \"Startup/connection timeout.\",\n          \"type\": \"integer\",\n          \"minimum\": 0,\n          \"default\": 30000\n        }\n      }\n    },\n    \"mcpRegistryConfig\": {\n      \"description\": \"A registry pointing at a standard MCP registry endpoint.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"url\"],\n      \"properties\": {\n        \"url\": {\n          \"description\": \"Base URL of the MCP registry.\",\n          \"type\": \"string\",\n          \"minLength\": 1\n        }\n      }\n    }\n  }\n}\n","frontmatter":{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://nanobot.dev/schemas/mcp.schema.json","title":"Nanobot MCP Configuration","type":"object","additionalProperties":false,"required":["mcpServers"],"properties":{"mcpServers":{"description":"Map of MCP server name -> server configuration.","type":"object","additionalProperties":{"$ref":"#/$defs/mcpServerConfig"},"default":{}},"mcpRegistries":{"description":"Map of registry name -> registry configuration.","type":"object","additionalProperties":{"$ref":"#/$defs/mcpRegistryConfig"},"default":{}}},"$defs":{"mcpServerConfig":{"type":"object","additionalProperties":false,"required":["command"],"properties":{"command":{"description":"Executable used to start the MCP server.","type":"string","minLength":1},"args":{"description":"Arguments passed to the command.","type":"array","items":{"type":"string"},"default":[]},"env":{"description":"Environment variables for the MCP server process.","type":"object","additionalProperties":{"type":"string"},"default":{}},"cwd":{"description":"Optional working directory for the MCP server process.","type":"string"},"allow_tools":{"description":"Optional allowlist of tool names exposed by this server (toolName portion of server/toolName). If omitted, all server tools are available to be authorized by agents.","type":"array","items":{"type":"string","minLength":1}},"timeout_ms":{"description":"Startup/connection timeout.","type":"integer","minimum":0,"default":30000}}},"mcpRegistryConfig":{"description":"A registry pointing at a standard MCP registry endpoint.","type":"object","additionalProperties":false,"required":["url"],"properties":{"url":{"description":"Base URL of the MCP registry.","type":"string","minLength":1}}}}},"isInternal":false,"tokens":578,"sizeBytes":2407},{"name":"skill.frontmatter.schema.json","path":"docs/agents/schemas/skill.frontmatter.schema.json","rawUrl":"https://raw.githubusercontent.com/obot-platform/nanobot/HEAD/docs/agents/schemas/skill.frontmatter.schema.json","title":"Schemas Skill","category":"anthropic-skill","format":"json","content":"{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/skill.frontmatter.schema.json\",\n  \"title\": \"Nanobot Skill Front Matter (Agent Skills compatible)\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"name\", \"description\"],\n  \"properties\": {\n    \"name\": {\n      \"type\": \"string\",\n      \"minLength\": 1,\n      \"maxLength\": 64,\n      \"pattern\": \"^(?!-)(?!.*--)[a-z0-9-]+(?<!-)$\",\n      \"description\": \"Must be 1-64 chars, lowercase letters/numbers/hyphens, no leading/trailing hyphen, no consecutive hyphens. Must also match directory name (enforced outside JSON Schema).\"\n    },\n    \"description\": {\n      \"type\": \"string\",\n      \"minLength\": 1,\n      \"maxLength\": 1024,\n      \"description\": \"Describe what the skill does and when to use it.\"\n    },\n    \"license\": {\n      \"type\": \"string\",\n      \"description\": \"License name or reference to bundled license file.\"\n    },\n    \"compatibility\": {\n      \"type\": \"string\",\n      \"minLength\": 1,\n      \"maxLength\": 500,\n      \"description\": \"Environment requirements (product, packages, network access, etc.).\"\n    },\n    \"metadata\": {\n      \"type\": \"object\",\n      \"description\": \"Arbitrary key-value mapping.\",\n      \"additionalProperties\": {\n        \"oneOf\": [\n          { \"type\": \"string\" },\n          { \"type\": \"number\" },\n          { \"type\": \"integer\" },\n          { \"type\": \"boolean\" }\n        ]\n      },\n      \"default\": {}\n    },\n    \"allowed-tools\": {\n      \"type\": \"string\",\n      \"description\": \"Space-delimited list of pre-approved tools (experimental). E.g. 'Bash(git:*) Read'.\"\n    },\n\n    \"tools\": {\n      \"description\": \"Nanobot extension: tools visible/available while executing this skill. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tool_approvals\": {\n      \"description\": \"Nanobot extension: tool approval policy while executing this skill.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": {\n          \"description\": \"Default authorization behavior. Currently only 'approve' is supported.\",\n          \"type\": \"string\",\n          \"enum\": [\"approve\"],\n          \"default\": \"approve\"\n        },\n        \"rules\": {\n          \"description\": \"Ordered allow rules; first matching rule wins.\",\n          \"type\": \"array\",\n          \"items\": { \"$ref\": \"#/$defs/toolApprovalRule\" },\n          \"default\": []\n        }\n      }\n    },\n\n    \"skills\": {\n      \"description\": \"Nanobot extension: skills visible/available while executing this skill. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tasks\": {\n      \"description\": \"Nanobot extension: tasks visible/available while executing this skill (task IDs are directory paths under .nanobot/tasks).\",\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\", \"minLength\": 1 },\n      \"default\": [\"inherit\"]\n    },\n\n    \"task_approvals\": {\n      \"description\": \"Nanobot extension: task approval policy while executing this skill.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": {\n          \"description\": \"Default task authorization behavior. Currently only 'approve' is supported.\",\n          \"type\": \"string\",\n          \"enum\": [\"approve\"],\n          \"default\": \"approve\"\n        },\n        \"rules\": {\n          \"description\": \"Ordered allow rules; first matching rule wins.\",\n          \"type\": \"array\",\n          \"items\": { \"$ref\": \"#/$defs/taskApprovalRule\" },\n          \"default\": []\n        }\n      }\n    }\n  },\n\n  \"$defs\": {\n    \"toolApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"tool\", \"allow\"],\n      \"properties\": {\n        \"tool\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Tool identifier (must match an entry in tools).\"\n        },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": {\n          \"description\": \"Optional matchers keyed by tool argument name.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"taskApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"task\", \"allow\"],\n      \"properties\": {\n        \"task\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Task identifier (must match an entry in tasks).\"\n        },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": {\n          \"description\": \"Optional matchers keyed by task input name.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"matcher\": {\n      \"description\": \"A value matcher for a tool argument key.\",\n      \"oneOf\": [\n        { \"$ref\": \"#/$defs/equalsMatcher\" },\n        { \"$ref\": \"#/$defs/inMatcher\" },\n        { \"$ref\": \"#/$defs/startsWithMatcher\" },\n        { \"$ref\": \"#/$defs/matchesMatcher\" },\n        { \"$ref\": \"#/$defs/containsMatcher\" },\n        { \"$ref\": \"#/$defs/containsAllMatcher\" },\n        { \"$ref\": \"#/$defs/anyOfMatcher\" },\n        { \"$ref\": \"#/$defs/allOfMatcher\" }\n      ]\n    },\n\n    \"equalsMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"equals\"],\n      \"properties\": { \"equals\": {} }\n    },\n\n    \"inMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"in\"],\n      \"properties\": {\n        \"in\": {\n          \"type\": \"array\",\n          \"items\": {}\n        }\n      }\n    },\n\n    \"startsWithMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"startsWith\"],\n      \"properties\": { \"startsWith\": { \"type\": \"string\" } }\n    },\n\n    \"matchesMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"matches\"],\n      \"properties\": { \"matches\": { \"type\": \"string\" } }\n    },\n\n    \"containsMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"contains\"],\n      \"properties\": { \"contains\": {} }\n    },\n\n    \"containsAllMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"containsAll\"],\n      \"properties\": {\n        \"containsAll\": {\n          \"type\": \"array\",\n          \"items\": {}\n        }\n      }\n    },\n\n    \"anyOfMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"anyOf\"],\n      \"properties\": {\n        \"anyOf\": {\n          \"type\": \"array\",\n          \"minItems\": 1,\n          \"items\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"allOfMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"allOf\"],\n      \"properties\": {\n        \"allOf\": {\n          \"type\": \"array\",\n          \"minItems\": 1,\n          \"items\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    }\n  }\n}\n","frontmatter":{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://nanobot.dev/schemas/skill.frontmatter.schema.json","title":"Nanobot Skill Front Matter (Agent Skills compatible)","type":"object","additionalProperties":false,"required":["name","description"],"properties":{"name":{"type":"string","minLength":1,"maxLength":64,"pattern":"^(?!-)(?!.*--)[a-z0-9-]+(?<!-)$","description":"Must be 1-64 chars, lowercase letters/numbers/hyphens, no leading/trailing hyphen, no consecutive hyphens. Must also match directory name (enforced outside JSON Schema)."},"description":{"type":"string","minLength":1,"maxLength":1024,"description":"Describe what the skill does and when to use it."},"license":{"type":"string","description":"License name or reference to bundled license file."},"compatibility":{"type":"string","minLength":1,"maxLength":500,"description":"Environment requirements (product, packages, network access, etc.)."},"metadata":{"type":"object","description":"Arbitrary key-value mapping.","additionalProperties":{"oneOf":[{"type":"string"},{"type":"number"},{"type":"integer"},{"type":"boolean"}]},"default":{}},"allowed-tools":{"type":"string","description":"Space-delimited list of pre-approved tools (experimental). E.g. 'Bash(git:*) Read'."},"tools":{"description":"Nanobot extension: tools visible/available while executing this skill. If omitted, defaults to [\"inherit\"].","type":"array","items":{"oneOf":[{"type":"string","enum":["inherit"]},{"type":"string","minLength":1}]}},"tool_approvals":{"description":"Nanobot extension: tool approval policy while executing this skill.","type":"object","additionalProperties":false,"required":["default"],"properties":{"default":{"description":"Default authorization behavior. Currently only 'approve' is supported.","type":"string","enum":["approve"],"default":"approve"},"rules":{"description":"Ordered allow rules; first matching rule wins.","type":"array","items":{"$ref":"#/$defs/toolApprovalRule"},"default":[]}}},"skills":{"description":"Nanobot extension: skills visible/available while executing this skill. If omitted, defaults to [\"inherit\"].","type":"array","items":{"oneOf":[{"type":"string","enum":["inherit"]},{"type":"string","minLength":1}]}},"tasks":{"description":"Nanobot extension: tasks visible/available while executing this skill (task IDs are directory paths under .nanobot/tasks).","type":"array","items":{"type":"string","minLength":1},"default":["inherit"]},"task_approvals":{"description":"Nanobot extension: task approval policy while executing this skill.","type":"object","additionalProperties":false,"required":["default"],"properties":{"default":{"description":"Default task authorization behavior. Currently only 'approve' is supported.","type":"string","enum":["approve"],"default":"approve"},"rules":{"description":"Ordered allow rules; first matching rule wins.","type":"array","items":{"$ref":"#/$defs/taskApprovalRule"},"default":[]}}}},"$defs":{"toolApprovalRule":{"type":"object","additionalProperties":false,"required":["tool","allow"],"properties":{"tool":{"type":"string","minLength":1,"description":"Tool identifier (must match an entry in tools)."},"allow":{"type":"boolean"},"when":{"description":"Optional matchers keyed by tool argument name.","type":"object","additionalProperties":{"$ref":"#/$defs/matcher"}}}},"taskApprovalRule":{"type":"object","additionalProperties":false,"required":["task","allow"],"properties":{"task":{"type":"string","minLength":1,"description":"Task identifier (must match an entry in tasks)."},"allow":{"type":"boolean"},"when":{"description":"Optional matchers keyed by task input name.","type":"object","additionalProperties":{"$ref":"#/$defs/matcher"}}}},"matcher":{"description":"A value matcher for a tool argument key.","oneOf":[{"$ref":"#/$defs/equalsMatcher"},{"$ref":"#/$defs/inMatcher"},{"$ref":"#/$defs/startsWithMatcher"},{"$ref":"#/$defs/matchesMatcher"},{"$ref":"#/$defs/containsMatcher"},{"$ref":"#/$defs/containsAllMatcher"},{"$ref":"#/$defs/anyOfMatcher"},{"$ref":"#/$defs/allOfMatcher"}]},"equalsMatcher":{"type":"object","additionalProperties":false,"required":["equals"],"properties":{"equals":{}}},"inMatcher":{"type":"object","additionalProperties":false,"required":["in"],"properties":{"in":{"type":"array","items":{}}}},"startsWithMatcher":{"type":"object","additionalProperties":false,"required":["startsWith"],"properties":{"startsWith":{"type":"string"}}},"matchesMatcher":{"type":"object","additionalProperties":false,"required":["matches"],"properties":{"matches":{"type":"string"}}},"containsMatcher":{"type":"object","additionalProperties":false,"required":["contains"],"properties":{"contains":{}}},"containsAllMatcher":{"type":"object","additionalProperties":false,"required":["containsAll"],"properties":{"containsAll":{"type":"array","items":{}}}},"anyOfMatcher":{"type":"object","additionalProperties":false,"required":["anyOf"],"properties":{"anyOf":{"type":"array","minItems":1,"items":{"$ref":"#/$defs/matcher"}}}},"allOfMatcher":{"type":"object","additionalProperties":false,"required":["allOf"],"properties":{"allOf":{"type":"array","minItems":1,"items":{"$ref":"#/$defs/matcher"}}}}}},"isInternal":false,"tokens":1828,"sizeBytes":7250},{"name":"task.frontmatter.schema.json","path":"docs/agents/schemas/task.frontmatter.schema.json","rawUrl":"https://raw.githubusercontent.com/obot-platform/nanobot/HEAD/docs/agents/schemas/task.frontmatter.schema.json","title":"Schemas Skill","category":"anthropic-skill","format":"json","content":"{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/task.frontmatter.schema.json\",\n  \"title\": \"Nanobot Task Front Matter (TASK.md only)\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"name\"],\n  \"properties\": {\n    \"name\": { \"type\": \"string\", \"minLength\": 1 },\n    \"description\": { \"type\": \"string\" },\n    \"metadata\": {\n      \"type\": \"object\",\n      \"description\": \"Free-form metadata.\",\n      \"additionalProperties\": true,\n      \"default\": {}\n    },\n\n    \"agent\": {\n      \"description\": \"Agent ID to execute this task with.\",\n      \"type\": \"string\",\n      \"minLength\": 1\n    },\n\n    \"tools\": {\n      \"description\": \"Tools visible/available while executing this task. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tool_approvals\": {\n      \"description\": \"Tool approval policy while executing this task.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": { \"type\": \"string\", \"enum\": [\"approve\"], \"default\": \"approve\" },\n        \"rules\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/toolApprovalRule\" }, \"default\": [] }\n      }\n    },\n\n    \"tasks\": {\n      \"description\": \"Tasks visible/available while executing this task (task IDs are directory paths under .nanobot/tasks).\",\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\", \"minLength\": 1 },\n      \"default\": [\"inherit\"]\n    },\n\n    \"task_approvals\": {\n      \"description\": \"Task approval policy while executing this task.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": { \"type\": \"string\", \"enum\": [\"approve\"], \"default\": \"approve\" },\n        \"rules\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/taskApprovalRule\" }, \"default\": [] }\n      }\n    },\n\n    \"inputs\": {\n      \"description\": \"Task inputs (TASK.md only).\",\n      \"type\": \"array\",\n      \"items\": { \"$ref\": \"#/$defs/taskInput\" },\n      \"default\": []\n    },\n\n    \"next\": {\n      \"description\": \"Relative path to the next step markdown file within the same task directory.\",\n      \"type\": \"string\",\n      \"minLength\": 1\n    }\n  },\n\n  \"$defs\": {\n    \"taskInput\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"name\", \"description\"],\n      \"properties\": {\n        \"name\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Input name.\"\n        },\n        \"description\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Describe what the input means and expected format.\"\n        },\n        \"default\": {\n          \"type\": \"string\",\n          \"description\": \"Optional default value (string).\"\n        }\n      }\n    },\n\n    \"toolApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"tool\", \"allow\"],\n      \"properties\": {\n        \"tool\": { \"type\": \"string\", \"minLength\": 1 },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": { \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" } }\n      }\n    },\n\n    \"taskApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"task\", \"allow\"],\n      \"properties\": {\n        \"task\": { \"type\": \"string\", \"minLength\": 1 },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": { \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" } }\n      }\n    },\n\n    \"matcher\": {\n      \"oneOf\": [\n        { \"$ref\": \"#/$defs/equalsMatcher\" },\n        { \"$ref\": \"#/$defs/inMatcher\" },\n        { \"$ref\": \"#/$defs/startsWithMatcher\" },\n        { \"$ref\": \"#/$defs/matchesMatcher\" },\n        { \"$ref\": \"#/$defs/containsMatcher\" },\n        { \"$ref\": \"#/$defs/containsAllMatcher\" },\n        { \"$ref\": \"#/$defs/anyOfMatcher\" },\n        { \"$ref\": \"#/$defs/allOfMatcher\" }\n      ]\n    },\n\n    \"equalsMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"equals\"], \"properties\": { \"equals\": {} } },\n    \"inMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"in\"], \"properties\": { \"in\": { \"type\": \"array\", \"items\": {} } } },\n    \"startsWithMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"startsWith\"], \"properties\": { \"startsWith\": { \"type\": \"string\" } } },\n    \"matchesMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"matches\"], \"properties\": { \"matches\": { \"type\": \"string\" } } },\n    \"containsMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"contains\"], \"properties\": { \"contains\": {} } },\n    \"containsAllMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"containsAll\"], \"properties\": { \"containsAll\": { \"type\": \"array\", \"items\": {} } } },\n    \"anyOfMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"anyOf\"], \"properties\": { \"anyOf\": { \"type\": \"array\", \"minItems\": 1, \"items\": { \"$ref\": \"#/$defs/matcher\" } } } },\n    \"allOfMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"allOf\"], \"properties\": { \"allOf\": { \"type\": \"array\", \"minItems\": 1, \"items\": { \"$ref\": \"#/$defs/matcher\" } } } }\n  }\n}\n","frontmatter":{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://nanobot.dev/schemas/task.frontmatter.schema.json","title":"Nanobot Task Front Matter (TASK.md only)","type":"object","additionalProperties":false,"required":["name"],"properties":{"name":{"type":"string","minLength":1},"description":{"type":"string"},"metadata":{"type":"object","description":"Free-form metadata.","additionalProperties":true,"default":{}},"agent":{"description":"Agent ID to execute this task with.","type":"string","minLength":1},"tools":{"description":"Tools visible/available while executing this task. If omitted, defaults to [\"inherit\"].","type":"array","items":{"oneOf":[{"type":"string","enum":["inherit"]},{"type":"string","minLength":1}]}},"tool_approvals":{"description":"Tool approval policy while executing this task.","type":"object","additionalProperties":false,"required":["default"],"properties":{"default":{"type":"string","enum":["approve"],"default":"approve"},"rules":{"type":"array","items":{"$ref":"#/$defs/toolApprovalRule"},"default":[]}}},"tasks":{"description":"Tasks visible/available while executing this task (task IDs are directory paths under .nanobot/tasks).","type":"array","items":{"type":"string","minLength":1},"default":["inherit"]},"task_approvals":{"description":"Task approval policy while executing this task.","type":"object","additionalProperties":false,"required":["default"],"properties":{"default":{"type":"string","enum":["approve"],"default":"approve"},"rules":{"type":"array","items":{"$ref":"#/$defs/taskApprovalRule"},"default":[]}}},"inputs":{"description":"Task inputs (TASK.md only).","type":"array","items":{"$ref":"#/$defs/taskInput"},"default":[]},"next":{"description":"Relative path to the next step markdown file within the same task directory.","type":"string","minLength":1}},"$defs":{"taskInput":{"type":"object","additionalProperties":false,"required":["name","description"],"properties":{"name":{"type":"string","minLength":1,"description":"Input name."},"description":{"type":"string","minLength":1,"description":"Describe what the input means and expected format."},"default":{"type":"string","description":"Optional default value (string)."}}},"toolApprovalRule":{"type":"object","additionalProperties":false,"required":["tool","allow"],"properties":{"tool":{"type":"string","minLength":1},"allow":{"type":"boolean"},"when":{"type":"object","additionalProperties":{"$ref":"#/$defs/matcher"}}}},"taskApprovalRule":{"type":"object","additionalProperties":false,"required":["task","allow"],"properties":{"task":{"type":"string","minLength":1},"allow":{"type":"boolean"},"when":{"type":"object","additionalProperties":{"$ref":"#/$defs/matcher"}}}},"matcher":{"oneOf":[{"$ref":"#/$defs/equalsMatcher"},{"$ref":"#/$defs/inMatcher"},{"$ref":"#/$defs/startsWithMatcher"},{"$ref":"#/$defs/matchesMatcher"},{"$ref":"#/$defs/containsMatcher"},{"$ref":"#/$defs/containsAllMatcher"},{"$ref":"#/$defs/anyOfMatcher"},{"$ref":"#/$defs/allOfMatcher"}]},"equalsMatcher":{"type":"object","additionalProperties":false,"required":["equals"],"properties":{"equals":{}}},"inMatcher":{"type":"object","additionalProperties":false,"required":["in"],"properties":{"in":{"type":"array","items":{}}}},"startsWithMatcher":{"type":"object","additionalProperties":false,"required":["startsWith"],"properties":{"startsWith":{"type":"string"}}},"matchesMatcher":{"type":"object","additionalProperties":false,"required":["matches"],"properties":{"matches":{"type":"string"}}},"containsMatcher":{"type":"object","additionalProperties":false,"required":["contains"],"properties":{"contains":{}}},"containsAllMatcher":{"type":"object","additionalProperties":false,"required":["containsAll"],"properties":{"containsAll":{"type":"array","items":{}}}},"anyOfMatcher":{"type":"object","additionalProperties":false,"required":["anyOf"],"properties":{"anyOf":{"type":"array","minItems":1,"items":{"$ref":"#/$defs/matcher"}}}},"allOfMatcher":{"type":"object","additionalProperties":false,"required":["allOf"],"properties":{"allOf":{"type":"array","minItems":1,"items":{"$ref":"#/$defs/matcher"}}}}}},"isInternal":false,"tokens":1455,"sizeBytes":5378},{"name":"task.step.frontmatter.schema.json","path":"docs/agents/schemas/task.step.frontmatter.schema.json","rawUrl":"https://raw.githubusercontent.com/obot-platform/nanobot/HEAD/docs/agents/schemas/task.step.frontmatter.schema.json","title":"Schemas Skill","category":"anthropic-skill","format":"json","content":"{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/task.step.frontmatter.schema.json\",\n  \"title\": \"Nanobot Task Step Front Matter\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"name\"],\n  \"properties\": {\n    \"name\": { \"type\": \"string\", \"minLength\": 1 },\n    \"description\": { \"type\": \"string\" },\n    \"metadata\": {\n      \"type\": \"object\",\n      \"description\": \"Free-form metadata.\",\n      \"additionalProperties\": true,\n      \"default\": {}\n    },\n\n    \"agent\": {\n      \"description\": \"Agent ID to execute this step with.\",\n      \"type\": \"string\",\n      \"minLength\": 1\n    },\n\n    \"tools\": {\n      \"description\": \"Tools visible/available while executing this step. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tool_approvals\": {\n      \"description\": \"Tool approval policy while executing this step.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": { \"type\": \"string\", \"enum\": [\"approve\"], \"default\": \"approve\" },\n        \"rules\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/toolApprovalRule\" }, \"default\": [] }\n      }\n    },\n\n    \"skills\": {\n      \"description\": \"Skills visible/available while executing this step. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tasks\": {\n      \"description\": \"Tasks visible/available while executing this step (task IDs are directory paths under .nanobot/tasks).\",\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\", \"minLength\": 1 },\n      \"default\": [\"inherit\"]\n    },\n\n    \"next\": {\n      \"description\": \"Relative path to the next step markdown file within the same task directory.\",\n      \"type\": \"string\",\n      \"minLength\": 1\n    }\n  },\n\n  \"$defs\": {\n    \"toolApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"tool\", \"allow\"],\n      \"properties\": {\n        \"tool\": { \"type\": \"string\", \"minLength\": 1 },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": { \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" } }\n      }\n    },\n    \"taskApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"task\", \"allow\"],\n      \"properties\": {\n        \"task\": { \"type\": \"string\", \"minLength\": 1 },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": { \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" } }\n      }\n    },\n\n    \"matcher\": {\n      \"oneOf\": [\n        { \"$ref\": \"#/$defs/equalsMatcher\" },\n        { \"$ref\": \"#/$defs/inMatcher\" },\n        { \"$ref\": \"#/$defs/startsWithMatcher\" },\n        { \"$ref\": \"#/$defs/matchesMatcher\" },\n        { \"$ref\": \"#/$defs/containsMatcher\" },\n        { \"$ref\": \"#/$defs/containsAllMatcher\" },\n        { \"$ref\": \"#/$defs/anyOfMatcher\" },\n        { \"$ref\": \"#/$defs/allOfMatcher\" }\n      ]\n    },\n    \"equalsMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"equals\"], \"properties\": { \"equals\": {} } },\n    \"inMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"in\"], \"properties\": { \"in\": { \"type\": \"array\", \"items\": {} } } },\n    \"startsWithMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"startsWith\"], \"properties\": { \"startsWith\": { \"type\": \"string\" } } },\n    \"matchesMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"matches\"], \"properties\": { \"matches\": { \"type\": \"string\" } } },\n    \"containsMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"contains\"], \"properties\": { \"contains\": {} } },\n    \"containsAllMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"containsAll\"], \"properties\": { \"containsAll\": { \"type\": \"array\", \"items\": {} } } },\n    \"anyOfMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"anyOf\"], \"properties\": { \"anyOf\": { \"type\": \"array\", \"minItems\": 1, \"items\": { \"$ref\": \"#/$defs/matcher\" } } } },\n    \"allOfMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"allOf\"], \"properties\": { \"allOf\": { \"type\": \"array\", \"minItems\": 1, \"items\": { \"$ref\": \"#/$defs/matcher\" } } } }\n  }\n}\n","frontmatter":{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://nanobot.dev/schemas/task.step.frontmatter.schema.json","title":"Nanobot Task Step Front Matter","type":"object","additionalProperties":false,"required":["name"],"properties":{"name":{"type":"string","minLength":1},"description":{"type":"string"},"metadata":{"type":"object","description":"Free-form metadata.","additionalProperties":true,"default":{}},"agent":{"description":"Agent ID to execute this step with.","type":"string","minLength":1},"tools":{"description":"Tools visible/available while executing this step. If omitted, defaults to [\"inherit\"].","type":"array","items":{"oneOf":[{"type":"string","enum":["inherit"]},{"type":"string","minLength":1}]}},"tool_approvals":{"description":"Tool approval policy while executing this step.","type":"object","additionalProperties":false,"required":["default"],"properties":{"default":{"type":"string","enum":["approve"],"default":"approve"},"rules":{"type":"array","items":{"$ref":"#/$defs/toolApprovalRule"},"default":[]}}},"skills":{"description":"Skills visible/available while executing this step. If omitted, defaults to [\"inherit\"].","type":"array","items":{"oneOf":[{"type":"string","enum":["inherit"]},{"type":"string","minLength":1}]}},"tasks":{"description":"Tasks visible/available while executing this step (task IDs are directory paths under .nanobot/tasks).","type":"array","items":{"type":"string","minLength":1},"default":["inherit"]},"next":{"description":"Relative path to the next step markdown file within the same task directory.","type":"string","minLength":1}},"$defs":{"toolApprovalRule":{"type":"object","additionalProperties":false,"required":["tool","allow"],"properties":{"tool":{"type":"string","minLength":1},"allow":{"type":"boolean"},"when":{"type":"object","additionalProperties":{"$ref":"#/$defs/matcher"}}}},"taskApprovalRule":{"type":"object","additionalProperties":false,"required":["task","allow"],"properties":{"task":{"type":"string","minLength":1},"allow":{"type":"boolean"},"when":{"type":"object","additionalProperties":{"$ref":"#/$defs/matcher"}}}},"matcher":{"oneOf":[{"$ref":"#/$defs/equalsMatcher"},{"$ref":"#/$defs/inMatcher"},{"$ref":"#/$defs/startsWithMatcher"},{"$ref":"#/$defs/matchesMatcher"},{"$ref":"#/$defs/containsMatcher"},{"$ref":"#/$defs/containsAllMatcher"},{"$ref":"#/$defs/anyOfMatcher"},{"$ref":"#/$defs/allOfMatcher"}]},"equalsMatcher":{"type":"object","additionalProperties":false,"required":["equals"],"properties":{"equals":{}}},"inMatcher":{"type":"object","additionalProperties":false,"required":["in"],"properties":{"in":{"type":"array","items":{}}}},"startsWithMatcher":{"type":"object","additionalProperties":false,"required":["startsWith"],"properties":{"startsWith":{"type":"string"}}},"matchesMatcher":{"type":"object","additionalProperties":false,"required":["matches"],"properties":{"matches":{"type":"string"}}},"containsMatcher":{"type":"object","additionalProperties":false,"required":["contains"],"properties":{"contains":{}}},"containsAllMatcher":{"type":"object","additionalProperties":false,"required":["containsAll"],"properties":{"containsAll":{"type":"array","items":{}}}},"anyOfMatcher":{"type":"object","additionalProperties":false,"required":["anyOf"],"properties":{"anyOf":{"type":"array","minItems":1,"items":{"$ref":"#/$defs/matcher"}}}},"allOfMatcher":{"type":"object","additionalProperties":false,"required":["allOf"],"properties":{"allOf":{"type":"array","minItems":1,"items":{"$ref":"#/$defs/matcher"}}}}}},"isInternal":false,"tokens":1254,"sizeBytes":4539},{"name":"browser-use.md","path":"pkg/servers/system/skills/browser-use.md","rawUrl":"https://raw.githubusercontent.com/obot-platform/nanobot/HEAD/pkg/servers/system/skills/browser-use.md","title":"browser-use","category":"anthropic-skill","format":"markdown","content":"---\nname: browser-use\ndescription: Automates browser interactions for web testing, form filling, screenshots, and data extraction. Use when the user needs to navigate websites, interact with web pages, fill forms, take screenshots, or extract information from web pages. IMPORTANT - If webfetch fails, try this tool instead.\n---\n\n# Browser Automation with browser-use CLI\n\nThe `browser-use` command provides fast, persistent browser automation. It maintains browser sessions across commands, enabling complex multi-step workflows.\n\n`browser-use` is already installed in the dedicated Nanobot agent image. Use the built-in CLI directly.\n\nWhen using this skill, default to `--browser chromium --headed` unless the user explicitly wants headless/background execution. Headed mode is the safer default because it keeps the browser visible in BrowserView and makes manual intervention possible.\n\n## Quick Start\n\n```bash\nbrowser-use --browser chromium --headed open https://example.com  # Navigate to URL\nbrowser-use state                              # Get page elements with indices\nbrowser-use click 5                            # Click element by index\nbrowser-use type \"Hello World\"                 # Type text\nbrowser-use screenshot                         # Take screenshot\nbrowser-use close                              # Close browser\n```\n\n## Core Workflow\n\n1. **Navigate**: `browser-use --browser chromium --headed open <url>` - Opens URL (starts browser if needed)\n2. **Inspect**: `browser-use state` - Returns clickable elements with indices\n3. **Interact**: Use indices from state to interact (`browser-use click 5`, `browser-use input 3 \"text\"`)\n4. **Verify**: `browser-use state` or `browser-use screenshot` to confirm actions\n5. **Repeat**: Browser stays open between commands\n\n## Browser Modes\n\n```bash\nbrowser-use --browser chromium --headed open <url>  # Recommended default: visible Chromium window\nbrowser-use --browser chromium open <url>      # Headless Chromium for explicit background runs\nbrowser-use --browser real open <url>          # User's Chrome with login sessions\n```\n\n- **chromium**: Fast, isolated, and should usually be run with `--headed`\n- **real**: Uses your Chrome with cookies, extensions, logged-in sessions\n\n## Browser Viewing with VNC\n\nThe VNC server is automatically started and ready to use. You can view and interact with the browser in real-time using VNC. This is **essential** for:\n\n- **Solving CAPTCHAs** - Many sites require human verification\n- **Debugging** - See exactly what the browser is doing\n- **User interaction** - Manual intervention when automation gets stuck\n\nThe VNC server provides:\n- X virtual framebuffer (Xvfb) on display :99\n- VNC server on port 5900\n- an internal WebSocket proxy on port 6080, exposed only through Nanobot's `/browser` path\n- a borderless, maximized browser window sized for the BrowserView pane by default\n\n### Accessing the Browser View\n\nThe Nanobot UI includes a browser viewer component that embeds VNC access. The backend proxies only the WebSocket transport; the standalone noVNC `vnc.html` page is not exposed.\n\n### Using with browser-use\n\n**Default to `--headed` mode. It is required when you need VNC viewing:**\n\n```bash\n# RECOMMENDED DEFAULT (visible in VNC viewer, user can interact)\nbrowser-use --browser chromium --headed open https://example.com\n\n# HEADLESS ONLY WHEN EXPLICITLY APPROPRIATE (faster, but user can't see it)\nbrowser-use --browser chromium open https://example.com\n```\n\nThe `--headed` flag makes the browser visible on the VNC display (:99).\n\n### CAPTCHA Solving Workflow\n\n**When a CAPTCHA is encountered:**\n\n1. **Detect the CAPTCHA** - Look for verification challenges, \"I'm not a robot\" checkboxes, or automation blocks\n2. **Inform the user immediately** - Tell them a CAPTCHA needs to be solved\n3. **Ensure VNC is running** - Verify the browser is running in `--headed` mode\n4. **Guide the user** - Tell them to open the BrowserView pane in the Nanobot UI\n5. **Wait for completion** - Pause automation until the user confirms they've solved the CAPTCHA\n6. **Continue** - Resume automation after manual intervention\n\n**Example:**\n```bash\n# You're automating a task and hit a CAPTCHA\nbrowser-use --browser chromium --headed open https://example.com\nbrowser-use state\n# → Detects CAPTCHA verification challenge\n\n# STOP and inform user:\n# \"I've encountered a CAPTCHA that needs human verification.\n#  Please open the BrowserView pane in the Nanobot UI\n#  and solve the CAPTCHA. Let me know when you're done.\"\n\n# Wait for user confirmation, then continue:\nbrowser-use state  # Verify CAPTCHA is solved\nbrowser-use click 5  # Continue with automation\n```\n\n**Important:** CAPTCHAs cannot be automated - they require human interaction. Always use `--headed` mode when you anticipate CAPTCHAs, and be prepared to pause and ask the user for help.\n\n## Commands\n\n### Navigation\n```bash\nbrowser-use open <url>                    # Navigate to URL\nbrowser-use back                          # Go back in history\nbrowser-use scroll down                   # Scroll down\nbrowser-use scroll up                     # Scroll up\n```\n\n### Page State\n```bash\nbrowser-use state                         # Get URL, title, and clickable elements\nbrowser-use screenshot                    # Take screenshot (outputs base64)\nbrowser-use screenshot path.png           # Save screenshot to file\nbrowser-use screenshot --full path.png    # Full page screenshot\n```\n\n### Interactions (use indices from `browser-use state`)\n```bash\nbrowser-use click <index>                 # Click element\nbrowser-use type \"text\"                   # Type text into focused element\nbrowser-use input <index> \"text\"          # Click element, then type text\nbrowser-use keys \"Enter\"                  # Send keyboard keys\nbrowser-use keys \"Control+a\"              # Send key combination\nbrowser-use select <index> \"option\"       # Select dropdown option\n```\n\n### Tab Management\n```bash\nbrowser-use switch <tab>                  # Switch to tab by index\nbrowser-use close-tab                     # Close current tab\nbrowser-use close-tab <tab>               # Close specific tab\n```\n\n### JavaScript & Data\n```bash\nbrowser-use eval \"document.title\"         # Execute JavaScript, return result\n```\n\n### Cookies\n```bash\nbrowser-use cookies get                   # Get all cookies\nbrowser-use cookies get --url <url>       # Get cookies for specific URL\nbrowser-use cookies set <name> <value>    # Set a cookie\nbrowser-use cookies set name val --domain .example.com --secure --http-only\nbrowser-use cookies clear                 # Clear all cookies\nbrowser-use cookies clear --url <url>     # Clear cookies for specific URL\nbrowser-use cookies export <file>         # Export all cookies to JSON file\nbrowser-use cookies export <file> --url <url>  # Export cookies for specific URL\nbrowser-use cookies import <file>         # Import cookies from JSON file\n```\n\n### Wait Conditions\n```bash\nbrowser-use wait selector \"h1\"            # Wait for element to be visible\nbrowser-use wait selector \".loading\" --state hidden  # Wait for element to disappear\nbrowser-use wait selector \"#btn\" --state attached    # Wait for element in DOM\nbrowser-use wait text \"Success\"           # Wait for text to appear\nbrowser-use wait selector \"h1\" --timeout 5000  # Custom timeout in ms\n```\n\n### Additional Interactions\n```bash\nbrowser-use hover <index>                 # Hover over element (triggers CSS :hover)\nbrowser-use dblclick <index>              # Double-click element\nbrowser-use rightclick <index>            # Right-click element (context menu)\n```\n\n### Information Retrieval\n```bash\nbrowser-use get title                     # Get page title\nbrowser-use get html                      # Get full page HTML\nbrowser-use get html --selector \"h1\"      # Get HTML of specific element\nbrowser-use get text <index>              # Get text content of element\nbrowser-use get value <index>             # Get value of input/textarea\nbrowser-use get attributes <index>        # Get all attributes of element\nbrowser-use get bbox <index>              # Get bounding box (x, y, width, height)\n```\n\n### Python Execution (Persistent Session)\n```bash\nbrowser-use python \"x = 42\"               # Set variable\nbrowser-use python \"print(x)\"             # Access variable (outputs: 42)\nbrowser-use python \"print(browser.url)\"   # Access browser object\nbrowser-use python --vars                 # Show defined variables\nbrowser-use python --reset                # Clear Python namespace\nbrowser-use python --file script.py       # Execute Python file\n```\n\nThe Python session maintains state across commands. The `browser` object provides:\n- `browser.url` - Current page URL\n- `browser.title` - Page title\n- `browser.goto(url)` - Navigate\n- `browser.click(index)` - Click element\n- `browser.type(text)` - Type text\n- `browser.screenshot(path)` - Take screenshot\n- `browser.scroll()` - Scroll page\n- `browser.html` - Get page HTML\n\n### Session Management\n```bash\nbrowser-use sessions                      # List active sessions\nbrowser-use close                         # Close current session\nbrowser-use close --all                   # Close all sessions\n```\n\n### Profile Management\n```bash\nbrowser-use profile list-local            # List local Chrome profiles\n```\n\n**Before opening a real browser (`--browser real`)**, always ask the user if they want to use a specific Chrome profile or no profile. Use `profile list-local` to show available profiles:\n\n```bash\nbrowser-use profile list-local\n# Output: Default: Person 1 (user@gmail.com)\n#         Profile 1: Work (work@company.com)\n\n# With a specific profile (has that profile's cookies/logins)\nbrowser-use --browser real --profile \"Profile 1\" open https://gmail.com\n\n# Without a profile (fresh browser, no existing logins)\nbrowser-use --browser real open https://gmail.com\n\n# Headless mode (no visible window) - useful for cookie export\nbrowser-use --browser real --profile \"Default\" cookies export /tmp/cookies.json\n```\n\nEach Chrome profile has its own cookies, history, and logged-in sessions. Choosing the right profile determines whether sites will be pre-authenticated.\n\n## Global Options\n\n| Option | Description |\n|--------|-------------|\n| `--session NAME` | Use named session (default: \"default\") |\n| `--browser MODE` | Browser mode: chromium, real |\n| `--headed` | Show browser window (chromium mode) |\n| `--profile NAME` | Chrome profile (real mode only) |\n| `--json` | Output as JSON |\n\n**Session behavior**: All commands without `--session` use the same \"default\" session. The browser stays open and is reused across commands. Use `--session NAME` to run multiple browsers in parallel.\n\n## Usage Notes\n\n- Always run `browser-use state` before interacting so you have current element indices.\n- Default to `--browser chromium --headed`.\n- Only omit `--headed` when the user explicitly wants headless/background execution or when visibility and manual takeover are clearly unnecessary.\n- Use `browser-use screenshot` or `browser-use state` to confirm each critical action.\n- Close the session with `browser-use close` when the browser task is complete.\n","frontmatter":{"name":"browser-use","description":"Automates browser interactions for web testing, form filling, screenshots, and data extraction. Use when the user needs to navigate websites, interact with web pages, fill forms, take screenshots, or extract information from web pages. IMPORTANT - If webfetch fails, try this tool instead."},"isInternal":false,"tokens":2432,"sizeBytes":11143},{"name":"python-scripts.md","path":"pkg/servers/system/skills/python-scripts.md","rawUrl":"https://raw.githubusercontent.com/obot-platform/nanobot/HEAD/pkg/servers/system/skills/python-scripts.md","title":"python-scripts","category":"anthropic-skill","format":"markdown","content":"---\nname: python-scripts\ndescription: Write and execute Python scripts using uv for programmatic tasks.\n---\n\nUse Python when a step needs data transformation, API calls, calculations, or deterministic operations where code is more reliable than natural language.\n\n## How to Use\n\nWrite a script with inline dependencies and run it with `uv run`:\n\n```python\n#!/usr/bin/env python3\n# /// script\n# requires-python = \">=3.11\"\n# dependencies = [\n#     \"requests\",\n# ]\n# ///\n\nimport json\nimport requests\n\ndata = requests.get(\"https://api.example.com/data\").json()\nprint(json.dumps(data))\n```\n\n```bash\nuv run script.py\n```\n\nThe `# /// script` block declares dependencies - `uv` installs them automatically.\n\n## Output Conventions\n\n- **Structured data**: Print JSON to stdout\n- **Progress/debug info**: Print to stderr\n- **Errors**: Print to stderr and exit with non-zero code\n\n```python\nimport sys\nimport json\n\n# Output result\nprint(json.dumps({\"items\": [...], \"count\": 42}))\n\n# Debug info (won't pollute output)\nprint(\"Processed 42 items\", file=sys.stderr)\n```\n\n## When to Use Python vs LLM\n\n**Use Python for:** JSON manipulation, API calls with auth/pagination, calculations, file parsing, sorting/filtering, large datasets\n\n**Keep as LLM for:** Tasks requiring judgment, natural language generation, flexible interpretation\n","frontmatter":{"name":"python-scripts","description":"Write and execute Python scripts using uv for programmatic tasks."},"isInternal":false,"tokens":302,"sizeBytes":1319},{"name":"scheduled-tasks.md","path":"pkg/servers/system/skills/scheduled-tasks.md","rawUrl":"https://raw.githubusercontent.com/obot-platform/nanobot/HEAD/pkg/servers/system/skills/scheduled-tasks.md","title":"scheduled-tasks","category":"anthropic-skill","format":"markdown","content":"---\nname: scheduled-tasks\ndescription: Load this skill for ANY request that mentions scheduled tasks, recurring jobs, reminders, future runs, or time-based automation.\n---\n\n# Scheduled Tasks\n\nScheduled tasks are persistent task definitions managed through the `nanobot.tasks` tools. When a scheduled task fires, nanobot starts a new chat thread and runs the saved prompt there.\n\n## When to Use Scheduled Tasks\n\nUse scheduled tasks when work should happen later or recur on a schedule:\n- Daily, weekly, or monthly reports\n- Reminders or recurring checks\n- Periodic file updates, cleanups, or syncs\n\nIf the user wants the work done right now and not again later, do it directly instead of creating a scheduled task.\n\n## Scheduled Tasks vs Workflows\n\nDo not conflate scheduled tasks with workflows:\n- **Workflows** are reusable procedures stored under `workflows/`.\n- **Scheduled tasks** decide when nanobot should start a future chat thread.\n\nUse a workflow when the user wants a reusable multi-step procedure. Use a scheduled task when the user wants something to happen later or recur automatically.\n\nIf the user wants a workflow to run on a schedule, keep them separate:\n1. Create or update the workflow first.\n2. Create a scheduled task whose prompt tells nanobot to run that workflow.\n\n## Managing Existing Tasks\n\nBefore updating, deleting, or manually running an existing task, call `listScheduledTasks` unless the user already provided the exact `task:///...` URI.\n\nUse the task URI for all follow-up task operations:\n- `updateScheduledTask`\n- `deleteScheduledTask`\n- `startScheduledTask`\n\n## Timezone Rules\n\nWhen creating a scheduled task, always set the timezone explicitly.\n\n- If the user's current timezone is already known from context, use that by default unless they say otherwise.\n- If you do not know the user's timezone, collect it before creating the task.\n- Do not guess a timezone from a city, country, or language unless the user explicitly gave enough information to make that unambiguous.\n\nUnless the user asks for a different timezone, new scheduled tasks should use the user's current timezone.\n\n## Valid Schedule Shapes\n\nThe task tools only accept five-field cron expressions in these shapes:\n\n- **Daily:** minute hour `* * *`\n  - Example: `45 2 * * *` for every day at 02:45\n- **Weekly:** minute hour `* * day-of-week`\n  - Example: `20 23 * * 1,2,3,4` for Monday through Thursday at 23:20\n- **Monthly:** minute hour `day-of-month * *`\n  - Example: `45 2 22,24 * *` for the 22nd and 24th of every month at 02:45\n- **One-time date:** minute hour `day-of-month month *` with a matching expiration date\n  - Example: `45 2 26 3 *` with expiration `2026-03-26` for March 26 at 02:45\n\nDo not use unsupported cron shapes. In particular:\n- Do not use six-field cron expressions\n- Do not use schedules that combine both day-of-month and day-of-week\n- Do not use arbitrary cron patterns outside the daily, weekly, monthly, or one-time date forms above\n\n## Designing Good Task Prompts\n\nEach scheduled run starts a fresh chat thread. The stored prompt should be self-contained:\n- State exactly what to do\n- Name any files, outputs, or deliverables to produce\n- Mention any workflow to run by name if applicable\n- Avoid depending on transient context from the current conversation\n\nFor recurring actions that send messages or modify external systems, confirm the user's intent unless they have already clearly requested that recurring behavior.\n","frontmatter":{"name":"scheduled-tasks","description":"Load this skill for ANY request that mentions scheduled tasks, recurring jobs, reminders, future runs, or time-based automation."},"isInternal":false,"tokens":778,"sizeBytes":3456},{"name":"workflows.md","path":"pkg/servers/system/skills/workflows.md","rawUrl":"https://raw.githubusercontent.com/obot-platform/nanobot/HEAD/pkg/servers/system/skills/workflows.md","title":"workflows","category":"anthropic-skill","format":"markdown","content":"---\nname: workflows\ndescription: Load this skill for ANY request that mentions workflows, including listing, creating, running, editing, or discussing them.\n---\n\n# Workflows\n\nWorkflows are directories in `workflows/` that codify repeatable processes. Each workflow is a directory containing a `SKILL.md` file (with YAML frontmatter) and any supporting files (scripts, assets, etc.). Each step's output can be referenced by later steps using `{{Step Name}}`.\n\n## Discovering Workflows\n\nThere are two places workflows can exist:\n\n1. **Local workflows** — in the `workflows/` directory on the local filesystem. These are workflows the user has created or installed. List the `workflows/` directory to find these.\n2. **Shared/published workflows** — in the remote Obot registry. These are workflows other users have published (publicly or to the organization). Use `searchArtifacts` to find these.\n\n**When the user asks about \"shared workflows\", \"public workflows\", workflows from other users, or wants to discover/find/browse available workflows they haven't installed yet, ALWAYS use `searchArtifacts` to search the remote registry.** Do not just list the local `workflows/` directory — that only shows what is already installed locally.\n\nIf the user asks to \"list all workflows\", \"show my workflows\", \"which workflows are installed\" or similar without specifying shared/public, find workflows by using the Glob tool (never Bash with find or ls) with pattern `**/SKILL.md` in the `workflows/` directory (do NOT include `workflows/` in both the path and the pattern — that double-nests). If they ask to \"list shared workflows\" or \"find workflows\", use `searchArtifacts`.\n\n## When to Use Workflows\n\nWorkflows are for repeatable tasks. If it's a one-time thing, just execute it directly.\n\n## Tracking Progress\n\nUse TodoWrite during **both** workflow design and execution. The user sees your todo list — it's your primary way of keeping them informed.\n\n- **When designing:** Create todos for your design steps (gather requirements, identify inputs/edge cases, draft workflow, write file, present for review).\n- **When executing:** Create a todo for each workflow step and update status as you go. Add new todos if unexpected issues arise.\n\nKeep todo titles short and scannable. Update them as you progress — don't leave stale todos sitting around.\n\n## Workflow Format\n\nEach workflow is a directory under `workflows/` containing a `SKILL.md` file with:\n- **Frontmatter** (required): YAML frontmatter with `name` (lowercase-hyphenated slug that must match the directory name), `description` (always wrap in double quotes to avoid YAML parsing errors from colons or special characters), and optional `metadata` map.\n- **Inputs**: Optional parameters with defaults\n- **Steps**: Numbered steps with clear instructions\n- **Output**: Optional template for the final result\n\nWhen creating a new workflow, always include frontmatter with `name` (must match the directory name, lowercase with hyphens only), `description` (always in double quotes), and `metadata.createdAt` set to the current date/time in ISO 8601 format (e.g., `2026-01-15T09:00:00Z`). Use `bash` (e.g., `date -u +\"%Y-%m-%dT%H:%M:%SZ\"`) to get the current UTC time — do not guess or hardcode it.\n\n**IMPORTANT:** Always wrap the `description` value in double quotes in the YAML frontmatter. Unquoted descriptions containing colons (`:`) cause YAML parsing errors. For example, write `description: \"Analyze code: find and fix issues\"` not `description: Analyze code: find and fix issues`.\n\n**Name constraints:** lowercase letters, numbers, and hyphens only; no leading/trailing or consecutive hyphens; must match the directory name exactly.\n\n### Example: Simple Review\n\n```markdown\n---\nname: code-review\ndescription: \"Review code changes for quality issues.\"\nmetadata:\n  createdAt: \"2026-01-15T09:00:00Z\"\n---\n\n## Inputs\n\n- **target** (optional): Files to review. Default: `.`\n\n## Steps\n\n### 1. Find Changes\nFind all modified code files in {{input.target}}.\n\n### 2. Review Code\nReview these files for quality issues:\n{{Find Changes}}\n\nFocus on: error handling, edge cases, and readability.\n\n## Output\n\n{{Review Code}}\n```\n\n### Example: With Conditions\n\n```markdown\n---\nname: smart-fix\ndescription: \"Analyze an issue and apply a fix only if it's safe to do so.\"\nmetadata:\n  createdAt: \"2026-01-15T09:00:00Z\"\n---\n\n## Steps\n\n### 1. Analyze Issue\nAnalyze the reported issue and determine severity.\n\n### 2. Check Safety\nBased on this analysis, determine if an automated fix is safe:\n{{Analyze Issue}}\n\nEnd your response with SAFE or UNSAFE.\n\n### 3. Apply Fix\nApply the fix for: {{Analyze Issue}}\n\n**Condition:** {{Check Safety}} contains SAFE\n\n### 4. Create Manual Report\nCreate a report explaining why manual intervention is needed:\n{{Analyze Issue}}\n\n**Condition:** {{Check Safety}} contains UNSAFE\n```\n\n### Example: With Error Handling\n\n```markdown\n---\nname: deploy-with-rollback\ndescription: \"Deploy changes with automatic rollback on failure.\"\nmetadata:\n  createdAt: \"2026-01-15T09:00:00Z\"\n---\n\n## Inputs\n\n- **environment** (required): Target environment (staging, production)\n\n## Steps\n\n### 1. Run Tests\nRun the full test suite. Report any failures.\n\n### 2. Deploy\nDeploy to {{input.environment}}.\n\n**On error:** Rollback\n\n### 3. Verify\nVerify the deployment is healthy.\n\n**On error:** Rollback\n\n### 4. Rollback\nRoll back to the previous version and report what went wrong.\n\n## Output\n\nDeployment to {{input.environment}} complete.\n{{Verify}}\n```\n\n## Key Concepts\n\n**Variables:** Use `{{input.name}}` for inputs and `{{Step Name}}` for outputs from previous steps.\n\n**Conditions:** Add `**Condition:**` to make a step run only when the condition is true. Common patterns:\n- `{{Step Name}} contains X` - check if output contains text\n- `{{Step Name}} not empty` - check if output exists\n- `{{input.flag}} equals yes` - check exact value\n\n**Error handling:** Add `**On error:**` to control what happens when a step fails:\n- `stop` (default) - halt the workflow\n- `continue` - log the error and move to the next step\n- `Step Name` - jump to a specific step (useful for cleanup/rollback)\n\n**Output:** The `## Output` section defines the final result. If omitted, the last step's output is used.\n\n**File dependencies:** When a workflow depends on any files (scripts, templates, data files, configs, etc.), those files **must** be placed in the same directory as the workflow's `SKILL.md` file or in a subdirectory within it. Workflows are self-contained directories — files outside the workflow directory will not be available when the workflow is published, installed, or shared.\n\n## Designing Workflows\n\nWhen asked to create a workflow, DO NOT start writing it immediately. Follow this process:\n\n### Phase 1: Understand\n\n1. Use TodoWrite to create a plan for designing the workflow (e.g., \"Gather requirements\", \"Identify inputs and edge cases\", \"Draft workflow\", \"Write to file\", \"Present for review\").\n2. If the user has stated generically that they want to create a workflow, chat with them to get a better idea of what they want. Don't use the AskUserQuestion tool at this phase.\n3. Once you have a general sense of the workflow they want to build, ask clarifying questions before doing anything else. DO use the AskUserQuestion tool for these specific questions. You need to understand:\n    - What the workflow should accomplish end-to-end\n    - What the expected outcome is\n    - What inputs/variables are needed\n    - What external services or tools are involved\n    - How errors should be handled (stop? retry?)\n    - DON'T ask about execution frequency or external triggers because we don't yet support those features \n4. **Wait for answers. Do not proceed until you have enough information to design a good workflow.** Do not guess or assume — ask.\n\n### Phase 2: Draft & Save\n\n1. Once you understand the requirements, draft the workflow.\n2. Create the directory `workflows/<name>/` and write the workflow to `workflows/<name>/SKILL.md`. Place any supporting files (scripts, data, etc.) alongside `SKILL.md` in the same directory.\n3. Mark your design todos as complete.\n\n### Phase 3: Hand Off\n\n1. Present the workflow to the user with a brief summary of what each step does.\n2. Ask the user to review it. Offer to make changes.\n3. **Offer to execute the workflow — do NOT execute it automatically.** Say something like: \"Want me to run this now, or would you like to make changes first?\"\n\n**IMPORTANT:** Never skip Phase 1. Never jump straight to writing. Never auto-execute after designing.\n\nUse descriptive step names in title case (e.g., \"Fetch Issues\" not \"step1\") — these become variables for later steps.\n\nFor deterministic operations (data transformation, calculations), consider having steps use Python scripts (see `python-scripts` skill). For external services (Slack, Jira, databases), consider MCP servers (see `mcp-curl` skill).\n\n## Executing Workflows\n\nUsers may ask to run a workflow at any time — not just immediately after designing one. Workflows in `workflows/` are persistent, reusable artifacts. A user might say \"run the code-review workflow\" days or weeks after it was created. Treat execution as a standalone operation: load the file, collect inputs, and run it. Don't assume you have any prior context about the workflow beyond what's in the file itself.\n\n**IMPORTANT:** If the user asks you to *create* a workflow, do NOT auto-execute it after writing the file. Present it for review and offer to run it. But if the user asks you to *run* an existing workflow, go ahead — that's an explicit request.\n\n**IMPORTANT:** Installed workflows live on the local filesystem in the `workflows/` directory. When running a workflow, ALWAYS load it from `workflows/<name>/SKILL.md`. Do NOT use `searchArtifacts` to find workflows to run — that tool searches the remote Obot registry for published artifacts, not locally installed workflows.\n\nWhen the user asks you to run a workflow:\n\n1. Load the workflow from `workflows/<name>/SKILL.md`. If you're unsure which workflows are available, use glob pattern `**/SKILL.md` with path `workflows/` to find them.\n2. Use TodoWrite to create a todo for each workflow step before you begin. This is your execution plan — the user will follow along.\n3. **Present the execution plan to the user.** After creating the todos, present a brief summary of what will be executed and ask the user to confirm before proceeding. For example: \"I've planned the following steps: [list steps]. Does this look good to proceed?\"\n4. **Wait for user approval.** Do not begin execution until the user confirms.\n5. Collect any required inputs from the user. If inputs are missing and have no defaults, ask for them.\n6. Call `recordWorkflowRun` with the workflow URI (e.g. `workflow:///workflow-name`) to record the run in the current session.\n7. Execute each step in order:\n    - Check conditions before running conditional steps.\n    - Interpolate variables (`{{input.name}}` and `{{Step Name}}`).\n    - Follow error handling directives.\n    - Mark each todo complete/failed/skipped as you go.\n8. Provide running updates in normal chat messages as you work through each step. If errors occur, judgment calls are needed, or unexpected conditions arise, mention them in chat and add new todos as needed.\n\nComplete tasks fully, follow requested formats exactly, and provide specific analysis rather than vague summaries.\n\nAfter execution, present a structured run summary directly in chat:\n- A heading with the workflow name and a pass/fail indicator (e.g., ## Workflow Run: Code Review)\n- A tally of step outcomes (e.g., \"3 of 3 steps succeeded\" or \"2 of 3 steps succeeded, 1 failed\")\n- For failed steps, a brief explanation of what went wrong\n- A summary of any modifications made to external systems or artifacts produced\n- If the workflow defines an Output section, include that content last\n\nIf the workflow was aborted or no steps completed, still produce a summary explaining what happened and which steps were skipped.\n\n## Improving Workflows\n\nAfter executing a workflow, you can analyze the execution and propose improvements to the workflow file.\n\n**What to look for:**\n- Steps that needed retries or failed — unclear prompt? missing format spec?\n- Steps that produced poor output — missing context? too vague?\n- Steps that should be split into smaller steps\n- Missing error handling for steps that failed\n- Hardcoded values that should be inputs\n\n**Process:** Review which steps succeeded/failed/retried and what fixes were applied. For each issue, determine the concrete fix (rewrite prompt, add format spec, split step, add error handling, etc.). Present each proposed change with the issue, current text, and proposed text.\n\n**IMPORTANT:** Get user approval before editing the workflow file. Never apply changes automatically.\n\n**Example:**\n```\n## Proposed Changes to issue-triage.md\n\n### 1. Step: Analyze Issues\n**Issue:** Agent returned prose instead of JSON. Had to retry.\n**Change:** Add explicit format requirement.\n\nCurrent:\n> Analyze each issue and categorize by priority.\n> Return the results as JSON.\n\nProposed:\n> Analyze each issue and categorize by priority.\n>\n> Return as a JSON array:\n> [{\"number\": 1, \"title\": \"...\", \"priority\": \"High|Medium|Low\", \"reason\": \"...\"}]\n\nApply these changes? (yes/no/select specific)\n```\n\nDon't change things that worked fine or add complexity without a concrete problem to solve.\n\n## Publishing & Sharing Workflows\n\nYou can publish workflows to Obot's registry so other users can discover and install them.\n\n### Publishing\n\nTo publish a workflow, use the `publishArtifact` tool:\n\n1. Ensure the workflow exists in `workflows/<name>/` with a `SKILL.md` that has proper frontmatter (name, description). The workflow must pass format validation before publishing.\n2. Call `publishArtifact` with the workflow directory name. For example: `publishArtifact({ \"workflowName\": \"code-review\" })`.\n3. The tool bundles all files in the directory and uploads to Obot. The `SKILL.md` frontmatter is the source of truth for artifact metadata.\n4. The first publish creates version 1. Subsequent publishes of the same workflow create new versions (v2, v3, etc.).\n5. Version 1 starts owner-only unless its subjects are updated.\n6. Each later published version defaults to the previous version's sharing settings.\n7. To change who a specific published version is shared with, use `setArtifactSubjects({ \"id\": \"<artifact-id>\", \"version\": <n>, \"subjects\": [...] })`. If `version` is omitted, the latest version is updated.\n8. Supported subjects are:\n   - `{ \"type\": \"user\", \"id\": \"<user-id>\" }`\n   - `{ \"type\": \"group\", \"id\": \"<group-id>\" }`\n   - `{ \"type\": \"selector\", \"id\": \"*\" }` for all Obot users\n9. An empty `subjects` list makes that version owner-only again.\n10. To find valid user or group IDs before setting subjects, use `listSubjects({ \"type\": \"user\" | \"group\", \"query\": \"...\" })`. Leave `query` blank to list everything visible.\n\n### Searching the Registry\n\nTo find published or shared workflows from other users in the **remote Obot registry**, use `searchArtifacts`:\n- Search by keyword: `searchArtifacts({ \"query\": \"code review\", \"artifactType\": \"workflow\" })`\n- List all shared workflows: `searchArtifacts({ \"artifactType\": \"workflow\" })` (empty query returns all visible artifacts)\n- This is for discovering shared, public, or published workflows — not for finding workflows already on your local filesystem.\n- **When a user asks about \"shared workflows\", this is the tool to use.**\n\n### Installing\n\nTo install a published workflow, use `installArtifact`:\n- `installArtifact({ \"id\": \"<artifact-id>\" })` installs the latest version\n- `installArtifact({ \"id\": \"<artifact-id>\", \"version\": 2 })` installs a specific version\n- The workflow is automatically extracted into `workflows/<name>/` and is immediately available.\n- Installing overwrites any existing local workflow with the same name.\n","frontmatter":{"name":"workflows","description":"Load this skill for ANY request that mentions workflows, including listing, creating, running, editing, or discussing them."},"isInternal":false,"tokens":3657,"sizeBytes":15967},{"name":"workflows.md","path":"pkg/servers/system/testdata/with-override/skills/workflows.md","rawUrl":"https://raw.githubusercontent.com/obot-platform/nanobot/HEAD/pkg/servers/system/testdata/with-override/skills/workflows.md","title":"Custom Workflows","category":"anthropic-skill","format":"markdown","content":"---\nname: Custom Workflows\ndescription: My custom workflows skill that overrides the built-in\n---\n\n# Custom Workflows Skill\n\nThis overrides the built-in workflows skill.\n","frontmatter":{"name":"Custom Workflows","description":"My custom workflows skill that overrides the built-in"},"isInternal":false,"tokens":34,"sizeBytes":170},{"name":"my-custom-skill.md","path":"pkg/servers/system/testdata/with-user-skills/skills/my-custom-skill.md","rawUrl":"https://raw.githubusercontent.com/obot-platform/nanobot/HEAD/pkg/servers/system/testdata/with-user-skills/skills/my-custom-skill.md","title":"Custom Skill","category":"anthropic-skill","format":"markdown","content":"---\nname: Custom Skill\ndescription: A custom skill\n---\n\n# My Custom Skill\n\nCustom content here.\n","frontmatter":{"name":"Custom Skill","description":"A custom skill"},"isInternal":false,"tokens":22,"sizeBytes":96},{"name":"user-skill.md","path":"pkg/servers/system/testdata/with-user-skills/skills/user-skill.md","rawUrl":"https://raw.githubusercontent.com/obot-platform/nanobot/HEAD/pkg/servers/system/testdata/with-user-skills/skills/user-skill.md","title":"User-Defined Skill","category":"anthropic-skill","format":"markdown","content":"---\nname: User-Defined Skill\ndescription: A user-defined skill\n---\n\n# User Skill\n\nThis is a custom user skill.\n","frontmatter":{"name":"User-Defined Skill","description":"A user-defined skill"},"isInternal":false,"tokens":27,"sizeBytes":111},{"name":"SKILL.md","path":"pkg/servers/workflows/testdata/with-workflows/workflows/another/SKILL.md","rawUrl":"https://raw.githubusercontent.com/obot-platform/nanobot/HEAD/pkg/servers/workflows/testdata/with-workflows/workflows/another/SKILL.md","title":"another","category":"anthropic-skill","format":"markdown","content":"---\nname: another\ndescription: Another workflow for testing multiple workflow listing.\nmetadata:\n  createdAt: \"2026-01-16T10:30:00Z\"\n---\n\n## Steps\n\n### 1. only_step\nExecute the single step.\n\n---\n\n## Output\n\nDone.\n","frontmatter":{"name":"another","description":"Another workflow for testing multiple workflow listing.","metadata":{"createdAt":"2026-01-16T10:30:00Z"}},"isInternal":false,"tokens":57,"sizeBytes":213},{"name":"SKILL.md","path":"pkg/servers/workflows/testdata/with-workflows/workflows/no-description/SKILL.md","rawUrl":"https://raw.githubusercontent.com/obot-platform/nanobot/HEAD/pkg/servers/workflows/testdata/with-workflows/workflows/no-description/SKILL.md","title":"no-description","category":"anthropic-skill","format":"markdown","content":"# No Workflow Header\n\nThis file doesn't have the standard workflow header format.\n","isInternal":false,"tokens":16,"sizeBytes":82},{"name":"SKILL.md","path":"pkg/servers/workflows/testdata/with-workflows/workflows/test-workflow/SKILL.md","rawUrl":"https://raw.githubusercontent.com/obot-platform/nanobot/HEAD/pkg/servers/workflows/testdata/with-workflows/workflows/test-workflow/SKILL.md","title":"test-workflow","category":"anthropic-skill","format":"markdown","content":"---\nname: test-workflow\ndescription: This is a test workflow for unit testing purposes.\nlicense: MIT\ncompatibility: nanobot >= 0.1.0\nmetadata:\n  createdAt: \"2026-01-15T09:00:00Z\"\n  author: testuser\n---\n\n## Inputs\n\n- **input_name** (required): A test input parameter.\n\n## Steps\n\n### 1. first_step\nDo something useful.\n\n---\n\n### 2. second_step\nDo something else.\n\n---\n\n## Output\n\nThe workflow completed successfully.\n","frontmatter":{"name":"test-workflow","description":"This is a test workflow for unit testing purposes.","license":"MIT","compatibility":"nanobot >= 0.1.0","metadata":{"createdAt":"2026-01-15T09:00:00Z","author":"testuser"}},"isInternal":false,"tokens":114,"sizeBytes":415},{"name":"README.md","path":"pkg/config/testdata/directory-with-readme/agents/README.md","rawUrl":"https://raw.githubusercontent.com/obot-platform/nanobot/HEAD/pkg/config/testdata/directory-with-readme/agents/README.md","title":"Subagent: readme","category":"subagent-persona","format":"markdown","content":"# README\n\nThis is documentation and should not be loaded as an agent.\n","isInternal":false,"tokens":15,"sizeBytes":70}],"systemPromptSnippet":"<yakaai_skills repo=\"obot-platform/nanobot\">\n<!-- File: CLAUDE.md (Tokens: ~1659 | Category: claude-rule) -->\n# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nNanobot is a standalone MCP (Model Context Protocol) host that enables building agents with MCP and MCP-UI. Unlike built-in MCP hosts in applications like VSCode, Claude, or ChatGPT, Nanobot is designed to be an open-source, deployable solution that combines MCP servers with LLMs to create agent experiences through external interfaces such as Obot. The project is written in Go.\n\n**Technology Stack:**\n- Go 1.26.0 with GORM (SQLite, MySQL, PostgreSQL) and goja (JavaScript runtime for hooks)\n\n## Build and Development Commands\n\n### Backend (Go)\n\n```bash\n# Build the nanobot binary\nmake\n\n# Run nanobot with a configuration file\n./bin/nanobot run ./nanobot.yaml\n\n# Run Go tests\ngo test ./...\n\n# Run a specific test\ngo test ./pkg/agents -run TestName\n\n# Format Go code\ngofmt -w .\n```\n\n## Architecture Overview\n\n### Backend Architecture\n\n**Core Components:**\n\n- **Runtime (`pkg/runtime/`)** - Main orchestration layer that initializes the system. Creates and wires together the LLM client, tool service, agents, and sampling components. Manages the overall execution environment.\n\n- **Agents (`pkg/agents/`)** - Agent execution engine that handles tool mapping, request population, and agent interactions. Responsible for running agents backed by LLMs with access to tools.\n\n- **Tools Service (`pkg/tools/`)** - Central registry for tools and agents. Handles tool discovery, mapping, and execution delegation. Manages MCP server connections.\n\n- **MCP Layer (`pkg/mcp/`)** - MCP protocol implementation including sessions, clients, servers, and wire protocols. Handles both stdio and HTTP transports. Key types:\n  - `Session` - Manages MCP session lifecycle and message routing\n  - `Client` - MCP client implementation for connecting to servers\n  - `Wire` - Protocol transport abstraction (stdio/HTTP)\n\n- **LLM Integration (`pkg/llm/`)** - Abstraction over different LLM providers (OpenAI, Anthropic). Routes requests to appropriate providers based on model names. Handles both completion and response APIs.\n\n- **Session Management (`pkg/session/`, `pkg/sessiondata/`)** - Manages user sessions, conversation state, and session-scoped data. Handles agent context, tool mappings, and resource mappings within sessions. Supports parent-child session relationships and database-backed OAuth token storage.\n\n- **Server Layer (`pkg/server/`)** - HTTP server handling MCP protocol over HTTP. Routes requests for initialize, tools/list, tools/call, prompts/*, resources/*, etc. Manages session creation and request routing.\n\n- **Built-in MCP Servers (`pkg/servers/`)** - Nanobot includes several built-in MCP servers:\n  - `agent/` - Exposes individual agents as MCP servers with chat capabilities\n  - `capabilities/` - Session initialization and capability management (workspace setup)\n  - `meta/` - Metadata and introspection tools (list_chats, update_chat, list_agents)\n  - `resources/` - Database-backed resource management (create_resource, delete_resource) with automatic mimetype detection\n  - `workspace/` - Workspace and session management (create/update/delete workspaces, session reading)\n\n- **Configuration (`pkg/config/`)** - YAML-based configuration loading and validation. Supports profiles, extends (inheritance), and environment variables. See `pkg/config/schema.yaml` for the complete schema.\n\n**Key Architectural Patterns:**\n\n- **Tool Mappings** - Tools from MCP servers are mapped to agent-accessible tools. The `BuildToolMappings` method creates this mapping by resolving tool references from agents and MCP servers.\n\n- **Hooks** - Lifecycle hooks for agents and MCP servers (config, request, response). Hooks are TypeScript/JavaScript functions that can modify configuration and messages. See `hooks.ts` for type definitions.\n\n- **Sandboxing** - MCP servers can run in Docker containers for isolation. The `pkg/mcp/sandbox/` handles containerization and port mapping.\n\nNanobot does not bundle a frontend. External UIs communicate through MCP endpoints, `/api/events`, and the browser proxy routes.\n\n## Configuration\n\nConfiguration is YAML-based. Key top-level sections:\n\n- `agents` - Define agents with their models, tools, instructions, and behaviors\n- `mcp-servers` - MCP server configurations (command, URL, Docker image, etc.)\n- `prompts` - Template definitions\n- `publish` - Defines what to expose when Nanobot itself acts as an MCP server\n- `env` - Environment variable definitions with descriptions and defaults\n- `auth` - Authentication configuration (OAuth, remote headers)\n- `profiles` - Configuration profiles for different environments\n- `extends` - Inherit from other configuration files\n\nExample minimal configuration:\n\n```yaml\nagents:\n  myagent:\n    name: My Agent\n    model: gpt-4\n    mcpServers: my-mcp-server\n\nmcpServers:\n  my-mcp-server:\n    url: https://example.com/mcp\n```\n\n## Important Go Packages\n\n- `pkg/types/` - Core type definitions shared across the system (Config, Agent, Message, ToolCall, etc.)\n- `pkg/complete/` - Utility package for handling option completion and merging\n- `pkg/expr/` - Expression evaluation for dynamic values in configurations\n- `pkg/schema/` - JSON Schema validation and manipulation\n- `pkg/supervise/` - Process supervision for running MCP server subprocesses\n- `pkg/sampling/` - Handles MCP sampling requests (LLM-in-the-loop)\n- `pkg/envvar/` - Environment variable handling with descriptions and defaults\n- `pkg/cmd/` - CLI command handling (routes from `main.go`)\n\n## Entry Points and Special Modes\n\n**Main Entry Point:** `main.go` routes commands to `pkg/cmd`\n\n**Special `_exec` Mode:** Nanobot can act as a daemon wrapper for MCP server subprocesses. When invoked with `_exec` as the first argument, it handles stdio piping and process lifecycle management for MCP servers. This enables Nanobot to supervise and manage MCP server processes.\n\n## Testing\n\nGo tests follow standard Go conventions:\n- Test files are named `*_test.go`\n- Run all tests: `go test ./...`\n- Run specific package tests: `go test ./pkg/agents`\n- Run specific test: `go test ./pkg/agents -run TestName`\n\n## Common Patterns\n\n### Adding a New Agent Hook\n\n1. Define TypeScript types in `hooks.ts` (root level)\n2. Update corresponding Go types in `pkg/types/hooks.go`\n3. Implement hook handling in `pkg/agents/` or relevant package\n4. Hook execution is managed through `pkg/mcp/hooks.go`\n\n### Adding a New Tool\n\n1. Tools come from MCP servers (external or built-in servers in `pkg/servers/`)\n2. Tool resolution happens in `pkg/tools/service.go`\n\n### Working with Sessions\n\n- Sessions are scoped to MCP connections\n- Use `mcp.SessionFromContext(ctx)` to get current session\n- Session state includes tool mappings, current agent, and custom attributes\n- Parent sessions can be accessed via `Session.Parent`\n- Root session can be accessed via `session.Root()`\n\n## MCP Protocol Notes\n\nNanobot supports both MCP standard and MCP-UI extensions:\n- Standard MCP: tools, prompts, resources, sampling\n- MCP-UI: Elicitations (user input prompts), progress notifications, structured UI elements\n\nWhen implementing MCP features, refer to:\n- MCP types in `pkg/mcp/types.go`\n- Message handling in `pkg/mcp/message.go`\n- Protocol reference at `https://modelcontextprotocol.io`\n\n## Code Style\n\n- Go: Follow standard Go conventions, use `gofmt`\n\n<!-- File: docs/agents/schemas/agent.frontmatter.schema.json (Tokens: ~1600 | Category: anthropic-skill) -->\n{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/agent.frontmatter.schema.json\",\n  \"title\": \"Nanobot Agent Front Matter\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"name\"],\n  \"properties\": {\n    \"name\": { \"type\": \"string\", \"minLength\": 1 },\n    \"description\": { \"type\": \"string\" },\n    \"metadata\": {\n      \"type\": \"object\",\n      \"description\": \"Free-form metadata.\",\n      \"additionalProperties\": true,\n      \"default\": {}\n    },\n\n    \"model\": { \"type\": \"string\" },\n    \"allowed_models\": {\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\" },\n      \"default\": []\n    },\n    \"temperature\": { \"type\": \"number\" },\n    \"max_tokens\": { \"type\": \"integer\", \"minimum\": 1 },\n\n    \"tools\": {\n      \"description\": \"Tools visible/available to the agent. If omitted, defaults to [\\\"inherit\\\"]. Built-ins are logical names (e.g. Read/Write). MCP tools are mcpServer/toolName.\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tool_approvals\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": {\n          \"description\": \"Default authorization behavior. Currently only 'approve' is supported.\",\n          \"type\": \"string\",\n          \"enum\": [\"approve\"],\n          \"default\": \"approve\"\n        },\n        \"rules\": {\n          \"description\": \"Ordered allow rules; first matching rule wins.\",\n          \"type\": \"array\",\n          \"items\": { \"$ref\": \"#/$defs/toolApprovalRule\" },\n          \"default\": []\n        }\n      }\n    },\n\n    \"skills\": {\n      \"description\": \"Skills visible/available to the agent. If omitted, defaults to [\\\"inherit\\\"]. Skill IDs are directory names.\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tasks\": {\n      \"description\": \"Tasks visible/available to the agent (task IDs are directory paths under .nanobot/tasks).\",\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\", \"minLength\": 1 },\n      \"default\": [\"inherit\"]\n    },\n\n    \"task_approvals\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": {\n          \"description\": \"Default task authorization behavior. Currently only 'approve' is supported.\",\n          \"type\": \"string\",\n          \"enum\": [\"approve\"],\n          \"default\": \"approve\"\n        },\n        \"rules\": {\n          \"description\": \"Ordered allow rules; first matching rule wins.\",\n          \"type\": \"array\",\n          \"items\": { \"$ref\": \"#/$defs/taskApprovalRule\" },\n          \"default\": []\n        }\n      }\n    }\n  },\n\n  \"$defs\": {\n    \"toolApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"tool\", \"allow\"],\n      \"properties\": {\n        \"tool\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Tool identifier (must match an entry in tools).\"\n        },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": {\n          \"description\": \"Optional matchers keyed by tool argument name.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"taskApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"task\", \"allow\"],\n      \"properties\": {\n        \"task\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Task identifier (must match an entry in tasks).\"\n        },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": {\n          \"description\": \"Optional matchers keyed by task input name.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"matcher\": {\n      \"description\": \"A value matcher for a tool argument key.\",\n      \"oneOf\": [\n        { \"$ref\": \"#/$defs/equalsMatcher\" },\n        { \"$ref\": \"#/$defs/inMatcher\" },\n        { \"$ref\": \"#/$defs/startsWithMatcher\" },\n        { \"$ref\": \"#/$defs/matchesMatcher\" },\n        { \"$ref\": \"#/$defs/containsMatcher\" },\n        { \"$ref\": \"#/$defs/containsAllMatcher\" },\n        { \"$ref\": \"#/$defs/anyOfMatcher\" },\n        { \"$ref\": \"#/$defs/allOfMatcher\" }\n      ]\n    },\n\n    \"equalsMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"equals\"],\n      \"properties\": {\n        \"equals\": {}\n      }\n    },\n\n    \"inMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"in\"],\n      \"properties\": {\n        \"in\": {\n          \"type\": \"array\",\n          \"items\": {}\n        }\n      }\n    },\n\n    \"startsWithMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"startsWith\"],\n      \"properties\": {\n        \"startsWith\": { \"type\": \"string\" }\n      }\n    },\n\n    \"matchesMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"matches\"],\n      \"properties\": {\n        \"matches\": {\n          \"type\": \"string\",\n          \"description\": \"Regex string.\"\n        }\n      }\n    },\n\n    \"containsMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"contains\"],\n      \"properties\": {\n        \"contains\": {}\n      }\n    },\n\n    \"containsAllMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"containsAll\"],\n      \"properties\": {\n        \"containsAll\": {\n          \"type\": \"array\",\n          \"items\": {}\n        }\n      }\n    },\n\n    \"anyOfMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"anyOf\"],\n      \"properties\": {\n        \"anyOf\": {\n          \"type\": \"array\",\n          \"minItems\": 1,\n          \"items\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"allOfMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"allOf\"],\n      \"properties\": {\n        \"allOf\": {\n          \"type\": \"array\",\n          \"minItems\": 1,\n          \"items\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    }\n  }\n}\n\n<!-- File: docs/agents/schemas/mcp.schema.json (Tokens: ~578 | Category: anthropic-skill) -->\n{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/mcp.schema.json\",\n  \"title\": \"Nanobot MCP Configuration\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"mcpServers\"],\n  \"properties\": {\n    \"mcpServers\": {\n      \"description\": \"Map of MCP server name -> server configuration.\",\n      \"type\": \"object\",\n      \"additionalProperties\": { \"$ref\": \"#/$defs/mcpServerConfig\" },\n      \"default\": {}\n    },\n    \"mcpRegistries\": {\n      \"description\": \"Map of registry name -> registry configuration.\",\n      \"type\": \"object\",\n      \"additionalProperties\": { \"$ref\": \"#/$defs/mcpRegistryConfig\" },\n      \"default\": {}\n    }\n  },\n  \"$defs\": {\n    \"mcpServerConfig\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"command\"],\n      \"properties\": {\n        \"command\": {\n          \"description\": \"Executable used to start the MCP server.\",\n          \"type\": \"string\",\n          \"minLength\": 1\n        },\n        \"args\": {\n          \"description\": \"Arguments passed to the command.\",\n          \"type\": \"array\",\n          \"items\": { \"type\": \"string\" },\n          \"default\": []\n        },\n        \"env\": {\n          \"description\": \"Environment variables for the MCP server process.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"type\": \"string\" },\n          \"default\": {}\n        },\n        \"cwd\": {\n          \"description\": \"Optional working directory for the MCP server process.\",\n          \"type\": \"string\"\n        },\n        \"allow_tools\": {\n          \"description\": \"Optional allowlist of tool names exposed by this server (toolName portion of server/toolName). If omitted, all server tools are available to be authorized by agents.\",\n          \"type\": \"array\",\n          \"items\": { \"type\": \"string\", \"minLength\": 1 }\n        },\n        \"timeout_ms\": {\n          \"description\": \"Startup/connection timeout.\",\n          \"type\": \"integer\",\n          \"minimum\": 0,\n          \"default\": 30000\n        }\n      }\n    },\n    \"mcpRegistryConfig\": {\n      \"description\": \"A registry pointing at a standard MCP registry endpoint.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"url\"],\n      \"properties\": {\n        \"url\": {\n          \"description\": \"Base URL of the MCP registry.\",\n          \"type\": \"string\",\n          \"minLength\": 1\n        }\n      }\n    }\n  }\n}\n\n<!-- File: docs/agents/schemas/skill.frontmatter.schema.json (Tokens: ~1828 | Category: anthropic-skill) -->\n{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/skill.frontmatter.schema.json\",\n  \"title\": \"Nanobot Skill Front Matter (Agent Skills compatible)\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"name\", \"description\"],\n  \"properties\": {\n    \"name\": {\n      \"type\": \"string\",\n      \"minLength\": 1,\n      \"maxLength\": 64,\n      \"pattern\": \"^(?!-)(?!.*--)[a-z0-9-]+(?<!-)$\",\n      \"description\": \"Must be 1-64 chars, lowercase letters/numbers/hyphens, no leading/trailing hyphen, no consecutive hyphens. Must also match directory name (enforced outside JSON Schema).\"\n    },\n    \"description\": {\n      \"type\": \"string\",\n      \"minLength\": 1,\n      \"maxLength\": 1024,\n      \"description\": \"Describe what the skill does and when to use it.\"\n    },\n    \"license\": {\n      \"type\": \"string\",\n      \"description\": \"License name or reference to bundled license file.\"\n    },\n    \"compatibility\": {\n      \"type\": \"string\",\n      \"minLength\": 1,\n      \"maxLength\": 500,\n      \"description\": \"Environment requirements (product, packages, network access, etc.).\"\n    },\n    \"metadata\": {\n      \"type\": \"object\",\n      \"description\": \"Arbitrary key-value mapping.\",\n      \"additionalProperties\": {\n        \"oneOf\": [\n          { \"type\": \"string\" },\n          { \"type\": \"number\" },\n          { \"type\": \"integer\" },\n          { \"type\": \"boolean\" }\n        ]\n      },\n      \"default\": {}\n    },\n    \"allowed-tools\": {\n      \"type\": \"string\",\n      \"description\": \"Space-delimited list of pre-approved tools (experimental). E.g. 'Bash(git:*) Read'.\"\n    },\n\n    \"tools\": {\n      \"description\": \"Nanobot extension: tools visible/available while executing this skill. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tool_approvals\": {\n      \"description\": \"Nanobot extension: tool approval policy while executing this skill.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": {\n          \"description\": \"Default authorization behavior. Currently only 'approve' is supported.\",\n          \"type\": \"string\",\n          \"enum\": [\"approve\"],\n          \"default\": \"approve\"\n        },\n        \"rules\": {\n          \"description\": \"Ordered allow rules; first matching rule wins.\",\n          \"type\": \"array\",\n          \"items\": { \"$ref\": \"#/$defs/toolApprovalRule\" },\n          \"default\": []\n        }\n      }\n    },\n\n    \"skills\": {\n      \"description\": \"Nanobot extension: skills visible/available while executing this skill. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tasks\": {\n      \"description\": \"Nanobot extension: tasks visible/available while executing this skill (task IDs are directory paths under .nanobot/tasks).\",\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\", \"minLength\": 1 },\n      \"default\": [\"inherit\"]\n    },\n\n    \"task_approvals\": {\n      \"description\": \"Nanobot extension: task approval policy while executing this skill.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": {\n          \"description\": \"Default task authorization behavior. Currently only 'approve' is supported.\",\n          \"type\": \"string\",\n          \"enum\": [\"approve\"],\n          \"default\": \"approve\"\n        },\n        \"rules\": {\n          \"description\": \"Ordered allow rules; first matching rule wins.\",\n          \"type\": \"array\",\n          \"items\": { \"$ref\": \"#/$defs/taskApprovalRule\" },\n          \"default\": []\n        }\n      }\n    }\n  },\n\n  \"$defs\": {\n    \"toolApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"tool\", \"allow\"],\n      \"properties\": {\n        \"tool\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Tool identifier (must match an entry in tools).\"\n        },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": {\n          \"description\": \"Optional matchers keyed by tool argument name.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"taskApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"task\", \"allow\"],\n      \"properties\": {\n        \"task\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Task identifier (must match an entry in tasks).\"\n        },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": {\n          \"description\": \"Optional matchers keyed by task input name.\",\n          \"type\": \"object\",\n          \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"matcher\": {\n      \"description\": \"A value matcher for a tool argument key.\",\n      \"oneOf\": [\n        { \"$ref\": \"#/$defs/equalsMatcher\" },\n        { \"$ref\": \"#/$defs/inMatcher\" },\n        { \"$ref\": \"#/$defs/startsWithMatcher\" },\n        { \"$ref\": \"#/$defs/matchesMatcher\" },\n        { \"$ref\": \"#/$defs/containsMatcher\" },\n        { \"$ref\": \"#/$defs/containsAllMatcher\" },\n        { \"$ref\": \"#/$defs/anyOfMatcher\" },\n        { \"$ref\": \"#/$defs/allOfMatcher\" }\n      ]\n    },\n\n    \"equalsMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"equals\"],\n      \"properties\": { \"equals\": {} }\n    },\n\n    \"inMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"in\"],\n      \"properties\": {\n        \"in\": {\n          \"type\": \"array\",\n          \"items\": {}\n        }\n      }\n    },\n\n    \"startsWithMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"startsWith\"],\n      \"properties\": { \"startsWith\": { \"type\": \"string\" } }\n    },\n\n    \"matchesMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"matches\"],\n      \"properties\": { \"matches\": { \"type\": \"string\" } }\n    },\n\n    \"containsMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"contains\"],\n      \"properties\": { \"contains\": {} }\n    },\n\n    \"containsAllMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"containsAll\"],\n      \"properties\": {\n        \"containsAll\": {\n          \"type\": \"array\",\n          \"items\": {}\n        }\n      }\n    },\n\n    \"anyOfMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"anyOf\"],\n      \"properties\": {\n        \"anyOf\": {\n          \"type\": \"array\",\n          \"minItems\": 1,\n          \"items\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    },\n\n    \"allOfMatcher\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"allOf\"],\n      \"properties\": {\n        \"allOf\": {\n          \"type\": \"array\",\n          \"minItems\": 1,\n          \"items\": { \"$ref\": \"#/$defs/matcher\" }\n        }\n      }\n    }\n  }\n}\n\n<!-- File: docs/agents/schemas/task.frontmatter.schema.json (Tokens: ~1455 | Category: anthropic-skill) -->\n{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/task.frontmatter.schema.json\",\n  \"title\": \"Nanobot Task Front Matter (TASK.md only)\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"name\"],\n  \"properties\": {\n    \"name\": { \"type\": \"string\", \"minLength\": 1 },\n    \"description\": { \"type\": \"string\" },\n    \"metadata\": {\n      \"type\": \"object\",\n      \"description\": \"Free-form metadata.\",\n      \"additionalProperties\": true,\n      \"default\": {}\n    },\n\n    \"agent\": {\n      \"description\": \"Agent ID to execute this task with.\",\n      \"type\": \"string\",\n      \"minLength\": 1\n    },\n\n    \"tools\": {\n      \"description\": \"Tools visible/available while executing this task. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tool_approvals\": {\n      \"description\": \"Tool approval policy while executing this task.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": { \"type\": \"string\", \"enum\": [\"approve\"], \"default\": \"approve\" },\n        \"rules\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/toolApprovalRule\" }, \"default\": [] }\n      }\n    },\n\n    \"tasks\": {\n      \"description\": \"Tasks visible/available while executing this task (task IDs are directory paths under .nanobot/tasks).\",\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\", \"minLength\": 1 },\n      \"default\": [\"inherit\"]\n    },\n\n    \"task_approvals\": {\n      \"description\": \"Task approval policy while executing this task.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": { \"type\": \"string\", \"enum\": [\"approve\"], \"default\": \"approve\" },\n        \"rules\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/taskApprovalRule\" }, \"default\": [] }\n      }\n    },\n\n    \"inputs\": {\n      \"description\": \"Task inputs (TASK.md only).\",\n      \"type\": \"array\",\n      \"items\": { \"$ref\": \"#/$defs/taskInput\" },\n      \"default\": []\n    },\n\n    \"next\": {\n      \"description\": \"Relative path to the next step markdown file within the same task directory.\",\n      \"type\": \"string\",\n      \"minLength\": 1\n    }\n  },\n\n  \"$defs\": {\n    \"taskInput\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"name\", \"description\"],\n      \"properties\": {\n        \"name\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Input name.\"\n        },\n        \"description\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"description\": \"Describe what the input means and expected format.\"\n        },\n        \"default\": {\n          \"type\": \"string\",\n          \"description\": \"Optional default value (string).\"\n        }\n      }\n    },\n\n    \"toolApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"tool\", \"allow\"],\n      \"properties\": {\n        \"tool\": { \"type\": \"string\", \"minLength\": 1 },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": { \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" } }\n      }\n    },\n\n    \"taskApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"task\", \"allow\"],\n      \"properties\": {\n        \"task\": { \"type\": \"string\", \"minLength\": 1 },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": { \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" } }\n      }\n    },\n\n    \"matcher\": {\n      \"oneOf\": [\n        { \"$ref\": \"#/$defs/equalsMatcher\" },\n        { \"$ref\": \"#/$defs/inMatcher\" },\n        { \"$ref\": \"#/$defs/startsWithMatcher\" },\n        { \"$ref\": \"#/$defs/matchesMatcher\" },\n        { \"$ref\": \"#/$defs/containsMatcher\" },\n        { \"$ref\": \"#/$defs/containsAllMatcher\" },\n        { \"$ref\": \"#/$defs/anyOfMatcher\" },\n        { \"$ref\": \"#/$defs/allOfMatcher\" }\n      ]\n    },\n\n    \"equalsMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"equals\"], \"properties\": { \"equals\": {} } },\n    \"inMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"in\"], \"properties\": { \"in\": { \"type\": \"array\", \"items\": {} } } },\n    \"startsWithMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"startsWith\"], \"properties\": { \"startsWith\": { \"type\": \"string\" } } },\n    \"matchesMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"matches\"], \"properties\": { \"matches\": { \"type\": \"string\" } } },\n    \"containsMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"contains\"], \"properties\": { \"contains\": {} } },\n    \"containsAllMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"containsAll\"], \"properties\": { \"containsAll\": { \"type\": \"array\", \"items\": {} } } },\n    \"anyOfMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"anyOf\"], \"properties\": { \"anyOf\": { \"type\": \"array\", \"minItems\": 1, \"items\": { \"$ref\": \"#/$defs/matcher\" } } } },\n    \"allOfMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"allOf\"], \"properties\": { \"allOf\": { \"type\": \"array\", \"minItems\": 1, \"items\": { \"$ref\": \"#/$defs/matcher\" } } } }\n  }\n}\n\n<!-- File: docs/agents/schemas/task.step.frontmatter.schema.json (Tokens: ~1254 | Category: anthropic-skill) -->\n{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://nanobot.dev/schemas/task.step.frontmatter.schema.json\",\n  \"title\": \"Nanobot Task Step Front Matter\",\n  \"type\": \"object\",\n  \"additionalProperties\": false,\n  \"required\": [\"name\"],\n  \"properties\": {\n    \"name\": { \"type\": \"string\", \"minLength\": 1 },\n    \"description\": { \"type\": \"string\" },\n    \"metadata\": {\n      \"type\": \"object\",\n      \"description\": \"Free-form metadata.\",\n      \"additionalProperties\": true,\n      \"default\": {}\n    },\n\n    \"agent\": {\n      \"description\": \"Agent ID to execute this step with.\",\n      \"type\": \"string\",\n      \"minLength\": 1\n    },\n\n    \"tools\": {\n      \"description\": \"Tools visible/available while executing this step. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tool_approvals\": {\n      \"description\": \"Tool approval policy while executing this step.\",\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"default\"],\n      \"properties\": {\n        \"default\": { \"type\": \"string\", \"enum\": [\"approve\"], \"default\": \"approve\" },\n        \"rules\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/$defs/toolApprovalRule\" }, \"default\": [] }\n      }\n    },\n\n    \"skills\": {\n      \"description\": \"Skills visible/available while executing this step. If omitted, defaults to [\\\"inherit\\\"].\",\n      \"type\": \"array\",\n      \"items\": {\n        \"oneOf\": [\n          { \"type\": \"string\", \"enum\": [\"inherit\"] },\n          { \"type\": \"string\", \"minLength\": 1 }\n        ]\n      }\n    },\n\n    \"tasks\": {\n      \"description\": \"Tasks visible/available while executing this step (task IDs are directory paths under .nanobot/tasks).\",\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\", \"minLength\": 1 },\n      \"default\": [\"inherit\"]\n    },\n\n    \"next\": {\n      \"description\": \"Relative path to the next step markdown file within the same task directory.\",\n      \"type\": \"string\",\n      \"minLength\": 1\n    }\n  },\n\n  \"$defs\": {\n    \"toolApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"tool\", \"allow\"],\n      \"properties\": {\n        \"tool\": { \"type\": \"string\", \"minLength\": 1 },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": { \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" } }\n      }\n    },\n    \"taskApprovalRule\": {\n      \"type\": \"object\",\n      \"additionalProperties\": false,\n      \"required\": [\"task\", \"allow\"],\n      \"properties\": {\n        \"task\": { \"type\": \"string\", \"minLength\": 1 },\n        \"allow\": { \"type\": \"boolean\" },\n        \"when\": { \"type\": \"object\", \"additionalProperties\": { \"$ref\": \"#/$defs/matcher\" } }\n      }\n    },\n\n    \"matcher\": {\n      \"oneOf\": [\n        { \"$ref\": \"#/$defs/equalsMatcher\" },\n        { \"$ref\": \"#/$defs/inMatcher\" },\n        { \"$ref\": \"#/$defs/startsWithMatcher\" },\n        { \"$ref\": \"#/$defs/matchesMatcher\" },\n        { \"$ref\": \"#/$defs/containsMatcher\" },\n        { \"$ref\": \"#/$defs/containsAllMatcher\" },\n        { \"$ref\": \"#/$defs/anyOfMatcher\" },\n        { \"$ref\": \"#/$defs/allOfMatcher\" }\n      ]\n    },\n    \"equalsMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"equals\"], \"properties\": { \"equals\": {} } },\n    \"inMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"in\"], \"properties\": { \"in\": { \"type\": \"array\", \"items\": {} } } },\n    \"startsWithMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"startsWith\"], \"properties\": { \"startsWith\": { \"type\": \"string\" } } },\n    \"matchesMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"matches\"], \"properties\": { \"matches\": { \"type\": \"string\" } } },\n    \"containsMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"contains\"], \"properties\": { \"contains\": {} } },\n    \"containsAllMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"containsAll\"], \"properties\": { \"containsAll\": { \"type\": \"array\", \"items\": {} } } },\n    \"anyOfMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"anyOf\"], \"properties\": { \"anyOf\": { \"type\": \"array\", \"minItems\": 1, \"items\": { \"$ref\": \"#/$defs/matcher\" } } } },\n    \"allOfMatcher\": { \"type\": \"object\", \"additionalProperties\": false, \"required\": [\"allOf\"], \"properties\": { \"allOf\": { \"type\": \"array\", \"minItems\": 1, \"items\": { \"$ref\": \"#/$defs/matcher\" } } } }\n  }\n}\n\n<!-- File: pkg/servers/system/skills/browser-use.md (Tokens: ~2432 | Category: anthropic-skill) -->\n---\nname: browser-use\ndescription: Automates browser interactions for web testing, form filling, screenshots, and data extraction. Use when the user needs to navigate websites, interact with web pages, fill forms, take screenshots, or extract information from web pages. IMPORTANT - If webfetch fails, try this tool instead.\n---\n\n# Browser Automation with browser-use CLI\n\nThe `browser-use` command provides fast, persistent browser automation. It maintains browser sessions across commands, enabling complex multi-step workflows.\n\n`browser-use` is already installed in the dedicated Nanobot agent image. Use the built-in CLI directly.\n\nWhen using this skill, default to `--browser chromium --headed` unless the user explicitly wants headless/background execution. Headed mode is the safer default because it keeps the browser visible in BrowserView and makes manual intervention possible.\n\n## Quick Start\n\n```bash\nbrowser-use --browser chromium --headed open https://example.com  # Navigate to URL\nbrowser-use state                              # Get page elements with indices\nbrowser-use click 5                            # Click element by index\nbrowser-use type \"Hello World\"                 # Type text\nbrowser-use screenshot                         # Take screenshot\nbrowser-use close                              # Close browser\n```\n\n## Core Workflow\n\n1. **Navigate**: `browser-use --browser chromium --headed open <url>` - Opens URL (starts browser if needed)\n2. **Inspect**: `browser-use state` - Returns clickable elements with indices\n3. **Interact**: Use indices from state to interact (`browser-use click 5`, `browser-use input 3 \"text\"`)\n4. **Verify**: `browser-use state` or `browser-use screenshot` to confirm actions\n5. **Repeat**: Browser stays open between commands\n\n## Browser Modes\n\n```bash\nbrowser-use --browser chromium --headed open <url>  # Recommended default: visible Chromium window\nbrowser-use --browser chromium open <url>      # Headless Chromium for explicit background runs\nbrowser-use --browser real open <url>          # User's Chrome with login sessions\n```\n\n- **chromium**: Fast, isolated, and should usually be run with `--headed`\n- **real**: Uses your Chrome with cookies, extensions, logged-in sessions\n\n## Browser Viewing with VNC\n\nThe VNC server is automatically started and ready to use. You can view and interact with the browser in real-time using VNC. This is **essential** for:\n\n- **Solving CAPTCHAs** - Many sites require human verification\n- **Debugging** - See exactly what the browser is doing\n- **User interaction** - Manual intervention when automation gets stuck\n\nThe VNC server provides:\n- X virtual framebuffer (Xvfb) on display :99\n- VNC server on port 5900\n- an internal WebSocket proxy on port 6080, exposed only through Nanobot's `/browser` path\n- a borderless, maximized browser window sized for the BrowserView pane by default\n\n### Accessing the Browser View\n\nThe Nanobot UI includes a browser viewer component that embeds VNC access. The backend proxies only the WebSocket transport; the standalone noVNC `vnc.html` page is not exposed.\n\n### Using with browser-use\n\n**Default to `--headed` mode. It is required when you need VNC viewing:**\n\n```bash\n# RECOMMENDED DEFAULT (visible in VNC viewer, user can interact)\nbrowser-use --browser chromium --headed open https://example.com\n\n# HEADLESS ONLY WHEN EXPLICITLY APPROPRIATE (faster, but user can't see it)\nbrowser-use --browser chromium open https://example.com\n```\n\nThe `--headed` flag makes the browser visible on the VNC display (:99).\n\n### CAPTCHA Solving Workflow\n\n**When a CAPTCHA is encountered:**\n\n1. **Detect the CAPTCHA** - Look for verification challenges, \"I'm not a robot\" checkboxes, or automation blocks\n2. **Inform the user immediately** - Tell them a CAPTCHA needs to be solved\n3. **Ensure VNC is running** - Verify the browser is running in `--headed` mode\n4. **Guide the user** - Tell them to open the BrowserView pane in the Nanobot UI\n5. **Wait for completion** - Pause automation until the user confirms they've solved the CAPTCHA\n6. **Continue** - Resume automation after manual intervention\n\n**Example:**\n```bash\n# You're automating a task and hit a CAPTCHA\nbrowser-use --browser chromium --headed open https://example.com\nbrowser-use state\n# → Detects CAPTCHA verification challenge\n\n# STOP and inform user:\n# \"I've encountered a CAPTCHA that needs human verification.\n#  Please open the BrowserView pane in the Nanobot UI\n#  and solve the CAPTCHA. Let me know when you're done.\"\n\n# Wait for user confirmation, then continue:\nbrowser-use state  # Verify CAPTCHA is solved\nbrowser-use click 5  # Continue with automation\n```\n\n**Important:** CAPTCHAs cannot be automated - they require human interaction. Always use `--headed` mode when you anticipate CAPTCHAs, and be prepared to pause and ask the user for help.\n\n## Commands\n\n### Navigation\n```bash\nbrowser-use open <url>                    # Navigate to URL\nbrowser-use back                          # Go back in history\nbrowser-use scroll down                   # Scroll down\nbrowser-use scroll up                     # Scroll up\n```\n\n### Page State\n```bash\nbrowser-use state                         # Get URL, title, and clickable elements\nbrowser-use screenshot                    # Take screenshot (outputs base64)\nbrowser-use screenshot path.png           # Save screenshot to file\nbrowser-use screenshot --full path.png    # Full page screenshot\n```\n\n### Interactions (use indices from `browser-use state`)\n```bash\nbrowser-use click <index>                 # Click element\nbrowser-use type \"text\"                   # Type text into focused element\nbrowser-use input <index> \"text\"          # Click element, then type text\nbrowser-use keys \"Enter\"                  # Send keyboard keys\nbrowser-use keys \"Control+a\"              # Send key combination\nbrowser-use select <index> \"option\"       # Select dropdown option\n```\n\n### Tab Management\n```bash\nbrowser-use switch <tab>                  # Switch to tab by index\nbrowser-use close-tab                     # Close current tab\nbrowser-use close-tab <tab>               # Close specific tab\n```\n\n### JavaScript & Data\n```bash\nbrowser-use eval \"document.title\"         # Execute JavaScript, return result\n```\n\n### Cookies\n```bash\nbrowser-use cookies get                   # Get all cookies\nbrowser-use cookies get --url <url>       # Get cookies for specific URL\nbrowser-use cookies set <name> <value>    # Set a cookie\nbrowser-use cookies set name val --domain .example.com --secure --http-only\nbrowser-use cookies clear                 # Clear all cookies\nbrowser-use cookies clear --url <url>     # Clear cookies for specific URL\nbrowser-use cookies export <file>         # Export all cookies to JSON file\nbrowser-use cookies export <file> --url <url>  # Export cookies for specific URL\nbrowser-use cookies import <file>         # Import cookies from JSON file\n```\n\n### Wait Conditions\n```bash\nbrowser-use wait selector \"h1\"            # Wait for element to be visible\nbrowser-use wait selector \".loading\" --state hidden  # Wait for element to disappear\nbrowser-use wait selector \"#btn\" --state attached    # Wait for element in DOM\nbrowser-use wait text \"Success\"           # Wait for text to appear\nbrowser-use wait selector \"h1\" --timeout 5000  # Custom timeout in ms\n```\n\n### Additional Interactions\n```bash\nbrowser-use hover <index>                 # Hover over element (triggers CSS :hover)\nbrowser-use dblclick <index>              # Double-click element\nbrowser-use rightclick <index>            # Right-click element (context menu)\n```\n\n### Information Retrieval\n```bash\nbrowser-use get title                     # Get page title\nbrowser-use get html                      # Get full page HTML\nbrowser-use get html --selector \"h1\"      # Get HTML of specific element\nbrowser-use get text <index>              # Get text content of element\nbrowser-use get value <index>             # Get value of input/textarea\nbrowser-use get attributes <index>        # Get all attributes of element\nbrowser-use get bbox <index>              # Get bounding box (x, y, width, height)\n```\n\n### Python Execution (Persistent Session)\n```bash\nbrowser-use python \"x = 42\"               # Set variable\nbrowser-use python \"print(x)\"             # Access variable (outputs: 42)\nbrowser-use python \"print(browser.url)\"   # Access browser object\nbrowser-use python --vars                 # Show defined variables\nbrowser-use python --reset                # Clear Python namespace\nbrowser-use python --file script.py       # Execute Python file\n```\n\nThe Python session maintains state across commands. The `browser` object provides:\n- `browser.url` - Current page URL\n- `browser.title` - Page title\n- `browser.goto(url)` - Navigate\n- `browser.click(index)` - Click element\n- `browser.type(text)` - Type text\n- `browser.screenshot(path)` - Take screenshot\n- `browser.scroll()` - Scroll page\n- `browser.html` - Get page HTML\n\n### Session Management\n```bash\nbrowser-use sessions                      # List active sessions\nbrowser-use close                         # Close current session\nbrowser-use close --all                   # Close all sessions\n```\n\n### Profile Management\n```bash\nbrowser-use profile list-local            # List local Chrome profiles\n```\n\n**Before opening a real browser (`--browser real`)**, always ask the user if they want to use a specific Chrome profile or no profile. Use `profile list-local` to show available profiles:\n\n```bash\nbrowser-use profile list-local\n# Output: Default: Person 1 (user@gmail.com)\n#         Profile 1: Work (work@company.com)\n\n# With a specific profile (has that profile's cookies/logins)\nbrowser-use --browser real --profile \"Profile 1\" open https://gmail.com\n\n# Without a profile (fresh browser, no existing logins)\nbrowser-use --browser real open https://gmail.com\n\n# Headless mode (no visible window) - useful for cookie export\nbrowser-use --browser real --profile \"Default\" cookies export /tmp/cookies.json\n```\n\nEach Chrome profile has its own cookies, history, and logged-in sessions. Choosing the right profile determines whether sites will be pre-authenticated.\n\n## Global Options\n\n| Option | Description |\n|--------|-------------|\n| `--session NAME` | Use named session (default: \"default\") |\n| `--browser MODE` | Browser mode: chromium, real |\n| `--headed` | Show browser window (chromium mode) |\n| `--profile NAME` | Chrome profile (real mode only) |\n| `--json` | Output as JSON |\n\n**Session behavior**: All commands without `--session` use the same \"default\" session. The browser stays open and is reused across commands. Use `--session NAME` to run multiple browsers in parallel.\n\n## Usage Notes\n\n- Always run `browser-use state` before interacting so you have current element indices.\n- Default to `--browser chromium --headed`.\n- Only omit `--headed` when the user explicitly wants headless/background execution or when visibility and manual takeover are clearly unnecessary.\n- Use `browser-use screenshot` or `browser-use state` to confirm each critical action.\n- Close the session with `browser-use close` when the browser task is complete.\n\n<!-- File: pkg/servers/system/skills/python-scripts.md (Tokens: ~302 | Category: anthropic-skill) -->\n---\nname: python-scripts\ndescription: Write and execute Python scripts using uv for programmatic tasks.\n---\n\nUse Python when a step needs data transformation, API calls, calculations, or deterministic operations where code is more reliable than natural language.\n\n## How to Use\n\nWrite a script with inline dependencies and run it with `uv run`:\n\n```python\n#!/usr/bin/env python3\n# /// script\n# requires-python = \">=3.11\"\n# dependencies = [\n#     \"requests\",\n# ]\n# ///\n\nimport json\nimport requests\n\ndata = requests.get(\"https://api.example.com/data\").json()\nprint(json.dumps(data))\n```\n\n```bash\nuv run script.py\n```\n\nThe `# /// script` block declares dependencies - `uv` installs them automatically.\n\n## Output Conventions\n\n- **Structured data**: Print JSON to stdout\n- **Progress/debug info**: Print to stderr\n- **Errors**: Print to stderr and exit with non-zero code\n\n```python\nimport sys\nimport json\n\n# Output result\nprint(json.dumps({\"items\": [...], \"count\": 42}))\n\n# Debug info (won't pollute output)\nprint(\"Processed 42 items\", file=sys.stderr)\n```\n\n## When to Use Python vs LLM\n\n**Use Python for:** JSON manipulation, API calls with auth/pagination, calculations, file parsing, sorting/filtering, large datasets\n\n**Keep as LLM for:** Tasks requiring judgment, natural language generation, flexible interpretation\n\n<!-- File: pkg/servers/system/skills/scheduled-tasks.md (Tokens: ~778 | Category: anthropic-skill) -->\n---\nname: scheduled-tasks\ndescription: Load this skill for ANY request that mentions scheduled tasks, recurring jobs, reminders, future runs, or time-based automation.\n---\n\n# Scheduled Tasks\n\nScheduled tasks are persistent task definitions managed through the `nanobot.tasks` tools. When a scheduled task fires, nanobot starts a new chat thread and runs the saved prompt there.\n\n## When to Use Scheduled Tasks\n\nUse scheduled tasks when work should happen later or recur on a schedule:\n- Daily, weekly, or monthly reports\n- Reminders or recurring checks\n- Periodic file updates, cleanups, or syncs\n\nIf the user wants the work done right now and not again later, do it directly instead of creating a scheduled task.\n\n## Scheduled Tasks vs Workflows\n\nDo not conflate scheduled tasks with workflows:\n- **Workflows** are reusable procedures stored under `workflows/`.\n- **Scheduled tasks** decide when nanobot should start a future chat thread.\n\nUse a workflow when the user wants a reusable multi-step procedure. Use a scheduled task when the user wants something to happen later or recur automatically.\n\nIf the user wants a workflow to run on a schedule, keep them separate:\n1. Create or update the workflow first.\n2. Create a scheduled task whose prompt tells nanobot to run that workflow.\n\n## Managing Existing Tasks\n\nBefore updating, deleting, or manually running an existing task, call `listScheduledTasks` unless the user already provided the exact `task:///...` URI.\n\nUse the task URI for all follow-up task operations:\n- `updateScheduledTask`\n- `deleteScheduledTask`\n- `startScheduledTask`\n\n## Timezone Rules\n\nWhen creating a scheduled task, always set the timezone explicitly.\n\n- If the user's current timezone is already known from context, use that by default unless they say otherwise.\n- If you do not know the user's timezone, collect it before creating the task.\n- Do not guess a timezone from a city, country, or language unless the user explicitly gave enough information to make that unambiguous.\n\nUnless the user asks for a different timezone, new scheduled tasks should use the user's current timezone.\n\n## Valid Schedule Shapes\n\nThe task tools only accept five-field cron expressions in these shapes:\n\n- **Daily:** minute hour `* * *`\n  - Example: `45 2 * * *` for every day at 02:45\n- **Weekly:** minute hour `* * day-of-week`\n  - Example: `20 23 * * 1,2,3,4` for Monday through Thursday at 23:20\n- **Monthly:** minute hour `day-of-month * *`\n  - Example: `45 2 22,24 * *` for the 22nd and 24th of every month at 02:45\n- **One-time date:** minute hour `day-of-month month *` with a matching expiration date\n  - Example: `45 2 26 3 *` with expiration `2026-03-26` for March 26 at 02:45\n\nDo not use unsupported cron shapes. In particular:\n- Do not use six-field cron expressions\n- Do not use schedules that combine both day-of-month and day-of-week\n- Do not use arbitrary cron patterns outside the daily, weekly, monthly, or one-time date forms above\n\n## Designing Good Task Prompts\n\nEach scheduled run starts a fresh chat thread. The stored prompt should be self-contained:\n- State exactly what to do\n- Name any files, outputs, or deliverables to produce\n- Mention any workflow to run by name if applicable\n- Avoid depending on transient context from the current conversation\n\nFor recurring actions that send messages or modify external systems, confirm the user's intent unless they have already clearly requested that recurring behavior.\n\n<!-- File: pkg/servers/system/skills/workflows.md (Tokens: ~3657 | Category: anthropic-skill) -->\n---\nname: workflows\ndescription: Load this skill for ANY request that mentions workflows, including listing, creating, running, editing, or discussing them.\n---\n\n# Workflows\n\nWorkflows are directories in `workflows/` that codify repeatable processes. Each workflow is a directory containing a `SKILL.md` file (with YAML frontmatter) and any supporting files (scripts, assets, etc.). Each step's output can be referenced by later steps using `{{Step Name}}`.\n\n## Discovering Workflows\n\nThere are two places workflows can exist:\n\n1. **Local workflows** — in the `workflows/` directory on the local filesystem. These are workflows the user has created or installed. List the `workflows/` directory to find these.\n2. **Shared/published workflows** — in the remote Obot registry. These are workflows other users have published (publicly or to the organization). Use `searchArtifacts` to find these.\n\n**When the user asks about \"shared workflows\", \"public workflows\", workflows from other users, or wants to discover/find/browse available workflows they haven't installed yet, ALWAYS use `searchArtifacts` to search the remote registry.** Do not just list the local `workflows/` directory — that only shows what is already installed locally.\n\nIf the user asks to \"list all workflows\", \"show my workflows\", \"which workflows are installed\" or similar without specifying shared/public, find workflows by using the Glob tool (never Bash with find or ls) with pattern `**/SKILL.md` in the `workflows/` directory (do NOT include `workflows/` in both the path and the pattern — that double-nests). If they ask to \"list shared workflows\" or \"find workflows\", use `searchArtifacts`.\n\n## When to Use Workflows\n\nWorkflows are for repeatable tasks. If it's a one-time thing, just execute it directly.\n\n## Tracking Progress\n\nUse TodoWrite during **both** workflow design and execution. The user sees your todo list — it's your primary way of keeping them informed.\n\n- **When designing:** Create todos for your design steps (gather requirements, identify inputs/edge cases, draft workflow, write file, present for review).\n- **When executing:** Create a todo for each workflow step and update status as you go. Add new todos if unexpected issues arise.\n\nKeep todo titles short and scannable. Update them as you progress — don't leave stale todos sitting around.\n\n## Workflow Format\n\nEach workflow is a directory under `workflows/` containing a `SKILL.md` file with:\n- **Frontmatter** (required): YAML frontmatter with `name` (lowercase-hyphenated slug that must match the directory name), `description` (always wrap in double quotes to avoid YAML parsing errors from colons or special characters), and optional `metadata` map.\n- **Inputs**: Optional parameters with defaults\n- **Steps**: Numbered steps with clear instructions\n- **Output**: Optional template for the final result\n\nWhen creating a new workflow, always include frontmatter with `name` (must match the directory name, lowercase with hyphens only), `description` (always in double quotes), and `metadata.createdAt` set to the current date/time in ISO 8601 format (e.g., `2026-01-15T09:00:00Z`). Use `bash` (e.g., `date -u +\"%Y-%m-%dT%H:%M:%SZ\"`) to get the current UTC time — do not guess or hardcode it.\n\n**IMPORTANT:** Always wrap the `description` value in double quotes in the YAML frontmatter. Unquoted descriptions containing colons (`:`) cause YAML parsing errors. For example, write `description: \"Analyze code: find and fix issues\"` not `description: Analyze code: find and fix issues`.\n\n**Name constraints:** lowercase letters, numbers, and hyphens only; no leading/trailing or consecutive hyphens; must match the directory name exactly.\n\n### Example: Simple Review\n\n```markdown\n---\nname: code-review\ndescription: \"Review code changes for quality issues.\"\nmetadata:\n  createdAt: \"2026-01-15T09:00:00Z\"\n---\n\n## Inputs\n\n- **target** (optional): Files to review. Default: `.`\n\n## Steps\n\n### 1. Find Changes\nFind all modified code files in {{input.target}}.\n\n### 2. Review Code\nReview these files for quality issues:\n{{Find Changes}}\n\nFocus on: error handling, edge cases, and readability.\n\n## Output\n\n{{Review Code}}\n```\n\n### Example: With Conditions\n\n```markdown\n---\nname: smart-fix\ndescription: \"Analyze an issue and apply a fix only if it's safe to do so.\"\nmetadata:\n  createdAt: \"2026-01-15T09:00:00Z\"\n---\n\n## Steps\n\n### 1. Analyze Issue\nAnalyze the reported issue and determine severity.\n\n### 2. Check Safety\nBased on this analysis, determine if an automated fix is safe:\n{{Analyze Issue}}\n\nEnd your response with SAFE or UNSAFE.\n\n### 3. Apply Fix\nApply the fix for: {{Analyze Issue}}\n\n**Condition:** {{Check Safety}} contains SAFE\n\n### 4. Create Manual Report\nCreate a report explaining why manual intervention is needed:\n{{Analyze Issue}}\n\n**Condition:** {{Check Safety}} contains UNSAFE\n```\n\n### Example: With Error Handling\n\n```markdown\n---\nname: deploy-with-rollback\ndescription: \"Deploy changes with automatic rollback on failure.\"\nmetadata:\n  createdAt: \"2026-01-15T09:00:00Z\"\n---\n\n## Inputs\n\n- **environment** (required): Target environment (staging, production)\n\n## Steps\n\n### 1. Run Tests\nRun the full test suite. Report any failures.\n\n### 2. Deploy\nDeploy to {{input.environment}}.\n\n**On error:** Rollback\n\n### 3. Verify\nVerify the deployment is healthy.\n\n**On error:** Rollback\n\n### 4. Rollback\nRoll back to the previous version and report what went wrong.\n\n## Output\n\nDeployment to {{input.environment}} complete.\n{{Verify}}\n```\n\n## Key Concepts\n\n**Variables:** Use `{{input.name}}` for inputs and `{{Step Name}}` for outputs from previous steps.\n\n**Conditions:** Add `**Condition:**` to make a step run only when the condition is true. Common patterns:\n- `{{Step Name}} contains X` - check if output contains text\n- `{{Step Name}} not empty` - check if output exists\n- `{{input.flag}} equals yes` - check exact value\n\n**Error handling:** Add `**On error:**` to control what happens when a step fails:\n- `stop` (default) - halt the workflow\n- `continue` - log the error and move to the next step\n- `Step Name` - jump to a specific step (useful for cleanup/rollback)\n\n**Output:** The `## Output` section defines the final result. If omitted, the last step's output is used.\n\n**File dependencies:** When a workflow depends on any files (scripts, templates, data files, configs, etc.), those files **must** be placed in the same directory as the workflow's `SKILL.md` file or in a subdirectory within it. Workflows are self-contained directories — files outside the workflow directory will not be available when the workflow is published, installed, or shared.\n\n## Designing Workflows\n\nWhen asked to create a workflow, DO NOT start writing it immediately. Follow this process:\n\n### Phase 1: Understand\n\n1. Use TodoWrite to create a plan for designing the workflow (e.g., \"Gather requirements\", \"Identify inputs and edge cases\", \"Draft workflow\", \"Write to file\", \"Present for review\").\n2. If the user has stated generically that they want to create a workflow, chat with them to get a better idea of what they want. Don't use the AskUserQuestion tool at this phase.\n3. Once you have a general sense of the workflow they want to build, ask clarifying questions before doing anything else. DO use the AskUserQuestion tool for these specific questions. You need to understand:\n    - What the workflow should accomplish end-to-end\n    - What the expected outcome is\n    - What inputs/variables are needed\n    - What external services or tools are involved\n    - How errors should be handled (stop? retry?)\n    - DON'T ask about execution frequency or external triggers because we don't yet support those features \n4. **Wait for answers. Do not proceed until you have enough information to design a good workflow.** Do not guess or assume — ask.\n\n### Phase 2: Draft & Save\n\n1. Once you understand the requirements, draft the workflow.\n2. Create the directory `workflows/<name>/` and write the workflow to `workflows/<name>/SKILL.md`. Place any supporting files (scripts, data, etc.) alongside `SKILL.md` in the same directory.\n3. Mark your design todos as complete.\n\n### Phase 3: Hand Off\n\n1. Present the workflow to the user with a brief summary of what each step does.\n2. Ask the user to review it. Offer to make changes.\n3. **Offer to execute the workflow — do NOT execute it automatically.** Say something like: \"Want me to run this now, or would you like to make changes first?\"\n\n**IMPORTANT:** Never skip Phase 1. Never jump straight to writing. Never auto-execute after designing.\n\nUse descriptive step names in title case (e.g., \"Fetch Issues\" not \"step1\") — these become variables for later steps.\n\nFor deterministic operations (data transformation, calculations), consider having steps use Python scripts (see `python-scripts` skill). For external services (Slack, Jira, databases), consider MCP servers (see `mcp-curl` skill).\n\n## Executing Workflows\n\nUsers may ask to run a workflow at any time — not just immediately after designing one. Workflows in `workflows/` are persistent, reusable artifacts. A user might say \"run the code-review workflow\" days or weeks after it was created. Treat execution as a standalone operation: load the file, collect inputs, and run it. Don't assume you have any prior context about the workflow beyond what's in the file itself.\n\n**IMPORTANT:** If the user asks you to *create* a workflow, do NOT auto-execute it after writing the file. Present it for review and offer to run it. But if the user asks you to *run* an existing workflow, go ahead — that's an explicit request.\n\n**IMPORTANT:** Installed workflows live on the local filesystem in the `workflows/` directory. When running a workflow, ALWAYS load it from `workflows/<name>/SKILL.md`. Do NOT use `searchArtifacts` to find workflows to run — that tool searches the remote Obot registry for published artifacts, not locally installed workflows.\n\nWhen the user asks you to run a workflow:\n\n1. Load the workflow from `workflows/<name>/SKILL.md`. If you're unsure which workflows are available, use glob pattern `**/SKILL.md` with path `workflows/` to find them.\n2. Use TodoWrite to create a todo for each workflow step before you begin. This is your execution plan — the user will follow along.\n3. **Present the execution plan to the user.** After creating the todos, present a brief summary of what will be executed and ask the user to confirm before proceeding. For example: \"I've planned the following steps: [list steps]. Does this look good to proceed?\"\n4. **Wait for user approval.** Do not begin execution until the user confirms.\n5. Collect any required inputs from the user. If inputs are missing and have no defaults, ask for them.\n6. Call `recordWorkflowRun` with the workflow URI (e.g. `workflow:///workflow-name`) to record the run in the current session.\n7. Execute each step in order:\n    - Check conditions before running conditional steps.\n    - Interpolate variables (`{{input.name}}` and `{{Step Name}}`).\n    - Follow error handling directives.\n    - Mark each todo complete/failed/skipped as you go.\n8. Provide running updates in normal chat messages as you work through each step. If errors occur, judgment calls are needed, or unexpected conditions arise, mention them in chat and add new todos as needed.\n\nComplete tasks fully, follow requested formats exactly, and provide specific analysis rather than vague summaries.\n\nAfter execution, present a structured run summary directly in chat:\n- A heading with the workflow name and a pass/fail indicator (e.g., ## Workflow Run: Code Review)\n- A tally of step outcomes (e.g., \"3 of 3 steps succeeded\" or \"2 of 3 steps succeeded, 1 failed\")\n- For failed steps, a brief explanation of what went wrong\n- A summary of any modifications made to external systems or artifacts produced\n- If the workflow defines an Output section, include that content last\n\nIf the workflow was aborted or no steps completed, still produce a summary explaining what happened and which steps were skipped.\n\n## Improving Workflows\n\nAfter executing a workflow, you can analyze the execution and propose improvements to the workflow file.\n\n**What to look for:**\n- Steps that needed retries or failed — unclear prompt? missing format spec?\n- Steps that produced poor output — missing context? too vague?\n- Steps that should be split into smaller steps\n- Missing error handling for steps that failed\n- Hardcoded values that should be inputs\n\n**Process:** Review which steps succeeded/failed/retried and what fixes were applied. For each issue, determine the concrete fix (rewrite prompt, add format spec, split step, add error handling, etc.). Present each proposed change with the issue, current text, and proposed text.\n\n**IMPORTANT:** Get user approval before editing the workflow file. Never apply changes automatically.\n\n**Example:**\n```\n## Proposed Changes to issue-triage.md\n\n### 1. Step: Analyze Issues\n**Issue:** Agent returned prose instead of JSON. Had to retry.\n**Change:** Add explicit format requirement.\n\nCurrent:\n> Analyze each issue and categorize by priority.\n> Return the results as JSON.\n\nProposed:\n> Analyze each issue and categorize by priority.\n>\n> Return as a JSON array:\n> [{\"number\": 1, \"title\": \"...\", \"priority\": \"High|Medium|Low\", \"reason\": \"...\"}]\n\nApply these changes? (yes/no/select specific)\n```\n\nDon't change things that worked fine or add complexity without a concrete problem to solve.\n\n## Publishing & Sharing Workflows\n\nYou can publish workflows to Obot's registry so other users can discover and install them.\n\n### Publishing\n\nTo publish a workflow, use the `publishArtifact` tool:\n\n1. Ensure the workflow exists in `workflows/<name>/` with a `SKILL.md` that has proper frontmatter (name, description). The workflow must pass format validation before publishing.\n2. Call `publishArtifact` with the workflow directory name. For example: `publishArtifact({ \"workflowName\": \"code-review\" })`.\n3. The tool bundles all files in the directory and uploads to Obot. The `SKILL.md` frontmatter is the source of truth for artifact metadata.\n4. The first publish creates version 1. Subsequent publishes of the same workflow create new versions (v2, v3, etc.).\n5. Version 1 starts owner-only unless its subjects are updated.\n6. Each later published version defaults to the previous version's sharing settings.\n7. To change who a specific published version is shared with, use `setArtifactSubjects({ \"id\": \"<artifact-id>\", \"version\": <n>, \"subjects\": [...] })`. If `version` is omitted, the latest version is updated.\n8. Supported subjects are:\n   - `{ \"type\": \"user\", \"id\": \"<user-id>\" }`\n   - `{ \"type\": \"group\", \"id\": \"<group-id>\" }`\n   - `{ \"type\": \"selector\", \"id\": \"*\" }` for all Obot users\n9. An empty `subjects` list makes that version owner-only again.\n10. To find valid user or group IDs before setting subjects, use `listSubjects({ \"type\": \"user\" | \"group\", \"query\": \"...\" })`. Leave `query` blank to list everything visible.\n\n### Searching the Registry\n\nTo find published or shared workflows from other users in the **remote Obot registry**, use `searchArtifacts`:\n- Search by keyword: `searchArtifacts({ \"query\": \"code review\", \"artifactType\": \"workflow\" })`\n- List all shared workflows: `searchArtifacts({ \"artifactType\": \"workflow\" })` (empty query returns all visible artifacts)\n- This is for discovering shared, public, or published workflows — not for finding workflows already on your local filesystem.\n- **When a user asks about \"shared workflows\", this is the tool to use.**\n\n### Installing\n\nTo install a published workflow, use `installArtifact`:\n- `installArtifact({ \"id\": \"<artifact-id>\" })` installs the latest version\n- `installArtifact({ \"id\": \"<artifact-id>\", \"version\": 2 })` installs a specific version\n- The workflow is automatically extracted into `workflows/<name>/` and is immediately available.\n- Installing overwrites any existing local workflow with the same name.\n\n<!-- File: pkg/servers/system/testdata/with-override/skills/workflows.md (Tokens: ~34 | Category: anthropic-skill) -->\n---\nname: Custom Workflows\ndescription: My custom workflows skill that overrides the built-in\n---\n\n# Custom Workflows Skill\n\nThis overrides the built-in workflows skill.\n\n<!-- File: pkg/servers/system/testdata/with-user-skills/skills/my-custom-skill.md (Tokens: ~22 | Category: anthropic-skill) -->\n---\nname: Custom Skill\ndescription: A custom skill\n---\n\n# My Custom Skill\n\nCustom content here.\n\n<!-- File: pkg/servers/system/testdata/with-user-skills/skills/user-skill.md (Tokens: ~27 | Category: anthropic-skill) -->\n---\nname: User-Defined Skill\ndescription: A user-defined skill\n---\n\n# User Skill\n\nThis is a custom user skill.\n\n<!-- File: pkg/servers/workflows/testdata/with-workflows/workflows/another/SKILL.md (Tokens: ~57 | Category: anthropic-skill) -->\n---\nname: another\ndescription: Another workflow for testing multiple workflow listing.\nmetadata:\n  createdAt: \"2026-01-16T10:30:00Z\"\n---\n\n## Steps\n\n### 1. only_step\nExecute the single step.\n\n---\n\n## Output\n\nDone.\n\n<!-- File: pkg/servers/workflows/testdata/with-workflows/workflows/no-description/SKILL.md (Tokens: ~16 | Category: anthropic-skill) -->\n# No Workflow Header\n\nThis file doesn't have the standard workflow header format.\n\n<!-- File: pkg/servers/workflows/testdata/with-workflows/workflows/test-workflow/SKILL.md (Tokens: ~114 | Category: anthropic-skill) -->\n---\nname: test-workflow\ndescription: This is a test workflow for unit testing purposes.\nlicense: MIT\ncompatibility: nanobot >= 0.1.0\nmetadata:\n  createdAt: \"2026-01-15T09:00:00Z\"\n  author: testuser\n---\n\n## Inputs\n\n- **input_name** (required): A test input parameter.\n\n## Steps\n\n### 1. first_step\nDo something useful.\n\n---\n\n### 2. second_step\nDo something else.\n\n---\n\n## Output\n\nThe workflow completed successfully.\n\n<!-- File: pkg/config/testdata/directory-with-readme/agents/README.md (Tokens: ~15 | Category: subagent-persona) -->\n# README\n\nThis is documentation and should not be loaded as an agent.\n</yakaai_skills>"}