-
Notifications
You must be signed in to change notification settings - Fork 7
Description
Problem
When two packs define a copyPackFile component with the same destination filename and the same CopyFileType, they collide silently:
- File level: The second pack's file overwrites the first at the shared path (e.g.,
~/.claude/hooks/lint.sh). Last writer wins with no warning. - Settings level: For hooks,
addHookEntrydeduplicates by command string (bash ~/.claude/hooks/lint.sh), so only one settings entry exists. The second pack'saddHookEntrycall returnsfalse. - Artifact ownership: Both packs record the same path/command in their
PackArtifactRecord. Removing Pack A strips the hook from settings even though Pack B still needs it. The next sync re-adds it, but there's an inconsistency window.
This affects all CopyFileType cases: hooks, skills, commands, agents, and generic files — any two packs using the same destination filename under the same subdirectory will collide.
Proposed Solution
Use the composed component ID (which already includes the pack prefix, e.g., my-pack.lint-hook) to derive the destination filename, preventing collisions by construction.
For example, a component my-pack.lint-hook with destination: lint.sh would be installed as:
- Current:
~/.claude/hooks/lint.sh - Proposed:
~/.claude/hooks/my-pack.lint-hook.sh(or similar namespaced scheme)
The hook command in settings would change accordingly:
- Current:
bash ~/.claude/hooks/lint.sh - Proposed:
bash ~/.claude/hooks/my-pack.lint-hook.sh
Components to audit
All CopyFileType destinations share a flat namespace per subdirectory:
.hook→.claude/hooks/.skill→.claude/skills/.command→.claude/commands/.agent→.claude/agents/.generic→.claude/
Each needs the same namespacing treatment.
Migration Plan
Existing installations have files at the old (non-namespaced) paths. A migration is required:
- Detection: On first sync after the change, compare old artifact records (non-namespaced paths) against the new naming scheme.
- Rename: Move existing files from old paths to new namespaced paths.
- Settings update: For hooks, remove the old command string from settings and add the new one.
- Artifact record update: Update
PackArtifactRecordentries (files, fileHashes, hookCommands) to reflect new paths. - State version bump: Increment the
ProjectStateversion to trigger migration on next sync. - Backward compatibility: Consider a transitional period where both old and new paths are recognized during doctor checks, to avoid false positives for users who haven't re-synced yet.
References
Sources/mcs/TechPack/Component.swift—CopyFileTypeenumSources/mcs/Install/ConfiguratorSupport.swift— hook command construction (hookCommandPrefix + destination)Sources/mcs/Install/GlobalSyncStrategy.swift— hook stripping by prefix matchSources/mcs/Install/Configurator.swift— artifact tracking for hook commands