terminalhire 0.3.3 → 0.4.0

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.
@@ -144,11 +144,11 @@ var init_graph_data = __esm({
144
144
  { id: "spark", parents: ["data-engineering"], synonyms: ["apache-spark"] },
145
145
  { id: "airflow", parents: ["data-engineering"], synonyms: ["apache-airflow"] },
146
146
  { id: "dbt", parents: ["data-engineering"] },
147
- { id: "ml", synonyms: ["machine-learning"], related: [{ to: "pytorch", w: 0.5 }, { to: "tensorflow", w: 0.5 }, { to: "scikit-learn", w: 0.5 }] },
147
+ { id: "ml", synonyms: ["machine-learning"], related: [{ to: "pytorch", w: 0.5 }, { to: "tensorflow", w: 0.5 }, { to: "scikit-learn", w: 0.5 }, { to: "data-engineering", w: 0.4 }] },
148
148
  { id: "llm", parents: ["ml"], synonyms: ["llms", "genai", "generative-ai"], related: [{ to: "langchain", w: 0.5 }, { to: "rag", w: 0.55 }, { to: "openai", w: 0.45 }, { to: "anthropic", w: 0.45 }] },
149
149
  { id: "pytorch", parents: ["ml"], synonyms: ["torch"], related: [{ to: "tensorflow", w: 0.5 }] },
150
150
  { id: "tensorflow", parents: ["ml"], synonyms: ["keras", "tf-keras"] },
151
- { id: "pandas", parents: ["python"], related: [{ to: "numpy", w: 0.6 }] },
151
+ { id: "pandas", parents: ["python"], related: [{ to: "numpy", w: 0.6 }, { to: "data-engineering", w: 0.45 }, { to: "spark", w: 0.4 }] },
152
152
  { id: "numpy", parents: ["python"] },
153
153
  { id: "scikit-learn", parents: ["ml"], synonyms: ["sklearn"] },
154
154
  { id: "jupyter", parents: ["python"] },
@@ -323,6 +323,46 @@ var init_types2 = __esm({
323
323
  }
324
324
  });
325
325
 
326
+ // ../../packages/core/src/vocab/extract.ts
327
+ var SOFT_DOMAIN, SYNONYM_ONLY;
328
+ var init_extract = __esm({
329
+ "../../packages/core/src/vocab/extract.ts"() {
330
+ "use strict";
331
+ init_vocab();
332
+ SOFT_DOMAIN = /* @__PURE__ */ new Set([
333
+ "frontend",
334
+ "backend",
335
+ "devops",
336
+ "security",
337
+ "payments",
338
+ "billing",
339
+ "microservices",
340
+ "caching",
341
+ "search",
342
+ "observability",
343
+ "monitoring",
344
+ "testing",
345
+ "accessibility",
346
+ "seo",
347
+ "performance",
348
+ "realtime",
349
+ "authentication",
350
+ "api-design"
351
+ ]);
352
+ SYNONYM_ONLY = /* @__PURE__ */ new Set(["performance", "security", "seo"]);
353
+ for (const id of SYNONYM_ONLY) {
354
+ if (!SOFT_DOMAIN.has(id)) throw new Error(`extract: SYNONYM_ONLY "${id}" not in SOFT_DOMAIN`);
355
+ }
356
+ }
357
+ });
358
+
359
+ // ../../packages/core/src/vocab/idf-background.ts
360
+ var init_idf_background = __esm({
361
+ "../../packages/core/src/vocab/idf-background.ts"() {
362
+ "use strict";
363
+ }
364
+ });
365
+
326
366
  // ../../packages/core/src/vocab/index.ts
327
367
  function normalize(tokens) {
328
368
  const result = /* @__PURE__ */ new Set();
@@ -346,6 +386,8 @@ var init_vocab = __esm({
346
386
  init_types2();
347
387
  init_closure();
348
388
  init_graph_data();
389
+ init_extract();
390
+ init_idf_background();
349
391
  GRAPH = buildGraph(VOCAB_NODES);
350
392
  VOCABULARY = [...GRAPH.ids];
351
393
  SYNONYMS = Object.fromEntries(GRAPH.synonyms);
@@ -360,11 +402,20 @@ var init_vocabulary = __esm({
360
402
  }
361
403
  });
362
404
 
405
+ // ../../packages/core/src/github.ts
406
+ var init_github = __esm({
407
+ "../../packages/core/src/github.ts"() {
408
+ "use strict";
409
+ init_vocabulary();
410
+ }
411
+ });
412
+
363
413
  // ../../packages/core/src/matcher.ts
364
414
  var init_matcher = __esm({
365
415
  "../../packages/core/src/matcher.ts"() {
366
416
  "use strict";
367
417
  init_vocabulary();
418
+ init_github();
368
419
  }
369
420
  });
370
421
 
@@ -594,14 +645,6 @@ var init_indexer = __esm({
594
645
  }
595
646
  });
596
647
 
597
- // ../../packages/core/src/github.ts
598
- var init_github = __esm({
599
- "../../packages/core/src/github.ts"() {
600
- "use strict";
601
- init_vocabulary();
602
- }
603
- });
604
-
605
648
  // ../../packages/core/src/index.ts
606
649
  var init_src = __esm({
607
650
  "../../packages/core/src/index.ts"() {
@@ -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 role matching what you're building`];
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 A role matching your ${a} work`, `\u2726 Your ${a} work \u2014 link in the tip below`];
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 A role that fits your work`, `\u2726 Job match for you \u2014 link in the tip below`];
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 bounty = list.find((m) => m && m.source === "bounty" && m.amountUSD != null) || list.find((m) => m && m.source === "bounty");
106
- if (bounty) {
107
- const money = bounty.amountUSD != null ? `$${bounty.amountUSD.toLocaleString()} ` : "";
108
- 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 = {}) {
@@ -144,11 +144,11 @@ var init_graph_data = __esm({
144
144
  { id: "spark", parents: ["data-engineering"], synonyms: ["apache-spark"] },
145
145
  { id: "airflow", parents: ["data-engineering"], synonyms: ["apache-airflow"] },
146
146
  { id: "dbt", parents: ["data-engineering"] },
147
- { id: "ml", synonyms: ["machine-learning"], related: [{ to: "pytorch", w: 0.5 }, { to: "tensorflow", w: 0.5 }, { to: "scikit-learn", w: 0.5 }] },
147
+ { id: "ml", synonyms: ["machine-learning"], related: [{ to: "pytorch", w: 0.5 }, { to: "tensorflow", w: 0.5 }, { to: "scikit-learn", w: 0.5 }, { to: "data-engineering", w: 0.4 }] },
148
148
  { id: "llm", parents: ["ml"], synonyms: ["llms", "genai", "generative-ai"], related: [{ to: "langchain", w: 0.5 }, { to: "rag", w: 0.55 }, { to: "openai", w: 0.45 }, { to: "anthropic", w: 0.45 }] },
149
149
  { id: "pytorch", parents: ["ml"], synonyms: ["torch"], related: [{ to: "tensorflow", w: 0.5 }] },
150
150
  { id: "tensorflow", parents: ["ml"], synonyms: ["keras", "tf-keras"] },
151
- { id: "pandas", parents: ["python"], related: [{ to: "numpy", w: 0.6 }] },
151
+ { id: "pandas", parents: ["python"], related: [{ to: "numpy", w: 0.6 }, { to: "data-engineering", w: 0.45 }, { to: "spark", w: 0.4 }] },
152
152
  { id: "numpy", parents: ["python"] },
153
153
  { id: "scikit-learn", parents: ["ml"], synonyms: ["sklearn"] },
154
154
  { id: "jupyter", parents: ["python"] },
@@ -323,6 +323,46 @@ var init_types2 = __esm({
323
323
  }
324
324
  });
325
325
 
326
+ // ../../packages/core/src/vocab/extract.ts
327
+ var SOFT_DOMAIN, SYNONYM_ONLY;
328
+ var init_extract = __esm({
329
+ "../../packages/core/src/vocab/extract.ts"() {
330
+ "use strict";
331
+ init_vocab();
332
+ SOFT_DOMAIN = /* @__PURE__ */ new Set([
333
+ "frontend",
334
+ "backend",
335
+ "devops",
336
+ "security",
337
+ "payments",
338
+ "billing",
339
+ "microservices",
340
+ "caching",
341
+ "search",
342
+ "observability",
343
+ "monitoring",
344
+ "testing",
345
+ "accessibility",
346
+ "seo",
347
+ "performance",
348
+ "realtime",
349
+ "authentication",
350
+ "api-design"
351
+ ]);
352
+ SYNONYM_ONLY = /* @__PURE__ */ new Set(["performance", "security", "seo"]);
353
+ for (const id of SYNONYM_ONLY) {
354
+ if (!SOFT_DOMAIN.has(id)) throw new Error(`extract: SYNONYM_ONLY "${id}" not in SOFT_DOMAIN`);
355
+ }
356
+ }
357
+ });
358
+
359
+ // ../../packages/core/src/vocab/idf-background.ts
360
+ var init_idf_background = __esm({
361
+ "../../packages/core/src/vocab/idf-background.ts"() {
362
+ "use strict";
363
+ }
364
+ });
365
+
326
366
  // ../../packages/core/src/vocab/index.ts
327
367
  function normalize(tokens) {
328
368
  const result = /* @__PURE__ */ new Set();
@@ -346,6 +386,8 @@ var init_vocab = __esm({
346
386
  init_types2();
347
387
  init_closure();
348
388
  init_graph_data();
389
+ init_extract();
390
+ init_idf_background();
349
391
  GRAPH = buildGraph(VOCAB_NODES);
350
392
  VOCABULARY = [...GRAPH.ids];
351
393
  SYNONYMS = Object.fromEntries(GRAPH.synonyms);
@@ -360,11 +402,20 @@ var init_vocabulary = __esm({
360
402
  }
361
403
  });
362
404
 
405
+ // ../../packages/core/src/github.ts
406
+ var init_github = __esm({
407
+ "../../packages/core/src/github.ts"() {
408
+ "use strict";
409
+ init_vocabulary();
410
+ }
411
+ });
412
+
363
413
  // ../../packages/core/src/matcher.ts
364
414
  var init_matcher = __esm({
365
415
  "../../packages/core/src/matcher.ts"() {
366
416
  "use strict";
367
417
  init_vocabulary();
418
+ init_github();
368
419
  }
369
420
  });
370
421
 
@@ -594,14 +645,6 @@ var init_indexer = __esm({
594
645
  }
595
646
  });
596
647
 
597
- // ../../packages/core/src/github.ts
598
- var init_github = __esm({
599
- "../../packages/core/src/github.ts"() {
600
- "use strict";
601
- init_vocabulary();
602
- }
603
- });
604
-
605
648
  // ../../packages/core/src/index.ts
606
649
  var init_src = __esm({
607
650
  "../../packages/core/src/index.ts"() {
@@ -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 role matching what you're building`];
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 A role matching your ${a} work`, `\u2726 Your ${a} work \u2014 link in the tip below`];
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 A role that fits your work`, `\u2726 Job match for you \u2014 link in the tip below`];
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 bounty = list.find((m) => m && m.source === "bounty" && m.amountUSD != null) || list.find((m) => m && m.source === "bounty");
113
- if (bounty) {
114
- const money = bounty.amountUSD != null ? `$${bounty.amountUSD.toLocaleString()} ` : "";
115
- 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 leadBounty = all.find((m) => m && m.source === "bounty");
203
- const ordered = leadBounty ? [leadBounty, ...interleaveBySource(all.filter((m) => m !== leadBounty))] : interleaveBySource(all);
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(":");
@@ -137,11 +137,11 @@ var VOCAB_NODES = [
137
137
  { id: "spark", parents: ["data-engineering"], synonyms: ["apache-spark"] },
138
138
  { id: "airflow", parents: ["data-engineering"], synonyms: ["apache-airflow"] },
139
139
  { id: "dbt", parents: ["data-engineering"] },
140
- { id: "ml", synonyms: ["machine-learning"], related: [{ to: "pytorch", w: 0.5 }, { to: "tensorflow", w: 0.5 }, { to: "scikit-learn", w: 0.5 }] },
140
+ { id: "ml", synonyms: ["machine-learning"], related: [{ to: "pytorch", w: 0.5 }, { to: "tensorflow", w: 0.5 }, { to: "scikit-learn", w: 0.5 }, { to: "data-engineering", w: 0.4 }] },
141
141
  { id: "llm", parents: ["ml"], synonyms: ["llms", "genai", "generative-ai"], related: [{ to: "langchain", w: 0.5 }, { to: "rag", w: 0.55 }, { to: "openai", w: 0.45 }, { to: "anthropic", w: 0.45 }] },
142
142
  { id: "pytorch", parents: ["ml"], synonyms: ["torch"], related: [{ to: "tensorflow", w: 0.5 }] },
143
143
  { id: "tensorflow", parents: ["ml"], synonyms: ["keras", "tf-keras"] },
144
- { id: "pandas", parents: ["python"], related: [{ to: "numpy", w: 0.6 }] },
144
+ { id: "pandas", parents: ["python"], related: [{ to: "numpy", w: 0.6 }, { to: "data-engineering", w: 0.45 }, { to: "spark", w: 0.4 }] },
145
145
  { id: "numpy", parents: ["python"] },
146
146
  { id: "scikit-learn", parents: ["ml"], synonyms: ["sklearn"] },
147
147
  { id: "jupyter", parents: ["python"] },
@@ -301,6 +301,32 @@ function buildGraph(nodes) {
301
301
  return { ids, synonyms, closure };
302
302
  }
303
303
 
304
+ // ../../packages/core/src/vocab/extract.ts
305
+ var SOFT_DOMAIN = /* @__PURE__ */ new Set([
306
+ "frontend",
307
+ "backend",
308
+ "devops",
309
+ "security",
310
+ "payments",
311
+ "billing",
312
+ "microservices",
313
+ "caching",
314
+ "search",
315
+ "observability",
316
+ "monitoring",
317
+ "testing",
318
+ "accessibility",
319
+ "seo",
320
+ "performance",
321
+ "realtime",
322
+ "authentication",
323
+ "api-design"
324
+ ]);
325
+ var SYNONYM_ONLY = /* @__PURE__ */ new Set(["performance", "security", "seo"]);
326
+ for (const id of SYNONYM_ONLY) {
327
+ if (!SOFT_DOMAIN.has(id)) throw new Error(`extract: SYNONYM_ONLY "${id}" not in SOFT_DOMAIN`);
328
+ }
329
+
304
330
  // ../../packages/core/src/vocab/index.ts
305
331
  var GRAPH = buildGraph(VOCAB_NODES);
306
332
  var VOCABULARY = [...GRAPH.ids];
@@ -127,11 +127,11 @@ var VOCAB_NODES = [
127
127
  { id: "spark", parents: ["data-engineering"], synonyms: ["apache-spark"] },
128
128
  { id: "airflow", parents: ["data-engineering"], synonyms: ["apache-airflow"] },
129
129
  { id: "dbt", parents: ["data-engineering"] },
130
- { id: "ml", synonyms: ["machine-learning"], related: [{ to: "pytorch", w: 0.5 }, { to: "tensorflow", w: 0.5 }, { to: "scikit-learn", w: 0.5 }] },
130
+ { id: "ml", synonyms: ["machine-learning"], related: [{ to: "pytorch", w: 0.5 }, { to: "tensorflow", w: 0.5 }, { to: "scikit-learn", w: 0.5 }, { to: "data-engineering", w: 0.4 }] },
131
131
  { id: "llm", parents: ["ml"], synonyms: ["llms", "genai", "generative-ai"], related: [{ to: "langchain", w: 0.5 }, { to: "rag", w: 0.55 }, { to: "openai", w: 0.45 }, { to: "anthropic", w: 0.45 }] },
132
132
  { id: "pytorch", parents: ["ml"], synonyms: ["torch"], related: [{ to: "tensorflow", w: 0.5 }] },
133
133
  { id: "tensorflow", parents: ["ml"], synonyms: ["keras", "tf-keras"] },
134
- { id: "pandas", parents: ["python"], related: [{ to: "numpy", w: 0.6 }] },
134
+ { id: "pandas", parents: ["python"], related: [{ to: "numpy", w: 0.6 }, { to: "data-engineering", w: 0.45 }, { to: "spark", w: 0.4 }] },
135
135
  { id: "numpy", parents: ["python"] },
136
136
  { id: "scikit-learn", parents: ["ml"], synonyms: ["sklearn"] },
137
137
  { id: "jupyter", parents: ["python"] },
@@ -291,6 +291,32 @@ function buildGraph(nodes) {
291
291
  return { ids, synonyms, closure };
292
292
  }
293
293
 
294
+ // ../../packages/core/src/vocab/extract.ts
295
+ var SOFT_DOMAIN = /* @__PURE__ */ new Set([
296
+ "frontend",
297
+ "backend",
298
+ "devops",
299
+ "security",
300
+ "payments",
301
+ "billing",
302
+ "microservices",
303
+ "caching",
304
+ "search",
305
+ "observability",
306
+ "monitoring",
307
+ "testing",
308
+ "accessibility",
309
+ "seo",
310
+ "performance",
311
+ "realtime",
312
+ "authentication",
313
+ "api-design"
314
+ ]);
315
+ var SYNONYM_ONLY = /* @__PURE__ */ new Set(["performance", "security", "seo"]);
316
+ for (const id of SYNONYM_ONLY) {
317
+ if (!SOFT_DOMAIN.has(id)) throw new Error(`extract: SYNONYM_ONLY "${id}" not in SOFT_DOMAIN`);
318
+ }
319
+
294
320
  // ../../packages/core/src/vocab/index.ts
295
321
  var GRAPH = buildGraph(VOCAB_NODES);
296
322
  var VOCABULARY = [...GRAPH.ids];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminalhire",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
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",