Repository navigation
clean undoes a user-scope plugin a project install collided with (cursor, codex, copilot) #829
Description
What happens
Cursor plugins install at user scope only, into ~/.cursor/plugins/local/<plugin>: the directory is named by the plugin alone, with no marketplace or project in the path. When a plugin of that name is already there, whether the person installed it themselves or another project did, aidd plugin install <plugin> --tool cursor merges into it and overwrites its .cursor-plugin/plugin.json. aidd clean in the installing project then removes the files it laid down, the overwritten manifest included.
Net effect: a project-level install plus a project-level clean deletes the manifest of a user-global plugin that existed before either ran. One project's clean also removes files another project's install still relies on.
Reproduce (throwaway HOME, nothing real touched)
T=$(mktemp -d); U=$T/home/.cursor/plugins/local/aidd-telemetry
mkdir -p $U/.cursor-plugin && echo '{"name":"aidd-telemetry","version":"0.1.0","mine":true}' > $U/.cursor-plugin/plugin.json
mkdir $T/proj && cd $T/proj && git init -q
export HOME=$T/home AIDD_USER_CONFIG_DIR=$T/config
aidd setup --ai cursor --no-default-marketplace --plugins none --yes
aidd marketplace add probe <a catalog carrying aidd-telemetry> --scope project --yes
aidd plugin install aidd-telemetry --tool cursor --from probe --yes # "mine" is gone from plugin.json
aidd clean --force # plugin.json is gone| Build | After install | After clean |
|---|---|---|
| published 5.2.2 | the person's plugin.json overwritten |
the installed copy left behind |
next at 42f572c |
the person's plugin.json overwritten |
plugin.json deleted |
Expected
An install never overwrites a file it did not lay down: it refuses, or stops and names the collision. A clean removes only what this install wrote, and never a file that existed before it.
How it was found
A live journal check of aidd-telemetry on Cursor, run in a real HOME that already had aidd-telemetry installed globally. Afterwards the person's global plugin had no .cursor-plugin/plugin.json, and its directory had been modified during the check's cursor clean. Its skills/ survived.
Same class on Codex and Copilot
These hosts key a plugin by <plugin>@<catalog name>. A project that adds a catalog whose name the person already installed at user scope (here aidd-framework) shares that key. aidd clean in the project then undoes the person's own entry. Reproduced in a throwaway HOME seeded with the person's entry enabled, on the stack of open PRs:
| Host | Before | After project install | After project clean |
|---|---|---|---|
Copilot settings.json |
"aidd-telemetry@aidd-framework": true |
true (marketplace add refused: already registered) |
false |
Codex config.toml |
[plugins."aidd-telemetry@aidd-framework"] enabled = true |
unchanged | section deleted |
Codex plugins/cache/aidd-framework/aidd-telemetry/ |
the person's 0.1.0/ |
replaced by 0.2.0/, the person's version gone |
directory deleted |
On the machine where this was found, backups from Sep 7 and Sep 8 show both entries enabled. After a live check that installed and cleaned under the aidd-framework catalog name, Copilot's key read false and Codex's section was gone. The flip itself was not observed; the mechanism above reproduces it.
Expected, all three hosts
clean undoes only what this project's install added. An entry that existed before the install is left as it was found.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Projects
- StatusShow more project fieldsDone
Activity
[-]cursor: a plugin install overwrites a same-named user plugin, and clean deletes its manifest[/-][+]clean undoes a user-scope plugin a project install collided with (cursor, codex, copilot)[/+]fix(cli): keep a skipped cursor install's project hooks and manifest …
fix(cli): leave what a project did not install to whoever installed it (
test(cli): two projects on one HOME installing the same cursor plugin (…
test(cli): a real-binary scenario where the person already has the pl…
blafourcade commentedon Sep 14, 2026
The remaining collision is still in #829’s original scope.
#834 protects a person’s pre-existing global installation, but the multi-project case remains: project A owns a user-scope directory, project B uses it, then cleaning A removes it and breaks B.
Reopen this as a High Bug. A project-level
cleanmust never delete a user-scope directory. Only an explicitclean --scope usermay remove machine-wide AIDD state.Add the two-project scenario as the regression: install from A, use from B, clean A, then prove B still works. The explicit user-scope cleanup remains responsible for deleting the shared state.
6 remaining items
test(cli): align portable fixtures and native host proofs
test(cli): preserve seeded identities in realpath fixtures
test(cli): a path assertion is written the way the platform builds it
test(cli): the in-memory key is spelled the way the source resolves it
test(cli): the in-memory key is spelled the way the source resolves it
test(cli): a registered catalogue is spelled the way a host stored it
fix(cli): a registered catalogue is compared by the directory, not it…
fix(cli): a registered catalogue is compared by the directory, not it…
test(cli): the manifest is searched as a tree, not as its serialization
test(cli): an e2e workspace hands out the path the CLI will print
test(cli): the manifest is searched as a tree, not as its serialization
fix(cli): protect pre-existing user plugins during project clean (#870)
blafourcade commentedon Sep 20, 2026
Corrigé par #870, mergé dans
next(61138e0).Vérifié sur
origin/next— 177 tests, 7 fichiers, 0 échec sur la surfaceclean:clean-user-scope-use-case.integration.test.ts:432— « refuses an active B Cursor user plugin but never treats project files as machine-owned », le cas Cursor décrit iciclean-shared-ref-guard.integration.test.ts— huit scénarios Codex de ref partagée entre projetsclean-shared-ref-codex.e2e.test.ts— « keeps the plugin's ref enabled, names the other project, and never calls plugin remove »clean-scope-user.e2e.test.ts— « purges its own whitelist only, leaving auth.json, telemetry/ and an unrelated marketplace entry untouched »Fermée à la main : la PR disait « Refs » et non « Closes », et elle a atterri sur
next.Limite connue, hors périmètre de cette issue : Copilot CLI 1.0.83 ne sait pas prouver une source de catalogue native, donc une activation native fraîche est refusée. L'état utilisateur existant est préservé.