@weareikko/code-review 0.9.6 → 0.9.7

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.
@@ -35,7 +35,7 @@ var GitlabReviewError = class extends Error {
35
35
  };
36
36
  /**
37
37
  * Patterns that identify a provider credit/quota-exhaustion error across
38
- * providers (Anthropic, OpenAI, Cloudflare AI Gateway, …). Matched against the
38
+ * providers (Anthropic, OpenAI, OpenRouter, …). Matched against the
39
39
  * provider's error message. Deliberately excludes transient rate limits (429),
40
40
  * which are retryable rather than a billing dead-end.
41
41
  */
@@ -399,7 +399,7 @@ async function git$1(args, options = {}) {
399
399
  try {
400
400
  const { stdout } = await exec$1("git", args, {
401
401
  cwd: options.cwd,
402
- maxBuffer: 50 * 1024 * 1024
402
+ maxBuffer: 52428800
403
403
  });
404
404
  return stdout;
405
405
  } catch (error) {
@@ -791,7 +791,8 @@ function redactUrl(url) {
791
791
  }
792
792
  /** Base directory for cached git-skill clones (honours `XDG_CACHE_HOME`). */
793
793
  function resolveSkillCacheDir() {
794
- return join(process.env.XDG_CACHE_HOME?.trim() || join(homedir(), ".cache"), "code-review", "skills");
794
+ const base = process.env.XDG_CACHE_HOME?.trim() || join(homedir(), ".cache");
795
+ return join(base, "code-review", "skills");
795
796
  }
796
797
  /**
797
798
  * Stable cache-directory name for a git skill. Keyed on the clone URL plus the
@@ -965,7 +966,7 @@ var DEFAULT_MARKETPLACE_FORMAT = "anthropic";
965
966
  * (`npm:`, `file:`, `git:`) or the bare-name builtin lookup, and so cannot be
966
967
  * used as a marketplace name — `<name>:<plugin>/<skill>` would be ambiguous.
967
968
  */
968
- var RESERVED_MARKETPLACE_NAMES = new Set([
969
+ var RESERVED_MARKETPLACE_NAMES = /* @__PURE__ */ new Set([
969
970
  "npm",
970
971
  "file",
971
972
  "git",
@@ -1431,7 +1432,7 @@ function buildSummaryBody(summary, costFooter, options = {}) {
1431
1432
  return `${withFooter}\n\n${buildSummaryHistoryBlock(historyEntries)}`;
1432
1433
  }
1433
1434
  function buildReviewedCommitFooter(commitSha) {
1434
- return `Reviewed by ${PRODUCT_LINK} v0.9.6 for commit ${commitSha}.`;
1435
+ return `Reviewed by ${PRODUCT_LINK} v0.9.7 for commit ${commitSha}.`;
1435
1436
  }
1436
1437
  function extractReviewedCommitSha(body) {
1437
1438
  return REVIEWED_COMMIT_FOOTER_PATTERN.exec(body)?.[1] ?? null;
@@ -1651,8 +1652,9 @@ async function createDraftsConcurrently(gitlab, project, mr, fresh) {
1651
1652
  next += 1;
1652
1653
  if (index >= fresh.length) return;
1653
1654
  const item = fresh[index];
1655
+ const draft = await gitlab.createDraftNote(project, mr, item.payload);
1654
1656
  records[index] = {
1655
- id: (await gitlab.createDraftNote(project, mr, item.payload)).id,
1657
+ id: draft.id,
1656
1658
  fingerprints: item.fingerprints
1657
1659
  };
1658
1660
  }
@@ -1783,8 +1785,8 @@ var RESERVED_ENV_SUFFIX_SET = new Set(RESERVED_ENV_SUFFIXES);
1783
1785
  * CI-wide variable of the same name.
1784
1786
  *
1785
1787
  * This lets credentials and infra vars that `@earendil-works/pi-ai` reads
1786
- * (`ANTHROPIC_API_KEY`, `CLOUDFLARE_API_KEY`, `CLOUDFLARE_ACCOUNT_ID`,
1787
- * `OLLAMA_HOST`, ambient AWS/Vertex creds, …) — and the GitLab tokens — be
1788
+ * (`ANTHROPIC_API_KEY`, `OPENROUTER_API_KEY`, `OLLAMA_HOST`,
1789
+ * ambient AWS/Vertex creds, …) — and the GitLab tokens — be
1788
1790
  * scoped under `CODE_REVIEW_` in shared CI without enumerating pi-ai's
1789
1791
  * provider list.
1790
1792
  *
@@ -1812,8 +1814,8 @@ function applyCodeReviewEnvPrefix(env = process.env) {
1812
1814
  /**
1813
1815
  * Default pi-ai's prompt-cache retention to `long` when the caller has not set
1814
1816
  * it. pi-ai reads `PI_CACHE_RETENTION` from `process.env` at request time; `long`
1815
- * asks providers that support it (e.g. OpenAI's `openai-responses` API, including
1816
- * via the Cloudflare AI Gateway) to keep the cached system-prompt prefix for up
1817
+ * asks providers that support it (e.g. OpenAI's `openai-responses` API) to keep
1818
+ * the cached system-prompt prefix for up
1817
1819
  * to 24h so reviews spaced hours apart still reuse it. It is a safe no-op for
1818
1820
  * providers/models without long-retention support (e.g. Anthropic), where it
1819
1821
  * behaves exactly like the default `short`.
@@ -1825,7 +1827,7 @@ function applyDefaultCacheRetention(env = process.env) {
1825
1827
  if (!env.PI_CACHE_RETENTION) env.PI_CACHE_RETENTION = "long";
1826
1828
  return env;
1827
1829
  }
1828
- var BOOLEAN_FLAGS = new Set([
1830
+ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
1829
1831
  "dry-run",
1830
1832
  "no-post",
1831
1833
  "no-summary",
@@ -1836,7 +1838,7 @@ var BOOLEAN_FLAGS = new Set([
1836
1838
  "help",
1837
1839
  "version"
1838
1840
  ]);
1839
- var MULTI_FLAGS = new Set(["skill", "marketplace"]);
1841
+ var MULTI_FLAGS = /* @__PURE__ */ new Set(["skill", "marketplace"]);
1840
1842
  function parseArgs(argv) {
1841
1843
  const args = {};
1842
1844
  for (let i = 0; i < argv.length; i += 1) {
@@ -2446,7 +2448,8 @@ function createGitTools(dir, options = {}) {
2446
2448
  dir,
2447
2449
  oid
2448
2450
  });
2449
- const diff = await diffCommits(dir, fs, commit.parent[0] ?? null, oid);
2451
+ const parent = commit.parent[0] ?? null;
2452
+ const diff = await diffCommits(dir, fs, parent, oid);
2450
2453
  return text(`commit ${oid}\nAuthor: ${commit.author.name} <${commit.author.email}>\n\n${commit.message.trim()}\n\n${diff}`);
2451
2454
  }
2452
2455
  },
@@ -2459,7 +2462,9 @@ function createGitTools(dir, options = {}) {
2459
2462
  to: Type.String({ description: "Target ref/sha." })
2460
2463
  }),
2461
2464
  async execute(_id, params) {
2462
- return text(await diffCommits(dir, fs, await resolveOid(dir, fs, params.from), await resolveOid(dir, fs, params.to)));
2465
+ const fromOid = await resolveOid(dir, fs, params.from);
2466
+ const toOid = await resolveOid(dir, fs, params.to);
2467
+ return text(await diffCommits(dir, fs, fromOid, toOid));
2463
2468
  }
2464
2469
  }
2465
2470
  ];
@@ -2625,6 +2630,80 @@ function removeAtIndex(text, start, count) {
2625
2630
  function endsWithCommaOrNewline(text) {
2626
2631
  return /[,\n][ \t\r]*$/.test(text);
2627
2632
  }
2633
+ var namedHtmlEntities = {
2634
+ "&quot;": "\"",
2635
+ "&amp;": "&",
2636
+ "&lt;": "<",
2637
+ "&gt;": ">",
2638
+ "&apos;": "'"
2639
+ };
2640
+ /**
2641
+ * Try to match an HTML entity at the start of the given fragment. The fragment
2642
+ * is a small slice of text that begins exactly at the candidate '&'. Returns the
2643
+ * decoded character and the number of characters consumed, or null when there
2644
+ * is no complete, valid entity (for example a truncated "&quot" without ';').
2645
+ */
2646
+ function matchHtmlEntity(fragment) {
2647
+ if (fragment.charAt(0) !== "&") return null;
2648
+ const semicolon = fragment.indexOf(";");
2649
+ if (semicolon === -1) return null;
2650
+ const entity = fragment.substring(0, semicolon + 1);
2651
+ const named = namedHtmlEntities[entity];
2652
+ if (named !== void 0) return {
2653
+ char: named,
2654
+ length: entity.length
2655
+ };
2656
+ if (fragment.charAt(1) === "#") {
2657
+ const body = fragment.substring(2, semicolon);
2658
+ const hex = body.charAt(0) === "x" || body.charAt(0) === "X";
2659
+ const digits = hex ? body.substring(1) : body;
2660
+ if (digits.length > 0) {
2661
+ const code = Number.parseInt(digits, hex ? 16 : 10);
2662
+ if (!Number.isNaN(code) && code >= 0 && code <= 1114111) return {
2663
+ char: String.fromCodePoint(code),
2664
+ length: entity.length
2665
+ };
2666
+ }
2667
+ }
2668
+ return null;
2669
+ }
2670
+ /**
2671
+ * Test whether a matched HTML entity decodes to a double quote character
2672
+ */
2673
+ function isDoubleQuoteEntity(match) {
2674
+ return match !== null && match.char === "\"";
2675
+ }
2676
+ /**
2677
+ * Test whether a matched HTML entity decodes to a single quote character
2678
+ */
2679
+ function isSingleQuoteEntity(match) {
2680
+ return match !== null && match.char === "'";
2681
+ }
2682
+ /**
2683
+ * Count the number of occurrences of a single character in a string
2684
+ */
2685
+ function countOccurrences(text, char) {
2686
+ let count = 0;
2687
+ for (let i = 0; i < text.length; i++) if (text.charAt(i) === char) count++;
2688
+ return count;
2689
+ }
2690
+ /**
2691
+ * Test whether `closeChar` is a closing bracket and `text` still contains an
2692
+ * unmatched opening bracket of the same kind. This indicates that the end of
2693
+ * `text` is located inside the brackets, for example the quote in
2694
+ * `"a (b") c"` is followed by `)` while `(` is still unclosed.
2695
+ *
2696
+ * Note that the (potentially expensive) counting is only performed when
2697
+ * `closeChar` actually is a closing bracket.
2698
+ */
2699
+ function isInsideUnclosedBracket(text, closeChar) {
2700
+ switch (closeChar) {
2701
+ case ")": return countOccurrences(text, "(") > countOccurrences(text, ")");
2702
+ case "]": return countOccurrences(text, "[") > countOccurrences(text, "]");
2703
+ case "}": return countOccurrences(text, "{") > countOccurrences(text, "}");
2704
+ default: return false;
2705
+ }
2706
+ }
2628
2707
  //#endregion
2629
2708
  //#region node_modules/jsonrepair/lib/esm/regular/jsonrepair.js
2630
2709
  var controlCharacters = {
@@ -2797,23 +2876,26 @@ function jsonrepair(text) {
2797
2876
  processedComma = parseCharacter(",");
2798
2877
  if (!processedComma) output = insertBeforeLastWhitespace(output, ",");
2799
2878
  parseWhitespaceAndSkipComments();
2800
- } else {
2801
- processedComma = true;
2802
- initial = false;
2803
- }
2879
+ } else processedComma = true;
2804
2880
  skipEllipsis();
2805
2881
  if (!(parseString() || parseUnquotedString(true))) {
2806
- if (text[i] === "}" || text[i] === "{" || text[i] === "]" || text[i] === "[" || text[i] === void 0) output = stripLastOccurrence(output, ",");
2807
- else throwObjectKeyExpected();
2882
+ if (text[i] === "}" || text[i] === "{" || text[i] === "]" || text[i] === "[" || text[i] === void 0) {
2883
+ if (!initial) output = stripLastOccurrence(output, ",");
2884
+ } else throwObjectKeyExpected();
2808
2885
  break;
2809
2886
  }
2810
2887
  parseWhitespaceAndSkipComments();
2811
2888
  const processedColon = parseCharacter(":");
2812
2889
  const truncatedText = i >= text.length;
2813
- if (!processedColon) if (isStartOfValue(text[i]) || truncatedText) output = insertBeforeLastWhitespace(output, ":");
2814
- else throwColonExpected();
2815
- if (!parseValue()) if (processedColon || truncatedText) output += "null";
2816
- else throwColonExpected();
2890
+ if (!processedColon) {
2891
+ if (isStartOfValue(text[i]) || truncatedText) output = insertBeforeLastWhitespace(output, ":");
2892
+ else throwColonExpected();
2893
+ }
2894
+ if (!parseValue()) {
2895
+ if (processedColon || truncatedText) output += "null";
2896
+ else throwColonExpected();
2897
+ }
2898
+ initial = false;
2817
2899
  }
2818
2900
  if (text[i] === "}") {
2819
2901
  output += "}";
@@ -2836,12 +2918,13 @@ function jsonrepair(text) {
2836
2918
  while (i < text.length && text[i] !== "]") {
2837
2919
  if (!initial) {
2838
2920
  if (!parseCharacter(",")) output = insertBeforeLastWhitespace(output, ",");
2839
- } else initial = false;
2921
+ }
2840
2922
  skipEllipsis();
2841
2923
  if (!parseValue()) {
2842
- output = stripLastOccurrence(output, ",");
2924
+ if (!initial) output = stripLastOccurrence(output, ",");
2843
2925
  break;
2844
2926
  }
2927
+ initial = false;
2845
2928
  }
2846
2929
  if (text[i] === "]") {
2847
2930
  output += "]";
@@ -2883,17 +2966,19 @@ function jsonrepair(text) {
2883
2966
  function parseString() {
2884
2967
  let stopAtDelimiter = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : false;
2885
2968
  let stopAtIndex = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : -1;
2886
- let skipEscapeChars = text[i] === "\\";
2969
+ const skipEscapeChars = text[i] === "\\";
2887
2970
  if (skipEscapeChars) {
2888
2971
  i++;
2889
- skipEscapeChars = true;
2972
+ if (!isQuote(text[i])) throwUnexpectedCharacter();
2890
2973
  }
2891
- if (isQuote(text[i])) {
2974
+ const openEntity = text[i] === "&" ? matchHtmlEntity(text.slice(i, i + 12)) : null;
2975
+ const openedByEntity = isDoubleQuoteEntity(openEntity) || isSingleQuoteEntity(openEntity);
2976
+ if (isQuote(text[i]) || openedByEntity) {
2892
2977
  const isEndQuote = isDoubleQuote(text[i]) ? isDoubleQuote : isSingleQuote(text[i]) ? isSingleQuote : isSingleQuoteLike(text[i]) ? isSingleQuoteLike : isDoubleQuoteLike;
2893
2978
  const iBefore = i;
2894
2979
  const oBefore = output.length;
2895
2980
  let str = "\"";
2896
- i++;
2981
+ i += openedByEntity && openEntity ? openEntity.length : 1;
2897
2982
  while (true) {
2898
2983
  if (i >= text.length) {
2899
2984
  const iPrev = prevNonWhitespaceIndex(i - 1);
@@ -2911,17 +2996,19 @@ function jsonrepair(text) {
2911
2996
  output += str;
2912
2997
  return true;
2913
2998
  }
2914
- if (isEndQuote(text[i])) {
2999
+ const entity = openedByEntity && text[i] === "&" ? matchHtmlEntity(text.slice(i, i + 12)) : null;
3000
+ if (entity && openEntity ? entity.char === openEntity.char : isEndQuote(text[i])) {
2915
3001
  const iQuote = i;
2916
3002
  const oQuote = str.length;
2917
3003
  str += "\"";
2918
- i++;
3004
+ i += entity ? entity.length : 1;
2919
3005
  output += str;
2920
3006
  parseWhitespaceAndSkipComments(false);
2921
- if (stopAtDelimiter || i >= text.length || isDelimiter(text[i]) || isQuote(text[i]) || isDigit(text[i])) {
3007
+ if (stopAtDelimiter || i >= text.length || isDelimiter(text[i]) && !isInsideUnclosedBracket(str, text[i]) || isQuote(text[i]) && !nextQuoteIsEndQuote(i) || isDigit(text[i])) {
2922
3008
  parseConcatenatedString();
2923
3009
  return true;
2924
3010
  }
3011
+ if (text[i] === "\\") throwUnexpectedCharacter();
2925
3012
  const iPrevChar = prevNonWhitespaceIndex(iQuote - 1);
2926
3013
  const prevChar = text.charAt(iPrevChar);
2927
3014
  if (prevChar === ",") {
@@ -2935,7 +3022,7 @@ function jsonrepair(text) {
2935
3022
  return parseString(true);
2936
3023
  }
2937
3024
  output = output.substring(0, oBefore);
2938
- i = iQuote + 1;
3025
+ i = iQuote + (entity ? entity.length : 1);
2939
3026
  str = `${str.substring(0, oQuote)}\\${str.substring(oQuote)}`;
2940
3027
  } else if (stopAtDelimiter && isUnquotedStringDelimiter(text[i])) {
2941
3028
  if (text[i - 1] === ":" && regexUrlStart.test(text.substring(iBefore + 1, i + 2))) while (i < text.length && regexUrlChar.test(text[i])) {
@@ -2946,6 +3033,12 @@ function jsonrepair(text) {
2946
3033
  output += str;
2947
3034
  parseConcatenatedString();
2948
3035
  return true;
3036
+ } else if (entity) {
3037
+ const char = entity.char;
3038
+ if (char === "\"") str += "\\\"";
3039
+ else if (isControlCharacter(char)) str += controlCharacters[char];
3040
+ else str += char;
3041
+ i += entity.length;
2949
3042
  } else if (text[i] === "\\") {
2950
3043
  const char = text.charAt(i + 1);
2951
3044
  if (escapeCharacters[char] !== void 0) {
@@ -3007,51 +3100,48 @@ function jsonrepair(text) {
3007
3100
  */
3008
3101
  function parseNumber() {
3009
3102
  const start = i;
3103
+ let num = "";
3104
+ let invalid = false;
3010
3105
  if (text[i] === "-") {
3106
+ num += text[i];
3107
+ i++;
3108
+ if (!isDigit(text[i]) && atEndOfNumber()) num += "0";
3109
+ }
3110
+ if (text[i] === "0" && isDigit(text[i + 1])) invalid = true;
3111
+ while (isDigit(text[i])) {
3112
+ num += text[i];
3011
3113
  i++;
3012
- if (atEndOfNumber()) {
3013
- repairNumberEndingWithNumericSymbol(start);
3014
- return true;
3015
- }
3016
- if (!isDigit(text[i])) {
3017
- i = start;
3018
- return false;
3019
- }
3020
3114
  }
3021
- while (isDigit(text[i])) i++;
3022
3115
  if (text[i] === ".") {
3116
+ if (num === "" || num === "-") num += "0";
3117
+ num += text[i];
3023
3118
  i++;
3024
- if (atEndOfNumber()) {
3025
- repairNumberEndingWithNumericSymbol(start);
3026
- return true;
3027
- }
3028
- if (!isDigit(text[i])) {
3029
- i = start;
3030
- return false;
3119
+ if (!isDigit(text[i])) num += "0";
3120
+ while (isDigit(text[i])) {
3121
+ num += text[i];
3122
+ i++;
3031
3123
  }
3032
- while (isDigit(text[i])) i++;
3033
3124
  }
3034
- if (text[i] === "e" || text[i] === "E") {
3035
- i++;
3036
- if (text[i] === "-" || text[i] === "+") i++;
3037
- if (atEndOfNumber()) {
3038
- repairNumberEndingWithNumericSymbol(start);
3039
- return true;
3125
+ if (i > start) {
3126
+ if (text[i] === "e" || text[i] === "E") {
3127
+ if (num === "-") invalid = true;
3128
+ num += text[i];
3129
+ i++;
3130
+ if (text[i] === "-" || text[i] === "+") {
3131
+ num += text[i];
3132
+ i++;
3133
+ }
3134
+ if (!isDigit(text[i])) num += "0";
3135
+ while (isDigit(text[i])) {
3136
+ num += text[i];
3137
+ i++;
3138
+ }
3040
3139
  }
3041
- if (!isDigit(text[i])) {
3140
+ if (!atEndOfNumber()) {
3042
3141
  i = start;
3043
3142
  return false;
3044
3143
  }
3045
- while (isDigit(text[i])) i++;
3046
- }
3047
- if (!atEndOfNumber()) {
3048
- i = start;
3049
- return false;
3050
- }
3051
- if (i > start) {
3052
- const num = text.slice(start, i);
3053
- const hasInvalidLeadingZero = /^0\d/.test(num);
3054
- output += hasInvalidLeadingZero ? `"${num}"` : num;
3144
+ output += invalid ? `"${text.substring(start, i)}"` : num;
3055
3145
  return true;
3056
3146
  }
3057
3147
  return false;
@@ -3064,7 +3154,7 @@ function jsonrepair(text) {
3064
3154
  return parseKeyword("true", "true") || parseKeyword("false", "false") || parseKeyword("null", "null") || parseKeyword("True", "true") || parseKeyword("False", "false") || parseKeyword("None", "null");
3065
3155
  }
3066
3156
  function parseKeyword(name, value) {
3067
- if (text.slice(i, i + name.length) === name) {
3157
+ if (text.slice(i, i + name.length) === name && !isFunctionNameChar(text[i + name.length])) {
3068
3158
  output += value;
3069
3159
  i += name.length;
3070
3160
  return true;
@@ -3117,12 +3207,14 @@ function jsonrepair(text) {
3117
3207
  while (prev > 0 && isWhitespace(text, prev)) prev--;
3118
3208
  return prev;
3119
3209
  }
3210
+ function nextQuoteIsEndQuote(index) {
3211
+ let next = index + 1;
3212
+ while (next < text.length && isWhitespace(text, next)) next++;
3213
+ return next >= text.length || isDelimiter(text[next]);
3214
+ }
3120
3215
  function atEndOfNumber() {
3121
3216
  return i >= text.length || isDelimiter(text[i]) || isWhitespace(text, i);
3122
3217
  }
3123
- function repairNumberEndingWithNumericSymbol(start) {
3124
- output += `${text.slice(start, i)}0`;
3125
- }
3126
3218
  function throwInvalidCharacter(char) {
3127
3219
  throw new JSONRepairError(`Invalid character ${JSON.stringify(char)}`, i);
3128
3220
  }
@@ -3766,7 +3858,8 @@ function slugify(path) {
3766
3858
  */
3767
3859
  async function writeSkippedDiffs(cwd, sections) {
3768
3860
  if (sections.length === 0) return [];
3769
- await mkdir(join(cwd, SKIPPED_DIFF_DIR), { recursive: true });
3861
+ const dir = join(cwd, SKIPPED_DIFF_DIR);
3862
+ await mkdir(dir, { recursive: true });
3770
3863
  const files = [];
3771
3864
  for (const { path, section } of sections) {
3772
3865
  const relative = join(SKIPPED_DIFF_DIR, slugify(path));
@@ -3838,7 +3931,7 @@ function normalizeSubject(body) {
3838
3931
  var MAX_LINE_DELTA = 2;
3839
3932
  /** Min token-set Jaccard similarity of normalised subjects required to merge. */
3840
3933
  var SUBJECT_SIMILARITY_THRESHOLD = .6;
3841
- var STOP_WORDS = new Set([
3934
+ var STOP_WORDS = /* @__PURE__ */ new Set([
3842
3935
  "a",
3843
3936
  "an",
3844
3937
  "the",
@@ -3961,7 +4054,7 @@ function triageFindings(groups) {
3961
4054
  }
3962
4055
  //#endregion
3963
4056
  //#region src/gitlab-review.ts
3964
- var DEFAULT_REVIEW_TIMEOUT_MS = 600 * 1e3;
4057
+ var DEFAULT_REVIEW_TIMEOUT_MS = 6e5;
3965
4058
  var DEFAULT_MAX_DIFF_CHARS = 1e5;
3966
4059
  var CONVENTION_FILES = ["AGENTS.md", "CLAUDE.md"];
3967
4060
  var REVIEW_RULE_FILES = ["REVIEW.md"];
@@ -3979,7 +4072,7 @@ var NOISE_PATH_PATTERNS = [
3979
4072
  /\.d\.ts$/,
3980
4073
  /\.(js|css)\.map$/
3981
4074
  ];
3982
- var LOCKFILE_BASENAMES = new Set([
4075
+ var LOCKFILE_BASENAMES = /* @__PURE__ */ new Set([
3983
4076
  "package-lock.json",
3984
4077
  "npm-shrinkwrap.json",
3985
4078
  "yarn.lock",
@@ -4020,12 +4113,11 @@ var exec = promisify(execFile);
4020
4113
  *
4021
4114
  * pi-ai >=0.82 requires an explicit stream function (earlier versions built one
4022
4115
  * internally from model + getApiKey); `streamSimple` is the drop-in. Critically,
4023
- * 0.83 also moved cloudflare-ai-gateway base-URL substitution — the
4024
- * `{CLOUDFLARE_ACCOUNT_ID}` / `{CLOUDFLARE_GATEWAY_ID}` placeholders — from a
4025
- * direct `process.env` read to an explicit `env` on the stream options. Without
4026
- * threading `env` through, the gateway URL keeps its literal placeholders and
4027
- * every request fails with Cloudflare 401 2035 ("Invalid request path"). Passing
4028
- * `env` is harmless for providers whose base URL has no placeholders.
4116
+ * 0.83 also moved base-URL placeholder substitution from a direct `process.env`
4117
+ * read to an explicit `env` on the stream options. Providers whose base URL
4118
+ * contains `{VAR}` placeholders keep them literal — and fail every request —
4119
+ * unless `env` is threaded through. Passing `env` is harmless for providers
4120
+ * whose base URL has none.
4029
4121
  *
4030
4122
  * `stream` is injectable so the env threading can be unit-tested without a live call.
4031
4123
  */
@@ -5404,7 +5496,8 @@ async function startOtelBridge(options = {}) {
5404
5496
  createAgentTelemetry(runId) {
5405
5497
  const reviewerEntry = openByRun.get(runId)?.get(GEN_AI_PHASE);
5406
5498
  if (!reviewerEntry || reviewerEntry.closed) return void 0;
5407
- return buildAgentSubscriber(tracer, tokenUsage, operationCost, timeToFirstToken, trace.setSpan(context.active(), reviewerEntry.span), {
5499
+ const reviewerSpanCtx = trace.setSpan(context.active(), reviewerEntry.span);
5500
+ return buildAgentSubscriber(tracer, tokenUsage, operationCost, timeToFirstToken, reviewerSpanCtx, {
5408
5501
  ciAttrs,
5409
5502
  runId,
5410
5503
  configuredModel: runMeta.get(runId)?.model,
@@ -5694,7 +5787,7 @@ async function loadDefaultRuntime() {
5694
5787
  const [sdkNode, resources, semconv] = modules;
5695
5788
  const serviceResource = resources.resourceFromAttributes({
5696
5789
  [semconv.ATTR_SERVICE_NAME ?? "service.name"]: SERVICE_NAME,
5697
- [semconv.ATTR_SERVICE_VERSION ?? "service.version"]: "0.9.6"
5790
+ [semconv.ATTR_SERVICE_VERSION ?? "service.version"]: "0.9.7"
5698
5791
  });
5699
5792
  applyOtelExporterDefaults(process.env);
5700
5793
  const sdk = new sdkNode.NodeSDK({ resource: resources.defaultResource().merge(serviceResource) });
@@ -6166,7 +6259,7 @@ function boldCommentTitle(body) {
6166
6259
  */
6167
6260
  function buildCommentBody(body, commitSha, confidence) {
6168
6261
  const confidenceLine = `_Confidence: ${confidence}._`;
6169
- const footer = `<sub>Reviewed by ${PRODUCT_LINK} v0.9.6 for commit ${commitSha}.</sub>`;
6262
+ const footer = `<sub>Reviewed by ${PRODUCT_LINK} v0.9.7 for commit ${commitSha}.</sub>`;
6170
6263
  return `${boldCommentTitle(body.trim())}\n\n${confidenceLine}\n\n---\n\n${footer}`;
6171
6264
  }
6172
6265
  function buildPayload(comment, body, refs, resolved) {
@@ -6745,7 +6838,7 @@ function createPlatform(config) {
6745
6838
  * assumed to be GitLab (`/<repo>/-/blob/<ref>/<path>`), which covers both
6746
6839
  * gitlab.com and the self-hosted instances this tool mostly runs against.
6747
6840
  */
6748
- var GITHUB_HOSTS = new Set(["github.com", "www.github.com"]);
6841
+ var GITHUB_HOSTS = /* @__PURE__ */ new Set(["github.com", "www.github.com"]);
6749
6842
  /** Percent-encode each path segment while keeping the `/` separators intact. */
6750
6843
  function encodePath(path) {
6751
6844
  return path.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
@@ -6796,7 +6889,7 @@ function gitRepoWebUrl(cloneUrl) {
6796
6889
  */
6797
6890
  function skillSourceUrl(origin, context = {}) {
6798
6891
  switch (origin.kind) {
6799
- case "builtin": return blobUrl(PRODUCT_URL, "0.9.6", `skills/${origin.name}/SKILL.md`);
6892
+ case "builtin": return blobUrl(PRODUCT_URL, "0.9.7", `skills/${origin.name}/SKILL.md`);
6800
6893
  case "project":
6801
6894
  if (!context.projectWebUrl || !context.commitSha) return void 0;
6802
6895
  return blobUrl(context.projectWebUrl, context.commitSha, `${origin.path}/SKILL.md`);
@@ -7375,10 +7468,10 @@ async function main(argv = process.argv.slice(2)) {
7375
7468
  return;
7376
7469
  }
7377
7470
  if (argv.includes("--version") || argv.includes("-v")) {
7378
- console.log("0.9.6");
7471
+ console.log("0.9.7");
7379
7472
  return;
7380
7473
  }
7381
- process.stderr.write(`[code-review] @weareikko/code-review v0.9.6\n`);
7474
+ process.stderr.write(`[code-review] @weareikko/code-review v0.9.7\n`);
7382
7475
  assertNodeVersion();
7383
7476
  applyCodeReviewEnvPrefix();
7384
7477
  applyDefaultCacheRetention();
@@ -7401,4 +7494,4 @@ if (isDirectRun()) main().catch((error) => {
7401
7494
  //#endregion
7402
7495
  export { normalizeBody as $, SUMMARY_HISTORY_END as A, buildSummaryHistoryEntries as B, createDiagnosticContext as C, traceDiagnosticPhase as D, traceDiagnostic as E, SUMMARY_MARKER as F, findExistingSummaryNoteId as G, extractSummaryHistoryEntries as H, buildArchivedSummaryEntry as I, upsertSummaryNote as J, stripSummaryHistory as K, buildReviewedCommitFooter as L, SUMMARY_HISTORY_ENTRY_START as M, SUMMARY_HISTORY_LIMIT as N, normalizeSeverity as O, SUMMARY_HISTORY_START as P, fingerprints as Q, buildSizeNoticeBlock as R, DIAGNOSTIC_CHANNEL_PREFIX as S, diagnosticChannels as T, findExistingReviewedCommitSha as U, extractReviewedCommitSha as V, findExistingSummaryNote as W, extractDiffHunkContext as X, appendFingerprintMarkers as Y, extractExistingFingerprints as Z, filterDiff as _, main as a, resolveSkillCacheDir as at, parseReviewMarkdownWithWarnings as b, blobUrl as c, skillDisplayName as d, sha256 as et, skillSourceUrl as f, startOtelBridge as g, isOtelEnabled as h, formatUsageLine as i, resolveNpmSkillDir as it, SUMMARY_HISTORY_ENTRY_END as j, toGitLabReviewSeverity as k, formatSkillLink as l, buildPayload as m, formatPerModelUsage as n, loadNamedSkill as nt, run as o, buildGeneratedComments as p, stripSummaryMarker as q, formatSkillsFooter as r, parseSkillSpec as rt, withHttpStamping as s, countPostedBySeverity as t, gitSkillCacheKey as tt, gitRepoWebUrl as u, runReview as v, createDiagnosticRunId as w, DIAGNOSTIC_CHANNEL_NAMES as x, parseReviewMarkdown as y, buildSummaryBody as z };
7403
7496
 
7404
- //# sourceMappingURL=cli-D6vNV3kA.js.map
7497
+ //# sourceMappingURL=cli-m4pS6_5q.js.map