codeninja 2.0.0 → 3.2.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 (46) hide show
  1. package/README.md +122 -251
  2. package/agent/global-agent.md +8 -0
  3. package/cli.js +248 -223
  4. package/commands/debug.workflow.md +94 -0
  5. package/commands/explain.workflow.md +59 -0
  6. package/commands/optimize.workflow.md +124 -0
  7. package/commands/review.workflow.md +85 -0
  8. package/ide/antigravity/.agents/personas/database-architect.md +249 -0
  9. package/ide/antigravity/.agents/personas/global-orchestrator.md +144 -0
  10. package/ide/antigravity/.agents/personas/nodejs-backend.md +250 -0
  11. package/ide/antigravity/.agents/personas/reactjs-frontend.md +179 -0
  12. package/ide/antigravity/.agents/skills/api-builder/SKILL.md +179 -0
  13. package/ide/antigravity/.agents/skills/code-intelligence/SKILL.md +184 -0
  14. package/ide/antigravity/.agents/skills/database/SKILL.md +165 -0
  15. package/ide/antigravity/.agents/skills/mcp-and-context/SKILL.md +111 -0
  16. package/ide/antigravity/.agents/skills/reactjs/SKILL.md +211 -0
  17. package/ide/antigravity/.agents/workflows/codeninja-api.md +111 -0
  18. package/ide/antigravity/.agents/workflows/codeninja-audit.md +81 -0
  19. package/ide/antigravity/.agents/workflows/codeninja-db-create.md +124 -0
  20. package/ide/antigravity/.agents/workflows/codeninja-db-drop.md +87 -0
  21. package/ide/antigravity/.agents/workflows/codeninja-db-index.md +70 -0
  22. package/ide/antigravity/.agents/workflows/codeninja-db-modify.md +106 -0
  23. package/ide/antigravity/.agents/workflows/codeninja-db-seed.md +76 -0
  24. package/ide/antigravity/.agents/workflows/codeninja-db-sync.md +70 -0
  25. package/ide/antigravity/.agents/workflows/codeninja-debug.md +82 -0
  26. package/ide/antigravity/.agents/workflows/codeninja-design.md +54 -0
  27. package/ide/antigravity/.agents/workflows/codeninja-explain.md +40 -0
  28. package/ide/antigravity/.agents/workflows/codeninja-init.md +336 -0
  29. package/ide/antigravity/.agents/workflows/codeninja-integrate-api.md +336 -0
  30. package/ide/antigravity/.agents/workflows/codeninja-modularize.md +216 -0
  31. package/ide/antigravity/.agents/workflows/codeninja-optimize.md +84 -0
  32. package/ide/antigravity/.agents/workflows/codeninja-refactor.md +68 -0
  33. package/ide/antigravity/.agents/workflows/codeninja-review.md +70 -0
  34. package/ide/antigravity/.agents/workflows/codeninja-sync.md +183 -0
  35. package/ide/antigravity/.agents/workflows/codeninja-test.md +61 -0
  36. package/ide/antigravity/.agents/workflows/codeninja-validate-page.md +250 -0
  37. package/ide/cursor/.cursor/mcp.json +8 -0
  38. package/ide/cursor/.cursor/rules/01-global-orchestrator.mdc +63 -0
  39. package/ide/cursor/.cursor/rules/02-mcp-and-context.mdc +38 -0
  40. package/ide/cursor/.cursor/rules/03-api-builder.mdc +124 -0
  41. package/ide/cursor/.cursor/rules/04-database.mdc +90 -0
  42. package/ide/cursor/.cursor/rules/05-reactjs.mdc +147 -0
  43. package/ide/cursor/.cursor/rules/06-code-intelligence.mdc +112 -0
  44. package/ide/vscode/.github/copilot-instructions.md +399 -0
  45. package/ide/vscode/.vscode/mcp.json +9 -0
  46. package/package.json +24 -23
@@ -0,0 +1,336 @@
1
+ ---
2
+ slash_command: /codeninja:init
3
+ personas: [global-orchestrator, nodejs-backend, reactjs-frontend, database-architect]
4
+ skills: [mcp-and-context, api-builder, reactjs, database]
5
+ description: >
6
+ Bootstrap a new NodeJS service, ReactJS app, or database-only project.
7
+ Collects project information first, then service details, then generates
8
+ ALL files in one pass after a single confirmation.
9
+ ---
10
+
11
+ # /codeninja:init
12
+
13
+ ## Before Running
14
+ 1. Call `context_check_stale` — resolve any stale scratchpad keys first
15
+ 2. Call `context_read` — if context_version > 0, load existing context
16
+ 3. Call `service_scan` — detect existing service directories
17
+
18
+ ## Execution — Full Step-by-Step
19
+
20
+ ### Phase 0 — Project Information (runs ONCE per repo)
21
+ Check `context.project_info` — if already populated, skip Phase 0 entirely.
22
+
23
+ **0a.** Ask: "Do you have a project information document or requirement document?"
24
+ - Options: Yes (provide URL or paste) / No
25
+ - Store: `context.project_info.has_doc`, `.doc_url` or `.doc_content`, `.from_doc`
26
+
27
+ **0b.** Ask: "Do you have a scope of work document?"
28
+ - Options: Yes (provide URL or paste) / No
29
+ - Store: `context.project_info.has_sow`, `.sow_url` or `.sow_content`, `.from_sow`
30
+
31
+ **0c.** Ask: "Do you have a Figma design URL?"
32
+ - Options: Yes (provide URL) / No
33
+ - Store: `context.project_info.has_figma`, `.figma_url`, `.from_figma`
34
+
35
+ After all three: synthesize `context.project_info.summary` (150–200 words covering
36
+ what the project does, key features, entities/modules, tech preferences,
37
+ third-party integrations) and `context.project_info.detected_entities[]`.
38
+ Use this summary for all suggestions throughout the session.
39
+
40
+ ---
41
+
42
+ ### Phase 1 — Init Mode and Project Type
43
+
44
+ **Step 0.** Ask: "How would you like to set up this service?"
45
+ - Option 1: Fast setup — auto-generate secure values, only 9 essential questions
46
+ - Option 2: Manual setup — walk through every value one at a time (22 questions)
47
+ - Store: `context.current_init.init_mode` ("fast" | "manual")
48
+
49
+ **Step 1.** Ask: "What are you initializing?"
50
+ - Option 1: NodeJS API service
51
+ - Option 2: ReactJS frontend app
52
+ - Option 3: Database only (no service code)
53
+ - Store: `context.current_init.project_type` ("nodejs" | "reactjs" | "database-only")
54
+
55
+ **Step 1b.** If `project_type == nodejs`:
56
+ - Ask: "What type of client will consume this API?"
57
+ - Options: ReactJS web app / Mobile app
58
+ - Store: `context.current_init.client_type` ("reactjs" | "app")
59
+ - Ask: "Does this service use encrypted transport (AES response wrapping)?"
60
+ - Options: Yes / No
61
+ - Store: `context.current_init.encrypted_transport` (true | false)
62
+ - Ask: "Which languages should this service support?"
63
+ - Show options + allow multi-select (en always included)
64
+ - Store: `context.current_init.supported_languages[]`
65
+
66
+ **Step 2.** If `project_type == reactjs`:
67
+ - Check `context.services` for any NodeJS services.
68
+ If none exist → ABORT: "A ReactJS service requires an existing NodeJS backend.
69
+ Run /codeninja:init first to create a NodeJS service."
70
+ - Ask: "Which NodeJS service should this ReactJS app connect to?"
71
+ - List available NodeJS services from `context.services`
72
+ - Store: `context.current_init.linked_service`
73
+ - Store: `context.current_init.linked_service_port` (read from context)
74
+ - Auto-inherit from the linked service (never ask user):
75
+ - `context.current_init.encryption_key`
76
+ - `context.current_init.encryption_iv`
77
+ - `context.current_init.api_key`
78
+ - Skip Phase 2 (no DB), skip Phase 4 (no package questions), skip Phase 5 (no security questions)
79
+ - Jump directly to Phase 3
80
+
81
+ **Step 3.** If `project_type == nodejs` or `database-only` → continue to Phase 2.
82
+
83
+ ---
84
+
85
+ ### Phase 2 — Database (nodejs and database-only only)
86
+
87
+ **Step 4.** Ask: "Which database type?"
88
+ - Options: PostgreSQL / MySQL / MongoDB
89
+ - If `context.db.type` already exists → ask: use existing or configure new?
90
+ - Store: `context.db.type`
91
+
92
+ **Step 5.** If `init_mode == manual`:
93
+ - Ask: "Database name?", "Database host?", "Database port?", "Database user?" (grouped display)
94
+ - Store: `context.db.name`, `context.db.host`, `context.db.port`, `context.db.user`
95
+
96
+ If `init_mode == fast`:
97
+ - Ask: "Database name?" only
98
+ - Ask: "Database user?" only
99
+ - (host and port auto-set by generate-fast-defaults)
100
+ - Store: `context.db.name`, `context.db.user`
101
+
102
+ **Step 9.** Delegate to database-agent — generate database folder at REPOSITORY ROOT
103
+ (not inside the service folder — always a sibling).
104
+
105
+ Check if `<repo_root>/database/<db_type>/` already exists:
106
+ - If NOT exists → generate:
107
+ - `database/<db_type>/migrations/` (empty)
108
+ - `database/<db_type>/create-schema.sql`
109
+ - `database/<db_type>/setup-database.sh`
110
+ - `database/<db_type>/setup-database.ps1`
111
+ - `database/<db_type>/reset-database.sh`
112
+ - `database/<db_type>/seeds/` (with .gitkeep)
113
+ - `database/README.md`
114
+ - Then generate `tbl_user_deviceinfo` migration (nodejs projects only)
115
+ - If ALREADY exists → skip generation entirely. Inform user.
116
+ Then check if `migrations/1-setup-tbl-user-deviceinfo.sql` exists:
117
+ - If NO and project_type == nodejs → generate it
118
+ - Otherwise → skip
119
+
120
+ **Step 10.** Inform: "Database folder initialized. You can run /codeninja:db:create to add tables."
121
+
122
+ **Step 11.** If `project_type == database-only` → skip Phases 3–5, jump to Phase 6.
123
+
124
+ ---
125
+
126
+ ### Phase 3 — Service Identity
127
+
128
+ **Step 12.** Ask: "Service name?"
129
+ - Must be unique across `context.services`
130
+ - Agent suggests based on `context.project_info.detected_entities`
131
+ - Store: `context.current_init.service_name`
132
+
133
+ **Step 13.** If `init_mode == manual`:
134
+ - Ask: "Port number?" (must not conflict with existing services)
135
+ - Store: `context.current_init.port`
136
+
137
+ **Step 14.** Ask: "Short description of this service?"
138
+ - Store: `context.current_init.description`
139
+
140
+ ---
141
+
142
+ ### Phase 4 — Package Info (nodejs + manual mode only)
143
+
144
+ **Step 15.** If `init_mode == manual`:
145
+ - Ask: "Package name?" (default: service_name)
146
+ - Store: `context.current_init.package_name`
147
+
148
+ **Step 16.** If `init_mode == manual`:
149
+ - Ask: "Author name?"
150
+ - Store: `context.current_init.author`
151
+
152
+ ---
153
+
154
+ ### Phase 5 — Runtime Config (nodejs + manual mode only)
155
+
156
+ **Step 17.** If `init_mode == manual`:
157
+ - Ask: "API key?" (auto-suggest 32 random chars)
158
+ - Store: `context.current_init.api_key`
159
+
160
+ **Step 18.** If `init_mode == manual`:
161
+ - Ask: "Encryption key?" (must be exactly 32 characters)
162
+ - Store: `context.current_init.encryption_key`
163
+
164
+ **Step 19.** If `init_mode == manual`:
165
+ - Auto-set `encryption_iv` = first 16 chars of `encryption_key`
166
+ - Show derived value: "Encryption IV auto-derived: [first 16 chars of key]"
167
+ - Store: `context.current_init.encryption_iv`
168
+
169
+ **Step 20.** If `init_mode == manual`:
170
+ - Ask: "Redis host and port?" (grouped)
171
+ - Store: `context.current_init.redis_host`, `context.current_init.redis_port`
172
+
173
+ ---
174
+
175
+ ### Phase 5b — Auto-populate Defaults (fast mode, nodejs only)
176
+
177
+ If `init_mode == fast` AND `project_type == nodejs`:
178
+ - `context.db.host` → "localhost" (if not set)
179
+ - `context.db.port` → 5432 / 3306 / 27017 based on db type
180
+ - Port: scan all `context.services` ports → highest + 1 → minimum 1001
181
+ - `package_name` → same as `service_name`
182
+ - `author` → ""
183
+ - `api_key` → 32 cryptographically random alphanumeric chars
184
+ - `encryption_key` → exactly 32 cryptographically random alphanumeric chars
185
+ - `encryption_iv` → first 16 chars of `encryption_key` (always derived, never random)
186
+ - `redis_host` → "localhost"
187
+ - `redis_port` → 6379
188
+ - All done silently — values shown in summary for review
189
+
190
+ ---
191
+
192
+ ### Phase 6 — Single Confirmation, Then Generate Everything
193
+
194
+ **Step 22.** Show init summary — ALL collected values including auto-generated ones.
195
+ Run pre-generation validation BEFORE displaying:
196
+ - BLOCKER: service name conflict
197
+ - BLOCKER: port conflict
198
+ - BLOCKER: encryption_key not 32 chars (nodejs)
199
+ - BLOCKER: encryption_iv not 16 chars (nodejs)
200
+ - BLOCKER: required fields missing
201
+ - BLOCKER: no linked service (reactjs)
202
+ - WARNING: port below 1024
203
+ - WARNING: service name has uppercase
204
+
205
+ Resolve all BLOCKERs interactively before showing summary.
206
+ Show the 5-wave generation plan.
207
+ Ask ONE question: "Confirm and generate all files? (yes / no / change a value)"
208
+ - If yes → generate immediately, NO further confirmations
209
+ - If no → abort
210
+ - If change → re-run specific ask-* task → re-validate → return to summary
211
+
212
+ ---
213
+
214
+ ### Phase 6b — Generation (nodejs)
215
+
216
+ Read `nodejs-agent.md` persona. Execute ALL waves. Do NOT pause between waves.
217
+
218
+ **Wave 1 — Foundation** (all simultaneously):
219
+ - `package.json`
220
+ - `.env` and `.env.example`
221
+ - `.gitignore`
222
+ - `README.md`
223
+ - `config/constants.js`
224
+ - `config/template.js`
225
+ - `logger/logging.js`
226
+ - `utilities/encryption.js`
227
+ - `languages/<lang>.js` for each in supported_languages[]
228
+ - `enc_dec.html` (if client_type == reactjs) OR `enc_dec.php` (if client_type == app)
229
+ - `pem/` directory with .gitkeep
230
+ - `images/` directory with .gitkeep
231
+ - `logger/logs/` directory with .gitkeep
232
+
233
+ **Wave 2 — Infrastructure**:
234
+ - `config/database.js`
235
+ - `utilities/ioRedis.js`
236
+ - `utilities/response.js`
237
+
238
+ **Wave 3 — Service Layer**:
239
+ - `config/common.js`
240
+ - `utilities/validator.js`
241
+ - `utilities/notification.js`
242
+ - `middleware/rateLimiter.js`
243
+
244
+ **Wave 4 — Middleware and Business Layer** (all simultaneously):
245
+ - `middleware/headerValidator.js`
246
+ - `modules/v1/<ServiceName>/route.js`
247
+ - `modules/v1/<ServiceName>/<service>_model.js`
248
+ - `document/v1/swagger_doc.json` (skeleton)
249
+
250
+ **Wave 5 — Orchestration Layer**:
251
+ - `modules/v1/route_manager.js`
252
+ - `app.js`
253
+
254
+ **Wave 6 — Docker** (post-completion):
255
+ - `Dockerfile`
256
+ - `.dockerignore`
257
+
258
+ COMPLETION GATE — before proceeding, verify these exist on disk:
259
+ - `<service_name>/app.js`
260
+ - `<service_name>/modules/v1/route_manager.js`
261
+ - `<service_name>/modules/v1/<ServiceName>/route.js`
262
+ - `<service_name>/modules/v1/<ServiceName>/<service>_model.js`
263
+ - `<service_name>/document/v1/swagger_doc.json`
264
+ If any missing → generate now before continuing.
265
+
266
+ ---
267
+
268
+ ### Phase 6c — Generation (reactjs)
269
+
270
+ Read `reactjs-frontend.md` persona. Execute ALL waves.
271
+
272
+ **Wave 1 — Foundation**:
273
+ - `package.json`
274
+ - `.env` and `.env.example`
275
+ - `.gitignore`
276
+ - `README.md`
277
+ - `public/index.html`
278
+ - `public/assets/css/style.css`
279
+ - `public/robots.txt`
280
+ - `public/favicon.ico` (placeholder note)
281
+ - `.htaccess` (root)
282
+ - `public/.htaccess`
283
+
284
+ **Wave 2 — API Layer**:
285
+ - `src/api/apiClient.js`
286
+ - `src/api/apiHandler.js`
287
+
288
+ **Wave 3 — Application Shell**:
289
+ - `src/pages/Welcome/index.jsx`
290
+ - `src/pages/Welcome/Welcome.module.css`
291
+ - `src/App.jsx`
292
+ - `src/index.jsx`
293
+ - `src/components/` (empty dir with .gitkeep)
294
+
295
+ **Wave 4 — Docker**:
296
+ - `Dockerfile`
297
+ - `nginx.conf`
298
+ - `.dockerignore`
299
+
300
+ COMPLETION GATE — verify before proceeding:
301
+ - `<service_name>/public/index.html`
302
+ - `<service_name>/src/api/apiClient.js`
303
+ - `<service_name>/src/api/apiHandler.js`
304
+ - `<service_name>/src/App.jsx`
305
+ - `<service_name>/src/index.jsx`
306
+ - `<service_name>/.env`
307
+
308
+ ---
309
+
310
+ ### Phase 7 — Post-Generation Steps
311
+
312
+ **Step 23b.** Generate IDE configs (first init only):
313
+ - Write `.vscode/mcp.json`
314
+ - Write `.cursor/mcp.json`
315
+ - Print Claude Desktop config snippet for manual addition
316
+
317
+ **Step 23c.** Generate docker-compose.yml:
318
+ - If does not exist → generate complete file at repo root
319
+ - If exists → update by adding the new service
320
+ - Also generate/update `.env.docker` and `.env.docker.example`
321
+
322
+ **Step 24.** Call `context_write`:
323
+ - Set `project_name` = service_name (top-level)
324
+ - Set `initialized_at` = ISO now
325
+ - Set `last_command` = "initialize-project"
326
+ - Append new service to `context.services[<service_name>]`
327
+ - For reactjs: store `linked_service` and `linked_service_port`
328
+ - Update `context.project_info` with `initialized_at`
329
+ - Clear `context.current_init`
330
+
331
+ **Step 25.** Call `context_clear_scratchpad` with keys: ["current_init"]
332
+
333
+ **Step 26.** Show final summary:
334
+ - List all files created per wave
335
+ - Show startup commands
336
+ - Offer next steps: /codeninja:db:create, /codeninja:api, /codeninja:design
@@ -0,0 +1,336 @@
1
+ ---
2
+ slash_command: /codeninja:integrate-api
3
+ personas: [global-orchestrator, reactjs-frontend]
4
+ skills: [mcp-and-context, reactjs, api-builder]
5
+ description: >
6
+ Wire a ReactJS page's forms and action buttons to backend API calls
7
+ through apiHandler.js and apiClient.js. Adds loading states, error
8
+ display, and success feedback. Never touches validation or layout.
9
+ ---
10
+
11
+ # /codeninja:integrate-api
12
+
13
+ ## Before Running
14
+ 1. Call `context_check_stale`
15
+ 2. Call `context_read` — load `context.services` and `context.api_routes`
16
+ 3. Call `service_scan` — verify services on disk
17
+
18
+ ## Rules
19
+ - Target ONE specific page per run
20
+ - NEVER modify layout, CSS, or validation logic
21
+ - ALWAYS use `apiHandler.js` for API calls — never call `axiosClient` directly from a page
22
+ - ALWAYS add handler functions to `apiHandler.js` if the needed function doesn't exist
23
+ - NEVER hardcode API endpoints in page files
24
+ - ONE confirmation before any file is written
25
+ - After writing files → call `context_write`
26
+
27
+ ---
28
+
29
+ ## Execution — Full Step-by-Step
30
+
31
+ ### Phase 1 — Target Selection
32
+
33
+ **Step 1.** Ask: "Which ReactJS service?" (list ReactJS services from `context.services`)
34
+ - Store: `context.current_action.service_name`
35
+
36
+ **Step 2.** Ask: "Which page path?" (e.g. `src/pages/Login/index.jsx`)
37
+ - Store: `context.current_action.page_path`, `context.current_action.page_name`
38
+
39
+ **Step 3.** Ask: "What scope of integration?"
40
+ 1. All forms and buttons on this page
41
+ 2. Specific form or button only
42
+ - Store: `context.current_action.api_integration_scope`
43
+ - If specific → ask: "Which form or button?" → Store: `context.current_action.api_integration_target`
44
+
45
+ ---
46
+
47
+ ### Phase 2 — Read Service Context
48
+
49
+ Read from context:
50
+ - `context.services[<service_name>].linked_service` → the NodeJS backend
51
+ - `context.api_routes` filtered by linked NodeJS service → available API routes
52
+
53
+ Read from disk:
54
+ - Full content of `context.current_action.page_path` (target page)
55
+ - Full content of `src/api/apiHandler.js`
56
+ - Full content of `src/api/apiClient.js` (read-only reference)
57
+
58
+ ---
59
+
60
+ ### Phase 3 — Scan the Page for Integration Points
61
+
62
+ #### 3a — Forms
63
+ For each `<form>` found:
64
+ - Note the form's apparent purpose (infer from field names, labels, heading text)
65
+ - Check if `onSubmit` is already connected
66
+ - Check if form imports anything from `apiHandler.js`
67
+
68
+ #### 3b — Action Buttons
69
+ For each `<button>` NOT a submit button:
70
+ - Check if `onClick` is connected
71
+ - Infer action from button text: Delete/Remove → delete, Edit/Update → update,
72
+ Save → save/create, Logout → logout, Load more/Next → pagination, Export/Download → export
73
+
74
+ #### 3c — Existing API Calls
75
+ Detect any existing API calls:
76
+ - Imports from `apiHandler.js` (existing — note them)
77
+ - Direct `axiosClient` calls (flag — should be moved to apiHandler)
78
+ - `fetch(` or `axios.` direct calls (flag — should be moved)
79
+
80
+ If scope == "specific" → focus only on the matching form/button.
81
+
82
+ ---
83
+
84
+ ### Phase 4 — Match Forms to API Routes
85
+
86
+ For each integration point, determine which API route it should call:
87
+
88
+ 1. **Exact match** — `apiHandler.js` function already exists for this purpose → use as-is
89
+ 2. **Route match** — matching route exists in `context.api_routes` for linked backend →
90
+ new handler function will be added to `apiHandler.js`
91
+ 3. **No match** — no suitable route exists yet → scaffold with TODO placeholder
92
+
93
+ Record: `{ form_purpose, handler_function_name, route_path, route_method, match_type }`
94
+
95
+ ---
96
+
97
+ ### Phase 5 — Design the Integration
98
+
99
+ For each integration point:
100
+
101
+ #### State requirements
102
+ - `loading` state — boolean, false by default
103
+ - `error` state — string or null
104
+ - Response data state based on form type:
105
+ - Auth forms → no data state (session stored by apiHandler)
106
+ - List/search → `data` state (array, [])
107
+ - Single item → `item` state (object, null)
108
+ - Delete/update → list refresh or redirect
109
+
110
+ #### Submit handler patterns
111
+
112
+ **Auth / destructive (login, delete, logout):**
113
+ ```jsx
114
+ const handle[Action] = async (e) => {
115
+ e?.preventDefault();
116
+ setLoading(true);
117
+ setError(null);
118
+ const res = await [handlerFunction]({ ...formData });
119
+ setLoading(false);
120
+ if (res?.code === 1) {
121
+ navigate('/dashboard');
122
+ } else {
123
+ setError(res?.message || 'Something went wrong. Please try again.');
124
+ }
125
+ };
126
+ ```
127
+
128
+ **Data fetch / load (get list, get details):**
129
+ ```jsx
130
+ const fetch[Resource] = async () => {
131
+ setLoading(true);
132
+ setError(null);
133
+ const res = await [handlerFunction]({ ...filters });
134
+ setLoading(false);
135
+ if (res?.code === 1) {
136
+ setData(res?.data ?? []);
137
+ } else {
138
+ setError(res?.message || 'Failed to load data. Please try again.');
139
+ }
140
+ };
141
+ // Also add: useEffect(() => { fetch[Resource](); }, []);
142
+ ```
143
+
144
+ **Create / Update (form submit):**
145
+ ```jsx
146
+ const handle[Action] = async (e) => {
147
+ e?.preventDefault();
148
+ setLoading(true);
149
+ setError(null);
150
+ const res = await [handlerFunction](formData);
151
+ setLoading(false);
152
+ if (res?.code === 1) {
153
+ setSuccessMsg(res?.message || '[Action] successful.');
154
+ resetForm();
155
+ } else {
156
+ setError(res?.message || 'Something went wrong. Please try again.');
157
+ }
158
+ };
159
+ ```
160
+
161
+ #### Loading feedback
162
+ ```jsx
163
+ <button type="submit" disabled={loading}>
164
+ {loading ? 'Please wait...' : '[Original Button Text]'}
165
+ </button>
166
+ ```
167
+
168
+ #### Error display (above submit button)
169
+ ```jsx
170
+ {error && <p className={styles.apiError}>{error}</p>}
171
+ ```
172
+
173
+ #### Success display (for non-navigating actions)
174
+ ```jsx
175
+ {successMsg && <p className={styles.successMsg}>{successMsg}</p>}
176
+ ```
177
+
178
+ ---
179
+
180
+ ### Phase 6 — Show Integration Plan
181
+
182
+ ```
183
+ ┌──────────────────────────────────────────────────────┐
184
+ │ API INTEGRATION PLAN │
185
+ ├──────────────────────────────────────────────────────┤
186
+ │ Page : [page_name] │
187
+ │ Service : [service_name] │
188
+ │ Backend : [linked_service] (port [port]) │
189
+ ├──────────────────────────────────────────────────────┤
190
+ │ INTEGRATIONS │
191
+ │ │
192
+ │ ① Login Form → webLogin() │
193
+ │ Route : POST /login │
194
+ │ Handler : webLogin (already in apiHandler.js) │
195
+ │ State add: loading, error │
196
+ │ Pattern : auth/destructive │
197
+ │ On success: navigate('/dashboard') │
198
+ │ │
199
+ │ ② Delete Button → deleteUser() │
200
+ │ Route : DELETE /users/:id │
201
+ │ Handler : deleteUser (NEW → will add to │
202
+ │ apiHandler.js) │
203
+ │ Pattern : destructive │
204
+ │ On success: refresh user list │
205
+ ├──────────────────────────────────────────────────────┤
206
+ │ GAPS (no backend route yet) │
207
+ │ ③ Export CSV Button — no route exists yet │
208
+ │ Will add TODO placeholder │
209
+ ├──────────────────────────────────────────────────────┤
210
+ │ FILES TO MODIFY │
211
+ │ → src/pages/[PageName]/index.jsx │
212
+ │ → src/pages/[PageName]/[PageName].module.css │
213
+ │ → src/api/apiHandler.js ([n] new function(s)) │
214
+ └──────────────────────────────────────────────────────┘
215
+ ```
216
+
217
+ Ask: "Apply this integration plan? (yes / no / adjust)"
218
+
219
+ ---
220
+
221
+ ### Phase 7 — Apply Integration
222
+
223
+ #### 7a — Update apiHandler.js (new functions only)
224
+
225
+ For each `match_type == "new_handler"`:
226
+ Append to `src/api/apiHandler.js`:
227
+ ```jsx
228
+ /**
229
+ * [Route description from context.api_routes or inferred].
230
+ *
231
+ * @param {Object} data - Request payload.
232
+ * @returns {Promise<Object>} API response.
233
+ */
234
+ export async function [functionName](data) {
235
+ const res = await axiosClient.[method]('[route_path]', data);
236
+ return res;
237
+ }
238
+ ```
239
+ Use correct method: `.get`, `.post`, `.put`, `.patch`, `.delete`.
240
+ For DELETE/GET with ID: `axiosClient.delete(\`/users/${data.id}\`)`
241
+
242
+ For `match_type == "no_route"`:
243
+ ```jsx
244
+ /**
245
+ * TODO: Connect to backend route once created via /codeninja:api.
246
+ */
247
+ export async function [functionName](data) {
248
+ console.warn('[functionName] is not yet connected to a backend route.');
249
+ return { code: 0, message: 'API not connected yet.' };
250
+ }
251
+ ```
252
+
253
+ #### 7b — Update the page file (surgical edits only)
254
+
255
+ 1. Add imports for new apiHandler functions (only new ones):
256
+ ```jsx
257
+ import { webLogin, deleteUser } from '../../api/apiHandler';
258
+ ```
259
+ Add `useNavigate` if any handler navigates:
260
+ ```jsx
261
+ import { useNavigate } from 'react-router-dom';
262
+ ```
263
+
264
+ 2. Add state declarations inside component:
265
+ ```jsx
266
+ const [loading, setLoading] = React.useState(false);
267
+ const [error, setError] = React.useState(null);
268
+ const navigate = useNavigate(); // if navigate is used
269
+ ```
270
+
271
+ 3. Add handler functions just before the return statement.
272
+ If a handler already exists → add API call logic inside it, don't create duplicate.
273
+
274
+ 4. Wire form `onSubmit` / button `onClick`:
275
+ - `<form onSubmit={handle[Action]}>`
276
+ - `<button onClick={handle[Action]}>`
277
+
278
+ 5. Add `disabled={loading}` and conditional button text to submit buttons.
279
+
280
+ 6. Add error and success display elements adjacent to submit buttons.
281
+
282
+ 7. Add `useEffect` for data-fetching handlers (load on mount).
283
+
284
+ #### 7c — Update page CSS module
285
+ Add `.apiError` and `.successMsg` classes (if not already present):
286
+ ```css
287
+ .apiError {
288
+ color: #dc3545;
289
+ font-size: 0.875rem;
290
+ margin-bottom: 0.75rem;
291
+ padding: 0.5rem 0.75rem;
292
+ background-color: #fff5f5;
293
+ border: 1px solid #f5c6cb;
294
+ border-radius: 4px;
295
+ }
296
+
297
+ .successMsg {
298
+ color: #155724;
299
+ font-size: 0.875rem;
300
+ margin-bottom: 0.75rem;
301
+ padding: 0.5rem 0.75rem;
302
+ background-color: #d4edda;
303
+ border: 1px solid #c3e6cb;
304
+ border-radius: 4px;
305
+ }
306
+ ```
307
+
308
+ ---
309
+
310
+ ### Phase 8 — Finalize
311
+
312
+ **Step 1.** Call `context_write`:
313
+ - Append to `context.services[<service_name>].integrated_pages`:
314
+ `{ page, integrations: [{ form_purpose, handler, route }], timestamp: ISO now }`
315
+ - Set `last_command` = "integrate-api"
316
+ - Append to `change_log`
317
+
318
+ **Step 2.** Call `context_clear_scratchpad` with keys: ["current_action"]
319
+
320
+ **Step 3.** Show completion report:
321
+ ```
322
+ /codeninja:integrate-api Complete
323
+ ─────────────────────────────────────────
324
+ Page : [page_name]
325
+ Integrations : [n] connected, [n] placeholders
326
+ ─────────────────────────────────────────
327
+ ✓ src/pages/[PageName]/index.jsx — updated
328
+ ✓ src/pages/[PageName]/[PageName].module.css — error/success styles added
329
+ [✓ src/api/apiHandler.js — [n] new function(s) added]
330
+ ─────────────────────────────────────────
331
+ [If gaps exist:]
332
+ ⚠ [n] action(s) have no backend route yet.
333
+ Placeholders added. Run /codeninja:api to create backend routes,
334
+ then re-run /codeninja:integrate-api to connect them.
335
+ ─────────────────────────────────────────
336
+ ```