terminalhire 0.3.0 → 0.3.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.
@@ -3177,16 +3177,26 @@ function buildContextVerbs(topMatches, sessionTags) {
3177
3177
  for (const t of sess) {
3178
3178
  if (roleTags.has(t) && !overlap.includes(t)) overlap.push(t);
3179
3179
  }
3180
+ let headers;
3180
3181
  if (overlap.length >= 2) {
3181
3182
  const a = titleCase(overlap[0]);
3182
3183
  const b = titleCase(overlap[1]);
3183
- return [`\u2726 Fits your ${a} + ${b} work`, `\u2726 A role matching what you're building`];
3184
- }
3185
- if (overlap.length === 1) {
3184
+ headers = [`\u2726 Fits your ${a} + ${b} work`, `\u2726 A role matching what you're building`];
3185
+ } else if (overlap.length === 1) {
3186
3186
  const a = titleCase(overlap[0]);
3187
- return [`\u2726 A role matching your ${a} work`, `\u2726 Your ${a} work \u2014 link in the tip below`];
3187
+ headers = [`\u2726 A role matching your ${a} work`, `\u2726 Your ${a} work \u2014 link in the tip below`];
3188
+ } else {
3189
+ headers = [`\u2726 A role that fits your work`, `\u2726 Job match for you \u2014 link in the tip below`];
3190
+ }
3191
+ const list = Array.isArray(topMatches) ? topMatches : [];
3192
+ const bounty = list.find((m) => m && m.source === "bounty" && m.amountUSD != null) || list.find((m) => m && m.source === "bounty");
3193
+ if (bounty) {
3194
+ const money = bounty.amountUSD != null ? `$${bounty.amountUSD.toLocaleString()} ` : "";
3195
+ const bountyHeader = `\u2726 \u{1F48E} A ${money}bounty in your stack \u2014 link below`;
3196
+ if (list[0] && list[0].source === "bounty") headers.unshift(bountyHeader);
3197
+ else headers.push(bountyHeader);
3188
3198
  }
3189
- return [`\u2726 A role that fits your work`, `\u2726 Job match for you \u2014 link in the tip below`];
3199
+ return headers;
3190
3200
  }
3191
3201
  function buildSpinnerPool(topMatches, max = 6, opts = {}) {
3192
3202
  const { sessionTags, frequency = "always" } = opts;
@@ -3291,7 +3301,13 @@ function buildTips(topMatches, baseUrl, max = 8) {
3291
3301
  const pct = Math.max(1, Math.min(99, Math.round((Number(m.score) || 0) * 100)));
3292
3302
  const token = Buffer.from(String(m.id)).toString("base64url");
3293
3303
  const url = `${base}/j/${token}`;
3294
- out.push(`\u2197 ${title} @ ${company} \xB7 ${pct}% \u2014 ${url}`);
3304
+ if (source === "bounty") {
3305
+ const money = m.amountUSD != null ? `$${Number(m.amountUSD).toLocaleString()}` : "$\u2014";
3306
+ const repo = m.repo || companyRaw;
3307
+ out.push(`\u{1F48E} ${money} \xB7 ${title} \xB7 ${repo} \xB7 ${pct}% \u2014 ${url}`);
3308
+ } else {
3309
+ out.push(`\u2197 ${title} @ ${company} \xB7 ${pct}% \u2014 ${url}`);
3310
+ }
3295
3311
  if (out.length >= max) break;
3296
3312
  }
3297
3313
  return out;
@@ -4096,7 +4112,12 @@ async function run10() {
4096
4112
  company: r.job.company,
4097
4113
  score: r.score,
4098
4114
  remote: r.job.remote,
4099
- matchedTags: r.matchedTags
4115
+ matchedTags: r.matchedTags,
4116
+ // Bounty fields so the spinner can render bounty framing ($ + 💎).
4117
+ // Public job text, stays LOCAL (same as the rest of topMatches).
4118
+ source: r.job.source,
4119
+ amountUSD: r.job.bounty?.amountUSD,
4120
+ repo: r.job.bounty?.repoFullName
4100
4121
  }));
4101
4122
  }
4102
4123
  } catch {
@@ -2006,16 +2006,26 @@ function buildContextVerbs(topMatches, sessionTags) {
2006
2006
  for (const t of sess) {
2007
2007
  if (roleTags.has(t) && !overlap.includes(t)) overlap.push(t);
2008
2008
  }
2009
+ let headers;
2009
2010
  if (overlap.length >= 2) {
2010
2011
  const a = titleCase(overlap[0]);
2011
2012
  const b = titleCase(overlap[1]);
2012
- return [`\u2726 Fits your ${a} + ${b} work`, `\u2726 A role matching what you're building`];
2013
- }
2014
- if (overlap.length === 1) {
2013
+ headers = [`\u2726 Fits your ${a} + ${b} work`, `\u2726 A role matching what you're building`];
2014
+ } else if (overlap.length === 1) {
2015
2015
  const a = titleCase(overlap[0]);
2016
- return [`\u2726 A role matching your ${a} work`, `\u2726 Your ${a} work \u2014 link in the tip below`];
2016
+ headers = [`\u2726 A role matching your ${a} work`, `\u2726 Your ${a} work \u2014 link in the tip below`];
2017
+ } else {
2018
+ headers = [`\u2726 A role that fits your work`, `\u2726 Job match for you \u2014 link in the tip below`];
2019
+ }
2020
+ const list = Array.isArray(topMatches) ? topMatches : [];
2021
+ const bounty = list.find((m) => m && m.source === "bounty" && m.amountUSD != null) || list.find((m) => m && m.source === "bounty");
2022
+ if (bounty) {
2023
+ const money = bounty.amountUSD != null ? `$${bounty.amountUSD.toLocaleString()} ` : "";
2024
+ const bountyHeader = `\u2726 \u{1F48E} A ${money}bounty in your stack \u2014 link below`;
2025
+ if (list[0] && list[0].source === "bounty") headers.unshift(bountyHeader);
2026
+ else headers.push(bountyHeader);
2017
2027
  }
2018
- return [`\u2726 A role that fits your work`, `\u2726 Job match for you \u2014 link in the tip below`];
2028
+ return headers;
2019
2029
  }
2020
2030
  function buildSpinnerPool(topMatches, max = 6, opts = {}) {
2021
2031
  const { sessionTags, frequency = "always" } = opts;
@@ -2120,7 +2130,13 @@ function buildTips(topMatches, baseUrl, max = 8) {
2120
2130
  const pct = Math.max(1, Math.min(99, Math.round((Number(m.score) || 0) * 100)));
2121
2131
  const token = Buffer.from(String(m.id)).toString("base64url");
2122
2132
  const url = `${base}/j/${token}`;
2123
- out.push(`\u2197 ${title} @ ${company} \xB7 ${pct}% \u2014 ${url}`);
2133
+ if (source === "bounty") {
2134
+ const money = m.amountUSD != null ? `$${Number(m.amountUSD).toLocaleString()}` : "$\u2014";
2135
+ const repo = m.repo || companyRaw;
2136
+ out.push(`\u{1F48E} ${money} \xB7 ${title} \xB7 ${repo} \xB7 ${pct}% \u2014 ${url}`);
2137
+ } else {
2138
+ out.push(`\u2197 ${title} @ ${company} \xB7 ${pct}% \u2014 ${url}`);
2139
+ }
2124
2140
  if (out.length >= max) break;
2125
2141
  }
2126
2142
  return out;
@@ -2483,7 +2499,12 @@ async function run() {
2483
2499
  company: r.job.company,
2484
2500
  score: r.score,
2485
2501
  remote: r.job.remote,
2486
- matchedTags: r.matchedTags
2502
+ matchedTags: r.matchedTags,
2503
+ // Bounty fields so the spinner can render bounty framing ($ + 💎).
2504
+ // Public job text, stays LOCAL (same as the rest of topMatches).
2505
+ source: r.job.source,
2506
+ amountUSD: r.job.bounty?.amountUSD,
2507
+ repo: r.job.bounty?.repoFullName
2487
2508
  }));
2488
2509
  }
2489
2510
  } catch {
@@ -90,16 +90,26 @@ function buildContextVerbs(topMatches, sessionTags) {
90
90
  for (const t of sess) {
91
91
  if (roleTags.has(t) && !overlap.includes(t)) overlap.push(t);
92
92
  }
93
+ let headers;
93
94
  if (overlap.length >= 2) {
94
95
  const a = titleCase(overlap[0]);
95
96
  const b = titleCase(overlap[1]);
96
- return [`\u2726 Fits your ${a} + ${b} work`, `\u2726 A role matching what you're building`];
97
- }
98
- if (overlap.length === 1) {
97
+ headers = [`\u2726 Fits your ${a} + ${b} work`, `\u2726 A role matching what you're building`];
98
+ } else if (overlap.length === 1) {
99
99
  const a = titleCase(overlap[0]);
100
- return [`\u2726 A role matching your ${a} work`, `\u2726 Your ${a} work \u2014 link in the tip below`];
100
+ headers = [`\u2726 A role matching your ${a} work`, `\u2726 Your ${a} work \u2014 link in the tip below`];
101
+ } else {
102
+ headers = [`\u2726 A role that fits your work`, `\u2726 Job match for you \u2014 link in the tip below`];
103
+ }
104
+ const list = Array.isArray(topMatches) ? topMatches : [];
105
+ const bounty = list.find((m) => m && m.source === "bounty" && m.amountUSD != null) || list.find((m) => m && m.source === "bounty");
106
+ if (bounty) {
107
+ const money = bounty.amountUSD != null ? `$${bounty.amountUSD.toLocaleString()} ` : "";
108
+ const bountyHeader = `\u2726 \u{1F48E} A ${money}bounty in your stack \u2014 link below`;
109
+ if (list[0] && list[0].source === "bounty") headers.unshift(bountyHeader);
110
+ else headers.push(bountyHeader);
101
111
  }
102
- return [`\u2726 A role that fits your work`, `\u2726 Job match for you \u2014 link in the tip below`];
112
+ return headers;
103
113
  }
104
114
  function buildSpinnerPool(topMatches, max = 6, opts = {}) {
105
115
  const { sessionTags, frequency = "always" } = opts;
@@ -97,16 +97,26 @@ function buildContextVerbs(topMatches, sessionTags) {
97
97
  for (const t of sess) {
98
98
  if (roleTags.has(t) && !overlap.includes(t)) overlap.push(t);
99
99
  }
100
+ let headers;
100
101
  if (overlap.length >= 2) {
101
102
  const a = titleCase(overlap[0]);
102
103
  const b = titleCase(overlap[1]);
103
- return [`\u2726 Fits your ${a} + ${b} work`, `\u2726 A role matching what you're building`];
104
- }
105
- if (overlap.length === 1) {
104
+ headers = [`\u2726 Fits your ${a} + ${b} work`, `\u2726 A role matching what you're building`];
105
+ } else if (overlap.length === 1) {
106
106
  const a = titleCase(overlap[0]);
107
- return [`\u2726 A role matching your ${a} work`, `\u2726 Your ${a} work \u2014 link in the tip below`];
107
+ headers = [`\u2726 A role matching your ${a} work`, `\u2726 Your ${a} work \u2014 link in the tip below`];
108
+ } else {
109
+ headers = [`\u2726 A role that fits your work`, `\u2726 Job match for you \u2014 link in the tip below`];
110
+ }
111
+ const list = Array.isArray(topMatches) ? topMatches : [];
112
+ const bounty = list.find((m) => m && m.source === "bounty" && m.amountUSD != null) || list.find((m) => m && m.source === "bounty");
113
+ if (bounty) {
114
+ const money = bounty.amountUSD != null ? `$${bounty.amountUSD.toLocaleString()} ` : "";
115
+ const bountyHeader = `\u2726 \u{1F48E} A ${money}bounty in your stack \u2014 link below`;
116
+ if (list[0] && list[0].source === "bounty") headers.unshift(bountyHeader);
117
+ else headers.push(bountyHeader);
108
118
  }
109
- return [`\u2726 A role that fits your work`, `\u2726 Job match for you \u2014 link in the tip below`];
119
+ return headers;
110
120
  }
111
121
  function buildSpinnerPool(topMatches, max = 6, opts = {}) {
112
122
  const { sessionTags, frequency = "always" } = opts;
@@ -211,7 +221,13 @@ function buildTips(topMatches, baseUrl, max = 8) {
211
221
  const pct = Math.max(1, Math.min(99, Math.round((Number(m.score) || 0) * 100)));
212
222
  const token = Buffer.from(String(m.id)).toString("base64url");
213
223
  const url = `${base}/j/${token}`;
214
- out.push(`\u2197 ${title} @ ${company} \xB7 ${pct}% \u2014 ${url}`);
224
+ if (source === "bounty") {
225
+ const money = m.amountUSD != null ? `$${Number(m.amountUSD).toLocaleString()}` : "$\u2014";
226
+ const repo = m.repo || companyRaw;
227
+ out.push(`\u{1F48E} ${money} \xB7 ${title} \xB7 ${repo} \xB7 ${pct}% \u2014 ${url}`);
228
+ } else {
229
+ out.push(`\u2197 ${title} @ ${company} \xB7 ${pct}% \u2014 ${url}`);
230
+ }
215
231
  if (out.length >= max) break;
216
232
  }
217
233
  return out;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminalhire",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Local-first job matching for developers — ambient job matches in the Claude Code spinner. Matching runs on your machine; your profile never leaves it.",
5
5
  "repository": {
6
6
  "type": "git",