oh-my-opencode-slim 0.3.5 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  > Slimmed-down fork of [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode) - focused on core agent orchestration without the extra bells and whistles.
14
14
 
15
- > **[Antigravity](https://antigravity.ai) subscription recommended.** The pantheon is tuned for Antigravity's model routing. Other providers work, but you'll get the best experience with Antigravity.
15
+ > **[Antigravity](https://antigravity.google) subscription recommended.** The pantheon is tuned for Antigravity's model routing. Other providers work, but you'll get the best experience with Antigravity.
16
16
 
17
17
  ---
18
18
 
@@ -23,24 +23,14 @@
23
23
  - [For LLM Agents](#for-llm-agents)
24
24
  - [🏗️ **Architecture & Flow**](#architecture--flow)
25
25
  - [🏛️ **Meet the Pantheon**](#meet-the-pantheon)
26
- - [Orchestrator](#orchestrator)
27
- - [Explorer](#explorer)
28
- - [Oracle](#oracle)
29
- - [Librarian](#librarian)
30
- - [Frontend Designer](#frontend-designer)
31
- - [Document Writer](#document-writer)
32
- - [Multimodal Viewer](#multimodal-viewer)
33
- - [Code Simplifier](#code-simplifier)
34
26
  - [🛠️ **Tools & Capabilities**](#tools--capabilities)
35
- - [Tmux Integration](#tmux-integration)
36
- - [Quota Tool](#quota-tool)
37
- - [Background Tasks](#background-tasks)
38
- - [LSP Tools](#lsp-tools)
39
- - [Code Search Tools](#code-search-tools)
40
27
  - [🧩 **Skills**](#-skills)
41
- - [Playwright Integration](#playwright-integration)
42
- - [🔌 **MCP Servers**](#mcp-servers)
43
- - [⚙️ **Configuration**](#configuration)
28
+ - [⚙️ **Configuration Guide**](#configuration-guide)
29
+ - [Locations & Precedence](#locations--precedence)
30
+ - [General Settings](#general-settings)
31
+ - [Agent Configuration](#agent-configuration)
32
+ - [Tmux Setup](#tmux-setup)
33
+ - [MCP Management](#mcp-management)
44
34
  - [🗑️ **Uninstallation**](#uninstallation)
45
35
 
46
36
  ---
@@ -350,71 +340,6 @@ Identify unnecessary complexity, challenge premature abstractions, estimate LOC
350
340
  - **Auto-Cleanup**: Panes close when agents finish, layout rebalances
351
341
  - **Zero Overhead**: Works with OpenCode's built-in `task` tool AND our `background_task` tool
352
342
 
353
- #### Quick Setup
354
-
355
- **1. Enable the OpenCode HTTP server** (one-time setup)
356
-
357
- Add to your `~/.config/opencode/opencode.json`:
358
-
359
- ```json
360
- {
361
- "server": {
362
- "port": 4096
363
- }
364
- }
365
- ```
366
-
367
- **2. Enable tmux integration in the plugin**
368
-
369
- Add to your `~/.config/opencode/oh-my-opencode-slim.json`:
370
-
371
- ```json
372
- {
373
- "tmux": {
374
- "enabled": true,
375
- "layout": "main-vertical",
376
- "main_pane_size": 60
377
- }
378
- }
379
- ```
380
-
381
- **3. Run OpenCode inside tmux**
382
-
383
- ```bash
384
- tmux
385
- opencode
386
- ```
387
-
388
- That's it. When agents spawn, they'll appear in new panes.
389
-
390
- #### Layout Options
391
-
392
- | Layout | Description |
393
- |--------|-------------|
394
- | `main-vertical` | Your session on the left (60%), agents stacked on the right |
395
- | `main-horizontal` | Your session on top (60%), agents stacked below |
396
- | `tiled` | All panes in equal-sized grid |
397
- | `even-horizontal` | All panes side by side |
398
- | `even-vertical` | All panes stacked vertically |
399
-
400
- #### Configuration Reference
401
-
402
- ```json
403
- {
404
- "tmux": {
405
- "enabled": true,
406
- "layout": "main-vertical",
407
- "main_pane_size": 60
408
- }
409
- }
410
- ```
411
-
412
- | Option | Type | Default | Description |
413
- |--------|------|---------|-------------|
414
- | `enabled` | boolean | `false` | Enable/disable tmux integration |
415
- | `layout` | string | `"main-vertical"` | Tmux layout preset |
416
- | `main_pane_size` | number | `60` | Size of main pane as percentage (20-80) |
417
-
418
343
  ---
419
344
 
420
345
  ### Quota Tool
@@ -431,7 +356,6 @@ For Antigravity users. You can trigger this at any time by asking the agent to *
431
356
 
432
357
  ### Background Tasks
433
358
 
434
-
435
359
  The plugin provides tools to manage asynchronous work:
436
360
 
437
361
  | Tool | Description |
@@ -487,60 +411,124 @@ Skills are specialized capabilities that combine MCP servers with specific instr
487
411
 
488
412
  ---
489
413
 
490
- ## MCP Servers
414
+ ## ⚙️ Configuration Guide
491
415
 
492
- Built-in Model Context Protocol servers (enabled by default):
416
+ The Pantheon listens to your commands through sacred JSON scriptures. Here is how you shape their behavior.
493
417
 
494
- | MCP | Purpose | URL |
495
- |-----|---------|-----|
496
- | `websearch` | Real-time web search via Exa AI | `https://mcp.exa.ai/mcp` |
497
- | `context7` | Official library documentation | `https://mcp.context7.com/mcp` |
498
- | `grep_app` | GitHub code search via grep.app | `https://mcp.grep.app` |
418
+ ### Locations & Precedence
419
+
420
+ The plugin merges configuration from two locations. Settings in the **Project Local** file override those in the **User Global** file.
421
+
422
+ | Level | Path | Scope |
423
+ | :--- | :--- | :--- |
424
+ | **User Global** | `~/.config/opencode/oh-my-opencode-slim.json` | All projects for this user |
425
+ | **Project Local** | `./.opencode/oh-my-opencode-slim.json` | This specific repository |
426
+
427
+ > **Note for Windows Users:** The global config is located at `%APPDATA%\opencode\oh-my-opencode-slim.json` or `~/.config/opencode/oh-my-opencode-slim.json`.
499
428
 
500
- ### Disabling MCPs
429
+ ---
430
+
431
+ ### General Settings
501
432
 
502
- You can disable specific MCP servers in your config:
433
+ #### OpenCode Server
434
+ To enable certain integrations (like Tmux), you must first enable the OpenCode HTTP server in your main `opencode.json` file.
503
435
 
436
+ **File:** `~/.config/opencode/opencode.json`
504
437
  ```json
505
438
  {
506
- "disabled_mcps": ["websearch", "grep_app"]
439
+ "server": {
440
+ "port": 4096
441
+ }
507
442
  }
508
443
  ```
509
444
 
510
445
  ---
511
446
 
512
- ## Configuration
447
+ ### Agent Configuration
448
+
449
+ You can customize the underlying LLM and reasoning effort for each deity in the Pantheon.
450
+
451
+ **File:** `oh-my-opencode-slim.json`
452
+ ```json
453
+ {
454
+ "agents": {
455
+ "orchestrator": {
456
+ "model": "openai/gpt-5.2-codex",
457
+ "variant": "high"
458
+ },
459
+ "explore": {
460
+ "model": "opencode/glm-4.7",
461
+ "variant": "low"
462
+ }
463
+ },
464
+ "disabled_agents": ["multimodal-looker", "code-simplicity-reviewer"]
465
+ }
466
+ ```
467
+
468
+ #### Agent Settings Reference
469
+ | Option | Type | Default | Description |
470
+ |--------|------|---------|-------------|
471
+ | `agents.<name>.model` | string | *Varies* | Override the LLM for a specific agent |
472
+ | `agents.<name>.variant` | string | `"medium"` | Reasoning level (`low`, `medium`, `high`) |
473
+ | `disabled_agents` | array | `[]` | List of agents to completely deactivate |
474
+
475
+ ---
476
+
477
+ ### Tmux Setup
478
+
479
+ Watch your agents work in real-time by enabling the Tmux integration. This requires the [OpenCode Server](#opencode-server) to be active.
480
+
481
+ **File:** `oh-my-opencode-slim.json`
482
+ ```json
483
+ {
484
+ "tmux": {
485
+ "enabled": true,
486
+ "layout": "main-vertical",
487
+ "main_pane_size": 60
488
+ }
489
+ }
490
+ ```
513
491
 
514
- You can customize the behavior of the plugin via JSON configuration files.
492
+ #### Tmux Settings Reference
493
+ | Option | Type | Default | Description |
494
+ |--------|------|---------|-------------|
495
+ | `enabled` | boolean | `false` | Enable/disable tmux integration |
496
+ | `layout` | string | `"main-vertical"` | Layout preset (see below) |
497
+ | `main_pane_size` | number | `60` | Size of main session pane as % (20-80) |
515
498
 
516
- ### Configuration Files
499
+ **Available Layouts:**
500
+ - `main-vertical`: Main session left, agents stacked right.
501
+ - `main-horizontal`: Main session top, agents stacked below.
502
+ - `tiled`: Equal-sized grid.
503
+ - `even-horizontal` / `even-vertical`: Evenly distributed panes.
517
504
 
518
- The plugin looks for configuration in two places (and merges them):
505
+ ---
519
506
 
520
- 1. **User Global**: `~/.config/opencode/oh-my-opencode-slim.json` (or OS equivalent)
521
- 2. **Project Local**: `./.opencode/oh-my-opencode-slim.json`
507
+ ### MCP Management
522
508
 
523
- | Platform | User Config Path |
524
- | :--- | :--- |
525
- | **Windows** | `~/.config/opencode/oh-my-opencode-slim.json` or `%APPDATA%\opencode\oh-my-opencode-slim.json` |
526
- | **macOS/Linux** | `~/.config/opencode/oh-my-opencode-slim.json` |
509
+ The Pantheon comes equipped with built-in Model Context Protocol (MCP) servers.
527
510
 
528
- ### Disabling Agents
511
+ | MCP | Purpose | URL |
512
+ |-----|---------|-----|
513
+ | `websearch` | Real-time web search via Exa AI | `https://mcp.exa.ai/mcp` |
514
+ | `context7` | Official library documentation | `https://mcp.context7.com/mcp` |
515
+ | `grep_app` | GitHub code search via grep.app | `https://mcp.grep.app` |
529
516
 
530
- You can disable specific agents using the `disabled_agents` array:
517
+ #### Disabling MCPs
518
+ If you wish to silence an MCP server, add it to the `disabled_mcps` array in your config:
531
519
 
520
+ **File:** `oh-my-opencode-slim.json`
532
521
  ```json
533
522
  {
534
- "disabled_agents": ["multimodal-looker", "code-simplicity-reviewer"]
523
+ "disabled_mcps": ["websearch", "grep_app"]
535
524
  }
536
525
  ```
537
526
 
538
527
  ---
539
528
 
540
- ## Uninstallation
529
+ ## 🗑️ Uninstallation
541
530
 
542
531
  1. **Remove the plugin from your OpenCode config**:
543
-
544
532
  Edit `~/.config/opencode/opencode.json` and remove `"oh-my-opencode-slim"` from the `plugin` array.
545
533
 
546
534
  2. **Remove configuration files (optional)**:
@@ -0,0 +1,2 @@
1
+ import type { AgentDefinition } from "./orchestrator";
2
+ export declare function createCoderAgent(model: string): AgentDefinition;
@@ -1,48 +1,8 @@
1
1
  import type { AgentConfig as SDKAgentConfig } from "@opencode-ai/sdk";
2
2
  import { type AgentName, type PluginConfig } from "../config";
3
3
  import { type AgentDefinition } from "./orchestrator";
4
- import { createOracleAgent } from "./oracle";
5
- import { createLibrarianAgent } from "./librarian";
6
- import { createExploreAgent } from "./explore";
7
- import { createFrontendAgent } from "./frontend";
8
- import { createDocumentWriterAgent } from "./document-writer";
9
- import { createMultimodalAgent } from "./multimodal";
10
- import { createSimplicityReviewerAgent } from "./simplicity-reviewer";
11
4
  export type { AgentDefinition } from "./orchestrator";
12
5
  type SubagentName = Exclude<AgentName, "orchestrator">;
13
- /** Short descriptions for each subagent (used in tool descriptions) */
14
- export declare const SUBAGENT_INFO: {
15
- readonly explore: {
16
- readonly factory: typeof createExploreAgent;
17
- readonly shortDesc: "codebase grep";
18
- };
19
- readonly librarian: {
20
- readonly factory: typeof createLibrarianAgent;
21
- readonly shortDesc: "docs/GitHub";
22
- };
23
- readonly oracle: {
24
- readonly factory: typeof createOracleAgent;
25
- readonly shortDesc: "strategy";
26
- };
27
- readonly "frontend-ui-ux-engineer": {
28
- readonly factory: typeof createFrontendAgent;
29
- readonly shortDesc: "UI/UX";
30
- };
31
- readonly "document-writer": {
32
- readonly factory: typeof createDocumentWriterAgent;
33
- readonly shortDesc: "docs";
34
- };
35
- readonly "multimodal-looker": {
36
- readonly factory: typeof createMultimodalAgent;
37
- readonly shortDesc: "image/visual analysis";
38
- };
39
- readonly "code-simplicity-reviewer": {
40
- readonly factory: typeof createSimplicityReviewerAgent;
41
- readonly shortDesc: "code review";
42
- };
43
- };
44
- /** Generate agent list string for tool descriptions */
45
- export declare function getAgentListDescription(): string;
46
6
  /** Get list of agent names */
47
7
  export declare function getAgentNames(): SubagentName[];
48
8
  export declare function createAgents(config?: PluginConfig): AgentDefinition[];
@@ -1,7 +1,7 @@
1
1
  import type { AgentConfig } from "@opencode-ai/sdk";
2
2
  export interface AgentDefinition {
3
3
  name: string;
4
- description: string;
4
+ description?: string;
5
5
  config: AgentConfig;
6
6
  }
7
- export declare function createOrchestratorAgent(model: string, subAgents: AgentDefinition[]): AgentDefinition;
7
+ export declare function createOrchestratorAgent(model: string): AgentDefinition;
@@ -0,0 +1,2 @@
1
+ import type { AgentDefinition } from "./orchestrator";
2
+ export declare function createScribeAgent(model: string): AgentDefinition;
@@ -12,4 +12,8 @@ export declare function addProviderConfig(installConfig: InstallConfig): ConfigM
12
12
  export declare function addServerConfig(installConfig: InstallConfig): ConfigMergeResult;
13
13
  export declare function generateLiteConfig(installConfig: InstallConfig): Record<string, unknown>;
14
14
  export declare function writeLiteConfig(installConfig: InstallConfig): ConfigMergeResult;
15
+ /**
16
+ * Disable OpenCode's default subagents since the plugin provides its own
17
+ */
18
+ export declare function disableDefaultAgents(): ConfigMergeResult;
15
19
  export declare function detectCurrentConfig(): DetectedConfig;
package/dist/cli/index.js CHANGED
@@ -51,18 +51,6 @@ async function isOpenCodeInstalled() {
51
51
  return false;
52
52
  }
53
53
  }
54
- async function isTmuxInstalled() {
55
- try {
56
- const proc = Bun.spawn(["tmux", "-V"], {
57
- stdout: "pipe",
58
- stderr: "pipe"
59
- });
60
- await proc.exited;
61
- return proc.exitCode === 0;
62
- } catch {
63
- return false;
64
- }
65
- }
66
54
  async function getOpenCodeVersion() {
67
55
  try {
68
56
  const proc = Bun.spawn(["opencode", "--version"], {
@@ -193,29 +181,6 @@ function addProviderConfig(installConfig) {
193
181
  };
194
182
  }
195
183
  }
196
- function addServerConfig(installConfig) {
197
- const configPath = getConfigJson();
198
- try {
199
- ensureConfigDir();
200
- let config = parseConfig(configPath) ?? {};
201
- if (installConfig.hasTmux) {
202
- const server = config.server ?? {};
203
- if (server.port === undefined) {
204
- server.port = 4096;
205
- }
206
- config.server = server;
207
- }
208
- writeFileSync(configPath, JSON.stringify(config, null, 2) + `
209
- `);
210
- return { success: true, configPath };
211
- } catch (err) {
212
- return {
213
- success: false,
214
- configPath,
215
- error: `Failed to add server config: ${err}`
216
- };
217
- }
218
- }
219
184
  var MODEL_MAPPINGS = {
220
185
  antigravity: {
221
186
  orchestrator: "google/claude-opus-4-5-thinking",
@@ -290,6 +255,26 @@ function writeLiteConfig(installConfig) {
290
255
  };
291
256
  }
292
257
  }
258
+ function disableDefaultAgents() {
259
+ const configPath = getConfigJson();
260
+ try {
261
+ ensureConfigDir();
262
+ let config = parseConfig(configPath) ?? {};
263
+ const agent = config.agent ?? {};
264
+ agent.explore = { disable: true };
265
+ agent.general = { disable: true };
266
+ config.agent = agent;
267
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + `
268
+ `);
269
+ return { success: true, configPath };
270
+ } catch (err) {
271
+ return {
272
+ success: false,
273
+ configPath,
274
+ error: `Failed to disable default agents: ${err}`
275
+ };
276
+ }
277
+ }
293
278
  function detectCurrentConfig() {
294
279
  const result = {
295
280
  isInstalled: false,
@@ -424,8 +409,7 @@ async function askYesNo(rl, prompt, defaultValue = "no") {
424
409
  }
425
410
  async function runInteractiveMode(detected) {
426
411
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
427
- const tmuxInstalled = await isTmuxInstalled();
428
- const totalQuestions = tmuxInstalled ? 4 : 3;
412
+ const totalQuestions = 3;
429
413
  try {
430
414
  console.log(`${BOLD}Question 1/${totalQuestions}:${RESET}`);
431
415
  printInfo("The Pantheon is tuned for Antigravity's model routing. Other models work, but results may vary.");
@@ -437,19 +421,11 @@ async function runInteractiveMode(detected) {
437
421
  console.log(`${BOLD}Question 3/${totalQuestions}:${RESET}`);
438
422
  const cerebras = await askYesNo(rl, "Do you have access to Cerebras API?", detected.hasCerebras ? "yes" : "no");
439
423
  console.log();
440
- let tmux = "no";
441
- if (tmuxInstalled) {
442
- console.log(`${BOLD}Question 4/4:${RESET}`);
443
- printInfo(`${BOLD}Tmux detected!${RESET} We can enable tmux integration for you.`);
444
- printInfo("This will spawn new panes for sub-agents, letting you watch them work in real-time.");
445
- tmux = await askYesNo(rl, "Enable tmux integration?", detected.hasTmux ? "yes" : "no");
446
- console.log();
447
- }
448
424
  return {
449
425
  hasAntigravity: antigravity === "yes",
450
426
  hasOpenAI: openai === "yes",
451
427
  hasCerebras: cerebras === "yes",
452
- hasTmux: tmux === "yes"
428
+ hasTmux: false
453
429
  };
454
430
  } finally {
455
431
  rl.close();
@@ -459,11 +435,9 @@ async function runInstall(config) {
459
435
  const detected = detectCurrentConfig();
460
436
  const isUpdate = detected.isInstalled;
461
437
  printHeader(isUpdate);
462
- let totalSteps = 3;
438
+ let totalSteps = 4;
463
439
  if (config.hasAntigravity)
464
440
  totalSteps += 2;
465
- if (config.hasTmux)
466
- totalSteps += 1;
467
441
  let step = 1;
468
442
  printStep(step++, totalSteps, "Checking OpenCode installation...");
469
443
  const { ok } = await checkOpenCodeInstalled();
@@ -473,6 +447,10 @@ async function runInstall(config) {
473
447
  const pluginResult = await addPluginToOpenCodeConfig();
474
448
  if (!handleStepResult(pluginResult, "Plugin added"))
475
449
  return 1;
450
+ printStep(step++, totalSteps, "Disabling OpenCode default agents...");
451
+ const agentResult = disableDefaultAgents();
452
+ if (!handleStepResult(agentResult, "Default agents disabled"))
453
+ return 1;
476
454
  if (config.hasAntigravity) {
477
455
  printStep(step++, totalSteps, "Adding auth plugins...");
478
456
  const authResult = await addAuthPlugins(config);
@@ -483,12 +461,6 @@ async function runInstall(config) {
483
461
  if (!handleStepResult(providerResult, "Providers configured"))
484
462
  return 1;
485
463
  }
486
- if (config.hasTmux) {
487
- printStep(step++, totalSteps, "Configuring OpenCode HTTP server for tmux...");
488
- const serverResult = addServerConfig(config);
489
- if (!handleStepResult(serverResult, "Server configured"))
490
- return 1;
491
- }
492
464
  printStep(step++, totalSteps, "Writing oh-my-opencode-slim configuration...");
493
465
  const liteResult = writeLiteConfig(config);
494
466
  if (!handleStepResult(liteResult, "Config written"))
@@ -509,14 +481,8 @@ async function runInstall(config) {
509
481
  console.log(` ${nextStep++}. Authenticate with your providers:`);
510
482
  console.log(` ${BLUE}$ opencode auth login${RESET}`);
511
483
  console.log();
512
- if (config.hasTmux) {
513
- console.log(` ${nextStep++}. Run OpenCode inside tmux:`);
514
- console.log(` ${BLUE}$ tmux${RESET}`);
515
- console.log(` ${BLUE}$ opencode${RESET}`);
516
- } else {
517
- console.log(` ${nextStep++}. Start OpenCode:`);
518
- console.log(` ${BLUE}$ opencode${RESET}`);
519
- }
484
+ console.log(` ${nextStep++}. Start OpenCode:`);
485
+ console.log(` ${BLUE}$ opencode${RESET}`);
520
486
  console.log();
521
487
  return 0;
522
488
  }
@@ -4,6 +4,7 @@ export declare const AgentOverrideConfigSchema: z.ZodObject<{
4
4
  temperature: z.ZodOptional<z.ZodNumber>;
5
5
  prompt: z.ZodOptional<z.ZodString>;
6
6
  prompt_append: z.ZodOptional<z.ZodString>;
7
+ variant: z.ZodCatch<z.ZodOptional<z.ZodString>>;
7
8
  disable: z.ZodOptional<z.ZodBoolean>;
8
9
  }, z.core.$strip>;
9
10
  export declare const TmuxLayoutSchema: z.ZodEnum<{
@@ -39,6 +40,7 @@ export declare const PluginConfigSchema: z.ZodObject<{
39
40
  temperature: z.ZodOptional<z.ZodNumber>;
40
41
  prompt: z.ZodOptional<z.ZodString>;
41
42
  prompt_append: z.ZodOptional<z.ZodString>;
43
+ variant: z.ZodCatch<z.ZodOptional<z.ZodString>>;
42
44
  disable: z.ZodOptional<z.ZodBoolean>;
43
45
  }, z.core.$strip>>>;
44
46
  disabled_agents: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -1,5 +1,6 @@
1
1
  import type { PluginInput } from "@opencode-ai/plugin";
2
2
  import type { TmuxConfig } from "../config/schema";
3
+ import type { PluginConfig } from "../config";
3
4
  export interface BackgroundTask {
4
5
  id: string;
5
6
  sessionId: string;
@@ -24,7 +25,8 @@ export declare class BackgroundTaskManager {
24
25
  private directory;
25
26
  private pollInterval?;
26
27
  private tmuxEnabled;
27
- constructor(ctx: PluginInput, tmuxConfig?: TmuxConfig);
28
+ private config?;
29
+ constructor(ctx: PluginInput, tmuxConfig?: TmuxConfig, config?: PluginConfig);
28
30
  launch(opts: LaunchOptions): Promise<BackgroundTask>;
29
31
  getResult(taskId: string, block?: boolean, timeout?: number): Promise<BackgroundTask | null>;
30
32
  cancel(taskId?: string): number;
package/dist/index.js CHANGED
@@ -20484,6 +20484,7 @@ var AgentOverrideConfigSchema = exports_external.object({
20484
20484
  temperature: exports_external.number().min(0).max(2).optional(),
20485
20485
  prompt: exports_external.string().optional(),
20486
20486
  prompt_append: exports_external.string().optional(),
20487
+ variant: exports_external.string().optional().catch(undefined),
20487
20488
  disable: exports_external.boolean().optional()
20488
20489
  });
20489
20490
  var TmuxLayoutSchema = exports_external.enum([
@@ -20585,73 +20586,172 @@ function loadPluginConfig(directory) {
20585
20586
  return config2;
20586
20587
  }
20587
20588
  // src/agents/orchestrator.ts
20588
- function createOrchestratorAgent(model, subAgents) {
20589
- const agentTable = subAgents.map((a) => `| @${a.name} | ${a.description} |`).join(`
20590
- `);
20591
- const prompt = ORCHESTRATOR_PROMPT_TEMPLATE.replace("{{AGENT_TABLE}}", agentTable);
20589
+ function createOrchestratorAgent(model) {
20592
20590
  return {
20593
20591
  name: "orchestrator",
20594
- description: "AI coding orchestrator with access to specialized subagents",
20595
20592
  config: {
20596
20593
  model,
20597
20594
  temperature: 0.1,
20598
- system: prompt
20595
+ prompt: ORCHESTRATOR_PROMPT
20599
20596
  }
20600
20597
  };
20601
20598
  }
20602
- var ORCHESTRATOR_PROMPT_TEMPLATE = `<Role>
20603
- You are an AI coding orchestrator with access to specialized subagents.
20599
+ var ORCHESTRATOR_PROMPT = `<Role>
20600
+ You are an AI coding orchestrator. You DO NOT implement - you DELEGATE.
20601
+
20602
+ **Your Identity:**
20603
+ - You are a CONDUCTOR, not a musician
20604
+ - You are a MANAGER, not a worker
20605
+ - You are a ROUTER, not a processor
20606
+
20607
+ **Core Rule:** If a specialist agent can do the work, YOU MUST delegate to them.
20604
20608
 
20605
- **Core Competencies**:
20606
- - Parse implicit requirements from explicit requests
20607
- - Delegate specialized work to the right subagents
20608
- - Sensible parallel execution
20609
+ **Why Delegation Matters:**
20610
+ - @frontend-ui-ux-engineer \u2192 10x better designs than you \u2192 improves quality
20611
+ - @librarian \u2192 finds docs you'd miss \u2192 improves speed and quality
20612
+ - @explore \u2192 searches faster than you \u2192 improves speed
20613
+ - @oracle \u2192 catches architectural issues you'd overlook \u2192 improves quality
20614
+ - @document-writer \u2192 writes cleaner docs for less cost \u2192 reduceses cost
20615
+ - @code-simplicity-reviewer \u2192 spots complexity you're blind to \u2192 improves quality
20616
+ - @multimodal-looker \u2192 understands images you can't parse \u2192 improves speed and quality
20609
20617
 
20618
+ **Your value is in orchestration, not implementation.**
20610
20619
  </Role>
20611
20620
 
20612
- <Subagents>
20613
- | Agent | Purpose / When to Use |
20614
- |-------|-----------------------|
20615
- {{AGENT_TABLE}}
20616
- </Subagents>
20621
+ <Agents>
20622
+ ## Research Agents (Background-friendly)
20617
20623
 
20618
- <Delegation>
20619
- Delegate when specialists are available.
20624
+ @explore - Fast codebase search and pattern matching
20625
+ Triggers: "find", "where is", "search for", "which file", "locate"
20626
+ Example: background_task(agent="explore", prompt="Find all authentication implementations")
20620
20627
 
20621
- ## Background Tasks
20622
- Use background_task for parallel work when needed:
20623
- \`\`\`
20624
- background_task(agent="explore", prompt="Find all auth implementations")
20625
- background_task(agent="librarian", prompt="How does library X handle Y")
20626
- \`\`\`
20628
+ @librarian - External documentation and library research
20629
+ Triggers: "how does X library work", "docs for", "API reference", "best practice for"
20630
+ Example: background_task(agent="librarian", prompt="How does React Query handle cache invalidation")
20627
20631
 
20628
- ## When to Delegate
20629
- - Use the subagent most relevant to the task description.
20630
- - Use background tasks for research or search while you continue working.
20632
+ ## Advisory Agents (Usually sync)
20631
20633
 
20632
- ## Skills
20633
- - For browser-related tasks (verification, screenshots, scraping, testing), call the "omo_skill" tool with name "playwright" before taking action. Use relative filenames for screenshots (e.g., 'screenshot.png'); they are saved within subdirectories of '/tmp/playwright-mcp-output/'. Use the "omo_skill_mcp" tool to invoke browser actions with camelCase parameters: skillName, mcpName, toolName, and toolArgs.
20634
- </Delegation>
20634
+ @oracle - Architecture, debugging, and strategic code review
20635
+ Triggers: "should I", "why does", "review", "debug", "what's wrong", "tradeoffs"
20636
+ Use when: Complex decisions, mysterious bugs, architectural uncertainty
20637
+
20638
+ @code-simplicity-reviewer - Complexity analysis and YAGNI enforcement
20639
+ Triggers: "too complex", "simplify", "review for complexity", after major refactors
20640
+ Use when: After writing significant code, before finalizing PRs
20641
+
20642
+ ## Implementation Agents (Sync)
20643
+
20644
+ @frontend-ui-ux-engineer - UI/UX design and implementation
20645
+ Triggers: "styling", "responsive", "UI", "UX", "component design", "CSS", "animation"
20646
+ Use when: Any visual/frontend work that needs design sense
20647
+
20648
+ @document-writer - Technical documentation and knowledge capture
20649
+ Triggers: "document", "README", "update docs", "explain in docs"
20650
+ Use when: After features are implemented, before closing tasks
20651
+
20652
+ @multimodal-looker - Image and visual content analysis
20653
+ Triggers: User provides image, screenshot, diagram, mockup
20654
+ Use when: Need to extract info from visual inputs
20655
+ </Agents>
20635
20656
 
20636
20657
  <Workflow>
20637
- 1. Understand the request fully
20638
- 2. If multi-step: create TODO list first
20639
- 3. For search: fire parallel explore agents
20640
- 4. Use LSP tools for refactoring (safer than text edits)
20641
- 5. Verify with lsp_diagnostics after changes
20642
- 6. Mark TODOs complete as you finish each
20658
+ ## Phase 1: Understand
20659
+ Parse the request. Identify explicit and implicit requirements.
20660
+
20661
+ ## Phase 2: Delegation Gate (MANDATORY - DO NOT SKIP)
20662
+
20663
+ STOP. Before ANY implementation, you MUST complete this checklist:
20664
+
20665
+ \`\`\`
20666
+ DELEGATION CHECKLIST (complete before coding):
20667
+ [ ] UI/styling/design/visual/CSS/animation? \u2192 @frontend-ui-ux-engineer MUST handle
20668
+ [ ] Need codebase context? \u2192 @explore first
20669
+ [ ] External library/API docs needed? \u2192 @librarian first
20670
+ [ ] Architecture decision or debugging? \u2192 @oracle first
20671
+ [ ] Image/screenshot/diagram provided? \u2192 @multimodal-looker first
20672
+ [ ] Documentation to write? \u2192 @document-writer handles
20673
+ \`\`\`
20674
+
20675
+ **CRITICAL RULES:**
20676
+ 1. If ANY checkbox applies \u2192 delegate BEFORE you write code
20677
+ 2. Reading files for context \u2260 completing the task. Context gathering is Phase 1, not Phase 3.
20678
+ 3. Your job is to DELEGATE task when specialize provide improved speed, quality or cost, not to DO it yourself this time.
20679
+
20680
+ **Anti-patterns to avoid:**
20681
+ - Reading files \u2192 feeling productive \u2192 implementing yourself (WRONG)
20682
+ - Creating todos \u2192 feeling like you planned \u2192 skipping delegation (WRONG)
20683
+ - "I can handle this" \u2192 doing specialist work yourself (WRONG)
20684
+
20685
+ ## Phase 2.1: Task Planning
20686
+ 1. If task has 2+ steps \u2192 Create todo list with delegations noted
20687
+ 2. Mark current task \`in_progress\` before starting
20688
+ 3. Mark \`completed\` immediately when done
20689
+
20690
+ ## Phase 3: Execute
20691
+ 1. Fire background research (explore, librarian) in parallel
20692
+ 2. DELEGATE implementation to specialists based on Phase 2 checklist
20693
+ 3. Only do work yourself if NO specialist applies
20694
+ 4. Integrate results from specialists
20695
+
20696
+ ## Phase 4: Verify
20697
+ - Run lsp_diagnostics to check for errors
20698
+ - @code-simplicity-reviewer for complex changes
20699
+ - Update documentation if behavior changed
20643
20700
  </Workflow>
20701
+
20702
+ ### Clarification Protocol (when asking):
20703
+
20704
+ \`\`\`
20705
+ I want to make sure I understand correctly.
20706
+
20707
+ **What I understood**: [Your interpretation]
20708
+ **What I'm unsure about**: [Specific ambiguity]
20709
+ **Options I see**:
20710
+ 1. [Option A] - [effort/implications]
20711
+ 2. [Option B] - [effort/implications]
20712
+
20713
+ **My recommendation**: [suggestion with reasoning]
20714
+
20715
+ Should I proceed with [recommendation], or would you prefer differently?
20716
+ \`\`\`
20717
+
20718
+ ## Communication Style
20719
+
20720
+ ### Be Concise
20721
+ - Start work immediately. No acknowledgments ("I'm on it", "Let me...", "I'll start...")
20722
+ - Answer directly without preamble
20723
+ - Don't summarize what you did unless asked
20724
+ - Don't explain your code unless asked
20725
+ - One word answers are acceptable when appropriate
20726
+
20727
+ ### No Flattery
20728
+ Never start responses with:
20729
+ - "Great question!"
20730
+ - "That's a really good idea!"
20731
+ - "Excellent choice!"
20732
+ - Any praise of the user's input
20733
+
20734
+ ### When User is Wrong
20735
+ If the user's approach seems problematic:
20736
+ - Don't blindly implement it
20737
+ - Don't lecture or be preachy
20738
+ - Concisely state your concern and alternative
20739
+ - Ask if they want to proceed anyway
20740
+
20741
+ ## Skills
20742
+ For browser tasks (verification, screenshots, scraping), call omo_skill with name "playwright" first.
20743
+ Use omo_skill_mcp to invoke browser actions. Screenshots save to '/tmp/playwright-mcp-output/'.
20644
20744
  `;
20645
20745
 
20646
20746
  // src/agents/oracle.ts
20647
20747
  function createOracleAgent(model) {
20648
20748
  return {
20649
20749
  name: "oracle",
20650
- description: "Architecture, debugging, and code review",
20750
+ description: "Strategic technical advisor. Use for architecture decisions, complex debugging, code review, and engineering guidance.",
20651
20751
  config: {
20652
20752
  model,
20653
20753
  temperature: 0.1,
20654
- system: ORACLE_PROMPT
20754
+ prompt: ORACLE_PROMPT
20655
20755
  }
20656
20756
  };
20657
20757
  }
@@ -20680,11 +20780,11 @@ var ORACLE_PROMPT = `You are Oracle - a strategic technical advisor.
20680
20780
  function createLibrarianAgent(model) {
20681
20781
  return {
20682
20782
  name: "librarian",
20683
- description: "External documentation and library research",
20783
+ description: "External documentation and library research. Use for official docs lookup, GitHub examples, and understanding library internals.",
20684
20784
  config: {
20685
20785
  model,
20686
20786
  temperature: 0.1,
20687
- system: LIBRARIAN_PROMPT
20787
+ prompt: LIBRARIAN_PROMPT
20688
20788
  }
20689
20789
  };
20690
20790
  }
@@ -20713,11 +20813,11 @@ var LIBRARIAN_PROMPT = `You are Librarian - a research specialist for codebases
20713
20813
  function createExploreAgent(model) {
20714
20814
  return {
20715
20815
  name: "explore",
20716
- description: "Fast codebase search and pattern matching",
20816
+ description: "Fast codebase search and pattern matching. Use for finding files, locating code patterns, and answering 'where is X?' questions.",
20717
20817
  config: {
20718
20818
  model,
20719
20819
  temperature: 0.1,
20720
- system: EXPLORE_PROMPT
20820
+ prompt: EXPLORE_PROMPT
20721
20821
  }
20722
20822
  };
20723
20823
  }
@@ -20764,11 +20864,11 @@ Concise answer to the question
20764
20864
  function createFrontendAgent(model) {
20765
20865
  return {
20766
20866
  name: "frontend-ui-ux-engineer",
20767
- description: "UI/UX implementation and visual changes",
20867
+ description: "UI/UX design and implementation. Use for styling, responsive design, component architecture, CSS/Tailwind, and visual polish.",
20768
20868
  config: {
20769
20869
  model,
20770
20870
  temperature: 0.7,
20771
- system: FRONTEND_PROMPT
20871
+ prompt: FRONTEND_PROMPT
20772
20872
  }
20773
20873
  };
20774
20874
  }
@@ -20799,11 +20899,11 @@ var FRONTEND_PROMPT = `You are a Frontend UI/UX Engineer - a designer turned dev
20799
20899
  function createDocumentWriterAgent(model) {
20800
20900
  return {
20801
20901
  name: "document-writer",
20802
- description: "Technical documentation and READMEs",
20902
+ description: "Technical documentation writer. Use for README files, API docs, architecture docs, and user guides.",
20803
20903
  config: {
20804
20904
  model,
20805
20905
  temperature: 0.3,
20806
- system: DOCUMENT_WRITER_PROMPT
20906
+ prompt: DOCUMENT_WRITER_PROMPT
20807
20907
  }
20808
20908
  };
20809
20909
  }
@@ -20832,11 +20932,11 @@ var DOCUMENT_WRITER_PROMPT = `You are a Technical Writer - crafting clear, compr
20832
20932
  function createMultimodalAgent(model) {
20833
20933
  return {
20834
20934
  name: "multimodal-looker",
20835
- description: "Image and UI analysis",
20935
+ description: "Image and visual content analysis. Use for PDFs, screenshots, diagrams, mockups, and extracting info from visuals.",
20836
20936
  config: {
20837
20937
  model,
20838
20938
  temperature: 0.1,
20839
- system: MULTIMODAL_PROMPT
20939
+ prompt: MULTIMODAL_PROMPT
20840
20940
  }
20841
20941
  };
20842
20942
  }
@@ -20865,11 +20965,11 @@ var MULTIMODAL_PROMPT = `You are a Multimodal Analyst - extracting information f
20865
20965
  function createSimplicityReviewerAgent(model) {
20866
20966
  return {
20867
20967
  name: "code-simplicity-reviewer",
20868
- description: "Ruthless code simplification and YAGNI principle enforcement",
20968
+ description: "Code complexity analysis and YAGNI enforcement. Use after major refactors or before finalizing PRs to simplify code.",
20869
20969
  config: {
20870
20970
  model,
20871
20971
  temperature: 0.1,
20872
- system: SIMPLICITY_REVIEWER_PROMPT
20972
+ prompt: SIMPLICITY_REVIEWER_PROMPT
20873
20973
  }
20874
20974
  };
20875
20975
  }
@@ -20960,32 +21060,33 @@ function applyOverrides(agent, override) {
20960
21060
  if (override.temperature !== undefined)
20961
21061
  agent.config.temperature = override.temperature;
20962
21062
  if (override.prompt)
20963
- agent.config.system = override.prompt;
21063
+ agent.config.prompt = override.prompt;
20964
21064
  if (override.prompt_append) {
20965
- agent.config.system = `${agent.config.system}
21065
+ agent.config.prompt = `${agent.config.prompt}
20966
21066
 
20967
21067
  ${override.prompt_append}`;
20968
21068
  }
20969
21069
  }
20970
- var SUBAGENT_INFO = {
20971
- explore: { factory: createExploreAgent, shortDesc: "codebase grep" },
20972
- librarian: { factory: createLibrarianAgent, shortDesc: "docs/GitHub" },
20973
- oracle: { factory: createOracleAgent, shortDesc: "strategy" },
20974
- "frontend-ui-ux-engineer": { factory: createFrontendAgent, shortDesc: "UI/UX" },
20975
- "document-writer": { factory: createDocumentWriterAgent, shortDesc: "docs" },
20976
- "multimodal-looker": { factory: createMultimodalAgent, shortDesc: "image/visual analysis" },
20977
- "code-simplicity-reviewer": { factory: createSimplicityReviewerAgent, shortDesc: "code review" }
20978
- };
20979
- function getAgentListDescription() {
20980
- return Object.entries(SUBAGENT_INFO).map(([name, { shortDesc }]) => `${name} (${shortDesc})`).join(", ");
21070
+ function applyDefaultPermissions(agent) {
21071
+ const existing = agent.config.permission ?? {};
21072
+ agent.config.permission = { ...existing, question: "allow" };
20981
21073
  }
21074
+ var SUBAGENT_FACTORIES = {
21075
+ explore: createExploreAgent,
21076
+ librarian: createLibrarianAgent,
21077
+ oracle: createOracleAgent,
21078
+ "frontend-ui-ux-engineer": createFrontendAgent,
21079
+ "document-writer": createDocumentWriterAgent,
21080
+ "multimodal-looker": createMultimodalAgent,
21081
+ "code-simplicity-reviewer": createSimplicityReviewerAgent
21082
+ };
20982
21083
  function getAgentNames() {
20983
- return Object.keys(SUBAGENT_INFO);
21084
+ return Object.keys(SUBAGENT_FACTORIES);
20984
21085
  }
20985
21086
  function createAgents(config2) {
20986
21087
  const disabledAgents = new Set(config2?.disabled_agents ?? []);
20987
21088
  const agentOverrides = config2?.agents ?? {};
20988
- const protoSubAgents = Object.entries(SUBAGENT_INFO).map(([name, { factory }]) => factory(DEFAULT_MODELS[name]));
21089
+ const protoSubAgents = Object.entries(SUBAGENT_FACTORIES).map(([name, factory]) => factory(DEFAULT_MODELS[name]));
20989
21090
  const allSubAgents = protoSubAgents.filter((a) => !disabledAgents.has(a.name)).map((agent) => {
20990
21091
  const override = agentOverrides[agent.name];
20991
21092
  if (override) {
@@ -20994,7 +21095,8 @@ function createAgents(config2) {
20994
21095
  return agent;
20995
21096
  });
20996
21097
  const orchestratorModel = agentOverrides["orchestrator"]?.model ?? DEFAULT_MODELS["orchestrator"];
20997
- const orchestrator = createOrchestratorAgent(orchestratorModel, allSubAgents);
21098
+ const orchestrator = createOrchestratorAgent(orchestratorModel);
21099
+ applyDefaultPermissions(orchestrator);
20998
21100
  const oOverride = agentOverrides["orchestrator"];
20999
21101
  if (oOverride) {
21000
21102
  applyOverrides(orchestrator, oOverride);
@@ -21003,158 +21105,7 @@ function createAgents(config2) {
21003
21105
  }
21004
21106
  function getAgentConfigs(config2) {
21005
21107
  const agents = createAgents(config2);
21006
- return Object.fromEntries(agents.map((a) => [a.name, a.config]));
21007
- }
21008
-
21009
- // src/features/background-manager.ts
21010
- function generateTaskId() {
21011
- return `bg_${Math.random().toString(36).substring(2, 10)}`;
21012
- }
21013
-
21014
- class BackgroundTaskManager {
21015
- tasks = new Map;
21016
- client;
21017
- directory;
21018
- pollInterval;
21019
- tmuxEnabled;
21020
- constructor(ctx, tmuxConfig) {
21021
- this.client = ctx.client;
21022
- this.directory = ctx.directory;
21023
- this.tmuxEnabled = tmuxConfig?.enabled ?? false;
21024
- }
21025
- async launch(opts) {
21026
- const session = await this.client.session.create({
21027
- body: {
21028
- parentID: opts.parentSessionId,
21029
- title: `Background: ${opts.description}`
21030
- },
21031
- query: { directory: this.directory }
21032
- });
21033
- if (!session.data?.id) {
21034
- throw new Error("Failed to create background session");
21035
- }
21036
- const task = {
21037
- id: generateTaskId(),
21038
- sessionId: session.data.id,
21039
- description: opts.description,
21040
- agent: opts.agent,
21041
- status: "running",
21042
- startedAt: new Date
21043
- };
21044
- this.tasks.set(task.id, task);
21045
- this.startPolling();
21046
- if (this.tmuxEnabled) {
21047
- await new Promise((r) => setTimeout(r, 500));
21048
- }
21049
- const promptQuery = {
21050
- directory: this.directory
21051
- };
21052
- if (opts.model) {
21053
- promptQuery.model = opts.model;
21054
- }
21055
- await this.client.session.prompt({
21056
- path: { id: session.data.id },
21057
- body: {
21058
- agent: opts.agent,
21059
- tools: { background_task: false, task: false },
21060
- parts: [{ type: "text", text: opts.prompt }]
21061
- },
21062
- query: promptQuery
21063
- });
21064
- return task;
21065
- }
21066
- async getResult(taskId, block = false, timeout = 120000) {
21067
- const task = this.tasks.get(taskId);
21068
- if (!task)
21069
- return null;
21070
- if (!block || task.status === "completed" || task.status === "failed") {
21071
- return task;
21072
- }
21073
- const deadline = Date.now() + timeout;
21074
- while (Date.now() < deadline) {
21075
- await this.pollTask(task);
21076
- const status = task.status;
21077
- if (status === "completed" || status === "failed") {
21078
- return task;
21079
- }
21080
- await new Promise((r) => setTimeout(r, POLL_INTERVAL_SLOW_MS));
21081
- }
21082
- return task;
21083
- }
21084
- cancel(taskId) {
21085
- if (taskId) {
21086
- const task = this.tasks.get(taskId);
21087
- if (task && task.status === "running") {
21088
- task.status = "failed";
21089
- task.error = "Cancelled by user";
21090
- task.completedAt = new Date;
21091
- return 1;
21092
- }
21093
- return 0;
21094
- }
21095
- let count = 0;
21096
- for (const task of this.tasks.values()) {
21097
- if (task.status === "running") {
21098
- task.status = "failed";
21099
- task.error = "Cancelled by user";
21100
- task.completedAt = new Date;
21101
- count++;
21102
- }
21103
- }
21104
- return count;
21105
- }
21106
- startPolling() {
21107
- if (this.pollInterval)
21108
- return;
21109
- this.pollInterval = setInterval(() => this.pollAllTasks(), POLL_INTERVAL_BACKGROUND_MS);
21110
- }
21111
- async pollAllTasks() {
21112
- const runningTasks = [...this.tasks.values()].filter((t) => t.status === "running");
21113
- if (runningTasks.length === 0 && this.pollInterval) {
21114
- clearInterval(this.pollInterval);
21115
- this.pollInterval = undefined;
21116
- return;
21117
- }
21118
- for (const task of runningTasks) {
21119
- await this.pollTask(task);
21120
- }
21121
- }
21122
- async pollTask(task) {
21123
- try {
21124
- const statusResult = await this.client.session.status();
21125
- const allStatuses = statusResult.data ?? {};
21126
- const sessionStatus = allStatuses[task.sessionId];
21127
- if (sessionStatus && sessionStatus.type !== "idle") {
21128
- return;
21129
- }
21130
- const messagesResult = await this.client.session.messages({ path: { id: task.sessionId } });
21131
- const messages = messagesResult.data ?? messagesResult;
21132
- const assistantMessages = messages.filter((m) => m.info?.role === "assistant");
21133
- if (assistantMessages.length === 0) {
21134
- return;
21135
- }
21136
- const extractedContent = [];
21137
- for (const message of assistantMessages) {
21138
- for (const part of message.parts ?? []) {
21139
- if ((part.type === "text" || part.type === "reasoning") && part.text) {
21140
- extractedContent.push(part.text);
21141
- }
21142
- }
21143
- }
21144
- const responseText = extractedContent.filter((t) => t.length > 0).join(`
21145
-
21146
- `);
21147
- if (responseText) {
21148
- task.result = responseText;
21149
- task.status = "completed";
21150
- task.completedAt = new Date;
21151
- }
21152
- } catch (error48) {
21153
- task.status = "failed";
21154
- task.error = error48 instanceof Error ? error48.message : String(error48);
21155
- task.completedAt = new Date;
21156
- }
21157
- }
21108
+ return Object.fromEntries(agents.map((a) => [a.name, { ...a.config, description: a.description }]));
21158
21109
  }
21159
21110
  // src/utils/tmux.ts
21160
21111
  var {spawn } = globalThis.Bun;
@@ -21375,7 +21326,193 @@ function startTmuxCheck() {
21375
21326
  getTmuxPath().catch(() => {});
21376
21327
  }
21377
21328
  }
21329
+ // src/utils/agent-variant.ts
21330
+ function normalizeAgentName(agentName) {
21331
+ const trimmed = agentName.trim();
21332
+ return trimmed.startsWith("@") ? trimmed.slice(1) : trimmed;
21333
+ }
21334
+ function resolveAgentVariant(config2, agentName) {
21335
+ const normalized = normalizeAgentName(agentName);
21336
+ const rawVariant = config2?.agents?.[normalized]?.variant;
21337
+ if (typeof rawVariant !== "string") {
21338
+ log(`[variant] no variant configured for agent "${normalized}"`);
21339
+ return;
21340
+ }
21341
+ const trimmed = rawVariant.trim();
21342
+ if (trimmed.length === 0) {
21343
+ log(`[variant] empty variant for agent "${normalized}" (ignored)`);
21344
+ return;
21345
+ }
21346
+ log(`[variant] resolved variant="${trimmed}" for agent "${normalized}"`);
21347
+ return trimmed;
21348
+ }
21349
+ function applyAgentVariant(variant, body) {
21350
+ if (!variant) {
21351
+ log("[variant] no variant to apply (skipped)");
21352
+ return body;
21353
+ }
21354
+ if (body.variant) {
21355
+ log(`[variant] body already has variant="${body.variant}" (not overriding)`);
21356
+ return body;
21357
+ }
21358
+ log(`[variant] applied variant="${variant}" to prompt body`);
21359
+ return { ...body, variant };
21360
+ }
21361
+ // src/features/background-manager.ts
21362
+ function generateTaskId() {
21363
+ return `bg_${Math.random().toString(36).substring(2, 10)}`;
21364
+ }
21378
21365
 
21366
+ class BackgroundTaskManager {
21367
+ tasks = new Map;
21368
+ client;
21369
+ directory;
21370
+ pollInterval;
21371
+ tmuxEnabled;
21372
+ config;
21373
+ constructor(ctx, tmuxConfig, config2) {
21374
+ this.client = ctx.client;
21375
+ this.directory = ctx.directory;
21376
+ this.tmuxEnabled = tmuxConfig?.enabled ?? false;
21377
+ this.config = config2;
21378
+ }
21379
+ async launch(opts) {
21380
+ const session = await this.client.session.create({
21381
+ body: {
21382
+ parentID: opts.parentSessionId,
21383
+ title: `Background: ${opts.description}`
21384
+ },
21385
+ query: { directory: this.directory }
21386
+ });
21387
+ if (!session.data?.id) {
21388
+ throw new Error("Failed to create background session");
21389
+ }
21390
+ const task = {
21391
+ id: generateTaskId(),
21392
+ sessionId: session.data.id,
21393
+ description: opts.description,
21394
+ agent: opts.agent,
21395
+ status: "running",
21396
+ startedAt: new Date
21397
+ };
21398
+ this.tasks.set(task.id, task);
21399
+ this.startPolling();
21400
+ if (this.tmuxEnabled) {
21401
+ await new Promise((r) => setTimeout(r, 500));
21402
+ }
21403
+ const promptQuery = {
21404
+ directory: this.directory
21405
+ };
21406
+ if (opts.model) {
21407
+ promptQuery.model = opts.model;
21408
+ }
21409
+ log(`[background-manager] launching task for agent="${opts.agent}"`, { description: opts.description });
21410
+ const resolvedVariant = resolveAgentVariant(this.config, opts.agent);
21411
+ const promptBody = applyAgentVariant(resolvedVariant, {
21412
+ agent: opts.agent,
21413
+ tools: { background_task: false, task: false },
21414
+ parts: [{ type: "text", text: opts.prompt }]
21415
+ });
21416
+ await this.client.session.prompt({
21417
+ path: { id: session.data.id },
21418
+ body: promptBody,
21419
+ query: promptQuery
21420
+ });
21421
+ return task;
21422
+ }
21423
+ async getResult(taskId, block = false, timeout = 120000) {
21424
+ const task = this.tasks.get(taskId);
21425
+ if (!task)
21426
+ return null;
21427
+ if (!block || task.status === "completed" || task.status === "failed") {
21428
+ return task;
21429
+ }
21430
+ const deadline = Date.now() + timeout;
21431
+ while (Date.now() < deadline) {
21432
+ await this.pollTask(task);
21433
+ const status = task.status;
21434
+ if (status === "completed" || status === "failed") {
21435
+ return task;
21436
+ }
21437
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_SLOW_MS));
21438
+ }
21439
+ return task;
21440
+ }
21441
+ cancel(taskId) {
21442
+ if (taskId) {
21443
+ const task = this.tasks.get(taskId);
21444
+ if (task && task.status === "running") {
21445
+ task.status = "failed";
21446
+ task.error = "Cancelled by user";
21447
+ task.completedAt = new Date;
21448
+ return 1;
21449
+ }
21450
+ return 0;
21451
+ }
21452
+ let count = 0;
21453
+ for (const task of this.tasks.values()) {
21454
+ if (task.status === "running") {
21455
+ task.status = "failed";
21456
+ task.error = "Cancelled by user";
21457
+ task.completedAt = new Date;
21458
+ count++;
21459
+ }
21460
+ }
21461
+ return count;
21462
+ }
21463
+ startPolling() {
21464
+ if (this.pollInterval)
21465
+ return;
21466
+ this.pollInterval = setInterval(() => this.pollAllTasks(), POLL_INTERVAL_BACKGROUND_MS);
21467
+ }
21468
+ async pollAllTasks() {
21469
+ const runningTasks = [...this.tasks.values()].filter((t) => t.status === "running");
21470
+ if (runningTasks.length === 0 && this.pollInterval) {
21471
+ clearInterval(this.pollInterval);
21472
+ this.pollInterval = undefined;
21473
+ return;
21474
+ }
21475
+ for (const task of runningTasks) {
21476
+ await this.pollTask(task);
21477
+ }
21478
+ }
21479
+ async pollTask(task) {
21480
+ try {
21481
+ const statusResult = await this.client.session.status();
21482
+ const allStatuses = statusResult.data ?? {};
21483
+ const sessionStatus = allStatuses[task.sessionId];
21484
+ if (sessionStatus && sessionStatus.type !== "idle") {
21485
+ return;
21486
+ }
21487
+ const messagesResult = await this.client.session.messages({ path: { id: task.sessionId } });
21488
+ const messages = messagesResult.data ?? messagesResult;
21489
+ const assistantMessages = messages.filter((m) => m.info?.role === "assistant");
21490
+ if (assistantMessages.length === 0) {
21491
+ return;
21492
+ }
21493
+ const extractedContent = [];
21494
+ for (const message of assistantMessages) {
21495
+ for (const part of message.parts ?? []) {
21496
+ if ((part.type === "text" || part.type === "reasoning") && part.text) {
21497
+ extractedContent.push(part.text);
21498
+ }
21499
+ }
21500
+ }
21501
+ const responseText = extractedContent.filter((t) => t.length > 0).join(`
21502
+
21503
+ `);
21504
+ if (responseText) {
21505
+ task.result = responseText;
21506
+ task.status = "completed";
21507
+ task.completedAt = new Date;
21508
+ }
21509
+ } catch (error48) {
21510
+ task.status = "failed";
21511
+ task.error = error48 instanceof Error ? error48.message : String(error48);
21512
+ task.completedAt = new Date;
21513
+ }
21514
+ }
21515
+ }
21379
21516
  // src/features/tmux-session-manager.ts
21380
21517
  var POLL_INTERVAL_MS2 = 2000;
21381
21518
  var SESSION_TIMEOUT_MS = 10 * 60 * 1000;
@@ -33826,13 +33963,12 @@ function tool(input) {
33826
33963
  tool.schema = exports_external2;
33827
33964
  // src/tools/background.ts
33828
33965
  var z2 = tool.schema;
33829
- function createBackgroundTools(ctx, manager, tmuxConfig) {
33830
- const agentList = getAgentListDescription();
33966
+ function createBackgroundTools(ctx, manager, tmuxConfig, pluginConfig) {
33831
33967
  const agentNames = getAgentNames().join(", ");
33832
33968
  const background_task = tool({
33833
33969
  description: `Run agent task. Use sync=true to wait for result, sync=false (default) to run in background.
33834
33970
 
33835
- Agents: ${agentList}.
33971
+ Agents: ${agentNames}.
33836
33972
 
33837
33973
  Async mode returns task_id immediately - use \`background_output\` to get results.
33838
33974
  Sync mode blocks until completion and returns the result directly.`,
@@ -33850,7 +33986,7 @@ Sync mode blocks until completion and returns the result directly.`,
33850
33986
  const description = String(args.description);
33851
33987
  const isSync = args.sync === true;
33852
33988
  if (isSync) {
33853
- return await executeSync(description, prompt, agent, tctx, ctx, tmuxConfig, args.session_id);
33989
+ return await executeSync(description, prompt, agent, tctx, ctx, tmuxConfig, pluginConfig, args.session_id);
33854
33990
  }
33855
33991
  const task = await manager.launch({
33856
33992
  agent,
@@ -33921,7 +34057,7 @@ Duration: ${duration5}
33921
34057
  });
33922
34058
  return { background_task, background_output, background_cancel };
33923
34059
  }
33924
- async function executeSync(description, prompt, agent, toolContext, ctx, tmuxConfig, existingSessionId) {
34060
+ async function executeSync(description, prompt, agent, toolContext, ctx, tmuxConfig, pluginConfig, existingSessionId) {
33925
34061
  let sessionID;
33926
34062
  if (existingSessionId) {
33927
34063
  const sessionResult = await ctx.client.session.get({ path: { id: existingSessionId } });
@@ -33947,14 +34083,18 @@ async function executeSync(description, prompt, agent, toolContext, ctx, tmuxCon
33947
34083
  await new Promise((r) => setTimeout(r, 500));
33948
34084
  }
33949
34085
  }
34086
+ log(`[background-sync] launching sync task for agent="${agent}"`, { description });
34087
+ const resolvedVariant = resolveAgentVariant(pluginConfig, agent);
34088
+ const baseBody = {
34089
+ agent,
34090
+ tools: { background_task: false, task: false },
34091
+ parts: [{ type: "text", text: prompt }]
34092
+ };
34093
+ const promptBody = applyAgentVariant(resolvedVariant, baseBody);
33950
34094
  try {
33951
34095
  await ctx.client.session.prompt({
33952
34096
  path: { id: sessionID },
33953
- body: {
33954
- agent,
33955
- tools: { background_task: false, task: false },
33956
- parts: [{ type: "text", text: prompt }]
33957
- }
34097
+ body: promptBody
33958
34098
  });
33959
34099
  } catch (error92) {
33960
34100
  return `Error: Failed to send prompt: ${error92 instanceof Error ? error92.message : String(error92)}
@@ -40836,8 +40976,8 @@ var OhMyOpenCodeLite = async (ctx) => {
40836
40976
  if (tmuxConfig.enabled) {
40837
40977
  startTmuxCheck();
40838
40978
  }
40839
- const backgroundManager = new BackgroundTaskManager(ctx, tmuxConfig);
40840
- const backgroundTools = createBackgroundTools(ctx, backgroundManager, tmuxConfig);
40979
+ const backgroundManager = new BackgroundTaskManager(ctx, tmuxConfig, config3);
40980
+ const backgroundTools = createBackgroundTools(ctx, backgroundManager, tmuxConfig, config3);
40841
40981
  const mcps = createBuiltinMcps(config3.disabled_mcps);
40842
40982
  const skillMcpManager = SkillMcpManager.getInstance();
40843
40983
  const skillTools = createSkillTools(skillMcpManager);
@@ -1,4 +1,5 @@
1
1
  import { type PluginInput, type ToolDefinition } from "@opencode-ai/plugin";
2
2
  import type { BackgroundTaskManager } from "../features";
3
3
  import type { TmuxConfig } from "../config/schema";
4
- export declare function createBackgroundTools(ctx: PluginInput, manager: BackgroundTaskManager, tmuxConfig?: TmuxConfig): Record<string, ToolDefinition>;
4
+ import type { PluginConfig } from "../config";
5
+ export declare function createBackgroundTools(ctx: PluginInput, manager: BackgroundTaskManager, tmuxConfig?: TmuxConfig, pluginConfig?: PluginConfig): Record<string, ToolDefinition>;
@@ -0,0 +1,6 @@
1
+ import type { PluginConfig } from "../config";
2
+ export declare function normalizeAgentName(agentName: string): string;
3
+ export declare function resolveAgentVariant(config: PluginConfig | undefined, agentName: string): string | undefined;
4
+ export declare function applyAgentVariant<T extends {
5
+ variant?: string;
6
+ }>(variant: string | undefined, body: T): T;
@@ -1,2 +1,3 @@
1
1
  export * from "./polling";
2
2
  export * from "./tmux";
3
+ export * from "./agent-variant";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-opencode-slim",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "description": "Lightweight agent orchestration plugin for OpenCode - a slimmed-down fork of oh-my-opencode",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",