docs: add OpenCode AI-assisted development guide (#2384)

Adds OpenCode support for AI-assisted development, including custom
commands and skills to help contributors maintain consistency and
streamline common workflows.

#### Changes
- Added "AI-Assisted Development with OpenCode" section to
CONTRIBUTING.md with:
  - Installation instructions and provider configuration
- Documentation for 8 custom commands (/implement, /continue,
/interview, /document, /commit, /create-plan, /create-scratch,
/create-justification)
  - Typical workflow guide
- Clear policy that AI-generated code must be reviewed before submission
- Added .agents/ directory for plans, scratches, and justifications
- Added .opencode/ commands and skills for the agent
- Added helper scripts for creating agent files
This commit is contained in:
Lucas Smith 2026-01-14 10:10:20 +11:00 committed by GitHub
parent db913e95b6
commit 34f512bd55
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 1300 additions and 0 deletions

View file

0
.agents/plans/.gitkeep Normal file
View file

View file

View file

@ -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**: `<type>: <description>`
- 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
```
<type>[scope]: <subject>
[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.

View file

@ -0,0 +1,112 @@
---
description: Continue implementing a spec from a previous session
argument-hint: <spec-file-path>
---
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.

View file

@ -0,0 +1,75 @@
---
description: Create a new justification file in .agents/justifications/
argument-hint: <justification-slug> [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.

View file

@ -0,0 +1,76 @@
---
description: Create a new plan file in .agents/plans/
argument-hint: <plan-slug> [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.

View file

@ -0,0 +1,75 @@
---
description: Create a new scratch file in .agents/scratches/
argument-hint: <scratch-slug> [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.

View file

@ -0,0 +1,201 @@
---
description: Generate MDX documentation for a module or feature
argument-hint: <module-path-or-feature>
---
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: <Module|Feature Name>
description: Brief description of what this does and when to use it
---
# <Module|Feature Name>
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 <Component />;
};
```
## 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';
<Component prop="value" optional={true} />;
```
### 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:
- `<Callout type="info">` for notes
- `<Steps>` 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';
<Callout type="info">
This is an informational note.
</Callout>
<Steps>
<Steps.Step>First step</Steps.Step>
<Steps.Step>Second step</Steps.Step>
</Steps>
```
### 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.

View file

@ -0,0 +1,100 @@
---
description: Implement a spec from the plans directory
argument-hint: <spec-file-path>
---
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.

View file

@ -0,0 +1,57 @@
---
description: Deep-dive interview to flesh out a spec or design document
agent: build
argument-hint: <file-path>
---
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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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 <spec-path>` | Implement a spec from `.agents/plans/` autonomously |
| `/continue <spec-path>` | Continue implementing a spec from a previous session |
| `/interview <file-path>` | Deep-dive interview to flesh out a spec or design |
| `/document <module-path>` | Generate MDX documentation for a module or feature |
| `/commit` | Create a conventional commit for staged changes |
| `/create-plan <slug>` | Create a new plan file in `.agents/plans/` |
| `/create-scratch <slug>` | Create a scratch file for notes in `.agents/scratches/` |
| `/create-justification <slug>` | 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/<file>.md` to refine requirements
3. **Implement**: Use `/implement .agents/plans/<file>.md` to build the feature
4. **Continue if needed**: Use `/continue .agents/plans/<file>.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.

View file

@ -0,0 +1,74 @@
#!/usr/bin/env node
import { readFileSync } from 'fs';
import { mkdirSync, writeFileSync } from 'fs';
import { join } from 'path';
import { generateId } from './utils/generate-id';
const JUSTIFICATIONS_DIR = join(process.cwd(), '.agents', 'justifications');
const main = () => {
const args = process.argv.slice(2);
if (args.length === 0) {
console.error('Usage: npx tsx scripts/create-justification.ts "file-slug" [content]');
console.error(' or: npx tsx scripts/create-justification.ts "file-slug" << HEREDOC');
process.exit(1);
}
const slug = args[0];
let content = '';
// Check if content is provided as second argument
if (args.length > 1) {
content = args.slice(1).join(' ');
} else {
// Read from stdin (heredoc)
try {
const stdin = readFileSync(0, 'utf-8');
content = stdin.trim();
} catch (error) {
console.error('Error reading from stdin:', error);
process.exit(1);
}
}
if (!content) {
console.error('Error: No content provided');
process.exit(1);
}
// Generate unique ID
const id = generateId();
const filename = `${id}-${slug}.md`;
const filepath = join(JUSTIFICATIONS_DIR, filename);
// Format title from slug (kebab-case to Title Case)
const title = slug
.split('-')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
// Get current date in ISO format
const date = new Date().toISOString().split('T')[0];
// Create frontmatter
const frontmatter = `---
date: ${date}
title: ${title}
---
`;
// Ensure directory exists
mkdirSync(JUSTIFICATIONS_DIR, { recursive: true });
// Write file with frontmatter
writeFileSync(filepath, frontmatter + content, 'utf-8');
console.log(`Created justification: ${filepath}`);
console.log(`ID: ${id}`);
console.log(`Filename: ${filename}`);
};
main();

74
scripts/create-plan.ts Normal file
View file

@ -0,0 +1,74 @@
#!/usr/bin/env node
import { readFileSync } from 'fs';
import { mkdirSync, writeFileSync } from 'fs';
import { join } from 'path';
import { generateId } from './utils/generate-id';
const PLANS_DIR = join(process.cwd(), '.agents', 'plans');
const main = () => {
const args = process.argv.slice(2);
if (args.length === 0) {
console.error('Usage: npx tsx scripts/create-plan.ts "file-slug" [content]');
console.error(' or: npx tsx scripts/create-plan.ts "file-slug" << HEREDOC');
process.exit(1);
}
const slug = args[0];
let content = '';
// Check if content is provided as second argument
if (args.length > 1) {
content = args.slice(1).join(' ');
} else {
// Read from stdin (heredoc)
try {
const stdin = readFileSync(0, 'utf-8');
content = stdin.trim();
} catch (error) {
console.error('Error reading from stdin:', error);
process.exit(1);
}
}
if (!content) {
console.error('Error: No content provided');
process.exit(1);
}
// Generate unique ID
const id = generateId();
const filename = `${id}-${slug}.md`;
const filepath = join(PLANS_DIR, filename);
// Format title from slug (kebab-case to Title Case)
const title = slug
.split('-')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
// Get current date in ISO format
const date = new Date().toISOString().split('T')[0];
// Create frontmatter
const frontmatter = `---
date: ${date}
title: ${title}
---
`;
// Ensure directory exists
mkdirSync(PLANS_DIR, { recursive: true });
// Write file with frontmatter
writeFileSync(filepath, frontmatter + content, 'utf-8');
console.log(`Created plan: ${filepath}`);
console.log(`ID: ${id}`);
console.log(`Filename: ${filename}`);
};
main();

74
scripts/create-scratch.ts Normal file
View file

@ -0,0 +1,74 @@
#!/usr/bin/env node
import { readFileSync } from 'fs';
import { mkdirSync, writeFileSync } from 'fs';
import { join } from 'path';
import { generateId } from './utils/generate-id';
const SCRATCHES_DIR = join(process.cwd(), '.agents', 'scratches');
const main = () => {
const args = process.argv.slice(2);
if (args.length === 0) {
console.error('Usage: npx tsx scripts/create-scratch.ts "file-slug" [content]');
console.error(' or: npx tsx scripts/create-scratch.ts "file-slug" << HEREDOC');
process.exit(1);
}
const slug = args[0];
let content = '';
// Check if content is provided as second argument
if (args.length > 1) {
content = args.slice(1).join(' ');
} else {
// Read from stdin (heredoc)
try {
const stdin = readFileSync(0, 'utf-8');
content = stdin.trim();
} catch (error) {
console.error('Error reading from stdin:', error);
process.exit(1);
}
}
if (!content) {
console.error('Error: No content provided');
process.exit(1);
}
// Generate unique ID
const id = generateId();
const filename = `${id}-${slug}.md`;
const filepath = join(SCRATCHES_DIR, filename);
// Format title from slug (kebab-case to Title Case)
const title = slug
.split('-')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
// Get current date in ISO format
const date = new Date().toISOString().split('T')[0];
// Create frontmatter
const frontmatter = `---
date: ${date}
title: ${title}
---
`;
// Ensure directory exists
mkdirSync(SCRATCHES_DIR, { recursive: true });
// Write file with frontmatter
writeFileSync(filepath, frontmatter + content, 'utf-8');
console.log(`Created scratch: ${filepath}`);
console.log(`ID: ${id}`);
console.log(`Filename: ${filename}`);
};
main();

View file

@ -0,0 +1,84 @@
/**
* Generates a unique identifier using three simple words.
* Falls back to unix timestamp if word generation fails.
*/
export const generateId = (): string => {
const adjectives = [
'happy',
'bright',
'swift',
'calm',
'bold',
'clever',
'gentle',
'quick',
'sharp',
'warm',
'cool',
'fresh',
'solid',
'clear',
'sweet',
'wild',
'quiet',
'loud',
'smooth',
];
const nouns = [
'moon',
'star',
'ocean',
'river',
'forest',
'mountain',
'cloud',
'wave',
'stone',
'flower',
'bird',
'wind',
'light',
'shadow',
'fire',
'earth',
'sky',
'tree',
'leaf',
'rock',
];
const colors = [
'blue',
'red',
'green',
'yellow',
'purple',
'orange',
'pink',
'cyan',
'amber',
'emerald',
'violet',
'indigo',
'coral',
'teal',
'gold',
'silver',
'copper',
'bronze',
'ivory',
'jade',
];
try {
const randomAdjective = adjectives[Math.floor(Math.random() * adjectives.length)];
const randomColor = colors[Math.floor(Math.random() * colors.length)];
const randomNoun = nouns[Math.floor(Math.random() * nouns.length)];
return `${randomAdjective}-${randomColor}-${randomNoun}`;
} catch {
// Fallback to unix timestamp if something goes wrong
return Date.now().toString();
}
};