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.
- package/CHANGELOG.md +55 -0
- package/README.md +5 -4
- package/dist/cli.js +2 -0
- package/docs/SKILLS-CATALOG.md +30 -3
- package/docs/USER-GUIDE.md +2 -2
- package/docs/coding-standards.md +26 -0
- package/package.json +2 -1
- package/profiles/job-hunt.yaml +12 -2
- package/profiles/second-brain.yaml +37 -0
- package/rules/drafter-reviewer.md +39 -0
- package/rules/vault-conventions.md +144 -0
- package/skills/brain-dump/SKILL.md +53 -0
- package/skills/cold-outreach/SKILL.md +10 -10
- package/skills/cold-outreach/references/recruiter-playbook.md +5 -5
- package/skills/cover-letter/SKILL.md +8 -8
- package/skills/cv-ats-export/SKILL.md +19 -6
- package/skills/cv-ats-export/scripts/cv_export.py +49 -4
- package/skills/cv-ats-export/scripts/verify_pdf.py +90 -0
- package/skills/cv-tailor/SKILL.md +7 -7
- package/skills/cv-tailor/references/ats-keywords.md +9 -9
- package/skills/hub-note/SKILL.md +68 -0
- package/skills/interview-prep/SKILL.md +7 -7
- package/skills/interview-prep/references/star-framework.md +10 -10
- package/skills/job-aplicar/SKILL.md +88 -0
- package/skills/job-hunt/SKILL.md +20 -13
- package/skills/job-hunt/references/templates/Aplicacion.md +6 -2
- package/skills/job-hunt/references/templates/CV Custom.md +1 -1
- package/skills/job-hunt/references/templates/Contacto.md +1 -1
- package/skills/job-hunt/references/templates/Dashboard.md +19 -15
- package/skills/job-hunt/references/templates/Perfil.md +2 -2
- package/skills/job-outcome/SKILL.md +55 -0
- package/skills/job-scrape/SKILL.md +95 -0
- package/skills/job-scrape/references/dedup-playbook.md +25 -0
- package/skills/job-scrape/references/getonbrd-cli.md +62 -0
- package/skills/job-scrape/references/getonbrd-urls.md +37 -0
- package/skills/job-scrape/references/linkedin-cli.md +64 -0
- package/skills/job-scrape/references/linkedin-urls.md +42 -0
- package/skills/job-scrape/references/seen_jobs.schema.json +68 -0
- package/skills/job-scrape/scripts/getonbrd_search.py +501 -0
- package/skills/job-scrape/scripts/linkedin_search.py +428 -0
- package/skills/job-search/SKILL.md +22 -19
- package/skills/job-search/references/scoring-rubric.md +8 -8
- package/skills/job-upskill/SKILL.md +58 -0
- package/skills/linkedin-optimize/SKILL.md +5 -5
- package/skills/master-profile/SKILL.md +17 -17
- package/skills/network-map/SKILL.md +7 -7
- package/skills/obsidian-bases/SKILL.md +85 -0
- package/skills/obsidian-bases/references/bases-syntax.md +123 -0
- package/skills/obsidian-canvas/SKILL.md +91 -0
- package/skills/obsidian-markdown/SKILL.md +110 -0
- package/skills/pdf-generator/scripts/fill_inspect.py +2 -0
- package/skills/pdf-generator/scripts/render_body.py +2 -0
- package/skills/personal-brand/SKILL.md +10 -10
- package/skills/personal-brand/references/post-pillars.md +14 -14
- package/skills/portfolio-site/SKILL.md +11 -11
- package/skills/review-dump/SKILL.md +108 -0
- package/skills/review-monthly/SKILL.md +56 -0
- package/skills/review-weekly/SKILL.md +91 -0
- package/skills/second-brain/SKILL.md +211 -0
- package/skills/second-brain/hooks/hooks.json +16 -0
- package/skills/second-brain/hooks/validate-note.sh +85 -0
- package/skills/second-brain/references/cross-project.md +57 -0
- package/skills/second-brain/references/templates/Atomic.md +22 -0
- package/skills/second-brain/references/templates/Daily.md +25 -0
- package/skills/second-brain/references/templates/Dashboard.base +70 -0
- package/skills/second-brain/references/templates/Dump.md +11 -0
- package/skills/second-brain/references/templates/Hub.md +22 -0
- package/skills/second-brain/references/templates/Monthly.md +29 -0
- package/skills/second-brain/references/templates/Weekly.md +29 -0
- package/skills/second-brain/references/templates/codebases.md +37 -0
- package/skills/second-brain/references/templates/hot.md +37 -0
- package/skills/second-brain/references/templates/vault-CLAUDE.md +127 -0
- package/skills/vault-audit/SKILL.md +72 -0
- package/skills/vault-audit/scripts/vault_audit.py +539 -0
- package/skills/vault-clip/SKILL.md +73 -0
- package/skills/vault-recall/SKILL.md +82 -0
- package/skills/vault-recall/scripts/vault_index.py +626 -0
- package/skills/vault-tidy/SKILL.md +76 -0
- package/skills/xlsx-generator/scripts/formula_check.py +2 -0
- package/skills/xlsx-generator/scripts/libreoffice_recalc.py +2 -0
- package/skills/xlsx-generator/scripts/xlsx_add_column.py +2 -0
- package/skills/xlsx-generator/scripts/xlsx_insert_row.py +2 -0
- package/skills/xlsx-generator/scripts/xlsx_reader.py +2 -0
- package/skills/zettel/SKILL.md +66 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: job-aplicar
|
|
3
|
+
description: "Orchestrate a full job application from a URL or note: fit score, application note, tailored CV, cover letter, fresh-context review and ATS-verified PDF. Sending is always manual. Triggers: aplicar a esta oferta, postularme a, armar la postulacion, pipeline de aplicacion, preparar CV y cover para una oferta, aplicar a este trabajo."
|
|
4
|
+
argument-hint: "<url | nota de aplicación>"
|
|
5
|
+
category: "career"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Bash, Write, Edit, WebFetch, WebSearch, Task
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# /job-aplicar - Pipeline completo de una postulación
|
|
11
|
+
|
|
12
|
+
Orquesta de punta a punta: evaluar fit → nota de aplicación → CV custom → cover → reviewer → PDF ATS → dashboard. Adaptado del `/apply` de MadsLorentzen/ai-job-search a los skills y reglas de este skillset. **El envío final es siempre manual del usuario; este skill nunca marca `estado: aplicado`** (eso lo registra `/job-outcome` después de enviar).
|
|
13
|
+
|
|
14
|
+
Argumento: `$ARGUMENTS` (URL de la oferta, o nombre/wikilink de una nota de `03-Aplicaciones/`).
|
|
15
|
+
|
|
16
|
+
Rutas relativas al career workspace (`--workspace`, env `CAREER_WORKSPACE`, o `./career-workspace/`). Usa los skills `cv-tailor`, `cover-letter` y `cv-ats-export`, la regla `drafter-reviewer` y, si está instalado, los CLIs y el estado de dedup de `job-scrape`.
|
|
17
|
+
|
|
18
|
+
## Paso 0 - Resolver argumento y detectar re-entrada
|
|
19
|
+
|
|
20
|
+
- Si es nombre/wikilink: cargar la nota de `03-Aplicaciones/`.
|
|
21
|
+
- Si es URL: canonicalizarla (reglas en `.claude/skills/job-scrape/references/dedup-playbook.md`) y buscar nota existente por `link_oferta` (Grep). Si existe, modo re-entrada.
|
|
22
|
+
- **Detectar desde los artefactos qué pasos ya están hechos** (no hay archivo de estado; los artefactos SON el estado):
|
|
23
|
+
1. ¿Existe la nota? 2. ¿Tiene `match_score`? 3. ¿`cv_usado` apunta a un CV .md existente en `02-CVs/`? 4. ¿La nota tiene sección "Cover letter" con contenido? 5. ¿Hay resumen de decisiones de review en `## Notas`? 6. ¿Existe el PDF en `02-CVs/exports/` (o `archivo_pdf`)? 7. ¿Hay fila en el dashboard?
|
|
24
|
+
- Informar "retomo desde el paso X" y saltar lo ya hecho.
|
|
25
|
+
|
|
26
|
+
## Paso 1 - Obtener el JD
|
|
27
|
+
|
|
28
|
+
- URL de GetOnBoard → `python .claude/skills/job-scrape/scripts/getonbrd_search.py detail "<url>" --format plain` (si reporta CERRADA, avisar y preguntar si igual seguir).
|
|
29
|
+
- URL de LinkedIn → `python .claude/skills/job-scrape/scripts/linkedin_search.py detail "<url>" --format plain`.
|
|
30
|
+
- Otra fuente, o si `job-scrape` no está instalado → WebFetch.
|
|
31
|
+
- Registrar `fuente` (getonbrd / linkedin / otro).
|
|
32
|
+
|
|
33
|
+
## Paso 2 - Score
|
|
34
|
+
|
|
35
|
+
Aplicar la rúbrica de `.claude/skills/job-search/references/scoring-rubric.md` (si el skill job-search no está instalado, pedir instalarlo o scorear a criterio declarando la limitación) contra el perfil correspondiente de `01-Perfiles/` (elegir por stack del JD; preguntar si es ambiguo). Presentar: score 0-100, breakdown por dimensión, gaps reales, red flags.
|
|
36
|
+
|
|
37
|
+
## Paso 3 - CONFIRMACIÓN 1: ¿proceder?
|
|
38
|
+
|
|
39
|
+
Pedir approval explícito antes de crear cualquier archivo:
|
|
40
|
+
- **Proceder** → paso 4.
|
|
41
|
+
- **Descartar** → si existe `<workspace>/tools/job_scraper/seen_jobs.json`, registrar `status: descartado` + motivo; terminar. No crear nada.
|
|
42
|
+
|
|
43
|
+
## Paso 4 - Nota de aplicación
|
|
44
|
+
|
|
45
|
+
Crear (o completar) la nota desde el template `Templates/Aplicacion.md` del workspace: frontmatter completo (`estado: interesado`, `match_score`, `link_oferta` canónica, `perfil`, `fuente`, `salario_rango`, `modalidad`, `ubicacion`, `prioridad`, `contacto_principal` si se conoce), secciones "Por qué me interesa" y "Match con mi perfil" (alineaciones + gaps reales), línea en Timeline. En el idioma de las notas del workspace, sin guiones largos.
|
|
46
|
+
|
|
47
|
+
## Paso 5 - CV custom
|
|
48
|
+
|
|
49
|
+
Usar el skill **cv-tailor** con el JD y el perfil base: genera `02-CVs/CV - <Empresa> - <Rol>.md`, con keywords ATS de la oferta y delta de match reportado. Setear `cv_usado` en la nota de aplicación.
|
|
50
|
+
|
|
51
|
+
## Paso 6 - Cover letter
|
|
52
|
+
|
|
53
|
+
Usar el skill **cover-letter** según el canal de aplicación (formulario ATS → carta 250-350 palabras; LinkedIn/email → mensaje 150-250). Guardar en la sección "Cover letter (v1)" de la nota.
|
|
54
|
+
|
|
55
|
+
## Paso 7 - Reviewer (regla drafter-reviewer, obligatorio)
|
|
56
|
+
|
|
57
|
+
Seguir `.claude/rules/drafter-reviewer.md`: lanzar UN agente general-purpose de contexto fresco con el JD + borradores inline; lectura acotada al perfil; research web breve de la empresa. Aplicar la Parte A (reemplazos exactos) e incorporar lo relevante de la Parte B. Una sola ronda. Never stuff keywords: los gaps se reconocen en el cover, no se rellenan en el CV. Normalizar guiones largos del texto sugerido.
|
|
58
|
+
|
|
59
|
+
## Paso 8 - CONFIRMACIÓN 2: aprobar CV final
|
|
60
|
+
|
|
61
|
+
Mostrar el CV final (y cover) con las decisiones del review aplicadas. Solo con approval explícito, exportar:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
python .claude/skills/cv-ats-export/scripts/cv_export.py "CV - <Empresa> - <Rol>"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
(o la copia instalada en `<workspace>/tools/`). La verificación ATS corre automática ([ATS OK] esperado; si [ATS WARN], corregir y re-exportar). Registrar la ruta del PDF en la nota (sección "Archivo final exportado" del CV custom).
|
|
68
|
+
|
|
69
|
+
## Paso 9 - Dashboard
|
|
70
|
+
|
|
71
|
+
Agregar/actualizar la fila de la oferta en la sección de aplicaciones activas de `00-Dashboard.md`, respetando las columnas exactas del header, escapando `\|` en wikilinks con alias. Estado ⬜ (interesado).
|
|
72
|
+
|
|
73
|
+
## Paso 10 - Cierre
|
|
74
|
+
|
|
75
|
+
- Resumen de 3-5 decisiones de tailoring en `## Notas` de la nota (qué se enfatizó, ángulo de empresa, gaps reconocidos).
|
|
76
|
+
- Si existe seen_jobs: entrada → `nota_creada` (crearla si no existía).
|
|
77
|
+
- Recordatorio al usuario: **enviar es manual**; el PDF y el cover están listos, el link de aplicación está en la nota y el dashboard. Al enviar, correr `/job-outcome <empresa>` para marcar `aplicado`.
|
|
78
|
+
|
|
79
|
+
## Qué NO hace
|
|
80
|
+
|
|
81
|
+
- No envía la postulación ni marca `aplicado` (ToS de los portales + control de calidad humano).
|
|
82
|
+
- No exporta PDF sin la confirmación 2.
|
|
83
|
+
- No toca otras notas ni otras filas del dashboard.
|
|
84
|
+
- No inventa experiencia: los gaps se reconocen, no se rellenan.
|
|
85
|
+
|
|
86
|
+
## Handoff
|
|
87
|
+
|
|
88
|
+
Con el PDF verificado y el cover aprobados, el paquete queda READY para enviar a mano. Después de enviar: `/job-outcome <empresa>`. Si hay un contacto directo en la empresa, `/cold-outreach` para el mensaje al recruiter/hiring manager.
|
package/skills/job-hunt/SKILL.md
CHANGED
|
@@ -7,9 +7,9 @@ user-invocable: true
|
|
|
7
7
|
allowed-tools: Read, Glob, Grep, Bash, Write, Edit
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
# Job Hunt
|
|
10
|
+
# Job Hunt - Career Workspace Orchestrator
|
|
11
11
|
|
|
12
|
-
Eres el punto de entrada de la suite de búsqueda laboral. Tu trabajo es: detectar (o crear) el **career workspace**, entender en qué etapa está el usuario, y rutear al skill correcto. No hacés todo vos
|
|
12
|
+
Eres el punto de entrada de la suite de búsqueda laboral. Tu trabajo es: detectar (o crear) el **career workspace**, entender en qué etapa está el usuario, y rutear al skill correcto. No hacés todo vos - orquestás.
|
|
13
13
|
|
|
14
14
|
## Idioma
|
|
15
15
|
|
|
@@ -32,26 +32,29 @@ career-workspace/
|
|
|
32
32
|
07-Recursos/ cover letters reutilizables, links
|
|
33
33
|
portfolio/ datos source-of-truth del portfolio web
|
|
34
34
|
Templates/ plantillas con frontmatter
|
|
35
|
-
tools/ cv_export.py (
|
|
35
|
+
tools/ cv_export.py + verify_pdf.py (los instala /cv-ats-export)
|
|
36
|
+
y job_scraper/seen_jobs.json (memoria de dedup de /job-scrape)
|
|
36
37
|
```
|
|
37
38
|
|
|
38
39
|
Es **portable y agnóstico de Obsidian**: usa `[[wikilinks]]` (funcionan en Obsidian si el usuario lo usa, y son legibles en texto plano), frontmatter YAML, y prefijos numéricos para ordenar carpetas. No requiere Obsidian instalado.
|
|
39
40
|
|
|
41
|
+
Estados de una aplicación (frontmatter `estado`): `interesado → aplicado → entrevista → oferta`, con cierres `contratado | rechazado | sin_respuesta | declinada | descartado`. Los mueve `/job-outcome`; ninguna otra skill marca `aplicado`.
|
|
42
|
+
|
|
40
43
|
## Modos
|
|
41
44
|
|
|
42
|
-
### `setup`
|
|
45
|
+
### `setup` - Crear el workspace
|
|
43
46
|
1. Preguntar dónde crear el workspace (default `./career-workspace/`) y el/los rol(es) objetivo.
|
|
44
47
|
2. Crear el árbol de carpetas completo.
|
|
45
48
|
3. Copiar los 7 templates desde `references/templates/` a `Templates/` del workspace.
|
|
46
49
|
4. Crear `00-Dashboard.md` desde `references/templates/Dashboard.md`.
|
|
47
50
|
5. Sugerir el siguiente paso: `/master-profile` para construir el perfil maestro.
|
|
48
51
|
|
|
49
|
-
### `status`
|
|
52
|
+
### `status` - Dónde estoy
|
|
50
53
|
1. Leer `00-Dashboard.md` y escanear `03-Aplicaciones/`.
|
|
51
54
|
2. Resumir: aplicaciones activas por prioridad, próximas entrevistas, ofertas que cierran pronto, gaps (aplicaciones sin CV/cover letter).
|
|
52
55
|
3. Recomendar la acción de mayor impacto.
|
|
53
56
|
|
|
54
|
-
### `next`
|
|
57
|
+
### `next` - Qué hago ahora
|
|
55
58
|
Según el estado del workspace, recomendar el skill siguiente (ver routing).
|
|
56
59
|
|
|
57
60
|
## Routing
|
|
@@ -61,6 +64,10 @@ Según el estado del workspace, recomendar el skill siguiente (ver routing).
|
|
|
61
64
|
| No hay workspace | `/job-hunt setup` |
|
|
62
65
|
| Falta perfil maestro | `/master-profile` |
|
|
63
66
|
| Encontrar/scorear ofertas, plan de búsqueda | `/job-search` |
|
|
67
|
+
| Corrida de búsqueda con CLIs y dedup entre corridas | `/job-scrape` |
|
|
68
|
+
| Postulación completa (nota + CV + cover + review + PDF) | `/job-aplicar` |
|
|
69
|
+
| Registrar resultado/avance de una postulación | `/job-outcome` |
|
|
70
|
+
| Gaps de skills agregados + plan de estudio | `/job-upskill` |
|
|
64
71
|
| Adaptar CV a una oferta | `/cv-tailor` |
|
|
65
72
|
| Exportar CV a PDF ATS | `/cv-ats-export` |
|
|
66
73
|
| Escribir cover letter / mensaje de aplicación | `/cover-letter` |
|
|
@@ -71,17 +78,17 @@ Según el estado del workspace, recomendar el skill siguiente (ver routing).
|
|
|
71
78
|
|
|
72
79
|
## Workflow estándar para una nueva oportunidad
|
|
73
80
|
|
|
74
|
-
1. `/job-
|
|
81
|
+
1. `/job-scrape` → corrida de búsqueda con dedup (o `/job-search` para una oferta/búsqueda puntual): nota en `03-Aplicaciones/` + `04-Empresas/`.
|
|
75
82
|
2. Identificar contacto objetivo → nota en `05-Contactos/`.
|
|
76
|
-
3. `/
|
|
77
|
-
4. `/
|
|
78
|
-
5. `/
|
|
79
|
-
6.
|
|
80
|
-
7.
|
|
83
|
+
3. `/job-aplicar` → pipeline completo de la postulación: CV custom + cover + review (regla drafter-reviewer) + PDF ATS verificado. (A mano: `/cv-tailor` → `/cv-ats-export` → `/cover-letter`.)
|
|
84
|
+
4. `/cold-outreach` → si hay contacto al que escribirle directo.
|
|
85
|
+
5. Enviar la postulación (**siempre manual**) → `/job-outcome <empresa>` para marcar `aplicado`; registrar ahí cada avance.
|
|
86
|
+
6. Cuando haya entrevista → `/interview-prep`.
|
|
87
|
+
7. Con rechazos acumulados → `/job-upskill` para el plan de estudio según gaps reales.
|
|
81
88
|
|
|
82
89
|
## Reglas
|
|
83
90
|
|
|
84
|
-
- Nunca reorganizar carpetas ni renombrar notas sin preguntar
|
|
91
|
+
- Nunca reorganizar carpetas ni renombrar notas sin preguntar - rompe `[[wikilinks]]` en silencio.
|
|
85
92
|
- Nunca commitear datos sensibles (CV con teléfono/dirección, salarios, emails de recruiters) a un remoto sin confirmación explícita.
|
|
86
93
|
- Si una categoría necesita un template que no existe, proponerlo antes de inventarlo.
|
|
87
94
|
- Preservar los prefijos numéricos al crear archivos dentro de las carpetas ordenadas.
|
|
@@ -4,7 +4,8 @@ empresa:
|
|
|
4
4
|
perfil:
|
|
5
5
|
cv_usado:
|
|
6
6
|
estado: interesado
|
|
7
|
-
|
|
7
|
+
match_score:
|
|
8
|
+
fuente:
|
|
8
9
|
fecha_aplicacion:
|
|
9
10
|
fecha_actualizacion: {{date:YYYY-MM-DD}}
|
|
10
11
|
link_oferta:
|
|
@@ -14,6 +15,7 @@ modalidad:
|
|
|
14
15
|
ubicacion:
|
|
15
16
|
prioridad: media
|
|
16
17
|
---
|
|
18
|
+
%% estado: interesado -> aplicado -> entrevista -> oferta | cierres: contratado / rechazado / sin_respuesta / declinada / descartado (los mueve /job-outcome) %%
|
|
17
19
|
|
|
18
20
|
# {{title}}
|
|
19
21
|
|
|
@@ -29,6 +31,8 @@ prioridad: media
|
|
|
29
31
|
- Perfil base: [[]]
|
|
30
32
|
- Qué de mi experiencia alinea con lo que piden:
|
|
31
33
|
-
|
|
34
|
+
- Gaps reales (alimentan /job-upskill):
|
|
35
|
+
-
|
|
32
36
|
|
|
33
37
|
## A quién escribirle / contacto principal
|
|
34
38
|
- [[]]
|
|
@@ -39,7 +43,7 @@ prioridad: media
|
|
|
39
43
|
- [[]]
|
|
40
44
|
|
|
41
45
|
## Timeline / seguimiento
|
|
42
|
-
- {{date:YYYY-MM-DD}}
|
|
46
|
+
- {{date:YYYY-MM-DD}} - creada la nota
|
|
43
47
|
|
|
44
48
|
## Entrevistas
|
|
45
49
|
-
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
tipo: dashboard
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
# Dashboard
|
|
5
|
+
# Dashboard - Búsqueda laboral
|
|
6
6
|
|
|
7
7
|
> Índice del workspace. Cuando haya datos (aplicaciones, entrevistas), estas secciones pueden convertirse en queries dinámicas con Bases/Dataview si usás Obsidian.
|
|
8
8
|
|
|
@@ -16,6 +16,9 @@ tipo: dashboard
|
|
|
16
16
|
**Cerradas / descartadas:**
|
|
17
17
|
-
|
|
18
18
|
|
|
19
|
+
## Leyenda de estados
|
|
20
|
+
⬜ interesado · ✅ aplicado · 📅 entrevista/oferta · ❌ rechazado/sin_respuesta/declinada · 🚫 descartado/cerrada
|
|
21
|
+
|
|
19
22
|
## Próximas entrevistas
|
|
20
23
|
-
|
|
21
24
|
|
|
@@ -26,20 +29,21 @@ tipo: dashboard
|
|
|
26
29
|
-
|
|
27
30
|
|
|
28
31
|
## Accesos rápidos por carpeta
|
|
29
|
-
- `01-Perfiles/`
|
|
30
|
-
- `02-CVs/`
|
|
31
|
-
- `03-Aplicaciones/`
|
|
32
|
-
- `04-Empresas/`
|
|
33
|
-
- `05-Contactos/`
|
|
34
|
-
- `06-Entrevistas/`
|
|
35
|
-
- `07-Recursos/`
|
|
36
|
-
- `portfolio/`
|
|
37
|
-
- `Templates/`
|
|
32
|
+
- `01-Perfiles/` - perfiles profesionales base + perfil maestro
|
|
33
|
+
- `02-CVs/` - versiones de CV (incluye CVs custom por postulación)
|
|
34
|
+
- `03-Aplicaciones/` - una nota por postulación
|
|
35
|
+
- `04-Empresas/` - research de empresas
|
|
36
|
+
- `05-Contactos/` - recruiters, referidos, hiring managers
|
|
37
|
+
- `06-Entrevistas/` - prep y feedback por ronda
|
|
38
|
+
- `07-Recursos/` - cover letters reutilizables, links útiles
|
|
39
|
+
- `portfolio/` - source-of-truth del portfolio web
|
|
40
|
+
- `Templates/` - plantillas con frontmatter
|
|
41
|
+
- `tools/` - cv_export.py, verify_pdf.py y job_scraper/seen_jobs.json
|
|
38
42
|
|
|
39
43
|
## Workflow estándar para una nueva oportunidad
|
|
40
|
-
1. `/job-
|
|
44
|
+
1. `/job-scrape` - corrida de búsqueda con dedup (o `/job-search` para una oferta puntual) → nota en `03-Aplicaciones/` + `04-Empresas/`.
|
|
41
45
|
2. Identificar contacto objetivo → nota en `05-Contactos/`.
|
|
42
|
-
3. `/
|
|
43
|
-
4. `/
|
|
44
|
-
5. `/
|
|
45
|
-
6. `/
|
|
46
|
+
3. `/job-aplicar` - pipeline completo: CV custom + cover + review + PDF ATS verificado.
|
|
47
|
+
4. Enviar (siempre manual) → `/job-outcome <empresa>` para marcar `aplicado` y registrar avances.
|
|
48
|
+
5. `/interview-prep` - cuando haya entrevista.
|
|
49
|
+
6. `/job-upskill` - con rechazos acumulados, plan de estudio según gaps reales.
|
|
@@ -13,10 +13,10 @@ regla_seniority:
|
|
|
13
13
|
fuentes: []
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
-
# Perfil
|
|
16
|
+
# Perfil - {{title}}
|
|
17
17
|
|
|
18
18
|
## Resumen
|
|
19
|
-
> 2
|
|
19
|
+
> 2-3 líneas: quién sos para este rol y qué te hace fuerte.
|
|
20
20
|
|
|
21
21
|
## Stack principal
|
|
22
22
|
-
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: job-outcome
|
|
3
|
+
description: "Record the outcome or progress of a job application (interview, offer, hired, rejected, no reply) updating the application note and the dashboard. Triggers: me respondieron de, registrar resultado, me rechazaron, tengo entrevista con, recibi una oferta, no me contestaron, actualizar estado de la postulacion."
|
|
4
|
+
argument-hint: "[empresa]"
|
|
5
|
+
category: "career"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Write, Edit
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# /job-outcome - Registrar resultado de una aplicación
|
|
11
|
+
|
|
12
|
+
Registrá qué pasó con una postulación: avances (entrevista agendada/hecha, oferta recibida) o resoluciones (contratado, rechazado, sin respuesta, oferta declinada, descartado). Adaptado del `/outcome` de MadsLorentzen/ai-job-search al esquema de notas del career workspace (rutas relativas al workspace: `--workspace`, env `CAREER_WORKSPACE`, o `./career-workspace/`).
|
|
13
|
+
|
|
14
|
+
Empresa/oferta indicada: `$ARGUMENTS`
|
|
15
|
+
|
|
16
|
+
## Pasos
|
|
17
|
+
|
|
18
|
+
1. **Localizar la nota.** Buscar en `03-Aplicaciones/` la nota cuyo nombre o frontmatter `empresa` matchee (case-insensitive) con `$ARGUMENTS`. Si no se pasó argumento o hay ambigüedad, listar las aplicaciones con estado abierto (`interesado`, `aplicado`, `entrevista`, `oferta`) y preguntar cuál.
|
|
19
|
+
|
|
20
|
+
2. **Recolectar qué pasó.** Preguntar al usuario y clasificar:
|
|
21
|
+
- **Progreso:** entrevista agendada / entrevista realizada / oferta recibida → estados `entrevista` u `oferta`.
|
|
22
|
+
- **Resolución:** `contratado` · `rechazado` · `sin_respuesta` · `declinada` (la oferta la rechazó el usuario) · `descartado` (el usuario perdió interés / la oferta cerró).
|
|
23
|
+
Registrar fecha, feedback textual recibido y lecciones aprendidas. **Registrar datos, no interpretarlos:** citar el feedback tal como llegó, sin suavizarlo ni analizarlo acá.
|
|
24
|
+
|
|
25
|
+
3. **Actualizar la nota de aplicación**, con approval del usuario sobre lo recolectado (idempotente - agregar, nunca duplicar ni sobrescribir):
|
|
26
|
+
- Frontmatter: `estado` al nuevo valor y `fecha_actualizacion` a hoy. Compatibilidad: si la nota vieja usa `score:` en vez de `match_score:`, no renombrar el campo, solo tocar `estado`.
|
|
27
|
+
- `## Timeline / seguimiento`: agregar línea `- YYYY-MM-DD - <qué pasó>`.
|
|
28
|
+
- Si hubo entrevista: entrada en `## Entrevistas` (fecha, etapa, entrevistador si se sabe, y link a la nota de `06-Entrevistas/` si existe).
|
|
29
|
+
- Feedback y lecciones van en `## Notas`.
|
|
30
|
+
|
|
31
|
+
4. **Actualizar el dashboard** (`00-Dashboard.md`): buscar la fila de la oferta y actualizar la columna **Estado** con el nuevo estado (usar la leyenda de emojis de abajo, la misma del template Dashboard) y la columna de nota si aporta (ej. "Rechazada 2026-07-13").
|
|
32
|
+
|
|
33
|
+
5. **Sugerir siguiente paso** según el caso:
|
|
34
|
+
- `entrevista` → ofrecer `/interview-prep`.
|
|
35
|
+
- `rechazado` con feedback → anotar la lección en la nota; si se acumulan 3+ resoluciones, ofrecer `/job-upskill` y revisar los criterios de búsqueda contra los resultados reales.
|
|
36
|
+
- `sin_respuesta` a los 10-14 días → ofrecer follow-up con `/cold-outreach`.
|
|
37
|
+
- `contratado` → 🎉 y actualizar el resto de las aplicaciones abiertas (¿retirarse de procesos?).
|
|
38
|
+
|
|
39
|
+
6. **Confirmar** al usuario qué se registró (nota, campos tocados, fila del dashboard).
|
|
40
|
+
|
|
41
|
+
## Vocabulario de `estado` (frontmatter)
|
|
42
|
+
|
|
43
|
+
`interesado → aplicado → entrevista → oferta` y cierra en: `contratado | rechazado | sin_respuesta | declinada | descartado`
|
|
44
|
+
|
|
45
|
+
Emojis del dashboard: ⬜ interesado · ✅ aplicado · 📅 entrevista/oferta · ❌ rechazado/sin_respuesta/declinada · 🚫 descartado/cerrada
|
|
46
|
+
|
|
47
|
+
## Reglas
|
|
48
|
+
|
|
49
|
+
- No editar en masa notas existentes: solo la nota de la aplicación en cuestión y su fila del dashboard.
|
|
50
|
+
- No inventar datos: si falta información (fecha, feedback), preguntar o dejar el campo como está.
|
|
51
|
+
- Sin guiones largos en todo texto agregado.
|
|
52
|
+
|
|
53
|
+
## Handoff
|
|
54
|
+
|
|
55
|
+
Registro COMPLETE cuando nota y dashboard quedaron consistentes. Según el resultado, el siguiente paso es `/interview-prep` (entrevista agendada), `/cold-outreach` (follow-up) o `/job-upskill` (rechazos acumulados).
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: job-scrape
|
|
3
|
+
description: "Search job boards (bundled LinkedIn + GetOnBoard CLIs) with dedup memory across runs, score new openings against a role profile and offer to create application notes. Triggers: buscar ofertas nuevas, scrape de ofertas, correr los CLIs de busqueda, buscar trabajo en LinkedIn o GetOnBoard, nueva corrida de busqueda, ofertas con dedup."
|
|
4
|
+
argument-hint: "[perfil] [--jobage N]"
|
|
5
|
+
category: "career"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Bash, Write, Edit
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# /job-scrape - Búsqueda de ofertas con memoria
|
|
11
|
+
|
|
12
|
+
Corre los CLIs de búsqueda bundleados (LinkedIn + GetOnBoard, en `scripts/`) con los criterios de un perfil, deduplica contra `seen_jobs.json` y contra las notas existentes, scorea lo nuevo y ofrece crear notas de aplicación. Adaptado del skill job-scraper de MadsLorentzen/ai-job-search.
|
|
13
|
+
|
|
14
|
+
Flags y datos que devuelve cada script: `references/linkedin-cli.md` y `references/getonbrd-cli.md`.
|
|
15
|
+
|
|
16
|
+
Argumentos: `$ARGUMENTS` (perfil, opcional; `--jobage N` para override de recencia, default 7 días).
|
|
17
|
+
|
|
18
|
+
Todas las rutas de notas son relativas al **career workspace** (flag `--workspace`, env `CAREER_WORKSPACE`, o `./career-workspace/`; lo crea `/job-hunt setup`). El estado de dedup vive en `<workspace>/tools/job_scraper/seen_jobs.json`.
|
|
19
|
+
|
|
20
|
+
## Requisitos
|
|
21
|
+
|
|
22
|
+
- **Python 3** en el PATH (los scripts usan solo la stdlib: cero dependencias, sin build, sin venv).
|
|
23
|
+
- Career workspace con al menos un perfil en `01-Perfiles/`.
|
|
24
|
+
- Opcional: skill `job-search` instalado (aporta la rúbrica de scoring completa).
|
|
25
|
+
|
|
26
|
+
## Pasos
|
|
27
|
+
|
|
28
|
+
1. **Perfil y criterios.** Si `$ARGUMENTS` no matchea una nota de `01-Perfiles/` (excluir `_index`), listar los perfiles y preguntar cuál usar. Leer del frontmatter: `keywords`, `keywords_excluir`, `match_score_minimo`, `salario_minimo_usd_mes`, `modalidad`, `seniority`, `regla_seniority`, `mercado_objetivo`.
|
|
29
|
+
|
|
30
|
+
2. **Cargar estado.** Leer `<workspace>/tools/job_scraper/seen_jobs.json` (si no existe, crearlo con el esqueleto de `references/seen_jobs.schema.json`; si hay notas previas en `03-Aplicaciones/`, ofrecer backfillearlas como `nota_creada`). Validar que toda entrada `nota_creada` apunte a una nota existente en `03-Aplicaciones/`; reportar punteros rotos sin frenar.
|
|
31
|
+
|
|
32
|
+
3. **Derivar queries.** 3-5 queries con forma de rol desde las keywords del perfil (ej. "fullstack developer", "backend developer"), no fragmentos de skill sueltos.
|
|
33
|
+
|
|
34
|
+
4. **CONFIRMACIÓN: correr los CLIs.** Antes de la primera corrida de la sesión, mostrar el warning y pedir approval explícito (mismo criterio que `/job-search` con Apify):
|
|
35
|
+
|
|
36
|
+
> ⚠️ El CLI de LinkedIn lee páginas públicas de LinkedIn. El acceso automatizado va **contra sus Terms of Service**, y el uso es a tu propia responsabilidad: personal, volumen bajo, nunca comercial ni barridos masivos. GetOnBoard usa su API pública v0 y no tiene esta restricción.
|
|
37
|
+
> ¿Corro ambos CLIs, solo GetOnBoard, o ninguno?
|
|
38
|
+
|
|
39
|
+
Si el usuario elige solo GetOnBoard, saltear LinkedIn y declarar la cobertura reducida en el resumen final.
|
|
40
|
+
|
|
41
|
+
5. **Ejecutar los CLIs** (tolerar el fallo de uno y reportar la degradación):
|
|
42
|
+
```
|
|
43
|
+
python .claude/skills/job-scrape/scripts/linkedin_search.py search -q "<query>" -l "<location>" --remote remote --jobage <N> --format json
|
|
44
|
+
python .claude/skills/job-scrape/scripts/getonbrd_search.py search -q "<query>" --jobage <N> --brief
|
|
45
|
+
```
|
|
46
|
+
La `<location>` de LinkedIn sale del `mercado_objetivo` del perfil; si es worldwide, usar además `-l "Remote"`. Volumen bajo: máximo ~5 queries por CLI por corrida.
|
|
47
|
+
|
|
48
|
+
**`--brief` no es opcional en el barrido:** los JD completos son ~80% del payload de GetOnBoard (~58 KB por query), y el quick score del paso 8 solo necesita los campos estructurados + el arranque de la description. El JD completo se trae en el paso 9, solo para las `high`. Si el CLI de LinkedIn emite un `warning` `NO_CARDS_PARSED` en stderr, la respuesta traía contenido pero el markup cambió: reportarlo como degradación de esa fuente, no como "no hay ofertas".
|
|
49
|
+
|
|
50
|
+
6. **Normalizar y dedup intra-batch.** Canonicalizar URLs (sin utm/tracking; LinkedIn → `jobs/view/<id>`; GetOnBoard `/empleos/` → `/jobs/`), calcular `job_key = slug(empresa)|slug(titulo)` (reglas exactas en `references/dedup-playbook.md`). Misma oferta en ambas fuentes = una entrada con dos `sources[]`.
|
|
51
|
+
|
|
52
|
+
7. **Dedup contra historial:**
|
|
53
|
+
- `seen_jobs.json`: entradas `nota_creada` o `descartado` se omiten de la tabla (contarlas aparte); entradas `new` re-vistas actualizan `last_seen` y se muestran marcadas "vista antes, sin decisión".
|
|
54
|
+
- `03-Aplicaciones/`: Grep por `link_oferta` con las URLs canónicas y por nombre `<Empresa> - <Rol>`. Si hay match no registrado en seen_jobs, registrarlo como `nota_creada` (autocuración).
|
|
55
|
+
- Matching blando (misma empresa + títulos muy parecidos): mostrar warning "posible duplicado de [[nota]]", no dedup automático.
|
|
56
|
+
|
|
57
|
+
8. **Quick score (todas las nuevas)** usando solo datos del listing:
|
|
58
|
+
- `low`: keyword de `keywords_excluir` en el título (o en la description truncada de GetOnBoard); seniority fuera de banda (aplicando `regla_seniority` del perfil si existe); `salary_max < salario_minimo_usd_mes`; o elegibilidad bloqueada (countries/remote_zone excluye el país del usuario según `mercado_objetivo`).
|
|
59
|
+
- `high`: título matchea keywords del perfil + seniority en banda + sin bloqueos.
|
|
60
|
+
- `medium`: el resto. Campos null = desconocido, no penalizar.
|
|
61
|
+
|
|
62
|
+
9. **Score completo solo para las `high`**, con la rúbrica de `.claude/skills/job-search/references/scoring-rubric.md` (si el skill job-search no está instalado, quedarse con el quick score y avisar). El JD completo se trae con `detail` de la fuente que corresponda (`detail <slug>` en GetOnBoard, `detail <id>` en LinkedIn), con un **cap de ~10 fetches por corrida sumando ambas fuentes** (ToS, volumen bajo), priorizando por quick score y recencia. Si las `high` exceden el cap, scorear las que entren y listar el resto con su quick score, declarando el corte.
|
|
63
|
+
|
|
64
|
+
10. **Presentar tabla** ordenada por score: Título | Empresa | Fuente(s) | Salario | Score | Veredicto. Preguntar si alguna se descarta ya (registrar `status: descartado` + `descarte_motivo`).
|
|
65
|
+
|
|
66
|
+
11. **Persistir** todas las entradas nuevas/actualizadas en `seen_jobs.json` y actualizar el campo `actualizado`. JSON válido siempre (es un data file con schema).
|
|
67
|
+
|
|
68
|
+
12. **Ofrecer crear notas** para las de score ≥ `match_score_minimo`, con approval explícito **por oferta** (o "todas") antes de escribir. Por cada confirmada:
|
|
69
|
+
- Nota en `03-Aplicaciones/<Empresa> - <Rol>.md` desde el template `Templates/Aplicacion.md` del workspace, frontmatter completo (`estado: interesado`, `match_score`, `fuente`, `link_oferta`, `salario_rango`, `modalidad`, `ubicacion`, `prioridad`) y sección "Match con mi perfil" con alineaciones y **gaps reales** (alimentan `/job-upskill`).
|
|
70
|
+
- Fila en `00-Dashboard.md`, sección de aplicaciones activas, respetando las columnas exactas del header y escapando `\|` en wikilinks con alias.
|
|
71
|
+
- Entrada seen_jobs → `status: nota_creada` + `nota`.
|
|
72
|
+
|
|
73
|
+
13. **Resumen final:** contadores (encontradas / ya vistas / nuevas / descartadas / notas creadas), fuentes efectivamente consultadas y cortes aplicados (cap de `detail`, fuente degradada), y sugerencia de `/job-aplicar` para las top.
|
|
74
|
+
|
|
75
|
+
## Qué NO hace
|
|
76
|
+
|
|
77
|
+
- No corre el CLI de LinkedIn sin la confirmación del paso 4.
|
|
78
|
+
- No crea notas sin confirmación individual.
|
|
79
|
+
- No genera CVs ni covers (eso es `/job-aplicar`).
|
|
80
|
+
- No marca `estado: aplicado` nunca.
|
|
81
|
+
- No edita notas existentes ni re-scorea ofertas ya con nota.
|
|
82
|
+
- No borra filas del dashboard ni entradas de seen_jobs.
|
|
83
|
+
- No se corre en loop ni excede los caps de volumen.
|
|
84
|
+
|
|
85
|
+
## Reglas
|
|
86
|
+
|
|
87
|
+
- Respetar la matriz de fuente de verdad de `references/dedup-playbook.md`: seen_jobs → nota → dashboard, unidireccional.
|
|
88
|
+
- Notas nuevas en el idioma del workspace, sin guiones largos, con wikilinks.
|
|
89
|
+
- Volumen bajo siempre en LinkedIn (el acceso automatizado va contra sus ToS; uso personal y puntual), y con el warning + approval del paso 4 en la primera corrida de la sesión. El profile pre-aprueba el permiso de Bash, no la decisión: la confirmación la pide el skill.
|
|
90
|
+
- `--brief` en el barrido de GetOnBoard; el JD completo solo vía `detail` para las `high`.
|
|
91
|
+
- Si ambos CLIs fallan, frenar y reportar (no degradar a scraping manual sin avisar).
|
|
92
|
+
|
|
93
|
+
## Handoff
|
|
94
|
+
|
|
95
|
+
Al cerrar la corrida, las ofertas con nota creada quedan READY para el pipeline de postulación: correr `/job-aplicar <nota|url>` con la mejor. Para gaps recurrentes detectados en los scores, `/job-upskill`.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# seen_jobs - estado de deduplicacion de /job-scrape
|
|
2
|
+
|
|
3
|
+
`seen_jobs.json` registra todas las ofertas vistas por `/job-scrape` para no volver a presentarlas en corridas siguientes. Schema formal en `seen_jobs.schema.json` (junto a este archivo). Vive en `<workspace>/tools/job_scraper/` (no entre las notas) porque es dato de maquina, no nota: el vault/Obsidian no lo indexa.
|
|
4
|
+
|
|
5
|
+
## Matriz de fuente de verdad (regla anti-divergencia)
|
|
6
|
+
|
|
7
|
+
| Dato | Fuente de verdad | Quien escribe |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| "Ya vi esta oferta" + triage + descartes pre-nota | `seen_jobs.json` | `/job-scrape` (y `/job-aplicar` al crear o descartar) |
|
|
10
|
+
| Estado del pipeline (`interesado -> aplicado -> ...`), match_score definitivo | Frontmatter de la nota en `03-Aplicaciones/` | `/job-aplicar` crea; `/job-outcome` actualiza |
|
|
11
|
+
| Vista humana de la cola | `00-Dashboard.md` | Derivado de las notas; `/job-aplicar` y `/job-outcome` fila a fila |
|
|
12
|
+
|
|
13
|
+
El flujo es **unidireccional**: seen_jobs -> nota -> dashboard. Cuando una entrada pasa a `status: nota_creada` se congela (solo conserva el puntero `nota`); el pipeline sigue en la nota y NUNCA se sincroniza de vuelta. Por eso no existe el estado `aplicado` en este archivo.
|
|
14
|
+
|
|
15
|
+
## Claves de dedup
|
|
16
|
+
|
|
17
|
+
1. **URL canonica** (por fuente): sin utm/tracking params; LinkedIn reducido a `linkedin.com/jobs/view/<id>`; GetOnBoard `/empleos/` -> `/jobs/`.
|
|
18
|
+
2. **`job_key`** = `slug(empresa)|slug(titulo)`: lowercase, sin acentos, sin puntuacion (conserva `.` interno: coderslab.io, .net), sin sufijos legales (inc, llc, sa...), sin calificadores del titulo entre parentesis ("(remote)", "(latam)"). Detecta la misma oferta publicada en dos fuentes con URLs distintas; se agrega a `sources[]` de la entrada existente en vez de duplicar.
|
|
19
|
+
3. Matching blando (misma empresa + alto solapamiento de tokens del titulo) = **warning** en `/job-scrape`, nunca dedup automatico (una empresa puede tener dos roles parecidos legitimos, o el mismo rol con titulo traducido).
|
|
20
|
+
|
|
21
|
+
## Mantenimiento
|
|
22
|
+
|
|
23
|
+
- **Punteros rotos:** `/job-scrape` valida al arrancar que toda entrada `nota_creada` apunte a una nota existente y reporta las rotas.
|
|
24
|
+
- **Poda (opt-in):** entradas `new`/`descartado` con `last_seen` de mas de 90 dias se pueden borrar. Solo a pedido del usuario, nunca automatico. Las `nota_creada` no se podan (son el historial de dedup contra aplicaciones reales).
|
|
25
|
+
- **Primer run:** el archivo arranca vacio (esqueleto del schema). Si ya existen notas en `03-Aplicaciones/`, `/job-scrape` ofrece backfillearlas como `nota_creada` (con `quick_score: null` y `url: "nota://<archivo>"` si la nota no tiene `link_oferta`).
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# GetOnBoard Search CLI
|
|
2
|
+
|
|
3
|
+
Busca ofertas en GetOnBoard (job board tech de LATAM) via su API pública v0. Sin auth, sin API key, **cero dependencias**: solo la stdlib de Python 3. Espejo del contrato de `linkedin_search.py` para que `/job-scrape` pueda usar ambos de forma intercambiable.
|
|
4
|
+
|
|
5
|
+
El script es `scripts/getonbrd_search.py`. Construido a medida siguiendo el patrón `/add-portal` de MadsLorentzen/ai-job-search (investigar endpoint → scaffold con el mismo contrato → test en vivo), y portado de TypeScript a Python por la regla de lenguaje por capa (`docs/coding-standards.md`); la salida de las dos versiones se comparó campo por campo sobre las mismas queries: identica.
|
|
6
|
+
|
|
7
|
+
## Uso
|
|
8
|
+
|
|
9
|
+
### Buscar ofertas
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
python .claude/skills/job-scrape/scripts/getonbrd_search.py search -q "<keywords>" [flags]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Flags:
|
|
16
|
+
- `-q / --query <texto>` - **requerido**, minimo 3 chars. La API hace AND de todos los terminos.
|
|
17
|
+
- `--jobage <dias>` - publicada en los ultimos N dias (filtro client-side sobre `published_at`).
|
|
18
|
+
- `--remote <modo>` - `remote` | `hybrid` | `onsite` (filtro client-side).
|
|
19
|
+
- `--page <n>` - paginacion (20 resultados por pagina).
|
|
20
|
+
- `-n / --limit <n>` - tope de resultados (`0` no emite ninguno).
|
|
21
|
+
- `--brief` - solo en `json`: trunca cada `description` a 300 chars y marca `description_truncated`. Los JD completos son ~80% del payload, asi que para triage conviene `--brief` y despues `detail` del shortlist.
|
|
22
|
+
- `--format json|table|plain` - default `json`.
|
|
23
|
+
|
|
24
|
+
Sin flag `--location`: GetOnBoard es LATAM/remoto por naturaleza; filtrar con `--remote` y leer `countries` en el JSON.
|
|
25
|
+
|
|
26
|
+
### Detalle de una oferta
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
python .claude/skills/job-scrape/scripts/getonbrd_search.py detail <slug|url> [--format json|plain]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Acepta el `ID` (slug) de los resultados de search o cualquier URL de getonbrd.com (`/jobs/...` o `/empleos/...`, con o sin categoria). Dos capas:
|
|
33
|
+
1. **Search API** (queries progresivas derivadas del slug): datos completos, incluye `applications_count`.
|
|
34
|
+
2. **Microdata fallback** (pagina SSR, schema.org JobPosting): para ofertas viejas o fuera del indice. Detecta y marca ofertas **CERRADAS**.
|
|
35
|
+
|
|
36
|
+
## Datos que devuelve (JSON)
|
|
37
|
+
|
|
38
|
+
`id` (slug), `title`, `company`, `location`, `date` (ISO), `url`, `seniority` (Junior/Semi Senior/Senior/Expert), `remote`, `remote_modality` (`remote_local`/`hybrid`/`no_remote`...), `remote_zone`, `countries`, `salary_min_usd_month`/`salary_max_usd_month` (USD/mes cuando esta publicado), `applications_count` (util para la dimension "calidad de la oferta" de la rubrica), `description` (texto plano).
|
|
39
|
+
|
|
40
|
+
## Ejemplos
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Fullstack, ultimas 2 semanas
|
|
44
|
+
python .claude/skills/job-scrape/scripts/getonbrd_search.py search -q "fullstack developer" --jobage 14 --format table
|
|
45
|
+
|
|
46
|
+
# Triage barato: estructurado completo, JD truncado
|
|
47
|
+
python .claude/skills/job-scrape/scripts/getonbrd_search.py search -q "fullstack developer" --jobage 14 --brief
|
|
48
|
+
|
|
49
|
+
# .NET remoto
|
|
50
|
+
python .claude/skills/job-scrape/scripts/getonbrd_search.py search -q ".NET developer" --remote remote --format table
|
|
51
|
+
|
|
52
|
+
# JD completo de una oferta
|
|
53
|
+
python .claude/skills/job-scrape/scripts/getonbrd_search.py detail "https://www.getonbrd.com/jobs/programming/full-stack-developer-buildwithin-remote-b1ef" --format plain
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Notas
|
|
57
|
+
|
|
58
|
+
- API publica en beta (`api/v0`); si cambia, el fallback de microdata sigue funcionando.
|
|
59
|
+
- Errores a **stderr** como `{ "error": "...", "code": "..." }` con exit code `1`.
|
|
60
|
+
- Volumen bajo por cortesia; la API es publica pero no esta pensada para bulk scraping.
|
|
61
|
+
- Referencia de endpoints: `getonbrd-urls.md`.
|
|
62
|
+
- Tests de los parsers: `tests/job_scrape/test_getonbrd_search.py` en el repo de Arcane.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# GetOnBoard API v0 - referencia de endpoints (verificada 2026-07-13)
|
|
2
|
+
|
|
3
|
+
Base: `https://www.getonbrd.com/api/v0` - sin autenticacion para endpoints publicos. Formato JSON:API.
|
|
4
|
+
|
|
5
|
+
## Search de ofertas
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
GET /search/jobs?query=<q>&per_page=<n>&page=<n>&expand[]=seniority&expand[]=company
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- `query`: requerido, min 3 chars. Hace AND de todos los terminos (queries largas sobre-restringen).
|
|
12
|
+
- `per_page` / `page`: paginacion; `meta: { page, per_page, total_pages }` en la respuesta.
|
|
13
|
+
- `expand[]`: resuelve las referencias `seniority` y `company` inline (sin expand vienen solo como `{id, type}`).
|
|
14
|
+
|
|
15
|
+
Respuesta por job (`data[]`): `id` = slug, `links.public_url`, y en `attributes`:
|
|
16
|
+
`title`, `description` (HTML), `functions` (HTML), `desirable` (HTML), `benefits` (HTML), `remote` (bool), `remote_modality` (`remote_local` | `hybrid` | `no_remote` | ...), `remote_zone`, `countries[]`, `min_salary`/`max_salary` (USD/mes, null si no publican), `published_at` (unix), `applications_count`, `seniority.data.attributes.name`, `company.data.attributes.name`, `lang`, `perks[]`.
|
|
17
|
+
|
|
18
|
+
## Catalogos publicos
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
GET /seniorities -> Junior=2, Semi Senior=3, Senior=4, Expert=5, Sin experiencia=1
|
|
22
|
+
GET /categories -> categorias (programming, design-ux, etc.)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Lo que NO es publico
|
|
26
|
+
|
|
27
|
+
- `GET /jobs/<slug>` devuelve **401** (endpoint privado, requiere API key de cuenta).
|
|
28
|
+
Por eso `detail` del script usa search + filtro por id exacto, con fallback a microdata.
|
|
29
|
+
|
|
30
|
+
## Paginas SSR (fallback de detail)
|
|
31
|
+
|
|
32
|
+
- `https://www.getonbrd.com/jobs/<slug>` redirige 301 a `/jobs/<categoria>/<slug>`.
|
|
33
|
+
- La pagina renderiza microdata schema.org JobPosting via atributos `itemprop`:
|
|
34
|
+
`title`, `hiringOrganization > name`, `datePosted`, `baseSalary > minValue/maxValue`,
|
|
35
|
+
`description`, `qualifications` (seniority), `address`.
|
|
36
|
+
- Ofertas cerradas: pierden el `itemtype="http://schema.org/JobPosting"` pero conservan
|
|
37
|
+
los `itemprop`; la pagina contiene el texto "Closed job" / "No longer accepting".
|