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