Core features:
- Dependency-aware issue tracking with SQLite backend
- Ready work detection (issues with no open blockers)
- Dependency tree visualization
- Cycle detection and prevention
- Full audit trail
- CLI with colored output
Security and correctness fixes applied:
- Fixed SQL injection vulnerability in UpdateIssue (whitelisted fields)
- Fixed race condition in ID generation (added mutex)
- Fixed cycle detection to return full paths (not just issue IDs)
- Added cycle prevention in AddDependency (validates before commit)
- Added comprehensive input validation (priority, status, types, etc.)
- Fixed N+1 query in GetBlockedIssues (using GROUP_CONCAT)
- Improved query building in GetReadyWork (proper string joining)
- Fixed P0 priority filter bug (using Changed() instead of value check)
All critical and major issues from code review have been addressed.
🤖 Generated with Claude Code
785 lines
21 KiB
Markdown
785 lines
21 KiB
Markdown
# Beads - Dependency-Aware Issue Tracker
|
|
|
|
**Tagline**: "Issues chained together like beads"
|
|
|
|
## Vision
|
|
|
|
A lightweight, standalone issue tracker that makes dependency graphs first-class citizens. The killer feature: automatic detection of "ready work" - issues with no open blockers.
|
|
|
|
**Philosophy**:
|
|
- SQLite by default (zero setup, single binary + database file)
|
|
- PostgreSQL for teams/scale (multi-user, better performance)
|
|
- CLI-first, with clean output for both humans and scripts
|
|
- Dependencies are the core primitive, not an afterthought
|
|
- Full audit trail of all changes
|
|
|
|
---
|
|
|
|
## Core Data Model
|
|
|
|
### Issues
|
|
|
|
```go
|
|
type Issue struct {
|
|
ID string // "bd-1", "bd-2" (beads- prefix)
|
|
Title string // max 500 chars
|
|
Description string // problem statement (what/why)
|
|
Design string // solution design (how)
|
|
AcceptanceCriteria string // definition of done
|
|
Notes string // working notes
|
|
Status Status // open, in_progress, blocked, closed
|
|
Priority int // 0 (highest) to 4 (lowest), default 2
|
|
IssueType IssueType // bug, feature, task, epic, chore
|
|
Assignee string // optional
|
|
EstimatedMinutes *int // optional
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
ClosedAt *time.Time
|
|
}
|
|
|
|
type Status string
|
|
const (
|
|
StatusOpen Status = "open"
|
|
StatusInProgress Status = "in_progress"
|
|
StatusBlocked Status = "blocked"
|
|
StatusClosed Status = "closed"
|
|
)
|
|
|
|
type IssueType string
|
|
const (
|
|
TypeBug IssueType = "bug"
|
|
TypeFeature IssueType = "feature"
|
|
TypeTask IssueType = "task"
|
|
TypeEpic IssueType = "epic"
|
|
TypeChore IssueType = "chore"
|
|
)
|
|
```
|
|
|
|
### Dependencies
|
|
|
|
```go
|
|
type Dependency struct {
|
|
IssueID string // the issue that depends
|
|
DependsOnID string // the issue it depends on
|
|
Type DependencyType // relationship type
|
|
CreatedAt time.Time
|
|
CreatedBy string
|
|
}
|
|
|
|
type DependencyType string
|
|
const (
|
|
DepBlocks DependencyType = "blocks" // hard blocker
|
|
DepRelated DependencyType = "related" // soft relationship
|
|
DepParentChild DependencyType = "parent-child" // epic/subtask
|
|
)
|
|
```
|
|
|
|
### Labels
|
|
|
|
```go
|
|
type Label struct {
|
|
IssueID string
|
|
Label string // freeform tag
|
|
}
|
|
```
|
|
|
|
### Events (Audit Trail)
|
|
|
|
```go
|
|
type Event struct {
|
|
ID int64
|
|
IssueID string
|
|
EventType EventType
|
|
Actor string // who made the change
|
|
OldValue *string // before state (JSON)
|
|
NewValue *string // after state (JSON)
|
|
Comment *string // for comment events
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
type EventType string
|
|
const (
|
|
EventCreated EventType = "created"
|
|
EventUpdated EventType = "updated"
|
|
EventStatusChanged EventType = "status_changed"
|
|
EventCommented EventType = "commented"
|
|
EventClosed EventType = "closed"
|
|
EventReopened EventType = "reopened"
|
|
EventDependencyAdded EventType = "dependency_added"
|
|
EventDependencyRemoved EventType = "dependency_removed"
|
|
EventLabelAdded EventType = "label_added"
|
|
EventLabelRemoved EventType = "label_removed"
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## Backend Abstraction
|
|
|
|
### Storage Interface
|
|
|
|
```go
|
|
// Storage defines the interface for issue storage backends
|
|
type Storage interface {
|
|
// Issues
|
|
CreateIssue(ctx context.Context, issue *Issue, actor string) error
|
|
GetIssue(ctx context.Context, id string) (*Issue, error)
|
|
UpdateIssue(ctx context.Context, id string, updates map[string]interface{}, actor string) error
|
|
CloseIssue(ctx context.Context, id string, reason string, actor string) error
|
|
SearchIssues(ctx context.Context, query string, filter IssueFilter) ([]*Issue, error)
|
|
|
|
// Dependencies
|
|
AddDependency(ctx context.Context, dep *Dependency, actor string) error
|
|
RemoveDependency(ctx context.Context, issueID, dependsOnID string, actor string) error
|
|
GetDependencies(ctx context.Context, issueID string) ([]*Issue, error)
|
|
GetDependents(ctx context.Context, issueID string) ([]*Issue, error)
|
|
GetDependencyTree(ctx context.Context, issueID string, maxDepth int) ([]*TreeNode, error)
|
|
DetectCycles(ctx context.Context) ([][]*Issue, error)
|
|
|
|
// Labels
|
|
AddLabel(ctx context.Context, issueID, label, actor string) error
|
|
RemoveLabel(ctx context.Context, issueID, label, actor string) error
|
|
GetLabels(ctx context.Context, issueID string) ([]string, error)
|
|
GetIssuesByLabel(ctx context.Context, label string) ([]*Issue, error)
|
|
|
|
// Ready Work & Blocking
|
|
GetReadyWork(ctx context.Context, filter WorkFilter) ([]*Issue, error)
|
|
GetBlockedIssues(ctx context.Context) ([]*BlockedIssue, error)
|
|
|
|
// Events
|
|
AddComment(ctx context.Context, issueID, actor, comment string) error
|
|
GetEvents(ctx context.Context, issueID string, limit int) ([]*Event, error)
|
|
|
|
// Statistics
|
|
GetStatistics(ctx context.Context) (*Statistics, error)
|
|
|
|
// Lifecycle
|
|
Close() error
|
|
}
|
|
|
|
type IssueFilter struct {
|
|
Status *Status
|
|
Priority *int
|
|
IssueType *IssueType
|
|
Assignee *string
|
|
Labels []string
|
|
Limit int
|
|
}
|
|
|
|
type WorkFilter struct {
|
|
Status Status // default: open
|
|
Priority *int // filter by priority
|
|
Assignee *string
|
|
Limit int // default: 10
|
|
}
|
|
|
|
type BlockedIssue struct {
|
|
Issue
|
|
BlockedByCount int
|
|
BlockedBy []string // issue IDs
|
|
}
|
|
|
|
type TreeNode struct {
|
|
Issue
|
|
Depth int
|
|
Truncated bool // if hit max depth
|
|
}
|
|
|
|
type Statistics struct {
|
|
TotalIssues int
|
|
OpenIssues int
|
|
InProgressIssues int
|
|
ClosedIssues int
|
|
BlockedIssues int
|
|
ReadyIssues int
|
|
AverageLeadTime float64 // hours from open to closed
|
|
}
|
|
```
|
|
|
|
### Backend Implementations
|
|
|
|
```
|
|
storage/
|
|
storage.go // Interface definition
|
|
sqlite/
|
|
sqlite.go // SQLite implementation
|
|
migrations.go // Schema migrations
|
|
postgres/
|
|
postgres.go // PostgreSQL implementation
|
|
migrations.go // Schema migrations
|
|
factory.go // Backend factory
|
|
```
|
|
|
|
### Factory Pattern
|
|
|
|
```go
|
|
type Config struct {
|
|
Backend string // "sqlite" or "postgres"
|
|
|
|
// SQLite config
|
|
Path string // default: ~/.beads/beads.db
|
|
|
|
// PostgreSQL config
|
|
Host string
|
|
Port int
|
|
Database string
|
|
User string
|
|
Password string
|
|
SSLMode string
|
|
}
|
|
|
|
func NewStorage(config Config) (Storage, error) {
|
|
switch config.Backend {
|
|
case "sqlite":
|
|
return sqlite.New(config.Path)
|
|
case "postgres":
|
|
return postgres.New(config.Host, config.Port, config.Database,
|
|
config.User, config.Password, config.SSLMode)
|
|
default:
|
|
return nil, fmt.Errorf("unknown backend: %s", config.Backend)
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Schema Design
|
|
|
|
### SQLite Schema
|
|
|
|
```sql
|
|
-- Issues table
|
|
CREATE TABLE issues (
|
|
id TEXT PRIMARY KEY,
|
|
title TEXT NOT NULL CHECK(length(title) <= 500),
|
|
description TEXT NOT NULL DEFAULT '',
|
|
design TEXT NOT NULL DEFAULT '',
|
|
acceptance_criteria TEXT NOT NULL DEFAULT '',
|
|
notes TEXT NOT NULL DEFAULT '',
|
|
status TEXT NOT NULL DEFAULT 'open',
|
|
priority INTEGER NOT NULL DEFAULT 2 CHECK(priority >= 0 AND priority <= 4),
|
|
issue_type TEXT NOT NULL DEFAULT 'task',
|
|
assignee TEXT,
|
|
estimated_minutes INTEGER,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
closed_at DATETIME
|
|
);
|
|
|
|
CREATE INDEX idx_issues_status ON issues(status);
|
|
CREATE INDEX idx_issues_priority ON issues(priority);
|
|
CREATE INDEX idx_issues_assignee ON issues(assignee);
|
|
CREATE INDEX idx_issues_created_at ON issues(created_at);
|
|
|
|
-- Dependencies table
|
|
CREATE TABLE dependencies (
|
|
issue_id TEXT NOT NULL,
|
|
depends_on_id TEXT NOT NULL,
|
|
type TEXT NOT NULL DEFAULT 'blocks',
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by TEXT NOT NULL,
|
|
PRIMARY KEY (issue_id, depends_on_id),
|
|
FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (depends_on_id) REFERENCES issues(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE INDEX idx_dependencies_issue ON dependencies(issue_id);
|
|
CREATE INDEX idx_dependencies_depends_on ON dependencies(depends_on_id);
|
|
|
|
-- Labels table (many-to-many)
|
|
CREATE TABLE labels (
|
|
issue_id TEXT NOT NULL,
|
|
label TEXT NOT NULL,
|
|
PRIMARY KEY (issue_id, label),
|
|
FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE INDEX idx_labels_label ON labels(label);
|
|
|
|
-- Events table (audit trail)
|
|
CREATE TABLE events (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
issue_id TEXT NOT NULL,
|
|
event_type TEXT NOT NULL,
|
|
actor TEXT NOT NULL,
|
|
old_value TEXT,
|
|
new_value TEXT,
|
|
comment TEXT,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE INDEX idx_events_issue ON events(issue_id);
|
|
CREATE INDEX idx_events_created_at ON events(created_at);
|
|
|
|
-- Ready work view (materialized via trigger)
|
|
-- Issues with no open dependencies
|
|
CREATE VIEW ready_issues AS
|
|
SELECT i.*
|
|
FROM issues i
|
|
WHERE i.status = 'open'
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM dependencies d
|
|
JOIN issues blocked ON d.depends_on_id = blocked.id
|
|
WHERE d.issue_id = i.id
|
|
AND d.type = 'blocks'
|
|
AND blocked.status IN ('open', 'in_progress', 'blocked')
|
|
);
|
|
|
|
-- Blocked issues view
|
|
CREATE VIEW blocked_issues AS
|
|
SELECT
|
|
i.*,
|
|
COUNT(d.depends_on_id) as blocked_by_count
|
|
FROM issues i
|
|
JOIN dependencies d ON i.id = d.issue_id
|
|
JOIN issues blocker ON d.depends_on_id = blocker.id
|
|
WHERE i.status IN ('open', 'in_progress', 'blocked')
|
|
AND d.type = 'blocks'
|
|
AND blocker.status IN ('open', 'in_progress', 'blocked')
|
|
GROUP BY i.id;
|
|
```
|
|
|
|
### PostgreSQL Schema Extensions
|
|
|
|
PostgreSQL can leverage more advanced features:
|
|
|
|
```sql
|
|
-- Use JSONB for flexible metadata
|
|
ALTER TABLE issues ADD COLUMN metadata JSONB;
|
|
CREATE INDEX idx_issues_metadata ON issues USING GIN(metadata);
|
|
|
|
-- Use array type for labels (alternative to junction table)
|
|
-- (Keep junction table for compatibility, but could optimize)
|
|
|
|
-- Use recursive CTEs for dependency trees (more efficient)
|
|
-- Implement as stored function:
|
|
CREATE OR REPLACE FUNCTION get_dependency_tree(root_issue_id TEXT, max_depth INT DEFAULT 50)
|
|
RETURNS TABLE (
|
|
issue_id TEXT,
|
|
title TEXT,
|
|
status TEXT,
|
|
priority INT,
|
|
depth INT,
|
|
path TEXT[]
|
|
) AS $$
|
|
WITH RECURSIVE tree AS (
|
|
SELECT
|
|
i.id as issue_id,
|
|
i.title,
|
|
i.status,
|
|
i.priority,
|
|
0 as depth,
|
|
ARRAY[i.id] as path
|
|
FROM issues i
|
|
WHERE i.id = root_issue_id
|
|
|
|
UNION ALL
|
|
|
|
SELECT
|
|
i.id,
|
|
i.title,
|
|
i.status,
|
|
i.priority,
|
|
t.depth + 1,
|
|
t.path || i.id
|
|
FROM issues i
|
|
JOIN dependencies d ON i.id = d.depends_on_id
|
|
JOIN tree t ON d.issue_id = t.issue_id
|
|
WHERE t.depth < max_depth
|
|
AND NOT (i.id = ANY(t.path)) -- cycle detection
|
|
)
|
|
SELECT * FROM tree ORDER BY depth, priority;
|
|
$$ LANGUAGE SQL;
|
|
|
|
-- Cycle detection using CTEs
|
|
CREATE OR REPLACE FUNCTION detect_dependency_cycles()
|
|
RETURNS TABLE (cycle_path TEXT[]) AS $$
|
|
WITH RECURSIVE paths AS (
|
|
SELECT
|
|
issue_id,
|
|
depends_on_id,
|
|
ARRAY[issue_id, depends_on_id] as path,
|
|
false as is_cycle
|
|
FROM dependencies
|
|
|
|
UNION ALL
|
|
|
|
SELECT
|
|
p.issue_id,
|
|
d.depends_on_id,
|
|
p.path || d.depends_on_id,
|
|
d.depends_on_id = ANY(p.path)
|
|
FROM paths p
|
|
JOIN dependencies d ON p.depends_on_id = d.issue_id
|
|
WHERE NOT p.is_cycle
|
|
AND array_length(p.path, 1) < 100
|
|
)
|
|
SELECT DISTINCT path
|
|
FROM paths
|
|
WHERE is_cycle
|
|
ORDER BY path;
|
|
$$ LANGUAGE SQL;
|
|
```
|
|
|
|
---
|
|
|
|
## CLI Design
|
|
|
|
### Command Structure
|
|
|
|
```
|
|
beads [global options] <command> [command options]
|
|
|
|
Global Options:
|
|
--db <path> Database path (default: ~/.beads/beads.db)
|
|
--backend <type> Backend type: sqlite, postgres (default: sqlite)
|
|
--config <path> Config file path (default: ~/.beads/config.yaml)
|
|
--format <format> Output format: text, json, yaml (default: text)
|
|
--no-color Disable colored output
|
|
|
|
Commands:
|
|
init Initialize a new beads database
|
|
create Create a new issue
|
|
update Update an issue
|
|
show Show issue details
|
|
list List issues
|
|
search Search issues by text
|
|
close Close one or more issues
|
|
reopen Reopen a closed issue
|
|
|
|
comment Add a comment to an issue
|
|
|
|
dep add Add a dependency
|
|
dep remove Remove a dependency
|
|
dep tree Show dependency tree
|
|
dep cycles Detect dependency cycles
|
|
|
|
label add Add a label
|
|
label remove Remove a label
|
|
label list List all labels
|
|
label issues List issues with label
|
|
|
|
ready Show ready work (no blockers)
|
|
blocked Show blocked issues
|
|
stats Show statistics
|
|
|
|
config Manage configuration
|
|
export Export database to JSON/YAML
|
|
import Import from JSON/YAML
|
|
migrate Migrate from other issue trackers
|
|
|
|
help Show help
|
|
version Show version
|
|
```
|
|
|
|
### Example Commands
|
|
|
|
```bash
|
|
# Initialize
|
|
beads init # Creates ~/.beads/beads.db
|
|
beads init --db ./project.db # Project-local database
|
|
beads init --backend postgres # Interactive setup for PostgreSQL
|
|
|
|
# Create
|
|
beads create "Fix login bug" \
|
|
--description "Users can't log in with Google OAuth" \
|
|
--priority 1 \
|
|
--type bug \
|
|
--label "auth,critical"
|
|
|
|
# Update
|
|
beads update bd-1 --status in_progress --assignee "alice"
|
|
|
|
# Show
|
|
beads show bd-1 # Full details with dependencies
|
|
beads show bd-1 --format json # JSON output
|
|
|
|
# List
|
|
beads list # All open issues
|
|
beads list --status closed # Closed issues
|
|
beads list --priority 1 # P1 issues
|
|
beads list --label "auth" # Issues with label
|
|
|
|
# Dependencies
|
|
beads dep add bd-2 bd-1 # bd-2 depends on bd-1
|
|
beads dep tree bd-2 # Show full tree
|
|
beads dep cycles # Check for cycles
|
|
|
|
# Ready work
|
|
beads ready # Top 10 ready issues
|
|
beads ready --limit 20 --assignee alice
|
|
|
|
# Comments
|
|
beads comment bd-1 "Started investigation"
|
|
beads comment bd-1 --file notes.md # From file
|
|
|
|
# Close
|
|
beads close bd-1 "Fixed in commit abc123"
|
|
beads close bd-1 bd-2 bd-3 --reason "Duplicate"
|
|
|
|
# Search
|
|
beads search "oauth" # Full-text search
|
|
beads search "oauth" --status open # With filters
|
|
|
|
# Stats
|
|
beads stats # Overall statistics
|
|
beads stats --format json # Machine-readable
|
|
|
|
# Export/Import
|
|
beads export --output backup.json
|
|
beads import --input backup.json
|
|
beads migrate --from github --repo owner/repo
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration
|
|
|
|
### Config File (~/.beads/config.yaml)
|
|
|
|
```yaml
|
|
# Default backend
|
|
backend: sqlite
|
|
|
|
# SQLite config
|
|
sqlite:
|
|
path: ~/.beads/beads.db
|
|
|
|
# PostgreSQL config
|
|
postgres:
|
|
host: localhost
|
|
port: 5432
|
|
database: beads
|
|
user: beads
|
|
password: ""
|
|
sslmode: prefer
|
|
|
|
# Display preferences
|
|
display:
|
|
color: true
|
|
format: text # text, json, yaml
|
|
date_format: "2006-01-02 15:04"
|
|
|
|
# Issue defaults
|
|
defaults:
|
|
priority: 2
|
|
type: task
|
|
status: open
|
|
|
|
# ID prefix (default: "bd-")
|
|
id_prefix: "bd-"
|
|
|
|
# Actor name (for audit trail)
|
|
actor: $USER
|
|
```
|
|
|
|
---
|
|
|
|
## Implementation Roadmap
|
|
|
|
### Phase 1: Core Foundation
|
|
- [ ] Project setup (go.mod, directory structure)
|
|
- [ ] Data model (types, interfaces)
|
|
- [ ] SQLite storage implementation
|
|
- [ ] Basic CLI (create, list, show, update, close)
|
|
- [ ] Dependency management (add, remove, tree)
|
|
- [ ] Ready work detection
|
|
- [ ] Tests
|
|
|
|
### Phase 2: Polish & Features
|
|
- [ ] Labels support
|
|
- [ ] Comments and events
|
|
- [ ] Full-text search
|
|
- [ ] Cycle detection
|
|
- [ ] Statistics
|
|
- [ ] Colored CLI output
|
|
- [ ] JSON/YAML output formats
|
|
|
|
### Phase 3: PostgreSQL
|
|
- [ ] PostgreSQL storage implementation
|
|
- [ ] Config file support
|
|
- [ ] Backend switching
|
|
- [ ] Migration utilities
|
|
- [ ] Performance optimization
|
|
|
|
### Phase 4: Advanced
|
|
- [ ] Export/import
|
|
- [ ] GitHub/GitLab/Jira migration tools
|
|
- [ ] TUI (bubble tea?)
|
|
- [ ] Web UI (templ?)
|
|
- [ ] API server mode
|
|
- [ ] Multi-user workflows
|
|
|
|
---
|
|
|
|
## Key Design Decisions
|
|
|
|
### Why SQLite Default?
|
|
|
|
1. **Zero setup**: Single binary + database file
|
|
2. **Portability**: Database is a file, easy to backup/share
|
|
3. **Performance**: More than enough for <100k issues
|
|
4. **Simplicity**: No server to run
|
|
5. **Git-friendly**: Can commit database file for small teams
|
|
|
|
### Why Support PostgreSQL?
|
|
|
|
1. **Scale**: Better for large teams (>10 people)
|
|
2. **Concurrency**: Better multi-user support
|
|
3. **Features**: Recursive CTEs, JSONB, full-text search
|
|
4. **Existing infrastructure**: Teams already running PostgreSQL
|
|
|
|
### ID Prefix: "bd-" vs "beads-"
|
|
|
|
- **bd-**: Shorter, easier to type
|
|
- **beads-**: More explicit
|
|
- **Configurable**: Let users choose in config
|
|
|
|
I lean toward **bd-** for brevity.
|
|
|
|
### Dependency Types
|
|
|
|
- **blocks**: Hard blocker (affects ready work calculation)
|
|
- **related**: Soft relationship (just for context)
|
|
- **parent-child**: Epic/subtask hierarchy
|
|
|
|
Only "blocks" affects ready work detection.
|
|
|
|
### Status vs. Blocked Field
|
|
|
|
Should we have a separate `blocked` status, or compute it dynamically?
|
|
|
|
**Decision**: Compute dynamically
|
|
- `blocked` status is redundant with dependency graph
|
|
- Auto-blocking based on dependencies is error-prone
|
|
- Let users manually set `blocked` if they want (e.g., blocked on external dependency)
|
|
- `ready` command shows what's actually unblocked
|
|
|
|
### Event Storage
|
|
|
|
Full audit trail in `events` table. This enables:
|
|
- Change history for issues
|
|
- Comment threads
|
|
- "Who changed what when" debugging
|
|
- Potential undo/revert functionality
|
|
|
|
---
|
|
|
|
## What to Port from VibeCoder
|
|
|
|
### ✅ Keep
|
|
- Core data model (issues, dependencies, labels, events)
|
|
- Ready work detection algorithm
|
|
- Dependency tree traversal
|
|
- Cycle detection
|
|
- CLI structure (create, update, show, list, etc.)
|
|
- Priority system (1-5)
|
|
- Issue types (bug, feature, task, epic, chore)
|
|
|
|
### ❌ Leave Behind
|
|
- MCP server (can add later as separate project)
|
|
- VibeCoder-specific concepts (missions, campaigns, amps)
|
|
- Temporal workflows
|
|
- Web portal integration
|
|
- Mission tracking
|
|
- Campaign aggregation views
|
|
|
|
### 🤔 Maybe Later
|
|
- Web UI (keep CLI-first)
|
|
- API server mode
|
|
- TUI with bubble tea
|
|
- GitHub/GitLab sync
|
|
- Email notifications
|
|
- Webhooks
|
|
|
|
---
|
|
|
|
## Go Dependencies
|
|
|
|
Minimal dependencies:
|
|
|
|
```go
|
|
// Core
|
|
database/sql
|
|
github.com/mattn/go-sqlite3 // SQLite driver
|
|
github.com/lib/pq // PostgreSQL driver
|
|
|
|
// CLI
|
|
github.com/spf13/cobra // CLI framework
|
|
github.com/spf13/viper // Config management
|
|
github.com/fatih/color // Terminal colors
|
|
|
|
// Serialization
|
|
gopkg.in/yaml.v3 // YAML support
|
|
|
|
// Testing
|
|
github.com/stretchr/testify // Test assertions
|
|
```
|
|
|
|
No frameworks, no ORMs. Keep it simple.
|
|
|
|
---
|
|
|
|
## Open Questions
|
|
|
|
1. **Multi-database support**: Should one beads installation manage multiple databases?
|
|
- Probably yes: `beads --db project1.db` vs `beads --db project2.db`
|
|
|
|
2. **Git integration**: Should beads auto-commit the database?
|
|
- Probably no: Let users manage their own git workflow
|
|
- But provide hooks/examples
|
|
|
|
3. **Web UI**: Build one, or keep it CLI-only?
|
|
- Start CLI-only
|
|
- Web UI as separate project later (beads-web?)
|
|
|
|
4. **API server**: Should beads run as a server?
|
|
- Start as CLI tool
|
|
- Add `beads serve` command later for HTTP API
|
|
|
|
5. **Migrations**: How to handle schema changes?
|
|
- Embed migrations in binary
|
|
- Track schema version in database
|
|
- Auto-migrate on startup (with backup)
|
|
|
|
6. **Concurrency**: SQLite WAL mode for better concurrency?
|
|
- Yes, enable by default
|
|
- Document limitations (single writer at a time)
|
|
|
|
7. **Full-text search**: SQLite FTS5 or simple LIKE queries?
|
|
- Start with LIKE queries (simpler)
|
|
- Add FTS5 in phase 2
|
|
|
|
8. **Transactions**: Where do we need them?
|
|
- Issue creation (issue + labels + event)
|
|
- Dependency changes (dep + event + cycle check)
|
|
- Bulk operations (close multiple issues)
|
|
|
|
---
|
|
|
|
## Success Metrics
|
|
|
|
Beads is successful if:
|
|
|
|
1. **Installation**: `go install github.com/user/beads@latest` just works
|
|
2. **First use**: `beads init && beads create "test"` works in <10 seconds
|
|
3. **Performance**: Can handle 10k issues with instant CLI responses
|
|
4. **Portability**: Database file can be moved between machines, checked into git
|
|
5. **Adoption**: Used by at least 3 other developers/teams within 6 months
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
1. Create `~/src/beads` directory structure
|
|
2. Initialize Go module
|
|
3. Implement core types (Issue, Dependency, Storage interface)
|
|
4. Build SQLite storage implementation
|
|
5. Build basic CLI (create, show, list)
|
|
6. Test with VibeCoder's issue data (export and import)
|
|
7. Iterate toward parity with scripts/issue.ts
|
|
8. Release v0.1.0
|
|
|
|
Let's build something beautiful. 🔗✨
|