Compare commits

...

14 Commits

Author SHA1 Message Date
paoloricciuti
6bbecaaf05 chore: update docs and schema 2026-03-10 23:16:59 +01:00
paoloricciuti
91b89d3191 fix: expand to other agents props and remove unneeded variant 2026-03-10 23:13:30 +01:00
paoloricciuti
f0754909d9 fix: better type 2026-03-10 17:44:50 +01:00
paoloricciuti
d2711aa57d fix: regenerate schema 2026-03-10 17:41:45 +01:00
paoloricciuti
2071d91e54 Merge remote-tracking branch 'origin/main' into fix/merge-user-svelte-agent-config 2026-03-10 17:40:56 +01:00
Paolo Ricciuti
024dd1f640 chore: delete bun.lock 2026-03-10 11:21:24 +01:00
Momochiro
a83d466155 refactor: move agent config inside subagent.agents
Consolidates agent configuration under subagent.agents to avoid
confusion between separate 'subagent' and 'agent' config keys.
Users now configure agents via subagent.agents instead of a
top-level agent key.
2026-03-08 18:01:27 +01:00
Momochiro
94953dd315 refactor: read agent names dynamically from tools/agents folder
Instead of hardcoding the list of known agents, discover them by reading
.md files from the tools/agents directory. This follows the same pattern
used for skill discovery and makes it easier to add new agents.
2026-03-08 17:56:56 +01:00
Momochiro
c4e3cf1cf7 fix: agent config only from svelte.json, not opencode.json
svelte.json is the sole source of truth for svelte agent configuration.
2026-03-08 10:02:57 +01:00
Momochiro
bc1e6b1583 fix: give svelte.json agent config priority over opencode.json
svelte.json is the canonical place for svelte-specific configuration,
so it should take precedence over any agent config in opencode.json.
2026-03-08 10:01:38 +01:00
Momochiro
e718b6fe7f fix: make agent config description generic
Don't mention specific agent names in the description, similar to how
skills schema doesn't mention specific skill names. Intellisense for
known agents is still provided via the schema generator.
2026-03-08 09:20:21 +01:00
Momochiro
d62a0982ae fix: remove unnecessary skills logic from schema generator
Only agent configuration is being added, not skills.
Took inspiration from PR #174 for the intellisense pattern only.
2026-03-08 08:48:28 +01:00
Momochiro
9eb7cf834b feat: add model and variant support to svelte.json config
Allows users to configure model and variant for the svelte-file-editor
agent directly in svelte.json instead of opencode.json.

Users can now set:
{
  "model": "anthropic/claude-sonnet-4-20250514",
  "variant": "coding"
}

in ~/.config/opencode/svelte.json or project-local .opencode/svelte.json

Fixes #175
2026-03-07 21:47:38 +01:00
Momochiro
04877c1bb9 fix: merge user-configured svelte-file-editor agent settings
Previously, the plugin unconditionally overwrote the svelte-file-editor
agent configuration, preventing users from customizing settings like
model, variant, or other agent properties.

Now user configuration is merged with plugin defaults, with user config
taking precedence. This allows users to override model/variant in their
opencode.json:

{
  "agent": {
    "svelte-file-editor": {
      "model": "anthropic/claude-sonnet-4-20250514",
      "variant": "coding"
    }
  }
}

Fixes #175
2026-03-07 20:16:37 +01:00
5 changed files with 127 additions and 7 deletions

View File

@@ -29,7 +29,15 @@ The default configuration for the Svelte OpenCode plugin looks like this...
"enabled": true
},
"subagent": {
"enabled": true
"enabled": true,
"agents": {
"svelte-file-editor": {
"model": "other-model", // defaults to the same as main agent,
"temperature": 1, // default to unset
"top_p": 0.7, // default to unset,
"maxSteps": 20 // default to unlimited
}
}
},
"skills": {
"enabled": true // it can also be an array of all the skills to enable like ['svelte-core-bestpractices']

View File

@@ -4,6 +4,28 @@ import { homedir } from 'os';
import { join } from 'path';
import * as v from 'valibot';
// Schema for individual agent configuration
const agent_config_schema = v.object({
model: v.pipe(
v.optional(v.string()),
v.description('Model identifier for the agent (e.g., "anthropic/claude-sonnet-4-20250514")'),
),
temperature: v.pipe(
v.optional(v.number()),
v.description('Temperature setting for the agent (e.g., 0.7)'),
),
top_p: v.pipe(
v.optional(v.number()),
v.description(
'Control response diversity with the top_p option. Alternative to temperature for controlling randomness.',
),
),
maxSteps: v.pipe(
v.optional(v.number()),
v.description('Maximum number of steps the agent can take (e.g., 10)'),
),
});
const default_config = {
mcp: {
type: 'remote' as 'remote' | 'local',
@@ -11,6 +33,7 @@ const default_config = {
},
subagent: {
enabled: true,
agents: {} as Record<string, v.InferInput<typeof agent_config_schema>>,
},
instructions: {
enabled: true,
@@ -36,6 +59,7 @@ export const config_schema = v.object({
v.optional(
v.object({
enabled: v.optional(v.boolean()),
agents: v.optional(v.record(v.string(), agent_config_schema)),
}),
),
v.description('Configuration for the subagent. You can choose if it should be enabled or not.'),
@@ -49,9 +73,6 @@ export const config_schema = v.object({
v.description(
'Configuration for the automatic AGENTS.md injection. You can choose if it should be enabled or not.',
),
v.description(
'Configuration for the automatic AGENTS.md injection. You can choose if it should be enabled or not.',
),
),
skills: v.pipe(
v.optional(
@@ -138,8 +159,12 @@ function merge_with_defaults(user_config: Partial<McpConfig>): McpConfig {
...user_config.mcp,
},
subagent: {
...default_config.subagent,
enabled: default_config.subagent.enabled,
...user_config.subagent,
agents: {
...default_config.subagent.agents,
...user_config.subagent?.agents,
},
},
instructions: {
...default_config.instructions,
@@ -177,7 +202,11 @@ export function get_mcp_config(ctx: PluginInput) {
if (parsed.success) {
merged = {
mcp: { ...merged.mcp, ...parsed.output.mcp },
subagent: { ...merged.subagent, ...parsed.output.subagent },
subagent: {
...merged.subagent,
...parsed.output.subagent,
agents: { ...merged.subagent?.agents, ...parsed.output.subagent?.agents },
},
instructions: { ...merged.instructions, ...parsed.output.instructions },
skills: { ...merged.skills, ...parsed.output.skills },
};

View File

@@ -73,7 +73,7 @@ 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
input.agent['svelte-file-editor'] = {
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.
@@ -150,6 +150,27 @@ After completing your work, provide:
[`${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,
}),
};
}
},
};

View File

@@ -22,6 +22,43 @@
"properties": {
"enabled": {
"type": "boolean"
},
"agents": {
"type": "object",
"propertyNames": {
"anyOf": [
{
"enum": [
"svelte-file-editor"
]
},
{
"type": "string"
}
]
},
"additionalProperties": {
"type": "object",
"properties": {
"model": {
"type": "string",
"description": "Model identifier for the agent (e.g., \"anthropic/claude-sonnet-4-20250514\")"
},
"temperature": {
"type": "number",
"description": "Temperature setting for the agent (e.g., 0.7)"
},
"top_p": {
"type": "number",
"description": "Control response diversity with the top_p option. Alternative to temperature for controlling randomness."
},
"maxSteps": {
"type": "number",
"description": "Maximum number of steps the agent can take (e.g., 10)"
}
},
"required": []
}
}
},
"required": [],

View File

@@ -3,6 +3,15 @@ import { config_schema } from '../config.js';
import fs from 'node:fs';
import path from 'node:path';
// Read agent names from tools/agents/*.md files
function get_agent_names(agents_dir: string) {
if (!fs.existsSync(agents_dir)) return [];
return fs
.readdirSync(agents_dir, { withFileTypes: true })
.filter((entry) => entry.isFile() && entry.name.endsWith('.md'))
.map((entry) => entry.name.replace(/\.md$/, ''));
}
function get_skill_names(skills_dir: string) {
if (!fs.existsSync(skills_dir)) return [];
return fs
@@ -32,4 +41,20 @@ if (skill_names.length > 0) {
}
}
// Post-process: inject known agent names for intellisense
// This is the JSON Schema equivalent of `"a" | "b" | (string & {})` —
// editors will autocomplete the known names but any string is still valid.
const agents_dir = path.resolve('../../tools/agents');
const agent_names = get_agent_names(agents_dir);
if (agent_names.length > 0) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const agents = (json_schema as any).properties?.subagent?.properties?.agents;
if (agents) {
agents.propertyNames = {
anyOf: [{ enum: agent_names }, { type: 'string' }],
};
}
}
fs.writeFileSync(path.resolve('./schema.json'), JSON.stringify(json_schema, null, '\t'));