@every-env/compound-plugin 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@every-env/compound-plugin",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "bin": {
@@ -88,7 +88,8 @@ export default defineCommand({
88
88
  if (!bundle) {
89
89
  throw new Error(`Target ${targetName} did not return a bundle.`)
90
90
  }
91
- const primaryOutputRoot = resolveTargetOutputRoot(targetName, outputRoot, codexHome)
91
+ const hasExplicitOutput = Boolean(args.output && String(args.output).trim())
92
+ const primaryOutputRoot = resolveTargetOutputRoot(targetName, outputRoot, codexHome, hasExplicitOutput)
92
93
  await target.write(primaryOutputRoot, bundle)
93
94
  console.log(`Installed ${plugin.manifest.name} to ${primaryOutputRoot}`)
94
95
 
@@ -109,7 +110,7 @@ export default defineCommand({
109
110
  console.warn(`Skipping ${extra}: no output returned.`)
110
111
  continue
111
112
  }
112
- const extraRoot = resolveTargetOutputRoot(extra, path.join(outputRoot, extra), codexHome)
113
+ const extraRoot = resolveTargetOutputRoot(extra, path.join(outputRoot, extra), codexHome, hasExplicitOutput)
113
114
  await handler.write(extraRoot, extraBundle)
114
115
  console.log(`Installed ${plugin.manifest.name} to ${extraRoot}`)
115
116
  }
@@ -131,12 +132,15 @@ type ResolvedPluginPath = {
131
132
  }
132
133
 
133
134
  async function resolvePluginPath(input: string): Promise<ResolvedPluginPath> {
134
- const directPath = path.resolve(input)
135
- if (await pathExists(directPath)) return { path: directPath }
136
-
137
- const pluginsPath = path.join(process.cwd(), "plugins", input)
138
- if (await pathExists(pluginsPath)) return { path: pluginsPath }
135
+ // Only treat as a local path if it explicitly looks like one
136
+ if (input.startsWith(".") || input.startsWith("/") || input.startsWith("~")) {
137
+ const expanded = expandHome(input)
138
+ const directPath = path.resolve(expanded)
139
+ if (await pathExists(directPath)) return { path: directPath }
140
+ throw new Error(`Local plugin path not found: ${directPath}`)
141
+ }
139
142
 
143
+ // Otherwise, always fetch the latest from GitHub
140
144
  return await resolveGitHubPluginPath(input)
141
145
  }
142
146
 
@@ -178,10 +182,13 @@ function resolveOutputRoot(value: unknown): string {
178
182
  return path.join(os.homedir(), ".config", "opencode")
179
183
  }
180
184
 
181
- function resolveTargetOutputRoot(targetName: string, outputRoot: string, codexHome: string): string {
185
+ function resolveTargetOutputRoot(targetName: string, outputRoot: string, codexHome: string, hasExplicitOutput: boolean): string {
182
186
  if (targetName === "codex") return codexHome
183
187
  if (targetName === "droid") return path.join(os.homedir(), ".factory")
184
- if (targetName === "cursor") return path.join(outputRoot, ".cursor")
188
+ if (targetName === "cursor") {
189
+ const base = hasExplicitOutput ? outputRoot : process.cwd()
190
+ return path.join(base, ".cursor")
191
+ }
185
192
  return outputRoot
186
193
  }
187
194
 
package/tests/cli.test.ts CHANGED
@@ -180,6 +180,68 @@ describe("CLI", () => {
180
180
  expect(await exists(path.join(tempRoot, ".config", "opencode", "agents", "repo-research-analyst.md"))).toBe(true)
181
181
  })
182
182
 
183
+ test("install by name ignores same-named local directory", async () => {
184
+ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "cli-shadow-"))
185
+ const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), "cli-shadow-workspace-"))
186
+ const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), "cli-shadow-repo-"))
187
+
188
+ // Create a directory with the plugin name that is NOT a valid plugin
189
+ const shadowDir = path.join(workspaceRoot, "compound-engineering")
190
+ await fs.mkdir(shadowDir, { recursive: true })
191
+ await fs.writeFile(path.join(shadowDir, "README.md"), "Not a plugin")
192
+
193
+ // Set up a fake GitHub source with a valid plugin
194
+ const fixtureRoot = path.join(import.meta.dir, "fixtures", "sample-plugin")
195
+ const pluginRoot = path.join(repoRoot, "plugins", "compound-engineering")
196
+ await fs.mkdir(path.dirname(pluginRoot), { recursive: true })
197
+ await fs.cp(fixtureRoot, pluginRoot, { recursive: true })
198
+
199
+ const gitEnv = {
200
+ ...process.env,
201
+ GIT_AUTHOR_NAME: "Test",
202
+ GIT_AUTHOR_EMAIL: "test@example.com",
203
+ GIT_COMMITTER_NAME: "Test",
204
+ GIT_COMMITTER_EMAIL: "test@example.com",
205
+ }
206
+ await runGit(["init"], repoRoot, gitEnv)
207
+ await runGit(["add", "."], repoRoot, gitEnv)
208
+ await runGit(["commit", "-m", "fixture"], repoRoot, gitEnv)
209
+
210
+ const projectRoot = path.join(import.meta.dir, "..")
211
+ const proc = Bun.spawn([
212
+ "bun",
213
+ "run",
214
+ path.join(projectRoot, "src", "index.ts"),
215
+ "install",
216
+ "compound-engineering",
217
+ "--to",
218
+ "opencode",
219
+ "--output",
220
+ tempRoot,
221
+ ], {
222
+ cwd: workspaceRoot,
223
+ stdout: "pipe",
224
+ stderr: "pipe",
225
+ env: {
226
+ ...process.env,
227
+ HOME: tempRoot,
228
+ COMPOUND_PLUGIN_GITHUB_SOURCE: repoRoot,
229
+ },
230
+ })
231
+
232
+ const exitCode = await proc.exited
233
+ const stdout = await new Response(proc.stdout).text()
234
+ const stderr = await new Response(proc.stderr).text()
235
+
236
+ if (exitCode !== 0) {
237
+ throw new Error(`CLI failed (exit ${exitCode}).\nstdout: ${stdout}\nstderr: ${stderr}`)
238
+ }
239
+
240
+ // Should succeed by fetching from GitHub, NOT failing on the local shadow directory
241
+ expect(stdout).toContain("Installed compound-engineering")
242
+ expect(await exists(path.join(tempRoot, "opencode.json"))).toBe(true)
243
+ })
244
+
183
245
  test("convert writes OpenCode output", async () => {
184
246
  const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "cli-convert-"))
185
247
  const fixtureRoot = path.join(import.meta.dir, "fixtures", "sample-plugin")