package main import ( "context" "fmt" "os" "time" "github.com/cobr-ai/apex/internal/scan" "github.com/cobr-ai/apex/internal/tui" ) // main dispatches on the first argv: no subcommand → TUI; "discover" → // resolve a careers URL into a Workday adapter snippet (useful during // portal-curation). Keeping the dispatcher tiny avoids pulling in cobra just // for one flag path. func main() { if len(os.Args) > 1 { switch os.Args[1] { case "discover": os.Exit(runDiscover(os.Args[2:])) case "scan": os.Exit(runScan(os.Args[2:])) case "eval": os.Exit(runEval(os.Args[2:])) case "ingest": os.Exit(runIngest(os.Args[2:])) case "offers": os.Exit(runOffers(os.Args[2:])) case "linkedin": os.Exit(runLinkedIn(os.Args[2:])) case "cv": os.Exit(runCV(os.Args[2:])) case "patterns": os.Exit(runPatterns(os.Args[2:])) case "followup": os.Exit(runFollowup(os.Args[2:])) case "stories": os.Exit(runStories(os.Args[2:])) case "pipeline": if len(os.Args) < 3 { fmt.Fprintln(os.Stderr, "usage: apex pipeline ...") os.Exit(2) } switch os.Args[2] { case "add": os.Exit(runPipelineAdd(os.Args[3:])) case "list": os.Exit(runPipelineList(os.Args[3:])) case "run": os.Exit(runPipelineRun(os.Args[3:])) default: fmt.Fprintf(os.Stderr, "unknown pipeline subcommand: %s\n", os.Args[2]) os.Exit(2) } case "apply": os.Exit(runApply(os.Args[2:])) case "compare": os.Exit(runCompare(os.Args[2:])) case "deep": os.Exit(runDeep(os.Args[2:])) case "training": os.Exit(runTraining(os.Args[2:])) case "project": os.Exit(runProject(os.Args[2:])) case "negotiate": os.Exit(runNegotiate(os.Args[2:])) case "contacto": os.Exit(runContacto(os.Args[2:])) case "intake": os.Exit(runIntake(os.Args[2:])) case "-h", "--help", "help": printUsage() return } } app := tui.NewApp() if err := app.Run(); err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) } } func printUsage() { fmt.Println("apex — TUI-first AI job search platform") fmt.Println() fmt.Println("Usage:") fmt.Println(" apex launch the TUI (default)") fmt.Println(" apex discover resolve Workday host/tenant/site for a careers URL") fmt.Println(" apex scan headless scan of all enabled adapters; writes new jobs to ~/.apex/apex.db") fmt.Println(" apex ingest parse DOCX resume into cv.md with writing style extraction") fmt.Println(" apex eval [--limit N] [--min-score F] [--workers N] evaluate pending jobs via Claude CLI; reports under ~/.apex/reports/") fmt.Println(" apex offers [url...] compare evaluated jobs side-by-side with Claude ranking") fmt.Println(" apex pipeline manage pipeline queue (add/list/run jobs)") fmt.Println(" apex apply mark a job as applied (interactive confirmation)") fmt.Println(" apex compare [url] compare job eval against senior engineer benchmark") fmt.Println(" apex deep [url] deep company/culture/red-flag analysis") fmt.Println(" apex training [url] personal development roadmap from job eval") fmt.Println(" apex project [url] portfolio projects to address job gaps") fmt.Println(" apex negotiate [url] salary negotiation strategy") fmt.Println(" apex contacto [url] draft LinkedIn outreach messages for hiring team") fmt.Println(" apex cv [--job-url URL] generate tailored HTML CV + PDF for a job (or generic CV if no job given)") fmt.Println(" apex patterns show application pattern analysis (ghost rate, score distribution, etc)") fmt.Println(" apex followup show pending follow-ups based on status cadence") fmt.Println(" apex stories [--limit N] show STAR stories extracted from recent evaluations") fmt.Println(" apex intake [--synthesize] interactive 8-phase intake interview → cv.md + profile.yml") fmt.Println(" apex linkedin LinkedIn browser automation (messages/thread/reply/invite/jobs/contacts/test)") fmt.Println(" apex help show this message") fmt.Println() fmt.Println("Environment:") fmt.Println(" APEX_DISCOVER_SCRIPT absolute path to career-ops/discover-workday.mjs") fmt.Println(" (used when the input URL isn't directly *.myworkdayjobs.com)") fmt.Println(" APEX_MIN_SCORE eval score threshold; below = status Discarded") } func runDiscover(args []string) int { if len(args) == 0 { fmt.Fprintln(os.Stderr, "usage: apex discover ") return 2 } d := &scan.Discoverer{ DiscoverScript: os.Getenv("APEX_DISCOVER_SCRIPT"), Timeout: 90 * time.Second, } ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cancel() out, err := d.Discover(ctx, args[0]) if err != nil { fmt.Fprintf(os.Stderr, "discover: %v\n", err) return 1 } fmt.Printf("# Source: %s Input: %s\n", out.Source, out.Input) fmt.Println(out.YAMLSnippet("")) return 0 }