// Writing skills — deep dive
function SkillsGuide() {
  const shell = useShell();
  const tocItems = [
    { id: 'what', label: 'What a skill is' },
    { id: 'anatomy', label: 'Anatomy of SKILL.md' },
    { id: 'frontmatter', label: 'Front-matter', level: 3 },
    { id: 'body', label: 'Body', level: 3 },
    { id: 'discovery', label: 'How the agent discovers skills' },
    { id: 'disclosure', label: 'Progressive disclosure' },
    { id: 'examples', label: 'Examples' },
    { id: 'testing', label: 'Testing a skill' },
    { id: 'versioning', label: 'Versioning & edits' },
    { id: 'best', label: 'Best practices' },
  ];

  return (
    <div className="app">
      <Topbar section="docs" theme={shell.theme} setTheme={shell.setTheme}
              onSearch={() => shell.setSearchOpen(true)}
              onMenuToggle={() => shell.setMobileMenuOpen(true)} />
      <div className="main">
        <Sidebar activeId="skills-guide"
                 mobileOpen={shell.mobileMenuOpen}
                 onMobileClose={() => shell.setMobileMenuOpen(false)} />
        <article className="content">
          <div className="crumbs">
            <a href="index.html">Docs</a>
            <span className="sep">/</span>
            <a href="#">Concepts</a>
            <span className="sep">/</span>
            <span>Writing skills</span>
          </div>

          <div className="eyebrow">Concepts · ~10 min</div>
          <h1 className="h1">Writing <em>skills.</em></h1>
          <p className="lede">
            A skill is the agent's procedural memory — a Markdown file describing how to do one
            type of task. The agent sees skill names + descriptions on every turn; the full body
            loads only when the skill is invoked. This page covers front-matter fields, body
            conventions, discovery, and how to test.
          </p>

          <h2 id="what" className="h2">What a skill is</h2>
          <p>
            A skill is a directory containing a <code>SKILL.md</code> file. The directory name is
            the skill's stable id; the file's front-matter provides the human-readable name,
            description, and optional trigger phrase. Skills live under the tenant workspace:
          </p>
          <CodeBlock tabs={[{ label: 'layout', raw: `/data/tenants/{id}/skills/\n├── weekly-summary/\n│   ├── SKILL.md\n│   └── templates/\n│       └── weekly-report.md\n├── generate-invoice/\n│   └── SKILL.md\n└── onboard-new-user/\n    ├── SKILL.md\n    └── scripts/\n        └── send-welcome-email.sh`,
            code: `/data/tenants/{id}/skills/
├── weekly-summary/
│   ├── SKILL.md
│   └── templates/
│       └── weekly-report.md
├── generate-invoice/
│   └── SKILL.md
└── onboard-new-user/
    ├── SKILL.md
    └── scripts/
        └── send-welcome-email.sh` }]} />

          <h2 id="anatomy" className="h2">Anatomy of SKILL.md</h2>

          <h3 id="frontmatter" className="h3">Front-matter</h3>
          <CodeBlock tabs={[{ label: 'SKILL.md', raw: `---\nname: weekly-summary\ndescription: Summarize this week's activity across all channels and deliver a report\ntrigger: "summarize my week"\n---`,
            code: `<span class="tok-pun">---</span>
<span class="tok-var">name</span>: weekly-summary
<span class="tok-var">description</span>: Summarize this week's activity across all channels and deliver a report
<span class="tok-var">trigger</span>: <span class="tok-str">"summarize my week"</span>
<span class="tok-pun">---</span>` }]} />
          <div style={{overflow:'auto', margin:'12px 0 28px'}}>
          <table className="doc-table">
            <thead><tr><th>Field</th><th>Required</th><th>Purpose</th></tr></thead>
            <tbody>
              <tr><td><code>name</code></td><td>Yes</td><td>Stable id used by <code>load_skill</code>. Match the directory name. Kebab-case.</td></tr>
              <tr><td><code>description</code></td><td>Yes</td><td>One-line summary shown to the LLM on every turn. Describe <em>when</em> the skill should be used, not just what it does.</td></tr>
              <tr><td><code>trigger</code></td><td>No</td><td>Optional phrase that hints at the invocation pattern ("summarize my week"). Not enforced; purely documentation.</td></tr>
            </tbody>
          </table>
          </div>

          <h3 id="body" className="h3">Body</h3>
          <p>
            Everything after the front-matter is the skill body — free-form Markdown. No required
            structure, but common patterns work well:
          </p>
          <CodeBlock tabs={[{ label: 'SKILL.md', raw: `# Weekly summary\n\nWhen the user asks for a weekly summary:\n\n1. Run \`bash tunder messages list --since 7d --all-channels\` to fetch the last 7 days.\n2. Group by sender. Count unread threads. Flag anything marked urgent.\n3. Render the report via \`bash tunder render --template templates/weekly-report.md --data -\`.\n4. Reply with a one-paragraph summary, then 3–5 bullet action items.\n\n## Notes\n\n- If there are no messages, say so plainly rather than inventing activity.\n- Preserve sender names as given — don't paraphrase "Jamie" into "the engineering lead".`,
            code: `<span class="tok-kw"># Weekly summary</span>

When the user asks for a weekly summary:

1. Run <span class="tok-str">\`bash tunder messages list --since 7d --all-channels\`</span> to fetch the last 7 days.
2. Group by sender. Count unread threads. Flag anything marked urgent.
3. Render the report via <span class="tok-str">\`bash tunder render --template templates/weekly-report.md --data -\`</span>.
4. Reply with a one-paragraph summary, then 3–5 bullet action items.

<span class="tok-kw">## Notes</span>

- If there are no messages, say so plainly rather than inventing activity.
- Preserve sender names as given — don't paraphrase "Jamie" into "the engineering lead".` }]} />

          <h2 id="discovery" className="h2">How the agent discovers skills</h2>
          <ol>
            <li>At session start, the context builder walks <code>skills/*/SKILL.md</code>, parses the front-matter, and appends a one-liner per skill to the system prompt.</li>
            <li>The LLM sees names + descriptions every turn — ~10–20 tokens per skill. With 30+ skills this is still under 1K tokens.</li>
            <li>When the LLM decides to use a skill, it calls <code>load_skill(&#123; name: "weekly-summary" &#125;)</code>. The handler returns the full body as the tool result.</li>
            <li>Subsequent turns in the same session reuse the loaded body from conversation history — no repeat load.</li>
          </ol>

          <h2 id="disclosure" className="h2">Progressive disclosure</h2>
          <Callout type="note" title="Why not just include bodies in the system prompt?">
            30 skills × 800 tokens each = 24K tokens burned on skills the LLM may not use this
            turn. Progressive disclosure keeps the baseline prompt ~1K regardless of skill count
            — bodies load on demand, paid for only when used.
          </Callout>

          <h2 id="examples" className="h2">Examples</h2>
          <CodeBlock tabs={[
            { label: 'daily-briefing', raw: `---\nname: daily-briefing\ndescription: At 08:00, compile yesterday's activity + today's calendar into a briefing and send to the owner\ntrigger: scheduled\n---\n\n# Daily briefing\n\nSteps:\n1. \`bash tunder messages list --since 24h\` — get inbox activity.\n2. \`bash tunder calendar list --date today\` — get today's events.\n3. Summarize: top 3 messages needing reply, any calendar conflicts, suggested first task.\n4. \`message({ channel: owner_preferred_channel, text: "..." })\` — deliver it.`,
              code: `<span class="tok-pun">---</span>
<span class="tok-var">name</span>: daily-briefing
<span class="tok-var">description</span>: At 08:00, compile yesterday's activity + today's calendar into a briefing and send to the owner
<span class="tok-var">trigger</span>: scheduled
<span class="tok-pun">---</span>

<span class="tok-kw"># Daily briefing</span>

Steps:
1. <span class="tok-str">\`bash tunder messages list --since 24h\`</span> — get inbox activity.
2. <span class="tok-str">\`bash tunder calendar list --date today\`</span> — get today's events.
3. Summarize: top 3 messages needing reply, any calendar conflicts, suggested first task.
4. <span class="tok-str">\`message({ channel: owner_preferred_channel, text: "..." })\`</span> — deliver it.` },
            { label: 'lookup-customer', raw: `---\nname: lookup-customer\ndescription: Look up a customer by email or phone and return their recent orders + open tickets\ntrigger: "look up customer"\n---\n\n# Customer lookup\n\nWhen asked about a customer:\n\n1. \`memory_query({ query: "<email or phone>" })\` — check if we have notes on them.\n2. \`bash tunder crm get --by <identifier>\` — fetch from the CRM.\n3. Return: name, last order date, open ticket count, a one-line note.\n\nIf no match: say "no customer on file" rather than guessing.`,
              code: `<span class="tok-pun">---</span>
<span class="tok-var">name</span>: lookup-customer
<span class="tok-var">description</span>: Look up a customer by email or phone and return their recent orders + open tickets
<span class="tok-var">trigger</span>: <span class="tok-str">"look up customer"</span>
<span class="tok-pun">---</span>

<span class="tok-kw"># Customer lookup</span>

When asked about a customer:

1. <span class="tok-str">\`memory_query({ query: "&lt;email or phone&gt;" })\`</span> — check if we have notes on them.
2. <span class="tok-str">\`bash tunder crm get --by &lt;identifier&gt;\`</span> — fetch from the CRM.
3. Return: name, last order date, open ticket count, a one-line note.

If no match: say "no customer on file" rather than guessing.` },
          ]} />

          <h2 id="testing" className="h2">Testing a skill</h2>
          <ol>
            <li>Write the skill file under <code>/data/tenants/&#123;id&#125;/skills/&lt;name&gt;/SKILL.md</code>.</li>
            <li>Send a message that should trigger it — the LLM looks at descriptions, so phrase the message in terms of the task.</li>
            <li>Watch the SSE stream: you should see a <code>tool_call_start</code> for <code>load_skill</code>, then the tool calls the skill recommends.</li>
            <li>If the wrong skill is chosen or none at all, the description is ambiguous — tighten it.</li>
          </ol>

          <h2 id="versioning" className="h2">Versioning & edits</h2>
          <p>
            Skills are plain Markdown — commit them alongside the rest of your repo. The agent
            picks up edits on the next turn (no cache). If you want a safe revert, restore the
            previous version from git. For team-managed skills, review edits the same way you'd
            review config files.
          </p>

          <h2 id="best" className="h2">Best practices</h2>
          <ul>
            <li><strong>Write descriptions from the LLM's perspective.</strong> "Generate a weekly sales report when the user asks for a summary" beats "Weekly sales report generator".</li>
            <li><strong>Number the steps.</strong> The LLM follows ordered lists reliably.</li>
            <li><strong>Name the tools.</strong> Concrete tool calls in the body (<code>bash tunder ...</code>, <code>message(...)</code>) beat abstract descriptions.</li>
            <li><strong>Handle empty states.</strong> "If there are no results, say so" prevents hallucination.</li>
            <li><strong>One skill per outcome.</strong> If a skill covers two unrelated things, split it — each will be easier for the LLM to pick correctly.</li>
            <li><strong>Keep bodies under ~800 tokens.</strong> Longer is fine if it's truly needed, but most skills are instructions, not essays.</li>
          </ul>

          <Feedback />
          <PageFoot
            prev={{ label: 'Providers', href: 'providers.html' }}
            next={{ label: 'Configuring agents', href: 'agents-guide.html' }}
          />
        </article>
        <TOC items={tocItems} />
      </div>
      <SearchOverlay open={shell.searchOpen} onClose={() => shell.setSearchOpen(false)} />
      <TweaksPanel visible={shell.tweaksVisible} theme={shell.theme} setTheme={shell.setTheme} />
    </div>
  );
}
ReactDOM.createRoot(document.getElementById('root')).render(<SkillsGuide />);
