* examples: Add complete Go extension example with documentation Adds a comprehensive Go extension example demonstrating bd's extension patterns and Go API usage: **New bd-example-extension-go package:** - Complete working example in 116 lines total - main.go (93 lines): Full workflow with embedded schema - schema.sql (23 lines): Extension tables with foreign keys - Comprehensive README.md (241 lines): Documentation and usage guide - Go module with proper dependencies **Key patterns demonstrated:** - Schema extension with namespaced tables (example_executions, example_checkpoints) - Foreign key integration with bd's issues table - Dual-layer access using bd's Go API + direct SQL queries - Complex joined queries across bd and extension tables - Execution tracking with agent assignment and checkpointing **Features:** - Auto-discovery of bd database path - Proper SQLite configuration (WAL mode, busy timeout) - Real-world orchestration patterns - Installation and usage instructions - Integration examples with bd's ready work queue This provides a complete reference implementation for developers building bd extensions, complementing the Go API added in recent commits. * Update go.mod after merge with main, add .gitignore - Fix Go version to 1.21 (matches main module) - Reorganize dependencies properly - Keep replace directive for local development - Add .gitignore for built binary --------- Co-authored-by: Steve Yegge <steve.yegge@gmail.com>
94 lines
2.4 KiB
Go
94 lines
2.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
_ "embed"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"time"
|
|
|
|
"github.com/steveyegge/beads"
|
|
)
|
|
|
|
//go:embed schema.sql
|
|
var schema string
|
|
|
|
func main() {
|
|
dbPath := flag.String("db", "", "Database path (default: auto-discover)")
|
|
flag.Parse()
|
|
|
|
if *dbPath == "" {
|
|
*dbPath = beads.FindDatabasePath()
|
|
}
|
|
if *dbPath == "" {
|
|
log.Fatal("No database found. Run 'bd init'")
|
|
}
|
|
|
|
// Open bd storage + extension database
|
|
store, _ := beads.NewSQLiteStorage(*dbPath)
|
|
defer store.Close()
|
|
db, _ := sql.Open("sqlite3", *dbPath)
|
|
defer db.Close()
|
|
db.Exec("PRAGMA journal_mode=WAL")
|
|
db.Exec("PRAGMA busy_timeout=5000")
|
|
db.Exec(schema) // Initialize extension schema
|
|
|
|
// Get ready work
|
|
ctx := context.Background()
|
|
readyIssues, _ := store.GetReadyWork(ctx, beads.WorkFilter{Limit: 1})
|
|
if len(readyIssues) == 0 {
|
|
fmt.Println("No ready work")
|
|
return
|
|
}
|
|
|
|
issue := readyIssues[0]
|
|
fmt.Printf("Claiming: %s\n", issue.ID)
|
|
|
|
// Create execution record
|
|
result, _ := db.Exec(`INSERT INTO example_executions (issue_id, status, agent_id, started_at)
|
|
VALUES (?, 'running', 'demo-agent', ?)`, issue.ID, time.Now())
|
|
execID, _ := result.LastInsertId()
|
|
|
|
// Update issue in bd
|
|
store.UpdateIssue(ctx, issue.ID, map[string]interface{}{"status": beads.StatusInProgress}, "demo-agent")
|
|
|
|
// Create checkpoints
|
|
for _, phase := range []string{"assess", "implement", "test"} {
|
|
data, _ := json.Marshal(map[string]interface{}{"phase": phase, "time": time.Now()})
|
|
db.Exec(`INSERT INTO example_checkpoints (execution_id, phase, checkpoint_data) VALUES (?, ?, ?)`,
|
|
execID, phase, string(data))
|
|
fmt.Printf(" ✓ %s\n", phase)
|
|
}
|
|
|
|
// Complete
|
|
db.Exec(`UPDATE example_executions SET status='completed', completed_at=? WHERE id=?`, time.Now(), execID)
|
|
store.CloseIssue(ctx, issue.ID, "Done", "demo-agent")
|
|
|
|
// Show status
|
|
fmt.Println("\nStatus:")
|
|
rows, _ := db.Query(`
|
|
SELECT i.id, i.title, i.status, e.agent_id, COUNT(c.id)
|
|
FROM issues i
|
|
LEFT JOIN example_executions e ON i.id = e.issue_id
|
|
LEFT JOIN example_checkpoints c ON e.id = c.execution_id
|
|
GROUP BY i.id, e.id
|
|
ORDER BY i.priority
|
|
LIMIT 5`)
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
var id, title, status string
|
|
var agent sql.NullString
|
|
var checkpoints int
|
|
rows.Scan(&id, &title, &status, &agent, &checkpoints)
|
|
agentStr := "-"
|
|
if agent.Valid {
|
|
agentStr = agent.String
|
|
}
|
|
fmt.Printf(" %s: %s [%s] agent=%s checkpoints=%d\n", id, title, status, agentStr, checkpoints)
|
|
}
|
|
}
|