Initial commit
This commit is contained in:
179
PLAN.md
Normal file
179
PLAN.md
Normal 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)
|
||||
Reference in New Issue
Block a user