add cursor rules and commands

This commit is contained in:
tom
2025-10-15 18:24:34 +02:00
parent bf09c9fef7
commit 10d028fc4b
5 changed files with 559 additions and 1 deletions
+42
View File
@@ -0,0 +1,42 @@
# Create PR
## Overview
Create a well-structured pull request with proper description, labels, and reviewers.
## Steps
1. **Check that the pull request is not already open in the remote repository**
- If it is opened, write only PR summary (see step 3) and skip all other steps
2. **Prepare branch**
- Ensure all changes are committed
- Push branch to remote
- Verify branch is up to date with main
3. **Write PR description**
- Use template from `./docs/PULL_REQUEST_TEMPLATE.md`
- Check if the branch name contains an issue number (use the regexp `/issue-\d+/`)
- If found, fetch the issue details using `gh issue view [issue_number]`
- Include "Resolves #[issue_number]" at the very beginning of the description (in the "Description and Related Issue(s)" section)
- Summarize the changes clearly and concisely, using no more than two paragraphs. If necessary, use bullet points to highlight the main changes in the codebase.
- List any changes in the enviroment variables (look at the `./docs/ENVS.md` file) in a separate section, describe purpose of each variable change
- Bad example: "Added `NEXT_PUBLIC_VIEWS_TX_GROUPED_FEES` environment variable to the documentation"
- Good example: "Added `NEXT_PUBLIC_VIEWS_TX_GROUPED_FEES` to group transaction fees into one section on the transaction page"
- Good example: "Extended possible values for `NEXT_PUBLIC_VIEWS_TX_ADDITIONAL_FIELDS` with set_max_gas_limit to display the maximum gas price set by the transaction sender"
- Good example: "Introduced a new option, `"fee reception"`, for the `NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE` variable"
4. **Set up PR**
- Create a pull request with a descriptive title. Use draft mode for the pull request if specified in the prompt.
- Add appropriate labels
- If something has added or changed in `./docs/ENVS.md` file, add label "ENVs"
- If the `package.json` file has changed, add label "dependencies"
- If the branch name has the issue number (use the regexp `/issue-\d+/`), copy all tags from the related issue
5. **Monitor checks workflow**
- Wait for the "Checks" workflow (see `.github/workflows/checks.yml`) to start (triggered automatically by PR creation)
- Watch the workflow run until it completes (use `gh run watch`)
- Track the workflow status (success or failure)
6. **Send system notification with results**
- On workflow completion: notify with final status (success/failure)
- On failure: notify about the error with brief details
+34
View File
@@ -0,0 +1,34 @@
# Deploy demo
## Overview
Create Github deployment for current branch using `.github/workflows/deploy-review.yml` workflow.
## Steps
1. **Ensure all changes are pushed to remote**
2. **Check if there is a running deployment workflow for current branch**
- If yes, abort the command
3. **Detect value for "envs_preset" workflow input**
- Get all possible options from workflow file
- Figure out from the prompt with which preset user wants to deploy demo. Examples:
- "/deploy-demo for base" - preset name "base"
- "/deploy-demo using main preset" - preset name "main"
- "/deploy-demo without preset" - preset name "none"
- Be aware that this input is optional, and if user didn't mention any preset, skip this step completely
- If the preset is not listed in the worklow input options, notify user and abort the command
4. **Run workflow from the current branch with appropriate inputs**
- Get the workflow run ID after triggering
5. **Monitor workflow execution**
- Watch the workflow run until it completes (use `gh run watch`)
- Track the workflow status (success, failure, cancelled)
- Once completed, retrieve the workflow outputs and deployment URL
6. **Send system notification with results**
- On workflow start: notify that deployment has been triggered
- On workflow completion: notify with final status (success/failure), deployment URL if available, and any relevant outputs
- On failure: notify about the error with details
+37
View File
@@ -0,0 +1,37 @@
---
description:
globs: *.tsx,*.ts
alwaysApply: false
---
You are an expert senior software engineer specializing in modern web development, with deep expertise in TypeScript, React 19, Next.js 15 (Pages Router), Chakra UI, and React Query. You are thoughtful, precise, and focus on delivering high-quality, maintainable solutions.
## Code Style and Structure
### General Principles
- Write concise, readable TypeScript code
- Use functional and declarative programming patterns; avoid classes
- Prefer iteration and modularization over code duplication
- Implement early returns for better readability
- Structure components logically: exports, subcomponents, helpers, types
### Naming Conventions
- Use descriptive names with auxiliary verbs (isLoading, hasError)
- Prefix event handlers with "handle" (handleClick, handleSubmit)
- Favor default exports for React components
### TypeScript Usage
- Use TypeScript for all code
- Prefer interfaces over types
- Avoid enums; use const maps instead
- Implement proper type safety and inference
- Use `satisfies` operator for type validation
### UI and Styling
- Utilize Chakra UI v3 components for consistent design
- Be aware that some components are tailored for the project's specific requirements. They are all located in the "toolkit/chakra" directory. Always use these instead of the native Chakra components.
+446
View File
@@ -0,0 +1,446 @@
---
description:
globs: *.tsx,*.ts
alwaysApply: false
---
# TypeScript Usage
- Use TypeScript for all code
- Prefer interfaces over types
- Implement proper type safety and inference
- Use `satisfies` operator for type validation
## Import types
Use import type whenever you are importing a type.
Prefer top-level `import type` over inline `import { type ... }`.
```ts
// BAD
import { type User } from "./user";
```
```ts
// GOOD
import type { User } from "./user";
```
The reason for this is that in certain environments, the first version's import will not be erased. So you'll be left with:
```ts
// Before transpilation
import { type User } from "./user";
// After transpilation
import "./user";
```
## "any" inside generic function
When building generic functions, you may need to use any inside the function body.
This is because TypeScript often cannot match your runtime logic to the logic done inside your types.
One example:
```ts
const youSayGoodbyeISayHello = <
TInput extends "hello" | "goodbye",
>(
input: TInput,
): TInput extends "hello" ? "goodbye" : "hello" => {
if (input === "goodbye") {
return "hello"; // Error!
} else {
return "goodbye"; // Error!
}
};
```
On the type level (and the runtime), this function returns `goodbye` when the input is `hello`.
There is no way to make this work concisely in TypeScript.
So using `any` is the most concise solution:
```ts
const youSayGoodbyeISayHello = <
TInput extends "hello" | "goodbye",
>(
input: TInput,
): TInput extends "hello" ? "goodbye" : "hello" => {
if (input === "goodbye") {
return "hello" as any;
} else {
return "goodbye" as any;
}
};
```
Outside of generic functions, use `any` extremely sparingly.
## Default exports
Unless explicitly required by the framework, do not use default exports.
```ts
// BAD
export default function myFunction() {
return <div>Hello</div>;
}
```
```ts
// GOOD
export function myFunction() {
return <div>Hello</div>;
}
```
Default exports create confusion from the importing file.
```ts
// BAD
import myFunction from "./myFunction";
```
```ts
// GOOD
import { myFunction } from "./myFunction";
```
There are certain situations where a framework may require a default export. For instance, Next.js requires a default export for pages.
```tsx
// This is fine, if required by the framework
export default function MyPage() {
return <div>Hello</div>;
}
```
## Discriminated unions
Proactively use discriminated unions to model data that can be in one of a few different shapes.
For example, when sending events between environments:
```ts
type UserCreatedEvent = {
type: "user.created";
data: { id: string; email: string };
};
type UserDeletedEvent = {
type: "user.deleted";
data: { id: string };
};
type Event = UserCreatedEvent | UserDeletedEvent;
```
Use switch statements to handle the results of discriminated unions:
```ts
const handleEvent = (event: Event) => {
switch (event.type) {
case "user.created":
console.log(event.data.email);
break;
case "user.deleted":
console.log(event.data.id);
break;
}
};
```
Use discriminated unions to prevent the 'bag of optionals' problem.
For example, when describing a fetching state:
```ts
// BAD - allows impossible states
type FetchingState<TData> = {
status: "idle" | "loading" | "success" | "error";
data?: TData;
error?: Error;
};
// GOOD - prevents impossible states
type FetchingState<TData> =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: TData }
| { status: "error"; error: Error };
```
## ENUMs
Do not introduce new enums into the codebase. Retain existing enums.
If you require enum-like behaviour, use an `as const` object:
```ts
const backendToFrontendEnum = {
xs: "EXTRA_SMALL",
sm: "SMALL",
md: "MEDIUM",
} as const;
type LowerCaseEnum = keyof typeof backendToFrontendEnum; // "xs" | "sm" | "md"
type UpperCaseEnum =
(typeof backendToFrontendEnum)[LowerCaseEnum]; // "EXTRA_SMALL" | "SMALL" | "MEDIUM"
```
Remember that numeric enums behave differently to string enums. Numeric enums produce a reverse mapping:
```ts
enum Direction {
Up,
Down,
Left,
Right,
}
const direction = Direction.Up; // 0
const directionName = Direction[0]; // "Up"
```
This means that the enum `Direction` above will have eight keys instead of four.
```ts
enum Direction {
Up,
Down,
Left,
Right,
}
Object.keys(Direction).length; // 8
```
## Interface extends
ALWAYS prefer interfaces when modelling inheritance.
The `&` operator has terrible performance in TypeScript. Only use it where `interface extends` is not possible.
```ts
// BAD
type A = {
a: string;
};
type B = {
b: string;
};
type C = A & B;
```
```ts
// GOOD
interface A {
a: string;
}
interface B {
b: string;
}
interface C extends A, B {
// Additional properties can be added here
}
```
## JS-doc comments
Use JSDoc comments to annotate functions and types.
Be concise in JSDoc comments, and only provide JSDoc comments if the function's behaviour is not self-evident.
Use the JSDoc inline `@link` tag to link to other functions and types within the same file.
```ts
/**
* Subtracts two numbers
*/
const subtract = (a: number, b: number) => a - b;
/**
* Does the opposite to {@link subtract}
*/
const add = (a: number, b: number) => a + b;
```
## Naming conventions
- Use kebab-case for file names (e.g., `my-component.ts`)
- Use camelCase for variables and function names (e.g., `myVariable`, `myFunction()`)
- Use UpperCamelCase (PascalCase) for classes, types, and interfaces (e.g., `MyClass`, `MyInterface`)
- Use ALL_CAPS for constants and enum values (e.g., `MAX_COUNT`, `Color.RED`)
- Inside generic types, functions or classes, prefix type parameters with `T` (e.g., `TKey`, `TValue`)
```ts
type RecordOfArrays<TItem> = Record<string, TItem[]>;
```
## No unchecked indexed access
If the user has this rule enabled in their `tsconfig.json`, indexing into objects and arrays will behave differently from how you expect.
```ts
const obj: Record<string, string> = {};
// With noUncheckedIndexedAccess, value will
// be `string | undefined`
// Without it, value will be `string`
const value = obj.key;
```
```ts
const arr: string[] = [];
// With noUncheckedIndexedAccess, value will
// be `string | undefined`
// Without it, value will be `string`
const value = arr[0];
```
## Optional properties
Use optional properties extremely sparingly. Only use them when the property is truly optional, and consider whether bugs may be caused by a failure to pass the property.
In the example below we always want to pass user ID to `AuthOptions`. This is because if we forget to pass it somewhere in the code base, it will cause our function to be not authenticated.
```ts
// BAD
type AuthOptions = {
userId?: string;
};
const func = (options: AuthOptions) => {
const userId = options.userId;
};
```
```ts
// GOOD
type AuthOptions = {
userId: string | undefined;
};
const func = (options: AuthOptions) => {
const userId = options.userId;
};
```
## Read-only properties
Use `readonly` properties for object types by default. This will prevent accidental mutation at runtime.
Omit `readonly` only when the property is genuinely mutable.
```ts
// BAD
type User = {
id: string;
};
const user: User = {
id: "1",
};
user.id = "2";
```
```ts
// GOOD
type User = {
readonly id: string;
};
const user: User = {
id: "1",
};
user.id = "2"; // Error
```
## Return types
When declaring functions on the top-level of a module,
declare their return types. This will help future AI
assistants understand the function's purpose.
```ts
const myFunc = (): string => {
return "hello";
};
```
One exception to this is components which return JSX.
No need to declare the return type of a component,
as it is always JSX.
```tsx
const MyComponent = () => {
return <div>Hello</div>;
};
```
## Throwing
Think carefully before implementing code that throws errors.
If a thrown error produces a desirable outcome in the system, go for it. For instance, throwing a custom error inside a backend framework's request handler.
However, for code that you would need a manual try catch for, consider using a result type instead:
```ts
type Result<T, E extends Error> =
| { ok: true; value: T }
| { ok: false; error: E };
```
For example, when parsing JSON:
```ts
const parseJson = (
input: string,
): Result<unknown, Error> => {
try {
return { ok: true, value: JSON.parse(input) };
} catch (error) {
return { ok: false, error: error as Error };
}
};
```
This way you can handle the error in the caller:
```ts
const result = parseJson('{"name": "John"}');
if (result.ok) {
console.log(result.value);
} else {
console.error(result.error);
}
```
-1
View File
@@ -33,7 +33,6 @@
*.pem
.tools
grafana
.cursor
.zed
# debug