@timurtekb/tekjobs 0.24.1
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.
- package/CHANGELOG.md +131 -0
- package/LICENSE +21 -0
- package/README.md +145 -0
- package/app/README.md +22 -0
- package/app/dist/assets/index-DzGgV6NF.css +1 -0
- package/app/dist/assets/index-QteuouKv.js +77 -0
- package/app/dist/favicon.svg +1 -0
- package/app/dist/index.html +16 -0
- package/app/server/cover-letter.mjs +243 -0
- package/app/server/index.mjs +113 -0
- package/app/server/mail-check.mjs +234 -0
- package/app/server/mcp.mjs +129 -0
- package/app/server/people.mjs +164 -0
- package/app/server/store.mjs +1131 -0
- package/app/server/tailored-resume.mjs +213 -0
- package/cli.mjs +205 -0
- package/package.json +74 -0
- package/run.cmd +10 -0
- package/run.mjs +201 -0
- package/run.sh +12 -0
- package/samples/vault/Jobs/Basalt Systems - Senior Design Engineer, Growth (1010).md +56 -0
- package/samples/vault/Jobs/Brightline Studio - Design Engineer (1005).md +51 -0
- package/samples/vault/Jobs/Copperleaf - Principal Design Engineer (1006).md +53 -0
- package/samples/vault/Jobs/Example Studio - Senior Design Engineer (1013).md +52 -0
- package/samples/vault/Jobs/Fjord Analytics - Staff Frontend Engineer, Platform (1003).md +58 -0
- package/samples/vault/Jobs/Halcyon Robotics - Senior UX Engineer (1004).md +52 -0
- package/samples/vault/Jobs/Lumen Health - Design Systems Engineer (1002).md +51 -0
- package/samples/vault/Jobs/Meridian Pay - Staff Design Engineer (1009).md +67 -0
- package/samples/vault/Jobs/Northwind Labs - Staff Design Engineer, Design Systems (1000).md +57 -0
- package/samples/vault/Jobs/Orbital Software - Senior Design Engineer (1001).md +52 -0
- package/samples/vault/Jobs/Quill & Co - Design Engineer, Editor (1008).md +55 -0
- package/samples/vault/Jobs/Signalfire Design - Design Engineer (1012).md +53 -0
- package/samples/vault/Jobs/Tessellate - Senior Frontend Engineer, Design Systems (1007).md +52 -0
- package/samples/vault/Jobs/Verdant - UX Engineer (1011).md +51 -0
- package/samples/vault/Logs/2026-09-16.md +13 -0
- package/samples/vault/Logs/2026-09-23.md +13 -0
- package/samples/vault/People/Lee Example (Fjord Analytics).md +22 -0
- package/samples/vault/People/Priya Example (Meridian Pay).md +23 -0
- package/samples/vault/People/Sam Example (Northwind Labs).md +20 -0
- package/samples/vault/Profile/Positioning.md +16 -0
- package/samples/vault/Profile/Profile.md +41 -0
- package/samples/vault/Profile/Resume.md +32 -0
- package/samples/vault/Profile/Voice.md +15 -0
- package/samples/vault/README.md +11 -0
- package/samples/vault/Targets/Companies.md +318 -0
- package/samples/vault/Targets/Search Criteria.md +234 -0
- package/samples/vault/_Home.md +54 -0
- package/scraper/config.mjs +122 -0
- package/scraper/health.mjs +50 -0
- package/scraper/import-link.mjs +281 -0
- package/scraper/profile.mjs +186 -0
- package/scraper/rescore.mjs +236 -0
- package/scraper/resume-sync.mjs +224 -0
- package/scraper/resume.mjs +65 -0
- package/scraper/score.mjs +114 -0
- package/scraper/sources-email.mjs +242 -0
- package/scraper/sources-extra.mjs +558 -0
- package/scraper/sources-sites.mjs +280 -0
- package/scraper/sources.mjs +264 -0
- package/scraper/starter/companies-table.md +306 -0
- package/scraper/starter/criteria.json +56 -0
- package/scraper/starter/profile.md +38 -0
- package/scraper/vault.mjs +212 -0
package/run.mjs
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// TekJobs runner. Usage: node run.mjs [--dry] [--min N] [--floor N] [--only slug] [--retry-failed] [--criteria <preset name | file>] [--no-remoteok] [--no-hn] [--check-slugs]
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { ensureDirs, loadCriteria, loadCompanies, writeCompanyStatuses, criteriaPresetFile, P } from './scraper/config.mjs';
|
|
6
|
+
import { fetchCompany, fetchRemoteOK, fetchHNWhoIsHiring, htmlToText } from './scraper/sources.mjs';
|
|
7
|
+
import { scoreJob, parseSalary } from './scraper/score.mjs';
|
|
8
|
+
import { loadSeen, saveSeen, writeJobNote, markClosedListings, appendLog, writeDashboard, readFrontmatter } from './scraper/vault.mjs';
|
|
9
|
+
import { weightsFingerprint } from './scraper/rescore.mjs';
|
|
10
|
+
import { loadHealth, saveHealth, record, healthState } from './scraper/health.mjs';
|
|
11
|
+
|
|
12
|
+
const args = process.argv.slice(2);
|
|
13
|
+
const flag = (f) => args.includes(f);
|
|
14
|
+
const opt = (f) => { const i = args.indexOf(f); return i >= 0 ? args[i + 1] : undefined; };
|
|
15
|
+
const DRY = flag('--dry');
|
|
16
|
+
const ONLY = opt('--only');
|
|
17
|
+
// Only the boards whose latest attempt failed, and no feeds: the Sources page's Retry button.
|
|
18
|
+
const RETRY = flag('--retry-failed');
|
|
19
|
+
|
|
20
|
+
async function pool(items, n, fn) {
|
|
21
|
+
const out = new Array(items.length);
|
|
22
|
+
let i = 0;
|
|
23
|
+
await Promise.all(Array.from({ length: n }, async () => {
|
|
24
|
+
while (i < items.length) { const k = i++; out[k] = await fn(items[k], k); }
|
|
25
|
+
}));
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const t0 = Date.now();
|
|
30
|
+
ensureDirs();
|
|
31
|
+
// Which criteria this run scores with: the active note, or a named preset / any criteria file for this run only.
|
|
32
|
+
let criteriaFile = P.criteria, criteriaName = 'Search Criteria';
|
|
33
|
+
if (opt('--criteria')) {
|
|
34
|
+
const want = opt('--criteria');
|
|
35
|
+
criteriaFile = fs.existsSync(want) ? path.resolve(want) : criteriaPresetFile(want);
|
|
36
|
+
if (!fs.existsSync(criteriaFile)) { console.error(`No criteria preset or file "${want}" (looked in ${P.criteriaDir}).`); process.exit(1); }
|
|
37
|
+
criteriaName = path.basename(criteriaFile, '.md');
|
|
38
|
+
}
|
|
39
|
+
const criteria = loadCriteria(criteriaFile);
|
|
40
|
+
const criteriaLabel = `${criteriaName} (${weightsFingerprint(criteria)})`;
|
|
41
|
+
if (opt('--min')) criteria.minScore = Number(opt('--min'));
|
|
42
|
+
if (opt('--floor')) { criteria.salary = { ...(criteria.salary || {}), minAnnual: Number(opt('--floor')) }; console.log(` pay floor overridden: $${criteria.salary.minAnnual}`); }
|
|
43
|
+
let companies = loadCompanies();
|
|
44
|
+
if (ONLY) companies = companies.filter((c) => c.slug === ONLY || c.name.toLowerCase() === ONLY.toLowerCase());
|
|
45
|
+
const health = loadHealth();
|
|
46
|
+
if (RETRY) {
|
|
47
|
+
companies = companies.filter((c) => healthState(health.boards[`${c.ats}:${c.slug}`], c.status) === 'failed');
|
|
48
|
+
if (companies.length === 0) { console.log('No failed boards to retry.'); process.exit(0); }
|
|
49
|
+
}
|
|
50
|
+
console.log(`TekJobs run ${new Date().toISOString()}${DRY ? ' (dry)' : ''} — ${companies.length} companies, minScore ${criteria.minScore}, criteria ${criteriaLabel}`);
|
|
51
|
+
|
|
52
|
+
// 1. Fetch company boards
|
|
53
|
+
const statusBySlug = {};
|
|
54
|
+
const liveIdsByCompanyKey = {};
|
|
55
|
+
const failed = [];
|
|
56
|
+
// Workday boards are slow (search + per-job detail); run them last so the fast boards finish first.
|
|
57
|
+
companies.sort((a, b) => (a.ats === 'workday') - (b.ats === 'workday'));
|
|
58
|
+
const results = await pool(companies, 8, async (c) => {
|
|
59
|
+
const key = `${c.ats}:${c.slug}`;
|
|
60
|
+
const r = await fetchCompany(c, criteria);
|
|
61
|
+
health.boards[key] = record(health.boards[key], r);
|
|
62
|
+
if (!r.ok) {
|
|
63
|
+
statusBySlug[key] = `bad-slug (${r.error})`;
|
|
64
|
+
failed.push({ ...c, error: r.error });
|
|
65
|
+
process.stdout.write(` x ${c.name} [${key}] ${r.error}\n`);
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
statusBySlug[key] = r.emptyBoard ? 'ok · 0 jobs (slug?)' : r.scanned != null ? `ok · ${r.jobs.length} of ${r.scanned} searched` : `ok · ${r.jobs.length}`;
|
|
69
|
+
liveIdsByCompanyKey[key] = new Set(r.jobs.map((j) => j.id));
|
|
70
|
+
for (const j of r.jobs) { j.companyKey = key; j.tier = c.tier; }
|
|
71
|
+
return r.jobs;
|
|
72
|
+
});
|
|
73
|
+
let jobs = results.flat();
|
|
74
|
+
|
|
75
|
+
// 2. Open sources (no slug). RemoteOK and HN are always on unless flagged; the rest toggle via criteria.openSources.
|
|
76
|
+
const extras = [];
|
|
77
|
+
const open = criteria.openSources || {};
|
|
78
|
+
if (!ONLY && !RETRY) {
|
|
79
|
+
const { OPEN_SOURCES } = await import('./scraper/sources-extra.mjs');
|
|
80
|
+
const tasks = [];
|
|
81
|
+
if (!flag('--no-remoteok') && open.remoteok !== false) tasks.push(['RemoteOK', fetchRemoteOK]);
|
|
82
|
+
if (!flag('--no-hn') && open.hn !== false) tasks.push(['HN Who is hiring', fetchHNWhoIsHiring]);
|
|
83
|
+
// A source marked defaultOn (TekJobs' own postings) runs unless the criteria say `false`; the rest are opt-in.
|
|
84
|
+
for (const [key, def] of Object.entries(OPEN_SOURCES)) if (def.defaultOn ? open[key] !== false : open[key]) tasks.push([def.label, def.fn]);
|
|
85
|
+
// Open-source fetchers are handed the criteria. Most ignore it; the ones that read the profile (Adzuna
|
|
86
|
+
// searches for its own titleTerms) need it, and the rest destructure their own options, so an extra
|
|
87
|
+
// argument is harmless.
|
|
88
|
+
// The inbox folder is a path, not a preference, so it is passed alongside the criteria rather than
|
|
89
|
+
// written into a file the person edits by hand.
|
|
90
|
+
const withPaths = { ...criteria, _inbox: P.inbox };
|
|
91
|
+
const results = await pool(tasks, 4, async ([label, fn]) => [label, await fn(withPaths)]);
|
|
92
|
+
for (const [label, r] of results) {
|
|
93
|
+
extras.push([label, r]);
|
|
94
|
+
health.feeds[label] = record(health.feeds[label], r);
|
|
95
|
+
if (r.ok) { for (const j of r.jobs) j.companyKey = `open:${label}`; jobs.push(...r.jobs); liveIdsByCompanyKey[`open:${label}`] = new Set(r.jobs.map((j) => j.id)); }
|
|
96
|
+
else console.log(` x ${label} ${r.error}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (flag('--check-slugs')) {
|
|
100
|
+
console.table(Object.entries(statusBySlug).map(([k, v]) => ({ board: k, status: v })));
|
|
101
|
+
if (!DRY) writeCompanyStatuses(statusBySlug);
|
|
102
|
+
process.exit(0);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 2b. Dedupe. The same opening shows up per-location on some boards (Rippling, Greenhouse multi-city) and again on
|
|
106
|
+
// aggregators. Key on company + title; keep the ATS copy over the aggregator copy; merge locations of the rest.
|
|
107
|
+
// Sources whose copy of a posting is thinner than the ATS's, and so loses when both are present: the
|
|
108
|
+
// aggregators, Adzuna (snippet descriptions) and anything imported from an alert email (a title and
|
|
109
|
+
// little else). USAJOBS is not one of them — it is the authority for the jobs it carries.
|
|
110
|
+
const AGG = new Set(['themuse', 'remotive', 'himalayas', 'jobicy', 'workingnomads', 'arbeitnow', 'wwr', 'remoteok', 'hn', 'adzuna', 'wellfound', 'builtin']);
|
|
111
|
+
const isThin = (j) => AGG.has(j.source) || String(j.source).startsWith('email:');
|
|
112
|
+
{
|
|
113
|
+
const norm = (s) => (s || '').toLowerCase().replace(/\(.*?\)|\[.*?\]/g, '').replace(/[^a-z0-9]+/g, ' ').trim();
|
|
114
|
+
const byKey = new Map();
|
|
115
|
+
const ordered = [...jobs].sort((a, b) => (isThin(a) - isThin(b)));
|
|
116
|
+
for (const j of ordered) {
|
|
117
|
+
const key = `${norm(j.company)}|${norm(j.title)}`;
|
|
118
|
+
const keep = byKey.get(key);
|
|
119
|
+
if (!keep) { byKey.set(key, j); continue; }
|
|
120
|
+
if (keep.source === j.source && j.location && !keep.location.includes(j.location)) keep.location = `${keep.location}; ${j.location}`.slice(0, 300);
|
|
121
|
+
if (!keep.salary && j.salary) { keep.salary = j.salary; }
|
|
122
|
+
if (!keep.remote && j.remote) keep.remote = true;
|
|
123
|
+
keep.dupes = (keep.dupes || 0) + 1;
|
|
124
|
+
// Some boards repeat the same posting id once per location; never drop the id the kept note points at.
|
|
125
|
+
if (j.id !== keep.id && j.companyKey && liveIdsByCompanyKey[j.companyKey]) liveIdsByCompanyKey[j.companyKey].delete(j.id);
|
|
126
|
+
}
|
|
127
|
+
const before = jobs.length;
|
|
128
|
+
jobs = [...byKey.values()];
|
|
129
|
+
console.log(` dedupe: ${before} → ${jobs.length}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 3. Score
|
|
133
|
+
for (const j of jobs) {
|
|
134
|
+
j.descriptionText = htmlToText(j.descriptionHtml || '');
|
|
135
|
+
const range = parseSalary(`${j.salary || ''}\n${j.descriptionText}`);
|
|
136
|
+
if (range) { j.salaryMin = range.min; j.salaryMax = range.max; if (!j.salary) j.salary = `$${Math.round(range.min / 1000)}k–$${Math.round(range.max / 1000)}k`; }
|
|
137
|
+
j.scored = scoreJob(j, criteria);
|
|
138
|
+
}
|
|
139
|
+
const scored = jobs.filter((j) => !j.scored.excluded).sort((a, b) => b.scored.score - a.scored.score);
|
|
140
|
+
const matches = scored.filter((j) => j.scored.score >= criteria.minScore);
|
|
141
|
+
|
|
142
|
+
// 4. Write new matches to the vault
|
|
143
|
+
const seen = loadSeen();
|
|
144
|
+
const fresh = matches.filter((j) => !seen[j.id]);
|
|
145
|
+
const written = [];
|
|
146
|
+
for (const j of fresh) {
|
|
147
|
+
if (DRY) { written.push({ job: j, file: '(dry)' }); continue; }
|
|
148
|
+
const file = writeJobNote(j, j.scored, criteria);
|
|
149
|
+
seen[j.id] = { path: file, firstSeen: new Date().toISOString(), company: j.company, title: j.title, companyKey: j.companyKey || j.source, score: j.scored.score };
|
|
150
|
+
written.push({ job: j, file });
|
|
151
|
+
}
|
|
152
|
+
let closed = [];
|
|
153
|
+
if (!DRY) {
|
|
154
|
+
closed = markClosedListings(liveIdsByCompanyKey, seen);
|
|
155
|
+
saveSeen(seen);
|
|
156
|
+
writeCompanyStatuses(statusBySlug);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Source health is diagnostic, so it is written on dry runs too.
|
|
160
|
+
saveHealth(health);
|
|
161
|
+
|
|
162
|
+
// 5. Debug snapshot: everything scored, so near-misses can be reviewed and criteria tuned
|
|
163
|
+
fs.writeFileSync(P.lastRun, JSON.stringify({
|
|
164
|
+
when: new Date().toISOString(), minScore: criteria.minScore, failed,
|
|
165
|
+
jobs: scored.slice(0, 800).map((j) => ({ score: j.scored.score, company: j.company, title: j.title, location: j.location, remote: !!j.remote, salary: j.salary || '', salaryMin: j.salaryMin || null, salaryMax: j.salaryMax || null, payBand: j.scored.payBand, posted: j.posted, url: j.url, source: j.source, reasons: j.scored.reasons, isNew: !seen[j.id] || fresh.includes(j) })),
|
|
166
|
+
}, null, 2));
|
|
167
|
+
|
|
168
|
+
// 6. Log + dashboard
|
|
169
|
+
const summary = {
|
|
170
|
+
when: new Date().toISOString().replace('T', ' ').slice(0, 16) + ' UTC',
|
|
171
|
+
companiesTotal: companies.length,
|
|
172
|
+
companiesOk: companies.length - failed.length,
|
|
173
|
+
companiesFailed: failed.length,
|
|
174
|
+
totalJobs: jobs.length,
|
|
175
|
+
newMatches: written.length,
|
|
176
|
+
};
|
|
177
|
+
const logLines = [
|
|
178
|
+
`## Run ${summary.when}${DRY ? ' (dry)' : ''}`,
|
|
179
|
+
`- Boards: ${summary.companiesOk}/${summary.companiesTotal} ok · postings scanned: ${summary.totalJobs} · scored ≥ ${criteria.minScore}: ${matches.length} · **new: ${written.length}** · closed: ${closed.length} · ${((Date.now() - t0) / 1000).toFixed(1)}s`,
|
|
180
|
+
`- Criteria: ${criteriaLabel}`,
|
|
181
|
+
// Who started it: the morning task (run.cmd / run.sh), the app, an agent over MCP, or someone at the CLI.
|
|
182
|
+
`- Via: ${process.env.TEKJOBS_RUN_VIA || 'cli'}`,
|
|
183
|
+
...extras.map(([n, r]) => `- ${n}: ${r.ok ? `${r.jobs.length} postings${r.thread ? ` (${r.thread})` : ''}` : `failed: ${r.error}`}`),
|
|
184
|
+
...(failed.length ? [`- Failed slugs: ${failed.map((f) => `${f.name} (${f.ats}:${f.slug} — ${f.error})`).join('; ')}`] : []),
|
|
185
|
+
...(written.length ? ['', '### New matches', ...written.map(({ job }) => `- **${job.scored.score}** ${job.company} — [${job.title}](${job.url}) · ${job.location || 'n/a'}`)] : []),
|
|
186
|
+
...(closed.length ? ['', '### Closed since last run', ...closed.map((c) => `- ${c.company} — ${c.title}`)] : []),
|
|
187
|
+
];
|
|
188
|
+
if (!DRY) {
|
|
189
|
+
appendLog(logLines);
|
|
190
|
+
writeDashboard(summary);
|
|
191
|
+
}
|
|
192
|
+
console.log(logLines.join('\n'));
|
|
193
|
+
// The top of the list, with where each one stands. Ranking ignores status on purpose (the score is about the
|
|
194
|
+
// posting), but a person reading the log should not be shown a job they already applied to as if it were news.
|
|
195
|
+
const statusOf = (j) => { const p = seen[j.id]?.path; if (!p) return 'new'; const fm = readFrontmatter(p); return fm?.status || 'new'; };
|
|
196
|
+
const top = scored.map((j) => ({ j, status: statusOf(j) }));
|
|
197
|
+
const stillOpen = top.filter((t) => !['applied', 'interviewing', 'offer', 'rejected', 'passed'].includes(t.status));
|
|
198
|
+
console.log(`\nTop 15 still open (not yet applied or passed):`);
|
|
199
|
+
for (const { j, status } of stillOpen.slice(0, 15)) console.log(` ${String(j.scored.score).padStart(4)} ${status === 'new' ? '' : `[${status}] `}${j.company} — ${j.title} [${j.location}]`);
|
|
200
|
+
const done = top.slice(0, 30).filter((t) => ['applied', 'interviewing', 'offer', 'passed', 'rejected'].includes(t.status));
|
|
201
|
+
if (done.length) console.log(` (${done.length} of the top 30 already ${done.map((t) => t.status).filter((s, i, a) => a.indexOf(s) === i).join('/')}: ${done.map((t) => t.j.company).join(', ')})`);
|
package/run.sh
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Daily TekJobs scan for cron and launchd: the scan, then the read-only mail pass. `tekjobs schedule` prints
|
|
3
|
+
# the one line that calls this at 07:30; run.cmd is the Windows twin.
|
|
4
|
+
cd "$(dirname "$0")" || exit 1
|
|
5
|
+
export TEKJOBS_RUN_VIA=schedule
|
|
6
|
+
mkdir -p data
|
|
7
|
+
{
|
|
8
|
+
echo "===== $(date) ====="
|
|
9
|
+
node run.mjs
|
|
10
|
+
echo "----- mail $(date) -----"
|
|
11
|
+
node cli.mjs mail
|
|
12
|
+
} >> data/runs.log 2>&1
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: job
|
|
3
|
+
status: rejected
|
|
4
|
+
listing: open
|
|
5
|
+
company: "Basalt Systems"
|
|
6
|
+
title: "Senior Design Engineer, Growth"
|
|
7
|
+
location: "Remote US"
|
|
8
|
+
remote: true
|
|
9
|
+
source: ashby
|
|
10
|
+
url: "https://jobs.example.com/basalt/senior-design-engineer-growth"
|
|
11
|
+
score: 114
|
|
12
|
+
weights: 3e895160
|
|
13
|
+
posted: "2026-08-29"
|
|
14
|
+
found: 2026-08-30
|
|
15
|
+
salary: "$180k–$220k"
|
|
16
|
+
salary_max: 220000
|
|
17
|
+
pay_band: floor
|
|
18
|
+
department: "Growth"
|
|
19
|
+
job_id: "as:basalt:1010"
|
|
20
|
+
tags: [job]
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# Senior Design Engineer, Growth @ Basalt Systems
|
|
24
|
+
|
|
25
|
+
**[Open posting](https://jobs.example.com/basalt/senior-design-engineer-growth)** · Remote US · Remote · score **114** · $180k–$220k
|
|
26
|
+
|
|
27
|
+
## Why it matched
|
|
28
|
+
- title +40: design engineer
|
|
29
|
+
- seniority +8 (senior)
|
|
30
|
+
- description +14: design system, react, typescript
|
|
31
|
+
- remote +15
|
|
32
|
+
- pay range tops out at $220k, at or above floor (+10, +2 for 20k above)
|
|
33
|
+
- posted 1d ago (+25)
|
|
34
|
+
|
|
35
|
+
## Status log
|
|
36
|
+
- 2026-08-30 — found by scraper (score 114). Set `status:` above to `reviewing`, `applying`, `applied`, `interviewing`, `offer`, `rejected`, or `passed`.
|
|
37
|
+
- 2026-09-01 — new → **reviewing** (via app)
|
|
38
|
+
- 2026-09-03 — reviewing → **applying** (via app)
|
|
39
|
+
- 2026-09-05 — applying → **applied** (via app)
|
|
40
|
+
- 2026-09-17 — applied → **rejected** (via app)
|
|
41
|
+
|
|
42
|
+
## Notes
|
|
43
|
+
- 2026-09-17 (app): Form rejection after twelve days. No reason given.
|
|
44
|
+
|
|
45
|
+
## Application
|
|
46
|
+
- **Narrative:**
|
|
47
|
+
- **Resume variant:**
|
|
48
|
+
- **Cover letter:**
|
|
49
|
+
- **Applied on:**
|
|
50
|
+
- **Contact / referral:**
|
|
51
|
+
- **Follow-up due:**
|
|
52
|
+
|
|
53
|
+
## Job description
|
|
54
|
+
> Source: ashby
|
|
55
|
+
|
|
56
|
+
Basalt Systems sells infrastructure monitoring. The growth team builds onboarding, pricing and the signup flow, and wants a design engineer who ships polished interface fast in React and TypeScript on our design system. Remote in the US.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: job
|
|
3
|
+
status: new
|
|
4
|
+
listing: open
|
|
5
|
+
company: "Brightline Studio"
|
|
6
|
+
title: "Design Engineer"
|
|
7
|
+
location: "Remote · United States"
|
|
8
|
+
remote: true
|
|
9
|
+
source: lever
|
|
10
|
+
url: "https://jobs.example.com/brightline/design-engineer"
|
|
11
|
+
score: 95
|
|
12
|
+
weights: 3e895160
|
|
13
|
+
posted: "2026-09-22"
|
|
14
|
+
found: 2026-09-22
|
|
15
|
+
salary: "$160k–$195k"
|
|
16
|
+
salary_max: 195000
|
|
17
|
+
pay_band: stretch
|
|
18
|
+
department: "Studio"
|
|
19
|
+
job_id: "le:brightline:1005"
|
|
20
|
+
tags: [job, stretch]
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# Design Engineer @ Brightline Studio
|
|
24
|
+
|
|
25
|
+
**[Open posting](https://jobs.example.com/brightline/design-engineer)** · Remote · United States · Remote · score **95** · $160k–$195k
|
|
26
|
+
|
|
27
|
+
## Why it matched
|
|
28
|
+
- title +40: design engineer
|
|
29
|
+
- description +23: react, typescript, storybook, figma, accessibility, component library
|
|
30
|
+
- remote +15
|
|
31
|
+
- pay range tops out at $195k, stretch band (-8)
|
|
32
|
+
- posted 0d ago (+25)
|
|
33
|
+
|
|
34
|
+
## Status log
|
|
35
|
+
- 2026-09-22 — found by scraper (score 95). Set `status:` above to `reviewing`, `applying`, `applied`, `interviewing`, `offer`, `rejected`, or `passed`.
|
|
36
|
+
|
|
37
|
+
## Notes
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
## Application
|
|
41
|
+
- **Narrative:**
|
|
42
|
+
- **Resume variant:**
|
|
43
|
+
- **Cover letter:**
|
|
44
|
+
- **Applied on:**
|
|
45
|
+
- **Contact / referral:**
|
|
46
|
+
- **Follow-up due:**
|
|
47
|
+
|
|
48
|
+
## Job description
|
|
49
|
+
> Source: lever
|
|
50
|
+
|
|
51
|
+
Brightline is a 40-person product studio. You would be our first design engineer: build the component library our client work starts from, in React and TypeScript with Storybook, and set the accessibility bar for every project. Figma fluency matters; you will work in the file as much as in the editor. Remote in the US.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: job
|
|
3
|
+
status: reviewing
|
|
4
|
+
listing: open
|
|
5
|
+
company: "Copperleaf"
|
|
6
|
+
title: "Principal Design Engineer"
|
|
7
|
+
location: "Remote - US"
|
|
8
|
+
remote: true
|
|
9
|
+
source: greenhouse
|
|
10
|
+
url: "https://jobs.example.com/copperleaf/principal-design-engineer"
|
|
11
|
+
score: 134
|
|
12
|
+
weights: 3e895160
|
|
13
|
+
posted: "2026-09-19"
|
|
14
|
+
found: 2026-09-20
|
|
15
|
+
salary: "$240k–$290k"
|
|
16
|
+
salary_max: 290000
|
|
17
|
+
pay_band: floor
|
|
18
|
+
department: "Design Systems"
|
|
19
|
+
job_id: "gr:copperleaf:1006"
|
|
20
|
+
tags: [job]
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# Principal Design Engineer @ Copperleaf
|
|
24
|
+
|
|
25
|
+
**[Open posting](https://jobs.example.com/copperleaf/principal-design-engineer)** · Remote - US · Remote · score **134** · $240k–$290k
|
|
26
|
+
|
|
27
|
+
## Why it matched
|
|
28
|
+
- title +40: design engineer
|
|
29
|
+
- seniority +10 (principal)
|
|
30
|
+
- description +26: design system, design systems, react, typescript, tokens, wcag
|
|
31
|
+
- remote +15
|
|
32
|
+
- pay range tops out at $290k, at or above floor (+10, +8 for 90k above)
|
|
33
|
+
- posted 1d ago (+25)
|
|
34
|
+
|
|
35
|
+
## Status log
|
|
36
|
+
- 2026-09-20 — found by scraper (score 134). Set `status:` above to `reviewing`, `applying`, `applied`, `interviewing`, `offer`, `rejected`, or `passed`.
|
|
37
|
+
- 2026-09-20 — new → **reviewing** (via app)
|
|
38
|
+
|
|
39
|
+
## Notes
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
## Application
|
|
43
|
+
- **Narrative:**
|
|
44
|
+
- **Resume variant:**
|
|
45
|
+
- **Cover letter:**
|
|
46
|
+
- **Applied on:**
|
|
47
|
+
- **Contact / referral:**
|
|
48
|
+
- **Follow-up due:**
|
|
49
|
+
|
|
50
|
+
## Job description
|
|
51
|
+
> Source: greenhouse
|
|
52
|
+
|
|
53
|
+
Copperleaf makes accounting software for small firms; 300,000 businesses use it. The design systems team is five engineers and two designers; the principal role sets technical direction for the system across web and the embedded views in our desktop app. React, TypeScript, tokens, theming for white-label partners, WCAG 2.1 AA across the board. Remote in the US.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: job
|
|
3
|
+
status: new
|
|
4
|
+
listing: open
|
|
5
|
+
company: "Example Studio"
|
|
6
|
+
title: "Senior Design Engineer"
|
|
7
|
+
location: "Remote · United States"
|
|
8
|
+
remote: true
|
|
9
|
+
source: tekjobs
|
|
10
|
+
url: "https://tekjobs.timurtek.com/jobs/example-tekjobs"
|
|
11
|
+
score: 130
|
|
12
|
+
weights: 3e895160
|
|
13
|
+
posted: "2026-09-23"
|
|
14
|
+
found: 2026-09-23
|
|
15
|
+
salary: "$180k–$240k"
|
|
16
|
+
salary_max: 240000
|
|
17
|
+
pay_band: floor
|
|
18
|
+
department: "Product Design"
|
|
19
|
+
job_id: "tj:tekjobs:1013"
|
|
20
|
+
tags: [job]
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# Senior Design Engineer @ Example Studio
|
|
24
|
+
|
|
25
|
+
**[Open posting](https://tekjobs.timurtek.com/jobs/example-tekjobs)** · Remote · United States · Remote · score **130** · $180k–$240k
|
|
26
|
+
|
|
27
|
+
## Why it matched
|
|
28
|
+
- title +40: design engineer
|
|
29
|
+
- seniority +8 (senior)
|
|
30
|
+
- description +28: design system, react, typescript, storybook, figma, accessibility, tokens
|
|
31
|
+
- remote +15
|
|
32
|
+
- pay range tops out at $240k, at or above floor (+10, +4 for 40k above)
|
|
33
|
+
- posted 0d ago (+25)
|
|
34
|
+
|
|
35
|
+
## Status log
|
|
36
|
+
- 2026-09-23 — found by scraper (score 130). Set `status:` above to `reviewing`, `applying`, `applied`, `interviewing`, `offer`, `rejected`, or `passed`.
|
|
37
|
+
|
|
38
|
+
## Notes
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## Application
|
|
42
|
+
- **Narrative:**
|
|
43
|
+
- **Resume variant:**
|
|
44
|
+
- **Cover letter:**
|
|
45
|
+
- **Applied on:**
|
|
46
|
+
- **Contact / referral:**
|
|
47
|
+
- **Follow-up due:**
|
|
48
|
+
|
|
49
|
+
## Job description
|
|
50
|
+
> Source: tekjobs
|
|
51
|
+
|
|
52
|
+
Example Studio is a fictional company whose posting exists to show what an employer posting on TekJobs looks like on the board. The role owns the product surface end to end: the React front end, the design system the team ships on, and the accessibility bar. TypeScript, Storybook, Figma tokens. Remote in the United States.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: job
|
|
3
|
+
status: applied
|
|
4
|
+
listing: open
|
|
5
|
+
company: "Fjord Analytics"
|
|
6
|
+
title: "Staff Frontend Engineer, Platform"
|
|
7
|
+
location: "Remote, United States"
|
|
8
|
+
remote: true
|
|
9
|
+
source: greenhouse
|
|
10
|
+
url: "https://jobs.example.com/fjord/staff-frontend-engineer-platform"
|
|
11
|
+
score: 111
|
|
12
|
+
weights: 3e895160
|
|
13
|
+
posted: "2026-09-14"
|
|
14
|
+
found: 2026-09-15
|
|
15
|
+
salary: "$220k–$270k"
|
|
16
|
+
salary_max: 270000
|
|
17
|
+
pay_band: floor
|
|
18
|
+
department: "Platform"
|
|
19
|
+
job_id: "gr:fjord:1003"
|
|
20
|
+
tags: [job]
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# Staff Frontend Engineer, Platform @ Fjord Analytics
|
|
24
|
+
|
|
25
|
+
**[Open posting](https://jobs.example.com/fjord/staff-frontend-engineer-platform)** · Remote, United States · Remote · score **111** · $220k–$270k
|
|
26
|
+
|
|
27
|
+
## Why it matched
|
|
28
|
+
- title +15: frontend engineer
|
|
29
|
+
- seniority +10 (staff)
|
|
30
|
+
- description +29: design system, react, typescript, storybook, accessibility, tokens, component library
|
|
31
|
+
- remote +15
|
|
32
|
+
- pay range tops out at $270k, at or above floor (+10, +7 for 70k above)
|
|
33
|
+
- posted 1d ago (+25)
|
|
34
|
+
|
|
35
|
+
## Status log
|
|
36
|
+
- 2026-09-15 — found by scraper (score 111). Set `status:` above to `reviewing`, `applying`, `applied`, `interviewing`, `offer`, `rejected`, or `passed`.
|
|
37
|
+
- 2026-09-16 — new → **reviewing** (via app)
|
|
38
|
+
- 2026-09-17 — reviewing → **applying** (via app)
|
|
39
|
+
- 2026-09-19 — applying → **applied** (via app)
|
|
40
|
+
|
|
41
|
+
## Notes
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
## Application
|
|
45
|
+
- **Narrative:**
|
|
46
|
+
- **Resume variant:**
|
|
47
|
+
- **Cover letter:**
|
|
48
|
+
- **Applied on:** 2026-09-19
|
|
49
|
+
- **Contact / referral:**
|
|
50
|
+
- **Follow-up due:** 2026-09-26
|
|
51
|
+
|
|
52
|
+
## People
|
|
53
|
+
- [[People/Lee Example (Fjord Analytics)|Lee Example]] · referral · lee@example.com · submitted the internal referral
|
|
54
|
+
|
|
55
|
+
## Job description
|
|
56
|
+
> Source: greenhouse
|
|
57
|
+
|
|
58
|
+
Fjord Analytics is a data platform for mid-size retailers. The platform team owns the web application shell, the build, the design system and the component library that eleven feature teams use. You would lead the component library: React, TypeScript, tokens, Storybook, performance budgets. Experience taking a system through an accessibility audit is a strong signal. Remote in the US.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: job
|
|
3
|
+
status: new
|
|
4
|
+
listing: open
|
|
5
|
+
company: "Halcyon Robotics"
|
|
6
|
+
title: "Senior UX Engineer"
|
|
7
|
+
location: "Remote (United States) · Boston, MA"
|
|
8
|
+
remote: true
|
|
9
|
+
source: ashby
|
|
10
|
+
url: "https://jobs.example.com/halcyon/senior-ux-engineer"
|
|
11
|
+
score: 106
|
|
12
|
+
weights: 3e895160
|
|
13
|
+
posted: "2026-09-08"
|
|
14
|
+
found: 2026-09-09
|
|
15
|
+
salary: "$180k–$215k"
|
|
16
|
+
salary_max: 215000
|
|
17
|
+
pay_band: floor
|
|
18
|
+
department: "Product"
|
|
19
|
+
job_id: "as:halcyon:1004"
|
|
20
|
+
tags: [job]
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# Senior UX Engineer @ Halcyon Robotics
|
|
24
|
+
|
|
25
|
+
**[Open posting](https://jobs.example.com/halcyon/senior-ux-engineer)** · Remote (United States) · Boston, MA · Remote · score **106** · $180k–$215k
|
|
26
|
+
|
|
27
|
+
## Why it matched
|
|
28
|
+
- title +25: ux engineer
|
|
29
|
+
- seniority +8 (senior)
|
|
30
|
+
- description +21: design system, react, typescript, storybook, figma
|
|
31
|
+
- remote +15
|
|
32
|
+
- pay range tops out at $215k, at or above floor (+10, +2 for 15k above)
|
|
33
|
+
- posted 1d ago (+25)
|
|
34
|
+
|
|
35
|
+
## Status log
|
|
36
|
+
- 2026-09-09 — found by scraper (score 106). Set `status:` above to `reviewing`, `applying`, `applied`, `interviewing`, `offer`, `rejected`, or `passed`.
|
|
37
|
+
|
|
38
|
+
## Notes
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## Application
|
|
42
|
+
- **Narrative:**
|
|
43
|
+
- **Resume variant:**
|
|
44
|
+
- **Cover letter:**
|
|
45
|
+
- **Applied on:**
|
|
46
|
+
- **Contact / referral:**
|
|
47
|
+
- **Follow-up due:**
|
|
48
|
+
|
|
49
|
+
## Job description
|
|
50
|
+
> Source: ashby
|
|
51
|
+
|
|
52
|
+
Halcyon builds fleet software for warehouse robots. The UX engineering team sits between design and the product engineers and owns the design system and prototyping. React, TypeScript, Figma, Storybook. Remote in the US, with a Boston office.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: job
|
|
3
|
+
status: new
|
|
4
|
+
listing: open
|
|
5
|
+
company: "Lumen Health"
|
|
6
|
+
title: "Design Systems Engineer"
|
|
7
|
+
location: "Remote US · Denver, CO"
|
|
8
|
+
remote: true
|
|
9
|
+
source: lever
|
|
10
|
+
url: "https://jobs.example.com/lumen/design-systems-engineer"
|
|
11
|
+
score: 109
|
|
12
|
+
weights: 3e895160
|
|
13
|
+
posted: "2026-09-11"
|
|
14
|
+
found: 2026-09-12
|
|
15
|
+
salary: "$175k–$205k"
|
|
16
|
+
salary_max: 205000
|
|
17
|
+
pay_band: floor
|
|
18
|
+
department: "Engineering"
|
|
19
|
+
job_id: "le:lumen:1002"
|
|
20
|
+
tags: [job]
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# Design Systems Engineer @ Lumen Health
|
|
24
|
+
|
|
25
|
+
**[Open posting](https://jobs.example.com/lumen/design-systems-engineer)** · Remote US · Denver, CO · Remote · score **109** · $175k–$205k
|
|
26
|
+
|
|
27
|
+
## Why it matched
|
|
28
|
+
- title +35: design systems, design system
|
|
29
|
+
- description +23: react, typescript, storybook, accessibility, tokens, component library
|
|
30
|
+
- remote +15
|
|
31
|
+
- pay range tops out at $205k, at or above floor (+10, +1 for 5k above)
|
|
32
|
+
- posted 1d ago (+25)
|
|
33
|
+
|
|
34
|
+
## Status log
|
|
35
|
+
- 2026-09-12 — found by scraper (score 109). Set `status:` above to `reviewing`, `applying`, `applied`, `interviewing`, `offer`, `rejected`, or `passed`.
|
|
36
|
+
|
|
37
|
+
## Notes
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
## Application
|
|
41
|
+
- **Narrative:**
|
|
42
|
+
- **Resume variant:**
|
|
43
|
+
- **Cover letter:**
|
|
44
|
+
- **Applied on:**
|
|
45
|
+
- **Contact / referral:**
|
|
46
|
+
- **Follow-up due:**
|
|
47
|
+
|
|
48
|
+
## Job description
|
|
49
|
+
> Source: lever
|
|
50
|
+
|
|
51
|
+
Lumen Health runs a patient portal used by 900 clinics. We are rebuilding the front end on a shared component library and need an engineer who has done this before: tokens, theming, accessibility, documentation in Storybook. TypeScript and React; some Angular remains and you would help retire it. Remote in the US or Denver.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: job
|
|
3
|
+
status: interviewing
|
|
4
|
+
listing: open
|
|
5
|
+
company: "Meridian Pay"
|
|
6
|
+
title: "Staff Design Engineer"
|
|
7
|
+
location: "Remote, United States"
|
|
8
|
+
remote: true
|
|
9
|
+
source: greenhouse
|
|
10
|
+
url: "https://jobs.example.com/meridian/staff-design-engineer"
|
|
11
|
+
score: 137
|
|
12
|
+
weights: 3e895160
|
|
13
|
+
posted: "2026-09-02"
|
|
14
|
+
found: 2026-09-03
|
|
15
|
+
salary: "$225k–$275k"
|
|
16
|
+
salary_max: 275000
|
|
17
|
+
pay_band: floor
|
|
18
|
+
department: "Platform"
|
|
19
|
+
job_id: "gr:meridian:1009"
|
|
20
|
+
tags: [job]
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# Staff Design Engineer @ Meridian Pay
|
|
24
|
+
|
|
25
|
+
**[Open posting](https://jobs.example.com/meridian/staff-design-engineer)** · Remote, United States · Remote · score **137** · $225k–$275k
|
|
26
|
+
|
|
27
|
+
## Why it matched
|
|
28
|
+
- title +40: design engineer
|
|
29
|
+
- seniority +10 (staff)
|
|
30
|
+
- description +29: design system, react, typescript, storybook, accessibility, tokens, component library
|
|
31
|
+
- remote +15
|
|
32
|
+
- pay range tops out at $275k, at or above floor (+10, +8 for 75k above)
|
|
33
|
+
- posted 1d ago (+25)
|
|
34
|
+
|
|
35
|
+
## Status log
|
|
36
|
+
- 2026-09-03 — found by scraper (score 137). Set `status:` above to `reviewing`, `applying`, `applied`, `interviewing`, `offer`, `rejected`, or `passed`.
|
|
37
|
+
- 2026-09-04 — new → **reviewing** (via app)
|
|
38
|
+
- 2026-09-06 — reviewing → **applying** (via app)
|
|
39
|
+
- 2026-09-11 — applying → **applied** (via app)
|
|
40
|
+
- 2026-09-18 — applied → **interviewing** (via app)
|
|
41
|
+
|
|
42
|
+
## Notes
|
|
43
|
+
- 2026-09-18 (app): Recruiter screen with Priya went well; technical interview with the platform lead scheduled for next week. She asked for the Storybook link.
|
|
44
|
+
|
|
45
|
+
## Application
|
|
46
|
+
- **Narrative:** Design engineer; the Acme system numbers lead (62 components, 12 teams, defects down a third).
|
|
47
|
+
- **Resume variant:** Design Engineer (Staff)
|
|
48
|
+
- **Cover letter:** Drafted 2026-09-11, 107 words. Full text under "Cover letter" in this note.
|
|
49
|
+
- **Applied on:** 2026-09-11
|
|
50
|
+
- **Contact / referral:**
|
|
51
|
+
- **Follow-up due:** 2026-09-18
|
|
52
|
+
- **Tailored summary:** Design engineer with eight years on design systems; led the Acme Finance system from 4 to 62 components across 12 teams, including white-label theming for partner banks.
|
|
53
|
+
- **Tailored bullets:** System architecture and tokens · white-label theming · the ESM migration (1.9 MB to 640 kB) · the WCAG 2.1 AA audit with zero blockers
|
|
54
|
+
- **Risks:** No payroll domain experience; say so, and point at fintech at Acme.
|
|
55
|
+
|
|
56
|
+
## Cover letter
|
|
57
|
+
> Drafted 2026-09-11 (via app), 107 words. A draft: read it, edit it, send it yourself.
|
|
58
|
+
|
|
59
|
+
Hello. Your posting says Meridian Pay's design system serves six product teams and the white-label theming your partners depend on. At Acme Finance I took the system from 4 components to 62, used by 12 teams, with partner theming built on the same tokens; UI defect reports fell by a third in the year after. The part I would bring first is the token pipeline, because a system that themes for partners lives or dies on it. In the first month I would inventory the two legacy apps against the system and write down what they still need, since that list is the migration plan. Jordan Example.
|
|
60
|
+
|
|
61
|
+
## People
|
|
62
|
+
- [[People/Priya Example (Meridian Pay)|Priya Example]] · recruiter · priya@meridian.example.com · reached out after the application
|
|
63
|
+
|
|
64
|
+
## Job description
|
|
65
|
+
> Source: greenhouse
|
|
66
|
+
|
|
67
|
+
Meridian Pay processes payroll for 30,000 companies. The platform team owns the design system used by six product teams and the white-label theming our partners depend on. You would lead the system: architecture, tokens, the component library in React and TypeScript, Storybook, accessibility, and the migration of the last two legacy apps. Remote in the US.
|