Browse Source

Merge pull request #536 from alvinunreal/v2-release

V2 release
Alvin 1 month ago
parent
commit
326f7f6238

+ 76 - 38
README.md

@@ -1,12 +1,12 @@
 <div align="center">
 <div align="center">
   <a href="https://github.com/alvinunreal/oh-my-opencode-slim/stargazers">
   <a href="https://github.com/alvinunreal/oh-my-opencode-slim/stargazers">
-    <img src="img/4k.png" alt="4K GitHub Stars Milestone" style="border-radius: 10px;">
+    <img src="img/v2.webp" alt="oh-my-opencode-slim V2 Release" style="border-radius: 10px;">
   </a>
   </a>
-  <h3>✨ Default Background Orchestration Has Arrived ✨</h3>
-  <p><i>The orchestrator now manages the workflow and schedules specialist agents in the background,<br>while <code>/deepwork</code> turns big goals into file-backed plans.<br>Share feedback and questions with us on Telegram.</i></p>
+  <h3>✨ oh-my-opencode-slim ✨</h3>
 
 
-  <p><b>Open Multi Agent Suite</b> · Mix any models · Auto delegate tasks</p>
+  <p><i>Seven divine beings emerged from the dawn of code, each an immortal master of their craft,<br>awaiting your command to forge order from chaos and build what was once thought impossible.</i></p>
 
 
+  <p><b>Opencode Multi Agent Suite</b> · Mix any models · Auto delegate tasks</p>
   <p><sub>by <b>Boring Dystopia Development</b></sub></p>
   <p><sub>by <b>Boring Dystopia Development</b></sub></p>
   <p>
   <p>
     <a href="https://boringdystopia.ai/"><img src="https://img.shields.io/badge/boringdystopia.ai-111111?style=for-the-badge&logo=vercel&logoColor=white" alt="boringdystopia.ai"></a>&nbsp;
     <a href="https://boringdystopia.ai/"><img src="https://img.shields.io/badge/boringdystopia.ai-111111?style=for-the-badge&logo=vercel&logoColor=white" alt="boringdystopia.ai"></a>&nbsp;
@@ -20,20 +20,8 @@
 
 
   <p><sub>✦ ✦ ✦</sub></p>
   <p><sub>✦ ✦ ✦</sub></p>
 
 
-## Support the Project 💖
-
-oh-my-opencode-slim is built and maintained in my spare time. If it helps you consider sponsoring my codex subscription.
-
-**Goal: $200/month**
-
-[![Sponsor](https://img.shields.io/github/sponsors/alvinunreal?style=for-the-badge&logo=GitHub-Sponsors&logoColor=EA4AAA&label=Sponsor)](https://github.com/sponsors/alvinunreal)
-
-See live progress + become a sponsor → [GitHub Sponsors](https://github.com/sponsors/alvinunreal)
-
 </div>
 </div>
 
 
----
-
 ## What's This Plugin
 ## What's This Plugin
 
 
 oh-my-opencode-slim is an agent orchestration plugin for OpenCode. It includes a built-in team of specialized agents that can scout a codebase, look up fresh documentation, review architecture, handle UI work, and execute well-scoped implementation tasks under one orchestrator.
 oh-my-opencode-slim is an agent orchestration plugin for OpenCode. It includes a built-in team of specialized agents that can scout a codebase, look up fresh documentation, review architecture, handle UI work, and execute well-scoped implementation tasks under one orchestrator.
@@ -58,27 +46,83 @@ Install and configure oh-my-opencode-slim: https://raw.githubusercontent.com/alv
 bunx oh-my-opencode-slim@latest install
 bunx oh-my-opencode-slim@latest install
 ```
 ```
 
 
-### Default Background Orchestration
+### What's New in V2
+
+V2 turns oh-my-opencode-slim into a scheduler-first multi-agent workflow system.
+The Orchestrator stays focused on planning, delegation, reconciliation, and
+verification while specialists do the work in their own lanes.
 
 
-The orchestrator is now a workflow manager and scheduler, not the main coding
-worker: it plans work, dispatches specialists as background tasks, receives
-completion events from OpenCode or checks status only when needed, then
-reconciles results before continuing. This uses OpenCode's native background
-subagent support, so OpenCode must run with the background-subagents environment
-variable enabled.
+- **[Background agents](#background-agents)** — the Orchestrator now dispatches
+  specialists as background tasks, tracks task/session IDs, waits for completion
+  events, and reconciles results before continuing.
+- **[Companion](#companion)** — an optional floating desktop window shows which
+  agents are currently active, including parallel background specialists.
+- **[Deepwork](#deepwork)** — a structured workflow for large, multi-file, risky,
+  or phased coding work using persistent plan files and Oracle review gates.
+- **[oh-my-opencode-slim skill](#oh-my-opencode-slim-skill)** — a bundled
+  configuration skill that helps tune models, prompts, custom agents, MCP access,
+  presets, and plugin behavior safely.
+
+#### Background Agents
+
+V2 makes background specialists the default mental model: the Orchestrator plans
+the work graph, launches the right agents, avoids overlapping write ownership,
+and waits for terminal task results before acting on them.
+
+See **[Background Orchestration](docs/v2-background-orchestration.md)** for the
+full scheduler model.
+
+#### Companion
+
+The optional Companion is a floating desktop status window for live agent
+activity. It shows the current session state and which agents are active, so
+background work is easier to follow at a glance.
+
+<div align="center">
+  <img src="img/companion.gif" alt="Companion showing active agents" width="600">
+  <p><i>Left bottom visual companion.</i></p>
+</div>
+
+During interactive install, the installer asks whether to enable Companion and
+defaults to `yes`. For automation, enable it explicitly with:
 
 
 ```bash
 ```bash
-bunx oh-my-opencode-slim@beta install --background-subagents=yes
+bunx oh-my-opencode-slim@latest install --companion=yes
+```
+
+See **[Companion](docs/companion.md)** for configuration, positions, sizes, and
+install details.
+
+#### Deepwork
+
+Deepwork is for heavy coding sessions: broad refactors, multi-phase features,
+risky architecture changes, or work that needs a persistent plan. It creates a
+local markdown progress file, uses Oracle review gates, and keeps implementation
+phases structured.
+
+Start it with:
+
+```text
+/deepwork <heavy coding task>
 ```
 ```
 
 
-The installer can set this up for you with
-`--background-subagents=ask|yes|no`. In an interactive TTY, the default is
-`ask`; in non-interactive mode, the default is `no`. Use
-`--background-subagents=yes` to opt in immediately or `--background-subagents=no`
-to skip. If you want the installer to write to a specific shell/profile file,
-add `--background-subagents-target=<path>`. After shell setup, restart your
-terminal or source the updated file before starting `opencode`; for a one-shot
-launch, run `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true opencode`.
+See **[Skills](docs/skills.md#deepwork)** for when to use it and how the workflow
+runs.
+
+#### oh-my-opencode-slim Skill
+
+The bundled `oh-my-opencode-slim` skill helps the Orchestrator configure and
+improve the plugin itself. Use it for model tuning, custom agents, prompt
+overrides, skill/MCP permissions, presets, optional agents, background
+orchestration, and recurring workflow friction.
+
+<div align="center">
+  <img src="img/oh-my-opencode-skill.png" alt="oh-my-opencode-slim skill in use" width="600">
+  <p><i>Ask the bundled skill to tune and improve your agent setup.</i></p>
+</div>
+
+See **[Skills](docs/skills.md#oh-my-opencode-slim)** for examples and safety
+rules.
 
 
 ### Getting Started
 ### Getting Started
 
 
@@ -105,12 +149,6 @@ Then:
 > [!TIP]
 > [!TIP]
 > It's **recommended** to understand how background orchestration works. The **[Orchestrator prompt](https://github.com/alvinunreal/oh-my-opencode-slim/blob/master/src/agents/orchestrator.ts#L28)** contains the scheduler rules, specialist routing logic, and thresholds for when work should be assigned to background agents. You can always delegate manually by calling a subagent via: `@agentName <task>`
 > It's **recommended** to understand how background orchestration works. The **[Orchestrator prompt](https://github.com/alvinunreal/oh-my-opencode-slim/blob/master/src/agents/orchestrator.ts#L28)** contains the scheduler rules, specialist routing logic, and thresholds for when work should be assigned to background agents. You can always delegate manually by calling a subagent via: `@agentName <task>`
 
 
-### Legacy V1 note
-
-The current `@latest` package is the background-orchestration release. If a
-maintained V1 branch or tag is created later, it will be documented separately as
-historical compatibility guidance rather than part of the default install path.
-
 The default generated configuration includes both `openai` and `opencode-go` presets.
 The default generated configuration includes both `openai` and `opencode-go` presets.
 
 
 ```jsonc
 ```jsonc

+ 6 - 4
docs/companion.md

@@ -33,15 +33,17 @@ You can enable the companion by adding a `companion` section to your setting con
 
 
 ## Installer Flag
 ## Installer Flag
 
 
-When running the installer, pass `--companion=yes` to download the native
-binary and generate the enabled config block:
+During interactive installation, the installer asks whether to download and
+enable the native Companion binary. The prompt defaults to `yes`, so pressing
+Enter installs it.
+
+For automation, pass `--companion=yes` to install without prompting:
 
 
 ```bash
 ```bash
 bunx oh-my-opencode-slim install --companion=yes
 bunx oh-my-opencode-slim install --companion=yes
 ```
 ```
 
 
-Pass `--companion=no` or omit the flag to skip the native binary and omit the
-config block.
+Pass `--companion=no` to skip the native binary and omit the config block.
 
 
 ---
 ---
 
 

+ 13 - 21
docs/installation.md

@@ -35,11 +35,11 @@ The installer supports the following options:
 |--------|-------------|
 |--------|-------------|
 | `--skills=yes|no` | Install bundled skills (default: yes) |
 | `--skills=yes|no` | Install bundled skills (default: yes) |
 | `--preset=<name>` | Active generated config preset: `openai` or `opencode-go` (default: `openai`) |
 | `--preset=<name>` | Active generated config preset: `openai` or `opencode-go` (default: `openai`) |
+| `--background-subagents=ask\|yes\|no` | Configure the required background-subagents environment export (`ask` by default; prompt defaults to yes) |
+| `--background-subagents-target=<path>` | Write the background-subagents export to a specific shell/profile file |
 | `--no-tui` | Non-interactive mode |
 | `--no-tui` | Non-interactive mode |
 | `--dry-run` | Simulate install without writing files |
 | `--dry-run` | Simulate install without writing files |
 | `--reset` | Force overwrite of existing configuration |
 | `--reset` | Force overwrite of existing configuration |
-| `--background-subagents=ask\|yes\|no` | Configure `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true` in your shell startup file (`ask` by default only in an interactive TTY; otherwise `no`) |
-| `--background-subagents-target=<path>` | Write the background-subagents export to a specific shell/profile file |
 
 
 ### Background Subagents Environment Setup
 ### Background Subagents Environment Setup
 
 
@@ -50,22 +50,17 @@ background subagents, which are enabled by this environment variable:
 OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true
 OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true
 ```
 ```
 
 
-The installer can add that export to your shell startup file. Use one of:
+The installer asks before adding that export to your shell startup file. The
+prompt defaults to `yes` because V2's default orchestration depends on it.
 
 
 ```bash
 ```bash
-# Ask before editing a shell startup file (default in interactive TTY only)
-bunx oh-my-opencode-slim@latest install --background-subagents=ask
-
-# Always configure the export when possible
-bunx oh-my-opencode-slim@latest install --background-subagents=yes
+bunx oh-my-opencode-slim@latest install
+```
 
 
-# Do not modify shell startup files
-bunx oh-my-opencode-slim@latest install --background-subagents=no
+For non-interactive setup, pass the choice explicitly:
 
 
-# Write to an explicit target file
-bunx oh-my-opencode-slim@latest install \
-  --background-subagents=yes \
-  --background-subagents-target="$HOME/.zshrc"
+```bash
+bunx oh-my-opencode-slim@latest install --no-tui --background-subagents=yes
 ```
 ```
 
 
 After the installer updates a shell startup file, restart your terminal or source
 After the installer updates a shell startup file, restart your terminal or source
@@ -77,7 +72,7 @@ source ~/.zshrc
 source ~/.bashrc
 source ~/.bashrc
 ```
 ```
 
 
-For a one-shot manual launch without changing shell files:
+For a one-shot manual launch without restarting your terminal:
 
 
 ```bash
 ```bash
 OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true opencode
 OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true opencode
@@ -153,7 +148,7 @@ If not installed, direct the user to https://opencode.ai/docs first.
 The installer generates OpenAI and OpenCode Go presets, with OpenAI active by default:
 The installer generates OpenAI and OpenCode Go presets, with OpenAI active by default:
 
 
 ```bash
 ```bash
-bunx oh-my-opencode-slim@latest install --no-tui --skills=yes --background-subagents=yes
+bunx oh-my-opencode-slim@latest install --no-tui --skills=yes
 ```
 ```
 
 
 **Examples:**
 **Examples:**
@@ -164,9 +159,6 @@ bunx oh-my-opencode-slim@latest install
 # Non-interactive with bundled skills
 # Non-interactive with bundled skills
 bunx oh-my-opencode-slim@latest install --no-tui --skills=yes --background-subagents=yes
 bunx oh-my-opencode-slim@latest install --no-tui --skills=yes --background-subagents=yes
 
 
-# Non-interactive and configure background subagents env setup
-bunx oh-my-opencode-slim@latest install --no-tui --background-subagents=yes
-
 # Make the generated OpenCode Go preset active
 # Make the generated OpenCode Go preset active
 bunx oh-my-opencode-slim@latest install --preset=opencode-go
 bunx oh-my-opencode-slim@latest install --preset=opencode-go
 
 
@@ -276,9 +268,9 @@ return task IDs, or delegation behaves like a blocking foreground call:
    OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true opencode
    OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true opencode
    ```
    ```
 
 
-4. If you intentionally skipped shell setup, rerun the installer with:
+4. If shell setup was missing, rerun the installer:
    ```bash
    ```bash
-   bunx oh-my-opencode-slim@latest install --background-subagents=yes
+   bunx oh-my-opencode-slim@latest install
    ```
    ```
 
 
 ### Authentication Issues
 ### Authentication Issues

BIN
img/companion.gif


BIN
img/oh-my-opencode-skill.png


BIN
img/v2.webp


+ 37 - 36
src/cli/background-subagents.test.ts

@@ -15,10 +15,7 @@ import {
   writeBackgroundSubagentsBlock,
   writeBackgroundSubagentsBlock,
 } from './background-subagents';
 } from './background-subagents';
 import { parseArgs } from './index';
 import { parseArgs } from './index';
-import {
-  configureBackgroundSubagents,
-  shouldPromptForBackgroundSubagents,
-} from './install';
+import { configureBackgroundSubagents } from './install';
 
 
 describe('background subagents helpers', () => {
 describe('background subagents helpers', () => {
   test('detects true-like environment values', () => {
   test('detects true-like environment values', () => {
@@ -127,6 +124,11 @@ describe('background subagents writing', () => {
 });
 });
 
 
 describe('parseArgs background subagents', () => {
 describe('parseArgs background subagents', () => {
+  test('defaults background subagents to ask', () => {
+    expect(parseArgs([]).backgroundSubagents).toBe('ask');
+    expect(parseArgs(['--no-tui']).backgroundSubagents).toBe('ask');
+  });
+
   test('parses mode and target override', () => {
   test('parses mode and target override', () => {
     expect(
     expect(
       parseArgs([
       parseArgs([
@@ -138,9 +140,17 @@ describe('parseArgs background subagents', () => {
       backgroundSubagentsTarget: '/tmp/profile',
       backgroundSubagentsTarget: '/tmp/profile',
     });
     });
   });
   });
+});
 
 
-  test('--no-tui defaults background subagents to no', () => {
-    expect(parseArgs(['--no-tui']).backgroundSubagents).toBe('no');
+describe('parseArgs companion', () => {
+  test('defaults companion install to ask', () => {
+    expect(parseArgs([]).companion).toBe('ask');
+  });
+
+  test('parses companion mode override', () => {
+    expect(parseArgs(['--companion=yes']).companion).toBe('yes');
+    expect(parseArgs(['--companion=no']).companion).toBe('no');
+    expect(parseArgs(['--companion=ask']).companion).toBe('ask');
   });
   });
 });
 });
 
 
@@ -160,47 +170,34 @@ describe('configureBackgroundSubagents', () => {
     }
     }
   });
   });
 
 
-  test('does not prompt for ask mode when noninteractive', async () => {
+  test('writes shell config without prompting', async () => {
     tempDir = mkdtempSync(join(tmpdir(), 'omoo-bg-'));
     tempDir = mkdtempSync(join(tmpdir(), 'omoo-bg-'));
     delete process.env.OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS;
     delete process.env.OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS;
-    const target = join(tempDir, '.bashrc');
     const log = spyOn(console, 'log').mockImplementation(() => undefined);
     const log = spyOn(console, 'log').mockImplementation(() => undefined);
-    const originalIsTty = process.stdin.isTTY;
-    Object.defineProperty(process.stdin, 'isTTY', {
-      configurable: true,
-      value: false,
-    });
+    const originalShell = process.env.SHELL;
+    const originalHome = process.env.HOME;
+    process.env.SHELL = '/bin/zsh';
+    process.env.HOME = tempDir;
 
 
     try {
     try {
-      expect(
-        shouldPromptForBackgroundSubagents({
-          hasTmux: false,
-          installCustomSkills: false,
-          promptForStar: false,
-          reset: false,
-          backgroundSubagents: 'ask',
-          backgroundSubagentsTarget: target,
-        }),
-      ).toBe(false);
-
       const result = await configureBackgroundSubagents({
       const result = await configureBackgroundSubagents({
         hasTmux: false,
         hasTmux: false,
         installCustomSkills: false,
         installCustomSkills: false,
         promptForStar: false,
         promptForStar: false,
         reset: false,
         reset: false,
-        backgroundSubagents: 'ask',
-        backgroundSubagentsTarget: target,
+        backgroundSubagents: 'yes',
       });
       });
 
 
-      expect(result).toEqual({ enabledNow: false });
+      expect(result.configuredTarget?.endsWith('/.zshrc')).toBe(true);
+      expect(readFileSync(join(tempDir, '.zshrc'), 'utf8')).toContain(
+        'OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS',
+      );
       expect(log.mock.calls.join('\n')).toContain(
       expect(log.mock.calls.join('\n')).toContain(
-        'Skipped background subagents shell configuration.',
+        'Background subagents enabled',
       );
       );
     } finally {
     } finally {
-      Object.defineProperty(process.stdin, 'isTTY', {
-        configurable: true,
-        value: originalIsTty,
-      });
+      process.env.SHELL = originalShell;
+      process.env.HOME = originalHome;
       log.mockRestore();
       log.mockRestore();
     }
     }
   });
   });
@@ -208,10 +205,13 @@ describe('configureBackgroundSubagents', () => {
   test('returns no configured target when writing shell config fails', async () => {
   test('returns no configured target when writing shell config fails', async () => {
     tempDir = mkdtempSync(join(tmpdir(), 'omoo-bg-'));
     tempDir = mkdtempSync(join(tmpdir(), 'omoo-bg-'));
     delete process.env.OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS;
     delete process.env.OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS;
-    const blockingFile = join(tempDir, 'not-a-directory');
-    writeFileSync(blockingFile, 'already a file');
-    const target = join(blockingFile, '.bashrc');
+    rmSync(tempDir, { recursive: true, force: true });
+    writeFileSync(tempDir, 'not a directory');
     const log = spyOn(console, 'log').mockImplementation(() => undefined);
     const log = spyOn(console, 'log').mockImplementation(() => undefined);
+    const originalShell = process.env.SHELL;
+    const originalHome = process.env.HOME;
+    process.env.SHELL = '/bin/zsh';
+    process.env.HOME = tempDir;
 
 
     try {
     try {
       const result = await configureBackgroundSubagents({
       const result = await configureBackgroundSubagents({
@@ -220,7 +220,6 @@ describe('configureBackgroundSubagents', () => {
         promptForStar: false,
         promptForStar: false,
         reset: false,
         reset: false,
         backgroundSubagents: 'yes',
         backgroundSubagents: 'yes',
-        backgroundSubagentsTarget: target,
       });
       });
 
 
       expect(result).toEqual({ enabledNow: false });
       expect(result).toEqual({ enabledNow: false });
@@ -229,6 +228,8 @@ describe('configureBackgroundSubagents', () => {
       );
       );
       expect(log.mock.calls.join('\n')).toContain('Add the setting manually');
       expect(log.mock.calls.join('\n')).toContain('Add the setting manually');
     } finally {
     } finally {
+      process.env.SHELL = originalShell;
+      process.env.HOME = originalHome;
       log.mockRestore();
       log.mockRestore();
     }
     }
   });
   });

+ 4 - 3
src/cli/background-subagents.ts

@@ -29,10 +29,11 @@ export function detectBackgroundSubagentsTarget(
   env: NodeJS.ProcessEnv = process.env,
   env: NodeJS.ProcessEnv = process.env,
 ): string | undefined {
 ): string | undefined {
   const shell = detectShellKind(env.SHELL);
   const shell = detectShellKind(env.SHELL);
-  if (shell === 'zsh') return join(homedir(), '.zshrc');
-  if (shell === 'bash') return join(homedir(), '.bashrc');
+  const home = env.HOME || homedir();
+  if (shell === 'zsh') return join(home, '.zshrc');
+  if (shell === 'bash') return join(home, '.bashrc');
   if (shell === 'fish') {
   if (shell === 'fish') {
-    const configHome = env.XDG_CONFIG_HOME || join(homedir(), '.config');
+    const configHome = env.XDG_CONFIG_HOME || join(home, '.config');
     return join(
     return join(
       configHome,
       configHome,
       'fish',
       'fish',

+ 16 - 12
src/cli/index.ts

@@ -2,13 +2,18 @@
 import { doctor, parseDoctorArgs } from './doctor';
 import { doctor, parseDoctorArgs } from './doctor';
 import { install } from './install';
 import { install } from './install';
 import { getGeneratedPresetNames, isGeneratedPresetName } from './providers';
 import { getGeneratedPresetNames, isGeneratedPresetName } from './providers';
-import type { BackgroundSubagentsArg, BooleanArg, InstallArgs } from './types';
+import type {
+  BackgroundSubagentsArg,
+  BooleanArg,
+  CompanionArg,
+  InstallArgs,
+} from './types';
 
 
 export function parseArgs(args: string[]): InstallArgs {
 export function parseArgs(args: string[]): InstallArgs {
   const result: InstallArgs = {
   const result: InstallArgs = {
     tui: true,
     tui: true,
     skills: 'yes',
     skills: 'yes',
-    companion: 'no',
+    companion: 'ask',
   };
   };
 
 
   for (const arg of args) {
   for (const arg of args) {
@@ -17,9 +22,9 @@ export function parseArgs(args: string[]): InstallArgs {
     } else if (arg.startsWith('--skills=')) {
     } else if (arg.startsWith('--skills=')) {
       result.skills = arg.split('=')[1] as BooleanArg;
       result.skills = arg.split('=')[1] as BooleanArg;
     } else if (arg.startsWith('--companion=')) {
     } else if (arg.startsWith('--companion=')) {
-      const mode = arg.split('=')[1] as BooleanArg;
-      if (!['yes', 'no'].includes(mode)) {
-        console.error('Unsupported --companion value: use yes or no');
+      const mode = arg.split('=')[1] as CompanionArg;
+      if (!['ask', 'yes', 'no'].includes(mode)) {
+        console.error('Unsupported --companion value: use ask, yes, or no');
         process.exit(1);
         process.exit(1);
       }
       }
       result.companion = mode;
       result.companion = mode;
@@ -53,8 +58,7 @@ export function parseArgs(args: string[]): InstallArgs {
     }
     }
   }
   }
 
 
-  result.backgroundSubagents ??=
-    result.tui && process.stdin.isTTY ? 'ask' : 'no';
+  result.backgroundSubagents ??= 'ask';
 
 
   return result;
   return result;
 }
 }
@@ -69,14 +73,14 @@ Usage:
 
 
 Options:
 Options:
   --skills=yes|no        Install bundled skills (default: yes)
   --skills=yes|no        Install bundled skills (default: yes)
-  --companion=yes|no     Install desktop companion binary and enable config
-                         (default: no)
+  --companion=ask|yes|no Install desktop companion binary and enable config
+                         (default: ask; prompt defaults to yes)
   --preset=<name>        Active generated config preset (default: openai)
   --preset=<name>        Active generated config preset (default: openai)
   --background-subagents=ask|yes|no
   --background-subagents=ask|yes|no
-                         Persist required OpenCode background subagent env
-                         (default: ask in interactive TTY, otherwise no)
+                          Persist required OpenCode background subagent env
+                          (default: ask; prompt defaults to yes)
   --background-subagents-target=<path>
   --background-subagents-target=<path>
-                         Shell startup file to update
+                          Shell startup file to update
   --no-tui               Non-interactive mode
   --no-tui               Non-interactive mode
   --dry-run              Simulate install without writing files
   --dry-run              Simulate install without writing files
   --reset                Force overwrite of existing configuration
   --reset                Force overwrite of existing configuration

+ 54 - 14
src/cli/install.ts

@@ -139,12 +139,6 @@ async function checkOpenCodeInstalled(): Promise<{
   return { ok: true, version: version ?? undefined, path: path ?? undefined };
   return { ok: true, version: version ?? undefined, path: path ?? undefined };
 }
 }
 
 
-export function shouldPromptForBackgroundSubagents(
-  config: InstallConfig,
-): boolean {
-  return Boolean(config.promptForStar && process.stdin.isTTY);
-}
-
 export async function configureBackgroundSubagents(
 export async function configureBackgroundSubagents(
   config: InstallConfig,
   config: InstallConfig,
 ): Promise<{ enabledNow: boolean; configuredTarget?: string }> {
 ): Promise<{ enabledNow: boolean; configuredTarget?: string }> {
@@ -165,7 +159,7 @@ export async function configureBackgroundSubagents(
       : detectBackgroundSubagentsTarget();
       : detectBackgroundSubagentsTarget();
 
 
   if (config.backgroundSubagents === 'no') {
   if (config.backgroundSubagents === 'no') {
-    printInfo('OpenCode background subagents are not enabled.');
+    printInfo('OpenCode background subagents shell setup skipped.');
     console.log(manualBackgroundSubagentsInstructions({ targetPath: target }));
     console.log(manualBackgroundSubagentsInstructions({ targetPath: target }));
     return { enabledNow: false };
     return { enabledNow: false };
   }
   }
@@ -188,20 +182,27 @@ export async function configureBackgroundSubagents(
   }
   }
 
 
   if (config.backgroundSubagents === 'ask') {
   if (config.backgroundSubagents === 'ask') {
-    if (!shouldPromptForBackgroundSubagents(config)) {
-      printInfo('Skipped background subagents shell configuration.');
+    if (!process.stdin.isTTY) {
+      printInfo('Skipped background subagents shell setup in non-TTY mode.');
       console.log(
       console.log(
         manualBackgroundSubagentsInstructions({ targetPath: target }),
         manualBackgroundSubagentsInstructions({ targetPath: target }),
       );
       );
       return { enabledNow: false };
       return { enabledNow: false };
     }
     }
 
 
+    console.log();
+    printInfo(
+      'V2 requires OpenCode background subagents for default orchestration.',
+    );
+    printInfo(
+      `The installer can add the required environment export to ${target}.`,
+    );
     const shouldWrite = await confirm(
     const shouldWrite = await confirm(
-      `Enable OpenCode background subagents in ${target}?`,
+      'Add OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true now?',
       true,
       true,
     );
     );
     if (!shouldWrite) {
     if (!shouldWrite) {
-      printInfo('Skipped background subagents shell configuration.');
+      printInfo('Skipped background subagents shell setup.');
       console.log(
       console.log(
         manualBackgroundSubagentsInstructions({ targetPath: target }),
         manualBackgroundSubagentsInstructions({ targetPath: target }),
       );
       );
@@ -225,6 +226,43 @@ export async function configureBackgroundSubagents(
   return { enabledNow: false, configuredTarget: target };
   return { enabledNow: false, configuredTarget: target };
 }
 }
 
 
+export async function shouldInstallCompanion(
+  config: InstallConfig,
+): Promise<boolean> {
+  if (config.companion === 'yes') return true;
+  if (config.companion === 'no') return false;
+
+  if (config.dryRun) {
+    printInfo(
+      'Dry run mode - would ask to install the desktop companion (default: yes).',
+    );
+    config.companion = 'yes';
+    return true;
+  }
+
+  if (!process.stdin.isTTY) {
+    printInfo(
+      'Skipped desktop companion prompt in non-TTY mode. Use --companion=yes to install it.',
+    );
+    config.companion = 'no';
+    return false;
+  }
+
+  console.log();
+  printInfo('The optional desktop companion shows live agent activity.');
+  const shouldInstall = await confirm(
+    'Install and enable the desktop companion?',
+    true,
+  );
+  config.companion = shouldInstall ? 'yes' : 'no';
+
+  if (!shouldInstall) {
+    printInfo('Desktop companion install skipped.');
+  }
+
+  return shouldInstall;
+}
+
 function handleStepResult(
 function handleStepResult(
   result: ConfigMergeResult,
   result: ConfigMergeResult,
   successMsg: string,
   successMsg: string,
@@ -245,9 +283,11 @@ async function runInstall(config: InstallConfig): Promise<number> {
 
 
   printHeader(isUpdate);
   printHeader(isUpdate);
 
 
+  const companionInstall = await shouldInstallCompanion(config);
+
   let totalSteps = 7;
   let totalSteps = 7;
   if (config.installCustomSkills) totalSteps += 1;
   if (config.installCustomSkills) totalSteps += 1;
-  if (config.companion === 'yes') totalSteps += 1;
+  if (companionInstall) totalSteps += 1;
   totalSteps += 1;
   totalSteps += 1;
 
 
   let step = 1;
   let step = 1;
@@ -312,7 +352,7 @@ async function runInstall(config: InstallConfig): Promise<number> {
   printStep(step++, totalSteps, 'Configuring OpenCode background subagents...');
   printStep(step++, totalSteps, 'Configuring OpenCode background subagents...');
   const backgroundSubagents = await configureBackgroundSubagents(config);
   const backgroundSubagents = await configureBackgroundSubagents(config);
 
 
-  if (config.companion === 'yes') {
+  if (companionInstall) {
     printStep(step++, totalSteps, 'Installing desktop companion binary...');
     printStep(step++, totalSteps, 'Installing desktop companion binary...');
     const companionResult = await installCompanion(config);
     const companionResult = await installCompanion(config);
     if (!handleStepResult(companionResult, 'Companion installed')) return 1;
     if (!handleStepResult(companionResult, 'Companion installed')) return 1;
@@ -439,7 +479,7 @@ export async function install(args: InstallArgs): Promise<number> {
     promptForStar: args.tui,
     promptForStar: args.tui,
     dryRun: args.dryRun,
     dryRun: args.dryRun,
     reset: args.reset ?? false,
     reset: args.reset ?? false,
-    backgroundSubagents: args.backgroundSubagents ?? 'no',
+    backgroundSubagents: args.backgroundSubagents ?? 'ask',
     backgroundSubagentsTarget: args.backgroundSubagentsTarget,
     backgroundSubagentsTarget: args.backgroundSubagentsTarget,
     companion: args.companion,
     companion: args.companion,
   };
   };

+ 3 - 2
src/cli/types.ts

@@ -1,5 +1,6 @@
 export type BooleanArg = 'yes' | 'no';
 export type BooleanArg = 'yes' | 'no';
 export type BackgroundSubagentsArg = 'ask' | 'yes' | 'no';
 export type BackgroundSubagentsArg = 'ask' | 'yes' | 'no';
+export type CompanionArg = 'ask' | BooleanArg;
 
 
 export interface InstallArgs {
 export interface InstallArgs {
   tui: boolean;
   tui: boolean;
@@ -9,7 +10,7 @@ export interface InstallArgs {
   reset?: boolean;
   reset?: boolean;
   backgroundSubagents?: BackgroundSubagentsArg;
   backgroundSubagents?: BackgroundSubagentsArg;
   backgroundSubagentsTarget?: string;
   backgroundSubagentsTarget?: string;
-  companion?: BooleanArg;
+  companion?: CompanionArg;
 }
 }
 
 
 export interface OpenCodeConfig {
 export interface OpenCodeConfig {
@@ -28,7 +29,7 @@ export interface InstallConfig {
   reset: boolean;
   reset: boolean;
   backgroundSubagents: BackgroundSubagentsArg;
   backgroundSubagents: BackgroundSubagentsArg;
   backgroundSubagentsTarget?: string;
   backgroundSubagentsTarget?: string;
-  companion?: BooleanArg;
+  companion?: CompanionArg;
 }
 }
 
 
 export interface ConfigMergeResult {
 export interface ConfigMergeResult {

+ 1 - 1
src/hooks/auto-update-checker/index.test.ts

@@ -332,7 +332,7 @@ describe('auto-update-checker/index', () => {
       body: {
       body: {
         title: 'oh-my-opencode-slim v2.0.0 is available.',
         title: 'oh-my-opencode-slim v2.0.0 is available.',
         message:
         message:
-          'It requires OpenCode background subagents.\nRun: bunx oh-my-opencode-slim@latest install --background-subagents=yes',
+          'It requires OpenCode background subagents.\nRun: bunx oh-my-opencode-slim@latest install',
         variant: 'info',
         variant: 'info',
         duration: 12000,
         duration: 12000,
       },
       },

+ 1 - 1
src/hooks/auto-update-checker/index.ts

@@ -189,7 +189,7 @@ function showMajorUpgradeToast(ctx: PluginInput, version: string): void {
   showToast(
   showToast(
     ctx,
     ctx,
     `oh-my-opencode-slim v${version} is available.`,
     `oh-my-opencode-slim v${version} is available.`,
-    'It requires OpenCode background subagents.\nRun: bunx oh-my-opencode-slim@latest install --background-subagents=yes',
+    'It requires OpenCode background subagents.\nRun: bunx oh-my-opencode-slim@latest install',
     'info',
     'info',
     12_000,
     12_000,
   );
   );