WorkWingman — Technical Overview¶
WorkWingman is a local-first Windows desktop application: a job-application Career Concierge that pulls your saved jobs, tailors documents in your voice, fills applications in a browser you can watch, helps you study for each role, and tracks where you applied and your interviews. Everything sensitive stays on your machine.
- Repository: private GitHub repo
andrewjonesdev/WorkWingman. - Target: Windows desktop, single user, offline-capable.
The problem it solves¶
Applying to jobs is repetitive, demoralizing, and error-prone. You re-type the same personal details into form after form, rewrite the same resume for every posting, and lose track of who you applied to and when interviews land. Existing "auto-apply" tools solve this by uploading your entire profile to a hosted service and blasting out applications without your review.
WorkWingman takes the opposite stance. It does the tedious work on your own machine, shows you everything before anything is submitted, and never auto-submits. It is a Career Concierge, not an autopilot.
Stack¶
| Layer | Technology | Docs |
|---|---|---|
| Shell | Electron (spawns the API + hosts the UI) | electronjs.org/docs · GitHub |
| Frontend | Angular 22 (standalone components, signals) | angular.dev · GitHub |
| Backend | ASP.NET Core on .NET 10, loopback-only | learn.microsoft.com/dotnet · GitHub |
| HTTP clients | Flurl.Http | flurl.dev · GitHub |
| Resilience | Polly (retry + backoff on 429 / 5xx / network) | pollydocs.org · GitHub |
| Browser automation | Microsoft.Playwright (.NET) | playwright.dev/dotnet · GitHub |
| HTML parsing | HtmlAgilityPack | html-agility-pack.net · GitHub |
| Secrets | KeePass vault (vault.kdbx) |
keepass.info · KeePassXC |
Solution structure¶
The .NET solution (WorkWingman.slnx) is split into four projects with a strict dependency
direction (Core has no dependencies; everyone points inward toward it):
| Project | Responsibility |
|---|---|
WorkWingman.Core |
Domain models + service interfaces. No infrastructure dependencies. |
WorkWingman.Infrastructure |
Flurl + Polly REST clients, Playwright automation, KeePass vault, LocalJsonStore, dependency doctor, service implementations. |
WorkWingman.Api |
ASP.NET Core controllers, bound to loopback http://127.0.0.1:5211 only (never 0.0.0.0). |
WorkWingman.Tests |
381 xUnit tests (unit + in-memory API smoke tests). |
Alongside the solution: frontend/ (Angular), electron/ (Electron main + preload), and
design/ (imported prototype).
Architecture¶
The Electron shell is the process supervisor: it spawns the .NET API, waits for it to come up on loopback, then opens the Angular renderer. All app data flows renderer → loopback API → controllers → Core interfaces → Infrastructure implementations → external systems.
flowchart TD
subgraph Electron["Electron shell (process supervisor)"]
Renderer["Angular 22 renderer<br/>(10 screens, signals)"]
LiveView["Embedded automation browser<br/>(live-run viewer)"]
end
Renderer -->|"loopback HTTP<br/>127.0.0.1:5211"| API
subgraph Backend[".NET 10 API (loopback only)"]
API["Controllers"]
Core["Core interfaces<br/>(models + contracts)"]
API --> Core
subgraph Infra["Infrastructure implementations"]
Clients["Flurl + Polly REST clients"]
Auto["Playwright automation<br/>(scraper + Workday engine)"]
Vault["KeePass vault<br/>(metadata over HTTP, secrets in-process)"]
Store["LocalJsonStore<br/>(~/Wingman/data — source of truth)"]
Doctor["Dependency doctor"]
end
Core --> Clients
Core --> Auto
Core --> Vault
Core --> Store
Core --> Doctor
end
Clients -->|"REST"| Ext["Google Sheets / Calendar,<br/>Apple Calendar, LinkedIn"]
Auto -->|"drives"| LiveView
Auto -->|"scrapes / fills"| Sites["LinkedIn jobs tracker,<br/>Workday applications"]
Doctor -->|"winget / npm / Playwright"| Tools["Node, Chromium, KeePassXC,<br/>Claude CLI, .NET, Git, gh"]
Key boundary rule: secrets never cross the HTTP API. The vault exposes only metadata over HTTP; the automation engine resolves passwords in-process, just-in-time.
The 10 screens¶
| # | Screen | What it does |
|---|---|---|
| 1 | Connections | Shows the status of external accounts (Google, Apple, LinkedIn) and connection settings. |
| 2 | Onboarding | Captures the intake profile — the basic facts used to fill common form fields. |
| 3 | Your story | A textarea-first deep dive into your background and voice; the raw material for tailored documents. Long spoken answers (via system dictation) beat short typed ones. |
| 4 | Job queue | Pulls saved jobs from your LinkedIn jobs-tracker, oldest-first, and lets you resync. |
| 5 | Documents | Generates and versions a tailored resume + cover letter per job, with an approval step. When no ATS adapter exists, offers one-click copy-paste of every field. |
| 6 | Study & prep | Builds a per-job study plan: docs, YouTube, Udemy, Google Skills Boost, AWS Skill Builder, MS Learn, LeetCode, and Medium links, plus an optional Claude study project. |
| 7 | Live run | The watchable run: fills a Workday application in an embedded browser, pausing to ask when unsure. Pause / resume / stop / resolve controls. |
| 8 | Applied & interviews | Tracks submitted applications and interviews, synced to Google + Apple calendars. |
| 9 | Settings & vault | Vault status/unlock and secret metadata; app settings. |
| 10 | Dependencies (doctor) | Checks required tooling versions on launch; installs/repairs after per-item approval. |
The 5-step run, end to end¶
- Read jobs —
LinkedInJobsScraper(Playwright) harvests saved jobs oldest-first into the local store. - Tailor documents —
DocumentServiceproduces a versioned resume + cover letter in the user's voice, gated by an approval step. - Fill the application —
WorkdayAutomationEnginefills the form in a watchable browser and pauses for judgement calls when confidence is low. - Study —
StudyPlanServiceassembles a per-job study plan from curated learning sources. - Track —
ApplicationTrackerServicerecords the submission and surfaces interviews on the user's calendars.
Trust guarantees (baked into the code)¶
These are not settings — they are structural properties of the system:
- Never auto-submits.
AutomationSettings.AlwaysRequireReviewBeforeSubmitis a computedtrue. There is no code path to disable it; the final submit always waits for you. - Judgement calls, not guesses. Below the fill-confidence threshold (default 90%) the run pauses and presents ranked options. Your pick persists as a rule and is never asked again.
- Secrets never cross the HTTP API. The vault exposes metadata only; the automation engine resolves passwords in-process, just-in-time. A hostile local web page hitting the loopback API cannot read your passwords.
- Local-first. The JSON store under
~/Wingman/datais the source of truth. Google Drive/Sheets is backup sync, not hosting. - Encouragement, deliberately. Rotating affirmations and milestone toasts, because job hunting is demoralizing.
Dependency doctor security¶
On launch the doctor checks that Node 22.12+, Playwright's Chromium, KeePassXC, the Claude
CLI, .NET, Git, and gh meet their minimum versions (read-only --version probes). It
installs or repairs via winget / npm / the in-process Playwright installer only after
per-item user approval. Because the install endpoint shells installers, it is token-gated
by a per-launch secret readable only by processes running as you — a hostile web page hitting
loopback cannot trigger installs. Detection (GET) stays open; only installs are gated.
Current stubs (compile, DI-registered, wired later)¶
These exist deliberately so the app compiles and runs end-to-end today, with one obvious replacement point each. They are placeholders, not finished features:
| Stub | Current behavior | Replacement point |
|---|---|---|
| Google OAuth | UnconfiguredGoogleTokenProvider throws with setup instructions. |
Desktop loopback OAuth flow; refresh token into the vault. |
| KeePass kdbx | In-memory seed data. | Real KeePassLib reads/writes of ~/Wingman/vault.kdbx. |
| Claude drafting | TemplateDrafter produces deterministic placeholder documents. |
Real Claude-backed tailoring; versioning/approval already works around it. |
| Gmail interview detection | Sync hook present, no real Gmail search. | Search Gmail for invites, then create calendar events. |
| Live-run browser | Angular shows a placeholder frame. | Electron WebContentsView attached over that region, driven by the automation engine. |
| Sheets mirroring | Not yet called on submission. | Append a row to Google Sheets once OAuth is wired. |
Roadmap (not yet built)¶
The following are planned, not implemented. They are named here only so the stubs above have context — do not treat them as working today:
- Real Google OAuth and Sheets/Calendar mirroring on submission.
- On-disk encrypted KeePass
vault.kdbx(replacing the in-memory seed). - Real Claude-backed document tailoring and Claude study projects.
- Gmail-based interview detection feeding the calendars.
- The Electron
WebContentsViewlive-run browser.
Related docs¶
- Plain-language version: ../plain/overview.md
- Docs index: ../README.md
- Architecture deep dive: ../ARCHITECTURE.md