das - #36
Open
spencrmartin wants to merge 79 commits into
Open
Conversation
Update README.md
- Remove SKILLS_DEPLOYMENT.md (feature already deployed) - Remove SKILLS_INTEGRATION_SUMMARY.md (internal dev docs) - Remove TESTING_SKILLS.md (testing completed) - These were internal documentation from skills feature development - Skills feature is complete and documented in README.md
- Remove unused view files (FeedView.jsx, TimelineView.jsx, GraphView.jsx) - Remove unused components (Timeline.jsx, InfinitePinboard.jsx, LinkPreview.jsx, Antigravity.jsx) - Simplify App.jsx to only Home, Graph, and Settings views - Remove unused state variables (filterType, detailItem, isDarkMode) - Remove unused helper functions (getTypeIcon, getGraphData, filterButtons) - Remove all Feed, Timeline, and Pinboard view rendering code - Clean up imports (removed unused Card components, icons, utilities) - Reduce App.jsx from 732 lines to ~280 lines - Streamlined codebase for maintainability
chore: remove obsolete skills documentation files
- Remove HOME_REDESIGN.md (internal design docs) - Remove IMPLEMENTATION_COMPLETE.md (internal dev summary) - Remove TEST_HOME_VIEW.md (internal testing checklist) - Keep README.md and INSTALL.md (user-facing documentation) - These were temporary development documents not needed in main
chore: remove internal home redesign documentation
Phase 1 MVP — Brian as a standalone macOS desktop app. Tauri Shell: - src-tauri/ scaffolded with Tauri v2, shell + log plugins - Window config: 1280x860, CSP for backend + wttr.in - externalBin sidecar registration for brian-backend - Sidecar lifecycle in lib.rs: spawn, stdout/stderr streaming, health check polling (30 retries), clean shutdown on window close - Emits backend-ready / backend-error events to frontend - Added reqwest + tokio dependencies PyInstaller Backend Packaging: - brian-backend.spec: --onefile mode, all hidden imports for FastAPI/uvicorn/pydantic/starlette/mcp/httpx/bs4 - brian-backend-entry.py: wrapper to fix relative import issue - scripts/build-backend.sh: cross-platform build script with auto platform triple detection, copies to src-tauri/binaries/ - Produces 21MB standalone binary, 34MB .app, 26MB .dmg Frontend Health Check: - useBackendStatus hook: dual-mode Tauri events + HTTP polling - BackendStatus overlay: colored square grid loading animation, monochrome error state with SVG alert icon, fade-out on ready - Wired into main.jsx root - Added @tauri-apps/api dependency End-to-end verified: Tauri launches → sidecar spawns → health check passes (~8s) → backend-ready event → overlay fades → app ready Closes BRIAN-8, BRIAN-11, BRIAN-32, BRIAN-34
Database safety net for packaged app upgrades: Backup & Restore (connection.py): - db.backup(reason) — SQLite online backup API for consistent snapshots - db.restore(backup_path) — replace db file + integrity check - db.list_backups() — list all backup files with metadata - db.cleanup_backups(keep=5) — prune old backups - db.get_schema_version() — query current schema version Migration Rollback: - Pre-migration backup created automatically before any migration - On migration failure: auto-rollback to pre-migration backup - Backup cleanup after successful migration (keeps last 5) - Clear error messages with rollback status API Endpoints (routes.py): - GET /api/v1/database/info — path, size, schema version - GET /api/v1/database/backups — list all backups - POST /api/v1/database/backups — create manual backup - POST /api/v1/database/restore — restore from backup path Health Endpoint (main.py): - /health now includes schema_version field Tested: - Fresh DB init + backup + restore cycle - Migration rollback: simulated failed v8→v9 migration, verified auto-rollback preserves data and schema version - All API endpoints verified against live backend Closes BRIAN-12
…s (BRIAN-13) Config Management (config.py): - Config class loads from: env vars > ~/.brian/config.json > defaults - config.save() persists settings to JSON file - config.resolve_port() finds free port starting from configured default - Port file (~/.brian/port) written on startup, cleaned up on exit - read_port_file() / cleanup_port_file() for external process discovery - Signal handlers (SIGTERM, SIGINT) ensure port file cleanup - GET /api/v1/config endpoint exposes current configuration - /health endpoint now includes port number Port Conflict Detection: - _find_free_port() tries sequential ports (up to 20 attempts) - Warns when configured port is in use and auto-increments - Tested: 8080 occupied → 8081, both occupied → 8082 Dynamic Backend URLs (frontend): - New lib/backend.js: centralized setBackendPort()/getApiBaseUrl() - useBackendStatus hook reads port from backend-ready event payload - Both API clients (api/client.js, lib/api.js) use getApiBaseUrl() - Replaced all hardcoded localhost:8080 URLs in: - RegionEditDialog.jsx (7 URLs) - SimilarityGraph.jsx (5 URLs) - Settings.jsx (1 URL) Tauri Integration (lib.rs): - Reads ~/.brian/port file via dirs crate for health check URL - Re-reads port file on each retry (sidecar may not have written it yet) - Emits port number in backend-ready event payload - Added dirs = "6" dependency CSP (tauri.conf.json): - Widened connect-src to http://127.0.0.1:* and http://localhost:* to support dynamic port assignment Closes BRIAN-13
The Rust health check was detecting port file changes but still polling the original port. Now rebuilds the URL with the current port on each attempt, so dynamic port assignment works correctly when the sidecar picks a non-default port.
…-14) Search Robustness (repository.py): - search() now checks FTS5 availability before querying - _fts5_available() verifies knowledge_search virtual table exists - _search_fts5() — existing FTS5 ranked search (unchanged logic) - _search_like() — new LIKE-based fallback for degraded search when FTS5 is unavailable (e.g. custom SQLite builds) SQLite Diagnostics (connection.py): - db.get_sqlite_version() — returns SQLite library version - db.fts5_available() — tests FTS5 support via in-memory table API Endpoints: - /health now includes sqlite_version and fts5_available fields - /api/v1/database/info now includes sqlite_version and fts5_available Verified: - FTS5 search works in dev (SQLite 3.51.1) - FTS5 search works in packaged PyInstaller binary - LIKE fallback returns correct results - Both search paths tested with phrase and single-word queries Closes BRIAN-14
Version Management: - scripts/version.sh — CLI tool for version operations: - show: display version across all files with sync status - sync: propagate pyproject.toml version to all files - bump [major|minor|patch]: increment and sync everywhere - set X.Y.Z: set explicit version across all files - tag: create annotated git tag for current version - Single source of truth: pyproject.toml - Syncs to: tauri.conf.json, Cargo.toml, package.json, config.py Dynamic Version Reading (config.py): - _read_version() reads from pyproject.toml at runtime - Falls back to hardcoded '0.1.0' in PyInstaller builds where pyproject.toml isn't available - Config.APP_VERSION now uses _read_version() Files managed by version.sh: - pyproject.toml (source of truth) - src-tauri/tauri.conf.json - src-tauri/Cargo.toml - frontend/package.json - brian/config.py (fallback only, runtime reads pyproject.toml) Tested: show, bump patch, set, sync all verified working Closes BRIAN-16
Cross-platform release workflow (.github/workflows/release.yml):
Trigger: push tag v* (or manual workflow_dispatch)
Build Matrix:
- macOS arm64 (macos-14) — .dmg
- macOS x86_64 (macos-13) — .dmg
- Ubuntu 22.04 — .AppImage + .deb
- Windows latest — .msi + .exe
Pipeline (2-stage):
1. build-sidecar: PyInstaller --onefile per platform
- Sets up Python 3.12, installs deps + pyinstaller
- Builds brian-backend-{platform-triple} binary
- Uploads as artifact for next stage
2. build-tauri: Tauri desktop app per platform
- Downloads sidecar artifact from stage 1
- Sets up Node 20 + pnpm 9 + Rust stable
- Installs Linux system deps (webkit2gtk, etc.)
- Builds frontend (pnpm install + build)
- Runs cargo tauri build with platform target
- Uploads .dmg/.AppImage/.deb/.msi/.exe artifacts
3. release: Creates draft GitHub Release
- Downloads all build artifacts
- Auto-generates release notes from commits
- Attaches all platform installers
Also:
- Removed hardcoded absolute path from tauri.conf.json
beforeBuildCommand (was /Users/spencermartin/... path)
- CI builds frontend explicitly before cargo tauri build
Usage:
./scripts/version.sh bump patch
git add -A && git commit -m 'chore: bump version'
./scripts/version.sh tag
git push origin spence/tauri-work --tags
Closes BRIAN-15
Onboarding Flow (3 steps): 1. Welcome — Brian logo + explanation of what it is 2. Connect AI Tools — Goose, Claude Desktop, Cursor detection with one-click MCP connection buttons 3. Add First Note — inline note creation with title/content Detection: - useOnboarding hook checks /api/v1/stats for total_items === 0 - Stores completion flag in localStorage (brian_onboarding_complete) - Only shows once per install, never again after completion Components: - Onboarding.tsx — full-screen dark overlay with step navigation - BrianLogo (static colored squares from loading.svg) - WelcomeStep, ConnectToolsStep, AddFirstItemStep - StepDots indicator at bottom - Monochrome design matching app theme - useOnboarding.ts — first-run detection hook App Integration (main.jsx): - Flow: BackendStatus → Onboarding (if first run) → App - Backend must be healthy before onboarding checks stats - Onboarding gates App mount until completed or skipped HomeView Updates: - 'Welcome' instead of 'Welcome back' when ≤1 items - 'Start building your knowledge universe' for empty state Closes BRIAN-9
New step 0 before the existing onboarding:
- Brian colored logo
- 'Keep your knowledge local' tagline
- Name entry text box
- Log in button
Flow is now: Login → This is Brian → Connect Tools → Add Item
User name stored in localStorage (brian_user_name) and used in:
- Onboarding step 1: 'Hey {name}, this is Brian'
- HomeView: 'Welcome back, {name}'
Exported getUserName() from Onboarding.tsx for use by other components.
Step dots updated to 4 steps.
Settings:
- Log Out button at bottom of settings page
- Shows 'Signed in as {name}' with current user
- Clears localStorage (onboarding flag + user name) and reloads
- Added 'Logged in as' to About section
- Fixed version display to 0.1.0
useOnboarding hook:
- Now shows onboarding when no user name is stored (logged out)
- Previously only checked total_items === 0
- Logout → reload → login screen appears regardless of item count
- Login with existing items skips to app (onboarding flag set)
Backend (routes.py): - POST /api/v1/tools/connect — writes MCP config for Goose/Claude/Cursor - Goose: writes brian extension to ~/.config/goose/config.yaml - Claude Desktop: writes mcpServers entry to claude_desktop_config.json - Cursor: writes mcpServers entry to ~/.cursor/mcp.json - GET /api/v1/tools/status — checks which tools have Brian configured - Reads each tool's config file and checks for brian entry Frontend (Onboarding.tsx): - ConnectToolsStep now fetches /tools/status on mount to show already-connected tools with checkmarks - Connect button calls real endpoint, shows error on failure - Previously was a stub that silently caught errors Tested: Goose and Cursor configs written and verified correct format
Login screen: logo, tagline, name input left-aligned, Log in button right-aligned Welcome screen: logo, title, description left-aligned, Get Started button right-aligned
Replaced all hardcoded white/black colors with Tailwind theme variables: - bg-[#0a0a0a] → bg-background - text-white/90 → text-foreground - text-white/40 → text-muted-foreground - bg-white/10 → bg-primary / bg-muted - border-white/8 → border-border - All buttons use bg-primary text-primary-foreground Onboarding now respects light/dark mode setting automatically.
- Wider max-w-md with px-12 padding for breathing room - Logo at top, tagline with mt-16 gap, form with mt-8 - Welcome step matches same layout pattern - Both screens use consistent vertical rhythm
Login screen now shows: - Logo (left) - 'brian' title (left) - 'Keep your knowledge local' subtitle (left) - Name input (left, full width) - Log in button (left-aligned, not right) All elements flush left with consistent spacing.
BrianLogo component now accepts 'animated' prop that enables
staggered fade-in/pulse animation on each colored square,
matching the loading screen animation style.
Login screen uses animated={true}, other steps stay static.
Added 'MCP Config' expandable section below the tool list: - Dashed border button with code icon toggles the panel - Shows the raw JSON MCP server config for manual setup - Copy button copies config to clipboard - Animated expand/collapse with framer-motion - For users with tools not in the preset list
Added logo assets: - frontend/public/goose-logo.png - frontend/public/claude-logo.svg - frontend/public/cursor-logo.png Replaced emoji/placeholder SVG icons with actual product logos in the ConnectToolsStep onboarding component.
…e onboarding screens Replaced the static/animated BrianLogo grid with the PixelBlast canvas component on the Login and Welcome onboarding steps. Uses the user's accent color from settings context. Matches the same PixelBlast style used on the HomeView dashboard.
Both login and welcome steps now use the same inverted gradient background as the HomeView dashboard PixelBlast cards: - Light mode: dark foreground gradient (from-foreground/90) - Dark mode: light gray gradient (from-gray-200) PixelBlast renders on top with absolute inset-0 positioning.
GitHub Actions workflow updated for macOS signing: - Imports .p12 certificate into temporary CI keychain - Sets APPLE_SIGNING_IDENTITY for Tauri codesign - Sets APPLE_ID, APPLE_PASSWORD, APPLE_TEAM_ID for notarization - Tauri automatically handles notarization when these env vars are set - Separate build steps for macOS (signed) vs Linux/Windows (unsigned) Tauri config updated: - Added signingIdentity to bundle.macOS for local builds - Identity: Developer ID Application: SPENCER ERNEST F MARTIN (8HGTQKQUPP) Required GitHub repository secrets (already set): - APPLE_CERTIFICATE (base64 .p12) - APPLE_CERTIFICATE_PASSWORD - APPLE_ID - APPLE_PASSWORD - APPLE_TEAM_ID Closes BRIAN-17
Phase 1: Package & Bundle (MVP) - Tauri desktop shell wrapping React frontend - Python backend as PyInstaller sidecar (21MB) - Health check system with colored square loading animation - First-run onboarding: login, connect AI tools, add first note - PixelBlast animated logo on onboarding screens - Real tool connection endpoints (Goose, Claude, Cursor) - MCP config panel for manual setup - Logout button in Settings Phase 4: Backend Packaging - DB backup/restore with migration rollback - Config management (~/.brian/config.json) - Port conflict detection + auto-increment - FTS5 verification + LIKE fallback Phase 2: Build & Release Pipeline - Semantic versioning (scripts/version.sh) - GitHub Actions CI/CD (macOS arm64/x86, Linux, Windows) - Apple code signing with Developer ID Application Build: Brian.app 34MB | .dmg 26MB | Signed with 8HGTQKQUPP
…runcation Added max_content_length parameter to get_knowledge_context tool schema (default: 0 = full content). Removed hardcoded 300-char truncation from both get_knowledge_context and get_project_context handlers. AI assistants now get complete item content for better context quality. Callers can still opt into truncation by setting max_content_length.
Added EmbeddingSimilarityService that uses sentence-transformers for semantic similarity when available, with automatic TF-IDF fallback. - create_similarity_service() factory auto-selects best backend - EmbeddingSimilarityService: lazy model loading, batch encoding, pre-normalized embeddings for fast cosine sim, handles pseudo-items (query strings) that aren't in the index - Configurable via BRIAN_SIMILARITY_BACKEND env var (auto/tfidf/embedding) and BRIAN_EMBEDDING_MODEL (default: all-MiniLM-L6-v2) - Updated all instantiation points in routes.py and server.py to use the factory - Zero-config: works with TF-IDF by default, upgrades automatically when sentence-transformers is pip installed
Added useDeepLink hook that syncs app navigation with URL hash: - #/ → home view - #/graph → graph view - #/settings → settings view - #/item/:id → home with item detail sheet open - #/project/:id → switch to project (placeholder) Supports browser back/forward navigation. HomeView updates the hash when opening/closing item details so links are shareable. No external router dependency — lightweight hash-based approach for Tauri.
Spence/bugfix
Brings the onboarding tool connection UI into Settings as a persistent panel. Shows connection status for Goose, Claude Desktop, and Cursor with connect/reconnect buttons. Includes 'Reconnect All' to re-run auto-connect, and an expandable MCP Config section for manual setup with copy-to-clipboard. Fetches live status from /tools/status and real sidecar path from /tools/mcp-info on mount.
When a link item has a URL, the detail sheet now loads the website in an iframe instead of just showing text content. Falls back to regular markdown content if the iframe fails to load (e.g. X-Frame-Options). Resets iframe error state when switching between items. Updated Tauri CSP to allow frame-src from https: and http: origins.
- Added IMAGE item type to the model
- POST /upload/image endpoint: saves to ~/.brian/images/, creates item
- GET /images/{filename} endpoint: serves uploaded images
- HomeView: image items render as full-bleed photo cards with title
overlay on gradient, tags as translucent badges
- ItemDetailSheet: image items show full-width photo at top
- App.jsx: added Upload Photo button (ImagePlus icon) with hidden
file input that POSTs to the upload endpoint
- CSP updated to allow img-src from backend (127.0.0.1:*)
Removed ~/.brian/images/ directory, file save, and GET /images/{filename}
serve endpoint. Upload now encodes the image as a base64 data URL
(data:image/...;base64,...) and stores it directly in the content and
url fields of the knowledge item in SQLite. Frontend reads the data URL
directly from item.content — no backend image serving needed. Hides the
URL link in the detail sheet for image items since it's a data URL.
…s, URL resolution
The existing image item stored a /api/v1/images/ path from the old
file-based upload. After the base64 refactor removed the serve route,
this caused a broken image and app crash.
Fixes:
- Re-added GET /images/{filename} as legacy fallback serving from
~/.brian/images/ for old items
- Frontend resolveImageUrl() now prepends backend URL for /api/ paths
- ItemDetailSheet uses getApiBaseUrl() for legacy image URL resolution
- Added onError handlers on all <img> tags to hide broken images
gracefully instead of crashing
- New uploads still use base64 data URLs stored in DB
…ition The frontend can fire loadItems() before the backend port is fully resolved (default 8080 vs actual dynamic port). Added automatic retry with 3 attempts and 1s delay between retries. Only shows error to the user after all retries are exhausted. This eliminates the intermittent 'error loading items' on app startup.
Card: stripped gradient overlay, title text, and tag badges from image cards. Now renders only the photo edge-to-edge in a rounded card. Bottom sheet: hidden title heading for image items. Photo renders full-width with negative margins to bleed to edges, no padding above.
Removed the separate Upload Photo button from App.jsx. Image upload is now integrated into the New Item dialog — select 'Image' type to see a drag-and-drop file picker with preview. Uploads via POST /upload/image and dispatches 'brian-items-changed' event to refresh the items list. useKnowledge listens for this event to auto-reload.
Spence/bugfix
Accepts base64-encoded image data and creates an IMAGE item with the data stored as a data URL in the DB. Handles both raw base64 and data:... prefixed input. Validates base64 is decodable before storing. Supports optional title, media_type, tags, and project_id params.
feat: add upload_image MCP tool for AI assistant image uploads
- Replace simple text loading with full-screen PixelBlast animation - Fix infinite loading loop in HomeView by removing redundant loadItems() call - HomeView now only fetches weather on mount, items are loaded by App.jsx - Add centered 256x256 PixelBlast with accent color on loading state - Improve loading experience with smooth animated squares
Fix loading UI jank with centered PixelBlast animation
- Changed useEffect to depend on currentProject.id and viewAllProjects instead of loadItems callback - This prevents the effect from re-running when loadItems is recreated - Added initialLoadDone ref to track first load completion - Should eliminate the loading jank/flickering
- Changed currentProject?.id to stable projectId variable (null instead of undefined) - Prevents useEffect from re-running when currentProject is null - Fixes loading jank and errors when switching to 'all projects' view
- Frontend was using old build from Feb 21 16:54 - Loading UI changes weren't being included in app bundle - Now rebuilds frontend before every Tauri build
…ture - Add comprehensive ASCII diagrams for: - Core features overview - Knowledge graph visualization - Hierarchical zoom levels - Smart search pipeline - Multi-project architecture - Goose AI integration workflow - Similarity algorithm pipeline - UI interface overview - Reorganize content into clear sections: - What Makes Brian Special - Core Features (with diagrams) - Quick Start - Usage Guide - Configuration - Development - Troubleshooting - Roadmap - Add detailed tables for: - Prerequisites - View modes - Search examples - Connection types - Troubleshooting - Roadmap - Improve project structure documentation - Add performance optimizations section - Enhance acknowledgments with categorized technologies - Maintain the playful 'Brian vs Brain' humor throughout Generated by Mistral Vibe Code
…ed structure" This reverts commit 2044e01.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.