Browse Source

fix(installer): enable lsp by default

Alvin Unreal 3 months ago
parent
commit
df58a3680c
4 changed files with 122 additions and 56 deletions
  1. 1 0
      docs/installation.md
  2. 26 0
      src/cli/config-io.test.ts
  3. 30 0
      src/cli/config-io.ts
  4. 65 56
      src/cli/install.ts

+ 1 - 0
docs/installation.md

@@ -129,6 +129,7 @@ bunx oh-my-opencode-slim@latest install --reset
 The installer automatically:
 - Adds the plugin to `~/.config/opencode/opencode.json`
 - Disables default OpenCode agents
+- Enables OpenCode LSP integration when no explicit `lsp` setting exists
 - Generates agent model mappings in `~/.config/opencode/oh-my-opencode-slim.json` (or `.jsonc`)
 
 ### Step 3: Authenticate with Providers

+ 26 - 0
src/cli/config-io.test.ts

@@ -15,6 +15,7 @@ import {
   addPluginToOpenCodeConfig,
   detectCurrentConfig,
   disableDefaultAgents,
+  enableLspByDefault,
   parseConfig,
   parseConfigFile,
   stripJsonComments,
@@ -262,6 +263,31 @@ describe('config-io', () => {
     expect(saved.agent.general.disable).toBe(true);
   });
 
+  test('enableLspByDefault sets lsp true when missing', () => {
+    const configPath = join(tmpDir, 'opencode', 'opencode.json');
+    paths.ensureConfigDir();
+    writeFileSync(configPath, JSON.stringify({ plugin: ['other'] }));
+
+    const result = enableLspByDefault();
+    expect(result.success).toBe(true);
+
+    const saved = JSON.parse(readFileSync(configPath, 'utf-8'));
+    expect(saved.lsp).toBe(true);
+    expect(saved.plugin).toEqual(['other']);
+  });
+
+  test('enableLspByDefault preserves explicit lsp config', () => {
+    const configPath = join(tmpDir, 'opencode', 'opencode.json');
+    paths.ensureConfigDir();
+    writeFileSync(configPath, JSON.stringify({ lsp: false }));
+
+    const result = enableLspByDefault();
+    expect(result.success).toBe(true);
+
+    const saved = JSON.parse(readFileSync(configPath, 'utf-8'));
+    expect(saved.lsp).toBe(false);
+  });
+
   test('detectCurrentConfig detects installed status', () => {
     const configPath = join(tmpDir, 'opencode', 'opencode.json');
     const litePath = join(tmpDir, 'opencode', 'oh-my-opencode-slim.json');

+ 30 - 0
src/cli/config-io.ts

@@ -307,6 +307,36 @@ export function disableDefaultAgents(): ConfigMergeResult {
   }
 }
 
+export function enableLspByDefault(): ConfigMergeResult {
+  const configPath = getExistingConfigPath();
+
+  try {
+    ensureOpenCodeConfigDir();
+    const { config: parsedConfig, error } = parseConfig(configPath);
+    if (error) {
+      return {
+        success: false,
+        configPath,
+        error: `Failed to parse config: ${error}`,
+      };
+    }
+    const config = parsedConfig ?? {};
+
+    if (config.lsp === undefined) {
+      config.lsp = true;
+    }
+
+    writeConfig(configPath, config);
+    return { success: true, configPath };
+  } catch (err) {
+    return {
+      success: false,
+      configPath,
+      error: `Failed to enable LSP: ${err}`,
+    };
+  }
+}
+
 export function canModifyOpenCodeConfig(): boolean {
   try {
     const configPath = getExistingConfigPath();

+ 65 - 56
src/cli/install.ts

@@ -1,27 +1,28 @@
-import { existsSync } from "node:fs";
+import { existsSync } from 'node:fs';
 import {
   addPluginToOpenCodeConfig,
   detectCurrentConfig,
   disableDefaultAgents,
+  enableLspByDefault,
   generateLiteConfig,
   getOpenCodePath,
   getOpenCodeVersion,
   isOpenCodeInstalled,
   writeLiteConfig,
-} from "./config-manager";
-import { CUSTOM_SKILLS, installCustomSkill } from "./custom-skills";
-import { getExistingLiteConfigPath } from "./paths";
-import { installSkill, RECOMMENDED_SKILLS } from "./skills";
-import type { ConfigMergeResult, InstallArgs, InstallConfig } from "./types";
+} from './config-manager';
+import { CUSTOM_SKILLS, installCustomSkill } from './custom-skills';
+import { getExistingLiteConfigPath } from './paths';
+import { installSkill, RECOMMENDED_SKILLS } from './skills';
+import type { ConfigMergeResult, InstallArgs, InstallConfig } from './types';
 
 // Colors
-const GREEN = "\x1b[32m";
-const BLUE = "\x1b[34m";
-const YELLOW = "\x1b[33m";
-const RED = "\x1b[31m";
-const BOLD = "\x1b[1m";
-const DIM = "\x1b[2m";
-const RESET = "\x1b[0m";
+const GREEN = '\x1b[32m';
+const BLUE = '\x1b[34m';
+const YELLOW = '\x1b[33m';
+const RED = '\x1b[31m';
+const BOLD = '\x1b[1m';
+const DIM = '\x1b[2m';
+const RESET = '\x1b[0m';
 
 const SYMBOLS = {
   check: `${GREEN}[ok]${RESET}`,
@@ -36,9 +37,9 @@ const SYMBOLS = {
 function printHeader(isUpdate: boolean): void {
   console.log();
   console.log(
-    `${BOLD}oh-my-opencode-slim ${isUpdate ? "Update" : "Install"}${RESET}`
+    `${BOLD}oh-my-opencode-slim ${isUpdate ? 'Update' : 'Install'}${RESET}`,
   );
-  console.log("=".repeat(30));
+  console.log('='.repeat(30));
   console.log();
 }
 
@@ -65,35 +66,35 @@ async function checkOpenCodeInstalled(): Promise<{
 }> {
   const installed = await isOpenCodeInstalled();
   if (!installed) {
-    printError("OpenCode is not installed on this system.");
-    printInfo("Install it with:");
+    printError('OpenCode is not installed on this system.');
+    printInfo('Install it with:');
     console.log(
-      `     ${BLUE}curl -fsSL https://opencode.ai/install | bash${RESET}`
+      `     ${BLUE}curl -fsSL https://opencode.ai/install | bash${RESET}`,
     );
     console.log();
-    printInfo("Or if already installed, add it to your PATH:");
+    printInfo('Or if already installed, add it to your PATH:');
     console.log(`     ${BLUE}export PATH="$HOME/.local/bin:$PATH"${RESET}`);
     console.log(`     ${BLUE}export PATH="$HOME/.opencode/bin:$PATH"${RESET}`);
     return { ok: false };
   }
   const version = await getOpenCodeVersion();
   const path = getOpenCodePath();
-  const detectedVersion = version ?? "";
-  const pathInfo = path ? ` (${DIM}${path}${RESET})` : "";
+  const detectedVersion = version ?? '';
+  const pathInfo = path ? ` (${DIM}${path}${RESET})` : '';
   printSuccess(`OpenCode ${detectedVersion} detected${pathInfo}`);
   return { ok: true, version: version ?? undefined, path: path ?? undefined };
 }
 
 function handleStepResult(
   result: ConfigMergeResult,
-  successMsg: string
+  successMsg: string,
 ): boolean {
   if (!result.success) {
     printError(`Failed: ${result.error}`);
     return false;
   }
   printSuccess(
-    `${successMsg} ${SYMBOLS.arrow} ${DIM}${result.configPath}${RESET}`
+    `${successMsg} ${SYMBOLS.arrow} ${DIM}${result.configPath}${RESET}`,
   );
   return true;
 }
@@ -104,38 +105,46 @@ async function runInstall(config: InstallConfig): Promise<number> {
 
   printHeader(isUpdate);
 
-  let totalSteps = 4;
+  let totalSteps = 5;
   if (config.installSkills) totalSteps += 1;
   if (config.installCustomSkills) totalSteps += 1;
 
   let step = 1;
 
-  printStep(step++, totalSteps, "Checking OpenCode installation...");
+  printStep(step++, totalSteps, 'Checking OpenCode installation...');
   if (config.dryRun) {
-    printInfo("Dry run mode - skipping OpenCode check");
+    printInfo('Dry run mode - skipping OpenCode check');
   } else {
     const { ok } = await checkOpenCodeInstalled();
     if (!ok) return 1;
   }
-  printStep(step++, totalSteps, "Adding oh-my-opencode-slim plugin...");
+  printStep(step++, totalSteps, 'Adding oh-my-opencode-slim plugin...');
   if (config.dryRun) {
-    printInfo("Dry run mode - skipping plugin installation");
+    printInfo('Dry run mode - skipping plugin installation');
   } else {
     const pluginResult = await addPluginToOpenCodeConfig();
-    if (!handleStepResult(pluginResult, "Plugin added")) return 1;
+    if (!handleStepResult(pluginResult, 'Plugin added')) return 1;
   }
-  printStep(step++, totalSteps, "Disabling OpenCode default agents...");
+  printStep(step++, totalSteps, 'Disabling OpenCode default agents...');
   if (config.dryRun) {
-    printInfo("Dry run mode - skipping agent disabling");
+    printInfo('Dry run mode - skipping agent disabling');
   } else {
     const agentResult = disableDefaultAgents();
-    if (!handleStepResult(agentResult, "Default agents disabled")) return 1;
+    if (!handleStepResult(agentResult, 'Default agents disabled')) return 1;
   }
 
-  printStep(step++, totalSteps, "Writing oh-my-opencode-slim configuration...");
+  printStep(step++, totalSteps, 'Enabling OpenCode LSP integration...');
+  if (config.dryRun) {
+    printInfo('Dry run mode - skipping LSP configuration');
+  } else {
+    const lspResult = enableLspByDefault();
+    if (!handleStepResult(lspResult, 'LSP enabled')) return 1;
+  }
+
+  printStep(step++, totalSteps, 'Writing oh-my-opencode-slim configuration...');
   if (config.dryRun) {
     const liteConfig = generateLiteConfig(config);
-    printInfo("Dry run mode - configuration that would be written:");
+    printInfo('Dry run mode - configuration that would be written:');
     console.log(`\n${JSON.stringify(liteConfig, null, 2)}\n`);
   } else {
     const configPath = getExistingLiteConfigPath();
@@ -144,17 +153,17 @@ async function runInstall(config: InstallConfig): Promise<number> {
     if (configExists && !config.reset) {
       printInfo(
         `Configuration already exists at ${configPath}. ` +
-          "Use --reset to overwrite."
+          'Use --reset to overwrite.',
       );
     } else {
       const liteResult = writeLiteConfig(
         config,
-        configExists ? configPath : undefined
+        configExists ? configPath : undefined,
       );
       if (
         !handleStepResult(
           liteResult,
-          configExists ? "Config reset" : "Config written"
+          configExists ? 'Config reset' : 'Config written',
         )
       )
         return 1;
@@ -163,9 +172,9 @@ async function runInstall(config: InstallConfig): Promise<number> {
 
   // Install skills if requested
   if (config.installSkills) {
-    printStep(step++, totalSteps, "Installing recommended skills...");
+    printStep(step++, totalSteps, 'Installing recommended skills...');
     if (config.dryRun) {
-      printInfo("Dry run mode - would install skills:");
+      printInfo('Dry run mode - would install skills:');
       for (const skill of RECOMMENDED_SKILLS) {
         printInfo(`  - ${skill.name}`);
       }
@@ -181,16 +190,16 @@ async function runInstall(config: InstallConfig): Promise<number> {
         }
       }
       printSuccess(
-        `${skillsInstalled}/${RECOMMENDED_SKILLS.length} skills processed`
+        `${skillsInstalled}/${RECOMMENDED_SKILLS.length} skills processed`,
       );
     }
   }
 
   // Install custom skills if requested
   if (config.installCustomSkills) {
-    printStep(step++, totalSteps, "Installing custom skills...");
+    printStep(step++, totalSteps, 'Installing custom skills...');
     if (config.dryRun) {
-      printInfo("Dry run mode - would install custom skills:");
+      printInfo('Dry run mode - would install custom skills:');
       for (const skill of CUSTOM_SKILLS) {
         printInfo(`  - ${skill.name}`);
       }
@@ -207,14 +216,14 @@ async function runInstall(config: InstallConfig): Promise<number> {
       }
       const totalCustom = CUSTOM_SKILLS.length;
       printSuccess(
-        `${customSkillsInstalled}/${totalCustom} custom skills processed`
+        `${customSkillsInstalled}/${totalCustom} custom skills processed`,
       );
     }
   }
 
   const statusMsg = isUpdate
-    ? "Configuration updated!"
-    : "Installation complete!";
+    ? 'Configuration updated!'
+    : 'Installation complete!';
   console.log(`${SYMBOLS.star} ${BOLD}${GREEN}${statusMsg}${RESET}`);
   console.log();
   console.log(`${BOLD}Next steps:${RESET}`);
@@ -222,30 +231,30 @@ async function runInstall(config: InstallConfig): Promise<number> {
 
   const configPath = getExistingLiteConfigPath();
 
-  console.log("  1. Log in to the provider(s) you want to use:");
+  console.log('  1. Log in to the provider(s) you want to use:');
   console.log(`     ${BLUE}$ opencode auth login${RESET}`);
   console.log();
-  console.log("  2. Refresh the models OpenCode can see:");
+  console.log('  2. Refresh the models OpenCode can see:');
   console.log(`     ${BLUE}$ opencode models --refresh${RESET}`);
   console.log();
-  console.log("  3. Review your generated config:");
+  console.log('  3. Review your generated config:');
   console.log(`     ${BLUE}${configPath}${RESET}`);
   console.log();
-  console.log("  4. Start OpenCode:");
+  console.log('  4. Start OpenCode:');
   console.log(`     ${BLUE}$ opencode${RESET}`);
   console.log();
-  console.log("  5. Verify the agents are responding:");
+  console.log('  5. Verify the agents are responding:');
   console.log(`     ${BLUE}> ping all agents${RESET}`);
   console.log();
 
   const modelsInfo =
-    "Default configuration uses OpenAI models (gpt-5.5 / gpt-5.4-mini).";
+    'Default configuration uses OpenAI models (gpt-5.5 / gpt-5.4-mini).';
   console.log(`${modelsInfo}`);
-  const altProviders = "For the full configuration reference, see:";
+  const altProviders = 'For the full configuration reference, see:';
   console.log(altProviders);
   const docsUrl =
-    "https://github.com/alvinunreal/oh-my-opencode-slim/" +
-    "blob/master/docs/configuration.md";
+    'https://github.com/alvinunreal/oh-my-opencode-slim/' +
+    'blob/master/docs/configuration.md';
   console.log(`  ${BLUE}${docsUrl}${RESET}`);
   console.log();
 
@@ -255,8 +264,8 @@ async function runInstall(config: InstallConfig): Promise<number> {
 export async function install(args: InstallArgs): Promise<number> {
   const config: InstallConfig = {
     hasTmux: false,
-    installSkills: args.skills === "yes",
-    installCustomSkills: args.skills === "yes",
+    installSkills: args.skills === 'yes',
+    installCustomSkills: args.skills === 'yes',
     dryRun: args.dryRun,
     reset: args.reset ?? false,
   };