180 lines
6.3 KiB
Markdown
180 lines
6.3 KiB
Markdown
# 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)
|