Day 2:同一台電腦、換一個帳號,為什麼等於半台新機器?
Vibe Coding 日誌 Day 2——真正會出事的不是「忘了裝什麼」,是三個不會報錯的靜默失敗
Vibe Coding 日誌 Day 2。Day 1 講「環境決定 AI 能幫你做到哪一層」,今天講這個環境有多脆—— 同一台實體電腦,只是換了一個 macOS 使用者帳號,開發環境就崩掉一半。
Vibe Coding 日誌 Day 2。Day 1 講「環境決定 AI 能幫你做到哪一層」,今天講這個環境有多脆—— 同一台實體電腦,只是換了一個 macOS 使用者帳號,開發環境就崩掉一半。
而且崩的方式很不公平:缺工具會報錯,很好修;真正貴的是三個不會報錯的靜默失敗。 一、先建立判準,不要照抄「新機安裝手冊」
換帳號時最浪費時間的做法,是把新電腦的安裝步驟從頭跑一遍。因為同一台機器上,很多東西是共用的。
判準只有一句話: 東西在 /opt/homebrew、/Applications、/Library → 共用,不用重裝。 在 / 或 login keychain → 一定要重做。
實際掃出來的對照:
共用(一個都不用重裝) 綁帳號(一定要重做) --- --- Homebrew 全部套件(git、gh、direnv、ffmpeg、pandoc、redis…) Node 本身 /Applications,含 Xcode.app 本體 全域 npm 套件(pnpm、firebase-tools、vercel…) gcloud SDK、Flutter 的執行檔 四套雲端登入、keychain 憑證 — direnv 的授權狀態、launchd 排程、/.claude
有一個特別反直覺、也是實際卡最久的:
fnm 這個版本管理器是共用的,但它「裝好的 Node 版本」在 /.local/share/fnm(per-user)。
所以新帳號 fnm list 只會看到 system,而 macOS 沒有內建 Node ⇒ node、npm、npx、pnpm 全部不存在。 你會覺得「怎麼什麼指令都沒有」,但 command -v fnm 又明明找得到。這一步沒解決,後面每一步都做不了。
二、三個靜默失敗(真正的重點) 失敗 1:AI「突然什麼都不記得了」
Claude Code 的專案記憶存在 /.claude/projects/<slug>/,而那個 <slug> 是專案絕對路徑把 / 換成 - 編出來的:
換帳號=使用者名變了=slug 全部對不上。檔案原封不動搬過去,382 個 memory 檔好端端躺在那裡, 但 Claude 會把這個專案當成全新的:歷史對話找不到、累積的專案規則歸零。
而且完全沒有錯誤訊息。 它不會說「找不到記憶」,它只是表現得像第一次見到你。
修法很簡單,難的是想到要修:
同一類問題還有:git worktree 的註冊檔記的也是絕對路徑。 這次 git worktree list 就跑出一筆指向 /Users/<舊帳號>/Projects/... 的殘留, 標成 prunable——git worktree prune 清掉即可,但不清就會一直看到不存在的路徑。 失敗 2:build 成功,但產出的是白屏死站
.env 因為 gitignore 不會跟著 git 走。少了它: 後端部署會用空設定覆蓋線上(這個很兇,但至少會壞給你看) 前端 vite build / next build 會成功,只是 VITE / NEXTPUBLIC 全變成 undefined
第二種才可怕:CI 全綠、build log 沒有一行警告、產物大小看起來也正常, 部署上去是一個沒有任何後端設定的空殼——白屏、無法登入。
所以驗收不能只看 build 成功,要看產物裡到底有沒有設定:
這次實測是 4,才算真的接上。 失敗 3:備份好幾週沒跑,沒有人會發現
/Library/LaunchAgents 是 per-user 的,而且只有該帳號登入時才會跑。 換帳號之後,舊帳號的定時任務就靜靜地不再執行——不會有通知、不會有紅字。
這次掃出來的結果是:新帳號的 LaunchAgents 是空的,代表這台機器目前沒有任何個人排程在跑(含異地備份)。
結論不是「趕快在新帳號重建 launchd」,而是:真正重要的排程本來就不該綁在某個人的登入狀態上。 搬去 GitHub Actions 這種不綁登入的地方,才不會因為換帳號、換電腦、或那天沒開機就整個消失。 三、nodemodules 不要複製,一定重裝
複製過來的 nodemodules 是舊家目錄的 pnpm store 建出來的。這次直接被擋下:
pnpm 偵測到這包不是自己這個環境建的,想整個清掉重建,但在非互動 shell(AI agent、CI)裡它不敢自己決定,於是中止。 加一個環境變數就等於回答「是,清掉重裝」——而這正是我們要的:
看到這個錯誤時,不要去找「怎麼讓 pnpm 別清」。你就是該清。 四、順手踩到的兩個腳本坑(跟換帳號無關,但很值得記)
把整套流程寫成可重跑的腳本時,實際炸了兩次:
① bash 在非 UTF-8 locale 下,會把緊貼變數的全形字當成變數名的一部分
錯誤訊息長這樣:slug�: unbound variable——那個亂碼就是全形括號的第一個位元組。 寫中文訊息的 shell script,變數一律加大括號。
② tar czf - openssl enc 時,openssl 讀不到密碼
原因是 openssl 的 stdin 已經被 tar 的 pipe 佔住了,它沒辦法從那裡讀密碼。 解法是由腳本自己讀,再用檔案描述子餵進去:
順帶一個還原時的坑:tar 存的是舊帳號的絕對路徑。直接 tar xzf - -C / 會把檔案還原到 /Users/<舊帳號>/...,新家目錄一個都沒拿到——而且指令會「成功」。要先解到暫存區再逐一搬。 五、最貴的一課:最容易外洩的是 /.zshrc
盤點環境時,我把 /.zshrc 整份印出來看。那份檔案裡有三個 export 出去的 API token。
它們就這樣進到了對話紀錄裡。沒有人攻擊、沒有漏洞,只是一個再普通不過的「看一下設定檔」的動作。
這件事的教訓有三層: secret 明文放 .zshrc 是個定時炸彈——因為 .zshrc 是你「會很自然拿給人看」的檔案。 移到 /.config/secrets.env(chmod 600)再 source 進來,看 shell 設定時就不會連帶攤開值。 檢查環境時只看變數名、不看值。grep -oE '^export [A-Z]+' /.zshrc 就夠判斷「有沒有設」了。 看過就算外洩,要輪替。不要說服自己「應該沒關係」。
這也是為什麼把開發對話變成公開筆記的工具,機密掃描是刻意不給 --force 的—— 開發對話天然含 .env 值、API key、客戶名稱,方便的逃生門遲早會被用掉。 六、最後把它變成可重跑的東西
一次性的排錯只值一次。所以這次的結果不是一份筆記,是兩支腳本: audit.sh — 檢查後才裝:印出共用資源清單、per-user 缺什麼、四套登入誰沒登、 memory slug 對不對、launchd 是不是空的、哪些 .env 是 1 byte 空殼 pack.sh — 打包三包:機密(AES-256 加密)、AI session/memory、以及一份不含任何值的清單
順序永遠是先盤點 → 判斷風險 → 才動手。 換帳號那天你最想做的事是「趕快把東西裝回來」,但先花三分鐘掃一遍,會發現有一半根本不用裝。 這次的完成度
項目 狀態 --- --- Node 22 + 全域 CLI + 依賴重裝 ✅ git credential / direnv / psql PATH ✅ build 驗收(產物含設定 4 檔) ✅ 四套雲端登入 ⏳ 要本人開瀏覽器,代跑不了 備份排程 ⚠️ 沒跟過來,建議改掛 CI 而不是重建 launchd 那三個 token 🔴 待輪替
「指令都跑完了」不等於「環境好了」。 沒有實際 build 過、沒看過產物內容,就照實說沒驗證。 本篇由 claude-code(claude-opus-5) 於開發過程中產生。 repo:RedBallFlow 標籤:macos migration dotfiles dev-environment claude-code shell