terminalhire 0.3.3 → 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 +23 -14
- package/dist/bin/jpi-jobs.js +6 -3
- package/dist/bin/jpi-login.js +6 -3
- package/dist/bin/jpi-refresh.js +23 -14
- package/dist/bin/jpi-spinner.js +5 -8
- package/dist/bin/spinner.js +14 -10
- 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,19 +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
|
-
headers.unshift(`\u2726 \u{1F48E} A ${money}bounty in your stack \u2014 link below`);
|
|
3196
|
-
}
|
|
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`);
|
|
3197
3197
|
return headers;
|
|
3198
3198
|
}
|
|
3199
3199
|
function buildSpinnerPool(topMatches, max = 6, opts = {}) {
|
|
@@ -3279,8 +3279,15 @@ function buildTips(topMatches, baseUrl, max = 8) {
|
|
|
3279
3279
|
const perCompany = /* @__PURE__ */ new Map();
|
|
3280
3280
|
const COMPANY_CAP = 2;
|
|
3281
3281
|
const all = Array.isArray(topMatches) ? topMatches : [];
|
|
3282
|
-
const
|
|
3283
|
-
const
|
|
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
|
+
}
|
|
3284
3291
|
for (const m of ordered) {
|
|
3285
3292
|
if (!m || !m.title || !m.company || !m.id) continue;
|
|
3286
3293
|
const idx = String(m.id).indexOf(":");
|
|
@@ -4108,7 +4115,9 @@ async function run10() {
|
|
|
4108
4115
|
const results = match2(fp, jobs, jobs.length);
|
|
4109
4116
|
matchCount = results.length;
|
|
4110
4117
|
const BOUNTY_SLOTS = 5;
|
|
4111
|
-
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);
|
|
4112
4121
|
const roleTop = results.filter((r) => r.job.source !== "bounty").slice(0, 25 - bountyTop.length);
|
|
4113
4122
|
topMatches = [...roleTop, ...bountyTop].map((r) => ({
|
|
4114
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,19 +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
|
-
headers.unshift(`\u2726 \u{1F48E} A ${money}bounty in your stack \u2014 link below`);
|
|
2025
|
-
}
|
|
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`);
|
|
2026
2026
|
return headers;
|
|
2027
2027
|
}
|
|
2028
2028
|
function buildSpinnerPool(topMatches, max = 6, opts = {}) {
|
|
@@ -2108,8 +2108,15 @@ function buildTips(topMatches, baseUrl, max = 8) {
|
|
|
2108
2108
|
const perCompany = /* @__PURE__ */ new Map();
|
|
2109
2109
|
const COMPANY_CAP = 2;
|
|
2110
2110
|
const all = Array.isArray(topMatches) ? topMatches : [];
|
|
2111
|
-
const
|
|
2112
|
-
const
|
|
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
|
+
}
|
|
2113
2120
|
for (const m of ordered) {
|
|
2114
2121
|
if (!m || !m.title || !m.company || !m.id) continue;
|
|
2115
2122
|
const idx = String(m.id).indexOf(":");
|
|
@@ -2495,7 +2502,9 @@ async function run() {
|
|
|
2495
2502
|
const results = match2(fp, jobs, jobs.length);
|
|
2496
2503
|
matchCount = results.length;
|
|
2497
2504
|
const BOUNTY_SLOTS = 5;
|
|
2498
|
-
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);
|
|
2499
2508
|
const roleTop = results.filter((r) => r.job.source !== "bounty").slice(0, 25 - bountyTop.length);
|
|
2500
2509
|
topMatches = [...roleTop, ...bountyTop].map((r) => ({
|
|
2501
2510
|
id: r.job.id,
|
package/dist/bin/jpi-spinner.js
CHANGED
|
@@ -94,19 +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
|
-
headers.unshift(`\u2726 \u{1F48E} A ${money}bounty in your stack \u2014 link below`);
|
|
109
|
-
}
|
|
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`);
|
|
110
107
|
return headers;
|
|
111
108
|
}
|
|
112
109
|
function buildSpinnerPool(topMatches, max = 6, opts = {}) {
|
package/dist/bin/spinner.js
CHANGED
|
@@ -101,19 +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
|
-
headers.unshift(`\u2726 \u{1F48E} A ${money}bounty in your stack \u2014 link below`);
|
|
116
|
-
}
|
|
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`);
|
|
117
114
|
return headers;
|
|
118
115
|
}
|
|
119
116
|
function buildSpinnerPool(topMatches, max = 6, opts = {}) {
|
|
@@ -199,8 +196,15 @@ function buildTips(topMatches, baseUrl, max = 8) {
|
|
|
199
196
|
const perCompany = /* @__PURE__ */ new Map();
|
|
200
197
|
const COMPANY_CAP = 2;
|
|
201
198
|
const all = Array.isArray(topMatches) ? topMatches : [];
|
|
202
|
-
const
|
|
203
|
-
const
|
|
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
|
+
}
|
|
204
208
|
for (const m of ordered) {
|
|
205
209
|
if (!m || !m.title || !m.company || !m.id) continue;
|
|
206
210
|
const idx = String(m.id).indexOf(":");
|
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",
|