Skip to content

Commit fe61437

Browse files
committed
fix(update): prevent updateRemoveDir from deleting non-wrapper directories
The ?? fallback after wrapperSpec() returned undefined was reading dependencies from an arbitrary parent directory's package.json, which could match and return that directory as the removal target. For example, when the plugin sits inside .config/opencode/node_modules/, updateRemoveDir would return .config/opencode as the dir to rm -rf, because .config/opencode/package.json lists the DCP dependency. Remove the fallback entirely. wrapperSpec() is the sole authority: if the wrapper directory doesn't match the expected opencode naming convention (@scope/pkg@spec), return undefined. Add a regression test that reproduces the .config/opencode-like directory structure and asserts undefined is returned.
1 parent 2cc6b88 commit fe61437

2 files changed

Lines changed: 18 additions & 2 deletions

File tree

lib/update.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,7 @@ export async function updateRemoveDir(packageDir: string, name: string) {
8787
if (basename(nodeModulesDir) !== "node_modules") return undefined
8888

8989
const wrapperDir = dirname(nodeModulesDir)
90-
const wrapperPkg = await readPackageJson(join(wrapperDir, "package.json"))
91-
const spec = wrapperSpec(wrapperDir, name) ?? wrapperPkg?.dependencies?.[name]
90+
const spec = wrapperSpec(wrapperDir, name)
9291
if (!spec || !isAutoUpdatableSpec(spec)) return undefined
9392

9493
return wrapperDir

tests/update.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,23 @@ test("updateRemoveDir removes opencode npm wrapper for latest installs", async (
4040
assert.equal(await updateRemoveDir(packageDir, "@tarquinen/opencode-dcp"), wrapperDir)
4141
})
4242

43+
test("updateRemoveDir rejects directories not matching opencode wrapper naming", async () => {
44+
const rootDir = await mkdtemp(join(tmpdir(), "dcp-update-"))
45+
// Simulate .config/opencode-like setup: plugin lives in node_modules but
46+
// the parent dir is NOT an @scope/pkg@spec wrapper — updateRemoveDir
47+
// must NOT return the parent dir even if its package.json lists the dep.
48+
const packageDir = join(rootDir, "node_modules", "@tarquinen", "opencode-dcp")
49+
await writePackageJson(rootDir, {
50+
dependencies: { "@tarquinen/opencode-dcp": "latest" },
51+
})
52+
await writePackageJson(packageDir, {
53+
name: "@tarquinen/opencode-dcp",
54+
version: "3.1.9",
55+
})
56+
57+
assert.equal(await updateRemoveDir(packageDir, "@tarquinen/opencode-dcp"), undefined)
58+
})
59+
4360
test("updateRemoveDir skips version-locked opencode installs", async () => {
4461
const rootDir = await mkdtemp(join(tmpdir(), "dcp-update-"))
4562
const wrapperDir = join(rootDir, "@tarquinen", "opencode-dcp@3.1.9")

0 commit comments

Comments
 (0)