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.
@@ -1,216 +1,310 @@
1
1
  // src/signal.ts
2
2
  import { readFileSync as readFileSync2, readdirSync } from "fs";
3
- import { execSync } from "child_process";
3
+ import { execFileSync } from "child_process";
4
4
  import { join as join2 } from "path";
5
5
 
6
- // ../../packages/core/src/vocabulary.ts
7
- var VOCABULARY = [
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
- // Frontend frameworks / libs
27
- "react",
28
- "nextjs",
29
- "vue",
30
- "nuxt",
31
- "svelte",
32
- "angular",
33
- "solidjs",
34
- "tailwind",
35
- "css",
36
- "html",
37
- "graphql",
38
- "trpc",
39
- // Backend frameworks
40
- "nodejs",
41
- "express",
42
- "fastify",
43
- "nestjs",
44
- "django",
45
- "fastapi",
46
- "flask",
47
- "rails",
48
- "spring",
49
- "actix",
50
- "gin",
51
- "phoenix",
52
- "laravel",
53
- "dotnet",
54
- // Infrastructure & DevOps
55
- "kubernetes",
56
- "docker",
57
- "terraform",
58
- "aws",
59
- "gcp",
60
- "azure",
61
- "ci-cd",
62
- "github-actions",
63
- "linux",
64
- "nginx",
65
- "pulumi",
66
- "ansible",
67
- "prometheus",
68
- "grafana",
69
- "datadog",
70
- "opentelemetry",
71
- // Data & ML
72
- "postgresql",
73
- "mysql",
74
- "sqlite",
75
- "mongodb",
76
- "redis",
77
- "elasticsearch",
78
- "kafka",
79
- "rabbitmq",
80
- "data-engineering",
81
- "spark",
82
- "airflow",
83
- "dbt",
84
- "ml",
85
- "llm",
86
- "pytorch",
87
- "tensorflow",
88
- "pandas",
89
- "numpy",
90
- // Domains / capabilities
91
- "oauth",
92
- "authentication",
93
- "security",
94
- "payments",
95
- "billing",
96
- "frontend",
97
- "backend",
98
- "devops",
99
- "mobile",
100
- "ios",
101
- "android",
102
- "api-design",
103
- "microservices",
104
- "websockets",
105
- "testing",
106
- "accessibility",
107
- "seo",
108
- "performance",
109
- "observability",
110
- "search",
111
- "realtime"
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
- var SYNONYMS = {
114
- // Kubernetes aliases
115
- "k8s": "kubernetes",
116
- "kube": "kubernetes",
117
- // Auth / identity
118
- "passport": "authentication",
119
- "oauth2": "oauth",
120
- "oidc": "oauth",
121
- "jwt": "authentication",
122
- "saml": "authentication",
123
- "auth0": "authentication",
124
- "clerk": "authentication",
125
- "nextauth": "authentication",
126
- // Payments
127
- "@stripe/stripe-js": "payments",
128
- "stripe": "payments",
129
- "braintree": "payments",
130
- "paddle": "payments",
131
- "lemonsqueezy": "payments",
132
- "recurly": "billing",
133
- "chargebee": "billing",
134
- // Framework / lib aliases
135
- "next": "nextjs",
136
- "next.js": "nextjs",
137
- "nuxt.js": "nuxt",
138
- "vue.js": "vue",
139
- "angular.js": "angular",
140
- "angularjs": "angular",
141
- "express.js": "express",
142
- "expressjs": "express",
143
- "fastapi": "fastapi",
144
- "nest": "nestjs",
145
- "nest.js": "nestjs",
146
- "sveltekit": "svelte",
147
- // Language aliases
148
- "ts": "typescript",
149
- "js": "javascript",
150
- "py": "python",
151
- "golang": "go",
152
- "c++": "cpp",
153
- "c#": "csharp",
154
- ".net": "dotnet",
155
- "asp.net": "dotnet",
156
- // DB aliases
157
- "postgres": "postgresql",
158
- "pg": "postgresql",
159
- "mongo": "mongodb",
160
- "elastic": "elasticsearch",
161
- // Cloud aliases
162
- "amazon web services": "aws",
163
- "google cloud": "gcp",
164
- "google cloud platform": "gcp",
165
- "microsoft azure": "azure",
166
- // CI/CD aliases
167
- "github actions": "github-actions",
168
- "circle ci": "ci-cd",
169
- "circleci": "ci-cd",
170
- "jenkins": "ci-cd",
171
- "gitlab ci": "ci-cd",
172
- "travis": "ci-cd",
173
- // Mobile
174
- "react native": "mobile",
175
- "flutter": "mobile",
176
- "expo": "mobile",
177
- // AI / ML
178
- "openai": "llm",
179
- "anthropic": "llm",
180
- "langchain": "llm",
181
- "llamaindex": "llm",
182
- "hugging face": "ml",
183
- "huggingface": "ml",
184
- "scikit-learn": "ml",
185
- "sklearn": "ml",
186
- // Data pipeline
187
- "apache kafka": "kafka",
188
- "apache spark": "spark",
189
- "apache airflow": "airflow",
190
- // Misc
191
- "tailwindcss": "tailwind",
192
- "tw": "tailwind",
193
- "gql": "graphql",
194
- "ws": "websockets",
195
- "socket.io": "websockets",
196
- "jest": "testing",
197
- "vitest": "testing",
198
- "playwright": "testing",
199
- "cypress": "testing"
200
- };
201
- var VOCAB_SET = new Set(VOCABULARY);
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 (VOCAB_SET.has(lower)) {
302
+ if (GRAPH.ids.has(lower)) {
207
303
  result.add(lower);
208
304
  continue;
209
305
  }
210
- const mapped = SYNONYMS[lower];
211
- if (mapped && VOCAB_SET.has(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/coastal.ts
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 safeExec(cmd) {
507
+ function safeGit(args, cwd) {
406
508
  try {
407
- return execSync(cmd, { timeout: 2e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
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 remote = safeExec('git -C "' + cwd + '" remote get-url origin 2>/dev/null');
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
- try {
416
- const sshMatch = remote.match(/^git@([^:]+):/);
417
- const httpsMatch = remote.match(/^https?:\/\/([^/]+)/);
418
- const host = (sshMatch?.[1] ?? httpsMatch?.[1] ?? "").toLowerCase();
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 ["go"];
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 = Array.from(content.matchAll(/^([a-zA-Z0-9_-]+)\s*=/gm)).map((m) => m[1].toLowerCase());
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
- ...tokensFromPackageJson(cwd),
539
- ...tokensFromRequirementsTxt(cwd),
540
- ...tokensFromGoMod(cwd),
541
- ...tokensFromCargoToml(cwd),
542
- ...tokensFromFileExtensions(cwd)
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));