一键导入
adding-features
Use when adding or extending agent tools, LLM providers, API routes, or focus modes in YAAWC, or when asked how to add new functionality.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when adding or extending agent tools, LLM providers, API routes, or focus modes in YAAWC, or when asked how to add new functionality.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
YAAWC design system rules and tokens. MUST be used whenever editing or creating UI — any .tsx component, Tailwind class change, styling work, color/spacing/radius/shadow/typography decision, or globals.css edit. Activates on terms like "component", "style", "UI", "button", "card", "modal", "color", "theme", "dark mode", "Tailwind", "CSS", "layout", "padding", "spacing", "rounded", "shadow", "hover", "focus", "MessageBox", "Sidebar", "Navbar", "ChatWindow", or any work under src/components or src/app.
Use when modifying React UI components, ChatWindow, MessageBox, MarkdownRenderer, frontend state/streaming event handling, styling, or debugging UI rendering.
Build, run, and drive YAAWC (the Next.js AI search app in this repo). Use when asked to start YAAWC, launch the dev server, build it, smoke-test it, screenshot or inspect the home/dashboard UI, or interact with the running app in a browser.
Use when working on tool-call lifecycle events, todo updates, subagent streaming, SimplifiedAgent/ChatWindow event handling, MarkdownRenderer ToolCall, TodoWidget, or debugging missing or broken streaming output.
Use when adding, modifying, or debugging API endpoints, request/response schemas, payload formats, or the chat and search route handlers.
Use when working on image upload/display, the uploads API, MessageInput image/clipboard paste, multimodal chat messages, or vision model integration.
| name | adding-features |
| description | Use when adding or extending agent tools, LLM providers, API routes, or focus modes in YAAWC, or when asked how to add new functionality. |
This skill covers the established patterns for extending YAAWC's core subsystems. Follow these patterns to maintain consistency with existing code.
Tools live in src/lib/tools/agents/. Each tool follows a consistent pattern.
src/lib/tools/agents/myTool.ts):import { tool } from '@langchain/core/tools';
import { z } from 'zod';
import { getCurrentTaskInput } from '@langchain/langgraph';
import { Command } from '@langchain/langgraph';
import { Document } from '@langchain/core/documents';
import { ToolMessage } from '@langchain/core/messages';
import { SimplifiedAgentStateType } from '@/lib/state/chatAgentState';
import { isSoftStop } from '@/lib/utils/runControl';
const myToolSchema = z.object({
query: z.string().describe('The search query'),
});
export const myTool = tool(
async (input, config) => {
// Access infrastructure from config
const systemLlm = config.configurable?.systemLlm;
const embeddings = config.configurable?.embeddings;
const emitter = config.configurable?.emitter;
const messageId = config.configurable?.messageId as string | undefined;
// Access current agent state
const currentState = getCurrentTaskInput() as SimplifiedAgentStateType;
// Check soft-stop before doing expensive work
if (messageId && isSoftStop(messageId)) {
return new Command({
update: {
messages: [
new ToolMessage({
content: 'Aborted',
tool_call_id: (config as unknown as { toolCall: { id: string } })
?.toolCall.id,
}),
],
},
});
}
// Do the work...
const documents: Document[] = [];
const summary = 'Results...';
// Return Command to merge documents into state
return new Command({
update: {
relevantDocuments: documents,
messages: [
new ToolMessage({
content: summary,
tool_call_id: (config as unknown as { toolCall: { id: string } })
?.toolCall.id,
}),
],
},
});
},
{
name: 'my_tool',
description: 'Description shown to the LLM agent for tool selection',
schema: myToolSchema,
},
);
src/lib/tools/agents/index.ts):import { myTool } from './myTool';
// Add to the appropriate static arrays:
export const allAgentTools = [...existing, myTool];
export const webSearchTools = [...existing, myTool]; // if web-search relevant
Tool arrays and their usage:
allAgentTools — all tools (used by webSearch mode when no files attached)webSearchTools — web search mode (excludes fileSearchTool)fileSearchTools — [fileSearchTool] only; appended to other sets when files are presentcoreTools — chat mode; includes imageGenerationTool, chatHistorySearchTool, getChatMessagesTool, readSkillTool (NOT empty)Interactive tools (codeExecutionTool, askUserTool, editSkillTool) are NOT in the static arrays. They are appended at runtime by withInteractiveTools(). Use the dynamic getters (getAllAgentTools(), getWebSearchTools(), getCoreTools(), getLocalResearchTools()) when building tool lists at runtime.
Add icon/label handling in src/components/MarkdownRenderer.tsx:
The ToolCall component uses two switch/if blocks — getIcon() (returns an icon JSX for the tool type string) and formatToolMessage() (returns the full label row). Add cases for your tool's type string in both. There is no iconMap object; the icon is selected via a switch statement in getIcon().
SimplifiedAgentStateType from @/lib/state/chatAgentState (type alias, not SimplifiedAgentState.State)config.configurable.systemLlm for any internal LLM calls (NOT the chat LLM)Command with update: { relevantDocuments, messages } — documents merge into agent state via append reducerToolMessage in the returned messages; get the id via (config as unknown as { toolCall: { id: string } })?.toolCall.id (there is no config.toolCallId)messageId && isSoftStop(messageId) before starting expensive operationsconfig.configurable.retrievalSignal?.aborted for hard cancellationToolCall component in MarkdownRenderer.tsx — tool attributes (query, url, etc.) are extracted by handleToolStart in simplifiedAgent.tsIf your tool needs to emit additional events (like todoListTool emits todo_update):
const emitter = config.configurable?.emitter;
emitter?.emit('data', JSON.stringify({
type: 'my_custom_event',
data: { ... },
}));
Providers live in src/lib/providers/. Each follows a consistent fetch-and-register pattern.
src/lib/providers/myProvider.ts):import { getMyProviderApiKey } from '../config';
export const PROVIDER_INFO = {
key: 'myprovider',
displayName: 'My Provider',
};
export const loadMyProviderChatModels = async () => {
const apiKey = getMyProviderApiKey();
if (!apiKey) return {};
try {
// Fetch available models from provider API
const response = await fetch('https://api.myprovider.com/v1/models', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const data = await response.json();
return data.models.reduce((acc, model) => {
acc[model.id] = {
displayName: model.name,
model: new ChatMyProvider({ apiKey, model: model.id }),
};
return acc;
}, {});
} catch (err) {
return {}; // Graceful degradation — provider unavailable
}
};
Add config getter in src/lib/config.ts:
Config interface under MODELSexport const getMyProviderApiKey = () => loadConfig().MODELS.MYPROVIDER?.API_KEY;Register in provider index (src/lib/providers/index.ts):
PROVIDER_INFO and add to PROVIDER_METADATA object (it's a keyed object, not an array)chatModelProviders mapembeddingModelProviders if the provider supports embeddingsAdd TOML config section in config.toml:
[MODELS.MYPROVIDER]
API_KEY = ""
{} on any error (graceful degradation)ChatOpenAI, ChatAnthropic, etc.)PROVIDER_METADATA in src/lib/providers/index.ts is a plain object keyed by provider key, not an arrayAPI routes use the Next.js App Router pattern.
src/app/api/myEndpoint/route.ts):import { NextResponse } from 'next/server';
export const POST = async (request: Request) => {
try {
const body = await request.json();
// Validate input...
// Process...
return NextResponse.json({ result: '...' });
} catch (err: any) {
return NextResponse.json({ message: 'An error occurred' }, { status: 500 });
}
};
For streaming responses, see the chat route pattern (src/app/api/chat/route.ts):
TransformStream with a writer for server-pushContent-Type: text/event-streamwriter.write(encoder.encode(JSON.stringify({ type, data }) + '\n'))POST, GET, DELETE)NextResponse.json() for JSON responsesTransformStream with JSON lines (not SSE format)Focus modes control which tools and prompts the agent uses.
Register the mode in src/lib/focusModes.ts:
focusModes array with key, title, and descriptionCreate prompt (src/lib/prompts/simplifiedAgent/myMode.ts):
src/lib/prompts/templates.ts for citation formattingAdd tool selection in src/lib/search/simplifiedAgent.ts:
getToolsForFocusMode() method, add a case for your focus mode returning the appropriate tool set from getAllAgentTools(), getWebSearchTools(), getCoreTools(), or getLocalResearchTools()switch (focusMode) that sets the system prompt), add a case returning your new promptAdd UI button in src/components/MessageInputActions/Focus.tsx:
focusModes array imported from src/lib/focusModes.ts — adding an entry there is sufficient; no manual button code requiredUpdate API validation in src/app/api/chat/route.ts if you need strict validation of the focus mode string (currently the route passes the string through without an allow-list check)