@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.
- package/dist/{cli-D6vNV3kA.js → cli-m4pS6_5q.js} +183 -90
- package/dist/cli-m4pS6_5q.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/config.d.ts +6 -6
- package/dist/config.d.ts.map +1 -1
- package/dist/diagnostics.d.ts +2 -2
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/gitlab-review.d.ts +5 -6
- package/dist/gitlab-review.d.ts.map +1 -1
- package/dist/marketplaces.d.ts +1 -1
- package/dist/marketplaces.d.ts.map +1 -1
- package/dist/review.js +1 -1
- package/package.json +15 -15
- package/dist/cli-D6vNV3kA.js.map +0 -1
|
@@ -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,
|
|
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:
|
|
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
|
-
|
|
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.
|
|
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:
|
|
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`, `
|
|
1787
|
-
*
|
|
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
|
|
1816
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
+
""": "\"",
|
|
2635
|
+
"&": "&",
|
|
2636
|
+
"<": "<",
|
|
2637
|
+
">": ">",
|
|
2638
|
+
"'": "'"
|
|
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 """ 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)
|
|
2807
|
-
|
|
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)
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
2969
|
+
const skipEscapeChars = text[i] === "\\";
|
|
2887
2970
|
if (skipEscapeChars) {
|
|
2888
2971
|
i++;
|
|
2889
|
-
|
|
2972
|
+
if (!isQuote(text[i])) throwUnexpectedCharacter();
|
|
2890
2973
|
}
|
|
2891
|
-
|
|
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
|
-
|
|
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 (
|
|
3025
|
-
|
|
3026
|
-
|
|
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 (
|
|
3035
|
-
i
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
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 (!
|
|
3140
|
+
if (!atEndOfNumber()) {
|
|
3042
3141
|
i = start;
|
|
3043
3142
|
return false;
|
|
3044
3143
|
}
|
|
3045
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
4024
|
-
*
|
|
4025
|
-
*
|
|
4026
|
-
*
|
|
4027
|
-
*
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
7471
|
+
console.log("0.9.7");
|
|
7379
7472
|
return;
|
|
7380
7473
|
}
|
|
7381
|
-
process.stderr.write(`[code-review] @weareikko/code-review v0.9.
|
|
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-
|
|
7497
|
+
//# sourceMappingURL=cli-m4pS6_5q.js.map
|