From 37f4faec4c6f706dbab55e321120c0a80afbf636 Mon Sep 17 00:00:00 2001 From: Asaf Varon Date: Fri, 10 Jul 2026 12:32:26 +0300 Subject: [PATCH 1/2] Add initial extra setup skill Co-Authored-By: Claude Opus 4.8 --- .github/workflows/validate.yml | 20 ++ .gitignore | 8 + LICENSE | 2 +- README.md | 87 +++++++ package.json | 11 + scripts/validate-skills.mjs | 196 ++++++++++++++ skills/extra-setup/SKILL.md | 243 ++++++++++++++++++ skills/extra-setup/assets/minimal-agents.yml | 78 ++++++ .../references/agents-yml-guide.md | 216 ++++++++++++++++ .../extra-setup/references/extra-concepts.md | 155 +++++++++++ .../extra-setup/references/troubleshooting.md | 199 ++++++++++++++ 11 files changed, 1214 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/validate.yml create mode 100644 .gitignore create mode 100644 README.md create mode 100644 package.json create mode 100644 scripts/validate-skills.mjs create mode 100644 skills/extra-setup/SKILL.md create mode 100644 skills/extra-setup/assets/minimal-agents.yml create mode 100644 skills/extra-setup/references/agents-yml-guide.md create mode 100644 skills/extra-setup/references/extra-concepts.md create mode 100644 skills/extra-setup/references/troubleshooting.md diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..af2a63d --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,20 @@ +name: Validate Skills + +on: + push: + pull_request: + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Validate skills + run: npm run validate diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..da8ebd1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +node_modules/ +.idea/ +.DS_Store +.env +.env.* +npm-debug.log* +pnpm-debug.log* +yarn-debug.log* diff --git a/LICENSE b/LICENSE index be8f35f..be069f2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2026 extra +Copyright (c) 2026 extra-org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md new file mode 100644 index 0000000..5b18167 --- /dev/null +++ b/README.md @@ -0,0 +1,87 @@ +# Extra Skills + +Claude Code Agent Skills for the [`extra`](https://github.com/extra-org/extra) +agent framework. + +Two repositories, two jobs: + +``` +extra = the main framework / runtime / CLI (agentctl, the engine, Docker image) +extra-skills = Claude Code Agent Skills for setup, debugging, and onboarding +``` + +This repo does **not** contain the `extra` source — only skills, their +reference docs and templates, a validation script, and CI. + +## Install + +List the skills in this repo: + +```bash +npx skills add extra-org/extra-skills --list +``` + +Install all skills into the **current project** (for Claude Code): + +```bash +npx skills add extra-org/extra-skills --skill '*' -a claude-code +``` + +Install all skills **globally** for Claude Code (available in every project): + +```bash +npx skills add extra-org/extra-skills --skill '*' -a claude-code -g +``` + +- **without `-g`** → installs into the current project only. +- **with `-g`** → installs globally for Claude Code. + +## Usage + +Open Claude Code in any project that uses (or will use) `extra`, then run: + +``` +/extra-setup +/extra-setup simple banking demo +/extra-setup repair my agents.yml +/extra-setup configure MCP tools and resolvers +``` + +## What the skill does + +`/extra-setup` turns Claude Code into a setup agent for `extra`. It: + +1. **Inspects** the project (looks for `agents.yml`/`agents.yaml`, `plugins/`, + `prompts/`, package manager, Docker, whether `extra` is installed). +2. **Understands the goal** from your argument, or infers a safe minimal demo. +3. **Creates or repairs `agents.yml`** — one root orchestrator plus focused + agents — using a minimal, valid template, without destructive rewrites. +4. **Configures** agents, MCP servers (URL-based), tools, resolvers (with + `shared`/`agent` scope), and access control (`protected` + `plugins/access.py`). +5. **Discovers the real commands** from the project (never guesses) and runs + `agentctl generate` / `agentctl validate` (or their Docker equivalents), + plus any tests/lint/typecheck the project defines. +6. **Summarizes** what it detected, created, changed, ran, and what to do next. + +It ships with reference docs it reads on demand: +[extra-concepts](skills/extra-setup/references/extra-concepts.md), +[agents-yml-guide](skills/extra-setup/references/agents-yml-guide.md), and +[troubleshooting](skills/extra-setup/references/troubleshooting.md), plus a +[minimal `agents.yml` template](skills/extra-setup/assets/minimal-agents.yml). + +## Development + +```bash +npm install # no runtime deps; sets up npm scripts +npm run validate # validate every skill in skills/ +``` + +`npm run validate` runs [`scripts/validate-skills.mjs`](scripts/validate-skills.mjs) +(Node built-ins only), which checks that each skill has a `SKILL.md` with valid +frontmatter, a `name` matching its folder, kebab-case naming, resolvable +reference links, and no committed secrets or private paths. CI runs the same +check on every push and pull request. + +## License + +[MIT](LICENSE) © extra-org diff --git a/package.json b/package.json new file mode 100644 index 0000000..0d72a31 --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "name": "extra-skills", + "version": "0.1.0", + "private": true, + "type": "module", + "description": "Claude Code Agent Skills for the extra agent framework.", + "license": "MIT", + "scripts": { + "validate": "node scripts/validate-skills.mjs" + } +} diff --git a/scripts/validate-skills.mjs b/scripts/validate-skills.mjs new file mode 100644 index 0000000..affdc5c --- /dev/null +++ b/scripts/validate-skills.mjs @@ -0,0 +1,196 @@ +#!/usr/bin/env node +// Validate the extra-skills repository structure. +// +// Dependency-free (Node.js built-ins only). Checks that every skill under +// `skills/` is well-formed enough to be installed by `npx skills add` and +// loaded by Claude Code: +// +// 1. `skills/` exists. +// 2. Each direct child folder has a SKILL.md. +// 3. Each SKILL.md starts with YAML frontmatter. +// 4. Frontmatter includes `name`. +// 5. Frontmatter includes `description`. +// 6. `name` exactly matches the skill folder name. +// 7. Skill names are safe lowercase kebab-case. +// 8. Markdown links to reference files resolve to existing files. +// 9. No obvious private paths or secrets are committed. +// +// Exits non-zero on any error so it can gate CI. + +import { readdirSync, readFileSync, statSync, existsSync } from "node:fs"; +import { join, dirname, relative, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const SKILLS_DIR = join(ROOT, "skills"); + +const errors = []; +const warnings = []; +const skillsChecked = []; + +const KEBAB_CASE = /^[a-z0-9]+(-[a-z0-9]+)*$/; + +// Patterns that must never appear in committed skill content. +const SECRET_PATTERNS = [ + { label: "macOS/Linux home path", re: /\/Users\/[A-Za-z0-9._-]+\//g }, + { label: "Linux home path", re: /\/home\/[A-Za-z0-9._-]+\//g }, + { label: "Windows user path", re: /[A-Za-z]:\\Users\\[A-Za-z0-9._-]+/g }, + { label: "AWS access key id", re: /\bAKIA[0-9A-Z]{16}\b/g }, + { label: "Anthropic-style secret key", re: /\bsk-ant-[A-Za-z0-9_-]{20,}\b/g }, + { label: "generic secret assignment", re: /\b(?:api[_-]?key|secret|password|token)\s*[:=]\s*['"][^'"\s]{12,}['"]/gi }, +]; + +function fail(msg) { + errors.push(msg); +} + +function parseFrontmatter(content) { + // Frontmatter must be the very first thing in the file. + if (!content.startsWith("---")) { + return { ok: false, reason: "file does not start with '---' frontmatter" }; + } + const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/); + if (!match) { + return { ok: false, reason: "frontmatter block is not closed with '---'" }; + } + const body = match[1]; + const data = {}; + for (const rawLine of body.split(/\r?\n/)) { + const line = rawLine.trimEnd(); + if (!line.trim() || line.trimStart().startsWith("#")) continue; + const m = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/); + if (!m) continue; // tolerate nested/multiline values we don't need + const key = m[1]; + let value = m[2].trim(); + if ( + (value.startsWith('"') && value.endsWith('"')) || + (value.startsWith("'") && value.endsWith("'")) + ) { + value = value.slice(1, -1); + } + if (!(key in data)) data[key] = value; + } + return { ok: true, data }; +} + +function scanForSecrets(relPath, content) { + for (const { label, re } of SECRET_PATTERNS) { + const found = content.match(re); + if (found) { + fail(`${relPath}: contains a ${label} (${JSON.stringify(found[0])})`); + } + } +} + +function checkReferenceLinks(skillDir, relPath, content) { + // Markdown links like [text](references/foo.md) or [text](assets/bar.yml). + const linkRe = /\]\(([^)]+)\)/g; + let m; + while ((m = linkRe.exec(content)) !== null) { + let target = m[1].trim(); + // Skip external links, anchors, and mailto. + if (/^([a-z]+:)?\/\//i.test(target) || target.startsWith("#") || target.startsWith("mailto:")) { + continue; + } + // Strip anchor / query suffixes. + target = target.split("#")[0].split("?")[0].trim(); + if (!target) continue; + const resolved = resolve(skillDir, target); + if (!existsSync(resolved)) { + fail(`${relPath}: link target does not exist -> ${target}`); + } + } +} + +function main() { + if (!existsSync(SKILLS_DIR) || !statSync(SKILLS_DIR).isDirectory()) { + fail("skills/ directory does not exist"); + return report(); + } + + const entries = readdirSync(SKILLS_DIR, { withFileTypes: true }).filter((e) => + e.isDirectory() + ); + + if (entries.length === 0) { + fail("skills/ contains no skill folders"); + return report(); + } + + for (const entry of entries) { + const skillName = entry.name; + const skillDir = join(SKILLS_DIR, skillName); + const skillMdPath = join(skillDir, "SKILL.md"); + const relSkillMd = relative(ROOT, skillMdPath); + + if (!existsSync(skillMdPath)) { + fail(`skills/${skillName}: missing SKILL.md`); + continue; + } + + const content = readFileSync(skillMdPath, "utf8"); + const fm = parseFrontmatter(content); + if (!fm.ok) { + fail(`${relSkillMd}: ${fm.reason}`); + continue; + } + + if (!fm.data.name) fail(`${relSkillMd}: frontmatter missing 'name'`); + if (!fm.data.description) fail(`${relSkillMd}: frontmatter missing 'description'`); + + if (fm.data.name && fm.data.name !== skillName) { + fail( + `${relSkillMd}: frontmatter name '${fm.data.name}' does not match folder '${skillName}'` + ); + } + + if (!KEBAB_CASE.test(skillName)) { + fail(`skills/${skillName}: folder name is not safe lowercase kebab-case`); + } + if (fm.data.name && !KEBAB_CASE.test(fm.data.name)) { + fail(`${relSkillMd}: name '${fm.data.name}' is not safe lowercase kebab-case`); + } + + checkReferenceLinks(skillDir, relSkillMd, content); + + // Scan the whole skill tree for secrets / private paths. + for (const file of walk(skillDir)) { + const rel = relative(ROOT, file); + const raw = readFileSync(file, "utf8"); + scanForSecrets(rel, raw); + } + + skillsChecked.push(skillName); + } + + return report(); +} + +function* walk(dir) { + for (const entry of readdirSync(dir, { withFileTypes: true })) { + const full = join(dir, entry.name); + if (entry.isDirectory()) { + yield* walk(full); + } else if (entry.isFile()) { + // Only scan text-like files. + if (/\.(md|mdx|ya?ml|toml|txt|json|py|mjs|js|ts)$/i.test(entry.name)) { + yield full; + } + } + } +} + +function report() { + for (const w of warnings) console.warn(`⚠ ${w}`); + if (errors.length > 0) { + console.error(`\n✖ Validation failed with ${errors.length} error(s):\n`); + for (const e of errors) console.error(` - ${e}`); + process.exit(1); + } + console.log( + `✔ Validated ${skillsChecked.length} skill(s): ${skillsChecked.join(", ") || "(none)"}` + ); + process.exit(0); +} + +main(); diff --git a/skills/extra-setup/SKILL.md b/skills/extra-setup/SKILL.md new file mode 100644 index 0000000..8bcf1a5 --- /dev/null +++ b/skills/extra-setup/SKILL.md @@ -0,0 +1,243 @@ +--- +name: extra-setup +description: Set up, configure, repair, and validate a project that uses the extra agent framework, including agents.yml, agents, tools, MCPs, resolvers, generation, Docker, and local execution. +--- + +# extra-setup + +You are acting as a **setup agent for the `extra` framework**. `extra` turns a +single declarative YAML file (`agents.yml`) into a running multi-agent system: +an orchestrator routes each request to a focused agent, and each agent has its +own prompt, tools, MCP servers, and resolvers. The CLI is `agentctl` (also +shipped as the Docker image `ghcr.io/asaf-prog/extra:latest`). + +Your job when invoked as `/extra-setup [goal]` is to inspect the current +project, then create or repair a **minimal, valid** `extra` configuration and +verify it with the real commands the project supports. Be conservative: never +guess commands, never invent schema fields, and never delete working +configuration. + +Read the reference files as needed: + +- [extra-concepts.md](references/extra-concepts.md) — what `extra`, agents, + orchestrators, tools, MCPs, resolvers, and generation actually are. +- [agents-yml-guide.md](references/agents-yml-guide.md) — the exact `agents.yml` + schema, examples, common mistakes, and a validation checklist. +- [troubleshooting.md](references/troubleshooting.md) — fixes for specific + errors (no default agent, undefined tool/MCP/resolver, invalid scope, Docker + failures, wrong routing, access denials, etc.). +- [minimal-agents.yml](assets/minimal-agents.yml) — the starting template for a + new `agents.yml`. + +Follow this workflow in order. + +## 1. Inspect the current project + +Look for these files/dirs and note which exist: + +``` +agents.yml agents.yaml resolvers.toml plugins/plugins.toml +README.md package.json pnpm-lock.yaml yarn.lock package-lock.json +pyproject.toml requirements.txt Dockerfile docker-compose.yml +Makefile examples/ tests/ src/ prompts/ plugins/ +``` + +> Note: `extra` specs are commonly named `agents.yml` **or** `agents.yaml`, and +> the flagship example uses `agents.yaml`. Check both. `extra` does not use a +> `resolvers.toml` — resolver metadata lives in `plugins/plugins.toml` and +> resolver code lives in `plugins/resolvers/`. If you find a `resolvers.toml`, +> treat it as project-specific and do not assume `extra` reads it. + +Then summarize concisely: + +- what exists and what is missing, +- whether this looks like a **new** or **existing** project, +- which package manager / build system is in use (npm, pnpm, yarn, poetry, pip, + make, Docker), +- whether `extra` is already installed or referenced (a `pip install + agent-engine`, an `agentctl` invocation, or the `ghcr.io/asaf-prog/extra` + image in docs/Makefile/compose). + +## 2. Understand the setup goal + +If the user passed an argument to `/extra-setup`, that is the goal. Examples: + +``` +/extra-setup simple banking demo +/extra-setup create an agent that answers balance questions +/extra-setup repair my agents.yml +/extra-setup configure MCP tools and resolvers +/extra-setup add access resolver +/extra-setup Docker local demo +``` + +If no argument is given: + +- infer a minimal setup from the current project, +- ask **at most one** clarifying question, and only if the goal is genuinely + impossible to infer, +- otherwise proceed with a safe minimal demo (one orchestrator + one agent). + +## 3. Create or repair `agents.yml` + +**If `agents.yml` / `agents.yaml` does not exist**, create one: + +- start from [assets/minimal-agents.yml](assets/minimal-agents.yml), +- adapt `system.name`, agent ids, descriptions, and prompt filenames to the + goal, +- create the referenced prompt files under `prompts/` with short, real content + (an orchestrator prompt is **mandatory** for every orchestrator), +- keep it minimal and valid — one root, one router, one or two agents. + +**If it already exists**, repair rather than rewrite: + +- read it and preserve the user's intent, +- validate structure against [agents-yml-guide.md](references/agents-yml-guide.md), +- fix only obvious problems: undefined `graph`/`tools`/`mcps`/`resolvers` + references, a missing/ambiguous root entrypoint, duplicate ids, a + node-level `model` override missing required fields, an orchestrator missing + its mandatory `orchestrator` prompt, `protected: true` without + `plugins/access.py`, +- do **not** delete configuration unless it is clearly broken and the fix is + obvious. When unsure, report the issue instead of editing. + +## 4. Configure agents and topology + +Ensure there is exactly one **root** node in `graph` (usually an orchestrator) +and a clear set of leaf agents. Check: + +- every id in `graph` is declared under `orchestrators` or `agents`, +- orchestrators have a `description` and a `prompts.orchestrator`, +- agents have a `description` (prompts are optional but recommended), +- routing reads well: each child's `description` is the routing criterion its + parent uses, so descriptions must be distinct and specific, +- disabled/unused nodes are removed from `graph` (a declaration not referenced + in `graph` is not reachable), +- tool/MCP/resolver bindings on each agent point at declared top-level ids. + +Keep the final graph small and easy to read. + +## 5. Configure MCPs and tools + +`extra` MCPs are **URL-based only** (remote servers); stdio/local-process MCPs +are not part of the schema. Under `mcps:` each entry needs a `url`. Tools under +`tools:` need a `description` and are implemented in `plugins/tools/.py`. + +Validate: + +- MCP ids are unique and referenced MCPs exist, +- MCP `url` values are real URLs (not placeholders that will fail at runtime), +- tool ids are unique and every `tools: [...]` reference on an agent is declared, +- no duplicate ids across sections. + +If the user asks for MCP integration but gives no real URL, **do not invent +one**. Add the `mcps:` entry with a clearly-marked `TODO` placeholder URL and +tell the user exactly what to fill in, or leave it out and explain what is +missing. Never bind a tool to an agent whose plugin stub you have not generated. + +## 6. Configure resolvers + +Resolvers fill `{{variables}}` in prompts before a node runs; they are chosen by +the engine and never exposed to the LLM. Declare them under `resolvers:` with a +`scope`: + +- `scope: shared` → generated once on `SharedResolver` (in + `plugins/resolvers/shared.py`), inherited by all agents, +- `scope: agent` (default) → generated on the declaring agent's `Resolver` + subclass in `plugins/resolvers/.py`. + +Check that every `resolvers: [...]` reference on a node is declared, and that +each declared resolver has a valid scope. Do **not** create a `resolvers.toml` +— `extra` generates resolver stubs via `agentctl generate`. + +For access control: a node with `protected: true` requires a +`plugins/access.py` with `class AccessResolver: def can_access(self, ctx, +node_id) -> bool`. Never surface auth/access/user/organization context to the +LLM — it stays inside resolver/hook/access plugin code. + +## 7. Discover real commands — do not guess + +Discover the project's actual commands before running anything. Read: + +``` +README.md package.json (scripts) pyproject.toml Makefile +Dockerfile docker-compose.yml examples/ existing scripts +``` + +The canonical `extra` CLI (present when `agent-engine` is installed): + +```bash +agentctl validate agents.yml # offline: schema, refs, prompt paths, hooks +agentctl inspect agents.yml # summary of nodes, MCPs, hooks, tags +agentctl generate agents.yml # create plugin stubs (safe to re-run) +agentctl run --config agents.yml --message "..." # one message through the engine +agentctl serve --config agents.yml # stateless HTTP API (port 8080) +``` + +Docker equivalents (when Python/`agentctl` is unavailable): + +```bash +docker run --rm -v "$(pwd):/workspace" -w /workspace \ + ghcr.io/asaf-prog/extra:latest validate agents.yml +docker run --rm -v "$(pwd):/workspace" -w /workspace \ + ghcr.io/asaf-prog/extra:latest generate agents.yml +docker run -p 8080:8080 -v "$(pwd):/workspace" -w /workspace \ + -e ANTHROPIC_API_KEY=... ghcr.io/asaf-prog/extra:latest serve --config agents.yml +``` + +Only use a command form the project actually supports (installed CLI vs. +Docker). For dependency install / test / lint / typecheck / build, use whatever +the project declares (`npm test`, `pnpm lint`, `make check`, `pytest`, etc.) — +do not assume they exist. + +## 8. Generate and validate + +1. If tools/resolvers are declared, run the real generation command + (`agentctl generate agents.yml` or the Docker form). `generate` is safe to + re-run — it preserves existing implementations by default. +2. Verify the expected files now exist: `plugins/tools/.py`, + `plugins/resolvers/shared.py`, `plugins/resolvers/.py`, + `plugins/plugins.toml`, and `plugins/access.py` if any node is `protected`. +3. Run offline validation: `agentctl validate agents.yml` (and optionally + `agentctl inspect`). +4. Run any project verification that exists (tests, lint, typecheck, build). + +Rules: only run commands that exist and make sense; never run destructive +commands; do not install global dependencies unless the project docs explicitly +require it; do not make real LLM/network calls just to "test" — `validate` is +offline and is the right check. + +## 9. Final summary + +End with a clean, copy-pasteable summary in this shape: + +``` +Detected: +- ... + +Created: +- ... + +Changed: +- ... + +Commands run: +- ... + +Passed: +- ... + +Failed: +- ... + +Next commands: +- ... + +Common next debugging step: +- ... +``` + +Fill in `Next commands` with the exact commands the user should run next +(generate / validate / serve), and `Common next debugging step` with the single +most likely follow-up from [troubleshooting.md](references/troubleshooting.md) +given what you saw. diff --git a/skills/extra-setup/assets/minimal-agents.yml b/skills/extra-setup/assets/minimal-agents.yml new file mode 100644 index 0000000..84734a7 --- /dev/null +++ b/skills/extra-setup/assets/minimal-agents.yml @@ -0,0 +1,78 @@ +# Minimal, valid extra `agents.yml` template. +# +# This is the smallest useful shape: one orchestrator that routes, and two +# focused leaf agents. Adapt the ids, descriptions, and prompt paths to your +# domain, then run `agentctl generate agents.yml` and `agentctl validate +# agents.yml`. +# +# Editor autocomplete (optional): point this at the extra JSON schema, e.g. +# # yaml-language-server: $schema=path/to/config.schema.json +# +# Rules to keep it valid: +# - `system.name` and `graph` are required; `graph` must have ONE root. +# - Every id used in `graph` / `tools` / `mcps` / `resolvers` must be +# declared in the matching top-level section. +# - Every orchestrator MUST have a `prompts.orchestrator` file. +# - Secrets, tokens, and private local paths must NEVER appear in this file. + +system: + name: "Example Assistant" + +defaults: + model: + provider: anthropic # supported: anthropic | bedrock + name: claude-haiku-4-5 # use a current model id for your provider + temperature: 0.0 + +# --- Optional: remote MCP servers (URL-based only) ------------------------- +# mcps: +# my_mcp: +# url: "https://mcp.example.com/mcp" # TODO: replace with a real URL + +# --- Optional: Python tools the LLM may call ------------------------------- +# Implemented in plugins/tools/.py after `agentctl generate`. +# tools: +# lookup_record: +# description: "Look up a record by id and return its current status." + +# --- Optional: prompt-variable resolvers ----------------------------------- +# `shared` -> generated on SharedResolver (inherited by all agents). +# `agent` -> generated on the declaring agent's Resolver subclass (default). +resolvers: + current_date: + scope: shared + +orchestrators: + main_router: + name: "Main Router" + description: > + Entry point. Reads the user's request and routes it to the agent whose + description best matches. + prompts: + orchestrator: prompts/main_router/orchestrator.md + system: prompts/main_router/system.md + +agents: + general_agent: + name: "General Assistant" + description: > + Answers general questions and handles requests that do not need a + specialized agent. + prompts: + system: prompts/general_agent/system.md + resolvers: + - current_date + + faq_agent: + name: "FAQ Assistant" + description: > + Answers frequently asked questions about the product using its own + focused prompt. + prompts: + system: prompts/faq_agent/system.md + +# Topology: indentation is the graph. One root; children are routing targets. +graph: + main_router: + general_agent: + faq_agent: diff --git a/skills/extra-setup/references/agents-yml-guide.md b/skills/extra-setup/references/agents-yml-guide.md new file mode 100644 index 0000000..69f3868 --- /dev/null +++ b/skills/extra-setup/references/agents-yml-guide.md @@ -0,0 +1,216 @@ +# `agents.yml` Guide + +The exact structure of an `extra` spec, with examples, common mistakes, and a +validation checklist. Field names and rules match the `extra` JSON schema and +validator. + +## Top-level keys + +| Key | Required | Purpose | +| --------------- | -------- | ------- | +| `system` | yes | System metadata; `system.name` is required. | +| `defaults` | no | System-wide defaults, currently `defaults.model`. | +| `execution` | no | Runtime limits (max iterations / tool calls / child calls). | +| `mcps` | no | URL-based MCP server declarations, keyed by id. | +| `tools` | no | Python plugin tools exposed to LLM agents. | +| `resolvers` | no | Prompt-variable resolvers, keyed by id, with a `scope`. | +| `orchestrators` | no | Router nodes. | +| `agents` | no | Executor nodes. | +| `graph` | yes | Runtime topology; exactly one root. | +| `hooks` | no | Trusted runtime hooks (not exposed to the LLM). | +| `plugins` | no | Plugin loading config (`plugins.import_roots`). | + +Unknown top-level keys are rejected. Secrets must never appear anywhere in the +file. + +## Node fields + +**Orchestrator** (`orchestrators.`): `description` (required), +`prompts.orchestrator` (required), optional `name`, `model`, `resolvers`, +`protected`, `prompts.system`, `prompts.user`. + +**Agent** (`agents.`): `description` (required), optional `name`, `model`, +`resolvers`, `tools`, `mcps`, `protected`, `prompts.system`, `prompts.user`. + +`model` overrides are **full replacement**, not a merge: if a node declares +`model`, it must include all required model fields (`provider`, `name`). + +## Minimal example + +```yaml +system: + name: "Support Bot" + +defaults: + model: + provider: anthropic + name: claude-haiku-4-5 + temperature: 0.0 + +orchestrators: + router: + description: "Routes the user to the right department." + prompts: + orchestrator: prompts/router/orchestrator.md + +agents: + orders_agent: + description: "Handles order status and tracking questions." + prompts: + system: prompts/orders/system.md + + returns_agent: + description: "Handles return requests and refunds." + prompts: + system: prompts/returns/system.md + +graph: + router: + orders_agent: + returns_agent: +``` + +## Multi-agent example (nested routers) + +Orchestrators can route to other orchestrators, forming a DAG: + +```yaml +graph: + research_router: + knowledge_router: + repository_agent: + documentation_agent: + analysis_router: + comparison_agent: + learning_planner_agent: +``` + +Each intermediate router needs its own `prompts.orchestrator`, and every id here +must be declared under `orchestrators` or `agents`. + +## MCP + tool example + +```yaml +mcps: + deepwiki: + url: "https://mcp.deepwiki.com/mcp" + docs_platform: + url: "https://mcp.company.com/mcp" + tool_tags: ["policies"] # optional discovery selector + +tools: + generate_decision_matrix: + description: "Generate a structured comparison matrix between technologies." + +agents: + repository_agent: + description: "Explains repository architecture and source organization." + prompts: + system: prompts/repository_agent/system.md + mcps: [deepwiki] + + comparison_agent: + description: "Produces objective technology comparisons." + prompts: + system: prompts/comparison_agent/system.md + tools: [generate_decision_matrix] +``` + +MCPs are URL-based only — no stdio/local MCP servers. Tools get a stub at +`plugins/tools/.py` from `agentctl generate`. + +## Resolver example + +```yaml +resolvers: + current_date: + scope: shared # on SharedResolver, inherited by all agents + experience_level: + scope: agent # on the declaring agent's Resolver subclass + +agents: + learning_planner_agent: + description: "Produces personalized learning roadmaps." + prompts: + system: prompts/learning_planner_agent/system.md + resolvers: [current_date, experience_level] +``` + +A legacy array form (`resolvers: [current_date]`) is accepted as agent-scoped +shorthand, but prefer the object form with explicit `scope`. + +## Access control example + +```yaml +agents: + admin_agent: + description: "Sensitive administrative operations." + protected: true + prompts: + system: prompts/admin/system.md +``` + +Any `protected: true` node requires `plugins/access.py`: + +```python +class AccessResolver: + def can_access(self, ctx: dict, node_id: str) -> bool: + ... +``` + +## Model configuration + +```yaml +# Anthropic +model: + provider: anthropic + name: claude-haiku-4-5 + temperature: 0.0 + +# Amazon Bedrock (Claude models) +model: + provider: bedrock + name: anthropic.claude-3-5-haiku-20241022-v1:0 + region: us-east-1 + temperature: 0.0 +``` + +Supported providers: `anthropic`, `bedrock`. Credentials come from environment +/ the AWS credential chain — never from YAML. + +## Common mistakes + +- **Missing root / multiple roots in `graph`.** There must be exactly one + top-level key. +- **Referencing an undeclared id.** Every id in `graph`, `tools`, `mcps`, + `resolvers` must exist in the matching top-level section. +- **Orchestrator without `prompts.orchestrator`.** It is mandatory for every + orchestrator. +- **Partial `model` override.** A node `model` fully replaces the default; + include `provider` and `name`. +- **`protected: true` without `plugins/access.py`.** Startup error. +- **Secrets in YAML.** Put credentials in env / secret manager, read them in + plugin code. +- **Local/stdio MCP.** Not supported — MCPs must be a `url`. +- **Prompt path that doesn't exist.** `validate` checks prompt file paths; + create the referenced files. +- **Node declared but not in `graph`.** It is unreachable at runtime. +- **Giving an orchestrator `tools`/`mcps`.** Orchestrators route only; put + tools/MCPs on agents. + +## Validation checklist + +Before running the engine, confirm: + +- [ ] `system.name` is present. +- [ ] `graph` has exactly one root and every key is a declared node. +- [ ] Every orchestrator has `description` + `prompts.orchestrator`. +- [ ] Every agent has a `description`. +- [ ] All `tools` / `mcps` / `resolvers` references are declared top-level. +- [ ] Every referenced prompt file exists under `prompts/`. +- [ ] Each resolver has a valid `scope` (`shared` or `agent`). +- [ ] Each MCP has a real `url`. +- [ ] Any node-level `model` includes `provider` and `name`. +- [ ] Any `protected` node has `plugins/access.py`. +- [ ] No secrets or private paths anywhere. +- [ ] `agentctl validate agents.yml` exits 0. diff --git a/skills/extra-setup/references/extra-concepts.md b/skills/extra-setup/references/extra-concepts.md new file mode 100644 index 0000000..943f553 --- /dev/null +++ b/skills/extra-setup/references/extra-concepts.md @@ -0,0 +1,155 @@ +# extra — Concepts + +Practical reference for the `extra` agent framework, written for setting up and +repairing real projects. It reflects the `extra` schema and CLI, not generic +agent theory. + +## What `extra` is + +`extra` is a lightweight engine that adds an agentic layer to an application +from a single declarative YAML file. You describe your agents — what each is +responsible for and what it can access — and `extra` compiles that into a +running system that routes each request to the right agent and answers it. + +- The CLI is **`agentctl`** (installed via `pip install agent-engine`), also + published as the Docker image **`ghcr.io/asaf-prog/extra:latest`**. +- The engine is **stateless with respect to conversation**: an upstream app + owns sessions/memory and invokes the engine with a full message list. +- The runtime never executes raw YAML. The spec is validated into typed models, + compiled into a graph, and requests are run against that graph. + +## `agents.yml` + +The single spec file (commonly `agents.yml`; the flagship example uses +`agents.yaml`). It has two conceptual halves: + +1. **Flat declarations** — what exists: `mcps`, `tools`, `resolvers`, + `orchestrators`, `agents`. +2. **`graph` topology** — how those nodes connect at runtime, expressed purely + by indentation. + +Top-level keys: `system` (required), `defaults`, `execution`, `mcps`, `tools`, +`resolvers`, `orchestrators`, `agents`, `graph` (required), `hooks`, `plugins`. +Secrets must never appear in the YAML. + +## Nodes: orchestrators and agents + +There are two node types. They are separate for clarity even though the compiler +stores them in one internal model. + +**Orchestrators** are routers. They choose among their children using the +children's `description` fields plus the orchestrator prompt. They do **not** +own tools or MCP servers — their children in the graph are their capabilities. +Every orchestrator must declare `prompts.orchestrator`. + +**Agents** are executors. They run an LLM with prompt files and may call tools +and MCP servers. Agents are normally leaves in the graph. Every agent needs a +`description` (used by its parent as the routing criterion); prompts are +optional but recommended. + +## Child agents / the graph + +`graph` is a nested mapping where indentation is the topology. The single +top-level key is the root entrypoint. A node's nested children are the nodes it +can route to; a node with no children is a leaf. + +Semantic rules: + +- exactly one root entrypoint, +- every graph id is declared under `orchestrators` or `agents`, +- orchestrators may have children; agents are normally leaves, +- a node id may appear in multiple places to model a DAG (reachable from several + parents), but cycles are rejected. + +## MCPs + +MCP servers are **remote and URL-based**. Under `mcps:` each entry declares a +`url`; the engine creates a remote MCP client per URL at build time and +discovers that server's tools automatically. You do not implement MCP clients, +and stdio/local-process MCP servers are not part of the schema. + +A server may optionally declare `tool_tags` (a discovery selector sent as the +`X-MCP-Tool-Tag` header by default) and an advanced `tool_tag_transport` +override. Absent tags change nothing. + +## Tools + +Tools are Python functions the LLM may call during execution. Declare each under +`tools:` with a `description` (shown to the LLM). The implementation lives in +`plugins/tools/.py`, created as a stub by `agentctl generate`. An agent gets +a tool by listing its id under the agent's `tools: [...]`. + +## Resolvers + +Resolvers fill `{{variables}}` in prompt files **before** a node runs. They are +chosen by the engine (not the LLM), are never exposed to the LLM, and cost no +tokens — the opposite trust boundary from tools. + +Each resolver has a **scope**: + +- `shared` — generated once on `SharedResolver` in + `plugins/resolvers/shared.py`, inherited by all agents; +- `agent` (default) — generated on the declaring agent's `Resolver` subclass in + `plugins/resolvers/.py`. + +Resolver methods receive `ctx`, which the engine builds from request headers and +data. Auth/access/user/organization context stays inside plugin code and is +never surfaced to the LLM. + +## Hooks and access control + +**Hooks** are trusted runtime callbacks (e.g. `on_engine_start`, +`before_mcp_request`, `after_tool_call`, `transform_tool_result`, +`on_run_error`). They are **not** tools and are never exposed to the LLM. Hook +entries use either an explicit `ref` (`module.path:function`) or a managed +`plugin` + `method` resolved through `plugins/plugins.toml`. + +**Access control** is opt-in per node via `protected: true`. If any node is +protected, the engine expects `plugins/access.py` with `class AccessResolver` +implementing `can_access(self, ctx, node_id) -> bool`. Protected nodes are +checked before routing; denied nodes are hidden from the router entirely +(fail-closed). `protected: true` without the access plugin is a startup error. + +## Generation + +`agentctl generate agents.yml` reads the spec and creates plugin stubs — it does +not write business logic. It generates: + +- `plugins/resolvers/shared.py` — `SharedResolver` with shared methods, +- `plugins/resolvers/.py` — per-agent `Resolver` subclass, +- `plugins/tools/.py` — tool stubs (`raise NotImplementedError`), +- `plugins/hooks/.py` — hook stubs, +- `plugins/plugins.toml` — the plugin manifest. + +It is safe to re-run: existing implementations are preserved by default. Use +`--force` to overwrite, or `--mode child --agent ` to scope regeneration. + +## How config relates to generated code and runtime + +1. You **declare** ids and topology in `agents.yml`. +2. `agentctl generate` turns declarations into **stub files** under `plugins/`. +3. You **fill in** the stubs with your business logic (tools, resolvers, hooks, + access policy). +4. At runtime the engine validates the spec, compiles the graph, resolves + `{{variables}}` via resolvers, routes each request through orchestrators to + the matching agent, and lets that agent call its bound tools/MCPs. + +`agentctl validate` checks all of step 1 offline (schema, references, prompt +paths, hook imports) — run it after any edit. + +## Runtime execution (roughly) + +A request is a full conversation posted to the engine: + +```http +POST /invoke +Content-Type: application/json + +{ "messages": [{ "role": "user", "content": "Where is my order?" }] } +``` + +The root orchestrator routes by matching the message against child +`description`s (respecting `protected` access checks), the chosen agent renders +its prompts (with resolver values), optionally calls tools/MCPs, and returns a +grounded response. `execution` limits (max iterations, tool calls, child agent +calls) bound the run. diff --git a/skills/extra-setup/references/troubleshooting.md b/skills/extra-setup/references/troubleshooting.md new file mode 100644 index 0000000..3a87672 --- /dev/null +++ b/skills/extra-setup/references/troubleshooting.md @@ -0,0 +1,199 @@ +# extra — Troubleshooting + +Specific failures you hit while setting up or running an `extra` project, and +how to fix them. Always start with the offline check: + +```bash +agentctl validate agents.yml # or the ghcr.io/asaf-prog/extra Docker form +``` + +`validate` checks schema, references, prompt paths, and hook imports without any +LLM or network calls. Fix everything it reports before running `serve`/`run`. + +--- + +## No default agent / no root found + +**Cause:** `graph` has zero or more than one top-level key, or the root key is +not a declared node. + +**Fix:** `graph` must have exactly one top-level entry (the entrypoint), usually +an orchestrator. Ensure that key is declared under `orchestrators` (or +`agents`). Everything else nests beneath it by indentation. + +```yaml +graph: + main_router: # single root + orders_agent: + returns_agent: +``` + +--- + +## Undefined agent (graph references an unknown node) + +**Cause:** A key in `graph` has no matching declaration under `orchestrators` or +`agents` (often a typo or a renamed id). + +**Fix:** Make the id in `graph` match the declaration exactly, or add the +missing `agents.` / `orchestrators.` block. Ids are case-sensitive. + +--- + +## Undefined tool + +**Cause:** An agent lists `tools: [foo]` but `foo` is not under top-level +`tools:`, or the stub `plugins/tools/foo.py` was never generated. + +**Fix:** Declare the tool with a `description` under `tools:`, then run +`agentctl generate agents.yml` to create `plugins/tools/foo.py`, and implement +it (replace `raise NotImplementedError`). Remove the reference if the tool is +not needed. + +--- + +## Undefined MCP + +**Cause:** An agent lists `mcps: [bar]` but `bar` is not declared under +`mcps:`, or the entry has no `url`. + +**Fix:** Add the MCP with a real `url`: + +```yaml +mcps: + bar: + url: "https://mcp.example.com/mcp" +``` + +MCPs are URL-based only. If you don't yet have a URL, mark it clearly as a +`TODO` and do not run against it until it's real. + +--- + +## Undefined resolver + +**Cause:** A node lists `resolvers: [baz]` but `baz` is not declared under +`resolvers:`, or its generated method is missing. + +**Fix:** Declare it with a scope, regenerate, and implement the method: + +```yaml +resolvers: + baz: + scope: shared # or: agent +``` + +`shared` → method on `SharedResolver` in `plugins/resolvers/shared.py`; +`agent` → method on the agent's `Resolver` in `plugins/resolvers/.py`. + +--- + +## Invalid resolver scope + +**Cause:** A resolver's `scope` is something other than `shared` or `agent`. + +**Fix:** Use exactly `shared` or `agent`. Omitting scope defaults to `agent`. +`shared` resolvers are inherited by all agents; `agent` resolvers exist only on +the declaring agent. + +--- + +## Generation command failed + +**Causes & fixes:** + +- **`agentctl: command not found`** — the CLI isn't installed. Either + `pip install agent-engine` (Python 3.11+) or use the Docker form: + `docker run --rm -v "$(pwd):/workspace" -w /workspace ghcr.io/asaf-prog/extra:latest generate agents.yml`. +- **Spec invalid** — `generate` reads the spec; fix `agentctl validate` errors + first. +- **Overwrote my code?** — it won't by default; existing implementations are + preserved. Only `--force` overwrites. Use `--mode child --agent ` to + scope regeneration. + +--- + +## Docker run failed + +**Causes & fixes:** + +- **`agents.yml` not found in container** — mount the workdir and set it: + `-v "$(pwd):/workspace" -w /workspace`, then reference `agents.yml` + (relative to `/workspace`). +- **Auth errors at runtime** — pass the provider key: `-e ANTHROPIC_API_KEY=...` + (or AWS creds for Bedrock). `validate`/`generate` don't need keys; `serve`/ + `run` do. +- **Port already in use / can't reach it** — publish the port (`-p 8080:8080` + for `serve`, `-p 8100:8100` for `agent-manager`) and curl the mapped port. +- **Permission errors writing `plugins/`** — the container writes into the + mounted workdir; ensure it's writable by your user. + +--- + +## Agent routes to the wrong child + +**Cause:** Two children have overlapping or vague `description`s, so the router +can't tell them apart; or the orchestrator prompt doesn't explain the choice. + +**Fix:** Make each child `description` specific and mutually distinct — it is the +routing criterion. Tighten `prompts//orchestrator.md` to state which +child handles what and when to clarify. Use `agentctl inspect agents.yml` to +review the routing surface. + +--- + +## Access resolver denies a node + +**Cause:** A `protected: true` node is hidden from routing because +`AccessResolver.can_access(ctx, node_id)` returned `False` (access fails +closed), or `plugins/access.py` is missing entirely (startup error). + +**Fix:** Ensure `plugins/access.py` exists with `class AccessResolver` and +`can_access(self, ctx, node_id) -> bool`. For local dev it may `return True`; +in production inspect `ctx` (user role/tier from request headers) and decide per +`node_id`. Confirm the caller sends whatever identity the resolver reads. + +--- + +## Context missing (`{{variable}}` not filled) + +**Cause:** A prompt uses `{{var}}` but no resolver named `var` is bound to that +node, or the resolver returns empty because its expected `ctx`/env input is +absent. + +**Fix:** Declare a resolver `var` with a scope and list it in the node's +`resolvers: [...]`. Ensure the resolver reads the right source (request header +via `ctx`, or an env var) and that the caller/environment provides it. Shared +values (e.g. `current_date`) belong in `scope: shared`. + +--- + +## Tool is not available to the expected agent + +**Cause:** The tool is declared but not bound to that agent, or it was bound to +a different agent, or it was placed on an orchestrator (which cannot hold +tools). + +**Fix:** Add the tool id to the correct **agent's** `tools: [...]`. Orchestrators +route only — move any tools/MCPs down to leaf agents. Verify with `agentctl +inspect agents.yml`. + +--- + +## Tests pass but runtime fails + +**Causes & fixes:** + +- **Unimplemented stubs** — generated tools/resolvers still `raise + NotImplementedError`. Implement them; `validate` and unit tests may pass + without exercising the actual call path. +- **Missing runtime env** — provider keys (`ANTHROPIC_API_KEY` / AWS creds) or + MCP auth headers (added in a `before_mcp_request` hook) are only needed at + run time, not during validation. +- **Unreachable MCP URL** — the URL resolves offline as a string but the server + is down or wrong at runtime. Curl it independently. +- **Access policy** — a `protected` node that your tests allow may be denied in + the real environment; check the identity the caller sends. +- **Execution limits** — a run can stop early on `execution.max_iterations` / + `max_tool_calls` / `max_child_agent_calls`. Raise them if a legitimate flow + needs more steps. From 0c6e7f8cd9863e156467bd81fecd62ec573dfe65 Mon Sep 17 00:00:00 2001 From: Asaf Varon Date: Fri, 10 Jul 2026 13:04:56 +0300 Subject: [PATCH 2/2] Align extra setup skill with official docs Co-Authored-By: Claude Opus 4.8 --- README.md | 4 ++ scripts/validate-skills.mjs | 26 ++++++++++++ skills/extra-setup/SKILL.md | 40 ++++++++++++++++--- skills/extra-setup/assets/minimal-agents.yml | 3 ++ .../references/agents-yml-guide.md | 4 ++ .../extra-setup/references/extra-concepts.md | 5 +++ .../extra-setup/references/troubleshooting.md | 4 +- 7 files changed, 80 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 5b18167..3352b0a 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ extra-skills = Claude Code Agent Skills for setup, debugging, and onboarding This repo does **not** contain the `extra` source — only skills, their reference docs and templates, a validation script, and CI. +> **Official documentation:** +> Go there for conceptual documentation (architecture, YAML reference, CLI, +> plugins). Use `/extra-setup` in Claude Code for hands-on project setup. + ## Install List the skills in this repo: diff --git a/scripts/validate-skills.mjs b/scripts/validate-skills.mjs index affdc5c..07e87cd 100644 --- a/scripts/validate-skills.mjs +++ b/scripts/validate-skills.mjs @@ -14,6 +14,8 @@ // 7. Skill names are safe lowercase kebab-case. // 8. Markdown links to reference files resolve to existing files. // 9. No obvious private paths or secrets are committed. +// 10. README and each SKILL.md link the official docs; README carries the +// canonical `extra-org/extra-skills` install slug. // // Exits non-zero on any error so it can gate CI. @@ -30,6 +32,10 @@ const skillsChecked = []; const KEBAB_CASE = /^[a-z0-9]+(-[a-z0-9]+)*$/; +// The official public docs must be discoverable from the README and every +// skill should point Claude Code at them as the public source of truth. +const OFFICIAL_DOCS = "https://extra-c586718a.mintlify.site/docs"; + // Patterns that must never appear in committed skill content. const SECRET_PATTERNS = [ { label: "macOS/Linux home path", re: /\/Users\/[A-Za-z0-9._-]+\//g }, @@ -108,6 +114,21 @@ function main() { return report(); } + // README must link the official docs and use the canonical install slug. + const readmePath = join(ROOT, "README.md"); + if (!existsSync(readmePath)) { + fail("README.md is missing"); + } else { + const readme = readFileSync(readmePath, "utf8"); + if (!readme.includes(OFFICIAL_DOCS)) { + fail(`README.md: does not link the official docs (${OFFICIAL_DOCS}...)`); + } + if (!readme.includes("extra-org/extra-skills")) { + fail("README.md: missing the 'extra-org/extra-skills' install slug"); + } + scanForSecrets(relative(ROOT, readmePath), readme); + } + const entries = readdirSync(SKILLS_DIR, { withFileTypes: true }).filter((e) => e.isDirectory() ); @@ -144,6 +165,11 @@ function main() { ); } + // Each skill should reference the official docs as the public source of truth. + if (!content.includes(OFFICIAL_DOCS)) { + fail(`${relSkillMd}: does not link the official docs (${OFFICIAL_DOCS}...)`); + } + if (!KEBAB_CASE.test(skillName)) { fail(`skills/${skillName}: folder name is not safe lowercase kebab-case`); } diff --git a/skills/extra-setup/SKILL.md b/skills/extra-setup/SKILL.md index 8bcf1a5..fafef85 100644 --- a/skills/extra-setup/SKILL.md +++ b/skills/extra-setup/SKILL.md @@ -17,6 +17,30 @@ verify it with the real commands the project supports. Be conservative: never guess commands, never invent schema fields, and never delete working configuration. +## Sources of truth (in priority order) + +1. **The current project** — inspect it first; its existing files and intent win. +2. **The official public docs** — the user-facing source of truth for concepts, + naming, CLI, Docker, and the config filename: + + (quickstart, `yaml-spec`, `cli`, `mcp-and-tools`, `plugins`, `architecture`). +3. **The local `extra` repo / JSON schema** (when present) — the implementation + source of truth for exact field names and validator behavior. + +Operating rules, always: + +- **Never invent config fields.** Use only fields documented in the public docs + or the JSON schema. If unsure, prefer the documented form or leave it out. +- **Prefer the documented CLI** (`agentctl`) and its documented subcommands. +- **Prefer the documented Docker image** (`ghcr.io/asaf-prog/extra:latest`). +- **Prefer the documented config filename** `agents.yml` for new files (the docs + use `agents.yml`; some repo examples use `agents.yaml` — accept an existing + one, but create new specs as `agents.yml`). +- **Preserve user intent** when repairing — fix, don't rewrite. +- **Discover real commands before running them** (§7); run nothing you can't + confirm the project supports. +- **Summarize exactly what changed** at the end (§9). + Read the reference files as needed: - [extra-concepts.md](references/extra-concepts.md) — what `extra`, agents, @@ -42,11 +66,12 @@ pyproject.toml requirements.txt Dockerfile docker-compose.yml Makefile examples/ tests/ src/ prompts/ plugins/ ``` -> Note: `extra` specs are commonly named `agents.yml` **or** `agents.yaml`, and -> the flagship example uses `agents.yaml`. Check both. `extra` does not use a -> `resolvers.toml` — resolver metadata lives in `plugins/plugins.toml` and -> resolver code lives in `plugins/resolvers/`. If you find a `resolvers.toml`, -> treat it as project-specific and do not assume `extra` reads it. +> Note: the public docs use **`agents.yml`** as the config filename; some repo +> examples use `agents.yaml`. Check both and accept whichever exists; create new +> specs as `agents.yml`. `extra` does not use a `resolvers.toml` — resolver +> metadata lives in `plugins/plugins.toml` and resolver code lives in +> `plugins/resolvers/`. If you find a `resolvers.toml`, treat it as +> project-specific and do not assume `extra` reads it. Then summarize concisely: @@ -78,6 +103,11 @@ If no argument is given: impossible to infer, - otherwise proceed with a safe minimal demo (one orchestrator + one agent). +Before writing config, ground yourself in the current documented schema and +naming from the public docs (`yaml-spec`, `cli`, `mcp-and-tools`, `plugins`) so +every field you emit is one `extra` actually supports: + + ## 3. Create or repair `agents.yml` **If `agents.yml` / `agents.yaml` does not exist**, create one: diff --git a/skills/extra-setup/assets/minimal-agents.yml b/skills/extra-setup/assets/minimal-agents.yml index 84734a7..8433ce3 100644 --- a/skills/extra-setup/assets/minimal-agents.yml +++ b/skills/extra-setup/assets/minimal-agents.yml @@ -1,5 +1,8 @@ # Minimal, valid extra `agents.yml` template. # +# Save new specs as `agents.yml` (the filename the official docs use). +# Docs / YAML reference: https://extra-c586718a.mintlify.site/docs/yaml-spec +# # This is the smallest useful shape: one orchestrator that routes, and two # focused leaf agents. Adapt the ids, descriptions, and prompt paths to your # domain, then run `agentctl generate agents.yml` and `agentctl validate diff --git a/skills/extra-setup/references/agents-yml-guide.md b/skills/extra-setup/references/agents-yml-guide.md index 69f3868..d36cb16 100644 --- a/skills/extra-setup/references/agents-yml-guide.md +++ b/skills/extra-setup/references/agents-yml-guide.md @@ -4,6 +4,10 @@ The exact structure of an `extra` spec, with examples, common mistakes, and a validation checklist. Field names and rules match the `extra` JSON schema and validator. +> Public source of truth: the YAML reference at +> . The docs use the +> filename **`agents.yml`**; create new specs with that name. + ## Top-level keys | Key | Required | Purpose | diff --git a/skills/extra-setup/references/extra-concepts.md b/skills/extra-setup/references/extra-concepts.md index 943f553..0a99614 100644 --- a/skills/extra-setup/references/extra-concepts.md +++ b/skills/extra-setup/references/extra-concepts.md @@ -4,6 +4,11 @@ Practical reference for the `extra` agent framework, written for setting up and repairing real projects. It reflects the `extra` schema and CLI, not generic agent theory. +> Public source of truth: the official docs at +> +> (see `introduction`, `architecture`, `yaml-spec`, `plugins`). This file +> summarizes them for setup work; when in doubt, defer to the live docs. + ## What `extra` is `extra` is a lightweight engine that adds an agentic layer to an application diff --git a/skills/extra-setup/references/troubleshooting.md b/skills/extra-setup/references/troubleshooting.md index 3a87672..2e1da27 100644 --- a/skills/extra-setup/references/troubleshooting.md +++ b/skills/extra-setup/references/troubleshooting.md @@ -1,7 +1,9 @@ # extra — Troubleshooting Specific failures you hit while setting up or running an `extra` project, and -how to fix them. Always start with the offline check: +how to fix them. For anything not covered here, consult the official docs: + (see `cli`, +`yaml-spec`, `plugins`). Always start with the offline check: ```bash agentctl validate agents.yml # or the ghcr.io/asaf-prog/extra Docker form