Files
qwen3-6-lora/data/raw/sanitized/plans/quiero-hacer-una-skill-sprightly-moore.md
T

7.6 KiB

Skill global: cargar contexto de Docmost al iniciar conversación

Contexto

El usuario quiere que, al iniciar cualquier conversación de Claude Code, se cargue automáticamente el contexto del proyecto actual desde Docmost (MCP mcp__docmost__*): listar los spaces, identificar cuál corresponde al proyecto en el que se está trabajando, y leer el contenido completo de todas sus páginas.

Investigación realizada (en modo solo-lectura):

  • No existe ningún CLAUDE.md global ni hook SessionStart hoy en ~/.claude/settings.json (solo hay un hook Notification para email vía Gmail).
  • Las skills se activan por coincidencia de descripción, no automáticamente al iniciar sesión. Para que algo corra siempre al inicio, el único mecanismo real es un hook SessionStart, cuyo stdout se inyecta como contexto adicional (esto es lo que documenta hooks-guide.md de Claude Code, ej. "Re-inject context after compaction"). El hook (bash) no puede llamar herramientas MCP directamente — eso solo lo puede hacer el modelo. Por eso el diseño es: hook (bash) → inyecta instrucción → skill (markdown) → modelo ejecuta las llamadas MCP reales.
  • Ya existe en background-orchestrator/SKILL.md una lógica de detección de proyecto reutilizable (package.jsonpyproject.toml → basename del repo git), que se reutiliza aquí tal cual.
  • Verifiqué en vivo el MCP de Docmost: list_spaces devuelve {id, name, slug, description, ...}; list_pages({spaceId}) devuelve el árbol de páginas (id, title, parentPageId) sin contenido; get_page({pageId}) sí devuelve el contenido completo (content, markdown). Los nombres de space no siempre calzan literal con el nombre del repo (ej. space "Create React SSR App" vs. un repo en kebab-case), así que el match debe ser normalizado (minúsculas, sin espacios/guiones), no exacto. Algunos spaces ya tienen 30+ páginas con contenido extenso.

Decisiones confirmadas con el usuario:

  1. Leer todo siempre — al detectar el space, leer el contenido completo de todas sus páginas (sin recortar ni resumir antes de cargar).
  2. Si no hay match — preguntar al usuario (mostrar los spaces disponibles y dejar que elija uno o indique que no corresponde ninguno).
  3. Alcance — el hook solo actúa si el cwd está dentro de un repo git; fuera de un repo no intenta nada.

Diseño

1. Script del hook — ~/.claude/hooks/docmost-session-start.sh (nuevo, chmod +x)

Bash puro, sin llamadas MCP (no puede hacerlas). Solo detecta el proyecto y, si aplica, imprime a stdout una instrucción para que el modelo invoque la skill. Si no está en un repo git, no imprime nada (exit 0 silencioso → no se inyecta contexto ni se gasta turno en esto).

#!/bin/bash
# Solo actuar dentro de un repo git (decisión confirmada con el usuario)
git rev-parse --show-toplevel >/dev/null 2>&1 || exit 0

PROJECT_NAME=$(jq -r '.name // empty' package.json 2>/dev/null)
[ -z "$PROJECT_NAME" ] && PROJECT_NAME=$(grep -oP '^\s*name\s*=\s*"\K[^"]+' pyproject.toml 2>/dev/null | head -1)
[ -z "$PROJECT_NAME" ] && PROJECT_NAME=$(basename "$(git rev-parse --show-toplevel)")

cat <<EOF
[docmost-context] Nueva conversación detectada en el proyecto git "$PROJECT_NAME" ($(pwd)).
Antes de responder al primer mensaje del usuario, invoca la skill "docmost-context" (Skill tool,
skill: docmost-context, args: "$PROJECT_NAME") para cargar el contexto del proyecto desde Docmost.
Hazlo una sola vez al inicio de esta conversación; no la vuelvas a invocar en turnos posteriores salvo
que el usuario lo pida explícitamente.
EOF

2. Registrar el hook en ~/.claude/settings.json (editar)

Añadir la clave SessionStart dentro de hooks (se conserva el Notification existente). Se registran tres matchers por separado (startup, resume, clear) — deliberadamente sin compact, para no releer 30+ páginas completas cada vez que el contexto se compacta a mitad de conversación:

"SessionStart": [
  { "matcher": "startup", "hooks": [{ "type": "command", "command": "/home/aleleba/.claude/hooks/docmost-session-start.sh" }] },
  { "matcher": "resume",  "hooks": [{ "type": "command", "command": "/home/aleleba/.claude/hooks/docmost-session-start.sh" }] },
  { "matcher": "clear",   "hooks": [{ "type": "command", "command": "/home/aleleba/.claude/hooks/docmost-session-start.sh" }] }
]

También añadir a permissions.allow: "mcp__docmost__list_spaces" (los demás —list_pages, get_page, search— ya están permitidos desde background-orchestrator).

3. Skill global — ~/.claude/skills/docmost-context/SKILL.md (nuevo)

---
name: docmost-context
description: >-
  Carga el contexto del proyecto actual desde Docmost (MCP) al inicio de una conversación: lista los
  spaces, identifica cuál corresponde al proyecto del directorio actual, y lee el contenido completo
  de todas sus páginas. Se activa automáticamente vía el hook SessionStart (ver
  ~/.claude/hooks/docmost-session-start.sh) — no requiere que el usuario la invoque a mano, aunque
  también responde a "carga el contexto de Docmost", "lee el proyecto en Docmost", "/docmost-context".
effort: medium
argument-hint: "[nombre del proyecto, si ya se conoce]"
---

Cuerpo (workflow):

  1. Determinar el nombre del proyecto: usar el argumento recibido si viene; si no, repetir la misma detección de background-orchestrator (package.jsonpyproject.toml → basename del repo git).
  2. Listar spaces: mcp__docmost__list_spaces.
  3. Buscar coincidencia comparando el nombre del proyecto contra name y slug de cada space, normalizando (minúsculas, sin espacios/guiones/guiones bajos) — match exacto normalizado, no substring débil.
    • Una sola coincidencia → usarla directamente, sin confirmar con el usuario.
    • Cero coincidencias → usar AskUserQuestion mostrando la lista de spaces disponibles, dejando elegir uno existente o responder que ninguno corresponde (en ese caso, no cargar nada y continuar la conversación normalmente).
    • Varias coincidencias ambiguas → preguntar igual, mostrando solo los candidatos ambiguos.
  4. Leer el space completo: mcp__docmost__list_pages({spaceId}) para obtener todas las páginas, y luego mcp__docmost__get_page({pageId}) para cada una (contenido completo, sin omitir ninguna).
  5. No repetir el dump al usuario: una vez cargado el contenido al contexto, responder con un mensaje breve (1-2 líneas) indicando qué space y cuántas páginas se cargaron — no transcribir el contenido de las páginas en el chat.
  6. Ejecutar esto una sola vez por conversación (al primer turno, disparado por el hook); no repetir en turnos siguientes salvo pedido explícito del usuario.

Verificación

  1. chmod +x ~/.claude/hooks/docmost-session-start.sh y ejecutarlo manualmente dentro de un repo git y fuera de uno, confirmando que imprime la instrucción solo en el primer caso.
  2. Iniciar una conversación nueva (startup) dentro de un repo con space conocido (ej. Ro-ut) y confirmar que el modelo invoca la skill sin que el usuario lo pida, y que reporta el space/páginas cargadas en 1-2 líneas.
  3. Probar dentro de un repo sin space coincidente y confirmar que se dispara AskUserQuestion con la lista de spaces.
  4. Si en la prueba real el stdout plano del hook no llega como contexto, usar como fallback el formato JSON documentado: {"hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": "..."}}.
  5. Confirmar que /clear también dispara la carga, y que compact (compactación automática) no la repite.