This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Package manager is pnpm. The lockfile is committed; CI uses --frozen-lockfile.
pnpm install # also runs `wxt prepare` via postinstall (regenerates .wxt/)
pnpm dev # Chrome dev with HMR
pnpm dev:firefox # Firefox dev
pnpm build # production build to .output/chrome-mv3/
pnpm build:firefox # .output/firefox-mv2/
pnpm zip # build + zip for Chrome Web Store -> .output/*-chrome.zip
pnpm check # svelte-check (uses generated .wxt/tsconfig.json)
pnpm format # prettier write
pnpm format:check # CI gate
pnpm test # vitest run (unit)
pnpm test:watch # vitest watch
pnpm test:e2e # Playwright; REQUIRES `pnpm build` first (loads unpacked from .output/chrome-mv3)Run a single Vitest file or test:
pnpm test src/utils/__tests__/password.test.ts
pnpm test -t "rejection sampling" # filter by test nameRun a single E2E test (must have run pnpm build since the last code change):
pnpm exec playwright test e2e/popup.spec.ts -g "regenerate".wxt/ is generated by wxt prepare. If TypeScript suddenly complains about missing defineBackground or extension globals, re-run pnpm exec wxt prepare.
WXT-based Chrome MV3 extension. Svelte 5 (runes mode), Tailwind 4, TypeScript. Path alias @/* -> src/* is set in vitest.config.ts and inherited by WXT's generated tsconfig.
src/entrypoints/background.ts— service worker. SeedsDEFAULT_SETTINGSintochrome.storage.syncon install and openswelcome.htmlon first install only.src/entrypoints/popup/— the popup UI.App.svelteis the only component; it owns all state via Svelte 5 runes ($state,$derived).
The popup deliberately keeps two separate password values in state (randomPassword, passphrasePassword) so switching between Random and Passphrase tabs does not regenerate or lose either password. The displayed password is $derived from the active mode. Don't collapse these into a single string.
password.ts— all cryptographic generation, scoring, color interpolation. Pure functions, no DOM or chrome.* dependencies. This is the security-critical module. All tests live in__tests__/password.test.ts.wordlist.ts— frozen 2,048-word list (BIP39-style). The exact size is load-bearing forcalculatePassphraseEntropy(entropy = wordCount * log2(2048) = 11 bits/word).defaults.ts—Settingsinterface,DEFAULT_SETTINGS, andSTORAGE_KEYconstant. The whole settings object is stored under one sync-storage key; do not split across keys.
Settings are read from and written back to chrome.storage.sync under STORAGE_KEY. The popup uses a skipNextSave guard pattern so the initial hydration from storage doesn't immediately re-trigger a save and clobber what was just loaded. Preserve this when adding new settings: extend Settings in defaults.ts, then plumb a single $state plus the corresponding load/save in App.svelte.
e2e/popup.spec.ts launches a persistent Chromium context with --load-extension=.output/chrome-mv3, then navigates to chrome-extension://<id>/popup.html. Selectors are tied to live class names (.pw-field-inner, .pw-switch.on, .pw-strength-label, .pw-num-btn, etc.) and aria-labels. Renaming or restyling those classes will break E2E without breaking unit tests; check e2e/popup.spec.ts when changing UI primitives. Playwright is configured headed (headless: false) — CI runs it under xvfb-run.
These are not preferences. They are commitments documented in SECURITY.md and the Chrome Web Store listing. Do not regress them.
- All randomness goes through
crypto.getRandomValues.Math.random()must not appear anywhere insrc/. ThegetRandomIndexrejection-sampling loop exists specifically to eliminate modulo bias; do not "simplify" it to% max. - No password persistence. Generated passwords live only in component state. Never write them to
chrome.storage,localStorage, IndexedDB, the network, or the DOM outside the popup. Logging counts as persistence — don't log passwords even in dev. - Clipboard auto-clears 30s after copy. If you change the clipboard flow, preserve
clipboardClearTimer. - Manifest permissions are
storageonly. Addingtabs,activeTab,host_permissions, or content scripts requires a written justification and a Chrome Web Store re-review. Don't add them casually. - All assets (fonts, icons) are bundled in
public/. Zero runtime network requests. Don't pull anything from a CDN.
When touching password.ts, run pnpm test before considering the change done. When touching anything that affects the popup UI, run pnpm build && pnpm test:e2e.
.github/workflows/ci.ymlruns on every push/PR tomaster: format check, vitest, build,svelte-check, Playwright E2E. All five must pass..github/workflows/release.ymlisworkflow_dispatchonly. It bumps the version (patch/minor/major), re-runs the gates, builds + zips, commits + tagsvX.Y.Z, then publishes to the Chrome Web Store via a service-account JWT. The extension IDlkahbfdjppdglmacdhifnfaokjealmbais hardcoded in the workflow. Don't bump the version manually inpackage.json; let the release workflow do it.
- Prettier: single quotes, trailing commas, 120-col, semis, 2-space.
prettier-plugin-sveltehandles.svelte. CI fails on formatting drift, so runpnpm formatbefore committing. - Svelte 5 runes only (
$state,$derived,$effect). Do not introduce stores or Svelte 4 reactive$:syntax.
This project is indexed by GitNexus as PasswordGenerator (82 symbols, 95 relationships, 1 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
If any GitNexus tool warns the index is stale, run
npx gitnexus analyzein terminal first.
- MUST run impact analysis before editing any symbol. Before modifying a function, class, or method, run
gitnexus_impact({target: "symbolName", direction: "upstream"})and report the blast radius (direct callers, affected processes, risk level) to the user. - MUST run
gitnexus_detect_changes()before committing to verify your changes only affect expected symbols and execution flows. - MUST warn the user if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
- When exploring unfamiliar code, use
gitnexus_query({query: "concept"})to find execution flows instead of grepping. It returns process-grouped results ranked by relevance. - When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use
gitnexus_context({name: "symbolName"}).
gitnexus_query({query: "<error or symptom>"})— find execution flows related to the issuegitnexus_context({name: "<suspect function>"})— see all callers, callees, and process participationREAD gitnexus://repo/PasswordGenerator/process/{processName}— trace the full execution flow step by step- For regressions:
gitnexus_detect_changes({scope: "compare", base_ref: "main"})— see what your branch changed
- Renaming: MUST use
gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})first. Review the preview — graph edits are safe, text_search edits need manual review. Then run withdry_run: false. - Extracting/Splitting: MUST run
gitnexus_context({name: "target"})to see all incoming/outgoing refs, thengitnexus_impact({target: "target", direction: "upstream"})to find all external callers before moving code. - After any refactor: run
gitnexus_detect_changes({scope: "all"})to verify only expected files changed.
- NEVER edit a function, class, or method without first running
gitnexus_impacton it. - NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
- NEVER rename symbols with find-and-replace — use
gitnexus_renamewhich understands the call graph. - NEVER commit changes without running
gitnexus_detect_changes()to check affected scope.
| Tool | When to use | Command |
|---|---|---|
query |
Find code by concept | gitnexus_query({query: "auth validation"}) |
context |
360-degree view of one symbol | gitnexus_context({name: "validateUser"}) |
impact |
Blast radius before editing | gitnexus_impact({target: "X", direction: "upstream"}) |
detect_changes |
Pre-commit scope check | gitnexus_detect_changes({scope: "staged"}) |
rename |
Safe multi-file rename | gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true}) |
cypher |
Custom graph queries | gitnexus_cypher({query: "MATCH ..."}) |
| Depth | Meaning | Action |
|---|---|---|
| d=1 | WILL BREAK — direct callers/importers | MUST update these |
| d=2 | LIKELY AFFECTED — indirect deps | Should test |
| d=3 | MAY NEED TESTING — transitive | Test if critical path |
| Resource | Use for |
|---|---|
gitnexus://repo/PasswordGenerator/context |
Codebase overview, check index freshness |
gitnexus://repo/PasswordGenerator/clusters |
All functional areas |
gitnexus://repo/PasswordGenerator/processes |
All execution flows |
gitnexus://repo/PasswordGenerator/process/{name} |
Step-by-step execution trace |
Before completing any code modification task, verify:
gitnexus_impactwas run for all modified symbols- No HIGH/CRITICAL risk warnings were ignored
gitnexus_detect_changes()confirms changes match expected scope- All d=1 (WILL BREAK) dependents were updated
After committing code changes, the GitNexus index becomes stale. Re-run analyze to update it:
npx gitnexus analyzeIf the index previously included embeddings, preserve them by adding --embeddings:
npx gitnexus analyze --embeddingsTo check whether embeddings exist, inspect .gitnexus/meta.json — the stats.embeddings field shows the count (0 means no embeddings). Running analyze without --embeddings will delete any previously generated embeddings.
Claude Code users: A PostToolUse hook handles this automatically after
git commitandgit merge.
| Task | Read this skill file |
|---|---|
| Understand architecture / "How does X work?" | .claude/skills/gitnexus/gitnexus-exploring/SKILL.md |
| Blast radius / "What breaks if I change X?" | .claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md |
| Trace bugs / "Why is X failing?" | .claude/skills/gitnexus/gitnexus-debugging/SKILL.md |
| Rename / extract / split / refactor | .claude/skills/gitnexus/gitnexus-refactoring/SKILL.md |
| Tools, resources, schema reference | .claude/skills/gitnexus/gitnexus-guide/SKILL.md |
| Index, status, clean, wiki CLI commands | .claude/skills/gitnexus/gitnexus-cli/SKILL.md |