diff --git a/.agents/skills/agentic-ux/SKILL.md b/.agents/skills/agentic-ux/SKILL.md new file mode 100644 index 0000000..0511b65 --- /dev/null +++ b/.agents/skills/agentic-ux/SKILL.md @@ -0,0 +1,148 @@ +--- +name: agentic-ux +description: >- + **WORKFLOW SKILL** - Fully agentic UX where a thin prompt shell renders + tool-linked MCP UI resources. + + USE FOR: fully agentic mode, MCP UI host, dynamic tool UI, agent shell, + sandboxed UI resource. + + DO NOT USE FOR: side panel chat (use promptable-ux), prompt-first layout + (use promptable-ux), schema layers or server boundaries (use + tanstack-promptable-fullstack-app-template), UI kit lookup (use + reference-tech-stack), env parse or logging (use observability-and-env). + + INVOKES: MCP UI host shell, tool-linked UI resources, and companion skills. + + FOR SINGLE OPERATIONS: Load promptable-ux when the app has domain screens; + this skill is the tool-only shell. +license: MIT +metadata: + author: Carlos Martin-Sanchez + version: "0.1.0" + repository: https://github.com/carlosvin/tanstack-fullstack-ai-template +--- + +## Companion skills (install if missing) + +This template publishes **multiple** skills. If only **this** skill is installed, add companions **before** related work: + +- **`tanstack-promptable-fullstack-app-template`** (parent) — Architecture contract: schema layers, loader-first routes, AI tools, and server boundaries. Install for every TanStack app; this skill only places the prompt and renders tool UI. + ```bash + npx skills add carlosvin/tanstack-fullstack-ai-template --skill tanstack-promptable-fullstack-app-template + ``` + +- **`promptable-ux`** (companion) — Shared UX for the route-based concepts (hidden side or bottom panel, and prompt-first). Install when the app has domain screens instead of this tool-only shell. + ```bash + npx skills add carlosvin/tanstack-fullstack-ai-template --skill promptable-ux + ``` + +- **`reference-tech-stack`** (companion) — Opinionated packages for this template, including the MCP UI client and server packages when the shell lands. Install when implementing the host against demo stack defaults. + ```bash + npx skills add carlosvin/tanstack-fullstack-ai-template --skill reference-tech-stack + ``` + +- **`observability-and-env`** (companion) — Env parse, logger factories, and error-tracking bootstrap. Install when shell work also touches `shellSession`, logging, or `process.env`. + ```bash + npx skills add carlosvin/tanstack-fullstack-ai-template --skill observability-and-env + ``` + +Discover all skills: `npx skills add carlosvin/tanstack-fullstack-ai-template --list` + +# Agentic UX + +**Purpose:** One UX contract for fully agentic apps. The product is a thin shell — a prompt and a response surface. The agent receives the full tool set. Views appear only as [MCP UI](https://github.com/MCP-UI-Org/mcp-ui) resources returned by those tools and rendered in a sandboxed iframe. No hand-built domain screens. + +> **Parent skill:** `tanstack-promptable-fullstack-app-template` — schemas, tools, loaders, URL-as-state, `getAIAvailability()`, bounded `chat()`. Do not restate that contract here. +> +> **Other UX skill:** `promptable-ux` — side panel and prompt-first concepts for apps with domain routes. When the app has domain screens, load that skill instead of this one. + +## Skill routing + +| Task | Load | +|------|------| +| Fully agentic mode, MCP UI host, dynamic tool UI, agent shell, sandboxed UI resource | **This skill** | +| Side panel chat, prompt-first layout, domain screens | **`promptable-ux`** | +| Schemas, routes, AI tools, server boundaries, availability gate | **`tanstack-promptable-fullstack-app-template`** | +| Which UI library or MCP UI package this template uses | **`reference-tech-stack`** | +| Env, logging, error tracking | **`observability-and-env`** | + +## How to use this skill + +1. Declare the mode. One mode per deployment. This shell never mounts beside a drawer, a prompt-first bar, an overview, or drill-down routes. +2. Reuse none of the route-based components. No `ChatDrawer`, `PromptBar`, `AppNavbar`, task pages, or navigation manifest. The shell is identity plus prompt plus thread. Everything visual beyond text comes from a tool resource. +3. Implement the **Shell**, **Tool surface**, **MCP UI rendering**, **UI actions**, and **Security** sections below. +4. Run the **UX checklist** before a shell change. +5. Keep schemas, tools, and server boundaries on the architecture skill. This skill changes composition and rendering, not the data model. + +## Shell + +- One root layout per deployment. Conversation state lives there so scrolling never wipes the thread. Layout: app bar with identity only, conversation stage as the main surface, bottom-pinned composer (footer) that stays reachable regardless of thread length. +- When empty, the stage renders a clean hero state with welcome text and suggested starter chips. As the conversation progresses it shows the scrollable thread. Response order within the thread: markdown text, tool status, then the tool-linked UI resource. Text-only results stay markdown with GFM tables, code, and links. +- Prompt behavior: Enter sends, Shift+Enter inserts a newline, stop while generating, clear once messages exist. Tool activity shows as short status labels. Errors show in an alert. Suggested prompts may come from the single help document (`docs/help.md`). +- This matches the prompt-first bottom-composer shape from `promptable-ux` (see `AppShell.Footer`, `ChatThread`, `ChatComposer`) but without its route surfaces: no `AppNavbar`, no drill-down routes, no stacked dashboard, no navigation manifest. The composer and thread patterns transfer; the route surfaces do not. +- Mobile first (default): usable at the narrowest width with the composer pinned and responses scrolling, then richer spacing as the viewport grows. Both color schemes work with theme tokens. Keep one icon library. +- Unconfigured AI: `getAIAvailability()` gates the prompt and there is no domain UI behind it, so render an empty configuration state explaining that AI is not configured. No disabled prompt. + +## Tool surface + +- Expose every repository method as a server AI tool via `createSafeServerTool`, including distinct-values tools. The agent receives that full set on every turn. +- Do not register `navigate` or `invalidateRouter`. There are no in-app routes to open or refresh. After a write, the host re-renders from the new tool result. +- `browserContext` carries timezone, locale, and current time plus a marker that this surface is the agent shell. It does not carry a current path or search. There is one route, so nothing resolves "this item" from a URL. Reuse the `captureBrowserContext()` shape and drop the location fields. +- Every `chat()` call still sets `agentLoopStrategy: maxIterations(N)` explicitly (default `N=10`). + +## MCP UI rendering + +- A tool that needs a view links it with `_meta.ui.resourceUri` (`ui://…`). The host reads the resource and renders it with `@mcp-ui/client` `AppRenderer`. MIME type is `text/html;profile=mcp-app`. The resource is built with `createUIResource` from the tool result. +- v1 scope is `rawHtml` (plus `externalUrl` where needed). Defer `remote-dom`, component libraries, and host adapters. +- Legacy `UIResourceRenderer` is fallback-only for tools that embed the resource directly in the result. Prefer `_meta.ui.resourceUri`. +- A capability that returns a UI resource does not also get a hand-built page. No parallel component for the same view. +- Resource states: loading placeholder while fetching, sandboxed render on success, error state with retry plus fallback to the markdown result when the resource fails. + +## UI actions + +Preferred host is `@mcp-ui/client` `AppRenderer`. It has no `onUIAction`. Map guest requests to tools and prompts only, never to an in-app route: + +- `onCallTool` — run the same server tool with its input schema, append the result, render any new resource. +- `onMessage` — send the text as the next user message, or show a status notice in the thread. +- `onOpenLink` — open the URL externally. + +Legacy `UIResourceRenderer` is the only host that uses `onUIAction`. Keep those five action types on that fallback: + +- **tool** — same as `onCallTool`. +- **prompt** — send the text as the next user message. +- **notify** — status in the thread. +- **link** — same as `onOpenLink`. +- **intent** — map to a tool call or a prompt. Never to an in-app route. + +## Security + +- HTML from a tool or the model renders only inside the MCP UI iframe. The host document never injects that HTML. +- Allowlist `ui://` resource URIs and validate the MIME type before rendering. Cap resource size. +- UI-action tool calls pass the same input schemas and server guards as chat tool calls. Hiding a widget is not a guard. Writes still use the auth ticket and `TraceabilityContext`. +- Log resource URI, tool latency, and render failures through the observability interface. + +## UX checklist + +- Mode declared. No drawer, prompt-first bar, overview, drill-down, navbar, or domain route in this deployment. +- No reused route-based components. Every view beyond text is a tool-linked MCP UI resource. +- `getAIAvailability()` gates the prompt. Unconfigured AI shows the empty configuration state. +- Chat state lives on the layout and survives scrolling. Prompt stays pinned on narrow viewports. +- Resources render sandboxed with loading, error, and markdown-fallback states. +- UI actions map to tools or prompts only. No in-app navigation. +- Narrow layout and both color schemes checked. +- Writes stay server-guarded with the auth ticket and `TraceabilityContext`. + +## What stays on other skills + +| Concern | Where it lives | +|---------|----------------| +| `getAIAvailability()` gate, tool coverage, agent loop | Architecture Core Contract #11–#13 | +| Schemas, server functions, trust boundaries | Architecture Core Contract #3–#6 and Schema Boundaries | +| MCP UI package versions for this repo | **`reference-tech-stack`** | +| Side panel and prompt-first concepts | **`promptable-ux`** | +| Env, logging, error tracking | **`observability-and-env`** | + +## Verification + +After UX-contract changes: update this `SKILL.md`, run `pnpm skills:check` and `pnpm skills:waza`, and follow AGENTS.md §15 when the reference shell changes. diff --git a/.agents/skills/observability-and-env/SKILL.md b/.agents/skills/observability-and-env/SKILL.md index 0e27734..6202896 100644 --- a/.agents/skills/observability-and-env/SKILL.md +++ b/.agents/skills/observability-and-env/SKILL.md @@ -47,6 +47,11 @@ This template publishes **multiple** skills. If only **this** skill is installed npx skills add carlosvin/tanstack-fullstack-ai-template --skill promptable-ux ``` +- **`agentic-ux`** (companion) — Fully agentic tool-only shell where views render as MCP UI resources. Install when shell work has no domain screens. + ```bash + npx skills add carlosvin/tanstack-fullstack-ai-template --skill agentic-ux + ``` + Discover all skills: `npx skills add carlosvin/tanstack-fullstack-ai-template --list` # Observability and Environment Setup diff --git a/.agents/skills/promptable-ux/SKILL.md b/.agents/skills/promptable-ux/SKILL.md index a1d4b79..30b5ef0 100644 --- a/.agents/skills/promptable-ux/SKILL.md +++ b/.agents/skills/promptable-ux/SKILL.md @@ -41,6 +41,11 @@ This template publishes **multiple** skills. If only **this** skill is installed npx skills add carlosvin/tanstack-fullstack-ai-template --skill observability-and-env ``` +- **`agentic-ux`** (companion) — Fully agentic tool-only shell where views render as MCP UI resources. Install when the deployment has no domain screens. + ```bash + npx skills add carlosvin/tanstack-fullstack-ai-template --skill agentic-ux + ``` + Discover all skills: `npx skills add carlosvin/tanstack-fullstack-ai-template --list` # Promptable UX diff --git a/.agents/skills/reference-tech-stack/SKILL.md b/.agents/skills/reference-tech-stack/SKILL.md index 31e2252..495024f 100644 --- a/.agents/skills/reference-tech-stack/SKILL.md +++ b/.agents/skills/reference-tech-stack/SKILL.md @@ -47,6 +47,11 @@ This template publishes **multiple** skills. If only **this** skill is installed npx skills add carlosvin/tanstack-fullstack-ai-template --skill promptable-ux ``` +- **`agentic-ux`** (companion) — Fully agentic tool-only shell where views render as MCP UI resources. This stack skill will name the MCP UI packages when the shell lands. + ```bash + npx skills add carlosvin/tanstack-fullstack-ai-template --skill agentic-ux + ``` + Discover all skills: `npx skills add carlosvin/tanstack-fullstack-ai-template --list` # Reference Tech Stack (Opinionated Defaults) diff --git a/.agents/skills/tanstack-promptable-fullstack-app-template/SKILL.md b/.agents/skills/tanstack-promptable-fullstack-app-template/SKILL.md index b296174..2b3d7dd 100644 --- a/.agents/skills/tanstack-promptable-fullstack-app-template/SKILL.md +++ b/.agents/skills/tanstack-promptable-fullstack-app-template/SKILL.md @@ -49,6 +49,11 @@ This template publishes **multiple** skills. If only **this** skill is installed npx skills add carlosvin/tanstack-fullstack-ai-template --skill promptable-ux ``` +- **`agentic-ux`** (companion) — Fully agentic tool-only shell where views render as MCP UI resources. Install when the deployment has no domain screens. + ```bash + npx skills add carlosvin/tanstack-fullstack-ai-template --skill agentic-ux + ``` + Discover all skills: `npx skills add carlosvin/tanstack-fullstack-ai-template --list` # TanStack Fullstack Pattern diff --git a/evals/agentic-ux/eval.yaml b/evals/agentic-ux/eval.yaml new file mode 100644 index 0000000..5b30a3e --- /dev/null +++ b/evals/agentic-ux/eval.yaml @@ -0,0 +1,33 @@ +name: agentic-ux-eval +description: CI eval suite for the fully agentic UX skill. Mock executor; no model API keys. +skill: agentic-ux +schemaVersion: "1.0" +version: "1.0" + +config: + trials_per_task: 1 + timeout_seconds: 60 + parallel: false + executor: mock + model: mock + +metrics: + - name: task_completion + weight: 1.0 + threshold: 0.8 + description: Mock executor produced graded output for the skill task. + +graders: + - type: code + name: has_output + config: + assertions: + - "len(output) > 10" + - type: text + name: no_errors + config: + regex_not_match: + - "(?i)fatal error|crashed|exception occurred" + +tasks: + - "tasks/*.yaml" diff --git a/evals/agentic-ux/tasks/agentic-anti-architecture.yaml b/evals/agentic-ux/tasks/agentic-anti-architecture.yaml new file mode 100644 index 0000000..4458f6c --- /dev/null +++ b/evals/agentic-ux/tasks/agentic-anti-architecture.yaml @@ -0,0 +1,19 @@ +id: agentic-anti-architecture +name: Do not use for schema or server boundaries +description: >- + Covers DO NOT USE FOR schema layers or server boundaries (use + tanstack-promptable-fullstack-app-template). +tags: [ux, anti-trigger] +inputs: + prompt: >- + Explain schema layers or server boundaries (use + tanstack-promptable-fullstack-app-template) rather than the agentic shell. +expected: + outcomes: + - type: task_completed +graders: + - name: has_output + type: code + config: + assertions: + - "len(output) > 10" diff --git a/evals/agentic-ux/tasks/agentic-anti-env.yaml b/evals/agentic-ux/tasks/agentic-anti-env.yaml new file mode 100644 index 0000000..007ac71 --- /dev/null +++ b/evals/agentic-ux/tasks/agentic-anti-env.yaml @@ -0,0 +1,17 @@ +id: agentic-anti-env +name: Do not use for env or logging +description: Covers DO NOT USE FOR env parse or logging (use observability-and-env). +tags: [ux, anti-trigger] +inputs: + prompt: >- + Set up env parse or logging (use observability-and-env) rather than the + agentic UX skill. +expected: + outcomes: + - type: task_completed +graders: + - name: has_output + type: code + config: + assertions: + - "len(output) > 10" diff --git a/evals/agentic-ux/tasks/agentic-anti-route-components.yaml b/evals/agentic-ux/tasks/agentic-anti-route-components.yaml new file mode 100644 index 0000000..949a6ad --- /dev/null +++ b/evals/agentic-ux/tasks/agentic-anti-route-components.yaml @@ -0,0 +1,17 @@ +id: agentic-anti-route-components +name: Do not reuse route components +description: Covers DO NOT USE FOR side panel chat and prompt-first layout. +tags: [ux, anti-trigger] +inputs: + prompt: >- + Explain side panel chat or prompt-first layout (use promptable-ux) rather + than the fully agentic shell. +expected: + outcomes: + - type: task_completed +graders: + - name: has_output + type: code + config: + assertions: + - "len(output) > 10" diff --git a/evals/agentic-ux/tasks/agentic-anti-ui-library.yaml b/evals/agentic-ux/tasks/agentic-anti-ui-library.yaml new file mode 100644 index 0000000..2bae6ac --- /dev/null +++ b/evals/agentic-ux/tasks/agentic-anti-ui-library.yaml @@ -0,0 +1,15 @@ +id: agentic-anti-ui-library +name: Do not use for UI kit lookup +description: Covers DO NOT USE FOR UI kit lookup (use reference-tech-stack). +tags: [ux, anti-trigger] +inputs: + prompt: UI kit lookup (use reference-tech-stack) for this template? +expected: + outcomes: + - type: task_completed +graders: + - name: has_output + type: code + config: + assertions: + - "len(output) > 10" diff --git a/evals/agentic-ux/tasks/agentic-contract-overview.yaml b/evals/agentic-ux/tasks/agentic-contract-overview.yaml new file mode 100644 index 0000000..3c409c2 --- /dev/null +++ b/evals/agentic-ux/tasks/agentic-contract-overview.yaml @@ -0,0 +1,17 @@ +id: agentic-contract-overview +name: Agentic UX overview +description: Covers the skill description tagline. +tags: [ux, description] +inputs: + prompt: >- + Fully agentic UX where a thin prompt shell renders tool-linked MCP UI + resources. +expected: + outcomes: + - type: task_completed +graders: + - name: has_output + type: code + config: + assertions: + - "len(output) > 10" diff --git a/evals/agentic-ux/tasks/agentic-mcp-ui-render.yaml b/evals/agentic-ux/tasks/agentic-mcp-ui-render.yaml new file mode 100644 index 0000000..bd95a4f --- /dev/null +++ b/evals/agentic-ux/tasks/agentic-mcp-ui-render.yaml @@ -0,0 +1,15 @@ +id: agentic-mcp-ui-render +name: MCP UI resource render +description: Covers USE FOR MCP UI host and sandboxed UI resource. +tags: [ux, trigger] +inputs: + prompt: How does a tool-linked MCP UI resource render in the agentic shell? +expected: + outcomes: + - type: task_completed +graders: + - name: has_output + type: code + config: + assertions: + - "len(output) > 10" diff --git a/evals/agentic-ux/tasks/agentic-shell-layout.yaml b/evals/agentic-ux/tasks/agentic-shell-layout.yaml new file mode 100644 index 0000000..2cae5af --- /dev/null +++ b/evals/agentic-ux/tasks/agentic-shell-layout.yaml @@ -0,0 +1,15 @@ +id: agentic-shell-layout +name: Fully agentic shell layout +description: Covers USE FOR fully agentic mode and agent shell. +tags: [ux, trigger] +inputs: + prompt: How should a fully agentic app place the prompt and render responses? +expected: + outcomes: + - type: task_completed +graders: + - name: has_output + type: code + config: + assertions: + - "len(output) > 10" diff --git a/evals/agentic-ux/tasks/agentic-ui-action-tool.yaml b/evals/agentic-ux/tasks/agentic-ui-action-tool.yaml new file mode 100644 index 0000000..496bec6 --- /dev/null +++ b/evals/agentic-ux/tasks/agentic-ui-action-tool.yaml @@ -0,0 +1,15 @@ +id: agentic-ui-action-tool +name: UI action tool call +description: Covers USE FOR dynamic tool UI. +tags: [ux, trigger] +inputs: + prompt: What happens when a sandboxed UI widget triggers a tool action? +expected: + outcomes: + - type: task_completed +graders: + - name: has_output + type: code + config: + assertions: + - "len(output) > 10" diff --git a/scripts/skills/runSkillEvals.mjs b/scripts/skills/runSkillEvals.mjs index 8c8417f..5c6b52a 100644 --- a/scripts/skills/runSkillEvals.mjs +++ b/scripts/skills/runSkillEvals.mjs @@ -782,6 +782,40 @@ export function createSkillEvals(rootDir = defaultRootDir) { : fail('new MongoClient is owned by src/services/db/mongoClient.server.ts', clientOwners) }, }, + { + id: 'agentic-ux-shell-contract', + skill: 'agentic-ux', + description: 'Agentic skill defines the tool-only shell and sandbox rule without reusing route components', + async run() { + const { agentSkillsDir } = getSkillPaths(rootDir) + let skillMd + try { + skillMd = await readText(path.join(agentSkillsDir, 'agentic-ux', 'SKILL.md')) + } catch { + return fail('Missing .agents/skills/agentic-ux/SKILL.md') + } + const missing = [] + if (!/## Shell/.test(skillMd)) missing.push('Shell section') + if (!/## MCP UI rendering/.test(skillMd)) missing.push('MCP UI rendering section') + if (!/## Security/.test(skillMd)) missing.push('Security section') + if (!/_meta\.ui\.resourceUri/.test(skillMd)) missing.push('_meta.ui.resourceUri link') + if (!/only inside the MCP UI iframe/.test(skillMd)) missing.push('iframe-only render rule') + if (!/No `ChatDrawer`, `PromptBar`, `AppNavbar`/.test(skillMd)) missing.push('no route-component reuse rule') + if (!/\*\*`agentic-ux`\*\*\s*\(companion\)/.test(skillMd) && !/agentic-ux/.test(skillMd)) { + missing.push('companion wiring') + } + if (missing.length > 0) { + return fail('agentic-ux skill is missing required contract text', missing) + } + const templateSkill = await readText( + path.join(agentSkillsDir, 'tanstack-promptable-fullstack-app-template', 'SKILL.md'), + ) + if (!/\*\*`agentic-ux`\*\*\s*\(companion\)/.test(templateSkill)) { + return fail('Parent architecture skill must list agentic-ux as a companion') + } + return pass() + }, + }, ] } diff --git a/skills/README.md b/skills/README.md index 0b8800c..455478a 100644 --- a/skills/README.md +++ b/skills/README.md @@ -7,6 +7,7 @@ This repo ships **[Agent Skills](https://agentskills.io)** — one `SKILL.md` pe - **`reference-tech-stack`** — **opinionated defaults** for *this* template: Zod, Mantine, lucide-react, MongoDB + seed, jose JWT, OpenAI adapter, pino + Sentry, react-markdown, Biome, Vitest, Playwright, Netlify. - **`repository-architecture`** — **language-agnostic repository architecture**: injected collection owners, indexes, stale-data cleanup, and resource lifetime (TypeScript `AsyncDisposable`, Java `AutoCloseable`, Python context-manager magic methods). - **`promptable-ux`** — **shared prompt UX**: mobile first, assistant markdown, and two concepts that differ only in prompt placement. **Promptable UI (side)** hides the prompt in a side or bottom panel (this reference app). **Prompt-first** keeps the prompt on screen and drills from an overview down the page. +- **`agentic-ux`** — **fully agentic UX**: a thin prompt shell with no domain screens. Every view beyond text renders as a tool-linked MCP UI resource in a sandboxed iframe. Operational how-to (file paths, snippets, validation commands) still lives in **[AGENTS.md](../AGENTS.md)**. @@ -19,6 +20,7 @@ Operational how-to (file paths, snippets, validation commands) still lives in ** | "Which package does this template use?" / match the demo app | `reference-tech-stack` | | Collection repositories, indexes, stale-data cleanup, resource lifetime | `repository-architecture` | | Prompt chrome, mobile first, assistant markdown, prompt-first vs side panel | `promptable-ux` | +| Fully agentic shell, MCP UI host, dynamic tool UI | `agentic-ux` | | Scaffolding this template as-is | Architecture + `reference-tech-stack` + `promptable-ux` (+ observability when touching env; repository-architecture when splitting repositories) | Keep them **separate**. The parent skill states architecture **invariants** and which stack pieces are **swappable**; `reference-tech-stack` names this repo's vendors; `observability-and-env` owns the env/logging setup recipe; `repository-architecture` owns how repositories are split, indexed, cleaned up, and disposed; `promptable-ux` owns shared layout rules and the two prompt concepts; AGENTS.md owns day-to-day ops. @@ -43,6 +45,7 @@ npx skills add carlosvin/tanstack-fullstack-ai-template --skill observability-an npx skills add carlosvin/tanstack-fullstack-ai-template --skill reference-tech-stack npx skills add carlosvin/tanstack-fullstack-ai-template --skill repository-architecture npx skills add carlosvin/tanstack-fullstack-ai-template --skill promptable-ux +npx skills add carlosvin/tanstack-fullstack-ai-template --skill agentic-ux ``` List what this repo publishes: @@ -81,6 +84,7 @@ Author and commit these files — they are the contract, not generated output: - `.agents/skills/reference-tech-stack/SKILL.md` - `.agents/skills/repository-architecture/SKILL.md` - `.agents/skills/promptable-ux/SKILL.md` +- `.agents/skills/agentic-ux/SKILL.md` - `evals//` — [Waza](https://microsoft.github.io/waza/) eval suites (CI via `pnpm skills:waza`) Format: [agentskills.io specification](https://agentskills.io/specification) (`name` + `description` frontmatter; directory name matches `name`).