opencode-manifold 0.4.7 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -71,8 +71,9 @@ Open Manifold requires:
71
71
 
72
72
  1. **Install** using one of the methods above
73
73
  2. **Run `/manifold-init`** in the opencode TUI to set up agents, skills, and the Manifold directory
74
- 3. **Create a plan** any format (markdown, TODO list, email, meeting notes)
75
- 4. **Point the Lead Dev agent** at your plan file and tell it to execute
74
+ 3. **(Optional) Run `/manifold-models`** to configure which models each sub-agent uses
75
+ 4. **Create a plan** any format (markdown, TODO list, email, meeting notes)
76
+ 5. **Point the Lead Dev agent** at your plan file and tell it to execute
76
77
 
77
78
  The Lead Dev will:
78
79
  - Extract tasks from your plan
@@ -180,7 +181,13 @@ rm -rf .opencode/agents/ .opencode/skills/ Manifold/
180
181
 
181
182
  ### Assigning models to agents
182
183
 
183
- Add a `model` field to the agent's frontmatter or your `opencode.json`:
184
+ **Interactive (Recommended):** Run `/manifold-models` in the TUI. You'll be prompted to:
185
+ 1. Select a sub-agent (clerk, senior-dev, junior-dev, or debug)
186
+ 2. Choose from all available models in your configured providers
187
+
188
+ This updates the agent's frontmatter automatically.
189
+
190
+ **Manual:** Add a `model` field to the agent's frontmatter or your `opencode.json`:
184
191
 
185
192
  ```json
186
193
  {
@@ -203,8 +210,8 @@ See the [Example Configurations](#example-configurations) section for model pair
203
210
 
204
211
  | Budget | Senior Dev | Junior Dev | Debug |
205
212
  |--------|-----------|-----------|-------|
206
- | Cost-effective | MiniMax-2.7 | Qwen-3.5 122B-A10B | GLM 5.1 |
207
- | Balanced | GLM 5.1 | Minimax-2.7 | GPT 5.4 mini |
213
+ | Self Host | Qwen3.5 27B | Gemma 4 31B | GLM 4.7 Flash |
214
+ | Cost-effective | Qwen3.5 397B A17B | MiniMax-2.7 | GLM 5.1 |
208
215
  | Premium | Claude Opus | GPT 5.4 | Gemini 3.1 pro |
209
216
 
210
217
  Models change frequently. These examples prioritize sr/debug diversity and cost gradient.
package/dist/index.js CHANGED
@@ -4191,6 +4191,244 @@ ${testResult.result.output}`,
4191
4191
  }
4192
4192
  });
4193
4193
 
4194
+ // src/tools/set-subagent-model.ts
4195
+ import { readFile as readFile5, writeFile as writeFile5, readdir as readdir3 } from "fs/promises";
4196
+ import { existsSync as existsSync6 } from "fs";
4197
+ import { join as join6 } from "path";
4198
+ import { homedir as homedir2 } from "os";
4199
+ var MANIFOLD_AGENTS = ["clerk", "senior-dev", "junior-dev", "debug"];
4200
+ async function getManifoldAgents(directory) {
4201
+ const agentsDir = join6(directory, ".opencode", "agents");
4202
+ if (!existsSync6(agentsDir)) {
4203
+ return [];
4204
+ }
4205
+ const files = await readdir3(agentsDir);
4206
+ const agents = [];
4207
+ for (const file of files) {
4208
+ if (!file.endsWith(".md"))
4209
+ continue;
4210
+ const name = file.replace(".md", "");
4211
+ if (MANIFOLD_AGENTS.includes(name)) {
4212
+ agents.push(name);
4213
+ }
4214
+ }
4215
+ return agents;
4216
+ }
4217
+ async function getAvailableModels(client) {
4218
+ try {
4219
+ const result = await client.config.providers();
4220
+ const providers = result.data?.providers || [];
4221
+ const models = [];
4222
+ for (const provider of providers) {
4223
+ if (provider.models) {
4224
+ for (const [modelId, model] of Object.entries(provider.models)) {
4225
+ models.push({
4226
+ id: `${provider.id}/${modelId}`,
4227
+ name: model.name || modelId,
4228
+ providerID: provider.id
4229
+ });
4230
+ }
4231
+ }
4232
+ }
4233
+ return models.sort((a, b) => a.name.localeCompare(b.name));
4234
+ } catch (error) {
4235
+ await client.app.log({
4236
+ body: {
4237
+ service: "opencode-manifold",
4238
+ level: "error",
4239
+ message: `Error fetching models: ${error}`
4240
+ }
4241
+ });
4242
+ return [];
4243
+ }
4244
+ }
4245
+ async function readAgentFile(agentName, directory) {
4246
+ const agentPath = join6(directory, ".opencode", "agents", `${agentName}.md`);
4247
+ if (!existsSync6(agentPath)) {
4248
+ const globalPath = join6(homedir2(), ".config", "opencode", "manifold", "agents", `${agentName}.md`);
4249
+ if (existsSync6(globalPath)) {
4250
+ const content2 = await readFile5(globalPath, "utf-8");
4251
+ const { frontmatter: frontmatter2, body: body2 } = parseFrontmatter(content2);
4252
+ return { content: content2, frontmatter: frontmatter2, body: body2 };
4253
+ }
4254
+ throw new Error(`Agent file not found for ${agentName}`);
4255
+ }
4256
+ const content = await readFile5(agentPath, "utf-8");
4257
+ const { frontmatter, body } = parseFrontmatter(content);
4258
+ return { content, frontmatter, body };
4259
+ }
4260
+ function parseFrontmatter(content) {
4261
+ const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
4262
+ if (!match) {
4263
+ return { frontmatter: {}, body: content };
4264
+ }
4265
+ const frontmatterYaml = match[1];
4266
+ const body = match[2];
4267
+ const frontmatter = jsYaml.load(frontmatterYaml) || {};
4268
+ return { frontmatter, body };
4269
+ }
4270
+ function buildAgentFile(frontmatter, body) {
4271
+ const yamlContent = jsYaml.dump(frontmatter, {
4272
+ lineWidth: -1,
4273
+ noCompatMode: true
4274
+ });
4275
+ return `---
4276
+ ${yamlContent}---
4277
+ ${body}`;
4278
+ }
4279
+ async function updateAgentModel(client, agentName, modelId, directory) {
4280
+ const { frontmatter, body } = await readAgentFile(agentName, directory);
4281
+ frontmatter.model = modelId;
4282
+ const newContent = buildAgentFile(frontmatter, body);
4283
+ const agentPath = join6(directory, ".opencode", "agents", `${agentName}.md`);
4284
+ await writeFile5(agentPath, newContent);
4285
+ await client.app.log({
4286
+ body: {
4287
+ service: "opencode-manifold",
4288
+ level: "info",
4289
+ message: `Updated ${agentName} model to ${modelId}`
4290
+ }
4291
+ });
4292
+ }
4293
+ async function handleSubModelCommand(client, directory) {
4294
+ await client.app.log({
4295
+ body: {
4296
+ service: "opencode-manifold",
4297
+ level: "info",
4298
+ message: "Starting /sub-model command"
4299
+ }
4300
+ });
4301
+ const agents = await getManifoldAgents(directory);
4302
+ if (agents.length === 0) {
4303
+ await client.tui.showToast({
4304
+ body: {
4305
+ type: "error",
4306
+ message: "No Manifold agents found. Run /manifold-init first."
4307
+ }
4308
+ });
4309
+ return;
4310
+ }
4311
+ const models = await getAvailableModels(client);
4312
+ if (models.length === 0) {
4313
+ await client.tui.showToast({
4314
+ body: {
4315
+ type: "error",
4316
+ message: "No models available. Configure providers first."
4317
+ }
4318
+ });
4319
+ return;
4320
+ }
4321
+ const agentList = agents.map((a) => `${a} (sub-agent)`).join(`
4322
+ `);
4323
+ await client.tui.appendPrompt({
4324
+ body: {
4325
+ type: "text",
4326
+ text: `\uD83D\uDCCB Select a Manifold sub-agent to configure:
4327
+
4328
+ ${agentList}`
4329
+ }
4330
+ });
4331
+ await client.tui.publish({
4332
+ body: {
4333
+ type: "tui.prompt_append",
4334
+ data: {
4335
+ type: "text",
4336
+ text: `
4337
+
4338
+ Type the agent name (clerk, senior-dev, junior-dev, or debug): `
4339
+ }
4340
+ }
4341
+ });
4342
+ const agentResponse = await client.tui.control.next();
4343
+ const selectedAgent = agentResponse.data?.input?.trim().toLowerCase();
4344
+ if (!selectedAgent || !MANIFOLD_AGENTS.includes(selectedAgent)) {
4345
+ await client.tui.showToast({
4346
+ body: {
4347
+ type: "error",
4348
+ message: `Invalid agent selection. Must be one of: ${MANIFOLD_AGENTS.join(", ")}`
4349
+ }
4350
+ });
4351
+ return;
4352
+ }
4353
+ if (!agents.includes(selectedAgent)) {
4354
+ await client.tui.showToast({
4355
+ body: {
4356
+ type: "error",
4357
+ message: `Agent ${selectedAgent} not found in .opencode/agents/`
4358
+ }
4359
+ });
4360
+ return;
4361
+ }
4362
+ const currentModel = await readAgentFile(selectedAgent, directory).then((f) => f.frontmatter.model || "not set");
4363
+ const modelList = models.map((m, i2) => `${i2 + 1}. ${m.name} (${m.id})`).join(`
4364
+ `);
4365
+ await client.tui.appendPrompt({
4366
+ body: {
4367
+ type: "text",
4368
+ text: `
4369
+ \uD83D\uDCE6 Available models for ${selectedAgent} (current: ${currentModel}):
4370
+
4371
+ ${modelList}`
4372
+ }
4373
+ });
4374
+ await client.tui.publish({
4375
+ body: {
4376
+ type: "tui.prompt_append",
4377
+ data: {
4378
+ type: "text",
4379
+ text: `
4380
+
4381
+ Type the model number or full model ID (e.g., anthropic/claude-sonnet-4-20250514): `
4382
+ }
4383
+ }
4384
+ });
4385
+ const modelResponse = await client.tui.control.next();
4386
+ const selectedModelInput = modelResponse.data?.input?.trim();
4387
+ if (!selectedModelInput) {
4388
+ await client.tui.showToast({
4389
+ body: {
4390
+ type: "error",
4391
+ message: "No model selected"
4392
+ }
4393
+ });
4394
+ return;
4395
+ }
4396
+ let selectedModelId;
4397
+ const modelIndex = parseInt(selectedModelInput, 10) - 1;
4398
+ if (!isNaN(modelIndex) && modelIndex >= 0 && modelIndex < models.length) {
4399
+ selectedModelId = models[modelIndex].id;
4400
+ } else {
4401
+ const found = models.find((m) => m.id.toLowerCase() === selectedModelInput.toLowerCase());
4402
+ if (found) {
4403
+ selectedModelId = found.id;
4404
+ }
4405
+ }
4406
+ if (!selectedModelId) {
4407
+ await client.tui.showToast({
4408
+ body: {
4409
+ type: "error",
4410
+ message: `Invalid model selection: ${selectedModelInput}`
4411
+ }
4412
+ });
4413
+ return;
4414
+ }
4415
+ await updateAgentModel(client, selectedAgent, selectedModelId, directory);
4416
+ await client.tui.showToast({
4417
+ body: {
4418
+ type: "success",
4419
+ message: `✅ Set ${selectedAgent} to ${selectedModelId}`
4420
+ }
4421
+ });
4422
+ await client.tui.appendPrompt({
4423
+ body: {
4424
+ type: "text",
4425
+ text: `
4426
+ ✅ Model updated for ${selectedAgent}: ${selectedModelId}
4427
+ `
4428
+ }
4429
+ });
4430
+ }
4431
+
4194
4432
  // src/index.ts
4195
4433
  var ManifoldPlugin = async (ctx) => {
4196
4434
  setPluginContext(ctx.client);
@@ -4224,6 +4462,14 @@ var ManifoldPlugin = async (ctx) => {
4224
4462
  }
4225
4463
  ];
4226
4464
  }
4465
+ } else if (input.command === "manifold-models") {
4466
+ await handleSubModelCommand(ctx.client, ctx.directory);
4467
+ output.parts = [
4468
+ {
4469
+ type: "text",
4470
+ text: "Sub-agent model configuration complete."
4471
+ }
4472
+ ];
4227
4473
  }
4228
4474
  }
4229
4475
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-manifold",
3
- "version": "0.4.7",
3
+ "version": "0.4.8",
4
4
  "description": "Multi-agent development system for opencode with persistent knowledge",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -2,6 +2,7 @@
2
2
  description: Researches context, composes scoped prompts, and maintains the project wiki
3
3
  mode: subagent
4
4
  hidden: true
5
+ model: Kilo-Gateway/Gemini-3-Flash-Preview
5
6
  permission:
6
7
  edit:
7
8
  "*": deny
@@ -0,0 +1,4 @@
1
+ ---
2
+ description: Set the model for a Manifold sub-agent (clerk, senior-dev, junior-dev, debug)
3
+ ---
4
+ The /manifold-models command is handled by the plugin. It will prompt you to select a sub-agent and then a model.