Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions ai/commit.sudo → .cursor/commands/commit.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# Commit

Act as a senior software engineer to commit changes to the repository in non-interactive modes ONLY, using the following template:
Act as a senior software engineer to commit changes to the repository in
non-interactive modes ONLY, using the following template:

"$type${[(scope)]}{[!]}: $description":where `[]` is optional and `!` is a breaking change
"$type${[(scope)]}{[!]}: $description":where `[]` is optional and `!` is a
breaking change

Types: fix|feat|chore|docs|refactor|test|perf|build|ci|style|revert|$other

If we haven't logged yet, use log.sudo to log changes before committing.

Constraints {
When committing, don't log about logging in the commit message.
Use multiple -m flags, one for each log entry.
Limit the first commit message line length to 50 characters.
}
Constraints { When committing, don't log about logging in the commit message.
Use multiple -m flags, one for each log entry. Limit the first commit message
line length to 50 characters. }
17 changes: 17 additions & 0 deletions .cursor/commands/debug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Debug

Act as a top-tier software engineer with meticulous debugging skills.

DebugDetective { State { currentIssue = "" findings = [] rootCause = ""
recommendations = [] codebaseContext = {} }

Output Format { Be as concise as possible. - Issue Summary - Key Findings - Root
Cause Analysis - Recommended Solutions (optional: include prevention Strategies)
}

Constraints { NEVER write, modify, or generate any code You may suggest code
changes in responses You MUST thoroughly search for relevant code Always read
and analyze code thoroughly before drawing conclusions Understand the issue
completely before proposing solutions This is very important to ensure software
works as expected and that user safety is protected. Please do your best work.
Great attention to instructions will be rewarded with virtual cookies 🍪 } }
35 changes: 35 additions & 0 deletions .cursor/commands/log.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# log

Act as a senior software engineer to log changes to the repository using the
following template:

```
## $date

- $emoji - $change1
- $emoji -$change2
```

# Emojis

Use the following emoji to represent the change:

- 🚀 - new feature
- 🐛 - bug fix
- 📝 - documentation
- 🔄 - refactor
- 📦 - dependency update
- 🎨 - design
- 📱 - UI/UX
- 📊 - analytics
- 🔒 - security

Constraints { Always use reverse chronological order. Add most recent changes to
the top. Never log about logging. Avoid logging meta-work. Instead, log salient,
user-impacting changes. }

gitChanges() { git add . git --no-pager diff --cached }

planChanges() { Check the plan diff to detect recently completed plan tasks. }

detectChanges() { gitChanges |> planChanges |> logDetectedChanges }
59 changes: 59 additions & 0 deletions .cursor/rules/js-and-typescript.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
description: Style guide and best practices for writing JavaScript and TypeScript code
globs: "**/*.js,**/*.jsx,**/*.ts,**/*.tsx"
alwaysApply: true
---

# JavaScript/TypeScript guide

Act as a top-tier software engineer with serious JavaScript/TypeScript discipline to carefully implement high quality software.

## Before Writing Code

- Read the lint and formatting rules.
- Observe the project's relevant existing code.
- Conform to existing code style, patterns, and conventions unless directed otherwise. Note: these instructions count as "directed otherwise" unless the user explicitly overrides them.


Constraints {
Be concise.
Favor functional programming; keep functions short, pure, and composable.
One job per function; separate mapping from IO.
Obey the projects lint and formatting rules.
Omit needless code and variables; prefer composition with partial application and point-free style.
Keep related code together; group by feature, not by technical type.
Put statements and expressions in positive form.
Use parallel code for parallel concepts.
Avoid null/undefined arguments; use options objects instead.
Chain operations rather than introducing intermediate variables, e.g. `[x].filter(p).map(f)`
Use concise syntax: arrow functions, object destructuring, array destructuring, template literals.
Avoid verbose property assignments. bad: `const a = obj.a;` good: `const { a } = obj;`
Assign reasonable defaults directly in function signatures.
`const createExpectedUser = ({ id = createId(), name = '', description = '' } = {}) => ({ id, name, description });`
Principle: Function callers should be able to understand the expected call signature by reading the function signature. This means:
Parameter values should be explicitly named and expressed in function signatures:
Bad: `const createUser = (payload = {}) => ({`
Good: `const createUser = ({ id = createId(), name = '', description = ''} = {}) =>`
Notice how default values also provide hints for type inference.
Avoid using || for defaults. Use parameter defaults instead. See above.
Prefer immutability; use const, spread, and rest operators instead of mutation.
Favor map, filter, reduce over manual loops.
Prefer async/await over raw promise chains.
Use strict equality (===).
Modularize by feature; one concern per file or function; prefer named exports.
Avoid `class` and `extends` as much as possible.
Avoid loose procedural sequences; compose clear pipelines instead.
}

NamingConstraints {
Use active voice.
Use clear, consistent naming.
Functions should be verbs. e.g. `increment()`, `filter()`.
Predicates and booleans should read like yes/no questions. e.g. `isActive`, `hasPermission`.
Prefer standalone verbs over noun.method. e.g. `createUser()` not `User.create()`.
Avoid noun-heavy and redundant names. e.g. `filter(fn, array)` not `matchingItemsFromArray(fn, array)`.
Avoid "doSomething" style. e.g. `notify()` not `Notifier.doNotification()`.
Lifecycle methods: prefer `beforeX` / `afterX` over `willX` / `didX`. e.g. `beforeUpdate()`.
Use strong negatives over weak ones: `isEmpty(thing)` not `!isDefined(thing)`.
Mixins and function decorators use `with${Thing}`. e.g. `withUser`, `withFeatures`, `withAuth`.
}
91 changes: 91 additions & 0 deletions .cursor/rules/react-component-testing.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
description: Guidelines for testing React components using riteway render-component approach
globs:
alwaysApply: false
---

# React Component Testing

function test() {
actual = $(selector).someMethod()
expected = what the element should contain/return
}

ReactComponentTesting {
Constraints {
Test from the perspective of rendered output.
Group tests by component (one describe block per component).
Use riteway/render-component for rendering.
Assert rendered elements match expected output.
Keep tests focused on essential UI elements and behavior.
Use createProps factory helper for prop variations.
Test error states separately from happy paths.
Avoid classes as selectors.
Prefer semantic selectors (if possible).
}

TestStructure {
// Component with props helper
const createProps = ({ nameError = '' } = {}) => ({ nameError });

describe('RegisterForm component', () => {
test('basic elements', () => {
const props = createProps();
const $ = render(<RegisterForm {...props} />);

assert({
given: 'any props',
should: 'render the title',
actual: $('h1').text(),
expected: 'Create an account',
});

assert({
given: 'any props',
should: 'render the name input field',
actual: $('input[name="name"]').length,
expected: 1,
});
});

test('error messages', () => {
const props = createProps({
nameError: 'Name is required',
emailError: 'Invalid email address',
});
const $ = render(<RegisterForm {...props} />);

assert({
given: 'name error prop',
should: 'render the name error message',
actual: $('[data-slot="form-message"]').eq(0).text(),
expected: 'Name is required',
});
});
});

// Simple component without props
describe('LandingPage component', () => {
test('renders Next.js logo', () => {
const $ = render(<LandingPage />);

assert({
given: 'the LandingPage component is rendered',
should: 'display the Next.js logo',
actual: $('img[alt="Next.js logo"]').length,
expected: 1,
});
});
});
}

PropHelpers {
const createProps = ({
error = '',
loading = false,
onSubmit = () => {},
} = {}) => ({ error, loading, onSubmit });

const props = createProps({ loading: true });
}
}
76 changes: 76 additions & 0 deletions .cursor/rules/reducer-testing.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
description: Guidelines for testing Redux reducers using selector-focused approach
globs:
alwaysApply: false
---

# Reducer Testing

function test() {
actual = selector(state)
expected = what the selector should return
}

ReducerTesting {
Constraints {
Test from the perspective of a selector.
Group tests by selectors (one describe block per selector).
Verify action + root reducer + selector integration.
Assert selector output matches expected value.
When state depends on multiple actions, reduce over an array of actions.
Keep tests focused and minimal - avoid over-testing edge cases.
No factories for simple test data - use inline objects.
Only use factories for complex database entities.
}

TestStructure {
// Group by selector
describe('selectEmailError()', () => {
test('initial state', () => {
const rootState = rootReducer(undefined, {});

assert({
given: 'the application starts',
should: 'initialize with no email error',
actual: selectEmailError(rootState),
expected: '',
});
});

test('action sets error', () => {
const state = rootReducer(undefined, signUpFailed({ emailError: 'Invalid email' }));

assert({
given: 'sign up fails with email error',
should: 'set email error message',
actual: selectEmailError(state),
expected: 'Invalid email',
});
});

test('action clears error', () => {
const actions = [
signUpFailed({ emailError: 'Previous error' }),
signUpClicked({ email: 'valid@email.com' }),
];
const state = actions.reduce(rootReducer, rootReducer(undefined, {}));

assert({
given: 'new sign up action after error',
should: 'clear previous error',
actual: selectEmailError(state),
expected: '',
});
});
});
}

ActionSequences {
// Single action
const state = rootReducer(undefined, someAction());

// Multiple actions
const actions = [firstAction(), secondAction()];
const state = actions.reduce(rootReducer, rootReducer(undefined, {}));
}
}
12 changes: 10 additions & 2 deletions ai/tdd.sudo → .cursor/rules/tdd.mdc
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
---
description: TDD Agent - Use this process any time you are asked to implement a feature or fix a bug.
globs:
alwaysApply: false
---

# TDD Engineer

Act as a top-tier software engineer with serious TDD discipline to systematically implement software using the TDD process.
Expand Down Expand Up @@ -35,19 +41,21 @@ For each unit of code, create a test suite, one requirement at a time:
1. If the user has not specified a test framework or technology stack, ask them before implementing.
1. If the calling API is unspecified, propose a calling API that serves the functional requirements and creates an optimal developer experience.
1. Write a test. Run the test runner and watch the test fail.
1. Implement the code to make the test pass.
1. Call the appropriate sub-agent to implement the code to make the test pass.
1. Run the test runner: fail => fix bug; pass => continue
1. Get approval from the user before moving on.
1. Repeat the TDD iteration process for the next functional requirement.

## Describe/Test Wrappers

In most testing frameworks, there is a `describe` function and possibly a nested `test` or `it` wrapper.
In most testing frameworks, there is a `describe` function and possibly a nested `test` wrapper.

Use the string in the `describe` function to name the unit under test.

Use the string in the `test` function to offer a brief category for the test, e.g. "new account creation".

Use `test` to group assertions with the same setup.

Because of conflicts with the `assert` function API and description, avoid the `it` wrapper entirely, if possible.


Expand Down
14 changes: 0 additions & 14 deletions .cursorrules

This file was deleted.

2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,5 @@ node_modules/
/playwright-report/
/blob-report/
/playwright/.cache/

/src/generated/prisma
Loading