feat(multi-account): cross-account sharing — engine + CLI + UI (Batch 3)
Summary
Batch 3: cross-account sharing — share the anchor account's global files (CLAUDE.md / skills/ / commands/) with other accounts, per item: Link (symlink, stays in sync) / Copy (independent fork) / skip. Engine + CLI + UI in one PR (per discussion, so it's testable end-to-end from the UI).
Depends on #129 (the isAnchor refactor — merged). Sharing mechanism was verified live earlier (Claude Code follows symlinks for skills entries, whole dirs, and CLAUDE.md; this machine has been running on hand-made links since).
Semantics (the part worth reviewing carefully)
- Never silently overwrite: sharing over an account's own real file/dir first renames it to a timestamped
.codev-bak-*sibling (neverln -sf). An existing symlink is just replaced (a pointer needs no backup). - Unshare is reversible by construction:
- plain Unlink — zero data loss (removes a pointer; the anchor's copy is untouched; re-link anytime)
- Unlink & restore (
--restore-backup) — true undo: brings back the content the account had before sharing --keep-copy— materializes the shared content as an independent fork
- Status engine distinguishes
none/own/linked → target/broken-link/mixed(a real dir containing per-entry links) — and correctly recognizes pre-existing hand-made links it didn't create. sync-settingscopiesstatusLine/model/effortLevel/themefrom the anchor (settings.jsonkeys). Hooks (installed per-dir by CodeV),enabledPlugins, andpermissionsare deliberately excluded.plugins/is NOT shareable — experiment result: it's a per-account install registry (installed_plugins.jsonwith absoluteinstallPaths) + caches, i.e. mutable state, not payload. Same exclusion class as session data.
What's included
src/cli/share-manager.ts— path-based engine (tmpdir-testable) + registry-aware wrappers- CLI:
codev account share <name>(status),share <name> <item> --link|--copy [--entry E],unshare <name> <item> [--restore-backup|--keep-copy],sync-settings <name> <key...>; tab completion for all of it - UI: Sharing button per non-anchor account row → inline panel (per-item status + Link / Copy / Unlink / Unlink & restore;
mixeddirs point at the CLI's--entry) - IPC trio (
accounts-share-status/share/unshare) as thin wrappers - 12 real-fs integration tests under
os.tmpdir()(32 total)
Also added during review/testing
- Settings-key sync in the UI (user request): the Sharing panel gains a
settingsrow with one-click buttons —statusLine(the headline case) /model/effortLevel/theme— each copies that key from the anchor with copied/missing feedback. - Panel freshness (user-reported: stale state persisted until an app restart): share status reloads when the Accounts tab reopens and when the window regains focus (the natural flow after editing files in a terminal), gated to fire only while the panel is visible.
Sharing ▸/▾disclosure arrow (user feedback: the button read as a one-shot action, not an expander).- Hardening from the review rounds: unshare validates the restore-backup/keep-copy source before unlinking (a failed undo can't half-remove the share); keep-copy stages to a unique temp sibling first (no TOCTOU window, no stale-temp contamination); backup names uniquified beyond 1s timestamp precision; CLI rejects ambiguous
--link --copyand the IPC validates the mode; entry names starting with-fail early; malformedsettings.jsonerrors include the path; popup ignores stale share-status responses when rapidly switching accounts. - Deferred with rationale (on the threads): renderer tilde helper stays regex-based (macOS-only app; no
osin the renderer); typing preload againstIElectronAPIneeds the d.ts to become a module first — its own tiny refactor.
How to test (UI, needs yarn make)
- Settings → Accounts → Sharing on the
workrow → panel should show your existing hand-made links: all three itemslinked → ~/.claude/...(the engine recognizing state it didn't create). - Unlink
commands→ statusnone; check~/.claude/commandsstill exists (source untouched). Link it back →linkedagain. - Fork test: Unlink an item then Copy it → status
own (not shared); editing the copy won't touch the anchor's version. (Restore to Link afterwards.) - Backup/undo test:
echo test > ~/.claude-work/CLAUDE.mdafter unlinking claude-md → panel showsown→ Link (notice mentions backup) → Unlink & restore → yourtestcontent is back. - CLI spot-check:
yarn account share work(status table),yarn account sync-settings work model(copies your anchor's model setting),claude w<TAB>still completes. - Settings row: in the Sharing panel click statusLine → notice
Copied statusLine from the anchor…. - Freshness: with the panel open, change a target from a terminal (e.g.
rm/echoon~/.claude-work/CLAUDE.md) → cmd-tab back to CodeV → the row updates without restarting.
tsc --noEmit 0 errors; yarn test 35/35 (14 real-fs engine tests).
Notes
- Per-entry sharing (
--entry <skill-name>) is CLI-only in this PR; the UI showsmixedstate and defers to the CLI. UI per-entry management can come later if wanted. - Backlog after this: #127 (detect existing config dirs + one-click register), #128 (copy-fork resume), #121 (VS Code).
🤖 Generated with Claude Code
Summary by cubic
Adds cross-account sharing and one-click settings sync so non-anchor accounts can reuse the anchor’s global CLAUDE.md, skills/, and commands/. Includes the engine, CLI, UI, and docs updates for end-to-end testing; version 1.0.82.
-
New Features
- Path-based share engine with clear status: none / own / linked → target / broken-link / mixed (recognizes hand-made links).
- Safe by default: never overwrite; own content backed up to
.codev-bak-<ts>; Unlink is lossless; Unlink & restore truly undoes;--keep-copymaterializes a fork via a unique temp. - CLI:
codev account share <name>(status),share <name> <item> --link|--copy [--entry E],unshare <name> <item> [--entry E] [--restore-backup|--keep-copy],sync-settings <name> <key...>; completion for labels/items/keys. - UI: per-account Sharing panel with per-item status and actions (Link/Copy/Unlink/Unlink & restore), plus per-key sync buttons for
statusLine,model,effortLevel,theme; panel refreshes on Accounts tab reopen and when the window regains focus;mixeddirs point to the CLI. plugins/not shareable (per-account install state). IPC endpoints for share status/share/unshare/sync-settings; UI renders syncable settings keys from the engine’s IPC allowlist. Real-fs integration tests cover the engine. README/design docs updated.
-
Bug Fixes
- Panel freshness: reload on reopen/focus; stale share-status responses ignored during rapid account switches.
- Unshare validates restore-backup/keep-copy before unlinking; backup names uniquified;
--keep-copycopies to a unique temp before unlinking (no TOCTOU). - CLI/IPC validation: reject ambiguous
--link --copy; validate mode; early-reject--entrynames starting with-; settings JSON errors show the path;accounts-sync-settingsvalidateskeysis an array.
Summary by CodeRabbit
- New Features
- Added cross-account sharing for Claude settings, skills, and commands (link for sync or copy for independence).
- Added per-item Sharing controls in the Accounts view, including link/copy, unlink, and optional “unlink & restore” using recoverable backups.
- Extended the CLI with
codev accountsubcommands:share,unshare, andsync-settings(syncs an allowlisted set of settings keys).
- Bug Fixes
- Improved protection against overwriting by creating recoverable backups and supporting restore/keep-copy outcomes.
- Documentation
- Updated the changelog and multi-account support documentation with the new UI/CLI behavior.
- Chores
- Released version 1.0.82.