opencode-orchestrator 0.4.20 → 0.4.22

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/dist/index.d.ts CHANGED
@@ -13,24 +13,6 @@
13
13
  import type { PluginInput } from "@opencode-ai/plugin";
14
14
  declare const OrchestratorPlugin: (input: PluginInput) => Promise<{
15
15
  tool: {
16
- git_branch: {
17
- description: string;
18
- args: {
19
- action: import("zod").ZodEnum<{
20
- status: "status";
21
- diff: "diff";
22
- current: "current";
23
- list: "list";
24
- recent: "recent";
25
- all: "all";
26
- }>;
27
- baseBranch: import("zod").ZodOptional<import("zod").ZodString>;
28
- };
29
- execute(args: {
30
- action: "status" | "diff" | "current" | "list" | "recent" | "all";
31
- baseBranch?: string | undefined;
32
- }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
33
- };
34
16
  call_agent: {
35
17
  description: string;
36
18
  args: {
@@ -126,13 +108,13 @@ declare const OrchestratorPlugin: (input: PluginInput) => Promise<{
126
108
  args: {
127
109
  status: import("zod").ZodOptional<import("zod").ZodEnum<{
128
110
  running: "running";
129
- all: "all";
130
111
  done: "done";
131
112
  error: "error";
113
+ all: "all";
132
114
  }>>;
133
115
  };
134
116
  execute(args: {
135
- status?: "running" | "all" | "done" | "error" | undefined;
117
+ status?: "running" | "done" | "error" | "all" | undefined;
136
118
  }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
137
119
  };
138
120
  kill_background: {
package/dist/index.js CHANGED
@@ -13798,206 +13798,6 @@ Returns matches grouped by pattern, with file paths and line numbers.
13798
13798
  }
13799
13799
  });
13800
13800
 
13801
- // src/tools/git.ts
13802
- var gitBranchTool = (directory) => tool({
13803
- description: `Get Git branch information and status.
13804
-
13805
- <purpose>
13806
- Analyze current Git workspace context including:
13807
- - Current branch name
13808
- - All branches (local and remote)
13809
- - Branch relationships (upstream, ahead/behind)
13810
- - Staged, unstaged, and untracked files
13811
- - Recent commits
13812
- </purpose>
13813
-
13814
- <examples>
13815
- - Get current branch: Returns branch name and status
13816
- - List all branches: Shows local and remote branches
13817
- - Check status: Shows modified files
13818
- - Recent commits: Last 5 commits with file changes
13819
- </examples>
13820
-
13821
- <output>
13822
- Returns structured Git information for better code decisions.
13823
- </output>`,
13824
- args: {
13825
- action: tool.schema.enum([
13826
- "current",
13827
- "list",
13828
- "status",
13829
- "diff",
13830
- "recent",
13831
- "all"
13832
- ]).describe("Action to perform: current, list, status, diff, recent, all"),
13833
- baseBranch: tool.schema.string().optional().describe("Base branch for comparison (e.g., 'main', 'develop'). Required for diff action.")
13834
- },
13835
- async execute(args) {
13836
- const { action, baseBranch } = args;
13837
- try {
13838
- switch (action) {
13839
- case "current":
13840
- return await getCurrentBranch(directory);
13841
- case "list":
13842
- return await listBranches(directory);
13843
- case "status":
13844
- return await getGitStatus(directory);
13845
- case "diff":
13846
- return await getDiff(directory, baseBranch);
13847
- case "recent":
13848
- return await getRecentCommits(directory, 5);
13849
- case "all":
13850
- return await getAllInfo(directory);
13851
- default:
13852
- return await getCurrentBranch(directory);
13853
- }
13854
- } catch (error45) {
13855
- return "\u274C Git error: " + (error45 instanceof Error ? error45.message : String(error45));
13856
- }
13857
- }
13858
- });
13859
- async function execGit(directory, args) {
13860
- const { execSync } = await import("child_process");
13861
- try {
13862
- return execSync("git " + args.join(" "), {
13863
- cwd: directory,
13864
- encoding: "utf-8",
13865
- stdio: ["ignore", "pipe", "pipe"],
13866
- maxBuffer: 10 * 1024 * 1024
13867
- // 10MB
13868
- }).toString();
13869
- } catch (error45) {
13870
- if (error45.status === 128) {
13871
- throw new Error("Not a git repository");
13872
- }
13873
- throw error45;
13874
- }
13875
- }
13876
- async function getAheadBehind(directory, branch) {
13877
- try {
13878
- const output = await execGit(directory, ["rev-list", "--left-right", "--count", branch + "...@{u}"]);
13879
- const parts = output.trim().split(/\s+/);
13880
- const ahead = parseInt(parts[0], 10);
13881
- const behind = parseInt(parts[1] || "0", 10);
13882
- const resultParts = [];
13883
- if (ahead > 0) resultParts.push(ahead + " ahead");
13884
- if (behind > 0) resultParts.push(behind + " behind");
13885
- return resultParts.length > 0 ? "| **Sync** | " + resultParts.join(", ") + " |" : "";
13886
- } catch {
13887
- return "";
13888
- }
13889
- }
13890
- async function getCurrentBranch(directory) {
13891
- const output = await execGit(directory, ["branch", "--show-current"]);
13892
- const current = output.trim() || "HEAD (detached)";
13893
- const upstream = await execGit(directory, ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"]).catch(() => "");
13894
- return "\u{1F33F} **Current Branch**: `" + current + "`\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n" + (upstream ? "| **Upstream** | `" + upstream + "` |" : "") + await getAheadBehind(directory, current);
13895
- }
13896
- async function listBranches(directory) {
13897
- const branches = await execGit(directory, ["branch", "-vv"]);
13898
- const branchList = branches.split("\n").filter((b) => b.trim()).map((b) => {
13899
- const isCurrent = b.startsWith("*");
13900
- const name = isCurrent ? b.substring(2).trim() : b.trim();
13901
- const parts = name.split(/\s+/);
13902
- const branchName = parts[0];
13903
- const upstream = parts[1] ? parts[1].match(/\[([^\]]+)\]/)?.[1] : void 0;
13904
- const icon = isCurrent ? "\u{1F33F}" : "\u{1F4C2}";
13905
- const status = isCurrent ? "(current)" : "";
13906
- const upstreamInfo = upstream ? "\u2192 " + upstream : "";
13907
- return icon + " `" + branchName + "` " + status + " " + upstreamInfo;
13908
- }).join("\n");
13909
- return "\u{1F33F} **All Branches**\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n" + branchList;
13910
- }
13911
- async function getGitStatus(directory) {
13912
- const status = await execGit(directory, ["status", "--porcelain"]);
13913
- const lines = status.split("\n").filter((l) => l.trim());
13914
- if (lines.length === 0) {
13915
- return "\u2705 Working directory clean";
13916
- }
13917
- const staged = [];
13918
- const unstaged = [];
13919
- const untracked = [];
13920
- const conflicted = [];
13921
- for (const line of lines) {
13922
- if (!line || line.length < 2) continue;
13923
- const statusCode = line.substring(0, 2);
13924
- const filename = line.substring(3);
13925
- if (statusCode === "??") {
13926
- untracked.push(filename);
13927
- } else if (statusCode.startsWith("U") || statusCode === "AA") {
13928
- conflicted.push(filename);
13929
- } else if (statusCode[0] !== " ") {
13930
- staged.push(filename);
13931
- }
13932
- if (statusCode[1] !== " " && statusCode[1] !== "?") {
13933
- unstaged.push(filename);
13934
- }
13935
- }
13936
- const current = await execGit(directory, ["branch", "--show-current"]).catch(() => "unknown");
13937
- let result = "\u{1F33F} **Branch**: `" + current + "`\n\n";
13938
- if (staged.length > 0) {
13939
- result += "\u2705 **Staged** (" + staged.length + ")\n" + staged.map((f) => " + " + f).join("\n") + "\n\n";
13940
- }
13941
- if (unstaged.length > 0) {
13942
- result += "\u{1F4DD} **Modified** (" + unstaged.length + ")\n" + unstaged.map((f) => " ~ " + f).join("\n") + "\n\n";
13943
- }
13944
- if (untracked.length > 0) {
13945
- result += "\u2795 **Untracked** (" + untracked.length + ")\n" + untracked.slice(0, 10).map((f) => " ? " + f).join("\n") + (untracked.length > 10 ? "\n ... and " + (untracked.length - 10) + " more" : "") + "\n\n";
13946
- }
13947
- if (conflicted.length > 0) {
13948
- result += "\u26A0\uFE0F **Conflicts** (" + conflicted.length + ")\n" + conflicted.map((f) => " ! " + f).join("\n") + "\n\n";
13949
- }
13950
- return result.trim();
13951
- }
13952
- async function getDiff(directory, baseBranch) {
13953
- const current = await execGit(directory, ["branch", "--show-current"]).catch(() => "HEAD");
13954
- const base = baseBranch || "main";
13955
- const diff = await execGit(directory, ["diff", base + "..." + current, "--stat"]);
13956
- if (!diff.trim()) {
13957
- return "\u2705 No differences between `" + current + "` and `" + base + "`";
13958
- }
13959
- const files = await execGit(directory, ["diff", base + "..." + current, "--name-only"]);
13960
- const fileList = files.split("\n").filter((f) => f.trim());
13961
- return "\u{1F4CA} **Diff**: `" + current + "` vs `" + base + "`\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n" + diff + "\n\n\u{1F4C1} **Changed Files** (" + fileList.length + "):\n" + fileList.map((f, i) => " " + (i + 1) + ". " + f).join("\n");
13962
- }
13963
- async function getRecentCommits(directory, count = 5) {
13964
- const log2 = await execGit(directory, [
13965
- "log",
13966
- "-" + count,
13967
- "--pretty=format:%H|%an|%ad|%s",
13968
- "--date=short",
13969
- "--name-only"
13970
- ]);
13971
- const blocks = log2.split("\n\n").filter((b) => b.trim());
13972
- const commits = [];
13973
- for (const block of blocks) {
13974
- const lines = block.split("\n").filter((l) => l.trim());
13975
- if (lines.length < 2) continue;
13976
- const [hash2, author, date5, message] = lines[0].split("|");
13977
- const files = lines.slice(1);
13978
- commits.push({
13979
- hash: hash2.substring(0, 7),
13980
- author,
13981
- date: date5,
13982
- message,
13983
- files
13984
- });
13985
- }
13986
- let result = "\u{1F4DC} **Recent Commits** (last " + commits.length + ")\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n";
13987
- for (const commit of commits) {
13988
- result += "\n\u{1F539} `" + commit.hash + "` - " + commit.message + "\n";
13989
- result += " \u{1F464} " + commit.author + " | \u{1F4C5} " + commit.date + "\n";
13990
- result += " \u{1F4C1} " + commit.files.length + " file" + (commit.files.length > 1 ? "s" : "") + "\n";
13991
- }
13992
- return result;
13993
- }
13994
- async function getAllInfo(directory) {
13995
- const current = await getCurrentBranch(directory);
13996
- const status = await getGitStatus(directory);
13997
- const recent = await getRecentCommits(directory, 3);
13998
- return "\u{1F50D} **Full Git Context**\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\n" + current + "\n\n" + status + "\n\n" + recent;
13999
- }
14000
-
14001
13801
  // src/core/background.ts
14002
13802
  import { spawn as spawn2 } from "child_process";
14003
13803
  import { randomBytes } from "crypto";
@@ -15674,476 +15474,6 @@ function createAsyncAgentTools(manager, client) {
15674
15474
  };
15675
15475
  }
15676
15476
 
15677
- // src/core/batch-processor.ts
15678
- var SmartBatchProcessor = class {
15679
- parallelAgentManager;
15680
- tasks = /* @__PURE__ */ new Map();
15681
- constructor(parallelAgentManager2) {
15682
- this.parallelAgentManager = parallelAgentManager2;
15683
- }
15684
- /**
15685
- * Process a batch of tasks with smart validation
15686
- */
15687
- async processBatch(tasks, options = {
15688
- concurrency: 3,
15689
- maxRetries: 2,
15690
- validateAfterEach: false,
15691
- continueOnError: true
15692
- }) {
15693
- const startTime = Date.now();
15694
- for (const task of tasks) {
15695
- this.tasks.set(task.id, {
15696
- ...task,
15697
- status: "pending",
15698
- attempts: 0
15699
- });
15700
- }
15701
- console.log(`
15702
- \u{1F4E6} [Smart Batch] Starting ${tasks.length} tasks with concurrency ${options.concurrency}`);
15703
- console.log(` Strategy: ${options.validateAfterEach ? "Validate each" : "Centralized validation"}`);
15704
- await this.executePhase(tasks, options, "initial");
15705
- const failedTasks = Array.from(this.tasks.values()).filter((t) => t.status === "failed" || t.status === "pending");
15706
- if (failedTasks.length === 0) {
15707
- console.log(`
15708
- \u2705 [Smart Batch] All ${tasks.length} tasks succeeded!`);
15709
- return this.buildResult(startTime, tasks);
15710
- }
15711
- console.log(`
15712
- \u{1F50D} [Smart Batch] Validation complete: ${failedTasks.length}/${tasks.length} tasks need retry`);
15713
- let retryCount = 0;
15714
- while (failedTasks.length > 0 && retryCount < options.maxRetries) {
15715
- retryCount++;
15716
- console.log(`
15717
- \u{1F504} [Smart Batch] Retry round ${retryCount}/${options.maxRetries} for ${failedTasks.length} tasks`);
15718
- await this.executePhase(failedTasks, options, `retry-${retryCount}`);
15719
- const stillFailed = Array.from(this.tasks.values()).filter((t) => t.status === "failed");
15720
- if (stillFailed.length === failedTasks.length) {
15721
- console.log(`\u26A0\uFE0F [Smart Batch] No progress in retry round ${retryCount}, stopping`);
15722
- break;
15723
- }
15724
- }
15725
- return this.buildResult(startTime, tasks);
15726
- }
15727
- /**
15728
- * Execute a phase with concurrency control
15729
- */
15730
- async executePhase(tasks, options, phase) {
15731
- for (const task of tasks) {
15732
- this.parallelAgentManager.setConcurrencyLimit(task.agent, options.concurrency);
15733
- }
15734
- const batches = [];
15735
- for (let i = 0; i < tasks.length; i += options.concurrency) {
15736
- batches.push(tasks.slice(i, i + options.concurrency));
15737
- }
15738
- for (let batchIndex = 0; batchIndex < batches.length; batchIndex++) {
15739
- const batch = batches[batchIndex];
15740
- console.log(` [${phase}] Processing batch ${batchIndex + 1}/${batches.length} (${batch.length} tasks)`);
15741
- const batchPromises = batch.map((task) => this.executeTask(task, options));
15742
- await Promise.all(batchPromises);
15743
- if (!options.validateAfterEach) {
15744
- continue;
15745
- }
15746
- const failedInBatch = batch.filter((t) => t.status === "failed");
15747
- if (failedInBatch.length > 0) {
15748
- console.log(` [${phase}] ${failedInBatch.length} failed in batch, immediate retry`);
15749
- const retryPromises = failedInBatch.map((t) => this.executeTask(t, options));
15750
- await Promise.all(retryPromises);
15751
- }
15752
- }
15753
- }
15754
- /**
15755
- * Execute a single task
15756
- */
15757
- async executeTask(task, options) {
15758
- const currentTask = this.tasks.get(task.id);
15759
- if (!options.continueOnError && currentTask.status === "failed") {
15760
- return;
15761
- }
15762
- try {
15763
- currentTask.attempts++;
15764
- currentTask.status = "pending";
15765
- const launched = await this.parallelAgentManager.launch({
15766
- agent: task.agent,
15767
- description: task.description,
15768
- prompt: task.prompt,
15769
- parentSessionID: ""
15770
- // Would need actual parent ID
15771
- });
15772
- const maxWaitTime = 5 * 60 * 1e3;
15773
- const startTime = Date.now();
15774
- while (Date.now() - startTime < maxWaitTime) {
15775
- const taskData = this.parallelAgentManager.getTask(launched.id);
15776
- if (!taskData) break;
15777
- if (taskData.status === "completed") {
15778
- currentTask.status = "success";
15779
- console.log(` \u2705 [${task.id}] Success on attempt ${currentTask.attempts}`);
15780
- return;
15781
- }
15782
- if (taskData.status === "error" || taskData.status === "timeout") {
15783
- currentTask.status = "failed";
15784
- currentTask.error = taskData.error || "Unknown error";
15785
- console.log(` \u274C [${task.id}] Failed: ${currentTask.error}`);
15786
- return;
15787
- }
15788
- await new Promise((resolve) => setTimeout(resolve, 1e3));
15789
- }
15790
- currentTask.status = "failed";
15791
- currentTask.error = "Timeout";
15792
- } catch (error45) {
15793
- currentTask.status = "failed";
15794
- currentTask.error = error45 instanceof Error ? error45.message : String(error45);
15795
- console.log(` \u274C [${task.id}] Exception: ${currentTask.error}`);
15796
- }
15797
- }
15798
- /**
15799
- * Build result summary
15800
- */
15801
- buildResult(startTime, tasks) {
15802
- const allTasks = Array.from(this.tasks.values());
15803
- const successCount = allTasks.filter((t) => t.status === "success").length;
15804
- const failedCount = allTasks.filter((t) => t.status === "failed").length;
15805
- const retriedCount = allTasks.filter((t) => t.attempts > 1).length;
15806
- return {
15807
- total: tasks.length,
15808
- success: successCount,
15809
- failed: failedCount,
15810
- retried: retriedCount,
15811
- duration: Date.now() - startTime,
15812
- tasks: allTasks
15813
- };
15814
- }
15815
- /**
15816
- * Export failed tasks for manual review
15817
- */
15818
- exportFailedTasks() {
15819
- const failedTasks = Array.from(this.tasks.values()).filter((t) => t.status === "failed");
15820
- if (failedTasks.length === 0) {
15821
- return "\u2705 No failed tasks to export.";
15822
- }
15823
- const output = failedTasks.map((task) => {
15824
- return `
15825
- ---
15826
- Task ID: ${task.id}
15827
- Agent: ${task.agent}
15828
- Attempts: ${task.attempts}
15829
- Error: ${task.error}
15830
- Description: ${task.description}
15831
- ---
15832
- `.trim();
15833
- }).join("\n");
15834
- return `\u274C ${failedTasks.length} failed tasks:
15835
- ${output}`;
15836
- }
15837
- /**
15838
- * Clear all tasks
15839
- */
15840
- clear() {
15841
- this.tasks.clear();
15842
- }
15843
- };
15844
-
15845
- // src/tools/batch.ts
15846
- var createProcessBatchTool = (parallelAgentManager2, client) => tool({
15847
- description: `Execute a batch of tasks with intelligent validation and retry.
15848
-
15849
- <strategy>
15850
- 1. Execute all tasks in parallel (respecting concurrency limits)
15851
- 2. Centralized validation: Identify ALL failed tasks at once
15852
- 3. Retry ONLY failed tasks (not everything)
15853
- </strategy>
15854
-
15855
- <benefits>
15856
- - Faster than naive retry: Failed tasks batch-identified
15857
- - More efficient: No redundant work on successful tasks
15858
- - Controlled: Concurrency limits prevent API overload
15859
- </benefits>
15860
-
15861
- <examples>
15862
- process_batch({
15863
- concurrency: 10,
15864
- tasks: [
15865
- { id: "task1", agent: "builder", description: "Test A", prompt: "..." },
15866
- { id: "task2", agent: "inspector", description: "Test B", prompt: "..." }
15867
- ]
15868
- })
15869
- </examples>`,
15870
- args: {
15871
- concurrency: tool.schema.string().describe("Concurrency limit (default: 3, max: 10)"),
15872
- maxRetries: tool.schema.string().optional().describe("Maximum retry rounds (default: 2)"),
15873
- validateAfterEach: tool.schema.boolean().optional().describe("Validate after each task (default: false = centralized validation)"),
15874
- tasks: tool.schema.string().describe("Array of task objects in JSON format")
15875
- },
15876
- async execute(args) {
15877
- const { concurrency, maxRetries = "2", validateAfterEach = false, tasks } = args;
15878
- let taskList;
15879
- try {
15880
- taskList = JSON.parse(tasks);
15881
- } catch {
15882
- return "\u274C Invalid tasks JSON. Must be valid array.";
15883
- }
15884
- if (!Array.isArray(taskList)) {
15885
- return "\u274C tasks must be an array of task objects.";
15886
- }
15887
- for (const task of taskList) {
15888
- if (!task.id || !task.agent || !task.description || !task.prompt) {
15889
- return `\u274C Task missing required fields (id, agent, description, prompt)`;
15890
- }
15891
- }
15892
- const numConcurrency = parseInt(concurrency, 10);
15893
- const numRetries = parseInt(maxRetries, 10);
15894
- if (isNaN(numConcurrency) || numConcurrency < 1 || numConcurrency > 10) {
15895
- return "\u274C Invalid concurrency. Must be 1-10.";
15896
- }
15897
- if (isNaN(numRetries) || numRetries < 0 || numRetries > 5) {
15898
- return "\u274C Invalid maxRetries. Must be 0-5.";
15899
- }
15900
- const processor = new SmartBatchProcessor(parallelAgentManager2);
15901
- const result = await processor.processBatch(taskList, {
15902
- concurrency: numConcurrency,
15903
- maxRetries: numRetries,
15904
- validateAfterEach,
15905
- continueOnError: true
15906
- });
15907
- const durationSecs = Math.floor(result.duration / 1e3);
15908
- const successRate = (result.success / result.total * 100).toFixed(1);
15909
- let output = `\u2705 **Batch Processing Complete**
15910
-
15911
- | Metric | Value |
15912
- |---------|--------|
15913
- | **Total Tasks** | ${result.total} |
15914
- | **Successful** | ${result.success} (${successRate}%) |
15915
- | **Failed** | ${result.failed} |
15916
- | **Retried** | ${result.retried} |
15917
- | **Duration** | ${durationSecs}s |
15918
-
15919
- `;
15920
- if (result.failed > 0) {
15921
- output += `\u26A0\uFE0F **Failed Tasks**
15922
-
15923
- Use \`export_failed_tasks()\` to review failed tasks and manually fix issues.
15924
-
15925
- ---
15926
-
15927
- Failed Task IDs:
15928
- ${result.tasks.filter((t) => t.status === "failed").map((t) => ` - ${t.id}`).join("\n")}
15929
- ---
15930
- `;
15931
- }
15932
- return output;
15933
- }
15934
- });
15935
- var createExportFailedTasksTool = (parallelAgentManager2) => tool({
15936
- description: `Export failed tasks from the last batch for manual review.`,
15937
- args: {},
15938
- async execute() {
15939
- const processor = new SmartBatchProcessor(parallelAgentManager2);
15940
- return processor.exportFailedTasks();
15941
- }
15942
- });
15943
- var createCompareStrategiesTool = (parallelAgentManager2) => tool({
15944
- description: `Compare naive retry vs smart batch validation performance.
15945
-
15946
- <comparison>
15947
- **Naive Strategy** (current):
15948
- - Concurrency: 3 fixed
15949
- - Retry: Per-task immediate retry
15950
- - Issue: Slow for large batches, redundant work
15951
-
15952
- **Smart Batch Strategy** (new):
15953
- - Concurrency: Configurable (up to 10)
15954
- - Validation: Centralized, batch-identify failures
15955
- - Retry: Only failed tasks
15956
- - Benefit: Faster, less redundant work
15957
- </comparison>`,
15958
- args: {
15959
- taskCount: tool.schema.string().describe("Number of simulated tasks (default: 100)"),
15960
- concurrency1: tool.schema.string().optional().describe("Strategy 1 concurrency (default: 3)"),
15961
- concurrency2: tool.schema.string().optional().describe("Strategy 2 concurrency (default: 10)")
15962
- },
15963
- async execute(args) {
15964
- const taskCount = parseInt(args.taskCount || "100", 10);
15965
- const concurrency1 = parseInt(args.concurrency1 || "3", 10);
15966
- const concurrency2 = parseInt(args.concurrency2 || "10", 10);
15967
- const naiveBatches = Math.ceil(taskCount / concurrency1);
15968
- const naiveTime = naiveBatches * 60;
15969
- const smartBatches = Math.ceil(taskCount / concurrency2);
15970
- const smartTime = smartBatches * 60 + 30;
15971
- const timeDiff = naiveTime - smartTime;
15972
- const improvement = (timeDiff / naiveTime * 100).toFixed(1);
15973
- return `\u{1F4CA} **Strategy Comparison for ${taskCount} tasks**
15974
-
15975
- **Strategy 1: Naive (Current)**
15976
- | Metric | Value |
15977
- |---------|--------|
15978
- | Concurrency | ${concurrency1} |
15979
- | Batches | ${naiveBatches} |
15980
- | Est. Time | ${naiveTime}s |
15981
-
15982
- **Strategy 2: Smart Batch (Proposed)**
15983
- | Metric | Value |
15984
- |---------|--------|
15985
- | Concurrency | ${concurrency2} |
15986
- | Batches | ${smartBatches} |
15987
- | Est. Time | ${smartTime}s |
15988
-
15989
- **Summary**
15990
- | Metric | Value |
15991
- |---------|--------|
15992
- | Time Saved | ${timeDiff}s (${improvement}%) |
15993
- | Batches Saved | ${naiveBatches - smartBatches} |
15994
-
15995
- Recommendation: Use **Smart Batch** with concurrency ${concurrency2} for ${timeDiff}s improvement.`;
15996
- }
15997
- });
15998
- function createBatchTools(parallelAgentManager2, client) {
15999
- return {
16000
- process_batch: createProcessBatchTool(parallelAgentManager2, client),
16001
- export_failed_tasks: createExportFailedTasksTool(parallelAgentManager2),
16002
- compare_strategies: createCompareStrategiesTool(parallelAgentManager2)
16003
- };
16004
- }
16005
-
16006
- // src/tools/config.ts
16007
- var createShowConfigTool = () => tool({
16008
- description: `Display current OpenCode Orchestrator configuration.
16009
-
16010
- Shows all dynamic settings including timeouts, concurrency limits, and debug flags.`,
16011
- args: {},
16012
- async execute() {
16013
- configManager.exportConfigs();
16014
- return "";
16015
- }
16016
- });
16017
- var createSetConcurrencyTool = (client) => tool({
16018
- description: `Update concurrency limit for a specific agent type.
16019
-
16020
- <examples>
16021
- set_concurrency({ agent: "builder", limit: 5 })
16022
- set_concurrency({ agent: "inspector", limit: 2 })
16023
- </examples>
16024
-
16025
- <notes>
16026
- - Changes take effect immediately
16027
- - Queued tasks will start when slots become available
16028
- - Limit cannot exceed global max (10)
16029
- </notes>`,
16030
- args: {
16031
- agent: tool.schema.string().describe('Agent type (e.g., "builder", "inspector", "architect")'),
16032
- limit: tool.schema.string().describe("New concurrency limit (number)")
16033
- },
16034
- async execute(args) {
16035
- const { agent, limit } = args;
16036
- const numLimit = parseInt(limit, 10);
16037
- if (isNaN(numLimit) || numLimit < 1) {
16038
- return `\u274C Invalid limit: "${limit}". Must be a number >= 1.`;
16039
- }
16040
- const maxLimit = configManager.getParallelAgentConfig().maxConcurrency;
16041
- if (numLimit > maxLimit) {
16042
- return `\u274C Limit ${numLimit} exceeds global max ${maxLimit}. Using ${maxLimit}.`;
16043
- }
16044
- configManager.updateParallelAgentConfig({
16045
- defaultConcurrency: numLimit
16046
- });
16047
- return `\u2705 **Concurrency Updated**
16048
-
16049
- | Property | Value |
16050
- |----------|-------|
16051
- | **Agent** | ${agent} |
16052
- | **New Limit** | ${numLimit} parallel tasks |
16053
-
16054
- Changes take effect immediately. New tasks will respect to the new limit.`;
16055
- }
16056
- });
16057
- var createSetTimeoutTool = () => tool({
16058
- description: `Update task timeout duration.
16059
-
16060
- <examples>
16061
- set_timeout({ taskTtlMinutes: 45 })
16062
- set_timeout({ cleanupDelayMinutes: 2 })
16063
- </examples>`,
16064
- args: {
16065
- taskTtlMinutes: tool.schema.string().optional().describe("Task timeout in minutes (default: 30)"),
16066
- cleanupDelayMinutes: tool.schema.string().optional().describe("Cleanup delay after completion in minutes (default: 5)")
16067
- },
16068
- async execute(args) {
16069
- const { taskTtlMinutes, cleanupDelayMinutes } = args;
16070
- const updates = {};
16071
- if (taskTtlMinutes) {
16072
- const ttl = parseInt(taskTtlMinutes, 10);
16073
- if (isNaN(ttl) || ttl < 1) {
16074
- return `\u274C Invalid taskTtlMinutes: "${taskTtlMinutes}". Must be number >= 1.`;
16075
- }
16076
- updates.taskTtlMs = ttl * 60 * 1e3;
16077
- }
16078
- if (cleanupDelayMinutes) {
16079
- const delay = parseInt(cleanupDelayMinutes, 10);
16080
- if (isNaN(delay) || delay < 0) {
16081
- return `\u274C Invalid cleanupDelayMinutes: "${cleanupDelayMinutes}". Must be number >= 0.`;
16082
- }
16083
- updates.cleanupDelayMs = delay * 60 * 1e3;
16084
- }
16085
- if (Object.keys(updates).length === 0) {
16086
- return "\u26A0\uFE0F No changes specified. Provide at least one parameter.";
16087
- }
16088
- configManager.updateParallelAgentConfig(updates);
16089
- let output = "\u2705 **Timeout Configuration Updated**\n\n";
16090
- if (updates.taskTtlMs) {
16091
- output += `| Task TTL | ${updates.taskTtlMs / 60 / 1e3} minutes |
16092
- `;
16093
- }
16094
- if (updates.cleanupDelayMs) {
16095
- output += `| Cleanup Delay | ${updates.cleanupDelayMs / 60 / 1e3} minutes |
16096
- `;
16097
- }
16098
- output += "\nChanges take effect immediately.";
16099
- return output;
16100
- }
16101
- });
16102
- var createSetDebugTool = () => tool({
16103
- description: `Enable or disable debug logging.
16104
-
16105
- <examples>
16106
- set_debug({ component: "parallel_agent", enable: true })
16107
- set_debug({ component: "background_task", enable: false })
16108
- </examples>`,
16109
- args: {
16110
- component: tool.schema.string().describe('Component to debug: "parallel_agent", "background_task", or "all"'),
16111
- enable: tool.schema.boolean().describe("Enable (true) or disable (false) debug logs")
16112
- },
16113
- async execute(args) {
16114
- const { component, enable } = args;
16115
- const enableValue = enable ? "true" : "false";
16116
- if (component === "parallel_agent" || component === "all") {
16117
- process.env.OPENCODE_DEBUG_PARALLEL = enableValue;
16118
- configManager.updateParallelAgentConfig({
16119
- enableDebug: enable
16120
- });
16121
- }
16122
- if (component === "background_task" || component === "all") {
16123
- process.env.OPENCODE_DEBUG_BACKGROUND = enableValue;
16124
- configManager.updateBackgroundTaskConfig({
16125
- enableDebug: enable
16126
- });
16127
- }
16128
- return `\u2705 **Debug Logging ${enable ? "Enabled" : "Disabled"}**
16129
-
16130
- | Component | Debug Status |
16131
- |-----------|--------------|
16132
- | ${component === "all" ? "parallel_agent" : component} | ${enable ? "\u{1F527} Enabled" : "\u{1F507} Disabled"} |
16133
- ${component === "all" ? "| background_task | \u{1F527} Enabled |" : ""}
16134
-
16135
- Changes take effect immediately.`;
16136
- }
16137
- });
16138
- function createConfigTools(client) {
16139
- return {
16140
- show_config: createShowConfigTool(),
16141
- set_concurrency: createSetConcurrencyTool(client),
16142
- set_timeout: createSetTimeoutTool(),
16143
- set_debug: createSetDebugTool()
16144
- };
16145
- }
16146
-
16147
15477
  // src/utils/common.ts
16148
15478
  function detectSlashCommand(text) {
16149
15479
  const match = text.trim().match(/^\/([a-zA-Z0-9_-]+)(?:\s+(.*))?$/);
@@ -16200,8 +15530,6 @@ var OrchestratorPlugin = async (input) => {
16200
15530
  const sessions = /* @__PURE__ */ new Map();
16201
15531
  const parallelAgentManager2 = ParallelAgentManager.getInstance(client, directory);
16202
15532
  const asyncAgentTools = createAsyncAgentTools(parallelAgentManager2, client);
16203
- const batchTools = createBatchTools(parallelAgentManager2, client);
16204
- const configTools = createConfigTools(client);
16205
15533
  return {
16206
15534
  // -----------------------------------------------------------------
16207
15535
  // Tools we expose to the LLM
@@ -16219,13 +15547,13 @@ var OrchestratorPlugin = async (input) => {
16219
15547
  list_background: listBackgroundTool,
16220
15548
  kill_background: killBackgroundTool,
16221
15549
  // Async agent tools - spawn agents in parallel sessions
16222
- ...asyncAgentTools,
15550
+ ...asyncAgentTools
16223
15551
  // Git tools - branch info and status
16224
- git_branch: gitBranchTool(directory),
15552
+ // git_branch: gitBranchTool(directory),
16225
15553
  // Smart batch tools - centralized validation and retry
16226
- ...batchTools,
15554
+ // ...batchTools,
16227
15555
  // Configuration tools - dynamic runtime settings
16228
- ...configTools
15556
+ // ...configTools,
16229
15557
  },
16230
15558
  // -----------------------------------------------------------------
16231
15559
  // Config hook - registers our commands and agents with OpenCode
@@ -37,13 +37,13 @@ export declare const listBackgroundTool: {
37
37
  args: {
38
38
  status: import("zod").ZodOptional<import("zod").ZodEnum<{
39
39
  running: "running";
40
- all: "all";
41
40
  done: "done";
42
41
  error: "error";
42
+ all: "all";
43
43
  }>>;
44
44
  };
45
45
  execute(args: {
46
- status?: "running" | "all" | "done" | "error" | undefined;
46
+ status?: "running" | "done" | "error" | "all" | undefined;
47
47
  }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
48
48
  };
49
49
  export declare const killBackgroundTool: {
@@ -33,16 +33,16 @@ export declare const gitBranchTool: (directory: string) => {
33
33
  args: {
34
34
  action: import("zod").ZodEnum<{
35
35
  status: "status";
36
+ all: "all";
36
37
  diff: "diff";
37
38
  current: "current";
38
39
  list: "list";
39
40
  recent: "recent";
40
- all: "all";
41
41
  }>;
42
42
  baseBranch: import("zod").ZodOptional<import("zod").ZodString>;
43
43
  };
44
44
  execute(args: {
45
- action: "status" | "diff" | "current" | "list" | "recent" | "all";
45
+ action: "status" | "all" | "diff" | "current" | "list" | "recent";
46
46
  baseBranch?: string | undefined;
47
47
  }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
48
48
  };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "opencode-orchestrator",
3
3
  "displayName": "OpenCode Orchestrator",
4
4
  "description": "Distributed Cognitive Architecture for OpenCode. Turns simple prompts into specialized multi-agent workflows (Planner, Coder, Reviewer).",
5
- "version": "0.4.20",
5
+ "version": "0.4.22",
6
6
  "author": "agnusdei1207",
7
7
  "license": "MIT",
8
8
  "repository": {
@@ -51,7 +51,7 @@
51
51
  "dev:status": "echo '=== Global Link Status ===' && shx ls -l $(npm root -g)/opencode-orchestrator || echo 'Not linked'",
52
52
  "dev:test": "node dist/scripts/postinstall.js && echo '---' && node dist/scripts/preuninstall.js",
53
53
  "delete": "brew uninstall opencode && rm -rf ~/.config/opencode && npm uninstall -g opencode-orchestrator",
54
- "install": "brew install opencode && npm install -g opencode-orchestrator"
54
+ "setup": "brew install opencode && npm install -g opencode-orchestrator"
55
55
  },
56
56
  "dependencies": {
57
57
  "@opencode-ai/plugin": "^1.1.1",