opencode-manifold 0.4.6 → 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.
@@ -306,6 +313,28 @@ rm -rf ~/.config/opencode/manifold/ ~/.config/opencode/commands/manifold-init.md
306
313
 
307
314
  To fully uninstall the plugin, also remove `"opencode-manifold"` from the `plugin` array in your global or project `opencode.json`.
308
315
 
316
+ ### Complete Clean Uninstall
317
+ # Mac:
318
+ OpenCode caches plugins in multiple locations. To fully clear all cached versions before reinstalling:
319
+
320
+ ```bash
321
+ # Remove plugin from all cache locations
322
+ rm -rf ~/node_modules/opencode-manifold
323
+ rm -rf ~/.cache/opencode/packages/opencode-manifold@latest
324
+ rm -rf ~/.config/opencode/manifold/
325
+ rm -rf ~/.config/opencode/commands/manifold-init.md
326
+
327
+ # Then restart opencode and run /manifold-init
328
+ ```
329
+
330
+ This ensures you're running the latest version after updates.
331
+
332
+ # Win:
333
+ I have no clue. Use linux.
334
+
335
+ # Linux:
336
+ Still have no clue but I know you can handle it.
337
+
309
338
  ---
310
339
 
311
340
  ## License
package/dist/index.js CHANGED
@@ -4,9 +4,19 @@ import { existsSync } from "fs";
4
4
  import { join, dirname } from "path";
5
5
  import { fileURLToPath } from "url";
6
6
  import { homedir } from "os";
7
+ import { createRequire } from "module";
7
8
  var __dirname2 = dirname(fileURLToPath(import.meta.url));
9
+ var require2 = createRequire(import.meta.url);
8
10
  var bundledTemplatesDir = join(__dirname2, "..", "src", "templates");
9
11
  var globalTemplatesDir = join(homedir(), ".config", "opencode", "manifold");
12
+ async function getPluginVersion() {
13
+ try {
14
+ const packageJson = require2(join(__dirname2, "..", "package.json"));
15
+ return packageJson.version || "unknown";
16
+ } catch {
17
+ return "unknown";
18
+ }
19
+ }
10
20
  async function dirHasContent(dirPath) {
11
21
  if (!existsSync(dirPath))
12
22
  return false;
@@ -101,6 +111,9 @@ async function initProject(directory, client) {
101
111
  if (manifoldCopied.length > 0) {
102
112
  initialized.push(`Manifold/ (${manifoldCopied.join(", ")})`);
103
113
  }
114
+ const version = await getPluginVersion();
115
+ await writeFile(join(directory, "Manifold", "VERSION"), version + `
116
+ `);
104
117
  await client.app.log({
105
118
  body: {
106
119
  service: "opencode-manifold",
@@ -4178,6 +4191,244 @@ ${testResult.result.output}`,
4178
4191
  }
4179
4192
  });
4180
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
+
4181
4432
  // src/index.ts
4182
4433
  var ManifoldPlugin = async (ctx) => {
4183
4434
  setPluginContext(ctx.client);
@@ -4211,6 +4462,14 @@ var ManifoldPlugin = async (ctx) => {
4211
4462
  }
4212
4463
  ];
4213
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
+ ];
4214
4473
  }
4215
4474
  }
4216
4475
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-manifold",
3
- "version": "0.4.6",
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
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  description: Orchestrates development by reading plans and dispatching tasks
3
3
  mode: primary
4
+ color: #6024bf
4
5
  permission:
5
6
  skill:
6
7
  manifold-workflow: allow
@@ -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.
@@ -0,0 +1 @@
1
+ 0.4.6