Skip to content

das - #36

Open
spencrmartin wants to merge 79 commits into
spencrmartin-patch-4from
main
Open

das#36
spencrmartin wants to merge 79 commits into
spencrmartin-patch-4from
main

Conversation

@spencrmartin

Copy link
Copy Markdown
Owner

No description provided.

- 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
spencrmartin and others added 30 commits February 21, 2026 14:50
…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.
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.
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants