@icarusmx/creta 1.5.3 → 1.5.4
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/bin/creta.js +14 -4
- package/lib/executors/ExercisesExecutor.js +271 -0
- package/package.json +1 -1
package/bin/creta.js
CHANGED
|
@@ -9,6 +9,7 @@ import { CretaCodeSession } from '../lib/session.js'
|
|
|
9
9
|
import { PullRequestTutorial } from '../lib/pr-tutorial.js'
|
|
10
10
|
import { CommandHelpExecutor } from '../lib/executors/CommandHelpExecutor.js'
|
|
11
11
|
import { PapersExecutor } from '../lib/papers/PapersExecutor.js'
|
|
12
|
+
import { ExercisesExecutor } from '../lib/executors/ExercisesExecutor.js'
|
|
12
13
|
import { LessonBuilder } from '../lib/builders/LessonBuilder.js'
|
|
13
14
|
import { LESSON_1_SYSTEM_DECOMPOSITION } from '../lib/data/lessons/lesson1-system-decomposition.js'
|
|
14
15
|
import { LESSON_2_OBJECT_REQUESTS } from '../lib/data/lessons/lesson2-object-requests.js'
|
|
@@ -638,7 +639,8 @@ async function startMainMenuInteractive() {
|
|
|
638
639
|
{ id: 1, title: "Aprender sintaxis" },
|
|
639
640
|
{ id: 2, title: "Aprender conceptos de diseño" },
|
|
640
641
|
{ id: 3, title: "Construir proyectos" },
|
|
641
|
-
{ id: 4, title: "
|
|
642
|
+
{ id: 4, title: "Recrear papers clásicos" },
|
|
643
|
+
{ id: 5, title: "Practicar con ejercicios" }
|
|
642
644
|
]
|
|
643
645
|
|
|
644
646
|
let selectedIndex = 0
|
|
@@ -711,6 +713,9 @@ Salgamos de este laberinto 🏛️
|
|
|
711
713
|
} else if (selectedOption.id === 4) {
|
|
712
714
|
const executor = new PapersExecutor()
|
|
713
715
|
executor.execute().then(resolve)
|
|
716
|
+
} else if (selectedOption.id === 5) {
|
|
717
|
+
const executor = new ExercisesExecutor()
|
|
718
|
+
executor.execute().then(resolve)
|
|
714
719
|
}
|
|
715
720
|
return
|
|
716
721
|
}
|
|
@@ -747,10 +752,11 @@ async function startMainMenuFallback() {
|
|
|
747
752
|
console.log("1. Aprender sintaxis")
|
|
748
753
|
console.log("2. Aprender conceptos de diseño")
|
|
749
754
|
console.log("3. Construir proyectos")
|
|
750
|
-
console.log("4.
|
|
755
|
+
console.log("4. Recrear papers clásicos")
|
|
756
|
+
console.log("5. Practicar con ejercicios")
|
|
751
757
|
console.log("")
|
|
752
758
|
|
|
753
|
-
const respuesta = await askQuestion("Elige una opción (1-
|
|
759
|
+
const respuesta = await askQuestion("Elige una opción (1-5) o 'q' para salir: ")
|
|
754
760
|
|
|
755
761
|
if (respuesta.toLowerCase() === 'q') {
|
|
756
762
|
console.log("Hecho con <3 por icarus.mx")
|
|
@@ -773,8 +779,12 @@ async function startMainMenuFallback() {
|
|
|
773
779
|
rl.close()
|
|
774
780
|
const executor = new PapersExecutor()
|
|
775
781
|
await executor.execute()
|
|
782
|
+
} else if (opcionSeleccionada === 5) {
|
|
783
|
+
rl.close()
|
|
784
|
+
const executor = new ExercisesExecutor()
|
|
785
|
+
await executor.execute()
|
|
776
786
|
} else {
|
|
777
|
-
console.log("❌ Opción no válida. Elige 1, 2, 3 o
|
|
787
|
+
console.log("❌ Opción no válida. Elige 1, 2, 3, 4 o 5.")
|
|
778
788
|
rl.close()
|
|
779
789
|
}
|
|
780
790
|
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { createInterface } from 'readline'
|
|
2
|
+
import { execSync } from 'child_process'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import { fileURLToPath } from 'url'
|
|
5
|
+
import fs from 'fs'
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
8
|
+
const __dirname = path.dirname(__filename)
|
|
9
|
+
|
|
10
|
+
export class ExercisesExecutor {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.exercises = [
|
|
13
|
+
{
|
|
14
|
+
id: 1,
|
|
15
|
+
title: "Array & Object Manipulation Warmups",
|
|
16
|
+
description: "JavaScript fundamentals with LazyVim tips",
|
|
17
|
+
file: "array-object-manipulation.md",
|
|
18
|
+
lines: 1282,
|
|
19
|
+
topics: ["JavaScript", "Arrays", "Objects", "LazyVim"]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: 2,
|
|
23
|
+
title: "Git Stash Workflow",
|
|
24
|
+
description: "Wrong branch recovery patterns",
|
|
25
|
+
file: "git-stash-workflow.md",
|
|
26
|
+
lines: 427,
|
|
27
|
+
topics: ["Git", "Stash", "Branching", "Workflow"]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 3,
|
|
31
|
+
title: "Railway Deployment Guide",
|
|
32
|
+
description: "Deploy Node.js apps with Railway CLI",
|
|
33
|
+
file: "railway-deployment.md",
|
|
34
|
+
lines: 500,
|
|
35
|
+
topics: ["Railway", "Deployment", "Node.js", "DevOps"]
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async execute() {
|
|
41
|
+
// Check if setRawMode is available
|
|
42
|
+
if (typeof process.stdin.setRawMode === 'function') {
|
|
43
|
+
return await this.executeInteractive()
|
|
44
|
+
} else {
|
|
45
|
+
return await this.executeFallback()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async executeInteractive() {
|
|
50
|
+
console.log("\n📝 Ejercicios de Práctica")
|
|
51
|
+
console.log("Elige qué ejercicio quieres practicar:")
|
|
52
|
+
console.log("")
|
|
53
|
+
|
|
54
|
+
let selectedIndex = 0
|
|
55
|
+
|
|
56
|
+
// Enable raw mode
|
|
57
|
+
process.stdin.setRawMode(true)
|
|
58
|
+
process.stdin.resume()
|
|
59
|
+
process.stdin.setEncoding('utf8')
|
|
60
|
+
|
|
61
|
+
const renderOptions = () => {
|
|
62
|
+
// Clear screen
|
|
63
|
+
process.stdout.write('\x1b[2J')
|
|
64
|
+
process.stdout.write('\x1b[H')
|
|
65
|
+
|
|
66
|
+
console.log("📝 Ejercicios de Práctica")
|
|
67
|
+
console.log("Elige qué ejercicio quieres practicar:")
|
|
68
|
+
console.log("")
|
|
69
|
+
|
|
70
|
+
this.exercises.forEach((exercise, index) => {
|
|
71
|
+
const isSelected = index === selectedIndex
|
|
72
|
+
const prefix = isSelected ? '▶ ' : ' '
|
|
73
|
+
const highlight = isSelected ? '\x1b[36m' : '\x1b[37m'
|
|
74
|
+
const reset = '\x1b[0m'
|
|
75
|
+
|
|
76
|
+
console.log(`${highlight}${prefix}${exercise.id}. ${exercise.title}${reset}`)
|
|
77
|
+
if (isSelected) {
|
|
78
|
+
console.log(`${highlight} ${exercise.description} (${exercise.lines} líneas)${reset}`)
|
|
79
|
+
}
|
|
80
|
+
console.log("")
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
console.log("\n💡 Tip: Estos archivos están optimizados para LazyVim")
|
|
84
|
+
console.log(" Usa 'nvim' para abrirlos y aprovechar los fold markers")
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return new Promise((resolve) => {
|
|
88
|
+
renderOptions()
|
|
89
|
+
|
|
90
|
+
const onKeyPress = async (key) => {
|
|
91
|
+
if (key === 'q' || key === '\x03') { // q or Ctrl+C
|
|
92
|
+
process.stdin.setRawMode(false)
|
|
93
|
+
process.stdin.removeListener('data', onKeyPress)
|
|
94
|
+
console.log("\nHecho con <3 por icarus.mx")
|
|
95
|
+
resolve()
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (key === '\r' || key === '\n') { // Enter
|
|
100
|
+
process.stdin.setRawMode(false)
|
|
101
|
+
process.stdin.removeListener('data', onKeyPress)
|
|
102
|
+
|
|
103
|
+
const selectedExercise = this.exercises[selectedIndex]
|
|
104
|
+
|
|
105
|
+
// Clear screen
|
|
106
|
+
process.stdout.write('\x1b[2J')
|
|
107
|
+
process.stdout.write('\x1b[H')
|
|
108
|
+
|
|
109
|
+
await this.openExercise(selectedExercise)
|
|
110
|
+
resolve()
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Arrow keys
|
|
115
|
+
if (key === '\u001b[A') { // Up
|
|
116
|
+
selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : this.exercises.length - 1
|
|
117
|
+
renderOptions()
|
|
118
|
+
} else if (key === '\u001b[B') { // Down
|
|
119
|
+
selectedIndex = selectedIndex < this.exercises.length - 1 ? selectedIndex + 1 : 0
|
|
120
|
+
renderOptions()
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
process.stdin.on('data', onKeyPress)
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async executeFallback() {
|
|
129
|
+
const rl = createInterface({
|
|
130
|
+
input: process.stdin,
|
|
131
|
+
output: process.stdout
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
const askQuestion = (question) => {
|
|
135
|
+
return new Promise((resolve) => {
|
|
136
|
+
rl.question(question, resolve)
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
console.log("\n📝 Ejercicios de Práctica")
|
|
142
|
+
console.log("Elige qué ejercicio quieres practicar:")
|
|
143
|
+
console.log("")
|
|
144
|
+
|
|
145
|
+
this.exercises.forEach((exercise) => {
|
|
146
|
+
console.log(`${exercise.id}. ${exercise.title}`)
|
|
147
|
+
console.log(` ${exercise.description} (${exercise.lines} líneas)`)
|
|
148
|
+
console.log("")
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
console.log("💡 Tip: Estos archivos están optimizados para LazyVim")
|
|
152
|
+
console.log(" Usa 'nvim' para abrirlos y aprovechar los fold markers")
|
|
153
|
+
console.log("")
|
|
154
|
+
|
|
155
|
+
const respuesta = await askQuestion(`Elige una opción (1-${this.exercises.length}) o 'q' para salir: `)
|
|
156
|
+
|
|
157
|
+
if (respuesta.toLowerCase() === 'q') {
|
|
158
|
+
console.log("Hecho con <3 por icarus.mx")
|
|
159
|
+
rl.close()
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const opcionSeleccionada = parseInt(respuesta)
|
|
164
|
+
|
|
165
|
+
if (opcionSeleccionada >= 1 && opcionSeleccionada <= this.exercises.length) {
|
|
166
|
+
const selectedExercise = this.exercises[opcionSeleccionada - 1]
|
|
167
|
+
rl.close()
|
|
168
|
+
await this.openExercise(selectedExercise)
|
|
169
|
+
} else {
|
|
170
|
+
console.log(`❌ Opción no válida. Elige un número entre 1 y ${this.exercises.length}.`)
|
|
171
|
+
rl.close()
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
} catch (error) {
|
|
175
|
+
console.error('Error:', error.message)
|
|
176
|
+
rl.close()
|
|
177
|
+
process.exit(1)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async openExercise(exercise) {
|
|
182
|
+
const exercisePath = path.join(__dirname, '..', 'exercises', exercise.file)
|
|
183
|
+
|
|
184
|
+
console.log(`\n📖 ${exercise.title}`)
|
|
185
|
+
console.log("=" .repeat(50))
|
|
186
|
+
console.log("")
|
|
187
|
+
console.log(`📍 Ubicación: ${exercisePath}`)
|
|
188
|
+
console.log(`📊 Líneas: ${exercise.lines}`)
|
|
189
|
+
console.log(`🏷️ Temas: ${exercise.topics.join(', ')}`)
|
|
190
|
+
console.log("")
|
|
191
|
+
|
|
192
|
+
// Check if file exists
|
|
193
|
+
if (!fs.existsSync(exercisePath)) {
|
|
194
|
+
console.log(`❌ Error: No se encontró el archivo ${exercise.file}`)
|
|
195
|
+
console.log("")
|
|
196
|
+
console.log("💡 Tip: Asegúrate de estar ejecutando Creta desde el directorio correcto")
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
console.log("📚 Opciones:")
|
|
201
|
+
console.log("")
|
|
202
|
+
console.log("1. Abrir con nvim (recomendado para aprovechar fold markers)")
|
|
203
|
+
console.log("2. Mostrar la ruta (copiar al portapapeles)")
|
|
204
|
+
console.log("3. Ver los primeros 20 líneas")
|
|
205
|
+
console.log("")
|
|
206
|
+
|
|
207
|
+
const rl = createInterface({
|
|
208
|
+
input: process.stdin,
|
|
209
|
+
output: process.stdout
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
const askQuestion = (question) => {
|
|
213
|
+
return new Promise((resolve) => {
|
|
214
|
+
rl.question(question, resolve)
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const choice = await askQuestion("Elige una opción (1-3) o Enter para abrir con nvim: ")
|
|
219
|
+
rl.close()
|
|
220
|
+
|
|
221
|
+
if (choice === '' || choice === '1') {
|
|
222
|
+
// Open with nvim
|
|
223
|
+
console.log("\n🚀 Abriendo con nvim...")
|
|
224
|
+
console.log("💡 Tip: Usa 'zM' para cerrar todos los folds y ver la estructura")
|
|
225
|
+
console.log("")
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
// Try to open with nvim
|
|
229
|
+
execSync(`nvim "${exercisePath}"`, { stdio: 'inherit' })
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.log("\n⚠️ No se pudo abrir nvim. Intentando con el editor por defecto...")
|
|
232
|
+
try {
|
|
233
|
+
execSync(`$EDITOR "${exercisePath}" || vi "${exercisePath}"`, { stdio: 'inherit' })
|
|
234
|
+
} catch (fallbackError) {
|
|
235
|
+
console.log("\n❌ No se pudo abrir ningún editor.")
|
|
236
|
+
console.log(`📍 Abre manualmente: ${exercisePath}`)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
} else if (choice === '2') {
|
|
240
|
+
// Show path
|
|
241
|
+
console.log("\n📋 Ruta copiada al portapapeles (en algunos sistemas):")
|
|
242
|
+
console.log(exercisePath)
|
|
243
|
+
console.log("")
|
|
244
|
+
console.log("💡 Tip: Usa este comando para abrirlo:")
|
|
245
|
+
console.log(` nvim "${exercisePath}"`)
|
|
246
|
+
|
|
247
|
+
// Try to copy to clipboard (macOS)
|
|
248
|
+
try {
|
|
249
|
+
execSync(`echo "${exercisePath}" | pbcopy`, { stdio: 'ignore' })
|
|
250
|
+
console.log("✓ Ruta copiada al portapapeles (macOS)")
|
|
251
|
+
} catch (error) {
|
|
252
|
+
// Silently fail if pbcopy not available
|
|
253
|
+
}
|
|
254
|
+
} else if (choice === '3') {
|
|
255
|
+
// Show preview
|
|
256
|
+
console.log("\n📄 Primeras 20 líneas:")
|
|
257
|
+
console.log("=" .repeat(50))
|
|
258
|
+
try {
|
|
259
|
+
const preview = execSync(`head -20 "${exercisePath}"`, { encoding: 'utf8' })
|
|
260
|
+
console.log(preview)
|
|
261
|
+
} catch (error) {
|
|
262
|
+
console.log("❌ Error al leer el archivo")
|
|
263
|
+
}
|
|
264
|
+
console.log("=" .repeat(50))
|
|
265
|
+
console.log("")
|
|
266
|
+
console.log(`💡 Abre el archivo completo con: nvim "${exercisePath}"`)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
console.log("")
|
|
270
|
+
}
|
|
271
|
+
}
|