terminalhire 0.18.1 → 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 +16 -1
- package/dist/bin/jpi-contribute.js +16 -1
- package/dist/bin/jpi-devs.js +16 -1
- package/dist/bin/jpi-dispatch.js +47 -2
- package/dist/bin/jpi-jobs.js +16 -1
- package/dist/bin/jpi-login.js +16 -1
- package/dist/bin/jpi-project.js +16 -1
- package/dist/bin/jpi-refresh.js +212 -61
- 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
|
}
|
|
@@ -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
|
}
|
package/dist/bin/jpi-devs.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
|
}
|
package/dist/bin/jpi-dispatch.js
CHANGED
|
@@ -917,6 +917,11 @@ ${body}`;
|
|
|
917
917
|
for (const tok of tokens) {
|
|
918
918
|
const r = resolveToken(tok);
|
|
919
919
|
if (!r) continue;
|
|
920
|
+
const synGate = AMBIGUOUS_SYNONYM[tok] ?? AMBIGUOUS_SYNONYM[tok.replace(/^[.\-+#]+|[.\-+#]+$/g, "")];
|
|
921
|
+
if (synGate && r.id === synGate.id) {
|
|
922
|
+
if (synGate.cue.test(text)) ids.add(r.id);
|
|
923
|
+
continue;
|
|
924
|
+
}
|
|
920
925
|
if (NON_EXTRACTABLE.has(r.id)) continue;
|
|
921
926
|
if (SYNONYM_ONLY.has(r.id) && !r.viaSynonym) continue;
|
|
922
927
|
const cue = AMBIGUOUS[r.id];
|
|
@@ -934,7 +939,7 @@ ${body}`;
|
|
|
934
939
|
function coreTagsFromTitle(title) {
|
|
935
940
|
return extractSkillTags(title, "").filter((t) => !SOFT_DOMAIN.has(t));
|
|
936
941
|
}
|
|
937
|
-
var SOFT_DOMAIN, SYNONYM_ONLY, NON_EXTRACTABLE, AMBIGUOUS, ENG_INTENT, NON_ENG_TITLE;
|
|
942
|
+
var SOFT_DOMAIN, SYNONYM_ONLY, NON_EXTRACTABLE, AMBIGUOUS, AMBIGUOUS_SYNONYM, ENG_INTENT, NON_ENG_TITLE;
|
|
938
943
|
var init_extract = __esm({
|
|
939
944
|
"../../packages/core/src/vocab/extract.ts"() {
|
|
940
945
|
"use strict";
|
|
@@ -971,6 +976,16 @@ var init_extract = __esm({
|
|
|
971
976
|
r: /\b(rstudio|tidyverse|ggplot|shiny|dplyr|cran|r-lang|rlang)\b/i,
|
|
972
977
|
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
|
|
973
978
|
};
|
|
979
|
+
AMBIGUOUS_SYNONYM = {
|
|
980
|
+
// "prompt" is the LLM skill only in AI context. In raw JD prose it is "prompt
|
|
981
|
+
// delivery / response / payment / communication / attention". Accept only with
|
|
982
|
+
// the explicit "prompt engineering" phrase OR an LLM/AI ecosystem cue; the cue
|
|
983
|
+
// deliberately excludes the bare word "prompt(s)" itself so it can't self-satisfy.
|
|
984
|
+
prompt: {
|
|
985
|
+
id: "prompt-engineering",
|
|
986
|
+
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
|
|
987
|
+
}
|
|
988
|
+
};
|
|
974
989
|
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;
|
|
975
990
|
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;
|
|
976
991
|
}
|
|
@@ -7931,6 +7946,13 @@ var init_jpi_login = __esm({
|
|
|
7931
7946
|
});
|
|
7932
7947
|
|
|
7933
7948
|
// bin/job-status-store.js
|
|
7949
|
+
var job_status_store_exports = {};
|
|
7950
|
+
__export(job_status_store_exports, {
|
|
7951
|
+
markClicked: () => markClicked,
|
|
7952
|
+
markStatus: () => markStatus,
|
|
7953
|
+
readStatusMap: () => readStatusMap,
|
|
7954
|
+
statusFilePath: () => statusFilePath
|
|
7955
|
+
});
|
|
7934
7956
|
import {
|
|
7935
7957
|
readFileSync as readFileSync7,
|
|
7936
7958
|
writeFileSync as writeFileSync5,
|
|
@@ -7944,6 +7966,9 @@ import {
|
|
|
7944
7966
|
} from "fs";
|
|
7945
7967
|
import { join as join7, dirname } from "path";
|
|
7946
7968
|
import { homedir as homedir6 } from "os";
|
|
7969
|
+
function statusFilePath() {
|
|
7970
|
+
return STATUS_FILE;
|
|
7971
|
+
}
|
|
7947
7972
|
function atomicWriteJson(path, obj) {
|
|
7948
7973
|
mkdirSync5(dirname(path), { recursive: true });
|
|
7949
7974
|
const tmp = `${path}.tmp-${process.pid}`;
|
|
@@ -38044,6 +38069,23 @@ var init_match_slots = __esm({
|
|
|
38044
38069
|
}
|
|
38045
38070
|
});
|
|
38046
38071
|
|
|
38072
|
+
// bin/job-status-suppress.js
|
|
38073
|
+
function isEngaged(rec) {
|
|
38074
|
+
if (!rec) return false;
|
|
38075
|
+
if (rec.status === "applied" || rec.status === "dismissed") return true;
|
|
38076
|
+
if (rec.status === "saved") return false;
|
|
38077
|
+
return rec.clicked === true;
|
|
38078
|
+
}
|
|
38079
|
+
function suppressEngaged(results, statusMap) {
|
|
38080
|
+
const map = statusMap || {};
|
|
38081
|
+
return results.filter((r) => !isEngaged(map[r?.job?.id]));
|
|
38082
|
+
}
|
|
38083
|
+
var init_job_status_suppress = __esm({
|
|
38084
|
+
"bin/job-status-suppress.js"() {
|
|
38085
|
+
"use strict";
|
|
38086
|
+
}
|
|
38087
|
+
});
|
|
38088
|
+
|
|
38047
38089
|
// bin/jpi-refresh.js
|
|
38048
38090
|
var jpi_refresh_exports = {};
|
|
38049
38091
|
__export(jpi_refresh_exports, {
|
|
@@ -38086,6 +38128,7 @@ async function run21() {
|
|
|
38086
38128
|
try {
|
|
38087
38129
|
const { readProfile: readProfile2, profileToFingerprint: profileToFingerprint2 } = await Promise.resolve().then(() => (init_profile(), profile_exports));
|
|
38088
38130
|
const { match: match2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
38131
|
+
const { readStatusMap: readStatusMap2 } = await Promise.resolve().then(() => (init_job_status_store(), job_status_store_exports));
|
|
38089
38132
|
const profile = await readProfile2();
|
|
38090
38133
|
if (profile.skillTags.length > 0 && jobs.length > 0) {
|
|
38091
38134
|
const fp = profileToFingerprint2(profile);
|
|
@@ -38094,7 +38137,8 @@ async function run21() {
|
|
|
38094
38137
|
contributeNudge.onRamp = true;
|
|
38095
38138
|
}
|
|
38096
38139
|
const pool = contribute.length > 0 ? [...jobs, ...contribute] : jobs;
|
|
38097
|
-
|
|
38140
|
+
let results = match2(fp, pool, pool.length);
|
|
38141
|
+
results = suppressEngaged(results, readStatusMap2());
|
|
38098
38142
|
matchCount = results.length;
|
|
38099
38143
|
const { roleTop, bountyTop, contributeTop } = budgetSlots(results, {
|
|
38100
38144
|
thinProfile,
|
|
@@ -38282,6 +38326,7 @@ var init_jpi_refresh = __esm({
|
|
|
38282
38326
|
init_directory2();
|
|
38283
38327
|
init_cache_store();
|
|
38284
38328
|
init_match_slots();
|
|
38329
|
+
init_job_status_suppress();
|
|
38285
38330
|
init_config();
|
|
38286
38331
|
init_web_session();
|
|
38287
38332
|
GH_SESSION_COOKIE7 = "__jpi_gh_session";
|
package/dist/bin/jpi-jobs.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
|
}
|
package/dist/bin/jpi-login.js
CHANGED
|
@@ -696,6 +696,11 @@ ${body}`;
|
|
|
696
696
|
for (const tok of tokens) {
|
|
697
697
|
const r = resolveToken(tok);
|
|
698
698
|
if (!r) continue;
|
|
699
|
+
const synGate = AMBIGUOUS_SYNONYM[tok] ?? AMBIGUOUS_SYNONYM[tok.replace(/^[.\-+#]+|[.\-+#]+$/g, "")];
|
|
700
|
+
if (synGate && r.id === synGate.id) {
|
|
701
|
+
if (synGate.cue.test(text)) ids.add(r.id);
|
|
702
|
+
continue;
|
|
703
|
+
}
|
|
699
704
|
if (NON_EXTRACTABLE.has(r.id)) continue;
|
|
700
705
|
if (SYNONYM_ONLY.has(r.id) && !r.viaSynonym) continue;
|
|
701
706
|
const cue = AMBIGUOUS[r.id];
|
|
@@ -713,7 +718,7 @@ ${body}`;
|
|
|
713
718
|
function coreTagsFromTitle(title) {
|
|
714
719
|
return extractSkillTags(title, "").filter((t) => !SOFT_DOMAIN.has(t));
|
|
715
720
|
}
|
|
716
|
-
var SOFT_DOMAIN, SYNONYM_ONLY, NON_EXTRACTABLE, AMBIGUOUS, ENG_INTENT, NON_ENG_TITLE;
|
|
721
|
+
var SOFT_DOMAIN, SYNONYM_ONLY, NON_EXTRACTABLE, AMBIGUOUS, AMBIGUOUS_SYNONYM, ENG_INTENT, NON_ENG_TITLE;
|
|
717
722
|
var init_extract = __esm({
|
|
718
723
|
"../../packages/core/src/vocab/extract.ts"() {
|
|
719
724
|
"use strict";
|
|
@@ -750,6 +755,16 @@ var init_extract = __esm({
|
|
|
750
755
|
r: /\b(rstudio|tidyverse|ggplot|shiny|dplyr|cran|r-lang|rlang)\b/i,
|
|
751
756
|
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
|
|
752
757
|
};
|
|
758
|
+
AMBIGUOUS_SYNONYM = {
|
|
759
|
+
// "prompt" is the LLM skill only in AI context. In raw JD prose it is "prompt
|
|
760
|
+
// delivery / response / payment / communication / attention". Accept only with
|
|
761
|
+
// the explicit "prompt engineering" phrase OR an LLM/AI ecosystem cue; the cue
|
|
762
|
+
// deliberately excludes the bare word "prompt(s)" itself so it can't self-satisfy.
|
|
763
|
+
prompt: {
|
|
764
|
+
id: "prompt-engineering",
|
|
765
|
+
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
|
|
766
|
+
}
|
|
767
|
+
};
|
|
753
768
|
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;
|
|
754
769
|
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;
|
|
755
770
|
}
|
package/dist/bin/jpi-project.js
CHANGED
|
@@ -362,6 +362,11 @@ ${body}`;
|
|
|
362
362
|
for (const tok of tokens) {
|
|
363
363
|
const r = resolveToken(tok);
|
|
364
364
|
if (!r) continue;
|
|
365
|
+
const synGate = AMBIGUOUS_SYNONYM[tok] ?? AMBIGUOUS_SYNONYM[tok.replace(/^[.\-+#]+|[.\-+#]+$/g, "")];
|
|
366
|
+
if (synGate && r.id === synGate.id) {
|
|
367
|
+
if (synGate.cue.test(text)) ids.add(r.id);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
365
370
|
if (NON_EXTRACTABLE.has(r.id)) continue;
|
|
366
371
|
if (SYNONYM_ONLY.has(r.id) && !r.viaSynonym) continue;
|
|
367
372
|
const cue = AMBIGUOUS[r.id];
|
|
@@ -379,7 +384,7 @@ ${body}`;
|
|
|
379
384
|
function coreTagsFromTitle(title) {
|
|
380
385
|
return extractSkillTags(title, "").filter((t) => !SOFT_DOMAIN.has(t));
|
|
381
386
|
}
|
|
382
|
-
var SOFT_DOMAIN, SYNONYM_ONLY, NON_EXTRACTABLE, AMBIGUOUS, ENG_INTENT, NON_ENG_TITLE;
|
|
387
|
+
var SOFT_DOMAIN, SYNONYM_ONLY, NON_EXTRACTABLE, AMBIGUOUS, AMBIGUOUS_SYNONYM, ENG_INTENT, NON_ENG_TITLE;
|
|
383
388
|
var init_extract = __esm({
|
|
384
389
|
"../../packages/core/src/vocab/extract.ts"() {
|
|
385
390
|
"use strict";
|
|
@@ -416,6 +421,16 @@ var init_extract = __esm({
|
|
|
416
421
|
r: /\b(rstudio|tidyverse|ggplot|shiny|dplyr|cran|r-lang|rlang)\b/i,
|
|
417
422
|
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
|
|
418
423
|
};
|
|
424
|
+
AMBIGUOUS_SYNONYM = {
|
|
425
|
+
// "prompt" is the LLM skill only in AI context. In raw JD prose it is "prompt
|
|
426
|
+
// delivery / response / payment / communication / attention". Accept only with
|
|
427
|
+
// the explicit "prompt engineering" phrase OR an LLM/AI ecosystem cue; the cue
|
|
428
|
+
// deliberately excludes the bare word "prompt(s)" itself so it can't self-satisfy.
|
|
429
|
+
prompt: {
|
|
430
|
+
id: "prompt-engineering",
|
|
431
|
+
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
|
|
432
|
+
}
|
|
433
|
+
};
|
|
419
434
|
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;
|
|
420
435
|
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;
|
|
421
436
|
}
|
package/dist/bin/jpi-refresh.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
|
}
|
|
@@ -6906,14 +6921,136 @@ var init_profile = __esm({
|
|
|
6906
6921
|
}
|
|
6907
6922
|
});
|
|
6908
6923
|
|
|
6924
|
+
// bin/job-status-store.js
|
|
6925
|
+
var job_status_store_exports = {};
|
|
6926
|
+
__export(job_status_store_exports, {
|
|
6927
|
+
markClicked: () => markClicked,
|
|
6928
|
+
markStatus: () => markStatus,
|
|
6929
|
+
readStatusMap: () => readStatusMap,
|
|
6930
|
+
statusFilePath: () => statusFilePath
|
|
6931
|
+
});
|
|
6932
|
+
import {
|
|
6933
|
+
readFileSync as readFileSync7,
|
|
6934
|
+
writeFileSync as writeFileSync6,
|
|
6935
|
+
renameSync as renameSync2,
|
|
6936
|
+
mkdirSync as mkdirSync6,
|
|
6937
|
+
existsSync as existsSync4,
|
|
6938
|
+
copyFileSync,
|
|
6939
|
+
openSync,
|
|
6940
|
+
closeSync,
|
|
6941
|
+
unlinkSync
|
|
6942
|
+
} from "fs";
|
|
6943
|
+
import { join as join7, dirname } from "path";
|
|
6944
|
+
import { homedir as homedir6 } from "os";
|
|
6945
|
+
function statusFilePath() {
|
|
6946
|
+
return STATUS_FILE;
|
|
6947
|
+
}
|
|
6948
|
+
function atomicWriteJson(path, obj) {
|
|
6949
|
+
mkdirSync6(dirname(path), { recursive: true });
|
|
6950
|
+
const tmp = `${path}.tmp-${process.pid}`;
|
|
6951
|
+
writeFileSync6(tmp, JSON.stringify(obj, null, 2) + "\n", "utf8");
|
|
6952
|
+
renameSync2(tmp, path);
|
|
6953
|
+
}
|
|
6954
|
+
function sleepMs(ms) {
|
|
6955
|
+
try {
|
|
6956
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
6957
|
+
} catch {
|
|
6958
|
+
const end = Date.now() + ms;
|
|
6959
|
+
while (Date.now() < end) {
|
|
6960
|
+
}
|
|
6961
|
+
}
|
|
6962
|
+
}
|
|
6963
|
+
function withLock(fn) {
|
|
6964
|
+
const deadline = Date.now() + 2e3;
|
|
6965
|
+
for (; ; ) {
|
|
6966
|
+
let fd;
|
|
6967
|
+
try {
|
|
6968
|
+
mkdirSync6(dirname(LOCK_FILE), { recursive: true });
|
|
6969
|
+
fd = openSync(LOCK_FILE, "wx");
|
|
6970
|
+
} catch (err) {
|
|
6971
|
+
if (err && err.code === "EEXIST") {
|
|
6972
|
+
if (Date.now() > deadline) {
|
|
6973
|
+
try {
|
|
6974
|
+
unlinkSync(LOCK_FILE);
|
|
6975
|
+
} catch {
|
|
6976
|
+
}
|
|
6977
|
+
continue;
|
|
6978
|
+
}
|
|
6979
|
+
sleepMs(5);
|
|
6980
|
+
continue;
|
|
6981
|
+
}
|
|
6982
|
+
throw err;
|
|
6983
|
+
}
|
|
6984
|
+
try {
|
|
6985
|
+
return fn();
|
|
6986
|
+
} finally {
|
|
6987
|
+
try {
|
|
6988
|
+
closeSync(fd);
|
|
6989
|
+
} catch {
|
|
6990
|
+
}
|
|
6991
|
+
try {
|
|
6992
|
+
unlinkSync(LOCK_FILE);
|
|
6993
|
+
} catch {
|
|
6994
|
+
}
|
|
6995
|
+
}
|
|
6996
|
+
}
|
|
6997
|
+
}
|
|
6998
|
+
function readStatusMap() {
|
|
6999
|
+
if (!existsSync4(STATUS_FILE)) return {};
|
|
7000
|
+
let raw;
|
|
7001
|
+
try {
|
|
7002
|
+
raw = readFileSync7(STATUS_FILE, "utf8");
|
|
7003
|
+
} catch {
|
|
7004
|
+
return {};
|
|
7005
|
+
}
|
|
7006
|
+
try {
|
|
7007
|
+
const parsed = JSON.parse(raw);
|
|
7008
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
7009
|
+
throw new Error("status store is not an object");
|
|
7010
|
+
} catch {
|
|
7011
|
+
try {
|
|
7012
|
+
copyFileSync(STATUS_FILE, BAK_FILE);
|
|
7013
|
+
} catch {
|
|
7014
|
+
}
|
|
7015
|
+
return {};
|
|
7016
|
+
}
|
|
7017
|
+
}
|
|
7018
|
+
function markStatus(id, status) {
|
|
7019
|
+
return withLock(() => {
|
|
7020
|
+
const current = readStatusMap();
|
|
7021
|
+
const next = setStatus(current, id, status);
|
|
7022
|
+
atomicWriteJson(STATUS_FILE, next);
|
|
7023
|
+
return next[id];
|
|
7024
|
+
});
|
|
7025
|
+
}
|
|
7026
|
+
function markClicked(id) {
|
|
7027
|
+
return withLock(() => {
|
|
7028
|
+
const current = readStatusMap();
|
|
7029
|
+
const next = recordClick(current, id);
|
|
7030
|
+
atomicWriteJson(STATUS_FILE, next);
|
|
7031
|
+
return next[id];
|
|
7032
|
+
});
|
|
7033
|
+
}
|
|
7034
|
+
var TERMINALHIRE_DIR5, STATUS_FILE, LOCK_FILE, BAK_FILE;
|
|
7035
|
+
var init_job_status_store = __esm({
|
|
7036
|
+
"bin/job-status-store.js"() {
|
|
7037
|
+
"use strict";
|
|
7038
|
+
init_src();
|
|
7039
|
+
TERMINALHIRE_DIR5 = process.env.TERMINALHIRE_DIR || join7(homedir6(), ".terminalhire");
|
|
7040
|
+
STATUS_FILE = join7(TERMINALHIRE_DIR5, "job-status.json");
|
|
7041
|
+
LOCK_FILE = `${STATUS_FILE}.lock`;
|
|
7042
|
+
BAK_FILE = `${STATUS_FILE}.bak`;
|
|
7043
|
+
}
|
|
7044
|
+
});
|
|
7045
|
+
|
|
6909
7046
|
// src/signal.ts
|
|
6910
7047
|
var signal_exports = {};
|
|
6911
7048
|
__export(signal_exports, {
|
|
6912
7049
|
extractFingerprint: () => extractFingerprint
|
|
6913
7050
|
});
|
|
6914
|
-
import { readFileSync as
|
|
7051
|
+
import { readFileSync as readFileSync8, readdirSync } from "fs";
|
|
6915
7052
|
import { execFileSync } from "child_process";
|
|
6916
|
-
import { join as
|
|
7053
|
+
import { join as join8 } from "path";
|
|
6917
7054
|
function safeGit(args, cwd) {
|
|
6918
7055
|
try {
|
|
6919
7056
|
return execFileSync("git", ["-C", cwd, ...args], {
|
|
@@ -6941,20 +7078,20 @@ function isEmployerContext(cwd) {
|
|
|
6941
7078
|
}
|
|
6942
7079
|
function readJsonSafe(path) {
|
|
6943
7080
|
try {
|
|
6944
|
-
return JSON.parse(
|
|
7081
|
+
return JSON.parse(readFileSync8(path, "utf8"));
|
|
6945
7082
|
} catch {
|
|
6946
7083
|
return null;
|
|
6947
7084
|
}
|
|
6948
7085
|
}
|
|
6949
7086
|
function readFileSafe(path) {
|
|
6950
7087
|
try {
|
|
6951
|
-
return
|
|
7088
|
+
return readFileSync8(path, "utf8");
|
|
6952
7089
|
} catch {
|
|
6953
7090
|
return "";
|
|
6954
7091
|
}
|
|
6955
7092
|
}
|
|
6956
7093
|
function tokensFromPackageJson(cwd) {
|
|
6957
|
-
const pkg = readJsonSafe(
|
|
7094
|
+
const pkg = readJsonSafe(join8(cwd, "package.json"));
|
|
6958
7095
|
if (!pkg || typeof pkg !== "object") return [];
|
|
6959
7096
|
const p = pkg;
|
|
6960
7097
|
const deps = {
|
|
@@ -6968,9 +7105,9 @@ function workspaceMemberDirs(cwd) {
|
|
|
6968
7105
|
const dirs = [cwd];
|
|
6969
7106
|
for (const group of ["apps", "packages"]) {
|
|
6970
7107
|
try {
|
|
6971
|
-
const groupDir =
|
|
7108
|
+
const groupDir = join8(cwd, group);
|
|
6972
7109
|
for (const e of readdirSync(groupDir, { withFileTypes: true })) {
|
|
6973
|
-
if (e.isDirectory() && !e.isSymbolicLink()) dirs.push(
|
|
7110
|
+
if (e.isDirectory() && !e.isSymbolicLink()) dirs.push(join8(groupDir, e.name));
|
|
6974
7111
|
}
|
|
6975
7112
|
} catch {
|
|
6976
7113
|
}
|
|
@@ -6978,18 +7115,18 @@ function workspaceMemberDirs(cwd) {
|
|
|
6978
7115
|
return dirs;
|
|
6979
7116
|
}
|
|
6980
7117
|
function tokensFromRequirementsTxt(cwd) {
|
|
6981
|
-
const content = readFileSafe(
|
|
7118
|
+
const content = readFileSafe(join8(cwd, "requirements.txt"));
|
|
6982
7119
|
if (!content) return [];
|
|
6983
7120
|
return content.split("\n").map((l) => l.trim().split(/[>=<!\[;]/)[0].trim().toLowerCase()).filter(Boolean);
|
|
6984
7121
|
}
|
|
6985
7122
|
function tokensFromGoMod(cwd) {
|
|
6986
|
-
const content = readFileSafe(
|
|
7123
|
+
const content = readFileSafe(join8(cwd, "go.mod"));
|
|
6987
7124
|
if (!content) return [];
|
|
6988
7125
|
const requires = Array.from(content.matchAll(/^\s+([^\s]+)\s+v/gm)).map((m) => m[1].split("/").pop() ?? "").filter(Boolean);
|
|
6989
7126
|
return ["go", ...requires];
|
|
6990
7127
|
}
|
|
6991
7128
|
function tokensFromCargoToml(cwd) {
|
|
6992
|
-
const content = readFileSafe(
|
|
7129
|
+
const content = readFileSafe(join8(cwd, "Cargo.toml"));
|
|
6993
7130
|
if (!content) return [];
|
|
6994
7131
|
const deps = [];
|
|
6995
7132
|
let inDeps = false;
|
|
@@ -7010,7 +7147,7 @@ function tokensFromFileExtensions(cwd) {
|
|
|
7010
7147
|
const tokens = [];
|
|
7011
7148
|
const scanDirs = [cwd];
|
|
7012
7149
|
try {
|
|
7013
|
-
const srcDir =
|
|
7150
|
+
const srcDir = join8(cwd, "src");
|
|
7014
7151
|
readdirSync(srcDir);
|
|
7015
7152
|
scanDirs.push(srcDir);
|
|
7016
7153
|
} catch {
|
|
@@ -7180,22 +7317,22 @@ __export(spinner_seen_exports, {
|
|
|
7180
7317
|
seenFilePath: () => seenFilePath
|
|
7181
7318
|
});
|
|
7182
7319
|
import {
|
|
7183
|
-
readFileSync as
|
|
7184
|
-
writeFileSync as
|
|
7185
|
-
renameSync as
|
|
7186
|
-
mkdirSync as
|
|
7320
|
+
readFileSync as readFileSync9,
|
|
7321
|
+
writeFileSync as writeFileSync7,
|
|
7322
|
+
renameSync as renameSync3,
|
|
7323
|
+
mkdirSync as mkdirSync7
|
|
7187
7324
|
} from "fs";
|
|
7188
|
-
import { join as
|
|
7189
|
-
import { homedir as
|
|
7325
|
+
import { join as join9, dirname as dirname2 } from "path";
|
|
7326
|
+
import { homedir as homedir7 } from "os";
|
|
7190
7327
|
function seenFilePath() {
|
|
7191
|
-
const dir = process.env["TERMINALHIRE_DIR"] ||
|
|
7192
|
-
return
|
|
7328
|
+
const dir = process.env["TERMINALHIRE_DIR"] || join9(homedir7(), ".terminalhire");
|
|
7329
|
+
return join9(dir, "seen-history.json");
|
|
7193
7330
|
}
|
|
7194
|
-
function
|
|
7195
|
-
|
|
7331
|
+
function atomicWriteJson2(path, obj) {
|
|
7332
|
+
mkdirSync7(dirname2(path), { recursive: true });
|
|
7196
7333
|
const tmp = `${path}.tmp-${process.pid}`;
|
|
7197
|
-
|
|
7198
|
-
|
|
7334
|
+
writeFileSync7(tmp, JSON.stringify(obj) + "\n", { encoding: "utf8", mode: 384 });
|
|
7335
|
+
renameSync3(tmp, path);
|
|
7199
7336
|
}
|
|
7200
7337
|
function emptyHistory() {
|
|
7201
7338
|
return { surface: 0, entries: {} };
|
|
@@ -7223,7 +7360,7 @@ function capEntries(entries) {
|
|
|
7223
7360
|
function loadSeenHistory(now = Date.now()) {
|
|
7224
7361
|
let raw;
|
|
7225
7362
|
try {
|
|
7226
|
-
raw = JSON.parse(
|
|
7363
|
+
raw = JSON.parse(readFileSync9(seenFilePath(), "utf8"));
|
|
7227
7364
|
} catch {
|
|
7228
7365
|
return emptyHistory();
|
|
7229
7366
|
}
|
|
@@ -7247,7 +7384,7 @@ function recordSurface(ids, now = Date.now()) {
|
|
|
7247
7384
|
}
|
|
7248
7385
|
const next = { surface, entries: capEntries(pruneEntries(entries, now)) };
|
|
7249
7386
|
try {
|
|
7250
|
-
|
|
7387
|
+
atomicWriteJson2(seenFilePath(), next);
|
|
7251
7388
|
} catch {
|
|
7252
7389
|
}
|
|
7253
7390
|
return next;
|
|
@@ -7264,35 +7401,35 @@ var init_spinner_seen = __esm({
|
|
|
7264
7401
|
|
|
7265
7402
|
// bin/spinner-io.js
|
|
7266
7403
|
import {
|
|
7267
|
-
readFileSync as
|
|
7268
|
-
writeFileSync as
|
|
7269
|
-
existsSync as
|
|
7270
|
-
mkdirSync as
|
|
7271
|
-
renameSync as
|
|
7404
|
+
readFileSync as readFileSync10,
|
|
7405
|
+
writeFileSync as writeFileSync8,
|
|
7406
|
+
existsSync as existsSync5,
|
|
7407
|
+
mkdirSync as mkdirSync8,
|
|
7408
|
+
renameSync as renameSync4
|
|
7272
7409
|
} from "fs";
|
|
7273
|
-
import { join as
|
|
7274
|
-
import { homedir as
|
|
7410
|
+
import { join as join10, dirname as dirname3 } from "path";
|
|
7411
|
+
import { homedir as homedir8 } from "os";
|
|
7275
7412
|
function thDir() {
|
|
7276
|
-
return process.env["TERMINALHIRE_DIR"] ||
|
|
7413
|
+
return process.env["TERMINALHIRE_DIR"] || join10(homedir8(), ".terminalhire");
|
|
7277
7414
|
}
|
|
7278
7415
|
function claudeSettingsPath() {
|
|
7279
|
-
return process.env["TERMINALHIRE_CLAUDE_SETTINGS"] ||
|
|
7416
|
+
return process.env["TERMINALHIRE_CLAUDE_SETTINGS"] || join10(homedir8(), ".claude", "settings.json");
|
|
7280
7417
|
}
|
|
7281
7418
|
function spinnerStateFilePath() {
|
|
7282
|
-
return
|
|
7419
|
+
return join10(thDir(), "spinner-state.json");
|
|
7283
7420
|
}
|
|
7284
7421
|
function readJson(path, fallback) {
|
|
7285
7422
|
try {
|
|
7286
|
-
return
|
|
7423
|
+
return existsSync5(path) ? JSON.parse(readFileSync10(path, "utf8")) : fallback;
|
|
7287
7424
|
} catch {
|
|
7288
7425
|
return fallback;
|
|
7289
7426
|
}
|
|
7290
7427
|
}
|
|
7291
|
-
function
|
|
7292
|
-
|
|
7428
|
+
function atomicWriteJson3(path, obj) {
|
|
7429
|
+
mkdirSync8(dirname3(path), { recursive: true });
|
|
7293
7430
|
const tmp = `${path}.tmp-${process.pid}`;
|
|
7294
|
-
|
|
7295
|
-
|
|
7431
|
+
writeFileSync8(tmp, JSON.stringify(obj, null, 2) + "\n", "utf8");
|
|
7432
|
+
renameSync4(tmp, path);
|
|
7296
7433
|
}
|
|
7297
7434
|
function readState() {
|
|
7298
7435
|
const SPINNER_STATE_FILE = spinnerStateFilePath();
|
|
@@ -7309,9 +7446,9 @@ function applySpinnerVerbs(ourVerbs, mode = "replace") {
|
|
|
7309
7446
|
const userVerbs = existing && Array.isArray(existing.verbs) ? existing.verbs.filter((v) => !prevOurs.has(v)) : [];
|
|
7310
7447
|
const newVerbs = [...verbs, ...userVerbs];
|
|
7311
7448
|
settings.spinnerVerbs = { mode: mode === "append" ? "append" : "replace", verbs: newVerbs };
|
|
7312
|
-
|
|
7449
|
+
atomicWriteJson3(CLAUDE_SETTINGS, settings);
|
|
7313
7450
|
const st = readState();
|
|
7314
|
-
|
|
7451
|
+
atomicWriteJson3(SPINNER_STATE_FILE, { ...st, verbs, mode, ts: Date.now() });
|
|
7315
7452
|
return { applied: verbs.length, total: newVerbs.length };
|
|
7316
7453
|
}
|
|
7317
7454
|
function clearSpinnerVerbs() {
|
|
@@ -7331,11 +7468,11 @@ function clearSpinnerVerbs() {
|
|
|
7331
7468
|
} else {
|
|
7332
7469
|
delete settings.spinnerVerbs;
|
|
7333
7470
|
}
|
|
7334
|
-
|
|
7471
|
+
atomicWriteJson3(CLAUDE_SETTINGS, settings);
|
|
7335
7472
|
}
|
|
7336
7473
|
try {
|
|
7337
7474
|
const st = readState();
|
|
7338
|
-
|
|
7475
|
+
atomicWriteJson3(SPINNER_STATE_FILE, { ...st, verbs: [], mode: st.mode || "replace", ts: Date.now() });
|
|
7339
7476
|
} catch {
|
|
7340
7477
|
}
|
|
7341
7478
|
return { cleared: true, keptUserVerbs };
|
|
@@ -7351,9 +7488,9 @@ function applySpinnerTips(ourTips) {
|
|
|
7351
7488
|
const userTips = existing.filter((t) => !prevOurs.has(t));
|
|
7352
7489
|
settings.spinnerTipsEnabled = true;
|
|
7353
7490
|
settings.spinnerTipsOverride = { excludeDefault: true, tips: [...tips, ...userTips] };
|
|
7354
|
-
|
|
7491
|
+
atomicWriteJson3(CLAUDE_SETTINGS, settings);
|
|
7355
7492
|
const st = readState();
|
|
7356
|
-
|
|
7493
|
+
atomicWriteJson3(SPINNER_STATE_FILE, { ...st, tips, ts: Date.now() });
|
|
7357
7494
|
return { applied: tips.length };
|
|
7358
7495
|
}
|
|
7359
7496
|
function clearSpinnerTips() {
|
|
@@ -7372,11 +7509,11 @@ function clearSpinnerTips() {
|
|
|
7372
7509
|
delete settings.spinnerTipsOverride;
|
|
7373
7510
|
delete settings.spinnerTipsEnabled;
|
|
7374
7511
|
}
|
|
7375
|
-
|
|
7512
|
+
atomicWriteJson3(CLAUDE_SETTINGS, settings);
|
|
7376
7513
|
}
|
|
7377
7514
|
try {
|
|
7378
7515
|
const st = readState();
|
|
7379
|
-
|
|
7516
|
+
atomicWriteJson3(SPINNER_STATE_FILE, { ...st, tips: [], ts: Date.now() });
|
|
7380
7517
|
} catch {
|
|
7381
7518
|
}
|
|
7382
7519
|
return { cleared: true };
|
|
@@ -7388,9 +7525,9 @@ var init_spinner_io = __esm({
|
|
|
7388
7525
|
});
|
|
7389
7526
|
|
|
7390
7527
|
// bin/spinner-config.js
|
|
7391
|
-
import { join as
|
|
7528
|
+
import { join as join11 } from "path";
|
|
7392
7529
|
function configFilePath() {
|
|
7393
|
-
return
|
|
7530
|
+
return join11(thDir(), "config.json");
|
|
7394
7531
|
}
|
|
7395
7532
|
function readSpinnerConfig() {
|
|
7396
7533
|
const CONFIG_FILE2 = configFilePath();
|
|
@@ -7820,9 +7957,9 @@ __export(version_nudge_exports, {
|
|
|
7820
7957
|
readLatestVersionFromCache: () => readLatestVersionFromCache,
|
|
7821
7958
|
readLocalVersion: () => readLocalVersion
|
|
7822
7959
|
});
|
|
7823
|
-
import { readFileSync as
|
|
7824
|
-
import { join as
|
|
7825
|
-
import { homedir as
|
|
7960
|
+
import { readFileSync as readFileSync11, existsSync as existsSync6 } from "fs";
|
|
7961
|
+
import { join as join12 } from "path";
|
|
7962
|
+
import { homedir as homedir9 } from "os";
|
|
7826
7963
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7827
7964
|
function parseVersion(v) {
|
|
7828
7965
|
if (typeof v !== "string") return null;
|
|
@@ -7847,12 +7984,12 @@ function buildStaleNudge(local, latest) {
|
|
|
7847
7984
|
function readLocalVersion() {
|
|
7848
7985
|
try {
|
|
7849
7986
|
const candidates = [
|
|
7850
|
-
|
|
7851
|
-
|
|
7987
|
+
join12(__dirname, "..", "..", "package.json"),
|
|
7988
|
+
join12(__dirname, "..", "package.json")
|
|
7852
7989
|
];
|
|
7853
7990
|
for (const p of candidates) {
|
|
7854
|
-
if (
|
|
7855
|
-
const pkg = JSON.parse(
|
|
7991
|
+
if (existsSync6(p)) {
|
|
7992
|
+
const pkg = JSON.parse(readFileSync11(p, "utf8"));
|
|
7856
7993
|
if (pkg.version) return pkg.version;
|
|
7857
7994
|
}
|
|
7858
7995
|
}
|
|
@@ -7862,7 +7999,7 @@ function readLocalVersion() {
|
|
|
7862
7999
|
}
|
|
7863
8000
|
function readLatestVersionFromCache() {
|
|
7864
8001
|
try {
|
|
7865
|
-
const cache = JSON.parse(
|
|
8002
|
+
const cache = JSON.parse(readFileSync11(INDEX_CACHE_FILE2, "utf8"));
|
|
7866
8003
|
const v = cache?.index?.cliVersion;
|
|
7867
8004
|
return typeof v === "string" ? v : null;
|
|
7868
8005
|
} catch {
|
|
@@ -7879,7 +8016,7 @@ var init_version_nudge = __esm({
|
|
|
7879
8016
|
"bin/version-nudge.js"() {
|
|
7880
8017
|
"use strict";
|
|
7881
8018
|
__dirname = fileURLToPath2(new URL(".", import.meta.url));
|
|
7882
|
-
INDEX_CACHE_FILE2 =
|
|
8019
|
+
INDEX_CACHE_FILE2 = join12(process.env.TERMINALHIRE_DIR || join12(homedir9(), ".terminalhire"), "index-cache.json");
|
|
7883
8020
|
}
|
|
7884
8021
|
});
|
|
7885
8022
|
|
|
@@ -8034,6 +8171,18 @@ function budgetSlots(results, opts = {}) {
|
|
|
8034
8171
|
return { roleTop, bountyTop, contributeTop };
|
|
8035
8172
|
}
|
|
8036
8173
|
|
|
8174
|
+
// bin/job-status-suppress.js
|
|
8175
|
+
function isEngaged(rec) {
|
|
8176
|
+
if (!rec) return false;
|
|
8177
|
+
if (rec.status === "applied" || rec.status === "dismissed") return true;
|
|
8178
|
+
if (rec.status === "saved") return false;
|
|
8179
|
+
return rec.clicked === true;
|
|
8180
|
+
}
|
|
8181
|
+
function suppressEngaged(results, statusMap) {
|
|
8182
|
+
const map = statusMap || {};
|
|
8183
|
+
return results.filter((r) => !isEngaged(map[r?.job?.id]));
|
|
8184
|
+
}
|
|
8185
|
+
|
|
8037
8186
|
// src/config.ts
|
|
8038
8187
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync } from "fs";
|
|
8039
8188
|
import { join as join3 } from "path";
|
|
@@ -8143,6 +8292,7 @@ async function run() {
|
|
|
8143
8292
|
try {
|
|
8144
8293
|
const { readProfile: readProfile2, profileToFingerprint: profileToFingerprint2 } = await Promise.resolve().then(() => (init_profile(), profile_exports));
|
|
8145
8294
|
const { match: match2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
8295
|
+
const { readStatusMap: readStatusMap2 } = await Promise.resolve().then(() => (init_job_status_store(), job_status_store_exports));
|
|
8146
8296
|
const profile = await readProfile2();
|
|
8147
8297
|
if (profile.skillTags.length > 0 && jobs.length > 0) {
|
|
8148
8298
|
const fp = profileToFingerprint2(profile);
|
|
@@ -8151,7 +8301,8 @@ async function run() {
|
|
|
8151
8301
|
contributeNudge.onRamp = true;
|
|
8152
8302
|
}
|
|
8153
8303
|
const pool = contribute.length > 0 ? [...jobs, ...contribute] : jobs;
|
|
8154
|
-
|
|
8304
|
+
let results = match2(fp, pool, pool.length);
|
|
8305
|
+
results = suppressEngaged(results, readStatusMap2());
|
|
8155
8306
|
matchCount = results.length;
|
|
8156
8307
|
const { roleTop, bountyTop, contributeTop } = budgetSlots(results, {
|
|
8157
8308
|
thinProfile,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "terminalhire",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.2",
|
|
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",
|