18 KiB
Project Guidelines
Protecting This File
IMPORTANT: This file (CLAUDE.md) can be lost during branch operations!
Git doesn't protect untracked/ignored files during git checkout or branch switches. Before major git operations:
cp CLAUDE.md CLAUDE.md.backup
Project Structure
This project follows a standardized structure for Tauri desktop apps.
ProjectRoot/
├── CLAUDE.md # AI assistant guidelines
├── README.md # Project documentation
├── build_spec.md # Full project specification
├── build_plan.md # Phased task checklist
├── .gitignore
├── docs/ # Technical documentation
│ ├── README.md # Docs index with table of contents
│ ├── getting-started.md
│ ├── architecture.md
│ ├── development.md
│ ├── deployment.md
│ └── feature-*.md # Feature docs (added per phase)
└── app/ # ALL project code lives here
├── build.sh # Build script
├── package.json
├── vite.config.ts
├── tsconfig.json
├── index.html
├── src/
│ ├── main.ts
│ ├── App.vue
│ ├── boot/
│ │ └── console-logger.ts
│ ├── components/
│ ├── composables/
│ ├── css/
│ ├── layouts/
│ ├── pages/
│ ├── router/
│ ├── stores/
│ ├── services/
│ └── types/
└── src-tauri/
├── Cargo.toml
├── tauri.conf.json
├── icons/
└── src/
└── main.rs
TeamIDE Project Management
If this project was scaffolded from Ideation, .teamide/ PM files (plans, tasks, project.yml) should already exist — generated from build_plan.md during scaffolding. See the Ideation workspace's claude.md for the PM generation workflow and format details.
Terminology
- "build spec" or "spec" refers to
build_spec.md- the full project description - "build plan" or "plan" refers to
build_plan.md- the phased task checklist
Onboarding Questions (Ask at Project Start)
At the start of every new project conversation, gather these details before writing any code:
- Brand colors & fonts - Primary, secondary, accent colors. Typography preferences. Any existing brand guidelines or color palette?
- App personality & tone - Formal or casual? Dark or light default theme? Dense or spacious layout? Any design inspiration or reference apps?
- Target platform - Desktop-first, mobile-first, or responsive? Specific OS targets (macOS, Windows, Linux)? Minimum screen sizes?
- Auth requirements - Login method (email/password, OAuth, magic link)? User roles and permissions? Public or private app?
Don't assume defaults for any of these. Ask, get answers, then document them in the Project-Specific Configuration section at the bottom of this file.
Tech Stack
- Frontend: Quasar Framework (Vue 3) + Vite
- Desktop: Tauri (Rust) - Cross-platform builds (macOS, Windows, Linux)
- State: Pinia
- Routing: Vue Router
- Backend (optional): Nhost (self-hosted or cloud)
- PostgreSQL - Database
- Hasura - GraphQL API
- Auth - Authentication service
- Storage - File storage
- Functions - Serverless functions (Node.js)
Testing Changes (CRITICAL)
After every code editing session, you MUST rebuild the app so the user can test your changes.
cd app
./build.sh -dev -open
Why This Matters
- This uses a build-and-run approach, not hot reload
- Your code changes are NOT visible until you rebuild
- Always rebuild before asking the user to test
- If the user reports issues, check the logs first:
./build.sh logs
Build Script
Always use build.sh for building the app. This ensures consistent builds and logging for collaborative debugging.
cd app
./build.sh -dev -open # Build dev and open app
./build.sh -prod # Build prod to build/prod/
./build.sh -install # Build prod and open Finder for drag-to-Applications
./build.sh logs # View latest console output (last 100 lines)
./build.sh clean # Clean build artifacts (preserves logs)
./build.sh clean-logs # Remove all logs
Commands
| Command | Description |
|---|---|
-dev |
Development build. Bundle ID: com.[app].dev |
-prod |
Production build. Builds to build/prod/. |
-install |
Build prod and open Finder for drag-to-Applications. |
-open |
Open the app after building (use with -dev or -prod) |
-secure |
Build without automation (no HTTP server on port 9876) |
logs |
Show last 100 lines of console output |
clean |
Remove build artifacts (preserves logs) |
clean-logs |
Remove all log files |
Why Build-and-Run (No Hot Reload)
This project uses a build-and-run approach instead of hot reload:
| Aspect | Hot Reload | Build-and-Run |
|---|---|---|
| Speed | Instant updates | Full rebuild required |
| Dev/Prod parity | Different environments | Identical to production |
| Debugging | Dev-only issues possible | What you test = what ships |
| Logging | Console only | Full logs to files |
The tradeoff is slower iteration, but you always test exactly what will ship.
What the Build Does
- Build with Tauri -
npx tauri builddoes all of the following:- Build frontend (
npm run build→dist/) - Build Rust binary with embedded frontend
- Create .app bundle in
src-tauri/target/release/bundle/macos/
- Build frontend (
- Copy to build folder - Copies bundle to
build/dev/orbuild/prod/ - Update Info.plist - Sets correct bundle ID for dev/prod
- Install (prod only) - Copies to
~/Applications
Important: Using npx tauri build ensures the frontend is properly embedded in the binary. Manual cargo build --release does NOT embed the frontend.
Build Output
Builds are organized in separate folders:
app/build/
├── dev/
│ └── AppName.app # Dev build (bundle ID: .dev)
├── prod/
│ └── AppName.app # Prod build
├── logs/
│ ├── build_dev_*.log # Build output logs
│ └── build_latest.log # Symlink to latest
└── console/
├── console_*.log # App runtime console output
└── console_latest.log # Symlink to latest
Dev and prod have different bundle IDs, so both can run simultaneously.
Logging
All build output and app console output is logged:
- Build logs:
build/logs/build_latest.log- compilation output, errors - Console logs:
build/console/console_latest.log- runtimeconsole.log(), errors
When debugging, run ./build.sh logs to see the latest console output.
Console Logging (Already Configured)
This template includes console logging out of the box. Frontend console.log() calls are forwarded to Rust and captured in log files.
How it works:
console.log() → console-logger.ts → invoke('log_to_stdout') → Rust println!() → stdout → build.sh logs
Automation Plugin (Already Configured)
This template includes an automation HTTP server for external testing and AI-assisted development.
How it works:
External Tool (curl) → HTTP Server (port 9876) → Rust → JavaScript → DOM
Automation API
When the app is running, these endpoints are available:
# Health check
curl http://localhost:9876/automation/health
# Execute a command
curl -X POST http://localhost:9876/automation/execute \
-H "Content-Type: application/json" \
-d '{"command": "click", "args": {"selector": "#my-button"}}'
# Take a screenshot
curl http://localhost:9876/automation/screenshot --output screenshot.png
Available Commands
| Command | Args | Description |
|---|---|---|
navigate |
{path} |
Navigate to route |
click |
{selector} |
Click element |
type |
{selector, text} |
Type into input |
getText |
{selector} |
Get element text |
getValue |
{selector} |
Get input value |
exists |
{selector} |
Check if element exists |
waitFor |
{selector, timeout?} |
Wait for element |
getUrl |
{} |
Get current URL |
screenshot |
- | Capture page (via endpoint) |
Full command list in src/automation/README.md.
Disabling Automation
Option 1: Build flag (recommended)
Use the -secure flag to build without automation:
./build.sh -prod -secure # Production build without automation
Option 2: Permanently disable
Remove automation from the default features in Cargo.toml:
[features]
default = ["custom-protocol"] # Remove "automation"
When to Clean
Run ./build.sh clean when:
- Switching between major dependency versions
- Build errors reference stale/cached files
- Rust compilation issues after Tauri config changes
- Before creating a fresh production build
Node.js Environment
Node.js version: Always match Debian stable (currently Node.js 18.x for Debian 12 Bookworm)
Check current Debian stable Node version: https://packages.debian.org/stable/nodejs
Package manager: Use npm. Commit lock files for reproducible builds.
Rules:
- Always commit
package-lock.json- required for deterministic installs - Never ignore lock files (
package-lock.json,yarn.lock,pnpm-lock.yaml) - Use
npm installto install dependencies - Use
npm run <script>to run package scripts
Communication Rules
Always explain before acting. Before running any command, SSH, database query, or making changes:
- Give a short, concise explanation of WHAT you're about to do and WHY
- Wait for approval on anything that modifies production data or server state
- Don't chain multiple server commands without explaining each step
Database Rules (If Using Nhost Backend)
NEVER make direct database changes via Hasura MCP or Hasura Console.
All database changes MUST be done through migrations so they:
- Are tracked in version control
- Persist across restarts (
nhost down/nhost up) - Can be reproduced in production
Hasura MCP Usage
- READ-ONLY: Use Hasura MCP only to inspect schema, check what tables exist, verify tracking status
- NO MUTATIONS: Never use it to create tables, add columns, track tables, or modify permissions
- MIGRATIONS ONLY: All schema changes go in
app/nhost/migrations/default/ - METADATA ONLY: All Hasura config (tracking, permissions, relationships) go in
app/nhost/metadata/
How to Make Database Changes
- Write SQL migration in
app/nhost/migrations/default/<N>_<name>/up.sql(N = next version number) - Write rollback in
app/nhost/migrations/default/<N>_<name>/down.sql - Add Hasura metadata YAML in
app/nhost/metadata/databases/default/tables/ - Restart dev environment to apply migrations
- Commit and push
Hasura Role Permissions (CRITICAL)
NEVER define permissions for the admin role in Hasura metadata.
Hasura reserves admin as a special role with full unrestricted access. Defining explicit permissions for admin causes metadata errors.
Define permissions only for user, public, and custom roles.
Development Approach
Integrate-as-you-go - Connect real backend services as you build. Don't build elaborate mock systems.
Persistence Rules (CRITICAL)
All persistence requires an explicit user action. No exceptions.
Never auto-save. Never sync state in the background. Never write to the database on a timer or watcher. Every single write to a database, file, or storage must be traceable to a deliberate user action.
The Rule
Data is only persisted when the user deliberately triggers it — clicking "Save", submitting a form, confirming a dialog, toggling a setting, etc.
WRONG — Never Do This
- Auto-saving form fields as the user types
- Syncing component state to the database on mount/unmount
- Watchers that write to storage when reactive data changes
- Debounced save on input change
- Persisting UI state automatically (sidebar position, tab selection, scroll position)
- Writing to the database during navigation or route changes
- Background sync intervals or polling that writes data
RIGHT — Always Do This
- User clicks "Save" → write to database
- User submits a form → persist the data
- User explicitly toggles a setting → save that setting
- User clicks "Delete" → confirm, then remove from database
- User drags an item to reorder → save on drop (explicit action)
Why This Matters
Auto-save creates unpredictable behavior, race conditions, and persists data the user didn't intend to save. Every write should be traceable to a conscious user decision. If you can't point to the button or action the user took to trigger the save, it shouldn't be saving.
Implementation Pattern
User Action → Service Call → Database Write → Confirm Success to User
- User triggers action (click, submit, confirm)
- Call the appropriate service/API
- Write to database
- Show success feedback (Quasar Notify or UI update)
- If it fails, show an error — don't silently retry
Code Quality
TypeScript
- Use TypeScript strict mode
- Define interfaces/types for all data structures in
src/types/ - No
anytypes unless absolutely unavoidable — useunknownand narrow instead - Type all function parameters and return values
Vue / Quasar Conventions
- Use Composition API with
<script setup lang="ts">for all components - One component per file, named in PascalCase (e.g.,
UserProfile.vue) - Composables go in
src/composables/, prefixed withuse(e.g.,useAuth.ts) - Keep components focused — if it's doing too much, split it
- Props down, events up — don't mutate props, emit events to parent
Naming
- Files: PascalCase for components (
UserCard.vue), camelCase for everything else (userService.ts) - Variables/functions: camelCase
- Constants: UPPER_SNAKE_CASE for true constants, camelCase for derived/computed values
- Types/Interfaces: PascalCase, no
Iprefix (useUsernotIUser) - CSS classes: kebab-case
General
- No magic numbers or strings — use named constants
- Keep functions short and single-purpose
- Colocate related code — don't scatter logic across files unnecessarily
- Delete dead code, don't comment it out
Error Handling
User-Facing Errors
- Always show human-readable messages — never expose raw error objects, stack traces, or technical details to the user
- Use Quasar's
Notifyplugin for transient feedback (success, warning, error) - Use inline error messages for form validation
Technical Errors
- Log technical details to console for debugging (
console.errorwith context) - Include the operation that failed and relevant IDs in log messages
- Never swallow errors silently — if you catch it, either handle it or log it
Pattern
try {
const result = await userService.updateProfile(data)
Notify.create({ type: 'positive', message: 'Profile updated' })
} catch (error) {
console.error('Failed to update profile:', error)
Notify.create({ type: 'negative', message: 'Could not update profile. Try again.' })
}
Where to Catch
- Catch at service boundaries (API calls, database operations), not deep in utility functions
- Let errors propagate up to where they can be meaningfully handled
- Don't wrap every line in try/catch — catch at the level where you can show the user feedback
Documentation
Every phase ends with documentation. After completing a feature or phase:
- Create feature docs - Copy
docs/TEMPLATE-feature.mdtodocs/feature-[name].md - Update the index - Add links to
docs/README.mdtable of contents - Update core docs - Keep
architecture.md,development.mdcurrent
Docs Structure
| File | Purpose |
|---|---|
docs/README.md |
Index with table of contents |
docs/getting-started.md |
Installation & setup |
docs/architecture.md |
Tech stack & design decisions |
docs/development.md |
Development workflow |
docs/deployment.md |
Build & deployment |
docs/feature-*.md |
Feature-specific documentation |
Naming Convention
feature-[name].md- Feature docs (e.g.,feature-authentication.md)component-[name].md- Component docsintegration-[name].md- Integration docsguide-[topic].md- How-to guides
Commit Rules
- Never mention Claude, AI, or "Generated with Claude Code" in commits
- All commits are authored by the developer - no co-author tags
- Write commit messages in first person as the developer
- Keep commit messages concise and descriptive of the changes
Branch Strategy
- main - Production branch
- test - Staging/testing branch
Workflow:
- Develop and test locally
- Push to
testbranch for staging - Verify on staging
- Merge
test→mainfor production
Customization Checklist
When using this template for a new project:
-
Update
app/src-tauri/tauri.conf.json:productNameidentifier(e.g.,com.yourapp.app)title
-
Update
app/package.json:namedescription
-
Update
app/build.sh:APP_NAMEBINARY_NAME(must match productName)BUNDLE_ID_DEVBUNDLE_ID_PROD
-
Replace placeholder icons in
app/src-tauri/icons/ -
Update documentation:
- Replace
[Project Name]in alldocs/*.mdfiles - Update
docs/architecture.mdwith project-specific tech decisions - Update
docs/getting-started.mdwith project-specific setup
- Replace
-
Add your pages, components, and stores
-
Add Tauri commands to
src-tauri/src/main.rs
Project-Specific Configuration
Project Name
[PROJECT_NAME]
Description
[Brief description from build_spec.md]
Backend (If Applicable)
| Service | URL |
|---|---|
| GraphQL | |
| Auth | |
| Storage |
Database Schema (If Applicable)
[Tables and relationships]
TeamIDE Hosting (If Applicable)
- Workspace ID:
your-workspace-id-here - Use
teamide_deployto deploy changes - Use
teamide_set_env_varsto configure environment variables - Use
teamide_get_logsto check build status