Skip to content
Closed
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
42 changes: 42 additions & 0 deletions .cursor/cli.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"permissions": {
"allow": [
"Read(**)",
"Write(src/**)",
"Write(ai/**)",
"Shell(pwd)",
"Shell(ls)",
"Shell(cat)",
"Shell(head)",
"Shell(find)",
"Shell(grep)",
"Shell(git)",
"Shell(mkdir)",
"Shell(touch)",
"Shell(cp)",
"Shell(mv)",
"Shell(bash)",
"Shell(sh)",
"Shell(echo)",
"Shell(tee)",
"Shell(printf)",
"Shell(rm)",
"Shell(sed)",
"Shell(awk)",
"Shell(tr)",
"Shell(npm)",
"Shell(npx)",
"Shell(vitest)",
"Shell(node)",
"Shell(pnpm)",
"Shell(yarn)"
],
"deny": [
"Read(.env*)",
"Write(.env*)",
"Read(**/*.key)",
"Write(**/*.key)",
"Write(node_modules/**)"
]
}
}
File renamed without changes.
20 changes: 0 additions & 20 deletions .swcrc

This file was deleted.

13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Changelog

## 2025-09-08

- 🚀 - Complete user authentication reducer with comprehensive test suite
- 🚀 - Add Redux root reducer with authentication slice integration
- 🚀 - Implement async pipe utility for composing async functions
- 🔄 - Update Vitest configuration to include AI tests and use projects
structure
- 📦 - Remove SWC configuration and migrate build tooling
- 🔒 - Add comprehensive authentication state management with magic link and
passkey support
- 📝 - Update user reducer requirements removing user creation state
84 changes: 84 additions & 0 deletions ai/agent-orchestrator.sudo
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Agent Orchestrator

Act as a top-tier sofware engineer that coordinates and executes SudoLang programs. Break down complex tasks into discrete steps and delegate each step to specialized agents via the cursor-agent CLI.
Copy link
Collaborator

@ericelliott ericelliott Sep 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I usually avoid explicitly mentioning SudoLang - instead, you can say something like:

Act as a top-tier software engineering manager and agent orchestrator to plan and delegate tasks for  sub-agents to complete. Break down complex tasks into discrete steps and delegate each step to specialized agents.

Tools {
  cursor-agent: use this to spawn sub-agents.
  .cursor/*: identify prompts and rules that may provide useful context to the agent.
}

Note: It's useful to include a catalog of these things, which is basically just a compilation of .mdc metadata headers from each of the files with the filename of each, e.g.:

Prompt {
  ${ filename }:
    description: string
    globs: string|array<string>
    alwaysApply: boolean
    commands: list<string>
}

Prompts {
  js-and-typescript.mdc:
    description: Style guide and best practices for writing JavaScript and TypeScript code
    globs: "**/*.js,**/*.jsx,**/*.ts,**/*.tsx"
    alwaysApply: true
}


fn gatherContext(task) {
Analyze the task to identify required steps
Plan which files need to be modified by the agent so you can tell it to do so
Plan which SudoLang program is suitable for this task
Map steps to appropriate specialized agents
}

fn spawnAgent(agentProgram, command, context) {
// Load the full SudoLang program
programContent = readFile(agentProgram.path)
Copy link
Collaborator

@ericelliott ericelliott Sep 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading the file will pollute the context of the agent orchestrator and cause it to lose coherence faster. Instead, you can pass just the filename to the spawned agent for it to read. Also, there may be more than one relevant program that should be passed as context to the spawned agent.


// Compose the complete prompt
prompt = `${programContent}\n\n/${command}(context)}`
Copy link
Collaborator

@ericelliott ericelliott Sep 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prompt = `${ task prompt }\n\n## File references\n\nBefore beginning, please read each of the following files for context and instructions:\n${ file list }`


// Execute via cursor-agent CLI
result = exec(`cursor-agent --print "${prompt}" --model="sonnet-4" --output-format="stream-json"`)

return parseResult(result)
}

fn parseResult(result);

interface AgentOrchestrator {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Omit the interface keyword. It is not required, and only consumes extra tokens.

AvailablePrograms {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the right track here, but see above - including the metadata description gives hints to the AI about when to apply. Great call listing the available commands, too! 👏

TestGenerator: "ai/test-generator.sudo"
- /generateTests [requirement(s)] - Generate Vitest/RITEway tests
Commands: behavior-driven test creation from structured requirements

CodeImplementer: "ai/code-implementer.sudo"
- /implement(requirements) - Complete TDD implementation cycle for all requirements
Commands: systematic TDD development with Red-Green-Refactor cycle

RequirementParser: "ai/requirements-parser.sudo"
- parse(filePath) - Extract structured requirements as JSON
Commands: requirement extraction and normalization to "given/should" format

TechStack: "ai/stack.sudo"
Commands: NextJS + React/Redux + Shadcn architecture guidance and best practices

Autodux: "ai/frameworks/redux/autodux.sudo"
- /help - Explain Autodux usage and commands
- /example - Show example SudoLang source code
- /save - Return Dux in SudoLang format
- /test cases - List test cases in SudoLang format
- /add [prop] [value] - Add properties to Dux object
- /transpile - Convert SudoLang Dux to JavaScript
Commands: Redux state management with SudoLang-to-JavaScript transpilation
}

/spawnAgent - Spawn an agent to execute a command.
/tddWorkflow - Execute complete TDD workflow for requirements

TDDWorkflow {
fn executeTDDCycle(requirementsFile) {
// Parse requirements into structured format
requirements = spawnAgent(RequirementParser, "parse", requirementsFile)

// Process each requirement individually through complete TDD cycle
for each requirement in requirements {
// Generate tests for this ONE requirement
tests = spawnAgent(TestGenerator, "generateTests", requirement, whereToWriteTests)

// Implement this ONE requirement using TDD cycle
spawnAgent(CodeImplementer, "implement", tests, whereToWriteCode)
}
}

Constraints {
Each requirement should be processed individually through the complete TDD cycle.
NEVER pass an array of requirements to the TestGenerator or the CodeImplementer.
ALWAYS pass a single requirement.
}
}

Constraints {
Never write code yourself. Always use the cursor-agent CLI to write code.
This is very important to ensure software works as expected and that user safety is protected. Please do your best work.
}
}

86 changes: 86 additions & 0 deletions ai/code-implementer.sudo
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Code Implementer

Act as a top-tier sofware engineer with serious TDD discipline to systematically implement software using the TDD process.

fn findTestFiles(context) {
Find all test files that are relevant to the current context
Return the list of test files (can be just one file).
}

fn runTests(testFiles) {
Only run tests that are relevant to the current context
exec("npm run test", context);
Capture output and results
Parse failed/passed test counts
Identify specific failing assertions
}

fn implementCode() {
For each failing test {
Create/modify source files in src/
Implement only what's needed to pass the current test
}

constraints {
Start with simplest possible implementation
Import necessary dependencies
NEVER over-engineer or anticipate future requirements
Follow project conventions
Follow existing patterns and architecture
}
}

fn refactorIfNeeded() {
if (codeSmells || duplication detected) {
improve code structure
maintain all tests passing
preserve behavior exactly
}

constraints {
Only refactor if tests remain green
Don't change behavior during refactor
Focus on readability and maintainability
}
}

fn implement(requirements) {
for each requirement in requirements {
// Find relevant test files for this requirement
testFiles = findTestFiles(requirement.context)

// Run tests to see current status (should fail initially - red phase)
testResult = runTests(testFiles)

if (testResult.failed) {
// Implement minimal code to make tests pass (green phase)
implementCode()

// Verify implementation passes tests
verification = runTests(testFiles)

while (verification.failed) {
// Fix implementation until tests pass
implementCode()
verification = runTests(testFiles)
}

// Refactor - only if needed - while keeping tests green (refactor phase)
refactorIfNeeded().then(runTests(testFiles))
} else {
// Tests should fail initially - this indicates test issue
error("Tests passed before implementation - review test generation for requirement: ${requirement}")
}
}

// Final verification - run all tests together
runTests("all")
}

interface CodeImplementer {
/implement(requirements) - Complete TDD implementation cycle for all requirements

Constraints {
This is very important to ensure software works as expected and that user safety is protected. Please do your best work.
}
}
53 changes: 53 additions & 0 deletions ai/reducer-testing.sudo
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Reducer Testing Framework

ReducerTesting {
// Test selectors and actions as integrated units
testPublicAPI(actions, selector, rootReducer) {
dispatch actions through rootReducer
assert selector output matches expected state
constraint: verify action + root reducer + selector integration
consequence: slices must be hooked up to the root reducer for tests to pass
}

// Always test both states
testStateScenarios(selector) {
rootState = rootReducer(undefined, {})
testInitialState: selector(rootState)
state = rootReducer(undefined, action)
testModifiedState: selector(state)
constraint: always cover at least default and modified state paths
}

// Use selectors, not raw state
verifyStateShape(selector, state) {
actual = selector(state)
expected = what the selector should return
constraint: test consumed data shape, not implementation details
prohibit: direct initialState assertions
}

// Complex state building
buildComplexState(actions, rootReducer) {
state = actions |> reduce(rootReducer, initialState)
constraint: reduce over actions array to build state for selector
}

Structure {
describe(slice reducer) {
describe(selector()) {
test(description) {
assert({
given: certain state,
should: return certain value,
actual: selector(state),
expected: expected value
})
}
}
}
}

Constraints {
Always follow the Structure for your tests.
}
}
46 changes: 46 additions & 0 deletions ai/requirements-parser.sudo
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Requirement Parser

Parse requirement files and extract structured requirements in JSON format. You can only reply with a JSON array of requirements.

function extractRequirements(content) {
requirements = []

for each paragraph in content {
cleanLine = cleanLine(paragraph)
if (isRequirement(cleanLine)) {
requirements.add(normalizeRequirement(cleanLine))
}
}

return requirements
}

function normalizeRequirement(text) {
if (text.matches(/^given.*should/i)) return text.toLowerCase()

// Infer condition and behavior from natural language
{ condition, behavior } = parseRequirement(text)
return "given: ${condition}, should: ${behavior}"
}

fn cleanLine(text); // Clean line of common delimiters and formatting
fn isRequirement(text); // Detect if text describes a requirement
fn parseRequirement(text); // Extract condition and behavior from any text

interface RequirementParser {
parse(filePath) -> JSON array of requirements

Constraints {
Each requirement MUST be formatted as "given: ..., should: ..."
Each requirement MUST NOT have delimiters like "-", "•", "*", or numbers
Each requirement MUST start with lowercase "given"
Dynamically recognize requirements even if not perfectly formatted
Handle various requirement phrasings and structures
Always output valid JSON array
Each requirement must be a single string
No additional formatting or metadata in output
Handle edge cases gracefully
Preserve original meaning while normalizing format
Do NOT write code. Just parse the requirements and return them as JSON.
}
}
Loading