agentsdotmd 1.1.2 → 1.1.4
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 +9 -8
- package/bin/agentsdotmd.js +1 -3
- package/{changelog.md → changelogs.md} +1 -8
- package/core.md +13 -2
- package/dedent.md +16 -0
- package/fly.md +2 -0
- package/github.md +31 -4
- package/opentui.md +53 -0
- package/orb.md +64 -0
- package/package.json +1 -1
- package/prisma.md +24 -4
- package/react-router.md +0 -34
- package/react.md +21 -1
- package/retries-and-resumability.md +84 -0
- package/shadcn.md +5 -1
- package/stripe.md +37 -3
- package/tailwind.md +2 -0
- package/threejs.md +9 -0
- package/tiktoken.md +7 -0
- package/tmux.md +62 -0
- package/typescript.md +6 -3
- package/vercel-sandbox.md +85 -0
- package/vercel.md +108 -0
- package/vitest.md +10 -4
package/README
CHANGED
|
@@ -1,27 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
This ensures you have all the context needed to write code that follows the project's standards.
|
|
1
|
+
this repo has many documents i use in all my projects so i can share them and keep them updated in a centralized place.
|
|
3
2
|
|
|
4
|
-
##
|
|
3
|
+
## installation
|
|
5
4
|
|
|
6
5
|
```bash
|
|
7
6
|
npm install -g agentsdotmd
|
|
8
7
|
```
|
|
9
8
|
|
|
10
|
-
##
|
|
9
|
+
## usage
|
|
10
|
+
|
|
11
|
+
the agentsdotmd is just a clit to download files from github. it let you download some md files and merge them into an agents.md output
|
|
11
12
|
|
|
12
13
|
```bash
|
|
13
|
-
agentsdotmd core.md typescript.md pnpm.md react.md sentry.md vitest.md
|
|
14
|
+
agentsdotmd core.md typescript.md pnpm.md react.md tmux.md sentry.md vitest.md changelogs.md docs-writing.md doppler.md cac.md github.md prisma.md react-router.md shadcn.md tailwind.md lucide.md spiceflow.md vercel-ai-sdk.md playwright.md zod.md stripe.md gitchamber.md fly.md
|
|
14
15
|
```
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
this will generate an `agents.md` file with all the coding guidelines concatenated together.
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
you can also use local files by prefixing them with `./`:
|
|
19
20
|
|
|
20
21
|
```bash
|
|
21
22
|
agentsdotmd core.md ./my-custom-rules.md typescript.md
|
|
22
23
|
```
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
or use a different repository (a fork of this repo for example):
|
|
25
26
|
|
|
26
27
|
```bash
|
|
27
28
|
agentsdotmd --repo yourname/yourrepo file1.md file2.md
|
package/bin/agentsdotmd.js
CHANGED
|
@@ -50,9 +50,7 @@ cli
|
|
|
50
50
|
|
|
51
51
|
try {
|
|
52
52
|
const contents = await Promise.all(promises)
|
|
53
|
-
const header = `<!-- This file is
|
|
54
|
-
Do not edit this file directly. To add custom instructions, create a local file ./MY_AGENTS.md
|
|
55
|
-
and add it to your agentsdotmd command in package.json. -->\n\n`
|
|
53
|
+
const header = `<!-- This AGENTS.md file is generated. Look for an agents.md package.json script to see what files to update instead. -->\n\n`
|
|
56
54
|
const content = header + contents.join('\n') + '\n'
|
|
57
55
|
fs.writeFileSync('AGENTS.md', content)
|
|
58
56
|
console.log(`AGENTS.md generated successfully with ${files.length} file(s)`)
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
## 1.1.2
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
- Header comment in generated AGENTS.md instructing not to edit directly
|
|
8
|
-
- Instructions to create ./MY_AGENTS.md for custom instructions
|
|
1
|
+
# writing changelogs
|
|
9
2
|
|
|
10
3
|
after you make a change that is noteworthy, add an entry in the CHANGELOG.md file in the root of the package. there are 2 kinds of packages, public and private packages. private packages have a private: true field in package.json, public packages do not and instead have a version field in package.json. public packages are the ones that are published to npm.
|
|
11
4
|
|
package/core.md
CHANGED
|
@@ -4,17 +4,28 @@ when summarizing changes at the end of the message, be super short, a few words
|
|
|
4
4
|
|
|
5
5
|
please ask questions and confirm assumptions before generating complex architecture code.
|
|
6
6
|
|
|
7
|
-
NEVER run commands with & at the end to run them in the background. this is leaky and harmful! instead ask me to run commands in the background if needed.
|
|
7
|
+
NEVER run commands with & at the end to run them in the background. this is leaky and harmful! instead ask me to run commands in the background using tmux if needed.
|
|
8
8
|
|
|
9
9
|
NEVER commit yourself unless asked to do so. I will commit the code myself.
|
|
10
10
|
|
|
11
|
-
NEVER
|
|
11
|
+
NEVER use git to revert files to previous state if you did not create those files yourself! there can be user changes in files you touched, if you revert those changes the user will be very upset!
|
|
12
12
|
|
|
13
13
|
## files
|
|
14
14
|
|
|
15
15
|
always use kebab case for new filenames. never use uppercase letters in filenames
|
|
16
16
|
|
|
17
|
+
never write temporary files to /tmp. instead write them to a local ./tmp folder instead. make sure it is in .gitignore too
|
|
17
18
|
|
|
18
19
|
## see files in the repo
|
|
19
20
|
|
|
20
21
|
use `git ls-files | tree --fromfile` to see files in the repo. this command will ignore files ignored by git
|
|
22
|
+
|
|
23
|
+
## handling unexpected file contents after a read or write
|
|
24
|
+
|
|
25
|
+
if you find code that was not there since the last time you read the file it means the user or another agent edited the file. do not revert the changes that were added. instead keep them and integrate them with your new changes
|
|
26
|
+
|
|
27
|
+
IMPORTANT: NEVER commit your changes unless clearly and specifically asked to!
|
|
28
|
+
|
|
29
|
+
## opening me files in zed to show me a specific portion of code
|
|
30
|
+
|
|
31
|
+
you can open files when i ask me "open in zed the line where ..." using the command `zed path/to/file:line`
|
package/dedent.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
## dedent
|
|
2
|
+
|
|
3
|
+
when creating long strings in functions use dedent so that we can indent the string content and make it more readable
|
|
4
|
+
|
|
5
|
+
for example:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import dedent from 'string-dedent'
|
|
9
|
+
|
|
10
|
+
const content = dedent`
|
|
11
|
+
some content
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
IMPORTANT: notice that i have at start and end a new line. this is required when using string-dedent. Also notice npm package `string-dedent` instead of `dedent`.
|
|
15
|
+
|
|
16
|
+
When creating code snippets alias dedent to variables like html or javascript so that I get syntax highlight in my editor: `const html = dedent`
|
package/fly.md
CHANGED
|
@@ -4,6 +4,8 @@ fly is a deployment platform. some packages use it to deploy the website. you ca
|
|
|
4
4
|
|
|
5
5
|
usually there are 2 fly apps for each package, one staging environment and one production. these are 2 different apps at 2 different urls, you can target the right app usually by using `pnpm fly:preview` or `pnpm fly:prod`. sometimes there is only `pnpm fly` and you can use that instead. These scripts will append the right --app argument to work on the right fly app.
|
|
6
6
|
|
|
7
|
+
Never deploy with fly yourself. ask the user to do it
|
|
8
|
+
|
|
7
9
|
## reading logs
|
|
8
10
|
|
|
9
11
|
you can read fly apps logs using `pnpm fly:preview logs --no-tail | tail -n 100`
|
package/github.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# github
|
|
2
2
|
|
|
3
|
+
|
|
3
4
|
you can use the `gh` cli to do operations on github for the current repository. For example: open issues, open PRs, check actions status, read workflow logs, etc.
|
|
4
5
|
|
|
5
6
|
## creating issues and pull requests
|
|
@@ -34,16 +35,42 @@ Error: Request timeout at /api/auth/login
|
|
|
34
35
|
```bash
|
|
35
36
|
gh run list # lists latest actions runs
|
|
36
37
|
gh run watch <id> --exit-status # if workflow is in progress, wait for the run to complete. the actions run is finished when this command exits. Set a tiemout of at least 10 minutes when running this command
|
|
38
|
+
gh pr checks --watch --fail-fast # watch for current branch pr ci checks to finish
|
|
37
39
|
gh run view <id> --log-failed | tail -n 300 # read the logs for failed steps in the actions run
|
|
38
40
|
gh run view <id> --log | tail -n 300 # read all logs for a github actions run
|
|
39
41
|
```
|
|
40
42
|
|
|
41
|
-
##
|
|
43
|
+
## responding to PR reviews and comments (gh-pr-review extension)
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# view reviews and get thread IDs
|
|
47
|
+
gh pr-review review view 42 -R owner/repo --unresolved
|
|
48
|
+
|
|
49
|
+
# reply to a review comment
|
|
50
|
+
gh pr-review comments reply 42 -R owner/repo \
|
|
51
|
+
--thread-id PRRT_kwDOAAABbcdEFG12 \
|
|
52
|
+
--body "Fixed in latest commit"
|
|
53
|
+
|
|
54
|
+
# resolve a thread
|
|
55
|
+
gh pr-review threads resolve 42 -R owner/repo --thread-id PRRT_kwDOAAABbcdEFG12
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## listing, searching, reading github repos files with gitchamber
|
|
42
59
|
|
|
43
|
-
you
|
|
60
|
+
you MUST use gitchamber.com to read repo files. first ALWAYS run `curl https://gitchamber.com` to read detailed usage docs. always use curl to fetch the responses of gitchamber.com
|
|
44
61
|
|
|
45
62
|
for example when working with the vercel ai sdk, you can fetch the latest docs using:
|
|
46
63
|
|
|
47
|
-
https://gitchamber.com/repos/
|
|
64
|
+
https://gitchamber.com/repos/facebook/react/main/files
|
|
65
|
+
|
|
66
|
+
https://gitchamber.com/repos/remorses/fumabase/main/files?glob=**/*.ts
|
|
67
|
+
|
|
68
|
+
https://gitchamber.com/repos/facebook/react/main/files/README.md?start=10&end=50
|
|
69
|
+
|
|
70
|
+
https://gitchamber.com/repos/facebook/react/main/search/useState
|
|
48
71
|
|
|
49
|
-
|
|
72
|
+
gitchamber allows you to list, search and read files in a repo. you MUST use it over alternatives likes raw.github.com, because
|
|
73
|
+
- it allows you to use context usage better via limit and offset pagination
|
|
74
|
+
- it can list files, even filtering by a specific glob (default is *.md and *.mdx)
|
|
75
|
+
- it can search a repo for a specific substring
|
|
76
|
+
- it can show the code with line numbers for each line, letting you find a specific line number
|
package/opentui.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
|
|
2
|
+
## opentui
|
|
3
|
+
|
|
4
|
+
opentui is the framework used to render the tui, using react.
|
|
5
|
+
|
|
6
|
+
IMPORTANT! before starting every task ALWAYS read opentui docs with `curl -s https://raw.githubusercontent.com/sst/opentui/refs/heads/main/packages/react/README.md`
|
|
7
|
+
|
|
8
|
+
do this every time you have to edit .tsx files in the project.
|
|
9
|
+
|
|
10
|
+
## React
|
|
11
|
+
|
|
12
|
+
NEVER NEVER use forwardRef. it is not needed. instead just use a ref prop like React 19 best practice
|
|
13
|
+
|
|
14
|
+
NEVER pass function or callbacks as dependencies of useEffect, this will very easily cause infinite loops if you forget to use useCallback
|
|
15
|
+
|
|
16
|
+
NEVER use useCallback other than for ref callbacks. it is useless if we never pass functions in useEffect dependencies
|
|
17
|
+
|
|
18
|
+
Try to never use useEffect if possible. usually you can move logic directly in event handlers instead
|
|
19
|
+
|
|
20
|
+
This is not a plain react project, instead it is a project using opentui renderer, which supports box, group, textarea, etc
|
|
21
|
+
|
|
22
|
+
Styles are implemented via Yoga. there is a style prop to pass an object or you can also pass styles using a prop for each style (which is preferred)
|
|
23
|
+
|
|
24
|
+
Not all CSS and react style props are implemented. Only flexbox one.
|
|
25
|
+
|
|
26
|
+
To understand how to use these components read other files in the project. try to use the theme.tsx file for colors.
|
|
27
|
+
|
|
28
|
+
## text wrapping
|
|
29
|
+
|
|
30
|
+
text elements wrap by default. to disable this pass wrapMode="none"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## researching opentui patterns
|
|
34
|
+
|
|
35
|
+
you can read more examples of opentui react code using gitchamber by listing and reading files from the correct endpoint: https://gitchamber.com/repos/sst/opentui/main/files?glob=packages/react/examples/**
|
|
36
|
+
|
|
37
|
+
or for example to see how to use the `<code>` opentui element: https://gitchamber.com/repos/sst/opentui/main/search/<code?glob=\*\*
|
|
38
|
+
|
|
39
|
+
do something like this for every new element you want to use and not know about, for exampel `<scrollbox>`, to see examples
|
|
40
|
+
|
|
41
|
+
## keys
|
|
42
|
+
|
|
43
|
+
cdm modifier (named hyper in opentui) cannot be intercepted in opentui. because parent terminal app will not forward it. instead use alt or ctrl
|
|
44
|
+
|
|
45
|
+
enter key is named return in opentui. alt is option.
|
|
46
|
+
|
|
47
|
+
## overlapping text in boxes
|
|
48
|
+
|
|
49
|
+
if you see text elements too close to each other the issues is probably that the content does not fit in the box row so elements shrink and gaps or paddings are no longer respected.
|
|
50
|
+
|
|
51
|
+
to fix this issue add flexShrink={0} to all elements inside the row
|
|
52
|
+
|
|
53
|
+
this common when using wrapMode none.
|
package/orb.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# OrbStack Linux VM
|
|
2
|
+
|
|
3
|
+
use `orb` to run commands in a persistent linux VM. useful for linux-only debugging or isolated experiments.
|
|
4
|
+
|
|
5
|
+
## running commands
|
|
6
|
+
|
|
7
|
+
orb automatically uses your current mac directory and can access mac files directly:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
orb uname -a # run command in default machine
|
|
11
|
+
orb ls # lists files in current mac directory
|
|
12
|
+
orb ./script.sh # run a script from current directory
|
|
13
|
+
orb # open interactive shell
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## installing packages
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
orb sudo apt-get update
|
|
20
|
+
orb sudo apt-get install -y nodejs npm
|
|
21
|
+
orb node --version
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## file paths
|
|
25
|
+
|
|
26
|
+
mac directories are a shared filesystem (not synced). this means:
|
|
27
|
+
|
|
28
|
+
- changes are instant in both directions, no delay
|
|
29
|
+
- files created in orb (in mac paths) appear immediately on mac
|
|
30
|
+
- files created on mac appear immediately in orb
|
|
31
|
+
- it's the same file, not a copy
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
orb touch foo.txt # appears immediately on mac
|
|
35
|
+
touch bar.txt # appears immediately in orb
|
|
36
|
+
orb cat bar.txt # can read mac-created file instantly
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
path mapping:
|
|
40
|
+
|
|
41
|
+
- mac paths like `/Users/...` work directly in orb commands (auto-translated)
|
|
42
|
+
- from inside linux shell, mac files are also accessible at `/mnt/mac/...`
|
|
43
|
+
- files in `/home/morse/` are accessible from mac at `~/OrbStack/<machine>/home/morse/...`
|
|
44
|
+
- `/tmp/` is a tmpfs (RAM) - isolated from mac, lost on reboot
|
|
45
|
+
|
|
46
|
+
## avoiding file pollution
|
|
47
|
+
|
|
48
|
+
NEVER run commands in orb that create files in the current mac directory. since the filesystem is shared, these files will pollute your mac project:
|
|
49
|
+
|
|
50
|
+
- node_modules (linux binaries)
|
|
51
|
+
- build outputs (dist/, .next/, etc)
|
|
52
|
+
- downloads
|
|
53
|
+
- caches
|
|
54
|
+
|
|
55
|
+
instead, copy the project to `/tmp` and work there:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
orb cp -r . /tmp/linux-vm-myproject
|
|
59
|
+
orb bash -c 'cd /tmp/linux-vm-myproject && npm install'
|
|
60
|
+
orb bash -c 'cd /tmp/linux-vm-myproject && npm run build'
|
|
61
|
+
orb bash -c 'cd /tmp/linux-vm-myproject && npm test'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`/tmp` is a tmpfs (RAM filesystem) - fully isolated from mac but files are lost on VM reboot.
|
package/package.json
CHANGED
package/prisma.md
CHANGED
|
@@ -49,10 +49,10 @@ this simply means to always include a check in prisma queries to make sure that
|
|
|
49
49
|
|
|
50
50
|
```typescript
|
|
51
51
|
const resource = await prisma.resource.findFirst({
|
|
52
|
-
|
|
53
|
-
})
|
|
52
|
+
where: { resourceId, parentResource: { users: { some: { userId } } } },
|
|
53
|
+
});
|
|
54
54
|
if (!resource) {
|
|
55
|
-
|
|
55
|
+
throw new AppError(`cannot find resource`);
|
|
56
56
|
}
|
|
57
57
|
```
|
|
58
58
|
|
|
@@ -81,4 +81,24 @@ if (!user.subscription) {
|
|
|
81
81
|
JSON.stringify({ message: `user has no subscription` }),
|
|
82
82
|
)
|
|
83
83
|
}
|
|
84
|
-
````
|
|
84
|
+
````
|
|
85
|
+
|
|
86
|
+
## foreign key constraints
|
|
87
|
+
|
|
88
|
+
sometimes you will get errors like "Invalid `upsert()` invocation: Foreign key constraint violated on the constraint: `filed1_filed2_fkey`". This can be caused by the following issue
|
|
89
|
+
|
|
90
|
+
- a field that has a relation to table X is being passed a value where no table X exists for that id. You can fix the issue by making sure that the table exists before doing the create or upsert
|
|
91
|
+
- With upsert, even if the create branch is valid, the update branch can violate the FK.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
await prisma.child.upsert({
|
|
95
|
+
where: { id: 1 },
|
|
96
|
+
create: {
|
|
97
|
+
parent: { connect: { id: 1 } },
|
|
98
|
+
},
|
|
99
|
+
update: {
|
|
100
|
+
parent: { connect: { id: 9999 } }, // no such parent
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
-
|
package/react-router.md
CHANGED
|
@@ -201,40 +201,6 @@ so that internal navigation is done client side and is faster. notice that navig
|
|
|
201
201
|
|
|
202
202
|
ALWAYS use link components instead of the navigate function if possible. for example, in a dropdown component you should wrap the dropdown item in a link instead of adding an onClick handler.
|
|
203
203
|
|
|
204
|
-
# Creating New React Router Routes and Handling Types
|
|
205
|
-
|
|
206
|
-
When creating a new React Router route, follow these steps:
|
|
207
|
-
|
|
208
|
-
## 1. Create the route file
|
|
209
|
-
Create a file in `src/routes/` using flat routes naming convention (dots for separators, $ for params, kebab-case).
|
|
210
|
-
|
|
211
|
-
## 2. Generate types
|
|
212
|
-
**IMPORTANT**: Types are NOT automatically generated. After creating a route, run:
|
|
213
|
-
```bash
|
|
214
|
-
pnpm exec react-router typegen
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
## 3. Import Route types
|
|
218
|
-
```typescript
|
|
219
|
-
import type { Route } from './+types/your-route-name'
|
|
220
|
-
```
|
|
221
|
-
Note: The `+types` directory doesn't physically exist - it's virtual/generated.
|
|
222
|
-
|
|
223
|
-
## 4. Verify with typecheck
|
|
224
|
-
```bash
|
|
225
|
-
pnpm typecheck # This runs typegen first, then tsc
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
## Troubleshooting Missing Types
|
|
229
|
-
- Types missing? Run `pnpm exec react-router typegen`
|
|
230
|
-
- Import failing? Check filename matches import path exactly
|
|
231
|
-
- The `+types` directory is virtual - don't look for it in the filesystem
|
|
232
|
-
|
|
233
|
-
## Best Practices
|
|
234
|
-
- Always run `pnpm typecheck` after creating/modifying routes
|
|
235
|
-
- Export `Route` type from layout routes for child routes to import
|
|
236
|
-
- Use `href()` for all internal paths, even in redirects
|
|
237
|
-
|
|
238
204
|
## debugging build failures
|
|
239
205
|
|
|
240
206
|
when you build the website always pipe the output to a file so you can later grep inside it for errors. with `pnpm build 2>&1 | build.log`
|
package/react.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
- too many `useState` calls are bad. if some piece of state is dependent on other state just compute it as an expression in render. do not add new state unless strictly necessary. before adding a new useState to a component, use @think tool to think hard if you can instead: use expression with already existing local state, use expression with some global state, use expression with loader data, use expression with some other existing variable instead. for example if you need to show a popover when there is an error you should use the error as open state for the popover instead of adding new useState hook
|
|
14
14
|
|
|
15
|
-
- `useCallback` is bad. it should be always avoided.
|
|
15
|
+
- `useCallback` is bad. it should be always avoided unless for ref props. ref props ALWAYS need to be passed memoized functions or the component could remount on ever render!
|
|
16
16
|
|
|
17
17
|
- NEVER pass functions to useEffect or useMemo dependencies. when you start passing functions to hook dependencies you need to add useCallback everywhere in the code, useCallback is a virus that infects the codebase and should be ALWAYS avoided.
|
|
18
18
|
|
|
@@ -37,3 +37,23 @@
|
|
|
37
37
|
- non component code should be put in the src/lib folder.
|
|
38
38
|
|
|
39
39
|
- hooks should be put in the src/hooks.tsx file. do not create a new file for each new hook. also notice that you should never create custom hooks, only do it if asked for.
|
|
40
|
+
|
|
41
|
+
## zustand
|
|
42
|
+
|
|
43
|
+
zustand is the preferred way to created global React state. put it in files like state.ts or x-state.ts where x is something that describe a portion of app state in case of multiple global states or multiple apps
|
|
44
|
+
|
|
45
|
+
- NEVER add zustand state setter methods. instead use useStore.setState to set state. For example never add a method `setVariable` in the state type. Instead call `setState` directly
|
|
46
|
+
|
|
47
|
+
- zustand already merges new partial state with the previous state. NEVER DO `useStore.setState({ ...useStore.getInitialState(), ... })` unless for resetting state
|
|
48
|
+
|
|
49
|
+
## non controlled input components
|
|
50
|
+
|
|
51
|
+
some components do not have a value prop to set the value via React state. these are called uncontrolled components. Instead they usually let you get the current input value via ref. something like ref.current.value. They usually also have an onChange prop that let you know when the value changes
|
|
52
|
+
|
|
53
|
+
these usually have a initialValue or defaultValue to programmatically set the initial value of the input
|
|
54
|
+
|
|
55
|
+
when using these components you SHOULD not track their state via React: instead you should programmatically set their value and read their value via refs in event handlers
|
|
56
|
+
|
|
57
|
+
tracking uncontrolled inputs via React state means that you will need to add useEffect to programmatically change their value when our state changes. this is an anti pattern. instead you MUST keep in mind the uncontrolled input manages its own state and we interface with it via refs and initialValue prop.
|
|
58
|
+
|
|
59
|
+
using React state in these cases is only necessary if you have to show the input value during render. if that is not the case you can just use `inputRef.current.value` instead and set the value via `inputRef.current.value = something`
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Retryable, Resumable, Idempotent Tasks — Summary
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
- Long-running tasks (e.g.,Notion sync, AI chat, website generation) take minutes.
|
|
6
|
+
- Keeping a single connection open → network error probability grows with time.
|
|
7
|
+
- Tasks also do outbound network calls → compound failure surface.
|
|
8
|
+
- Risk of duplicate/concurrent runs corrupting state.
|
|
9
|
+
|
|
10
|
+
## Goals
|
|
11
|
+
|
|
12
|
+
- **Resumable**: continue from last checkpoint after failures.
|
|
13
|
+
- **Retryable**: safe to retry automatically.
|
|
14
|
+
- **Idempotent**: side effects via upserts/deterministic writes.
|
|
15
|
+
- **Single-flight**: prevent concurrent execution per task.
|
|
16
|
+
|
|
17
|
+
## Core Principles
|
|
18
|
+
|
|
19
|
+
- Checkpoint progress (cursor/step) frequently.
|
|
20
|
+
- Deterministic ordering (e.g., pages list) so “skip until cursor” is correct.
|
|
21
|
+
- At-least-once safe semantics: dedupe by (task_id, sequence/event_id).
|
|
22
|
+
- Small, explicit payload carrying resume info on every retry.
|
|
23
|
+
|
|
24
|
+
## Client vs Server State
|
|
25
|
+
|
|
26
|
+
### Client-held (prefer for AI chat / interactive flows)
|
|
27
|
+
|
|
28
|
+
- Client persists: messages[], partial assistant text, last_event_seq.
|
|
29
|
+
- Request includes: task_id (resume_token), messages[], partial_assistant, last_event_seq.
|
|
30
|
+
- Server is stateless: reconstructs from request; uses model “prefill” to resume (verify model support; Anthropic supports; OpenAI may vary).
|
|
31
|
+
- Benefit: fewer server-side moving parts. Cost: model compatibility + client must track emitted events.
|
|
32
|
+
|
|
33
|
+
### Server-held (required for background jobs / fixed retry bodies, e.g., QStash)
|
|
34
|
+
|
|
35
|
+
- DB state per task: {task_id, step, cursor (last_synced_page_id), checksum, updated_at}.
|
|
36
|
+
- Resume by skipping to cursor using deterministic ordering.
|
|
37
|
+
- Needed when infrastructure cannot modify retry payloads.
|
|
38
|
+
|
|
39
|
+
## Concurrency Control
|
|
40
|
+
|
|
41
|
+
- Single-flight/lease per task_id:
|
|
42
|
+
- Acquire lease with TTL + heartbeats.
|
|
43
|
+
- If a retry arrives while running: attach to existing run or reject with retry-after.
|
|
44
|
+
- Release lease on completion or expiry.
|
|
45
|
+
|
|
46
|
+
## API Shape (minimal)
|
|
47
|
+
|
|
48
|
+
- POST /tasks/start → {task_id, optional cursor}
|
|
49
|
+
- POST /tasks/resume → body must include:
|
|
50
|
+
- task_id (resume_token)
|
|
51
|
+
- last_cursor (e.g., last_synced_page_id)
|
|
52
|
+
- last_event_seq (for stream dedupe)
|
|
53
|
+
- client_state if server is stateless (e.g., messages[])
|
|
54
|
+
- GET /tasks/status?task_id=… → {step, cursor, percent}
|
|
55
|
+
|
|
56
|
+
## Retry Strategy
|
|
57
|
+
|
|
58
|
+
- Exponential backoff + jitter; cap total time.
|
|
59
|
+
- Persist checkpoint before/after each substantial step.
|
|
60
|
+
- Retries always include last_cursor + last_event_seq.
|
|
61
|
+
- Treat transport and model/API timeouts as retriable; invalid input as terminal.
|
|
62
|
+
|
|
63
|
+
## Event Replay (AI chat)
|
|
64
|
+
|
|
65
|
+
- All server events carry monotonically increasing seq.
|
|
66
|
+
- Client sends last_event_seq on resume; server suppresses ≤ seq to avoid duplicates.
|
|
67
|
+
|
|
68
|
+
## Sync Pattern
|
|
69
|
+
|
|
70
|
+
- Pre-compute deterministic ordered page list.
|
|
71
|
+
- Cursor = last_synced_page_id.
|
|
72
|
+
- On resume: “skip-until-cursor” then continue.
|
|
73
|
+
- Idempotent writes (upsert by external_id).
|
|
74
|
+
|
|
75
|
+
## Spice Flow Notes
|
|
76
|
+
|
|
77
|
+
- Current “retry same body” is insufficient; add ability to override body on retry.
|
|
78
|
+
- Temporary workaround: client-managed loop that updates last_cursor and re-posts.
|
|
79
|
+
|
|
80
|
+
## What to Verify
|
|
81
|
+
|
|
82
|
+
- Model prefill/continuation support for chosen LLM.
|
|
83
|
+
- Lease/lock implementation (row-level lock or key-value lease with TTL).
|
|
84
|
+
- Observability: log task_id, step, cursor, retry_count, lease_owner.
|
package/shadcn.md
CHANGED
|
@@ -16,4 +16,8 @@
|
|
|
16
16
|
|
|
17
17
|
this project uses shadcn components placed in the website/src/components/ui folder. never add a new shadcn component yourself by writing code. instead use the shadcn cli installed locally.
|
|
18
18
|
|
|
19
|
-
try to reuse these available components when you can, for example for buttons, tooltips, scroll areas, etc.
|
|
19
|
+
try to reuse these available components when you can, for example for buttons, tooltips, scroll areas, etc.
|
|
20
|
+
|
|
21
|
+
## reusing shadcn components
|
|
22
|
+
|
|
23
|
+
when creating a new React component or adding jsx before creating your own buttons or other elements first check the files inside `src/components/ui` and `src/components` to see what is already available. So you can reuse things like Button and Tooltip components instead of creating your own.
|
package/stripe.md
CHANGED
|
@@ -7,6 +7,31 @@ the Stripe billing portal is used to
|
|
|
7
7
|
- send user to payment to create a sub via `stripe.checkout.sessions.create`
|
|
8
8
|
- let user change plan, cancel or change payment method ("manage subscription") via `stripe.billingPortal.sessions.create`
|
|
9
9
|
|
|
10
|
+
## customerId
|
|
11
|
+
|
|
12
|
+
every time you are about to do a call to `checkout.sessions.create` make sure that we create the Stripe customer first. So that we do not get duplicate Stripe customers for different subscriptions:
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
|
|
16
|
+
let customerId = org.stripeCustomerId
|
|
17
|
+
|
|
18
|
+
if (!customerId) {
|
|
19
|
+
const customer = await stripe.customers.create({
|
|
20
|
+
email: org.email || undefined,
|
|
21
|
+
name: org.name || undefined,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
customerId = customer.id
|
|
25
|
+
|
|
26
|
+
await prisma.org.update({
|
|
27
|
+
where: { id: orgId },
|
|
28
|
+
data: { stripeCustomerId: customerId },
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
await stripe.checkout.sessions.create({ customer: customerId, ... })
|
|
33
|
+
```
|
|
34
|
+
|
|
10
35
|
## subscriptions
|
|
11
36
|
|
|
12
37
|
a subscription is active if state is in
|
|
@@ -14,6 +39,18 @@ a subscription is active if state is in
|
|
|
14
39
|
- trialing
|
|
15
40
|
- active
|
|
16
41
|
|
|
42
|
+
```ts
|
|
43
|
+
await prisma.subscription.findFirst({
|
|
44
|
+
where: {
|
|
45
|
+
orgId: orgId,
|
|
46
|
+
status: {
|
|
47
|
+
in: ["active", "trialing"],
|
|
48
|
+
},
|
|
49
|
+
// ...
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
17
54
|
a subscription can be reactivated if state is NOT in
|
|
18
55
|
|
|
19
56
|
- canceled
|
|
@@ -21,6 +58,3 @@ a subscription can be reactivated if state is NOT in
|
|
|
21
58
|
- unpaid
|
|
22
59
|
|
|
23
60
|
> If sub is in any of these states the user will not be able to use the billing portal to reactivate it. Meaning we should treat a subscription in these states as completely missing. Forcing the user to create a new one instead of shoging the "manage subscription" button that redirects the user to the billing portal. BUT customer id must be preserved, reusing previous sub customerId in `stripe.billingPortal.sessions.create({ customer: prevCustomerId })`
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
##
|
package/tailwind.md
CHANGED
|
@@ -11,3 +11,5 @@ for margin, padding, gaps, widths and heights it is preferable to use multiples
|
|
|
11
11
|
4 is equal to 16px which is the default font size of the page. this way every spacing is a multiple of the height and width of a default letter.
|
|
12
12
|
|
|
13
13
|
user interfaces are mostly text so using the letter width and height as a base unit makes it easier to reason about the layout and sizes.
|
|
14
|
+
|
|
15
|
+
use grow instead of flex-1.
|
package/threejs.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# three.js
|
|
2
|
+
|
|
3
|
+
to read three.js manual you can see available pages with
|
|
4
|
+
|
|
5
|
+
`curl -Ls https://gitchamber.com/repos/mrdoob/three.js/dev/files?glob=manual/en/**/*.html`
|
|
6
|
+
|
|
7
|
+
reading about TLS, to create webgpu shaders
|
|
8
|
+
|
|
9
|
+
`curl -L https://github.com/mrdoob/three.js/wiki/Three.js-Shading-Language.md`
|
package/tiktoken.md
ADDED
package/tmux.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
|
|
2
|
+
Use tmux to run long-lived background commands as background “tasks” like vite dev servers, commands with watch mode.
|
|
3
|
+
Each task should be a tmux **session** that the agent can start, inspect, and stop via CLI.
|
|
4
|
+
|
|
5
|
+
ALWAYS give long and descriptive names for the sessions, so other agents know what they are for.
|
|
6
|
+
|
|
7
|
+
Run a background task (e.g. Vite dev server) without blocking:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
tmux new-session -d -s project-name-vite-dev-port-8034 'cd /path/to/project && npm run dev --port 8034'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Every time you are about to start a new session, first check if there is one already.
|
|
14
|
+
|
|
15
|
+
List all background tasks (sessions):
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
tmux ls
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
You can assume sessions that do not have names were not started by you or agents so you can ignore them
|
|
22
|
+
|
|
23
|
+
Kill a background task:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
tmux kill-session -t vite-dev
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Never attach to a session. You are inside a non TTY terminal, meaning you instead will have to read the latest n logs instead.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
Fetch the last N log lines for a task without attaching (returns immediately):
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
tmux capture-pane -t vite-dev:0 -S -100 -p
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Example pattern for a coding agent:
|
|
39
|
+
|
|
40
|
+
1. Start a task:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
tmux new-session -d -s build 'cd /repo && npm run build'
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
2. Poll logs:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
tmux capture-pane -t build:0 -S -80 -p
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
3. List all running tasks:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
tmux ls
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
4. Stop a task when done:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
tmux kill-session -t build
|
|
62
|
+
```
|
package/typescript.md
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
- always add the {} block body in arrow functions: arrow functions should never be written as `onClick={(x) => setState('')}`. NEVER. instead you should ALWAYS write `onClick={() => {setState('')}}`. this way it's easy to add new statements in the arrow function without refactoring it.
|
|
8
8
|
|
|
9
|
+
- in array operations .map, .filter, .reduce and .flatMap are preferred over .forEach and for of loops. For example prefer doing `.push(...array.map(x => x.items))` over mutating array variables inside for loops. Always think of how to turn for loops into expressions using .map, .filter or .flatMap if you ever are about to write a for loop.
|
|
10
|
+
|
|
11
|
+
- if you encounter typescript errors like "undefined | T is not assignable to T" after .filter(Boolean) operations: use a guarded function instead of Boolean: `.filter(isTruthy)`. implemented as `function isTruthy<T>(value: T): value is NonNullable<T> { return Boolean(value) }`
|
|
12
|
+
|
|
9
13
|
- minimize useless comments: do not add useless comments if the code is self descriptive. only add comments if requested or if this was a change that i asked for, meaning it is not obvious code and needs some inline documentation. if a comment is required because the part of the code was result of difficult back and forth with me, keep it very short.
|
|
10
14
|
|
|
11
15
|
- ALWAYS add all information encapsulated in my prompt to comments: when my prompt is super detailed and in depth, all this information should be added to comments in your code. this is because if the prompt is very detailed it must be the fruit of a lot of research. all this information would be lost if you don't put it in the code. next LLM calls would misinterpret the code and miss context.
|
|
@@ -20,15 +24,13 @@
|
|
|
20
24
|
|
|
21
25
|
- NEVER do `(x as any).field` or `'field' in x` before checking if the code compiles first without it. the code probably doesn't need any or the in check. even if it does not compile, use think tool first! before adding (x as any).something, ALWAYS read the .d.ts to understand the types
|
|
22
26
|
|
|
23
|
-
- after any change to typescript code ALWAYS run the `pnpm typecheck` script of that package, or if there is no typecheck script run `pnpm tsc` yourself
|
|
24
|
-
|
|
25
27
|
- do not declare uninitialized variables that are defined later in the flow. instead use an IIFE with returns. this way there is less state. also define the type of the variable before the iife. here is an example:
|
|
26
28
|
|
|
27
29
|
- use || over in: avoid 'x' in obj checks. prefer doing `obj?.x || ''` over doing `'x' in obj ? obj.x : ''`. only use the in operator if that field causes problems in typescript checks because typescript thinks the field is missing, as a last resort.
|
|
28
30
|
|
|
29
31
|
- when creating urls from a path and a base url, prefer using `new URL(path, baseUrl).toString()` instead of normal string interpolation. use type-safe react-router `href` or spiceflow `this.safePath` (available inside routes) if possible
|
|
30
32
|
|
|
31
|
-
- for node built-in imports, never import singular names. instead do `import fs from 'node:fs'`, same for path, os, etc.
|
|
33
|
+
- for node built-in imports, never import singular exported names. instead do `import fs from 'node:fs'`, same for path, os, etc.
|
|
32
34
|
|
|
33
35
|
- NEVER start the development server with pnpm dev yourself. there is no reason to do so, even with &
|
|
34
36
|
|
|
@@ -36,6 +38,7 @@
|
|
|
36
38
|
|
|
37
39
|
- if you encounter typescript lint errors for an npm package, read the node_modules/package/\*.d.ts files to understand the typescript types of the package. if you cannot understand them, ask me to help you with it.
|
|
38
40
|
|
|
41
|
+
- NEVER silently suppress errors in catch {} blocks if they contain more than one function call
|
|
39
42
|
```ts
|
|
40
43
|
// BAD. DO NOT DO THIS
|
|
41
44
|
let favicon: string | undefined;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Vercel Sandbox
|
|
2
|
+
|
|
3
|
+
use `sandbox` CLI to run commands in ephemeral remote Linux VMs. useful for isolated code execution, testing untrusted code, or running agent-generated scripts safely.
|
|
4
|
+
|
|
5
|
+
## running commands
|
|
6
|
+
|
|
7
|
+
sandbox runs commands non-interactively (no TTY needed):
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
sandbox run echo "hello world" # one-shot command
|
|
11
|
+
sandbox run node -e "console.log(2+2)" # run node code
|
|
12
|
+
sandbox run python -c "print('hello')" # run python (use --runtime python3.13)
|
|
13
|
+
sandbox run --rm ls -la # auto-cleanup after command
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
for multiple commands, manage sandbox lifecycle manually:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
ID=$(sandbox create -q) # create sandbox, get ID
|
|
20
|
+
sandbox exec $ID node -e "console.log('step 1')"
|
|
21
|
+
sandbox exec $ID node -e "console.log('step 2')"
|
|
22
|
+
sandbox stop $ID # cleanup
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## installing packages
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
sandbox run --sudo dnf install -y golang # system packages (Amazon Linux)
|
|
29
|
+
sandbox run npm install express # npm packages
|
|
30
|
+
sandbox run pip install requests # python packages (with --runtime python3.13)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
available runtimes: `node22` (default), `python3.13`
|
|
34
|
+
|
|
35
|
+
## file paths
|
|
36
|
+
|
|
37
|
+
sandbox has an isolated filesystem. the writable directory is `/vercel/sandbox`:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
sandbox exec $ID touch /vercel/sandbox/foo.txt # works
|
|
41
|
+
sandbox exec $ID touch /home/test.txt # permission denied
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## copying files
|
|
45
|
+
|
|
46
|
+
use `sandbox cp` to transfer files between local and remote:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# copy local file to sandbox
|
|
50
|
+
sandbox cp ./script.js $ID:/vercel/sandbox/
|
|
51
|
+
|
|
52
|
+
# copy from sandbox to local
|
|
53
|
+
sandbox cp $ID:/vercel/sandbox/output.txt ./
|
|
54
|
+
|
|
55
|
+
# copy directory
|
|
56
|
+
sandbox cp -r ./src $ID:/vercel/sandbox/
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## reading logs
|
|
60
|
+
|
|
61
|
+
command output is returned directly from `sandbox exec` and `sandbox run`:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# stdout is printed directly
|
|
65
|
+
sandbox run node -e "console.log('hello')"
|
|
66
|
+
# Output: hello
|
|
67
|
+
|
|
68
|
+
# capture output in a variable
|
|
69
|
+
OUTPUT=$(sandbox exec $ID node -e "console.log(JSON.stringify({ok:true}))")
|
|
70
|
+
echo $OUTPUT
|
|
71
|
+
|
|
72
|
+
# stderr is also printed
|
|
73
|
+
sandbox run node -e "console.error('warning')"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
for long-running processes or debugging:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# write logs to file, then retrieve
|
|
80
|
+
sandbox exec $ID bash -c "node app.js > /vercel/sandbox/app.log 2>&1"
|
|
81
|
+
sandbox cp $ID:/vercel/sandbox/app.log ./
|
|
82
|
+
|
|
83
|
+
# or tail logs in real-time (requires -t for streaming)
|
|
84
|
+
sandbox exec -t $ID tail -f /vercel/sandbox/app.log
|
|
85
|
+
```
|
package/vercel.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vercel
|
|
3
|
+
description: List deployments, check build status, and stream runtime logs with Vercel CLI
|
|
4
|
+
tags: [vercel, deployment, logs, cli]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# vercel
|
|
8
|
+
|
|
9
|
+
use the vercel cli to list deployments, check build status, and read runtime logs.
|
|
10
|
+
|
|
11
|
+
## listing deployments
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
vercel list # list recent deployments
|
|
15
|
+
vercel list --prod # list only production deployments
|
|
16
|
+
vercel list --limit 5 # limit results
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## deployment status and build logs
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
vercel inspect <deployment-url-or-id> # get deployment info and status
|
|
23
|
+
vercel inspect <deployment-url-or-id> --logs # print build logs
|
|
24
|
+
vercel inspect <deployment-url-or-id> --logs --wait # wait for build to complete, stream logs
|
|
25
|
+
vercel inspect <deployment-url-or-id> --wait --timeout=5m # wait with timeout
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## reading runtime logs
|
|
29
|
+
|
|
30
|
+
runtime logs only stream new logs from when you start the command. there is no way to fetch historical logs via cli.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
vercel logs <deployment-url-or-id> # stream logs for 5 minutes
|
|
34
|
+
vercel logs <deployment-url-or-id> --json # output as json (useful for filtering)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
> the `--since`, `--limit`, and `--follow` options are deprecated and ignored. use the vercel dashboard for historical logs.
|
|
38
|
+
|
|
39
|
+
## reading logs for latest deployment
|
|
40
|
+
|
|
41
|
+
get the latest production deployment url and stream its logs:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
DEPLOY_URL=$(vercel list --prod --limit 1 | tail -n 1 | awk '{print $1}')
|
|
45
|
+
vercel logs "$DEPLOY_URL"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
for preview deployments:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
DEPLOY_URL=$(vercel list --limit 1 | tail -n 1 | awk '{print $1}')
|
|
52
|
+
vercel logs "$DEPLOY_URL"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## background log streaming with tmux
|
|
56
|
+
|
|
57
|
+
since `vercel logs` streams indefinitely (up to 5 minutes), use tmux to run it in background while you trigger errors. see tmux.md for more details.
|
|
58
|
+
|
|
59
|
+
1. get latest deployment url:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
DEPLOY_URL=$(vercel list --prod --limit 1 | tail -n 1 | awk '{print $1}')
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
2. start log streaming in background:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
tmux new-session -d -s vercel-logs-prod "vercel logs $DEPLOY_URL --json"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
3. trigger an error (e.g. hit an endpoint that fails):
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
curl -s "https://$DEPLOY_URL/api/some-endpoint" || true
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
4. read captured logs:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
tmux capture-pane -t vercel-logs-prod:0 -S -100 -p
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
5. filter for errors with jq:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
tmux capture-pane -t vercel-logs-prod:0 -S -200 -p | jq -s 'map(select(.level == "error"))'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
6. kill the session when done:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
tmux kill-session -t vercel-logs-prod
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## timeout wrapper
|
|
96
|
+
|
|
97
|
+
if you need logs for a fixed duration without tmux:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
timeout 30s vercel logs <deployment-url> --json > logs.json
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## limitations
|
|
104
|
+
|
|
105
|
+
- runtime logs stream only, no historical fetch via cli
|
|
106
|
+
- logs retained 1 hour (cli) or 3 days (dashboard)
|
|
107
|
+
- build logs stored indefinitely (truncated at 4MB)
|
|
108
|
+
- for long-term storage use log drains (pro/enterprise)
|
package/vitest.md
CHANGED
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
# testing
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
.toMatchInlineSnapshot is the preferred way to write tests. leave them empty the first time, update them with -u. check git diff for the test file every time you update them with -u
|
|
4
|
+
|
|
5
|
+
never use timeouts longer than 5 seconds for expects and other statements timeouts. increase timeouts for tests if required, up to 1 minute
|
|
6
|
+
|
|
7
|
+
do not create dumb tests that test nothing. do not write tests if there is not already a test file or describe block for that function or module.
|
|
8
|
+
|
|
9
|
+
if the inputs for the tests is an array of repetitive fields and long content, generate this input data programmatically instead of hardcoding everything. only hardcode the important parts and generate other repetitive fields in a .map or .reduce
|
|
4
10
|
|
|
5
11
|
tests should validate complex and non-obvious logic. if a test looks like a placeholder, do not add it.
|
|
6
12
|
|
|
7
|
-
use vitest to run tests. tests should be run from the current package directory and not root. try using the test script instead of vitest directly. additional vitest flags can be added at the end, like --run to disable watch mode or -u to update snapshots.
|
|
13
|
+
use vitest or bun test to run tests. tests should be run from the current package directory and not root. try using the test script instead of vitest directly. additional vitest flags can be added at the end, like --run to disable watch mode or -u to update snapshots.
|
|
8
14
|
|
|
9
15
|
to understand how the code you are writing works, you should add inline snapshots in the test files with expect().toMatchInlineSnapshot(), then run the test with `pnpm test -u --run` or `pnpm vitest -u --run` to update the snapshot in the file, then read the file again to inspect the result. if the result is not expected, update the code and repeat until the snapshot matches your expectations. never write the inline snapshots in test files yourself. just leave them empty and run `pnpm test -u --run` to update them.
|
|
10
16
|
|
|
11
17
|
> always call `pnpm vitest` or `pnpm test` with `--run` or they will hang forever waiting for changes!
|
|
12
18
|
> ALWAYS read back the test if you use the `-u` option to make sure the inline snapshots are as you expect.
|
|
13
19
|
|
|
14
|
-
- NEVER
|
|
20
|
+
- NEVER write the snapshots content yourself in `toMatchInlineSnapshot`. instead leave it as is and call `pnpm test -u` to fill in snapshots content. the first time you call `toMatchInlineSnapshot()` you can leave it empty
|
|
15
21
|
|
|
16
22
|
- when updating implementation and `toMatchInlineSnapshot` should change, DO NOT remove the inline snapshots yourself, just run `pnpm test -u` instead! This will replace contents of the snapshots without wasting time doing it yourself.
|
|
17
23
|
|
|
18
24
|
- for very long snapshots you should use `toMatchFileSnapshot(filename)` instead of `toMatchInlineSnapshot()`. put the snapshot files in a snapshots/ directory and use the appropriate extension for the file based on the content
|
|
19
25
|
|
|
20
|
-
never test client react components. only
|
|
26
|
+
never test client react components. only React and browser independent code.
|
|
21
27
|
|
|
22
28
|
most tests should be simple calls to functions with some expect calls, no mocks. test files should be called the same as the file where the tested function is being exported from.
|
|
23
29
|
|