EveryInc/compound-engineering-plugin · warning
Skipping ${targetPath}: existing user-managed symlink (not o
Error message
Skipping ${targetPath}: existing user-managed symlink (not overwritten) What it means
cleanupCurrentManagedFile in src/targets/opencode.ts is the preserve-check for OpenCode managed files during cleanup. It returns true (skip) when the target path is a symlink — treated as user-managed and never overwritten. This is the OpenCode analogue of the shared isPreservedSymlink guard and protects user links from being deleted or replaced by install/cleanup runs.
Source
Thrown at src/targets/opencode.ts:37
writeManagedInstallManifest,
} from "./managed-artifacts"
// Returns true when the existing path was preserved (skip cleanup AND the
// subsequent write -- writing through a preserved symlink would clobber the
// user's fork, which is worse than not overwriting at all). All four
// OpenCode artifact kinds (agents, commands, plugins, skills) are tracked in
// the install manifest's `groups` map, so the same ownership rule applies
// uniformly here rather than a symlink-only guard.
async function cleanupCurrentManagedFile(
targetPath: string,
manifest: ManagedInstallManifest | null,
group: string,
entryName: string,
): Promise<boolean> {
const stat = await lstatOrNull(targetPath)
if (!stat) return false
if (stat.isSymbolicLink()) {
console.warn(`Skipping ${targetPath}: existing user-managed symlink (not overwritten)`)
return true
}
if (!manifest?.groups[group]?.includes(entryName)) {
console.warn(`Skipping ${targetPath}: existing unmanaged file (not overwritten)`)
return true
}
return false
}
async function mergeOpenCodeConfig(
configPath: string,
incoming: OpenCodeConfig,
): Promise<OpenCodeConfig> {
if (!(await pathExists(configPath))) return incoming
let existing: OpenCodeConfig
try {
existing = await readJson<OpenCodeConfig>(configPath)View on GitHub (pinned to c9c10f8c75)
Solutions
- If the symlink is intentional, nothing to do — the file is preserved as-is.
- Remove the symlink (rm the link only) and re-run the install to get the generated file in place.
- If the link target holds customizations you want, merge them into your own copy or into the plugin source, then delete the link and reinstall.
- Verify with `ls -la .opencode/agents/` which entries are links before re-running.
Example fix
// before ls -la .opencode/agents/review.md # -> ~/shared/agents/review.md // after rm .opencode/agents/review.md bun run convert --to opencode
Defensive patterns
Strategy: validation
Validate before calling
import { lstat } from 'node:fs/promises'
async function pathIsSymlink(p: string): Promise<boolean> {
try { return (await lstat(p)).isSymbolicLink() } catch { return false }
}
// before install: if (await pathIsSymlink('.opencode/agents/<name>.md')) resolve manually Type guard
function isSymlinkStat(stat: { isSymbolicLink(): boolean } | undefined): boolean {
return stat?.isSymbolicLink() === true
} Prevention
- Do not place personal symlinks inside .opencode managed groups
- Inventory `.opencode` for symlinks before each install
- Keep manual customizations outside the managed file names (use distinct filenames)
When it happens
Trigger: Running an OpenCode bundle write/cleanup when the managed file path (e.g. .opencode/agents/<name>.md) exists and lstat reports it is a symbolic link; cleanupCurrentManagedFile is reached via the `preserved` path of the cleanup routine.
Common situations: Dotfiles-managed .opencode directories (stow/chezmoi symlinking each file), users linking an agent file to a shared or customized copy, or prior tooling that replaced managed files with links.
Related errors
- Skipping ${targetPath}: existing user-managed symlink (not o
- Skipping ${storeDir}: resolves outside the managed root via
- Skipping ${targetPath}: existing unmanaged file (not overwri
- Skipping ${targetDir}: existing user-managed symlink (not ov
- ${context.collectionPath} exists and is not a symlink; refus
AI-assisted analysis of EveryInc/compound-engineering-plugin@c9c10f8c75 (2026-08-31).
Data as JSON: /api/errors/05f5acdfae8610e3.
Report an issue: GitHub.