@jpkme/diff-review 0.1.10 → 0.1.13

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.
Files changed (159) hide show
  1. package/README.md +38 -15
  2. package/bin/diff-review.mjs +151 -34
  3. package/lib/acp-policy.mjs +223 -0
  4. package/lib/acp-policy.test.mjs +136 -0
  5. package/lib/acp-shell.mjs +409 -0
  6. package/lib/acp-shell.test.mjs +158 -0
  7. package/lib/acp.mjs +368 -0
  8. package/lib/acp.test.mjs +58 -0
  9. package/lib/agent-login.mjs +105 -0
  10. package/lib/agents.mjs +87 -14
  11. package/lib/agents.test.mjs +4 -6
  12. package/lib/check-report.mjs +30 -11
  13. package/lib/check-report.test.mjs +1 -0
  14. package/lib/extract-cache.test.mjs +162 -0
  15. package/lib/extract.mjs +197 -1
  16. package/lib/model-label.mjs +263 -0
  17. package/lib/model-label.test.mjs +77 -0
  18. package/lib/models-cache.mjs +75 -0
  19. package/lib/models-cache.test.mjs +50 -0
  20. package/lib/output-name.mjs +8 -0
  21. package/lib/prompt.mjs +168 -22
  22. package/lib/review-dir.mjs +108 -0
  23. package/lib/review-meta.mjs +5 -3
  24. package/lib/review-meta.test.mjs +11 -0
  25. package/lib/review-prep.mjs +63 -0
  26. package/lib/validate.mjs +2 -2
  27. package/lib/wait-output.mjs +46 -23
  28. package/lib/wait-output.test.mjs +24 -1
  29. package/package.json +3 -1
  30. package/web/build/client/_app/immutable/assets/{0.CLQiq3Ed.css → 0.BKRG1f36.css} +1 -1
  31. package/web/build/client/_app/immutable/assets/0.BKRG1f36.css.br +0 -0
  32. package/web/build/client/_app/immutable/assets/{0.CLQiq3Ed.css.gz → 0.BKRG1f36.css.gz} +0 -0
  33. package/web/build/client/_app/immutable/assets/2.tqrMRTM0.css +1 -0
  34. package/web/build/client/_app/immutable/assets/2.tqrMRTM0.css.br +0 -0
  35. package/web/build/client/_app/immutable/assets/2.tqrMRTM0.css.gz +0 -0
  36. package/web/build/client/_app/immutable/assets/3.BT54aVKb.css +1 -0
  37. package/web/build/client/_app/immutable/assets/3.BT54aVKb.css.br +0 -0
  38. package/web/build/client/_app/immutable/assets/3.BT54aVKb.css.gz +0 -0
  39. package/web/build/client/_app/immutable/assets/storage.DqYH0w6R.css +1 -0
  40. package/web/build/client/_app/immutable/assets/storage.DqYH0w6R.css.br +0 -0
  41. package/web/build/client/_app/immutable/assets/storage.DqYH0w6R.css.gz +0 -0
  42. package/web/build/client/_app/immutable/chunks/BXUWMim4.js +213 -0
  43. package/web/build/client/_app/immutable/chunks/BXUWMim4.js.br +0 -0
  44. package/web/build/client/_app/immutable/chunks/BXUWMim4.js.gz +0 -0
  45. package/web/build/client/_app/immutable/chunks/Bu1mm6Oz.js +1 -0
  46. package/web/build/client/_app/immutable/chunks/Bu1mm6Oz.js.br +0 -0
  47. package/web/build/client/_app/immutable/chunks/Bu1mm6Oz.js.gz +0 -0
  48. package/web/build/client/_app/immutable/chunks/D8WOD_eS.js +3 -0
  49. package/web/build/client/_app/immutable/chunks/D8WOD_eS.js.br +0 -0
  50. package/web/build/client/_app/immutable/chunks/D8WOD_eS.js.gz +0 -0
  51. package/web/build/client/_app/immutable/chunks/{BBWAo_yW.js → DPfXzQnm.js} +2 -2
  52. package/web/build/client/_app/immutable/chunks/DPfXzQnm.js.br +0 -0
  53. package/web/build/client/_app/immutable/chunks/DPfXzQnm.js.gz +0 -0
  54. package/web/build/client/_app/immutable/chunks/lUYXRDtZ.js +1 -0
  55. package/web/build/client/_app/immutable/chunks/lUYXRDtZ.js.br +0 -0
  56. package/web/build/client/_app/immutable/chunks/lUYXRDtZ.js.gz +0 -0
  57. package/web/build/client/_app/immutable/entry/app.CTgiuiU1.js +2 -0
  58. package/web/build/client/_app/immutable/entry/app.CTgiuiU1.js.br +0 -0
  59. package/web/build/client/_app/immutable/entry/app.CTgiuiU1.js.gz +0 -0
  60. package/web/build/client/_app/immutable/entry/start.CFSl0kht.js +1 -0
  61. package/web/build/client/_app/immutable/entry/start.CFSl0kht.js.br +0 -0
  62. package/web/build/client/_app/immutable/entry/start.CFSl0kht.js.gz +0 -0
  63. package/web/build/client/_app/immutable/nodes/0.Dj97JW46.js +1 -0
  64. package/web/build/client/_app/immutable/nodes/0.Dj97JW46.js.br +0 -0
  65. package/web/build/client/_app/immutable/nodes/0.Dj97JW46.js.gz +0 -0
  66. package/web/build/client/_app/immutable/nodes/1.0PQZpSbJ.js +1 -0
  67. package/web/build/client/_app/immutable/nodes/1.0PQZpSbJ.js.br +2 -0
  68. package/web/build/client/_app/immutable/nodes/1.0PQZpSbJ.js.gz +0 -0
  69. package/web/build/client/_app/immutable/nodes/2.DQaoQoy4.js +10 -0
  70. package/web/build/client/_app/immutable/nodes/2.DQaoQoy4.js.br +0 -0
  71. package/web/build/client/_app/immutable/nodes/2.DQaoQoy4.js.gz +0 -0
  72. package/web/build/client/_app/immutable/nodes/3.AciugOtL.js +171 -0
  73. package/web/build/client/_app/immutable/nodes/3.AciugOtL.js.br +0 -0
  74. package/web/build/client/_app/immutable/nodes/3.AciugOtL.js.gz +0 -0
  75. package/web/build/client/_app/version.json +1 -1
  76. package/web/build/client/_app/version.json.br +0 -0
  77. package/web/build/client/_app/version.json.gz +0 -0
  78. package/web/build/handler.js +3 -3
  79. package/web/build/index.js +3 -3
  80. package/web/build/server/chunks/0-DUStDOsr.js +11 -0
  81. package/web/build/server/chunks/{0-SseDy__O.js.map → 0-DUStDOsr.js.map} +1 -1
  82. package/web/build/server/chunks/1-C2qlInYk.js +7 -0
  83. package/web/build/server/chunks/1-C2qlInYk.js.map +1 -0
  84. package/web/build/server/chunks/2-DyE177MK.js +7 -0
  85. package/web/build/server/chunks/2-DyE177MK.js.map +1 -0
  86. package/web/build/server/chunks/3-SO3atuWO.js +7 -0
  87. package/web/build/server/chunks/3-SO3atuWO.js.map +1 -0
  88. package/web/build/server/chunks/_server.ts-CRNhzL2a.js +62 -0
  89. package/web/build/server/chunks/_server.ts-CRNhzL2a.js.map +1 -0
  90. package/web/build/server/chunks/agent-sessions-kcUaLhK5.js +2819 -0
  91. package/web/build/server/chunks/agent-sessions-kcUaLhK5.js.map +1 -0
  92. package/web/build/server/chunks/{remote-1g4ayag-B67QMR_2.js → git-review-CXKcjYgZ.js} +70 -704
  93. package/web/build/server/chunks/git-review-CXKcjYgZ.js.map +1 -0
  94. package/web/build/server/chunks/{handler-B5P65tLk.js → handler-_E0NucKN.js} +3 -3
  95. package/web/build/server/chunks/{handler-B5P65tLk.js.map → handler-_E0NucKN.js.map} +1 -1
  96. package/web/build/server/chunks/hooks.server-D7yPODoc.js +42 -0
  97. package/web/build/server/chunks/hooks.server-D7yPODoc.js.map +1 -0
  98. package/web/build/server/chunks/{index.js-CL95e0Gl.js → index.js-lMtDM8ZQ.js} +3 -5
  99. package/web/build/server/chunks/index.js-lMtDM8ZQ.js.map +1 -0
  100. package/web/build/server/chunks/manifest.js-BIm6jot5.js +58 -0
  101. package/web/build/server/chunks/manifest.js-BIm6jot5.js.map +1 -0
  102. package/web/build/server/chunks/remote-1g4ayag-DmqxwUIz.js +144 -0
  103. package/web/build/server/chunks/remote-1g4ayag-DmqxwUIz.js.map +1 -0
  104. package/web/build/server/chunks/remote-wh6t6j-DZk0W9Pp.js +162 -0
  105. package/web/build/server/chunks/remote-wh6t6j-DZk0W9Pp.js.map +1 -0
  106. package/web/build/server/chunks/validate-BsTKyhsq.js +1073 -0
  107. package/web/build/server/chunks/validate-BsTKyhsq.js.map +1 -0
  108. package/web/build/client/_app/immutable/assets/0.CLQiq3Ed.css.br +0 -0
  109. package/web/build/client/_app/immutable/assets/2.CNFDEnjx.css +0 -1
  110. package/web/build/client/_app/immutable/assets/2.CNFDEnjx.css.br +0 -0
  111. package/web/build/client/_app/immutable/assets/2.CNFDEnjx.css.gz +0 -0
  112. package/web/build/client/_app/immutable/assets/3.PHTPLlZ7.css +0 -1
  113. package/web/build/client/_app/immutable/assets/3.PHTPLlZ7.css.br +0 -0
  114. package/web/build/client/_app/immutable/assets/3.PHTPLlZ7.css.gz +0 -0
  115. package/web/build/client/_app/immutable/assets/ThemeToggle.DcT79vPK.css +0 -1
  116. package/web/build/client/_app/immutable/assets/ThemeToggle.DcT79vPK.css.br +0 -0
  117. package/web/build/client/_app/immutable/assets/ThemeToggle.DcT79vPK.css.gz +0 -0
  118. package/web/build/client/_app/immutable/chunks/BBWAo_yW.js.br +0 -0
  119. package/web/build/client/_app/immutable/chunks/BBWAo_yW.js.gz +0 -0
  120. package/web/build/client/_app/immutable/chunks/Dur-vdUf.js +0 -3
  121. package/web/build/client/_app/immutable/chunks/Dur-vdUf.js.br +0 -0
  122. package/web/build/client/_app/immutable/chunks/Dur-vdUf.js.gz +0 -0
  123. package/web/build/client/_app/immutable/chunks/QdZF3nkf.js +0 -176
  124. package/web/build/client/_app/immutable/chunks/QdZF3nkf.js.br +0 -0
  125. package/web/build/client/_app/immutable/chunks/QdZF3nkf.js.gz +0 -0
  126. package/web/build/client/_app/immutable/chunks/tYeNsyIK.js +0 -1
  127. package/web/build/client/_app/immutable/chunks/tYeNsyIK.js.br +0 -0
  128. package/web/build/client/_app/immutable/chunks/tYeNsyIK.js.gz +0 -0
  129. package/web/build/client/_app/immutable/entry/app.Dce6uJU3.js +0 -2
  130. package/web/build/client/_app/immutable/entry/app.Dce6uJU3.js.br +0 -0
  131. package/web/build/client/_app/immutable/entry/app.Dce6uJU3.js.gz +0 -0
  132. package/web/build/client/_app/immutable/entry/start.DnBSC38l.js +0 -1
  133. package/web/build/client/_app/immutable/entry/start.DnBSC38l.js.br +0 -0
  134. package/web/build/client/_app/immutable/entry/start.DnBSC38l.js.gz +0 -0
  135. package/web/build/client/_app/immutable/nodes/0.s5xmjXoS.js +0 -1
  136. package/web/build/client/_app/immutable/nodes/0.s5xmjXoS.js.br +0 -1
  137. package/web/build/client/_app/immutable/nodes/0.s5xmjXoS.js.gz +0 -0
  138. package/web/build/client/_app/immutable/nodes/1.5Iv4ssDT.js +0 -1
  139. package/web/build/client/_app/immutable/nodes/1.5Iv4ssDT.js.br +0 -0
  140. package/web/build/client/_app/immutable/nodes/1.5Iv4ssDT.js.gz +0 -0
  141. package/web/build/client/_app/immutable/nodes/2.BomzYVBF.js +0 -66
  142. package/web/build/client/_app/immutable/nodes/2.BomzYVBF.js.br +0 -0
  143. package/web/build/client/_app/immutable/nodes/2.BomzYVBF.js.gz +0 -0
  144. package/web/build/client/_app/immutable/nodes/3.CeUs4lBX.js +0 -170
  145. package/web/build/client/_app/immutable/nodes/3.CeUs4lBX.js.br +0 -0
  146. package/web/build/client/_app/immutable/nodes/3.CeUs4lBX.js.gz +0 -0
  147. package/web/build/server/chunks/0-SseDy__O.js +0 -11
  148. package/web/build/server/chunks/1-2OO2bb7H.js +0 -7
  149. package/web/build/server/chunks/1-2OO2bb7H.js.map +0 -1
  150. package/web/build/server/chunks/2-BJvuqKN6.js +0 -7
  151. package/web/build/server/chunks/2-BJvuqKN6.js.map +0 -1
  152. package/web/build/server/chunks/3-ZIl1rHef.js +0 -7
  153. package/web/build/server/chunks/3-ZIl1rHef.js.map +0 -1
  154. package/web/build/server/chunks/hooks.server-DERFlOe1.js +0 -14
  155. package/web/build/server/chunks/hooks.server-DERFlOe1.js.map +0 -1
  156. package/web/build/server/chunks/index.js-CL95e0Gl.js.map +0 -1
  157. package/web/build/server/chunks/manifest.js-uD_a0079.js +0 -50
  158. package/web/build/server/chunks/manifest.js-uD_a0079.js.map +0 -1
  159. 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, _ as __exportAll, i as init_remote_functions } from './index.js-CL95e0Gl.js';
2
- import { join, basename, resolve } from 'node:path';
3
- import { existsSync, readFileSync, statSync, readdirSync, mkdtempSync, rmSync, writeFileSync, mkdirSync } from 'node:fs';
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-lMtDM8ZQ.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$1(value, max) {
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$1(item.title, MAX.groupTitle) || `${oneOf(item.kind, GROUP_KINDS, "chore")}: cambios`,
1720
- intent: text$1(item.intent, MAX.groupIntent)
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$1(item.lines, 24),
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$1(item.what, MAX.what),
1747
- why: text$1(item.why, MAX.why),
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$1(item?.what || item?.title, MAX.finding);
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$1(item.fix || item.detail, MAX.finding)
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$1(payload?.intent, MAX.intent),
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 = join(root, preferred);
2512
- if (existsSync(preferredPath)) try {
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
- try {
2522
- for (const name of readdirSync(root)) {
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(root, name);
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 en la raíz del repo (solo nombres válidos). */
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 = join(root, name);
2552
- if (!existsSync(filePath)) return null;
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
- async function pickLocalFolder$1() {
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 tips = branchTips(assertGitRepo(raw), b, baseRef);
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 del repo si es local. */
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
- start = assertGitRepo(dir);
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 skeleton = extractBranchDiff({
2635
- repo: resolveWorktree(resolveSource(input.source, input.repo), input.repo, input.branch, input.base || "develop"),
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: input.base || "develop"
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 { _sveltekit_remote_1g4ayag_default as default };
2780
- //# sourceMappingURL=remote-1g4ayag-B67QMR_2.js.map
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-CXKcjYgZ.js.map