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.
- package/README.md +122 -251
- package/agent/global-agent.md +8 -0
- package/cli.js +248 -223
- package/commands/debug.workflow.md +94 -0
- package/commands/explain.workflow.md +59 -0
- package/commands/optimize.workflow.md +124 -0
- package/commands/review.workflow.md +85 -0
- package/ide/antigravity/.agents/personas/database-architect.md +249 -0
- package/ide/antigravity/.agents/personas/global-orchestrator.md +144 -0
- package/ide/antigravity/.agents/personas/nodejs-backend.md +250 -0
- package/ide/antigravity/.agents/personas/reactjs-frontend.md +179 -0
- package/ide/antigravity/.agents/skills/api-builder/SKILL.md +179 -0
- package/ide/antigravity/.agents/skills/code-intelligence/SKILL.md +184 -0
- package/ide/antigravity/.agents/skills/database/SKILL.md +165 -0
- package/ide/antigravity/.agents/skills/mcp-and-context/SKILL.md +111 -0
- package/ide/antigravity/.agents/skills/reactjs/SKILL.md +211 -0
- package/ide/antigravity/.agents/workflows/codeninja-api.md +111 -0
- package/ide/antigravity/.agents/workflows/codeninja-audit.md +81 -0
- package/ide/antigravity/.agents/workflows/codeninja-db-create.md +124 -0
- package/ide/antigravity/.agents/workflows/codeninja-db-drop.md +87 -0
- package/ide/antigravity/.agents/workflows/codeninja-db-index.md +70 -0
- package/ide/antigravity/.agents/workflows/codeninja-db-modify.md +106 -0
- package/ide/antigravity/.agents/workflows/codeninja-db-seed.md +76 -0
- package/ide/antigravity/.agents/workflows/codeninja-db-sync.md +70 -0
- package/ide/antigravity/.agents/workflows/codeninja-debug.md +82 -0
- package/ide/antigravity/.agents/workflows/codeninja-design.md +54 -0
- package/ide/antigravity/.agents/workflows/codeninja-explain.md +40 -0
- package/ide/antigravity/.agents/workflows/codeninja-init.md +336 -0
- package/ide/antigravity/.agents/workflows/codeninja-integrate-api.md +336 -0
- package/ide/antigravity/.agents/workflows/codeninja-modularize.md +216 -0
- package/ide/antigravity/.agents/workflows/codeninja-optimize.md +84 -0
- package/ide/antigravity/.agents/workflows/codeninja-refactor.md +68 -0
- package/ide/antigravity/.agents/workflows/codeninja-review.md +70 -0
- package/ide/antigravity/.agents/workflows/codeninja-sync.md +183 -0
- package/ide/antigravity/.agents/workflows/codeninja-test.md +61 -0
- package/ide/antigravity/.agents/workflows/codeninja-validate-page.md +250 -0
- package/ide/cursor/.cursor/mcp.json +8 -0
- package/ide/cursor/.cursor/rules/01-global-orchestrator.mdc +63 -0
- package/ide/cursor/.cursor/rules/02-mcp-and-context.mdc +38 -0
- package/ide/cursor/.cursor/rules/03-api-builder.mdc +124 -0
- package/ide/cursor/.cursor/rules/04-database.mdc +90 -0
- package/ide/cursor/.cursor/rules/05-reactjs.mdc +147 -0
- package/ide/cursor/.cursor/rules/06-code-intelligence.mdc +112 -0
- package/ide/vscode/.github/copilot-instructions.md +399 -0
- package/ide/vscode/.vscode/mcp.json +9 -0
- 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
|
+
```
|