@jpkme/diff-review 0.1.10 → 0.1.12
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/README.md +38 -15
- package/bin/diff-review.mjs +151 -34
- package/lib/acp-policy.mjs +136 -0
- package/lib/acp-policy.test.mjs +83 -0
- package/lib/acp-shell.mjs +379 -0
- package/lib/acp-shell.test.mjs +127 -0
- package/lib/acp.mjs +368 -0
- package/lib/acp.test.mjs +58 -0
- package/lib/agent-login.mjs +105 -0
- package/lib/agents.mjs +87 -14
- package/lib/agents.test.mjs +4 -6
- package/lib/check-report.mjs +29 -11
- package/lib/extract-cache.test.mjs +162 -0
- package/lib/extract.mjs +197 -1
- package/lib/model-label.mjs +263 -0
- package/lib/model-label.test.mjs +77 -0
- package/lib/models-cache.mjs +75 -0
- package/lib/models-cache.test.mjs +50 -0
- package/lib/output-name.mjs +8 -0
- package/lib/prompt.mjs +168 -22
- package/lib/review-dir.mjs +108 -0
- package/lib/review-meta.mjs +5 -3
- package/lib/review-meta.test.mjs +11 -0
- package/lib/review-prep.mjs +63 -0
- package/lib/validate.mjs +2 -2
- package/lib/wait-output.mjs +46 -23
- package/lib/wait-output.test.mjs +24 -1
- package/package.json +3 -1
- package/web/build/client/_app/immutable/assets/2.CKE12GwZ.css +1 -0
- package/web/build/client/_app/immutable/assets/2.CKE12GwZ.css.br +0 -0
- package/web/build/client/_app/immutable/assets/2.CKE12GwZ.css.gz +0 -0
- package/web/build/client/_app/immutable/assets/3.Dx3c_e7d.css +1 -0
- package/web/build/client/_app/immutable/assets/3.Dx3c_e7d.css.br +0 -0
- package/web/build/client/_app/immutable/assets/3.Dx3c_e7d.css.gz +0 -0
- package/web/build/client/_app/immutable/assets/storage.gDV-BhtT.css +1 -0
- package/web/build/client/_app/immutable/assets/storage.gDV-BhtT.css.br +0 -0
- package/web/build/client/_app/immutable/assets/storage.gDV-BhtT.css.gz +0 -0
- package/web/build/client/_app/immutable/chunks/{BBWAo_yW.js → B5ByWcPE.js} +2 -2
- package/web/build/client/_app/immutable/chunks/B5ByWcPE.js.br +0 -0
- package/web/build/client/_app/immutable/chunks/B5ByWcPE.js.gz +0 -0
- package/web/build/client/_app/immutable/chunks/CPyWH418.js +1 -0
- package/web/build/client/_app/immutable/chunks/CPyWH418.js.br +0 -0
- package/web/build/client/_app/immutable/chunks/CPyWH418.js.gz +0 -0
- package/web/build/client/_app/immutable/chunks/D8WOD_eS.js +3 -0
- package/web/build/client/_app/immutable/chunks/D8WOD_eS.js.br +0 -0
- package/web/build/client/_app/immutable/chunks/D8WOD_eS.js.gz +0 -0
- package/web/build/client/_app/immutable/chunks/NwkMsx0h.js +210 -0
- package/web/build/client/_app/immutable/chunks/NwkMsx0h.js.br +0 -0
- package/web/build/client/_app/immutable/chunks/NwkMsx0h.js.gz +0 -0
- package/web/build/client/_app/immutable/chunks/lUYXRDtZ.js +1 -0
- package/web/build/client/_app/immutable/chunks/lUYXRDtZ.js.br +0 -0
- package/web/build/client/_app/immutable/chunks/lUYXRDtZ.js.gz +0 -0
- package/web/build/client/_app/immutable/entry/app.BiBohpyO.js +2 -0
- package/web/build/client/_app/immutable/entry/app.BiBohpyO.js.br +0 -0
- package/web/build/client/_app/immutable/entry/app.BiBohpyO.js.gz +0 -0
- package/web/build/client/_app/immutable/entry/start.CjWbsiSO.js +1 -0
- package/web/build/client/_app/immutable/entry/start.CjWbsiSO.js.br +0 -0
- package/web/build/client/_app/immutable/entry/start.CjWbsiSO.js.gz +0 -0
- package/web/build/client/_app/immutable/nodes/0.Cak4ehiE.js +1 -0
- package/web/build/client/_app/immutable/nodes/0.Cak4ehiE.js.br +0 -0
- package/web/build/client/_app/immutable/nodes/0.Cak4ehiE.js.gz +0 -0
- package/web/build/client/_app/immutable/nodes/1.BzPXaJJj.js +1 -0
- package/web/build/client/_app/immutable/nodes/1.BzPXaJJj.js.br +2 -0
- package/web/build/client/_app/immutable/nodes/1.BzPXaJJj.js.gz +0 -0
- package/web/build/client/_app/immutable/nodes/2.Cp6Ttd_9.js +10 -0
- package/web/build/client/_app/immutable/nodes/2.Cp6Ttd_9.js.br +0 -0
- package/web/build/client/_app/immutable/nodes/2.Cp6Ttd_9.js.gz +0 -0
- package/web/build/client/_app/immutable/nodes/3.DLiDQm__.js +171 -0
- package/web/build/client/_app/immutable/nodes/3.DLiDQm__.js.br +0 -0
- package/web/build/client/_app/immutable/nodes/3.DLiDQm__.js.gz +0 -0
- package/web/build/client/_app/version.json +1 -1
- package/web/build/client/_app/version.json.br +0 -0
- package/web/build/client/_app/version.json.gz +0 -0
- package/web/build/handler.js +3 -3
- package/web/build/index.js +3 -3
- package/web/build/server/chunks/{0-SseDy__O.js → 0-BYok4D0n.js} +2 -2
- package/web/build/server/chunks/{0-SseDy__O.js.map → 0-BYok4D0n.js.map} +1 -1
- package/web/build/server/chunks/1-C7zcB0q0.js +7 -0
- package/web/build/server/chunks/1-C7zcB0q0.js.map +1 -0
- package/web/build/server/chunks/2-v6ejI_0-.js +7 -0
- package/web/build/server/chunks/2-v6ejI_0-.js.map +1 -0
- package/web/build/server/chunks/3-DAAmG6VX.js +7 -0
- package/web/build/server/chunks/3-DAAmG6VX.js.map +1 -0
- package/web/build/server/chunks/_server.ts-qYVrMYPB.js +62 -0
- package/web/build/server/chunks/_server.ts-qYVrMYPB.js.map +1 -0
- package/web/build/server/chunks/agent-sessions-DSqGavSn.js +2688 -0
- package/web/build/server/chunks/agent-sessions-DSqGavSn.js.map +1 -0
- package/web/build/server/chunks/{remote-1g4ayag-B67QMR_2.js → git-review-CpO_G8z8.js} +70 -704
- package/web/build/server/chunks/git-review-CpO_G8z8.js.map +1 -0
- package/web/build/server/chunks/{handler-B5P65tLk.js → handler-Di1wJQuU.js} +3 -3
- package/web/build/server/chunks/{handler-B5P65tLk.js.map → handler-Di1wJQuU.js.map} +1 -1
- package/web/build/server/chunks/hooks.server-BLOHobEK.js +42 -0
- package/web/build/server/chunks/hooks.server-BLOHobEK.js.map +1 -0
- package/web/build/server/chunks/{index.js-CL95e0Gl.js → index.js-yLYmrAWy.js} +3 -5
- package/web/build/server/chunks/index.js-yLYmrAWy.js.map +1 -0
- package/web/build/server/chunks/manifest.js-CXj8IIo3.js +58 -0
- package/web/build/server/chunks/manifest.js-CXj8IIo3.js.map +1 -0
- package/web/build/server/chunks/remote-1g4ayag-BiJeU0Pk.js +144 -0
- package/web/build/server/chunks/remote-1g4ayag-BiJeU0Pk.js.map +1 -0
- package/web/build/server/chunks/remote-wh6t6j-CHanXurm.js +162 -0
- package/web/build/server/chunks/remote-wh6t6j-CHanXurm.js.map +1 -0
- package/web/build/server/chunks/validate-BsTKyhsq.js +1073 -0
- package/web/build/server/chunks/validate-BsTKyhsq.js.map +1 -0
- package/web/build/client/_app/immutable/assets/2.CNFDEnjx.css +0 -1
- package/web/build/client/_app/immutable/assets/2.CNFDEnjx.css.br +0 -0
- package/web/build/client/_app/immutable/assets/2.CNFDEnjx.css.gz +0 -0
- package/web/build/client/_app/immutable/assets/3.PHTPLlZ7.css +0 -1
- package/web/build/client/_app/immutable/assets/3.PHTPLlZ7.css.br +0 -0
- package/web/build/client/_app/immutable/assets/3.PHTPLlZ7.css.gz +0 -0
- package/web/build/client/_app/immutable/assets/ThemeToggle.DcT79vPK.css +0 -1
- package/web/build/client/_app/immutable/assets/ThemeToggle.DcT79vPK.css.br +0 -0
- package/web/build/client/_app/immutable/assets/ThemeToggle.DcT79vPK.css.gz +0 -0
- package/web/build/client/_app/immutable/chunks/BBWAo_yW.js.br +0 -0
- package/web/build/client/_app/immutable/chunks/BBWAo_yW.js.gz +0 -0
- package/web/build/client/_app/immutable/chunks/Dur-vdUf.js +0 -3
- package/web/build/client/_app/immutable/chunks/Dur-vdUf.js.br +0 -0
- package/web/build/client/_app/immutable/chunks/Dur-vdUf.js.gz +0 -0
- package/web/build/client/_app/immutable/chunks/QdZF3nkf.js +0 -176
- package/web/build/client/_app/immutable/chunks/QdZF3nkf.js.br +0 -0
- package/web/build/client/_app/immutable/chunks/QdZF3nkf.js.gz +0 -0
- package/web/build/client/_app/immutable/chunks/tYeNsyIK.js +0 -1
- package/web/build/client/_app/immutable/chunks/tYeNsyIK.js.br +0 -0
- package/web/build/client/_app/immutable/chunks/tYeNsyIK.js.gz +0 -0
- package/web/build/client/_app/immutable/entry/app.Dce6uJU3.js +0 -2
- package/web/build/client/_app/immutable/entry/app.Dce6uJU3.js.br +0 -0
- package/web/build/client/_app/immutable/entry/app.Dce6uJU3.js.gz +0 -0
- package/web/build/client/_app/immutable/entry/start.DnBSC38l.js +0 -1
- package/web/build/client/_app/immutable/entry/start.DnBSC38l.js.br +0 -0
- package/web/build/client/_app/immutable/entry/start.DnBSC38l.js.gz +0 -0
- package/web/build/client/_app/immutable/nodes/0.s5xmjXoS.js +0 -1
- package/web/build/client/_app/immutable/nodes/0.s5xmjXoS.js.br +0 -1
- package/web/build/client/_app/immutable/nodes/0.s5xmjXoS.js.gz +0 -0
- package/web/build/client/_app/immutable/nodes/1.5Iv4ssDT.js +0 -1
- package/web/build/client/_app/immutable/nodes/1.5Iv4ssDT.js.br +0 -0
- package/web/build/client/_app/immutable/nodes/1.5Iv4ssDT.js.gz +0 -0
- package/web/build/client/_app/immutable/nodes/2.BomzYVBF.js +0 -66
- package/web/build/client/_app/immutable/nodes/2.BomzYVBF.js.br +0 -0
- package/web/build/client/_app/immutable/nodes/2.BomzYVBF.js.gz +0 -0
- package/web/build/client/_app/immutable/nodes/3.CeUs4lBX.js +0 -170
- package/web/build/client/_app/immutable/nodes/3.CeUs4lBX.js.br +0 -0
- package/web/build/client/_app/immutable/nodes/3.CeUs4lBX.js.gz +0 -0
- package/web/build/server/chunks/1-2OO2bb7H.js +0 -7
- package/web/build/server/chunks/1-2OO2bb7H.js.map +0 -1
- package/web/build/server/chunks/2-BJvuqKN6.js +0 -7
- package/web/build/server/chunks/2-BJvuqKN6.js.map +0 -1
- package/web/build/server/chunks/3-ZIl1rHef.js +0 -7
- package/web/build/server/chunks/3-ZIl1rHef.js.map +0 -1
- package/web/build/server/chunks/hooks.server-DERFlOe1.js +0 -14
- package/web/build/server/chunks/hooks.server-DERFlOe1.js.map +0 -1
- package/web/build/server/chunks/index.js-CL95e0Gl.js.map +0 -1
- package/web/build/server/chunks/manifest.js-uD_a0079.js +0 -50
- package/web/build/server/chunks/manifest.js-uD_a0079.js.map +0 -1
- package/web/build/server/chunks/remote-1g4ayag-B67QMR_2.js.map +0 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { e as error, g as get_request_store, b as stringify_remote_arg, M as MUTATIVE_METHODS, w as with_request_store, c as create_remote_key, n as noop, H as HttpError, S as SvelteKitError, h as handle_error_and_jsonify
|
|
2
|
-
import {
|
|
3
|
-
import { existsSync, readFileSync, statSync, readdirSync, mkdtempSync, rmSync,
|
|
1
|
+
import { e as error, g as get_request_store, b as stringify_remote_arg, M as MUTATIVE_METHODS, w as with_request_store, c as create_remote_key, n as noop, H as HttpError, S as SvelteKitError, h as handle_error_and_jsonify } from './index.js-yLYmrAWy.js';
|
|
2
|
+
import { m as makeOutputFilename, a as assertGitRepo, b as branchTips, f as fingerprint, e as ensureReviewDir, l as listBranches, g as getRemoteUrl, t as tryResolveGitRoot, i as isOutputFilename, d as decodePayload, c as locateReviewFile, r as reviewDir, h as companionPaths, s as showFileLines, j as loadBranchDiff, k as git, F as FINDING_CLASSES, n as FINDING_SEVERITIES, o as FINDING_KINDS, G as GROUP_KINDS, W as WHY_SOURCES, B as BLOCK_OPS, S as SKIP_REASONS } from './validate-BsTKyhsq.js';
|
|
3
|
+
import { existsSync, readFileSync, statSync, readdirSync, writeFileSync, mkdtempSync, rmSync, mkdirSync } from 'node:fs';
|
|
4
4
|
import { execFileSync, spawnSync } from 'node:child_process';
|
|
5
|
-
import { createHash } from 'node:crypto';
|
|
6
5
|
import { tmpdir, platform } from 'node:os';
|
|
6
|
+
import { join, resolve, basename } from 'node:path';
|
|
7
|
+
import { createHash } from 'node:crypto';
|
|
7
8
|
import process$1 from 'node:process';
|
|
8
9
|
|
|
9
10
|
//#region node_modules/@sveltejs/kit/src/runtime/app/server/remote/shared.js
|
|
@@ -957,502 +958,6 @@ Object.defineProperty(query, "live", {
|
|
|
957
958
|
enumerable: true
|
|
958
959
|
});
|
|
959
960
|
|
|
960
|
-
//#region ../lib/fingerprint.mjs
|
|
961
|
-
/**
|
|
962
|
-
* Tips cortos de SHA. Sin APIs de Node: la UI lo importa para armar el nombre del JSON.
|
|
963
|
-
*/
|
|
964
|
-
function shortSha(sha, n = 10) {
|
|
965
|
-
const hex = String(sha || "").toLowerCase().replace(/[^a-f0-9]/g, "");
|
|
966
|
-
return hex.length >= 7 ? hex.slice(0, n) : "";
|
|
967
|
-
}
|
|
968
|
-
function fingerprint(branchSha, baseSha) {
|
|
969
|
-
const a = shortSha(branchSha);
|
|
970
|
-
const b = shortSha(baseSha);
|
|
971
|
-
if (!a || !b) return null;
|
|
972
|
-
return `${a}-${b}`;
|
|
973
|
-
}
|
|
974
|
-
//#endregion
|
|
975
|
-
//#region ../lib/output-name.mjs
|
|
976
|
-
/**
|
|
977
|
-
* Nombre del JSON que escribe el agente.
|
|
978
|
-
* Preferido: tips cortos branch/base. Legacy (con stamp o sin tips) sigue válido al importar.
|
|
979
|
-
*/
|
|
980
|
-
/** Prefijo del formato actual. */
|
|
981
|
-
var OUTPUT_PREFIX = "diff-rev";
|
|
982
|
-
/** Nombre legacy fijo. */
|
|
983
|
-
var OUTPUT_FILENAME_LEGACY = `diff-review-output.json`;
|
|
984
|
-
/**
|
|
985
|
-
* Válidos:
|
|
986
|
-
* - diff-rev_{branchTip}_{baseTip}.json
|
|
987
|
-
* - diff-report_{branchTip}-{baseTip}_YYYYMMDD-HHmmss.json (legacy)
|
|
988
|
-
* - diff-review-output.json / diff-review-output-YYYYMMDD-HHmmss.json (legacy)
|
|
989
|
-
*/
|
|
990
|
-
var OUTPUT_FILENAME_RE = /^(?:diff-rev_[0-9a-f]{7,12}_[0-9a-f]{7,12}|diff-report_[0-9a-f]{7,12}-[0-9a-f]{7,12}_\d{8}-\d{6}|diff-review-output(?:-\d{8}-\d{6})?)\.json$/i;
|
|
991
|
-
/**
|
|
992
|
-
* @param {{ branchSha?: string, baseSha?: string }} [input]
|
|
993
|
-
* @returns {string}
|
|
994
|
-
* con tips: diff-rev_aaaaaaaaaa_bbbbbbbbbb.json
|
|
995
|
-
* sin tips: diff-review-output.json
|
|
996
|
-
*/
|
|
997
|
-
function makeOutputFilename(input = {}) {
|
|
998
|
-
const opts = input instanceof Date ? {} : input || {};
|
|
999
|
-
const fp = fingerprint(opts.branchSha, opts.baseSha);
|
|
1000
|
-
if (fp) {
|
|
1001
|
-
const [branchTip, baseTip] = fp.split("-");
|
|
1002
|
-
if (branchTip && baseTip) return `${OUTPUT_PREFIX}_${branchTip}_${baseTip}.json`;
|
|
1003
|
-
}
|
|
1004
|
-
return OUTPUT_FILENAME_LEGACY;
|
|
1005
|
-
}
|
|
1006
|
-
function isOutputFilename(name) {
|
|
1007
|
-
return OUTPUT_FILENAME_RE.test(String(name || "").trim());
|
|
1008
|
-
}
|
|
1009
|
-
//#endregion
|
|
1010
|
-
//#region ../lib/json-payload.mjs
|
|
1011
|
-
function tryParseJson(s) {
|
|
1012
|
-
try {
|
|
1013
|
-
return JSON.parse(s);
|
|
1014
|
-
} catch {
|
|
1015
|
-
return null;
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
/** Extrae un objeto JSON del texto del modelo (fences, prosa alrededor, envelope). */
|
|
1019
|
-
function decodePayload(text) {
|
|
1020
|
-
const raw = String(text || "").trim();
|
|
1021
|
-
if (!raw) throw new Error("Respuesta vacía.");
|
|
1022
|
-
const fence = raw.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
1023
|
-
if (fence) {
|
|
1024
|
-
const parsed = tryParseJson(fence[1].trim());
|
|
1025
|
-
if (parsed && typeof parsed === "object") return parsed;
|
|
1026
|
-
}
|
|
1027
|
-
const direct = tryParseJson(raw);
|
|
1028
|
-
if (direct && typeof direct === "object") return direct;
|
|
1029
|
-
const start = raw.indexOf("{");
|
|
1030
|
-
const end = raw.lastIndexOf("}");
|
|
1031
|
-
if (start >= 0 && end > start) {
|
|
1032
|
-
const parsed = tryParseJson(raw.slice(start, end + 1));
|
|
1033
|
-
if (parsed && typeof parsed === "object") return parsed;
|
|
1034
|
-
}
|
|
1035
|
-
throw new Error("No se pudo interpretar la respuesta como JSON.");
|
|
1036
|
-
}
|
|
1037
|
-
//#endregion
|
|
1038
|
-
//#region ../lib/config.mjs
|
|
1039
|
-
var DEFAULT_IGNORE = [
|
|
1040
|
-
"**/package-lock.json",
|
|
1041
|
-
"**/pnpm-lock.yaml",
|
|
1042
|
-
"**/yarn.lock",
|
|
1043
|
-
"**/bun.lock",
|
|
1044
|
-
"**/bun.lockb",
|
|
1045
|
-
"**/*.g.cs",
|
|
1046
|
-
"**/*Snapshot.cs",
|
|
1047
|
-
"**/*.Designer.cs",
|
|
1048
|
-
"**/node_modules/**",
|
|
1049
|
-
"**/.svelte-kit/**",
|
|
1050
|
-
"**/dist/**",
|
|
1051
|
-
"**/build/**",
|
|
1052
|
-
"**/*.min.js",
|
|
1053
|
-
"**/*.min.css",
|
|
1054
|
-
"**/CODE_REVIEW_CORRECTNESS_FINDINGS.md"
|
|
1055
|
-
];
|
|
1056
|
-
var STORE_DIFF_MAX = 4e5;
|
|
1057
|
-
var GUIDELINE_FILES = [
|
|
1058
|
-
"REVIEW.md",
|
|
1059
|
-
"CLAUDE.md",
|
|
1060
|
-
"AGENTS.md"
|
|
1061
|
-
];
|
|
1062
|
-
function unquote(s) {
|
|
1063
|
-
const t = String(s || "").trim();
|
|
1064
|
-
if (t.startsWith("\"") && t.endsWith("\"") || t.startsWith("'") && t.endsWith("'")) return t.slice(1, -1);
|
|
1065
|
-
return t;
|
|
1066
|
-
}
|
|
1067
|
-
/** Minimal YAML: `key: value` and `key:\n - item`. */
|
|
1068
|
-
function parseSimpleYaml(text) {
|
|
1069
|
-
const result = {};
|
|
1070
|
-
let key = null;
|
|
1071
|
-
for (const raw of String(text || "").split(/\r?\n/)) {
|
|
1072
|
-
if (!raw.trim() || raw.trim().startsWith("#")) continue;
|
|
1073
|
-
const list = raw.match(/^\s+-\s+(.*)$/);
|
|
1074
|
-
if (list && key) {
|
|
1075
|
-
if (!Array.isArray(result[key])) result[key] = [];
|
|
1076
|
-
result[key].push(unquote(list[1]));
|
|
1077
|
-
continue;
|
|
1078
|
-
}
|
|
1079
|
-
const kv = raw.match(/^([A-Za-z][\w]*)\s*:\s*(.*)$/);
|
|
1080
|
-
if (!kv) continue;
|
|
1081
|
-
key = kv[1];
|
|
1082
|
-
const v = kv[2].trim();
|
|
1083
|
-
result[key] = v === "" ? [] : unquote(v);
|
|
1084
|
-
}
|
|
1085
|
-
return result;
|
|
1086
|
-
}
|
|
1087
|
-
function globToRegExp(glob) {
|
|
1088
|
-
let g = String(glob || "").replace(/\\/g, "/");
|
|
1089
|
-
if (!g.includes("/")) g = `**/${g}`;
|
|
1090
|
-
let s = "";
|
|
1091
|
-
for (let i = 0; i < g.length; i++) {
|
|
1092
|
-
const c = g[i];
|
|
1093
|
-
if (c === "*" && g[i + 1] === "*") if (g[i + 2] === "/") {
|
|
1094
|
-
s += "(?:.*/)?";
|
|
1095
|
-
i += 2;
|
|
1096
|
-
} else {
|
|
1097
|
-
s += ".*";
|
|
1098
|
-
i += 1;
|
|
1099
|
-
}
|
|
1100
|
-
else if (c === "*") s += "[^/]*";
|
|
1101
|
-
else if (c === "?") s += "[^/]";
|
|
1102
|
-
else if ("+()[]{}|.^$".includes(c)) s += `\\${c}`;
|
|
1103
|
-
else s += c;
|
|
1104
|
-
}
|
|
1105
|
-
return new RegExp(`^${s}$`, "i");
|
|
1106
|
-
}
|
|
1107
|
-
function globMatch(filePath, pattern) {
|
|
1108
|
-
const norm = String(filePath || "").replace(/\\/g, "/");
|
|
1109
|
-
return globToRegExp(pattern).test(norm);
|
|
1110
|
-
}
|
|
1111
|
-
function isIgnored(filePath, patterns) {
|
|
1112
|
-
return (patterns || []).some((p) => globMatch(filePath, p));
|
|
1113
|
-
}
|
|
1114
|
-
function isTestPath(filePath) {
|
|
1115
|
-
const p = String(filePath || "").replace(/\\/g, "/");
|
|
1116
|
-
return /(^|\/)(__tests__|tests?|spec)s?(\/|$)/i.test(p) || /\.(test|spec)\.[^.]+$/i.test(p);
|
|
1117
|
-
}
|
|
1118
|
-
function readIfExists(path) {
|
|
1119
|
-
try {
|
|
1120
|
-
return existsSync(path) ? readFileSync(path, "utf8") : null;
|
|
1121
|
-
} catch {
|
|
1122
|
-
return null;
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
function loadReviewConfig(repo) {
|
|
1126
|
-
const jsonText = readIfExists(join(repo, ".diff-review.json"));
|
|
1127
|
-
const yamlText = readIfExists(join(repo, ".diff-review.yml")) || readIfExists(join(repo, ".diff-review.yaml"));
|
|
1128
|
-
let extra = {};
|
|
1129
|
-
if (jsonText) try {
|
|
1130
|
-
extra = JSON.parse(jsonText);
|
|
1131
|
-
} catch {
|
|
1132
|
-
extra = {};
|
|
1133
|
-
}
|
|
1134
|
-
else if (yamlText) extra = parseSimpleYaml(yamlText);
|
|
1135
|
-
const extraIgnore = Array.isArray(extra.ignore) ? extra.ignore.map(String) : [];
|
|
1136
|
-
const rulesFile = extra.rulesFile ? String(extra.rulesFile) : "REVIEW.md";
|
|
1137
|
-
const rules = readIfExists(join(repo, rulesFile)) || "";
|
|
1138
|
-
const model = extra.model && extra.model !== "default" ? String(extra.model) : null;
|
|
1139
|
-
return {
|
|
1140
|
-
ignore: [...DEFAULT_IGNORE, ...extraIgnore],
|
|
1141
|
-
model,
|
|
1142
|
-
rulesFile,
|
|
1143
|
-
rules: rules.trim()
|
|
1144
|
-
};
|
|
1145
|
-
}
|
|
1146
|
-
/** CLAUDE.md / REVIEW.md / AGENTS.md from repo root and immediate parent dirs of touched files. */
|
|
1147
|
-
function collectGuidelines(repo, filePaths, { rulesFile = "REVIEW.md", maxChars = 8e3, maxTotalChars = 12e3, maxDepth = 1, skipNames = [] } = {}) {
|
|
1148
|
-
const skip = new Set(skipNames);
|
|
1149
|
-
const names = [.../* @__PURE__ */ new Set([rulesFile, ...GUIDELINE_FILES])].filter((name) => !skip.has(name));
|
|
1150
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1151
|
-
const out = [];
|
|
1152
|
-
const addRel = (rel) => {
|
|
1153
|
-
const norm = String(rel || "").replace(/\\/g, "/").replace(/^\/+/, "");
|
|
1154
|
-
if (!norm || seen.has(norm)) return;
|
|
1155
|
-
const text = readIfExists(join(repo, ...norm.split("/")));
|
|
1156
|
-
if (!text || !text.trim()) return;
|
|
1157
|
-
seen.add(norm);
|
|
1158
|
-
out.push({
|
|
1159
|
-
path: norm,
|
|
1160
|
-
text: text.trim()
|
|
1161
|
-
});
|
|
1162
|
-
};
|
|
1163
|
-
for (const name of names) addRel(name);
|
|
1164
|
-
const depth = Math.max(1, Number(maxDepth) || 1);
|
|
1165
|
-
for (const filePath of filePaths || []) {
|
|
1166
|
-
const parts = String(filePath || "").replace(/\\/g, "/").split("/").filter(Boolean);
|
|
1167
|
-
const minLen = Math.max(1, parts.length - depth);
|
|
1168
|
-
for (let len = parts.length - 1; len >= minLen; len--) {
|
|
1169
|
-
const dir = parts.slice(0, len).join("/");
|
|
1170
|
-
for (const name of names) addRel(`${dir}/${name}`);
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
let total = 0;
|
|
1174
|
-
const capped = [];
|
|
1175
|
-
for (const item of out) {
|
|
1176
|
-
if (total >= maxTotalChars) break;
|
|
1177
|
-
const room = maxTotalChars - total;
|
|
1178
|
-
const slice = item.text.slice(0, Math.min(maxChars, room));
|
|
1179
|
-
if (!slice) continue;
|
|
1180
|
-
capped.push({
|
|
1181
|
-
path: item.path,
|
|
1182
|
-
text: slice
|
|
1183
|
-
});
|
|
1184
|
-
total += slice.length;
|
|
1185
|
-
}
|
|
1186
|
-
return capped;
|
|
1187
|
-
}
|
|
1188
|
-
function capDiff(diff, max = STORE_DIFF_MAX) {
|
|
1189
|
-
const text = String(diff || "");
|
|
1190
|
-
if (text.length <= max) return {
|
|
1191
|
-
diff: text,
|
|
1192
|
-
diffChars: text.length,
|
|
1193
|
-
truncated: false
|
|
1194
|
-
};
|
|
1195
|
-
return {
|
|
1196
|
-
diff: `${text.slice(0, max)}\n\n… [diff recortado: se guardan ${max} de ${text.length} caracteres]`,
|
|
1197
|
-
diffChars: text.length,
|
|
1198
|
-
truncated: true
|
|
1199
|
-
};
|
|
1200
|
-
}
|
|
1201
|
-
//#endregion
|
|
1202
|
-
//#region ../lib/git.mjs
|
|
1203
|
-
function assertGitRepo(repo) {
|
|
1204
|
-
const raw = String(repo || "").trim();
|
|
1205
|
-
if (!raw) throw new Error("Indicá la ruta absoluta de un repositorio git.");
|
|
1206
|
-
if (!/[\\/]/.test(raw) && !/^[A-Za-z]:/.test(raw)) throw new Error(`"${raw}" no es una ruta. Pegá la ruta absoluta del repo (ej. C:\\tienda-argenta\\tienda-argenta-2).`);
|
|
1207
|
-
const resolved = resolve(raw);
|
|
1208
|
-
if (!resolved || resolved === ".") throw new Error("Indicá la ruta absoluta de un repositorio git.");
|
|
1209
|
-
if (!existsSync(resolved)) throw new Error(`No existe la carpeta: ${resolved}`);
|
|
1210
|
-
if (!existsSync(join(resolved, ".git"))) throw new Error(`No es un repositorio git (falta .git): ${resolved}`);
|
|
1211
|
-
return resolved;
|
|
1212
|
-
}
|
|
1213
|
-
/**
|
|
1214
|
-
* Si `cwd` está dentro de un repo git, devuelve la raíz absoluta; si no, null.
|
|
1215
|
-
* Acepta subcarpetas (usa `git rev-parse --show-toplevel`).
|
|
1216
|
-
*/
|
|
1217
|
-
function tryResolveGitRoot(cwd) {
|
|
1218
|
-
const raw = String(cwd || "").trim();
|
|
1219
|
-
if (!raw) return null;
|
|
1220
|
-
const dir = resolve(raw);
|
|
1221
|
-
if (!existsSync(dir)) return null;
|
|
1222
|
-
try {
|
|
1223
|
-
const root = execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
1224
|
-
cwd: dir,
|
|
1225
|
-
encoding: "utf8",
|
|
1226
|
-
windowsHide: true,
|
|
1227
|
-
stdio: [
|
|
1228
|
-
"ignore",
|
|
1229
|
-
"pipe",
|
|
1230
|
-
"ignore"
|
|
1231
|
-
]
|
|
1232
|
-
}).trim();
|
|
1233
|
-
return root ? resolve(root) : null;
|
|
1234
|
-
} catch {
|
|
1235
|
-
return null;
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
function git(repo, args, { maxBuffer = 64 * 1024 * 1024 } = {}) {
|
|
1239
|
-
return execFileSync("git", args, {
|
|
1240
|
-
cwd: repo,
|
|
1241
|
-
encoding: "utf8",
|
|
1242
|
-
maxBuffer,
|
|
1243
|
-
windowsHide: true
|
|
1244
|
-
});
|
|
1245
|
-
}
|
|
1246
|
-
function resolveRev(repo, ref) {
|
|
1247
|
-
return git(repo, ["rev-parse", ref]).trim();
|
|
1248
|
-
}
|
|
1249
|
-
function branchTips(repo, branch, base) {
|
|
1250
|
-
return {
|
|
1251
|
-
branchSha: resolveRev(repo, branch),
|
|
1252
|
-
baseSha: resolveRev(repo, base)
|
|
1253
|
-
};
|
|
1254
|
-
}
|
|
1255
|
-
function commitLog(repo, mergeBase, branch, limit = 25) {
|
|
1256
|
-
try {
|
|
1257
|
-
return git(repo, [
|
|
1258
|
-
"log",
|
|
1259
|
-
"--oneline",
|
|
1260
|
-
"--no-merges",
|
|
1261
|
-
"-n",
|
|
1262
|
-
String(limit),
|
|
1263
|
-
`${mergeBase}..${branch}`
|
|
1264
|
-
]).trim().split("\n").filter(Boolean);
|
|
1265
|
-
} catch {
|
|
1266
|
-
return [];
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
var FILE_LINE_CACHE = /* @__PURE__ */ new Map();
|
|
1270
|
-
var FILE_LINE_CACHE_MAX = 48;
|
|
1271
|
-
function rememberFileLines(key, lines) {
|
|
1272
|
-
if (FILE_LINE_CACHE.has(key)) FILE_LINE_CACHE.delete(key);
|
|
1273
|
-
FILE_LINE_CACHE.set(key, lines);
|
|
1274
|
-
while (FILE_LINE_CACHE.size > FILE_LINE_CACHE_MAX) {
|
|
1275
|
-
const oldest = FILE_LINE_CACHE.keys().next().value;
|
|
1276
|
-
FILE_LINE_CACHE.delete(oldest);
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1279
|
-
function showFileLines(repo, rev, path) {
|
|
1280
|
-
const resolved = assertGitRepo(repo);
|
|
1281
|
-
const safeRev = String(rev || "").trim();
|
|
1282
|
-
const safePath = String(path || "").replace(/\\/g, "/").trim();
|
|
1283
|
-
if (!safeRev || !/^[A-Za-z0-9._/\-]+$/.test(safeRev)) throw new Error("Rev inválido.");
|
|
1284
|
-
if (!safePath || safePath.startsWith("/") || safePath.includes("..") || safePath.includes("\0")) throw new Error("Ruta inválida.");
|
|
1285
|
-
const key = `${resolved}\0${safeRev}\0${safePath}`;
|
|
1286
|
-
if (FILE_LINE_CACHE.has(key)) return FILE_LINE_CACHE.get(key);
|
|
1287
|
-
let text;
|
|
1288
|
-
try {
|
|
1289
|
-
text = git(resolved, ["show", `${safeRev}:${safePath}`]);
|
|
1290
|
-
} catch {
|
|
1291
|
-
rememberFileLines(key, null);
|
|
1292
|
-
return null;
|
|
1293
|
-
}
|
|
1294
|
-
if (text.includes("\0")) {
|
|
1295
|
-
rememberFileLines(key, null);
|
|
1296
|
-
return null;
|
|
1297
|
-
}
|
|
1298
|
-
const raw = text.replace(/\r\n/g, "\n");
|
|
1299
|
-
const lines = raw.endsWith("\n") ? raw.slice(0, -1).split("\n") : raw.split("\n");
|
|
1300
|
-
rememberFileLines(key, lines);
|
|
1301
|
-
return lines;
|
|
1302
|
-
}
|
|
1303
|
-
function listBranches(repo) {
|
|
1304
|
-
const names = git(repo, [
|
|
1305
|
-
"for-each-ref",
|
|
1306
|
-
"--sort=-committerdate",
|
|
1307
|
-
"--format=%(refname:short)",
|
|
1308
|
-
"refs/heads",
|
|
1309
|
-
"refs/remotes"
|
|
1310
|
-
]).split("\n").map((s) => s.trim()).filter(Boolean).map((name) => name.replace(/^origin\//, "")).filter((name) => name !== "HEAD" && !name.endsWith("/HEAD"));
|
|
1311
|
-
const unique = [...new Set(names)];
|
|
1312
|
-
const current = git(repo, [
|
|
1313
|
-
"rev-parse",
|
|
1314
|
-
"--abbrev-ref",
|
|
1315
|
-
"HEAD"
|
|
1316
|
-
]).trim();
|
|
1317
|
-
const bases = unique.filter((n) => n === "develop" || n === "main" || n === "master");
|
|
1318
|
-
return {
|
|
1319
|
-
branches: unique,
|
|
1320
|
-
current,
|
|
1321
|
-
defaultBase: bases.includes("develop") ? "develop" : bases.includes("main") ? "main" : bases[0] || "develop"
|
|
1322
|
-
};
|
|
1323
|
-
}
|
|
1324
|
-
/** URL del remote (origin, o el primero disponible). Vacío si no hay. */
|
|
1325
|
-
function getRemoteUrl(repo, preferred = "origin") {
|
|
1326
|
-
try {
|
|
1327
|
-
const url = git(repo, [
|
|
1328
|
-
"remote",
|
|
1329
|
-
"get-url",
|
|
1330
|
-
preferred
|
|
1331
|
-
]).trim();
|
|
1332
|
-
if (url) return url;
|
|
1333
|
-
} catch {}
|
|
1334
|
-
try {
|
|
1335
|
-
const remotes = git(repo, ["remote"]).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
1336
|
-
if (!remotes.length) return "";
|
|
1337
|
-
return git(repo, [
|
|
1338
|
-
"remote",
|
|
1339
|
-
"get-url",
|
|
1340
|
-
remotes[0]
|
|
1341
|
-
]).trim();
|
|
1342
|
-
} catch {
|
|
1343
|
-
return "";
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
//#endregion
|
|
1347
|
-
//#region ../lib/extract.mjs
|
|
1348
|
-
function extractBranchDiff({ repo, branch, base = "develop" }) {
|
|
1349
|
-
if (!branch) throw new Error("Falta el branch a revisar.");
|
|
1350
|
-
const config = loadReviewConfig(repo);
|
|
1351
|
-
const mergeBase = git(repo, [
|
|
1352
|
-
"merge-base",
|
|
1353
|
-
base,
|
|
1354
|
-
branch
|
|
1355
|
-
]).trim();
|
|
1356
|
-
const branchSha = git(repo, ["rev-parse", branch]).trim();
|
|
1357
|
-
const baseSha = git(repo, ["rev-parse", base]).trim();
|
|
1358
|
-
const nameStatus = git(repo, [
|
|
1359
|
-
"diff",
|
|
1360
|
-
mergeBase,
|
|
1361
|
-
branch,
|
|
1362
|
-
"--name-status"
|
|
1363
|
-
]).trim().split("\n").filter(Boolean).map((line) => {
|
|
1364
|
-
const [status, ...rest] = line.split(" ");
|
|
1365
|
-
const path = rest[rest.length - 1];
|
|
1366
|
-
const oldPath = rest.length > 1 ? rest[0] : null;
|
|
1367
|
-
let changeType = "modified";
|
|
1368
|
-
if (status.startsWith("A")) changeType = "added";
|
|
1369
|
-
else if (status.startsWith("D")) changeType = "deleted";
|
|
1370
|
-
else if (status.startsWith("R")) changeType = "renamed";
|
|
1371
|
-
return {
|
|
1372
|
-
path,
|
|
1373
|
-
oldPath,
|
|
1374
|
-
changeType
|
|
1375
|
-
};
|
|
1376
|
-
});
|
|
1377
|
-
const ignored = [];
|
|
1378
|
-
const included = [];
|
|
1379
|
-
for (const f of nameStatus) if (isIgnored(f.path, config.ignore)) ignored.push(f.path);
|
|
1380
|
-
else included.push(f);
|
|
1381
|
-
const files = included.map((f) => {
|
|
1382
|
-
let raw = "";
|
|
1383
|
-
try {
|
|
1384
|
-
raw = git(repo, [
|
|
1385
|
-
"diff",
|
|
1386
|
-
mergeBase,
|
|
1387
|
-
branch,
|
|
1388
|
-
"--",
|
|
1389
|
-
f.path
|
|
1390
|
-
]);
|
|
1391
|
-
const idx = raw.indexOf("\n@@");
|
|
1392
|
-
if (idx !== -1) raw = raw.slice(idx + 1);
|
|
1393
|
-
} catch {
|
|
1394
|
-
raw = "";
|
|
1395
|
-
}
|
|
1396
|
-
const capped = capDiff(raw);
|
|
1397
|
-
return {
|
|
1398
|
-
path: f.path,
|
|
1399
|
-
oldPath: f.oldPath || void 0,
|
|
1400
|
-
changeType: f.changeType,
|
|
1401
|
-
diff: capped.diff,
|
|
1402
|
-
diffChars: capped.diffChars,
|
|
1403
|
-
truncated: capped.truncated,
|
|
1404
|
-
modelChars: capped.diff.length,
|
|
1405
|
-
group: ""
|
|
1406
|
-
};
|
|
1407
|
-
});
|
|
1408
|
-
const shortstat = git(repo, [
|
|
1409
|
-
"diff",
|
|
1410
|
-
mergeBase,
|
|
1411
|
-
branch,
|
|
1412
|
-
"--shortstat"
|
|
1413
|
-
]).trim();
|
|
1414
|
-
const insMatch = shortstat.match(/(\d+) insertion/);
|
|
1415
|
-
const delMatch = shortstat.match(/(\d+) deletion/);
|
|
1416
|
-
return {
|
|
1417
|
-
version: 2,
|
|
1418
|
-
repo,
|
|
1419
|
-
branch,
|
|
1420
|
-
baseBranch: base,
|
|
1421
|
-
mergeBase,
|
|
1422
|
-
branchSha,
|
|
1423
|
-
baseSha,
|
|
1424
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1425
|
-
intent: "",
|
|
1426
|
-
stats: {
|
|
1427
|
-
filesChanged: files.length,
|
|
1428
|
-
filesIgnored: ignored.length,
|
|
1429
|
-
filesTruncated: files.filter((f) => f.truncated).length,
|
|
1430
|
-
insertions: insMatch ? Number(insMatch[1]) : 0,
|
|
1431
|
-
deletions: delMatch ? Number(delMatch[1]) : 0
|
|
1432
|
-
},
|
|
1433
|
-
context: {
|
|
1434
|
-
commits: commitLog(repo, mergeBase, branch),
|
|
1435
|
-
rules: config.rules,
|
|
1436
|
-
rulesFile: config.rulesFile,
|
|
1437
|
-
ignored: ignored.slice(0, 40),
|
|
1438
|
-
testsTouched: files.filter((f) => isTestPath(f.path)).map((f) => f.path),
|
|
1439
|
-
testsMissing: files.filter((f) => !isTestPath(f.path)).length,
|
|
1440
|
-
guidelines: collectGuidelines(repo, files.map((f) => f.path), {
|
|
1441
|
-
rulesFile: config.rulesFile,
|
|
1442
|
-
skipNames: config.rules.trim() ? [config.rulesFile] : [],
|
|
1443
|
-
maxDepth: 1,
|
|
1444
|
-
maxChars: 8e3,
|
|
1445
|
-
maxTotalChars: 12e3
|
|
1446
|
-
})
|
|
1447
|
-
},
|
|
1448
|
-
files,
|
|
1449
|
-
groups: [],
|
|
1450
|
-
blocks: [],
|
|
1451
|
-
findings: [],
|
|
1452
|
-
skipped: []
|
|
1453
|
-
};
|
|
1454
|
-
}
|
|
1455
|
-
//#endregion
|
|
1456
961
|
//#region ../lib/remote-repo.mjs
|
|
1457
962
|
var CACHE_ROOT = join(tmpdir(), "diff-review-clones");
|
|
1458
963
|
var MAX_DEEPEN_ROUNDS = 8;
|
|
@@ -1610,59 +1115,6 @@ function ensureRemoteWorktree(url, branch, base = "develop") {
|
|
|
1610
1115
|
return dir;
|
|
1611
1116
|
}
|
|
1612
1117
|
//#endregion
|
|
1613
|
-
//#region ../lib/validate.mjs
|
|
1614
|
-
var GROUP_KINDS = [
|
|
1615
|
-
"feat",
|
|
1616
|
-
"fix",
|
|
1617
|
-
"refactor",
|
|
1618
|
-
"perf",
|
|
1619
|
-
"test",
|
|
1620
|
-
"chore",
|
|
1621
|
-
"docs",
|
|
1622
|
-
"infra"
|
|
1623
|
-
];
|
|
1624
|
-
var BLOCK_OPS = [
|
|
1625
|
-
"add",
|
|
1626
|
-
"mod",
|
|
1627
|
-
"del",
|
|
1628
|
-
"rename"
|
|
1629
|
-
];
|
|
1630
|
-
var WHY_SOURCES = [
|
|
1631
|
-
"code",
|
|
1632
|
-
"commit",
|
|
1633
|
-
"pr",
|
|
1634
|
-
"issue",
|
|
1635
|
-
"inferred"
|
|
1636
|
-
];
|
|
1637
|
-
var FINDING_CLASSES = ["risk", "quality"];
|
|
1638
|
-
var FINDING_SEVERITIES = [
|
|
1639
|
-
"high",
|
|
1640
|
-
"med",
|
|
1641
|
-
"low",
|
|
1642
|
-
"nit"
|
|
1643
|
-
];
|
|
1644
|
-
var FINDING_KINDS = [
|
|
1645
|
-
"bug",
|
|
1646
|
-
"race",
|
|
1647
|
-
"auth",
|
|
1648
|
-
"security",
|
|
1649
|
-
"api-break",
|
|
1650
|
-
"missing-test",
|
|
1651
|
-
"perf",
|
|
1652
|
-
"maintainability",
|
|
1653
|
-
"docs",
|
|
1654
|
-
"other"
|
|
1655
|
-
];
|
|
1656
|
-
var SKIP_REASONS = [
|
|
1657
|
-
"generated",
|
|
1658
|
-
"lockfile",
|
|
1659
|
-
"format",
|
|
1660
|
-
"vendored",
|
|
1661
|
-
"binary",
|
|
1662
|
-
"trivial",
|
|
1663
|
-
"ignored"
|
|
1664
|
-
];
|
|
1665
|
-
//#endregion
|
|
1666
1118
|
//#region ../lib/review.mjs
|
|
1667
1119
|
var MAX = {
|
|
1668
1120
|
intent: 700,
|
|
@@ -1672,7 +1124,7 @@ var MAX = {
|
|
|
1672
1124
|
why: 240,
|
|
1673
1125
|
finding: 360
|
|
1674
1126
|
};
|
|
1675
|
-
function text
|
|
1127
|
+
function text(value, max) {
|
|
1676
1128
|
const raw = String(value ?? "").replace(/\s+/g, " ").trim();
|
|
1677
1129
|
return raw.length > max ? `${raw.slice(0, max - 1).trimEnd()}…` : raw;
|
|
1678
1130
|
}
|
|
@@ -1716,8 +1168,8 @@ function normalizeGroups(raw) {
|
|
|
1716
1168
|
groups.push({
|
|
1717
1169
|
id,
|
|
1718
1170
|
kind: oneOf(item.kind, GROUP_KINDS, "chore"),
|
|
1719
|
-
title: text
|
|
1720
|
-
intent: text
|
|
1171
|
+
title: text(item.title, MAX.groupTitle) || `${oneOf(item.kind, GROUP_KINDS, "chore")}: cambios`,
|
|
1172
|
+
intent: text(item.intent, MAX.groupIntent)
|
|
1721
1173
|
});
|
|
1722
1174
|
}
|
|
1723
1175
|
return groups;
|
|
@@ -1738,13 +1190,13 @@ function normalizeBlocks(raw, { groupIds, knownPaths }) {
|
|
|
1738
1190
|
id,
|
|
1739
1191
|
group,
|
|
1740
1192
|
file,
|
|
1741
|
-
lines: range ? formatLineRange(range) : text
|
|
1193
|
+
lines: range ? formatLineRange(range) : text(item.lines, 24),
|
|
1742
1194
|
side: range?.side || "new",
|
|
1743
1195
|
start: range?.start ?? 0,
|
|
1744
1196
|
end: range?.end ?? 0,
|
|
1745
1197
|
op: oneOf(item.op, BLOCK_OPS, "mod"),
|
|
1746
|
-
what: text
|
|
1747
|
-
why: text
|
|
1198
|
+
what: text(item.what, MAX.what),
|
|
1199
|
+
why: text(item.why, MAX.why),
|
|
1748
1200
|
source: oneOf(item.source, WHY_SOURCES, "inferred")
|
|
1749
1201
|
});
|
|
1750
1202
|
}
|
|
@@ -1764,7 +1216,7 @@ function normalizeFindings(raw, { blockIds, knownPaths }) {
|
|
|
1764
1216
|
const findings = [];
|
|
1765
1217
|
for (const item of raw || []) {
|
|
1766
1218
|
const file = String(item?.file || item?.path || "").trim();
|
|
1767
|
-
const what = text
|
|
1219
|
+
const what = text(item?.what || item?.title, MAX.finding);
|
|
1768
1220
|
if (!file || !what) continue;
|
|
1769
1221
|
if (knownPaths.size && !knownPaths.has(file)) continue;
|
|
1770
1222
|
const cls = oneOf(item.class, FINDING_CLASSES, "risk");
|
|
@@ -1781,7 +1233,7 @@ function normalizeFindings(raw, { blockIds, knownPaths }) {
|
|
|
1781
1233
|
line,
|
|
1782
1234
|
block: blockIds.has(block) ? block : "",
|
|
1783
1235
|
what,
|
|
1784
|
-
fix: text
|
|
1236
|
+
fix: text(item.fix || item.detail, MAX.finding)
|
|
1785
1237
|
};
|
|
1786
1238
|
const key = findingKey(finding);
|
|
1787
1239
|
if (seen.has(key)) continue;
|
|
@@ -1858,7 +1310,7 @@ function buildReview({ skeleton, payload, notes = [] }) {
|
|
|
1858
1310
|
return {
|
|
1859
1311
|
...skeleton,
|
|
1860
1312
|
version: 2,
|
|
1861
|
-
intent: text
|
|
1313
|
+
intent: text(payload?.intent, MAX.intent),
|
|
1862
1314
|
groups: liveGroups,
|
|
1863
1315
|
blocks,
|
|
1864
1316
|
findings,
|
|
@@ -2494,7 +1946,7 @@ function takePendingImport() {
|
|
|
2494
1946
|
}
|
|
2495
1947
|
}
|
|
2496
1948
|
/**
|
|
2497
|
-
* Busca un JSON de review en la raíz del repo local.
|
|
1949
|
+
* Busca un JSON de review en `.diff-review/` o (formato viejo) en la raíz del repo local.
|
|
2498
1950
|
* Prefiere `preferredName` si existe; si no, el más reciente que matchee el patrón.
|
|
2499
1951
|
*/
|
|
2500
1952
|
function detectReviewOutput(repo, preferredName) {
|
|
@@ -2508,8 +1960,8 @@ function detectReviewOutput(repo, preferredName) {
|
|
|
2508
1960
|
}
|
|
2509
1961
|
const preferred = String(preferredName || "").trim();
|
|
2510
1962
|
if (preferred && isOutputFilename(preferred)) {
|
|
2511
|
-
const preferredPath =
|
|
2512
|
-
if (
|
|
1963
|
+
const preferredPath = locateReviewFile(root, preferred);
|
|
1964
|
+
if (preferredPath) try {
|
|
2513
1965
|
return {
|
|
2514
1966
|
filename: preferred,
|
|
2515
1967
|
path: preferredPath,
|
|
@@ -2518,10 +1970,16 @@ function detectReviewOutput(repo, preferredName) {
|
|
|
2518
1970
|
} catch {}
|
|
2519
1971
|
}
|
|
2520
1972
|
let best = null;
|
|
2521
|
-
|
|
2522
|
-
|
|
1973
|
+
for (const dir of [reviewDir(root), root]) {
|
|
1974
|
+
let names;
|
|
1975
|
+
try {
|
|
1976
|
+
names = readdirSync(dir);
|
|
1977
|
+
} catch {
|
|
1978
|
+
continue;
|
|
1979
|
+
}
|
|
1980
|
+
for (const name of names) {
|
|
2523
1981
|
if (!isOutputFilename(name)) continue;
|
|
2524
|
-
const full = join(
|
|
1982
|
+
const full = join(dir, name);
|
|
2525
1983
|
try {
|
|
2526
1984
|
const st = statSync(full);
|
|
2527
1985
|
if (!st.isFile()) continue;
|
|
@@ -2532,12 +1990,10 @@ function detectReviewOutput(repo, preferredName) {
|
|
|
2532
1990
|
};
|
|
2533
1991
|
} catch {}
|
|
2534
1992
|
}
|
|
2535
|
-
} catch {
|
|
2536
|
-
return null;
|
|
2537
1993
|
}
|
|
2538
1994
|
return best;
|
|
2539
1995
|
}
|
|
2540
|
-
/** Lee y parsea un JSON de review
|
|
1996
|
+
/** Lee y parsea un JSON de review de `.diff-review/` o la raíz del repo (solo nombres válidos). */
|
|
2541
1997
|
function readReviewOutput(repo, filename) {
|
|
2542
1998
|
const raw = String(repo || "").trim();
|
|
2543
1999
|
const name = basename(String(filename || "").trim());
|
|
@@ -2548,8 +2004,8 @@ function readReviewOutput(repo, filename) {
|
|
|
2548
2004
|
} catch {
|
|
2549
2005
|
return null;
|
|
2550
2006
|
}
|
|
2551
|
-
const filePath =
|
|
2552
|
-
if (!
|
|
2007
|
+
const filePath = locateReviewFile(root, name);
|
|
2008
|
+
if (!filePath) return null;
|
|
2553
2009
|
try {
|
|
2554
2010
|
return {
|
|
2555
2011
|
filename: name,
|
|
@@ -2559,7 +2015,21 @@ function readReviewOutput(repo, filename) {
|
|
|
2559
2015
|
return null;
|
|
2560
2016
|
}
|
|
2561
2017
|
}
|
|
2562
|
-
|
|
2018
|
+
/** Escribe (o pisa) el JSON de review en `.diff-review/` para poder abrirlo luego solo con el puntero. */
|
|
2019
|
+
function writeReviewOutput(repo, filename, payload) {
|
|
2020
|
+
const raw = String(repo || "").trim();
|
|
2021
|
+
const name = basename(String(filename || "").trim());
|
|
2022
|
+
if (!raw || !name || !isOutputFilename(name) || looksLikeGitUrl(raw)) throw new Error("Repo local y nombre de salida válidos requeridos para guardar el JSON.");
|
|
2023
|
+
const root = assertGitRepo(raw);
|
|
2024
|
+
ensureReviewDir(root);
|
|
2025
|
+
const path = companionPaths(root, name).json;
|
|
2026
|
+
writeFileSync(path, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
2027
|
+
return {
|
|
2028
|
+
filename: name,
|
|
2029
|
+
payload
|
|
2030
|
+
};
|
|
2031
|
+
}
|
|
2032
|
+
async function pickLocalFolder() {
|
|
2563
2033
|
return pickFolder();
|
|
2564
2034
|
}
|
|
2565
2035
|
/** Nombre de salida con tips cortos del branch y la base (repo local). */
|
|
@@ -2572,8 +2042,12 @@ function resolveOutputName(repo, branch, base) {
|
|
|
2572
2042
|
fingerprint: null
|
|
2573
2043
|
};
|
|
2574
2044
|
try {
|
|
2575
|
-
const
|
|
2045
|
+
const root = assertGitRepo(raw);
|
|
2046
|
+
const tips = branchTips(root, b, baseRef);
|
|
2576
2047
|
const fp = fingerprint(tips.branchSha, tips.baseSha);
|
|
2048
|
+
try {
|
|
2049
|
+
ensureReviewDir(root);
|
|
2050
|
+
} catch {}
|
|
2577
2051
|
return {
|
|
2578
2052
|
filename: makeOutputFilename({
|
|
2579
2053
|
branchSha: tips.branchSha,
|
|
@@ -2590,12 +2064,13 @@ function resolveOutputName(repo, branch, base) {
|
|
|
2590
2064
|
};
|
|
2591
2065
|
}
|
|
2592
2066
|
}
|
|
2593
|
-
/** Diálogo nativo de JSON; abre en la raíz
|
|
2067
|
+
/** Diálogo nativo de JSON; abre en `.diff-review/` (o la raíz) si el repo es local. */
|
|
2594
2068
|
async function pickReviewJsonFile(defaultDir) {
|
|
2595
2069
|
const dir = String(defaultDir || "").trim();
|
|
2596
2070
|
let start = dir;
|
|
2597
2071
|
if (dir && !looksLikeGitUrl(dir)) try {
|
|
2598
|
-
|
|
2072
|
+
const root = assertGitRepo(dir);
|
|
2073
|
+
start = existsSync(reviewDir(root)) ? reviewDir(root) : root;
|
|
2599
2074
|
} catch {
|
|
2600
2075
|
start = dir;
|
|
2601
2076
|
}
|
|
@@ -2631,10 +2106,23 @@ function loadFileLines(input) {
|
|
|
2631
2106
|
}
|
|
2632
2107
|
}
|
|
2633
2108
|
function hydrateReviewPayload(input) {
|
|
2634
|
-
const
|
|
2635
|
-
|
|
2109
|
+
const repo = resolveWorktree(resolveSource(input.source, input.repo), input.repo, input.branch, input.base || "develop");
|
|
2110
|
+
const base = input.base || "develop";
|
|
2111
|
+
let filename = String(input.outputFilename || "").trim();
|
|
2112
|
+
if (!filename || !isOutputFilename(filename)) try {
|
|
2113
|
+
const tips = branchTips(repo, input.branch, base);
|
|
2114
|
+
filename = makeOutputFilename({
|
|
2115
|
+
branchSha: tips.branchSha,
|
|
2116
|
+
baseSha: tips.baseSha
|
|
2117
|
+
});
|
|
2118
|
+
} catch {
|
|
2119
|
+
filename = "";
|
|
2120
|
+
}
|
|
2121
|
+
const skeleton = loadBranchDiff({
|
|
2122
|
+
repo,
|
|
2636
2123
|
branch: input.branch,
|
|
2637
|
-
base
|
|
2124
|
+
base,
|
|
2125
|
+
...filename ? { filename } : {}
|
|
2638
2126
|
});
|
|
2639
2127
|
const extraNotes = Array.isArray(input.payload?.notes) ? input.payload.notes.filter((n) => typeof n === "string") : [];
|
|
2640
2128
|
const built = buildReview({
|
|
@@ -2653,128 +2141,6 @@ function hydrateReviewPayload(input) {
|
|
|
2653
2141
|
};
|
|
2654
2142
|
return JSON.parse(JSON.stringify(doc));
|
|
2655
2143
|
}
|
|
2656
|
-
//#endregion
|
|
2657
|
-
//#region src/lib/report/git.remote.ts
|
|
2658
|
-
var git_remote_exports = /* @__PURE__ */ __exportAll({
|
|
2659
|
-
detectOutput: () => detectOutput,
|
|
2660
|
-
fileLines: () => fileLines,
|
|
2661
|
-
hydrateFromGit: () => hydrateFromGit,
|
|
2662
|
-
launchContext: () => launchContext,
|
|
2663
|
-
loadDetectedOutput: () => loadDetectedOutput,
|
|
2664
|
-
loadRepo: () => loadRepo,
|
|
2665
|
-
outputName: () => outputName,
|
|
2666
|
-
pendingImport: () => pendingImport,
|
|
2667
|
-
pickLocalFolder: () => pickLocalFolder,
|
|
2668
|
-
pickReviewFile: () => pickReviewFile
|
|
2669
|
-
});
|
|
2670
|
-
function fail(err) {
|
|
2671
|
-
const message = describeGitError(err) || "No se pudo hablar con git.";
|
|
2672
|
-
console.error("[diff-review git]", message, err);
|
|
2673
|
-
error(400, message);
|
|
2674
|
-
}
|
|
2675
|
-
var loadRepo = /* @__PURE__ */ query("unchecked", async (input) => {
|
|
2676
|
-
try {
|
|
2677
|
-
return loadRepoInfo(input?.source, String(input?.repo || "").trim());
|
|
2678
|
-
} catch (err) {
|
|
2679
|
-
fail(err);
|
|
2680
|
-
}
|
|
2681
|
-
});
|
|
2682
|
-
/** Repo del CWD de lanzamiento, o null si no hay git. */
|
|
2683
|
-
var launchContext = /* @__PURE__ */ query(async () => {
|
|
2684
|
-
try {
|
|
2685
|
-
return loadLaunchRepoInfo();
|
|
2686
|
-
} catch (err) {
|
|
2687
|
-
console.error("[diff-review git] launch context", describeGitError(err), err);
|
|
2688
|
-
return null;
|
|
2689
|
-
}
|
|
2690
|
-
});
|
|
2691
|
-
/** JSON pendiente del CLI (DIFF_REVIEW_IMPORT_FILE); se consume al leer. */
|
|
2692
|
-
var pendingImport = /* @__PURE__ */ query(async () => {
|
|
2693
|
-
try {
|
|
2694
|
-
return takePendingImport();
|
|
2695
|
-
} catch (err) {
|
|
2696
|
-
console.error("[diff-review git] pending import", describeGitError(err), err);
|
|
2697
|
-
return null;
|
|
2698
|
-
}
|
|
2699
|
-
});
|
|
2700
|
-
/** ¿Hay un JSON de review en la raíz del repo local? */
|
|
2701
|
-
var detectOutput = /* @__PURE__ */ query("unchecked", async (input) => {
|
|
2702
|
-
try {
|
|
2703
|
-
return detectReviewOutput(String(input?.repo || "").trim(), input?.preferredName);
|
|
2704
|
-
} catch (err) {
|
|
2705
|
-
console.error("[diff-review git] detect output", describeGitError(err), err);
|
|
2706
|
-
return null;
|
|
2707
|
-
}
|
|
2708
|
-
});
|
|
2709
|
-
/** Nombre sugerido con tips cortos (branch/base). */
|
|
2710
|
-
var outputName = /* @__PURE__ */ query("unchecked", async (input) => {
|
|
2711
|
-
try {
|
|
2712
|
-
return resolveOutputName(String(input?.repo || "").trim(), String(input?.branch || "").trim(), String(input?.base || "develop").trim());
|
|
2713
|
-
} catch (err) {
|
|
2714
|
-
console.error("[diff-review git] output name", describeGitError(err), err);
|
|
2715
|
-
return {
|
|
2716
|
-
filename: makeOutputFilename({}),
|
|
2717
|
-
fingerprint: null
|
|
2718
|
-
};
|
|
2719
|
-
}
|
|
2720
|
-
});
|
|
2721
|
-
/** Lee un JSON detectado en el repo (sin diálogo). */
|
|
2722
|
-
var loadDetectedOutput = /* @__PURE__ */ command("unchecked", async (input) => {
|
|
2723
|
-
try {
|
|
2724
|
-
return readReviewOutput(String(input?.repo || "").trim(), String(input?.filename || "").trim());
|
|
2725
|
-
} catch (err) {
|
|
2726
|
-
fail(err);
|
|
2727
|
-
}
|
|
2728
|
-
});
|
|
2729
|
-
var pickLocalFolder = /* @__PURE__ */ command("unchecked", async (_input) => {
|
|
2730
|
-
try {
|
|
2731
|
-
return await pickLocalFolder$1();
|
|
2732
|
-
} catch (err) {
|
|
2733
|
-
fail(err);
|
|
2734
|
-
}
|
|
2735
|
-
});
|
|
2736
|
-
/** Diálogo nativo de JSON; `directory` = raíz del repo local. */
|
|
2737
|
-
var pickReviewFile = /* @__PURE__ */ command("unchecked", async (input) => {
|
|
2738
|
-
try {
|
|
2739
|
-
const picked = await pickReviewJsonFile(input?.directory);
|
|
2740
|
-
if (!picked) return null;
|
|
2741
|
-
return {
|
|
2742
|
-
filename: basename(picked.path),
|
|
2743
|
-
payload: decodePayload(picked.text)
|
|
2744
|
-
};
|
|
2745
|
-
} catch (err) {
|
|
2746
|
-
fail(err);
|
|
2747
|
-
}
|
|
2748
|
-
});
|
|
2749
|
-
/** Líneas del archivo en el branch revisado, para expandir el contexto oculto de un diff. */
|
|
2750
|
-
var fileLines = /* @__PURE__ */ query("unchecked", async (input) => loadFileLines({
|
|
2751
|
-
repo: String(input?.repo || "").trim(),
|
|
2752
|
-
branch: String(input?.branch || "").trim(),
|
|
2753
|
-
base: String(input?.base || "develop").trim(),
|
|
2754
|
-
source: input?.source,
|
|
2755
|
-
path: String(input?.path || "").trim()
|
|
2756
|
-
}));
|
|
2757
|
-
var hydrateFromGit = /* @__PURE__ */ command("unchecked", async (input) => {
|
|
2758
|
-
try {
|
|
2759
|
-
return hydrateReviewPayload({
|
|
2760
|
-
repo: String(input?.repo || "").trim(),
|
|
2761
|
-
branch: String(input?.branch || "").trim(),
|
|
2762
|
-
base: String(input?.base || "develop").trim(),
|
|
2763
|
-
source: input?.source,
|
|
2764
|
-
payload: input?.payload
|
|
2765
|
-
});
|
|
2766
|
-
} catch (err) {
|
|
2767
|
-
fail(err);
|
|
2768
|
-
}
|
|
2769
|
-
});
|
|
2770
|
-
init_remote_functions(git_remote_exports, "src/lib/report/git.remote.ts", "1g4ayag");
|
|
2771
|
-
for (const [name, fn] of Object.entries(git_remote_exports)) {
|
|
2772
|
-
fn.__.id = "1g4ayag/" + name;
|
|
2773
|
-
fn.__.name = name;
|
|
2774
|
-
}
|
|
2775
|
-
|
|
2776
|
-
//#region \0sveltekit-remote:1g4ayag
|
|
2777
|
-
var _sveltekit_remote_1g4ayag_default = git_remote_exports;
|
|
2778
2144
|
|
|
2779
|
-
export {
|
|
2780
|
-
//# sourceMappingURL=
|
|
2145
|
+
export { loadLaunchRepoInfo as a, detectReviewOutput as b, command as c, describeGitError as d, readReviewOutput as e, pickReviewJsonFile as f, loadFileLines as g, hydrateReviewPayload as h, loadRepoInfo as l, pickLocalFolder as p, query as q, resolveOutputName as r, takePendingImport as t, writeReviewOutput as w };
|
|
2146
|
+
//# sourceMappingURL=git-review-CpO_G8z8.js.map
|