codeninja 3.2.0 → 4.0.1

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 (111) hide show
  1. package/README.md +15 -4
  2. package/agent/database-agent.md +24 -1
  3. package/agent/nodejs-agent.md +79 -0
  4. package/cli.js +27 -7
  5. package/commands/audit.workflow.md +4 -1
  6. package/commands/db-create-table.workflow.md +1 -1
  7. package/commands/initialize-project.workflow.md +21 -0
  8. package/ide/antigravity/.agents/personas/database-architect.md +431 -153
  9. package/ide/antigravity/.agents/personas/global-orchestrator.md +202 -85
  10. package/ide/antigravity/.agents/personas/nodejs-backend.md +368 -133
  11. package/ide/antigravity/.agents/personas/reactjs-frontend.md +182 -101
  12. package/ide/antigravity/.agents/skills/api-builder/SKILL.md +58 -0
  13. package/ide/antigravity/.agents/skills/code-intelligence/SKILL.md +22 -0
  14. package/ide/antigravity/.agents/skills/database/SKILL.md +32 -0
  15. package/ide/antigravity/.agents/skills/mcp-and-context/SKILL.md +76 -82
  16. package/ide/antigravity/.agents/skills/reactjs/SKILL.md +36 -0
  17. package/ide/antigravity/.agents/workflows/codeninja-api.md +76 -83
  18. package/ide/antigravity/.agents/workflows/codeninja-audit.md +82 -44
  19. package/ide/antigravity/.agents/workflows/codeninja-db-create.md +107 -94
  20. package/ide/antigravity/.agents/workflows/codeninja-db-drop.md +89 -67
  21. package/ide/antigravity/.agents/workflows/codeninja-db-index.md +86 -54
  22. package/ide/antigravity/.agents/workflows/codeninja-db-modify.md +126 -68
  23. package/ide/antigravity/.agents/workflows/codeninja-db-seed.md +87 -59
  24. package/ide/antigravity/.agents/workflows/codeninja-db-sync.md +77 -41
  25. package/ide/antigravity/.agents/workflows/codeninja-debug.md +35 -21
  26. package/ide/antigravity/.agents/workflows/codeninja-design.md +49 -35
  27. package/ide/antigravity/.agents/workflows/codeninja-explain.md +41 -20
  28. package/ide/antigravity/.agents/workflows/codeninja-init.md +479 -289
  29. package/ide/antigravity/.agents/workflows/codeninja-integrate-api.md +253 -136
  30. package/ide/antigravity/.agents/workflows/codeninja-modularize.md +250 -132
  31. package/ide/antigravity/.agents/workflows/codeninja-optimize.md +71 -29
  32. package/ide/antigravity/.agents/workflows/codeninja-refactor.md +50 -42
  33. package/ide/antigravity/.agents/workflows/codeninja-review.md +38 -21
  34. package/ide/antigravity/.agents/workflows/codeninja-sync.md +922 -141
  35. package/ide/antigravity/.agents/workflows/codeninja-test.md +34 -49
  36. package/ide/antigravity/.agents/workflows/codeninja-validate-page.md +449 -151
  37. package/ide/claude-code/.claude/CLAUDE.md +99 -0
  38. package/ide/claude-code/.claude/agents/database-agent.md +535 -0
  39. package/ide/claude-code/.claude/agents/nodejs-agent.md +493 -0
  40. package/ide/claude-code/.claude/agents/reactjs-agent.md +267 -0
  41. package/ide/claude-code/.claude/commands/codeninja-api.md +104 -0
  42. package/ide/claude-code/.claude/commands/codeninja-audit.md +119 -0
  43. package/ide/claude-code/.claude/commands/codeninja-db-create.md +138 -0
  44. package/ide/claude-code/.claude/commands/codeninja-db-drop.md +109 -0
  45. package/ide/claude-code/.claude/commands/codeninja-db-index.md +103 -0
  46. package/ide/claude-code/.claude/commands/codeninja-db-modify.md +165 -0
  47. package/ide/claude-code/.claude/commands/codeninja-db-seed.md +104 -0
  48. package/ide/claude-code/.claude/commands/codeninja-db-sync.md +106 -0
  49. package/ide/claude-code/.claude/commands/codeninja-debug.md +99 -0
  50. package/ide/claude-code/.claude/commands/codeninja-design.md +68 -0
  51. package/ide/claude-code/.claude/commands/codeninja-explain.md +61 -0
  52. package/ide/claude-code/.claude/commands/codeninja-init.md +529 -0
  53. package/ide/claude-code/.claude/commands/codeninja-integrate-api.md +453 -0
  54. package/ide/claude-code/.claude/commands/codeninja-modularize.md +334 -0
  55. package/ide/claude-code/.claude/commands/codeninja-optimize.md +129 -0
  56. package/ide/claude-code/.claude/commands/codeninja-refactor.md +76 -0
  57. package/ide/claude-code/.claude/commands/codeninja-review.md +87 -0
  58. package/ide/claude-code/.claude/commands/codeninja-sync.md +964 -0
  59. package/ide/claude-code/.claude/commands/codeninja-test.md +45 -0
  60. package/ide/claude-code/.claude/commands/codeninja-validate-page.md +548 -0
  61. package/ide/cursor/.cursor/rules/01-global-orchestrator.mdc +12 -13
  62. package/ide/cursor/.cursor/rules/02-mcp-and-context.mdc +47 -31
  63. package/ide/cursor/.cursor/rules/03-api-builder.mdc +32 -110
  64. package/ide/cursor/.cursor/rules/04-nodejs-generation.mdc +58 -0
  65. package/ide/cursor/.cursor/rules/05-database.mdc +54 -0
  66. package/ide/cursor/.cursor/rules/06-reactjs.mdc +36 -0
  67. package/ide/cursor/.cursor/rules/07-reactjs-generation.mdc +49 -0
  68. package/ide/cursor/.cursor/rules/08-code-intelligence.mdc +56 -0
  69. package/ide/cursor/.cursor/rules/09-workflow-steps.mdc +53 -0
  70. package/ide/vscode/.github/copilot-instructions.md +67 -382
  71. package/ide/vscode/.vscode/instructions/code-intelligence.instructions.md +58 -0
  72. package/ide/vscode/.vscode/instructions/database.instructions.md +55 -0
  73. package/ide/vscode/.vscode/instructions/nodejs.instructions.md +77 -0
  74. package/ide/vscode/.vscode/instructions/reactjs.instructions.md +42 -0
  75. package/package.json +2 -2
  76. package/tasks/ask-hashing-library.task.md +31 -0
  77. package/tasks/ask-language-type.task.md +26 -0
  78. package/tasks/ask-new-module-name.task.md +13 -0
  79. package/tasks/ask-new-service-name.task.md +13 -0
  80. package/tasks/ask-old-module-name.task.md +15 -0
  81. package/tasks/ask-old-service-name.task.md +13 -0
  82. package/tasks/ask-orm-type.task.md +26 -0
  83. package/tasks/collect-seed-data.task.md +19 -0
  84. package/tasks/generate-app.task.md +42 -0
  85. package/tasks/generate-common.task.md +13 -0
  86. package/tasks/generate-constants.task.md +13 -0
  87. package/tasks/generate-database.task.md +32 -0
  88. package/tasks/generate-encryption.task.md +28 -0
  89. package/tasks/generate-fast-defaults.task.md +7 -0
  90. package/tasks/generate-hashing.task.md +180 -0
  91. package/tasks/generate-headerValidator.task.md +13 -0
  92. package/tasks/generate-ioRedis.task.md +20 -0
  93. package/tasks/generate-language-en.task.md +12 -0
  94. package/tasks/generate-logging.task.md +12 -0
  95. package/tasks/generate-model.task.md +74 -6
  96. package/tasks/generate-notification.task.md +12 -0
  97. package/tasks/generate-package-json.task.md +69 -0
  98. package/tasks/generate-prisma-client.task.md +56 -0
  99. package/tasks/generate-prisma-schema.task.md +71 -0
  100. package/tasks/generate-rateLimiter.task.md +20 -0
  101. package/tasks/generate-readme.task.md +24 -0
  102. package/tasks/generate-response.task.md +27 -0
  103. package/tasks/generate-route-manager.task.md +32 -0
  104. package/tasks/generate-route.task.md +37 -0
  105. package/tasks/generate-swagger.task.md +8 -0
  106. package/tasks/generate-template.task.md +12 -0
  107. package/tasks/generate-tsconfig.task.md +38 -0
  108. package/tasks/generate-validator.task.md +31 -0
  109. package/ide/cursor/.cursor/rules/04-database.mdc +0 -90
  110. package/ide/cursor/.cursor/rules/05-reactjs.mdc +0 -147
  111. package/ide/cursor/.cursor/rules/06-code-intelligence.mdc +0 -112
@@ -1,124 +1,198 @@
1
+ This workflow runs when user invokes /codeninja:validate-page
2
+
1
3
  ---
2
- slash_command: /codeninja:validate-page
3
- personas: [global-orchestrator, reactjs-frontend]
4
- skills: [mcp-and-context, reactjs, code-intelligence]
4
+ type: workflow
5
+ name: validate-page
5
6
  description: >
6
- Add complete client-side form validation to a ReactJS page.
7
- Scans all inputs, assigns name/id attributes where missing, installs
8
- the chosen validation library if needed, and wires error messages.
9
- Never touches API calls or business logic.
7
+ Adds complete client-side form validation to a specific ReactJS page.
8
+ Scans all input, select, and textarea fields, assigns name and id
9
+ attributes where missing, installs the chosen validation library if
10
+ needed, wires up validation logic, and generates standard user-facing
11
+ error messages for every field. Never touches API calls or business logic.
10
12
  ---
11
13
 
12
- # /codeninja:validate-page
14
+ # Workflow: @validate-page
13
15
 
14
- ## Before Running
15
- 1. Call `context_check_stale`
16
- 2. Call `context_read` load `context.services`
17
- 3. Call `service_scan` — verify service on disk
16
+ ## Goal
17
+ Take a page that has a form and make every field fully validated with
18
+ clear, user-friendly error messages before the form is ever submitted.
18
19
 
19
20
  ## Rules
20
- - Target ONE specific page per run — never multiple pages at once
21
- - NEVER modify API call logic, submit handlers (beyond adding a validation gate), or non-form code
22
- - NEVER overwrite existing validation if the field already has it — only fill gaps
23
- - Assign `name` and `id` to every input/select/textarea that is missing them
21
+ - Target one specific page per run — never multiple pages at once
22
+ - NEVER modify API call logic, submit handlers beyond adding a validation
23
+ gate, or any non-form code
24
+ - NEVER overwrite existing validation if the field already has it
25
+ only fill gaps
26
+ - Assign `name` and `id` to every input/select/textarea that is missing
27
+ them — id = name = the field's semantic name in camelCase
24
28
  - ONE confirmation before any file is written
25
29
 
26
30
  ---
27
31
 
28
- ## Execution — Full Step-by-Step
32
+ ## Step-by-Step Execution
33
+
34
+ ---
29
35
 
30
36
  ### Phase 1 — Target Selection
31
37
 
32
- **Step 1.** Ask: "Which ReactJS service?" (list ReactJS services from `context.services`)
33
- - Store: `context.current_action.service_name`
38
+ 1. Run task: `ask-react-target-service`
39
+ Stores: `context.current_action.service_name`
34
40
 
35
- **Step 2.** Ask: "Which page path?" (e.g. `src/pages/Login/index.jsx`)
36
- - Store: `context.current_action.page_path`, `context.current_action.page_name`
41
+ 2. Run task: `ask-page-path`
42
+ Stores: `context.current_action.page_path`
43
+ Stores: `context.current_action.page_name`
37
44
 
38
- **Step 3.** Ask: "Which validation library?"
39
- 1. Yup — schema-based async validation
40
- 2. React Hook Form — controlled forms with built-in validation
41
- 3. Parsley — jQuery-based HTML5 validation (CDN, no npm)
42
- 4. Validator.js — utility functions (validator npm package)
43
- 5. Custom — plain JavaScript, no library
44
- - Store: `context.current_action.validation_library`
45
+ 3. Run task: `ask-validation-library`
46
+ Stores: `context.current_action.validation_library`
45
47
 
46
48
  ---
47
49
 
48
50
  ### Phase 2 — Scan the Page
49
51
 
50
- Read the full file at `context.current_action.page_path`.
52
+ Read the full page file at `context.current_action.page_path`.
51
53
 
52
54
  #### 2a — Find All Form Elements
53
- Scan JSX for every `<form>`, `<input>`, `<select>`, `<textarea>`, `<button type="submit">`.
54
55
 
55
- For each `<input>` record:
56
- - `type`, `name`, `id`, `value`/`defaultValue`, `onChange`, `required`, `placeholder`
57
- - Any existing validation attributes (minLength, maxLength, pattern, min, max)
58
- - Adjacent label text (`<label htmlFor="...">` or sibling `<label>`)
59
- - Any existing error display nearby (`<span className="error">`, `{errors.<n>}`, etc.)
60
-
61
- For each `<select>` record: `name`, `id`, available `<option>` values, default empty option.
62
- For each `<textarea>` record: `name`, `id`, `rows`, `cols`, character limit hints.
56
+ Scan the JSX for every `<form>`, `<input>`, `<select>`, `<textarea>`,
57
+ and `<button type="submit">` element.
58
+
59
+ For each `<input>` found, record:
60
+ - `type` attribute (text, email, password, number, tel, checkbox,
61
+ radio, file, hidden, date, etc.)
62
+ - `name` attribute (if present)
63
+ - `id` attribute (if present)
64
+ - `value` or `defaultValue` binding (if present)
65
+ - `onChange` handler (if present)
66
+ - `required` attribute (if present)
67
+ - `placeholder` text (if present)
68
+ - Any existing validation attributes (minLength, maxLength, pattern, min, max)
69
+ - Any adjacent label text (look for `<label htmlFor="...">` or a
70
+ `<label>` immediately before/after the input in the JSX)
71
+ - Any existing error display element nearby
72
+ (look for `<span className="error">`, `<p className={styles.error}>`,
73
+ `{errors.<n>}` patterns, etc.)
74
+
75
+ For each `<select>` found, record:
76
+ - `name`, `id`
77
+ - The `<option>` values available
78
+ - Whether a default empty option exists
79
+
80
+ For each `<textarea>` found, record:
81
+ - `name`, `id`
82
+ - `rows`, `cols`
83
+ - Any character limit hints
63
84
 
64
85
  #### 2b — Group Fields Into Forms
65
- If multiple `<form>` elements → group by parent form (each gets its own validation schema).
66
- If no `<form>` but inputs present treat all as one implicit form.
86
+
87
+ If multiple `<form>` elements exist on the page, group fields by their
88
+ parent `<form>`. Each form gets its own validation schema.
89
+
90
+ If no `<form>` element exists but inputs are present → treat all inputs
91
+ as belonging to one implicit form. Note this in the plan.
67
92
 
68
93
  #### 2c — Detect Existing Validation
69
- Check for any existing patterns:
70
- - `yup.object()` / `yup.string()` imports → Yup schema exists
71
- - `useForm()` from react-hook-form → RHF wired
72
- - `.parsley()` calls → Parsley initialized
73
- - Manual `if (!value)` checks in submit handler
74
- - `errors` state object
75
94
 
76
- Mark fields with existing validation as "already validated" skip them.
95
+ Check for any of these patterns already in the file:
96
+ - `yup.object()` or `yup.string()` imports → Yup schema exists
97
+ - `useForm()` from react-hook-form → RHF already wired
98
+ - `.parsley()` calls → Parsley already initialized
99
+ - Manual `if (!value)` style checks in submit handler
100
+ - Any `errors` state object
101
+
102
+ If validation already exists for a field → mark that field as
103
+ "already validated" and skip it. Only add what is missing.
77
104
 
78
105
  ---
79
106
 
80
107
  ### Phase 3 — Infer Field Semantics and Error Messages
81
108
 
82
- For each field, detect semantic type using label text → name attribute → placeholder → input type:
83
-
84
- | Signal | Semantic | Required error | Format error |
85
- |---|---|---|---|
86
- | "first name" | first_name | "Please enter your first name." | "First name must be at least 2 characters." |
87
- | "last name" | last_name | "Please enter your last name." | "Last name must be at least 2 characters." |
88
- | "full name" / "name" | full_name | "Please enter your full name." | "Full name must be at least 3 characters." |
89
- | "email" | email | "Please enter your email address." | "Please enter a valid email address." |
90
- | "phone" / "mobile" | phone | "Please enter your phone number." | "Please enter a valid phone number." |
91
- | "password" (no "confirm") | password | "Please enter your password." | "Password must be at least 8 characters." |
92
- | "confirm" + "password" | confirm_password | "Please confirm your password." | "Password and confirm password do not match." |
93
- | "username" | username | "Please enter a username." | "Username must be 3–20 characters, letters and numbers only." |
94
- | "address" | address | "Please enter your address." | |
95
- | "city" | city | "Please enter your city." | |
96
- | "zip" / "postal" | zip_code | "Please enter your zip/postal code." | "Please enter a valid zip/postal code." |
97
- | "country" | country | "Please select your country." | |
98
- | "state" / "province" | state | "Please select your state/province." | |
99
- | "date" / "dob" / "birth" | date | "Please select a date." | "Please enter a valid date." |
100
- | "amount" / "price" | amount | "Please enter an amount." | "Please enter a valid amount greater than 0." |
101
- | "description" / "message" | description | "Please enter a description." | |
102
- | "otp" / "code" / "pin" | otp | "Please enter the OTP." | "OTP must be [n] digits." |
103
- | type="file" | file_upload | "Please select a file." | "File size must not exceed [n]MB." |
104
- | "agree" / "terms" (checkbox) | terms | "Please accept the terms and conditions." | |
105
- | select (no clear label) | dropdown | "Please make a selection." | |
106
- | anything else | generic | "This field is required." | |
109
+ For each field, determine its semantic type to generate the correct
110
+ error message. Use label text first, then name attribute, then
111
+ placeholder, then input type as fallback.
112
+
113
+ #### Semantic type detection rules:
114
+
115
+ | Signal | Semantic type |
116
+ |---|---|
117
+ | label/name/placeholder contains "first name" | first_name |
118
+ | label/name/placeholder contains "last name" | last_name |
119
+ | label/name/placeholder contains "full name" or just "name" | full_name |
120
+ | label/name/placeholder contains "email" | email |
121
+ | label/name/placeholder contains "phone" or "mobile" | phone |
122
+ | label/name/placeholder contains "password" and no "confirm" | password |
123
+ | label/name/placeholder contains "confirm" and "password" | confirm_password |
124
+ | label/name/placeholder contains "username" | username |
125
+ | label/name/placeholder contains "address" | address |
126
+ | label/name/placeholder contains "city" | city |
127
+ | label/name/placeholder contains "zip" or "postal" | zip_code |
128
+ | label/name/placeholder contains "country" | country |
129
+ | label/name/placeholder contains "state" or "province" | state |
130
+ | label/name/placeholder contains "date" or "dob" or "birth" | date |
131
+ | label/name/placeholder contains "amount" or "price" | amount |
132
+ | label/name/placeholder contains "description" or "message" | description |
133
+ | label/name/placeholder contains "otp" or "code" or "pin" | otp |
134
+ | input type == "file" | file_upload |
135
+ | input type == "checkbox" and label contains "agree" or "terms" | terms |
136
+ | select with no clear label | dropdown |
137
+ | anything else | generic |
138
+
139
+ #### Standard error messages per semantic type:
140
+
141
+ | Semantic type | Required error | Format/constraint error |
142
+ |---|---|---|
143
+ | first_name | "Please enter your first name." | "First name must be at least 2 characters." |
144
+ | last_name | "Please enter your last name." | "Last name must be at least 2 characters." |
145
+ | full_name | "Please enter your full name." | "Full name must be at least 3 characters." |
146
+ | email | "Please enter your email address." | "Please enter a valid email address." |
147
+ | phone | "Please enter your phone number." | "Please enter a valid phone number." |
148
+ | password | "Please enter your password." | "Password must be at least 8 characters." |
149
+ | confirm_password | "Please confirm your password." | "Password and confirm password do not match." |
150
+ | username | "Please enter a username." | "Username must be 3–20 characters, letters and numbers only." |
151
+ | address | "Please enter your address." | — |
152
+ | city | "Please enter your city." | — |
153
+ | zip_code | "Please enter your zip/postal code." | "Please enter a valid zip/postal code." |
154
+ | country | "Please select your country." | — |
155
+ | state | "Please select your state/province." | — |
156
+ | date | "Please select a date." | "Please enter a valid date." |
157
+ | amount | "Please enter an amount." | "Please enter a valid amount greater than 0." |
158
+ | description | "Please enter a description." | — |
159
+ | otp | "Please enter the OTP." | "OTP must be [n] digits." (n from maxLength) |
160
+ | file_upload | "Please select a file." | "File size must not exceed [n]MB." (only if max is known) |
161
+ | terms | "Please accept the terms and conditions." | — |
162
+ | dropdown | "Please make a selection." | — |
163
+ | generic | "This field is required." | — |
164
+
165
+ These messages are hardcoded strings in the generated validation logic.
166
+ Do not use i18n keys for validation messages — they are frontend-only,
167
+ not served by the backend language system.
107
168
 
108
169
  ---
109
170
 
110
171
  ### Phase 4 — Assign Missing name and id Attributes
111
172
 
112
- For each field missing `name` or `id`:
113
- 1. Derive from label text or placeholder: strip non-alphanumeric, convert to camelCase
114
- e.g. "First Name" `firstName`, "Email Address" `emailAddress`
115
- 2. If no label/placeholder → use semantic type: `email`, `password`, `phone`, etc.
116
- 3. If would duplicate → append number: `phone2`, `address2`
173
+ For each field that is missing `name` or `id`:
174
+
175
+ 1. Derive the field name from its label text or placeholder (in that order):
176
+ - Strip non-alphanumeric characters
177
+ - Convert to camelCase
178
+ - Examples: "First Name" → `firstName`, "Email Address" → `emailAddress`,
179
+ "Date of Birth" → `dateOfBirth`
180
+
181
+ 2. If no label or placeholder → use semantic type as the name:
182
+ `email`, `password`, `phone`, etc.
183
+
184
+ 3. If the field name would duplicate another field in the same form →
185
+ append a number: `phone2`, `address2`
186
+
117
187
  4. Set both `name` and `id` to the same derived value.
118
188
 
189
+ Record: `{ original_element_position, assigned_name, assigned_id }`
190
+
119
191
  ---
120
192
 
121
- ### Phase 5 — Show Validation Plan
193
+ ### Phase 5 — Build Validation Plan
194
+
195
+ Show the user a complete plan before writing anything:
122
196
 
123
197
  ```
124
198
  ┌──────────────────────────────────────────────────────┐
@@ -128,17 +202,24 @@ For each field missing `name` or `id`:
128
202
  │ Library : [validation_library] │
129
203
  │ Install pkg : [package_name] (if needed) │
130
204
  ├──────────────────────────────────────────────────────┤
131
- │ FIELDS TO VALIDATE
132
- │ ✓ firstName (text) — "Please enter your first name."
133
- email (email) — "Please enter a valid email."
134
- │ ✓ password (password)— "Please enter your password."
135
- confirmPw (password)— "Passwords do not match."
205
+ │ FIELDS FOUND
206
+ │ ✓ firstName (text) — "Please enter your
207
+ first name."
208
+ │ ✓ lastName (text) — "Please enter your
209
+ last name."
210
+ │ ✓ email (email) — "Please enter your │
211
+ │ email address." │
212
+ │ ✓ password (password)— "Please enter your │
213
+ │ password." │
214
+ │ ✓ confirmPw (password)— "Password and confirm │
215
+ │ password do not match." │
136
216
  │ │
137
217
  │ FIELDS SKIPPED (already validated) │
138
218
  │ — phone (has existing required check) │
139
219
  ├──────────────────────────────────────────────────────┤
140
220
  │ ATTRIBUTES TO ASSIGN │
141
- │ confirmPassword → add name="confirmPassword", id="confirmPassword"
221
+ │ confirmPassword input → add name="confirmPassword"
222
+ │ add id="confirmPassword" │
142
223
  ├──────────────────────────────────────────────────────┤
143
224
  │ FILES TO MODIFY │
144
225
  │ → src/pages/[PageName]/index.jsx │
@@ -146,97 +227,299 @@ For each field missing `name` or `id`:
146
227
  └──────────────────────────────────────────────────────┘
147
228
  ```
148
229
 
149
- Ask: "Apply this validation plan? (yes / no / adjust)"
230
+ Ask exactly this question:
231
+ "Apply this validation plan? (yes / no / adjust)"
232
+
233
+ - If yes → proceed to Phase 6
234
+ - If no → abort. Nothing is written.
235
+ - If adjust → ask what to change (field name, error message, skip a
236
+ field). Apply and re-display.
150
237
 
151
238
  ---
152
239
 
153
240
  ### Phase 6 — Apply Validation by Library
154
241
 
155
- Read current page file. Apply ALL edits surgically never rewrite the whole file.
156
-
157
- #### If library == "yup"
158
- - Add import: `import * as Yup from 'yup';`
159
- - Create `validationSchema` above component:
160
- ```jsx
161
- const validationSchema = Yup.object({
162
- firstName: Yup.string().required('Please enter your first name.').min(2, '...'),
163
- email: Yup.string().required('...').email('Please enter a valid email address.'),
164
- password: Yup.string().required('...').min(8, '...'),
165
- confirmPassword: Yup.string().required('...').oneOf([Yup.ref('password')], '...'),
166
- // one entry per field
167
- });
168
- ```
169
- - Add state: `const [errors, setErrors] = React.useState({});`
170
- - Add `validateForm` async function inside component
171
- - Add validation gate at top of submit handler: `const isValid = await validateForm({...}); if (!isValid) return;`
172
- - Add `{errors.<field> && <span className={styles.errorMsg}>{errors.<field>}</span>}` after each input
173
- - Add `.errorMsg` CSS class to page's `.module.css`
174
-
175
- #### If library == "react-hook-form"
176
- - Add import: `import { useForm } from 'react-hook-form';`
177
- - Add hook: `const { register, handleSubmit, watch, formState: { errors } } = useForm();`
178
- - Wrap form: `<form onSubmit={handleSubmit(onSubmit)}>`
179
- - Add `{...register('fieldName', { required: '...', pattern/min/validate rules })}` to each input
180
- - For confirmPassword: `validate: (val) => val === watch('password') || '...'`
181
- - Add error display after each input
182
- - Remove redundant `value`/`onChange` props only if purely for controlled form state
183
-
184
- #### If library == "parsley"
185
- - Add to `public/index.html` before `</body>`: jQuery CDN + Parsley CDN + Parsley CSS link
186
- - Add `data-parsley-required`, `data-parsley-required-message`, `data-parsley-type`, etc. to each input
187
- - Add `id` to `<form>` element (derive: `[pageName]Form`)
188
- - Initialize via `useEffect`: `window.$('#loginForm').parsley()`
189
- - Gate in submit handler: `if (!window.$('#loginForm').parsley().validate()) return;`
190
-
191
- #### If library == "validatorjs"
192
- - Add import: `import validator from 'validator';`
193
- - Add state: `const [errors, setErrors] = React.useState({});`
194
- - Add `validateForm` function with per-field checks using `validator.isEmail()`, `validator.isEmpty()`, etc.
195
- - Add gate: `if (!validateForm()) return;`
196
- - Add error display + `.errorMsg` CSS
197
-
198
- #### If library == "custom"
199
- - No imports needed — plain JavaScript only
200
- - Same `validateForm` pattern as validatorjs but using native JS:
201
- - `value.trim().length > 0` for required
202
- - `/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)` for email
203
- - `value.length >= 8` for min length
204
- - `value === otherValue` for match
242
+ Read the current page file. Apply all edits surgically. Never rewrite
243
+ the whole file — only insert and modify what is needed.
244
+
245
+ ---
246
+
247
+ #### If validation_library == "yup"
248
+
249
+ **Imports to add at the top of the file:**
250
+ ```jsx
251
+ import * as Yup from 'yup';
252
+ ```
253
+
254
+ **Schema to create above the component function:**
255
+ ```jsx
256
+ const validationSchema = Yup.object({
257
+ firstName: Yup.string()
258
+ .required('Please enter your first name.')
259
+ .min(2, 'First name must be at least 2 characters.'),
260
+ email: Yup.string()
261
+ .required('Please enter your email address.')
262
+ .email('Please enter a valid email address.'),
263
+ password: Yup.string()
264
+ .required('Please enter your password.')
265
+ .min(8, 'Password must be at least 8 characters.'),
266
+ confirmPassword: Yup.string()
267
+ .required('Please confirm your password.')
268
+ .oneOf([Yup.ref('password')], 'Password and confirm password do not match.'),
269
+ // ... one entry per field
270
+ });
271
+ ```
272
+
273
+ **State to add inside the component:**
274
+ ```jsx
275
+ const [errors, setErrors] = React.useState({});
276
+ ```
277
+
278
+ **Validation function to add inside the component:**
279
+ ```jsx
280
+ const validateForm = async (values) => {
281
+ try {
282
+ await validationSchema.validate(values, { abortEarly: false });
283
+ setErrors({});
284
+ return true;
285
+ } catch (err) {
286
+ const newErrors = {};
287
+ err.inner.forEach((e) => { newErrors[e.path] = e.message; });
288
+ setErrors(newErrors);
289
+ return false;
290
+ }
291
+ };
292
+ ```
293
+
294
+ **Validation gate to add in the submit handler:**
295
+ Locate the existing submit handler (look for `onSubmit`, `handleSubmit`,
296
+ `onClick` on a submit button). Add a validation gate at the very top:
297
+ ```jsx
298
+ const isValid = await validateForm({ firstName, lastName, email, ... });
299
+ if (!isValid) return;
300
+ ```
301
+
302
+ **Error display elements to add under each input:**
303
+ ```jsx
304
+ {errors.firstName && <span className={styles.errorMsg}>{errors.firstName}</span>}
305
+ ```
306
+
307
+ **CSS class to add to the page's `.module.css`:**
308
+ ```css
309
+ .errorMsg {
310
+ display: block;
311
+ color: #dc3545;
312
+ font-size: 0.813rem;
313
+ margin-top: 0.25rem;
314
+ }
315
+ ```
316
+
317
+ ---
318
+
319
+ #### If validation_library == "parsley"
320
+
321
+ **In `public/index.html` (add before `</body>`):**
322
+ ```html
323
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
324
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/parsley.js/2.9.2/parsley.min.js"></script>
325
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/parsley.js/2.9.2/parsley.min.css"/>
326
+ ```
327
+
328
+ **Add `data-parsley-*` attributes to each field in JSX:**
329
+ ```jsx
330
+ <input
331
+ type="email"
332
+ name="email"
333
+ id="email"
334
+ required
335
+ data-parsley-required="true"
336
+ data-parsley-required-message="Please enter your email address."
337
+ data-parsley-type="email"
338
+ data-parsley-type-message="Please enter a valid email address."
339
+ />
340
+ ```
341
+
342
+ **Initialize Parsley on the form via useEffect:**
343
+ ```jsx
344
+ React.useEffect(() => {
345
+ if (window.$) {
346
+ window.$('#[formId]').parsley();
347
+ }
348
+ }, []);
349
+ ```
350
+
351
+ **Add `id` to the `<form>` element** if not already present.
352
+ Derive form id from: `[pageName]Form` in camelCase (e.g. `loginForm`).
353
+
354
+ **Validation gate in submit handler:**
355
+ ```jsx
356
+ if (window.$ && !window.$('#loginForm').parsley().validate()) return;
357
+ ```
358
+
359
+ ---
360
+
361
+ #### If validation_library == "react-hook-form"
362
+
363
+ **Imports to add:**
364
+ ```jsx
365
+ import { useForm } from 'react-hook-form';
366
+ ```
367
+
368
+ **Hook setup inside component (add near top of function body):**
369
+ ```jsx
370
+ const { register, handleSubmit, watch, formState: { errors } } = useForm();
371
+ ```
372
+
373
+ **Replace or wrap the existing form's onSubmit:**
374
+ ```jsx
375
+ <form onSubmit={handleSubmit(onSubmit)}>
376
+ ```
377
+ Where `onSubmit` is the existing submit handler — rename it if needed
378
+ to avoid conflict with RHF's `handleSubmit` parameter name.
379
+
380
+ **Add `{...register(...)}` to each input:**
381
+ ```jsx
382
+ <input
383
+ type="email"
384
+ id="email"
385
+ {...register('email', {
386
+ required: 'Please enter your email address.',
387
+ pattern: {
388
+ value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
389
+ message: 'Please enter a valid email address.'
390
+ }
391
+ })}
392
+ />
393
+ ```
394
+
395
+ **For confirm password — use `watch`:**
396
+ ```jsx
397
+ {...register('confirmPassword', {
398
+ required: 'Please confirm your password.',
399
+ validate: (val) => val === watch('password') ||
400
+ 'Password and confirm password do not match.'
401
+ })}
402
+ ```
403
+
404
+ **Error display elements after each input:**
405
+ ```jsx
406
+ {errors.email && <span className={styles.errorMsg}>{errors.email.message}</span>}
407
+ ```
408
+
409
+ **CSS class** (same `.errorMsg` class as Yup — add to module.css if not present).
410
+
411
+ **Remove any now-redundant `value` and `onChange` props** from inputs
412
+ that are fully managed by RHF's `register()`. Only remove these if they
413
+ were purely for controlled form state — never remove them if they are
414
+ used for other purposes (e.g. to drive conditional rendering).
415
+
416
+ ---
417
+
418
+ #### If validation_library == "validatorjs"
419
+
420
+ **Imports to add:**
421
+ ```jsx
422
+ import validator from 'validator';
423
+ ```
424
+
425
+ **State to add inside component:**
426
+ ```jsx
427
+ const [errors, setErrors] = React.useState({});
428
+ ```
429
+
430
+ **Validation function to add inside component:**
431
+ Generate one validation function that checks each field individually:
432
+ ```jsx
433
+ const validateForm = () => {
434
+ const newErrors = {};
435
+ if (!firstName || firstName.trim().length < 2) {
436
+ newErrors.firstName = 'Please enter your first name.';
437
+ }
438
+ if (!email || !validator.isEmail(email)) {
439
+ newErrors.email = validator.isEmpty(email ?? '')
440
+ ? 'Please enter your email address.'
441
+ : 'Please enter a valid email address.';
442
+ }
443
+ if (!password || password.length < 8) {
444
+ newErrors.password = !password
445
+ ? 'Please enter your password.'
446
+ : 'Password must be at least 8 characters.';
447
+ }
448
+ if (password !== confirmPassword) {
449
+ newErrors.confirmPassword = 'Password and confirm password do not match.';
450
+ }
451
+ setErrors(newErrors);
452
+ return Object.keys(newErrors).length === 0;
453
+ };
454
+ ```
455
+
456
+ **Validation gate in submit handler:**
457
+ ```jsx
458
+ if (!validateForm()) return;
459
+ ```
460
+
461
+ **Error display and CSS** — same pattern as Yup.
462
+
463
+ ---
464
+
465
+ #### If validation_library == "custom"
466
+
467
+ Generate the same `validateForm` function as the validatorjs pattern
468
+ but WITHOUT any library imports. Use only native JavaScript:
469
+ - `value.trim().length > 0` for required checks
470
+ - `/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)` for email format
471
+ - `value.length >= 8` for minimum length
472
+ - `value === otherValue` for match checks
473
+ - `/^\d+$/.test(value)` for numeric checks
474
+
475
+ No new packages. No imports beyond React.
476
+
477
+ **Error display and CSS** — same pattern as Yup.
205
478
 
206
479
  ---
207
480
 
208
481
  ### Phase 7 — Install Package (if needed)
209
482
 
210
- If library is yup/react-hook-form/validatorjs AND not in `package.json`:
211
- - Read current `package.json`
212
- - Add to dependencies:
483
+ If `validation_library` is one of: `yup`, `react-hook-form`, `validatorjs`
484
+ AND the package is NOT already in the service's `package.json`:
485
+
486
+ Read the current `package.json` for the target service.
487
+ Add the package to `dependencies`:
213
488
  - yup → `"yup": "^1.3.3"`
214
489
  - react-hook-form → `"react-hook-form": "^7.51.0"`
215
490
  - validatorjs → `"validator": "^13.12.0"`
216
- - Update `package.json` on disk
217
- - Display: "📦 Package added to package.json. Run `npm install` in [service_name]/ before testing."
218
491
 
219
- For parsley CDN only, no npm needed.
220
- For custom → no package.
492
+ Update `package.json` on disk.
493
+
494
+ Display to user:
495
+ "📦 Package added to package.json. Run `npm install` in [service_name]/
496
+ before testing."
497
+
498
+ For `parsley` → scripts added to `public/index.html` via CDN. No npm install needed.
499
+ For `custom` → no package needed.
221
500
 
222
501
  ---
223
502
 
224
- ### Phase 8 — Finalize
503
+ ### Phase 8 — Write Context and Report
225
504
 
226
- **Step 1.** Call `context_write`:
505
+ > **Multi-agent:** Delegate to `reactjs-frontend` via Task invocation for parallel execution.
506
+ > Read `.codeninja/tasks/` before applying validation changes to each page.
507
+
508
+ Run task: `write-context` with:
227
509
  - Append to `context.services[<service_name>].validated_pages`:
228
- `{ page, library, fields_validated: [...], timestamp: ISO now }`
510
+ `{ page, library, fields_validated: [list], timestamp: ISO now }`
229
511
  - Set `last_command` = "validate-page"
230
- - Append to `change_log`
512
+ - Append to top-level `change_log`:
513
+ `{ timestamp, command: "validate-page", action: "Added [library] validation to [page]", affected_files: [...] }`
514
+ - After write-context → call MCP tool `context_clear_scratchpad` with keys: ["current_action"]
231
515
 
232
- **Step 2.** Call `context_clear_scratchpad` with keys: ["current_action"]
516
+ Display completion report:
233
517
 
234
- **Step 3.** Show completion report:
235
518
  ```
236
- /codeninja:validate-page Complete
519
+ @validate-page Complete
237
520
  ─────────────────────────────────────────
238
521
  Page : [page_name]
239
- Library : [library]
522
+ Library : [validation_library]
240
523
  ─────────────────────────────────────────
241
524
  Fields validated : [n]
242
525
  Fields skipped : [n] (already had validation)
@@ -246,5 +529,20 @@ Attributes added : [n] (name/id assigned)
246
529
  ✓ src/pages/[PageName]/[PageName].module.css — .errorMsg class added
247
530
  [✓ package.json — [package] added (run npm install)]
248
531
  ─────────────────────────────────────────
249
- Next: /codeninja:integrate-api to wire forms to the backend
250
532
  ```
533
+
534
+ Run task: `show-final-summary`
535
+
536
+ ---
537
+
538
+ ## What This Workflow Does NOT Do
539
+
540
+ - Does not add server-side validation — backend validation is
541
+ handled by the NodeJS service's validators
542
+ - Does not add validation to non-form elements (buttons, nav links)
543
+ - Does not modify apiHandler.js or apiClient.js
544
+ - Does not add authentication logic
545
+ - Does not add success/error toast notifications — those belong in
546
+ the API integration layer (@integrate-api)
547
+ - Does not validate dynamic fields (fields added/removed at runtime) —
548
+ only static JSX fields present in the file at analysis time