codeninja 3.1.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/ide/antigravity/.agents/personas/global-orchestrator.md +103 -84
- package/ide/antigravity/.agents/workflows/codeninja-api.md +98 -15
- package/ide/antigravity/.agents/workflows/codeninja-audit.md +69 -11
- package/ide/antigravity/.agents/workflows/codeninja-db-create.md +118 -5
- package/ide/antigravity/.agents/workflows/codeninja-db-drop.md +81 -5
- package/ide/antigravity/.agents/workflows/codeninja-db-index.md +64 -5
- package/ide/antigravity/.agents/workflows/codeninja-db-modify.md +100 -5
- package/ide/antigravity/.agents/workflows/codeninja-db-seed.md +70 -4
- package/ide/antigravity/.agents/workflows/codeninja-db-sync.md +64 -6
- package/ide/antigravity/.agents/workflows/codeninja-debug.md +76 -6
- package/ide/antigravity/.agents/workflows/codeninja-design.md +45 -12
- package/ide/antigravity/.agents/workflows/codeninja-explain.md +35 -6
- package/ide/antigravity/.agents/workflows/codeninja-init.md +329 -22
- package/ide/antigravity/.agents/workflows/codeninja-integrate-api.md +334 -9
- package/ide/antigravity/.agents/workflows/codeninja-modularize.md +214 -9
- package/ide/antigravity/.agents/workflows/codeninja-optimize.md +78 -7
- package/ide/antigravity/.agents/workflows/codeninja-refactor.md +58 -13
- package/ide/antigravity/.agents/workflows/codeninja-review.md +64 -6
- package/ide/antigravity/.agents/workflows/codeninja-sync.md +172 -12
- package/ide/antigravity/.agents/workflows/codeninja-test.md +51 -9
- package/ide/antigravity/.agents/workflows/codeninja-validate-page.md +248 -9
- package/ide/cursor/.cursor/rules/01-global-orchestrator.mdc +35 -32
- package/ide/cursor/.cursor/rules/03-api-builder.mdc +100 -50
- package/ide/cursor/.cursor/rules/04-database.mdc +73 -70
- package/ide/cursor/.cursor/rules/05-reactjs.mdc +133 -69
- package/ide/vscode/.github/copilot-instructions.md +304 -190
- package/package.json +1 -1
|
@@ -1,11 +1,336 @@
|
|
|
1
1
|
---
|
|
2
|
-
slash_command:
|
|
2
|
+
slash_command: /codeninja:integrate-api
|
|
3
3
|
personas: [global-orchestrator, reactjs-frontend]
|
|
4
|
-
skills: [mcp-and-context, reactjs]
|
|
5
|
-
description:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
+
```
|
|
@@ -1,11 +1,216 @@
|
|
|
1
1
|
---
|
|
2
|
-
slash_command:
|
|
2
|
+
slash_command: /codeninja:modularize
|
|
3
3
|
personas: [global-orchestrator, reactjs-frontend]
|
|
4
|
-
skills: [mcp-and-context, reactjs]
|
|
5
|
-
description:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
skills: [mcp-and-context, reactjs, code-intelligence]
|
|
5
|
+
description: >
|
|
6
|
+
Scan ReactJS pages, extract repeated layout blocks into reusable components
|
|
7
|
+
under src/components/, and update each page to import and use them.
|
|
8
|
+
Layout structure only — never touches business logic, state, or API calls.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# /codeninja:modularize
|
|
12
|
+
|
|
13
|
+
## Before Running
|
|
14
|
+
1. Call `context_check_stale`
|
|
15
|
+
2. Call `context_read` — load `context.services` to find ReactJS services
|
|
16
|
+
3. Call `service_scan` — verify ReactJS service exists on disk
|
|
17
|
+
|
|
18
|
+
## Rules
|
|
19
|
+
- Read ALL target pages fully before creating any component
|
|
20
|
+
- NEVER create a component that already exists in `src/components/` — reuse it
|
|
21
|
+
- NEVER modify business logic, state, API calls, or event handlers in any page
|
|
22
|
+
- NEVER break existing imports or prop flows
|
|
23
|
+
- ONE confirmation before any file is written
|
|
24
|
+
- After writing files → call `context_write`
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Execution — Full Step-by-Step
|
|
29
|
+
|
|
30
|
+
### Phase 1 — Target Selection
|
|
31
|
+
|
|
32
|
+
**Step 1.** Ask: "Which ReactJS service?" (list ReactJS services from `context.services`)
|
|
33
|
+
- Store: `context.current_action.service_name`
|
|
34
|
+
|
|
35
|
+
**Step 2.** Ask: "What scope?"
|
|
36
|
+
1. All pages — scan every page in `src/pages/` and `src/views/`
|
|
37
|
+
2. Specific page — I'll provide the path
|
|
38
|
+
- Store: `context.current_action.modularize_scope`
|
|
39
|
+
- If specific → also ask: "Page path?" → Store: `context.current_action.page_path`
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
### Phase 2 — Inventory Existing Components
|
|
44
|
+
|
|
45
|
+
Before scanning any pages, inventory what already exists:
|
|
46
|
+
|
|
47
|
+
**Step 1.** Scan `src/components/` in the target service.
|
|
48
|
+
For each subdirectory → record component name and file path.
|
|
49
|
+
|
|
50
|
+
**Step 2.** For each existing component:
|
|
51
|
+
- Read the file
|
|
52
|
+
- Identify structural role: header, footer, sidebar, navbar, modal wrapper, card shell, breadcrumbs, etc.
|
|
53
|
+
- Record: `{ name, path, role, props_accepted }`
|
|
54
|
+
|
|
55
|
+
Store this as the "existing components registry" — the source of truth for reuse decisions.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
### Phase 3 — Scan Target Pages
|
|
60
|
+
|
|
61
|
+
#### If scope == "all":
|
|
62
|
+
Scan `src/pages/` — each subdirectory with `index.jsx` or `index.js` = one page.
|
|
63
|
+
Also scan `src/views/` if it exists.
|
|
64
|
+
|
|
65
|
+
#### If scope == "specific":
|
|
66
|
+
Pages list = [ context.current_action.page_path ]
|
|
67
|
+
|
|
68
|
+
For each page:
|
|
69
|
+
|
|
70
|
+
**Step 1.** Read the full file content.
|
|
71
|
+
|
|
72
|
+
**Step 2.** Identify all structural blocks (layout HTML — NOT business logic):
|
|
73
|
+
- Look for repeated layout patterns: `<header>`, `<nav>`, `<footer>`, `<aside>`, sidebar wrappers
|
|
74
|
+
- Look for structural patterns repeated across pages: top nav bars, breadcrumb areas, page wrappers
|
|
75
|
+
- Record each structural block as: `{ block_type, jsx_content, props_needed }`
|
|
76
|
+
|
|
77
|
+
**Step 3.** Cross-check against existing components registry:
|
|
78
|
+
- Does a component already exist that covers this block?
|
|
79
|
+
- If YES → mark as "reuse existing: <ComponentName>"
|
|
80
|
+
- If NO → mark as "new component needed: <ComponentName>"
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
### Phase 4 — Deduplicate Across Pages
|
|
85
|
+
|
|
86
|
+
After scanning all pages:
|
|
87
|
+
|
|
88
|
+
**Step 1.** Group blocks by structural similarity across pages.
|
|
89
|
+
- If the same header JSX appears in 3+ pages → one shared Header component
|
|
90
|
+
- If a sidebar appears in 2+ pages → one shared Sidebar component
|
|
91
|
+
- If a block appears in only 1 page → do NOT extract it (no duplication benefit)
|
|
92
|
+
|
|
93
|
+
**Step 2.** For each component to create, decide:
|
|
94
|
+
- Component name (PascalCase, descriptive: `PageHeader`, `AppSidebar`, `MainFooter`)
|
|
95
|
+
- Props needed (what varies between pages: title, navLinks, userName, etc.)
|
|
96
|
+
- File location: `src/components/<ComponentName>/index.jsx` and `<ComponentName>.module.css`
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
### Phase 5 — Show Extraction Plan
|
|
101
|
+
|
|
102
|
+
Display the plan before writing anything:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
┌─────────────────────────────────────────────────────┐
|
|
106
|
+
│ MODULARIZE PLAN │
|
|
107
|
+
├─────────────────────────────────────────────────────┤
|
|
108
|
+
│ Service : [service_name] │
|
|
109
|
+
│ Scope : [all pages | specific page] │
|
|
110
|
+
│ Pages : [n] scanned │
|
|
111
|
+
├─────────────────────────────────────────────────────┤
|
|
112
|
+
│ COMPONENTS TO CREATE │
|
|
113
|
+
│ ✦ AppHeader — appears in [n] pages │
|
|
114
|
+
│ Props: title (string) │
|
|
115
|
+
│ File: src/components/AppHeader/index.jsx │
|
|
116
|
+
│ │
|
|
117
|
+
│ ✦ MainSidebar — appears in [n] pages │
|
|
118
|
+
│ Props: activeRoute (string) │
|
|
119
|
+
│ File: src/components/MainSidebar/index.jsx │
|
|
120
|
+
├─────────────────────────────────────────────────────┤
|
|
121
|
+
│ COMPONENTS REUSED (already exist) │
|
|
122
|
+
│ ✓ Footer — src/components/Footer/index.jsx │
|
|
123
|
+
├─────────────────────────────────────────────────────┤
|
|
124
|
+
│ PAGES TO UPDATE │
|
|
125
|
+
│ → src/pages/Dashboard/index.jsx │
|
|
126
|
+
│ → src/pages/Profile/index.jsx │
|
|
127
|
+
│ → src/pages/Settings/index.jsx │
|
|
128
|
+
└─────────────────────────────────────────────────────┘
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Ask: "Apply this plan? (yes / no / adjust)"
|
|
132
|
+
- If yes → proceed to Phase 6
|
|
133
|
+
- If no → abort, nothing written
|
|
134
|
+
- If adjust → ask what to change → re-display plan
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
### Phase 6 — Generate Components
|
|
139
|
+
|
|
140
|
+
For each new component:
|
|
141
|
+
|
|
142
|
+
**Step 1.** Generate `src/components/<ComponentName>/index.jsx`:
|
|
143
|
+
```jsx
|
|
144
|
+
/**
|
|
145
|
+
* <ComponentName> — <one-line description of structural role>.
|
|
146
|
+
*
|
|
147
|
+
* @param {Object} props
|
|
148
|
+
* @param {string} props.<propName> - <description>
|
|
149
|
+
* @returns {JSX.Element}
|
|
150
|
+
*/
|
|
151
|
+
function <ComponentName>({ <props> }) {
|
|
152
|
+
return (
|
|
153
|
+
<extracted JSX block, using props where values varied>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export default <ComponentName>;
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Step 2.** Generate `src/components/<ComponentName>/<ComponentName>.module.css`:
|
|
161
|
+
- Extract any inline styles or className references that were in the original block
|
|
162
|
+
- Create CSS classes for them
|
|
163
|
+
- If no styles → empty file with a comment: `/* <ComponentName> styles */`
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
### Phase 7 — Update Pages
|
|
168
|
+
|
|
169
|
+
For each page that used the extracted block:
|
|
170
|
+
|
|
171
|
+
**Step 1.** Read the current page file.
|
|
172
|
+
|
|
173
|
+
**Step 2.** Apply changes surgically:
|
|
174
|
+
1. Add import at top of file:
|
|
175
|
+
```jsx
|
|
176
|
+
import <ComponentName> from '../../components/<ComponentName>';
|
|
177
|
+
```
|
|
178
|
+
(adjust relative path based on page depth)
|
|
179
|
+
|
|
180
|
+
2. Replace the extracted JSX block with the component:
|
|
181
|
+
```jsx
|
|
182
|
+
<ComponentName <prop>={<value>} />
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
3. Remove any now-unused imports that were only used by the extracted block.
|
|
186
|
+
|
|
187
|
+
4. Remove any CSS classes in the page's `.module.css` that are now handled by the component's own CSS file.
|
|
188
|
+
|
|
189
|
+
**Step 3.** Write the updated file.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
### Phase 8 — Finalize
|
|
194
|
+
|
|
195
|
+
**Step 1.** Call `context_write`:
|
|
196
|
+
- Append to `context.services[<service_name>].components`:
|
|
197
|
+
`{ name, path, role, props_accepted, used_in: [list of page paths] }`
|
|
198
|
+
- Set `last_command` = "modularize"
|
|
199
|
+
- Append to `change_log`: `{ timestamp, command: "modularize", action: "Created [n] component(s) from [n] pages" }`
|
|
200
|
+
|
|
201
|
+
**Step 2.** Call `context_clear_scratchpad` with keys: ["current_action"]
|
|
202
|
+
|
|
203
|
+
**Step 3.** Show completion report:
|
|
204
|
+
```
|
|
205
|
+
/codeninja:modularize Complete
|
|
206
|
+
─────────────────────────────────────────
|
|
207
|
+
Components created : [n]
|
|
208
|
+
Components reused : [n]
|
|
209
|
+
Pages updated : [n]
|
|
210
|
+
─────────────────────────────────────────
|
|
211
|
+
[list created files]
|
|
212
|
+
[list modified files]
|
|
213
|
+
─────────────────────────────────────────
|
|
214
|
+
Next: /codeninja:validate-page to add validation
|
|
215
|
+
/codeninja:integrate-api to wire forms to backend
|
|
216
|
+
```
|
|
@@ -2,12 +2,83 @@
|
|
|
2
2
|
slash_command: /codeninja:optimize
|
|
3
3
|
personas: [global-orchestrator, nodejs-backend, database-architect]
|
|
4
4
|
skills: [mcp-and-context, api-builder, database, code-intelligence]
|
|
5
|
-
description:
|
|
5
|
+
description: Analyse and improve performance using real project context.
|
|
6
6
|
---
|
|
7
|
+
|
|
7
8
|
# /codeninja:optimize
|
|
8
|
-
|
|
9
|
-
Before
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
|
|
10
|
+
## Before Running
|
|
11
|
+
1. Call `context_read` — load `context.db.schema`, `context.services`
|
|
12
|
+
2. Call `context_check_stale`
|
|
13
|
+
|
|
14
|
+
## Execution — Full Step-by-Step
|
|
15
|
+
|
|
16
|
+
### Step 1 — Identify Target
|
|
17
|
+
If not specified, ask:
|
|
18
|
+
"What would you like to optimise?
|
|
19
|
+
(a) A slow API endpoint — paste the route path
|
|
20
|
+
(b) A heavy SQL query — paste the query or file path
|
|
21
|
+
(c) Overall service startup / memory usage
|
|
22
|
+
(d) Something else — describe it"
|
|
23
|
+
|
|
24
|
+
### Step 2 — Load Context
|
|
25
|
+
1. Call `fs_read` on the relevant files (route.js, _model.js)
|
|
26
|
+
2. For DB queries: list existing indexes from `context.db.schema.<table>.indexes`
|
|
27
|
+
|
|
28
|
+
### Step 3A — Database Query Analysis
|
|
29
|
+
|
|
30
|
+
For each query in the target:
|
|
31
|
+
|
|
32
|
+
**Check 1 — Missing Indexes**
|
|
33
|
+
Compare WHERE / JOIN / ORDER BY columns against existing indexes.
|
|
34
|
+
Generate `CREATE INDEX CONCURRENTLY` for missing ones.
|
|
35
|
+
|
|
36
|
+
**Check 2 — SELECT * Anti-Pattern**
|
|
37
|
+
Replace with explicit column list. Show before/after.
|
|
38
|
+
|
|
39
|
+
**Check 3 — N+1 Query Pattern**
|
|
40
|
+
Flag any loop calling a DB function per iteration. Suggest single query with IN or JOIN.
|
|
41
|
+
|
|
42
|
+
**Check 4 — Window Function Choice**
|
|
43
|
+
- `RANK()` → gaps after ties (wrong for leaderboards)
|
|
44
|
+
- `DENSE_RANK()` → no gaps (correct for leaderboards)
|
|
45
|
+
- `ROW_NUMBER()` → unique position (correct for pagination)
|
|
46
|
+
|
|
47
|
+
**Check 5 — Duplicate Row Prevention**
|
|
48
|
+
Flag duplicate rows from missing dedup. Suggest `MIN(id) GROUP BY user_id`.
|
|
49
|
+
|
|
50
|
+
**Check 6 — Functional Index Trap**
|
|
51
|
+
Flag `DATE(created_at) = '...'` → suggest range form for index usage.
|
|
52
|
+
|
|
53
|
+
**Check 7 — work_mem for Sort Operations**
|
|
54
|
+
If EXPLAIN shows external merge disk sort → suggest `SET work_mem = '64MB'`.
|
|
55
|
+
|
|
56
|
+
### Step 3B — API Route Analysis
|
|
57
|
+
|
|
58
|
+
- Is heavy middleware on lightweight routes?
|
|
59
|
+
- Synchronous blocking in request path?
|
|
60
|
+
- Repeated DB lookups that could be cached in Redis?
|
|
61
|
+
- Pagination missing on list endpoints?
|
|
62
|
+
|
|
63
|
+
### Step 4 — Output Recommendations
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
[HIGH | MED | LOW] — Impact label
|
|
67
|
+
|
|
68
|
+
Target: what's being optimised
|
|
69
|
+
Root cause: why it's slow right now
|
|
70
|
+
Fix:
|
|
71
|
+
[exact SQL or code change]
|
|
72
|
+
Estimated gain: concrete estimate
|
|
73
|
+
Side effects: anything developer should know
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
List all, highest impact first.
|
|
77
|
+
|
|
78
|
+
### Step 5 — Offer to Apply
|
|
79
|
+
"I've found [N] optimisations. Want me to apply them?
|
|
80
|
+
I'll implement each one and show you the change before writing."
|
|
81
|
+
|
|
82
|
+
For index changes → generate `NNN_add_index_<n>.sql` migration file.
|
|
83
|
+
For code changes → edit the relevant model or route file surgically.
|
|
84
|
+
Always call `context_write` after applying index additions.
|