@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,136 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * BFSI feature scaffolder.
4
+ *
5
+ * Reads templates from references/templates/ and writes a complete feature module.
6
+ *
7
+ * Usage: node scaffold.mjs <FeatureName> --variant=rtk|tanstack [--no-i18n]
8
+ */
9
+ import { argv, exit, cwd } from 'node:process';
10
+ import { mkdir, readFile, writeFile, access } from 'node:fs/promises';
11
+ import { join, dirname } from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = dirname(__filename);
16
+ const TEMPLATES_DIR = join(__dirname, '..', 'references', 'templates');
17
+
18
+ const log = (msg) => console.error(`[bfsi-feature] ${msg}`);
19
+ const die = (msg) => {
20
+ log(`error: ${msg}`);
21
+ exit(2);
22
+ };
23
+
24
+ function parseArgs(args) {
25
+ const positional = args.filter((a) => !a.startsWith('--'));
26
+ const flags = Object.fromEntries(
27
+ args
28
+ .filter((a) => a.startsWith('--'))
29
+ .map((a) => {
30
+ const [k, v = 'true'] = a.replace(/^--/, '').split('=');
31
+ return [k, v];
32
+ }),
33
+ );
34
+ return { positional, flags };
35
+ }
36
+
37
+ function toKebab(s) {
38
+ return s.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
39
+ }
40
+
41
+ async function exists(p) {
42
+ try {
43
+ await access(p);
44
+ return true;
45
+ } catch {
46
+ return false;
47
+ }
48
+ }
49
+
50
+ async function render(templateName, vars) {
51
+ const tplPath = join(TEMPLATES_DIR, templateName);
52
+ if (!(await exists(tplPath))) {
53
+ log(`warning: template missing: ${templateName} (skipping)`);
54
+ return null;
55
+ }
56
+ let content = await readFile(tplPath, 'utf8');
57
+ for (const [k, v] of Object.entries(vars)) {
58
+ content = content.replaceAll(`{{${k}}}`, v);
59
+ }
60
+ return content;
61
+ }
62
+
63
+ async function writeIfTemplate(templateName, dest, vars) {
64
+ const content = await render(templateName, vars);
65
+ if (content === null) return false;
66
+ await mkdir(dirname(dest), { recursive: true });
67
+ await writeFile(dest, content, 'utf8');
68
+ log(`wrote ${dest}`);
69
+ return true;
70
+ }
71
+
72
+ async function main() {
73
+ const { positional, flags } = parseArgs(argv.slice(2));
74
+ const Name = positional[0];
75
+
76
+ if (!Name) die('feature name is required');
77
+ if (!/^[A-Z][A-Za-z0-9]+$/.test(Name)) die('feature name must be PascalCase');
78
+
79
+ const variant = flags.variant || 'rtk';
80
+ if (!['rtk', 'tanstack'].includes(variant)) {
81
+ die(`unknown variant: ${variant} (expected rtk or tanstack)`);
82
+ }
83
+
84
+ const kebab = toKebab(Name);
85
+ const root = cwd();
86
+ const featureDir = join(root, 'src', 'features', Name);
87
+
88
+ if (await exists(featureDir)) {
89
+ die(`feature directory already exists: ${featureDir}`);
90
+ }
91
+
92
+ const vars = {
93
+ Name,
94
+ name: Name.charAt(0).toLowerCase() + Name.slice(1),
95
+ kebab,
96
+ NAME: Name.toUpperCase(),
97
+ };
98
+
99
+ // Generate files (templates may not all exist yet — that's OK in v0.1)
100
+ const apiTemplate = variant === 'rtk' ? 'api.rtk.ts.tpl' : 'api.tanstack.ts.tpl';
101
+ await writeIfTemplate(apiTemplate, join(featureDir, 'api.ts'), vars);
102
+ await writeIfTemplate('schema.ts.tpl', join(featureDir, 'schema.ts'), vars);
103
+ await writeIfTemplate('types.ts.tpl', join(featureDir, 'types.ts'), vars);
104
+ await writeIfTemplate('constants.ts.tpl', join(featureDir, 'constants.ts'), vars);
105
+ await writeIfTemplate('routes.tsx.tpl', join(featureDir, 'routes.tsx'), vars);
106
+ await writeIfTemplate('index.ts.tpl', join(featureDir, 'index.ts'), vars);
107
+ await writeIfTemplate(
108
+ 'containers.list.tsx.tpl',
109
+ join(featureDir, 'containers', `${Name}List.tsx`),
110
+ vars,
111
+ );
112
+ await writeIfTemplate(
113
+ 'containers.form.tsx.tpl',
114
+ join(featureDir, 'containers', `${Name}Form.tsx`),
115
+ vars,
116
+ );
117
+ await writeIfTemplate(
118
+ 'components.table.tsx.tpl',
119
+ join(featureDir, 'components', `${Name}Table.tsx`),
120
+ vars,
121
+ );
122
+ await writeIfTemplate(
123
+ 'tests.schema.test.ts.tpl',
124
+ join(featureDir, '__tests__', 'schema.test.ts'),
125
+ vars,
126
+ );
127
+
128
+ log(`done: feature "${Name}" scaffolded under src/features/${Name}/`);
129
+ log(`variant: ${variant}`);
130
+ log(`next: pnpm typecheck && pnpm lint --filter ${kebab}`);
131
+ }
132
+
133
+ main().catch((err) => {
134
+ log(`unexpected error: ${err.message}`);
135
+ exit(2);
136
+ });
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: bfsi-form
3
+ description: Creates a React Hook Form + Zod form with BFSI-secure defaults (no autocomplete on PII fields, paste prevention on sensitive inputs, audit on submit, error masking). Use when the user types /bfsi-form, asks to "create a form", "add a form to this page", "build a KYC form", or "scaffold a transfer form".
4
+ disable-model-invocation: true
5
+ argument-hint: <FormName> [--fields "name:type,..."]
6
+ allowed-tools: Read Write Edit Glob Grep
7
+ ---
8
+
9
+ # BFSI Form Scaffold
10
+
11
+ Generates a `react-hook-form` + `zod` form with BFSI-secure defaults wired into shadcn/ui's `<Form>` primitives.
12
+
13
+ ## What it adds
14
+
15
+ ```
16
+ <TargetFile>.tsx
17
+ └── <FormName>Form # Container component
18
+ ├── useFormWithZod() # from @react-vault/ui
19
+ ├── audit-wrapped onSubmit # via useAuditedAction
20
+ ├── PIIMaskedDisplay # auto-wrap for fields matching PII pattern
21
+ └── BFSI defaults:
22
+ ├── autocomplete="off" on PII fields
23
+ ├── onPaste guards on sensitive inputs
24
+ ├── inputMode + pattern for known formats (PAN, Aadhaar, mobile)
25
+ ├── error messages from i18n keys (never raw)
26
+ └── submit disabled while !isDirty || !isValid || isSubmitting
27
+ ```
28
+
29
+ ## Workflow
30
+
31
+ ### Step 1: Determine field set
32
+
33
+ If `--fields` is provided, parse it (`name:type` comma-separated, e.g. `pan:string,amount:number,date:date`).
34
+ Otherwise, ask the user (via prompt in chat) which fields they need.
35
+
36
+ ### Step 2: Build the Zod schema
37
+
38
+ For each field, choose a base from BFSI presets:
39
+
40
+ - `pan` → `z.string().regex(/^[A-Z]{5}[0-9]{4}[A-Z]$/, t('error.pan_invalid'))`
41
+ - `aadhaar` → `z.string().regex(/^\d{12}$/).refine(verhoeff)`
42
+ - `mobile` → `z.string().regex(/^[6-9]\d{9}$/)`
43
+ - `email` → `z.string().email()`
44
+ - `amount` → `z.number().positive().multipleOf(0.01)`
45
+ - `account_number` → `z.string().regex(/^\d{9,18}$/)`
46
+ - `ifsc` → `z.string().regex(/^[A-Z]{4}0[A-Z0-9]{6}$/)`
47
+ - `date` → `z.coerce.date()`
48
+
49
+ If a field name doesn't match a preset, fall back to `z.string().min(1)` and flag it for review.
50
+
51
+ ### Step 3: Generate the form component
52
+
53
+ Use the template at `references/templates/form.tsx.tpl`. Imports from `@react-vault/ui` and `@react-vault/core`. Submit handler uses `useAuditedAction("<feature>.<form>.submitted")`.
54
+
55
+ ### Step 4: Add i18n keys
56
+
57
+ Add label/placeholder/error keys to the project's translation files (en.json + hi.json placeholder).
58
+
59
+ ### Step 5: Verify
60
+
61
+ Run `pnpm typecheck` on the new file. If `useFormWithZod` import fails, the project doesn't have `@react-vault/ui` installed — tell the user to install it.
62
+
63
+ ## Conventions
64
+
65
+ - **Never** capture card numbers or CVVs in a regular form. Use `<PCITokenizedCardInput>` from `@react-vault/ui` which embeds a PCI-compliant iframe.
66
+ - **Never** persist form drafts of PII fields to localStorage. Use `sessionStorage` with the `secureStorage` wrapper from `@react-vault/core/storage`.
67
+ - **Submit handler returns a Promise.** Errors thrown inside it are caught by the form, surfaced via toast, and audited as `<form>.submission_failed`.
68
+
69
+ ## References
70
+
71
+ - Templates: [`references/templates/`](references/templates/)
72
+ - Validation regex catalogue: [`references/validation-regex.md`](references/validation-regex.md)
73
+ - shadcn/ui Form primer: https://ui.shadcn.com/docs/components/form
@@ -0,0 +1,50 @@
1
+ # BFSI Validation Regex Catalogue
2
+
3
+ All regex patterns used by the `bfsi-form` skill. Keep this file as the single source of truth — the `@react-vault/core/pii` module imports from here.
4
+
5
+ ## Identity
6
+
7
+ | Field | Regex | Notes |
8
+ | --------------- | ------------------------------- | ------------------------------------------------------ |
9
+ | PAN | `^[A-Z]{5}[0-9]{4}[A-Z]$` | Permanent Account Number (Income Tax India) |
10
+ | Aadhaar | `^\d{12}$` | 12 digits + Verhoeff checksum (see `aadhaar.verhoeff`) |
11
+ | Passport | `^[A-Z][0-9]{7}$` | Indian passport |
12
+ | Voter ID | `^[A-Z]{3}\d{7}$` | EPIC number |
13
+ | Driving Licence | `^[A-Z]{2}\d{2} ?\d{4} ?\d{7}$` | State-prefix + sequence |
14
+
15
+ ## Contact
16
+
17
+ | Field | Regex | Notes |
18
+ | -------------- | ---------------------------------------- | ---------------------- |
19
+ | Mobile (India) | `^[6-9]\d{9}$` | 10 digits starting 6-9 |
20
+ | Email | RFC 5322 lite (use `z.string().email()`) | Don't roll your own |
21
+ | Pincode | `^\d{6}$` | Indian postal code |
22
+
23
+ ## Banking
24
+
25
+ | Field | Regex | Notes |
26
+ | -------------- | --------------------------------------------- | --------------------------------------- |
27
+ | Account Number | `^\d{9,18}$` | Bank-specific; widest common range |
28
+ | IFSC | `^[A-Z]{4}0[A-Z0-9]{6}$` | 4 alphas + `0` + 6 alphanumeric |
29
+ | MICR | `^\d{9}$` | Magnetic Ink Character Recognition code |
30
+ | UPI VPA | `^[a-zA-Z0-9.\-_]{2,256}@[a-zA-Z]{2,64}$` | virtual payment address |
31
+ | SWIFT/BIC | `^[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?$` | 8 or 11 chars |
32
+ | GSTIN | `^\d{2}[A-Z]{5}\d{4}[A-Z][A-Z\d][Z][A-Z\d]$` | Goods and Services Tax ID |
33
+
34
+ ## Money
35
+
36
+ | Field | Pattern | Notes |
37
+ | ---------- | ---------------------------------------- | ------------------------------------------------------------------------------- |
38
+ | Amount (₹) | `z.number().positive().multipleOf(0.01)` | NEVER use float math — always paise as integer for internal, format for display |
39
+ | Percentage | `z.number().min(0).max(100)` | Interest rates, ratios |
40
+
41
+ ## Card data — DO NOT USE
42
+
43
+ There are no regex patterns for card numbers, CVVs, expiry dates in this catalogue. **All card data must flow through `<PCITokenizedCardInput>` which uses a PCI-compliant iframe.** A bare `<input>` capturing card data brings your app into PCI-DSS scope — never worth it.
44
+
45
+ ## Implementation notes
46
+
47
+ - All regex patterns are case-sensitive unless noted.
48
+ - For "uppercase-or-lowercase" inputs, normalise to uppercase in the schema's `.transform()` step — don't make the regex case-insensitive.
49
+ - For visual readability (e.g. account number with spaces), add a `display` mapper that formats; never store the formatted version.
50
+ - Aadhaar regex alone is not enough — add `.refine(aadhaarVerhoeff)` to validate the checksum digit.
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: bfsi-onboarding
3
+ description: Onboards a new developer to a Your Real Company BFSI project. Explains the project structure, key conventions, how features are organised, where security primitives live, and how the Claude toolkit assists day-to-day work. Use when the user is new to the codebase and asks "how does this project work", "where do I start", "give me an overview", "what's the architecture", or "how do I add a feature".
4
+ ---
5
+
6
+ # BFSI Project Onboarding
7
+
8
+ You are explaining a Your Real Company BFSI React project to a developer who is new to it. Be concise but thorough. Adapt depth based on their background (ask once if unclear: "Are you new to React, or new to this specific BFSI starter?").
9
+
10
+ ## The project at a glance
11
+
12
+ This project was scaffolded from `@react-vault/react-starter`. It's a **Vite + React + TypeScript SPA** with security, audit, and compliance primitives wired in by default.
13
+
14
+ Stack:
15
+
16
+ - **React 18** + **Vite 5** + **TypeScript strict**
17
+ - **Tailwind CSS** + **shadcn/ui** (components owned in `src/components/ui/`)
18
+ - **React Hook Form** + **Zod** for forms
19
+ - **RTK Query** OR **TanStack Query** for data fetching (check `package.json`)
20
+ - **react-router-dom v6** with `<ProtectedRoute>` + `<CanAccess>` guards
21
+ - **react-i18next** for i18n (en + hi default)
22
+ - **Vitest** + **Testing Library** + **Playwright**
23
+
24
+ ## Where things live
25
+
26
+ ```
27
+ src/
28
+ ├── app/ # App.tsx, providers, root layout
29
+ ├── features/<Feature>/ # ALL feature code lives here (api, containers, components, tests)
30
+ ├── routes/ # Route config, ProtectedRoute, CanAccess
31
+ ├── shared/ # Cross-feature components (ErrorBoundary, NotFound)
32
+ ├── i18n/ # react-i18next setup + translations
33
+ ├── env.ts # Zod-validated env vars
34
+ └── main.tsx # Entry point
35
+ ```
36
+
37
+ Security & audit primitives come from npm packages:
38
+
39
+ - `@react-vault/core` — encryption, PII utils, audit client, axios factory, auth helpers
40
+ - `@react-vault/ui` — Tailwind + shadcn + BFSI compositions (PIIMaskedDisplay, ConfirmModal, etc.)
41
+
42
+ ## Day-to-day workflows
43
+
44
+ ### Adding a new feature
45
+
46
+ Use `/bfsi-feature MyFeature` — generates the full directory.
47
+
48
+ ### Adding an endpoint
49
+
50
+ Use `/bfsi-api-endpoint GET /my-resource --feature MyFeature` — adds typed endpoint with audit.
51
+
52
+ ### Adding a form
53
+
54
+ Use `/bfsi-form MyForm --fields "pan:string,amount:number"` — generates RHF + Zod form with BFSI defaults.
55
+
56
+ ### Masking a PII field in display
57
+
58
+ Use `/bfsi-pii-field pan user.pan` — wraps with `<PIIMaskedDisplay>`.
59
+
60
+ ### Before a PR
61
+
62
+ Run `/bfsi-compliance-check` — runs OWASP + RBI + PCI checklist over the diff.
63
+ Optionally run `/bfsi-review` — spawns full multi-agent review.
64
+
65
+ ### Committing
66
+
67
+ Use `/bfsi-commit` — generates audit-friendly Conventional Commits message.
68
+
69
+ ## Critical conventions
70
+
71
+ 1. **Container-component split**: containers hold side-effects (API, audit), components are pure JSX.
72
+ 2. **All API responses are Zod-parsed**: runtime safety. Never trust the network shape.
73
+ 3. **All mutations are audited**: use `useAuditedMutation` (RTK) or wrap with `useAuditedAction` (TanStack).
74
+ 4. **All routes are protected**: `<ProtectedRoute permission="...">`. Defaults to authenticated-only if `permission` omitted, but explicit is better.
75
+ 5. **PII never enters localStorage**: use `secureStorage` from `@react-vault/core/storage` (memory-first, sessionStorage fallback, encrypted IndexedDB option).
76
+ 6. **No card data in HTML inputs**: use `<PCITokenizedCardInput>` from `@react-vault/ui`.
77
+ 7. **No `any` types**: types flow from Zod schemas.
78
+ 8. **All user-facing strings via `t()`**: never inline. Even error messages.
79
+ 9. **Conventional Commits with BFSI types**: see `/bfsi-commit`. `security` and `compliance` are extra types beyond standard set.
80
+
81
+ ## The Claude toolkit
82
+
83
+ The `.claude/` directory in this project enables a plugin called `toolkit`. Run `/hooks` to see registered hooks (file protection, secret scanner, formatter, PII scanner, etc.). Run `/plugin` to see the toolkit. Run `/bfsi-doctor` to verify everything's wired up.
84
+
85
+ Hooks may block you from:
86
+
87
+ - Editing `.env*`, `*.pem`, `credentials.json`, `.git/` files
88
+ - Running `rm -rf`, `git push --force` on protected branches
89
+ - Writing files that contain secret patterns (API keys, tokens)
90
+ - Writing files that introduce PII patterns into logs
91
+
92
+ These are not personal — they protect every dev from a class of mistake that's expensive in BFSI.
93
+
94
+ ## Getting unstuck
95
+
96
+ - Architecture questions → ask `bfsi-architect` agent (`@bfsi-architect how should I structure ...`)
97
+ - Security questions → ask `bfsi-security-reviewer`
98
+ - Performance questions → ask `bfsi-performance-reviewer`
99
+ - Test patterns → look at `bfsi-test-pattern` reference skill (it'll auto-load when you ask)
100
+ - Stuck on an error → look at `bfsi-error-message` reference skill
101
+
102
+ ## What NOT to do (common pitfalls)
103
+
104
+ - ❌ Don't bypass `useAuditedMutation` — every state change is auditable.
105
+ - ❌ Don't put PII in URL search params.
106
+ - ❌ Don't trust client-side permission checks alone — backend re-checks every API call.
107
+ - ❌ Don't use `localStorage` for tokens. Use the auth module's storage strategy.
108
+ - ❌ Don't use `dangerouslySetInnerHTML` without sanitisation.
109
+ - ❌ Don't write your own crypto — use `@react-vault/core/encryption`.
110
+ - ❌ Don't commit to `main` directly — always via PR.
@@ -0,0 +1,90 @@
1
+ ---
2
+ name: bfsi-pii-field
3
+ description: Wraps a data field with PII masking (click-to-reveal + audit log) using PIIMaskedDisplay from @react-vault/ui. Use when the user types /bfsi-pii-field, asks to "mask this PAN", "hide the account number", "add masking to Aadhaar", or "make this field a PII field".
4
+ disable-model-invocation: true
5
+ argument-hint: <field-type> <variable-or-prop>
6
+ allowed-tools: Read Edit Grep
7
+ ---
8
+
9
+ # BFSI PII Field
10
+
11
+ Wraps a value display with `<PIIMaskedDisplay type="pan" value={...} />` from `@react-vault/ui`.
12
+
13
+ ## Supported types
14
+
15
+ | Type | Default mask | Reveal duration |
16
+ | ---------------- | ------------------ | --------------- |
17
+ | `pan` | `ABCDE****F` | 30s |
18
+ | `aadhaar` | `XXXX XXXX 1234` | 15s |
19
+ | `account_number` | `****1234` | 30s |
20
+ | `mobile` | `+91 ******1234` | 30s |
21
+ | `email` | `j***@example.com` | 30s |
22
+ | `card_last4` | `**** 1234` | 60s |
23
+ | `name` | initials only | 60s |
24
+ | `address` | first line + `…` | 60s |
25
+ | `dob` | masked, age shown | never |
26
+
27
+ ## What it does
28
+
29
+ Given a JSX expression like:
30
+
31
+ ```tsx
32
+ <td>{user.pan}</td>
33
+ ```
34
+
35
+ Replaces it with:
36
+
37
+ ```tsx
38
+ <td>
39
+ <PIIMaskedDisplay type="pan" value={user.pan} auditTarget={{ type: 'user', id: user.id }} />
40
+ </td>
41
+ ```
42
+
43
+ This adds:
44
+
45
+ 1. **Click-to-reveal** with audit log entry (`data.pan.revealed`)
46
+ 2. **Auto re-mask** after the type-specific duration
47
+ 3. **Copy guard** — copying the masked text returns "\*\*\*\*" not the value
48
+ 4. **a11y** — `aria-label="masked PAN, click to reveal"`, screen-reader-friendly
49
+
50
+ ## Workflow
51
+
52
+ ### Step 1: Locate the field
53
+
54
+ If the user gives a variable like `user.pan`, find the occurrences in the current file. If multiple, ask which one.
55
+
56
+ ### Step 2: Determine the type
57
+
58
+ If not provided, infer from the variable name:
59
+
60
+ - contains `pan` → `pan`
61
+ - contains `aadhaar` / `uid` → `aadhaar`
62
+ - contains `account` / `acc_no` → `account_number`
63
+ - contains `mobile` / `phone` → `mobile`
64
+ - contains `email` → `email`
65
+ - contains `dob` / `birth` → `dob`
66
+ - otherwise → ask the user.
67
+
68
+ ### Step 3: Determine the audit target
69
+
70
+ The `auditTarget` prop tells the audit log which resource the field belongs to. Infer from context:
71
+
72
+ - If the variable is `user.pan`, target = `{ type: 'user', id: user.id }`
73
+ - If it's `kycRecord.aadhaar`, target = `{ type: 'kyc_record', id: kycRecord.id }`
74
+ - Otherwise, ask the user.
75
+
76
+ ### Step 4: Add the import (if missing)
77
+
78
+ ```tsx
79
+ import { PIIMaskedDisplay } from '@react-vault/ui';
80
+ ```
81
+
82
+ ### Step 5: Replace + verify
83
+
84
+ Edit the file. Run `pnpm typecheck` on the changed file. Report success.
85
+
86
+ ## When NOT to use
87
+
88
+ - **In a `<form>` input field.** Use `<SecureFormField>` instead — that handles paste guards and autocomplete prevention for input, not display.
89
+ - **In exported data.** Exports should use the same mask but emit a `data.<field>.exported` event with a higher severity (don't reveal in CSV without explicit user consent).
90
+ - **In server-rendered HTML.** Masking on the client only is insufficient for SSR contexts — the backend must also mask before sending. Flag this to the user if the file looks like SSR.
@@ -0,0 +1,179 @@
1
+ ---
2
+ name: bfsi-test-pattern
3
+ description: Reference for BFSI testing patterns — security scenarios (auth bypass, injection, race conditions), accessibility tests, audit log assertions, PII masking checks, and form validation edge cases. Auto-loads when the user asks about writing tests, test coverage, security tests, a11y tests, or BFSI-specific test patterns.
4
+ ---
5
+
6
+ # BFSI Testing Patterns
7
+
8
+ Reference for what to test in a Rsense BFSI project. Goes beyond happy-path unit tests.
9
+
10
+ ## Tools
11
+
12
+ - **Vitest** for unit + integration tests
13
+ - **@testing-library/react** for component tests
14
+ - **@axe-core/react** for a11y assertions
15
+ - **MSW (Mock Service Worker)** for network mocking
16
+ - **Playwright** for E2E
17
+
18
+ ## The BFSI test pyramid
19
+
20
+ ```
21
+ /\
22
+ / \ Playwright E2E (few)
23
+ /____\
24
+ / \
25
+ / RTL + \ Integration (some)
26
+ / MSW \
27
+ /____________\
28
+ / \
29
+ / Vitest unit \ Unit (many)
30
+ /__________________\
31
+ ```
32
+
33
+ ## Required test categories per feature
34
+
35
+ For every feature, you need:
36
+
37
+ ### 1. Schema tests (Zod)
38
+
39
+ ```ts
40
+ // src/features/Kyc/__tests__/schema.test.ts
41
+ import { describe, expect, it } from 'vitest';
42
+ import { kycSubmissionSchema } from '../schema';
43
+
44
+ describe('kycSubmissionSchema', () => {
45
+ it('accepts valid PAN', () => {
46
+ expect(() => kycSubmissionSchema.parse({ pan: 'ABCDE1234F', ... })).not.toThrow();
47
+ });
48
+ it('rejects malformed PAN', () => {
49
+ expect(() => kycSubmissionSchema.parse({ pan: 'abcd', ... })).toThrow(/invalid/i);
50
+ });
51
+ it('rejects Aadhaar with wrong checksum', () => {
52
+ expect(() => kycSubmissionSchema.parse({ aadhaar: '123456789012', ... })).toThrow();
53
+ });
54
+ it('strips unknown fields (whitelist)', () => {
55
+ const parsed = kycSubmissionSchema.parse({ pan: 'ABCDE1234F', extra: 'evil', ... });
56
+ expect(parsed).not.toHaveProperty('extra');
57
+ });
58
+ });
59
+ ```
60
+
61
+ ### 2. Container tests (RTL + MSW)
62
+
63
+ ```ts
64
+ // src/features/Kyc/__tests__/containers.test.tsx
65
+ describe('KycList container', () => {
66
+ it('renders empty state when no records', async () => { /* ... */ });
67
+ it('masks PAN in the table by default', async () => {
68
+ render(<KycList />);
69
+ await screen.findByText(/loaded/i);
70
+ // PIIMaskedDisplay shows masked value
71
+ expect(screen.getByText('ABCDE****F')).toBeInTheDocument();
72
+ expect(screen.queryByText('ABCDE1234F')).not.toBeInTheDocument();
73
+ });
74
+ it('reveals PAN on click and fires audit event', async () => {
75
+ const audit = vi.spyOn(auditClient, 'record');
76
+ render(<KycList />);
77
+ await userEvent.click(screen.getByRole('button', { name: /reveal pan/i }));
78
+ expect(screen.getByText('ABCDE1234F')).toBeInTheDocument();
79
+ expect(audit).toHaveBeenCalledWith(expect.objectContaining({
80
+ event_name: 'data.pan.revealed',
81
+ }));
82
+ });
83
+ });
84
+ ```
85
+
86
+ ### 3. Permission tests
87
+
88
+ ```ts
89
+ describe('KycList route', () => {
90
+ it('redirects to /login when unauthenticated', () => { /* ... */ });
91
+ it('shows 403 when authenticated but lacks kyc.view permission', () => { /* ... */ });
92
+ it('renders for users with kyc.view', () => { /* ... */ });
93
+ });
94
+ ```
95
+
96
+ ### 4. Idempotency tests
97
+
98
+ ```ts
99
+ describe('submitKyc mutation', () => {
100
+ it('includes Idempotency-Key header', async () => {
101
+ const spy = vi.spyOn(http, 'post');
102
+ await store.dispatch(api.endpoints.submitKyc.initiate(payload));
103
+ expect(spy).toHaveBeenCalledWith(
104
+ expect.anything(),
105
+ expect.objectContaining({
106
+ headers: expect.objectContaining({ 'Idempotency-Key': expect.any(String) }),
107
+ }),
108
+ );
109
+ });
110
+ it('uses the same key on retry', async () => { /* ... */ });
111
+ });
112
+ ```
113
+
114
+ ### 5. A11y tests
115
+
116
+ ```ts
117
+ import { axe } from 'jest-axe';
118
+
119
+ describe('KycForm a11y', () => {
120
+ it('has no a11y violations', async () => {
121
+ const { container } = render(<KycForm />);
122
+ expect(await axe(container)).toHaveNoViolations();
123
+ });
124
+ it('all inputs have labels', () => {
125
+ render(<KycForm />);
126
+ expect(screen.getByLabelText(/pan/i)).toBeInTheDocument();
127
+ expect(screen.getByLabelText(/aadhaar/i)).toBeInTheDocument();
128
+ });
129
+ it('error messages are announced (aria-live)', async () => { /* ... */ });
130
+ });
131
+ ```
132
+
133
+ ### 6. Security tests
134
+
135
+ ```ts
136
+ describe('KycList security', () => {
137
+ it('does not log PAN to console', async () => {
138
+ const logSpy = vi.spyOn(console, 'log');
139
+ render(<KycList />);
140
+ await screen.findByText(/loaded/i);
141
+ const allLogArgs = logSpy.mock.calls.flat().join(' ');
142
+ expect(allLogArgs).not.toMatch(/[A-Z]{5}\d{4}[A-Z]/); // PAN pattern
143
+ });
144
+ it('does not store PAN in localStorage', async () => {
145
+ render(<KycList />);
146
+ await screen.findByText(/loaded/i);
147
+ for (let i = 0; i < localStorage.length; i++) {
148
+ const key = localStorage.key(i)!;
149
+ const value = localStorage.getItem(key) || '';
150
+ expect(value).not.toMatch(/[A-Z]{5}\d{4}[A-Z]/);
151
+ }
152
+ });
153
+ it('does not include PAN in URLs (query / hash)', () => {
154
+ expect(window.location.search).not.toMatch(/[A-Z]{5}\d{4}[A-Z]/);
155
+ expect(window.location.hash).not.toMatch(/[A-Z]{5}\d{4}[A-Z]/);
156
+ });
157
+ });
158
+ ```
159
+
160
+ ### 7. E2E (Playwright)
161
+
162
+ For each route, one E2E that:
163
+ - Logs in
164
+ - Navigates to the route
165
+ - Performs the primary user action
166
+ - Asserts the success state
167
+ - Asserts the audit event was sent (via mocked endpoint or test backend)
168
+
169
+ ## Patterns to copy
170
+
171
+ When unsure, copy patterns from `src/features/_example/__tests__/`. The example feature is intentionally well-tested as a reference.
172
+
173
+ ## Coverage targets
174
+
175
+ - Container components: 80%+ statement coverage
176
+ - Schemas (Zod): 100% (cheap and high-value)
177
+ - Pure components: 60%+ (style edge cases caught visually)
178
+ - E2E: one per route
179
+ - Security: every PII-handling component must have category 5 + 6 tests
@@ -0,0 +1,2 @@
1
+ export declare function main(): Promise<void>;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA2DA,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAyG1C"}