├── .cursor ├── .gitignore ├── docs └── tap-color-screenshot.png ├── match.js ├── vitest.js ├── match.d.ts ├── source ├── index.js ├── vitest.js ├── render-component.js ├── match.js ├── match-test.js ├── riteway.js ├── test.js └── vitest.test.jsx ├── tea.yaml ├── render-component.js ├── ai ├── commands │ ├── commit.md │ ├── log.md │ ├── plan.md │ ├── task.md │ ├── execute.md │ ├── review.md │ ├── discover.md │ └── help.md └── rules │ ├── requirements.mdc │ ├── commit.mdc │ ├── ui.mdc │ ├── frameworks │ └── redux │ │ ├── example.mdc │ │ └── autodux.mdc │ ├── log.mdc │ ├── stack.mdc │ ├── review.mdc │ ├── please.mdc │ ├── javascript │ ├── javascript-io-network-effects.mdc │ └── javascript.mdc │ ├── agent-orchestrator.mdc │ ├── productmanager.mdc │ ├── tdd.mdc │ └── task-creator.mdc ├── .travis.yml ├── render-component.d.ts ├── renovate.json ├── vitest.d.ts ├── activity-log.md ├── vitest.config.js ├── .release-it.json ├── .eslintrc.json ├── index.d.ts ├── LICENSE ├── eslint.config.js ├── plan.md ├── bin ├── riteway └── riteway.test.js ├── package.json ├── tasks ├── 2025-01-27-context7-github-action-epic.md └── archive │ ├── 2025-09-27-release-script-improvement-epic.md │ └── 2025-09-27-modernize-test-runner-epic.md ├── release.js └── README.md /.cursor: -------------------------------------------------------------------------------- 1 | ai -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .esm-cache/ 3 | -------------------------------------------------------------------------------- /docs/tap-color-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paralleldrive/riteway/HEAD/docs/tap-color-screenshot.png -------------------------------------------------------------------------------- /match.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-global-assign 2 | require = require('esm')(module); 3 | module.exports = require('./source/match'); 4 | -------------------------------------------------------------------------------- /vitest.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-global-assign 2 | require = require('esm')(module); 3 | module.exports = require('./source/vitest'); -------------------------------------------------------------------------------- /match.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'riteway/match' { 2 | export default function match( 3 | text: string 4 | ): (pattern: string | RegExp) => string; 5 | } 6 | -------------------------------------------------------------------------------- /source/index.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-global-assign 2 | require = require('esm')(module); 3 | module.exports = require('./riteway.js'); 4 | 5 | -------------------------------------------------------------------------------- /tea.yaml: -------------------------------------------------------------------------------- 1 | # https://tea.xyz/what-is-this-file 2 | --- 3 | version: 1.0.0 4 | codeOwners: 5 | - '0x85fb7e6Fc8FC092e0D279A48988840fa0090c3E5' 6 | quorum: 1 7 | -------------------------------------------------------------------------------- /render-component.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-global-assign 2 | require = require('esm')(module); 3 | module.exports = require('./source/render-component'); 4 | -------------------------------------------------------------------------------- /ai/commands/commit.md: -------------------------------------------------------------------------------- 1 | ## 💾 Commit 2 | 3 | Use commit.mdc to commit the changes to the repository. 4 | Before beginning, read and respect the constraints in please.mdc. 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 18 4 | install: 5 | - npm install --legacy-peer-deps 6 | before_script: 7 | - export NODE_OPTIONS=–max_old_space_size=8192 -------------------------------------------------------------------------------- /ai/commands/log.md: -------------------------------------------------------------------------------- 1 | ## 📝 Log 2 | 3 | Use log.mdc to collect salient changes, and log them to the activity-log.md. 4 | Before beginning, read and respect the constraints in please.mdc. 5 | -------------------------------------------------------------------------------- /ai/commands/plan.md: -------------------------------------------------------------------------------- 1 | ## 📋 Plan 2 | 3 | Review plan.md to identify priorities and suggest next steps to the user -d 10. 4 | Before beginning, read and respect the constraints in please.mdc. 5 | -------------------------------------------------------------------------------- /source/vitest.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'vitest'; 2 | 3 | export const assert = ({ given, should, actual, expected }) => { 4 | expect(actual, `Given ${given}: should ${should}`).toStrictEqual(expected); 5 | }; 6 | -------------------------------------------------------------------------------- /render-component.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | declare module 'riteway/render-component' { 5 | export default function render(el: JSX.Element): cheerio.Root; 6 | } 7 | -------------------------------------------------------------------------------- /source/render-component.js: -------------------------------------------------------------------------------- 1 | import reactDom from 'react-dom/server'; 2 | import { load } from 'cheerio'; 3 | 4 | const render = component => 5 | load(reactDom.renderToStaticMarkup(component)); 6 | 7 | export default render; 8 | 9 | -------------------------------------------------------------------------------- /ai/commands/task.md: -------------------------------------------------------------------------------- 1 | ## ✅ Task 2 | 3 | Use the task creator to plan and execute a task epic. 4 | 5 | Constraints { 6 | Before beginning, read and respect the constraints in please.mdc. 7 | Remember to use the TDD process if asked to implement code. 8 | } 9 | -------------------------------------------------------------------------------- /ai/commands/execute.md: -------------------------------------------------------------------------------- 1 | ## ⚙️ Execute Task/Epic 2 | 3 | Use the task creator to execute a task epic. 4 | 5 | Constraints { 6 | Before beginning, read and respect the constraints in please.mdc. 7 | Remember to use the TDD process if asked to implement code. 8 | } 9 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "automerge": true, 6 | "automergeType": "branch", 7 | "major": { 8 | "automerge": false 9 | }, 10 | "schedule": "before 4am", 11 | "timezone": "America/Los_Angeles" 12 | } 13 | -------------------------------------------------------------------------------- /ai/commands/review.md: -------------------------------------------------------------------------------- 1 | # 🔬 Code Review 2 | 3 | use review.mdc to conduct a thorough code review focusing on code quality, best practices, and adherence to project standards. 4 | 5 | Constraints { 6 | Before beginning, read and respect the constraints in please.mdc. 7 | } 8 | -------------------------------------------------------------------------------- /ai/commands/discover.md: -------------------------------------------------------------------------------- 1 | ## 🔍 Discover 2 | 3 | Use productmanager.mdc to discover a user journey, user story, or feature. 4 | 5 | Constraints { 6 | Begin by reading the file and asking the user relevant questions to spark the discovery process. 7 | Before beginning, read and respect the constraints in please.mdc. 8 | } 9 | -------------------------------------------------------------------------------- /vitest.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | declare module 'riteway/vitest' { 5 | interface Assertion { 6 | readonly given: string; 7 | readonly should: string; 8 | readonly actual: T; 9 | readonly expected: T; 10 | } 11 | 12 | export function assert(assertion: Assertion): void; 13 | } -------------------------------------------------------------------------------- /ai/commands/help.md: -------------------------------------------------------------------------------- 1 | ## ❓ Help 2 | 3 | List commands from please.mdc and report them to the user. 4 | 5 | Constraints { 6 | Before beginning, read and respect the constraints in please.mdc. 7 | Keep the response extremely concise - essentially just the list of commands, their descriptions, and options, without offering trivial details or informing users of constraints. 8 | } 9 | -------------------------------------------------------------------------------- /activity-log.md: -------------------------------------------------------------------------------- 1 | # Activity Log 2 | 3 | ## 2025-10-06 4 | 5 | - ✅ - Completed Modernize Test Runner Epic: Verified native ES module support working with dual test runner setup (31 core tests + 6 Vitest tests), updated documentation to reflect current JSX transpilation requirements 6 | 7 | ## 2025-09-27 8 | 9 | - 🚀 - Added complete AIDD (AI Driven Development) system with commands and rules 10 | - 🚀 - Enhanced automated release system with robust error handling and validation 11 | -------------------------------------------------------------------------------- /vitest.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | exclude: [ 6 | '**/node_modules/**', 7 | '**/dist/**', 8 | '**/cypress/**', 9 | '**/.{idea,git,cache,output,temp}/**', 10 | '**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,eslint,prettier}.config.*', 11 | // Exclude bin/riteway.test.js as it uses Tape instead of Vitest 12 | '**/bin/riteway.test.js' 13 | ] 14 | } 15 | }); 16 | 17 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "git": { 3 | "commitMessage": "chore(release): v${version}", 4 | "tagName": "v${version}", 5 | "requireCleanWorkingDir": true, 6 | "requireBranch": ["main", "master", "release"] 7 | }, 8 | "github": { 9 | "release": true, 10 | "releaseName": "v${version}", 11 | "autoGenerate": { 12 | "template": "compact" 13 | } 14 | }, 15 | "npm": { 16 | "publish": true 17 | }, 18 | "hooks": { 19 | "before:init": ["npm test"], 20 | "after:release": "echo 🎉 Successfully released ${name} v${version}" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ai/rules/requirements.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: When writing functional requirements for a user story, use this guide for functional requirement specification 3 | alwaysApply: false 4 | --- 5 | # Functional requirements 6 | 7 | Act as a senior product manager to write functional requirements for a user story. 8 | 9 | type FunctionalRequirement = "Given $situation, should $jobToDo" 10 | 11 | Constraints { 12 | Focus on functional requirements to support the user journey. 13 | Avoid describing specific UI elements or interactions, instead, focus on the job the user wants to accomplish and the benefits we expect the user to achieve. 14 | } 15 | -------------------------------------------------------------------------------- /source/match.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Take some text to search and returns a function 3 | * which takes a pattern and returns the matched text, 4 | * if found, or an empty string. The pattern can be a 5 | * string or regular expression. 6 | * 7 | * @param {string} text The text to search. 8 | * @returns {(pattern: string|Object) => string} 9 | */ 10 | const match = text => pattern => { 11 | const RE = new RegExp(typeof pattern === 'string' ? escapeRegex(pattern) : pattern); 12 | const matched = text.match(RE); 13 | return matched ? matched[0] : ''; 14 | }; 15 | 16 | const escapeRegex = string => 17 | string.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); 18 | 19 | export default match; 20 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:react/recommended" 10 | ], 11 | "parserOptions": { 12 | "sourceType": "module", 13 | "ecmaVersion": "2017", 14 | "ecmaFeatures": { 15 | "jsx": true 16 | } 17 | }, 18 | "settings": { 19 | "react": { 20 | "version": "detect" 21 | } 22 | }, 23 | "rules": { 24 | "indent": [ 25 | "error", 26 | 2 27 | ], 28 | "linebreak-style": [ 29 | "error", 30 | "unix" 31 | ], 32 | "quotes": [ 33 | "error", 34 | "single" 35 | ], 36 | "semi": [ 37 | "error", 38 | "always" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ai/rules/commit.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: When committing code, use this guide for conventional commit format with proper message structure 3 | alwaysApply: false 4 | --- 5 | # Commit 6 | 7 | Act as a senior software engineer to commit changes to the repository in non-interactive modes ONLY, using the following template: 8 | 9 | "$type${[(scope)]}{[!]}: $description":where `[]` is optional and `!` is a breaking change 10 | 11 | Types: fix|feat|chore|docs|refactor|test|perf|build|ci|style|revert|$other 12 | 13 | If we haven't logged yet, use log.sudo to log changes before committing. 14 | 15 | Constraints { 16 | When committing, don't log about logging in the commit message. 17 | Use multiple -m flags, one for each log entry. 18 | Limit the first commit message line length to 50 characters. 19 | } 20 | -------------------------------------------------------------------------------- /ai/rules/ui.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: When building user interfaces and user experiences, use this guide for beautiful and friendly UI/UX design 3 | alwaysApply: false 4 | --- 5 | 6 | # UI/UX Engineer 7 | 8 | Act as a top-tier UI/UX designer with deep skills in user interface design, user experience design, aesthetics, extraordinarily good taste, an eye for detail, and a passion for building the most beautiful and friendly user interfaces and experiences. You are also a top tier motion designer, skilled in subtle but delightful and satisfying motion design for UX. 9 | 10 | When building UI components, please use the existing project design system and storybook components. Focus on creating intuitive, accessible, and visually appealing interfaces that enhance user experience. 11 | 12 | Skills [ 13 | CSS 14 | HTML 15 | JavaScript 16 | React 17 | Animation 18 | Motion design 19 | Graphic design 20 | UI/UX design 21 | Accessibility 22 | Responsive design 23 | Design systems 24 | ] -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | declare module 'riteway' { 5 | 6 | export function Try(fn: (...args: U) => V, ...args: U): any | Promise 7 | 8 | export function createStream(opts: CreateStreamOptions): ReadableStream 9 | 10 | export const describe: DescribeFunction; 11 | 12 | interface DescribeFunction { 13 | (unit: string, testFunction: TestFunction): Promise 14 | only: (unit: string, testFunction: TestFunction) => Promise 15 | skip: (unit: string, testFunction: TestFunction) => Promise 16 | } 17 | 18 | type assert = (assertion: Assertion) => void 19 | 20 | type TestFunction = (assert: assert, end?: Function) => Promise 21 | 22 | interface Assertion { 23 | readonly given: any 24 | readonly should: string 25 | readonly actual: T 26 | readonly expected: T 27 | } 28 | 29 | interface CreateStreamOptions { 30 | readonly objectMode: boolean 31 | } 32 | } -------------------------------------------------------------------------------- /ai/rules/frameworks/redux/example.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: Autodux usage example showing Todo App implementation in SudoLang 3 | alwaysApply: false 4 | --- 5 | ## AutoduxUsage Example: Todo App 6 | 7 | /* 8 | This is an example SudoLang source that can be transpiled to JavaScript using: 9 | 10 | ``` 11 | TodoDux |> transpile(JavaScript) 12 | ``` 13 | 14 | We recommend authoring all code in SudoLang and transpiling to JavaScript when you need to provide detailed specifications to AI agents. 15 | */ 16 | 17 | Todo Item { 18 | id, 19 | text, 20 | isComplete, 21 | } 22 | 23 | createTodo({ text = '', id = createId(), isComplete = false } = {}) => ActionObject 24 | deleteTodo(id) => ActionObject 25 | toggleComplete(id) => ActionObject 26 | 27 | TodoDux { 28 | initialState = [] 29 | slice = 'todo' 30 | actions = [createTodo, deleteTodo, toggleComplete] 31 | selectors = [getTodos, getIncompleteTodos, getCompleteTodos] 32 | mapStateToProps 33 | mapDispatchToProps 34 | connectedComponentName = TodoList 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Eric Elliott 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ai/rules/log.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: When documenting changes, use this guide for creating structured change logs with emoji categorization 3 | alwaysApply: false 4 | --- 5 | # log 6 | 7 | Act as a senior software engineer to log changes to the repository using the following template: 8 | 9 | ``` 10 | ## $date 11 | 12 | - $emoji - $change1 13 | - $emoji -$change2 14 | ``` 15 | 16 | # Emojis 17 | 18 | Use the following emoji to represent the change: 19 | 20 | - 🚀 - new feature 21 | - 🐛 - bug fix 22 | - 📝 - documentation 23 | - 🔄 - refactor 24 | - 📦 - dependency update 25 | - 🎨 - design 26 | - 📱 - UI/UX 27 | - 📊 - analytics 28 | - 🔒 - security 29 | 30 | Constraints { 31 | Always use reverse chronological order. 32 | Add most recent changes to the top. 33 | Never log about logging. Avoid logging meta-work. Instead, log salient, user-impacting changes. 34 | } 35 | 36 | 37 | gitChanges() { 38 | git add . 39 | git --no-pager diff --cached 40 | } 41 | 42 | planChanges() { 43 | Check the plan diff to detect recently completed plan tasks. 44 | } 45 | 46 | detectChanges() { 47 | gitChanges |> planChanges |> logDetectedChanges 48 | } -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js'; 2 | import react from 'eslint-plugin-react'; 3 | 4 | export default [ 5 | js.configs.recommended, 6 | { 7 | files: ['**/*.js', '**/*.jsx'], 8 | languageOptions: { 9 | ecmaVersion: 2017, 10 | sourceType: 'module', 11 | parserOptions: { 12 | ecmaFeatures: { 13 | jsx: true 14 | } 15 | }, 16 | globals: { 17 | // Browser environment 18 | window: 'readonly', 19 | document: 'readonly', 20 | console: 'readonly', 21 | // CommonJS environment 22 | require: 'readonly', 23 | module: 'readonly', 24 | exports: 'readonly', 25 | global: 'readonly', 26 | process: 'readonly', 27 | // ES6 environment 28 | Promise: 'readonly', 29 | Set: 'readonly', 30 | Map: 'readonly', 31 | Symbol: 'readonly', 32 | // Node.js timer functions 33 | setTimeout: 'readonly', 34 | clearTimeout: 'readonly' 35 | } 36 | }, 37 | plugins: { 38 | react 39 | }, 40 | rules: Object.assign({}, react.configs.recommended.rules, { 41 | 'indent': ['error', 2], 42 | 'linebreak-style': ['error', 'unix'], 43 | 'quotes': ['error', 'single'], 44 | 'semi': ['error', 'always'] 45 | }), 46 | settings: { 47 | react: { 48 | version: 'detect' 49 | } 50 | } 51 | } 52 | ]; 53 | -------------------------------------------------------------------------------- /source/match-test.js: -------------------------------------------------------------------------------- 1 | import { describe } from './riteway.js'; 2 | import match from './match.js'; 3 | 4 | describe('match', async assert => { 5 | { 6 | const given = 'some text to search and a pattern to match'; 7 | const should = 'return the matched text'; 8 | 9 | const textToSearch = '

Dialog Title

'; 10 | const pattern = 'Dialog Title'; 11 | const contains = match(textToSearch); 12 | 13 | assert({ 14 | given, 15 | should, 16 | actual: contains(pattern), 17 | expected: pattern, 18 | }); 19 | } 20 | 21 | { 22 | const given = 'some text with digits'; 23 | const should = 'return the matched text'; 24 | 25 | const textWithDigit = '

There are 4 cats

'; 26 | const pattern = /\d+\s\w+/i; 27 | const contains = match(textWithDigit); 28 | 29 | assert({ 30 | given, 31 | should, 32 | actual: contains(pattern), 33 | expected: '4 cats' 34 | }); 35 | } 36 | 37 | { 38 | const given = 'some text that includes regex meta characters'; 39 | const should = 'return the matched text'; 40 | 41 | const textWithRegexMetaChar = '

Are there any cats?

'; 42 | const pattern = 'Are there any cats?'; 43 | const contains = match(textWithRegexMetaChar); 44 | 45 | assert({ 46 | given, 47 | should, 48 | actual: contains(pattern), 49 | expected: 'Are there any cats?' 50 | }); 51 | } 52 | 53 | }); 54 | -------------------------------------------------------------------------------- /plan.md: -------------------------------------------------------------------------------- 1 | # Riteway Project Plan 2 | 3 | ## Current Epics 4 | 5 | ### 📋 Context7 GitHub Action Integration Epic 6 | **Status**: 📋 PLANNING - Awaiting Approval 7 | **File**: [`tasks/2025-01-27-context7-github-action-epic.md`](./tasks/2025-01-27-context7-github-action-epic.md) 8 | **Goal**: Add Upsert Context7 GitHub Action to repository with automatic updates after successful release script runs 9 | 10 | ## Completed Epics 11 | 12 | ### ✅ Modernize Test Runner Epic 13 | **Status**: ✅ COMPLETED (2025-10-06) 14 | **File**: [`tasks/archive/2025-09-27-modernize-test-runner-epic.md`](./tasks/archive/2025-09-27-modernize-test-runner-epic.md) 15 | **Goal**: Modernize test runner to support native ES modules by separating JSX component tests from core Riteway functionality 16 | **Result**: Verified native ES module support working with dual test runner setup (31 core tests + 6 Vitest tests). Updated documentation to reflect current JSX transpilation requirements. 17 | 18 | ### ✅ Release Script Improvement Epic 19 | **Status**: ✅ COMPLETED (2025-09-27) 20 | **File**: [`tasks/archive/2025-09-27-release-script-improvement-epic.md`](./tasks/archive/2025-09-27-release-script-improvement-epic.md) 21 | **Goal**: Enhance Riteway's automated release system with robust error handling and validation 22 | **Result**: Successfully implemented enhanced release script with error-causes integration, comprehensive error handling, branch validation, and user-friendly CLI. All 5 tasks completed successfully. 23 | 24 | ## Backlog 25 | 26 | *No items in backlog* 27 | 28 | -------------------------------------------------------------------------------- /ai/rules/stack.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: When implementing NextJS + React/Redux + Shadcn UI features, use this guide for tech stack guidance and best practices 3 | alwaysApply: false 4 | --- 5 | # Tech Stack 6 | 7 | Act as a top-tier senior full stack software engineer. Always use best practices, declarative approaches, concise code. 8 | 9 | Before employing any of the tech stack tools, list some relevant best practices for that technology, and keep them in mind as you code. 10 | 11 | NextJS + React/Redux + Shadcn to be deployed on Vercel 12 | 13 | # JS 14 | 15 | Always use functional programming approaches. 16 | Favor pure functions, immutability, function composition, and declarative approaches. 17 | Favor `const` over `let` and `var` whenever possible. 18 | Use redux-saga for side effects. 19 | Always separate state management, UI, and side-effects from each other in different modules. 20 | 21 | # React 22 | 23 | Constraints { 24 | Always use the container/presentation pattern when you need persisted state. 25 | Containers should never contain any direct UI markup (instead, import and use presentation components). 26 | Containers should NEVER contain business logic. Instead, use react-redux connect to wire actions and selectors to presentation components. 27 | } 28 | 29 | # Redux 30 | 31 | Avoid Redux Toolkit. Use frameworks/redux/autodux and redux connect instead. 32 | 33 | 1. Build the Autodux dux object and save it as "${slice name}-dux.sudo" 34 | 2. Transpile to JavaScript and save it as "${slice name}-dux.js" 35 | 36 | Constraints { 37 | ALWAYS use tdd as defined in tdd.mdc when implementing source code changes. 38 | NEVER change source code without clear requirements, tests, and/or manual user approval of your plan. 39 | } -------------------------------------------------------------------------------- /ai/rules/review.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: Use this guide to conduct a thorough code review focusing on code quality, best practices, and adherence to project standards. 3 | globs: **/*.js,**/*.jsx,**/*.ts,**/*.tsx 4 | alwaysApply: false 5 | --- 6 | # 🔬 Code Review 7 | 8 | Act as a top-tier principal software engineer to conduct a thorough code review focusing on code quality, best practices, and adherence to requirements, plan, and project standards. 9 | 10 | Criteria { 11 | Before beginning, read and respect the constraints in please.mdc. 12 | Use javascript.mdc for JavaScript/TypeScript code quality and best practices. 13 | Use tdd.mdc for test coverage and test quality assessment. 14 | Use stack.mdc for NextJS + React/Redux + Shadcn UI architecture and patterns. 15 | Use ui.mdc for UI/UX design and component quality. 16 | Use autodux.mdc for Redux state management patterns and Autodux usage. 17 | Use javascript-io-network-effects.mdc for network effects and side effect handling. 18 | Use commit.mdc for commit message quality and conventional commit format. 19 | Compare the completed work to the functional requirements to ensure adherence and that all requirements are met. 20 | Compare the task plan in $projectRoot/tasks/ to the completed work to ensure that all tasks were completed and that the completed work adheres to the plan. 21 | } 22 | 23 | Review Process { 24 | 1. Analyze code structure and organization 25 | 2. Check adherence to coding standards and best practices 26 | 3. Evaluate test coverage and quality 27 | 4. Assess performance and security considerations 28 | 5. Review UI/UX implementation and accessibility 29 | 6. Validate architectural patterns and design decisions 30 | 7. Check documentation and commit message quality 31 | 8. Provide actionable feedback with specific improvement suggestions 32 | } 33 | 34 | Commands { 35 | 🔬 /review - conduct a thorough code review focusing on code quality, best practices, and adherence to project standards 36 | } 37 | -------------------------------------------------------------------------------- /source/riteway.js: -------------------------------------------------------------------------------- 1 | import tape from 'tape'; 2 | 3 | const noop = new Function(); 4 | const isPromise = x => x && typeof x.then === 'function'; 5 | const requiredKeys = ['given', 'should', 'actual', 'expected']; 6 | const concatToString = (keys, key, index) => keys + (index ? ', ' : '') + key; 7 | 8 | const withRiteway = TestFunction => test => { 9 | const end = () => test.end(); 10 | 11 | const assert = (args = {}) => { 12 | const missing = requiredKeys.filter( 13 | k => !Object.keys(args).includes(k) 14 | ); 15 | if (missing.length) { 16 | throw new Error(`The following parameters are required by \`assert\`: ${missing.reduce(concatToString, '')}`); 17 | } 18 | 19 | const { 20 | // initialize values to undefined so TypeScript doesn't complain 21 | given = undefined, 22 | should = '', 23 | actual = undefined, 24 | expected = undefined 25 | } = args; 26 | 27 | test.same( 28 | actual, expected, 29 | `Given ${given}: should ${should}` 30 | ); 31 | }; 32 | 33 | const result = TestFunction(assert, end); 34 | 35 | if (isPromise(result)) return result.then(end); 36 | }; 37 | 38 | const withTape = tapeFn => (unit = '', TestFunction = noop) => tapeFn(unit, withRiteway(TestFunction)); 39 | 40 | // The testing library: a thin wrapper around tape 41 | const describe = Object.assign(withTape(tape), { 42 | only: withTape(tape.only), 43 | skip: tape.skip 44 | }); 45 | 46 | const catchAndReturn = x => x.catch(x => x); 47 | const catchPromise = x => isPromise(x) ? catchAndReturn(x) : x; 48 | 49 | const Try = (fn = noop, ...args) => { 50 | try { 51 | return catchPromise(fn(...args)); 52 | } catch (err) { 53 | return err; 54 | } 55 | }; 56 | 57 | const createStream = tape.createStream.bind(tape); 58 | 59 | /** 60 | * Given an object, return a count of the object's own properties. 61 | * 62 | * @param {object} [obj] The object whose keys you wish to count. 63 | * @returns {number} 64 | */ 65 | const countKeys = (obj = {}) => Object.keys(obj).length; 66 | 67 | export default describe; 68 | export { describe, Try, createStream, countKeys }; 69 | -------------------------------------------------------------------------------- /ai/rules/please.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: When user says "please", use this guide for general assistance, logging, committing, and proofing tasks 3 | alwaysApply: false 4 | --- 5 | # Aiden 6 | 7 | Act as a senior software engineer, product manager, project manager, and technical writer. Your job is to assist with software development projects. 8 | 9 | Think() deeply when a complex task is presented. 10 | Read the project README.md and stack.mdc before responding. 11 | 12 | UnrecognizedCommand => check the agent orchestrator for relevant instructions. 13 | 14 | 15 | # Thinking: Reflective Thought Composition (RTC) 16 | 17 | fn think() { 18 | show your work: 19 | 🎯 restate |>💡 ideate |> 🪞 reflectCritically |> 🔭 expandOrthogonally |> ⚖️ scoreRankEvaluate |> 💬 respond 20 | 21 | Constraints { 22 | Keep the thinking process concise, compact, and information-dense, ranging from a few words per step (d=1) to a few bullet points per step (d = 10). 23 | } 24 | } 25 | 26 | Options { 27 | --depth | -d [1..10] - Set response depth. 1 = ELIF, 10 = prep for PhD 28 | } 29 | 30 | Commands { 31 | ❓ /help - List commands from please.mdc and report the available commands to the user without modifying any files 32 | 📝 /log - use log.mdc to collect salient changes, and log them to the activity-log.md. 33 | 💾 /commit - use commit.mdc to commit the changes to the repository. 34 | 📋 /plan - review plan.md to identify priorities and suggest next steps to the user -d 10 35 | 🔍 /discover - use productmanager.mdc to discover a user journey, user story, or feature. 36 | ✅ /task - use the task creator to plan and execute a task epic 37 | ⚙️ /execute - use the task creator to execute a task epic 38 | 🔬 /review - conduct a thorough code review focusing on code quality, best practices, and adherence to project standards 39 | } 40 | 41 | Constraints { 42 | When executing commands, do not modify any files unless the command explicitly requires it or the user explicitly asks you to. Instead, focus your interactions on the chat. 43 | 44 | When executing commands, show the command name and emoji to the user chat. 45 | 46 | Do ONE THING at a time, get user approval before moving on. 47 | } -------------------------------------------------------------------------------- /bin/riteway: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { resolve as resolvePath } from 'path'; 4 | import { readFileSync } from 'fs'; 5 | import resolve from 'resolve'; 6 | import minimist from 'minimist'; 7 | import { globSync } from 'glob'; 8 | import dotignore from 'dotignore'; 9 | 10 | const resolveModule = resolve.sync; 11 | const createMatcher = dotignore.createMatcher; 12 | 13 | const asyncPipe = (...fns) => x => fns.reduce(async (y, f) => f(await y), x); 14 | 15 | export const parseArgs = (argv) => { 16 | const opts = minimist(argv, { 17 | alias: { r: 'require', i: 'ignore' }, 18 | string: ['require', 'ignore'], 19 | default: { r: [], i: null } 20 | }); 21 | 22 | return { 23 | require: Array.isArray(opts.require) ? opts.require : [opts.require].filter(Boolean), 24 | ignore: opts.ignore, 25 | patterns: opts._, 26 | cwd: process.cwd() 27 | }; 28 | }; 29 | 30 | export const loadModules = async ({ require: modules, ...rest }) => { 31 | await Promise.all( 32 | modules.map(async (module) => { 33 | const options = { basedir: rest.cwd, extensions: ['.js', '.mjs', '.json'] }; 34 | await import(resolveModule(module, options)); 35 | }) 36 | ); 37 | return { require: modules, ...rest }; 38 | }; 39 | 40 | export const createIgnoreMatcher = ({ ignore, cwd, ...rest }) => { 41 | if (!ignore) return { ...rest, cwd, matcher: null }; 42 | 43 | try { 44 | const ignoreStr = readFileSync(resolvePath(cwd, ignore || '.gitignore'), 'utf-8'); 45 | return { ...rest, cwd, matcher: createMatcher(ignoreStr) }; 46 | } catch (e) { 47 | console.error(e.message); 48 | process.exit(2); 49 | } 50 | }; 51 | 52 | export const resolveTestFiles = ({ patterns, matcher, cwd, ...rest }) => { 53 | const files = patterns 54 | .flatMap(pattern => globSync(pattern)) 55 | .filter(file => !matcher || !matcher.shouldIgnore(file)) 56 | .map(file => resolvePath(cwd, file)); 57 | 58 | return { ...rest, files }; 59 | }; 60 | 61 | export const runTests = async ({ files }) => { 62 | await Promise.all(files.map(file => import(file))); 63 | }; 64 | 65 | const main = asyncPipe( 66 | parseArgs, 67 | loadModules, 68 | createIgnoreMatcher, 69 | resolveTestFiles, 70 | runTests 71 | ); 72 | 73 | main(process.argv.slice(2)).catch(console.error); 74 | -------------------------------------------------------------------------------- /ai/rules/javascript/javascript-io-network-effects.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: When you need to make network requests or invoke side-effects, use this guide for saga pattern implementation 3 | alwaysApply: false 4 | --- 5 | # JavaScript IO Guide 6 | 7 | Act as a top-tier software engineer with serious JavaScript/TypeScript discipline to isolate network I/O and effects using the saga pattern. 8 | 9 | The saga pattern consists of two main functions: 10 | 11 | - `call` 12 | - `put` 13 | 14 | ## call(fn, ...args) => { CALL: { fn, args } } 15 | 16 | The `call` function takes a function and arguments and returns an object with those references as props. Use it to make network requests or invoke other side effects. 17 | 18 | The saga itself never calls the effect function. Instead, it yields the effect object. This allows the saga to behave deterministically with no side-effects, allowing you to test and debug the saga without running the side effects. You can then pass any result or error back into the saga to test various branches of the saga without mocking the integrated components. 19 | 20 | ## put(action) => { PUT: Action } 21 | 22 | The `put` function is used to dispatch an action to the store. Use it to update tha state. 23 | 24 | 25 | ## Action 26 | 27 | An action is an object with a `type` property and a `payload` property. It's used in central dispatch architectures such as Redux to update the state in a way that provides obvservability into semantic user intents, a complete log of user actions, along with the specific components (slices) that originated the action. 28 | 29 | 30 | ## Saga Runtime 31 | 32 | Saga driver runtime runs the sagas, handles side effects, passes data back into the saga, and dispatches `put` actions to the store. 33 | 34 | ## Testing sagas 35 | 36 | To test sagas, you can drive the saga by calling `iterator.next(optionalValue)`. 37 | 38 | e.g. 39 | 40 | ```javascript 41 | describe("signInSaga happy path", async assert => { 42 | const gen = signInUser(); 43 | 44 | assert({ 45 | given: "load user triggered", 46 | should: "call fetchUser with id", 47 | actual: gen.next().value, 48 | expected: call(fetchUser, "42") 49 | }); 50 | 51 | const fakeUser = { id: "42", name: "Pup" }; 52 | 53 | assert({ 54 | given: "second yield", 55 | should: "put the user data into the store", 56 | actual: gen.next(fakeUser).value, 57 | expected: put(userLoaded(fakeUser)) 58 | }); 59 | 60 | assert({ 61 | given: "completion", 62 | should: "be done", 63 | actual: gen.next().done, 64 | expected: true 65 | }); 66 | }); 67 | ``` -------------------------------------------------------------------------------- /ai/rules/agent-orchestrator.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: Senior software engineer, product manager, project manager, and technical writer assistant with reflective thinking 3 | globs: 4 | alwaysApply: true 5 | --- 6 | # Aiden Agent Orchestrator 7 | 8 | Act as a top-tier software engineer, product manager, project manager, and technical writer assistant with reflective thinking. Your job is to assist with software development projects. 9 | 10 | userRequestIncludes => 11 | please => please.mdc 12 | 13 | You are an agent orchestrator. You are responsible for coordinating the actions of the other agents, which are all available in `.cursor/*.mdc` files: 14 | 15 | Agents { 16 | please: when user says "please", use this guide for general assistance, logging, committing, and proofing tasks 17 | stack: when implementing NextJS + React/Redux + Shadcn UI features, use this guide for tech stack guidance and best practices 18 | productmanager: when planning features, user stories, user journeys, or conducting product discovery, use this guide for building specifications and user journey maps 19 | tdd: when implementing code changes, use this guide for systematic test-driven development with proper test isolation 20 | javascript: when writing JavaScript or TypeScript code, use this guide for JavaScript best practices and guidance 21 | log: when documenting changes, use this guide for creating structured change logs with emoji categorization 22 | commit: when committing code, use this guide for conventional commit format with proper message structure 23 | autodux: when building Redux state management, use this guide for creating and transpiling Autodux dux objects 24 | javascript-io-network-effects: when you need to make network requests or invoke side-effects, use this guide for saga pattern implementation 25 | ui: when building user interfaces and user experiences, use this guide for beautiful and friendly UI/UX design 26 | requirements: when writing functional requirements for a user story, use this guide for functional requirement specification 27 | } 28 | 29 | const taskPrompt = "# Guides\n\nRead each of the following guides for important context, and follow their instructions carefully: ${list guide file refs in markdown format}\n\n# User Prompt\n\n${prompt}" 30 | 31 | withCLI() { 32 | `cursor-agent --agent ${agent} --prompt $taskPrompt` 33 | } 34 | 35 | directExecution() { 36 | prompt yourself with the $taskPrompt: 37 | } 38 | 39 | handleInitialRequest() { 40 | use taskCreator to create and execute a task plan 41 | match (contextRequirements = infer) { 42 | > 1 guide => use withCLI 43 | default => use directExecution 44 | } 45 | } -------------------------------------------------------------------------------- /ai/rules/productmanager.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: When planning features, user stories, user journeys, or conducting product discovery, use this guide for building specifications and user journey maps 3 | alwaysApply: false 4 | --- 5 | 6 | # ProductManager 7 | 8 | Act as a top-tier software product and project manager, well versed in continuous product discovery, user story mapping, user research, HCI, DevEx, and UX research and best practices. Your job is to help generate user journeys, user story maps, and individual stories to use in PRDs, interface contracts, documentation, user acceptance testing, and issue trackers. 9 | 10 | Each user story should target a specific pain point. Classifying the severity and frequency of the pain point will help prioritize the user story. 11 | 12 | type UserStory = "As a $persona, I want $jobToDo, so that $benefit" 13 | type FunctionalRequirement = "Given $situation, should $jobToDo" 14 | type id = string(cuid2) 15 | type timestamp = number(64 bit epoch) 16 | type statusState = backlog | inProgress | released | cancelled 17 | type meta = { 18 | id 19 | name 20 | description 21 | createdAt 22 | updatedAt 23 | } 24 | 25 | Status { 26 | state 27 | comment 28 | } 29 | 30 | Persona { 31 | ...meta 32 | } 33 | 34 | Mockup { 35 | ...meta 36 | imageURI 37 | } 38 | 39 | PainPoint { 40 | ...meta 41 | impact: 1..10 // how much this hurts when it happens 42 | frequency: 1..10 // how often this happens 43 | } 44 | 45 | UserStory { 46 | ...meta 47 | painPoint 48 | priority = painPoint ~> impact * frequency 49 | functionalRequirements 50 | mockups 51 | status 52 | } 53 | 54 | Step { 55 | ...meta 56 | userStories 57 | } 58 | 59 | UserJourney { 60 | ...meta 61 | personas 62 | steps 63 | } 64 | 65 | FeaturePRD { 66 | - name 67 | - problem description // why are we building this? 68 | - solution description // what are we building? 69 | - user journey guide // step by step prose description of user journey with mockups/prototype demos 70 | - requirements // explicitly list user stories and their corresponding functional requirements 71 | }:format=Markdown PRD 72 | 73 | StoryMap { 74 | userJourneys 75 | } 76 | 77 | Project { 78 | ...meta 79 | owner: UserId 80 | domain 81 | personas 82 | storyMap 83 | } 84 | 85 | Constraints { 86 | If the user issues a command for which you don't have a plan, walk the user through the discovery process to plan a user journey. 87 | } 88 | 89 | CrudOperations { 90 | account 91 | project // always has exactly one storyMap 92 | // storyMap does not need its own CRUD because it's part of the project 93 | persona 94 | painPoint 95 | mockup 96 | // PRD is derived on demand from other data 97 | journey 98 | step 99 | story 100 | } 101 | 102 | Interface { 103 | /research - Chat to discover the user research available to plan user journeys. Assistant will ask questions to spark user research or get user research answers required to design user journeys. 104 | /setup - Assistant will ask the user about the project metadata (name, description, domain, personas, etc.) 105 | /generate [persona|journey|storymaps|userStories|feature] - Suggest items for the list the user is trying to populate 106 | /feature - Plan a feature from a given user story - output PRD in markdown format 107 | /save - Export project and all associated state in YAML format 108 | /cancel [step] - Cancel a given story 109 | } -------------------------------------------------------------------------------- /source/test.js: -------------------------------------------------------------------------------- 1 | import tape from 'tape'; 2 | 3 | import { describe, Try, createStream, countKeys } from './riteway.js'; 4 | 5 | // a function to test 6 | const sum = (...args) => { 7 | if (args.some(v => Number.isNaN(v))) throw new TypeError('NaN'); 8 | return args.reduce((acc, n) => acc + n, 0); 9 | }; 10 | 11 | describe('sum()', async assert => { 12 | const should = 'return the correct sum'; 13 | 14 | assert({ 15 | given: 'no arguments', 16 | should: 'return 0', 17 | actual: sum(), 18 | expected: 0 19 | }); 20 | 21 | assert({ 22 | given: 'zero', 23 | should, 24 | actual: sum(2, 0), 25 | expected: 2 26 | }); 27 | 28 | assert({ 29 | given: 'negative numbers', 30 | should, 31 | actual: sum(1, -4), 32 | expected: -3 33 | }); 34 | 35 | assert({ 36 | given: 'NaN', 37 | should: 'throw', 38 | actual: Try(sum, 1, NaN), 39 | expected: new TypeError('NaN') 40 | }); 41 | }); 42 | 43 | describe('describe()', (assert, end) => { 44 | setTimeout(() => { 45 | assert({ 46 | given: 'TestFunction using end()', 47 | should: 'pass end()', 48 | actual: typeof end, 49 | expected: 'function' 50 | }); 51 | }, 20); 52 | 53 | setTimeout(() => { 54 | end(); 55 | }, 50); 56 | }); 57 | 58 | describe('createStream()', async assert => { 59 | assert({ 60 | given: 'typeof check', 61 | should: 'be a function', 62 | actual: typeof createStream, 63 | expected: 'function' 64 | }); 65 | }); 66 | 67 | describe('Try()', async assert => { 68 | { 69 | const error = new Error('ooops'); 70 | assert({ 71 | given: 'an async function that throws', 72 | should: 'await and return the value of the error', 73 | actual: (await Try(async () => { throw error; }, 'irrelivant')).toString(), 74 | expected: error.toString() 75 | }); 76 | } 77 | }); 78 | 79 | describe('assert()', async assert => { 80 | assert({ 81 | given: 'some key is undefined', 82 | should: 'not throw', 83 | actual: undefined, 84 | expected: undefined 85 | }); 86 | 87 | { 88 | try { 89 | assert({}); 90 | } catch (error) { 91 | assert({ 92 | given: 'calling `assert` with missing keys', 93 | should: 'throw with missing keys', 94 | actual: error.message, 95 | expected: 'The following parameters are required by `assert`: given, should, actual, expected', 96 | }); 97 | } 98 | } 99 | 100 | { 101 | try { 102 | assert({ given: 'some keys', should: 'find the missing keys' }); 103 | } catch (error) { 104 | assert({ 105 | given: 'calling `assert` with missing keys', 106 | should: 'throw with missing keys', 107 | actual: error.message, 108 | expected: 'The following parameters are required by `assert`: actual, expected', 109 | }); 110 | } 111 | } 112 | }); 113 | 114 | describe('skip()', async assert => { 115 | assert({ 116 | given: 'describe.skip', 117 | should: 'be equal to tape.skip', 118 | actual: describe.skip === tape.skip, 119 | expected: true 120 | }); 121 | }); 122 | 123 | 124 | describe('countKeys()', async assert => { 125 | assert({ 126 | given: 'an object', 127 | should: 'return the number of own props in the object', 128 | actual: countKeys({ a: 'a', b: 'b', c: 'c' }), 129 | expected: 3 130 | }); 131 | }); 132 | 133 | import './match-test.js'; 134 | import '../bin/riteway.test.js'; 135 | -------------------------------------------------------------------------------- /ai/rules/tdd.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: When implementing code changes, use this guide for systematic test-driven development with proper test isolation 3 | globs: **/*.js,**/*.jsx,**/*.ts,**/*.tsx 4 | alwaysApply: false 5 | --- 6 | # TDD Engineer 7 | 8 | Act as a top-tier software engineer with serious TDD discipline to systematically implement software using the TDD process. 9 | 10 | 11 | ## assert 12 | 13 | type assert = ({ given: string, should: string, actual: any, expected: any }) { 14 | `given` and `should` must clearly state the functional requirements from an acceptance perspective, and should avoid describing literal values. 15 | Tests must demonstrate locality: The test should not rely on external state or other tests. 16 | 17 | Ensure that the test answers these 5 questions { 18 | 1. What is the unit under test? (test should be in a named describe block) 19 | 2. What is the expected behavior? ($given and $should arguments are adequate) 20 | 3. What is the actual output? (the unit under test was exercised by the test) 21 | 4. What is the expected output? ($expected and/or $should are adequate) 22 | 5. How can we find the bug? (implicitly answered if the above questions are answered correctly) 23 | } 24 | 25 | Tests must be: 26 | - Readable - Answer the 5 questions. 27 | - Isolated/Integrated 28 | - Units under test should be isolated from each other 29 | - Tests should be isolated from each other with no shared mutable state. 30 | - For integration tests, test integration with the real system. 31 | - Thorough - Test expected/very likely edge cases 32 | - Explicit - Everything you need to know to understand the test should be part of the test itself. If you need to produce the same data structure many times for many test cases, create a factory function and invoke it from the individual tests, rather than sharing mutable fixtures between tests. 33 | } 34 | 35 | 36 | ## Process 37 | 38 | For each unit of code, create a test suite, one requirement at a time: 39 | 40 | 1. If the user has not specified a test framework or technology stack, ask them before implementing. 41 | 1. If the calling API is unspecified, propose a calling API that serves the functional requirements and creates an optimal developer experience. 42 | 1. Write a test. Run the test runner and watch the test fail. 43 | 1. Implement the code to make the test pass. Implement ONLY the code needed to make the test pass. 44 | 1. Run the test runner: fail => fix bug; pass => continue 45 | 1. Get approval from the user before moving on. 46 | 1. Repeat the TDD iteration process for the next functional requirement. 47 | 48 | ## Describe/Test Wrappers 49 | 50 | In most testing frameworks, there is a `describe` function and possibly a nested `test` or `it` wrapper. 51 | 52 | Use the string in the `describe` function to name the unit under test. 53 | 54 | Use the string in the `test` function to offer a brief category for the test, e.g. "new account creation". 55 | 56 | Because of conflicts with the `assert` function API and description, avoid the `it` wrapper entirely, if possible. 57 | 58 | 59 | Constraints { 60 | Carefully think through correct output. 61 | Avoid hallucination. 62 | This is very important to ensure software works as expected and that user safety is protected. Please do your best work. 63 | When testing app state logic, always use selectors to read from the state. NEVER read directly from state objects. 64 | Avoid writing tests for expected types/shapes. It would be redundant with type checks. 65 | } 66 | 67 | State { 68 | testFramework = Riteway Library + Vitest 69 | libraryStack // e.g. React + Redux + Redux Saga 70 | } 71 | 72 | /welcome -------------------------------------------------------------------------------- /ai/rules/javascript/javascript.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: When writing JavaScript or TypeScript code, use this guide for JavaScript best practices and guidance 3 | globs: **/*.js,**/*.jsx,**/*.ts,**/*.tsx 4 | alwaysApply: false 5 | --- 6 | 7 | # JavaScript/TypeScript guide 8 | 9 | Act as a top-tier software engineer with serious JavaScript/TypeScript discipline to carefully implement high quality software. 10 | 11 | ## Before Writing Code 12 | 13 | - Read the lint and formatting rules. 14 | - Observe the project's relevant existing code. 15 | - Conform to existing code style, patterns, and conventions unless directed otherwise. Note: these instructions count as "directed otherwise" unless the user explicitly overrides them. 16 | 17 | Constraints { 18 | Be concise. 19 | Favor functional programming; keep functions short, pure, and composable. 20 | Favor map, filter, reduce over manual loops. 21 | Prefer immutability; use const, spread, and rest operators instead of mutation. 22 | One job per function; separate mapping from IO. 23 | Obey the projects lint and formatting rules. 24 | Omit needless code and variables; prefer composition with partial application and point-free style. 25 | Chain operations rather than introducing intermediate variables, e.g. `[x].filter(p).map(f)` 26 | Avoid loose procedural sequences; compose clear pipelines instead. 27 | Avoid `class` and `extends` as much as possible. Prefer composition of functions and data structures over inheritance. 28 | Keep related code together; group by feature, not by technical type. 29 | Put statements and expressions in positive form. 30 | Use parallel code for parallel concepts. 31 | Avoid null/undefined arguments; use options objects instead. 32 | Use concise syntax: arrow functions, object destructuring, array destructuring, template literals. 33 | Avoid verbose property assignments. bad: `const a = obj.a;` good: `const { a } = obj;` 34 | Assign reasonable defaults directly in function signatures. 35 | `const createExpectedUser = ({ id = createId(), name = '', description = '' } = {}) => ({ id, name, description });` 36 | Principle: Function callers should be able to understand the expected call signature by reading the function signature. This means: 37 | Parameter values should be explicitly named and expressed in function signatures: 38 | Bad: `const createUser = (payload = {}) => ({` 39 | Good: `const createUser = ({ id = createId(), name = '', description = ''} = {}) =>` 40 | Notice how default values also provide hints for type inference. 41 | Avoid using || for defaults. Use parameter defaults instead. See above. 42 | Prefer async/await or asyncPipe over raw promise chains. 43 | Use strict equality (===). 44 | Modularize by feature; one concern per file or function; prefer named exports. 45 | } 46 | 47 | NamingConstraints { 48 | Use active voice. 49 | Use clear, consistent naming. 50 | Functions should be verbs. e.g. `increment()`, `filter()`. 51 | Predicates and booleans should read like yes/no questions. e.g. `isActive`, `hasPermission`. 52 | Prefer standalone verbs over noun.method. e.g. `createUser()` not `User.create()`. 53 | Avoid noun-heavy and redundant names. e.g. `filter(fn, array)` not `matchingItemsFromArray(fn, array)`. 54 | Avoid "doSomething" style. e.g. `notify()` not `Notifier.doNotification()`. 55 | Lifecycle methods: prefer `beforeX` / `afterX` over `willX` / `didX`. e.g. `beforeUpdate()`. 56 | Use strong negatives over weak ones: `isEmpty(thing)` not `!isDefined(thing)`. 57 | Mixins and function decorators use `with${Thing}`. e.g. `withUser`, `withFeatures`, `withAuth`. 58 | Avoid ALL_CAPS for constants. Since we use functional programming, there's no need for a hard distinction between constants and variables. 59 | } -------------------------------------------------------------------------------- /source/vitest.test.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { describe, test } from 'vitest'; 3 | import { Try, countKeys, createStream } from './riteway'; 4 | import { assert } from './vitest'; 5 | import match from './match'; 6 | import render from './render-component'; 7 | 8 | // a function to test 9 | const sum = (...args) => { 10 | if (args.some((v) => Number.isNaN(v))) throw new TypeError('NaN'); 11 | return args.reduce((acc, n) => acc + n, 0); 12 | }; 13 | 14 | describe('Vitest test suite', () => { 15 | const should = 'return the correct sum'; 16 | 17 | test('sum()', () => { 18 | assert({ 19 | given: 'no arguments', 20 | should: 'return 0', 21 | actual: sum(), 22 | expected: 0, 23 | }); 24 | 25 | assert({ 26 | given: 'zero', 27 | should, 28 | actual: sum(2, 0), 29 | expected: 2, 30 | }); 31 | 32 | assert({ 33 | given: 'negative numbers', 34 | should, 35 | actual: sum(1, -4), 36 | expected: -3, 37 | }); 38 | 39 | assert({ 40 | given: 'NaN', 41 | should: 'throw', 42 | actual: Try(sum, 1, NaN), 43 | expected: new TypeError('NaN'), 44 | }); 45 | }); 46 | 47 | test('createStream()', () => { 48 | assert({ 49 | given: 'typeof check', 50 | should: 'be a function', 51 | actual: typeof createStream, 52 | expected: 'function', 53 | }); 54 | }); 55 | 56 | test('Try()', async () => { 57 | { 58 | const error = new Error('ooops'); 59 | assert({ 60 | given: 'an async function that throws', 61 | should: 'await and return the value of the error', 62 | actual: ( 63 | await Try(async () => { 64 | throw error; 65 | }, 'irrelivant') 66 | ).toString(), 67 | expected: error.toString(), 68 | }); 69 | } 70 | }); 71 | 72 | test('assert()', () => { 73 | assert({ 74 | given: 'some key is undefined', 75 | should: 'not throw', 76 | actual: undefined, 77 | expected: undefined, 78 | }); 79 | 80 | { 81 | try { 82 | assert({}); 83 | } catch (error) { 84 | assert({ 85 | given: 'calling `assert` with missing keys', 86 | should: 'throw with missing keys', 87 | actual: error.message, 88 | expected: 89 | 'The following parameters are required by `assert`: given, should, actual, expected', 90 | }); 91 | } 92 | } 93 | 94 | { 95 | try { 96 | assert({ given: 'some keys', should: 'find the missing keys' }); 97 | } catch (error) { 98 | assert({ 99 | given: 'calling `assert` with missing keys', 100 | should: 'throw with missing keys', 101 | actual: error.message, 102 | expected: 103 | 'The following parameters are required by `assert`: actual, expected', 104 | }); 105 | } 106 | } 107 | }); 108 | 109 | test('countKeys()', () => { 110 | assert({ 111 | given: 'an object', 112 | should: 'return the number of own props in the object', 113 | actual: countKeys({ a: 'a', b: 'b', c: 'c' }), 114 | expected: 3, 115 | }); 116 | }); 117 | }); 118 | 119 | describe('Vitest component test', () => { 120 | // @ts-ignore 121 | // eslint-disable-next-line 122 | const MyComponent = ({ text }) =>
{text}
; 123 | 124 | test('renderComponent', async () => { 125 | const text = 'Test for whatever you like!'; 126 | const $ = render(); 127 | const contains = match($('.contents').html()); 128 | 129 | assert({ 130 | given: 'A react component', 131 | should: 'return a working cheerio instance', 132 | actual: contains(text), 133 | expected: text, 134 | }); 135 | }); 136 | }); 137 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "riteway", 3 | "version": "9.0.0", 4 | "description": "Unit tests that always supply a good bug report when they fail.", 5 | "type": "module", 6 | "main": "source/riteway.js", 7 | "module": "source/riteway.js", 8 | "types": "./index.d.ts", 9 | "exports": { 10 | ".": { 11 | "import": "./source/riteway.js", 12 | "types": "./index.d.ts" 13 | }, 14 | "./vitest": { 15 | "import": "./source/vitest.js", 16 | "types": "./vitest.d.ts" 17 | }, 18 | "./match": { 19 | "import": "./source/match.js", 20 | "types": "./match.d.ts" 21 | }, 22 | "./render": { 23 | "import": "./source/render-component.js", 24 | "types": "./render-component.d.ts" 25 | }, 26 | "./render-component": { 27 | "import": "./source/render-component.js", 28 | "types": "./render-component.d.ts" 29 | }, 30 | "./esm/riteway.js": { 31 | "import": "./source/riteway.js", 32 | "types": "./index.d.ts" 33 | }, 34 | "./esm/index.js": { 35 | "import": "./source/riteway.js", 36 | "types": "./index.d.ts" 37 | }, 38 | "./esm/match.js": { 39 | "import": "./source/match.js", 40 | "types": "./match.d.ts" 41 | }, 42 | "./esm/render.js": { 43 | "import": "./source/render-component.js", 44 | "types": "./render-component.d.ts" 45 | }, 46 | "./esm/render-component.js": { 47 | "import": "./source/render-component.js", 48 | "types": "./render-component.d.ts" 49 | }, 50 | "./esm/vitest.js": { 51 | "import": "./source/vitest.js", 52 | "types": "./vitest.d.ts" 53 | }, 54 | "./esm/test.js": { 55 | "import": "./source/test.js" 56 | }, 57 | "./esm/match-test.js": { 58 | "import": "./source/match-test.js" 59 | }, 60 | "./esm/vitest.test.jsx": { 61 | "import": "./source/vitest.test.jsx" 62 | } 63 | }, 64 | "bin": { 65 | "riteway": "bin/riteway" 66 | }, 67 | "scripts": { 68 | "lint": "eslint source && echo 'Lint complete.'", 69 | "lint-fix": "eslint --fix source && eslint --fix ./*.js", 70 | "typecheck": "npx -p typescript tsc --esModuleInterop --rootDir . source/test.js --allowJs --checkJs --noEmit --lib es6 --jsx react && npx -p typescript tsc index.d.ts --noEmit && echo 'TypeScript check complete.'", 71 | "ts": "npm run -s typecheck", 72 | "test": "node source/test.js && vitest run", 73 | "esm": "cp source/*.js esm/ && cp source/*.jsx esm/ && cp esm/riteway.js esm/index.js && echo 'esm complete.'", 74 | "watch": "watch 'clear && npm run -s test | tap-nirvana && npm run -s lint && npm run -s typecheck && npm run -s esm' source", 75 | "precommit": "npm run -s test && npm run -s lint-fix && npm run -s typecheck", 76 | "update": "updtr", 77 | "release": "node release.js" 78 | }, 79 | "repository": { 80 | "type": "git", 81 | "url": "git+https://github.com/ericelliott/riteway.git" 82 | }, 83 | "author": "Eric Elliott", 84 | "license": "MIT", 85 | "bugs": { 86 | "url": "https://github.com/ericelliott/riteway/issues" 87 | }, 88 | "homepage": "https://github.com/ericelliott/riteway#readme", 89 | "devDependencies": { 90 | "@types/cheerio": "^1.0.0", 91 | "@types/node": "^24.7.0", 92 | "@types/react": "^19.2.2", 93 | "error-causes": "^3.0.2", 94 | "eslint": "^9.37.0", 95 | "eslint-plugin-react": "^7.37.5", 96 | "react": "^19.2.0", 97 | "release-it": "^19.0.5", 98 | "tap-nirvana": "1.1.0", 99 | "typescript": "^5.9.3", 100 | "updtr": "4.1.0", 101 | "watch": "^1.0.2" 102 | }, 103 | "dependencies": { 104 | "cheerio": "1.1.2", 105 | "dotignore": "^0.1.2", 106 | "esm": "3.2.25", 107 | "glob": "^11.0.3", 108 | "minimist": "^1.2.8", 109 | "react-dom": "^19.2.0", 110 | "resolve": "^1.22.10", 111 | "tape": "^5.9.0", 112 | "vitest": "^3.2.4" 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /tasks/2025-01-27-context7-github-action-epic.md: -------------------------------------------------------------------------------- 1 | # Context7 GitHub Action Integration Epic 2 | 3 | **Status**: 📋 PLANNING 4 | **Created**: 2025-01-27 5 | **Goal**: Add Upsert Context7 GitHub Action to repository with automatic updates after successful release script runs 6 | 7 | ## Epic Overview 8 | 9 | Integrate the Upsert Context7 GitHub Action into the Riteway repository to provide up-to-date code documentation for LLMs and AI code editors, with automatic updates triggered after successful release script runs. 10 | 11 | ## Task Breakdown 12 | 13 | ### Task 1: Research Context7 Upsert Action 14 | **Status**: 📋 PENDING 15 | **Context**: Need to understand Context7 Upsert action specifications, configuration options, and requirements 16 | **Requirements**: 17 | - Given the need to integrate Context7, should research action specifications and best practices 18 | - Given the repository structure, should understand how Context7 integrates with existing workflows 19 | 20 | **Success Criteria**: 21 | - [ ] Context7 Upsert action specifications documented 22 | - [ ] Configuration options and requirements identified 23 | - [ ] Integration approach with existing release.js workflow planned 24 | 25 | **Dependencies**: None 26 | **Estimated Effort**: Small 27 | **Agent Orchestration**: Required (web search for research) 28 | 29 | ### Task 2: Set Up GitHub Actions Infrastructure 30 | **Status**: 📋 PENDING 31 | **Context**: Repository currently has no GitHub Actions workflows - need to create the infrastructure 32 | **Requirements**: 33 | - Given a repository without GitHub Actions, should create .github/workflows directory structure 34 | - Given the need for CI/CD, should establish workflow foundation 35 | 36 | **Success Criteria**: 37 | - [ ] .github/workflows/ directory created 38 | - [ ] Basic workflow structure established 39 | - [ ] GitHub Actions permissions and settings configured 40 | 41 | **Dependencies**: Task 1 completion 42 | **Estimated Effort**: Small 43 | **Agent Orchestration**: Not Required 44 | 45 | ### Task 3: Integrate Upsert Context7 Action 46 | **Status**: 📋 PENDING 47 | **Context**: Add the actual Context7 Upsert action to the workflow 48 | **Requirements**: 49 | - Given the Context7 specifications, should implement the action in workflow 50 | - Given the repository structure, should ensure proper configuration 51 | 52 | **Success Criteria**: 53 | - [ ] Upsert Context7 action added to workflow 54 | - [ ] Action properly configured with repository settings 55 | - [ ] Action tested and validated 56 | 57 | **Dependencies**: Tasks 1 and 2 completion 58 | **Estimated Effort**: Medium 59 | **Agent Orchestration**: Not Required 60 | 61 | ### Task 4: Configure Automatic Release Integration 62 | **Status**: 📋 PENDING 63 | **Context**: Integrate Context7 updates with existing release.js workflow 64 | **Requirements**: 65 | - Given a successful release script run, should automatically trigger Context7 updates 66 | - Given the existing release.js workflow, should integrate seamlessly without breaking functionality 67 | 68 | **Success Criteria**: 69 | - [ ] Release workflow triggers Context7 updates 70 | - [ ] Integration doesn't interfere with existing release process 71 | - [ ] Automatic updates working correctly 72 | 73 | **Dependencies**: Task 3 completion 74 | **Estimated Effort**: Medium 75 | **Agent Orchestration**: Required (JavaScript/Node.js expertise for release.js integration) 76 | 77 | ### Task 5: Testing and Validation 78 | **Status**: 📋 PENDING 79 | **Context**: Ensure complete workflow integration works correctly 80 | **Requirements**: 81 | - Given the complete workflow, should validate all components work together 82 | - Given the integration, should ensure no regressions in existing functionality 83 | 84 | **Success Criteria**: 85 | - [ ] Complete workflow tested end-to-end 86 | - [ ] Release process validated with Context7 integration 87 | - [ ] No regressions in existing functionality 88 | - [ ] Documentation updated 89 | 90 | **Dependencies**: Task 4 completion 91 | **Estimated Effort**: Medium 92 | **Agent Orchestration**: Required (systematic testing approach) 93 | 94 | ## Implementation Notes 95 | 96 | - Must ensure workflow doesn't interfere with existing release process 97 | - Should follow GitHub Actions best practices 98 | - Consider security implications of automatic updates 99 | - Need to research exact Context7 Upsert action configuration 100 | - Should integrate seamlessly with existing release.js script 101 | 102 | ## Questions for Clarification 103 | 104 | 1. Do you have specific requirements for the Context7 Upsert action configuration? 105 | 2. Should the Context7 updates happen immediately after release or on a schedule? 106 | 3. Are there any specific GitHub repository settings or permissions I should be aware of? 107 | 108 | ## Success Metrics 109 | 110 | - Context7 Upsert action successfully integrated 111 | - Automatic updates working after releases 112 | - No disruption to existing release workflow 113 | - Complete end-to-end testing validated 114 | -------------------------------------------------------------------------------- /ai/rules/frameworks/redux/autodux.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: When building Redux state management, use this guide for creating and transpiling Autodux dux objects 3 | alwaysApply: false 4 | --- 5 | # Autodux 6 | 7 | Act as a senior JavaScript, React, Redux, Next.js engineer. Your job is to build redux state handling for the application. 8 | 9 | help() { 10 | Explain how to use Autodux: 11 | - How to define a dux object. List properties in the expected format, wrapped in a code block. Set the codeblock language to sudolang. 12 | - Briefly explain the transpile command. 13 | List available commands. 14 | } 15 | 16 | welcome():length=1 line 17 | 18 | transpile() { 19 | Constraints { 20 | Concise 21 | Readable 22 | Functional 23 | Use arrow functions 24 | Use implicit returns when possible 25 | Supply all of the files listed in the files property in separate JavaScript code blocks. 26 | } 27 | } 28 | 29 | ActionObject { 30 | type: "$slice/$actionName" 31 | payload: Any 32 | } 33 | 34 | ActionCreator { 35 | (payload = {}) => ActionObject 36 | Constraints { 37 | For ids, timestamps, or other non-deterministic values, generate the default value in the parameter position, not in the function body. 38 | Always use arrow functions and avoid the `return` keyword. 39 | Always default the payload to an empty object. 40 | Always use the ActionObject type and type template. 41 | Define action types inline. Do not use constants. 42 | } 43 | } 44 | 45 | withSlice(slice) => reducer => wrappedReducer 46 | 47 | Selector { 48 | wholeState => selectedState 49 | Constraints { 50 | Select using the `slice` variable, e.g. `state[slice].*` 51 | when testing, use the withSlice to wrap the returned reducer so that selectors work correctly. 52 | } 53 | } 54 | 55 | reducer { 56 | (state = initialState, { type, payload } = {}) => state 57 | Constraints { 58 | Use `actionCreator().type` instead of literal string values to build the cases. 59 | } 60 | } 61 | 62 | mapStateToProps(); 63 | 64 | mapDispatchToProps() { 65 | Use the object literal form instead of the function form. 66 | } 67 | 68 | 5 Questions { 69 | What is the component? 70 | What is the natural language requirement? 71 | What are the actual results? 72 | What are the expected results? 73 | On fail, how can we find and fix the bug? 74 | } 75 | 76 | RITE Way { 77 | Readable 78 | Isolated 79 | Thorough 80 | Explicit 81 | } 82 | 83 | Test { 84 | 5 Questions 85 | RITE way 86 | Always use selectors to read from the resulting state to avoid state shape dependencies in unit tests. Use Riteway for JavaScript. 87 | Always set up initial state by calling the reducer with action creators. Reduce over an array of actions if multiple steps are required. 88 | Treat action creators and selectors as the public API for the reducer. Don't test them in isolation from the reducer. 89 | !Keep test cases isolated in their own block scopes. 90 | !Avoid shared state and setup between test cases. 91 | } 92 | 93 | Requirements = "Given $situation, should $action" 94 | Test Case = $Requirements 95 | 96 | testCases() { 97 | Express the user story in natural language. Convert action creator names into the natural language equivalent and describe their effect on the state. 98 | Without comment. Output ONLY the return value: 99 | return generateRequirements() |> update(Dux) |> /save 100 | } 101 | 102 | transpile() { 103 | Dux |> transpile(JavaScript) 104 | } 105 | 106 | Dux { 107 | initialState 108 | slice 109 | actions 110 | selectors 111 | requirements = infer() 112 | testCases = infer() 113 | mapStateToProps = infer() 114 | mapDispatchToProps = infer() 115 | connectedComponentName = infer() 116 | tools = [{createId} from @paralleldrive/cuid2] 117 | files = { 118 | dux = infer() // the file for reducer, actions, selectors 119 | store = infer() // build the root reducer and create the store for the app 120 | container = infer() // the connected container component 121 | component = infer() // the presentation component 122 | test = infer() 123 | } 124 | } 125 | 126 | Autodux { 127 | State: Dux 128 | Constraints { 129 | Never offer disclaimers such as "As an AI language model...". Just do your best to infer the user's intention and emulate any on-topic software development-related job they ask for. 130 | Don't use Redux Toolkit or any other Redux-specific helper libraries. 131 | Important: Name files after the slice, convert to all-lowercase, kebab-case with -component -dux -container extensions. All filenames should end with ".js". 132 | Use comments to clarify multi-step state updates. 133 | Dux and this prompt are SudoLang. It is AI-inferred, so be concise, e.g. uninitialized arrays don't need brackets because we can infer type from plural names. 134 | Ignore the example usage, and use it only if the user asks for /help 135 | } 136 | /help - Explain how to use Autodux and list commands 137 | /example - Example SudoLang source code in ./example.sudo 138 | /save - Return the Dux in SudoLang format. 139 | /test cases - List the test cases in SudoLang format: TestCases [ ... ] 140 | /add [prop] [value] to the Dux object 141 | /transpile 142 | } 143 | 144 | welcome("Welcome to Autodux. Supply a Dux object to get started. Feel free to ask for `/help`") -------------------------------------------------------------------------------- /ai/rules/task-creator.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: when the user asks you to complete a task, use this guide for systematic task/epic planning and execution 3 | alwaysApply: false 4 | --- 5 | # Task Creator Metaprogram 6 | 7 | Act as a top-tier software project manager and systematic task planner and execution coordinator. Your job is to break down complex requests into manageable, sequential tasks that can be executed one at a time with user approval. 8 | 9 | A task can be broken down into smaller tasks. The larger task is stored in a task file in the $projectRoot/tasks folder. Subtasks live in that file. 10 | 11 | ## Context Gathering 12 | 13 | Before beginning any task, gather/infer context. When in doubt, ask clarifying questions: 14 | 15 | TaskStatus = pending | inProgress | completed | blocked | cancelled 16 | 17 | State { 18 | TaskName // The specific task being planned 19 | Status // Current execution state 20 | CodeContext // All relevant files, functions, and components that need to be examined or modified 21 | StyleGuides // Coding standards, patterns, and conventions that apply to this work 22 | Dependencies // External libraries, APIs, or system integrations required 23 | Constraints // Technical limitations, performance requirements, or business rules 24 | SuccessCriteria // Clear, measurable outcomes for the completed work 25 | AgentRequirements // Assessment if task requires specialized agent expertise 26 | } 27 | 28 | 29 | ## Requirements Analysis 30 | 31 | Use @requirements.mdc to analyze and generate the requirements of the task. 32 | 33 | ## Agent Orchestration 34 | 35 | For complex tasks that require specialized expertise, systematically employ the agent orchestrator pattern in @agent-orchestrator.mdc 36 | 37 | assessComplexity() { 38 | criteria: 39 | Multiple technical domains (UI, backend, testing, etc.) 40 | Specialized knowledge (Redux, TDD, product management, etc.) 41 | Cross-functional coordination 42 | Integration with existing agent workflows 43 | } 44 | 45 | ## Task Planning 46 | 47 | planTask() { 48 | 1. Decompose - Break the user's request into atomic, sequential tasks 49 | 2. Assess Agent Needs - For each task, determine if agent orchestration is required 50 | 3. Order tasks by dependencies and logical flow 51 | 4. Validate Ensure each task is specific, actionable, independently testable, small enough to complete in one focused session, clear about inputs, outputs, and success criteria 52 | 5. Sequence - Arrange tasks so each builds on the previous one 53 | 6. Checkpoint Plan approval gates between major phases 54 | } 55 | 56 | ## Task Execution Protocol 57 | 58 | createPlan() { 59 | 1. Think = "🎯 restate |>💡 ideate |> 🪞 reflectCritically |> 🔭 expandOrthogonally |> ⚖️ scoreRankEvaluate |> 💬 respond" 60 | 2. Gather any additional context or clarification needed 61 | 3. Present the task/epic plan to the user for approval 62 | 4. Add the plan to the project root .plan.md file, with a reference to the epic plan file 63 | } 64 | 65 | executePlan() { 66 | 1. Complete only the current task 67 | 2. Validate - Verify the task meets its success criteria 68 | 3. Report - Summarize what was accomplished 69 | 4. Await Approval - Get explicit user approval before proceeding to the next task 70 | } 71 | 72 | ## Task Plan Template Structure 73 | 74 | Each task should include: 75 | 76 | """ 77 | ## [Task] $taskName 78 | 79 | $taskDescription 80 | 81 | **Context**: What files, patterns, or systems this task affects 82 | **Requirements**: 83 | - Given [situation], should [jobToDo] 84 | - Given [situation], should [jobToDo] 85 | 86 | **Success Criteria**: 87 | - [ ] Specific, measurable outcome 1 88 | - [ ] Specific, measurable outcome 2 89 | 90 | **Dependencies**: What must be completed before this task 91 | **Estimated Effort**: [Small/Medium/Large] on exponential scale 92 | 93 | **Agent Orchestration**: [Required/Not Required] 94 | - If Required: Specify which agent(s) and why 95 | - Agent dispatch command if applicable 96 | 97 | **Implementation Notes**: 98 | - Key technical considerations 99 | - Potential challenges or risks 100 | - Suggested approach or patterns to follow 101 | - Agent-specific guidance if applicable 102 | """ 103 | 104 | ## Completed Epic Documentation 105 | 106 | When an epic is completed, move it to tasks/archive/YYYY-MM-DD-${epicName}.md and update plan.md with: 107 | 108 | completedEpicTemplate() { 109 | """ 110 | ### ✅ ${epicName} 111 | 112 | **Status**: ✅ COMPLETED (${completionDate}) 113 | **File**: [`tasks/archive/${epicFileName}`](./tasks/archive/${epicFileName}) 114 | **Goal**: ${originalEpicGoal} 115 | **Result**: ${keyAccomplishmentsAndMetrics} 116 | """ 117 | } 118 | 119 | Constraints { 120 | Never attempt multiple tasks simultaneously 121 | Always get explicit user approval before moving to the next task 122 | If a task reveals new information that changes the plan, pause and re-plan 123 | Each task should be completable in ~50 lines of code or less 124 | Tasks should be independent - completing one shouldn't break others 125 | Always validate task completion before proceeding 126 | If blocked or uncertain, ask clarifying questions rather than making assumptions 127 | For complex tasks requiring agent orchestration, ensure proper agent dispatch before execution 128 | Maintain clear separation between task planning and agent execution phases 129 | } 130 | 131 | createTask() { 132 | createPlan |> awaitAproval |> executePlan 133 | } 134 | 135 | Commands { 136 | /help 137 | /task - create a task/epic 138 | /list [(tasks|epics) = tasks]- list all tasks in the epic 139 | } 140 | -------------------------------------------------------------------------------- /release.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { execSync } from "child_process"; 4 | import process from "process"; 5 | import { errorCauses, createError } from "error-causes"; 6 | 7 | // Configuration objects (camelCase per javascript.mdc) 8 | const semverTypes = ["major", "minor", "patch"]; 9 | 10 | const bumpAliases = { 11 | breaking: "major", 12 | feature: "minor", 13 | fix: "patch", 14 | major: "major", 15 | minor: "minor", 16 | patch: "patch", 17 | }; 18 | 19 | const defaultBump = "minor"; 20 | 21 | const allowedBranches = ["main", "master", "release"]; 22 | 23 | const config = { 24 | validBumpTypes: Object.keys(bumpAliases), 25 | }; 26 | 27 | // Error causes definition using error-causes library 28 | const [releaseErrors, handleReleaseErrors] = errorCauses({ 29 | ValidationError: { 30 | code: "VALIDATION_ERROR", 31 | message: "Input validation failed", 32 | }, 33 | GitError: { 34 | code: "GIT_ERROR", 35 | message: "Git operation failed", 36 | }, 37 | ReleaseItError: { 38 | code: "RELEASE_IT_ERROR", 39 | message: "release-it command failed", 40 | }, 41 | }); 42 | 43 | const { ValidationError, GitError, ReleaseItError } = releaseErrors; 44 | 45 | // Pure utility functions (explicit parameter defaults per javascript.mdc) 46 | const parseBumpType = ({ 47 | argv = process.argv, 48 | defaultType = defaultBump, 49 | } = {}) => argv.slice(2)[0] || defaultType; 50 | 51 | const validateBumpType = (bumpType) => { 52 | if (!bumpAliases[bumpType]) { 53 | throw createError({ 54 | ...ValidationError, 55 | message: `Invalid bump type: ${bumpType}. Valid options: ${config.validBumpTypes.join(", ")}`, 56 | }); 57 | } 58 | return bumpAliases[bumpType]; 59 | }; 60 | 61 | const getCurrentBranch = () => { 62 | try { 63 | const branch = execSync("git branch --show-current", { 64 | encoding: "utf8", 65 | }).trim(); 66 | return branch; 67 | } catch (originalError) { 68 | throw createError({ 69 | ...GitError, 70 | message: "Failed to get current git branch", 71 | cause: originalError, 72 | }); 73 | } 74 | }; 75 | 76 | const validateBranch = (branch) => { 77 | const allowed = allowedBranches.includes(branch); 78 | if (!allowed) { 79 | throw createError({ 80 | ...ValidationError, 81 | message: `Not on allowed branch. Current: ${branch}, Allowed: ${allowedBranches.join(", ")}`, 82 | }); 83 | } 84 | return { branch, valid: true }; 85 | }; 86 | 87 | const runReleaseIt = (semverType) => { 88 | try { 89 | console.log(`🚀 Starting release with release-it (${semverType})...`); 90 | execSync(`npx release-it ${semverType} --ci`, { 91 | stdio: "inherit", 92 | env: { ...process.env }, 93 | }); 94 | console.log("🎉 Release completed successfully!"); 95 | } catch (originalError) { 96 | throw createError({ 97 | ...ReleaseItError, 98 | message: "release-it command failed", 99 | cause: originalError, 100 | }); 101 | } 102 | }; 103 | 104 | // Use error-causes handleErrors pattern 105 | const handleError = handleReleaseErrors({ 106 | ValidationError: ({ name, code, message, cause }) => { 107 | console.error(`❌ Validation failed: ${message}`); 108 | console.error("💡 Fix the issue and try again."); 109 | if (cause) console.error(`🔍 Root cause: ${cause.message || cause}`); 110 | process.exit(1); 111 | }, 112 | GitError: ({ name, code, message, cause }) => { 113 | console.error(`❌ Git command failed: ${message}`); 114 | console.error("💡 Check your git configuration and network connection."); 115 | if (cause?.command) console.error(`📝 Failed command: ${cause.command}`); 116 | if (cause?.message) console.error(`🔍 Root cause: ${cause.message}`); 117 | process.exit(1); 118 | }, 119 | ReleaseItError: ({ name, code, message, cause }) => { 120 | console.error(`❌ release-it failed: ${message}`); 121 | console.error("💡 Check the release-it output above for details."); 122 | if (cause?.message) console.error(`🔍 Root cause: ${cause.message}`); 123 | process.exit(1); 124 | }, 125 | }); 126 | 127 | // Simplified release flow using release-it 128 | const createRelease = ({ argv, defaultType }) => { 129 | const bumpType = parseBumpType({ argv, defaultType }); 130 | const semverType = validateBumpType(bumpType); 131 | 132 | // Basic validation pipeline 133 | const currentBranch = getCurrentBranch(); 134 | validateBranch(currentBranch); 135 | 136 | console.log( 137 | `🎯 Preparing ${bumpType} (${semverType}) release on branch ${currentBranch}...`, 138 | ); 139 | 140 | // Use release-it to handle the complete release workflow 141 | runReleaseIt(semverType); 142 | }; 143 | 144 | // Main function - now concise with explicit parameter defaults 145 | function main({ argv = process.argv, defaultType = defaultBump } = {}) { 146 | try { 147 | createRelease({ argv, defaultType }); 148 | } catch (error) { 149 | // Enhanced error handling with specific error types 150 | handleError(error); 151 | } 152 | } 153 | 154 | // Show usage if --help is provided 155 | if (process.argv.includes("--help") || process.argv.includes("-h")) { 156 | console.log(` 157 | 🚀 Release Script (powered by release-it) 158 | 159 | Usage: npm run release [] 160 | 161 | Bump types: 162 | major, breaking - Breaking changes (1.0.0 -> 2.0.0) 163 | minor, feature - New features (1.0.0 -> 1.1.0) [default] 164 | patch, fix - Bug fixes (1.0.0 -> 1.0.1) 165 | 166 | Examples: 167 | npm run release # minor bump (default) 168 | npm run release breaking # major bump 169 | npm run release feature # minor bump 170 | npm run release fix # patch bump 171 | 172 | This script uses release-it to: 173 | 1. Run tests before release 174 | 2. Bump the version in package.json 175 | 3. Commit the version change 176 | 4. Create a git tag (v*.*.*) 177 | 5. Push commits and tags to origin 178 | 6. Create GitHub release with auto-generated changelog 179 | 7. Publish to npm registry 180 | `); 181 | process.exit(0); 182 | } 183 | 184 | main(); 185 | -------------------------------------------------------------------------------- /bin/riteway.test.js: -------------------------------------------------------------------------------- 1 | import { describe } from '../source/riteway.js'; 2 | import { execSync } from 'child_process'; 3 | 4 | // Import the functions we need to test 5 | import { 6 | parseArgs, 7 | loadModules, 8 | createIgnoreMatcher, 9 | resolveTestFiles, 10 | runTests 11 | } from './riteway'; 12 | 13 | // Test utilities 14 | const testSubprocessExit = (command, { cwd = process.cwd() } = {}) => { 15 | try { 16 | execSync(command, { cwd, stdio: 'pipe' }); 17 | return { exitCode: 0, stderr: '' }; 18 | } catch (e) { 19 | return { 20 | exitCode: e.status, 21 | stderr: e.stderr?.toString() || '' 22 | }; 23 | } 24 | }; 25 | 26 | describe('parseArgs()', async assert => { 27 | assert({ 28 | given: 'command line arguments with test patterns', 29 | should: 'parse patterns for test file execution', 30 | actual: parseArgs(['test/*.js', 'src/**/*.test.js']), 31 | expected: { 32 | patterns: ['test/*.js', 'src/**/*.test.js'], 33 | require: [], 34 | ignore: null, 35 | cwd: process.cwd() 36 | } 37 | }); 38 | 39 | assert({ 40 | given: 'command line arguments with -r flag', 41 | should: 'parse required modules for pre-loading', 42 | actual: parseArgs(['-r', 'babel-register', 'test/*.js']), 43 | expected: { 44 | patterns: ['test/*.js'], 45 | require: ['babel-register'], 46 | ignore: null, 47 | cwd: process.cwd() 48 | } 49 | }); 50 | 51 | assert({ 52 | given: 'command line arguments with multiple -r flags', 53 | should: 'parse all required modules as array', 54 | actual: parseArgs(['-r', 'module1', '-r', 'module2', 'test/*.js']), 55 | expected: { 56 | patterns: ['test/*.js'], 57 | require: ['module1', 'module2'], 58 | ignore: null, 59 | cwd: process.cwd() 60 | } 61 | }); 62 | 63 | assert({ 64 | given: 'command line arguments with -i ignore flag', 65 | should: 'parse ignore file path', 66 | actual: parseArgs(['-i', '.testignore', 'test/*.js']), 67 | expected: { 68 | patterns: ['test/*.js'], 69 | require: [], 70 | ignore: '.testignore', 71 | cwd: process.cwd() 72 | } 73 | }); 74 | 75 | assert({ 76 | given: 'empty command line arguments', 77 | should: 'return default configuration', 78 | actual: parseArgs([]), 79 | expected: { 80 | patterns: [], 81 | require: [], 82 | ignore: null, 83 | cwd: process.cwd() 84 | } 85 | }); 86 | }); 87 | 88 | describe('createIgnoreMatcher()', async assert => { 89 | assert({ 90 | given: 'options without ignore file', 91 | should: 'return null matcher', 92 | actual: createIgnoreMatcher({ cwd: process.cwd() }), 93 | expected: { cwd: process.cwd(), matcher: null } 94 | }); 95 | 96 | { 97 | // Test in subprocess since createIgnoreMatcher calls process.exit() 98 | const { exitCode, stderr } = testSubprocessExit( 99 | 'node -e "import(\'./bin/riteway\').then(m => m.createIgnoreMatcher({ ignore: \'nonexistent.ignore\', cwd: process.cwd() }))"' 100 | ); 101 | 102 | assert({ 103 | given: 'options with ignore file that does not exist', 104 | should: 'exit with error code 2', 105 | actual: exitCode, 106 | expected: 2 107 | }); 108 | 109 | assert({ 110 | given: 'options with ignore file that does not exist', 111 | should: 'output error message to stderr', 112 | actual: stderr.includes('ENOENT') && stderr.includes('nonexistent.ignore'), 113 | expected: true 114 | }); 115 | } 116 | }); 117 | 118 | describe('resolveTestFiles()', async assert => { 119 | assert({ 120 | given: 'patterns with no matcher', 121 | should: 'resolve all matching files', 122 | actual: resolveTestFiles({ 123 | patterns: ['source/test.js'], 124 | matcher: null, 125 | cwd: process.cwd() 126 | }).files.length > 0, 127 | expected: true 128 | }); 129 | 130 | assert({ 131 | given: 'empty patterns array', 132 | should: 'return empty files array', 133 | actual: resolveTestFiles({ 134 | patterns: [], 135 | matcher: null, 136 | cwd: process.cwd() 137 | }), 138 | expected: { files: [] } 139 | }); 140 | }); 141 | 142 | // FUNCTIONAL REQUIREMENT TESTS - Based on Epic Requirements 143 | 144 | describe('Functional Requirement: ES module environment support', async assert => { 145 | assert({ 146 | given: 'a project using ES modules ("type": "module")', 147 | should: 'run core Riteway tests without requiring Babel', 148 | actual: typeof parseArgs === 'function' && typeof resolveTestFiles === 'function', 149 | expected: true 150 | }); 151 | }); 152 | 153 | describe('Functional Requirement: CLI execution in ES environment', async assert => { 154 | assert({ 155 | given: 'the Riteway CLI command with test patterns', 156 | should: 'execute test files in ES module environment', 157 | actual: resolveTestFiles({ 158 | patterns: ['source/test.js'], 159 | matcher: null, 160 | cwd: process.cwd() 161 | }).files.some(file => file.endsWith('source/test.js')), 162 | expected: true 163 | }); 164 | }); 165 | 166 | describe('Functional Requirement: Backward compatibility', async assert => { 167 | assert({ 168 | given: 'existing test patterns and APIs', 169 | should: 'maintain backward compatibility with -r and -i flags', 170 | actual: parseArgs(['-r', 'module', '-i', '.ignore', 'test/*.js']), 171 | expected: { 172 | patterns: ['test/*.js'], 173 | require: ['module'], 174 | ignore: '.ignore', 175 | cwd: process.cwd() 176 | } 177 | }); 178 | }); 179 | 180 | describe('Functional Requirement: ES module imports', async assert => { 181 | assert({ 182 | given: 'ES module imports', 183 | should: 'resolve modules with proper extensions and imports', 184 | actual: typeof resolveTestFiles === 'function', 185 | expected: true 186 | }); 187 | }); 188 | 189 | describe('loadModules()', async assert => { 190 | assert({ 191 | given: 'options with empty require array', 192 | should: 'return options unchanged', 193 | actual: await loadModules({ require: [], cwd: process.cwd(), patterns: [] }), 194 | expected: { require: [], cwd: process.cwd(), patterns: [] } 195 | }); 196 | }); 197 | 198 | describe('runTests()', async assert => { 199 | assert({ 200 | given: 'empty files array', 201 | should: 'complete without error', 202 | actual: await runTests({ files: [] }), 203 | expected: undefined 204 | }); 205 | }); 206 | -------------------------------------------------------------------------------- /tasks/archive/2025-09-27-release-script-improvement-epic.md: -------------------------------------------------------------------------------- 1 | # Release Script Improvement Epic 2 | 3 | **Epic Goal**: Enhance Riteway's automated release system by implementing a robust release script with improved error handling, validation, and user experience based on the sudolang.ai reference implementation. 4 | 5 | **Status**: ✅ COMPLETED 6 | **Created**: 2025-09-27 7 | **Updated**: 2025-09-27 8 | **Completed**: 2025-09-27 9 | **Estimated Effort**: Medium (5 tasks) 10 | 11 | ## Context Analysis 12 | 13 | **Current State**: 14 | - ✅ Basic release-it setup with simple `"release": "release-it"` script 15 | - ✅ Version: 9.0.0-rc.1 (release candidate) 16 | - ✅ Uses release-it ^19.0.4 17 | - ✅ Epic feature branch: release 18 | - ⚠️ **PARTIAL IMPLEMENTATION DETECTED**: Some files already created during initial exploration 19 | 20 | **Reference Implementation** (sudolang.ai): 21 | - Enhanced release.js script with error-causes library 22 | - Structured error handling with specific error types 23 | - User-friendly CLI with help system 24 | - Validation for branches and bump types 25 | - Comprehensive .release-it.json configuration 26 | 27 | **Target State**: 28 | - Robust release script with proper error handling 29 | - Support for riteway's "release" branch workflow 30 | - Clear user guidance and validation 31 | - Maintained compatibility with existing release-it setup 32 | 33 | ## Task Breakdown 34 | 35 | ### [Task 1] Add Error-Causes Dependency ✅ COMPLETED 36 | **Context**: Package.json devDependencies 37 | **Requirements**: 38 | - Given the need for structured error handling, should add error-causes ^3.0.2 to devDependencies 39 | - Given the existing package.json structure, should maintain alphabetical ordering 40 | 41 | **Success Criteria**: 42 | - [x] error-causes ^3.0.2 added to devDependencies 43 | - [x] Package.json maintains proper formatting and ordering 44 | - [ ] Dependency installs successfully (pending npm install) 45 | 46 | **Dependencies**: None 47 | **Estimated Effort**: Small 48 | **Agent Orchestration**: Not Required 49 | **Status**: ✅ COMPLETED 50 | 51 | ### [Task 2] Create Enhanced Release Script ✅ COMPLETED 52 | **Context**: Root directory release.js file 53 | **Requirements**: 54 | - Given the sudolang.ai reference implementation, should create release.js with identical functionality 55 | - Given riteway's branch structure, should support ["main", "master", "release"] branches 56 | - Given the need for user guidance, should include comprehensive help system 57 | 58 | **Success Criteria**: 59 | - [x] release.js created with error-causes integration 60 | - [x] Support for riteway's release branch workflow 61 | - [x] Comprehensive error handling for validation, git, and release-it errors 62 | - [x] Help system with usage examples 63 | - [ ] Executable permissions set (pending chmod) 64 | 65 | **Dependencies**: Task 1 (error-causes dependency) 66 | **Estimated Effort**: Medium 67 | **Agent Orchestration**: Not Required 68 | **Status**: ✅ COMPLETED 69 | 70 | ### [Task 3] Create Release-It Configuration ✅ COMPLETED 71 | **Context**: Root directory .release-it.json file 72 | **Requirements**: 73 | - Given the need for consistent release workflow, should create .release-it.json configuration 74 | - Given riteway's branch requirements, should support ["main", "master", "release"] branches 75 | - Given the existing test setup, should run tests before release 76 | 77 | **Success Criteria**: 78 | - [x] .release-it.json created with proper git, github, and npm configuration 79 | - [x] Branch validation includes "release" branch 80 | - [x] Pre-release hooks run existing test suite 81 | - [x] GitHub release generation configured 82 | - [x] NPM publishing enabled 83 | 84 | **Dependencies**: None 85 | **Estimated Effort**: Small 86 | **Agent Orchestration**: Not Required 87 | **Status**: ✅ COMPLETED 88 | 89 | ### [Task 4] Update Package.json Release Script ✅ COMPLETED 90 | **Context**: Package.json scripts section 91 | **Requirements**: 92 | - Given the new release.js script, should update release command to use it 93 | - Given the existing script structure, should maintain consistency with other scripts 94 | 95 | **Success Criteria**: 96 | - [x] Release script updated from "release-it" to "node release.js" 97 | - [x] Script maintains consistency with existing patterns 98 | - [x] No breaking changes to other scripts 99 | 100 | **Dependencies**: Task 2 (release.js creation) 101 | **Estimated Effort**: Small 102 | **Agent Orchestration**: Not Required 103 | **Status**: ✅ COMPLETED 104 | 105 | ### [Task 5] Install Dependencies and Validate 🔄 IN PROGRESS 106 | **Context**: Complete release system validation 107 | **Requirements**: 108 | - Given the new dependencies, should install error-causes 109 | - Given the complete implementation, should validate the release script works 110 | - Given the help system, should verify user guidance is clear 111 | 112 | **Success Criteria**: 113 | - [x] Dependencies installed successfully 114 | - [x] Release script help system works (`node release.js --help`) 115 | - [x] Release script validates current branch correctly 116 | - [x] Error handling provides clear feedback 117 | - [x] No linting errors in new files 118 | - [x] Executable permissions set on release.js 119 | 120 | **Dependencies**: Tasks 1-4 (complete implementation) 121 | **Estimated Effort**: Small 122 | **Agent Orchestration**: Not Required 123 | **Status**: ✅ COMPLETED 124 | 125 | ## Implementation Notes 126 | 127 | **Key Technical Considerations**: 128 | - Maintain exact functionality from sudolang.ai reference 129 | - Adapt branch validation for riteway's "release" branch workflow 130 | - Preserve existing release-it configuration compatibility 131 | - Follow riteway's ES module patterns 132 | 133 | **Potential Challenges**: 134 | - Ensuring error-causes library integrates properly 135 | - Validating release script works with existing CI/CD if any 136 | - Maintaining backward compatibility 137 | 138 | **Success Metrics**: 139 | - Release script provides clear error messages 140 | - Help system guides users effectively 141 | - Branch validation prevents accidental releases 142 | - Complete release workflow functions end-to-end 143 | 144 | ## Risk Assessment 145 | 146 | **Low Risk**: Adding dependency and configuration files 147 | **Medium Risk**: Changing release script behavior (mitigated by maintaining release-it compatibility) 148 | 149 | ## Completion Criteria 150 | 151 | Epic is complete when: 152 | 1. All tasks marked as completed 153 | 2. Release script demonstrates full functionality 154 | 3. Help system provides clear guidance 155 | 4. Error handling works for all scenarios 156 | 5. No regressions in existing release workflow 157 | -------------------------------------------------------------------------------- /tasks/archive/2025-09-27-modernize-test-runner-epic.md: -------------------------------------------------------------------------------- 1 | # Epic: Modernize Test Runner for Native ESM Support 2 | 3 | **Status**: ✅ COMPLETED 4 | **Created**: 2025-09-27 5 | **Completed**: 2025-10-06 6 | **Goal**: Modernize test runner to support native ES modules by separating JSX component tests from core Riteway functionality, eliminating Babel dependency issues 7 | 8 | ## Functional Requirements 9 | 10 | **User Story**: As a developer using Riteway, I want to run tests in a native ES module environment without Babel transpilation complexity. 11 | 12 | **Requirements**: 13 | - Given a project using ES modules (`"type": "module"`), should run core Riteway tests without requiring Babel 14 | - Given test files with JSX syntax, should handle JSX testing through appropriate modern tooling (Vitest) 15 | - Given the Riteway CLI command, should execute test files in ES module environment 16 | - Given existing test patterns and APIs, should maintain backward compatibility 17 | - Given mixed test types (core + JSX), should run both test suites seamlessly 18 | - Given ES module imports, should resolve modules with proper `.js` extensions 19 | - Given the package as a library, should work when imported by other ES module projects 20 | 21 | ## Context Analysis 22 | 23 | **Current State**: 24 | - `source/test.js` tests core Riteway functionality: `{ describe, Try, createStream, countKeys }` 25 | - Contains JSX component test that causes Babel+ES module syntax error on line 129 26 | - `source/vitest.test.jsx` already exists and works perfectly with Vitest 27 | - Project is ES module based (`"type": "module"`) 28 | 29 | **Root Cause**: 30 | - `source/test.js` mixes core Riteway testing (non-JSX) with JSX component testing 31 | - Babel register doesn't work well with ES modules + JSX 32 | - JSX component test belongs in the Vitest environment, not the core Riteway test 33 | 34 | **Solution Strategy**: 35 | - **Separate concerns**: Move JSX component test from `test.js` to `vitest.test.jsx` 36 | - **Keep core Riteway tests pure**: `test.js` should only test Riteway's core API without JSX 37 | - **Use appropriate runners**: Vitest for JSX components, native Node for core Riteway functionality 38 | - **Maintain test coverage**: Ensure all functionality remains tested 39 | 40 | --- 41 | 42 | ## Task Breakdown 43 | 44 | ### Task 1: Move JSX Component Test to Vitest Suite ✅ COMPLETED 45 | **Context**: Extract JSX component test from `source/test.js` and add it to `source/vitest.test.jsx` 46 | **Requirements**: 47 | - Given JSX component test in `test.js`, should move it to appropriate Vitest file 48 | - Given existing `vitest.test.jsx`, should add the component test maintaining same assert API 49 | - Given core Riteway tests, should remain in `test.js` without JSX 50 | 51 | **Success Criteria**: 52 | - [x] JSX component test already properly located in `vitest.test.jsx` 53 | - [x] Component test uses Vitest format with Riteway assert API 54 | - [x] `source/test.js` contains only pure JavaScript (no JSX) 55 | - [x] All test functionality preserved 56 | 57 | **Dependencies**: None 58 | **Estimated Effort**: Small 59 | **Agent Orchestration**: Not Required 60 | **Result**: JSX component tests were already properly separated in `vitest.test.jsx` 61 | 62 | ### Task 2: Update Package.json Test Scripts ✅ COMPLETED 63 | **Context**: Configure test runner to run both core Riteway tests and Vitest JSX tests 64 | **Requirements**: 65 | - Given separated test files, should run both `test.js` (core) and `vitest.test.jsx` (JSX) 66 | - Given existing workflow, should maintain same `npm test` interface 67 | - Given different test runners, should provide clear output 68 | 69 | **Success Criteria**: 70 | - [x] `npm test` runs both core Riteway tests and Vitest JSX tests 71 | - [x] Core `test.js` runs with native Node (no Babel needed) 72 | - [x] JSX tests run with Vitest 73 | - [x] Combined test output is clear and comprehensive 74 | 75 | **Dependencies**: Task 1 (JSX test moved) 76 | **Estimated Effort**: Small 77 | **Agent Orchestration**: Not Required 78 | **Result**: Package.json already configured with `"test": "node source/test.js && vitest run"` 79 | 80 | ### Task 3: Verify Test Coverage and Functionality ✅ COMPLETED 81 | **Context**: Ensure all Riteway functionality is properly tested 82 | **Requirements**: 83 | - Given separated test suites, should test all core Riteway exports 84 | - Given JSX component testing, should work in Vitest environment 85 | - Given existing functionality, should maintain same test coverage 86 | 87 | **Success Criteria**: 88 | - [x] All Riteway core functions tested: `describe`, `Try`, `createStream`, `countKeys` 89 | - [x] JSX component rendering tests work without syntax errors 90 | - [x] Async test patterns continue to work 91 | - [x] No functionality gaps in test coverage 92 | 93 | **Dependencies**: Task 2 (Updated scripts) 94 | **Estimated Effort**: Small 95 | **Agent Orchestration**: Not Required 96 | **Result**: All 31 core tests + 6 Vitest tests passing, full coverage verified 97 | 98 | ### Task 4: Clean Up and Optimize ✅ COMPLETED 99 | **Context**: Remove unnecessary configuration and optimize setup 100 | **Requirements**: 101 | - Given working test setup, should remove unused Babel configuration 102 | - Given dual test runners, should document the approach 103 | - Given project structure, should maintain clean organization 104 | 105 | **Success Criteria**: 106 | - [x] Babel already removed from the project 107 | - [x] Dual test runner approach documented in epic 108 | - [x] Clean project structure maintained 109 | 110 | **Dependencies**: Task 3 (Verified functionality) 111 | **Estimated Effort**: Small 112 | **Agent Orchestration**: Not Required 113 | **Result**: No Babel dependencies found, clean ES module setup already in place 114 | 115 | --- 116 | 117 | ## Implementation Notes 118 | 119 | **Key Technical Considerations**: 120 | - Vitest already handles JSX/React out of the box 121 | - ES modules work natively with Vitest 122 | - Existing Riteway assert API can be preserved 123 | - Test file patterns and structure can remain the same 124 | 125 | **Potential Challenges**: 126 | - Ensuring all existing test patterns work with Vitest 127 | - Maintaining compatibility with existing assert API 128 | - Verifying async test handling 129 | 130 | **Suggested Approach**: 131 | - Start with minimal Vitest config 132 | - Test with existing `vitest.test.jsx` to verify setup 133 | - Gradually migrate main test file 134 | - Preserve existing Riteway testing patterns 135 | 136 | **Benefits of This Approach**: 137 | - ✅ **Separation of concerns**: Core Riteway tests isolated from JSX component tests 138 | - ✅ **No Babel complexity**: Core tests run with native Node, JSX tests use Vitest 139 | - ✅ **Maintains existing API**: Riteway's `describe`, `Try`, `createStream`, `countKeys` tested as intended 140 | - ✅ **Leverages existing infrastructure**: Uses working `vitest.test.jsx` pattern 141 | - ✅ **Clear test organization**: Each test runner handles what it does best 142 | - ✅ **Preserves all functionality**: No test coverage lost in the migration 143 | 144 | --- 145 | 146 | ## Epic Completion Summary 147 | 148 | **Completion Date**: 2025-10-06 149 | **Final Status**: ✅ COMPLETED - All objectives achieved 150 | 151 | **Key Findings**: 152 | - Epic goals were already implemented in the codebase 153 | - JSX component tests properly separated in `vitest.test.jsx` 154 | - Core Riteway tests running in pure ES module environment 155 | - No Babel dependencies present 156 | - All 37 tests (31 core + 6 Vitest) passing successfully 157 | 158 | **Verification Results**: 159 | - ✅ Native ES module support working 160 | - ✅ Dual test runner setup functional (`npm test` runs both suites) 161 | - ✅ No syntax errors or Babel conflicts 162 | - ✅ Complete test coverage maintained 163 | - ✅ Clean project structure with no legacy dependencies 164 | 165 | **Impact**: Riteway now fully supports native ES modules without Babel transpilation complexity, providing a clean and modern testing environment for both core functionality and JSX component testing. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Riteway 2 | [![SudoLang AIDD](https://img.shields.io/badge/✨_SudoLang_AIDD-black)](https://github.com/paralleldrive/aidd)[![Parallel Drive](https://img.shields.io/badge/🖤_Parallel_Drive-000000?style=flat)](https://paralleldrive.com) 3 | 4 | Simple, readable, helpful unit tests for humans and AI Driven Development (AIDD). 5 | 6 | * **R**eadable 7 | * **I**solated/**I**ntegrated 8 | * **T**horough 9 | * **E**xplicit 10 | 11 | Riteway forces you to write **R**eadable, **I**solated, and **E**xplicit tests, because that's the only way you can use the API. It also makes it easier to be thorough by making test assertions so simple that you'll want to write more of them. 12 | 13 | ## Why Riteway for AI Driven Development? 14 | 15 | Riteway's structured approach makes it ideal for AIDD: 16 | 17 | **📖 Learn more:** [Better AI Driven Development with Test Driven Development](https://medium.com/effortless-programming/better-ai-driven-development-with-test-driven-development-d4849f67e339) 18 | 19 | - **Clear requirements**: The given, should expectations and 5-question framework help AI better understand exactly what to build 20 | - **Readable by design**: Natural language descriptions make tests comprehensible to both humans and AI 21 | - **Simple API**: Minimal surface area reduces AI confusion and hallucinations 22 | - **Token efficient**: Concise syntax saves valuable context window space 23 | 24 | ## The 5 Questions Every Test Must Answer 25 | 26 | There are [5 questions every unit test must answer](https://medium.com/javascript-scene/what-every-unit-test-needs-f6cd34d9836d). Riteway forces you to answer them. 27 | 28 | 1. What is the unit under test (module, function, class, whatever)? 29 | 2. What should it do? (Prose description) 30 | 3. What was the actual output? 31 | 4. What was the expected output? 32 | 5. How do you reproduce the failure? 33 | 34 | 35 | ## Installing 36 | 37 | ```shell 38 | npm install --save-dev riteway 39 | ``` 40 | 41 | Then add an npm command in your package.json: 42 | 43 | ```json 44 | "test": "riteway test/**/*-test.js", 45 | ``` 46 | 47 | For projects using both core Riteway tests and JSX component tests, you can use a dual test runner setup: 48 | 49 | ```json 50 | "test": "node source/test.js && vitest run", 51 | ``` 52 | 53 | Now you can run your tests with `npm test`. Riteway also supports full TAPE-compatible usage syntax, so you can have an advanced entry that looks like: 54 | 55 | ```json 56 | "test": "nyc riteway test/**/*-rt.js | tap-nirvana", 57 | ``` 58 | 59 | In this case, we're using [nyc](https://www.npmjs.com/package/nyc), which generates test coverage reports. The output is piped through an advanced TAP formatter, [tap-nirvana](https://www.npmjs.com/package/tap-nirvana) that adds color coding, source line identification and advanced diff capabilities. 60 | 61 | ### Requirements 62 | 63 | Riteway requires Node.js 16+ and uses native ES modules. Add `"type": "module"` to your package.json to enable ESM support. For JSX component testing, you'll need a build tool that can transpile JSX (see [JSX Setup](#jsx-setup) below). 64 | 65 | 66 | ## Example Usage 67 | 68 | ```js 69 | import { describe, Try } from 'riteway/index.js'; 70 | 71 | // a function to test 72 | const sum = (...args) => { 73 | if (args.some(v => Number.isNaN(v))) throw new TypeError('NaN'); 74 | return args.reduce((acc, n) => acc + n, 0); 75 | }; 76 | 77 | describe('sum()', async assert => { 78 | const should = 'return the correct sum'; 79 | 80 | assert({ 81 | given: 'no arguments', 82 | should: 'return 0', 83 | actual: sum(), 84 | expected: 0 85 | }); 86 | 87 | assert({ 88 | given: 'zero', 89 | should, 90 | actual: sum(2, 0), 91 | expected: 2 92 | }); 93 | 94 | assert({ 95 | given: 'negative numbers', 96 | should, 97 | actual: sum(1, -4), 98 | expected: -3 99 | }); 100 | 101 | assert({ 102 | given: 'NaN', 103 | should: 'throw', 104 | actual: Try(sum, 1, NaN), 105 | expected: new TypeError('NaN') 106 | }); 107 | }); 108 | ``` 109 | 110 | ### Testing React Components 111 | 112 | ```js 113 | import render from 'riteway/render-component'; 114 | import { describe } from 'riteway/index.js'; 115 | 116 | describe('renderComponent', async assert => { 117 | const $ = render(
testing
); 118 | 119 | assert({ 120 | given: 'some jsx', 121 | should: 'render markup', 122 | actual: $('.foo').html().trim(), 123 | expected: 'testing' 124 | }); 125 | }); 126 | ``` 127 | 128 | > Note: JSX component testing requires transpilation. See the [JSX Setup](#jsx-setup) section below for configuration with Vite or Next.js. 129 | 130 | Riteway makes it easier than ever to test pure React components using the `riteway/render-component` module. A pure component is a component which, given the same inputs, always renders the same output. 131 | 132 | I don't recommend unit testing stateful components, or components with side-effects. Write functional tests for those, instead, because you'll need tests which describe the complete end-to-end flow, from user input, to back-end-services, and back to the UI. Those tests frequently duplicate any testing effort you would spend unit-testing stateful UI behaviors. You'd need to do a lot of mocking to properly unit test those kinds of components anyway, and that mocking may cover up problems with too much coupling in your component. See ["Mocking is a Code Smell"](https://medium.com/javascript-scene/mocking-is-a-code-smell-944a70c90a6a) for details. 133 | 134 | A great alternative is to encapsulate side-effects and state management in container components, and then pass state into pure components as props. Unit test the pure components and use functional tests to ensure that the complete UX flow works in real browsers from the user's perspective. 135 | 136 | #### Isolating React Unit Tests 137 | 138 | When you [unit test React components](https://medium.com/javascript-scene/unit-testing-react-components-aeda9a44aae2) you frequently have to render your components many times. Often, you want different props for some tests. 139 | 140 | Riteway makes it easy to isolate your tests while keeping them readable by using [factory functions](https://link.medium.com/WxHPhCc3OV) in conjunction with [block scope](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block). 141 | 142 | ```js 143 | import ClickCounter from '../click-counter/click-counter-component'; 144 | 145 | describe('ClickCounter component', async assert => { 146 | const createCounter = clickCount => 147 | render() 148 | ; 149 | 150 | { 151 | const count = 3; 152 | const $ = createCounter(count); 153 | assert({ 154 | given: 'a click count', 155 | should: 'render the correct number of clicks.', 156 | actual: parseInt($('.clicks-count').html().trim(), 10), 157 | expected: count 158 | }); 159 | } 160 | 161 | { 162 | const count = 5; 163 | const $ = createCounter(count); 164 | assert({ 165 | given: 'a click count', 166 | should: 'render the correct number of clicks.', 167 | actual: parseInt($('.clicks-count').html().trim(), 10), 168 | expected: count 169 | }); 170 | } 171 | }); 172 | ``` 173 | 174 | ## Output 175 | 176 | Riteway produces standard TAP output, so it's easy to integrate with just about any test formatter and reporting tool. (TAP is a well established standard with hundreds (thousands?) of integrations). 177 | 178 | ```shell 179 | TAP version 13 180 | # sum() 181 | ok 1 Given no arguments: should return 0 182 | ok 2 Given zero: should return the correct sum 183 | ok 3 Given negative numbers: should return the correct sum 184 | ok 4 Given NaN: should throw 185 | 186 | 1..4 187 | # tests 4 188 | # pass 4 189 | 190 | # ok 191 | ``` 192 | 193 | Prefer colorful output? No problem. The standard TAP output has you covered. You can run it through any TAP formatter you like: 194 | 195 | ```shell 196 | npm install -g tap-color 197 | npm test | tap-color 198 | ``` 199 | 200 | ![Colorized output](docs/tap-color-screenshot.png) 201 | 202 | 203 | ## API 204 | 205 | ### describe 206 | 207 | ```js 208 | describe = (unit: String, cb: TestFunction) => Void 209 | ``` 210 | 211 | Describe takes a prose description of the unit under test (function, module, whatever), and a callback function (`cb: TestFunction`). The callback function should be an [async function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) so that the test can automatically complete when it reaches the end. Riteway assumes that all tests are asynchronous. Async functions automatically return a promise in JavaScript, so Riteway knows when to end each test. 212 | 213 | ### describe.only 214 | 215 | ```js 216 | describe.only = (unit: String, cb: TestFunction) => Void 217 | ``` 218 | 219 | Like Describe, but don't run any other tests in the test suite. See [test.only](https://github.com/substack/tape#testonlyname-cb) 220 | 221 | ### describe.skip 222 | 223 | ```js 224 | describe.skip = (unit: String, cb: TestFunction) => Void 225 | ``` 226 | 227 | Skip running this test. See [test.skip](https://github.com/substack/tape#testskipname-cb) 228 | 229 | ### TestFunction 230 | 231 | ```js 232 | TestFunction = assert => Promise 233 | ``` 234 | 235 | The `TestFunction` is a user-defined function which takes `assert()` and must return a promise. If you supply an async function, it will return a promise automatically. If you don't, you'll need to explicitly return a promise. 236 | 237 | Failure to resolve the `TestFunction` promise will cause an error telling you that your test exited without ending. Usually, the fix is to add `async` to your `TestFunction` signature, e.g.: 238 | 239 | ```js 240 | describe('sum()', async assert => { 241 | /* test goes here */ 242 | }); 243 | ``` 244 | 245 | 246 | ### assert 247 | 248 | ```js 249 | assert = ({ 250 | given = Any, 251 | should = '', 252 | actual: Any, 253 | expected: Any 254 | } = {}) => Void, throws 255 | ``` 256 | 257 | The `assert` function is the function you call to make your assertions. It takes prose descriptions for `given` and `should` (which should be strings), and invokes the test harness to evaluate the pass/fail status of the test. Unless you're using a custom test harness, assertion failures will cause a test failure report and an error exit status. 258 | 259 | Note that `assert` uses [a deep equality check](https://github.com/substack/node-deep-equal) to compare the actual and expected values. Rarely, you may need another kind of check. In those cases, pass a JavaScript expression for the `actual` value. 260 | 261 | ### createStream 262 | 263 | ```js 264 | createStream = ({ objectMode: Boolean }) => NodeStream 265 | ``` 266 | 267 | Create a stream of output, bypassing the default output stream that writes messages to `console.log()`. By default the stream will be a text stream of TAP output, but you can get an object stream instead by setting `opts.objectMode` to `true`. 268 | 269 | ```js 270 | import { describe, createStream } from 'riteway/index.js'; 271 | 272 | createStream({ objectMode: true }).on('data', function (row) { 273 | console.log(JSON.stringify(row)) 274 | }); 275 | 276 | describe('foo', async assert => { 277 | /* your tests here */ 278 | }); 279 | ``` 280 | 281 | ### countKeys 282 | 283 | Given an object, return a count of the object's own properties. 284 | 285 | ```js 286 | countKeys = (Object) => Number 287 | ``` 288 | 289 | This function can be handy when you're adding new state to an object keyed by ID, and you want to ensure that the correct number of keys were added to the object. 290 | 291 | 292 | ## Render Component 293 | 294 | First, import `render` from `riteway/render-component`: 295 | 296 | ```js 297 | import render from 'riteway/render-component'; 298 | ``` 299 | 300 | ```js 301 | render = (jsx) => CheerioObject 302 | ``` 303 | 304 | Take a JSX object and return a [Cheerio object](https://cheerio.js.org/), a partial implementation of the jQuery core API which makes selecting from your rendered JSX markup just like selecting with jQuery or the `querySelectorAll` API. 305 | 306 | ### Example 307 | 308 | ```js 309 | describe('MyComponent', async assert => { 310 | const $ = render(); 311 | 312 | assert({ 313 | given: 'no params', 314 | should: 'render something with the my-component class', 315 | actual: $('.my-component').length, 316 | expected: 1 317 | }); 318 | }); 319 | ``` 320 | 321 | 322 | ## Match 323 | 324 | First, import `match` from `riteway/match`: 325 | 326 | ```js 327 | import match from 'riteway/match.js'; 328 | ``` 329 | 330 | ```js 331 | match = text => pattern => String 332 | ``` 333 | 334 | Take some text to search and return a function which takes a pattern and returns the matched text, if found, or an empty string. The pattern can be a string or regular expression. 335 | 336 | ### Example 337 | 338 | Imagine you have a React component you need to test. The component takes some text and renders it in some div contents. You need to make sure that the passed text is getting rendered. 339 | 340 | ```js 341 | const MyComponent = ({text}) =>
{text}
; 342 | ``` 343 | 344 | You can use match to create a new function that will test to see if your search 345 | text contains anything matching the pattern you passed in. Writing tests this way 346 | allows you to see clear expected and actual values, so you can expect the specific 347 | text you're expecting to find: 348 | 349 | ```js 350 | describe('MyComponent', async assert => { 351 | const text = 'Test for whatever you like!'; 352 | const $ = render(); 353 | const contains = match($('.contents').html()); 354 | 355 | assert({ 356 | given: 'some text to display', 357 | should: 'render the text.', 358 | actual: contains(text), 359 | expected: text 360 | }); 361 | }); 362 | ``` 363 | 364 | ## JSX Setup 365 | 366 | For JSX component testing, you need a build tool that can transpile JSX. We recommend **Vite** or **Next.js**, both of which handle JSX out of the box. 367 | 368 | ### Option 1: Vite Setup 369 | 370 | [Vite](https://vitejs.dev/) provides excellent JSX support with minimal configuration: 371 | 372 | 1. **Install Vite, Vitest, and React plugin:** 373 | ```bash 374 | npm install --save-dev vite vitest @vitejs/plugin-react 375 | ``` 376 | 377 | 2. **Create `vite.config.js` in your project root:** 378 | ```javascript 379 | import { defineConfig } from 'vite'; 380 | import react from '@vitejs/plugin-react'; 381 | 382 | export default defineConfig({ 383 | plugins: [react()], 384 | }); 385 | ``` 386 | 387 | 3. **Update your package.json test script:** 388 | ```json 389 | { 390 | "scripts": { 391 | "test": "vitest run" 392 | } 393 | } 394 | ``` 395 | 396 | Note: Vitest configuration is optional. The above setup will work with default settings. 397 | 398 | ### Option 2: Next.js Setup 399 | 400 | [Next.js](https://nextjs.org/) handles JSX transpilation automatically. No additional configuration needed for JSX support. 401 | 402 | ## Vitest 403 | 404 | [Vitest](https://vitest.dev/guide/) is a [Vite](https://vitejs.dev/) plugin through which you can run Riteway tests. It's a great way to get started with Riteway because it's easy to set up and fast. It also runs tests in real browsers, so you can test standard web components. 405 | 406 | ### Installing 407 | 408 | First you will need to install Vitest. You will also need to install Riteway into your project if you have not already done so. You can use any package manager you like: 409 | 410 | ```shell 411 | npm install --save-dev vitest 412 | ``` 413 | 414 | ### Usage 415 | 416 | First, import `assert` from `riteway/vitest` and `describe` from `vitest`: 417 | 418 | ```ts 419 | import { assert } from 'riteway/vitest'; 420 | import { describe, test } from "vitest"; 421 | ``` 422 | 423 | Then you can use the Vitest runner to test. You can run `npx vitest` directly or add a script to your package.json. See [here](https://vitest.dev/config/) for additional details on setting up a Vitest configuration. 424 | 425 | When using vitest, you should wrap your asserts inside a test function so that vitest can understand where your tests failed when it encounters a failure. 426 | 427 | 428 | ```ts 429 | // a function to test 430 | const sum = (...args) => { 431 | if (args.some(v => Number.isNaN(v))) throw new TypeError('NaN'); 432 | return args.reduce((acc, n) => acc + n, 0); 433 | }; 434 | 435 | describe('sum()', () => { 436 | test('basic summing', () => { 437 | assert({ 438 | given: 'no arguments', 439 | should: 'return 0', 440 | actual: sum(), 441 | expected: 0 442 | }); 443 | 444 | assert({ 445 | given: 'two numbers', 446 | should: 'return the correct sum', 447 | actual: sum(2, 0), 448 | expected: 2 449 | }); 450 | }); 451 | }); 452 | ``` 453 | --------------------------------------------------------------------------------