@icarusmx/creta 1.5.15 → 1.5.17

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 CHANGED
@@ -10,7 +10,7 @@ import { PullRequestTutorial } from '../lib/pr-tutorial.js'
10
10
  import { VimSetupTutorial } from '../lib/vim-setup-tutorial.js'
11
11
  import { AWSGuideViewer } from '../lib/aws-guide-viewer.js'
12
12
  import { CommandHelpExecutor } from '../lib/executors/CommandHelpExecutor.js'
13
- import { readExercise, listExercises } from '../lib/readers/exercise-reader.js'
13
+ import { readExercise, listExercises, syncBiblioteca } from '../lib/readers/exercise-reader.js'
14
14
  import { PapersExecutor } from '../lib/papers/PapersExecutor.js'
15
15
  import { ExercisesExecutor } from '../lib/executors/ExercisesExecutor.js'
16
16
  import { executeSintaxis } from '../lib/executors/sintaxis-executor.js'
@@ -193,6 +193,9 @@ if (command.startsWith('portafolio')) {
193
193
  } else if (command === 'exercises' || command === 'ejercicios') {
194
194
  // List all exercises
195
195
  await listExercises()
196
+ } else if (command === 'biblioteca') {
197
+ // Sync exercises to ~/.creta/biblioteca
198
+ await syncBiblioteca()
196
199
  } else if (command === 'help' || command === 'ayuda') {
197
200
  // Show available commands
198
201
  showHelp()
@@ -708,6 +711,7 @@ function showHelp() {
708
711
  console.log(" creta code - Sesión interactiva de programación con IA\n")
709
712
 
710
713
  console.log("🔧 Utilidades:")
714
+ console.log(" creta biblioteca - Sincroniza ejercicios a ~/.creta/biblioteca/")
711
715
  console.log(" creta reset - Reinicia tu progreso (elimina ~/.creta/user.json)")
712
716
  console.log(" creta privacy - Explica qué datos guarda Creta y dónde\n")
713
717
 
@@ -1,80 +1,270 @@
1
1
  export const curl = {
2
2
  command: 'curl',
3
- description: 'Realiza peticiones HTTP desde la terminal - tu herramienta para APIs',
3
+ description: 'Realiza peticiones HTTP desde la terminal - para APIs y descargar scripts',
4
4
  usage: 'curl [opciones] [URL]',
5
+
6
+ learningPath: {
7
+ intro: 'curl tiene dos usos principales: trabajar con APIs y descargar scripts',
8
+ progression: [
9
+ 'Primero: Aprende cada flag individual (-L, -s, -f, -S)',
10
+ 'Segundo: Combínalos para formar -fsSL',
11
+ 'Tercero: Úsalo en instalaciones reales (curl -fsSL url | bash)',
12
+ 'Cuarto: Aplícalo a APIs REST (-X, -H, -d)'
13
+ ],
14
+ goal: 'Dominar -fsSL te hace ver profesional. Lo verás en toda instalación de herramientas.'
15
+ },
16
+
5
17
  commonOptions: [
6
18
  {
7
- flag: '-X',
8
- description: 'Especifica el método HTTP (GET, POST, PUT, DELETE)'
19
+ flag: '-L',
20
+ description: 'Sigue redirects automáticamente',
21
+ why: 'Las URLs pueden redirigir. Sin -L, curl se detiene en la primera respuesta',
22
+ example: 'curl -L https://bit.ly/ejemplo',
23
+ level: 1
9
24
  },
10
25
  {
11
- flag: '-H',
12
- description: 'Agrega un header a la petición'
26
+ flag: '-s',
27
+ description: 'Modo silencioso - oculta la barra de progreso',
28
+ why: 'El progreso es ruido visual cuando haces piping o scripts',
29
+ example: 'curl -s https://api.ejemplo.com | jq',
30
+ level: 2
13
31
  },
14
32
  {
15
- flag: '-d',
16
- description: 'Envía datos en el body (JSON, form data, etc.)'
33
+ flag: '-f',
34
+ description: 'Falla visiblemente en errores HTTP (400+, 500+)',
35
+ why: 'Sin esto, curl devuelve el HTML de error como si fuera éxito',
36
+ example: 'curl -f https://api.ejemplo.com/404',
37
+ level: 3
17
38
  },
18
39
  {
19
- flag: '-i',
20
- description: 'Incluye los headers de respuesta en el output'
40
+ flag: '-S',
41
+ description: 'Muestra errores incluso en modo silencioso',
42
+ why: 'Para combinar con -s: silencioso pero con errores visibles',
43
+ example: 'curl -sS https://api.ejemplo.com',
44
+ level: 4
21
45
  },
22
46
  {
23
- flag: '-L',
24
- description: 'Sigue redirects automáticamente'
47
+ flag: '-fsSL',
48
+ description: 'EL COMBO DEFINITIVO - Siempre úsalo para instalar herramientas',
49
+ why: 'Combina todo: fail on error + silent + show errors + follow redirects',
50
+ example: 'curl -fsSL https://deno.land/install.sh | bash',
51
+ level: 5,
52
+ note: 'Este patrón aparece en TODAS las instalaciones profesionales'
25
53
  },
26
54
  {
27
- flag: '-o',
28
- description: 'Guarda el output en un archivo'
55
+ flag: '-X METHOD',
56
+ description: 'Especifica método HTTP: GET, POST, PUT, DELETE',
57
+ context: 'APIs REST',
58
+ example: 'curl -X POST https://api.ejemplo.com/users'
29
59
  },
30
60
  {
31
- flag: '-s',
32
- description: 'Modo silencioso (no muestra progreso)'
61
+ flag: '-H "Header: valor"',
62
+ description: 'Agrega headers HTTP a la petición',
63
+ context: 'Content-Type, Authorization, etc.',
64
+ example: 'curl -H "Content-Type: application/json"'
65
+ },
66
+ {
67
+ flag: '-d "data"',
68
+ description: 'Envía datos en el body (JSON, form data)',
69
+ context: 'POST/PUT requests',
70
+ example: 'curl -d \'{"nombre": "Juan"}\''
71
+ },
72
+ {
73
+ flag: '-i',
74
+ description: 'Incluye headers de respuesta en output',
75
+ context: 'Ver status codes: 200 OK, 404 Not Found, 500 Error',
76
+ example: 'curl -i https://api.ejemplo.com'
33
77
  },
34
78
  {
35
79
  flag: '-v',
36
- description: 'Modo verbose (muestra toda la comunicación)'
80
+ description: 'Modo verbose - muestra request y response completos',
81
+ context: 'Debugging profundo',
82
+ example: 'curl -v https://api.ejemplo.com'
83
+ },
84
+ {
85
+ flag: '-o archivo',
86
+ description: 'Guarda output en archivo',
87
+ context: 'Descargar recursos',
88
+ example: 'curl -o data.json https://api.ejemplo.com/data'
37
89
  }
38
90
  ],
91
+
39
92
  examples: [
40
93
  {
41
- command: 'curl https://api.github.com',
42
- description: 'GET simple - obtiene datos de una API'
94
+ title: 'LA PROGRESIÓN: Construyendo hacia -fsSL',
95
+ commands: [
96
+ {
97
+ command: 'curl https://api.github.com',
98
+ step: 'Paso 1: GET básico',
99
+ output: 'Verás barra de progreso y datos',
100
+ problema: 'Demasiado ruido visual'
101
+ },
102
+ {
103
+ command: 'curl -L https://nodejs.org/install',
104
+ step: 'Paso 2: + sigue redirects',
105
+ output: 'Sigue automáticamente si la URL redirige',
106
+ mejora: 'Ahora funciona con URLs acortadas'
107
+ },
108
+ {
109
+ command: 'curl -sL https://nodejs.org/install',
110
+ step: 'Paso 3: + modo silencioso',
111
+ output: 'Sin barra de progreso, solo datos',
112
+ mejora: 'Output limpio, listo para piping'
113
+ },
114
+ {
115
+ command: 'curl -fsSL https://deno.land/install.sh',
116
+ step: 'Paso 4: + falla en errores + muestra errores',
117
+ output: 'El combo completo',
118
+ nivel: 'PROFESIONAL - Memoriza esto'
119
+ },
120
+ {
121
+ command: 'curl -fsSL https://deno.land/install.sh | bash',
122
+ step: 'Paso 5: + ejecuta con piping',
123
+ output: 'Descarga e instala en un comando',
124
+ uso: 'Así instalan los profesionales'
125
+ }
126
+ ]
43
127
  },
44
128
  {
45
- command: 'curl -i https://api.github.com/users/octocat',
46
- description: 'GET con headers de respuesta incluidos'
129
+ title: 'CASOS REALES: Instalaciones que verás constantemente',
130
+ commands: [
131
+ {
132
+ command: 'curl -fsSL https://get.docker.com | sh',
133
+ tool: 'Docker',
134
+ note: 'Containerización'
135
+ },
136
+ {
137
+ command: 'curl -fsSL https://deno.land/install.sh | sh',
138
+ tool: 'Deno',
139
+ note: 'Runtime de JavaScript/TypeScript'
140
+ },
141
+ {
142
+ command: 'curl -fsSL https://sh.rustup.rs | sh',
143
+ tool: 'Rust',
144
+ note: 'Lenguaje de sistemas'
145
+ },
146
+ {
147
+ command: 'curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh | bash',
148
+ tool: 'Homebrew',
149
+ note: 'Package manager para macOS'
150
+ },
151
+ {
152
+ command: 'curl -fsSL https://get.pnpm.io/install.sh | sh',
153
+ tool: 'pnpm',
154
+ note: 'Package manager rápido'
155
+ }
156
+ ]
47
157
  },
48
158
  {
49
- command: 'curl -X POST https://api.ejemplo.com/users -H "Content-Type: application/json" -d \'{"nombre": "Juan"}\'',
50
- description: 'POST con JSON - envía datos a una API'
159
+ title: 'TRABAJANDO CON APIs REST',
160
+ commands: [
161
+ {
162
+ command: 'curl https://api.github.com/users/octocat',
163
+ use: 'GET simple - API pública',
164
+ response: 'JSON con datos del usuario'
165
+ },
166
+ {
167
+ command: 'curl -X POST https://httpbin.org/post -H "Content-Type: application/json" -d \'{"nombre": "Juan", "edad": 25}\'',
168
+ use: 'POST con JSON - enviar datos',
169
+ response: 'Echo del servidor con tus datos'
170
+ },
171
+ {
172
+ command: 'curl -H "Authorization: Bearer TOKEN" https://api.ejemplo.com/protected',
173
+ use: 'GET autenticado - API privada',
174
+ response: 'Recursos protegidos'
175
+ },
176
+ {
177
+ command: 'curl -X PUT https://api.ejemplo.com/users/123 -H "Content-Type: application/json" -d \'{"nombre": "Juan Actualizado"}\'',
178
+ use: 'PUT - actualizar recurso',
179
+ response: 'Recurso modificado'
180
+ },
181
+ {
182
+ command: 'curl -X DELETE https://api.ejemplo.com/users/123 -H "Authorization: Bearer TOKEN"',
183
+ use: 'DELETE - eliminar recurso',
184
+ response: 'Confirmación de eliminación'
185
+ }
186
+ ]
51
187
  },
52
188
  {
53
- command: 'curl -X POST https://httpbin.org/post -d "nombre=Juan&edad=25"',
54
- description: 'POST con form data - formato URL-encoded'
189
+ title: 'DEBUGGING: Cuando las cosas no funcionan',
190
+ commands: [
191
+ {
192
+ command: 'curl -i https://api.ejemplo.com',
193
+ purpose: 'Ver headers de respuesta',
194
+ verás: 'HTTP/1.1 200 OK, Content-Type, etc.'
195
+ },
196
+ {
197
+ command: 'curl -v https://api.ejemplo.com',
198
+ purpose: 'Ver comunicación completa',
199
+ verás: 'Request headers, response headers, handshake SSL, etc.'
200
+ },
201
+ {
202
+ command: 'curl -f https://api.ejemplo.com/404',
203
+ purpose: 'Forzar error visible en 404/500',
204
+ verás: 'curl: (22) The requested URL returned error: 404'
205
+ },
206
+ {
207
+ command: 'curl -w "\\nStatus: %{http_code}\\n" https://api.ejemplo.com',
208
+ purpose: 'Mostrar solo el status code',
209
+ verás: 'Status: 200'
210
+ }
211
+ ]
212
+ }
213
+ ],
214
+
215
+ relatedLesson: 'APIs y HTTP',
216
+
217
+ tips: [
218
+ 'REGLA DE ORO: curl -fsSL url | bash es el patrón universal para instalaciones',
219
+ 'Desglose de -fsSL: (f)ail on error + (s)ilent + (S)how errors + (L)ocation',
220
+ 'Para APIs: curl -X POST -H "Content-Type: application/json" -d \'{"key":"val"}\'',
221
+ 'Para ver status codes: curl -i (headers) o curl -v (todo)',
222
+ 'Siempre usa -L con URLs acortadas o que pueden redirigir'
223
+ ],
224
+
225
+ commonMistakes: [
226
+ {
227
+ error: 'Olvidar -L cuando la URL redirige',
228
+ resultado: 'Verás HTML de redirect en vez del recurso',
229
+ solución: 'Siempre agrega -L por defecto'
55
230
  },
56
231
  {
57
- command: 'curl -H "Authorization: Bearer tu_token_aqui" https://api.ejemplo.com/protected',
58
- description: 'GET con autenticación - usa un token JWT'
232
+ error: 'No usar -f en scripts automatizados',
233
+ resultado: 'curl "tiene éxito" incluso con 404/500',
234
+ solución: 'Usa -f para que falle visiblemente'
59
235
  },
60
236
  {
61
- command: 'curl -L https://github.com',
62
- description: 'Sigue redirects automáticamente (importante para URLs acortadas)'
237
+ error: 'Usar -v en scripts de producción',
238
+ resultado: 'Logs llenos de ruido innecesario',
239
+ solución: 'Usa -i para ver solo headers o -s para silencio'
63
240
  },
64
241
  {
65
- command: 'curl -o archivo.json https://api.ejemplo.com/data',
66
- description: 'Descarga la respuesta y guárdala en un archivo'
242
+ error: 'Olvidar comillas en JSON',
243
+ resultado: 'curl -d {"key":"value"} falla (bash interpreta las llaves)',
244
+ solución: 'Siempre usa comillas simples: -d \'{"key":"value"}\''
67
245
  },
68
246
  {
69
- command: 'curl -v https://api.ejemplo.com',
70
- description: 'Modo verbose - ve toda la negociación HTTP (útil para debugging)'
247
+ error: 'No especificar Content-Type en POST',
248
+ resultado: 'El servidor no entiende el formato',
249
+ solución: 'Siempre agrega -H "Content-Type: application/json"'
71
250
  }
72
251
  ],
73
- relatedLesson: 'APIs y HTTP',
74
- tips: [
75
- 'Pro tip: usa -i siempre para ver los status codes (200, 404, 500) en la respuesta',
76
- 'Para APIs modernas siempre agrega el header: -H "Content-Type: application/json"',
77
- 'curl -L es esencial cuando trabajas con URLs acortadas o redirects',
78
- 'Combina -s (silencioso) con herramientas como jq para parsear JSON: curl -s url | jq'
252
+
253
+ proTips: [
254
+ {
255
+ tip: 'Combina curl con jq para parsear JSON',
256
+ command: 'curl -s https://api.github.com/users/octocat | jq .name',
257
+ output: '"The Octocat"'
258
+ },
259
+ {
260
+ tip: 'Usa alias para curl -fsSL',
261
+ command: 'alias curlsh="curl -fsSL"',
262
+ entonces: 'curlsh https://ejemplo.com/install.sh | bash'
263
+ },
264
+ {
265
+ tip: 'Guarda cookies para sesiones',
266
+ command: 'curl -c cookies.txt -d "user=juan" https://ejemplo.com/login',
267
+ luego: 'curl -b cookies.txt https://ejemplo.com/dashboard'
268
+ }
79
269
  ]
80
270
  }
@@ -1,6 +1,7 @@
1
- import { readFileSync, readdirSync } from 'fs'
1
+ import { readFileSync, readdirSync, existsSync, mkdirSync, copyFileSync, statSync } from 'fs'
2
2
  import { join, dirname } from 'path'
3
3
  import { fileURLToPath } from 'url'
4
+ import { homedir } from 'os'
4
5
  import chalk from 'chalk'
5
6
 
6
7
  const __filename = fileURLToPath(import.meta.url)
@@ -9,6 +10,78 @@ const __dirname = dirname(__filename)
9
10
  export class ExerciseReader {
10
11
  constructor() {
11
12
  this.exercisesDir = join(__dirname, '../exercises')
13
+ this.bibliotecaDir = join(homedir(), '.creta', 'biblioteca')
14
+ }
15
+
16
+ /**
17
+ * Ensure biblioteca directory exists and sync exercises
18
+ */
19
+ syncBiblioteca(silent = false) {
20
+ try {
21
+ // Create directory if needed
22
+ if (!existsSync(this.bibliotecaDir)) {
23
+ mkdirSync(this.bibliotecaDir, { recursive: true })
24
+ if (!silent) {
25
+ console.log(chalk.cyan('\n📚 Creando biblioteca en ~/.creta/biblioteca...'))
26
+ }
27
+ }
28
+
29
+ // Get all exercise files
30
+ const exercises = this.getAvailableExercises()
31
+ let copied = 0
32
+ let updated = 0
33
+ let skipped = 0
34
+
35
+ exercises.forEach(exercise => {
36
+ const sourcePath = join(this.exercisesDir, exercise.filename)
37
+ const destPath = join(this.bibliotecaDir, exercise.filename)
38
+
39
+ // Check if destination exists and is up to date
40
+ if (existsSync(destPath)) {
41
+ const sourceStats = statSync(sourcePath)
42
+ const destStats = statSync(destPath)
43
+
44
+ if (sourceStats.mtime > destStats.mtime) {
45
+ copyFileSync(sourcePath, destPath)
46
+ updated++
47
+ } else {
48
+ skipped++
49
+ }
50
+ } else {
51
+ copyFileSync(sourcePath, destPath)
52
+ copied++
53
+ }
54
+ })
55
+
56
+ if (!silent) {
57
+ console.log(chalk.green('\n✅ Biblioteca sincronizada'))
58
+ if (copied > 0) console.log(chalk.gray(` ${copied} ejercicio${copied !== 1 ? 's' : ''} copiado${copied !== 1 ? 's' : ''}`))
59
+ if (updated > 0) console.log(chalk.gray(` ${updated} ejercicio${updated !== 1 ? 's' : ''} actualizado${updated !== 1 ? 's' : ''}`))
60
+ if (skipped > 0) console.log(chalk.gray(` ${skipped} ejercicio${skipped !== 1 ? 's' : ''} sin cambios`))
61
+ console.log(chalk.cyan(`\n💡 Abre ejercicios con: nvim ~/.creta/biblioteca/<archivo>.md`))
62
+ console.log(chalk.gray(` Ejemplo: nvim ~/.creta/biblioteca/14-gh-fundamentals.md\n`))
63
+ }
64
+
65
+ return { copied, updated, skipped, total: exercises.length }
66
+ } catch (error) {
67
+ if (!silent) {
68
+ console.error(chalk.red('\n❌ Error sincronizando biblioteca:'), error.message)
69
+ }
70
+ return { copied: 0, updated: 0, skipped: 0, total: 0, error }
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Check if biblioteca needs initialization (show tip on first read)
76
+ */
77
+ checkBiblioteca() {
78
+ if (!existsSync(this.bibliotecaDir)) {
79
+ return {
80
+ exists: false,
81
+ message: chalk.dim(`\n💡 Tip: Ejecuta ${chalk.cyan('creta biblioteca')} para copiar ejercicios a ~/.creta/biblioteca/\n Así podrás abrirlos en nvim con fold markers.`)
82
+ }
83
+ }
84
+ return { exists: true, message: null }
12
85
  }
13
86
 
14
87
  /**
@@ -94,6 +167,13 @@ export class ExerciseReader {
94
167
  const content = readFileSync(filepath, 'utf-8')
95
168
 
96
169
  this.displayExercise(exercise, content)
170
+
171
+ // Show biblioteca tip on first read
172
+ const bibliotecaStatus = this.checkBiblioteca()
173
+ if (!bibliotecaStatus.exists && bibliotecaStatus.message) {
174
+ console.log(bibliotecaStatus.message)
175
+ }
176
+
97
177
  return true
98
178
  } catch (error) {
99
179
  console.error(chalk.red('❌ Error leyendo ejercicio:'), error.message)
@@ -195,3 +275,11 @@ export async function listExercises() {
195
275
  const reader = new ExerciseReader()
196
276
  reader.list()
197
277
  }
278
+
279
+ /**
280
+ * Sync biblioteca CLI entry point
281
+ */
282
+ export async function syncBiblioteca() {
283
+ const reader = new ExerciseReader()
284
+ reader.syncBiblioteca()
285
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@icarusmx/creta",
3
- "version": "1.5.15",
3
+ "version": "1.5.17",
4
4
  "description": "Salgamos de este laberinto.",
5
5
  "type": "module",
6
6
  "bin": {