Files
apex-public/cmd/apex/main.go
T
2026-07-06 11:05:50 -04:00

136 lines
5.0 KiB
Go

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 <add|list|run> ...")
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 <url> 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 <file.docx> 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> <url> [url...] compare evaluated jobs side-by-side with Claude ranking")
fmt.Println(" apex pipeline <add|list|run> manage pipeline queue (add/list/run jobs)")
fmt.Println(" apex apply <url> 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 <subcommand> 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 <careers-url>")
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
}