Files
WHOOSH/cmd/whoosh/main.go
Claude Code 56ea52b743 Implement initial scan logic and council formation for WHOOSH project kickoffs
- Replace incremental sync with full scan for new repositories
- Add initial_scan status to bypass Since parameter filtering
- Implement council formation detection for Design Brief issues
- Add version display to WHOOSH UI header for debugging
- Fix Docker token authentication with trailing newline removal
- Add comprehensive council orchestration with Docker Swarm integration
- Include BACKBEAT prototype integration for distributed timing
- Support council-specific agent roles and deployment strategies
- Transition repositories to active status after content discovery

Key architectural improvements:
- Full scan approach for new project detection vs incremental sync
- Council formation triggered by chorus-entrypoint labeled Design Briefs
- Proper token handling and authentication for Gitea API calls
- Support for both initial discovery and ongoing task monitoring

This enables autonomous project kickoff workflows where Design Brief issues
automatically trigger formation of specialized agent councils for new projects.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-12 09:49:36 +10:00

210 lines
5.1 KiB
Go

package main
import (
"context"
"flag"
"fmt"
"net/http"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/chorus-services/whoosh/internal/config"
"github.com/chorus-services/whoosh/internal/database"
"github.com/chorus-services/whoosh/internal/server"
"github.com/kelseyhightower/envconfig"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
const (
serviceName = "whoosh"
)
var (
// Build-time variables (set via ldflags)
version = "0.1.1-debug"
commitHash = "unknown"
buildDate = "unknown"
)
func main() {
// Parse command line flags
var (
healthCheck = flag.Bool("health-check", false, "Run health check and exit")
showVersion = flag.Bool("version", false, "Show version information and exit")
)
flag.Parse()
// Handle version flag
if *showVersion {
fmt.Printf("WHOOSH %s\n", version)
fmt.Printf("Commit: %s\n", commitHash)
fmt.Printf("Built: %s\n", buildDate)
return
}
// Handle health check flag
if *healthCheck {
if err := runHealthCheck(); err != nil {
log.Fatal().Err(err).Msg("Health check failed")
}
return
}
// Configure structured logging
setupLogging()
log.Info().
Str("service", serviceName).
Str("version", version).
Str("commit", commitHash).
Str("build_date", buildDate).
Msg("🎭 Starting WHOOSH - Autonomous AI Development Teams")
// Load configuration
var cfg config.Config
// Debug: Print all environment variables starting with WHOOSH
log.Debug().Msg("Environment variables:")
for _, env := range os.Environ() {
if strings.HasPrefix(env, "WHOOSH_") {
// Don't log passwords in full, just indicate they exist
if strings.Contains(env, "PASSWORD") {
parts := strings.SplitN(env, "=", 2)
if len(parts) == 2 && len(parts[1]) > 0 {
log.Debug().Str("env", parts[0]+"=[REDACTED]").Msg("Found password env var")
}
} else {
log.Debug().Str("env", env).Msg("Found env var")
}
}
}
if err := envconfig.Process("whoosh", &cfg); err != nil {
log.Fatal().Err(err).Msg("Failed to load configuration")
}
// Validate configuration
if err := cfg.Validate(); err != nil {
log.Fatal().Err(err).Msg("Invalid configuration")
}
log.Info().
Str("listen_addr", cfg.Server.ListenAddr).
Str("database_host", cfg.Database.Host).
Bool("redis_enabled", cfg.Redis.Enabled).
Msg("📋 Configuration loaded")
// Initialize database
db, err := database.NewPostgresDB(cfg.Database)
if err != nil {
log.Fatal().Err(err).Msg("Failed to initialize database")
}
defer db.Close()
log.Info().Msg("🗄️ Database connection established")
// Run migrations
if cfg.Database.AutoMigrate {
log.Info().Msg("🔄 Running database migrations...")
if err := database.RunMigrations(cfg.Database.URL); err != nil {
log.Fatal().Err(err).Msg("Database migration failed")
}
log.Info().Msg("✅ Database migrations completed")
}
// Set version for server
server.SetVersion(version)
// Initialize server
srv, err := server.NewServer(&cfg, db)
if err != nil {
log.Fatal().Err(err).Msg("Failed to create server")
}
// Start server
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
log.Info().
Str("addr", cfg.Server.ListenAddr).
Msg("🌐 Starting HTTP server")
if err := srv.Start(ctx); err != nil {
log.Error().Err(err).Msg("Server startup failed")
cancel()
}
}()
// Wait for shutdown signal
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
select {
case sig := <-sigChan:
log.Info().Str("signal", sig.String()).Msg("🛑 Shutdown signal received")
case <-ctx.Done():
log.Info().Msg("🛑 Context cancelled")
}
// Graceful shutdown
log.Info().Msg("🔄 Starting graceful shutdown...")
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second)
defer shutdownCancel()
if err := srv.Shutdown(shutdownCtx); err != nil {
log.Error().Err(err).Msg("Server shutdown failed")
}
log.Info().Msg("✅ WHOOSH shutdown complete")
}
func runHealthCheck() error {
// Simple health check - try to connect to health endpoint
client := &http.Client{Timeout: 5 * time.Second}
// Use localhost for health check
healthURL := "http://localhost:8080/health"
resp, err := client.Get(healthURL)
if err != nil {
return fmt.Errorf("health check request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("health check returned status %d", resp.StatusCode)
}
return nil
}
func setupLogging() {
// Configure zerolog for structured logging
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
// Set log level from environment
level := os.Getenv("LOG_LEVEL")
switch level {
case "debug":
zerolog.SetGlobalLevel(zerolog.DebugLevel)
case "info":
zerolog.SetGlobalLevel(zerolog.InfoLevel)
case "warn":
zerolog.SetGlobalLevel(zerolog.WarnLevel)
case "error":
zerolog.SetGlobalLevel(zerolog.ErrorLevel)
default:
zerolog.SetGlobalLevel(zerolog.InfoLevel)
}
// Pretty logging for development
if os.Getenv("ENVIRONMENT") == "development" {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
}
}