git-smart-flow 0.3.0
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/CHANGELOG.md +33 -0
- package/LICENSE +21 -0
- package/README.md +130 -0
- package/bin/git-smart-flow.js +2 -0
- package/bin/gsf.js +2 -0
- package/bin/gsfc.js +3 -0
- package/bin/gsfm.js +3 -0
- package/bin/gsfp.js +3 -0
- package/bin/gsfpr.js +3 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +214 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/aliases.d.ts +2 -0
- package/dist/commands/aliases.js +37 -0
- package/dist/commands/aliases.js.map +1 -0
- package/dist/commands/branch.d.ts +2 -0
- package/dist/commands/branch.js +414 -0
- package/dist/commands/branch.js.map +1 -0
- package/dist/commands/commit-message.d.ts +7 -0
- package/dist/commands/commit-message.js +95 -0
- package/dist/commands/commit-message.js.map +1 -0
- package/dist/commands/commit.d.ts +3 -0
- package/dist/commands/commit.js +597 -0
- package/dist/commands/commit.js.map +1 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +88 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +246 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/info.d.ts +2 -0
- package/dist/commands/info.js +155 -0
- package/dist/commands/info.js.map +1 -0
- package/dist/commands/install-hooks.d.ts +2 -0
- package/dist/commands/install-hooks.js +66 -0
- package/dist/commands/install-hooks.js.map +1 -0
- package/dist/commands/log.d.ts +2 -0
- package/dist/commands/log.js +101 -0
- package/dist/commands/log.js.map +1 -0
- package/dist/commands/menu.d.ts +2 -0
- package/dist/commands/menu.js +297 -0
- package/dist/commands/menu.js.map +1 -0
- package/dist/commands/merge.d.ts +6 -0
- package/dist/commands/merge.js +128 -0
- package/dist/commands/merge.js.map +1 -0
- package/dist/commands/pr.d.ts +2 -0
- package/dist/commands/pr.js +731 -0
- package/dist/commands/pr.js.map +1 -0
- package/dist/commands/push.d.ts +7 -0
- package/dist/commands/push.js +225 -0
- package/dist/commands/push.js.map +1 -0
- package/dist/commands/reflog.d.ts +2 -0
- package/dist/commands/reflog.js +162 -0
- package/dist/commands/reflog.js.map +1 -0
- package/dist/commands/repo-init.d.ts +2 -0
- package/dist/commands/repo-init.js +466 -0
- package/dist/commands/repo-init.js.map +1 -0
- package/dist/commands/revert.d.ts +7 -0
- package/dist/commands/revert.js +694 -0
- package/dist/commands/revert.js.map +1 -0
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.js +86 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/stash.d.ts +2 -0
- package/dist/commands/stash.js +130 -0
- package/dist/commands/stash.js.map +1 -0
- package/dist/commands/sync.d.ts +2 -0
- package/dist/commands/sync.js +335 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/tag.d.ts +2 -0
- package/dist/commands/tag.js +163 -0
- package/dist/commands/tag.js.map +1 -0
- package/dist/commands/validate.d.ts +2 -0
- package/dist/commands/validate.js +203 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/config/config.d.ts +10 -0
- package/dist/config/config.js +126 -0
- package/dist/config/config.js.map +1 -0
- package/dist/git/ai-context-builder.d.ts +11 -0
- package/dist/git/ai-context-builder.js +112 -0
- package/dist/git/ai-context-builder.js.map +1 -0
- package/dist/git/convention-detector.d.ts +3 -0
- package/dist/git/convention-detector.js +211 -0
- package/dist/git/convention-detector.js.map +1 -0
- package/dist/git/ensure-repo.d.ts +7 -0
- package/dist/git/ensure-repo.js +141 -0
- package/dist/git/ensure-repo.js.map +1 -0
- package/dist/git/gitignore.d.ts +8 -0
- package/dist/git/gitignore.js +261 -0
- package/dist/git/gitignore.js.map +1 -0
- package/dist/git/remote-setup.d.ts +2 -0
- package/dist/git/remote-setup.js +129 -0
- package/dist/git/remote-setup.js.map +1 -0
- package/dist/git/repo.d.ts +73 -0
- package/dist/git/repo.js +308 -0
- package/dist/git/repo.js.map +1 -0
- package/dist/git/validate.d.ts +36 -0
- package/dist/git/validate.js +113 -0
- package/dist/git/validate.js.map +1 -0
- package/dist/providers/base.provider.d.ts +10 -0
- package/dist/providers/base.provider.js +40 -0
- package/dist/providers/base.provider.js.map +1 -0
- package/dist/providers/claude.provider.d.ts +14 -0
- package/dist/providers/claude.provider.js +85 -0
- package/dist/providers/claude.provider.js.map +1 -0
- package/dist/providers/copilot.provider.d.ts +12 -0
- package/dist/providers/copilot.provider.js +88 -0
- package/dist/providers/copilot.provider.js.map +1 -0
- package/dist/providers/heuristic.provider.d.ts +9 -0
- package/dist/providers/heuristic.provider.js +163 -0
- package/dist/providers/heuristic.provider.js.map +1 -0
- package/dist/providers/ollama.provider.d.ts +14 -0
- package/dist/providers/ollama.provider.js +83 -0
- package/dist/providers/ollama.provider.js.map +1 -0
- package/dist/providers/openai.provider.d.ts +14 -0
- package/dist/providers/openai.provider.js +84 -0
- package/dist/providers/openai.provider.js.map +1 -0
- package/dist/providers/provider.factory.d.ts +5 -0
- package/dist/providers/provider.factory.js +51 -0
- package/dist/providers/provider.factory.js.map +1 -0
- package/dist/security/scanner.d.ts +13 -0
- package/dist/security/scanner.js +138 -0
- package/dist/security/scanner.js.map +1 -0
- package/dist/types/index.d.ts +146 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/ux/components/BranchTree.d.ts +8 -0
- package/dist/ux/components/BranchTree.js +57 -0
- package/dist/ux/components/BranchTree.js.map +1 -0
- package/dist/ux/components/CommitProposal.d.ts +13 -0
- package/dist/ux/components/CommitProposal.js +127 -0
- package/dist/ux/components/CommitProposal.js.map +1 -0
- package/dist/ux/components/DiagnosticReport.d.ts +18 -0
- package/dist/ux/components/DiagnosticReport.js +19 -0
- package/dist/ux/components/DiagnosticReport.js.map +1 -0
- package/dist/ux/components/ErrorBox.d.ts +7 -0
- package/dist/ux/components/ErrorBox.js +9 -0
- package/dist/ux/components/ErrorBox.js.map +1 -0
- package/dist/ux/components/FileSelector.d.ts +14 -0
- package/dist/ux/components/FileSelector.js +87 -0
- package/dist/ux/components/FileSelector.js.map +1 -0
- package/dist/ux/components/Logo.d.ts +6 -0
- package/dist/ux/components/Logo.js +21 -0
- package/dist/ux/components/Logo.js.map +1 -0
- package/dist/ux/components/RepoContext.d.ts +8 -0
- package/dist/ux/components/RepoContext.js +17 -0
- package/dist/ux/components/RepoContext.js.map +1 -0
- package/dist/ux/components/SecurityAlert.d.ts +9 -0
- package/dist/ux/components/SecurityAlert.js +16 -0
- package/dist/ux/components/SecurityAlert.js.map +1 -0
- package/dist/ux/components/StatusDashboard.d.ts +14 -0
- package/dist/ux/components/StatusDashboard.js +36 -0
- package/dist/ux/components/StatusDashboard.js.map +1 -0
- package/dist/ux/components/SuccessBox.d.ts +7 -0
- package/dist/ux/components/SuccessBox.js +9 -0
- package/dist/ux/components/SuccessBox.js.map +1 -0
- package/dist/ux/components/ValidationReport.d.ts +16 -0
- package/dist/ux/components/ValidationReport.js +19 -0
- package/dist/ux/components/ValidationReport.js.map +1 -0
- package/dist/ux/components/WarningBox.d.ts +7 -0
- package/dist/ux/components/WarningBox.js +9 -0
- package/dist/ux/components/WarningBox.js.map +1 -0
- package/dist/ux/display.d.ts +21 -0
- package/dist/ux/display.js +96 -0
- package/dist/ux/display.js.map +1 -0
- package/dist/ux/hooks/useActivation.d.ts +8 -0
- package/dist/ux/hooks/useActivation.js +16 -0
- package/dist/ux/hooks/useActivation.js.map +1 -0
- package/dist/ux/hooks/useSpinner.d.ts +2 -0
- package/dist/ux/hooks/useSpinner.js +13 -0
- package/dist/ux/hooks/useSpinner.js.map +1 -0
- package/dist/ux/menu.d.ts +7 -0
- package/dist/ux/menu.js +56 -0
- package/dist/ux/menu.js.map +1 -0
- package/dist/ux/prompt.d.ts +7 -0
- package/dist/ux/prompt.js +361 -0
- package/dist/ux/prompt.js.map +1 -0
- package/dist/ux/renderer.d.ts +9 -0
- package/dist/ux/renderer.js +45 -0
- package/dist/ux/renderer.js.map +1 -0
- package/dist/ux/spinner.d.ts +6 -0
- package/dist/ux/spinner.js +42 -0
- package/dist/ux/spinner.js.map +1 -0
- package/dist/ux/statusbar.d.ts +2 -0
- package/dist/ux/statusbar.js +44 -0
- package/dist/ux/statusbar.js.map +1 -0
- package/dist/ux/theme.d.ts +37 -0
- package/dist/ux/theme.js +55 -0
- package/dist/ux/theme.js.map +1 -0
- package/package.json +125 -0
|
@@ -0,0 +1,731 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { spawnSync } from 'child_process';
|
|
4
|
+
import { getConfig } from '../config/config.js';
|
|
5
|
+
import { buildAIContext } from '../git/ai-context-builder.js';
|
|
6
|
+
import { detectConvention } from '../git/convention-detector.js';
|
|
7
|
+
import { ensureGitRepo } from '../git/ensure-repo.js';
|
|
8
|
+
import { extractTicketFromBranch, getCurrentBranch, getCommitsSinceBase, getRepoName, getStagedFiles, } from '../git/repo.js';
|
|
9
|
+
import { createProviderWithFallback } from '../providers/provider.factory.js';
|
|
10
|
+
import { isCI } from '../ux/renderer.js';
|
|
11
|
+
import { blank, divider, error, info, keyValue, section, success } from '../ux/display.js';
|
|
12
|
+
import { confirmPrompt, inputPrompt, selectPrompt } from '../ux/prompt.js';
|
|
13
|
+
import { failSpinner, startSpinner, succeedSpinner } from '../ux/spinner.js';
|
|
14
|
+
const DEFAULT_TEMPLATE = `## Context
|
|
15
|
+
<!-- Why is this change needed? -->
|
|
16
|
+
|
|
17
|
+
## Changes
|
|
18
|
+
<!-- What has been changed? -->
|
|
19
|
+
|
|
20
|
+
## Testing
|
|
21
|
+
<!-- How has this been tested? -->
|
|
22
|
+
|
|
23
|
+
## Risks / Impact
|
|
24
|
+
<!-- Any risks or impacts? -->
|
|
25
|
+
|
|
26
|
+
## Additional Notes
|
|
27
|
+
<!-- Any other relevant information? -->
|
|
28
|
+
`;
|
|
29
|
+
// ── gh CLI helpers ──────────────────────────────────────────────────────────
|
|
30
|
+
function ghAvailable() {
|
|
31
|
+
return spawnSync('gh', ['auth', 'status'], { encoding: 'utf-8', stdio: 'pipe' }).status === 0;
|
|
32
|
+
}
|
|
33
|
+
function requireGh() {
|
|
34
|
+
if (ghAvailable())
|
|
35
|
+
return true;
|
|
36
|
+
error('Esta funcionalidad requiere GitHub CLI (gh) autenticado.');
|
|
37
|
+
info(' Instala gh: https://cli.github.com');
|
|
38
|
+
info(' Luego ejecuta: gh auth login');
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
// ── New GitHub-integrated operations ───────────────────────────────────────
|
|
42
|
+
async function runCreatePROnGitHub(title, body) {
|
|
43
|
+
if (!requireGh())
|
|
44
|
+
return;
|
|
45
|
+
const config = getConfig();
|
|
46
|
+
const cwd = process.cwd();
|
|
47
|
+
const defaultBase = config.git.defaultBaseBranches[0] ?? 'main';
|
|
48
|
+
const basePick = await selectPrompt(`Rama base (actual: ${defaultBase}):`, [
|
|
49
|
+
...config.git.defaultBaseBranches,
|
|
50
|
+
'Ingresar manualmente',
|
|
51
|
+
]);
|
|
52
|
+
let base;
|
|
53
|
+
if (basePick === 'Ingresar manualmente') {
|
|
54
|
+
base = await inputPrompt('Rama base', defaultBase);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
base = basePick;
|
|
58
|
+
}
|
|
59
|
+
const draft = await confirmPrompt('¿Crear como draft?', false);
|
|
60
|
+
const reviewersInput = await inputPrompt('Reviewers (opcional, separados por coma)', '');
|
|
61
|
+
const reviewers = reviewersInput
|
|
62
|
+
.split(',')
|
|
63
|
+
.map((r) => r.trim())
|
|
64
|
+
.filter(Boolean);
|
|
65
|
+
blank();
|
|
66
|
+
section('Vista previa del PR');
|
|
67
|
+
keyValue('Título', title);
|
|
68
|
+
keyValue('Base', base);
|
|
69
|
+
keyValue('Draft', draft ? 'Sí' : 'No');
|
|
70
|
+
if (reviewers.length > 0)
|
|
71
|
+
keyValue('Reviewers', reviewers.join(', '));
|
|
72
|
+
blank();
|
|
73
|
+
console.log(body.split('\n').slice(0, 10).join('\n'));
|
|
74
|
+
if (body.split('\n').length > 10) {
|
|
75
|
+
info(` … (${body.split('\n').length - 10} líneas más)`);
|
|
76
|
+
}
|
|
77
|
+
blank();
|
|
78
|
+
const confirmed = await confirmPrompt('¿Crear el PR?', true);
|
|
79
|
+
if (!confirmed) {
|
|
80
|
+
info('Operación cancelada.');
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const args = ['pr', 'create', '--title', title, '--body', body, '--base', base];
|
|
84
|
+
if (draft)
|
|
85
|
+
args.push('--draft');
|
|
86
|
+
for (const reviewer of reviewers) {
|
|
87
|
+
args.push('--reviewer', reviewer);
|
|
88
|
+
}
|
|
89
|
+
startSpinner('Creando PR en GitHub...');
|
|
90
|
+
const result = spawnSync('gh', args, { encoding: 'utf-8', stdio: 'pipe', cwd });
|
|
91
|
+
if (result.status !== 0) {
|
|
92
|
+
failSpinner('Error al crear PR');
|
|
93
|
+
error(result.stderr?.trim() || 'Error desconocido');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
succeedSpinner('PR creado');
|
|
97
|
+
const url = result.stdout?.trim() ?? '';
|
|
98
|
+
if (url) {
|
|
99
|
+
success(`URL: ${url}`);
|
|
100
|
+
const open = await confirmPrompt('¿Abrir en el navegador?', true);
|
|
101
|
+
if (open) {
|
|
102
|
+
spawnSync('gh', ['pr', 'view', '--web'], { cwd, stdio: 'inherit' });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function runPRStatus() {
|
|
107
|
+
if (!requireGh())
|
|
108
|
+
return;
|
|
109
|
+
const cwd = process.cwd();
|
|
110
|
+
startSpinner('Obteniendo estado de PRs...');
|
|
111
|
+
const result = spawnSync('gh', ['pr', 'status', '--json', 'number,title,headRefName,statusCheckRollup,reviewDecision,url'], { encoding: 'utf-8', stdio: 'pipe', cwd });
|
|
112
|
+
if (result.status !== 0) {
|
|
113
|
+
failSpinner('Error al obtener estado');
|
|
114
|
+
error(result.stderr?.trim() || 'Error desconocido');
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
succeedSpinner();
|
|
118
|
+
let data = {};
|
|
119
|
+
try {
|
|
120
|
+
data = JSON.parse(result.stdout);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
error('No se pudo parsear la respuesta de gh.');
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
function checksLabel(checks) {
|
|
127
|
+
if (!checks || checks.length === 0)
|
|
128
|
+
return '';
|
|
129
|
+
const pass = checks.filter((c) => c.state === 'SUCCESS').length;
|
|
130
|
+
const fail = checks.filter((c) => c.state === 'FAILURE').length;
|
|
131
|
+
const pending = checks.filter((c) => c.state === 'PENDING' || c.state === 'IN_PROGRESS').length;
|
|
132
|
+
if (fail > 0)
|
|
133
|
+
return `❌ ${fail} check(s) fallaron`;
|
|
134
|
+
if (pending > 0)
|
|
135
|
+
return `⏳ ${pending} en progreso`;
|
|
136
|
+
return `✅ ${pass}/${checks.length} checks`;
|
|
137
|
+
}
|
|
138
|
+
function reviewLabel(decision) {
|
|
139
|
+
if (!decision)
|
|
140
|
+
return '';
|
|
141
|
+
if (decision === 'APPROVED')
|
|
142
|
+
return '✅ Aprobado';
|
|
143
|
+
if (decision === 'CHANGES_REQUESTED')
|
|
144
|
+
return '🔄 Cambios solicitados';
|
|
145
|
+
if (decision === 'REVIEW_REQUIRED')
|
|
146
|
+
return '🔍 Review requerido';
|
|
147
|
+
return decision;
|
|
148
|
+
}
|
|
149
|
+
blank();
|
|
150
|
+
section('Estado de Pull Requests');
|
|
151
|
+
divider();
|
|
152
|
+
blank();
|
|
153
|
+
info('Rama actual');
|
|
154
|
+
if (data.currentBranch) {
|
|
155
|
+
const pr = data.currentBranch;
|
|
156
|
+
const checks = checksLabel(pr.statusCheckRollup);
|
|
157
|
+
const review = reviewLabel(pr.reviewDecision);
|
|
158
|
+
const meta = [checks, review].filter(Boolean).join(' · ');
|
|
159
|
+
console.log(` #${pr.number} ${pr.title} [${pr.headRefName}]`);
|
|
160
|
+
if (meta)
|
|
161
|
+
console.log(` ${meta}`);
|
|
162
|
+
keyValue('URL', pr.url, 2);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
info(' No hay PR asociado a la rama actual.');
|
|
166
|
+
}
|
|
167
|
+
blank();
|
|
168
|
+
info('Creados por ti');
|
|
169
|
+
if (data.createdBy && data.createdBy.length > 0) {
|
|
170
|
+
for (const pr of data.createdBy) {
|
|
171
|
+
const checks = checksLabel(pr.statusCheckRollup);
|
|
172
|
+
const review = reviewLabel(pr.reviewDecision);
|
|
173
|
+
const meta = [checks, review].filter(Boolean).join(' · ');
|
|
174
|
+
console.log(` #${pr.number} ${pr.title} [${pr.headRefName}]`);
|
|
175
|
+
if (meta)
|
|
176
|
+
console.log(` ${meta}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
info(' Sin PRs abiertos.');
|
|
181
|
+
}
|
|
182
|
+
blank();
|
|
183
|
+
info('Esperando tu review');
|
|
184
|
+
if (data.needsReview && data.needsReview.length > 0) {
|
|
185
|
+
for (const pr of data.needsReview) {
|
|
186
|
+
console.log(` #${pr.number} ${pr.title} [${pr.headRefName}]`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
info(' Ninguno.');
|
|
191
|
+
}
|
|
192
|
+
blank();
|
|
193
|
+
divider();
|
|
194
|
+
}
|
|
195
|
+
async function runPRChecks() {
|
|
196
|
+
if (!requireGh())
|
|
197
|
+
return;
|
|
198
|
+
const cwd = process.cwd();
|
|
199
|
+
const display = () => {
|
|
200
|
+
startSpinner('Obteniendo CI checks...');
|
|
201
|
+
const result = spawnSync('gh', ['pr', 'checks', '--json', 'name,state,completedAt,startedAt,link'], { encoding: 'utf-8', stdio: 'pipe', cwd });
|
|
202
|
+
if (result.status !== 0) {
|
|
203
|
+
failSpinner('Error al obtener checks');
|
|
204
|
+
error(result.stderr?.trim() || 'Error desconocido');
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
succeedSpinner();
|
|
208
|
+
let checks = [];
|
|
209
|
+
try {
|
|
210
|
+
checks = JSON.parse(result.stdout);
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
error('No se pudo parsear la respuesta de gh.');
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
blank();
|
|
217
|
+
section('CI Checks');
|
|
218
|
+
divider();
|
|
219
|
+
for (const check of checks) {
|
|
220
|
+
const state = check.state?.toUpperCase() ?? '';
|
|
221
|
+
let icon;
|
|
222
|
+
if (state === 'SUCCESS' || state === 'PASS')
|
|
223
|
+
icon = '✅';
|
|
224
|
+
else if (state === 'FAILURE' || state === 'FAIL' || state === 'ERROR')
|
|
225
|
+
icon = '❌';
|
|
226
|
+
else if (state === 'PENDING' || state === 'IN_PROGRESS' || state === 'QUEUED')
|
|
227
|
+
icon = '⏳';
|
|
228
|
+
else if (state === 'SKIPPED' || state === 'NEUTRAL')
|
|
229
|
+
icon = '○';
|
|
230
|
+
else
|
|
231
|
+
icon = '○';
|
|
232
|
+
console.log(` ${icon} ${check.name} ${check.link ? `[${check.link}]` : ''}`);
|
|
233
|
+
}
|
|
234
|
+
divider();
|
|
235
|
+
blank();
|
|
236
|
+
};
|
|
237
|
+
display();
|
|
238
|
+
while (true) {
|
|
239
|
+
const again = await confirmPrompt('¿Actualizar checks?', false);
|
|
240
|
+
if (!again)
|
|
241
|
+
break;
|
|
242
|
+
display();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
async function runPRMerge() {
|
|
246
|
+
if (!requireGh())
|
|
247
|
+
return;
|
|
248
|
+
const cwd = process.cwd();
|
|
249
|
+
startSpinner('Obteniendo información del PR...');
|
|
250
|
+
const viewResult = spawnSync('gh', ['pr', 'view', '--json', 'number,title,state,statusCheckRollup,reviewDecision'], { encoding: 'utf-8', stdio: 'pipe', cwd });
|
|
251
|
+
if (viewResult.status !== 0) {
|
|
252
|
+
failSpinner('Error al obtener PR');
|
|
253
|
+
error(viewResult.stderr?.trim() || 'No hay PR asociado a la rama actual.');
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
succeedSpinner();
|
|
257
|
+
let pr;
|
|
258
|
+
try {
|
|
259
|
+
pr = JSON.parse(viewResult.stdout);
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
error('No se pudo parsear la respuesta de gh.');
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
blank();
|
|
266
|
+
section(`PR #${pr.number}: ${pr.title}`);
|
|
267
|
+
keyValue('Estado', pr.state);
|
|
268
|
+
if (pr.reviewDecision)
|
|
269
|
+
keyValue('Review', pr.reviewDecision);
|
|
270
|
+
blank();
|
|
271
|
+
const strategy = await selectPrompt('Estrategia de merge:', [
|
|
272
|
+
'Merge commit',
|
|
273
|
+
'Squash and merge (recomendado para features)',
|
|
274
|
+
'Rebase and merge',
|
|
275
|
+
]);
|
|
276
|
+
const deleteBranch = await confirmPrompt('¿Eliminar rama tras el merge?', true);
|
|
277
|
+
const confirmed = await confirmPrompt(`¿Confirmar merge del PR #${pr.number}?`, true);
|
|
278
|
+
if (!confirmed) {
|
|
279
|
+
info('Operación cancelada.');
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const mergeFlag = strategy === 'Squash and merge (recomendado para features)'
|
|
283
|
+
? '--squash'
|
|
284
|
+
: strategy === 'Rebase and merge'
|
|
285
|
+
? '--rebase'
|
|
286
|
+
: '--merge';
|
|
287
|
+
const args = ['pr', 'merge', String(pr.number), mergeFlag];
|
|
288
|
+
if (deleteBranch)
|
|
289
|
+
args.push('--delete-branch');
|
|
290
|
+
startSpinner('Mergeando PR...');
|
|
291
|
+
const mergeResult = spawnSync('gh', args, { encoding: 'utf-8', stdio: 'pipe', cwd });
|
|
292
|
+
if (mergeResult.status !== 0) {
|
|
293
|
+
failSpinner('Error al mergear');
|
|
294
|
+
error(mergeResult.stderr?.trim() || 'Error desconocido');
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
succeedSpinner(`PR #${pr.number} mergeado correctamente`);
|
|
298
|
+
if (mergeResult.stdout?.trim())
|
|
299
|
+
console.log(mergeResult.stdout.trim());
|
|
300
|
+
}
|
|
301
|
+
async function runPRCheckout() {
|
|
302
|
+
if (!requireGh())
|
|
303
|
+
return;
|
|
304
|
+
const cwd = process.cwd();
|
|
305
|
+
startSpinner('Obteniendo lista de PRs...');
|
|
306
|
+
const result = spawnSync('gh', ['pr', 'list', '--json', 'number,title,headRefName,author,url', '--limit', '10'], { encoding: 'utf-8', stdio: 'pipe', cwd });
|
|
307
|
+
if (result.status !== 0) {
|
|
308
|
+
failSpinner('Error al obtener PRs');
|
|
309
|
+
error(result.stderr?.trim() || 'Error desconocido');
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
succeedSpinner();
|
|
313
|
+
let prs = [];
|
|
314
|
+
try {
|
|
315
|
+
prs = JSON.parse(result.stdout);
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
error('No se pudo parsear la respuesta de gh.');
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
if (prs.length === 0) {
|
|
322
|
+
info('No hay PRs abiertos.');
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const choices = prs.map((pr) => `#${pr.number} ${pr.title} (by @${pr.author?.login ?? 'unknown'})`);
|
|
326
|
+
const picked = await selectPrompt('Selecciona un PR para checkout:', choices);
|
|
327
|
+
const idx = choices.indexOf(picked);
|
|
328
|
+
const pr = prs[idx];
|
|
329
|
+
if (!pr)
|
|
330
|
+
return;
|
|
331
|
+
startSpinner(`Haciendo checkout del PR #${pr.number}...`);
|
|
332
|
+
const checkoutResult = spawnSync('gh', ['pr', 'checkout', String(pr.number)], {
|
|
333
|
+
encoding: 'utf-8',
|
|
334
|
+
stdio: 'pipe',
|
|
335
|
+
cwd,
|
|
336
|
+
});
|
|
337
|
+
if (checkoutResult.status !== 0) {
|
|
338
|
+
failSpinner('Error en checkout');
|
|
339
|
+
error(checkoutResult.stderr?.trim() || 'Error desconocido');
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
succeedSpinner(`Checkout en rama: ${pr.headRefName}`);
|
|
343
|
+
blank();
|
|
344
|
+
section(`PR #${pr.number}: ${pr.title}`);
|
|
345
|
+
keyValue('Rama', pr.headRefName);
|
|
346
|
+
keyValue('Autor', `@${pr.author?.login ?? 'unknown'}`);
|
|
347
|
+
keyValue('URL', pr.url);
|
|
348
|
+
}
|
|
349
|
+
async function runPRReview() {
|
|
350
|
+
if (!requireGh())
|
|
351
|
+
return;
|
|
352
|
+
const cwd = process.cwd();
|
|
353
|
+
startSpinner('Buscando PRs que requieren tu review...');
|
|
354
|
+
const result = spawnSync('gh', [
|
|
355
|
+
'pr',
|
|
356
|
+
'list',
|
|
357
|
+
'--search',
|
|
358
|
+
'review-requested:@me',
|
|
359
|
+
'--json',
|
|
360
|
+
'number,title,author,url',
|
|
361
|
+
'--limit',
|
|
362
|
+
'10',
|
|
363
|
+
], { encoding: 'utf-8', stdio: 'pipe', cwd });
|
|
364
|
+
if (result.status !== 0) {
|
|
365
|
+
failSpinner('Error al obtener PRs');
|
|
366
|
+
error(result.stderr?.trim() || 'Error desconocido');
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
succeedSpinner();
|
|
370
|
+
let prs = [];
|
|
371
|
+
try {
|
|
372
|
+
prs = JSON.parse(result.stdout);
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
error('No se pudo parsear la respuesta de gh.');
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
if (prs.length === 0) {
|
|
379
|
+
info('No hay PRs esperando tu review.');
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const choices = prs.map((pr) => `#${pr.number} ${pr.title} (by @${pr.author?.login ?? 'unknown'})`);
|
|
383
|
+
const picked = await selectPrompt('Selecciona un PR para revisar:', choices);
|
|
384
|
+
const idx = choices.indexOf(picked);
|
|
385
|
+
const pr = prs[idx];
|
|
386
|
+
if (!pr)
|
|
387
|
+
return;
|
|
388
|
+
blank();
|
|
389
|
+
section(`Diff del PR #${pr.number}`);
|
|
390
|
+
divider();
|
|
391
|
+
const diffResult = spawnSync('gh', ['pr', 'diff', String(pr.number)], {
|
|
392
|
+
encoding: 'utf-8',
|
|
393
|
+
stdio: 'pipe',
|
|
394
|
+
cwd,
|
|
395
|
+
});
|
|
396
|
+
if (diffResult.status === 0 && diffResult.stdout) {
|
|
397
|
+
const lines = diffResult.stdout.split('\n');
|
|
398
|
+
const preview = lines.slice(0, 50);
|
|
399
|
+
console.log(preview.join('\n'));
|
|
400
|
+
if (lines.length > 50) {
|
|
401
|
+
info(` … (${lines.length - 50} líneas más)`);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
info('No se pudo obtener el diff.');
|
|
406
|
+
}
|
|
407
|
+
divider();
|
|
408
|
+
blank();
|
|
409
|
+
const action = await selectPrompt('Acción:', [
|
|
410
|
+
'✅ Aprobar',
|
|
411
|
+
'💬 Comentar',
|
|
412
|
+
'🔄 Solicitar cambios',
|
|
413
|
+
'← Volver',
|
|
414
|
+
]);
|
|
415
|
+
if (action === '← Volver')
|
|
416
|
+
return;
|
|
417
|
+
let commentBody = '';
|
|
418
|
+
if (action !== '✅ Aprobar') {
|
|
419
|
+
commentBody = await inputPrompt('Comentario (opcional)', '');
|
|
420
|
+
}
|
|
421
|
+
let reviewFlag;
|
|
422
|
+
if (action === '✅ Aprobar')
|
|
423
|
+
reviewFlag = '--approve';
|
|
424
|
+
else if (action === '💬 Comentar')
|
|
425
|
+
reviewFlag = '--comment';
|
|
426
|
+
else
|
|
427
|
+
reviewFlag = '--request-changes';
|
|
428
|
+
const reviewArgs = ['pr', 'review', String(pr.number), reviewFlag];
|
|
429
|
+
if (commentBody) {
|
|
430
|
+
reviewArgs.push('-b', commentBody);
|
|
431
|
+
}
|
|
432
|
+
startSpinner('Enviando review...');
|
|
433
|
+
const reviewResult = spawnSync('gh', reviewArgs, { encoding: 'utf-8', stdio: 'pipe', cwd });
|
|
434
|
+
if (reviewResult.status !== 0) {
|
|
435
|
+
failSpinner('Error al enviar review');
|
|
436
|
+
error(reviewResult.stderr?.trim() || 'Error desconocido');
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
succeedSpinner('Review enviado correctamente');
|
|
440
|
+
}
|
|
441
|
+
// ── Ink (interactive TTY) PR description flow ───────────────────────────────
|
|
442
|
+
async function runInkPR() {
|
|
443
|
+
const React = (await import('react')).default;
|
|
444
|
+
const { Box, Text } = await import('ink');
|
|
445
|
+
const { Select, Spinner } = await import('@inkjs/ui');
|
|
446
|
+
const { renderInteractive } = await import('../ux/renderer.js');
|
|
447
|
+
const { theme } = await import('../ux/theme.js');
|
|
448
|
+
const { useState, useEffect } = await import('react');
|
|
449
|
+
const { useApp } = await import('ink');
|
|
450
|
+
const cwd = process.cwd();
|
|
451
|
+
const config = getConfig();
|
|
452
|
+
const convention = await detectConvention(cwd);
|
|
453
|
+
const branch = getCurrentBranch(cwd);
|
|
454
|
+
const repoName = getRepoName(cwd);
|
|
455
|
+
const ticket = extractTicketFromBranch(branch, config.commit.ticketPattern);
|
|
456
|
+
const base = config.git.defaultBaseBranches[0] ?? 'main';
|
|
457
|
+
const commits = getCommitsSinceBase(base, cwd);
|
|
458
|
+
const staged = getStagedFiles(cwd);
|
|
459
|
+
const templatePaths = [
|
|
460
|
+
join(cwd, '.github/pull_request_template.md'),
|
|
461
|
+
join(cwd, '.github/PULL_REQUEST_TEMPLATE.md'),
|
|
462
|
+
];
|
|
463
|
+
const templateContent = templatePaths.reduce((found, p) => {
|
|
464
|
+
if (found)
|
|
465
|
+
return found;
|
|
466
|
+
return existsSync(p) ? readFileSync(p, 'utf-8') : null;
|
|
467
|
+
}, null) ?? DEFAULT_TEMPLATE;
|
|
468
|
+
const aiContext = buildAIContext({
|
|
469
|
+
repoName,
|
|
470
|
+
branch,
|
|
471
|
+
ticket,
|
|
472
|
+
convention,
|
|
473
|
+
stagedFiles: staged,
|
|
474
|
+
allowRawDiff: config.ai.allowRawDiff,
|
|
475
|
+
});
|
|
476
|
+
const postAction = {
|
|
477
|
+
title: '',
|
|
478
|
+
body: '',
|
|
479
|
+
create: false,
|
|
480
|
+
};
|
|
481
|
+
function PRFlow({ onDone }) {
|
|
482
|
+
const { exit } = useApp();
|
|
483
|
+
const [phase, setPhase] = useState('generating');
|
|
484
|
+
const [title, setTitle] = useState('');
|
|
485
|
+
const [body, setBody] = useState('');
|
|
486
|
+
const [active, setActive] = useState(false);
|
|
487
|
+
useEffect(() => {
|
|
488
|
+
const t = setTimeout(() => setActive(true), 120);
|
|
489
|
+
return () => clearTimeout(t);
|
|
490
|
+
}, []);
|
|
491
|
+
const finish = () => {
|
|
492
|
+
exit();
|
|
493
|
+
onDone();
|
|
494
|
+
};
|
|
495
|
+
useEffect(() => {
|
|
496
|
+
if (phase !== 'generating')
|
|
497
|
+
return;
|
|
498
|
+
let cancelled = false;
|
|
499
|
+
void (async () => {
|
|
500
|
+
try {
|
|
501
|
+
const provider = await createProviderWithFallback(config);
|
|
502
|
+
const proposal = await provider.generatePRDescription(aiContext);
|
|
503
|
+
if (!cancelled) {
|
|
504
|
+
setTitle(proposal.title);
|
|
505
|
+
setBody(proposal.body);
|
|
506
|
+
setPhase('result');
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
catch {
|
|
510
|
+
if (!cancelled) {
|
|
511
|
+
setTitle(branch);
|
|
512
|
+
setBody(templateContent);
|
|
513
|
+
setPhase('result');
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
})();
|
|
517
|
+
return () => {
|
|
518
|
+
cancelled = true;
|
|
519
|
+
};
|
|
520
|
+
}, [phase]);
|
|
521
|
+
const width = Math.min(process.stdout.columns ?? 80, 78);
|
|
522
|
+
if (phase === 'generating') {
|
|
523
|
+
return React.createElement(Box, { paddingX: 1, flexDirection: 'column' }, React.createElement(Spinner, {
|
|
524
|
+
label: `Analizando ${commits.length} commits desde ${base}...`,
|
|
525
|
+
}));
|
|
526
|
+
}
|
|
527
|
+
const options = [
|
|
528
|
+
{ label: '📋 Copiar título al portapapeles', value: 'copy-title' },
|
|
529
|
+
{ label: '📋 Copiar descripción completa', value: 'copy-body' },
|
|
530
|
+
{ label: '💾 Guardar en PULL_REQUEST.md', value: 'save' },
|
|
531
|
+
{ label: '🚀 Crear PR en GitHub', value: 'create-gh' },
|
|
532
|
+
{ label: '🔄 Regenerar', value: 'regenerate' },
|
|
533
|
+
{ label: '✖ Salir', value: 'exit' },
|
|
534
|
+
];
|
|
535
|
+
return React.createElement(Box, { flexDirection: 'column', paddingX: 1, width }, React.createElement(Box, {
|
|
536
|
+
flexDirection: 'column',
|
|
537
|
+
borderStyle: 'round',
|
|
538
|
+
borderColor: theme.accent,
|
|
539
|
+
paddingX: 1,
|
|
540
|
+
marginBottom: 1,
|
|
541
|
+
}, React.createElement(Text, { color: theme.muted }, 'Título del PR'), React.createElement(Text, { bold: true, color: 'white' }, title)), React.createElement(Box, {
|
|
542
|
+
flexDirection: 'column',
|
|
543
|
+
borderStyle: 'round',
|
|
544
|
+
borderColor: theme.border,
|
|
545
|
+
paddingX: 1,
|
|
546
|
+
marginBottom: 1,
|
|
547
|
+
width: width - 2,
|
|
548
|
+
}, React.createElement(Text, { color: theme.muted }, 'Descripción'), React.createElement(Text, null), ...body
|
|
549
|
+
.split('\n')
|
|
550
|
+
.slice(0, 15)
|
|
551
|
+
.map((line, i) => React.createElement(Text, { key: i, color: '#d1d5db' }, line)), body.split('\n').length > 15
|
|
552
|
+
? React.createElement(Text, { color: theme.muted }, ` … (${body.split('\n').length - 15} líneas más)`)
|
|
553
|
+
: null), React.createElement(Text, { color: theme.muted }, ` ${commits.length} commits · ${staged.length} archivos · ${config.ai.provider}`), React.createElement(Text, null), React.createElement(Select, {
|
|
554
|
+
isDisabled: !active,
|
|
555
|
+
options,
|
|
556
|
+
onChange: (val) => {
|
|
557
|
+
if (val === 'exit') {
|
|
558
|
+
finish();
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
if (val === 'regenerate') {
|
|
562
|
+
setPhase('generating');
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
if (val === 'create-gh') {
|
|
566
|
+
postAction.title = title;
|
|
567
|
+
postAction.body = body;
|
|
568
|
+
postAction.create = true;
|
|
569
|
+
finish();
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
const fullContent = `# ${title}\n\n${body}`;
|
|
573
|
+
void (async () => {
|
|
574
|
+
try {
|
|
575
|
+
if (val === 'copy-title' || val === 'copy-body') {
|
|
576
|
+
const { default: clipboardy } = await import('clipboardy');
|
|
577
|
+
await clipboardy.write(val === 'copy-title' ? title : fullContent);
|
|
578
|
+
success('Copiado al portapapeles.');
|
|
579
|
+
}
|
|
580
|
+
else if (val === 'save') {
|
|
581
|
+
writeFileSync(join(cwd, 'PULL_REQUEST.md'), fullContent, 'utf-8');
|
|
582
|
+
success('Guardado en PULL_REQUEST.md');
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
catch {
|
|
586
|
+
error('Operación fallida.');
|
|
587
|
+
}
|
|
588
|
+
finish();
|
|
589
|
+
})();
|
|
590
|
+
},
|
|
591
|
+
}));
|
|
592
|
+
}
|
|
593
|
+
await renderInteractive((resolve) => React.createElement(PRFlow, { onDone: resolve }));
|
|
594
|
+
if (postAction.create) {
|
|
595
|
+
await runCreatePROnGitHub(postAction.title, postAction.body);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
// ── Plain (CI / non-TTY) PR description flow ────────────────────────────────
|
|
599
|
+
async function runPlainPR() {
|
|
600
|
+
const cwd = process.cwd();
|
|
601
|
+
const config = getConfig();
|
|
602
|
+
const convention = await detectConvention(cwd);
|
|
603
|
+
const branch = getCurrentBranch(cwd);
|
|
604
|
+
const repoName = getRepoName(cwd);
|
|
605
|
+
const ticket = extractTicketFromBranch(branch, config.commit.ticketPattern);
|
|
606
|
+
let base = config.git.defaultBaseBranches[0] ?? 'main';
|
|
607
|
+
const basePick = await selectPrompt(`Base branch for PR (current: ${base}):`, [
|
|
608
|
+
...config.git.defaultBaseBranches,
|
|
609
|
+
'Enter manually',
|
|
610
|
+
]);
|
|
611
|
+
if (basePick === 'Enter manually') {
|
|
612
|
+
base = await inputPrompt('Base branch');
|
|
613
|
+
}
|
|
614
|
+
else {
|
|
615
|
+
base = basePick;
|
|
616
|
+
}
|
|
617
|
+
const commits = getCommitsSinceBase(base, cwd);
|
|
618
|
+
const staged = getStagedFiles(cwd);
|
|
619
|
+
const templatePaths = [
|
|
620
|
+
join(cwd, '.github/pull_request_template.md'),
|
|
621
|
+
join(cwd, '.github/PULL_REQUEST_TEMPLATE.md'),
|
|
622
|
+
join(cwd, '.gitlab/merge_request_templates/Default.md'),
|
|
623
|
+
];
|
|
624
|
+
const templateContent = templatePaths.reduce((found, p) => {
|
|
625
|
+
if (found)
|
|
626
|
+
return found;
|
|
627
|
+
return existsSync(p) ? readFileSync(p, 'utf-8') : null;
|
|
628
|
+
}, null) ?? DEFAULT_TEMPLATE;
|
|
629
|
+
const aiContext = buildAIContext({
|
|
630
|
+
repoName,
|
|
631
|
+
branch,
|
|
632
|
+
ticket,
|
|
633
|
+
convention,
|
|
634
|
+
stagedFiles: staged,
|
|
635
|
+
allowRawDiff: config.ai.allowRawDiff,
|
|
636
|
+
});
|
|
637
|
+
const provider = await createProviderWithFallback(config);
|
|
638
|
+
startSpinner(`Generating PR description with ${provider.name}...`);
|
|
639
|
+
let proposal;
|
|
640
|
+
try {
|
|
641
|
+
proposal = await provider.generatePRDescription(aiContext);
|
|
642
|
+
succeedSpinner();
|
|
643
|
+
}
|
|
644
|
+
catch {
|
|
645
|
+
failSpinner();
|
|
646
|
+
proposal = { title: branch, body: templateContent };
|
|
647
|
+
}
|
|
648
|
+
section('PR Title');
|
|
649
|
+
console.log(`\n ${proposal.title}\n`);
|
|
650
|
+
section('PR Body');
|
|
651
|
+
console.log('\n' + proposal.body);
|
|
652
|
+
blank();
|
|
653
|
+
keyValue('Commits since base', String(commits.length));
|
|
654
|
+
keyValue('Provider', provider.name);
|
|
655
|
+
blank();
|
|
656
|
+
const action = await selectPrompt('What do you want to do?', [
|
|
657
|
+
'Copy to clipboard',
|
|
658
|
+
'Save to pr-description.md',
|
|
659
|
+
'Print to terminal',
|
|
660
|
+
'Create PR on GitHub',
|
|
661
|
+
'Done',
|
|
662
|
+
]);
|
|
663
|
+
const fullContent = `# ${proposal.title}\n\n${proposal.body}`;
|
|
664
|
+
if (action === 'Copy to clipboard') {
|
|
665
|
+
try {
|
|
666
|
+
const { default: clipboardy } = await import('clipboardy');
|
|
667
|
+
await clipboardy.write(fullContent);
|
|
668
|
+
success('Copied to clipboard.');
|
|
669
|
+
}
|
|
670
|
+
catch {
|
|
671
|
+
error('Failed to copy to clipboard.');
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
else if (action === 'Save to pr-description.md') {
|
|
675
|
+
writeFileSync(join(cwd, 'pr-description.md'), fullContent, 'utf-8');
|
|
676
|
+
success('Saved to pr-description.md');
|
|
677
|
+
}
|
|
678
|
+
else if (action === 'Print to terminal') {
|
|
679
|
+
console.log('\n' + fullContent);
|
|
680
|
+
}
|
|
681
|
+
else if (action === 'Create PR on GitHub') {
|
|
682
|
+
await runCreatePROnGitHub(proposal.title, proposal.body);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
// ── Main entry point ─────────────────────────────────────────────────────────
|
|
686
|
+
export async function runPR() {
|
|
687
|
+
const cwd = process.cwd();
|
|
688
|
+
if (!(await ensureGitRepo(cwd)))
|
|
689
|
+
return;
|
|
690
|
+
if (isCI()) {
|
|
691
|
+
await runPlainPR();
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
const choice = await selectPrompt('gsf pr — ¿Qué quieres hacer?', [
|
|
695
|
+
'Generar descripción del PR (IA)',
|
|
696
|
+
'Crear PR en GitHub',
|
|
697
|
+
'Ver estado de PRs',
|
|
698
|
+
'Monitorizar CI checks',
|
|
699
|
+
'Mergear PR en GitHub',
|
|
700
|
+
'Checkout de PR',
|
|
701
|
+
'Review de PR',
|
|
702
|
+
'Salir',
|
|
703
|
+
]);
|
|
704
|
+
switch (choice) {
|
|
705
|
+
case 'Generar descripción del PR (IA)':
|
|
706
|
+
await runInkPR();
|
|
707
|
+
break;
|
|
708
|
+
case 'Crear PR en GitHub':
|
|
709
|
+
await runCreatePROnGitHub('', '');
|
|
710
|
+
break;
|
|
711
|
+
case 'Ver estado de PRs':
|
|
712
|
+
runPRStatus();
|
|
713
|
+
break;
|
|
714
|
+
case 'Monitorizar CI checks':
|
|
715
|
+
await runPRChecks();
|
|
716
|
+
break;
|
|
717
|
+
case 'Mergear PR en GitHub':
|
|
718
|
+
await runPRMerge();
|
|
719
|
+
break;
|
|
720
|
+
case 'Checkout de PR':
|
|
721
|
+
await runPRCheckout();
|
|
722
|
+
break;
|
|
723
|
+
case 'Review de PR':
|
|
724
|
+
await runPRReview();
|
|
725
|
+
break;
|
|
726
|
+
case 'Salir':
|
|
727
|
+
default:
|
|
728
|
+
break;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
//# sourceMappingURL=pr.js.map
|