Initial commit

This commit is contained in:
Alejandro Martinez
2026-02-12 02:04:10 +01:00
commit f09af719cf
13433 changed files with 2193445 additions and 0 deletions

179
PLAN.md Normal file
View File

@@ -0,0 +1,179 @@
# 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
```bash
npm create astro@latest . -- --template minimal --typescript strict --install --git
```
2. **Instalar dependencias**
```bash
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
6. **`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
7. **`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
8. **`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
9. **`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
10. **`src/layouts/Base.astro`** - HTML shell con nav (logo + link "New Skill")
11. **`src/components/SkillCard.astro`** - Card con nombre, descripcion truncada, badges de tools
12. **`src/pages/index.astro`** - Catalogo: llama `listSkills()`, renderiza grid de SkillCards. Empty state si no hay skills.
13. **`src/pages/skills/[slug].astro`** - Vista detalle: renderiza markdown con `marked`, muestra metadata, botones Edit/Delete
### Fase 4: UI write
14. **`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
15. **`src/pages/skills/new.astro`** - Monta SkillEditor en modo create
16. **`src/pages/skills/[slug]/edit.astro`** - Carga skill, monta SkillEditor en modo edit con datos
17. **`src/components/DeleteButton.vue`** (island `client:load`)
- Prop: `slug`
- Click -> confirm -> `fetch DELETE` -> redirect a `/`
### Fase 5: Deployment
18. **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
19. **.dockerignore**: `node_modules`, `dist`, `.git`, `.env`
---
## Verificacion
**Tras Fase 2 (API):**
```bash
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):**
```bash
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)