@methiu/raigal 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -108,7 +108,28 @@ const getProviderStatuses = () => PROVIDERS.map((provider) => {
108
108
  };
109
109
  });
110
110
  const resolveProvider = (selection, statuses = getProviderStatuses()) => {
111
- if (selection !== "auto") return statuses.find((status) => status.provider.id === selection) ?? null;
111
+ if (selection !== "auto") {
112
+ const found = statuses.find((status) => status.provider.id === selection);
113
+ if (found) return found;
114
+ if (commandExists(selection)) return {
115
+ provider: {
116
+ id: selection,
117
+ label: selection,
118
+ bin: selection,
119
+ loginCommand: {
120
+ command: selection,
121
+ args: ["login"]
122
+ },
123
+ loginHint: `Run \`${selection} login\`.`,
124
+ buildArgs: (prompt) => [prompt]
125
+ },
126
+ installed: true,
127
+ authenticated: true,
128
+ version: readVersion(selection),
129
+ authHint: null
130
+ };
131
+ return null;
132
+ }
112
133
  return statuses.find((status) => status.installed && status.authenticated !== false) ?? statuses.find((status) => status.installed) ?? null;
113
134
  };
114
135
  //#endregion
@@ -863,7 +884,7 @@ const createLogger = (deps = {}) => {
863
884
  const log = createLogger();
864
885
  //#endregion
865
886
  //#region src/version.ts
866
- const APP_VERSION = "1.0.0";
887
+ const APP_VERSION = "1.0.2";
867
888
  //#endregion
868
889
  //#region src/commands/agent-connect.ts
869
890
  const commandText = (status) => [status.provider.loginCommand.command, ...status.provider.loginCommand.args].join(" ");
@@ -1058,7 +1079,8 @@ const Finding = z.object({
1058
1079
  severity: Severity,
1059
1080
  path: z.string().max(500),
1060
1081
  line: z.number().int().nonnegative(),
1061
- fingerprint: z.string().length(32)
1082
+ fingerprint: z.string().length(32),
1083
+ message: z.string().max(500).optional()
1062
1084
  });
1063
1085
  const RepoRef = z.object({
1064
1086
  provider: z.literal("github"),
@@ -1073,12 +1095,21 @@ const Step = z.object({
1073
1095
  ms: z.number().int().nonnegative(),
1074
1096
  counts: z.record(z.string(), z.number()).optional()
1075
1097
  });
1098
+ const AgentTelemetry = z.object({
1099
+ provider: z.string().max(80),
1100
+ before_score: z.number().min(0).max(100).nullable(),
1101
+ after_score: z.number().min(0).max(100).nullable(),
1102
+ pr_url: z.string().url().max(500).optional(),
1103
+ patch_diff: z.string().max(2e4).optional(),
1104
+ duration_ms: z.number().int().nonnegative().optional()
1105
+ });
1076
1106
  const Report = z.object({
1077
1107
  score: z.number().min(0).max(100).nullable(),
1078
1108
  scoreable: z.boolean(),
1079
1109
  engine_scores: z.record(z.string(), z.number()).optional(),
1080
1110
  files_scanned: z.number().int().nonnegative(),
1081
- findings: z.array(Finding).max(5e3)
1111
+ findings: z.array(Finding).max(5e3),
1112
+ agent_telemetry: AgentTelemetry.optional()
1082
1113
  });
1083
1114
  const Run = z.object({
1084
1115
  run_id: z.uuid(),
@@ -1095,7 +1126,13 @@ const Run = z.object({
1095
1126
  branch: z.string().max(200).optional(),
1096
1127
  head_sha: z.string().max(64).optional(),
1097
1128
  base_ref: z.string().max(200).optional(),
1098
- pr_number: z.number().int().optional()
1129
+ pr_number: z.number().int().optional(),
1130
+ diff_summary: z.object({
1131
+ files_changed: z.number().int().nonnegative(),
1132
+ additions: z.number().int().nonnegative(),
1133
+ deletions: z.number().int().nonnegative()
1134
+ }).optional(),
1135
+ diff_preview: z.string().max(1e4).optional()
1099
1136
  }),
1100
1137
  ci: z.object({
1101
1138
  provider: z.string().max(40),
@@ -1111,7 +1148,8 @@ const Run = z.object({
1111
1148
  ended_at: z.iso.datetime(),
1112
1149
  exit_code: z.number().int(),
1113
1150
  steps: z.array(Step).max(200),
1114
- report: Report.optional()
1151
+ report: Report.optional(),
1152
+ agent_telemetry: AgentTelemetry.optional()
1115
1153
  });
1116
1154
  const Policy = z.object({
1117
1155
  version: z.number().int(),
@@ -1132,7 +1170,7 @@ const AllowedOwner = z.object({
1132
1170
  login: z.string().max(100)
1133
1171
  });
1134
1172
  const EntitlementClaims = z.object({
1135
- iss: z.literal("https://app.raigal.dev"),
1173
+ iss: z.literal(process.env.RAIGAL_ISSUER?.trim() || "https://app.raigal.dev"),
1136
1174
  sub: z.string(),
1137
1175
  org_name: z.string(),
1138
1176
  plan: z.enum(["trial", "active"]),
@@ -1345,7 +1383,7 @@ const verifyEntitlementJwt = (jwt, options = {}) => {
1345
1383
  const claims = parsed.data;
1346
1384
  const expectedIssuer = process.env.RAIGAL_ISSUER?.trim() || "https://app.raigal.dev";
1347
1385
  if (claims.iss !== expectedIssuer) throw new JwtVerificationError(`Untrusted issuer: ${claims.iss}`, "invalid_issuer");
1348
- if (!isVersionAtLeast("1.0.0", claims.min_cli_version)) throw new JwtVerificationError(`CLI version ${APP_VERSION} is below required minimum ${claims.min_cli_version}. Run "raigal update" or "npm install -g @methiu/raigal@latest".`, "cli_version_unsupported");
1386
+ if (!isVersionAtLeast("1.0.2", claims.min_cli_version)) throw new JwtVerificationError(`CLI version ${APP_VERSION} is below required minimum ${claims.min_cli_version}. Run "raigal update" or "npm install -g @methiu/raigal@latest".`, "cli_version_unsupported");
1349
1387
  const now = options.nowSeconds ?? Math.floor(Date.now() / 1e3);
1350
1388
  const grace = options.allowGraceSeconds ?? 0;
1351
1389
  if (now > claims.exp + grace) {
@@ -2575,6 +2613,385 @@ var AgentTui = class {
2575
2613
  }
2576
2614
  };
2577
2615
  //#endregion
2616
+ //#region src/cloud/context.ts
2617
+ const execGit = (args, cwd) => {
2618
+ try {
2619
+ const res = spawnSync("git", args, {
2620
+ cwd: path.resolve(cwd),
2621
+ encoding: "utf-8",
2622
+ stdio: [
2623
+ "ignore",
2624
+ "pipe",
2625
+ "ignore"
2626
+ ]
2627
+ });
2628
+ if (res.error || res.status !== 0) return void 0;
2629
+ return res.stdout.trim() || void 0;
2630
+ } catch {
2631
+ return;
2632
+ }
2633
+ };
2634
+ const detectDefaultBranch = (directory) => {
2635
+ try {
2636
+ const out = execGit(["symbolic-ref", "refs/remotes/origin/HEAD"], directory);
2637
+ if (out && out.startsWith("refs/remotes/origin/")) return out.replace("refs/remotes/origin/", "").trim();
2638
+ } catch {}
2639
+ return "main";
2640
+ };
2641
+ const detectGitDiff = (directory, baseRef) => {
2642
+ const targetBase = baseRef || detectDefaultBranch(directory);
2643
+ try {
2644
+ let shortstat = execGit([
2645
+ "diff",
2646
+ "--shortstat",
2647
+ `${targetBase}...HEAD`
2648
+ ], directory);
2649
+ if (!shortstat) shortstat = execGit([
2650
+ "diff",
2651
+ "--shortstat",
2652
+ `origin/${targetBase}...HEAD`
2653
+ ], directory);
2654
+ if (!shortstat) shortstat = execGit([
2655
+ "diff",
2656
+ "--shortstat",
2657
+ "HEAD~1"
2658
+ ], directory);
2659
+ let diffSummary;
2660
+ if (shortstat) {
2661
+ const filesMatch = shortstat.match(/(\d+)\s+files?\s+changed/);
2662
+ const addMatch = shortstat.match(/(\d+)\s+insertions?\s*\(\+\)/);
2663
+ const delMatch = shortstat.match(/(\d+)\s+deletions?\s*\(-\)/);
2664
+ diffSummary = {
2665
+ files_changed: filesMatch ? Number.parseInt(filesMatch[1], 10) : 1,
2666
+ additions: addMatch ? Number.parseInt(addMatch[1], 10) : 0,
2667
+ deletions: delMatch ? Number.parseInt(delMatch[1], 10) : 0
2668
+ };
2669
+ }
2670
+ let preview = execGit([
2671
+ "diff",
2672
+ "-U2",
2673
+ "--no-color",
2674
+ `${targetBase}...HEAD`
2675
+ ], directory);
2676
+ if (!preview) preview = execGit([
2677
+ "diff",
2678
+ "-U2",
2679
+ "--no-color",
2680
+ `origin/${targetBase}...HEAD`
2681
+ ], directory);
2682
+ if (!preview) preview = execGit([
2683
+ "diff",
2684
+ "-U2",
2685
+ "--no-color",
2686
+ "HEAD~1"
2687
+ ], directory);
2688
+ let diffPreview;
2689
+ if (preview) diffPreview = preview.split("\n").slice(0, 40).join("\n").slice(0, 1e4);
2690
+ return {
2691
+ diffSummary,
2692
+ diffPreview
2693
+ };
2694
+ } catch {
2695
+ return {};
2696
+ }
2697
+ };
2698
+ const getRootCommitHash = (directory) => {
2699
+ const hash = execGit([
2700
+ "rev-list",
2701
+ "--max-parents=0",
2702
+ "HEAD"
2703
+ ], directory);
2704
+ if (!hash) return void 0;
2705
+ const firstLine = hash.split("\n")[0]?.trim();
2706
+ return firstLine && /^[0-9a-f]{40,64}$/i.test(firstLine) ? firstLine : void 0;
2707
+ };
2708
+ const detectRepoRef = (directory, ownerId) => {
2709
+ const detected = detectLocalRepoOwner(directory);
2710
+ if (!detected.owner || !detected.repo) return null;
2711
+ return {
2712
+ provider: "github",
2713
+ slug: `${detected.owner}/${detected.repo}`.toLowerCase(),
2714
+ owner_id: ownerId,
2715
+ root_commit_hash: getRootCommitHash(directory)
2716
+ };
2717
+ };
2718
+ const readGitHubEventPayload = () => {
2719
+ const eventPath = process.env.GITHUB_EVENT_PATH;
2720
+ if (!eventPath || !fs.existsSync(eventPath)) return null;
2721
+ try {
2722
+ const content = fs.readFileSync(eventPath, "utf-8");
2723
+ return JSON.parse(content);
2724
+ } catch {
2725
+ return null;
2726
+ }
2727
+ };
2728
+ const detectGitContext = (directory) => {
2729
+ const eventPayload = readGitHubEventPayload();
2730
+ const pr = eventPayload?.pull_request;
2731
+ let headSha = pr?.head?.sha;
2732
+ if (!headSha) headSha = process.env.GITHUB_SHA || execGit(["rev-parse", "HEAD"], directory);
2733
+ let branch = process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME;
2734
+ if (!branch) {
2735
+ branch = execGit([
2736
+ "rev-parse",
2737
+ "--abbrev-ref",
2738
+ "HEAD"
2739
+ ], directory);
2740
+ if (branch === "HEAD") branch = void 0;
2741
+ }
2742
+ let baseRef = pr?.base?.ref || process.env.GITHUB_BASE_REF;
2743
+ if (!baseRef) baseRef = void 0;
2744
+ let prNumber;
2745
+ if (typeof pr?.number === "number") prNumber = pr.number;
2746
+ else if (typeof eventPayload?.number === "number") prNumber = eventPayload.number;
2747
+ else if (process.env.GITHUB_REF) {
2748
+ const match = process.env.GITHUB_REF.match(/^refs\/pull\/(\d+)\//);
2749
+ if (match && match[1]) prNumber = Number.parseInt(match[1], 10);
2750
+ }
2751
+ const result = {};
2752
+ if (branch) result.branch = branch;
2753
+ if (headSha) result.head_sha = headSha;
2754
+ if (baseRef) result.base_ref = baseRef;
2755
+ if (prNumber !== void 0 && !Number.isNaN(prNumber)) result.pr_number = prNumber;
2756
+ const { diffSummary, diffPreview } = detectGitDiff(directory, baseRef);
2757
+ if (diffSummary) result.diff_summary = diffSummary;
2758
+ if (diffPreview) result.diff_preview = diffPreview;
2759
+ return result;
2760
+ };
2761
+ const detectCiContext = () => {
2762
+ if (process.env.GITHUB_ACTIONS === "true" || process.env.GITHUB_ACTIONS === "1") {
2763
+ const serverUrl = process.env.GITHUB_SERVER_URL || "https://github.com";
2764
+ const repo = process.env.GITHUB_REPOSITORY;
2765
+ const runId = process.env.GITHUB_RUN_ID;
2766
+ let runUrl;
2767
+ if (repo && runId) {
2768
+ const attempt = process.env.GITHUB_RUN_ATTEMPT;
2769
+ runUrl = `${serverUrl}/${repo}/actions/runs/${runId}${attempt ? `/attempts/${attempt}` : ""}`;
2770
+ }
2771
+ return {
2772
+ provider: "github-actions",
2773
+ ...runUrl ? { run_url: runUrl } : {}
2774
+ };
2775
+ }
2776
+ if (process.env.CI === "true" || process.env.CI === "1") return { provider: process.env.GITLAB_CI && "gitlab-ci" || process.env.BITBUCKET_BUILD_NUMBER && "bitbucket-pipelines" || process.env.CIRCLECI && "circleci" || process.env.BUILDKITE && "buildkite" || process.env.TRAVIS && "travis-ci" || "generic-ci" };
2777
+ };
2778
+ //#endregion
2779
+ //#region src/cloud/outbox.ts
2780
+ const MAX_OUTBOX_BYTES = 5 * 1024 * 1024;
2781
+ const getMaxOutboxBytes = () => {
2782
+ const override = process.env.RAIGAL_MAX_OUTBOX_BYTES;
2783
+ if (override) {
2784
+ const parsed = Number.parseInt(override, 10);
2785
+ if (!Number.isNaN(parsed) && parsed > 0) return parsed;
2786
+ }
2787
+ return MAX_OUTBOX_BYTES;
2788
+ };
2789
+ const loadOutboxRuns = () => {
2790
+ const outboxPath = getOutboxPath();
2791
+ if (!fs.existsSync(outboxPath)) return [];
2792
+ try {
2793
+ const lines = fs.readFileSync(outboxPath, "utf-8").split("\n").filter((l) => l.trim().length > 0);
2794
+ const runs = [];
2795
+ for (const line of lines) try {
2796
+ const parsed = JSON.parse(line);
2797
+ const valid = Run.safeParse(parsed);
2798
+ if (valid.success) runs.push(valid.data);
2799
+ } catch {}
2800
+ return runs;
2801
+ } catch {
2802
+ return [];
2803
+ }
2804
+ };
2805
+ const writeRunsToOutbox = (runs) => {
2806
+ const outboxPath = getOutboxPath();
2807
+ const dir = path.dirname(outboxPath);
2808
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, {
2809
+ recursive: true,
2810
+ mode: 448
2811
+ });
2812
+ let lines = runs.map((r) => JSON.stringify(r));
2813
+ let text = lines.length > 0 ? `${lines.join("\n")}\n` : "";
2814
+ const maxBytes = getMaxOutboxBytes();
2815
+ while (Buffer.byteLength(text, "utf-8") > maxBytes && lines.length > 1) {
2816
+ lines.shift();
2817
+ text = `${lines.join("\n")}\n`;
2818
+ }
2819
+ const temp = `${outboxPath}.${Date.now()}.tmp`;
2820
+ fs.writeFileSync(temp, text, { mode: 384 });
2821
+ try {
2822
+ fs.renameSync(temp, outboxPath);
2823
+ } catch {
2824
+ fs.rmSync(temp, { force: true });
2825
+ fs.writeFileSync(outboxPath, text, { mode: 384 });
2826
+ }
2827
+ };
2828
+ const enqueueRun = (run) => {
2829
+ const existing = loadOutboxRuns();
2830
+ existing.push(run);
2831
+ writeRunsToOutbox(existing);
2832
+ };
2833
+ const getLastRecordedRun = () => {
2834
+ const runs = loadOutboxRuns();
2835
+ return runs.length > 0 ? runs[runs.length - 1] ?? null : null;
2836
+ };
2837
+ const flushOutbox = async (token) => {
2838
+ const runs = loadOutboxRuns();
2839
+ if (runs.length === 0) return {
2840
+ sent: 0,
2841
+ failed: 0,
2842
+ remaining: 0
2843
+ };
2844
+ let sentCount = 0;
2845
+ let failCount = 0;
2846
+ const remainingRuns = [];
2847
+ for (let i = 0; i < runs.length; i++) {
2848
+ const run = runs[i];
2849
+ try {
2850
+ await requestApi("/v1/runs", {
2851
+ method: "POST",
2852
+ token,
2853
+ body: run,
2854
+ timeoutMs: 4e3
2855
+ });
2856
+ sentCount++;
2857
+ } catch (err) {
2858
+ if (err instanceof ApiError) {
2859
+ if ([
2860
+ 400,
2861
+ 401,
2862
+ 403,
2863
+ 413,
2864
+ 422
2865
+ ].includes(err.status)) {
2866
+ failCount++;
2867
+ continue;
2868
+ }
2869
+ if (err.status === 409) {
2870
+ sentCount++;
2871
+ continue;
2872
+ }
2873
+ }
2874
+ failCount++;
2875
+ remainingRuns.push(...runs.slice(i));
2876
+ break;
2877
+ }
2878
+ }
2879
+ writeRunsToOutbox(remainingRuns);
2880
+ return {
2881
+ sent: sentCount,
2882
+ failed: failCount,
2883
+ remaining: remainingRuns.length
2884
+ };
2885
+ };
2886
+ //#endregion
2887
+ //#region src/cloud/recorder.ts
2888
+ var CloudRunRecorder = class {
2889
+ runId;
2890
+ command;
2891
+ directory;
2892
+ flags;
2893
+ startedAt;
2894
+ steps = [];
2895
+ report;
2896
+ policyMeta;
2897
+ gitContextOverride;
2898
+ agentTelemetry;
2899
+ seq = 0;
2900
+ finished = false;
2901
+ constructor(command, directory, flags = []) {
2902
+ this.runId = randomUUID();
2903
+ this.command = command;
2904
+ this.directory = directory;
2905
+ this.flags = flags.map((f) => f.replace(/^--?/, "")).filter((f) => f.length > 0).slice(0, 40);
2906
+ this.startedAt = (/* @__PURE__ */ new Date()).toISOString();
2907
+ }
2908
+ recordStep(name, durationMs, ok = true, counts) {
2909
+ if (this.steps.length >= 200) return;
2910
+ this.steps.push({
2911
+ seq: this.seq++,
2912
+ name: name.slice(0, 80),
2913
+ ok,
2914
+ ms: Math.max(0, Math.round(durationMs)),
2915
+ counts
2916
+ });
2917
+ }
2918
+ setReport(report) {
2919
+ this.report = report;
2920
+ }
2921
+ setPolicyMeta(version, hash) {
2922
+ this.policyMeta = {
2923
+ version,
2924
+ hash: hash.slice(0, 64)
2925
+ };
2926
+ }
2927
+ setAgentTelemetry(telemetry) {
2928
+ this.agentTelemetry = telemetry;
2929
+ }
2930
+ setGitContext(git) {
2931
+ this.gitContextOverride = {
2932
+ ...this.gitContextOverride,
2933
+ ...git
2934
+ };
2935
+ }
2936
+ buildRunPayload(exitCode) {
2937
+ const repo = detectRepoRef(this.directory) || {
2938
+ provider: "github",
2939
+ slug: "unknown/unknown"
2940
+ };
2941
+ const git = {
2942
+ ...detectGitContext(this.directory),
2943
+ ...this.gitContextOverride
2944
+ };
2945
+ const ci = detectCiContext();
2946
+ const ownerVerified = Boolean(process.env.ACTIONS_ID_TOKEN_REQUEST_URL);
2947
+ return {
2948
+ run_id: this.runId,
2949
+ command: this.command,
2950
+ repo,
2951
+ owner_verified: ownerVerified,
2952
+ git,
2953
+ ci,
2954
+ flags: this.flags,
2955
+ cli_version: APP_VERSION,
2956
+ policy: this.policyMeta,
2957
+ started_at: this.startedAt,
2958
+ ended_at: (/* @__PURE__ */ new Date()).toISOString(),
2959
+ exit_code: exitCode,
2960
+ steps: this.steps.slice(0, 200),
2961
+ report: this.report ? {
2962
+ ...this.report,
2963
+ agent_telemetry: this.agentTelemetry || this.report.agent_telemetry
2964
+ } : void 0,
2965
+ agent_telemetry: this.agentTelemetry
2966
+ };
2967
+ }
2968
+ async finish(exitCode) {
2969
+ if (this.finished) return;
2970
+ this.finished = true;
2971
+ try {
2972
+ enqueueRun(this.buildRunPayload(exitCode));
2973
+ const activeToken = getActiveToken();
2974
+ if (activeToken) await flushOutbox(activeToken.token);
2975
+ } catch {}
2976
+ }
2977
+ };
2978
+ let activeRecorder = null;
2979
+ const getActiveRecorder = () => activeRecorder;
2980
+ const startRunRecorder = (command, directory, flags = []) => {
2981
+ const recorder = new CloudRunRecorder(command, directory, flags);
2982
+ activeRecorder = recorder;
2983
+ const activeToken = getActiveToken();
2984
+ if (activeToken) flushOutbox(activeToken.token).catch(() => {});
2985
+ return recorder;
2986
+ };
2987
+ const finishActiveRecorder = async (exitCode = 0) => {
2988
+ if (activeRecorder) {
2989
+ const rec = activeRecorder;
2990
+ activeRecorder = null;
2991
+ await rec.finish(exitCode);
2992
+ }
2993
+ };
2994
+ //#endregion
2578
2995
  //#region src/commands/agent-local-cli.ts
2579
2996
  const cliPath$1 = () => {
2580
2997
  const arg = process.argv[1];
@@ -3466,6 +3883,47 @@ const runAgentSession = async (selected, resolvedDir, options, started) => {
3466
3883
  published: Boolean(published)
3467
3884
  });
3468
3885
  await tui.finish({ footer: `Done · ${selected.provider.id} · ${Math.round(performance.now() - started)}ms` });
3886
+ const recorder = getActiveRecorder();
3887
+ if (recorder) {
3888
+ let patchDiff;
3889
+ if (created?.worktree?.path) try {
3890
+ patchDiff = (spawnSync("git", [
3891
+ "diff",
3892
+ "-U2",
3893
+ "--no-color",
3894
+ "HEAD"
3895
+ ], {
3896
+ cwd: created.worktree.path,
3897
+ encoding: "utf-8"
3898
+ }).stdout || "").slice(0, 2e4) || void 0;
3899
+ } catch {}
3900
+ if (published) {
3901
+ const prMatch = published.prUrl?.match(/\/pull\/(\d+)/);
3902
+ const prNumber = prMatch ? Number.parseInt(prMatch[1], 10) : void 0;
3903
+ recorder.setGitContext({
3904
+ branch: published.branch,
3905
+ pr_number: prNumber
3906
+ });
3907
+ } else if (options.branch) recorder.setGitContext({ branch: options.branch });
3908
+ const agentTelemetry = {
3909
+ provider: selected.provider.id,
3910
+ before_score: before.score,
3911
+ after_score: verified.after.score,
3912
+ pr_url: published?.prUrl || void 0,
3913
+ patch_diff: patchDiff,
3914
+ duration_ms: Math.round(performance.now() - started)
3915
+ };
3916
+ recorder.setAgentTelemetry(agentTelemetry);
3917
+ recorder.recordStep("agent:baseline", 50, true);
3918
+ recorder.recordStep("agent:verify", Math.round(performance.now() - started), verified.after.score !== null && verified.after.score >= options.targetScore);
3919
+ recorder.setReport({
3920
+ score: verified.after.score,
3921
+ scoreable: true,
3922
+ files_scanned: changedFiles.length,
3923
+ findings: [],
3924
+ agent_telemetry: agentTelemetry
3925
+ });
3926
+ }
3469
3927
  printAgentSessionSummary({
3470
3928
  before,
3471
3929
  after: verified.after,
@@ -7184,169 +7642,61 @@ const pollDeviceAuthorization = async (grant) => {
7184
7642
  process.exitCode = 1;
7185
7643
  return;
7186
7644
  }
7187
- if (pollResult.status === "expired") {
7188
- log.error("Login authorization code expired. Please try running `raigal login` again.");
7189
- process.exitCode = 1;
7190
- return;
7191
- }
7192
- if (pollResult.status === "ok" && pollResult.data) {
7193
- const { access_token, org, user, expires_at } = pollResult.data;
7194
- saveStoredCredentials({
7195
- token: access_token,
7196
- org,
7197
- user,
7198
- expires_at
7199
- });
7200
- process.stdout.write("\n");
7201
- log.success(`Signed in as ${user.email} · ${org.name}`);
7202
- return;
7203
- }
7204
- } catch (err) {
7205
- log.error(`Polling error: ${err instanceof Error ? err.message : String(err)}`);
7206
- process.exitCode = 1;
7207
- return;
7208
- }
7209
- }
7210
- log.error("Login timed out. Please run `raigal login` again.");
7211
- process.exitCode = 1;
7212
- };
7213
- const loginCommand = async (options = {}) => {
7214
- if (options.withToken) {
7215
- await loginWithToken();
7216
- return;
7217
- }
7218
- log.info("Starting browser login...");
7219
- let grant;
7220
- try {
7221
- grant = await startDeviceLogin();
7222
- } catch (err) {
7223
- log.error(`Failed to start login: ${err instanceof Error ? err.message : String(err)}`);
7224
- process.exitCode = 1;
7225
- return;
7226
- }
7227
- process.stdout.write("\n");
7228
- process.stdout.write(` Confirmation code: ${grant.user_code}\n`);
7229
- process.stdout.write(` Verification URL: ${grant.verification_uri_complete}\n\n`);
7230
- log.muted("Opening browser to confirm authorization...");
7231
- openBrowser(grant.verification_uri_complete);
7232
- await pollDeviceAuthorization(grant);
7233
- };
7234
- //#endregion
7235
- //#region src/commands/logout.ts
7236
- const logoutCommand = async () => {
7237
- const active = getActiveToken();
7238
- if (active && active.kind === "session") await revokeSessionApi(active.token);
7239
- clearStoredCredentials();
7240
- clearCachedEntitlement();
7241
- log.success("Signed out and cleared cached credentials.");
7242
- };
7243
- //#endregion
7244
- //#region src/cloud/outbox.ts
7245
- const MAX_OUTBOX_BYTES = 5 * 1024 * 1024;
7246
- const getMaxOutboxBytes = () => {
7247
- const override = process.env.RAIGAL_MAX_OUTBOX_BYTES;
7248
- if (override) {
7249
- const parsed = Number.parseInt(override, 10);
7250
- if (!Number.isNaN(parsed) && parsed > 0) return parsed;
7251
- }
7252
- return MAX_OUTBOX_BYTES;
7253
- };
7254
- const loadOutboxRuns = () => {
7255
- const outboxPath = getOutboxPath();
7256
- if (!fs.existsSync(outboxPath)) return [];
7257
- try {
7258
- const lines = fs.readFileSync(outboxPath, "utf-8").split("\n").filter((l) => l.trim().length > 0);
7259
- const runs = [];
7260
- for (const line of lines) try {
7261
- const parsed = JSON.parse(line);
7262
- const valid = Run.safeParse(parsed);
7263
- if (valid.success) runs.push(valid.data);
7264
- } catch {}
7265
- return runs;
7266
- } catch {
7267
- return [];
7268
- }
7269
- };
7270
- const writeRunsToOutbox = (runs) => {
7271
- const outboxPath = getOutboxPath();
7272
- const dir = path.dirname(outboxPath);
7273
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, {
7274
- recursive: true,
7275
- mode: 448
7276
- });
7277
- let lines = runs.map((r) => JSON.stringify(r));
7278
- let text = lines.length > 0 ? `${lines.join("\n")}\n` : "";
7279
- const maxBytes = getMaxOutboxBytes();
7280
- while (Buffer.byteLength(text, "utf-8") > maxBytes && lines.length > 1) {
7281
- lines.shift();
7282
- text = `${lines.join("\n")}\n`;
7283
- }
7284
- const temp = `${outboxPath}.${Date.now()}.tmp`;
7285
- fs.writeFileSync(temp, text, { mode: 384 });
7286
- try {
7287
- fs.renameSync(temp, outboxPath);
7288
- } catch {
7289
- fs.rmSync(temp, { force: true });
7290
- fs.writeFileSync(outboxPath, text, { mode: 384 });
7291
- }
7292
- };
7293
- const enqueueRun = (run) => {
7294
- const existing = loadOutboxRuns();
7295
- existing.push(run);
7296
- writeRunsToOutbox(existing);
7297
- };
7298
- const getLastRecordedRun = () => {
7299
- const runs = loadOutboxRuns();
7300
- return runs.length > 0 ? runs[runs.length - 1] ?? null : null;
7301
- };
7302
- const flushOutbox = async (token) => {
7303
- const runs = loadOutboxRuns();
7304
- if (runs.length === 0) return {
7305
- sent: 0,
7306
- failed: 0,
7307
- remaining: 0
7308
- };
7309
- let sentCount = 0;
7310
- let failCount = 0;
7311
- const remainingRuns = [];
7312
- for (let i = 0; i < runs.length; i++) {
7313
- const run = runs[i];
7314
- try {
7315
- await requestApi("/v1/runs", {
7316
- method: "POST",
7317
- token,
7318
- body: run,
7319
- timeoutMs: 4e3
7320
- });
7321
- sentCount++;
7322
- } catch (err) {
7323
- if (err instanceof ApiError) {
7324
- if ([
7325
- 400,
7326
- 401,
7327
- 403,
7328
- 413,
7329
- 422
7330
- ].includes(err.status)) {
7331
- failCount++;
7332
- continue;
7333
- }
7334
- if (err.status === 409) {
7335
- sentCount++;
7336
- continue;
7337
- }
7338
- }
7339
- failCount++;
7340
- remainingRuns.push(...runs.slice(i));
7341
- break;
7645
+ if (pollResult.status === "expired") {
7646
+ log.error("Login authorization code expired. Please try running `raigal login` again.");
7647
+ process.exitCode = 1;
7648
+ return;
7649
+ }
7650
+ if (pollResult.status === "ok" && pollResult.data) {
7651
+ const { access_token, org, user, expires_at } = pollResult.data;
7652
+ saveStoredCredentials({
7653
+ token: access_token,
7654
+ org,
7655
+ user,
7656
+ expires_at
7657
+ });
7658
+ process.stdout.write("\n");
7659
+ log.success(`Signed in as ${user.email} · ${org.name}`);
7660
+ return;
7661
+ }
7662
+ } catch (err) {
7663
+ log.error(`Polling error: ${err instanceof Error ? err.message : String(err)}`);
7664
+ process.exitCode = 1;
7665
+ return;
7342
7666
  }
7343
7667
  }
7344
- writeRunsToOutbox(remainingRuns);
7345
- return {
7346
- sent: sentCount,
7347
- failed: failCount,
7348
- remaining: remainingRuns.length
7349
- };
7668
+ log.error("Login timed out. Please run `raigal login` again.");
7669
+ process.exitCode = 1;
7670
+ };
7671
+ const loginCommand = async (options = {}) => {
7672
+ if (options.withToken) {
7673
+ await loginWithToken();
7674
+ return;
7675
+ }
7676
+ log.info("Starting browser login...");
7677
+ let grant;
7678
+ try {
7679
+ grant = await startDeviceLogin();
7680
+ } catch (err) {
7681
+ log.error(`Failed to start login: ${err instanceof Error ? err.message : String(err)}`);
7682
+ process.exitCode = 1;
7683
+ return;
7684
+ }
7685
+ process.stdout.write("\n");
7686
+ process.stdout.write(` Confirmation code: ${grant.user_code}\n`);
7687
+ process.stdout.write(` Verification URL: ${grant.verification_uri_complete}\n\n`);
7688
+ log.muted("Opening browser to confirm authorization...");
7689
+ openBrowser(grant.verification_uri_complete);
7690
+ await pollDeviceAuthorization(grant);
7691
+ };
7692
+ //#endregion
7693
+ //#region src/commands/logout.ts
7694
+ const logoutCommand = async () => {
7695
+ const active = getActiveToken();
7696
+ if (active && active.kind === "session") await revokeSessionApi(active.token);
7697
+ clearStoredCredentials();
7698
+ clearCachedEntitlement();
7699
+ log.success("Signed out and cleared cached credentials.");
7350
7700
  };
7351
7701
  //#endregion
7352
7702
  //#region src/commands/telemetry-show.ts
@@ -7450,6 +7800,14 @@ const registerCloudCommands = (program) => {
7450
7800
  if (flags.show || isJson) await telemetryShowCommand({ json: isJson });
7451
7801
  else process.stdout.write("Run `raigal telemetry --show` to inspect recorded telemetry payloads.\n");
7452
7802
  });
7803
+ program.command("report-pr-closed").description("Report a pull request closed/merged event to Raigal Cloud").option("--pr <number>", "PR number (default: read from GITHUB_EVENT_PATH)").option("--merged", "Flag indicating whether PR was merged").action(async (flags) => {
7804
+ const prNumber = flags.pr ? Number.parseInt(flags.pr, 10) : void 0;
7805
+ const { reportPrClosedCommand } = await import("./report-pr-closed-CDdQjRWj.js");
7806
+ await reportPrClosedCommand({
7807
+ prNumber,
7808
+ merged: Boolean(flags.merged)
7809
+ });
7810
+ });
7453
7811
  };
7454
7812
  //#endregion
7455
7813
  //#region src/hooks/feedback.ts
@@ -23984,7 +24342,7 @@ const maybeNotifyUpdate = async (now = Date.now()) => {
23984
24342
  if (!process.stderr.isTTY) return;
23985
24343
  const cachePath = resolveUpdateCachePath();
23986
24344
  const cache = readCache(cachePath);
23987
- if (cache && isOutdated("1.0.0", cache.latest)) process.stderr.write(formatUpdateNotice(APP_VERSION, cache.latest));
24345
+ if (cache && isOutdated("1.0.2", cache.latest)) process.stderr.write(formatUpdateNotice(APP_VERSION, cache.latest));
23988
24346
  if (!cache || now - cache.checkedAt > CHECK_INTERVAL_MS) {
23989
24347
  const latest = await fetchLatestVersion$1();
23990
24348
  if (latest) writeCache(cachePath, {
@@ -24504,7 +24862,7 @@ const renderCommandGroup = (group, commandWidth) => {
24504
24862
  return lines;
24505
24863
  };
24506
24864
  const renderCommandReference = (input = {}) => {
24507
- const version = input.version ?? "1.0.0";
24865
+ const version = input.version ?? "1.0.2";
24508
24866
  const commandWidth = Math.max(...COMMAND_REFERENCE.map((item) => item.command.length));
24509
24867
  const lines = [renderHeader({
24510
24868
  version,
@@ -24727,7 +25085,7 @@ const renderHelpDetails = () => [
24727
25085
  ].map((line) => highlightRaigal(line, theme)).join("\n");
24728
25086
  const renderHome = (input = {}) => {
24729
25087
  let out = renderHeader({
24730
- version: input.version ?? "1.0.0",
25088
+ version: input.version ?? "1.0.2",
24731
25089
  command: "--bare",
24732
25090
  context: []
24733
25091
  });
@@ -24885,100 +25243,15 @@ const matchFixAgent = (flags) => {
24885
25243
  return FIX_AGENT_FLAGS.find((a) => flags[a.name])?.flag;
24886
25244
  };
24887
25245
  //#endregion
24888
- //#region src/cloud/context.ts
24889
- const execGit = (args, cwd) => {
24890
- try {
24891
- const res = spawnSync("git", args, {
24892
- cwd: path.resolve(cwd),
24893
- encoding: "utf-8",
24894
- stdio: [
24895
- "ignore",
24896
- "pipe",
24897
- "ignore"
24898
- ]
24899
- });
24900
- if (res.error || res.status !== 0) return void 0;
24901
- return res.stdout.trim() || void 0;
24902
- } catch {
24903
- return;
24904
- }
24905
- };
24906
- const getRootCommitHash = (directory) => {
24907
- const hash = execGit([
24908
- "rev-list",
24909
- "--max-parents=0",
24910
- "HEAD"
24911
- ], directory);
24912
- if (!hash) return void 0;
24913
- const firstLine = hash.split("\n")[0]?.trim();
24914
- return firstLine && /^[0-9a-f]{40,64}$/i.test(firstLine) ? firstLine : void 0;
24915
- };
24916
- const detectRepoRef = (directory, ownerId) => {
24917
- const detected = detectLocalRepoOwner(directory);
24918
- if (!detected.owner || !detected.repo) return null;
24919
- return {
24920
- provider: "github",
24921
- slug: `${detected.owner}/${detected.repo}`.toLowerCase(),
24922
- owner_id: ownerId,
24923
- root_commit_hash: getRootCommitHash(directory)
24924
- };
24925
- };
24926
- const readGitHubEventPayload = () => {
24927
- const eventPath = process.env.GITHUB_EVENT_PATH;
24928
- if (!eventPath || !fs.existsSync(eventPath)) return null;
24929
- try {
24930
- const content = fs.readFileSync(eventPath, "utf-8");
24931
- return JSON.parse(content);
24932
- } catch {
24933
- return null;
24934
- }
24935
- };
24936
- const detectGitContext = (directory) => {
24937
- const eventPayload = readGitHubEventPayload();
24938
- const pr = eventPayload?.pull_request;
24939
- let headSha = pr?.head?.sha;
24940
- if (!headSha) headSha = process.env.GITHUB_SHA || execGit(["rev-parse", "HEAD"], directory);
24941
- let branch = process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME;
24942
- if (!branch) {
24943
- branch = execGit([
24944
- "rev-parse",
24945
- "--abbrev-ref",
24946
- "HEAD"
24947
- ], directory);
24948
- if (branch === "HEAD") branch = void 0;
24949
- }
24950
- let baseRef = pr?.base?.ref || process.env.GITHUB_BASE_REF;
24951
- if (!baseRef) baseRef = void 0;
24952
- let prNumber;
24953
- if (typeof pr?.number === "number") prNumber = pr.number;
24954
- else if (typeof eventPayload?.number === "number") prNumber = eventPayload.number;
24955
- else if (process.env.GITHUB_REF) {
24956
- const match = process.env.GITHUB_REF.match(/^refs\/pull\/(\d+)\//);
24957
- if (match && match[1]) prNumber = Number.parseInt(match[1], 10);
24958
- }
24959
- const result = {};
24960
- if (branch) result.branch = branch;
24961
- if (headSha) result.head_sha = headSha;
24962
- if (baseRef) result.base_ref = baseRef;
24963
- if (prNumber !== void 0 && !Number.isNaN(prNumber)) result.pr_number = prNumber;
24964
- return result;
24965
- };
24966
- const detectCiContext = () => {
24967
- if (process.env.GITHUB_ACTIONS === "true" || process.env.GITHUB_ACTIONS === "1") {
24968
- const serverUrl = process.env.GITHUB_SERVER_URL || "https://github.com";
24969
- const repo = process.env.GITHUB_REPOSITORY;
24970
- const runId = process.env.GITHUB_RUN_ID;
24971
- let runUrl;
24972
- if (repo && runId) {
24973
- const attempt = process.env.GITHUB_RUN_ATTEMPT;
24974
- runUrl = `${serverUrl}/${repo}/actions/runs/${runId}${attempt ? `/attempts/${attempt}` : ""}`;
24975
- }
24976
- return {
24977
- provider: "github-actions",
24978
- ...runUrl ? { run_url: runUrl } : {}
24979
- };
24980
- }
24981
- if (process.env.CI === "true" || process.env.CI === "1") return { provider: process.env.GITLAB_CI && "gitlab-ci" || process.env.BITBUCKET_BUILD_NUMBER && "bitbucket-pipelines" || process.env.CIRCLECI && "circleci" || process.env.BUILDKITE && "buildkite" || process.env.TRAVIS && "travis-ci" || "generic-ci" };
25246
+ //#region src/output/rule-message.ts
25247
+ /**
25248
+ * Pure deterministic formatter for diagnostic messages.
25249
+ * Secret-class rules (or redactSource: true) unconditionally return FIXED_SECRET_DIAGNOSTIC_MESSAGE.
25250
+ * Non-secret rules return diagnostic.message (clamped to max 500 chars).
25251
+ */
25252
+ const formatFindingMessage = (diagnostic) => {
25253
+ if (diagnostic.redactSource || isSecretClassRule(diagnostic.rule)) return FIXED_SECRET_DIAGNOSTIC_MESSAGE;
25254
+ return diagnostic.message.slice(0, 500);
24982
25255
  };
24983
25256
  //#endregion
24984
25257
  //#region src/cloud/fingerprint.ts
@@ -25020,7 +25293,8 @@ const toContractFinding = (diagnostic, rootDirectory, lineTextOverride) => {
25020
25293
  severity,
25021
25294
  path: relativePath,
25022
25295
  line: Math.max(0, diagnostic.line),
25023
- fingerprint
25296
+ fingerprint,
25297
+ message: formatFindingMessage(diagnostic)
25024
25298
  };
25025
25299
  };
25026
25300
  //#endregion
@@ -25111,96 +25385,6 @@ const applyPolicyToConfig = (localConfig, policy) => {
25111
25385
  };
25112
25386
  };
25113
25387
  //#endregion
25114
- //#region src/cloud/recorder.ts
25115
- var CloudRunRecorder = class {
25116
- runId;
25117
- command;
25118
- directory;
25119
- flags;
25120
- startedAt;
25121
- steps = [];
25122
- report;
25123
- policyMeta;
25124
- seq = 0;
25125
- finished = false;
25126
- constructor(command, directory, flags = []) {
25127
- this.runId = randomUUID();
25128
- this.command = command;
25129
- this.directory = directory;
25130
- this.flags = flags.map((f) => f.replace(/^--?/, "")).filter((f) => f.length > 0).slice(0, 40);
25131
- this.startedAt = (/* @__PURE__ */ new Date()).toISOString();
25132
- }
25133
- recordStep(name, durationMs, ok = true, counts) {
25134
- if (this.steps.length >= 200) return;
25135
- this.steps.push({
25136
- seq: this.seq++,
25137
- name: name.slice(0, 80),
25138
- ok,
25139
- ms: Math.max(0, Math.round(durationMs)),
25140
- counts
25141
- });
25142
- }
25143
- setReport(report) {
25144
- this.report = report;
25145
- }
25146
- setPolicyMeta(version, hash) {
25147
- this.policyMeta = {
25148
- version,
25149
- hash: hash.slice(0, 64)
25150
- };
25151
- }
25152
- buildRunPayload(exitCode) {
25153
- const repo = detectRepoRef(this.directory) || {
25154
- provider: "github",
25155
- slug: "unknown/unknown"
25156
- };
25157
- const git = detectGitContext(this.directory);
25158
- const ci = detectCiContext();
25159
- const ownerVerified = Boolean(process.env.ACTIONS_ID_TOKEN_REQUEST_URL);
25160
- return {
25161
- run_id: this.runId,
25162
- command: this.command,
25163
- repo,
25164
- owner_verified: ownerVerified,
25165
- git,
25166
- ci,
25167
- flags: this.flags,
25168
- cli_version: APP_VERSION,
25169
- policy: this.policyMeta,
25170
- started_at: this.startedAt,
25171
- ended_at: (/* @__PURE__ */ new Date()).toISOString(),
25172
- exit_code: exitCode,
25173
- steps: this.steps.slice(0, 200),
25174
- report: this.report
25175
- };
25176
- }
25177
- async finish(exitCode) {
25178
- if (this.finished) return;
25179
- this.finished = true;
25180
- try {
25181
- enqueueRun(this.buildRunPayload(exitCode));
25182
- const activeToken = getActiveToken();
25183
- if (activeToken) await flushOutbox(activeToken.token);
25184
- } catch {}
25185
- }
25186
- };
25187
- let activeRecorder = null;
25188
- const getActiveRecorder = () => activeRecorder;
25189
- const startRunRecorder = (command, directory, flags = []) => {
25190
- const recorder = new CloudRunRecorder(command, directory, flags);
25191
- activeRecorder = recorder;
25192
- const activeToken = getActiveToken();
25193
- if (activeToken) flushOutbox(activeToken.token).catch(() => {});
25194
- return recorder;
25195
- };
25196
- const finishActiveRecorder = async (exitCode = 0) => {
25197
- if (activeRecorder) {
25198
- const rec = activeRecorder;
25199
- activeRecorder = null;
25200
- await rec.finish(exitCode);
25201
- }
25202
- };
25203
- //#endregion
25204
25388
  //#region src/engagement/pilot-invitation.ts
25205
25389
  const STATE_BASENAME = "pilot_invitation.scans";
25206
25390
  const INVITATION_SCAN_THRESHOLD = 3;
@@ -26435,12 +26619,12 @@ const runScanBody = async (resolvedDir, config, options, projectInfo, scanScope,
26435
26619
  scoreFileCount
26436
26620
  });
26437
26621
  if (options.sarif) {
26438
- const { buildSarifLog } = await import("./sarif-B-l1Fhzd.js");
26622
+ const { buildSarifLog } = await import("./sarif--DI_2p2r.js");
26439
26623
  console.log(JSON.stringify(buildSarifLog(results), null, 2));
26440
26624
  return completion;
26441
26625
  }
26442
26626
  if (options.json) {
26443
- const { buildJsonOutput } = await import("./json-DBN8fnd3.js");
26627
+ const { buildJsonOutput } = await import("./json-C9fBWj3V.js");
26444
26628
  const jsonOut = buildJsonOutput(results, scoreResult, scoreFileCount, elapsedMs, scanCoverage);
26445
26629
  console.log(JSON.stringify(jsonOut, null, 2));
26446
26630
  return completion;
@@ -29390,7 +29574,16 @@ const shouldRenderPlainVersion = () => {
29390
29574
  "version"
29391
29575
  ].includes(args[0] ?? "");
29392
29576
  };
29393
- const program = new Command().name("raigal").description("The quality gate for agentic coding.").version(APP_VERSION, "-v, --version").argument("[directory]", "directory to scan when no command is passed", ".").option("--changes", "only scan changed files (git diff)").option("--staged", "only scan staged files").option("--base <ref>", "diff base for --changes, e.g. origin/main (default HEAD)").option("-d, --verbose", "show file details per rule").option("--json", "output JSON instead of terminal UI").option("--sarif", "output SARIF 2.1.0 (for GitHub code scanning)").option("--format <format>", "output format: json or sarif");
29577
+ const program = new Command().name("raigal").description("The quality gate for agentic coding.").version(APP_VERSION, "-v, --version").argument("[directory]", "directory to scan when no command is passed", ".");
29578
+ for (const [flag, description] of [
29579
+ ["--changes", "only scan changed files (git diff)"],
29580
+ ["--staged", "only scan staged files"],
29581
+ ["--base <ref>", "diff base for --changes, e.g. origin/main (default HEAD)"],
29582
+ ["-d, --verbose", "show file details per rule"],
29583
+ ["--json", "output JSON instead of terminal UI"],
29584
+ ["--sarif", "output SARIF 2.1.0 (for GitHub code scanning)"],
29585
+ ["--format <format>", "output format: json or sarif"]
29586
+ ]) program.option(flag, description);
29394
29587
  addFilterAndFailOnOptions(program).showSuggestionAfterError().action(async (directory, flags) => {
29395
29588
  if (hasNoUserArgs() && noFlagsPassed(flags) && process.stdin.isTTY) try {
29396
29589
  await interactiveCommand(directory, loadConfig(directory));
@@ -29398,7 +29591,17 @@ addFilterAndFailOnOptions(program).showSuggestionAfterError().action(async (dire
29398
29591
  } catch {}
29399
29592
  await runScan(directory, flags);
29400
29593
  });
29401
- addFilterAndFailOnOptions(program.command("scan [directory]").description("Score a project and print findings").option("--changes", "only scan changed files").option("--staged", "only scan staged files").option("--base <ref>", "diff base for --changes, e.g. origin/main (default HEAD)").option("-d, --verbose", "show file details per rule").option("--json", "output JSON").option("--sarif", "output SARIF 2.1.0 (for GitHub code scanning)").option("--format <format>", "output format: json or sarif")).action(async (directory = ".", _flags, command) => {
29594
+ const scanCmd = program.command("scan [directory]").description("Score a project and print findings");
29595
+ for (const [flag, description] of [
29596
+ ["--changes", "only scan changed files"],
29597
+ ["--staged", "only scan staged files"],
29598
+ ["--base <ref>", "diff base for --changes, e.g. origin/main (default HEAD)"],
29599
+ ["-d, --verbose", "show file details per rule"],
29600
+ ["--json", "output JSON"],
29601
+ ["--sarif", "output SARIF 2.1.0 (for GitHub code scanning)"],
29602
+ ["--format <format>", "output format: json or sarif"]
29603
+ ]) scanCmd.option(flag, description);
29604
+ addFilterAndFailOnOptions(scanCmd).action(async (directory = ".", _flags, command) => {
29402
29605
  await runScan(directory, command.optsWithGlobals());
29403
29606
  });
29404
29607
  const fixProgram = program.command("fix [directory]").description("Auto-fix findings or hand off to a coding agent").option("-d, --verbose", "show detailed fix progress").option("-f, --force", "run aggressive fixes (audit and framework dependency alignment)").option("--safe", "only apply reversible fixes (imports, comment removal, safe formatters); skip anything that deletes code, rewrites behaviour, or runs unsafe formatter configs").option("--dry-run", "print planned fix steps without writing files").addOption(new Option("--changes", "only fix changed files (git diff)").conflicts("staged")).addOption(new Option("--staged", "only fix staged files").conflicts("changes")).option("--base <ref>", "diff base for --changes, e.g. origin/main (default HEAD)").option("-p, --prompt", "print a prompt for your coding agent to fix remaining issues");
@@ -29532,4 +29735,4 @@ const main = async () => {
29532
29735
  };
29533
29736
  main();
29534
29737
  //#endregion
29535
- export { FIXED_SECRET_DIAGNOSTIC_MESSAGE as a, APP_VERSION as i, withFindingAssessments as n, isSecretClassRule as o, ENGINE_INFO as r, summarizeFindingAssessments as t };
29738
+ export { requestApi as a, log as c, detectRepoRef as i, FIXED_SECRET_DIAGNOSTIC_MESSAGE as l, withFindingAssessments as n, getActiveToken as o, ENGINE_INFO as r, APP_VERSION as s, summarizeFindingAssessments as t, isSecretClassRule as u };