@react-vault/create-app 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/LICENSE +12 -0
  2. package/README.md +16 -0
  3. package/bin/create-app.js +8 -0
  4. package/claude-toolkit/README.md +131 -0
  5. package/claude-toolkit/agents/bfsi-accessibility-auditor.md +132 -0
  6. package/claude-toolkit/agents/bfsi-architect.md +156 -0
  7. package/claude-toolkit/agents/bfsi-code-reviewer.md +137 -0
  8. package/claude-toolkit/agents/bfsi-compliance-auditor.md +161 -0
  9. package/claude-toolkit/agents/bfsi-pii-scanner.md +142 -0
  10. package/claude-toolkit/agents/bfsi-pr-reviewer.md +114 -0
  11. package/claude-toolkit/agents/bfsi-security-reviewer.md +136 -0
  12. package/claude-toolkit/commands/bfsi-audit.md +46 -0
  13. package/claude-toolkit/commands/bfsi-doctor.md +97 -0
  14. package/claude-toolkit/commands/bfsi-review.md +46 -0
  15. package/claude-toolkit/commands/bfsi-scaffold.md +47 -0
  16. package/claude-toolkit/hooks/hooks.json +181 -0
  17. package/claude-toolkit/hooks/scripts/a11y-check.sh +63 -0
  18. package/claude-toolkit/hooks/scripts/audit-prompt.sh +36 -0
  19. package/claude-toolkit/hooks/scripts/block-destructive.sh +41 -0
  20. package/claude-toolkit/hooks/scripts/block-force-push.sh +30 -0
  21. package/claude-toolkit/hooks/scripts/format.sh +42 -0
  22. package/claude-toolkit/hooks/scripts/inject-context.sh +44 -0
  23. package/claude-toolkit/hooks/scripts/lint.sh +45 -0
  24. package/claude-toolkit/hooks/scripts/protect-files.sh +53 -0
  25. package/claude-toolkit/hooks/scripts/save-compliance-context.sh +35 -0
  26. package/claude-toolkit/hooks/scripts/scan-pii.sh +87 -0
  27. package/claude-toolkit/hooks/scripts/scan-secrets.sh +67 -0
  28. package/claude-toolkit/hooks/scripts/verify-clean.sh +50 -0
  29. package/claude-toolkit/package.json +22 -0
  30. package/claude-toolkit/plugin.json +31 -0
  31. package/claude-toolkit/skills/bfsi-api-endpoint/SKILL.md +105 -0
  32. package/claude-toolkit/skills/bfsi-commit/SKILL.md +102 -0
  33. package/claude-toolkit/skills/bfsi-compliance-check/SKILL.md +107 -0
  34. package/claude-toolkit/skills/bfsi-encrypt-helper/SKILL.md +127 -0
  35. package/claude-toolkit/skills/bfsi-error-message/SKILL.md +162 -0
  36. package/claude-toolkit/skills/bfsi-feature/SKILL.md +120 -0
  37. package/claude-toolkit/skills/bfsi-feature/references/architecture.md +69 -0
  38. package/claude-toolkit/skills/bfsi-feature/references/audit-events.md +70 -0
  39. package/claude-toolkit/skills/bfsi-feature/scripts/scaffold.mjs +136 -0
  40. package/claude-toolkit/skills/bfsi-form/SKILL.md +73 -0
  41. package/claude-toolkit/skills/bfsi-form/references/validation-regex.md +50 -0
  42. package/claude-toolkit/skills/bfsi-onboarding/SKILL.md +110 -0
  43. package/claude-toolkit/skills/bfsi-pii-field/SKILL.md +90 -0
  44. package/claude-toolkit/skills/bfsi-test-pattern/SKILL.md +179 -0
  45. package/dist/index.d.ts +2 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +339 -0
  48. package/dist/index.js.map +1 -0
  49. package/package.json +69 -0
  50. package/templates/_shared/.claude/settings.json +31 -0
  51. package/templates/_shared/.env.local.sample +25 -0
  52. package/templates/_shared/.github/workflows/ci.yml +49 -0
  53. package/templates/_shared/CLAUDE.md +89 -0
  54. package/templates/_shared/README.md +50 -0
  55. package/templates/_shared/index.html +16 -0
  56. package/templates/_shared/package.json +73 -0
  57. package/templates/_shared/postcss.config.cjs +6 -0
  58. package/templates/_shared/src/app/App.tsx +13 -0
  59. package/templates/_shared/src/app/globals.css +64 -0
  60. package/templates/_shared/src/env.ts +33 -0
  61. package/templates/_shared/src/i18n/i18n.ts +18 -0
  62. package/templates/_shared/src/i18n/translations/en.json +54 -0
  63. package/templates/_shared/src/i18n/translations/hi.json +30 -0
  64. package/templates/_shared/src/main.tsx +16 -0
  65. package/templates/_shared/src/routes/ProtectedRoute.tsx +28 -0
  66. package/templates/_shared/src/routes/index.tsx +67 -0
  67. package/templates/_shared/src/shared/ErrorBoundary.tsx +60 -0
  68. package/templates/_shared/tailwind.config.ts +68 -0
  69. package/templates/_shared/tests/setup.ts +7 -0
  70. package/templates/_shared/tsconfig.json +33 -0
  71. package/templates/_shared/tsconfig.node.json +13 -0
  72. package/templates/_shared/vite.config.ts +47 -0
  73. package/templates/rtk-query/.claude/skills/axios-auth/SKILL.md +103 -0
  74. package/templates/rtk-query/.claude/skills/axios-auth/references/error-shape.md +84 -0
  75. package/templates/rtk-query/.claude/skills/axios-auth/references/full-code-walkthrough.md +146 -0
  76. package/templates/rtk-query/.claude/skills/axios-auth/references/notification-wiring.md +141 -0
  77. package/templates/rtk-query/.claude/skills/constants-organization/SKILL.md +112 -0
  78. package/templates/rtk-query/.claude/skills/constants-organization/references/example-files.md +134 -0
  79. package/templates/rtk-query/.claude/skills/constants-organization/references/tag-types-catalog.md +53 -0
  80. package/templates/rtk-query/.claude/skills/redux-store-integration/SKILL.md +159 -0
  81. package/templates/rtk-query/.claude/skills/redux-store-integration/references/localStorage-persistence.md +70 -0
  82. package/templates/rtk-query/.claude/skills/redux-store-integration/references/middleware-patterns.md +82 -0
  83. package/templates/rtk-query/.claude/skills/rtk-query-api/SKILL.md +148 -0
  84. package/templates/rtk-query/.claude/skills/rtk-query-api/references/cache-strategies.md +96 -0
  85. package/templates/rtk-query/.claude/skills/rtk-query-api/references/endpoint-cookbook.md +145 -0
  86. package/templates/rtk-query/.claude/skills/rtk-query-api/references/optimistic-update.md +53 -0
  87. package/templates/rtk-query/README.md +84 -0
  88. package/templates/rtk-query/package.partial.json +7 -0
  89. package/templates/rtk-query/src/app/App.tsx +23 -0
  90. package/templates/rtk-query/src/axiosconfig/axiosInstance.ts +26 -0
  91. package/templates/rtk-query/src/axiosconfig/baseQuery.ts +72 -0
  92. package/templates/rtk-query/src/axiosconfig/interceptor.ts +42 -0
  93. package/templates/rtk-query/src/redux/invalidateCacheMiddleware.ts +20 -0
  94. package/templates/rtk-query/src/redux/reduxHooks.ts +10 -0
  95. package/templates/rtk-query/src/redux/rootReducer.ts +18 -0
  96. package/templates/rtk-query/src/redux/store.ts +36 -0
  97. package/templates/tanstack-query/.claude/skills/axios-auth/SKILL.md +109 -0
  98. package/templates/tanstack-query/.claude/skills/axios-auth/references/error-shape.md +89 -0
  99. package/templates/tanstack-query/.claude/skills/axios-auth/references/full-code-walkthrough.md +121 -0
  100. package/templates/tanstack-query/.claude/skills/axios-auth/references/notification-pattern.md +109 -0
  101. package/templates/tanstack-query/.claude/skills/constants-organization/SKILL.md +144 -0
  102. package/templates/tanstack-query/.claude/skills/constants-organization/references/example-files.md +111 -0
  103. package/templates/tanstack-query/.claude/skills/constants-organization/references/query-key-factories.md +129 -0
  104. package/templates/tanstack-query/.claude/skills/query-client-setup/SKILL.md +165 -0
  105. package/templates/tanstack-query/.claude/skills/query-client-setup/references/devtools.md +67 -0
  106. package/templates/tanstack-query/.claude/skills/query-client-setup/references/global-handlers.md +94 -0
  107. package/templates/tanstack-query/.claude/skills/tanstack-services/SKILL.md +142 -0
  108. package/templates/tanstack-query/.claude/skills/tanstack-services/references/audited-mutation.md +144 -0
  109. package/templates/tanstack-query/.claude/skills/tanstack-services/references/optimistic-update.md +102 -0
  110. package/templates/tanstack-query/.claude/skills/tanstack-services/references/service-cookbook.md +151 -0
  111. package/templates/tanstack-query/README.md +63 -0
  112. package/templates/tanstack-query/package.partial.json +8 -0
  113. package/templates/tanstack-query/src/api/axiosInstance.ts +20 -0
  114. package/templates/tanstack-query/src/api/http.ts +62 -0
  115. package/templates/tanstack-query/src/api/queryClient.ts +28 -0
  116. package/templates/tanstack-query/src/app/App.tsx +20 -0
  117. package/templates/tanstack-query/src/services/example.ts +32 -0
@@ -0,0 +1,107 @@
1
+ ---
2
+ name: bfsi-compliance-check
3
+ description: Runs an OWASP Top 10 + RBI + PCI-DSS + IRDAI + SOC2 compliance checklist against the current branch's diff. Reports findings grouped by severity (critical, high, medium, low) with file:line references and remediation steps. Use when the user types /bfsi-compliance-check, asks to "run compliance check", "audit my changes for compliance", or "check this PR for security issues".
4
+ disable-model-invocation: true
5
+ allowed-tools: Read Grep Glob Bash(git diff:*) Bash(git log:*)
6
+ ---
7
+
8
+ # BFSI Compliance Check
9
+
10
+ Static compliance scan over the current branch's diff (`git diff origin/main...HEAD`).
11
+
12
+ ## Checks performed
13
+
14
+ ### Critical (must fix before merge)
15
+
16
+ 1. **Hardcoded secrets** — API keys, tokens, passwords, connection strings in code/configs
17
+ - Pattern: `api_key|secret|password|token|private_key` followed by `= ?['"][^'"]+['"]`
18
+ - Action: extract to env var
19
+ 2. **Card data in non-PCI iframe** — `<input>` capturing card number/CVV outside `<PCITokenizedCardInput>`
20
+ - Pattern: `name="card_number"|name="cvv"|name="cvc"` not inside `PCITokenized*`
21
+ 3. **PII in console.log / console.error** — `console.log(user.pan)`, etc.
22
+ - Pattern: `console\.(log|info|warn|error).*\.(pan|aadhaar|account|password|cvv|otp)`
23
+ 4. **localStorage with PII** — `localStorage.setItem(..., user.pan)`, etc.
24
+ - Pattern: `localStorage\.setItem.*\.(pan|aadhaar|account|password)`
25
+ 5. **Unencrypted IndexedDB** — direct `idb.put()` of objects containing PII fields
26
+ - Suggest using `secureStorage` from `@react-vault/core/storage`
27
+ 6. **Missing CSRF token on mutation** — `fetch('/api/...', { method: 'POST', ... })` without `X-CSRF-Token` (if not using cookie-less JWT)
28
+
29
+ ### High (fix before next sprint)
30
+
31
+ 7. **Weak crypto** — `md5`, `sha1`, `Math.random()` for security purposes, `crypto.createCipher` (deprecated)
32
+ 8. **`dangerouslySetInnerHTML`** without sanitisation
33
+ 9. **Missing `<ProtectedRoute>`** on a route that fetches user data
34
+ 10. **Missing `permission` prop** on `<ProtectedRoute>` (defaults to authenticated-only)
35
+ 11. **API mutation without audit hook** — `useMutation` instead of `useAuditedMutation`
36
+ 12. **Missing Zod parse on API response** — `query` without `transformResponse: z.parse(...)`
37
+ 13. **`autocomplete="on"` on PII input field**
38
+ 14. **Form submit without idempotency-key header**
39
+
40
+ ### Medium (track for hardening)
41
+
42
+ 15. **Inline event handlers in JSX** for sensitive actions (harder to audit)
43
+ 16. **Untranslated user-facing strings** (no `t()` wrap)
44
+ 17. **Missing `aria-label`** on PII reveal buttons
45
+ 18. **Magic numbers** for amounts (use named constants)
46
+ 19. **TODO/FIXME** comments mentioning security
47
+ 20. **No tests** for files in `src/features/*/api.ts` or containers
48
+
49
+ ### Low (best-practice nudges)
50
+
51
+ 21. **`React.FC`** usage (prefer named function components for stack traces)
52
+ 22. **Default exports** (prefer named exports for refactor safety)
53
+ 23. **`as any`** type assertions
54
+ 24. **Files over 400 lines**
55
+
56
+ ## Workflow
57
+
58
+ ### Step 1: Get the diff
59
+
60
+ ```bash
61
+ git diff --name-only origin/main...HEAD
62
+ ```
63
+
64
+ If no diff (or origin/main missing), fall back to `git diff HEAD~5...HEAD`.
65
+
66
+ ### Step 2: For each category, run targeted greps
67
+
68
+ Use the patterns above. Be specific — minimise false positives. Each finding records: file, line, category, severity, the offending snippet, suggested fix.
69
+
70
+ ### Step 3: Group + report
71
+
72
+ Output as markdown:
73
+
74
+ ```
75
+ # Compliance Check Report
76
+
77
+ Branch: <name> → origin/main (N files changed)
78
+
79
+ ## Critical (must fix): N findings
80
+ 1. **Hardcoded secret** in src/api/auth.ts:42
81
+ ```
82
+
83
+ const API_KEY = 'sk-abc123...'
84
+
85
+ ```
86
+ Fix: replace with `import.meta.env.VITE_API_KEY` and add to `.env.local.sample`.
87
+
88
+ ## High: N findings
89
+ ...
90
+ ```
91
+
92
+ ### Step 4: Exit code
93
+
94
+ If any critical findings, end with: "❌ NOT MERGE-READY: N critical findings must be addressed."
95
+ If high but no critical: "⚠️ Mergeable but address N high findings before next sprint."
96
+ Otherwise: "✅ All checks passed."
97
+
98
+ ## Note on scope
99
+
100
+ This is a **static** check based on patterns. It catches obvious issues but is not a substitute for:
101
+
102
+ - `bfsi-security-reviewer` agent (which reasons about flows)
103
+ - Backend security review
104
+ - Penetration testing
105
+ - Third-party SAST/DAST tools
106
+
107
+ Always run before requesting human PR review.
@@ -0,0 +1,127 @@
1
+ ---
2
+ name: bfsi-encrypt-helper
3
+ description: Reference for encrypting and decrypting sensitive data using @react-vault/core/encryption. Covers AES-GCM (symmetric), RSA-OAEP (asymmetric), PBKDF2 (key derivation), and envelope encryption patterns. Auto-loads when the user asks about encrypting data, decrypting data, key derivation, key rotation, Web Crypto API usage, securing sensitive fields, or implementing envelope encryption.
4
+ ---
5
+
6
+ # BFSI Encryption Helper
7
+
8
+ Reference for `@react-vault/core/encryption`. Uses the browser's Web Crypto API — no external dependencies, no key material crosses package boundaries unprotected.
9
+
10
+ ## Algorithms
11
+
12
+ | Algorithm | Use | Module |
13
+ | --------------- | ----------------------------- | ------------------------------------ |
14
+ | AES-GCM 256 | Symmetric encryption of data | `aesgcm.encrypt`, `aesgcm.decrypt` |
15
+ | RSA-OAEP-SHA256 | Asymmetric encryption of keys | `rsaoaep.encrypt`, `rsaoaep.decrypt` |
16
+ | PBKDF2-SHA256 | Derive key from password | `pbkdf2.deriveKey` |
17
+ | HKDF-SHA256 | Derive key from existing key | `hkdf.deriveKey` |
18
+ | ECDSA-P256 | Sign / verify | `ecdsa.sign`, `ecdsa.verify` |
19
+
20
+ ## Common patterns
21
+
22
+ ### Encrypt a string with a known symmetric key
23
+
24
+ ```ts
25
+ import { aesgcm } from '@react-vault/core/encryption';
26
+
27
+ const key = await aesgcm.generateKey();
28
+ const ciphertext = await aesgcm.encrypt(key, 'sensitive value');
29
+ const plaintext = await aesgcm.decrypt(key, ciphertext);
30
+ ```
31
+
32
+ `encrypt` returns a base64 blob containing IV + ciphertext + auth tag. Don't try to compose this yourself — it's not interchangeable with raw Web Crypto output without the framing.
33
+
34
+ ### Derive a key from a password
35
+
36
+ ```ts
37
+ import { pbkdf2 } from '@react-vault/core/encryption';
38
+
39
+ const salt = crypto.getRandomValues(new Uint8Array(16));
40
+ const key = await pbkdf2.deriveKey(password, salt, { iterations: 600_000 });
41
+ // Use the key for AES-GCM. Store salt alongside ciphertext.
42
+ ```
43
+
44
+ 600k iterations is OWASP 2025 recommendation for PBKDF2-SHA256. Don't lower it.
45
+
46
+ ### Envelope encryption (BFSI standard)
47
+
48
+ This is the right pattern for storing many fields with one key, with key rotation:
49
+
50
+ ```ts
51
+ import { envelope } from '@react-vault/core/encryption';
52
+
53
+ // Setup: backend provides a public KEK (key-encrypting-key).
54
+ const masterPublicKey = await rsaoaep.importPublicKey(masterPublicKeyPem);
55
+
56
+ // Encrypt: generate a per-record DEK, encrypt data with DEK,
57
+ // then encrypt DEK with the master KEK.
58
+ const { ciphertext, encryptedDek } = await envelope.encrypt(
59
+ masterPublicKey,
60
+ JSON.stringify({ pan: '...', aadhaar: '...' }),
61
+ );
62
+
63
+ // Store: { ciphertext, encryptedDek } alongside the record.
64
+ // Decrypt: backend decrypts DEK with master private key, sends DEK to client,
65
+ // client decrypts ciphertext with DEK.
66
+ ```
67
+
68
+ Key rotation: rotate the KEK on the backend; re-encrypt only the small `encryptedDek` per record. The large `ciphertext` doesn't need to be touched.
69
+
70
+ ## Where NOT to use
71
+
72
+ - **Passwords**: never encrypt — hash with bcrypt/argon2 on the BACKEND. Client-side hashing helps you nothing.
73
+ - **Card numbers**: never encrypt yourself. Use `<PCITokenizedCardInput>`. Real card data should never reach your app.
74
+ - **Auth tokens**: don't encrypt — protect via secure storage (memory-first, see `@react-vault/core/storage`).
75
+ - **Session cookies**: server's job, not yours.
76
+
77
+ ## Anti-patterns
78
+
79
+ ```ts
80
+ // ❌ Don't roll your own crypto
81
+ const encrypted = btoa(JSON.stringify(data)); // NOT encryption
82
+
83
+ // ❌ Don't use Math.random() for keys / IVs
84
+ const iv = new Uint8Array(12).map(() => Math.random() * 256); // INSECURE
85
+
86
+ // ❌ Don't reuse IV across messages
87
+ const fixedIv = new Uint8Array(12); // CATASTROPHIC for GCM
88
+
89
+ // ❌ Don't store keys in localStorage
90
+ localStorage.setItem('encryption_key', exportedKey); // Defeats the point
91
+
92
+ // ❌ Don't use SHA-1 for anything security-related
93
+ const hash = crypto.subtle.digest('SHA-1', data); // Use SHA-256 minimum
94
+ ```
95
+
96
+ ## Verification
97
+
98
+ After implementing encryption, verify with:
99
+
100
+ ```ts
101
+ import { aesgcm } from '@react-vault/core/encryption';
102
+ import { describe, expect, it } from 'vitest';
103
+
104
+ describe('encryption round-trip', () => {
105
+ it('produces different ciphertext each time (IV randomness)', async () => {
106
+ const key = await aesgcm.generateKey();
107
+ const a = await aesgcm.encrypt(key, 'test');
108
+ const b = await aesgcm.encrypt(key, 'test');
109
+ expect(a).not.toBe(b);
110
+ });
111
+
112
+ it('decrypts to the original', async () => {
113
+ const key = await aesgcm.generateKey();
114
+ const ciphertext = await aesgcm.encrypt(key, 'test');
115
+ expect(await aesgcm.decrypt(key, ciphertext)).toBe('test');
116
+ });
117
+
118
+ it('throws on tampered ciphertext', async () => {
119
+ const key = await aesgcm.generateKey();
120
+ const ciphertext = await aesgcm.encrypt(key, 'test');
121
+ const tampered = ciphertext.slice(0, -2) + 'XX';
122
+ await expect(aesgcm.decrypt(key, tampered)).rejects.toThrow();
123
+ });
124
+ });
125
+ ```
126
+
127
+ These three tests catch 95% of misuses.
@@ -0,0 +1,162 @@
1
+ ---
2
+ name: bfsi-error-message
3
+ description: Reference for writing safe error messages — what users see, what gets logged, what gets sent to telemetry. Prevents stack traces, SQL, request IDs, or PII from leaking to UI or third-party services. Auto-loads when the user asks about error messages, error handling, exception messages, error boundaries, or Sentry/observability config.
4
+ ---
5
+
6
+ # Safe Error Messages
7
+
8
+ Reference for what's safe to show users, log to console, and send to telemetry.
9
+
10
+ ## The three-tier model
11
+
12
+ Every error has three audiences:
13
+
14
+ | Tier | Audience | What they should see |
15
+ | ------------- | ---------------- | ---------------------------------------------------------- |
16
+ | **UI** | End user | Friendly, actionable, generic. NO technical detail. |
17
+ | **Logs** | Developers | Full technical detail, structured. PII scrubbed. |
18
+ | **Telemetry** | Sentry / Datadog | Anonymised stack + breadcrumbs. NO PII, NO request bodies. |
19
+
20
+ A single error gives different content to each tier.
21
+
22
+ ## UI messages
23
+
24
+ ### What users SHOULD see
25
+
26
+ - ✅ "Something went wrong. Please try again."
27
+ - ✅ "We couldn't process your transaction. Please check your details and try again. (Ref: ERR-A7K2)"
28
+ - ✅ "This PAN is already registered with another account."
29
+ - ✅ "Your session has expired. Please log in again."
30
+
31
+ ### What users SHOULD NEVER see
32
+
33
+ - ❌ `TypeError: Cannot read properties of undefined (reading 'data')`
34
+ - ❌ `Failed to fetch: HTTP 500 from /api/v1/users/12345/kyc`
35
+ - ❌ `Connection refused: postgres://prod-db.internal:5432`
36
+ - ❌ `SQL syntax error near "DROP TABLE users"`
37
+ - ❌ Any raw error from the backend
38
+ - ❌ Stack traces
39
+ - ❌ Internal IDs (user IDs, session IDs — except a short ref code)
40
+
41
+ ### Reference codes
42
+
43
+ Generate a short alphanumeric code (e.g. `ERR-A7K2`) that maps to the full error in logs. The user reads this to support; support looks it up. The user sees ONLY the code, never the underlying ID or message.
44
+
45
+ ```ts
46
+ import { generateErrorRef, recordError } from '@react-vault/core/audit';
47
+
48
+ try {
49
+ // ...
50
+ } catch (err) {
51
+ const ref = generateErrorRef();
52
+ recordError(ref, err, { feature: 'kyc', action: 'submit' });
53
+ showToast({ title: t('errors.generic'), description: t('errors.ref', { ref }) });
54
+ }
55
+ ```
56
+
57
+ ## Log messages
58
+
59
+ Logs go to console (dev) and structured log shipper (prod). Full detail is fine, but:
60
+
61
+ - **PII scrubbed** — run through `auditClient.scrub()` before logging
62
+ - **Structured** — log as JSON (`pino`-style), not strings
63
+ - **Correlated** — include `request_id`, `user_id` (NOT email/name), `session_id`
64
+
65
+ ```ts
66
+ log.error({
67
+ request_id: req.id,
68
+ user_id: user.id,
69
+ feature: 'kyc',
70
+ action: 'submit',
71
+ error: err.message,
72
+ stack: err.stack,
73
+ // NEVER: pan: user.pan, aadhaar: user.aadhaar
74
+ });
75
+ ```
76
+
77
+ ## Telemetry (Sentry)
78
+
79
+ Sentry is third-party and may be subject to different data residency rules. Treat its data as escaping your perimeter.
80
+
81
+ ### Configure scrubbing
82
+
83
+ ```ts
84
+ Sentry.init({
85
+ dsn: import.meta.env.VITE_SENTRY_DSN,
86
+ beforeSend(event) {
87
+ // Scrub PII from breadcrumbs, request bodies, URL params
88
+ return scrubSentryEvent(event);
89
+ },
90
+ ignoreErrors: [
91
+ // Browser extension noise
92
+ 'top.GLOBALS',
93
+ 'ResizeObserver loop limit exceeded',
94
+ ],
95
+ });
96
+ ```
97
+
98
+ `scrubSentryEvent` (from `@react-vault/core/observability`) walks the event and:
99
+
100
+ - Removes `request.data` (request body)
101
+ - Removes URL query params matching PII patterns
102
+ - Replaces values in `extra` / `tags` / `user` that match PII patterns with `<scrubbed>`
103
+ - Trims breadcrumbs older than 30 seconds (to limit blast radius)
104
+
105
+ ### Sentry user context
106
+
107
+ Set the bare minimum:
108
+
109
+ ```ts
110
+ Sentry.setUser({ id: user.id }); // ID only — NEVER email, name, mobile
111
+ ```
112
+
113
+ ## Error boundary
114
+
115
+ Wrap every route in `<BFSIErrorBoundary>` (from `@react-vault/ui`). It:
116
+
117
+ 1. Catches the error
118
+ 2. Generates a ref code
119
+ 3. Records full detail to logs + telemetry (with scrubbing)
120
+ 4. Shows a generic friendly message with the ref code
121
+ 5. Provides a "go back" button
122
+
123
+ Never expose the actual error message via `componentDidCatch`'s `error.message` to JSX.
124
+
125
+ ## When to throw vs when to swallow
126
+
127
+ - **Throw** when something genuinely went wrong and the calling code can't continue safely.
128
+ - **Swallow + log** when the operation can degrade gracefully (e.g. analytics failed — show UI anyway).
129
+
130
+ Never swallow silently. Even degraded paths should leave a log entry.
131
+
132
+ ## Form validation errors (different rules)
133
+
134
+ Validation errors are NOT exceptions — they're expected user input. These can be specific:
135
+
136
+ - ✅ "PAN must be 10 characters: 5 letters, 4 digits, 1 letter"
137
+ - ✅ "Amount must be at least ₹100"
138
+ - ✅ "Mobile number must start with 6, 7, 8, or 9"
139
+
140
+ These come from Zod's `.message()` and are user-facing on purpose. Just keep them generic — don't include the _value_ the user typed back into the error.
141
+
142
+ ## Edge cases
143
+
144
+ ### Network errors
145
+
146
+ Don't show "Failed to fetch" or similar. Map to: "We couldn't reach our servers. Check your internet connection and try again."
147
+
148
+ ### 401 (auth)
149
+
150
+ Don't show "Unauthorized" or "Token expired". Map to: "Your session has expired. Please log in again." Then redirect to login.
151
+
152
+ ### 403 (permission)
153
+
154
+ Don't show "Forbidden" or "Insufficient permissions". Map to: "You don't have access to this. Contact your administrator if you think this is wrong."
155
+
156
+ ### 5xx (server)
157
+
158
+ Don't show "Internal server error" or HTTP details. Map to: "We're having trouble right now. Please try again in a moment. (Ref: ERR-XXXX)"
159
+
160
+ ### 429 (rate limit)
161
+
162
+ Map to: "Too many attempts. Please wait a moment and try again."
@@ -0,0 +1,120 @@
1
+ ---
2
+ name: bfsi-feature
3
+ description: Scaffolds a new BFSI feature module with the full directory structure (api, containers, components, routes, tests, i18n keys). Variant-aware — generates RTK Query OR TanStack Query code based on the project's stack. Use when the user types /bfsi-feature, asks to "scaffold a feature", "create a new feature module", "add a CRUD page", or "start a new BFSI module".
4
+ disable-model-invocation: true
5
+ argument-hint: <feature-name> [--variant rtk|tanstack] [--no-i18n]
6
+ allowed-tools: Read Write Edit Glob Grep Bash(mkdir:*) Bash(node:*)
7
+ ---
8
+
9
+ # BFSI Feature Scaffold
10
+
11
+ Generates a complete feature module under `src/features/<FeatureName>/` following the Your Real Company BFSI architecture: container-component split, RTK Query / TanStack Query API layer, Zod validation, audit-wrapped mutations, accessible UI, i18n keys.
12
+
13
+ ## Arguments
14
+
15
+ - `$0` — feature name in PascalCase (e.g. `KycVerification`, `LoanApplication`, `Transactions`). **Required.**
16
+ - `--variant rtk|tanstack` — overrides project default (auto-detected from `package.json`).
17
+ - `--no-i18n` — skip i18n key generation.
18
+
19
+ ## What gets generated
20
+
21
+ ```
22
+ src/features/<FeatureName>/
23
+ ├── api.ts # RTK Query slice OR TanStack hook factories
24
+ ├── schema.ts # Zod schemas (request, response, form)
25
+ ├── types.ts # Inferred TS types from Zod
26
+ ├── constants.ts # API URLs, cache tags, audit event names
27
+ ├── routes.tsx # Feature routes with <ProtectedRoute>
28
+ ├── containers/
29
+ │ ├── <FeatureName>List.tsx # Container: data + handlers
30
+ │ └── <FeatureName>Form.tsx # Container: form state
31
+ ├── components/
32
+ │ ├── <FeatureName>Table.tsx # Presentational: receives props
33
+ │ ├── <FeatureName>FormFields.tsx # Presentational: form fields
34
+ │ └── <FeatureName>Actions.tsx # Audit-wrapped action buttons
35
+ ├── hooks/
36
+ │ └── use<FeatureName>.ts # Custom hooks
37
+ ├── utils/
38
+ │ └── mappers.ts # snake_case ↔ camelCase, value mappers
39
+ ├── __tests__/
40
+ │ ├── containers.test.tsx
41
+ │ ├── schema.test.ts
42
+ │ └── e2e.spec.ts # Playwright
43
+ └── index.ts # Barrel export
44
+ ```
45
+
46
+ Plus updates to:
47
+
48
+ - `src/routes/index.tsx` — registers the new feature routes
49
+ - `src/i18n/translations/en.json` — adds `<feature>.*` namespace
50
+ - `src/i18n/translations/hi.json` — placeholder keys (translator fills in)
51
+
52
+ ## Workflow
53
+
54
+ ### Step 1: Validate inputs
55
+
56
+ Confirm:
57
+
58
+ - `$0` is PascalCase (regex `^[A-Z][A-Za-z0-9]+$`).
59
+ - The feature directory does NOT already exist.
60
+ - A `package.json` exists at the project root.
61
+ - Detect variant: look for `@reduxjs/toolkit` vs `@tanstack/react-query` in dependencies.
62
+
63
+ If validation fails, exit and tell the user what to fix.
64
+
65
+ ### Step 2: Run the scaffold script
66
+
67
+ ```bash
68
+ node ${CLAUDE_PLUGIN_ROOT}/skills/bfsi-feature/scripts/scaffold.mjs $0 --variant=<detected>
69
+ ```
70
+
71
+ The script writes all files using the templates in `references/templates/`.
72
+
73
+ ### Step 3: Verify
74
+
75
+ After generation:
76
+
77
+ 1. Run `pnpm typecheck` and report any errors.
78
+ 2. Run `pnpm lint` on the new files only.
79
+ 3. Read the generated `routes.tsx` and confirm it's registered.
80
+
81
+ ### Step 4: Summarise
82
+
83
+ Output a short summary to the user:
84
+
85
+ - N files created
86
+ - Routes registered: `/<feature>` and `/<feature>/:id`
87
+ - Next step suggestion: "Run `pnpm dev` and visit /<feature> to see the empty list. Then add fields to `schema.ts`."
88
+
89
+ ## Conventions enforced
90
+
91
+ - **No `any` types.** All types flow from Zod schemas via `z.infer<>`.
92
+ - **No hardcoded strings.** All user-facing strings go through `t()` (or `<Trans>`).
93
+ - **Sensitive fields get `<PIIMaskedDisplay>` wrappers** by default if their names match `/^(pan|aadhaar|account|mobile|email|dob)$/i`.
94
+ - **All mutations go through `useAuditedMutation`** which logs the action.
95
+ - **All routes are `<ProtectedRoute>`** with an explicit `permission` prop.
96
+ - **All forms use `useFormWithZod`** (from `@react-vault/ui`) for consistent validation display.
97
+
98
+ ## Examples
99
+
100
+ ### Create a KYC feature
101
+
102
+ ```
103
+ /bfsi-feature KycVerification
104
+ ```
105
+
106
+ Result: `src/features/KycVerification/` populated. PAN, Aadhaar, address fields auto-wrapped with PIIMaskedDisplay. Routes `/kyc-verification` and `/kyc-verification/:id` registered with `permission="kyc.view"`.
107
+
108
+ ### Override variant
109
+
110
+ ```
111
+ /bfsi-feature LoanApplication --variant tanstack
112
+ ```
113
+
114
+ Force TanStack Query API layer even if project default is RTK.
115
+
116
+ ## References
117
+
118
+ - Full file templates: [`references/templates/`](references/templates/)
119
+ - BFSI architecture rationale: [`references/architecture.md`](references/architecture.md)
120
+ - Audit event naming convention: [`references/audit-events.md`](references/audit-events.md)
@@ -0,0 +1,69 @@
1
+ # BFSI Feature — Architecture rationale
2
+
3
+ ## Why container-component split?
4
+
5
+ **Containers** own all side-effects: API calls, audit logging, mutations, navigation, form state. They have no JSX of their own — just compose a presentational component and pass it props.
6
+
7
+ **Components** receive props, render UI, emit events. No `useFetch`, no `useDispatch`, no `useNavigate`. They are pure and easy to test with React Testing Library.
8
+
9
+ For BFSI, this split matters because:
10
+
11
+ - **Audit clarity** — security review only needs to look at containers to see what data flows in/out
12
+ - **Easy mocking** — components test in isolation; you don't fight network in unit tests
13
+ - **Reusability** — same component renders in two contexts with different containers (e.g. customer-view vs admin-view)
14
+
15
+ ## Why Zod everywhere?
16
+
17
+ We use Zod for THREE distinct purposes:
18
+
19
+ 1. **API response validation** — every API response is parsed through a Zod schema before reaching components. Rejects malformed responses (XSS defence).
20
+ 2. **Form validation** — same schema or a derived schema validates form inputs.
21
+ 3. **Type generation** — `z.infer<typeof schema>` gives us TS types from a single source of truth.
22
+
23
+ This means: edit one Zod schema, and types + runtime checks update together. No drift.
24
+
25
+ ## Why audit-wrapped mutations?
26
+
27
+ Every state-changing API call must be audited for BFSI compliance (RBI Annexure I, SOC2 CC7.3). The `useAuditedMutation` hook from `@react-vault/core/audit` wraps RTK Query / TanStack Query mutations and:
28
+
29
+ 1. Generates an event ID (UUID v4)
30
+ 2. Records: actor, action, target, timestamp, request hash
31
+ 3. POSTs to `/api/audit` with PII scrubbed
32
+ 4. Records success/failure result
33
+ 5. Returns the same shape as the underlying mutation
34
+
35
+ The container layer always uses `useAuditedMutation` instead of raw `useMutation`.
36
+
37
+ ## Why permission-gated routes?
38
+
39
+ `<ProtectedRoute permission="kyc.view">` does TWO checks:
40
+
41
+ 1. **Authentication** — is there a valid session?
42
+ 2. **Authorization** — does the current user's permission set include `"kyc.view"`?
43
+
44
+ Failing #1 → redirect to login. Failing #2 → render 403 with audit log entry.
45
+
46
+ The permission string is checked against the current user's permission set fetched at login. We do NOT trust client-side permissions for security; the backend re-checks on every API call. The client check is a UX optimisation (don't show what they can't access) and an audit-log trigger.
47
+
48
+ ## Sensitive field auto-wrap
49
+
50
+ The scaffolder auto-wraps fields whose names match `/^(pan|aadhaar|account|mobile|email|dob)$/i` with `<PIIMaskedDisplay>` in the table component. This means:
51
+
52
+ - The value is masked by default (`****1234` for account numbers, `ABCDE****F` for PAN, etc.)
53
+ - A reveal toggle fires an audit log event with reason
54
+ - Reveals expire after 30 seconds and re-mask
55
+
56
+ This is a default; you can opt out by editing the generated component. But by default, PII never appears unmasked in lists or tables.
57
+
58
+ ## i18n namespace per feature
59
+
60
+ Each feature gets its own i18n namespace `<feature>.*`. This keeps translation files navigable (one team can own KYC translations, another Loans) and lets us lazy-load translation chunks per route.
61
+
62
+ Naming:
63
+
64
+ - `<feature>.title`
65
+ - `<feature>.list.empty`
66
+ - `<feature>.form.fields.<fieldName>.label`
67
+ - `<feature>.form.fields.<fieldName>.placeholder`
68
+ - `<feature>.errors.<errorCode>`
69
+ - `<feature>.actions.<actionName>`
@@ -0,0 +1,70 @@
1
+ # Audit Event Naming Convention
2
+
3
+ Every state-changing action emits an audit event. Names follow:
4
+
5
+ ```
6
+ <feature>.<entity>.<action>
7
+ ```
8
+
9
+ Examples:
10
+
11
+ - `kyc.verification.submitted`
12
+ - `kyc.verification.approved`
13
+ - `kyc.verification.rejected`
14
+ - `loan.application.created`
15
+ - `loan.application.documents_uploaded`
16
+ - `transaction.transfer.initiated`
17
+ - `transaction.transfer.confirmed`
18
+ - `user.profile.password_changed`
19
+ - `user.session.logged_in`
20
+ - `user.session.logged_out_idle`
21
+ - `data.account_number.revealed`
22
+ - `data.pan.revealed`
23
+
24
+ ## Required event metadata
25
+
26
+ Every event must include:
27
+
28
+ | Field | Source | Notes |
29
+ | ------------------ | ----------------------------------- | ------------------------------------------------- |
30
+ | `event_id` | UUID v4 | Generated client-side |
31
+ | `event_name` | string | e.g. `kyc.verification.submitted` |
32
+ | `actor_id` | from session | User performing the action |
33
+ | `actor_session_id` | from session | Session correlation |
34
+ | `target_type` | string | e.g. `kyc_verification` |
35
+ | `target_id` | string | Resource ID |
36
+ | `timestamp` | ISO 8601 | Client clock — backend re-stamps for legal record |
37
+ | `outcome` | `success` \| `failure` \| `pending` | Always populated |
38
+ | `request_hash` | sha256 | Of request body, for tamper detection |
39
+ | `client_metadata` | object | User agent, viewport — no PII |
40
+
41
+ ## Reveal events
42
+
43
+ When a user reveals a masked PII field, fire:
44
+
45
+ ```
46
+ data.<field>.revealed
47
+ ```
48
+
49
+ With metadata `{ field, reason?, duration_ms }`. If `reason` is required by policy (e.g. for Aadhaar in some flows), the UI must prompt and include it.
50
+
51
+ ## Failure events
52
+
53
+ For failed sensitive operations:
54
+
55
+ ```
56
+ <feature>.<entity>.<action>_failed
57
+ ```
58
+
59
+ With metadata including the error code (but NEVER the raw error message — that may contain PII).
60
+
61
+ ## NEVER include in audit events
62
+
63
+ - Card numbers (raw or last-4)
64
+ - Passwords
65
+ - OTP codes
66
+ - Full PAN / Aadhaar (only masked, e.g. `****1234`)
67
+ - Full account numbers
68
+ - Free-text user input from forms (it might contain PII)
69
+
70
+ The `auditClient` in `@react-vault/core/audit` runs every payload through a PII scrubber before POST.