設定專案,逐一切片進行
每次調用只處理一個切片,透過一個固定的循環:
參考 → 研究 → 規劃 → 執行 → 本地驗證 → 修正 → 生產驗證 → 修正 → 將手動步驟交給使用者 → 提交
slices/ 中的檔案是參考資料,而非腳本。它們記錄了一個已經過驗證的技術棧,更重要的是,也記錄了那些真正耗費時間的陷阱:pnpm init 寫入的 devEngines 區塊會被 corepack 拒絕,create-next-app 在 apps/web 內部開啟的第二個 pnpm workspace。這些推理是有價值的。但是版本號、CLI 標誌和 API 形狀則會隨著時間改變。直接從參考檔案執行一個切片,很可能會因為某個移動的東西而失敗。
因此,你所執行的計劃永遠不是參考檔案本身。它是一個為當前專案、從參考檔案加上研究得出的當前事實,所撰寫的全新計劃。而且因為這個計劃在經過驗證之前就已寫好,所以循環不會在綠色驗證標誌處結束——它結束於計劃已被修正,以符合實際運作情況,且使用者已收到那份只有人類才能進行的檢查清單。
技術棧是固定的:pnpm workspace · 透過 OpenNext 在 Cloudflare Workers 上執行的 Next.js · GraphQL Yoga Worker · Drizzle + Postgres,本地使用 Docker,生產環境透過 Hyperdrive 使用 Neon · Tailwind + shadcn · 可選的 Resend、Better Auth 和 Stripe。如果使用者想要不同的技術棧,請明確告知——這個技能沒有其他選項。
0. 分派
/setup-project [切片名稱] 會建構一個切片。參數是選填的,可以指定切片的編號或名稱——例如 01、web shell、database、auth。請根據 reference/slices.md 解析這個參數;如果沒有符合的項目,請告知使用者並列出可用的名稱,而不是自行猜測。
ls docs/setup/ 2>/dev/null # 查看這個專案已經建構了哪些切片
git status --short # 髒的樹狀結構表示上一個切片可能尚未完成
docs/setup/ 是已經建構的記錄,而非未來預測。 一個切片檔案存在於這裡,是因為該切片已經被建構並通過驗證。因此,編號最高的檔案就是「我們目前進度」的答案,而且不需要另外維護一個標記。
如果沒有指定切片名稱——永遠先顯示目錄,然後再推薦。 列出 reference/slices.md 中的每個切片,包括其編號、名稱,以及 docs/setup/ 是否顯示它已建構。然後推薦一個,並在執行前取得確認。這也適用於第一次執行:先確定專案名稱(§1),然後顯示目錄(所有未建構的切片,下一個是切片 00),並在確認後再建構。絕不能因為下一步看起來很明顯,就跳過目錄或確認步驟。
| 狀態 | 該做什麼 |
|---|---|
沒有 docs/setup/ 目錄 | 首次執行:確定專案名稱(§1),顯示目錄,確認切片 00 |
| 指定了切片名稱 | 建構它 |
| 未指定切片名稱 | 顯示目錄,推薦已建構的最高編號 + 1,並確認 |
切片 99 不在上述的算術規則內。 它是安全審計:不建構任何東西,只需要切片 00,並且可以對任何現有內容重新執行,而不是佔據鏈條中的一個位置。因此,docs/setup/ 中存在 99-security-audit.md 並不表示鏈條已完成,而且「已建構的最高編號 + 1」必須忽略它——一個包含 00–05 和 99 的儲存庫,其下一個要建構的是 06。它也從 --through 範圍中排除,因為範圍指的是建構鏈條,而對不存在的切片進行審計等於什麼也沒審計。建議在任何新增公開介面的切片之後執行它,並明確指定名稱:/setup-project 99。
永遠不要在一次調用中建構兩個切片。當切片被提交後就停止。
跳過切片的處理。 如果指定的參數比已建構的切片超過一個以上,請列出它跳過了哪些切片以及這些切片建構了什麼,然後讓使用者決定。鏈條是真實存在的:切片 04 需要切片 02 和 03,而切片 05–07 會修補早期切片寫入的檔案,因此缺少先決條件是嚴格的 docs:check 錯誤,而非警告。reference/slices.md §依賴關係中有詳細說明。
1. 僅首次執行:專案名稱
限小寫字母、數字和連字符——它會成為 npm scope(@名稱/db)和 Worker 前綴(名稱-web、名稱-graphql)。如果儲存庫目錄名稱已經符合規則,則預設使用它。僅詢問一次;之後的每個切片都會從 package.json 讀取它。
2. 閱讀參考切片
在執行任何其他操作之前,完整閱讀 slices/NN-*.md,並閱讀 reference/slices.md 以了解這個切片的驗證標誌證明了什麼,以及它需要哪個帳戶。程式碼區塊之間的敘述是保留價值的部分——請閱讀它以了解推理過程,並將其中的每個版本號、標誌和檔案路徑視為需要驗證的主張,而非事實。
如果切片需要使用者沒有的帳戶(Cloudflare、Neon、Resend、Stripe),請立即告知。缺少帳戶並不會阻礙撰寫計劃,但會導致切片在生產驗證環節中途卡住,而本機部分已經提交。
回顧過去,而非預測未來。 閱讀專案已有的內容——docs/setup/、CLAUDE.md、以及早期切片寫入的技能檔案——並遵循它們設定的慣例,以便這個切片能夠融入其中,而不是重新發明。反過來的情況永遠不會發生:沒有切片會假設後續的切片(無論是否已命名)會被建構——是否建構由使用者在分派環節(§0)決定,而不是參考文件可以預期的事情。如果一個已建立的慣例現在看來不適合這個切片的需求,請說明情況並讓使用者決定,而不是默默地覆蓋它或默默地遵守它。
移除這個切片所取代的暫用工件。 有些東西只是為了在真正的東西存在之前證明一個機制而建立的——packages/mock 是第一個也是最明顯的例子:切片 0 建立它是為了在沒有真正的套件可以經過它時,證明這個測試框架正常運作。暫用工件永遠不會命名它的繼任者,因為哪個切片取代它是由分派環節(§0)決定的,而不是由參考文件決定。義務是反過來的:每個切片都會查看已有的內容,並將移除現在已成為多餘部分的動作,納入其自身的計劃,連同任何描述它的 CLAUDE.md 行。
因此,如果 packages/mock 仍然存在,這個切片就是 workspace 得到的第一個真正的東西,而將其退役是這個切片工作的一部分。同樣的規則也適用於後續切片留下的任何東西——被真實 resolver 取代的靜態 resolver、變成 binding 的佔位值。
這個機制只適用於有繼任者的工件,因此請宣告那些沒有繼任者的工件。 packages/mock 被退役是因為一個真正的套件很明顯地取代了它。沒有東西明顯地取代 sendTestEmail:切片 6 新增了真正的驗證郵件與之並存,切片 7 將其描述為 schema 的一部分,而一個未經認證的發送真實郵件的 mutation 將作為永久功能發布。沒有切片是錯的——只是這個移除動作沒有擁有者。等待後來的切片來發現它不是一個機制;它只是一個希望。
因此,每個切片都以一個 遺留物 區塊結束,列出兩個清單,這些清單是在工件建立時就寫好的,而不是等到有人發現它時:
- 暫用 — 它是什麼、為什麼存在、以及它被退役的條件。如果沒有後續切片會合理地滿足這個條件,請在該行中明確說明。誠實地寫下「沒有東西會取代這個」才是有效的條目。
- 已知接受 — 一個被有意承擔的風險、誰可以接觸到它、以及其理由。記錄下來,以便後續切片可以重新決定它,而不是重新發現它;並且可以區分繼承的預設值(例如 Yoga 在生產環境中提供 GraphiQL)和主動選擇的值。
切片 99 會將這些作為交叉檢查來閱讀——但絕非其輸入,因為一份清單只能包含某人已經想到要記錄的內容。它會先閱讀程式碼。
3. 研究當前的事實
這一步驟讓參考文件可以安全地使用,因此請有意識且低成本地執行。對於這個切片要安裝或設定的每個工具,請檢查實際情況而非參考文件:
npm view <套件> version # pin 的版本應該要實際是多少
npx <cli> --help # 標誌,以及哪些提示是非互動式的
然後為任何切片結構上依賴的東西閱讀官方文件——框架當前的適配器指南、執行階段的相容性標誌、函式庫的當前 API 形狀。優先使用供應商自己的文件,而非參考文件對它們的摘要,並記錄它們不一致的地方。
閱讀安裝過程的輸出,而不僅是其退出碼。 一個被合併到另一個套件、已重新命名或已棄用的套件,仍然可以安裝、仍然匯出相同的名稱、仍然可以編譯——npm view <套件> version 會報告一個健康的版本,而參考文件中的程式碼區塊也會顯示綠色。pnpm add 輸出中的單行 [WARN] deprecated … 是唯一會顯示的地方。切片 5 就是這種情況:@react-email/components 在五個月前已被合併到 react-email,但其他任何地方都沒有提到。
npm view <套件> deprecated # 非空值表示參考文件描述了一個已棄用的套件
記錄你發現了什麼以及在哪裡發現的。參考文件與當前文件之間的矛盾是這一步驟的全部輸出,它們會進入你即將撰寫的計劃中。
3b. 接入已建構的內容
一個沒有連接好的切片,是沒有人能判斷其是否正常運作的切片。在撰寫計劃之前,請查看 docs/setup/ 中存在哪些內容,並詢問這個切片現在可能與哪些層級有連接——然後提議在這個切片中證明這個連接,而不是留給一個可能永遠不會建構的後續切片。
四個規則可以防止這變成範圍蔓延:
- 僅向後連接。 連接到已經建構的切片。永遠不要推測可能永遠不會存在的東西——這與
adapting.md中的「一個切片可以引用它前面的任何切片,永遠不要引用之後的」規則是相同的。 - 一個探測,而非一個功能。 標準是_一件如果連接中斷就會失敗的事情_。如果需要產品決策,那就是一個獨立的切片,而不是一個探測。
- 這是一個提議。 連接會擴大切片的範圍,因此它會與其他所有內容一起進入 §4 的審查,並且需要使用者的同意。
- 它需要一個驗證列, 否則就證明不了任何事情。
一個探測是一個暫用工件,而 §2 的規則已經涵蓋了它的移除:它沒有說明什麼會取代它,而取代它的那個切片(無論是哪個)會將其退役。
4. 將執行計劃寫入專案
node <技能目錄>/scripts/install-plan.mjs \
--repo <儲存庫目錄> --name <專案名稱> --slices NN
<技能目錄> 是這個技能安裝的位置:~/.claude/skills/setup-project、~/.agents/skills/setup-project,或專案自己的 .claude/skills/setup-project。該腳本會根據自身解析切片,因此它可以從任何這些位置正確執行。
這會將 slices/NN-*.md 渲染到 docs/setup/ 中,並解析 __PROJECT__ 和 __DOCS__,且不會寫入其他任何東西——沒有索引、沒有來源清單。docs/setup/ 只包含切片檔案,每個已建構的切片一個,而 ls 就是索引。不會安裝、建構或提交任何東西。
如果切片檔案已經存在,除非你傳入 --force,否則它將被保留不動,因為此時它是這個專案的執行記錄,而非參考文件的副本。
然後編輯腳本寫入的內容,在執行任何部分之前——這是計劃,不是參考文件的副本。將 §3 的研究結果融入:修正 pin 的版本、修復已移動的標誌、調整 API 已變更的步驟、並刪除或重寫任何與當前文件矛盾的部分。在敘述中說明原因,就像檔案其他部分所做的那樣。
向使用者展示你相對於參考文件所做的變更及其原因,並在執行前取得同意。此時計劃尚未經過驗證,值得他們花一分鐘檢視。
5. 執行
依序執行計劃中的區塊。cd 指令是根據上一個區塊結束的位置來撰寫的,因此請不要重新排列區塊或單獨執行一個區塊。
預期會發生失敗。這個計劃從未被執行過。這裡的失敗代表循環正在運作,而不是循環中斷——這正是 §7 存在要捕捉的資訊。修正原因,記錄下來,然後繼續。
6. 驗證——三輪,按此順序
一個切片透過三個階段來驗證,而它們並不是同一件事的三種變體。其中兩個階段由你執行並採取行動;第三個則是一個交接。前兩個階段的每一個都以修正計劃和此技能(§7)結尾——在下一個階段開始之前,趁著失敗的內容仍然在你眼前。只在最後修正一次,會失去本地經驗教訓,而被生產環境的經驗教訓所掩蓋。
第一輪——自動化,本地
完全按照所寫的方式執行切片的第一輪表格,從它命名的 執行位置 欄位開始——從儲存庫根目錄執行 typecheck 和測試腳本,從套件目錄執行開發伺服器和 preview。這種區分不是為了記帳:根目錄會透過 turbo 將腳本分派出去,因此它回答的是「workspace 是否正常」,這是驗證標誌唯一會問的問題。在套件內部執行相同的腳本,你只會得到該套件的統計資料,當另一個套件出問題時,這個統計資料仍然會顯示綠色。每個套件都位於兩層之下,因此 cd ../.. 可以從任何一個套件返回——而切片的主體通常會把你留在錯誤的位置,這就是為什麼每一行都要指定它在哪裡執行。
按給定的順序執行各行:有些行會產生後續行需要的東西。閱讀測試數量,而非退出碼——passWithNoTests 會讓空執行顯示綠色,因此驗證標誌命名的 Tests N passed 和 N 是唯一的證據。閱讀指令列印的內容,而不僅僅是它們返回的內容:棄用通知是 §3 最壞情況下的全部發現。
修復失敗的內容,然後立即進行修正(§7)——修正專案的切片文件和此技能的,兩者都要。
驗證標誌仍然是一個合併表格的切片。 只有部分切片經歷過這個三輪架構;其餘的仍然攜帶著一個混合了建構步驟與驗證步驟的單一表格。
| 步驟 | 執行位置 | 預期結果 |
|---|---|---|
執行 tsc --noEmit | repo 根目錄 | WARN 次數:比上一個切片相同或少, ERROR 為 0 |
執行 pnpm lint | repo 根目錄 | WARN 同上, ERROR 為 0 |
| …單元測試 |
一個仍然使用舊有樣式的切片絕對不能新增步驟到其混合表格中——新增的步驟會進入一個全新的第一輪表格。
切片 02 是導入的關鍵點。 它是 db 和 graphql 套件首次連結到專案的 web shell 的地方(即使 apps/web 在當時並不「正式」使用它們)。驗證類型而不匯入類型,無法證明類型是正確的。敘述會顯示是透過 tsconfig.json 路徑還是 workspace:* 相依性進行連結。兩者都做是常見的情況,但第二輪的生產驗證會發現 workspace:* 是否被錯誤地解析為本機路徑。
無需使用者確認。 失敗訊息是你修正計劃時的輸入,而不是請求許可的理由。
修正(§7)— 立即
在開始第二輪之前。
第二輪——自動化,生產環境
在 Neon 和 Cloudflare Workers 上執行,使用 wrangler。如果切片的執行計劃包含 wrangler deploy,則使用 --dry-run 來驗證 binding 和相容性日期,而不實際部署。
對於檢查 binding 和相容性日期的非部署切片,--dry-run 是標準工具;請記錄其輸出即使沒有錯誤也要注意——bindings: [new KV, removed D1, unchanged R2] 是審計軌跡。
如果切片包含一個暴露給網際網路的 Worker,請使用 wrangler deploy 進行一個暫存部署,並記錄 URL。
此輪沒有使用者確認。
如果第二輪失敗,修正計劃,進行修正(§7),然後重新執行第一輪和第二輪。第二輪的失敗通常意味著本機驗證遺漏了某些東西——環境變數、binding 或相容性旗標。
如果第二輪成功,繼續進行第三輪。
第三輪——手動,使用者
切片的第三輪表格列出了使用者應該在生產環境中檢查的內容,然後才說切片已完成。這些都是本機自動化無法涵蓋的事項:實際電子郵件是否送達、Stripe webhook 是否端到端運作、Cloudflare 儀表板中是否可以看到快取命中。
這是給使用者的。你不需要幫助他們完成這些步驟——他們只需要知道需要檢查什麼,以及如何從外部驗證。提供 curl 指令、Django shell 程式碼片段等等。
此輪沒有你的動作。 當使用者確認他們已經檢查過後,你才提交。
7. 修正——現在,而不是最後
每次執行計劃時,你都會學習一些關於參考文件和關於專案的東西。修正會將這些知識寫入兩個地方,而不是一次:
7a. 專案:docs/setup/NN-name.md
在檔案末尾新增一個 ## 修正 區塊(如果尚未存在則建立一個)。使用與檔案其餘部分相同的語氣:
## 修正
### 2025-04-03
- `pnpm-workspace.yaml` 需要一個明確的 `packages:` 陣列,因為 `create-next-app` 在新的 `apps/web` 內部建立了一個帶有自己圖案的 `pnpm-workspace.yaml`。兩個 workspace 根目錄都會產生 pnpm 錯誤,而不僅僅是第二個。
- `@lib/auth` 的 TypeScript 路徑在 `wrangler.toml` 的 `compatibility_flags` 中需要 `nodejs_compat`,因為它使用 `crypto`。
新增診斷資訊,而非摘要。 不要寫「修復了 TypeScript 錯誤」。寫出錯誤是什麼、你如何發現它、以及修復方法——這樣下一版的參考文件才能從中受益。
7b. 技能:slices/NN-name.md 和 reference/slices.md
將修正內容新增到技能本身的檔案中。這與寫入專案不同:技能檔案是此技能所有專案共用的。因此,要嘛將修正內容作為檔案中的一個新小節(例如,在一個已存在的 ## 已知問題 或 ## 附錄 區塊中),要嘛如果它適用於此技能的所有使用者,則將其合併到主體中。如果兩者兼而有之,則進行合併並在附錄中保留一個說明。
修正區塊是為了記錄,而非自動執行。 但在執行專案時,修正區塊是第一件事——它們是在執行計劃之前,你「研究當前事實」這一步驟(§3)的一部分。請參考專案的修正區塊作為參考:修正區塊包含在該儲存庫的上下文中被發現的陷阱。
8. 提交
git add -A
git commit -m "setup: slice NN — <名稱>"
當切片被提交後,這個調用就結束了。使用者在接下來的調用中可以建構下一個切片。
9. 例外情況:ToFu
如果使用者要求一組「要嘛全有,要嘛全無」的整體設定(通常稱為「ToFu 設定」),則不要使用逐步的切片方法。相反地,直接建立一個資料夾,從一個適合的模板複製一個合適的 docker-compose.yml 和 .env 檔案,然後結束。你可以參考 slices/00 以了解通用的設定建議,但不需要建立 docs/setup/、不需要計劃,也不需要驗證。
這不是一個切片的替代品——這是要嘛快速啟動一個開發環境(然後可能會被棄用),要嘛是為了協助概念驗證。如果使用者稍後想要生產環境,他們需要從頭開始進行逐步設定。