opencode-manifold 0.4.12 → 0.4.13

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 (2) hide show
  1. package/dist/index.js +818 -560
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,346 +1,11 @@
1
1
  import { createRequire } from "node:module";
2
2
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
3
 
4
- // src/init.ts
5
- import { readFile, writeFile, mkdir, readdir } from "fs/promises";
6
- import { existsSync } from "fs";
7
- import { join, dirname } from "path";
8
- import { fileURLToPath } from "url";
9
- import { homedir } from "os";
10
- import { createRequire as createRequire2 } from "module";
11
- var __dirname2 = dirname(fileURLToPath(import.meta.url));
12
- var require2 = createRequire2(import.meta.url);
13
- var bundledTemplatesDir = join(__dirname2, "..", "src", "templates");
14
- var globalTemplatesDir = join(homedir(), ".config", "opencode", "manifold");
15
- async function getPluginVersion() {
16
- try {
17
- const packageJson = require2(join(__dirname2, "..", "package.json"));
18
- return packageJson.version || "unknown";
19
- } catch {
20
- return "unknown";
21
- }
22
- }
23
- async function dirHasContent(dirPath) {
24
- if (!existsSync(dirPath))
25
- return false;
26
- try {
27
- const entries = await readdir(dirPath);
28
- return entries.length > 0;
29
- } catch {
30
- return false;
31
- }
32
- }
33
- async function copyMissingFiles(src, dest) {
34
- if (!existsSync(src))
35
- return [];
36
- await mkdir(dest, { recursive: true });
37
- const copied = [];
38
- const entries = await readdir(src, { withFileTypes: true });
39
- for (const entry of entries) {
40
- const srcPath = join(src, entry.name);
41
- const destPath = join(dest, entry.name);
42
- if (entry.isDirectory()) {
43
- const subCopied = await copyMissingFiles(srcPath, destPath);
44
- if (subCopied.length > 0) {
45
- copied.push(entry.name);
46
- }
47
- } else if (!existsSync(destPath)) {
48
- await writeFile(destPath, await readFile(srcPath));
49
- copied.push(entry.name);
50
- }
51
- }
52
- return copied;
53
- }
54
- async function ensureGlobalTemplates(ctx) {
55
- await ctx.client.app.log({
56
- body: {
57
- service: "opencode-manifold",
58
- level: "info",
59
- message: "Checking global templates..."
60
- }
61
- });
62
- if (!existsSync(bundledTemplatesDir)) {
63
- await ctx.client.app.log({
64
- body: {
65
- service: "opencode-manifold",
66
- level: "error",
67
- message: `Bundled templates not found at ${bundledTemplatesDir}`
68
- }
69
- });
70
- return;
71
- }
72
- await copyMissingFiles(bundledTemplatesDir, globalTemplatesDir);
73
- const globalCommandsDir = join(homedir(), ".config", "opencode", "commands");
74
- const bundledCommandsDir = join(bundledTemplatesDir, "commands");
75
- if (existsSync(bundledCommandsDir)) {
76
- await copyMissingFiles(bundledCommandsDir, globalCommandsDir);
77
- }
78
- await ctx.client.app.log({
79
- body: {
80
- service: "opencode-manifold",
81
- level: "info",
82
- message: "Global templates ready"
83
- }
84
- });
85
- }
86
- async function initProject(directory, client) {
87
- const initialized = [];
88
- await client.app.log({
89
- body: {
90
- service: "opencode-manifold",
91
- level: "info",
92
- message: `Running /manifold-init in ${directory}`
93
- }
94
- });
95
- if (!await dirHasContent(globalTemplatesDir)) {
96
- await client.app.log({
97
- body: {
98
- service: "opencode-manifold",
99
- level: "error",
100
- message: `Global templates not found at ${globalTemplatesDir}. Plugin may not have loaded correctly.`
101
- }
102
- });
103
- return initialized;
104
- }
105
- const agentsCopied = await copyMissingFiles(join(globalTemplatesDir, "agents"), join(directory, ".opencode", "agents"));
106
- if (agentsCopied.length > 0) {
107
- initialized.push(`agents (${agentsCopied.join(", ")})`);
108
- }
109
- const skillsCopied = await copyMissingFiles(join(globalTemplatesDir, "skills"), join(directory, ".opencode", "skills"));
110
- if (skillsCopied.length > 0) {
111
- initialized.push(`skills (${skillsCopied.join(", ")})`);
112
- }
113
- const manifoldCopied = await copyMissingFiles(join(globalTemplatesDir, "manifold"), join(directory, "Manifold"));
114
- if (manifoldCopied.length > 0) {
115
- initialized.push(`Manifold/ (${manifoldCopied.join(", ")})`);
116
- }
117
- const version = await getPluginVersion();
118
- await writeFile(join(directory, "Manifold", "VERSION"), version + `
119
- `);
120
- await client.app.log({
121
- body: {
122
- service: "opencode-manifold",
123
- level: "info",
124
- message: `/manifold-init complete: ${initialized.join(", ") || "already initialized"}`
125
- }
126
- });
127
- return initialized;
128
- }
129
-
130
- // src/tools/dispatch-task.ts
131
- import { tool } from "@opencode-ai/plugin";
132
- import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
133
- import { existsSync as existsSync5 } from "fs";
134
- import { join as join5 } from "path";
135
-
136
- // src/state-machine.ts
137
- import { readFile as readFile3, writeFile as writeFile3, readdir as readdir2 } from "fs/promises";
138
- import { existsSync as existsSync4 } from "fs";
139
- import { join as join4 } from "path";
140
-
141
- // src/session-spawner.ts
142
- async function waitForSessionIdle(client, sessionId, timeoutMs) {
143
- const startTime = Date.now();
144
- while (Date.now() - startTime < timeoutMs) {
145
- const statusResponse = await client.session.status({});
146
- const statusData = statusResponse.data;
147
- if (statusData && statusData[sessionId]) {
148
- const status = statusData[sessionId];
149
- if (status.type === "idle") {
150
- return true;
151
- }
152
- if (status.type === "retry") {
153
- const waitTime = status.next - Date.now();
154
- if (waitTime > 0) {
155
- await new Promise((resolve) => setTimeout(resolve, Math.min(waitTime, 1000)));
156
- }
157
- continue;
158
- }
159
- }
160
- await new Promise((resolve) => setTimeout(resolve, 500));
161
- }
162
- return false;
163
- }
164
- async function getLastAssistantMessage(client, sessionId) {
165
- const messagesResponse = await client.session.messages({
166
- path: { id: sessionId }
167
- });
168
- const messages = messagesResponse.data;
169
- if (!messages || !Array.isArray(messages)) {
170
- return "";
171
- }
172
- for (let i = messages.length - 1;i >= 0; i--) {
173
- const msg = messages[i];
174
- if (msg.info && msg.info.role === "assistant") {
175
- if (msg.parts && Array.isArray(msg.parts)) {
176
- for (const part of msg.parts) {
177
- if (part.type === "text" && part.text) {
178
- return part.text;
179
- }
180
- }
181
- }
182
- }
183
- }
184
- return "";
185
- }
186
- async function cleanupSession(client, sessionId) {
187
- try {
188
- await client.session.delete({ path: { id: sessionId } });
189
- } catch {}
190
- }
191
- async function spawnSession(client, agent, prompt, timeoutSeconds) {
192
- const timeoutMs = timeoutSeconds * 1000;
193
- try {
194
- const createResponse = await client.session.create({});
195
- const session = createResponse.data;
196
- if (!session || !session.id) {
197
- return {
198
- content: "",
199
- success: false,
200
- error: "Failed to create session"
201
- };
202
- }
203
- const sessionId = session.id;
204
- await client.app.log({
205
- body: {
206
- service: "opencode-manifold",
207
- level: "info",
208
- message: `Created session ${sessionId} for agent ${agent}`
209
- }
210
- });
211
- try {
212
- await client.session.promptAsync({
213
- path: { id: sessionId },
214
- body: {
215
- agent,
216
- noReply: true,
217
- parts: [{ type: "text", text: prompt }]
218
- }
219
- });
220
- const isIdle = await waitForSessionIdle(client, sessionId, timeoutMs);
221
- if (!isIdle) {
222
- await client.session.abort({ path: { id: sessionId } });
223
- return {
224
- content: "",
225
- success: false,
226
- error: `Session timed out after ${timeoutSeconds}s`
227
- };
228
- }
229
- const content = await getLastAssistantMessage(client, sessionId);
230
- await client.app.log({
231
- body: {
232
- service: "opencode-manifold",
233
- level: "info",
234
- message: `Session ${sessionId} completed, content length: ${content.length}`
235
- }
236
- });
237
- return {
238
- content,
239
- success: true
240
- };
241
- } finally {
242
- await cleanupSession(client, sessionId);
243
- }
244
- } catch (error) {
245
- return {
246
- content: "",
247
- success: false,
248
- error: error instanceof Error ? error.message : String(error)
249
- };
250
- }
251
- }
252
- async function spawnClerkSession(client, prompt, agent, timeout) {
253
- await client.app.log({
254
- body: {
255
- service: "opencode-manifold",
256
- level: "info",
257
- message: `Spawning Clerk session (agent: ${agent}, timeout: ${timeout}s)`
258
- }
259
- });
260
- return spawnSession(client, agent, prompt, timeout);
261
- }
262
- async function spawnSeniorDevSession(client, prompt, agent, timeout) {
263
- await client.app.log({
264
- body: {
265
- service: "opencode-manifold",
266
- level: "info",
267
- message: `Spawning Senior Dev session (agent: ${agent}, timeout: ${timeout}s)`
268
- }
269
- });
270
- return spawnSession(client, agent, prompt, timeout);
271
- }
272
- async function spawnJuniorDevSession(client, prompt, seniorOutput, agent, timeout) {
273
- await client.app.log({
274
- body: {
275
- service: "opencode-manifold",
276
- level: "info",
277
- message: `Spawning Junior Dev session (agent: ${agent}, timeout: ${timeout}s)`
278
- }
279
- });
280
- const fullPrompt = `${prompt}
281
-
282
- ---
283
-
284
- Senior Dev's Implementation:
285
- ${seniorOutput}`;
286
- return spawnSession(client, agent, fullPrompt, timeout);
287
- }
288
- async function spawnDebugSession(client, prompt, loopHistory, agent, timeout) {
289
- await client.app.log({
290
- body: {
291
- service: "opencode-manifold",
292
- level: "info",
293
- message: `Spawning Debug session (agent: ${agent}, timeout: ${timeout}s)`
294
- }
295
- });
296
- const fullPrompt = `${prompt}
297
-
298
- ---
299
-
300
- Loop History:
301
- ${loopHistory}`;
302
- return spawnSession(client, agent, fullPrompt, timeout);
303
- }
304
- function parseJuniorFirstWord(response) {
305
- const firstWord = response.trim().split(/\s+/)[0].toUpperCase();
306
- if (firstWord === "COMPLETE") {
307
- return "COMPLETE";
308
- }
309
- return "QUESTIONS";
310
- }
311
-
312
- // src/error-handler.ts
313
- class ModelCallError extends Error {
314
- agent;
315
- attempt;
316
- constructor(message, agent, attempt) {
317
- super(message);
318
- this.agent = agent;
319
- this.attempt = attempt;
320
- this.name = "ModelCallError";
321
- }
322
- }
323
- async function retryWithBackoff(fn, options) {
324
- let lastError;
325
- for (let attempt = 0;attempt <= options.maxRetries; attempt++) {
326
- try {
327
- return await fn();
328
- } catch (error) {
329
- lastError = error instanceof Error ? error : new Error(String(error));
330
- if (attempt < options.maxRetries) {
331
- options.onRetry?.(attempt + 1, lastError);
332
- const delay = Math.pow(2, attempt) * 100;
333
- await new Promise((resolve) => setTimeout(resolve, delay));
334
- }
335
- }
336
- }
337
- throw lastError;
338
- }
339
-
340
- // src/graph.ts
341
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
342
- import { existsSync as existsSync2 } from "fs";
343
- import { join as join2, dirname as dirname2 } from "path";
4
+ // src/tui.ts
5
+ import { existsSync } from "fs";
6
+ import { join } from "path";
7
+ import { homedir } from "os";
8
+ import { readFile, writeFile, readdir } from "fs/promises";
344
9
 
345
10
  // node_modules/js-yaml/dist/js-yaml.mjs
346
11
  /*! js-yaml 4.1.1 https://github.com/nodeca/js-yaml @license MIT */
@@ -2804,230 +2469,822 @@ function writeBlockMapping(state, level, object, compact) {
2804
2469
  if (explicitPair) {
2805
2470
  pairBuffer += generateNextLine(state, level);
2806
2471
  }
2807
- if (!writeNode(state, level + 1, objectValue, true, explicitPair)) {
2808
- continue;
2472
+ if (!writeNode(state, level + 1, objectValue, true, explicitPair)) {
2473
+ continue;
2474
+ }
2475
+ if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {
2476
+ pairBuffer += ":";
2477
+ } else {
2478
+ pairBuffer += ": ";
2479
+ }
2480
+ pairBuffer += state.dump;
2481
+ _result += pairBuffer;
2482
+ }
2483
+ state.tag = _tag;
2484
+ state.dump = _result || "{}";
2485
+ }
2486
+ function detectType(state, object, explicit) {
2487
+ var _result, typeList, index, length, type2, style;
2488
+ typeList = explicit ? state.explicitTypes : state.implicitTypes;
2489
+ for (index = 0, length = typeList.length;index < length; index += 1) {
2490
+ type2 = typeList[index];
2491
+ if ((type2.instanceOf || type2.predicate) && (!type2.instanceOf || typeof object === "object" && object instanceof type2.instanceOf) && (!type2.predicate || type2.predicate(object))) {
2492
+ if (explicit) {
2493
+ if (type2.multi && type2.representName) {
2494
+ state.tag = type2.representName(object);
2495
+ } else {
2496
+ state.tag = type2.tag;
2497
+ }
2498
+ } else {
2499
+ state.tag = "?";
2500
+ }
2501
+ if (type2.represent) {
2502
+ style = state.styleMap[type2.tag] || type2.defaultStyle;
2503
+ if (_toString.call(type2.represent) === "[object Function]") {
2504
+ _result = type2.represent(object, style);
2505
+ } else if (_hasOwnProperty.call(type2.represent, style)) {
2506
+ _result = type2.represent[style](object, style);
2507
+ } else {
2508
+ throw new exception("!<" + type2.tag + '> tag resolver accepts not "' + style + '" style');
2509
+ }
2510
+ state.dump = _result;
2511
+ }
2512
+ return true;
2513
+ }
2514
+ }
2515
+ return false;
2516
+ }
2517
+ function writeNode(state, level, object, block, compact, iskey, isblockseq) {
2518
+ state.tag = null;
2519
+ state.dump = object;
2520
+ if (!detectType(state, object, false)) {
2521
+ detectType(state, object, true);
2522
+ }
2523
+ var type2 = _toString.call(state.dump);
2524
+ var inblock = block;
2525
+ var tagStr;
2526
+ if (block) {
2527
+ block = state.flowLevel < 0 || state.flowLevel > level;
2528
+ }
2529
+ var objectOrArray = type2 === "[object Object]" || type2 === "[object Array]", duplicateIndex, duplicate;
2530
+ if (objectOrArray) {
2531
+ duplicateIndex = state.duplicates.indexOf(object);
2532
+ duplicate = duplicateIndex !== -1;
2533
+ }
2534
+ if (state.tag !== null && state.tag !== "?" || duplicate || state.indent !== 2 && level > 0) {
2535
+ compact = false;
2536
+ }
2537
+ if (duplicate && state.usedDuplicates[duplicateIndex]) {
2538
+ state.dump = "*ref_" + duplicateIndex;
2539
+ } else {
2540
+ if (objectOrArray && duplicate && !state.usedDuplicates[duplicateIndex]) {
2541
+ state.usedDuplicates[duplicateIndex] = true;
2542
+ }
2543
+ if (type2 === "[object Object]") {
2544
+ if (block && Object.keys(state.dump).length !== 0) {
2545
+ writeBlockMapping(state, level, state.dump, compact);
2546
+ if (duplicate) {
2547
+ state.dump = "&ref_" + duplicateIndex + state.dump;
2548
+ }
2549
+ } else {
2550
+ writeFlowMapping(state, level, state.dump);
2551
+ if (duplicate) {
2552
+ state.dump = "&ref_" + duplicateIndex + " " + state.dump;
2553
+ }
2554
+ }
2555
+ } else if (type2 === "[object Array]") {
2556
+ if (block && state.dump.length !== 0) {
2557
+ if (state.noArrayIndent && !isblockseq && level > 0) {
2558
+ writeBlockSequence(state, level - 1, state.dump, compact);
2559
+ } else {
2560
+ writeBlockSequence(state, level, state.dump, compact);
2561
+ }
2562
+ if (duplicate) {
2563
+ state.dump = "&ref_" + duplicateIndex + state.dump;
2564
+ }
2565
+ } else {
2566
+ writeFlowSequence(state, level, state.dump);
2567
+ if (duplicate) {
2568
+ state.dump = "&ref_" + duplicateIndex + " " + state.dump;
2569
+ }
2570
+ }
2571
+ } else if (type2 === "[object String]") {
2572
+ if (state.tag !== "?") {
2573
+ writeScalar(state, state.dump, level, iskey, inblock);
2574
+ }
2575
+ } else if (type2 === "[object Undefined]") {
2576
+ return false;
2577
+ } else {
2578
+ if (state.skipInvalid)
2579
+ return false;
2580
+ throw new exception("unacceptable kind of an object to dump " + type2);
2581
+ }
2582
+ if (state.tag !== null && state.tag !== "?") {
2583
+ tagStr = encodeURI(state.tag[0] === "!" ? state.tag.slice(1) : state.tag).replace(/!/g, "%21");
2584
+ if (state.tag[0] === "!") {
2585
+ tagStr = "!" + tagStr;
2586
+ } else if (tagStr.slice(0, 18) === "tag:yaml.org,2002:") {
2587
+ tagStr = "!!" + tagStr.slice(18);
2588
+ } else {
2589
+ tagStr = "!<" + tagStr + ">";
2590
+ }
2591
+ state.dump = tagStr + " " + state.dump;
2592
+ }
2593
+ }
2594
+ return true;
2595
+ }
2596
+ function getDuplicateReferences(object, state) {
2597
+ var objects = [], duplicatesIndexes = [], index, length;
2598
+ inspectNode(object, objects, duplicatesIndexes);
2599
+ for (index = 0, length = duplicatesIndexes.length;index < length; index += 1) {
2600
+ state.duplicates.push(objects[duplicatesIndexes[index]]);
2601
+ }
2602
+ state.usedDuplicates = new Array(length);
2603
+ }
2604
+ function inspectNode(object, objects, duplicatesIndexes) {
2605
+ var objectKeyList, index, length;
2606
+ if (object !== null && typeof object === "object") {
2607
+ index = objects.indexOf(object);
2608
+ if (index !== -1) {
2609
+ if (duplicatesIndexes.indexOf(index) === -1) {
2610
+ duplicatesIndexes.push(index);
2611
+ }
2612
+ } else {
2613
+ objects.push(object);
2614
+ if (Array.isArray(object)) {
2615
+ for (index = 0, length = object.length;index < length; index += 1) {
2616
+ inspectNode(object[index], objects, duplicatesIndexes);
2617
+ }
2618
+ } else {
2619
+ objectKeyList = Object.keys(object);
2620
+ for (index = 0, length = objectKeyList.length;index < length; index += 1) {
2621
+ inspectNode(object[objectKeyList[index]], objects, duplicatesIndexes);
2622
+ }
2623
+ }
2624
+ }
2625
+ }
2626
+ }
2627
+ function dump$1(input, options) {
2628
+ options = options || {};
2629
+ var state = new State(options);
2630
+ if (!state.noRefs)
2631
+ getDuplicateReferences(input, state);
2632
+ var value = input;
2633
+ if (state.replacer) {
2634
+ value = state.replacer.call({ "": value }, "", value);
2635
+ }
2636
+ if (writeNode(state, 0, value, true, true))
2637
+ return state.dump + `
2638
+ `;
2639
+ return "";
2640
+ }
2641
+ var dump_1 = dump$1;
2642
+ var dumper = {
2643
+ dump: dump_1
2644
+ };
2645
+ function renamed(from, to) {
2646
+ return function() {
2647
+ throw new Error("Function yaml." + from + " is removed in js-yaml 4. " + "Use yaml." + to + " instead, which is now safe by default.");
2648
+ };
2649
+ }
2650
+ var Type = type;
2651
+ var Schema = schema;
2652
+ var FAILSAFE_SCHEMA = failsafe;
2653
+ var JSON_SCHEMA = json;
2654
+ var CORE_SCHEMA = core;
2655
+ var DEFAULT_SCHEMA = _default;
2656
+ var load = loader.load;
2657
+ var loadAll = loader.loadAll;
2658
+ var dump = dumper.dump;
2659
+ var YAMLException = exception;
2660
+ var types = {
2661
+ binary,
2662
+ float,
2663
+ map,
2664
+ null: _null,
2665
+ pairs,
2666
+ set,
2667
+ timestamp,
2668
+ bool,
2669
+ int,
2670
+ merge,
2671
+ omap,
2672
+ seq,
2673
+ str
2674
+ };
2675
+ var safeLoad = renamed("safeLoad", "load");
2676
+ var safeLoadAll = renamed("safeLoadAll", "loadAll");
2677
+ var safeDump = renamed("safeDump", "dump");
2678
+ var jsYaml = {
2679
+ Type,
2680
+ Schema,
2681
+ FAILSAFE_SCHEMA,
2682
+ JSON_SCHEMA,
2683
+ CORE_SCHEMA,
2684
+ DEFAULT_SCHEMA,
2685
+ load,
2686
+ loadAll,
2687
+ dump,
2688
+ YAMLException,
2689
+ types,
2690
+ safeLoad,
2691
+ safeLoadAll,
2692
+ safeDump
2693
+ };
2694
+
2695
+ // src/tui.ts
2696
+ var MANIFOLD_AGENTS = ["clerk", "senior-dev", "junior-dev", "debug"];
2697
+ async function getManifoldAgents(directory) {
2698
+ const agentsDir = join(directory, ".opencode", "agents");
2699
+ if (!existsSync(agentsDir)) {
2700
+ return [];
2701
+ }
2702
+ const files = await readdir(agentsDir);
2703
+ const agents = [];
2704
+ for (const file of files) {
2705
+ if (!file.endsWith(".md"))
2706
+ continue;
2707
+ const name = file.replace(".md", "");
2708
+ if (MANIFOLD_AGENTS.includes(name)) {
2709
+ agents.push(name);
2710
+ }
2711
+ }
2712
+ return agents;
2713
+ }
2714
+ async function readAgentFile(agentName, directory) {
2715
+ const agentPath = join(directory, ".opencode", "agents", `${agentName}.md`);
2716
+ if (!existsSync(agentPath)) {
2717
+ const globalPath = join(homedir(), ".config", "opencode", "manifold", "agents", `${agentName}.md`);
2718
+ if (existsSync(globalPath)) {
2719
+ const content2 = await readFile(globalPath, "utf-8");
2720
+ const { frontmatter: frontmatter2, body: body2 } = parseFrontmatter(content2);
2721
+ return { content: content2, frontmatter: frontmatter2, body: body2 };
2722
+ }
2723
+ throw new Error(`Agent file not found for ${agentName}`);
2724
+ }
2725
+ const content = await readFile(agentPath, "utf-8");
2726
+ const { frontmatter, body } = parseFrontmatter(content);
2727
+ return { content, frontmatter, body };
2728
+ }
2729
+ function parseFrontmatter(content) {
2730
+ const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
2731
+ if (!match) {
2732
+ return { frontmatter: {}, body: content };
2733
+ }
2734
+ const frontmatterYaml = match[1];
2735
+ const body = match[2];
2736
+ const frontmatter = jsYaml.load(frontmatterYaml) || {};
2737
+ return { frontmatter, body };
2738
+ }
2739
+ function buildAgentFile(frontmatter, body) {
2740
+ const yamlContent = jsYaml.dump(frontmatter, {
2741
+ lineWidth: -1,
2742
+ noCompatMode: true
2743
+ });
2744
+ return `---
2745
+ ${yamlContent}---
2746
+ ${body}`;
2747
+ }
2748
+ async function updateAgentModel(agentName, modelId, directory) {
2749
+ const { frontmatter, body } = await readAgentFile(agentName, directory);
2750
+ frontmatter.model = modelId;
2751
+ const newContent = buildAgentFile(frontmatter, body);
2752
+ const agentPath = join(directory, ".opencode", "agents", `${agentName}.md`);
2753
+ await writeFile(agentPath, newContent);
2754
+ }
2755
+ var tui = async (api) => {
2756
+ api.command.register(() => [
2757
+ {
2758
+ title: "Manifold Models",
2759
+ value: "manifold-models",
2760
+ description: "Set the model for a Manifold sub-agent",
2761
+ category: "Manifold",
2762
+ slash: {
2763
+ name: "manifold-models"
2764
+ }
2765
+ },
2766
+ {
2767
+ title: "Manifold Update",
2768
+ value: "manifold-update",
2769
+ description: "Clear opencode-manifold plugin cache and prompt for restart",
2770
+ category: "Manifold",
2771
+ slash: {
2772
+ name: "manifold-update"
2773
+ }
2774
+ }
2775
+ ]);
2776
+ api.event.on("tui.command.execute", async (event) => {
2777
+ if (event.properties.command === "manifold-models") {
2778
+ await handleManifoldModels(api);
2779
+ } else if (event.properties.command === "manifold-update") {
2780
+ await handleManifoldUpdate(api);
2809
2781
  }
2810
- if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {
2811
- pairBuffer += ":";
2812
- } else {
2813
- pairBuffer += ": ";
2782
+ });
2783
+ };
2784
+ async function handleManifoldModels(api) {
2785
+ const directory = api.state.path.directory;
2786
+ const agents = await getManifoldAgents(directory);
2787
+ if (agents.length === 0) {
2788
+ api.ui.toast({
2789
+ variant: "error",
2790
+ message: "No Manifold agents found. Run /manifold-init first."
2791
+ });
2792
+ return;
2793
+ }
2794
+ const providers = api.state.provider;
2795
+ const models = [];
2796
+ for (const provider of providers) {
2797
+ if (provider.models) {
2798
+ for (const [modelId, model] of Object.entries(provider.models)) {
2799
+ models.push({
2800
+ id: `${provider.id}/${modelId}`,
2801
+ name: model.name || modelId,
2802
+ providerID: provider.id
2803
+ });
2804
+ }
2814
2805
  }
2815
- pairBuffer += state.dump;
2816
- _result += pairBuffer;
2817
2806
  }
2818
- state.tag = _tag;
2819
- state.dump = _result || "{}";
2807
+ if (models.length === 0) {
2808
+ api.ui.toast({
2809
+ variant: "error",
2810
+ message: "No models available. Configure providers first."
2811
+ });
2812
+ return;
2813
+ }
2814
+ models.sort((a, b) => a.name.localeCompare(b.name));
2815
+ const agentOptions = agents.map((agent) => ({
2816
+ title: `${agent} (sub-agent)`,
2817
+ value: agent,
2818
+ description: `Configure model for ${agent}`
2819
+ }));
2820
+ api.ui.dialog.setSize("medium");
2821
+ return new Promise((resolve) => {
2822
+ api.ui.ui.DialogSelect({
2823
+ title: "Select Manifold Sub-Agent",
2824
+ options: agentOptions,
2825
+ onSelect: async (option) => {
2826
+ const selectedAgent = option.value;
2827
+ const currentModel = await readAgentFile(selectedAgent, directory).then((f) => f.frontmatter.model || "not set").catch(() => "not set");
2828
+ const modelOptions = models.map((model) => ({
2829
+ title: `${model.name}`,
2830
+ value: model.id,
2831
+ description: model.id,
2832
+ footer: model.id === currentModel ? "✓ Current" : undefined
2833
+ }));
2834
+ api.ui.ui.DialogSelect({
2835
+ title: `Select Model for ${selectedAgent}`,
2836
+ options: modelOptions,
2837
+ current: currentModel !== "not set" ? currentModel : undefined,
2838
+ onSelect: async (modelOption) => {
2839
+ const selectedModelId = modelOption.value;
2840
+ await updateAgentModel(selectedAgent, selectedModelId, directory);
2841
+ api.ui.toast({
2842
+ variant: "success",
2843
+ message: `Set ${selectedAgent} to ${selectedModelId}`
2844
+ });
2845
+ resolve();
2846
+ }
2847
+ });
2848
+ }
2849
+ });
2850
+ });
2820
2851
  }
2821
- function detectType(state, object, explicit) {
2822
- var _result, typeList, index, length, type2, style;
2823
- typeList = explicit ? state.explicitTypes : state.implicitTypes;
2824
- for (index = 0, length = typeList.length;index < length; index += 1) {
2825
- type2 = typeList[index];
2826
- if ((type2.instanceOf || type2.predicate) && (!type2.instanceOf || typeof object === "object" && object instanceof type2.instanceOf) && (!type2.predicate || type2.predicate(object))) {
2827
- if (explicit) {
2828
- if (type2.multi && type2.representName) {
2829
- state.tag = type2.representName(object);
2830
- } else {
2831
- state.tag = type2.tag;
2852
+ async function handleManifoldUpdate(api) {
2853
+ const directory = api.state.path.directory;
2854
+ const settingsPath = join(directory, "Manifold", "settings.json");
2855
+ let settings = {};
2856
+ if (existsSync(settingsPath)) {
2857
+ const content = await readFile(settingsPath, "utf-8");
2858
+ try {
2859
+ settings = JSON.parse(content);
2860
+ } catch {}
2861
+ }
2862
+ const configuredPaths = settings.updateCachePaths || [];
2863
+ if (configuredPaths.length === 0) {
2864
+ api.ui.dialog.setSize("large");
2865
+ api.ui.ui.DialogSelect({
2866
+ title: "Manifold Plugin Update",
2867
+ options: [
2868
+ {
2869
+ title: "Configure Cache Paths",
2870
+ value: "configure",
2871
+ description: "Add updateCachePaths to Manifold/settings.json",
2872
+ footer: "Opens settings file"
2832
2873
  }
2833
- } else {
2834
- state.tag = "?";
2874
+ ],
2875
+ onSelect: () => {
2876
+ api.ui.toast({
2877
+ variant: "info",
2878
+ message: "Add updateCachePaths to Manifold/settings.json"
2879
+ });
2835
2880
  }
2836
- if (type2.represent) {
2837
- style = state.styleMap[type2.tag] || type2.defaultStyle;
2838
- if (_toString.call(type2.represent) === "[object Function]") {
2839
- _result = type2.represent(object, style);
2840
- } else if (_hasOwnProperty.call(type2.represent, style)) {
2841
- _result = type2.represent[style](object, style);
2842
- } else {
2843
- throw new exception("!<" + type2.tag + '> tag resolver accepts not "' + style + '" style');
2881
+ });
2882
+ return;
2883
+ }
2884
+ const resolvedPaths = configuredPaths.map((p) => {
2885
+ const expanded = p.startsWith("~") ? join(homedir(), p.slice(1)) : p;
2886
+ return expanded;
2887
+ });
2888
+ api.ui.dialog.setSize("large");
2889
+ return new Promise((resolve) => {
2890
+ api.ui.ui.DialogSelect({
2891
+ title: "Manifold Plugin Update",
2892
+ options: [
2893
+ {
2894
+ title: "Clear Cache",
2895
+ value: "confirm",
2896
+ description: `Will clear ${resolvedPaths.length} path(s)`,
2897
+ footer: resolvedPaths.map((p) => `• ${p}`).join(`
2898
+ `)
2899
+ },
2900
+ {
2901
+ title: "Cancel",
2902
+ value: "cancel",
2903
+ description: "Do not clear cache"
2844
2904
  }
2845
- state.dump = _result;
2905
+ ],
2906
+ onSelect: async (option) => {
2907
+ if (option.value === "cancel") {
2908
+ resolve();
2909
+ return;
2910
+ }
2911
+ const { exec } = await import("child_process");
2912
+ const { promisify } = await import("util");
2913
+ const execAsync = promisify(exec);
2914
+ const cleared = [];
2915
+ const skipped = [];
2916
+ const blocked = [];
2917
+ for (const pathStr of resolvedPaths) {
2918
+ try {
2919
+ await execAsync(`rm -rf "${pathStr}"`);
2920
+ cleared.push(pathStr);
2921
+ } catch (error) {
2922
+ blocked.push({ path: pathStr, reason: `Failed to delete: ${error}` });
2923
+ }
2924
+ }
2925
+ let message = "";
2926
+ if (cleared.length > 0) {
2927
+ message += `✅ Cleared: ${cleared.length} path(s)
2928
+ `;
2929
+ }
2930
+ if (blocked.length > 0) {
2931
+ message += `\uD83D\uDEAB Blocked: ${blocked.length} path(s)
2932
+ `;
2933
+ }
2934
+ if (cleared.length > 0) {
2935
+ message += `
2936
+ Restart opencode to pull the latest plugin version.`;
2937
+ }
2938
+ api.ui.toast({
2939
+ variant: cleared.length > 0 ? "success" : "error",
2940
+ message: cleared.length > 0 ? `Cache cleared. Restart opencode to update.` : `Cache update blocked`
2941
+ });
2942
+ resolve();
2846
2943
  }
2847
- return true;
2944
+ });
2945
+ });
2946
+ }
2947
+
2948
+ // src/init.ts
2949
+ import { readFile as readFile2, writeFile as writeFile2, mkdir, readdir as readdir2 } from "fs/promises";
2950
+ import { existsSync as existsSync2 } from "fs";
2951
+ import { join as join2, dirname } from "path";
2952
+ import { fileURLToPath } from "url";
2953
+ import { homedir as homedir2 } from "os";
2954
+ import { createRequire as createRequire2 } from "module";
2955
+ var __dirname2 = dirname(fileURLToPath(import.meta.url));
2956
+ var require2 = createRequire2(import.meta.url);
2957
+ var bundledTemplatesDir = join2(__dirname2, "..", "src", "templates");
2958
+ var globalTemplatesDir = join2(homedir2(), ".config", "opencode", "manifold");
2959
+ async function getPluginVersion() {
2960
+ try {
2961
+ const packageJson = require2(join2(__dirname2, "..", "package.json"));
2962
+ return packageJson.version || "unknown";
2963
+ } catch {
2964
+ return "unknown";
2965
+ }
2966
+ }
2967
+ async function dirHasContent(dirPath) {
2968
+ if (!existsSync2(dirPath))
2969
+ return false;
2970
+ try {
2971
+ const entries = await readdir2(dirPath);
2972
+ return entries.length > 0;
2973
+ } catch {
2974
+ return false;
2975
+ }
2976
+ }
2977
+ async function copyFiles(src, dest) {
2978
+ if (!existsSync2(src))
2979
+ return [];
2980
+ await mkdir(dest, { recursive: true });
2981
+ const copied = [];
2982
+ const entries = await readdir2(src, { withFileTypes: true });
2983
+ for (const entry of entries) {
2984
+ const srcPath = join2(src, entry.name);
2985
+ const destPath = join2(dest, entry.name);
2986
+ if (entry.isDirectory()) {
2987
+ const subCopied = await copyFiles(srcPath, destPath);
2988
+ if (subCopied.length > 0) {
2989
+ copied.push(entry.name);
2990
+ }
2991
+ } else {
2992
+ await writeFile2(destPath, await readFile2(srcPath));
2993
+ copied.push(entry.name);
2848
2994
  }
2849
2995
  }
2850
- return false;
2996
+ return copied;
2851
2997
  }
2852
- function writeNode(state, level, object, block, compact, iskey, isblockseq) {
2853
- state.tag = null;
2854
- state.dump = object;
2855
- if (!detectType(state, object, false)) {
2856
- detectType(state, object, true);
2998
+ async function ensureGlobalTemplates(ctx) {
2999
+ await ctx.client.app.log({
3000
+ body: {
3001
+ service: "opencode-manifold",
3002
+ level: "info",
3003
+ message: "Synchronizing global templates..."
3004
+ }
3005
+ });
3006
+ if (!existsSync2(bundledTemplatesDir)) {
3007
+ await ctx.client.app.log({
3008
+ body: {
3009
+ service: "opencode-manifold",
3010
+ level: "error",
3011
+ message: `Bundled templates not found at ${bundledTemplatesDir}`
3012
+ }
3013
+ });
3014
+ return;
2857
3015
  }
2858
- var type2 = _toString.call(state.dump);
2859
- var inblock = block;
2860
- var tagStr;
2861
- if (block) {
2862
- block = state.flowLevel < 0 || state.flowLevel > level;
3016
+ await copyFiles(bundledTemplatesDir, globalTemplatesDir);
3017
+ const globalCommandsDir = join2(homedir2(), ".config", "opencode", "commands");
3018
+ const bundledCommandsDir = join2(bundledTemplatesDir, "commands");
3019
+ if (existsSync2(bundledCommandsDir)) {
3020
+ await copyFiles(bundledCommandsDir, globalCommandsDir);
2863
3021
  }
2864
- var objectOrArray = type2 === "[object Object]" || type2 === "[object Array]", duplicateIndex, duplicate;
2865
- if (objectOrArray) {
2866
- duplicateIndex = state.duplicates.indexOf(object);
2867
- duplicate = duplicateIndex !== -1;
3022
+ await ctx.client.app.log({
3023
+ body: {
3024
+ service: "opencode-manifold",
3025
+ level: "info",
3026
+ message: "Global templates synchronized"
3027
+ }
3028
+ });
3029
+ }
3030
+ async function initProject(directory, client) {
3031
+ const initialized = [];
3032
+ await client.app.log({
3033
+ body: {
3034
+ service: "opencode-manifold",
3035
+ level: "info",
3036
+ message: `Running /manifold-init in ${directory}`
3037
+ }
3038
+ });
3039
+ if (!await dirHasContent(globalTemplatesDir)) {
3040
+ await client.app.log({
3041
+ body: {
3042
+ service: "opencode-manifold",
3043
+ level: "error",
3044
+ message: `Global templates not found at ${globalTemplatesDir}. Plugin may not have loaded correctly.`
3045
+ }
3046
+ });
3047
+ return initialized;
2868
3048
  }
2869
- if (state.tag !== null && state.tag !== "?" || duplicate || state.indent !== 2 && level > 0) {
2870
- compact = false;
3049
+ const agentsCopied = await copyMissingFiles(join2(globalTemplatesDir, "agents"), join2(directory, ".opencode", "agents"));
3050
+ if (agentsCopied.length > 0) {
3051
+ initialized.push(`agents (${agentsCopied.join(", ")})`);
2871
3052
  }
2872
- if (duplicate && state.usedDuplicates[duplicateIndex]) {
2873
- state.dump = "*ref_" + duplicateIndex;
2874
- } else {
2875
- if (objectOrArray && duplicate && !state.usedDuplicates[duplicateIndex]) {
2876
- state.usedDuplicates[duplicateIndex] = true;
3053
+ const skillsCopied = await copyMissingFiles(join2(globalTemplatesDir, "skills"), join2(directory, ".opencode", "skills"));
3054
+ if (skillsCopied.length > 0) {
3055
+ initialized.push(`skills (${skillsCopied.join(", ")})`);
3056
+ }
3057
+ const manifoldCopied = await copyMissingFiles(join2(globalTemplatesDir, "manifold"), join2(directory, "Manifold"));
3058
+ if (manifoldCopied.length > 0) {
3059
+ initialized.push(`Manifold/ (${manifoldCopied.join(", ")})`);
3060
+ }
3061
+ const version = await getPluginVersion();
3062
+ await writeFile2(join2(directory, "Manifold", "VERSION"), version + `
3063
+ `);
3064
+ await client.app.log({
3065
+ body: {
3066
+ service: "opencode-manifold",
3067
+ level: "info",
3068
+ message: `/manifold-init complete: ${initialized.join(", ") || "already initialized"}`
2877
3069
  }
2878
- if (type2 === "[object Object]") {
2879
- if (block && Object.keys(state.dump).length !== 0) {
2880
- writeBlockMapping(state, level, state.dump, compact);
2881
- if (duplicate) {
2882
- state.dump = "&ref_" + duplicateIndex + state.dump;
2883
- }
2884
- } else {
2885
- writeFlowMapping(state, level, state.dump);
2886
- if (duplicate) {
2887
- state.dump = "&ref_" + duplicateIndex + " " + state.dump;
2888
- }
3070
+ });
3071
+ return initialized;
3072
+ }
3073
+
3074
+ // src/tools/dispatch-task.ts
3075
+ import { tool } from "@opencode-ai/plugin";
3076
+ import { readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
3077
+ import { existsSync as existsSync6 } from "fs";
3078
+ import { join as join6 } from "path";
3079
+
3080
+ // src/state-machine.ts
3081
+ import { readFile as readFile4, writeFile as writeFile4, readdir as readdir3 } from "fs/promises";
3082
+ import { existsSync as existsSync5 } from "fs";
3083
+ import { join as join5 } from "path";
3084
+
3085
+ // src/session-spawner.ts
3086
+ async function waitForSessionIdle(client, sessionId, timeoutMs) {
3087
+ const startTime = Date.now();
3088
+ while (Date.now() - startTime < timeoutMs) {
3089
+ const statusResponse = await client.session.status({});
3090
+ const statusData = statusResponse.data;
3091
+ if (statusData && statusData[sessionId]) {
3092
+ const status = statusData[sessionId];
3093
+ if (status.type === "idle") {
3094
+ return true;
2889
3095
  }
2890
- } else if (type2 === "[object Array]") {
2891
- if (block && state.dump.length !== 0) {
2892
- if (state.noArrayIndent && !isblockseq && level > 0) {
2893
- writeBlockSequence(state, level - 1, state.dump, compact);
2894
- } else {
2895
- writeBlockSequence(state, level, state.dump, compact);
2896
- }
2897
- if (duplicate) {
2898
- state.dump = "&ref_" + duplicateIndex + state.dump;
2899
- }
2900
- } else {
2901
- writeFlowSequence(state, level, state.dump);
2902
- if (duplicate) {
2903
- state.dump = "&ref_" + duplicateIndex + " " + state.dump;
3096
+ if (status.type === "retry") {
3097
+ const waitTime = status.next - Date.now();
3098
+ if (waitTime > 0) {
3099
+ await new Promise((resolve) => setTimeout(resolve, Math.min(waitTime, 1000)));
2904
3100
  }
3101
+ continue;
2905
3102
  }
2906
- } else if (type2 === "[object String]") {
2907
- if (state.tag !== "?") {
2908
- writeScalar(state, state.dump, level, iskey, inblock);
2909
- }
2910
- } else if (type2 === "[object Undefined]") {
2911
- return false;
2912
- } else {
2913
- if (state.skipInvalid)
2914
- return false;
2915
- throw new exception("unacceptable kind of an object to dump " + type2);
2916
- }
2917
- if (state.tag !== null && state.tag !== "?") {
2918
- tagStr = encodeURI(state.tag[0] === "!" ? state.tag.slice(1) : state.tag).replace(/!/g, "%21");
2919
- if (state.tag[0] === "!") {
2920
- tagStr = "!" + tagStr;
2921
- } else if (tagStr.slice(0, 18) === "tag:yaml.org,2002:") {
2922
- tagStr = "!!" + tagStr.slice(18);
2923
- } else {
2924
- tagStr = "!<" + tagStr + ">";
2925
- }
2926
- state.dump = tagStr + " " + state.dump;
2927
3103
  }
3104
+ await new Promise((resolve) => setTimeout(resolve, 500));
2928
3105
  }
2929
- return true;
3106
+ return false;
2930
3107
  }
2931
- function getDuplicateReferences(object, state) {
2932
- var objects = [], duplicatesIndexes = [], index, length;
2933
- inspectNode(object, objects, duplicatesIndexes);
2934
- for (index = 0, length = duplicatesIndexes.length;index < length; index += 1) {
2935
- state.duplicates.push(objects[duplicatesIndexes[index]]);
3108
+ async function getLastAssistantMessage(client, sessionId) {
3109
+ const messagesResponse = await client.session.messages({
3110
+ path: { id: sessionId }
3111
+ });
3112
+ const messages = messagesResponse.data;
3113
+ if (!messages || !Array.isArray(messages)) {
3114
+ return "";
2936
3115
  }
2937
- state.usedDuplicates = new Array(length);
3116
+ for (let i2 = messages.length - 1;i2 >= 0; i2--) {
3117
+ const msg = messages[i2];
3118
+ if (msg.info && msg.info.role === "assistant") {
3119
+ if (msg.parts && Array.isArray(msg.parts)) {
3120
+ for (const part of msg.parts) {
3121
+ if (part.type === "text" && part.text) {
3122
+ return part.text;
3123
+ }
3124
+ }
3125
+ }
3126
+ }
3127
+ }
3128
+ return "";
2938
3129
  }
2939
- function inspectNode(object, objects, duplicatesIndexes) {
2940
- var objectKeyList, index, length;
2941
- if (object !== null && typeof object === "object") {
2942
- index = objects.indexOf(object);
2943
- if (index !== -1) {
2944
- if (duplicatesIndexes.indexOf(index) === -1) {
2945
- duplicatesIndexes.push(index);
3130
+ async function cleanupSession(client, sessionId) {
3131
+ try {
3132
+ await client.session.delete({ path: { id: sessionId } });
3133
+ } catch {}
3134
+ }
3135
+ async function spawnSession(client, agent, prompt, timeoutSeconds) {
3136
+ const timeoutMs = timeoutSeconds * 1000;
3137
+ try {
3138
+ const createResponse = await client.session.create({});
3139
+ const session = createResponse.data;
3140
+ if (!session || !session.id) {
3141
+ return {
3142
+ content: "",
3143
+ success: false,
3144
+ error: "Failed to create session"
3145
+ };
3146
+ }
3147
+ const sessionId = session.id;
3148
+ await client.app.log({
3149
+ body: {
3150
+ service: "opencode-manifold",
3151
+ level: "info",
3152
+ message: `Created session ${sessionId} for agent ${agent}`
2946
3153
  }
2947
- } else {
2948
- objects.push(object);
2949
- if (Array.isArray(object)) {
2950
- for (index = 0, length = object.length;index < length; index += 1) {
2951
- inspectNode(object[index], objects, duplicatesIndexes);
2952
- }
2953
- } else {
2954
- objectKeyList = Object.keys(object);
2955
- for (index = 0, length = objectKeyList.length;index < length; index += 1) {
2956
- inspectNode(object[objectKeyList[index]], objects, duplicatesIndexes);
3154
+ });
3155
+ try {
3156
+ await client.session.promptAsync({
3157
+ path: { id: sessionId },
3158
+ body: {
3159
+ agent,
3160
+ noReply: true,
3161
+ parts: [{ type: "text", text: prompt }]
2957
3162
  }
3163
+ });
3164
+ const isIdle = await waitForSessionIdle(client, sessionId, timeoutMs);
3165
+ if (!isIdle) {
3166
+ await client.session.abort({ path: { id: sessionId } });
3167
+ return {
3168
+ content: "",
3169
+ success: false,
3170
+ error: `Session timed out after ${timeoutSeconds}s`
3171
+ };
2958
3172
  }
3173
+ const content = await getLastAssistantMessage(client, sessionId);
3174
+ await client.app.log({
3175
+ body: {
3176
+ service: "opencode-manifold",
3177
+ level: "info",
3178
+ message: `Session ${sessionId} completed, content length: ${content.length}`
3179
+ }
3180
+ });
3181
+ return {
3182
+ content,
3183
+ success: true
3184
+ };
3185
+ } finally {
3186
+ await cleanupSession(client, sessionId);
2959
3187
  }
3188
+ } catch (error) {
3189
+ return {
3190
+ content: "",
3191
+ success: false,
3192
+ error: error instanceof Error ? error.message : String(error)
3193
+ };
2960
3194
  }
2961
3195
  }
2962
- function dump$1(input, options) {
2963
- options = options || {};
2964
- var state = new State(options);
2965
- if (!state.noRefs)
2966
- getDuplicateReferences(input, state);
2967
- var value = input;
2968
- if (state.replacer) {
2969
- value = state.replacer.call({ "": value }, "", value);
3196
+ async function spawnClerkSession(client, prompt, agent, timeout) {
3197
+ await client.app.log({
3198
+ body: {
3199
+ service: "opencode-manifold",
3200
+ level: "info",
3201
+ message: `Spawning Clerk session (agent: ${agent}, timeout: ${timeout}s)`
3202
+ }
3203
+ });
3204
+ return spawnSession(client, agent, prompt, timeout);
3205
+ }
3206
+ async function spawnSeniorDevSession(client, prompt, agent, timeout) {
3207
+ await client.app.log({
3208
+ body: {
3209
+ service: "opencode-manifold",
3210
+ level: "info",
3211
+ message: `Spawning Senior Dev session (agent: ${agent}, timeout: ${timeout}s)`
3212
+ }
3213
+ });
3214
+ return spawnSession(client, agent, prompt, timeout);
3215
+ }
3216
+ async function spawnJuniorDevSession(client, prompt, seniorOutput, agent, timeout) {
3217
+ await client.app.log({
3218
+ body: {
3219
+ service: "opencode-manifold",
3220
+ level: "info",
3221
+ message: `Spawning Junior Dev session (agent: ${agent}, timeout: ${timeout}s)`
3222
+ }
3223
+ });
3224
+ const fullPrompt = `${prompt}
3225
+
3226
+ ---
3227
+
3228
+ Senior Dev's Implementation:
3229
+ ${seniorOutput}`;
3230
+ return spawnSession(client, agent, fullPrompt, timeout);
3231
+ }
3232
+ async function spawnDebugSession(client, prompt, loopHistory, agent, timeout) {
3233
+ await client.app.log({
3234
+ body: {
3235
+ service: "opencode-manifold",
3236
+ level: "info",
3237
+ message: `Spawning Debug session (agent: ${agent}, timeout: ${timeout}s)`
3238
+ }
3239
+ });
3240
+ const fullPrompt = `${prompt}
3241
+
3242
+ ---
3243
+
3244
+ Loop History:
3245
+ ${loopHistory}`;
3246
+ return spawnSession(client, agent, fullPrompt, timeout);
3247
+ }
3248
+ function parseJuniorFirstWord(response) {
3249
+ const firstWord = response.trim().split(/\s+/)[0].toUpperCase();
3250
+ if (firstWord === "COMPLETE") {
3251
+ return "COMPLETE";
2970
3252
  }
2971
- if (writeNode(state, 0, value, true, true))
2972
- return state.dump + `
2973
- `;
2974
- return "";
3253
+ return "QUESTIONS";
2975
3254
  }
2976
- var dump_1 = dump$1;
2977
- var dumper = {
2978
- dump: dump_1
2979
- };
2980
- function renamed(from, to) {
2981
- return function() {
2982
- throw new Error("Function yaml." + from + " is removed in js-yaml 4. " + "Use yaml." + to + " instead, which is now safe by default.");
2983
- };
3255
+
3256
+ // src/error-handler.ts
3257
+ class ModelCallError extends Error {
3258
+ agent;
3259
+ attempt;
3260
+ constructor(message, agent, attempt) {
3261
+ super(message);
3262
+ this.agent = agent;
3263
+ this.attempt = attempt;
3264
+ this.name = "ModelCallError";
3265
+ }
3266
+ }
3267
+ async function retryWithBackoff(fn, options) {
3268
+ let lastError;
3269
+ for (let attempt = 0;attempt <= options.maxRetries; attempt++) {
3270
+ try {
3271
+ return await fn();
3272
+ } catch (error) {
3273
+ lastError = error instanceof Error ? error : new Error(String(error));
3274
+ if (attempt < options.maxRetries) {
3275
+ options.onRetry?.(attempt + 1, lastError);
3276
+ const delay = Math.pow(2, attempt) * 100;
3277
+ await new Promise((resolve) => setTimeout(resolve, delay));
3278
+ }
3279
+ }
3280
+ }
3281
+ throw lastError;
2984
3282
  }
2985
- var Type = type;
2986
- var Schema = schema;
2987
- var FAILSAFE_SCHEMA = failsafe;
2988
- var JSON_SCHEMA = json;
2989
- var CORE_SCHEMA = core;
2990
- var DEFAULT_SCHEMA = _default;
2991
- var load = loader.load;
2992
- var loadAll = loader.loadAll;
2993
- var dump = dumper.dump;
2994
- var YAMLException = exception;
2995
- var types = {
2996
- binary,
2997
- float,
2998
- map,
2999
- null: _null,
3000
- pairs,
3001
- set,
3002
- timestamp,
3003
- bool,
3004
- int,
3005
- merge,
3006
- omap,
3007
- seq,
3008
- str
3009
- };
3010
- var safeLoad = renamed("safeLoad", "load");
3011
- var safeLoadAll = renamed("safeLoadAll", "loadAll");
3012
- var safeDump = renamed("safeDump", "dump");
3013
- var jsYaml = {
3014
- Type,
3015
- Schema,
3016
- FAILSAFE_SCHEMA,
3017
- JSON_SCHEMA,
3018
- CORE_SCHEMA,
3019
- DEFAULT_SCHEMA,
3020
- load,
3021
- loadAll,
3022
- dump,
3023
- YAMLException,
3024
- types,
3025
- safeLoad,
3026
- safeLoadAll,
3027
- safeDump
3028
- };
3029
3283
 
3030
3284
  // src/graph.ts
3285
+ import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir2 } from "fs/promises";
3286
+ import { existsSync as existsSync3 } from "fs";
3287
+ import { join as join3, dirname as dirname2 } from "path";
3031
3288
  var ENCODE_MAP = { "/": "__SL__", ".": "__DT__" };
3032
3289
  var ENCODE_RE = /[/.]/g;
3033
3290
  function pathToGraphFilename(filePath) {
@@ -3137,12 +3394,12 @@ ${tasksSection}
3137
3394
  }
3138
3395
  async function readGraphFile(directory, filePath) {
3139
3396
  const filename = pathToGraphFilename(filePath);
3140
- const graphPath = join2(directory, "Manifold", "graph", filename);
3141
- if (!existsSync2(graphPath)) {
3397
+ const graphPath = join3(directory, "Manifold", "graph", filename);
3398
+ if (!existsSync3(graphPath)) {
3142
3399
  return null;
3143
3400
  }
3144
3401
  try {
3145
- const content = await readFile2(graphPath, "utf-8");
3402
+ const content = await readFile3(graphPath, "utf-8");
3146
3403
  return parseGraphContent(content);
3147
3404
  } catch {
3148
3405
  return null;
@@ -3150,9 +3407,9 @@ async function readGraphFile(directory, filePath) {
3150
3407
  }
3151
3408
  async function updateGraphFile(directory, filePath, taskId, calls, dependsOn) {
3152
3409
  const filename = pathToGraphFilename(filePath);
3153
- const graphPath = join2(directory, "Manifold", "graph", filename);
3410
+ const graphPath = join3(directory, "Manifold", "graph", filename);
3154
3411
  let entry;
3155
- if (existsSync2(graphPath)) {
3412
+ if (existsSync3(graphPath)) {
3156
3413
  const existing = await readGraphFile(directory, filePath);
3157
3414
  entry = existing || {
3158
3415
  filePath,
@@ -3178,11 +3435,11 @@ async function updateGraphFile(directory, filePath, taskId, calls, dependsOn) {
3178
3435
  entry.tasksThatEdited.push(taskId);
3179
3436
  }
3180
3437
  const graphDir = dirname2(graphPath);
3181
- if (!existsSync2(graphDir)) {
3438
+ if (!existsSync3(graphDir)) {
3182
3439
  await mkdir2(graphDir, { recursive: true });
3183
3440
  }
3184
3441
  const content = formatGraphContent(entry);
3185
- await writeFile2(graphPath, content, "utf-8");
3442
+ await writeFile3(graphPath, content, "utf-8");
3186
3443
  }
3187
3444
  async function updateGraphFilesForTask(directory, taskId, files) {
3188
3445
  for (const file of files) {
@@ -3195,9 +3452,9 @@ async function updateGraphFilesForTask(directory, taskId, files) {
3195
3452
  }
3196
3453
  async function replaceGraphCalls(directory, filePath, calls, dependsOn) {
3197
3454
  const filename = pathToGraphFilename(filePath);
3198
- const graphPath = join2(directory, "Manifold", "graph", filename);
3455
+ const graphPath = join3(directory, "Manifold", "graph", filename);
3199
3456
  let entry;
3200
- if (existsSync2(graphPath)) {
3457
+ if (existsSync3(graphPath)) {
3201
3458
  const existing = await readGraphFile(directory, filePath);
3202
3459
  entry = existing || {
3203
3460
  filePath,
@@ -3216,20 +3473,20 @@ async function replaceGraphCalls(directory, filePath, calls, dependsOn) {
3216
3473
  entry.calls = [...new Set(calls)];
3217
3474
  entry.dependsOn = [...new Set(dependsOn)];
3218
3475
  const graphDir = dirname2(graphPath);
3219
- if (!existsSync2(graphDir)) {
3476
+ if (!existsSync3(graphDir)) {
3220
3477
  await mkdir2(graphDir, { recursive: true });
3221
3478
  }
3222
3479
  const content = formatGraphContent(entry);
3223
- await writeFile2(graphPath, content, "utf-8");
3480
+ await writeFile3(graphPath, content, "utf-8");
3224
3481
  }
3225
3482
 
3226
3483
  // src/graph-sync.ts
3227
3484
  import Database from "better-sqlite3";
3228
- import { existsSync as existsSync3 } from "fs";
3229
- import { join as join3 } from "path";
3485
+ import { existsSync as existsSync4 } from "fs";
3486
+ import { join as join4 } from "path";
3230
3487
  function openIndexDb(projectRoot) {
3231
- const dbPath = join3(projectRoot, ".opencode", "index", "codebase.db");
3232
- if (!existsSync3(dbPath)) {
3488
+ const dbPath = join4(projectRoot, ".opencode", "index", "codebase.db");
3489
+ if (!existsSync4(dbPath)) {
3233
3490
  return null;
3234
3491
  }
3235
3492
  try {
@@ -3317,9 +3574,9 @@ async function syncGraphFilesFromIndex(projectRoot, files) {
3317
3574
 
3318
3575
  // src/state-machine.ts
3319
3576
  async function readState(directory) {
3320
- const statePath = join4(directory, "Manifold", "state.json");
3321
- if (existsSync4(statePath)) {
3322
- const content = await readFile3(statePath, "utf-8");
3577
+ const statePath = join5(directory, "Manifold", "state.json");
3578
+ if (existsSync5(statePath)) {
3579
+ const content = await readFile4(statePath, "utf-8");
3323
3580
  return JSON.parse(content);
3324
3581
  }
3325
3582
  return {
@@ -3335,8 +3592,8 @@ async function readState(directory) {
3335
3592
  };
3336
3593
  }
3337
3594
  async function writeState(directory, state) {
3338
- const statePath = join4(directory, "Manifold", "state.json");
3339
- await writeFile3(statePath, JSON.stringify(state, null, 2));
3595
+ const statePath = join5(directory, "Manifold", "state.json");
3596
+ await writeFile4(statePath, JSON.stringify(state, null, 2));
3340
3597
  }
3341
3598
  function buildLoopHistory(state) {
3342
3599
  if (state.loop_history.length === 0) {
@@ -3348,16 +3605,16 @@ ${entry}`).join(`
3348
3605
  `);
3349
3606
  }
3350
3607
  async function readRecentTaskLogs(directory, count) {
3351
- const tasksDir = join4(directory, "Manifold", "tasks");
3352
- if (!existsSync4(tasksDir)) {
3608
+ const tasksDir = join5(directory, "Manifold", "tasks");
3609
+ if (!existsSync5(tasksDir)) {
3353
3610
  return [];
3354
3611
  }
3355
3612
  try {
3356
- const files = await readdir2(tasksDir);
3613
+ const files = await readdir3(tasksDir);
3357
3614
  const mdFiles = files.filter((f) => f.endsWith(".md")).sort();
3358
3615
  const recentFiles = mdFiles.slice(-count);
3359
3616
  const tasks = await Promise.all(recentFiles.map(async (filename) => {
3360
- const content = await readFile3(join4(tasksDir, filename), "utf-8");
3617
+ const content = await readFile4(join5(tasksDir, filename), "utf-8");
3361
3618
  return { filename, content };
3362
3619
  }));
3363
3620
  return tasks;
@@ -4065,9 +4322,9 @@ function setPluginContext(client) {
4065
4322
  pluginClient = client;
4066
4323
  }
4067
4324
  async function readSettings(directory) {
4068
- const settingsPath = join5(directory, "Manifold", "settings.json");
4069
- if (existsSync5(settingsPath)) {
4070
- const content = await readFile4(settingsPath, "utf-8");
4325
+ const settingsPath = join6(directory, "Manifold", "settings.json");
4326
+ if (existsSync6(settingsPath)) {
4327
+ const content = await readFile5(settingsPath, "utf-8");
4071
4328
  return JSON.parse(content);
4072
4329
  }
4073
4330
  return {
@@ -4082,10 +4339,10 @@ async function readSettings(directory) {
4082
4339
  };
4083
4340
  }
4084
4341
  async function updatePlansRegistry(directory, planFile) {
4085
- const plansPath = join5(directory, "Manifold", "plans.json");
4342
+ const plansPath = join6(directory, "Manifold", "plans.json");
4086
4343
  let plans = {};
4087
- if (existsSync5(plansPath)) {
4088
- const content = await readFile4(plansPath, "utf-8");
4344
+ if (existsSync6(plansPath)) {
4345
+ const content = await readFile5(plansPath, "utf-8");
4089
4346
  plans = JSON.parse(content);
4090
4347
  }
4091
4348
  const planSlug = planFile.replace(/[^a-zA-Z0-9]/g, "-").toLowerCase().substring(0, 30);
@@ -4099,7 +4356,7 @@ async function updatePlansRegistry(directory, planFile) {
4099
4356
  };
4100
4357
  }
4101
4358
  plans[planSlug].task_count++;
4102
- await writeFile4(plansPath, JSON.stringify(plans, null, 2));
4359
+ await writeFile5(plansPath, JSON.stringify(plans, null, 2));
4103
4360
  return plans[planSlug].task_count;
4104
4361
  }
4105
4362
  function getClient() {
@@ -4234,5 +4491,6 @@ var ManifoldPlugin = async (ctx) => {
4234
4491
  };
4235
4492
  var server = ManifoldPlugin;
4236
4493
  export {
4494
+ tui,
4237
4495
  server
4238
4496
  };