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.
@@ -22,7 +22,7 @@ var IV_BYTES = 12;
22
22
  var GITHUB_SCOPE = "read:user";
23
23
  var DEVICE_CODE_URL = "https://github.com/login/device/code";
24
24
  var ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
25
- var DEV_PLACEHOLDER_CLIENT_ID = "Ov23lignE2ZSBe0J3a6B";
25
+ var BAKED_IN_CLIENT_ID = "Ov23lignE2ZSBe0J3a6B";
26
26
  async function loadKey() {
27
27
  try {
28
28
  const kt = await import("keytar");
@@ -92,7 +92,7 @@ async function runDeviceFlow() {
92
92
  await writeGitHubToken(MOCK_TOKEN);
93
93
  return MOCK_LOGIN;
94
94
  }
95
- const clientId = process.env["GITHUB_DEVICE_CLIENT_ID"] ?? process.env["GITHUB_CLIENT_ID"] ?? DEV_PLACEHOLDER_CLIENT_ID;
95
+ const clientId = process.env["GITHUB_DEVICE_CLIENT_ID"] ?? process.env["GITHUB_CLIENT_ID"] ?? BAKED_IN_CLIENT_ID;
96
96
  if (clientId === "Iv1.PLACEHOLDER_REGISTER_YOUR_APP") {
97
97
  console.warn("\nWarning: GITHUB_CLIENT_ID env var looks like a placeholder.");
98
98
  console.warn("Remove it to use the baked-in client ID, or set it to your own OAuth App Client ID.\n");
@@ -115,7 +115,7 @@ async function runDeviceFlow() {
115
115
  console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
116
116
  console.log(` 1. Open: ${deviceData.verification_uri}`);
117
117
  console.log(` 2. Enter code: ${deviceData.user_code}`);
118
- console.log(' 3. Authorize "jpi" (scope: read:user \u2014 public data only)');
118
+ console.log(' 3. Authorize "Terminalhire" (scope: read:user \u2014 public data only)');
119
119
  console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
120
120
  console.log(" Waiting for authorization...");
121
121
  console.log("");
@@ -13,214 +13,308 @@ import {
13
13
  import { join as join2 } from "path";
14
14
  import { homedir } from "os";
15
15
 
16
- // ../../packages/core/src/vocabulary.ts
17
- var VOCABULARY = [
18
- // Languages
19
- "typescript",
20
- "javascript",
21
- "python",
22
- "go",
23
- "rust",
24
- "java",
25
- "ruby",
26
- "elixir",
27
- "scala",
28
- "kotlin",
29
- "swift",
30
- "cpp",
31
- "csharp",
32
- "php",
33
- "haskell",
34
- "clojure",
35
- "r",
36
- // Frontend frameworks / libs
37
- "react",
38
- "nextjs",
39
- "vue",
40
- "nuxt",
41
- "svelte",
42
- "angular",
43
- "solidjs",
44
- "tailwind",
45
- "css",
46
- "html",
47
- "graphql",
48
- "trpc",
49
- // Backend frameworks
50
- "nodejs",
51
- "express",
52
- "fastify",
53
- "nestjs",
54
- "django",
55
- "fastapi",
56
- "flask",
57
- "rails",
58
- "spring",
59
- "actix",
60
- "gin",
61
- "phoenix",
62
- "laravel",
63
- "dotnet",
64
- // Infrastructure & DevOps
65
- "kubernetes",
66
- "docker",
67
- "terraform",
68
- "aws",
69
- "gcp",
70
- "azure",
71
- "ci-cd",
72
- "github-actions",
73
- "linux",
74
- "nginx",
75
- "pulumi",
76
- "ansible",
77
- "prometheus",
78
- "grafana",
79
- "datadog",
80
- "opentelemetry",
81
- // Data & ML
82
- "postgresql",
83
- "mysql",
84
- "sqlite",
85
- "mongodb",
86
- "redis",
87
- "elasticsearch",
88
- "kafka",
89
- "rabbitmq",
90
- "data-engineering",
91
- "spark",
92
- "airflow",
93
- "dbt",
94
- "ml",
95
- "llm",
96
- "pytorch",
97
- "tensorflow",
98
- "pandas",
99
- "numpy",
100
- // Domains / capabilities
101
- "oauth",
102
- "authentication",
103
- "security",
104
- "payments",
105
- "billing",
106
- "frontend",
107
- "backend",
108
- "devops",
109
- "mobile",
110
- "ios",
111
- "android",
112
- "api-design",
113
- "microservices",
114
- "websockets",
115
- "testing",
116
- "accessibility",
117
- "seo",
118
- "performance",
119
- "observability",
120
- "search",
121
- "realtime"
16
+ // ../../packages/core/src/vocab/graph.data.ts
17
+ var VOCAB_NODES = [
18
+ // ── Languages ─────────────────────────────────────────────────────────────
19
+ { id: "javascript", synonyms: ["js"], related: [{ to: "typescript", w: 0.6 }] },
20
+ { id: "typescript", parents: ["javascript"], synonyms: ["ts"] },
21
+ { id: "python", synonyms: ["py"] },
22
+ { id: "go", synonyms: ["golang"] },
23
+ { id: "rust" },
24
+ { id: "java", related: [{ to: "kotlin", w: 0.45 }, { to: "scala", w: 0.4 }] },
25
+ { id: "ruby" },
26
+ { id: "elixir" },
27
+ { id: "scala", related: [{ to: "java", w: 0.4 }] },
28
+ { id: "kotlin", related: [{ to: "java", w: 0.45 }] },
29
+ { id: "swift" },
30
+ { id: "cpp", synonyms: ["c++"] },
31
+ { id: "csharp", synonyms: ["c#"] },
32
+ { id: "php" },
33
+ { id: "haskell" },
34
+ { id: "clojure" },
35
+ { id: "r" },
36
+ { id: "dart" },
37
+ // ── Frontend ──────────────────────────────────────────────────────────────
38
+ {
39
+ id: "react",
40
+ parents: ["javascript"],
41
+ synonyms: ["reactjs"],
42
+ 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 }]
43
+ },
44
+ { id: "nextjs", parents: ["react"], synonyms: ["next", "next.js"], related: [{ to: "remix", w: 0.5 }] },
45
+ { id: "vue", parents: ["javascript"], synonyms: ["vue.js"], related: [{ to: "nuxt", w: 0.6 }] },
46
+ { id: "nuxt", parents: ["vue"], synonyms: ["nuxt.js"] },
47
+ { id: "svelte", parents: ["javascript"], related: [{ to: "sveltekit", w: 0.65 }] },
48
+ { id: "sveltekit", parents: ["svelte"] },
49
+ { id: "angular", parents: ["typescript"], synonyms: ["angular.js", "angularjs"] },
50
+ { id: "solidjs", parents: ["javascript"] },
51
+ { id: "remix", parents: ["react"], synonyms: ["remix.run"] },
52
+ { id: "astro", parents: ["javascript"], related: [{ to: "nextjs", w: 0.4 }] },
53
+ { id: "qwik", parents: ["javascript"] },
54
+ { id: "tailwind", parents: ["css"], synonyms: ["tailwindcss", "tw"] },
55
+ { id: "css" },
56
+ { id: "html" },
57
+ { id: "redux", parents: ["react"] },
58
+ { id: "vite", parents: ["frontend"] },
59
+ { id: "webpack", parents: ["frontend"] },
60
+ { id: "storybook", parents: ["frontend"] },
61
+ // ── Backend frameworks / runtimes ───────────────────────────────────────────
62
+ {
63
+ id: "nodejs",
64
+ parents: ["javascript"],
65
+ synonyms: ["node", "node.js"],
66
+ related: [{ to: "express", w: 0.5 }, { to: "fastify", w: 0.45 }, { to: "nestjs", w: 0.45 }]
67
+ },
68
+ { id: "express", parents: ["nodejs"], synonyms: ["express.js", "expressjs"], related: [{ to: "fastify", w: 0.5 }] },
69
+ { id: "fastify", parents: ["nodejs"] },
70
+ { id: "nestjs", parents: ["nodejs"], synonyms: ["nest", "nest.js"] },
71
+ { id: "hono", parents: ["nodejs"] },
72
+ { id: "deno", parents: ["javascript"], related: [{ to: "nodejs", w: 0.5 }, { to: "bun", w: 0.5 }] },
73
+ { id: "bun", parents: ["javascript"], related: [{ to: "nodejs", w: 0.5 }] },
74
+ { id: "django", parents: ["python"], related: [{ to: "flask", w: 0.5 }, { to: "fastapi", w: 0.45 }] },
75
+ { id: "fastapi", parents: ["python"], related: [{ to: "flask", w: 0.55 }, { to: "django", w: 0.45 }] },
76
+ { id: "flask", parents: ["python"] },
77
+ { id: "rails", parents: ["ruby"], synonyms: ["ruby-on-rails", "ror"] },
78
+ { id: "spring", parents: ["java"], synonyms: ["spring-boot", "springboot"] },
79
+ { id: "actix", parents: ["rust"] },
80
+ { id: "gin", parents: ["go"] },
81
+ { id: "phoenix", parents: ["elixir"] },
82
+ { id: "laravel", parents: ["php"] },
83
+ { id: "dotnet", parents: ["csharp"], synonyms: [".net", "asp.net", "dotnet-core"] },
84
+ // ── Infrastructure & DevOps ─────────────────────────────────────────────────
85
+ { 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 }] },
86
+ { id: "docker", parents: ["devops"], related: [{ to: "kubernetes", w: 0.5 }] },
87
+ { id: "terraform", synonyms: ["tf"], related: [{ to: "pulumi", w: 0.55 }, { to: "ansible", w: 0.4 }, { to: "aws", w: 0.4 }] },
88
+ { id: "pulumi", related: [{ to: "terraform", w: 0.55 }] },
89
+ { id: "ansible" },
90
+ { id: "aws", synonyms: ["amazon-web-services"], related: [{ to: "gcp", w: 0.4 }, { to: "azure", w: 0.4 }] },
91
+ { id: "gcp", synonyms: ["google-cloud", "google-cloud-platform"], related: [{ to: "aws", w: 0.4 }, { to: "azure", w: 0.4 }] },
92
+ { id: "azure", synonyms: ["microsoft-azure"], related: [{ to: "aws", w: 0.4 }] },
93
+ { id: "ci-cd", synonyms: ["cicd", "jenkins", "circleci", "circle-ci", "travis"], related: [{ to: "github-actions", w: 0.6 }, { to: "gitlab-ci", w: 0.6 }] },
94
+ { id: "github-actions", parents: ["ci-cd"], synonyms: ["github-action"] },
95
+ { id: "gitlab-ci", parents: ["ci-cd"], synonyms: ["gitlab"] },
96
+ { id: "linux" },
97
+ { id: "nginx" },
98
+ { id: "prometheus", parents: ["observability"], related: [{ to: "grafana", w: 0.6 }] },
99
+ { id: "grafana", parents: ["observability"] },
100
+ { id: "datadog", parents: ["observability"] },
101
+ { id: "opentelemetry", parents: ["observability"], synonyms: ["otel"] },
102
+ { id: "vercel", related: [{ to: "netlify", w: 0.5 }, { to: "nextjs", w: 0.4 }] },
103
+ { id: "netlify" },
104
+ { id: "fly", synonyms: ["fly.io"], related: [{ to: "railway", w: 0.5 }, { to: "render", w: 0.5 }] },
105
+ { id: "railway", related: [{ to: "render", w: 0.5 }] },
106
+ { id: "render" },
107
+ { id: "cloudflare", synonyms: ["cloudflare-workers"] },
108
+ { id: "helm", parents: ["kubernetes"] },
109
+ { id: "argocd", parents: ["kubernetes"] },
110
+ { id: "serverless", parents: ["devops"] },
111
+ // ── Databases & storage ─────────────────────────────────────────────────────
112
+ { id: "postgresql", synonyms: ["postgres", "pg"], related: [{ to: "mysql", w: 0.45 }, { to: "sqlite", w: 0.4 }] },
113
+ { id: "mysql", related: [{ to: "postgresql", w: 0.45 }] },
114
+ { id: "sqlite" },
115
+ { id: "mongodb", synonyms: ["mongo"] },
116
+ { id: "redis", related: [{ to: "caching", w: 0.5 }] },
117
+ { id: "elasticsearch", synonyms: ["elastic"], related: [{ to: "search", w: 0.55 }] },
118
+ { id: "kafka", synonyms: ["apache-kafka"], related: [{ to: "rabbitmq", w: 0.5 }, { to: "message-queue", w: 0.55 }] },
119
+ { id: "rabbitmq", related: [{ to: "message-queue", w: 0.55 }] },
120
+ { id: "cassandra" },
121
+ { id: "dynamodb", parents: ["aws"] },
122
+ { id: "snowflake", parents: ["data-engineering"], related: [{ to: "clickhouse", w: 0.4 }] },
123
+ { id: "clickhouse", parents: ["data-engineering"], related: [{ to: "duckdb", w: 0.35 }] },
124
+ { id: "duckdb", parents: ["data-engineering"] },
125
+ { id: "supabase", related: [{ to: "postgresql", w: 0.5 }, { to: "neon", w: 0.4 }] },
126
+ { id: "planetscale", related: [{ to: "mysql", w: 0.5 }] },
127
+ { id: "neon", related: [{ to: "postgresql", w: 0.5 }] },
128
+ { id: "turso", related: [{ to: "sqlite", w: 0.5 }] },
129
+ { id: "cockroachdb", related: [{ to: "postgresql", w: 0.45 }] },
130
+ { id: "prisma", parents: ["backend"], synonyms: ["@prisma/client"], related: [{ to: "drizzle", w: 0.5 }, { to: "typeorm", w: 0.45 }, { to: "sequelize", w: 0.4 }] },
131
+ { id: "drizzle", synonyms: ["drizzle-orm"], related: [{ to: "prisma", w: 0.5 }] },
132
+ { id: "sequelize", related: [{ to: "typeorm", w: 0.4 }] },
133
+ { id: "typeorm", related: [{ to: "prisma", w: 0.45 }] },
134
+ { id: "sqlalchemy", parents: ["python"] },
135
+ // ── Data engineering & ML ───────────────────────────────────────────────────
136
+ { id: "data-engineering", synonyms: ["data-eng"], related: [{ to: "spark", w: 0.5 }, { to: "airflow", w: 0.5 }, { to: "dbt", w: 0.45 }] },
137
+ { id: "spark", parents: ["data-engineering"], synonyms: ["apache-spark"] },
138
+ { id: "airflow", parents: ["data-engineering"], synonyms: ["apache-airflow"] },
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 }] },
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
+ { id: "pytorch", parents: ["ml"], synonyms: ["torch"], related: [{ to: "tensorflow", w: 0.5 }] },
143
+ { id: "tensorflow", parents: ["ml"], synonyms: ["keras", "tf-keras"] },
144
+ { id: "pandas", parents: ["python"], related: [{ to: "numpy", w: 0.6 }] },
145
+ { id: "numpy", parents: ["python"] },
146
+ { id: "scikit-learn", parents: ["ml"], synonyms: ["sklearn"] },
147
+ { id: "jupyter", parents: ["python"] },
148
+ { id: "langchain", parents: ["llm"], synonyms: ["llamaindex"] },
149
+ { id: "huggingface", parents: ["ml"], synonyms: ["hugging-face"] },
150
+ { id: "openai", parents: ["llm"] },
151
+ { id: "anthropic", parents: ["llm"], synonyms: ["claude"] },
152
+ { id: "rag", parents: ["llm"], synonyms: ["retrieval-augmented-generation"] },
153
+ { id: "mlops", parents: ["ml"], related: [{ to: "devops", w: 0.4 }] },
154
+ // ── Mobile ──────────────────────────────────────────────────────────────────
155
+ { id: "mobile", related: [{ to: "ios", w: 0.5 }, { to: "android", w: 0.5 }] },
156
+ { id: "ios", parents: ["mobile", "swift"], related: [{ to: "android", w: 0.4 }] },
157
+ { id: "android", parents: ["mobile"], related: [{ to: "kotlin", w: 0.4 }] },
158
+ { id: "swiftui", parents: ["ios", "swift"] },
159
+ { id: "react-native", parents: ["mobile", "react"], synonyms: ["reactnative"], related: [{ to: "flutter", w: 0.4 }, { to: "expo", w: 0.6 }] },
160
+ { id: "flutter", parents: ["mobile", "dart"] },
161
+ { id: "expo", parents: ["react-native"] },
162
+ { id: "kotlin-multiplatform", parents: ["mobile", "kotlin"], synonyms: ["kmp"] },
163
+ // ── Domains / capabilities ──────────────────────────────────────────────────
164
+ { id: "frontend", related: [{ to: "react", w: 0.4 }, { to: "css", w: 0.3 }] },
165
+ { id: "backend", related: [{ to: "api-design", w: 0.4 }, { to: "microservices", w: 0.4 }] },
166
+ { id: "devops", related: [{ to: "kubernetes", w: 0.4 }, { to: "ci-cd", w: 0.4 }, { to: "docker", w: 0.4 }] },
167
+ { id: "authentication", synonyms: ["auth", "jwt", "saml", "passport", "auth0", "clerk", "nextauth"], related: [{ to: "oauth", w: 0.6 }, { to: "security", w: 0.5 }] },
168
+ { id: "oauth", parents: ["authentication"], synonyms: ["oauth2", "oidc"], related: [{ to: "security", w: 0.4 }] },
169
+ { id: "security", related: [{ to: "authentication", w: 0.5 }] },
170
+ { id: "payments", synonyms: ["stripe", "braintree", "paddle", "lemonsqueezy", "@stripe/stripe-js"], related: [{ to: "billing", w: 0.6 }] },
171
+ { id: "billing", synonyms: ["recurly", "chargebee"] },
172
+ { id: "api-design", synonyms: ["rest", "restful", "rest-api"], related: [{ to: "graphql", w: 0.4 }, { to: "grpc", w: 0.4 }, { to: "backend", w: 0.4 }] },
173
+ { id: "graphql", synonyms: ["gql"], related: [{ to: "trpc", w: 0.4 }] },
174
+ { id: "trpc", related: [{ to: "graphql", w: 0.4 }] },
175
+ { id: "grpc", synonyms: ["grpc-web"], related: [{ to: "microservices", w: 0.3 }] },
176
+ { id: "microservices" },
177
+ { id: "websockets", synonyms: ["ws", "socket.io"], related: [{ to: "realtime", w: 0.6 }] },
178
+ { id: "realtime", synonyms: ["real-time"] },
179
+ { id: "message-queue", synonyms: ["mq"] },
180
+ { id: "caching", synonyms: ["cache"] },
181
+ { id: "search", synonyms: ["full-text-search"] },
182
+ { id: "observability", synonyms: ["o11y"], related: [{ to: "monitoring", w: 0.6 }] },
183
+ { id: "monitoring", related: [{ to: "prometheus", w: 0.4 }] },
184
+ { id: "testing", related: [{ to: "unit-testing", w: 0.5 }, { to: "e2e-testing", w: 0.5 }] },
185
+ { id: "unit-testing", parents: ["testing"] },
186
+ { id: "e2e-testing", parents: ["testing"], synonyms: ["e2e", "end-to-end-testing"] },
187
+ { id: "jest", parents: ["testing"], related: [{ to: "vitest", w: 0.6 }, { to: "mocha", w: 0.5 }] },
188
+ { id: "vitest", parents: ["testing"], related: [{ to: "jest", w: 0.6 }] },
189
+ { id: "playwright", parents: ["e2e-testing"], related: [{ to: "cypress", w: 0.6 }] },
190
+ { id: "cypress", parents: ["e2e-testing"] },
191
+ { id: "mocha", parents: ["testing"] },
192
+ { id: "pytest", parents: ["testing", "python"] },
193
+ { id: "accessibility", synonyms: ["a11y"] },
194
+ { id: "seo" },
195
+ { id: "performance", synonyms: ["perf", "web-performance"] }
122
196
  ];
123
- var SYNONYMS = {
124
- // Kubernetes aliases
125
- "k8s": "kubernetes",
126
- "kube": "kubernetes",
127
- // Auth / identity
128
- "passport": "authentication",
129
- "oauth2": "oauth",
130
- "oidc": "oauth",
131
- "jwt": "authentication",
132
- "saml": "authentication",
133
- "auth0": "authentication",
134
- "clerk": "authentication",
135
- "nextauth": "authentication",
136
- // Payments
137
- "@stripe/stripe-js": "payments",
138
- "stripe": "payments",
139
- "braintree": "payments",
140
- "paddle": "payments",
141
- "lemonsqueezy": "payments",
142
- "recurly": "billing",
143
- "chargebee": "billing",
144
- // Framework / lib aliases
145
- "next": "nextjs",
146
- "next.js": "nextjs",
147
- "nuxt.js": "nuxt",
148
- "vue.js": "vue",
149
- "angular.js": "angular",
150
- "angularjs": "angular",
151
- "express.js": "express",
152
- "expressjs": "express",
153
- "fastapi": "fastapi",
154
- "nest": "nestjs",
155
- "nest.js": "nestjs",
156
- "sveltekit": "svelte",
157
- // Language aliases
158
- "ts": "typescript",
159
- "js": "javascript",
160
- "py": "python",
161
- "golang": "go",
162
- "c++": "cpp",
163
- "c#": "csharp",
164
- ".net": "dotnet",
165
- "asp.net": "dotnet",
166
- // DB aliases
167
- "postgres": "postgresql",
168
- "pg": "postgresql",
169
- "mongo": "mongodb",
170
- "elastic": "elasticsearch",
171
- // Cloud aliases
172
- "amazon web services": "aws",
173
- "google cloud": "gcp",
174
- "google cloud platform": "gcp",
175
- "microsoft azure": "azure",
176
- // CI/CD aliases
177
- "github actions": "github-actions",
178
- "circle ci": "ci-cd",
179
- "circleci": "ci-cd",
180
- "jenkins": "ci-cd",
181
- "gitlab ci": "ci-cd",
182
- "travis": "ci-cd",
183
- // Mobile
184
- "react native": "mobile",
185
- "flutter": "mobile",
186
- "expo": "mobile",
187
- // AI / ML
188
- "openai": "llm",
189
- "anthropic": "llm",
190
- "langchain": "llm",
191
- "llamaindex": "llm",
192
- "hugging face": "ml",
193
- "huggingface": "ml",
194
- "scikit-learn": "ml",
195
- "sklearn": "ml",
196
- // Data pipeline
197
- "apache kafka": "kafka",
198
- "apache spark": "spark",
199
- "apache airflow": "airflow",
200
- // Misc
201
- "tailwindcss": "tailwind",
202
- "tw": "tailwind",
203
- "gql": "graphql",
204
- "ws": "websockets",
205
- "socket.io": "websockets",
206
- "jest": "testing",
207
- "vitest": "testing",
208
- "playwright": "testing",
209
- "cypress": "testing"
210
- };
211
- var VOCAB_SET = new Set(VOCABULARY);
197
+
198
+ // ../../packages/core/src/vocab/closure.ts
199
+ var PARENT_UP = 0.6;
200
+ var PARENT_DOWN = 0.35;
201
+ var DECAY_FLOOR = 0.25;
202
+ function round3(n) {
203
+ return Math.round(n * 1e3) / 1e3;
204
+ }
205
+ function validateGraph(nodes) {
206
+ const ids = /* @__PURE__ */ new Set();
207
+ for (const n of nodes) {
208
+ if (ids.has(n.id)) throw new Error(`vocab: duplicate id "${n.id}"`);
209
+ ids.add(n.id);
210
+ }
211
+ const seenAlias = /* @__PURE__ */ new Map();
212
+ for (const n of nodes) {
213
+ for (const p of n.parents ?? []) {
214
+ if (p === n.id) throw new Error(`vocab: "${n.id}" lists itself as a parent`);
215
+ if (!ids.has(p)) throw new Error(`vocab: "${n.id}" parent "${p}" is not a defined id`);
216
+ }
217
+ for (const e of n.related ?? []) {
218
+ if (e.to === n.id) throw new Error(`vocab: "${n.id}" relates to itself`);
219
+ if (!ids.has(e.to)) throw new Error(`vocab: "${n.id}" related "${e.to}" is not a defined id`);
220
+ if (!(e.w > 0 && e.w <= 1)) throw new Error(`vocab: "${n.id}"\u2192"${e.to}" weight ${e.w} out of (0,1]`);
221
+ }
222
+ for (const s of n.synonyms ?? []) {
223
+ const alias = s.toLowerCase();
224
+ if (ids.has(alias)) throw new Error(`vocab: synonym "${alias}" collides with a canonical id`);
225
+ const prev = seenAlias.get(alias);
226
+ if (prev && prev !== n.id) throw new Error(`vocab: synonym "${alias}" maps to both "${prev}" and "${n.id}"`);
227
+ seenAlias.set(alias, n.id);
228
+ }
229
+ }
230
+ const visiting = /* @__PURE__ */ new Set();
231
+ const done = /* @__PURE__ */ new Set();
232
+ const parentMap = new Map(nodes.map((n) => [n.id, n.parents ?? []]));
233
+ const walk = (id, path) => {
234
+ if (done.has(id)) return;
235
+ if (visiting.has(id)) throw new Error(`vocab: parent cycle ${[...path, id].join(" \u2192 ")}`);
236
+ visiting.add(id);
237
+ for (const p of parentMap.get(id) ?? []) walk(p, [...path, id]);
238
+ visiting.delete(id);
239
+ done.add(id);
240
+ };
241
+ for (const n of nodes) walk(n.id, []);
242
+ }
243
+ function buildAdjacency(nodes) {
244
+ const adj = /* @__PURE__ */ new Map();
245
+ const add = (from, to, w) => {
246
+ let m = adj.get(from);
247
+ if (!m) adj.set(from, m = /* @__PURE__ */ new Map());
248
+ if (w > (m.get(to) ?? 0)) m.set(to, w);
249
+ };
250
+ for (const n of nodes) {
251
+ for (const p of n.parents ?? []) {
252
+ add(n.id, p, PARENT_UP);
253
+ add(p, n.id, PARENT_DOWN);
254
+ }
255
+ for (const e of n.related ?? []) {
256
+ add(n.id, e.to, e.w);
257
+ add(e.to, n.id, e.w);
258
+ }
259
+ }
260
+ return adj;
261
+ }
262
+ function closureFrom(source, adj) {
263
+ const best = /* @__PURE__ */ new Map();
264
+ for (const [t, w] of adj.get(source) ?? []) {
265
+ if (w >= DECAY_FLOOR) best.set(t, { w: round3(w), via: t });
266
+ }
267
+ const settled = /* @__PURE__ */ new Set([source]);
268
+ while (true) {
269
+ let u;
270
+ let uw = 0;
271
+ for (const [t, e] of best) {
272
+ if (!settled.has(t) && e.w > uw) {
273
+ u = t;
274
+ uw = e.w;
275
+ }
276
+ }
277
+ if (!u) break;
278
+ settled.add(u);
279
+ const via = best.get(u).via;
280
+ for (const [t, we] of adj.get(u) ?? []) {
281
+ if (settled.has(t)) continue;
282
+ const cand = round3(uw * we);
283
+ if (cand >= DECAY_FLOOR && cand > (best.get(t)?.w ?? 0)) {
284
+ best.set(t, { w: cand, via });
285
+ }
286
+ }
287
+ }
288
+ best.delete(source);
289
+ return best;
290
+ }
291
+ function buildGraph(nodes) {
292
+ validateGraph(nodes);
293
+ const ids = new Set(nodes.map((n) => n.id));
294
+ const synonyms = /* @__PURE__ */ new Map();
295
+ for (const n of nodes) {
296
+ for (const s of n.synonyms ?? []) synonyms.set(s.toLowerCase(), n.id);
297
+ }
298
+ const adj = buildAdjacency(nodes);
299
+ const closure = /* @__PURE__ */ new Map();
300
+ for (const n of nodes) closure.set(n.id, closureFrom(n.id, adj));
301
+ return { ids, synonyms, closure };
302
+ }
303
+
304
+ // ../../packages/core/src/vocab/index.ts
305
+ var GRAPH = buildGraph(VOCAB_NODES);
306
+ var VOCABULARY = [...GRAPH.ids];
307
+ var SYNONYMS = Object.fromEntries(GRAPH.synonyms);
212
308
  function normalize(tokens) {
213
309
  const result = /* @__PURE__ */ new Set();
214
310
  for (const raw of tokens) {
215
311
  const lower = raw.toLowerCase().trim();
216
- if (VOCAB_SET.has(lower)) {
312
+ if (GRAPH.ids.has(lower)) {
217
313
  result.add(lower);
218
314
  continue;
219
315
  }
220
- const mapped = SYNONYMS[lower];
221
- if (mapped && VOCAB_SET.has(mapped)) {
222
- result.add(mapped);
223
- }
316
+ const mapped = GRAPH.synonyms.get(lower);
317
+ if (mapped) result.add(mapped);
224
318
  }
225
319
  return Array.from(result);
226
320
  }
@@ -335,10 +429,18 @@ var DEFAULT_GREENHOUSE_SLUGS = flattenTiers(GREENHOUSE_SLUGS_BY_TIER);
335
429
  var DEFAULT_ASHBY_SLUGS = flattenTiers(ASHBY_SLUGS_BY_TIER);
336
430
  var DEFAULT_LEVER_SLUGS = flattenTiers(LEVER_SLUGS_BY_TIER);
337
431
 
338
- // ../../packages/core/src/coastal.ts
432
+ // ../../packages/core/src/partners.ts
339
433
  import { readFileSync } from "fs";
340
434
  import { join } from "path";
341
435
  import { fileURLToPath } from "url";
436
+ var EXAMPLE_BUYER = {
437
+ id: "northstar",
438
+ legalName: "Northstar Talent Partners",
439
+ matchCriteria: { roleTypes: ["full_time"] }
440
+ };
441
+ var BUYER_REGISTRY = {
442
+ [EXAMPLE_BUYER.id]: EXAMPLE_BUYER
443
+ };
342
444
 
343
445
  // src/profile.ts
344
446
  var TERMINALHIRE_DIR = join2(homedir(), ".terminalhire");
@@ -415,10 +517,10 @@ function migrateTagWeights(profile) {
415
517
  if (!profile.tagWeights) {
416
518
  profile.tagWeights = {};
417
519
  }
418
- const now = (/* @__PURE__ */ new Date()).toISOString();
520
+ const seed = profile.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
419
521
  for (const tag of profile.skillTags) {
420
522
  if (!profile.tagWeights[tag]) {
421
- profile.tagWeights[tag] = { count: 1, firstSeen: now, lastSeen: now, sessions: 1 };
523
+ profile.tagWeights[tag] = { count: 1, firstSeen: seed, lastSeen: seed, sessions: 1 };
422
524
  }
423
525
  }
424
526
  }
@@ -463,7 +565,7 @@ var LANGUAGE_TAGS = /* @__PURE__ */ new Set([
463
565
  "clojure",
464
566
  "r"
465
567
  ]);
466
- function accumulateSession(profile, tags, isEmployerContext, inferredSeniority) {
568
+ function accumulateSession(profile, tags, isEmployerContext, inferredSeniority, seniorityIsAuthoritative = false) {
467
569
  const now = (/* @__PURE__ */ new Date()).toISOString();
468
570
  let filtered = normalize(tags);
469
571
  if (isEmployerContext) {
@@ -481,7 +583,9 @@ function accumulateSession(profile, tags, isEmployerContext, inferredSeniority)
481
583
  }
482
584
  }
483
585
  if (inferredSeniority && !isEmployerContext) {
484
- profile.seniority = inferredSeniority;
586
+ if (seniorityIsAuthoritative || !profile.github) {
587
+ profile.seniority = inferredSeniority;
588
+ }
485
589
  }
486
590
  }
487
591
  async function accumulateTags(rawTokens, isEmployerContext, inferredSeniority) {
@@ -489,12 +593,14 @@ async function accumulateTags(rawTokens, isEmployerContext, inferredSeniority) {
489
593
  accumulateSession(profile, rawTokens, isEmployerContext, inferredSeniority);
490
594
  await writeProfile(profile);
491
595
  }
492
- function accumulateGitHubTags(profile, tags) {
596
+ function accumulateGitHubTags(profile, tags, inferredSeniority) {
493
597
  accumulateSession(
494
598
  profile,
495
599
  tags,
496
600
  /* isEmployerContext */
497
- false
601
+ false,
602
+ inferredSeniority,
603
+ true
498
604
  );
499
605
  }
500
606
  async function listSavedJobs() {