{"repo": "alfredooe/aurdit", "files": {"cmd/aurdit/main.go": {"18347b0f8dad5acb518bda4bf916c04d51aca071": {"t": 1785745650.480243, "n": 6124, "h": "b3b4db3f9ecaa247", "c": "package main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"text/tabwriter\"\n\n\t\"github.com/Alfredooe/aurdit/internal/audit\"\n)\n\nfunc main() {\n\tif len(os.Args) < 2 {\n\t\tusage()\n\t\tos.Exit(1)\n\t}\n\n\tcmd := os.Args[1]\n\n\tswitch cmd {\n\tcase \"check\":\n\t\tcmdCheck()\n\tcase \"help\", \"-h\", \"--help\":\n\t\tusage()\n\tdefault:\n\t\tcmdAudit(cmd)\n\t}\n}\n\nfunc cmdAudit(pkg string) {\n\thistory := 5\n\tskillsDir := \"./skills\"\n\tverbose := false\n\tjsonOut := false\n\tcommit := \"\"\n\n\t// Parse remaining flags after the package name\n\targs := os.Args[2:]\n\tfor i := 0; i < len(args); i++ {\n\t\tswitch args[i] {\n\t\tcase \"--history\":\n\t\t\tif i+1 < len(args) {\n\t\t\t\thistory, _ = strconv.Atoi(args[i+1])\n\t\t\t\ti++\n\t\t\t}\n\t\tcase \"--skills-dir\":\n\t\t\tif i+1 < len(args) {\n\t\t\t\tskillsDir = args[i+1]\n\t\t\t\ti++\n\t\t\t}\n\t\tcase \"--commit\":\n\t\t\tif i+1 < len(args) {\n\t\t\t\tcommit = args[i+1]\n\t\t\t\ti++\n\t\t\t}\n\t\tcase \"--json\":\n\t\t\tjsonOut = true\n\t\tcase \"-v\", \"--verbose\":\n\t\t\tverbose = true\n\t\t}\n\t}\n\tif history < 2 {\n\t\thistory = 2\n\t}\n\n\tapiKey := audit.APIKey()\n\tif apiKey == \"\" {\n\t\tfmt.Fprintln(os.Stderr, \"error: DEEPSEEK_API_KEY not set\")\n\t\tos.Exit(1)\n\t}\n\n\ta := audit.New(apiKey, skillsDir, loadConfig())\n\tif verbose {\n\t\ta.Verbose(os.Stderr)\n\t}\n\tctx := context.Background()\n\n\tvar result *audit.PackageResult\n\tvar err error\n\tif commit != \"\" {\n\t\tresult, err = a.AuditCommit(ctx, pkg, commit, history)\n\t} else {\n\t\tresult, err = a.AuditHistory(ctx, pkg, history)\n\t}\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\n\tif jsonOut {\n\t\tprintResultJSON(result)\n\t} else {\n\t\tprintResult(result)\n\t}\n}\n\nfunc cmdCheck() {\n\tskillsDir := \"./skills\"\n\tverbose := false\n\tjsonOut := false\n\targs := os.Args[2:]\n\tfor i := 0; i < len(args); i++ {\n\t\tif args[i] == \"--skills-dir\" && i+1 < len(args) {\n\t\t\tskillsDir = args[i+1]\n\t\t\ti++\n\t\t} else if args[i] == \"-v\" || args[i] == \"--verbose\" {\n\t\t\tverbose = true\n\t\t} else if args[i] == \"--json\" {\n\t\t\tjsonOut = true\n\t\t}\n\t}\n\n\tapiKey := audit.APIKey()\n\tif apiKey == \"\" {\n\t\tfmt.Fprintln(os.Stderr, \"error: DEEPSEEK_API_KEY not set\")\n\t\tos.Exit(1)\n\t}\n\n\tupdates, err := audit.ListUpdates()\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\n\tif len(updates) == 0 {\n\t\tif !jsonOut {\n\t\t\tfmt.Println(\"No AUR packages with pending updates found.\")\n\t\t}\n\t\treturn\n\t}\n\n\tif !jsonOut {\n\t\tfmt.Printf(\"\\nFound %d package(s) with pending updates:\\n\", len(updates))\n\t\tfor _, u := range updates {\n\t\t\tfmt.Printf(\" %s %s → %s\\n\", u.Name, u.InstalledVersion, u.AURVersion)\n\t\t}\n\t\tfmt.Println()\n\t}\n\n\ta := audit.New(apiKey, skillsDir, loadConfig())\n\tif verbose {\n\t\ta.Verbose(os.Stderr)\n\t}\n\n\tprogress := func(pkg, verdict string) {\n\t\tif jsonOut {\n\t\t\treturn\n\t\t}\n\t\tif verdict == \"ERROR\" {\n\t\t\tfmt.Printf(\" %s — ERROR\\n\", pkg)\n\t\t} else {\n\t\t\tfmt.Printf(\" %s — %s\\n\", pkg, verdict)\n\t\t}\n\t}\n\n\tctx := context.Background()\n\tresults := a.AuditUpdateList(ctx, updates, progress)\n\n\tif len(results) == 0 {\n\t\tfmt.Println(\"No AUR packages with pending updates found.\")\n\t\treturn\n\t}\n\n\tif jsonOut {\n\t\tprintResultsJSON(results)\n\t\treturn\n\t}\n\n\tfmt.Printf(\"\\nAudited %d package(s) with pending updates:\\n\\n\", len(results))\n\n\t// Summary table\n\tw := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)\n\tfmt.Fprintln(w, \"PKG\\tVERDICT\\tCONFIDENCE\\tSUMMARY\")\n\tfmt.Fprintln(w, \"---\\t---\\t---\\t---\")\n\tfor _, r := range results {\n\t\tif r.Error != \"\" {\n\t\t\tfmt.Fprintf(w, \"%s\\tERROR\\t-\\t%s\\n\", r.Package, r.Error)\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Fprintf(w, \"%s\\t%s\\t%s\\t%s\\n\", r.Package, r.Verdict.Verdict, r.Verdict.Confidence, truncate(r.Verdict.Summary, 80))\n\t}\n\tw.Flush()\n\n\t// Detailed findings\n\tfmt.Println()\n\tfor _, r := range results {\n\t\tif r.Error != \"\" {\n\t\t\tfmt.Printf(\"\\n── %s (ERROR) ──\\n %s\\n\", r.Package, r.Error)\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Printf(\"\\n── %s (%s, %s confidence) ──\\n\", r.Package, r.Verdict.Verdict, r.Verdict.Confidence)\n\t\tfmt.Println(r.Verdict.Summary)\n\t\tif len(r.Verdict.Findings) == 0 {\n\t\t\tfmt.Println(\"\\n No findings.\")\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Println()\n\t\tfor _, f := range r.Verdict.Findings {\n\t\t\tseverity := severityLabel(f.Severity)\n\t\t\tfmt.Printf(\" %s [%s] line %d: %s\\n\", severity, f.TTP, f.Line, f.Detail)\n\t\t}\n\t}\n}\n\nfunc printResult(r *audit.PackageResult) {\n\ticon := verdictLabel(r.Verdict.Verdict)\n\tfmt.Printf(\"\\n%s %s — %s (confidence: %s)\\n\", icon, r.Package, r.Verdict.Verdict, r.Verdict.Confidence)\n\tfmt.Println(strings.Repeat(\"─\", 60))\n\tfmt.Println(r.Verdict.Summary)\n\tfmt.Println()\n\n\tif len(r.Verdict.Findings) == 0 {\n\t\tfmt.Println(\"No findings.\")\n\t\treturn\n\t}\n\n\tfor _, f := range r.Verdict.Findings {\n\t\tif f.Detail == \"\" && f.TTP == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tseverity := severityLabel(f.Severity)\n\t\tfmt.Printf(\" %s [%s] line %d: %s\\n\", severity, f.TTP, f.Line, f.Detail)\n\t}\n\tfmt.Println()\n}\n\nfunc severityLabel(s string) string {\n\tswitch s {\n\tcase \"CRITICAL\":\n\t\treturn \"CRITICAL\"\n\tcase \"HIGH\":\n\t\treturn \"HIGH\"\n\tcase \"MEDIUM\":\n\t\treturn \"MEDIUM\"\n\tcase \"LOW\":\n\t\treturn \"LOW\"\n\tdefault:\n\t\treturn s\n\t}\n}\n\nfunc verdictLabel(v string) string {\n\tswitch v {\n\tcase \"SAFE\":\n\t\treturn \"[SAFE]\"\n\tcase \"SUSPICIOUS\":\n\t\treturn \"[SUSPICIOUS]\"\n\tcase \"MALICIOUS\":\n\t\treturn \"[MALICIOUS]\"\n\tdefault:\n\t\treturn \"[?]\"\n\t}\n}\n\nfunc truncate(s string, n int) string {\n\tif len(s) <= n {\n\t\treturn s\n\t}\n\treturn s[:n-3] + \"...\"\n}\n\nfunc printResultJSON(r *audit.PackageResult) {\n\tjson.NewEncoder(os.Stdout).Encode(r)\n}\n\nfunc printResultsJSON(results []*audit.PackageResult) {\n\tjson.NewEncoder(os.Stdout).Encode(results)\n}\n\nfunc loadConfig() audit.Config {\n\treturn audit.LoadConfig(\n\t\t\"./configs/aurdit.yaml\",\n\t\tos.ExpandEnv(\"$HOME/.config/aurdit/config.yaml\"),\n\t)\n}\n\nfunc usage() {\n\tfmt.Println(`aurdit — audit AUR PKGBUILDs for malicious changes\n\nUsage:\n aurdit [--history 5] [--commit ] [--skills-dir ./skills] [--verbose]\n Audit a package by comparing recent PKGBUILD versions to detect\n anomalous changes. Defaults to the last 5 versions.\n With --commit, center the comparison around a specific commit hash.\n\n aurdit check [--skills-dir ./skills] [--verbose]\n Discover all installed AUR packages with pending updates and audit\n each one, printing a summary.\n\nEnvironment:\n DEEPSEEK_API_KEY DeepSeek API key (required)`)\n}\n"}, "ec5b9401e1310a78e245ce40e469ca9639f490db": {"t": 1785755375.290546, "n": 6124, "h": "b3b4db3f9ecaa247", "c": "package main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"text/tabwriter\"\n\n\t\"github.com/Alfredooe/aurdit/internal/audit\"\n)\n\nfunc main() {\n\tif len(os.Args) < 2 {\n\t\tusage()\n\t\tos.Exit(1)\n\t}\n\n\tcmd := os.Args[1]\n\n\tswitch cmd {\n\tcase \"check\":\n\t\tcmdCheck()\n\tcase \"help\", \"-h\", \"--help\":\n\t\tusage()\n\tdefault:\n\t\tcmdAudit(cmd)\n\t}\n}\n\nfunc cmdAudit(pkg string) {\n\thistory := 5\n\tskillsDir := \"./skills\"\n\tverbose := false\n\tjsonOut := false\n\tcommit := \"\"\n\n\t// Parse remaining flags after the package name\n\targs := os.Args[2:]\n\tfor i := 0; i < len(args); i++ {\n\t\tswitch args[i] {\n\t\tcase \"--history\":\n\t\t\tif i+1 < len(args) {\n\t\t\t\thistory, _ = strconv.Atoi(args[i+1])\n\t\t\t\ti++\n\t\t\t}\n\t\tcase \"--skills-dir\":\n\t\t\tif i+1 < len(args) {\n\t\t\t\tskillsDir = args[i+1]\n\t\t\t\ti++\n\t\t\t}\n\t\tcase \"--commit\":\n\t\t\tif i+1 < len(args) {\n\t\t\t\tcommit = args[i+1]\n\t\t\t\ti++\n\t\t\t}\n\t\tcase \"--json\":\n\t\t\tjsonOut = true\n\t\tcase \"-v\", \"--verbose\":\n\t\t\tverbose = true\n\t\t}\n\t}\n\tif history < 2 {\n\t\thistory = 2\n\t}\n\n\tapiKey := audit.APIKey()\n\tif apiKey == \"\" {\n\t\tfmt.Fprintln(os.Stderr, \"error: DEEPSEEK_API_KEY not set\")\n\t\tos.Exit(1)\n\t}\n\n\ta := audit.New(apiKey, skillsDir, loadConfig())\n\tif verbose {\n\t\ta.Verbose(os.Stderr)\n\t}\n\tctx := context.Background()\n\n\tvar result *audit.PackageResult\n\tvar err error\n\tif commit != \"\" {\n\t\tresult, err = a.AuditCommit(ctx, pkg, commit, history)\n\t} else {\n\t\tresult, err = a.AuditHistory(ctx, pkg, history)\n\t}\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\n\tif jsonOut {\n\t\tprintResultJSON(result)\n\t} else {\n\t\tprintResult(result)\n\t}\n}\n\nfunc cmdCheck() {\n\tskillsDir := \"./skills\"\n\tverbose := false\n\tjsonOut := false\n\targs := os.Args[2:]\n\tfor i := 0; i < len(args); i++ {\n\t\tif args[i] == \"--skills-dir\" && i+1 < len(args) {\n\t\t\tskillsDir = args[i+1]\n\t\t\ti++\n\t\t} else if args[i] == \"-v\" || args[i] == \"--verbose\" {\n\t\t\tverbose = true\n\t\t} else if args[i] == \"--json\" {\n\t\t\tjsonOut = true\n\t\t}\n\t}\n\n\tapiKey := audit.APIKey()\n\tif apiKey == \"\" {\n\t\tfmt.Fprintln(os.Stderr, \"error: DEEPSEEK_API_KEY not set\")\n\t\tos.Exit(1)\n\t}\n\n\tupdates, err := audit.ListUpdates()\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\n\tif len(updates) == 0 {\n\t\tif !jsonOut {\n\t\t\tfmt.Println(\"No AUR packages with pending updates found.\")\n\t\t}\n\t\treturn\n\t}\n\n\tif !jsonOut {\n\t\tfmt.Printf(\"\\nFound %d package(s) with pending updates:\\n\", len(updates))\n\t\tfor _, u := range updates {\n\t\t\tfmt.Printf(\" %s %s → %s\\n\", u.Name, u.InstalledVersion, u.AURVersion)\n\t\t}\n\t\tfmt.Println()\n\t}\n\n\ta := audit.New(apiKey, skillsDir, loadConfig())\n\tif verbose {\n\t\ta.Verbose(os.Stderr)\n\t}\n\n\tprogress := func(pkg, verdict string) {\n\t\tif jsonOut {\n\t\t\treturn\n\t\t}\n\t\tif verdict == \"ERROR\" {\n\t\t\tfmt.Printf(\" %s — ERROR\\n\", pkg)\n\t\t} else {\n\t\t\tfmt.Printf(\" %s — %s\\n\", pkg, verdict)\n\t\t}\n\t}\n\n\tctx := context.Background()\n\tresults := a.AuditUpdateList(ctx, updates, progress)\n\n\tif len(results) == 0 {\n\t\tfmt.Println(\"No AUR packages with pending updates found.\")\n\t\treturn\n\t}\n\n\tif jsonOut {\n\t\tprintResultsJSON(results)\n\t\treturn\n\t}\n\n\tfmt.Printf(\"\\nAudited %d package(s) with pending updates:\\n\\n\", len(results))\n\n\t// Summary table\n\tw := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)\n\tfmt.Fprintln(w, \"PKG\\tVERDICT\\tCONFIDENCE\\tSUMMARY\")\n\tfmt.Fprintln(w, \"---\\t---\\t---\\t---\")\n\tfor _, r := range results {\n\t\tif r.Error != \"\" {\n\t\t\tfmt.Fprintf(w, \"%s\\tERROR\\t-\\t%s\\n\", r.Package, r.Error)\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Fprintf(w, \"%s\\t%s\\t%s\\t%s\\n\", r.Package, r.Verdict.Verdict, r.Verdict.Confidence, truncate(r.Verdict.Summary, 80))\n\t}\n\tw.Flush()\n\n\t// Detailed findings\n\tfmt.Println()\n\tfor _, r := range results {\n\t\tif r.Error != \"\" {\n\t\t\tfmt.Printf(\"\\n── %s (ERROR) ──\\n %s\\n\", r.Package, r.Error)\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Printf(\"\\n── %s (%s, %s confidence) ──\\n\", r.Package, r.Verdict.Verdict, r.Verdict.Confidence)\n\t\tfmt.Println(r.Verdict.Summary)\n\t\tif len(r.Verdict.Findings) == 0 {\n\t\t\tfmt.Println(\"\\n No findings.\")\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Println()\n\t\tfor _, f := range r.Verdict.Findings {\n\t\t\tseverity := severityLabel(f.Severity)\n\t\t\tfmt.Printf(\" %s [%s] line %d: %s\\n\", severity, f.TTP, f.Line, f.Detail)\n\t\t}\n\t}\n}\n\nfunc printResult(r *audit.PackageResult) {\n\ticon := verdictLabel(r.Verdict.Verdict)\n\tfmt.Printf(\"\\n%s %s — %s (confidence: %s)\\n\", icon, r.Package, r.Verdict.Verdict, r.Verdict.Confidence)\n\tfmt.Println(strings.Repeat(\"─\", 60))\n\tfmt.Println(r.Verdict.Summary)\n\tfmt.Println()\n\n\tif len(r.Verdict.Findings) == 0 {\n\t\tfmt.Println(\"No findings.\")\n\t\treturn\n\t}\n\n\tfor _, f := range r.Verdict.Findings {\n\t\tif f.Detail == \"\" && f.TTP == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tseverity := severityLabel(f.Severity)\n\t\tfmt.Printf(\" %s [%s] line %d: %s\\n\", severity, f.TTP, f.Line, f.Detail)\n\t}\n\tfmt.Println()\n}\n\nfunc severityLabel(s string) string {\n\tswitch s {\n\tcase \"CRITICAL\":\n\t\treturn \"CRITICAL\"\n\tcase \"HIGH\":\n\t\treturn \"HIGH\"\n\tcase \"MEDIUM\":\n\t\treturn \"MEDIUM\"\n\tcase \"LOW\":\n\t\treturn \"LOW\"\n\tdefault:\n\t\treturn s\n\t}\n}\n\nfunc verdictLabel(v string) string {\n\tswitch v {\n\tcase \"SAFE\":\n\t\treturn \"[SAFE]\"\n\tcase \"SUSPICIOUS\":\n\t\treturn \"[SUSPICIOUS]\"\n\tcase \"MALICIOUS\":\n\t\treturn \"[MALICIOUS]\"\n\tdefault:\n\t\treturn \"[?]\"\n\t}\n}\n\nfunc truncate(s string, n int) string {\n\tif len(s) <= n {\n\t\treturn s\n\t}\n\treturn s[:n-3] + \"...\"\n}\n\nfunc printResultJSON(r *audit.PackageResult) {\n\tjson.NewEncoder(os.Stdout).Encode(r)\n}\n\nfunc printResultsJSON(results []*audit.PackageResult) {\n\tjson.NewEncoder(os.Stdout).Encode(results)\n}\n\nfunc loadConfig() audit.Config {\n\treturn audit.LoadConfig(\n\t\t\"./configs/aurdit.yaml\",\n\t\tos.ExpandEnv(\"$HOME/.config/aurdit/config.yaml\"),\n\t)\n}\n\nfunc usage() {\n\tfmt.Println(`aurdit — audit AUR PKGBUILDs for malicious changes\n\nUsage:\n aurdit [--history 5] [--commit ] [--skills-dir ./skills] [--verbose]\n Audit a package by comparing recent PKGBUILD versions to detect\n anomalous changes. Defaults to the last 5 versions.\n With --commit, center the comparison around a specific commit hash.\n\n aurdit check [--skills-dir ./skills] [--verbose]\n Discover all installed AUR packages with pending updates and audit\n each one, printing a summary.\n\nEnvironment:\n DEEPSEEK_API_KEY DeepSeek API key (required)`)\n}\n"}, "a695e50a3131ffe3091cc5f69782ceebd9b73959": {"t": 1785755376.9046326, "n": 6235, "h": "a017b6280ac58ccf", "c": "package main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"text/tabwriter\"\n\n\t\"github.com/Alfredooe/aurdit/internal/audit\"\n)\n\nfunc main() {\n\tif len(os.Args) < 2 {\n\t\tusage()\n\t\tos.Exit(1)\n\t}\n\n\tcmd := os.Args[1]\n\n\tswitch cmd {\n\tcase \"check\":\n\t\tcmdCheck()\n\tcase \"help\", \"-h\", \"--help\":\n\t\tusage()\n\tdefault:\n\t\tcmdAudit(cmd)\n\t}\n}\n\nfunc cmdAudit(pkg string) {\n\thistory := 5\n\tskillsDir := \"./skills\"\n\tverbose := false\n\tjsonOut := false\n\tcommit := \"\"\n\n\t// Parse remaining flags after the package name\n\targs := os.Args[2:]\n\tfor i := 0; i < len(args); i++ {\n\t\tswitch args[i] {\n\t\tcase \"--history\":\n\t\t\tif i+1 < len(args) {\n\t\t\t\thistory, _ = strconv.Atoi(args[i+1])\n\t\t\t\ti++\n\t\t\t}\n\t\tcase \"--skills-dir\":\n\t\t\tif i+1 < len(args) {\n\t\t\t\tskillsDir = args[i+1]\n\t\t\t\ti++\n\t\t\t}\n\t\tcase \"--commit\":\n\t\t\tif i+1 < len(args) {\n\t\t\t\tcommit = args[i+1]\n\t\t\t\ti++\n\t\t\t}\n\t\tcase \"--json\":\n\t\t\tjsonOut = true\n\t\tcase \"-v\", \"--verbose\":\n\t\t\tverbose = true\n\t\t}\n\t}\n\tif history < 2 {\n\t\thistory = 2\n\t}\n\n\tapiKey := audit.APIKey()\n\tif apiKey == \"\" {\n\t\tfmt.Fprintln(os.Stderr, \"error: DEEPSEEK_API_KEY not set\")\n\t\tos.Exit(1)\n\t}\n\n\ta := audit.New(apiKey, skillsDir, loadConfig())\n\tif verbose {\n\t\ta.Verbose(os.Stderr)\n\t}\n\tctx := context.Background()\n\n\tvar result *audit.PackageResult\n\tvar err error\n\tif history < 2 {\n\t\thistory = 2\n\t}\n\tif commit != \"\" {\n\t\tresult, err = a.AuditCommit(ctx, pkg, commit, history)\n\t} else {\n\t\tresult, err = a.AuditHistory(ctx, pkg, history)\n\t}\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\n\tif jsonOut {\n\t\tprintResultJSON(result)\n\t} else {\n\t\tprintResult(result)\n\t}\n}\n\nfunc cmdCheck() {\n\tskillsDir := \"./skills\"\n\tverbose := false\n\tjsonOut := false\n\targs := os.Args[2:]\n\tfor i := 0; i < len(args); i++ {\n\t\tif args[i] == \"--skills-dir\" && i+1 < len(args) {\n\t\t\tskillsDir = args[i+1]\n\t\t\ti++\n\t\t} else if args[i] == \"-v\" || args[i] == \"--verbose\" {\n\t\t\tverbose = true\n\t\t} else if args[i] == \"--json\" {\n\t\t\tjsonOut = true\n\t\t}\n\t}\n\n\tapiKey := audit.APIKey()\n\tif apiKey == \"\" {\n\t\tfmt.Fprintln(os.Stderr, \"error: DEEPSEEK_API_KEY not set\")\n\t\tos.Exit(1)\n\t}\n\n\tupdates, err := audit.ListUpdates()\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\n\tif len(updates) == 0 {\n\t\tif !jsonOut {\n\t\t\tfmt.Println(\"No AUR packages with pending updates found.\")\n\t\t}\n\t\treturn\n\t}\n\n\tif !jsonOut {\n\t\tfmt.Printf(\"\\nFound %d package(s) with pending updates:\\n\", len(updates))\n\t\tfor _, u := range updates {\n\t\t\tfmt.Printf(\" %s %s → %s\\n\", u.Name, u.InstalledVersion, u.AURVersion)\n\t\t}\n\t\tfmt.Println()\n\t}\n\n\ta := audit.New(apiKey, skillsDir, loadConfig())\n\tif verbose {\n\t\ta.Verbose(os.Stderr)\n\t}\n\n\tprogress := func(pkg, verdict string) {\n\t\tif jsonOut {\n\t\t\treturn\n\t\t}\n\t\tif verdict == \"ERROR\" {\n\t\t\tfmt.Printf(\" %s — ERROR\\n\", pkg)\n\t\t} else {\n\t\t\tfmt.Printf(\" %s — %s\\n\", pkg, verdict)\n\t\t}\n\t}\n\n\tctx := context.Background()\n\tresults := a.AuditUpdateList(ctx, updates, progress)\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\n\tif len(results) == 0 {\n\t\tfmt.Println(\"No AUR packages with pending updates found.\")\n\t\treturn\n\t}\n\n\tif jsonOut {\n\t\tprintResultsJSON(results)\n\t\treturn\n\t}\n\n\tfmt.Printf(\"\\nAudited %d package(s) with pending updates:\\n\\n\", len(results))\n\n\t// Summary table\n\tw := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)\n\tfmt.Fprintln(w, \"PKG\\tVERDICT\\tCONFIDENCE\\tSUMMARY\")\n\tfmt.Fprintln(w, \"---\\t---\\t---\\t---\")\n\tfor _, r := range results {\n\t\tif r.Error != \"\" {\n\t\t\tfmt.Fprintf(w, \"%s\\tERROR\\t-\\t%s\\n\", r.Package, r.Error)\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Fprintf(w, \"%s\\t%s\\t%s\\t%s\\n\", r.Package, r.Verdict.Verdict, r.Verdict.Confidence, truncate(r.Verdict.Summary, 80))\n\t}\n\tw.Flush()\n\n\t// Detailed findings\n\tfmt.Println()\n\tfor _, r := range results {\n\t\tif r.Error != \"\" {\n\t\t\tfmt.Printf(\"\\n── %s (ERROR) ──\\n %s\\n\", r.Package, r.Error)\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Printf(\"\\n── %s (%s, %s confidence) ──\\n\", r.Package, r.Verdict.Verdict, r.Verdict.Confidence)\n\t\tfmt.Println(r.Verdict.Summary)\n\t\tif len(r.Verdict.Findings) == 0 {\n\t\t\tfmt.Println(\"\\n No findings.\")\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Println()\n\t\tfor _, f := range r.Verdict.Findings {\n\t\t\tseverity := colorSeverity(f.Severity)\n\t\t\tfmt.Printf(\" %s [%s] line %d: %s\\n\", severity, f.TTP, f.Line, f.Detail)\n\t\t}\n\t}\n}\n\nfunc printResult(r *audit.PackageResult) {\n\ticon := verdictIcon(r.Verdict.Verdict)\n\tfmt.Printf(\"\\n%s %s — %s (confidence: %s)\\n\", icon, r.Package, r.Verdict.Verdict, r.Verdict.Confidence)\n\tfmt.Println(strings.Repeat(\"─\", 60))\n\tfmt.Println(r.Verdict.Summary)\n\tfmt.Println()\n\n\tif len(r.Verdict.Findings) == 0 {\n\t\tfmt.Println(\"No findings.\")\n\t\treturn\n\t}\n\n\tfor _, f := range r.Verdict.Findings {\n\t\tif f.Detail == \"\" && f.TTP == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tseverity := colorSeverity(f.Severity)\n\t\tfmt.Printf(\" %s [%s] line %d: %s\\n\", severity, f.TTP, f.Line, f.Detail)\n\t}\n\tfmt.Println()\n}\n\nfunc colorSeverity(s string) string {\n\tswitch s {\n\tcase \"CRITICAL\":\n\t\treturn \"CRITICAL\"\n\tcase \"HIGH\":\n\t\treturn \"HIGH\"\n\tcase \"MEDIUM\":\n\t\treturn \"MEDIUM\"\n\tcase \"LOW\":\n\t\treturn \"LOW\"\n\tdefault:\n\t\treturn s\n\t}\n}\n\nfunc verdictIcon(v string) string {\n\tswitch v {\n\tcase \"SAFE\":\n\t\treturn \"[SAFE]\"\n\tcase \"SUSPICIOUS\":\n\t\treturn \"[SUSPICIOUS]\"\n\tcase \"MALICIOUS\":\n\t\treturn \"[MALICIOUS]\"\n\tdefault:\n\t\treturn \"[?]\"\n\t}\n}\n\nfunc truncate(s string, n int) string {\n\tif len(s) <= n {\n\t\treturn s\n\t}\n\treturn s[:n-3] + \"...\"\n}\n\nfunc printResultJSON(r *audit.PackageResult) {\n\tjson.NewEncoder(os.Stdout).Encode(r)\n}\n\nfunc printResultsJSON(results []*audit.PackageResult) {\n\tjson.NewEncoder(os.Stdout).Encode(results)\n}\n\nfunc loadConfig() audit.Config {\n\treturn audit.LoadConfig(\n\t\t\"./configs/aurdit.yaml\",\n\t\tos.ExpandEnv(\"$HOME/.config/aurdit/config.yaml\"),\n\t)\n}\n\nfunc usage() {\n\tfmt.Println(`aurdit — audit AUR PKGBUILDs for malicious changes\n\nUsage:\n aurdit [--history 5] [--commit ] [--skills-dir ./skills] [--verbose]\n Audit a package by comparing recent PKGBUILD versions to detect\n anomalous changes. Defaults to the last 5 versions.\n With --commit, center the comparison around a specific commit hash.\n\n aurdit check [--skills-dir ./skills] [--verbose]\n Discover all installed AUR packages with pending updates and audit\n each one, printing a summary.\n\nEnvironment:\n DEEPSEEK_API_KEY DeepSeek API key (required)`)\n}\n"}}}}