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. **
|
|
75
|
-
4. **
|
|
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
|
-
|
|
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
|
-
|
|
|
207
|
-
|
|
|
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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.4.6
|