open-xmen 0.1.0

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.
Files changed (60) hide show
  1. package/.cerebro/.gitignore +27 -0
  2. package/.cerebro/cerebro-identity.md +76 -0
  3. package/.cerebro/docs/agent-mapping.md +54 -0
  4. package/.cerebro/docs/cerebro-workflow.md +115 -0
  5. package/.cerebro/docs/orchestration.md +28 -0
  6. package/.cerebro/docs/overview.md +44 -0
  7. package/.cerebro/docs/skill-policy.md +25 -0
  8. package/.cerebro/integrations/semble.md +30 -0
  9. package/.cerebro/opencode/model-routing.md +37 -0
  10. package/.cerebro/schemas/boulder.schema.json +143 -0
  11. package/.cerebro/schemas/team-run.schema.json +234 -0
  12. package/.cerebro/schemas/upgrade-manifest.schema.json +45 -0
  13. package/.cerebro/schemas/upgrade-state.schema.json +38 -0
  14. package/.cerebro/scripts/check-agent-teams-enabled.py +24 -0
  15. package/.cerebro/scripts/ensure-upgrade-cache-gitignored.py +27 -0
  16. package/.cerebro/scripts/fetch-upstream-ref.py +67 -0
  17. package/.cerebro/scripts/reset-runtime.py +125 -0
  18. package/.cerebro/scripts/setup-status.py +101 -0
  19. package/.cerebro/scripts/test-stop-hook.py +60 -0
  20. package/.cerebro/scripts/upgrade-latest-tag.py +34 -0
  21. package/.cerebro/scripts/validate-agent-frontmatter.py +87 -0
  22. package/.cerebro/scripts/validate-boulder.py +105 -0
  23. package/.cerebro/scripts/validate-opencode-runtime.py +94 -0
  24. package/.cerebro/scripts/validate-team-runs.py +310 -0
  25. package/.cerebro/scripts/validate-upgrade-metadata.py +104 -0
  26. package/.cerebro/scripts/write-upgrade-state.py +93 -0
  27. package/.cerebro/templates/customer-vision.md +58 -0
  28. package/.cerebro/templates/plan.md +35 -0
  29. package/.cerebro/templates/product-brief.md +110 -0
  30. package/.cerebro/templates/project-context.md +64 -0
  31. package/.cerebro/templates/requirements-brief.md +67 -0
  32. package/.cerebro/templates/team-run.json +22 -0
  33. package/.cerebro/upgrade-manifest.json +160 -0
  34. package/.opencode/.gitignore +5 -0
  35. package/.opencode/agents/beast.md +38 -0
  36. package/.opencode/agents/cerebro.md +22 -0
  37. package/.opencode/agents/cyclops.md +22 -0
  38. package/.opencode/agents/cypher.md +46 -0
  39. package/.opencode/agents/emma-frost.md +38 -0
  40. package/.opencode/agents/forge.md +22 -0
  41. package/.opencode/agents/legion.md +45 -0
  42. package/.opencode/agents/nightcrawler.md +22 -0
  43. package/.opencode/agents/professor-x.md +39 -0
  44. package/.opencode/agents/sage.md +22 -0
  45. package/.opencode/agents/storm.md +49 -0
  46. package/.opencode/agents/wolverine.md +22 -0
  47. package/.opencode/commands/cerebro-doctor.md +19 -0
  48. package/.opencode/commands/cerebro-index.md +22 -0
  49. package/.opencode/commands/cerebro-plan.md +21 -0
  50. package/.opencode/commands/cerebro-reset.md +20 -0
  51. package/.opencode/commands/cerebro-start-work.md +20 -0
  52. package/.opencode/commands/cerebro-upgrade.md +19 -0
  53. package/.opencode/commands/to-me-my-x-men.md +27 -0
  54. package/AGENTS.md +12 -0
  55. package/README.md +193 -0
  56. package/dist/cli.d.ts +2 -0
  57. package/dist/cli.js +597 -0
  58. package/dist/index.d.ts +4 -0
  59. package/dist/index.js +466 -0
  60. package/package.json +54 -0
package/dist/index.js ADDED
@@ -0,0 +1,466 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ import { appendFile, lstat, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
3
+ import { existsSync } from "node:fs";
4
+ import path from "node:path";
5
+ const DEFAULT_MODEL_SLOTS = {
6
+ frontier: "openai/gpt-5.4",
7
+ strong: "openai/gpt-5.4",
8
+ coding: "openai/gpt-5.3-codex",
9
+ spark: "openai/gpt-5.3-codex-spark",
10
+ fast: "openai/gpt-5.4-mini",
11
+ image: "openai/gpt-image-2",
12
+ };
13
+ const COMMANDS = new Set([
14
+ "/to-me-my-x-men",
15
+ "/cerebro-plan",
16
+ "/cerebro-start-work",
17
+ "/cerebro-index",
18
+ "/cerebro-doctor",
19
+ "/cerebro-reset",
20
+ "/cerebro-upgrade",
21
+ ]);
22
+ const RISKS = ["LOW", "MEDIUM", "HIGH"];
23
+ const TASK_STATUSES = ["pending", "active", "blocked", "done", "verified", "failed"];
24
+ const CEREBRO_AGENTS = ["cerebro", "legion", "cypher", "professor-x", "cyclops", "wolverine", "storm", "forge", "nightcrawler", "sage", "beast", "emma-frost"];
25
+ const SAFE_NAME_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/i;
26
+ const SECRET_PATH_PATTERN = /(^|[/\\])\.env(\.|$|[/\\])|secret|credential|token|private[-_]?key/i;
27
+ const MAX_PENDING_FILES = 500;
28
+ const MAX_PENDING_FILE_BYTES = 64 * 1024;
29
+ function now() {
30
+ return new Date().toISOString().replace("+00:00", "Z");
31
+ }
32
+ function slug(input) {
33
+ return input
34
+ .toLowerCase()
35
+ .replace(/[^a-z0-9]+/g, "-")
36
+ .replace(/^-+|-+$/g, "")
37
+ .slice(0, 48) || "cerebro-run";
38
+ }
39
+ function runtimeRoot(ctx) {
40
+ return path.join(ctx.worktree || ctx.directory, ".cerebro");
41
+ }
42
+ function safeRuntimePath(ctx, relativePath) {
43
+ const root = runtimeRoot(ctx);
44
+ const full = path.resolve(root, relativePath);
45
+ const normalizedRoot = path.resolve(root) + path.sep;
46
+ if (full !== path.resolve(root) && !full.startsWith(normalizedRoot)) {
47
+ throw new Error(`Path escapes .cerebro runtime: ${relativePath}`);
48
+ }
49
+ return full;
50
+ }
51
+ async function readJson(file, fallback) {
52
+ if (!existsSync(file))
53
+ return fallback;
54
+ return JSON.parse(await readFile(file, "utf8"));
55
+ }
56
+ async function writeJson(file, data) {
57
+ await mkdir(path.dirname(file), { recursive: true });
58
+ await writeFile(file, `${JSON.stringify(data, null, 2)}\n`, "utf8");
59
+ }
60
+ async function appendJsonl(file, data) {
61
+ await mkdir(path.dirname(file), { recursive: true });
62
+ await appendFile(file, `${JSON.stringify(data)}\n`, "utf8");
63
+ }
64
+ function modelSlots() {
65
+ return {
66
+ frontier: process.env.CEREBRO_MODEL_FRONTIER || DEFAULT_MODEL_SLOTS.frontier,
67
+ strong: process.env.CEREBRO_MODEL_STRONG || DEFAULT_MODEL_SLOTS.strong,
68
+ coding: process.env.CEREBRO_MODEL_CODING || DEFAULT_MODEL_SLOTS.coding,
69
+ spark: process.env.CEREBRO_MODEL_SPARK || DEFAULT_MODEL_SLOTS.spark,
70
+ fast: process.env.CEREBRO_MODEL_FAST || DEFAULT_MODEL_SLOTS.fast,
71
+ image: process.env.CEREBRO_MODEL_IMAGE || DEFAULT_MODEL_SLOTS.image,
72
+ };
73
+ }
74
+ function parseModelID(model) {
75
+ const [providerID, ...rest] = model.split("/");
76
+ const modelID = rest.join("/");
77
+ if (!providerID || !modelID)
78
+ throw new Error(`Invalid model id: ${model}`);
79
+ return { providerID, modelID };
80
+ }
81
+ function isRecord(value) {
82
+ return typeof value === "object" && value !== null;
83
+ }
84
+ function assertSafeName(value, label) {
85
+ if (!SAFE_NAME_PATTERN.test(value))
86
+ throw new Error(`${label} must be a safe slug-like name`);
87
+ }
88
+ function assertSafeAgent(agent) {
89
+ if (!CEREBRO_AGENTS.includes(agent))
90
+ throw new Error(`Unsupported Cerebro agent: ${agent}`);
91
+ }
92
+ function readToolArgs(args) {
93
+ return isRecord(args) ? args : {};
94
+ }
95
+ function isSecretPath(filePath) {
96
+ return SECRET_PATH_PATTERN.test(filePath);
97
+ }
98
+ function isInside(parent, child) {
99
+ const relative = path.relative(parent, child);
100
+ return relative === "" || (!!relative && !relative.startsWith("..") && !path.isAbsolute(relative));
101
+ }
102
+ async function scanPendingTodos(root, teamName) {
103
+ const normalizedRoot = path.resolve(root);
104
+ const base = teamName ? path.resolve(normalizedRoot, teamName) : normalizedRoot;
105
+ if (!isInside(normalizedRoot, base))
106
+ throw new Error("pending todo path escapes .cerebro/pending-todos");
107
+ const items = [];
108
+ let filesSeen = 0;
109
+ async function walk(current) {
110
+ if (!isInside(normalizedRoot, current))
111
+ throw new Error("pending todo scan escaped .cerebro/pending-todos");
112
+ if (isSecretPath(current))
113
+ throw new Error("Cerebro safety policy blocks pending todo scans of secret-like paths");
114
+ if (!existsSync(current))
115
+ return;
116
+ const stats = await lstat(current);
117
+ if (stats.isSymbolicLink())
118
+ return;
119
+ if (stats.isDirectory()) {
120
+ for (const name of await readdir(current))
121
+ await walk(path.join(current, name));
122
+ return;
123
+ }
124
+ if (!stats.isFile())
125
+ return;
126
+ filesSeen += 1;
127
+ if (filesSeen > MAX_PENDING_FILES)
128
+ throw new Error(`pending todo scan exceeded ${MAX_PENDING_FILES} files`);
129
+ if (stats.size > MAX_PENDING_FILE_BYTES)
130
+ throw new Error(`pending todo file is too large: ${current}`);
131
+ for (const line of (await readFile(current, "utf8")).split("\n")) {
132
+ const trimmed = line.trim();
133
+ if (trimmed)
134
+ items.push({ file: current, line: trimmed });
135
+ }
136
+ }
137
+ await walk(base);
138
+ return items;
139
+ }
140
+ function hasChildSessionClient(client) {
141
+ if (!isRecord(client))
142
+ return false;
143
+ const session = client.session;
144
+ if (!isRecord(session))
145
+ return false;
146
+ return typeof session.create === "function" && typeof session.promptAsync === "function";
147
+ }
148
+ function childSessionID(result) {
149
+ if (!isRecord(result))
150
+ return undefined;
151
+ const data = result.data;
152
+ if (isRecord(data) && typeof data.id === "string")
153
+ return data.id;
154
+ return typeof result.id === "string" ? result.id : undefined;
155
+ }
156
+ function taskFile(ctx, runId) {
157
+ return safeRuntimePath(ctx, `team-runs/${runId}.tasks.json`);
158
+ }
159
+ function manifestFile(ctx, runId) {
160
+ return safeRuntimePath(ctx, `team-runs/${runId}.json`);
161
+ }
162
+ async function loadTasks(ctx, runId) {
163
+ return readJson(taskFile(ctx, runId), []);
164
+ }
165
+ async function saveTasks(ctx, runId, tasks) {
166
+ await writeJson(taskFile(ctx, runId), tasks);
167
+ }
168
+ export const CerebroPlugin = async ({ worktree, directory, client }) => {
169
+ const ctx = { worktree, directory };
170
+ return {
171
+ async event({ event }) {
172
+ if (!event || typeof event !== "object")
173
+ return;
174
+ const eventType = "type" in event ? String(event.type) : "unknown";
175
+ if (!eventType.startsWith("session.") && !eventType.startsWith("command.") && !eventType.startsWith("todo."))
176
+ return;
177
+ await appendJsonl(safeRuntimePath(ctx, "team-runs/events.jsonl"), {
178
+ logged_at: now(),
179
+ event: eventType,
180
+ payload: event,
181
+ });
182
+ },
183
+ async "command.execute.before"(input, output) {
184
+ const command = input.command.startsWith("/") ? input.command : `/${input.command}`;
185
+ if (!COMMANDS.has(command))
186
+ return;
187
+ output.parts.unshift({
188
+ id: `cerebro-command-${input.sessionID}`,
189
+ sessionID: input.sessionID,
190
+ messageID: `cerebro-command-${input.command}`,
191
+ type: "text",
192
+ text: [
193
+ "Cerebro OpenCode runtime is active.",
194
+ "Before acting, use the Cerebro coordination tools for run state, mailbox, checkpoints, pending-todo checks, and model-slot lookup.",
195
+ "Runtime root: `.cerebro/`. Preserve command names and role names.",
196
+ ].join("\n"),
197
+ synthetic: true,
198
+ });
199
+ },
200
+ async "tool.execute.before"(_input, output) {
201
+ const args = readToolArgs(output.args);
202
+ const candidate = String(args.filePath || args.path || args.command || "");
203
+ if (isSecretPath(candidate)) {
204
+ throw new Error("Cerebro safety policy blocks reading or touching env/secret/credential paths without explicit user authorization.");
205
+ }
206
+ },
207
+ async "experimental.session.compacting"(_input, output) {
208
+ output.context.push("Preserve Cerebro state before compacting: summarize active command, run_id, boulder status, pending approvals, pending todos, verification evidence, and next checkpoint under `.cerebro/`.");
209
+ },
210
+ tool: {
211
+ cerebro_model_slots: tool({
212
+ description: "Return the configured Cerebro OpenAI model slots for role routing.",
213
+ args: {},
214
+ async execute() {
215
+ return JSON.stringify(modelSlots(), null, 2);
216
+ },
217
+ }),
218
+ cerebro_run_start: tool({
219
+ description: "Create or initialize a Cerebro run manifest and boulder checkpoint under .cerebro.",
220
+ args: {
221
+ command: tool.schema.enum(Array.from(COMMANDS)).describe("Cerebro command name"),
222
+ objective: tool.schema.string().min(1).describe("User objective for this run"),
223
+ risk_level: tool.schema.enum(RISKS).describe("Risk level"),
224
+ team_name: tool.schema.string().optional().describe("Optional stable team/session name"),
225
+ },
226
+ async execute(args) {
227
+ const timestamp = now();
228
+ const runId = `${timestamp.slice(0, 10).replace(/-/g, "")}-${timestamp.slice(11, 19).replace(/:/g, "")}-${slug(args.objective)}`;
229
+ const teamName = args.team_name || slug(`${args.command}-${args.objective}`);
230
+ const manifest = {
231
+ version: 1,
232
+ run_id: runId,
233
+ command: args.command,
234
+ status: args.command === "/cerebro-plan" ? "planning" : "running",
235
+ lead: "cerebro",
236
+ team_name: teamName,
237
+ objective: args.objective,
238
+ risk_level: args.risk_level,
239
+ started_at: timestamp,
240
+ updated_at: timestamp,
241
+ teammates: [],
242
+ ownership: [],
243
+ mailbox_decisions: [],
244
+ approvals: [],
245
+ verification: [],
246
+ cleanup: {
247
+ team_stopped: false,
248
+ pending_todos_clear: false,
249
+ notes: "OpenCode-managed Cerebro run",
250
+ },
251
+ };
252
+ await writeJson(manifestFile(ctx, runId), manifest);
253
+ await saveTasks(ctx, runId, []);
254
+ if (["/to-me-my-x-men", "/cerebro-start-work"].includes(args.command)) {
255
+ await writeJson(safeRuntimePath(ctx, "boulder.json"), {
256
+ version: 2,
257
+ active_plan: "",
258
+ plan_name: slug(args.objective),
259
+ status: "in_progress",
260
+ risk_level: args.risk_level,
261
+ team_name: teamName,
262
+ started_at: timestamp,
263
+ updated_at: timestamp,
264
+ approval_gates: [],
265
+ verification_history: [],
266
+ decisions: [],
267
+ });
268
+ }
269
+ return JSON.stringify({ run_id: runId, manifest: `.cerebro/team-runs/${runId}.json`, tasks: `.cerebro/team-runs/${runId}.tasks.json` }, null, 2);
270
+ },
271
+ }),
272
+ cerebro_task_create: tool({
273
+ description: "Create a task record for an OpenCode-managed Cerebro run.",
274
+ args: {
275
+ run_id: tool.schema.string().min(1),
276
+ subject: tool.schema.string().min(1),
277
+ description: tool.schema.string().min(1),
278
+ owner: tool.schema.string().min(1).describe("Cerebro role or spawned agent name"),
279
+ depends_on: tool.schema.array(tool.schema.string()).optional(),
280
+ },
281
+ async execute(args) {
282
+ const tasks = await loadTasks(ctx, args.run_id);
283
+ const id = `task-${String(tasks.length + 1).padStart(3, "0")}`;
284
+ const record = {
285
+ id,
286
+ subject: args.subject,
287
+ description: args.description,
288
+ owner: args.owner,
289
+ status: "pending",
290
+ depends_on: args.depends_on || [],
291
+ created_at: now(),
292
+ updated_at: now(),
293
+ notes: [],
294
+ verification: [],
295
+ };
296
+ tasks.push(record);
297
+ await saveTasks(ctx, args.run_id, tasks);
298
+ return JSON.stringify(record, null, 2);
299
+ },
300
+ }),
301
+ cerebro_task_list: tool({
302
+ description: "List task records for an OpenCode-managed Cerebro run.",
303
+ args: { run_id: tool.schema.string().min(1) },
304
+ async execute(args) {
305
+ return JSON.stringify(await loadTasks(ctx, args.run_id), null, 2);
306
+ },
307
+ }),
308
+ cerebro_task_update: tool({
309
+ description: "Update task status, notes, or verification for a Cerebro run.",
310
+ args: {
311
+ run_id: tool.schema.string().min(1),
312
+ task_id: tool.schema.string().min(1),
313
+ status: tool.schema.enum(TASK_STATUSES).optional(),
314
+ note: tool.schema.string().optional(),
315
+ verification_result: tool.schema.enum(["PASS", "FAIL", "BLOCKED", "NOT RUN"]).optional(),
316
+ verification_command: tool.schema.string().optional(),
317
+ },
318
+ async execute(args) {
319
+ const tasks = await loadTasks(ctx, args.run_id);
320
+ const taskRecord = tasks.find((task) => task.id === args.task_id);
321
+ if (!taskRecord)
322
+ throw new Error(`Unknown task ${args.task_id}`);
323
+ if (args.status)
324
+ taskRecord.status = args.status;
325
+ if (args.note)
326
+ taskRecord.notes.push(`${now()} ${args.note}`);
327
+ if (args.verification_result) {
328
+ taskRecord.verification.push({
329
+ at: now(),
330
+ result: args.verification_result,
331
+ command: args.verification_command,
332
+ notes: args.note,
333
+ });
334
+ }
335
+ taskRecord.updated_at = now();
336
+ await saveTasks(ctx, args.run_id, tasks);
337
+ return JSON.stringify(taskRecord, null, 2);
338
+ },
339
+ }),
340
+ cerebro_mailbox_send: tool({
341
+ description: "Append a mailbox message for a Cerebro run and optionally record decisions in the manifest.",
342
+ args: {
343
+ run_id: tool.schema.string().min(1),
344
+ from: tool.schema.string().min(1),
345
+ to: tool.schema.string().min(1),
346
+ type: tool.schema.string().min(1),
347
+ body: tool.schema.string().min(1),
348
+ decision: tool.schema.string().optional(),
349
+ },
350
+ async execute(args) {
351
+ const record = { at: now(), ...args };
352
+ await appendJsonl(safeRuntimePath(ctx, `team-runs/${args.run_id}.mailbox.jsonl`), record);
353
+ if (args.decision) {
354
+ const file = manifestFile(ctx, args.run_id);
355
+ const manifest = await readJson(file, {});
356
+ manifest.mailbox_decisions ||= [];
357
+ manifest.mailbox_decisions.push({ at: record.at, from: args.from, to: args.to, decision: args.decision, notes: args.body });
358
+ manifest.updated_at = now();
359
+ await writeJson(file, manifest);
360
+ }
361
+ return JSON.stringify(record, null, 2);
362
+ },
363
+ }),
364
+ cerebro_mailbox_read: tool({
365
+ description: "Read mailbox messages for a Cerebro run, optionally filtered by recipient.",
366
+ args: {
367
+ run_id: tool.schema.string().min(1),
368
+ to: tool.schema.string().optional(),
369
+ limit: tool.schema.number().int().min(1).max(200).optional(),
370
+ },
371
+ async execute(args) {
372
+ const file = safeRuntimePath(ctx, `team-runs/${args.run_id}.mailbox.jsonl`);
373
+ if (!existsSync(file))
374
+ return "[]";
375
+ const lines = (await readFile(file, "utf8")).split("\n");
376
+ const records = lines
377
+ .filter(Boolean)
378
+ .map((line) => JSON.parse(line))
379
+ .filter((record) => !args.to || record.to === args.to)
380
+ .slice(-(args.limit || 50));
381
+ return JSON.stringify(records, null, 2);
382
+ },
383
+ }),
384
+ cerebro_dispatch_agent: tool({
385
+ description: "Dispatch an OpenCode child session/subagent for Cerebro work and record the dispatch intent. Returns child session metadata when the SDK supports it.",
386
+ args: {
387
+ run_id: tool.schema.string().min(1),
388
+ agent: tool.schema.enum(CEREBRO_AGENTS).describe("OpenCode/Cerebro agent name, e.g. nightcrawler, wolverine, professor-x"),
389
+ description: tool.schema.string().min(1),
390
+ prompt: tool.schema.string().min(1),
391
+ model_slot: tool.schema.enum(["frontier", "strong", "coding", "spark", "fast", "image"]).optional().describe("Optional Cerebro model slot override for this dispatch"),
392
+ no_reply: tool.schema.boolean().optional(),
393
+ },
394
+ async execute(args, toolContext) {
395
+ assertSafeAgent(args.agent);
396
+ const dispatchRecord = { at: now(), type: "dispatch", from: "cerebro", to: args.agent, description: args.description };
397
+ await appendJsonl(safeRuntimePath(ctx, `team-runs/${args.run_id}.mailbox.jsonl`), dispatchRecord);
398
+ try {
399
+ if (!hasChildSessionClient(client))
400
+ throw new Error("OpenCode SDK client does not expose child session create/promptAsync methods");
401
+ const created = await client.session.create({
402
+ body: { parentID: toolContext.sessionID, title: `${args.agent}: ${args.description}` },
403
+ query: { directory: toolContext.directory },
404
+ });
405
+ const childID = childSessionID(created);
406
+ if (!childID)
407
+ throw new Error("OpenCode SDK did not return a child session id");
408
+ const slots = modelSlots();
409
+ const selectedModel = args.model_slot ? slots[args.model_slot] : undefined;
410
+ await client.session.promptAsync({
411
+ path: { id: childID },
412
+ query: { directory: toolContext.directory },
413
+ body: {
414
+ agent: args.agent,
415
+ ...(selectedModel ? { model: parseModelID(selectedModel) } : {}),
416
+ noReply: args.no_reply ?? true,
417
+ parts: [{ type: "text", text: args.prompt }],
418
+ },
419
+ });
420
+ return JSON.stringify({ dispatched: true, child_session_id: childID, agent: args.agent, model: selectedModel || "agent-default" }, null, 2);
421
+ }
422
+ catch (error) {
423
+ return JSON.stringify({
424
+ dispatched: false,
425
+ agent: args.agent,
426
+ fallback: `Use @${args.agent} with the prompt supplied to cerebro_dispatch_agent.`,
427
+ error: error instanceof Error ? error.message : String(error),
428
+ }, null, 2);
429
+ }
430
+ },
431
+ }),
432
+ cerebro_checkpoint: tool({
433
+ description: "Write a durable Cerebro checkpoint for compaction/resume.",
434
+ args: {
435
+ run_id: tool.schema.string().min(1),
436
+ summary: tool.schema.string().min(1),
437
+ next: tool.schema.string().optional(),
438
+ verification: tool.schema.string().optional(),
439
+ },
440
+ async execute(args) {
441
+ const record = { at: now(), ...args };
442
+ await appendJsonl(safeRuntimePath(ctx, `team-runs/${args.run_id}.checkpoints.jsonl`), record);
443
+ return JSON.stringify(record, null, 2);
444
+ },
445
+ }),
446
+ cerebro_verify_pending: tool({
447
+ description: "Scan .cerebro pending todo files and report whether final response is allowed.",
448
+ args: { team_name: tool.schema.string().optional() },
449
+ async execute(args) {
450
+ const pendingRoot = safeRuntimePath(ctx, "pending-todos");
451
+ const legacyPendingRoot = safeRuntimePath(ctx, ".pending-todos");
452
+ const teamName = args.team_name?.trim();
453
+ if (teamName)
454
+ assertSafeName(teamName, "team_name");
455
+ const items = [
456
+ ...(await scanPendingTodos(pendingRoot, teamName)),
457
+ ...(await scanPendingTodos(legacyPendingRoot, teamName)),
458
+ ];
459
+ return [items.length ? "BLOCKED" : "CLEAR", ...items.map((item) => `- ${item.line} (${item.file})`)].join("\n");
460
+ },
461
+ }),
462
+ },
463
+ };
464
+ };
465
+ export const server = CerebroPlugin;
466
+ export default CerebroPlugin;
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "open-xmen",
3
+ "version": "0.1.0",
4
+ "description": "Cerebro/X-Men orchestration workflow for OpenCode.",
5
+ "type": "module",
6
+ "bin": {
7
+ "open-xmen": "dist/cli.js"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "types": "dist/index.d.ts",
12
+ "import": "dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ ".opencode/.gitignore",
18
+ ".opencode/agents",
19
+ ".opencode/commands",
20
+ ".cerebro/.gitignore",
21
+ ".cerebro/cerebro-identity.md",
22
+ ".cerebro/docs",
23
+ ".cerebro/integrations",
24
+ ".cerebro/opencode",
25
+ ".cerebro/schemas",
26
+ ".cerebro/scripts",
27
+ ".cerebro/templates",
28
+ ".cerebro/upgrade-manifest.json",
29
+ "AGENTS.md",
30
+ "README.md"
31
+ ],
32
+ "scripts": {
33
+ "build": "tsc -p tsconfig.json",
34
+ "doctor": "node dist/cli.js doctor",
35
+ "validate": "npm run build && node dist/cli.js doctor && npm run verify:release",
36
+ "prepare": "npm run build",
37
+ "prepublishOnly": "npm run validate",
38
+ "pack:check": "npm pack --dry-run",
39
+ "verify:release": "bun scripts/verify-release-artifact.ts"
40
+ },
41
+ "dependencies": {
42
+ "@opencode-ai/plugin": "^1.4.7",
43
+ "zod": "^4.1.8"
44
+ },
45
+ "devDependencies": {
46
+ "@types/node": "^22.13.9",
47
+ "typescript": "^5.8.2"
48
+ },
49
+ "engines": {
50
+ "node": ">=22"
51
+ },
52
+ "main": "dist/index.js",
53
+ "types": "dist/index.d.ts"
54
+ }