@asaro/git-cli 1.0.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/README.md +189 -0
- package/dist/adapters/git/SimpleGitAdapter.js +126 -0
- package/dist/adapters/github/GitHubAPIAdapter.js +243 -0
- package/dist/adapters/storage/FileSecureStorageAdapter.js +67 -0
- package/dist/adapters/ui/ConsoleUIAdapter.js +91 -0
- package/dist/commands/BaseCommand.js +11 -0
- package/dist/commands/BranchesCommand.js +152 -0
- package/dist/commands/Command.js +1 -0
- package/dist/commands/CommitCommand.js +85 -0
- package/dist/commands/IntegrationCommand.js +151 -0
- package/dist/commands/LogCommand.js +42 -0
- package/dist/commands/PullCommand.js +55 -0
- package/dist/commands/PushCommand.js +227 -0
- package/dist/commands/RepositoryCommand.js +268 -0
- package/dist/commands/StashCommand.js +140 -0
- package/dist/commands/StatusCommand.js +66 -0
- package/dist/commands/github/BaseGitHubCommand.js +17 -0
- package/dist/commands/github/GitHubAuthCommand.js +48 -0
- package/dist/commands/github/GitHubMenuCommand.js +57 -0
- package/dist/commands/github/GitHubReposCommand.js +210 -0
- package/dist/commands/github/GitHubWorkflowsCommand.js +243 -0
- package/dist/commit.js +98 -0
- package/dist/core/errors/GitError.js +19 -0
- package/dist/core/errors/GitHubError.js +36 -0
- package/dist/core/ports/GitHubPort.js +1 -0
- package/dist/core/ports/GitPort.js +1 -0
- package/dist/core/ports/SecureStoragePort.js +1 -0
- package/dist/core/ports/UIPort.js +1 -0
- package/dist/core/services/GitHubService.js +95 -0
- package/dist/core/services/GitService.js +44 -0
- package/dist/estado.js +97 -0
- package/dist/factories/CommandFactory.js +51 -0
- package/dist/index.js +71 -0
- package/dist/integracion.js +152 -0
- package/dist/ramas.js +178 -0
- package/dist/remoto.js +129 -0
- package/dist/repositorio.js +159 -0
- package/dist/stash.js +169 -0
- package/dist/utils.js +49 -0
- package/package.json +53 -0
package/dist/commit.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { git, c, separador, verificarRepo, spinner } from "./utils.js";
|
|
2
|
+
import { checkbox, input, select, confirm } from "@inquirer/prompts";
|
|
3
|
+
// ── Commit guiado (add + commit) ─────────────────────────────────────────────
|
|
4
|
+
export async function hacerCommit() {
|
|
5
|
+
if (!(await verificarRepo()))
|
|
6
|
+
return;
|
|
7
|
+
const spin = spinner("Leyendo estado del repositorio...");
|
|
8
|
+
const status = await git.status();
|
|
9
|
+
spin.stop();
|
|
10
|
+
// Armar lista de archivos disponibles para stagear
|
|
11
|
+
const disponibles = [
|
|
12
|
+
...status.modified.map(f => ({ name: c.aviso(` ~ ${f}`) + c.tenue(" (modificado)"), value: f })),
|
|
13
|
+
...status.not_added.map(f => ({ name: c.tenue(` ? ${f}`) + c.tenue(" (nuevo)"), value: f })),
|
|
14
|
+
...status.deleted.map(f => ({ name: c.error(` - ${f}`) + c.tenue(" (eliminado)"), value: f })),
|
|
15
|
+
];
|
|
16
|
+
// Si ya hay staged, ofrecer usarlos o agregar más
|
|
17
|
+
let archivosParaStage = [];
|
|
18
|
+
if (status.staged.length > 0) {
|
|
19
|
+
console.log();
|
|
20
|
+
console.log(c.info(" Ya tienes archivos preparados (staged):"));
|
|
21
|
+
status.staged.forEach(f => console.log(c.exito(` + ${f}`)));
|
|
22
|
+
console.log();
|
|
23
|
+
const usarExistentes = await confirm({
|
|
24
|
+
message: "¿Usar los archivos ya preparados sin agregar más?",
|
|
25
|
+
default: true,
|
|
26
|
+
});
|
|
27
|
+
if (!usarExistentes && disponibles.length > 0) {
|
|
28
|
+
archivosParaStage = await checkbox({
|
|
29
|
+
message: "Selecciona archivos adicionales para incluir:",
|
|
30
|
+
choices: disponibles,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
if (disponibles.length === 0) {
|
|
36
|
+
console.log(c.aviso("\n No hay cambios para hacer commit.\n"));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
// Opción de agregar todo o seleccionar
|
|
40
|
+
const modo = await select({
|
|
41
|
+
message: "¿Qué archivos incluir en el commit?",
|
|
42
|
+
choices: [
|
|
43
|
+
{ name: "Todos los cambios (git add .)", value: "todos" },
|
|
44
|
+
{ name: "Seleccionar manualmente", value: "manual" },
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
if (modo === "todos") {
|
|
48
|
+
archivosParaStage = ["."];
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
archivosParaStage = await checkbox({
|
|
52
|
+
message: "Selecciona los archivos (espacio para marcar):",
|
|
53
|
+
choices: disponibles,
|
|
54
|
+
validate: (val) => val.length > 0 || "Debes seleccionar al menos uno.",
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
const spinAdd = spinner("Agregando archivos...");
|
|
58
|
+
await git.add(archivosParaStage);
|
|
59
|
+
spinAdd.stop();
|
|
60
|
+
console.log(c.exito(" ✓ Archivos agregados al stage"));
|
|
61
|
+
}
|
|
62
|
+
// Mensaje de commit
|
|
63
|
+
console.log();
|
|
64
|
+
separador();
|
|
65
|
+
const tipo = await select({
|
|
66
|
+
message: "Tipo de commit:",
|
|
67
|
+
choices: [
|
|
68
|
+
{ name: "feat · Nueva funcionalidad", value: "feat" },
|
|
69
|
+
{ name: "fix · Corrección de bug", value: "fix" },
|
|
70
|
+
{ name: "docs · Documentación", value: "docs" },
|
|
71
|
+
{ name: "style · Estilos / formato", value: "style" },
|
|
72
|
+
{ name: "refactor · Refactorización", value: "refactor" },
|
|
73
|
+
{ name: "test · Tests", value: "test" },
|
|
74
|
+
{ name: "chore · Tareas de mantenimiento", value: "chore" },
|
|
75
|
+
{ name: "build · Sistema de build", value: "build" },
|
|
76
|
+
{ name: "ci · CI/CD", value: "ci" },
|
|
77
|
+
{ name: "Ninguno · Mensaje libre", value: "" },
|
|
78
|
+
],
|
|
79
|
+
});
|
|
80
|
+
const descripcion = await input({
|
|
81
|
+
message: "Descripción del commit:",
|
|
82
|
+
validate: (v) => v.trim().length > 0 || "El mensaje no puede estar vacío.",
|
|
83
|
+
});
|
|
84
|
+
const mensaje = tipo ? `${tipo}: ${descripcion.trim()}` : descripcion.trim();
|
|
85
|
+
// Confirmar
|
|
86
|
+
separador();
|
|
87
|
+
console.log(c.titulo(` Commit: `) + c.info(`"${mensaje}"`));
|
|
88
|
+
separador();
|
|
89
|
+
const confirmar = await confirm({ message: "¿Confirmar commit?", default: true });
|
|
90
|
+
if (!confirmar) {
|
|
91
|
+
console.log(c.aviso(" Commit cancelado.\n"));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const spinCommit = spinner("Realizando commit...");
|
|
95
|
+
await git.commit(mensaje);
|
|
96
|
+
spinCommit.stop();
|
|
97
|
+
console.log(c.exito(`\n ✓ Commit realizado: "${mensaje}"\n`));
|
|
98
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export class GitError extends Error {
|
|
2
|
+
constructor(message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = 'GitError';
|
|
5
|
+
Object.setPrototypeOf(this, GitError.prototype);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export class NotARepositoryError extends GitError {
|
|
9
|
+
constructor() {
|
|
10
|
+
super('Este directorio no es un repositorio Git');
|
|
11
|
+
this.name = 'NotARepositoryError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class NoChangesError extends GitError {
|
|
15
|
+
constructor() {
|
|
16
|
+
super('No hay cambios para realizar');
|
|
17
|
+
this.name = 'NoChangesError';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export class GitHubError extends Error {
|
|
2
|
+
constructor(message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = 'GitHubError';
|
|
5
|
+
Object.setPrototypeOf(this, GitHubError.prototype);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export class GitHubAuthError extends GitHubError {
|
|
9
|
+
constructor(message = 'Token de GitHub inválido o expirado') {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = 'GitHubAuthError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class GitHubRateLimitError extends GitHubError {
|
|
15
|
+
constructor(resetTime) {
|
|
16
|
+
let message = 'Límite de tasa de GitHub excedido';
|
|
17
|
+
if (resetTime) {
|
|
18
|
+
const resetDate = new Date(resetTime * 1000);
|
|
19
|
+
message += `. Se restablecerá en ${resetDate.toLocaleString('es-CL')}`;
|
|
20
|
+
}
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = 'GitHubRateLimitError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export class GitHubPermissionError extends GitHubError {
|
|
26
|
+
constructor(message = 'No tienes permisos para realizar esta operación') {
|
|
27
|
+
super(message);
|
|
28
|
+
this.name = 'GitHubPermissionError';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export class GitHubNotFoundError extends GitHubError {
|
|
32
|
+
constructor(resource = 'Recurso') {
|
|
33
|
+
super(`${resource} no encontrado`);
|
|
34
|
+
this.name = 'GitHubNotFoundError';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { GitHubAuthError } from '../errors/GitHubError.js';
|
|
2
|
+
const TOKEN_KEY = 'github_pat';
|
|
3
|
+
export class GitHubService {
|
|
4
|
+
gitHubPort;
|
|
5
|
+
storage;
|
|
6
|
+
currentUser = null;
|
|
7
|
+
constructor(gitHubPort, storage) {
|
|
8
|
+
this.gitHubPort = gitHubPort;
|
|
9
|
+
this.storage = storage;
|
|
10
|
+
}
|
|
11
|
+
async isAuthenticated() {
|
|
12
|
+
const tokenExists = await this.storage.exists(TOKEN_KEY);
|
|
13
|
+
if (!tokenExists)
|
|
14
|
+
return false;
|
|
15
|
+
try {
|
|
16
|
+
await this.loadAndAuthenticate();
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async login(token) {
|
|
24
|
+
await this.gitHubPort.authenticate(token);
|
|
25
|
+
await this.gitHubPort.validateToken();
|
|
26
|
+
await this.storage.save(TOKEN_KEY, token);
|
|
27
|
+
this.currentUser = await this.gitHubPort.getCurrentUser();
|
|
28
|
+
}
|
|
29
|
+
async logout() {
|
|
30
|
+
await this.storage.delete(TOKEN_KEY);
|
|
31
|
+
this.currentUser = null;
|
|
32
|
+
}
|
|
33
|
+
async getCurrentUser() {
|
|
34
|
+
if (!this.currentUser) {
|
|
35
|
+
await this.loadAndAuthenticate();
|
|
36
|
+
}
|
|
37
|
+
return this.currentUser;
|
|
38
|
+
}
|
|
39
|
+
async loadAndAuthenticate() {
|
|
40
|
+
const token = await this.storage.load(TOKEN_KEY);
|
|
41
|
+
if (!token)
|
|
42
|
+
throw new GitHubAuthError('No hay token guardado');
|
|
43
|
+
this.gitHubPort.authenticate(token);
|
|
44
|
+
this.currentUser = await this.gitHubPort.getCurrentUser();
|
|
45
|
+
}
|
|
46
|
+
async listRepos(filters) {
|
|
47
|
+
await this.ensureAuthenticated();
|
|
48
|
+
return this.gitHubPort.listRepos(filters);
|
|
49
|
+
}
|
|
50
|
+
async getRepo(owner, repo) {
|
|
51
|
+
await this.ensureAuthenticated();
|
|
52
|
+
return this.gitHubPort.getRepo(owner, repo);
|
|
53
|
+
}
|
|
54
|
+
async createRepo(options) {
|
|
55
|
+
await this.ensureAuthenticated();
|
|
56
|
+
return this.gitHubPort.createRepo(options);
|
|
57
|
+
}
|
|
58
|
+
async archiveRepo(owner, repo) {
|
|
59
|
+
await this.ensureAuthenticated();
|
|
60
|
+
return this.gitHubPort.archiveRepo(owner, repo);
|
|
61
|
+
}
|
|
62
|
+
async unarchiveRepo(owner, repo) {
|
|
63
|
+
await this.ensureAuthenticated();
|
|
64
|
+
return this.gitHubPort.unarchiveRepo(owner, repo);
|
|
65
|
+
}
|
|
66
|
+
async deleteRepo(owner, repo) {
|
|
67
|
+
await this.ensureAuthenticated();
|
|
68
|
+
return this.gitHubPort.deleteRepo(owner, repo);
|
|
69
|
+
}
|
|
70
|
+
async listWorkflows(owner, repo) {
|
|
71
|
+
await this.ensureAuthenticated();
|
|
72
|
+
return this.gitHubPort.listWorkflows(owner, repo);
|
|
73
|
+
}
|
|
74
|
+
async listWorkflowRuns(owner, repo, filters) {
|
|
75
|
+
await this.ensureAuthenticated();
|
|
76
|
+
return this.gitHubPort.listWorkflowRuns(owner, repo, filters);
|
|
77
|
+
}
|
|
78
|
+
async enableWorkflow(owner, repo, workflowId) {
|
|
79
|
+
await this.ensureAuthenticated();
|
|
80
|
+
return this.gitHubPort.enableWorkflow(owner, repo, workflowId);
|
|
81
|
+
}
|
|
82
|
+
async disableWorkflow(owner, repo, workflowId) {
|
|
83
|
+
await this.ensureAuthenticated();
|
|
84
|
+
return this.gitHubPort.disableWorkflow(owner, repo, workflowId);
|
|
85
|
+
}
|
|
86
|
+
async triggerWorkflowDispatch(owner, repo, workflowId, ref, inputs) {
|
|
87
|
+
await this.ensureAuthenticated();
|
|
88
|
+
return this.gitHubPort.triggerWorkflowDispatch(owner, repo, workflowId, ref, inputs);
|
|
89
|
+
}
|
|
90
|
+
async ensureAuthenticated() {
|
|
91
|
+
if (!this.currentUser) {
|
|
92
|
+
await this.loadAndAuthenticate();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { NotARepositoryError } from '../errors/GitError.js';
|
|
2
|
+
export class GitService {
|
|
3
|
+
gitPort;
|
|
4
|
+
constructor(gitPort) {
|
|
5
|
+
this.gitPort = gitPort;
|
|
6
|
+
}
|
|
7
|
+
async ensureIsRepository() {
|
|
8
|
+
const isRepo = await this.gitPort.isRepository();
|
|
9
|
+
if (!isRepo) {
|
|
10
|
+
throw new NotARepositoryError();
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
async getStatus() {
|
|
14
|
+
await this.ensureIsRepository();
|
|
15
|
+
return this.gitPort.getStatus();
|
|
16
|
+
}
|
|
17
|
+
async getLog(maxCount = 10) {
|
|
18
|
+
await this.ensureIsRepository();
|
|
19
|
+
return this.gitPort.getLog(maxCount);
|
|
20
|
+
}
|
|
21
|
+
async getBranches() {
|
|
22
|
+
await this.ensureIsRepository();
|
|
23
|
+
return this.gitPort.getBranches();
|
|
24
|
+
}
|
|
25
|
+
async commitChanges(files, message) {
|
|
26
|
+
await this.ensureIsRepository();
|
|
27
|
+
if (files.length > 0) {
|
|
28
|
+
await this.gitPort.add(files);
|
|
29
|
+
}
|
|
30
|
+
await this.gitPort.commit(message);
|
|
31
|
+
}
|
|
32
|
+
async push(remote = 'origin', branch, options) {
|
|
33
|
+
await this.ensureIsRepository();
|
|
34
|
+
await this.gitPort.push(remote, branch, options);
|
|
35
|
+
}
|
|
36
|
+
async pull(remote = 'origin', branch, useRebase = false) {
|
|
37
|
+
await this.ensureIsRepository();
|
|
38
|
+
await this.gitPort.pull(remote, branch, useRebase ? { '--rebase': 'true' } : undefined);
|
|
39
|
+
}
|
|
40
|
+
async getStashList() {
|
|
41
|
+
await this.ensureIsRepository();
|
|
42
|
+
return this.gitPort.stashList();
|
|
43
|
+
}
|
|
44
|
+
}
|
package/dist/estado.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { git, c, separador, verificarRepo, spinner } from "./utils.js";
|
|
2
|
+
import { select } from "@inquirer/prompts";
|
|
3
|
+
// ── Status detallado ─────────────────────────────────────────────────────────
|
|
4
|
+
export async function mostrarStatus() {
|
|
5
|
+
if (!(await verificarRepo()))
|
|
6
|
+
return;
|
|
7
|
+
const spin = spinner("Obteniendo estado del repositorio...");
|
|
8
|
+
const status = await git.status();
|
|
9
|
+
spin.stop();
|
|
10
|
+
const rama = status.current ?? "desconocida";
|
|
11
|
+
console.log();
|
|
12
|
+
console.log(c.titulo(" 📋 Estado del repositorio"));
|
|
13
|
+
separador();
|
|
14
|
+
console.log(` Rama actual: ${c.rama(rama)}`);
|
|
15
|
+
// Indicador de sincronía con remoto
|
|
16
|
+
if (status.ahead > 0 && status.behind > 0) {
|
|
17
|
+
console.log(c.aviso(` ⚡ ${status.ahead} commit(s) adelante y ${status.behind} atrás del remoto`));
|
|
18
|
+
}
|
|
19
|
+
else if (status.ahead > 0) {
|
|
20
|
+
console.log(c.aviso(` ↑ ${status.ahead} commit(s) por subir`));
|
|
21
|
+
}
|
|
22
|
+
else if (status.behind > 0) {
|
|
23
|
+
console.log(c.aviso(` ↓ ${status.behind} commit(s) por bajar`));
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
console.log(c.exito(" ✓ Sincronizado con el remoto"));
|
|
27
|
+
}
|
|
28
|
+
separador();
|
|
29
|
+
// Archivos staged
|
|
30
|
+
if (status.staged.length > 0) {
|
|
31
|
+
console.log(c.exito(" Preparados para commit (staged):"));
|
|
32
|
+
status.staged.forEach(f => console.log(c.exito(` + ${f}`)));
|
|
33
|
+
}
|
|
34
|
+
// Modificados sin stagear
|
|
35
|
+
if (status.modified.length > 0) {
|
|
36
|
+
console.log(c.aviso(" Modificados (sin stagear):"));
|
|
37
|
+
status.modified.forEach(f => console.log(c.aviso(` ~ ${f}`)));
|
|
38
|
+
}
|
|
39
|
+
// Sin seguimiento
|
|
40
|
+
if (status.not_added.length > 0) {
|
|
41
|
+
console.log(c.tenue(" Sin seguimiento (untracked):"));
|
|
42
|
+
status.not_added.forEach(f => console.log(c.tenue(` ? ${f}`)));
|
|
43
|
+
}
|
|
44
|
+
// Eliminados
|
|
45
|
+
if (status.deleted.length > 0) {
|
|
46
|
+
console.log(c.error(" Eliminados:"));
|
|
47
|
+
status.deleted.forEach(f => console.log(c.error(` - ${f}`)));
|
|
48
|
+
}
|
|
49
|
+
// Conflictos
|
|
50
|
+
if (status.conflicted.length > 0) {
|
|
51
|
+
console.log(c.error(" Con conflictos:"));
|
|
52
|
+
status.conflicted.forEach(f => console.log(c.error(` ✗ ${f}`)));
|
|
53
|
+
}
|
|
54
|
+
const total = status.staged.length +
|
|
55
|
+
status.modified.length +
|
|
56
|
+
status.not_added.length +
|
|
57
|
+
status.deleted.length;
|
|
58
|
+
if (total === 0) {
|
|
59
|
+
console.log(c.tenue(" Árbol de trabajo limpio."));
|
|
60
|
+
}
|
|
61
|
+
separador();
|
|
62
|
+
console.log();
|
|
63
|
+
}
|
|
64
|
+
// ── Log visual ───────────────────────────────────────────────────────────────
|
|
65
|
+
export async function mostrarLog() {
|
|
66
|
+
if (!(await verificarRepo()))
|
|
67
|
+
return;
|
|
68
|
+
const cantidad = await select({
|
|
69
|
+
message: "¿Cuántos commits mostrar?",
|
|
70
|
+
choices: [
|
|
71
|
+
{ name: "Últimos 10", value: 10 },
|
|
72
|
+
{ name: "Últimos 20", value: 20 },
|
|
73
|
+
{ name: "Últimos 50", value: 50 },
|
|
74
|
+
],
|
|
75
|
+
});
|
|
76
|
+
const spin = spinner("Cargando historial...");
|
|
77
|
+
const log = await git.log({ maxCount: cantidad });
|
|
78
|
+
spin.stop();
|
|
79
|
+
console.log();
|
|
80
|
+
console.log(c.titulo(` 📜 Últimos ${cantidad} commits`));
|
|
81
|
+
separador();
|
|
82
|
+
log.all.forEach((commit, i) => {
|
|
83
|
+
const esUltimo = i === 0;
|
|
84
|
+
const prefijo = esUltimo ? "◉" : "○";
|
|
85
|
+
const fecha = new Date(commit.date).toLocaleDateString("es-CL", {
|
|
86
|
+
day: "2-digit",
|
|
87
|
+
month: "short",
|
|
88
|
+
year: "numeric",
|
|
89
|
+
});
|
|
90
|
+
console.log(` ${c.hash(prefijo)} ${c.hash(commit.hash.slice(0, 7))}` +
|
|
91
|
+
` ${c.tenue(fecha)} ` +
|
|
92
|
+
`${c.titulo(commit.message.slice(0, 45))}` +
|
|
93
|
+
` ${c.tenue(commit.author_name)}`);
|
|
94
|
+
});
|
|
95
|
+
separador();
|
|
96
|
+
console.log();
|
|
97
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { GitHubService } from '../core/services/GitHubService.js';
|
|
2
|
+
import { StatusCommand } from '../commands/StatusCommand.js';
|
|
3
|
+
import { LogCommand } from '../commands/LogCommand.js';
|
|
4
|
+
import { CommitCommand } from '../commands/CommitCommand.js';
|
|
5
|
+
import { PushCommand } from '../commands/PushCommand.js';
|
|
6
|
+
import { PullCommand } from '../commands/PullCommand.js';
|
|
7
|
+
import { BranchesCommand } from '../commands/BranchesCommand.js';
|
|
8
|
+
import { IntegrationCommand } from '../commands/IntegrationCommand.js';
|
|
9
|
+
import { StashCommand } from '../commands/StashCommand.js';
|
|
10
|
+
import { RepositoryCommand } from '../commands/RepositoryCommand.js';
|
|
11
|
+
import { GitHubMenuCommand } from '../commands/github/GitHubMenuCommand.js';
|
|
12
|
+
export class CommandFactory {
|
|
13
|
+
gitPort;
|
|
14
|
+
uiPort;
|
|
15
|
+
gitHubPort;
|
|
16
|
+
storage;
|
|
17
|
+
gitHubService;
|
|
18
|
+
constructor(gitPort, uiPort, gitHubPort, storage) {
|
|
19
|
+
this.gitPort = gitPort;
|
|
20
|
+
this.uiPort = uiPort;
|
|
21
|
+
this.gitHubPort = gitHubPort;
|
|
22
|
+
this.storage = storage;
|
|
23
|
+
this.gitHubService = new GitHubService(gitHubPort, storage);
|
|
24
|
+
}
|
|
25
|
+
createCommand(type) {
|
|
26
|
+
switch (type) {
|
|
27
|
+
case 'status':
|
|
28
|
+
return new StatusCommand(this.gitPort, this.uiPort);
|
|
29
|
+
case 'log':
|
|
30
|
+
return new LogCommand(this.gitPort, this.uiPort);
|
|
31
|
+
case 'commit':
|
|
32
|
+
return new CommitCommand(this.gitPort, this.uiPort);
|
|
33
|
+
case 'push':
|
|
34
|
+
return new PushCommand(this.gitPort, this.uiPort, this.gitHubService);
|
|
35
|
+
case 'pull':
|
|
36
|
+
return new PullCommand(this.gitPort, this.uiPort);
|
|
37
|
+
case 'branches':
|
|
38
|
+
return new BranchesCommand(this.gitPort, this.uiPort);
|
|
39
|
+
case 'merge':
|
|
40
|
+
return new IntegrationCommand(this.gitPort, this.uiPort);
|
|
41
|
+
case 'stash':
|
|
42
|
+
return new StashCommand(this.gitPort, this.uiPort);
|
|
43
|
+
case 'repo':
|
|
44
|
+
return new RepositoryCommand(this.gitPort, this.uiPort, this.gitHubService);
|
|
45
|
+
case 'github':
|
|
46
|
+
return new GitHubMenuCommand(this.gitHubService, this.gitPort, this.uiPort);
|
|
47
|
+
default:
|
|
48
|
+
throw new Error(`Unknown command type: ${type}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { SimpleGitAdapter } from './adapters/git/SimpleGitAdapter.js';
|
|
3
|
+
import { ConsoleUIAdapter } from './adapters/ui/ConsoleUIAdapter.js';
|
|
4
|
+
import { GitHubAPIAdapter } from './adapters/github/GitHubAPIAdapter.js';
|
|
5
|
+
import { FileSecureStorageAdapter } from './adapters/storage/FileSecureStorageAdapter.js';
|
|
6
|
+
import { CommandFactory } from './factories/CommandFactory.js';
|
|
7
|
+
async function main() {
|
|
8
|
+
const gitPort = new SimpleGitAdapter();
|
|
9
|
+
const uiPort = new ConsoleUIAdapter();
|
|
10
|
+
const gitHubPort = new GitHubAPIAdapter();
|
|
11
|
+
const storage = new FileSecureStorageAdapter();
|
|
12
|
+
const commandFactory = new CommandFactory(gitPort, uiPort, gitHubPort, storage);
|
|
13
|
+
let continuar = true;
|
|
14
|
+
while (continuar) {
|
|
15
|
+
try {
|
|
16
|
+
uiPort.clear();
|
|
17
|
+
uiPort.showBanner();
|
|
18
|
+
await showContextInfo(gitPort, uiPort);
|
|
19
|
+
const opcion = await uiPort.select('¿Qué deseas hacer?', [
|
|
20
|
+
{ name: '📋 Estado del repositorio', value: 'status' },
|
|
21
|
+
{ name: '📜 Ver historial (log)', value: 'log' },
|
|
22
|
+
{ name: '─────────────────────────────', value: 'sep1', disabled: true },
|
|
23
|
+
{ name: '✅ Commit (add + commit)', value: 'commit' },
|
|
24
|
+
{ name: '↑ Push (subir cambios)', value: 'push' },
|
|
25
|
+
{ name: '↓ Pull (bajar cambios)', value: 'pull' },
|
|
26
|
+
{ name: '─────────────────────────────', value: 'sep2', disabled: true },
|
|
27
|
+
{ name: '🌿 Ramas (crear/cambiar/eliminar)', value: 'branches' },
|
|
28
|
+
{ name: '🔀 Merge / Rebase', value: 'merge' },
|
|
29
|
+
{ name: '📦 Stash (guardar/restaurar)', value: 'stash' },
|
|
30
|
+
{ name: '─────────────────────────────', value: 'sep3', disabled: true },
|
|
31
|
+
{ name: '🆕 Repositorio (clone/init/remoto)', value: 'repo' },
|
|
32
|
+
{ name: '─────────────────────────────', value: 'sep4', disabled: true },
|
|
33
|
+
{ name: '🌐 Gestión GitHub', value: 'github' },
|
|
34
|
+
{ name: '─────────────────────────────', value: 'sep5', disabled: true },
|
|
35
|
+
{ name: '🚪 Salir', value: 'salir' },
|
|
36
|
+
]);
|
|
37
|
+
if (opcion === 'salir') {
|
|
38
|
+
uiPort.log(uiPort.colors.tenue('\n Hasta pronto.\n'));
|
|
39
|
+
continuar = false;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
const command = commandFactory.createCommand(opcion);
|
|
43
|
+
await command.execute();
|
|
44
|
+
await uiPort.pause();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
if (err instanceof Error &&
|
|
49
|
+
(err.message.includes('User force closed') || err.name === 'ExitPromptError')) {
|
|
50
|
+
uiPort.log(uiPort.colors.tenue('\n\n Hasta pronto.\n'));
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function showContextInfo(gitPort, uiPort) {
|
|
58
|
+
try {
|
|
59
|
+
const status = await gitPort.getStatus();
|
|
60
|
+
const rama = status.current ?? '?';
|
|
61
|
+
const sucios = status.modified.length +
|
|
62
|
+
status.staged.length +
|
|
63
|
+
status.not_added.length +
|
|
64
|
+
status.deleted.length;
|
|
65
|
+
uiPort.showContextInfo(rama, status.ahead, status.behind, sucios);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
uiPort.separator();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
main().catch(console.error);
|