terminalhire 0.18.0 → 0.18.2
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 +83 -28
- package/dist/bin/jpi-chat-read.js +11 -2
- package/dist/bin/jpi-chat.js +11 -2
- package/dist/bin/jpi-contribute.js +61 -12
- package/dist/bin/jpi-devs.js +82 -27
- package/dist/bin/jpi-dispatch.js +136 -61
- package/dist/bin/jpi-inbox.js +11 -2
- package/dist/bin/jpi-intro.js +11 -2
- package/dist/bin/jpi-jobs.js +84 -31
- package/dist/bin/jpi-learn.js +11 -2
- package/dist/bin/jpi-login.js +49 -19
- package/dist/bin/jpi-profile.js +11 -2
- package/dist/bin/jpi-project.js +47 -17
- package/dist/bin/jpi-refresh.js +245 -79
- package/dist/bin/jpi-save.js +11 -2
- package/dist/bin/jpi-sync.js +11 -2
- package/dist/bin/jpi-trajectory.js +11 -2
- package/dist/bin/sanitize.js +35 -0
- package/dist/src/chat-client.js +2 -2
- package/dist/src/chat-keystore.js +2 -2
- package/dist/src/github-auth.js +2 -2
- package/dist/src/intro.js +11 -2
- package/dist/src/profile.js +2 -2
- package/dist/src/trajectory.js +11 -2
- package/package.json +1 -1
package/dist/bin/jpi-bounties.js
CHANGED
|
@@ -391,6 +391,11 @@ ${body}`;
|
|
|
391
391
|
for (const tok of tokens) {
|
|
392
392
|
const r = resolveToken(tok);
|
|
393
393
|
if (!r) continue;
|
|
394
|
+
const synGate = AMBIGUOUS_SYNONYM[tok] ?? AMBIGUOUS_SYNONYM[tok.replace(/^[.\-+#]+|[.\-+#]+$/g, "")];
|
|
395
|
+
if (synGate && r.id === synGate.id) {
|
|
396
|
+
if (synGate.cue.test(text)) ids.add(r.id);
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
394
399
|
if (NON_EXTRACTABLE.has(r.id)) continue;
|
|
395
400
|
if (SYNONYM_ONLY.has(r.id) && !r.viaSynonym) continue;
|
|
396
401
|
const cue = AMBIGUOUS[r.id];
|
|
@@ -408,7 +413,7 @@ ${body}`;
|
|
|
408
413
|
function coreTagsFromTitle(title) {
|
|
409
414
|
return extractSkillTags(title, "").filter((t) => !SOFT_DOMAIN.has(t));
|
|
410
415
|
}
|
|
411
|
-
var SOFT_DOMAIN, SYNONYM_ONLY, NON_EXTRACTABLE, AMBIGUOUS, ENG_INTENT, NON_ENG_TITLE;
|
|
416
|
+
var SOFT_DOMAIN, SYNONYM_ONLY, NON_EXTRACTABLE, AMBIGUOUS, AMBIGUOUS_SYNONYM, ENG_INTENT, NON_ENG_TITLE;
|
|
412
417
|
var init_extract = __esm({
|
|
413
418
|
"../../packages/core/src/vocab/extract.ts"() {
|
|
414
419
|
"use strict";
|
|
@@ -445,6 +450,16 @@ var init_extract = __esm({
|
|
|
445
450
|
r: /\b(rstudio|tidyverse|ggplot|shiny|dplyr|cran|r-lang|rlang)\b/i,
|
|
446
451
|
ml: /\b(machine[\s-]?learning|pytorch|tensorflow|scikit|sklearn|keras|neural|model training|deep[\s-]?learning|numpy|pandas|ml\s+(?:engineer|platform|researcher|infrastructure)|(?:ml|ai)\s+research)\b/i
|
|
447
452
|
};
|
|
453
|
+
AMBIGUOUS_SYNONYM = {
|
|
454
|
+
// "prompt" is the LLM skill only in AI context. In raw JD prose it is "prompt
|
|
455
|
+
// delivery / response / payment / communication / attention". Accept only with
|
|
456
|
+
// the explicit "prompt engineering" phrase OR an LLM/AI ecosystem cue; the cue
|
|
457
|
+
// deliberately excludes the bare word "prompt(s)" itself so it can't self-satisfy.
|
|
458
|
+
prompt: {
|
|
459
|
+
id: "prompt-engineering",
|
|
460
|
+
cue: /\bprompt[\s-]?engineer(?:ing|s)?\b|\b(llms?|gpt-?[0-9o]*|claude|gemini|llama|mistral|openai|anthropic|langchain|llama[\s-]?index|rag|retrieval[\s-]?augmented|embeddings?|fine[\s-]?tun(?:e|ed|ing)|vector[\s-]?(?:db|database|store)|agentic|ai agents?|chatbots?|generative ai|gen[\s-]?ai|genai|few[\s-]?shot|zero[\s-]?shot)\b/i
|
|
461
|
+
}
|
|
462
|
+
};
|
|
448
463
|
ENG_INTENT = /\b(engineer|engineering|developer|dev\b|swe|sde|programmer|architect|full[\s-]?stack|front[\s-]?end|back[\s-]?end|devops|sre|software|coding|codebase|technical staff|tech(?:nical)? lead)\b/i;
|
|
449
464
|
NON_ENG_TITLE = /\b(account executive|account manager|sales (?:rep|representative|development|manager|lead)|sdr|bdr|recruiter|recruiting|talent|marketing|administrative|business partner|billing coordinator|operations (?:administrator|coordinator)|customer success|project finance|controller|bookkeeper|graphic|brand)\b/i;
|
|
450
465
|
}
|
|
@@ -921,6 +936,7 @@ async function fetchRepoMeta(owner, name, token, cache) {
|
|
|
921
936
|
stars: r.stargazers_count ?? 0,
|
|
922
937
|
archived: !!r.archived,
|
|
923
938
|
fork: !!r.fork,
|
|
939
|
+
private: !!r.private,
|
|
924
940
|
language: r.language ?? null,
|
|
925
941
|
topics: r.topics ?? [],
|
|
926
942
|
contributors
|
|
@@ -953,7 +969,7 @@ async function computeAcceptanceFromSearch(login, token, ownedOrgs, cache, gates
|
|
|
953
969
|
const loginLc = login.toLowerCase();
|
|
954
970
|
let items;
|
|
955
971
|
try {
|
|
956
|
-
const q = encodeURIComponent(`type:pr is:merged author:${login} -user:${login} sort:updated`);
|
|
972
|
+
const q = encodeURIComponent(`type:pr is:merged is:public author:${login} -user:${login} sort:updated`);
|
|
957
973
|
const res = await ghFetch(
|
|
958
974
|
`/search/issues?q=${q}&per_page=${CANDIDATE_PR_PAGE}`,
|
|
959
975
|
token
|
|
@@ -966,6 +982,7 @@ async function computeAcceptanceFromSearch(login, token, ownedOrgs, cache, gates
|
|
|
966
982
|
}
|
|
967
983
|
const byDomain = {};
|
|
968
984
|
let qualifyingTotal = 0;
|
|
985
|
+
const qualifyingPRs = [];
|
|
969
986
|
for (const item of items) {
|
|
970
987
|
const repo = parseRepoUrl(item.repository_url);
|
|
971
988
|
if (!repo) continue;
|
|
@@ -975,13 +992,22 @@ async function computeAcceptanceFromSearch(login, token, ownedOrgs, cache, gates
|
|
|
975
992
|
if (isTrivialPRTitle(item.title)) continue;
|
|
976
993
|
const meta = await fetchRepoMeta(repo.owner, repo.name, token, cache);
|
|
977
994
|
if (!meta) continue;
|
|
995
|
+
if (meta.private) continue;
|
|
978
996
|
if (meta.archived || meta.fork) continue;
|
|
979
997
|
if (meta.stars < gates.minStars) continue;
|
|
980
998
|
if (meta.contributors !== void 0 && meta.contributors < gates.minContributors) continue;
|
|
981
999
|
qualifyingTotal += 1;
|
|
982
1000
|
const mergedAt = item.pull_request?.merged_at ?? item.closed_at ?? item.created_at;
|
|
983
1001
|
const rawDomains = [meta.language ?? "", ...meta.topics].filter(Boolean);
|
|
984
|
-
|
|
1002
|
+
const domainTags = [...new Set(normalize(rawDomains))];
|
|
1003
|
+
qualifyingPRs.push({
|
|
1004
|
+
url: item.html_url,
|
|
1005
|
+
title: item.title,
|
|
1006
|
+
repo: `${repo.owner}/${repo.name}`,
|
|
1007
|
+
domains: domainTags,
|
|
1008
|
+
mergedAt
|
|
1009
|
+
});
|
|
1010
|
+
for (const d of domainTags) {
|
|
985
1011
|
const b = byDomain[d] ?? (byDomain[d] = { mergedPRs: 0, distinctOrgs: 0, lastMergedAt: mergedAt, orgs: /* @__PURE__ */ new Set() });
|
|
986
1012
|
b.mergedPRs += 1;
|
|
987
1013
|
b.orgs.add(ownerLc);
|
|
@@ -996,7 +1022,7 @@ async function computeAcceptanceFromSearch(login, token, ownedOrgs, cache, gates
|
|
|
996
1022
|
lastMergedAt: b.lastMergedAt
|
|
997
1023
|
};
|
|
998
1024
|
}
|
|
999
|
-
return { status: "ok", byDomain: finalDomains, qualifyingTotal, computedAt };
|
|
1025
|
+
return { status: "ok", byDomain: finalDomains, qualifyingTotal, qualifyingPRs, computedAt };
|
|
1000
1026
|
}
|
|
1001
1027
|
async function computeAcceptanceCredential(login, token, cache = /* @__PURE__ */ new Map()) {
|
|
1002
1028
|
if (!token) return emptyCredential("no-token");
|
|
@@ -1807,6 +1833,19 @@ var init_concurrency = __esm({
|
|
|
1807
1833
|
}
|
|
1808
1834
|
});
|
|
1809
1835
|
|
|
1836
|
+
// ../../packages/core/src/feeds/effort.ts
|
|
1837
|
+
function effortFromAmount(amount) {
|
|
1838
|
+
if (amount == null) return void 0;
|
|
1839
|
+
if (amount <= 500) return "small";
|
|
1840
|
+
if (amount <= 2e3) return "medium";
|
|
1841
|
+
return "large";
|
|
1842
|
+
}
|
|
1843
|
+
var init_effort = __esm({
|
|
1844
|
+
"../../packages/core/src/feeds/effort.ts"() {
|
|
1845
|
+
"use strict";
|
|
1846
|
+
}
|
|
1847
|
+
});
|
|
1848
|
+
|
|
1810
1849
|
// ../../packages/core/src/feeds/github-bounties.ts
|
|
1811
1850
|
function authHeaders() {
|
|
1812
1851
|
const token = process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"];
|
|
@@ -1829,12 +1868,6 @@ function parseAmountUSD(text) {
|
|
|
1829
1868
|
if (!Number.isFinite(n) || n <= 0 || n > 1e6) return void 0;
|
|
1830
1869
|
return Math.round(n);
|
|
1831
1870
|
}
|
|
1832
|
-
function effortFromAmount(amount) {
|
|
1833
|
-
if (amount == null) return void 0;
|
|
1834
|
-
if (amount <= 500) return "small";
|
|
1835
|
-
if (amount <= 2e3) return "medium";
|
|
1836
|
-
return "large";
|
|
1837
|
-
}
|
|
1838
1871
|
function labelNames(issue) {
|
|
1839
1872
|
return (issue.labels ?? []).map((l) => typeof l === "string" ? l : l.name ?? "").filter(Boolean);
|
|
1840
1873
|
}
|
|
@@ -2082,6 +2115,7 @@ var init_github_bounties = __esm({
|
|
|
2082
2115
|
init_bounty_gate();
|
|
2083
2116
|
init_http();
|
|
2084
2117
|
init_concurrency();
|
|
2118
|
+
init_effort();
|
|
2085
2119
|
GITHUB_API = "https://api.github.com";
|
|
2086
2120
|
BOUNTY_LABEL_RE = /bounty|reward|funded|💎|💰/i;
|
|
2087
2121
|
SEARCH_QUERIES = [
|
|
@@ -2133,12 +2167,6 @@ var init_github_bounties = __esm({
|
|
|
2133
2167
|
function tokenize3(text) {
|
|
2134
2168
|
return text.toLowerCase().replace(/[^a-z0-9.\-+#]/g, " ").split(/\s+/).filter((w) => w.length > 1);
|
|
2135
2169
|
}
|
|
2136
|
-
function effortFromAmount2(usd) {
|
|
2137
|
-
if (usd == null) return void 0;
|
|
2138
|
-
if (usd < 150) return "small";
|
|
2139
|
-
if (usd < 750) return "medium";
|
|
2140
|
-
return "large";
|
|
2141
|
-
}
|
|
2142
2170
|
function priceToUSD(p) {
|
|
2143
2171
|
if (!p || typeof p.value !== "number") return void 0;
|
|
2144
2172
|
if (p.unit === "USD_CENT") return Math.round(p.value) / 100;
|
|
@@ -2161,6 +2189,7 @@ var init_opire = __esm({
|
|
|
2161
2189
|
init_bounty_gate();
|
|
2162
2190
|
init_github_bounties();
|
|
2163
2191
|
init_http();
|
|
2192
|
+
init_effort();
|
|
2164
2193
|
OPIRE_REWARDS_URL = "https://api.opire.dev/rewards";
|
|
2165
2194
|
MIN_USD = 25;
|
|
2166
2195
|
MAX_USD = 25e3;
|
|
@@ -2197,7 +2226,7 @@ var init_opire = __esm({
|
|
|
2197
2226
|
const tags = normalize([...r.programmingLanguages ?? [], ...tokenize3(title)]);
|
|
2198
2227
|
const bounty = {
|
|
2199
2228
|
amountUSD,
|
|
2200
|
-
estimatedEffort:
|
|
2229
|
+
estimatedEffort: effortFromAmount(amountUSD),
|
|
2201
2230
|
bountySource: "opire",
|
|
2202
2231
|
claimUrl: r.url,
|
|
2203
2232
|
repoFullName
|
|
@@ -6544,6 +6573,7 @@ __export(src_exports, {
|
|
|
6544
6573
|
isBounty: () => isBounty,
|
|
6545
6574
|
isContribution: () => isContribution,
|
|
6546
6575
|
isOverIntroLimit: () => isOverIntroLimit,
|
|
6576
|
+
isTrivialPRTitle: () => isTrivialPRTitle,
|
|
6547
6577
|
joinLabels: () => joinLabels,
|
|
6548
6578
|
labelFor: () => labelFor,
|
|
6549
6579
|
lever: () => lever,
|
|
@@ -6600,9 +6630,9 @@ var init_keytar = __esm({
|
|
|
6600
6630
|
}
|
|
6601
6631
|
});
|
|
6602
6632
|
|
|
6603
|
-
// node-file:/Users/ericgang/job-placement-inline
|
|
6633
|
+
// node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node
|
|
6604
6634
|
var require_keytar = __commonJS({
|
|
6605
|
-
"node-file:/Users/ericgang/job-placement-inline
|
|
6635
|
+
"node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node"(exports, module) {
|
|
6606
6636
|
"use strict";
|
|
6607
6637
|
init_keytar();
|
|
6608
6638
|
try {
|
|
@@ -6928,6 +6958,37 @@ function updateIndexCache(patch) {
|
|
|
6928
6958
|
return entry;
|
|
6929
6959
|
}
|
|
6930
6960
|
|
|
6961
|
+
// bin/sanitize.js
|
|
6962
|
+
var CONTROL_CHARS = /[\x00-\x1f\x7f-\x9f]/g;
|
|
6963
|
+
function sanitizeText(s) {
|
|
6964
|
+
if (s == null) return "";
|
|
6965
|
+
return String(s).replace(CONTROL_CHARS, "");
|
|
6966
|
+
}
|
|
6967
|
+
function safeHttpUrl(url) {
|
|
6968
|
+
if (url == null) return null;
|
|
6969
|
+
const raw = String(url);
|
|
6970
|
+
CONTROL_CHARS.lastIndex = 0;
|
|
6971
|
+
if (CONTROL_CHARS.test(raw)) return null;
|
|
6972
|
+
let parsed;
|
|
6973
|
+
try {
|
|
6974
|
+
parsed = new URL(raw);
|
|
6975
|
+
} catch {
|
|
6976
|
+
return null;
|
|
6977
|
+
}
|
|
6978
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
6979
|
+
return parsed.href;
|
|
6980
|
+
}
|
|
6981
|
+
function linkTitle(title, url) {
|
|
6982
|
+
const safeTitle = sanitizeText(title);
|
|
6983
|
+
const href = safeHttpUrl(url);
|
|
6984
|
+
const isTTY = process.stdout.isTTY;
|
|
6985
|
+
const noColor = process.env["NO_COLOR"] !== void 0;
|
|
6986
|
+
if (isTTY && !noColor && href) {
|
|
6987
|
+
return `\x1B]8;;${href}\x1B\\${safeTitle}\x1B]8;;\x1B\\`;
|
|
6988
|
+
}
|
|
6989
|
+
return href ? `${safeTitle} (${href})` : safeTitle;
|
|
6990
|
+
}
|
|
6991
|
+
|
|
6931
6992
|
// bin/jpi-bounties.js
|
|
6932
6993
|
var TERMINALHIRE_DIR3 = process.env.TERMINALHIRE_DIR || join4(homedir3(), ".terminalhire");
|
|
6933
6994
|
var INDEX_CACHE_FILE2 = join4(TERMINALHIRE_DIR3, "index-cache.json");
|
|
@@ -6974,12 +7035,6 @@ function formatAmount(b) {
|
|
|
6974
7035
|
return b.amountUSD != null ? "$" + b.amountUSD.toLocaleString() : "$\u2014";
|
|
6975
7036
|
}
|
|
6976
7037
|
var EFFORT_LABEL = { small: "small (~\xBD day)", medium: "medium (~1 day)", large: "large (multi-day)" };
|
|
6977
|
-
function linkTitle(title, url) {
|
|
6978
|
-
const isTTY = process.stdout.isTTY;
|
|
6979
|
-
const noColor = process.env["NO_COLOR"] !== void 0;
|
|
6980
|
-
if (isTTY && !noColor && url) return `\x1B]8;;${url}\x1B\\${title}\x1B]8;;\x1B\\`;
|
|
6981
|
-
return url ? `${title} (${url})` : title;
|
|
6982
|
-
}
|
|
6983
7038
|
function printBounty(i, job, score, reason, matchedTags) {
|
|
6984
7039
|
const b = job.bounty ?? {};
|
|
6985
7040
|
const stars = b.repoStars != null ? ` \xB7 ${b.repoStars}\u2605` : "";
|
|
@@ -6990,11 +7045,11 @@ function printBounty(i, job, score, reason, matchedTags) {
|
|
|
6990
7045
|
const ref = opportunityShortToken(job.id);
|
|
6991
7046
|
console.log(`
|
|
6992
7047
|
${i + 1}. ${linkTitle(job.title, job.url)} [${ref}]`);
|
|
6993
|
-
console.log(` ${formatAmount(b)}${effort} \xB7 ${b.repoFullName ?? job.company}${stars}${scoreStr}${contend}`);
|
|
7048
|
+
console.log(` ${formatAmount(b)}${effort} \xB7 ${sanitizeText(b.repoFullName ?? job.company)}${stars}${scoreStr}${contend}`);
|
|
6994
7049
|
if (reason) console.log(` ${reason}`);
|
|
6995
7050
|
if (matchedTags && matchedTags.length) console.log(` Tags matched: ${matchedTags.slice(0, 5).join(", ")}`);
|
|
6996
7051
|
console.log(` id: ${job.id}`);
|
|
6997
|
-
console.log(` Claim: ${b.claimUrl ?? job.url}`);
|
|
7052
|
+
console.log(` Claim: ${sanitizeText(b.claimUrl ?? job.url)}`);
|
|
6998
7053
|
console.log(` \u2192 terminalhire claim ${ref}`);
|
|
6999
7054
|
}
|
|
7000
7055
|
async function run() {
|
|
@@ -7050,7 +7105,7 @@ Enter a number to open a bounty's claim page, or press Enter to exit: `);
|
|
|
7050
7105
|
console.log(
|
|
7051
7106
|
`
|
|
7052
7107
|
Open this to claim/work the bounty (you go straight to the source \u2014 we never touch payment):
|
|
7053
|
-
${chosen.bounty?.claimUrl ?? chosen.url}`
|
|
7108
|
+
${sanitizeText(chosen.bounty?.claimUrl ?? chosen.url)}`
|
|
7054
7109
|
);
|
|
7055
7110
|
} catch (err) {
|
|
7056
7111
|
console.error("terminalhire bounties error:", err.message ?? err);
|
|
@@ -559,6 +559,13 @@ var init_concurrency = __esm({
|
|
|
559
559
|
}
|
|
560
560
|
});
|
|
561
561
|
|
|
562
|
+
// ../../packages/core/src/feeds/effort.ts
|
|
563
|
+
var init_effort = __esm({
|
|
564
|
+
"../../packages/core/src/feeds/effort.ts"() {
|
|
565
|
+
"use strict";
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
|
|
562
569
|
// ../../packages/core/src/feeds/github-bounties.ts
|
|
563
570
|
var init_github_bounties = __esm({
|
|
564
571
|
"../../packages/core/src/feeds/github-bounties.ts"() {
|
|
@@ -568,6 +575,7 @@ var init_github_bounties = __esm({
|
|
|
568
575
|
init_bounty_gate();
|
|
569
576
|
init_http();
|
|
570
577
|
init_concurrency();
|
|
578
|
+
init_effort();
|
|
571
579
|
}
|
|
572
580
|
});
|
|
573
581
|
|
|
@@ -579,6 +587,7 @@ var init_opire = __esm({
|
|
|
579
587
|
init_bounty_gate();
|
|
580
588
|
init_github_bounties();
|
|
581
589
|
init_http();
|
|
590
|
+
init_effort();
|
|
582
591
|
}
|
|
583
592
|
});
|
|
584
593
|
|
|
@@ -3925,9 +3934,9 @@ var init_keytar = __esm({
|
|
|
3925
3934
|
}
|
|
3926
3935
|
});
|
|
3927
3936
|
|
|
3928
|
-
// node-file:/Users/ericgang/job-placement-inline
|
|
3937
|
+
// node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node
|
|
3929
3938
|
var require_keytar = __commonJS({
|
|
3930
|
-
"node-file:/Users/ericgang/job-placement-inline
|
|
3939
|
+
"node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node"(exports, module) {
|
|
3931
3940
|
"use strict";
|
|
3932
3941
|
init_keytar();
|
|
3933
3942
|
try {
|
package/dist/bin/jpi-chat.js
CHANGED
|
@@ -576,6 +576,13 @@ var init_concurrency = __esm({
|
|
|
576
576
|
}
|
|
577
577
|
});
|
|
578
578
|
|
|
579
|
+
// ../../packages/core/src/feeds/effort.ts
|
|
580
|
+
var init_effort = __esm({
|
|
581
|
+
"../../packages/core/src/feeds/effort.ts"() {
|
|
582
|
+
"use strict";
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
|
|
579
586
|
// ../../packages/core/src/feeds/github-bounties.ts
|
|
580
587
|
var init_github_bounties = __esm({
|
|
581
588
|
"../../packages/core/src/feeds/github-bounties.ts"() {
|
|
@@ -585,6 +592,7 @@ var init_github_bounties = __esm({
|
|
|
585
592
|
init_bounty_gate();
|
|
586
593
|
init_http();
|
|
587
594
|
init_concurrency();
|
|
595
|
+
init_effort();
|
|
588
596
|
}
|
|
589
597
|
});
|
|
590
598
|
|
|
@@ -596,6 +604,7 @@ var init_opire = __esm({
|
|
|
596
604
|
init_bounty_gate();
|
|
597
605
|
init_github_bounties();
|
|
598
606
|
init_http();
|
|
607
|
+
init_effort();
|
|
599
608
|
}
|
|
600
609
|
});
|
|
601
610
|
|
|
@@ -3953,9 +3962,9 @@ var init_keytar = __esm({
|
|
|
3953
3962
|
}
|
|
3954
3963
|
});
|
|
3955
3964
|
|
|
3956
|
-
// node-file:/Users/ericgang/job-placement-inline
|
|
3965
|
+
// node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node
|
|
3957
3966
|
var require_keytar = __commonJS({
|
|
3958
|
-
"node-file:/Users/ericgang/job-placement-inline
|
|
3967
|
+
"node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node"(exports, module) {
|
|
3959
3968
|
"use strict";
|
|
3960
3969
|
init_keytar();
|
|
3961
3970
|
try {
|
|
@@ -385,6 +385,11 @@ ${body}`;
|
|
|
385
385
|
for (const tok of tokens) {
|
|
386
386
|
const r = resolveToken(tok);
|
|
387
387
|
if (!r) continue;
|
|
388
|
+
const synGate = AMBIGUOUS_SYNONYM[tok] ?? AMBIGUOUS_SYNONYM[tok.replace(/^[.\-+#]+|[.\-+#]+$/g, "")];
|
|
389
|
+
if (synGate && r.id === synGate.id) {
|
|
390
|
+
if (synGate.cue.test(text)) ids.add(r.id);
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
388
393
|
if (NON_EXTRACTABLE.has(r.id)) continue;
|
|
389
394
|
if (SYNONYM_ONLY.has(r.id) && !r.viaSynonym) continue;
|
|
390
395
|
const cue = AMBIGUOUS[r.id];
|
|
@@ -402,7 +407,7 @@ ${body}`;
|
|
|
402
407
|
function coreTagsFromTitle(title) {
|
|
403
408
|
return extractSkillTags(title, "").filter((t) => !SOFT_DOMAIN.has(t));
|
|
404
409
|
}
|
|
405
|
-
var SOFT_DOMAIN, SYNONYM_ONLY, NON_EXTRACTABLE, AMBIGUOUS, ENG_INTENT, NON_ENG_TITLE;
|
|
410
|
+
var SOFT_DOMAIN, SYNONYM_ONLY, NON_EXTRACTABLE, AMBIGUOUS, AMBIGUOUS_SYNONYM, ENG_INTENT, NON_ENG_TITLE;
|
|
406
411
|
var init_extract = __esm({
|
|
407
412
|
"../../packages/core/src/vocab/extract.ts"() {
|
|
408
413
|
"use strict";
|
|
@@ -439,6 +444,16 @@ var init_extract = __esm({
|
|
|
439
444
|
r: /\b(rstudio|tidyverse|ggplot|shiny|dplyr|cran|r-lang|rlang)\b/i,
|
|
440
445
|
ml: /\b(machine[\s-]?learning|pytorch|tensorflow|scikit|sklearn|keras|neural|model training|deep[\s-]?learning|numpy|pandas|ml\s+(?:engineer|platform|researcher|infrastructure)|(?:ml|ai)\s+research)\b/i
|
|
441
446
|
};
|
|
447
|
+
AMBIGUOUS_SYNONYM = {
|
|
448
|
+
// "prompt" is the LLM skill only in AI context. In raw JD prose it is "prompt
|
|
449
|
+
// delivery / response / payment / communication / attention". Accept only with
|
|
450
|
+
// the explicit "prompt engineering" phrase OR an LLM/AI ecosystem cue; the cue
|
|
451
|
+
// deliberately excludes the bare word "prompt(s)" itself so it can't self-satisfy.
|
|
452
|
+
prompt: {
|
|
453
|
+
id: "prompt-engineering",
|
|
454
|
+
cue: /\bprompt[\s-]?engineer(?:ing|s)?\b|\b(llms?|gpt-?[0-9o]*|claude|gemini|llama|mistral|openai|anthropic|langchain|llama[\s-]?index|rag|retrieval[\s-]?augmented|embeddings?|fine[\s-]?tun(?:e|ed|ing)|vector[\s-]?(?:db|database|store)|agentic|ai agents?|chatbots?|generative ai|gen[\s-]?ai|genai|few[\s-]?shot|zero[\s-]?shot)\b/i
|
|
455
|
+
}
|
|
456
|
+
};
|
|
442
457
|
ENG_INTENT = /\b(engineer|engineering|developer|dev\b|swe|sde|programmer|architect|full[\s-]?stack|front[\s-]?end|back[\s-]?end|devops|sre|software|coding|codebase|technical staff|tech(?:nical)? lead)\b/i;
|
|
443
458
|
NON_ENG_TITLE = /\b(account executive|account manager|sales (?:rep|representative|development|manager|lead)|sdr|bdr|recruiter|recruiting|talent|marketing|administrative|business partner|billing coordinator|operations (?:administrator|coordinator)|customer success|project finance|controller|bookkeeper|graphic|brand)\b/i;
|
|
444
459
|
}
|
|
@@ -894,6 +909,13 @@ var init_concurrency = __esm({
|
|
|
894
909
|
}
|
|
895
910
|
});
|
|
896
911
|
|
|
912
|
+
// ../../packages/core/src/feeds/effort.ts
|
|
913
|
+
var init_effort = __esm({
|
|
914
|
+
"../../packages/core/src/feeds/effort.ts"() {
|
|
915
|
+
"use strict";
|
|
916
|
+
}
|
|
917
|
+
});
|
|
918
|
+
|
|
897
919
|
// ../../packages/core/src/feeds/github-bounties.ts
|
|
898
920
|
var init_github_bounties = __esm({
|
|
899
921
|
"../../packages/core/src/feeds/github-bounties.ts"() {
|
|
@@ -903,6 +925,7 @@ var init_github_bounties = __esm({
|
|
|
903
925
|
init_bounty_gate();
|
|
904
926
|
init_http();
|
|
905
927
|
init_concurrency();
|
|
928
|
+
init_effort();
|
|
906
929
|
}
|
|
907
930
|
});
|
|
908
931
|
|
|
@@ -914,6 +937,7 @@ var init_opire = __esm({
|
|
|
914
937
|
init_bounty_gate();
|
|
915
938
|
init_github_bounties();
|
|
916
939
|
init_http();
|
|
940
|
+
init_effort();
|
|
917
941
|
}
|
|
918
942
|
});
|
|
919
943
|
|
|
@@ -1242,9 +1266,9 @@ var init_keytar = __esm({
|
|
|
1242
1266
|
}
|
|
1243
1267
|
});
|
|
1244
1268
|
|
|
1245
|
-
// node-file:/Users/ericgang/job-placement-inline
|
|
1269
|
+
// node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node
|
|
1246
1270
|
var require_keytar = __commonJS({
|
|
1247
|
-
"node-file:/Users/ericgang/job-placement-inline
|
|
1271
|
+
"node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node"(exports, module) {
|
|
1248
1272
|
"use strict";
|
|
1249
1273
|
init_keytar();
|
|
1250
1274
|
try {
|
|
@@ -1608,6 +1632,37 @@ function isContributeEnabled() {
|
|
|
1608
1632
|
return readConfig().contributeEnabled === true;
|
|
1609
1633
|
}
|
|
1610
1634
|
|
|
1635
|
+
// bin/sanitize.js
|
|
1636
|
+
var CONTROL_CHARS = /[\x00-\x1f\x7f-\x9f]/g;
|
|
1637
|
+
function sanitizeText(s) {
|
|
1638
|
+
if (s == null) return "";
|
|
1639
|
+
return String(s).replace(CONTROL_CHARS, "");
|
|
1640
|
+
}
|
|
1641
|
+
function safeHttpUrl(url) {
|
|
1642
|
+
if (url == null) return null;
|
|
1643
|
+
const raw = String(url);
|
|
1644
|
+
CONTROL_CHARS.lastIndex = 0;
|
|
1645
|
+
if (CONTROL_CHARS.test(raw)) return null;
|
|
1646
|
+
let parsed;
|
|
1647
|
+
try {
|
|
1648
|
+
parsed = new URL(raw);
|
|
1649
|
+
} catch {
|
|
1650
|
+
return null;
|
|
1651
|
+
}
|
|
1652
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
1653
|
+
return parsed.href;
|
|
1654
|
+
}
|
|
1655
|
+
function linkTitle(title, url) {
|
|
1656
|
+
const safeTitle = sanitizeText(title);
|
|
1657
|
+
const href = safeHttpUrl(url);
|
|
1658
|
+
const isTTY = process.stdout.isTTY;
|
|
1659
|
+
const noColor = process.env["NO_COLOR"] !== void 0;
|
|
1660
|
+
if (isTTY && !noColor && href) {
|
|
1661
|
+
return `\x1B]8;;${href}\x1B\\${safeTitle}\x1B]8;;\x1B\\`;
|
|
1662
|
+
}
|
|
1663
|
+
return href ? `${safeTitle} (${href})` : safeTitle;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1611
1666
|
// bin/jpi-contribute.js
|
|
1612
1667
|
var TERMINALHIRE_DIR4 = process.env.TERMINALHIRE_DIR || join5(homedir4(), ".terminalhire");
|
|
1613
1668
|
var INDEX_CACHE_FILE2 = join5(TERMINALHIRE_DIR4, "index-cache.json");
|
|
@@ -1709,19 +1764,13 @@ function displayLanguage(job) {
|
|
|
1709
1764
|
const tag = (job.tags ?? []).find((t) => LANGUAGES.has(String(t).toLowerCase()));
|
|
1710
1765
|
return tag ?? "\u2014";
|
|
1711
1766
|
}
|
|
1712
|
-
function linkTitle(title, url) {
|
|
1713
|
-
const isTTY = process.stdout.isTTY;
|
|
1714
|
-
const noColor = process.env["NO_COLOR"] !== void 0;
|
|
1715
|
-
if (isTTY && !noColor && url) return `\x1B]8;;${url}\x1B\\${title}\x1B]8;;\x1B\\`;
|
|
1716
|
-
return url ? `${title} (${url})` : title;
|
|
1717
|
-
}
|
|
1718
1767
|
function renderRow(i, result) {
|
|
1719
1768
|
const job = result.job;
|
|
1720
1769
|
const c = job.contribution ?? {};
|
|
1721
|
-
const repo = c.repoFullName ?? job.company ?? "";
|
|
1770
|
+
const repo = sanitizeText(c.repoFullName ?? job.company ?? "");
|
|
1722
1771
|
const num = c.issueNumber != null ? `#${c.issueNumber}` : "";
|
|
1723
|
-
const label = c.labels && c.labels.length ? c.labels[0] : "\u2014";
|
|
1724
|
-
const lang = displayLanguage(job);
|
|
1772
|
+
const label = c.labels && c.labels.length ? sanitizeText(c.labels[0]) : "\u2014";
|
|
1773
|
+
const lang = sanitizeText(displayLanguage(job));
|
|
1725
1774
|
const scorePct = `match ${Math.round((result.score ?? 0) * 100)}%`;
|
|
1726
1775
|
const ref = opportunityShortToken(job.id);
|
|
1727
1776
|
const line1 = `${i + 1}. ${linkTitle(job.title, c.issueUrl ?? job.url)} [${ref}]`;
|