@orxataguy/tyr 1.0.16 → 1.0.18

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- Creative Commons Attribution 4.0 International (CC BY 4.0)
1
+ MIT License
2
2
 
3
3
  Copyright (c) 2026 Manel Andreu Pérez
4
4
 
5
- You are free to:
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
6
11
 
7
- Share — copy and redistribute the material in any medium or format
8
- Adapt — remix, transform, and build upon the material for any purpose,
9
- even commercially
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
10
14
 
11
- Under the following terms:
12
-
13
- Attribution You must give appropriate credit, provide a link to the
14
- license, and indicate if changes were made. You may do so
15
- in any reasonable manner, but not in any way that suggests
16
- the licensor endorses you or your use.
17
-
18
- No additional restrictions — You may not apply legal terms or technological
19
- measures that legally restrict others from doing anything the license permits.
20
-
21
- Full license text: https://creativecommons.org/licenses/by/4.0/legalcode
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  <p align="center">
12
12
  <a href="https://www.npmjs.com/package/@orxataguy/tyr"><img src="https://img.shields.io/npm/v/@orxataguy/tyr.svg" alt="npm version" /></a>
13
- <img src="https://img.shields.io/badge/license-CC%20BY%204.0-lightgrey.svg" alt="CC BY 4.0 License" />
13
+ <img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License" />
14
14
  <img src="https://img.shields.io/badge/node-%3E%3D18-brightgreen" alt="Node 18+" />
15
15
  <img src="https://img.shields.io/badge/TypeScript-5.x-blue" alt="TypeScript" />
16
16
  </p>
@@ -356,8 +356,4 @@ If you want your fork listed as a community distribution, open a PR adding it to
356
356
 
357
357
  ## License
358
358
 
359
- Tyr is licensed under [Creative Commons Attribution 4.0 International (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/).
360
-
361
- You are free to use, modify, and distribute this project — including for commercial purposes — as long as you give appropriate credit to the original author.
362
-
363
- **Author:** [Manel Andreu Pérez](https://github.com/orxataguy)
359
+ MIT [Manel Andreu Pérez](https://github.com/orxataguy)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orxataguy/tyr",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "tyr": "./bin/tyr.js"
@@ -39,7 +39,7 @@
39
39
  "access": "public"
40
40
  },
41
41
  "author": "Manel Andreu Pérez",
42
- "license": "CC-BY-4.0",
42
+ "license": "MIT",
43
43
  "description": "Tyr is a TypeScript-based environment that resolves this fragmentation through the creation, execution, and automation of CLI tools. Its architecture is built on dependency injection: the \"Kernel\" provides an execution context where \"Managers\" expose their functionality via an auto-generated API. Thanks to a code introspection system, the environment analyzes types and documentation in real-time, offering the programmer a ready-to-use catalog of tools. This completely decouples command logic from underlying libraries, allowing developers to invoke complex functions without needing to manage external packages or configurations.",
44
44
  "dependencies": {
45
45
  "@types/inquirer": "^9.0.9",
@@ -104,14 +104,14 @@ export class Kernel {
104
104
  const shell = this.container.get().shell;
105
105
  const gitDir = path.join(this.userRoot, '.git');
106
106
  if (!fs.existsSync(gitDir)) {
107
- console.log('~/.tyr no está vinculado a ningún repositorio git.');
108
- console.log('Ejecuta: tyr --config --repo <url> para vincularlo.');
107
+ console.log('~/.tyr is not linked to any git repository.');
108
+ console.log('Run: tyr --config --repo <url> to link it.');
109
109
  return;
110
110
  }
111
- console.log('Actualizando ~/.tyr desde el repositorio...');
111
+ console.log('Updating ~/.tyr from repository...');
112
112
  shell.cd(this.userRoot);
113
113
  await shell.exec('git pull');
114
- console.log('Actualización completada.');
114
+ console.log('Update complete.');
115
115
  return;
116
116
  }
117
117
 
@@ -120,13 +120,13 @@ export class Kernel {
120
120
  const pkgPath = path.resolve(this.frameworkRoot, 'package.json');
121
121
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
122
122
  const shell = this.container.get().shell;
123
- console.log(`Actualizando ${pkg.name}...`);
123
+ console.log(`Updating ${pkg.name}...`);
124
124
  await shell.exec(`npm update -g ${pkg.name}`);
125
- console.log('Actualización completada. Ejecuta tyr --version para confirmar.');
125
+ console.log('Update complete. Run tyr --version to confirm.');
126
126
  return;
127
127
  }
128
128
 
129
- // --help / -h: lista todos los comandos disponibles con su documentación
129
+ // --help / -h: lists all available commands with their documentation
130
130
  if (commandName === '--help' || commandName === '-h') {
131
131
  const helpContext = {
132
132
  ...this.container.get(),
@@ -64,7 +64,7 @@ const PACKAGE_JSON_TEMPLATE = `{
64
64
  "version": "1.0.0",
65
65
  "type": "module",
66
66
  "private": true,
67
- "description": "Comandos personalizados de Tyr (~/.tyr/)",
67
+ "description": "Custom Tyr commands (~/.tyr/)",
68
68
  "dependencies": {
69
69
  "@orxataguy/tyr": "latest"
70
70
  }
@@ -87,47 +87,47 @@ const TSCONFIG_TEMPLATE = `{
87
87
  `;
88
88
 
89
89
  const ENV_TEMPLATE = `# ~/.tyr/.env
90
- # Variables de entorno para Tyr. Este archivo nunca debe subirse a git.
90
+ # Environment variables for Tyr. This file must never be committed to git.
91
91
  #
92
- # Base de datos SQL Server
92
+ # SQL Server database
93
93
  MSSQL_USER=
94
94
  MSSQL_PASSWORD=
95
95
  MSSQL_SERVER=
96
96
  MSSQL_DATABASE=
97
- # Basde de datos de Mongo
97
+ # MongoDB database
98
98
  MONGO_URI=
99
99
  MONGO_DATABASE=
100
100
  `;
101
101
 
102
102
  const SH_ALIASES_TEMPLATE = `# ~/.tyr/aliases
103
- # Añade aquí tus aliases personalizados.
104
- # Este archivo se carga automáticamente por tu shell.
103
+ # Add your custom aliases here.
104
+ # This file is loaded automatically by your shell.
105
105
  #
106
- # Ejemplos:
106
+ # Examples:
107
107
  # alias gs='git status'
108
108
  # alias tyr-deploy='tyr deploy'
109
109
  `;
110
110
 
111
111
  const SH_PLUGINS_TEMPLATE = `# ~/.tyr/plugins
112
- # Añade aquí tus plugins de shell.
113
- # Compatible con zsh, bash y otros shells POSIX.
112
+ # Add your shell plugins here.
113
+ # Compatible with zsh, bash and other POSIX shells.
114
114
  #
115
- # Ejemplos (zsh):
115
+ # Examples (zsh):
116
116
  # source /usr/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
117
117
  `;
118
118
 
119
119
  const PS_ALIASES_TEMPLATE = `# ~/.tyr/aliases.ps1
120
- # Añade aquí tus aliases personalizados para PowerShell.
120
+ # Add your custom aliases for PowerShell here.
121
121
  #
122
- # Ejemplos:
122
+ # Examples:
123
123
  # Set-Alias gs git-status
124
124
  # function tyr-deploy { tyr deploy @args }
125
125
  `;
126
126
 
127
127
  const PS_PLUGINS_TEMPLATE = `# ~/.tyr/plugins.ps1
128
- # Añade aquí tus módulos y plugins de PowerShell.
128
+ # Add your PowerShell modules and plugins here.
129
129
  #
130
- # Ejemplos:
130
+ # Examples:
131
131
  # Import-Module posh-git
132
132
  # Import-Module PSReadLine
133
133
  `;
@@ -141,8 +141,8 @@ function makeTimestamp(): string {
141
141
  async function configureUnixShell(tyrFs: any, logger: any, homeDir: string, aliasesPath: string, pluginsPath: string): Promise<void> {
142
142
  const rcFile = detectShellRcFile(homeDir);
143
143
  if (!rcFile) {
144
- logger.warn('No se pudo detectar el archivo de configuración del shell.');
145
- logger.info(`Añade manualmente:\n source "${aliasesPath}"\n source "${pluginsPath}"`);
144
+ logger.warn('Could not detect shell configuration file.');
145
+ logger.info(`Add manually:\n source "${aliasesPath}"\n source "${pluginsPath}"`);
146
146
  return;
147
147
  }
148
148
  await tyrFs.ensureLine(rcFile, `source "${aliasesPath}"`);
@@ -156,14 +156,14 @@ async function configureWindowsShell(tyrFs: any, logger: any, aliasesPath: strin
156
156
  ? path.join(process.env.USERPROFILE, 'Documents', 'PowerShell', 'Microsoft.PowerShell_profile.ps1')
157
157
  : null;
158
158
  if (!psProfile) {
159
- logger.warn('No se pudo detectar el perfil de PowerShell.');
160
- logger.info(`Añade manualmente:\n . "${aliasesPath}"\n . "${pluginsPath}"`);
159
+ logger.warn('Could not detect PowerShell profile.');
160
+ logger.info(`Add manually:\n . "${aliasesPath}"\n . "${pluginsPath}"`);
161
161
  return;
162
162
  }
163
163
  await tyrFs.ensureLine(psProfile, `. "${aliasesPath}"`);
164
164
  await tyrFs.ensureLine(psProfile, `. "${pluginsPath}"`);
165
- logger.success(`Perfil de PowerShell configurado: ${psProfile}`);
166
- logger.info('Reinicia PowerShell para aplicar los cambios.');
165
+ logger.success(`PowerShell profile configured: ${psProfile}`);
166
+ logger.info('Restart PowerShell to apply the changes.');
167
167
  }
168
168
 
169
169
  export default function config({ logger, fs: tyrFs, frameworkRoot, shell }: TyrContext) {
@@ -178,8 +178,8 @@ export default function config({ logger, fs: tyrFs, frameworkRoot, shell }: TyrC
178
178
  const repoUrl = repoIndex !== -1 ? (args[repoIndex + 1] ?? null) : null;
179
179
 
180
180
  if (repoIndex !== -1 && (!repoUrl || repoUrl.startsWith('--'))) {
181
- logger.error('Falta la URL del repositorio.');
182
- logger.info('Uso: tyr --config --repo <url>');
181
+ logger.error('Repository URL is missing.');
182
+ logger.info('Usage: tyr --config --repo <url>');
183
183
  return;
184
184
  }
185
185
 
@@ -187,7 +187,7 @@ export default function config({ logger, fs: tyrFs, frameworkRoot, shell }: TyrC
187
187
  if (existsSync(userRoot)) {
188
188
  backupPath = `${userRoot}.bak.${makeTimestamp()}`;
189
189
  backupUserRoot(userRoot, backupPath);
190
- logger.warn(`Configuración anterior respaldada en: ${backupPath}`);
190
+ logger.warn(`Previous configuration backed up at: ${backupPath}`);
191
191
  }
192
192
 
193
193
  let repoHasContent = false;
@@ -197,7 +197,7 @@ export default function config({ logger, fs: tyrFs, frameworkRoot, shell }: TyrC
197
197
  try { removeDirRecursive(tempDir); } catch {}
198
198
  }
199
199
 
200
- logger.info(`\nClonando repositorio: ${repoUrl}`);
200
+ logger.info(`\nCloning repository: ${repoUrl}`);
201
201
  try {
202
202
  await shell.exec(`git clone "${repoUrl}" "${tempDir}"`);
203
203
  } catch (e) {
@@ -209,8 +209,8 @@ export default function config({ logger, fs: tyrFs, frameworkRoot, shell }: TyrC
209
209
 
210
210
  repoHasContent = existsSync(path.join(tempDir, 'map.yml'));
211
211
  logger.success(repoHasContent
212
- ? 'Repositorio clonado con configuración existente.'
213
- : 'Repositorio vacíoiniciando configuración por defecto...');
212
+ ? 'Repository cloned with existing configuration.'
213
+ : 'Empty repositorystarting default configuration...');
214
214
 
215
215
  clearDirExceptLogs(userRoot);
216
216
  copyDirContents(tempDir, userRoot);
@@ -218,64 +218,64 @@ export default function config({ logger, fs: tyrFs, frameworkRoot, shell }: TyrC
218
218
  }
219
219
 
220
220
  if (!repoHasContent) {
221
- logger.info('\nInicializando ~/.tyr...\n');
221
+ logger.info('\nInitializing ~/.tyr...\n');
222
222
 
223
223
  await tyrFs.createDir(path.join(userRoot, 'commands'));
224
- logger.success(`Directorio creado: ${path.join(userRoot, 'commands')}`);
224
+ logger.success(`Directory created: ${path.join(userRoot, 'commands')}`);
225
225
 
226
226
  const aliasesPath = path.join(userRoot, `aliases${ext}`);
227
227
  if (!tyrFs.exists(aliasesPath)) {
228
228
  await tyrFs.write(aliasesPath, isWindows ? PS_ALIASES_TEMPLATE : SH_ALIASES_TEMPLATE);
229
- logger.success(`Archivo creado: ${aliasesPath}`);
229
+ logger.success(`File created: ${aliasesPath}`);
230
230
  }
231
231
 
232
232
  const pluginsPath = path.join(userRoot, `plugins${ext}`);
233
233
  if (!tyrFs.exists(pluginsPath)) {
234
234
  await tyrFs.write(pluginsPath, isWindows ? PS_PLUGINS_TEMPLATE : SH_PLUGINS_TEMPLATE);
235
- logger.success(`Archivo creado: ${pluginsPath}`);
235
+ logger.success(`File created: ${pluginsPath}`);
236
236
  }
237
237
 
238
238
  const mapPath = path.join(userRoot, 'map.yml');
239
239
  await tyrFs.write(mapPath, 'commands: {}\n');
240
- logger.success(`Archivo creado: ${mapPath}`);
240
+ logger.success(`File created: ${mapPath}`);
241
241
 
242
242
  const envPath = path.join(userRoot, '.env');
243
243
  if (!tyrFs.exists(envPath)) {
244
244
  await tyrFs.write(envPath, ENV_TEMPLATE);
245
- logger.success(`Archivo creado: ${envPath}`);
245
+ logger.success(`File created: ${envPath}`);
246
246
  }
247
247
 
248
248
  const packageJsonPath = path.join(userRoot, 'package.json');
249
249
  if (!tyrFs.exists(packageJsonPath)) {
250
250
  await tyrFs.write(packageJsonPath, PACKAGE_JSON_TEMPLATE);
251
- logger.success(`Archivo creado: ${packageJsonPath}`);
251
+ logger.success(`File created: ${packageJsonPath}`);
252
252
  }
253
253
 
254
254
  const tsconfigPath = path.join(userRoot, 'tsconfig.json');
255
255
  if (!tyrFs.exists(tsconfigPath)) {
256
256
  await tyrFs.write(tsconfigPath, TSCONFIG_TEMPLATE);
257
- logger.success(`Archivo creado: ${tsconfigPath}`);
257
+ logger.success(`File created: ${tsconfigPath}`);
258
258
  }
259
259
 
260
- logger.info('\nInstalando dependencias de tipos en ~/.tyr...');
260
+ logger.info('\nInstalling type dependencies in ~/.tyr...');
261
261
  shell.cd(userRoot);
262
262
  try {
263
263
  await shell.exec('npm install');
264
- logger.success('Dependencias instaladas correctamente.');
264
+ logger.success('Dependencies installed successfully.');
265
265
  } catch {
266
- logger.warn('No se pudo ejecutar npm install en ~/.tyr. Hazlo manualmente.');
266
+ logger.warn('Could not run npm install in ~/.tyr. Run it manually.');
267
267
  }
268
268
 
269
269
  if (repoUrl) {
270
- logger.info('\nSubiendo configuración inicial al repositorio...');
270
+ logger.info('\nPushing initial configuration to repository...');
271
271
  shell.cd(userRoot);
272
272
  try {
273
273
  await shell.exec('git add .');
274
274
  await shell.exec('git commit -m "Initial tyr configuration"');
275
275
  await shell.exec('git push -u origin HEAD');
276
- logger.success('Configuración subida al repositorio.');
276
+ logger.success('Configuration pushed to repository.');
277
277
  } catch (e) {
278
- logger.warn('No se pudo hacer push automático. Hazlo manualmente desde ~/.tyr');
278
+ logger.warn('Could not push automatically. Do it manually from ~/.tyr');
279
279
  }
280
280
  }
281
281
  }
@@ -299,13 +299,13 @@ export default function config({ logger, fs: tyrFs, frameworkRoot, shell }: TyrC
299
299
  }
300
300
 
301
301
  if (needsInstall) {
302
- logger.info('\nInstalando dependencias de tipos en ~/.tyr...');
302
+ logger.info('\nInstalling type dependencies in ~/.tyr...');
303
303
  shell.cd(userRoot);
304
304
  try {
305
305
  await shell.exec('npm install');
306
- logger.success('Dependencias instaladas.');
306
+ logger.success('Dependencies installed.');
307
307
  } catch {
308
- logger.warn('No se pudo ejecutar npm install en ~/.tyr. Hazlo manualmente.');
308
+ logger.warn('Could not run npm install in ~/.tyr. Run it manually.');
309
309
  }
310
310
  }
311
311
 
@@ -321,11 +321,11 @@ export default function config({ logger, fs: tyrFs, frameworkRoot, shell }: TyrC
321
321
  }
322
322
  }
323
323
 
324
- logger.success('\nTyr configurado correctamente.');
325
- logger.info(`Directorio de configuración: ${userRoot}`);
326
- if (repoUrl) logger.info(`Repositorio vinculado: ${repoUrl}`);
327
- logger.info('\nPróximos pasos:');
328
- logger.info(' tyr gen <nombre> <archivo> Crear un nuevo comando');
329
- logger.info(' tyr doc Ver documentación de la API');
324
+ logger.success('\nTyr configured successfully.');
325
+ logger.info(`Configuration directory: ${userRoot}`);
326
+ if (repoUrl) logger.info(`Linked repository: ${repoUrl}`);
327
+ logger.info('\nNext steps:');
328
+ logger.info(' tyr gen <name> <file> Create a new command');
329
+ logger.info(' tyr doc View API documentation');
330
330
  };
331
331
  }
@@ -17,14 +17,14 @@ interface DocStructure {
17
17
 
18
18
  export default function doc({ logger, frameworkRoot, run }: TyrContext) {
19
19
  return async (args: string[]) => {
20
- logger.info("📚 Generando documentación del sistema (TS Mode)...");
20
+ logger.info("📚 Generating system documentation (TS Mode)...");
21
21
 
22
22
  const libPath = path.resolve(frameworkRoot, 'src/lib');
23
23
 
24
24
  const parseJSDoc = (filename: string, content: string): DocStructure => {
25
25
  const fileDoc: DocStructure = {
26
26
  name: filename,
27
- description: "Sin descripción.",
27
+ description: "No description.",
28
28
  methods: []
29
29
  };
30
30
 
@@ -81,7 +81,7 @@ export default function doc({ logger, frameworkRoot, run }: TyrContext) {
81
81
  description = descMatch[1].trim();
82
82
  } else {
83
83
  const textLines = cleanComment.split('\n').filter(l => !l.startsWith('@'));
84
- description = textLines.join(' ').trim() || "Sin descripción";
84
+ description = textLines.join(' ').trim() || "No description";
85
85
  }
86
86
 
87
87
  let example = null;
@@ -104,14 +104,14 @@ export default function doc({ logger, frameworkRoot, run }: TyrContext) {
104
104
  };
105
105
 
106
106
  if (!fs.existsSync(libPath)) {
107
- logger.error(`No se encuentra la carpeta de librerías: ${libPath}`);
107
+ logger.error(`Library folder not found: ${libPath}`);
108
108
  return;
109
109
  }
110
110
 
111
111
  const files = fs.readdirSync(libPath).filter(f => f.endsWith('.ts'));
112
112
 
113
113
  if (files.length === 0) {
114
- logger.warn("No se encontraron archivos .ts en /src/lib para documentar.");
114
+ logger.warn("No .ts files found in /src/lib to document.");
115
115
  }
116
116
 
117
117
  const fileDocs = files.map(file => {
@@ -120,44 +120,44 @@ export default function doc({ logger, frameworkRoot, run }: TyrContext) {
120
120
 
121
121
  const systemDocs: DocStructure = {
122
122
  name: 'TyrContext (Kernel)',
123
- description: 'Utilidades globales inyectadas en cada comando. Accesibles destructurando el contexto.',
123
+ description: 'Global utilities injected into every command. Accessible by destructuring the context.',
124
124
  methods: [
125
125
  {
126
126
  name: 'run',
127
- description: 'Ejecuta otro comando del sistema programáticamente (Composición de comandos). Útil para que un comando invoque a otros.',
127
+ description: 'Programmatically runs another system command (command composition). Useful for a command to invoke others.',
128
128
  example: `
129
- // Llama al comando 'test' pasándole argumentos adicionales
129
+ // Calls the 'test' command passing extra arguments
130
130
  const secret = "123";
131
131
  args.push(secret);
132
132
  await run('test', args);`.trim()
133
133
  },
134
134
  {
135
135
  name: 'task',
136
- description: 'Helper que envuelve una operación crítica. Si falla, el framework captura el error, añade contexto y lo muestra limpio en consola. Elimina la necesidad de try/catch manuales.',
136
+ description: 'Helper that wraps a critical operation. If it fails, the framework captures the error, adds context, and displays it cleanly in the console. Removes the need for manual try/catch.',
137
137
  example: `
138
- // Ejemplo: Tarea asíncrona que retorna un valor
139
- const buildId = await task('Compilando proyecto', async () => {
138
+ // Example: async task that returns a value
139
+ const buildId = await task('Building project', async () => {
140
140
  return await shell.exec('npm run build');
141
141
  });
142
142
 
143
- // Si falla, el log dirá: "Falló la tarea: Compilando proyecto"`.trim()
143
+ // If it fails, the log will say: "Task failed: Building project"`.trim()
144
144
  },
145
145
  {
146
146
  name: 'fail',
147
- description: 'Detiene la ejecución del comando inmediatamente lanzando un error controlado. Permite añadir una "sugerencia" para ayudar al usuario a solucionarlo.',
147
+ description: 'Stops command execution immediately by throwing a controlled error. Allows adding a "suggestion" to help the user resolve the issue.',
148
148
  example: `
149
- // Úsalo para validaciones lógicas
149
+ // Use it for logic validations
150
150
  if (!fs.existsSync('./package.json')) {
151
151
  fail(
152
- 'No se encuentra el archivo package de npm',
153
- 'Ejecuta "npm init -y" para generar uno.'
152
+ 'npm package file not found',
153
+ 'Run "npm init -y" to generate one.'
154
154
  );
155
155
  }`.trim()
156
156
  },
157
157
  {
158
158
  name: 'logger',
159
- description: 'Sistema de logs estandarizado con colores y formatos.',
160
- example: `logger.info('Iniciando...');\nlogger.success('Creado');\nlogger.warn('Cuidado');`
159
+ description: 'Standardised logging system with colours and formats.',
160
+ example: `logger.info('Starting...');\nlogger.success('Created');\nlogger.warn('Warning');`
161
161
  }
162
162
  ]
163
163
  };
@@ -192,7 +192,7 @@ if (!fs.existsSync('./package.json')) {
192
192
  </head>
193
193
  <body>
194
194
  <nav>
195
- <h3 style="color: #888; text-transform: uppercase; font-size: 0.8rem;">Módulos TS</h3>
195
+ <h3 style="color: #888; text-transform: uppercase; font-size: 0.8rem;">TS Modules</h3>
196
196
  ${docs.map(d => `<a href="#${d.name}">📦 ${d.name.replace('.ts', '')}</a>`).join('')}
197
197
  </nav>
198
198
  <main>
@@ -223,17 +223,17 @@ if (!fs.existsSync('./package.json')) {
223
223
  const { name, prompt } = JSON.parse(body);
224
224
  if (!name || !prompt) {
225
225
  res.writeHead(400, { 'Content-Type': 'application/json' });
226
- res.end(JSON.stringify({ success: false, message: 'Faltan campos obligatorios.' }));
226
+ res.end(JSON.stringify({ success: false, message: 'Missing required fields.' }));
227
227
  return;
228
228
  }
229
229
 
230
230
  await run('ai', [name, prompt]);
231
231
 
232
232
  res.writeHead(200, { 'Content-Type': 'application/json' });
233
- res.end(JSON.stringify({ success: true, message: `Comando '${name}' generado correctamente en ~/.tyr/commands/${name}.tyr.ts` }));
233
+ res.end(JSON.stringify({ success: true, message: `Command '${name}' generated successfully at ~/.tyr/commands/${name}.tyr.ts` }));
234
234
  } catch (e: any) {
235
235
  res.writeHead(500, { 'Content-Type': 'application/json' });
236
- res.end(JSON.stringify({ success: false, message: e.message || 'Error al generar el comando.' }));
236
+ res.end(JSON.stringify({ success: false, message: e.message || 'Error generating command.' }));
237
237
  }
238
238
  });
239
239
  return;
@@ -244,8 +244,8 @@ if (!fs.existsSync('./package.json')) {
244
244
  });
245
245
 
246
246
  server.listen(PORT, () => {
247
- logger.success(`Documentación TS lista en: http://localhost:${PORT}`);
248
- logger.info("Presiona Ctrl+C para detener.");
247
+ logger.success(`TS documentation ready at: http://localhost:${PORT}`);
248
+ logger.info("Press Ctrl+C to stop.");
249
249
  });
250
250
  };
251
251
  };
@@ -11,12 +11,12 @@ const template = `import type { TyrContext } from '@orxataguy/tyr';
11
11
 
12
12
  export default ({ run, task, fail, logger, shell, fs }: TyrContext) => {
13
13
  return async (args: string[]) => {
14
- logger.info("Ejecutando comando: %s");
14
+ logger.info("Running command: %s");
15
15
 
16
- // Tu lógica aquí...
17
- // Ejecuta "tyr doc" para ver la documentación de managers disponibles
16
+ // Your logic here...
17
+ // Run "tyr doc" to see the documentation for available managers
18
18
 
19
- logger.success("¡Comando %s finalizado!");
19
+ logger.success("Command %s finished!");
20
20
  };
21
21
  };
22
22
 
@@ -29,18 +29,18 @@ export default function gen({ logger, fs, userRoot }: TyrContext) {
29
29
  const fileName = args[1];
30
30
 
31
31
  if (!commandName || !fileName) {
32
- logger.error('Uso incorrecto.');
33
- logger.info('Sintaxis: tyr gen [nombre-comando] [nombre-archivo]');
32
+ logger.error('Incorrect usage.');
33
+ logger.info('Syntax: tyr gen [command-name] [file-name]');
34
34
  return;
35
35
  }
36
36
 
37
- logger.info(`Creando nuevo comando: '${commandName}' -> '${fileName}.tyr.ts'`);
37
+ logger.info(`Creating new command: '${commandName}' -> '${fileName}.tyr.ts'`);
38
38
 
39
39
  const commandsDir = path.join(userRoot, 'commands');
40
40
  const filePath = path.join(commandsDir, `${fileName}.tyr.ts`);
41
41
 
42
42
  if (fs.exists(filePath)) {
43
- logger.error(`El archivo ${fileName}.tyr.ts ya existe. Abortando.`);
43
+ logger.error(`File ${fileName}.tyr.ts already exists. Aborting.`);
44
44
  return;
45
45
  }
46
46
 
@@ -56,7 +56,7 @@ export default function gen({ logger, fs, userRoot }: TyrContext) {
56
56
  if (!config.commands) config.commands = {};
57
57
 
58
58
  if (config.commands[commandName]) {
59
- logger.warn(`El comando '${commandName}' ya existía. Actualizando ruta...`);
59
+ logger.warn(`Command '${commandName}' already existed. Updating path...`);
60
60
  }
61
61
 
62
62
  // Store path relative to userRoot so it remains portable
@@ -65,10 +65,10 @@ export default function gen({ logger, fs, userRoot }: TyrContext) {
65
65
  const newYaml = yaml.dump(config, { indent: 2, lineWidth: -1 });
66
66
  await fs.write(mapPath, newYaml);
67
67
 
68
- logger.success(`Comando '${commandName}' creado en ${filePath}`);
69
- logger.success(`Registrado en ${mapPath}`);
68
+ logger.success(`Command '${commandName}' created at ${filePath}`);
69
+ logger.success(`Registered in ${mapPath}`);
70
70
  } catch (e) {
71
- logger.error('Error al actualizar la configuración.');
71
+ logger.error('Error updating configuration.');
72
72
  console.error(e);
73
73
  }
74
74
  };
@@ -9,8 +9,8 @@ interface CommandDoc {
9
9
  }
10
10
 
11
11
  /**
12
- * Extrae el primer bloque JSDoc de un archivo .tyr.ts y lo parsea
13
- * en descripción y ejemplos de uso.
12
+ * Extracts the first JSDoc block from a .tyr.ts file and parses it
13
+ * into a description and usage examples.
14
14
  */
15
15
  function parseCommandDoc(filePath: string): CommandDoc {
16
16
  const fileName = path.basename(filePath, '.tyr.ts');
@@ -21,12 +21,12 @@ function parseCommandDoc(filePath: string): CommandDoc {
21
21
  return { name: fileName, description: '', usage: '' };
22
22
  }
23
23
 
24
- // Limpiar cada línea: eliminar el * inicial y espacios
24
+ // Clean each line: remove leading * and spaces
25
25
  const lines = match[1]
26
26
  .split('\n')
27
27
  .map(line => line.replace(/^\s*\*\s?/, '').trimEnd());
28
28
 
29
- // Separar en descripción y bloque "Uso:"
29
+ // Split into description and "Usage:" block
30
30
  const usoIndex = lines.findIndex(l => /^uso:/i.test(l.trim()));
31
31
 
32
32
  let description = '';
@@ -70,19 +70,19 @@ export default function help({ userRoot }: TyrContext) {
70
70
  const separator = `${gray} ${'─'.repeat(50)}${reset}`;
71
71
 
72
72
  console.log('');
73
- console.log(` ${bold}${cyan}tyr${reset} ${white}Comandos disponibles${reset}`);
73
+ console.log(` ${bold}${cyan}tyr${reset} ${white}Available commands${reset}`);
74
74
  console.log(separator);
75
75
  console.log('');
76
76
 
77
- // Flags y comandos built-in del framework
77
+ // Framework flags and built-in commands
78
78
  const builtins = [
79
- { name: '--help', description: 'Muestra este listado de comandos.', usage: 'tyr --help' },
80
- { name: '--version', description: 'Muestra la versión instalada de tyr.', usage: 'tyr --version' },
81
- { name: '--config', description: 'Configura tyr por primera vez.', usage: 'tyr --config' },
82
- { name: '--update', description: 'Actualiza ~/.tyr desde el repositorio git.', usage: 'tyr --update' },
83
- { name: '--upgrade', description: 'Actualiza el paquete npm de tyr.', usage: 'tyr --upgrade' },
84
- { name: 'gen', description: 'Genera un nuevo comando a partir de una descripción con IA.', usage: 'tyr gen <nombre> "<descripción>"' },
85
- { name: 'doc', description: 'Levanta la documentación del framework en el navegador.', usage: 'tyr doc' },
79
+ { name: '--help', description: 'Shows this command listing.', usage: 'tyr --help' },
80
+ { name: '--version', description: 'Shows the installed version of tyr.', usage: 'tyr --version' },
81
+ { name: '--config', description: 'Configures tyr for the first time.', usage: 'tyr --config' },
82
+ { name: '--update', description: 'Updates ~/.tyr from the git repository.', usage: 'tyr --update' },
83
+ { name: '--upgrade', description: 'Upgrades the tyr npm package.', usage: 'tyr --upgrade' },
84
+ { name: 'gen', description: 'Generates a new command from a description using AI.', usage: 'tyr gen <name> "<description>"' },
85
+ { name: 'doc', description: 'Opens the framework documentation in the browser.', usage: 'tyr doc' },
86
86
  ];
87
87
 
88
88
  console.log(` ${bold}${yellow}Framework${reset}`);
@@ -94,10 +94,10 @@ export default function help({ userRoot }: TyrContext) {
94
94
  console.log('');
95
95
  }
96
96
 
97
- // Comandos de usuario en ~/.tyr/commands/
97
+ // User commands in ~/.tyr/commands/
98
98
  if (!fs.existsSync(commandsDir)) {
99
99
  console.log(separator);
100
- console.log(` ${yellow}No se encontró la carpeta de comandos: ${commandsDir}${reset}`);
100
+ console.log(` ${yellow}Commands folder not found: ${commandsDir}${reset}`);
101
101
  console.log('');
102
102
  return;
103
103
  }
@@ -108,14 +108,14 @@ export default function help({ userRoot }: TyrContext) {
108
108
 
109
109
  if (files.length === 0) {
110
110
  console.log(separator);
111
- console.log(` ${dim}No hay comandos en ${commandsDir}${reset}`);
111
+ console.log(` ${dim}No commands in ${commandsDir}${reset}`);
112
112
  console.log('');
113
113
  return;
114
114
  }
115
115
 
116
116
  console.log(separator);
117
117
  console.log('');
118
- console.log(` ${bold}${yellow}Comandos de usuario${reset} ${gray}(~/.tyr/commands/)${reset}`);
118
+ console.log(` ${bold}${yellow}User commands${reset} ${gray}(~/.tyr/commands/)${reset}`);
119
119
  console.log('');
120
120
 
121
121
  for (const file of files) {
@@ -128,12 +128,12 @@ export default function help({ userRoot }: TyrContext) {
128
128
  console.log(` ${dim}${line}${reset}`);
129
129
  }
130
130
  } else {
131
- console.log(` ${gray}Sin descripción${reset}`);
131
+ console.log(` ${gray}No description${reset}`);
132
132
  }
133
133
 
134
134
  if (doc.usage) {
135
135
  console.log('');
136
- console.log(` ${gray} Uso:${reset}`);
136
+ console.log(` ${gray} Usage:${reset}`);
137
137
  for (const line of doc.usage.split('\n')) {
138
138
  console.log(` ${cyan} ${line}${reset}`);
139
139
  }
@@ -143,7 +143,7 @@ export default function help({ userRoot }: TyrContext) {
143
143
  }
144
144
 
145
145
  console.log(separator);
146
- console.log(` ${dim}Genera un comando nuevo con ${cyan}tyr gen <nombre> "<qué debe hacer>"${reset}`);
146
+ console.log(` ${dim}Generate a new command with ${cyan}tyr gen <name> "<what it should do>"${reset}`);
147
147
  console.log('');
148
148
  };
149
149
  }
@@ -12,25 +12,25 @@ export default function rem({ logger, fs, userRoot }: TyrContext) {
12
12
  const commandName = args[0];
13
13
 
14
14
  if (!commandName) {
15
- logger.error('Falta el nombre del comando a eliminar.');
15
+ logger.error('Missing command name to remove.');
16
16
  return;
17
17
  }
18
18
 
19
- logger.info(`Iniciando eliminación del comando: '${commandName}'`);
19
+ logger.info(`Starting removal of command: '${commandName}'`);
20
20
 
21
21
  const mapPath = path.join(userRoot, 'map.yml');
22
22
 
23
23
  try {
24
24
  const currentConfigRaw = await fs.read(mapPath);
25
25
  if (!currentConfigRaw) {
26
- logger.error(`No se encontró ${mapPath}. Ejecuta 'tyr --config' primero.`);
26
+ logger.error(`${mapPath} not found. Run 'tyr --config' first.`);
27
27
  return;
28
28
  }
29
29
 
30
30
  const config = yaml.load(currentConfigRaw) as TyrConfig;
31
31
 
32
32
  if (!config.commands?.[commandName]) {
33
- logger.error(`El comando '${commandName}' no existe en ~/.tyr/map.yml.`);
33
+ logger.error(`Command '${commandName}' does not exist in ~/.tyr/map.yml.`);
34
34
  return;
35
35
  }
36
36
 
@@ -44,7 +44,7 @@ export default function rem({ logger, fs, userRoot }: TyrContext) {
44
44
  for (const [alias, target] of Object.entries(config.aliases)) {
45
45
  if (target === commandName) {
46
46
  delete config.aliases[alias];
47
- logger.info(`Alias '${alias}' eliminado.`);
47
+ logger.info(`Alias '${alias}' removed.`);
48
48
  }
49
49
  }
50
50
  }
@@ -52,9 +52,9 @@ export default function rem({ logger, fs, userRoot }: TyrContext) {
52
52
  const newYaml = yaml.dump(config, { indent: 2, lineWidth: -1 });
53
53
  await fs.write(mapPath, newYaml);
54
54
 
55
- logger.success(`Comando '${commandName}' eliminado.`);
55
+ logger.success(`Command '${commandName}' removed.`);
56
56
  } catch (e) {
57
- logger.error('Error crítico durante la eliminación.');
57
+ logger.error('Critical error during removal.');
58
58
  console.error(e);
59
59
  }
60
60
  };
@@ -74,16 +74,16 @@ export class GitManager {
74
74
  * await git.cloneTo('git@github.com:org/repo.git', '/path/to/dest');
75
75
  */
76
76
  public async cloneTo(repoUrl: string, destDir: string): Promise<void> {
77
- this.logger.info(`Clonando ${repoUrl}...`);
78
- const loader = this.shell.showLoader('Clonando repositorio...');
77
+ this.logger.info(`Cloning ${repoUrl}...`);
78
+ const loader = this.shell.showLoader('Cloning repository...');
79
79
  try {
80
80
  await this.shell.exec(`git clone "${repoUrl}" "${destDir}"`);
81
81
  await this.shell.exec(`git -C "${destDir}" config --add core.filemode false`);
82
82
  loader.stop();
83
- this.logger.success('Clonación completada.');
83
+ this.logger.success('Cloning complete.');
84
84
  } catch (e) {
85
85
  loader.stop();
86
- throw new TyrError(`No se pudo clonar el repositorio: ${repoUrl}`, e, 'Comprueba que el repositorio existe y que tienes permisos para clonarlo.');
86
+ throw new TyrError(`Could not clone repository: ${repoUrl}`, e, 'Check that the repository exists and that you have permission to clone it.');
87
87
  }
88
88
  }
89
89
 
@@ -118,9 +118,9 @@ export class GitManager {
118
118
  await this.shell.exec(
119
119
  `cd "${dir}" && rm -rf .git && git init -b master && git remote add origin "${remoteUrl}" && git config --add core.filemode false && echo 'node_modules' >> .gitignore`
120
120
  );
121
- this.logger.success(`Repositorio Git inicializado en ${dir}`);
121
+ this.logger.success(`Git repository initialized at ${dir}`);
122
122
  } catch (e) {
123
- throw new TyrError(`No se pudo inicializar el repositorio en ${dir}`, e);
123
+ throw new TyrError(`Could not initialize git repository at ${dir}`, e);
124
124
  }
125
125
  }
126
126
  }
@@ -74,18 +74,18 @@ export class JiraManager {
74
74
  name: `${i.key} - ${i.summary} [${i.status}]`,
75
75
  value: i.key,
76
76
  })),
77
- { name: 'Sin ticket (introducir rama manualmente)', value: '__manual__' },
78
- { name: 'Omitir (no crear rama)', value: '__skip__' },
77
+ { name: 'No ticket (enter branch manually)', value: '__manual__' },
78
+ { name: 'Skip (do not create branch)', value: '__skip__' },
79
79
  ];
80
80
 
81
- const selected = await this.shell.select(choices, 'Selecciona un ticket de Jira:');
81
+ const selected = await this.shell.select(choices, 'Select a Jira ticket:');
82
82
 
83
83
  if (selected === '__skip__') return null;
84
84
  if (selected === '__manual__') return this.askForBranch();
85
85
 
86
86
  return selected;
87
87
  } catch {
88
- this.logger.warn('No se ha podido conectar con Jira. Introducción manual de rama.');
88
+ this.logger.warn('Could not connect to Jira. Manual branch entry.');
89
89
  }
90
90
  }
91
91
 
@@ -93,7 +93,7 @@ export class JiraManager {
93
93
  }
94
94
 
95
95
  private async askForBranch(): Promise<string | null> {
96
- const raw = await this.shell.input('Introduce el nombre de la nueva rama (vacío para omitir):');
96
+ const raw = await this.shell.input('Enter the new branch name (empty to skip):');
97
97
  if (!raw.trim()) return null;
98
98
  // Normalise: take the last segment separated by '/'
99
99
  return raw.trim().split('/').pop()?.toUpperCase() ?? raw.trim();
@@ -2,7 +2,7 @@ import { MongoClient, Db, Document, Filter, UpdateFilter, InsertOneResult, Inser
2
2
 
3
3
  /**
4
4
  * @class MongoManager
5
- * @description Conector con MongoDB que gestiona el ciclo de vida de la conexión y expone un CRUD genérico.
5
+ * @description MongoDB connector that manages the connection lifecycle and exposes a generic CRUD interface.
6
6
  */
7
7
  export class MongoManager {
8
8
  private client!: MongoClient;
@@ -32,10 +32,10 @@ export class MongoManager {
32
32
 
33
33
  /**
34
34
  * @method insertOne
35
- * @description Inserta un documento en la colección indicada.
36
- * @param {string} collection - Nombre de la colección.
37
- * @param {Document} document - Documento a insertar.
38
- * @returns {Promise<InsertOneResult>} Resultado de la inserción.
35
+ * @description Inserts a document into the specified collection.
36
+ * @param {string} collection - Collection name.
37
+ * @param {Document} document - Document to insert.
38
+ * @returns {Promise<InsertOneResult>} Insertion result.
39
39
  * @example
40
40
  * const result = await mongo.insertOne('users', { name: 'Ana', age: 30 });
41
41
  */
@@ -48,10 +48,10 @@ export class MongoManager {
48
48
 
49
49
  /**
50
50
  * @method insertMany
51
- * @description Inserta múltiples documentos en la colección indicada.
52
- * @param {string} collection - Nombre de la colección.
53
- * @param {Document[]} documents - Array de documentos a insertar.
54
- * @returns {Promise<InsertManyResult>} Resultado de la inserción.
51
+ * @description Inserts multiple documents into the specified collection.
52
+ * @param {string} collection - Collection name.
53
+ * @param {Document[]} documents - Array of documents to insert.
54
+ * @returns {Promise<InsertManyResult>} Insertion result.
55
55
  * @example
56
56
  * const result = await mongo.insertMany('users', [{ name: 'Ana' }, { name: 'Luis' }]);
57
57
  */
@@ -64,11 +64,11 @@ export class MongoManager {
64
64
 
65
65
  /**
66
66
  * @method findOne
67
- * @description Busca el primer documento que coincida con el filtro.
68
- * @param {string} collection - Nombre de la colección.
69
- * @param {Filter<Document>} filter - Filtro de búsqueda.
70
- * @param {FindOptions} [options] - Opciones adicionales (proyección, etc.).
71
- * @returns {Promise<WithId<T> | null>} El documento encontrado o null.
67
+ * @description Finds the first document matching the filter.
68
+ * @param {string} collection - Collection name.
69
+ * @param {Filter<Document>} filter - Search filter.
70
+ * @param {FindOptions} [options] - Additional options (projection, etc.).
71
+ * @returns {Promise<WithId<T> | null>} The matching document or null.
72
72
  * @example
73
73
  * const user = await mongo.findOne('users', { name: 'Ana' });
74
74
  */
@@ -81,11 +81,11 @@ export class MongoManager {
81
81
 
82
82
  /**
83
83
  * @method find
84
- * @description Busca todos los documentos que coincidan con el filtro.
85
- * @param {string} collection - Nombre de la colección.
86
- * @param {Filter<Document>} filter - Filtro de búsqueda. Usa {} para traer todos los documentos.
87
- * @param {FindOptions} [options] - Opciones adicionales (proyección, límite, etc.).
88
- * @returns {Promise<WithId<T>[]>} Array de documentos encontrados.
84
+ * @description Finds all documents matching the filter.
85
+ * @param {string} collection - Collection name.
86
+ * @param {Filter<Document>} filter - Search filter. Use {} to return all documents.
87
+ * @param {FindOptions} [options] - Additional options (projection, limit, etc.).
88
+ * @returns {Promise<WithId<T>[]>} Array of matching documents.
89
89
  * @example
90
90
  * const users = await mongo.find('users', { age: { $gte: 18 } });
91
91
  */
@@ -98,11 +98,11 @@ export class MongoManager {
98
98
 
99
99
  /**
100
100
  * @method updateOne
101
- * @description Actualiza el primer documento que coincida con el filtro.
102
- * @param {string} collection - Nombre de la colección.
103
- * @param {Filter<Document>} filter - Filtro para identificar el documento.
104
- * @param {UpdateFilter<Document>} update - Operación de actualización (ej: { $set: { field: value } }).
105
- * @returns {Promise<UpdateResult>} Resultado de la actualización.
101
+ * @description Updates the first document matching the filter.
102
+ * @param {string} collection - Collection name.
103
+ * @param {Filter<Document>} filter - Filter to identify the document.
104
+ * @param {UpdateFilter<Document>} update - Update operation (e.g. { $set: { field: value } }).
105
+ * @returns {Promise<UpdateResult>} Update result.
106
106
  * @example
107
107
  * const result = await mongo.updateOne('users', { name: 'Ana' }, { $set: { age: 31 } });
108
108
  */
@@ -115,11 +115,11 @@ export class MongoManager {
115
115
 
116
116
  /**
117
117
  * @method updateMany
118
- * @description Actualiza todos los documentos que coincidan con el filtro.
119
- * @param {string} collection - Nombre de la colección.
120
- * @param {Filter<Document>} filter - Filtro para identificar los documentos.
121
- * @param {UpdateFilter<Document>} update - Operación de actualización.
122
- * @returns {Promise<UpdateResult>} Resultado de la actualización.
118
+ * @description Updates all documents matching the filter.
119
+ * @param {string} collection - Collection name.
120
+ * @param {Filter<Document>} filter - Filter to identify the documents.
121
+ * @param {UpdateFilter<Document>} update - Update operation.
122
+ * @returns {Promise<UpdateResult>} Update result.
123
123
  * @example
124
124
  * const result = await mongo.updateMany('users', { active: false }, { $set: { active: true } });
125
125
  */
@@ -132,10 +132,10 @@ export class MongoManager {
132
132
 
133
133
  /**
134
134
  * @method deleteOne
135
- * @description Elimina el primer documento que coincida con el filtro.
136
- * @param {string} collection - Nombre de la colección.
137
- * @param {Filter<Document>} filter - Filtro para identificar el documento.
138
- * @returns {Promise<DeleteResult>} Resultado de la eliminación.
135
+ * @description Deletes the first document matching the filter.
136
+ * @param {string} collection - Collection name.
137
+ * @param {Filter<Document>} filter - Filter to identify the document.
138
+ * @returns {Promise<DeleteResult>} Deletion result.
139
139
  * @example
140
140
  * const result = await mongo.deleteOne('users', { name: 'Ana' });
141
141
  */
@@ -148,10 +148,10 @@ export class MongoManager {
148
148
 
149
149
  /**
150
150
  * @method deleteMany
151
- * @description Elimina todos los documentos que coincidan con el filtro.
152
- * @param {string} collection - Nombre de la colección.
153
- * @param {Filter<Document>} filter - Filtro para identificar los documentos.
154
- * @returns {Promise<DeleteResult>} Resultado de la eliminación.
151
+ * @description Deletes all documents matching the filter.
152
+ * @param {string} collection - Collection name.
153
+ * @param {Filter<Document>} filter - Filter to identify the documents.
154
+ * @returns {Promise<DeleteResult>} Deletion result.
155
155
  * @example
156
156
  * const result = await mongo.deleteMany('users', { active: false });
157
157
  */
@@ -165,7 +165,7 @@ export class MongoManager {
165
165
 
166
166
  /**
167
167
  * @object MongoManagerTests
168
- * @description Parámetros de pruebas para validar la funcionalidad de MongoManager.
168
+ * @description Test parameters to validate MongoManager functionality.
169
169
  */
170
170
  export const MongoManagerTests = {
171
171
  // insertOne: { collection: 'test', document: { name: 'test_doc', value: 1 } },
@@ -3,7 +3,7 @@ import sql, { config as SQLConfig } from 'mssql';
3
3
 
4
4
  /**
5
5
  * @class SQLManager
6
- * @description Conector con la base de datos SQL Server.
6
+ * @description SQL Server database connector.
7
7
  */
8
8
  export class SQLManager {
9
9
  private pool!: sql.ConnectionPool;
@@ -33,12 +33,12 @@ export class SQLManager {
33
33
 
34
34
  /**
35
35
  * @method select
36
- * @description Ejecuta un comando SELECT en SQL Server y devuelve el resultado como JSON.
37
- * @param {string} query - El comando SELECT completo.
38
- * @returns {Promise<any[]>} Los registros del resultado.
36
+ * @description Executes a SELECT command on SQL Server and returns the result as JSON.
37
+ * @param {string} query - The full SELECT command.
38
+ * @returns {Promise<any[]>} The result records.
39
39
  * @example
40
40
  * await dbManager.init();
41
- * const data = await dbManager.select('SELECT * FROM tabla');
41
+ * const data = await dbManager.select('SELECT * FROM table');
42
42
  */
43
43
  public async select(query: string): Promise<any[]> {
44
44
  await this.init();
@@ -51,9 +51,9 @@ export class SQLManager {
51
51
 
52
52
  /**
53
53
  * @method searchBrokerOnDB
54
- * @description Busca broker por hostname usando la query encoded.
55
- * @param {string | URL} url - URL o string para extraer hostname.
56
- * @returns {Promise<string>} Nombre del broker.
54
+ * @description Looks up a broker by hostname using the encoded query.
55
+ * @param {string | URL} url - URL or string to extract the hostname from.
56
+ * @returns {Promise<string>} Broker name.
57
57
  * @example
58
58
  * const broker = await db.searchBrokerOnDB('https://www.foo.com');
59
59
  */
@@ -88,7 +88,7 @@ export class SQLManager {
88
88
  await this.close();
89
89
 
90
90
  if (!result.recordset[0] || !result.recordset[0].BROKER) {
91
- throw new Error(`No se encontró broker para ${urlObj}`);
91
+ throw new Error(`No broker found for ${urlObj}`);
92
92
  }
93
93
 
94
94
  return result.recordset[0].BROKER as string;
@@ -104,7 +104,7 @@ export class SQLManager {
104
104
 
105
105
  /**
106
106
  * @object SQLManagerTests
107
- * @description Parámetros de pruebas para validar la funcionalidad de SQLManager.
107
+ * @description Test parameters to validate SQLManager functionality.
108
108
  */
109
109
  export const SQLManagerTests = {
110
110
  // init: {},
@@ -203,18 +203,18 @@ export class SetupManager {
203
203
 
204
204
  if (await this.binExists('docker-compose')) return 'docker-compose';
205
205
 
206
- this.logger.warn('docker-compose no encontrado. Intentando instalar...');
206
+ this.logger.warn('docker-compose not found. Attempting to install...');
207
207
 
208
208
  const rawArch = await this.shell.exec('uname -m').catch(() => 'x86_64');
209
209
  const osName = await this.shell.exec("uname -s | tr '[:upper:]' '[:lower:]'").catch(() => 'linux');
210
210
  const bin = '/usr/local/bin/docker-compose';
211
211
  const url = `https://github.com/docker/compose/releases/latest/download/docker-compose-${osName.trim()}-${rawArch.trim()}`;
212
212
 
213
- this.logger.info('Descargando docker-compose desde GitHub...');
213
+ this.logger.info('Downloading docker-compose from GitHub...');
214
214
  try {
215
215
  await this.shell.exec(`curl -fsSL "${url}" -o "${bin}" && chmod +x "${bin}"`);
216
216
  if (await this.shell.exec('docker-compose version 2>/dev/null').then(() => true).catch(() => false)) {
217
- this.logger.success('docker-compose instalado correctamente.');
217
+ this.logger.success('docker-compose installed successfully.');
218
218
  return 'docker-compose';
219
219
  }
220
220
  } catch { /* fall through to pip */ }
@@ -226,14 +226,14 @@ export class SetupManager {
226
226
  `${pip.trim()} install --quiet docker-compose --break-system-packages 2>/dev/null || ${pip.trim()} install --quiet docker-compose`
227
227
  );
228
228
  if (await this.binExists('docker-compose')) {
229
- this.logger.success('docker-compose instalado via pip.');
229
+ this.logger.success('docker-compose installed via pip.');
230
230
  return 'docker-compose';
231
231
  }
232
232
  } catch { /* fall through */ }
233
233
  }
234
234
 
235
- this.logger.warn('No se pudo instalar docker-compose automaticamente.');
236
- this.logger.warn('Instalalo manualmente: https://docs.docker.com/compose/install/');
235
+ this.logger.warn('Could not install docker-compose automatically.');
236
+ this.logger.warn('Install it manually: https://docs.docker.com/compose/install/');
237
237
  return null;
238
238
  }
239
239
 
@@ -249,7 +249,7 @@ export class SetupManager {
249
249
  .then(() => true).catch(() => false);
250
250
  if (ok) return;
251
251
 
252
- this.logger.info('Instalando docker buildx plugin...');
252
+ this.logger.info('Installing docker buildx plugin...');
253
253
  const rawArch = await this.shell.exec('uname -m').catch(() => 'x86_64');
254
254
  const arch = rawArch.trim().replace('x86_64', 'amd64').replace('aarch64', 'arm64');
255
255
  const osName = await this.shell.exec("uname -s | tr '[:upper:]' '[:lower:]'").catch(() => 'linux');
@@ -261,16 +261,16 @@ export class SetupManager {
261
261
  ).catch(() => '');
262
262
 
263
263
  if (!urlRaw.trim()) {
264
- this.logger.warn('No se pudo obtener la URL de docker buildx. El build puede mostrar advertencias.');
264
+ this.logger.warn('Could not fetch the docker buildx URL. The build may show warnings.');
265
265
  return;
266
266
  }
267
267
  try {
268
268
  await this.shell.exec(
269
269
  `mkdir -p "${pluginDir}" && wget -qO "${pluginDir}/docker-buildx" "${urlRaw.trim()}" && chmod +x "${pluginDir}/docker-buildx"`
270
270
  );
271
- this.logger.success('docker buildx instalado.');
271
+ this.logger.success('docker buildx installed.');
272
272
  } catch {
273
- this.logger.warn('No se pudo instalar docker buildx. El build puede mostrar advertencias.');
273
+ this.logger.warn('Could not install docker buildx. The build may show warnings.');
274
274
  }
275
275
  }
276
276
 
@@ -292,13 +292,13 @@ export class SetupManager {
292
292
  const invokePrefix = hasExisting ? 'make -f Makefile.dev' : 'make';
293
293
 
294
294
  if (this.fs.exists(makefilePath)) {
295
- this.logger.info(`${makefileName} ya existe. No se sobreescribe.`);
295
+ this.logger.info(`${makefileName} already exists. Skipping.`);
296
296
  return;
297
297
  }
298
298
 
299
299
  const TAB = '\t';
300
300
  const content = [
301
- `# ${makefileName} generado por setup-dev`,
301
+ `# ${makefileName} generated by setup-dev`,
302
302
  '',
303
303
  'up:', TAB + 'docker-compose up -d', '',
304
304
  'build:', TAB + 'docker-compose up -d --build', '',
@@ -309,12 +309,12 @@ export class SetupManager {
309
309
  ].join('\n');
310
310
 
311
311
  await this.fs.write(makefilePath, content);
312
- this.logger.success(`${makefileName} generado en: ${makefilePath}`);
313
- this.logger.info(` ${invokePrefix} up -> arrancar contenedores`);
314
- this.logger.info(` ${invokePrefix} build -> rebuild y arrancar`);
315
- this.logger.info(` ${invokePrefix} stop -> parar y eliminar (sin orphans)`);
316
- this.logger.info(` ${invokePrefix} logs -> ver logs en tiempo real`);
317
- this.logger.info(` ${invokePrefix} ps -> estado de los contenedores`);
312
+ this.logger.success(`${makefileName} generated at: ${makefilePath}`);
313
+ this.logger.info(` ${invokePrefix} up -> start containers`);
314
+ this.logger.info(` ${invokePrefix} build -> rebuild and start`);
315
+ this.logger.info(` ${invokePrefix} stop -> stop and remove (no orphans)`);
316
+ this.logger.info(` ${invokePrefix} logs -> watch logs in real time`);
317
+ this.logger.info(` ${invokePrefix} ps -> container status`);
318
318
  }
319
319
  }
320
320
 
@@ -123,7 +123,7 @@ export class ShellManager {
123
123
  * @param {boolean} defaultValue - Default answer if user presses Enter (default: false).
124
124
  * @returns {Promise<boolean>} True if the user confirmed.
125
125
  * @example
126
- * const ok = await shell.confirm('¿Continuar?', false);
126
+ * const ok = await shell.confirm('Continue?', false);
127
127
  */
128
128
  public async confirm(question: string, defaultValue: boolean = false): Promise<boolean> {
129
129
  try {
@@ -135,7 +135,7 @@ export class ShellManager {
135
135
  }]);
136
136
  return result.value;
137
137
  } catch (e) {
138
- throw new TyrError(`Error al mostrar la confirmación: ${question}`, e);
138
+ throw new TyrError(`Error showing confirmation: ${question}`, e);
139
139
  }
140
140
  }
141
141
 
@@ -146,7 +146,7 @@ export class ShellManager {
146
146
  * @param {string} question - The question to display.
147
147
  * @returns {Promise<string>} The selected value.
148
148
  * @example
149
- * const branch = await shell.select([{ name: 'main', value: 'main' }], '¿Qué rama?');
149
+ * const branch = await shell.select([{ name: 'main', value: 'main' }], 'Which branch?');
150
150
  */
151
151
  public async select(choices: { name: string; value: string }[], question: string): Promise<string> {
152
152
  try {
@@ -158,7 +158,7 @@ export class ShellManager {
158
158
  }]);
159
159
  return result.value;
160
160
  } catch (e) {
161
- throw new TyrError(`Error al mostrar la selección: ${question}`, e);
161
+ throw new TyrError(`Error showing selection: ${question}`, e);
162
162
  }
163
163
  }
164
164
 
@@ -169,7 +169,7 @@ export class ShellManager {
169
169
  * @param {string} question - The question to display.
170
170
  * @returns {Promise<string[]>} The selected values.
171
171
  * @example
172
- * const widgets = await shell.checkbox(choices, '¿Qué widgets incluir?');
172
+ * const widgets = await shell.checkbox(choices, 'Which widgets to include?');
173
173
  */
174
174
  public async checkbox(choices: { name: string; value: string }[], question: string): Promise<string[]> {
175
175
  try {
@@ -181,7 +181,7 @@ export class ShellManager {
181
181
  }]);
182
182
  return result.value;
183
183
  } catch (e) {
184
- throw new TyrError(`Error al mostrar las opciones: ${question}`, e);
184
+ throw new TyrError(`Error showing options: ${question}`, e);
185
185
  }
186
186
  }
187
187
  }
@@ -195,7 +195,7 @@ export const ShellManagerTests = {
195
195
  cd: { path: '/tmp' },
196
196
  input: { question: 'Enter a test value:' },
197
197
  showLoader: { message: 'Loading test...' },
198
- confirm: { question: '¿Continuar?', defaultValue: false },
199
- select: { choices: [{ name: 'Opción A', value: 'a' }, { name: 'Opción B', value: 'b' }], question: '¿Cuál eliges?' },
200
- checkbox: { choices: [{ name: 'Item 1', value: '1' }, { name: 'Item 2', value: '2' }], question: '¿Cuáles quieres?' },
198
+ confirm: { question: 'Continue?', defaultValue: false },
199
+ select: { choices: [{ name: 'Option A', value: 'a' }, { name: 'Option B', value: 'b' }], question: 'Which one?' },
200
+ checkbox: { choices: [{ name: 'Item 1', value: '1' }, { name: 'Item 2', value: '2' }], question: 'Which ones?' },
201
201
  };
@@ -25,29 +25,29 @@ export class WorkspaceManager {
25
25
  * If it does, asks the user if they want to replace it (deleting it first).
26
26
  * Returns true if the caller should proceed with creating the workspace.
27
27
  * @param {string} dirPath - Absolute path to the workspace directory.
28
- * @param {string} type - Human-readable type name shown in messages (e.g. 'integración', 'web').
28
+ * @param {string} type - Human-readable type name shown in messages (e.g. 'integration', 'web').
29
29
  * @returns {Promise<boolean>} True if the workspace can be created/overwritten.
30
30
  * @example
31
- * const proceed = await workspace.checkExisting('/path/to/repo', 'integración');
31
+ * const proceed = await workspace.checkExisting('/path/to/repo', 'integration');
32
32
  * if (!proceed) return;
33
33
  */
34
- public async checkExisting(dirPath: string, type: string = 'directorio'): Promise<boolean> {
34
+ public async checkExisting(dirPath: string, type: string = 'directory'): Promise<boolean> {
35
35
  if (!this.fs.exists(dirPath)) return true;
36
36
 
37
- this.logger.warn(`Este ${type} ya existe: ${dirPath}`);
38
- const replace = await this.shell.confirm('¿Quieres reemplazarlo?', false);
37
+ this.logger.warn(`This ${type} already exists: ${dirPath}`);
38
+ const replace = await this.shell.confirm('Do you want to replace it?', false);
39
39
 
40
40
  if (!replace) {
41
- this.logger.info('Operación cancelada.');
41
+ this.logger.info('Operation cancelled.');
42
42
  return false;
43
43
  }
44
44
 
45
45
  try {
46
46
  await this.shell.exec(`rm -rf "${dirPath}"`);
47
- this.logger.info('Directorio existente eliminado.');
47
+ this.logger.info('Existing directory removed.');
48
48
  return true;
49
49
  } catch (e) {
50
- throw new TyrError(`No se pudo eliminar el directorio existente: ${dirPath}`, e);
50
+ throw new TyrError(`Could not remove existing directory: ${dirPath}`, e);
51
51
  }
52
52
  }
53
53
 
@@ -65,9 +65,9 @@ export class WorkspaceManager {
65
65
  if (branch) {
66
66
  try {
67
67
  await this.shell.exec(`git -C "${dir}" checkout -b ${branch}`);
68
- this.logger.success(`Rama '${branch}' creada.`);
68
+ this.logger.success(`Branch '${branch}' created.`);
69
69
  } catch (e) {
70
- this.logger.warn(`No se pudo crear la rama '${branch}'. Puede que ya exista.`);
70
+ this.logger.warn(`Could not create branch '${branch}'. It may already exist.`);
71
71
  }
72
72
  }
73
73
 
@@ -75,13 +75,13 @@ export class WorkspaceManager {
75
75
  try {
76
76
  await this.shell.exec(`code "${dir}"`);
77
77
  } catch {
78
- this.logger.warn('No se pudo abrir VSCode. Asegúrate de tener el comando "code" instalado.');
78
+ this.logger.warn('Could not open VSCode. Make sure the "code" command is installed.');
79
79
  }
80
80
  }
81
81
  }
82
82
  }
83
83
 
84
84
  export const WorkspaceManagerTests = {
85
- checkExisting: { dirPath: '/tmp/tyr-workspace-test', type: 'integración' },
85
+ checkExisting: { dirPath: '/tmp/tyr-workspace-test', type: 'integration' },
86
86
  tagWorkspace: { dir: '/tmp/tyr-workspace-test', branch: null, openCode: false },
87
87
  };