Install
$ agentstack add skill-shub-rajput-arc-to-zen-migration-arc-to-zen-migration ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Arc → Zen Migration
Overview
Zen has no Arc importer. But Zen (Firefox-based) keeps its whole tab/folder/workspace state in one compressed file you can script against, so Arc spaces can be rebuilt in bulk — folders become folders, not flat pins.
Read side (Arc): ~/Library/Application Support/Arc/StorableSidebar.json — plain JSON holding every space, folder, and pinned tab. No manual pasting needed. Write side (Zen): /zen-sessions.jsonlz4 — Mozilla-lz4 (mozLz40\0) compressed JSON.
Tools:
scripts/arc_extract.py— reads Arc's sidebar, returns each space as a nested tree.scripts/zen_session.py— read/write Zen's mozlz4 session + folder/tab/essential builders.scripts/migrate.py— end-to-end: Arc space → Zen workspace (preserves nested folders).scripts/migrate_example.py— manual fill-in template (when there's no Arc file, e.g. user pastes URLs).
When to Use
- "Migrate my Arc workspaces/spaces to Zen"
- "Bulk add folders / pinned tabs / essentials to Zen"
- Recreating Arc's folder structure inside a Zen workspace
Not for: bookmarks (use Arc export HTML → Zen bookmark import) or passwords (use a password manager / keychain export).
The Iron Rule
Zen MUST be fully quit (Cmd+Q) before writing. It rewrites the session on exit and will clobber your changes otherwise. Always backup() first — a wrong schema corrupts the session; the .bak restores instantly.
Workflow (automatic — Arc installed)
- Quit Zen (Cmd+Q) — non-negotiable (see Iron Rule).
- Preview:
python3 scripts/migrate.py --listprints Arc spaces ↔ Zen workspaces. - Migrate everything from zero:
python3 scripts/migrate.py --all --favorites
— creates a Zen workspace per Arc space (with Arc's emoji icon), imports folders + pins, and adds Arc's global Favorites as global essentials.
- Or one space:
python3 scripts/migrate.py "Home" --favorites(creates the workspace if missing;--zen "Name"maps to a differently-named one;--essentials Npromotes the first N top-level pins to essentials). - Reopen Zen, verify. Re-runnable —
clear_workspacewipes the target first. Restore the.bakif wrong.
Mapping Arc → Zen: space list → folder (nesting preserved) · space top-level tab → regular pin · global Favorites (topApps) → global essentials.
Workflow (manual — no Arc file, user pastes URLs)
Use migrate_example.py: fill ESSENTIALS/PINS/FOLDERS, quit Zen, run. Screenshots of the Arc sidebar are enough to fill it.
If Zen's schema drifted (script misbehaves): make ONE folder with 2 tabs by hand in Zen, quit, zen_session.py --dump, compare the live folders[]/groups[]/tabs[] shapes, update the builders.
Schema (Zen 1.20.x / Firefox 151)
A folder = two synced records sharing one id, plus member tabs:
| Where | Key fields | |---|---| | folders[] | id, name, workspaceId, collapsed, parentId:null, emptyTabIds:[], userIcon | | groups[] | id (same), name, color:"zen-workspace-color", collapsed | | tabs[] (member) | groupId:, zenWorkspace:, pinned:true, zenEssential:false |
- Essential:
zenEssential:true. Scoped by CONTAINER (userContextId), notzenWorkspace— this is the #1 gotcha. Withzen.workspaces.separate-essentials=true(Zen default), an essential shows only in workspaces whosecontainerTabIdmatches itsuserContextId;userContextId:0= the default container = the first/Home workspace. To make essentials global (Arc-style, same in every space), setzen.workspaces.separate-essentials=false(viauser.js) and useuserContextId:0/zenWorkspace:null. For a per-workspace essential, setuserContextIdto that workspace'scontainerTabId. - Plain pin:
zenEssential:false,zenWorkspace:, nogroupId. Scoped byzenWorkspace(works withuserContextId:0). - Workspace lives in
spaces[]:{uuid, name, icon(emoji), containerTabId, position, theme}.ensure_workspace()creates one if absent. - Ordering = tab array order.
tab.indexis the active history-entry pointer (set to 1), NOT position. - IDs are
-. Tabs need only a minimalentries:[{url,title,triggeringPrincipal_base64:"{\"3\":{}}"}]; Firefox fills the rest on load. - Skip
arc://*URLs — they don't resolve in Zen.
Library Quickref
from zen_session import ZenSession, find_profiles
s = ZenSession(find_profiles()[0])
s.backup()
home = s.workspace_by_name("Home")
s.clear_folders(workspace=home) # idempotent re-runs
s.add_essential("https://discord.com/") # global top icon
s.add_tab("https://news.com/", workspace=home) # plain pin
fid = s.add_folder("Finance", home)
s.add_tab("https://sheet/", workspace=home, folder_id=fid)
s.save() # Zen must be quit
Common Mistakes
- Writing while Zen runs → changes vanish on quit. Quit first.
- Folder in only one array → ghost/empty folder. Always write BOTH
folders[]andgroups[](the library does). - Tab missing
zenWorkspace→ tab lands in the wrong/no space. Set it (essentials usenull). - liblz4 not found →
brew install lz4(mac) /apt install liblz4-1(linux); fix the path list inzen_session.pyif needed. - No backup → re-run with
backup(); keep the.bakuntil verified.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: shub-rajput
- Source: shub-rajput/arc-to-zen-migration
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.