terminalhire 0.2.4 → 0.3.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.
- package/dist/bin/jpi-bounties.js +2031 -0
- package/dist/bin/jpi-dispatch.js +1174 -473
- package/dist/bin/jpi-jobs.js +705 -276
- package/dist/bin/jpi-learn.js +442 -249
- package/dist/bin/jpi-login.js +700 -282
- package/dist/bin/jpi-profile.js +381 -213
- package/dist/bin/jpi-refresh.js +757 -311
- package/dist/bin/jpi-save.js +381 -213
- package/dist/bin/jpi-sync.js +502 -230
- package/dist/src/github-auth.js +3 -3
- package/dist/src/profile.js +313 -207
- package/dist/src/signal.js +364 -237
- package/package.json +3 -3
package/dist/src/signal.js
CHANGED
|
@@ -1,216 +1,310 @@
|
|
|
1
1
|
// src/signal.ts
|
|
2
2
|
import { readFileSync as readFileSync2, readdirSync } from "fs";
|
|
3
|
-
import {
|
|
3
|
+
import { execFileSync } from "child_process";
|
|
4
4
|
import { join as join2 } from "path";
|
|
5
5
|
|
|
6
|
-
// ../../packages/core/src/
|
|
7
|
-
var
|
|
8
|
-
// Languages
|
|
9
|
-
"typescript",
|
|
10
|
-
"javascript",
|
|
11
|
-
"python",
|
|
12
|
-
"go",
|
|
13
|
-
"rust",
|
|
14
|
-
"java",
|
|
15
|
-
"ruby",
|
|
16
|
-
"elixir",
|
|
17
|
-
"scala",
|
|
18
|
-
"kotlin",
|
|
19
|
-
"swift",
|
|
20
|
-
"cpp",
|
|
21
|
-
"csharp",
|
|
22
|
-
"php",
|
|
23
|
-
"haskell",
|
|
24
|
-
"clojure",
|
|
25
|
-
"r",
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
"
|
|
109
|
-
"
|
|
110
|
-
"
|
|
111
|
-
"
|
|
6
|
+
// ../../packages/core/src/vocab/graph.data.ts
|
|
7
|
+
var VOCAB_NODES = [
|
|
8
|
+
// ── Languages ─────────────────────────────────────────────────────────────
|
|
9
|
+
{ id: "javascript", synonyms: ["js"], related: [{ to: "typescript", w: 0.6 }] },
|
|
10
|
+
{ id: "typescript", parents: ["javascript"], synonyms: ["ts"] },
|
|
11
|
+
{ id: "python", synonyms: ["py"] },
|
|
12
|
+
{ id: "go", synonyms: ["golang"] },
|
|
13
|
+
{ id: "rust" },
|
|
14
|
+
{ id: "java", related: [{ to: "kotlin", w: 0.45 }, { to: "scala", w: 0.4 }] },
|
|
15
|
+
{ id: "ruby" },
|
|
16
|
+
{ id: "elixir" },
|
|
17
|
+
{ id: "scala", related: [{ to: "java", w: 0.4 }] },
|
|
18
|
+
{ id: "kotlin", related: [{ to: "java", w: 0.45 }] },
|
|
19
|
+
{ id: "swift" },
|
|
20
|
+
{ id: "cpp", synonyms: ["c++"] },
|
|
21
|
+
{ id: "csharp", synonyms: ["c#"] },
|
|
22
|
+
{ id: "php" },
|
|
23
|
+
{ id: "haskell" },
|
|
24
|
+
{ id: "clojure" },
|
|
25
|
+
{ id: "r" },
|
|
26
|
+
{ id: "dart" },
|
|
27
|
+
// ── Frontend ──────────────────────────────────────────────────────────────
|
|
28
|
+
{
|
|
29
|
+
id: "react",
|
|
30
|
+
parents: ["javascript"],
|
|
31
|
+
synonyms: ["reactjs"],
|
|
32
|
+
related: [{ to: "nextjs", w: 0.55 }, { to: "vue", w: 0.4 }, { to: "svelte", w: 0.4 }, { to: "solidjs", w: 0.5 }, { to: "angular", w: 0.35 }]
|
|
33
|
+
},
|
|
34
|
+
{ id: "nextjs", parents: ["react"], synonyms: ["next", "next.js"], related: [{ to: "remix", w: 0.5 }] },
|
|
35
|
+
{ id: "vue", parents: ["javascript"], synonyms: ["vue.js"], related: [{ to: "nuxt", w: 0.6 }] },
|
|
36
|
+
{ id: "nuxt", parents: ["vue"], synonyms: ["nuxt.js"] },
|
|
37
|
+
{ id: "svelte", parents: ["javascript"], related: [{ to: "sveltekit", w: 0.65 }] },
|
|
38
|
+
{ id: "sveltekit", parents: ["svelte"] },
|
|
39
|
+
{ id: "angular", parents: ["typescript"], synonyms: ["angular.js", "angularjs"] },
|
|
40
|
+
{ id: "solidjs", parents: ["javascript"] },
|
|
41
|
+
{ id: "remix", parents: ["react"], synonyms: ["remix.run"] },
|
|
42
|
+
{ id: "astro", parents: ["javascript"], related: [{ to: "nextjs", w: 0.4 }] },
|
|
43
|
+
{ id: "qwik", parents: ["javascript"] },
|
|
44
|
+
{ id: "tailwind", parents: ["css"], synonyms: ["tailwindcss", "tw"] },
|
|
45
|
+
{ id: "css" },
|
|
46
|
+
{ id: "html" },
|
|
47
|
+
{ id: "redux", parents: ["react"] },
|
|
48
|
+
{ id: "vite", parents: ["frontend"] },
|
|
49
|
+
{ id: "webpack", parents: ["frontend"] },
|
|
50
|
+
{ id: "storybook", parents: ["frontend"] },
|
|
51
|
+
// ── Backend frameworks / runtimes ───────────────────────────────────────────
|
|
52
|
+
{
|
|
53
|
+
id: "nodejs",
|
|
54
|
+
parents: ["javascript"],
|
|
55
|
+
synonyms: ["node", "node.js"],
|
|
56
|
+
related: [{ to: "express", w: 0.5 }, { to: "fastify", w: 0.45 }, { to: "nestjs", w: 0.45 }]
|
|
57
|
+
},
|
|
58
|
+
{ id: "express", parents: ["nodejs"], synonyms: ["express.js", "expressjs"], related: [{ to: "fastify", w: 0.5 }] },
|
|
59
|
+
{ id: "fastify", parents: ["nodejs"] },
|
|
60
|
+
{ id: "nestjs", parents: ["nodejs"], synonyms: ["nest", "nest.js"] },
|
|
61
|
+
{ id: "hono", parents: ["nodejs"] },
|
|
62
|
+
{ id: "deno", parents: ["javascript"], related: [{ to: "nodejs", w: 0.5 }, { to: "bun", w: 0.5 }] },
|
|
63
|
+
{ id: "bun", parents: ["javascript"], related: [{ to: "nodejs", w: 0.5 }] },
|
|
64
|
+
{ id: "django", parents: ["python"], related: [{ to: "flask", w: 0.5 }, { to: "fastapi", w: 0.45 }] },
|
|
65
|
+
{ id: "fastapi", parents: ["python"], related: [{ to: "flask", w: 0.55 }, { to: "django", w: 0.45 }] },
|
|
66
|
+
{ id: "flask", parents: ["python"] },
|
|
67
|
+
{ id: "rails", parents: ["ruby"], synonyms: ["ruby-on-rails", "ror"] },
|
|
68
|
+
{ id: "spring", parents: ["java"], synonyms: ["spring-boot", "springboot"] },
|
|
69
|
+
{ id: "actix", parents: ["rust"] },
|
|
70
|
+
{ id: "gin", parents: ["go"] },
|
|
71
|
+
{ id: "phoenix", parents: ["elixir"] },
|
|
72
|
+
{ id: "laravel", parents: ["php"] },
|
|
73
|
+
{ id: "dotnet", parents: ["csharp"], synonyms: [".net", "asp.net", "dotnet-core"] },
|
|
74
|
+
// ── Infrastructure & DevOps ─────────────────────────────────────────────────
|
|
75
|
+
{ id: "kubernetes", synonyms: ["k8s", "kube"], related: [{ to: "docker", w: 0.5 }, { to: "helm", w: 0.55 }, { to: "terraform", w: 0.4 }, { to: "argocd", w: 0.45 }] },
|
|
76
|
+
{ id: "docker", parents: ["devops"], related: [{ to: "kubernetes", w: 0.5 }] },
|
|
77
|
+
{ id: "terraform", synonyms: ["tf"], related: [{ to: "pulumi", w: 0.55 }, { to: "ansible", w: 0.4 }, { to: "aws", w: 0.4 }] },
|
|
78
|
+
{ id: "pulumi", related: [{ to: "terraform", w: 0.55 }] },
|
|
79
|
+
{ id: "ansible" },
|
|
80
|
+
{ id: "aws", synonyms: ["amazon-web-services"], related: [{ to: "gcp", w: 0.4 }, { to: "azure", w: 0.4 }] },
|
|
81
|
+
{ id: "gcp", synonyms: ["google-cloud", "google-cloud-platform"], related: [{ to: "aws", w: 0.4 }, { to: "azure", w: 0.4 }] },
|
|
82
|
+
{ id: "azure", synonyms: ["microsoft-azure"], related: [{ to: "aws", w: 0.4 }] },
|
|
83
|
+
{ id: "ci-cd", synonyms: ["cicd", "jenkins", "circleci", "circle-ci", "travis"], related: [{ to: "github-actions", w: 0.6 }, { to: "gitlab-ci", w: 0.6 }] },
|
|
84
|
+
{ id: "github-actions", parents: ["ci-cd"], synonyms: ["github-action"] },
|
|
85
|
+
{ id: "gitlab-ci", parents: ["ci-cd"], synonyms: ["gitlab"] },
|
|
86
|
+
{ id: "linux" },
|
|
87
|
+
{ id: "nginx" },
|
|
88
|
+
{ id: "prometheus", parents: ["observability"], related: [{ to: "grafana", w: 0.6 }] },
|
|
89
|
+
{ id: "grafana", parents: ["observability"] },
|
|
90
|
+
{ id: "datadog", parents: ["observability"] },
|
|
91
|
+
{ id: "opentelemetry", parents: ["observability"], synonyms: ["otel"] },
|
|
92
|
+
{ id: "vercel", related: [{ to: "netlify", w: 0.5 }, { to: "nextjs", w: 0.4 }] },
|
|
93
|
+
{ id: "netlify" },
|
|
94
|
+
{ id: "fly", synonyms: ["fly.io"], related: [{ to: "railway", w: 0.5 }, { to: "render", w: 0.5 }] },
|
|
95
|
+
{ id: "railway", related: [{ to: "render", w: 0.5 }] },
|
|
96
|
+
{ id: "render" },
|
|
97
|
+
{ id: "cloudflare", synonyms: ["cloudflare-workers"] },
|
|
98
|
+
{ id: "helm", parents: ["kubernetes"] },
|
|
99
|
+
{ id: "argocd", parents: ["kubernetes"] },
|
|
100
|
+
{ id: "serverless", parents: ["devops"] },
|
|
101
|
+
// ── Databases & storage ─────────────────────────────────────────────────────
|
|
102
|
+
{ id: "postgresql", synonyms: ["postgres", "pg"], related: [{ to: "mysql", w: 0.45 }, { to: "sqlite", w: 0.4 }] },
|
|
103
|
+
{ id: "mysql", related: [{ to: "postgresql", w: 0.45 }] },
|
|
104
|
+
{ id: "sqlite" },
|
|
105
|
+
{ id: "mongodb", synonyms: ["mongo"] },
|
|
106
|
+
{ id: "redis", related: [{ to: "caching", w: 0.5 }] },
|
|
107
|
+
{ id: "elasticsearch", synonyms: ["elastic"], related: [{ to: "search", w: 0.55 }] },
|
|
108
|
+
{ id: "kafka", synonyms: ["apache-kafka"], related: [{ to: "rabbitmq", w: 0.5 }, { to: "message-queue", w: 0.55 }] },
|
|
109
|
+
{ id: "rabbitmq", related: [{ to: "message-queue", w: 0.55 }] },
|
|
110
|
+
{ id: "cassandra" },
|
|
111
|
+
{ id: "dynamodb", parents: ["aws"] },
|
|
112
|
+
{ id: "snowflake", parents: ["data-engineering"], related: [{ to: "clickhouse", w: 0.4 }] },
|
|
113
|
+
{ id: "clickhouse", parents: ["data-engineering"], related: [{ to: "duckdb", w: 0.35 }] },
|
|
114
|
+
{ id: "duckdb", parents: ["data-engineering"] },
|
|
115
|
+
{ id: "supabase", related: [{ to: "postgresql", w: 0.5 }, { to: "neon", w: 0.4 }] },
|
|
116
|
+
{ id: "planetscale", related: [{ to: "mysql", w: 0.5 }] },
|
|
117
|
+
{ id: "neon", related: [{ to: "postgresql", w: 0.5 }] },
|
|
118
|
+
{ id: "turso", related: [{ to: "sqlite", w: 0.5 }] },
|
|
119
|
+
{ id: "cockroachdb", related: [{ to: "postgresql", w: 0.45 }] },
|
|
120
|
+
{ id: "prisma", parents: ["backend"], synonyms: ["@prisma/client"], related: [{ to: "drizzle", w: 0.5 }, { to: "typeorm", w: 0.45 }, { to: "sequelize", w: 0.4 }] },
|
|
121
|
+
{ id: "drizzle", synonyms: ["drizzle-orm"], related: [{ to: "prisma", w: 0.5 }] },
|
|
122
|
+
{ id: "sequelize", related: [{ to: "typeorm", w: 0.4 }] },
|
|
123
|
+
{ id: "typeorm", related: [{ to: "prisma", w: 0.45 }] },
|
|
124
|
+
{ id: "sqlalchemy", parents: ["python"] },
|
|
125
|
+
// ── Data engineering & ML ───────────────────────────────────────────────────
|
|
126
|
+
{ id: "data-engineering", synonyms: ["data-eng"], related: [{ to: "spark", w: 0.5 }, { to: "airflow", w: 0.5 }, { to: "dbt", w: 0.45 }] },
|
|
127
|
+
{ id: "spark", parents: ["data-engineering"], synonyms: ["apache-spark"] },
|
|
128
|
+
{ id: "airflow", parents: ["data-engineering"], synonyms: ["apache-airflow"] },
|
|
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 }] },
|
|
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
|
+
{ id: "pytorch", parents: ["ml"], synonyms: ["torch"], related: [{ to: "tensorflow", w: 0.5 }] },
|
|
133
|
+
{ id: "tensorflow", parents: ["ml"], synonyms: ["keras", "tf-keras"] },
|
|
134
|
+
{ id: "pandas", parents: ["python"], related: [{ to: "numpy", w: 0.6 }] },
|
|
135
|
+
{ id: "numpy", parents: ["python"] },
|
|
136
|
+
{ id: "scikit-learn", parents: ["ml"], synonyms: ["sklearn"] },
|
|
137
|
+
{ id: "jupyter", parents: ["python"] },
|
|
138
|
+
{ id: "langchain", parents: ["llm"], synonyms: ["llamaindex"] },
|
|
139
|
+
{ id: "huggingface", parents: ["ml"], synonyms: ["hugging-face"] },
|
|
140
|
+
{ id: "openai", parents: ["llm"] },
|
|
141
|
+
{ id: "anthropic", parents: ["llm"], synonyms: ["claude"] },
|
|
142
|
+
{ id: "rag", parents: ["llm"], synonyms: ["retrieval-augmented-generation"] },
|
|
143
|
+
{ id: "mlops", parents: ["ml"], related: [{ to: "devops", w: 0.4 }] },
|
|
144
|
+
// ── Mobile ──────────────────────────────────────────────────────────────────
|
|
145
|
+
{ id: "mobile", related: [{ to: "ios", w: 0.5 }, { to: "android", w: 0.5 }] },
|
|
146
|
+
{ id: "ios", parents: ["mobile", "swift"], related: [{ to: "android", w: 0.4 }] },
|
|
147
|
+
{ id: "android", parents: ["mobile"], related: [{ to: "kotlin", w: 0.4 }] },
|
|
148
|
+
{ id: "swiftui", parents: ["ios", "swift"] },
|
|
149
|
+
{ id: "react-native", parents: ["mobile", "react"], synonyms: ["reactnative"], related: [{ to: "flutter", w: 0.4 }, { to: "expo", w: 0.6 }] },
|
|
150
|
+
{ id: "flutter", parents: ["mobile", "dart"] },
|
|
151
|
+
{ id: "expo", parents: ["react-native"] },
|
|
152
|
+
{ id: "kotlin-multiplatform", parents: ["mobile", "kotlin"], synonyms: ["kmp"] },
|
|
153
|
+
// ── Domains / capabilities ──────────────────────────────────────────────────
|
|
154
|
+
{ id: "frontend", related: [{ to: "react", w: 0.4 }, { to: "css", w: 0.3 }] },
|
|
155
|
+
{ id: "backend", related: [{ to: "api-design", w: 0.4 }, { to: "microservices", w: 0.4 }] },
|
|
156
|
+
{ id: "devops", related: [{ to: "kubernetes", w: 0.4 }, { to: "ci-cd", w: 0.4 }, { to: "docker", w: 0.4 }] },
|
|
157
|
+
{ id: "authentication", synonyms: ["auth", "jwt", "saml", "passport", "auth0", "clerk", "nextauth"], related: [{ to: "oauth", w: 0.6 }, { to: "security", w: 0.5 }] },
|
|
158
|
+
{ id: "oauth", parents: ["authentication"], synonyms: ["oauth2", "oidc"], related: [{ to: "security", w: 0.4 }] },
|
|
159
|
+
{ id: "security", related: [{ to: "authentication", w: 0.5 }] },
|
|
160
|
+
{ id: "payments", synonyms: ["stripe", "braintree", "paddle", "lemonsqueezy", "@stripe/stripe-js"], related: [{ to: "billing", w: 0.6 }] },
|
|
161
|
+
{ id: "billing", synonyms: ["recurly", "chargebee"] },
|
|
162
|
+
{ id: "api-design", synonyms: ["rest", "restful", "rest-api"], related: [{ to: "graphql", w: 0.4 }, { to: "grpc", w: 0.4 }, { to: "backend", w: 0.4 }] },
|
|
163
|
+
{ id: "graphql", synonyms: ["gql"], related: [{ to: "trpc", w: 0.4 }] },
|
|
164
|
+
{ id: "trpc", related: [{ to: "graphql", w: 0.4 }] },
|
|
165
|
+
{ id: "grpc", synonyms: ["grpc-web"], related: [{ to: "microservices", w: 0.3 }] },
|
|
166
|
+
{ id: "microservices" },
|
|
167
|
+
{ id: "websockets", synonyms: ["ws", "socket.io"], related: [{ to: "realtime", w: 0.6 }] },
|
|
168
|
+
{ id: "realtime", synonyms: ["real-time"] },
|
|
169
|
+
{ id: "message-queue", synonyms: ["mq"] },
|
|
170
|
+
{ id: "caching", synonyms: ["cache"] },
|
|
171
|
+
{ id: "search", synonyms: ["full-text-search"] },
|
|
172
|
+
{ id: "observability", synonyms: ["o11y"], related: [{ to: "monitoring", w: 0.6 }] },
|
|
173
|
+
{ id: "monitoring", related: [{ to: "prometheus", w: 0.4 }] },
|
|
174
|
+
{ id: "testing", related: [{ to: "unit-testing", w: 0.5 }, { to: "e2e-testing", w: 0.5 }] },
|
|
175
|
+
{ id: "unit-testing", parents: ["testing"] },
|
|
176
|
+
{ id: "e2e-testing", parents: ["testing"], synonyms: ["e2e", "end-to-end-testing"] },
|
|
177
|
+
{ id: "jest", parents: ["testing"], related: [{ to: "vitest", w: 0.6 }, { to: "mocha", w: 0.5 }] },
|
|
178
|
+
{ id: "vitest", parents: ["testing"], related: [{ to: "jest", w: 0.6 }] },
|
|
179
|
+
{ id: "playwright", parents: ["e2e-testing"], related: [{ to: "cypress", w: 0.6 }] },
|
|
180
|
+
{ id: "cypress", parents: ["e2e-testing"] },
|
|
181
|
+
{ id: "mocha", parents: ["testing"] },
|
|
182
|
+
{ id: "pytest", parents: ["testing", "python"] },
|
|
183
|
+
{ id: "accessibility", synonyms: ["a11y"] },
|
|
184
|
+
{ id: "seo" },
|
|
185
|
+
{ id: "performance", synonyms: ["perf", "web-performance"] }
|
|
112
186
|
];
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
};
|
|
201
|
-
|
|
187
|
+
|
|
188
|
+
// ../../packages/core/src/vocab/closure.ts
|
|
189
|
+
var PARENT_UP = 0.6;
|
|
190
|
+
var PARENT_DOWN = 0.35;
|
|
191
|
+
var DECAY_FLOOR = 0.25;
|
|
192
|
+
function round3(n) {
|
|
193
|
+
return Math.round(n * 1e3) / 1e3;
|
|
194
|
+
}
|
|
195
|
+
function validateGraph(nodes) {
|
|
196
|
+
const ids = /* @__PURE__ */ new Set();
|
|
197
|
+
for (const n of nodes) {
|
|
198
|
+
if (ids.has(n.id)) throw new Error(`vocab: duplicate id "${n.id}"`);
|
|
199
|
+
ids.add(n.id);
|
|
200
|
+
}
|
|
201
|
+
const seenAlias = /* @__PURE__ */ new Map();
|
|
202
|
+
for (const n of nodes) {
|
|
203
|
+
for (const p of n.parents ?? []) {
|
|
204
|
+
if (p === n.id) throw new Error(`vocab: "${n.id}" lists itself as a parent`);
|
|
205
|
+
if (!ids.has(p)) throw new Error(`vocab: "${n.id}" parent "${p}" is not a defined id`);
|
|
206
|
+
}
|
|
207
|
+
for (const e of n.related ?? []) {
|
|
208
|
+
if (e.to === n.id) throw new Error(`vocab: "${n.id}" relates to itself`);
|
|
209
|
+
if (!ids.has(e.to)) throw new Error(`vocab: "${n.id}" related "${e.to}" is not a defined id`);
|
|
210
|
+
if (!(e.w > 0 && e.w <= 1)) throw new Error(`vocab: "${n.id}"\u2192"${e.to}" weight ${e.w} out of (0,1]`);
|
|
211
|
+
}
|
|
212
|
+
for (const s of n.synonyms ?? []) {
|
|
213
|
+
const alias = s.toLowerCase();
|
|
214
|
+
if (ids.has(alias)) throw new Error(`vocab: synonym "${alias}" collides with a canonical id`);
|
|
215
|
+
const prev = seenAlias.get(alias);
|
|
216
|
+
if (prev && prev !== n.id) throw new Error(`vocab: synonym "${alias}" maps to both "${prev}" and "${n.id}"`);
|
|
217
|
+
seenAlias.set(alias, n.id);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
221
|
+
const done = /* @__PURE__ */ new Set();
|
|
222
|
+
const parentMap = new Map(nodes.map((n) => [n.id, n.parents ?? []]));
|
|
223
|
+
const walk = (id, path) => {
|
|
224
|
+
if (done.has(id)) return;
|
|
225
|
+
if (visiting.has(id)) throw new Error(`vocab: parent cycle ${[...path, id].join(" \u2192 ")}`);
|
|
226
|
+
visiting.add(id);
|
|
227
|
+
for (const p of parentMap.get(id) ?? []) walk(p, [...path, id]);
|
|
228
|
+
visiting.delete(id);
|
|
229
|
+
done.add(id);
|
|
230
|
+
};
|
|
231
|
+
for (const n of nodes) walk(n.id, []);
|
|
232
|
+
}
|
|
233
|
+
function buildAdjacency(nodes) {
|
|
234
|
+
const adj = /* @__PURE__ */ new Map();
|
|
235
|
+
const add = (from, to, w) => {
|
|
236
|
+
let m = adj.get(from);
|
|
237
|
+
if (!m) adj.set(from, m = /* @__PURE__ */ new Map());
|
|
238
|
+
if (w > (m.get(to) ?? 0)) m.set(to, w);
|
|
239
|
+
};
|
|
240
|
+
for (const n of nodes) {
|
|
241
|
+
for (const p of n.parents ?? []) {
|
|
242
|
+
add(n.id, p, PARENT_UP);
|
|
243
|
+
add(p, n.id, PARENT_DOWN);
|
|
244
|
+
}
|
|
245
|
+
for (const e of n.related ?? []) {
|
|
246
|
+
add(n.id, e.to, e.w);
|
|
247
|
+
add(e.to, n.id, e.w);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return adj;
|
|
251
|
+
}
|
|
252
|
+
function closureFrom(source, adj) {
|
|
253
|
+
const best = /* @__PURE__ */ new Map();
|
|
254
|
+
for (const [t, w] of adj.get(source) ?? []) {
|
|
255
|
+
if (w >= DECAY_FLOOR) best.set(t, { w: round3(w), via: t });
|
|
256
|
+
}
|
|
257
|
+
const settled = /* @__PURE__ */ new Set([source]);
|
|
258
|
+
while (true) {
|
|
259
|
+
let u;
|
|
260
|
+
let uw = 0;
|
|
261
|
+
for (const [t, e] of best) {
|
|
262
|
+
if (!settled.has(t) && e.w > uw) {
|
|
263
|
+
u = t;
|
|
264
|
+
uw = e.w;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (!u) break;
|
|
268
|
+
settled.add(u);
|
|
269
|
+
const via = best.get(u).via;
|
|
270
|
+
for (const [t, we] of adj.get(u) ?? []) {
|
|
271
|
+
if (settled.has(t)) continue;
|
|
272
|
+
const cand = round3(uw * we);
|
|
273
|
+
if (cand >= DECAY_FLOOR && cand > (best.get(t)?.w ?? 0)) {
|
|
274
|
+
best.set(t, { w: cand, via });
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
best.delete(source);
|
|
279
|
+
return best;
|
|
280
|
+
}
|
|
281
|
+
function buildGraph(nodes) {
|
|
282
|
+
validateGraph(nodes);
|
|
283
|
+
const ids = new Set(nodes.map((n) => n.id));
|
|
284
|
+
const synonyms = /* @__PURE__ */ new Map();
|
|
285
|
+
for (const n of nodes) {
|
|
286
|
+
for (const s of n.synonyms ?? []) synonyms.set(s.toLowerCase(), n.id);
|
|
287
|
+
}
|
|
288
|
+
const adj = buildAdjacency(nodes);
|
|
289
|
+
const closure = /* @__PURE__ */ new Map();
|
|
290
|
+
for (const n of nodes) closure.set(n.id, closureFrom(n.id, adj));
|
|
291
|
+
return { ids, synonyms, closure };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// ../../packages/core/src/vocab/index.ts
|
|
295
|
+
var GRAPH = buildGraph(VOCAB_NODES);
|
|
296
|
+
var VOCABULARY = [...GRAPH.ids];
|
|
297
|
+
var SYNONYMS = Object.fromEntries(GRAPH.synonyms);
|
|
202
298
|
function normalize(tokens) {
|
|
203
299
|
const result = /* @__PURE__ */ new Set();
|
|
204
300
|
for (const raw of tokens) {
|
|
205
301
|
const lower = raw.toLowerCase().trim();
|
|
206
|
-
if (
|
|
302
|
+
if (GRAPH.ids.has(lower)) {
|
|
207
303
|
result.add(lower);
|
|
208
304
|
continue;
|
|
209
305
|
}
|
|
210
|
-
const mapped =
|
|
211
|
-
if (mapped
|
|
212
|
-
result.add(mapped);
|
|
213
|
-
}
|
|
306
|
+
const mapped = GRAPH.synonyms.get(lower);
|
|
307
|
+
if (mapped) result.add(mapped);
|
|
214
308
|
}
|
|
215
309
|
return Array.from(result);
|
|
216
310
|
}
|
|
@@ -325,10 +419,18 @@ var DEFAULT_GREENHOUSE_SLUGS = flattenTiers(GREENHOUSE_SLUGS_BY_TIER);
|
|
|
325
419
|
var DEFAULT_ASHBY_SLUGS = flattenTiers(ASHBY_SLUGS_BY_TIER);
|
|
326
420
|
var DEFAULT_LEVER_SLUGS = flattenTiers(LEVER_SLUGS_BY_TIER);
|
|
327
421
|
|
|
328
|
-
// ../../packages/core/src/
|
|
422
|
+
// ../../packages/core/src/partners.ts
|
|
329
423
|
import { readFileSync } from "fs";
|
|
330
424
|
import { join } from "path";
|
|
331
425
|
import { fileURLToPath } from "url";
|
|
426
|
+
var EXAMPLE_BUYER = {
|
|
427
|
+
id: "northstar",
|
|
428
|
+
legalName: "Northstar Talent Partners",
|
|
429
|
+
matchCriteria: { roleTypes: ["full_time"] }
|
|
430
|
+
};
|
|
431
|
+
var BUYER_REGISTRY = {
|
|
432
|
+
[EXAMPLE_BUYER.id]: EXAMPLE_BUYER
|
|
433
|
+
};
|
|
332
434
|
|
|
333
435
|
// src/signal.ts
|
|
334
436
|
var LANGUAGE_TAGS = /* @__PURE__ */ new Set([
|
|
@@ -402,33 +504,29 @@ var PERSONAL_EMAIL_DOMAINS = /* @__PURE__ */ new Set([
|
|
|
402
504
|
"hey.com",
|
|
403
505
|
"duck.com"
|
|
404
506
|
]);
|
|
405
|
-
function
|
|
507
|
+
function safeGit(args, cwd) {
|
|
406
508
|
try {
|
|
407
|
-
return
|
|
509
|
+
return execFileSync("git", ["-C", cwd, ...args], {
|
|
510
|
+
timeout: 2e3,
|
|
511
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
512
|
+
}).toString().trim();
|
|
408
513
|
} catch {
|
|
409
514
|
return "";
|
|
410
515
|
}
|
|
411
516
|
}
|
|
412
517
|
function isEmployerContext(cwd) {
|
|
413
|
-
const
|
|
518
|
+
const inRepo = safeGit(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
519
|
+
if (inRepo !== "true") return false;
|
|
520
|
+
const remote = safeGit(["remote", "get-url", "origin"], cwd);
|
|
414
521
|
if (remote) {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
if (host && !PERSONAL_GIT_HOSTS.has(host)) {
|
|
420
|
-
return true;
|
|
421
|
-
}
|
|
422
|
-
} catch {
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
const email = safeExec('git -C "' + cwd + '" config user.email 2>/dev/null');
|
|
426
|
-
if (email) {
|
|
427
|
-
const domain = email.split("@")[1]?.toLowerCase() ?? "";
|
|
428
|
-
if (domain && !PERSONAL_EMAIL_DOMAINS.has(domain)) {
|
|
429
|
-
return true;
|
|
430
|
-
}
|
|
522
|
+
const sshMatch = remote.match(/^git@([^:]+):/);
|
|
523
|
+
const httpsMatch = remote.match(/^https?:\/\/([^/]+)/);
|
|
524
|
+
const host = (sshMatch?.[1] ?? httpsMatch?.[1] ?? "").toLowerCase();
|
|
525
|
+
if (host) return !PERSONAL_GIT_HOSTS.has(host);
|
|
431
526
|
}
|
|
527
|
+
const email = safeGit(["config", "user.email"], cwd);
|
|
528
|
+
const domain = email.split("@")[1]?.toLowerCase() ?? "";
|
|
529
|
+
if (domain) return !PERSONAL_EMAIL_DOMAINS.has(domain);
|
|
432
530
|
return false;
|
|
433
531
|
}
|
|
434
532
|
function readJsonSafe(path) {
|
|
@@ -451,10 +549,24 @@ function tokensFromPackageJson(cwd) {
|
|
|
451
549
|
const p = pkg;
|
|
452
550
|
const deps = {
|
|
453
551
|
...typeof p["dependencies"] === "object" ? p["dependencies"] : {},
|
|
454
|
-
...typeof p["devDependencies"] === "object" ? p["devDependencies"] : {}
|
|
552
|
+
...typeof p["devDependencies"] === "object" ? p["devDependencies"] : {},
|
|
553
|
+
...typeof p["peerDependencies"] === "object" ? p["peerDependencies"] : {}
|
|
455
554
|
};
|
|
456
555
|
return Object.keys(deps);
|
|
457
556
|
}
|
|
557
|
+
function workspaceMemberDirs(cwd) {
|
|
558
|
+
const dirs = [cwd];
|
|
559
|
+
for (const group of ["apps", "packages"]) {
|
|
560
|
+
try {
|
|
561
|
+
const groupDir = join2(cwd, group);
|
|
562
|
+
for (const e of readdirSync(groupDir, { withFileTypes: true })) {
|
|
563
|
+
if (e.isDirectory() && !e.isSymbolicLink()) dirs.push(join2(groupDir, e.name));
|
|
564
|
+
}
|
|
565
|
+
} catch {
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return dirs;
|
|
569
|
+
}
|
|
458
570
|
function tokensFromRequirementsTxt(cwd) {
|
|
459
571
|
const content = readFileSafe(join2(cwd, "requirements.txt"));
|
|
460
572
|
if (!content) return [];
|
|
@@ -462,14 +574,26 @@ function tokensFromRequirementsTxt(cwd) {
|
|
|
462
574
|
}
|
|
463
575
|
function tokensFromGoMod(cwd) {
|
|
464
576
|
const content = readFileSafe(join2(cwd, "go.mod"));
|
|
465
|
-
if (!content) return [
|
|
577
|
+
if (!content) return [];
|
|
466
578
|
const requires = Array.from(content.matchAll(/^\s+([^\s]+)\s+v/gm)).map((m) => m[1].split("/").pop() ?? "").filter(Boolean);
|
|
467
579
|
return ["go", ...requires];
|
|
468
580
|
}
|
|
469
581
|
function tokensFromCargoToml(cwd) {
|
|
470
582
|
const content = readFileSafe(join2(cwd, "Cargo.toml"));
|
|
471
583
|
if (!content) return [];
|
|
472
|
-
const deps =
|
|
584
|
+
const deps = [];
|
|
585
|
+
let inDeps = false;
|
|
586
|
+
for (const line of content.split("\n")) {
|
|
587
|
+
const trimmed = line.trim();
|
|
588
|
+
const section = trimmed.match(/^\[([^\]]+)\]/);
|
|
589
|
+
if (section) {
|
|
590
|
+
inDeps = /(^|\.)(dependencies|dev-dependencies|build-dependencies)$/.test(section[1].trim());
|
|
591
|
+
continue;
|
|
592
|
+
}
|
|
593
|
+
if (!inDeps) continue;
|
|
594
|
+
const key = trimmed.match(/^([a-zA-Z0-9_-]+)\s*=/);
|
|
595
|
+
if (key) deps.push(key[1].toLowerCase());
|
|
596
|
+
}
|
|
473
597
|
return ["rust", ...deps];
|
|
474
598
|
}
|
|
475
599
|
function tokensFromFileExtensions(cwd) {
|
|
@@ -509,11 +633,7 @@ function inferSeniority(rawTokens) {
|
|
|
509
633
|
"opentelemetry",
|
|
510
634
|
"prometheus",
|
|
511
635
|
"grafana",
|
|
512
|
-
"microservices"
|
|
513
|
-
"api-design",
|
|
514
|
-
"security",
|
|
515
|
-
"oauth",
|
|
516
|
-
"payments"
|
|
636
|
+
"microservices"
|
|
517
637
|
]);
|
|
518
638
|
const midSignals = /* @__PURE__ */ new Set([
|
|
519
639
|
"docker",
|
|
@@ -523,7 +643,11 @@ function inferSeniority(rawTokens) {
|
|
|
523
643
|
"postgresql",
|
|
524
644
|
"redis",
|
|
525
645
|
"graphql",
|
|
526
|
-
"trpc"
|
|
646
|
+
"trpc",
|
|
647
|
+
"api-design",
|
|
648
|
+
"security",
|
|
649
|
+
"oauth",
|
|
650
|
+
"payments"
|
|
527
651
|
]);
|
|
528
652
|
const normalized = new Set(normalize(rawTokens));
|
|
529
653
|
const seniorHits = [...normalized].filter((t) => seniorSignals.has(t)).length;
|
|
@@ -534,13 +658,16 @@ function inferSeniority(rawTokens) {
|
|
|
534
658
|
}
|
|
535
659
|
function extractFingerprint(cwd) {
|
|
536
660
|
const employer = isEmployerContext(cwd);
|
|
537
|
-
const rawTokens = [
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
661
|
+
const rawTokens = [];
|
|
662
|
+
for (const dir of workspaceMemberDirs(cwd)) {
|
|
663
|
+
rawTokens.push(
|
|
664
|
+
...tokensFromPackageJson(dir),
|
|
665
|
+
...tokensFromRequirementsTxt(dir),
|
|
666
|
+
...tokensFromGoMod(dir),
|
|
667
|
+
...tokensFromCargoToml(dir),
|
|
668
|
+
...tokensFromFileExtensions(dir)
|
|
669
|
+
);
|
|
670
|
+
}
|
|
544
671
|
let skillTags = normalize(rawTokens);
|
|
545
672
|
if (employer) {
|
|
546
673
|
skillTags = skillTags.filter((t) => LANGUAGE_TAGS.has(t));
|