job-forge 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/.codex/config.toml +8 -0
  2. package/.cursor/mcp.json +21 -0
  3. package/.cursor/rules/main.mdc +519 -0
  4. package/.mcp.json +21 -0
  5. package/.opencode/agents/general-free.md +85 -0
  6. package/.opencode/agents/general-paid.md +39 -0
  7. package/.opencode/agents/glm-minimal.md +50 -0
  8. package/.opencode/skills/job-forge.md +185 -0
  9. package/AGENTS.md +514 -0
  10. package/CLAUDE.md +514 -0
  11. package/LICENSE +21 -0
  12. package/README.md +195 -0
  13. package/batch/README.md +60 -0
  14. package/batch/batch-prompt.md +399 -0
  15. package/batch/batch-runner.sh +673 -0
  16. package/bin/create-job-forge.mjs +375 -0
  17. package/bin/job-forge.mjs +120 -0
  18. package/bin/sync.mjs +141 -0
  19. package/config/profile.example.yml +67 -0
  20. package/cv-sync-check.mjs +128 -0
  21. package/dedup-tracker.mjs +201 -0
  22. package/docs/ARCHITECTURE.md +220 -0
  23. package/docs/CUSTOMIZATION.md +101 -0
  24. package/docs/MODEL-ROUTING.md +195 -0
  25. package/docs/README.md +54 -0
  26. package/docs/SETUP.md +186 -0
  27. package/docs/demo.gif +0 -0
  28. package/fonts/dm-sans-latin-ext.woff2 +0 -0
  29. package/fonts/dm-sans-latin.woff2 +0 -0
  30. package/fonts/space-grotesk-latin-ext.woff2 +0 -0
  31. package/fonts/space-grotesk-latin.woff2 +0 -0
  32. package/generate-pdf.mjs +168 -0
  33. package/iso/agents/general-free.md +90 -0
  34. package/iso/agents/general-paid.md +44 -0
  35. package/iso/agents/glm-minimal.md +55 -0
  36. package/iso/commands/job-forge.md +188 -0
  37. package/iso/config.json +7 -0
  38. package/iso/instructions.md +514 -0
  39. package/iso/mcp.json +15 -0
  40. package/merge-tracker.mjs +377 -0
  41. package/modes/README.md +30 -0
  42. package/modes/_shared-calibration.md +26 -0
  43. package/modes/_shared.md +272 -0
  44. package/modes/apply.md +257 -0
  45. package/modes/auto-pipeline.md +70 -0
  46. package/modes/batch.md +110 -0
  47. package/modes/compare.md +23 -0
  48. package/modes/contact.md +82 -0
  49. package/modes/deep.md +99 -0
  50. package/modes/followup.md +68 -0
  51. package/modes/negotiation.md +146 -0
  52. package/modes/offer.md +199 -0
  53. package/modes/pdf.md +121 -0
  54. package/modes/pipeline.md +83 -0
  55. package/modes/project.md +30 -0
  56. package/modes/rejection.md +92 -0
  57. package/modes/scan.md +185 -0
  58. package/modes/tracker.md +31 -0
  59. package/modes/training.md +27 -0
  60. package/normalize-statuses.mjs +152 -0
  61. package/opencode.json +28 -0
  62. package/package.json +78 -0
  63. package/scripts/add-tags.mjs +894 -0
  64. package/scripts/cursor-agent-loop.sh +211 -0
  65. package/scripts/cursor-agent-stream-format.py +134 -0
  66. package/scripts/next-num.mjs +33 -0
  67. package/scripts/release/check-source.mjs +37 -0
  68. package/scripts/render-report-header.mjs +78 -0
  69. package/scripts/session-report.mjs +129 -0
  70. package/scripts/slugify.mjs +27 -0
  71. package/scripts/today.mjs +20 -0
  72. package/scripts/token-usage-report.mjs +315 -0
  73. package/scripts/tracker-line.mjs +67 -0
  74. package/scripts/verify-greenhouse-urls.mjs +195 -0
  75. package/templates/cv-template.html +395 -0
  76. package/templates/portals.example.yml +3140 -0
  77. package/templates/states.yml +62 -0
  78. package/tracker-lib.mjs +257 -0
  79. package/verify-pipeline.mjs +267 -0
package/modes/batch.md ADDED
@@ -0,0 +1,110 @@
1
+ # Mode: batch — Bulk Offer Processing
2
+
3
+ Two usage modes: **conductor** (navigates portals in real time via Geometra MCP) or **standalone** (script for already-collected URLs).
4
+
5
+ ## Apply The Session-Length Rule
6
+
7
+ **Never run `batch` as one long interactive session.** Each offer gets its own `opencode run` worker via `batch-runner.sh` — that's the whole point of the architecture. Workers have clean ~200K-token contexts and exit after producing one report + PDF + tracker line, so prompt caching stays healthy.
8
+
9
+ If you find yourself doing `geometra_fill_form` or `geometra_page_model` for the Nth time in the *same* session, stop and delegate. See "Session Hygiene" in `.opencode/skills/job-forge.md` for the full rationale (cache-bust behavior with repeated Geometra tool calls).
10
+
11
+ ## Use This Architecture
12
+
13
+ ```
14
+ opencode Conductor (opencode --dangerously-skip-permissions)
15
+
16
+ │ Geometra MCP: navigates portals (logged-in sessions)
17
+ │ Reads structured page model — the user sees everything in real time
18
+
19
+ ├─ Offer 1: reads JD from DOM + URL
20
+ │ └─► opencode run worker → report .md + PDF + tracker-line
21
+
22
+ ├─ Offer 2: click next, reads JD + URL
23
+ │ └─► opencode run worker → report .md + PDF + tracker-line
24
+
25
+ └─ End: merge tracker-additions → data/applications/ + summary
26
+ ```
27
+
28
+ Each worker is a child `opencode run` with a clean 200K token context. The conductor only orchestrates.
29
+
30
+ ## Read These Files
31
+
32
+ ```
33
+ batch/
34
+ batch-input.tsv # URLs (from conductor or manual)
35
+ batch-state.tsv # Progress (auto-generated, gitignored)
36
+ batch-runner.sh # Standalone orchestrator script
37
+ batch-prompt.md # Prompt template for workers
38
+ logs/ # One log per offer (gitignored)
39
+ tracker-additions/ # Tracker lines (gitignored)
40
+ ```
41
+
42
+ ## Run Mode A Conductor --chrome
43
+
44
+ 1. **Read state**: `batch/batch-state.tsv` → know what has already been processed
45
+ 2. **Navigate portal**: Chrome → search URL
46
+ 3. **Extract URLs**: Read results DOM → extract URL list → append to `batch-input.tsv`
47
+ 4. **For each pending URL**:
48
+ a. Chrome: click on the offer → read JD text from DOM
49
+ b. Save JD to `/tmp/batch-jd-{id}.txt`
50
+ c. Calculate next sequential REPORT_NUM
51
+ d. Execute via Bash:
52
+ ```bash
53
+ opencode run --dangerously-skip-permissions \
54
+ --file batch/batch-prompt.md \
55
+ "Process this offer. URL: {url}. JD: /tmp/batch-jd-{id}.txt. Report: {num}. ID: {id}"
56
+ ```
57
+ e. Update `batch-state.tsv` (completed/failed + score + report_num)
58
+ f. Log to `logs/{report_num}-{id}.log`
59
+ g. Chrome: go back → next offer
60
+ 5. **Pagination**: If no more offers → click "Next" → repeat
61
+ 6. **End**: Merge `tracker-additions/` → `data/applications/` (via `merge-tracker.mjs`) + summary
62
+
63
+ ## Run Mode B Standalone Script
64
+
65
+ ```bash
66
+ batch/batch-runner.sh [OPTIONS]
67
+ ```
68
+
69
+ Options:
70
+ - `--dry-run` — list pending without executing
71
+ - `--retry-failed` — only retry failed ones
72
+ - `--start-from N` — start from ID N
73
+ - `--parallel N` — N workers in parallel
74
+ - `--max-retries N` — attempts per offer (default: 2)
75
+
76
+ ## Read batch-state.tsv Format
77
+
78
+ ```
79
+ id url status started_at completed_at report_num score error retries
80
+ 1 https://... completed 2026-... 2026-... 002 4.2 - 0
81
+ 2 https://... failed 2026-... 2026-... - - Error msg 1
82
+ 3 https://... pending - - - - - 0
83
+ ```
84
+
85
+ ## Use Resumability
86
+
87
+ - If it dies → re-run → reads `batch-state.tsv` → skips completed
88
+ - Lock file (`batch-runner.pid`) prevents double execution
89
+ - Each worker is independent: failure on offer #47 does not affect the rest
90
+
91
+ ## Run Workers (opencode run)
92
+
93
+ Each worker receives `batch-prompt.md` as system prompt. It is self-contained.
94
+
95
+ The worker produces:
96
+ 1. Report `.md` in `reports/`
97
+ 2. PDF in `output/`
98
+ 3. Tracker line in `batch/tracker-additions/{id}.tsv`
99
+ 4. JSON result via stdout
100
+
101
+ ## Apply Error Handling
102
+
103
+ | Error | Recovery |
104
+ |-------|----------|
105
+ | URL inaccessible | Worker fails → conductor marks `failed`, moves on |
106
+ | JD behind login | Conductor tries to read DOM. If it fails → `failed` |
107
+ | Portal changes layout | Conductor reasons about HTML, adapts |
108
+ | Worker crashes | Conductor marks `failed`, moves on. Retry with `--retry-failed` |
109
+ | Conductor dies | Re-run → reads state → skips completed |
110
+ | PDF fails | Report .md is saved. PDF remains pending |
@@ -0,0 +1,23 @@
1
+ # Mode: compare — Multi-Offer Comparison
2
+
3
+ **Uses the Canonical Scoring Model from `modes/_shared.md`** — the same 10 weighted dimensions used in `offer`, `auto-pipeline`, and `batch` evaluations. This means scores from prior evaluations are directly comparable. No need to re-score unless the user explicitly requests it.
4
+
5
+ ## Run This Workflow
6
+
7
+ 1. **Gather offers**: Ask the user for offers if not in context. Can be text, URLs, or references to already-evaluated offers in the tracker.
8
+ 2. **Score each offer**: If an offer was already evaluated (has a report in `reports/`), read the existing per-dimension scores from the report. If not yet evaluated, score using the full Canonical Scoring Model.
9
+ 3. **Build comparison matrix**: Show all 10 dimensions side-by-side across offers, with the weighted total.
10
+ 4. **Rank and recommend**: Final ranking with recommendation. Highlight where offers differ most (the dimensions that actually discriminate). Include time-to-offer as a tiebreaker when scores are close (within 0.3).
11
+
12
+ ## Output format
13
+
14
+ ```
15
+ | Dimension (weight) | Offer A | Offer B | Offer C |
16
+ |---------------------|---------|---------|---------|
17
+ | North Star (25%) | X/5 | X/5 | X/5 |
18
+ | CV match (15%) | ... | ... | ... |
19
+ | ... | ... | ... | ... |
20
+ | **Weighted total** | **X.X** | **X.X** | **X.X** |
21
+ ```
22
+
23
+ Then: narrative recommendation explaining which offer to prioritize and why, considering both score and practical factors (timeline, negotiation leverage, start-date flexibility, competing offer stages).
@@ -0,0 +1,82 @@
1
+ # Mode: contact — LinkedIn Power Move
2
+
3
+ ## Step 0 — Load evaluation context
4
+
5
+ Check for an existing evaluation report before generating any message.
6
+
7
+ 1. Identify the company + role (from user input, current conversation, or most recent evaluation).
8
+ 2. Search `reports/` for a matching report (Grep case-insensitive by company name).
9
+ 3. Read any matching report and extract these fields (all block references below point into `modes/offer.md`).
10
+ <!-- isolint-disable-next-line undefined-step-reference -->
11
+ - **Archetype** detected (Step 0 of evaluation).
12
+ <!-- isolint-disable-next-line undefined-step-reference -->
13
+ - **Top 3 proof points** from Block B (the JD requirements where CV match was strongest).
14
+ <!-- isolint-disable-next-line undefined-step-reference -->
15
+ - **Score** and key gaps from Block B.
16
+ <!-- isolint-disable-next-line undefined-step-reference -->
17
+ - **STAR stories** from Block F that are most relevant.
18
+ <!-- isolint-disable-next-line undefined-step-reference -->
19
+ - **Case study** recommended in Block F.
20
+ 4. Also read all day files in `data/applications/` to check current status of this application.
21
+ 5. If NO report exists, inform the user and offer to run an evaluation first — or proceed with cv.md only.
22
+
23
+ The loaded evaluation context is what makes the outreach message specific instead of generic.
24
+
25
+ ## Step 1 — Identify Targets
26
+
27
+ Use WebSearch to find:
28
+
29
+ - Hiring manager of the team.
30
+ - Recruiter assigned to the role.
31
+ - 2-3 peers on the team (people in a similar role).
32
+
33
+ ## Step 2 — Select primary target
34
+
35
+ Choose the person who would most benefit from the candidate joining. Typically:
36
+
37
+ - For IC roles: the hiring manager or tech lead.
38
+ - For leadership roles: a peer or the person the role reports to.
39
+ - Avoid cold-messaging the recruiter first unless no other option — a warm intro from a team member is stronger.
40
+
41
+ ## Step 3 — Generate message
42
+
43
+ Generate the message directly from the evaluation report, not generic claims.
44
+
45
+ Framework (3 sentences, max 300 characters for LinkedIn connection request):
46
+
47
+ <!-- isolint-disable-next-line undefined-step-reference -->
48
+ - **Sentence 1 (Hook)**: Something specific about their company or current challenge with AI — NOT generic. If the report's Block A identified the domain and function, reference it.
49
+ <!-- isolint-disable-next-line undefined-step-reference -->
50
+ - **Sentence 2 (Proof)**: The single strongest proof point from Block B's top matches. Use the exact framing that scored highest against the JD. If article-digest.md has a quantified metric for this proof point, use it.
51
+ - **Sentence 3 (Proposal)**: Quick chat, no pressure — "Would love to chat about [specific topic from the JD] for 15 min".
52
+
53
+ **Archetype-adapted framing** from the report — use the single row matching the detected archetype:
54
+
55
+ | Archetype | Emphasize |
56
+ |-----------|-----------|
57
+ | FDE | fast delivery, client-facing results |
58
+ | SA | system design, integration wins |
59
+ | PM | product discovery, stakeholder outcomes |
60
+ | LLMOps | production metrics, evals, observability |
61
+ | Agentic | orchestration, reliability, HITL |
62
+ | Transformation | adoption, change management, org impact |
63
+
64
+ ## Step 4 — Generate Versions
65
+
66
+ Generate:
67
+ - EN (default)
68
+ <!-- isolint-disable-next-line undefined-step-reference -->
69
+ - **Follow-up variant**: A longer version (2-3 sentences) for LinkedIn InMail or email, where the 300-char limit doesn't apply. This version can include a second proof point and a link to the relevant case study from Block F.
70
+
71
+ ## Step 5 — List Alternative Targets
72
+
73
+ List 2-3 backup contacts with justification for why they're strong second choices.
74
+
75
+ ## Apply These Message Rules
76
+
77
+ - Max 300 characters for connection request version
78
+ - NO corporate-speak
79
+ - NO "I'm passionate about..."
80
+ - Something that makes them want to respond
81
+ - NEVER share phone number
82
+ - **Every claim must trace back to cv.md or article-digest.md** — no invented metrics
package/modes/deep.md ADDED
@@ -0,0 +1,99 @@
1
+ # Mode: deep — Deep Company Research
2
+
3
+ Structured company research that feeds directly into evaluations and interview prep.
4
+
5
+ ## Step 0 — Check for existing evaluation
6
+
7
+ Check `reports/` for an existing evaluation of this company+role before researching. If found, load the report — deep research MUST build on what the evaluation already identified (archetype, gaps, proof points), not start from scratch.
8
+
9
+ ## Scan These 6 Research Axes
10
+
11
+ ### Scan Axis 1: AI Strategy
12
+ - What products/features use AI/ML?
13
+ - What's their AI stack? (models, infra, tools)
14
+ - Engineering blog — what do they publish?
15
+ - Papers or talks on AI?
16
+
17
+ ### Scan Axis 2: Recent Moves (last 6 months)
18
+ - Relevant hires in AI/ML/product?
19
+ - Acquisitions or partnerships?
20
+ - Product launches or pivots?
21
+ - Funding rounds or leadership changes?
22
+
23
+ ### Scan Axis 3: Engineering Culture
24
+ - How do they ship? (deploy cadence, CI/CD)
25
+ - Mono-repo or multi-repo?
26
+ - Languages/frameworks?
27
+ - Remote-first or office-first?
28
+ - Glassdoor/Blind reviews on eng culture?
29
+
30
+ ### Scan Axis 4: Likely Challenges
31
+ - Scaling problems?
32
+ - Reliability, cost, latency challenges?
33
+ - Migrating anything? (infra, models, platforms)
34
+ - Pain points from reviews?
35
+
36
+ ### Scan Axis 5: Competitors And Differentiation
37
+ - Main competitors?
38
+ - Moat/differentiator?
39
+ - Positioning vs competition?
40
+
41
+ ### Scan Axis 6: Candidate Angle
42
+ cv.md is already in context via opencode.json:instructions — do NOT Read it again. Read profile.yml once if you need identity fields:
43
+ - What unique value does the candidate bring to this team?
44
+ - Which projects are most relevant?
45
+ - What story should they tell in the interview?
46
+
47
+ ## Feed Research Back Into Evaluation
48
+
49
+ **The feedback loop is the key step that makes deep research actionable.**
50
+
51
+ After completing the 6 axes, update the evaluation if one exists:
52
+
53
+ 1. **Score adjustments**: Deep research may reveal information that changes scoring dimensions.
54
+ - Company reputation (axis 3: culture) → dimension #7.
55
+ - Tech stack modernity (axis 1: AI strategy) → dimension #8.
56
+ - Speed to offer (axis 2: recent moves, hiring pace) → dimension #9.
57
+ - Cultural signals (axis 3: culture) → dimension #10.
58
+ - Growth trajectory (axis 2: funding, launches) → dimension #5.
59
+
60
+ 2. **Append a `## Deep Research` section to the existing report** with findings organized by axis. Include date of research so it can be refreshed.
61
+
62
+ <!-- isolint-disable-next-line undefined-step-reference -->
63
+ 3. **Update Block F (Interview Plan)**: The "likely challenges" (axis 4) directly inform STAR story selection — pick stories that address the company's actual problems, not generic ones.
64
+
65
+ 4. **Update contact targeting**: The "recent moves" (axis 2) often reveal who to reach out to (new hires, team leads mentioned in blog posts).
66
+
67
+ <!-- isolint-disable-next-line undefined-step-reference -->
68
+ If NO evaluation exists yet, save the research to `reports/deep-{company-slug}-{date}.md` so it's available when the user evaluates later. The evaluation mode MUST check for existing deep research before starting Block D (Comp & Demand).
69
+
70
+ ## Output Format
71
+
72
+ ```markdown
73
+ ## Deep Research: [Company] — [Role]
74
+ **Date:** YYYY-MM-DD
75
+ **Linked report:** #NNN (if exists)
76
+
77
+ ### 1. AI Strategy
78
+ (findings)
79
+
80
+ ### 2. Recent Moves
81
+ (findings)
82
+
83
+ ### 3. Engineering Culture
84
+ (findings)
85
+
86
+ ### 4. Likely Challenges
87
+ (findings)
88
+
89
+ ### 5. Competitors & Differentiation
90
+ (findings)
91
+
92
+ ### 6. Candidate Angle
93
+ (findings)
94
+
95
+ ### Score Impact
96
+ | Dimension | Before | After | Why |
97
+ |-----------|--------|-------|-----|
98
+ (only dimensions where research changed the score)
99
+ ```
@@ -0,0 +1,68 @@
1
+ # Mode: followup — Follow-Up Timing & Nudge System
2
+
3
+ Scans all day files in `data/applications/` for entries that need follow-up action based on their current state and how long they've been in that state.
4
+
5
+ **This mode is read-only on existing pipeline logic.** It reads the tracker and suggests actions — it never changes scores, reports, or pipeline behavior.
6
+
7
+ ## Timing Rules
8
+
9
+ | Current State | Days Since | Action |
10
+ |---------------|-----------|--------|
11
+ | Applied | 7-10 days | Nudge: LinkedIn outreach via `/job-forge contact` if not already Contacted |
12
+ | Applied | 14+ days | Flag as stale. Suggest: nudge or archive to Discarded |
13
+ | Contacted | 5-7 days | Follow-up message (shorter, reference first message) |
14
+ | Contacted | 14+ days | Flag as stale. Likely no response — move on |
15
+ | Responded | 5 days | If no next step scheduled, ask: "Did they propose a call?" |
16
+ | Interview | 1 day after | Send thank-you note (generate draft) |
17
+ | Interview | 7+ days no update | Nudge recruiter: "Following up on our conversation last week" |
18
+ | Evaluated | 14+ days | Stale evaluation. Offer may be closed — suggest verifying or archiving |
19
+
20
+ ## Run This Workflow
21
+
22
+ 1. **Read** all day files in `data/applications/` — parse all entries with dates.
23
+ 2. **Compute** days since the date column for each entry (using today's date).
24
+ 3. **Filter** to entries matching the Timing Rules table above.
25
+ 4. **Sort** by urgency (most overdue first).
26
+ 5. **Present** action list.
27
+
28
+ ```
29
+ ## Follow-Up Actions — {today's date}
30
+
31
+ ### Urgent (overdue)
32
+ - #045 Anthropic — AI Engineer | Applied 12 days ago → Nudge via LinkedIn
33
+ - #078 Datadog — Staff PM | Interviewed 8 days ago → Follow up with recruiter
34
+
35
+ ### Coming Up (next 3 days)
36
+ - #102 Stripe — AI Platform | Applied 6 days ago → Nudge window opens in 1 day
37
+
38
+ ### Stale (consider archiving)
39
+ - #023 OldCo — Senior Eng | Evaluated 21 days ago → Verify if still open or Discard
40
+ ```
41
+
42
+ ## Nudge Message Generation
43
+
44
+ When the user selects an entry to nudge:
45
+
46
+ 1. Read the existing report from `reports/`.
47
+ 2. Use the **contact** mode logic to generate a follow-up message (not a first outreach).
48
+ 3. Follow-up messages are shorter and reference the application.
49
+ - "I applied to [role] [N days] ago and wanted to follow up...".
50
+ <!-- isolint-disable-next-line undefined-step-reference -->
51
+ - Reference one specific proof point from Block B (see `modes/offer.md`).
52
+ - Keep it under 200 characters for LinkedIn.
53
+
54
+ ## Generate Thank-You Notes
55
+
56
+ After interviews, generate a thank-you note.
57
+ <!-- isolint-disable-next-line undefined-step-reference -->
58
+ 1. Read the report + Block F STAR stories (Block F lives in `modes/offer.md`).
59
+ 2. Reference something specific discussed in the interview (ask the user what stood out).
60
+ 3. Reinforce one proof point.
61
+ 4. 3-4 sentences max, send within 24 hours.
62
+
63
+ ## Automate This Mode
64
+
65
+ The followup mode works well with `/loop` or `/schedule`:
66
+
67
+ - Run `/job-forge followup` every 2-3 days to catch nudge windows.
68
+ - Suggest this to the user after their first batch of applications.
@@ -0,0 +1,146 @@
1
+ # Mode: negotiation — Offer Negotiation
2
+
3
+ Structured mode for when the user receives an actual job offer. Completely separate from `offer` (which evaluates job postings).
4
+
5
+ **This mode is standalone.** It reads existing data (reports, tracker, profile.yml) but creates its own output. No changes to existing pipeline behavior.
6
+
7
+ ## Use These Trigger Conditions
8
+
9
+ - User says "I got an offer from [company]".
10
+ - User updates a tracker entry to Offer.
11
+ - User runs `/job-forge negotiation`.
12
+
13
+ ## Step 1 — Capture The Offer
14
+
15
+ Ask the user for the offer details (or read from a document/screenshot):
16
+
17
+ | Field | Example | Notes |
18
+ |-------|---------|-------|
19
+ | Base salary | $180,000 | Annual |
20
+ | Equity/RSUs | $50,000/yr (4yr vest) | Total grant ÷ vesting period |
21
+ | Signing bonus | $20,000 | One-time |
22
+ | Annual bonus | 15% target | As % of base |
23
+ | Benefits | Standard | Healthcare, 401k match, dental, vision |
24
+ | PTO | Unlimited / 20 days | |
25
+ | Remote policy | Full remote | |
26
+ | Start date | YYYY-MM-DD | |
27
+ | Deadline to respond | YYYY-MM-DD | Critical — drives urgency |
28
+ | Level/title | Senior Engineer | |
29
+ | Other perks | $5K learning budget | Anything else |
30
+
31
+ ## Step 2 — Total Comp Calculation
32
+
33
+ Compute total annual comp:
34
+
35
+ ```
36
+ Base: $180,000
37
+ Equity (annual): $50,000 (total grant ÷ vesting years)
38
+ Bonus (target): $27,000 (base × bonus %)
39
+ Signing (annual): $10,000 (signing ÷ 2, amortized over first 2 years)
40
+ ───────────────────────────
41
+ Total Year 1: $267,000
42
+ Total Ongoing: $257,000 (without signing bonus amortization)
43
+ ```
44
+
45
+ **For equity at private companies:**
46
+ - Apply a discount: pre-IPO equity is illiquid. Common: 50-70% discount for early stage, 20-30% for late stage.
47
+ - Note the strike price if options (not RSUs). Compute current spread if possible.
48
+ - Flag if there's no secondary market.
49
+
50
+ ## Step 3 — Compare Against Target & Pipeline
51
+
52
+ Read from `config/profile.yml`:
53
+ - `compensation.target_range` — is the offer within, above, or below?
54
+ - `compensation.minimum` — is it above the walk-away number?
55
+
56
+ Read from all day files in `data/applications/`:
57
+ - Count other offers at Interview or Offer stage (these in-flight offers form your leverage).
58
+ - What scores did those have? (Higher-scored alternatives = stronger negotiating position.)
59
+
60
+ ```
61
+ ## Leverage Assessment
62
+
63
+ Active pipeline:
64
+ - Offer: [this one] + N others
65
+ - Interview stage: N companies
66
+ - Applied (awaiting): N companies
67
+
68
+ Leverage: [Strong/Moderate/Weak]
69
+ - Strong: 2+ competing offers or 3+ active interviews
70
+ - Moderate: 1 other offer or 2+ interviews
71
+ - Weak: this is the only active opportunity
72
+ ```
73
+
74
+ ## Step 4 — Build Counter-Offer Strategy
75
+
76
+ Build the strategy based on leverage and gap to target.
77
+
78
+ **If offer is below target range:**
79
+ ```
80
+ Recommended counter: [target midpoint + 10%]
81
+ Justification: "Based on market data for [role] at [level],
82
+ and given [competing offers / pipeline activity], I'm targeting [range]."
83
+ ```
84
+
85
+ **If offer is within target range:**
86
+ ```
87
+ Recommended counter: [target top end]
88
+ Justification: "I'm excited about this role. To make the decision
89
+ straightforward, [specific ask — e.g., bump equity, signing bonus, level]."
90
+ ```
91
+
92
+ **If offer is above target range:**
93
+ ```
94
+ Focus negotiation on non-comp factors: level/title, scope,
95
+ team choice, start date flexibility, remote policy.
96
+ ```
97
+
98
+ ### Generate Negotiation Scripts (adapted from _shared.md)
99
+
100
+ Generate 3 scripts ready for use:
101
+
102
+ 1. **Initial response** (buy time): "Thank you for the offer. I'm very interested and want to give this proper consideration. Can I have until [deadline or +3 days] to review the full package?"
103
+
104
+ <!-- isolint-disable-next-line undefined-step-reference -->
105
+ 2. **Counter-offer** when comp is below the target range: Specific to the gap. Reference market data from Block D (see `modes/offer.md`) when available. Never give a single number — always a range.
106
+
107
+ 3. **Acceptance** (when ready): Confirm all terms in writing. Ask for the offer letter with the agreed terms before giving verbal acceptance.
108
+
109
+ ### Skip These Negotiation Anti-Patterns
110
+ - Never counter before understanding the full package
111
+ - Never reveal your current comp or other offer amounts
112
+ - Never give an ultimatum unless you're prepared to walk
113
+ - Never negotiate via email if you can do it via call (nuance matters)
114
+
115
+ ## Step 5 — Build Decision Framework
116
+
117
+ Build a decision matrix when the user has multiple offers, using the same scoring dimensions from `_shared.md`.
118
+
119
+ ```
120
+ | Dimension | Offer A | Offer B |
121
+ |-----------|---------|---------|
122
+ | Total comp (Year 1) | $267K | $245K |
123
+ | Equity upside | Moderate (Series C) | High (Series A) |
124
+ | Role match (from eval) | 4.5/5 | 3.8/5 |
125
+ | Growth trajectory | Clear path to Staff | Flat org |
126
+ | Remote quality | Full remote | Hybrid 2x/week |
127
+ | Gut feeling | High | Medium |
128
+ ```
129
+
130
+ Include a "gut feeling" row — quantitative analysis doesn't capture everything.
131
+
132
+ ## Step 6 — Save And Update Tracker
133
+
134
+ Update the tracker entry to reflect the offer.
135
+
136
+ 1. Update the entry in its day file under `data/applications/YYYY-MM-DD.md`: status → `Offer`, notes → comp summary.
137
+ 2. Append `## Offer Details` section to the existing report with full comp breakdown and negotiation outcome.
138
+ 3. If the user accepts: status → a new note like `Accepted YYYY-MM-DD`.
139
+
140
+ ## Manage The Deadline
141
+
142
+ When the user has a response deadline, run these checks in order.
143
+
144
+ 1. Calculate business days remaining.
145
+ 2. If < 3 business days remain AND other interviews are active, suggest: "Ask [company] for an extension — 'I want to make a thoughtful decision and have a final interview scheduled this week. Could we extend to [date]?'"
146
+ 3. If the deadline is firm and leverage is weak, help the user decide with what they have.