Commit graph

42 commits

Author SHA1 Message Date
6812e3f9ef feat: /messages route + /contacts redirect + nav rename (#74) 2026-04-20 13:04:27 -07:00
aa09b20e7e feat: MessageTemplateModal component (apply/create/edit modes) (#74) 2026-04-20 12:58:00 -07:00
b77ec81cc6 fix: thread logged_at through message stack; Esc handler and localNow fixes
- scripts/messaging.py: add logged_at param to create_message; use provided value or fall back to _now_utc()
- dev-api.py: add logged_at: Optional[str] = None to MessageCreateBody
- web/src/stores/messaging.ts: remove logged_at from Omit, add as optional intersection so callers can pass it through
- web/src/components/MessageLogModal.vue: pass logged_at in handleSubmit payload; move @keydown.esc from backdrop to modal-dialog (which holds focus); compute localNow fresh inside watch so it reflects actual open time
2026-04-20 12:55:41 -07:00
8df3297ab6 feat: MessageLogModal component (#74) 2026-04-20 12:52:19 -07:00
43599834d5 feat: ResumeSyncConfirmModal — before/after confirmation for profile sync 2026-04-16 14:11:37 -07:00
03b9e52301 feat: references tracker and recommendation letter system (#96)
- references_ + job_references tables with CREATE + migration
- Full CRUD: GET/POST /api/references, PATCH/DELETE /api/references/:id
- Link/unlink to jobs: POST/DELETE /api/references/:id/link-job/:job_id
- GET /api/references/for-job/:job_id — linked refs with prep/letter drafts
- POST /api/references/:id/prep-email — LLM drafts heads-up email to send
  reference before interview; persisted to job_references.prep_email
- POST /api/references/:id/rec-letter — LLM drafts recommendation letter
  reference can edit and send on their letterhead (Paid/BYOK tier)
- ReferencesView.vue: add/edit/delete form, tag system (technical/managerial/
  character/academic), inline confirm-before-delete
- Route /references + IdentificationIcon nav link
2026-04-15 08:42:06 -07:00
0e4fce44c4 feat: shadow listing detector, hired feedback widget, contacts manager
Shadow listing detector (#95):
- Capture date_posted from JobSpy in discover.py + insert_job()
- Add date_posted migration to _MIGRATIONS
- _shadow_score() heuristic: 'shadow' (≥30 days stale), 'stale' (≥14 days)
- list_jobs() computes shadow_score per listing
- JobCard.vue: 'Ghost post' and 'Stale' badges with tooltip

Post-hire feedback widget (#91):
- Add hired_feedback migration to _MIGRATIONS
- POST /api/jobs/:id/hired-feedback endpoint
- InterviewCard.vue: optional widget on hired cards with factor
  checkboxes + freetext; dismissible; shows saved state
- PipelineJob interface extended with hired_feedback field

Contacts manager (#73):
- GET /api/contacts endpoint with job join, direction/search filters
- New ContactsView.vue: searchable table, inbound/outbound filter,
  signal chip column, job link
- Route /contacts added; Contacts nav link (UsersIcon) in AppNav

Also: add git to Dockerfile apt-get for circuitforge-core editable install
2026-04-15 08:34:12 -07:00
8e36863a49 feat: Interview prep Q&A, cf-orch hardware profile, a11y fixes, dark theme
Some checks failed
CI / Backend (Python) (push) Failing after 2m15s
CI / Frontend (Vue) (push) Failing after 21s
Mirror / mirror (push) Failing after 9s
Backend
- dev-api.py: Q&A suggest endpoint, Log Contact, cf-orch node detection in wizard
  hardware step, canonical search_profiles format (profiles:[...]), connections
  settings endpoints, Resume Library endpoints
- db_migrate.py: migrations 002/003/004 — ATS columns, resume review, final
  resume struct
- discover.py: _normalize_profiles() for legacy wizard YAML format compat
- resume_optimizer.py: section-by-section resume parsing + scoring
- task_runner.py: Q&A and contact-log task types
- company_research.py: accessibility brief column wiring
- generate_cover_letter.py: restore _candidate module-level binding

Frontend
- InterviewPrepView.vue: Q&A chat tab, Log Contact form, MarkdownView rendering
- InterviewCard.vue: new reusable card component for interviews kanban
- InterviewsView.vue: rejected analytics section with stage breakdown chips
- ResumeProfileView.vue: sync with new resume store shape
- SearchPrefsView.vue: cf-orch toggle, profile format migration
- SystemSettingsView.vue: connections settings wiring
- ConnectionsSettingsView.vue: new view for integration connections
- MarkdownView.vue: new component for safe markdown rendering
- ApplyWorkspace.vue: a11y — h1→h2 demotion, aria-expanded on Q&A toggle,
  confirmation dialog on Reject action (#98 #99 #100)
- peregrine.css: explicit [data-theme="dark"] token block for light-OS users (#101),
  :focus-visible outline (#97)
- wizard.css: cf-orch hardware step styles
- WizardHardwareStep.vue: cf-orch node display, profile selection with orch option
- WizardLayout.vue: hardware step wiring

Infra
- compose.yml / compose.cloud.yml: cf-orch agent sidecar, llm.cloud.yaml mount
- Dockerfile.cfcore: cf-core editable install in image build
- HANDOFF-xanderland.md: Podman/systemd setup guide for beta tester
- podman-standalone.sh: standalone Podman run script

Tests
- test_dev_api_settings.py: remove stale worktree path bootstrap (credential_store
  now in main repo); fix job_boards fixture to use non-empty list
- test_wizard_api.py: update profiles assertion to superset check (cf-orch added);
  update step6 assertion to canonical profiles[].titles format
2026-04-14 17:01:18 -07:00
f22e713968 fix: review issues — import size limit, aria labels, CSS vars, Set reactivity 2026-04-12 11:52:23 -07:00
1ada92f7d7 feat: add Resumes route and nav link
Add /resumes route (lazy-loaded ResumesView) to router and add
DocumentTextIcon Resumes entry to AppNav sidebar navLinks after Apply.
2026-04-12 11:36:13 -07:00
8245333c9c feat: add ResumeLibraryCard to Apply workspace 2026-04-12 11:35:06 -07:00
f7b719f854 feat: replace inline review with ResumeReviewModal; add save-to-library on approve 2026-04-12 11:29:03 -07:00
a3aaed0e0c feat: add ResumeReviewModal with paged tab navigation and color-coded status 2026-04-12 11:18:01 -07:00
6115a68550 feat: Vue SPA demo mode support
Some checks failed
CI / Backend (Python) (push) Failing after 10s
CI / Frontend (Vue) (push) Successful in 18s
Mirror / mirror (push) Failing after 8s
- useToast.ts: global reactive toast singleton for cross-component toasts
- App.vue: sticky demo mode banner + global toast slot
- router: bypass wizard gate entirely in demo mode (pre-seeded data)
- ApplyWorkspace, CompanyResearchModal: guard generate() in demo mode
- fineTune store: guard submitJob() in demo mode
- ui_switcher.py: remove Vue→Streamlit fallback in demo mode (now handled natively)

All LLM-triggering actions show a toast and no-op in demo mode.
Backend already blocks inference via DEMO_MODE env; Vue layer adds UX signal.

Closes #46
2026-04-06 00:07:26 -07:00
4f825d0f00 feat(#45): manual theme switcher (light/dark/solarized/colorblind-safe)
Some checks failed
CI / test (push) Failing after 18s
- theme.css: explicit [data-theme] blocks for light, dark, solarized-dark,
  solarized-light, colorblind (Wong 2011 palette); auto-dark media query
  updated to :root:not([data-theme]) so explicit themes always win
- useTheme.ts: singleton composable — setTheme(), restoreTheme(), initTheme();
  persists to localStorage + API; coordinates with hacker mode exit
- AppNav.vue: theme <select> in sidebar footer; exitHackerMode now calls
  restoreTheme() instead of deleting data-theme directly
- useEasterEgg.ts: hacker mode toggle-off calls restoreTheme()
- App.vue: calls initTheme() on mount before restore()
- dev-api.py: POST /api/settings/theme endpoint persists to user.yaml
2026-04-04 22:22:04 -07:00
53b07568d9 feat(vue): accumulated parity work — Q&A, Apply highlights, AppNav switcher, cloud API
API additions (dev-api.py split across this and next commit):
- /api/jobs/{job_id}/qa GET/PATCH/suggest — Interview Prep answer storage + LLM suggestions
- /api/settings/ui-preference POST — persist streamlit/vue preference to user.yaml
- cancel_task() added to scripts/db.py (per-task cancel for Danger Zone)

Vue / UI:
- AppNav: " Classic" button to switch back to Streamlit UI (writes cookie + persists to user.yaml)
- ApplyWorkspace: Resume Highlights panel (collapsible skills/domains/keywords with job-match highlighting)
- SettingsView: hide Data tab in cloud mode (showData guard)
- ResumeProfileView: minor improvements
- useApi.ts: error handling improvements

Infra:
- compose.cloud.yml: add api service (uvicorn dev_api running in cloud container)
- docker/web/nginx.conf: proxy /api/* to api service in cloud mode
- README.md: Vue SPA now listed as Free tier feature
2026-04-04 22:04:51 -07:00
b79d13b4f2 feat(vue): parity gaps #50, #54, #61 — sort/filter, research modal, draft CL button
Some checks failed
CI / test (push) Failing after 30s
#50 Job Review list view — sort + filter controls:
- Sort by best match / newest first / company A-Z (client-side computed)
- Remote-only checkbox filter
- Job count indicator; filters reset on tab switch
- Remote badge on list items

#61 Cover letter generation from approved tab:
- ' Draft' button on each approved-list item → /apply/:id
- No extra API call; ApplyWorkspace handles generation from there

#54 Company research modal (all API endpoints already existed):
- CompanyResearchModal.vue: 3-state machine (empty→generating→ready)
  polling /research/task every 3s, displays all 7 research sections
  (company, leadership, talking points, tech, funding, red flags,
  accessibility), copy-to-clipboard for talking points, ↺ Refresh
- InterviewCard: new 'research' emit + '🔍 Research' button for
  phone_screen/interviewing/offer stages
- InterviewsView: wires modal with researchJobId/Title/AutoGen state;
  auto-opens modal with autoGenerate=true when a job is moved to
  phone_screen (mirrors Streamlit behaviour)
2026-04-02 19:26:13 -07:00
8c42de3f5c feat(merge): merge feature/vue-spa into main
Full Vue 3 SPA merge — closes #8. Major additions:

Backend (dev API):
- dev_api.py → symlink to dev-api.py (importable module alias)
- dev-api.py: full FastAPI backend (settings, jobs, interviews, prep,
  survey, digest, resume optimizer endpoints); cloud session middleware
- scripts/user_profile.py: load_user_profile / save_user_profile helpers
- scripts/discover.py + scripts/imap_sync.py: API-compatible additions

Frontend (web/src/):
- ApplyWorkspace: ATS resume optimizer panel (gap report free, rewrite paid+)
- ResumeOptimizerPanel.vue: new component with task polling + .txt download

Test suite:
- test_dev_api_settings/survey/prep/digest/interviews: full API test coverage
- fix: replace importlib.reload with monkeypatch.setattr(dev_api, "DB_PATH")
  to prevent module global reset breaking isolation across test files

Docs:
- docs/vue-spa-migration.md: migration guide
2026-04-01 07:11:14 -07:00
ee66b6b235 feat(web): add task indicator component and task store for background jobs
- web/src/stores/tasks.ts: Pinia store polling /api/tasks/active
- web/src/components/TaskIndicator.vue: sidebar + mobile task queue
  display with live count badge
- web/public/: peregrine logo assets (SVG + PNG variants)
2026-04-01 07:09:55 -07:00
931a07d4e0 chore(merge): merge main into feature/vue-spa — resolve ApplyWorkspace conflict
ApplyWorkspace.vue: kept HEAD (vue-spa) version for resume optimizer panel,
cl-error__actions wrapper, and ResumeOptimizerPanel import. main's older
version lacked these additions.
2026-03-31 21:25:15 -07:00
faf0a7c4dc feat(apply): ATS resume optimizer — gap report + LLM rewrite (paid tier)
- scripts/resume_optimizer.py: extract_jd_signals, prioritize_gaps,
  rewrite_for_ats, hallucination_check, render_resume_text
- dev_api.py: GET/POST /api/jobs/{id}/resume_optimizer + /task endpoints
- web/src/components/ResumeOptimizerPanel.vue: gap report (all tiers),
  per-section LLM rewrite + hallucination badge (paid+)
- ApplyWorkspace.vue: ResumeOptimizerPanel wired in below cover letter

Closes #29
2026-03-31 21:24:49 -07:00
49e3265132 feat(web): merge Vue SPA from feature/vue-spa; add ClassicUIButton + useFeatureFlag
- Import web/ directory (Vue 3 + Vite + UnoCSS SPA) from feature/vue-spa branch
- Add web/src/components/ClassicUIButton.vue: switch-back to Streamlit via
  cookie (prgn_ui=streamlit) + ?prgn_switch=streamlit query param bridge
- Add web/src/composables/useFeatureFlag.ts: reads prgn_demo_tier cookie for
  demo toolbar visual consistency (not an authoritative gate, see issue #8)
- Update .gitignore: add .superpowers/, pytest-output.txt, docs/superpowers/
2026-03-22 18:46:11 -07:00
4bea0899db feat(survey): implement SurveyView with navigation wiring 2026-03-21 00:27:57 -07:00
8479f79701 fix: aria-label binding, dead import, guardAndLoad network error handling
- Fix 1: Add missing `:` binding prefix to aria-label on score badge
  (was emitting literal backtick template string to DOM)
- Fix 2: Remove unused `watch` import from InterviewPrepView.vue
- Fix 3: guardAndLoad now checks interviewsStore.error after fetchAll;
  shows pageError banner instead of silently redirecting to /interviews
  on network failure; job is now a ref set explicitly in the guard
- Fix 4: Remove unconditional research-badge from InterviewCard.vue
  (added in this branch; card has no access to prep store so badge
  always showed regardless of whether research exists)
2026-03-20 18:57:41 -07:00
1cee73e233 fix: hide Prep button on hired stage cards 2026-03-20 18:51:18 -07:00
247f807e02 fix: bind aria-label on nav badge span (was static string, not template expression) 2026-03-20 10:10:10 -07:00
165811c420 feat: add Digest tab to nav and router 2026-03-20 10:07:12 -07:00
154f691334 style: use void instead of .catch on fire-and-forget digest-queue call 2026-03-20 10:06:04 -07:00
4246e71061 feat: fire digest-queue add call from digest chip handler 2026-03-20 09:58:16 -07:00
87aae6eefc feat(signals): add Unrelated and Digest reclassify chips to InterviewCard 2026-03-19 20:00:27 -07:00
804c2a8064 fix(signals): per-signal expand state, error rollback, type safety in InterviewCard 2026-03-19 19:26:36 -07:00
2796d0d911 feat(signals): expandable body + reclassify chips in InterviewCard 2026-03-19 19:22:10 -07:00
5ca25e160c feat(interviews): add stage signal banners and extend move emit in InterviewCard 2026-03-19 16:31:33 -07:00
52c7dfcfe3 feat(interviews): add preSelectedStage prop to MoveToSheet 2026-03-19 16:25:48 -07:00
4abdf21981 fix(apply): check saveCoverLetter error; document cover-letter-generated in wrapper 2026-03-19 08:36:19 -07:00
d8aca3ec52 feat(apply): extract ApplyWorkspace component with job-removed emit and perfect match easter egg 2026-03-19 08:14:15 -07:00
c5b3d31cb9 feat(interviews): add MoveToSheet bottom sheet / dialog component 2026-03-18 18:15:02 -07:00
b523707d17 feat(interviews): add InterviewCard component (medium density) 2026-03-18 18:15:01 -07:00
d138b27619 fix(vue-spa): suppress spring snap-back on processed cards
When a new job prop arrives after approve/reject, the watch cleared the
exit-transform inline style while the spring transition was still active,
causing the card to animate from offscreen back to center before the new
card rendered. Fix: set transition:none before clearing the style, then
restore it on the next rAF — browser paints the new position first.
2026-03-17 22:39:06 -07:00
75cc0760e1 feat(vue-spa): JobReviewView card stack with swipe gestures
- stores/review.ts: Pinia setup store — pending queue, undo stack,
  stoop-speed session timer (easter egg 9.2: 10 cards/60s)
- components/JobCard.vue: card content with match-score badge (colored
  pill), keyword-gap pills, expand/collapse description, footer with
  job URL + relative date; shimmer animation for ≥95% matches (ee 9.4)
- components/JobCardStack.vue: pointer-event drag with setPointerCapture,
  rolling 50ms velocity buffer for fling detection (600px/s + cos45°
  alignment), left/right color-tint overlay (red/green), spring snap-back
  on no-action, buffered exit animation before emitting approve/reject
- views/JobReviewView.vue: segmented status tabs, card stack for pending,
  list view for other statuses, action buttons, keyboard shortcuts
  (←/J reject, →/L approve, S skip, Z undo, ? help), help overlay,
  undo toast (5s), falcon stoop empty state (easter egg 9.3)
2026-03-17 22:30:33 -07:00
f3ce46e252 feat(web): implement design spec — peregrine.css, sidebar nav, HomeView
Applies the full design spec from circuitforge-plans/peregrine/2026-03-03-nuxt-design-system.md:

CSS tokens:
- Falcon Blue (#2B6CB0 / #68A8D8 dark) — was incorrectly using forest green
- Talon Orange (#E06820 / #F6872A dark) with --app-accent-text dark navy (never white)
- Full pipeline status token set (--status-pending/approve/reject/applied/synced/...)
- Match score tokens, motion tokens, type scale tokens
- Dark mode + hacker mode overrides

AppNav: sidebar layout (replaces top bar)
- Desktop ≥1024px: persistent sidebar with brand, links, hacker-exit, settings footer
- Mobile <1024px: bottom tab bar with 5 primary destinations
- Click-the-bird easter egg (9.6): 5 rapid clicks → ruffle animation
- Heroicons via @heroicons/vue/24/outline

App.vue:
- Skip-to-content link (a11y)
- Sidebar margin-left layout (desktop) / tab bar clearance (mobile)

HomeView: full dashboard implementation
- Pipeline metric cards (Pending/Approved/Applied/Synced/Rejected) with status colors
- Primary workflow buttons (Run Discovery, Sync Emails, Score Unscored) + sync banner
- Auto-enrichment status row
- Backlog management (conditionally visible)
- Add Jobs by URL / CSV upload tabs
- Advanced/danger zone in collapsible <details>
- Stoop speed toast easter egg (9.2)
- Midnight mode greeting easter egg (9.7)

WorkflowButton component with loading spinner, proper touch targets (min-height 44px)
Pinia jobs store (setup form) with counts + system status

Build: clean 2.28s, 0 errors
2026-03-17 22:00:42 -07:00
ae6021ceeb feat(web): Vue 3 SPA scaffold with avocet lessons applied
Sets up web/ Vue 3 SPA skeleton for issue #8, synthesizing all 15 gotchas
from avocet's Vue port testbed. Key fixes baked in before any component work:

- App.vue root uses .app-root class (not id="app") — gotcha #1
- overflow-x: clip on html (not hidden) — gotcha #3
- UnoCSS presetAttributify with prefixedOnly: true — gotcha #4
- peregrine.css alias map for theme variable names — gotcha #5
- useHaptics guards navigator.vibrate — gotcha #9
- Pinia setup store pattern documented — gotcha #10
- test-setup.ts stubs matchMedia, vibrate, ResizeObserver — gotcha #12
- min-height: 100dvh throughout — gotcha #13

Includes:
- All 7 Peregrine views as stubs (ready to port from Streamlit)
- AppNav with all routes
- useApi (fetch + SSE), useMotion, useHaptics, useEasterEgg composables
- Konami hacker mode easter egg + confetti + cursor trail
- docs/vue-spa-migration.md: full migration guide + implementation order
- Build verified clean (0 errors)
- .gitleaks.toml: allowlist web/package-lock.json (sha512 integrity hashes)
2026-03-17 21:24:00 -07:00