-
-
Notifications
You must be signed in to change notification settings - Fork 93
feat: welcome ai-tools
#1050
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: welcome ai-tools
#1050
Changes from all commits
b2708a6
1ea90d1
d594c43
980ee92
cbe7987
b9d2d34
f2546ba
b2588fd
44d2dc8
dcf4696
daf980f
9d05cee
e7d1d77
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| 'sv': minor | ||
| --- | ||
|
|
||
| feat(ai-tools): replace `mcp` add-on with `ai-tools` add-on that includes both MCP and skills setup |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| packages/sv/src/cli/tests/snapshots/* | ||
| packages/sv-utils/src/tests/**/output.ts | ||
| packages/sv-utils/src/tests/**/output.ts | ||
| packages/sv/src/create/shared/+skills/* |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,12 +29,26 @@ const options = defineAddonOptions() | |
| required: true, | ||
| condition: ({ ide }) => !(ide.length === 1 && ide.includes('opencode')) | ||
| }) | ||
| .add('skills', { | ||
| question: 'Do you want to install skills?', | ||
| type: 'select', | ||
| default: 'files', | ||
| options: [ | ||
| { value: 'files', label: 'Add files to the project' }, | ||
| { | ||
| value: 'none', | ||
| label: 'Skip', | ||
| hint: 'for Claude Code you can install the plugin instead: /plugin install svelte' | ||
| } | ||
| ], | ||
| condition: ({ ide }) => ide.some((i) => i !== 'opencode' && i !== 'other') | ||
| }) | ||
|
Comment on lines
+32
to
+45
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we also add a skill selection? Right now, there's only two, but maybe in the future there will be more, and you don't want them all? |
||
| .build(); | ||
|
|
||
| export default defineAddon({ | ||
| id: 'mcp', | ||
| shortDescription: 'Svelte MCP', | ||
| homepage: 'https://svelte.dev/docs/mcp', | ||
| id: 'ai-tools', | ||
| shortDescription: 'Svelte AI Tools', | ||
| homepage: 'https://svelte.dev/docs/ai', | ||
| options, | ||
| run: ({ sv, options }) => { | ||
| const getLocalConfig = (o?: { | ||
|
|
@@ -72,14 +86,19 @@ export default defineAddon({ | |
| }; | ||
| agentPath: string; | ||
| configPath: string; | ||
| skillsPath?: string; | ||
| agentsPath?: string; | ||
| agentExtension?: string; | ||
| customData?: Record<string, any>; | ||
| extraFiles?: Array<{ path: string; data: Record<string, any> }>; | ||
| } | ||
| | { other: true } | ||
| > = { | ||
| 'claude-code': { | ||
| agentPath: 'CLAUDE.md', | ||
| agentPath: '.claude/CLAUDE.md', | ||
| configPath: '.mcp.json', | ||
| skillsPath: '.claude/skills', | ||
|
jycouet marked this conversation as resolved.
|
||
| agentsPath: '.claude/agents', | ||
| mcpOptions: { | ||
| typeLocal: 'stdio', | ||
| typeRemote: 'http', | ||
|
|
@@ -89,11 +108,13 @@ export default defineAddon({ | |
| cursor: { | ||
| agentPath: 'AGENTS.md', | ||
| configPath: '.cursor/mcp.json', | ||
| agentsPath: '.cursor/agents', | ||
| mcpOptions: {} | ||
| }, | ||
| gemini: { | ||
| agentPath: 'GEMINI.md', | ||
| configPath: '.gemini/settings.json', | ||
| agentsPath: '.gemini/agents', | ||
| schema: | ||
| 'https://raw.githubusercontent.com/google-gemini/gemini-cli/main/schemas/settings.schema.json', | ||
| mcpOptions: {} | ||
|
|
@@ -115,6 +136,8 @@ export default defineAddon({ | |
| vscode: { | ||
| agentPath: 'AGENTS.md', | ||
| configPath: '.vscode/mcp.json', | ||
| agentsPath: '.github/agents', | ||
| agentExtension: '.agent.md', | ||
| mcpOptions: { | ||
| serversKey: 'servers' | ||
| } | ||
|
|
@@ -127,16 +150,29 @@ export default defineAddon({ | |
| const filesAdded: string[] = []; | ||
| const filesExistingAlready: string[] = []; | ||
|
|
||
| const sharedFiles = getSharedFiles().filter((file) => file.include.includes('mcp')); | ||
| const agentFile = sharedFiles.find((file) => file.name === 'AGENTS.md'); | ||
| const sharedFiles = getSharedFiles(); | ||
| const mcpFiles = sharedFiles.filter((file) => file.include.includes('mcp')); | ||
| const skillFiles = sharedFiles.filter((file) => file.include.includes('skills')); | ||
| const agentFiles = sharedFiles.filter((file) => file.include.includes('agents')); | ||
| const agentFile = mcpFiles.find((file) => file.name === 'AGENTS.md'); | ||
|
|
||
| for (const ide of options.ide) { | ||
| const value = configurator[ide]; | ||
|
|
||
| if (value === undefined) continue; | ||
| if ('other' in value) continue; | ||
|
|
||
| const { mcpOptions, agentPath, configPath, schema, customData, extraFiles } = value; | ||
| const { | ||
| mcpOptions, | ||
| agentPath, | ||
| configPath, | ||
| skillsPath, | ||
| agentsPath, | ||
| agentExtension, | ||
| schema, | ||
| customData, | ||
| extraFiles | ||
| } = value; | ||
|
|
||
| // We only add the agent file if it's not already added | ||
| if (!filesAdded.includes(agentPath)) { | ||
|
|
@@ -184,12 +220,42 @@ export default defineAddon({ | |
| ); | ||
| } | ||
| } | ||
|
|
||
| // Add skills for clients that support them (not opencode - plugin handles it) | ||
| if (skillsPath && options.skills === 'files') { | ||
| for (const file of skillFiles) { | ||
| const filePath = `${skillsPath}/${file.name}`; | ||
| sv.file(filePath, (content) => { | ||
| if (content) { | ||
| filesExistingAlready.push(filePath); | ||
| return false; | ||
| } | ||
| return file.contents; | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| // Add sub-agents for clients that support them (not opencode - plugin handles it) | ||
| if (agentsPath) { | ||
| for (const file of agentFiles) { | ||
| const ext = agentExtension ?? '.md'; | ||
| const name = file.name.replace(/\.md$/, ext); | ||
| const filePath = `${agentsPath}/${name}`; | ||
| sv.file(filePath, (content) => { | ||
| if (content) { | ||
| filesExistingAlready.push(filePath); | ||
| return false; | ||
| } | ||
| return file.contents; | ||
| }); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (filesExistingAlready.length > 0) { | ||
| log.warn( | ||
| `${filesExistingAlready.map((path) => color.path(path)).join(', ')} already exists, we didn't touch ${filesExistingAlready.length > 1 ? 'them' : 'it'}. ` + | ||
| `See ${color.website('https://svelte.dev/docs/mcp/overview#Usage')} for manual setup.` | ||
| `See ${color.website('https://svelte.dev/docs/ai')} for manual setup.` | ||
| ); | ||
| } | ||
| }, | ||
|
|
@@ -199,7 +265,7 @@ export default defineAddon({ | |
|
|
||
| if (options.ide.includes('other')) { | ||
| steps.push( | ||
| `For other clients: ${color.website(`https://svelte.dev/docs/mcp/${options.setup}-setup#Other-clients`)}` | ||
| `For other clients: ${color.website(`https://svelte.dev/docs/ai/${options.setup}-setup#Other-clients`)}` | ||
| ); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,31 @@ | ||
| import fs from 'node:fs'; | ||
| import path from 'node:path'; | ||
| import { expect } from 'vitest'; | ||
| import mcp from '../../mcp.ts'; | ||
| import aiTools from '../../ai-tools.ts'; | ||
| import { setupTest } from '../_setup/suite.ts'; | ||
|
|
||
| const { test, testCases } = setupTest( | ||
| { mcp }, | ||
| { 'ai-tools': aiTools }, | ||
| { | ||
| kinds: [ | ||
| { | ||
| type: 'default-local', | ||
| options: { | ||
| mcp: { ide: ['claude-code', 'cursor', 'gemini', 'opencode', 'vscode'], setup: 'local' } | ||
| 'ai-tools': { | ||
| ide: ['claude-code', 'cursor', 'gemini', 'opencode', 'vscode'], | ||
| setup: 'local', | ||
| skills: 'files' | ||
| } | ||
| } | ||
| }, | ||
| { | ||
| type: 'default-remote', | ||
| options: { | ||
| mcp: { ide: ['claude-code', 'cursor', 'gemini', 'opencode', 'vscode'], setup: 'remote' } | ||
| 'ai-tools': { | ||
| ide: ['claude-code', 'cursor', 'gemini', 'opencode', 'vscode'], | ||
| setup: 'remote', | ||
| skills: 'files' | ||
| } | ||
| } | ||
| } | ||
| ], | ||
|
|
@@ -45,12 +53,12 @@ const { test, testCases } = setupTest( | |
| } | ||
| ); | ||
|
|
||
| test.concurrent.for(testCases)('mcp $kind.type $variant', (testCase, ctx) => { | ||
| test.concurrent.for(testCases)('ai-tools $kind.type $variant', (testCase, ctx) => { | ||
| const cwd = ctx.cwd(testCase); | ||
|
|
||
| const getContent = (filePath: string) => { | ||
| const cursorPath = path.resolve(cwd, filePath); | ||
| return fs.readFileSync(cursorPath, 'utf8'); | ||
| const fullPath = path.resolve(cwd, filePath); | ||
| return fs.readFileSync(fullPath, 'utf8'); | ||
| }; | ||
|
|
||
| const cursorMcpContent = getContent(`.cursor/mcp.json`); | ||
|
|
@@ -214,4 +222,22 @@ test.concurrent.for(testCases)('mcp $kind.type $variant', (testCase, ctx) => { | |
| } | ||
| `); | ||
| } | ||
|
|
||
| // skills should be installed for claude-code only (opencode uses plugin) | ||
| const claudeSkillsDir = path.resolve(cwd, '.claude/skills'); | ||
| expect(fs.existsSync(claudeSkillsDir)).toBe(true); | ||
| expect(fs.existsSync(path.resolve(claudeSkillsDir, 'svelte-code-writer/SKILL.md'))).toBe(true); | ||
| expect(fs.existsSync(path.resolve(claudeSkillsDir, 'svelte-core-bestpractices/SKILL.md'))).toBe( | ||
| true | ||
| ); | ||
|
|
||
| // opencode should NOT have skills (plugin handles it) | ||
| expect(fs.existsSync(path.resolve(cwd, '.opencode/skills'))).toBe(false); | ||
|
|
||
| // sub-agents should be installed for all clients except opencode | ||
| expect(fs.existsSync(path.resolve(cwd, '.claude/agents/svelte-file-editor.md'))).toBe(true); | ||
| expect(fs.existsSync(path.resolve(cwd, '.cursor/agents/svelte-file-editor.md'))).toBe(true); | ||
| expect(fs.existsSync(path.resolve(cwd, '.gemini/agents/svelte-file-editor.md'))).toBe(true); | ||
| expect(fs.existsSync(path.resolve(cwd, '.github/agents/svelte-file-editor.agent.md'))).toBe(true); | ||
| expect(fs.existsSync(path.resolve(cwd, '.opencode/agents'))).toBe(false); | ||
|
Comment on lines
+227
to
+242
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Technically, we also suggest claude code (and cursor) users to use the plugin but I don't think there's a way to programmatically add a plugin to claude code (and cursor). What I'm worried about is if we install the skills/subagents, and then they add the plugin (and they have multiple skills/subagents) |
||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| svelte-file-editor.md |
Uh oh!
There was an error while loading. Please reload this page.