From df718b81fa9c642e0fec2515408fbea9b5c91056 Mon Sep 17 00:00:00 2001 From: Baptiste Leproux Date: Fri, 27 Mar 2026 21:33:36 +0100 Subject: [PATCH 01/20] hack agent task --- doc-agent-tasks.md | 305 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100644 doc-agent-tasks.md diff --git a/doc-agent-tasks.md b/doc-agent-tasks.md new file mode 100644 index 000000000..4e22a76c5 --- /dev/null +++ b/doc-agent-tasks.md @@ -0,0 +1,305 @@ +# Specs + +# Doc-Agent Integration — Task Breakdown + +## Context + +Docus has an existing `assistant` module at `layer/modules/assistant/`. This feature extends it with a durable AI agent triggered by GitHub webhooks on PRs: it detects missing or outdated docs, generates/updates MDC pages, and commits the result back to the PR branch. + +The MCP server already exposes `list-pages` and `get-page`. We only need to add `commit-update`. + +## References + +* [Docus repository]() +* [Docus documentation]() +* [Workflow AI documentation]() +* [Workflow Nuxt getting started]() + +--- + +## File Structure (all new files) + +```text +layer/modules/assistant/runtime/server/ +├── utils/ +│ └── github-auth.ts # JWT → installation token +├── mcp/ +│ ├── tools/ +│ │ └── commit-update.ts # new MCP tool +│ └── prompts/ +│ └── review-pr-docs.ts # new MCP prompt +├── workflows/ +│ └── doc-agent.ts # durable workflow +└── api/webhook/ + └── github.post.ts # webhook endpoint +``` + +## Files to modify + +```text +layer/package.json — add workflow, @workflow/ai +layer/nuxt.config.ts — add workflow/nuxt module + runtimeConfig keys +layer/modules/assistant/index.ts — add docAgent options to module schema +layer/app/app.config.ts — add docus.ai.docAgent config block +``` + +--- + +## Tasks + +### Task 1 — Foundation: config, dependencies, module options + +**Files:** `layer/package.json`, `layer/nuxt.config.ts`, `layer/modules/assistant/index.ts`, `layer/app/app.config.ts` + +**References:** + +* [Workflow Nuxt getting started]() + +Add dependencies: + +```json +"workflow": "latest", +"@workflow/ai": "latest" +``` + +Add to `nuxt.config.ts` modules array (alongside existing modules): + +```ts +'workflow/nuxt' +``` + +Add to `nuxt.config.ts` runtimeConfig (match existing `assistant` block pattern): + +```ts +runtimeConfig: { + githubAppId: '', // GITHUB_APP_ID + githubAppPrivateKey: '', // GITHUB_APP_PRIVATE_KEY (PEM) + webhookSecret: '', // GITHUB_WEBHOOK_SECRET +} +``` + +Add `docAgent` to the assistant module options schema in `index.ts`: + +```ts +docAgent?: { + enabled?: boolean + mode?: 'comment' | 'commit' + githubRepo?: string +} +``` + +Extend `docus.ai` in `app.config.ts`: + +```ts +docus: { + ai: { + // ...existing options unchanged... + docAgent: { + enabled: false, + mode: 'comment', // 'comment' | 'commit' + githubRepo: 'org/repo', + } + } +} +``` + +GitHub App permissions required: `contents: write`, `pull_requests: write`, `metadata: read` + +--- + +### Task 2 — GitHub Auth Utility + +**File:** `layer/modules/assistant/runtime/server/utils/github-auth.ts` + +**References:** + +* [Docus repository]() + +Signs a JWT with the GitHub App private key, exchanges it for a short-lived installation access token using `installation.id` from the webhook payload. + +* Uses `runtimeConfig.githubAppId` and `runtimeConfig.githubAppPrivateKey` +* Signs a JWT valid for 10 minutes (GitHub App auth requirement) +* POSTs to `https://api.github.com/app/installations/{installationId}/access_tokens` +* Returns the short-lived `token` string +* Export: `getInstallationToken(installationId: number): Promise` + +--- + +### Task 3 — `commit-update` MCP Tool + +**File:** `layer/modules/assistant/runtime/server/mcp/tools/commit-update.ts` + +**References:** + +* [Docus repository]() +* [Docus documentation]() + +Uses `defineMcpTool` from `@nuxtjs/mcp-toolkit/server` (match pattern of existing tools in the module). + +Input schema (Zod): + +```ts +{ + owner: string, + repo: string, + branch: string, + path: string, // e.g. content/2.guide/my-feature.md + content: string, // full MDC file content + token: string, // installation access token +} +``` + +Execute function — marked `"use step"` (for workflow retries): + +1. Try GET `/repos/{owner}/{repo}/contents/{path}?ref={branch}` to fetch current SHA +2. If file exists → PUT with `{ message, content: base64, sha, branch }` +3. If file doesn't exist → PUT with `{ message, content: base64, branch }` (no sha) +4. Commit message: `docs: update {path} via doc-agent` + +--- + +### Task 4 — `review-pr-docs` MCP Prompt + +**File:** `layer/modules/assistant/runtime/server/mcp/prompts/review-pr-docs.ts` + +**References:** + +* [Docus repository]() +* [Docus documentation]() + +Uses `defineMcpPrompt` from `@nuxtjs/mcp-toolkit/server`. + +Arguments: + +```ts +{ + owner: string, + repo: string, + branch: string, + diff: string, // filtered PR diff (already truncated) + token: string, +} +``` + +The prompt instructs Claude to: + +1. Call `list-pages` to discover existing documentation +2. Call `get-page` on pages that may be related to the diff +3. Decide what to create (new page) or update (existing section) +4. Call `commit-update` for each file changed + +MDC output rules: + +* Use Docus components: `::note`, `::callout`, `::code-group` +* File paths follow content structure: `content/2.guide/my-feature.md` +* Never delete existing sections, only append or update +* Skip docs if the diff is only types, tests, or config files with no user-facing changes + +--- + +### Task 5 — Durable Workflow + +**File:** `layer/modules/assistant/runtime/server/workflows/doc-agent.ts` + +**References:** + +* [Workflow AI documentation]() +* [Workflow Nuxt getting started]() + +Marked `"use workflow"` at the top of the file. + +Input: `{ branch: string, filteredDiff: string }` + +Uses `DurableAgent` from `@workflow/ai/agent` with tools via `experimental_createMCPClient` pointed at `/mcp` (same MCP server path already used by `search.ts`). + +The agent: + +1. Receives the `review-pr-docs` prompt with branch + diff +2. Calls MCP tools (`list-pages`, `get-page`, `commit-update`) until done +3. `commit-update` execute is `"use step"` → automatically retried on failure + +Export: `docAgentWorkflow` (named export used by the webhook handler) + +--- + +### Task 6 — GitHub Webhook Handler + +**File:** `layer/modules/assistant/runtime/server/api/webhook/github.post.ts` + +**References:** + +* [Docus repository]() +* [Workflow Nuxt getting started]() + +Uses `defineEventHandler` (match pattern from `search.ts`). + +Handles `pull_request` events with actions `opened` and `synchronize`. + +Processing order: + +1. Read raw body — verify `x-hub-signature-256` using `runtimeConfig.webhookSecret` (HMAC-SHA256). Return `401` if invalid. +2. Parse payload — extract `installation.id`, `pull_request.head.ref` (branch), `pull_request.base.sha`, `pull_request.head.sha`, `repository.owner.login`, `repository.name` +3. Call `getInstallationToken(installation.id)` → short-lived token +4. Fetch diff: `GET /repos/{owner}/{repo}/compare/{base.sha}...{head.sha}` with `Accept: application/vnd.github.v3.diff` +5. Filter diff: + * Keep only `.ts`, `.vue`, `.js` files + * Drop deleted files, lockfiles (`package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`), generated files (`*.d.ts`, `dist/`) +6. Truncate to 12,000 token hard limit — log file names of any skipped files +7. Call `start(docAgentWorkflow, [{ branch, filteredDiff }])` — **do not await** +8. Return `200` immediately + +> Enable Fluid Compute on the Vercel project for efficient workflow suspension/resumption. + +--- + +## Dependency handling + +Some tasks are blocked by others, while others are only related and can move in parallel. + +**Blocking chain** + +* Task 1 blocks all implementation tasks +* Task 2 blocks Task 6 +* Task 3 blocks Task 4 and Task 5 +* Task 4 blocks Task 5 +* Task 5 and Task 6 both block the final end-to-end wiring and validation + +**Related but parallelizable work** + +* Task 2 and Task 3 can run in parallel after Task 1 +* Task 6 can start after Task 2, even before Task 5 is finished, as long as the workflow start can be stubbed or logged first +* Task 4 and Task 6 are related through the end-to-end flow, but Task 6 does not need to wait for Task 4 to begin + +**Execution rule** + +* Mark issues as `blocked by` only when another task must land first +* Mark issues as `related to` when the work should stay aligned but can proceed independently +* For testing-first delivery, prefer shipping the webhook path with a temporary stub/logging milestone before the agent side is fully connected + +--- + +## Split suggestion + +| Order | Task | Owner | Depends on | Relationship notes | +| -- | -- | -- | -- | -- | +| 1 | Task 1 — Foundation | — | — | Blocks all implementation tasks | +| 2 | Task 2 — GitHub Auth | — | Task 1 | Blocks Task 6 | +| 3 | Task 3 — `commit-update` tool | — | Task 1 | Blocks Tasks 4 and 5 | +| 4 | Task 6 — Webhook handler | — | Task 1, Task 2 | Related to Tasks 4 and 5; can start before they finish | +| 5 | Task 4 — `review-pr-docs` prompt | — | Task 3 | Blocks Task 5 | +| 6 | Task 5 — Durable workflow | — | Task 3, Task 4 | Blocks final end-to-end wiring | +| 7 | Wire webhook to workflow | — | Task 5, Task 6 | Final integration and validation step | + +**Testing-first split for two people:** + +* **Person A**: Tasks 1 + 2 + 6 — foundation, auth, and webhook path first so PR events can be received and the diff filtering can be tested early +* **Person B**: Tasks 3 + 4 + 5 — MCP tool, prompt, and workflow in parallel once the foundation is in place + +Task 1 is still the initial blocker. After that, split the work into two tracks: + +* **Track A:** Task 2 → Task 6 +* **Track B:** Task 3 → Task 4 → Task 5 + +These tracks should stay related to each other, but only converge as blocking work at the final wiring step. + +A practical milestone is to get the webhook returning `200`, verifying signatures, fetching diffs, and logging the workflow input before connecting it to the full agent execution. \ No newline at end of file From 67f57aceabb604b638ade94350c81073580345e1 Mon Sep 17 00:00:00 2001 From: Baptiste Leproux Date: Fri, 27 Mar 2026 21:44:44 +0100 Subject: [PATCH 02/20] github webhook integration --- docs/nuxt.config.ts | 7 + layer/modules/assistant/index.ts | 156 ++- .../runtime/composables/useAssistant.ts | 4 +- .../assistant/runtime/server/api/search.ts | 4 +- .../runtime/server/api/webhook/github.post.ts | 11 + .../runtime/server/utils/github-auth.ts | 34 + layer/nuxt.config.ts | 7 + layer/package.json | 2 +- pnpm-lock.yaml | 1073 ++++++++++++++++- 9 files changed, 1201 insertions(+), 97 deletions(-) create mode 100644 layer/modules/assistant/runtime/server/api/webhook/github.post.ts create mode 100644 layer/modules/assistant/runtime/server/utils/github-auth.ts diff --git a/docs/nuxt.config.ts b/docs/nuxt.config.ts index 5c548acd9..add1d6d55 100644 --- a/docs/nuxt.config.ts +++ b/docs/nuxt.config.ts @@ -16,6 +16,13 @@ export default defineNuxtConfig({ sourcemap: false, }, }, + docus: { + agent: { + review: { + enabled: true, + }, + }, + }, i18n: { defaultLocale: 'en', locales: [{ diff --git a/layer/modules/assistant/index.ts b/layer/modules/assistant/index.ts index c41b9a0ee..9f9d5a5b7 100644 --- a/layer/modules/assistant/index.ts +++ b/layer/modules/assistant/index.ts @@ -1,45 +1,96 @@ import { addComponent, addImports, addServerHandler, createResolver, defineNuxtModule, logger } from '@nuxt/kit' +import { getGitEnv, getLocalGitInfo } from '../../utils/git' -export interface AssistantModuleOptions { - /** - * API endpoint path for the assistant - * @default '/__docus__/assistant' - */ - apiPath?: string - /** - * MCP server URL or path. - * - Use a path like '/mcp' to use the built-in Docus MCP server - * - Use a full URL like 'https://docs.example.com/mcp' for external MCP servers - * @default '/mcp' - */ - mcpServer?: string - /** - * AI model to use via AI SDK Gateway - * @default 'google/gemini-3-flash' - */ - model?: string +export interface DocusModuleOptions { + agent?: { + /** + * AI model to use via AI SDK Gateway + * @default 'google/gemini-3-flash' + */ + model?: string + /** + * MCP server URL or path. + * - Use a path like '/mcp' to use the built-in Docus MCP server + * - Use a full URL like 'https://docs.example.com/mcp' for external MCP servers + * @default '/mcp' + */ + mcpServer?: string + /** + * AI chat assistant embedded in the docs site. + */ + chat?: { + /** + * Enable the chat assistant (auto-detected from AI_GATEWAY_API_KEY) + * @default true + */ + enabled?: boolean + /** + * API endpoint path for the chat assistant + * @default '/__docus__/assistant' + */ + apiPath?: string + } + /** + * PR documentation review agent triggered by GitHub webhooks. + */ + review?: { + /** + * Enable the webhook handler + * @default false + */ + enabled?: boolean + /** + * Whether the agent posts a PR comment ('comment') or directly commits ('commit') + * @default 'comment' + */ + mode?: 'comment' | 'commit' + /** + * Target GitHub repository in 'org/repo' format. + * Auto-detected from Vercel, Netlify, and GitHub Actions CI environment variables. + */ + githubRepo?: string + } + } } const log = logger.withTag('Docus') -export default defineNuxtModule({ +async function detectGithubRepo(rootDir: string): Promise { + // Local: read origin remote from .git/config + const local = await getLocalGitInfo(rootDir) + if (local?.owner && local?.name) + return `${local.owner}/${local.name}` + + // CI: Vercel, Netlify, GitHub Actions, GitLab CI + const env = getGitEnv() + if (env.owner && env.name) + return `${env.owner}/${env.name}` +} + +export default defineNuxtModule({ meta: { - name: 'assistant', - configKey: 'assistant', + name: 'docus:agent', + configKey: 'docus', }, defaults: { - apiPath: '/__docus__/assistant', - mcpServer: '/mcp', - model: 'google/gemini-3-flash', + agent: { + model: 'google/gemini-3-flash', + mcpServer: '/mcp', + chat: { + apiPath: '/__docus__/assistant', + }, + }, }, - setup(options, nuxt) { + async setup(options, nuxt) { + const agent = options.agent! const hasApiKey = !!process.env.AI_GATEWAY_API_KEY + const chatEnabled = hasApiKey && (agent.chat?.enabled !== false) const { resolve } = createResolver(import.meta.url) - nuxt.options.runtimeConfig.public.assistant = { - enabled: hasApiKey, - apiPath: options.apiPath!, + nuxt.options.runtimeConfig.public.agent = { + chatEnabled, + chatApiPath: agent.chat?.apiPath!, } addImports([ @@ -60,23 +111,48 @@ export default defineNuxtModule({ components.forEach(name => addComponent({ name, - filePath: hasApiKey + filePath: chatEnabled ? resolve(`./runtime/components/${name}.vue`) : resolve('./runtime/components/AssistantChatDisabled.vue'), }), ) + if (process.env.GITHUB_APP_ID) + nuxt.options.runtimeConfig.githubAppId = process.env.GITHUB_APP_ID + if (process.env.GITHUB_APP_PRIVATE_KEY) + nuxt.options.runtimeConfig.githubAppPrivateKey = process.env.GITHUB_APP_PRIVATE_KEY + if (process.env.GITHUB_WEBHOOK_SECRET) + nuxt.options.runtimeConfig.webhookSecret = process.env.GITHUB_WEBHOOK_SECRET + + if (agent.review?.enabled) { + const githubRepo = agent.review.githubRepo || await detectGithubRepo(nuxt.options.rootDir) + if (githubRepo) + nuxt.options.runtimeConfig.agentGithubRepo = githubRepo + else + log.warn('Review agent enabled but no GitHub repository detected — set docus.agent.review.githubRepo or deploy on Vercel, Netlify, or GitHub Actions') + + addServerHandler({ + route: '/__docus__/webhook/github', + handler: resolve('./runtime/server/api/webhook/github.post'), + }) + + const webhookPath = '/__docus__/webhook/github' + const siteUrl = process.env.NUXT_PUBLIC_SITE_URL?.replace(/\/$/, '') + || nuxt.options.devServer?.url?.replace(/\/$/, '') + log.info(`GitHub webhook registered — set Webhook URL to: ${siteUrl ? `${siteUrl}${webhookPath}` : webhookPath}`) + } + if (!hasApiKey) { - log.warn('AI assistant disabled: AI_GATEWAY_API_KEY not found') + log.warn('AI agent disabled: AI_GATEWAY_API_KEY not found') return } - nuxt.options.runtimeConfig.assistant = { - mcpServer: options.mcpServer!, - model: options.model!, + nuxt.options.runtimeConfig.agent = { + mcpServer: agent.mcpServer!, + model: agent.model!, } - const routePath = options.apiPath!.replace(/^\//, '') + const routePath = agent.chat?.apiPath!.replace(/^\//, '') addServerHandler({ route: `/${routePath}`, handler: resolve('./runtime/server/api/search'), @@ -86,13 +162,17 @@ export default defineNuxtModule({ declare module 'nuxt/schema' { interface PublicRuntimeConfig { - assistant: { - enabled: boolean - apiPath: string + agent: { + chatEnabled: boolean + chatApiPath: string } } interface RuntimeConfig { - assistant: { + githubAppId: string + githubAppPrivateKey: string + webhookSecret: string + agentGithubRepo: string + agent: { mcpServer: string model: string } diff --git a/layer/modules/assistant/runtime/composables/useAssistant.ts b/layer/modules/assistant/runtime/composables/useAssistant.ts index bdcf419de..1c6ba528c 100644 --- a/layer/modules/assistant/runtime/composables/useAssistant.ts +++ b/layer/modules/assistant/runtime/composables/useAssistant.ts @@ -25,10 +25,10 @@ const PANEL_WIDTH_EXPANDED = 520 export function useAssistant() { const config = useRuntimeConfig() const appConfig = useAppConfig() - const assistantRuntimeConfig = config.public.assistant as { enabled?: boolean } | undefined + const assistantRuntimeConfig = config.public.agent as { chatEnabled?: boolean } | undefined const assistantConfig = appConfig.assistant as { faqQuestions?: FaqQuestions | LocalizedFaqQuestions } | undefined const docusRuntimeConfig = appConfig.docus as { locale?: string } | undefined - const isEnabled = computed(() => assistantRuntimeConfig?.enabled ?? false) + const isEnabled = computed(() => assistantRuntimeConfig?.chatEnabled ?? false) const isOpen = useState('assistant-open', () => false) const isExpanded = useState('assistant-expanded', () => false) diff --git a/layer/modules/assistant/runtime/server/api/search.ts b/layer/modules/assistant/runtime/server/api/search.ts index 279508057..4160bd53a 100644 --- a/layer/modules/assistant/runtime/server/api/search.ts +++ b/layer/modules/assistant/runtime/server/api/search.ts @@ -58,7 +58,7 @@ export default defineEventHandler(async (event) => { const siteName = siteConfig.name || 'Documentation' - const mcpServer = config.assistant.mcpServer + const mcpServer = config.agent.mcpServer const isExternalUrl = mcpServer.startsWith('http://') || mcpServer.startsWith('https://') const baseURL = config.app?.baseURL?.replace(/\/$/, '') || '' const mcpUrl = isExternalUrl @@ -76,7 +76,7 @@ export default defineEventHandler(async (event) => { execute: async ({ writer }: { writer: UIMessageStreamWriter }) => { const modelMessages = await convertToModelMessages(messages) const result = streamText({ - model: config.assistant.model, + model: config.agent.model, maxOutputTokens: 4000, maxRetries: 2, stopWhen: stopWhenResponseComplete, diff --git a/layer/modules/assistant/runtime/server/api/webhook/github.post.ts b/layer/modules/assistant/runtime/server/api/webhook/github.post.ts new file mode 100644 index 000000000..3e49f8a89 --- /dev/null +++ b/layer/modules/assistant/runtime/server/api/webhook/github.post.ts @@ -0,0 +1,11 @@ +export default defineEventHandler(async (event) => { + const payload = await readBody(event) + + const action = payload?.action + const repo = payload?.repository?.full_name + const pr = payload?.pull_request?.number + + console.log(`[doc-agent] webhook received — action: ${action}, repo: ${repo}, PR: #${pr}`) + + return { ok: true } +}) diff --git a/layer/modules/assistant/runtime/server/utils/github-auth.ts b/layer/modules/assistant/runtime/server/utils/github-auth.ts new file mode 100644 index 000000000..52123ac26 --- /dev/null +++ b/layer/modules/assistant/runtime/server/utils/github-auth.ts @@ -0,0 +1,34 @@ +import { createSign } from 'node:crypto' + +function base64url(value: string): string { + return Buffer.from(value).toString('base64url') +} + +function buildJwt(appId: string, privateKey: string): string { + const now = Math.floor(Date.now() / 1000) + const header = base64url(JSON.stringify({ alg: 'RS256', typ: 'JWT' })) + const payload = base64url(JSON.stringify({ iat: now - 60, exp: now + 600, iss: appId })) + const data = `${header}.${payload}` + const sign = createSign('RSA-SHA256') + sign.update(data) + return `${data}.${sign.sign(privateKey, 'base64url')}` +} + +export async function getInstallationToken(installationId: number): Promise { + const config = useRuntimeConfig() + const jwt = buildJwt(config.githubAppId, config.githubAppPrivateKey) + + const response = await $fetch<{ token: string }>( + `https://api.github.com/app/installations/${installationId}/access_tokens`, + { + method: 'POST', + headers: { + 'Authorization': `Bearer ${jwt}`, + 'Accept': 'application/vnd.github.v3+json', + 'User-Agent': 'docus-doc-agent', + }, + }, + ) + + return response.token +} diff --git a/layer/nuxt.config.ts b/layer/nuxt.config.ts index 92537b823..88c4be0ad 100644 --- a/layer/nuxt.config.ts +++ b/layer/nuxt.config.ts @@ -105,6 +105,13 @@ export default defineNuxtConfig({ nitroConfig.prerender.routes.push('/sitemap.xml') }, }, + docus: { + agent: { + review: { + enabled: true, + }, + }, + }, icon: { customCollections: [ { diff --git a/layer/package.json b/layer/package.json index 5709b47e5..3a90a8666 100644 --- a/layer/package.json +++ b/layer/package.json @@ -33,7 +33,7 @@ "@nuxt/kit": "^4.4.2", "@nuxt/ui": "^4.5.1", "@nuxtjs/i18n": "^10.2.3", - "@nuxtjs/mcp-toolkit": "^0.11.0", + "@nuxtjs/mcp-toolkit": "^0.12.0", "@nuxtjs/mdc": "^0.20.2", "@nuxtjs/robots": "^5.7.1", "@shikijs/core": "^4.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8f2c9a32c..847e4f8c6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -28,7 +28,7 @@ importers: version: 10.0.3(jiti@2.6.1) nuxt: specifier: 4.4.2 - version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2) + version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2) release-it: specifier: ^19.2.4 version: 19.2.4(@types/node@25.5.0)(magicast@0.5.2) @@ -71,7 +71,7 @@ importers: version: 25.5.0 tsup: specifier: ^8.5.1 - version: 8.5.1(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) + version: 8.5.1(@swc/core@1.15.3(@swc/helpers@0.5.19))(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) tsx: specifier: ^4.21.0 version: 4.21.0 @@ -80,25 +80,25 @@ importers: dependencies: '@nuxt/ui': specifier: ^4.5.1 - version: 4.5.1(@nuxt/content@3.12.0(better-sqlite3@12.8.0)(magicast@0.5.2))(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.7)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.8.0))(embla-carousel@8.6.0)(ioredis@5.10.1)(magicast@0.5.2)(tailwindcss@4.2.2)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-router@4.6.4(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3))(yjs@13.6.30)(zod@4.3.6) + version: 4.5.1(@nuxt/content@3.12.0(better-sqlite3@12.8.0)(magicast@0.5.2))(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.7)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.8.0))(embla-carousel@8.6.0)(ioredis@5.10.1)(magicast@0.5.2)(tailwindcss@4.2.2)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-router@4.6.4(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3))(yjs@13.6.30)(zod@4.3.6) '@nuxtjs/i18n': specifier: ^10.2.3 - version: 10.2.3(@vue/compiler-dom@3.5.30)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(rollup@4.59.0)(vue@3.5.30(typescript@5.9.3)) + version: 10.2.3(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(@vue/compiler-dom@3.5.30)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(rollup@4.59.0)(vue@3.5.30(typescript@5.9.3)) '@vercel/analytics': specifier: ^2.0.1 - version: 2.0.1(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2))(vue-router@4.6.4(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3)) + version: 2.0.1(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2))(vue-router@4.6.4(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3)) '@vercel/speed-insights': specifier: ^2.0.0 - version: 2.0.0(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2))(vue-router@4.6.4(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3)) + version: 2.0.0(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2))(vue-router@4.6.4(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3)) docus: specifier: workspace:* version: link:../layer nuxt: specifier: 4.4.2 - version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2) + version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2) nuxt-studio: specifier: ^1.5.1 - version: 1.5.1(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(magicast@0.5.2)(vue@3.5.30(typescript@5.9.3)) + version: 1.5.1(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(magicast@0.5.2)(vue@3.5.30(typescript@5.9.3)) tailwindcss: specifier: ^4.2.2 version: 4.2.2 @@ -128,19 +128,19 @@ importers: version: 3.12.0(better-sqlite3@12.8.0)(magicast@0.5.2) '@nuxt/image': specifier: ^2.0.0 - version: 2.0.0(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(magicast@0.5.2) + version: 2.0.0(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(magicast@0.5.2) '@nuxt/kit': specifier: ^4.4.2 version: 4.4.2(magicast@0.5.2) '@nuxt/ui': specifier: ^4.5.1 - version: 4.5.1(@nuxt/content@3.12.0(better-sqlite3@12.8.0)(magicast@0.5.2))(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.7)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.8.0))(embla-carousel@8.6.0)(ioredis@5.10.1)(magicast@0.5.2)(tailwindcss@4.2.2)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-router@4.6.4(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3))(yjs@13.6.30)(zod@4.3.6) + version: 4.5.1(@nuxt/content@3.12.0(better-sqlite3@12.8.0)(magicast@0.5.2))(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.7)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.8.0))(embla-carousel@8.6.0)(ioredis@5.10.1)(magicast@0.5.2)(tailwindcss@4.2.2)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-router@4.6.4(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3))(yjs@13.6.30)(zod@4.3.6) '@nuxtjs/i18n': specifier: ^10.2.3 - version: 10.2.3(@vue/compiler-dom@3.5.30)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(rollup@4.59.0)(vue@3.5.30(typescript@5.9.3)) + version: 10.2.3(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(@vue/compiler-dom@3.5.30)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(rollup@4.59.0)(vue@3.5.30(typescript@5.9.3)) '@nuxtjs/mcp-toolkit': - specifier: ^0.11.0 - version: 0.11.0(h3@1.15.9)(magicast@0.5.2)(zod@4.3.6) + specifier: ^0.12.0 + version: 0.12.0(h3@1.15.9)(magicast@0.5.2)(zod@4.3.6) '@nuxtjs/mdc': specifier: ^0.20.2 version: 0.20.2(magicast@0.5.2) @@ -185,13 +185,13 @@ importers: version: 2.0.1(@vueuse/core@14.2.1(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3)) nuxt: specifier: 4.x - version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2) + version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2) nuxt-llms: specifier: ^0.2.0 version: 0.2.0(magicast@0.5.2) nuxt-og-image: specifier: ^6.0.7 - version: 6.0.7(@resvg/resvg-js@2.6.2)(@resvg/resvg-wasm@2.6.2)(@takumi-rs/core@0.73.1)(@unhead/vue@2.1.12(vue@3.5.30(typescript@5.9.3)))(fontless@0.2.1(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)))(playwright-core@1.58.2)(sharp@0.34.5)(tailwindcss@4.2.2)(unifont@0.7.4)(unstorage@1.17.4(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3)) + version: 6.0.7(@resvg/resvg-js@2.6.2)(@resvg/resvg-wasm@2.6.2)(@takumi-rs/core@0.73.1)(@unhead/vue@2.1.12(vue@3.5.30(typescript@5.9.3)))(fontless@0.2.1(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)))(playwright-core@1.58.2)(sharp@0.34.5)(tailwindcss@4.2.2)(unifont@0.7.4)(unstorage@1.17.4(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3)) pkg-types: specifier: ^2.3.0 version: 2.3.0 @@ -261,6 +261,83 @@ packages: peerDependencies: '@types/json-schema': ^7.0.15 + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/core@3.973.25': + resolution: {integrity: sha512-TNrx7eq6nKNOO62HWPqoBqPLXEkW6nLZQGwjL6lq1jZtigWYbK1NbCnT7mKDzbLMHZfuOECUt3n6CzxjUW9HWQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.972.13': + resolution: {integrity: sha512-a6iFMh1pgUH0TdcouBppLJUfPM7Yd3R9S1xFodPtCRoLqCz2RQFA3qjA8x4112PVYXEd4/pHX2eihapq39w0rA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-host-header@3.972.8': + resolution: {integrity: sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-logger@3.972.8': + resolution: {integrity: sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.972.9': + resolution: {integrity: sha512-/Wt5+CT8dpTFQxEJ9iGy/UGrXr7p2wlIOEHvIr/YcHYByzoLjrqkYqXdJjd9UIgWjv7eqV2HnFJen93UTuwfTQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-user-agent@3.972.26': + resolution: {integrity: sha512-AilFIh4rI/2hKyyGN6XrB0yN96W2o7e7wyrPWCM6QjZM1mcC/pVkW3IWWRvuBWMpVP8Fg+rMpbzeLQ6dTM4gig==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/nested-clients@3.996.15': + resolution: {integrity: sha512-k6WAVNkub5DrU46iPQvH1m0xc1n+0dX79+i287tYJzf5g1yU2rX3uf4xNeL5JvK1NtYgfwMnsxHqhOXFBn367A==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/region-config-resolver@3.972.10': + resolution: {integrity: sha512-1dq9ToC6e070QvnVhhbAs3bb5r6cQ10gTVc6cyRV5uvQe7P138TV2uG2i6+Yok4bAkVAcx5AqkTEBUvWEtBlsQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.973.6': + resolution: {integrity: sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-endpoints@3.996.5': + resolution: {integrity: sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-locate-window@3.965.5': + resolution: {integrity: sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-user-agent-browser@3.972.8': + resolution: {integrity: sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==} + + '@aws-sdk/util-user-agent-node@3.973.12': + resolution: {integrity: sha512-8phW0TS8ntENJgDcFewYT/Q8dOmarpvSxEjATu2GUBAutiHr++oEGCiBUwxslCMNvwW2cAPZNT53S/ym8zm/gg==} + engines: {node: '>=20.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/xml-builder@3.972.16': + resolution: {integrity: sha512-iu2pyvaqmeatIJLURLqx9D+4jKAdTH20ntzB6BFwjyN7V960r4jK32mx0Zf7YbtOYAbmbtQfDNuL60ONinyw7A==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.2.4': + resolution: {integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==} + engines: {node: '>=18.0.0'} + '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} @@ -1499,15 +1576,18 @@ packages: resolution: {integrity: sha512-nRAQJbWjbiBvW6XRsG3Q6olYw2EKz7V1J6cDCHLCPbF1EyNhrAH/9aCNQaR5PYcoXPeRLpF86DIPBEnamghyHw==} engines: {node: '>=20.11.1'} - '@nuxtjs/mcp-toolkit@0.11.0': - resolution: {integrity: sha512-Fgj8GyhML4/sv2kltk1VNbRFEwp4YzP3V5GnUNLzzFQ95OelPpCUrNtf/wkN+8wXpjccJtTSX98TE+r3WLNLSw==} + '@nuxtjs/mcp-toolkit@0.12.0': + resolution: {integrity: sha512-OdfzEgM6ZtI/g/WbfKrrmJdATJFPvEOLTGteXT9uPEFPPmdjIO/V3qFFjD+Itvzij282uX/Nu/Tn95yJZl/Y7w==} peerDependencies: agents: '>=0.7.6' h3: ^1.15.6 + secure-exec: '>=0.1.0' zod: ^4.1.13 peerDependenciesMeta: agents: optional: true + secure-exec: + optional: true '@nuxtjs/mdc@0.20.2': resolution: {integrity: sha512-afAJKnXKdvDtoNOGARQMpZoGprL1T3OGnj+K9edJjX+WdhCwvVabBijhi8BAlpx+YzA/DpcZx8bDFZk/aoSJmA==} @@ -2804,6 +2884,178 @@ packages: resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} + '@smithy/abort-controller@4.2.12': + resolution: {integrity: sha512-xolrFw6b+2iYGl6EcOL7IJY71vvyZ0DJ3mcKtpykqPe2uscwtzDZJa1uVQXyP7w9Dd+kGwYnPbMsJrGISKiY/Q==} + engines: {node: '>=18.0.0'} + + '@smithy/config-resolver@4.4.13': + resolution: {integrity: sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg==} + engines: {node: '>=18.0.0'} + + '@smithy/core@3.23.12': + resolution: {integrity: sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.2.12': + resolution: {integrity: sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.3.15': + resolution: {integrity: sha512-T4jFU5N/yiIfrtrsb9uOQn7RdELdM/7HbyLNr6uO/mpkj1ctiVs7CihVr51w4LyQlXWDpXFn4BElf1WmQvZu/A==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-node@4.2.12': + resolution: {integrity: sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w==} + engines: {node: '>=18.0.0'} + + '@smithy/invalid-dependency@4.2.12': + resolution: {integrity: sha512-/4F1zb7Z8LOu1PalTdESFHR0RbPwHd3FcaG1sI3UEIriQTWakysgJr65lc1jj6QY5ye7aFsisajotH6UhWfm/g==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@4.2.2': + resolution: {integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-content-length@4.2.12': + resolution: {integrity: sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-endpoint@4.4.27': + resolution: {integrity: sha512-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-retry@4.4.44': + resolution: {integrity: sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-serde@4.2.15': + resolution: {integrity: sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-stack@4.2.12': + resolution: {integrity: sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw==} + engines: {node: '>=18.0.0'} + + '@smithy/node-config-provider@4.3.12': + resolution: {integrity: sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.5.0': + resolution: {integrity: sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A==} + engines: {node: '>=18.0.0'} + + '@smithy/property-provider@4.2.12': + resolution: {integrity: sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A==} + engines: {node: '>=18.0.0'} + + '@smithy/protocol-http@5.3.12': + resolution: {integrity: sha512-fit0GZK9I1xoRlR4jXmbLhoN0OdEpa96ul8M65XdmXnxXkuMxM0Y8HDT0Fh0Xb4I85MBvBClOzgSrV1X2s1Hxw==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-builder@4.2.12': + resolution: {integrity: sha512-6wTZjGABQufekycfDGMEB84BgtdOE/rCVTov+EDXQ8NHKTUNIp/j27IliwP7tjIU9LR+sSzyGBOXjeEtVgzCHg==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-parser@4.2.12': + resolution: {integrity: sha512-P2OdvrgiAKpkPNKlKUtWbNZKB1XjPxM086NeVhK+W+wI46pIKdWBe5QyXvhUm3MEcyS/rkLvY8rZzyUdmyDZBw==} + engines: {node: '>=18.0.0'} + + '@smithy/service-error-classification@4.2.12': + resolution: {integrity: sha512-LlP29oSQN0Tw0b6D0Xo6BIikBswuIiGYbRACy5ujw/JgWSzTdYj46U83ssf6Ux0GyNJVivs2uReU8pt7Eu9okQ==} + engines: {node: '>=18.0.0'} + + '@smithy/shared-ini-file-loader@4.4.7': + resolution: {integrity: sha512-HrOKWsUb+otTeo1HxVWeEb99t5ER1XrBi/xka2Wv6NVmTbuCUC1dvlrksdvxFtODLBjsC+PHK+fuy2x/7Ynyiw==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.3.12': + resolution: {integrity: sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==} + engines: {node: '>=18.0.0'} + + '@smithy/smithy-client@4.12.7': + resolution: {integrity: sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.13.1': + resolution: {integrity: sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g==} + engines: {node: '>=18.0.0'} + + '@smithy/url-parser@4.2.12': + resolution: {integrity: sha512-wOPKPEpso+doCZGIlr+e1lVI6+9VAKfL4kZWFgzVgGWY2hZxshNKod4l2LXS3PRC9otH/JRSjtEHqQ/7eLciRA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-base64@4.3.2': + resolution: {integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-browser@4.2.2': + resolution: {integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-node@4.2.3': + resolution: {integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@4.2.2': + resolution: {integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==} + engines: {node: '>=18.0.0'} + + '@smithy/util-config-provider@4.2.2': + resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-browser@4.3.43': + resolution: {integrity: sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-node@4.2.47': + resolution: {integrity: sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-endpoints@3.3.3': + resolution: {integrity: sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig==} + engines: {node: '>=18.0.0'} + + '@smithy/util-hex-encoding@4.2.2': + resolution: {integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-middleware@4.2.12': + resolution: {integrity: sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-retry@4.2.12': + resolution: {integrity: sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-stream@4.5.20': + resolution: {integrity: sha512-4yXLm5n/B5SRBR2p8cZ90Sbv4zL4NKsgxdzCzp/83cXw2KxLEumt5p+GAVyRNZgQOSrzXn9ARpO0lUe8XSlSDw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-uri-escape@4.2.2': + resolution: {integrity: sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@4.2.2': + resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==} + engines: {node: '>=18.0.0'} + + '@smithy/uuid@1.1.2': + resolution: {integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==} + engines: {node: '>=18.0.0'} + '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} @@ -2823,9 +3075,88 @@ packages: peerDependencies: eslint: ^9.0.0 || ^10.0.0 + '@swc/core-darwin-arm64@1.15.3': + resolution: {integrity: sha512-AXfeQn0CvcQ4cndlIshETx6jrAM45oeUrK8YeEY6oUZU/qzz0Id0CyvlEywxkWVC81Ajpd8TQQ1fW5yx6zQWkQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + + '@swc/core-darwin-x64@1.15.3': + resolution: {integrity: sha512-p68OeCz1ui+MZYG4wmfJGvcsAcFYb6Sl25H9TxWl+GkBgmNimIiRdnypK9nBGlqMZAcxngNPtnG3kEMNnvoJ2A==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + + '@swc/core-linux-arm-gnueabihf@1.15.3': + resolution: {integrity: sha512-Nuj5iF4JteFgwrai97mUX+xUOl+rQRHqTvnvHMATL/l9xE6/TJfPBpd3hk/PVpClMXG3Uvk1MxUFOEzM1JrMYg==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + + '@swc/core-linux-arm64-gnu@1.15.3': + resolution: {integrity: sha512-2Nc/s8jE6mW2EjXWxO/lyQuLKShcmTrym2LRf5Ayp3ICEMX6HwFqB1EzDhwoMa2DcUgmnZIalesq2lG3krrUNw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@swc/core-linux-arm64-musl@1.15.3': + resolution: {integrity: sha512-j4SJniZ/qaZ5g8op+p1G9K1z22s/EYGg1UXIb3+Cg4nsxEpF5uSIGEE4mHUfA70L0BR9wKT2QF/zv3vkhfpX4g==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@swc/core-linux-x64-gnu@1.15.3': + resolution: {integrity: sha512-aKttAZnz8YB1VJwPQZtyU8Uk0BfMP63iDMkvjhJzRZVgySmqt/apWSdnoIcZlUoGheBrcqbMC17GGUmur7OT5A==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@swc/core-linux-x64-musl@1.15.3': + resolution: {integrity: sha512-oe8FctPu1gnUsdtGJRO2rvOUIkkIIaHqsO9xxN0bTR7dFTlPTGi2Fhk1tnvXeyAvCPxLIcwD8phzKg6wLv9yug==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@swc/core-win32-arm64-msvc@1.15.3': + resolution: {integrity: sha512-L9AjzP2ZQ/Xh58e0lTRMLvEDrcJpR7GwZqAtIeNLcTK7JVE+QineSyHp0kLkO1rttCHyCy0U74kDTj0dRz6raA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + + '@swc/core-win32-ia32-msvc@1.15.3': + resolution: {integrity: sha512-B8UtogMzErUPDWUoKONSVBdsgKYd58rRyv2sHJWKOIMCHfZ22FVXICR4O/VwIYtlnZ7ahERcjayBHDlBZpR0aw==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + + '@swc/core-win32-x64-msvc@1.15.3': + resolution: {integrity: sha512-SpZKMR9QBTecHeqpzJdYEfgw30Oo8b/Xl6rjSzBt1g0ZsXyy60KLXrp6IagQyfTYqNYE/caDvwtF2FPn7pomog==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core@1.15.3': + resolution: {integrity: sha512-Qd8eBPkUFL4eAONgGjycZXj1jFCBW8Fd+xF0PzdTlBCWQIV1xnUT7B93wUANtW3KGjl3TRcOyxwSx/u/jyKw/Q==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': '>=0.5.17' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + '@swc/helpers@0.5.19': resolution: {integrity: sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA==} + '@swc/types@0.1.26': + resolution: {integrity: sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw==} + '@tailwindcss/node@4.2.2': resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==} @@ -3480,6 +3811,15 @@ packages: vue-router: optional: true + '@vercel/functions@3.4.3': + resolution: {integrity: sha512-kA14KIUVgAY6VXbhZ5jjY+s0883cV3cZqIU3WhrSRxuJ9KvxatMjtmzl0K23HK59oOUjYl7HaE/eYMmhmqpZzw==} + engines: {node: '>= 20'} + peerDependencies: + '@aws-sdk/credential-provider-web-identity': '*' + peerDependenciesMeta: + '@aws-sdk/credential-provider-web-identity': + optional: true + '@vercel/nft@1.4.0': resolution: {integrity: sha512-rr7JVnI7YGjA4lngucrWjZ7eCOJZZQaDHB+5NRGOuNc+k4PU2Lb9PmYm8uBmW8qichF7WkR2RmwmhXHBhx6wzw==} engines: {node: '>=20'} @@ -3489,6 +3829,10 @@ packages: resolution: {integrity: sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==} engines: {node: '>= 20'} + '@vercel/oidc@3.2.0': + resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==} + engines: {node: '>= 20'} + '@vercel/speed-insights@2.0.0': resolution: {integrity: sha512-jwkNcrTeafWxjmWq4AHBaptSqZiJkYU5adLC9QBSqeim0GcqDMgN5Ievh8OG1rJ6W3A4l1oiP7qr9CWxGuzu3w==} peerDependencies: @@ -3911,6 +4255,9 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} @@ -4830,6 +5177,13 @@ packages: fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fast-xml-builder@1.1.4: + resolution: {integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==} + + fast-xml-parser@5.5.8: + resolution: {integrity: sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==} + hasBin: true + fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} @@ -6313,6 +6667,10 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-expression-matcher@1.2.0: + resolution: {integrity: sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==} + engines: {node: '>=14.0.0'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -7190,6 +7548,9 @@ packages: strip-literal@3.1.0: resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + strnum@2.2.2: + resolution: {integrity: sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==} + structured-clone-es@2.0.0: resolution: {integrity: sha512-5UuAHmBLXYPCl22xWJrFuGmIhBKQzxISPVz6E7nmTmTcAOpUzlbjKJsRrCE4vADmMQ0dzeCnlWn9XufnAGf76Q==} @@ -8097,6 +8458,203 @@ snapshots: '@types/json-schema': 7.0.15 js-yaml: 4.1.1 + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-locate-window': 3.965.5 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + optional: true + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.6 + tslib: 2.8.1 + optional: true + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + optional: true + + '@aws-sdk/core@3.973.25': + dependencies: + '@aws-sdk/types': 3.973.6 + '@aws-sdk/xml-builder': 3.972.16 + '@smithy/core': 3.23.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/property-provider': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/signature-v4': 5.3.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/util-base64': 4.3.2 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + optional: true + + '@aws-sdk/credential-provider-web-identity@3.972.13': + dependencies: + '@aws-sdk/core': 3.973.25 + '@aws-sdk/nested-clients': 3.996.15 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + optional: true + + '@aws-sdk/middleware-host-header@3.972.8': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@aws-sdk/middleware-logger@3.972.8': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@aws-sdk/middleware-recursion-detection@3.972.9': + dependencies: + '@aws-sdk/types': 3.973.6 + '@aws/lambda-invoke-store': 0.2.4 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@aws-sdk/middleware-user-agent@3.972.26': + dependencies: + '@aws-sdk/core': 3.973.25 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-endpoints': 3.996.5 + '@smithy/core': 3.23.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-retry': 4.2.12 + tslib: 2.8.1 + optional: true + + '@aws-sdk/nested-clients@3.996.15': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.25 + '@aws-sdk/middleware-host-header': 3.972.8 + '@aws-sdk/middleware-logger': 3.972.8 + '@aws-sdk/middleware-recursion-detection': 3.972.9 + '@aws-sdk/middleware-user-agent': 3.972.26 + '@aws-sdk/region-config-resolver': 3.972.10 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-endpoints': 3.996.5 + '@aws-sdk/util-user-agent-browser': 3.972.8 + '@aws-sdk/util-user-agent-node': 3.973.12 + '@smithy/config-resolver': 4.4.13 + '@smithy/core': 3.23.12 + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/hash-node': 4.2.12 + '@smithy/invalid-dependency': 4.2.12 + '@smithy/middleware-content-length': 4.2.12 + '@smithy/middleware-endpoint': 4.4.27 + '@smithy/middleware-retry': 4.4.44 + '@smithy/middleware-serde': 4.2.15 + '@smithy/middleware-stack': 4.2.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/node-http-handler': 4.5.0 + '@smithy/protocol-http': 5.3.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.43 + '@smithy/util-defaults-mode-node': 4.2.47 + '@smithy/util-endpoints': 3.3.3 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-retry': 4.2.12 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + optional: true + + '@aws-sdk/region-config-resolver@3.972.10': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/config-resolver': 4.4.13 + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@aws-sdk/types@3.973.6': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@aws-sdk/util-endpoints@3.996.5': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-endpoints': 3.3.3 + tslib: 2.8.1 + optional: true + + '@aws-sdk/util-locate-window@3.965.5': + dependencies: + tslib: 2.8.1 + optional: true + + '@aws-sdk/util-user-agent-browser@3.972.8': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 + bowser: 2.14.1 + tslib: 2.8.1 + optional: true + + '@aws-sdk/util-user-agent-node@3.973.12': + dependencies: + '@aws-sdk/middleware-user-agent': 3.972.26 + '@aws-sdk/types': 3.973.6 + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-config-provider': 4.2.2 + tslib: 2.8.1 + optional: true + + '@aws-sdk/xml-builder@3.972.16': + dependencies: + '@smithy/types': 4.13.1 + fast-xml-parser: 5.5.8 + tslib: 2.8.1 + optional: true + + '@aws/lambda-invoke-store@0.2.4': + optional: true + '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -9264,13 +9822,13 @@ snapshots: - utf-8-validate - vite - '@nuxt/fonts@0.14.0(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(magicast@0.5.2)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))': + '@nuxt/fonts@0.14.0(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(magicast@0.5.2)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@nuxt/devtools-kit': 3.2.4(magicast@0.5.2)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) '@nuxt/kit': 4.4.2(magicast@0.5.2) consola: 3.4.2 defu: 6.1.4 - fontless: 0.2.1(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) + fontless: 0.2.1(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) h3: 1.15.9 magic-regexp: 0.10.0 ofetch: 1.5.1 @@ -9280,7 +9838,7 @@ snapshots: ufo: 1.6.3 unifont: 0.7.4 unplugin: 3.0.0 - unstorage: 1.17.4(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) + unstorage: 1.17.4(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -9325,7 +9883,7 @@ snapshots: - vite - vue - '@nuxt/image@2.0.0(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(magicast@0.5.2)': + '@nuxt/image@2.0.0(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(magicast@0.5.2)': dependencies: '@nuxt/kit': 4.4.2(magicast@0.5.2) consola: 3.4.2 @@ -9338,7 +9896,7 @@ snapshots: std-env: 3.10.0 ufo: 1.6.3 optionalDependencies: - ipx: 3.1.1(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) + ipx: 3.1.1(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -9412,7 +9970,7 @@ snapshots: transitivePeerDependencies: - magicast - '@nuxt/nitro-server@4.4.2(@babel/core@7.29.0)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2))(typescript@5.9.3)': + '@nuxt/nitro-server@4.4.2(@babel/core@7.29.0)(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2))(typescript@5.9.3)': dependencies: '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) '@nuxt/devalue': 2.0.2 @@ -9430,8 +9988,8 @@ snapshots: impound: 1.1.5 klona: 2.0.6 mocked-exports: 0.1.1 - nitropack: 2.13.1(better-sqlite3@12.8.0) - nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2) + nitropack: 2.13.1(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(better-sqlite3@12.8.0) + nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2) nypm: 0.6.5 ohash: 2.0.11 pathe: 2.0.3 @@ -9440,7 +9998,7 @@ snapshots: std-env: 4.0.0 ufo: 1.6.3 unctx: 2.5.0 - unstorage: 1.17.4(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) + unstorage: 1.17.4(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) vue: 3.5.30(typescript@5.9.3) vue-bundle-renderer: 2.2.0 vue-devtools-stub: 0.1.0 @@ -9498,13 +10056,13 @@ snapshots: rc9: 3.0.0 std-env: 3.10.0 - '@nuxt/ui@4.5.1(@nuxt/content@3.12.0(better-sqlite3@12.8.0)(magicast@0.5.2))(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.7)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.8.0))(embla-carousel@8.6.0)(ioredis@5.10.1)(magicast@0.5.2)(tailwindcss@4.2.2)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-router@4.6.4(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3))(yjs@13.6.30)(zod@4.3.6)': + '@nuxt/ui@4.5.1(@nuxt/content@3.12.0(better-sqlite3@12.8.0)(magicast@0.5.2))(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.7)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30))(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(change-case@5.4.4)(db0@0.3.4(better-sqlite3@12.8.0))(embla-carousel@8.6.0)(ioredis@5.10.1)(magicast@0.5.2)(tailwindcss@4.2.2)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-router@4.6.4(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3))(yjs@13.6.30)(zod@4.3.6)': dependencies: '@floating-ui/dom': 1.7.6 '@iconify/vue': 5.0.0(vue@3.5.30(typescript@5.9.3)) '@internationalized/date': 3.12.0 '@internationalized/number': 3.6.5 - '@nuxt/fonts': 0.14.0(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(magicast@0.5.2)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) + '@nuxt/fonts': 0.14.0(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(magicast@0.5.2)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) '@nuxt/icon': 2.2.1(magicast@0.5.2)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3)) '@nuxt/kit': 4.4.2(magicast@0.5.2) '@nuxt/schema': 4.4.2 @@ -9612,7 +10170,7 @@ snapshots: - vue - yjs - '@nuxt/vite-builder@4.4.2(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@types/node@25.5.0)(eslint@10.0.3(jiti@2.6.1))(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2))(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.30(typescript@5.9.3))(yaml@2.8.2)': + '@nuxt/vite-builder@4.4.2(f52e8cd5b3f246b958ca0cfda5abd4c2)': dependencies: '@nuxt/kit': 4.4.2(magicast@0.5.2) '@rollup/plugin-replace': 6.0.3(rollup@4.59.0) @@ -9630,7 +10188,7 @@ snapshots: magic-string: 0.30.21 mlly: 1.8.2 mocked-exports: 0.1.1 - nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2) + nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2) nypm: 0.6.5 pathe: 2.0.3 pkg-types: 2.3.0 @@ -9681,7 +10239,7 @@ snapshots: transitivePeerDependencies: - magicast - '@nuxtjs/i18n@10.2.3(@vue/compiler-dom@3.5.30)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(rollup@4.59.0)(vue@3.5.30(typescript@5.9.3))': + '@nuxtjs/i18n@10.2.3(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(@vue/compiler-dom@3.5.30)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(magicast@0.5.2)(rollup@4.59.0)(vue@3.5.30(typescript@5.9.3))': dependencies: '@intlify/core': 11.3.0 '@intlify/h3': 0.7.4 @@ -9708,7 +10266,7 @@ snapshots: ufo: 1.6.3 unplugin: 2.3.11 unplugin-vue-router: 0.16.2(@vue/compiler-sfc@3.5.30)(vue-router@4.6.4(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3)) - unstorage: 1.17.4(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) + unstorage: 1.17.4(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) vue-i18n: 11.3.0(vue@3.5.30(typescript@5.9.3)) vue-router: 4.6.4(vue@3.5.30(typescript@5.9.3)) transitivePeerDependencies: @@ -9739,7 +10297,7 @@ snapshots: - uploadthing - vue - '@nuxtjs/mcp-toolkit@0.11.0(h3@1.15.9)(magicast@0.5.2)(zod@4.3.6)': + '@nuxtjs/mcp-toolkit@0.12.0(h3@1.15.9)(magicast@0.5.2)(zod@4.3.6)': dependencies: '@modelcontextprotocol/sdk': 1.27.1(zod@4.3.6) '@nuxt/kit': 4.4.2(magicast@0.5.2) @@ -10645,6 +11203,324 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} + '@smithy/abort-controller@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@smithy/config-resolver@4.4.13': + dependencies: + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-endpoints': 3.3.3 + '@smithy/util-middleware': 4.2.12 + tslib: 2.8.1 + optional: true + + '@smithy/core@3.23.12': + dependencies: + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-stream': 4.5.20 + '@smithy/util-utf8': 4.2.2 + '@smithy/uuid': 1.1.2 + tslib: 2.8.1 + optional: true + + '@smithy/credential-provider-imds@4.2.12': + dependencies: + '@smithy/node-config-provider': 4.3.12 + '@smithy/property-provider': 4.2.12 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + tslib: 2.8.1 + optional: true + + '@smithy/fetch-http-handler@5.3.15': + dependencies: + '@smithy/protocol-http': 5.3.12 + '@smithy/querystring-builder': 4.2.12 + '@smithy/types': 4.13.1 + '@smithy/util-base64': 4.3.2 + tslib: 2.8.1 + optional: true + + '@smithy/hash-node@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + optional: true + + '@smithy/invalid-dependency@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@smithy/is-array-buffer@4.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@smithy/middleware-content-length@4.2.12': + dependencies: + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@smithy/middleware-endpoint@4.4.27': + dependencies: + '@smithy/core': 3.23.12 + '@smithy/middleware-serde': 4.2.15 + '@smithy/node-config-provider': 4.3.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-middleware': 4.2.12 + tslib: 2.8.1 + optional: true + + '@smithy/middleware-retry@4.4.44': + dependencies: + '@smithy/node-config-provider': 4.3.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/service-error-classification': 4.2.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-retry': 4.2.12 + '@smithy/uuid': 1.1.2 + tslib: 2.8.1 + optional: true + + '@smithy/middleware-serde@4.2.15': + dependencies: + '@smithy/core': 3.23.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@smithy/middleware-stack@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@smithy/node-config-provider@4.3.12': + dependencies: + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@smithy/node-http-handler@4.5.0': + dependencies: + '@smithy/abort-controller': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/querystring-builder': 4.2.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@smithy/property-provider@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@smithy/protocol-http@5.3.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@smithy/querystring-builder@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + '@smithy/util-uri-escape': 4.2.2 + tslib: 2.8.1 + optional: true + + '@smithy/querystring-parser@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@smithy/service-error-classification@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + optional: true + + '@smithy/shared-ini-file-loader@4.4.7': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@smithy/signature-v4@5.3.12': + dependencies: + '@smithy/is-array-buffer': 4.2.2 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-uri-escape': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + optional: true + + '@smithy/smithy-client@4.12.7': + dependencies: + '@smithy/core': 3.23.12 + '@smithy/middleware-endpoint': 4.4.27 + '@smithy/middleware-stack': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-stream': 4.5.20 + tslib: 2.8.1 + optional: true + + '@smithy/types@4.13.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@smithy/url-parser@4.2.12': + dependencies: + '@smithy/querystring-parser': 4.2.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@smithy/util-base64@4.3.2': + dependencies: + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + optional: true + + '@smithy/util-body-length-browser@4.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@smithy/util-body-length-node@4.2.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + optional: true + + '@smithy/util-buffer-from@4.2.2': + dependencies: + '@smithy/is-array-buffer': 4.2.2 + tslib: 2.8.1 + optional: true + + '@smithy/util-config-provider@4.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@smithy/util-defaults-mode-browser@4.3.43': + dependencies: + '@smithy/property-provider': 4.2.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@smithy/util-defaults-mode-node@4.2.47': + dependencies: + '@smithy/config-resolver': 4.4.13 + '@smithy/credential-provider-imds': 4.2.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/property-provider': 4.2.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@smithy/util-endpoints@3.3.3': + dependencies: + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@smithy/util-hex-encoding@4.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@smithy/util-middleware@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@smithy/util-retry@4.2.12': + dependencies: + '@smithy/service-error-classification': 4.2.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + optional: true + + '@smithy/util-stream@4.5.20': + dependencies: + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/node-http-handler': 4.5.0 + '@smithy/types': 4.13.1 + '@smithy/util-base64': 4.3.2 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + optional: true + + '@smithy/util-uri-escape@4.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + optional: true + + '@smithy/util-utf8@4.2.2': + dependencies: + '@smithy/util-buffer-from': 4.2.2 + tslib: 2.8.1 + optional: true + + '@smithy/uuid@1.1.2': + dependencies: + tslib: 2.8.1 + optional: true + '@socket.io/component-emitter@3.1.2': {} '@speed-highlight/core@1.2.15': {} @@ -10663,10 +11539,66 @@ snapshots: estraverse: 5.3.0 picomatch: 4.0.3 + '@swc/core-darwin-arm64@1.15.3': + optional: true + + '@swc/core-darwin-x64@1.15.3': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.15.3': + optional: true + + '@swc/core-linux-arm64-gnu@1.15.3': + optional: true + + '@swc/core-linux-arm64-musl@1.15.3': + optional: true + + '@swc/core-linux-x64-gnu@1.15.3': + optional: true + + '@swc/core-linux-x64-musl@1.15.3': + optional: true + + '@swc/core-win32-arm64-msvc@1.15.3': + optional: true + + '@swc/core-win32-ia32-msvc@1.15.3': + optional: true + + '@swc/core-win32-x64-msvc@1.15.3': + optional: true + + '@swc/core@1.15.3(@swc/helpers@0.5.19)': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.26 + optionalDependencies: + '@swc/core-darwin-arm64': 1.15.3 + '@swc/core-darwin-x64': 1.15.3 + '@swc/core-linux-arm-gnueabihf': 1.15.3 + '@swc/core-linux-arm64-gnu': 1.15.3 + '@swc/core-linux-arm64-musl': 1.15.3 + '@swc/core-linux-x64-gnu': 1.15.3 + '@swc/core-linux-x64-musl': 1.15.3 + '@swc/core-win32-arm64-msvc': 1.15.3 + '@swc/core-win32-ia32-msvc': 1.15.3 + '@swc/core-win32-x64-msvc': 1.15.3 + '@swc/helpers': 0.5.19 + optional: true + + '@swc/counter@0.1.3': + optional: true + '@swc/helpers@0.5.19': dependencies: tslib: 2.8.1 + '@swc/types@0.1.26': + dependencies: + '@swc/counter': 0.1.3 + optional: true + '@tailwindcss/node@4.2.2': dependencies: '@jridgewell/remapping': 2.3.5 @@ -11242,12 +12174,19 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vercel/analytics@2.0.1(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2))(vue-router@4.6.4(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3))': + '@vercel/analytics@2.0.1(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2))(vue-router@4.6.4(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3))': optionalDependencies: - nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2) + nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2) vue: 3.5.30(typescript@5.9.3) vue-router: 4.6.4(vue@3.5.30(typescript@5.9.3)) + '@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13)': + dependencies: + '@vercel/oidc': 3.2.0 + optionalDependencies: + '@aws-sdk/credential-provider-web-identity': 3.972.13 + optional: true + '@vercel/nft@1.4.0(rollup@4.59.0)': dependencies: '@mapbox/node-pre-gyp': 2.0.3 @@ -11269,9 +12208,12 @@ snapshots: '@vercel/oidc@3.1.0': {} - '@vercel/speed-insights@2.0.0(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2))(vue-router@4.6.4(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3))': + '@vercel/oidc@3.2.0': + optional: true + + '@vercel/speed-insights@2.0.0(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2))(vue-router@4.6.4(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3))': optionalDependencies: - nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2) + nuxt: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2) vue: 3.5.30(typescript@5.9.3) vue-router: 4.6.4(vue@3.5.30(typescript@5.9.3)) @@ -11697,6 +12639,9 @@ snapshots: boolbase@1.0.0: {} + bowser@2.14.1: + optional: true + brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 @@ -12703,6 +13648,18 @@ snapshots: fast-uri@3.1.0: {} + fast-xml-builder@1.1.4: + dependencies: + path-expression-matcher: 1.2.0 + optional: true + + fast-xml-parser@5.5.8: + dependencies: + fast-xml-builder: 1.1.4 + path-expression-matcher: 1.2.0 + strnum: 2.2.2 + optional: true + fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -12777,7 +13734,7 @@ snapshots: dependencies: tiny-inflate: 1.0.3 - fontless@0.2.1(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)): + fontless@0.2.1(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: consola: 3.4.2 css-tree: 3.2.1 @@ -12791,7 +13748,7 @@ snapshots: pathe: 2.0.3 ufo: 1.6.3 unifont: 0.7.4 - unstorage: 1.17.4(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) + unstorage: 1.17.4(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) optionalDependencies: vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: @@ -13255,7 +14212,7 @@ snapshots: ipaddr.js@1.9.1: {} - ipx@3.1.1(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1): + ipx@3.1.1(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1): dependencies: '@fastify/accept-negotiator': 2.0.1 citty: 0.1.6 @@ -13271,7 +14228,7 @@ snapshots: sharp: 0.34.5 svgo: 4.0.1 ufo: 1.6.3 - unstorage: 1.17.4(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) + unstorage: 1.17.4(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) xss: 1.0.15 transitivePeerDependencies: - '@azure/app-configuration' @@ -14137,7 +15094,7 @@ snapshots: dependencies: type-fest: 2.19.0 - nitropack@2.13.1(better-sqlite3@12.8.0): + nitropack@2.13.1(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(better-sqlite3@12.8.0): dependencies: '@cloudflare/kv-asset-handler': 0.4.2 '@rollup/plugin-alias': 6.0.0(rollup@4.59.0) @@ -14204,7 +15161,7 @@ snapshots: unenv: 2.0.0-rc.24 unimport: 5.6.0 unplugin-utils: 0.3.1 - unstorage: 1.17.4(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) + unstorage: 1.17.4(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) untyped: 2.0.0 unwasm: 0.5.3 youch: 4.1.0 @@ -14313,7 +15270,7 @@ snapshots: transitivePeerDependencies: - magicast - nuxt-og-image@6.0.7(@resvg/resvg-js@2.6.2)(@resvg/resvg-wasm@2.6.2)(@takumi-rs/core@0.73.1)(@unhead/vue@2.1.12(vue@3.5.30(typescript@5.9.3)))(fontless@0.2.1(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)))(playwright-core@1.58.2)(sharp@0.34.5)(tailwindcss@4.2.2)(unifont@0.7.4)(unstorage@1.17.4(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3)): + nuxt-og-image@6.0.7(@resvg/resvg-js@2.6.2)(@resvg/resvg-wasm@2.6.2)(@takumi-rs/core@0.73.1)(@unhead/vue@2.1.12(vue@3.5.30(typescript@5.9.3)))(fontless@0.2.1(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)))(playwright-core@1.58.2)(sharp@0.34.5)(tailwindcss@4.2.2)(unifont@0.7.4)(unstorage@1.17.4(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3)): dependencies: '@clack/prompts': 1.1.0 '@nuxt/devtools-kit': 3.2.4(magicast@0.5.2)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) @@ -14347,12 +15304,12 @@ snapshots: ufo: 1.6.3 ultrahtml: 1.6.0 unplugin: 3.0.0 - unstorage: 1.17.4(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) + unstorage: 1.17.4(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) optionalDependencies: '@resvg/resvg-js': 2.6.2 '@resvg/resvg-wasm': 2.6.2 '@takumi-rs/core': 0.73.1 - fontless: 0.2.1(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) + fontless: 0.2.1(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) playwright-core: 1.58.2 sharp: 0.34.5 tailwindcss: 4.2.2 @@ -14389,7 +15346,7 @@ snapshots: - vite - vue - nuxt-studio@1.5.1(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(magicast@0.5.2)(vue@3.5.30(typescript@5.9.3)): + nuxt-studio@1.5.1(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(magicast@0.5.2)(vue@3.5.30(typescript@5.9.3)): dependencies: '@ai-sdk/gateway': 3.0.66(zod@4.3.6) '@ai-sdk/vue': 3.0.116(vue@3.5.30(typescript@5.9.3))(zod@4.3.6) @@ -14399,13 +15356,13 @@ snapshots: ai: 6.0.116(zod@4.3.6) defu: 6.1.4 destr: 2.0.5 - ipx: 3.1.1(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) + ipx: 3.1.1(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) js-yaml: 4.1.1 minimatch: 10.2.4 nuxt-component-meta: 0.17.2(magicast@0.5.2) remark-mdc: 3.10.0 shiki: 3.23.0 - unstorage: 1.17.4(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) + unstorage: 1.17.4(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1) zod: 4.3.6 zod-to-json-schema: 3.25.1(zod@4.3.6) transitivePeerDependencies: @@ -14432,16 +15389,16 @@ snapshots: - uploadthing - vue - nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2): + nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2): dependencies: '@dxup/nuxt': 0.4.0(magicast@0.5.2)(typescript@5.9.3) '@nuxt/cli': 3.34.0(@nuxt/schema@4.4.2)(cac@6.7.14)(magicast@0.5.2) '@nuxt/devtools': 3.2.4(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.30(typescript@5.9.3)) '@nuxt/kit': 4.4.2(magicast@0.5.2) - '@nuxt/nitro-server': 4.4.2(@babel/core@7.29.0)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2))(typescript@5.9.3) + '@nuxt/nitro-server': 4.4.2(@babel/core@7.29.0)(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1)(magicast@0.5.2)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2))(typescript@5.9.3) '@nuxt/schema': 4.4.2 '@nuxt/telemetry': 2.7.0(@nuxt/kit@4.4.2(magicast@0.5.2)) - '@nuxt/vite-builder': 4.4.2(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@types/node@25.5.0)(eslint@10.0.3(jiti@2.6.1))(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(nuxt@4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.30)(better-sqlite3@12.8.0)(db0@0.3.4(better-sqlite3@12.8.0))(eslint@10.0.3(jiti@2.6.1))(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(meow@13.2.0)(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.2))(optionator@0.9.4)(rollup-plugin-visualizer@6.0.11(rollup@4.59.0))(rollup@4.59.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.30(typescript@5.9.3))(yaml@2.8.2) + '@nuxt/vite-builder': 4.4.2(f52e8cd5b3f246b958ca0cfda5abd4c2) '@unhead/vue': 2.1.12(vue@3.5.30(typescript@5.9.3)) '@vue/shared': 3.5.30 c12: 3.3.3(magicast@0.5.2) @@ -14890,6 +15847,9 @@ snapshots: path-exists@4.0.0: {} + path-expression-matcher@1.2.0: + optional: true + path-key@3.1.1: {} path-key@4.0.0: {} @@ -15975,6 +16935,9 @@ snapshots: dependencies: js-tokens: 9.0.1 + strnum@2.2.2: + optional: true + structured-clone-es@2.0.0: {} stylehacks@7.0.8(postcss@8.5.8): @@ -16143,7 +17106,7 @@ snapshots: tslib@2.8.1: {} - tsup@8.5.1(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2): + tsup@8.5.1(@swc/core@1.15.3(@swc/helpers@0.5.19))(jiti@2.6.1)(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2): dependencies: bundle-require: 5.1.0(esbuild@0.27.4) cac: 6.7.14 @@ -16163,6 +17126,7 @@ snapshots: tinyglobby: 0.2.15 tree-kill: 1.2.2 optionalDependencies: + '@swc/core': 1.15.3(@swc/helpers@0.5.19) postcss: 8.5.8 typescript: 5.9.3 transitivePeerDependencies: @@ -16416,7 +17380,7 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - unstorage@1.17.4(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1): + unstorage@1.17.4(@vercel/functions@3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13))(db0@0.3.4(better-sqlite3@12.8.0))(ioredis@5.10.1): dependencies: anymatch: 3.1.3 chokidar: 5.0.0 @@ -16427,6 +17391,7 @@ snapshots: ofetch: 1.5.1 ufo: 1.6.3 optionalDependencies: + '@vercel/functions': 3.4.3(@aws-sdk/credential-provider-web-identity@3.972.13) db0: 0.3.4(better-sqlite3@12.8.0) ioredis: 5.10.1 From 829886f9e84d971fe5f7717d4fa65b9dac758dec Mon Sep 17 00:00:00 2001 From: Baptiste Leproux Date: Fri, 27 Mar 2026 22:44:35 +0100 Subject: [PATCH 03/20] hook signaure + diff --- .../runtime/server/api/webhook/github.post.ts | 45 ++++++++++-- .../runtime/server/utils/github-diff.ts | 70 +++++++++++++++++++ .../runtime/server/utils/github-webhook.ts | 19 +++++ 3 files changed, 129 insertions(+), 5 deletions(-) create mode 100644 layer/modules/assistant/runtime/server/utils/github-diff.ts create mode 100644 layer/modules/assistant/runtime/server/utils/github-webhook.ts diff --git a/layer/modules/assistant/runtime/server/api/webhook/github.post.ts b/layer/modules/assistant/runtime/server/api/webhook/github.post.ts index 3e49f8a89..b9d4f23fa 100644 --- a/layer/modules/assistant/runtime/server/api/webhook/github.post.ts +++ b/layer/modules/assistant/runtime/server/api/webhook/github.post.ts @@ -1,11 +1,46 @@ +import { getInstallationToken } from '../../utils/github-auth' +import { fetchPrDiff, filterDiff, logDiff } from '../../utils/github-diff' +import { verifyWebhookSignature } from '../../utils/github-webhook' + export default defineEventHandler(async (event) => { - const payload = await readBody(event) + const githubEvent = getHeader(event, 'x-github-event') + if (githubEvent !== 'pull_request') return { ok: true } + + const config = useRuntimeConfig() + + const rawBody = await readRawBody(event, false) + if (!rawBody) return sendError(event, createError({ statusCode: 400 })) + + const signature = getHeader(event, 'x-hub-signature-256') ?? '' + if (!verifyWebhookSignature(config.webhookSecret, rawBody, signature)) + return sendError(event, createError({ statusCode: 401, message: 'Invalid signature' })) + + const payload = JSON.parse(rawBody.toString('utf8')) + + if (!['opened', 'reopened', 'synchronize'].includes(payload.action)) return { ok: true } + + const { installation, pull_request, repository } = payload + + if (pull_request.base.ref !== repository.default_branch) return { ok: true } + const owner: string = repository.owner.login + const repo: string = repository.name + const _branch: string = pull_request.head.ref + const baseSha: string = pull_request.base.sha + const headSha: string = pull_request.head.sha + + const token = await getInstallationToken(installation.id) + + const rawDiff = await fetchPrDiff(owner, repo, baseSha, headSha, token) + + const filteredDiff = filterDiff(rawDiff) - const action = payload?.action - const repo = payload?.repository?.full_name - const pr = payload?.pull_request?.number + if (!filteredDiff) { + console.log('[doc-agent] No relevant files in diff, skipping') + return { ok: true } + } - console.log(`[doc-agent] webhook received — action: ${action}, repo: ${repo}, PR: #${pr}`) + logDiff(filteredDiff) + // TODO Task 5: start(docAgentWorkflow, [{ branch, filteredDiff }]) return { ok: true } }) diff --git a/layer/modules/assistant/runtime/server/utils/github-diff.ts b/layer/modules/assistant/runtime/server/utils/github-diff.ts new file mode 100644 index 000000000..a9865467c --- /dev/null +++ b/layer/modules/assistant/runtime/server/utils/github-diff.ts @@ -0,0 +1,70 @@ +// ~12 000 tokens at 4 chars/token +const TOKEN_CHAR_LIMIT = 48_000 + +const KEEP_EXTENSIONS = /\.(?:ts|vue|js)$/ +const DROP_PATTERNS = /package-lock\.json|pnpm-lock\.yaml|yarn\.lock|\.d\.ts$|\/dist\// + +export function filterDiff(rawDiff: string): string { + const chunks = rawDiff.split(/(?=^diff --git )/m) + const kept: string[] = [] + const skipped: string[] = [] + let totalChars = 0 + + for (const chunk of chunks) { + if (!chunk.startsWith('diff --git ')) continue + + const pathMatch = chunk.match(/^diff --git a\/.+ b\/(.+)$/m) + const filePath = pathMatch?.[1] ?? '' + + if (!KEEP_EXTENSIONS.test(filePath) || DROP_PATTERNS.test(filePath) || /^deleted file mode/m.test(chunk)) { + skipped.push(filePath) + continue + } + + if (totalChars + chunk.length > TOKEN_CHAR_LIMIT) { + skipped.push(filePath) + continue + } + + kept.push(chunk) + totalChars += chunk.length + } + + if (skipped.length > 0) + console.warn(`[doc-agent] Skipped files: ${skipped.filter(Boolean).join(', ')}`) + + return kept.join('') +} + +export function logDiff(filteredDiff: string): void { + const chunks = filteredDiff.split(/(?=^diff --git )/m).filter(c => c.startsWith('diff --git ')) + + const files = chunks.map((chunk) => { + const pathMatch = chunk.match(/^diff --git a\/.+ b\/(.+)$/m) + const filePath = pathMatch?.[1] ?? '?' + const added = (chunk.match(/^\+(?!\+\+)/mg) ?? []).length + const removed = (chunk.match(/^-(?!--)/mg) ?? []).length + return { filePath, added, removed } + }) + + const totalAdded = files.reduce((s, f) => s + f.added, 0) + const totalRemoved = files.reduce((s, f) => s + f.removed, 0) + + console.log(`[doc-agent] Diff: ${files.length} file(s) +${totalAdded} -${totalRemoved}`) + for (const { filePath, added, removed } of files) + console.log(` ${filePath} +${added} -${removed}`) +} + +export async function fetchPrDiff(owner: string, repo: string, baseSha: string, headSha: string, token: string): Promise { + return $fetch( + `https://api.github.com/repos/${owner}/${repo}/compare/${baseSha}...${headSha}`, + { + responseType: 'text', + headers: { + 'Authorization': `Bearer ${token}`, + 'Accept': 'application/vnd.github.v3.diff', + 'User-Agent': 'docus-doc-agent', + }, + }, + ) +} diff --git a/layer/modules/assistant/runtime/server/utils/github-webhook.ts b/layer/modules/assistant/runtime/server/utils/github-webhook.ts new file mode 100644 index 000000000..a2a2f77be --- /dev/null +++ b/layer/modules/assistant/runtime/server/utils/github-webhook.ts @@ -0,0 +1,19 @@ +import { createHmac, timingSafeEqual } from 'node:crypto' + +export function verifyWebhookSignature(secret: string, body: Buffer, signature: string): boolean { + const trimmedSecret = secret.trim() + + // Try both buffer and utf8-string HMAC — some proxies re-encode the body + const hmacBuf = createHmac('sha256', trimmedSecret) + hmacBuf.update(body) + const expectedFromBuffer = `sha256=${hmacBuf.digest('hex')}` + const hmacStr = createHmac('sha256', trimmedSecret) + hmacStr.update(body.toString('utf8')) + const received = signature.trim() + const expected = Buffer.from(expectedFromBuffer) + const receivedBuf = Buffer.from(received) + + if (expected.length !== receivedBuf.length) return false + + return timingSafeEqual(expected, receivedBuf) +} From a1ecc3f021d0c5b22aec7153e186f43b484caeba Mon Sep 17 00:00:00 2001 From: "docus-dev[bot]" <271635299+docus-dev[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2026 23:13:22 +0000 Subject: [PATCH 04/20] docs: update 3 documentation files content/en/ai/assistant.md, content/en/ai/review.md, content/en/ai/mcp.md --- content/en/ai/assistant.md | 520 +++++++++++++++++++++++++++++++++++++ content/en/ai/mcp.md | 367 ++++++++++++++++++++++++++ content/en/ai/review.md | 89 +++++++ 3 files changed, 976 insertions(+) create mode 100644 content/en/ai/assistant.md create mode 100644 content/en/ai/mcp.md create mode 100644 content/en/ai/review.md diff --git a/content/en/ai/assistant.md b/content/en/ai/assistant.md new file mode 100644 index 000000000..fcc1e5887 --- /dev/null +++ b/content/en/ai/assistant.md @@ -0,0 +1,520 @@ +# Assistant + +> Add AI-powered chat to your docs that answers questions, cites sources, and generates code examples. + +## About the Assistant + +The assistant answers questions about your documentation through natural language queries. It is embedded directly in your documentation site, so users can find answers quickly and succeed with your product. + +When users ask questions, the assistant: + +- **Searches and retrieves** relevant content from your documentation using an [MCP server](/en/ai/mcp). +- **Cites sources** with navigable links to take users directly to referenced pages. +- **Generates copyable code examples** to help users implement solutions from your documentation. + + + +The assistant requires an [AI Gateway](https://vercel.com/docs/ai-gateway) API key to function. + + + +## How It Works + +The assistant uses a multi-agent architecture: + +1. **Main Agent** - Receives user questions and decides when to search documentation +2. **Search Agent** - Uses [MCP server](/en/ai/mcp) tools to find relevant content +3. **Response Generation** - Synthesizes information into helpful, conversational answers + +By default, the assistant connects to your documentation's built-in MCP server at `/mcp`, giving it access to all your pages without additional configuration. You can also connect to an external MCP server if needed. + +## Quick Start + +### 1. Get an API Key + +Get an API key from [Vercel AI Gateway](https://vercel.com/~/ai/api-keys). AI Gateway works with multiple AI providers (OpenAI, Anthropic, Google, and more) through a unified API. + +### 2. Set Environment Variable + +Add your API key to your environment: + +```bash [.env] +AI_GATEWAY_API_KEY=your-api-key +``` + +### 3. Deploy + +That's it! The assistant is automatically enabled when an API key is detected. Deploy your documentation and the assistant will be available to your users. + +## Using the Assistant + +Users can interact with the assistant in multiple ways: + +### Floating Input + +On documentation pages, a floating input appears at the bottom of the screen. Users can type their questions directly and press Enter to get answers. + + + +Use the keyboard shortcut + + + + + + + + + + + + to focus the floating input. + + + +### Explain with AI + +Each documentation page includes an **Explain with AI** button in the table of contents sidebar. Clicking this button opens the assistant with the current page as context, asking it to explain the content. + +### Slideover Chat + +When a conversation starts, a slideover panel opens on the right side of the screen. This panel displays the conversation history and allows users to continue asking questions. + +## Configuration + +Configure the assistant through `app.config.ts`: + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + // Show the floating input on documentation pages + floatingInput: true, + + // Show the \"Explain with AI\" button in the sidebar + explainWithAi: true, + + // FAQ questions to display when chat is empty + faqQuestions: [], + + // Keyboard shortcuts + shortcuts: { + focusInput: 'meta_i' + }, + + // Custom icons + icons: { + trigger: 'i-lucide-sparkles', + explain: 'i-lucide-brain' + } + } +}) +``` + +### FAQ Questions + +Display suggested questions when the chat is empty. This helps users discover what they can ask. + +#### Simple Format + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + faqQuestions: [ + 'How do I install Docus?', + 'How do I customize the theme?', + 'How do I add components to my pages?' + ] + } +}) +``` + +#### Category Format + +Organize questions into categories: + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + faqQuestions: [ + { + category: 'Getting Started', + items: [ + 'How do I install Docus?', + 'What is the project structure?' + ] + }, + { + category: 'Customization', + items: [ + 'How do I change the theme colors?', + 'How do I add a custom logo?' + ] + } + ] + } +}) +``` + +#### Localized Format + +For multi-language documentation, provide FAQ questions per locale: + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + faqQuestions: { + en: [ + { category: 'Getting Started', items: ['How do I install?'] } + ], + fr: [ + { category: 'Démarrage', items: ['Comment installer ?'] } + ] + } + } +}) +``` + +## Keyboard Shortcuts + +Configure the keyboard shortcut for focusing the floating input: + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + shortcuts: { + // Default: 'meta_i' (Cmd+I on Mac, Ctrl+I on Windows) + focusInput: 'meta_k' // Change to Cmd/Ctrl+K + } + } +}) +``` + +The shortcut format uses underscores to separate keys. Common examples: + +- `meta_i` - Cmd+I (Mac) / Ctrl+I (Windows) +- `meta_k` - Cmd+K (Mac) / Ctrl+K (Windows) +- `ctrl_shift_p` - Ctrl+Shift+P + +## Custom Icons + +Customize the icons used by the assistant: + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + icons: { + // Icon for the trigger button and slideover header + trigger: 'i-lucide-bot', + + // Icon for the \"Explain with AI\" button + explain: 'i-lucide-lightbulb' + } + } +}) +``` + +Icons use the [Iconify](https://iconify.design/) format (e.g., `i-lucide-sparkles`, `i-heroicons-sparkles`). + +## Internationalization + +All UI texts are automatically translated based on the user's locale. Docus includes built-in translations for English and French. + +The following texts are translated: + +- Slideover title and placeholder +- Tooltip texts +- Button labels (\"Clear chat\", \"Close\", \"Explain with AI\") +- Status messages (\"Thinking...\", \"Chat is cleared on refresh\") + +## Disable Features + +### Disable the Floating Input + +Hide the floating input at the bottom of documentation pages: + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + floatingInput: false + } +}) +``` + +### Disable \"Explain with AI\" + +Hide the \"Explain with AI\" button in the documentation sidebar: + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + explainWithAi: false + } +}) +``` + +### Disable the Assistant Entirely + +The assistant is automatically disabled when no API key is set. To explicitly disable it, you can remove the environment variable or set `enabled: false` in `nuxt.config.ts`: + + + +```bash [.env] +# Comment out or remove the API key +# AI_GATEWAY_API_KEY=your-api-key +``` + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + chat: { + enabled: false + } + } + } +}) +``` + + + +## Advanced Configuration + +Configure advanced agent options in `nuxt.config.ts`: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + // AI model (uses AI SDK Gateway format) + model: 'google/gemini-3-flash', + + // MCP server (path or URL) + mcpServer: '/mcp', + + chat: { + // API endpoint path + apiPath: '/__docus__/assistant' + } + } + } +}) +``` + +### MCP Server Configuration + +The assistant uses an MCP server to access your documentation. You have two options: + +#### Use the Built-in MCP Server (Default) + +By default, the assistant uses Docus's built-in MCP server at `/mcp`: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + mcpServer: '/mcp' + } + } +}) +``` + + + +Make sure the MCP server is enabled in your configuration. If you've customized the MCP path, update `mcpServer` accordingly. + + + +#### Use an External MCP Server + +Connect to any external MCP server by providing a full URL: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + mcpServer: 'https://other-docs.example.com/mcp' + } + } +}) +``` + +This is useful when you want the assistant to answer questions from a different documentation source, or when connecting to a centralized knowledge base. + +### Custom AI Model + +The assistant uses `google/gemini-3-flash` by default. You can change this to any model supported by the AI SDK Gateway: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + model: 'anthropic/claude-3-5-sonnet' + } + } +}) +``` + +### Site Name in Responses + +The assistant automatically uses your site name in its responses. Configure the site name in `nuxt.config.ts`: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + site: { + name: 'My Documentation' + } +}) +``` + +This makes the assistant respond as \"the My Documentation assistant\" and speak with authority about your specific product. + +## Programmatic Access + +Use the `useAssistant` composable to control the assistant programmatically: + +```vue + + + +``` + +### Composable API + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Property + + Type + + Description +
+ + isEnabled + + + + ComputedRef + + + Whether the assistant is enabled (API key present) +
+ + isOpen + + + + Ref + + + Whether the slideover is open +
+ + open(message?, clearPrevious?) + + + + Function + + + Open the assistant, optionally with a message +
+ + close() + + + + Function + + + Close the assistant slideover +
+ + toggle() + + + + Function + + + Toggle the assistant open/closed +
+ + clearMessages() + + + + Function + + + Clear the conversation history +
diff --git a/content/en/ai/mcp.md b/content/en/ai/mcp.md new file mode 100644 index 000000000..16f3ff79c --- /dev/null +++ b/content/en/ai/mcp.md @@ -0,0 +1,367 @@ +# MCP Server + +> Connect your documentation to AI tools with a native MCP server. + +## About MCP Servers + +The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is an open protocol that creates standardized connections between AI applications and external services, like documentation. + +Every Docus instance includes a built-in MCP server, preparing your content for the broader AI ecosystem where any MCP client (like Claude, Cursor, VS Code, and others) can connect to your documentation. + +### How MCP Servers Work + +When an MCP server is connected to an AI tool, the LLM can decide to use your documentation tools during response generation: + +- The LLM can **proactively search your documentation** while generating a response, not just when explicitly asked. +- The LLM determines **when to use tools** based on the context of the conversation and the relevance of your documentation. +- Each tool call happens **during the generation process**, allowing the LLM to incorporate real-time information from your documentation into its response. + +For example, if a user asks a coding question and the LLM determines that your documentation is relevant, it can search your docs and include that information in the response without the user explicitly asking about your documentation. + +## Accessing Your MCP Server + +Your MCP server is automatically available at the `/mcp` path of your documentation URL. + + + +For example, if your documentation is hosted at `https://docs.example.com`, your MCP server URL is `https://docs.example.com/mcp`. + + + +## Use by Docus Agents + +Docus's [Assistant](/en/ai/assistant) and [Review Agent](/en/ai/review) both use the built-in MCP server to understand your documentation content. + +You can configure the MCP server used by these agents in `nuxt.config.ts`: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + // Connect agents to a specific MCP server + mcpServer: '/mcp' + } + } +}) +``` + +## Disable the MCP Server + +If you want to disable the MCP server, you can do so in your `nuxt.config.ts`: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + mcp: { + enabled: false, + }, +}) +``` + +## Built-in Tools + +Docus provides two tools out of the box that allow any LLM to discover and read your documentation: + +### `list-pages` + +Lists all documentation pages with their titles, paths, and descriptions. AI assistants should call this first to discover available content. + + + + + + + + + + + + + + + + + + + + + +
+ Parameter + + Type + + Description +
+ + locale + + + string (optional) + + Filter pages by locale +
+ +### `get-page` + +Retrieves the full markdown content of a specific documentation page. + + + + + + + + + + + + + + + + + + + + + +
+ Parameter + + Type + + Description +
+ + path + + + string (required) + + The page path (e.g., + /en/getting-started/installation + + + ) +
+ +## Setup + +The Docus MCP server uses HTTP transport and can be installed in different AI assistants. + +### Claude Code + +Add the server using the CLI command: + +```bash +claude mcp add --transport http my-docs https://docs.example.com/mcp +``` + +### Cursor + + + + + + + +Or manually create/update `.cursor/mcp.json` in your project root: + +```json [.cursor/mcp.json] +{ + \"mcpServers\": { + \"my-docs\": { + \"type\": \"http\", + \"url\": \"https://docs.example.com/mcp\" + } + } +} +``` + +### Visual Studio Code + +Ensure you have GitHub Copilot and GitHub Copilot Chat extensions installed. + + + + + + + +Or manually create/update the `.vscode/mcp.json` file: + +```json [.vscode/mcp.json] +{ + \"servers\": { + \"my-docs\": { + \"type\": \"http\", + \"url\": \"https://docs.example.com/mcp\" + } + } +} +``` + +### Windsurf + +1. Open Windsurf and navigate to **Settings** > **Windsurf Settings** > **Cascade** +2. Click the **Manage MCPs** button, then select the **View raw config** option +3. Add the following configuration: + +```json [.codeium/windsurf/mcp_config.json] +{ + \"mcpServers\": { + \"my-docs\": { + \"type\": \"http\", + \"url\": \"https://docs.example.com/mcp\" + } + } +} +``` + +### Zed + +1. Open Zed and go to **Settings** > **Open Settings** +2. Navigate to the JSON settings file +3. Add the following context server configuration: + +```json [.config/zed/settings.json] +{ + \"context_servers\": { + \"my-docs\": { + \"source\": \"custom\", + \"command\": \"npx\", + \"args\": [\"mcp-remote\", \"https://docs.example.com/mcp\"], + \"env\": {} + } + } +} +``` + +## Customization + +Since Docus uses the `@nuxtjs/mcp-toolkit` module, you can extend the MCP server with custom tools, resources, prompts, and handlers. + +### Adding Custom Tools + +Create new tools in the `server/mcp/tools/` directory: + +```ts [server/mcp/tools/search.ts] +import { z } from 'zod' + +export default defineMcpTool({ + description: 'Search documentation by keyword', + inputSchema: { + query: z.string().describe('The search query'), + }, + handler: async ({ query }) => { + const results = await searchDocs(query) + return { + content: [{ type: 'text', text: JSON.stringify(results) }], + } + }, +}) +``` + +### Adding Resources + +Expose files or data sources as MCP resources in the `server/mcp/resources/` directory. The simplest way is using the `file` property: + +```ts [server/mcp/resources/changelog.ts] +export default defineMcpResource({ + file: 'CHANGELOG.md', + metadata: { + description: 'Project changelog', + }, +}) +``` + +This automatically handles URI generation, MIME type detection, and file reading. + +### Adding Prompts + +Create reusable prompts for AI assistants in the `server/mcp/prompts/` directory: + +```ts [server/mcp/prompts/migration-help.ts] +import { z } from 'zod' + +export default defineMcpPrompt({ + description: 'Get help with migrating between versions', + inputSchema: { + fromVersion: z.string().describe('Current version'), + toVersion: z.string().describe('Target version'), + }, + handler: async ({ fromVersion, toVersion }) => { + return { + messages: [{ + role: 'user', + content: { + type: 'text', + text: `Help me migrate from version ${fromVersion} to ${toVersion}. What are the breaking changes and steps I need to follow?`, + }, + }], + } + }, +}) +``` + +### Adding Custom Handlers + +Handlers allow you to create separate MCP endpoints with their own tools, resources, and prompts. This is useful for exposing different capabilities at different routes. + +For example, you could have: + +- `/mcp` - Main documentation MCP server +- `/mcp/migration` - Dedicated MCP server for migration assistance + +```ts [server/mcp/migration.ts] +import { z } from 'zod' + +const migrationTool = defineMcpTool({ + name: 'migrate-v3-to-v4', + description: 'Migrate code from version 3 to version 4', + inputSchema: { + code: z.string().describe('The code to migrate'), + }, + handler: async ({ code }) => { + // Migration logic + return { + content: [{ type: 'text', text: migratedCode }], + } + }, +}) + +export default defineMcpHandler({ + route: '/mcp/migration', + name: 'Migration Assistant', + version: '1.0.0', + tools: [migrationTool], +}) +``` + +### Overwriting Built-in Tools + +You can override the default `list-pages` or `get-page` tools by creating a tool with the same name in your project: + +```ts [server/mcp/tools/list-pages.ts] +import { z } from 'zod' + +export default defineMcpTool({ + description: 'Custom list pages implementation', + inputSchema: { + locale: z.string().optional(), + category: z.string().optional(), + }, + handler: async ({ locale, category }) => { + const pages = await getCustomPageList(locale, category) + return { + content: [{ type: 'text', text: JSON.stringify(pages) }], + } + }, +}) +``` + + + +Check the MCP Toolkit documentation for more information about tools, resources, prompts, handlers and advanced configuration. + + diff --git a/content/en/ai/review.md b/content/en/ai/review.md new file mode 100644 index 000000000..5fb958ac9 --- /dev/null +++ b/content/en/ai/review.md @@ -0,0 +1,89 @@ +# Review Agent + +> PR documentation review agent triggered by GitHub webhooks. + +The Review Agent is a specialized AI assistant that helps keep your documentation accurate by reviewing pull requests. It can identify inconsistencies, suggest improvements, and even commit fixes directly to the PR branch. + +## How It Works + +The Review Agent integrates with GitHub using webhooks: + +1. **Webhook Trigger** - A GitHub webhook notifies your Docus site about a new or updated pull request. +2. **Analysis** - The Review Agent analyzes the PR diff and compares it with the existing documentation. +3. **Action** - Based on its configuration, the agent either posts a comment with suggestions or commits documentation updates directly. + +## Configuration + +Enable the Review Agent in your `nuxt.config.ts`: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + review: { + // Enable the webhook handler + enabled: true, + // Mode: 'comment' (default) or 'commit' + mode: 'comment', + // Target repository (org/repo) + githubRepo: 'nuxt-content/docus' + } + } + } +}) +``` + +### Options + +| Option | Type | Default | Description | +| --- | --- | --- | --- | +| `enabled` | `boolean` | `false` | Enable the webhook handler. | +| `mode` | `'comment' \| 'commit'` | `'comment'` | Whether the agent posts a PR comment or commits fixes. | +| `githubRepo` | `string` | *auto-detected* | Target GitHub repository in `org/repo` format. | + + + +The agent automatically detects the repository name when deployed on Vercel, Netlify, or GitHub Actions. + + + +## GitHub App Setup + +The Review Agent requires a GitHub App to interact with your pull requests. + +### 1. Create a GitHub App + +1. Go to your GitHub organization's **Settings** > **Developer settings** > **GitHub Apps** and click **New GitHub App**. +2. Set the **Webhook URL** to `https://your-docs.com/__docus__/webhook/github`. +3. Set the **Webhook secret**. +4. Grant the following permissions: + - **Pull requests**: Read & write + - **Contents**: Read & write (required for `commit` mode) + - **Metadata**: Read-only +5. Subscribe to **Pull request** events. + +### 2. Set Environment Variables + +Add the following variables to your environment: + +```bash [.env] +# Required for GitHub App authentication +GITHUB_APP_ID=your-app-id +GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----" +GITHUB_WEBHOOK_SECRET=your-webhook-secret + +# Required for AI model access +AI_GATEWAY_API_KEY=your-api-key +``` + +### 3. Install the App + +Install the GitHub App on your repository by going to **Install App** in the GitHub App settings. + +## Webhook Endpoint + +The Review Agent registers a webhook handler at the following route: + +`https://your-docs.com/__docus__/webhook/github` + +Ensure your GitHub App's webhook configuration matches this URL. If you are developing locally, you can use a service like [ngrok](https://ngrok.com/) to expose your local server to the internet. From 25eb841f67a5e3738850900592cee72adb85db88 Mon Sep 17 00:00:00 2001 From: "docus-dev[bot]" <271635299+docus-dev[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2026 23:14:49 +0000 Subject: [PATCH 05/20] docs: update 2 documentation files content/en/ai/assistant.md, content/en/ai/review.md --- content/en/ai/assistant.md | 14 ++--- content/en/ai/review.md | 125 +++++++++++++++++++++---------------- 2 files changed, 76 insertions(+), 63 deletions(-) diff --git a/content/en/ai/assistant.md b/content/en/ai/assistant.md index fcc1e5887..bfcd4d015 100644 --- a/content/en/ai/assistant.md +++ b/content/en/ai/assistant.md @@ -90,7 +90,7 @@ export default defineAppConfig({ // Show the floating input on documentation pages floatingInput: true, - // Show the \"Explain with AI\" button in the sidebar + // Show the "Explain with AI\" button in the sidebar explainWithAi: true, // FAQ questions to display when chat is empty @@ -254,15 +254,15 @@ export default defineAppConfig({ ### Disable the Assistant Entirely -The assistant is automatically disabled when no API key is set. To explicitly disable it, you can remove the environment variable or set `enabled: false` in `nuxt.config.ts`: - - +The assistant is automatically disabled when no API key is set. To explicitly disable it, simply remove the environment variable: ```bash [.env] # Comment out or remove the API key # AI_GATEWAY_API_KEY=your-api-key ``` +You can also explicitly disable the chat assistant in your `nuxt.config.ts`: + ```ts [nuxt.config.ts] export default defineNuxtConfig({ docus: { @@ -275,11 +275,9 @@ export default defineNuxtConfig({ }) ``` - - ## Advanced Configuration -Configure advanced agent options in `nuxt.config.ts`: +Configure advanced options in `nuxt.config.ts` under the `docus.agent` key: ```ts [nuxt.config.ts] export default defineNuxtConfig({ @@ -348,7 +346,7 @@ The assistant uses `google/gemini-3-flash` by default. You can change this to an export default defineNuxtConfig({ docus: { agent: { - model: 'anthropic/claude-3-5-sonnet' + model: 'anthropic/claude-opus-4.5' } } }) diff --git a/content/en/ai/review.md b/content/en/ai/review.md index 5fb958ac9..360cdd112 100644 --- a/content/en/ai/review.md +++ b/content/en/ai/review.md @@ -1,16 +1,55 @@ +--- +title: Review Agent +description: Automate documentation reviews on your pull requests with a built-in AI agent. +--- + # Review Agent -> PR documentation review agent triggered by GitHub webhooks. +> Automate documentation reviews on your pull requests with a built-in AI agent. + +The Review Agent helps keep your documentation accurate and up to date by automatically reviewing pull requests. When a PR is opened or updated, the agent analyzes the changes and provides feedback or suggests documentation updates based on your existing content. + +## Features + +- **Automatic Reviews**: Triggered by GitHub webhooks on every pull request. +- **Smart Filtering**: Automatically ignores internal changes, lockfiles, and non-documentation files to focus on what matters. +- **Multiple Modes**: Choose between receiving suggestions as comments or having the agent commit directly to your branch. +- **Context Aware**: Uses your existing documentation (via MCP) as context to ensure consistency in tone and style. + +## Quick Start + +### 1. Create a GitHub App + +To use the Review Agent, you need to create a GitHub App for your organization or account. + +1. Go to your GitHub **Settings** > **Developer settings** > **GitHub Apps** > **New GitHub App**. +2. **Name**: `My Docs Agent` (or any name you prefer). +3. **Homepage URL**: Your documentation site URL. +4. **Webhook**: Enabled. + - **Webhook URL**: `https://your-docs-site.com/__docus__/webhook/github` + - **Webhook Secret**: Generate a random secret string and keep it safe. +5. **Permissions**: + - **Repository permissions**: + - `Pull requests`: Read & Write + - `Contents`: Read & Write (required if you want the agent to commit changes) + - `Metadata`: Read-only (default) +6. **Events**: + - Subscribe to `Pull request` events. + +### 2. Install the App -The Review Agent is a specialized AI assistant that helps keep your documentation accurate by reviewing pull requests. It can identify inconsistencies, suggest improvements, and even commit fixes directly to the PR branch. +After creating the app, click **Install App** in the sidebar and install it on the repository where your documentation is hosted. -## How It Works +### 3. Set Environment Variables -The Review Agent integrates with GitHub using webhooks: +Add the following variables to your deployment environment (e.g., Vercel, Netlify): -1. **Webhook Trigger** - A GitHub webhook notifies your Docus site about a new or updated pull request. -2. **Analysis** - The Review Agent analyzes the PR diff and compares it with the existing documentation. -3. **Action** - Based on its configuration, the agent either posts a comment with suggestions or commits documentation updates directly. +```bash [.env] +GITHUB_APP_ID=your-app-id +GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----" +GITHUB_WEBHOOK_SECRET=your-webhook-secret +AI_GATEWAY_API_KEY=your-vercel-ai-gateway-key +``` ## Configuration @@ -21,11 +60,13 @@ export default defineNuxtConfig({ docus: { agent: { review: { - // Enable the webhook handler + // Enable the review agent enabled: true, - // Mode: 'comment' (default) or 'commit' + + // 'comment' (default) or 'commit' mode: 'comment', - // Target repository (org/repo) + + // Target repository in 'org/repo' format (auto-detected if blank) githubRepo: 'nuxt-content/docus' } } @@ -33,57 +74,31 @@ export default defineNuxtConfig({ }) ``` -### Options - -| Option | Type | Default | Description | -| --- | --- | --- | --- | -| `enabled` | `boolean` | `false` | Enable the webhook handler. | -| `mode` | `'comment' \| 'commit'` | `'comment'` | Whether the agent posts a PR comment or commits fixes. | -| `githubRepo` | `string` | *auto-detected* | Target GitHub repository in `org/repo` format. | +### Review Modes - +The agent can operate in two modes: -The agent automatically detects the repository name when deployed on Vercel, Netlify, or GitHub Actions. +- `comment`: The agent posts its findings and suggestions as a comment on the pull request. +- `commit`: The agent directly commits suggested documentation changes to the pull request branch. - +### Repository Detection -## GitHub App Setup +By default, Docus attempts to auto-detect your GitHub repository from environment variables provided by CI providers (Vercel, Netlify, GitHub Actions) or from your local `.git/config`. If detection fails, you can explicitly set `githubRepo`. -The Review Agent requires a GitHub App to interact with your pull requests. - -### 1. Create a GitHub App +## Advanced Agent Configuration -1. Go to your GitHub organization's **Settings** > **Developer settings** > **GitHub Apps** and click **New GitHub App**. -2. Set the **Webhook URL** to `https://your-docs.com/__docus__/webhook/github`. -3. Set the **Webhook secret**. -4. Grant the following permissions: - - **Pull requests**: Read & write - - **Contents**: Read & write (required for `commit` mode) - - **Metadata**: Read-only -5. Subscribe to **Pull request** events. +The Review Agent shares the same base configuration as the Chat Assistant: -### 2. Set Environment Variables - -Add the following variables to your environment: - -```bash [.env] -# Required for GitHub App authentication -GITHUB_APP_ID=your-app-id -GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----" -GITHUB_WEBHOOK_SECRET=your-webhook-secret +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + // AI model to use for reviews + model: 'google/gemini-3-flash', -# Required for AI model access -AI_GATEWAY_API_KEY=your-api-key + // MCP server used to retrieve documentation context + mcpServer: '/mcp' + } + } +}) ``` - -### 3. Install the App - -Install the GitHub App on your repository by going to **Install App** in the GitHub App settings. - -## Webhook Endpoint - -The Review Agent registers a webhook handler at the following route: - -`https://your-docs.com/__docus__/webhook/github` - -Ensure your GitHub App's webhook configuration matches this URL. If you are developing locally, you can use a service like [ngrok](https://ngrok.com/) to expose your local server to the internet. From 16778ee932a0d9ea01aaa999118039b0ae14484e Mon Sep 17 00:00:00 2001 From: Baptiste Leproux Date: Sat, 28 Mar 2026 00:23:05 +0100 Subject: [PATCH 06/20] update --- .../runtime/server/workflows/agent-review.ts | 94 ++++++++ layer/server/mcp/prompts/review-pr-docs.ts | 62 ++++++ layer/server/mcp/tools/commit-files.ts | 200 ++++++++++++++++++ layer/server/routes/raw/[...slug].md.get.ts | 67 ++++++ 4 files changed, 423 insertions(+) create mode 100644 layer/modules/assistant/runtime/server/workflows/agent-review.ts create mode 100644 layer/server/mcp/prompts/review-pr-docs.ts create mode 100644 layer/server/mcp/tools/commit-files.ts create mode 100644 layer/server/routes/raw/[...slug].md.get.ts diff --git a/layer/modules/assistant/runtime/server/workflows/agent-review.ts b/layer/modules/assistant/runtime/server/workflows/agent-review.ts new file mode 100644 index 000000000..4faa53b63 --- /dev/null +++ b/layer/modules/assistant/runtime/server/workflows/agent-review.ts @@ -0,0 +1,94 @@ +import { generateText } from 'ai' +import { gateway } from '@ai-sdk/gateway' +import { createMCPClient } from '@ai-sdk/mcp' + +interface AgentReviewInput { + owner: string + repo: string + branch: string + filteredDiff: string + model: string + mcpUrl: string + token: string + webhookSecret: string +} + +export async function agentReviewWorkflow({ owner, repo, branch, filteredDiff, model, mcpUrl, token, webhookSecret }: AgentReviewInput) { + // TODO(workflow): 'use workflow' + console.log(`[agent-review] Starting review for ${owner}/${repo} branch: ${branch}, model: ${model}`) + console.log(`[agent-review] MCP server: ${mcpUrl}`) + + const mcpClient = await createMCPClient({ + transport: { type: 'http', url: mcpUrl, headers: { 'x-agent-review': webhookSecret } }, + }) + + try { + console.log(`[agent-review] Fetching MCP tools`) + const tools = await mcpClient.tools() + const toolNames = Object.keys(tools) + console.log(`[agent-review] Tools available: ${toolNames.join(', ')}`) + + const prompt = buildReviewPrompt({ owner, repo, branch, diff: filteredDiff, token }) + + console.log(`[agent-review] Running agent (diff: ${filteredDiff.length} chars)`) + const { text, steps } = await generateText({ + model: gateway(model), + stopWhen: ({ steps }) => steps.length >= 20, + messages: [{ role: 'user', content: prompt }], + tools, + }) + + const toolCallCount = steps.reduce((n, s) => n + (s.toolCalls?.length ?? 0), 0) + console.log(`[agent-review] Done — ${steps.length} step(s), ${toolCallCount} tool call(s)`) + if (text) console.log(`[agent-review] Agent message:\n${text}`) + } + finally { + await mcpClient.close() + } +} + +function buildReviewPrompt({ owner, repo, branch, diff, token }: { owner: string, repo: string, branch: string, diff: string, token: string }): string { + return `You are a documentation agent for a Docus documentation site. Your job is to keep the docs in sync with code changes. + +You have been triggered by a pull request on ${owner}/${repo} (branch: ${branch}). + +## Your workflow + +1. Call \`list-pages\` to discover the existing documentation structure +2. Call \`get-page\` on pages that appear related to the diff (by topic, file name, or module name) +3. Decide what documentation action to take for each relevant file: + - **Existing page that is now outdated** → update the relevant section(s) in place — **this is always the preferred action** + - **Brand-new major feature with absolutely no existing page** → only then create a new MDC page + - **Only internal refactor / types / tests / config** → skip, no docs needed + - When in doubt, update an existing page rather than creating a new one +4. Call \`commit-files\` **ONCE** with ALL files to create or update — do not call it multiple times + +## Commit parameters + +Always pass these exact values to \`commit-files\`: +- owner: "${owner}" +- repo: "${repo}" +- branch: "${branch}" +- token: "${token}" +- files: array of every \`{ path, content }\` you want to write, all in one call +- message: a conventional commit message describing what changed and why (e.g. \`docs: document new \\\`useHead\\\` options added in this PR\`) + +## MDC writing rules + +- Use Docus components where appropriate: \`::note\`, \`::callout\`, \`::code-group\`, \`::card\` +- File paths follow the content structure: \`content/2.guide/my-feature.md\` +- Frontmatter must include at minimum: \`title\` and \`description\` + +## Tone and existing content + +- **Match the tone of the existing docs exactly.** Read related pages with \`get-page\` first and mirror their sentence structure, vocabulary, and level of detail. +- You may only append new content or update a specific outdated value (e.g. a renamed option, a changed default). If a section is no longer accurate you can delete it if you really think it's not needed anymore. Be careful with deletion. +- **Do not rewrite for style.** If the existing text is correct, leave it unchanged even if you would phrase it differently. +- **Always prefer updating an existing page over creating a new one.** Only create a new page for a brand-new major feature that has no related existing page at all. + +## PR diff + +\`\`\`diff +${diff} +\`\`\`` +} diff --git a/layer/server/mcp/prompts/review-pr-docs.ts b/layer/server/mcp/prompts/review-pr-docs.ts new file mode 100644 index 000000000..434d970d1 --- /dev/null +++ b/layer/server/mcp/prompts/review-pr-docs.ts @@ -0,0 +1,62 @@ +import { z } from 'zod' + +export default defineMcpPrompt({ + enabled: (event: import('h3').H3Event) => { + const config = useRuntimeConfig(event) + return getHeader(event, 'x-agent-review') === config.webhookSecret + }, + name: 'review-pr-docs', + description: 'Reviews a PR diff and updates the Docus documentation site accordingly. Discovers existing pages, identifies gaps, then creates or updates MDC files directly on the PR branch.', + inputSchema: { + owner: z.string().describe('GitHub repository owner'), + repo: z.string().describe('GitHub repository name'), + branch: z.string().describe('PR head branch to commit documentation updates to'), + diff: z.string().describe('Filtered PR diff (only .ts/.vue/.js files, already truncated)'), + token: z.string().describe('GitHub installation access token for committing changes'), + }, + handler: ({ owner, repo, branch, diff, token }: { owner: string, repo: string, branch: string, diff: string, token: string }) => { + return `You are a documentation agent for a Docus documentation site. Your job is to keep the docs in sync with code changes. + +You have been triggered by a pull request on ${owner}/${repo} (branch: ${branch}). + +## Your workflow + +1. Call \`list-pages\` to discover the existing documentation structure +2. Call \`get-page\` on pages that appear related to the diff (by topic, file name, or module name) +3. Decide what documentation action to take for each relevant file: + - **Existing page that is now outdated** → update the relevant section(s) in place — **this is always the preferred action** + - **Brand-new major feature with absolutely no existing page** → only then create a new MDC page + - **Only internal refactor / types / tests / config** → skip, no docs needed + - When in doubt, update an existing page rather than creating a new one +4. Call \`commit-files\` **ONCE** with ALL files to create or update — do not call it multiple times + +## Commit parameters + +Always pass these exact values to \`commit-files\`: +- owner: "${owner}" +- repo: "${repo}" +- branch: "${branch}" +- token: "${token}" +- files: array of every \`{ path, content }\` you want to write, all in one call +- message: a conventional commit message describing what changed and why (e.g. \`docs: document new \\\`useHead\\\` options added in this PR\`) + +## MDC writing rules + +- Use Docus components where appropriate: \`::note\`, \`::callout\`, \`::code-group\`, \`::card\` +- File paths follow the content structure: \`content/2.guide/my-feature.md\` +- Frontmatter must include at minimum: \`title\` and \`description\` + +## Tone and existing content + +- **Match the tone of the existing docs exactly.** Read related pages with \`get-page\` first and mirror their sentence structure, vocabulary, and level of detail. +- **Never remove or shorten existing sections.** You may only append new content or update a specific outdated value (e.g. a renamed option, a changed default). If a section is no longer accurate, add a note below it — do not delete it. +- **Do not rewrite for style.** If the existing text is correct, leave it unchanged even if you would phrase it differently. +- **Always prefer updating an existing page over creating a new one.** Only create a new page for a brand-new major feature that has no related existing page at all. + +## PR diff + +\`\`\`diff +${diff} +\`\`\`` + }, +}) diff --git a/layer/server/mcp/tools/commit-files.ts b/layer/server/mcp/tools/commit-files.ts new file mode 100644 index 000000000..2724d16c0 --- /dev/null +++ b/layer/server/mcp/tools/commit-files.ts @@ -0,0 +1,200 @@ +import { z } from 'zod' + +const ALLOWED_EXTENSIONS = /\.(?:md|mdx|mdc)$/ +const MAX_CONTENT_SIZE = 500 * 1024 // 500 KB per file +const PROTECTED_BRANCH_PATTERNS = [ + /^main$/, + /^master$/, + /^release\//, + /^hotfix\//, +] + +export default defineMcpTool({ + description: 'Commits one or more documentation files to a GitHub branch in a single atomic commit using the Git Trees API.', + enabled: (event) => { + const config = useRuntimeConfig(event) + return getHeader(event, 'x-agent-review') === config.webhookSecret + }, + inputSchema: { + owner: z.string().describe('GitHub repository owner'), + repo: z.string().describe('GitHub repository name'), + branch: z.string().describe('Branch to commit to (must be the PR head branch, never the default branch)'), + token: z.string().describe('GitHub installation access token'), + files: z.array(z.object({ + path: z.string().describe('File path relative to repo root (e.g. content/2.guide/my-feature.md)'), + content: z.string().describe('Full file content to write'), + })).min(1).describe('Array of files to create or update in a single commit'), + message: z.string().describe('Commit message. Use conventional commits format: "docs: " — be specific about what was added or updated (e.g. "docs: document new `useHead` options added in PR #42")'), + }, + handler: async ({ owner, repo, branch, token, files, message }: { + owner: string + repo: string + branch: string + token: string + files: Array<{ path: string, content: string }> + message: string + }) => { + console.log(`[commit-files] Preparing commit: ${owner}/${repo} branch=${branch} files=${files.length}`) + + // --- Security guards --- + + // 1. Reject protected branch patterns + for (const pattern of PROTECTED_BRANCH_PATTERNS) { + if (pattern.test(branch)) { + console.warn(`[commit-files] Rejected: branch "${branch}" matches protected pattern ${pattern}`) + return errorResult(`Refusing to commit to protected branch: ${branch}`) + } + } + + const githubHeaders = { + 'Authorization': `token ${token}`, + 'Accept': 'application/vnd.github.v3+json', + 'Content-Type': 'application/json', + } + + // 2. Verify branch exists and is not the default branch + let defaultBranch: string + let branchSha: string + try { + const repoInfo = await $fetch<{ default_branch: string }>(`https://api.github.com/repos/${owner}/${repo}`, { + headers: githubHeaders, + }) + defaultBranch = repoInfo.default_branch + console.log(`[commit-files] Default branch: ${defaultBranch}`) + + if (branch === defaultBranch) { + console.warn(`[commit-files] Rejected: branch "${branch}" is the default branch`) + return errorResult(`Refusing to commit directly to the default branch: ${branch}`) + } + + const branchInfo = await $fetch<{ commit: { sha: string } }>(`https://api.github.com/repos/${owner}/${repo}/branches/${branch}`, { + headers: githubHeaders, + }) + branchSha = branchInfo.commit.sha + console.log(`[commit-files] Branch HEAD: ${branchSha}`) + } + catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err) + console.error(`[commit-files] Failed to fetch repo/branch info: ${msg}`) + return errorResult(`Failed to fetch repo or branch info: ${msg}`) + } + + // 3. Validate each file + for (const file of files) { + // Path traversal + if (file.path.includes('..') || file.path.startsWith('/')) { + console.warn(`[commit-files] Rejected: invalid path "${file.path}"`) + return errorResult(`Invalid file path: ${file.path}`) + } + + // Must live under content/ + if (!file.path.startsWith('content/')) { + console.warn(`[commit-files] Rejected: path outside content/: "${file.path}"`) + return errorResult(`File path must start with "content/": ${file.path}`) + } + + // Markdown-only + if (!ALLOWED_EXTENSIONS.test(file.path)) { + console.warn(`[commit-files] Rejected: non-markdown extension "${file.path}"`) + return errorResult(`Only .md, .mdx, and .mdc files are allowed: ${file.path}`) + } + + // Size limit + const byteSize = Buffer.byteLength(file.content, 'utf8') + if (byteSize > MAX_CONTENT_SIZE) { + console.warn(`[commit-files] Rejected: file "${file.path}" exceeds size limit (${byteSize} bytes)`) + return errorResult(`File exceeds 500 KB limit: ${file.path} (${byteSize} bytes)`) + } + + console.log(`[commit-files] Validated: ${file.path} (${byteSize} bytes)`) + } + + // --- Git Trees API --- + + // 4. Get the current tree SHA + let baseTreeSha: string + try { + const commitInfo = await $fetch<{ tree: { sha: string } }>(`https://api.github.com/repos/${owner}/${repo}/git/commits/${branchSha}`, { + headers: githubHeaders, + }) + baseTreeSha = commitInfo.tree.sha + console.log(`[commit-files] Base tree SHA: ${baseTreeSha}`) + } + catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err) + console.error(`[commit-files] Failed to fetch commit tree: ${msg}`) + return errorResult(`Failed to fetch commit tree: ${msg}`) + } + + // 5. Create a new tree with all files + let newTreeSha: string + try { + const treePayload = { + base_tree: baseTreeSha, + tree: files.map(file => ({ + path: file.path, + mode: '100644', + type: 'blob', + content: file.content, + })), + } + const newTree = await $fetch<{ sha: string }>(`https://api.github.com/repos/${owner}/${repo}/git/trees`, { + method: 'POST', + headers: githubHeaders, + body: JSON.stringify(treePayload), + }) + newTreeSha = newTree.sha + console.log(`[commit-files] New tree SHA: ${newTreeSha}`) + } + catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err) + console.error(`[commit-files] Failed to create tree: ${msg}`) + return errorResult(`Failed to create Git tree: ${msg}`) + } + + // 6. Create the commit + let newCommitSha: string + try { + const newCommit = await $fetch<{ sha: string }>(`https://api.github.com/repos/${owner}/${repo}/git/commits`, { + method: 'POST', + headers: githubHeaders, + body: JSON.stringify({ + message, + tree: newTreeSha, + parents: [branchSha], + }), + }) + newCommitSha = newCommit.sha + console.log(`[commit-files] New commit SHA: ${newCommitSha}`) + } + catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err) + console.error(`[commit-files] Failed to create commit: ${msg}`) + return errorResult(`Failed to create commit: ${msg}`) + } + + // 7. Update the branch ref + try { + await $fetch(`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}`, { + method: 'PATCH', + headers: githubHeaders, + body: JSON.stringify({ sha: newCommitSha }), + }) + console.log(`[commit-files] Branch "${branch}" updated to ${newCommitSha}`) + } + catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err) + console.error(`[commit-files] Failed to update ref: ${msg}`) + return errorResult(`Failed to update branch ref: ${msg}`) + } + + const paths = files.map(f => f.path).join(', ') + console.log(`[commit-files] Done — committed ${files.length} file(s) to ${branch}: ${paths}`) + + return jsonResult({ + sha: newCommitSha, + branch, + files: files.map(f => f.path), + }) + }, +}) diff --git a/layer/server/routes/raw/[...slug].md.get.ts b/layer/server/routes/raw/[...slug].md.get.ts new file mode 100644 index 000000000..904968780 --- /dev/null +++ b/layer/server/routes/raw/[...slug].md.get.ts @@ -0,0 +1,67 @@ +// TODO: upstream this fix to @nuxt/content +// Bug: page.body.value is null for pages with no markdown body (e.g. frontmatter-only / config-driven pages). +// The original route crashes at `page.body.value[0]?.[0]` when value is null. +// Fix: return 404 when body.value is null instead of crashing. + +// @ts-expect-error — minimark/stringify has no types entry in its exports map +import { stringify } from 'minimark/stringify' +import { queryCollection } from '@nuxt/content/server' +import type { Collections } from '@nuxt/content' +// @ts-expect-error — virtual module, no types +import contentCollections from '#content/manifest' +import { withLeadingSlash } from 'ufo' + +export default defineEventHandler(async (event) => { + const config = useRuntimeConfig(event) + const llmsConfig = (config as { llms?: { contentRawMarkdown?: boolean | { excludeCollections?: string[] } } }).llms + const slug = getRouterParam(event, 'slug.md') + + if (!slug?.endsWith('.md') || llmsConfig?.contentRawMarkdown === false) { + throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true }) + } + + let path = withLeadingSlash(slug.replace('.md', '')) + if (path.endsWith('/index')) { + path = path.substring(0, path.length - 6) + } + + const excludeCollections: string[] = (llmsConfig?.contentRawMarkdown as { excludeCollections?: string[] } | undefined)?.excludeCollections || [] + + const collectionNames: string[] = Object.entries(contentCollections as Record) + .filter(([key, value]) => value.type === 'page' && !excludeCollections.includes(key)) + .map(([key]) => key) + + let page: { title: string, description: string, body: { value: unknown[] | null }, links?: unknown[], meta?: { links?: unknown[] } } | null = null + for (const collection of collectionNames) { + page = await queryCollection(event, collection as keyof Collections).path(path).first() as typeof page + if (page) break + } + + if (!page) { + throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true }) + } + + // Fix: body.value can be null for pages with no markdown content + if (!page.body?.value) { + throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true }) + } + + if ((page.body.value[0] as unknown[])?.[0] !== 'h1') { + page.body.value.unshift(['blockquote', {}, page.description]) + page.body.value.unshift(['h1', {}, page.title]) + } + + const links = page.links || page.meta?.links + if (Array.isArray(links) && links.length > 0) { + const linkItems = (links as { label?: string, to?: string }[]) + .filter(link => link.label && link.to) + .map(link => ['li', {}, ['a', { href: link.to }, link.label]]) + if (linkItems.length > 0) { + page.body.value.push(['hr']) + page.body.value.push(['ul', {}, ...linkItems]) + } + } + + setHeader(event, 'Content-Type', 'text/markdown; charset=utf-8') + return stringify({ ...page.body, type: 'minimark' }, { format: 'markdown/html' }) +}) From f735018ff00739574ff2dd9c69edde5d4738eb24 Mon Sep 17 00:00:00 2001 From: Baptiste Leproux Date: Sat, 28 Mar 2026 00:24:41 +0100 Subject: [PATCH 07/20] del content wrong --- content/en/ai/assistant.md | 518 ------------------------------------- content/en/ai/mcp.md | 367 -------------------------- content/en/ai/review.md | 104 -------- 3 files changed, 989 deletions(-) delete mode 100644 content/en/ai/assistant.md delete mode 100644 content/en/ai/mcp.md delete mode 100644 content/en/ai/review.md diff --git a/content/en/ai/assistant.md b/content/en/ai/assistant.md deleted file mode 100644 index bfcd4d015..000000000 --- a/content/en/ai/assistant.md +++ /dev/null @@ -1,518 +0,0 @@ -# Assistant - -> Add AI-powered chat to your docs that answers questions, cites sources, and generates code examples. - -## About the Assistant - -The assistant answers questions about your documentation through natural language queries. It is embedded directly in your documentation site, so users can find answers quickly and succeed with your product. - -When users ask questions, the assistant: - -- **Searches and retrieves** relevant content from your documentation using an [MCP server](/en/ai/mcp). -- **Cites sources** with navigable links to take users directly to referenced pages. -- **Generates copyable code examples** to help users implement solutions from your documentation. - - - -The assistant requires an [AI Gateway](https://vercel.com/docs/ai-gateway) API key to function. - - - -## How It Works - -The assistant uses a multi-agent architecture: - -1. **Main Agent** - Receives user questions and decides when to search documentation -2. **Search Agent** - Uses [MCP server](/en/ai/mcp) tools to find relevant content -3. **Response Generation** - Synthesizes information into helpful, conversational answers - -By default, the assistant connects to your documentation's built-in MCP server at `/mcp`, giving it access to all your pages without additional configuration. You can also connect to an external MCP server if needed. - -## Quick Start - -### 1. Get an API Key - -Get an API key from [Vercel AI Gateway](https://vercel.com/~/ai/api-keys). AI Gateway works with multiple AI providers (OpenAI, Anthropic, Google, and more) through a unified API. - -### 2. Set Environment Variable - -Add your API key to your environment: - -```bash [.env] -AI_GATEWAY_API_KEY=your-api-key -``` - -### 3. Deploy - -That's it! The assistant is automatically enabled when an API key is detected. Deploy your documentation and the assistant will be available to your users. - -## Using the Assistant - -Users can interact with the assistant in multiple ways: - -### Floating Input - -On documentation pages, a floating input appears at the bottom of the screen. Users can type their questions directly and press Enter to get answers. - - - -Use the keyboard shortcut - - - - - - - - - - - - to focus the floating input. - - - -### Explain with AI - -Each documentation page includes an **Explain with AI** button in the table of contents sidebar. Clicking this button opens the assistant with the current page as context, asking it to explain the content. - -### Slideover Chat - -When a conversation starts, a slideover panel opens on the right side of the screen. This panel displays the conversation history and allows users to continue asking questions. - -## Configuration - -Configure the assistant through `app.config.ts`: - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - // Show the floating input on documentation pages - floatingInput: true, - - // Show the "Explain with AI\" button in the sidebar - explainWithAi: true, - - // FAQ questions to display when chat is empty - faqQuestions: [], - - // Keyboard shortcuts - shortcuts: { - focusInput: 'meta_i' - }, - - // Custom icons - icons: { - trigger: 'i-lucide-sparkles', - explain: 'i-lucide-brain' - } - } -}) -``` - -### FAQ Questions - -Display suggested questions when the chat is empty. This helps users discover what they can ask. - -#### Simple Format - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - faqQuestions: [ - 'How do I install Docus?', - 'How do I customize the theme?', - 'How do I add components to my pages?' - ] - } -}) -``` - -#### Category Format - -Organize questions into categories: - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - faqQuestions: [ - { - category: 'Getting Started', - items: [ - 'How do I install Docus?', - 'What is the project structure?' - ] - }, - { - category: 'Customization', - items: [ - 'How do I change the theme colors?', - 'How do I add a custom logo?' - ] - } - ] - } -}) -``` - -#### Localized Format - -For multi-language documentation, provide FAQ questions per locale: - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - faqQuestions: { - en: [ - { category: 'Getting Started', items: ['How do I install?'] } - ], - fr: [ - { category: 'Démarrage', items: ['Comment installer ?'] } - ] - } - } -}) -``` - -## Keyboard Shortcuts - -Configure the keyboard shortcut for focusing the floating input: - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - shortcuts: { - // Default: 'meta_i' (Cmd+I on Mac, Ctrl+I on Windows) - focusInput: 'meta_k' // Change to Cmd/Ctrl+K - } - } -}) -``` - -The shortcut format uses underscores to separate keys. Common examples: - -- `meta_i` - Cmd+I (Mac) / Ctrl+I (Windows) -- `meta_k` - Cmd+K (Mac) / Ctrl+K (Windows) -- `ctrl_shift_p` - Ctrl+Shift+P - -## Custom Icons - -Customize the icons used by the assistant: - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - icons: { - // Icon for the trigger button and slideover header - trigger: 'i-lucide-bot', - - // Icon for the \"Explain with AI\" button - explain: 'i-lucide-lightbulb' - } - } -}) -``` - -Icons use the [Iconify](https://iconify.design/) format (e.g., `i-lucide-sparkles`, `i-heroicons-sparkles`). - -## Internationalization - -All UI texts are automatically translated based on the user's locale. Docus includes built-in translations for English and French. - -The following texts are translated: - -- Slideover title and placeholder -- Tooltip texts -- Button labels (\"Clear chat\", \"Close\", \"Explain with AI\") -- Status messages (\"Thinking...\", \"Chat is cleared on refresh\") - -## Disable Features - -### Disable the Floating Input - -Hide the floating input at the bottom of documentation pages: - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - floatingInput: false - } -}) -``` - -### Disable \"Explain with AI\" - -Hide the \"Explain with AI\" button in the documentation sidebar: - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - explainWithAi: false - } -}) -``` - -### Disable the Assistant Entirely - -The assistant is automatically disabled when no API key is set. To explicitly disable it, simply remove the environment variable: - -```bash [.env] -# Comment out or remove the API key -# AI_GATEWAY_API_KEY=your-api-key -``` - -You can also explicitly disable the chat assistant in your `nuxt.config.ts`: - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - docus: { - agent: { - chat: { - enabled: false - } - } - } -}) -``` - -## Advanced Configuration - -Configure advanced options in `nuxt.config.ts` under the `docus.agent` key: - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - docus: { - agent: { - // AI model (uses AI SDK Gateway format) - model: 'google/gemini-3-flash', - - // MCP server (path or URL) - mcpServer: '/mcp', - - chat: { - // API endpoint path - apiPath: '/__docus__/assistant' - } - } - } -}) -``` - -### MCP Server Configuration - -The assistant uses an MCP server to access your documentation. You have two options: - -#### Use the Built-in MCP Server (Default) - -By default, the assistant uses Docus's built-in MCP server at `/mcp`: - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - docus: { - agent: { - mcpServer: '/mcp' - } - } -}) -``` - - - -Make sure the MCP server is enabled in your configuration. If you've customized the MCP path, update `mcpServer` accordingly. - - - -#### Use an External MCP Server - -Connect to any external MCP server by providing a full URL: - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - docus: { - agent: { - mcpServer: 'https://other-docs.example.com/mcp' - } - } -}) -``` - -This is useful when you want the assistant to answer questions from a different documentation source, or when connecting to a centralized knowledge base. - -### Custom AI Model - -The assistant uses `google/gemini-3-flash` by default. You can change this to any model supported by the AI SDK Gateway: - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - docus: { - agent: { - model: 'anthropic/claude-opus-4.5' - } - } -}) -``` - -### Site Name in Responses - -The assistant automatically uses your site name in its responses. Configure the site name in `nuxt.config.ts`: - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - site: { - name: 'My Documentation' - } -}) -``` - -This makes the assistant respond as \"the My Documentation assistant\" and speak with authority about your specific product. - -## Programmatic Access - -Use the `useAssistant` composable to control the assistant programmatically: - -```vue - - - -``` - -### Composable API - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Property - - Type - - Description -
- - isEnabled - - - - ComputedRef - - - Whether the assistant is enabled (API key present) -
- - isOpen - - - - Ref - - - Whether the slideover is open -
- - open(message?, clearPrevious?) - - - - Function - - - Open the assistant, optionally with a message -
- - close() - - - - Function - - - Close the assistant slideover -
- - toggle() - - - - Function - - - Toggle the assistant open/closed -
- - clearMessages() - - - - Function - - - Clear the conversation history -
diff --git a/content/en/ai/mcp.md b/content/en/ai/mcp.md deleted file mode 100644 index 16f3ff79c..000000000 --- a/content/en/ai/mcp.md +++ /dev/null @@ -1,367 +0,0 @@ -# MCP Server - -> Connect your documentation to AI tools with a native MCP server. - -## About MCP Servers - -The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is an open protocol that creates standardized connections between AI applications and external services, like documentation. - -Every Docus instance includes a built-in MCP server, preparing your content for the broader AI ecosystem where any MCP client (like Claude, Cursor, VS Code, and others) can connect to your documentation. - -### How MCP Servers Work - -When an MCP server is connected to an AI tool, the LLM can decide to use your documentation tools during response generation: - -- The LLM can **proactively search your documentation** while generating a response, not just when explicitly asked. -- The LLM determines **when to use tools** based on the context of the conversation and the relevance of your documentation. -- Each tool call happens **during the generation process**, allowing the LLM to incorporate real-time information from your documentation into its response. - -For example, if a user asks a coding question and the LLM determines that your documentation is relevant, it can search your docs and include that information in the response without the user explicitly asking about your documentation. - -## Accessing Your MCP Server - -Your MCP server is automatically available at the `/mcp` path of your documentation URL. - - - -For example, if your documentation is hosted at `https://docs.example.com`, your MCP server URL is `https://docs.example.com/mcp`. - - - -## Use by Docus Agents - -Docus's [Assistant](/en/ai/assistant) and [Review Agent](/en/ai/review) both use the built-in MCP server to understand your documentation content. - -You can configure the MCP server used by these agents in `nuxt.config.ts`: - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - docus: { - agent: { - // Connect agents to a specific MCP server - mcpServer: '/mcp' - } - } -}) -``` - -## Disable the MCP Server - -If you want to disable the MCP server, you can do so in your `nuxt.config.ts`: - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - mcp: { - enabled: false, - }, -}) -``` - -## Built-in Tools - -Docus provides two tools out of the box that allow any LLM to discover and read your documentation: - -### `list-pages` - -Lists all documentation pages with their titles, paths, and descriptions. AI assistants should call this first to discover available content. - - - - - - - - - - - - - - - - - - - - - -
- Parameter - - Type - - Description -
- - locale - - - string (optional) - - Filter pages by locale -
- -### `get-page` - -Retrieves the full markdown content of a specific documentation page. - - - - - - - - - - - - - - - - - - - - - -
- Parameter - - Type - - Description -
- - path - - - string (required) - - The page path (e.g., - /en/getting-started/installation - - - ) -
- -## Setup - -The Docus MCP server uses HTTP transport and can be installed in different AI assistants. - -### Claude Code - -Add the server using the CLI command: - -```bash -claude mcp add --transport http my-docs https://docs.example.com/mcp -``` - -### Cursor - - - - - - - -Or manually create/update `.cursor/mcp.json` in your project root: - -```json [.cursor/mcp.json] -{ - \"mcpServers\": { - \"my-docs\": { - \"type\": \"http\", - \"url\": \"https://docs.example.com/mcp\" - } - } -} -``` - -### Visual Studio Code - -Ensure you have GitHub Copilot and GitHub Copilot Chat extensions installed. - - - - - - - -Or manually create/update the `.vscode/mcp.json` file: - -```json [.vscode/mcp.json] -{ - \"servers\": { - \"my-docs\": { - \"type\": \"http\", - \"url\": \"https://docs.example.com/mcp\" - } - } -} -``` - -### Windsurf - -1. Open Windsurf and navigate to **Settings** > **Windsurf Settings** > **Cascade** -2. Click the **Manage MCPs** button, then select the **View raw config** option -3. Add the following configuration: - -```json [.codeium/windsurf/mcp_config.json] -{ - \"mcpServers\": { - \"my-docs\": { - \"type\": \"http\", - \"url\": \"https://docs.example.com/mcp\" - } - } -} -``` - -### Zed - -1. Open Zed and go to **Settings** > **Open Settings** -2. Navigate to the JSON settings file -3. Add the following context server configuration: - -```json [.config/zed/settings.json] -{ - \"context_servers\": { - \"my-docs\": { - \"source\": \"custom\", - \"command\": \"npx\", - \"args\": [\"mcp-remote\", \"https://docs.example.com/mcp\"], - \"env\": {} - } - } -} -``` - -## Customization - -Since Docus uses the `@nuxtjs/mcp-toolkit` module, you can extend the MCP server with custom tools, resources, prompts, and handlers. - -### Adding Custom Tools - -Create new tools in the `server/mcp/tools/` directory: - -```ts [server/mcp/tools/search.ts] -import { z } from 'zod' - -export default defineMcpTool({ - description: 'Search documentation by keyword', - inputSchema: { - query: z.string().describe('The search query'), - }, - handler: async ({ query }) => { - const results = await searchDocs(query) - return { - content: [{ type: 'text', text: JSON.stringify(results) }], - } - }, -}) -``` - -### Adding Resources - -Expose files or data sources as MCP resources in the `server/mcp/resources/` directory. The simplest way is using the `file` property: - -```ts [server/mcp/resources/changelog.ts] -export default defineMcpResource({ - file: 'CHANGELOG.md', - metadata: { - description: 'Project changelog', - }, -}) -``` - -This automatically handles URI generation, MIME type detection, and file reading. - -### Adding Prompts - -Create reusable prompts for AI assistants in the `server/mcp/prompts/` directory: - -```ts [server/mcp/prompts/migration-help.ts] -import { z } from 'zod' - -export default defineMcpPrompt({ - description: 'Get help with migrating between versions', - inputSchema: { - fromVersion: z.string().describe('Current version'), - toVersion: z.string().describe('Target version'), - }, - handler: async ({ fromVersion, toVersion }) => { - return { - messages: [{ - role: 'user', - content: { - type: 'text', - text: `Help me migrate from version ${fromVersion} to ${toVersion}. What are the breaking changes and steps I need to follow?`, - }, - }], - } - }, -}) -``` - -### Adding Custom Handlers - -Handlers allow you to create separate MCP endpoints with their own tools, resources, and prompts. This is useful for exposing different capabilities at different routes. - -For example, you could have: - -- `/mcp` - Main documentation MCP server -- `/mcp/migration` - Dedicated MCP server for migration assistance - -```ts [server/mcp/migration.ts] -import { z } from 'zod' - -const migrationTool = defineMcpTool({ - name: 'migrate-v3-to-v4', - description: 'Migrate code from version 3 to version 4', - inputSchema: { - code: z.string().describe('The code to migrate'), - }, - handler: async ({ code }) => { - // Migration logic - return { - content: [{ type: 'text', text: migratedCode }], - } - }, -}) - -export default defineMcpHandler({ - route: '/mcp/migration', - name: 'Migration Assistant', - version: '1.0.0', - tools: [migrationTool], -}) -``` - -### Overwriting Built-in Tools - -You can override the default `list-pages` or `get-page` tools by creating a tool with the same name in your project: - -```ts [server/mcp/tools/list-pages.ts] -import { z } from 'zod' - -export default defineMcpTool({ - description: 'Custom list pages implementation', - inputSchema: { - locale: z.string().optional(), - category: z.string().optional(), - }, - handler: async ({ locale, category }) => { - const pages = await getCustomPageList(locale, category) - return { - content: [{ type: 'text', text: JSON.stringify(pages) }], - } - }, -}) -``` - - - -Check the MCP Toolkit documentation for more information about tools, resources, prompts, handlers and advanced configuration. - - diff --git a/content/en/ai/review.md b/content/en/ai/review.md deleted file mode 100644 index 360cdd112..000000000 --- a/content/en/ai/review.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -title: Review Agent -description: Automate documentation reviews on your pull requests with a built-in AI agent. ---- - -# Review Agent - -> Automate documentation reviews on your pull requests with a built-in AI agent. - -The Review Agent helps keep your documentation accurate and up to date by automatically reviewing pull requests. When a PR is opened or updated, the agent analyzes the changes and provides feedback or suggests documentation updates based on your existing content. - -## Features - -- **Automatic Reviews**: Triggered by GitHub webhooks on every pull request. -- **Smart Filtering**: Automatically ignores internal changes, lockfiles, and non-documentation files to focus on what matters. -- **Multiple Modes**: Choose between receiving suggestions as comments or having the agent commit directly to your branch. -- **Context Aware**: Uses your existing documentation (via MCP) as context to ensure consistency in tone and style. - -## Quick Start - -### 1. Create a GitHub App - -To use the Review Agent, you need to create a GitHub App for your organization or account. - -1. Go to your GitHub **Settings** > **Developer settings** > **GitHub Apps** > **New GitHub App**. -2. **Name**: `My Docs Agent` (or any name you prefer). -3. **Homepage URL**: Your documentation site URL. -4. **Webhook**: Enabled. - - **Webhook URL**: `https://your-docs-site.com/__docus__/webhook/github` - - **Webhook Secret**: Generate a random secret string and keep it safe. -5. **Permissions**: - - **Repository permissions**: - - `Pull requests`: Read & Write - - `Contents`: Read & Write (required if you want the agent to commit changes) - - `Metadata`: Read-only (default) -6. **Events**: - - Subscribe to `Pull request` events. - -### 2. Install the App - -After creating the app, click **Install App** in the sidebar and install it on the repository where your documentation is hosted. - -### 3. Set Environment Variables - -Add the following variables to your deployment environment (e.g., Vercel, Netlify): - -```bash [.env] -GITHUB_APP_ID=your-app-id -GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----" -GITHUB_WEBHOOK_SECRET=your-webhook-secret -AI_GATEWAY_API_KEY=your-vercel-ai-gateway-key -``` - -## Configuration - -Enable the Review Agent in your `nuxt.config.ts`: - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - docus: { - agent: { - review: { - // Enable the review agent - enabled: true, - - // 'comment' (default) or 'commit' - mode: 'comment', - - // Target repository in 'org/repo' format (auto-detected if blank) - githubRepo: 'nuxt-content/docus' - } - } - } -}) -``` - -### Review Modes - -The agent can operate in two modes: - -- `comment`: The agent posts its findings and suggestions as a comment on the pull request. -- `commit`: The agent directly commits suggested documentation changes to the pull request branch. - -### Repository Detection - -By default, Docus attempts to auto-detect your GitHub repository from environment variables provided by CI providers (Vercel, Netlify, GitHub Actions) or from your local `.git/config`. If detection fails, you can explicitly set `githubRepo`. - -## Advanced Agent Configuration - -The Review Agent shares the same base configuration as the Chat Assistant: - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - docus: { - agent: { - // AI model to use for reviews - model: 'google/gemini-3-flash', - - // MCP server used to retrieve documentation context - mcpServer: '/mcp' - } - } -}) -``` From 79e85123a3226f4632285d35b9f4fda4b5237735 Mon Sep 17 00:00:00 2001 From: "docus-dev[bot]" <271635299+docus-dev[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2026 23:29:14 +0000 Subject: [PATCH 08/20] docs: document new AI Review Agent and update Assistant configuration - Move `assistant` configuration to `docus.agent.chat` in `nuxt.config.ts` - Add new `Review Agent` documentation for PR automated reviews - Update environment variables and setup instructions for GitHub App integration --- content/en/ai/assistant.md | 520 ++++++++++++++++++++++++++++++++++ content/en/ai/review-agent.md | 121 ++++++++ 2 files changed, 641 insertions(+) create mode 100644 content/en/ai/assistant.md create mode 100644 content/en/ai/review-agent.md diff --git a/content/en/ai/assistant.md b/content/en/ai/assistant.md new file mode 100644 index 000000000..a2eee8797 --- /dev/null +++ b/content/en/ai/assistant.md @@ -0,0 +1,520 @@ +# Assistant + +> Add AI-powered chat to your docs that answers questions, cites sources, and generates code examples. + +## About the Assistant + +The assistant answers questions about your documentation through natural language queries. It is embedded directly in your documentation site, so users can find answers quickly and succeed with your product. + +When users ask questions, the assistant: + +- **Searches and retrieves** relevant content from your documentation using an [MCP server](/en/ai/mcp). +- **Cites sources** with navigable links to take users directly to referenced pages. +- **Generates copyable code examples** to help users implement solutions from your documentation. + + + +The assistant requires an [AI Gateway](https://vercel.com/docs/ai-gateway) API key to function. + + + +## How It Works + +The assistant uses a multi-agent architecture: + +1. **Main Agent** - Receives user questions and decides when to search documentation +2. **Search Agent** - Uses [MCP server](/en/ai/mcp) tools to find relevant content +3. **Response Generation** - Synthesizes information into helpful, conversational answers + +By default, the assistant connects to your documentation's built-in MCP server at `/mcp`, giving it access to all your pages without additional configuration. You can also connect to an external MCP server if needed. + +## Quick Start + +### 1. Get an API Key + +Get an API key from [Vercel AI Gateway](https://vercel.com/~/ai/api-keys). AI Gateway works with multiple AI providers (OpenAI, Anthropic, Google, and more) through a unified API. + +### 2. Set Environment Variable + +Add your API key to your environment: + +```bash [.env] +AI_GATEWAY_API_KEY=your-api-key +``` + +### 3. Deploy + +That's it! The assistant is automatically enabled when an API key is detected. Deploy your documentation and the assistant will be available to your users. + +## Using the Assistant + +Users can interact with the assistant in multiple ways: + +### Floating Input + +On documentation pages, a floating input appears at the bottom of the screen. Users can type their questions directly and press Enter to get answers. + + + +Use the keyboard shortcut + + + + + + + + + + + + to focus the floating input. + + + +### Explain with AI + +Each documentation page includes an **Explain with AI** button in the table of contents sidebar. Clicking this button opens the assistant with the current page as context, asking it to explain the content. + +### Slideover Chat + +When a conversation starts, a slideover panel opens on the right side of the screen. This panel displays the conversation history and allows users to continue asking questions. + +## Configuration + +Configure the assistant UI through `app.config.ts`: + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + // Show the floating input on documentation pages + floatingInput: true, + + // Show the "Explain with AI" button in the sidebar + explainWithAi: true, + + // FAQ questions to display when chat is empty + faqQuestions: [], + + // Keyboard shortcuts + shortcuts: { + focusInput: 'meta_i' + }, + + // Custom icons + icons: { + trigger: 'i-lucide-sparkles', + explain: 'i-lucide-brain' + } + } +}) +``` + +### FAQ Questions + +Display suggested questions when the chat is empty. This helps users discover what they can ask. + +#### Simple Format + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + faqQuestions: [ + 'How do I install Docus?', + 'How do I customize the theme?', + 'How do I add components to my pages?' + ] + } +}) +``` + +#### Category Format + +Organize questions into categories: + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + faqQuestions: [ + { + category: 'Getting Started', + items: [ + 'How do I install Docus?', + 'What is the project structure?' + ] + }, + { + category: 'Customization', + items: [ + 'How do I change the theme colors?', + 'How do I add a custom logo?' + ] + } + ] + } +}) +``` + +#### Localized Format + +For multi-language documentation, provide FAQ questions per locale: + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + faqQuestions: { + en: [ + { category: 'Getting Started', items: ['How do I install?'] } + ], + fr: [ + { category: 'Démarrage', items: ['Comment installer ?'] } + ] + } + } +}) +``` + +## Keyboard Shortcuts + +Configure the keyboard shortcut for focusing the floating input: + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + shortcuts: { + // Default: 'meta_i' (Cmd+I on Mac, Ctrl+I on Windows) + focusInput: 'meta_k' // Change to Cmd/Ctrl+K + } + } +}) +``` + +The shortcut format uses underscores to separate keys. Common examples: + +- `meta_i` - Cmd+I (Mac) / Ctrl+I (Windows) +- `meta_k` - Cmd+K (Mac) / Ctrl+K (Windows) +- `ctrl_shift_p` - Ctrl+Shift+P + +## Custom Icons + +Customize the icons used by the assistant: + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + icons: { + // Icon for the trigger button and slideover header + trigger: 'i-lucide-bot', + + // Icon for the "Explain with AI" button + explain: 'i-lucide-lightbulb' + } + } +}) +``` + +Icons use the [Iconify](https://iconify.design/) format (e.g., `i-lucide-sparkles`, `i-heroicons-sparkles`). + +## Internationalization + +All UI texts are automatically translated based on the user's locale. Docus includes built-in translations for English and French. + +The following texts are translated: + +- Slideover title and placeholder +- Tooltip texts +- Button labels ("Clear chat", "Close", "Explain with AI") +- Status messages ("Thinking...", "Chat is cleared on refresh") + +## Disable Features + +### Disable the Floating Input + +Hide the floating input at the bottom of documentation pages: + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + floatingInput: false + } +}) +``` + +### Disable "Explain with AI" + +Hide the "Explain with AI" button in the documentation sidebar: + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + explainWithAi: false + } +}) +``` + +### Disable the Assistant Entirely + +The assistant is automatically enabled when an API key is detected. To explicitly disable it, you can either remove the environment variable or disable it in your `nuxt.config.ts`: + + + +```bash [.env] +# Comment out or remove the API key +# AI_GATEWAY_API_KEY=your-api-key +``` + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + chat: { + enabled: false + } + } + } +}) +``` + + + +## Advanced Configuration + +Configure advanced options in `nuxt.config.ts` using the `docus.agent` key: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + // AI model (uses AI SDK Gateway format) + model: 'google/gemini-3-flash', + + // MCP server (path or URL) + mcpServer: '/mcp', + + chat: { + // API endpoint path + apiPath: '/__docus__/assistant' + } + } + } +}) +``` + +### MCP Server Configuration + +The assistant uses an MCP server to access your documentation. You have two options: + +#### Use the Built-in MCP Server (Default) + +By default, the assistant uses Docus's built-in MCP server at `/mcp`: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + mcpServer: '/mcp' + } + } +}) +``` + + + +Make sure the MCP server is enabled in your configuration. If you've customized the MCP path, update `mcpServer` accordingly. + + + +#### Use an External MCP Server + +Connect to any external MCP server by providing a full URL: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + mcpServer: 'https://other-docs.example.com/mcp' + } + } +}) +``` + +This is useful when you want the assistant to answer questions from a different documentation source, or when connecting to a centralized knowledge base. + +### Custom AI Model + +The assistant uses `google/gemini-3-flash` by default. You can change this to any model supported by the AI SDK Gateway: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + model: 'anthropic/claude-opus-4.5' + } + } +}) +``` + +### Site Name in Responses + +The assistant automatically uses your site name in its responses. Configure the site name in `nuxt.config.ts`: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + site: { + name: 'My Documentation' + } +}) +``` + +This makes the assistant respond as "the My Documentation assistant" and speak with authority about your specific product. + +## Programmatic Access + +Use the `useAssistant` composable to control the assistant programmatically: + +```vue + + + +``` + +### Composable API + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Property + + Type + + Description +
+ + isEnabled + + + + ComputedRef + + + Whether the assistant is enabled (API key present) +
+ + isOpen + + + + Ref + + + Whether the slideover is open +
+ + open(message?, clearPrevious?) + + + + Function + + + Open the assistant, optionally with a message +
+ + close() + + + + Function + + + Close the assistant slideover +
+ + toggle() + + + + Function + + + Toggle the assistant open/closed +
+ + clearMessages() + + + + Function + + + Clear the conversation history +
diff --git a/content/en/ai/review-agent.md b/content/en/ai/review-agent.md new file mode 100644 index 000000000..47b5fea2b --- /dev/null +++ b/content/en/ai/review-agent.md @@ -0,0 +1,121 @@ +# Review Agent + +> Keep your documentation in sync with code changes using the Docus AI review agent. + +The Review Agent is an AI-powered tool that monitors your GitHub repository for pull requests. When a PR is opened or updated, the agent analyzes the changes and automatically suggests documentation updates to keep your docs in sync with your code. + +## About the Review Agent + +The agent acts as a virtual documentation maintainer. It uses the [built-in MCP server](/en/ai/mcp) to understand your project's documentation structure and content, then compares it with the code changes in a pull request. + +Depending on your configuration, the agent can: + +- **Analyze code diffs** to identify new features or breaking changes that require documentation. +- **Suggest updates** to existing documentation pages. +- **Create new pages** for major features that aren't yet documented. +- **Maintain consistency** in tone and style across your documentation. + +## Setup + +### 1. Get an AI Gateway API Key + +The review agent requires an [AI Gateway](https://vercel.com/docs/ai-gateway) API key. If you already set this up for the [Assistant](/en/ai/assistant), you can skip this step. + +```bash [.env] +AI_GATEWAY_API_KEY=your-api-key +``` + +### 2. Create a GitHub App + +To use the Review Agent, you need to create and install a GitHub App: + +1. Go to your **GitHub Settings > Developer settings > GitHub Apps** and click **New GitHub App**. +2. Set a **Name** and **Homepage URL** (your documentation site URL). +3. Under **Webhook**, set the **Webhook URL** to `https://your-docs-site.com/__docus__/webhook/github`. +4. Create a **Webhook secret** and save it for later. +5. Under **Permissions**, grant the following: + - **Pull requests**: Read & write + - **Contents**: Read & write (required for `commit` mode) + - **Metadata**: Read-only +6. Under **Events**, subscribe to **Pull request**. +7. After creating the app, **Generate a private key** and download the `.pem` file. +8. **Install** the app on your repository. + +### 3. Set Environment Variables + +Add the GitHub App credentials to your environment: + +```bash [.env] +# The App ID found in your GitHub App settings +GITHUB_APP_ID=123456 + +# The content of the .pem private key file +# For CI/CD, use a single line with \n for newlines +GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n..." + +# The Webhook secret you created +GITHUB_WEBHOOK_SECRET=your-webhook-secret +``` + +### 4. Enable the Agent + +Enable the review agent in your `nuxt.config.ts`: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + review: { + enabled: true + } + } + } +}) +``` + +## Configuration + +Configure the agent's behavior in `nuxt.config.ts`: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + review: { + enabled: true, + + // feedback mode: + // 'comment' (default): Posts suggested changes as a PR comment + // 'commit': Directly commits changes to the PR branch + mode: 'comment', + + // Target repository (owner/repo) + // Auto-detected from environment variables or .git folder + githubRepo: 'nuxt-content/docus' + } + } + } +}) +``` + +### Repository Auto-detection + +Docus automatically detects the GitHub repository when deployed on popular platforms (Vercel, Netlify, GitHub Actions, GitLab CI) or running in local development. If auto-detection fails, the agent will log a warning and you must specify `githubRepo` manually. + +## Advanced Configuration + +The review agent shares the same AI model and MCP server configuration as the [Assistant](/en/ai/assistant): + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + // AI model used for review and chat + model: 'google/gemini-3-flash', + + // MCP server used to read documentation + mcpServer: '/mcp' + } + } +}) +``` From b33fd889ec893ddcb95e9d72d32e464cae134eec Mon Sep 17 00:00:00 2001 From: Baptiste Leproux Date: Sat, 28 Mar 2026 00:31:03 +0100 Subject: [PATCH 09/20] up --- .../runtime/server/api/webhook/github.post.ts | 29 +++++++- layer/server/mcp/tools/get-page.ts | 45 ++++++++----- layer/server/routes/raw/[...slug].md.get.ts | 67 ------------------- 3 files changed, 53 insertions(+), 88 deletions(-) delete mode 100644 layer/server/routes/raw/[...slug].md.get.ts diff --git a/layer/modules/assistant/runtime/server/api/webhook/github.post.ts b/layer/modules/assistant/runtime/server/api/webhook/github.post.ts index b9d4f23fa..71fedf51f 100644 --- a/layer/modules/assistant/runtime/server/api/webhook/github.post.ts +++ b/layer/modules/assistant/runtime/server/api/webhook/github.post.ts @@ -1,3 +1,5 @@ +// TODO(workflow): import { start } from 'workflow/api' +import { agentReviewWorkflow } from '../../workflows/agent-review' import { getInstallationToken } from '../../utils/github-auth' import { fetchPrDiff, filterDiff, logDiff } from '../../utils/github-diff' import { verifyWebhookSignature } from '../../utils/github-webhook' @@ -19,12 +21,18 @@ export default defineEventHandler(async (event) => { if (!['opened', 'reopened', 'synchronize'].includes(payload.action)) return { ok: true } + // Skip events triggered by bots (e.g. the agent itself committing back to the PR branch) + if (payload.sender?.type === 'Bot') { + console.log(`[agent-review] Skipping bot-triggered event (sender: ${payload.sender.login})`) + return { ok: true } + } + const { installation, pull_request, repository } = payload if (pull_request.base.ref !== repository.default_branch) return { ok: true } const owner: string = repository.owner.login const repo: string = repository.name - const _branch: string = pull_request.head.ref + const branch: string = pull_request.head.ref const baseSha: string = pull_request.base.sha const headSha: string = pull_request.head.sha @@ -35,12 +43,27 @@ export default defineEventHandler(async (event) => { const filteredDiff = filterDiff(rawDiff) if (!filteredDiff) { - console.log('[doc-agent] No relevant files in diff, skipping') + console.log('[agent-review] No relevant files in diff, skipping') return { ok: true } } logDiff(filteredDiff) - // TODO Task 5: start(docAgentWorkflow, [{ branch, filteredDiff }]) + + const mcpPath = config.agent.mcpServer + const isExternalUrl = mcpPath.startsWith('http://') || mcpPath.startsWith('https://') + const baseURL = (config.app?.baseURL as string | undefined)?.replace(/\/$/, '') || '' + const mcpUrl = isExternalUrl + ? mcpPath + : import.meta.dev + ? `http://localhost:3000${baseURL}${mcpPath}` + : `${getRequestURL(event).origin}${baseURL}${mcpPath}` + + // Return immediately so GitHub doesn't retry the delivery — agent runs in the background + // TODO(workflow): replace with start(agentReviewWorkflow, [...]) for durable execution + event.waitUntil( + agentReviewWorkflow({ owner, repo, branch, filteredDiff, model: config.agent.model, mcpUrl, token, webhookSecret: config.webhookSecret }) + .catch(err => console.error('[agent-review] Workflow failed:', err)), + ) return { ok: true } }) diff --git a/layer/server/mcp/tools/get-page.ts b/layer/server/mcp/tools/get-page.ts index 1f2bec3e3..e3bb7ab62 100644 --- a/layer/server/mcp/tools/get-page.ts +++ b/layer/server/mcp/tools/get-page.ts @@ -31,28 +31,37 @@ WORKFLOW: This tool returns the complete page content including title, descripti ? getCollectionFromPath(path, availableLocales) : 'docs' + let page: { title: string, path: string, description: string, body: { value: unknown[] | null } | null } | null try { - const page = await queryCollection(event, collectionName as keyof Collections) + page = await queryCollection(event, collectionName as keyof Collections) .where('path', '=', path) - .select('title', 'path', 'description') - .first() - - if (!page) { - return errorResult('Page not found') - } - - const content = await event.$fetch(`/raw${path}.md`) - - return jsonResult({ - title: page.title, - path: page.path, - description: page.description, - content, - url: `${siteUrl}${page.path}`, - }) + .select('title', 'path', 'description', 'body') + .first() as typeof page } catch { - return errorResult('Failed to get page') + return errorResult('Failed to query page') + } + + if (!page) { + return errorResult('Page not found') } + + let content: string | undefined + if (page.body?.value) { + try { + content = await event.$fetch(`/raw${path}.md`) + } + catch { + // Raw fetch failed — return page metadata without content + } + } + + return jsonResult({ + title: page.title, + path: page.path, + description: page.description, + content, + url: `${siteUrl}${page.path}`, + }) }, }) diff --git a/layer/server/routes/raw/[...slug].md.get.ts b/layer/server/routes/raw/[...slug].md.get.ts deleted file mode 100644 index 904968780..000000000 --- a/layer/server/routes/raw/[...slug].md.get.ts +++ /dev/null @@ -1,67 +0,0 @@ -// TODO: upstream this fix to @nuxt/content -// Bug: page.body.value is null for pages with no markdown body (e.g. frontmatter-only / config-driven pages). -// The original route crashes at `page.body.value[0]?.[0]` when value is null. -// Fix: return 404 when body.value is null instead of crashing. - -// @ts-expect-error — minimark/stringify has no types entry in its exports map -import { stringify } from 'minimark/stringify' -import { queryCollection } from '@nuxt/content/server' -import type { Collections } from '@nuxt/content' -// @ts-expect-error — virtual module, no types -import contentCollections from '#content/manifest' -import { withLeadingSlash } from 'ufo' - -export default defineEventHandler(async (event) => { - const config = useRuntimeConfig(event) - const llmsConfig = (config as { llms?: { contentRawMarkdown?: boolean | { excludeCollections?: string[] } } }).llms - const slug = getRouterParam(event, 'slug.md') - - if (!slug?.endsWith('.md') || llmsConfig?.contentRawMarkdown === false) { - throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true }) - } - - let path = withLeadingSlash(slug.replace('.md', '')) - if (path.endsWith('/index')) { - path = path.substring(0, path.length - 6) - } - - const excludeCollections: string[] = (llmsConfig?.contentRawMarkdown as { excludeCollections?: string[] } | undefined)?.excludeCollections || [] - - const collectionNames: string[] = Object.entries(contentCollections as Record) - .filter(([key, value]) => value.type === 'page' && !excludeCollections.includes(key)) - .map(([key]) => key) - - let page: { title: string, description: string, body: { value: unknown[] | null }, links?: unknown[], meta?: { links?: unknown[] } } | null = null - for (const collection of collectionNames) { - page = await queryCollection(event, collection as keyof Collections).path(path).first() as typeof page - if (page) break - } - - if (!page) { - throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true }) - } - - // Fix: body.value can be null for pages with no markdown content - if (!page.body?.value) { - throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true }) - } - - if ((page.body.value[0] as unknown[])?.[0] !== 'h1') { - page.body.value.unshift(['blockquote', {}, page.description]) - page.body.value.unshift(['h1', {}, page.title]) - } - - const links = page.links || page.meta?.links - if (Array.isArray(links) && links.length > 0) { - const linkItems = (links as { label?: string, to?: string }[]) - .filter(link => link.label && link.to) - .map(link => ['li', {}, ['a', { href: link.to }, link.label]]) - if (linkItems.length > 0) { - page.body.value.push(['hr']) - page.body.value.push(['ul', {}, ...linkItems]) - } - } - - setHeader(event, 'Content-Type', 'text/markdown; charset=utf-8') - return stringify({ ...page.body, type: 'minimark' }, { format: 'markdown/html' }) -}) From 63d506739616b0071366643795fed4e2df20a360 Mon Sep 17 00:00:00 2001 From: "docus-dev[bot]" <271635299+docus-dev[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2026 23:43:08 +0000 Subject: [PATCH 10/20] docs: update AI agent configuration and document PR review agent --- docs/content/en/4.ai/1.assistant.md | 214 ++++++++++++++++++++---- docs/content/en/4.ai/3.review-agent.md | 85 ++++++++++ docs/content/fr/4.ai/1.assistant.md | 222 ++++++++++++++++++++----- docs/content/fr/4.ai/3.review-agent.md | 85 ++++++++++ 4 files changed, 535 insertions(+), 71 deletions(-) create mode 100644 docs/content/en/4.ai/3.review-agent.md create mode 100644 docs/content/fr/4.ai/3.review-agent.md diff --git a/docs/content/en/4.ai/1.assistant.md b/docs/content/en/4.ai/1.assistant.md index d39b9edc7..22036a1f3 100644 --- a/docs/content/en/4.ai/1.assistant.md +++ b/docs/content/en/4.ai/1.assistant.md @@ -1,9 +1,6 @@ ---- -title: Assistant -description: Add AI-powered chat to your docs that answers questions, cites sources, and generates code examples. -navigation: - icon: i-lucide-sparkles ---- +# Assistant + +> Add AI-powered chat to your docs that answers questions, cites sources, and generates code examples. ## About the Assistant @@ -15,9 +12,11 @@ When users ask questions, the assistant: - **Cites sources** with navigable links to take users directly to referenced pages. - **Generates copyable code examples** to help users implement solutions from your documentation. -::note + + The assistant requires an [AI Gateway](https://vercel.com/docs/ai-gateway) API key to function. -:: + + ## How It Works @@ -55,9 +54,23 @@ Users can interact with the assistant in multiple ways: On documentation pages, a floating input appears at the bottom of the screen. Users can type their questions directly and press Enter to get answers. -::tip -Use the keyboard shortcut :kbd{value="meta"} :kbd{value="I"} to focus the floating input. -:: + + +Use the keyboard shortcut + + + + + + + + + + + + to focus the floating input. + + ### Explain with AI @@ -177,6 +190,7 @@ export default defineAppConfig({ ``` The shortcut format uses underscores to separate keys. Common examples: + - `meta_i` - Cmd+I (Mac) / Ctrl+I (Windows) - `meta_k` - Cmd+K (Mac) / Ctrl+K (Windows) - `ctrl_shift_p` - Ctrl+Shift+P @@ -206,6 +220,7 @@ Icons use the [Iconify](https://iconify.design/) format (e.g., `i-lucide-sparkle All UI texts are automatically translated based on the user's locale. Docus includes built-in translations for English and French. The following texts are translated: + - Slideover title and placeholder - Tooltip texts - Button labels ("Clear chat", "Close", "Explain with AI") @@ -252,15 +267,19 @@ Configure advanced options in `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ - assistant: { - // AI model (uses AI SDK Gateway format) - model: 'google/gemini-3-flash', + docus: { + agent: { + // AI model (uses AI SDK Gateway format) + model: 'google/gemini-3-flash', - // MCP server (path or URL) - mcpServer: '/mcp', + // MCP server (path or URL) + mcpServer: '/mcp', - // API endpoint path - apiPath: '/__docus__/assistant' + chat: { + // API endpoint path + apiPath: '/__docus__/assistant' + } + } } }) ``` @@ -275,15 +294,19 @@ By default, the assistant uses Docus's built-in MCP server at `/mcp`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ - assistant: { - mcpServer: '/mcp' + docus: { + agent: { + mcpServer: '/mcp' + } } }) ``` -::warning + + Make sure the MCP server is enabled in your configuration. If you've customized the MCP path, update `mcpServer` accordingly. -:: + + #### Use an External MCP Server @@ -291,8 +314,10 @@ Connect to any external MCP server by providing a full URL: ```ts [nuxt.config.ts] export default defineNuxtConfig({ - assistant: { - mcpServer: 'https://other-docs.example.com/mcp' + docus: { + agent: { + mcpServer: 'https://other-docs.example.com/mcp' + } } }) ``` @@ -305,8 +330,10 @@ The assistant uses `google/gemini-3-flash` by default. You can change this to an ```ts [nuxt.config.ts] export default defineNuxtConfig({ - assistant: { - model: 'anthropic/claude-opus-4.5' + docus: { + agent: { + model: 'anthropic/claude-opus-4.5' + } } }) ``` @@ -348,11 +375,130 @@ function askQuestion() { ### Composable API -| Property | Type | Description | -|----------|------|-------------| -| `isEnabled` | `ComputedRef` | Whether the assistant is enabled (API key present) | -| `isOpen` | `Ref` | Whether the slideover is open | -| `open(message?, clearPrevious?)` | `Function` | Open the assistant, optionally with a message | -| `close()` | `Function` | Close the assistant slideover | -| `toggle()` | `Function` | Toggle the assistant open/closed | -| `clearMessages()` | `Function` | Clear the conversation history | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Property + + Type + + Description +
+ + isEnabled + + + + ComputedRef + + + Whether the chat assistant is enabled (API key present) +
+ + isOpen + + + + Ref + + + Whether the slideover is open +
+ + open(message?, clearPrevious?) + + + + Function + + + Open the assistant, optionally with a message +
+ + close() + + + + Function + + + Close the assistant slideover +
+ + toggle() + + + + Function + + + Toggle the assistant open/closed +
+ + clearMessages() + + + + Function + + + Clear the conversation history +
diff --git a/docs/content/en/4.ai/3.review-agent.md b/docs/content/en/4.ai/3.review-agent.md new file mode 100644 index 000000000..6772f5e6b --- /dev/null +++ b/docs/content/en/4.ai/3.review-agent.md @@ -0,0 +1,85 @@ +# Review Agent + +> PR documentation review agent triggered by GitHub webhooks. + +The Review Agent is an AI-powered tool that automatically reviews documentation changes in pull requests. It provides feedback on content quality, style, and consistency, helping maintain high documentation standards. + +## How It Works + +The Review Agent is triggered by GitHub webhooks whenever a pull request is opened or updated. It analyzes the changed files and provides feedback based on your configuration. + +1. **Webhook Trigger** - GitHub notifies your Docus site of PR activity. +2. **Analysis** - The agent uses your configured AI model to review the documentation diff. +3. **Feedback** - Suggestions are delivered either as PR comments or as direct commits to the branch. + +## Setup + +### 1. Enable the Agent + +Configure the review agent in your `nuxt.config.ts`: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + review: { + enabled: true, + // 'comment' (default) or 'commit' + mode: 'comment' + } + } + } +}) +``` + +### 2. GitHub App Integration + +The agent requires a GitHub App with permissions to read PRs and post comments (or commit changes). Set the following environment variables: + +- `GITHUB_APP_ID`: The ID of your GitHub App. +- `GITHUB_APP_PRIVATE_KEY`: The private key of your GitHub App. +- `GITHUB_WEBHOOK_SECRET`: The secret used to verify webhook payloads. + +### 3. Webhook Registration + +Register the following URL as a webhook in your GitHub App settings: + +`https://your-site.com/__docus__/webhook/github` + +Ensure the webhook is configured to receive `pull_request` events. + +## Configuration + +### mode + +Define how the agent provides feedback: +- `comment`: Post suggestions as comments on the pull request. +- `commit`: Automatically commit suggested improvements to the PR branch. + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + review: { + mode: 'commit' + } + } + } +}) +``` + +### githubRepo + +The agent automatically detects your GitHub repository from the environment (Vercel, Netlify, GitHub Actions) or local `.git/config`. You can override this manually: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + review: { + githubRepo: 'owner/repository' + } + } + } +}) +``` diff --git a/docs/content/fr/4.ai/1.assistant.md b/docs/content/fr/4.ai/1.assistant.md index 0d4f03649..022972ba6 100644 --- a/docs/content/fr/4.ai/1.assistant.md +++ b/docs/content/fr/4.ai/1.assistant.md @@ -1,9 +1,6 @@ ---- -title: Assistant -description: Ajoutez un chat propulsé par l'IA à votre documentation qui répond aux questions, cite les sources et génère des exemples de code. -navigation: - icon: i-lucide-sparkles ---- +# Assistant + +> Ajoutez un chat propulsé par l'IA à votre documentation qui répond aux questions, cite les sources et génère des exemples de code. ## À propos de l'Assistant @@ -15,9 +12,11 @@ Lorsque les utilisateurs posent des questions, l'assistant : - **Cite les sources** avec des liens navigables vers les pages référencées. - **Génère des exemples de code** copiables pour aider les utilisateurs à implémenter les solutions. -::note + + L'assistant nécessite une clé API [AI Gateway](https://vercel.com/docs/ai-gateway) pour fonctionner. -:: + + ## Comment ça fonctionne @@ -55,9 +54,23 @@ Les utilisateurs peuvent interagir avec l'assistant de plusieurs façons : Sur les pages de documentation, un champ de saisie flottant apparaît en bas de l'écran. Les utilisateurs peuvent taper leurs questions directement et appuyer sur Entrée pour obtenir des réponses. -::tip -Utilisez le raccourci clavier :kbd{value="meta"} :kbd{value="I"} pour activer l'input flottant. -:: + + +Utilisez le raccourci clavier + + + + + + + + + + + + pour activer l'input flottant. + + ### Expliquer avec l'IA @@ -169,7 +182,7 @@ Configurez le raccourci clavier pour activer l'input flottant : export default defineAppConfig({ assistant: { shortcuts: { - // Par défaut : 'meta_i' (Cmd+I sur Mac, Ctrl+I sur Windows) + // Par défaut : 'meta_i' (Cmd+I sur Mac, Ctrl+I on Windows) focusInput: 'meta_k' // Changer pour Cmd/Ctrl+K } } @@ -177,6 +190,7 @@ export default defineAppConfig({ ``` Le format de raccourci utilise des underscores pour séparer les touches. Exemples courants : + - `meta_i` - Cmd+I (Mac) / Ctrl+I (Windows) - `meta_k` - Cmd+K (Mac) / Ctrl+K (Windows) - `ctrl_shift_p` - Ctrl+Shift+P @@ -206,14 +220,17 @@ Les icônes utilisent le format [Iconify](https://iconify.design/) (ex: `i-lucid Tous les textes de l'interface sont automatiquement traduits selon la locale de l'utilisateur. Docus inclut des traductions intégrées pour l'anglais et le français. Les textes suivants sont traduits : + - Titre et placeholder du panneau - Textes des infobulles - Libellés des boutons ("Effacer le chat", "Fermer", "Expliquer avec l'IA") - Messages de statut ("Réflexion...", "Le chat est effacé au rechargement") -::tip + + Si aucun `title` ou `placeholder` personnalisé n'est défini dans `app.config.ts`, les valeurs traduites des fichiers i18n seront utilisées automatiquement. -:: + + ## Désactiver des fonctionnalités @@ -256,15 +273,19 @@ Configurez les options avancées dans `nuxt.config.ts` : ```ts [nuxt.config.ts] export default defineNuxtConfig({ - assistant: { - // Modèle IA (utilise le format AI SDK Gateway) - model: 'google/gemini-3-flash', + docus: { + agent: { + // Modèle IA (utilise le format AI SDK Gateway) + model: 'google/gemini-3-flash', - // Serveur MCP (chemin ou URL) - mcpServer: '/mcp', + // Serveur MCP (chemin ou URL) + mcpServer: '/mcp', - // Chemin de l'endpoint API - apiPath: '/__docus__/assistant' + chat: { + // Chemin de l'endpoint API + apiPath: '/__docus__/assistant' + } + } } }) ``` @@ -279,15 +300,19 @@ Par défaut, l'assistant utilise le serveur MCP intégré de Docus à `/mcp` : ```ts [nuxt.config.ts] export default defineNuxtConfig({ - assistant: { - mcpServer: '/mcp' + docus: { + agent: { + mcpServer: '/mcp' + } } }) ``` -::warning + + Assurez-vous que le serveur MCP est activé dans votre configuration. Si vous avez personnalisé le chemin MCP, mettez à jour `mcpServer` en conséquence. -:: + + #### Utiliser un serveur MCP externe @@ -295,8 +320,10 @@ Connectez-vous à n'importe quel serveur MCP externe en fournissant une URL comp ```ts [nuxt.config.ts] export default defineNuxtConfig({ - assistant: { - mcpServer: 'https://autre-docs.exemple.com/mcp' + docus: { + agent: { + mcpServer: 'https://autre-docs.exemple.com/mcp' + } } }) ``` @@ -309,8 +336,10 @@ L'assistant utilise `google/gemini-3-flash` par défaut. Vous pouvez le changer ```ts [nuxt.config.ts] export default defineNuxtConfig({ - assistant: { - model: 'anthropic/claude-opus-4.5' + docus: { + agent: { + model: 'anthropic/claude-opus-4.5' + } } }) ``` @@ -352,11 +381,130 @@ function askQuestion() { ### API du composable -| Propriété | Type | Description | -|-----------|------|-------------| -| `isEnabled` | `ComputedRef` | Si l'assistant est activé (clé API présente) | -| `isOpen` | `Ref` | Si le panneau est ouvert | -| `open(message?, clearPrevious?)` | `Function` | Ouvrir l'assistant, optionnellement avec un message | -| `close()` | `Function` | Fermer le panneau de l'assistant | -| `toggle()` | `Function` | Basculer l'assistant ouvert/fermé | -| `clearMessages()` | `Function` | Effacer l'historique de conversation | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Propriété + + Type + + Description +
+ + isEnabled + + + + ComputedRef + + + Si le chat de l'assistant est activé (clé API présente) +
+ + isOpen + + + + Ref + + + Si le panneau est ouvert +
+ + open(message?, clearPrevious?) + + + + Function + + + Ouvrir l'assistant, optionnellement avec un message +
+ + close() + + + + Function + + + Fermer le panneau de l'assistant +
+ + toggle() + + + + Function + + + Basculer l'assistant ouvert/fermé +
+ + clearMessages() + + + + Function + + + Effacer l'historique de conversation +
diff --git a/docs/content/fr/4.ai/3.review-agent.md b/docs/content/fr/4.ai/3.review-agent.md new file mode 100644 index 000000000..4dbe45d15 --- /dev/null +++ b/docs/content/fr/4.ai/3.review-agent.md @@ -0,0 +1,85 @@ +# Agent de révision + +> Agent de révision de documentation pour PR déclenché par les webhooks GitHub. + +L'Agent de révision est un outil propulsé par l'IA qui examine automatiquement les modifications de documentation dans les pull requests. Il fournit des commentaires sur la qualité, le style et la cohérence du contenu. + +## Fonctionnement + +L'Agent de révision est déclenché par les webhooks GitHub chaque fois qu'une pull request est ouverte ou mise à jour. Il analyse les fichiers modifiés et fournit des retours selon votre configuration. + +1. **Déclencheur Webhook** - GitHub notifie votre site Docus de l'activité sur les PR. +2. **Analyse** - L'agent utilise votre modèle IA configuré pour examiner le diff de la documentation. +3. **Retour** - Les suggestions sont livrées soit sous forme de commentaires sur la PR, soit sous forme de commits directs sur la branche. + +## Configuration + +### 1. Activer l'agent + +Configurez l'agent de révision dans votre `nuxt.config.ts` : + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + review: { + enabled: true, + // 'comment' (par défaut) ou 'commit' + mode: 'comment' + } + } + } +}) +``` + +### 2. Intégration GitHub App + +L'agent nécessite une GitHub App avec les permissions de lecture des PR et de publication de commentaires (ou de commit). Définissez les variables d'environnement suivantes : + +- `GITHUB_APP_ID` : L'ID de votre GitHub App. +- `GITHUB_APP_PRIVATE_KEY` : La clé privée de votre GitHub App. +- `GITHUB_WEBHOOK_SECRET` : Le secret utilisé pour vérifier les charges utiles des webhooks. + +### 3. Enregistrement du Webhook + +Enregistrez l'URL suivante en tant que webhook dans les paramètres de votre GitHub App : + +`https://votre-site.com/__docus__/webhook/github` + +Assurez-vous que le webhook est configuré pour recevoir les événements `pull_request`. + +## Options + +### mode + +Définit comment l'agent fournit ses retours : +- `comment` : Publie les suggestions sous forme de commentaires sur la pull request. +- `commit` : Valide automatiquement les améliorations suggérées sur la branche de la PR. + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + review: { + mode: 'commit' + } + } + } +}) +``` + +### githubRepo + +L'agent détecte automatiquement votre dépôt GitHub depuis l'environnement (Vercel, Netlify, GitHub Actions) ou le fichier `.git/config` local. Vous pouvez le surcharger manuellement : + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + review: { + githubRepo: 'propriétaire/dépôt' + } + } + } +}) +``` From 6d46c9745217af69e4e8eddf8ade64d9ab740b06 Mon Sep 17 00:00:00 2001 From: Baptiste Leproux Date: Sat, 28 Mar 2026 00:45:37 +0100 Subject: [PATCH 11/20] up --- .../runtime/server/workflows/agent-review.ts | 6 +++--- layer/server/mcp/prompts/review-pr-docs.ts | 6 +++--- layer/server/mcp/tools/commit-files.ts | 15 ++++++++++----- layer/server/mcp/tools/get-page.ts | 9 +++++++-- layer/server/mcp/tools/list-pages.ts | 7 ++++++- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/layer/modules/assistant/runtime/server/workflows/agent-review.ts b/layer/modules/assistant/runtime/server/workflows/agent-review.ts index 4faa53b63..f6b245517 100644 --- a/layer/modules/assistant/runtime/server/workflows/agent-review.ts +++ b/layer/modules/assistant/runtime/server/workflows/agent-review.ts @@ -54,8 +54,8 @@ You have been triggered by a pull request on ${owner}/${repo} (branch: ${branch} ## Your workflow -1. Call \`list-pages\` to discover the existing documentation structure -2. Call \`get-page\` on pages that appear related to the diff (by topic, file name, or module name) +1. Call \`list-pages\` to discover the existing documentation structure — it returns a \`filePath\` field for each page (the actual path in the repository to use with \`commit-files\`) +2. Call \`get-page\` on pages that appear related to the diff (by topic, file name, or module name) — it also returns \`filePath\` 3. Decide what documentation action to take for each relevant file: - **Existing page that is now outdated** → update the relevant section(s) in place — **this is always the preferred action** - **Brand-new major feature with absolutely no existing page** → only then create a new MDC page @@ -70,7 +70,7 @@ Always pass these exact values to \`commit-files\`: - repo: "${repo}" - branch: "${branch}" - token: "${token}" -- files: array of every \`{ path, content }\` you want to write, all in one call +- files: array of every \`{ path, content }\` you want to write, all in one call — use the \`filePath\` value from \`list-pages\`/\`get-page\` for existing files; for new files, follow the exact same directory pattern - message: a conventional commit message describing what changed and why (e.g. \`docs: document new \\\`useHead\\\` options added in this PR\`) ## MDC writing rules diff --git a/layer/server/mcp/prompts/review-pr-docs.ts b/layer/server/mcp/prompts/review-pr-docs.ts index 434d970d1..e6716b612 100644 --- a/layer/server/mcp/prompts/review-pr-docs.ts +++ b/layer/server/mcp/prompts/review-pr-docs.ts @@ -21,8 +21,8 @@ You have been triggered by a pull request on ${owner}/${repo} (branch: ${branch} ## Your workflow -1. Call \`list-pages\` to discover the existing documentation structure -2. Call \`get-page\` on pages that appear related to the diff (by topic, file name, or module name) +1. Call \`list-pages\` to discover the existing documentation structure — it returns a \`filePath\` field for each page (the actual path in the repository to use with \`commit-files\`) +2. Call \`get-page\` on pages that appear related to the diff (by topic, file name, or module name) — it also returns \`filePath\` 3. Decide what documentation action to take for each relevant file: - **Existing page that is now outdated** → update the relevant section(s) in place — **this is always the preferred action** - **Brand-new major feature with absolutely no existing page** → only then create a new MDC page @@ -37,7 +37,7 @@ Always pass these exact values to \`commit-files\`: - repo: "${repo}" - branch: "${branch}" - token: "${token}" -- files: array of every \`{ path, content }\` you want to write, all in one call +- files: array of every \`{ path, content }\` you want to write, all in one call — use the \`filePath\` value from \`list-pages\`/\`get-page\` for existing files; for new files, follow the exact same directory pattern - message: a conventional commit message describing what changed and why (e.g. \`docs: document new \\\`useHead\\\` options added in this PR\`) ## MDC writing rules diff --git a/layer/server/mcp/tools/commit-files.ts b/layer/server/mcp/tools/commit-files.ts index 2724d16c0..e27d0c4f8 100644 --- a/layer/server/mcp/tools/commit-files.ts +++ b/layer/server/mcp/tools/commit-files.ts @@ -34,7 +34,12 @@ export default defineMcpTool({ files: Array<{ path: string, content: string }> message: string }) => { - console.log(`[commit-files] Preparing commit: ${owner}/${repo} branch=${branch} files=${files.length}`) + const appConfig = useAppConfig() as { github?: { rootDir?: string } } + const contentRepoBase = appConfig.github?.rootDir + ? `${appConfig.github.rootDir}/content` + : 'content' + + console.log(`[commit-files] Preparing commit: ${owner}/${repo} branch=${branch} files=${files.length} contentRepoBase=${contentRepoBase}`) // --- Security guards --- @@ -87,10 +92,10 @@ export default defineMcpTool({ return errorResult(`Invalid file path: ${file.path}`) } - // Must live under content/ - if (!file.path.startsWith('content/')) { - console.warn(`[commit-files] Rejected: path outside content/: "${file.path}"`) - return errorResult(`File path must start with "content/": ${file.path}`) + // Must live under the content directory + if (!file.path.startsWith(`${contentRepoBase}/`)) { + console.warn(`[commit-files] Rejected: path outside ${contentRepoBase}/: "${file.path}"`) + return errorResult(`File path must start with "${contentRepoBase}/": ${file.path}`) } // Markdown-only diff --git a/layer/server/mcp/tools/get-page.ts b/layer/server/mcp/tools/get-page.ts index e3bb7ab62..43d1e9768 100644 --- a/layer/server/mcp/tools/get-page.ts +++ b/layer/server/mcp/tools/get-page.ts @@ -24,6 +24,10 @@ WORKFLOW: This tool returns the complete page content including title, descripti handler: async ({ path }) => { const event = useEvent() const config = useRuntimeConfig(event).public + const appConfig = useAppConfig() as { github?: { rootDir?: string } } + const contentRepoBase = appConfig.github?.rootDir + ? `${appConfig.github.rootDir}/content` + : 'content' const siteUrl = getRequestURL(event).origin || inferSiteURL() const availableLocales = getAvailableLocales(config) @@ -31,11 +35,11 @@ WORKFLOW: This tool returns the complete page content including title, descripti ? getCollectionFromPath(path, availableLocales) : 'docs' - let page: { title: string, path: string, description: string, body: { value: unknown[] | null } | null } | null + let page: { title: string, path: string, description: string, stem: string, extension: string, body: { value: unknown[] | null } | null } | null try { page = await queryCollection(event, collectionName as keyof Collections) .where('path', '=', path) - .select('title', 'path', 'description', 'body') + .select('title', 'path', 'description', 'stem', 'extension', 'body') .first() as typeof page } catch { @@ -60,6 +64,7 @@ WORKFLOW: This tool returns the complete page content including title, descripti title: page.title, path: page.path, description: page.description, + filePath: `${contentRepoBase}/${page.stem}.${page.extension}`, content, url: `${siteUrl}${page.path}`, }) diff --git a/layer/server/mcp/tools/list-pages.ts b/layer/server/mcp/tools/list-pages.ts index 66df7da26..87e5aff06 100644 --- a/layer/server/mcp/tools/list-pages.ts +++ b/layer/server/mcp/tools/list-pages.ts @@ -30,6 +30,10 @@ OUTPUT: Returns a structured list with: handler: async ({ locale }) => { const event = useEvent() const config = useRuntimeConfig(event).public + const appConfig = useAppConfig() as { github?: { rootDir?: string } } + const contentRepoBase = appConfig.github?.rootDir + ? `${appConfig.github.rootDir}/content` + : 'content' const siteUrl = getRequestURL(event).origin || inferSiteURL() const availableLocales = getAvailableLocales(config) @@ -39,13 +43,14 @@ OUTPUT: Returns a structured list with: const allPages = await Promise.all( collections.map(async (collectionName) => { const pages = await queryCollection(event, collectionName as keyof Collections) - .select('title', 'path', 'description') + .select('title', 'path', 'description', 'stem', 'extension') .all() return pages.map(page => ({ title: page.title, path: page.path, description: page.description, + filePath: `${contentRepoBase}/${page.stem}.${page.extension}`, locale: collectionName.replace('docs_', ''), url: `${siteUrl}${page.path}`, })) From 8aa8f9b984fd7e332a227343b6f32ce7b407c8f0 Mon Sep 17 00:00:00 2001 From: Baptiste Leproux Date: Sat, 28 Mar 2026 00:46:24 +0100 Subject: [PATCH 12/20] Revert "docs: update AI agent configuration and document PR review agent" This reverts commit 63d506739616b0071366643795fed4e2df20a360. --- docs/content/en/4.ai/1.assistant.md | 214 ++++-------------------- docs/content/en/4.ai/3.review-agent.md | 85 ---------- docs/content/fr/4.ai/1.assistant.md | 222 +++++-------------------- docs/content/fr/4.ai/3.review-agent.md | 85 ---------- 4 files changed, 71 insertions(+), 535 deletions(-) delete mode 100644 docs/content/en/4.ai/3.review-agent.md delete mode 100644 docs/content/fr/4.ai/3.review-agent.md diff --git a/docs/content/en/4.ai/1.assistant.md b/docs/content/en/4.ai/1.assistant.md index 22036a1f3..d39b9edc7 100644 --- a/docs/content/en/4.ai/1.assistant.md +++ b/docs/content/en/4.ai/1.assistant.md @@ -1,6 +1,9 @@ -# Assistant - -> Add AI-powered chat to your docs that answers questions, cites sources, and generates code examples. +--- +title: Assistant +description: Add AI-powered chat to your docs that answers questions, cites sources, and generates code examples. +navigation: + icon: i-lucide-sparkles +--- ## About the Assistant @@ -12,11 +15,9 @@ When users ask questions, the assistant: - **Cites sources** with navigable links to take users directly to referenced pages. - **Generates copyable code examples** to help users implement solutions from your documentation. - - +::note The assistant requires an [AI Gateway](https://vercel.com/docs/ai-gateway) API key to function. - - +:: ## How It Works @@ -54,23 +55,9 @@ Users can interact with the assistant in multiple ways: On documentation pages, a floating input appears at the bottom of the screen. Users can type their questions directly and press Enter to get answers. - - -Use the keyboard shortcut - - - - - - - - - - - - to focus the floating input. - - +::tip +Use the keyboard shortcut :kbd{value="meta"} :kbd{value="I"} to focus the floating input. +:: ### Explain with AI @@ -190,7 +177,6 @@ export default defineAppConfig({ ``` The shortcut format uses underscores to separate keys. Common examples: - - `meta_i` - Cmd+I (Mac) / Ctrl+I (Windows) - `meta_k` - Cmd+K (Mac) / Ctrl+K (Windows) - `ctrl_shift_p` - Ctrl+Shift+P @@ -220,7 +206,6 @@ Icons use the [Iconify](https://iconify.design/) format (e.g., `i-lucide-sparkle All UI texts are automatically translated based on the user's locale. Docus includes built-in translations for English and French. The following texts are translated: - - Slideover title and placeholder - Tooltip texts - Button labels ("Clear chat", "Close", "Explain with AI") @@ -267,19 +252,15 @@ Configure advanced options in `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ - docus: { - agent: { - // AI model (uses AI SDK Gateway format) - model: 'google/gemini-3-flash', + assistant: { + // AI model (uses AI SDK Gateway format) + model: 'google/gemini-3-flash', - // MCP server (path or URL) - mcpServer: '/mcp', + // MCP server (path or URL) + mcpServer: '/mcp', - chat: { - // API endpoint path - apiPath: '/__docus__/assistant' - } - } + // API endpoint path + apiPath: '/__docus__/assistant' } }) ``` @@ -294,19 +275,15 @@ By default, the assistant uses Docus's built-in MCP server at `/mcp`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ - docus: { - agent: { - mcpServer: '/mcp' - } + assistant: { + mcpServer: '/mcp' } }) ``` - - +::warning Make sure the MCP server is enabled in your configuration. If you've customized the MCP path, update `mcpServer` accordingly. - - +:: #### Use an External MCP Server @@ -314,10 +291,8 @@ Connect to any external MCP server by providing a full URL: ```ts [nuxt.config.ts] export default defineNuxtConfig({ - docus: { - agent: { - mcpServer: 'https://other-docs.example.com/mcp' - } + assistant: { + mcpServer: 'https://other-docs.example.com/mcp' } }) ``` @@ -330,10 +305,8 @@ The assistant uses `google/gemini-3-flash` by default. You can change this to an ```ts [nuxt.config.ts] export default defineNuxtConfig({ - docus: { - agent: { - model: 'anthropic/claude-opus-4.5' - } + assistant: { + model: 'anthropic/claude-opus-4.5' } }) ``` @@ -375,130 +348,11 @@ function askQuestion() { ### Composable API - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Property - - Type - - Description -
- - isEnabled - - - - ComputedRef - - - Whether the chat assistant is enabled (API key present) -
- - isOpen - - - - Ref - - - Whether the slideover is open -
- - open(message?, clearPrevious?) - - - - Function - - - Open the assistant, optionally with a message -
- - close() - - - - Function - - - Close the assistant slideover -
- - toggle() - - - - Function - - - Toggle the assistant open/closed -
- - clearMessages() - - - - Function - - - Clear the conversation history -
+| Property | Type | Description | +|----------|------|-------------| +| `isEnabled` | `ComputedRef` | Whether the assistant is enabled (API key present) | +| `isOpen` | `Ref` | Whether the slideover is open | +| `open(message?, clearPrevious?)` | `Function` | Open the assistant, optionally with a message | +| `close()` | `Function` | Close the assistant slideover | +| `toggle()` | `Function` | Toggle the assistant open/closed | +| `clearMessages()` | `Function` | Clear the conversation history | diff --git a/docs/content/en/4.ai/3.review-agent.md b/docs/content/en/4.ai/3.review-agent.md deleted file mode 100644 index 6772f5e6b..000000000 --- a/docs/content/en/4.ai/3.review-agent.md +++ /dev/null @@ -1,85 +0,0 @@ -# Review Agent - -> PR documentation review agent triggered by GitHub webhooks. - -The Review Agent is an AI-powered tool that automatically reviews documentation changes in pull requests. It provides feedback on content quality, style, and consistency, helping maintain high documentation standards. - -## How It Works - -The Review Agent is triggered by GitHub webhooks whenever a pull request is opened or updated. It analyzes the changed files and provides feedback based on your configuration. - -1. **Webhook Trigger** - GitHub notifies your Docus site of PR activity. -2. **Analysis** - The agent uses your configured AI model to review the documentation diff. -3. **Feedback** - Suggestions are delivered either as PR comments or as direct commits to the branch. - -## Setup - -### 1. Enable the Agent - -Configure the review agent in your `nuxt.config.ts`: - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - docus: { - agent: { - review: { - enabled: true, - // 'comment' (default) or 'commit' - mode: 'comment' - } - } - } -}) -``` - -### 2. GitHub App Integration - -The agent requires a GitHub App with permissions to read PRs and post comments (or commit changes). Set the following environment variables: - -- `GITHUB_APP_ID`: The ID of your GitHub App. -- `GITHUB_APP_PRIVATE_KEY`: The private key of your GitHub App. -- `GITHUB_WEBHOOK_SECRET`: The secret used to verify webhook payloads. - -### 3. Webhook Registration - -Register the following URL as a webhook in your GitHub App settings: - -`https://your-site.com/__docus__/webhook/github` - -Ensure the webhook is configured to receive `pull_request` events. - -## Configuration - -### mode - -Define how the agent provides feedback: -- `comment`: Post suggestions as comments on the pull request. -- `commit`: Automatically commit suggested improvements to the PR branch. - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - docus: { - agent: { - review: { - mode: 'commit' - } - } - } -}) -``` - -### githubRepo - -The agent automatically detects your GitHub repository from the environment (Vercel, Netlify, GitHub Actions) or local `.git/config`. You can override this manually: - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - docus: { - agent: { - review: { - githubRepo: 'owner/repository' - } - } - } -}) -``` diff --git a/docs/content/fr/4.ai/1.assistant.md b/docs/content/fr/4.ai/1.assistant.md index 022972ba6..0d4f03649 100644 --- a/docs/content/fr/4.ai/1.assistant.md +++ b/docs/content/fr/4.ai/1.assistant.md @@ -1,6 +1,9 @@ -# Assistant - -> Ajoutez un chat propulsé par l'IA à votre documentation qui répond aux questions, cite les sources et génère des exemples de code. +--- +title: Assistant +description: Ajoutez un chat propulsé par l'IA à votre documentation qui répond aux questions, cite les sources et génère des exemples de code. +navigation: + icon: i-lucide-sparkles +--- ## À propos de l'Assistant @@ -12,11 +15,9 @@ Lorsque les utilisateurs posent des questions, l'assistant : - **Cite les sources** avec des liens navigables vers les pages référencées. - **Génère des exemples de code** copiables pour aider les utilisateurs à implémenter les solutions. - - +::note L'assistant nécessite une clé API [AI Gateway](https://vercel.com/docs/ai-gateway) pour fonctionner. - - +:: ## Comment ça fonctionne @@ -54,23 +55,9 @@ Les utilisateurs peuvent interagir avec l'assistant de plusieurs façons : Sur les pages de documentation, un champ de saisie flottant apparaît en bas de l'écran. Les utilisateurs peuvent taper leurs questions directement et appuyer sur Entrée pour obtenir des réponses. - - -Utilisez le raccourci clavier - - - - - - - - - - - - pour activer l'input flottant. - - +::tip +Utilisez le raccourci clavier :kbd{value="meta"} :kbd{value="I"} pour activer l'input flottant. +:: ### Expliquer avec l'IA @@ -182,7 +169,7 @@ Configurez le raccourci clavier pour activer l'input flottant : export default defineAppConfig({ assistant: { shortcuts: { - // Par défaut : 'meta_i' (Cmd+I sur Mac, Ctrl+I on Windows) + // Par défaut : 'meta_i' (Cmd+I sur Mac, Ctrl+I sur Windows) focusInput: 'meta_k' // Changer pour Cmd/Ctrl+K } } @@ -190,7 +177,6 @@ export default defineAppConfig({ ``` Le format de raccourci utilise des underscores pour séparer les touches. Exemples courants : - - `meta_i` - Cmd+I (Mac) / Ctrl+I (Windows) - `meta_k` - Cmd+K (Mac) / Ctrl+K (Windows) - `ctrl_shift_p` - Ctrl+Shift+P @@ -220,17 +206,14 @@ Les icônes utilisent le format [Iconify](https://iconify.design/) (ex: `i-lucid Tous les textes de l'interface sont automatiquement traduits selon la locale de l'utilisateur. Docus inclut des traductions intégrées pour l'anglais et le français. Les textes suivants sont traduits : - - Titre et placeholder du panneau - Textes des infobulles - Libellés des boutons ("Effacer le chat", "Fermer", "Expliquer avec l'IA") - Messages de statut ("Réflexion...", "Le chat est effacé au rechargement") - - +::tip Si aucun `title` ou `placeholder` personnalisé n'est défini dans `app.config.ts`, les valeurs traduites des fichiers i18n seront utilisées automatiquement. - - +:: ## Désactiver des fonctionnalités @@ -273,19 +256,15 @@ Configurez les options avancées dans `nuxt.config.ts` : ```ts [nuxt.config.ts] export default defineNuxtConfig({ - docus: { - agent: { - // Modèle IA (utilise le format AI SDK Gateway) - model: 'google/gemini-3-flash', + assistant: { + // Modèle IA (utilise le format AI SDK Gateway) + model: 'google/gemini-3-flash', - // Serveur MCP (chemin ou URL) - mcpServer: '/mcp', + // Serveur MCP (chemin ou URL) + mcpServer: '/mcp', - chat: { - // Chemin de l'endpoint API - apiPath: '/__docus__/assistant' - } - } + // Chemin de l'endpoint API + apiPath: '/__docus__/assistant' } }) ``` @@ -300,19 +279,15 @@ Par défaut, l'assistant utilise le serveur MCP intégré de Docus à `/mcp` : ```ts [nuxt.config.ts] export default defineNuxtConfig({ - docus: { - agent: { - mcpServer: '/mcp' - } + assistant: { + mcpServer: '/mcp' } }) ``` - - +::warning Assurez-vous que le serveur MCP est activé dans votre configuration. Si vous avez personnalisé le chemin MCP, mettez à jour `mcpServer` en conséquence. - - +:: #### Utiliser un serveur MCP externe @@ -320,10 +295,8 @@ Connectez-vous à n'importe quel serveur MCP externe en fournissant une URL comp ```ts [nuxt.config.ts] export default defineNuxtConfig({ - docus: { - agent: { - mcpServer: 'https://autre-docs.exemple.com/mcp' - } + assistant: { + mcpServer: 'https://autre-docs.exemple.com/mcp' } }) ``` @@ -336,10 +309,8 @@ L'assistant utilise `google/gemini-3-flash` par défaut. Vous pouvez le changer ```ts [nuxt.config.ts] export default defineNuxtConfig({ - docus: { - agent: { - model: 'anthropic/claude-opus-4.5' - } + assistant: { + model: 'anthropic/claude-opus-4.5' } }) ``` @@ -381,130 +352,11 @@ function askQuestion() { ### API du composable - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Propriété - - Type - - Description -
- - isEnabled - - - - ComputedRef - - - Si le chat de l'assistant est activé (clé API présente) -
- - isOpen - - - - Ref - - - Si le panneau est ouvert -
- - open(message?, clearPrevious?) - - - - Function - - - Ouvrir l'assistant, optionnellement avec un message -
- - close() - - - - Function - - - Fermer le panneau de l'assistant -
- - toggle() - - - - Function - - - Basculer l'assistant ouvert/fermé -
- - clearMessages() - - - - Function - - - Effacer l'historique de conversation -
+| Propriété | Type | Description | +|-----------|------|-------------| +| `isEnabled` | `ComputedRef` | Si l'assistant est activé (clé API présente) | +| `isOpen` | `Ref` | Si le panneau est ouvert | +| `open(message?, clearPrevious?)` | `Function` | Ouvrir l'assistant, optionnellement avec un message | +| `close()` | `Function` | Fermer le panneau de l'assistant | +| `toggle()` | `Function` | Basculer l'assistant ouvert/fermé | +| `clearMessages()` | `Function` | Effacer l'historique de conversation | diff --git a/docs/content/fr/4.ai/3.review-agent.md b/docs/content/fr/4.ai/3.review-agent.md deleted file mode 100644 index 4dbe45d15..000000000 --- a/docs/content/fr/4.ai/3.review-agent.md +++ /dev/null @@ -1,85 +0,0 @@ -# Agent de révision - -> Agent de révision de documentation pour PR déclenché par les webhooks GitHub. - -L'Agent de révision est un outil propulsé par l'IA qui examine automatiquement les modifications de documentation dans les pull requests. Il fournit des commentaires sur la qualité, le style et la cohérence du contenu. - -## Fonctionnement - -L'Agent de révision est déclenché par les webhooks GitHub chaque fois qu'une pull request est ouverte ou mise à jour. Il analyse les fichiers modifiés et fournit des retours selon votre configuration. - -1. **Déclencheur Webhook** - GitHub notifie votre site Docus de l'activité sur les PR. -2. **Analyse** - L'agent utilise votre modèle IA configuré pour examiner le diff de la documentation. -3. **Retour** - Les suggestions sont livrées soit sous forme de commentaires sur la PR, soit sous forme de commits directs sur la branche. - -## Configuration - -### 1. Activer l'agent - -Configurez l'agent de révision dans votre `nuxt.config.ts` : - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - docus: { - agent: { - review: { - enabled: true, - // 'comment' (par défaut) ou 'commit' - mode: 'comment' - } - } - } -}) -``` - -### 2. Intégration GitHub App - -L'agent nécessite une GitHub App avec les permissions de lecture des PR et de publication de commentaires (ou de commit). Définissez les variables d'environnement suivantes : - -- `GITHUB_APP_ID` : L'ID de votre GitHub App. -- `GITHUB_APP_PRIVATE_KEY` : La clé privée de votre GitHub App. -- `GITHUB_WEBHOOK_SECRET` : Le secret utilisé pour vérifier les charges utiles des webhooks. - -### 3. Enregistrement du Webhook - -Enregistrez l'URL suivante en tant que webhook dans les paramètres de votre GitHub App : - -`https://votre-site.com/__docus__/webhook/github` - -Assurez-vous que le webhook est configuré pour recevoir les événements `pull_request`. - -## Options - -### mode - -Définit comment l'agent fournit ses retours : -- `comment` : Publie les suggestions sous forme de commentaires sur la pull request. -- `commit` : Valide automatiquement les améliorations suggérées sur la branche de la PR. - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - docus: { - agent: { - review: { - mode: 'commit' - } - } - } -}) -``` - -### githubRepo - -L'agent détecte automatiquement votre dépôt GitHub depuis l'environnement (Vercel, Netlify, GitHub Actions) ou le fichier `.git/config` local. Vous pouvez le surcharger manuellement : - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - docus: { - agent: { - review: { - githubRepo: 'propriétaire/dépôt' - } - } - } -}) -``` From 662fb047a48c4eb1fd0affab581c875fc4e106f4 Mon Sep 17 00:00:00 2001 From: "docus-dev[bot]" <271635299+docus-dev[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2026 23:48:28 +0000 Subject: [PATCH 13/20] docs: document AI Agent Review and configuration changes --- docs/content/en/4.ai/1.assistant.md | 415 ++++++++-------------------- docs/content/en/4.ai/2.mcp.md | 121 ++++++-- 2 files changed, 209 insertions(+), 327 deletions(-) diff --git a/docs/content/en/4.ai/1.assistant.md b/docs/content/en/4.ai/1.assistant.md index d39b9edc7..7263d4cdd 100644 --- a/docs/content/en/4.ai/1.assistant.md +++ b/docs/content/en/4.ai/1.assistant.md @@ -1,330 +1,142 @@ ---- -title: Assistant -description: Add AI-powered chat to your docs that answers questions, cites sources, and generates code examples. -navigation: - icon: i-lucide-sparkles ---- +# AI Agent -## About the Assistant +> Add AI-powered chat and automated PR documentation reviews to your docs. -The assistant answers questions about your documentation through natural language queries. It is embedded directly in your documentation site, so users can find answers quickly and succeed with your product. - -When users ask questions, the assistant: - -- **Searches and retrieves** relevant content from your documentation using an [MCP server](/en/ai/mcp). -- **Cites sources** with navigable links to take users directly to referenced pages. -- **Generates copyable code examples** to help users implement solutions from your documentation. - -::note -The assistant requires an [AI Gateway](https://vercel.com/docs/ai-gateway) API key to function. -:: - -## How It Works - -The assistant uses a multi-agent architecture: - -1. **Main Agent** - Receives user questions and decides when to search documentation -2. **Search Agent** - Uses [MCP server](/en/ai/mcp) tools to find relevant content -3. **Response Generation** - Synthesizes information into helpful, conversational answers - -By default, the assistant connects to your documentation's built-in MCP server at `/mcp`, giving it access to all your pages without additional configuration. You can also connect to an external MCP server if needed. - -## Quick Start - -### 1. Get an API Key - -Get an API key from [Vercel AI Gateway](https://vercel.com/~/ai/api-keys). AI Gateway works with multiple AI providers (OpenAI, Anthropic, Google, and more) through a unified API. - -### 2. Set Environment Variable - -Add your API key to your environment: - -```bash [.env] -AI_GATEWAY_API_KEY=your-api-key -``` - -### 3. Deploy - -That's it! The assistant is automatically enabled when an API key is detected. Deploy your documentation and the assistant will be available to your users. - -## Using the Assistant - -Users can interact with the assistant in multiple ways: - -### Floating Input - -On documentation pages, a floating input appears at the bottom of the screen. Users can type their questions directly and press Enter to get answers. - -::tip -Use the keyboard shortcut :kbd{value="meta"} :kbd{value="I"} to focus the floating input. -:: - -### Explain with AI - -Each documentation page includes an **Explain with AI** button in the table of contents sidebar. Clicking this button opens the assistant with the current page as context, asking it to explain the content. - -### Slideover Chat - -When a conversation starts, a slideover panel opens on the right side of the screen. This panel displays the conversation history and allows users to continue asking questions. - -## Configuration - -Configure the assistant through `app.config.ts`: - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - // Show the floating input on documentation pages - floatingInput: true, - - // Show the "Explain with AI" button in the sidebar - explainWithAi: true, - - // FAQ questions to display when chat is empty - faqQuestions: [], - - // Keyboard shortcuts - shortcuts: { - focusInput: 'meta_i' - }, - - // Custom icons - icons: { - trigger: 'i-lucide-sparkles', - explain: 'i-lucide-brain' - } - } -}) -``` - -### FAQ Questions - -Display suggested questions when the chat is empty. This helps users discover what they can ask. - -#### Simple Format - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - faqQuestions: [ - 'How do I install Docus?', - 'How do I customize the theme?', - 'How do I add components to my pages?' - ] - } -}) -``` - -#### Category Format - -Organize questions into categories: - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - faqQuestions: [ - { - category: 'Getting Started', - items: [ - 'How do I install Docus?', - 'What is the project structure?' - ] - }, - { - category: 'Customization', - items: [ - 'How do I change the theme colors?', - 'How do I add a custom logo?' - ] - } - ] - } -}) -``` - -#### Localized Format - -For multi-language documentation, provide FAQ questions per locale: - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - faqQuestions: { - en: [ - { category: 'Getting Started', items: ['How do I install?'] } - ], - fr: [ - { category: 'Démarrage', items: ['Comment installer ?'] } - ] - } - } -}) -``` - -## Keyboard Shortcuts - -Configure the keyboard shortcut for focusing the floating input: - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - shortcuts: { - // Default: 'meta_i' (Cmd+I on Mac, Ctrl+I on Windows) - focusInput: 'meta_k' // Change to Cmd/Ctrl+K - } - } -}) -``` - -The shortcut format uses underscores to separate keys. Common examples: -- `meta_i` - Cmd+I (Mac) / Ctrl+I (Windows) -- `meta_k` - Cmd+K (Mac) / Ctrl+K (Windows) -- `ctrl_shift_p` - Ctrl+Shift+P - -## Custom Icons - -Customize the icons used by the assistant: - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - icons: { - // Icon for the trigger button and slideover header - trigger: 'i-lucide-bot', - - // Icon for the "Explain with AI" button - explain: 'i-lucide-lightbulb' - } - } -}) -``` - -Icons use the [Iconify](https://iconify.design/) format (e.g., `i-lucide-sparkles`, `i-heroicons-sparkles`). +## Overview -## Internationalization +The Docus AI Agent brings advanced AI capabilities to your documentation, helping both your users and your developers. -All UI texts are automatically translated based on the user's locale. Docus includes built-in translations for English and French. +1. **AI Assistant (Chat)** - Answers user questions directly on your documentation site, citing sources and generating code examples. +2. **Agent Review** - Automatically reviews pull requests to ensure documentation stays in sync with code changes, committing or commenting with the necessary updates. -The following texts are translated: -- Slideover title and placeholder -- Tooltip texts -- Button labels ("Clear chat", "Close", "Explain with AI") -- Status messages ("Thinking...", "Chat is cleared on refresh") + -## Disable Features +Both features require an [AI Gateway](https://vercel.com/docs/ai-gateway) API key to function. -### Disable the Floating Input + -Hide the floating input at the bottom of documentation pages: +## Assistant (Chat) -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - floatingInput: false - } -}) -``` - -### Disable "Explain with AI" +The assistant answers questions about your documentation through natural language queries. It is embedded directly in your documentation site, so users can find answers quickly and succeed with your product. -Hide the "Explain with AI" button in the documentation sidebar: +### How It Works -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - explainWithAi: false - } -}) -``` - -### Disable the Assistant Entirely +When users ask questions, the assistant: -The assistant is automatically disabled when no API key is set. To explicitly disable it, simply remove the environment variable: +- **Searches and retrieves** relevant content from your documentation using an [MCP server](/en/ai/mcp). +- **Cites sources** with navigable links to take users directly to referenced pages. +- **Generates copyable code examples** to help users implement solutions from your documentation. -```bash [.env] -# Comment out or remove the API key -# AI_GATEWAY_API_KEY=your-api-key -``` +### Quick Start + +1. **Get an API Key** - Get an API key from [Vercel AI Gateway](https://vercel.com/~/ai/api-keys). +2. **Set Environment Variable** - Add your API key to your environment: + ```bash [.env] + AI_GATEWAY_API_KEY=your-api-key + ``` +3. **Deploy** - The assistant is automatically enabled when an API key is detected. + +### Using the Assistant + +- **Floating Input**: A floating input appears at the bottom of documentation pages. Press I to focus. +- **Explain with AI**: A button in the sidebar that asks the assistant to explain the current page. +- **Slideover Chat**: A panel on the right side of the screen that displays the conversation history. + +## Agent Review + +The review agent monitors pull requests on your GitHub repository. When a PR is opened or updated, the agent analyzes the diff and uses the [built-in MCP tools](/en/ai/mcp#built-in-tools) to find relevant documentation pages. It then suggests or commits documentation updates directly to the PR branch. + +### Quick Start + +1. **Create a GitHub App** - Go to your GitHub organization settings and create a new GitHub App with: + - **Repository permissions**: `Content (Read & Write)`, `Pull Requests (Read & Write)` + - **Events**: `Pull request` +2. **Configure Environment Variables** - Set the following variables in your production environment: + ```bash [.env] + GITHUB_APP_ID=your-app-id + GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----" + GITHUB_WEBHOOK_SECRET=your-webhook-secret + ``` +3. **Enable Agent Review** - Update your `nuxt.config.ts`: + ```ts [nuxt.config.ts] + export default defineNuxtConfig({ + docus: { + agent: { + review: { + enabled: true + } + } + } + }) + ``` +4. **Register Webhook** - Deploy your site and set the GitHub App's Webhook URL to `https://your-site.com/__docus__/webhook/github`. + +### Configuration Options + +| Option | Type | Default | Description | +| --- | --- | --- | --- | +| `enabled` | `boolean` | `false` | Enable the webhook handler and review workflow. | +| `mode` | `'comment' \| 'commit'` | `'comment'` | Whether the agent should post a PR comment or commit changes directly. | +| `githubRepo` | `string` | `auto` | Target repository in `owner/repo` format. Auto-detected on Vercel, Netlify, and CI. | ## Advanced Configuration -Configure advanced options in `nuxt.config.ts`: - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - assistant: { - // AI model (uses AI SDK Gateway format) - model: 'google/gemini-3-flash', - - // MCP server (path or URL) - mcpServer: '/mcp', - - // API endpoint path - apiPath: '/__docus__/assistant' - } -}) -``` - -### MCP Server Configuration - -The assistant uses an MCP server to access your documentation. You have two options: - -#### Use the Built-in MCP Server (Default) - -By default, the assistant uses Docus's built-in MCP server at `/mcp`: +Configure the AI agent in your `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ - assistant: { - mcpServer: '/mcp' - } -}) -``` + docus: { + agent: { + // AI model (uses AI SDK Gateway format) + model: 'google/gemini-3-flash', -::warning -Make sure the MCP server is enabled in your configuration. If you've customized the MCP path, update `mcpServer` accordingly. -:: + // MCP server (path or URL) + mcpServer: '/mcp', -#### Use an External MCP Server - -Connect to any external MCP server by providing a full URL: + chat: { + // API endpoint path for the chat assistant + apiPath: '/__docus__/assistant' + }, -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - assistant: { - mcpServer: 'https://other-docs.example.com/mcp' + review: { + // Enable automated PR documentation reviews + enabled: true, + // How the agent provides feedback + mode: 'comment' + } + } } }) ``` -This is useful when you want the assistant to answer questions from a different documentation source, or when connecting to a centralized knowledge base. +### AI Model -### Custom AI Model - -The assistant uses `google/gemini-3-flash` by default. You can change this to any model supported by the AI SDK Gateway: +The agent uses `google/gemini-3-flash` by default. You can change this to any model supported by the AI SDK Gateway: ```ts [nuxt.config.ts] export default defineNuxtConfig({ - assistant: { - model: 'anthropic/claude-opus-4.5' + docus: { + agent: { + model: 'anthropic/claude-3-5-sonnet' + } } }) ``` -### Site Name in Responses +### MCP Server -The assistant automatically uses your site name in its responses. Configure the site name in `nuxt.config.ts`: +The agent uses an MCP server to access your documentation. By default, it uses Docus's built-in MCP server at `/mcp`. You can connect to an external server: ```ts [nuxt.config.ts] export default defineNuxtConfig({ - site: { - name: 'My Documentation' + docus: { + agent: { + mcpServer: 'https://other-docs.example.com/mcp' + } } }) ``` -This makes the assistant respond as "the My Documentation assistant" and speak with authority about your specific product. - ## Programmatic Access Use the `useAssistant` composable to control the assistant programmatically: @@ -332,27 +144,22 @@ Use the `useAssistant` composable to control the assistant programmatically: ```vue +``` + +`isEnabled` returns `true` if the chat assistant is enabled (API key present and not explicitly disabled). - +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + // FAQ questions, shortcuts, and icons are still configured in app.config.ts + faqQuestions: ['How do I install Docus?'] + } +}) ``` -### Composable API + + +Learn more about the MCP server that powers the AI Agent. -| Property | Type | Description | -|----------|------|-------------| -| `isEnabled` | `ComputedRef` | Whether the assistant is enabled (API key present) | -| `isOpen` | `Ref` | Whether the slideover is open | -| `open(message?, clearPrevious?)` | `Function` | Open the assistant, optionally with a message | -| `close()` | `Function` | Close the assistant slideover | -| `toggle()` | `Function` | Toggle the assistant open/closed | -| `clearMessages()` | `Function` | Clear the conversation history | + diff --git a/docs/content/en/4.ai/2.mcp.md b/docs/content/en/4.ai/2.mcp.md index b8b5c4f05..40ceac222 100644 --- a/docs/content/en/4.ai/2.mcp.md +++ b/docs/content/en/4.ai/2.mcp.md @@ -1,9 +1,6 @@ ---- -title: MCP Server -description: Connect your documentation to AI tools with a native MCP server. -navigation: - icon: i-lucide-cpu ---- +# MCP Server + +> Connect your documentation to AI tools with a native MCP server. ## About MCP Servers @@ -25,9 +22,9 @@ For example, if a user asks a coding question and the LLM determines that your d Your MCP server is automatically available at the `/mcp` path of your documentation URL. -::note -For example, if your documentation is hosted at `https://docs.example.com`, your MCP server URL is `https://docs.example.com/mcp`. -:: + + +For example, if your documentation is hosted at `https://docs.example.com`, your MCP server URL is `https://docs.example.com/mcp`.\n\n ## Disable the MCP Server @@ -47,19 +44,87 @@ Docus provides two tools out of the box that allow any LLM to discover and read ### `list-pages` -Lists all documentation pages with their titles, paths, and descriptions. AI assistants should call this first to discover available content. - -| Parameter | Type | Description | -| --------- | ----------------- | ---------------------- | -| `locale` | string (optional) | Filter pages by locale | +Lists all documentation pages with their titles, paths, descriptions, and repository file paths. AI assistants should call this first to discover available content. + + + + + + + + + + + + + + + + + + + + + +
+ Parameter + + Type + + Description +
+ + locale + + + string (optional) + + Filter pages by locale +
### `get-page` -Retrieves the full markdown content of a specific documentation page. - -| Parameter | Type | Description | -| --------- | ----------------- | -------------------------------------------------------- | -| `path` | string (required) | The page path (e.g., `/en/getting-started/installation`) | +Retrieves the full markdown content of a specific documentation page, including its physical file path in the repository. + + + + + + + + + + + + + + + + + + + + + +
+ Parameter + + Type + + Description +
+ + path + + + string (required) + + The page path (e.g., + /en/getting-started/installation + + + ) +
## Setup @@ -75,7 +140,11 @@ claude mcp add --transport http my-docs https://docs.example.com/mcp ### Cursor -:install-button{ide="cursor" label="Install in Cursor" url="https://docs.example.com/mcp"} + + + + + Or manually create/update `.cursor/mcp.json` in your project root: @@ -94,7 +163,11 @@ Or manually create/update `.cursor/mcp.json` in your project root: Ensure you have GitHub Copilot and GitHub Copilot Chat extensions installed. -:install-button{ide="vscode" label="Install in VS Code" url="https://docs.example.com/mcp"} + + + + + Or manually create/update the `.vscode/mcp.json` file: @@ -268,6 +341,8 @@ export default defineMcpTool({ }) ``` -::tip{to="https://mcp-toolkit.nuxt.dev/"} + + Check the MCP Toolkit documentation for more information about tools, resources, prompts, handlers and advanced configuration. -:: + + From 121c67a5ae24caeeea8ba99de61cda99270207ad Mon Sep 17 00:00:00 2001 From: Baptiste Leproux Date: Sat, 28 Mar 2026 00:53:24 +0100 Subject: [PATCH 14/20] improve prompts --- .../runtime/server/workflows/agent-review.ts | 111 +++++++++++++++++- layer/server/mcp/prompts/review-pr-docs.ts | 109 ++++++++++++++++- 2 files changed, 211 insertions(+), 9 deletions(-) diff --git a/layer/modules/assistant/runtime/server/workflows/agent-review.ts b/layer/modules/assistant/runtime/server/workflows/agent-review.ts index f6b245517..05d5850ad 100644 --- a/layer/modules/assistant/runtime/server/workflows/agent-review.ts +++ b/layer/modules/assistant/runtime/server/workflows/agent-review.ts @@ -73,11 +73,112 @@ Always pass these exact values to \`commit-files\`: - files: array of every \`{ path, content }\` you want to write, all in one call — use the \`filePath\` value from \`list-pages\`/\`get-page\` for existing files; for new files, follow the exact same directory pattern - message: a conventional commit message describing what changed and why (e.g. \`docs: document new \\\`useHead\\\` options added in this PR\`) -## MDC writing rules - -- Use Docus components where appropriate: \`::note\`, \`::callout\`, \`::code-group\`, \`::card\` -- File paths follow the content structure: \`content/2.guide/my-feature.md\` -- Frontmatter must include at minimum: \`title\` and \`description\` +## MDC syntax rules + +**CRITICAL — violations will break the site:** + +- **Never write HTML tags.** Use MDC components instead (e.g. \`::note\` not \`
\`). +- **Never delete or modify the frontmatter block** (\`---\` ... \`---\` at the top of the file). You may only append new keys. +- **Preserve all existing MDC component syntax.** If a page already uses \`::callout\`, keep it exactly as-is unless updating its content. +- Frontmatter must include at minimum: \`title\` and \`description\`. + +### Block components (use \`::\` delimiters) + +\`\`\`mdc +::note +Informational note. +:: + +::tip +Helpful suggestion. +:: + +::warning +Important warning. +:: + +::caution +Destructive / irreversible action warning. +:: + +::callout{icon="i-lucide-info" color="blue"} +Custom callout with icon and color. +:: + +::code-group +\`\`\`bash [npm] +npm install foo +\`\`\` +\`\`\`bash [pnpm] +pnpm add foo +\`\`\` +:: + +::card{title="My card" icon="i-lucide-star" to="/some-link"} +Card body text. +:: + +:::card-group +::card{title="A" icon="i-lucide-box"} +First card. +:: +::card{title="B" icon="i-lucide-box"} +Second card. +:: +::: + +::field-group +::field{name="myOption" type="string"} +Description of the option. +:: +:: + +::steps{level="4"} +#### Step one title +Step one body. +#### Step two title +Step two body. +:: +\`\`\` + +### Inline components (use \`:\` prefix) + +\`\`\`mdc +:icon{name="i-lucide-check"} +:badge[v1.2.0] +\`\`\` + +### Nesting: add one \`:\` per nesting level + +\`\`\`mdc +::tabs +:::tabs-item{label="Preview" icon="i-lucide-eye"} +Content here. +::: +:::tabs-item{label="Code" icon="i-lucide-code"} +\`\`\`ts +const x = 1 +\`\`\` +::: +:: +\`\`\` + +### Component props: inline vs YAML + +\`\`\`mdc +// Inline (short props) +::card{title="Hello" icon="i-lucide-star"} + +// YAML block (many or long props) +::card +--- +title: Hello +icon: i-lucide-star +to: /some-page +--- +Card body. +:: +\`\`\` ## Tone and existing content diff --git a/layer/server/mcp/prompts/review-pr-docs.ts b/layer/server/mcp/prompts/review-pr-docs.ts index e6716b612..6cefc10c1 100644 --- a/layer/server/mcp/prompts/review-pr-docs.ts +++ b/layer/server/mcp/prompts/review-pr-docs.ts @@ -40,11 +40,112 @@ Always pass these exact values to \`commit-files\`: - files: array of every \`{ path, content }\` you want to write, all in one call — use the \`filePath\` value from \`list-pages\`/\`get-page\` for existing files; for new files, follow the exact same directory pattern - message: a conventional commit message describing what changed and why (e.g. \`docs: document new \\\`useHead\\\` options added in this PR\`) -## MDC writing rules +## MDC syntax rules -- Use Docus components where appropriate: \`::note\`, \`::callout\`, \`::code-group\`, \`::card\` -- File paths follow the content structure: \`content/2.guide/my-feature.md\` -- Frontmatter must include at minimum: \`title\` and \`description\` +**CRITICAL — violations will break the site:** + +- **Never write HTML tags.** Use MDC components instead (e.g. \`::note\` not \`
\`). +- **Never delete or modify the frontmatter block** (\`---\` ... \`---\` at the top of the file). You may only append new keys. +- **Preserve all existing MDC component syntax.** If a page already uses \`::callout\`, keep it exactly as-is unless updating its content. +- Frontmatter must include at minimum: \`title\` and \`description\`. + +### Block components (use \`::\` delimiters) + +\`\`\`mdc +::note +Informational note. +:: + +::tip +Helpful suggestion. +:: + +::warning +Important warning. +:: + +::caution +Destructive / irreversible action warning. +:: + +::callout{icon="i-lucide-info" color="blue"} +Custom callout with icon and color. +:: + +::code-group +\`\`\`bash [npm] +npm install foo +\`\`\` +\`\`\`bash [pnpm] +pnpm add foo +\`\`\` +:: + +::card{title="My card" icon="i-lucide-star" to="/some-link"} +Card body text. +:: + +:::card-group +::card{title="A" icon="i-lucide-box"} +First card. +:: +::card{title="B" icon="i-lucide-box"} +Second card. +:: +::: + +::field-group +::field{name="myOption" type="string"} +Description of the option. +:: +:: + +::steps{level="4"} +#### Step one title +Step one body. +#### Step two title +Step two body. +:: +\`\`\` + +### Inline components (use \`:\` prefix) + +\`\`\`mdc +:icon{name="i-lucide-check"} +:badge[v1.2.0] +\`\`\` + +### Nesting: add one \`:\` per nesting level + +\`\`\`mdc +::tabs +:::tabs-item{label="Preview" icon="i-lucide-eye"} +Content here. +::: +:::tabs-item{label="Code" icon="i-lucide-code"} +\`\`\`ts +const x = 1 +\`\`\` +::: +:: +\`\`\` + +### Component props: inline vs YAML + +\`\`\`mdc +// Inline (short props) +::card{title="Hello" icon="i-lucide-star"} + +// YAML block (many or long props) +::card +--- +title: Hello +icon: i-lucide-star +to: /some-page +--- +Card body. +:: +\`\`\` ## Tone and existing content From 19e903a063f972191d72134252d1afd7307e5971 Mon Sep 17 00:00:00 2001 From: "docus-dev[bot]" <271635299+docus-dev[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2026 23:55:13 +0000 Subject: [PATCH 15/20] docs: rename Assistant to AI Agent and document new PR review features --- docs/content/en/4.ai/1.assistant.md | 373 +++++++++++++++++++++------- 1 file changed, 280 insertions(+), 93 deletions(-) diff --git a/docs/content/en/4.ai/1.assistant.md b/docs/content/en/4.ai/1.assistant.md index 7263d4cdd..05ea698ac 100644 --- a/docs/content/en/4.ai/1.assistant.md +++ b/docs/content/en/4.ai/1.assistant.md @@ -1,87 +1,231 @@ +--- +title: AI Agent +description: Add AI-powered chat and automated PR reviews to your documentation. +--- + # AI Agent -> Add AI-powered chat and automated PR documentation reviews to your docs. +> Add AI-powered chat and automated PR reviews to your documentation. + +## About the AI Agent -## Overview +The Docus AI Agent provides two main capabilities to enhance your documentation workflow: -The Docus AI Agent brings advanced AI capabilities to your documentation, helping both your users and your developers. +1. **Chat Assistant**: Answers user questions through natural language queries directly on your documentation site. +2. **Review Agent**: Automatically reviews documentation changes in Pull Requests, providing feedback or committing improvements. -1. **AI Assistant (Chat)** - Answers user questions directly on your documentation site, citing sources and generating code examples. -2. **Agent Review** - Automatically reviews pull requests to ensure documentation stays in sync with code changes, committing or commenting with the necessary updates. +### Chat Assistant - +When users ask questions, the agent: -Both features require an [AI Gateway](https://vercel.com/docs/ai-gateway) API key to function. +- **Searches and retrieves** relevant content from your documentation using an [MCP server](/en/ai/mcp). +- **Cites sources** with navigable links to take users directly to referenced pages. +- **Generates copyable code examples** to help users implement solutions from your documentation. - +### Review Agent -## Assistant (Chat) +The review agent monitors your GitHub repository for Pull Requests. When documentation files are changed, it: -The assistant answers questions about your documentation through natural language queries. It is embedded directly in your documentation site, so users can find answers quickly and succeed with your product. +- **Analyzes the diff** to understand the changes. +- **Checks for consistency** with existing documentation. +- **Suggests improvements** or identifies potential issues. +- **Posts comments** or **directly commits** suggested changes to the PR branch. -### How It Works +::note +Both features require an [AI Gateway](https://vercel.com/docs/ai-gateway) API key. The Review Agent additionally requires a GitHub App configuration. +:: -When users ask questions, the assistant: +## How It Works -- **Searches and retrieves** relevant content from your documentation using an [MCP server](/en/ai/mcp). -- **Cites sources** with navigable links to take users directly to referenced pages. -- **Generates copyable code examples** to help users implement solutions from your documentation. +The AI Agent uses a multi-agent architecture: + +1. **Main Agent** - Receives user questions or PR diffs and decides how to process them. +2. **Search Agent** - Uses [MCP server](/en/ai/mcp) tools to find relevant context from your documentation. +3. **Task Execution** - Synthesizes information into conversational answers or generates documentation reviews. + +By default, the agent connects to your documentation's built-in MCP server at `/mcp`, giving it access to all your pages without additional configuration. + +## Quick Start (Chat) + +### 1. Get an API Key + +Get an API key from [Vercel AI Gateway](https://vercel.com/~/ai/api-keys). AI Gateway works with multiple AI providers (OpenAI, Anthropic, Google, and more) through a unified API. + +### 2. Set Environment Variable + +Add your API key to your environment: + +```bash [.env] +AI_GATEWAY_API_KEY=your-api-key +``` + +### 3. Deploy + +That's it! The chat assistant is automatically enabled when an API key is detected. + +## Review Agent Setup + +The Review Agent requires a GitHub App to interact with your repository. + +### 1. Create a GitHub App + +Create a new GitHub App in your organization or account settings: + +1. **Webhook URL**: Set this to `https://your-docs-site.com/__docus__/webhook/github`. +2. **Webhook Secret**: Generate a random string and save it. +3. **Permissions**: + - `Pull requests`: Read & write + - `Content`: Read & write (required for `commit` mode) + - `Metadata`: Read-only +4. **Events**: Subscribe to `Pull request` events. + +### 2. Configure Environment Variables + +Add the following variables to your environment: + +```bash [.env] +# AI Provider +AI_GATEWAY_API_KEY=your-api-key + +# GitHub App +GITHUB_APP_ID=your-app-id +GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----" +GITHUB_WEBHOOK_SECRET=your-webhook-secret +``` + +### 3. Enable in Configuration + +Enable the review agent in your `nuxt.config.ts`: -### Quick Start - -1. **Get an API Key** - Get an API key from [Vercel AI Gateway](https://vercel.com/~/ai/api-keys). -2. **Set Environment Variable** - Add your API key to your environment: - ```bash [.env] - AI_GATEWAY_API_KEY=your-api-key - ``` -3. **Deploy** - The assistant is automatically enabled when an API key is detected. - -### Using the Assistant - -- **Floating Input**: A floating input appears at the bottom of documentation pages. Press I to focus. -- **Explain with AI**: A button in the sidebar that asks the assistant to explain the current page. -- **Slideover Chat**: A panel on the right side of the screen that displays the conversation history. - -## Agent Review - -The review agent monitors pull requests on your GitHub repository. When a PR is opened or updated, the agent analyzes the diff and uses the [built-in MCP tools](/en/ai/mcp#built-in-tools) to find relevant documentation pages. It then suggests or commits documentation updates directly to the PR branch. - -### Quick Start - -1. **Create a GitHub App** - Go to your GitHub organization settings and create a new GitHub App with: - - **Repository permissions**: `Content (Read & Write)`, `Pull Requests (Read & Write)` - - **Events**: `Pull request` -2. **Configure Environment Variables** - Set the following variables in your production environment: - ```bash [.env] - GITHUB_APP_ID=your-app-id - GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----" - GITHUB_WEBHOOK_SECRET=your-webhook-secret - ``` -3. **Enable Agent Review** - Update your `nuxt.config.ts`: - ```ts [nuxt.config.ts] - export default defineNuxtConfig({ - docus: { - agent: { - review: { - enabled: true - } - } - } - }) - ``` -4. **Register Webhook** - Deploy your site and set the GitHub App's Webhook URL to `https://your-site.com/__docus__/webhook/github`. - -### Configuration Options - -| Option | Type | Default | Description | -| --- | --- | --- | --- | -| `enabled` | `boolean` | `false` | Enable the webhook handler and review workflow. | -| `mode` | `'comment' \| 'commit'` | `'comment'` | Whether the agent should post a PR comment or commit changes directly. | -| `githubRepo` | `string` | `auto` | Target repository in `owner/repo` format. Auto-detected on Vercel, Netlify, and CI. | +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + review: { + enabled: true + } + } + } +}) +``` + +## Using the Chat Assistant + +Users can interact with the assistant in multiple ways: + +### Floating Input + +On documentation pages, a floating input appears at the bottom of the screen. Users can type their questions directly and press Enter to get answers. + +::tip +Use the keyboard shortcut :icon{name="i-lucide-command"} + :icon{name="i-lucide-keyboard"} **I** to focus the floating input. +:: + +### Explain with AI + +Each documentation page includes an **Explain with AI** button in the table of contents sidebar. Clicking this button opens the assistant with the current page as context, asking it to explain the content. + +### Slideover Chat + +When a conversation starts, a slideover panel opens on the right side of the screen. This panel displays the conversation history and allows users to continue asking questions. + +## Chat Configuration + +Configure the UI of the chat assistant through `app.config.ts`: + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + // Show the floating input on documentation pages + floatingInput: true, + + // Show the "Explain with AI\" button in the sidebar + explainWithAi: true, + + // FAQ questions to display when chat is empty + faqQuestions: [], + + // Keyboard shortcuts + shortcuts: { + focusInput: 'meta_i' + }, + + // Custom icons + icons: { + trigger: 'i-lucide-sparkles', + explain: 'i-lucide-brain' + } + } +}) +``` + +### FAQ Questions + +Display suggested questions when the chat is empty. This helps users discover what they can ask. + +#### Simple Format + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + faqQuestions: [ + 'How do I install Docus?', + 'How do I customize the theme?', + 'How do I add components to my pages?' + ] + } +}) +``` + +#### Category Format + +Organize questions into categories: + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + faqQuestions: [ + { + category: 'Getting Started', + items: [ + 'How do I install Docus?', + 'What is the project structure?' + ] + }, + { + category: 'Customization', + items: [ + 'How do I change the theme colors?', + 'How do I add a custom logo?' + ] + } + ] + } +}) +``` + +#### Localized Format + +For multi-language documentation, provide FAQ questions per locale: + +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + faqQuestions: { + en: [ + { category: 'Getting Started', items: ['How do I install?'] } + ], + fr: [ + { category: 'Démarrage', items: ['Comment installer ?'] } + ] + } + } +}) +``` ## Advanced Configuration -Configure the AI agent in your `nuxt.config.ts`: +Configure the agent core through `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ @@ -94,38 +238,50 @@ export default defineNuxtConfig({ mcpServer: '/mcp', chat: { - // API endpoint path for the chat assistant + // Enable/disable the chat assistant + enabled: true, + // API endpoint path apiPath: '/__docus__/assistant' }, review: { - // Enable automated PR documentation reviews - enabled: true, - // How the agent provides feedback - mode: 'comment' + // Enable/disable the review agent + enabled: false, + // Mode: 'comment' (post PR comment) or 'commit' (direct commit) + mode: 'comment', + // Target repository (org/repo), auto-detected in CI (Vercel, Netlify, GH Actions) + githubRepo: 'nuxt-content/docus' } } } }) ``` -### AI Model +### MCP Server Configuration -The agent uses `google/gemini-3-flash` by default. You can change this to any model supported by the AI SDK Gateway: +The agent uses an MCP server to access your documentation. You have two options: + +#### Use the Built-in MCP Server (Default) + +By default, the agent uses Docus's built-in MCP server at `/mcp`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ docus: { agent: { - model: 'anthropic/claude-3-5-sonnet' + mcpServer: '/mcp' } } }) ``` -### MCP Server +::warning +Make sure the MCP server is enabled in your configuration. If you've customized the MCP path, update `mcpServer` accordingly. +:: + +#### Use an External MCP Server -The agent uses an MCP server to access your documentation. By default, it uses Docus's built-in MCP server at `/mcp`. You can connect to an external server: +Connect to any external MCP server by providing a full URL: ```ts [nuxt.config.ts] export default defineNuxtConfig({ @@ -137,29 +293,60 @@ export default defineNuxtConfig({ }) ``` +### Custom AI Model + +The agent uses `google/gemini-3-flash` by default. You can change this to any model supported by the AI SDK Gateway: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + model: 'anthropic/claude-opus-4.5' + } + } +}) +``` + ## Programmatic Access -Use the `useAssistant` composable to control the assistant programmatically: +Use the `useAssistant` composable to control the chat assistant programmatically: ```vue -``` -`isEnabled` returns `true` if the chat assistant is enabled (API key present and not explicitly disabled). +function askQuestion() { + // Open the assistant with a pre-filled question + open('How do I configure the theme?', true) +} + -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - // FAQ questions, shortcuts, and icons are still configured in app.config.ts - faqQuestions: ['How do I install Docus?'] - } -}) + ``` - - -Learn more about the MCP server that powers the AI Agent. - - +### Composable API + +::field-group + ::field{name="isEnabled" type="ComputedRef"} + Whether the chat assistant is enabled (API key present). + :: + ::field{name="isOpen" type="Ref"} + Whether the slideover is open. + :: + ::field{name="open(message?, clearPrevious?)" type="Function"} + Open the assistant, optionally with a message. + :: + ::field{name="close()" type="Function"} + Close the assistant slideover. + :: + ::field{name="toggle()" type="Function"} + Toggle the assistant open/closed. + :: + ::field{name="clearMessages()" type="Function"} + Clear the conversation history. + :: +:: From 4d61f538f222344a8beefdb5b6441032ce5fdbd0 Mon Sep 17 00:00:00 2001 From: Baptiste Leproux Date: Sat, 28 Mar 2026 01:27:48 +0100 Subject: [PATCH 16/20] Revert "docs: rename Assistant to AI Agent and document new PR review features" This reverts commit 19e903a063f972191d72134252d1afd7307e5971. --- docs/content/en/4.ai/1.assistant.md | 373 +++++++--------------------- 1 file changed, 93 insertions(+), 280 deletions(-) diff --git a/docs/content/en/4.ai/1.assistant.md b/docs/content/en/4.ai/1.assistant.md index 05ea698ac..7263d4cdd 100644 --- a/docs/content/en/4.ai/1.assistant.md +++ b/docs/content/en/4.ai/1.assistant.md @@ -1,231 +1,87 @@ ---- -title: AI Agent -description: Add AI-powered chat and automated PR reviews to your documentation. ---- - # AI Agent -> Add AI-powered chat and automated PR reviews to your documentation. - -## About the AI Agent - -The Docus AI Agent provides two main capabilities to enhance your documentation workflow: - -1. **Chat Assistant**: Answers user questions through natural language queries directly on your documentation site. -2. **Review Agent**: Automatically reviews documentation changes in Pull Requests, providing feedback or committing improvements. - -### Chat Assistant - -When users ask questions, the agent: - -- **Searches and retrieves** relevant content from your documentation using an [MCP server](/en/ai/mcp). -- **Cites sources** with navigable links to take users directly to referenced pages. -- **Generates copyable code examples** to help users implement solutions from your documentation. - -### Review Agent - -The review agent monitors your GitHub repository for Pull Requests. When documentation files are changed, it: - -- **Analyzes the diff** to understand the changes. -- **Checks for consistency** with existing documentation. -- **Suggests improvements** or identifies potential issues. -- **Posts comments** or **directly commits** suggested changes to the PR branch. - -::note -Both features require an [AI Gateway](https://vercel.com/docs/ai-gateway) API key. The Review Agent additionally requires a GitHub App configuration. -:: - -## How It Works - -The AI Agent uses a multi-agent architecture: - -1. **Main Agent** - Receives user questions or PR diffs and decides how to process them. -2. **Search Agent** - Uses [MCP server](/en/ai/mcp) tools to find relevant context from your documentation. -3. **Task Execution** - Synthesizes information into conversational answers or generates documentation reviews. - -By default, the agent connects to your documentation's built-in MCP server at `/mcp`, giving it access to all your pages without additional configuration. - -## Quick Start (Chat) - -### 1. Get an API Key - -Get an API key from [Vercel AI Gateway](https://vercel.com/~/ai/api-keys). AI Gateway works with multiple AI providers (OpenAI, Anthropic, Google, and more) through a unified API. - -### 2. Set Environment Variable - -Add your API key to your environment: - -```bash [.env] -AI_GATEWAY_API_KEY=your-api-key -``` - -### 3. Deploy - -That's it! The chat assistant is automatically enabled when an API key is detected. - -## Review Agent Setup - -The Review Agent requires a GitHub App to interact with your repository. - -### 1. Create a GitHub App - -Create a new GitHub App in your organization or account settings: +> Add AI-powered chat and automated PR documentation reviews to your docs. -1. **Webhook URL**: Set this to `https://your-docs-site.com/__docus__/webhook/github`. -2. **Webhook Secret**: Generate a random string and save it. -3. **Permissions**: - - `Pull requests`: Read & write - - `Content`: Read & write (required for `commit` mode) - - `Metadata`: Read-only -4. **Events**: Subscribe to `Pull request` events. +## Overview -### 2. Configure Environment Variables +The Docus AI Agent brings advanced AI capabilities to your documentation, helping both your users and your developers. -Add the following variables to your environment: +1. **AI Assistant (Chat)** - Answers user questions directly on your documentation site, citing sources and generating code examples. +2. **Agent Review** - Automatically reviews pull requests to ensure documentation stays in sync with code changes, committing or commenting with the necessary updates. -```bash [.env] -# AI Provider -AI_GATEWAY_API_KEY=your-api-key + -# GitHub App -GITHUB_APP_ID=your-app-id -GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----" -GITHUB_WEBHOOK_SECRET=your-webhook-secret -``` - -### 3. Enable in Configuration - -Enable the review agent in your `nuxt.config.ts`: - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - docus: { - agent: { - review: { - enabled: true - } - } - } -}) -``` - -## Using the Chat Assistant - -Users can interact with the assistant in multiple ways: +Both features require an [AI Gateway](https://vercel.com/docs/ai-gateway) API key to function. -### Floating Input + -On documentation pages, a floating input appears at the bottom of the screen. Users can type their questions directly and press Enter to get answers. +## Assistant (Chat) -::tip -Use the keyboard shortcut :icon{name="i-lucide-command"} + :icon{name="i-lucide-keyboard"} **I** to focus the floating input. -:: +The assistant answers questions about your documentation through natural language queries. It is embedded directly in your documentation site, so users can find answers quickly and succeed with your product. -### Explain with AI +### How It Works -Each documentation page includes an **Explain with AI** button in the table of contents sidebar. Clicking this button opens the assistant with the current page as context, asking it to explain the content. +When users ask questions, the assistant: -### Slideover Chat - -When a conversation starts, a slideover panel opens on the right side of the screen. This panel displays the conversation history and allows users to continue asking questions. - -## Chat Configuration - -Configure the UI of the chat assistant through `app.config.ts`: - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - // Show the floating input on documentation pages - floatingInput: true, - - // Show the "Explain with AI\" button in the sidebar - explainWithAi: true, - - // FAQ questions to display when chat is empty - faqQuestions: [], - - // Keyboard shortcuts - shortcuts: { - focusInput: 'meta_i' - }, - - // Custom icons - icons: { - trigger: 'i-lucide-sparkles', - explain: 'i-lucide-brain' - } - } -}) -``` - -### FAQ Questions - -Display suggested questions when the chat is empty. This helps users discover what they can ask. - -#### Simple Format - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - faqQuestions: [ - 'How do I install Docus?', - 'How do I customize the theme?', - 'How do I add components to my pages?' - ] - } -}) -``` - -#### Category Format - -Organize questions into categories: - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - faqQuestions: [ - { - category: 'Getting Started', - items: [ - 'How do I install Docus?', - 'What is the project structure?' - ] - }, - { - category: 'Customization', - items: [ - 'How do I change the theme colors?', - 'How do I add a custom logo?' - ] - } - ] - } -}) -``` - -#### Localized Format +- **Searches and retrieves** relevant content from your documentation using an [MCP server](/en/ai/mcp). +- **Cites sources** with navigable links to take users directly to referenced pages. +- **Generates copyable code examples** to help users implement solutions from your documentation. -For multi-language documentation, provide FAQ questions per locale: - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - faqQuestions: { - en: [ - { category: 'Getting Started', items: ['How do I install?'] } - ], - fr: [ - { category: 'Démarrage', items: ['Comment installer ?'] } - ] - } - } -}) -``` +### Quick Start + +1. **Get an API Key** - Get an API key from [Vercel AI Gateway](https://vercel.com/~/ai/api-keys). +2. **Set Environment Variable** - Add your API key to your environment: + ```bash [.env] + AI_GATEWAY_API_KEY=your-api-key + ``` +3. **Deploy** - The assistant is automatically enabled when an API key is detected. + +### Using the Assistant + +- **Floating Input**: A floating input appears at the bottom of documentation pages. Press I to focus. +- **Explain with AI**: A button in the sidebar that asks the assistant to explain the current page. +- **Slideover Chat**: A panel on the right side of the screen that displays the conversation history. + +## Agent Review + +The review agent monitors pull requests on your GitHub repository. When a PR is opened or updated, the agent analyzes the diff and uses the [built-in MCP tools](/en/ai/mcp#built-in-tools) to find relevant documentation pages. It then suggests or commits documentation updates directly to the PR branch. + +### Quick Start + +1. **Create a GitHub App** - Go to your GitHub organization settings and create a new GitHub App with: + - **Repository permissions**: `Content (Read & Write)`, `Pull Requests (Read & Write)` + - **Events**: `Pull request` +2. **Configure Environment Variables** - Set the following variables in your production environment: + ```bash [.env] + GITHUB_APP_ID=your-app-id + GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----" + GITHUB_WEBHOOK_SECRET=your-webhook-secret + ``` +3. **Enable Agent Review** - Update your `nuxt.config.ts`: + ```ts [nuxt.config.ts] + export default defineNuxtConfig({ + docus: { + agent: { + review: { + enabled: true + } + } + } + }) + ``` +4. **Register Webhook** - Deploy your site and set the GitHub App's Webhook URL to `https://your-site.com/__docus__/webhook/github`. + +### Configuration Options + +| Option | Type | Default | Description | +| --- | --- | --- | --- | +| `enabled` | `boolean` | `false` | Enable the webhook handler and review workflow. | +| `mode` | `'comment' \| 'commit'` | `'comment'` | Whether the agent should post a PR comment or commit changes directly. | +| `githubRepo` | `string` | `auto` | Target repository in `owner/repo` format. Auto-detected on Vercel, Netlify, and CI. | ## Advanced Configuration -Configure the agent core through `nuxt.config.ts`: +Configure the AI agent in your `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ @@ -238,50 +94,38 @@ export default defineNuxtConfig({ mcpServer: '/mcp', chat: { - // Enable/disable the chat assistant - enabled: true, - // API endpoint path + // API endpoint path for the chat assistant apiPath: '/__docus__/assistant' }, review: { - // Enable/disable the review agent - enabled: false, - // Mode: 'comment' (post PR comment) or 'commit' (direct commit) - mode: 'comment', - // Target repository (org/repo), auto-detected in CI (Vercel, Netlify, GH Actions) - githubRepo: 'nuxt-content/docus' + // Enable automated PR documentation reviews + enabled: true, + // How the agent provides feedback + mode: 'comment' } } } }) ``` -### MCP Server Configuration +### AI Model -The agent uses an MCP server to access your documentation. You have two options: - -#### Use the Built-in MCP Server (Default) - -By default, the agent uses Docus's built-in MCP server at `/mcp`: +The agent uses `google/gemini-3-flash` by default. You can change this to any model supported by the AI SDK Gateway: ```ts [nuxt.config.ts] export default defineNuxtConfig({ docus: { agent: { - mcpServer: '/mcp' + model: 'anthropic/claude-3-5-sonnet' } } }) ``` -::warning -Make sure the MCP server is enabled in your configuration. If you've customized the MCP path, update `mcpServer` accordingly. -:: - -#### Use an External MCP Server +### MCP Server -Connect to any external MCP server by providing a full URL: +The agent uses an MCP server to access your documentation. By default, it uses Docus's built-in MCP server at `/mcp`. You can connect to an external server: ```ts [nuxt.config.ts] export default defineNuxtConfig({ @@ -293,60 +137,29 @@ export default defineNuxtConfig({ }) ``` -### Custom AI Model - -The agent uses `google/gemini-3-flash` by default. You can change this to any model supported by the AI SDK Gateway: - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - docus: { - agent: { - model: 'anthropic/claude-opus-4.5' - } - } -}) -``` - ## Programmatic Access -Use the `useAssistant` composable to control the chat assistant programmatically: +Use the `useAssistant` composable to control the assistant programmatically: ```vue +``` + +`isEnabled` returns `true` if the chat assistant is enabled (API key present and not explicitly disabled). - +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + // FAQ questions, shortcuts, and icons are still configured in app.config.ts + faqQuestions: ['How do I install Docus?'] + } +}) ``` -### Composable API - -::field-group - ::field{name="isEnabled" type="ComputedRef"} - Whether the chat assistant is enabled (API key present). - :: - ::field{name="isOpen" type="Ref"} - Whether the slideover is open. - :: - ::field{name="open(message?, clearPrevious?)" type="Function"} - Open the assistant, optionally with a message. - :: - ::field{name="close()" type="Function"} - Close the assistant slideover. - :: - ::field{name="toggle()" type="Function"} - Toggle the assistant open/closed. - :: - ::field{name="clearMessages()" type="Function"} - Clear the conversation history. - :: -:: + + +Learn more about the MCP server that powers the AI Agent. + + From 41e431e1e84b3cae48f051562df1e73de35d51fc Mon Sep 17 00:00:00 2001 From: Baptiste Leproux Date: Sat, 28 Mar 2026 01:29:26 +0100 Subject: [PATCH 17/20] fix types --- layer/server/mcp/tools/get-page.ts | 2 +- layer/server/mcp/tools/list-pages.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/layer/server/mcp/tools/get-page.ts b/layer/server/mcp/tools/get-page.ts index 43d1e9768..aabe17b98 100644 --- a/layer/server/mcp/tools/get-page.ts +++ b/layer/server/mcp/tools/get-page.ts @@ -30,7 +30,7 @@ WORKFLOW: This tool returns the complete page content including title, descripti : 'content' const siteUrl = getRequestURL(event).origin || inferSiteURL() - const availableLocales = getAvailableLocales(config) + const availableLocales = getAvailableLocales(config as unknown as Parameters[0]) const collectionName = config.i18n?.locales ? getCollectionFromPath(path, availableLocales) : 'docs' diff --git a/layer/server/mcp/tools/list-pages.ts b/layer/server/mcp/tools/list-pages.ts index 87e5aff06..a770155a9 100644 --- a/layer/server/mcp/tools/list-pages.ts +++ b/layer/server/mcp/tools/list-pages.ts @@ -27,7 +27,7 @@ OUTPUT: Returns a structured list with: locale: z.string().optional().describe('The locale to filter pages by'), }, cache: '1h', - handler: async ({ locale }) => { + handler: async ({ locale }: { locale?: string }) => { const event = useEvent() const config = useRuntimeConfig(event).public const appConfig = useAppConfig() as { github?: { rootDir?: string } } @@ -36,7 +36,7 @@ OUTPUT: Returns a structured list with: : 'content' const siteUrl = getRequestURL(event).origin || inferSiteURL() - const availableLocales = getAvailableLocales(config) + const availableLocales = getAvailableLocales(config as unknown as Parameters[0]) const collections = getCollectionsToQuery(locale, availableLocales) try { From ded5ffa6a8958bb57893ee2f0918668d77781eff Mon Sep 17 00:00:00 2001 From: "docus-dev[bot]" <271635299+docus-dev[bot]@users.noreply.github.com> Date: Sat, 28 Mar 2026 02:27:45 +0000 Subject: [PATCH 18/20] docs: document new Docus Agent and Review Agent features --- docs/content/en/4.ai/1.assistant.md | 299 ++++++++++++++++++---------- docs/content/en/4.ai/2.mcp.md | 144 +++++--------- 2 files changed, 247 insertions(+), 196 deletions(-) diff --git a/docs/content/en/4.ai/1.assistant.md b/docs/content/en/4.ai/1.assistant.md index 7263d4cdd..467392bf7 100644 --- a/docs/content/en/4.ai/1.assistant.md +++ b/docs/content/en/4.ai/1.assistant.md @@ -1,25 +1,26 @@ -# AI Agent +--- +title: Agent +description: Docus Agent provides AI-powered features for your documentation, including a chat assistant and automated PR documentation reviews. +--- -> Add AI-powered chat and automated PR documentation reviews to your docs. +# Agent -## Overview +> Docus Agent provides AI-powered features for your documentation, including a chat assistant and automated PR documentation reviews. -The Docus AI Agent brings advanced AI capabilities to your documentation, helping both your users and your developers. +## About Docus Agent -1. **AI Assistant (Chat)** - Answers user questions directly on your documentation site, citing sources and generating code examples. -2. **Agent Review** - Automatically reviews pull requests to ensure documentation stays in sync with code changes, committing or commenting with the necessary updates. +The agent enhances your documentation workflow and user experience through two main features: - +- **Chat Assistant**: Answers user questions directly on your documentation site using natural language and source citations. +- **Review Agent**: Automatically reviews Pull Requests and suggests or commits documentation updates to keep your docs in sync with code changes. -Both features require an [AI Gateway](https://vercel.com/docs/ai-gateway) API key to function. +::note +The agent requires an [AI Gateway](https://vercel.com/docs/ai-gateway) API key to function. +:: - +## Chat Assistant -## Assistant (Chat) - -The assistant answers questions about your documentation through natural language queries. It is embedded directly in your documentation site, so users can find answers quickly and succeed with your product. - -### How It Works +The chat assistant answers questions about your documentation through natural language queries. It is embedded directly in your documentation site, so users can find answers quickly. When users ask questions, the assistant: @@ -27,139 +28,227 @@ When users ask questions, the assistant: - **Cites sources** with navigable links to take users directly to referenced pages. - **Generates copyable code examples** to help users implement solutions from your documentation. -### Quick Start +### Using the Assistant -1. **Get an API Key** - Get an API key from [Vercel AI Gateway](https://vercel.com/~/ai/api-keys). -2. **Set Environment Variable** - Add your API key to your environment: - ```bash [.env] - AI_GATEWAY_API_KEY=your-api-key - ``` -3. **Deploy** - The assistant is automatically enabled when an API key is detected. +Users can interact with the assistant in multiple ways: -### Using the Assistant +#### Floating Input + +On documentation pages, a floating input appears at the bottom of the screen. Users can type their questions directly and press Enter to get answers. + +::tip +Use the keyboard shortcut to focus the floating input. +:: + +#### Explain with AI + +Each documentation page includes an **Explain with AI** button in the table of contents sidebar. Clicking this button opens the assistant with the current page as context, asking it to explain the content. + +#### Slideover Chat + +When a conversation starts, a slideover panel opens on the right side of the screen. This panel displays the conversation history and allows users to continue asking questions. + +## Review Agent + +The Review Agent is a specialized documentation agent that keeps your docs in sync with your code. Triggered by GitHub webhooks, it analyzes Pull Request diffs and identifies necessary documentation updates. + +- **Automated Reviews**: Automatically scans every PR for changes that impact documentation. +- **Content Discovery**: Uses the built-in MCP server to find existing pages related to the changed code. +- **Direct Commits**: Can commit updated documentation directly to your PR branch or post suggestions as comments. + +### Setup for Review Agent + +To enable the Review Agent, you need to provide GitHub App credentials: + +::steps{level="4"} + +#### Create a GitHub App +Create a new GitHub App with **Read & Write** permissions for `Pull Requests` and `Repository Contents`. + +#### Generate Credentials +Generate a private key and a webhook secret for your app. -- **Floating Input**: A floating input appears at the bottom of documentation pages. Press I to focus. -- **Explain with AI**: A button in the sidebar that asks the assistant to explain the current page. -- **Slideover Chat**: A panel on the right side of the screen that displays the conversation history. - -## Agent Review - -The review agent monitors pull requests on your GitHub repository. When a PR is opened or updated, the agent analyzes the diff and uses the [built-in MCP tools](/en/ai/mcp#built-in-tools) to find relevant documentation pages. It then suggests or commits documentation updates directly to the PR branch. - -### Quick Start - -1. **Create a GitHub App** - Go to your GitHub organization settings and create a new GitHub App with: - - **Repository permissions**: `Content (Read & Write)`, `Pull Requests (Read & Write)` - - **Events**: `Pull request` -2. **Configure Environment Variables** - Set the following variables in your production environment: - ```bash [.env] - GITHUB_APP_ID=your-app-id - GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----" - GITHUB_WEBHOOK_SECRET=your-webhook-secret - ``` -3. **Enable Agent Review** - Update your `nuxt.config.ts`: - ```ts [nuxt.config.ts] - export default defineNuxtConfig({ - docus: { - agent: { - review: { - enabled: true - } - } - } - }) - ``` -4. **Register Webhook** - Deploy your site and set the GitHub App's Webhook URL to `https://your-site.com/__docus__/webhook/github`. - -### Configuration Options - -| Option | Type | Default | Description | -| --- | --- | --- | --- | -| `enabled` | `boolean` | `false` | Enable the webhook handler and review workflow. | -| `mode` | `'comment' \| 'commit'` | `'comment'` | Whether the agent should post a PR comment or commit changes directly. | -| `githubRepo` | `string` | `auto` | Target repository in `owner/repo` format. Auto-detected on Vercel, Netlify, and CI. | - -## Advanced Configuration - -Configure the AI agent in your `nuxt.config.ts`: +#### Set Environment Variables +Add the following to your environment: + +```bash [.env] +GITHUB_APP_ID=your-app-id +GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n..." +GITHUB_WEBHOOK_SECRET=your-webhook-secret +``` + +#### Enable in Configuration +Enable the review feature in your `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ docus: { agent: { - // AI model (uses AI SDK Gateway format) - model: 'google/gemini-3-flash', - - // MCP server (path or URL) - mcpServer: '/mcp', - - chat: { - // API endpoint path for the chat assistant - apiPath: '/__docus__/assistant' - }, - review: { - // Enable automated PR documentation reviews enabled: true, - // How the agent provides feedback - mode: 'comment' + mode: 'commit' // or 'comment' } } } }) ``` +:: + +## Quick Start -### AI Model +### 1. Set Environment Variables -The agent uses `google/gemini-3-flash` by default. You can change this to any model supported by the AI SDK Gateway: +Add your API key and GitHub credentials to your environment: + +```bash [.env] +AI_GATEWAY_API_KEY=your-api-key + +# For Review Agent +GITHUB_APP_ID=... +GITHUB_APP_PRIVATE_KEY=... +GITHUB_WEBHOOK_SECRET=... +``` + +### 2. Configure Nuxt + +The agent features are automatically detected based on environment variables, but you can explicitly configure them: ```ts [nuxt.config.ts] export default defineNuxtConfig({ docus: { agent: { - model: 'anthropic/claude-3-5-sonnet' + chat: { + enabled: true + }, + review: { + enabled: true + } } } }) ``` -### MCP Server +## Configuration + +### Nuxt Configuration + +Configure advanced options in `nuxt.config.ts`: + +::field-group + ::field{name="model" type="string"} + AI model to use (uses AI SDK Gateway format). Default: `google/gemini-3-flash` + :: + ::field{name="mcpServer" type="string"} + MCP server path or URL. Default: `/mcp` + :: + ::field{name="chat.enabled" type="boolean"} + Enable the chat assistant. Default: `true` (if API key present) + :: + ::field{name="chat.apiPath" type="string"} + API endpoint for the chat assistant. Default: `/__docus__/assistant` + :: + ::field{name="review.enabled" type="boolean"} + Enable the PR documentation review agent. Default: `false` + :: + ::field{name="review.mode" type="string"} + Mode for the review agent: `commit` (direct commits) or `comment` (PR comments). Default: `comment` + :: + ::field{name="review.githubRepo" type="string"} + Target GitHub repository (e.g., `owner/repo`). Auto-detected in CI. + :: +:: + +### App Configuration + +The Chat Assistant UI can be customized through `app.config.ts`: -The agent uses an MCP server to access your documentation. By default, it uses Docus's built-in MCP server at `/mcp`. You can connect to an external server: +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + // Show the floating input on documentation pages + floatingInput: true, -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - docus: { - agent: { - mcpServer: 'https://other-docs.example.com/mcp' + // Show the "Explain with AI" button in the sidebar + explainWithAi: true, + + // FAQ questions to display when chat is empty + faqQuestions: [], + + // Keyboard shortcuts + shortcuts: { + focusInput: 'meta_i' + }, + + // Custom icons + icons: { + trigger: 'i-lucide-sparkles', + explain: 'i-lucide-brain' } } }) ``` -## Programmatic Access +#### FAQ Questions -Use the `useAssistant` composable to control the assistant programmatically: +Display suggested questions when the chat is empty. -```vue - +::code-group +```ts [Simple] +export default defineAppConfig({ + assistant: { + faqQuestions: [ + 'How do I install Docus?', + 'How do I customize the theme?' + ] + } +}) ``` - -`isEnabled` returns `true` if the chat assistant is enabled (API key present and not explicitly disabled). - -```ts [app.config.ts] +```ts [Categorized] export default defineAppConfig({ assistant: { - // FAQ questions, shortcuts, and icons are still configured in app.config.ts - faqQuestions: ['How do I install Docus?'] + faqQuestions: [ + { + category: 'Getting Started', + items: ['How do I install Docus?', 'What is the project structure?'] + } + ] } }) ``` +:: - +## Programmatic Access -Learn more about the MCP server that powers the AI Agent. +Use the `useAssistant` composable to control the assistant programmatically: + +```vue + +``` - +::field-group + ::field{name="isEnabled" type="ComputedRef"} + Whether the agent's chat feature is enabled. + :: + ::field{name="isOpen" type="Ref"} + Whether the slideover is open. + :: + ::field{name="open" type="Function"} + Open the assistant: `open(message?: string, clearPrevious?: boolean)` + :: + ::field{name="close" type="Function"} + Close the assistant slideover. + :: + ::field{name="toggle" type="Function"} + Toggle the assistant open/closed. + :: + ::field{name="clearMessages" type="Function"} + Clear the conversation history. + :: +:: diff --git a/docs/content/en/4.ai/2.mcp.md b/docs/content/en/4.ai/2.mcp.md index 40ceac222..e2a49d772 100644 --- a/docs/content/en/4.ai/2.mcp.md +++ b/docs/content/en/4.ai/2.mcp.md @@ -1,3 +1,8 @@ +--- +title: MCP Server +description: Connect your documentation to AI tools with a native MCP server. +--- + # MCP Server > Connect your documentation to AI tools with a native MCP server. @@ -22,109 +27,70 @@ For example, if a user asks a coding question and the LLM determines that your d Your MCP server is automatically available at the `/mcp` path of your documentation URL. - - -For example, if your documentation is hosted at `https://docs.example.com`, your MCP server URL is `https://docs.example.com/mcp`.\n\n +::note +For example, if your documentation is hosted at `https://docs.example.com`, your MCP server URL is `https://docs.example.com/mcp`. +:: -## Disable the MCP Server +## Configuration -If you want to disable the MCP server, you can do so in your `nuxt.config.ts`: +The MCP server is managed by the Docus Agent. You can configure it in your `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ - mcp: { - enabled: false, - }, + docus: { + agent: { + // Path or full URL to the MCP server + mcpServer: '/mcp' + } + } }) ``` ## Built-in Tools -Docus provides two tools out of the box that allow any LLM to discover and read your documentation: +Docus provides tools out of the box that allow any LLM to discover, read, and update your documentation. These tools are used internally by the [Docus Agent](/en/ai/assistant). ### `list-pages` -Lists all documentation pages with their titles, paths, descriptions, and repository file paths. AI assistants should call this first to discover available content. - - - - - - - - - - - - - - - - - - - - - -
- Parameter - - Type - - Description -
- - locale - - - string (optional) - - Filter pages by locale -
+Lists all documentation pages with their titles, paths, descriptions, and repository file paths. + +::field-group + ::field{name="locale" type="string"} + Filter pages by locale (optional). + :: +:: ### `get-page` -Retrieves the full markdown content of a specific documentation page, including its physical file path in the repository. - - - - - - - - - - - - - - - - - - - - - -
- Parameter - - Type - - Description -
- - path - - - string (required) - - The page path (e.g., - /en/getting-started/installation - - - ) -
+Retrieves the full markdown content and repository file path of a specific documentation page. + +::field-group + ::field{name="path" type="string"} + The page path (e.g., `/en/getting-started/installation`). + :: +:: + +### `commit-files` + +Commits one or more documentation files to a GitHub branch in a single atomic commit. + +::field-group + ::field{name="owner" type="string"} + GitHub repository owner. + :: + ::field{name="repo" type="string"} + GitHub repository name. + :: + ::field{name="branch" type="string"} + Target branch for the documentation update. + :: + ::field{name="files" type="array"} + Array of `{ path, content }` objects to create or update. + :: + ::field{name="message" type="string"} + Commit message (e.g. `docs: update assistant configuration`). + :: +:: ## Setup @@ -142,8 +108,6 @@ claude mcp add --transport http my-docs https://docs.example.com/mcp - - Or manually create/update `.cursor/mcp.json` in your project root: @@ -165,8 +129,6 @@ Ensure you have GitHub Copilot and GitHub Copilot Chat extensions installed. - - Or manually create/update the `.vscode/mcp.json` file: From f7f45c000bcd8ac9d1ba2c8842243676d1e0d5c4 Mon Sep 17 00:00:00 2001 From: Baptiste Leproux Date: Sat, 28 Mar 2026 03:31:13 +0100 Subject: [PATCH 19/20] Apply suggestion from @larbish --- content/en/ai/assistant.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/en/ai/assistant.md b/content/en/ai/assistant.md index a2eee8797..e99baf68c 100644 --- a/content/en/ai/assistant.md +++ b/content/en/ai/assistant.md @@ -2,7 +2,7 @@ > Add AI-powered chat to your docs that answers questions, cites sources, and generates code examples. -## About the Assistant +## About the Agent The assistant answers questions about your documentation through natural language queries. It is embedded directly in your documentation site, so users can find answers quickly and succeed with your product. From 77425490ed0a601da816dbf58c88634285721ef2 Mon Sep 17 00:00:00 2001 From: "docus-dev[bot]" <271635299+docus-dev[bot]@users.noreply.github.com> Date: Sat, 28 Mar 2026 02:32:46 +0000 Subject: [PATCH 20/20] docs: rebrand Assistant to AI Agent and document Review Agent feature --- docs/content/en/4.ai/2.mcp.md | 159 ++++++++---- docs/content/fr/4.ai/1.assistant.md | 385 +++++++--------------------- docs/content/fr/4.ai/2.mcp.md | 142 ++++++++-- 3 files changed, 328 insertions(+), 358 deletions(-) diff --git a/docs/content/en/4.ai/2.mcp.md b/docs/content/en/4.ai/2.mcp.md index e2a49d772..412e4f36d 100644 --- a/docs/content/en/4.ai/2.mcp.md +++ b/docs/content/en/4.ai/2.mcp.md @@ -1,8 +1,3 @@ ---- -title: MCP Server -description: Connect your documentation to AI tools with a native MCP server. ---- - # MCP Server > Connect your documentation to AI tools with a native MCP server. @@ -27,69 +22,135 @@ For example, if a user asks a coding question and the LLM determines that your d Your MCP server is automatically available at the `/mcp` path of your documentation URL. -::note + + For example, if your documentation is hosted at `https://docs.example.com`, your MCP server URL is `https://docs.example.com/mcp`. -:: -## Configuration + -The MCP server is managed by the Docus Agent. You can configure it in your `nuxt.config.ts`: +## Disable the MCP Server + +If you want to disable the MCP server, you can do so in your `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ - docus: { - agent: { - // Path or full URL to the MCP server - mcpServer: '/mcp' - } - } + mcp: { + enabled: false, + }, }) ``` ## Built-in Tools -Docus provides tools out of the box that allow any LLM to discover, read, and update your documentation. These tools are used internally by the [Docus Agent](/en/ai/assistant). +Docus provides tools out of the box that allow any LLM to discover and read your documentation: ### `list-pages` -Lists all documentation pages with their titles, paths, descriptions, and repository file paths. - -::field-group - ::field{name="locale" type="string"} - Filter pages by locale (optional). - :: -:: +Lists all documentation pages with their titles, paths, descriptions, and repository file paths. AI assistants should call this first to discover available content. + + + + + + + + + + + + + + + + + + + + + +
+ Parameter + + Type + + Description +
+ + locale + + + string (optional) + + Filter pages by locale +
### `get-page` -Retrieves the full markdown content and repository file path of a specific documentation page. - -::field-group - ::field{name="path" type="string"} - The page path (e.g., `/en/getting-started/installation`). - :: -:: +Retrieves the full markdown content of a specific documentation page, including its physical file path in the repository. + + + + + + + + + + + + + + + + + + + + + +
+ Parameter + + Type + + Description +
+ + path + + + string (required) + + The page path (e.g., + /en/getting-started/installation + + + ) +
### `commit-files` -Commits one or more documentation files to a GitHub branch in a single atomic commit. +Commits one or more documentation files to a GitHub branch in a single atomic commit using the Git Trees API. ::field-group - ::field{name="owner" type="string"} - GitHub repository owner. - :: - ::field{name="repo" type="string"} - GitHub repository name. - :: - ::field{name="branch" type="string"} - Target branch for the documentation update. - :: - ::field{name="files" type="array"} - Array of `{ path, content }` objects to create or update. - :: - ::field{name="message" type="string"} - Commit message (e.g. `docs: update assistant configuration`). - :: +::field{name="owner" type="string" required} +GitHub repository owner. +:: +::field{name="repo" type="string" required} +GitHub repository name. +:: +::field{name="branch" type="string" required} +Branch to commit to (must be the PR head branch). +:: +::field{name="token" type="string" required} +GitHub installation access token. +:: +::field{name="files" type="array" required} +Array of `{ path, content }` objects to commit. +:: +::field{name="message" type="string" required} +Commit message (conventional commits format recommended). +:: :: ## Setup @@ -108,6 +169,8 @@ claude mcp add --transport http my-docs https://docs.example.com/mcp + + Or manually create/update `.cursor/mcp.json` in your project root: @@ -129,6 +192,8 @@ Ensure you have GitHub Copilot and GitHub Copilot Chat extensions installed. + + Or manually create/update the `.vscode/mcp.json` file: @@ -147,7 +212,7 @@ Or manually create/update the `.vscode/mcp.json` file: ### Windsurf 1. Open Windsurf and navigate to **Settings** > **Windsurf Settings** > **Cascade** -2. Click the **Manage MCPs** button, then select the **View raw config** option +2. Click the **Manage MCPs**, then select the **View raw config** option 3. Add the following configuration: ```json [.codeium/windsurf/mcp_config.json] diff --git a/docs/content/fr/4.ai/1.assistant.md b/docs/content/fr/4.ai/1.assistant.md index 0d4f03649..35ba5f341 100644 --- a/docs/content/fr/4.ai/1.assistant.md +++ b/docs/content/fr/4.ai/1.assistant.md @@ -1,334 +1,146 @@ ---- -title: Assistant -description: Ajoutez un chat propulsé par l'IA à votre documentation qui répond aux questions, cite les sources et génère des exemples de code. -navigation: - icon: i-lucide-sparkles ---- +# AI Agent -## À propos de l'Assistant +> Ajoutez un chat propulsé par l'IA et des revues automatiques de documentation pour vos pull requests. -L'assistant répond aux questions sur votre documentation via des requêtes en langage naturel. Il est intégré directement dans votre site de documentation, permettant aux utilisateurs de trouver rapidement des réponses. +## Aperçu -Lorsque les utilisateurs posent des questions, l'assistant : +L'Agent IA de Docus apporte des capacités d'intelligence artificielle avancées à votre documentation, aidant à la fois vos utilisateurs et vos développeurs. -- **Recherche et récupère** le contenu pertinent de votre documentation en utilisant un [serveur MCP](/fr/ai/mcp). -- **Cite les sources** avec des liens navigables vers les pages référencées. -- **Génère des exemples de code** copiables pour aider les utilisateurs à implémenter les solutions. +1. **Assistant IA (Chat)** - Répond aux questions des utilisateurs directement sur votre site de documentation, cite les sources et génère des exemples de code. +2. **Agent Review** - Surveille automatiquement les pull requests pour s'assurer que la documentation reste synchronisée avec les changements de code, en suggérant ou en effectuant les mises à jour nécessaires. ::note -L'assistant nécessite une clé API [AI Gateway](https://vercel.com/docs/ai-gateway) pour fonctionner. +Ces deux fonctionnalités nécessitent une clé API [AI Gateway](https://vercel.com/docs/ai-gateway) pour fonctionner. :: -## Comment ça fonctionne - -L'assistant utilise une architecture multi-agents : +## Assistant (Chat) -1. **Agent principal** - Reçoit les questions des utilisateurs et décide quand rechercher dans la documentation -2. **Agent de recherche** - Utilise les outils du [serveur MCP](/fr/ai/mcp) pour trouver le contenu pertinent -3. **Génération de réponse** - Synthétise les informations en réponses utiles et conversationnelles +L'assistant répond aux questions sur votre documentation via des requêtes en langage naturel. Il est intégré directement dans votre site, permettant aux utilisateurs de trouver rapidement des réponses. -Par défaut, l'assistant se connecte au serveur MCP intégré de votre documentation à `/mcp`, lui donnant accès à toutes vos pages sans configuration supplémentaire. Vous pouvez également vous connecter à un serveur MCP externe si nécessaire. +### Comment ça fonctionne -## Démarrage rapide - -### 1. Obtenir une clé API - -Obtenez une clé API depuis [Vercel AI Gateway](https://vercel.com/~/ai/api-keys). AI Gateway fonctionne avec plusieurs fournisseurs d'IA (OpenAI, Anthropic, Google, et plus) via une API unifiée. +Lorsque les utilisateurs posent des questions, l'assistant : -### 2. Configurer la variable d'environnement +- **Recherche et récupère** le contenu pertinent de votre documentation en utilisant un [serveur MCP](/fr/ai/mcp). +- **Cite les sources** avec des liens navigables vers les pages référencées. +- **Génère des exemples de code** copiables pour aider les utilisateurs à implémenter les solutions. -Ajoutez votre clé API à votre environnement : +### Démarrage rapide +1. **Obtenir une clé API** - Obtenez une clé API depuis [Vercel AI Gateway](https://vercel.com/~/ai/api-keys). +2. **Configurer la variable d'environnement** - Ajoutez votre clé API à votre environnement : ```bash [.env] AI_GATEWAY_API_KEY=votre-cle-api ``` +3. **Déployer** - L'assistant est automatiquement activé lorsqu'une clé API est détectée. -### 3. Déployer - -C'est tout ! L'assistant est automatiquement activé lorsqu'une clé API est détectée. Déployez votre documentation et l'assistant sera disponible pour vos utilisateurs. - -## Utiliser l'Assistant - -Les utilisateurs peuvent interagir avec l'assistant de plusieurs façons : - -### Input flottant - -Sur les pages de documentation, un champ de saisie flottant apparaît en bas de l'écran. Les utilisateurs peuvent taper leurs questions directement et appuyer sur Entrée pour obtenir des réponses. - -::tip -Utilisez le raccourci clavier :kbd{value="meta"} :kbd{value="I"} pour activer l'input flottant. -:: - -### Expliquer avec l'IA +### Utiliser l'Assistant -Chaque page de documentation inclut un bouton **Explain with AI** dans la barre latérale de la table des matières. Cliquer sur ce bouton ouvre l'assistant avec la page actuelle comme contexte. +- **Input flottant** : Un champ de saisie apparaît en bas des pages. Appuyez sur I pour lui donner le focus. +- **Expliquer avec l'IA** : Un bouton dans la barre latérale qui demande à l'assistant d'expliquer la page actuelle. +- **Slideover Chat** : Un panneau latéral qui affiche l'historique de la conversation. -### Chat en panneau latéral +## Agent Review -Lorsqu'une conversation commence, un panneau coulissant s'ouvre sur le côté droit de l'écran. Ce panneau affiche l'historique de la conversation et permet aux utilisateurs de continuer à poser des questions. +L'agent de revue surveille les pull requests sur votre dépôt GitHub. Lorsqu'une PR est ouverte ou mise à jour, l'agent analyse les changements et utilise les [outils MCP intégrés](/fr/ai/mcp#outils-int%C3%A9gr%C3%A9s) pour trouver les pages de documentation concernées. Il suggère ou valide ensuite les mises à jour directement sur la branche de la PR. -## Configuration +### Démarrage rapide -Configurez l'assistant via `app.config.ts` : - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - // Afficher l'input flottant sur les pages de documentation - floatingInput: true, - - // Afficher le bouton "Expliquer avec l'IA" dans la barre latérale - explainWithAi: true, - - // Questions FAQ à afficher quand le chat est vide - faqQuestions: [], - - // Raccourcis clavier - shortcuts: { - focusInput: 'meta_i' - }, - - // Icônes personnalisées - icons: { - trigger: 'i-lucide-sparkles', - explain: 'i-lucide-brain' - } - } -}) -``` - -### Questions FAQ - -Affichez des questions suggérées quand le chat est vide. Cela aide les utilisateurs à découvrir ce qu'ils peuvent demander. - -#### Format simple - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - faqQuestions: [ - 'Comment installer Docus ?', - 'Comment personnaliser le thème ?', - 'Comment ajouter des composants à mes pages ?' - ] - } -}) +1. **Créer une GitHub App** - Dans les paramètres de votre organisation GitHub, créez une nouvelle application avec : + - **Repository permissions** : `Content (Read & Write)`, `Pull Requests (Read & Write)` + - **Events** : `Pull request` +2. **Configurer les variables d'environnement** - Définissez les variables suivantes dans votre environnement de production : +```bash [.env] +GITHUB_APP_ID=votre-app-id +GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----" +GITHUB_WEBHOOK_SECRET=votre-secret-webhook ``` - -#### Format avec catégories - -Organisez les questions en catégories : - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - faqQuestions: [ - { - category: 'Démarrage', - items: [ - 'Comment installer Docus ?', - 'Quelle est la structure du projet ?' - ] - }, - { - category: 'Personnalisation', - items: [ - 'Comment changer les couleurs du thème ?', - 'Comment ajouter un logo personnalisé ?' - ] +3. **Activer l'Agent Review** - Mettez à jour votre `nuxt.config.ts` : +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + docus: { + agent: { + review: { + enabled: true } - ] - } -}) -``` - -#### Format multilingue - -Pour une documentation multilingue, fournissez les questions FAQ par locale : - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - faqQuestions: { - en: [ - { category: 'Getting Started', items: ['How do I install?'] } - ], - fr: [ - { category: 'Démarrage', items: ['Comment installer ?'] } - ] } } }) ``` +4. **Enregistrer le Webhook** - Déployez votre site et définissez l'URL de webhook de l'application GitHub sur `https://votre-site.com/__docus__/webhook/github`. -## Raccourcis clavier - -Configurez le raccourci clavier pour activer l'input flottant : +### Options de configuration -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - shortcuts: { - // Par défaut : 'meta_i' (Cmd+I sur Mac, Ctrl+I sur Windows) - focusInput: 'meta_k' // Changer pour Cmd/Ctrl+K - } - } -}) -``` - -Le format de raccourci utilise des underscores pour séparer les touches. Exemples courants : -- `meta_i` - Cmd+I (Mac) / Ctrl+I (Windows) -- `meta_k` - Cmd+K (Mac) / Ctrl+K (Windows) -- `ctrl_shift_p` - Ctrl+Shift+P - -## Icônes personnalisées - -Personnalisez les icônes utilisées par l'assistant : - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - icons: { - // Icône pour le bouton déclencheur et l'en-tête du panneau - trigger: 'i-lucide-bot', - - // Icône pour le bouton "Expliquer avec l'IA" - explain: 'i-lucide-lightbulb' - } - } -}) -``` - -Les icônes utilisent le format [Iconify](https://iconify.design/) (ex: `i-lucide-sparkles`, `i-heroicons-sparkles`). - -## Internationalisation - -Tous les textes de l'interface sont automatiquement traduits selon la locale de l'utilisateur. Docus inclut des traductions intégrées pour l'anglais et le français. - -Les textes suivants sont traduits : -- Titre et placeholder du panneau -- Textes des infobulles -- Libellés des boutons ("Effacer le chat", "Fermer", "Expliquer avec l'IA") -- Messages de statut ("Réflexion...", "Le chat est effacé au rechargement") - -::tip -Si aucun `title` ou `placeholder` personnalisé n'est défini dans `app.config.ts`, les valeurs traduites des fichiers i18n seront utilisées automatiquement. +::field-group +::field{name="enabled" type="boolean" default="false"} +Active le gestionnaire de webhook et le workflow de revue. +:: +::field{name="mode" type="'comment' | 'commit'" default="'comment'"} +Définit si l'agent doit poster un commentaire sur la PR ou committer directement les changements. +:: +::field{name="githubRepo" type="string" default="auto"} +Dépôt cible au format `owner/repo`. Auto-détecté sur Vercel, Netlify et CI. +:: :: - -## Désactiver des fonctionnalités - -### Désactiver l'input flottant - -Masquez l'input flottant en bas des pages de documentation : - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - floatingInput: false - } -}) -``` - -### Désactiver "Explain with AI" - -Masquez le bouton "Explain with AI" dans la barre latérale de documentation : - -```ts [app.config.ts] -export default defineAppConfig({ - assistant: { - explainWithAi: false - } -}) -``` - -### Désactiver l'assistant entièrement - -L'assistant est automatiquement désactivé quand aucune clé API n'est configurée. Pour le désactiver explicitement, supprimez simplement la variable d'environnement : - -```bash [.env] -# Commentez ou supprimez la clé API -# AI_GATEWAY_API_KEY=votre-cle-api -``` ## Configuration avancée -Configurez les options avancées dans `nuxt.config.ts` : +Configurez l'agent IA dans votre `nuxt.config.ts` : ```ts [nuxt.config.ts] export default defineNuxtConfig({ - assistant: { - // Modèle IA (utilise le format AI SDK Gateway) - model: 'google/gemini-3-flash', + docus: { + agent: { + // Modèle IA (utilise le format AI SDK Gateway) + model: 'google/gemini-3-flash', - // Serveur MCP (chemin ou URL) - mcpServer: '/mcp', + // Serveur MCP (chemin ou URL) + mcpServer: '/mcp', - // Chemin de l'endpoint API - apiPath: '/__docus__/assistant' - } -}) -``` - -### Configuration du serveur MCP - -L'assistant utilise un serveur MCP pour accéder à votre documentation. Vous avez deux options : - -#### Utiliser le serveur MCP intégré (par défaut) - -Par défaut, l'assistant utilise le serveur MCP intégré de Docus à `/mcp` : - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - assistant: { - mcpServer: '/mcp' - } -}) -``` - -::warning -Assurez-vous que le serveur MCP est activé dans votre configuration. Si vous avez personnalisé le chemin MCP, mettez à jour `mcpServer` en conséquence. -:: - -#### Utiliser un serveur MCP externe - -Connectez-vous à n'importe quel serveur MCP externe en fournissant une URL complète : + chat: { + // Chemin de l'endpoint API pour l'assistant + apiPath: '/__docus__/assistant' + }, -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - assistant: { - mcpServer: 'https://autre-docs.exemple.com/mcp' + review: { + // Activer les revues automatiques de documentation + enabled: true, + // Mode de retour de l'agent + mode: 'comment' + } + } } }) ``` -C'est utile lorsque vous voulez que l'assistant réponde aux questions d'une autre source de documentation, ou lors de la connexion à une base de connaissances centralisée. +### Modèle IA -### Modèle IA personnalisé - -L'assistant utilise `google/gemini-3-flash` par défaut. Vous pouvez le changer pour n'importe quel modèle supporté par AI SDK Gateway : +L'agent utilise `google/gemini-3-flash` par défaut. Vous pouvez le changer pour n'importe quel modèle supporté par AI SDK Gateway : ```ts [nuxt.config.ts] export default defineNuxtConfig({ - assistant: { - model: 'anthropic/claude-opus-4.5' + docus: { + agent: { + model: 'anthropic/claude-3-5-sonnet' + } } }) ``` -### Nom du site dans les réponses +### Serveur MCP -L'assistant utilise automatiquement le nom de votre site dans ses réponses. Configurez le nom du site dans `nuxt.config.ts` : +L'agent utilise un serveur MCP pour accéder à votre documentation. Par défaut, il utilise le serveur MCP intégré de Docus à `/mcp`. ```ts [nuxt.config.ts] export default defineNuxtConfig({ - site: { - name: 'Ma Documentation' + docus: { + agent: { + mcpServer: 'https://autre-docs.exemple.com/mcp' + } } }) ``` -Cela permet à l'assistant de répondre en tant qu'"assistant de Ma Documentation" et de parler avec autorité sur votre produit spécifique. - ## Accès programmatique Utilisez le composable `useAssistant` pour contrôler l'assistant programmatiquement : @@ -336,27 +148,20 @@ Utilisez le composable `useAssistant` pour contrôler l'assistant programmatique ```vue - - ``` -### API du composable +`isEnabled` retourne `true` si l'assistant de chat est activé (clé API présente). -| Propriété | Type | Description | -|-----------|------|-------------| -| `isEnabled` | `ComputedRef` | Si l'assistant est activé (clé API présente) | -| `isOpen` | `Ref` | Si le panneau est ouvert | -| `open(message?, clearPrevious?)` | `Function` | Ouvrir l'assistant, optionnellement avec un message | -| `close()` | `Function` | Fermer le panneau de l'assistant | -| `toggle()` | `Function` | Basculer l'assistant ouvert/fermé | -| `clearMessages()` | `Function` | Effacer l'historique de conversation | +```ts [app.config.ts] +export default defineAppConfig({ + assistant: { + // Les FAQ, raccourcis et icônes sont toujours configurés dans app.config.ts + faqQuestions: ['Comment installer Docus ?'] + } +}) +``` + +::tip{to="/fr/ai/mcp"} +En savoir plus sur le serveur MCP qui alimente l'Agent IA. +:: diff --git a/docs/content/fr/4.ai/2.mcp.md b/docs/content/fr/4.ai/2.mcp.md index 14995bfc0..e02f981ab 100644 --- a/docs/content/fr/4.ai/2.mcp.md +++ b/docs/content/fr/4.ai/2.mcp.md @@ -1,9 +1,6 @@ ---- -title: Serveur MCP -description: Connectez votre documentation aux outils IA avec un serveur MCP natif. -navigation: - icon: i-lucide-cpu ---- +# Serveur MCP + +> Connectez votre documentation aux outils IA avec un serveur MCP natif. ## À Propos des Serveurs MCP @@ -25,9 +22,11 @@ Par exemple, si un utilisateur pose une question de code et que le LLM détermin Votre serveur MCP est automatiquement disponible au chemin `/mcp` de l'URL de votre documentation. -::note + + Par exemple, si votre documentation est hébergée à `https://docs.example.com`, l'URL de votre serveur MCP est `https://docs.example.com/mcp`. -:: + + ## Désactiver le Serveur MCP @@ -43,23 +42,116 @@ export default defineNuxtConfig({ ## Outils Intégrés -Docus fournit deux outils par défaut qui permettent à n'importe quel LLM de découvrir et lire votre documentation : +Docus fournit des outils par défaut qui permettent à n'importe quel LLM de découvrir et lire votre documentation : ### `list-pages` -Liste toutes les pages de documentation avec leurs titres, chemins et descriptions. Les assistants IA doivent appeler cet outil en premier pour découvrir le contenu disponible. - -| Paramètre | Type | Description | -| --------- | ------------------ | ---------------------------- | -| `locale` | string (optionnel) | Filtrer les pages par locale | +Liste toutes les pages de documentation avec leurs titres, chemins, descriptions et chemins de fichiers dans le dépôt. Les assistants IA doivent appeler cet outil en premier pour découvrir le contenu disponible. + + + + + + + + + + + + + + + + + + + + + +
+ Paramètre + + Type + + Description +
+ + locale + + + string (optionnel) + + Filtrer les pages par locale +
### `get-page` -Récupère le contenu markdown complet d'une page de documentation spécifique. - -| Paramètre | Type | Description | -| --------- | --------------- | ------------------------------------------------------------- | -| `path` | string (requis) | Le chemin de la page (ex: `/fr/getting-started/installation`) | +Récupère le contenu markdown complet d'une page de documentation spécifique, incluant son chemin de fichier physique dans le dépôt. + + + + + + + + + + + + + + + + + + + + + +
+ Paramètre + + Type + + Description +
+ + path + + + string (requis) + + Le chemin de la page (ex: + /fr/getting-started/installation + + + ) +
+ +### `commit-files` + +Commit un ou plusieurs fichiers de documentation sur une branche GitHub en un seul commit atomique via l'API Git Trees. + +::field-group +::field{name="owner" type="string" required} +Propriétaire du dépôt GitHub. +:: +::field{name="repo" type="string" required} +Nom du dépôt GitHub. +:: +::field{name="branch" type="string" required} +Branche sur laquelle committer (doit être la branche de la PR). +:: +::field{name="token" type="string" required} +Jeton d'accès d'installation GitHub. +:: +::field{name="files" type="array" required} +Tableau d'objets `{ path, content }` à committer. +:: +::field{name="message" type="string" required} +Message de commit (format conventional commits recommandé). +:: +:: ## Configuration @@ -75,7 +167,11 @@ claude mcp add --transport http my-docs https://docs.example.com/mcp ### Cursor -:install-button{ide="cursor" label="Installer dans Cursor" url="https://docs.example.com/mcp"} + + + + + Ou créez/modifiez manuellement `.cursor/mcp.json` à la racine de votre projet : @@ -94,7 +190,11 @@ Ou créez/modifiez manuellement `.cursor/mcp.json` à la racine de votre projet Assurez-vous d'avoir les extensions GitHub Copilot et GitHub Copilot Chat installées. -:install-button{ide="vscode" label="Installer dans VS Code" url="https://docs.example.com/mcp"} + + + + + Ou créez/modifiez manuellement le fichier `.vscode/mcp.json` :