bd sync: 2025-12-23 23:38:57

This commit is contained in:
Steve Yegge
2025-12-23 23:38:57 -08:00
parent 05e10b6759
commit e67f27c092
80 changed files with 7165 additions and 8490 deletions

View File

@@ -166,8 +166,7 @@ Examples:
} else {
sqliteStore, ok := store.(*sqlite.SQLiteStorage)
if !ok {
fmt.Fprintf(os.Stderr, "Error: compact requires SQLite storage\n")
os.Exit(1)
FatalError("compact requires SQLite storage")
}
runCompactStats(ctx, sqliteStore)
}
@@ -188,26 +187,20 @@ Examples:
// Check for exactly one mode
if activeModes == 0 {
fmt.Fprintf(os.Stderr, "Error: must specify one mode: --analyze, --apply, or --auto\n")
os.Exit(1)
FatalError("must specify one mode: --analyze, --apply, or --auto")
}
if activeModes > 1 {
fmt.Fprintf(os.Stderr, "Error: cannot use multiple modes together (--analyze, --apply, --auto are mutually exclusive)\n")
os.Exit(1)
FatalError("cannot use multiple modes together (--analyze, --apply, --auto are mutually exclusive)")
}
// Handle analyze mode (requires direct database access)
if compactAnalyze {
if err := ensureDirectMode("compact --analyze requires direct database access"); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
fmt.Fprintf(os.Stderr, "Hint: Use --no-daemon flag to bypass daemon and access database directly\n")
os.Exit(1)
FatalErrorWithHint(fmt.Sprintf("%v", err), "Use --no-daemon flag to bypass daemon and access database directly")
}
sqliteStore, ok := store.(*sqlite.SQLiteStorage)
if !ok {
fmt.Fprintf(os.Stderr, "Error: failed to open database in direct mode\n")
fmt.Fprintf(os.Stderr, "Hint: Ensure .beads/beads.db exists and is readable\n")
os.Exit(1)
FatalErrorWithHint("failed to open database in direct mode", "Ensure .beads/beads.db exists and is readable")
}
runCompactAnalyze(ctx, sqliteStore)
return
@@ -216,23 +209,17 @@ Examples:
// Handle apply mode (requires direct database access)
if compactApply {
if err := ensureDirectMode("compact --apply requires direct database access"); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
fmt.Fprintf(os.Stderr, "Hint: Use --no-daemon flag to bypass daemon and access database directly\n")
os.Exit(1)
FatalErrorWithHint(fmt.Sprintf("%v", err), "Use --no-daemon flag to bypass daemon and access database directly")
}
if compactID == "" {
fmt.Fprintf(os.Stderr, "Error: --apply requires --id\n")
os.Exit(1)
FatalError("--apply requires --id")
}
if compactSummary == "" {
fmt.Fprintf(os.Stderr, "Error: --apply requires --summary\n")
os.Exit(1)
FatalError("--apply requires --summary")
}
sqliteStore, ok := store.(*sqlite.SQLiteStorage)
if !ok {
fmt.Fprintf(os.Stderr, "Error: failed to open database in direct mode\n")
fmt.Fprintf(os.Stderr, "Hint: Ensure .beads/beads.db exists and is readable\n")
os.Exit(1)
FatalErrorWithHint("failed to open database in direct mode", "Ensure .beads/beads.db exists and is readable")
}
runCompactApply(ctx, sqliteStore)
return
@@ -248,16 +235,13 @@ Examples:
// Validation checks
if compactID != "" && compactAll {
fmt.Fprintf(os.Stderr, "Error: cannot use --id and --all together\n")
os.Exit(1)
FatalError("cannot use --id and --all together")
}
if compactForce && compactID == "" {
fmt.Fprintf(os.Stderr, "Error: --force requires --id\n")
os.Exit(1)
FatalError("--force requires --id")
}
if compactID == "" && !compactAll && !compactDryRun {
fmt.Fprintf(os.Stderr, "Error: must specify --all, --id, or --dry-run\n")
os.Exit(1)
FatalError("must specify --all, --id, or --dry-run")
}
// Use RPC if daemon available, otherwise direct mode
@@ -269,14 +253,12 @@ Examples:
// Fallback to direct mode
apiKey := os.Getenv("ANTHROPIC_API_KEY")
if apiKey == "" && !compactDryRun {
fmt.Fprintf(os.Stderr, "Error: --auto mode requires ANTHROPIC_API_KEY environment variable\n")
os.Exit(1)
FatalError("--auto mode requires ANTHROPIC_API_KEY environment variable")
}
sqliteStore, ok := store.(*sqlite.SQLiteStorage)
if !ok {
fmt.Fprintf(os.Stderr, "Error: compact requires SQLite storage\n")
os.Exit(1)
FatalError("compact requires SQLite storage")
}
config := &compact.Config{
@@ -289,8 +271,7 @@ Examples:
compactor, err := compact.New(sqliteStore, apiKey, config)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to create compactor: %v\n", err)
os.Exit(1)
FatalError("failed to create compactor: %v", err)
}
if compactID != "" {
@@ -309,19 +290,16 @@ func runCompactSingle(ctx context.Context, compactor *compact.Compactor, store *
if !compactForce {
eligible, reason, err := store.CheckEligibility(ctx, issueID, compactTier)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to check eligibility: %v\n", err)
os.Exit(1)
FatalError("failed to check eligibility: %v", err)
}
if !eligible {
fmt.Fprintf(os.Stderr, "Error: %s is not eligible for Tier %d compaction: %s\n", issueID, compactTier, reason)
os.Exit(1)
FatalError("%s is not eligible for Tier %d compaction: %s", issueID, compactTier, reason)
}
}
issue, err := store.GetIssue(ctx, issueID)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to get issue: %v\n", err)
os.Exit(1)
FatalError("failed to get issue: %v", err)
}
originalSize := len(issue.Description) + len(issue.Design) + len(issue.Notes) + len(issue.AcceptanceCriteria)
@@ -349,19 +327,16 @@ func runCompactSingle(ctx context.Context, compactor *compact.Compactor, store *
if compactTier == 1 {
compactErr = compactor.CompactTier1(ctx, issueID)
} else {
fmt.Fprintf(os.Stderr, "Error: Tier 2 compaction not yet implemented\n")
os.Exit(1)
FatalError("Tier 2 compaction not yet implemented")
}
if compactErr != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", compactErr)
os.Exit(1)
FatalError("%v", compactErr)
}
issue, err = store.GetIssue(ctx, issueID)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to get updated issue: %v\n", err)
os.Exit(1)
FatalError("failed to get updated issue: %v", err)
}
compactedSize := len(issue.Description)
@@ -407,8 +382,7 @@ func runCompactAll(ctx context.Context, compactor *compact.Compactor, store *sql
if compactTier == 1 {
tier1, err := store.GetTier1Candidates(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to get candidates: %v\n", err)
os.Exit(1)
FatalError("failed to get candidates: %v", err)
}
for _, c := range tier1 {
candidates = append(candidates, c.IssueID)
@@ -416,8 +390,7 @@ func runCompactAll(ctx context.Context, compactor *compact.Compactor, store *sql
} else {
tier2, err := store.GetTier2Candidates(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to get candidates: %v\n", err)
os.Exit(1)
FatalError("failed to get candidates: %v", err)
}
for _, c := range tier2 {
candidates = append(candidates, c.IssueID)
@@ -471,8 +444,7 @@ func runCompactAll(ctx context.Context, compactor *compact.Compactor, store *sql
results, err := compactor.CompactTier1Batch(ctx, candidates)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: batch compaction failed: %v\n", err)
os.Exit(1)
FatalError("batch compaction failed: %v", err)
}
successCount := 0
@@ -535,14 +507,12 @@ func runCompactAll(ctx context.Context, compactor *compact.Compactor, store *sql
func runCompactStats(ctx context.Context, store *sqlite.SQLiteStorage) {
tier1, err := store.GetTier1Candidates(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to get Tier 1 candidates: %v\n", err)
os.Exit(1)
FatalError("failed to get Tier 1 candidates: %v", err)
}
tier2, err := store.GetTier2Candidates(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to get Tier 2 candidates: %v\n", err)
os.Exit(1)
FatalError("failed to get Tier 2 candidates: %v", err)
}
tier1Size := 0
@@ -608,24 +578,20 @@ func progressBar(current, total int) string {
//nolint:unparam // ctx may be used in future for cancellation
func runCompactRPC(_ context.Context) {
if compactID != "" && compactAll {
fmt.Fprintf(os.Stderr, "Error: cannot use --id and --all together\n")
os.Exit(1)
FatalError("cannot use --id and --all together")
}
if compactForce && compactID == "" {
fmt.Fprintf(os.Stderr, "Error: --force requires --id\n")
os.Exit(1)
FatalError("--force requires --id")
}
if compactID == "" && !compactAll && !compactDryRun {
fmt.Fprintf(os.Stderr, "Error: must specify --all, --id, or --dry-run\n")
os.Exit(1)
FatalError("must specify --all, --id, or --dry-run")
}
apiKey := os.Getenv("ANTHROPIC_API_KEY")
if apiKey == "" && !compactDryRun {
fmt.Fprintf(os.Stderr, "Error: ANTHROPIC_API_KEY environment variable not set\n")
os.Exit(1)
FatalError("ANTHROPIC_API_KEY environment variable not set")
}
args := map[string]interface{}{
@@ -643,13 +609,11 @@ func runCompactRPC(_ context.Context) {
resp, err := daemonClient.Execute("compact", args)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
FatalError("%v", err)
}
if !resp.Success {
fmt.Fprintf(os.Stderr, "Error: %s\n", resp.Error)
os.Exit(1)
FatalError("%s", resp.Error)
}
if jsonOutput {
@@ -676,8 +640,7 @@ func runCompactRPC(_ context.Context) {
}
if err := json.Unmarshal(resp.Data, &result); err != nil {
fmt.Fprintf(os.Stderr, "Error parsing response: %v\n", err)
os.Exit(1)
FatalError("parsing response: %v", err)
}
if compactID != "" {
@@ -722,13 +685,11 @@ func runCompactStatsRPC() {
resp, err := daemonClient.Execute("compact_stats", args)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
FatalError("%v", err)
}
if !resp.Success {
fmt.Fprintf(os.Stderr, "Error: %s\n", resp.Error)
os.Exit(1)
FatalError("%s", resp.Error)
}
if jsonOutput {
@@ -749,8 +710,7 @@ func runCompactStatsRPC() {
}
if err := json.Unmarshal(resp.Data, &result); err != nil {
fmt.Fprintf(os.Stderr, "Error parsing response: %v\n", err)
os.Exit(1)
FatalError("parsing response: %v", err)
}
fmt.Printf("\nCompaction Statistics\n")
@@ -784,8 +744,7 @@ func runCompactAnalyze(ctx context.Context, store *sqlite.SQLiteStorage) {
if compactID != "" {
issue, err := store.GetIssue(ctx, compactID)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to get issue: %v\n", err)
os.Exit(1)
FatalError("failed to get issue: %v", err)
}
sizeBytes := len(issue.Description) + len(issue.Design) + len(issue.Notes) + len(issue.AcceptanceCriteria)
@@ -816,8 +775,7 @@ func runCompactAnalyze(ctx context.Context, store *sqlite.SQLiteStorage) {
tierCandidates, err = store.GetTier2Candidates(ctx)
}
if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to get candidates: %v\n", err)
os.Exit(1)
FatalError("failed to get candidates: %v", err)
}
// Apply limit if specified
@@ -879,15 +837,13 @@ func runCompactApply(ctx context.Context, store *sqlite.SQLiteStorage) {
// Read from stdin
summaryBytes, err = io.ReadAll(os.Stdin)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to read summary from stdin: %v\n", err)
os.Exit(1)
FatalError("failed to read summary from stdin: %v", err)
}
} else {
// #nosec G304 -- summary file path provided explicitly by operator
summaryBytes, err = os.ReadFile(compactSummary)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to read summary file: %v\n", err)
os.Exit(1)
FatalError("failed to read summary file: %v", err)
}
}
summary := string(summaryBytes)
@@ -895,8 +851,7 @@ func runCompactApply(ctx context.Context, store *sqlite.SQLiteStorage) {
// Get issue
issue, err := store.GetIssue(ctx, compactID)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to get issue: %v\n", err)
os.Exit(1)
FatalError("failed to get issue: %v", err)
}
// Calculate sizes
@@ -907,20 +862,15 @@ func runCompactApply(ctx context.Context, store *sqlite.SQLiteStorage) {
if !compactForce {
eligible, reason, err := store.CheckEligibility(ctx, compactID, compactTier)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to check eligibility: %v\n", err)
os.Exit(1)
FatalError("failed to check eligibility: %v", err)
}
if !eligible {
fmt.Fprintf(os.Stderr, "Error: %s is not eligible for Tier %d compaction: %s\n", compactID, compactTier, reason)
fmt.Fprintf(os.Stderr, "Hint: use --force to bypass eligibility checks\n")
os.Exit(1)
FatalErrorWithHint(fmt.Sprintf("%s is not eligible for Tier %d compaction: %s", compactID, compactTier, reason), "use --force to bypass eligibility checks")
}
// Enforce size reduction unless --force
if compactedSize >= originalSize {
fmt.Fprintf(os.Stderr, "Error: summary (%d bytes) is not shorter than original (%d bytes)\n", compactedSize, originalSize)
fmt.Fprintf(os.Stderr, "Hint: use --force to bypass size validation\n")
os.Exit(1)
FatalErrorWithHint(fmt.Sprintf("summary (%d bytes) is not shorter than original (%d bytes)", compactedSize, originalSize), "use --force to bypass size validation")
}
}
@@ -938,27 +888,23 @@ func runCompactApply(ctx context.Context, store *sqlite.SQLiteStorage) {
}
if err := store.UpdateIssue(ctx, compactID, updates, actor); err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to update issue: %v\n", err)
os.Exit(1)
FatalError("failed to update issue: %v", err)
}
commitHash := compact.GetCurrentCommitHash()
if err := store.ApplyCompaction(ctx, compactID, compactTier, originalSize, compactedSize, commitHash); err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to apply compaction: %v\n", err)
os.Exit(1)
FatalError("failed to apply compaction: %v", err)
}
savingBytes := originalSize - compactedSize
reductionPct := float64(savingBytes) / float64(originalSize) * 100
eventData := fmt.Sprintf("Tier %d compaction: %d → %d bytes (saved %d, %.1f%%)", compactTier, originalSize, compactedSize, savingBytes, reductionPct)
if err := store.AddComment(ctx, compactID, actor, eventData); err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to record event: %v\n", err)
os.Exit(1)
FatalError("failed to record event: %v", err)
}
if err := store.MarkIssueDirty(ctx, compactID); err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to mark dirty: %v\n", err)
os.Exit(1)
FatalError("failed to mark dirty: %v", err)
}
elapsed := time.Since(start)