@icarusmx/creta 1.5.12 → 1.5.14

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.
Files changed (27) hide show
  1. package/bin/creta.js +31 -1
  2. package/lib/data/command-help/aws-ec2.js +34 -0
  3. package/lib/data/command-help/grep.js +76 -0
  4. package/lib/data/command-help/index.js +11 -1
  5. package/lib/data/command-help/lz.js +57 -0
  6. package/lib/executors/CommandHelpExecutor.js +6 -1
  7. package/lib/exercises/.claude/settings.local.json +12 -0
  8. package/lib/exercises/01-developing-muscle-for-nvim.md +528 -0
  9. package/lib/exercises/{iterm2-pane-navigation.md → 02-iterm2-pane-navigation.md} +1 -1
  10. package/lib/exercises/05-svelte-first-steps.md +1340 -0
  11. package/lib/exercises/{curl-and-pipes.md → 06-curl-and-pipes.md} +187 -72
  12. package/lib/exercises/07-claude-api-first-steps.md +855 -0
  13. package/lib/exercises/08-playwright-svelte-guide.md +1384 -0
  14. package/lib/exercises/09-docker-first-steps.md +1475 -0
  15. package/lib/exercises/{railway-deployment.md → 10-railway-deployment.md} +1 -0
  16. package/lib/exercises/{aws-billing-detective.md → 11-aws-billing-detective.md} +215 -35
  17. package/lib/exercises/12-install-skills.md +755 -0
  18. package/lib/exercises/13-shell-aliases.md +134 -0
  19. package/lib/exercises/README.md +187 -0
  20. package/lib/exercises/utils/booklet-2up.js +133 -0
  21. package/lib/exercises/utils/booklet-manual-duplex.js +159 -0
  22. package/lib/exercises/utils/booklet-simple.js +136 -0
  23. package/lib/exercises/utils/create-booklet.js +116 -0
  24. package/lib/scripts/aws-ec2-all.sh +58 -0
  25. package/package.json +3 -2
  26. /package/lib/exercises/{git-stash-workflow.md → 03-git-stash-workflow.md} +0 -0
  27. /package/lib/exercises/{array-object-manipulation.md → 04-array-object-manipulation.md} +0 -0
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { PDFDocument, degrees } from 'pdf-lib';
4
+ import fs from 'fs/promises';
5
+ import path from 'path';
6
+
7
+ async function createManualDuplexBooklet(inputPath) {
8
+ try {
9
+ const filename = path.basename(inputPath, '.pdf');
10
+ console.log(`\n📚 Creando booklet para: ${filename}`);
11
+
12
+ // Leer PDF original
13
+ const srcFile = await fs.readFile(inputPath);
14
+ const srcDoc = await PDFDocument.load(srcFile);
15
+ const totalPages = srcDoc.getPageCount();
16
+
17
+ console.log(`📄 Páginas originales: ${totalPages}`);
18
+
19
+ // Calcular páginas necesarias (múltiplo de 4)
20
+ const sheetsNeeded = Math.ceil(totalPages / 4);
21
+ const paddedPages = sheetsNeeded * 4;
22
+ const blankPages = paddedPages - totalPages;
23
+
24
+ if (blankPages > 0) {
25
+ console.log(`📄 Páginas en blanco añadidas: ${blankPages}`);
26
+ }
27
+ console.log(`📄 Total hojas físicas: ${sheetsNeeded}`);
28
+
29
+ // Crear array de páginas incluyendo blancos
30
+ const pages = [];
31
+ for (let i = 0; i < totalPages; i++) {
32
+ pages.push(srcDoc.getPage(i));
33
+ }
34
+ // Añadir páginas en blanco si es necesario
35
+ const firstPage = srcDoc.getPage(0);
36
+ const { width, height } = firstPage.getSize();
37
+ for (let i = totalPages; i < paddedPages; i++) {
38
+ // Crear página en blanco temporal (no la añadimos al doc original)
39
+ pages.push(null); // null representa página en blanco
40
+ }
41
+
42
+ // Crear documentos finales
43
+ const frontDoc = await PDFDocument.create();
44
+ const backDoc = await PDFDocument.create();
45
+
46
+ // Para cada hoja física
47
+ for (let sheet = 0; sheet < sheetsNeeded; sheet++) {
48
+ // Calcular índices de página (0-indexed)
49
+ const frontLeft = paddedPages - (sheet * 2) - 1; // última hacia adentro
50
+ const frontRight = (sheet * 2); // primera hacia afuera
51
+ const backLeft = (sheet * 2) + 1; // segunda
52
+ const backRight = paddedPages - (sheet * 2) - 2; // penúltima hacia adentro
53
+
54
+ console.log(`Hoja ${sheet + 1}: Frente[${frontLeft < totalPages ? frontLeft + 1 : 'blank'}, ${frontRight < totalPages ? frontRight + 1 : 'blank'}] Reverso[${backLeft < totalPages ? backLeft + 1 : 'blank'}, ${backRight < totalPages ? backRight + 1 : 'blank'}]`);
55
+
56
+ // FRENTE: Agregar página derecha (frontRight) y luego izquierda (frontLeft)
57
+ // El orden importa porque así queda al doblar
58
+ if (frontRight < totalPages) {
59
+ const [copied] = await frontDoc.copyPages(srcDoc, [frontRight]);
60
+ frontDoc.addPage(copied);
61
+ } else {
62
+ frontDoc.addPage(frontDoc.addPage([width, height]));
63
+ }
64
+
65
+ if (frontLeft < totalPages) {
66
+ const [copied] = await frontDoc.copyPages(srcDoc, [frontLeft]);
67
+ frontDoc.addPage(copied);
68
+ } else {
69
+ frontDoc.addPage(frontDoc.addPage([width, height]));
70
+ }
71
+
72
+ // REVERSO: Agregar en el orden correcto para duplex sin voltear
73
+ // Izquierda primero, derecha después
74
+ if (backLeft < totalPages) {
75
+ const [copied] = await backDoc.copyPages(srcDoc, [backLeft]);
76
+ backDoc.addPage(copied);
77
+ } else {
78
+ backDoc.addPage(backDoc.addPage([width, height]));
79
+ }
80
+
81
+ if (backRight < totalPages) {
82
+ const [copied] = await backDoc.copyPages(srcDoc, [backRight]);
83
+ backDoc.addPage(copied);
84
+ } else {
85
+ backDoc.addPage(backDoc.addPage([width, height]));
86
+ }
87
+ }
88
+
89
+ // Guardar PDFs
90
+ const frontPath = path.join(path.dirname(inputPath), `${filename}-FRENTE-v2.pdf`);
91
+ const backPath = path.join(path.dirname(inputPath), `${filename}-REVERSO-v2.pdf`);
92
+
93
+ await fs.writeFile(frontPath, await frontDoc.save());
94
+ await fs.writeFile(backPath, await backDoc.save());
95
+
96
+ console.log('\n✅ Booklets creados:\n');
97
+ console.log(` 📄 FRENTE: ${path.basename(frontPath)}`);
98
+ console.log(` 📄 REVERSO: ${path.basename(backPath)}`);
99
+
100
+ console.log('\n📖 INSTRUCCIONES:\n');
101
+ console.log('1. Imprime FRENTE (todas las páginas)');
102
+ console.log('2. Toma la pila y métela de nuevo SIN voltear');
103
+ console.log('3. Imprime REVERSO');
104
+ console.log('4. Dobla por la mitad y encuaderna a la izquierda\n');
105
+
106
+ return { frontPath, backPath };
107
+
108
+ } catch (error) {
109
+ console.error('❌ Error:', error.message);
110
+ console.error(error);
111
+ throw error;
112
+ }
113
+ }
114
+
115
+ async function main() {
116
+ const args = process.argv.slice(2);
117
+
118
+ if (args.length === 0) {
119
+ console.log('Uso: node booklet-simple.js <archivo.pdf>');
120
+ process.exit(1);
121
+ }
122
+
123
+ const inputPath = path.resolve(args[0]);
124
+
125
+ try {
126
+ await fs.access(inputPath);
127
+ } catch {
128
+ console.error(`❌ Archivo no encontrado: ${inputPath}`);
129
+ process.exit(1);
130
+ }
131
+
132
+ await createManualDuplexBooklet(inputPath);
133
+ console.log('\n¡Listo! 🎉\n');
134
+ }
135
+
136
+ main().catch(console.error);
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { shortEdge } from 'pdf-book';
4
+ import { PDFDocument } from 'pdf-lib';
5
+ import fs from 'fs/promises';
6
+ import path from 'path';
7
+ import readline from 'readline';
8
+
9
+ const rl = readline.createInterface({
10
+ input: process.stdin,
11
+ output: process.stdout
12
+ });
13
+
14
+ function ask(question) {
15
+ return new Promise((resolve) => {
16
+ rl.question(question, resolve);
17
+ });
18
+ }
19
+
20
+ async function createBooklet(inputPath) {
21
+ try {
22
+ const filename = path.basename(inputPath, '.pdf');
23
+ console.log(`\n📚 Creando booklet para: ${filename}`);
24
+
25
+ // Leer PDF original
26
+ const srcFile = await fs.readFile(inputPath);
27
+
28
+ // Crear booklet (ordena páginas para doblar por la mitad)
29
+ console.log('⚙️ Reorganizando páginas para booklet...');
30
+
31
+ // Suprimir console.log temporalmente
32
+ const originalLog = console.log;
33
+ console.log = () => {};
34
+
35
+ const { saved } = await shortEdge(srcFile);
36
+
37
+ // Restaurar console.log
38
+ console.log = originalLog;
39
+
40
+ // Cargar el booklet para separar páginas
41
+ const bookletDoc = await PDFDocument.load(saved);
42
+ const totalPages = bookletDoc.getPageCount();
43
+
44
+ console.log(`📄 Total de páginas en booklet: ${totalPages}`);
45
+
46
+ // Crear PDF para FRENTE (páginas impares: 1, 3, 5, 7...)
47
+ const frontDoc = await PDFDocument.create();
48
+ // Crear PDF para REVERSO (páginas pares: 2, 4, 6, 8...)
49
+ const backDoc = await PDFDocument.create();
50
+
51
+ for (let i = 0; i < totalPages; i++) {
52
+ const [page] = await (i % 2 === 0 ? frontDoc : backDoc).copyPages(bookletDoc, [i]);
53
+ (i % 2 === 0 ? frontDoc : backDoc).addPage(page);
54
+ }
55
+
56
+ // Guardar PDFs separados
57
+ const frontPath = path.join(path.dirname(inputPath), `${filename}-FRENTE.pdf`);
58
+ const backPath = path.join(path.dirname(inputPath), `${filename}-REVERSO.pdf`);
59
+
60
+ await fs.writeFile(frontPath, await frontDoc.save());
61
+ await fs.writeFile(backPath, await backDoc.save());
62
+
63
+ console.log('\n✅ Booklets creados:\n');
64
+ console.log(` 📄 FRENTE: ${path.basename(frontPath)}`);
65
+ console.log(` 📄 REVERSO: ${path.basename(backPath)}`);
66
+
67
+ console.log('\n📖 INSTRUCCIONES DE IMPRESIÓN:\n');
68
+ console.log('1. Imprime PRIMERO el archivo FRENTE');
69
+ console.log(' → Imprime todas las páginas');
70
+ console.log(' → Deja las hojas EN EL ORDEN que salieron\n');
71
+ console.log('2. Toma la pila de hojas impresas');
72
+ console.log(' → VOLTÉALAS (como voltear una página de libro)');
73
+ console.log(' → Mételas de nuevo en la bandeja\n');
74
+ console.log('3. Imprime el archivo REVERSO');
75
+ console.log(' → Se imprimirá en el reverso de las hojas\n');
76
+ console.log('4. Dobla las hojas por la mitad');
77
+ console.log('5. Encuaderna por el lado IZQUIERDO\n');
78
+
79
+ return { frontPath, backPath };
80
+
81
+ } catch (error) {
82
+ console.error('❌ Error:', error.message);
83
+ throw error;
84
+ }
85
+ }
86
+
87
+ async function main() {
88
+ const args = process.argv.slice(2);
89
+
90
+ if (args.length === 0) {
91
+ console.log('Uso: node create-booklet.js <archivo.pdf>');
92
+ console.log('Ejemplo: node create-booklet.js aws-billing-detective.pdf');
93
+ process.exit(1);
94
+ }
95
+
96
+ const inputPath = path.resolve(args[0]);
97
+
98
+ try {
99
+ await fs.access(inputPath);
100
+ } catch {
101
+ console.error(`❌ Archivo no encontrado: ${inputPath}`);
102
+ process.exit(1);
103
+ }
104
+
105
+ const { frontPath, backPath } = await createBooklet(inputPath);
106
+
107
+ console.log('\n🖨️ Los PDFs están listos para imprimir.');
108
+ console.log('\n✅ Para abrirlos, ejecuta:');
109
+ console.log(` open "${path.basename(frontPath)}"`);
110
+ console.log(` open "${path.basename(backPath)}"`);
111
+
112
+ rl.close();
113
+ console.log('\n¡Listo! 🎉\n');
114
+ }
115
+
116
+ main().catch(console.error);
@@ -0,0 +1,58 @@
1
+ #!/bin/bash
2
+
3
+ # AWS EC2 All Regions Audit
4
+ # Scans every AWS region for EC2 instances and EBS volumes
5
+ # Usage: ./aws-ec2-all.sh
6
+
7
+ echo "🔍 AWS EC2 & EBS Volumes Audit"
8
+ echo "Scanning all regions for instances and volumes..."
9
+ echo ""
10
+
11
+ for region in $(aws ec2 describe-regions --query 'Regions[].RegionName' --output text); do
12
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
13
+ echo "📍 Region: $region"
14
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
15
+ echo ""
16
+
17
+ # Check EC2 Instances
18
+ echo "💻 EC2 Instances:"
19
+ instances=$(aws ec2 describe-instances --region $region --output table 2>/dev/null | grep -v "^$")
20
+ if [ -z "$instances" ]; then
21
+ echo " No instances found"
22
+ else
23
+ echo "$instances"
24
+ fi
25
+ echo ""
26
+
27
+ # Check ALL EBS Volumes
28
+ echo "💾 All EBS Volumes (VolumeId | State | Size | Type):"
29
+ volumes=$(aws ec2 describe-volumes --region $region \
30
+ --query 'Volumes[].[VolumeId,State,Size,VolumeType]' \
31
+ --output table 2>/dev/null | grep -v "^$")
32
+ if [ -z "$volumes" ]; then
33
+ echo " No volumes found"
34
+ else
35
+ echo "$volumes"
36
+ fi
37
+ echo ""
38
+
39
+ # Check AVAILABLE Volumes (💰 MONEY WASTERS!)
40
+ echo "⚠️ AVAILABLE Volumes (NOT ATTACHED - WASTING MONEY!):"
41
+ available=$(aws ec2 describe-volumes --region $region \
42
+ --filters "Name=status,Values=available" \
43
+ --query 'Volumes[].[VolumeId,Size,VolumeType,CreateTime]' \
44
+ --output table 2>/dev/null | grep -v "^$")
45
+ if [ -z "$available" ]; then
46
+ echo " ✅ No unattached volumes (good!)"
47
+ else
48
+ echo "$available"
49
+ echo " 💡 Delete these volumes if you don't need them!"
50
+ fi
51
+ echo ""
52
+ done
53
+
54
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
55
+ echo "✅ Scan complete"
56
+ echo ""
57
+ echo "💡 Pro tip: AVAILABLE volumes are costing you money every month!"
58
+ echo " Use 'aws ec2 delete-volume --volume-id vol-xxxxx' to remove them"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@icarusmx/creta",
3
- "version": "1.5.12",
3
+ "version": "1.5.14",
4
4
  "description": "Salgamos de este laberinto.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -25,6 +25,7 @@
25
25
  "dependencies": {
26
26
  "chalk": "^5.3.0",
27
27
  "chokidar": "^3.5.3",
28
- "node-fetch": "^3.3.2"
28
+ "node-fetch": "^3.3.2",
29
+ "pdf-book": "^0.0.10"
29
30
  }
30
31
  }