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 +12 -5
- package/dist/index.js +246 -0
- package/package.json +1 -1
- package/src/templates/agents/clerk.md +1 -0
- package/src/templates/commands/manifold-models.md +4 -0
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.
|
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