Files
skills-here-run-place/PLAN.md
Alejandro Martinez f09af719cf Initial commit
2026-02-12 02:04:10 +01:00

6.3 KiB

Skillit - Plan de Implementacion

Contexto

App web para gestionar y distribuir Claude Code skills. Los usuarios suben/editan skills via web, y Claude Code las descarga ejecutando un script de sync (curl ... | bash).

Stack: Astro 5 (SSR) + Vue 3 (islands) + TailwindCSS 4 + Node adapter Deploy: Coolify (Docker) Storage: Filesystem directo (no Content Collections, no DB) Auth: Ninguna


Decision arquitectonica clave

No usar Astro Content Collections. Las Content Collections cachean datos en build time, pero esta app necesita CRUD en tiempo real. Usamos gray-matter + fs/promises directamente. Los skills se guardan en data/skills/ (fuera de src/content/) para evitar conflictos con Astro.


Estructura de archivos

skillit/
├── astro.config.mjs
├── tsconfig.json
├── package.json
├── Dockerfile
├── .dockerignore
├── data/
│   └── skills/                         # Skills .md (target del CRUD)
│       └── example-skill.md            # Seed data
├── src/
│   ├── styles/global.css               # @import "tailwindcss"
│   ├── lib/skills.ts                   # CRUD filesystem helpers
│   ├── components/
│   │   ├── SkillCard.astro             # Card para el catalogo
│   │   ├── SkillEditor.vue             # Editor markdown + preview
│   │   └── DeleteButton.vue            # Boton eliminar con confirmacion
│   ├── layouts/Base.astro              # HTML shell, nav, CSS
│   └── pages/
│       ├── index.astro                 # Catalogo (grid de cards)
│       ├── skills/
│       │   ├── [slug].astro            # Ver skill (markdown renderizado)
│       │   ├── new.astro               # Crear skill (monta SkillEditor)
│       │   └── [slug]/edit.astro       # Editar skill
│       └── api/
│           ├── skills/index.ts         # GET lista + POST crear
│           ├── skills/[slug].ts        # GET raw + PUT + DELETE
│           └── sync.ts                 # GET -> script bash de sync

Fases de implementacion

Fase 0: Scaffolding

  1. Crear proyecto Astro en el directorio actual

    npm create astro@latest . -- --template minimal --typescript strict --install --git
    
  2. Instalar dependencias

    npx astro add node vue tailwind
    npm install gray-matter marked
    
  3. Configurar astro.config.mjs

    • output: 'server' (SSR)
    • adapter: node({ mode: 'standalone' })
    • Integraciones: Vue, TailwindCSS vite plugin
  4. src/styles/global.css: solo @import "tailwindcss"

  5. Crear data/skills/ y el seed example-skill.md

Fase 1: Core library

  1. src/lib/skills.ts - Modulo central de CRUD:
    • listSkills() - lee directorio, parsea con gray-matter
    • getSkill(slug) - lee un .md, devuelve null si no existe
    • createSkill(slug, content) - escribe .md, error si ya existe
    • updateSkill(slug, content) - sobreescribe .md
    • deleteSkill(slug) - elimina .md
    • isValidSlug() - valida /^[a-z0-9][a-z0-9-]*[a-z0-9]$/, max 64 chars
    • SKILLS_DIR configurable via env var, default data/skills/

Fase 2: API endpoints

  1. src/pages/api/skills/index.ts

    • GET: lista skills como JSON [{slug, name, description, allowedTools}]
    • POST: crea skill, body {slug, content}, returns 201/400/409
  2. src/pages/api/skills/[slug].ts

    • GET: devuelve raw .md (Content-Type: text/markdown)
    • PUT: actualiza skill, body {content}, returns 200/404
    • DELETE: elimina skill, returns 204/404
  3. src/pages/api/sync.ts

    • GET: genera script bash que:
      • Crea ~/.claude/skills/ si no existe
      • Para cada skill: mkdir -p + curl del raw .md a SKILL.md
    • Uso: curl -fsSL https://skillit.example.com/api/sync | bash

Fase 3: UI read-only

  1. src/layouts/Base.astro - HTML shell con nav (logo + link "New Skill")

  2. src/components/SkillCard.astro - Card con nombre, descripcion truncada, badges de tools

  3. src/pages/index.astro - Catalogo: llama listSkills(), renderiza grid de SkillCards. Empty state si no hay skills.

  4. src/pages/skills/[slug].astro - Vista detalle: renderiza markdown con marked, muestra metadata, botones Edit/Delete

Fase 4: UI write

  1. src/components/SkillEditor.vue (island client:load)

    • Props: initialContent?, slug?, mode: 'create' | 'edit'
    • Layout 2 paneles: textarea izquierda + preview derecha
    • Campos de formulario arriba: name (auto-genera slug), description, allowed-tools
    • Preview en tiempo real con marked (debounced 300ms)
    • Save: POST o PUT segun mode, redirect al detalle
  2. src/pages/skills/new.astro - Monta SkillEditor en modo create

  3. src/pages/skills/[slug]/edit.astro - Carga skill, monta SkillEditor en modo edit con datos

  4. src/components/DeleteButton.vue (island client:load)

    • Prop: slug
    • Click -> confirm -> fetch DELETE -> redirect a /

Fase 5: Deployment

  1. Dockerfile (multi-stage):

    • Build: node:22-alpine, npm install, npm run build
    • Runtime: copia dist/ + node_modules (prod) + data/skills/
    • ENV SKILLS_DIR=/app/data/skills
    • CMD ["node", "./dist/server/entry.mjs"]
    • Puerto 4321
  2. .dockerignore: node_modules, dist, .git, .env


Verificacion

Tras Fase 2 (API):

npm run dev
curl http://localhost:4321/api/skills              # JSON array
curl http://localhost:4321/api/skills/example-skill # raw .md

Tras Fase 3 (UI read-only):

  • Visitar / -> ver card del example-skill
  • Click card -> ver skill renderizada

Tras Fase 4 (CRUD completo):

  • /skills/new -> crear skill -> aparece en catalogo
  • Editar skill -> cambios persistidos
  • Eliminar skill -> desaparece
  • curl http://localhost:4321/api/sync -> script bash funcional
  • curl -fsSL http://localhost:4321/api/sync | bash && ls ~/.claude/skills/

Tras Fase 5 (Docker):

docker build -t skillit .
docker run -p 4321:4321 -v skillit-data:/app/data/skills skillit

Notas para Coolify

  • Build pack: Dockerfile
  • Volumen persistente: montar en /app/data/skills para que los skills sobrevivan rebuilds
  • Puerto: 4321
  • Env var opcional: SKILLS_DIR (default ya configurado en Dockerfile)