Browse Source

feat(installer): enable Exa websearch

Alvin 1 week ago
parent
commit
0ddae2afff
5 changed files with 74 additions and 25 deletions
  1. 7 4
      docs/installation.md
  2. 4 0
      docs/mcps.md
  3. 41 4
      src/cli/background-subagents.test.ts
  4. 10 7
      src/cli/background-subagents.ts
  5. 12 10
      src/cli/install.ts

+ 7 - 4
docs/installation.md

@@ -36,8 +36,8 @@ The installer supports the following options:
 | `--skills=yes|no` | Install bundled skills (default: yes) |
 | `--skills=yes|no` | Install bundled skills (default: yes) |
 | `--companion=ask\|yes\|no` | Install and enable the desktop Companion (`ask` by default; prompt defaults to no) |
 | `--companion=ask\|yes\|no` | Install and enable the desktop Companion (`ask` by default; prompt defaults to no) |
 | `--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 |
+| `--background-subagents=ask\|yes\|no` | Configure the required background-subagents and Exa websearch environment exports (`ask` by default; prompt defaults to yes) |
+| `--background-subagents-target=<path>` | Write the background-subagents and websearch exports 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 |
@@ -49,10 +49,13 @@ background subagents, which are enabled by this environment variable:
 
 
 ```bash
 ```bash
 OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true
 OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true
+OPENCODE_ENABLE_EXA=1
 ```
 ```
 
 
-The installer asks before adding that export to your shell startup file. The
+The installer asks before adding those exports to your shell startup file. The
 prompt defaults to `yes` because V2's default orchestration depends on it.
 prompt defaults to `yes` because V2's default orchestration depends on it.
+The Exa flag enables OpenCode's built-in `websearch` tool without requiring an
+API key.
 
 
 ```bash
 ```bash
 bunx oh-my-opencode-slim@latest install
 bunx oh-my-opencode-slim@latest install
@@ -76,7 +79,7 @@ source ~/.bashrc
 For a one-shot manual launch without restarting your terminal:
 For a one-shot manual launch without restarting your terminal:
 
 
 ```bash
 ```bash
-OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true opencode
+OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true OPENCODE_ENABLE_EXA=1 opencode
 ```
 ```
 
 
 ### Non-Destructive Behavior
 ### Non-Destructive Behavior

+ 4 - 0
docs/mcps.md

@@ -14,6 +14,10 @@ Enable the built-in tool by setting these environment variables in your shell pr
 env OPENCODE_ENABLE_EXA=true OPENCODE_ENABLE_PARALLEL=true opencode
 env OPENCODE_ENABLE_EXA=true OPENCODE_ENABLE_PARALLEL=true opencode
 ```
 ```
 
 
+The oh-my-opencode-slim installer sets `OPENCODE_ENABLE_EXA=1` alongside its
+background-subagents export when you accept the environment setup. Parallel
+search remains opt-in.
+
 The built-in tool is Exa-backed (optionally Parallel), needs no API key, and is only available when using the `opencode` provider OR when those flags are set. Control access per agent with `permission: { "websearch": "allow" }` (all tools are allowed by default).
 The built-in tool is Exa-backed (optionally Parallel), needs no API key, and is only available when using the `opencode` provider OR when those flags are set. Control access per agent with `permission: { "websearch": "allow" }` (all tools are allowed by default).
 
 
 ---
 ---

+ 41 - 4
src/cli/background-subagents.test.ts

@@ -54,12 +54,16 @@ describe('background subagents helpers', () => {
   });
   });
 
 
   test('builds shell-specific managed blocks with true', () => {
   test('builds shell-specific managed blocks with true', () => {
-    expect(getBackgroundSubagentsBlock('/tmp/.bashrc')).toContain(
+    const bashBlock = getBackgroundSubagentsBlock('/tmp/.bashrc');
+    const fishBlock = getBackgroundSubagentsBlock('/tmp/config.fish');
+    expect(bashBlock).toContain(
       'export OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true',
       'export OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true',
     );
     );
-    expect(getBackgroundSubagentsBlock('/tmp/config.fish')).toContain(
+    expect(bashBlock).toContain('export OPENCODE_ENABLE_EXA=1');
+    expect(fishBlock).toContain(
       'set -gx OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS true',
       'set -gx OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS true',
     );
     );
+    expect(fishBlock).toContain('set -gx OPENCODE_ENABLE_EXA 1');
   });
   });
 
 
   test('prints fish manual instructions for fish targets', () => {
   test('prints fish manual instructions for fish targets', () => {
@@ -71,8 +75,9 @@ describe('background subagents helpers', () => {
       'set -gx OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS true',
       'set -gx OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS true',
     );
     );
     expect(instructions).toContain(
     expect(instructions).toContain(
-      'env OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true opencode',
+      'env OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true OPENCODE_ENABLE_EXA=1 opencode',
     );
     );
+    expect(instructions).toContain('set -gx OPENCODE_ENABLE_EXA 1');
   });
   });
 
 
   test('expands tilde target paths', () => {
   test('expands tilde target paths', () => {
@@ -164,6 +169,7 @@ describe('configureBackgroundSubagents', () => {
   let tempDir: string | undefined;
   let tempDir: string | undefined;
   const originalBackgroundEnv =
   const originalBackgroundEnv =
     process.env.OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS;
     process.env.OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS;
+  const originalExaEnv = process.env.OPENCODE_ENABLE_EXA;
 
 
   afterEach(() => {
   afterEach(() => {
     if (tempDir) rmSync(tempDir, { recursive: true, force: true });
     if (tempDir) rmSync(tempDir, { recursive: true, force: true });
@@ -174,6 +180,11 @@ describe('configureBackgroundSubagents', () => {
       process.env.OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS =
       process.env.OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS =
         originalBackgroundEnv;
         originalBackgroundEnv;
     }
     }
+    if (originalExaEnv === undefined) {
+      delete process.env.OPENCODE_ENABLE_EXA;
+    } else {
+      process.env.OPENCODE_ENABLE_EXA = originalExaEnv;
+    }
   });
   });
 
 
   test('writes shell config without prompting', async () => {
   test('writes shell config without prompting', async () => {
@@ -197,8 +208,11 @@ describe('configureBackgroundSubagents', () => {
       expect(readFileSync(join(tempDir, '.zshrc'), 'utf8')).toContain(
       expect(readFileSync(join(tempDir, '.zshrc'), 'utf8')).toContain(
         'OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS',
         'OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS',
       );
       );
+      expect(readFileSync(join(tempDir, '.zshrc'), 'utf8')).toContain(
+        'OPENCODE_ENABLE_EXA=1',
+      );
       expect(log.mock.calls.join('\n')).toContain(
       expect(log.mock.calls.join('\n')).toContain(
-        'Background subagents enabled',
+        'Background subagents and Exa websearch enabled',
       );
       );
     } finally {
     } finally {
       process.env.SHELL = originalShell;
       process.env.SHELL = originalShell;
@@ -207,6 +221,29 @@ describe('configureBackgroundSubagents', () => {
     }
     }
   });
   });
 
 
+  test('writes shell config when only background subagents are enabled', async () => {
+    tempDir = mkdtempSync(join(tmpdir(), 'omoo-bg-'));
+    process.env.OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS = 'true';
+    delete process.env.OPENCODE_ENABLE_EXA;
+    const log = spyOn(console, 'log').mockImplementation(() => undefined);
+
+    try {
+      await configureBackgroundSubagents({
+        installCustomSkills: false,
+        promptForStar: false,
+        reset: false,
+        backgroundSubagents: 'yes',
+        backgroundSubagentsTarget: join(tempDir, '.zshrc'),
+      });
+
+      expect(readFileSync(join(tempDir, '.zshrc'), 'utf8')).toContain(
+        'OPENCODE_ENABLE_EXA=1',
+      );
+    } finally {
+      log.mockRestore();
+    }
+  });
+
   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;

+ 10 - 7
src/cli/background-subagents.ts

@@ -6,6 +6,7 @@ export type BackgroundSubagentsMode = 'ask' | 'yes' | 'no';
 export type ShellKind = 'bash' | 'fish' | 'zsh';
 export type ShellKind = 'bash' | 'fish' | 'zsh';
 
 
 const ENV_NAME = 'OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS';
 const ENV_NAME = 'OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS';
+const EXA_ENV_NAME = 'OPENCODE_ENABLE_EXA';
 const START_MARKER = '# >>> oh-my-opencode-slim background subagents >>>';
 const START_MARKER = '# >>> oh-my-opencode-slim background subagents >>>';
 const END_MARKER = '# <<< oh-my-opencode-slim background subagents <<<';
 const END_MARKER = '# <<< oh-my-opencode-slim background subagents <<<';
 
 
@@ -46,11 +47,11 @@ export function detectBackgroundSubagentsTarget(
 
 
 export function getBackgroundSubagentsBlock(targetPath: string): string {
 export function getBackgroundSubagentsBlock(targetPath: string): string {
   const isFish = targetPath.endsWith('.fish');
   const isFish = targetPath.endsWith('.fish');
-  const command = isFish
-    ? `set -gx ${ENV_NAME} true`
-    : `export ${ENV_NAME}=true`;
+  const commands = isFish
+    ? `set -gx ${ENV_NAME} true\nset -gx ${EXA_ENV_NAME} 1`
+    : `export ${ENV_NAME}=true\nexport ${EXA_ENV_NAME}=1`;
 
 
-  return `${START_MARKER}\n${command}\n${END_MARKER}`;
+  return `${START_MARKER}\n${commands}\n${END_MARKER}`;
 }
 }
 
 
 export function manualBackgroundSubagentsInstructions(options?: {
 export function manualBackgroundSubagentsInstructions(options?: {
@@ -63,16 +64,18 @@ export function manualBackgroundSubagentsInstructions(options?: {
     detectShellKind(options?.targetPath);
     detectShellKind(options?.targetPath);
   const bashZshSnippet = `export ${ENV_NAME}=true`;
   const bashZshSnippet = `export ${ENV_NAME}=true`;
   const fishSnippet = `set -gx ${ENV_NAME} true`;
   const fishSnippet = `set -gx ${ENV_NAME} true`;
+  const bashZshExaSnippet = `export ${EXA_ENV_NAME}=1`;
+  const fishExaSnippet = `set -gx ${EXA_ENV_NAME} 1`;
 
 
   if (shell === 'fish') {
   if (shell === 'fish') {
-    return `Start OpenCode with background subagents enabled:\n  env ${ENV_NAME}=true opencode\n\nOr add this to your fish startup file:\n  ${fishSnippet}`;
+    return `Start OpenCode with background subagents and websearch enabled:\n  env ${ENV_NAME}=true ${EXA_ENV_NAME}=1 opencode\n\nOr add these to your fish startup file:\n  ${fishSnippet}\n  ${fishExaSnippet}`;
   }
   }
 
 
   if (shell === 'bash' || shell === 'zsh') {
   if (shell === 'bash' || shell === 'zsh') {
-    return `Start OpenCode with background subagents enabled:\n  ${ENV_NAME}=true opencode\n\nOr add this to your shell startup file:\n  ${bashZshSnippet}`;
+    return `Start OpenCode with background subagents and websearch enabled:\n  ${ENV_NAME}=true ${EXA_ENV_NAME}=1 opencode\n\nOr add these to your shell startup file:\n  ${bashZshSnippet}\n  ${bashZshExaSnippet}`;
   }
   }
 
 
-  return `Start OpenCode with background subagents enabled:\n  ${ENV_NAME}=true opencode\n\nOr add one of these to your shell startup file:\n  bash/zsh: ${bashZshSnippet}\n  fish: ${fishSnippet}`;
+  return `Start OpenCode with background subagents and websearch enabled:\n  ${ENV_NAME}=true ${EXA_ENV_NAME}=1 opencode\n\nOr add the matching pair to your shell startup file:\n  bash/zsh: ${bashZshSnippet}\n            ${bashZshExaSnippet}\n  fish: ${fishSnippet}\n        ${fishExaSnippet}`;
 }
 }
 
 
 export function expandHomePath(targetPath: string): string {
 export function expandHomePath(targetPath: string): string {

+ 12 - 10
src/cli/install.ts

@@ -165,13 +165,15 @@ async function checkOpenCodeInstalled(): Promise<{
 export async function configureBackgroundSubagents(
 export async function configureBackgroundSubagents(
   config: InstallConfig,
   config: InstallConfig,
 ): Promise<{ enabledNow: boolean; configuredTarget?: string }> {
 ): Promise<{ enabledNow: boolean; configuredTarget?: string }> {
-  if (
-    isBackgroundSubagentsEnabled(
-      process.env.OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS,
-    )
-  ) {
+  const backgroundSubagentsEnabled = isBackgroundSubagentsEnabled(
+    process.env.OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS,
+  );
+  const exaEnabled = isBackgroundSubagentsEnabled(
+    process.env.OPENCODE_ENABLE_EXA,
+  );
+  if (backgroundSubagentsEnabled && exaEnabled) {
     printSuccess(
     printSuccess(
-      'OpenCode background subagents already enabled in environment',
+      'OpenCode background subagents and Exa websearch already enabled in environment',
     );
     );
     return { enabledNow: true };
     return { enabledNow: true };
   }
   }
@@ -218,10 +220,10 @@ export async function configureBackgroundSubagents(
       'V2 requires OpenCode background subagents for default orchestration.',
       'V2 requires OpenCode background subagents for default orchestration.',
     );
     );
     printInfo(
     printInfo(
-      `The installer can add the required environment export to ${target}.`,
+      `The installer can add the required environment exports to ${target}.`,
     );
     );
     const shouldWrite = await confirm(
     const shouldWrite = await confirm(
-      'Add OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true now?',
+      'Enable background subagents and Exa websearch now?',
       true,
       true,
     );
     );
     if (!shouldWrite) {
     if (!shouldWrite) {
@@ -244,7 +246,7 @@ export async function configureBackgroundSubagents(
   }
   }
 
 
   printSuccess(
   printSuccess(
-    `Background subagents enabled ${SYMBOLS.arrow} ${DIM}${target}${RESET}`,
+    `Background subagents and Exa websearch enabled ${SYMBOLS.arrow} ${DIM}${target}${RESET}`,
   );
   );
   return { enabledNow: false, configuredTarget: target };
   return { enabledNow: false, configuredTarget: target };
 }
 }
@@ -537,7 +539,7 @@ async function runInstall(config: InstallConfig): Promise<number> {
     );
     );
   } else {
   } else {
     console.log(
     console.log(
-      `     ${BLUE}$ OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true opencode${RESET}`,
+      `     ${BLUE}$ OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true OPENCODE_ENABLE_EXA=1 opencode${RESET}`,
     );
     );
   }
   }
   console.log();
   console.log();