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.
- package/README.md +15 -4
- package/agent/database-agent.md +24 -1
- package/agent/nodejs-agent.md +79 -0
- package/cli.js +27 -7
- package/commands/audit.workflow.md +4 -1
- package/commands/db-create-table.workflow.md +1 -1
- package/commands/initialize-project.workflow.md +21 -0
- package/ide/antigravity/.agents/personas/database-architect.md +431 -153
- package/ide/antigravity/.agents/personas/global-orchestrator.md +202 -85
- package/ide/antigravity/.agents/personas/nodejs-backend.md +368 -133
- package/ide/antigravity/.agents/personas/reactjs-frontend.md +182 -101
- package/ide/antigravity/.agents/skills/api-builder/SKILL.md +58 -0
- package/ide/antigravity/.agents/skills/code-intelligence/SKILL.md +22 -0
- package/ide/antigravity/.agents/skills/database/SKILL.md +32 -0
- package/ide/antigravity/.agents/skills/mcp-and-context/SKILL.md +76 -82
- package/ide/antigravity/.agents/skills/reactjs/SKILL.md +36 -0
- package/ide/antigravity/.agents/workflows/codeninja-api.md +76 -83
- package/ide/antigravity/.agents/workflows/codeninja-audit.md +82 -44
- package/ide/antigravity/.agents/workflows/codeninja-db-create.md +107 -94
- package/ide/antigravity/.agents/workflows/codeninja-db-drop.md +89 -67
- package/ide/antigravity/.agents/workflows/codeninja-db-index.md +86 -54
- package/ide/antigravity/.agents/workflows/codeninja-db-modify.md +126 -68
- package/ide/antigravity/.agents/workflows/codeninja-db-seed.md +87 -59
- package/ide/antigravity/.agents/workflows/codeninja-db-sync.md +77 -41
- package/ide/antigravity/.agents/workflows/codeninja-debug.md +35 -21
- package/ide/antigravity/.agents/workflows/codeninja-design.md +49 -35
- package/ide/antigravity/.agents/workflows/codeninja-explain.md +41 -20
- package/ide/antigravity/.agents/workflows/codeninja-init.md +479 -289
- package/ide/antigravity/.agents/workflows/codeninja-integrate-api.md +253 -136
- package/ide/antigravity/.agents/workflows/codeninja-modularize.md +250 -132
- package/ide/antigravity/.agents/workflows/codeninja-optimize.md +71 -29
- package/ide/antigravity/.agents/workflows/codeninja-refactor.md +50 -42
- package/ide/antigravity/.agents/workflows/codeninja-review.md +38 -21
- package/ide/antigravity/.agents/workflows/codeninja-sync.md +922 -141
- package/ide/antigravity/.agents/workflows/codeninja-test.md +34 -49
- package/ide/antigravity/.agents/workflows/codeninja-validate-page.md +449 -151
- package/ide/claude-code/.claude/CLAUDE.md +99 -0
- package/ide/claude-code/.claude/agents/database-agent.md +535 -0
- package/ide/claude-code/.claude/agents/nodejs-agent.md +493 -0
- package/ide/claude-code/.claude/agents/reactjs-agent.md +267 -0
- package/ide/claude-code/.claude/commands/codeninja-api.md +104 -0
- package/ide/claude-code/.claude/commands/codeninja-audit.md +119 -0
- package/ide/claude-code/.claude/commands/codeninja-db-create.md +138 -0
- package/ide/claude-code/.claude/commands/codeninja-db-drop.md +109 -0
- package/ide/claude-code/.claude/commands/codeninja-db-index.md +103 -0
- package/ide/claude-code/.claude/commands/codeninja-db-modify.md +165 -0
- package/ide/claude-code/.claude/commands/codeninja-db-seed.md +104 -0
- package/ide/claude-code/.claude/commands/codeninja-db-sync.md +106 -0
- package/ide/claude-code/.claude/commands/codeninja-debug.md +99 -0
- package/ide/claude-code/.claude/commands/codeninja-design.md +68 -0
- package/ide/claude-code/.claude/commands/codeninja-explain.md +61 -0
- package/ide/claude-code/.claude/commands/codeninja-init.md +529 -0
- package/ide/claude-code/.claude/commands/codeninja-integrate-api.md +453 -0
- package/ide/claude-code/.claude/commands/codeninja-modularize.md +334 -0
- package/ide/claude-code/.claude/commands/codeninja-optimize.md +129 -0
- package/ide/claude-code/.claude/commands/codeninja-refactor.md +76 -0
- package/ide/claude-code/.claude/commands/codeninja-review.md +87 -0
- package/ide/claude-code/.claude/commands/codeninja-sync.md +964 -0
- package/ide/claude-code/.claude/commands/codeninja-test.md +45 -0
- package/ide/claude-code/.claude/commands/codeninja-validate-page.md +548 -0
- package/ide/cursor/.cursor/rules/01-global-orchestrator.mdc +12 -13
- package/ide/cursor/.cursor/rules/02-mcp-and-context.mdc +47 -31
- package/ide/cursor/.cursor/rules/03-api-builder.mdc +32 -110
- package/ide/cursor/.cursor/rules/04-nodejs-generation.mdc +58 -0
- package/ide/cursor/.cursor/rules/05-database.mdc +54 -0
- package/ide/cursor/.cursor/rules/06-reactjs.mdc +36 -0
- package/ide/cursor/.cursor/rules/07-reactjs-generation.mdc +49 -0
- package/ide/cursor/.cursor/rules/08-code-intelligence.mdc +56 -0
- package/ide/cursor/.cursor/rules/09-workflow-steps.mdc +53 -0
- package/ide/vscode/.github/copilot-instructions.md +67 -382
- package/ide/vscode/.vscode/instructions/code-intelligence.instructions.md +58 -0
- package/ide/vscode/.vscode/instructions/database.instructions.md +55 -0
- package/ide/vscode/.vscode/instructions/nodejs.instructions.md +77 -0
- package/ide/vscode/.vscode/instructions/reactjs.instructions.md +42 -0
- package/package.json +2 -2
- package/tasks/ask-hashing-library.task.md +31 -0
- package/tasks/ask-language-type.task.md +26 -0
- package/tasks/ask-new-module-name.task.md +13 -0
- package/tasks/ask-new-service-name.task.md +13 -0
- package/tasks/ask-old-module-name.task.md +15 -0
- package/tasks/ask-old-service-name.task.md +13 -0
- package/tasks/ask-orm-type.task.md +26 -0
- package/tasks/collect-seed-data.task.md +19 -0
- package/tasks/generate-app.task.md +42 -0
- package/tasks/generate-common.task.md +13 -0
- package/tasks/generate-constants.task.md +13 -0
- package/tasks/generate-database.task.md +32 -0
- package/tasks/generate-encryption.task.md +28 -0
- package/tasks/generate-fast-defaults.task.md +7 -0
- package/tasks/generate-hashing.task.md +180 -0
- package/tasks/generate-headerValidator.task.md +13 -0
- package/tasks/generate-ioRedis.task.md +20 -0
- package/tasks/generate-language-en.task.md +12 -0
- package/tasks/generate-logging.task.md +12 -0
- package/tasks/generate-model.task.md +74 -6
- package/tasks/generate-notification.task.md +12 -0
- package/tasks/generate-package-json.task.md +69 -0
- package/tasks/generate-prisma-client.task.md +56 -0
- package/tasks/generate-prisma-schema.task.md +71 -0
- package/tasks/generate-rateLimiter.task.md +20 -0
- package/tasks/generate-readme.task.md +24 -0
- package/tasks/generate-response.task.md +27 -0
- package/tasks/generate-route-manager.task.md +32 -0
- package/tasks/generate-route.task.md +37 -0
- package/tasks/generate-swagger.task.md +8 -0
- package/tasks/generate-template.task.md +12 -0
- package/tasks/generate-tsconfig.task.md +38 -0
- package/tasks/generate-validator.task.md +31 -0
- package/ide/cursor/.cursor/rules/04-database.mdc +0 -90
- package/ide/cursor/.cursor/rules/05-reactjs.mdc +0 -147
- 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
|
-
|
|
3
|
-
|
|
4
|
-
skills: [mcp-and-context, reactjs, code-intelligence]
|
|
4
|
+
type: workflow
|
|
5
|
+
name: validate-page
|
|
5
6
|
description: >
|
|
6
|
-
|
|
7
|
-
Scans all
|
|
8
|
-
the chosen validation library if
|
|
9
|
-
|
|
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
|
-
#
|
|
14
|
+
# Workflow: @validate-page
|
|
13
15
|
|
|
14
|
-
##
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
21
|
-
- NEVER modify API call logic, submit handlers
|
|
22
|
-
|
|
23
|
-
-
|
|
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
|
-
##
|
|
32
|
+
## Step-by-Step Execution
|
|
33
|
+
|
|
34
|
+
---
|
|
29
35
|
|
|
30
36
|
### Phase 1 — Target Selection
|
|
31
37
|
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
1. Run task: `ask-react-target-service`
|
|
39
|
+
Stores: `context.current_action.service_name`
|
|
34
40
|
|
|
35
|
-
|
|
36
|
-
|
|
41
|
+
2. Run task: `ask-page-path`
|
|
42
|
+
Stores: `context.current_action.page_path`
|
|
43
|
+
Stores: `context.current_action.page_name`
|
|
37
44
|
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
66
|
-
If
|
|
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
|
-
|
|
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,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
|
89
|
-
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
|
|
|
93
|
-
|
|
|
94
|
-
|
|
|
95
|
-
|
|
|
96
|
-
|
|
|
97
|
-
|
|
|
98
|
-
|
|
|
99
|
-
|
|
|
100
|
-
|
|
|
101
|
-
|
|
|
102
|
-
|
|
|
103
|
-
|
|
|
104
|
-
|
|
|
105
|
-
|
|
|
106
|
-
|
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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 —
|
|
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
|
|
132
|
-
│ ✓ firstName (text) — "Please enter your
|
|
133
|
-
│
|
|
134
|
-
│ ✓
|
|
135
|
-
│
|
|
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"
|
|
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
|
|
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
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
220
|
-
|
|
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 —
|
|
503
|
+
### Phase 8 — Write Context and Report
|
|
225
504
|
|
|
226
|
-
**
|
|
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: [
|
|
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
|
-
|
|
516
|
+
Display completion report:
|
|
233
517
|
|
|
234
|
-
**Step 3.** Show completion report:
|
|
235
518
|
```
|
|
236
|
-
|
|
519
|
+
@validate-page Complete
|
|
237
520
|
─────────────────────────────────────────
|
|
238
521
|
Page : [page_name]
|
|
239
|
-
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
|