terminalhire 0.12.0 → 0.14.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.
@@ -13,6 +13,9 @@ var __export = (target, all) => {
13
13
  function isBounty(job) {
14
14
  return job.source === "bounty" && job.bounty != null;
15
15
  }
16
+ function isContribution(job) {
17
+ return job.source === "contribute" && job.contribution != null;
18
+ }
16
19
  var init_types = __esm({
17
20
  "../../packages/core/src/types.ts"() {
18
21
  "use strict";
@@ -549,6 +552,31 @@ function normalize(tokens) {
549
552
  }
550
553
  return Array.from(result);
551
554
  }
555
+ function getAdjacentTags(tag, hops = 1, graph = GRAPH) {
556
+ const lower = String(tag ?? "").toLowerCase().trim();
557
+ const id = graph.ids.has(lower) ? lower : graph.synonyms.get(lower);
558
+ if (!id) return [];
559
+ const oneHop = (source) => {
560
+ const out = [];
561
+ for (const [to, edge] of graph.closure.get(source) ?? []) {
562
+ if (edge.via === to) out.push([to, edge.w]);
563
+ }
564
+ return out;
565
+ };
566
+ const byWeightThenId = (a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : 1);
567
+ const ring1 = oneHop(id).sort(byWeightThenId);
568
+ if (hops === 1) return ring1.map(([to]) => to);
569
+ const exclude = /* @__PURE__ */ new Set([id, ...ring1.map(([to]) => to)]);
570
+ const best = /* @__PURE__ */ new Map();
571
+ for (const [n, w1] of ring1) {
572
+ for (const [to, w2] of oneHop(n)) {
573
+ if (exclude.has(to)) continue;
574
+ const w = w1 * w2;
575
+ if (w > (best.get(to) ?? 0)) best.set(to, w);
576
+ }
577
+ }
578
+ return [...best.entries()].sort(byWeightThenId).map(([to]) => to);
579
+ }
552
580
  function expandWeighted(tags, graph = GRAPH) {
553
581
  const out = /* @__PURE__ */ new Map();
554
582
  const put = (tag, weight, via) => {
@@ -587,6 +615,21 @@ var init_vocabulary = __esm({
587
615
  }
588
616
  });
589
617
 
618
+ // ../../packages/core/src/feeds/contribution-gate.ts
619
+ function passesContributionGate(input) {
620
+ if (input.contributors === void 0) return false;
621
+ return input.stars >= MIN_STARS && input.contributors >= MIN_CONTRIBUTORS && !TRIVIAL_PR_TITLE.test(input.title) && !input.archived && !input.fork;
622
+ }
623
+ var MIN_STARS, MIN_CONTRIBUTORS, TRIVIAL_PR_TITLE;
624
+ var init_contribution_gate = __esm({
625
+ "../../packages/core/src/feeds/contribution-gate.ts"() {
626
+ "use strict";
627
+ MIN_STARS = 50;
628
+ MIN_CONTRIBUTORS = 10;
629
+ TRIVIAL_PR_TITLE = /^\s*(fix\s+typo|typo\b|update\s+readme|readme\b|docs?:|docs?\(|chore:|chore\(|style:|ci:|build:|bump\b|update\s+dependenc)/i;
630
+ }
631
+ });
632
+
590
633
  // ../../packages/core/src/github.ts
591
634
  function ghHeaders(token) {
592
635
  const headers = {
@@ -934,16 +977,14 @@ function deriveResumeTrend(cred, repoRecency, now = Date.now()) {
934
977
  }
935
978
  return scored.sort((a, b) => b.weight - a.weight).slice(0, 12).map((s) => s.t);
936
979
  }
937
- var TRACTION_TOP_N, MIN_STARS, MIN_CONTRIBUTORS, CANDIDATE_PR_PAGE, TRIVIAL_PR_TITLE, RESUME_DECAY_HALF_LIFE_MS, RESUME_MIN_SCORE;
980
+ var TRACTION_TOP_N, CANDIDATE_PR_PAGE, RESUME_DECAY_HALF_LIFE_MS, RESUME_MIN_SCORE;
938
981
  var init_github = __esm({
939
982
  "../../packages/core/src/github.ts"() {
940
983
  "use strict";
941
984
  init_vocabulary();
985
+ init_contribution_gate();
942
986
  TRACTION_TOP_N = 6;
943
- MIN_STARS = 50;
944
- MIN_CONTRIBUTORS = 10;
945
987
  CANDIDATE_PR_PAGE = 50;
946
- TRIVIAL_PR_TITLE = /^\s*(fix\s+typo|typo\b|update\s+readme|readme\b|docs?:|docs?\(|chore:|chore\(|style:|ci:|build:|bump\b|update\s+dependenc)/i;
947
988
  RESUME_DECAY_HALF_LIFE_MS = 30 * 24 * 60 * 60 * 1e3;
948
989
  RESUME_MIN_SCORE = 0.05;
949
990
  }
@@ -1670,13 +1711,18 @@ var init_bounty_gate = __esm({
1670
1711
  "aragon/hack",
1671
1712
  "spacemeshos/app",
1672
1713
  "archestra-ai/archestra",
1673
- "boundlessfi/boundless",
1674
1714
  "ucfopen/Obojobo",
1675
- "widgetti/ipyvolume",
1676
1715
  "moorcheh-ai/memanto",
1677
1716
  "PrismarineJS/mineflayer"
1678
1717
  ];
1679
- BOUNTY_REPO_DENYLIST = ["SecureBananaLabs/bug-bounty"];
1718
+ BOUNTY_REPO_DENYLIST = [
1719
+ "SecureBananaLabs/bug-bounty",
1720
+ // Meta-farm: a bounty PLATFORM whose own issues are an assignment-gated
1721
+ // contributor queue ("please assign me, my chief") — an unsolicited PR won't
1722
+ // merge, so it's not a real claimable bounty. Not structurally derivable from
1723
+ // any fetched field, so it's a manual entry (also dropped from the allowlist).
1724
+ "boundlessfi/boundless"
1725
+ ];
1680
1726
  DENYLIST_LC = new Set(BOUNTY_REPO_DENYLIST.map((r) => r.toLowerCase()));
1681
1727
  MAX_BOUNTIES_PER_REPO = 10;
1682
1728
  MAX_BOUNTIES_PER_DISCOVERED_REPO = 3;
@@ -1746,6 +1792,9 @@ function isBountyIssue(issue) {
1746
1792
  if (labels.some((n) => BOUNTY_LABEL_RE.test(n))) return true;
1747
1793
  return /bounty/i.test(issue.title) && parseAmountUSD(issue.title) != null;
1748
1794
  }
1795
+ function isAssigned(issue) {
1796
+ return !!issue.assignee || (issue.assignees?.length ?? 0) > 0;
1797
+ }
1749
1798
  async function ghJson(path) {
1750
1799
  let res;
1751
1800
  try {
@@ -1798,7 +1847,7 @@ async function fetchRepoBounties(repoFullName) {
1798
1847
  }
1799
1848
  const issues = await ghJson(`/repos/${repoFullName}/issues?state=open&per_page=100`);
1800
1849
  if (!issues) return [];
1801
- const bounties = issues.filter(isBountyIssue).slice(0, MAX_BOUNTIES_PER_REPO);
1850
+ const bounties = issues.filter((i) => isBountyIssue(i) && !isAssigned(i)).slice(0, MAX_BOUNTIES_PER_REPO);
1802
1851
  const owner = repo.owner.login;
1803
1852
  return mapWithConcurrency(bounties, BOUNTY_FETCH_CONCURRENCY, async (issue) => {
1804
1853
  const title = decodeEntities(issue.title).trim();
@@ -1921,6 +1970,7 @@ async function fetchSearchBounties() {
1921
1970
  if (jobs.length >= MAX_SEARCH_BOUNTIES) break;
1922
1971
  const fullName = repoFullNameFromApiUrl(issue.repository_url);
1923
1972
  if (!fullName) continue;
1973
+ if (isAssigned(issue)) continue;
1924
1974
  if ((perRepo.get(fullName) ?? 0) >= MAX_BOUNTIES_PER_REPO) continue;
1925
1975
  const repo = await repoMetaCached(fullName);
1926
1976
  if (!repo) continue;
@@ -2432,6 +2482,7 @@ var init_feeds = __esm({
2432
2482
  init_directory();
2433
2483
  init_bounty_gate();
2434
2484
  init_bounty_gate();
2485
+ init_contribution_gate();
2435
2486
  FEEDS = [greenhouse, ashby, lever, workable, himalayas, wwr, hn];
2436
2487
  GREENHOUSE_SLUGS_BY_TIER = {
2437
2488
  bigco: [
@@ -2542,6 +2593,202 @@ var init_feeds = __esm({
2542
2593
  }
2543
2594
  });
2544
2595
 
2596
+ // ../../packages/core/src/feeds/contributions.ts
2597
+ function authHeaders2() {
2598
+ const token = process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"];
2599
+ const h = {
2600
+ Accept: "application/vnd.github+json",
2601
+ "User-Agent": "terminalhire",
2602
+ "X-GitHub-Api-Version": "2022-11-28"
2603
+ };
2604
+ if (token) h["Authorization"] = `Bearer ${token}`;
2605
+ return h;
2606
+ }
2607
+ function tokenize4(text) {
2608
+ return text.toLowerCase().replace(/[^a-z0-9.\-+#]/g, " ").split(/\s+/).filter(Boolean);
2609
+ }
2610
+ function labelNames2(labels) {
2611
+ return (labels ?? []).map((l) => typeof l === "string" ? l : l.name ?? "").filter(Boolean);
2612
+ }
2613
+ function repoFullNameFromApiUrl2(url) {
2614
+ const m = url.match(/\/repos\/([^/]+)\/([^/]+)\/?$/);
2615
+ return m ? `${m[1]}/${m[2]}` : null;
2616
+ }
2617
+ function makeClient(fetchImpl) {
2618
+ async function raw(path) {
2619
+ try {
2620
+ return await fetchImpl(`${GITHUB_API2}${path}`, { headers: authHeaders2() });
2621
+ } catch {
2622
+ return null;
2623
+ }
2624
+ }
2625
+ async function json(path) {
2626
+ const res = await raw(path);
2627
+ if (!res) return null;
2628
+ if (res.status === 403 && res.headers.get("x-ratelimit-remaining") === "0") return null;
2629
+ if (!res.ok) return null;
2630
+ try {
2631
+ return await res.json();
2632
+ } catch {
2633
+ return null;
2634
+ }
2635
+ }
2636
+ return { raw, json };
2637
+ }
2638
+ async function contributorCount(client, fullName) {
2639
+ const res = await client.raw(`/repos/${fullName}/contributors?per_page=1&anon=false`);
2640
+ if (!res || !res.ok) return void 0;
2641
+ const link = res.headers.get("link");
2642
+ const m = link?.match(/[?&]page=(\d+)>;\s*rel="last"/);
2643
+ if (m) return Number(m[1]);
2644
+ try {
2645
+ const body = await res.json();
2646
+ return Array.isArray(body) ? body.length : 0;
2647
+ } catch {
2648
+ return void 0;
2649
+ }
2650
+ }
2651
+ async function openPRIssueRefs(client, fullName) {
2652
+ const refs = /* @__PURE__ */ new Set();
2653
+ const prs = await client.json(
2654
+ `/repos/${fullName}/pulls?state=open&per_page=100`
2655
+ );
2656
+ if (!Array.isArray(prs)) return refs;
2657
+ for (const pr of prs) {
2658
+ for (const m of `${pr.title ?? ""}
2659
+ ${pr.body ?? ""}`.matchAll(/#(\d+)\b/g)) {
2660
+ refs.add(Number(m[1]));
2661
+ }
2662
+ }
2663
+ return refs;
2664
+ }
2665
+ async function searchContribIssues(client, queries) {
2666
+ const byUrl = /* @__PURE__ */ new Map();
2667
+ for (const q of queries) {
2668
+ const res = await client.json(
2669
+ `/search/issues?q=${encodeURIComponent(q)}&sort=created&order=desc&per_page=${SEARCH_PER_PAGE2}`
2670
+ );
2671
+ for (const it of res?.items ?? []) {
2672
+ if (it.pull_request) continue;
2673
+ if (!byUrl.has(it.html_url)) byUrl.set(it.html_url, it);
2674
+ }
2675
+ }
2676
+ return [...byUrl.values()].sort(
2677
+ (a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
2678
+ );
2679
+ }
2680
+ async function aggregateContributions(opts = {}) {
2681
+ const client = makeClient(opts.fetchImpl ?? fetchWithTimeout);
2682
+ const queries = opts.queries ?? CONTRIB_SEARCH_QUERIES;
2683
+ const issues = (await searchContribIssues(client, queries)).slice(0, MAX_CONTRIB_ISSUES_SCANNED);
2684
+ const repoCache = /* @__PURE__ */ new Map();
2685
+ const contribCache = /* @__PURE__ */ new Map();
2686
+ const prRefsCache = /* @__PURE__ */ new Map();
2687
+ async function repoMeta(fullName) {
2688
+ const hit = repoCache.get(fullName);
2689
+ if (hit !== void 0) return hit;
2690
+ const r = await client.json(`/repos/${fullName}`) ?? null;
2691
+ repoCache.set(fullName, r);
2692
+ return r;
2693
+ }
2694
+ async function repoContribCount(fullName) {
2695
+ if (contribCache.has(fullName)) return contribCache.get(fullName);
2696
+ const n = await contributorCount(client, fullName);
2697
+ contribCache.set(fullName, n);
2698
+ return n;
2699
+ }
2700
+ async function repoPRRefs(fullName) {
2701
+ const hit = prRefsCache.get(fullName);
2702
+ if (hit !== void 0) return hit;
2703
+ const refs = await openPRIssueRefs(client, fullName);
2704
+ prRefsCache.set(fullName, refs);
2705
+ return refs;
2706
+ }
2707
+ const jobs = [];
2708
+ const seen = /* @__PURE__ */ new Set();
2709
+ const perRepo = /* @__PURE__ */ new Map();
2710
+ for (const issue of issues) {
2711
+ if (jobs.length >= MAX_CONTRIB_ITEMS) break;
2712
+ const fullName = repoFullNameFromApiUrl2(issue.repository_url);
2713
+ if (!fullName) continue;
2714
+ const id = `contribute:${fullName}#${issue.number}`;
2715
+ if (seen.has(id)) continue;
2716
+ if (isDenylistedRepo(fullName)) continue;
2717
+ if (isAssigned(issue)) continue;
2718
+ if ((perRepo.get(fullName) ?? 0) >= MAX_BOUNTIES_PER_DISCOVERED_REPO) continue;
2719
+ const repo = await repoMeta(fullName);
2720
+ if (!repo) continue;
2721
+ const title = decodeEntities(issue.title).trim();
2722
+ const contributors = await repoContribCount(fullName);
2723
+ if (!passesContributionGate({
2724
+ stars: repo.stargazers_count,
2725
+ contributors,
2726
+ title,
2727
+ archived: repo.archived,
2728
+ fork: repo.fork
2729
+ })) {
2730
+ continue;
2731
+ }
2732
+ if (repo.disabled) continue;
2733
+ const prRefs = await repoPRRefs(fullName);
2734
+ if (prRefs.has(issue.number)) continue;
2735
+ const body = issue.body ? decodeEntities(issue.body) : "";
2736
+ const labels = labelNames2(issue.labels);
2737
+ const tags = normalize(
2738
+ tokenize4([title, repo.language ?? "", labels.join(" "), body.slice(0, 2e3)].join(" "))
2739
+ );
2740
+ seen.add(id);
2741
+ perRepo.set(fullName, (perRepo.get(fullName) ?? 0) + 1);
2742
+ jobs.push({
2743
+ id,
2744
+ source: "contribute",
2745
+ title,
2746
+ company: repo.owner.login,
2747
+ url: issue.html_url,
2748
+ remote: true,
2749
+ location: "Remote",
2750
+ tags,
2751
+ roleType: "freelance",
2752
+ postedAt: issue.created_at,
2753
+ applyMode: "direct",
2754
+ contribution: {
2755
+ repoFullName: fullName,
2756
+ repoStars: repo.stargazers_count,
2757
+ repoContributors: contributors,
2758
+ // gate guarantees a number here
2759
+ issueNumber: issue.number,
2760
+ labels,
2761
+ issueUrl: issue.html_url,
2762
+ issueBody: body.slice(0, 1e3) || void 0
2763
+ },
2764
+ raw: issue
2765
+ });
2766
+ }
2767
+ return jobs;
2768
+ }
2769
+ var GITHUB_API2, CONTRIB_SEARCH_QUERIES, SEARCH_PER_PAGE2, MAX_CONTRIB_ITEMS, MAX_CONTRIB_ISSUES_SCANNED;
2770
+ var init_contributions = __esm({
2771
+ "../../packages/core/src/feeds/contributions.ts"() {
2772
+ "use strict";
2773
+ init_vocabulary();
2774
+ init_entities();
2775
+ init_bounty_gate();
2776
+ init_contribution_gate();
2777
+ init_github_bounties();
2778
+ init_http();
2779
+ GITHUB_API2 = "https://api.github.com";
2780
+ CONTRIB_SEARCH_QUERIES = [
2781
+ 'label:"good first issue" type:issue state:open',
2782
+ 'label:"help wanted" type:issue state:open',
2783
+ 'label:"good-first-issue" type:issue state:open',
2784
+ 'label:"help-wanted" type:issue state:open'
2785
+ ];
2786
+ SEARCH_PER_PAGE2 = 100;
2787
+ MAX_CONTRIB_ITEMS = 150;
2788
+ MAX_CONTRIB_ISSUES_SCANNED = 300;
2789
+ }
2790
+ });
2791
+
2545
2792
  // ../../packages/core/src/partners.ts
2546
2793
  import { readFileSync } from "fs";
2547
2794
  import { join } from "path";
@@ -2617,15 +2864,21 @@ async function buildIndex(opts) {
2617
2864
  }
2618
2865
  }
2619
2866
  const jobs = allJobs.map(({ raw: _raw, ...rest }) => rest);
2620
- return {
2867
+ const index = {
2621
2868
  builtAt: (/* @__PURE__ */ new Date()).toISOString(),
2622
2869
  jobs
2623
2870
  };
2871
+ if (opts?.includeContribute) {
2872
+ const contributions = await aggregateContributions(opts.contributeOpts);
2873
+ index.contribute = contributions.map(({ raw: _raw, ...rest }) => rest);
2874
+ }
2875
+ return index;
2624
2876
  }
2625
2877
  var init_indexer = __esm({
2626
2878
  "../../packages/core/src/indexer.ts"() {
2627
2879
  "use strict";
2628
2880
  init_feeds();
2881
+ init_contributions();
2629
2882
  init_partners();
2630
2883
  }
2631
2884
  });
@@ -5887,6 +6140,179 @@ var init_job_status = __esm({
5887
6140
  }
5888
6141
  });
5889
6142
 
6143
+ // ../../packages/core/src/episodes/schema.ts
6144
+ var init_schema = __esm({
6145
+ "../../packages/core/src/episodes/schema.ts"() {
6146
+ "use strict";
6147
+ }
6148
+ });
6149
+
6150
+ // ../../packages/core/src/episodes/doors.ts
6151
+ var init_doors = __esm({
6152
+ "../../packages/core/src/episodes/doors.ts"() {
6153
+ "use strict";
6154
+ init_schema();
6155
+ }
6156
+ });
6157
+
6158
+ // ../../packages/core/src/episodes/node-model.ts
6159
+ var init_node_model = __esm({
6160
+ "../../packages/core/src/episodes/node-model.ts"() {
6161
+ "use strict";
6162
+ }
6163
+ });
6164
+
6165
+ // ../../packages/core/src/episodes/derivers/signals.ts
6166
+ var init_signals = __esm({
6167
+ "../../packages/core/src/episodes/derivers/signals.ts"() {
6168
+ "use strict";
6169
+ init_node_model();
6170
+ }
6171
+ });
6172
+
6173
+ // ../../packages/core/src/episodes/derivers/recency-split.ts
6174
+ var DORMANT_THRESHOLD_DAYS;
6175
+ var init_recency_split = __esm({
6176
+ "../../packages/core/src/episodes/derivers/recency-split.ts"() {
6177
+ "use strict";
6178
+ init_doors();
6179
+ init_signals();
6180
+ DORMANT_THRESHOLD_DAYS = 90;
6181
+ }
6182
+ });
6183
+
6184
+ // ../../packages/core/src/credential/legible.ts
6185
+ function labelFor(id) {
6186
+ return DISPLAY_LABEL[id] ?? id.split("-").map((w) => w.length > 0 ? w.charAt(0).toUpperCase() + w.slice(1) : w).join(" ");
6187
+ }
6188
+ function capitalize(s) {
6189
+ return s.length > 0 ? s.charAt(0).toUpperCase() + s.slice(1) : s;
6190
+ }
6191
+ function deriveLegibleProfile(credential, recency, traction, seniorityBand) {
6192
+ const ok = credential.status === "ok";
6193
+ const domains = ok ? credential.byDomain : {};
6194
+ const chips = Object.entries(domains).map(([rawDomain, d]) => {
6195
+ const canon = normalize([rawDomain])[0];
6196
+ return canon ? { domain: canon, mergedPRs: d.mergedPRs } : null;
6197
+ }).filter((c) => c !== null).sort((a, b) => b.mergedPRs - a.mergedPRs || (a.domain < b.domain ? -1 : 1));
6198
+ const dominant = chips.length > 0 ? chips[0].domain : void 0;
6199
+ const role = dominant ? ROLE_BY_DOMAIN[dominant] ?? GENERIC_ROLE : GENERIC_ROLE;
6200
+ const stackChips = chips.filter((c) => !(c.domain in CONCEPT_TAGS));
6201
+ const conceptChips = chips.filter((c) => c.domain in CONCEPT_TAGS && c.domain !== dominant);
6202
+ const topStacks = stackChips.slice(0, 3).map((c) => labelFor(c.domain)).join(", ");
6203
+ const specialization = dominant && dominant in CONCEPT_TAGS ? CONCEPT_TAGS[dominant] : conceptChips.length > 0 ? CONCEPT_TAGS[conceptChips[0].domain] : "";
6204
+ const sen = seniorityBand ? capitalize(seniorityBand) : "";
6205
+ let headline;
6206
+ if (chips.length > 0) {
6207
+ const roleSeg = sen ? `${sen} ${role}` : role;
6208
+ const segs = [roleSeg];
6209
+ if (specialization) segs.push(specialization);
6210
+ if (topStacks) segs.push(topStacks);
6211
+ headline = segs.join(" \xB7 ");
6212
+ } else {
6213
+ const roleSeg = sen ? `${sen} ${GENERIC_ROLE}` : GENERIC_ROLE;
6214
+ headline = traction.status === "ok" && traction.totalStars > 0 ? `${roleSeg} \xB7 ${traction.totalStars}\u2605 across owned repos` : roleSeg;
6215
+ }
6216
+ const mergedDates = Object.values(domains).map((d) => d.lastMergedAt).filter((s) => typeof s === "string" && s.length > 0).sort();
6217
+ const mostRecent = mergedDates.length > 0 ? mergedDates[mergedDates.length - 1] : void 0;
6218
+ const thresholdDays = Number.isFinite(recency.value.thresholdDays) && recency.value.thresholdDays > 0 ? recency.value.thresholdDays : DORMANT_THRESHOLD_DAYS;
6219
+ const nowIso = recency.value.now && recency.value.now.length > 0 ? recency.value.now : credential.computedAt || (/* @__PURE__ */ new Date()).toISOString();
6220
+ const nowMs = Date.parse(nowIso);
6221
+ let recencyBadge = null;
6222
+ let daysAgo;
6223
+ if (mostRecent) {
6224
+ const ageDays2 = (nowMs - Date.parse(mostRecent)) / DAY_MS;
6225
+ daysAgo = Math.max(0, Math.round(ageDays2));
6226
+ recencyBadge = { lastMergedAt: mostRecent, state: ageDays2 <= thresholdDays ? "live" : "dormant" };
6227
+ }
6228
+ const orgCount = Object.values(domains).reduce((m, d) => Math.max(m, d.distinctOrgs), 0);
6229
+ let proofSentence;
6230
+ if (!ok) {
6231
+ proofSentence = "Contribution credential unavailable \u2014 could not verify.";
6232
+ } else {
6233
+ const prs = credential.qualifyingTotal;
6234
+ let s = `${prs} substantive PR${prs === 1 ? "" : "s"} merged into at least ${orgCount} external org${orgCount === 1 ? "" : "s"} (\u2265${MIN_STARS}\u2605, \u2265${MIN_CONTRIBUTORS} contributors)`;
6235
+ if (daysAgo !== void 0) s += ` \u2014 most recent ${daysAgo}d ago`;
6236
+ proofSentence = `${s}.`;
6237
+ }
6238
+ const auditableBadge = ok ? {
6239
+ mergedTotal: credential.qualifyingTotal,
6240
+ distinctOrgs: orgCount,
6241
+ thresholds: { stars: MIN_STARS, contributors: MIN_CONTRIBUTORS }
6242
+ } : null;
6243
+ const profile = {
6244
+ headline,
6245
+ verifiedSkillChips: chips,
6246
+ recencyBadge,
6247
+ proofSentence,
6248
+ auditableBadge
6249
+ };
6250
+ if (seniorityBand) profile.seniority = seniorityBand;
6251
+ return profile;
6252
+ }
6253
+ var DAY_MS, GENERIC_ROLE, ROLE_BY_DOMAIN, CONCEPT_TAGS, DISPLAY_LABEL;
6254
+ var init_legible = __esm({
6255
+ "../../packages/core/src/credential/legible.ts"() {
6256
+ "use strict";
6257
+ init_contribution_gate();
6258
+ init_vocabulary();
6259
+ init_recency_split();
6260
+ DAY_MS = 864e5;
6261
+ GENERIC_ROLE = "Software Engineer";
6262
+ ROLE_BY_DOMAIN = {
6263
+ backend: "Backend Engineer",
6264
+ go: "Backend Engineer",
6265
+ rust: "Backend Engineer",
6266
+ java: "Backend Engineer",
6267
+ python: "Backend Engineer",
6268
+ nodejs: "Backend Engineer",
6269
+ "api-design": "Backend Engineer",
6270
+ microservices: "Backend Engineer",
6271
+ postgresql: "Backend Engineer",
6272
+ frontend: "Frontend Engineer",
6273
+ react: "Frontend Engineer",
6274
+ vue: "Frontend Engineer",
6275
+ css: "Frontend Engineer",
6276
+ ml: "ML Engineer",
6277
+ pytorch: "ML Engineer",
6278
+ llm: "ML Engineer",
6279
+ "computer-vision": "ML Engineer",
6280
+ devops: "Platform Engineer",
6281
+ kubernetes: "Platform Engineer",
6282
+ terraform: "Platform Engineer",
6283
+ docker: "Platform Engineer",
6284
+ aws: "Platform Engineer",
6285
+ ios: "Mobile Engineer",
6286
+ android: "Mobile Engineer"
6287
+ };
6288
+ CONCEPT_TAGS = {
6289
+ "distributed-systems": "Distributed systems",
6290
+ microservices: "Microservices",
6291
+ security: "Security",
6292
+ payments: "Payments",
6293
+ ml: "Machine learning",
6294
+ llm: "LLM systems",
6295
+ "computer-vision": "Computer vision",
6296
+ recsys: "Recommendation systems",
6297
+ "api-design": "API design"
6298
+ };
6299
+ DISPLAY_LABEL = {
6300
+ postgresql: "Postgres",
6301
+ nodejs: "Node.js",
6302
+ ml: "ML",
6303
+ llm: "LLM",
6304
+ "api-design": "API design",
6305
+ "ci-cd": "CI/CD",
6306
+ "computer-vision": "Computer vision",
6307
+ "distributed-systems": "Distributed systems",
6308
+ ios: "iOS",
6309
+ css: "CSS",
6310
+ graphql: "GraphQL",
6311
+ aws: "AWS"
6312
+ };
6313
+ }
6314
+ });
6315
+
5890
6316
  // ../../packages/core/src/index.ts
5891
6317
  var src_exports = {};
5892
6318
  __export(src_exports, {
@@ -5906,8 +6332,11 @@ __export(src_exports, {
5906
6332
  INTRO_ALLOWED_FIELDS: () => INTRO_ALLOWED_FIELDS,
5907
6333
  INTRO_PENDING_TTL_MS: () => INTRO_PENDING_TTL_MS,
5908
6334
  LEVER_SLUGS_BY_TIER: () => LEVER_SLUGS_BY_TIER,
6335
+ MIN_CONTRIBUTORS: () => MIN_CONTRIBUTORS,
6336
+ MIN_STARS: () => MIN_STARS,
5909
6337
  STRONG_MATCH_THRESHOLD: () => STRONG_MATCH_THRESHOLD,
5910
6338
  SYNONYMS: () => SYNONYMS,
6339
+ TRIVIAL_PR_TITLE: () => TRIVIAL_PR_TITLE,
5911
6340
  VOCABULARY: () => VOCABULARY,
5912
6341
  VOCAB_NODES: () => VOCAB_NODES,
5913
6342
  acceptanceCountForDomains: () => acceptanceCountForDomains,
@@ -5930,6 +6359,7 @@ __export(src_exports, {
5930
6359
  coreTagsFromTitle: () => coreTagsFromTitle,
5931
6360
  decorate: () => decorate,
5932
6361
  decryptMessage: () => decryptMessage,
6362
+ deriveLegibleProfile: () => deriveLegibleProfile,
5933
6363
  deriveResumeTrend: () => deriveResumeTrend,
5934
6364
  deriveSharedKey: () => deriveSharedKey,
5935
6365
  encryptMessage: () => encryptMessage,
@@ -5941,6 +6371,7 @@ __export(src_exports, {
5941
6371
  flattenTiers: () => flattenTiers,
5942
6372
  funnelCounts: () => funnelCounts,
5943
6373
  generateIdentityKeypair: () => generateIdentityKeypair,
6374
+ getAdjacentTags: () => getAdjacentTags,
5944
6375
  getBuyer: () => getBuyer,
5945
6376
  githubBounties: () => githubBounties,
5946
6377
  githubToFingerprint: () => githubToFingerprint,
@@ -5951,6 +6382,7 @@ __export(src_exports, {
5951
6382
  introRateLimitCheck: () => introRateLimitCheck,
5952
6383
  introRetentionAction: () => introRetentionAction,
5953
6384
  isBounty: () => isBounty,
6385
+ isContribution: () => isContribution,
5954
6386
  isOverIntroLimit: () => isOverIntroLimit,
5955
6387
  lever: () => lever,
5956
6388
  loadPartnerRoles: () => loadPartnerRoles,
@@ -5959,6 +6391,7 @@ __export(src_exports, {
5959
6391
  normalize: () => normalize,
5960
6392
  opire: () => opire,
5961
6393
  pageMatches: () => pageMatches,
6394
+ passesContributionGate: () => passesContributionGate,
5962
6395
  passesMaturityGate: () => passesMaturityGate,
5963
6396
  personCardToJob: () => personCardToJob,
5964
6397
  projectCardToJob: () => projectCardToJob,
@@ -5988,6 +6421,7 @@ var init_src = __esm({
5988
6421
  init_directoryThreshold();
5989
6422
  init_chatCrypto();
5990
6423
  init_job_status();
6424
+ init_legible();
5991
6425
  }
5992
6426
  });
5993
6427