尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗Mindboard is a personal life dashboard built for fast task capture and daily planning. It tracks tasks across responsibility groups, surfaces what is due today, and embeds a read-only Google Calendar view directly into the dashboard.
The primary use case is an installed iOS PWA: open it, jot a task in a few seconds, and get back to the day.
localStorage.@supabase/ssr for server/browser auth clients.main.Important: this project uses Next.js 16. Before changing framework conventions, read the relevant docs in node_modules/next/dist/docs/.
Install dependencies:
npm install
Create .env.local:
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=your_supabase_publishable_key
GOOGLE_CLIENT_ID=your_google_oauth_client_id
GOOGLE_CLIENT_SECRET=your_google_oauth_client_secret
Run the development server:
npm run dev
Open http://localhost:3000.
npm run dev
npm run lint
npm run build
npm run test
npm run start
npm run build may need network access because next/font fetches Geist Mono from Google Fonts.
Apply the migrations in supabase/migrations:
0001_init.sql: creates groups and tasks, including tasks.notes for Markdown task details.0002_google_tokens.sql: creates google_tokens for Google provider tokens.0003_group_calendars.sql: adds groups.google_calendar_id for per-group Google Calendar linking.Every table has Row Level Security enabled and policies scoped to auth.uid() = user_id.
Do not disable RLS for debugging. If a query fails because of RLS, fix the policy or the query context.
Enable the Google Calendar API in Google Cloud.
Add these OAuth consent scopes:
https://www.googleapis.com/auth/calendar.readonly
https://www.googleapis.com/auth/calendar.events
https://www.googleapis.com/auth/calendar.calendarlist.readonly
calendar.events is read+write on events (no calendar management) and is required for drag-to-reschedule and inline event editing.
Make sure Supabase Google Auth and Vercel use the same OAuth client:
GOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRETAfter changing scopes, sign out and sign back in. Google provider tokens are captured in app/auth/callback/route.ts and stored in google_tokens. Supabase does not refresh provider tokens automatically, so utils/google/calendar.ts handles refresh with Google’s token endpoint.
No Google API key is used.
Mindboard exposes its data to external Claude clients (claude.ai web, mobile, Cowork) as a remote Model Context Protocol server, built on the Phase 0 tool registry. Reads are safe; writes go through an explicit propose → confirm step, and every executed write is recorded in ai_audit_log.
The server is multi-tenant: every request resolves to a specific Supabase user and every tool call is scoped to that user's rows. See docs/mcp-provisioning.md for the new-user setup flow.
POST /api/mcp/mcp — App Router route at app/api/mcp/[transport]/route.ts, via Vercel's mcp-handler (stateless streamable HTTP, no Redis). One URL for all users; identity comes from the Authorization layer.authInfo.extra.userId):
/.well-known/oauth-protected-resource + /.well-known/oauth-authorization-server), dynamic client registration, authorization-code + PKCE. The /authorize step reuses the app's Google/Supabase login; whoever signs in authorizes access to their own data (the token's sub is their user id). All OAuth artifacts are stateless HMAC-signed tokens (MCP_OAUTH_SECRET), no DB tables.mbp_…) for non-OAuth clients (Claude Desktop, MCP inspector, curl) — generated on /settings, shown once, stored only as a SHA-256 hash in user_settings.mcp_token_hash.MCP_BEARER_TOKEN — still accepted and mapped to the deployment owner (MINDBOARD_OWNER_USER_ID); also the home worker's auth for /api/worker.SUPABASE_SERVICE_ROLE_KEY) with every query filtered by the authenticated user id explicitly (the service role bypasses RLS; the app's own pages use session clients where RLS is the boundary).WORKER_ALLOWED_USER_IDS (comma-separated user ids) are claimed.Additional server env vars:
SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_secret # Dashboard > Settings > API
MCP_BEARER_TOKEN=a_long_random_secret # legacy static token, maps to the owner
MINDBOARD_OWNER_USER_ID=your_supabase_auth_user_id # owner mapping + worker heartbeat
MCP_OAUTH_SECRET=a_long_random_secret # signs the stateless OAuth tokens
WORKER_ALLOWED_USER_IDS=uuid1,uuid2 # optional: extra users allowed on the home worker
WATCH_TOKEN=a_long_random_secret # Apple Watch API bearer (see below)
Tools:
finance_snapshot, tasks_snapshot, inventory_snapshot, list_tasks, list_groups, list_accounts, list_categories, list_recent_ledger.create_task, complete_task, log_spend — each returns a preview + proposalId and writes nothing on its own.confirm_action executes a proposal (applies the write and flips its audit row to executed); cancel_action discards it.In production, add it to claude.ai as a custom connector (Settings → Connectors → Add custom connector) pointing at https://<your-domain>/api/mcp/mcp — claude.ai runs the OAuth flow and each user approves once via their own Google login. Test locally with the MCP inspector (npx @modelcontextprotocol/inspector) using OAuth, a per-user mbp_ token from /settings, or the legacy static bearer.
The watchOS client (mindboard-watch, a separate repo) talks to a small JSON API under /api/watch/*, authenticated with a bearer token from the WATCH_TOKEN env var:
WATCH_TOKEN=a_long_random_secret — maps to the deployment owner (MINDBOARD_OWNER_USER_ID), like the legacy static MCP token.WATCH_TOKEN={"secret_for_lucca":"<lucca_user_id>","secret_for_friend":"<friend_user_id>"}.Generate a token with openssl rand -base64 48, set it in Vercel (Production) and in .env.local, then paste the same value into the watch app's Secrets.xcconfig. Rotating the token is just changing the env var and redeploying; a missing or malformed WATCH_TOKEN makes every watch request 401.
Endpoints (all Authorization: Bearer <token>; writes are JSON POSTs and accept an Idempotency-Key header so a retried request from a flaky watch connection never double-logs — the key derives the ai_audit_log row id, and a repeat replays the first attempt's result):
GET /api/watch/today — overdue + due-today tasks (with group name + color, priority, and notes for the detail screen), today's remaining calendar events (with calendar, color — linked group's, else the Google calendar's — location, and description for the event screen), today's routines with done state, upcoming events and tasks for the next 7 days, next timed event, free hours left, plus meta (server time, user timezone).POST /api/watch/complete { "type": "task" | "recurring", "id" } — same executors as the MCP complete_task / complete_recurring_task tools (recurring: today's occurrence only).POST /api/watch/miss { "id" } — mark a task missed (the MCP miss_task executor).POST /api/watch/followup { "id", "text" } — queue a dictated follow-up on a task as a followup job for the home worker (worker/worker.py), where Claude Code turns it into a new task in the same group with the same due date; the original stays open. meta.followups in /today reports pending and recently failed jobs. The app's task edit panel queues the same job via its ✦ follow up button (payload source: "mindboard app").POST /api/watch/defer { "id" } — push a task's due date by one day, keeping its time (the MCP update_task executor). Send an Idempotency-Key: a retried request would otherwise push it twice.POST /api/watch/task { "title" } — inbox task, no group, priority med.POST /api/watch/spend { "amount", "note"? } — logs a spend today against the default account (the oldest active one, as the dock's $ capture does).POST /api/watch/capture { "text" } — capture_to_brain with source "apple watch".app/
_components/ shared dashboard/task UI
actions/ server actions for auth, groups, tasks
auth/callback/route.ts Supabase OAuth callback and token capture
groups/ group list and group task pages
inbox/ inbox task page
login/ Google sign-in page
layout.tsx app metadata, viewport, font, PWA hooks
page.tsx main dashboard
utils/
google/ Google Calendar scopes, token refresh, event fetch
supabase/ SSR browser/server/proxy clients
supabase/migrations/ SQL schema and RLS policies
public/ PWA manifest and icons
The visual direction is “Terminal Calm”:
background #0d0d0d
foreground #f5f0e8
accent #b5ff3c
muted text #6b6b6b
borders #1f1f1f / #2a2a2a
danger #ff6b6b
font Geist Mono
Keep the app mobile-first, dense, and quiet. The fixed task capture island is the most important interaction and should remain usable while scrolling. Quick capture supports task title, group, due date, and Markdown notes without leaving the bar.
Theme colors are centralized in app/globals.css and app/_components/themes.ts. The dashboard settings panel stores per-theme palette overrides in localStorage as palette-${theme}.
For AI agents and Claude Code, read AGENTS.md first. It contains the current architecture, product constraints, security rules, and implementation notes. CLAUDE.md points to that file.
CODEX_HANDOFF.md is historical checkpoint context and should not be treated as current ground truth.
Pushes to main deploy through Vercel.
Before pushing code changes, run:
npm run lint
npm run test
npm run build
Browser-only setup, such as Supabase dashboard settings, Google Cloud OAuth consent, and Vercel environment variables, must be done manually in those dashboards.