pull-request-split-advisor 3.1.2 → 3.2.1
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 +7 -0
- package/dist/ai/config-wizard.js +2 -2
- package/dist/cli.js +82 -1
- package/dist/config/config.js +1 -1
- package/dist/config/default-config.js +1 -2
- package/dist/output/report.js +135 -0
- package/package.json +2 -2
- package/scripts/postinstall.cjs +39 -13
package/README.md
CHANGED
|
@@ -27,6 +27,8 @@ La capa de IA ofrece tres puntos de enriquecimiento: **mejora de mensajes de com
|
|
|
27
27
|
|
|
28
28
|
La v3.0.0 también mejora el **apply**: rama de respaldo con snapshot completo del working tree, re-prompt en nombres duplicados en lugar de rollback, y nomenclatura de ramas con mayúsculas en la descripción (p. ej. `feature/TEAM-123-PRUEBA-IA`). Si la IA no está configurada o falla en cualquier punto, la herramienta continúa en modo heurístico sin interrupciones.
|
|
29
29
|
|
|
30
|
+
A partir de la **v3.2.0**, se añade el subcomando **`score`** que calcula y muestra el score del estado actual de la rama sin dividir en PRs, exportando también un reporte HTML independiente (`pr-split-score.html`) con desglose de métricas, valores brutos y detalle de archivos.
|
|
31
|
+
|
|
30
32
|
A partir de la **v3.1.0**, el proyecto incluye una **suite completa de tests** (Vitest, 199 tests, 12 suites) que cubre todos los módulos core, AI, git y configuración con mocks de dependencias externas (git, fs). A partir de la **v3.1.1**, el proveedor `copilot` solo puede activarse dentro de **Visual Studio Code** — fuera del IDE se emite un aviso y se desactiva automáticamente sin interrumpir el análisis.
|
|
31
33
|
|
|
32
34
|
---
|
|
@@ -38,6 +40,7 @@ A partir de la **v3.1.0**, el proyecto incluye una **suite completa de tests** (
|
|
|
38
40
|
- Cálculo de score de calidad por rama (0–5) con factores ponderados
|
|
39
41
|
- Generación de nombres de rama y mensajes de commit convencionales
|
|
40
42
|
- Reporte HTML interactivo exportado automáticamente (8 secciones: hero ejecutivo, resumen de cambios, TOC con barras de score, detalle de archivos, dependencias, bloques, plan de commits con línea de tiempo y comandos git listos para ejecutar) — ver [formato completo](docs/FEATURES.md#reporte-html-interactivo)
|
|
43
|
+
- **`score`**: subcomando (v3.2.0+) que calcula el score del estado actual sin dividir y genera `pr-split-score.html` con desglose completo de métricas
|
|
41
44
|
- Exportación del plan en formato JSON para integraciones externas
|
|
42
45
|
- Historial de scores con seguimiento entre análisis
|
|
43
46
|
- Validaciones de nomenclatura de ramas y estado del working tree
|
|
@@ -74,6 +77,10 @@ pr-split-advisor --apply
|
|
|
74
77
|
# Especificar rama base de forma explícita
|
|
75
78
|
pr-split-advisor --base develop
|
|
76
79
|
|
|
80
|
+
# Ver el score actual de la rama sin dividir en PRs
|
|
81
|
+
pr-split-advisor score
|
|
82
|
+
pr-split-advisor score --base develop
|
|
83
|
+
|
|
77
84
|
# Consultar historial de análisis anteriores
|
|
78
85
|
pr-split-advisor --stats
|
|
79
86
|
|
package/dist/ai/config-wizard.js
CHANGED
|
@@ -60,7 +60,7 @@ async function testApiKeyConnection(provider, model, apiKey, apiKeyEnvVar) {
|
|
|
60
60
|
model,
|
|
61
61
|
apiKey,
|
|
62
62
|
apiKeyEnvVar,
|
|
63
|
-
features: { commitMessages: false, branchDescriptions: false },
|
|
63
|
+
features: { commitMessages: false, branchDescriptions: false, planRebalance: false },
|
|
64
64
|
timeoutMs: 10000,
|
|
65
65
|
maxTokens: 16,
|
|
66
66
|
},
|
|
@@ -227,7 +227,7 @@ export async function runConfigWizard() {
|
|
|
227
227
|
* Valida la key contra el proveedor antes de guardar. Si falla, aborta con error.
|
|
228
228
|
*
|
|
229
229
|
* @param apiKey - La API key del proveedor (literal, no nombre de env var).
|
|
230
|
-
* @param provider - ID del proveedor: "groq" | "
|
|
230
|
+
* @param provider - ID del proveedor: "groq" | "copilot". Predeterminado: "groq".
|
|
231
231
|
*/
|
|
232
232
|
export async function runConfigWithKey(apiKey, provider = "groq") {
|
|
233
233
|
const configPath = resolve(process.cwd(), CONFIG_FILE);
|
package/dist/cli.js
CHANGED
|
@@ -30,9 +30,10 @@ import { APP_BIN, APP_NAME, APP_VERSION } from "./shared/constants.js";
|
|
|
30
30
|
import { applyPlan } from "./git/executor.js";
|
|
31
31
|
import { detectRemote, fetchQuiet, getCurrentBranch, localAheadCount, localBehindCount, remoteTrackingExists, requireCleanIndex, requireGitRepo } from "./git/git.js";
|
|
32
32
|
import { buildBlocks, buildDependencyEdges, findBestPlan, gatherChangedFiles, getFileStats } from "./core/planner.js";
|
|
33
|
-
import { exportJson, writeHtmlReport } from "./output/report.js";
|
|
33
|
+
import { exportJson, writeHtmlReport, writeScoreHtmlReport } from "./output/report.js";
|
|
34
34
|
import { appendHistory, readHistory } from "./core/history.js";
|
|
35
35
|
import { buildSuggestedMessage } from "./core/commit-planner.js";
|
|
36
|
+
import { scorePullRequest } from "./core/scoring.js";
|
|
36
37
|
import { ui, closeReadlineInterface } from "./output/ui.js";
|
|
37
38
|
import { changeTypeLabel } from "./shared/utils.js";
|
|
38
39
|
import { aiEnrichPlans } from "./ai/enricher.js";
|
|
@@ -279,6 +280,7 @@ async function main() {
|
|
|
279
280
|
config.jsonOutputFile,
|
|
280
281
|
"pr-split-advisor.config.json",
|
|
281
282
|
"pr-split-report.html",
|
|
283
|
+
"pr-split-score.html",
|
|
282
284
|
config.historyFile
|
|
283
285
|
].filter(Boolean);
|
|
284
286
|
if (config.verbose) {
|
|
@@ -417,6 +419,85 @@ async function main() {
|
|
|
417
419
|
}
|
|
418
420
|
closeReadlineInterface();
|
|
419
421
|
});
|
|
422
|
+
// ─── Subcomando: score ───────────────────────────────────────────────────
|
|
423
|
+
program
|
|
424
|
+
.command("score")
|
|
425
|
+
.description("Muestra el score del estado actual de la rama sin dividir")
|
|
426
|
+
.option("-b, --base <branch>", "rama base para calcular los cambios")
|
|
427
|
+
.action(async (opts) => {
|
|
428
|
+
const config = loadConfig();
|
|
429
|
+
try {
|
|
430
|
+
requireGitRepo();
|
|
431
|
+
}
|
|
432
|
+
catch (error) {
|
|
433
|
+
ui.error(error.message);
|
|
434
|
+
closeReadlineInterface();
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
const baseBranch = opts.base ?? config.baseBranch ?? "main";
|
|
438
|
+
const currentBranch = getCurrentBranch();
|
|
439
|
+
config.runtimeExcludedFiles = [
|
|
440
|
+
config.jsonOutputFile,
|
|
441
|
+
"pr-split-advisor.config.json",
|
|
442
|
+
"pr-split-report.html",
|
|
443
|
+
"pr-split-score.html",
|
|
444
|
+
config.historyFile
|
|
445
|
+
].filter(Boolean);
|
|
446
|
+
ui.spinner.start("Calculando score del estado actual...");
|
|
447
|
+
const changedFiles = gatherChangedFiles(config, baseBranch);
|
|
448
|
+
if (!changedFiles.length) {
|
|
449
|
+
ui.spinner.stop();
|
|
450
|
+
ui.warn("No hay cambios respecto a la rama base.");
|
|
451
|
+
closeReadlineInterface();
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
const fileStats = getFileStats(changedFiles, baseBranch);
|
|
455
|
+
ui.spinner.stop("Calculo completado.");
|
|
456
|
+
const totalFiles = fileStats.length;
|
|
457
|
+
const totalLines = fileStats.reduce((sum, f) => sum + f.lines, 0);
|
|
458
|
+
const aheadCommits = localAheadCount(baseBranch);
|
|
459
|
+
const commitCount = Math.max(aheadCommits, 1);
|
|
460
|
+
const filesPerCommit = Number((totalFiles / commitCount).toFixed(2));
|
|
461
|
+
const avgLinesPerCommit = Math.round(totalLines / commitCount);
|
|
462
|
+
const result = scorePullRequest({ commitCount, filesPerCommit, avgLinesPerCommit, totalLinesChanged: totalLines }, config);
|
|
463
|
+
const warnThreshold = config.targetScore > 4 ? 4 : Math.max(0, config.targetScore - 1);
|
|
464
|
+
const scoreColor = ui.scoreColor(result.complexity, config.targetScore);
|
|
465
|
+
const statusBadge = result.complexity >= config.targetScore
|
|
466
|
+
? ui.badge("ÓPTIMO", "green")
|
|
467
|
+
: result.complexity >= warnThreshold
|
|
468
|
+
? ui.badge("ACEPTABLE", "yellow")
|
|
469
|
+
: ui.badge("RIESGO", "red");
|
|
470
|
+
const scoreHtmlFile = "pr-split-score.html";
|
|
471
|
+
writeScoreHtmlReport(scoreHtmlFile, {
|
|
472
|
+
currentBranch,
|
|
473
|
+
baseBranch,
|
|
474
|
+
config,
|
|
475
|
+
fileStats,
|
|
476
|
+
commitCount,
|
|
477
|
+
filesPerCommit,
|
|
478
|
+
avgLinesPerCommit,
|
|
479
|
+
result
|
|
480
|
+
});
|
|
481
|
+
ui.ok(`Reporte HTML generado: ${scoreHtmlFile}`);
|
|
482
|
+
ui.card(`Score actual de la rama: ${currentBranch}`, [
|
|
483
|
+
`${statusBadge}`,
|
|
484
|
+
"",
|
|
485
|
+
`Rama base: ${baseBranch}`,
|
|
486
|
+
`Archivos cambiados: ${totalFiles}`,
|
|
487
|
+
`Líneas totales: ${totalLines}`,
|
|
488
|
+
`Commits adelantados: ${commitCount}`,
|
|
489
|
+
`Archivos por commit: ${filesPerCommit}`,
|
|
490
|
+
`Líneas por commit (avg):${avgLinesPerCommit}`,
|
|
491
|
+
"",
|
|
492
|
+
`Score: ${ui.score(result.complexity, config.targetScore)}`,
|
|
493
|
+
`Score objetivo: ${config.targetScore}`,
|
|
494
|
+
"",
|
|
495
|
+
...Object.values(result.metrics).map((m) => ` ${m.label}: valor=${m.rawValue} pts=${m.points} pond=${m.weightedScore}`),
|
|
496
|
+
"",
|
|
497
|
+
`Recomendación: ${result.recommendation}`
|
|
498
|
+
], scoreColor);
|
|
499
|
+
closeReadlineInterface();
|
|
500
|
+
});
|
|
420
501
|
// ─── Subcomando: config ──────────────────────────────────────────────────
|
|
421
502
|
program
|
|
422
503
|
.command("config")
|
package/dist/config/config.js
CHANGED
|
@@ -304,7 +304,7 @@ export function loadConfig(configPath = "pr-split-advisor.config.json") {
|
|
|
304
304
|
}
|
|
305
305
|
if (isObject(ai["features"])) {
|
|
306
306
|
const f = ai["features"];
|
|
307
|
-
for (const flag of ["commitMessages", "branchDescriptions"]) {
|
|
307
|
+
for (const flag of ["commitMessages", "branchDescriptions", "planRebalance"]) {
|
|
308
308
|
if (flag in f && typeof f[flag] !== "boolean") {
|
|
309
309
|
throw new Error(`Configuración inválida: "ai.features.${flag}" debe ser true o false (recibido: ${JSON.stringify(f[flag])}).`);
|
|
310
310
|
}
|
|
@@ -188,9 +188,8 @@ export const defaultConfig = {
|
|
|
188
188
|
//
|
|
189
189
|
// Configurar vía `pr-split-advisor config` o editando directamente el JSON.
|
|
190
190
|
//
|
|
191
|
-
// Opciones de `provider` soportadas: "groq" | "
|
|
191
|
+
// Opciones de `provider` soportadas: "groq" | "copilot"
|
|
192
192
|
// groq → requiere GROQ_API_KEY (console.groq.com, free tier)
|
|
193
|
-
// github → requiere GITHUB_TOKEN (github.com/marketplace/models, free)
|
|
194
193
|
// copilot → sin token — usa automáticamente credenciales de gh CLI
|
|
195
194
|
//
|
|
196
195
|
// API key: preferir `apiKeyEnvVar` (ej: GROQ_API_KEY) antes que literal `apiKey`.
|
package/dist/output/report.js
CHANGED
|
@@ -706,6 +706,141 @@ function handleCopy(btn) {
|
|
|
706
706
|
export function writeHtmlReport(filePath, input) {
|
|
707
707
|
writeFileSync(filePath, renderHtmlReport(input), "utf8");
|
|
708
708
|
}
|
|
709
|
+
/**
|
|
710
|
+
* Genera el reporte HTML del subcomando `score` como string.
|
|
711
|
+
* Muestra el score actual de la rama sin dividir, con desglose de métricas y detalle de archivos.
|
|
712
|
+
*/
|
|
713
|
+
export function renderScoreHtmlReport(input) {
|
|
714
|
+
const { currentBranch, baseBranch, config, fileStats, commitCount, filesPerCommit, avgLinesPerCommit, result } = input;
|
|
715
|
+
const totalFiles = fileStats.length;
|
|
716
|
+
const totalLines = fileStats.reduce((sum, f) => sum + f.lines, 0);
|
|
717
|
+
const totalAdditions = fileStats.reduce((sum, f) => sum + f.additions, 0);
|
|
718
|
+
const totalDeletions = fileStats.reduce((sum, f) => sum + f.deletions, 0);
|
|
719
|
+
const maxLinesForBar = totalFiles > 0 ? Math.max(...fileStats.map((f) => f.lines)) : 1;
|
|
720
|
+
const label = scoreLabel(result.complexity, config.targetScore);
|
|
721
|
+
const warnThreshold = config.targetScore > 4 ? 4 : Math.max(0, config.targetScore - 1);
|
|
722
|
+
const scoreClass = result.complexity >= config.targetScore ? "green" : result.complexity >= warnThreshold ? "yellow" : "red";
|
|
723
|
+
const timestamp = new Date().toLocaleString("es-PE", {
|
|
724
|
+
year: "numeric", month: "long", day: "numeric",
|
|
725
|
+
hour: "2-digit", minute: "2-digit"
|
|
726
|
+
});
|
|
727
|
+
const metricRows = Object.values(result.metrics).map((m) => {
|
|
728
|
+
const ptsPct = Math.round((m.points / 5) * 100);
|
|
729
|
+
const barColor = m.points >= 4 ? "#16a34a" : m.points >= 3 ? "#d97706" : "#dc2626";
|
|
730
|
+
const ptsClass = m.points >= 4 ? "pts-good" : m.points >= 3 ? "pts-warn" : "pts-bad";
|
|
731
|
+
return `<tr>
|
|
732
|
+
<td><code class="metric-code">${esc(m.code)}</code></td>
|
|
733
|
+
<td class="metric-label">${esc(m.label)}</td>
|
|
734
|
+
<td class="metric-value">${m.rawValue}</td>
|
|
735
|
+
<td>
|
|
736
|
+
<div class="pts-bar-wrap"><div class="pts-bar" style="width:${ptsPct}%;background:${barColor}"></div></div>
|
|
737
|
+
<span class="${ptsClass}">${m.points} / 5</span>
|
|
738
|
+
</td>
|
|
739
|
+
<td class="metric-weight">${(m.weight * 100).toFixed(0)}%</td>
|
|
740
|
+
<td class="metric-contrib">${m.weightedScore.toFixed(3)}</td>
|
|
741
|
+
</tr>`;
|
|
742
|
+
}).join("");
|
|
743
|
+
return `<!doctype html>
|
|
744
|
+
<html lang="es">
|
|
745
|
+
<head>
|
|
746
|
+
<meta charset="utf-8" />
|
|
747
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
748
|
+
<title>Score actual – ${esc(currentBranch)}</title>
|
|
749
|
+
<style>${buildReportStyles()}</style>
|
|
750
|
+
</head>
|
|
751
|
+
<body>
|
|
752
|
+
<div class="container">
|
|
753
|
+
|
|
754
|
+
<header class="hero">
|
|
755
|
+
<div class="hero-top">
|
|
756
|
+
<div class="hero-title-area">
|
|
757
|
+
<h1>📊 Score actual de la rama</h1>
|
|
758
|
+
<p>Análisis del estado presente sin dividir en múltiples PRs.</p>
|
|
759
|
+
</div>
|
|
760
|
+
<div class="hero-timestamp">📅 ${timestamp}</div>
|
|
761
|
+
</div>
|
|
762
|
+
<div class="hero-grid">
|
|
763
|
+
<div class="hero-item"><div class="label">Rama analizada</div><div class="value">⎇ ${esc(currentBranch)}</div></div>
|
|
764
|
+
<div class="hero-item"><div class="label">Rama base</div><div class="value">⎇ ${esc(baseBranch)}</div></div>
|
|
765
|
+
<div class="hero-item"><div class="label">Score actual</div><div class="value">${result.complexity.toFixed(2)}</div></div>
|
|
766
|
+
<div class="hero-item"><div class="label">Score objetivo</div><div class="value">${config.targetScore} / 5.00</div></div>
|
|
767
|
+
<div class="hero-item"><div class="label">Archivos</div><div class="value">${totalFiles}</div></div>
|
|
768
|
+
<div class="hero-item"><div class="label">Líneas totales</div><div class="value">${totalLines}</div></div>
|
|
769
|
+
<div class="hero-item"><div class="label">Commits adelantados</div><div class="value">${commitCount}</div></div>
|
|
770
|
+
<div class="hero-item"><div class="label">Estado</div><div class="value score-label-${scoreClass}">${esc(label)}</div></div>
|
|
771
|
+
</div>
|
|
772
|
+
</header>
|
|
773
|
+
|
|
774
|
+
<section class="section">
|
|
775
|
+
<h2 class="section-heading">🎯 Score</h2>
|
|
776
|
+
<div class="card" style="display:flex;align-items:center;gap:32px;padding:24px">
|
|
777
|
+
${buildScoreGaugeSvg(result.complexity, config.targetScore)}
|
|
778
|
+
<div>
|
|
779
|
+
<div style="font-size:2rem;font-weight:800;color:${result.complexity >= config.targetScore ? "#16a34a" : result.complexity >= warnThreshold ? "#d97706" : "#dc2626"}">${result.complexity.toFixed(2)} / 5.00</div>
|
|
780
|
+
<div class="badge ${scoreClass}" style="margin-top:8px">${esc(label)}</div>
|
|
781
|
+
<p style="margin-top:12px;color:#64748b">${esc(result.recommendation)}</p>
|
|
782
|
+
</div>
|
|
783
|
+
</div>
|
|
784
|
+
</section>
|
|
785
|
+
|
|
786
|
+
<section class="section">
|
|
787
|
+
<h2 class="section-heading">📊 Desglose de métricas</h2>
|
|
788
|
+
<table class="metrics-table">
|
|
789
|
+
<thead>
|
|
790
|
+
<tr><th>Cód.</th><th>Métrica</th><th>Valor medido</th><th>Puntos</th><th>Peso</th><th>Aporte</th></tr>
|
|
791
|
+
</thead>
|
|
792
|
+
<tbody>
|
|
793
|
+
${metricRows}
|
|
794
|
+
<tr class="metrics-total-row">
|
|
795
|
+
<td colspan="5" style="text-align:right;font-weight:700;">Score total</td>
|
|
796
|
+
<td class="metric-total-value">${result.complexity.toFixed(3)}</td>
|
|
797
|
+
</tr>
|
|
798
|
+
</tbody>
|
|
799
|
+
</table>
|
|
800
|
+
</section>
|
|
801
|
+
|
|
802
|
+
<section class="section">
|
|
803
|
+
<h2 class="section-heading">📈 Valores brutos usados</h2>
|
|
804
|
+
<div class="summary-grid">
|
|
805
|
+
<div class="stat-card blue"><div class="label">Commits</div><div class="value">${commitCount}</div></div>
|
|
806
|
+
<div class="stat-card blue"><div class="label">Archivos / commit</div><div class="value">${filesPerCommit}</div></div>
|
|
807
|
+
<div class="stat-card blue"><div class="label">Líneas / commit (avg)</div><div class="value">${avgLinesPerCommit}</div></div>
|
|
808
|
+
<div class="stat-card blue"><div class="label">Total de líneas</div><div class="value">${totalLines}</div></div>
|
|
809
|
+
<div class="stat-card green"><div class="label">Líneas agregadas</div><div class="value">+${totalAdditions}</div></div>
|
|
810
|
+
<div class="stat-card red"><div class="label">Líneas eliminadas</div><div class="value">-${totalDeletions}</div></div>
|
|
811
|
+
</div>
|
|
812
|
+
</section>
|
|
813
|
+
|
|
814
|
+
<section class="section">
|
|
815
|
+
<h2 class="section-heading">📄 Detalle de archivos</h2>
|
|
816
|
+
<table>
|
|
817
|
+
<thead>
|
|
818
|
+
<tr><th>Tipo</th><th>Archivo</th><th>Líneas</th><th>Cambios (+/-)</th><th>Prioridad</th></tr>
|
|
819
|
+
</thead>
|
|
820
|
+
<tbody>
|
|
821
|
+
${fileStats.map((f) => buildFilesBarsRow(f, maxLinesForBar)).join("")}
|
|
822
|
+
</tbody>
|
|
823
|
+
</table>
|
|
824
|
+
</section>
|
|
825
|
+
|
|
826
|
+
<footer class="footer">
|
|
827
|
+
<p>Generado por <strong>${APP_REPORT_TITLE}</strong> · ${timestamp}</p>
|
|
828
|
+
<p>Rama: <strong>${esc(currentBranch)}</strong> vs <strong>${esc(baseBranch)}</strong> · Score: <strong>${result.complexity.toFixed(2)}</strong> · Objetivo: <strong>${config.targetScore}</strong></p>
|
|
829
|
+
</footer>
|
|
830
|
+
|
|
831
|
+
</div>
|
|
832
|
+
</body>
|
|
833
|
+
</html>`;
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Genera el reporte HTML de score puntual y lo escribe en disco.
|
|
837
|
+
*
|
|
838
|
+
* @param filePath - Ruta de destino del archivo HTML.
|
|
839
|
+
* @param input - Datos del análisis de score.
|
|
840
|
+
*/
|
|
841
|
+
export function writeScoreHtmlReport(filePath, input) {
|
|
842
|
+
writeFileSync(filePath, renderScoreHtmlReport(input), "utf8");
|
|
843
|
+
}
|
|
709
844
|
/**
|
|
710
845
|
* Genera el JSON serializado del plan.
|
|
711
846
|
* @param plans - Lista de planes de rama.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pull-request-split-advisor",
|
|
3
|
-
"version": "3.1
|
|
3
|
+
"version": "3.2.1",
|
|
4
4
|
"description": "CLI that analyses your Git working tree and suggests how to split changes into smaller, reviewable pull requests and commits.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"git",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
],
|
|
34
34
|
"scripts": {
|
|
35
35
|
"build:styles": "node scripts/compile-styles.cjs",
|
|
36
|
-
"build": "npm run build:styles && tsc -p tsconfig.json",
|
|
36
|
+
"build": "npm run build:styles && tsc -p tsconfig.build.json",
|
|
37
37
|
"prepare": "npm run build",
|
|
38
38
|
"dev": "tsx src/cli.ts",
|
|
39
39
|
"start": "node dist/cli.js",
|
package/scripts/postinstall.cjs
CHANGED
|
@@ -83,30 +83,56 @@ async function main() {
|
|
|
83
83
|
);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
// ── Añadir
|
|
86
|
+
// ── Añadir entradas al .gitignore del proyecto consumidor ──────────────
|
|
87
87
|
// La config es local por equipo/desarrollador y no debe versionarse.
|
|
88
|
-
|
|
89
|
-
const
|
|
88
|
+
// Los artefactos generados (reportes HTML, plan JSON, historial) tampoco.
|
|
89
|
+
const gitignorePath = join(targetDir, ".gitignore");
|
|
90
|
+
|
|
91
|
+
const gitignoreEntries = [
|
|
92
|
+
{
|
|
93
|
+
entry: "pr-split-advisor.config.json",
|
|
94
|
+
comment: "# pr-split-advisor — config local (no compartir en el repositorio)"
|
|
95
|
+
},
|
|
96
|
+
{ entry: "pr-split-report.html", comment: null },
|
|
97
|
+
{ entry: "pr-split-score.html", comment: null },
|
|
98
|
+
{ entry: "pr-split-plan.json", comment: null },
|
|
99
|
+
{ entry: ".pr-split-history.json", comment: null }
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
// Cabecera del grupo de artefactos: solo se añade si al menos uno de ellos
|
|
103
|
+
// no está ya en el .gitignore, y solo una vez para todo el grupo.
|
|
104
|
+
const artifactEntries = ["pr-split-report.html", "pr-split-score.html", "pr-split-plan.json", ".pr-split-history.json"];
|
|
90
105
|
|
|
91
106
|
try {
|
|
92
107
|
const currentContent = existsSync(gitignorePath)
|
|
93
108
|
? readFileSync(gitignorePath, "utf-8")
|
|
94
109
|
: "";
|
|
95
110
|
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
111
|
+
const existingLines = currentContent.split("\n").map((l) => l.trim());
|
|
112
|
+
let block = currentContent.length && !currentContent.endsWith("\n") ? "\n" : "";
|
|
113
|
+
let addedAny = false;
|
|
114
|
+
let artifactHeaderAdded = false;
|
|
115
|
+
|
|
116
|
+
for (const { entry, comment } of gitignoreEntries) {
|
|
117
|
+
if (existingLines.includes(entry)) continue;
|
|
100
118
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
"\n# pr-split-advisor
|
|
105
|
-
|
|
119
|
+
const isArtifact = artifactEntries.includes(entry);
|
|
120
|
+
if (isArtifact && !artifactHeaderAdded && !comment) {
|
|
121
|
+
// Escribir la cabecera de artefactos la primera vez que haya uno nuevo
|
|
122
|
+
block += "\n# pr-split-advisor \u2014 artefactos generados\n";
|
|
123
|
+
artifactHeaderAdded = true;
|
|
124
|
+
} else if (comment) {
|
|
125
|
+
block += "\n" + comment + "\n";
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
block += entry + "\n";
|
|
129
|
+
addedAny = true;
|
|
130
|
+
}
|
|
106
131
|
|
|
132
|
+
if (addedAny) {
|
|
107
133
|
appendFileSync(gitignorePath, block, "utf-8");
|
|
108
134
|
console.log(
|
|
109
|
-
"[pr-split-advisor] ✔
|
|
135
|
+
"[pr-split-advisor] ✔ Añadidas entradas de pr-split-advisor al .gitignore"
|
|
110
136
|
);
|
|
111
137
|
}
|
|
112
138
|
} catch (err) {
|