bitwarden/cipher-type-planner
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.
npx skills add https://github.com/bitwarden/clients --skill cipher-type-planner
Ask the user the following questions (use AskUserQuestion). Adapt questions based on what
the user has already provided.
Required questions:
CipherType enum definition.$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.and Identity support autofill.) Only ask if $0 is browser or all.
should be linkable?
with the plan until the user confirms these are done.**
Additional questions:
Ask 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.
subTitle getter on the view model return? This appears in vault list items.bwi- icon classes.After gathering requirements, enter plan mode using EnterPlanMode. Explore the codebase to
verify current patterns and file locations. Use the SshKey cipher type (value 5) as the canonical
reference for implementation patterns.
Key files to inspect for patterns:
libs/common/src/vault/enums/cipher-type.ts - Enum definitionlibs/common/src/vault/models/api/ssh-key.api.ts - API model patternlibs/common/src/vault/models/data/ssh-key.data.ts - Data model patternlibs/common/src/vault/models/domain/ssh-key.ts - Domain model patternlibs/common/src/vault/models/view/ssh-key.view.ts - View model patternlibs/common/src/models/export/ssh-key.export.ts - Export model patternlibs/common/src/vault/models/domain/cipher.ts - Container switch patternslibs/vault/src/cipher-form/components/sshkey-section/ - Form component patternlibs/vault/src/cipher-view/sshkey-sections/ - View component patternWrite a comprehensive plan to the plan file. The plan MUST include all sections below.
List every file that needs to be created, with the full path and a brief description. Organize by
layer:
Model stack:
libs/common/src/vault/models/api/<type>.api.ts - API response shapelibs/common/src/vault/models/data/<type>.data.ts - Serializable storage formatlibs/common/src/vault/models/domain/<type>.ts - Encrypted business objectlibs/common/src/vault/models/domain/<type>.spec.ts - Domain model testslibs/common/src/vault/models/view/<type>.view.ts - Decrypted view for UIExport (if import/export is included):
libs/common/src/models/export/<type>.export.ts - Export modelUI components:
libs/vault/src/cipher-form/components/<type>-section/ - Form section component (TS, HTML, spec)libs/vault/src/cipher-view/<type>-sections/ - View section component (TS, HTML)List every file that needs modification, organized by concern. For each file, describe the specific
change needed.
Core enum:
libs/common/src/vault/enums/cipher-type.ts - Add <Type>: <N> to CipherTypelibs/common/src/vault/enums/cipher-type.spec.ts - Update testsContainer switches (add case for new type):
libs/common/src/vault/models/data/cipher.data.ts - Constructorlibs/common/src/vault/models/domain/cipher.ts - Constructor, decrypt(),toCipherData(), fromJSON(), toSdkCipher(), fromSdkCipher()
libs/common/src/vault/models/view/cipher.view.ts - item getter, fromJSON(),fromSdkCipherView(), getSdkCipherViewType(), toSdkCipherView()
libs/common/src/vault/models/request/cipher.request.ts - Constructorlibs/common/src/vault/models/response/cipher.response.ts - Constructorlibs/common/src/vault/services/cipher.service.ts - encryptCipherData()Export (if import/export is included):
libs/common/src/models/export/cipher.export.ts - toView(), toDomain(), build()SDK integration:
libs/common/src/vault/models/domain/cipher-sdk-mapper.ts - Record mappertoSdk*/fromSdk*)UI wiring:
libs/vault/src/cipher-form/components/cipher-form.component.ts - Import and wire sectionlibs/vault/src/cipher-form/components/cipher-form.component.html - Add section templatelibs/vault/src/cipher-view/cipher-view.component.ts - Import and wire sectionlibs/vault/src/cipher-view/cipher-view.component.html - Add section templatelibs/common/src/vault/icon/build-cipher-icon.ts - Add icon caseVault filters (CRITICAL — without these, ciphers won't appear in the vault list):
All vault filter files must be feature-flag-gated so the new type only appears when the flag is
enabled. Use ConfigService.getFeatureFlag$() with combineLatest to filter the type out of
arrays when the flag is off.
_Always included (shared):_
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.libs/vault/src/models/filter-function.ts - Add filter case for the new typelibs/angular/src/vault/components/vault-items.component.ts - Feature-flag-gate empty state type buttons_Include if $0 is web or all:_
apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts - Add to allTypeFilters, searchPlaceholder, and feature-flag-gate in buildAllFilters()apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts - Feature-flag-gate in buildAllFilters()_Include if $0 is desktop or all:_
apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts - Add ConfigService, combineLatest with feature flag_Include if $0 is browser or all:_
apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts - Add ConfigService, feature-flag-gate cipherTypesNew item menus (feature-flag-gated):
_Always included (shared):_
libs/common/src/vault/types/cipher-menu-items.ts - Add menu item entry for new typelibs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts - Add canCreate<Type> = input(false) signal, gate in cipherMenuItems observable_Include if $0 is web or all:_
apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts - Add canCreate<Type>$ observable from feature flagapps/web/src/app/vault/individual-vault/vault-header/vault-header.component.html - Bind [canCreate<Type>] to <vault-new-cipher-menu>_Include if $0 is browser or all:_
apps/browser/src/vault/popup/components/vault/new-item-dropdown/new-item-dropdown.component.ts - Add ConfigService, combineLatest with feature flagLocalization (add i18n keys):
_Include only locale files for $0. If $0 is all, include all three:_
apps/web/src/locales/en/messages.json _(web)_apps/desktop/src/locales/en/messages.json _(desktop)_apps/browser/src/_locales/en/messages.json _(browser)_Linked fields (if applicable):
libs/common/src/vault/enums/linked-id-type.enum.tsAutofill (if applicable — only if $0 is browser or all):
apps/browser/src/autofill/ only if the type supportsautofill
Restricted item types (if applicable):
Restricted item type enforcement is used across all clients. Include files for $0:
_Always included (shared):_
libs/common/src/vault/services/vault-settings/vault-settings.service.ts - Restricted types service_Include if $0 is web or all:_
apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.ts - Policy configuration UIapps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.html_Include if $0 is browser or all:_
apps/browser/src/vault/popup/components/vault/item-more-options/item-more-options.component.ts - Restricted type checks_Include if $0 is cli or all:_
apps/cli/src/vault/create.command.ts - Restricted type checksapps/cli/src/commands/list.command.ts - Restricted type checksapps/cli/src/commands/get.command.ts - Restricted type checksList all i18n keys that need to be added. At minimum:
List all test files that need to be created or updated:
libs/common/src/vault/enums/cipher-type.spec.tslibs/common/src/vault/models/domain/cipher.spec.tslibs/common/src/vault/models/domain/<type>.spec.ts (new)libs/common/src/vault/models/view/cipher.view.spec.tslibs/common/src/vault/services/cipher.service.spec.tslibs/common/src/vault/services/cipher-sdk.service.spec.tslibs/common/src/vault/icon/build-cipher-icon.spec.tslibs/common/src/models/export/cipher.export.spec.tslibs/vault/src/cipher-form/components/cipher-form.component.spec.tslibs/vault/src/cipher-view/cipher-view.component.spec.tsRecommended implementation order, customized for this specific type. Only include steps relevant
to $0 (shared steps are always included):
toSdk*/fromSdk*) _(shared)_$0)_$0)_$0)_10. Per-app UI (form section, view section) _(shared)_
11. Context menu / copy actions — see Section 8 _(shared + $0)_
12. CLI _(only if $0 is cli or all)_
13. Autofill _(only if $0 is browser or all)_
14. Tests _(shared + $0)_
desired display value. If an existing key has the same message text, reuse it instead of creating
a duplicate. Only create new keys when no existing key matches.
Each cipher type can expose copiable fields in the vault list item context menus (right-click / more
menu). Include only the sections relevant to $0.
| File | What to add |
| ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 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). |
| 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. |
$0 is browser or all)| File | What to add |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 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. |
| 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. |
$0 is web or all)| File | What to add |
| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| 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. |
| apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html | Add copy buttons using @if syntax with appCopyField directive. |
$0 is desktop or all)| File | What to add |
| ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 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. |
with product requirements for which fields get copy buttons.
Take bitwarden/cipher-type-planner from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.