ucode-agent 1.1.0 → 1.3.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 +86 -29
- package/package.json +3 -4
- package/skills/ai-features/SKILL.md +140 -0
- package/skills/build-app/SKILL.md +3 -2
- package/skills/performance/SKILL.md +84 -0
- package/skills/refactor/SKILL.md +72 -0
- package/skills/security/SKILL.md +110 -0
- package/src/core/context.js +151 -0
- package/src/core/loop.js +376 -25
- package/src/core/provider.js +39 -22
- package/src/core/version.js +16 -0
- package/src/tools/files.js +173 -16
- package/src/tools/index.js +445 -394
- package/src/tools/shell.js +141 -0
- package/src/ui/plain.js +330 -325
- package/src/ui/screen.js +1193 -1094
- package/src/ui/theme.js +18 -0
- package/ucode.js +3 -4
package/README.md
CHANGED
|
@@ -2,31 +2,49 @@
|
|
|
2
2
|
|
|
3
3
|
A coding agent that lives in your terminal. It reads your code, edits it, runs
|
|
4
4
|
your commands, and keeps every conversation on disk. It runs on NVIDIA and
|
|
5
|
-
Cohere models
|
|
5
|
+
Cohere models, all of them free.
|
|
6
|
+
|
|
7
|
+
It opens on a quiet screen — the name, the place to type, and the version in the
|
|
8
|
+
corner:
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
██╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗
|
|
12
|
+
██║ ██║██╔════╝██╔═══██╗██╔══██╗██╔════╝
|
|
13
|
+
██║ ██║██║ ██║ ██║██║ ██║█████╗
|
|
14
|
+
██║ ██║██║ ██║ ██║██║ ██║██╔══╝
|
|
15
|
+
╚██████╔╝╚██████╗╚██████╔╝██████╔╝███████╗
|
|
16
|
+
╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
20
|
+
│ › Ask anything… │
|
|
21
|
+
│ │
|
|
22
|
+
│ ◆ Build · Nemotron 3 Ultra 0% │
|
|
23
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
v1.2.0
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
and once you are talking, each message you send is boxed in the same blue as
|
|
30
|
+
the input, so your own words are easy to find in a long session:
|
|
6
31
|
|
|
7
32
|
```
|
|
8
33
|
╭──────────────────────────────────────────────────────────────────────────────────╮
|
|
9
|
-
│
|
|
10
|
-
│ ██║ ██║██╔════╝██╔═══██╗██╔══██╗██╔════╝ keys /help · esc interrupts │
|
|
11
|
-
│ ██║ ██║██║ ██║ ██║██║ ██║█████╗ │
|
|
12
|
-
│ ██║ ██║██║ ██║ ██║██║ ██║██╔══╝ │
|
|
13
|
-
│ ╚██████╔╝╚██████╗╚██████╔╝██████╔╝███████╗ │
|
|
14
|
-
│ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ made with ❤️ by om dixit │
|
|
34
|
+
│ › build a notes dashboard │
|
|
15
35
|
╰──────────────────────────────────────────────────────────────────────────────────╯
|
|
16
|
-
|
|
17
36
|
● Writing index.html
|
|
18
37
|
└ created · 148 lines
|
|
19
38
|
1 + <!doctype html>
|
|
20
39
|
2 + <html lang="en">
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
└ exit 0 · 12 lines
|
|
40
|
+
… 146 more lines
|
|
41
|
+
● Running npm run dev
|
|
42
|
+
└ ready · http://localhost:3000 · PID 4812
|
|
25
43
|
|
|
26
44
|
╭──────────────────────────────────────────────────────────────────────────────────╮
|
|
27
45
|
│ › now add a dark mode toggle │
|
|
28
46
|
│ │
|
|
29
|
-
│ ◆ Build · Nemotron 3 Ultra
|
|
47
|
+
│ ◆ Build · Nemotron 3 Ultra 4% │
|
|
30
48
|
╰──────────────────────────────────────────────────────────────────────────────────╯
|
|
31
49
|
```
|
|
32
50
|
|
|
@@ -47,7 +65,7 @@ Then put a key where it will survive upgrades:
|
|
|
47
65
|
|
|
48
66
|
```bash
|
|
49
67
|
mkdir -p ~/.ucode
|
|
50
|
-
echo "
|
|
68
|
+
echo "UCODE_API_KEY=sk-or-..." > ~/.ucode/.env
|
|
51
69
|
```
|
|
52
70
|
|
|
53
71
|
Keys are free at [openrouter.ai/keys](https://openrouter.ai/keys). A `.env` in
|
|
@@ -65,7 +83,7 @@ Needs Node 22 or newer.
|
|
|
65
83
|
## The models
|
|
66
84
|
|
|
67
85
|
Five, and no picker full of names nobody recognises. NVIDIA and Cohere both
|
|
68
|
-
serve capable models free
|
|
86
|
+
serve capable models free, and both handle tool calling
|
|
69
87
|
properly, which is the thing an agent actually depends on.
|
|
70
88
|
|
|
71
89
|
| Model | Context | For |
|
|
@@ -85,15 +103,44 @@ long think are for. When the wait stops being worth it, switch.
|
|
|
85
103
|
|
|
86
104
|
## What it does
|
|
87
105
|
|
|
88
|
-
**
|
|
89
|
-
`
|
|
90
|
-
`web_search`. Read-only calls run in parallel
|
|
91
|
-
|
|
106
|
+
**Thirteen tools.** `read_file`, `read_files`, `write_file`, `batch_write`,
|
|
107
|
+
`edit_file`, `multi_edit`, `edit_files`, `list_dir`, `glob`, `grep`,
|
|
108
|
+
`run_command`, `run_commands`, `web_search`. Read-only calls run in parallel,
|
|
109
|
+
and start the moment the model finishes writing them — while the rest of its
|
|
110
|
+
reply is still arriving. Anything that writes runs on its own, in order.
|
|
111
|
+
|
|
112
|
+
**Parallel workers.** When a build splits into parts that touch different files
|
|
113
|
+
— the API route, the upload component, the results view — the model hands them
|
|
114
|
+
to up to three workers that build at the same time, each line in the transcript
|
|
115
|
+
tagged with the worker's name. File writes take turns so two never collide.
|
|
116
|
+
|
|
117
|
+
**Installs that start early.** The moment a `package.json` with dependencies is
|
|
118
|
+
written, its install starts in the background while the rest of the app is
|
|
119
|
+
still being written. An install the model asks for later waits for that one
|
|
120
|
+
instead of running twice, and anything run in that folder waits for it too.
|
|
121
|
+
|
|
122
|
+
**Errors fixed before you see them.** When the model says it is done, ucode
|
|
123
|
+
type-checks every file it changed — `tsc --noEmit` for TypeScript projects,
|
|
124
|
+
a syntax check for JavaScript and Python — and hands any errors back to fix,
|
|
125
|
+
up to three rounds.
|
|
126
|
+
|
|
127
|
+
**A plan you can see.** For longer jobs the model keeps a short checklist, shown
|
|
128
|
+
as one line: `plan 2/5 ✓ Scaffold · ✓ Upload · ▸ Score dial · ○ Findings · ○ Polish`.
|
|
129
|
+
|
|
130
|
+
**It knows the project before it asks.** Each turn starts with a map of every
|
|
131
|
+
file and the names each code file exports, so the model goes straight to the
|
|
132
|
+
right file instead of searching for it.
|
|
133
|
+
|
|
134
|
+
**Project memory.** `UCODE.md` in a project — and `~/.ucode/UCODE.md` for how you
|
|
135
|
+
like to work everywhere — is read at the start of every turn. `/remember <note>`
|
|
136
|
+
adds a line to it.
|
|
92
137
|
|
|
93
138
|
**Edits that never guess.** `edit_file` matches exactly once or it fails, and
|
|
94
|
-
when it fails it says *why
|
|
95
|
-
|
|
96
|
-
|
|
139
|
+
when it fails it says *why*. It tolerates what does not matter — tabs against
|
|
140
|
+
spaces, a different indent depth, Windows line endings — and re-indents the
|
|
141
|
+
replacement to fit the file, but a match found twice is still refused.
|
|
142
|
+
`edit_files` changes several files in one call, and writes none of them if any
|
|
143
|
+
edit fails.
|
|
97
144
|
|
|
98
145
|
**Diffs with real line numbers.** Removed lines are numbered where they were,
|
|
99
146
|
added lines where they now are. Numbers you can jump to, not decoration.
|
|
@@ -128,10 +175,15 @@ when it is wanted, so the prompt stays the same size however many you add.
|
|
|
128
175
|
| `debug` | finding the real cause instead of the first plausible one |
|
|
129
176
|
| `code-review` | reviewing a change the way a careful colleague would |
|
|
130
177
|
| `write-tests` | tests that fail for the right reason |
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
178
|
+
| `ai-features` | model-backed features: prompts with rules, validated JSON, images, failure handling |
|
|
179
|
+
| `security` | secrets, auth, ownership checks, injection, XSS, CSRF, SSRF, uploads |
|
|
180
|
+
| `performance` | measure first, find the real bottleneck, prove the win with numbers |
|
|
181
|
+
| `refactor` | change the shape of code without changing what it does |
|
|
182
|
+
|
|
183
|
+
**Every skill loads itself** when the request calls for it — an app pulls in
|
|
184
|
+
`ui-ux` and `build-app`, "it crashes" pulls in `debug`, an AI feature pulls in
|
|
185
|
+
`ai-features`, an API key pulls in `security` — so the whole skill is in
|
|
186
|
+
context before the model takes its first step. Waiting for the model to decide it needs design guidance means
|
|
135
187
|
finding out it did not after the app is built.
|
|
136
188
|
|
|
137
189
|
Add your own in `.ucode/skills/<name>/SKILL.md` inside a project. A project
|
|
@@ -156,6 +208,7 @@ Everything after the frontmatter is the instruction.
|
|
|
156
208
|
| `/model` | show the models and switch — `/models` does the same |
|
|
157
209
|
| `/resume` | pick up an earlier conversation — `/session`, `/sessions` too |
|
|
158
210
|
| `/new` | save this one and start fresh |
|
|
211
|
+
| `/remember <note>` | add a standing note to this project's `UCODE.md` |
|
|
159
212
|
| `/skills` | what it knows how to do, and what is loaded |
|
|
160
213
|
| `/search <query>` | look something up on the web |
|
|
161
214
|
| `/copy` | last reply to the clipboard |
|
|
@@ -183,11 +236,14 @@ ucode [options]
|
|
|
183
236
|
|
|
184
237
|
| | |
|
|
185
238
|
| --- | --- |
|
|
186
|
-
| `~/.ucode/.env` | `
|
|
239
|
+
| `~/.ucode/.env` | `UCODE_API_KEY`, and `TAVILY_API_KEY` for web search |
|
|
187
240
|
| `~/.ucode/sessions/` | one JSON per conversation |
|
|
188
241
|
| `.ucode/skills/` | skills belonging to a project |
|
|
242
|
+
| `UCODE.md` | project memory, read every turn |
|
|
243
|
+
| `~/.ucode/UCODE.md` | your own standing instructions, for every project |
|
|
189
244
|
|
|
190
|
-
Environment overrides: `UCODE_MODEL`, `
|
|
245
|
+
Environment overrides: `UCODE_MODEL`, `UCODE_WORKER_MODEL` (a faster model for
|
|
246
|
+
parallel workers), `UCODE_WORKER_STEPS`, `UCODE_MAX_CONTEXT_TOKENS`,
|
|
191
247
|
`UCODE_MAX_STEPS`, `UCODE_MAX_TOOL_OUTPUT`, `UCODE_REQUEST_TIMEOUT_MS`,
|
|
192
248
|
`UCODE_BASE_URL`.
|
|
193
249
|
|
|
@@ -199,10 +255,11 @@ one, ucode answers from what it knows and says that it could not check.
|
|
|
199
255
|
```
|
|
200
256
|
ucode.js the command: arguments in, Agent out
|
|
201
257
|
src/core/loop.js the agent loop, the system prompt, the slash commands
|
|
202
|
-
src/core/provider.js the only file that knows
|
|
258
|
+
src/core/provider.js the only file that knows which provider answers
|
|
203
259
|
src/core/history.js sessions on disk
|
|
204
260
|
src/core/window.js folding a long conversation to fit
|
|
205
261
|
src/core/skills.js loading skills, and deciding which load themselves
|
|
262
|
+
src/core/context.js the project map and project memory
|
|
206
263
|
src/core/failure.js one error shape: what, why, what next
|
|
207
264
|
src/tools/ the eleven tools, plus their shared plumbing
|
|
208
265
|
src/ui/screen.js the full-screen interface
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ucode-agent",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "ucode - a terminal coding agent that reads, edits and runs your code
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "ucode - a terminal coding agent that reads, edits and runs your code, on NVIDIA and Cohere models.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "ucode.js",
|
|
7
7
|
"bin": {
|
|
@@ -39,8 +39,7 @@
|
|
|
39
39
|
"llm",
|
|
40
40
|
"nvidia",
|
|
41
41
|
"nemotron",
|
|
42
|
-
"cohere"
|
|
43
|
-
"openrouter"
|
|
42
|
+
"cohere"
|
|
44
43
|
],
|
|
45
44
|
"author": "om dixit",
|
|
46
45
|
"license": "ISC",
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ai-features
|
|
3
|
+
description: Build features on top of language and vision models that behave like product, not demos — prompts with explicit rules, structured output validated in code, images, streaming, timeouts, retries, cost and failure handling.
|
|
4
|
+
auto: ai, llm, llms, gpt, chatgpt, claude, gemini, nemotron, chatbot, chat bot, embeddings, rag, vision model, image recognition, ocr, structured output, ai feature, ai-powered, ai powered, prompt engineering, analyze image, analyse image, reads the image, read the label
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# AI features
|
|
8
|
+
|
|
9
|
+
A model call that works once in a demo is easy. A feature that gives a
|
|
10
|
+
consistent, correct, well-formatted answer every time, fails gracefully, and
|
|
11
|
+
does not leak a key is the actual job. Treat the model as an unreliable
|
|
12
|
+
upstream service with a very good API.
|
|
13
|
+
|
|
14
|
+
## 1. Architecture
|
|
15
|
+
|
|
16
|
+
- **Server only.** The call and the key live in a server route or server action.
|
|
17
|
+
The browser sends the input to your route; your route calls the model.
|
|
18
|
+
- **One module per model integration** (`lib/server/analyze.ts`): builds the
|
|
19
|
+
prompt, calls the API, parses and validates the reply, returns a typed result
|
|
20
|
+
or a typed error. Routes and UI never touch raw model output.
|
|
21
|
+
- **Types first.** Define the result with zod before writing the prompt. The
|
|
22
|
+
schema is the contract the prompt has to satisfy.
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
const Finding = z.object({
|
|
26
|
+
nutrient: z.string(),
|
|
27
|
+
amount: z.string(), // "820 mg", as printed
|
|
28
|
+
severity: z.enum(['high', 'low']),
|
|
29
|
+
why: z.string().max(160),
|
|
30
|
+
});
|
|
31
|
+
export const Analysis = z.object({
|
|
32
|
+
isNutritionLabel: z.boolean(),
|
|
33
|
+
score: z.number().min(1).max(10),
|
|
34
|
+
summary: z.string().max(400),
|
|
35
|
+
findings: z.array(Finding).max(8),
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 2. Prompts that produce the same answer twice
|
|
40
|
+
|
|
41
|
+
- **Role and task in the first line**, then the rules, then the output format.
|
|
42
|
+
- **Make every judgement explicit.** "Flag sodium if it is over 20% of daily
|
|
43
|
+
value per serving (≈460 mg)" is reproducible; "flag unhealthy things" is not.
|
|
44
|
+
Write the thresholds, the scale anchors ("10 = whole food with no concerns,
|
|
45
|
+
5 = fine occasionally, 1 = mostly sugar or salt"), and what to leave out
|
|
46
|
+
("if no nutrient crosses a threshold, return an empty findings array — never
|
|
47
|
+
pad it").
|
|
48
|
+
- **Handle the wrong input inside the prompt too**: "If the image is not a
|
|
49
|
+
nutrition facts label, set isNutritionLabel to false and leave the rest empty."
|
|
50
|
+
- **Specify the JSON exactly** — field names, types, units, lengths — and say
|
|
51
|
+
"Reply with the JSON object only. No prose, no code fences."
|
|
52
|
+
- **Put the unchanging instructions in the system message** and the per-request
|
|
53
|
+
input in the user message.
|
|
54
|
+
- Temperature 0–0.3 for extraction and scoring; higher only for creative text.
|
|
55
|
+
|
|
56
|
+
## 3. Calling the API
|
|
57
|
+
|
|
58
|
+
OpenAI-compatible chat completions (most providers, including model routers):
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
const res = await fetch(`${BASE_URL}/chat/completions`, {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
headers: { Authorization: `Bearer ${KEY}`, 'Content-Type': 'application/json' },
|
|
64
|
+
body: JSON.stringify({
|
|
65
|
+
model: MODEL_ID,
|
|
66
|
+
temperature: 0.2,
|
|
67
|
+
messages: [
|
|
68
|
+
{ role: 'system', content: SYSTEM_PROMPT },
|
|
69
|
+
{ role: 'user', content: [
|
|
70
|
+
{ type: 'text', text: 'Analyse this label.' },
|
|
71
|
+
{ type: 'image_url', image_url: { url: dataUrl } },
|
|
72
|
+
] },
|
|
73
|
+
],
|
|
74
|
+
}),
|
|
75
|
+
signal: AbortSignal.timeout(90_000),
|
|
76
|
+
});
|
|
77
|
+
if (!res.ok) throw new ModelError(res.status, await res.text());
|
|
78
|
+
const text = (await res.json()).choices?.[0]?.message?.content ?? '';
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- **Timeout on every call.** Reasoning models can think for 30–60s; set the route's
|
|
82
|
+
own limit (`export const maxDuration = 90` in Next.js) above the call's.
|
|
83
|
+
- **Retry only what is safe to retry**: 429 (after `retry-after`), 5xx, network
|
|
84
|
+
errors, timeouts — at most 2 retries with backoff. Never retry a 400 or 401.
|
|
85
|
+
- **Map errors to user language** at the boundary: rate limited → "busy, try
|
|
86
|
+
again in a moment"; timeout → "took too long"; invalid output → "could not
|
|
87
|
+
read the result"; not a label → "that does not look like a nutrition label".
|
|
88
|
+
|
|
89
|
+
## 4. Parsing model output defensively
|
|
90
|
+
|
|
91
|
+
Models add prose, wrap JSON in fences, use trailing commas, or return numbers
|
|
92
|
+
as strings. Never `JSON.parse` the raw text directly.
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
function extractJson(text: string): unknown {
|
|
96
|
+
const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i)?.[1];
|
|
97
|
+
const raw = fenced ?? text.slice(text.indexOf('{'), text.lastIndexOf('}') + 1);
|
|
98
|
+
return JSON.parse(raw);
|
|
99
|
+
}
|
|
100
|
+
const parsed = Analysis.safeParse(extractJson(text));
|
|
101
|
+
if (!parsed.success) throw new ModelError(422, 'unreadable model output');
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Then **enforce the rules in code as well**: clamp the score, drop findings that
|
|
105
|
+
do not meet the threshold, cap list lengths. The prompt asks; the code
|
|
106
|
+
guarantees.
|
|
107
|
+
|
|
108
|
+
## 5. Images
|
|
109
|
+
|
|
110
|
+
- Accept jpeg, png, webp. Check type and size on the client before upload.
|
|
111
|
+
- Downscale in a canvas to ~1600px on the long edge and re-encode as JPEG at
|
|
112
|
+
0.85 — faster upload, fewer tokens, same accuracy for text in the image.
|
|
113
|
+
- Send as a `data:image/jpeg;base64,...` URL. Handle HEIC from phones by
|
|
114
|
+
telling the user to export as JPEG if the browser cannot decode it.
|
|
115
|
+
- Check that the model you are calling accepts images at all before building
|
|
116
|
+
on it.
|
|
117
|
+
|
|
118
|
+
## 6. The experience around the call
|
|
119
|
+
|
|
120
|
+
- A real loading state with words ("Reading the label…"), elapsed time if it can
|
|
121
|
+
exceed ~5s, and a way to cancel (`AbortController`).
|
|
122
|
+
- Stream text responses when the output is prose, so something appears at once.
|
|
123
|
+
For structured results, show a skeleton of the result shape instead.
|
|
124
|
+
- Show the result with its reasoning visible (the summary, the findings), not
|
|
125
|
+
just a number.
|
|
126
|
+
- Keep the user's input on failure so "try again" is one click.
|
|
127
|
+
|
|
128
|
+
## 7. Cost, privacy and abuse
|
|
129
|
+
|
|
130
|
+
- Log the model, latency, token usage and outcome — never the key, and never
|
|
131
|
+
the user's content unless they agreed to it.
|
|
132
|
+
- Limit input size and request rate per user on public endpoints.
|
|
133
|
+
- Cache results for identical inputs where it is safe (hash of the image).
|
|
134
|
+
|
|
135
|
+
## 8. Verify with real inputs
|
|
136
|
+
|
|
137
|
+
Test with at least: a clear typical input, a hard one (blurry photo, cropped
|
|
138
|
+
label), a wrong one (a photo of a cat), and an edge (a food that genuinely has
|
|
139
|
+
no concerns — the findings list must come back empty). Report what each
|
|
140
|
+
returned.
|
|
@@ -28,7 +28,8 @@ One line each:
|
|
|
28
28
|
- **The file list** — the whole tree, before creating any of it.
|
|
29
29
|
|
|
30
30
|
If there is a user interface, the `ui-ux` skill is already loaded. Decide the
|
|
31
|
-
design direction now, not after the logic works.
|
|
31
|
+
design direction now, not after the logic works. If the app calls a model,
|
|
32
|
+
load `ai-features`; if it has accounts, keys or uploads, load `security`.
|
|
32
33
|
|
|
33
34
|
## 2. Scaffold without being asked questions
|
|
34
35
|
|
|
@@ -89,7 +90,7 @@ src/
|
|
|
89
90
|
of the response status, and an error that says what failed — surfaced to the
|
|
90
91
|
UI as a real message, never a silent `catch {}`.
|
|
91
92
|
|
|
92
|
-
### Calling an AI model (
|
|
93
|
+
### Calling an AI model (any OpenAI-compatible API)
|
|
93
94
|
|
|
94
95
|
```ts
|
|
95
96
|
// src/app/api/analyze/route.ts — runs on the server only
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: performance
|
|
3
|
+
description: Make software measurably faster — measure first, find the real bottleneck, fix it, and prove the improvement with numbers. Covers web vitals, bundles, React rendering, APIs, databases and Node.
|
|
4
|
+
auto: slow, slower, performance, perf, optimize, optimise, optimization, speed up, faster, lag, laggy, sluggish, bundle size, lighthouse, core web vitals, web vitals, lcp, cls, inp, memory leak, re-render, rerender, n+1, latency, takes too long
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Performance
|
|
8
|
+
|
|
9
|
+
Guessing at performance is how people spend a day optimizing something that
|
|
10
|
+
was never slow. Measure, change one thing, measure again.
|
|
11
|
+
|
|
12
|
+
## 1. Measure before touching anything
|
|
13
|
+
|
|
14
|
+
- **Define the slow thing precisely**: which page, action or endpoint, how slow
|
|
15
|
+
now, how fast it needs to be.
|
|
16
|
+
- **Get a baseline number** you can re-run:
|
|
17
|
+
- Web page: Lighthouse / PageSpeed (LCP, INP, CLS, total JS), the browser
|
|
18
|
+
Performance panel, `next build` output (per-route JS size).
|
|
19
|
+
- API: time the request (`curl -w "%{time_total}\n"`), log durations per step.
|
|
20
|
+
- Node/Python: a profiler (`node --cpu-prof`, `clinic`, `py-spy`), or timers
|
|
21
|
+
around the suspect code.
|
|
22
|
+
- Database: `EXPLAIN ANALYZE` on the slow query.
|
|
23
|
+
- Measure the production build, not dev mode — dev is deliberately slow.
|
|
24
|
+
|
|
25
|
+
## 2. Find the actual bottleneck
|
|
26
|
+
|
|
27
|
+
It is almost always one of these, in roughly this order of likelihood:
|
|
28
|
+
|
|
29
|
+
1. **Network waterfalls** — requests that wait on each other when they could run
|
|
30
|
+
in parallel; data fetched on the client that could be fetched on the server.
|
|
31
|
+
2. **Too much JavaScript** — heavy dependencies, everything marked
|
|
32
|
+
`"use client"`, no code splitting.
|
|
33
|
+
3. **Unoptimized images and fonts** — huge images, no dimensions (layout shift),
|
|
34
|
+
blocking font loads.
|
|
35
|
+
4. **Database** — N+1 queries, missing indexes, fetching whole tables, no
|
|
36
|
+
pagination.
|
|
37
|
+
5. **Rendering** — React re-rendering large trees on every keystroke, expensive
|
|
38
|
+
work inside render, long lists without virtualization.
|
|
39
|
+
6. **Algorithmic** — nested loops over large data, repeated work that could be
|
|
40
|
+
cached or computed once.
|
|
41
|
+
|
|
42
|
+
## 3. Fixes by area
|
|
43
|
+
|
|
44
|
+
**Web (Next.js / React)**
|
|
45
|
+
- Server components by default; `"use client"` at the leaves only.
|
|
46
|
+
- `next/image` with explicit sizes; `priority` on the LCP image; modern formats.
|
|
47
|
+
- `next/font` with `display: swap`, only the weights used.
|
|
48
|
+
- Dynamic `import()` for heavy, below-the-fold or rarely used components.
|
|
49
|
+
- Replace heavy libraries (moment → date-fns/Intl, lodash → native, big chart
|
|
50
|
+
libs → lighter ones) and check the per-route JS in the build output.
|
|
51
|
+
- Parallelize independent fetches with `Promise.all`; stream with `Suspense`.
|
|
52
|
+
- Cache: static where possible, `revalidate` for data that changes slowly.
|
|
53
|
+
|
|
54
|
+
**React rendering**
|
|
55
|
+
- Keep state as low in the tree as possible; lift only what must be shared.
|
|
56
|
+
- Stable props: memoize expensive values and callbacks passed to memoized
|
|
57
|
+
children — but only where the profiler shows a real cost.
|
|
58
|
+
- Virtualize lists over a few hundred rows.
|
|
59
|
+
- Debounce input-driven work (search, validation) at ~200–300ms.
|
|
60
|
+
|
|
61
|
+
**APIs and databases**
|
|
62
|
+
- Index columns used in `WHERE`, `JOIN` and `ORDER BY`; confirm with `EXPLAIN`.
|
|
63
|
+
- Batch or join instead of querying in a loop (N+1).
|
|
64
|
+
- Select only the columns needed; paginate everything user-sized.
|
|
65
|
+
- Cache expensive, repeatable results (in memory, Redis, HTTP caching) with a
|
|
66
|
+
clear invalidation rule.
|
|
67
|
+
- Move slow non-essential work (emails, analytics, thumbnails) to a background
|
|
68
|
+
job.
|
|
69
|
+
|
|
70
|
+
**Node**
|
|
71
|
+
- Never block the event loop with sync I/O or heavy CPU in a request handler.
|
|
72
|
+
- Stream large files instead of reading them whole.
|
|
73
|
+
- Reuse clients and connections (DB pools, HTTP keep-alive).
|
|
74
|
+
|
|
75
|
+
## 4. Change one thing at a time, and prove it
|
|
76
|
+
|
|
77
|
+
After each change, re-run the same measurement. Keep changes that move the
|
|
78
|
+
number; revert ones that do not — complexity without a measured win is a cost.
|
|
79
|
+
|
|
80
|
+
## 5. Report with numbers
|
|
81
|
+
|
|
82
|
+
Before and after for each metric that changed ("LCP 4.1s → 1.6s, route JS
|
|
83
|
+
312 kB → 148 kB"), what caused it, and anything left that would need a bigger
|
|
84
|
+
change to fix.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: refactor
|
|
3
|
+
description: Restructure code without changing what it does — a safety net first, small verified steps, clear boundaries and names, and no behaviour change slipped in along the way.
|
|
4
|
+
auto: refactor, refactoring, clean up, cleanup, clean this, restructure, reorganize, reorganise, simplify, tech debt, technical debt, extract, split this file, split up, dead code, duplicate code, duplication, messy code, spaghetti
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Refactoring
|
|
8
|
+
|
|
9
|
+
A refactor changes the shape of code and nothing else. The moment behaviour
|
|
10
|
+
changes, it is a rewrite, and a rewrite hidden inside a refactor is how
|
|
11
|
+
regressions ship with nobody noticing.
|
|
12
|
+
|
|
13
|
+
## 1. Know why, and where it stops
|
|
14
|
+
|
|
15
|
+
- State the goal in one line: *split the 700-line page into components*,
|
|
16
|
+
*remove the duplicated fetch logic*, *make the scoring rules testable*.
|
|
17
|
+
- State the boundary: which files are in scope. Resist improving everything you
|
|
18
|
+
pass on the way — note it and leave it.
|
|
19
|
+
|
|
20
|
+
## 2. Build the safety net first
|
|
21
|
+
|
|
22
|
+
- Run the existing tests and record the result. If there are none around the
|
|
23
|
+
code you are changing, **write characterization tests first**: tests that pin
|
|
24
|
+
down what the code does now, including its odd behaviour. You are preserving
|
|
25
|
+
behaviour, so you must be able to detect when it changes.
|
|
26
|
+
- For UI with no tests, capture the current behaviour: what renders in each
|
|
27
|
+
state, what each control does.
|
|
28
|
+
|
|
29
|
+
## 3. Read it all before moving anything
|
|
30
|
+
|
|
31
|
+
Read every file in scope in full, and find every caller of what you will change
|
|
32
|
+
(`grep` for the names, the imports, the routes). A rename that misses one
|
|
33
|
+
dynamic reference is a runtime error waiting for the one path nobody tested.
|
|
34
|
+
|
|
35
|
+
## 4. Small steps, each one green
|
|
36
|
+
|
|
37
|
+
Do one kind of change at a time, and run the tests after each:
|
|
38
|
+
|
|
39
|
+
- **Rename** to say what things are: `data` → `analysis`, `handle()` →
|
|
40
|
+
`submitLabel()`. Names are most of readability.
|
|
41
|
+
- **Extract** a function or component for each distinct job; a function should
|
|
42
|
+
do one thing at one level of abstraction.
|
|
43
|
+
- **Move** code next to what uses it: feature folders over type folders.
|
|
44
|
+
- **Remove duplication** only when the copies really are the same concept —
|
|
45
|
+
two similar-looking pieces that change for different reasons should stay two.
|
|
46
|
+
- **Delete dead code** — unused exports, unreachable branches, commented-out
|
|
47
|
+
blocks. Confirm it is unused with a search first.
|
|
48
|
+
- **Simplify conditionals**: early returns over nesting, lookup tables over long
|
|
49
|
+
`if/else` chains, named booleans over complex expressions.
|
|
50
|
+
- **Push side effects to the edges**: pure logic in the middle (easy to test),
|
|
51
|
+
I/O at the boundary.
|
|
52
|
+
|
|
53
|
+
Use `multi_edit` for several changes in one file, and keep each step small
|
|
54
|
+
enough that a failing test points straight at the cause.
|
|
55
|
+
|
|
56
|
+
## 5. Keep behaviour identical
|
|
57
|
+
|
|
58
|
+
- Same inputs, same outputs, same errors, same side effects, same order of
|
|
59
|
+
side effects.
|
|
60
|
+
- Public APIs and stored formats unchanged, or every caller and every stored
|
|
61
|
+
record updated in the same change.
|
|
62
|
+
- If you find a bug while refactoring, **do not fix it silently inside the
|
|
63
|
+
refactor**. Finish the refactor, then fix the bug as its own change — or
|
|
64
|
+
report it — so each can be reviewed and reverted on its own.
|
|
65
|
+
|
|
66
|
+
## 6. Finish
|
|
67
|
+
|
|
68
|
+
- All tests pass, the build passes, the linter and type checker are clean.
|
|
69
|
+
- The code is measurably simpler: fewer lines, fewer branches, smaller files,
|
|
70
|
+
clearer names — say which.
|
|
71
|
+
- Report what moved where, anything you noticed but deliberately left alone,
|
|
72
|
+
and any behaviour you had to pin down with new tests.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: security
|
|
3
|
+
description: Build and audit software so it is safe by default — secrets, authentication and authorization, input validation, injection, XSS, CSRF, SSRF, uploads, dependencies and headers, checked against how attacks actually happen.
|
|
4
|
+
auto: security, secure, vulnerability, vulnerabilities, auth, authentication, authorization, login, sign in, signup, sign up, password, passwords, jwt, oauth, session cookie, xss, csrf, ssrf, sql injection, injection, secrets, api key, api keys, owasp, harden, hardening, permissions, rate limit
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Security
|
|
8
|
+
|
|
9
|
+
Assume every input is hostile and every secret will be looked for. Most real
|
|
10
|
+
breaches come from a short list of boring mistakes; this skill is that list,
|
|
11
|
+
with what to do about each.
|
|
12
|
+
|
|
13
|
+
## 1. Secrets
|
|
14
|
+
|
|
15
|
+
- **Never in the browser.** Anything imported by client code ships to every
|
|
16
|
+
visitor — including a "temporary" hardcoded key. Keys live in server routes,
|
|
17
|
+
server actions or server-only modules (`import 'server-only'`).
|
|
18
|
+
- **Never in the repository.** Use `.env.local` / `.env` (gitignored) and commit
|
|
19
|
+
a `.env.example` with placeholders. If a key was ever committed or pasted
|
|
20
|
+
somewhere public, rotate it — deleting the line does not un-leak it.
|
|
21
|
+
- **Never in logs, errors or URLs.** Redact before logging; do not put tokens
|
|
22
|
+
in query strings.
|
|
23
|
+
- If the user insists on hardcoding a key for now, put it in one server-only
|
|
24
|
+
file, say exactly where it is, and recommend moving it to an env var.
|
|
25
|
+
|
|
26
|
+
## 2. Authentication
|
|
27
|
+
|
|
28
|
+
- Use a proven library or provider (Auth.js/NextAuth, Clerk, Supabase Auth,
|
|
29
|
+
Lucia-style patterns) rather than hand-rolled sessions and hashing.
|
|
30
|
+
- Passwords: argon2id or bcrypt, never reversible, never logged. Rate-limit
|
|
31
|
+
login and reset endpoints. Same error message for "no such user" and "wrong
|
|
32
|
+
password".
|
|
33
|
+
- Sessions: `HttpOnly`, `Secure`, `SameSite=Lax` (or `Strict`) cookies. Rotate
|
|
34
|
+
the session on login. Expire idle sessions.
|
|
35
|
+
- JWTs: short expiry, verify signature and algorithm server-side, never trust
|
|
36
|
+
claims the client can edit.
|
|
37
|
+
|
|
38
|
+
## 3. Authorization — the most common real hole
|
|
39
|
+
|
|
40
|
+
- **Check ownership on every request**, on the server, for every object:
|
|
41
|
+
`WHERE id = $1 AND user_id = $session.user`. Changing an ID in a URL or body
|
|
42
|
+
must never reveal someone else's data (IDOR).
|
|
43
|
+
- Deny by default. Every route states who may call it.
|
|
44
|
+
- Hiding a button is not authorization; the endpoint behind it must check too.
|
|
45
|
+
|
|
46
|
+
## 4. Input validation and injection
|
|
47
|
+
|
|
48
|
+
- Validate every input at the boundary with a schema (zod, pydantic): type,
|
|
49
|
+
length, range, format. Reject, do not "clean".
|
|
50
|
+
- **SQL:** parameterized queries or an ORM only. Never string-build SQL with
|
|
51
|
+
user input.
|
|
52
|
+
- **Shell:** avoid it; if unavoidable, pass arguments as an array
|
|
53
|
+
(`execFile`, `spawn` without `shell: true`), never interpolate.
|
|
54
|
+
- **Paths:** resolve and verify the result stays inside the allowed directory;
|
|
55
|
+
reject `..` and absolute paths from users.
|
|
56
|
+
- **SSRF:** when fetching a user-supplied URL, allow-list hosts, block private
|
|
57
|
+
and link-local ranges (127.0.0.0/8, 10/8, 172.16/12, 192.168/16, 169.254/16,
|
|
58
|
+
::1), and do not follow redirects blindly.
|
|
59
|
+
|
|
60
|
+
## 5. Output: XSS
|
|
61
|
+
|
|
62
|
+
- Let the framework escape (React, templating engines). Treat
|
|
63
|
+
`dangerouslySetInnerHTML`, `innerHTML`, `v-html` and markdown-to-HTML as
|
|
64
|
+
red flags: sanitize with DOMPurify if you must render HTML.
|
|
65
|
+
- Never put user input into `href` without checking the scheme
|
|
66
|
+
(`javascript:` URLs), or into inline `<script>` or event handlers.
|
|
67
|
+
- Set a Content-Security-Policy where you can.
|
|
68
|
+
|
|
69
|
+
## 6. Requests
|
|
70
|
+
|
|
71
|
+
- **CSRF:** SameSite cookies plus a CSRF token or origin check on state-changing
|
|
72
|
+
requests that use cookie auth. Next.js server actions check origin; custom
|
|
73
|
+
routes need it done.
|
|
74
|
+
- **CORS:** never `*` with credentials; allow-list the origins that need it.
|
|
75
|
+
- **Rate limiting** on login, signup, password reset, and any expensive or paid
|
|
76
|
+
endpoint (AI calls especially) — per IP and per user.
|
|
77
|
+
|
|
78
|
+
## 7. File uploads
|
|
79
|
+
|
|
80
|
+
- Check type by content (magic bytes), not just the extension or MIME header.
|
|
81
|
+
- Enforce a size limit on the server, not only the client.
|
|
82
|
+
- Store outside the web root or in object storage with generated names; never
|
|
83
|
+
execute or serve uploads from the app's own origin as HTML.
|
|
84
|
+
- Strip metadata (EXIF location) from images when privacy matters.
|
|
85
|
+
|
|
86
|
+
## 8. Errors, headers, dependencies
|
|
87
|
+
|
|
88
|
+
- Errors to users are generic; details go to server logs. No stack traces in
|
|
89
|
+
responses.
|
|
90
|
+
- Headers: `Strict-Transport-Security`, `X-Content-Type-Options: nosniff`,
|
|
91
|
+
`Referrer-Policy: strict-origin-when-cross-origin`, `frame-ancestors` via CSP.
|
|
92
|
+
- Dependencies: `npm audit` (or the ecosystem equivalent), remove unused
|
|
93
|
+
packages, pin versions with a lockfile, be wary of new packages with few
|
|
94
|
+
downloads or typo-like names.
|
|
95
|
+
|
|
96
|
+
## 9. Auditing existing code
|
|
97
|
+
|
|
98
|
+
Search rather than read everything:
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
grep for: api[_-]?key|secret|token|password (hardcoded secrets)
|
|
102
|
+
dangerouslySetInnerHTML|innerHTML|eval\(|new Function
|
|
103
|
+
exec\(|execSync|shell: true|child_process
|
|
104
|
+
\$\{.*\}.*(SELECT|INSERT|UPDATE|DELETE) (string-built SQL)
|
|
105
|
+
fetch\(.*req\.|axios\(.*req\. (user-controlled URLs)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Then check every route for authentication and ownership checks. Report
|
|
109
|
+
findings ranked by severity with the file, the attack, and the fix — and fix
|
|
110
|
+
the critical ones first if asked to fix.
|