Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 'lts/*'
Expand All @@ -23,6 +25,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Tauri Linux system dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
libgtk-3-dev \
libwebkit2gtk-4.1-dev \
libsoup-3.0-dev \
libjavascriptcoregtk-4.1-dev \
librsvg2-dev \
libayatana-appindicator3-dev
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
Expand Down
45 changes: 0 additions & 45 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,48 +290,3 @@ Keep the active local branch focused on the existing lockfile/sidecar cleanup, t
- Do not log applicant PII (email, phone, SSN) to any log file — only submission metadata
- Do not bundle Playwright browsers with the app — require user to run `playwright install chromium` once during setup
- Do not attempt to scaffold all ATS adapters in one Claude Code session — each adapter is a full session

<!-- portfolio-context:start -->

# Portfolio Context

## What This Project Is

Tauri 2 desktop app: central hub for automated job search pipeline. Tracks listings, automates ATS submissions (Ashby, Greenhouse, LinkedIn, Indeed, Workday), manages follow-up emails via Gmail API, generates interview prep briefs.

## Current State

v1.0 complete per MEMORY.md. Core Tauri scaffold with React frontend, Rust backend, Python sidecar (FastAPI on :9876). SQLite via tauri-plugin-sql. Gmail OAuth2 flow and Keychain credential storage wired.

## Stack

- **Desktop**: Tauri 2 + Rust
- **Frontend**: React 19 + TypeScript + Vite + shadcn/ui + Tailwind
- **State**: Zustand (UI) + TanStack Query (SQLite)
- **IPC**: tauri-specta (auto-generated TS bindings)
- **Database**: SQLite via tauri-plugin-sql
- **Sidecar**: Python 3.12 + FastAPI + Playwright + httpx + Anthropic SDK
- **Auth**: Gmail OAuth2 + macOS Keychain

## How To Run

```
npm install
npm run tauri dev
```

Python sidecar starts automatically via Tauri sidecar command. Requires `playwright install chromium` once for browser automation. Gmail OAuth prompts on first use.

## Known Risks

- Auto-submit disabled by design — dry-run preview is always the default before submitting
- Playwright LinkedIn/Indeed automation may break on DOM changes — heuristic selectors, not stable IDs
- Gmail OAuth token stored locally; revoke at Google account settings if machine is compromised
- Do not log applicant PII (email, phone) to any file — submission metadata only
- Python sidecar port 9876 conflicts will surface as generic health-check failure

## Next Recommended Move

Review the known issues table in CLAUDE.md for the highest-priority UX gap. Each major view (Tracker, Submit Console, Follow-ups, Analytics) is its own session scope.

<!-- portfolio-context:end -->
256 changes: 131 additions & 125 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,127 +1,133 @@
{
"name": "job-command-center",
"private": true,
"version": "1.0.0",
"type": "module",
"author": "Danny Smith",
"copyright": "Copyright © 2025 Danny Smith. All rights reserved.",
"license": "MIT",
"engines": {
"node": ">=20.0.0"
},
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"typecheck": "tsc --noEmit",
"lint": "eslint . --max-warnings 0",
"lint:fix": "eslint . --fix",
"format": "prettier --write .",
"format:check": "prettier --check .",
"test": "vitest",
"test:run": "vitest run",
"test:ui": "vitest --ui",
"test:coverage": "vitest run --coverage",
"rust:fmt": "source ~/.cargo/env && cd src-tauri && cargo fmt",
"rust:fmt:check": "source ~/.cargo/env && cd src-tauri && cargo fmt --check",
"rust:clippy": "source ~/.cargo/env && cd src-tauri && cargo clippy -- -D warnings",
"rust:clippy:fix": "source ~/.cargo/env && cd src-tauri && cargo clippy --fix --allow-dirty",
"rust:test": "source ~/.cargo/env && cd src-tauri && cargo test",
"rust:bindings": "source ~/.cargo/env && cd src-tauri && cargo test export_bindings -- --ignored --nocapture",
"tauri": "tauri",
"tauri:dev": "source ~/.cargo/env && npm run tauri dev",
"tauri:build": "npm run tauri build",
"tauri:check": "npm run typecheck && npm run tauri build --check",
"test:all": "npm run test:run && npm run rust:test",
"check:all": "npm run typecheck && npm run lint && npm run ast:lint && npm run format:check && npm run rust:fmt:check && npm run rust:clippy && npm run test:run && npm run rust:test",
"fix:all": "npm run lint:fix && npm run format && npm run rust:fmt && npm run rust:clippy:fix",
"build:analyze": "vite build && echo '\\n📊 Bundle analysis complete. Check dist/ folder sizes or use a tool like webpack-bundle-analyzer on the dist folder.'",
"release:prepare": "node scripts/prepare-release.js",
"task:complete": "node scripts/complete-task.js",
"task:rename-done": "node scripts/complete-task.js --rename-existing",
"knip": "knip",
"jscpd": "jscpd",
"ast:lint": "ast-grep scan",
"ast:fix": "ast-grep scan --fix"
},
"dependencies": {
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@radix-ui/react-alert-dialog": "^1.1.15",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.8",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-toggle": "^1.1.10",
"@radix-ui/react-toggle-group": "^1.1.11",
"@radix-ui/react-tooltip": "^1.2.8",
"@tailwindcss/vite": "^4.1.18",
"@tanstack/react-query": "^5.90.12",
"@tanstack/react-query-devtools": "^5.91.1",
"@tauri-apps/api": "^2.9.1",
"@tauri-apps/plugin-clipboard-manager": "^2.3.2",
"@tauri-apps/plugin-dialog": "^2.7.0",
"@tauri-apps/plugin-fs": "^2.5.0",
"@tauri-apps/plugin-log": "^2.7.1",
"@tauri-apps/plugin-notification": "^2.3.3",
"@tauri-apps/plugin-opener": "^2.5.2",
"@tauri-apps/plugin-os": "^2.3.2",
"@tauri-apps/plugin-process": "^2.3.1",
"@tauri-apps/plugin-updater": "^2.9.0",
"@tauri-apps/plugin-window-state": "^2.4.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"i18next": "^25.7.3",
"lucide-react": "^0.561.0",
"radix-ui": "^1.4.3",
"react": "^19.2.3",
"react-day-picker": "^9.12.0",
"react-dom": "^19.2.3",
"react-i18next": "^16.5.0",
"react-markdown": "^10.1.0",
"react-resizable-panels": "^3.0.6",
"recharts": "^3.8.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.4.0",
"tailwindcss": "^4.1.18",
"zustand": "^5.0.9"
},
"devDependencies": {
"@ast-grep/cli": "^0.40.3",
"@eslint/js": "^9.39.2",
"@tauri-apps/cli": "^2.9.6",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.1",
"@testing-library/user-event": "^14.6.1",
"@types/node": "^25.0.2",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.2",
"@vitest/coverage-v8": "^4.0.15",
"babel-plugin-react-compiler": "^1.0.0",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.25",
"jscpd": "^4.0.5",
"jsdom": "^27.3.0",
"knip": "^5.73.4",
"prettier": "^3.7.4",
"tw-animate-css": "^1.4.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.49.0",
"vite": "^7.3.0",
"vitest": "^4.0.15"
}
"name": "job-command-center",
"private": true,
"version": "1.0.0",
"type": "module",
"author": "Danny Smith",
"copyright": "Copyright © 2025 Danny Smith. All rights reserved.",
"license": "MIT",
"engines": {
"node": ">=20.0.0"
},
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"typecheck": "tsc --noEmit",
"lint": "eslint . --max-warnings 0",
"lint:fix": "eslint . --fix",
"format": "prettier --write .",
"format:check": "prettier --check .",
"test": "vitest",
"test:run": "vitest run",
"test:ui": "vitest --ui",
"test:coverage": "vitest run --coverage",
"rust:fmt": "source ~/.cargo/env && cd src-tauri && cargo fmt",
"rust:fmt:check": "source ~/.cargo/env && cd src-tauri && cargo fmt --check",
"rust:clippy": "source ~/.cargo/env && cd src-tauri && cargo clippy -- -D warnings",
"rust:clippy:fix": "source ~/.cargo/env && cd src-tauri && cargo clippy --fix --allow-dirty",
"rust:test": "source ~/.cargo/env && cd src-tauri && cargo test",
"rust:bindings": "source ~/.cargo/env && cd src-tauri && cargo test export_bindings -- --ignored --nocapture",
"tauri": "tauri",
"tauri:dev": "source ~/.cargo/env && npm run tauri dev",
"tauri:build": "npm run tauri build",
"tauri:check": "npm run typecheck && npm run tauri build --check",
"test:all": "npm run test:run && npm run rust:test",
"check:all": "npm run typecheck && npm run lint && npm run ast:lint && npm run format:check && npm run rust:fmt:check && npm run rust:clippy && npm run test:run && npm run rust:test",
"fix:all": "npm run lint:fix && npm run format && npm run rust:fmt && npm run rust:clippy:fix",
"build:analyze": "vite build && echo '\\n📊 Bundle analysis complete. Check dist/ folder sizes or use a tool like webpack-bundle-analyzer on the dist folder.'",
"release:prepare": "node scripts/prepare-release.js",
"task:complete": "node scripts/complete-task.js",
"task:rename-done": "node scripts/complete-task.js --rename-existing",
"knip": "knip",
"jscpd": "jscpd",
"ast:lint": "ast-grep scan",
"ast:fix": "ast-grep scan --fix"
},
"dependencies": {
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@radix-ui/react-alert-dialog": "^1.1.15",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.8",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-toggle": "^1.1.10",
"@radix-ui/react-toggle-group": "^1.1.11",
"@radix-ui/react-tooltip": "^1.2.8",
"@tailwindcss/vite": "^4.1.18",
"@tanstack/react-query": "^5.90.12",
"@tanstack/react-query-devtools": "^5.91.1",
"@tauri-apps/api": "^2.9.1",
"@tauri-apps/plugin-clipboard-manager": "^2.3.2",
"@tauri-apps/plugin-dialog": "^2.7.0",
"@tauri-apps/plugin-fs": "^2.5.0",
"@tauri-apps/plugin-log": "^2.7.1",
"@tauri-apps/plugin-notification": "^2.3.3",
"@tauri-apps/plugin-opener": "^2.5.2",
"@tauri-apps/plugin-os": "^2.3.2",
"@tauri-apps/plugin-process": "^2.3.1",
"@tauri-apps/plugin-updater": "^2.9.0",
"@tauri-apps/plugin-window-state": "^2.4.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"i18next": "^25.7.3",
"lucide-react": "^0.561.0",
"radix-ui": "^1.4.3",
"react": "^19.2.3",
"react-day-picker": "^9.12.0",
"react-dom": "^19.2.3",
"react-i18next": "^16.5.0",
"react-markdown": "^10.1.0",
"react-resizable-panels": "^3.0.6",
"recharts": "^3.8.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.4.0",
"tailwindcss": "^4.1.18",
"zustand": "^5.0.9"
},
"devDependencies": {
"@ast-grep/cli": "^0.40.3",
"@eslint/js": "^9.39.2",
"@tauri-apps/cli": "^2.9.6",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.1",
"@testing-library/user-event": "^14.6.1",
"@types/node": "^25.0.2",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.2",
"@vitest/coverage-v8": "^4.0.15",
"babel-plugin-react-compiler": "^1.0.0",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.25",
"jscpd": "^4.0.5",
"jsdom": "^27.3.0",
"knip": "^5.73.4",
"prettier": "^3.7.4",
"tw-animate-css": "^1.4.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.49.0",
"vite": "^7.3.0",
"vitest": "^4.0.15"
},
"pnpm": {
"onlyBuiltDependencies": [
"@ast-grep/cli",
"esbuild"
]
}
}
Loading