fix(opencode): use correct global config path ~/.config/opencode (#117)
The OpenCode installer was writing to ~/.opencode but OpenCode expects global configuration at ~/.config/opencode per XDG Base Directory spec. Fixes: - src/commands/install.ts: Change default output from ~/.opencode to ~/.config/opencode - src/targets/opencode.ts: Recognize "opencode" basename (not just ".opencode") for direct writes without nesting Closes #114 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -175,7 +175,9 @@ function resolveOutputRoot(value: unknown): string {
|
|||||||
const expanded = expandHome(String(value).trim())
|
const expanded = expandHome(String(value).trim())
|
||||||
return path.resolve(expanded)
|
return path.resolve(expanded)
|
||||||
}
|
}
|
||||||
return path.join(os.homedir(), ".opencode")
|
// OpenCode global config lives at ~/.config/opencode per XDG spec
|
||||||
|
// See: https://opencode.ai/docs/config/
|
||||||
|
return path.join(os.homedir(), ".config", "opencode")
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resolveGitHubPluginPath(pluginName: string): Promise<ResolvedPluginPath> {
|
async function resolveGitHubPluginPath(pluginName: string): Promise<ResolvedPluginPath> {
|
||||||
|
|||||||
@@ -28,7 +28,10 @@ export async function writeOpenCodeBundle(outputRoot: string, bundle: OpenCodeBu
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resolveOpenCodePaths(outputRoot: string) {
|
function resolveOpenCodePaths(outputRoot: string) {
|
||||||
if (path.basename(outputRoot) === ".opencode") {
|
const base = path.basename(outputRoot)
|
||||||
|
// Global install: ~/.config/opencode (basename is "opencode")
|
||||||
|
// Project install: .opencode (basename is ".opencode")
|
||||||
|
if (base === "opencode" || base === ".opencode") {
|
||||||
return {
|
return {
|
||||||
root: outputRoot,
|
root: outputRoot,
|
||||||
configPath: path.join(outputRoot, "opencode.json"),
|
configPath: path.join(outputRoot, "opencode.json"),
|
||||||
@@ -38,6 +41,7 @@ function resolveOpenCodePaths(outputRoot: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Custom output directory - nest under .opencode subdirectory
|
||||||
return {
|
return {
|
||||||
root: outputRoot,
|
root: outputRoot,
|
||||||
configPath: path.join(outputRoot, "opencode.json"),
|
configPath: path.join(outputRoot, "opencode.json"),
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ describe("CLI", () => {
|
|||||||
expect(await exists(path.join(tempRoot, ".opencode", "plugins", "converted-hooks.ts"))).toBe(true)
|
expect(await exists(path.join(tempRoot, ".opencode", "plugins", "converted-hooks.ts"))).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("install defaults output to ~/.opencode", async () => {
|
test("install defaults output to ~/.config/opencode", async () => {
|
||||||
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "cli-local-default-"))
|
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "cli-local-default-"))
|
||||||
const fixtureRoot = path.join(import.meta.dir, "fixtures", "sample-plugin")
|
const fixtureRoot = path.join(import.meta.dir, "fixtures", "sample-plugin")
|
||||||
|
|
||||||
@@ -95,8 +95,9 @@ describe("CLI", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expect(stdout).toContain("Installed compound-engineering")
|
expect(stdout).toContain("Installed compound-engineering")
|
||||||
expect(await exists(path.join(tempRoot, ".opencode", "opencode.json"))).toBe(true)
|
// OpenCode global config lives at ~/.config/opencode per XDG spec
|
||||||
expect(await exists(path.join(tempRoot, ".opencode", "agents", "repo-research-analyst.md"))).toBe(true)
|
expect(await exists(path.join(tempRoot, ".config", "opencode", "opencode.json"))).toBe(true)
|
||||||
|
expect(await exists(path.join(tempRoot, ".config", "opencode", "agents", "repo-research-analyst.md"))).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("list returns plugins in a temp workspace", async () => {
|
test("list returns plugins in a temp workspace", async () => {
|
||||||
@@ -174,8 +175,9 @@ describe("CLI", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expect(stdout).toContain("Installed compound-engineering")
|
expect(stdout).toContain("Installed compound-engineering")
|
||||||
expect(await exists(path.join(tempRoot, ".opencode", "opencode.json"))).toBe(true)
|
// OpenCode global config lives at ~/.config/opencode per XDG spec
|
||||||
expect(await exists(path.join(tempRoot, ".opencode", "agents", "repo-research-analyst.md"))).toBe(true)
|
expect(await exists(path.join(tempRoot, ".config", "opencode", "opencode.json"))).toBe(true)
|
||||||
|
expect(await exists(path.join(tempRoot, ".config", "opencode", "agents", "repo-research-analyst.md"))).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("convert writes OpenCode output", async () => {
|
test("convert writes OpenCode output", async () => {
|
||||||
|
|||||||
@@ -59,4 +59,29 @@ describe("writeOpenCodeBundle", () => {
|
|||||||
expect(await exists(path.join(outputRoot, "skills", "skill-one", "SKILL.md"))).toBe(true)
|
expect(await exists(path.join(outputRoot, "skills", "skill-one", "SKILL.md"))).toBe(true)
|
||||||
expect(await exists(path.join(outputRoot, ".opencode"))).toBe(false)
|
expect(await exists(path.join(outputRoot, ".opencode"))).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("writes directly into ~/.config/opencode style output root", async () => {
|
||||||
|
// Simulates the global install path: ~/.config/opencode
|
||||||
|
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "config-opencode-"))
|
||||||
|
const outputRoot = path.join(tempRoot, ".config", "opencode")
|
||||||
|
const bundle: OpenCodeBundle = {
|
||||||
|
config: { $schema: "https://opencode.ai/config.json" },
|
||||||
|
agents: [{ name: "agent-one", content: "Agent content" }],
|
||||||
|
plugins: [],
|
||||||
|
skillDirs: [
|
||||||
|
{
|
||||||
|
name: "skill-one",
|
||||||
|
sourceDir: path.join(import.meta.dir, "fixtures", "sample-plugin", "skills", "skill-one"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
await writeOpenCodeBundle(outputRoot, bundle)
|
||||||
|
|
||||||
|
// Should write directly, not nested under .opencode
|
||||||
|
expect(await exists(path.join(outputRoot, "opencode.json"))).toBe(true)
|
||||||
|
expect(await exists(path.join(outputRoot, "agents", "agent-one.md"))).toBe(true)
|
||||||
|
expect(await exists(path.join(outputRoot, "skills", "skill-one", "SKILL.md"))).toBe(true)
|
||||||
|
expect(await exists(path.join(outputRoot, ".opencode"))).toBe(false)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user