AI-assisted software architecture documentation
Keystone is an open-source Electron desktop application that streamlines the creation and maintenance of software architecture documentation through conversational AI. Generate, refine, and maintain PRDs (Product Requirements Documents), TDDs (Technical Design Documents), and ADRs (Architecture Decision Records) with AI assistance.
- Multi-threaded AI Conversations: Maintain parallel conversation threads with full context persistence
- Side-by-Side Workspace: Conversations and documents live together in a unified interface
- Highlight-to-Interact: Select any document section to inquire or refine
- ADR Generation: Automatic architecture decision records when pivoting decisions
- Provider Agnostic: Works with OpenAI, Google Gemini, and Anthropic Claude
- Node.js
- pnpm
- Git
# Clone the repository
git clone https://github.com/dortort/keystone.git
cd keystone
# Install dependencies
pnpm install
# Start development server
pnpm dev# Development mode with hot reload
pnpm dev
# Build for production
pnpm build
# Run production build
pnpm start| Tool | Purpose |
|---|---|
| VS Code / Cursor | Primary IDE |
| ESLint | Code linting |
| Prettier | Code formatting |
| TypeScript | Type checking |
- ESLint
- Prettier - Code formatter
- Tailwind CSS IntelliSense
- GitLens
Create a .env.local file in the project root:
# Optional: API key fallbacks (subscription auth preferred)
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GOOGLE_API_KEY=...We follow trunk-based development with short-lived feature branches.
| Type | Pattern | Example |
|---|---|---|
| Feature | feat/<description> |
feat/add-adr-generation |
| Bug fix | fix/<description> |
fix/thread-context-loss |
| Chore | chore/<description> |
chore/update-deps |
| Docs | docs/<description> |
docs/api-reference |
| Refactor | refactor/<description> |
refactor/orchestrator |
| CI/CD | ci/<description> |
ci/add-e2e-tests |
main
│
├── feat/add-adr-generation (short-lived, < 2 days)
│ └── PR → main
│
└── fix/thread-context-loss (short-lived)
└── PR → main
- Create branch from
main:git checkout -b feat/my-feature - Commit frequently with conventional commits
- Open PR early (draft if WIP)
- Keep branches short-lived (< 2 days ideally)
- Rebase before merge to maintain linear history
We use Conventional Commits:
<type>(<scope>): <description>
[optional body]
[optional footer(s)]
Types:
feat: New featurefix: Bug fixdocs: Documentation onlystyle: Formatting (no code change)refactor: Code change that neither fixes a bug nor adds a featuretest: Adding testschore: Build process or auxiliary tool changes
Examples:
git commit -m "feat(orchestrator): add decision pivot detection"
git commit -m "fix(ui): resolve thread list scroll issue"
git commit -m "docs: update API reference"| Workflow | Trigger | Purpose |
|---|---|---|
| CI | Push, PR to main |
Lint, type-check, test |
| Build | Push to main |
Build & package for all platforms |
| Release | Tag v* |
Create GitHub release with artifacts |
# Runs on every push and pull request
- Checkout code
- Setup Node.js + pnpm
- Install dependencies
- Run linting (ESLint)
- Run type checking (tsc)
- Run unit tests (Vitest)
- Run integration testsAll PRs to main must pass:
- ✅ Linting (
pnpm lint) - ✅ Type checking (
pnpm typecheck) - ✅ Unit tests (
pnpm test) - ✅ Build succeeds (
pnpm build)
main branch is protected with:
- Require PR reviews (1 minimum)
- Require status checks to pass
- Require linear history (rebase merging)
- No force pushes
- Strict mode enabled — No
anytypes without justification - Prefer interfaces over type aliases for object shapes
- Use
readonlyfor immutable properties - Explicit return types for exported functions
// ✅ Good
interface ThreadConfig {
readonly id: string;
maxMessages: number;
}
export function createThread(config: ThreadConfig): Thread {
// ...
}
// ❌ Avoid
export const createThread = (config: any) => {
// ...
}- Functional components with hooks
- Named exports for components
- Props interface named
<Component>Props - Colocate styles, tests, and types
// ✅ Good
interface ThreadListProps {
threads: Thread[];
onSelect: (id: string) => void;
}
export function ThreadList({ threads, onSelect }: ThreadListProps) {
return (
// ...
);
}src/
├── main/ # Electron main process
│ ├── ipc/ # IPC handlers
│ ├── services/ # Business logic
│ └── index.ts
├── renderer/ # React frontend
│ ├── components/ # Reusable UI components
│ ├── features/ # Feature-based modules
│ │ ├── conversation/
│ │ └── document/
│ ├── hooks/ # Custom React hooks
│ ├── stores/ # Zustand stores
│ └── App.tsx
├── shared/ # Shared types and utilities
│ ├── types/
│ └── utils/
└── agents/ # AI agent implementations
├── orchestrator/
└── specialists/
| Element | Convention | Example |
|---|---|---|
| Files (components) | PascalCase | ThreadList.tsx |
| Files (utilities) | camelCase | formatDate.ts |
| Files (types) | PascalCase | Thread.ts |
| Interfaces | PascalCase | ThreadConfig |
| Functions | camelCase | createThread |
| Constants | SCREAMING_SNAKE | MAX_THREADS |
| CSS classes | kebab-case | thread-list-item |
// 1. Built-in Node modules
import path from 'path';
// 2. External packages
import React from 'react';
import { useStore } from 'zustand';
// 3. Internal aliases (@/)
import { Thread } from '@/shared/types';
import { formatDate } from '@/shared/utils';
// 4. Relative imports
import { ThreadItem } from './ThreadItem';
import styles from './ThreadList.module.css';| Type | Tool | Location |
|---|---|---|
| Unit | Vitest | *.test.ts colocated |
| Component | Vitest + Testing Library | *.test.tsx colocated |
| E2E | Playwright | e2e/ directory |
# Run all unit tests
pnpm test
# Run tests in watch mode
pnpm test:watch
# Run tests with coverage
pnpm test:coverage
# Run E2E tests
pnpm test:e2edescribe('ThreadList', () => {
it('should render all threads', () => {
// ...
});
it('should call onSelect when thread is clicked', () => {
// ...
});
});pnpm dev # Start development server
pnpm build # Build for production
pnpm start # Run production build
pnpm lint # Run ESLint
pnpm lint:fix # Fix ESLint issues
pnpm typecheck # Run TypeScript type check
pnpm test # Run unit tests
pnpm test:watch # Run tests in watch mode
pnpm test:e2e # Run E2E tests
pnpm format # Format code with Prettier- Fork the repository
- Create a feature branch (
feat/amazing-feature) - Commit your changes using conventional commits
- Push to your fork
- Open a Pull Request
Please ensure your PR:
- Passes all CI checks
- Includes tests for new functionality
- Updates documentation as needed
- Follows the coding conventions above
This project is open source. See LICENSE for details.