terminalhire 0.3.2 → 0.3.5
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/dist/bin/jpi-bounties.js +6 -3
- package/dist/bin/jpi-dispatch.js +25 -15
- package/dist/bin/jpi-jobs.js +6 -3
- package/dist/bin/jpi-login.js +6 -3
- package/dist/bin/jpi-refresh.js +25 -15
- package/dist/bin/jpi-spinner.js +5 -10
- package/dist/bin/spinner.js +16 -11
- package/package.json +1 -1
package/dist/bin/jpi-bounties.js
CHANGED
|
@@ -905,10 +905,13 @@ function parseRss(xml) {
|
|
|
905
905
|
return decodeEntities(plainMatch?.[1].trim() ?? "");
|
|
906
906
|
};
|
|
907
907
|
const rawTitle = get("title");
|
|
908
|
-
const
|
|
909
|
-
|
|
910
|
-
const titleAfterColon =
|
|
908
|
+
const m = rawTitle.match(/^(.*?):\s+(.*)$/);
|
|
909
|
+
let company = m ? m[1].trim() : "Unknown";
|
|
910
|
+
const titleAfterColon = m ? m[2].trim() : rawTitle;
|
|
911
911
|
const title = titleAfterColon.replace(/\s*\([^)]*\)\s*$/, "").trim();
|
|
912
|
+
if (/^https?:\/\//i.test(company)) {
|
|
913
|
+
company = company.replace(/^https?:\/\//i, "").replace(/\/.*$/, "").trim() || "Unknown";
|
|
914
|
+
}
|
|
912
915
|
items.push({
|
|
913
916
|
title,
|
|
914
917
|
link: get("link") || get("guid"),
|
package/dist/bin/jpi-dispatch.js
CHANGED
|
@@ -1119,10 +1119,13 @@ function parseRss(xml) {
|
|
|
1119
1119
|
return decodeEntities(plainMatch?.[1].trim() ?? "");
|
|
1120
1120
|
};
|
|
1121
1121
|
const rawTitle = get("title");
|
|
1122
|
-
const
|
|
1123
|
-
|
|
1124
|
-
const titleAfterColon =
|
|
1122
|
+
const m = rawTitle.match(/^(.*?):\s+(.*)$/);
|
|
1123
|
+
let company = m ? m[1].trim() : "Unknown";
|
|
1124
|
+
const titleAfterColon = m ? m[2].trim() : rawTitle;
|
|
1125
1125
|
const title = titleAfterColon.replace(/\s*\([^)]*\)\s*$/, "").trim();
|
|
1126
|
+
if (/^https?:\/\//i.test(company)) {
|
|
1127
|
+
company = company.replace(/^https?:\/\//i, "").replace(/\/.*$/, "").trim() || "Unknown";
|
|
1128
|
+
}
|
|
1126
1129
|
items.push({
|
|
1127
1130
|
title,
|
|
1128
1131
|
link: get("link") || get("guid"),
|
|
@@ -3181,21 +3184,16 @@ function buildContextVerbs(topMatches, sessionTags) {
|
|
|
3181
3184
|
if (overlap.length >= 2) {
|
|
3182
3185
|
const a = titleCase(overlap[0]);
|
|
3183
3186
|
const b = titleCase(overlap[1]);
|
|
3184
|
-
headers = [`\u2726 Fits your ${a} + ${b} work`, `\u2726 A
|
|
3187
|
+
headers = [`\u2726 Fits your ${a} + ${b} work`, `\u2726 A match for what you're building \u2014 link below`];
|
|
3185
3188
|
} else if (overlap.length === 1) {
|
|
3186
3189
|
const a = titleCase(overlap[0]);
|
|
3187
|
-
headers = [`\u2726
|
|
3190
|
+
headers = [`\u2726 Work in your ${a} stack \u2014 link below`, `\u2726 Your ${a} work \u2014 link in the tip below`];
|
|
3188
3191
|
} else {
|
|
3189
|
-
headers = [`\u2726
|
|
3192
|
+
headers = [`\u2726 Work that fits your stack`, `\u2726 A match for you \u2014 link in the tip below`];
|
|
3190
3193
|
}
|
|
3191
3194
|
const list = Array.isArray(topMatches) ? topMatches : [];
|
|
3192
|
-
const
|
|
3193
|
-
if (
|
|
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);
|
|
3198
|
-
}
|
|
3195
|
+
const hasBounty = list.some((m) => m && m.source === "bounty");
|
|
3196
|
+
if (hasBounty) headers.unshift(`\u2726 Roles + \u{1F48E} paid bounties in your stack \u2014 link below`);
|
|
3199
3197
|
return headers;
|
|
3200
3198
|
}
|
|
3201
3199
|
function buildSpinnerPool(topMatches, max = 6, opts = {}) {
|
|
@@ -3280,7 +3278,17 @@ function buildTips(topMatches, baseUrl, max = 8) {
|
|
|
3280
3278
|
const seenRole = /* @__PURE__ */ new Set();
|
|
3281
3279
|
const perCompany = /* @__PURE__ */ new Map();
|
|
3282
3280
|
const COMPANY_CAP = 2;
|
|
3283
|
-
|
|
3281
|
+
const all = Array.isArray(topMatches) ? topMatches : [];
|
|
3282
|
+
const bountyQ = all.filter((m) => m && m.source === "bounty");
|
|
3283
|
+
const roleQ = interleaveBySource(all.filter((m) => m && m.source !== "bounty"));
|
|
3284
|
+
const ordered = [];
|
|
3285
|
+
let bi = 0;
|
|
3286
|
+
let ri = 0;
|
|
3287
|
+
while (bi < bountyQ.length || ri < roleQ.length) {
|
|
3288
|
+
if (bi < bountyQ.length) ordered.push(bountyQ[bi++]);
|
|
3289
|
+
if (ri < roleQ.length) ordered.push(roleQ[ri++]);
|
|
3290
|
+
}
|
|
3291
|
+
for (const m of ordered) {
|
|
3284
3292
|
if (!m || !m.title || !m.company || !m.id) continue;
|
|
3285
3293
|
const idx = String(m.id).indexOf(":");
|
|
3286
3294
|
if (idx <= 0) continue;
|
|
@@ -4107,7 +4115,9 @@ async function run10() {
|
|
|
4107
4115
|
const results = match2(fp, jobs, jobs.length);
|
|
4108
4116
|
matchCount = results.length;
|
|
4109
4117
|
const BOUNTY_SLOTS = 5;
|
|
4110
|
-
const
|
|
4118
|
+
const bountyMatches = results.filter((r) => r.job.source === "bounty");
|
|
4119
|
+
const rot = bountyMatches.length > 0 ? Math.floor(Date.now() / (5 * 60 * 1e3)) % bountyMatches.length : 0;
|
|
4120
|
+
const bountyTop = [...bountyMatches.slice(rot), ...bountyMatches.slice(0, rot)].slice(0, BOUNTY_SLOTS);
|
|
4111
4121
|
const roleTop = results.filter((r) => r.job.source !== "bounty").slice(0, 25 - bountyTop.length);
|
|
4112
4122
|
topMatches = [...roleTop, ...bountyTop].map((r) => ({
|
|
4113
4123
|
id: r.job.id,
|
package/dist/bin/jpi-jobs.js
CHANGED
|
@@ -905,10 +905,13 @@ function parseRss(xml) {
|
|
|
905
905
|
return decodeEntities(plainMatch?.[1].trim() ?? "");
|
|
906
906
|
};
|
|
907
907
|
const rawTitle = get("title");
|
|
908
|
-
const
|
|
909
|
-
|
|
910
|
-
const titleAfterColon =
|
|
908
|
+
const m = rawTitle.match(/^(.*?):\s+(.*)$/);
|
|
909
|
+
let company = m ? m[1].trim() : "Unknown";
|
|
910
|
+
const titleAfterColon = m ? m[2].trim() : rawTitle;
|
|
911
911
|
const title = titleAfterColon.replace(/\s*\([^)]*\)\s*$/, "").trim();
|
|
912
|
+
if (/^https?:\/\//i.test(company)) {
|
|
913
|
+
company = company.replace(/^https?:\/\//i, "").replace(/\/.*$/, "").trim() || "Unknown";
|
|
914
|
+
}
|
|
912
915
|
items.push({
|
|
913
916
|
title,
|
|
914
917
|
link: get("link") || get("guid"),
|
package/dist/bin/jpi-login.js
CHANGED
|
@@ -1119,10 +1119,13 @@ function parseRss(xml) {
|
|
|
1119
1119
|
return decodeEntities(plainMatch?.[1].trim() ?? "");
|
|
1120
1120
|
};
|
|
1121
1121
|
const rawTitle = get("title");
|
|
1122
|
-
const
|
|
1123
|
-
|
|
1124
|
-
const titleAfterColon =
|
|
1122
|
+
const m = rawTitle.match(/^(.*?):\s+(.*)$/);
|
|
1123
|
+
let company = m ? m[1].trim() : "Unknown";
|
|
1124
|
+
const titleAfterColon = m ? m[2].trim() : rawTitle;
|
|
1125
1125
|
const title = titleAfterColon.replace(/\s*\([^)]*\)\s*$/, "").trim();
|
|
1126
|
+
if (/^https?:\/\//i.test(company)) {
|
|
1127
|
+
company = company.replace(/^https?:\/\//i, "").replace(/\/.*$/, "").trim() || "Unknown";
|
|
1128
|
+
}
|
|
1126
1129
|
items.push({
|
|
1127
1130
|
title,
|
|
1128
1131
|
link: get("link") || get("guid"),
|
package/dist/bin/jpi-refresh.js
CHANGED
|
@@ -905,10 +905,13 @@ function parseRss(xml) {
|
|
|
905
905
|
return decodeEntities(plainMatch?.[1].trim() ?? "");
|
|
906
906
|
};
|
|
907
907
|
const rawTitle = get("title");
|
|
908
|
-
const
|
|
909
|
-
|
|
910
|
-
const titleAfterColon =
|
|
908
|
+
const m = rawTitle.match(/^(.*?):\s+(.*)$/);
|
|
909
|
+
let company = m ? m[1].trim() : "Unknown";
|
|
910
|
+
const titleAfterColon = m ? m[2].trim() : rawTitle;
|
|
911
911
|
const title = titleAfterColon.replace(/\s*\([^)]*\)\s*$/, "").trim();
|
|
912
|
+
if (/^https?:\/\//i.test(company)) {
|
|
913
|
+
company = company.replace(/^https?:\/\//i, "").replace(/\/.*$/, "").trim() || "Unknown";
|
|
914
|
+
}
|
|
912
915
|
items.push({
|
|
913
916
|
title,
|
|
914
917
|
link: get("link") || get("guid"),
|
|
@@ -2010,21 +2013,16 @@ function buildContextVerbs(topMatches, sessionTags) {
|
|
|
2010
2013
|
if (overlap.length >= 2) {
|
|
2011
2014
|
const a = titleCase(overlap[0]);
|
|
2012
2015
|
const b = titleCase(overlap[1]);
|
|
2013
|
-
headers = [`\u2726 Fits your ${a} + ${b} work`, `\u2726 A
|
|
2016
|
+
headers = [`\u2726 Fits your ${a} + ${b} work`, `\u2726 A match for what you're building \u2014 link below`];
|
|
2014
2017
|
} else if (overlap.length === 1) {
|
|
2015
2018
|
const a = titleCase(overlap[0]);
|
|
2016
|
-
headers = [`\u2726
|
|
2019
|
+
headers = [`\u2726 Work in your ${a} stack \u2014 link below`, `\u2726 Your ${a} work \u2014 link in the tip below`];
|
|
2017
2020
|
} else {
|
|
2018
|
-
headers = [`\u2726
|
|
2021
|
+
headers = [`\u2726 Work that fits your stack`, `\u2726 A match for you \u2014 link in the tip below`];
|
|
2019
2022
|
}
|
|
2020
2023
|
const list = Array.isArray(topMatches) ? topMatches : [];
|
|
2021
|
-
const
|
|
2022
|
-
if (
|
|
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);
|
|
2027
|
-
}
|
|
2024
|
+
const hasBounty = list.some((m) => m && m.source === "bounty");
|
|
2025
|
+
if (hasBounty) headers.unshift(`\u2726 Roles + \u{1F48E} paid bounties in your stack \u2014 link below`);
|
|
2028
2026
|
return headers;
|
|
2029
2027
|
}
|
|
2030
2028
|
function buildSpinnerPool(topMatches, max = 6, opts = {}) {
|
|
@@ -2109,7 +2107,17 @@ function buildTips(topMatches, baseUrl, max = 8) {
|
|
|
2109
2107
|
const seenRole = /* @__PURE__ */ new Set();
|
|
2110
2108
|
const perCompany = /* @__PURE__ */ new Map();
|
|
2111
2109
|
const COMPANY_CAP = 2;
|
|
2112
|
-
|
|
2110
|
+
const all = Array.isArray(topMatches) ? topMatches : [];
|
|
2111
|
+
const bountyQ = all.filter((m) => m && m.source === "bounty");
|
|
2112
|
+
const roleQ = interleaveBySource(all.filter((m) => m && m.source !== "bounty"));
|
|
2113
|
+
const ordered = [];
|
|
2114
|
+
let bi = 0;
|
|
2115
|
+
let ri = 0;
|
|
2116
|
+
while (bi < bountyQ.length || ri < roleQ.length) {
|
|
2117
|
+
if (bi < bountyQ.length) ordered.push(bountyQ[bi++]);
|
|
2118
|
+
if (ri < roleQ.length) ordered.push(roleQ[ri++]);
|
|
2119
|
+
}
|
|
2120
|
+
for (const m of ordered) {
|
|
2113
2121
|
if (!m || !m.title || !m.company || !m.id) continue;
|
|
2114
2122
|
const idx = String(m.id).indexOf(":");
|
|
2115
2123
|
if (idx <= 0) continue;
|
|
@@ -2494,7 +2502,9 @@ async function run() {
|
|
|
2494
2502
|
const results = match2(fp, jobs, jobs.length);
|
|
2495
2503
|
matchCount = results.length;
|
|
2496
2504
|
const BOUNTY_SLOTS = 5;
|
|
2497
|
-
const
|
|
2505
|
+
const bountyMatches = results.filter((r) => r.job.source === "bounty");
|
|
2506
|
+
const rot = bountyMatches.length > 0 ? Math.floor(Date.now() / (5 * 60 * 1e3)) % bountyMatches.length : 0;
|
|
2507
|
+
const bountyTop = [...bountyMatches.slice(rot), ...bountyMatches.slice(0, rot)].slice(0, BOUNTY_SLOTS);
|
|
2498
2508
|
const roleTop = results.filter((r) => r.job.source !== "bounty").slice(0, 25 - bountyTop.length);
|
|
2499
2509
|
topMatches = [...roleTop, ...bountyTop].map((r) => ({
|
|
2500
2510
|
id: r.job.id,
|
package/dist/bin/jpi-spinner.js
CHANGED
|
@@ -94,21 +94,16 @@ function buildContextVerbs(topMatches, sessionTags) {
|
|
|
94
94
|
if (overlap.length >= 2) {
|
|
95
95
|
const a = titleCase(overlap[0]);
|
|
96
96
|
const b = titleCase(overlap[1]);
|
|
97
|
-
headers = [`\u2726 Fits your ${a} + ${b} work`, `\u2726 A
|
|
97
|
+
headers = [`\u2726 Fits your ${a} + ${b} work`, `\u2726 A match for what you're building \u2014 link below`];
|
|
98
98
|
} else if (overlap.length === 1) {
|
|
99
99
|
const a = titleCase(overlap[0]);
|
|
100
|
-
headers = [`\u2726
|
|
100
|
+
headers = [`\u2726 Work in your ${a} stack \u2014 link below`, `\u2726 Your ${a} work \u2014 link in the tip below`];
|
|
101
101
|
} else {
|
|
102
|
-
headers = [`\u2726
|
|
102
|
+
headers = [`\u2726 Work that fits your stack`, `\u2726 A match for you \u2014 link in the tip below`];
|
|
103
103
|
}
|
|
104
104
|
const list = Array.isArray(topMatches) ? topMatches : [];
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
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);
|
|
111
|
-
}
|
|
105
|
+
const hasBounty = list.some((m) => m && m.source === "bounty");
|
|
106
|
+
if (hasBounty) headers.unshift(`\u2726 Roles + \u{1F48E} paid bounties in your stack \u2014 link below`);
|
|
112
107
|
return headers;
|
|
113
108
|
}
|
|
114
109
|
function buildSpinnerPool(topMatches, max = 6, opts = {}) {
|
package/dist/bin/spinner.js
CHANGED
|
@@ -101,21 +101,16 @@ function buildContextVerbs(topMatches, sessionTags) {
|
|
|
101
101
|
if (overlap.length >= 2) {
|
|
102
102
|
const a = titleCase(overlap[0]);
|
|
103
103
|
const b = titleCase(overlap[1]);
|
|
104
|
-
headers = [`\u2726 Fits your ${a} + ${b} work`, `\u2726 A
|
|
104
|
+
headers = [`\u2726 Fits your ${a} + ${b} work`, `\u2726 A match for what you're building \u2014 link below`];
|
|
105
105
|
} else if (overlap.length === 1) {
|
|
106
106
|
const a = titleCase(overlap[0]);
|
|
107
|
-
headers = [`\u2726
|
|
107
|
+
headers = [`\u2726 Work in your ${a} stack \u2014 link below`, `\u2726 Your ${a} work \u2014 link in the tip below`];
|
|
108
108
|
} else {
|
|
109
|
-
headers = [`\u2726
|
|
109
|
+
headers = [`\u2726 Work that fits your stack`, `\u2726 A match for you \u2014 link in the tip below`];
|
|
110
110
|
}
|
|
111
111
|
const list = Array.isArray(topMatches) ? topMatches : [];
|
|
112
|
-
const
|
|
113
|
-
if (
|
|
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);
|
|
118
|
-
}
|
|
112
|
+
const hasBounty = list.some((m) => m && m.source === "bounty");
|
|
113
|
+
if (hasBounty) headers.unshift(`\u2726 Roles + \u{1F48E} paid bounties in your stack \u2014 link below`);
|
|
119
114
|
return headers;
|
|
120
115
|
}
|
|
121
116
|
function buildSpinnerPool(topMatches, max = 6, opts = {}) {
|
|
@@ -200,7 +195,17 @@ function buildTips(topMatches, baseUrl, max = 8) {
|
|
|
200
195
|
const seenRole = /* @__PURE__ */ new Set();
|
|
201
196
|
const perCompany = /* @__PURE__ */ new Map();
|
|
202
197
|
const COMPANY_CAP = 2;
|
|
203
|
-
|
|
198
|
+
const all = Array.isArray(topMatches) ? topMatches : [];
|
|
199
|
+
const bountyQ = all.filter((m) => m && m.source === "bounty");
|
|
200
|
+
const roleQ = interleaveBySource(all.filter((m) => m && m.source !== "bounty"));
|
|
201
|
+
const ordered = [];
|
|
202
|
+
let bi = 0;
|
|
203
|
+
let ri = 0;
|
|
204
|
+
while (bi < bountyQ.length || ri < roleQ.length) {
|
|
205
|
+
if (bi < bountyQ.length) ordered.push(bountyQ[bi++]);
|
|
206
|
+
if (ri < roleQ.length) ordered.push(roleQ[ri++]);
|
|
207
|
+
}
|
|
208
|
+
for (const m of ordered) {
|
|
204
209
|
if (!m || !m.title || !m.company || !m.id) continue;
|
|
205
210
|
const idx = String(m.id).indexOf(":");
|
|
206
211
|
if (idx <= 0) continue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "terminalhire",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
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",
|