136 lines
5.0 KiB
Go
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
|
|
}
|