在配置了 ESLint 規則、Agent 技能與 MCP 伺服器之後,Gemini 的表現依然沒有完全符合我的預期。
在進行 AI 輔助寫程式時,有些行為讓我非常困擾:
effect() 來更新 Signal,這是不好的做法。ReactiveForm 來建立新表單,而非使用 Angular v22 中的 SignalForm。angular-cli MCP 伺服器的 search_documentations 工具。當我遇到不盡理想的情況時,我會請 AI 協助我更新 GEMINI.md、AGENTS.md 或 CONTEXT.md,這樣類似的問題就不會再次發生。
我從 Angular 官方網站複製了 GEMINI.md,並在其中加入了新規則。
Angular 團隊提供了 effect,但他們期望我們不要在許多使用案例中使用它
- **DOM Manipulation and Side-Effects**:
- **No `effect()` for DOM Manipulation**: Never use the standard `effect()` function to interact with or manipulate the DOM (doing so is unsafe, can cause layout thrashing, and crashes during Server-Side Rendering).
- **Prefer `afterRenderEffect()` for DOM**: For manual DOM tasks (e.g., measuring dimensions, focusing inputs, or integrating non-Angular charts), always use `afterRenderEffect()` or `afterNextRender()`. Perform any DOM-write operations strictly inside the scheduled `write` phase callback.
- **Prefer `linkedSignal()` for State Synchronization**: Never use `effect()` to reset or synchronize local signal states. Use `linkedSignal()` to cleanly reset, synchronize, or clamp values in response to other source signal changes.
effect() 雖然不是反模式,但它的設計初衷是用於更新非響應式資料(如本機儲存空間)或發出輸出。如果某個 Signal 取決於一或多個 Signal 的變更,我們應該使用 linkedsignal()。如果我們需要寫入 DOM 元素,我們應該在元件建構函式中呼叫 afterREnderEffect() 回呼函式。
## SSR & Browser Globals Protection
- **Avoid SSR Crashes**: Never access raw browser global objects (`window`, `document`, `navigator`, `localStorage`, etc.) directly inside components, services, or utilities. Doing so crashes Server-Side Rendering (SSR) or pre-rendering contexts.
- **Inject `DOCUMENT`**: Always inject Angular's built-in `DOCUMENT` token from `@angular/common` instead of referencing the global `document` keyword.
- **Composed Browser-Global Injection Tokens**: To safely reference browser-only global objects (such as `window`, `localStorage`, `sessionStorage`, `navigator`, etc.) without crashing during SSR:
1. Define a global cached `IS_BROWSER` token to identify the platform context.
2. **Compose other global tokens** by injecting `IS_BROWSER` into their factory callbacks.
3. Explicitly type these composed tokens as `<Type> | null` so that TypeScript natively compile-checks and forces developers to verify existence before accessing them:
我新增了 SSR & Browser Globals Protection 章節來解釋存取全域物件(例如 window)的安全方式。我們使用 isPlatformBrowser() 與 PLATFORM_ID 注入權杖來判斷應用程式目前是用戶端渲染(CSR)還是伺服器端渲染(SSR)。如果是前者,window 已被定義且可安全使用;否則,我們應該回傳 undefined 或 null 以避免非預期的當機。
當我們在 Antigravity CLI 中執行 /setup-matt-pocock-skills 時,它在專案中新增了一個基礎的 AGENTS.md。我透過反覆迭代修改它以建立新規則,進而解決我的痛點。
2. **Tailwind CSS v4 Component Styling**: Always prioritize Tailwind CSS v4 `@apply` utility classes over raw vanilla CSS inside component-scoped stylesheets. To compile correctly in isolation, you MUST prepend an explicit `@reference` directive pointing relatively to the global `src/styles.css` stylesheet file.
- **Correct**:
@reference "../../../../../styles.css";
.toast-container {
@apply fixed top-6 right-6 flex flex-col;
}
套用 TailwindCSS V4 類別,並將行內 CSS 類別提取至外部 CSS 檔案中。
4. **Signal Forms (`@angular/forms/signals`) Constraint**: All forms in this repository MUST be built using Angular's modern, reactive Signal Forms (`@angular/forms/signals`). The use of legacy Reactive Forms (`FormGroup`, `FormControl`, `FormBuilder` from `@angular/forms`) or template-driven forms is strictly prohibited. This guarantees optimal performance, native reactivity, and a unified state management architecture.
我的技術堆疊是 Angular v22,而 SignalForm 在此版本中已經穩定,因此我強制要求所有新表單都必須使用 Signal Forms 來實作。
When researching framework/platform APIs, architecture, or resolving errors:
1. **Angular Questions (`angular-cli` MCP)**:
- Primary: Query the `angular-cli` MCP server (`search_documentation`, `get_best_practices`, `ai_tutor`) targeting the project's current major Angular version (`22`).
- Fallback: Use web search if the MCP server is unavailable or returns no results.
Gemini 變得很偷懶,選擇使用 WebSearch 工具來尋找 Angular 問題的答案。我建立了一條規則,強制使用 angular-cli MCP 伺服器來搜尋文件,並以網路搜尋作為備援方案。這對 Firebase 查詢也是一樣:優先使用 Firebase MCP 伺服器,並以網路搜尋作為備援。
2. **Defer Full-Suite Verification to End-of-Task**:
- Do NOT run full repo-wide commands (`npm run lint`, `npm run format`, `npm test`, full `npm run test:once`, `npm run build`) recursively after every individual contiguous block edit. Gather all contiguous and related edits, apply them first, and run full validation commands EXACTLY ONCE at the end of the complete task milestone.
這條規則將格式化、程式碼檢查與測試延後至對所有檔案完成修改之後。這縮短了開發時間,並減少了我需要按「Y」以繼續的次數。
3. **ESLint Compliance in Spec Files**:
- When generating or updating unit tests (`*.spec.ts`), ensure they adhere to the project's ESLint config. Avoid common violations like relative imports in TypeScript (always use absolute path aliases starting with `@/` for TS files; relative paths are strictly for CSS `@reference` directives) and missing curly braces. Do not use legacy reactive forms or custom test classes that expand member visibility just for testing.
雖然模型能夠高效率地生成新的測試與程式碼,但它有時會投機取巧以讓測試通過。例如將變數轉型為 as any 而不是 as unknown。當變更推送到存放庫時,自動化管線會因為 ESLint 錯誤而失敗。這條規則要求模型在宣告任務完成之前,必須先修正所有違反 ESLint 規則的生成程式碼。
包含領域詞典的完整 CONTEXT.md 內容。
當專案剛啟動或正在進行中時,該檔案應該用 1 到 2 句話來說明問題的背景脈絡。例如:它從文字生成語音,並使用 Web Audio API 播放語音。當程式碼趨於穩定後,可以請 AI 掃描程式碼工作區以進行更新。