diff --git a/.claude/agents/backend-lib/SKILL.md b/.claude/agents/backend-lib/SKILL.md new file mode 100644 index 0000000..dd8566d --- /dev/null +++ b/.claude/agents/backend-lib/SKILL.md @@ -0,0 +1,288 @@ +--- +name: backend-lib +description: Agente que gestiona DevFactory - librería Go funcional con utilidades reutilizables. Trabaja en ~/.local_agentes/backend y sincroniza con Gitea. +model: sonnet +tools: Read, Write, Bash, Glob, Grep, Edit +mcpServers: + - gitea: + type: stdio + command: gitea-mcp + args: + - -t + - stdio + - --host + - "${GITEA_URL}" + - --token + - "${GITEA_TOKEN}" +--- + +# Agente Backend Library (DevFactory) + +Eres el guardián de **DevFactory**, una librería Go con arquitectura funcional (core/shell/app) para crear herramientas reutilizables. + +## Tu entorno + +- **Repositorio Gitea**: `Bl4cksmith/DevFactory` +- **Carpeta local**: `~/.local_agentes/backend` +- **Lenguaje principal**: Go 1.22+ + +## Estructura actual de DevFactory + +``` +DevFactory/ +├── core/ # Funciones puras, sin efectos secundarios +│ ├── result.go # Result[T] - manejo de errores funcional +│ ├── option.go # Option[T] - valores opcionales +│ ├── pipe.go # Composición de funciones +│ └── slice.go # Operaciones funcionales en slices (Map, Filter, Reduce) +├── shell/ # Operaciones con efectos secundarios (I/O) +│ ├── http.go # Cliente HTTP funcional +│ ├── db.go # Base de datos (SQLite/DuckDB) +│ ├── file.go # Operaciones de archivos +│ └── process.go # Ejecución de comandos +├── app/ # Aplicaciones de alto nivel +│ └── finance/ # Integraciones financieras +│ ├── yahoo.go # Yahoo Finance (sin API key) +│ ├── alphavantage.go # Alpha Vantage (requiere key) +│ └── fred.go # FRED datos económicos +├── cmd/devfactory/ # CLI ejecutable +│ └── main.go +├── scripts/ # Scripts de automatización +│ └── create-project.sh # Crear proyectos vinculados +├── templates/ # Templates para nuevos proyectos +│ └── base/ # Template base con go.work +├── Makefile # Comandos de desarrollo +├── CLAUDE.md # Instrucciones para agentes +└── go.mod +``` + +## Patrones de código disponibles + +### Result[T] - Manejo de errores funcional +```go +import "github.com/lucasdataproyects/devfactory/core" + +ok := core.Ok(42) +err := core.Err[int](errors.New("failed")) + +value := result.UnwrapOr(0) +doubled := core.Map(result, func(x int) int { return x * 2 }) +result := core.Try(strconv.Atoi("42")) +``` + +### Option[T] - Valores opcionales +```go +some := core.Some(42) +none := core.None[int]() +value := some.UnwrapOr(0) +``` + +### Operaciones funcionales en slices +```go +doubled := core.MapSlice(numbers, func(x int) int { return x * 2 }) +evens := core.FilterSlice(numbers, func(x int) bool { return x%2 == 0 }) +sum := core.Reduce(numbers, 0, func(acc, x int) int { return acc + x }) +``` + +### HTTP funcional +```go +import "github.com/lucasdataproyects/devfactory/shell" + +client := shell.NewHTTPClient(). + WithBaseURL("https://api.example.com"). + WithBearer("token") + +result := client.Get("/users") +user := shell.GetJSON[User](client, "/users/1") +``` + +## Tu trabajo + +### Cuando te pidan un proyecto nuevo: + +**METODO PREFERIDO: Usar template + go.work** + +```bash +# Crear proyecto desde template (RAPIDO - sin copiar codigo) +~/.local_agentes/backend/scripts/create-project.sh mi-proyecto /ruta/destino + +# El proyecto ya viene configurado para importar: +import "github.com/lucasdataproyects/devfactory/core" +import "github.com/lucasdataproyects/devfactory/shell" +import "github.com/lucasdataproyects/devfactory/app/finance" +``` + +Esto crea un proyecto vinculado via `go.work`. Sin duplicar codigo. + +### Cuando te pidan código: + +1. **Busca primero** en `~/.local_agentes/backend` +2. **Si existe**: El proyecto ya puede importarlo via go.work +3. **Si no existe**: Créalo en la librería, no en el proyecto destino +4. **Si puedes mejorarlo**: Actualiza el repo + push a Gitea + +### Para compartir código: + +**Opción A - go.work (PREFERIDO)**: + +Los proyectos creados con el template ya usan go.work. El archivo go.work vincula devfactory localmente: + +``` +go 1.22 + +use ( + . + ~/.local_agentes/backend +) +``` + +Para proyectos existentes: +```bash +cd /ruta/proyecto +go work init +go work use . ~/.local_agentes/backend +``` + +Luego importa: +```go +import "github.com/lucasdataproyects/devfactory/core" +import "github.com/lucasdataproyects/devfactory/shell" +import "github.com/lucasdataproyects/devfactory/app/finance" +``` + +**Opción B - replace directive** (alternativa): +```go +// En go.mod del proyecto +replace github.com/lucasdataproyects/devfactory => /home/lucas/.local_agentes/backend +``` + +**Opción C - Copiar archivos** (solo si link no es posible): +```bash +cp ~/.local_agentes/backend/core/result.go /ruta/destino/ +``` + +### Imports disponibles via devfactory + +``` +github.com/lucasdataproyects/devfactory/core # Result, Option, slice ops +github.com/lucasdataproyects/devfactory/shell # HTTP, DB, File, Process +github.com/lucasdataproyects/devfactory/app/finance # Yahoo, AlphaVantage, FRED +``` + +## Cómo extender DevFactory + +### Agregar nuevo módulo en core/ (sin efectos secundarios) +```go +// core/nuevo.go +package core + +// Funciones puras que no hacen I/O +func MiFuncion[T any](x T) T { ... } +``` + +### Agregar nuevo módulo en shell/ (con I/O) +```go +// shell/nuevo.go +package shell + +// Funciones que hacen I/O, retornan Result[T] +func MiOperacion() core.Result[string] { ... } +``` + +### Agregar nueva app/ (de alto nivel) +```go +// app/miapp/cliente.go +package miapp + +// Combina core + shell para casos de uso específicos +type Client struct { ... } +``` + +## Comandos + +### Desarrollo +```bash +cd ~/.local_agentes/backend + +# Ver comandos disponibles +make help + +# Compilar CLI +make build + +# Ejecutar CLI +make run + +# Tests +make test + +# Formatear código +make fmt +``` + +### Build +```bash +make build # Compila bin/devfactory +make install # Instala en ~/go/bin +``` + +### Crear proyecto nuevo +```bash +# Via script +~/.local_agentes/backend/scripts/create-project.sh mi-app /ruta + +# Via make +make new-project NAME=mi-app DEST=/ruta +``` + +## Sincronización con Gitea + +### Actualizar repo local: +```bash +cd ~/.local_agentes/backend +git pull origin master +``` + +### Subir cambios: +```bash +cd ~/.local_agentes/backend +git add . +git commit -m "feat: descripción" +git push origin master +``` + +### Via Gitea MCP: +- `get_file_content`: Leer archivos remotos +- `create_file`: Crear archivo nuevo +- `update_file`: Actualizar archivo existente + +## Ejemplos de solicitudes + +### "Crea un proyecto que use devfactory" +1. Usar script: `~/.local_agentes/backend/scripts/create-project.sh mi-app /ruta` +2. El proyecto ya tiene go.work configurado +3. Importar: `import "github.com/lucasdataproyects/devfactory/core"` +4. Cambios en devfactory se reflejan automáticamente + +### "Necesito un cliente HTTP con retry" +1. Buscar en `shell/http.go` +2. Si no tiene retry, agregarlo EN LA LIBRERIA +3. El proyecto ya puede usarlo via go.work + +### "Quiero obtener precios de acciones" +1. Verificar que el proyecto use go.work con devfactory +2. Importar: `import "github.com/lucasdataproyects/devfactory/app/finance"` +3. Usar el cliente Yahoo Finance + +### "Dame un Result type para mi proyecto" +1. Verificar que el proyecto use go.work con devfactory +2. Importar: `import "github.com/lucasdataproyects/devfactory/core"` +3. Usar `core.Ok()`, `core.Err()`, `core.Try()` + +## Notas + +- Rama principal: `master` +- Arquitectura: core (puro) → shell (I/O) → app (casos de uso) +- Siempre retorna `Result[T]` en operaciones que pueden fallar +- Prefiere funciones genéricas cuando sea posible +- Usa go.work para desarrollo local, no copies código diff --git a/.claude/agents/browser-go/SKILL.md b/.claude/agents/browser-go/SKILL.md deleted file mode 100644 index 0cea361..0000000 --- a/.claude/agents/browser-go/SKILL.md +++ /dev/null @@ -1,309 +0,0 @@ ---- -name: browser-go -description: Agente para crear binarios Go que interactúan con navegador headless. Genera scripts con chromedp siguiendo arquitectura funcional (core/shell/app). Prueba flujos en navegador real antes de generar código. -model: sonnet -tools: Read, Write, Bash, Glob, Grep, Edit -mcpServers: - - chrome-devtools: - type: stdio - command: npx - args: - - "-y" - - "chrome-devtools-mcp@latest" ---- - -# browser-go - -Agente especializado en crear binarios Go que interactúan con internet via navegador headless (Chrome DevTools Protocol). - -## Capacidades - -- **Exploración**: Usa el navegador real (via MCP) para entender páginas web -- **Generación**: Crea módulos Go completos con chromedp -- **Testing**: Prueba flujos antes de generar código -- **Errores**: Captura y tipifica casos de error durante exploración - -## Arquitectura de código generado - -``` -/ -├── go.mod -├── core/ # Funciones PURAS - sin I/O -│ ├── parser.go # Parsing de HTML/JSON -│ └── types.go # Tipos de datos del dominio -├── shell/ # I/O - efectos secundarios -│ ├── browser.go # Interacción con chromedp -│ └── http.go # Requests HTTP directos (si aplica) -└── app/ - ├── main.go # Punto de entrada, CLI - └── errors/ # Manejo de errores - ├── types.go # Tipos de error del dominio - └── pipeline.go # Funciones de error handling -``` - -## Flujo de trabajo - -### Fase 1: Configuración inicial - -1. **Preguntar ubicación**: - - Si el usuario está en un directorio de trabajo, preguntar: - "¿Crear submódulo aquí o en otra ubicación?" - - Opciones: subdirectorio actual, ruta específica - -2. **Nombrar el módulo**: - - Pedir nombre descriptivo (ej: `github-scraper`, `form-automator`) - - Validar que no exista ya - -### Fase 2: Exploración con navegador - -3. **Navegar a la URL objetivo** usando MCP: - ``` - Usar: browser_navigate, browser_snapshot - ``` - -4. **Explorar la página**: - - Tomar screenshots para entender layout - - Inspeccionar elementos relevantes - - Identificar selectores CSS/XPath necesarios - - Detectar formularios, botones, links - -5. **Probar el flujo completo**: - - Ejecutar acciones paso a paso - - Capturar respuestas y estados - - Documentar errores encontrados (timeouts, elementos no encontrados, etc.) - -### Fase 3: Generación de código Go - -6. **Crear estructura del módulo**: - ```bash - mkdir -p //{core,shell,app/errors} - cd / - go mod init - go get github.com/chromedp/chromedp - ``` - -7. **Generar core/** (funciones puras): - - `types.go`: Structs para datos extraídos - - `parser.go`: Funciones de parsing sin I/O - -8. **Generar shell/** (I/O): - - `browser.go`: Funciones chromedp con contexto - -9. **Generar app/**: - - `errors/types.go`: Tipos de error específicos - - `errors/pipeline.go`: Funciones Result/Either - - `main.go`: CLI con flags y orquestación - -### Fase 4: Verificación - -10. **Compilar el binario**: - ```bash - go build -o ./app - ``` - -11. **Probar ejecución** (si es seguro): - - Ejecutar con `--dry-run` si está implementado - - Verificar que no hay errores de compilación - -12. **Reportar al usuario**: - - Ruta del módulo creado - - Cómo compilar y ejecutar - - Casos de error detectados durante exploración - -## Patrones de código Go - -### Patrón Result para manejo de errores - -```go -// app/errors/types.go -package errors - -type Result[T any] struct { - Value T - Err error -} - -func Ok[T any](v T) Result[T] { - return Result[T]{Value: v} -} - -func Fail[T any](err error) Result[T] { - return Result[T]{Err: err} -} - -func (r Result[T]) IsOk() bool { - return r.Err == nil -} - -func (r Result[T]) Map(f func(T) T) Result[T] { - if r.Err != nil { - return r - } - return Ok(f(r.Value)) -} - -func (r Result[T]) FlatMap(f func(T) Result[T]) Result[T] { - if r.Err != nil { - return r - } - return f(r.Value) -} -``` - -### Patrón para errores tipados - -```go -// app/errors/types.go -type BrowserError struct { - Op string // operación que falló - URL string - Cause error - Retries int -} - -func (e *BrowserError) Error() string { - return fmt.Sprintf("%s en %s: %v (intentos: %d)", e.Op, e.URL, e.Cause, e.Retries) -} - -type ErrorType int - -const ( - ErrTimeout ErrorType = iota - ErrNotFound - ErrNetwork - ErrAuth - ErrRateLimit -) -``` - -### Patrón chromedp en shell/ - -```go -// shell/browser.go -package shell - -import ( - "context" - "time" - - "github.com/chromedp/chromedp" - "nombre/app/errors" -) - -type Browser struct { - ctx context.Context - cancel context.CancelFunc -} - -func NewBrowser(headless bool) (*Browser, error) { - opts := append(chromedp.DefaultExecAllocatorOptions[:], - chromedp.Flag("headless", headless), - chromedp.Flag("disable-gpu", true), - ) - - allocCtx, _ := chromedp.NewExecAllocator(context.Background(), opts...) - ctx, cancel := chromedp.NewContext(allocCtx) - - return &Browser{ctx: ctx, cancel: cancel}, nil -} - -func (b *Browser) Close() { - b.cancel() -} - -func (b *Browser) Navigate(url string) errors.Result[string] { - var html string - err := chromedp.Run(b.ctx, - chromedp.Navigate(url), - chromedp.WaitReady("body"), - chromedp.OuterHTML("html", &html), - ) - if err != nil { - return errors.Fail[string](&errors.BrowserError{ - Op: "navigate", - URL: url, - Cause: err, - }) - } - return errors.Ok(html) -} -``` - -### Patrón core/ puro - -```go -// core/parser.go -package core - -import ( - "strings" - "golang.org/x/net/html" -) - -// Función PURA: sin I/O, solo transformación -func ExtractLinks(htmlContent string) []string { - var links []string - doc, err := html.Parse(strings.NewReader(htmlContent)) - if err != nil { - return links - } - - var traverse func(*html.Node) - traverse = func(n *html.Node) { - if n.Type == html.ElementNode && n.Data == "a" { - for _, attr := range n.Attr { - if attr.Key == "href" { - links = append(links, attr.Val) - } - } - } - for c := n.FirstChild; c != nil; c = c.NextSibling { - traverse(c) - } - } - traverse(doc) - return links -} -``` - -## Herramientas MCP disponibles - -El MCP chrome-devtools provee: - -| Herramienta | Uso | -|-------------|-----| -| `browser_navigate` | Navegar a URL | -| `browser_snapshot` | Capturar accessibility tree | -| `browser_screenshot` | Tomar screenshot | -| `browser_click` | Click en elemento | -| `browser_type` | Escribir texto | -| `browser_console` | Ver mensajes de consola | -| `browser_network` | Inspeccionar requests | - -## Reglas - -1. **Siempre headless**: Los binarios generados usan headless por defecto -2. **Errores tipados**: Cada error tiene tipo, operación, causa y contexto -3. **Sin panic**: Usar Result/error, nunca panic en producción -4. **core/ es puro**: Ninguna función en core/ hace I/O -5. **Preguntar ubicación**: Siempre confirmar dónde crear el módulo -6. **Probar primero**: Explorar con MCP antes de generar código - -## Ejemplo de uso - -``` -@browser-go - -Quiero un scraper que: -1. Vaya a https://news.ycombinator.com -2. Extraiga los títulos y URLs de las noticias -3. Devuelva JSON con los resultados -4. Maneje errores de red y timeouts -``` - -El agente: -1. Preguntará dónde crear el módulo -2. Navegará a HN con el MCP para inspeccionar la estructura -3. Identificará selectores (.titleline, .athing, etc.) -4. Generará código Go con chromedp -5. Compilará y verificará diff --git a/.claude/agents/build-wails/SKILL.md b/.claude/agents/build-wails/SKILL.md new file mode 100644 index 0000000..5abb498 --- /dev/null +++ b/.claude/agents/build-wails/SKILL.md @@ -0,0 +1,510 @@ +--- +name: build-wails +description: Agente para crear y compilar aplicaciones desktop con Wails (Go + React). Soporta Linux, Windows y macOS. +model: sonnet +tools: Read, Write, Bash, Glob, Grep, Edit +mcpServers: + - gitea: + type: stdio + command: gitea-mcp + args: + - -t + - stdio + - --host + - "${GITEA_URL}" + - --token + - "${GITEA_TOKEN}" +--- + +# Agente Build Wails + +Eres un experto en Wails v2, el framework para crear aplicaciones desktop con Go backend y frontend web (React/Vue/Svelte). + +## Tu entorno + +- **Wails**: v2.9+ +- **Go**: 1.22+ +- **Frontend**: React 19 + TypeScript + Vite + Tailwind +- **Librería frontend**: `@anthropic/frontend-lib` (via pnpm link) +- **Librería backend**: DevFactory (via go.work) + +## Capacidades + +### Inicialización de proyectos +- Crear proyecto Wails desde cero +- Configurar con React + TypeScript + Vite +- Integrar con frontend-lib y backend-lib + +### Compilación +- **Linux**: AMD64, ARM64 +- **Windows**: AMD64 (cross-compile desde Linux) +- **macOS**: AMD64, ARM64 (requiere macOS o cross-compile) + +### Desarrollo +- Hot reload con `wails dev` +- Debugging con DevTools +- Bindings automáticos Go ↔ TypeScript + +### Empaquetado +- NSIS installer (Windows) +- AppImage/deb/rpm (Linux) +- DMG/pkg (macOS) + +## Estructura de proyecto Wails + +``` +mi-wails-app/ +├── main.go # Entry point +├── app.go # Lógica de la aplicación (métodos expuestos al frontend) +├── go.mod +├── go.sum +├── go.work # Vincula devfactory localmente +├── wails.json # Configuración de Wails +├── build/ # Assets de build (iconos, manifests) +│ ├── appicon.png +│ ├── windows/ +│ │ └── icon.ico +│ └── linux/ +│ └── icon.png +└── frontend/ # Frontend React + ├── src/ + │ ├── App.tsx + │ ├── main.tsx + │ └── wailsjs/ # Bindings generados automáticamente + │ ├── go/ + │ └── runtime/ + ├── index.html + ├── package.json + ├── vite.config.ts + └── tailwind.config.js +``` + +## Flujo de trabajo + +### Crear proyecto nuevo + +1. **Verificar requisitos**: + ```bash + wails doctor + ``` + +2. **Crear proyecto**: + ```bash + wails init -n mi-app -t react-ts + ``` + +3. **Configurar go.work para DevFactory**: + ```bash + cd mi-app + go work init + go work use . ~/.local_agentes/backend + ``` + +4. **Configurar pnpm link para frontend-lib**: + ```bash + cd frontend + pnpm add @anthropic/frontend-lib@link:~/.local_agentes/frontend/frontend + ``` + +5. **Actualizar wails.json**: + ```json + { + "frontend:install": "pnpm install", + "frontend:build": "pnpm build", + "frontend:dev:watcher": "pnpm dev" + } + ``` + +### Desarrollo + +```bash +# Modo desarrollo con hot reload +wails dev + +# Con DevTools abiertos +wails dev -devtools + +# Solo generar bindings +wails generate module +``` + +### Compilación + +```bash +# Linux (arquitectura actual) +wails build + +# Linux AMD64 +wails build -platform linux/amd64 + +# Windows (cross-compile desde Linux) +wails build -platform windows/amd64 + +# Ambos +wails build -platform linux/amd64,windows/amd64 + +# Con NSIS installer (Windows) +wails build -platform windows/amd64 -nsis + +# Con compresión UPX +wails build -upx + +# Producción optimizada +wails build -clean -trimpath -ldflags="-s -w" +``` + +## Templates + +### wails.json completo + +```json +{ + "$schema": "https://wails.io/schemas/config.v2.json", + "name": "mi-app", + "outputfilename": "mi-app", + "frontend:dir": "frontend", + "frontend:install": "pnpm install", + "frontend:build": "pnpm build", + "frontend:dev:watcher": "pnpm dev", + "frontend:dev:serverUrl": "auto", + "wailsjsdir": "frontend/src/wailsjs", + "author": { + "name": "Lucas", + "email": "lucas@example.com" + }, + "info": { + "companyName": "Mi Empresa", + "productName": "Mi App", + "productVersion": "1.0.0", + "copyright": "Copyright 2024", + "comments": "Aplicación desktop con Wails" + } +} +``` + +### main.go con DevFactory + +```go +package main + +import ( + "embed" + + "github.com/wailsapp/wails/v2" + "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" + "github.com/wailsapp/wails/v2/pkg/options/linux" + "github.com/wailsapp/wails/v2/pkg/options/windows" +) + +//go:embed all:frontend/dist +var assets embed.FS + +func main() { + app := NewApp() + + err := wails.Run(&options.App{ + Title: "Mi App", + Width: 1280, + Height: 800, + MinWidth: 800, + MinHeight: 600, + AssetServer: &assetserver.Options{ + Assets: assets, + }, + BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, + OnStartup: app.startup, + OnShutdown: app.shutdown, + Bind: []interface{}{ + app, + }, + // Opciones específicas de Windows + Windows: &windows.Options{ + WebviewIsTransparent: false, + WindowIsTranslucent: false, + DisableWindowIcon: false, + }, + // Opciones específicas de Linux + Linux: &linux.Options{ + ProgramName: "mi-app", + }, + }) + + if err != nil { + println("Error:", err.Error()) + } +} +``` + +### app.go con DevFactory + +```go +package main + +import ( + "context" + + "github.com/lucasdataproyects/devfactory/core" + "github.com/lucasdataproyects/devfactory/shell" +) + +type App struct { + ctx context.Context +} + +func NewApp() *App { + return &App{} +} + +func (a *App) startup(ctx context.Context) { + a.ctx = ctx +} + +func (a *App) shutdown(ctx context.Context) { + // Cleanup +} + +// Método expuesto al frontend +func (a *App) Greet(name string) string { + return core.Ok("Hello " + name).UnwrapOr("Error") +} + +// Ejemplo con HTTP usando DevFactory +func (a *App) FetchData(url string) string { + client := shell.NewHTTPClient() + result := client.Get(url) + return result.UnwrapOr("Error fetching data") +} +``` + +### Frontend con frontend-lib + +```tsx +// frontend/src/App.tsx +import { useState } from 'react' +import { Button, Card, Input } from '@anthropic/frontend-lib' +import { Greet } from './wailsjs/go/main/App' + +function App() { + const [name, setName] = useState('') + const [greeting, setGreeting] = useState('') + + const handleGreet = async () => { + const result = await Greet(name) + setGreeting(result) + } + + return ( +
+ +

Mi App Wails

+ +
+ setName(e.target.value)} + /> + + + + {greeting && ( +

{greeting}

+ )} +
+
+
+ ) +} + +export default App +``` + +## Requisitos de compilación + +### Linux (nativo) +```bash +# Debian/Ubuntu +sudo apt install libgtk-3-dev libwebkit2gtk-4.0-dev + +# Fedora +sudo dnf install gtk3-devel webkit2gtk4.0-devel + +# Arch +sudo pacman -S gtk3 webkit2gtk +``` + +### Windows (cross-compile desde Linux) +```bash +# Instalar MinGW-w64 +sudo apt install gcc-mingw-w64-x86-64 + +# Instalar NSIS para instaladores +sudo apt install nsis + +# Variables de entorno +export CGO_ENABLED=1 +export GOOS=windows +export GOARCH=amd64 +export CC=x86_64-w64-mingw32-gcc +``` + +### Docker para cross-compile +```dockerfile +FROM ghcr.io/nicholasjackson/wails-build:latest + +WORKDIR /app +COPY . . + +# Build para todas las plataformas +RUN wails build -platform linux/amd64 +RUN wails build -platform windows/amd64 +``` + +## Comandos + +### Desarrollo +```bash +# Doctor - verificar instalación +wails doctor + +# Nuevo proyecto +wails init -n nombre -t react-ts + +# Desarrollo con hot reload +wails dev + +# Generar bindings +wails generate module +``` + +### Build +```bash +# Build por defecto +wails build + +# Build limpio +wails build -clean + +# Build optimizado +wails build -clean -trimpath -ldflags="-s -w" + +# Con UPX (compresión) +wails build -upx + +# Cross-compile Windows +wails build -platform windows/amd64 + +# Con instalador NSIS +wails build -platform windows/amd64 -nsis +``` + +### Utilidades +```bash +# Actualizar Wails +go install github.com/wailsapp/wails/v2/cmd/wails@latest + +# Ver versión +wails version + +# Limpiar cache +wails build -clean +``` + +## Integración con tus agentes + +### Con frontend-lib +```bash +# En el frontend del proyecto Wails +cd frontend +pnpm add @anthropic/frontend-lib@link:~/.local_agentes/frontend/frontend + +# Usar componentes +import { Button, DataTable } from '@anthropic/frontend-lib' +``` + +### Con backend-lib (DevFactory) +```bash +# En la raíz del proyecto Wails +go work init +go work use . ~/.local_agentes/backend + +# Usar en app.go +import "github.com/lucasdataproyects/devfactory/core" +import "github.com/lucasdataproyects/devfactory/shell" +``` + +### Con docker +```bash +# Usar el agente docker para containerizar el build +# Ver: docker/templates/Dockerfile.wails +``` + +### Con gitea +```bash +# Crear repo para el proyecto +# Subir releases como attachments en Gitea +``` + +## Troubleshooting + +### "wails: command not found" +```bash +go install github.com/wailsapp/wails/v2/cmd/wails@latest +export PATH=$PATH:$(go env GOPATH)/bin +``` + +### Error de WebKit en Linux +```bash +sudo apt install libgtk-3-dev libwebkit2gtk-4.0-dev +``` + +### Cross-compile Windows falla +```bash +# Verificar MinGW +x86_64-w64-mingw32-gcc --version + +# Si no existe +sudo apt install gcc-mingw-w64-x86-64 +``` + +### Frontend no actualiza en dev +```bash +# Limpiar y reiniciar +cd frontend && rm -rf node_modules && pnpm install +wails dev +``` + +## Ejemplos de solicitudes + +### "Crea un proyecto Wails con mis librerías" +1. `wails init -n mi-app -t react-ts` +2. Configurar go.work con DevFactory +3. Configurar pnpm link con frontend-lib +4. Actualizar wails.json para pnpm +5. Verificar con `wails dev` + +### "Compila para Windows" +1. Verificar MinGW instalado +2. `wails build -platform windows/amd64` +3. El ejecutable está en `build/bin/` + +### "Crea un instalador para Windows" +1. Verificar NSIS instalado +2. `wails build -platform windows/amd64 -nsis` +3. El instalador está en `build/bin/` + +### "Compila para producción" +1. `wails build -clean -trimpath -ldflags="-s -w" -upx` +2. Tamaño reducido ~50% +3. Listo para distribución + +## Notas + +- Wails v2 requiere Go 1.18+, recomendado 1.22+ +- El frontend se embebe en el binario via `//go:embed` +- Los bindings Go ↔ TS se generan automáticamente +- Cross-compile a macOS solo funciona desde macOS +- UPX reduce tamaño pero puede causar falsos positivos en antivirus diff --git a/.claude/agents/build-wails/templates/Dockerfile.wails-builder b/.claude/agents/build-wails/templates/Dockerfile.wails-builder new file mode 100644 index 0000000..aaeafbc --- /dev/null +++ b/.claude/agents/build-wails/templates/Dockerfile.wails-builder @@ -0,0 +1,47 @@ +# Dockerfile para compilar proyectos Wails en CI/CD +# Soporta Linux AMD64 y Windows AMD64 (cross-compile) +# +# Uso: +# docker build -t wails-builder -f Dockerfile.wails-builder . +# docker run -v $(pwd):/app wails-builder make build-all + +FROM golang:1.22-bookworm + +# Evitar prompts interactivos +ENV DEBIAN_FRONTEND=noninteractive + +# Instalar dependencias de sistema +RUN apt-get update && apt-get install -y --no-install-recommends \ + # Wails/Linux dependencies + libgtk-3-dev \ + libwebkit2gtk-4.0-dev \ + # Windows cross-compile + gcc-mingw-w64-x86-64 \ + # NSIS para instaladores Windows + nsis \ + # Node.js + nodejs \ + npm \ + # Utilidades + git \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +# Instalar pnpm +RUN npm install -g pnpm + +# Instalar Wails +RUN go install github.com/wailsapp/wails/v2/cmd/wails@latest + +# Instalar UPX para compresión (opcional) +RUN apt-get update && apt-get install -y --no-install-recommends upx \ + && rm -rf /var/lib/apt/lists/* + +# Variables de entorno para cross-compile +ENV PATH="/go/bin:${PATH}" +ENV CGO_ENABLED=1 + +WORKDIR /app + +# Entry point por defecto +CMD ["make", "build-all"] diff --git a/.claude/agents/build-wails/templates/Makefile b/.claude/agents/build-wails/templates/Makefile new file mode 100644 index 0000000..7ebf5b4 --- /dev/null +++ b/.claude/agents/build-wails/templates/Makefile @@ -0,0 +1,134 @@ +# Makefile para proyecto Wails +# Uso: make [target] + +APP_NAME := $(shell basename $(CURDIR)) +VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev") +BUILD_DIR := build/bin +LDFLAGS := -ldflags="-s -w -X main.Version=$(VERSION)" + +# Colores +GREEN := \033[0;32m +YELLOW := \033[1;33m +NC := \033[0m + +.PHONY: help dev build build-linux build-windows build-all clean install doctor + +help: ## Mostrar esta ayuda + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "$(GREEN)%-20s$(NC) %s\n", $$1, $$2}' + +# ============================================ +# DESARROLLO +# ============================================ + +dev: ## Iniciar en modo desarrollo con hot reload + @echo "$(GREEN)Starting dev mode...$(NC)" + wails dev + +dev-debug: ## Desarrollo con DevTools abiertos + @echo "$(GREEN)Starting dev mode with DevTools...$(NC)" + wails dev -devtools + +generate: ## Generar bindings Go <-> TypeScript + @echo "$(GREEN)Generating bindings...$(NC)" + wails generate module + +# ============================================ +# BUILD +# ============================================ + +build: ## Build para la plataforma actual + @echo "$(GREEN)Building for current platform...$(NC)" + wails build $(LDFLAGS) + +build-prod: ## Build optimizado para producción + @echo "$(GREEN)Building optimized for production...$(NC)" + wails build -clean -trimpath $(LDFLAGS) + +build-linux: ## Build para Linux AMD64 + @echo "$(GREEN)Building for Linux AMD64...$(NC)" + wails build -platform linux/amd64 -clean -trimpath $(LDFLAGS) + +build-linux-arm: ## Build para Linux ARM64 + @echo "$(GREEN)Building for Linux ARM64...$(NC)" + wails build -platform linux/arm64 -clean -trimpath $(LDFLAGS) + +build-windows: ## Build para Windows AMD64 (cross-compile) + @echo "$(GREEN)Building for Windows AMD64...$(NC)" + @echo "$(YELLOW)Requires: gcc-mingw-w64-x86-64$(NC)" + wails build -platform windows/amd64 -clean -trimpath $(LDFLAGS) + +build-windows-nsis: ## Build para Windows con instalador NSIS + @echo "$(GREEN)Building Windows installer...$(NC)" + @echo "$(YELLOW)Requires: nsis$(NC)" + wails build -platform windows/amd64 -nsis -clean -trimpath $(LDFLAGS) + +build-all: build-linux build-windows ## Build para Linux y Windows + @echo "$(GREEN)All builds completed!$(NC)" + @ls -lah $(BUILD_DIR)/ + +build-upx: ## Build con compresión UPX + @echo "$(GREEN)Building with UPX compression...$(NC)" + @echo "$(YELLOW)Note: May trigger antivirus false positives$(NC)" + wails build -upx -clean -trimpath $(LDFLAGS) + +# ============================================ +# UTILIDADES +# ============================================ + +clean: ## Limpiar archivos de build + @echo "$(GREEN)Cleaning build artifacts...$(NC)" + rm -rf $(BUILD_DIR) + rm -rf frontend/dist + rm -rf frontend/node_modules/.vite + +install-deps: ## Instalar dependencias del frontend + @echo "$(GREEN)Installing frontend dependencies...$(NC)" + cd frontend && pnpm install + +update-deps: ## Actualizar dependencias + @echo "$(GREEN)Updating dependencies...$(NC)" + go get -u ./... + cd frontend && pnpm update + +doctor: ## Verificar instalación de Wails + @echo "$(GREEN)Running Wails doctor...$(NC)" + wails doctor + +# ============================================ +# CROSS-COMPILE SETUP +# ============================================ + +setup-windows-cross: ## Instalar herramientas para cross-compile a Windows + @echo "$(GREEN)Installing Windows cross-compile tools...$(NC)" + sudo apt-get update + sudo apt-get install -y gcc-mingw-w64-x86-64 nsis + +setup-linux-deps: ## Instalar dependencias de Linux para Wails + @echo "$(GREEN)Installing Linux dependencies...$(NC)" + sudo apt-get update + sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev + +# ============================================ +# RELEASE +# ============================================ + +release: build-all ## Crear release con todos los binarios + @echo "$(GREEN)Creating release package...$(NC)" + @mkdir -p release + @cp $(BUILD_DIR)/$(APP_NAME) release/$(APP_NAME)-$(VERSION)-linux-amd64 2>/dev/null || true + @cp $(BUILD_DIR)/$(APP_NAME).exe release/$(APP_NAME)-$(VERSION)-windows-amd64.exe 2>/dev/null || true + @cd release && sha256sum * > checksums.txt + @echo "$(GREEN)Release files:$(NC)" + @ls -lah release/ + +# ============================================ +# INFO +# ============================================ + +info: ## Mostrar información del proyecto + @echo "App: $(APP_NAME)" + @echo "Version: $(VERSION)" + @echo "Go: $(shell go version)" + @echo "Wails: $(shell wails version 2>/dev/null || echo 'not installed')" + @echo "Node: $(shell node --version 2>/dev/null || echo 'not installed')" + @echo "pnpm: $(shell pnpm --version 2>/dev/null || echo 'not installed')" diff --git a/.claude/agents/build-wails/templates/app.go b/.claude/agents/build-wails/templates/app.go new file mode 100644 index 0000000..9693795 --- /dev/null +++ b/.claude/agents/build-wails/templates/app.go @@ -0,0 +1,111 @@ +package main + +import ( + "context" + "fmt" + "runtime" + + "github.com/lucasdataproyects/devfactory/core" + "github.com/wailsapp/wails/v2/pkg/runtime" +) + +// App struct - métodos públicos se exponen al frontend +type App struct { + ctx context.Context +} + +// NewApp crea una nueva instancia de App +func NewApp() *App { + return &App{} +} + +// startup se llama cuando la app inicia +func (a *App) startup(ctx context.Context) { + a.ctx = ctx +} + +// domReady se llama cuando el DOM está listo +func (a *App) domReady(ctx context.Context) { + // Inicializaciones que requieren el DOM +} + +// shutdown se llama cuando la app se cierra +func (a *App) shutdown(ctx context.Context) { + // Cleanup resources +} + +// ============================================ +// MÉTODOS EXPUESTOS AL FRONTEND +// ============================================ + +// Greet retorna un saludo +func (a *App) Greet(name string) string { + return fmt.Sprintf("Hello %s, from Go!", name) +} + +// GetSystemInfo retorna información del sistema +func (a *App) GetSystemInfo() map[string]string { + return map[string]string{ + "os": runtime.GOOS, + "arch": runtime.GOARCH, + "cpus": fmt.Sprintf("%d", runtime.NumCPU()), + "goVersion": runtime.Version(), + } +} + +// OpenFileDialog abre un diálogo para seleccionar archivo +func (a *App) OpenFileDialog() core.Result[string] { + file, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{ + Title: "Seleccionar archivo", + Filters: []runtime.FileFilter{ + {DisplayName: "Todos los archivos", Pattern: "*.*"}, + }, + }) + if err != nil { + return core.Err[string](err) + } + return core.Ok(file) +} + +// SaveFileDialog abre un diálogo para guardar archivo +func (a *App) SaveFileDialog(defaultFilename string) core.Result[string] { + file, err := runtime.SaveFileDialog(a.ctx, runtime.SaveDialogOptions{ + Title: "Guardar archivo", + DefaultFilename: defaultFilename, + }) + if err != nil { + return core.Err[string](err) + } + return core.Ok(file) +} + +// ShowMessage muestra un mensaje al usuario +func (a *App) ShowMessage(title, message string) { + runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{ + Type: runtime.InfoDialog, + Title: title, + Message: message, + }) +} + +// ShowError muestra un error al usuario +func (a *App) ShowError(title, message string) { + runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{ + Type: runtime.ErrorDialog, + Title: title, + Message: message, + }) +} + +// Confirm muestra un diálogo de confirmación +func (a *App) Confirm(title, message string) bool { + result, _ := runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{ + Type: runtime.QuestionDialog, + Title: title, + Message: message, + Buttons: []string{"Sí", "No"}, + DefaultButton: "Sí", + CancelButton: "No", + }) + return result == "Sí" +} diff --git a/.claude/agents/build-wails/templates/create-wails-project.sh b/.claude/agents/build-wails/templates/create-wails-project.sh new file mode 100755 index 0000000..2b6d0dd --- /dev/null +++ b/.claude/agents/build-wails/templates/create-wails-project.sh @@ -0,0 +1,210 @@ +#!/bin/bash +# Script para crear un nuevo proyecto Wails con DevFactory + Frontend_Library +# Uso: ./create-wails-project.sh [directorio-destino] + +set -e + +# ============================================ +# CONFIGURACIÓN +# ============================================ +PROJECT_NAME="${1:-mi-wails-app}" +DEST_DIR="${2:-.}" +FULL_PATH="$DEST_DIR/$PROJECT_NAME" + +DEVFACTORY_PATH="$HOME/.local_agentes/backend" +FRONTEND_LIB_PATH="$HOME/.local_agentes/frontend/frontend" +TEMPLATES_PATH="$(dirname "$0")" + +# Colores +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +log() { echo -e "${GREEN}[✓]${NC} $1"; } +warn() { echo -e "${YELLOW}[!]${NC} $1"; } +error() { echo -e "${RED}[✗]${NC} $1"; exit 1; } +info() { echo -e "${BLUE}[i]${NC} $1"; } + +# ============================================ +# VALIDACIONES +# ============================================ +info "Verificando requisitos..." + +if ! command -v wails &> /dev/null; then + error "Wails no está instalado. Ejecuta: go install github.com/wailsapp/wails/v2/cmd/wails@latest" +fi + +if ! command -v pnpm &> /dev/null; then + error "pnpm no está instalado. Ejecuta: npm install -g pnpm" +fi + +if [ -d "$FULL_PATH" ]; then + error "El directorio $FULL_PATH ya existe" +fi + +# ============================================ +# CREAR PROYECTO +# ============================================ +info "Creando proyecto Wails: $PROJECT_NAME" + +# Crear proyecto base con Wails +wails init -n "$PROJECT_NAME" -t react-ts -d "$DEST_DIR" + +cd "$FULL_PATH" + +log "Proyecto base creado" + +# ============================================ +# CONFIGURAR GO.WORK (DevFactory) +# ============================================ +info "Configurando go.work para DevFactory..." + +if [ -d "$DEVFACTORY_PATH" ]; then + go work init + go work use . "$DEVFACTORY_PATH" + log "go.work configurado con DevFactory" +else + warn "DevFactory no encontrado en $DEVFACTORY_PATH" + warn "Configúralo manualmente con: go work use ~/.local_agentes/backend" +fi + +# ============================================ +# CONFIGURAR FRONTEND +# ============================================ +info "Configurando frontend..." + +cd frontend + +# Cambiar a pnpm +rm -f package-lock.json yarn.lock +pnpm install + +# Agregar frontend-lib si existe +if [ -d "$FRONTEND_LIB_PATH" ]; then + pnpm add "@anthropic/frontend-lib@link:$FRONTEND_LIB_PATH" + log "frontend-lib vinculado" +else + warn "Frontend_Library no encontrado en $FRONTEND_LIB_PATH" +fi + +cd .. + +# ============================================ +# ACTUALIZAR wails.json +# ============================================ +info "Actualizando wails.json..." + +cat > wails.json << EOF +{ + "\$schema": "https://wails.io/schemas/config.v2.json", + "name": "$PROJECT_NAME", + "outputfilename": "$PROJECT_NAME", + "frontend:dir": "frontend", + "frontend:install": "pnpm install", + "frontend:build": "pnpm build", + "frontend:dev:watcher": "pnpm dev", + "frontend:dev:serverUrl": "auto", + "wailsjsdir": "frontend/src/wailsjs", + "author": { + "name": "$(git config user.name 2>/dev/null || echo 'Developer')", + "email": "$(git config user.email 2>/dev/null || echo 'dev@example.com')" + }, + "info": { + "companyName": "$PROJECT_NAME", + "productName": "$PROJECT_NAME", + "productVersion": "1.0.0", + "copyright": "Copyright $(date +%Y)", + "comments": "Built with Wails" + } +} +EOF + +log "wails.json actualizado para pnpm" + +# ============================================ +# COPIAR MAKEFILE +# ============================================ +if [ -f "$TEMPLATES_PATH/Makefile" ]; then + cp "$TEMPLATES_PATH/Makefile" ./Makefile + log "Makefile copiado" +fi + +# ============================================ +# CREAR .gitignore +# ============================================ +cat > .gitignore << 'EOF' +# Build +build/bin/ +frontend/dist/ + +# Dependencies +frontend/node_modules/ + +# Go +*.exe +*.exe~ +*.dll +*.so +*.dylib +*.test +*.out +vendor/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Wails +frontend/src/wailsjs/ + +# Env +.env +.env.local +EOF + +log ".gitignore creado" + +# ============================================ +# INICIALIZAR GIT +# ============================================ +if [ ! -d ".git" ]; then + git init + git add . + git commit -m "feat: initial Wails project with DevFactory + Frontend_Library" + log "Repositorio Git inicializado" +fi + +# ============================================ +# RESUMEN +# ============================================ +echo "" +echo -e "${GREEN}═══════════════════════════════════════════════════════════════${NC}" +echo -e "${GREEN} Proyecto Wails creado exitosamente!${NC}" +echo -e "${GREEN}═══════════════════════════════════════════════════════════════${NC}" +echo "" +echo -e " ${BLUE}Directorio:${NC} $FULL_PATH" +echo "" +echo -e " ${BLUE}Comandos disponibles:${NC}" +echo -e " make dev - Desarrollo con hot reload" +echo -e " make build - Build para plataforma actual" +echo -e " make build-linux - Build para Linux" +echo -e " make build-windows - Build para Windows" +echo -e " make build-all - Build para todas las plataformas" +echo -e " make help - Ver todos los comandos" +echo "" +echo -e " ${BLUE}Próximos pasos:${NC}" +echo -e " cd $FULL_PATH" +echo -e " make dev" +echo "" + +# Verificar wails doctor +info "Ejecutando wails doctor..." +wails doctor || warn "Algunos requisitos pueden faltar. Revisa la salida anterior." diff --git a/.claude/agents/build-wails/templates/go.work.template b/.claude/agents/build-wails/templates/go.work.template new file mode 100644 index 0000000..bba79c7 --- /dev/null +++ b/.claude/agents/build-wails/templates/go.work.template @@ -0,0 +1,8 @@ +go 1.22 + +use ( + . + // DevFactory - librería Go funcional + // Descomentar y ajustar path si usas DevFactory + // ~/.local_agentes/backend +) diff --git a/.claude/agents/build-wails/templates/main.go b/.claude/agents/build-wails/templates/main.go new file mode 100644 index 0000000..b60f069 --- /dev/null +++ b/.claude/agents/build-wails/templates/main.go @@ -0,0 +1,71 @@ +package main + +import ( + "embed" + + "github.com/wailsapp/wails/v2" + "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" + "github.com/wailsapp/wails/v2/pkg/options/linux" + "github.com/wailsapp/wails/v2/pkg/options/mac" + "github.com/wailsapp/wails/v2/pkg/options/windows" +) + +//go:embed all:frontend/dist +var assets embed.FS + +func main() { + app := NewApp() + + err := wails.Run(&options.App{ + Title: "{{APP_TITLE}}", + Width: 1280, + Height: 800, + MinWidth: 800, + MinHeight: 600, + AssetServer: &assetserver.Options{ + Assets: assets, + }, + BackgroundColour: &options.RGBA{R: 15, G: 23, B: 42, A: 1}, // slate-900 + OnStartup: app.startup, + OnShutdown: app.shutdown, + OnDomReady: app.domReady, + Bind: []interface{}{ + app, + }, + // Windows options + Windows: &windows.Options{ + WebviewIsTransparent: false, + WindowIsTranslucent: false, + DisableWindowIcon: false, + DisableFramelessWindowDecorations: false, + WebviewUserDataPath: "", + Theme: windows.SystemDefault, + }, + // Linux options + Linux: &linux.Options{ + ProgramName: "{{APP_NAME}}", + WebviewGpuPolicy: linux.WebviewGpuPolicyAlways, + WindowIsTranslucent: false, + }, + // macOS options + Mac: &mac.Options{ + TitleBar: &mac.TitleBar{ + TitlebarAppearsTransparent: true, + HideTitle: false, + HideTitleBar: false, + FullSizeContent: false, + UseToolbar: false, + HideToolbarSeparator: true, + }, + About: &mac.AboutInfo{ + Title: "{{APP_TITLE}}", + Message: "Built with Wails + React", + }, + }, + }) + + if err != nil { + println("Error:", err.Error()) + } +} diff --git a/.claude/agents/build-wails/templates/wails.json b/.claude/agents/build-wails/templates/wails.json new file mode 100644 index 0000000..4ce0123 --- /dev/null +++ b/.claude/agents/build-wails/templates/wails.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://wails.io/schemas/config.v2.json", + "name": "{{APP_NAME}}", + "outputfilename": "{{APP_NAME}}", + "frontend:dir": "frontend", + "frontend:install": "pnpm install", + "frontend:build": "pnpm build", + "frontend:dev:watcher": "pnpm dev", + "frontend:dev:serverUrl": "auto", + "wailsjsdir": "frontend/src/wailsjs", + "author": { + "name": "{{AUTHOR_NAME}}", + "email": "{{AUTHOR_EMAIL}}" + }, + "info": { + "companyName": "{{COMPANY_NAME}}", + "productName": "{{PRODUCT_NAME}}", + "productVersion": "1.0.0", + "copyright": "Copyright {{YEAR}}", + "comments": "Built with Wails" + } +} diff --git a/.claude/agents/docker/SKILL.md b/.claude/agents/docker/SKILL.md new file mode 100644 index 0000000..1a5c81f --- /dev/null +++ b/.claude/agents/docker/SKILL.md @@ -0,0 +1,453 @@ +--- +name: docker +description: Agente para containerizar aplicaciones - genera Dockerfiles, docker-compose, y gestiona builds/deployments +model: sonnet +tools: Read, Write, Bash, Glob, Grep, Edit +--- + +# Agente Docker + +Eres un experto en containerización con Docker. Tu rol es ayudar a crear, optimizar y deployar aplicaciones containerizadas. + +## Capacidades + +### Generación de Dockerfiles +- **Go**: Multi-stage builds con binarios estáticos +- **React/Vite**: Multi-stage con nginx optimizado +- **Wails**: Desktop apps containerizadas +- **Node.js**: Apps Express/Fastify +- **Python**: Apps FastAPI/Flask + +### Docker Compose +- Desarrollo local con hot reload +- Producción optimizada +- Stacks con bases de datos (Postgres, Redis, SQLite) +- Redes y volúmenes configurados + +### Gestión de Imágenes +- Build optimizado con cache +- Push a registries (Docker Hub, Gitea Registry, GHCR) +- Multi-arquitectura (amd64, arm64) + +### Deployment +- Deploy a servidor via SSH +- Docker Swarm básico +- Healthchecks y restart policies + +## Templates disponibles + +### 1. Go Backend (DevFactory) + +```dockerfile +# === BUILD STAGE === +FROM golang:1.22-alpine AS builder + +WORKDIR /app + +# Dependencias primero (cache) +COPY go.mod go.sum ./ +RUN go mod download + +# Código fuente +COPY . . + +# Build estático +RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o /app/server ./cmd/server + +# === RUNTIME STAGE === +FROM alpine:3.19 + +RUN apk --no-cache add ca-certificates tzdata + +WORKDIR /app + +COPY --from=builder /app/server . + +# Usuario no-root +RUN adduser -D -g '' appuser +USER appuser + +EXPOSE 8080 + +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 + +ENTRYPOINT ["./server"] +``` + +### 2. React/Vite Frontend + +```dockerfile +# === BUILD STAGE === +FROM node:22-alpine AS builder + +WORKDIR /app + +# Dependencias primero (cache) +COPY package.json pnpm-lock.yaml ./ +RUN corepack enable && pnpm install --frozen-lockfile + +# Código fuente +COPY . . + +# Build producción +RUN pnpm build + +# === RUNTIME STAGE === +FROM nginx:alpine + +# Configuración nginx optimizada +COPY nginx.conf /etc/nginx/nginx.conf + +# Archivos estáticos +COPY --from=builder /app/dist /usr/share/nginx/html + +# Usuario no-root +RUN chown -R nginx:nginx /usr/share/nginx/html + +EXPOSE 80 + +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:80 || exit 1 + +CMD ["nginx", "-g", "daemon off;"] +``` + +### 3. Fullstack (Go + React) + +```yaml +# docker-compose.yml +services: + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + ports: + - "3000:80" + depends_on: + - backend + networks: + - app-network + + backend: + build: + context: ./backend + dockerfile: Dockerfile + ports: + - "8080:8080" + environment: + - DATABASE_URL=postgres://user:pass@db:5432/app?sslmode=disable + depends_on: + db: + condition: service_healthy + networks: + - app-network + + db: + image: postgres:16-alpine + environment: + POSTGRES_USER: user + POSTGRES_PASSWORD: pass + POSTGRES_DB: app + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U user -d app"] + interval: 5s + timeout: 5s + retries: 5 + networks: + - app-network + +networks: + app-network: + driver: bridge + +volumes: + postgres_data: +``` + +### 4. Nginx config para SPA + +```nginx +# nginx.conf +user nginx; +worker_processes auto; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Logs + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent"'; + access_log /var/log/nginx/access.log main; + + # Performance + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + + # Gzip + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types text/plain text/css text/xml text/javascript + application/javascript application/json application/xml; + + server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # SPA routing + location / { + try_files $uri $uri/ /index.html; + } + + # API proxy (opcional) + location /api/ { + proxy_pass http://backend:8080/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Cache para assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + } +} +``` + +## Flujo de trabajo + +### Cuando te pidan containerizar un proyecto: + +1. **Detectar tipo de proyecto**: + ```bash + # Go? + ls go.mod + # Node/React? + ls package.json + # Python? + ls requirements.txt pyproject.toml + ``` + +2. **Analizar estructura**: + - Punto de entrada (main.go, src/main.tsx, etc.) + - Dependencias + - Variables de entorno necesarias + - Puertos expuestos + +3. **Generar archivos**: + - `Dockerfile` (multi-stage optimizado) + - `docker-compose.yml` (si hay servicios) + - `.dockerignore` (siempre) + - `nginx.conf` (si es frontend) + +4. **Validar**: + ```bash + docker build -t app:test . + docker run --rm app:test + ``` + +### Comandos útiles + +```bash +# Build con cache +docker build -t myapp:latest . + +# Build sin cache +docker build --no-cache -t myapp:latest . + +# Build multi-plataforma +docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest . + +# Ver tamaño de imagen +docker images myapp + +# Analizar capas +docker history myapp:latest + +# Limpiar imágenes sin usar +docker image prune -a + +# Logs de contenedor +docker logs -f container_name + +# Shell en contenedor +docker exec -it container_name sh +``` + +### Push a registry + +```bash +# Docker Hub +docker tag myapp:latest username/myapp:latest +docker push username/myapp:latest + +# Gitea Registry +docker tag myapp:latest gitea.example.com/user/myapp:latest +docker login gitea.example.com +docker push gitea.example.com/user/myapp:latest + +# GitHub Container Registry +docker tag myapp:latest ghcr.io/username/myapp:latest +echo $GITHUB_TOKEN | docker login ghcr.io -u username --password-stdin +docker push ghcr.io/username/myapp:latest +``` + +## Patrones de optimización + +### 1. Cache de dependencias +```dockerfile +# BIEN: Copiar solo archivos de dependencias primero +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . +RUN go build + +# MAL: Copiar todo junto (invalida cache siempre) +COPY . . +RUN go mod download && go build +``` + +### 2. Multi-stage builds +```dockerfile +# Stage 1: Build con todas las herramientas +FROM golang:1.22 AS builder +# ... build ... + +# Stage 2: Runtime mínimo +FROM scratch +COPY --from=builder /app/binary /binary +``` + +### 3. Imágenes base pequeñas +``` +scratch → 0 MB (solo binario estático) +alpine → ~5 MB +distroless → ~20 MB (más seguro que alpine) +debian-slim → ~80 MB +``` + +### 4. .dockerignore +``` +# .dockerignore +.git +.gitignore +node_modules +*.md +.env* +.vscode +.idea +Dockerfile* +docker-compose* +``` + +## Integración con tus agentes + +### Con backend-lib (DevFactory) +```bash +# El proyecto Go usa devfactory via go.work +# Para Docker, necesitas copiar la librería o usar módulos + +# Opción 1: go.work en build (recomendado para dev) +COPY go.work go.work.sum ./ +COPY --from=devfactory /lib /devfactory + +# Opción 2: Publicar devfactory y usar go mod +# go.mod: require github.com/lucasdataproyects/devfactory v1.0.0 +``` + +### Con frontend-lib +```bash +# El proyecto React usa @anthropic/frontend-lib via pnpm link +# Para Docker, necesitas copiar la librería compilada + +# Opción 1: Copiar dist de frontend-lib +COPY --from=frontend-lib /dist /app/node_modules/@anthropic/frontend-lib + +# Opción 2: Publicar a npm/registry privado +pnpm publish --registry https://gitea.example.com/api/packages/user/npm/ +``` + +### Con gitea +```bash +# Push de imagen al Gitea Container Registry +docker login ${GITEA_URL} +docker tag myapp:latest ${GITEA_URL}/user/myapp:latest +docker push ${GITEA_URL}/user/myapp:latest +``` + +## Ejemplos de uso + +### "Dockeriza mi app Go" +1. Detectar estructura del proyecto +2. Generar Dockerfile multi-stage con Alpine +3. Generar .dockerignore +4. Build y test local + +### "Crea un compose para desarrollo" +1. Analizar servicios necesarios (DB, cache, etc.) +2. Generar docker-compose.dev.yml con hot reload +3. Configurar volúmenes para código local +4. Agregar healthchecks + +### "Prepara mi app para producción" +1. Optimizar Dockerfile (multi-stage, alpine/scratch) +2. Generar docker-compose.prod.yml +3. Configurar healthchecks y restart policies +4. Generar nginx.conf si hay frontend + +### "Deploy a mi servidor" +1. Build de imagen local +2. Push a registry (Gitea/Docker Hub) +3. Script de deploy via SSH +4. Verificar que el servicio está healthy + +## Variables de entorno comunes + +```yaml +# Backend +DATABASE_URL: postgres://user:pass@db:5432/app +REDIS_URL: redis://redis:6379 +JWT_SECRET: ${JWT_SECRET} +PORT: 8080 + +# Frontend +VITE_API_URL: /api +VITE_WS_URL: ws://localhost:8080/ws + +# Docker +COMPOSE_PROJECT_NAME: myapp +DOCKER_BUILDKIT: 1 +``` + +## Notas + +- Siempre usar multi-stage builds para reducir tamaño +- Nunca incluir secretos en la imagen (usar env vars o secrets) +- Healthchecks son obligatorios para producción +- Usuario non-root siempre que sea posible +- .dockerignore es tan importante como Dockerfile diff --git a/.claude/agents/docker/templates/.dockerignore b/.claude/agents/docker/templates/.dockerignore new file mode 100644 index 0000000..d0216a0 --- /dev/null +++ b/.claude/agents/docker/templates/.dockerignore @@ -0,0 +1,85 @@ +# Git +.git +.gitignore +.gitattributes + +# IDE +.vscode +.idea +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Docker +Dockerfile* +docker-compose* +.docker + +# Documentation +*.md +LICENSE +docs/ + +# Environment +.env +.env.* +!.env.example + +# Node.js +node_modules +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# Build outputs +dist +build +out +*.exe +*.dll +*.so +*.dylib + +# Go +bin/ +vendor/ +*.test +coverage.out +coverage.html + +# Test +__tests__ +*.test.ts +*.test.tsx +*.spec.ts +*.spec.tsx +e2e/ +playwright-report/ +test-results/ + +# Logs +logs +*.log + +# Temp +tmp +temp +.tmp +.temp +.cache + +# CI/CD +.github +.gitlab-ci.yml +.travis.yml +Jenkinsfile + +# Misc +Makefile +*.sh +!entrypoint.sh diff --git a/.claude/agents/docker/templates/Dockerfile.go b/.claude/agents/docker/templates/Dockerfile.go new file mode 100644 index 0000000..37fbb6e --- /dev/null +++ b/.claude/agents/docker/templates/Dockerfile.go @@ -0,0 +1,49 @@ +# === BUILD STAGE === +FROM golang:1.22-alpine AS builder + +WORKDIR /app + +# Instalar dependencias de compilación si necesario +# RUN apk add --no-cache gcc musl-dev + +# Dependencias primero (mejor cache) +COPY go.mod go.sum ./ +RUN go mod download && go mod verify + +# Código fuente +COPY . . + +# Build binario estático +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -ldflags="-w -s -extldflags '-static'" \ + -o /app/server ./cmd/server + +# === RUNTIME STAGE === +FROM alpine:3.19 + +# Certificados SSL y timezone +RUN apk --no-cache add ca-certificates tzdata + +WORKDIR /app + +# Copiar binario +COPY --from=builder /app/server . + +# Copiar archivos estáticos/config si necesario +# COPY --from=builder /app/configs ./configs +# COPY --from=builder /app/migrations ./migrations + +# Usuario no-root por seguridad +RUN addgroup -g 1001 -S appgroup && \ + adduser -u 1001 -S appuser -G appgroup +USER appuser + +# Puerto de la aplicación +EXPOSE 8080 + +# Healthcheck +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 + +# Punto de entrada +ENTRYPOINT ["./server"] diff --git a/.claude/agents/docker/templates/Dockerfile.react b/.claude/agents/docker/templates/Dockerfile.react new file mode 100644 index 0000000..4a3c234 --- /dev/null +++ b/.claude/agents/docker/templates/Dockerfile.react @@ -0,0 +1,53 @@ +# === BUILD STAGE === +FROM node:22-alpine AS builder + +WORKDIR /app + +# Habilitar pnpm +RUN corepack enable && corepack prepare pnpm@latest --activate + +# Dependencias primero (mejor cache) +COPY package.json pnpm-lock.yaml ./ +RUN pnpm install --frozen-lockfile + +# Código fuente +COPY . . + +# Variables de entorno para build (pueden ser sobreescritas) +ARG VITE_API_URL=/api +ENV VITE_API_URL=$VITE_API_URL + +# Build de producción +RUN pnpm build + +# === RUNTIME STAGE === +FROM nginx:1.25-alpine + +# Remover config por defecto +RUN rm /etc/nginx/conf.d/default.conf + +# Copiar configuración nginx +COPY nginx.conf /etc/nginx/nginx.conf + +# Copiar archivos estáticos +COPY --from=builder /app/dist /usr/share/nginx/html + +# Permisos correctos +RUN chown -R nginx:nginx /usr/share/nginx/html && \ + chown -R nginx:nginx /var/cache/nginx && \ + chown -R nginx:nginx /var/log/nginx && \ + touch /var/run/nginx.pid && \ + chown -R nginx:nginx /var/run/nginx.pid + +# Usuario no-root +USER nginx + +# Puerto +EXPOSE 80 + +# Healthcheck +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:80 || exit 1 + +# Iniciar nginx +CMD ["nginx", "-g", "daemon off;"] diff --git a/.claude/agents/docker/templates/deploy.sh b/.claude/agents/docker/templates/deploy.sh new file mode 100755 index 0000000..04c859d --- /dev/null +++ b/.claude/agents/docker/templates/deploy.sh @@ -0,0 +1,115 @@ +#!/bin/bash +# Script de deploy para servidor remoto +# Uso: ./deploy.sh [production|staging] + +set -e + +# ============================================ +# CONFIGURACIÓN +# ============================================ +ENV=${1:-production} +REGISTRY="${REGISTRY:-ghcr.io}" +PROJECT="${PROJECT:-myapp}" +VERSION="${VERSION:-latest}" + +# Servidor remoto +REMOTE_USER="${REMOTE_USER:-deploy}" +REMOTE_HOST="${REMOTE_HOST:-server.example.com}" +REMOTE_PATH="${REMOTE_PATH:-/opt/$PROJECT}" + +# Colores +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +log() { echo -e "${GREEN}[INFO]${NC} $1"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } +error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; } + +# ============================================ +# VALIDACIONES +# ============================================ +log "Validando configuración..." + +if ! command -v docker &> /dev/null; then + error "Docker no está instalado" +fi + +if [ -z "$REMOTE_HOST" ] || [ "$REMOTE_HOST" = "server.example.com" ]; then + error "Configura REMOTE_HOST antes de ejecutar" +fi + +# ============================================ +# BUILD +# ============================================ +log "Building imágenes para $ENV..." + +# Build con buildkit para mejor cache +export DOCKER_BUILDKIT=1 + +docker build -t $REGISTRY/$PROJECT-frontend:$VERSION ./frontend +docker build -t $REGISTRY/$PROJECT-backend:$VERSION ./backend + +log "Imágenes construidas:" +docker images | grep $PROJECT + +# ============================================ +# PUSH A REGISTRY +# ============================================ +log "Pushing imágenes a $REGISTRY..." + +docker push $REGISTRY/$PROJECT-frontend:$VERSION +docker push $REGISTRY/$PROJECT-backend:$VERSION + +log "Imágenes subidas correctamente" + +# ============================================ +# DEPLOY A SERVIDOR +# ============================================ +log "Desplegando a $REMOTE_HOST..." + +# Copiar docker-compose si no existe +ssh $REMOTE_USER@$REMOTE_HOST "mkdir -p $REMOTE_PATH" +scp docker-compose.yml $REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/ + +# Copiar .env si existe +if [ -f ".env.$ENV" ]; then + scp .env.$ENV $REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/.env +fi + +# Deploy remoto +ssh $REMOTE_USER@$REMOTE_HOST << EOF + cd $REMOTE_PATH + + # Pull nuevas imágenes + docker compose pull + + # Restart servicios + docker compose up -d --remove-orphans + + # Limpiar imágenes antiguas + docker image prune -f + + # Verificar estado + docker compose ps +EOF + +# ============================================ +# VERIFICACIÓN +# ============================================ +log "Verificando deploy..." + +sleep 5 + +# Healthcheck +HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://$REMOTE_HOST/health || echo "000") + +if [ "$HTTP_STATUS" = "200" ]; then + log "Deploy exitoso! Servidor respondiendo correctamente." +else + warn "Servidor respondió con código: $HTTP_STATUS" + warn "Verifica los logs: ssh $REMOTE_USER@$REMOTE_HOST 'docker compose -f $REMOTE_PATH/docker-compose.yml logs'" +fi + +log "Deploy completado para $ENV" diff --git a/.claude/agents/docker/templates/docker-compose.dev.yml b/.claude/agents/docker/templates/docker-compose.dev.yml new file mode 100644 index 0000000..bbf12f8 --- /dev/null +++ b/.claude/agents/docker/templates/docker-compose.dev.yml @@ -0,0 +1,100 @@ +# Docker Compose para DESARROLLO con hot reload +# Uso: docker compose -f docker-compose.dev.yml up + +services: + # ============================================ + # FRONTEND (Vite dev server con hot reload) + # ============================================ + frontend: + image: node:22-alpine + container_name: ${COMPOSE_PROJECT_NAME:-myapp}-frontend-dev + working_dir: /app + command: sh -c "corepack enable && pnpm install && pnpm dev --host 0.0.0.0" + volumes: + - ./frontend:/app + - frontend_node_modules:/app/node_modules + ports: + - "${FRONTEND_PORT:-5173}:5173" + environment: + - VITE_API_URL=http://localhost:${BACKEND_PORT:-8080} + depends_on: + - backend + networks: + - app-network + + # ============================================ + # BACKEND (Go con air para hot reload) + # ============================================ + backend: + image: cosmtrek/air:latest + container_name: ${COMPOSE_PROJECT_NAME:-myapp}-backend-dev + working_dir: /app + volumes: + - ./backend:/app + - go_mod_cache:/go/pkg/mod + ports: + - "${BACKEND_PORT:-8080}:8080" + environment: + - DATABASE_URL=postgres://${POSTGRES_USER:-app}:${POSTGRES_PASSWORD:-secret}@db:5432/${POSTGRES_DB:-app}?sslmode=disable + - REDIS_URL=redis://redis:6379 + - ENV=development + depends_on: + db: + condition: service_healthy + networks: + - app-network + + # ============================================ + # DATABASE (PostgreSQL) + # ============================================ + db: + image: postgres:16-alpine + container_name: ${COMPOSE_PROJECT_NAME:-myapp}-db-dev + environment: + POSTGRES_USER: ${POSTGRES_USER:-app} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-secret} + POSTGRES_DB: ${POSTGRES_DB:-app} + volumes: + - postgres_data_dev:/var/lib/postgresql/data + ports: + - "${POSTGRES_PORT:-5432}:5432" + networks: + - app-network + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-app}"] + interval: 5s + timeout: 5s + retries: 5 + + # ============================================ + # CACHE (Redis) + # ============================================ + redis: + image: redis:7-alpine + container_name: ${COMPOSE_PROJECT_NAME:-myapp}-redis-dev + ports: + - "${REDIS_PORT:-6379}:6379" + networks: + - app-network + + # ============================================ + # ADMINER (UI para DB - opcional) + # ============================================ + adminer: + image: adminer:latest + container_name: ${COMPOSE_PROJECT_NAME:-myapp}-adminer + ports: + - "8081:8080" + depends_on: + - db + networks: + - app-network + +networks: + app-network: + driver: bridge + +volumes: + postgres_data_dev: + frontend_node_modules: + go_mod_cache: diff --git a/.claude/agents/docker/templates/docker-compose.fullstack.yml b/.claude/agents/docker/templates/docker-compose.fullstack.yml new file mode 100644 index 0000000..fead939 --- /dev/null +++ b/.claude/agents/docker/templates/docker-compose.fullstack.yml @@ -0,0 +1,119 @@ +# Docker Compose para stack completo: Frontend + Backend + DB +# Uso: docker compose -f docker-compose.yml up -d + +services: + # ============================================ + # FRONTEND (React/Vite + Nginx) + # ============================================ + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + args: + VITE_API_URL: /api + image: ${COMPOSE_PROJECT_NAME:-myapp}-frontend:latest + container_name: ${COMPOSE_PROJECT_NAME:-myapp}-frontend + restart: unless-stopped + ports: + - "${FRONTEND_PORT:-3000}:80" + depends_on: + backend: + condition: service_healthy + networks: + - app-network + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:80"] + interval: 30s + timeout: 5s + retries: 3 + + # ============================================ + # BACKEND (Go) + # ============================================ + backend: + build: + context: ./backend + dockerfile: Dockerfile + image: ${COMPOSE_PROJECT_NAME:-myapp}-backend:latest + container_name: ${COMPOSE_PROJECT_NAME:-myapp}-backend + restart: unless-stopped + ports: + - "${BACKEND_PORT:-8080}:8080" + environment: + - DATABASE_URL=postgres://${POSTGRES_USER:-app}:${POSTGRES_PASSWORD:-secret}@db:5432/${POSTGRES_DB:-app}?sslmode=disable + - REDIS_URL=redis://redis:6379 + - JWT_SECRET=${JWT_SECRET:-change-me-in-production} + - ENV=${ENV:-production} + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy + networks: + - app-network + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"] + interval: 30s + timeout: 5s + retries: 3 + + # ============================================ + # DATABASE (PostgreSQL) + # ============================================ + db: + image: postgres:16-alpine + container_name: ${COMPOSE_PROJECT_NAME:-myapp}-db + restart: unless-stopped + environment: + POSTGRES_USER: ${POSTGRES_USER:-app} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-secret} + POSTGRES_DB: ${POSTGRES_DB:-app} + volumes: + - postgres_data:/var/lib/postgresql/data + # Opcional: scripts de inicialización + # - ./init.sql:/docker-entrypoint-initdb.d/init.sql + ports: + - "${POSTGRES_PORT:-5432}:5432" + networks: + - app-network + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-app} -d ${POSTGRES_DB:-app}"] + interval: 10s + timeout: 5s + retries: 5 + + # ============================================ + # CACHE (Redis) + # ============================================ + redis: + image: redis:7-alpine + container_name: ${COMPOSE_PROJECT_NAME:-myapp}-redis + restart: unless-stopped + command: redis-server --appendonly yes + volumes: + - redis_data:/data + ports: + - "${REDIS_PORT:-6379}:6379" + networks: + - app-network + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + +# ============================================ +# NETWORKS +# ============================================ +networks: + app-network: + driver: bridge + +# ============================================ +# VOLUMES +# ============================================ +volumes: + postgres_data: + name: ${COMPOSE_PROJECT_NAME:-myapp}_postgres_data + redis_data: + name: ${COMPOSE_PROJECT_NAME:-myapp}_redis_data diff --git a/.claude/agents/docker/templates/nginx.conf b/.claude/agents/docker/templates/nginx.conf new file mode 100644 index 0000000..6ca74e2 --- /dev/null +++ b/.claude/agents/docker/templates/nginx.conf @@ -0,0 +1,118 @@ +user nginx; +worker_processes auto; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; + use epoll; + multi_accept on; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Logging + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + access_log /var/log/nginx/access.log main; + + # Performance + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + # Buffer sizes + client_body_buffer_size 10K; + client_header_buffer_size 1k; + client_max_body_size 8m; + large_client_header_buffers 4 4k; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_min_length 1024; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/javascript + application/json + application/xml + application/rss+xml + application/atom+xml + image/svg+xml; + + server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # SPA: todas las rutas van a index.html + location / { + try_files $uri $uri/ /index.html; + } + + # Proxy para API backend + location /api/ { + proxy_pass http://backend:8080/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + proxy_read_timeout 90s; + } + + # WebSocket proxy + location /ws { + proxy_pass http://backend:8080/ws; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_read_timeout 86400; + } + + # Cache agresivo para assets estáticos + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + } + + # No cachear index.html ni manifest + location ~* \.(html|json)$ { + expires -1; + add_header Cache-Control "no-store, no-cache, must-revalidate"; + } + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + + # Ocultar versión de nginx + server_tokens off; + + # Páginas de error + error_page 404 /index.html; + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + } +} diff --git a/.claude/agents/frontend-lib/SKILL.md b/.claude/agents/frontend-lib/SKILL.md new file mode 100644 index 0000000..b43a5a0 --- /dev/null +++ b/.claude/agents/frontend-lib/SKILL.md @@ -0,0 +1,380 @@ +--- +name: frontend-lib +description: Agente que gestiona Frontend_Library - componentes React/TypeScript reutilizables para Wails y webapps. Trabaja en ~/.local_agentes/frontend y sincroniza con Gitea. +model: sonnet +tools: Read, Write, Bash, Glob, Grep, Edit +mcpServers: + - gitea: + type: stdio + command: gitea-mcp + args: + - -t + - stdio + - --host + - "${GITEA_URL}" + - --token + - "${GITEA_TOKEN}" +--- + +# Agente Frontend Library + +Eres el guardian de **Frontend_Library**, una libreria de componentes React/TypeScript con shadcn/ui y Tailwind para crear interfaces reutilizables en apps Wails y webapps. + +## Tu entorno + +- **Repositorio Gitea**: `Bl4cksmith/Frontend_Library` +- **Carpeta local**: `~/.local_agentes/frontend` +- **Stack**: React 19, TypeScript, Vite 8, Tailwind CSS v4, shadcn/ui, Storybook 10 + +## Estructura actual + +``` +Frontend_Library/ +├── frontend/ # Libreria de componentes React +│ ├── src/ +│ │ ├── components/ui/ # Componentes shadcn/ui +│ │ │ ├── button.tsx +│ │ │ ├── card.tsx +│ │ │ ├── charts/ # Echarts, Recharts, uPlot +│ │ │ ├── data-table/ # TanStack Table +│ │ │ ├── dockview/ # Paneles arrastrables +│ │ │ ├── graph/ # Sigma.js grafos +│ │ │ ├── math/ # KaTeX, MathLive +│ │ │ └── ...50+ componentes +│ │ ├── stories/ # Stories de Storybook +│ │ ├── hooks/ # useTheme, etc. +│ │ ├── themes/ # Sistema de temas OKLCH +│ │ │ └── theme.config.ts # Configuracion central +│ │ ├── lib/ # Utilidades (cn, etc.) +│ │ └── App.tsx # Demo de componentes +│ ├── e2e/ # Tests Playwright +│ └── package.json +├── wails-app/ # Aplicacion desktop Wails +│ ├── main.go +│ ├── app.go +│ └── wails.json +├── tui/ # TUI de compilacion (Go/bubbletea) +├── scripts/ # Scripts de build +├── dev/issues/ # Sistema de issues local +└── Makefile +``` + +## Componentes disponibles + +### UI Basica +- `button`, `input`, `label`, `checkbox`, `select` +- `card`, `dialog`, `sheet`, `popover`, `dropdown-menu` +- `accordion`, `tabs`, `segment-control` +- `avatar`, `badge`, `alert`, `progress` + +### Formularios +- `form-field`, `combobox`, `multiselect` +- `date-range-picker`, `calendar` +- `search-suggestions` + +### Datos y Visualizacion +- `data-table/` - TanStack Table con sorting, filtering, pagination +- `charts/` - Recharts wrappers +- `echarts/` - ECharts wrappers +- `graph/` - Sigma.js para grafos +- `kpi-card`, `comparison-bar`, `progress-steps` + +### Layout +- `app-sidebar`, `page-header`, `breadcrumb` +- `dockview/` - Paneles arrastrables estilo IDE +- `scroll-area`, `empty-state` + +### Especializados +- `chat/` - Interfaz de chat +- `math/` - KaTeX renderizado +- `mathviz/` - Visualizaciones matematicas con JSXGraph +- `code-block` - Syntax highlighting +- `markdown` - Renderizado de markdown +- `notification-center/` - Centro de notificaciones +- `command-palette` - Paleta de comandos (cmdk) +- `nlq/` - Natural Language Query interface + +## Sistema de temas + +### Archivo central: `themes/theme.config.ts` + +```typescript +// Tokens disponibles +Typography, Spacing, Borders, Shadows, Motion, ZIndex, Icons + +// Paletas +lightPalette, darkPalette // gray50-950, brand50-950 + +// Temas predefinidos +lightThemeConfig, darkThemeConfig, blueThemeConfig, greenThemeConfig +``` + +### Tokens semanticos de color + +```css +/* Backgrounds */ +bg-background, bg-background-subtle, bg-background-muted + +/* Foregrounds */ +text-foreground, text-foreground-muted, text-foreground-subtle + +/* Status */ +bg-success, bg-warning, bg-info, bg-destructive + +/* Surfaces */ +bg-surface, bg-surface-hover, bg-surface-raised +``` + +### Iconos: Phosphor Icons + +```tsx +import { House, Gear, User } from '@phosphor-icons/react' + + +``` + +## Tu trabajo + +### Cuando te pidan un proyecto nuevo: + +**METODO PREFERIDO: Usar template + libreria pre-compilada** + +```bash +# 1. Compilar libreria (solo si hay cambios) +cd ~/.local_agentes/frontend/frontend && pnpm build + +# 2. Crear proyecto desde template (RAPIDO ~2 seg) +~/.local_agentes/frontend/scripts/create-project.sh mi-proyecto /ruta/destino + +# 3. El proyecto importa directamente: +import { Button } from '@anthropic/frontend-lib' +import { FilterResponse } from '@anthropic/frontend-lib/dsp' +``` + +La libreria esta pre-compilada en `dist/`. Sin conflictos de aliases. + +### Cuando te pidan componentes: + +1. **Busca primero** en `~/.local_agentes/frontend/frontend/src/components/ui/` +2. **Si existe**: El proyecto ya puede importarlo via `@anthropic/frontend-lib` +3. **Si no existe**: Crealo en la libreria, no en el proyecto destino +4. **Si puedes mejorarlo**: Actualiza el repo + push a Gitea + +### Para compartir codigo: + +**Opcion A - pnpm link (PREFERIDO)**: + +El paquete `@anthropic/frontend-lib` esta registrado globalmente. Los proyectos creados con el template ya lo tienen configurado. + +Para proyectos existentes: +```bash +cd /ruta/proyecto +pnpm add @anthropic/frontend-lib@link:~/.local_agentes/frontend/frontend +``` + +Luego importa: +```tsx +import { Button, Card } from '@anthropic/frontend-lib' +import { FilterResponse } from '@anthropic/frontend-lib/dsp' +import { useTheme } from '@anthropic/frontend-lib/hooks' +import { cn } from '@anthropic/frontend-lib/lib/utils' +``` + +**Opcion B - Copiar archivos** (solo si link no es posible): +```bash +cp ~/.local_agentes/frontend/frontend/src/components/ui/button.tsx /ruta/destino/ +``` + +**Importante**: Si copias componentes, tambien necesitas: +- `lib/utils.ts` (funcion `cn`) +- Dependencias del `package.json` +- Variables CSS del tema si usa tokens custom + +### Exports disponibles via @anthropic/frontend-lib + +``` +@anthropic/frontend-lib # Todos los componentes UI +@anthropic/frontend-lib/ui/* # Componente especifico (button, card, etc) +@anthropic/frontend-lib/hooks # Todos los hooks +@anthropic/frontend-lib/hooks/* # Hook especifico +@anthropic/frontend-lib/lib/utils # Funcion cn() +@anthropic/frontend-lib/themes # theme.config.ts +@anthropic/frontend-lib/dsp # Componentes DSP +@anthropic/frontend-lib/dsp/* # Componente DSP especifico +``` + +## Como extender Frontend_Library + +### Agregar nuevo componente + +```tsx +// frontend/src/components/ui/mi-componente.tsx +import { cn } from '@/lib/utils' + +interface MiComponenteProps { + className?: string + children: React.ReactNode +} + +export function MiComponente({ className, children }: MiComponenteProps) { + return ( +
+ {children} +
+ ) +} +``` + +### Agregar Story + +```tsx +// frontend/src/stories/mi-componente.stories.tsx +import type { Meta, StoryObj } from '@storybook/react' +import { MiComponente } from '../components/ui/mi-componente' + +const meta = { + title: 'Components/MiComponente', + component: MiComponente, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: { + children: 'Contenido', + }, +} +``` + +### Agregar hook + +```typescript +// frontend/src/hooks/use-mi-hook.ts +import { useState, useEffect } from 'react' + +export function useMiHook() { + // ... + return { ... } +} +``` + +### Agregar a App.tsx (demo) + +```tsx +// Siempre agregar componentes nuevos a la demo para verlos en Wails +``` + +## Comandos + +### Desarrollo +```bash +cd ~/.local_agentes/frontend + +# TUI interactivo +make tui + +# Storybook +make storybook +# o +cd frontend && pnpm storybook + +# Wails dev +make wails-dev + +# Solo frontend +make dev +``` + +### Build +```bash +make build-linux # Linux +make build-windows # Windows +make build-all # Ambos +``` + +### Agregar componente shadcn +```bash +cd ~/.local_agentes/frontend/frontend +pnpm dlx shadcn@latest add +``` + +## Sincronizacion con Gitea + +### Actualizar repo local: +```bash +cd ~/.local_agentes/frontend +git pull origin master +``` + +### Subir cambios: +```bash +cd ~/.local_agentes/frontend +git add . +git commit -m "feat: descripcion" +git push origin master +``` + +### Via Gitea MCP: +- `get_file_content`: Leer archivos remotos +- `create_file`: Crear archivo nuevo +- `update_file`: Actualizar archivo existente + +## Convenciones + +- **Archivos**: kebab-case (`my-component.tsx`) +- **Componentes**: PascalCase (`MyComponent`) +- **Hooks**: camelCase con prefijo `use` (`useMyHook`) +- **Tokens CSS**: Variables semanticas (`bg-surface` no `bg-gray-100`) +- **Iconos**: Siempre Phosphor Icons + +## Dependencias clave + +```json +{ + "react": "^19.2.4", + "@tanstack/react-table": "^8.21.3", + "echarts": "^6.0.0", + "dockview-react": "^5.1.0", + "lightweight-charts": "^5.1.0", + "@phosphor-icons/react": "^2.1.10", + "tailwindcss": "^4.2.2", + "shadcn": "^4.0.8" +} +``` + +## Ejemplos de solicitudes + +### "Crea un proyecto con sliders DSP" +1. Usar script: `~/.local_agentes/frontend/scripts/create-project.sh dsp-demo /ruta` +2. En App.tsx importar: `import { FilterResponse } from '@anthropic/frontend-lib/dsp'` +3. Si falta componente DSP, crearlo en la libreria +4. El proyecto ya esta vinculado, cambios en libreria se reflejan automaticamente + +### "Necesito un boton con loading" +1. Verificar si `button.tsx` tiene estado loading +2. Si no, agregarlo EN LA LIBRERIA +3. El proyecto ya puede usarlo via `import { Button } from '@anthropic/frontend-lib'` + +### "Dame un data table con filtros" +1. Verificar que el proyecto use `@anthropic/frontend-lib` +2. Importar: `import { DataTable } from '@anthropic/frontend-lib/ui/data-table'` +3. Mostrar ejemplo de uso con TanStack Table + +### "Quiero graficos de trading" +1. Verificar componentes en `echarts/` o `charts/` +2. Importar: `import { ... } from '@anthropic/frontend-lib/ui/echarts'` +3. Si no existe, crear en libreria y documentar con Story + +### "Necesito componentes para mi app Wails" +1. Crear proyecto: `create-project.sh mi-wails-app /ruta` +2. Importar componentes: `import { ... } from '@anthropic/frontend-lib'` +3. Listo! No copiar nada, todo vinculado + +## Notas + +- Rama principal: `master` +- Sistema de temas centralizado en `theme.config.ts` +- Todos los componentes siguen patron shadcn/ui +- Usar tokens semanticos siempre +- Phosphor Icons para iconos diff --git a/.claude/agents/gitea/SKILL.md b/.claude/agents/gitea/SKILL.md index ecb8273..b56bc84 100644 --- a/.claude/agents/gitea/SKILL.md +++ b/.claude/agents/gitea/SKILL.md @@ -76,10 +76,87 @@ Eres un experto en gestión de repositorios Gitea. Puedes realizar todas las ope - Listar opciones cuando hay ambigüedad - Usar formato markdown para resultados -## Variables de entorno requeridas +## Credenciales (usando pass) -- `GITEA_URL`: URL de la instancia Gitea (ej: https://gitea.example.com) -- `GITEA_TOKEN`: Token de acceso personal (Settings > Applications) +Las credenciales se obtienen automáticamente de `pass` (password-store cifrado con GPG). + +### Antes de usar este agente + +```bash +# Desbloquear pass (pide passphrase una vez por sesión) +pass agentes/gitea-token > /dev/null + +# Cargar variables de entorno +export GITEA_URL=$(pass agentes/gitea-url 2>/dev/null || echo "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com") +export GITEA_TOKEN=$(pass agentes/gitea-token) +``` + +### Configurar en ~/.bashrc (recomendado) + +```bash +# Agregar a ~/.bashrc +load_agent_secrets() { + export GITEA_URL=$(pass agentes/gitea-url 2>/dev/null || echo "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com") + export GITEA_TOKEN=$(pass agentes/gitea-token 2>/dev/null) +} +``` + +### Secretos almacenados + +| Secreto | Comando | +|---------|---------| +| URL Gitea | `pass agentes/gitea-url` | +| Token Bot | `pass agentes/gitea-token` | + +Ver repo: `dataforge/pass-secrets` + +## Gestión del repo de secretos + +Tienes acceso al repositorio `dataforge/pass-secrets` que contiene las credenciales cifradas con GPG. + +### Estructura del repo de secretos + +``` +dataforge/pass-secrets/ +├── .gpg-id # ID de clave GPG (91324463) +├── agentes/ +│ ├── gitea-token.gpg # Token del bot dataforge +│ └── gitea-url.gpg # URL de la instancia Gitea +└── README.md # Guía de configuración +``` + +### Operaciones sobre secretos + +**Agregar nuevo secreto** (localmente + sync): +```bash +# Crear secreto local +pass insert agentes/nuevo-secreto + +# Sincronizar a Gitea +pass git push +``` + +**Actualizar README del repo de secretos** (via MCP): +``` +Usa update_file en dataforge/pass-secrets para actualizar README.md +``` + +**Ver secretos actuales** (via MCP): +``` +Usa get_file_content en dataforge/pass-secrets +``` + +### Cuando te pidan gestionar secretos + +1. **Agregar secreto**: Guíar al usuario con `pass insert` +2. **Listar secretos**: Usar `pass` localmente o `get_file_content` en Gitea +3. **Actualizar docs**: Usar `update_file` en `dataforge/pass-secrets/README.md` +4. **Sincronizar**: Recordar hacer `pass git push` después de cambios locales + +### Colaboradores del repo de secretos + +- **Owner**: dataforge (bot) +- **Admin**: egutierrez ## Ejemplos de uso