惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

爱范儿
爱范儿
MyScale Blog
MyScale Blog
Recent Announcements
Recent Announcements
N
Netflix TechBlog - Medium
GbyAI
GbyAI
Vercel News
Vercel News
The GitHub Blog
The GitHub Blog
阮一峰的网络日志
阮一峰的网络日志
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
V
Visual Studio Blog
Martin Fowler
Martin Fowler
腾讯CDC
大猫的无限游戏
大猫的无限游戏
aimingoo的专栏
aimingoo的专栏
云风的 BLOG
云风的 BLOG
J
Java Code Geeks
WordPress大学
WordPress大学
P
Proofpoint News Feed
雷峰网
雷峰网
酷 壳 – CoolShell
酷 壳 – CoolShell
有赞技术团队
有赞技术团队
人人都是产品经理
人人都是产品经理
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Y
Y Combinator Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Spren: mi workspace de escritorio para programar con agentes
Ayoze Barrer · 2026-05-07 · via DEV Community

Hace poco escribí sobre cómo Claude Code y un CLI propio (tv) me ahorran 15 minutos de secretariado por ticket. Funcionó tan bien que me destapó el siguiente cuello de botella: aunque ya no copiaba títulos a mano ni creaba ramas manualmente, mi pantalla seguía teniendo abiertas siete ventanas distintas. Timeview en una pestaña del navegador para ver tareas, GitLab en otra para ver el pipeline, terminal con Claude Code corriendo, VSCode al lado, notas sueltas en otra app y Gemini cuando quería mejorar algún texto.

El secretariado por ticket lo había resuelto. El context-switching seguía intacto.

Spren es lo que construí para resolverlo: una app de escritorio que junta en una sola pantalla todo lo que mi día a día necesita. Terminal con tabs, estado de git en tiempo real, el panel de tareas de Timeview, las notificaciones, el estado del pipeline en GitLab, mi cuota de Claude, un widget de Gemini para preguntas rápidas, y un par de cosas más (TODO, pomodoro, recordatorio para beber agua). En este artículo te cuento cómo está montada y, sobre todo, por qué cada decisión de diseño es la que es, por si te da ideas para hacer tu propio workspace.


El problema: la app perfecta no existía

Cada uno tiene su forma de trabajar.

Warp es una buena terminal. Raycast es un buen launcher. Notion es buena para notas. Pero ninguna de estas resolvía mi problema: yo no necesitaba otra app más, necesitaba una app menos. Una que sustituyera a varias.

El problema con las soluciones existentes:

  • Demasiado generales: dashboards corporativos que muestran 200 cosas que no me importan.
  • Demasiado específicas: cada app hace bien una cosa, y cuando combinas cinco apps tienes el mismo problema de partida.
  • Sin integración con mi stack real: ninguna se conecta a Timeview ni a mi GitLab self-hosted.

Quería algo hecho a medida de mi flujo. Y la única forma de conseguirlo, era creando a Spren.

En el Cosmere, los spren son entidades que se manifiestan cuando las necesitas y se vinculan a quien las invoca


Las decisiones de stack que importan

Spren es una app de escritorio para macOS hecha con Electron + React + JavaScript. No TypeScript, no Tauri, no nada exótico. Te explico por qué cada elección.

Electron sobre Tauri: Tauri es más ligero pero su ecosistema de Node es menor. Yo necesitaba node-pty para terminales reales, simple-git para integración nativa con git, chokidar para watching del filesystem, y la API de safeStorage de Electron para cifrar tokens. Todo eso son problemas resueltos en Electron y problemas a resolver en Tauri. Para una app personal, era el camino claro.

JavaScript sobre TypeScript: decisión consciente. Iteración rápida, pocos colaboradores, y una codebase que no espero que crezca a 50.000 líneas. El coste de tipar todo no compensa el beneficio. (Si esto fuera una app pública con equipo, sería TypeScript sin pensarlo).

electron-vite como starter: HMR funcional, configuración mínima, no me peleo con webpack ni con builds.

TanStack Query para todo lo que viene de red: cache, retry, refetch, polling adaptativo. Si tu UI hace peticiones HTTP, TanStack Query te ahorra el 80% del código de estado... y me encanta!

xterm.js + node-pty para las terminales: las terminales son reales, con shell, history, ANSI colors, todo. Cada tab tiene su propio proceso pty.

safeStorage para tokens (no keytar): la primera versión usaba keytar y dio problemas con el code-signing. safeStorage de Electron es nativo, sin dependencias compiladas, y suficiente para uso personal. Los tokens viven cifrados en ~/Library/Application Support/spren/secrets.json.

Sin SQLite: lo consideré pero no lo necesité. Para los datos que persisto (TODO items, settings, code projects, mappings) un JSON plano vía un módulo settingsManager es más que suficiente. Si en el futuro quiero historial de pomodoros con gráficas, ahí sí entrará SQLite.


La arquitectura, en una imagen mental

Spren tiene tres procesos, como cualquier app de Electron:

  • Main process: todo lo sensible vive aquí — credenciales, llamadas HTTP a APIs externas, ejecución de procesos pty, watchers del filesystem, lectura del Keychain.
  • Preload: un puente tipado vía contextBridge que expone al renderer solo lo que tiene que poder llamar (window.api.timeview.getMyTasks(...) y similares).
  • Renderer: solo React, solo UI. El renderer nunca toca tokens, nunca lee archivos del usuario directamente, nunca llama a APIs externas. Todo va por IPC.

Esta separación es innegociable y vale la pena explicarla porque es lo que permite que una app así sea segura. Si el renderer pudiera acceder a tokens, cualquier vulnerabilidad de XSS comprometería tus credenciales de Timeview, GitLab y Gemini de golpe. Como no puede, el peor caso es que un script malicioso pida llamadas a tu API, pero nunca verá los tokens.


El layout: cuatro columnas que cuentan una historia

La pantalla principal (HOME) son cuatro columnas verticales:

TERMINAL (flex)  |  GIT (320px)  |  TIMEVIEW (400px)  |  WIDGETS (400px)

Enter fullscreen mode Exit fullscreen mode

El orden no es casual. Cuenta una historia de izquierda a derecha: dónde estoy escribiendo código, qué estado tiene mi código localmente, qué tengo pendiente y qué está pasando en mi sistema de tareas, y herramientas auxiliares.

Columna 1: TERMINAL

Multi-tab con Cmd+T, Cmd+W, Cmd+1..9. Cada tab es un pty real con su propio shell, su propio cwd, su propio history. Cuando hago cd en una tab, el resto sigue donde estaba.

Lo más importante: el cwd activo de la tab actual alimenta a las demás columnas. La columna GIT mira al repo donde estoy. La columna TIMEVIEW resalta el proyecto correspondiente. Si cambio de tab, todo se actualiza.

Columna 2: GIT

Lee el repo del cwd actual con simple-git y vigila cambios con chokidar. Muestra:

  • Branch actual, ahead/behind, tracking de upstream.
  • Archivos modificados / staged / untracked, con counters.
  • **Diff: puedo ver que ha cambiado en cada fichero sin abrir el IDE.
  • Bloque GITLAB: estado del MR de la rama actual (draft / opened / merged), último pipeline del proyecto (running / passed / failed), con botón "open in browser" que dispara shell.openExternal.

Detalle de UX: el bloque GITLAB solo aparece si hay datos relevantes. Si estoy en main sin MR, no aparece. Cero ruido.

GIT panel

Columna 3: TIMEVIEW

Tres bloques verticales:

  • MY TASKS: lista paginada de mis tareas, con toggle entre "asignadas a mí" y "sin asignar". Cada item muestra estado, id, título, tags y a quién está asignado. Paginación con scroll infinito vía TanStack Query.
  • UNREADS COUNTERS: cuatro contadores horizontales (rooms / tasks / notifs / docs) que vienen de de Timeview. Sirven como panorama general.
  • NOTIFICATIONS: lista de notificaciones, con borde izquierdo rojo destacado para las no leídas.

Click en una tarea me lleva al detalle. Y desde el detalle puedo escribir comentarios para "preparar el terreno" antes de delegar la tarea a Claude. Eso enlaza con el punto siguiente.

Columna 4: WIDGETS

Una pila vertical: TODO, Gemini, Claude Usage, Pomodoro, Water. Cada uno hace una cosa y la hace bien. Aquí los más interesantes:

  • TODO: items locales con done: false/true, ordenados pendientes-arriba completados-abajo, con botón "clear done" para limpiar de golpe. Persistencia en settingsManager.
  • Gemini: input + send + área de respuesta con streaming. Para "cambia esta frase a algo más profesional" o "tradúceme esto rápido". Modelo hardcoded a gemini-2.5-flash con tier gratuito. Evita salir a otra pestaña.
  • Claude Usage: muestra mi cuota actual (5h / 7d / opus) leída del endpoint OAuth interno de Claude Code. Barras ASCII, color verde / ámbar / rojo según umbral. Toggleable porque es un endpoint undocumented.

Widgets panel


La pieza que cierra el bucle: "Trabaja en esta tarea"

El flujo completo entre Spren y mi CLI tv es esto:

  1. Llega una notificación de Timeview a la columna correspondiente.
  2. Click en la tarea, se abre el detalle.
  3. Si necesito añadir contexto antes de delegar, escribo un comentario directamente desde Spren (pasa por la API de Timeview).
  4. Cuando estoy listo, click en "trabaja en esta tarea".
  5. Spren abre una tab nueva con el cwd en el code project que elija (los tengo configurados en Settings, uno por repo local), ejecuta claude con un prompt que invoca tv tasks work <id>, y a partir de ahí Claude se encarga del resto del flujo descrito en el artículo anterior.

Task

Un detalle interesante es el selector de code project. Una tarea de Timeview no tiene relación 1:1 con un repo. La tarea es "implementar X feature"; el repo donde se resuelve depende de qué área toques (frontend, backend, desktop, mobile). En mi equipo, una misma tarea puede resolverse en timeview/desktop, timeview/app o timeview/api según corresponda. Y cada developer tiene su propia ruta local para cada repo.

Code projects

Por eso Spren guarda una lista de "code projects" — pares nombre + ruta local que tú das de alta. Cuando le doy a "trabaja en esta tarea", el botón muestra "trabaja en esta tarea en <nombre>" y, si tengo más de uno, aparece un dropdown para cambiar antes de pulsar. Recuerda mi última elección entre sesiones.


Decisiones de diseño que cambiaron el resultado

Tres elecciones que parecen menores pero hicieron mucho:

1. Cero polling agresivo, refresh inteligente.

Mi primera versión hacía polling cada 30 segundos a Timeview, GitLab y Gemini. Resultado: la app consumía mucho innecesariamente y Anthropic / GitLab / Timeview veían tráfico constante mío.

La versión actual usa refetchInterval adaptativo de TanStack Query. Por ejemplo: el pipeline de GitLab se refresca cada 10 segundos si está running, cada 60 segundos si está en estado terminal. Las notificaciones de Timeview, refresh manual con botón. La cuota de Claude, cada 5 minutos.

Misma sensación de "estoy al día", una décima parte del tráfico.

2. Keep-alive vs unmount.

Spren tiene tres secciones (HOME, TIMEVIEW, SETTINGS). Cuando cambias de sección, la anterior no se desmonta. Sigue viva pero invisible (visibility: hidden + position: absolute). Las terminales siguen corriendo, las queries mantienen su estado, no se pierde nada.

Esto resolvió un bug famoso de xterm: si la columna terminal se desmontaba con display: none, al volver el contenedor tenía dimensiones 0×0, fitAddon.fit() calculaba 5 filas, y el pty perdía scrollback. Visualmente parecía que la terminal se había encogido. La solución de visibility: hidden + position: absolute mantiene dimensiones reales todo el tiempo.

3. Errores tipados en cada cliente HTTP.

Cada integración (Timeview, GitLab, Gemini) tiene sus propios errores tipados con un campo .code: TIMEVIEW_AUTH_FAILED, GITLAB_NETWORK, GEMINI_RATE_LIMIT, etc. Los IPC handlers nunca lanzan a través del bridge — siempre devuelven { ok: true, data } o { ok: false, error: { code, message } }.

Cuando veo un error en Spren, se exactamente qué pasa: "TOKEN no es válido" en lugar de "Network error". Y el código de UI puede mapear cada code a un mensaje específico y a una acción ("ir a Settings", "reintentar", "abrir Claude Code").


Lecciones inespereadas

Tres cosas que no esperaba al construir Spren:

1. El context-switching no se mide en clicks, se mide en pestañas abiertas.

No es solo el coste de cambiar de Timeview a GitLab. Es el coste acumulado de tener N pestañas abiertas todo el día, cada una pidiéndote atención visual aunque estés en otra. Ir cambiando cada cierto tiempo por si no has visto alguna notificación. Reducir mi número total de pestañas de 7 a 2 (Spren + el navegador) tiene un impacto en concentración que no esperaba.

2. Construir tu propia app te obliga a definir tu propio flujo.

Igual que escribir el CLAUDE.md me obligó a documentar reglas implícitas, construir Spren me obligó a definir qué información necesito ver constantemente y cuál no. Resultado: descubrí que no necesitaba ver "todas las tareas" — necesitaba ver "las mías". No necesitaba ver "todos los pipelines" — necesitaba ver "el último del proyecto en el que trabajo". El proceso de decidir qué meter en cada columna fue el ejercicio de productividad más útil que he hecho en mucho tiempo.

3. Una app personal no necesita ser perfecta; necesita ser tuya.

Spren no es accesible. No tiene tests automáticos. No tiene auto-update. No está firmada para distribución. No funciona en Windows ni Linux. Es deliberadamente cruda: tema oscuro hardcoded, fuente monoespaciada para todo, casi sin animaciones, una densidad de información que asustaría a un diseñador.

Precisamente por eso me funciona. Cada decisión está optimizada para mi caso y solo el mío. No hay tradeoffs con casos de uso que no son los míos. Esa es la diferencia entre una app personal y un producto.

Y me encanta abrirla, me encanta saber que todo lo que tengo delante es útil y cada vez que salto al navegador con frecuencia, pienso si puedo incluirlo en Spren, porque yo decido.


Lo que viene

Lo siguiente que he pensado incluir es agent harness: un panel "AGENTS" en Spren donde pueda lanzar varias instancias de Claude Code en modo no interactivo (claude -p) sobre distintas tareas, y monitorizar su progreso desde Spren — qué archivos están tocando, cuánto llevan, si terminaron OK o con error, con notificación nativa cuando completan.

La idea es pasar de "lanzo Claude en una tab y miro su output, tomando decisiones" a "delego cinco tareas en paralelo y vuelvo cuando estén listas". Si funciona, escribo el siguiente artículo, pero para conseguir eso, es necesario darle a los agentes skills, tools y tareas muy claras.


Cierre

No te voy a decir que tienes que construir tu propia app de escritorio. Lo que sí te digo es: si te encuentras con siete pestañas abiertas y notas que pierdes foco saltando entre ellas, el problema no es disciplina personal. Es que tu herramienta de trabajo está fragmentada en N apps diseñadas por personas distintas con prioridades distintas.

A veces la solución no es encontrar la app perfecta. Es construirla.


Si quieres ver el código de Spren, escribeme. Es deliberadamente personal — no busco contribuciones ni mantenimiento — pero las ideas son "robables". Si construyes algo similar, cuéntame!