diff --git a/.agents/justifications/.gitkeep b/.agents/justifications/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/.agents/plans/.gitkeep b/.agents/plans/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/.agents/plans/swift-pink-sky-simplewebauthn-v13-upgrade.md b/.agents/plans/swift-pink-sky-simplewebauthn-v13-upgrade.md new file mode 100644 index 000000000..8ca225c13 --- /dev/null +++ b/.agents/plans/swift-pink-sky-simplewebauthn-v13-upgrade.md @@ -0,0 +1,186 @@ +--- +date: 2026-01-14 +title: Simplewebauthn V13 Upgrade +--- + +## Overview + +Upgrade SimpleWebAuthn packages from v9.x to v13.x to address the deprecation of `@simplewebauthn/types` and take advantage of new features and improvements. + +## Current State + +The codebase currently uses: +- `@simplewebauthn/browser@9.x` +- `@simplewebauthn/server@9.x` +- `@simplewebauthn/types@9.x` + +## Breaking Changes Summary (v9 → v13) + +### v10.0.0 Breaking Changes +1. **Minimum Node version raised to Node v20** +2. **`generateRegistrationOptions()` now expects `Base64URLString` for `excludeCredentials` IDs** (no more `type: 'public-key'` needed) +3. **`generateAuthenticationOptions()` now expects `Base64URLString` for `allowCredentials` IDs** +4. **`credentialID` returned from verification methods is now `Base64URLString`** instead of `Uint8Array` +5. **`AuthenticatorDevice.credentialID` is now `Base64URLString`** +6. **`rpID` is now required when calling `generateAuthenticationOptions()`** +7. **`generateRegistrationOptions()` will generate random user IDs** if not provided +8. **`user.id` is treated as base64url string in `startRegistration()`** +9. **`userHandle` is treated as base64url string in `startAuthentication()`** + +### v11.0.0 Breaking Changes +1. **Positional arguments in `startRegistration()` and `startAuthentication()` replaced by object** + - Before: `startRegistration(options)` + - After: `startRegistration({ optionsJSON: options })` + - Before: `startAuthentication(options)` + - After: `startAuthentication({ optionsJSON: options })` +2. **`AuthenticatorDevice` type renamed to `WebAuthnCredential`** + - `credentialID` → `credential.id` + - `credentialPublicKey` → `credential.publicKey` +3. **`verifyRegistrationResponse()` returns `registrationInfo.credential` instead of individual properties** + - `credentialID` → `credential.id` + - `credentialPublicKey` → `credential.publicKey` + - `counter` → `credential.counter` + - `transports` are now in `credential.transports` +4. **`verifyAuthenticationResponse()` uses `credential` argument instead of `authenticator`** + +### v13.0.0 Breaking Changes +1. **`@simplewebauthn/types` package is retired** + - Types are now exported from `@simplewebauthn/browser` and `@simplewebauthn/server` + - Import types from `@simplewebauthn/server` instead + +## Files to Update + +### Package Changes +1. Remove `@simplewebauthn/types` dependency +2. Update `@simplewebauthn/browser` to `^13.2.2` +3. Update `@simplewebauthn/server` to `^13.2.2` + +### Server-side Files + +#### 1. `packages/lib/server-only/auth/create-passkey-registration-options.ts` +- Change import from `@simplewebauthn/types` to `@simplewebauthn/server` +- Remove `type: 'public-key'` from `excludeCredentials` items +- Update `userID` to use `isoUint8Array.fromUTF8String()` for proper encoding + +#### 2. `packages/lib/server-only/auth/create-passkey-authentication-options.ts` +- Change import from `@simplewebauthn/types` to `@simplewebauthn/server` +- Remove `type: 'public-key'` from `allowCredentials` items + +#### 3. `packages/lib/server-only/auth/create-passkey-signin-options.ts` +- No changes needed (already using correct options) + +#### 4. `packages/lib/server-only/auth/create-passkey.ts` +- Change import from `@simplewebauthn/types` to `@simplewebauthn/server` +- Update to use new `registrationInfo.credential` structure: + - `credentialID` → `credential.id` + - `credentialPublicKey` → `credential.publicKey` + - `counter` → `credential.counter` +- Note: `credential.id` is now a `Base64URLString`, so `Buffer.from(credentialID)` needs updating + +#### 5. `packages/lib/server-only/document/is-recipient-authorized.ts` +- Update `verifyAuthenticationResponse()` to use `credential` instead of `authenticator`: + - Change `authenticator: { credentialID, credentialPublicKey, counter }` to `credential: { id, publicKey, counter }` +- Since `credential.id` is now base64url string, convert stored `credentialId` buffer to base64url + +#### 6. `packages/auth/server/routes/passkey.ts` +- Update `verifyAuthenticationResponse()` to use `credential` instead of `authenticator` +- Same changes as `is-recipient-authorized.ts` + +#### 7. `packages/trpc/server/auth-router/create-passkey.ts` +- Change import from `@simplewebauthn/types` to `@simplewebauthn/server` + +### Browser-side Files + +#### 8. `apps/remix/app/components/dialogs/passkey-create-dialog.tsx` +- Update `startRegistration()` call: + - Before: `startRegistration(passkeyRegistrationOptions)` + - After: `startRegistration({ optionsJSON: passkeyRegistrationOptions })` + +#### 9. `apps/remix/app/components/forms/signin.tsx` +- Update `startAuthentication()` call: + - Before: `startAuthentication(options)` + - After: `startAuthentication({ optionsJSON: options })` + +#### 10. `apps/remix/app/components/general/document-signing/document-signing-auth-passkey.tsx` +- Update `startAuthentication()` call: + - Before: `startAuthentication(options)` + - After: `startAuthentication({ optionsJSON: options })` + +### Database/Schema Considerations + +The database stores `credentialId` as `Bytes`. The new API returns `credential.id` as `Base64URLString`. We need to: +1. When **storing** a new passkey: Convert from `Base64URLString` to `Buffer` +2. When **passing to verification**: Convert from `Buffer` to `Base64URLString` + +Use `isoBase64URL` helper from `@simplewebauthn/server/helpers` for these conversions. + +## Implementation Steps + +### Step 1: Update package.json dependencies +```bash +npm uninstall @simplewebauthn/types +npm install @simplewebauthn/browser@^13.2.2 @simplewebauthn/server@^13.2.2 +``` + +### Step 2: Update type imports +Replace all `@simplewebauthn/types` imports with `@simplewebauthn/server` + +### Step 3: Update browser-side API calls +- `startRegistration(options)` → `startRegistration({ optionsJSON: options })` +- `startAuthentication(options)` → `startAuthentication({ optionsJSON: options })` + +### Step 4: Update server-side registration +- Update `excludeCredentials` format (remove `type: 'public-key'`) +- Update `userID` encoding if needed +- Update `verifyRegistrationResponse()` result handling for new `credential` structure + +### Step 5: Update server-side authentication +- Update `allowCredentials` format (remove `type: 'public-key'`) +- Update `verifyAuthenticationResponse()` to use `credential` instead of `authenticator` +- Handle `Base64URLString` for `credential.id` + +### Step 6: Update credential storage/retrieval +- When storing: Convert `Base64URLString` to `Buffer` +- When reading: Convert `Buffer` to `Base64URLString` + +### Step 7: Test passkey flows +1. Test passkey creation +2. Test passkey sign-in +3. Test passkey authentication for document signing +4. Test passkey deletion + +## Code Examples + +### Converting stored Buffer to Base64URLString for verification +```typescript +import { isoBase64URL } from '@simplewebauthn/server/helpers'; + +// When reading from database (Buffer) and passing to verification +const credential = { + id: isoBase64URL.fromBuffer(passkey.credentialId), + publicKey: new Uint8Array(passkey.credentialPublicKey), + counter: Number(passkey.counter), + transports: passkey.transports, +}; +``` + +### Converting Base64URLString to Buffer for storage +```typescript +import { isoBase64URL } from '@simplewebauthn/server/helpers'; + +// When storing from registration response +const credentialIdBuffer = Buffer.from( + isoBase64URL.toBuffer(registrationInfo.credential.id) +); +``` + +## Risks and Mitigations + +1. **Database compatibility**: The `credentialId` is stored as `Bytes` in the database. The new API uses `Base64URLString`. We need proper conversion functions. + - **Mitigation**: Use `isoBase64URL.fromBuffer()` and `isoBase64URL.toBuffer()` for conversions + +2. **Existing passkeys**: Existing passkeys should continue to work as long as conversion is done correctly. + - **Mitigation**: Test with existing passkeys after upgrade + +3. **Browser compatibility**: v10+ requires newer browser APIs. + - **Mitigation**: `browserSupportsWebAuthn()` already handles this check \ No newline at end of file diff --git a/.agents/scratches/.gitkeep b/.agents/scratches/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/.opencode/commands/commit.md b/.opencode/commands/commit.md new file mode 100644 index 000000000..d4382f18c --- /dev/null +++ b/.opencode/commands/commit.md @@ -0,0 +1,80 @@ +--- +description: Add and commit changes using conventional commits +allowed-tools: Bash, Read, Glob, Grep +--- + +Create a git commit for the current changes using the Conventional Commits standard. + +## Process + +1. **Analyze the changes** by running: + - `git status` to see all modified/untracked files + - `git diff` to see unstaged changes + - `git diff --staged` to see already-staged changes + - `git log --oneline -5` to see recent commit style + +2. **Stage appropriate files**: + - Stage all related changes with `git add` + - Do NOT stage files that appear to contain secrets (.env, credentials, API keys, tokens) + - If you detect potential secrets, warn the user and skip those files + +3. **Determine the commit type** based on the changes: + - `feat`: New feature or capability + - `fix`: Bug fix + - `docs`: Documentation only + - `style`: Formatting, whitespace (not CSS) + - `refactor`: Code restructuring without behavior change + - `perf`: Performance improvement + - `test`: Adding or updating tests + - `build`: Build system or dependencies + - `ci`: CI/CD configuration + - `chore`: Maintenance tasks, tooling, config + + NOTE: Do not use a scope for commits + +4. **Write the commit message**: + - **Subject line**: `: ` + - Use imperative mood ("add" not "added") + - Lowercase, no period at end + - Max 50 characters if possible, 72 hard limit + - **Body** (if needed): Explain _why_, not _what_ + - Wrap at 72 characters + - Separate from subject with blank line + +## Commit Format + +``` +[scope]: + +[optional body explaining WHY this change was made] +``` + +## Examples + +Simple change: + +``` +fix: handle empty input in parser without throwing +``` + +With body: + +``` +feat: add streaming response support + +Large responses were causing memory issues in production. +Streaming allows processing chunks incrementally. +``` + +## Rules + +- NEVER commit files that may contain secrets +- NEVER use `git commit --amend` unless the user explicitly requests it +- NEVER use `--no-verify` to skip hooks +- If the pre-commit hook fails, fix the issues and create a NEW commit +- If there are no changes to commit, inform the user and stop +- Use a HEREDOC to pass the commit message to ensure proper formatting + +## Execute + +Run the git commands to analyze, stage, and commit the changes now. diff --git a/.opencode/commands/continue.md b/.opencode/commands/continue.md new file mode 100644 index 000000000..bedc43c33 --- /dev/null +++ b/.opencode/commands/continue.md @@ -0,0 +1,112 @@ +--- +description: Continue implementing a spec from a previous session +argument-hint: +--- + +You are continuing implementation of a specification that was started in a previous session. Work autonomously until the feature is complete and tests pass. + +## Your Task + +1. **Read the spec** at `$ARGUMENTS` +2. **Read CODE_STYLE.md** for formatting conventions +3. **Assess current state**: + - Check git status for uncommitted changes + - Run tests to see what's passing/failing (if E2E tests exist) + - Review any existing implementation +4. **Determine what remains** by comparing the spec to the current state +5. **Plan remaining work** using TodoWrite +6. **Continue implementing** until complete + +## Assessing Current State + +Run these commands to understand where the previous session left off: + +```bash +git status # See uncommitted changes +git log --oneline -10 # See recent commits +npm run typecheck -w @documenso/remix # Check for type errors +npm run lint:fix # Check for linting issues +``` + +Review the code that's already been written to understand: + +- What's already implemented +- What's partially done +- What's not started yet + +## Implementation Guidelines + +### During Implementation + +- Follow CODE_STYLE.md strictly (2-space indent, double quotes, braces always, etc.) +- Follow workspace rules for TypeScript, React, TRPC patterns, and Remix conventions +- Mark todos complete as you finish each task +- Commit logical chunks of work + +### Code Quality + +- No stubbed implementations +- Handle edge cases and error conditions +- Include descriptive error messages with context +- Use async/await for all I/O operations +- Use AppError class when throwing errors +- Use Zod for validation and react-hook-form for forms + +### Testing + +**Important**: E2E tests are time-consuming. Only write tests for non-trivial functionality. + +- Write E2E tests in `packages/app-tests/e2e/` using Playwright +- Test critical user flows and edge cases +- Follow existing E2E test patterns in the codebase +- Use descriptive test names that explain what is being tested +- Skip tests for trivial changes (simple UI tweaks, minor refactors, etc.) + +## Autonomous Workflow + +Work continuously through these steps: + +1. **Implement** - Write the code for the current task +2. **Typecheck** - Run `npm run typecheck -w @documenso/remix` to verify types +3. **Lint** - Run `npm run lint:fix` to fix linting issues +4. **Test** - If non-trivial, run E2E tests: `npm run test:dev -w @documenso/app-tests` +5. **Fix** - If tests fail, fix and re-run +6. **Repeat** - Move to next task + +## Stopping Conditions + +**Stop and report success when:** + +- All spec requirements are implemented +- Typecheck passes +- Lint passes +- E2E tests pass (if written for non-trivial functionality) + +**Stop and ask for help when:** + +- The spec is ambiguous and you need clarification +- You encounter a blocking issue you cannot resolve +- You need to make a decision that significantly deviates from the spec +- External dependencies are missing + +## Commands + +```bash +# Type checking +npm run typecheck -w @documenso/remix + +# Linting +npm run lint:fix + +# E2E Tests (only for non-trivial work) +npm run test:dev -w @documenso/app-tests # Run E2E tests in dev mode +npm run test-ui:dev -w @documenso/app-tests # Run E2E tests with UI +npm run test:e2e # Run full E2E test suite + +# Development +npm run dev # Start dev server +``` + +## Begin + +Read the spec file and CODE_STYLE.md, assess the current implementation state, then continue where the previous session left off. Use TodoWrite to track your progress throughout. diff --git a/.opencode/commands/create-justification.md b/.opencode/commands/create-justification.md new file mode 100644 index 000000000..f36a04f15 --- /dev/null +++ b/.opencode/commands/create-justification.md @@ -0,0 +1,75 @@ +--- +description: Create a new justification file in .agents/justifications/ +argument-hint: [content] +--- + +You are creating a new justification file in the `.agents/justifications/` directory. + +## Your Task + +1. **Determine the slug** - Use `$ARGUMENTS` as the file slug (kebab-case recommended) +2. **Gather content** - Collect or generate the justification content +3. **Create the file** - Use the create-justification script to generate the file + +## Usage + +The script will automatically: +- Generate a unique three-word ID (e.g., `swift-emerald-river`) +- Create frontmatter with current date and formatted title +- Save the file as `{id}-{slug}.md` in `.agents/justifications/` + +## Creating the File + +### Option 1: Direct Content + +If you have the content ready, run: + +```bash +npx tsx scripts/create-justification.ts "$ARGUMENTS" "Your justification content here" +``` + +### Option 2: Multi-line Content (Heredoc) + +For multi-line content, use heredoc: + +```bash +npx tsx scripts/create-justification.ts "$ARGUMENTS" << HEREDOC +Your multi-line +justification content +goes here +HEREDOC +``` + +### Option 3: Pipe Content + +You can also pipe content: + +```bash +echo "Your content" | npx tsx scripts/create-justification.ts "$ARGUMENTS" +``` + +## File Format + +The created file will have: + +```markdown +--- +date: 2026-01-13 +title: Justification Title +--- + +Your content here +``` + +The title is automatically formatted from the slug (e.g., `architecture-decision` → `Architecture Decision`). + +## Guidelines + +- Use descriptive slugs in kebab-case (e.g., `tech-stack-choice`, `api-design-rationale`) +- Include clear reasoning and context for the decision +- The unique ID ensures no filename conflicts +- Files are automatically dated for organization + +## Begin + +Create a justification file using the slug from `$ARGUMENTS` and appropriate content documenting the reasoning or justification. diff --git a/.opencode/commands/create-plan.md b/.opencode/commands/create-plan.md new file mode 100644 index 000000000..cde144440 --- /dev/null +++ b/.opencode/commands/create-plan.md @@ -0,0 +1,76 @@ +--- +description: Create a new plan file in .agents/plans/ +argument-hint: [content] +--- + +You are creating a new plan file in the `.agents/plans/` directory. + +## Your Task + +1. **Determine the slug** - Use `$ARGUMENTS` as the file slug (kebab-case recommended) +2. **Gather content** - Collect or generate the plan content +3. **Create the file** - Use the create-plan script to generate the file + +## Usage + +The script will automatically: + +- Generate a unique three-word ID (e.g., `happy-blue-moon`) +- Create frontmatter with current date and formatted title +- Save the file as `{id}-{slug}.md` in `.agents/plans/` + +## Creating the File + +### Option 1: Direct Content + +If you have the content ready, run: + +```bash +npx tsx scripts/create-plan.ts "$ARGUMENTS" "Your plan content here" +``` + +### Option 2: Multi-line Content (Heredoc) + +For multi-line content, use heredoc: + +```bash +npx tsx scripts/create-plan.ts "$ARGUMENTS" << HEREDOC +Your multi-line +plan content +goes here +HEREDOC +``` + +### Option 3: Pipe Content + +You can also pipe content: + +```bash +echo "Your content" | npx tsx scripts/create-plan.ts "$ARGUMENTS" +``` + +## File Format + +The created file will have: + +```markdown +--- +date: 2026-01-13 +title: Plan Title +--- + +Your content here +``` + +The title is automatically formatted from the slug (e.g., `my-feature` → `My Feature`). + +## Guidelines + +- Use descriptive slugs in kebab-case (e.g., `user-authentication`, `api-integration`) +- Include clear, actionable plan content +- The unique ID ensures no filename conflicts +- Files are automatically dated for organization + +## Begin + +Create a plan file using the slug from `$ARGUMENTS` and appropriate content for the planning task. diff --git a/.opencode/commands/create-scratch.md b/.opencode/commands/create-scratch.md new file mode 100644 index 000000000..1f4138944 --- /dev/null +++ b/.opencode/commands/create-scratch.md @@ -0,0 +1,75 @@ +--- +description: Create a new scratch file in .agents/scratches/ +argument-hint: [content] +--- + +You are creating a new scratch file in the `.agents/scratches/` directory. + +## Your Task + +1. **Determine the slug** - Use `$ARGUMENTS` as the file slug (kebab-case recommended) +2. **Gather content** - Collect or generate the scratch content +3. **Create the file** - Use the create-scratch script to generate the file + +## Usage + +The script will automatically: +- Generate a unique three-word ID (e.g., `calm-teal-cloud`) +- Create frontmatter with current date and formatted title +- Save the file as `{id}-{slug}.md` in `.agents/scratches/` + +## Creating the File + +### Option 1: Direct Content + +If you have the content ready, run: + +```bash +npx tsx scripts/create-scratch.ts "$ARGUMENTS" "Your scratch content here" +``` + +### Option 2: Multi-line Content (Heredoc) + +For multi-line content, use heredoc: + +```bash +npx tsx scripts/create-scratch.ts "$ARGUMENTS" << HEREDOC +Your multi-line +scratch content +goes here +HEREDOC +``` + +### Option 3: Pipe Content + +You can also pipe content: + +```bash +echo "Your content" | npx tsx scripts/create-scratch.ts "$ARGUMENTS" +``` + +## File Format + +The created file will have: + +```markdown +--- +date: 2026-01-13 +title: Scratch Title +--- + +Your content here +``` + +The title is automatically formatted from the slug (e.g., `quick-notes` → `Quick Notes`). + +## Guidelines + +- Use descriptive slugs in kebab-case (e.g., `exploration-ideas`, `temporary-notes`) +- Scratch files are for temporary notes, explorations, or ideas +- The unique ID ensures no filename conflicts +- Files are automatically dated for organization + +## Begin + +Create a scratch file using the slug from `$ARGUMENTS` and appropriate content for notes or exploration. diff --git a/.opencode/commands/document.md b/.opencode/commands/document.md new file mode 100644 index 000000000..72ca40259 --- /dev/null +++ b/.opencode/commands/document.md @@ -0,0 +1,201 @@ +--- +description: Generate MDX documentation for a module or feature +argument-hint: +--- + +You are creating proper MDX documentation for a module or feature in Documenso using Nextra. + +## Your Task + +1. **Identify the scope** - What does `$ARGUMENTS` refer to? (file, directory, or feature name) +2. **Read the source code** - Understand the public API, types, and behavior +3. **Read existing docs** - Check if there's documentation to update or reference +4. **Write comprehensive documentation** - Create or update MDX docs in the appropriate location +5. **Update navigation** - Add entry to `_meta.js` if creating a new page + +## Documentation Structure + +Create documentation in the appropriate location: + +- **Developer docs**: `apps/documentation/pages/developers/` +- **User docs**: `apps/documentation/pages/users/` + +### File Format + +All documentation files must be `.mdx` files with frontmatter: + +```mdx +--- +title: Page Title +description: Brief description for SEO and meta tags +--- + +# Page Title + +Content starts here... +``` + +### Navigation + +Each directory should have a `_meta.js` file that defines the navigation structure: + +```javascript +export default { + index: 'Introduction', + 'feature-name': 'Feature Name', + 'another-feature': 'Another Feature', +}; +``` + +If creating a new page, add it to the appropriate `_meta.js` file. + +### Documentation Format + +````mdx +--- +title: +description: Brief description of what this does and when to use it +--- + +# + +Brief description of what this module/feature does and when to use it. + +## Installation + +If there are specific packages or imports needed: + +```bash +npm install @documenso/package-name +``` + +## Quick Start + +```jsx +// Minimal working example +import { Component } from '@documenso/package'; + +const Example = () => { + return ; +}; +``` + +## API Reference + +### Component/Function Name + +Description of what it does. + +#### Props/Parameters + +| Prop/Param | Type | Description | +| ---------- | -------------------- | ------------------------- | +| prop | `string` | Description of the prop | +| optional | `boolean` (optional) | Optional prop description | + +#### Example + +```jsx +import { Component } from '@documenso/package'; + +; +``` + +### Types + +#### `TypeName` + +```typescript +type TypeName = { + property: string; + optional?: boolean; +}; +``` + +## Examples + +### Common Use Case + +```jsx +// Full working example +``` + +### Advanced Usage + +```jsx +// More complex example +``` + +## Related + +- [Link to related documentation](/developers/path) +- [Another related page](/users/path) +```` + +## Guidelines + +### Content Quality + +- **Be accurate** - Verify behavior by reading the code +- **Be complete** - Document all public API surface +- **Be practical** - Include real, working examples +- **Be concise** - Don't over-explain obvious things +- **Be user-focused** - Write for the target audience (developers or users) + +### Code Examples + +- Use appropriate language tags: `jsx`, `tsx`, `typescript`, `bash`, `json` +- Show imports when not obvious +- Include expected output in comments where helpful +- Progress from simple to complex +- Use real examples from the codebase when possible + +### Formatting + +- Always include frontmatter with `title` and `description` +- Use proper markdown headers (h1 for title, h2 for sections) +- Use tables for props/parameters documentation (matching existing style) +- Use code fences with appropriate language tags +- Use Nextra components when appropriate: + - `` for notes + - `` for step-by-step instructions +- Use relative links for internal documentation (e.g., `/developers/embedding/react`) + +### Nextra Components + +You can import and use Nextra components: + +```jsx +import { Callout, Steps } from 'nextra/components'; + + + This is an informational note. + + + + First step + Second step + +``` + +### Maintenance + +- Include types inline so docs don't get stale +- Reference source file locations for complex behavior +- Keep examples up-to-date with the codebase +- Update `_meta.js` when adding new pages + +## Process + +1. **Explore the code** - Read source files to understand the API +2. **Identify the audience** - Is this for developers or users? +3. **Check existing docs** - Look for similar pages to match style +4. **Draft the structure** - Outline sections before writing +5. **Write content** - Fill in each section with frontmatter +6. **Add examples** - Create working code samples +7. **Update navigation** - Add to `_meta.js` if needed +8. **Review** - Read through for clarity and accuracy + +## Begin + +Analyze `$ARGUMENTS`, read the relevant source code, check existing documentation patterns, and create comprehensive MDX documentation following the Documenso documentation style. diff --git a/.opencode/commands/implement.md b/.opencode/commands/implement.md new file mode 100644 index 000000000..e9ac84f8c --- /dev/null +++ b/.opencode/commands/implement.md @@ -0,0 +1,100 @@ +--- +description: Implement a spec from the plans directory +argument-hint: +--- + +You are implementing a specification from the `.agents/plans/` directory. Work autonomously until the feature is complete and tests pass. + +## Your Task + +1. **Read the spec** at `$ARGUMENTS` +2. **Read CODE_STYLE.md** for formatting conventions +3. **Plan the implementation** using the TodoWrite tool to break down the work +4. **Implement the feature** following the spec and code style +5. **Write E2E tests** only for non-trivial functionality (E2E tests are time-consuming) +6. **Run tests** and fix any failures +7. **Run typecheck and lint** and fix any issues + +## Implementation Guidelines + +### Before Coding + +- Understand the spec's goals and scope +- Identify the desired API from usage examples in the spec +- Review related existing code to understand patterns +- Break the work into discrete tasks using TodoWrite + +### During Implementation + +- Follow CODE_STYLE.md strictly (2-space indent, double quotes, braces always, etc.) +- Follow workspace rules for TypeScript, React, TRPC patterns, and Remix conventions +- Mark todos complete as you finish each task +- Commit logical chunks of work + +### Code Quality + +- No stubbed implementations +- Handle edge cases and error conditions +- Include descriptive error messages with context +- Use async/await for all I/O operations +- Use AppError class when throwing errors +- Use Zod for validation and react-hook-form for forms + +### Testing + +**Important**: E2E tests are time-consuming. Only write tests for non-trivial functionality. + +- Write E2E tests in `packages/app-tests/e2e/` using Playwright +- Test critical user flows and edge cases +- Follow existing E2E test patterns in the codebase +- Use descriptive test names that explain what is being tested +- Skip tests for trivial changes (simple UI tweaks, minor refactors, etc.) + +## Autonomous Workflow + +Work continuously through these steps: + +1. **Implement** - Write the code for the current task +2. **Typecheck** - Run `npm run typecheck -w @documenso/remix` to verify types +3. **Lint** - Run `npm run lint:fix` to fix linting issues +4. **Test** - If non-trivial, run E2E tests: `npm run test:dev -w @documenso/app-tests` +5. **Fix** - If tests fail, fix and re-run +6. **Repeat** - Move to next task + +## Stopping Conditions + +**Stop and report success when:** + +- All spec requirements are implemented +- Typecheck passes +- Lint passes +- E2E tests pass (if written for non-trivial functionality) + +**Stop and ask for help when:** + +- The spec is ambiguous and you need clarification +- You encounter a blocking issue you cannot resolve +- You need to make a decision that significantly deviates from the spec +- External dependencies are missing + +## Commands + +```bash +# Type checking +npm run typecheck -w @documenso/remix + +# Linting +npm run lint:fix + +# E2E Tests (only for non-trivial work) +npm run test:dev -w @documenso/app-tests # Run E2E tests in dev mode +npm run test-ui:dev -w @documenso/app-tests # Run E2E tests with UI +npm run test:e2e # Run full E2E test suite + +# Development +npm run dev # Start dev server +``` + +## Begin + +Read the spec file and CODE_STYLE.md, then start implementing. Use TodoWrite to track your progress throughout. diff --git a/.opencode/commands/interview.md b/.opencode/commands/interview.md new file mode 100644 index 000000000..e84e8f37d --- /dev/null +++ b/.opencode/commands/interview.md @@ -0,0 +1,57 @@ +--- +description: Deep-dive interview to flesh out a spec or design document +agent: build +argument-hint: +--- + +You are conducting a thorough interview to help flesh out and complete a specification or design document. + +## Your Task + +1. **Read the document** at `$ARGUMENTS` +2. **Analyze it deeply** - identify gaps, ambiguities, unexplored edge cases, and areas needing clarification +3. **Interview the user** by providing a question with some pre-determined options +4. **Write the completed spec** back to the file when the interview is complete + +## Interview Guidelines + +### Question Quality +- Ask **non-obvious, insightful questions** - avoid surface-level queries +- Focus on: technical implementation details, architectural decisions, edge cases, error handling, UX implications, security considerations, performance tradeoffs, integration points, migration strategies, rollback plans +- Each question should reveal something that would otherwise be missed +- Challenge assumptions embedded in the document +- Explore second and third-order consequences of design decisions +- Use the Web Search and other tools where required to ground questions (e.g. package recommendations) + +### Question Strategy +- Start by identifying the 3-5 most critical unknowns or ambiguities +- Use the AskUserQuestion tool with well-crafted options that represent real tradeoffs +- When appropriate, offer multiple valid approaches with their pros/cons as options +- Don't ask about things that are already clearly specified +- Probe deeper when answers reveal new areas of uncertainty + +### Topics to Explore (as relevant) +- **Technical**: Data models, API contracts, state management, concurrency, caching, validation +- **UX**: Error states, loading states, empty states, edge cases, accessibility, mobile considerations +- **Operations**: Deployment, monitoring, alerting, debugging, logging, feature flags +- **Security**: Auth, authz, input validation, rate limiting, audit trails +- **Scale**: Performance bottlenecks, data growth, traffic spikes, graceful degradation +- **Integration**: Dependencies, backwards compatibility, versioning, migration path +- **Failure modes**: What happens when X fails? How do we recover? What's the blast radius? + +### Interview Flow +1. Ask 2-4 questions at a time (use multiple questions in one when they're related) +2. After each round, incorporate answers and identify follow-up questions +3. Continue until all critical areas are addressed +4. Signal when you believe the interview is complete, but offer to go deeper + +## Output + +When the interview is complete: +1. Synthesize all gathered information +2. Rewrite/expand the original document with the new details +3. Preserve the document's original structure where sensible, but reorganize if needed +4. Add new sections for areas that weren't originally covered +5. Write the completed spec back to `$ARGUMENTS` + +Begin by reading the file and identifying your first set of deep questions. diff --git a/.opencode/skill/create-justification/SKILL.md b/.opencode/skill/create-justification/SKILL.md new file mode 100644 index 000000000..78a2aaea9 --- /dev/null +++ b/.opencode/skill/create-justification/SKILL.md @@ -0,0 +1,56 @@ +--- +name: create-justification +description: Create a new justification file in .agents/justifications/ with a unique three-word ID, frontmatter, and formatted title +license: MIT +compatibility: opencode +metadata: + audience: agents + workflow: decision-making +--- + +## What I do + +I help you create new justification files in the `.agents/justifications/` directory. Each justification file gets: + +- A unique three-word identifier (e.g., `swift-emerald-river`) +- Frontmatter with the current date and formatted title +- Content you provide + +## How to use + +Run the script with a slug and content: + +```bash +npx tsx scripts/create-justification.ts "decision-name" "Justification content here" +``` + +Or use heredoc for multi-line content: + +```bash +npx tsx scripts/create-justification.ts "decision-name" << HEREDOC +Multi-line +justification content +goes here +HEREDOC +``` + +## File format + +Files are created as: `{three-word-id}-{slug}.md` + +Example: `swift-emerald-river-decision-name.md` + +The file includes frontmatter: + +```markdown +--- +date: 2026-01-13 +title: Decision Name +--- + +Your content here +``` + +## When to use me + +Use this skill when you need to document the reasoning or justification for a decision, approach, or architectural choice. The unique ID ensures no filename conflicts, and the frontmatter provides metadata for organization. diff --git a/.opencode/skill/create-plan/SKILL.md b/.opencode/skill/create-plan/SKILL.md new file mode 100644 index 000000000..8ceb2ef8c --- /dev/null +++ b/.opencode/skill/create-plan/SKILL.md @@ -0,0 +1,56 @@ +--- +name: create-plan +description: Create a new plan file in .agents/plans/ with a unique three-word ID, frontmatter, and formatted title +license: MIT +compatibility: opencode +metadata: + audience: agents + workflow: planning +--- + +## What I do + +I help you create new plan files in the `.agents/plans/` directory. Each plan file gets: + +- A unique three-word identifier (e.g., `happy-blue-moon`) +- Frontmatter with the current date and formatted title +- Content you provide + +## How to use + +Run the script with a slug and content: + +```bash +npx tsx scripts/create-plan.ts "feature-name" "Plan content here" +``` + +Or use heredoc for multi-line content: + +```bash +npx tsx scripts/create-plan.ts "feature-name" << HEREDOC +Multi-line +plan content +goes here +HEREDOC +``` + +## File format + +Files are created as: `{three-word-id}-{slug}.md` + +Example: `happy-blue-moon-feature-name.md` + +The file includes frontmatter: + +```markdown +--- +date: 2026-01-13 +title: Feature Name +--- + +Your content here +``` + +## When to use me + +Use this skill when you need to create a new plan document for a feature, task, or project. The unique ID ensures no filename conflicts, and the frontmatter provides metadata for organization. diff --git a/.opencode/skill/create-scratch/SKILL.md b/.opencode/skill/create-scratch/SKILL.md new file mode 100644 index 000000000..e44e4779d --- /dev/null +++ b/.opencode/skill/create-scratch/SKILL.md @@ -0,0 +1,56 @@ +--- +name: create-scratch +description: Create a new scratch file in .agents/scratches/ with a unique three-word ID, frontmatter, and formatted title +license: MIT +compatibility: opencode +metadata: + audience: agents + workflow: exploration +--- + +## What I do + +I help you create new scratch files in the `.agents/scratches/` directory. Each scratch file gets: + +- A unique three-word identifier (e.g., `calm-teal-cloud`) +- Frontmatter with the current date and formatted title +- Content you provide + +## How to use + +Run the script with a slug and content: + +```bash +npx tsx scripts/create-scratch.ts "note-name" "Scratch content here" +``` + +Or use heredoc for multi-line content: + +```bash +npx tsx scripts/create-scratch.ts "note-name" << HEREDOC +Multi-line +scratch content +goes here +HEREDOC +``` + +## File format + +Files are created as: `{three-word-id}-{slug}.md` + +Example: `calm-teal-cloud-note-name.md` + +The file includes frontmatter: + +```markdown +--- +date: 2026-01-13 +title: Note Name +--- + +Your content here +``` + +## When to use me + +Use this skill when you need to create a temporary note, exploration document, or scratch pad for ideas. The unique ID ensures no filename conflicts, and the frontmatter provides metadata for organization. diff --git a/AGENTS.md b/AGENTS.md index b6fba867d..bf92317f2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,6 +11,8 @@ - `npm run format` - Format code with Prettier - `npm run dev` - Start development server for Remix app +**Important:** Do not run `npm run build` to verify changes unless explicitly asked. Builds take a long time (~2 minutes). Use `npx tsc --noEmit` for type checking specific packages if needed. + ## Code Style Guidelines - Use TypeScript for all code; prefer `type` over `interface` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fe2bb8304..501458cd4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,3 +52,53 @@ You can build the project with: ```bash npm run build ``` + +## AI-Assisted Development with OpenCode + +We use [OpenCode](https://opencode.ai) for AI-assisted development. OpenCode provides custom commands and skills to help maintain consistency and streamline common workflows. + +OpenCode works with most major AI providers (Anthropic, OpenAI, Google, etc.) or you can use [Zen](https://opencode.ai/zen) for optimized coding models. Configure your preferred provider in the OpenCode settings. + +> **Important**: All AI-generated code must be thoroughly reviewed by the contributor before submitting a PR. You are responsible for understanding and validating every line of code you submit. If we detect that contributors are simply throwing AI-generated code over the wall without proper review, they will be blocked from the repository. + +### Getting Started + +1. Install OpenCode (see [opencode.ai](https://opencode.ai) for other install methods): + ```bash + curl -fsSL https://opencode.ai/install | bash + ``` +2. Configure your AI provider (or use Zen for optimized models) +3. Run `opencode` in the project root + +### Available Commands + +Use these commands in OpenCode by typing the command name: + +| Command | Description | +| ------------------------------ | -------------------------------------------------------- | +| `/implement ` | Implement a spec from `.agents/plans/` autonomously | +| `/continue ` | Continue implementing a spec from a previous session | +| `/interview ` | Deep-dive interview to flesh out a spec or design | +| `/document ` | Generate MDX documentation for a module or feature | +| `/commit` | Create a conventional commit for staged changes | +| `/create-plan ` | Create a new plan file in `.agents/plans/` | +| `/create-scratch ` | Create a scratch file for notes in `.agents/scratches/` | +| `/create-justification ` | Create a justification file in `.agents/justifications/` | + +### Typical Workflow + +1. **Create a plan**: Use `/create-plan my-feature` to draft a spec for a new feature +2. **Flesh out the spec**: Use `/interview .agents/plans/.md` to refine requirements +3. **Implement**: Use `/implement .agents/plans/.md` to build the feature +4. **Continue if needed**: Use `/continue .agents/plans/.md` to pick up where you left off +5. **Commit**: Use `/commit` to create a conventional commit + +### Agent Files + +The `.agents/` directory stores AI-generated artifacts: + +- **`.agents/plans/`** - Feature specs and implementation plans +- **`.agents/scratches/`** - Temporary notes and explorations +- **`.agents/justifications/`** - Decision rationale and technical justifications + +These files use a unique ID format (`{word}-{word}-{word}-{slug}.md`) to prevent conflicts. diff --git a/apps/remix/app/components/dialogs/passkey-create-dialog.tsx b/apps/remix/app/components/dialogs/passkey-create-dialog.tsx index 77bd54b57..541d2d4c7 100644 --- a/apps/remix/app/components/dialogs/passkey-create-dialog.tsx +++ b/apps/remix/app/components/dialogs/passkey-create-dialog.tsx @@ -73,7 +73,9 @@ export const PasskeyCreateDialog = ({ trigger, onSuccess, ...props }: PasskeyCre try { const passkeyRegistrationOptions = await createPasskeyRegistrationOptions(); - const registrationResult = await startRegistration(passkeyRegistrationOptions); + const registrationResult = await startRegistration({ + optionsJSON: passkeyRegistrationOptions, + }); await createPasskey({ passkeyName, diff --git a/apps/remix/app/components/dialogs/sign-field-checkbox-dialog.tsx b/apps/remix/app/components/dialogs/sign-field-checkbox-dialog.tsx index 4d4b02f8e..b867ad947 100644 --- a/apps/remix/app/components/dialogs/sign-field-checkbox-dialog.tsx +++ b/apps/remix/app/components/dialogs/sign-field-checkbox-dialog.tsx @@ -82,9 +82,7 @@ export const SignFieldCheckboxDialog = createCallable< (!value ? call.end(null) : null)}> - - Sign Checkbox Field - + {fieldMeta.label || Select Options} { field.onChange({ @@ -154,7 +152,7 @@ export const SignFieldCheckboxDialog = createCallable< />