agentsdotmd 1.0.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 ADDED
@@ -0,0 +1,31 @@
1
+ IMPORTANT: As your first step, ALWAYS run this bash script to fetch ALL the necessary coding guidelines and rules for the technologies used in this project.
2
+ This ensures you have all the context needed to write code that follows the project's standards.
3
+
4
+ ```bash
5
+ #!/bin/bash
6
+
7
+ # Fetch all coding guidelines
8
+ for f in \
9
+ core.md \
10
+ typescript.md \
11
+ pnpm.md \
12
+ react.md \
13
+ sentry.md \
14
+ vitest.md \
15
+ changelog.md \
16
+ docs-writing.md \
17
+ doppler.md \
18
+ cac.md \
19
+ github.md \
20
+ prisma.md \
21
+ react-router.md \
22
+ shadcn.md \
23
+ tailwind.md \
24
+ lucide.md \
25
+ spiceflow.md \
26
+ vercel-ai-sdk.md \
27
+ playwright.md \
28
+ zod.md; do
29
+ curl -fsSL "https://raw.githubusercontent.com/remorses/AGENTS.md/main/$f"; printf '\n'
30
+ done
31
+ ```
package/cac.md ADDED
@@ -0,0 +1,3 @@
1
+ # cac for cli development
2
+
3
+ the cli uses cac npm package.
package/changelog.md ADDED
@@ -0,0 +1,42 @@
1
+ # changelog
2
+
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.
4
+
5
+ If the current package has a version field and it is not private then include the version in the changelog too like in the examples, otherwise use the current date and time.
6
+
7
+ If you use the version you MUST use a bumped version compared to the current package.json version, and you should update the package.json version field to that version. But do not publish. I will handle that myself.
8
+
9
+ to write a changelog.md file for a public package, use the following format, add a heading with the new version and a bullet list of your changes, like this:
10
+
11
+ ```md
12
+ ## 0.1.3
13
+
14
+ ### Patch Changes
15
+
16
+ - bug fixes
17
+
18
+ ## 0.1.2
19
+
20
+ ### Patch Changes
21
+
22
+ - add support for githubPath
23
+ ```
24
+
25
+ for private packages, which do not have versions, you must instead use the current date and time, for example:
26
+
27
+ ```md
28
+ # Changelog
29
+
30
+ ## 2025-01-24 19:50
31
+
32
+ - Added a feature to improve user experience
33
+ - Fixed a bug that caused the app to crash on startup
34
+ ```
35
+
36
+ these are just examples. be clear and concise in your changelog entries.
37
+
38
+ use present tense. be detailed but concise, omit useless verbs like "implement", "added", just put the subject there instead, so it is shorter. it's implicit we are adding features or fixes. do not use nested bullet points. always show example code snippets if applicable, and use proper markdown formatting.
39
+
40
+ ```
41
+
42
+ the website package has a dependency on docs-website. instead of duplicating code that is needed both in website and docs-website keep a file in docs-website instead and import from there for the website package.
package/core.md ADDED
@@ -0,0 +1,20 @@
1
+ # core guidelines
2
+
3
+ when summarizing changes at the end of the message, be super short, a few words and in bullet points, use bold text to highlight important keywords. use markdown.
4
+
5
+ please ask questions and confirm assumptions before generating complex architecture code.
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.
8
+
9
+ NEVER commit yourself unless asked to do so. I will commit the code myself.
10
+
11
+ NEVER add comments unless I tell you
12
+
13
+ ## files
14
+
15
+ always use kebab case for new filenames. never use uppercase letters in filenames
16
+
17
+
18
+ ## see files in the repo
19
+
20
+ use `git ls-files | tree --fromfile` to see files in the repo. this command will ignore files ignored by git
@@ -0,0 +1,3 @@
1
+ # writing docs
2
+
3
+ when generating a .md or .mdx file to document things, always add a frontmatter with title and description. also add a prompt field with the exact prompt used to generate the doc. use @ to reference files and urls and provide any context necessary to be able to recreate this file from scratch using a model. if you used urls also reference them. reference all files you had to read to create the doc. use yaml | syntax to add this prompt and never go over the column width of 80
package/doppler.md ADDED
@@ -0,0 +1,5 @@
1
+ # secrets
2
+
3
+ this project uses doppler to manage secrets, with a single project with 3 envs: dev, preview and production. dev is the env already selected and implicit in doppler calls.
4
+
5
+ in typescript never use process.env directly. instead find the closest `env.ts` file that exports an env object (this file should already exist). so the env can be used type-safely and i can clearly see which secrets are available and need to be added.
package/github.md ADDED
@@ -0,0 +1,26 @@
1
+ # github
2
+
3
+ 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
+ ## get current github repo
6
+
7
+ `git config --get remote.origin.url`
8
+
9
+ ## checking status of latest github actions workflow run
10
+
11
+ ```bash
12
+ gh run list # lists latest actions runs
13
+ 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
14
+ gh run view <id> --log-failed | tail -n 300 # read the logs for failed steps in the actions run
15
+ gh run view <id> --log | tail -n 300 # read all logs for a github actions run
16
+ ```
17
+
18
+ ## reading github repositories
19
+
20
+ you can use gitchamber.com to read repo files. run `curl https://gitchamber.com` to see how the API works. always use curl to fetch the responses of gitchamber.com
21
+
22
+ for example when working with the vercel ai sdk, you can fetch the latest docs using:
23
+
24
+ https://gitchamber.com/repos/repos/vercel/ai/main/files
25
+
26
+ use gitchamber to read the .md files using curl
package/lucide.md ADDED
@@ -0,0 +1,3 @@
1
+ # lucide icons
2
+
3
+ use lucide-react to import icons. always add the Icon import name, for example `ImageIcon` instead of just `Image`.
package/package.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "agentsdotmd",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "keywords": [],
10
+ "author": "remorses",
11
+ "license": "ISC",
12
+ "packageManager": "pnpm@10.18.1"
13
+ }
package/playwright.md ADDED
@@ -0,0 +1,5 @@
1
+ # playwright
2
+
3
+ you can control the browser using the playwright mcp tools. these tools let you control the browser to get information or accomplish actions
4
+
5
+ if i ask you to test something in the browser, know that the website dev server is already running at http://localhost:7664 for website and :7777 for docs-website (but docs-website needs to use the website domain specifically, for example name-hash.localhost:7777)
package/pnpm.md ADDED
@@ -0,0 +1,120 @@
1
+ # package manager: pnpm with workspace
2
+
3
+ this project uses pnpm workspaces to manage dependencies. important scripts are in the root package.json or various packages' package.json
4
+
5
+ try to run commands inside the package folder that you are working on. for example you should never run `pnpm test` from the root
6
+
7
+ if you need to install packages always use pnpm
8
+
9
+ instead of adding packages directly in package.json use `pnpm install package` inside the right workspace folder. NEVER manually add a package by updating package.json
10
+
11
+ ## updating a package
12
+
13
+ when i ask you to update a package always run `pnpm update -r packagename`. to update to latest also add --latest
14
+
15
+ Do not do `pnpm add packagename` to update a package. only to add a missing one. otherwise other packages versions will get out of sync.
16
+
17
+ ## fixing duplicate pnpm dependencies
18
+
19
+ sometimes typescript will fail if there are 2 duplicate packages in the workspace node_modules. this can happen in pnpm if a package is used in 2 different places (even if inside a node_module package, transitive dependency) with a different set of versions for a peer dependency
20
+
21
+ for example if better-auth depends on zod peer dep and zod is in different versions in 2 dependency subtrees
22
+
23
+ to identify if a pnpm package is duplicated, search for the string " packagename@" inside `pnpm-lock.yaml`, notice the space in the search string. then if the result returns multiple instances with a different set of peer deps inside the round brackets, it means that this package is being duplicated. here is an example of a package getting duplicated:
24
+
25
+ ```
26
+
27
+ better-auth@1.3.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(zod@3.25.76):
28
+ dependencies:
29
+ '@better-auth/utils': 0.2.6
30
+ '@better-fetch/fetch': 1.1.18
31
+ '@noble/ciphers': 0.6.0
32
+ '@noble/hashes': 1.8.0
33
+ '@simplewebauthn/browser': 13.1.2
34
+ '@simplewebauthn/server': 13.1.2
35
+ better-call: 1.0.13
36
+ defu: 6.1.4
37
+ jose: 5.10.0
38
+ kysely: 0.28.5
39
+ nanostores: 0.11.4
40
+ zod: 3.25.76
41
+ optionalDependencies:
42
+ react: 19.1.1
43
+ react-dom: 19.1.1(react@19.1.1)
44
+
45
+ better-auth@1.3.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(zod@4.0.17):
46
+ dependencies:
47
+ '@better-auth/utils': 0.2.6
48
+ '@better-fetch/fetch': 1.1.18
49
+ '@noble/ciphers': 0.6.0
50
+ '@noble/hashes': 1.8.0
51
+ '@simplewebauthn/browser': 13.1.2
52
+ '@simplewebauthn/server': 13.1.2
53
+ better-call: 1.0.13
54
+ defu: 6.1.4
55
+ jose: 5.10.0
56
+ kysely: 0.28.5
57
+ nanostores: 0.11.4
58
+ zod: 4.0.17
59
+ optionalDependencies:
60
+ react: 19.1.1
61
+ react-dom: 19.1.1(react@19.1.1)
62
+
63
+ ```
64
+
65
+ as you can see, better-auth is listed twice with different sets of peer deps. in this case it's because of zod being in version 3 and 4 in two subtrees of our workspace dependencies.
66
+
67
+ as a first step, try running `pnpm dedupe better-auth` with your package name and see if there is still the problem.
68
+
69
+ below i will describe how to generally deduplicate a package. i will use zod as an example. it works with any dependency found in the previous step.
70
+
71
+ to deduplicate the package, we have to make sure we only have 1 version of zod installed in your workspace. DO NOT use overrides for this. instead, fix the problem by manually updating the dependencies that are forcing the older version of zod in the dependency tree.
72
+
73
+ to do so, we first have to run the command `pnpm -r why zod@3.25.76` to see the reason the older zod version is installed. in this case, the result is something like this:
74
+
75
+ ```
76
+
77
+ website /Users/morse/Documents/GitHub/holocron/website (PRIVATE)
78
+
79
+ dependencies:
80
+ @better-auth/stripe 1.2.10
81
+ ├─┬ better-auth 1.3.6
82
+ │ └── zod 3.25.76 peer
83
+ └── zod 3.25.76
84
+ db link:../db
85
+ └─┬ docs-website link:../docs-website
86
+ ├─┬ fumadocs-docgen 2.0.1
87
+ │ └── zod 3.25.76
88
+ ├─┬ fumadocs-openapi link:../fumadocs/packages/openapi
89
+ │ └─┬ @modelcontextprotocol/sdk 1.17.3
90
+ │ ├── zod 3.25.76
91
+ │ └─┬ zod-to-json-schema 3.24.6
92
+ │ └── zod 3.25.76 peer
93
+ └─┬ searchapi link:../searchapi
94
+ └─┬ agents 0.0.109
95
+ ├─┬ @modelcontextprotocol/sdk 1.17.3
96
+ │ ├── zod 3.25.76
97
+ │ └─┬ zod-to-json-schema 3.24.6
98
+ │ └── zod 3.25.76 peer
99
+ └─┬ ai 4.3.19
100
+ ├─┬ @ai-sdk/provider-utils 2.2.8
101
+ │ └── zod 3.25.76 peer
102
+ └─┬ @ai-sdk/react 1.2.12
103
+ ├─┬ @ai-sdk/provider-utils 2.2.8
104
+ │ └── zod 3.25.76 peer
105
+ └─┬ @ai-sdk/ui-utils 1.2.11
106
+ └─┬ @ai-sdk/provider-utils 2.2.8
107
+ └── zod 3.25.76 peer
108
+ ```
109
+
110
+ here we can see zod 3 is installed because of @modelcontextprotocol/sdk, @better-auth/stripe and agents packages. to fix the problem, we can run
111
+
112
+ ```
113
+ pnpm update -r --latest @modelcontextprotocol/sdk @better-auth/stripe agents
114
+ ```
115
+
116
+ this way, if these packages include the newer version of the dependency, zod will be deduplicated automatically.
117
+
118
+ in this case, we could have only updated @better-auth/stripe to fix the issue too, that's because @better-auth/stripe is the one that has better-auth as a peer dep. but finding what is the exact problematic package is difficult, so it is easier to just update all packages you notice that we depend on directly in our workspace package.json files.
119
+
120
+ if after doing this we still have duplicate packages, you will have to ask the user for help. you can try deleting the node_modules and restarting the approach, but it rarely helps.
package/prisma.md ADDED
@@ -0,0 +1,84 @@
1
+ # prisma
2
+
3
+ this project uses prisma to interact with the database. if you need to add new queries always read the schema.prisma inside the db folder first so you understand the shape of the tables in the database.
4
+
5
+ never add new tables to the prisma schema, instead ask me to do so.
6
+
7
+ prisma upsert calls are preferable over updates, so that you also handle the case where the row is missing.
8
+
9
+ never make changes to schema.prisma yourself, instead propose a change with a message and ask me to do it. this file is too important to be edited by agents.
10
+
11
+ NEVER run `pnpm push` in db or commands like `pnpm prisma db push` or other prisma commands that mutate the database!
12
+
13
+ ### prisma queries for relations
14
+
15
+ - NEVER add more than 1 include nesting. this is very bad for performance because prisma will have to do the query to get the relation sequentially. instead of adding a new nested `include` you should add a new prisma query and wrap them in a `Promise.all`
16
+
17
+ ### prisma transactions for complex relations inserts
18
+
19
+ for very complex updates or inserts that involve more than 3 related tables, for example a Chat with ChatMessages and ChatMessagePath, you should use transaction instead of a super complex single query:
20
+
21
+ - start a transaction
22
+ - delete the parent table, the one with cascade deletes, so that the related tables are also deleted
23
+ - recreate all the tables again, reuse the old existing rows data when you don't have all the fields available
24
+ - make sure to create all the rows in the related tables. use for loops if necessary
25
+
26
+ ### prisma, always make sure user has access to prisma tables
27
+
28
+ > IMPORTANT! always read the schema.prisma file before adding a new prisma query, to understand how to structure it
29
+
30
+ try to never write sql by hand, use prisma
31
+
32
+ if a query becomes too complex because fetching too deeply into related tables (more than 1 `include` nesting), use different queries instead, put them in a Promise.all
33
+
34
+ ### prisma, concurrency
35
+
36
+ when doing prisma queries or other async operations try to parallelize them using Promise.all
37
+
38
+ this will speed up operations that can be done concurrently.
39
+
40
+ this is especially important in react-router loaders
41
+
42
+ ### prisma security
43
+
44
+ all loaders, actions and spiceflow routes of the project should have authorization checks.
45
+
46
+ these checks should check that the current user, identified by userId, has access to the fetched and updated rows.
47
+
48
+ this simply means to always include a check in prisma queries to make sure that the user has access to the updated or queried rows, for example:
49
+
50
+ ```typescript
51
+ const resource = await prisma.resource.findFirst({
52
+ where: { resourceId, parentResource: { users: { some: { userId } } } },
53
+ })
54
+ if (!resource) {
55
+ throw new AppError(`cannot find resource`)
56
+ }
57
+ ```
58
+
59
+ ### prisma transactions
60
+
61
+ NEVER use prisma interactive transactions (passing a function to `prisma.$transaction`), instead pass an array of operations. this is basically the same thing, operations are executed in order, but it has much better performance.
62
+
63
+ if you need to use complex logic to construct the array of operations, create an empty array using `const operations: Prisma.PrismaPromise<any>[]` first, then push to this array the queries you want to execute
64
+
65
+ > IMPORTANT! while constructing the operations array you should never call await in between, this would cause the prisma query to start and would make the transaction invalid.
66
+
67
+ ````typescript
68
+
69
+ ## errors
70
+
71
+ if you throw an error that is not unexpected you should use the `AppError` class, this way I can skip sending these errors to Sentry in the `notifyError` function
72
+
73
+ for example for cases where a resource is not found or user has no subscription.
74
+
75
+ you can even throw response errors, for example:
76
+
77
+ ```typescript
78
+ if (!user.subscription) {
79
+ throw new ResponseError(
80
+ 403,
81
+ JSON.stringify({ message: `user has no subscription` }),
82
+ )
83
+ }
84
+ ````
@@ -0,0 +1,240 @@
1
+ # react router v7
2
+
3
+ the website uses react-router v7.
4
+
5
+ NEVER start the dev server yourself with `pnpm dev`, instead ask me to do so.
6
+
7
+ react-router framework is the successor of remix. it is basically the same framework and it uses loaders and actions as core features.
8
+
9
+ react-router follows all the conventions of remix but all imports must be updated to point to `react-router` instead of `@remix-run/react` or `@remix-run/node`.
10
+
11
+ ## react-router navigation state
12
+
13
+ react-router has the hook `useNavigation` that exposes the navigation state. ALWAYS use this hook to track loading state for navigation
14
+
15
+ ```ts
16
+ const navigation = useNavigation()
17
+
18
+ if (navigation.state === 'loading' || navigation.state === 'submitting') {
19
+ return null
20
+ }
21
+ ```
22
+
23
+ > when making changes to the website code only use the `pnpm typecheck` script to validate changes, NEVER run `pnpm build` unless asked. It is too slow.
24
+
25
+ ## Creating New Routes and Handling Types
26
+
27
+ When creating a new React Router route, follow these steps:
28
+
29
+ ### 1. Create the route file
30
+ Create a file in `src/routes/` using flat routes naming convention (dots for separators, $ for params, kebab-case).
31
+
32
+ ### 2. Generate types
33
+ **IMPORTANT**: Types are NOT automatically generated. After creating a route, run:
34
+ ```bash
35
+ pnpm exec react-router typegen
36
+ ```
37
+
38
+ ### 3. Import Route types
39
+ ```typescript
40
+ import type { Route } from './+types/your-route-name'
41
+ ```
42
+ Note: The `+types` directory doesn't physically exist - it's virtual/generated.
43
+
44
+ ### 4. Verify with typecheck
45
+ ```bash
46
+ pnpm typecheck # This runs typegen first, then tsc
47
+ ```
48
+
49
+ ### Troubleshooting Missing Types
50
+ - Types missing? Run `pnpm exec react-router typegen`
51
+ - Import failing? Check filename matches import path exactly
52
+ - Types not updating? Run `pnpm typecheck` to regenerate
53
+ - The `+types` directory is virtual - don't look for it in the filesystem
54
+
55
+ ### Best Practices
56
+ - Always run `pnpm typecheck` after creating/modifying routes
57
+ - Export `Route` type from layout routes for child routes to import
58
+ - Use `href()` for all internal paths, even in redirects
59
+
60
+ ## react-router layout routes
61
+
62
+ react-router layout routes are simply routes that share a prefix with some children routes. these routes will run their loaders and components also when the children paths are fetched.
63
+
64
+ components can render children routes using the Outlet component
65
+
66
+ ```tsx
67
+ export function Component() {
68
+ return <Outlet />
69
+ }
70
+ ```
71
+
72
+ the loader data from parent layouts will NOT be present in the children routes `Route.componentProps['loaderData']` type. instead you have to use the `useRouteLoaderData('/prefix-path')` instead. always add the type to these calls getting the `Route` type from the parent layout
73
+
74
+ > layout routes should ALWAYS export their own Route namespace types so that child route can use it to type `useRouteLoaderData`!
75
+
76
+ ## cookies
77
+
78
+ never use react-router or remix `createCookieSessionStorage`. instead just use the npm cookie package to serialize and parse cookies. keep it simple.
79
+
80
+ if you want to store json data in cookies, remember to use encodeURIComponent to encode the data before storing it in the cookie, and decodeURIComponent to decode it when reading it back. this is because cookies can only store string values.
81
+
82
+ ## website, react-routes
83
+
84
+ website routes use the flat routes filesystem routes, inside src/routes. these files encode the routing logic in the filename, using $id for params and dot . for slashes.
85
+
86
+ if 2 routes share the same prefix, then the loader of both routes is run on a request and the route with the shorter route name is called a layout. a layout can also use <Outlet /> to render the child route inside it. for example, /org/x/site will run loaders in `org.$orgid` and `org.$orgid.site`. if you want instead to create a route that is not a layout route, where the loader does not run for routes that share the prefix, append \_index to the filename, for example `org.$orgid._index` in the example before.
87
+
88
+ if you need to add new prisma queries or data fetching in loaders, put it in layouts if possible. this way the data is fetched less often. you can do this if the data does not depend on the children routes' specific parameters.
89
+
90
+ ## route file exports
91
+
92
+ you can export the functions `loader` and `action` to handle loading data and submitting user data.
93
+
94
+ the default export (not always required for API routes) is the jsx component that renders the page visually.
95
+
96
+ notice that the `json` util was removed from `react-router`. instead there is a function `data` which is very similar and accepts a second argument to add headers and status like `json` does, but it supports more data types than json, like generators, async generators, dates, map, sets, etc.
97
+
98
+ ## Route type safety
99
+
100
+ react-router exports a `Route` namespace with types like `Route.LoaderArgs`, `Route.ActionArgs` and `Route.ComponentProps`
101
+
102
+ these types can be used for the main route exports, they must be imported from `./+types/{route-basename}`
103
+
104
+ for example, if the current file is `src/routes/home.tsx` you can import `import { Route } from './+types/home'`.
105
+
106
+ when using loader data in components, it is preferable to use useRouteLoaderData instead of just useLoaderData, so that if the route data is not accessible an error is thrown instead of silently failing with the wrong data.
107
+
108
+ you can use the Route types even to type other components that rely on `useRouteLoaderData`. but to do this you cannot import from `+types`, only route files can do that. instead you should export the Route type from the route file and let the component file import from the route.
109
+
110
+ here is an example to get the loader data type safely from a component:
111
+
112
+ > useRouteLoaderData return type is `Route.componentProps['loaderData']`
113
+
114
+ ```ts
115
+ import type { Route } from 'website/src/routes/root'
116
+
117
+ const { userId } = useRouteLoaderData(
118
+ 'root',
119
+ ) as Route.componentProps['loaderData']
120
+ ```
121
+
122
+ ```ts
123
+ // this path should export Route first. make sure of that
124
+ import type { Route } from 'website/src/routes/org.$orgId'
125
+
126
+ const { userId } = useRouteLoaderData(
127
+ 'routes/org.$orgId',
128
+ ) as Route.componentProps['loaderData']
129
+ ```
130
+
131
+ you can do the same thing with action data, using `Route.componentProps['actionData']`
132
+
133
+ ## links type safety
134
+
135
+ ALWAYS use the react-router href function to create links, it works as follow
136
+
137
+ ```ts
138
+ import { href } from 'react-router'
139
+
140
+ const path = href('/org/:orgId', { orgId })
141
+ ```
142
+
143
+ if you need to have an absolute url you can do `new URL(href('/some/path'), env.PUBLIC_URL)`
144
+
145
+ the only case where you should not use href is for urls outside of the current app or routes like `routes/$.tsx`, basically routes that match all paths.
146
+
147
+ > if you cannot use `href` simply because the route you would like to link to does not exist, you should do the following: list all the files in the src/routes folder first, to see if it already exists but not with the name you would expect. if still you can't find one, create a simple placeholder react-router route with a simple page component and a simple loader that does what you would expect. do not write too much code. you can improve on it in later messages.
148
+
149
+ ## showing spinner while loader does work and then redirect
150
+
151
+ for routes that do slow operations like creating PRs and then redirect, use a loader that returns a promise. the component uses window.location.replace when the promise resolves.
152
+
153
+ > IMPORTANT: react router does not preserve errors thrown in promises returned from loaders. NEVER throw errors inside promises returned from loaders. instead, add a .catch to make sure errors are never thrown and returned as values instead. then use instanceof check in client
154
+
155
+ ```tsx
156
+ export async function loader({ request, params: { id } }: Route.LoaderArgs) {
157
+ const url = new URL(request.url)
158
+ const data = url.searchParams.get('data')
159
+ const promise = doSlowWork(id, data)
160
+ .catch(error => {
161
+ notifyError(error)
162
+ return error
163
+ })
164
+ return { promise }
165
+ }
166
+
167
+ export default function Page() {
168
+ const { promise } = useLoaderData<typeof loader>()
169
+ const [error, setError] = useState('')
170
+
171
+ useEffect(() => {
172
+ promise.then(result => {
173
+ if (result instanceof Error) {
174
+ setError(result.message)
175
+ return
176
+ }
177
+ window.location.replace(result.url)
178
+ })
179
+ }, [promise])
180
+
181
+ if (error) return <p className='text-red-600'>Error: {error}</p>
182
+ return <Loader2Icon className='h-6 w-6 animate-spin' />
183
+ }
184
+ ```
185
+
186
+ ## do not redirect to missing routes that do not exist
187
+
188
+ never redirect or link to a route that does not exist. instead create a simple placeholder route with a simple loader and component. then redirect there using type-safe path with `href`
189
+
190
+ if instead it's not clear where to redirect because a user resource is missing, check if an onboarding route exists for that resource or a generic onboarding route. redirect there instead
191
+
192
+ also keep in mind it's preferable to throw redirects in loaders instead of returning responses, so loader keeps type safety.
193
+
194
+ ## client side navigation is preferred
195
+
196
+ always try to use react-router `useNavigate` or `Link` instead of doing window.location.href update.
197
+
198
+ so that internal navigation is done client side and is faster. notice that navigate only accepts a relative path and not a full url, so if you have a full url you should do new URL(url).pathname. only use navigate if you know the url is relative to the app.
199
+
200
+ ## Link or a components are preferred over `navigate`
201
+
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
+
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
+ ## debugging build failures
239
+
240
+ 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 ADDED
@@ -0,0 +1,39 @@
1
+ # react
2
+
3
+ - never test react code. instead put as much code as possible in react-agnostic functions or classes and test those if needed.
4
+
5
+ - hooks, all functions that start with use, MUST ALWAYS be called in the component render scope, never inside other closures in the component or event handlers. follow react rules of hooks.
6
+
7
+ - always put all hooks at the start of component functions. put hooks that are bigger and longer later if possible. all other non-hooks logic should go after hooks section, things like conditionals, expressions, etc
8
+
9
+ ## react code
10
+
11
+ - `useEffect` is bad: the use of useEffect is discouraged. please do not use it unless strictly necessary. before using useEffect call the @think tool to make sure that there are no other options. usually you can colocate code that runs inside useEffect to the functions that call that useEffect dependencies setState instead
12
+
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
+
15
+ - `useCallback` is bad. it should be always avoided.
16
+
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
+
19
+ - custom hooks are bad. NEVER add custom hooks unless asked to do so by me. instead of creating hooks create generic react-independent functions. every time you find yourself creating a custom hook call @think and think hard if you can just create a normal function instead, or just inline the expression in the component if small enough
20
+
21
+ - minimize number of props. do not use props if you can use zustand state instead. the app has global zustand state that lets you get a piece of state down from the component tree by using something like `useStore(x => x.something)` or `useLoaderData<typeof loader>()` or even useRouteLoaderData if you are deep in the react component tree
22
+
23
+ - do not consider local state truthful when interacting with server. when interacting with the server with rpc or api calls never use state from the render function as input for the api call. this state can easily become stale or not get updated in the closure context. instead prefer using zustand `useStore.getState().stateValue`. notice that useLoaderData or useParams should be fine in this case.
24
+
25
+ - when using useRef with a generic type always add undefined in the call, for example `useRef<number>(undefined)`. this is required by the react types definitions
26
+
27
+ - when using && in jsx make sure that the result type is not of type number. in that case add Boolean() wrapper. this way jsx will not show zeros when the value is falsy.
28
+
29
+ ## components
30
+
31
+ - place new components in the src/components folder. shadcn components will go to the src/components/ui folder, usually they are not manually updated but added with the shadcn cli (which is preferred to be run without npx, either with pnpm or globally just shadcn)
32
+
33
+ - component filenames should follow kebab case structure
34
+
35
+ - do not create a new component file if this new code will only be used in another component file. only create a component file if the component is used by multiple components or routes. colocate related components in the same file.
36
+
37
+ - non component code should be put in the src/lib folder.
38
+
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.
package/sentry.md ADDED
@@ -0,0 +1,65 @@
1
+ # sentry
2
+
3
+ this project uses sentry to notify about unexpected errors.
4
+
5
+ the website folder will have a src/lib/errors.ts file with an exported function `notifyError(error: Error, contextMessage: string)`.
6
+
7
+ you should ALWAYS use notifyError in these cases:
8
+
9
+ - create a new spiceflow api app, put notifyError in the onError callback with context message including the api route path
10
+ - suppressing an error for operations that can fail. instead of doing console.error(error) you should instead call notifyError
11
+ - wrapping a promise with cloudflare `waitUntil`. add a .catch and a notifyError so errors are tracked
12
+
13
+ this function will add the error in sentry so that the developer is able to track users' errors
14
+
15
+ ## errors.ts file
16
+
17
+ if a package is missing the errors.ts file, here is the template for adding one.
18
+
19
+ notice that
20
+
21
+ - dsn should be replaced by the user with the right one. ask to do so
22
+ - use the sentries npm package, this handles correctly every environment like Bun, Node, Browser, etc
23
+
24
+ ```tsx
25
+ import { captureException, flush, init } from "sentries";
26
+
27
+ init({
28
+ dsn: "https://e702f9c3dff49fd1aa16500c6056d0f7@o4509638447005696.ingest.de.sentry.io/4509638454476880",
29
+ integrations: [],
30
+ tracesSampleRate: 0.01,
31
+ profilesSampleRate: 0.01,
32
+ beforeSend(event) {
33
+ if (process.env.NODE_ENV === "development") {
34
+ return null;
35
+ }
36
+ if (process.env.BYTECODE_RUN) {
37
+ return null;
38
+ }
39
+ if (event?.["name"] === "AbortError") {
40
+ return null;
41
+ }
42
+
43
+ return event;
44
+ },
45
+ });
46
+
47
+ export async function notifyError(error: any, msg?: string) {
48
+ console.error(msg, error);
49
+ captureException(error, { extra: { msg } });
50
+ await flush(1000);
51
+ }
52
+
53
+ export class AppError extends Error {
54
+ constructor(message: string) {
55
+ super(message);
56
+ this.name = "AppError";
57
+ }
58
+ }
59
+ ```
60
+
61
+ ## app error
62
+
63
+ every time you throw a user-readable error you should use AppError instead of Error
64
+
65
+ AppError messages will be forwarded to the user as is. normal Error instances instead could have their messages obfuscated
package/shadcn.md ADDED
@@ -0,0 +1,19 @@
1
+ # styling
2
+
3
+ - always use tailwind for styling. prefer using simple styles using flex and gap. margins should be avoided, instead use flexbox gaps, grid gaps, or separate spacing divs.
4
+
5
+ - use shadcn theme colors instead of tailwind default colors. this way there is no need to add `dark:` variants most of the time.
6
+
7
+ - `flex flex-col gap-3` is preferred over `space-y-3`. same for the x direction.
8
+
9
+ - try to keep styles as simple as possible, for breakpoints too.
10
+
11
+ - to join many classes together use the `cn('class-1', 'class-2')` utility instead of `${}` or other methods. this utility is usually used in shadcn-compatible projects and mine is exported from `website/src/lib/cn` usually. prefer doing `cn(bool && 'class')` instead of `cn(bool ? 'class' : '')`
12
+
13
+ - prefer `size-4` over `w-4 h-4`
14
+
15
+ ## components
16
+
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
+
19
+ try to reuse these available components when you can, for example for buttons, tooltips, scroll areas, etc.
package/spiceflow.md ADDED
@@ -0,0 +1,50 @@
1
+ # spiceflow
2
+
3
+ before writing or updating spiceflow related code always execute this command to get Spiceflow full documentation: `curl -s https://gitchamber.com/repos/remorses/spiceflow/main/files/README.md`
4
+
5
+ spiceflow is an API library similar to hono, it allows you to write api servers using whatwg requests and responses
6
+
7
+ use zod to create schemas and types that need to be used for tool inputs or spiceflow API routes.
8
+
9
+ ## calling the server from the clientE
10
+
11
+ you can obtain a type safe client for the API using `createSpiceflowClient` from `spiceflow/client`
12
+
13
+ for simple routes that only have one interaction in the page, for example a form page, you should use react-router forms and actions to interact with the server.
14
+
15
+ but when you do interactions from a component that can be rendered from multiple routes, or simply is not implemented inside a route page, you should use spiceflow client instead.
16
+
17
+ > ALWAYS use the fetch tool to get the latest docs if you need to implement a new route in a spiceflow API app server or need to add a new rpc call with a spiceflow api client!
18
+
19
+ spiceflow has support for client-side type-safe rpc. use this client when you need to interact with the server from the client, for example for a settings save deep inside a component. here is example usage of it
20
+
21
+ > SUPER IMPORTANT! if you add a new route to a spiceflow app, use the spiceflow app state like `userId` to add authorization to the route. if there is no state then you can use functions like `getSession({request})` or similar.
22
+ > make sure the current userId has access to the fetched or updated rows. this can be done by checking that the parent row or current row has a relation with the current userId. for example `prisma.site.findFirst({where: {users: {some: {userId }}}})`
23
+
24
+ > IMPORTANT! spiceflow api client cannot be called server side to call a route! In that case instead you MUST call the server functions used in the route directly, otherwise the server would do fetch requests that would fail!
25
+
26
+ always use `const {data, error} = await apiClient...` when calling spiceflow rpc. if data is already declared, give it a different name with `const {data: data2, error} = await apiClient...`. this pattern of destructuring is preferred for all apis that return data and error object fields.
27
+
28
+ ## getting spiceflow docs
29
+
30
+ spiceflow is a little-known api framework. if you add server routes to a file that includes spiceflow in the name or you are using the apiClient rpc, you always need to fetch the spiceflow docs first, using the @fetch tool on https://getspiceflow.com/
31
+
32
+ this url returns a single long documentation that covers your use case. always fetch this document so you know how to use spiceflow. spiceflow is different from hono and other api frameworks, that's why you should ALWAYS fetch the docs first before using it
33
+
34
+ ## using spiceflow client in published public workspace packages
35
+
36
+ usually you can just import the App type from the server workspace to create the client with createSpiceflowClient
37
+
38
+ if you want to use the spiceflow client in a published package instead we will use the pattern of generating .d.ts and copying these in the workspace package, this way the package does not need to depend on unpublished private server package.
39
+
40
+ example:
41
+
42
+ ```json
43
+ {
44
+ "scripts": {
45
+ "gen-client": "export DIR=../plugin-mcp/src/generated/ && cd ../website && tsc --incremental && cd ../plugin-mcp && rm -rf $DIR && mkdir -p $DIR && cp ../website/dist/src/lib/api-client.* $DIR"
46
+ }
47
+ }
48
+ ```
49
+
50
+ notice that if you add a route in the spiceflow server you will need to run `pnpm --filter website gen-client` to update the apiClient inside cli.
package/stripe.md ADDED
@@ -0,0 +1,26 @@
1
+ # stripe
2
+
3
+ stripe is used to manage subscriptions and payments.
4
+
5
+ the Stripe billing portal is used to
6
+
7
+ - send user to payment to create a sub via `stripe.checkout.sessions.create`
8
+ - let user change plan, cancel or change payment method ("manage subscription") via `stripe.billingPortal.sessions.create`
9
+
10
+ ## subscriptions
11
+
12
+ a subscription is active if state is in
13
+
14
+ - trialing
15
+ - active
16
+
17
+ a subscription can be reactivated if state is NOT in
18
+
19
+ - canceled
20
+ - incomplete_expired
21
+ - unpaid
22
+
23
+ > 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 ADDED
@@ -0,0 +1,13 @@
1
+ # tailwind v4
2
+
3
+ this project uses tailwind v4. this new tailwind version does not use tailwind.config.js. instead it does all configuration in css files.
4
+
5
+ read https://tailwindcss.com/docs/upgrade-guide to understand the updates landed in tailwind v4 if you do not have tailwind v4 in your training context. ignore the parts that talk about running the upgrade cli. this project already uses tailwind v4 so no need to upgrade anything.
6
+
7
+ ## spacing should use multiples of 4
8
+
9
+ for margin, padding, gaps, widths and heights it is preferable to use multiples of 4 of the tailwind spacing scale. for example p-4 or gap-4
10
+
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
+
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.
package/typescript.md ADDED
@@ -0,0 +1,97 @@
1
+ # typescript
2
+
3
+ - ALWAYS use normal imports instead of dynamic imports, unless there is an issue with es module only packages and you are in a commonjs package (this is rare).
4
+
5
+ - use a single object argument instead of multiple positional args: use object arguments for new typescript functions if the function would accept more than one argument, so it is more readable, ({a,b,c}) instead of (a,b,c). this way you can use the object as a sort of named argument feature, where order of arguments does not matter and it's easier to discover parameters.
6
+
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
+
9
+ - 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
+
11
+ - 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.
12
+
13
+ - NEVER write comments that reference changes between previous and old code generated between iterations of our conversation. do that in prompt instead. comments should be used for information of the current code. code that is deleted does not matter.
14
+
15
+ - use early returns (and breaks in loops): do not nest code too much. follow the go best practice of if statements: avoid else, nest as little as possible, use top level ifs. minimize nesting. instead of doing `if (x) { if (b) {} }` you should do `if (x && b) {};` for example. you can always convert multiple nested ifs or elses into many linear ifs at one nesting level. use the @think tool for this if necessary.
16
+
17
+ - typecheck after updating code: 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
18
+
19
+ - do not use any: you must NEVER use any. if you find yourself using `as any` or `:any`, use the @think tool to think hard if there are types you can import instead. do even a search in the project for what the type could be. any should be used as a last resort.
20
+
21
+ - 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
+
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
+ - 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
+
27
+ - 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
+
29
+ - 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
+
31
+ - for node built-in imports, never import singular names. instead do `import fs from 'node:fs'`, same for path, os, etc.
32
+
33
+ - NEVER start the development server with pnpm dev yourself. there is no reason to do so, even with &
34
+
35
+ - When creating classes do not add setters and getters for a simple private field. instead make the field public directly so user can get it or set it himself without abstractions on top
36
+
37
+ - 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
+
39
+ ```ts
40
+ // BAD. DO NOT DO THIS
41
+ let favicon: string | undefined;
42
+ if (docsConfig?.favicon) {
43
+ if (typeof docsConfig.favicon === "string") {
44
+ favicon = docsConfig.favicon;
45
+ } else if (docsConfig.favicon?.light) {
46
+ // Use light favicon as default, could be enhanced with theme detection
47
+ favicon = docsConfig.favicon.light;
48
+ }
49
+ }
50
+ // DO THIS. use an iife. Immediately Invoked Function Expression
51
+ const favicon: string = (() => {
52
+ if (!docsConfig?.favicon) {
53
+ return "";
54
+ }
55
+ if (typeof docsConfig.favicon === "string") {
56
+ return docsConfig.favicon;
57
+ }
58
+ if (docsConfig.favicon?.light) {
59
+ // Use light favicon as default, could be enhanced with theme detection
60
+ return docsConfig.favicon.light;
61
+ }
62
+ return "";
63
+ })();
64
+ // if you already know the type use it:
65
+ const favicon: string = () => {
66
+ // ...
67
+ };
68
+ ```
69
+
70
+ - when a package has to import files from another packages in the workspace never add a new tsconfig path, instead add that package as a workspace dependency using `pnpm i "package@workspace:*"`
71
+
72
+ NEVER use require. always esm imports
73
+
74
+ always try to use non-relative imports. each package has an absolute import with the package name, you can find it in the tsconfig.json paths section. for example, paths inside website can be imported from website. notice these paths also need to include the src directory.
75
+
76
+ this is preferable to other aliases like @/ because i can easily move the code from one package to another without changing the import paths. this way you can even move a file and import paths do not change much.
77
+
78
+ always specify the type when creating arrays, especially for empty arrays. if you don't, typescript will infer the type as `never[]`, which can cause type errors when adding elements later.
79
+
80
+ **Example:**
81
+
82
+ ```ts
83
+ // BAD: Type will be never[]
84
+ const items = [];
85
+
86
+ // GOOD: Specify the expected type
87
+ const items: string[] = [];
88
+ const numbers: number[] = [];
89
+ const users: User[] = [];
90
+ ```
91
+
92
+ remember to always add the explicit type to avoid unexpected type inference.
93
+
94
+ - when using nodejs APIs like fs always import the module and not the named exports. I prefer hacing nodejs APIs accessed on the module namspace like fs, os, path, etc.
95
+
96
+ DO `import fs from 'fs'; fs.writeFileSync(...)`
97
+ DO NOT `import { writeFileSync } from 'fs';`
@@ -0,0 +1,10 @@
1
+ # ai sdk
2
+
3
+ i use the vercel ai sdk to interact with LLMs, also known as the npm package `ai`. never use the openai sdk or provider-specific sdks, always use the vercel ai sdk, npm package `ai`. streamText is preferred over generateText, unless the model used is very small and fast and the current code doesn't care about streaming tokens or showing a preview to the user. `streamObject` is also preferred over generateObject.
4
+
5
+ ALWAYS fetch the latest docs for the ai sdk using this url with curl:
6
+ https://gitchamber.com/repos/vercel/ai/main/files
7
+
8
+ use gitchamber to read the .md files using curl
9
+
10
+ you can swap out the topic with text you want to search docs for. you can also limit the total results returned with the param token to limit the tokens that will be added to the context window
package/vitest.md ADDED
@@ -0,0 +1,34 @@
1
+ # testing
2
+
3
+ do not write new test files unless asked. do not write tests if there is not already a test or describe block for that function or module.
4
+
5
+ tests should validate complex and non-obvious logic. if a test looks like a placeholder, do not add it.
6
+
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.
8
+
9
+ 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
+
11
+ > always call `pnpm vitest` or `pnpm test` with `--run` or they will hang forever waiting for changes!
12
+ > ALWAYS read back the test if you use the `-u` option to make sure the inline snapshots are as you expect.
13
+
14
+ - NEVER writes the snapshots content yourself in `toMatchInlineSnapshot`. instead leave it empty and call `pnpm test -u` to fill in snapshots content.
15
+
16
+ - 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
+
18
+ - 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
+
20
+ never test client react components. only server code that runs on the server.
21
+
22
+ 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
+
24
+ NEVER use mocks. the database does not need to be mocked, just use it. simply do not test functions that mutate the database if not asked.
25
+
26
+ tests should strive to be as simple as possible. the best test is a simple `.toMatchInlineSnapshot()` call. these can be easily evaluated by reading the test file after the run passing the -u option. you can clearly see from the inline snapshot if the function behaves as expected or not.
27
+
28
+ try to use only describe and test in your tests. do not use beforeAll, before, etc if not strictly required.
29
+
30
+ NEVER write tests for react components or react hooks. NEVER write tests for react components. you will be fired if you do.
31
+
32
+ sometimes tests work directly on database data, using prisma. to run these tests you have to use the package.json script, which will call `doppler run -- vitest` or similar. never run doppler cli yourself as you could delete or update production data. tests generally use a staging database instead.
33
+
34
+ never write tests yourself that call prisma or interact with database or emails. for these, ask the user to write them for you.
package/zod.md ADDED
@@ -0,0 +1,21 @@
1
+ # zod
2
+
3
+ when you need to create a complex type that comes from a prisma table, do not create a new schema that tries to recreate the prisma table structure. instead just use `z.any() as ZodType<PrismaTable>)` to get type safety but leave any in the schema. this gets most of the benefits of zod without having to define a new zod schema that can easily go out of sync.
4
+
5
+ ## converting zod schema to jsonschema
6
+
7
+ you MUST use the built in zod v4 toJSONSchema and not the npm package `zod-to-json-schema` which is outdated and does not support zod v4.
8
+
9
+ ```ts
10
+ import { toJSONSchema } from "zod";
11
+
12
+ const mySchema = z.object({
13
+ id: z.string().uuid(),
14
+ name: z.string().min(3).max(100),
15
+ age: z.number().min(0).optional(),
16
+ });
17
+
18
+ const jsonSchema = toJSONSchema(mySchema, {
19
+ removeAdditionalStrategy: "strict",
20
+ });
21
+ ```