Commit Graph

33 Commits

Author SHA1 Message Date
egutierrez 2401eb5abc feat(backend): card file attachments (issue 0128)
- migration 014_card_files: tabla con soft-delete + index activo
- handlers POST/GET/DELETE en backend/files.go
- routes /api/cards/{id}/files, /api/files/{id}
- limite 10MB, storage en uploads/<card_id>/<random>__<safe>
2026-05-27 10:51:52 +02:00
egutierrez 1923fd31a4 chore: auto-commit (1 archivos)
- registry.db

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 01:37:33 +02:00
egutierrez b599090876 chore: auto-commit (1 archivos)
- app.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 00:32:55 +02:00
egutierrez 69a0d351fc Merge issue 0094: kanban daily summary + pdf 2026-05-14 18:08:09 +02:00
egutierrez 9c5e76e03f feat(kanban): bocadillo agente + PDF descargable en reporte diario (issue 0094)
Anade tres capas sobre el reporte diario del issue 0093:

1) Bocadillo del agente: cuadro azul encima de "Tareas hechas" con un
   resumen en lenguaje natural (max 4 frases) generado por claude -p
   sobre el JSON del reporte. Botones Regenerar e icono Settings.

2) Settings del prompt: modal con textarea editable para el template
   del agente (key=daily_report_prompt). Compartido por todos los
   usuarios. Boton Restablecer por defecto.

3) PDF descargable: boton que abre ventana nueva con HTML imprimible
   (estilo A4, KPIs filtrados, tabla con enlaces absolutos por card).
   Permite compartir el listado de tareas hechas con los solicitantes.

Backend:
- Migration 013 anade tablas daily_summaries y settings; seed del
  prompt por defecto en castellano.
- daily_summary.go con GetSetting/SetSetting, GetDailySummary/Upsert,
  runClaudePrompt (envuelve claude -p) y GenerateDailySummary que
  orquesta DailyReportFor + plantilla + claude + persist.
- Nuevos endpoints:
  * GET  /api/reports/daily/summary
  * POST /api/reports/daily/summary
  * GET  /api/settings/{key}
  * PUT  /api/settings/{key}

Frontend:
- api.ts: getDailySummary, generateDailySummary, getSetting, setSetting.
- DailyReport.tsx: estado de summary, settingsOpen, promptDraft,
  filterRequester, filterAssignee, filteredDoneCards, exportPDF.
- Bocadillo con IconSparkles + IconRefresh + IconSettings.
- Modal de prompt con Guardar/Cancelar/Reset.
- Filtros Select por solicitante y asignado encima de la tabla.
- exportPDF abre window.open con HTML self-contained que incluye
  enlaces ${origin}/?card=${id} y window.print() automatico.

E2E nuevo (daily-summary-pdf.spec.ts): CRUD del setting, GET summary
shape, presencia del boton PDF/Settings/Regenerar en el modal. No
invoca claude real (binario externo, no disponible en CI).

Suite completa 11/11 pasa.
2026-05-14 18:08:09 +02:00
egutierrez fc7e6a34a7 feat(kanban): reporte diario al click en dia del calendario (issue 0093)
Adds a daily report dashboard accessible by clicking a day number in the
calendar view. Renders inside a full-width modal (90% width).

Backend (new file backend/reports.go):
- Type DailyReport with KPIs, rankings, done_cards list, reopened cards,
  3-bucket stale list (7/14/30d), lead time avg+p50+p95, 24-hour
  movement histogram, deadlines met/missed list, tag distribution and
  archived count.
- DB.DailyReportFor(date, tz) uses Europe/Madrid by default; computes
  [start,end) in local time, converts to UTC and queries:
  * cards.completed_at in range  -> done list
  * card_events kind=created in range -> created counts
  * card_column_history.entered_at in range -> moves + hourly
  * previousColumnWasDone() -> reopened detection
  * card_lock_history overlapping the day -> blocked_ms
  * stale buckets: open history entries on non-done columns aged >=7d
- New route GET /api/reports/daily?date=YYYY-MM-DD&tz=Europe/Madrid.

Frontend:
- api.ts: DailyReport type + dailyReport(date, tz?) call.
- New component DailyReportView (components/DailyReport.tsx):
  * 6 KPI cards (Hechas, Creadas, Movimientos, Bloqueado, Reabiertas,
    Deadlines on-time %).
  * 4 ranking cards (Top assignees done, Top assignees created,
    Top requesters atendidas, Top requesters aportadas).
  * Done cards table with click-to-jump (links open the card in board).
  * Mantine BarChart with movements per hour.
  * Tag chips, reopened list, deadlines list with late_ms, stale buckets.
- CalendarView wraps the day number in UnstyledButton with data-test
  attribute and forwards onOpenDailyReport.
- App.handleOpenDailyReport opens modals.open size 90% with the view;
  click on a card title closes the modal and jumps to the board with
  highlight (reuses existing handleJumpToCard).

Tests (e2e/daily-report.spec.ts):
- Endpoint shape: kpis, done_cards, hourly_moves[24], stale buckets.
- Calendar day click opens the modal with "Reporte diario" title and
  KPI labels visible.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 17:57:14 +02:00
egutierrez 9d3ab5f0f3 feat(kanban): hide "Seleccionar Aleatorio" in done columns
The random-pick menu entry is meaningless for done columns — cards there
are already finished and now get auto-archived after 30 days (issue
0092). Gate the Menu.Item on !column.is_done so the action only appears
in active columns.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 17:57:14 +02:00
egutierrez 9b503f0555 feat(kanban): archive automatico para cards Done +30 dias (issue 0092)
Adds an archive layer separate from the trash. Cards in is_done columns
that have been there for more than 30 days are auto-archived on the next
board load (throttled to once every 30 minutes). Archived cards leave
the board but stay in the DB and are listed in a new sidebar drawer
"Hecho (archivo)" below the existing Papelera, with a one-click restore.

Schema (migration 012_card_archived.sql):
- ALTER TABLE cards ADD COLUMN archived_at TEXT;
- NULL = active, ISO timestamp = archived. Independent from deleted_at.

Backend:
- Card.ArchivedAt + JSON; ListCardsWithTime filters archived_at IS NULL.
- New methods: ArchiveCard, UnarchiveCard, ListArchivedCards,
  AutoArchiveDoneOlderThan.
- New endpoints: GET /api/archive, POST /api/cards/:id/archive,
  POST /api/cards/:id/unarchive.
- handleGetBoard invokes maybeAutoArchive (atomic throttle, 30 min sweep,
  30 day cutoff). Errors logged but never block the board response.

Frontend:
- Card type + api.ts add the new field and helpers.
- App.tsx state for archive list, reload, archive/unarchive handlers.
- New sidebar drawer with toggle, count badge, restore button.
- KanbanCard gains an "Archivar" menu item (gated on isDone +
  onArchive prop) for manual archiving of any done card.

Tests:
- Playwright e2e/archive.spec.ts: manual archive via menu, drawer
  toggle, unarchive. Picks a done card via /api/board introspection so
  it stays stable regardless of board state.
- Auto-archive of >30d cards: not under e2e (real time travel needed);
  covered by code review of the SQL query and the throttle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 17:57:14 +02:00
egutierrez c4caff85be perf(kanban): split KanbanCard body into memoized child (dnd lag fix)
Drag perf measured via new Playwright spec drag-perf.spec.ts which drives
a slow drag across the biggest column (~35 cards) while capturing per-frame
durations via rAF inside the page. Pre-fix metrics in HECHO column:

  wrapper-renders=1942 body-renders=N/A
  p50=16.7ms p95=83.3ms max=116.7ms (12fps stalls)

Root cause: useSortable inside KanbanCardImpl subscribes to dnd-kit context;
every pointermove during a drag re-renders ALL cards in the SortableContext.
With the old monolithic component, each re-render rebuilt the full Stack +
Menu + 4 Popovers JSX tree — even though no data had changed.

Fix: split KanbanCardImpl into a thin outer (useSortable + Paper wrapper +
sticker overlay handler + style) and a memoed KanbanCardBody (Stack +
sticker overlay + popover state). All popover/requesterDraft local state
lives inside the body now, so its props are stable across drag and
React.memo skips the body work entirely.

Post-fix metrics:

  wrapper-renders=1943 body-renders=0
  p50=16.7ms p95=16.8ms max=50.0ms (steady 60fps with a single 33ms spike)

E2E thresholds tightened: p50<20, p95<50, max<60, body-renders<5. Regression
in any of these will fail CI.

Probe helpers (_probeRender / _probeBodyRender) are no-ops unless
window._cardRenderProbe is set. Production cost: ~3ns per render call.

Also: `now` clock interval already pauses while dragging (previous commit
e656e8c). `animateLayoutChanges:() => false` kept; it does not visibly
change reorder UX with this codebase.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 17:57:14 +02:00
egutierrez 7ba18f9114 fix(kanban): infinite ref-merge loop + drag lag
Two issues:

1. "Maximum update depth exceeded" inside Mantine's useMergedRef during
   drag. Root cause: the `data` object passed to dnd-kit's `useSortable`
   in KanbanCard and KanbanColumn was re-created on every render. Mantine
   Paper composes its internal ref with the consumer ref via
   useMergedRef, which uses a useCallback whose deps array contains the
   refs themselves. Whenever the underlying setNodeRef from useSortable
   became unstable (because dnd-kit's internal state churned on data
   identity change), the merged ref was reassigned each commit -> setState
   inside the ref callback -> next render -> new data identity -> loop.
   Wrap the sortable data in useMemo keyed on its real inputs.

2. Drag feels laggy. Each card listens to a 1-second `now` clock that
   re-renders the entire board to refresh the "time in column" label.
   Pause that interval while a drag is active so dnd-kit's per-pixel
   reconciliation does not also re-mount/re-layout every card every
   second. Tick resumes the moment the drag ends.

Also move the Select `data` array for the column max-time popover from
an inline expression to a module-level constant; same array identity
across all column instances and renders -> Mantine Combobox stops
re-running its diffing effect for free.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 17:57:14 +02:00
egutierrez 76d85959f1 feat(kanban): sidebar edge zone now toggles (open + close)
The 32px left drag zone now also closes the sidebar when it is open.
Symmetric behaviour: dwell ≥400ms while dragging closes if open / opens
if closed.

To prevent a drag that starts with the pointer already inside the
sidebar (e.g. dragging a sidebar card itself) from immediately auto-
closing, the close action requires the pointer to have left the strip
at least once after the drag started. So:
- closed + drag-to-edge -> opens (unchanged).
- open + drag a card out, then move the card back to the edge -> closes.
- open + drag a sidebar card directly to the board -> nothing happens.

After a successful toggle the dwell flag resets, so the user must leave
the strip and re-enter to trigger another action.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 17:57:14 +02:00
egutierrez 257858a1f3 feat(kanban): drag-aware sidebar dropzone (issue 0091)
Add a 32px invisible strip on the left edge of the viewport that
auto-opens the sidebar when the user drags a card and dwells near the
edge for >=400ms. Removes the manual toggle step when moving cards to
sidebar-located columns.

- App.tsx: global mousemove listener while drag is active; 400ms hover
  timer; sets navOpen(true) when triggered; cancels on pointer leave or
  drag end. No auto-close on drag end (user keeps sidebar open).
- dropzone.css: subtle inset blue glow with pulse animation while
  pointer is inside the strip and a drag is active.
- KanbanColumn.tsx: add data-column-id and data-column-location to the
  Paper root for stable e2e selectors.
- e2e/sidebar-dropzone.spec.ts: Playwright test driving a slow drag
  to the left edge, asserting the strip arms, sidebar opens, and the
  card moves to a sidebar column via /api/board.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 17:57:14 +02:00
egutierrez 30def13c55 feat(kanban): mejoras historial card — DONE check + tiempo por columna
HistoryModal now receives the board's columns via prop to enrich
history events with column metadata.

Timeline:
- Entries for column moves into a column with is_done=true now render
  with a green IconCheck bullet and the kind label "Hecho en columna"
  instead of the generic blue arrows / "Mueve a columna". Makes the
  card's "done" moments scannable at a glance.

Footer (below the timeline):
- Replaces the single Group-of-badges with a structured table showing
  one row per visited column: name, visits (entry count) and total time
  in column. DONE columns are flagged with a green check + bold.
- Total locked time keeps the same source (total_locked_ms) but moved
  to a header line above the table to declutter.
- Currently-active entry (exited_at=null) contributes now - entered_at
  to its row, so the table reflects live time.

App.tsx passes columns from board state when opening the history modal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 17:57:14 +02:00
egutierrez bc502df48a feat(kanban): tiempo maximo via popover con unidad (issue 0089 followup)
Replace the native window.prompt with a Popover that mirrors the deadline
picker pattern: NumberInput + unit Select (minutos/horas/dias/semanas/meses).
The selected unit converts to minutes at save time; the column's stored
unit on the backend stays unchanged (max_time_minutes).

On open the popover pre-selects the largest unit that yields a clean
integer for the current value (e.g. 1440 -> 1 dia, 60 -> 1 hora).

Includes a trash icon to clear the limit and a Guardar button.
data-test selectors added for future e2e:
- column-max-time, column-max-time-input, column-max-time-unit,
  column-max-time-save.

Menu label now shows "(N dias)" / "(N semanas)" / etc. instead of "(N min)".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 17:57:14 +02:00
egutierrez c93ac46c37 feat(kanban): Seleccionar Aleatorio en columna con ruleta (issue 0090)
Adds a column-level "Seleccionar Aleatorio" context menu entry that picks
a card at random from the column with a roulette-style highlight
animation that decelerates to the winner.

Selection respects existing filters (uses cardsByColumn which is the
post-filter view) and always excludes locked cards. Menu entry is
disabled when nothing pickable is left.

Implementation:
- KanbanColumn: new Menu.Item with IconDice5; data-test selector for e2e.
- onPickRandom prop wired from KanbanColumn -> App.
- handlePickRandom in App.tsx: cryptographically random winner via
  crypto.getRandomValues, 2 full laps + offset, cubic decay 50ms -> 220ms,
  follows the active card with scrollIntoView.
- src/styles/roulette.css: .kanban-roulette-active (blue pulse, single
  step) and .kanban-roulette-winner (green pulse + scale, ~1.6s).
  Imported globally from main.tsx.

Manual verification only (visual timing + needs real cards). Backend
untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 17:57:14 +02:00
egutierrez 9f4fd85db3 feat(kanban): tiempo maximo por columna con borde rojo (issue 0089)
Adds column-level max time limit. Cards whose time_in_column_ms exceeds
the limit show a red border + red halo. Columns marked as Done never
trigger the visual regardless of the limit (per spec).

Backend:
- Migration 011_column_max_time.sql adds columns.max_time_minutes
  INTEGER NOT NULL DEFAULT 0 (0 = no limit). Aditiva, idempotente.
- Column struct + ColumnPatch + UpdateColumn handle the new field;
  negatives clamp to 0; listing query includes it.
- handleUpdateColumn (PATCH /api/columns/:id) accepts max_time_minutes
  in the JSON body.

Frontend:
- Column TS interface + UpdateColumnInput updated.
- KanbanColumn context menu: new entry "Tiempo maximo" using
  window.prompt for low-friction config; shows current value when >0.
- KanbanCard receives columnOverdue prop calculated from the column
  state and card.time_in_column_ms; renders red border (var
  --mantine-color-red-6) with 2px width + 2px red halo when overdue.
- data-card-id, data-column-overdue, data-locked attributes on the card
  paper element so e2e tests / scripts can query state.

Tests: TestColumnMaxTimeMinutes_Defaults + _Update verify the schema
default, the clamp on negative input, and that updating max_time leaves
other fields untouched.

Visual regression of the red border kept out of automated e2e because
it requires either clock control or real cards aged > N minutes; will be
verified manually after merge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 17:57:14 +02:00
egutierrez eb1c13d82c feat(kanban): requester input empty + keyboard nav (issue 0088)
CardForm: drop pre-fill of requester from logged user; Enter inside the
Autocomplete no longer submits the form (Mantine handles dropdown
selection; arrows + Enter pick option without closing modal). Submit
remains via "Crear" button or Ctrl+Enter from description.

Adds data-field="requester" and data-test="add-card" selectors for stable
e2e queries.

Tests:
- vitest component test (CardForm.test.tsx): empty input, Enter does not
  submit, submit only via button. Dropdown arrow nav covered by e2e
  (jsdom portal handling is brittle).
- Playwright e2e (requester-input.spec.ts) using new browser capability
  group (pw_kanban_login, pw_keyboard_sequence) from registry.
- seed_e2e_user CLI to create deterministic test user against
  operations.db (bcrypt via standard backend hash).

Setup additions (frontend/):
- vitest + @testing-library + jsdom devDeps
- @playwright/test devDep + playwright.config.ts
- src/test/setup.ts polyfills jsdom for Mantine (matchMedia,
  visualViewport, document.fonts, ResizeObserver)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 12:57:00 +02:00
egutierrez a34a8142cc chore: auto-commit (23 archivos)
- app.md
- backend/auth.go
- backend/db.go
- backend/dist/assets/index-CPqSy0gZ.js
- backend/dist/index.html
- backend/handlers.go
- backend/main.go
- frontend/src/App.tsx
- frontend/src/api.ts
- frontend/src/components/KanbanCard.tsx
- ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 18:40:22 +02:00
egutierrez f1ee116d3b chore: auto-commit (5 archivos)
- app.md
- backend/chat.go
- Dockerfile
- docker-compose.yml
- traefik-dynamic.yml

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 18:11:24 +02:00
egutierrez ce807ec2ee Merge quick/fix-ws-proxy: vite ws proxy + chat WS e2e 2026-05-09 15:00:41 +02:00
egutierrez 69ebc1e59b fix(chat): vite proxy ws + e2e tests para chat WebSocket
- frontend/vite.config.ts: anadir ws: true al proxy de /api para que el
  dev server (5180) reenvie WebSocket upgrade al backend (8095). Sin esto
  Firefox da "websocket error" al abrir /api/chat/ws en modo dev.
- e2e/chat_ws_e2e_test.go: 4 tests nuevos que arrancan el binario kanban
  en puerto efimero con un fake claude (bash script que emite NDJSON), se
  loguean via /api/auth/login y dialean /api/chat/ws con cookie de sesion.
  Verifican: deltas + done, tool_use + tool_result + board_changed,
  rechazo sin sesion, /api/tool sin token = 401.
- e2e/go.mod: anade nhooyr.io/websocket (cliente WS para tests).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 15:00:38 +02:00
egutierrez b166609523 Merge quick/mcp-streaming: MCP server + WebSocket chat streaming 2026-05-09 14:54:42 +02:00
egutierrez ce49fdf9ff feat(chat): MCP server + WebSocket streaming, replace XML actions
- Backend: kanban binary gana subcomando `kanban mcp` que actua como MCP
  server via stdio. Tools = mismo set que executeTool (14). El subprocess
  llama de vuelta al backend via /api/tool/{name} con token interno.
- Backend: nuevo endpoint POST /api/tool/{name} (auth: X-Internal-Token).
- Backend: chat.go refactor — POST /api/chat reemplazado por GET
  /api/chat/ws (WebSocket). Lanza claude -p con --output-format stream-json
  --verbose --mcp-config y reenvia eventos (delta/tool_use/tool_result/
  result/done/error) como mensajes JSON al cliente.
- Backend: usa funciones nuevas del registry claude_stream_go_core (spawn
  + parser NDJSON) y mcp_server_stdio_go_infra (JSON-RPC stdio).
- Frontend: streamChat sobre WebSocket. ChatPanel renderiza deltas en
  vivo, chips para tool_use, badges teal/red para tool_result.
- Borrado: extractActions, actionsBlockMarker, XML system prompt.
- Tests: 7 nuevos en backend (chat_ws_test.go + endpoint /api/tool).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:54:38 +02:00
egutierrez 9e333b0e3e chore: remove percentile_int64 (transitivo via duration_stats)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 03:57:59 +02:00
egutierrez 7ce227ddea feat(kanban): deadlines en cards (context menu, badges, calendario, history)
- migration 009 + columna deadline TEXT en cards
- backend: CardPatch.HasDeadline, eventos deadline_set/deadline_cleared
- KanbanCard: menu derecho con DatePicker, badge countdown con colores por ratio (azul>=50%, amarillo<50%, rojo<10%, red.9 overdue)
- App.tsx: filtro "Con deadline", handleSetCardDeadline optimista, jump-to-card + highlight
- CalendarView: popover por dia con seq_num + titulo, click navega a card en tablero
- HistoryModal: render eventos deadline_set/deadline_cleared
- .gitignore: *.log

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 03:45:36 +02:00
egutierrez 5ba0254e57 feat(kanban): badges done/locked + drag locked en mismo column + migrations + UX stickers
- badges: locked → tiempo bloqueado; done → fecha completion + total lead time; otherwise → tiempo en columna
- locked cards: drag permitido dentro de mismo column (cross-column rejected con notification)
- card field: locked_at desde JOIN card_lock_history (open period)
- migrations: refactor a embed.FS, archivos 002-005 extraidos de ensureColumns; ensureColumns queda como backstop
- stickers UX: opacidad 1, debajo del texto, picker estable (useRef), boton entra directo a modo con 😀, popover cierra outside, cards done filter brightness
- format: formatDateTimeShort

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 00:44:43 +02:00
egutierrez 9931890d9b Merge issue/0063-stickers: kanban stickers feature 2026-05-08 21:00:35 +02:00
egutierrez 656516f219 feat(kanban): stickers feature + dashboard null guards (#0063)
- backend: Sticker type, idempotent stickers column, PUT /api/cards/:id/stickers, 4 tests
- frontend: emoji-mart picker, toolbar button + ESC, draggable overlay with right-click delete, % coords for resize survival
- dashboard: null guards on metrics arrays

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 21:00:30 +02:00
egutierrez 2a727eb7c1 chore: auto-commit (10 archivos)
- chat.log
- db.go
- frontend/src/App.tsx
- frontend/src/api.ts
- frontend/src/components/CardForm.tsx
- frontend/src/components/Dashboard.tsx
- frontend/src/components/KanbanCard.tsx
- frontend/src/types.ts
- handlers.go
- metrics.go

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 15:55:35 +02:00
egutierrez 9290a0b2d0 chore: auto-commit (1 archivos)
- frontend/src/components/Dashboard.tsx

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 00:38:32 +02:00
egutierrez bee688e574 chore: auto-commit (28 archivos)
- app.md
- auth.go
- chat.go
- chat.log
- db.go
- frontend/package.json
- frontend/pnpm-lock.yaml
- frontend/src/App.tsx
- frontend/src/Root.tsx
- frontend/src/api.ts
- ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 00:27:18 +02:00
egutierrez c915e721af chore: auto-commit (1 archivos)
- frontend/src/App.tsx

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 17:16:38 +02:00
fn-registry agent 94223e68f7 chore: sync from fn-registry agent 2026-05-06 19:04:45 +02:00