{"owner":"bitwarden","repo":"clients","hasSkills":true,"totalSkillsCount":67,"totalTokensCount":33215,"categories":["anthropic-skill","claude-rule","plugin-manifest"],"hasMcp":false,"mcpConfig":null,"found":[".claude/skills/angular-modernization/SKILL.md",".claude/skills/create-pull-request/SKILL.md",".claude/skills/create-hec-event-integration/SKILL.md",".claude/skills/figma-to-angular/SKILL.md",".claude/skills/evaluating-sdk-internal-updates/SKILL.md",".claude/skills/fix-angular-fixmes/SKILL.md",".claude/skills/cipher-type-planner/SKILL.md",".claude/CLAUDE.md",".claude/skills/writing-client-code/SKILL.md",".claude/rules/autofill-content-scripts.md",".claude/rules/angular.md",".claude/rules/flatpak-manifest.md",".claude/rules/i18n.md",".claude/rules/snap-permissions.md",".claude/rules/storybook-routing.md",".claude/rules/tailwind.md",".claude/rules/testing.md",".claude/rules/typescript.md","libs/tools/README.md",".claude/skills/angular-modernization/migration-patterns.md",".claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs","libs/tools/export-vault-core/README.md","libs/tools/export-vault-core/package.json","libs/tools/export-vault-core/project.json","libs/tools/export-vault-core/tsconfig.json","libs/tools/export-vault-core/tsconfig.spec.json","libs/tools/export-vault-ui/README.md","libs/tools/export-vault-ui/package.json","libs/tools/export-vault-ui/project.json","libs/tools/export-vault-ui/tsconfig.json","libs/tools/export-vault-ui/tsconfig.spec.json","libs/tools/send/README.md","libs/tools/share/README.md","libs/tools/share/package.json","libs/tools/share/project.json","libs/tools/share/tsconfig.eslint.json","libs/tools/share/tsconfig.json","libs/tools/share/tsconfig.spec.json",".claude/skills/create-hec-event-integration/example/token-auth.md",".claude/skills/figma-to-angular/evals/evals.json","libs/tools/generator/components/package.json","libs/tools/generator/components/project.json","libs/tools/generator/components/readme.md","libs/tools/generator/components/tsconfig.json","libs/tools/generator/components/tsconfig.spec.json","libs/tools/generator/core/package.json","libs/tools/generator/core/project.json","libs/tools/generator/core/tsconfig.json","libs/tools/generator/core/tsconfig.spec.json","libs/tools/send/send-ui/package.json","libs/tools/send/send-ui/project.json","libs/tools/send/send-ui/tsconfig.json","libs/tools/send/send-ui/tsconfig.spec.json","libs/common/src/tools/integration/README.md","libs/tools/generator/extensions/history/package.json","libs/tools/generator/extensions/history/project.json","libs/tools/generator/extensions/history/tsconfig.json","libs/tools/generator/extensions/history/tsconfig.spec.json","libs/tools/generator/extensions/legacy/package.json","libs/tools/generator/extensions/legacy/project.json","libs/tools/generator/extensions/legacy/tsconfig.json","libs/tools/generator/extensions/legacy/tsconfig.spec.json","libs/tools/generator/extensions/navigation/package.json","libs/tools/generator/extensions/navigation/project.json","libs/tools/generator/extensions/navigation/tsconfig.json","libs/tools/generator/extensions/navigation/tsconfig.spec.json","libs/common/src/tools/extension/vendor/readme.md"],"skills":{".claude/skills/angular-modernization/SKILL.md":"---\nname: angular-modernization\ndescription: Modernizes Angular code such as components and directives to follow best practices using both automatic CLI migrations and Bitwarden-specific patterns. YOU must use this skill when someone requests modernizing Angular code. DO NOT invoke for general Angular discussions unrelated to modernization.\nallowed-tools: Read, Write, Glob, Bash(npx ng generate:*)\n---\n\n# Angular Modernization\n\nTransforms legacy Angular components to modern architecture using a two-step approach:\n\n1. **Automated migrations** - Angular CLI schematics for standalone, control flow, and signals\n2. **Bitwarden patterns** - ADR compliance, OnPush change detection, proper visibility, thin components\n\n## Workflow\n\n### Step 1: Run Angular CLI Migrations\n\n**⚠️ CRITICAL: ALWAYS use Angular CLI migrations when available. DO NOT manually migrate features that have CLI schematics.**\n\nAngular provides automated schematics that handle edge cases, update tests, and ensure correctness. Manual migration should ONLY be used for patterns not covered by CLI tools.\n\n**IMPORTANT:**\n\n- Always run the commands using `npx ng`.\n- All the commands must be run on directories and NOT files. Use the `--path` option to target directories.\n- Run migrations in order (some depend on others)\n\n#### 1. Standalone Components\n\n```bash\nnpx ng generate @angular/core:standalone --path=<directory> --mode=convert-to-standalone\n```\n\nNgModule-based → standalone architecture\n\n#### 2. Control Flow Syntax\n\n```bash\nnpx ng generate @angular/core:control-flow\n```\n\n`*ngIf`, `*ngFor`, `*ngSwitch` → `@if`, `@for`, `@switch`\n\n#### 3. Signal Inputs\n\n```bash\nnpx ng generate @angular/core:signal-input-migration\n```\n\n`@Input()` → signal inputs\n\n#### 4. Signal Outputs\n\n```bash\nnpx ng generate @angular/core:output-migration\n```\n\n`@Output()` → signal outputs\n\n#### 5. Signal Queries\n\n```bash\nnpx ng generate @angular/core:signal-queries-migration\n```\n\n`@ViewChild`, `@ContentChild`, etc. → signal queries\n\n#### 6. inject() Function\n\n```bash\nnpx ng generate @angular/core:inject-migration\n```\n\nConstructor injection → `inject()` function\n\n#### 7. Self-Closing Tag\n\n```bash\nnpx ng generate @angular/core:self-closing-tag\n```\n\nUpdates templates to self-closing syntax\n\n#### 8. Unused Imports\n\n```bash\nnpx ng generate @angular/core:unused-imports\n```\n\nRemoves unused imports\n\n### Step 2: Apply Bitwarden Patterns\n\nSee [migration-patterns.md](migration-patterns.md) for detailed examples.\n\n1. Add OnPush change detection\n2. Apply visibility modifiers (`protected` for template access, `private` for internal)\n3. Convert local component state to signals\n4. Keep service observables (don't convert to signals)\n5. Extract business logic to services\n6. Organize class members correctly\n7. Update tests for standalone\n\n### Step 3: Validate\n\n- Fix linting and formatting using `npm run lint:fix`\n- Run tests using `npm run test`\n\nIf any errors occur, fix them accordingly.\n\n## Key Decisions\n\n### Signals vs Observables\n\n- **Signals** - Component-local state only (ADR-0027)\n- **Observables** - Service state and cross-component communication (ADR-0003)\n- Use `toSignal()` to bridge observables into signal-based components\n\n### Visibility\n\n- `protected` - Template-accessible members\n- `private` - Internal implementation\n\n### Other Rules\n\n- Always add OnPush change detection\n- No TypeScript enums (use const objects with type aliases per ADR-0025)\n- No code regions (refactor instead)\n- Thin components (business logic in services)\n\n## Validation Checklist\n\nBefore completing migration:\n\n- [ ] OnPush change detection added\n- [ ] Visibility modifiers applied (`protected`/`private`)\n- [ ] Signals for component state, observables for service state\n- [ ] Class members organized (see [migration-patterns.md](migration-patterns.md#class-member-organization))\n- [ ] Tests updated and passing\n- [ ] No new TypeScript enums\n- [ ] No code regions\n\n## References\n\n### Bitwarden ADRs\n\n- [ADR-0003: Observable Data Services](https://contributing.bitwarden.com/architecture/adr/observable-data-services)\n- [ADR-0025: No TypeScript Enums](https://contributing.bitwarden.com/architecture/adr/no-enums)\n- [ADR-0027: Angular Signals](https://contributing.bitwarden.com/architecture/adr/angular-signals)\n- [Bitwarden Angular Style Guide](https://contributing.bitwarden.com/contributing/code-style/web/angular)\n\n### Angular Resources\n\n- [Angular Style Guide](https://angular.dev/style-guide)\n- [Angular Migrations](https://angular.dev/reference/migrations)\n- [Angular CLI Schematics](https://angular.dev/tools/cli/schematics)\n",".claude/skills/create-pull-request/SKILL.md":"---\nname: create-pull-request\nversion: 0.1.0\ndescription: Pull request creation workflow for Bitwarden Clients. Use when creating PRs, writing PR descriptions, or preparing branches for review. Triggered by \"create PR\", \"pull request\", \"open PR\", \"gh pr create\", \"PR description\".\n---\n\n# Create Pull Request\n\n## PR Title Format\n\n```\n[PROJ-XXXXX] Short imperative summary\n```\n\n**Examples:**\n\n- `[PM-12345] Add passkey support to vault item creation`\n- `[CL-1124] Increase badge max-width`\n- `[PM-33446] Ensure attachments are cleared on clone`\n\n**Rules:**\n\n- Include the Jira ticket prefix (e.g. `PM-`, `CL-`)\n- Keep under 70 characters total\n- Use imperative mood in the summary\n\n---\n\n## PR Body Template\n\n**IMPORTANT:** Always follow the repo's PR template at `.github/PULL_REQUEST_TEMPLATE.md`. Add TODO items to the screenshots section for each scenario that should be captured if any.\n\n---\n\n## Pre-PR Checklist\n\n1. **All tests pass**: Run `npm test`\n2. **Lint clean**: Run `npm run lint`\n3. **No unintended changes**: Check `git diff origin/main...HEAD` for unexpected files\n4. **Branch up to date**: Rebase on `main` if needed\n\n---\n\n## Creating the PR\n\n```bash\n# Ensure branch is pushed\ngit push -u origin <branch-name>\n\n# Create PR as draft by default (body follows .github/PULL_REQUEST_TEMPLATE.md)\ngh pr create --draft --title \"[PM-XXXXX] Short summary\" --body \"<fill in from PR template>\"\n```\n\n**Default to draft PRs.** Only create a non-draft (ready for review) PR if the user explicitly requests it.\n\n---\n\n## Base Branch\n\n- Default target: `main`\n- Check with team if targeting a feature branch instead\n",".claude/skills/create-hec-event-integration/SKILL.md":"---\nname: create-hec-event-integration\ndescription: Use when adding a new HEC (HTTP Event Collector) event integration to the Bitwarden web client. Implements the Splunk token authentication model (Bearer token + URI). Covers feature flag setup and card registration behind the flag. Does not apply to API key integrations or integrations requiring a custom connect dialog.\n---\n\n# Create HEC Event Integration (Token Auth)\n\n## Step 1 - Prompts\n\nAsk these questions one at a time — wait for each answer before proceeding.\n\n**Prompt 1 — Service name:** \"What is the service name for this integration?\" (e.g. `Splunk`, `CrowdStrike`, `Panther`)\n\nUse the answer as `<ServiceName>` throughout. The string value in the constant must exactly match what you use as the card's `name` in Step 4 — a mismatch silently saves the config with the wrong service name.\n\n**Prompt 2 — Authentication:** \"How is this integration authenticated?\" (e.g. `Token`, `API key`)\n\n- **If Token** — continue with the steps below.\n- **If anything else** — stop and inform the user: \"This skill currently only supports token-based authentication. Support for other authentication methods hasn't been added yet.\"\n\n**Prompt 3 — Logos:** \"Do you have the integration logo(s) ready to provide?\"\n\n- **If yes** — ask for the light-mode SVG file path, and optionally a dark-mode SVG path. Copy both to `apps/web/src/images/integrations/` using the naming convention `logo-<service-name-kebab>-color.svg` and `logo-<service-name-kebab>-darkmode.svg`. Use those filenames in Step 4.\n- **If no** — use placeholder paths in Step 4 and add a `// TODO: add logo before shipping` comment.\n\n## Step 2 — Add service name constant\n\n**File:** `bitwarden_license/bit-common/src/dirt/organization-integrations/models/organization-integration-service-type.ts`\n\nAdd to `OrganizationIntegrationServiceName`:\n\n```typescript\nexport const OrganizationIntegrationServiceName = Object.freeze({\n  CrowdStrike: \"CrowdStrike\",\n  Datadog: \"Datadog\",\n  Huntress: \"Huntress\",\n  <ServiceName>: \"<ServiceName>\", // ← add here\n} as const);\n```\n\n## Step 3 — Add feature flag\n\n**File:** `libs/common/src/enums/feature-flag.enum.ts`\n\nAdd the enum entry and its default. The enum key is PascalCase; the string value is kebab-case (e.g. `CrowdStrike` → `crowdstrike`, `Sumo Logic` → `sumo-logic`):\n\n```typescript\n// In the FeatureFlag enum:\nEventManagementFor<ServiceName> = \"event-management-for-<service-name-kebab>\",\n\n// In the defaultFlags object:\n[FeatureFlag.EventManagementFor<ServiceName>]: FALSE,\n```\n\nExample for `Panther`:\n\n```typescript\nEventManagementForPanther = \"event-management-for-panther\",\n[FeatureFlag.EventManagementForPanther]: FALSE,\n```\n\n## Step 4 — Register the card behind the feature flag\n\n**File:** `bitwarden_license/bit-web/src/app/dirt/organization-integrations/organization-integrations.resolver.ts`\n\nIf logos were provided, copy them to `apps/web/src/images/integrations/` first, then use the actual filenames below. If not, use the placeholder paths with the TODO comment:\n\n```typescript\nconst <serviceName>FeatureEnabled = await firstValueFrom(\n  this.configService.getFeatureFlag$(FeatureFlag.EventManagementFor<ServiceName>),\n);\n\nif (<serviceName>FeatureEnabled) {\n  integrations.push({\n    name: OrganizationIntegrationServiceName.<ServiceName>, // must match Step 1 exactly\n    linkURL: \"https://bitwarden.com/help/<service-name>-siem/\",\n    image: \"../../../../../../../images/integrations/logo-<service-name>-color.svg\", // TODO: add logo before shipping (if not yet provided)\n    imageDarkMode: \"../../../../../../../images/integrations/logo-<service-name>-darkmode.svg\", // TODO: add logo before shipping (omit if no dark mode variant)\n    type: IntegrationType.EVENT,\n    canSetupConnection: true,\n    integrationType: OrganizationIntegrationType.Hec,\n  });\n}\n```\n\nNo changes needed to `IntegrationCardComponent` — new HEC services fall into the existing `else` branch, which calls `openHecConnectDialog` → `saveHec` → `deleteHec`. These methods already call `buildHecConfiguration` and `buildHecTemplate` using the card's `name` as the service name.\n\n## Step 5 — Add tests\n\n**File:** `bitwarden_license/bit-common/src/dirt/organization-integrations/models/integration-builder.spec.ts`\n\nAdd one `it` block inside the existing `describe(\"buildHecConfiguration\", ...)` block, and one inside `describe(\"buildHecTemplate\", ...)`. Use typed property access — do not use `JSON.parse`:\n\n```typescript\n// Inside describe(\"buildHecConfiguration\", ...)\nit(\"should work with <ServiceName> service name\", () => {\n  const config = OrgIntegrationBuilder.buildHecConfiguration(\n    \"https://test.<servicename>.com/hec\",\n    \"test-token\",\n    OrganizationIntegrationServiceName.<ServiceName>,\n  );\n\n  expect(config).toBeInstanceOf(HecConfiguration);\n  expect((config as HecConfiguration).uri).toBe(\"https://test.<servicename>.com/hec\");\n  expect((config as HecConfiguration).scheme).toBe(\"Bearer\");\n  expect((config as HecConfiguration).token).toBe(\"test-token\");\n  expect(config.bw_serviceName).toBe(OrganizationIntegrationServiceName.<ServiceName>);\n});\n\n// Inside describe(\"buildHecTemplate\", ...)\nit(\"should work with <ServiceName> service name\", () => {\n  const template = OrgIntegrationBuilder.buildHecTemplate(\n    \"test-index\",\n    OrganizationIntegrationServiceName.<ServiceName>,\n  );\n\n  expect(template).toBeInstanceOf(HecTemplate);\n  expect((template as HecTemplate).index).toBe(\"test-index\");\n  expect(template.bw_serviceName).toBe(OrganizationIntegrationServiceName.<ServiceName>);\n});\n```\n\n## Step 6 — Run unit tests\n\nRun the unit tests for the spec file and confirm they all pass before finishing:\n\n```bash\nnpx jest bitwarden_license/bit-common/src/dirt/organization-integrations/models/integration-builder.spec.ts\n```\n\nAll tests must pass. If any fail, fix them before proceeding.\n\n## Common Mistakes\n\n| Mistake                                                                 | Fix                                                                                                   |\n| ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |\n| `name` in card doesn't match `OrganizationIntegrationServiceName` value | They must be identical strings — `saveHec()` casts the name directly                                  |\n| Feature flag default not set to `FALSE`                                 | Always add the default entry in `defaultFlags`; new flags without a default will not work correctly   |\n| Kebab-case mismatch in flag string                                      | Convert consistently: lowercase, spaces → hyphens                                                     |\n| Adding a new `OrganizationIntegrationType`                              | Not needed — all HEC services share `OrganizationIntegrationType.Hec`                                 |\n| Creating a new config/template class                                    | Not needed — `HecConfiguration` and `HecTemplate` handle all HEC services                             |\n| Referencing an image path without copying the file                      | Copy SVGs to `apps/web/src/images/integrations/` first; if logos aren't ready, leave the TODO comment |\n",".claude/skills/figma-to-angular/SKILL.md":"---\nname: figma-to-angular\ndescription: >\n  Converts Figma designs into production Angular components with Storybook stories for the Bitwarden\n  Clients monorepo. Use this skill whenever the user provides a Figma URL and wants to create an\n  Angular component, or mentions \"implement this design\", \"create a component from Figma\",\n  \"build this from the design spec\", or similar. Also trigger when the user pastes a Figma link\n  and asks for a component, even if they don't say \"Figma\" explicitly.\n---\n\n# Figma to Angular Component\n\nThis skill turns a Figma design spec into a fully implemented Angular component with Storybook\nstories in the Bitwarden Clients monorepo. The output should match the design visually while\nfollowing all codebase conventions.\n\n## Workflow\n\n### Phase 1: Extract the design\n\nCall `get_design_context` from the Figma MCP server. Extract the fileKey and nodeId from the URL:\n\n- `figma.com/design/:fileKey/:fileName?node-id=:nodeId` → convert `-` to `:` in nodeId\n- Pass `clientFrameworks: \"angular\"` and `clientLanguages: \"typescript,html,css\"`\n\nThe response contains reference code (React+Tailwind), a screenshot, and design metadata. Treat the\ncode as a **structural reference only** — it must be fully converted to Angular.\n\n### Phase 2: Explore the codebase\n\nBefore writing any code, understand the conventions of the target location. Use an Explore agent to\ndiscover:\n\n1. **Component file patterns** — look at 2–3 sibling components in the same directory. Note the\n   file naming, module structure, barrel exports, and whether they use standalone components.\n2. **Existing components to reuse** — search for components that match parts of the design (labels,\n   buttons, inputs, icons). Reuse them instead of reimplementing.\n3. **Tailwind configuration** — read the relevant `tailwind.config` to understand the prefix\n   (commonly `tw-`), available design tokens, and color system.\n4. **Storybook patterns** — check how sibling stories are structured: imports, `moduleMetadata`,\n   `I18nMockService` usage, `argTypes`.\n5. **i18n setup** — find the `I18nPipe` import path and the locale messages file to add new keys.\n\nThis exploration is critical. Skipping it leads to components that don't match the codebase and\nrequire extensive rework.\n\n### Phase 3: Map design tokens\n\nCreate a mapping from Figma's design values to the codebase's Tailwind tokens. The Figma response\nincludes raw CSS values (hex colors, px sizes). These must be translated to semantic tokens.\n\nCommon mappings for Bitwarden:\n\n| Figma concept      | Where to look                                               |\n| ------------------ | ----------------------------------------------------------- |\n| Colors (hex)       | `tailwind.config` → `colors`, `bg`, `fg`, `border` sections |\n| Font sizes         | `tailwind.config` → `extend.fontSize`                       |\n| Spacing / padding  | Standard Tailwind scale with prefix                         |\n| Border radius      | Standard Tailwind (`rounded-*`)                             |\n| Component variants | Existing component source (e.g. `base-button.directive.ts`) |\n\nNever use raw hex values. Always map to a semantic token. If no exact match exists, use the closest\navailable token and note the discrepancy.\n\n### Phase 4: Plan the component\n\nBefore writing code, create a plan covering:\n\n- **Selector** — follows the relevant prefix convention\n- **Inputs and outputs** — use Angular signal-based `input()` and `output()` functions\n- **Content projection** — identify slots for `<ng-content>` (labels, hints, actions)\n- **Existing component reuse** — which existing components to import and use\n- **i18n keys** — list all user-visible strings that need localization\n- **Accessibility** — keyboard navigation, ARIA attributes, focus management\n\nPresent this plan to the user and get approval before writing files.\n\n### Phase 5: Implement\n\nCreate these files following the patterns discovered in Phase 2:\n\n#### Component TypeScript (`component-name.component.ts`)\n\n```typescript\n@Component({\n  selector: \"bit-component-name\",\n  templateUrl: \"./component-name.component.html\",\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  imports: [/* discovered dependencies */],\n  host: {\n    class: \"tw-block\",\n  },\n})\n```\n\nKey conventions:\n\n- `ChangeDetectionStrategy.OnPush` always\n- Signal-based inputs: `input()`, `input.required<T>()`, `input(default, { transform: booleanAttribute })`\n- Signal-based outputs: `output<T>()`\n- Internal state with `signal()` and `viewChild()`\n- Protected methods for template bindings, private for internal logic\n- No TypeScript enums — use `Object.freeze({ ... } as const)` with a same-name type alias (ADR-0025).\n  Even for simple string unions, prefer the const object pattern for consistency.\n\n#### Component template (`component-name.component.html`)\n\n- Use new Angular control flow: `@if`, `@for`, `@switch`\n- Avoid deprecated attributes like `ngClass` and `ngStyle`, prefer `class` and `style`.\n- All Tailwind classes use the library's prefix (check `tailwind.config`)\n- All user-visible strings go through `{{ \"key\" | i18n }}` or `{{ \"key\" | i18n: param }}`\n- Reuse existing components (`bit-label`, `bitButton`, `bit-hint`, etc.)\n\n#### Barrel export (`index.ts`)\n\n```typescript\nexport { ComponentNameComponent } from \"./component-name.component\";\n```\n\n#### Storybook stories (`component-name.stories.ts`)\n\nEvery component gets stories. Follow the exact patterns from sibling components:\n\n```typescript\nimport { Meta, moduleMetadata, StoryObj } from \"@storybook/angular\";\nimport { I18nService } from \"@bitwarden/common/platform/abstractions/i18n.service\";\nimport { I18nMockService } from \"../utils/i18n-mock.service\";\n\nexport default {\n  title: \"Component Library/ComponentName\",\n  component: ComponentNameComponent,\n  decorators: [\n    moduleMetadata({\n      imports: [/* component + dependencies */],\n      providers: [\n        {\n          provide: I18nService,\n          useFactory: () =>\n            new I18nMockService({\n              // All i18n keys used by the component\n              // Use __$1__ for parameter placeholders\n            }),\n        },\n      ],\n    }),\n  ],\n  parameters: {\n    design: {\n      type: \"figma\",\n      url: \"/* original Figma URL */\",\n    },\n  },\n} as Meta<ComponentNameComponent>;\n```\n\nInclude stories for:\n\n- Default state\n- Key variant states (if applicable)\n- Edge cases visible in the design (disabled, loading, empty, etc.)\n\n#### Unit tests (`component-name.component.spec.ts`)\n\nWrite unit tests when the component has behavior that Storybook stories alone can't verify. Stories\ncover visual rendering and variant appearance — don't duplicate that. Tests should focus on:\n\n- **Computed state and logic** — e.g. a rating component rounding fractional values, a progress bar\n  clamping percentages to 0–100, variant-to-class mappings\n- **Event emission** — outputs fire with the correct payload (files selected, rating changed, toast\n  dismissed)\n- **Input validation / edge cases** — boundary values, empty inputs, invalid types rejected\n- **Accessibility attributes** — correct `role`, `aria-label`, `aria-live` values change with state\n- **Keyboard interaction** — Enter/Space triggers actions, arrow keys navigate\n\nSkip tests for components that are purely presentational with no logic beyond displaying inputs\n(e.g. a simple badge or divider). Look at sibling `*.spec.ts` files to match the test setup\npatterns (TestBed configuration, imports, mock providers).\n\n#### Update barrel exports\n\nAdd `export * from \"./component-name\";` to the parent directory's `index.ts`, inserted\nalphabetically.\n\n### Phase 6: Verify in Storybook\n\nUse Playwright MCP tools to verify the component renders correctly:\n\n1. Navigate to the story's iframe URL:\n   `http://localhost:6006/iframe.html?id=component-library-component-name--default&viewMode=story`\n2. Take a snapshot to confirm the component rendered without errors\n3. Take a screenshot and compare it against the Figma design screenshot\n4. Test interactive behavior:\n   - Click buttons and interactive elements\n   - Verify correct responses (dialogs, state changes, etc.)\n5. Check the disabled story if applicable\n\nIf Storybook isn't running or Playwright isn't available, tell the user and suggest they verify\nmanually.\n\n### Phase 7: Iterate\n\nPresent the screenshot to the user alongside the original Figma screenshot. Ask if adjustments are\nneeded. Common refinements:\n\n- Token mismatches (wrong shade, spacing off)\n- Missing hover/focus states\n- Accessibility gaps\n- i18n strings that need rewording\n\n## Key principles\n\n- **Never hardcode user-visible strings.** Every string goes through `I18nPipe`. This includes\n  aria-labels and other accessibility text — they need localization too. Add keys to the locale file\n  and provide mocks in stories.\n- **Reuse existing components aggressively.** If the design has a button, use `bitButton`. If it\n  has a label, use `bit-label`. Don't reimplement what exists.\n- **Explore before you build.** The codebase exploration step prevents most rework. Patterns vary\n  between apps and libraries — always check the specific target directory.\n- **Semantic tokens over raw values.** Map every Figma color, spacing, and font size to the\n  codebase's design token system. Raw hex values break theming.\n- **Use native HTML patterns for file inputs.** For file upload components, use a `<label for>`\n  element pointing at a hidden `<input type=\"file\">` rather than programmatic `.click()`. The\n  codebase has an `AriaDisabledClickCaptureService` that intercepts programmatic clicks on certain\n  elements, causing silent failures.\n- **No NgModules.** All new components must be standalone (`imports: [...]` in the `@Component`\n  decorator). Do not create `*.module.ts` files. The codebase is moving away from NgModules.\n- **Plan before you code.** The plan step catches API design issues early. It's cheaper to fix a\n  plan than to rewrite files.\n",".claude/skills/evaluating-sdk-internal-updates/SKILL.md":"---\nname: evaluating-sdk-internal-updates\ndescription: Evaluates a bitwarden/clients \"Update sdk-internal to\" PR against the sdk-internal commit range for compile-time and runtime breaking changes, maps affected symbols to TypeScript call sites, and applies clear in-scope fixes. Use when reviewing an SDK bump PR, an @bitwarden/sdk-internal version change in package.json, or triaging sdk-internal breaking changes. Requires a sibling `bitwarden/sdk-internal` clone.\nallowed-tools:\n  - Bash(node .claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs *)\n  - Bash(npm run test:types)\n  - Bash(npm run lint:fix)\n  - Bash(npm test -- *)\n  - Bash(npx prettier *)\n  - Bash(gh pr diff:*)\n  - Bash(gh pr view:*)\n  - Bash(git -C * log *)\n  - Bash(git -C * show *)\n  - Bash(git grep:*)\n  - Bash(git add:*)\n  - Bash(git commit:*)\n  - Bash(grep:*)\n  - Read\n  - Grep\n  - Glob\n  - Edit\n  - Write\n  - Skill(writing-client-code)\n  - Skill(bitwarden-delivery-tools:committing-changes)\n---\n\n# Evaluating sdk-internal Updates\n\n**Identify both compile-time and runtime breaks before fixing anything — fixing the first break found is not finishing.** Steps 4-7 cover the entire commit range, step 8 reports it, and step 9 starts only after that.\n\nThis SDK is the boundary where Protected Data becomes Vault Data. A fix must not send a decrypted value to an API service, log one, or move a decrypt out of the SDK into TypeScript. `.claude/CLAUDE.md` forbids new encryption logic in this repo, so a bump that appears to require some is a finding to report, not a fix to write. [Security definitions](https://contributing.bitwarden.com/architecture/security/definitions).\n\n## Identify\n\nBinding surface facts specific to this SDK: the pin is two npm versions in the root `package.json`, `@bitwarden/sdk-internal` and `@bitwarden/commercial-sdk-internal`, always the same string — `npm run lint:sdk-internal-versions` fails if they differ. The version encodes no commit; the sdk-internal SHA lives in `VERSION` inside the tarball. Nothing generated is committed, here or upstream: the API record is `bitwarden_wasm_internal.d.ts` inside the published package, whose root and `node/` copies are byte-identical, and `bitwarden_wasm_internal_bg.wasm.d.ts` is raw ABI and never a finding.\n\nThe annotations behind this surface are `#[wasm_bindgen]` and `derive(..., Tsify)`. `#[uniffi::export]` is mobile-only and largely disjoint, so a uniffi sweep under-reports this surface badly. Serde attributes are part of the emitted type: `rename`, `rename_all`, `skip` and `Option` changes alter the `.d.ts` with no Rust signature change, and a `features = [\"wasm\"]` edit in `crates/bitwarden-wasm-internal/Cargo.toml` adds or removes types wholesale. Enum members are wire values (`CipherType.Login = 1`); a renumbering compiles clean at every call site and corrupts data.\n\nMost classes have a `private constructor()` and are reachable only through a parent client accessor (`sdk.user_crypto_management()`), so a renamed accessor breaks callers with the class itself unchanged. `tsconfig.base.json` sets `skipLibCheck: true`, so only call sites are checked. `bitwarden_license/bit-common/src/platform/sdk/sdk-alias.d.ts` re-exports the commercial package under the `@bitwarden/sdk-internal` module name and ESLint forbids importing it directly, so call sites grep identically for both packages.\n\n1. `gh pr diff <PR> -R bitwarden/clients -- package.json` → the old and new version.\n2. Locate the `bitwarden/sdk-internal` clone in a sibling directory — `../sdk-internal`, where CI places it. If there is none, stop and tell the user it is a required prerequisite for this skill; do not clone it yourself.\n3. Read the baseline type check. **In CI it has already run and the prompt names its log; grep it for `error TS` and treat that as this step's output. Do not run it.** Locally, `npm run test:types`. Note any failure and continue — a clean run rules out compile breaks only where the baseline looks, and step 7 is where it does not look.\n4. `node .claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs <old-version> <new-version>`, then again with `--commercial`. It prints RANGE (including the sdk-internal SHA pair step 5 needs), REMOVED, ADDED and MUTATED, keyed per member: a class method, interface property and enum member each get their own `Owner.member` entry. In ADDED, only a pre-existing owner is a break — a new non-optional property on an existing interface breaks every construction site.\n5. `git -C ../sdk-internal log --reverse --oneline <old-sha>..<new-sha>`, then `show` each commit. Classify per hunk, not per commit — a commit with one additive headline change can still carry a second, unrelated breaking hunk. A hunk that only touches a macro invocation does not show the surface; read the macro's definition before classifying it. This is also what explains a property the step 4 diff shows renamed or gone.\n6. For every symbol from step 4, `git grep -n '<Symbol>'` from the repo root for the bare name — no module list and no import-prefix filter, since everything arrives as a plain `@bitwarden/sdk-internal` import and both apps and `bitwarden_license` consume it. Use `git grep`, not `grep -r`, which also searches `node_modules/@bitwarden/*sdk-internal/` and reports the bindings themselves as call sites. A surface change stays a candidate until a call site makes it a finding.\n7. Three sets of call sites step 3 does not cover. Read each one the range touches.\n   - `apps/` and `bitwarden_license/` get no dedicated `tsc` leg and ride only on `tsc-strict`, which filters by file, so an ordinary type error inside a `@ts-strict-ignore` file is discarded: `git grep -l '@bitwarden/sdk-internal' -- 'apps/**' 'bitwarden_license/**'`, then check that file list for `@ts-strict-ignore` with the `Grep` tool rather than piping into `xargs`, which carries no grant of its own.\n   - The root `tsconfig.json` excludes app spec files but not `bitwarden_license` ones, so `git grep -l '@bitwarden/sdk-internal' -- 'apps/**/*.spec.ts'` is type-checked by nothing; the `bitwarden_license` equivalent is still covered by `tsc-strict`.\n   - `git grep -l 'jest.mock(\"@bitwarden/sdk-internal\"'` — factory-form mocks are not checked against the real module and drift silently. `libs/common/src/platform/spec/mock-sdk.service.ts` and `libs/common/spec/jest-sdk-client-factory.ts` are structural and break loudest, the latter on any `PasswordManagerClient` constructor change.\n8. Report under these headings, which are the same across every repo running this evaluation: `## SDK bump evaluated`, `## Compile-time breaks`, `## Runtime considerations` (wire-value and serialization findings go here; omit the heading when there are none), `## Everything else in range — confirmed safe`, `## Commit`. Give each finding its commit, symbol and call sites, and state whether the type check passed inside `## Compile-time breaks` rather than as its own section. Cite sdk-internal commits and PRs as `bitwarden/sdk-internal#<N>` or a full URL — a bare `#<N>` copied from a commit subject auto-links into bitwarden/clients and tags an unrelated PR.\n\n## Resolve\n\n9. Fix anything found, compile-time or runtime, whenever the correct behavior is clear and in scope. For a new required argument or method with no existing consumer, grep the underlying concept rather than the new name, which does not exist yet; with no consumer, stub it — an empty body with a `// no-op` comment, or a default return, never a `throw`, which turns a stub into a crash. A sibling's structure is a template; its behavior is not evidence for yours, so read the SDK to choose between two plausible argument values. Never edit `package.json`: the pin is the bump's output, not the fix's. If unsure, report it instead of guessing, along with anything needing a product decision. Invoke `Skill(writing-client-code)` first if the fix is not purely mechanical, then commit with `Skill(bitwarden-delivery-tools:committing-changes)`.\n10. `npm run lint:fix`, then `npx prettier --write <edited files>`, then `npm run test:types`, then `npm test -- <edited paths> --maxWorkers=2`. Run all four even in CI: `lint.yml`'s `npm run lint` step runs ESLint as well as Prettier, and `--maxWorkers=2` is this repo's workaround for Jest exhausting memory.\n",".claude/skills/fix-angular-fixmes/SKILL.md":"---\nname: fix-angular-fixmes\ndescription: Resolves eslint-disable suppression comments throughout the Bitwarden clients codebase by fixing the underlying issue. Use when the user asks to \"fix FIXMEs\", \"fix eslint suppressions\", \"clean up eslint-disable-next-line\", \"resolve CL-764\", \"resolve CL-903\", \"fix OnPush eslint suppressions\", \"fix Signals eslint suppressions\", or reduce linting suppressions.\nallowed-tools: Read, Write, Edit, Glob, Grep, Bash(npx ng generate:*), Bash(npm run lint:fix), Bash(npm run test)\n---\n\n## Key rules\n\n- Fix the underlying issue — never just delete the suppression comment and leave broken code.\n- Remove the **complete comment block**: FIXME line (if any) + TODO: Skipped block (if any) + eslint-disable-next-line line.\n- Both FIXME-paired and standalone suppressions are the same migration debt.\n- For Angular migration rules, prefer CLI schematics over manual edits.\n- Do NOT convert service observables to signals (ADR-0027).\n- For OnPush and signals patterns, the `angular-modernization` skill is the authoritative source — this skill only owns the ESLint suppression mechanics.\n\n## Step 1: Discover all suppressions\n\nUse the `Grep` tool to find suppressions in the target path:\n\n- Pattern `eslint-disable` — finds all eslint suppressions\n- Pattern `FIXME.*CL-` — finds Angular FIXME-tracked ones specifically\n\nGroup results by rule name. Two forms appear in this codebase:\n\n**Form A — FIXME-paired** (a FIXME tracking comment sits above the disable):\n\n```typescript\n// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush\n// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection\n```\n\n**Form B — Standalone** (disable without a FIXME, or with a CLI skip comment):\n\n```typescript\n// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection\n// TODO: Skipped for signal migration because:\n//  Accessor inputs cannot be migrated as they are too complex.\n// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals\n// eslint-disable-next-line @angular-eslint/prefer-signals\n```\n\nBoth forms must be fixed the same way.\n\n## Rule reference\n\n| Category         | Rule                                                        | Section below                                       |\n| ---------------- | ----------------------------------------------------------- | --------------------------------------------------- |\n| Angular          | `@angular-eslint/prefer-on-push-component-change-detection` | [OnPush](#onpush)                                   |\n| Angular          | `@angular-eslint/prefer-signals`                            | [Signals](#signals)                                 |\n| Angular          | `@angular-eslint/prefer-output-emitter-ref`                 | [Signals](#signals)                                 |\n| Angular template | `@angular-eslint/template/button-has-type`                  | [HTML rules](#html-rules)                           |\n| TypeScript       | `@typescript-eslint/no-floating-promises`                   | [no-floating-promises](#no-floating-promises)       |\n| TypeScript       | `@typescript-eslint/no-unused-vars`                         | [no-unused-vars](#no-unused-vars)                   |\n| TypeScript       | `@typescript-eslint/no-unsafe-function-type`                | [no-unsafe-function-type](#no-unsafe-function-type) |\n| RxJS             | `rxjs/no-async-subscribe`                                   | [rxjs rules](#rxjs-rules)                           |\n| RxJS             | `rxjs-angular/prefer-takeuntil`                             | [rxjs rules](#rxjs-rules)                           |\n| Bitwarden        | `@bitwarden/platform/no-enums`                              | [no-enums](#no-enums)                               |\n| Bitwarden        | `@bitwarden/components/no-bwi-class-usage`                  | [HTML rules](#html-rules)                           |\n| General          | `no-restricted-imports`                                     | [no-restricted-imports](#no-restricted-imports)     |\n| General          | `no-console`                                                | [no-console](#no-console)                           |\n| General          | `no-empty`                                                  | [no-empty](#no-empty)                               |\n| General          | bare `// eslint-disable-next-line`                          | [bare disable](#bare-disable)                       |\n| Tailwind         | `tailwindcss/no-custom-classname`                           | [HTML rules](#html-rules)                           |\n\n## OnPush\n\n**Rule:** `@angular-eslint/prefer-on-push-component-change-detection`\n\nFollow the OnPush guidance in the `angular-modernization` skill (add `changeDetection: ChangeDetectionStrategy.OnPush`, remove `ChangeDetectorRef` if only used for `detectChanges()`). Then remove the FIXME + `eslint-disable-next-line` lines.\n\n> `@Directive` does not support `changeDetection` — skip OnPush for pure directives.\n\n## Signals\n\n**Rules:** `@angular-eslint/prefer-signals`, `@angular-eslint/prefer-output-emitter-ref`\n\nApplies to `@Input()`, `@Output()`, `@ViewChild`, `@ContentChild`.\n\nFollow the Signal Inputs, Outputs, and Queries guidance in the `angular-modernization` skill (prefer CLI schematics, then manual conversion). After each migration, **manually remove** the FIXME and `eslint-disable-next-line` lines, and any `// TODO: Skipped for signal migration because:` comment blocks.\n\n> Do NOT convert service observables to signals — only component-local state and decorator bindings (ADR-0027).\n\n## no-floating-promises\n\n**Rule:** `@typescript-eslint/no-floating-promises`\n\nA returned Promise is not handled. Pick one fix:\n\n```typescript\n// 1. Await it (preferred in async functions)\nawait this.router.navigate([\"/login\"]);\n\n// 2. void — explicit fire-and-forget\nvoid this.router.navigate([\"/login\"]);\n\n// 3. Chain .catch() for explicit error handling\nthis.router.navigate([\"/login\"]).catch((err) => this.logService.error(err));\n```\n\nUse `void` for navigation or toast calls that genuinely don't need awaiting. Use `await` when the result matters or you're already in an async context.\n\n## no-unused-vars\n\n**Rule:** `@typescript-eslint/no-unused-vars`\n\n```typescript\n// Remove unused variable\nconst unused = computeSomething(); // delete this line\n\n// Or prefix with _ if it must be declared (e.g. destructuring)\nconst [_first, second] = array;\n\n// Or suppress a catch variable (TypeScript 4.0+)\ntry { ... } catch { ... } // omit the variable entirely\n```\n\n## no-unsafe-function-type\n\n**Rule:** `@typescript-eslint/no-unsafe-function-type`\n\nReplace the generic `Function` type with a specific signature:\n\n**Before**\n\n```typescript\nprivate callback: Function;\n```\n\n**After** — use the actual signature\n\n```typescript\nprivate callback: () => void;\n// or for unknown signatures:\nprivate callback: (...args: unknown[]) => unknown;\n```\n\n## RxJS rules\n\n**Rules:** `rxjs/no-async-subscribe`, `rxjs-angular/prefer-takeuntil`\n\n**`rxjs/no-async-subscribe`** — async callback inside `.subscribe()` swallows errors:\n\n**Before**\n\n```typescript\nthis.service.data$.subscribe(async (value) => {\n  await this.process(value);\n});\n```\n\n**After** — move async work into the pipe\n\n```typescript\nthis.service.data$\n  .pipe(\n    switchMap((value) => this.process(value)),\n    takeUntilDestroyed(),\n  )\n  .subscribe();\n```\n\n**`rxjs-angular/prefer-takeuntil`** — subscription without cleanup:\n\n**Before**\n\n```typescript\nthis.service.data$.subscribe((value) => {\n  this.data = value;\n});\n```\n\n**After** — add `takeUntilDestroyed()` (call in constructor or use `destroyRef`)\n\n```typescript\nconstructor() {\n  this.service.data$\n    .pipe(takeUntilDestroyed())\n    .subscribe((value) => { this.data = value; });\n}\n```\n\n## no-enums\n\n**Rule:** `@bitwarden/platform/no-enums`\n\nConvert TypeScript enums to const objects with type aliases (ADR-0025):\n\n**Before**\n\n```typescript\nenum CipherType {\n  Login = 1,\n  SecureNote = 2,\n}\n```\n\n**After**\n\n```typescript\nexport const CipherType = Object.freeze({ Login: 1, SecureNote: 2 } as const);\nexport type CipherType = (typeof CipherType)[keyof typeof CipherType];\n```\n\nUpdate all import sites — the usage (`CipherType.Login`) stays the same.\n\n## no-restricted-imports\n\n**Rule:** `no-restricted-imports`\n\nThe import is from a path that the ESLint config forbids. Steps:\n\n1. Read the context around the import to understand what is being imported.\n2. Check `eslint.config.mjs` at the repo root (or the nearest config) for the `no-restricted-imports` rule to find the allowed alternative path.\n3. Replace the import with the allowed path and remove the suppression.\n\nCommon cases: importing platform-internal modules directly instead of through the public API, or test-only helpers in non-test files.\n\n## no-console\n\n**Rule:** `no-console`\n\n```typescript\n// Remove debug statements\nconsole.log(\"debug\"); // delete\n\n// Replace with the application logging service\nthis.logService.error(\"Something failed\", error);\n```\n\nIn test files (`*.spec.ts`), a `console.error` or `console.warn` spy may be intentional — in that case, set up the spy properly rather than suppressing:\n\n```typescript\njest.spyOn(console, \"error\").mockImplementation(() => {});\n```\n\n## no-empty\n\n**Rule:** `no-empty`\n\nEmpty `catch` blocks silently swallow errors:\n\n**Before**\n\n```typescript\ntry {\n  await something();\n  // eslint-disable-next-line no-empty\n} catch {}\n```\n\n**After** — handle or log the error\n\n```typescript\ntry {\n  await something();\n} catch (e) {\n  // Intentionally ignored — operation is best-effort\n}\n\n// Or log it\ntry {\n  await something();\n} catch (e) {\n  this.logService.warning(\"Operation failed\", e);\n}\n```\n\n## Bare disable\n\n**Rule:** bare `// eslint-disable-next-line` (no rule specified)\n\nThis disables ALL rules for the next line, which is always wrong. Steps:\n\n1. Remove the suppression and run `npm run lint:fix` to see which specific rule triggers.\n2. Fix the underlying issue using the appropriate section above.\n3. If the violation truly cannot be fixed (rare), replace the bare disable with a specific named rule.\n\n## HTML rules\n\n**`@angular-eslint/template/button-has-type`** — Add an explicit type to every `<button>`:\n\n**Before**\n\n```html\n<button (click)=\"save()\">Save</button>\n```\n\n**After**\n\n```html\n<button type=\"button\" (click)=\"save()\">Save</button>\n<!-- or type=\"submit\" inside a <form> -->\n```\n\n**`@bitwarden/components/no-bwi-class-usage`** — Replace raw `bwi-*` icon classes with the `<bit-icon>` component or the appropriate icon token.\n\n**`tailwindcss/no-custom-classname`** — Use a Tailwind utility class with the `tw-` prefix, or register the class in the Tailwind safelist. Never use arbitrary custom class names.\n\n## Step 2: Cleanup checklist per fixed instance\n\n- [ ] `// FIXME(…)` line removed (if present)\n- [ ] `// TODO: Skipped for signal migration because: …` block removed (all lines, if present)\n- [ ] `// eslint-disable-next-line …` line removed\n- [ ] Unused imports removed; new imports added as needed\n- [ ] All in-class usages updated (e.g. signal reads need `()`)\n\n## Step 3: Validate\n\n```bash\nnpm run lint:fix\n```\n\nFix any errors that remain. Run `npm run test` if behaviour-critical code was changed.\n",".claude/skills/cipher-type-planner/SKILL.md":"---\nname: cipher-type-planner\ndescription: Plans the creation or modification of a cipher type (vault item type) across the Bitwarden clients monorepo. Use this skill when a user wants to add a new cipher type, modify an existing cipher type, or asks about what is needed to implement a cipher type. DO NOT invoke for general vault or cipher questions unrelated to adding or changing a cipher type.\nuser-invocable: true\nargument-hint: \"target-client\"\n---\n\n# Cipher Type Planner\n\n## Workflow\n\n### Step 1: Gather Requirements\n\nAsk the user the following questions (use `AskUserQuestion`). Adapt questions based on what\nthe user has already provided.\n\n**Required questions:**\n\n1.  **Type name and value** - What is the cipher type name and integer value? If the user hasn't specified a value, determine the next available integer by reading the `CipherType` enum definition.\n2.  **Fields** - What are the cipher's properties? Each property must contain:\n    - Field name\n    - Data type (string, number, boolean)\n    - Encryption required?\n    - Required\n3.  **Target client** (`$0`) - Which client should this plan focus on? (`web`, `desktop`, `browser`, `cli`, or `all`). Shared library changes (`libs/common`, `libs/vault`) are always included; `$0` controls which `apps/*` files appear. **Skip if already provided as an argument.** Default: `all`.\n4.  **Autofill** - Should this type participate in browser autofill? (Currently only Login, Card,\n    and Identity support autofill.) Only ask if `$0` is `browser` or `all`.\n5.  **Linked fields** - Should this type support linked custom fields? If yes, which properties\n    should be linkable?\n6.  **Feature flag** - What is the feature flag name?\n7.  **Prerequisites** - Have all server and SDK prerequisites been completed? **Do not proceed\n    with the plan until the user confirms these are done.**\n\n**Additional questions:**\n\nAsk each of the following. If the engineer does not have an answer, accept \"N/A\" or \"not yet decided\" and note it as a gap in the plan.\n\n- **Import/export** - Should import/export support be included in this plan?\n- **UI details** - Are there specific UI requirements for the form or view sections (e.g., dropdowns, masked fields, copy buttons)?\n- **Subtitle** - What value should the `subTitle` getter on the view model return? This appears in vault list items.\n- **Icon** - What icon represents this type in the vault? Bitwarden uses `bwi-` icon classes.\n- **Organization policy** - Does this type appear in the restricted item types policy UI?\n\n### Step 2: Enter Plan Mode\n\nAfter gathering requirements, enter plan mode using `EnterPlanMode`. Explore the codebase to\nverify current patterns and file locations. Use the SshKey cipher type (value 5) as the canonical\nreference for implementation patterns.\n\nKey files to inspect for patterns:\n\n- `libs/common/src/vault/enums/cipher-type.ts` - Enum definition\n- `libs/common/src/vault/models/api/ssh-key.api.ts` - API model pattern\n- `libs/common/src/vault/models/data/ssh-key.data.ts` - Data model pattern\n- `libs/common/src/vault/models/domain/ssh-key.ts` - Domain model pattern\n- `libs/common/src/vault/models/view/ssh-key.view.ts` - View model pattern\n- `libs/common/src/models/export/ssh-key.export.ts` - Export model pattern\n- `libs/common/src/vault/models/domain/cipher.ts` - Container switch patterns\n- `libs/vault/src/cipher-form/components/sshkey-section/` - Form component pattern\n- `libs/vault/src/cipher-view/sshkey-sections/` - View component pattern\n\n### Step 3: Build the Plan\n\nWrite a comprehensive plan to the plan file. The plan MUST include all sections below.\n\n---\n\n## Plan Output Format\n\n### 1. Overview\n\n- **Cipher type name:**\n- **Integer value:**\n- **Feature flag:**\n- **Minimum client version:**\n- **Fields:** Table of all fields with name, type, encrypted (yes/no), required (yes/no)\n- **Supports autofill:** Yes/No\n- **Supports linked fields:** Yes/No\n\n### 2. Clients - New Files to Create\n\nList every file that needs to be created, with the full path and a brief description. Organize by\nlayer:\n\n**Model stack:**\n\n- `libs/common/src/vault/models/api/<type>.api.ts` - API response shape\n- `libs/common/src/vault/models/data/<type>.data.ts` - Serializable storage format\n- `libs/common/src/vault/models/domain/<type>.ts` - Encrypted business object\n- `libs/common/src/vault/models/domain/<type>.spec.ts` - Domain model tests\n- `libs/common/src/vault/models/view/<type>.view.ts` - Decrypted view for UI\n\n**Export (if import/export is included):**\n\n- `libs/common/src/models/export/<type>.export.ts` - Export model\n\n**UI components:**\n\n- `libs/vault/src/cipher-form/components/<type>-section/` - Form section component (TS, HTML, spec)\n- `libs/vault/src/cipher-view/<type>-sections/` - View section component (TS, HTML)\n\n### 3. Clients - Existing Files to Modify\n\nList every file that needs modification, organized by concern. For each file, describe the specific\nchange needed.\n\n**Core enum:**\n\n- `libs/common/src/vault/enums/cipher-type.ts` - Add `<Type>: <N>` to `CipherType`\n- `libs/common/src/vault/enums/cipher-type.spec.ts` - Update tests\n\n**Container switches (add case for new type):**\n\n- `libs/common/src/vault/models/data/cipher.data.ts` - Constructor\n- `libs/common/src/vault/models/domain/cipher.ts` - Constructor, `decrypt()`,\n  `toCipherData()`, `fromJSON()`, `toSdkCipher()`, `fromSdkCipher()`\n- `libs/common/src/vault/models/view/cipher.view.ts` - `item` getter, `fromJSON()`,\n  `fromSdkCipherView()`, `getSdkCipherViewType()`, `toSdkCipherView()`\n- `libs/common/src/vault/models/request/cipher.request.ts` - Constructor\n- `libs/common/src/vault/models/response/cipher.response.ts` - Constructor\n- `libs/common/src/vault/services/cipher.service.ts` - `encryptCipherData()`\n\n**Export (if import/export is included):**\n\n- `libs/common/src/models/export/cipher.export.ts` - `toView()`, `toDomain()`, `build()`\n\n**SDK integration:**\n\n- `libs/common/src/vault/models/domain/cipher-sdk-mapper.ts` - Record mapper\n- Domain and view model SDK methods (`toSdk*`/`fromSdk*`)\n\n**UI wiring:**\n\n- `libs/vault/src/cipher-form/components/cipher-form.component.ts` - Import and wire section\n- `libs/vault/src/cipher-form/components/cipher-form.component.html` - Add section template\n- `libs/vault/src/cipher-view/cipher-view.component.ts` - Import and wire section\n- `libs/vault/src/cipher-view/cipher-view.component.html` - Add section template\n- `libs/common/src/vault/icon/build-cipher-icon.ts` - Add icon case\n\n**Vault filters (CRITICAL — without these, ciphers won't appear in the vault list):**\n\nAll vault filter files must be feature-flag-gated so the new type only appears when the flag is\nenabled. Use `ConfigService.getFeatureFlag$()` with `combineLatest` to filter the type out of\narrays when the flag is off.\n\n_Always included (shared):_\n\n- `libs/vault/src/services/vault-filter.service.ts` - **CRITICAL**: Add type to `buildCipherTypeTree()` `allTypeFilters` array. Without this, ciphers of the new type will not appear in the vault sidebar or list.\n- `libs/vault/src/models/filter-function.ts` - Add filter case for the new type\n- `libs/angular/src/vault/components/vault-items.component.ts` - Feature-flag-gate empty state type buttons\n\n_Include if `$0` is `web` or `all`:_\n\n- `apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts` - Add to `allTypeFilters`, `searchPlaceholder`, and feature-flag-gate in `buildAllFilters()`\n- `apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts` - Feature-flag-gate in `buildAllFilters()`\n\n_Include if `$0` is `desktop` or `all`:_\n\n- `apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts` - Add `ConfigService`, `combineLatest` with feature flag\n\n_Include if `$0` is `browser` or `all`:_\n\n- `apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts` - Add `ConfigService`, feature-flag-gate `cipherTypes`\n\n**New item menus (feature-flag-gated):**\n\n_Always included (shared):_\n\n- `libs/common/src/vault/types/cipher-menu-items.ts` - Add menu item entry for new type\n- `libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts` - Add `canCreate<Type> = input(false)` signal, gate in `cipherMenuItems` observable\n\n_Include if `$0` is `web` or `all`:_\n\n- `apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts` - Add `canCreate<Type>$` observable from feature flag\n- `apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.html` - Bind `[canCreate<Type>]` to `<vault-new-cipher-menu>`\n\n_Include if `$0` is `browser` or `all`:_\n\n- `apps/browser/src/vault/popup/components/vault/new-item-dropdown/new-item-dropdown.component.ts` - Add `ConfigService`, `combineLatest` with feature flag\n\n**Localization (add i18n keys):**\n\n_Include only locale files for `$0`. If `$0` is `all`, include all three:_\n\n- `apps/web/src/locales/en/messages.json` _(web)_\n- `apps/desktop/src/locales/en/messages.json` _(desktop)_\n- `apps/browser/src/_locales/en/messages.json` _(browser)_\n\n**Linked fields (if applicable):**\n\n- `libs/common/src/vault/enums/linked-id-type.enum.ts`\n\n**Autofill (if applicable — only if `$0` is `browser` or `all`):**\n\n- List relevant autofill files from `apps/browser/src/autofill/` only if the type supports\n  autofill\n\n**Restricted item types (if applicable):**\n\nRestricted item type enforcement is used across all clients. Include files for `$0`:\n\n_Always included (shared):_\n\n- `libs/common/src/vault/services/vault-settings/vault-settings.service.ts` - Restricted types service\n\n_Include if `$0` is `web` or `all`:_\n\n- `apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.ts` - Policy configuration UI\n- `apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.html`\n\n_Include if `$0` is `browser` or `all`:_\n\n- `apps/browser/src/vault/popup/components/vault/item-more-options/item-more-options.component.ts` - Restricted type checks\n\n_Include if `$0` is `cli` or `all`:_\n\n- `apps/cli/src/vault/create.command.ts` - Restricted type checks\n- `apps/cli/src/commands/list.command.ts` - Restricted type checks\n- `apps/cli/src/commands/get.command.ts` - Restricted type checks\n\n### 4. Localization Keys\n\nList all i18n keys that need to be added. At minimum:\n\n- Type label\n- Field labels for each type-specific field\n\n### 5. Tests\n\nList all test files that need to be created or updated:\n\n- `libs/common/src/vault/enums/cipher-type.spec.ts`\n- `libs/common/src/vault/models/domain/cipher.spec.ts`\n- `libs/common/src/vault/models/domain/<type>.spec.ts` (new)\n- `libs/common/src/vault/models/view/cipher.view.spec.ts`\n- `libs/common/src/vault/services/cipher.service.spec.ts`\n- `libs/common/src/vault/services/cipher-sdk.service.spec.ts`\n- `libs/common/src/vault/icon/build-cipher-icon.spec.ts`\n- `libs/common/src/models/export/cipher.export.spec.ts`\n- `libs/vault/src/cipher-form/components/cipher-form.component.spec.ts`\n- `libs/vault/src/cipher-view/cipher-view.component.spec.ts`\n- Form section component spec (new)\n\n### 6. Recommended Implementation Order\n\nRecommended implementation order, customized for this specific type. Only include steps relevant\nto `$0` (shared steps are always included):\n\n1. Core enum addition _(shared)_\n2. Feature flag registration _(shared)_\n3. Model stack (API, Data, Domain, View, Export if applicable) _(shared)_\n4. Container switch updates _(shared)_\n5. SDK bindings (`toSdk*`/`fromSdk*`) _(shared)_\n6. Localization keys _(`$0`)_\n7. Shared UI (icon, menu items) _(shared)_\n8. Vault filters with feature flag gating — CRITICAL for ciphers to appear _(shared + `$0`)_\n9. New item menus with feature flag gating _(shared + `$0`)_\n10. Per-app UI (form section, view section) _(shared)_\n11. Context menu / copy actions — see Section 8 _(shared + `$0`)_\n12. CLI _(only if `$0` is `cli` or `all`)_\n13. Autofill _(only if `$0` is `browser` or `all`)_\n14. Tests _(shared + `$0`)_\n\n### 7. Risks and Considerations\n\n- Cross-repo coordination requirements\n- Feature flag rollout strategy\n- Backward compatibility concerns\n- Any fields that need special encryption handling (reminder: no new encryption logic in clients)\n- Performance considerations for large vaults\n- **i18n key reuse** - Before adding new locale keys, check whether existing keys already have the\n  desired display value. If an existing key has the same message text, reuse it instead of creating\n  a duplicate. Only create new keys when no existing key matches.\n\n### 8. Context Menu / Copy Actions\n\nEach cipher type can expose copiable fields in the vault list item context menus (right-click / more\nmenu). Include only the sections relevant to `$0`.\n\n#### Core Infrastructure (always included)\n\n| File                                                    | What to add                                                                                                                                                                                                                                     |\n| ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `libs/vault/src/services/copy-cipher-field.service.ts`  | Add field names to the `CopyAction` type union. Add entries to the `CopyActions` record with `typeI18nKey` (i18n key for the toast message), `protected` (whether it requires password re-prompt), and optional `event` (for event collection). |\n| `libs/common/src/vault/utils/cipher-view-like-utils.ts` | Add cases to `hasCopyableValue()` that check whether the cipher has a non-empty value for each copiable field.                                                                                                                                  |\n\n#### Browser (include if `$0` is `browser` or `all`)\n\n| File                                                                                              | What to add                                                                                                                                                              |\n| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `apps/browser/src/vault/popup/components/vault/item-copy-action/item-copy-actions.component.ts`   | Add a `singleCopyable<Type>` getter (for single-field quick copy button), a `has<Type>Values` getter, and a `getNumberOf<Type>Values()` method. Follow the Card pattern. |\n| `apps/browser/src/vault/popup/components/vault/item-copy-action/item-copy-actions.component.html` | Add a section using `@if` syntax (NOT `*ngIf`) with the single/multi field pattern.                                                                                      |\n\n#### Web (include if `$0` is `web` or `all`)\n\n| File                                                                            | What to add                                                                                                               |\n| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |\n| `apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts`   | Add `is<Type>Cipher` and `hasVisible<Type>Options` getters. Add `hasVisible<Type>Options` to the `showMenuDivider` check. |\n| `apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html` | Add copy buttons using `@if` syntax with `appCopyField` directive.                                                        |\n\n#### Desktop (include if `$0` is `desktop` or `all`)\n\n| File                                                                            | What to add                                                                                                                                                                              |\n| ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `apps/desktop/src/vault/app/vault-v3/vault-items/vault-cipher-row.component.ts` | Add a `CipherType.<Type>` case to the `copyFields` computed signal, returning `CopyFieldConfig[]` entries. This is the most modern pattern — uses a computed signal rather than getters. |\n\n#### Critical Warnings\n\n- **CLI has no copy menu UI** — do not add copy-related i18n keys to the CLI locale.\n- **Only expose fields that should be copiable** — not every cipher field needs a copy action. Check\n  with product requirements for which fields get copy buttons.\n",".claude/CLAUDE.md":"# Bitwarden Clients - Claude Code Configuration\n\n## Project Context Files\n\n**Read these files before reviewing to ensure that you fully understand the project and contributing guidelines**\n\n1. `README.md` (read on demand for project overview)\n2. `CONTRIBUTING.md` (read on demand for contributing guidelines)\n3. @../.github/PULL_REQUEST_TEMPLATE.md\n\n## Critical Rules\n\n- **NEVER** use code regions: If complexity suggests regions, refactor for better readability\n- **CRITICAL**: new encryption logic should not be added to this repo.\n  - If significant encryption related logic is added or changed, make sure @bitwarden/team-key-management-dev is aware of the PR\n- **NEVER** send unencrypted vault data to API services\n- **NEVER** commit secrets, credentials, or sensitive information.\n- **NEVER** log decrypted data, encryption keys, or PII\n  - No vault data in error messages or console logs\n- **ALWAYS** Respect configuration files at the root and within each app/library (e.g., `eslint.config.mjs`, `jest.config.js`, `tsconfig.json`).\n- **CRITICAL**: Tailwind CSS classes MUST use the `tw-` prefix (e.g., `tw-flex`, `tw-p-4`). Missing prefix means the class is ignored and styling silently breaks. See [.claude/rules/tailwind.md](./rules/tailwind.md) for additional Tailwind rules.\n\n## Mono-Repo Architecture\n\nThis repository is organized as a **monorepo** containing multiple applications and libraries. The\nmain directories are:\n\n- `apps/<client>/` — single-client code (browser, cli, desktop, web). Each app is self-contained.\n- `libs/common/` — code shared across **all** clients, including non-Angular (CLI). No Angular APIs here: no `@Injectable`, no `inject()`, no decorators, no template references.\n- `libs/angular/` — code shared across Angular clients (browser/desktop/web). Angular APIs allowed.\n- Other `libs/*` (e.g. `ui`, `platform`, `key-management`, `vault`) — domain-scoped, follow the same Angular / non-Angular split based on which clients consume them.\n\nWhen adding new code, place it as deep and as narrow as possible. Promote to a shared `libs/` only when a second client needs it.\n\n**Strict boundaries** must be maintained between apps and libraries. Do not introduce\ncross-dependencies that violate the intended modular structure. Always consult and respect the\ndependency rules defined in `eslint.config.mjs`, `nx.json`, and other configuration files.\n\n## Verifying Changes\n\nAfter modifying code, run:\n\n- `npm run lint:fix` — applies ESLint fixes\n- `npm run prettier` — formats with Prettier\n- `npm run test:types` — type-checks the workspace\n- `npm test` — runs Jest. Scope with `npm test -- <path-or-pattern>` when changes are localized.\n\nIf any of these fail, fix the underlying issue before reporting the task complete. Do not skip hooks or bypass failures.\n\n## References\n\n- [Web Clients Architecture](https://contributing.bitwarden.com/architecture/clients)\n- [Architectural Decision Records (ADRs)](https://contributing.bitwarden.com/architecture/adr/)\n- [Contributing Guide](https://contributing.bitwarden.com/)\n- [Web Clients Setup Guide](https://contributing.bitwarden.com/getting-started/clients/)\n- [Code Style](https://contributing.bitwarden.com/contributing/code-style/)\n- [Security Whitepaper](https://bitwarden.com/help/bitwarden-security-white-paper/)\n- [Security Definitions](https://contributing.bitwarden.com/architecture/security/definitions)\n",".claude/skills/writing-client-code/SKILL.md":"---\nname: writing-client-code\ndescription: Bitwarden client code conventions for Angular and TypeScript. Use when creating components, services, or modifying web/browser/desktop apps.\n---\n\n### Why `libs/common` cannot import Angular\n\nCLI is a first-class client. Any code in `libs/common` must work without Angular's dependency injection, decorators, or lifecycle hooks. This is why cross-client services use abstract classes as interfaces — the concrete implementations (`Default*`, `Web*`, `Browser*`, `Desktop*`, `Cli*`) live in their respective apps.\n\n## Architectural Rationale\n\n### Thin components\n\nComponents contain only view logic. Business logic belongs in services. This keeps components testable, reusable, and prevents Angular lifecycle coupling from leaking into domain logic.\n\n### Composition over inheritance\n\nAvoid extending components across clients. Compose using shared child components instead. Inheritance creates tight coupling between client-specific UI and shared behavior — when one client's needs diverge, inherited components become hard to change safely.\n\n### Don't modernize existing code unless asked\n\nThe codebase contains both legacy and modern Angular patterns. When modifying an existing file, **follow the patterns already in that file**. Don't migrate any of these unless explicitly asked:\n\n- `*ngIf` → `@if`, `*ngFor` → `@for`\n- `@Input()` / `@Output()` → `input()` / `output()` signals\n- Constructor injection → `inject()`\n- Default change detection → `OnPush`\n- NgModule declarations → standalone components\n\nIf asked to modernize, follow this order (per the Angular migration guide): standalone → control flow → input/output signals → view queries → signals → computed → OnPush (last, only after full signal migration).\n\n### State management: Signals vs RxJS\n\n- **Component local state and Angular-only services:** Use Signals\n- **Cross-client services (`libs/common`):** Use RxJS (because CLI has no Angular Signals support)\n\nAvoid manual subscriptions. Prefer `| async` pipe. When subscriptions are necessary, pipe through `takeUntilDestroyed()` — enforced by the `prefer-takeUntil` lint rule.\n\n### No TypeScript enums (ADR-0025)\n\nUse frozen const objects with `Object.freeze()` and `as const`, plus a companion type alias. Enums have runtime behavior that creates subtle bugs with tree-shaking.\n\n## Critical Rules for New Code\n\nThese rules apply **strictly to new files and components**. For existing code, follow the patterns already in the file.\n\n- **New components must use `ChangeDetectionStrategy.OnPush`** and be `standalone: true`. `NgModules` are permitted only for grouping related standalone components\n- **Prefer `inject()` function** for DI in Angular primitives (components, pipes, directives). Use constructor injection for code shared with non-Angular clients (CLI)\n- **New templates must use control flow syntax** (`@if`, `@for`, `@switch`), not structural directives\n- **Use `host` property** in component decorators, not `@HostBinding` / `@HostListener`\n- **Use Reactive Forms exclusively** — not template-driven forms\n- **File naming:** `kebab-case.component.ts`, `.service.ts`, `.pipe.ts`, `.directive.ts`. Also: `.request.ts`, `.response.ts`, `.view.ts`, `.data.ts` for models (ADR-0012)\n- **All Tailwind classes require `tw-` prefix** — `tw-flex`, `tw-mt-2`, not `flex`, `mt-2`\n- **Testing with Jest** — use `jest-mock-extended` for mocking services. `describe`/`it` blocks, not `test()`\n- **Imports from `@bitwarden/common`** must not pull in Angular-specific code (breaks CLI)\n\n## Examples\n\n### Dependency injection (new Angular code)\n\n```typescript\n// CORRECT — inject() for Angular primitives\nexport class VaultComponent {\n  private vaultService = inject(VaultService);\n}\n\n// ALSO CORRECT — constructor injection for code shared with CLI\nexport class CryptoService {\n  constructor(private stateService: StateService) {}\n}\n```\n\n### Tailwind prefix\n\n```html\n<!-- CORRECT -->\n<div class=\"tw-flex tw-gap-2 tw-mt-4\">\n  <!-- WRONG — missing tw- prefix, will be stripped -->\n  <div class=\"flex gap-2 mt-4\"></div>\n</div>\n```\n\n### Const objects over enums (ADR-0025)\n\n```typescript\n// CORRECT — with companion type alias\nexport const CipherType = Object.freeze({\n  Login: 1,\n  SecureNote: 2,\n} as const);\nexport type CipherType = (typeof CipherType)[keyof typeof CipherType];\n\n// WRONG — TypeScript enums have runtime side effects\nexport enum CipherType {\n  Login = 1,\n  SecureNote = 2,\n}\n```\n\n## Further Reading\n\n- [Angular conventions](https://contributing.bitwarden.com/contributing/code-style/web/angular)\n- [Angular migration guide](https://contributing.bitwarden.com/contributing/code-style/web/angular-migration-guide)\n- [TypeScript style](https://contributing.bitwarden.com/contributing/code-style/typescript/)\n- [Component Library](https://contributing.bitwarden.com/contributing/ui/component-library/)\n",".claude/rules/autofill-content-scripts.md":"---\npaths:\n  - \"apps/browser/src/autofill/content/**\"\n---\n\n# Autofill Content Scripts - Critical Rules\n\n## Angular Exception\n\nCode in this directory runs as **injected content scripts**, not within the Angular application context.\n\n- **DO NOT** use Angular patterns (components, services, dependency injection, RxJS Observable Data Services, Signals, `takeUntilDestroyed()`, `async` pipe, etc.)\n- **DO NOT** import Angular modules or dependencies\n- The Angular patterns defined in [angular.md](./angular.md) and [angular-components.md](./angular-components.md), and the Observable Data Services pattern in [typescript.md](./typescript.md), do not apply here\n\n## Lit Components\n\n- Components in `content/components/` use [Lit](https://lit.dev/), not Angular\n- Lit components should consume shared concepts (constants, values) from the component library when possible, without introducing Angular or other dependency entanglements\n\n## Browser API Usage\n\n- Content scripts cannot import the `BrowserApi` abstraction required elsewhere in the browser extension\n- Direct use of `chrome.*` / `browser.*` APIs (e.g., `chrome.runtime.sendMessage`) is expected here\n- This is an exception to the `BrowserApi` rule in the parent `apps/browser/CLAUDE.md`\n\n## Cross-Browser Compatibility\n\n- Ensure injected code is compatible across browser clients, particularly Firefox, Chrome, and Safari\n- Use polyfills and vendor-specific implementations where needed\n\n## Memory Management\n\n- Ensure proper teardown of listeners, observers, and other concerns that might linger in memory\n- Content scripts do not benefit from Angular's lifecycle hooks — cleanup must be handled manually\n",".claude/rules/angular.md":"---\npaths:\n  - \"**/*.ts\"\n---\n\n# Angular Patterns\n\n> **Scope:** these rules apply **only to Angular code** — files in `apps/browser`, `apps/desktop`, `apps/web`, `libs/angular`, and any other library consumed by Angular clients. **Skip them in non-Angular TypeScript**: `libs/common`, `apps/cli`, SDK code, and anywhere `@Injectable` and Angular APIs are forbidden by the monorepo rules in the root [CLAUDE.md](../CLAUDE.md).\n\nDistilled from [Web Code Style — Angular](https://contributing.bitwarden.com/contributing/code-style/web/angular). Component-only patterns (templates, OnPush, signals, observable subscriptions, etc.) live in [angular-components.md](./angular-components.md); TypeScript-wide patterns live in [typescript.md](./typescript.md).\n\n## Dependency Injection\n\nUse `inject()` — not constructor injection. Constructor injection stays only in code shared with non-Angular clients (CLI).\n\n```typescript\nprivate folderService = inject(FolderService);\n```\n\nWhen declaring providers (component decorator, route config, module), wrap them in `safeProvider()` from `@bitwarden/ui-common`. It adds compile-time checks that the implementation matches the abstraction and that the `deps` array matches the constructor — raw Angular provider objects don't.\n\n```typescript\nimport { safeProvider } from \"@bitwarden/ui-common\";\n\nproviders: [\n  safeProvider({\n    provide: FolderService,\n    useClass: DefaultFolderService,\n    deps: [CipherService, I18nService],\n  }),\n],\n```\n\n### Injection tokens\n\nGive every `SafeInjectionToken<T>` a specific `T` — never `Type<unknown>`, `unknown` or `any`. For a component token, declare an interface or type for the inputs the consumer binds and use `Type<ThatShape>`.\n\n```typescript\nexport interface VaultGatedCollectionBanner {\n  readonly organizationId: InputSignal<OrganizationId | undefined>;\n}\n\nexport const VAULT_GATED_COLLECTION_BANNER = new SafeInjectionToken<\n  Type<VaultGatedCollectionBanner>\n>(\"VaultGatedCollectionBanner\");\n```\n",".claude/rules/flatpak-manifest.md":"# Rules Skill\n\nPath: `.claude/rules/flatpak-manifest.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/flatpak-manifest.md)",".claude/rules/i18n.md":"# Rules Skill\n\nPath: `.claude/rules/i18n.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/i18n.md)",".claude/rules/snap-permissions.md":"# Rules Skill\n\nPath: `.claude/rules/snap-permissions.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/snap-permissions.md)",".claude/rules/storybook-routing.md":"# Rules Skill\n\nPath: `.claude/rules/storybook-routing.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/storybook-routing.md)",".claude/rules/tailwind.md":"# Rules Skill\n\nPath: `.claude/rules/tailwind.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/tailwind.md)",".claude/rules/testing.md":"# Rules Skill\n\nPath: `.claude/rules/testing.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/testing.md)",".claude/rules/typescript.md":"# Rules Skill\n\nPath: `.claude/rules/typescript.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/typescript.md)","libs/tools/README.md":"# tools Documentation\n\nPath: `libs/tools/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/README.md)",".claude/skills/angular-modernization/migration-patterns.md":"# Angular-modernization Skill\n\nPath: `.claude/skills/angular-modernization/migration-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/angular-modernization/migration-patterns.md)",".claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs":"# Claude Directive: sdk-surface-diff.mjs\n\nPath: `.claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs)","libs/tools/export-vault-core/README.md":"# export-vault-core Documentation\n\nPath: `libs/tools/export-vault-core/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/README.md)","libs/tools/export-vault-core/package.json":"# Export-vault-core Skill\n\nPath: `libs/tools/export-vault-core/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/package.json)","libs/tools/export-vault-core/project.json":"# Export-vault-core Skill\n\nPath: `libs/tools/export-vault-core/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/project.json)","libs/tools/export-vault-core/tsconfig.json":"# Export-vault-core Skill\n\nPath: `libs/tools/export-vault-core/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/tsconfig.json)","libs/tools/export-vault-core/tsconfig.spec.json":"# Export-vault-core Skill\n\nPath: `libs/tools/export-vault-core/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/tsconfig.spec.json)","libs/tools/export-vault-ui/README.md":"# export-vault-ui Documentation\n\nPath: `libs/tools/export-vault-ui/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/README.md)","libs/tools/export-vault-ui/package.json":"# Export-vault-ui Skill\n\nPath: `libs/tools/export-vault-ui/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/package.json)","libs/tools/export-vault-ui/project.json":"# Export-vault-ui Skill\n\nPath: `libs/tools/export-vault-ui/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/project.json)","libs/tools/export-vault-ui/tsconfig.json":"# Export-vault-ui Skill\n\nPath: `libs/tools/export-vault-ui/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/tsconfig.json)","libs/tools/export-vault-ui/tsconfig.spec.json":"# Export-vault-ui Skill\n\nPath: `libs/tools/export-vault-ui/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/tsconfig.spec.json)","libs/tools/send/README.md":"# send Documentation\n\nPath: `libs/tools/send/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/README.md)","libs/tools/share/README.md":"# share Documentation\n\nPath: `libs/tools/share/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/README.md)","libs/tools/share/package.json":"# Share Skill\n\nPath: `libs/tools/share/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/package.json)","libs/tools/share/project.json":"# Share Skill\n\nPath: `libs/tools/share/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/project.json)","libs/tools/share/tsconfig.eslint.json":"# Share Skill\n\nPath: `libs/tools/share/tsconfig.eslint.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/tsconfig.eslint.json)","libs/tools/share/tsconfig.json":"# Share Skill\n\nPath: `libs/tools/share/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/tsconfig.json)","libs/tools/share/tsconfig.spec.json":"# Share Skill\n\nPath: `libs/tools/share/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/tsconfig.spec.json)",".claude/skills/create-hec-event-integration/example/token-auth.md":"# Example Skill\n\nPath: `.claude/skills/create-hec-event-integration/example/token-auth.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/create-hec-event-integration/example/token-auth.md)",".claude/skills/figma-to-angular/evals/evals.json":"# Evals Skill\n\nPath: `.claude/skills/figma-to-angular/evals/evals.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/figma-to-angular/evals/evals.json)","libs/tools/generator/components/package.json":"# Components Skill\n\nPath: `libs/tools/generator/components/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/package.json)","libs/tools/generator/components/project.json":"# Components Skill\n\nPath: `libs/tools/generator/components/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/project.json)","libs/tools/generator/components/readme.md":"# components Documentation\n\nPath: `libs/tools/generator/components/readme.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/readme.md)","libs/tools/generator/components/tsconfig.json":"# Components Skill\n\nPath: `libs/tools/generator/components/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/tsconfig.json)","libs/tools/generator/components/tsconfig.spec.json":"# Components Skill\n\nPath: `libs/tools/generator/components/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/tsconfig.spec.json)","libs/tools/generator/core/package.json":"# Core Skill\n\nPath: `libs/tools/generator/core/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/core/package.json)","libs/tools/generator/core/project.json":"# Core Skill\n\nPath: `libs/tools/generator/core/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/core/project.json)","libs/tools/generator/core/tsconfig.json":"# Core Skill\n\nPath: `libs/tools/generator/core/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/core/tsconfig.json)","libs/tools/generator/core/tsconfig.spec.json":"# Core Skill\n\nPath: `libs/tools/generator/core/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/core/tsconfig.spec.json)","libs/tools/send/send-ui/package.json":"# Send-ui Skill\n\nPath: `libs/tools/send/send-ui/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/send-ui/package.json)","libs/tools/send/send-ui/project.json":"# Send-ui Skill\n\nPath: `libs/tools/send/send-ui/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/send-ui/project.json)","libs/tools/send/send-ui/tsconfig.json":"# Send-ui Skill\n\nPath: `libs/tools/send/send-ui/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/send-ui/tsconfig.json)","libs/tools/send/send-ui/tsconfig.spec.json":"# Send-ui Skill\n\nPath: `libs/tools/send/send-ui/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/send-ui/tsconfig.spec.json)","libs/common/src/tools/integration/README.md":"# integration Documentation\n\nPath: `libs/common/src/tools/integration/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/common/src/tools/integration/README.md)","libs/tools/generator/extensions/history/package.json":"# History Skill\n\nPath: `libs/tools/generator/extensions/history/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/history/package.json)","libs/tools/generator/extensions/history/project.json":"# History Skill\n\nPath: `libs/tools/generator/extensions/history/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/history/project.json)","libs/tools/generator/extensions/history/tsconfig.json":"# History Skill\n\nPath: `libs/tools/generator/extensions/history/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/history/tsconfig.json)","libs/tools/generator/extensions/history/tsconfig.spec.json":"# History Skill\n\nPath: `libs/tools/generator/extensions/history/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/history/tsconfig.spec.json)","libs/tools/generator/extensions/legacy/package.json":"# Legacy Skill\n\nPath: `libs/tools/generator/extensions/legacy/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/legacy/package.json)","libs/tools/generator/extensions/legacy/project.json":"# Legacy Skill\n\nPath: `libs/tools/generator/extensions/legacy/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/legacy/project.json)","libs/tools/generator/extensions/legacy/tsconfig.json":"# Legacy Skill\n\nPath: `libs/tools/generator/extensions/legacy/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/legacy/tsconfig.json)","libs/tools/generator/extensions/legacy/tsconfig.spec.json":"# Legacy Skill\n\nPath: `libs/tools/generator/extensions/legacy/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/legacy/tsconfig.spec.json)","libs/tools/generator/extensions/navigation/package.json":"# Navigation Skill\n\nPath: `libs/tools/generator/extensions/navigation/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/navigation/package.json)","libs/tools/generator/extensions/navigation/project.json":"# Navigation Skill\n\nPath: `libs/tools/generator/extensions/navigation/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/navigation/project.json)","libs/tools/generator/extensions/navigation/tsconfig.json":"# Navigation Skill\n\nPath: `libs/tools/generator/extensions/navigation/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/navigation/tsconfig.json)","libs/tools/generator/extensions/navigation/tsconfig.spec.json":"# Navigation Skill\n\nPath: `libs/tools/generator/extensions/navigation/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/navigation/tsconfig.spec.json)","libs/common/src/tools/extension/vendor/readme.md":"# vendor Documentation\n\nPath: `libs/common/src/tools/extension/vendor/readme.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/common/src/tools/extension/vendor/readme.md)"},"files":{".claude/skills/angular-modernization/SKILL.md":"---\nname: angular-modernization\ndescription: Modernizes Angular code such as components and directives to follow best practices using both automatic CLI migrations and Bitwarden-specific patterns. YOU must use this skill when someone requests modernizing Angular code. DO NOT invoke for general Angular discussions unrelated to modernization.\nallowed-tools: Read, Write, Glob, Bash(npx ng generate:*)\n---\n\n# Angular Modernization\n\nTransforms legacy Angular components to modern architecture using a two-step approach:\n\n1. **Automated migrations** - Angular CLI schematics for standalone, control flow, and signals\n2. **Bitwarden patterns** - ADR compliance, OnPush change detection, proper visibility, thin components\n\n## Workflow\n\n### Step 1: Run Angular CLI Migrations\n\n**⚠️ CRITICAL: ALWAYS use Angular CLI migrations when available. DO NOT manually migrate features that have CLI schematics.**\n\nAngular provides automated schematics that handle edge cases, update tests, and ensure correctness. Manual migration should ONLY be used for patterns not covered by CLI tools.\n\n**IMPORTANT:**\n\n- Always run the commands using `npx ng`.\n- All the commands must be run on directories and NOT files. Use the `--path` option to target directories.\n- Run migrations in order (some depend on others)\n\n#### 1. Standalone Components\n\n```bash\nnpx ng generate @angular/core:standalone --path=<directory> --mode=convert-to-standalone\n```\n\nNgModule-based → standalone architecture\n\n#### 2. Control Flow Syntax\n\n```bash\nnpx ng generate @angular/core:control-flow\n```\n\n`*ngIf`, `*ngFor`, `*ngSwitch` → `@if`, `@for`, `@switch`\n\n#### 3. Signal Inputs\n\n```bash\nnpx ng generate @angular/core:signal-input-migration\n```\n\n`@Input()` → signal inputs\n\n#### 4. Signal Outputs\n\n```bash\nnpx ng generate @angular/core:output-migration\n```\n\n`@Output()` → signal outputs\n\n#### 5. Signal Queries\n\n```bash\nnpx ng generate @angular/core:signal-queries-migration\n```\n\n`@ViewChild`, `@ContentChild`, etc. → signal queries\n\n#### 6. inject() Function\n\n```bash\nnpx ng generate @angular/core:inject-migration\n```\n\nConstructor injection → `inject()` function\n\n#### 7. Self-Closing Tag\n\n```bash\nnpx ng generate @angular/core:self-closing-tag\n```\n\nUpdates templates to self-closing syntax\n\n#### 8. Unused Imports\n\n```bash\nnpx ng generate @angular/core:unused-imports\n```\n\nRemoves unused imports\n\n### Step 2: Apply Bitwarden Patterns\n\nSee [migration-patterns.md](migration-patterns.md) for detailed examples.\n\n1. Add OnPush change detection\n2. Apply visibility modifiers (`protected` for template access, `private` for internal)\n3. Convert local component state to signals\n4. Keep service observables (don't convert to signals)\n5. Extract business logic to services\n6. Organize class members correctly\n7. Update tests for standalone\n\n### Step 3: Validate\n\n- Fix linting and formatting using `npm run lint:fix`\n- Run tests using `npm run test`\n\nIf any errors occur, fix them accordingly.\n\n## Key Decisions\n\n### Signals vs Observables\n\n- **Signals** - Component-local state only (ADR-0027)\n- **Observables** - Service state and cross-component communication (ADR-0003)\n- Use `toSignal()` to bridge observables into signal-based components\n\n### Visibility\n\n- `protected` - Template-accessible members\n- `private` - Internal implementation\n\n### Other Rules\n\n- Always add OnPush change detection\n- No TypeScript enums (use const objects with type aliases per ADR-0025)\n- No code regions (refactor instead)\n- Thin components (business logic in services)\n\n## Validation Checklist\n\nBefore completing migration:\n\n- [ ] OnPush change detection added\n- [ ] Visibility modifiers applied (`protected`/`private`)\n- [ ] Signals for component state, observables for service state\n- [ ] Class members organized (see [migration-patterns.md](migration-patterns.md#class-member-organization))\n- [ ] Tests updated and passing\n- [ ] No new TypeScript enums\n- [ ] No code regions\n\n## References\n\n### Bitwarden ADRs\n\n- [ADR-0003: Observable Data Services](https://contributing.bitwarden.com/architecture/adr/observable-data-services)\n- [ADR-0025: No TypeScript Enums](https://contributing.bitwarden.com/architecture/adr/no-enums)\n- [ADR-0027: Angular Signals](https://contributing.bitwarden.com/architecture/adr/angular-signals)\n- [Bitwarden Angular Style Guide](https://contributing.bitwarden.com/contributing/code-style/web/angular)\n\n### Angular Resources\n\n- [Angular Style Guide](https://angular.dev/style-guide)\n- [Angular Migrations](https://angular.dev/reference/migrations)\n- [Angular CLI Schematics](https://angular.dev/tools/cli/schematics)\n",".claude/skills/create-pull-request/SKILL.md":"---\nname: create-pull-request\nversion: 0.1.0\ndescription: Pull request creation workflow for Bitwarden Clients. Use when creating PRs, writing PR descriptions, or preparing branches for review. Triggered by \"create PR\", \"pull request\", \"open PR\", \"gh pr create\", \"PR description\".\n---\n\n# Create Pull Request\n\n## PR Title Format\n\n```\n[PROJ-XXXXX] Short imperative summary\n```\n\n**Examples:**\n\n- `[PM-12345] Add passkey support to vault item creation`\n- `[CL-1124] Increase badge max-width`\n- `[PM-33446] Ensure attachments are cleared on clone`\n\n**Rules:**\n\n- Include the Jira ticket prefix (e.g. `PM-`, `CL-`)\n- Keep under 70 characters total\n- Use imperative mood in the summary\n\n---\n\n## PR Body Template\n\n**IMPORTANT:** Always follow the repo's PR template at `.github/PULL_REQUEST_TEMPLATE.md`. Add TODO items to the screenshots section for each scenario that should be captured if any.\n\n---\n\n## Pre-PR Checklist\n\n1. **All tests pass**: Run `npm test`\n2. **Lint clean**: Run `npm run lint`\n3. **No unintended changes**: Check `git diff origin/main...HEAD` for unexpected files\n4. **Branch up to date**: Rebase on `main` if needed\n\n---\n\n## Creating the PR\n\n```bash\n# Ensure branch is pushed\ngit push -u origin <branch-name>\n\n# Create PR as draft by default (body follows .github/PULL_REQUEST_TEMPLATE.md)\ngh pr create --draft --title \"[PM-XXXXX] Short summary\" --body \"<fill in from PR template>\"\n```\n\n**Default to draft PRs.** Only create a non-draft (ready for review) PR if the user explicitly requests it.\n\n---\n\n## Base Branch\n\n- Default target: `main`\n- Check with team if targeting a feature branch instead\n",".claude/skills/create-hec-event-integration/SKILL.md":"---\nname: create-hec-event-integration\ndescription: Use when adding a new HEC (HTTP Event Collector) event integration to the Bitwarden web client. Implements the Splunk token authentication model (Bearer token + URI). Covers feature flag setup and card registration behind the flag. Does not apply to API key integrations or integrations requiring a custom connect dialog.\n---\n\n# Create HEC Event Integration (Token Auth)\n\n## Step 1 - Prompts\n\nAsk these questions one at a time — wait for each answer before proceeding.\n\n**Prompt 1 — Service name:** \"What is the service name for this integration?\" (e.g. `Splunk`, `CrowdStrike`, `Panther`)\n\nUse the answer as `<ServiceName>` throughout. The string value in the constant must exactly match what you use as the card's `name` in Step 4 — a mismatch silently saves the config with the wrong service name.\n\n**Prompt 2 — Authentication:** \"How is this integration authenticated?\" (e.g. `Token`, `API key`)\n\n- **If Token** — continue with the steps below.\n- **If anything else** — stop and inform the user: \"This skill currently only supports token-based authentication. Support for other authentication methods hasn't been added yet.\"\n\n**Prompt 3 — Logos:** \"Do you have the integration logo(s) ready to provide?\"\n\n- **If yes** — ask for the light-mode SVG file path, and optionally a dark-mode SVG path. Copy both to `apps/web/src/images/integrations/` using the naming convention `logo-<service-name-kebab>-color.svg` and `logo-<service-name-kebab>-darkmode.svg`. Use those filenames in Step 4.\n- **If no** — use placeholder paths in Step 4 and add a `// TODO: add logo before shipping` comment.\n\n## Step 2 — Add service name constant\n\n**File:** `bitwarden_license/bit-common/src/dirt/organization-integrations/models/organization-integration-service-type.ts`\n\nAdd to `OrganizationIntegrationServiceName`:\n\n```typescript\nexport const OrganizationIntegrationServiceName = Object.freeze({\n  CrowdStrike: \"CrowdStrike\",\n  Datadog: \"Datadog\",\n  Huntress: \"Huntress\",\n  <ServiceName>: \"<ServiceName>\", // ← add here\n} as const);\n```\n\n## Step 3 — Add feature flag\n\n**File:** `libs/common/src/enums/feature-flag.enum.ts`\n\nAdd the enum entry and its default. The enum key is PascalCase; the string value is kebab-case (e.g. `CrowdStrike` → `crowdstrike`, `Sumo Logic` → `sumo-logic`):\n\n```typescript\n// In the FeatureFlag enum:\nEventManagementFor<ServiceName> = \"event-management-for-<service-name-kebab>\",\n\n// In the defaultFlags object:\n[FeatureFlag.EventManagementFor<ServiceName>]: FALSE,\n```\n\nExample for `Panther`:\n\n```typescript\nEventManagementForPanther = \"event-management-for-panther\",\n[FeatureFlag.EventManagementForPanther]: FALSE,\n```\n\n## Step 4 — Register the card behind the feature flag\n\n**File:** `bitwarden_license/bit-web/src/app/dirt/organization-integrations/organization-integrations.resolver.ts`\n\nIf logos were provided, copy them to `apps/web/src/images/integrations/` first, then use the actual filenames below. If not, use the placeholder paths with the TODO comment:\n\n```typescript\nconst <serviceName>FeatureEnabled = await firstValueFrom(\n  this.configService.getFeatureFlag$(FeatureFlag.EventManagementFor<ServiceName>),\n);\n\nif (<serviceName>FeatureEnabled) {\n  integrations.push({\n    name: OrganizationIntegrationServiceName.<ServiceName>, // must match Step 1 exactly\n    linkURL: \"https://bitwarden.com/help/<service-name>-siem/\",\n    image: \"../../../../../../../images/integrations/logo-<service-name>-color.svg\", // TODO: add logo before shipping (if not yet provided)\n    imageDarkMode: \"../../../../../../../images/integrations/logo-<service-name>-darkmode.svg\", // TODO: add logo before shipping (omit if no dark mode variant)\n    type: IntegrationType.EVENT,\n    canSetupConnection: true,\n    integrationType: OrganizationIntegrationType.Hec,\n  });\n}\n```\n\nNo changes needed to `IntegrationCardComponent` — new HEC services fall into the existing `else` branch, which calls `openHecConnectDialog` → `saveHec` → `deleteHec`. These methods already call `buildHecConfiguration` and `buildHecTemplate` using the card's `name` as the service name.\n\n## Step 5 — Add tests\n\n**File:** `bitwarden_license/bit-common/src/dirt/organization-integrations/models/integration-builder.spec.ts`\n\nAdd one `it` block inside the existing `describe(\"buildHecConfiguration\", ...)` block, and one inside `describe(\"buildHecTemplate\", ...)`. Use typed property access — do not use `JSON.parse`:\n\n```typescript\n// Inside describe(\"buildHecConfiguration\", ...)\nit(\"should work with <ServiceName> service name\", () => {\n  const config = OrgIntegrationBuilder.buildHecConfiguration(\n    \"https://test.<servicename>.com/hec\",\n    \"test-token\",\n    OrganizationIntegrationServiceName.<ServiceName>,\n  );\n\n  expect(config).toBeInstanceOf(HecConfiguration);\n  expect((config as HecConfiguration).uri).toBe(\"https://test.<servicename>.com/hec\");\n  expect((config as HecConfiguration).scheme).toBe(\"Bearer\");\n  expect((config as HecConfiguration).token).toBe(\"test-token\");\n  expect(config.bw_serviceName).toBe(OrganizationIntegrationServiceName.<ServiceName>);\n});\n\n// Inside describe(\"buildHecTemplate\", ...)\nit(\"should work with <ServiceName> service name\", () => {\n  const template = OrgIntegrationBuilder.buildHecTemplate(\n    \"test-index\",\n    OrganizationIntegrationServiceName.<ServiceName>,\n  );\n\n  expect(template).toBeInstanceOf(HecTemplate);\n  expect((template as HecTemplate).index).toBe(\"test-index\");\n  expect(template.bw_serviceName).toBe(OrganizationIntegrationServiceName.<ServiceName>);\n});\n```\n\n## Step 6 — Run unit tests\n\nRun the unit tests for the spec file and confirm they all pass before finishing:\n\n```bash\nnpx jest bitwarden_license/bit-common/src/dirt/organization-integrations/models/integration-builder.spec.ts\n```\n\nAll tests must pass. If any fail, fix them before proceeding.\n\n## Common Mistakes\n\n| Mistake                                                                 | Fix                                                                                                   |\n| ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |\n| `name` in card doesn't match `OrganizationIntegrationServiceName` value | They must be identical strings — `saveHec()` casts the name directly                                  |\n| Feature flag default not set to `FALSE`                                 | Always add the default entry in `defaultFlags`; new flags without a default will not work correctly   |\n| Kebab-case mismatch in flag string                                      | Convert consistently: lowercase, spaces → hyphens                                                     |\n| Adding a new `OrganizationIntegrationType`                              | Not needed — all HEC services share `OrganizationIntegrationType.Hec`                                 |\n| Creating a new config/template class                                    | Not needed — `HecConfiguration` and `HecTemplate` handle all HEC services                             |\n| Referencing an image path without copying the file                      | Copy SVGs to `apps/web/src/images/integrations/` first; if logos aren't ready, leave the TODO comment |\n",".claude/skills/figma-to-angular/SKILL.md":"---\nname: figma-to-angular\ndescription: >\n  Converts Figma designs into production Angular components with Storybook stories for the Bitwarden\n  Clients monorepo. Use this skill whenever the user provides a Figma URL and wants to create an\n  Angular component, or mentions \"implement this design\", \"create a component from Figma\",\n  \"build this from the design spec\", or similar. Also trigger when the user pastes a Figma link\n  and asks for a component, even if they don't say \"Figma\" explicitly.\n---\n\n# Figma to Angular Component\n\nThis skill turns a Figma design spec into a fully implemented Angular component with Storybook\nstories in the Bitwarden Clients monorepo. The output should match the design visually while\nfollowing all codebase conventions.\n\n## Workflow\n\n### Phase 1: Extract the design\n\nCall `get_design_context` from the Figma MCP server. Extract the fileKey and nodeId from the URL:\n\n- `figma.com/design/:fileKey/:fileName?node-id=:nodeId` → convert `-` to `:` in nodeId\n- Pass `clientFrameworks: \"angular\"` and `clientLanguages: \"typescript,html,css\"`\n\nThe response contains reference code (React+Tailwind), a screenshot, and design metadata. Treat the\ncode as a **structural reference only** — it must be fully converted to Angular.\n\n### Phase 2: Explore the codebase\n\nBefore writing any code, understand the conventions of the target location. Use an Explore agent to\ndiscover:\n\n1. **Component file patterns** — look at 2–3 sibling components in the same directory. Note the\n   file naming, module structure, barrel exports, and whether they use standalone components.\n2. **Existing components to reuse** — search for components that match parts of the design (labels,\n   buttons, inputs, icons). Reuse them instead of reimplementing.\n3. **Tailwind configuration** — read the relevant `tailwind.config` to understand the prefix\n   (commonly `tw-`), available design tokens, and color system.\n4. **Storybook patterns** — check how sibling stories are structured: imports, `moduleMetadata`,\n   `I18nMockService` usage, `argTypes`.\n5. **i18n setup** — find the `I18nPipe` import path and the locale messages file to add new keys.\n\nThis exploration is critical. Skipping it leads to components that don't match the codebase and\nrequire extensive rework.\n\n### Phase 3: Map design tokens\n\nCreate a mapping from Figma's design values to the codebase's Tailwind tokens. The Figma response\nincludes raw CSS values (hex colors, px sizes). These must be translated to semantic tokens.\n\nCommon mappings for Bitwarden:\n\n| Figma concept      | Where to look                                               |\n| ------------------ | ----------------------------------------------------------- |\n| Colors (hex)       | `tailwind.config` → `colors`, `bg`, `fg`, `border` sections |\n| Font sizes         | `tailwind.config` → `extend.fontSize`                       |\n| Spacing / padding  | Standard Tailwind scale with prefix                         |\n| Border radius      | Standard Tailwind (`rounded-*`)                             |\n| Component variants | Existing component source (e.g. `base-button.directive.ts`) |\n\nNever use raw hex values. Always map to a semantic token. If no exact match exists, use the closest\navailable token and note the discrepancy.\n\n### Phase 4: Plan the component\n\nBefore writing code, create a plan covering:\n\n- **Selector** — follows the relevant prefix convention\n- **Inputs and outputs** — use Angular signal-based `input()` and `output()` functions\n- **Content projection** — identify slots for `<ng-content>` (labels, hints, actions)\n- **Existing component reuse** — which existing components to import and use\n- **i18n keys** — list all user-visible strings that need localization\n- **Accessibility** — keyboard navigation, ARIA attributes, focus management\n\nPresent this plan to the user and get approval before writing files.\n\n### Phase 5: Implement\n\nCreate these files following the patterns discovered in Phase 2:\n\n#### Component TypeScript (`component-name.component.ts`)\n\n```typescript\n@Component({\n  selector: \"bit-component-name\",\n  templateUrl: \"./component-name.component.html\",\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  imports: [/* discovered dependencies */],\n  host: {\n    class: \"tw-block\",\n  },\n})\n```\n\nKey conventions:\n\n- `ChangeDetectionStrategy.OnPush` always\n- Signal-based inputs: `input()`, `input.required<T>()`, `input(default, { transform: booleanAttribute })`\n- Signal-based outputs: `output<T>()`\n- Internal state with `signal()` and `viewChild()`\n- Protected methods for template bindings, private for internal logic\n- No TypeScript enums — use `Object.freeze({ ... } as const)` with a same-name type alias (ADR-0025).\n  Even for simple string unions, prefer the const object pattern for consistency.\n\n#### Component template (`component-name.component.html`)\n\n- Use new Angular control flow: `@if`, `@for`, `@switch`\n- Avoid deprecated attributes like `ngClass` and `ngStyle`, prefer `class` and `style`.\n- All Tailwind classes use the library's prefix (check `tailwind.config`)\n- All user-visible strings go through `{{ \"key\" | i18n }}` or `{{ \"key\" | i18n: param }}`\n- Reuse existing components (`bit-label`, `bitButton`, `bit-hint`, etc.)\n\n#### Barrel export (`index.ts`)\n\n```typescript\nexport { ComponentNameComponent } from \"./component-name.component\";\n```\n\n#### Storybook stories (`component-name.stories.ts`)\n\nEvery component gets stories. Follow the exact patterns from sibling components:\n\n```typescript\nimport { Meta, moduleMetadata, StoryObj } from \"@storybook/angular\";\nimport { I18nService } from \"@bitwarden/common/platform/abstractions/i18n.service\";\nimport { I18nMockService } from \"../utils/i18n-mock.service\";\n\nexport default {\n  title: \"Component Library/ComponentName\",\n  component: ComponentNameComponent,\n  decorators: [\n    moduleMetadata({\n      imports: [/* component + dependencies */],\n      providers: [\n        {\n          provide: I18nService,\n          useFactory: () =>\n            new I18nMockService({\n              // All i18n keys used by the component\n              // Use __$1__ for parameter placeholders\n            }),\n        },\n      ],\n    }),\n  ],\n  parameters: {\n    design: {\n      type: \"figma\",\n      url: \"/* original Figma URL */\",\n    },\n  },\n} as Meta<ComponentNameComponent>;\n```\n\nInclude stories for:\n\n- Default state\n- Key variant states (if applicable)\n- Edge cases visible in the design (disabled, loading, empty, etc.)\n\n#### Unit tests (`component-name.component.spec.ts`)\n\nWrite unit tests when the component has behavior that Storybook stories alone can't verify. Stories\ncover visual rendering and variant appearance — don't duplicate that. Tests should focus on:\n\n- **Computed state and logic** — e.g. a rating component rounding fractional values, a progress bar\n  clamping percentages to 0–100, variant-to-class mappings\n- **Event emission** — outputs fire with the correct payload (files selected, rating changed, toast\n  dismissed)\n- **Input validation / edge cases** — boundary values, empty inputs, invalid types rejected\n- **Accessibility attributes** — correct `role`, `aria-label`, `aria-live` values change with state\n- **Keyboard interaction** — Enter/Space triggers actions, arrow keys navigate\n\nSkip tests for components that are purely presentational with no logic beyond displaying inputs\n(e.g. a simple badge or divider). Look at sibling `*.spec.ts` files to match the test setup\npatterns (TestBed configuration, imports, mock providers).\n\n#### Update barrel exports\n\nAdd `export * from \"./component-name\";` to the parent directory's `index.ts`, inserted\nalphabetically.\n\n### Phase 6: Verify in Storybook\n\nUse Playwright MCP tools to verify the component renders correctly:\n\n1. Navigate to the story's iframe URL:\n   `http://localhost:6006/iframe.html?id=component-library-component-name--default&viewMode=story`\n2. Take a snapshot to confirm the component rendered without errors\n3. Take a screenshot and compare it against the Figma design screenshot\n4. Test interactive behavior:\n   - Click buttons and interactive elements\n   - Verify correct responses (dialogs, state changes, etc.)\n5. Check the disabled story if applicable\n\nIf Storybook isn't running or Playwright isn't available, tell the user and suggest they verify\nmanually.\n\n### Phase 7: Iterate\n\nPresent the screenshot to the user alongside the original Figma screenshot. Ask if adjustments are\nneeded. Common refinements:\n\n- Token mismatches (wrong shade, spacing off)\n- Missing hover/focus states\n- Accessibility gaps\n- i18n strings that need rewording\n\n## Key principles\n\n- **Never hardcode user-visible strings.** Every string goes through `I18nPipe`. This includes\n  aria-labels and other accessibility text — they need localization too. Add keys to the locale file\n  and provide mocks in stories.\n- **Reuse existing components aggressively.** If the design has a button, use `bitButton`. If it\n  has a label, use `bit-label`. Don't reimplement what exists.\n- **Explore before you build.** The codebase exploration step prevents most rework. Patterns vary\n  between apps and libraries — always check the specific target directory.\n- **Semantic tokens over raw values.** Map every Figma color, spacing, and font size to the\n  codebase's design token system. Raw hex values break theming.\n- **Use native HTML patterns for file inputs.** For file upload components, use a `<label for>`\n  element pointing at a hidden `<input type=\"file\">` rather than programmatic `.click()`. The\n  codebase has an `AriaDisabledClickCaptureService` that intercepts programmatic clicks on certain\n  elements, causing silent failures.\n- **No NgModules.** All new components must be standalone (`imports: [...]` in the `@Component`\n  decorator). Do not create `*.module.ts` files. The codebase is moving away from NgModules.\n- **Plan before you code.** The plan step catches API design issues early. It's cheaper to fix a\n  plan than to rewrite files.\n",".claude/skills/evaluating-sdk-internal-updates/SKILL.md":"---\nname: evaluating-sdk-internal-updates\ndescription: Evaluates a bitwarden/clients \"Update sdk-internal to\" PR against the sdk-internal commit range for compile-time and runtime breaking changes, maps affected symbols to TypeScript call sites, and applies clear in-scope fixes. Use when reviewing an SDK bump PR, an @bitwarden/sdk-internal version change in package.json, or triaging sdk-internal breaking changes. Requires a sibling `bitwarden/sdk-internal` clone.\nallowed-tools:\n  - Bash(node .claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs *)\n  - Bash(npm run test:types)\n  - Bash(npm run lint:fix)\n  - Bash(npm test -- *)\n  - Bash(npx prettier *)\n  - Bash(gh pr diff:*)\n  - Bash(gh pr view:*)\n  - Bash(git -C * log *)\n  - Bash(git -C * show *)\n  - Bash(git grep:*)\n  - Bash(git add:*)\n  - Bash(git commit:*)\n  - Bash(grep:*)\n  - Read\n  - Grep\n  - Glob\n  - Edit\n  - Write\n  - Skill(writing-client-code)\n  - Skill(bitwarden-delivery-tools:committing-changes)\n---\n\n# Evaluating sdk-internal Updates\n\n**Identify both compile-time and runtime breaks before fixing anything — fixing the first break found is not finishing.** Steps 4-7 cover the entire commit range, step 8 reports it, and step 9 starts only after that.\n\nThis SDK is the boundary where Protected Data becomes Vault Data. A fix must not send a decrypted value to an API service, log one, or move a decrypt out of the SDK into TypeScript. `.claude/CLAUDE.md` forbids new encryption logic in this repo, so a bump that appears to require some is a finding to report, not a fix to write. [Security definitions](https://contributing.bitwarden.com/architecture/security/definitions).\n\n## Identify\n\nBinding surface facts specific to this SDK: the pin is two npm versions in the root `package.json`, `@bitwarden/sdk-internal` and `@bitwarden/commercial-sdk-internal`, always the same string — `npm run lint:sdk-internal-versions` fails if they differ. The version encodes no commit; the sdk-internal SHA lives in `VERSION` inside the tarball. Nothing generated is committed, here or upstream: the API record is `bitwarden_wasm_internal.d.ts` inside the published package, whose root and `node/` copies are byte-identical, and `bitwarden_wasm_internal_bg.wasm.d.ts` is raw ABI and never a finding.\n\nThe annotations behind this surface are `#[wasm_bindgen]` and `derive(..., Tsify)`. `#[uniffi::export]` is mobile-only and largely disjoint, so a uniffi sweep under-reports this surface badly. Serde attributes are part of the emitted type: `rename`, `rename_all`, `skip` and `Option` changes alter the `.d.ts` with no Rust signature change, and a `features = [\"wasm\"]` edit in `crates/bitwarden-wasm-internal/Cargo.toml` adds or removes types wholesale. Enum members are wire values (`CipherType.Login = 1`); a renumbering compiles clean at every call site and corrupts data.\n\nMost classes have a `private constructor()` and are reachable only through a parent client accessor (`sdk.user_crypto_management()`), so a renamed accessor breaks callers with the class itself unchanged. `tsconfig.base.json` sets `skipLibCheck: true`, so only call sites are checked. `bitwarden_license/bit-common/src/platform/sdk/sdk-alias.d.ts` re-exports the commercial package under the `@bitwarden/sdk-internal` module name and ESLint forbids importing it directly, so call sites grep identically for both packages.\n\n1. `gh pr diff <PR> -R bitwarden/clients -- package.json` → the old and new version.\n2. Locate the `bitwarden/sdk-internal` clone in a sibling directory — `../sdk-internal`, where CI places it. If there is none, stop and tell the user it is a required prerequisite for this skill; do not clone it yourself.\n3. Read the baseline type check. **In CI it has already run and the prompt names its log; grep it for `error TS` and treat that as this step's output. Do not run it.** Locally, `npm run test:types`. Note any failure and continue — a clean run rules out compile breaks only where the baseline looks, and step 7 is where it does not look.\n4. `node .claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs <old-version> <new-version>`, then again with `--commercial`. It prints RANGE (including the sdk-internal SHA pair step 5 needs), REMOVED, ADDED and MUTATED, keyed per member: a class method, interface property and enum member each get their own `Owner.member` entry. In ADDED, only a pre-existing owner is a break — a new non-optional property on an existing interface breaks every construction site.\n5. `git -C ../sdk-internal log --reverse --oneline <old-sha>..<new-sha>`, then `show` each commit. Classify per hunk, not per commit — a commit with one additive headline change can still carry a second, unrelated breaking hunk. A hunk that only touches a macro invocation does not show the surface; read the macro's definition before classifying it. This is also what explains a property the step 4 diff shows renamed or gone.\n6. For every symbol from step 4, `git grep -n '<Symbol>'` from the repo root for the bare name — no module list and no import-prefix filter, since everything arrives as a plain `@bitwarden/sdk-internal` import and both apps and `bitwarden_license` consume it. Use `git grep`, not `grep -r`, which also searches `node_modules/@bitwarden/*sdk-internal/` and reports the bindings themselves as call sites. A surface change stays a candidate until a call site makes it a finding.\n7. Three sets of call sites step 3 does not cover. Read each one the range touches.\n   - `apps/` and `bitwarden_license/` get no dedicated `tsc` leg and ride only on `tsc-strict`, which filters by file, so an ordinary type error inside a `@ts-strict-ignore` file is discarded: `git grep -l '@bitwarden/sdk-internal' -- 'apps/**' 'bitwarden_license/**'`, then check that file list for `@ts-strict-ignore` with the `Grep` tool rather than piping into `xargs`, which carries no grant of its own.\n   - The root `tsconfig.json` excludes app spec files but not `bitwarden_license` ones, so `git grep -l '@bitwarden/sdk-internal' -- 'apps/**/*.spec.ts'` is type-checked by nothing; the `bitwarden_license` equivalent is still covered by `tsc-strict`.\n   - `git grep -l 'jest.mock(\"@bitwarden/sdk-internal\"'` — factory-form mocks are not checked against the real module and drift silently. `libs/common/src/platform/spec/mock-sdk.service.ts` and `libs/common/spec/jest-sdk-client-factory.ts` are structural and break loudest, the latter on any `PasswordManagerClient` constructor change.\n8. Report under these headings, which are the same across every repo running this evaluation: `## SDK bump evaluated`, `## Compile-time breaks`, `## Runtime considerations` (wire-value and serialization findings go here; omit the heading when there are none), `## Everything else in range — confirmed safe`, `## Commit`. Give each finding its commit, symbol and call sites, and state whether the type check passed inside `## Compile-time breaks` rather than as its own section. Cite sdk-internal commits and PRs as `bitwarden/sdk-internal#<N>` or a full URL — a bare `#<N>` copied from a commit subject auto-links into bitwarden/clients and tags an unrelated PR.\n\n## Resolve\n\n9. Fix anything found, compile-time or runtime, whenever the correct behavior is clear and in scope. For a new required argument or method with no existing consumer, grep the underlying concept rather than the new name, which does not exist yet; with no consumer, stub it — an empty body with a `// no-op` comment, or a default return, never a `throw`, which turns a stub into a crash. A sibling's structure is a template; its behavior is not evidence for yours, so read the SDK to choose between two plausible argument values. Never edit `package.json`: the pin is the bump's output, not the fix's. If unsure, report it instead of guessing, along with anything needing a product decision. Invoke `Skill(writing-client-code)` first if the fix is not purely mechanical, then commit with `Skill(bitwarden-delivery-tools:committing-changes)`.\n10. `npm run lint:fix`, then `npx prettier --write <edited files>`, then `npm run test:types`, then `npm test -- <edited paths> --maxWorkers=2`. Run all four even in CI: `lint.yml`'s `npm run lint` step runs ESLint as well as Prettier, and `--maxWorkers=2` is this repo's workaround for Jest exhausting memory.\n",".claude/skills/fix-angular-fixmes/SKILL.md":"---\nname: fix-angular-fixmes\ndescription: Resolves eslint-disable suppression comments throughout the Bitwarden clients codebase by fixing the underlying issue. Use when the user asks to \"fix FIXMEs\", \"fix eslint suppressions\", \"clean up eslint-disable-next-line\", \"resolve CL-764\", \"resolve CL-903\", \"fix OnPush eslint suppressions\", \"fix Signals eslint suppressions\", or reduce linting suppressions.\nallowed-tools: Read, Write, Edit, Glob, Grep, Bash(npx ng generate:*), Bash(npm run lint:fix), Bash(npm run test)\n---\n\n## Key rules\n\n- Fix the underlying issue — never just delete the suppression comment and leave broken code.\n- Remove the **complete comment block**: FIXME line (if any) + TODO: Skipped block (if any) + eslint-disable-next-line line.\n- Both FIXME-paired and standalone suppressions are the same migration debt.\n- For Angular migration rules, prefer CLI schematics over manual edits.\n- Do NOT convert service observables to signals (ADR-0027).\n- For OnPush and signals patterns, the `angular-modernization` skill is the authoritative source — this skill only owns the ESLint suppression mechanics.\n\n## Step 1: Discover all suppressions\n\nUse the `Grep` tool to find suppressions in the target path:\n\n- Pattern `eslint-disable` — finds all eslint suppressions\n- Pattern `FIXME.*CL-` — finds Angular FIXME-tracked ones specifically\n\nGroup results by rule name. Two forms appear in this codebase:\n\n**Form A — FIXME-paired** (a FIXME tracking comment sits above the disable):\n\n```typescript\n// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush\n// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection\n```\n\n**Form B — Standalone** (disable without a FIXME, or with a CLI skip comment):\n\n```typescript\n// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection\n// TODO: Skipped for signal migration because:\n//  Accessor inputs cannot be migrated as they are too complex.\n// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals\n// eslint-disable-next-line @angular-eslint/prefer-signals\n```\n\nBoth forms must be fixed the same way.\n\n## Rule reference\n\n| Category         | Rule                                                        | Section below                                       |\n| ---------------- | ----------------------------------------------------------- | --------------------------------------------------- |\n| Angular          | `@angular-eslint/prefer-on-push-component-change-detection` | [OnPush](#onpush)                                   |\n| Angular          | `@angular-eslint/prefer-signals`                            | [Signals](#signals)                                 |\n| Angular          | `@angular-eslint/prefer-output-emitter-ref`                 | [Signals](#signals)                                 |\n| Angular template | `@angular-eslint/template/button-has-type`                  | [HTML rules](#html-rules)                           |\n| TypeScript       | `@typescript-eslint/no-floating-promises`                   | [no-floating-promises](#no-floating-promises)       |\n| TypeScript       | `@typescript-eslint/no-unused-vars`                         | [no-unused-vars](#no-unused-vars)                   |\n| TypeScript       | `@typescript-eslint/no-unsafe-function-type`                | [no-unsafe-function-type](#no-unsafe-function-type) |\n| RxJS             | `rxjs/no-async-subscribe`                                   | [rxjs rules](#rxjs-rules)                           |\n| RxJS             | `rxjs-angular/prefer-takeuntil`                             | [rxjs rules](#rxjs-rules)                           |\n| Bitwarden        | `@bitwarden/platform/no-enums`                              | [no-enums](#no-enums)                               |\n| Bitwarden        | `@bitwarden/components/no-bwi-class-usage`                  | [HTML rules](#html-rules)                           |\n| General          | `no-restricted-imports`                                     | [no-restricted-imports](#no-restricted-imports)     |\n| General          | `no-console`                                                | [no-console](#no-console)                           |\n| General          | `no-empty`                                                  | [no-empty](#no-empty)                               |\n| General          | bare `// eslint-disable-next-line`                          | [bare disable](#bare-disable)                       |\n| Tailwind         | `tailwindcss/no-custom-classname`                           | [HTML rules](#html-rules)                           |\n\n## OnPush\n\n**Rule:** `@angular-eslint/prefer-on-push-component-change-detection`\n\nFollow the OnPush guidance in the `angular-modernization` skill (add `changeDetection: ChangeDetectionStrategy.OnPush`, remove `ChangeDetectorRef` if only used for `detectChanges()`). Then remove the FIXME + `eslint-disable-next-line` lines.\n\n> `@Directive` does not support `changeDetection` — skip OnPush for pure directives.\n\n## Signals\n\n**Rules:** `@angular-eslint/prefer-signals`, `@angular-eslint/prefer-output-emitter-ref`\n\nApplies to `@Input()`, `@Output()`, `@ViewChild`, `@ContentChild`.\n\nFollow the Signal Inputs, Outputs, and Queries guidance in the `angular-modernization` skill (prefer CLI schematics, then manual conversion). After each migration, **manually remove** the FIXME and `eslint-disable-next-line` lines, and any `// TODO: Skipped for signal migration because:` comment blocks.\n\n> Do NOT convert service observables to signals — only component-local state and decorator bindings (ADR-0027).\n\n## no-floating-promises\n\n**Rule:** `@typescript-eslint/no-floating-promises`\n\nA returned Promise is not handled. Pick one fix:\n\n```typescript\n// 1. Await it (preferred in async functions)\nawait this.router.navigate([\"/login\"]);\n\n// 2. void — explicit fire-and-forget\nvoid this.router.navigate([\"/login\"]);\n\n// 3. Chain .catch() for explicit error handling\nthis.router.navigate([\"/login\"]).catch((err) => this.logService.error(err));\n```\n\nUse `void` for navigation or toast calls that genuinely don't need awaiting. Use `await` when the result matters or you're already in an async context.\n\n## no-unused-vars\n\n**Rule:** `@typescript-eslint/no-unused-vars`\n\n```typescript\n// Remove unused variable\nconst unused = computeSomething(); // delete this line\n\n// Or prefix with _ if it must be declared (e.g. destructuring)\nconst [_first, second] = array;\n\n// Or suppress a catch variable (TypeScript 4.0+)\ntry { ... } catch { ... } // omit the variable entirely\n```\n\n## no-unsafe-function-type\n\n**Rule:** `@typescript-eslint/no-unsafe-function-type`\n\nReplace the generic `Function` type with a specific signature:\n\n**Before**\n\n```typescript\nprivate callback: Function;\n```\n\n**After** — use the actual signature\n\n```typescript\nprivate callback: () => void;\n// or for unknown signatures:\nprivate callback: (...args: unknown[]) => unknown;\n```\n\n## RxJS rules\n\n**Rules:** `rxjs/no-async-subscribe`, `rxjs-angular/prefer-takeuntil`\n\n**`rxjs/no-async-subscribe`** — async callback inside `.subscribe()` swallows errors:\n\n**Before**\n\n```typescript\nthis.service.data$.subscribe(async (value) => {\n  await this.process(value);\n});\n```\n\n**After** — move async work into the pipe\n\n```typescript\nthis.service.data$\n  .pipe(\n    switchMap((value) => this.process(value)),\n    takeUntilDestroyed(),\n  )\n  .subscribe();\n```\n\n**`rxjs-angular/prefer-takeuntil`** — subscription without cleanup:\n\n**Before**\n\n```typescript\nthis.service.data$.subscribe((value) => {\n  this.data = value;\n});\n```\n\n**After** — add `takeUntilDestroyed()` (call in constructor or use `destroyRef`)\n\n```typescript\nconstructor() {\n  this.service.data$\n    .pipe(takeUntilDestroyed())\n    .subscribe((value) => { this.data = value; });\n}\n```\n\n## no-enums\n\n**Rule:** `@bitwarden/platform/no-enums`\n\nConvert TypeScript enums to const objects with type aliases (ADR-0025):\n\n**Before**\n\n```typescript\nenum CipherType {\n  Login = 1,\n  SecureNote = 2,\n}\n```\n\n**After**\n\n```typescript\nexport const CipherType = Object.freeze({ Login: 1, SecureNote: 2 } as const);\nexport type CipherType = (typeof CipherType)[keyof typeof CipherType];\n```\n\nUpdate all import sites — the usage (`CipherType.Login`) stays the same.\n\n## no-restricted-imports\n\n**Rule:** `no-restricted-imports`\n\nThe import is from a path that the ESLint config forbids. Steps:\n\n1. Read the context around the import to understand what is being imported.\n2. Check `eslint.config.mjs` at the repo root (or the nearest config) for the `no-restricted-imports` rule to find the allowed alternative path.\n3. Replace the import with the allowed path and remove the suppression.\n\nCommon cases: importing platform-internal modules directly instead of through the public API, or test-only helpers in non-test files.\n\n## no-console\n\n**Rule:** `no-console`\n\n```typescript\n// Remove debug statements\nconsole.log(\"debug\"); // delete\n\n// Replace with the application logging service\nthis.logService.error(\"Something failed\", error);\n```\n\nIn test files (`*.spec.ts`), a `console.error` or `console.warn` spy may be intentional — in that case, set up the spy properly rather than suppressing:\n\n```typescript\njest.spyOn(console, \"error\").mockImplementation(() => {});\n```\n\n## no-empty\n\n**Rule:** `no-empty`\n\nEmpty `catch` blocks silently swallow errors:\n\n**Before**\n\n```typescript\ntry {\n  await something();\n  // eslint-disable-next-line no-empty\n} catch {}\n```\n\n**After** — handle or log the error\n\n```typescript\ntry {\n  await something();\n} catch (e) {\n  // Intentionally ignored — operation is best-effort\n}\n\n// Or log it\ntry {\n  await something();\n} catch (e) {\n  this.logService.warning(\"Operation failed\", e);\n}\n```\n\n## Bare disable\n\n**Rule:** bare `// eslint-disable-next-line` (no rule specified)\n\nThis disables ALL rules for the next line, which is always wrong. Steps:\n\n1. Remove the suppression and run `npm run lint:fix` to see which specific rule triggers.\n2. Fix the underlying issue using the appropriate section above.\n3. If the violation truly cannot be fixed (rare), replace the bare disable with a specific named rule.\n\n## HTML rules\n\n**`@angular-eslint/template/button-has-type`** — Add an explicit type to every `<button>`:\n\n**Before**\n\n```html\n<button (click)=\"save()\">Save</button>\n```\n\n**After**\n\n```html\n<button type=\"button\" (click)=\"save()\">Save</button>\n<!-- or type=\"submit\" inside a <form> -->\n```\n\n**`@bitwarden/components/no-bwi-class-usage`** — Replace raw `bwi-*` icon classes with the `<bit-icon>` component or the appropriate icon token.\n\n**`tailwindcss/no-custom-classname`** — Use a Tailwind utility class with the `tw-` prefix, or register the class in the Tailwind safelist. Never use arbitrary custom class names.\n\n## Step 2: Cleanup checklist per fixed instance\n\n- [ ] `// FIXME(…)` line removed (if present)\n- [ ] `// TODO: Skipped for signal migration because: …` block removed (all lines, if present)\n- [ ] `// eslint-disable-next-line …` line removed\n- [ ] Unused imports removed; new imports added as needed\n- [ ] All in-class usages updated (e.g. signal reads need `()`)\n\n## Step 3: Validate\n\n```bash\nnpm run lint:fix\n```\n\nFix any errors that remain. Run `npm run test` if behaviour-critical code was changed.\n",".claude/skills/cipher-type-planner/SKILL.md":"---\nname: cipher-type-planner\ndescription: Plans the creation or modification of a cipher type (vault item type) across the Bitwarden clients monorepo. Use this skill when a user wants to add a new cipher type, modify an existing cipher type, or asks about what is needed to implement a cipher type. DO NOT invoke for general vault or cipher questions unrelated to adding or changing a cipher type.\nuser-invocable: true\nargument-hint: \"target-client\"\n---\n\n# Cipher Type Planner\n\n## Workflow\n\n### Step 1: Gather Requirements\n\nAsk the user the following questions (use `AskUserQuestion`). Adapt questions based on what\nthe user has already provided.\n\n**Required questions:**\n\n1.  **Type name and value** - What is the cipher type name and integer value? If the user hasn't specified a value, determine the next available integer by reading the `CipherType` enum definition.\n2.  **Fields** - What are the cipher's properties? Each property must contain:\n    - Field name\n    - Data type (string, number, boolean)\n    - Encryption required?\n    - Required\n3.  **Target client** (`$0`) - Which client should this plan focus on? (`web`, `desktop`, `browser`, `cli`, or `all`). Shared library changes (`libs/common`, `libs/vault`) are always included; `$0` controls which `apps/*` files appear. **Skip if already provided as an argument.** Default: `all`.\n4.  **Autofill** - Should this type participate in browser autofill? (Currently only Login, Card,\n    and Identity support autofill.) Only ask if `$0` is `browser` or `all`.\n5.  **Linked fields** - Should this type support linked custom fields? If yes, which properties\n    should be linkable?\n6.  **Feature flag** - What is the feature flag name?\n7.  **Prerequisites** - Have all server and SDK prerequisites been completed? **Do not proceed\n    with the plan until the user confirms these are done.**\n\n**Additional questions:**\n\nAsk each of the following. If the engineer does not have an answer, accept \"N/A\" or \"not yet decided\" and note it as a gap in the plan.\n\n- **Import/export** - Should import/export support be included in this plan?\n- **UI details** - Are there specific UI requirements for the form or view sections (e.g., dropdowns, masked fields, copy buttons)?\n- **Subtitle** - What value should the `subTitle` getter on the view model return? This appears in vault list items.\n- **Icon** - What icon represents this type in the vault? Bitwarden uses `bwi-` icon classes.\n- **Organization policy** - Does this type appear in the restricted item types policy UI?\n\n### Step 2: Enter Plan Mode\n\nAfter gathering requirements, enter plan mode using `EnterPlanMode`. Explore the codebase to\nverify current patterns and file locations. Use the SshKey cipher type (value 5) as the canonical\nreference for implementation patterns.\n\nKey files to inspect for patterns:\n\n- `libs/common/src/vault/enums/cipher-type.ts` - Enum definition\n- `libs/common/src/vault/models/api/ssh-key.api.ts` - API model pattern\n- `libs/common/src/vault/models/data/ssh-key.data.ts` - Data model pattern\n- `libs/common/src/vault/models/domain/ssh-key.ts` - Domain model pattern\n- `libs/common/src/vault/models/view/ssh-key.view.ts` - View model pattern\n- `libs/common/src/models/export/ssh-key.export.ts` - Export model pattern\n- `libs/common/src/vault/models/domain/cipher.ts` - Container switch patterns\n- `libs/vault/src/cipher-form/components/sshkey-section/` - Form component pattern\n- `libs/vault/src/cipher-view/sshkey-sections/` - View component pattern\n\n### Step 3: Build the Plan\n\nWrite a comprehensive plan to the plan file. The plan MUST include all sections below.\n\n---\n\n## Plan Output Format\n\n### 1. Overview\n\n- **Cipher type name:**\n- **Integer value:**\n- **Feature flag:**\n- **Minimum client version:**\n- **Fields:** Table of all fields with name, type, encrypted (yes/no), required (yes/no)\n- **Supports autofill:** Yes/No\n- **Supports linked fields:** Yes/No\n\n### 2. Clients - New Files to Create\n\nList every file that needs to be created, with the full path and a brief description. Organize by\nlayer:\n\n**Model stack:**\n\n- `libs/common/src/vault/models/api/<type>.api.ts` - API response shape\n- `libs/common/src/vault/models/data/<type>.data.ts` - Serializable storage format\n- `libs/common/src/vault/models/domain/<type>.ts` - Encrypted business object\n- `libs/common/src/vault/models/domain/<type>.spec.ts` - Domain model tests\n- `libs/common/src/vault/models/view/<type>.view.ts` - Decrypted view for UI\n\n**Export (if import/export is included):**\n\n- `libs/common/src/models/export/<type>.export.ts` - Export model\n\n**UI components:**\n\n- `libs/vault/src/cipher-form/components/<type>-section/` - Form section component (TS, HTML, spec)\n- `libs/vault/src/cipher-view/<type>-sections/` - View section component (TS, HTML)\n\n### 3. Clients - Existing Files to Modify\n\nList every file that needs modification, organized by concern. For each file, describe the specific\nchange needed.\n\n**Core enum:**\n\n- `libs/common/src/vault/enums/cipher-type.ts` - Add `<Type>: <N>` to `CipherType`\n- `libs/common/src/vault/enums/cipher-type.spec.ts` - Update tests\n\n**Container switches (add case for new type):**\n\n- `libs/common/src/vault/models/data/cipher.data.ts` - Constructor\n- `libs/common/src/vault/models/domain/cipher.ts` - Constructor, `decrypt()`,\n  `toCipherData()`, `fromJSON()`, `toSdkCipher()`, `fromSdkCipher()`\n- `libs/common/src/vault/models/view/cipher.view.ts` - `item` getter, `fromJSON()`,\n  `fromSdkCipherView()`, `getSdkCipherViewType()`, `toSdkCipherView()`\n- `libs/common/src/vault/models/request/cipher.request.ts` - Constructor\n- `libs/common/src/vault/models/response/cipher.response.ts` - Constructor\n- `libs/common/src/vault/services/cipher.service.ts` - `encryptCipherData()`\n\n**Export (if import/export is included):**\n\n- `libs/common/src/models/export/cipher.export.ts` - `toView()`, `toDomain()`, `build()`\n\n**SDK integration:**\n\n- `libs/common/src/vault/models/domain/cipher-sdk-mapper.ts` - Record mapper\n- Domain and view model SDK methods (`toSdk*`/`fromSdk*`)\n\n**UI wiring:**\n\n- `libs/vault/src/cipher-form/components/cipher-form.component.ts` - Import and wire section\n- `libs/vault/src/cipher-form/components/cipher-form.component.html` - Add section template\n- `libs/vault/src/cipher-view/cipher-view.component.ts` - Import and wire section\n- `libs/vault/src/cipher-view/cipher-view.component.html` - Add section template\n- `libs/common/src/vault/icon/build-cipher-icon.ts` - Add icon case\n\n**Vault filters (CRITICAL — without these, ciphers won't appear in the vault list):**\n\nAll vault filter files must be feature-flag-gated so the new type only appears when the flag is\nenabled. Use `ConfigService.getFeatureFlag$()` with `combineLatest` to filter the type out of\narrays when the flag is off.\n\n_Always included (shared):_\n\n- `libs/vault/src/services/vault-filter.service.ts` - **CRITICAL**: Add type to `buildCipherTypeTree()` `allTypeFilters` array. Without this, ciphers of the new type will not appear in the vault sidebar or list.\n- `libs/vault/src/models/filter-function.ts` - Add filter case for the new type\n- `libs/angular/src/vault/components/vault-items.component.ts` - Feature-flag-gate empty state type buttons\n\n_Include if `$0` is `web` or `all`:_\n\n- `apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts` - Add to `allTypeFilters`, `searchPlaceholder`, and feature-flag-gate in `buildAllFilters()`\n- `apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts` - Feature-flag-gate in `buildAllFilters()`\n\n_Include if `$0` is `desktop` or `all`:_\n\n- `apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts` - Add `ConfigService`, `combineLatest` with feature flag\n\n_Include if `$0` is `browser` or `all`:_\n\n- `apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts` - Add `ConfigService`, feature-flag-gate `cipherTypes`\n\n**New item menus (feature-flag-gated):**\n\n_Always included (shared):_\n\n- `libs/common/src/vault/types/cipher-menu-items.ts` - Add menu item entry for new type\n- `libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts` - Add `canCreate<Type> = input(false)` signal, gate in `cipherMenuItems` observable\n\n_Include if `$0` is `web` or `all`:_\n\n- `apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts` - Add `canCreate<Type>$` observable from feature flag\n- `apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.html` - Bind `[canCreate<Type>]` to `<vault-new-cipher-menu>`\n\n_Include if `$0` is `browser` or `all`:_\n\n- `apps/browser/src/vault/popup/components/vault/new-item-dropdown/new-item-dropdown.component.ts` - Add `ConfigService`, `combineLatest` with feature flag\n\n**Localization (add i18n keys):**\n\n_Include only locale files for `$0`. If `$0` is `all`, include all three:_\n\n- `apps/web/src/locales/en/messages.json` _(web)_\n- `apps/desktop/src/locales/en/messages.json` _(desktop)_\n- `apps/browser/src/_locales/en/messages.json` _(browser)_\n\n**Linked fields (if applicable):**\n\n- `libs/common/src/vault/enums/linked-id-type.enum.ts`\n\n**Autofill (if applicable — only if `$0` is `browser` or `all`):**\n\n- List relevant autofill files from `apps/browser/src/autofill/` only if the type supports\n  autofill\n\n**Restricted item types (if applicable):**\n\nRestricted item type enforcement is used across all clients. Include files for `$0`:\n\n_Always included (shared):_\n\n- `libs/common/src/vault/services/vault-settings/vault-settings.service.ts` - Restricted types service\n\n_Include if `$0` is `web` or `all`:_\n\n- `apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.ts` - Policy configuration UI\n- `apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.html`\n\n_Include if `$0` is `browser` or `all`:_\n\n- `apps/browser/src/vault/popup/components/vault/item-more-options/item-more-options.component.ts` - Restricted type checks\n\n_Include if `$0` is `cli` or `all`:_\n\n- `apps/cli/src/vault/create.command.ts` - Restricted type checks\n- `apps/cli/src/commands/list.command.ts` - Restricted type checks\n- `apps/cli/src/commands/get.command.ts` - Restricted type checks\n\n### 4. Localization Keys\n\nList all i18n keys that need to be added. At minimum:\n\n- Type label\n- Field labels for each type-specific field\n\n### 5. Tests\n\nList all test files that need to be created or updated:\n\n- `libs/common/src/vault/enums/cipher-type.spec.ts`\n- `libs/common/src/vault/models/domain/cipher.spec.ts`\n- `libs/common/src/vault/models/domain/<type>.spec.ts` (new)\n- `libs/common/src/vault/models/view/cipher.view.spec.ts`\n- `libs/common/src/vault/services/cipher.service.spec.ts`\n- `libs/common/src/vault/services/cipher-sdk.service.spec.ts`\n- `libs/common/src/vault/icon/build-cipher-icon.spec.ts`\n- `libs/common/src/models/export/cipher.export.spec.ts`\n- `libs/vault/src/cipher-form/components/cipher-form.component.spec.ts`\n- `libs/vault/src/cipher-view/cipher-view.component.spec.ts`\n- Form section component spec (new)\n\n### 6. Recommended Implementation Order\n\nRecommended implementation order, customized for this specific type. Only include steps relevant\nto `$0` (shared steps are always included):\n\n1. Core enum addition _(shared)_\n2. Feature flag registration _(shared)_\n3. Model stack (API, Data, Domain, View, Export if applicable) _(shared)_\n4. Container switch updates _(shared)_\n5. SDK bindings (`toSdk*`/`fromSdk*`) _(shared)_\n6. Localization keys _(`$0`)_\n7. Shared UI (icon, menu items) _(shared)_\n8. Vault filters with feature flag gating — CRITICAL for ciphers to appear _(shared + `$0`)_\n9. New item menus with feature flag gating _(shared + `$0`)_\n10. Per-app UI (form section, view section) _(shared)_\n11. Context menu / copy actions — see Section 8 _(shared + `$0`)_\n12. CLI _(only if `$0` is `cli` or `all`)_\n13. Autofill _(only if `$0` is `browser` or `all`)_\n14. Tests _(shared + `$0`)_\n\n### 7. Risks and Considerations\n\n- Cross-repo coordination requirements\n- Feature flag rollout strategy\n- Backward compatibility concerns\n- Any fields that need special encryption handling (reminder: no new encryption logic in clients)\n- Performance considerations for large vaults\n- **i18n key reuse** - Before adding new locale keys, check whether existing keys already have the\n  desired display value. If an existing key has the same message text, reuse it instead of creating\n  a duplicate. Only create new keys when no existing key matches.\n\n### 8. Context Menu / Copy Actions\n\nEach cipher type can expose copiable fields in the vault list item context menus (right-click / more\nmenu). Include only the sections relevant to `$0`.\n\n#### Core Infrastructure (always included)\n\n| File                                                    | What to add                                                                                                                                                                                                                                     |\n| ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `libs/vault/src/services/copy-cipher-field.service.ts`  | Add field names to the `CopyAction` type union. Add entries to the `CopyActions` record with `typeI18nKey` (i18n key for the toast message), `protected` (whether it requires password re-prompt), and optional `event` (for event collection). |\n| `libs/common/src/vault/utils/cipher-view-like-utils.ts` | Add cases to `hasCopyableValue()` that check whether the cipher has a non-empty value for each copiable field.                                                                                                                                  |\n\n#### Browser (include if `$0` is `browser` or `all`)\n\n| File                                                                                              | What to add                                                                                                                                                              |\n| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `apps/browser/src/vault/popup/components/vault/item-copy-action/item-copy-actions.component.ts`   | Add a `singleCopyable<Type>` getter (for single-field quick copy button), a `has<Type>Values` getter, and a `getNumberOf<Type>Values()` method. Follow the Card pattern. |\n| `apps/browser/src/vault/popup/components/vault/item-copy-action/item-copy-actions.component.html` | Add a section using `@if` syntax (NOT `*ngIf`) with the single/multi field pattern.                                                                                      |\n\n#### Web (include if `$0` is `web` or `all`)\n\n| File                                                                            | What to add                                                                                                               |\n| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |\n| `apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts`   | Add `is<Type>Cipher` and `hasVisible<Type>Options` getters. Add `hasVisible<Type>Options` to the `showMenuDivider` check. |\n| `apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html` | Add copy buttons using `@if` syntax with `appCopyField` directive.                                                        |\n\n#### Desktop (include if `$0` is `desktop` or `all`)\n\n| File                                                                            | What to add                                                                                                                                                                              |\n| ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `apps/desktop/src/vault/app/vault-v3/vault-items/vault-cipher-row.component.ts` | Add a `CipherType.<Type>` case to the `copyFields` computed signal, returning `CopyFieldConfig[]` entries. This is the most modern pattern — uses a computed signal rather than getters. |\n\n#### Critical Warnings\n\n- **CLI has no copy menu UI** — do not add copy-related i18n keys to the CLI locale.\n- **Only expose fields that should be copiable** — not every cipher field needs a copy action. Check\n  with product requirements for which fields get copy buttons.\n",".claude/CLAUDE.md":"# Bitwarden Clients - Claude Code Configuration\n\n## Project Context Files\n\n**Read these files before reviewing to ensure that you fully understand the project and contributing guidelines**\n\n1. `README.md` (read on demand for project overview)\n2. `CONTRIBUTING.md` (read on demand for contributing guidelines)\n3. @../.github/PULL_REQUEST_TEMPLATE.md\n\n## Critical Rules\n\n- **NEVER** use code regions: If complexity suggests regions, refactor for better readability\n- **CRITICAL**: new encryption logic should not be added to this repo.\n  - If significant encryption related logic is added or changed, make sure @bitwarden/team-key-management-dev is aware of the PR\n- **NEVER** send unencrypted vault data to API services\n- **NEVER** commit secrets, credentials, or sensitive information.\n- **NEVER** log decrypted data, encryption keys, or PII\n  - No vault data in error messages or console logs\n- **ALWAYS** Respect configuration files at the root and within each app/library (e.g., `eslint.config.mjs`, `jest.config.js`, `tsconfig.json`).\n- **CRITICAL**: Tailwind CSS classes MUST use the `tw-` prefix (e.g., `tw-flex`, `tw-p-4`). Missing prefix means the class is ignored and styling silently breaks. See [.claude/rules/tailwind.md](./rules/tailwind.md) for additional Tailwind rules.\n\n## Mono-Repo Architecture\n\nThis repository is organized as a **monorepo** containing multiple applications and libraries. The\nmain directories are:\n\n- `apps/<client>/` — single-client code (browser, cli, desktop, web). Each app is self-contained.\n- `libs/common/` — code shared across **all** clients, including non-Angular (CLI). No Angular APIs here: no `@Injectable`, no `inject()`, no decorators, no template references.\n- `libs/angular/` — code shared across Angular clients (browser/desktop/web). Angular APIs allowed.\n- Other `libs/*` (e.g. `ui`, `platform`, `key-management`, `vault`) — domain-scoped, follow the same Angular / non-Angular split based on which clients consume them.\n\nWhen adding new code, place it as deep and as narrow as possible. Promote to a shared `libs/` only when a second client needs it.\n\n**Strict boundaries** must be maintained between apps and libraries. Do not introduce\ncross-dependencies that violate the intended modular structure. Always consult and respect the\ndependency rules defined in `eslint.config.mjs`, `nx.json`, and other configuration files.\n\n## Verifying Changes\n\nAfter modifying code, run:\n\n- `npm run lint:fix` — applies ESLint fixes\n- `npm run prettier` — formats with Prettier\n- `npm run test:types` — type-checks the workspace\n- `npm test` — runs Jest. Scope with `npm test -- <path-or-pattern>` when changes are localized.\n\nIf any of these fail, fix the underlying issue before reporting the task complete. Do not skip hooks or bypass failures.\n\n## References\n\n- [Web Clients Architecture](https://contributing.bitwarden.com/architecture/clients)\n- [Architectural Decision Records (ADRs)](https://contributing.bitwarden.com/architecture/adr/)\n- [Contributing Guide](https://contributing.bitwarden.com/)\n- [Web Clients Setup Guide](https://contributing.bitwarden.com/getting-started/clients/)\n- [Code Style](https://contributing.bitwarden.com/contributing/code-style/)\n- [Security Whitepaper](https://bitwarden.com/help/bitwarden-security-white-paper/)\n- [Security Definitions](https://contributing.bitwarden.com/architecture/security/definitions)\n",".claude/skills/writing-client-code/SKILL.md":"---\nname: writing-client-code\ndescription: Bitwarden client code conventions for Angular and TypeScript. Use when creating components, services, or modifying web/browser/desktop apps.\n---\n\n### Why `libs/common` cannot import Angular\n\nCLI is a first-class client. Any code in `libs/common` must work without Angular's dependency injection, decorators, or lifecycle hooks. This is why cross-client services use abstract classes as interfaces — the concrete implementations (`Default*`, `Web*`, `Browser*`, `Desktop*`, `Cli*`) live in their respective apps.\n\n## Architectural Rationale\n\n### Thin components\n\nComponents contain only view logic. Business logic belongs in services. This keeps components testable, reusable, and prevents Angular lifecycle coupling from leaking into domain logic.\n\n### Composition over inheritance\n\nAvoid extending components across clients. Compose using shared child components instead. Inheritance creates tight coupling between client-specific UI and shared behavior — when one client's needs diverge, inherited components become hard to change safely.\n\n### Don't modernize existing code unless asked\n\nThe codebase contains both legacy and modern Angular patterns. When modifying an existing file, **follow the patterns already in that file**. Don't migrate any of these unless explicitly asked:\n\n- `*ngIf` → `@if`, `*ngFor` → `@for`\n- `@Input()` / `@Output()` → `input()` / `output()` signals\n- Constructor injection → `inject()`\n- Default change detection → `OnPush`\n- NgModule declarations → standalone components\n\nIf asked to modernize, follow this order (per the Angular migration guide): standalone → control flow → input/output signals → view queries → signals → computed → OnPush (last, only after full signal migration).\n\n### State management: Signals vs RxJS\n\n- **Component local state and Angular-only services:** Use Signals\n- **Cross-client services (`libs/common`):** Use RxJS (because CLI has no Angular Signals support)\n\nAvoid manual subscriptions. Prefer `| async` pipe. When subscriptions are necessary, pipe through `takeUntilDestroyed()` — enforced by the `prefer-takeUntil` lint rule.\n\n### No TypeScript enums (ADR-0025)\n\nUse frozen const objects with `Object.freeze()` and `as const`, plus a companion type alias. Enums have runtime behavior that creates subtle bugs with tree-shaking.\n\n## Critical Rules for New Code\n\nThese rules apply **strictly to new files and components**. For existing code, follow the patterns already in the file.\n\n- **New components must use `ChangeDetectionStrategy.OnPush`** and be `standalone: true`. `NgModules` are permitted only for grouping related standalone components\n- **Prefer `inject()` function** for DI in Angular primitives (components, pipes, directives). Use constructor injection for code shared with non-Angular clients (CLI)\n- **New templates must use control flow syntax** (`@if`, `@for`, `@switch`), not structural directives\n- **Use `host` property** in component decorators, not `@HostBinding` / `@HostListener`\n- **Use Reactive Forms exclusively** — not template-driven forms\n- **File naming:** `kebab-case.component.ts`, `.service.ts`, `.pipe.ts`, `.directive.ts`. Also: `.request.ts`, `.response.ts`, `.view.ts`, `.data.ts` for models (ADR-0012)\n- **All Tailwind classes require `tw-` prefix** — `tw-flex`, `tw-mt-2`, not `flex`, `mt-2`\n- **Testing with Jest** — use `jest-mock-extended` for mocking services. `describe`/`it` blocks, not `test()`\n- **Imports from `@bitwarden/common`** must not pull in Angular-specific code (breaks CLI)\n\n## Examples\n\n### Dependency injection (new Angular code)\n\n```typescript\n// CORRECT — inject() for Angular primitives\nexport class VaultComponent {\n  private vaultService = inject(VaultService);\n}\n\n// ALSO CORRECT — constructor injection for code shared with CLI\nexport class CryptoService {\n  constructor(private stateService: StateService) {}\n}\n```\n\n### Tailwind prefix\n\n```html\n<!-- CORRECT -->\n<div class=\"tw-flex tw-gap-2 tw-mt-4\">\n  <!-- WRONG — missing tw- prefix, will be stripped -->\n  <div class=\"flex gap-2 mt-4\"></div>\n</div>\n```\n\n### Const objects over enums (ADR-0025)\n\n```typescript\n// CORRECT — with companion type alias\nexport const CipherType = Object.freeze({\n  Login: 1,\n  SecureNote: 2,\n} as const);\nexport type CipherType = (typeof CipherType)[keyof typeof CipherType];\n\n// WRONG — TypeScript enums have runtime side effects\nexport enum CipherType {\n  Login = 1,\n  SecureNote = 2,\n}\n```\n\n## Further Reading\n\n- [Angular conventions](https://contributing.bitwarden.com/contributing/code-style/web/angular)\n- [Angular migration guide](https://contributing.bitwarden.com/contributing/code-style/web/angular-migration-guide)\n- [TypeScript style](https://contributing.bitwarden.com/contributing/code-style/typescript/)\n- [Component Library](https://contributing.bitwarden.com/contributing/ui/component-library/)\n",".claude/rules/autofill-content-scripts.md":"---\npaths:\n  - \"apps/browser/src/autofill/content/**\"\n---\n\n# Autofill Content Scripts - Critical Rules\n\n## Angular Exception\n\nCode in this directory runs as **injected content scripts**, not within the Angular application context.\n\n- **DO NOT** use Angular patterns (components, services, dependency injection, RxJS Observable Data Services, Signals, `takeUntilDestroyed()`, `async` pipe, etc.)\n- **DO NOT** import Angular modules or dependencies\n- The Angular patterns defined in [angular.md](./angular.md) and [angular-components.md](./angular-components.md), and the Observable Data Services pattern in [typescript.md](./typescript.md), do not apply here\n\n## Lit Components\n\n- Components in `content/components/` use [Lit](https://lit.dev/), not Angular\n- Lit components should consume shared concepts (constants, values) from the component library when possible, without introducing Angular or other dependency entanglements\n\n## Browser API Usage\n\n- Content scripts cannot import the `BrowserApi` abstraction required elsewhere in the browser extension\n- Direct use of `chrome.*` / `browser.*` APIs (e.g., `chrome.runtime.sendMessage`) is expected here\n- This is an exception to the `BrowserApi` rule in the parent `apps/browser/CLAUDE.md`\n\n## Cross-Browser Compatibility\n\n- Ensure injected code is compatible across browser clients, particularly Firefox, Chrome, and Safari\n- Use polyfills and vendor-specific implementations where needed\n\n## Memory Management\n\n- Ensure proper teardown of listeners, observers, and other concerns that might linger in memory\n- Content scripts do not benefit from Angular's lifecycle hooks — cleanup must be handled manually\n",".claude/rules/angular.md":"---\npaths:\n  - \"**/*.ts\"\n---\n\n# Angular Patterns\n\n> **Scope:** these rules apply **only to Angular code** — files in `apps/browser`, `apps/desktop`, `apps/web`, `libs/angular`, and any other library consumed by Angular clients. **Skip them in non-Angular TypeScript**: `libs/common`, `apps/cli`, SDK code, and anywhere `@Injectable` and Angular APIs are forbidden by the monorepo rules in the root [CLAUDE.md](../CLAUDE.md).\n\nDistilled from [Web Code Style — Angular](https://contributing.bitwarden.com/contributing/code-style/web/angular). Component-only patterns (templates, OnPush, signals, observable subscriptions, etc.) live in [angular-components.md](./angular-components.md); TypeScript-wide patterns live in [typescript.md](./typescript.md).\n\n## Dependency Injection\n\nUse `inject()` — not constructor injection. Constructor injection stays only in code shared with non-Angular clients (CLI).\n\n```typescript\nprivate folderService = inject(FolderService);\n```\n\nWhen declaring providers (component decorator, route config, module), wrap them in `safeProvider()` from `@bitwarden/ui-common`. It adds compile-time checks that the implementation matches the abstraction and that the `deps` array matches the constructor — raw Angular provider objects don't.\n\n```typescript\nimport { safeProvider } from \"@bitwarden/ui-common\";\n\nproviders: [\n  safeProvider({\n    provide: FolderService,\n    useClass: DefaultFolderService,\n    deps: [CipherService, I18nService],\n  }),\n],\n```\n\n### Injection tokens\n\nGive every `SafeInjectionToken<T>` a specific `T` — never `Type<unknown>`, `unknown` or `any`. For a component token, declare an interface or type for the inputs the consumer binds and use `Type<ThatShape>`.\n\n```typescript\nexport interface VaultGatedCollectionBanner {\n  readonly organizationId: InputSignal<OrganizationId | undefined>;\n}\n\nexport const VAULT_GATED_COLLECTION_BANNER = new SafeInjectionToken<\n  Type<VaultGatedCollectionBanner>\n>(\"VaultGatedCollectionBanner\");\n```\n",".claude/rules/flatpak-manifest.md":"# Rules Skill\n\nPath: `.claude/rules/flatpak-manifest.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/flatpak-manifest.md)",".claude/rules/i18n.md":"# Rules Skill\n\nPath: `.claude/rules/i18n.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/i18n.md)",".claude/rules/snap-permissions.md":"# Rules Skill\n\nPath: `.claude/rules/snap-permissions.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/snap-permissions.md)",".claude/rules/storybook-routing.md":"# Rules Skill\n\nPath: `.claude/rules/storybook-routing.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/storybook-routing.md)",".claude/rules/tailwind.md":"# Rules Skill\n\nPath: `.claude/rules/tailwind.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/tailwind.md)",".claude/rules/testing.md":"# Rules Skill\n\nPath: `.claude/rules/testing.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/testing.md)",".claude/rules/typescript.md":"# Rules Skill\n\nPath: `.claude/rules/typescript.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/typescript.md)","libs/tools/README.md":"# tools Documentation\n\nPath: `libs/tools/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/README.md)",".claude/skills/angular-modernization/migration-patterns.md":"# Angular-modernization Skill\n\nPath: `.claude/skills/angular-modernization/migration-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/angular-modernization/migration-patterns.md)",".claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs":"# Claude Directive: sdk-surface-diff.mjs\n\nPath: `.claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs)","libs/tools/export-vault-core/README.md":"# export-vault-core Documentation\n\nPath: `libs/tools/export-vault-core/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/README.md)","libs/tools/export-vault-core/package.json":"# Export-vault-core Skill\n\nPath: `libs/tools/export-vault-core/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/package.json)","libs/tools/export-vault-core/project.json":"# Export-vault-core Skill\n\nPath: `libs/tools/export-vault-core/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/project.json)","libs/tools/export-vault-core/tsconfig.json":"# Export-vault-core Skill\n\nPath: `libs/tools/export-vault-core/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/tsconfig.json)","libs/tools/export-vault-core/tsconfig.spec.json":"# Export-vault-core Skill\n\nPath: `libs/tools/export-vault-core/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/tsconfig.spec.json)","libs/tools/export-vault-ui/README.md":"# export-vault-ui Documentation\n\nPath: `libs/tools/export-vault-ui/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/README.md)","libs/tools/export-vault-ui/package.json":"# Export-vault-ui Skill\n\nPath: `libs/tools/export-vault-ui/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/package.json)","libs/tools/export-vault-ui/project.json":"# Export-vault-ui Skill\n\nPath: `libs/tools/export-vault-ui/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/project.json)","libs/tools/export-vault-ui/tsconfig.json":"# Export-vault-ui Skill\n\nPath: `libs/tools/export-vault-ui/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/tsconfig.json)","libs/tools/export-vault-ui/tsconfig.spec.json":"# Export-vault-ui Skill\n\nPath: `libs/tools/export-vault-ui/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/tsconfig.spec.json)","libs/tools/send/README.md":"# send Documentation\n\nPath: `libs/tools/send/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/README.md)","libs/tools/share/README.md":"# share Documentation\n\nPath: `libs/tools/share/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/README.md)","libs/tools/share/package.json":"# Share Skill\n\nPath: `libs/tools/share/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/package.json)","libs/tools/share/project.json":"# Share Skill\n\nPath: `libs/tools/share/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/project.json)","libs/tools/share/tsconfig.eslint.json":"# Share Skill\n\nPath: `libs/tools/share/tsconfig.eslint.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/tsconfig.eslint.json)","libs/tools/share/tsconfig.json":"# Share Skill\n\nPath: `libs/tools/share/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/tsconfig.json)","libs/tools/share/tsconfig.spec.json":"# Share Skill\n\nPath: `libs/tools/share/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/tsconfig.spec.json)",".claude/skills/create-hec-event-integration/example/token-auth.md":"# Example Skill\n\nPath: `.claude/skills/create-hec-event-integration/example/token-auth.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/create-hec-event-integration/example/token-auth.md)",".claude/skills/figma-to-angular/evals/evals.json":"# Evals Skill\n\nPath: `.claude/skills/figma-to-angular/evals/evals.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/figma-to-angular/evals/evals.json)","libs/tools/generator/components/package.json":"# Components Skill\n\nPath: `libs/tools/generator/components/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/package.json)","libs/tools/generator/components/project.json":"# Components Skill\n\nPath: `libs/tools/generator/components/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/project.json)","libs/tools/generator/components/readme.md":"# components Documentation\n\nPath: `libs/tools/generator/components/readme.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/readme.md)","libs/tools/generator/components/tsconfig.json":"# Components Skill\n\nPath: `libs/tools/generator/components/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/tsconfig.json)","libs/tools/generator/components/tsconfig.spec.json":"# Components Skill\n\nPath: `libs/tools/generator/components/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/tsconfig.spec.json)","libs/tools/generator/core/package.json":"# Core Skill\n\nPath: `libs/tools/generator/core/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/core/package.json)","libs/tools/generator/core/project.json":"# Core Skill\n\nPath: `libs/tools/generator/core/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/core/project.json)","libs/tools/generator/core/tsconfig.json":"# Core Skill\n\nPath: `libs/tools/generator/core/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/core/tsconfig.json)","libs/tools/generator/core/tsconfig.spec.json":"# Core Skill\n\nPath: `libs/tools/generator/core/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/core/tsconfig.spec.json)","libs/tools/send/send-ui/package.json":"# Send-ui Skill\n\nPath: `libs/tools/send/send-ui/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/send-ui/package.json)","libs/tools/send/send-ui/project.json":"# Send-ui Skill\n\nPath: `libs/tools/send/send-ui/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/send-ui/project.json)","libs/tools/send/send-ui/tsconfig.json":"# Send-ui Skill\n\nPath: `libs/tools/send/send-ui/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/send-ui/tsconfig.json)","libs/tools/send/send-ui/tsconfig.spec.json":"# Send-ui Skill\n\nPath: `libs/tools/send/send-ui/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/send-ui/tsconfig.spec.json)","libs/common/src/tools/integration/README.md":"# integration Documentation\n\nPath: `libs/common/src/tools/integration/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/common/src/tools/integration/README.md)","libs/tools/generator/extensions/history/package.json":"# History Skill\n\nPath: `libs/tools/generator/extensions/history/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/history/package.json)","libs/tools/generator/extensions/history/project.json":"# History Skill\n\nPath: `libs/tools/generator/extensions/history/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/history/project.json)","libs/tools/generator/extensions/history/tsconfig.json":"# History Skill\n\nPath: `libs/tools/generator/extensions/history/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/history/tsconfig.json)","libs/tools/generator/extensions/history/tsconfig.spec.json":"# History Skill\n\nPath: `libs/tools/generator/extensions/history/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/history/tsconfig.spec.json)","libs/tools/generator/extensions/legacy/package.json":"# Legacy Skill\n\nPath: `libs/tools/generator/extensions/legacy/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/legacy/package.json)","libs/tools/generator/extensions/legacy/project.json":"# Legacy Skill\n\nPath: `libs/tools/generator/extensions/legacy/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/legacy/project.json)","libs/tools/generator/extensions/legacy/tsconfig.json":"# Legacy Skill\n\nPath: `libs/tools/generator/extensions/legacy/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/legacy/tsconfig.json)","libs/tools/generator/extensions/legacy/tsconfig.spec.json":"# Legacy Skill\n\nPath: `libs/tools/generator/extensions/legacy/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/legacy/tsconfig.spec.json)","libs/tools/generator/extensions/navigation/package.json":"# Navigation Skill\n\nPath: `libs/tools/generator/extensions/navigation/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/navigation/package.json)","libs/tools/generator/extensions/navigation/project.json":"# Navigation Skill\n\nPath: `libs/tools/generator/extensions/navigation/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/navigation/project.json)","libs/tools/generator/extensions/navigation/tsconfig.json":"# Navigation Skill\n\nPath: `libs/tools/generator/extensions/navigation/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/navigation/tsconfig.json)","libs/tools/generator/extensions/navigation/tsconfig.spec.json":"# Navigation Skill\n\nPath: `libs/tools/generator/extensions/navigation/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/navigation/tsconfig.spec.json)","libs/common/src/tools/extension/vendor/readme.md":"# vendor Documentation\n\nPath: `libs/common/src/tools/extension/vendor/readme.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/common/src/tools/extension/vendor/readme.md)"},"items":[{"name":"angular.md","path":".claude/rules/angular.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/angular.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\npaths:\n  - \"**/*.ts\"\n---\n\n# Angular Patterns\n\n> **Scope:** these rules apply **only to Angular code** — files in `apps/browser`, `apps/desktop`, `apps/web`, `libs/angular`, and any other library consumed by Angular clients. **Skip them in non-Angular TypeScript**: `libs/common`, `apps/cli`, SDK code, and anywhere `@Injectable` and Angular APIs are forbidden by the monorepo rules in the root [CLAUDE.md](../CLAUDE.md).\n\nDistilled from [Web Code Style — Angular](https://contributing.bitwarden.com/contributing/code-style/web/angular). Component-only patterns (templates, OnPush, signals, observable subscriptions, etc.) live in [angular-components.md](./angular-components.md); TypeScript-wide patterns live in [typescript.md](./typescript.md).\n\n## Dependency Injection\n\nUse `inject()` — not constructor injection. Constructor injection stays only in code shared with non-Angular clients (CLI).\n\n```typescript\nprivate folderService = inject(FolderService);\n```\n\nWhen declaring providers (component decorator, route config, module), wrap them in `safeProvider()` from `@bitwarden/ui-common`. It adds compile-time checks that the implementation matches the abstraction and that the `deps` array matches the constructor — raw Angular provider objects don't.\n\n```typescript\nimport { safeProvider } from \"@bitwarden/ui-common\";\n\nproviders: [\n  safeProvider({\n    provide: FolderService,\n    useClass: DefaultFolderService,\n    deps: [CipherService, I18nService],\n  }),\n],\n```\n\n### Injection tokens\n\nGive every `SafeInjectionToken<T>` a specific `T` — never `Type<unknown>`, `unknown` or `any`. For a component token, declare an interface or type for the inputs the consumer binds and use `Type<ThatShape>`.\n\n```typescript\nexport interface VaultGatedCollectionBanner {\n  readonly organizationId: InputSignal<OrganizationId | undefined>;\n}\n\nexport const VAULT_GATED_COLLECTION_BANNER = new SafeInjectionToken<\n  Type<VaultGatedCollectionBanner>\n>(\"VaultGatedCollectionBanner\");\n```\n","frontmatter":{"paths":["**/*.ts"]},"isInternal":false,"tokens":444,"sizeBytes":1991},{"name":"autofill-content-scripts.md","path":".claude/rules/autofill-content-scripts.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/autofill-content-scripts.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\npaths:\n  - \"apps/browser/src/autofill/content/**\"\n---\n\n# Autofill Content Scripts - Critical Rules\n\n## Angular Exception\n\nCode in this directory runs as **injected content scripts**, not within the Angular application context.\n\n- **DO NOT** use Angular patterns (components, services, dependency injection, RxJS Observable Data Services, Signals, `takeUntilDestroyed()`, `async` pipe, etc.)\n- **DO NOT** import Angular modules or dependencies\n- The Angular patterns defined in [angular.md](./angular.md) and [angular-components.md](./angular-components.md), and the Observable Data Services pattern in [typescript.md](./typescript.md), do not apply here\n\n## Lit Components\n\n- Components in `content/components/` use [Lit](https://lit.dev/), not Angular\n- Lit components should consume shared concepts (constants, values) from the component library when possible, without introducing Angular or other dependency entanglements\n\n## Browser API Usage\n\n- Content scripts cannot import the `BrowserApi` abstraction required elsewhere in the browser extension\n- Direct use of `chrome.*` / `browser.*` APIs (e.g., `chrome.runtime.sendMessage`) is expected here\n- This is an exception to the `BrowserApi` rule in the parent `apps/browser/CLAUDE.md`\n\n## Cross-Browser Compatibility\n\n- Ensure injected code is compatible across browser clients, particularly Firefox, Chrome, and Safari\n- Use polyfills and vendor-specific implementations where needed\n\n## Memory Management\n\n- Ensure proper teardown of listeners, observers, and other concerns that might linger in memory\n- Content scripts do not benefit from Angular's lifecycle hooks — cleanup must be handled manually\n","frontmatter":{"paths":["apps/browser/src/autofill/content/**"]},"isInternal":false,"tokens":345,"sizeBytes":1665},{"name":"flatpak-manifest.md","path":".claude/rules/flatpak-manifest.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/flatpak-manifest.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"# Rules Skill\n\nPath: `.claude/rules/flatpak-manifest.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/flatpak-manifest.md)","tokens":300,"sizeBytes":178},{"name":"i18n.md","path":".claude/rules/i18n.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/i18n.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"# Rules Skill\n\nPath: `.claude/rules/i18n.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/i18n.md)","tokens":300,"sizeBytes":154},{"name":"snap-permissions.md","path":".claude/rules/snap-permissions.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/snap-permissions.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"# Rules Skill\n\nPath: `.claude/rules/snap-permissions.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/snap-permissions.md)","tokens":300,"sizeBytes":178},{"name":"storybook-routing.md","path":".claude/rules/storybook-routing.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/storybook-routing.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"# Rules Skill\n\nPath: `.claude/rules/storybook-routing.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/storybook-routing.md)","tokens":300,"sizeBytes":180},{"name":"tailwind.md","path":".claude/rules/tailwind.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/tailwind.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"# Rules Skill\n\nPath: `.claude/rules/tailwind.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/tailwind.md)","tokens":300,"sizeBytes":162},{"name":"testing.md","path":".claude/rules/testing.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/testing.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"# Rules Skill\n\nPath: `.claude/rules/testing.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/testing.md)","tokens":300,"sizeBytes":160},{"name":"typescript.md","path":".claude/rules/typescript.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/typescript.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"# Rules Skill\n\nPath: `.claude/rules/typescript.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/typescript.md)","tokens":300,"sizeBytes":166},{"name":"migration-patterns.md","path":".claude/skills/angular-modernization/migration-patterns.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/angular-modernization/migration-patterns.md","title":"Angular-modernization Skill","category":"anthropic-skill","format":"markdown","content":"# Angular-modernization Skill\n\nPath: `.claude/skills/angular-modernization/migration-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/angular-modernization/migration-patterns.md)","tokens":300,"sizeBytes":244},{"name":"SKILL.md","path":".claude/skills/angular-modernization/SKILL.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/angular-modernization/SKILL.md","title":"angular-modernization","category":"anthropic-skill","format":"markdown","content":"---\nname: angular-modernization\ndescription: Modernizes Angular code such as components and directives to follow best practices using both automatic CLI migrations and Bitwarden-specific patterns. YOU must use this skill when someone requests modernizing Angular code. DO NOT invoke for general Angular discussions unrelated to modernization.\nallowed-tools: Read, Write, Glob, Bash(npx ng generate:*)\n---\n\n# Angular Modernization\n\nTransforms legacy Angular components to modern architecture using a two-step approach:\n\n1. **Automated migrations** - Angular CLI schematics for standalone, control flow, and signals\n2. **Bitwarden patterns** - ADR compliance, OnPush change detection, proper visibility, thin components\n\n## Workflow\n\n### Step 1: Run Angular CLI Migrations\n\n**⚠️ CRITICAL: ALWAYS use Angular CLI migrations when available. DO NOT manually migrate features that have CLI schematics.**\n\nAngular provides automated schematics that handle edge cases, update tests, and ensure correctness. Manual migration should ONLY be used for patterns not covered by CLI tools.\n\n**IMPORTANT:**\n\n- Always run the commands using `npx ng`.\n- All the commands must be run on directories and NOT files. Use the `--path` option to target directories.\n- Run migrations in order (some depend on others)\n\n#### 1. Standalone Components\n\n```bash\nnpx ng generate @angular/core:standalone --path=<directory> --mode=convert-to-standalone\n```\n\nNgModule-based → standalone architecture\n\n#### 2. Control Flow Syntax\n\n```bash\nnpx ng generate @angular/core:control-flow\n```\n\n`*ngIf`, `*ngFor`, `*ngSwitch` → `@if`, `@for`, `@switch`\n\n#### 3. Signal Inputs\n\n```bash\nnpx ng generate @angular/core:signal-input-migration\n```\n\n`@Input()` → signal inputs\n\n#### 4. Signal Outputs\n\n```bash\nnpx ng generate @angular/core:output-migration\n```\n\n`@Output()` → signal outputs\n\n#### 5. Signal Queries\n\n```bash\nnpx ng generate @angular/core:signal-queries-migration\n```\n\n`@ViewChild`, `@ContentChild`, etc. → signal queries\n\n#### 6. inject() Function\n\n```bash\nnpx ng generate @angular/core:inject-migration\n```\n\nConstructor injection → `inject()` function\n\n#### 7. Self-Closing Tag\n\n```bash\nnpx ng generate @angular/core:self-closing-tag\n```\n\nUpdates templates to self-closing syntax\n\n#### 8. Unused Imports\n\n```bash\nnpx ng generate @angular/core:unused-imports\n```\n\nRemoves unused imports\n\n### Step 2: Apply Bitwarden Patterns\n\nSee [migration-patterns.md](migration-patterns.md) for detailed examples.\n\n1. Add OnPush change detection\n2. Apply visibility modifiers (`protected` for template access, `private` for internal)\n3. Convert local component state to signals\n4. Keep service observables (don't convert to signals)\n5. Extract business logic to services\n6. Organize class members correctly\n7. Update tests for standalone\n\n### Step 3: Validate\n\n- Fix linting and formatting using `npm run lint:fix`\n- Run tests using `npm run test`\n\nIf any errors occur, fix them accordingly.\n\n## Key Decisions\n\n### Signals vs Observables\n\n- **Signals** - Component-local state only (ADR-0027)\n- **Observables** - Service state and cross-component communication (ADR-0003)\n- Use `toSignal()` to bridge observables into signal-based components\n\n### Visibility\n\n- `protected` - Template-accessible members\n- `private` - Internal implementation\n\n### Other Rules\n\n- Always add OnPush change detection\n- No TypeScript enums (use const objects with type aliases per ADR-0025)\n- No code regions (refactor instead)\n- Thin components (business logic in services)\n\n## Validation Checklist\n\nBefore completing migration:\n\n- [ ] OnPush change detection added\n- [ ] Visibility modifiers applied (`protected`/`private`)\n- [ ] Signals for component state, observables for service state\n- [ ] Class members organized (see [migration-patterns.md](migration-patterns.md#class-member-organization))\n- [ ] Tests updated and passing\n- [ ] No new TypeScript enums\n- [ ] No code regions\n\n## References\n\n### Bitwarden ADRs\n\n- [ADR-0003: Observable Data Services](https://contributing.bitwarden.com/architecture/adr/observable-data-services)\n- [ADR-0025: No TypeScript Enums](https://contributing.bitwarden.com/architecture/adr/no-enums)\n- [ADR-0027: Angular Signals](https://contributing.bitwarden.com/architecture/adr/angular-signals)\n- [Bitwarden Angular Style Guide](https://contributing.bitwarden.com/contributing/code-style/web/angular)\n\n### Angular Resources\n\n- [Angular Style Guide](https://angular.dev/style-guide)\n- [Angular Migrations](https://angular.dev/reference/migrations)\n- [Angular CLI Schematics](https://angular.dev/tools/cli/schematics)\n","frontmatter":{"name":"angular-modernization","description":"Modernizes Angular code such as components and directives to follow best practices using both automatic CLI migrations and Bitwarden-specific patterns. YOU must use this skill when someone requests modernizing Angular code. DO NOT invoke for general Angular discussions unrelated to modernization.","allowed-tools":"Read, Write, Glob, Bash(npx ng generate:*)","allowedTools":["Read,","Write,","Glob,","Bash(npx","ng","generate:*)"]},"allowedTools":["Read,","Write,","Glob,","Bash(npx","ng","generate:*)"],"isInternal":false,"tokens":1083,"sizeBytes":4600},{"name":"SKILL.md","path":".claude/skills/cipher-type-planner/SKILL.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/cipher-type-planner/SKILL.md","title":"cipher-type-planner","category":"anthropic-skill","format":"markdown","content":"---\nname: cipher-type-planner\ndescription: Plans the creation or modification of a cipher type (vault item type) across the Bitwarden clients monorepo. Use this skill when a user wants to add a new cipher type, modify an existing cipher type, or asks about what is needed to implement a cipher type. DO NOT invoke for general vault or cipher questions unrelated to adding or changing a cipher type.\nuser-invocable: true\nargument-hint: \"target-client\"\n---\n\n# Cipher Type Planner\n\n## Workflow\n\n### Step 1: Gather Requirements\n\nAsk the user the following questions (use `AskUserQuestion`). Adapt questions based on what\nthe user has already provided.\n\n**Required questions:**\n\n1.  **Type name and value** - What is the cipher type name and integer value? If the user hasn't specified a value, determine the next available integer by reading the `CipherType` enum definition.\n2.  **Fields** - What are the cipher's properties? Each property must contain:\n    - Field name\n    - Data type (string, number, boolean)\n    - Encryption required?\n    - Required\n3.  **Target client** (`$0`) - Which client should this plan focus on? (`web`, `desktop`, `browser`, `cli`, or `all`). Shared library changes (`libs/common`, `libs/vault`) are always included; `$0` controls which `apps/*` files appear. **Skip if already provided as an argument.** Default: `all`.\n4.  **Autofill** - Should this type participate in browser autofill? (Currently only Login, Card,\n    and Identity support autofill.) Only ask if `$0` is `browser` or `all`.\n5.  **Linked fields** - Should this type support linked custom fields? If yes, which properties\n    should be linkable?\n6.  **Feature flag** - What is the feature flag name?\n7.  **Prerequisites** - Have all server and SDK prerequisites been completed? **Do not proceed\n    with the plan until the user confirms these are done.**\n\n**Additional questions:**\n\nAsk each of the following. If the engineer does not have an answer, accept \"N/A\" or \"not yet decided\" and note it as a gap in the plan.\n\n- **Import/export** - Should import/export support be included in this plan?\n- **UI details** - Are there specific UI requirements for the form or view sections (e.g., dropdowns, masked fields, copy buttons)?\n- **Subtitle** - What value should the `subTitle` getter on the view model return? This appears in vault list items.\n- **Icon** - What icon represents this type in the vault? Bitwarden uses `bwi-` icon classes.\n- **Organization policy** - Does this type appear in the restricted item types policy UI?\n\n### Step 2: Enter Plan Mode\n\nAfter gathering requirements, enter plan mode using `EnterPlanMode`. Explore the codebase to\nverify current patterns and file locations. Use the SshKey cipher type (value 5) as the canonical\nreference for implementation patterns.\n\nKey files to inspect for patterns:\n\n- `libs/common/src/vault/enums/cipher-type.ts` - Enum definition\n- `libs/common/src/vault/models/api/ssh-key.api.ts` - API model pattern\n- `libs/common/src/vault/models/data/ssh-key.data.ts` - Data model pattern\n- `libs/common/src/vault/models/domain/ssh-key.ts` - Domain model pattern\n- `libs/common/src/vault/models/view/ssh-key.view.ts` - View model pattern\n- `libs/common/src/models/export/ssh-key.export.ts` - Export model pattern\n- `libs/common/src/vault/models/domain/cipher.ts` - Container switch patterns\n- `libs/vault/src/cipher-form/components/sshkey-section/` - Form component pattern\n- `libs/vault/src/cipher-view/sshkey-sections/` - View component pattern\n\n### Step 3: Build the Plan\n\nWrite a comprehensive plan to the plan file. The plan MUST include all sections below.\n\n---\n\n## Plan Output Format\n\n### 1. Overview\n\n- **Cipher type name:**\n- **Integer value:**\n- **Feature flag:**\n- **Minimum client version:**\n- **Fields:** Table of all fields with name, type, encrypted (yes/no), required (yes/no)\n- **Supports autofill:** Yes/No\n- **Supports linked fields:** Yes/No\n\n### 2. Clients - New Files to Create\n\nList every file that needs to be created, with the full path and a brief description. Organize by\nlayer:\n\n**Model stack:**\n\n- `libs/common/src/vault/models/api/<type>.api.ts` - API response shape\n- `libs/common/src/vault/models/data/<type>.data.ts` - Serializable storage format\n- `libs/common/src/vault/models/domain/<type>.ts` - Encrypted business object\n- `libs/common/src/vault/models/domain/<type>.spec.ts` - Domain model tests\n- `libs/common/src/vault/models/view/<type>.view.ts` - Decrypted view for UI\n\n**Export (if import/export is included):**\n\n- `libs/common/src/models/export/<type>.export.ts` - Export model\n\n**UI components:**\n\n- `libs/vault/src/cipher-form/components/<type>-section/` - Form section component (TS, HTML, spec)\n- `libs/vault/src/cipher-view/<type>-sections/` - View section component (TS, HTML)\n\n### 3. Clients - Existing Files to Modify\n\nList every file that needs modification, organized by concern. For each file, describe the specific\nchange needed.\n\n**Core enum:**\n\n- `libs/common/src/vault/enums/cipher-type.ts` - Add `<Type>: <N>` to `CipherType`\n- `libs/common/src/vault/enums/cipher-type.spec.ts` - Update tests\n\n**Container switches (add case for new type):**\n\n- `libs/common/src/vault/models/data/cipher.data.ts` - Constructor\n- `libs/common/src/vault/models/domain/cipher.ts` - Constructor, `decrypt()`,\n  `toCipherData()`, `fromJSON()`, `toSdkCipher()`, `fromSdkCipher()`\n- `libs/common/src/vault/models/view/cipher.view.ts` - `item` getter, `fromJSON()`,\n  `fromSdkCipherView()`, `getSdkCipherViewType()`, `toSdkCipherView()`\n- `libs/common/src/vault/models/request/cipher.request.ts` - Constructor\n- `libs/common/src/vault/models/response/cipher.response.ts` - Constructor\n- `libs/common/src/vault/services/cipher.service.ts` - `encryptCipherData()`\n\n**Export (if import/export is included):**\n\n- `libs/common/src/models/export/cipher.export.ts` - `toView()`, `toDomain()`, `build()`\n\n**SDK integration:**\n\n- `libs/common/src/vault/models/domain/cipher-sdk-mapper.ts` - Record mapper\n- Domain and view model SDK methods (`toSdk*`/`fromSdk*`)\n\n**UI wiring:**\n\n- `libs/vault/src/cipher-form/components/cipher-form.component.ts` - Import and wire section\n- `libs/vault/src/cipher-form/components/cipher-form.component.html` - Add section template\n- `libs/vault/src/cipher-view/cipher-view.component.ts` - Import and wire section\n- `libs/vault/src/cipher-view/cipher-view.component.html` - Add section template\n- `libs/common/src/vault/icon/build-cipher-icon.ts` - Add icon case\n\n**Vault filters (CRITICAL — without these, ciphers won't appear in the vault list):**\n\nAll vault filter files must be feature-flag-gated so the new type only appears when the flag is\nenabled. Use `ConfigService.getFeatureFlag$()` with `combineLatest` to filter the type out of\narrays when the flag is off.\n\n_Always included (shared):_\n\n- `libs/vault/src/services/vault-filter.service.ts` - **CRITICAL**: Add type to `buildCipherTypeTree()` `allTypeFilters` array. Without this, ciphers of the new type will not appear in the vault sidebar or list.\n- `libs/vault/src/models/filter-function.ts` - Add filter case for the new type\n- `libs/angular/src/vault/components/vault-items.component.ts` - Feature-flag-gate empty state type buttons\n\n_Include if `$0` is `web` or `all`:_\n\n- `apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts` - Add to `allTypeFilters`, `searchPlaceholder`, and feature-flag-gate in `buildAllFilters()`\n- `apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts` - Feature-flag-gate in `buildAllFilters()`\n\n_Include if `$0` is `desktop` or `all`:_\n\n- `apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts` - Add `ConfigService`, `combineLatest` with feature flag\n\n_Include if `$0` is `browser` or `all`:_\n\n- `apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts` - Add `ConfigService`, feature-flag-gate `cipherTypes`\n\n**New item menus (feature-flag-gated):**\n\n_Always included (shared):_\n\n- `libs/common/src/vault/types/cipher-menu-items.ts` - Add menu item entry for new type\n- `libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts` - Add `canCreate<Type> = input(false)` signal, gate in `cipherMenuItems` observable\n\n_Include if `$0` is `web` or `all`:_\n\n- `apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts` - Add `canCreate<Type>$` observable from feature flag\n- `apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.html` - Bind `[canCreate<Type>]` to `<vault-new-cipher-menu>`\n\n_Include if `$0` is `browser` or `all`:_\n\n- `apps/browser/src/vault/popup/components/vault/new-item-dropdown/new-item-dropdown.component.ts` - Add `ConfigService`, `combineLatest` with feature flag\n\n**Localization (add i18n keys):**\n\n_Include only locale files for `$0`. If `$0` is `all`, include all three:_\n\n- `apps/web/src/locales/en/messages.json` _(web)_\n- `apps/desktop/src/locales/en/messages.json` _(desktop)_\n- `apps/browser/src/_locales/en/messages.json` _(browser)_\n\n**Linked fields (if applicable):**\n\n- `libs/common/src/vault/enums/linked-id-type.enum.ts`\n\n**Autofill (if applicable — only if `$0` is `browser` or `all`):**\n\n- List relevant autofill files from `apps/browser/src/autofill/` only if the type supports\n  autofill\n\n**Restricted item types (if applicable):**\n\nRestricted item type enforcement is used across all clients. Include files for `$0`:\n\n_Always included (shared):_\n\n- `libs/common/src/vault/services/vault-settings/vault-settings.service.ts` - Restricted types service\n\n_Include if `$0` is `web` or `all`:_\n\n- `apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.ts` - Policy configuration UI\n- `apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.html`\n\n_Include if `$0` is `browser` or `all`:_\n\n- `apps/browser/src/vault/popup/components/vault/item-more-options/item-more-options.component.ts` - Restricted type checks\n\n_Include if `$0` is `cli` or `all`:_\n\n- `apps/cli/src/vault/create.command.ts` - Restricted type checks\n- `apps/cli/src/commands/list.command.ts` - Restricted type checks\n- `apps/cli/src/commands/get.command.ts` - Restricted type checks\n\n### 4. Localization Keys\n\nList all i18n keys that need to be added. At minimum:\n\n- Type label\n- Field labels for each type-specific field\n\n### 5. Tests\n\nList all test files that need to be created or updated:\n\n- `libs/common/src/vault/enums/cipher-type.spec.ts`\n- `libs/common/src/vault/models/domain/cipher.spec.ts`\n- `libs/common/src/vault/models/domain/<type>.spec.ts` (new)\n- `libs/common/src/vault/models/view/cipher.view.spec.ts`\n- `libs/common/src/vault/services/cipher.service.spec.ts`\n- `libs/common/src/vault/services/cipher-sdk.service.spec.ts`\n- `libs/common/src/vault/icon/build-cipher-icon.spec.ts`\n- `libs/common/src/models/export/cipher.export.spec.ts`\n- `libs/vault/src/cipher-form/components/cipher-form.component.spec.ts`\n- `libs/vault/src/cipher-view/cipher-view.component.spec.ts`\n- Form section component spec (new)\n\n### 6. Recommended Implementation Order\n\nRecommended implementation order, customized for this specific type. Only include steps relevant\nto `$0` (shared steps are always included):\n\n1. Core enum addition _(shared)_\n2. Feature flag registration _(shared)_\n3. Model stack (API, Data, Domain, View, Export if applicable) _(shared)_\n4. Container switch updates _(shared)_\n5. SDK bindings (`toSdk*`/`fromSdk*`) _(shared)_\n6. Localization keys _(`$0`)_\n7. Shared UI (icon, menu items) _(shared)_\n8. Vault filters with feature flag gating — CRITICAL for ciphers to appear _(shared + `$0`)_\n9. New item menus with feature flag gating _(shared + `$0`)_\n10. Per-app UI (form section, view section) _(shared)_\n11. Context menu / copy actions — see Section 8 _(shared + `$0`)_\n12. CLI _(only if `$0` is `cli` or `all`)_\n13. Autofill _(only if `$0` is `browser` or `all`)_\n14. Tests _(shared + `$0`)_\n\n### 7. Risks and Considerations\n\n- Cross-repo coordination requirements\n- Feature flag rollout strategy\n- Backward compatibility concerns\n- Any fields that need special encryption handling (reminder: no new encryption logic in clients)\n- Performance considerations for large vaults\n- **i18n key reuse** - Before adding new locale keys, check whether existing keys already have the\n  desired display value. If an existing key has the same message text, reuse it instead of creating\n  a duplicate. Only create new keys when no existing key matches.\n\n### 8. Context Menu / Copy Actions\n\nEach cipher type can expose copiable fields in the vault list item context menus (right-click / more\nmenu). Include only the sections relevant to `$0`.\n\n#### Core Infrastructure (always included)\n\n| File                                                    | What to add                                                                                                                                                                                                                                     |\n| ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `libs/vault/src/services/copy-cipher-field.service.ts`  | Add field names to the `CopyAction` type union. Add entries to the `CopyActions` record with `typeI18nKey` (i18n key for the toast message), `protected` (whether it requires password re-prompt), and optional `event` (for event collection). |\n| `libs/common/src/vault/utils/cipher-view-like-utils.ts` | Add cases to `hasCopyableValue()` that check whether the cipher has a non-empty value for each copiable field.                                                                                                                                  |\n\n#### Browser (include if `$0` is `browser` or `all`)\n\n| File                                                                                              | What to add                                                                                                                                                              |\n| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `apps/browser/src/vault/popup/components/vault/item-copy-action/item-copy-actions.component.ts`   | Add a `singleCopyable<Type>` getter (for single-field quick copy button), a `has<Type>Values` getter, and a `getNumberOf<Type>Values()` method. Follow the Card pattern. |\n| `apps/browser/src/vault/popup/components/vault/item-copy-action/item-copy-actions.component.html` | Add a section using `@if` syntax (NOT `*ngIf`) with the single/multi field pattern.                                                                                      |\n\n#### Web (include if `$0` is `web` or `all`)\n\n| File                                                                            | What to add                                                                                                               |\n| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |\n| `apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts`   | Add `is<Type>Cipher` and `hasVisible<Type>Options` getters. Add `hasVisible<Type>Options` to the `showMenuDivider` check. |\n| `apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html` | Add copy buttons using `@if` syntax with `appCopyField` directive.                                                        |\n\n#### Desktop (include if `$0` is `desktop` or `all`)\n\n| File                                                                            | What to add                                                                                                                                                                              |\n| ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `apps/desktop/src/vault/app/vault-v3/vault-items/vault-cipher-row.component.ts` | Add a `CipherType.<Type>` case to the `copyFields` computed signal, returning `CopyFieldConfig[]` entries. This is the most modern pattern — uses a computed signal rather than getters. |\n\n#### Critical Warnings\n\n- **CLI has no copy menu UI** — do not add copy-related i18n keys to the CLI locale.\n- **Only expose fields that should be copiable** — not every cipher field needs a copy action. Check\n  with product requirements for which fields get copy buttons.\n","frontmatter":{"name":"cipher-type-planner","description":"Plans the creation or modification of a cipher type (vault item type) across the Bitwarden clients monorepo. Use this skill when a user wants to add a new cipher type, modify an existing cipher type, or asks about what is needed to implement a cipher type. DO NOT invoke for general vault or cipher questions unrelated to adding or changing a cipher type.","user-invocable":true,"argument-hint":"target-client"},"isInternal":false,"tokens":3791,"sizeBytes":17184},{"name":"token-auth.md","path":".claude/skills/create-hec-event-integration/example/token-auth.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/create-hec-event-integration/example/token-auth.md","title":"Example Skill","category":"anthropic-skill","format":"markdown","content":"# Example Skill\n\nPath: `.claude/skills/create-hec-event-integration/example/token-auth.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/create-hec-event-integration/example/token-auth.md)","tokens":300,"sizeBytes":244},{"name":"SKILL.md","path":".claude/skills/create-hec-event-integration/SKILL.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/create-hec-event-integration/SKILL.md","title":"create-hec-event-integration","category":"anthropic-skill","format":"markdown","content":"---\nname: create-hec-event-integration\ndescription: Use when adding a new HEC (HTTP Event Collector) event integration to the Bitwarden web client. Implements the Splunk token authentication model (Bearer token + URI). Covers feature flag setup and card registration behind the flag. Does not apply to API key integrations or integrations requiring a custom connect dialog.\n---\n\n# Create HEC Event Integration (Token Auth)\n\n## Step 1 - Prompts\n\nAsk these questions one at a time — wait for each answer before proceeding.\n\n**Prompt 1 — Service name:** \"What is the service name for this integration?\" (e.g. `Splunk`, `CrowdStrike`, `Panther`)\n\nUse the answer as `<ServiceName>` throughout. The string value in the constant must exactly match what you use as the card's `name` in Step 4 — a mismatch silently saves the config with the wrong service name.\n\n**Prompt 2 — Authentication:** \"How is this integration authenticated?\" (e.g. `Token`, `API key`)\n\n- **If Token** — continue with the steps below.\n- **If anything else** — stop and inform the user: \"This skill currently only supports token-based authentication. Support for other authentication methods hasn't been added yet.\"\n\n**Prompt 3 — Logos:** \"Do you have the integration logo(s) ready to provide?\"\n\n- **If yes** — ask for the light-mode SVG file path, and optionally a dark-mode SVG path. Copy both to `apps/web/src/images/integrations/` using the naming convention `logo-<service-name-kebab>-color.svg` and `logo-<service-name-kebab>-darkmode.svg`. Use those filenames in Step 4.\n- **If no** — use placeholder paths in Step 4 and add a `// TODO: add logo before shipping` comment.\n\n## Step 2 — Add service name constant\n\n**File:** `bitwarden_license/bit-common/src/dirt/organization-integrations/models/organization-integration-service-type.ts`\n\nAdd to `OrganizationIntegrationServiceName`:\n\n```typescript\nexport const OrganizationIntegrationServiceName = Object.freeze({\n  CrowdStrike: \"CrowdStrike\",\n  Datadog: \"Datadog\",\n  Huntress: \"Huntress\",\n  <ServiceName>: \"<ServiceName>\", // ← add here\n} as const);\n```\n\n## Step 3 — Add feature flag\n\n**File:** `libs/common/src/enums/feature-flag.enum.ts`\n\nAdd the enum entry and its default. The enum key is PascalCase; the string value is kebab-case (e.g. `CrowdStrike` → `crowdstrike`, `Sumo Logic` → `sumo-logic`):\n\n```typescript\n// In the FeatureFlag enum:\nEventManagementFor<ServiceName> = \"event-management-for-<service-name-kebab>\",\n\n// In the defaultFlags object:\n[FeatureFlag.EventManagementFor<ServiceName>]: FALSE,\n```\n\nExample for `Panther`:\n\n```typescript\nEventManagementForPanther = \"event-management-for-panther\",\n[FeatureFlag.EventManagementForPanther]: FALSE,\n```\n\n## Step 4 — Register the card behind the feature flag\n\n**File:** `bitwarden_license/bit-web/src/app/dirt/organization-integrations/organization-integrations.resolver.ts`\n\nIf logos were provided, copy them to `apps/web/src/images/integrations/` first, then use the actual filenames below. If not, use the placeholder paths with the TODO comment:\n\n```typescript\nconst <serviceName>FeatureEnabled = await firstValueFrom(\n  this.configService.getFeatureFlag$(FeatureFlag.EventManagementFor<ServiceName>),\n);\n\nif (<serviceName>FeatureEnabled) {\n  integrations.push({\n    name: OrganizationIntegrationServiceName.<ServiceName>, // must match Step 1 exactly\n    linkURL: \"https://bitwarden.com/help/<service-name>-siem/\",\n    image: \"../../../../../../../images/integrations/logo-<service-name>-color.svg\", // TODO: add logo before shipping (if not yet provided)\n    imageDarkMode: \"../../../../../../../images/integrations/logo-<service-name>-darkmode.svg\", // TODO: add logo before shipping (omit if no dark mode variant)\n    type: IntegrationType.EVENT,\n    canSetupConnection: true,\n    integrationType: OrganizationIntegrationType.Hec,\n  });\n}\n```\n\nNo changes needed to `IntegrationCardComponent` — new HEC services fall into the existing `else` branch, which calls `openHecConnectDialog` → `saveHec` → `deleteHec`. These methods already call `buildHecConfiguration` and `buildHecTemplate` using the card's `name` as the service name.\n\n## Step 5 — Add tests\n\n**File:** `bitwarden_license/bit-common/src/dirt/organization-integrations/models/integration-builder.spec.ts`\n\nAdd one `it` block inside the existing `describe(\"buildHecConfiguration\", ...)` block, and one inside `describe(\"buildHecTemplate\", ...)`. Use typed property access — do not use `JSON.parse`:\n\n```typescript\n// Inside describe(\"buildHecConfiguration\", ...)\nit(\"should work with <ServiceName> service name\", () => {\n  const config = OrgIntegrationBuilder.buildHecConfiguration(\n    \"https://test.<servicename>.com/hec\",\n    \"test-token\",\n    OrganizationIntegrationServiceName.<ServiceName>,\n  );\n\n  expect(config).toBeInstanceOf(HecConfiguration);\n  expect((config as HecConfiguration).uri).toBe(\"https://test.<servicename>.com/hec\");\n  expect((config as HecConfiguration).scheme).toBe(\"Bearer\");\n  expect((config as HecConfiguration).token).toBe(\"test-token\");\n  expect(config.bw_serviceName).toBe(OrganizationIntegrationServiceName.<ServiceName>);\n});\n\n// Inside describe(\"buildHecTemplate\", ...)\nit(\"should work with <ServiceName> service name\", () => {\n  const template = OrgIntegrationBuilder.buildHecTemplate(\n    \"test-index\",\n    OrganizationIntegrationServiceName.<ServiceName>,\n  );\n\n  expect(template).toBeInstanceOf(HecTemplate);\n  expect((template as HecTemplate).index).toBe(\"test-index\");\n  expect(template.bw_serviceName).toBe(OrganizationIntegrationServiceName.<ServiceName>);\n});\n```\n\n## Step 6 — Run unit tests\n\nRun the unit tests for the spec file and confirm they all pass before finishing:\n\n```bash\nnpx jest bitwarden_license/bit-common/src/dirt/organization-integrations/models/integration-builder.spec.ts\n```\n\nAll tests must pass. If any fail, fix them before proceeding.\n\n## Common Mistakes\n\n| Mistake                                                                 | Fix                                                                                                   |\n| ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |\n| `name` in card doesn't match `OrganizationIntegrationServiceName` value | They must be identical strings — `saveHec()` casts the name directly                                  |\n| Feature flag default not set to `FALSE`                                 | Always add the default entry in `defaultFlags`; new flags without a default will not work correctly   |\n| Kebab-case mismatch in flag string                                      | Convert consistently: lowercase, spaces → hyphens                                                     |\n| Adding a new `OrganizationIntegrationType`                              | Not needed — all HEC services share `OrganizationIntegrationType.Hec`                                 |\n| Creating a new config/template class                                    | Not needed — `HecConfiguration` and `HecTemplate` handle all HEC services                             |\n| Referencing an image path without copying the file                      | Copy SVGs to `apps/web/src/images/integrations/` first; if logos aren't ready, leave the TODO comment |\n","frontmatter":{"name":"create-hec-event-integration","description":"Use when adding a new HEC (HTTP Event Collector) event integration to the Bitwarden web client. Implements the Splunk token authentication model (Bearer token + URI). Covers feature flag setup and card registration behind the flag. Does not apply to API key integrations or integrations requiring a custom connect dialog."},"isInternal":false,"tokens":1600,"sizeBytes":7364},{"name":"SKILL.md","path":".claude/skills/create-pull-request/SKILL.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/create-pull-request/SKILL.md","title":"create-pull-request","category":"anthropic-skill","format":"markdown","content":"---\nname: create-pull-request\nversion: 0.1.0\ndescription: Pull request creation workflow for Bitwarden Clients. Use when creating PRs, writing PR descriptions, or preparing branches for review. Triggered by \"create PR\", \"pull request\", \"open PR\", \"gh pr create\", \"PR description\".\n---\n\n# Create Pull Request\n\n## PR Title Format\n\n```\n[PROJ-XXXXX] Short imperative summary\n```\n\n**Examples:**\n\n- `[PM-12345] Add passkey support to vault item creation`\n- `[CL-1124] Increase badge max-width`\n- `[PM-33446] Ensure attachments are cleared on clone`\n\n**Rules:**\n\n- Include the Jira ticket prefix (e.g. `PM-`, `CL-`)\n- Keep under 70 characters total\n- Use imperative mood in the summary\n\n---\n\n## PR Body Template\n\n**IMPORTANT:** Always follow the repo's PR template at `.github/PULL_REQUEST_TEMPLATE.md`. Add TODO items to the screenshots section for each scenario that should be captured if any.\n\n---\n\n## Pre-PR Checklist\n\n1. **All tests pass**: Run `npm test`\n2. **Lint clean**: Run `npm run lint`\n3. **No unintended changes**: Check `git diff origin/main...HEAD` for unexpected files\n4. **Branch up to date**: Rebase on `main` if needed\n\n---\n\n## Creating the PR\n\n```bash\n# Ensure branch is pushed\ngit push -u origin <branch-name>\n\n# Create PR as draft by default (body follows .github/PULL_REQUEST_TEMPLATE.md)\ngh pr create --draft --title \"[PM-XXXXX] Short summary\" --body \"<fill in from PR template>\"\n```\n\n**Default to draft PRs.** Only create a non-draft (ready for review) PR if the user explicitly requests it.\n\n---\n\n## Base Branch\n\n- Default target: `main`\n- Check with team if targeting a feature branch instead\n","frontmatter":{"name":"create-pull-request","version":"0.1.0","description":"Pull request creation workflow for Bitwarden Clients. Use when creating PRs, writing PR descriptions, or preparing branches for review. Triggered by \"create PR\", \"pull request\", \"open PR\", \"gh pr create\", \"PR description\"."},"isInternal":false,"tokens":409,"sizeBytes":1614},{"name":"SKILL.md","path":".claude/skills/evaluating-sdk-internal-updates/SKILL.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/evaluating-sdk-internal-updates/SKILL.md","title":"evaluating-sdk-internal-updates","category":"anthropic-skill","format":"markdown","content":"---\nname: evaluating-sdk-internal-updates\ndescription: Evaluates a bitwarden/clients \"Update sdk-internal to\" PR against the sdk-internal commit range for compile-time and runtime breaking changes, maps affected symbols to TypeScript call sites, and applies clear in-scope fixes. Use when reviewing an SDK bump PR, an @bitwarden/sdk-internal version change in package.json, or triaging sdk-internal breaking changes. Requires a sibling `bitwarden/sdk-internal` clone.\nallowed-tools:\n  - Bash(node .claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs *)\n  - Bash(npm run test:types)\n  - Bash(npm run lint:fix)\n  - Bash(npm test -- *)\n  - Bash(npx prettier *)\n  - Bash(gh pr diff:*)\n  - Bash(gh pr view:*)\n  - Bash(git -C * log *)\n  - Bash(git -C * show *)\n  - Bash(git grep:*)\n  - Bash(git add:*)\n  - Bash(git commit:*)\n  - Bash(grep:*)\n  - Read\n  - Grep\n  - Glob\n  - Edit\n  - Write\n  - Skill(writing-client-code)\n  - Skill(bitwarden-delivery-tools:committing-changes)\n---\n\n# Evaluating sdk-internal Updates\n\n**Identify both compile-time and runtime breaks before fixing anything — fixing the first break found is not finishing.** Steps 4-7 cover the entire commit range, step 8 reports it, and step 9 starts only after that.\n\nThis SDK is the boundary where Protected Data becomes Vault Data. A fix must not send a decrypted value to an API service, log one, or move a decrypt out of the SDK into TypeScript. `.claude/CLAUDE.md` forbids new encryption logic in this repo, so a bump that appears to require some is a finding to report, not a fix to write. [Security definitions](https://contributing.bitwarden.com/architecture/security/definitions).\n\n## Identify\n\nBinding surface facts specific to this SDK: the pin is two npm versions in the root `package.json`, `@bitwarden/sdk-internal` and `@bitwarden/commercial-sdk-internal`, always the same string — `npm run lint:sdk-internal-versions` fails if they differ. The version encodes no commit; the sdk-internal SHA lives in `VERSION` inside the tarball. Nothing generated is committed, here or upstream: the API record is `bitwarden_wasm_internal.d.ts` inside the published package, whose root and `node/` copies are byte-identical, and `bitwarden_wasm_internal_bg.wasm.d.ts` is raw ABI and never a finding.\n\nThe annotations behind this surface are `#[wasm_bindgen]` and `derive(..., Tsify)`. `#[uniffi::export]` is mobile-only and largely disjoint, so a uniffi sweep under-reports this surface badly. Serde attributes are part of the emitted type: `rename`, `rename_all`, `skip` and `Option` changes alter the `.d.ts` with no Rust signature change, and a `features = [\"wasm\"]` edit in `crates/bitwarden-wasm-internal/Cargo.toml` adds or removes types wholesale. Enum members are wire values (`CipherType.Login = 1`); a renumbering compiles clean at every call site and corrupts data.\n\nMost classes have a `private constructor()` and are reachable only through a parent client accessor (`sdk.user_crypto_management()`), so a renamed accessor breaks callers with the class itself unchanged. `tsconfig.base.json` sets `skipLibCheck: true`, so only call sites are checked. `bitwarden_license/bit-common/src/platform/sdk/sdk-alias.d.ts` re-exports the commercial package under the `@bitwarden/sdk-internal` module name and ESLint forbids importing it directly, so call sites grep identically for both packages.\n\n1. `gh pr diff <PR> -R bitwarden/clients -- package.json` → the old and new version.\n2. Locate the `bitwarden/sdk-internal` clone in a sibling directory — `../sdk-internal`, where CI places it. If there is none, stop and tell the user it is a required prerequisite for this skill; do not clone it yourself.\n3. Read the baseline type check. **In CI it has already run and the prompt names its log; grep it for `error TS` and treat that as this step's output. Do not run it.** Locally, `npm run test:types`. Note any failure and continue — a clean run rules out compile breaks only where the baseline looks, and step 7 is where it does not look.\n4. `node .claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs <old-version> <new-version>`, then again with `--commercial`. It prints RANGE (including the sdk-internal SHA pair step 5 needs), REMOVED, ADDED and MUTATED, keyed per member: a class method, interface property and enum member each get their own `Owner.member` entry. In ADDED, only a pre-existing owner is a break — a new non-optional property on an existing interface breaks every construction site.\n5. `git -C ../sdk-internal log --reverse --oneline <old-sha>..<new-sha>`, then `show` each commit. Classify per hunk, not per commit — a commit with one additive headline change can still carry a second, unrelated breaking hunk. A hunk that only touches a macro invocation does not show the surface; read the macro's definition before classifying it. This is also what explains a property the step 4 diff shows renamed or gone.\n6. For every symbol from step 4, `git grep -n '<Symbol>'` from the repo root for the bare name — no module list and no import-prefix filter, since everything arrives as a plain `@bitwarden/sdk-internal` import and both apps and `bitwarden_license` consume it. Use `git grep`, not `grep -r`, which also searches `node_modules/@bitwarden/*sdk-internal/` and reports the bindings themselves as call sites. A surface change stays a candidate until a call site makes it a finding.\n7. Three sets of call sites step 3 does not cover. Read each one the range touches.\n   - `apps/` and `bitwarden_license/` get no dedicated `tsc` leg and ride only on `tsc-strict`, which filters by file, so an ordinary type error inside a `@ts-strict-ignore` file is discarded: `git grep -l '@bitwarden/sdk-internal' -- 'apps/**' 'bitwarden_license/**'`, then check that file list for `@ts-strict-ignore` with the `Grep` tool rather than piping into `xargs`, which carries no grant of its own.\n   - The root `tsconfig.json` excludes app spec files but not `bitwarden_license` ones, so `git grep -l '@bitwarden/sdk-internal' -- 'apps/**/*.spec.ts'` is type-checked by nothing; the `bitwarden_license` equivalent is still covered by `tsc-strict`.\n   - `git grep -l 'jest.mock(\"@bitwarden/sdk-internal\"'` — factory-form mocks are not checked against the real module and drift silently. `libs/common/src/platform/spec/mock-sdk.service.ts` and `libs/common/spec/jest-sdk-client-factory.ts` are structural and break loudest, the latter on any `PasswordManagerClient` constructor change.\n8. Report under these headings, which are the same across every repo running this evaluation: `## SDK bump evaluated`, `## Compile-time breaks`, `## Runtime considerations` (wire-value and serialization findings go here; omit the heading when there are none), `## Everything else in range — confirmed safe`, `## Commit`. Give each finding its commit, symbol and call sites, and state whether the type check passed inside `## Compile-time breaks` rather than as its own section. Cite sdk-internal commits and PRs as `bitwarden/sdk-internal#<N>` or a full URL — a bare `#<N>` copied from a commit subject auto-links into bitwarden/clients and tags an unrelated PR.\n\n## Resolve\n\n9. Fix anything found, compile-time or runtime, whenever the correct behavior is clear and in scope. For a new required argument or method with no existing consumer, grep the underlying concept rather than the new name, which does not exist yet; with no consumer, stub it — an empty body with a `// no-op` comment, or a default return, never a `throw`, which turns a stub into a crash. A sibling's structure is a template; its behavior is not evidence for yours, so read the SDK to choose between two plausible argument values. Never edit `package.json`: the pin is the bump's output, not the fix's. If unsure, report it instead of guessing, along with anything needing a product decision. Invoke `Skill(writing-client-code)` first if the fix is not purely mechanical, then commit with `Skill(bitwarden-delivery-tools:committing-changes)`.\n10. `npm run lint:fix`, then `npx prettier --write <edited files>`, then `npm run test:types`, then `npm test -- <edited paths> --maxWorkers=2`. Run all four even in CI: `lint.yml`'s `npm run lint` step runs ESLint as well as Prettier, and `--maxWorkers=2` is this repo's workaround for Jest exhausting memory.\n","frontmatter":{"name":"evaluating-sdk-internal-updates","description":"Evaluates a bitwarden/clients \"Update sdk-internal to\" PR against the sdk-internal commit range for compile-time and runtime breaking changes, maps affected symbols to TypeScript call sites, and applies clear in-scope fixes. Use when reviewing an SDK bump PR, an @bitwarden/sdk-internal version change in package.json, or triaging sdk-internal breaking changes. Requires a sibling `bitwarden/sdk-internal` clone.","allowed-tools":["Bash(node .claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs *)","Bash(npm run test:types)","Bash(npm run lint:fix)","Bash(npm test -- *)","Bash(npx prettier *)","Bash(gh pr diff:*)","Bash(gh pr view:*)","Bash(git -C * log *)","Bash(git -C * show *)","Bash(git grep:*)","Bash(git add:*)","Bash(git commit:*)","Bash(grep:*)","Read","Grep","Glob","Edit","Write","Skill(writing-client-code)","Skill(bitwarden-delivery-tools:committing-changes)"],"allowedTools":["Bash(node .claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs *)","Bash(npm run test:types)","Bash(npm run lint:fix)","Bash(npm test -- *)","Bash(npx prettier *)","Bash(gh pr diff:*)","Bash(gh pr view:*)","Bash(git -C * log *)","Bash(git -C * show *)","Bash(git grep:*)","Bash(git add:*)","Bash(git commit:*)","Bash(grep:*)","Read","Grep","Glob","Edit","Write","Skill(writing-client-code)","Skill(bitwarden-delivery-tools:committing-changes)"]},"allowedTools":["Bash(node .claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs *)","Bash(npm run test:types)","Bash(npm run lint:fix)","Bash(npm test -- *)","Bash(npx prettier *)","Bash(gh pr diff:*)","Bash(gh pr view:*)","Bash(git -C * log *)","Bash(git -C * show *)","Bash(git grep:*)","Bash(git add:*)","Bash(git commit:*)","Bash(grep:*)","Read","Grep","Glob","Edit","Write","Skill(writing-client-code)","Skill(bitwarden-delivery-tools:committing-changes)"],"isInternal":false,"tokens":2065,"sizeBytes":8332},{"name":"evals.json","path":".claude/skills/figma-to-angular/evals/evals.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/figma-to-angular/evals/evals.json","title":"Evals Skill","category":"anthropic-skill","format":"json","content":"# Evals Skill\n\nPath: `.claude/skills/figma-to-angular/evals/evals.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/figma-to-angular/evals/evals.json)","tokens":300,"sizeBytes":208},{"name":"SKILL.md","path":".claude/skills/figma-to-angular/SKILL.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/figma-to-angular/SKILL.md","title":"figma-to-angular","category":"anthropic-skill","format":"markdown","content":"---\nname: figma-to-angular\ndescription: >\n  Converts Figma designs into production Angular components with Storybook stories for the Bitwarden\n  Clients monorepo. Use this skill whenever the user provides a Figma URL and wants to create an\n  Angular component, or mentions \"implement this design\", \"create a component from Figma\",\n  \"build this from the design spec\", or similar. Also trigger when the user pastes a Figma link\n  and asks for a component, even if they don't say \"Figma\" explicitly.\n---\n\n# Figma to Angular Component\n\nThis skill turns a Figma design spec into a fully implemented Angular component with Storybook\nstories in the Bitwarden Clients monorepo. The output should match the design visually while\nfollowing all codebase conventions.\n\n## Workflow\n\n### Phase 1: Extract the design\n\nCall `get_design_context` from the Figma MCP server. Extract the fileKey and nodeId from the URL:\n\n- `figma.com/design/:fileKey/:fileName?node-id=:nodeId` → convert `-` to `:` in nodeId\n- Pass `clientFrameworks: \"angular\"` and `clientLanguages: \"typescript,html,css\"`\n\nThe response contains reference code (React+Tailwind), a screenshot, and design metadata. Treat the\ncode as a **structural reference only** — it must be fully converted to Angular.\n\n### Phase 2: Explore the codebase\n\nBefore writing any code, understand the conventions of the target location. Use an Explore agent to\ndiscover:\n\n1. **Component file patterns** — look at 2–3 sibling components in the same directory. Note the\n   file naming, module structure, barrel exports, and whether they use standalone components.\n2. **Existing components to reuse** — search for components that match parts of the design (labels,\n   buttons, inputs, icons). Reuse them instead of reimplementing.\n3. **Tailwind configuration** — read the relevant `tailwind.config` to understand the prefix\n   (commonly `tw-`), available design tokens, and color system.\n4. **Storybook patterns** — check how sibling stories are structured: imports, `moduleMetadata`,\n   `I18nMockService` usage, `argTypes`.\n5. **i18n setup** — find the `I18nPipe` import path and the locale messages file to add new keys.\n\nThis exploration is critical. Skipping it leads to components that don't match the codebase and\nrequire extensive rework.\n\n### Phase 3: Map design tokens\n\nCreate a mapping from Figma's design values to the codebase's Tailwind tokens. The Figma response\nincludes raw CSS values (hex colors, px sizes). These must be translated to semantic tokens.\n\nCommon mappings for Bitwarden:\n\n| Figma concept      | Where to look                                               |\n| ------------------ | ----------------------------------------------------------- |\n| Colors (hex)       | `tailwind.config` → `colors`, `bg`, `fg`, `border` sections |\n| Font sizes         | `tailwind.config` → `extend.fontSize`                       |\n| Spacing / padding  | Standard Tailwind scale with prefix                         |\n| Border radius      | Standard Tailwind (`rounded-*`)                             |\n| Component variants | Existing component source (e.g. `base-button.directive.ts`) |\n\nNever use raw hex values. Always map to a semantic token. If no exact match exists, use the closest\navailable token and note the discrepancy.\n\n### Phase 4: Plan the component\n\nBefore writing code, create a plan covering:\n\n- **Selector** — follows the relevant prefix convention\n- **Inputs and outputs** — use Angular signal-based `input()` and `output()` functions\n- **Content projection** — identify slots for `<ng-content>` (labels, hints, actions)\n- **Existing component reuse** — which existing components to import and use\n- **i18n keys** — list all user-visible strings that need localization\n- **Accessibility** — keyboard navigation, ARIA attributes, focus management\n\nPresent this plan to the user and get approval before writing files.\n\n### Phase 5: Implement\n\nCreate these files following the patterns discovered in Phase 2:\n\n#### Component TypeScript (`component-name.component.ts`)\n\n```typescript\n@Component({\n  selector: \"bit-component-name\",\n  templateUrl: \"./component-name.component.html\",\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  imports: [/* discovered dependencies */],\n  host: {\n    class: \"tw-block\",\n  },\n})\n```\n\nKey conventions:\n\n- `ChangeDetectionStrategy.OnPush` always\n- Signal-based inputs: `input()`, `input.required<T>()`, `input(default, { transform: booleanAttribute })`\n- Signal-based outputs: `output<T>()`\n- Internal state with `signal()` and `viewChild()`\n- Protected methods for template bindings, private for internal logic\n- No TypeScript enums — use `Object.freeze({ ... } as const)` with a same-name type alias (ADR-0025).\n  Even for simple string unions, prefer the const object pattern for consistency.\n\n#### Component template (`component-name.component.html`)\n\n- Use new Angular control flow: `@if`, `@for`, `@switch`\n- Avoid deprecated attributes like `ngClass` and `ngStyle`, prefer `class` and `style`.\n- All Tailwind classes use the library's prefix (check `tailwind.config`)\n- All user-visible strings go through `{{ \"key\" | i18n }}` or `{{ \"key\" | i18n: param }}`\n- Reuse existing components (`bit-label`, `bitButton`, `bit-hint`, etc.)\n\n#### Barrel export (`index.ts`)\n\n```typescript\nexport { ComponentNameComponent } from \"./component-name.component\";\n```\n\n#### Storybook stories (`component-name.stories.ts`)\n\nEvery component gets stories. Follow the exact patterns from sibling components:\n\n```typescript\nimport { Meta, moduleMetadata, StoryObj } from \"@storybook/angular\";\nimport { I18nService } from \"@bitwarden/common/platform/abstractions/i18n.service\";\nimport { I18nMockService } from \"../utils/i18n-mock.service\";\n\nexport default {\n  title: \"Component Library/ComponentName\",\n  component: ComponentNameComponent,\n  decorators: [\n    moduleMetadata({\n      imports: [/* component + dependencies */],\n      providers: [\n        {\n          provide: I18nService,\n          useFactory: () =>\n            new I18nMockService({\n              // All i18n keys used by the component\n              // Use __$1__ for parameter placeholders\n            }),\n        },\n      ],\n    }),\n  ],\n  parameters: {\n    design: {\n      type: \"figma\",\n      url: \"/* original Figma URL */\",\n    },\n  },\n} as Meta<ComponentNameComponent>;\n```\n\nInclude stories for:\n\n- Default state\n- Key variant states (if applicable)\n- Edge cases visible in the design (disabled, loading, empty, etc.)\n\n#### Unit tests (`component-name.component.spec.ts`)\n\nWrite unit tests when the component has behavior that Storybook stories alone can't verify. Stories\ncover visual rendering and variant appearance — don't duplicate that. Tests should focus on:\n\n- **Computed state and logic** — e.g. a rating component rounding fractional values, a progress bar\n  clamping percentages to 0–100, variant-to-class mappings\n- **Event emission** — outputs fire with the correct payload (files selected, rating changed, toast\n  dismissed)\n- **Input validation / edge cases** — boundary values, empty inputs, invalid types rejected\n- **Accessibility attributes** — correct `role`, `aria-label`, `aria-live` values change with state\n- **Keyboard interaction** — Enter/Space triggers actions, arrow keys navigate\n\nSkip tests for components that are purely presentational with no logic beyond displaying inputs\n(e.g. a simple badge or divider). Look at sibling `*.spec.ts` files to match the test setup\npatterns (TestBed configuration, imports, mock providers).\n\n#### Update barrel exports\n\nAdd `export * from \"./component-name\";` to the parent directory's `index.ts`, inserted\nalphabetically.\n\n### Phase 6: Verify in Storybook\n\nUse Playwright MCP tools to verify the component renders correctly:\n\n1. Navigate to the story's iframe URL:\n   `http://localhost:6006/iframe.html?id=component-library-component-name--default&viewMode=story`\n2. Take a snapshot to confirm the component rendered without errors\n3. Take a screenshot and compare it against the Figma design screenshot\n4. Test interactive behavior:\n   - Click buttons and interactive elements\n   - Verify correct responses (dialogs, state changes, etc.)\n5. Check the disabled story if applicable\n\nIf Storybook isn't running or Playwright isn't available, tell the user and suggest they verify\nmanually.\n\n### Phase 7: Iterate\n\nPresent the screenshot to the user alongside the original Figma screenshot. Ask if adjustments are\nneeded. Common refinements:\n\n- Token mismatches (wrong shade, spacing off)\n- Missing hover/focus states\n- Accessibility gaps\n- i18n strings that need rewording\n\n## Key principles\n\n- **Never hardcode user-visible strings.** Every string goes through `I18nPipe`. This includes\n  aria-labels and other accessibility text — they need localization too. Add keys to the locale file\n  and provide mocks in stories.\n- **Reuse existing components aggressively.** If the design has a button, use `bitButton`. If it\n  has a label, use `bit-label`. Don't reimplement what exists.\n- **Explore before you build.** The codebase exploration step prevents most rework. Patterns vary\n  between apps and libraries — always check the specific target directory.\n- **Semantic tokens over raw values.** Map every Figma color, spacing, and font size to the\n  codebase's design token system. Raw hex values break theming.\n- **Use native HTML patterns for file inputs.** For file upload components, use a `<label for>`\n  element pointing at a hidden `<input type=\"file\">` rather than programmatic `.click()`. The\n  codebase has an `AriaDisabledClickCaptureService` that intercepts programmatic clicks on certain\n  elements, causing silent failures.\n- **No NgModules.** All new components must be standalone (`imports: [...]` in the `@Component`\n  decorator). Do not create `*.module.ts` files. The codebase is moving away from NgModules.\n- **Plan before you code.** The plan step catches API design issues early. It's cheaper to fix a\n  plan than to rewrite files.\n","frontmatter":{"name":"figma-to-angular","description":"Converts Figma designs into production Angular components with Storybook stories for the Bitwarden Clients monorepo. Use this skill whenever the user provides a Figma URL and wants to create an Angular component, or mentions \"implement this design\", \"create a component from Figma\", \"build this from the design spec\", or similar. Also trigger when the user pastes a Figma link and asks for a component, even if they don't say \"Figma\" explicitly.\n"},"isInternal":false,"tokens":2223,"sizeBytes":10013},{"name":"SKILL.md","path":".claude/skills/fix-angular-fixmes/SKILL.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/fix-angular-fixmes/SKILL.md","title":"fix-angular-fixmes","category":"anthropic-skill","format":"markdown","content":"---\nname: fix-angular-fixmes\ndescription: Resolves eslint-disable suppression comments throughout the Bitwarden clients codebase by fixing the underlying issue. Use when the user asks to \"fix FIXMEs\", \"fix eslint suppressions\", \"clean up eslint-disable-next-line\", \"resolve CL-764\", \"resolve CL-903\", \"fix OnPush eslint suppressions\", \"fix Signals eslint suppressions\", or reduce linting suppressions.\nallowed-tools: Read, Write, Edit, Glob, Grep, Bash(npx ng generate:*), Bash(npm run lint:fix), Bash(npm run test)\n---\n\n## Key rules\n\n- Fix the underlying issue — never just delete the suppression comment and leave broken code.\n- Remove the **complete comment block**: FIXME line (if any) + TODO: Skipped block (if any) + eslint-disable-next-line line.\n- Both FIXME-paired and standalone suppressions are the same migration debt.\n- For Angular migration rules, prefer CLI schematics over manual edits.\n- Do NOT convert service observables to signals (ADR-0027).\n- For OnPush and signals patterns, the `angular-modernization` skill is the authoritative source — this skill only owns the ESLint suppression mechanics.\n\n## Step 1: Discover all suppressions\n\nUse the `Grep` tool to find suppressions in the target path:\n\n- Pattern `eslint-disable` — finds all eslint suppressions\n- Pattern `FIXME.*CL-` — finds Angular FIXME-tracked ones specifically\n\nGroup results by rule name. Two forms appear in this codebase:\n\n**Form A — FIXME-paired** (a FIXME tracking comment sits above the disable):\n\n```typescript\n// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush\n// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection\n```\n\n**Form B — Standalone** (disable without a FIXME, or with a CLI skip comment):\n\n```typescript\n// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection\n// TODO: Skipped for signal migration because:\n//  Accessor inputs cannot be migrated as they are too complex.\n// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals\n// eslint-disable-next-line @angular-eslint/prefer-signals\n```\n\nBoth forms must be fixed the same way.\n\n## Rule reference\n\n| Category         | Rule                                                        | Section below                                       |\n| ---------------- | ----------------------------------------------------------- | --------------------------------------------------- |\n| Angular          | `@angular-eslint/prefer-on-push-component-change-detection` | [OnPush](#onpush)                                   |\n| Angular          | `@angular-eslint/prefer-signals`                            | [Signals](#signals)                                 |\n| Angular          | `@angular-eslint/prefer-output-emitter-ref`                 | [Signals](#signals)                                 |\n| Angular template | `@angular-eslint/template/button-has-type`                  | [HTML rules](#html-rules)                           |\n| TypeScript       | `@typescript-eslint/no-floating-promises`                   | [no-floating-promises](#no-floating-promises)       |\n| TypeScript       | `@typescript-eslint/no-unused-vars`                         | [no-unused-vars](#no-unused-vars)                   |\n| TypeScript       | `@typescript-eslint/no-unsafe-function-type`                | [no-unsafe-function-type](#no-unsafe-function-type) |\n| RxJS             | `rxjs/no-async-subscribe`                                   | [rxjs rules](#rxjs-rules)                           |\n| RxJS             | `rxjs-angular/prefer-takeuntil`                             | [rxjs rules](#rxjs-rules)                           |\n| Bitwarden        | `@bitwarden/platform/no-enums`                              | [no-enums](#no-enums)                               |\n| Bitwarden        | `@bitwarden/components/no-bwi-class-usage`                  | [HTML rules](#html-rules)                           |\n| General          | `no-restricted-imports`                                     | [no-restricted-imports](#no-restricted-imports)     |\n| General          | `no-console`                                                | [no-console](#no-console)                           |\n| General          | `no-empty`                                                  | [no-empty](#no-empty)                               |\n| General          | bare `// eslint-disable-next-line`                          | [bare disable](#bare-disable)                       |\n| Tailwind         | `tailwindcss/no-custom-classname`                           | [HTML rules](#html-rules)                           |\n\n## OnPush\n\n**Rule:** `@angular-eslint/prefer-on-push-component-change-detection`\n\nFollow the OnPush guidance in the `angular-modernization` skill (add `changeDetection: ChangeDetectionStrategy.OnPush`, remove `ChangeDetectorRef` if only used for `detectChanges()`). Then remove the FIXME + `eslint-disable-next-line` lines.\n\n> `@Directive` does not support `changeDetection` — skip OnPush for pure directives.\n\n## Signals\n\n**Rules:** `@angular-eslint/prefer-signals`, `@angular-eslint/prefer-output-emitter-ref`\n\nApplies to `@Input()`, `@Output()`, `@ViewChild`, `@ContentChild`.\n\nFollow the Signal Inputs, Outputs, and Queries guidance in the `angular-modernization` skill (prefer CLI schematics, then manual conversion). After each migration, **manually remove** the FIXME and `eslint-disable-next-line` lines, and any `// TODO: Skipped for signal migration because:` comment blocks.\n\n> Do NOT convert service observables to signals — only component-local state and decorator bindings (ADR-0027).\n\n## no-floating-promises\n\n**Rule:** `@typescript-eslint/no-floating-promises`\n\nA returned Promise is not handled. Pick one fix:\n\n```typescript\n// 1. Await it (preferred in async functions)\nawait this.router.navigate([\"/login\"]);\n\n// 2. void — explicit fire-and-forget\nvoid this.router.navigate([\"/login\"]);\n\n// 3. Chain .catch() for explicit error handling\nthis.router.navigate([\"/login\"]).catch((err) => this.logService.error(err));\n```\n\nUse `void` for navigation or toast calls that genuinely don't need awaiting. Use `await` when the result matters or you're already in an async context.\n\n## no-unused-vars\n\n**Rule:** `@typescript-eslint/no-unused-vars`\n\n```typescript\n// Remove unused variable\nconst unused = computeSomething(); // delete this line\n\n// Or prefix with _ if it must be declared (e.g. destructuring)\nconst [_first, second] = array;\n\n// Or suppress a catch variable (TypeScript 4.0+)\ntry { ... } catch { ... } // omit the variable entirely\n```\n\n## no-unsafe-function-type\n\n**Rule:** `@typescript-eslint/no-unsafe-function-type`\n\nReplace the generic `Function` type with a specific signature:\n\n**Before**\n\n```typescript\nprivate callback: Function;\n```\n\n**After** — use the actual signature\n\n```typescript\nprivate callback: () => void;\n// or for unknown signatures:\nprivate callback: (...args: unknown[]) => unknown;\n```\n\n## RxJS rules\n\n**Rules:** `rxjs/no-async-subscribe`, `rxjs-angular/prefer-takeuntil`\n\n**`rxjs/no-async-subscribe`** — async callback inside `.subscribe()` swallows errors:\n\n**Before**\n\n```typescript\nthis.service.data$.subscribe(async (value) => {\n  await this.process(value);\n});\n```\n\n**After** — move async work into the pipe\n\n```typescript\nthis.service.data$\n  .pipe(\n    switchMap((value) => this.process(value)),\n    takeUntilDestroyed(),\n  )\n  .subscribe();\n```\n\n**`rxjs-angular/prefer-takeuntil`** — subscription without cleanup:\n\n**Before**\n\n```typescript\nthis.service.data$.subscribe((value) => {\n  this.data = value;\n});\n```\n\n**After** — add `takeUntilDestroyed()` (call in constructor or use `destroyRef`)\n\n```typescript\nconstructor() {\n  this.service.data$\n    .pipe(takeUntilDestroyed())\n    .subscribe((value) => { this.data = value; });\n}\n```\n\n## no-enums\n\n**Rule:** `@bitwarden/platform/no-enums`\n\nConvert TypeScript enums to const objects with type aliases (ADR-0025):\n\n**Before**\n\n```typescript\nenum CipherType {\n  Login = 1,\n  SecureNote = 2,\n}\n```\n\n**After**\n\n```typescript\nexport const CipherType = Object.freeze({ Login: 1, SecureNote: 2 } as const);\nexport type CipherType = (typeof CipherType)[keyof typeof CipherType];\n```\n\nUpdate all import sites — the usage (`CipherType.Login`) stays the same.\n\n## no-restricted-imports\n\n**Rule:** `no-restricted-imports`\n\nThe import is from a path that the ESLint config forbids. Steps:\n\n1. Read the context around the import to understand what is being imported.\n2. Check `eslint.config.mjs` at the repo root (or the nearest config) for the `no-restricted-imports` rule to find the allowed alternative path.\n3. Replace the import with the allowed path and remove the suppression.\n\nCommon cases: importing platform-internal modules directly instead of through the public API, or test-only helpers in non-test files.\n\n## no-console\n\n**Rule:** `no-console`\n\n```typescript\n// Remove debug statements\nconsole.log(\"debug\"); // delete\n\n// Replace with the application logging service\nthis.logService.error(\"Something failed\", error);\n```\n\nIn test files (`*.spec.ts`), a `console.error` or `console.warn` spy may be intentional — in that case, set up the spy properly rather than suppressing:\n\n```typescript\njest.spyOn(console, \"error\").mockImplementation(() => {});\n```\n\n## no-empty\n\n**Rule:** `no-empty`\n\nEmpty `catch` blocks silently swallow errors:\n\n**Before**\n\n```typescript\ntry {\n  await something();\n  // eslint-disable-next-line no-empty\n} catch {}\n```\n\n**After** — handle or log the error\n\n```typescript\ntry {\n  await something();\n} catch (e) {\n  // Intentionally ignored — operation is best-effort\n}\n\n// Or log it\ntry {\n  await something();\n} catch (e) {\n  this.logService.warning(\"Operation failed\", e);\n}\n```\n\n## Bare disable\n\n**Rule:** bare `// eslint-disable-next-line` (no rule specified)\n\nThis disables ALL rules for the next line, which is always wrong. Steps:\n\n1. Remove the suppression and run `npm run lint:fix` to see which specific rule triggers.\n2. Fix the underlying issue using the appropriate section above.\n3. If the violation truly cannot be fixed (rare), replace the bare disable with a specific named rule.\n\n## HTML rules\n\n**`@angular-eslint/template/button-has-type`** — Add an explicit type to every `<button>`:\n\n**Before**\n\n```html\n<button (click)=\"save()\">Save</button>\n```\n\n**After**\n\n```html\n<button type=\"button\" (click)=\"save()\">Save</button>\n<!-- or type=\"submit\" inside a <form> -->\n```\n\n**`@bitwarden/components/no-bwi-class-usage`** — Replace raw `bwi-*` icon classes with the `<bit-icon>` component or the appropriate icon token.\n\n**`tailwindcss/no-custom-classname`** — Use a Tailwind utility class with the `tw-` prefix, or register the class in the Tailwind safelist. Never use arbitrary custom class names.\n\n## Step 2: Cleanup checklist per fixed instance\n\n- [ ] `// FIXME(…)` line removed (if present)\n- [ ] `// TODO: Skipped for signal migration because: …` block removed (all lines, if present)\n- [ ] `// eslint-disable-next-line …` line removed\n- [ ] Unused imports removed; new imports added as needed\n- [ ] All in-class usages updated (e.g. signal reads need `()`)\n\n## Step 3: Validate\n\n```bash\nnpm run lint:fix\n```\n\nFix any errors that remain. Run `npm run test` if behaviour-critical code was changed.\n","frontmatter":{"name":"fix-angular-fixmes","description":"Resolves eslint-disable suppression comments throughout the Bitwarden clients codebase by fixing the underlying issue. Use when the user asks to \"fix FIXMEs\", \"fix eslint suppressions\", \"clean up eslint-disable-next-line\", \"resolve CL-764\", \"resolve CL-903\", \"fix OnPush eslint suppressions\", \"fix Signals eslint suppressions\", or reduce linting suppressions.","allowed-tools":"Read, Write, Edit, Glob, Grep, Bash(npx ng generate:*), Bash(npm run lint:fix), Bash(npm run test)","allowedTools":["Read,","Write,","Edit,","Glob,","Grep,","Bash(npx","ng","generate:*),","Bash(npm","run","lint:fix),","Bash(npm","run","test)"]},"allowedTools":["Read,","Write,","Edit,","Glob,","Grep,","Bash(npx","ng","generate:*),","Bash(npm","run","lint:fix),","Bash(npm","run","test)"],"isInternal":false,"tokens":2550,"sizeBytes":11323},{"name":"SKILL.md","path":".claude/skills/writing-client-code/SKILL.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/writing-client-code/SKILL.md","title":"writing-client-code","category":"anthropic-skill","format":"markdown","content":"---\nname: writing-client-code\ndescription: Bitwarden client code conventions for Angular and TypeScript. Use when creating components, services, or modifying web/browser/desktop apps.\n---\n\n### Why `libs/common` cannot import Angular\n\nCLI is a first-class client. Any code in `libs/common` must work without Angular's dependency injection, decorators, or lifecycle hooks. This is why cross-client services use abstract classes as interfaces — the concrete implementations (`Default*`, `Web*`, `Browser*`, `Desktop*`, `Cli*`) live in their respective apps.\n\n## Architectural Rationale\n\n### Thin components\n\nComponents contain only view logic. Business logic belongs in services. This keeps components testable, reusable, and prevents Angular lifecycle coupling from leaking into domain logic.\n\n### Composition over inheritance\n\nAvoid extending components across clients. Compose using shared child components instead. Inheritance creates tight coupling between client-specific UI and shared behavior — when one client's needs diverge, inherited components become hard to change safely.\n\n### Don't modernize existing code unless asked\n\nThe codebase contains both legacy and modern Angular patterns. When modifying an existing file, **follow the patterns already in that file**. Don't migrate any of these unless explicitly asked:\n\n- `*ngIf` → `@if`, `*ngFor` → `@for`\n- `@Input()` / `@Output()` → `input()` / `output()` signals\n- Constructor injection → `inject()`\n- Default change detection → `OnPush`\n- NgModule declarations → standalone components\n\nIf asked to modernize, follow this order (per the Angular migration guide): standalone → control flow → input/output signals → view queries → signals → computed → OnPush (last, only after full signal migration).\n\n### State management: Signals vs RxJS\n\n- **Component local state and Angular-only services:** Use Signals\n- **Cross-client services (`libs/common`):** Use RxJS (because CLI has no Angular Signals support)\n\nAvoid manual subscriptions. Prefer `| async` pipe. When subscriptions are necessary, pipe through `takeUntilDestroyed()` — enforced by the `prefer-takeUntil` lint rule.\n\n### No TypeScript enums (ADR-0025)\n\nUse frozen const objects with `Object.freeze()` and `as const`, plus a companion type alias. Enums have runtime behavior that creates subtle bugs with tree-shaking.\n\n## Critical Rules for New Code\n\nThese rules apply **strictly to new files and components**. For existing code, follow the patterns already in the file.\n\n- **New components must use `ChangeDetectionStrategy.OnPush`** and be `standalone: true`. `NgModules` are permitted only for grouping related standalone components\n- **Prefer `inject()` function** for DI in Angular primitives (components, pipes, directives). Use constructor injection for code shared with non-Angular clients (CLI)\n- **New templates must use control flow syntax** (`@if`, `@for`, `@switch`), not structural directives\n- **Use `host` property** in component decorators, not `@HostBinding` / `@HostListener`\n- **Use Reactive Forms exclusively** — not template-driven forms\n- **File naming:** `kebab-case.component.ts`, `.service.ts`, `.pipe.ts`, `.directive.ts`. Also: `.request.ts`, `.response.ts`, `.view.ts`, `.data.ts` for models (ADR-0012)\n- **All Tailwind classes require `tw-` prefix** — `tw-flex`, `tw-mt-2`, not `flex`, `mt-2`\n- **Testing with Jest** — use `jest-mock-extended` for mocking services. `describe`/`it` blocks, not `test()`\n- **Imports from `@bitwarden/common`** must not pull in Angular-specific code (breaks CLI)\n\n## Examples\n\n### Dependency injection (new Angular code)\n\n```typescript\n// CORRECT — inject() for Angular primitives\nexport class VaultComponent {\n  private vaultService = inject(VaultService);\n}\n\n// ALSO CORRECT — constructor injection for code shared with CLI\nexport class CryptoService {\n  constructor(private stateService: StateService) {}\n}\n```\n\n### Tailwind prefix\n\n```html\n<!-- CORRECT -->\n<div class=\"tw-flex tw-gap-2 tw-mt-4\">\n  <!-- WRONG — missing tw- prefix, will be stripped -->\n  <div class=\"flex gap-2 mt-4\"></div>\n</div>\n```\n\n### Const objects over enums (ADR-0025)\n\n```typescript\n// CORRECT — with companion type alias\nexport const CipherType = Object.freeze({\n  Login: 1,\n  SecureNote: 2,\n} as const);\nexport type CipherType = (typeof CipherType)[keyof typeof CipherType];\n\n// WRONG — TypeScript enums have runtime side effects\nexport enum CipherType {\n  Login = 1,\n  SecureNote = 2,\n}\n```\n\n## Further Reading\n\n- [Angular conventions](https://contributing.bitwarden.com/contributing/code-style/web/angular)\n- [Angular migration guide](https://contributing.bitwarden.com/contributing/code-style/web/angular-migration-guide)\n- [TypeScript style](https://contributing.bitwarden.com/contributing/code-style/typescript/)\n- [Component Library](https://contributing.bitwarden.com/contributing/ui/component-library/)\n","frontmatter":{"name":"writing-client-code","description":"Bitwarden client code conventions for Angular and TypeScript. Use when creating components, services, or modifying web/browser/desktop apps."},"isInternal":false,"tokens":1108,"sizeBytes":4919},{"name":"package.json","path":"libs/tools/export-vault-core/package.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/package.json","title":"Export-vault-core Skill","category":"anthropic-skill","format":"json","content":"# Export-vault-core Skill\n\nPath: `libs/tools/export-vault-core/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/package.json)","tokens":300,"sizeBytes":206},{"name":"project.json","path":"libs/tools/export-vault-core/project.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/project.json","title":"Export-vault-core Skill","category":"anthropic-skill","format":"json","content":"# Export-vault-core Skill\n\nPath: `libs/tools/export-vault-core/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/project.json)","tokens":300,"sizeBytes":206},{"name":"tsconfig.json","path":"libs/tools/export-vault-core/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/tsconfig.json","title":"Export-vault-core Skill","category":"anthropic-skill","format":"json","content":"# Export-vault-core Skill\n\nPath: `libs/tools/export-vault-core/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/tsconfig.json)","tokens":300,"sizeBytes":208},{"name":"tsconfig.spec.json","path":"libs/tools/export-vault-core/tsconfig.spec.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/tsconfig.spec.json","title":"Export-vault-core Skill","category":"anthropic-skill","format":"json","content":"# Export-vault-core Skill\n\nPath: `libs/tools/export-vault-core/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/tsconfig.spec.json)","tokens":300,"sizeBytes":218},{"name":"package.json","path":"libs/tools/export-vault-ui/package.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/package.json","title":"Export-vault-ui Skill","category":"anthropic-skill","format":"json","content":"# Export-vault-ui Skill\n\nPath: `libs/tools/export-vault-ui/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/package.json)","tokens":300,"sizeBytes":200},{"name":"project.json","path":"libs/tools/export-vault-ui/project.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/project.json","title":"Export-vault-ui Skill","category":"anthropic-skill","format":"json","content":"# Export-vault-ui Skill\n\nPath: `libs/tools/export-vault-ui/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/project.json)","tokens":300,"sizeBytes":200},{"name":"tsconfig.json","path":"libs/tools/export-vault-ui/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/tsconfig.json","title":"Export-vault-ui Skill","category":"anthropic-skill","format":"json","content":"# Export-vault-ui Skill\n\nPath: `libs/tools/export-vault-ui/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/tsconfig.json)","tokens":300,"sizeBytes":202},{"name":"tsconfig.spec.json","path":"libs/tools/export-vault-ui/tsconfig.spec.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/tsconfig.spec.json","title":"Export-vault-ui Skill","category":"anthropic-skill","format":"json","content":"# Export-vault-ui Skill\n\nPath: `libs/tools/export-vault-ui/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/tsconfig.spec.json)","tokens":300,"sizeBytes":212},{"name":"package.json","path":"libs/tools/generator/components/package.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/package.json","title":"Components Skill","category":"anthropic-skill","format":"json","content":"# Components Skill\n\nPath: `libs/tools/generator/components/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/package.json)","tokens":300,"sizeBytes":205},{"name":"project.json","path":"libs/tools/generator/components/project.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/project.json","title":"Components Skill","category":"anthropic-skill","format":"json","content":"# Components Skill\n\nPath: `libs/tools/generator/components/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/project.json)","tokens":300,"sizeBytes":205},{"name":"tsconfig.json","path":"libs/tools/generator/components/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/tsconfig.json","title":"Components Skill","category":"anthropic-skill","format":"json","content":"# Components Skill\n\nPath: `libs/tools/generator/components/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/tsconfig.json)","tokens":300,"sizeBytes":207},{"name":"tsconfig.spec.json","path":"libs/tools/generator/components/tsconfig.spec.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/tsconfig.spec.json","title":"Components Skill","category":"anthropic-skill","format":"json","content":"# Components Skill\n\nPath: `libs/tools/generator/components/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/tsconfig.spec.json)","tokens":300,"sizeBytes":217},{"name":"package.json","path":"libs/tools/generator/core/package.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/core/package.json","title":"Core Skill","category":"anthropic-skill","format":"json","content":"# Core Skill\n\nPath: `libs/tools/generator/core/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/core/package.json)","tokens":300,"sizeBytes":187},{"name":"project.json","path":"libs/tools/generator/core/project.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/core/project.json","title":"Core Skill","category":"anthropic-skill","format":"json","content":"# Core Skill\n\nPath: `libs/tools/generator/core/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/core/project.json)","tokens":300,"sizeBytes":187},{"name":"tsconfig.json","path":"libs/tools/generator/core/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/core/tsconfig.json","title":"Core Skill","category":"anthropic-skill","format":"json","content":"# Core Skill\n\nPath: `libs/tools/generator/core/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/core/tsconfig.json)","tokens":300,"sizeBytes":189},{"name":"tsconfig.spec.json","path":"libs/tools/generator/core/tsconfig.spec.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/core/tsconfig.spec.json","title":"Core Skill","category":"anthropic-skill","format":"json","content":"# Core Skill\n\nPath: `libs/tools/generator/core/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/core/tsconfig.spec.json)","tokens":300,"sizeBytes":199},{"name":"package.json","path":"libs/tools/generator/extensions/history/package.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/history/package.json","title":"History Skill","category":"anthropic-skill","format":"json","content":"# History Skill\n\nPath: `libs/tools/generator/extensions/history/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/history/package.json)","tokens":300,"sizeBytes":218},{"name":"project.json","path":"libs/tools/generator/extensions/history/project.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/history/project.json","title":"History Skill","category":"anthropic-skill","format":"json","content":"# History Skill\n\nPath: `libs/tools/generator/extensions/history/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/history/project.json)","tokens":300,"sizeBytes":218},{"name":"tsconfig.json","path":"libs/tools/generator/extensions/history/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/history/tsconfig.json","title":"History Skill","category":"anthropic-skill","format":"json","content":"# History Skill\n\nPath: `libs/tools/generator/extensions/history/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/history/tsconfig.json)","tokens":300,"sizeBytes":220},{"name":"tsconfig.spec.json","path":"libs/tools/generator/extensions/history/tsconfig.spec.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/history/tsconfig.spec.json","title":"History Skill","category":"anthropic-skill","format":"json","content":"# History Skill\n\nPath: `libs/tools/generator/extensions/history/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/history/tsconfig.spec.json)","tokens":300,"sizeBytes":230},{"name":"package.json","path":"libs/tools/generator/extensions/legacy/package.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/legacy/package.json","title":"Legacy Skill","category":"anthropic-skill","format":"json","content":"# Legacy Skill\n\nPath: `libs/tools/generator/extensions/legacy/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/legacy/package.json)","tokens":300,"sizeBytes":215},{"name":"project.json","path":"libs/tools/generator/extensions/legacy/project.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/legacy/project.json","title":"Legacy Skill","category":"anthropic-skill","format":"json","content":"# Legacy Skill\n\nPath: `libs/tools/generator/extensions/legacy/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/legacy/project.json)","tokens":300,"sizeBytes":215},{"name":"tsconfig.json","path":"libs/tools/generator/extensions/legacy/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/legacy/tsconfig.json","title":"Legacy Skill","category":"anthropic-skill","format":"json","content":"# Legacy Skill\n\nPath: `libs/tools/generator/extensions/legacy/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/legacy/tsconfig.json)","tokens":300,"sizeBytes":217},{"name":"tsconfig.spec.json","path":"libs/tools/generator/extensions/legacy/tsconfig.spec.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/legacy/tsconfig.spec.json","title":"Legacy Skill","category":"anthropic-skill","format":"json","content":"# Legacy Skill\n\nPath: `libs/tools/generator/extensions/legacy/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/legacy/tsconfig.spec.json)","tokens":300,"sizeBytes":227},{"name":"package.json","path":"libs/tools/generator/extensions/navigation/package.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/navigation/package.json","title":"Navigation Skill","category":"anthropic-skill","format":"json","content":"# Navigation Skill\n\nPath: `libs/tools/generator/extensions/navigation/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/navigation/package.json)","tokens":300,"sizeBytes":227},{"name":"project.json","path":"libs/tools/generator/extensions/navigation/project.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/navigation/project.json","title":"Navigation Skill","category":"anthropic-skill","format":"json","content":"# Navigation Skill\n\nPath: `libs/tools/generator/extensions/navigation/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/navigation/project.json)","tokens":300,"sizeBytes":227},{"name":"tsconfig.json","path":"libs/tools/generator/extensions/navigation/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/navigation/tsconfig.json","title":"Navigation Skill","category":"anthropic-skill","format":"json","content":"# Navigation Skill\n\nPath: `libs/tools/generator/extensions/navigation/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/navigation/tsconfig.json)","tokens":300,"sizeBytes":229},{"name":"tsconfig.spec.json","path":"libs/tools/generator/extensions/navigation/tsconfig.spec.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/navigation/tsconfig.spec.json","title":"Navigation Skill","category":"anthropic-skill","format":"json","content":"# Navigation Skill\n\nPath: `libs/tools/generator/extensions/navigation/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/extensions/navigation/tsconfig.spec.json)","tokens":300,"sizeBytes":239},{"name":"package.json","path":"libs/tools/send/send-ui/package.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/send-ui/package.json","title":"Send-ui Skill","category":"anthropic-skill","format":"json","content":"# Send-ui Skill\n\nPath: `libs/tools/send/send-ui/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/send-ui/package.json)","tokens":300,"sizeBytes":186},{"name":"project.json","path":"libs/tools/send/send-ui/project.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/send-ui/project.json","title":"Send-ui Skill","category":"anthropic-skill","format":"json","content":"# Send-ui Skill\n\nPath: `libs/tools/send/send-ui/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/send-ui/project.json)","tokens":300,"sizeBytes":186},{"name":"tsconfig.json","path":"libs/tools/send/send-ui/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/send-ui/tsconfig.json","title":"Send-ui Skill","category":"anthropic-skill","format":"json","content":"# Send-ui Skill\n\nPath: `libs/tools/send/send-ui/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/send-ui/tsconfig.json)","tokens":300,"sizeBytes":188},{"name":"tsconfig.spec.json","path":"libs/tools/send/send-ui/tsconfig.spec.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/send-ui/tsconfig.spec.json","title":"Send-ui Skill","category":"anthropic-skill","format":"json","content":"# Send-ui Skill\n\nPath: `libs/tools/send/send-ui/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/send-ui/tsconfig.spec.json)","tokens":300,"sizeBytes":198},{"name":"package.json","path":"libs/tools/share/package.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/package.json","title":"Share Skill","category":"anthropic-skill","format":"json","content":"# Share Skill\n\nPath: `libs/tools/share/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/package.json)","tokens":300,"sizeBytes":170},{"name":"project.json","path":"libs/tools/share/project.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/project.json","title":"Share Skill","category":"anthropic-skill","format":"json","content":"# Share Skill\n\nPath: `libs/tools/share/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/project.json)","tokens":300,"sizeBytes":170},{"name":"tsconfig.eslint.json","path":"libs/tools/share/tsconfig.eslint.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/tsconfig.eslint.json","title":"Share Skill","category":"anthropic-skill","format":"json","content":"# Share Skill\n\nPath: `libs/tools/share/tsconfig.eslint.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/tsconfig.eslint.json)","tokens":300,"sizeBytes":186},{"name":"tsconfig.json","path":"libs/tools/share/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/tsconfig.json","title":"Share Skill","category":"anthropic-skill","format":"json","content":"# Share Skill\n\nPath: `libs/tools/share/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/tsconfig.json)","tokens":300,"sizeBytes":172},{"name":"tsconfig.spec.json","path":"libs/tools/share/tsconfig.spec.json","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/tsconfig.spec.json","title":"Share Skill","category":"anthropic-skill","format":"json","content":"# Share Skill\n\nPath: `libs/tools/share/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/tsconfig.spec.json)","tokens":300,"sizeBytes":182},{"name":"CLAUDE.md","path":".claude/CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/CLAUDE.md","title":".claude - Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# Bitwarden Clients - Claude Code Configuration\n\n## Project Context Files\n\n**Read these files before reviewing to ensure that you fully understand the project and contributing guidelines**\n\n1. `README.md` (read on demand for project overview)\n2. `CONTRIBUTING.md` (read on demand for contributing guidelines)\n3. @../.github/PULL_REQUEST_TEMPLATE.md\n\n## Critical Rules\n\n- **NEVER** use code regions: If complexity suggests regions, refactor for better readability\n- **CRITICAL**: new encryption logic should not be added to this repo.\n  - If significant encryption related logic is added or changed, make sure @bitwarden/team-key-management-dev is aware of the PR\n- **NEVER** send unencrypted vault data to API services\n- **NEVER** commit secrets, credentials, or sensitive information.\n- **NEVER** log decrypted data, encryption keys, or PII\n  - No vault data in error messages or console logs\n- **ALWAYS** Respect configuration files at the root and within each app/library (e.g., `eslint.config.mjs`, `jest.config.js`, `tsconfig.json`).\n- **CRITICAL**: Tailwind CSS classes MUST use the `tw-` prefix (e.g., `tw-flex`, `tw-p-4`). Missing prefix means the class is ignored and styling silently breaks. See [.claude/rules/tailwind.md](./rules/tailwind.md) for additional Tailwind rules.\n\n## Mono-Repo Architecture\n\nThis repository is organized as a **monorepo** containing multiple applications and libraries. The\nmain directories are:\n\n- `apps/<client>/` — single-client code (browser, cli, desktop, web). Each app is self-contained.\n- `libs/common/` — code shared across **all** clients, including non-Angular (CLI). No Angular APIs here: no `@Injectable`, no `inject()`, no decorators, no template references.\n- `libs/angular/` — code shared across Angular clients (browser/desktop/web). Angular APIs allowed.\n- Other `libs/*` (e.g. `ui`, `platform`, `key-management`, `vault`) — domain-scoped, follow the same Angular / non-Angular split based on which clients consume them.\n\nWhen adding new code, place it as deep and as narrow as possible. Promote to a shared `libs/` only when a second client needs it.\n\n**Strict boundaries** must be maintained between apps and libraries. Do not introduce\ncross-dependencies that violate the intended modular structure. Always consult and respect the\ndependency rules defined in `eslint.config.mjs`, `nx.json`, and other configuration files.\n\n## Verifying Changes\n\nAfter modifying code, run:\n\n- `npm run lint:fix` — applies ESLint fixes\n- `npm run prettier` — formats with Prettier\n- `npm run test:types` — type-checks the workspace\n- `npm test` — runs Jest. Scope with `npm test -- <path-or-pattern>` when changes are localized.\n\nIf any of these fail, fix the underlying issue before reporting the task complete. Do not skip hooks or bypass failures.\n\n## References\n\n- [Web Clients Architecture](https://contributing.bitwarden.com/architecture/clients)\n- [Architectural Decision Records (ADRs)](https://contributing.bitwarden.com/architecture/adr/)\n- [Contributing Guide](https://contributing.bitwarden.com/)\n- [Web Clients Setup Guide](https://contributing.bitwarden.com/getting-started/clients/)\n- [Code Style](https://contributing.bitwarden.com/contributing/code-style/)\n- [Security Whitepaper](https://bitwarden.com/help/bitwarden-security-white-paper/)\n- [Security Definitions](https://contributing.bitwarden.com/architecture/security/definitions)\n","isInternal":false,"tokens":797,"sizeBytes":3410},{"name":"sdk-surface-diff.mjs","path":".claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs","title":"Claude Directive: sdk-surface-diff.mjs","category":"claude-rule","format":"markdown","content":"# Claude Directive: sdk-surface-diff.mjs\n\nPath: `.claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs)","tokens":300,"sizeBytes":273},{"name":"readme.md","path":"libs/common/src/tools/extension/vendor/readme.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/common/src/tools/extension/vendor/readme.md","title":"vendor Documentation","category":"plugin-manifest","format":"markdown","content":"# vendor Documentation\n\nPath: `libs/common/src/tools/extension/vendor/readme.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/common/src/tools/extension/vendor/readme.md)","tokens":300,"sizeBytes":217},{"name":"README.md","path":"libs/common/src/tools/integration/README.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/common/src/tools/integration/README.md","title":"integration Documentation","category":"plugin-manifest","format":"markdown","content":"# integration Documentation\n\nPath: `libs/common/src/tools/integration/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/common/src/tools/integration/README.md)","tokens":300,"sizeBytes":212},{"name":"README.md","path":"libs/tools/export-vault-core/README.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/README.md","title":"export-vault-core Documentation","category":"plugin-manifest","format":"markdown","content":"# export-vault-core Documentation\n\nPath: `libs/tools/export-vault-core/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/README.md)","tokens":300,"sizeBytes":208},{"name":"README.md","path":"libs/tools/export-vault-ui/README.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/README.md","title":"export-vault-ui Documentation","category":"plugin-manifest","format":"markdown","content":"# export-vault-ui Documentation\n\nPath: `libs/tools/export-vault-ui/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/README.md)","tokens":300,"sizeBytes":202},{"name":"readme.md","path":"libs/tools/generator/components/readme.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/readme.md","title":"components Documentation","category":"plugin-manifest","format":"markdown","content":"# components Documentation\n\nPath: `libs/tools/generator/components/readme.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/readme.md)","tokens":300,"sizeBytes":207},{"name":"README.md","path":"libs/tools/README.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/README.md","title":"tools Documentation","category":"plugin-manifest","format":"markdown","content":"# tools Documentation\n\nPath: `libs/tools/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/README.md)","tokens":300,"sizeBytes":160},{"name":"README.md","path":"libs/tools/send/README.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/README.md","title":"send Documentation","category":"plugin-manifest","format":"markdown","content":"# send Documentation\n\nPath: `libs/tools/send/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/send/README.md)","tokens":300,"sizeBytes":169},{"name":"README.md","path":"libs/tools/share/README.md","rawUrl":"https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/README.md","title":"share Documentation","category":"plugin-manifest","format":"markdown","content":"# share Documentation\n\nPath: `libs/tools/share/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/share/README.md)","tokens":300,"sizeBytes":172}],"systemPromptSnippet":"<yakaai_skills repo=\"bitwarden/clients\">\n<!-- File: .claude/rules/angular.md (Tokens: ~444 | Category: anthropic-skill) -->\n---\npaths:\n  - \"**/*.ts\"\n---\n\n# Angular Patterns\n\n> **Scope:** these rules apply **only to Angular code** — files in `apps/browser`, `apps/desktop`, `apps/web`, `libs/angular`, and any other library consumed by Angular clients. **Skip them in non-Angular TypeScript**: `libs/common`, `apps/cli`, SDK code, and anywhere `@Injectable` and Angular APIs are forbidden by the monorepo rules in the root [CLAUDE.md](../CLAUDE.md).\n\nDistilled from [Web Code Style — Angular](https://contributing.bitwarden.com/contributing/code-style/web/angular). Component-only patterns (templates, OnPush, signals, observable subscriptions, etc.) live in [angular-components.md](./angular-components.md); TypeScript-wide patterns live in [typescript.md](./typescript.md).\n\n## Dependency Injection\n\nUse `inject()` — not constructor injection. Constructor injection stays only in code shared with non-Angular clients (CLI).\n\n```typescript\nprivate folderService = inject(FolderService);\n```\n\nWhen declaring providers (component decorator, route config, module), wrap them in `safeProvider()` from `@bitwarden/ui-common`. It adds compile-time checks that the implementation matches the abstraction and that the `deps` array matches the constructor — raw Angular provider objects don't.\n\n```typescript\nimport { safeProvider } from \"@bitwarden/ui-common\";\n\nproviders: [\n  safeProvider({\n    provide: FolderService,\n    useClass: DefaultFolderService,\n    deps: [CipherService, I18nService],\n  }),\n],\n```\n\n### Injection tokens\n\nGive every `SafeInjectionToken<T>` a specific `T` — never `Type<unknown>`, `unknown` or `any`. For a component token, declare an interface or type for the inputs the consumer binds and use `Type<ThatShape>`.\n\n```typescript\nexport interface VaultGatedCollectionBanner {\n  readonly organizationId: InputSignal<OrganizationId | undefined>;\n}\n\nexport const VAULT_GATED_COLLECTION_BANNER = new SafeInjectionToken<\n  Type<VaultGatedCollectionBanner>\n>(\"VaultGatedCollectionBanner\");\n```\n\n<!-- File: .claude/rules/autofill-content-scripts.md (Tokens: ~345 | Category: anthropic-skill) -->\n---\npaths:\n  - \"apps/browser/src/autofill/content/**\"\n---\n\n# Autofill Content Scripts - Critical Rules\n\n## Angular Exception\n\nCode in this directory runs as **injected content scripts**, not within the Angular application context.\n\n- **DO NOT** use Angular patterns (components, services, dependency injection, RxJS Observable Data Services, Signals, `takeUntilDestroyed()`, `async` pipe, etc.)\n- **DO NOT** import Angular modules or dependencies\n- The Angular patterns defined in [angular.md](./angular.md) and [angular-components.md](./angular-components.md), and the Observable Data Services pattern in [typescript.md](./typescript.md), do not apply here\n\n## Lit Components\n\n- Components in `content/components/` use [Lit](https://lit.dev/), not Angular\n- Lit components should consume shared concepts (constants, values) from the component library when possible, without introducing Angular or other dependency entanglements\n\n## Browser API Usage\n\n- Content scripts cannot import the `BrowserApi` abstraction required elsewhere in the browser extension\n- Direct use of `chrome.*` / `browser.*` APIs (e.g., `chrome.runtime.sendMessage`) is expected here\n- This is an exception to the `BrowserApi` rule in the parent `apps/browser/CLAUDE.md`\n\n## Cross-Browser Compatibility\n\n- Ensure injected code is compatible across browser clients, particularly Firefox, Chrome, and Safari\n- Use polyfills and vendor-specific implementations where needed\n\n## Memory Management\n\n- Ensure proper teardown of listeners, observers, and other concerns that might linger in memory\n- Content scripts do not benefit from Angular's lifecycle hooks — cleanup must be handled manually\n\n<!-- File: .claude/rules/flatpak-manifest.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Rules Skill\n\nPath: `.claude/rules/flatpak-manifest.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/flatpak-manifest.md)\n\n<!-- File: .claude/rules/i18n.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Rules Skill\n\nPath: `.claude/rules/i18n.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/i18n.md)\n\n<!-- File: .claude/rules/snap-permissions.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Rules Skill\n\nPath: `.claude/rules/snap-permissions.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/snap-permissions.md)\n\n<!-- File: .claude/rules/storybook-routing.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Rules Skill\n\nPath: `.claude/rules/storybook-routing.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/storybook-routing.md)\n\n<!-- File: .claude/rules/tailwind.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Rules Skill\n\nPath: `.claude/rules/tailwind.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/tailwind.md)\n\n<!-- File: .claude/rules/testing.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Rules Skill\n\nPath: `.claude/rules/testing.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/testing.md)\n\n<!-- File: .claude/rules/typescript.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Rules Skill\n\nPath: `.claude/rules/typescript.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/rules/typescript.md)\n\n<!-- File: .claude/skills/angular-modernization/migration-patterns.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Angular-modernization Skill\n\nPath: `.claude/skills/angular-modernization/migration-patterns.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/angular-modernization/migration-patterns.md)\n\n<!-- File: .claude/skills/angular-modernization/SKILL.md (Tokens: ~1083 | Category: anthropic-skill) -->\n---\nname: angular-modernization\ndescription: Modernizes Angular code such as components and directives to follow best practices using both automatic CLI migrations and Bitwarden-specific patterns. YOU must use this skill when someone requests modernizing Angular code. DO NOT invoke for general Angular discussions unrelated to modernization.\nallowed-tools: Read, Write, Glob, Bash(npx ng generate:*)\n---\n\n# Angular Modernization\n\nTransforms legacy Angular components to modern architecture using a two-step approach:\n\n1. **Automated migrations** - Angular CLI schematics for standalone, control flow, and signals\n2. **Bitwarden patterns** - ADR compliance, OnPush change detection, proper visibility, thin components\n\n## Workflow\n\n### Step 1: Run Angular CLI Migrations\n\n**⚠️ CRITICAL: ALWAYS use Angular CLI migrations when available. DO NOT manually migrate features that have CLI schematics.**\n\nAngular provides automated schematics that handle edge cases, update tests, and ensure correctness. Manual migration should ONLY be used for patterns not covered by CLI tools.\n\n**IMPORTANT:**\n\n- Always run the commands using `npx ng`.\n- All the commands must be run on directories and NOT files. Use the `--path` option to target directories.\n- Run migrations in order (some depend on others)\n\n#### 1. Standalone Components\n\n```bash\nnpx ng generate @angular/core:standalone --path=<directory> --mode=convert-to-standalone\n```\n\nNgModule-based → standalone architecture\n\n#### 2. Control Flow Syntax\n\n```bash\nnpx ng generate @angular/core:control-flow\n```\n\n`*ngIf`, `*ngFor`, `*ngSwitch` → `@if`, `@for`, `@switch`\n\n#### 3. Signal Inputs\n\n```bash\nnpx ng generate @angular/core:signal-input-migration\n```\n\n`@Input()` → signal inputs\n\n#### 4. Signal Outputs\n\n```bash\nnpx ng generate @angular/core:output-migration\n```\n\n`@Output()` → signal outputs\n\n#### 5. Signal Queries\n\n```bash\nnpx ng generate @angular/core:signal-queries-migration\n```\n\n`@ViewChild`, `@ContentChild`, etc. → signal queries\n\n#### 6. inject() Function\n\n```bash\nnpx ng generate @angular/core:inject-migration\n```\n\nConstructor injection → `inject()` function\n\n#### 7. Self-Closing Tag\n\n```bash\nnpx ng generate @angular/core:self-closing-tag\n```\n\nUpdates templates to self-closing syntax\n\n#### 8. Unused Imports\n\n```bash\nnpx ng generate @angular/core:unused-imports\n```\n\nRemoves unused imports\n\n### Step 2: Apply Bitwarden Patterns\n\nSee [migration-patterns.md](migration-patterns.md) for detailed examples.\n\n1. Add OnPush change detection\n2. Apply visibility modifiers (`protected` for template access, `private` for internal)\n3. Convert local component state to signals\n4. Keep service observables (don't convert to signals)\n5. Extract business logic to services\n6. Organize class members correctly\n7. Update tests for standalone\n\n### Step 3: Validate\n\n- Fix linting and formatting using `npm run lint:fix`\n- Run tests using `npm run test`\n\nIf any errors occur, fix them accordingly.\n\n## Key Decisions\n\n### Signals vs Observables\n\n- **Signals** - Component-local state only (ADR-0027)\n- **Observables** - Service state and cross-component communication (ADR-0003)\n- Use `toSignal()` to bridge observables into signal-based components\n\n### Visibility\n\n- `protected` - Template-accessible members\n- `private` - Internal implementation\n\n### Other Rules\n\n- Always add OnPush change detection\n- No TypeScript enums (use const objects with type aliases per ADR-0025)\n- No code regions (refactor instead)\n- Thin components (business logic in services)\n\n## Validation Checklist\n\nBefore completing migration:\n\n- [ ] OnPush change detection added\n- [ ] Visibility modifiers applied (`protected`/`private`)\n- [ ] Signals for component state, observables for service state\n- [ ] Class members organized (see [migration-patterns.md](migration-patterns.md#class-member-organization))\n- [ ] Tests updated and passing\n- [ ] No new TypeScript enums\n- [ ] No code regions\n\n## References\n\n### Bitwarden ADRs\n\n- [ADR-0003: Observable Data Services](https://contributing.bitwarden.com/architecture/adr/observable-data-services)\n- [ADR-0025: No TypeScript Enums](https://contributing.bitwarden.com/architecture/adr/no-enums)\n- [ADR-0027: Angular Signals](https://contributing.bitwarden.com/architecture/adr/angular-signals)\n- [Bitwarden Angular Style Guide](https://contributing.bitwarden.com/contributing/code-style/web/angular)\n\n### Angular Resources\n\n- [Angular Style Guide](https://angular.dev/style-guide)\n- [Angular Migrations](https://angular.dev/reference/migrations)\n- [Angular CLI Schematics](https://angular.dev/tools/cli/schematics)\n\n<!-- File: .claude/skills/cipher-type-planner/SKILL.md (Tokens: ~3791 | Category: anthropic-skill) -->\n---\nname: cipher-type-planner\ndescription: Plans the creation or modification of a cipher type (vault item type) across the Bitwarden clients monorepo. Use this skill when a user wants to add a new cipher type, modify an existing cipher type, or asks about what is needed to implement a cipher type. DO NOT invoke for general vault or cipher questions unrelated to adding or changing a cipher type.\nuser-invocable: true\nargument-hint: \"target-client\"\n---\n\n# Cipher Type Planner\n\n## Workflow\n\n### Step 1: Gather Requirements\n\nAsk the user the following questions (use `AskUserQuestion`). Adapt questions based on what\nthe user has already provided.\n\n**Required questions:**\n\n1.  **Type name and value** - What is the cipher type name and integer value? If the user hasn't specified a value, determine the next available integer by reading the `CipherType` enum definition.\n2.  **Fields** - What are the cipher's properties? Each property must contain:\n    - Field name\n    - Data type (string, number, boolean)\n    - Encryption required?\n    - Required\n3.  **Target client** (`$0`) - Which client should this plan focus on? (`web`, `desktop`, `browser`, `cli`, or `all`). Shared library changes (`libs/common`, `libs/vault`) are always included; `$0` controls which `apps/*` files appear. **Skip if already provided as an argument.** Default: `all`.\n4.  **Autofill** - Should this type participate in browser autofill? (Currently only Login, Card,\n    and Identity support autofill.) Only ask if `$0` is `browser` or `all`.\n5.  **Linked fields** - Should this type support linked custom fields? If yes, which properties\n    should be linkable?\n6.  **Feature flag** - What is the feature flag name?\n7.  **Prerequisites** - Have all server and SDK prerequisites been completed? **Do not proceed\n    with the plan until the user confirms these are done.**\n\n**Additional questions:**\n\nAsk each of the following. If the engineer does not have an answer, accept \"N/A\" or \"not yet decided\" and note it as a gap in the plan.\n\n- **Import/export** - Should import/export support be included in this plan?\n- **UI details** - Are there specific UI requirements for the form or view sections (e.g., dropdowns, masked fields, copy buttons)?\n- **Subtitle** - What value should the `subTitle` getter on the view model return? This appears in vault list items.\n- **Icon** - What icon represents this type in the vault? Bitwarden uses `bwi-` icon classes.\n- **Organization policy** - Does this type appear in the restricted item types policy UI?\n\n### Step 2: Enter Plan Mode\n\nAfter gathering requirements, enter plan mode using `EnterPlanMode`. Explore the codebase to\nverify current patterns and file locations. Use the SshKey cipher type (value 5) as the canonical\nreference for implementation patterns.\n\nKey files to inspect for patterns:\n\n- `libs/common/src/vault/enums/cipher-type.ts` - Enum definition\n- `libs/common/src/vault/models/api/ssh-key.api.ts` - API model pattern\n- `libs/common/src/vault/models/data/ssh-key.data.ts` - Data model pattern\n- `libs/common/src/vault/models/domain/ssh-key.ts` - Domain model pattern\n- `libs/common/src/vault/models/view/ssh-key.view.ts` - View model pattern\n- `libs/common/src/models/export/ssh-key.export.ts` - Export model pattern\n- `libs/common/src/vault/models/domain/cipher.ts` - Container switch patterns\n- `libs/vault/src/cipher-form/components/sshkey-section/` - Form component pattern\n- `libs/vault/src/cipher-view/sshkey-sections/` - View component pattern\n\n### Step 3: Build the Plan\n\nWrite a comprehensive plan to the plan file. The plan MUST include all sections below.\n\n---\n\n## Plan Output Format\n\n### 1. Overview\n\n- **Cipher type name:**\n- **Integer value:**\n- **Feature flag:**\n- **Minimum client version:**\n- **Fields:** Table of all fields with name, type, encrypted (yes/no), required (yes/no)\n- **Supports autofill:** Yes/No\n- **Supports linked fields:** Yes/No\n\n### 2. Clients - New Files to Create\n\nList every file that needs to be created, with the full path and a brief description. Organize by\nlayer:\n\n**Model stack:**\n\n- `libs/common/src/vault/models/api/<type>.api.ts` - API response shape\n- `libs/common/src/vault/models/data/<type>.data.ts` - Serializable storage format\n- `libs/common/src/vault/models/domain/<type>.ts` - Encrypted business object\n- `libs/common/src/vault/models/domain/<type>.spec.ts` - Domain model tests\n- `libs/common/src/vault/models/view/<type>.view.ts` - Decrypted view for UI\n\n**Export (if import/export is included):**\n\n- `libs/common/src/models/export/<type>.export.ts` - Export model\n\n**UI components:**\n\n- `libs/vault/src/cipher-form/components/<type>-section/` - Form section component (TS, HTML, spec)\n- `libs/vault/src/cipher-view/<type>-sections/` - View section component (TS, HTML)\n\n### 3. Clients - Existing Files to Modify\n\nList every file that needs modification, organized by concern. For each file, describe the specific\nchange needed.\n\n**Core enum:**\n\n- `libs/common/src/vault/enums/cipher-type.ts` - Add `<Type>: <N>` to `CipherType`\n- `libs/common/src/vault/enums/cipher-type.spec.ts` - Update tests\n\n**Container switches (add case for new type):**\n\n- `libs/common/src/vault/models/data/cipher.data.ts` - Constructor\n- `libs/common/src/vault/models/domain/cipher.ts` - Constructor, `decrypt()`,\n  `toCipherData()`, `fromJSON()`, `toSdkCipher()`, `fromSdkCipher()`\n- `libs/common/src/vault/models/view/cipher.view.ts` - `item` getter, `fromJSON()`,\n  `fromSdkCipherView()`, `getSdkCipherViewType()`, `toSdkCipherView()`\n- `libs/common/src/vault/models/request/cipher.request.ts` - Constructor\n- `libs/common/src/vault/models/response/cipher.response.ts` - Constructor\n- `libs/common/src/vault/services/cipher.service.ts` - `encryptCipherData()`\n\n**Export (if import/export is included):**\n\n- `libs/common/src/models/export/cipher.export.ts` - `toView()`, `toDomain()`, `build()`\n\n**SDK integration:**\n\n- `libs/common/src/vault/models/domain/cipher-sdk-mapper.ts` - Record mapper\n- Domain and view model SDK methods (`toSdk*`/`fromSdk*`)\n\n**UI wiring:**\n\n- `libs/vault/src/cipher-form/components/cipher-form.component.ts` - Import and wire section\n- `libs/vault/src/cipher-form/components/cipher-form.component.html` - Add section template\n- `libs/vault/src/cipher-view/cipher-view.component.ts` - Import and wire section\n- `libs/vault/src/cipher-view/cipher-view.component.html` - Add section template\n- `libs/common/src/vault/icon/build-cipher-icon.ts` - Add icon case\n\n**Vault filters (CRITICAL — without these, ciphers won't appear in the vault list):**\n\nAll vault filter files must be feature-flag-gated so the new type only appears when the flag is\nenabled. Use `ConfigService.getFeatureFlag$()` with `combineLatest` to filter the type out of\narrays when the flag is off.\n\n_Always included (shared):_\n\n- `libs/vault/src/services/vault-filter.service.ts` - **CRITICAL**: Add type to `buildCipherTypeTree()` `allTypeFilters` array. Without this, ciphers of the new type will not appear in the vault sidebar or list.\n- `libs/vault/src/models/filter-function.ts` - Add filter case for the new type\n- `libs/angular/src/vault/components/vault-items.component.ts` - Feature-flag-gate empty state type buttons\n\n_Include if `$0` is `web` or `all`:_\n\n- `apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts` - Add to `allTypeFilters`, `searchPlaceholder`, and feature-flag-gate in `buildAllFilters()`\n- `apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts` - Feature-flag-gate in `buildAllFilters()`\n\n_Include if `$0` is `desktop` or `all`:_\n\n- `apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts` - Add `ConfigService`, `combineLatest` with feature flag\n\n_Include if `$0` is `browser` or `all`:_\n\n- `apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts` - Add `ConfigService`, feature-flag-gate `cipherTypes`\n\n**New item menus (feature-flag-gated):**\n\n_Always included (shared):_\n\n- `libs/common/src/vault/types/cipher-menu-items.ts` - Add menu item entry for new type\n- `libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts` - Add `canCreate<Type> = input(false)` signal, gate in `cipherMenuItems` observable\n\n_Include if `$0` is `web` or `all`:_\n\n- `apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts` - Add `canCreate<Type>$` observable from feature flag\n- `apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.html` - Bind `[canCreate<Type>]` to `<vault-new-cipher-menu>`\n\n_Include if `$0` is `browser` or `all`:_\n\n- `apps/browser/src/vault/popup/components/vault/new-item-dropdown/new-item-dropdown.component.ts` - Add `ConfigService`, `combineLatest` with feature flag\n\n**Localization (add i18n keys):**\n\n_Include only locale files for `$0`. If `$0` is `all`, include all three:_\n\n- `apps/web/src/locales/en/messages.json` _(web)_\n- `apps/desktop/src/locales/en/messages.json` _(desktop)_\n- `apps/browser/src/_locales/en/messages.json` _(browser)_\n\n**Linked fields (if applicable):**\n\n- `libs/common/src/vault/enums/linked-id-type.enum.ts`\n\n**Autofill (if applicable — only if `$0` is `browser` or `all`):**\n\n- List relevant autofill files from `apps/browser/src/autofill/` only if the type supports\n  autofill\n\n**Restricted item types (if applicable):**\n\nRestricted item type enforcement is used across all clients. Include files for `$0`:\n\n_Always included (shared):_\n\n- `libs/common/src/vault/services/vault-settings/vault-settings.service.ts` - Restricted types service\n\n_Include if `$0` is `web` or `all`:_\n\n- `apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.ts` - Policy configuration UI\n- `apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.html`\n\n_Include if `$0` is `browser` or `all`:_\n\n- `apps/browser/src/vault/popup/components/vault/item-more-options/item-more-options.component.ts` - Restricted type checks\n\n_Include if `$0` is `cli` or `all`:_\n\n- `apps/cli/src/vault/create.command.ts` - Restricted type checks\n- `apps/cli/src/commands/list.command.ts` - Restricted type checks\n- `apps/cli/src/commands/get.command.ts` - Restricted type checks\n\n### 4. Localization Keys\n\nList all i18n keys that need to be added. At minimum:\n\n- Type label\n- Field labels for each type-specific field\n\n### 5. Tests\n\nList all test files that need to be created or updated:\n\n- `libs/common/src/vault/enums/cipher-type.spec.ts`\n- `libs/common/src/vault/models/domain/cipher.spec.ts`\n- `libs/common/src/vault/models/domain/<type>.spec.ts` (new)\n- `libs/common/src/vault/models/view/cipher.view.spec.ts`\n- `libs/common/src/vault/services/cipher.service.spec.ts`\n- `libs/common/src/vault/services/cipher-sdk.service.spec.ts`\n- `libs/common/src/vault/icon/build-cipher-icon.spec.ts`\n- `libs/common/src/models/export/cipher.export.spec.ts`\n- `libs/vault/src/cipher-form/components/cipher-form.component.spec.ts`\n- `libs/vault/src/cipher-view/cipher-view.component.spec.ts`\n- Form section component spec (new)\n\n### 6. Recommended Implementation Order\n\nRecommended implementation order, customized for this specific type. Only include steps relevant\nto `$0` (shared steps are always included):\n\n1. Core enum addition _(shared)_\n2. Feature flag registration _(shared)_\n3. Model stack (API, Data, Domain, View, Export if applicable) _(shared)_\n4. Container switch updates _(shared)_\n5. SDK bindings (`toSdk*`/`fromSdk*`) _(shared)_\n6. Localization keys _(`$0`)_\n7. Shared UI (icon, menu items) _(shared)_\n8. Vault filters with feature flag gating — CRITICAL for ciphers to appear _(shared + `$0`)_\n9. New item menus with feature flag gating _(shared + `$0`)_\n10. Per-app UI (form section, view section) _(shared)_\n11. Context menu / copy actions — see Section 8 _(shared + `$0`)_\n12. CLI _(only if `$0` is `cli` or `all`)_\n13. Autofill _(only if `$0` is `browser` or `all`)_\n14. Tests _(shared + `$0`)_\n\n### 7. Risks and Considerations\n\n- Cross-repo coordination requirements\n- Feature flag rollout strategy\n- Backward compatibility concerns\n- Any fields that need special encryption handling (reminder: no new encryption logic in clients)\n- Performance considerations for large vaults\n- **i18n key reuse** - Before adding new locale keys, check whether existing keys already have the\n  desired display value. If an existing key has the same message text, reuse it instead of creating\n  a duplicate. Only create new keys when no existing key matches.\n\n### 8. Context Menu / Copy Actions\n\nEach cipher type can expose copiable fields in the vault list item context menus (right-click / more\nmenu). Include only the sections relevant to `$0`.\n\n#### Core Infrastructure (always included)\n\n| File                                                    | What to add                                                                                                                                                                                                                                     |\n| ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `libs/vault/src/services/copy-cipher-field.service.ts`  | Add field names to the `CopyAction` type union. Add entries to the `CopyActions` record with `typeI18nKey` (i18n key for the toast message), `protected` (whether it requires password re-prompt), and optional `event` (for event collection). |\n| `libs/common/src/vault/utils/cipher-view-like-utils.ts` | Add cases to `hasCopyableValue()` that check whether the cipher has a non-empty value for each copiable field.                                                                                                                                  |\n\n#### Browser (include if `$0` is `browser` or `all`)\n\n| File                                                                                              | What to add                                                                                                                                                              |\n| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `apps/browser/src/vault/popup/components/vault/item-copy-action/item-copy-actions.component.ts`   | Add a `singleCopyable<Type>` getter (for single-field quick copy button), a `has<Type>Values` getter, and a `getNumberOf<Type>Values()` method. Follow the Card pattern. |\n| `apps/browser/src/vault/popup/components/vault/item-copy-action/item-copy-actions.component.html` | Add a section using `@if` syntax (NOT `*ngIf`) with the single/multi field pattern.                                                                                      |\n\n#### Web (include if `$0` is `web` or `all`)\n\n| File                                                                            | What to add                                                                                                               |\n| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |\n| `apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts`   | Add `is<Type>Cipher` and `hasVisible<Type>Options` getters. Add `hasVisible<Type>Options` to the `showMenuDivider` check. |\n| `apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html` | Add copy buttons using `@if` syntax with `appCopyField` directive.                                                        |\n\n#### Desktop (include if `$0` is `desktop` or `all`)\n\n| File                                                                            | What to add                                                                                                                                                                              |\n| ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `apps/desktop/src/vault/app/vault-v3/vault-items/vault-cipher-row.component.ts` | Add a `CipherType.<Type>` case to the `copyFields` computed signal, returning `CopyFieldConfig[]` entries. This is the most modern pattern — uses a computed signal rather than getters. |\n\n#### Critical Warnings\n\n- **CLI has no copy menu UI** — do not add copy-related i18n keys to the CLI locale.\n- **Only expose fields that should be copiable** — not every cipher field needs a copy action. Check\n  with product requirements for which fields get copy buttons.\n\n<!-- File: .claude/skills/create-hec-event-integration/example/token-auth.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Example Skill\n\nPath: `.claude/skills/create-hec-event-integration/example/token-auth.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/create-hec-event-integration/example/token-auth.md)\n\n<!-- File: .claude/skills/create-hec-event-integration/SKILL.md (Tokens: ~1600 | Category: anthropic-skill) -->\n---\nname: create-hec-event-integration\ndescription: Use when adding a new HEC (HTTP Event Collector) event integration to the Bitwarden web client. Implements the Splunk token authentication model (Bearer token + URI). Covers feature flag setup and card registration behind the flag. Does not apply to API key integrations or integrations requiring a custom connect dialog.\n---\n\n# Create HEC Event Integration (Token Auth)\n\n## Step 1 - Prompts\n\nAsk these questions one at a time — wait for each answer before proceeding.\n\n**Prompt 1 — Service name:** \"What is the service name for this integration?\" (e.g. `Splunk`, `CrowdStrike`, `Panther`)\n\nUse the answer as `<ServiceName>` throughout. The string value in the constant must exactly match what you use as the card's `name` in Step 4 — a mismatch silently saves the config with the wrong service name.\n\n**Prompt 2 — Authentication:** \"How is this integration authenticated?\" (e.g. `Token`, `API key`)\n\n- **If Token** — continue with the steps below.\n- **If anything else** — stop and inform the user: \"This skill currently only supports token-based authentication. Support for other authentication methods hasn't been added yet.\"\n\n**Prompt 3 — Logos:** \"Do you have the integration logo(s) ready to provide?\"\n\n- **If yes** — ask for the light-mode SVG file path, and optionally a dark-mode SVG path. Copy both to `apps/web/src/images/integrations/` using the naming convention `logo-<service-name-kebab>-color.svg` and `logo-<service-name-kebab>-darkmode.svg`. Use those filenames in Step 4.\n- **If no** — use placeholder paths in Step 4 and add a `// TODO: add logo before shipping` comment.\n\n## Step 2 — Add service name constant\n\n**File:** `bitwarden_license/bit-common/src/dirt/organization-integrations/models/organization-integration-service-type.ts`\n\nAdd to `OrganizationIntegrationServiceName`:\n\n```typescript\nexport const OrganizationIntegrationServiceName = Object.freeze({\n  CrowdStrike: \"CrowdStrike\",\n  Datadog: \"Datadog\",\n  Huntress: \"Huntress\",\n  <ServiceName>: \"<ServiceName>\", // ← add here\n} as const);\n```\n\n## Step 3 — Add feature flag\n\n**File:** `libs/common/src/enums/feature-flag.enum.ts`\n\nAdd the enum entry and its default. The enum key is PascalCase; the string value is kebab-case (e.g. `CrowdStrike` → `crowdstrike`, `Sumo Logic` → `sumo-logic`):\n\n```typescript\n// In the FeatureFlag enum:\nEventManagementFor<ServiceName> = \"event-management-for-<service-name-kebab>\",\n\n// In the defaultFlags object:\n[FeatureFlag.EventManagementFor<ServiceName>]: FALSE,\n```\n\nExample for `Panther`:\n\n```typescript\nEventManagementForPanther = \"event-management-for-panther\",\n[FeatureFlag.EventManagementForPanther]: FALSE,\n```\n\n## Step 4 — Register the card behind the feature flag\n\n**File:** `bitwarden_license/bit-web/src/app/dirt/organization-integrations/organization-integrations.resolver.ts`\n\nIf logos were provided, copy them to `apps/web/src/images/integrations/` first, then use the actual filenames below. If not, use the placeholder paths with the TODO comment:\n\n```typescript\nconst <serviceName>FeatureEnabled = await firstValueFrom(\n  this.configService.getFeatureFlag$(FeatureFlag.EventManagementFor<ServiceName>),\n);\n\nif (<serviceName>FeatureEnabled) {\n  integrations.push({\n    name: OrganizationIntegrationServiceName.<ServiceName>, // must match Step 1 exactly\n    linkURL: \"https://bitwarden.com/help/<service-name>-siem/\",\n    image: \"../../../../../../../images/integrations/logo-<service-name>-color.svg\", // TODO: add logo before shipping (if not yet provided)\n    imageDarkMode: \"../../../../../../../images/integrations/logo-<service-name>-darkmode.svg\", // TODO: add logo before shipping (omit if no dark mode variant)\n    type: IntegrationType.EVENT,\n    canSetupConnection: true,\n    integrationType: OrganizationIntegrationType.Hec,\n  });\n}\n```\n\nNo changes needed to `IntegrationCardComponent` — new HEC services fall into the existing `else` branch, which calls `openHecConnectDialog` → `saveHec` → `deleteHec`. These methods already call `buildHecConfiguration` and `buildHecTemplate` using the card's `name` as the service name.\n\n## Step 5 — Add tests\n\n**File:** `bitwarden_license/bit-common/src/dirt/organization-integrations/models/integration-builder.spec.ts`\n\nAdd one `it` block inside the existing `describe(\"buildHecConfiguration\", ...)` block, and one inside `describe(\"buildHecTemplate\", ...)`. Use typed property access — do not use `JSON.parse`:\n\n```typescript\n// Inside describe(\"buildHecConfiguration\", ...)\nit(\"should work with <ServiceName> service name\", () => {\n  const config = OrgIntegrationBuilder.buildHecConfiguration(\n    \"https://test.<servicename>.com/hec\",\n    \"test-token\",\n    OrganizationIntegrationServiceName.<ServiceName>,\n  );\n\n  expect(config).toBeInstanceOf(HecConfiguration);\n  expect((config as HecConfiguration).uri).toBe(\"https://test.<servicename>.com/hec\");\n  expect((config as HecConfiguration).scheme).toBe(\"Bearer\");\n  expect((config as HecConfiguration).token).toBe(\"test-token\");\n  expect(config.bw_serviceName).toBe(OrganizationIntegrationServiceName.<ServiceName>);\n});\n\n// Inside describe(\"buildHecTemplate\", ...)\nit(\"should work with <ServiceName> service name\", () => {\n  const template = OrgIntegrationBuilder.buildHecTemplate(\n    \"test-index\",\n    OrganizationIntegrationServiceName.<ServiceName>,\n  );\n\n  expect(template).toBeInstanceOf(HecTemplate);\n  expect((template as HecTemplate).index).toBe(\"test-index\");\n  expect(template.bw_serviceName).toBe(OrganizationIntegrationServiceName.<ServiceName>);\n});\n```\n\n## Step 6 — Run unit tests\n\nRun the unit tests for the spec file and confirm they all pass before finishing:\n\n```bash\nnpx jest bitwarden_license/bit-common/src/dirt/organization-integrations/models/integration-builder.spec.ts\n```\n\nAll tests must pass. If any fail, fix them before proceeding.\n\n## Common Mistakes\n\n| Mistake                                                                 | Fix                                                                                                   |\n| ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |\n| `name` in card doesn't match `OrganizationIntegrationServiceName` value | They must be identical strings — `saveHec()` casts the name directly                                  |\n| Feature flag default not set to `FALSE`                                 | Always add the default entry in `defaultFlags`; new flags without a default will not work correctly   |\n| Kebab-case mismatch in flag string                                      | Convert consistently: lowercase, spaces → hyphens                                                     |\n| Adding a new `OrganizationIntegrationType`                              | Not needed — all HEC services share `OrganizationIntegrationType.Hec`                                 |\n| Creating a new config/template class                                    | Not needed — `HecConfiguration` and `HecTemplate` handle all HEC services                             |\n| Referencing an image path without copying the file                      | Copy SVGs to `apps/web/src/images/integrations/` first; if logos aren't ready, leave the TODO comment |\n\n<!-- File: .claude/skills/create-pull-request/SKILL.md (Tokens: ~409 | Category: anthropic-skill) -->\n---\nname: create-pull-request\nversion: 0.1.0\ndescription: Pull request creation workflow for Bitwarden Clients. Use when creating PRs, writing PR descriptions, or preparing branches for review. Triggered by \"create PR\", \"pull request\", \"open PR\", \"gh pr create\", \"PR description\".\n---\n\n# Create Pull Request\n\n## PR Title Format\n\n```\n[PROJ-XXXXX] Short imperative summary\n```\n\n**Examples:**\n\n- `[PM-12345] Add passkey support to vault item creation`\n- `[CL-1124] Increase badge max-width`\n- `[PM-33446] Ensure attachments are cleared on clone`\n\n**Rules:**\n\n- Include the Jira ticket prefix (e.g. `PM-`, `CL-`)\n- Keep under 70 characters total\n- Use imperative mood in the summary\n\n---\n\n## PR Body Template\n\n**IMPORTANT:** Always follow the repo's PR template at `.github/PULL_REQUEST_TEMPLATE.md`. Add TODO items to the screenshots section for each scenario that should be captured if any.\n\n---\n\n## Pre-PR Checklist\n\n1. **All tests pass**: Run `npm test`\n2. **Lint clean**: Run `npm run lint`\n3. **No unintended changes**: Check `git diff origin/main...HEAD` for unexpected files\n4. **Branch up to date**: Rebase on `main` if needed\n\n---\n\n## Creating the PR\n\n```bash\n# Ensure branch is pushed\ngit push -u origin <branch-name>\n\n# Create PR as draft by default (body follows .github/PULL_REQUEST_TEMPLATE.md)\ngh pr create --draft --title \"[PM-XXXXX] Short summary\" --body \"<fill in from PR template>\"\n```\n\n**Default to draft PRs.** Only create a non-draft (ready for review) PR if the user explicitly requests it.\n\n---\n\n## Base Branch\n\n- Default target: `main`\n- Check with team if targeting a feature branch instead\n\n<!-- File: .claude/skills/evaluating-sdk-internal-updates/SKILL.md (Tokens: ~2065 | Category: anthropic-skill) -->\n---\nname: evaluating-sdk-internal-updates\ndescription: Evaluates a bitwarden/clients \"Update sdk-internal to\" PR against the sdk-internal commit range for compile-time and runtime breaking changes, maps affected symbols to TypeScript call sites, and applies clear in-scope fixes. Use when reviewing an SDK bump PR, an @bitwarden/sdk-internal version change in package.json, or triaging sdk-internal breaking changes. Requires a sibling `bitwarden/sdk-internal` clone.\nallowed-tools:\n  - Bash(node .claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs *)\n  - Bash(npm run test:types)\n  - Bash(npm run lint:fix)\n  - Bash(npm test -- *)\n  - Bash(npx prettier *)\n  - Bash(gh pr diff:*)\n  - Bash(gh pr view:*)\n  - Bash(git -C * log *)\n  - Bash(git -C * show *)\n  - Bash(git grep:*)\n  - Bash(git add:*)\n  - Bash(git commit:*)\n  - Bash(grep:*)\n  - Read\n  - Grep\n  - Glob\n  - Edit\n  - Write\n  - Skill(writing-client-code)\n  - Skill(bitwarden-delivery-tools:committing-changes)\n---\n\n# Evaluating sdk-internal Updates\n\n**Identify both compile-time and runtime breaks before fixing anything — fixing the first break found is not finishing.** Steps 4-7 cover the entire commit range, step 8 reports it, and step 9 starts only after that.\n\nThis SDK is the boundary where Protected Data becomes Vault Data. A fix must not send a decrypted value to an API service, log one, or move a decrypt out of the SDK into TypeScript. `.claude/CLAUDE.md` forbids new encryption logic in this repo, so a bump that appears to require some is a finding to report, not a fix to write. [Security definitions](https://contributing.bitwarden.com/architecture/security/definitions).\n\n## Identify\n\nBinding surface facts specific to this SDK: the pin is two npm versions in the root `package.json`, `@bitwarden/sdk-internal` and `@bitwarden/commercial-sdk-internal`, always the same string — `npm run lint:sdk-internal-versions` fails if they differ. The version encodes no commit; the sdk-internal SHA lives in `VERSION` inside the tarball. Nothing generated is committed, here or upstream: the API record is `bitwarden_wasm_internal.d.ts` inside the published package, whose root and `node/` copies are byte-identical, and `bitwarden_wasm_internal_bg.wasm.d.ts` is raw ABI and never a finding.\n\nThe annotations behind this surface are `#[wasm_bindgen]` and `derive(..., Tsify)`. `#[uniffi::export]` is mobile-only and largely disjoint, so a uniffi sweep under-reports this surface badly. Serde attributes are part of the emitted type: `rename`, `rename_all`, `skip` and `Option` changes alter the `.d.ts` with no Rust signature change, and a `features = [\"wasm\"]` edit in `crates/bitwarden-wasm-internal/Cargo.toml` adds or removes types wholesale. Enum members are wire values (`CipherType.Login = 1`); a renumbering compiles clean at every call site and corrupts data.\n\nMost classes have a `private constructor()` and are reachable only through a parent client accessor (`sdk.user_crypto_management()`), so a renamed accessor breaks callers with the class itself unchanged. `tsconfig.base.json` sets `skipLibCheck: true`, so only call sites are checked. `bitwarden_license/bit-common/src/platform/sdk/sdk-alias.d.ts` re-exports the commercial package under the `@bitwarden/sdk-internal` module name and ESLint forbids importing it directly, so call sites grep identically for both packages.\n\n1. `gh pr diff <PR> -R bitwarden/clients -- package.json` → the old and new version.\n2. Locate the `bitwarden/sdk-internal` clone in a sibling directory — `../sdk-internal`, where CI places it. If there is none, stop and tell the user it is a required prerequisite for this skill; do not clone it yourself.\n3. Read the baseline type check. **In CI it has already run and the prompt names its log; grep it for `error TS` and treat that as this step's output. Do not run it.** Locally, `npm run test:types`. Note any failure and continue — a clean run rules out compile breaks only where the baseline looks, and step 7 is where it does not look.\n4. `node .claude/skills/evaluating-sdk-internal-updates/sdk-surface-diff.mjs <old-version> <new-version>`, then again with `--commercial`. It prints RANGE (including the sdk-internal SHA pair step 5 needs), REMOVED, ADDED and MUTATED, keyed per member: a class method, interface property and enum member each get their own `Owner.member` entry. In ADDED, only a pre-existing owner is a break — a new non-optional property on an existing interface breaks every construction site.\n5. `git -C ../sdk-internal log --reverse --oneline <old-sha>..<new-sha>`, then `show` each commit. Classify per hunk, not per commit — a commit with one additive headline change can still carry a second, unrelated breaking hunk. A hunk that only touches a macro invocation does not show the surface; read the macro's definition before classifying it. This is also what explains a property the step 4 diff shows renamed or gone.\n6. For every symbol from step 4, `git grep -n '<Symbol>'` from the repo root for the bare name — no module list and no import-prefix filter, since everything arrives as a plain `@bitwarden/sdk-internal` import and both apps and `bitwarden_license` consume it. Use `git grep`, not `grep -r`, which also searches `node_modules/@bitwarden/*sdk-internal/` and reports the bindings themselves as call sites. A surface change stays a candidate until a call site makes it a finding.\n7. Three sets of call sites step 3 does not cover. Read each one the range touches.\n   - `apps/` and `bitwarden_license/` get no dedicated `tsc` leg and ride only on `tsc-strict`, which filters by file, so an ordinary type error inside a `@ts-strict-ignore` file is discarded: `git grep -l '@bitwarden/sdk-internal' -- 'apps/**' 'bitwarden_license/**'`, then check that file list for `@ts-strict-ignore` with the `Grep` tool rather than piping into `xargs`, which carries no grant of its own.\n   - The root `tsconfig.json` excludes app spec files but not `bitwarden_license` ones, so `git grep -l '@bitwarden/sdk-internal' -- 'apps/**/*.spec.ts'` is type-checked by nothing; the `bitwarden_license` equivalent is still covered by `tsc-strict`.\n   - `git grep -l 'jest.mock(\"@bitwarden/sdk-internal\"'` — factory-form mocks are not checked against the real module and drift silently. `libs/common/src/platform/spec/mock-sdk.service.ts` and `libs/common/spec/jest-sdk-client-factory.ts` are structural and break loudest, the latter on any `PasswordManagerClient` constructor change.\n8. Report under these headings, which are the same across every repo running this evaluation: `## SDK bump evaluated`, `## Compile-time breaks`, `## Runtime considerations` (wire-value and serialization findings go here; omit the heading when there are none), `## Everything else in range — confirmed safe`, `## Commit`. Give each finding its commit, symbol and call sites, and state whether the type check passed inside `## Compile-time breaks` rather than as its own section. Cite sdk-internal commits and PRs as `bitwarden/sdk-internal#<N>` or a full URL — a bare `#<N>` copied from a commit subject auto-links into bitwarden/clients and tags an unrelated PR.\n\n## Resolve\n\n9. Fix anything found, compile-time or runtime, whenever the correct behavior is clear and in scope. For a new required argument or method with no existing consumer, grep the underlying concept rather than the new name, which does not exist yet; with no consumer, stub it — an empty body with a `// no-op` comment, or a default return, never a `throw`, which turns a stub into a crash. A sibling's structure is a template; its behavior is not evidence for yours, so read the SDK to choose between two plausible argument values. Never edit `package.json`: the pin is the bump's output, not the fix's. If unsure, report it instead of guessing, along with anything needing a product decision. Invoke `Skill(writing-client-code)` first if the fix is not purely mechanical, then commit with `Skill(bitwarden-delivery-tools:committing-changes)`.\n10. `npm run lint:fix`, then `npx prettier --write <edited files>`, then `npm run test:types`, then `npm test -- <edited paths> --maxWorkers=2`. Run all four even in CI: `lint.yml`'s `npm run lint` step runs ESLint as well as Prettier, and `--maxWorkers=2` is this repo's workaround for Jest exhausting memory.\n\n<!-- File: .claude/skills/figma-to-angular/evals/evals.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Evals Skill\n\nPath: `.claude/skills/figma-to-angular/evals/evals.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/.claude/skills/figma-to-angular/evals/evals.json)\n\n<!-- File: .claude/skills/figma-to-angular/SKILL.md (Tokens: ~2223 | Category: anthropic-skill) -->\n---\nname: figma-to-angular\ndescription: >\n  Converts Figma designs into production Angular components with Storybook stories for the Bitwarden\n  Clients monorepo. Use this skill whenever the user provides a Figma URL and wants to create an\n  Angular component, or mentions \"implement this design\", \"create a component from Figma\",\n  \"build this from the design spec\", or similar. Also trigger when the user pastes a Figma link\n  and asks for a component, even if they don't say \"Figma\" explicitly.\n---\n\n# Figma to Angular Component\n\nThis skill turns a Figma design spec into a fully implemented Angular component with Storybook\nstories in the Bitwarden Clients monorepo. The output should match the design visually while\nfollowing all codebase conventions.\n\n## Workflow\n\n### Phase 1: Extract the design\n\nCall `get_design_context` from the Figma MCP server. Extract the fileKey and nodeId from the URL:\n\n- `figma.com/design/:fileKey/:fileName?node-id=:nodeId` → convert `-` to `:` in nodeId\n- Pass `clientFrameworks: \"angular\"` and `clientLanguages: \"typescript,html,css\"`\n\nThe response contains reference code (React+Tailwind), a screenshot, and design metadata. Treat the\ncode as a **structural reference only** — it must be fully converted to Angular.\n\n### Phase 2: Explore the codebase\n\nBefore writing any code, understand the conventions of the target location. Use an Explore agent to\ndiscover:\n\n1. **Component file patterns** — look at 2–3 sibling components in the same directory. Note the\n   file naming, module structure, barrel exports, and whether they use standalone components.\n2. **Existing components to reuse** — search for components that match parts of the design (labels,\n   buttons, inputs, icons). Reuse them instead of reimplementing.\n3. **Tailwind configuration** — read the relevant `tailwind.config` to understand the prefix\n   (commonly `tw-`), available design tokens, and color system.\n4. **Storybook patterns** — check how sibling stories are structured: imports, `moduleMetadata`,\n   `I18nMockService` usage, `argTypes`.\n5. **i18n setup** — find the `I18nPipe` import path and the locale messages file to add new keys.\n\nThis exploration is critical. Skipping it leads to components that don't match the codebase and\nrequire extensive rework.\n\n### Phase 3: Map design tokens\n\nCreate a mapping from Figma's design values to the codebase's Tailwind tokens. The Figma response\nincludes raw CSS values (hex colors, px sizes). These must be translated to semantic tokens.\n\nCommon mappings for Bitwarden:\n\n| Figma concept      | Where to look                                               |\n| ------------------ | ----------------------------------------------------------- |\n| Colors (hex)       | `tailwind.config` → `colors`, `bg`, `fg`, `border` sections |\n| Font sizes         | `tailwind.config` → `extend.fontSize`                       |\n| Spacing / padding  | Standard Tailwind scale with prefix                         |\n| Border radius      | Standard Tailwind (`rounded-*`)                             |\n| Component variants | Existing component source (e.g. `base-button.directive.ts`) |\n\nNever use raw hex values. Always map to a semantic token. If no exact match exists, use the closest\navailable token and note the discrepancy.\n\n### Phase 4: Plan the component\n\nBefore writing code, create a plan covering:\n\n- **Selector** — follows the relevant prefix convention\n- **Inputs and outputs** — use Angular signal-based `input()` and `output()` functions\n- **Content projection** — identify slots for `<ng-content>` (labels, hints, actions)\n- **Existing component reuse** — which existing components to import and use\n- **i18n keys** — list all user-visible strings that need localization\n- **Accessibility** — keyboard navigation, ARIA attributes, focus management\n\nPresent this plan to the user and get approval before writing files.\n\n### Phase 5: Implement\n\nCreate these files following the patterns discovered in Phase 2:\n\n#### Component TypeScript (`component-name.component.ts`)\n\n```typescript\n@Component({\n  selector: \"bit-component-name\",\n  templateUrl: \"./component-name.component.html\",\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  imports: [/* discovered dependencies */],\n  host: {\n    class: \"tw-block\",\n  },\n})\n```\n\nKey conventions:\n\n- `ChangeDetectionStrategy.OnPush` always\n- Signal-based inputs: `input()`, `input.required<T>()`, `input(default, { transform: booleanAttribute })`\n- Signal-based outputs: `output<T>()`\n- Internal state with `signal()` and `viewChild()`\n- Protected methods for template bindings, private for internal logic\n- No TypeScript enums — use `Object.freeze({ ... } as const)` with a same-name type alias (ADR-0025).\n  Even for simple string unions, prefer the const object pattern for consistency.\n\n#### Component template (`component-name.component.html`)\n\n- Use new Angular control flow: `@if`, `@for`, `@switch`\n- Avoid deprecated attributes like `ngClass` and `ngStyle`, prefer `class` and `style`.\n- All Tailwind classes use the library's prefix (check `tailwind.config`)\n- All user-visible strings go through `{{ \"key\" | i18n }}` or `{{ \"key\" | i18n: param }}`\n- Reuse existing components (`bit-label`, `bitButton`, `bit-hint`, etc.)\n\n#### Barrel export (`index.ts`)\n\n```typescript\nexport { ComponentNameComponent } from \"./component-name.component\";\n```\n\n#### Storybook stories (`component-name.stories.ts`)\n\nEvery component gets stories. Follow the exact patterns from sibling components:\n\n```typescript\nimport { Meta, moduleMetadata, StoryObj } from \"@storybook/angular\";\nimport { I18nService } from \"@bitwarden/common/platform/abstractions/i18n.service\";\nimport { I18nMockService } from \"../utils/i18n-mock.service\";\n\nexport default {\n  title: \"Component Library/ComponentName\",\n  component: ComponentNameComponent,\n  decorators: [\n    moduleMetadata({\n      imports: [/* component + dependencies */],\n      providers: [\n        {\n          provide: I18nService,\n          useFactory: () =>\n            new I18nMockService({\n              // All i18n keys used by the component\n              // Use __$1__ for parameter placeholders\n            }),\n        },\n      ],\n    }),\n  ],\n  parameters: {\n    design: {\n      type: \"figma\",\n      url: \"/* original Figma URL */\",\n    },\n  },\n} as Meta<ComponentNameComponent>;\n```\n\nInclude stories for:\n\n- Default state\n- Key variant states (if applicable)\n- Edge cases visible in the design (disabled, loading, empty, etc.)\n\n#### Unit tests (`component-name.component.spec.ts`)\n\nWrite unit tests when the component has behavior that Storybook stories alone can't verify. Stories\ncover visual rendering and variant appearance — don't duplicate that. Tests should focus on:\n\n- **Computed state and logic** — e.g. a rating component rounding fractional values, a progress bar\n  clamping percentages to 0–100, variant-to-class mappings\n- **Event emission** — outputs fire with the correct payload (files selected, rating changed, toast\n  dismissed)\n- **Input validation / edge cases** — boundary values, empty inputs, invalid types rejected\n- **Accessibility attributes** — correct `role`, `aria-label`, `aria-live` values change with state\n- **Keyboard interaction** — Enter/Space triggers actions, arrow keys navigate\n\nSkip tests for components that are purely presentational with no logic beyond displaying inputs\n(e.g. a simple badge or divider). Look at sibling `*.spec.ts` files to match the test setup\npatterns (TestBed configuration, imports, mock providers).\n\n#### Update barrel exports\n\nAdd `export * from \"./component-name\";` to the parent directory's `index.ts`, inserted\nalphabetically.\n\n### Phase 6: Verify in Storybook\n\nUse Playwright MCP tools to verify the component renders correctly:\n\n1. Navigate to the story's iframe URL:\n   `http://localhost:6006/iframe.html?id=component-library-component-name--default&viewMode=story`\n2. Take a snapshot to confirm the component rendered without errors\n3. Take a screenshot and compare it against the Figma design screenshot\n4. Test interactive behavior:\n   - Click buttons and interactive elements\n   - Verify correct responses (dialogs, state changes, etc.)\n5. Check the disabled story if applicable\n\nIf Storybook isn't running or Playwright isn't available, tell the user and suggest they verify\nmanually.\n\n### Phase 7: Iterate\n\nPresent the screenshot to the user alongside the original Figma screenshot. Ask if adjustments are\nneeded. Common refinements:\n\n- Token mismatches (wrong shade, spacing off)\n- Missing hover/focus states\n- Accessibility gaps\n- i18n strings that need rewording\n\n## Key principles\n\n- **Never hardcode user-visible strings.** Every string goes through `I18nPipe`. This includes\n  aria-labels and other accessibility text — they need localization too. Add keys to the locale file\n  and provide mocks in stories.\n- **Reuse existing components aggressively.** If the design has a button, use `bitButton`. If it\n  has a label, use `bit-label`. Don't reimplement what exists.\n- **Explore before you build.** The codebase exploration step prevents most rework. Patterns vary\n  between apps and libraries — always check the specific target directory.\n- **Semantic tokens over raw values.** Map every Figma color, spacing, and font size to the\n  codebase's design token system. Raw hex values break theming.\n- **Use native HTML patterns for file inputs.** For file upload components, use a `<label for>`\n  element pointing at a hidden `<input type=\"file\">` rather than programmatic `.click()`. The\n  codebase has an `AriaDisabledClickCaptureService` that intercepts programmatic clicks on certain\n  elements, causing silent failures.\n- **No NgModules.** All new components must be standalone (`imports: [...]` in the `@Component`\n  decorator). Do not create `*.module.ts` files. The codebase is moving away from NgModules.\n- **Plan before you code.** The plan step catches API design issues early. It's cheaper to fix a\n  plan than to rewrite files.\n\n<!-- File: .claude/skills/fix-angular-fixmes/SKILL.md (Tokens: ~2550 | Category: anthropic-skill) -->\n---\nname: fix-angular-fixmes\ndescription: Resolves eslint-disable suppression comments throughout the Bitwarden clients codebase by fixing the underlying issue. Use when the user asks to \"fix FIXMEs\", \"fix eslint suppressions\", \"clean up eslint-disable-next-line\", \"resolve CL-764\", \"resolve CL-903\", \"fix OnPush eslint suppressions\", \"fix Signals eslint suppressions\", or reduce linting suppressions.\nallowed-tools: Read, Write, Edit, Glob, Grep, Bash(npx ng generate:*), Bash(npm run lint:fix), Bash(npm run test)\n---\n\n## Key rules\n\n- Fix the underlying issue — never just delete the suppression comment and leave broken code.\n- Remove the **complete comment block**: FIXME line (if any) + TODO: Skipped block (if any) + eslint-disable-next-line line.\n- Both FIXME-paired and standalone suppressions are the same migration debt.\n- For Angular migration rules, prefer CLI schematics over manual edits.\n- Do NOT convert service observables to signals (ADR-0027).\n- For OnPush and signals patterns, the `angular-modernization` skill is the authoritative source — this skill only owns the ESLint suppression mechanics.\n\n## Step 1: Discover all suppressions\n\nUse the `Grep` tool to find suppressions in the target path:\n\n- Pattern `eslint-disable` — finds all eslint suppressions\n- Pattern `FIXME.*CL-` — finds Angular FIXME-tracked ones specifically\n\nGroup results by rule name. Two forms appear in this codebase:\n\n**Form A — FIXME-paired** (a FIXME tracking comment sits above the disable):\n\n```typescript\n// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush\n// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection\n```\n\n**Form B — Standalone** (disable without a FIXME, or with a CLI skip comment):\n\n```typescript\n// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection\n// TODO: Skipped for signal migration because:\n//  Accessor inputs cannot be migrated as they are too complex.\n// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals\n// eslint-disable-next-line @angular-eslint/prefer-signals\n```\n\nBoth forms must be fixed the same way.\n\n## Rule reference\n\n| Category         | Rule                                                        | Section below                                       |\n| ---------------- | ----------------------------------------------------------- | --------------------------------------------------- |\n| Angular          | `@angular-eslint/prefer-on-push-component-change-detection` | [OnPush](#onpush)                                   |\n| Angular          | `@angular-eslint/prefer-signals`                            | [Signals](#signals)                                 |\n| Angular          | `@angular-eslint/prefer-output-emitter-ref`                 | [Signals](#signals)                                 |\n| Angular template | `@angular-eslint/template/button-has-type`                  | [HTML rules](#html-rules)                           |\n| TypeScript       | `@typescript-eslint/no-floating-promises`                   | [no-floating-promises](#no-floating-promises)       |\n| TypeScript       | `@typescript-eslint/no-unused-vars`                         | [no-unused-vars](#no-unused-vars)                   |\n| TypeScript       | `@typescript-eslint/no-unsafe-function-type`                | [no-unsafe-function-type](#no-unsafe-function-type) |\n| RxJS             | `rxjs/no-async-subscribe`                                   | [rxjs rules](#rxjs-rules)                           |\n| RxJS             | `rxjs-angular/prefer-takeuntil`                             | [rxjs rules](#rxjs-rules)                           |\n| Bitwarden        | `@bitwarden/platform/no-enums`                              | [no-enums](#no-enums)                               |\n| Bitwarden        | `@bitwarden/components/no-bwi-class-usage`                  | [HTML rules](#html-rules)                           |\n| General          | `no-restricted-imports`                                     | [no-restricted-imports](#no-restricted-imports)     |\n| General          | `no-console`                                                | [no-console](#no-console)                           |\n| General          | `no-empty`                                                  | [no-empty](#no-empty)                               |\n| General          | bare `// eslint-disable-next-line`                          | [bare disable](#bare-disable)                       |\n| Tailwind         | `tailwindcss/no-custom-classname`                           | [HTML rules](#html-rules)                           |\n\n## OnPush\n\n**Rule:** `@angular-eslint/prefer-on-push-component-change-detection`\n\nFollow the OnPush guidance in the `angular-modernization` skill (add `changeDetection: ChangeDetectionStrategy.OnPush`, remove `ChangeDetectorRef` if only used for `detectChanges()`). Then remove the FIXME + `eslint-disable-next-line` lines.\n\n> `@Directive` does not support `changeDetection` — skip OnPush for pure directives.\n\n## Signals\n\n**Rules:** `@angular-eslint/prefer-signals`, `@angular-eslint/prefer-output-emitter-ref`\n\nApplies to `@Input()`, `@Output()`, `@ViewChild`, `@ContentChild`.\n\nFollow the Signal Inputs, Outputs, and Queries guidance in the `angular-modernization` skill (prefer CLI schematics, then manual conversion). After each migration, **manually remove** the FIXME and `eslint-disable-next-line` lines, and any `// TODO: Skipped for signal migration because:` comment blocks.\n\n> Do NOT convert service observables to signals — only component-local state and decorator bindings (ADR-0027).\n\n## no-floating-promises\n\n**Rule:** `@typescript-eslint/no-floating-promises`\n\nA returned Promise is not handled. Pick one fix:\n\n```typescript\n// 1. Await it (preferred in async functions)\nawait this.router.navigate([\"/login\"]);\n\n// 2. void — explicit fire-and-forget\nvoid this.router.navigate([\"/login\"]);\n\n// 3. Chain .catch() for explicit error handling\nthis.router.navigate([\"/login\"]).catch((err) => this.logService.error(err));\n```\n\nUse `void` for navigation or toast calls that genuinely don't need awaiting. Use `await` when the result matters or you're already in an async context.\n\n## no-unused-vars\n\n**Rule:** `@typescript-eslint/no-unused-vars`\n\n```typescript\n// Remove unused variable\nconst unused = computeSomething(); // delete this line\n\n// Or prefix with _ if it must be declared (e.g. destructuring)\nconst [_first, second] = array;\n\n// Or suppress a catch variable (TypeScript 4.0+)\ntry { ... } catch { ... } // omit the variable entirely\n```\n\n## no-unsafe-function-type\n\n**Rule:** `@typescript-eslint/no-unsafe-function-type`\n\nReplace the generic `Function` type with a specific signature:\n\n**Before**\n\n```typescript\nprivate callback: Function;\n```\n\n**After** — use the actual signature\n\n```typescript\nprivate callback: () => void;\n// or for unknown signatures:\nprivate callback: (...args: unknown[]) => unknown;\n```\n\n## RxJS rules\n\n**Rules:** `rxjs/no-async-subscribe`, `rxjs-angular/prefer-takeuntil`\n\n**`rxjs/no-async-subscribe`** — async callback inside `.subscribe()` swallows errors:\n\n**Before**\n\n```typescript\nthis.service.data$.subscribe(async (value) => {\n  await this.process(value);\n});\n```\n\n**After** — move async work into the pipe\n\n```typescript\nthis.service.data$\n  .pipe(\n    switchMap((value) => this.process(value)),\n    takeUntilDestroyed(),\n  )\n  .subscribe();\n```\n\n**`rxjs-angular/prefer-takeuntil`** — subscription without cleanup:\n\n**Before**\n\n```typescript\nthis.service.data$.subscribe((value) => {\n  this.data = value;\n});\n```\n\n**After** — add `takeUntilDestroyed()` (call in constructor or use `destroyRef`)\n\n```typescript\nconstructor() {\n  this.service.data$\n    .pipe(takeUntilDestroyed())\n    .subscribe((value) => { this.data = value; });\n}\n```\n\n## no-enums\n\n**Rule:** `@bitwarden/platform/no-enums`\n\nConvert TypeScript enums to const objects with type aliases (ADR-0025):\n\n**Before**\n\n```typescript\nenum CipherType {\n  Login = 1,\n  SecureNote = 2,\n}\n```\n\n**After**\n\n```typescript\nexport const CipherType = Object.freeze({ Login: 1, SecureNote: 2 } as const);\nexport type CipherType = (typeof CipherType)[keyof typeof CipherType];\n```\n\nUpdate all import sites — the usage (`CipherType.Login`) stays the same.\n\n## no-restricted-imports\n\n**Rule:** `no-restricted-imports`\n\nThe import is from a path that the ESLint config forbids. Steps:\n\n1. Read the context around the import to understand what is being imported.\n2. Check `eslint.config.mjs` at the repo root (or the nearest config) for the `no-restricted-imports` rule to find the allowed alternative path.\n3. Replace the import with the allowed path and remove the suppression.\n\nCommon cases: importing platform-internal modules directly instead of through the public API, or test-only helpers in non-test files.\n\n## no-console\n\n**Rule:** `no-console`\n\n```typescript\n// Remove debug statements\nconsole.log(\"debug\"); // delete\n\n// Replace with the application logging service\nthis.logService.error(\"Something failed\", error);\n```\n\nIn test files (`*.spec.ts`), a `console.error` or `console.warn` spy may be intentional — in that case, set up the spy properly rather than suppressing:\n\n```typescript\njest.spyOn(console, \"error\").mockImplementation(() => {});\n```\n\n## no-empty\n\n**Rule:** `no-empty`\n\nEmpty `catch` blocks silently swallow errors:\n\n**Before**\n\n```typescript\ntry {\n  await something();\n  // eslint-disable-next-line no-empty\n} catch {}\n```\n\n**After** — handle or log the error\n\n```typescript\ntry {\n  await something();\n} catch (e) {\n  // Intentionally ignored — operation is best-effort\n}\n\n// Or log it\ntry {\n  await something();\n} catch (e) {\n  this.logService.warning(\"Operation failed\", e);\n}\n```\n\n## Bare disable\n\n**Rule:** bare `// eslint-disable-next-line` (no rule specified)\n\nThis disables ALL rules for the next line, which is always wrong. Steps:\n\n1. Remove the suppression and run `npm run lint:fix` to see which specific rule triggers.\n2. Fix the underlying issue using the appropriate section above.\n3. If the violation truly cannot be fixed (rare), replace the bare disable with a specific named rule.\n\n## HTML rules\n\n**`@angular-eslint/template/button-has-type`** — Add an explicit type to every `<button>`:\n\n**Before**\n\n```html\n<button (click)=\"save()\">Save</button>\n```\n\n**After**\n\n```html\n<button type=\"button\" (click)=\"save()\">Save</button>\n<!-- or type=\"submit\" inside a <form> -->\n```\n\n**`@bitwarden/components/no-bwi-class-usage`** — Replace raw `bwi-*` icon classes with the `<bit-icon>` component or the appropriate icon token.\n\n**`tailwindcss/no-custom-classname`** — Use a Tailwind utility class with the `tw-` prefix, or register the class in the Tailwind safelist. Never use arbitrary custom class names.\n\n## Step 2: Cleanup checklist per fixed instance\n\n- [ ] `// FIXME(…)` line removed (if present)\n- [ ] `// TODO: Skipped for signal migration because: …` block removed (all lines, if present)\n- [ ] `// eslint-disable-next-line …` line removed\n- [ ] Unused imports removed; new imports added as needed\n- [ ] All in-class usages updated (e.g. signal reads need `()`)\n\n## Step 3: Validate\n\n```bash\nnpm run lint:fix\n```\n\nFix any errors that remain. Run `npm run test` if behaviour-critical code was changed.\n\n<!-- File: .claude/skills/writing-client-code/SKILL.md (Tokens: ~1108 | Category: anthropic-skill) -->\n---\nname: writing-client-code\ndescription: Bitwarden client code conventions for Angular and TypeScript. Use when creating components, services, or modifying web/browser/desktop apps.\n---\n\n### Why `libs/common` cannot import Angular\n\nCLI is a first-class client. Any code in `libs/common` must work without Angular's dependency injection, decorators, or lifecycle hooks. This is why cross-client services use abstract classes as interfaces — the concrete implementations (`Default*`, `Web*`, `Browser*`, `Desktop*`, `Cli*`) live in their respective apps.\n\n## Architectural Rationale\n\n### Thin components\n\nComponents contain only view logic. Business logic belongs in services. This keeps components testable, reusable, and prevents Angular lifecycle coupling from leaking into domain logic.\n\n### Composition over inheritance\n\nAvoid extending components across clients. Compose using shared child components instead. Inheritance creates tight coupling between client-specific UI and shared behavior — when one client's needs diverge, inherited components become hard to change safely.\n\n### Don't modernize existing code unless asked\n\nThe codebase contains both legacy and modern Angular patterns. When modifying an existing file, **follow the patterns already in that file**. Don't migrate any of these unless explicitly asked:\n\n- `*ngIf` → `@if`, `*ngFor` → `@for`\n- `@Input()` / `@Output()` → `input()` / `output()` signals\n- Constructor injection → `inject()`\n- Default change detection → `OnPush`\n- NgModule declarations → standalone components\n\nIf asked to modernize, follow this order (per the Angular migration guide): standalone → control flow → input/output signals → view queries → signals → computed → OnPush (last, only after full signal migration).\n\n### State management: Signals vs RxJS\n\n- **Component local state and Angular-only services:** Use Signals\n- **Cross-client services (`libs/common`):** Use RxJS (because CLI has no Angular Signals support)\n\nAvoid manual subscriptions. Prefer `| async` pipe. When subscriptions are necessary, pipe through `takeUntilDestroyed()` — enforced by the `prefer-takeUntil` lint rule.\n\n### No TypeScript enums (ADR-0025)\n\nUse frozen const objects with `Object.freeze()` and `as const`, plus a companion type alias. Enums have runtime behavior that creates subtle bugs with tree-shaking.\n\n## Critical Rules for New Code\n\nThese rules apply **strictly to new files and components**. For existing code, follow the patterns already in the file.\n\n- **New components must use `ChangeDetectionStrategy.OnPush`** and be `standalone: true`. `NgModules` are permitted only for grouping related standalone components\n- **Prefer `inject()` function** for DI in Angular primitives (components, pipes, directives). Use constructor injection for code shared with non-Angular clients (CLI)\n- **New templates must use control flow syntax** (`@if`, `@for`, `@switch`), not structural directives\n- **Use `host` property** in component decorators, not `@HostBinding` / `@HostListener`\n- **Use Reactive Forms exclusively** — not template-driven forms\n- **File naming:** `kebab-case.component.ts`, `.service.ts`, `.pipe.ts`, `.directive.ts`. Also: `.request.ts`, `.response.ts`, `.view.ts`, `.data.ts` for models (ADR-0012)\n- **All Tailwind classes require `tw-` prefix** — `tw-flex`, `tw-mt-2`, not `flex`, `mt-2`\n- **Testing with Jest** — use `jest-mock-extended` for mocking services. `describe`/`it` blocks, not `test()`\n- **Imports from `@bitwarden/common`** must not pull in Angular-specific code (breaks CLI)\n\n## Examples\n\n### Dependency injection (new Angular code)\n\n```typescript\n// CORRECT — inject() for Angular primitives\nexport class VaultComponent {\n  private vaultService = inject(VaultService);\n}\n\n// ALSO CORRECT — constructor injection for code shared with CLI\nexport class CryptoService {\n  constructor(private stateService: StateService) {}\n}\n```\n\n### Tailwind prefix\n\n```html\n<!-- CORRECT -->\n<div class=\"tw-flex tw-gap-2 tw-mt-4\">\n  <!-- WRONG — missing tw- prefix, will be stripped -->\n  <div class=\"flex gap-2 mt-4\"></div>\n</div>\n```\n\n### Const objects over enums (ADR-0025)\n\n```typescript\n// CORRECT — with companion type alias\nexport const CipherType = Object.freeze({\n  Login: 1,\n  SecureNote: 2,\n} as const);\nexport type CipherType = (typeof CipherType)[keyof typeof CipherType];\n\n// WRONG — TypeScript enums have runtime side effects\nexport enum CipherType {\n  Login = 1,\n  SecureNote = 2,\n}\n```\n\n## Further Reading\n\n- [Angular conventions](https://contributing.bitwarden.com/contributing/code-style/web/angular)\n- [Angular migration guide](https://contributing.bitwarden.com/contributing/code-style/web/angular-migration-guide)\n- [TypeScript style](https://contributing.bitwarden.com/contributing/code-style/typescript/)\n- [Component Library](https://contributing.bitwarden.com/contributing/ui/component-library/)\n\n<!-- File: libs/tools/export-vault-core/package.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Export-vault-core Skill\n\nPath: `libs/tools/export-vault-core/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/package.json)\n\n<!-- File: libs/tools/export-vault-core/project.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Export-vault-core Skill\n\nPath: `libs/tools/export-vault-core/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/project.json)\n\n<!-- File: libs/tools/export-vault-core/tsconfig.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Export-vault-core Skill\n\nPath: `libs/tools/export-vault-core/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/tsconfig.json)\n\n<!-- File: libs/tools/export-vault-core/tsconfig.spec.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Export-vault-core Skill\n\nPath: `libs/tools/export-vault-core/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-core/tsconfig.spec.json)\n\n<!-- File: libs/tools/export-vault-ui/package.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Export-vault-ui Skill\n\nPath: `libs/tools/export-vault-ui/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/package.json)\n\n<!-- File: libs/tools/export-vault-ui/project.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Export-vault-ui Skill\n\nPath: `libs/tools/export-vault-ui/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/project.json)\n\n<!-- File: libs/tools/export-vault-ui/tsconfig.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Export-vault-ui Skill\n\nPath: `libs/tools/export-vault-ui/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/tsconfig.json)\n\n<!-- File: libs/tools/export-vault-ui/tsconfig.spec.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Export-vault-ui Skill\n\nPath: `libs/tools/export-vault-ui/tsconfig.spec.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/export-vault-ui/tsconfig.spec.json)\n\n<!-- File: libs/tools/generator/components/package.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Components Skill\n\nPath: `libs/tools/generator/components/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/package.json)\n\n<!-- File: libs/tools/generator/components/project.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Components Skill\n\nPath: `libs/tools/generator/components/project.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/bitwarden/clients/HEAD/libs/tools/generator/components/project.json)\n\n<!-- ... +37 more modular skills available via /api/skills/bitwarden/clients?path={path} -->\n</yakaai_skills>"}