Skip to content

Commit df58a36

Browse files
committed
fix(installer): enable lsp by default
1 parent 0bfcbb1 commit df58a36

4 files changed

Lines changed: 122 additions & 56 deletions

File tree

docs/installation.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ bunx oh-my-opencode-slim@latest install --reset
129129
The installer automatically:
130130
- Adds the plugin to `~/.config/opencode/opencode.json`
131131
- Disables default OpenCode agents
132+
- Enables OpenCode LSP integration when no explicit `lsp` setting exists
132133
- Generates agent model mappings in `~/.config/opencode/oh-my-opencode-slim.json` (or `.jsonc`)
133134

134135
### Step 3: Authenticate with Providers

src/cli/config-io.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
addPluginToOpenCodeConfig,
1616
detectCurrentConfig,
1717
disableDefaultAgents,
18+
enableLspByDefault,
1819
parseConfig,
1920
parseConfigFile,
2021
stripJsonComments,
@@ -262,6 +263,31 @@ describe('config-io', () => {
262263
expect(saved.agent.general.disable).toBe(true);
263264
});
264265

266+
test('enableLspByDefault sets lsp true when missing', () => {
267+
const configPath = join(tmpDir, 'opencode', 'opencode.json');
268+
paths.ensureConfigDir();
269+
writeFileSync(configPath, JSON.stringify({ plugin: ['other'] }));
270+
271+
const result = enableLspByDefault();
272+
expect(result.success).toBe(true);
273+
274+
const saved = JSON.parse(readFileSync(configPath, 'utf-8'));
275+
expect(saved.lsp).toBe(true);
276+
expect(saved.plugin).toEqual(['other']);
277+
});
278+
279+
test('enableLspByDefault preserves explicit lsp config', () => {
280+
const configPath = join(tmpDir, 'opencode', 'opencode.json');
281+
paths.ensureConfigDir();
282+
writeFileSync(configPath, JSON.stringify({ lsp: false }));
283+
284+
const result = enableLspByDefault();
285+
expect(result.success).toBe(true);
286+
287+
const saved = JSON.parse(readFileSync(configPath, 'utf-8'));
288+
expect(saved.lsp).toBe(false);
289+
});
290+
265291
test('detectCurrentConfig detects installed status', () => {
266292
const configPath = join(tmpDir, 'opencode', 'opencode.json');
267293
const litePath = join(tmpDir, 'opencode', 'oh-my-opencode-slim.json');

src/cli/config-io.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,36 @@ export function disableDefaultAgents(): ConfigMergeResult {
307307
}
308308
}
309309

310+
export function enableLspByDefault(): ConfigMergeResult {
311+
const configPath = getExistingConfigPath();
312+
313+
try {
314+
ensureOpenCodeConfigDir();
315+
const { config: parsedConfig, error } = parseConfig(configPath);
316+
if (error) {
317+
return {
318+
success: false,
319+
configPath,
320+
error: `Failed to parse config: ${error}`,
321+
};
322+
}
323+
const config = parsedConfig ?? {};
324+
325+
if (config.lsp === undefined) {
326+
config.lsp = true;
327+
}
328+
329+
writeConfig(configPath, config);
330+
return { success: true, configPath };
331+
} catch (err) {
332+
return {
333+
success: false,
334+
configPath,
335+
error: `Failed to enable LSP: ${err}`,
336+
};
337+
}
338+
}
339+
310340
export function canModifyOpenCodeConfig(): boolean {
311341
try {
312342
const configPath = getExistingConfigPath();

src/cli/install.ts

Lines changed: 65 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,28 @@
1-
import { existsSync } from "node:fs";
1+
import { existsSync } from 'node:fs';
22
import {
33
addPluginToOpenCodeConfig,
44
detectCurrentConfig,
55
disableDefaultAgents,
6+
enableLspByDefault,
67
generateLiteConfig,
78
getOpenCodePath,
89
getOpenCodeVersion,
910
isOpenCodeInstalled,
1011
writeLiteConfig,
11-
} from "./config-manager";
12-
import { CUSTOM_SKILLS, installCustomSkill } from "./custom-skills";
13-
import { getExistingLiteConfigPath } from "./paths";
14-
import { installSkill, RECOMMENDED_SKILLS } from "./skills";
15-
import type { ConfigMergeResult, InstallArgs, InstallConfig } from "./types";
12+
} from './config-manager';
13+
import { CUSTOM_SKILLS, installCustomSkill } from './custom-skills';
14+
import { getExistingLiteConfigPath } from './paths';
15+
import { installSkill, RECOMMENDED_SKILLS } from './skills';
16+
import type { ConfigMergeResult, InstallArgs, InstallConfig } from './types';
1617

1718
// Colors
18-
const GREEN = "\x1b[32m";
19-
const BLUE = "\x1b[34m";
20-
const YELLOW = "\x1b[33m";
21-
const RED = "\x1b[31m";
22-
const BOLD = "\x1b[1m";
23-
const DIM = "\x1b[2m";
24-
const RESET = "\x1b[0m";
19+
const GREEN = '\x1b[32m';
20+
const BLUE = '\x1b[34m';
21+
const YELLOW = '\x1b[33m';
22+
const RED = '\x1b[31m';
23+
const BOLD = '\x1b[1m';
24+
const DIM = '\x1b[2m';
25+
const RESET = '\x1b[0m';
2526

2627
const SYMBOLS = {
2728
check: `${GREEN}[ok]${RESET}`,
@@ -36,9 +37,9 @@ const SYMBOLS = {
3637
function printHeader(isUpdate: boolean): void {
3738
console.log();
3839
console.log(
39-
`${BOLD}oh-my-opencode-slim ${isUpdate ? "Update" : "Install"}${RESET}`
40+
`${BOLD}oh-my-opencode-slim ${isUpdate ? 'Update' : 'Install'}${RESET}`,
4041
);
41-
console.log("=".repeat(30));
42+
console.log('='.repeat(30));
4243
console.log();
4344
}
4445

@@ -65,35 +66,35 @@ async function checkOpenCodeInstalled(): Promise<{
6566
}> {
6667
const installed = await isOpenCodeInstalled();
6768
if (!installed) {
68-
printError("OpenCode is not installed on this system.");
69-
printInfo("Install it with:");
69+
printError('OpenCode is not installed on this system.');
70+
printInfo('Install it with:');
7071
console.log(
71-
` ${BLUE}curl -fsSL https://opencode.ai/install | bash${RESET}`
72+
` ${BLUE}curl -fsSL https://opencode.ai/install | bash${RESET}`,
7273
);
7374
console.log();
74-
printInfo("Or if already installed, add it to your PATH:");
75+
printInfo('Or if already installed, add it to your PATH:');
7576
console.log(` ${BLUE}export PATH="$HOME/.local/bin:$PATH"${RESET}`);
7677
console.log(` ${BLUE}export PATH="$HOME/.opencode/bin:$PATH"${RESET}`);
7778
return { ok: false };
7879
}
7980
const version = await getOpenCodeVersion();
8081
const path = getOpenCodePath();
81-
const detectedVersion = version ?? "";
82-
const pathInfo = path ? ` (${DIM}${path}${RESET})` : "";
82+
const detectedVersion = version ?? '';
83+
const pathInfo = path ? ` (${DIM}${path}${RESET})` : '';
8384
printSuccess(`OpenCode ${detectedVersion} detected${pathInfo}`);
8485
return { ok: true, version: version ?? undefined, path: path ?? undefined };
8586
}
8687

8788
function handleStepResult(
8889
result: ConfigMergeResult,
89-
successMsg: string
90+
successMsg: string,
9091
): boolean {
9192
if (!result.success) {
9293
printError(`Failed: ${result.error}`);
9394
return false;
9495
}
9596
printSuccess(
96-
`${successMsg} ${SYMBOLS.arrow} ${DIM}${result.configPath}${RESET}`
97+
`${successMsg} ${SYMBOLS.arrow} ${DIM}${result.configPath}${RESET}`,
9798
);
9899
return true;
99100
}
@@ -104,38 +105,46 @@ async function runInstall(config: InstallConfig): Promise<number> {
104105

105106
printHeader(isUpdate);
106107

107-
let totalSteps = 4;
108+
let totalSteps = 5;
108109
if (config.installSkills) totalSteps += 1;
109110
if (config.installCustomSkills) totalSteps += 1;
110111

111112
let step = 1;
112113

113-
printStep(step++, totalSteps, "Checking OpenCode installation...");
114+
printStep(step++, totalSteps, 'Checking OpenCode installation...');
114115
if (config.dryRun) {
115-
printInfo("Dry run mode - skipping OpenCode check");
116+
printInfo('Dry run mode - skipping OpenCode check');
116117
} else {
117118
const { ok } = await checkOpenCodeInstalled();
118119
if (!ok) return 1;
119120
}
120-
printStep(step++, totalSteps, "Adding oh-my-opencode-slim plugin...");
121+
printStep(step++, totalSteps, 'Adding oh-my-opencode-slim plugin...');
121122
if (config.dryRun) {
122-
printInfo("Dry run mode - skipping plugin installation");
123+
printInfo('Dry run mode - skipping plugin installation');
123124
} else {
124125
const pluginResult = await addPluginToOpenCodeConfig();
125-
if (!handleStepResult(pluginResult, "Plugin added")) return 1;
126+
if (!handleStepResult(pluginResult, 'Plugin added')) return 1;
126127
}
127-
printStep(step++, totalSteps, "Disabling OpenCode default agents...");
128+
printStep(step++, totalSteps, 'Disabling OpenCode default agents...');
128129
if (config.dryRun) {
129-
printInfo("Dry run mode - skipping agent disabling");
130+
printInfo('Dry run mode - skipping agent disabling');
130131
} else {
131132
const agentResult = disableDefaultAgents();
132-
if (!handleStepResult(agentResult, "Default agents disabled")) return 1;
133+
if (!handleStepResult(agentResult, 'Default agents disabled')) return 1;
133134
}
134135

135-
printStep(step++, totalSteps, "Writing oh-my-opencode-slim configuration...");
136+
printStep(step++, totalSteps, 'Enabling OpenCode LSP integration...');
137+
if (config.dryRun) {
138+
printInfo('Dry run mode - skipping LSP configuration');
139+
} else {
140+
const lspResult = enableLspByDefault();
141+
if (!handleStepResult(lspResult, 'LSP enabled')) return 1;
142+
}
143+
144+
printStep(step++, totalSteps, 'Writing oh-my-opencode-slim configuration...');
136145
if (config.dryRun) {
137146
const liteConfig = generateLiteConfig(config);
138-
printInfo("Dry run mode - configuration that would be written:");
147+
printInfo('Dry run mode - configuration that would be written:');
139148
console.log(`\n${JSON.stringify(liteConfig, null, 2)}\n`);
140149
} else {
141150
const configPath = getExistingLiteConfigPath();
@@ -144,17 +153,17 @@ async function runInstall(config: InstallConfig): Promise<number> {
144153
if (configExists && !config.reset) {
145154
printInfo(
146155
`Configuration already exists at ${configPath}. ` +
147-
"Use --reset to overwrite."
156+
'Use --reset to overwrite.',
148157
);
149158
} else {
150159
const liteResult = writeLiteConfig(
151160
config,
152-
configExists ? configPath : undefined
161+
configExists ? configPath : undefined,
153162
);
154163
if (
155164
!handleStepResult(
156165
liteResult,
157-
configExists ? "Config reset" : "Config written"
166+
configExists ? 'Config reset' : 'Config written',
158167
)
159168
)
160169
return 1;
@@ -163,9 +172,9 @@ async function runInstall(config: InstallConfig): Promise<number> {
163172

164173
// Install skills if requested
165174
if (config.installSkills) {
166-
printStep(step++, totalSteps, "Installing recommended skills...");
175+
printStep(step++, totalSteps, 'Installing recommended skills...');
167176
if (config.dryRun) {
168-
printInfo("Dry run mode - would install skills:");
177+
printInfo('Dry run mode - would install skills:');
169178
for (const skill of RECOMMENDED_SKILLS) {
170179
printInfo(` - ${skill.name}`);
171180
}
@@ -181,16 +190,16 @@ async function runInstall(config: InstallConfig): Promise<number> {
181190
}
182191
}
183192
printSuccess(
184-
`${skillsInstalled}/${RECOMMENDED_SKILLS.length} skills processed`
193+
`${skillsInstalled}/${RECOMMENDED_SKILLS.length} skills processed`,
185194
);
186195
}
187196
}
188197

189198
// Install custom skills if requested
190199
if (config.installCustomSkills) {
191-
printStep(step++, totalSteps, "Installing custom skills...");
200+
printStep(step++, totalSteps, 'Installing custom skills...');
192201
if (config.dryRun) {
193-
printInfo("Dry run mode - would install custom skills:");
202+
printInfo('Dry run mode - would install custom skills:');
194203
for (const skill of CUSTOM_SKILLS) {
195204
printInfo(` - ${skill.name}`);
196205
}
@@ -207,45 +216,45 @@ async function runInstall(config: InstallConfig): Promise<number> {
207216
}
208217
const totalCustom = CUSTOM_SKILLS.length;
209218
printSuccess(
210-
`${customSkillsInstalled}/${totalCustom} custom skills processed`
219+
`${customSkillsInstalled}/${totalCustom} custom skills processed`,
211220
);
212221
}
213222
}
214223

215224
const statusMsg = isUpdate
216-
? "Configuration updated!"
217-
: "Installation complete!";
225+
? 'Configuration updated!'
226+
: 'Installation complete!';
218227
console.log(`${SYMBOLS.star} ${BOLD}${GREEN}${statusMsg}${RESET}`);
219228
console.log();
220229
console.log(`${BOLD}Next steps:${RESET}`);
221230
console.log();
222231

223232
const configPath = getExistingLiteConfigPath();
224233

225-
console.log(" 1. Log in to the provider(s) you want to use:");
234+
console.log(' 1. Log in to the provider(s) you want to use:');
226235
console.log(` ${BLUE}$ opencode auth login${RESET}`);
227236
console.log();
228-
console.log(" 2. Refresh the models OpenCode can see:");
237+
console.log(' 2. Refresh the models OpenCode can see:');
229238
console.log(` ${BLUE}$ opencode models --refresh${RESET}`);
230239
console.log();
231-
console.log(" 3. Review your generated config:");
240+
console.log(' 3. Review your generated config:');
232241
console.log(` ${BLUE}${configPath}${RESET}`);
233242
console.log();
234-
console.log(" 4. Start OpenCode:");
243+
console.log(' 4. Start OpenCode:');
235244
console.log(` ${BLUE}$ opencode${RESET}`);
236245
console.log();
237-
console.log(" 5. Verify the agents are responding:");
246+
console.log(' 5. Verify the agents are responding:');
238247
console.log(` ${BLUE}> ping all agents${RESET}`);
239248
console.log();
240249

241250
const modelsInfo =
242-
"Default configuration uses OpenAI models (gpt-5.5 / gpt-5.4-mini).";
251+
'Default configuration uses OpenAI models (gpt-5.5 / gpt-5.4-mini).';
243252
console.log(`${modelsInfo}`);
244-
const altProviders = "For the full configuration reference, see:";
253+
const altProviders = 'For the full configuration reference, see:';
245254
console.log(altProviders);
246255
const docsUrl =
247-
"https://github.com/alvinunreal/oh-my-opencode-slim/" +
248-
"blob/master/docs/configuration.md";
256+
'https://github.com/alvinunreal/oh-my-opencode-slim/' +
257+
'blob/master/docs/configuration.md';
249258
console.log(` ${BLUE}${docsUrl}${RESET}`);
250259
console.log();
251260

@@ -255,8 +264,8 @@ async function runInstall(config: InstallConfig): Promise<number> {
255264
export async function install(args: InstallArgs): Promise<number> {
256265
const config: InstallConfig = {
257266
hasTmux: false,
258-
installSkills: args.skills === "yes",
259-
installCustomSkills: args.skills === "yes",
267+
installSkills: args.skills === 'yes',
268+
installCustomSkills: args.skills === 'yes',
260269
dryRun: args.dryRun,
261270
reset: args.reset ?? false,
262271
};

0 commit comments

Comments
 (0)