mirror of
https://github.com/sveltejs/ai-tools.git
synced 2026-07-03 19:19:25 +08:00
Compare commits
9 Commits
@sveltejs/
...
remove-db-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7fd1d058ed | ||
|
|
030f9b6a68 | ||
|
|
2ded13539a | ||
|
|
96c50acae2 | ||
|
|
4920088e5c | ||
|
|
8557f0af6f | ||
|
|
972d0f0bb2 | ||
|
|
7bf364a9d5 | ||
|
|
d06d758b81 |
5
.changeset/small-actors-notice.md
Normal file
5
.changeset/small-actors-notice.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@sveltejs/mcp': patch
|
||||
---
|
||||
|
||||
chore: remove db requirement
|
||||
4
.github/workflows/check.yml
vendored
4
.github/workflows/check.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
uses: pnpm/action-setup@v5
|
||||
with:
|
||||
version: 10.28.2
|
||||
|
||||
@@ -32,6 +32,4 @@ jobs:
|
||||
- name: Run type check
|
||||
run: pnpm run check
|
||||
env:
|
||||
DATABASE_URL: file:test.db
|
||||
DATABASE_TOKEN: dummy-key
|
||||
VOYAGE_API_KEY: dummy-key
|
||||
|
||||
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
uses: pnpm/action-setup@v5
|
||||
with:
|
||||
version: 10.28.2
|
||||
|
||||
@@ -32,6 +32,4 @@ jobs:
|
||||
- name: Run linting
|
||||
run: pnpm run lint
|
||||
env:
|
||||
DATABASE_URL: file:test.db
|
||||
VOYAGE_API_KEY: dummy-key
|
||||
DATABASE_TOKEN: dummy-key
|
||||
|
||||
6
.github/workflows/test.yml
vendored
6
.github/workflows/test.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
uses: pnpm/action-setup@v5
|
||||
with:
|
||||
version: 10.28.2
|
||||
|
||||
@@ -32,13 +32,9 @@ jobs:
|
||||
- name: Build project
|
||||
run: pnpm run build
|
||||
env:
|
||||
DATABASE_URL: file:test.db
|
||||
VOYAGE_API_KEY: dummy-key
|
||||
DATABASE_TOKEN: dummy-key
|
||||
|
||||
- name: Run tests
|
||||
run: pnpm run test
|
||||
env:
|
||||
DATABASE_URL: file:test.db
|
||||
VOYAGE_API_KEY: dummy-key
|
||||
DATABASE_TOKEN: dummy-key
|
||||
|
||||
@@ -83,7 +83,6 @@ Located in `src/lib/server/analyze/`:
|
||||
|
||||
Required environment variables:
|
||||
|
||||
- `DATABASE_URL`: SQLite database path (default: `file:test.db`)
|
||||
- `VOYAGE_API_KEY`: API key for embeddings support (optional)
|
||||
|
||||
When connected to the svelte-llm MCP server, you have access to comprehensive Svelte 5 and SvelteKit documentation. Here's how to use the available tools effectively:
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
DATABASE_URL=file:test.db
|
||||
DATABASE_TOKEN=needs_to_be_set_but_it_can_be_anything
|
||||
VOYAGE_API_KEY=your_actual_api_key_here
|
||||
@@ -1,12 +0,0 @@
|
||||
import { defineConfig } from 'drizzle-kit';
|
||||
|
||||
if (!process.env.DATABASE_URL) throw new Error('DATABASE_URL is not set');
|
||||
if (!process.env.DATABASE_TOKEN) throw new Error('DATABASE_TOKEN is not set');
|
||||
|
||||
export default defineConfig({
|
||||
schema: './src/lib/server/db/schema.ts',
|
||||
dialect: 'turso',
|
||||
dbCredentials: { url: process.env.DATABASE_URL, authToken: process.env.DATABASE_TOKEN },
|
||||
verbose: true,
|
||||
strict: true,
|
||||
});
|
||||
@@ -23,10 +23,6 @@
|
||||
"test:unit": "vitest",
|
||||
"test": "npm run test:unit -- --run",
|
||||
"test:watch": "npm run test:unit -- --watch",
|
||||
"db:push": "drizzle-kit push",
|
||||
"db:generate": "drizzle-kit generate",
|
||||
"db:migrate": "drizzle-kit migrate",
|
||||
"db:studio": "drizzle-kit studio",
|
||||
"inspect": "pnpm mcp-inspector"
|
||||
},
|
||||
"keywords": [
|
||||
@@ -39,15 +35,12 @@
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "catalog:lint",
|
||||
"@eslint/js": "catalog:lint",
|
||||
"@libsql/client": "catalog:orm",
|
||||
"@modelcontextprotocol/inspector": "catalog:ai",
|
||||
"@sveltejs/adapter-vercel": "catalog:svelte",
|
||||
"@sveltejs/kit": "catalog:svelte",
|
||||
"@sveltejs/vite-plugin-svelte": "catalog:svelte",
|
||||
"@types/node": "catalog:tooling",
|
||||
"@typescript-eslint/parser": "catalog:lint",
|
||||
"drizzle-kit": "catalog:orm",
|
||||
"drizzle-orm": "catalog:orm",
|
||||
"eslint-config-prettier": "catalog:lint",
|
||||
"eslint-plugin-svelte": "catalog:lint",
|
||||
"globals": "catalog:lint",
|
||||
@@ -62,7 +55,6 @@
|
||||
"vitest": "catalog:tooling"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sveltejs/mcp-schema": "workspace:^",
|
||||
"@sveltejs/mcp-server": "workspace:^",
|
||||
"@tmcp/transport-http": "catalog:tmcp",
|
||||
"@vercel/analytics": "catalog:tooling",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { dev } from '$app/environment';
|
||||
import { http_transport } from '$lib/mcp/index.js';
|
||||
import { db } from '$lib/server/db/index.js';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { track } from '@vercel/analytics/server';
|
||||
|
||||
@@ -17,7 +16,6 @@ export async function handle({ event, resolve }) {
|
||||
}
|
||||
}
|
||||
const mcp_response = await http_transport.respond(event.request, {
|
||||
db,
|
||||
// only add analytics in production
|
||||
track: dev
|
||||
? undefined
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import { createClient } from '@libsql/client';
|
||||
import { drizzle } from 'drizzle-orm/libsql';
|
||||
import * as schema from './schema.js';
|
||||
import { DATABASE_TOKEN, DATABASE_URL } from '$env/static/private';
|
||||
if (!DATABASE_URL) throw new Error('DATABASE_URL is not set');
|
||||
if (!DATABASE_TOKEN) throw new Error('DATABASE_TOKEN is not set');
|
||||
|
||||
const client = createClient({
|
||||
url: DATABASE_URL,
|
||||
authToken: DATABASE_TOKEN,
|
||||
});
|
||||
|
||||
export const db = drizzle(client, { schema, logger: true });
|
||||
@@ -1,2 +0,0 @@
|
||||
// we need to re-export from here to allow for the drizzle config to pick them up for migrations
|
||||
export * from '@sveltejs/mcp-schema/schema';
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"name": "@sveltejs/mcp-schema",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"exports": {
|
||||
".": "./src/index.js",
|
||||
"./utils": "./src/utils.js",
|
||||
"./schema": "./src/schema.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"drizzle-orm": "catalog:orm"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* @import * as schema from './schema.js'
|
||||
*/
|
||||
export * from './schema.js';
|
||||
|
||||
/**
|
||||
* @typedef {typeof schema} Schema
|
||||
*/
|
||||
@@ -1,82 +0,0 @@
|
||||
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
|
||||
import { float_32_array } from './utils.js';
|
||||
|
||||
/**
|
||||
* NOTE: if you modify a schema adding a vector column you need to manually add this
|
||||
*
|
||||
* CREATE INDEX IF NOT EXISTS name_of_the_index
|
||||
* ON `name_of_the_table` (
|
||||
* libsql_vector_idx(name_of_the_column, 'metric=cosine')
|
||||
* )
|
||||
*
|
||||
* to the generated migration file
|
||||
*/
|
||||
|
||||
export const distillations = sqliteTable('distillations', {
|
||||
id: integer('id').primaryKey(),
|
||||
preset_name: text('preset_name').notNull(),
|
||||
version: text('version').notNull(),
|
||||
content: text('content').notNull(),
|
||||
size_kb: integer('size_kb').notNull(),
|
||||
document_count: integer('document_count').notNull(),
|
||||
distillation_job_id: integer('distillation_job_id').references(() => distillation_jobs.id),
|
||||
created_at: integer('created_at', { mode: 'timestamp' })
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date()),
|
||||
});
|
||||
|
||||
export const distillation_jobs = sqliteTable('distillation_jobs', {
|
||||
id: integer('id').primaryKey(),
|
||||
preset_name: text('preset_name').notNull(),
|
||||
batch_id: text('batch_id'),
|
||||
status: text('status', { enum: ['pending', 'processing', 'completed', 'failed'] }).notNull(),
|
||||
model_used: text('model_used').notNull(),
|
||||
total_files: integer('total_files').notNull(),
|
||||
processed_files: integer('processed_files').notNull().default(0),
|
||||
successful_files: integer('successful_files').notNull().default(0),
|
||||
minimize_applied: integer('minimize_applied', { mode: 'boolean' }).notNull().default(false),
|
||||
total_input_tokens: integer('total_input_tokens').notNull().default(0),
|
||||
total_output_tokens: integer('total_output_tokens').notNull().default(0),
|
||||
started_at: integer('started_at', { mode: 'timestamp' }),
|
||||
completed_at: integer('completed_at', { mode: 'timestamp' }),
|
||||
error_message: text('error_message'),
|
||||
metadata: text('metadata', { mode: 'json' }).notNull().default({}),
|
||||
created_at: integer('created_at', { mode: 'timestamp' })
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date()),
|
||||
updated_at: integer('updated_at', { mode: 'timestamp' })
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date()),
|
||||
});
|
||||
|
||||
export const content = sqliteTable('content', {
|
||||
id: integer('id').primaryKey(),
|
||||
path: text('path').notNull(),
|
||||
filename: text('filename').notNull(),
|
||||
content: text('content').notNull(),
|
||||
size_bytes: integer('size_bytes').notNull(),
|
||||
embeddings: float_32_array('embeddings', { dimensions: 1024 }),
|
||||
metadata: text('metadata', { mode: 'json' }).notNull().default({}),
|
||||
created_at: integer('created_at', { mode: 'timestamp' })
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date()),
|
||||
updated_at: integer('updated_at', { mode: 'timestamp' })
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date()),
|
||||
});
|
||||
|
||||
export const content_distilled = sqliteTable('content_distilled', {
|
||||
id: integer('id').primaryKey(),
|
||||
path: text('path').notNull(),
|
||||
filename: text('filename').notNull(),
|
||||
content: text('content').notNull(),
|
||||
size_bytes: integer('size_bytes').notNull(),
|
||||
embeddings: float_32_array('embeddings', { dimensions: 1024 }),
|
||||
metadata: text('metadata', { mode: 'json' }).notNull().default({}),
|
||||
created_at: integer('created_at', { mode: 'timestamp' })
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date()),
|
||||
updated_at: integer('updated_at', { mode: 'timestamp' })
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date()),
|
||||
});
|
||||
@@ -1,65 +0,0 @@
|
||||
/**
|
||||
* @import { Column } from 'drizzle-orm';
|
||||
*/
|
||||
import { sql } from 'drizzle-orm';
|
||||
import { customType } from 'drizzle-orm/sqlite-core';
|
||||
|
||||
/**
|
||||
* Helper function to convert an array of embeddings into a format that can be inserted into a LibSQL vector column.
|
||||
* @param {number[]} arr The embeddings array.
|
||||
*/
|
||||
export function vector(arr) {
|
||||
return sql`vector32(${JSON.stringify(arr)})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to calculate the distance between a vector column and an array of embeddings and return it as a columns.
|
||||
* @param {Column} column The drizzle column representing the vector.
|
||||
* @param {number} arr The embeddings array.
|
||||
* @param {string} as The name of the returned column. Default is 'distance'.
|
||||
*
|
||||
* @example
|
||||
* await db.select({
|
||||
* id: vector_table.id,
|
||||
* text: vector_table.text,
|
||||
* distance: distance(vector_table.vector, await get_embeddings(sentence)),
|
||||
* })
|
||||
* .from(vector_table)
|
||||
* .orderBy(sql`distance`)
|
||||
* .execute();
|
||||
*/
|
||||
export function distance(column, arr, as = 'distance') {
|
||||
return /** @type {typeof sql<number>} */ (
|
||||
sql
|
||||
)`CASE ${column} ISNULL WHEN 1 THEN 1 ELSE vector_distance_cos(${column}, vector32(${JSON.stringify(arr)})) END`.as(
|
||||
as,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom drizzle type to use the LibSQL vector column type.
|
||||
*/
|
||||
export const float_32_array = /** @type {typeof customType<{
|
||||
data: number[];
|
||||
config: { dimensions: number };
|
||||
configRequired: true;
|
||||
driverData: Buffer;
|
||||
}>} */ (customType)({
|
||||
dataType(config) {
|
||||
return `F32_BLOB(${config.dimensions})`;
|
||||
},
|
||||
/**
|
||||
* @param {Buffer} value
|
||||
*/
|
||||
fromDriver(value) {
|
||||
return Array.from(new Float32Array(value.buffer));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {number[]} value
|
||||
* @returns
|
||||
*/
|
||||
toDriver(value) {
|
||||
return vector(value);
|
||||
},
|
||||
});
|
||||
@@ -17,12 +17,8 @@
|
||||
".": "./src/index.ts",
|
||||
"./handlers": "./src/mcp/handlers/tools/handlers.ts"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"drizzle-orm": "^0.45.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mcp-ui/server": "catalog:ai",
|
||||
"@sveltejs/mcp-schema": "workspace:^",
|
||||
"@tmcp/adapter-valibot": "catalog:tmcp",
|
||||
"@tmcp/transport-in-memory": "catalog:tmcp",
|
||||
"@typescript-eslint/parser": "catalog:lint",
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { ValibotJsonSchemaAdapter } from '@tmcp/adapter-valibot';
|
||||
import { McpServer } from 'tmcp';
|
||||
import { setup_prompts, setup_resources, setup_tools } from './handlers/index.js';
|
||||
import type { LibSQLDatabase } from 'drizzle-orm/libsql';
|
||||
import type { Schema } from '@sveltejs/mcp-schema';
|
||||
import { icons } from './icons/index.js';
|
||||
|
||||
export const server = new McpServer(
|
||||
@@ -25,7 +23,6 @@ export const server = new McpServer(
|
||||
'This is the official Svelte MCP server. It MUST be used whenever svelte development is involved. It can provide official documentation, code examples and correct your code. After you correct the component call this tool again to confirm all the issues are fixed.',
|
||||
},
|
||||
).withContext<{
|
||||
db: LibSQLDatabase<Schema>;
|
||||
track?: (sessionId: string, event: string, extra?: string) => Promise<void>;
|
||||
}>();
|
||||
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @sveltejs/opencode
|
||||
|
||||
## 0.1.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- fix: add `server` export to opencode plugin ([`96c50ac`](https://github.com/sveltejs/ai-tools/commit/96c50acae2b4131a6c72d3579a73c44ab9158b18))
|
||||
|
||||
## 0.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- fix: import `ts` files directly ([#190](https://github.com/sveltejs/ai-tools/pull/190))
|
||||
|
||||
## 0.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
11
packages/opencode/agents.ts
Normal file
11
packages/opencode/agents.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// This file is auto-generated by scripts/sync-opencode-plugin.ts
|
||||
// Do not edit manually — edit the markdown files in tools/agents/ instead.
|
||||
|
||||
export const agents = {
|
||||
'svelte-file-editor': {
|
||||
description:
|
||||
'Specialized Svelte 5 code editor. MUST BE USED PROACTIVELY when creating, editing, or reviewing any .svelte file or .svelte.ts/.svelte.js module and MUST use the tools from the MCP server or the `svelte-file-editor` skill if they are available. Fetches relevant documentation and validates code using the Svelte MCP server tools.',
|
||||
prompt:
|
||||
"You are a Svelte 5 expert responsible for writing, editing, and validating Svelte components and modules. You have access to the Svelte MCP server which provides documentation and code analysis tools. Always use the tools from the svelte MCP server to fetch documentation with `get_documentation` and validating the code with `svelte_autofixer`. If the autofixer returns any issue or suggestions try to solve them.\n\nIf the MCP tools are not available you can use the `svelte-code-writer` skill to learn how to use the `@sveltejs/mcp` cli to access the same tools.\n\nIf the skill is not available you can run `npx @sveltejs/mcp@latest -y --help` to learn how to use it.\n\n## Available MCP Tools\n\n### 1. list-sections\n\nLists all available Svelte 5 and SvelteKit documentation sections with titles and paths. Use this first to discover what documentation is available.\n\n### 2. get-documentation\n\nRetrieves full documentation for specified sections. Accepts a single section name or an array of section names. Use after `list-sections` to fetch relevant docs for the task at hand.\n\n**Example sections:** `$state`, `$derived`, `$effect`, `$props`, `$bindable`, `snippets`, `routing`, `load functions`\n\n### 3. svelte-autofixer\n\nAnalyzes Svelte code and returns suggestions to fix issues. Pass the component code directly to this tool. It will detect common mistakes like:\n\n- Using `$effect` instead of `$derived` for computations\n- Missing cleanup in effects\n- Svelte 4 syntax (`on:click`, `export let`, `<slot>`)\n- Missing keys in `{#each}` blocks\n- And more\n\n## Workflow\n\nWhen invoked to work on a Svelte file:\n\n### 1. Gather Context (if needed)\n\nIf you're uncertain about Svelte 5 syntax or patterns, use the MCP tools:\n\n1. Call `list-sections` to see available documentation\n2. Call `get-documentation` with relevant section names\n\n### 2. Read the Target File\n\nRead the file to understand the current implementation.\n\n### 3. Make Changes\n\nApply edits following Svelte 5 best practices:\n\n### 4. Validate Changes\n\nAfter editing, ALWAYS call `svelte-autofixer` with the updated code to check for issues.\n\n### 5. Fix Any Issues\n\nIf the autofixer reports problems, fix them and re-validate until no issues remain.\n\n## Output Format\n\nAfter completing your work, provide:\n\n1. Summary of changes made\n2. Any issues found and fixed by the autofixer\n3. Recommendations for further improvements (if any)",
|
||||
},
|
||||
} as const;
|
||||
@@ -2,7 +2,8 @@ import type { Plugin } from '@opencode-ai/plugin';
|
||||
import { readdir } from 'node:fs/promises';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { get_mcp_config } from './config.js';
|
||||
import { agents } from './agents.ts';
|
||||
import { get_mcp_config } from './config.ts';
|
||||
|
||||
const current_dir = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
@@ -72,105 +73,44 @@ export const svelte_plugin: Plugin = async (ctx) => {
|
||||
}
|
||||
}
|
||||
if (mcp_config.subagent?.enabled !== false) {
|
||||
// we add the editor subagent that will be used when editing Svelte files to prevent wasting context on the main agent
|
||||
const default_config: (typeof input.agent)[string] = {
|
||||
color: '#ff3e00',
|
||||
mode: 'subagent',
|
||||
prompt: `You are a Svelte 5 expert responsible for writing, editing, and validating Svelte components and modules. You have access to the Svelte MCP server which provides documentation and code analysis tools. Always use the tools from the svelte MCP server to fetch documentation with \`get_documentation\` and validating the code with \`svelte_autofixer\`. If the autofixer returns any issue or suggestions try to solve them.
|
||||
for (const [agent_name, agent_data] of Object.entries(agents)) {
|
||||
// we add the editor subagent that will be used when editing Svelte files to prevent wasting context on the main agent
|
||||
const default_config: (typeof input.agent)[string] = {
|
||||
color: '#ff3e00',
|
||||
mode: 'subagent',
|
||||
prompt: agent_data.prompt,
|
||||
description: agent_data.description,
|
||||
permission: {
|
||||
bash: 'ask',
|
||||
edit: 'allow',
|
||||
webfetch: 'ask',
|
||||
},
|
||||
tools: {
|
||||
[`${svelte_mcp_name}_*`]: true,
|
||||
},
|
||||
};
|
||||
|
||||
If the MCP tools are not available you can use the \`svelte-code-writer\` skill to learn how to use the \`@sveltejs/mcp\` cli to access the same tools.
|
||||
// Get per-agent config from svelte.json (if any)
|
||||
const agent_config = mcp_config.subagent?.agents?.[agent_name];
|
||||
|
||||
If the skill is not available you can run \`npx @sveltejs/mcp@latest -y --help\` to learn how to use it.
|
||||
|
||||
## Available MCP Tools
|
||||
|
||||
### 1. list-sections
|
||||
|
||||
Lists all available Svelte 5 and SvelteKit documentation sections with titles and paths. Use this first to discover what documentation is available.
|
||||
|
||||
### 2. get-documentation
|
||||
|
||||
Retrieves full documentation for specified sections. Accepts a single section name or an array of section names. Use after \`list-sections\` to fetch relevant docs for the task at hand.
|
||||
|
||||
**Example sections:** \`$state\`, \`$derived\`, \`$effect\`, \`$props\`, \`$bindable\`, \`snippets\`, \`routing\`, \`load functions\`
|
||||
|
||||
### 3. svelte-autofixer
|
||||
|
||||
Analyzes Svelte code and returns suggestions to fix issues. Pass the component code directly to this tool. It will detect common mistakes like:
|
||||
|
||||
- Using \`$effect\` instead of \`$derived\` for computations
|
||||
- Missing cleanup in effects
|
||||
- Svelte 4 syntax (\`on:click\`, \`export let\`, \`<slot>\`)
|
||||
- Missing keys in \`{#each}\` blocks
|
||||
- And more
|
||||
|
||||
## Workflow
|
||||
|
||||
When invoked to work on a Svelte file:
|
||||
|
||||
### 1. Gather Context (if needed)
|
||||
|
||||
If you're uncertain about Svelte 5 syntax or patterns, use the MCP tools:
|
||||
|
||||
1. Call \`list-sections\` to see available documentation
|
||||
2. Call \`get-documentation\` with relevant section names
|
||||
|
||||
### 2. Read the Target File
|
||||
|
||||
Read the file to understand the current implementation.
|
||||
|
||||
### 3. Make Changes
|
||||
|
||||
Apply edits following Svelte 5 best practices:
|
||||
|
||||
### 4. Validate Changes
|
||||
|
||||
After editing, ALWAYS call \`svelte-autofixer\` with the updated code to check for issues.
|
||||
|
||||
### 5. Fix Any Issues
|
||||
|
||||
If the autofixer reports problems, fix them and re-validate until no issues remain.
|
||||
|
||||
## Output Format
|
||||
|
||||
After completing your work, provide:
|
||||
|
||||
1. Summary of changes made
|
||||
2. Any issues found and fixed by the autofixer
|
||||
3. Recommendations for further improvements (if any)
|
||||
`,
|
||||
description:
|
||||
'Specialized Svelte 5 code editor. MUST BE USED PROACTIVELY when creating, editing, or reviewing any .svelte file or .svelte.ts/.svelte.js module and MUST use the tools from the MCP server or the `svelte-code-writer` skill if they are available. Fetches relevant documentation and validates code using the Svelte MCP server tools.',
|
||||
permission: {
|
||||
bash: 'ask',
|
||||
edit: 'allow',
|
||||
webfetch: 'ask',
|
||||
},
|
||||
tools: {
|
||||
[`${svelte_mcp_name}_*`]: true,
|
||||
},
|
||||
};
|
||||
|
||||
// Get per-agent config from svelte.json (if any)
|
||||
const svelte_file_editor_config = mcp_config.subagent?.agents?.['svelte-file-editor'];
|
||||
|
||||
// Configure agent from svelte.json only
|
||||
// Priority: svelte.json agent config > defaults
|
||||
input.agent['svelte-file-editor'] = {
|
||||
...default_config,
|
||||
...(svelte_file_editor_config?.model !== undefined && {
|
||||
model: svelte_file_editor_config.model,
|
||||
}),
|
||||
...(svelte_file_editor_config?.temperature !== undefined && {
|
||||
temperature: svelte_file_editor_config.temperature,
|
||||
}),
|
||||
...(svelte_file_editor_config?.maxSteps !== undefined && {
|
||||
maxSteps: svelte_file_editor_config.maxSteps,
|
||||
}),
|
||||
...(svelte_file_editor_config?.top_p !== undefined && {
|
||||
top_p: svelte_file_editor_config.top_p,
|
||||
}),
|
||||
};
|
||||
// Configure agent from svelte.json only
|
||||
// Priority: svelte.json agent config > defaults
|
||||
input.agent[agent_name] = {
|
||||
...default_config,
|
||||
...(agent_config?.model !== undefined && {
|
||||
model: agent_config.model,
|
||||
}),
|
||||
...(agent_config?.temperature !== undefined && {
|
||||
temperature: agent_config.temperature,
|
||||
}),
|
||||
...(agent_config?.maxSteps !== undefined && {
|
||||
maxSteps: agent_config.maxSteps,
|
||||
}),
|
||||
...(agent_config?.top_p !== undefined && {
|
||||
top_p: agent_config.top_p,
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sveltejs/opencode",
|
||||
"version": "0.1.6",
|
||||
"version": "0.1.8",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/sveltejs/ai-tools#readme",
|
||||
@@ -14,9 +14,16 @@
|
||||
"files": [
|
||||
"index.ts",
|
||||
"config.ts",
|
||||
"agents.ts",
|
||||
"instructions",
|
||||
"skills"
|
||||
],
|
||||
"exports": {
|
||||
"./server": {
|
||||
"types": "./index.ts",
|
||||
"import": "./index.ts"
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/sveltejs/ai-tools.git",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": ["index.ts", "config.ts", "scripts/*"],
|
||||
"compilerOptions": {
|
||||
"allowImportingTsExtensions": true,
|
||||
"types": ["@types/node"]
|
||||
},
|
||||
"include": ["index.ts", "config.ts", "agents.ts", "scripts/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
700
pnpm-lock.yaml
generated
700
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -24,10 +24,6 @@ catalogs:
|
||||
prettier-plugin-svelte: ^3.3.3
|
||||
svelte-eslint-parser: ^1.4.0
|
||||
typescript-eslint: ^8.44.0
|
||||
orm:
|
||||
'@libsql/client': ^0.17.0
|
||||
drizzle-kit: ^0.31.0
|
||||
drizzle-orm: ^0.45.0
|
||||
svelte:
|
||||
'@sveltejs/adapter-vercel': ^6.0.0
|
||||
'@sveltejs/kit': ^2.42.2
|
||||
@@ -46,7 +42,7 @@ catalogs:
|
||||
'@types/estree': ^1.0.8
|
||||
'@types/node': ^24.3.1
|
||||
'@valibot/to-json-schema': ^1.5.0
|
||||
'@vercel/analytics': ^1.5.0
|
||||
'@vercel/analytics': ^2.0.0
|
||||
dotenv: ^17.2.3
|
||||
node-resolve-ts: ^1.0.2
|
||||
publint: ^0.3.13
|
||||
|
||||
@@ -37,7 +37,77 @@ async function sync_agents_md() {
|
||||
console.log('Synced AGENTS.md to opencode package and documentation');
|
||||
}
|
||||
|
||||
interface AgentData {
|
||||
name: string;
|
||||
description: string;
|
||||
prompt: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a markdown agent file with frontmatter (name, description) and body (prompt)
|
||||
*/
|
||||
function parse_agent_md(content: string, file_path: string): AgentData | null {
|
||||
const frontmatter_match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
||||
if (!frontmatter_match) {
|
||||
console.warn(`Skipping ${file_path}: no frontmatter found`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const frontmatter = frontmatter_match[1] ?? '';
|
||||
const body = (frontmatter_match[2] ?? '').trim();
|
||||
|
||||
const name = frontmatter.match(/^name:\s*(.+)$/m)?.[1]?.trim();
|
||||
const description = frontmatter.match(/^description:\s*(.+)$/m)?.[1]?.trim();
|
||||
|
||||
if (!name || !description) {
|
||||
console.warn(`Skipping ${file_path}: missing name or description in frontmatter`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return { name, description, prompt: body };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate agents.ts module from tools/agents/*.md files
|
||||
*/
|
||||
async function sync_agents() {
|
||||
const agents_dir = path.join(TOOLS_DIR, 'agents');
|
||||
const entries = await fs.readdir(agents_dir, { withFileTypes: true });
|
||||
const md_files = entries.filter((e) => e.isFile() && e.name.endsWith('.md'));
|
||||
|
||||
const agents: AgentData[] = [];
|
||||
|
||||
for (const file of md_files) {
|
||||
const file_path = path.join(agents_dir, file.name);
|
||||
const content = await fs.readFile(file_path, 'utf-8');
|
||||
const agent = parse_agent_md(content, file_path);
|
||||
if (agent) {
|
||||
agents.push(agent);
|
||||
}
|
||||
}
|
||||
|
||||
const output = [
|
||||
'// This file is auto-generated by scripts/sync-opencode-plugin.ts',
|
||||
'// Do not edit manually — edit the markdown files in tools/agents/ instead.',
|
||||
'',
|
||||
`export const agents = ${JSON.stringify(
|
||||
Object.fromEntries(
|
||||
agents.map((a) => [a.name, { description: a.description, prompt: a.prompt }]),
|
||||
),
|
||||
null,
|
||||
'\t',
|
||||
)} as const;`,
|
||||
'',
|
||||
].join('\n');
|
||||
|
||||
const dest = path.join(OPENCODE_PKG_DIR, 'agents.ts');
|
||||
await fs.writeFile(dest, output);
|
||||
|
||||
console.log(`Generated agents.ts with ${agents.length} agent(s)`);
|
||||
}
|
||||
|
||||
await sync_skills();
|
||||
await sync_agents_md();
|
||||
await sync_agents();
|
||||
|
||||
console.log('OpenCode plugin sync complete');
|
||||
|
||||
Reference in New Issue
Block a user