claude-code-arcane 1.4.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +5 -4
  3. package/dist/cli.js +2 -0
  4. package/docs/SKILLS-CATALOG.md +30 -3
  5. package/docs/USER-GUIDE.md +2 -2
  6. package/docs/coding-standards.md +26 -0
  7. package/package.json +2 -1
  8. package/profiles/job-hunt.yaml +12 -2
  9. package/profiles/second-brain.yaml +37 -0
  10. package/rules/drafter-reviewer.md +39 -0
  11. package/rules/vault-conventions.md +144 -0
  12. package/skills/brain-dump/SKILL.md +53 -0
  13. package/skills/cold-outreach/SKILL.md +10 -10
  14. package/skills/cold-outreach/references/recruiter-playbook.md +5 -5
  15. package/skills/cover-letter/SKILL.md +8 -8
  16. package/skills/cv-ats-export/SKILL.md +19 -6
  17. package/skills/cv-ats-export/scripts/cv_export.py +49 -4
  18. package/skills/cv-ats-export/scripts/verify_pdf.py +90 -0
  19. package/skills/cv-tailor/SKILL.md +7 -7
  20. package/skills/cv-tailor/references/ats-keywords.md +9 -9
  21. package/skills/hub-note/SKILL.md +68 -0
  22. package/skills/interview-prep/SKILL.md +7 -7
  23. package/skills/interview-prep/references/star-framework.md +10 -10
  24. package/skills/job-aplicar/SKILL.md +88 -0
  25. package/skills/job-hunt/SKILL.md +20 -13
  26. package/skills/job-hunt/references/templates/Aplicacion.md +6 -2
  27. package/skills/job-hunt/references/templates/CV Custom.md +1 -1
  28. package/skills/job-hunt/references/templates/Contacto.md +1 -1
  29. package/skills/job-hunt/references/templates/Dashboard.md +19 -15
  30. package/skills/job-hunt/references/templates/Perfil.md +2 -2
  31. package/skills/job-outcome/SKILL.md +55 -0
  32. package/skills/job-scrape/SKILL.md +95 -0
  33. package/skills/job-scrape/references/dedup-playbook.md +25 -0
  34. package/skills/job-scrape/references/getonbrd-cli.md +62 -0
  35. package/skills/job-scrape/references/getonbrd-urls.md +37 -0
  36. package/skills/job-scrape/references/linkedin-cli.md +64 -0
  37. package/skills/job-scrape/references/linkedin-urls.md +42 -0
  38. package/skills/job-scrape/references/seen_jobs.schema.json +68 -0
  39. package/skills/job-scrape/scripts/getonbrd_search.py +501 -0
  40. package/skills/job-scrape/scripts/linkedin_search.py +428 -0
  41. package/skills/job-search/SKILL.md +22 -19
  42. package/skills/job-search/references/scoring-rubric.md +8 -8
  43. package/skills/job-upskill/SKILL.md +58 -0
  44. package/skills/linkedin-optimize/SKILL.md +5 -5
  45. package/skills/master-profile/SKILL.md +17 -17
  46. package/skills/network-map/SKILL.md +7 -7
  47. package/skills/obsidian-bases/SKILL.md +85 -0
  48. package/skills/obsidian-bases/references/bases-syntax.md +123 -0
  49. package/skills/obsidian-canvas/SKILL.md +91 -0
  50. package/skills/obsidian-markdown/SKILL.md +110 -0
  51. package/skills/pdf-generator/scripts/fill_inspect.py +2 -0
  52. package/skills/pdf-generator/scripts/render_body.py +2 -0
  53. package/skills/personal-brand/SKILL.md +10 -10
  54. package/skills/personal-brand/references/post-pillars.md +14 -14
  55. package/skills/portfolio-site/SKILL.md +11 -11
  56. package/skills/review-dump/SKILL.md +108 -0
  57. package/skills/review-monthly/SKILL.md +56 -0
  58. package/skills/review-weekly/SKILL.md +91 -0
  59. package/skills/second-brain/SKILL.md +211 -0
  60. package/skills/second-brain/hooks/hooks.json +16 -0
  61. package/skills/second-brain/hooks/validate-note.sh +85 -0
  62. package/skills/second-brain/references/cross-project.md +57 -0
  63. package/skills/second-brain/references/templates/Atomic.md +22 -0
  64. package/skills/second-brain/references/templates/Daily.md +25 -0
  65. package/skills/second-brain/references/templates/Dashboard.base +70 -0
  66. package/skills/second-brain/references/templates/Dump.md +11 -0
  67. package/skills/second-brain/references/templates/Hub.md +22 -0
  68. package/skills/second-brain/references/templates/Monthly.md +29 -0
  69. package/skills/second-brain/references/templates/Weekly.md +29 -0
  70. package/skills/second-brain/references/templates/codebases.md +37 -0
  71. package/skills/second-brain/references/templates/hot.md +37 -0
  72. package/skills/second-brain/references/templates/vault-CLAUDE.md +127 -0
  73. package/skills/vault-audit/SKILL.md +72 -0
  74. package/skills/vault-audit/scripts/vault_audit.py +539 -0
  75. package/skills/vault-clip/SKILL.md +73 -0
  76. package/skills/vault-recall/SKILL.md +82 -0
  77. package/skills/vault-recall/scripts/vault_index.py +626 -0
  78. package/skills/vault-tidy/SKILL.md +76 -0
  79. package/skills/xlsx-generator/scripts/formula_check.py +2 -0
  80. package/skills/xlsx-generator/scripts/libreoffice_recalc.py +2 -0
  81. package/skills/xlsx-generator/scripts/xlsx_add_column.py +2 -0
  82. package/skills/xlsx-generator/scripts/xlsx_insert_row.py +2 -0
  83. package/skills/xlsx-generator/scripts/xlsx_reader.py +2 -0
  84. package/skills/zettel/SKILL.md +66 -0
@@ -1,10 +1,10 @@
1
1
  # Recruiter / Hiring Manager Playbook
2
2
 
3
- Plantillas y psicología para outreach de candidato. Adaptá siempre la primera línea — las plantillas son esqueleto, no copy-paste.
3
+ Plantillas y psicología para outreach de candidato. Adaptá siempre la primera línea - las plantillas son esqueleto, no copy-paste.
4
4
 
5
5
  ## Por qué responden (y por qué no)
6
6
 
7
- Responden cuando: el mensaje es corto, claro sobre el fit, fácil de responder, y se nota que es para *ellos*. No responden cuando: es genérico, largo, pide demasiado de entrada, o suena desesperado. El recruiter recibe decenas por día — el filtro es "¿esto me ahorra trabajo o me lo agrega?".
7
+ Responden cuando: el mensaje es corto, claro sobre el fit, fácil de responder, y se nota que es para *ellos*. No responden cuando: es genérico, largo, pide demasiado de entrada, o suena desesperado. El recruiter recibe decenas por día - el filtro es "¿esto me ahorra trabajo o me lo agrega?".
8
8
 
9
9
  ## Plantillas
10
10
 
@@ -37,14 +37,14 @@ Hola {nombre}, me interesa mucho {empresa/área}. Vengo de {1 cosa}.
37
37
  Me encantaría conectar y, si surge, charlar sobre {rol/equipo}.
38
38
  ```
39
39
 
40
- ### 5. Follow-up 1 (día 4–5) — con valor nuevo
40
+ ### 5. Follow-up 1 (día 4-5) - con valor nuevo
41
41
  ```
42
42
  Hola {nombre}, te escribo para reafirmar mi interés en {rol}.
43
43
  Sumo un dato: {proyecto/idea relevante para su producto}.
44
44
  Quedo a disposición para lo que necesiten del proceso.
45
45
  ```
46
46
 
47
- ### 6. Follow-up 2 (día 10–12) — cierre digno
47
+ ### 6. Follow-up 2 (día 10-12) - cierre digno
48
48
  ```
49
49
  Hola {nombre}, entiendo que están con mucho.
50
50
  Dejo la puerta abierta por si avanza {rol}; me encantaría sumar.
@@ -55,7 +55,7 @@ Dejo la puerta abierta por si avanza {rol}; me encantaría sumar.
55
55
 
56
56
  - Máximo 2 follow-ups. Después, soltar.
57
57
  - Cada follow-up agrega algo (no "¿novedades?").
58
- - Espaciar 4–5 días. Nunca el mismo día ni días consecutivos.
58
+ - Espaciar 4-5 días. Nunca el mismo día ni días consecutivos.
59
59
  - Registrar cada toque en la nota de `05-Contactos/`.
60
60
 
61
61
  ## Tono
@@ -7,9 +7,9 @@ user-invocable: true
7
7
  allowed-tools: Read, Glob, Grep, Write, Edit, WebFetch
8
8
  ---
9
9
 
10
- # Cover Letter — Mensaje de aplicación de élite
10
+ # Cover Letter - Mensaje de aplicación de élite
11
11
 
12
- Escribís cover letters y mensajes de aplicación **concisos, seguros y custom** — que suenan a una persona que entendió el rol y la empresa, no a una plantilla. El objetivo: que el lector quiera leer el CV.
12
+ Escribís cover letters y mensajes de aplicación **concisos, seguros y custom** - que suenan a una persona que entendió el rol y la empresa, no a una plantilla. El objetivo: que el lector quiera leer el CV.
13
13
 
14
14
  ## Inputs
15
15
 
@@ -20,19 +20,19 @@ Escribís cover letters y mensajes de aplicación **concisos, seguros y custom**
20
20
 
21
21
  ## Principios
22
22
 
23
- - **Una página máximo; idealmente 150–250 palabras** para mensajes, 250–350 para carta formal.
23
+ - **Una página máximo; idealmente 150-250 palabras** para mensajes, 250-350 para carta formal.
24
24
  - **El primer renglón gana o pierde.** Nada de "I am writing to apply for…". Abrí con un gancho específico: un logro relevante, un punto de conexión con la empresa, o por qué este rol te mueve.
25
- - **Mostrá fit, no historia.** 1–2 logros que mapean directo a lo que piden, con número.
25
+ - **Mostrá fit, no historia.** 1-2 logros que mapean directo a lo que piden, con número.
26
26
  - **Específico de la empresa.** Una frase que demuestre que investigaste (producto, valor, problema que resuelven). Si la podés pegar en otra aplicación, no sirve.
27
27
  - **Confiado, no arrogante; cálido, no servil.** Ni "would be honored", ni "I'm the best candidate".
28
28
  - **CTA simple al final.** Disponibilidad / ganas de charlar, sin rogar.
29
29
 
30
30
  ## Estructura (carta formal)
31
31
 
32
- 1. **Hook** — gancho específico (logro o conexión real).
33
- 2. **Fit** — 1–2 logros mapeados a must-haves del JD, con métrica.
34
- 3. **Empresa** — por qué *esta* empresa/rol (research real).
35
- 4. **Cierre** — CTA breve + gracias.
32
+ 1. **Hook** - gancho específico (logro o conexión real).
33
+ 2. **Fit** - 1-2 logros mapeados a must-haves del JD, con métrica.
34
+ 3. **Empresa** - por qué *esta* empresa/rol (research real).
35
+ 4. **Cierre** - CTA breve + gracias.
36
36
 
37
37
  ## Estructura (mensaje corto / LinkedIn / email)
38
38
 
@@ -1,22 +1,23 @@
1
1
  ---
2
2
  name: cv-ats-export
3
- description: "Export a Markdown CV to an ATS-compliant PDF (single column, selectable text, A4) using headless Chrome/Edge — no Obsidian or design tool needed. Triggers: exportar CV a PDF, generar PDF del CV, CV ATS PDF, convertir CV markdown a PDF, imprimir CV."
3
+ description: "Export a Markdown CV to an ATS-compliant PDF (single column, selectable text, A4) using headless Chrome/Edge - no Obsidian or design tool needed. Triggers: exportar CV a PDF, generar PDF del CV, CV ATS PDF, convertir CV markdown a PDF, imprimir CV."
4
4
  argument-hint: "[cv-name | all] [--workspace <path>]"
5
5
  category: "career"
6
6
  user-invocable: true
7
7
  allowed-tools: Read, Glob, Grep, Bash, Write
8
8
  ---
9
9
 
10
- # CV ATS Export — Markdown → PDF ATS-compliant
10
+ # CV ATS Export - Markdown → PDF ATS-compliant
11
11
 
12
- Convertís un CV en Markdown a un **PDF que pasa filtros ATS**: una sola columna, texto seleccionable (no imagen), A4, tipografía limpia. Usa Chrome o Edge en modo headless — no requiere Obsidian, LaTeX ni herramienta de diseño.
12
+ Convertís un CV en Markdown a un **PDF que pasa filtros ATS**: una sola columna, texto seleccionable (no imagen), A4, tipografía limpia. Usa Chrome o Edge en modo headless - no requiere Obsidian, LaTeX ni herramienta de diseño.
13
13
 
14
- El motor es `scripts/cv_export.py`. La primera vez, instalalo en el workspace (`tools/cv_export.py`) para que quede versionado con los CVs; o corrélo directo desde el skill.
14
+ El motor es `scripts/cv_export.py` + `scripts/verify_pdf.py` (verificación ATS post-export). La primera vez, instalá ambos en el workspace (`tools/`) para que queden versionados con los CVs; o corrélos directo desde el skill (van juntos: `cv_export` importa `verify_pdf` desde su mismo directorio).
15
15
 
16
16
  ## Requisitos
17
17
 
18
18
  - **Python 3** en el PATH.
19
19
  - **Google Chrome o Microsoft Edge** instalado (el script autodetecta rutas estándar en Windows/macOS/Linux).
20
+ - **`pypdf` (opcional)** para la verificación ATS automática: `pip install pypdf`. Sin pypdf el export funciona igual y reporta `[ATS SKIP]`.
20
21
  - CVs en Markdown dentro de `02-CVs/` del career workspace, con headers (`# Nombre`, `## Sección`), bullets y `---` como separadores.
21
22
 
22
23
  ## Uso
@@ -30,6 +31,9 @@ python scripts/cv_export.py "CV - Acme - Backend"
30
31
 
31
32
  # Workspace explícito
32
33
  python scripts/cv_export.py --workspace ./career-workspace "CV - Acme - Backend"
34
+
35
+ # Saltar la verificación ATS
36
+ python scripts/cv_export.py --no-verify "CV - Acme - Backend"
33
37
  ```
34
38
 
35
39
  El script:
@@ -37,6 +41,15 @@ El script:
37
41
  2. Lee el `.md`, separa frontmatter, convierte el cuerpo a HTML con el CSS ATS embebido.
38
42
  3. Imprime a PDF con Chrome/Edge headless (`--headless=new --print-to-pdf`).
39
43
  4. Guarda en `02-CVs/exports/`. El nombre sale del frontmatter `archivo_pdf` si existe; si no, del nombre del `.md` (sin el prefijo `CV - `).
44
+ 5. **Verifica la capa de texto ATS** con `verify_pdf.py` (pypdf): texto extraíble (mínimo 200 chars), que el nombre del H1 y el email sobrevivan al export, y avisa si supera las 2 páginas. Reporta `[ATS OK]` / `[ATS WARN]` / `[ATS SKIP]`; nunca rompe el export. Se salta con `--no-verify`.
45
+
46
+ Los dos `[ATS SKIP]` son distintos: `verify_pdf.py not found next to cv_export.py` significa que copiaste solo un archivo al workspace (instalar pypdf no cambia nada); `pypdf not installed` es el que se resuelve con `pip install pypdf`.
47
+
48
+ `verify_pdf.py` también funciona standalone para chequear cualquier PDF:
49
+
50
+ ```bash
51
+ python scripts/verify_pdf.py "02-CVs/exports/Backend.pdf" --min-chars 200 --max-pages 2 --contains "Jane Doe"
52
+ ```
40
53
 
41
54
  ## Markdown soportado
42
55
 
@@ -51,12 +64,12 @@ El script:
51
64
 
52
65
  ## Personalizar el estilo
53
66
 
54
- El CSS ATS está en la constante `CSS` dentro de `scripts/cv_export.py` (tamaño de fuente, márgenes A4, estilos de h1/h2). Editá ahí si querés ajustar tipografía o espaciado — mantené una sola columna y texto real.
67
+ El CSS ATS está en la constante `CSS` dentro de `scripts/cv_export.py` (tamaño de fuente, márgenes A4, estilos de h1/h2). Editá ahí si querés ajustar tipografía o espaciado - mantené una sola columna y texto real.
55
68
 
56
69
  ## Reglas
57
70
 
58
71
  - No metas tablas, columnas ni imágenes en el CV: rompen el parseo ATS.
59
- - Verificá el PDF generado: que el texto sea seleccionable y entre en 1–2 páginas.
72
+ - Atendé los `[ATS WARN]`: un PDF-imagen o sin el nombre/email extraíbles no pasa filtros ATS. Usá `--no-verify` solo para iterar rápido sobre el layout.
60
73
  - No commitees PDFs con datos sensibles a un remoto sin confirmación.
61
74
 
62
75
  ## Handoff
@@ -8,6 +8,12 @@ Usage (from anywhere; the script locates the career workspace):
8
8
  python cv_export.py # all base CVs (CV - *.md)
9
9
  python cv_export.py "CV - Acme - Backend" # a single CV (with/without .md)
10
10
  python cv_export.py --workspace ./career-workspace "CV - Acme"
11
+ python cv_export.py --no-verify ... # skip the ATS verification
12
+
13
+ After exporting, the PDF text layer is verified with verify_pdf.py (pypdf):
14
+ extractable text, the H1 name and the email must survive the export, and a
15
+ warning is printed above 2 pages. Reports [ATS OK] / [ATS WARN]; if pypdf is
16
+ not installed, [ATS SKIP] without breaking the export.
11
17
 
12
18
  Workspace resolution order:
13
19
  1. --workspace <path> flag
@@ -204,7 +210,41 @@ def find_browser():
204
210
  return None
205
211
 
206
212
 
207
- def export(md_path, browser, cv_dir, export_dir):
213
+ def verify_ats(pdf_path, body):
214
+ """Verify the exported PDF's text layer. Never breaks the export: reports."""
215
+ # Two distinct failures, two distinct messages: verify_pdf.py missing next to
216
+ # this script (installing pypdf would not help) vs pypdf missing, which
217
+ # verify_pdf only imports when it actually reads the PDF.
218
+ try:
219
+ from verify_pdf import verify
220
+ except ImportError:
221
+ print(f" [ATS SKIP] verify_pdf.py not found next to {Path(__file__).name}")
222
+ return
223
+
224
+ contains = []
225
+ m = re.search(r"^#\s+(.+)$", body, re.MULTILINE)
226
+ if m:
227
+ contains.append(re.sub(r"\*|\[|\]\([^)]*\)", "", m.group(1)).strip())
228
+ m = re.search(r"[\w.+-]+@[\w-]+\.[\w.]+", body)
229
+ if m:
230
+ contains.append(m.group(0))
231
+
232
+ try:
233
+ problems = verify(pdf_path, min_chars=200, contains=contains, max_pages=2)
234
+ except ImportError:
235
+ print(" [ATS SKIP] pypdf not installed (pip install pypdf)")
236
+ return
237
+ except Exception as e:
238
+ problems = [f"could not verify: {e}"]
239
+
240
+ if problems:
241
+ for p in problems:
242
+ print(f" [ATS WARN] {p}")
243
+ else:
244
+ print(" [ATS OK] text layer verified (name + email extractable)")
245
+
246
+
247
+ def export(md_path, browser, cv_dir, export_dir, check_ats=True):
208
248
  text = md_path.read_text(encoding="utf-8")
209
249
  fm, body = strip_frontmatter(text)
210
250
 
@@ -252,12 +292,15 @@ def export(md_path, browser, cv_dir, export_dir):
252
292
 
253
293
  size_kb = pdf_path.stat().st_size // 1024
254
294
  print(f"[OK] {md_path.name} -> {pdf_path} ({size_kb} KB)")
295
+ if check_ats:
296
+ verify_ats(pdf_path, body)
255
297
  return True
256
298
 
257
299
 
258
300
  def parse_args(argv):
259
301
  workspace = None
260
302
  names = []
303
+ check_ats = True
261
304
  i = 0
262
305
  while i < len(argv):
263
306
  a = argv[i]
@@ -266,14 +309,16 @@ def parse_args(argv):
266
309
  workspace = argv[i] if i < len(argv) else None
267
310
  elif a.startswith("--workspace="):
268
311
  workspace = a.split("=", 1)[1]
312
+ elif a == "--no-verify":
313
+ check_ats = False
269
314
  else:
270
315
  names.append(a)
271
316
  i += 1
272
- return workspace, names
317
+ return workspace, names, check_ats
273
318
 
274
319
 
275
320
  def main():
276
- workspace, names = parse_args(sys.argv[1:])
321
+ workspace, names, check_ats = parse_args(sys.argv[1:])
277
322
 
278
323
  browser = find_browser()
279
324
  if not browser:
@@ -298,7 +343,7 @@ def main():
298
343
  print(f"[ERROR] No CVs (CV - *.md) found in {cv_dir}.")
299
344
  sys.exit(1)
300
345
 
301
- ok = all([export(t, browser, cv_dir, export_dir) for t in targets])
346
+ ok = all([export(t, browser, cv_dir, export_dir, check_ats) for t in targets])
302
347
  sys.exit(0 if ok else 1)
303
348
 
304
349
 
@@ -0,0 +1,90 @@
1
+ """
2
+ verify_pdf.py — Verify that an exported PDF is ATS-compliant.
3
+
4
+ Checks that the PDF has an extractable text layer (not an image), the
5
+ expected page count, and that given strings (name, email) survived the
6
+ export. Adapted from tools/verify_pdf.py of MadsLorentzen/ai-job-search,
7
+ replacing poppler (pdftotext/pdfinfo) with pypdf so it runs on Windows
8
+ without external binaries.
9
+
10
+ Usage:
11
+ python verify_pdf.py <pdf> [--pages N] [--max-pages N] [--min-chars N]
12
+ [--contains TEXT ...]
13
+
14
+ Example:
15
+ python verify_pdf.py "career-workspace/02-CVs/exports/Backend.pdf" \
16
+ --min-chars 200 --contains "Jane Doe"
17
+
18
+ Exit code 0 if all checks pass, 1 if any check fails, 2 if pypdf is missing.
19
+ """
20
+
21
+ import argparse
22
+ import sys
23
+ from pathlib import Path
24
+
25
+
26
+ def extract_text(pdf_path):
27
+ """Return (page_count, normalized_text) of the PDF."""
28
+ from pypdf import PdfReader
29
+
30
+ reader = PdfReader(str(pdf_path))
31
+ text = "\n".join(page.extract_text() or "" for page in reader.pages)
32
+ return len(reader.pages), " ".join(text.split())
33
+
34
+
35
+ def verify(pdf_path, pages=None, min_chars=1, contains=(), max_pages=None):
36
+ """Return a list of problems (empty = PDF OK). Raises ImportError without pypdf."""
37
+ pdf_path = Path(pdf_path)
38
+ if not pdf_path.exists():
39
+ return [f"does not exist: {pdf_path}"]
40
+
41
+ n_pages, text = extract_text(pdf_path)
42
+ problems = []
43
+
44
+ if pages is not None and n_pages != pages:
45
+ problems.append(f"has {n_pages} pages (expected {pages})")
46
+ if max_pages is not None and n_pages > max_pages:
47
+ problems.append(f"has {n_pages} pages (ideally 1-{max_pages} for a CV)")
48
+ if len(text) < min_chars:
49
+ problems.append(
50
+ f"text layer has {len(text)} extractable chars (minimum {min_chars}); "
51
+ "likely an image-only PDF that an ATS cannot read"
52
+ )
53
+ for needle in contains:
54
+ if " ".join(needle.split()).lower() not in text.lower():
55
+ problems.append(f'missing required text: "{needle}"')
56
+
57
+ return problems
58
+
59
+
60
+ def main():
61
+ ap = argparse.ArgumentParser(description="Verify the ATS text layer of a PDF.")
62
+ ap.add_argument("pdf", help="path to the PDF")
63
+ ap.add_argument("--pages", type=int, default=None, help="expected page count")
64
+ ap.add_argument("--min-chars", type=int, default=1, help="minimum extractable chars")
65
+ ap.add_argument("--max-pages", type=int, default=None, help="warn above N pages")
66
+ ap.add_argument(
67
+ "--contains", action="append", default=[], metavar="TEXT",
68
+ help="string that must appear in the text (repeatable)",
69
+ )
70
+ args = ap.parse_args()
71
+
72
+ try:
73
+ problems = verify(
74
+ args.pdf, args.pages, args.min_chars, args.contains, args.max_pages
75
+ )
76
+ except ImportError:
77
+ print("[ERROR] pypdf is missing. Install with: pip install pypdf")
78
+ sys.exit(2)
79
+
80
+ name = Path(args.pdf).name
81
+ if problems:
82
+ for p in problems:
83
+ print(f"[FAIL] {name}: {p}")
84
+ sys.exit(1)
85
+ print(f"[OK] {name}: ATS text layer verified")
86
+ sys.exit(0)
87
+
88
+
89
+ if __name__ == "__main__":
90
+ main()
@@ -7,9 +7,9 @@ user-invocable: true
7
7
  allowed-tools: Read, Glob, Grep, Write, Edit, WebFetch
8
8
  ---
9
9
 
10
- # CV Tailor — Adaptar el CV a la oferta
10
+ # CV Tailor - Adaptar el CV a la oferta
11
11
 
12
- Tomás un job description y el perfil base del usuario, y producís un **CV custom** que matchea con los filtros automáticos (ATS) y con la lectura humana del hiring manager. No reescribís la verdad — la reorganizás y la traducís al vocabulario de la oferta.
12
+ Tomás un job description y el perfil base del usuario, y producís un **CV custom** que matchea con los filtros automáticos (ATS) y con la lectura humana del hiring manager. No reescribís la verdad - la reorganizás y la traducís al vocabulario de la oferta.
13
13
 
14
14
  ## Inputs
15
15
 
@@ -20,7 +20,7 @@ Tomás un job description y el perfil base del usuario, y producís un **CV cust
20
20
  ## Proceso
21
21
 
22
22
  ### 1. Analizar la oferta
23
- - Extraer **keywords literales** del JD: tecnologías, metodologías, responsabilidades, soft skills, seniority. Literal importa — el ATS hace matching de strings (ej: si piden "React.js" no asumas que "React" matchea igual).
23
+ - Extraer **keywords literales** del JD: tecnologías, metodologías, responsabilidades, soft skills, seniority. Literal importa - el ATS hace matching de strings (ej: si piden "React.js" no asumas que "React" matchea igual).
24
24
  - Detectar **must-have vs nice-to-have**.
25
25
  - Detectar **señales de cultura / ángulo** (startup vs enterprise, code-forward, ownership, etc.).
26
26
  - Ver `references/ats-keywords.md` para cómo extraer y mapear keywords.
@@ -38,17 +38,17 @@ Tomás un job description y el perfil base del usuario, y producís un **CV cust
38
38
  ### 4. Score before/after
39
39
  - Calcular un **match rate** aproximado: % de keywords/requisitos del JD presentes en el CV, **antes** vs **después** del tailoring.
40
40
  - Reportar el delta ("match 48% → 81%") y qué keywords se cubrieron.
41
- - **Flag de keyword stuffing:** marcar cualquier inserción que a un revisor humano le suene forzada o poco natural — el objetivo es pasar el ATS *y* leer bien para una persona.
41
+ - **Flag de keyword stuffing:** marcar cualquier inserción que a un revisor humano le suene forzada o poco natural - el objetivo es pasar el ATS *y* leer bien para una persona.
42
42
 
43
43
  ### 5. Producir el CV custom
44
44
  - Crear/actualizar la nota en `02-CVs/CV - <Empresa> - <Rol>.md` usando el template `CV Custom` (`Templates/CV Custom.md`).
45
45
  - Completar frontmatter (`perfil_base`, `aplicacion`, `empresa`, `archivo_pdf`).
46
- - Documentar las decisiones (keywords, qué se resaltó/bajó) en las secciones del template — sirve para la entrevista y para iterar.
46
+ - Documentar las decisiones (keywords, qué se resaltó/bajó) en las secciones del template - sirve para la entrevista y para iterar.
47
47
  - El cuerpo del CV en sí debe quedar listo para `/cv-ats-export` (markdown limpio: `# nombre`, `## secciones`, bullets, `---`).
48
48
 
49
49
  ## Formato del CV (ATS-friendly)
50
50
 
51
- - **Una columna.** Sin tablas, sin cajas de texto, sin columnas — los ATS las leen mal.
51
+ - **Una columna.** Sin tablas, sin cajas de texto, sin columnas - los ATS las leen mal.
52
52
  - Secciones estándar con headers claros: Summary, Experience, Skills, Projects, Education.
53
53
  - Fechas consistentes (MM/YYYY).
54
54
  - Sin gráficos ni imágenes para el contenido crítico.
@@ -62,7 +62,7 @@ CV en **inglés** para roles internacionales/remotos globales; **español** si l
62
62
 
63
63
  - Nunca inventar experiencia, fechas o métricas. Tailoring = reorganizar y traducir, no mentir.
64
64
  - No keyword-stuffing: cada keyword debe vivir en un bullet verdadero.
65
- - Un CV custom por postulación — no pisar el perfil base ni el maestro.
65
+ - Un CV custom por postulación - no pisar el perfil base ni el maestro.
66
66
  - Después de generar el `.md`, recordar al usuario correr `/cv-ats-export` para el PDF.
67
67
 
68
68
  ## Handoff
@@ -1,4 +1,4 @@
1
- # ATS Keywords — Cómo extraer y mapear
1
+ # ATS Keywords - Cómo extraer y mapear
2
2
 
3
3
  Los Applicant Tracking Systems (Greenhouse, Lever, Workday, Workable, etc.) hacen un primer filtro automático antes de que un humano vea el CV. Entender cómo "leen" cambia el resultado.
4
4
 
@@ -11,12 +11,12 @@ Los Applicant Tracking Systems (Greenhouse, Lever, Workday, Workable, etc.) hace
11
11
 
12
12
  ## Extracción de keywords del JD
13
13
 
14
- 1. **Hard skills / tecnologías** — lenguajes, frameworks, DBs, cloud, herramientas. Copiá la forma **literal** (React.js ≠ React ≠ ReactJS para un match exacto; cuando puedas, incluí la variante que usa la oferta).
15
- 2. **Responsabilidades / verbos** — "design REST APIs", "lead a team", "CI/CD pipelines".
16
- 3. **Metodologías** — Agile, Scrum, TDD, clean architecture.
17
- 4. **Seniority y años** — "5+ years", "senior", "lead".
18
- 5. **Soft skills explícitos** — communication, ownership, cross-functional.
19
- 6. **Señales de cultura / frases gatillo** — ej. "code-forward", "startup mentality", "high autonomy". Suelen indicar qué valoran y dan ángulo para el summary y la cover letter.
14
+ 1. **Hard skills / tecnologías** - lenguajes, frameworks, DBs, cloud, herramientas. Copiá la forma **literal** (React.js ≠ React ≠ ReactJS para un match exacto; cuando puedas, incluí la variante que usa la oferta).
15
+ 2. **Responsabilidades / verbos** - "design REST APIs", "lead a team", "CI/CD pipelines".
16
+ 3. **Metodologías** - Agile, Scrum, TDD, clean architecture.
17
+ 4. **Seniority y años** - "5+ years", "senior", "lead".
18
+ 5. **Soft skills explícitos** - communication, ownership, cross-functional.
19
+ 6. **Señales de cultura / frases gatillo** - ej. "code-forward", "startup mentality", "high autonomy". Suelen indicar qué valoran y dan ángulo para el summary y la cover letter.
20
20
 
21
21
  ## Mapeo contra el perfil
22
22
 
@@ -24,7 +24,7 @@ Los Applicant Tracking Systems (Greenhouse, Lever, Workday, Workable, etc.) hace
24
24
  |---|---|---|---|
25
25
  | React.js | Sí | "Built React + TS frontends @ X" | resaltar arriba |
26
26
  | Kubernetes | Parcial | exposición en CI/CD | mencionar honesto |
27
- | Go | No | — | gap; no inventar |
27
+ | Go | No | - | gap; no inventar |
28
28
 
29
29
  - **Must-have que tenés** → arriba, con espacio, con la palabra exacta.
30
30
  - **Must-have parcial** → mencionar con honestidad ("exposure to…").
@@ -41,6 +41,6 @@ Los Applicant Tracking Systems (Greenhouse, Lever, Workday, Workable, etc.) hace
41
41
  - [ ] Una sola columna, sin tablas/text boxes.
42
42
  - [ ] Headers de sección estándar (Experience, Skills, Education…).
43
43
  - [ ] Fechas consistentes MM/YYYY.
44
- - [ ] Texto seleccionable (no imagen) — el export de `/cv-ats-export` ya lo garantiza.
44
+ - [ ] Texto seleccionable (no imagen) - el export de `/cv-ats-export` ya lo garantiza.
45
45
  - [ ] Nombre del archivo profesional (`Nombre_Apellido_Empresa.pdf`).
46
46
  - [ ] Contacto en el cuerpo, no en el header/footer del documento.
@@ -0,0 +1,68 @@
1
+ ---
2
+ name: hub-note
3
+ description: "Crea o actualiza un hub file (MOC) del vault: una nota por entidad que importa (persona, herramienta, concepto, empresa) que acumula conocimiento y conecta PARA con Zettelkasten. Triggers: hub file, MOC, mapa de contenido, nota de entidad, crear hub de, actualizar hub, nota de persona."
4
+ argument-hint: "[entidad] [--update]"
5
+ category: "pkm"
6
+ user-invocable: true
7
+ allowed-tools: Read, Glob, Grep, Write, Edit
8
+ ---
9
+
10
+ # Hub Note - Tejido conectivo del vault
11
+
12
+ Un hub file es una nota por entidad que importa. No contiene conocimiento propio: contiene **el estado actual** de esa entidad y **los links** a todo lo que se sabe de ella. Es lo que hace que PARA (carpetas) y Zettelkasten (notas planas) convivan sin pelearse.
13
+
14
+ Entidad: `$ARGUMENTS`
15
+
16
+ Aplica la rule `vault-conventions`.
17
+
18
+ **Rutas por rol:** los destinos se nombran por rol y la ruta real de cada uno sale del `## Rutas` del `CLAUDE.md` del vault (leelo antes de escribir). Los defaults entre paréntesis solo aplican si el vault no declara otra cosa.
19
+
20
+ ## Fase 1 - Decidir si la entidad merece hub
21
+
22
+ El criterio es la recurrencia, no la importancia: **la entidad apareció en tres dumps o notas distintas**. Antes de eso es una mención, y un hub por mención produce cientos de notas de una línea que nadie visita.
23
+
24
+ 1. Buscar la entidad con `/vault-recall` si está disponible, o `Grep` en los roles `inbox`, `daily` y `atomic` (defaults `_inbox/`, `Reflect/Daily/`, `03_Resources/`) por el nombre y sus variantes.
25
+ 2. Contar apariciones y reportarlas con los links.
26
+ 3. Si hay menos de tres, decirlo y proponer esperar. Si el usuario quiere el hub igual (una persona nueva en el equipo, un proyecto que arranca), se crea: la anticipación es un motivo válido, la duda no.
27
+
28
+ Verdict: PASS si hay tres apariciones o una razón explícita del usuario.
29
+
30
+ ## Fase 2 - Buscar el hub existente
31
+
32
+ `Glob` en `<hubs>` (default `Hubs/`) y revisar los `aliases` del frontmatter de los hubs que haya. Las entidades llegan con nombres distintos ("Educabot", "educabot", "EB"): si ya existe un hub para la entidad bajo otro nombre, **actualizar ese** y agregar el nombre nuevo a `aliases`. Nunca crear un segundo hub para la misma entidad: es el duplicado más caro del vault, porque parte el conocimiento en dos lugares que nadie reconcilia.
33
+
34
+ ## Fase 3 - Escribir
35
+
36
+ Con approval, crear o actualizar el hub en `<hubs>` (default `Hubs/`) desde `<templates>/Hub.md`.
37
+
38
+ Al **crear**:
39
+ - Título: el nombre canónico de la entidad. Las variantes van a `aliases`.
40
+ - Una o dos líneas de qué es y por qué tiene nota propia.
41
+ - `## Estado actual`: lo que hay que saber hoy.
42
+ - `## Notas`: links a las notas atómicas y proyectos del tema que ya existan (los que encontraste en la fase 1).
43
+ - `## Historial`: una línea por aparición relevante, formato `- YYYY-MM-DD - qué pasó`, lo más nuevo arriba.
44
+ - `status`: `seed` al crearlo, `provisional` cuando ya acumuló contenido. Un hub que el usuario mantiene al día puede quedar `evergreen`, pero lo marca él.
45
+ - `repos:` si la entidad tiene código (una herramienta propia, un proyecto, un servicio). Es el puente al repo, y se traduce a path local en `codebases.md` del vault. **No copies arquitectura al hub:** el hub dice qué es y por qué, el repo dice cómo está hecho, y el que copia se desactualiza.
46
+
47
+ Al **actualizar** (`--update`, o cuando el hub ya existe):
48
+ - Agregar la entrada nueva al `## Historial`, sin duplicar una que ya esté con la misma fecha y el mismo contenido.
49
+ - **`## Estado actual` se reescribe, no se acumula.** Es la sección que responde "qué sé hoy de esto"; si se convierte en un log, el hub deja de servir para lo único que sirve. Lo que se reemplaza y sigue siendo relevante baja al historial.
50
+ - `## Notas`: agregar los links nuevos.
51
+
52
+ ## Fase 4 - Conectar
53
+
54
+ - El hub linkea hacia las notas atómicas y los proyectos. Los dumps y dailies linkean **hacia** el hub. El hub no linkea a dumps ni a dailies: son registro, no conocimiento, y llenan el hub de ruido en un mes.
55
+ - Si el hub cubre un tema amplio y ya pasó las 30 o 40 entradas en `## Notas`, proponer partirlo en sub-hubs por subtema. Un hub que nadie puede leer de arriba a abajo dejó de ser un mapa.
56
+
57
+ Hub COMPLETE cuando tiene estado actual, al menos un link saliente, y las apariciones encontradas quedaron en el historial.
58
+
59
+ ## Reglas
60
+
61
+ - Un hub por entidad. Los alias van al frontmatter, no a un hub nuevo.
62
+ - El hub no acumula conocimiento propio: si querés explicar una idea, es una nota atómica (`/zettel`) linkeada desde acá.
63
+ - Nunca borrar entradas del historial: si algo dejó de ser cierto, se agrega la entrada nueva.
64
+ - Sin guiones largos.
65
+
66
+ ## Handoff
67
+
68
+ Con el hub listo, el siguiente paso es `/zettel` para las ideas del tema que todavía no tienen nota propia, o volver a `/review-dump` si esto salió procesando el día. `/vault-audit` va a usar los hubs para distinguir una huérfana real de una nota que simplemente todavía no fue linkeada.
@@ -7,14 +7,14 @@ user-invocable: true
7
7
  allowed-tools: Read, Glob, Grep, Write, Edit, WebFetch
8
8
  ---
9
9
 
10
- # Interview Prep — Preparación de entrevista
10
+ # Interview Prep - Preparación de entrevista
11
11
 
12
12
  Preparás al usuario para una entrevista concreta: research de la empresa, banco de preguntas probable según rol y ronda, respuestas **STAR** construidas desde su experiencia real, preguntas inteligentes para hacer, y red flags a detectar. El output queda en una nota de `06-Entrevistas/`.
13
13
 
14
14
  ## Inputs
15
15
 
16
16
  - **Aplicación / empresa / rol** (`03-Aplicaciones/`, `04-Empresas/`) y la **ronda** (recruiter screen, técnica, system design, hiring manager, cultural, final).
17
- - **Perfil maestro** (`01-Perfiles/`) — de ahí salen las historias para las respuestas STAR.
17
+ - **Perfil maestro** (`01-Perfiles/`) - de ahí salen las historias para las respuestas STAR.
18
18
  - **Entrevistadores** si se conocen (research de background ayuda).
19
19
 
20
20
  ## Qué producís
@@ -28,19 +28,19 @@ Preparás al usuario para una entrevista concreta: research de la empresa, banco
28
28
 
29
29
  ### 2. Banco de preguntas por ronda
30
30
  - **Recruiter screen:** motivación, expectativas salariales, disponibilidad, "contame de vos".
31
- - **Técnica:** según stack del JD — fundamentos, problemas, debugging, code review.
31
+ - **Técnica:** según stack del JD - fundamentos, problemas, debugging, code review.
32
32
  - **System design** (si aplica): patrones, trade-offs, escala.
33
33
  - **Hiring manager / comportamiento:** liderazgo, conflicto, fracaso, impacto.
34
34
  - **Cultural:** valores, formas de trabajo, por qué esta empresa.
35
- - Marcar las **3–5 más probables** para este rol específico.
35
+ - Marcar las **3-5 más probables** para este rol específico.
36
36
 
37
37
  ### 3. Respuestas STAR
38
38
  - Construir respuestas con el método **STAR** (Situación, Tarea, Acción, Resultado) usando logros reales del perfil maestro.
39
- - Cada respuesta: concreta, con métrica, 60–90 segundos hablada.
39
+ - Cada respuesta: concreta, con métrica, 60-90 segundos hablada.
40
40
  - Ver `references/star-framework.md`.
41
41
 
42
42
  ### 4. Preguntas para hacer
43
- - 5–7 preguntas inteligentes que demuestran research y filtran si la empresa te conviene (equipo, expectativas de los primeros 90 días, cómo miden éxito, deuda técnica, proceso).
43
+ - 5-7 preguntas inteligentes que demuestran research y filtran si la empresa te conviene (equipo, expectativas de los primeros 90 días, cómo miden éxito, deuda técnica, proceso).
44
44
 
45
45
  ### 5. Red flags a detectar
46
46
  - Señales de mala empresa/equipo durante la entrevista (proceso caótico, evasión sobre cultura/turnover, expectativas irreales, falta de claridad de rol, hostilidad).
@@ -56,7 +56,7 @@ Preparás al usuario para una entrevista concreta: research de la empresa, banco
56
56
 
57
57
  1. Leer aplicación + empresa + perfil. WebFetch del sitio/notas de la empresa.
58
58
  2. Crear nota en `06-Entrevistas/` con el template `Entrevista`.
59
- 3. Llenar research, banco de preguntas (marcando las top), STAR de las 3–5 historias clave, preguntas a hacer, red flags.
59
+ 3. Llenar research, banco de preguntas (marcando las top), STAR de las 3-5 historias clave, preguntas a hacer, red flags.
60
60
  4. Ofrecer un **mock**: vos hacés de entrevistador y das feedback sobre las respuestas.
61
61
  5. Post-entrevista: ayudar a registrar feedback y definir próximo paso / follow-up (con `/cold-outreach`).
62
62
 
@@ -1,15 +1,15 @@
1
- # STAR Framework — Respuestas de comportamiento
1
+ # STAR Framework - Respuestas de comportamiento
2
2
 
3
3
  STAR estructura respuestas a preguntas de comportamiento ("contame de una vez que…") para que sean concretas, completas y memorables. La mayoría de los candidatos divaga; STAR te da una historia con resultado.
4
4
 
5
5
  ## Las 4 partes
6
6
 
7
- - **S — Situación:** el contexto, breve. Dónde, cuándo, qué estaba pasando. (1–2 oraciones)
8
- - **T — Tarea:** tu responsabilidad / el problema a resolver. (1 oración)
9
- - **A — Acción:** qué hiciste *vos* (no "el equipo"). El grueso de la respuesta. Decisiones concretas. (2–4 oraciones)
10
- - **R — Resultado:** el impacto, **con número** siempre que se pueda. Qué aprendiste. (1–2 oraciones)
7
+ - **S - Situación:** el contexto, breve. Dónde, cuándo, qué estaba pasando. (1-2 oraciones)
8
+ - **T - Tarea:** tu responsabilidad / el problema a resolver. (1 oración)
9
+ - **A - Acción:** qué hiciste *vos* (no "el equipo"). El grueso de la respuesta. Decisiones concretas. (2-4 oraciones)
10
+ - **R - Resultado:** el impacto, **con número** siempre que se pueda. Qué aprendiste. (1-2 oraciones)
11
11
 
12
- Objetivo: 60–90 segundos hablado. Ni telegrama, ni monólogo de 5 minutos.
12
+ Objetivo: 60-90 segundos hablado. Ni telegrama, ni monólogo de 5 minutos.
13
13
 
14
14
  ## Ejemplo
15
15
 
@@ -20,9 +20,9 @@ Objetivo: 60–90 segundos hablado. Ni telegrama, ni monólogo de 5 minutos.
20
20
  > **A:** Perfilé el hot path, encontré queries N+1 y serialización redundante; introduje caching por sesión y batch de writes, y agregué métricas para validar.
21
21
  > **R:** Latencia -25% y estabilidad del server +40%; duplicamos jugadores concurrentes sin sumar costos. Aprendí a medir antes de optimizar.
22
22
 
23
- ## Banco de historias (preparar 5–6)
23
+ ## Banco de historias (preparar 5-6)
24
24
 
25
- Construí 5–6 historias STAR reutilizables desde el perfil maestro, cubriendo:
25
+ Construí 5-6 historias STAR reutilizables desde el perfil maestro, cubriendo:
26
26
 
27
27
  1. **Logro/impacto** del que estés orgulloso (con métrica).
28
28
  2. **Problema técnico difícil** que resolviste.
@@ -31,11 +31,11 @@ Construí 5–6 historias STAR reutilizables desde el perfil maestro, cubriendo:
31
31
  5. **Liderazgo / iniciativa** (lideraste algo, mentoreaste, empujaste un cambio).
32
32
  6. **Ambigüedad / cambio de prioridades** que manejaste.
33
33
 
34
- La misma historia puede servir para varias preguntas — lo que cambia es qué parte enfatizás.
34
+ La misma historia puede servir para varias preguntas - lo que cambia es qué parte enfatizás.
35
35
 
36
36
  ## Errores comunes
37
37
 
38
- - Quedarse en "nosotros" — la entrevista evalúa *tu* aporte. Decí "yo".
38
+ - Quedarse en "nosotros" - la entrevista evalúa *tu* aporte. Decí "yo".
39
39
  - Saltearse el Resultado, o un resultado sin número.
40
40
  - Demasiada Situación, poca Acción.
41
41
  - Historias vagas/inventadas que no aguantan repreguntas.