EveryInc/compound-engineering-plugin · warning

Skipping agent " ": sanitized name " " collides with…

Error message

Skipping agent "${agent.name}": sanitized name "${safeName}" collides with another agent

What it means

writeOpenCodeBundle in src/targets/opencode.ts sanitizes each agent's name into a filename via sanitizePathName. If two distinct agent names sanitize to the same filename (e.g. 'Code Review' and 'code_review' both -> 'code-review'), the second is skipped with this warning and its agent file is not written, since one file cannot hold both.

Solutions

  1. Rename one of the colliding agents in its definition so the sanitized names differ (e.g. 'CodeReview' vs 'code-review-helper').
  2. Check the source plugin/manifest for duplicate agent definitions and remove the stale one.
  3. If collisions come from merging bundles, scope one set of agents into a different directory or prefix their names.
  4. Re-run the install and confirm the warning is gone and both agent files exist.

Example fix

// before: two agents colliding
{ "name": "Code Review" }
{ "name": "code_review" }   // both sanitize to code-review
// after
{ "name": "Code Review" }
{ "name": "Code Reviewer" } // distinct sanitized names
Defensive patterns

Strategy: validation

Validate before calling

function findSanitizedCollisions(agents: { name: string }[], sanitize: (s: string) => string): string[] {
  const seen = new Map<string, string[]>()
  for (const a of agents) {
    const k = sanitize(a.name)
    seen.set(k, [...(seen.get(k) ?? []), a.name])
  }
  return [...seen.entries()].filter(([, v]) => v.length > 1).map(([k, v]) => `${k}: ${v.join(', ')}`)
}
// run before writing the bundle; empty array means no collisions

Prevention

When it happens

Trigger: A bundle.agents array contains two agents whose names normalize to the same safeName — differing only by case, whitespace, underscores/dashes, or other characters stripped by sanitizePathName. The second occurrence in iteration order triggers the warning and `continue`.

Common situations: Plugin authors defining agents whose names differ only in case or punctuation; merging multiple plugins whose agents share a name; renaming an agent without removing the old definition so both 'OldName' and 'old-name' exist.

Related errors


AI-assisted analysis of EveryInc/compound-engineering-plugin@c9c10f8c75 (2026-08-31). Data as JSON: /api/errors/4e53a59584e35415. Report an issue: GitHub.

Appendix: source

Thrown at src/targets/opencode.ts:138

  if (!skillsEscaped) await cleanupRemovedManagedDirectories(openCodePaths.skillsDir, manifest, "skills", currentSkills)

  const hadExistingConfig = await pathExists(openCodePaths.configPath)
  const backupPath = await backupFile(openCodePaths.configPath)
  if (backupPath) {
    console.log(`Backed up existing config to ${backupPath}`)
  }
  const merged = await mergeOpenCodeConfig(openCodePaths.configPath, bundle.config)
  await writeJson(openCodePaths.configPath, merged)
  if (hadExistingConfig) {
    console.log("Merged plugin config into existing opencode.json (user settings preserved)")
  }

  const seenAgents = new Set<string>()
  const preservedAgentNames = new Set<string>()
  for (const agent of bundle.agents) {
    const safeName = sanitizePathName(agent.name)
    if (seenAgents.has(safeName)) {
      console.warn(`Skipping agent "${agent.name}": sanitized name "${safeName}" collides with another agent`)
      continue
    }
    seenAgents.add(safeName)
    const agentFileName = `${safeName}.md`
    if (agentsEscaped) {
      preservedAgentNames.add(agentFileName)
      continue
    }
    const targetPath = path.join(openCodePaths.agentsDir, agentFileName)
    const preserved = await cleanupCurrentManagedFile(targetPath, manifest, "agents", agentFileName)
    if (preserved) {
      preservedAgentNames.add(agentFileName)
      continue
    }
    await writeText(targetPath, agent.content + "\n")
  }

  const preservedCommandNames = new Set<string>()

View on GitHub (pinned to c9c10f8c75)