# Contributor Map (Agent-Oriented) This is a fast navigation guide for contributors (human or AI). It answers: - Where do I add a new tool? - Where do I add a new skill? - Where do I change routing/policy? - What tests should I run? For the execution-flow diagram, see `docs/architecture/AGENT_DIAGRAM.md`. ## 30-Second Repo Tour ```text src/ daemon/ Start-up wiring, service init, message routing backends/ Native agent + orchestrator (tool loop lives here) tools/ Tool interfaces, policy, executor, builtins skills/ Skill loader/registry + install/watch infra hooks/ Confirm/log/silent policy + autonomy resolution sandbox/ Docker sandbox manager + sandboxed tool wrappers models/ Provider clients + model router + retry/cost/capabilities channels/ Chat adapters + pairing gate gateway/ WebSocket JSON-RPC server + web UI + handlers memory/ Hybrid search + embeddings + persistence session/ SQLite store + session mgmt cli/ CLI entrypoints + setup wizard automation/ Cron/webhooks/heartbeat/gmail watcher docs/ api/ Tool and gateway protocol docs security/ Capability model, sandboxing, injection resistance architecture/ Diagrams + contributor maps config/ default.yaml Example configuration ``` ## Adding a New Tool ### Where code goes - Builtins live in `src/tools/builtin/`. - Core types live in `src/tools/types.ts`. - Tools are registered through the daemon wiring (see existing patterns in `src/daemon/index.ts`). ### Minimal tool skeleton ```ts import type { Tool, ToolResult } from '../types.js'; export const myTool: Tool = { name: 'my.tool', description: 'What it does (model-facing).', // Optional: gate credentialed actions requiredSecretScopes: ['my_scope'], inputSchema: { type: 'object', properties: { foo: { type: 'string', description: '...' }, }, required: ['foo'], }, execute: async (rawArgs: unknown, _context?: { signal?: AbortSignal }): Promise => { // ... return { success: true, output: 'ok' }; }, }; ``` ### Security checklist - If the tool calls an external service or uses credentials: - set `requiredSecretScopes` on the tool. - ensure skill permissions can gate it (`manifest.json.permissions.secrets`). - If it reads/writes files: - use `file.*` tools rather than bespoke FS access. - skills can restrict FS paths via `permissions.fs`. ### Tests to add - Unit tests in `src/tools/builtin/.test.ts`. - If you touch policy/executor logic: add tests in `src/tools/policy.test.ts` or `src/tools/executor.test.ts`. ## Adding a New Skill ### What a skill is A skill is a package with: - `SKILL.md`: instructions injected into the system prompt. - `manifest.json`: metadata + capability declarations. Where skills live: - Bundled skills: `skills/` - Managed skills (installed by Flynn): configured skill directory (see config) Skill loading: - Loader: `src/skills/loader.ts` - Registry: `src/skills/registry.ts` - Watcher (optional): `src/skills/watcher.ts` ### Capability permissions If the skill is used via routing (intent target type `skill`), add `permissions` to `manifest.json`. Without `permissions`, a skill is still loadable, but in skill context it has no tool access. Reference: `docs/security/SAFE_PERSONAL_AGENT.md`. ## Routing: Agents vs Skills vs Default Where routing decisions happen: - Inbound routing: `src/daemon/routing.ts` Inputs to routing: - Channel + sender (agent router) - Intent registry (regex rules) — can target `agent` or `skill` - Metadata overrides (gateway / channel adapters) If you need a new routing rule type: - Intent targets live in the intent registry/types (see `src/intents/registry.ts`). ## Tool Policy + Execution You will usually touch these files for capability/security work: - Tool allowlisting: `src/tools/policy.ts` - Tool runtime enforcement + audit: `src/tools/executor.ts` - Confirmation/autonomy: `src/hooks/engine.ts`, `src/hooks/autonomy.ts` In skill context: - `ToolPolicyContext.skillName` and `.skillPermissions` are set in `src/daemon/routing.ts`. - ToolPolicy filters available tools. - ToolExecutor enforces fs/net/secret/injection restrictions even if a tool is somehow called. ## Sandbox Sandbox components: - Docker sandbox manager: `src/sandbox/manager.ts` - Docker implementation: `src/sandbox/docker.ts` - Sandboxed tool wrappers: `src/sandbox/tools.ts` - Tool replacement wiring: `src/daemon/routing.ts` Notes: - Today the sandbox wiring replaces `shell.exec` and `process.start` when sandbox is enabled. - In skill context, high-risk execution defaults to sandbox unless the skill opts into host execution. ## Gateway / API Surface Gateway protocol docs: - `docs/api/PROTOCOL.md` Gateway handlers: - `src/gateway/handlers/` (JSON-RPC methods) Useful places to start: - `src/gateway/server.ts` (server lifecycle) - `src/gateway/protocol.ts` (types) ## Tests + Commands Common checks: ```bash pnpm typecheck pnpm lint pnpm test:run ``` Targeted tests for safety boundary changes: - Tool policy: `pnpm test:run src/tools/policy.test.ts` - Tool executor: `pnpm test:run src/tools/executor.test.ts` - Skill loader: `pnpm test:run src/skills/loader.test.ts` - Routing: `pnpm test:run src/daemon/routing.test.ts` ## First 3 PRs to Pick Up (Good Agent On-Ramps) These are small, high-leverage changes that teach you the architecture quickly. ### PR 1: Add a new "narrow" skill + permissions Goal: add a skill that can only do one bounded thing (example: summarize a URL). Deliverables: - `skills/url-summarizer/SKILL.md` - `skills/url-summarizer/manifest.json` with permissions: - `tool_groups: ["group:web"]` - `net: [{"host":"*","ports":[443]}]` (or narrower if you prefer) - `execution_environment: "sandbox"` (default) Acceptance: - Skill loads (`flynn doctor` / skills list) - In skill context, `shell.exec` is not available - `web.fetch` works for https URLs ### PR 2: Route into the skill via intents Goal: make it easy to invoke the skill without special UI. Deliverables: - Add an `intents.rules[]` entry targeting `type: skill` - Patterns like: `summarize *`, `tldr *` Acceptance: - A message like `summarize https://example.com` routes to the skill - Tool list is capability-filtered for that skill context ### PR 3: Add an end-to-end safety test Goal: lock in behavior so future refactors don’t weaken the boundary. Deliverables: - A test that asserts: when routed to a skill context with web-only permissions: - `ToolPolicy` excludes `shell.exec` and `file.write` - `ToolExecutor` denies a direct attempt to call `file.write` outside allowed fs globs Suggested test locations: - `src/tools/policy.test.ts` - `src/tools/executor.test.ts` ## Where to Add What (Cheat Sheet) ```text New tool .................. src/tools/builtin/ + register in daemon Tool allow/deny logic ...... src/tools/policy.ts Tool runtime enforcement .... src/tools/executor.ts New skill .................. skills//{SKILL.md,manifest.json} Skill loader/validation ..... src/skills/loader.ts Skill routing (intents) ..... src/daemon/routing.ts + config intents Sandbox behavior ........... src/sandbox/* + src/daemon/routing.ts Confirmation UX ............ src/hooks/* + frontends/gateway Web UI changes ............. src/gateway/ui/ ```