feat: bundle desktop skills
This commit is contained in:
@@ -33,6 +33,57 @@ function generateBrowserResponse(userContent: string): string {
|
||||
return `收到你的消息:「${userContent.length > 50 ? userContent.slice(0, 50) + '...' : userContent}」\n\n当前为浏览器预览模式,无法调用真实 AI 接口。此模式用于 UI 开发和体验测试。\n\n如需 AI 回复,请使用 \`cargo tauri dev\` 启动完整应用。`;
|
||||
}
|
||||
|
||||
const BUILT_IN_SKILLS = [
|
||||
{
|
||||
name: 'jianying-editor',
|
||||
description: '剪映 AI 自动化剪辑 Skill,支持草稿生成、素材导入、字幕、配音、特效和导出。',
|
||||
repo: 'luoluoluo22/jianying-editor-skill',
|
||||
},
|
||||
{
|
||||
name: 'seedance',
|
||||
description: '中文 Seedance 2.0 / 即梦视频与图片提示词工程,支持 @素材引用、分镜、运镜和长视频流水线。',
|
||||
repo: 'MapleShaw/seedance2.0-prompt-skill',
|
||||
},
|
||||
{
|
||||
name: 'seedance-prompt-en',
|
||||
description: 'English Seedance 2.0 multimodal AI video prompt writing guide.',
|
||||
repo: 'dexhunter/seedance2-skill',
|
||||
},
|
||||
{
|
||||
name: 'dwg-mcp-server',
|
||||
description: 'DWG MCP Server 包装技能:指导 Agent 安装并使用 dwg-mcp 检查 AutoCAD DWG 文件。',
|
||||
repo: 'dimitrovakulenko/dwg-mcp-server',
|
||||
},
|
||||
{
|
||||
name: 'cad-ai-agent',
|
||||
description: 'CAD/DXF Agent 包装技能:用于图纸摘要、合规检查、工程量统计、RFI 和安全编辑流程。',
|
||||
repo: 'jeremylongshore/cad-ai-agent',
|
||||
},
|
||||
{
|
||||
name: 'gorden-ppt-skill',
|
||||
description: '中文 PPT 生成与编辑 Skill,支持模板套用、版式保持、出框检测和 PowerPoint 成品导出。',
|
||||
repo: 'GordenSun/GordenPPTSkill',
|
||||
},
|
||||
];
|
||||
|
||||
function builtInSkillRows() {
|
||||
return BUILT_IN_SKILLS.map(skill => ({
|
||||
name: skill.name,
|
||||
description: skill.description,
|
||||
source: 'boosapi',
|
||||
sourcePath: `/mock/skills/${skill.name}/SKILL.md`,
|
||||
enabled: true,
|
||||
hasUpdate: false,
|
||||
userInvocable: true,
|
||||
}));
|
||||
}
|
||||
|
||||
function mergeBuiltInSkills(skills: any[]) {
|
||||
const existingNames = new Set(skills.map((skill: any) => skill.name));
|
||||
const missing = builtInSkillRows().filter(skill => !existingNames.has(skill.name));
|
||||
return [...missing, ...skills];
|
||||
}
|
||||
|
||||
// ── Built-in Providers ──────────────────────────────────────────────────
|
||||
|
||||
const OPENAI_REASONING_OVERRIDES = {
|
||||
@@ -1584,7 +1635,7 @@ export async function handleCommand<T>(cmd: string, args?: Record<string, unknow
|
||||
|
||||
// ── Skills ────────────────────────────────────────────────────────
|
||||
case 'list_skills':
|
||||
return getStore<any[]>('skills', []) as T;
|
||||
return mergeBuiltInSkills(getStore<any[]>('skills', [])) as T;
|
||||
|
||||
case 'get_skill':
|
||||
return {
|
||||
@@ -1613,13 +1664,16 @@ export async function handleCommand<T>(cmd: string, args?: Record<string, unknow
|
||||
|
||||
case 'install_skill': {
|
||||
const source = (args as any)?.source || 'installed-skill';
|
||||
const name = source.includes('jianying-editor-skill') ? 'jianying-editor' : String(source).split('/').pop();
|
||||
const known = BUILT_IN_SKILLS.find(skill => (
|
||||
skill.repo.toLowerCase() === String(source).replace(/^https:\/\/github\.com\//, '').replace(/\.git$/, '').toLowerCase()
|
||||
));
|
||||
const name = known?.name || (source.includes('jianying-editor-skill') ? 'jianying-editor' : String(source).split('/').pop());
|
||||
const skills = getStore<any[]>('skills', []);
|
||||
const nextSkill = {
|
||||
name,
|
||||
description: name === 'jianying-editor'
|
||||
description: known?.description || (name === 'jianying-editor'
|
||||
? '剪映 AI 自动化剪辑 Skill,支持草稿生成、素材导入、字幕、配音、特效和导出。'
|
||||
: 'Installed skill',
|
||||
: 'Installed skill'),
|
||||
source: 'boosapi',
|
||||
sourcePath: `/mock/skills/${name}`,
|
||||
enabled: true,
|
||||
@@ -1646,7 +1700,14 @@ export async function handleCommand<T>(cmd: string, args?: Record<string, unknow
|
||||
return undefined as T;
|
||||
|
||||
case 'search_marketplace':
|
||||
return [] as T;
|
||||
return BUILT_IN_SKILLS.map(skill => ({
|
||||
name: skill.name,
|
||||
description: skill.description,
|
||||
repo: skill.repo,
|
||||
stars: 0,
|
||||
installs: 0,
|
||||
installed: mergeBuiltInSkills(getStore<any[]>('skills', [])).some((row: any) => row.name === skill.name),
|
||||
})) as T;
|
||||
|
||||
case 'check_skill_updates':
|
||||
return [] as T;
|
||||
|
||||
@@ -28,7 +28,56 @@ const openExternalUrl = (url: string) => {
|
||||
|
||||
const { Text, Paragraph } = Typography;
|
||||
|
||||
const BUILTIN_MARKETPLACE_DETAILS: Record<string, string> = {
|
||||
'dimitrovakulenko/dwg-mcp-server': `# DWG MCP Server Skill
|
||||
|
||||
Use this skill when a user wants to inspect AutoCAD DWG drawings with an AI agent.
|
||||
|
||||
Upstream project: https://github.com/dimitrovakulenko/dwg-mcp-server
|
||||
|
||||
## Setup
|
||||
|
||||
\`\`\`bash
|
||||
npx -y @dmytro-prototypes/dwg-mcp-server
|
||||
\`\`\`
|
||||
|
||||
For clients without MCP roots:
|
||||
|
||||
\`\`\`bash
|
||||
DWG_MCP_ALLOWED_ROOTS="$HOME" npx -y @dmytro-prototypes/dwg-mcp-server
|
||||
\`\`\`
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Ask for the DWG file path and confirm it is under an allowed root.
|
||||
2. Use \`dwg.list_roots\`, \`dwg.open_file\`, \`dwg.list_file_types\`, \`dwg.describe_type\`, \`dwg.get_objects\`, and \`dwg.query_objects\`.
|
||||
3. Summarize entities, layers, blocks, layouts, references, and notable properties.
|
||||
4. Close the document with \`dwg.close_file\`.
|
||||
`,
|
||||
'jeremylongshore/cad-ai-agent': `# CAD AI Agent Skill
|
||||
|
||||
Use this skill for CAD/DXF drawing summaries, compliance checks, quantity takeoffs, RFIs, health reports, revision comparison, and safe edit planning.
|
||||
|
||||
Upstream project: https://github.com/jeremylongshore/cad-ai-agent
|
||||
|
||||
## Setup
|
||||
|
||||
\`\`\`bash
|
||||
git clone https://github.com/jeremylongshore/cad-ai-agent.git
|
||||
cd cad-ai-agent
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -e ".[dev]"
|
||||
\`\`\`
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Identify drawing format and user intent.
|
||||
2. Preserve originals and prefer save-as outputs.
|
||||
3. For edits, produce structured operation plans rather than touching raw CAD data directly.
|
||||
4. For analysis, return layer/entity summaries, issues, measurements, and concrete next actions.
|
||||
`,
|
||||
};
|
||||
|
||||
const SOURCE_ICONS: Record<string, React.ReactNode> = {
|
||||
boosapi: <img src={appLogo} alt="" style={{ width: 14, height: 14, verticalAlign: 'middle' }} />,
|
||||
@@ -299,10 +348,10 @@ export function SkillsPage() {
|
||||
setMarketplaceDetailContent({ name: skill.name, repo: skill.repo, content: '' });
|
||||
try {
|
||||
const res = await fetch(`https://raw.githubusercontent.com/${repo}/main/SKILL.md`);
|
||||
const content = res.ok ? await res.text() : '(SKILL.md not found)';
|
||||
const content = res.ok ? await res.text() : (BUILTIN_MARKETPLACE_DETAILS[repo] || '(SKILL.md not found)');
|
||||
setMarketplaceDetailContent({ name: skill.name, repo: skill.repo, content });
|
||||
} catch {
|
||||
setMarketplaceDetailContent({ name: skill.name, repo: skill.repo, content: '(Failed to fetch SKILL.md)' });
|
||||
setMarketplaceDetailContent({ name: skill.name, repo: skill.repo, content: BUILTIN_MARKETPLACE_DETAILS[repo] || '(Failed to fetch SKILL.md)' });
|
||||
} finally {
|
||||
setMarketplaceDetailLoading(false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user