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

推荐订阅源

Cyberwarzone
Cyberwarzone
F
Full Disclosure
V
Visual Studio Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
有赞技术团队
有赞技术团队
J
Java Code Geeks
博客园 - 【当耐特】
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 叶小钗
L
LINUX DO - 最新话题
T
Threatpost
S
SegmentFault 最新的问题
Vercel News
Vercel News
云风的 BLOG
云风的 BLOG
C
Cyber Attacks, Cyber Crime and Cyber Security
Google DeepMind News
Google DeepMind News
Know Your Adversary
Know Your Adversary
S
Schneier on Security
V
Vulnerabilities – Threatpost
D
DataBreaches.Net
G
GRAHAM CLULEY
Latest news
Latest news
P
Privacy International News Feed
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
Scott Helme
Scott Helme
L
Lohrmann on Cybersecurity
T
The Exploit Database - CXSecurity.com
Security Latest
Security Latest
G
Google Developers Blog
L
LangChain Blog
MyScale Blog
MyScale Blog
Project Zero
Project Zero
N
News and Events Feed by Topic
Hacker News - Newest:
Hacker News - Newest: "LLM"
大猫的无限游戏
大猫的无限游戏
P
Proofpoint News Feed
Blog — PlanetScale
Blog — PlanetScale
阮一峰的网络日志
阮一峰的网络日志
N
News | PayPal Newsroom
www.infosecurity-magazine.com
www.infosecurity-magazine.com
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
SecWiki News
SecWiki News
T
Tor Project blog
C
Check Point Blog
Google Online Security Blog
Google Online Security Blog
GbyAI
GbyAI
The Last Watchdog
The Last Watchdog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
WordPress大学
WordPress大学

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 Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
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!