terminalhire 0.9.2 → 0.10.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.
@@ -9,6 +9,79 @@ var __export = (target, all) => {
9
9
  __defProp(target, name, { get: all[name], enumerable: true });
10
10
  };
11
11
 
12
+ // bin/version-nudge.js
13
+ var version_nudge_exports = {};
14
+ __export(version_nudge_exports, {
15
+ buildStaleNudge: () => buildStaleNudge,
16
+ cachedStaleNudge: () => cachedStaleNudge,
17
+ compareVersions: () => compareVersions,
18
+ parseVersion: () => parseVersion,
19
+ readLatestVersionFromCache: () => readLatestVersionFromCache,
20
+ readLocalVersion: () => readLocalVersion
21
+ });
22
+ import { readFileSync, existsSync } from "fs";
23
+ import { join } from "path";
24
+ import { homedir } from "os";
25
+ import { fileURLToPath } from "url";
26
+ function parseVersion(v) {
27
+ if (typeof v !== "string") return null;
28
+ const m = v.trim().replace(/^v/, "").match(/^(\d+)\.(\d+)\.(\d+)/);
29
+ if (!m) return null;
30
+ return [Number(m[1]), Number(m[2]), Number(m[3])];
31
+ }
32
+ function compareVersions(a, b) {
33
+ const pa = parseVersion(a);
34
+ const pb = parseVersion(b);
35
+ if (!pa || !pb) return null;
36
+ for (let i = 0; i < 3; i++) {
37
+ if (pa[i] < pb[i]) return -1;
38
+ if (pa[i] > pb[i]) return 1;
39
+ }
40
+ return 0;
41
+ }
42
+ function buildStaleNudge(local, latest) {
43
+ if (compareVersions(local, latest) !== -1) return null;
44
+ return `your terminalhire CLI is behind (${local} \u2192 ${latest}) \u2014 npm i -g terminalhire@latest`;
45
+ }
46
+ function readLocalVersion() {
47
+ try {
48
+ const candidates = [
49
+ join(__dirname, "..", "..", "package.json"),
50
+ join(__dirname, "..", "package.json")
51
+ ];
52
+ for (const p of candidates) {
53
+ if (existsSync(p)) {
54
+ const pkg = JSON.parse(readFileSync(p, "utf8"));
55
+ if (pkg.version) return pkg.version;
56
+ }
57
+ }
58
+ } catch {
59
+ }
60
+ return null;
61
+ }
62
+ function readLatestVersionFromCache() {
63
+ try {
64
+ const cache = JSON.parse(readFileSync(INDEX_CACHE_FILE, "utf8"));
65
+ const v = cache?.index?.cliVersion;
66
+ return typeof v === "string" ? v : null;
67
+ } catch {
68
+ return null;
69
+ }
70
+ }
71
+ function cachedStaleNudge(localVersion) {
72
+ const local = localVersion ?? readLocalVersion();
73
+ if (!local) return null;
74
+ return buildStaleNudge(local, readLatestVersionFromCache());
75
+ }
76
+ var __dirname, INDEX_CACHE_FILE;
77
+ var init_version_nudge = __esm({
78
+ "bin/version-nudge.js"() {
79
+ "use strict";
80
+ __dirname = fileURLToPath(new URL(".", import.meta.url));
81
+ INDEX_CACHE_FILE = join(homedir(), ".terminalhire", "index-cache.json");
82
+ }
83
+ });
84
+
12
85
  // src/open-url.js
13
86
  var open_url_exports = {};
14
87
  __export(open_url_exports, {
@@ -62,14 +135,14 @@ import {
62
135
  randomBytes
63
136
  } from "crypto";
64
137
  import {
65
- readFileSync,
138
+ readFileSync as readFileSync2,
66
139
  writeFileSync,
67
140
  mkdirSync,
68
- existsSync,
141
+ existsSync as existsSync2,
69
142
  rmSync
70
143
  } from "fs";
71
- import { join } from "path";
72
- import { homedir } from "os";
144
+ import { join as join2 } from "path";
145
+ import { homedir as homedir2 } from "os";
73
146
  async function loadKey() {
74
147
  try {
75
148
  const kt = await import("keytar");
@@ -81,8 +154,8 @@ async function loadKey() {
81
154
  } catch {
82
155
  }
83
156
  mkdirSync(TERMINALHIRE_DIR, { recursive: true });
84
- if (existsSync(KEY_FILE)) {
85
- return Buffer.from(readFileSync(KEY_FILE, "utf8").trim(), "hex");
157
+ if (existsSync2(KEY_FILE)) {
158
+ return Buffer.from(readFileSync2(KEY_FILE, "utf8").trim(), "hex");
86
159
  }
87
160
  const key = randomBytes(KEY_BYTES);
88
161
  writeFileSync(KEY_FILE, key.toString("hex"), { mode: 384, encoding: "utf8" });
@@ -105,10 +178,10 @@ function decrypt(blob, key) {
105
178
  return plain.toString("utf8");
106
179
  }
107
180
  async function readGitHubToken() {
108
- if (!existsSync(TOKEN_FILE)) return void 0;
181
+ if (!existsSync2(TOKEN_FILE)) return void 0;
109
182
  try {
110
183
  const key = await loadKey();
111
- const raw = readFileSync(TOKEN_FILE, "utf8");
184
+ const raw = readFileSync2(TOKEN_FILE, "utf8");
112
185
  const blob = JSON.parse(raw);
113
186
  return decrypt(blob, key);
114
187
  } catch {
@@ -128,7 +201,7 @@ async function deleteGitHubToken() {
128
201
  }
129
202
  }
130
203
  async function hasGitHubToken() {
131
- return existsSync(TOKEN_FILE);
204
+ return existsSync2(TOKEN_FILE);
132
205
  }
133
206
  async function runDeviceFlow() {
134
207
  if (process.env["TERMINALHIRE_GITHUB_MOCK"] === "1" || process.env["TERMINALHIRE_GITHUB_MOCK"] === "1" || process.env["JPI_GITHUB_MOCK"] === "1") {
@@ -244,9 +317,9 @@ var TERMINALHIRE_DIR, TOKEN_FILE, KEY_FILE, ALGO, KEY_BYTES, IV_BYTES, GITHUB_SC
244
317
  var init_github_auth = __esm({
245
318
  "src/github-auth.ts"() {
246
319
  "use strict";
247
- TERMINALHIRE_DIR = join(homedir(), ".terminalhire");
248
- TOKEN_FILE = join(TERMINALHIRE_DIR, "github-token.enc");
249
- KEY_FILE = join(TERMINALHIRE_DIR, "key");
320
+ TERMINALHIRE_DIR = join2(homedir2(), ".terminalhire");
321
+ TOKEN_FILE = join2(TERMINALHIRE_DIR, "github-token.enc");
322
+ KEY_FILE = join2(TERMINALHIRE_DIR, "key");
250
323
  ALGO = "aes-256-gcm";
251
324
  KEY_BYTES = 32;
252
325
  IV_BYTES = 12;
@@ -1943,14 +2016,14 @@ async function mapWithConcurrency(items, limit, fn) {
1943
2016
  if (items.length === 0) return results;
1944
2017
  const workers = Math.max(1, Math.min(Math.floor(limit) || 1, items.length));
1945
2018
  let next = 0;
1946
- async function run19() {
2019
+ async function run20() {
1947
2020
  for (; ; ) {
1948
2021
  const i = next++;
1949
2022
  if (i >= items.length) return;
1950
2023
  results[i] = await fn(items[i], i);
1951
2024
  }
1952
2025
  }
1953
- await Promise.all(Array.from({ length: workers }, run19));
2026
+ await Promise.all(Array.from({ length: workers }, run20));
1954
2027
  return results;
1955
2028
  }
1956
2029
  var init_concurrency = __esm({
@@ -2793,21 +2866,21 @@ var init_feeds = __esm({
2793
2866
  });
2794
2867
 
2795
2868
  // ../../packages/core/src/partners.ts
2796
- import { readFileSync as readFileSync2 } from "fs";
2797
- import { join as join2 } from "path";
2798
- import { fileURLToPath } from "url";
2869
+ import { readFileSync as readFileSync3 } from "fs";
2870
+ import { join as join3 } from "path";
2871
+ import { fileURLToPath as fileURLToPath2 } from "url";
2799
2872
  function resolveDataPath() {
2800
2873
  try {
2801
- const dir = fileURLToPath(new URL("../../../data", import.meta.url));
2802
- return join2(dir, "partner-roles.json");
2874
+ const dir = fileURLToPath2(new URL("../../../data", import.meta.url));
2875
+ return join3(dir, "partner-roles.json");
2803
2876
  } catch {
2804
- return join2(process.cwd(), "data", "partner-roles.json");
2877
+ return join3(process.cwd(), "data", "partner-roles.json");
2805
2878
  }
2806
2879
  }
2807
2880
  function loadPartnerRoles() {
2808
2881
  const filePath = resolveDataPath();
2809
2882
  try {
2810
- const raw = readFileSync2(filePath, "utf-8");
2883
+ const raw = readFileSync3(filePath, "utf-8");
2811
2884
  const parsed = JSON.parse(raw);
2812
2885
  if (!Array.isArray(parsed)) {
2813
2886
  console.warn("[partners] partner-roles.json is not an array \u2014 skipping");
@@ -6208,13 +6281,13 @@ import {
6208
6281
  randomBytes as randomBytes4
6209
6282
  } from "crypto";
6210
6283
  import {
6211
- readFileSync as readFileSync3,
6284
+ readFileSync as readFileSync4,
6212
6285
  writeFileSync as writeFileSync2,
6213
6286
  mkdirSync as mkdirSync2,
6214
- existsSync as existsSync2
6287
+ existsSync as existsSync3
6215
6288
  } from "fs";
6216
- import { join as join3 } from "path";
6217
- import { homedir as homedir2 } from "os";
6289
+ import { join as join4 } from "path";
6290
+ import { homedir as homedir3 } from "os";
6218
6291
  async function loadKey2() {
6219
6292
  try {
6220
6293
  const kt = await import("keytar");
@@ -6228,8 +6301,8 @@ async function loadKey2() {
6228
6301
  } catch {
6229
6302
  }
6230
6303
  mkdirSync2(TERMINALHIRE_DIR2, { recursive: true });
6231
- if (existsSync2(KEY_FILE2)) {
6232
- return Buffer.from(readFileSync3(KEY_FILE2, "utf8").trim(), "hex");
6304
+ if (existsSync3(KEY_FILE2)) {
6305
+ return Buffer.from(readFileSync4(KEY_FILE2, "utf8").trim(), "hex");
6233
6306
  }
6234
6307
  const key = randomBytes4(KEY_BYTES2);
6235
6308
  writeFileSync2(KEY_FILE2, key.toString("hex"), { mode: 384, encoding: "utf8" });
@@ -6290,10 +6363,10 @@ function migrateTagWeights(profile) {
6290
6363
  }
6291
6364
  }
6292
6365
  async function readProfile() {
6293
- if (!existsSync2(PROFILE_FILE)) return blankProfile();
6366
+ if (!existsSync3(PROFILE_FILE)) return blankProfile();
6294
6367
  try {
6295
6368
  const key = await loadKey2();
6296
- const raw = readFileSync3(PROFILE_FILE, "utf8");
6369
+ const raw = readFileSync4(PROFILE_FILE, "utf8");
6297
6370
  const blob = JSON.parse(raw);
6298
6371
  const plaintext = decrypt2(blob, key);
6299
6372
  const parsed = JSON.parse(plaintext);
@@ -6398,9 +6471,9 @@ var init_profile = __esm({
6398
6471
  "src/profile.ts"() {
6399
6472
  "use strict";
6400
6473
  init_src();
6401
- TERMINALHIRE_DIR2 = join3(homedir2(), ".terminalhire");
6402
- PROFILE_FILE = join3(TERMINALHIRE_DIR2, "profile.enc");
6403
- KEY_FILE2 = join3(TERMINALHIRE_DIR2, "key");
6474
+ TERMINALHIRE_DIR2 = join4(homedir3(), ".terminalhire");
6475
+ PROFILE_FILE = join4(TERMINALHIRE_DIR2, "profile.enc");
6476
+ KEY_FILE2 = join4(TERMINALHIRE_DIR2, "key");
6404
6477
  ALGO2 = "aes-256-gcm";
6405
6478
  KEY_BYTES2 = 32;
6406
6479
  IV_BYTES2 = 12;
@@ -6429,13 +6502,13 @@ var init_profile = __esm({
6429
6502
  });
6430
6503
 
6431
6504
  // src/config.ts
6432
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync3 } from "fs";
6433
- import { join as join4 } from "path";
6434
- import { homedir as homedir3 } from "os";
6505
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync4 } from "fs";
6506
+ import { join as join5 } from "path";
6507
+ import { homedir as homedir4 } from "os";
6435
6508
  function readConfig() {
6436
6509
  try {
6437
- if (!existsSync3(CONFIG_FILE)) return { ...DEFAULT_CONFIG };
6438
- const raw = readFileSync4(CONFIG_FILE, "utf8");
6510
+ if (!existsSync4(CONFIG_FILE)) return { ...DEFAULT_CONFIG };
6511
+ const raw = readFileSync5(CONFIG_FILE, "utf8");
6439
6512
  const parsed = JSON.parse(raw);
6440
6513
  return { ...DEFAULT_CONFIG, ...parsed };
6441
6514
  } catch {
@@ -6451,18 +6524,22 @@ function writeConfig(config) {
6451
6524
  function isPeerConnectEnabled() {
6452
6525
  return readConfig().peerConnect === true;
6453
6526
  }
6527
+ function isInboundNudgeMuted() {
6528
+ return readConfig().inboundNudgeMuted === true;
6529
+ }
6454
6530
  var TERMINALHIRE_DIR3, CONFIG_FILE, DEFAULT_CONFIG;
6455
6531
  var init_config = __esm({
6456
6532
  "src/config.ts"() {
6457
6533
  "use strict";
6458
- TERMINALHIRE_DIR3 = join4(homedir3(), ".terminalhire");
6459
- CONFIG_FILE = join4(TERMINALHIRE_DIR3, "config.json");
6534
+ TERMINALHIRE_DIR3 = join5(homedir4(), ".terminalhire");
6535
+ CONFIG_FILE = join5(TERMINALHIRE_DIR3, "config.json");
6460
6536
  DEFAULT_CONFIG = {
6461
6537
  nudge: "session",
6462
6538
  peerConnect: false,
6463
6539
  peerConnectPrompted: false,
6464
6540
  resumePublishPrompted: false,
6465
- chatDisclosureAck: false
6541
+ chatDisclosureAck: false,
6542
+ inboundNudgeMuted: false
6466
6543
  };
6467
6544
  }
6468
6545
  });
@@ -6612,12 +6689,12 @@ async function runLogin() {
6612
6689
  let ghProfile;
6613
6690
  if (process.env["TERMINALHIRE_GITHUB_MOCK"] === "1" || process.env["JPI_GITHUB_MOCK"] === "1") {
6614
6691
  const { createRequire: createRequire2 } = await import("module");
6615
- const { fileURLToPath: fileURLToPath7 } = await import("url");
6616
- const { join: join25, dirname: dirname3 } = await import("path");
6617
- const __dirname6 = fileURLToPath7(new URL(".", import.meta.url));
6618
- const fixturePath = join25(__dirname6, "../../fixtures/github-sample.json");
6619
- const { readFileSync: readFileSync23 } = await import("fs");
6620
- ghProfile = JSON.parse(readFileSync23(fixturePath, "utf8"));
6692
+ const { fileURLToPath: fileURLToPath8 } = await import("url");
6693
+ const { join: join26, dirname: dirname3 } = await import("path");
6694
+ const __dirname7 = fileURLToPath8(new URL(".", import.meta.url));
6695
+ const fixturePath = join26(__dirname7, "../../fixtures/github-sample.json");
6696
+ const { readFileSync: readFileSync24 } = await import("fs");
6697
+ ghProfile = JSON.parse(readFileSync24(fixturePath, "utf8"));
6621
6698
  } else {
6622
6699
  ghProfile = await fetchGitHubProfile2(login, token);
6623
6700
  }
@@ -6739,14 +6816,14 @@ var jpi_jobs_exports = {};
6739
6816
  __export(jpi_jobs_exports, {
6740
6817
  run: () => run2
6741
6818
  });
6742
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync4 } from "fs";
6743
- import { join as join5 } from "path";
6744
- import { homedir as homedir4 } from "os";
6819
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync5 } from "fs";
6820
+ import { join as join6 } from "path";
6821
+ import { homedir as homedir5 } from "os";
6745
6822
  import { createInterface as createInterface2 } from "readline";
6746
- import { fileURLToPath as fileURLToPath2 } from "url";
6823
+ import { fileURLToPath as fileURLToPath3 } from "url";
6747
6824
  function readIndexCache() {
6748
6825
  try {
6749
- const raw = readFileSync5(INDEX_CACHE_FILE, "utf8");
6826
+ const raw = readFileSync6(INDEX_CACHE_FILE2, "utf8");
6750
6827
  const entry = JSON.parse(raw);
6751
6828
  if (Date.now() - entry.ts < INDEX_TTL_MS) return entry.index;
6752
6829
  return null;
@@ -6756,7 +6833,7 @@ function readIndexCache() {
6756
6833
  }
6757
6834
  function writeIndexCache(index) {
6758
6835
  mkdirSync4(TERMINALHIRE_DIR4, { recursive: true });
6759
- writeFileSync4(INDEX_CACHE_FILE, JSON.stringify({ ts: Date.now(), index }), "utf8");
6836
+ writeFileSync4(INDEX_CACHE_FILE2, JSON.stringify({ ts: Date.now(), index }), "utf8");
6760
6837
  }
6761
6838
  async function fetchIndex() {
6762
6839
  const cached = readIndexCache();
@@ -6921,10 +6998,10 @@ async function run2() {
6921
6998
  acceptance: profile.acceptance
6922
6999
  });
6923
7000
  try {
6924
- const cacheRaw = readFileSync5(INDEX_CACHE_FILE, "utf8");
7001
+ const cacheRaw = readFileSync6(INDEX_CACHE_FILE2, "utf8");
6925
7002
  const cacheEntry = JSON.parse(cacheRaw);
6926
7003
  cacheEntry.matchCount = results.length;
6927
- writeFileSync4(INDEX_CACHE_FILE, JSON.stringify(cacheEntry), "utf8");
7004
+ writeFileSync4(INDEX_CACHE_FILE2, JSON.stringify(cacheEntry), "utf8");
6928
7005
  } catch {
6929
7006
  }
6930
7007
  if (results.length === 0) {
@@ -6969,13 +7046,13 @@ Open this URL to apply directly (no data shared):
6969
7046
  process.exit(1);
6970
7047
  }
6971
7048
  }
6972
- var __dirname, TERMINALHIRE_DIR4, INDEX_CACHE_FILE, INDEX_TTL_MS, API_URL, DEFAULT_LIMIT, args, limitArg, LIMIT, REMOTE_ONLY, SHOW_ALL;
7049
+ var __dirname2, TERMINALHIRE_DIR4, INDEX_CACHE_FILE2, INDEX_TTL_MS, API_URL, DEFAULT_LIMIT, args, limitArg, LIMIT, REMOTE_ONLY, SHOW_ALL;
6973
7050
  var init_jpi_jobs = __esm({
6974
7051
  "bin/jpi-jobs.js"() {
6975
7052
  "use strict";
6976
- __dirname = fileURLToPath2(new URL(".", import.meta.url));
6977
- TERMINALHIRE_DIR4 = join5(homedir4(), ".terminalhire");
6978
- INDEX_CACHE_FILE = join5(TERMINALHIRE_DIR4, "index-cache.json");
7053
+ __dirname2 = fileURLToPath3(new URL(".", import.meta.url));
7054
+ TERMINALHIRE_DIR4 = join6(homedir5(), ".terminalhire");
7055
+ INDEX_CACHE_FILE2 = join6(TERMINALHIRE_DIR4, "index-cache.json");
6979
7056
  INDEX_TTL_MS = 15 * 60 * 1e3;
6980
7057
  API_URL = process.env["TERMINALHIRE_API_URL"] ?? process.env["JPI_API_URL"] ?? "https://terminalhire.com";
6981
7058
  DEFAULT_LIMIT = 10;
@@ -6988,12 +7065,12 @@ var init_jpi_jobs = __esm({
6988
7065
  });
6989
7066
 
6990
7067
  // bin/directory.js
6991
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5 } from "fs";
6992
- import { join as join6 } from "path";
6993
- import { homedir as homedir5 } from "os";
7068
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5 } from "fs";
7069
+ import { join as join7 } from "path";
7070
+ import { homedir as homedir6 } from "os";
6994
7071
  function readDirectoryCache() {
6995
7072
  try {
6996
- const entry = JSON.parse(readFileSync6(DIRECTORY_CACHE_FILE, "utf8"));
7073
+ const entry = JSON.parse(readFileSync7(DIRECTORY_CACHE_FILE, "utf8"));
6997
7074
  if (typeof entry.ts === "number" && Number.isFinite(entry.ts) && Date.now() - entry.ts < INDEX_TTL_MS2) {
6998
7075
  return { index: entry.index, ts: entry.ts };
6999
7076
  }
@@ -7008,7 +7085,7 @@ function writeDirectoryCache(index) {
7008
7085
  }
7009
7086
  function readProject() {
7010
7087
  try {
7011
- return JSON.parse(readFileSync6(PROJECT_FILE, "utf8"));
7088
+ return JSON.parse(readFileSync7(PROJECT_FILE, "utf8"));
7012
7089
  } catch {
7013
7090
  return null;
7014
7091
  }
@@ -7056,9 +7133,9 @@ var TERMINALHIRE_DIR5, DIRECTORY_CACHE_FILE, PROJECT_FILE, INDEX_TTL_MS2, API_UR
7056
7133
  var init_directory2 = __esm({
7057
7134
  "bin/directory.js"() {
7058
7135
  "use strict";
7059
- TERMINALHIRE_DIR5 = join6(homedir5(), ".terminalhire");
7060
- DIRECTORY_CACHE_FILE = join6(TERMINALHIRE_DIR5, "directory-cache.json");
7061
- PROJECT_FILE = join6(TERMINALHIRE_DIR5, "project.json");
7136
+ TERMINALHIRE_DIR5 = join7(homedir6(), ".terminalhire");
7137
+ DIRECTORY_CACHE_FILE = join7(TERMINALHIRE_DIR5, "directory-cache.json");
7138
+ PROJECT_FILE = join7(TERMINALHIRE_DIR5, "project.json");
7062
7139
  INDEX_TTL_MS2 = 15 * 60 * 1e3;
7063
7140
  API_URL2 = process.env["TERMINALHIRE_API_URL"] ?? process.env["JPI_API_URL"] ?? "https://terminalhire.com";
7064
7141
  }
@@ -7187,13 +7264,13 @@ var jpi_project_exports = {};
7187
7264
  __export(jpi_project_exports, {
7188
7265
  run: () => run4
7189
7266
  });
7190
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6 } from "fs";
7191
- import { join as join7 } from "path";
7192
- import { homedir as homedir6 } from "os";
7267
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6 } from "fs";
7268
+ import { join as join8 } from "path";
7269
+ import { homedir as homedir7 } from "os";
7193
7270
  import { createInterface as createInterface4 } from "readline";
7194
7271
  function readProject2() {
7195
7272
  try {
7196
- return JSON.parse(readFileSync7(PROJECT_FILE2, "utf8"));
7273
+ return JSON.parse(readFileSync8(PROJECT_FILE2, "utf8"));
7197
7274
  } catch {
7198
7275
  return null;
7199
7276
  }
@@ -7285,8 +7362,8 @@ var TERMINALHIRE_DIR6, PROJECT_FILE2, args3, SHOW, declarationArg;
7285
7362
  var init_jpi_project = __esm({
7286
7363
  "bin/jpi-project.js"() {
7287
7364
  "use strict";
7288
- TERMINALHIRE_DIR6 = join7(homedir6(), ".terminalhire");
7289
- PROJECT_FILE2 = join7(TERMINALHIRE_DIR6, "project.json");
7365
+ TERMINALHIRE_DIR6 = join8(homedir7(), ".terminalhire");
7366
+ PROJECT_FILE2 = join8(TERMINALHIRE_DIR6, "project.json");
7290
7367
  args3 = process.argv.slice(2);
7291
7368
  SHOW = args3.includes("--show");
7292
7369
  declarationArg = args3.filter((a) => !a.startsWith("--")).join(" ").trim();
@@ -7298,13 +7375,13 @@ var jpi_bounties_exports = {};
7298
7375
  __export(jpi_bounties_exports, {
7299
7376
  run: () => run5
7300
7377
  });
7301
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "fs";
7302
- import { join as join8 } from "path";
7303
- import { homedir as homedir7 } from "os";
7378
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "fs";
7379
+ import { join as join9 } from "path";
7380
+ import { homedir as homedir8 } from "os";
7304
7381
  import { createInterface as createInterface5 } from "readline";
7305
7382
  function readIndexCache2() {
7306
7383
  try {
7307
- const entry = JSON.parse(readFileSync8(INDEX_CACHE_FILE2, "utf8"));
7384
+ const entry = JSON.parse(readFileSync9(INDEX_CACHE_FILE3, "utf8"));
7308
7385
  if (Date.now() - entry.ts < INDEX_TTL_MS3) return entry.index;
7309
7386
  return null;
7310
7387
  } catch {
@@ -7313,7 +7390,7 @@ function readIndexCache2() {
7313
7390
  }
7314
7391
  function writeIndexCache2(index) {
7315
7392
  mkdirSync7(TERMINALHIRE_DIR7, { recursive: true });
7316
- writeFileSync7(INDEX_CACHE_FILE2, JSON.stringify({ ts: Date.now(), index }), "utf8");
7393
+ writeFileSync7(INDEX_CACHE_FILE3, JSON.stringify({ ts: Date.now(), index }), "utf8");
7317
7394
  }
7318
7395
  async function fetchIndex2() {
7319
7396
  const cached = readIndexCache2();
@@ -7417,12 +7494,12 @@ Open this to claim/work the bounty (you go straight to the source \u2014 we neve
7417
7494
  process.exit(1);
7418
7495
  }
7419
7496
  }
7420
- var TERMINALHIRE_DIR7, INDEX_CACHE_FILE2, INDEX_TTL_MS3, API_URL4, DEFAULT_LIMIT3, args4, limitArg3, LIMIT3, PRICED_ONLY, SHOW_ALL3, WINNABLE_ONLY, EFFORT_LABEL;
7497
+ var TERMINALHIRE_DIR7, INDEX_CACHE_FILE3, INDEX_TTL_MS3, API_URL4, DEFAULT_LIMIT3, args4, limitArg3, LIMIT3, PRICED_ONLY, SHOW_ALL3, WINNABLE_ONLY, EFFORT_LABEL;
7421
7498
  var init_jpi_bounties = __esm({
7422
7499
  "bin/jpi-bounties.js"() {
7423
7500
  "use strict";
7424
- TERMINALHIRE_DIR7 = join8(homedir7(), ".terminalhire");
7425
- INDEX_CACHE_FILE2 = join8(TERMINALHIRE_DIR7, "index-cache.json");
7501
+ TERMINALHIRE_DIR7 = join9(homedir8(), ".terminalhire");
7502
+ INDEX_CACHE_FILE3 = join9(TERMINALHIRE_DIR7, "index-cache.json");
7426
7503
  INDEX_TTL_MS3 = 15 * 60 * 1e3;
7427
7504
  API_URL4 = process.env["TERMINALHIRE_API_URL"] ?? process.env["JPI_API_URL"] ?? "https://terminalhire.com";
7428
7505
  DEFAULT_LIMIT3 = 15;
@@ -7447,16 +7524,16 @@ __export(claims_exports, {
7447
7524
  removeClaim: () => removeClaim,
7448
7525
  updateClaim: () => updateClaim
7449
7526
  });
7450
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8, renameSync, existsSync as existsSync5 } from "fs";
7451
- import { join as join9 } from "path";
7452
- import { homedir as homedir8 } from "os";
7527
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8, renameSync, existsSync as existsSync6 } from "fs";
7528
+ import { join as join10 } from "path";
7529
+ import { homedir as homedir9 } from "os";
7453
7530
  function nowISO() {
7454
7531
  return (/* @__PURE__ */ new Date()).toISOString();
7455
7532
  }
7456
7533
  function readClaims() {
7457
7534
  try {
7458
- if (!existsSync5(CLAIMS_FILE)) return [];
7459
- const data = JSON.parse(readFileSync9(CLAIMS_FILE, "utf8"));
7535
+ if (!existsSync6(CLAIMS_FILE)) return [];
7536
+ const data = JSON.parse(readFileSync10(CLAIMS_FILE, "utf8"));
7460
7537
  return Array.isArray(data?.claims) ? data.claims : [];
7461
7538
  } catch {
7462
7539
  return [];
@@ -7523,8 +7600,8 @@ var TERMINALHIRE_DIR8, CLAIMS_FILE, TERMINAL_STATES;
7523
7600
  var init_claims = __esm({
7524
7601
  "src/claims.ts"() {
7525
7602
  "use strict";
7526
- TERMINALHIRE_DIR8 = join9(homedir8(), ".terminalhire");
7527
- CLAIMS_FILE = join9(TERMINALHIRE_DIR8, "claims.json");
7603
+ TERMINALHIRE_DIR8 = join10(homedir9(), ".terminalhire");
7604
+ CLAIMS_FILE = join10(TERMINALHIRE_DIR8, "claims.json");
7528
7605
  TERMINAL_STATES = /* @__PURE__ */ new Set(["merged", "abandoned"]);
7529
7606
  }
7530
7607
  });
@@ -7534,9 +7611,9 @@ var jpi_claim_exports = {};
7534
7611
  __export(jpi_claim_exports, {
7535
7612
  run: () => run6
7536
7613
  });
7537
- import { readFileSync as readFileSync10, existsSync as existsSync6 } from "fs";
7538
- import { join as join10 } from "path";
7539
- import { homedir as homedir9 } from "os";
7614
+ import { readFileSync as readFileSync11, existsSync as existsSync7 } from "fs";
7615
+ import { join as join11 } from "path";
7616
+ import { homedir as homedir10 } from "os";
7540
7617
  import { execFile } from "child_process";
7541
7618
  import { promisify } from "util";
7542
7619
  import { createInterface as createInterface6 } from "readline";
@@ -7583,8 +7660,8 @@ function parseRepoFromRemote(url) {
7583
7660
  }
7584
7661
  function findBountyInCache(bountyId) {
7585
7662
  try {
7586
- if (!existsSync6(INDEX_CACHE_FILE3)) return null;
7587
- const entry = JSON.parse(readFileSync10(INDEX_CACHE_FILE3, "utf8"));
7663
+ if (!existsSync7(INDEX_CACHE_FILE4)) return null;
7664
+ const entry = JSON.parse(readFileSync11(INDEX_CACHE_FILE4, "utf8"));
7588
7665
  const jobs = entry?.index?.jobs ?? [];
7589
7666
  const job = jobs.find((j) => j.id === bountyId && j.source === "bounty");
7590
7667
  return job ?? null;
@@ -8088,12 +8165,12 @@ async function run6() {
8088
8165
  process.exit(1);
8089
8166
  }
8090
8167
  }
8091
- var TERMINALHIRE_DIR9, INDEX_CACHE_FILE3, GH_API, GH_HEADERS, pExecFile, VALUE_FLAGS;
8168
+ var TERMINALHIRE_DIR9, INDEX_CACHE_FILE4, GH_API, GH_HEADERS, pExecFile, VALUE_FLAGS;
8092
8169
  var init_jpi_claim = __esm({
8093
8170
  "bin/jpi-claim.js"() {
8094
8171
  "use strict";
8095
- TERMINALHIRE_DIR9 = join10(homedir9(), ".terminalhire");
8096
- INDEX_CACHE_FILE3 = join10(TERMINALHIRE_DIR9, "index-cache.json");
8172
+ TERMINALHIRE_DIR9 = join11(homedir10(), ".terminalhire");
8173
+ INDEX_CACHE_FILE4 = join11(TERMINALHIRE_DIR9, "index-cache.json");
8097
8174
  GH_API = "https://api.github.com";
8098
8175
  GH_HEADERS = { "User-Agent": "terminalhire-claim", Accept: "application/vnd.github+json" };
8099
8176
  pExecFile = promisify(execFile);
@@ -8377,7 +8454,7 @@ function finalize(build) {
8377
8454
  };
8378
8455
  }
8379
8456
  function reconstruct(files, opts = {}) {
8380
- const join25 = opts.joinSidechains !== false;
8457
+ const join26 = opts.joinSidechains !== false;
8381
8458
  const mains = [];
8382
8459
  const sidechains = [];
8383
8460
  for (const file of files) {
@@ -8402,7 +8479,7 @@ function reconstruct(files, opts = {}) {
8402
8479
  }
8403
8480
  const orphanedSidechainPaths = [];
8404
8481
  const joinedPaths = /* @__PURE__ */ new Set();
8405
- if (join25) {
8482
+ if (join26) {
8406
8483
  const sidechainsBySession = /* @__PURE__ */ new Map();
8407
8484
  for (const sc of sidechains) {
8408
8485
  const acc = sidechainsBySession.get(sc.sessionId) ?? [];
@@ -8889,12 +8966,12 @@ function deriveRecoveryDepth(episodes, nodesByUuid) {
8889
8966
  continue;
8890
8967
  }
8891
8968
  spansConsidered++;
8892
- let run19 = 0;
8969
+ let run20 = 0;
8893
8970
  const closeChain = () => {
8894
- if (run19 > 0) {
8971
+ if (run20 > 0) {
8895
8972
  recoveryChains++;
8896
- totalDepth += run19;
8897
- run19 = 0;
8973
+ totalDepth += run20;
8974
+ run20 = 0;
8898
8975
  }
8899
8976
  };
8900
8977
  for (const uuid of episode.mainNodeUuids) {
@@ -8903,9 +8980,9 @@ function deriveRecoveryDepth(episodes, nodesByUuid) {
8903
8980
  continue;
8904
8981
  }
8905
8982
  if (node.kind === "tool_result" /* ToolResult */ && node.isError) {
8906
- run19++;
8907
- if (run19 > maxConsecutiveErrors) {
8908
- maxConsecutiveErrors = run19;
8983
+ run20++;
8984
+ if (run20 > maxConsecutiveErrors) {
8985
+ maxConsecutiveErrors = run20;
8909
8986
  }
8910
8987
  } else if (node.kind === "tool_result" /* ToolResult */ && !node.isError) {
8911
8988
  closeChain();
@@ -8950,25 +9027,25 @@ var init_episodes = __esm({
8950
9027
  // src/web-session.ts
8951
9028
  import {
8952
9029
  chmodSync as chmodSync2,
8953
- existsSync as existsSync7,
9030
+ existsSync as existsSync8,
8954
9031
  mkdirSync as mkdirSync9,
8955
- readFileSync as readFileSync11,
9032
+ readFileSync as readFileSync12,
8956
9033
  rmSync as rmSync2,
8957
9034
  writeFileSync as writeFileSync9
8958
9035
  } from "fs";
8959
- import { homedir as homedir10 } from "os";
8960
- import { join as join11 } from "path";
9036
+ import { homedir as homedir11 } from "os";
9037
+ import { join as join12 } from "path";
8961
9038
  function terminalhireDir() {
8962
- return join11(homedir10(), ".terminalhire");
9039
+ return join12(homedir11(), ".terminalhire");
8963
9040
  }
8964
9041
  function webSessionFilePath() {
8965
- return join11(terminalhireDir(), "web-session");
9042
+ return join12(terminalhireDir(), "web-session");
8966
9043
  }
8967
9044
  function readWebSessionFile() {
8968
9045
  try {
8969
9046
  const path = webSessionFilePath();
8970
- if (!existsSync7(path)) return null;
8971
- const v = readFileSync11(path, "utf8").trim();
9047
+ if (!existsSync8(path)) return null;
9048
+ const v = readFileSync12(path, "utf8").trim();
8972
9049
  return v.length > 0 ? v : null;
8973
9050
  } catch {
8974
9051
  return null;
@@ -9010,14 +9087,14 @@ __export(trajectory_exports, {
9010
9087
  runTrajectoryPush: () => runTrajectoryPush
9011
9088
  });
9012
9089
  import {
9013
- existsSync as existsSync8,
9090
+ existsSync as existsSync9,
9014
9091
  mkdirSync as mkdirSync10,
9015
- readFileSync as readFileSync12,
9092
+ readFileSync as readFileSync13,
9016
9093
  readdirSync,
9017
9094
  writeFileSync as writeFileSync10
9018
9095
  } from "fs";
9019
- import { homedir as homedir11 } from "os";
9020
- import { join as join12 } from "path";
9096
+ import { homedir as homedir12 } from "os";
9097
+ import { join as join13 } from "path";
9021
9098
  function isRecord4(value) {
9022
9099
  return typeof value === "object" && value !== null && !Array.isArray(value);
9023
9100
  }
@@ -9049,7 +9126,7 @@ function findJsonlFiles(dir) {
9049
9126
  return out;
9050
9127
  }
9051
9128
  for (const entry of entries) {
9052
- const full = join12(dir, entry.name);
9129
+ const full = join13(dir, entry.name);
9053
9130
  if (entry.isDirectory()) {
9054
9131
  out.push(...findJsonlFiles(full));
9055
9132
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
@@ -9063,7 +9140,7 @@ function loadCorpus(paths) {
9063
9140
  for (const path of paths) {
9064
9141
  let text;
9065
9142
  try {
9066
- text = readFileSync12(path, "utf8");
9143
+ text = readFileSync13(path, "utf8");
9067
9144
  } catch {
9068
9145
  continue;
9069
9146
  }
@@ -9162,10 +9239,10 @@ function renderMarkdown(view) {
9162
9239
  return lines.join("\n");
9163
9240
  }
9164
9241
  function writeExportArtifacts(score, markdown) {
9165
- const dir = join12(homedir11(), ".terminalhire");
9242
+ const dir = join13(homedir12(), ".terminalhire");
9166
9243
  mkdirSync10(dir, { recursive: true });
9167
- const jsonPath = join12(dir, "trajectory-export.json");
9168
- const mdPath = join12(dir, "trajectory-export.md");
9244
+ const jsonPath = join13(dir, "trajectory-export.json");
9245
+ const mdPath = join13(dir, "trajectory-export.md");
9169
9246
  writeFileSync10(jsonPath, JSON.stringify(score, null, 2) + "\n", "utf8");
9170
9247
  writeFileSync10(mdPath, markdown, "utf8");
9171
9248
  return { jsonPath, mdPath };
@@ -9186,8 +9263,8 @@ function renderInward(allNodes, view, files) {
9186
9263
  console.log("");
9187
9264
  }
9188
9265
  function buildTrajectory() {
9189
- const projectsDir = join12(homedir11(), ".claude", "projects");
9190
- if (!existsSync8(projectsDir)) return null;
9266
+ const projectsDir = join13(homedir12(), ".claude", "projects");
9267
+ if (!existsSync9(projectsDir)) return null;
9191
9268
  const paths = findJsonlFiles(projectsDir);
9192
9269
  if (paths.length === 0) return null;
9193
9270
  const files = loadCorpus(paths);
@@ -9636,9 +9713,24 @@ async function runIntroRequest(args5, overrides) {
9636
9713
  deps.exit(1);
9637
9714
  return;
9638
9715
  }
9639
- deps.log(`
9716
+ let notified = false;
9717
+ try {
9718
+ const data = await res.json();
9719
+ notified = data.notified === true;
9720
+ } catch {
9721
+ }
9722
+ if (notified) {
9723
+ deps.log(`
9640
9724
  Intro request sent to @${targetLogin}. They will see only your public login`);
9641
- deps.log(" until they accept; your contact is shared only if they do.\n");
9725
+ deps.log(" until they accept; your contact is shared only if they do.\n");
9726
+ } else {
9727
+ deps.log(`
9728
+ Intro request recorded for @${targetLogin} \u2014 but we couldn't email them`);
9729
+ deps.log(" automatically (no public email on file). Send it to them yourself: they can run");
9730
+ deps.log(" `terminalhire intro --list` (or open their dashboard) to see and accept it.");
9731
+ deps.log(" They will see only your public login until they accept; your contact is");
9732
+ deps.log(" shared only if they do.\n");
9733
+ }
9642
9734
  }
9643
9735
  async function runIntroDecision(args5, overrides) {
9644
9736
  const deps = { ...defaultIntroDeps(), ...overrides };
@@ -9849,13 +9941,13 @@ var init_jpi_intro = __esm({
9849
9941
  });
9850
9942
 
9851
9943
  // src/chat-keystore.ts
9852
- import { existsSync as existsSync9, mkdirSync as mkdirSync11, readFileSync as readFileSync13, writeFileSync as writeFileSync11, rmSync as rmSync3 } from "fs";
9853
- import { homedir as homedir12 } from "os";
9854
- import { join as join13 } from "path";
9944
+ import { existsSync as existsSync10, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync11, rmSync as rmSync3 } from "fs";
9945
+ import { homedir as homedir13 } from "os";
9946
+ import { join as join14 } from "path";
9855
9947
  async function loadOrCreateIdentity() {
9856
9948
  const key = await loadKey();
9857
- if (existsSync9(IDENTITY_FILE)) {
9858
- const blob2 = JSON.parse(readFileSync13(IDENTITY_FILE, "utf8"));
9949
+ if (existsSync10(IDENTITY_FILE)) {
9950
+ const blob2 = JSON.parse(readFileSync14(IDENTITY_FILE, "utf8"));
9859
9951
  return JSON.parse(decrypt(blob2, key));
9860
9952
  }
9861
9953
  const keypair = generateIdentityKeypair();
@@ -9870,19 +9962,19 @@ var init_chat_keystore = __esm({
9870
9962
  "use strict";
9871
9963
  init_src();
9872
9964
  init_github_auth();
9873
- TERMINALHIRE_DIR10 = join13(homedir12(), ".terminalhire");
9874
- IDENTITY_FILE = join13(TERMINALHIRE_DIR10, "chat-identity.enc");
9965
+ TERMINALHIRE_DIR10 = join14(homedir13(), ".terminalhire");
9966
+ IDENTITY_FILE = join14(TERMINALHIRE_DIR10, "chat-identity.enc");
9875
9967
  }
9876
9968
  });
9877
9969
 
9878
9970
  // src/chat-client.ts
9879
- import { existsSync as existsSync10, mkdirSync as mkdirSync12, readFileSync as readFileSync14, writeFileSync as writeFileSync12 } from "fs";
9880
- import { homedir as homedir13 } from "os";
9881
- import { join as join14 } from "path";
9971
+ import { existsSync as existsSync11, mkdirSync as mkdirSync12, readFileSync as readFileSync15, writeFileSync as writeFileSync12 } from "fs";
9972
+ import { homedir as homedir14 } from "os";
9973
+ import { join as join15 } from "path";
9882
9974
  function defaultReadPeerPins() {
9883
9975
  try {
9884
- if (!existsSync10(PEERS_FILE)) return {};
9885
- const parsed = JSON.parse(readFileSync14(PEERS_FILE, "utf8"));
9976
+ if (!existsSync11(PEERS_FILE)) return {};
9977
+ const parsed = JSON.parse(readFileSync15(PEERS_FILE, "utf8"));
9886
9978
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
9887
9979
  const out = {};
9888
9980
  for (const [login, key] of Object.entries(parsed)) {
@@ -10058,8 +10150,8 @@ var init_chat_client = __esm({
10058
10150
  init_web_session();
10059
10151
  CHAT_BASE = process.env["TERMINALHIRE_API_URL"] || "https://www.terminalhire.com";
10060
10152
  GH_SESSION_COOKIE3 = "__jpi_gh_session";
10061
- TERMINALHIRE_DIR11 = join14(homedir13(), ".terminalhire");
10062
- PEERS_FILE = join14(TERMINALHIRE_DIR11, "chat-peers.json");
10153
+ TERMINALHIRE_DIR11 = join15(homedir14(), ".terminalhire");
10154
+ PEERS_FILE = join15(TERMINALHIRE_DIR11, "chat-peers.json");
10063
10155
  REQUEST_TIMEOUT_MS = 1e4;
10064
10156
  ChatNotLinkedError = class extends Error {
10065
10157
  constructor() {
@@ -10113,13 +10205,13 @@ __export(jpi_chat_read_exports, {
10113
10205
  runSend: () => runSend,
10114
10206
  writeReadCursor: () => writeReadCursor
10115
10207
  });
10116
- import { existsSync as existsSync11, mkdirSync as mkdirSync13, readFileSync as readFileSync15, writeFileSync as writeFileSync13 } from "fs";
10117
- import { homedir as homedir14 } from "os";
10118
- import { join as join15 } from "path";
10208
+ import { existsSync as existsSync12, mkdirSync as mkdirSync13, readFileSync as readFileSync16, writeFileSync as writeFileSync13 } from "fs";
10209
+ import { homedir as homedir15 } from "os";
10210
+ import { join as join16 } from "path";
10119
10211
  function readReadCursors() {
10120
10212
  try {
10121
- if (!existsSync11(READS_FILE)) return {};
10122
- const parsed = JSON.parse(readFileSync15(READS_FILE, "utf8"));
10213
+ if (!existsSync12(READS_FILE)) return {};
10214
+ const parsed = JSON.parse(readFileSync16(READS_FILE, "utf8"));
10123
10215
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
10124
10216
  const out = {};
10125
10217
  for (const [login, iso] of Object.entries(parsed)) {
@@ -10415,8 +10507,8 @@ var init_jpi_chat_read = __esm({
10415
10507
  init_chat_client();
10416
10508
  init_jpi_chat();
10417
10509
  CHAT_BASE2 = process.env["TERMINALHIRE_API_URL"] || "https://www.terminalhire.com";
10418
- TERMINALHIRE_DIR12 = join15(homedir14(), ".terminalhire");
10419
- READS_FILE = join15(TERMINALHIRE_DIR12, "chat-reads.json");
10510
+ TERMINALHIRE_DIR12 = join16(homedir15(), ".terminalhire");
10511
+ READS_FILE = join16(TERMINALHIRE_DIR12, "chat-reads.json");
10420
10512
  }
10421
10513
  });
10422
10514
 
@@ -11064,6 +11156,44 @@ var init_jpi_chat = __esm({
11064
11156
  }
11065
11157
  });
11066
11158
 
11159
+ // bin/jpi-connect.js
11160
+ var jpi_connect_exports = {};
11161
+ __export(jpi_connect_exports, {
11162
+ run: () => run10
11163
+ });
11164
+ async function run10() {
11165
+ const args5 = process.argv.slice(2).filter((a) => a !== "connect");
11166
+ if (args5.includes("--mute")) {
11167
+ writeConfig({ inboundNudgeMuted: true });
11168
+ console.log("\n Muted: your spinner will no longer surface incoming connection requests.");
11169
+ console.log(" Pending intros are still there \u2014 see them any time with `terminalhire intro --list`.");
11170
+ console.log(" Re-enable with `terminalhire connect --unmute`.\n");
11171
+ return;
11172
+ }
11173
+ if (args5.includes("--unmute")) {
11174
+ writeConfig({ inboundNudgeMuted: false });
11175
+ console.log("\n Unmuted: incoming connection requests will surface in your spinner again.");
11176
+ console.log(" (Requires a linked terminal \u2014 run `terminalhire link` if you have not.)\n");
11177
+ return;
11178
+ }
11179
+ const muted = isInboundNudgeMuted();
11180
+ console.log("\n terminalhire connect \u2014 find and reach other developers, locally matched:");
11181
+ console.log(" terminalhire devs Rank opted-in builders & projects for you");
11182
+ console.log(' terminalhire project "<title>: <skills>" Declare a project to staff');
11183
+ console.log(" terminalhire intro <github-login> Request a consented intro (double opt-in)");
11184
+ console.log(" terminalhire intro --list See intros you have sent + received");
11185
+ console.log(" terminalhire chat Message your accepted connections");
11186
+ console.log("");
11187
+ console.log(` Ambient inbound nudge: ${muted ? "muted" : "on"} (toggle: terminalhire connect --${muted ? "unmute" : "mute"})`);
11188
+ console.log("");
11189
+ }
11190
+ var init_jpi_connect = __esm({
11191
+ "bin/jpi-connect.js"() {
11192
+ "use strict";
11193
+ init_config();
11194
+ }
11195
+ });
11196
+
11067
11197
  // src/link.ts
11068
11198
  var link_exports = {};
11069
11199
  __export(link_exports, {
@@ -11176,6 +11306,8 @@ async function runLink(overrides) {
11176
11306
  deps.persistToken(outcome.token);
11177
11307
  deps.log("\n This terminal is now linked to your terminalhire account.");
11178
11308
  deps.log(" Try `terminalhire intro <login>`, `terminalhire chat`, or `terminalhire trajectory --push`.");
11309
+ deps.log(" Your spinner will quietly surface incoming connection requests.");
11310
+ deps.log(" Turn that off any time with `terminalhire connect --mute`.");
11179
11311
  deps.log(" Unlink any time with `terminalhire link --logout`.\n");
11180
11312
  deps.exit(0);
11181
11313
  }
@@ -11241,9 +11373,9 @@ var init_link = __esm({
11241
11373
  // bin/jpi-link.js
11242
11374
  var jpi_link_exports = {};
11243
11375
  __export(jpi_link_exports, {
11244
- run: () => run10
11376
+ run: () => run11
11245
11377
  });
11246
- async function run10() {
11378
+ async function run11() {
11247
11379
  try {
11248
11380
  const args5 = process.argv.slice(2);
11249
11381
  if (args5.includes("--logout")) {
@@ -11267,7 +11399,7 @@ var init_jpi_link = __esm({
11267
11399
  // bin/jpi-profile.js
11268
11400
  var jpi_profile_exports = {};
11269
11401
  __export(jpi_profile_exports, {
11270
- run: () => run11
11402
+ run: () => run12
11271
11403
  });
11272
11404
  import { createInterface as createInterface8 } from "readline";
11273
11405
  function prompt4(question) {
@@ -11279,7 +11411,7 @@ function prompt4(question) {
11279
11411
  });
11280
11412
  });
11281
11413
  }
11282
- async function run11() {
11414
+ async function run12() {
11283
11415
  const { readProfile: readProfile2, writeProfile: writeProfile2, deleteProfile: deleteProfile2 } = await Promise.resolve().then(() => (init_profile(), profile_exports));
11284
11416
  const args5 = process.argv.slice(2);
11285
11417
  if (args5.includes("--show")) {
@@ -11352,9 +11484,9 @@ var signal_exports = {};
11352
11484
  __export(signal_exports, {
11353
11485
  extractFingerprint: () => extractFingerprint
11354
11486
  });
11355
- import { readFileSync as readFileSync16, readdirSync as readdirSync2 } from "fs";
11487
+ import { readFileSync as readFileSync17, readdirSync as readdirSync2 } from "fs";
11356
11488
  import { execFileSync } from "child_process";
11357
- import { join as join16 } from "path";
11489
+ import { join as join17 } from "path";
11358
11490
  function safeGit(args5, cwd) {
11359
11491
  try {
11360
11492
  return execFileSync("git", ["-C", cwd, ...args5], {
@@ -11382,20 +11514,20 @@ function isEmployerContext(cwd) {
11382
11514
  }
11383
11515
  function readJsonSafe(path) {
11384
11516
  try {
11385
- return JSON.parse(readFileSync16(path, "utf8"));
11517
+ return JSON.parse(readFileSync17(path, "utf8"));
11386
11518
  } catch {
11387
11519
  return null;
11388
11520
  }
11389
11521
  }
11390
11522
  function readFileSafe(path) {
11391
11523
  try {
11392
- return readFileSync16(path, "utf8");
11524
+ return readFileSync17(path, "utf8");
11393
11525
  } catch {
11394
11526
  return "";
11395
11527
  }
11396
11528
  }
11397
11529
  function tokensFromPackageJson(cwd) {
11398
- const pkg = readJsonSafe(join16(cwd, "package.json"));
11530
+ const pkg = readJsonSafe(join17(cwd, "package.json"));
11399
11531
  if (!pkg || typeof pkg !== "object") return [];
11400
11532
  const p = pkg;
11401
11533
  const deps = {
@@ -11409,9 +11541,9 @@ function workspaceMemberDirs(cwd) {
11409
11541
  const dirs = [cwd];
11410
11542
  for (const group of ["apps", "packages"]) {
11411
11543
  try {
11412
- const groupDir = join16(cwd, group);
11544
+ const groupDir = join17(cwd, group);
11413
11545
  for (const e of readdirSync2(groupDir, { withFileTypes: true })) {
11414
- if (e.isDirectory() && !e.isSymbolicLink()) dirs.push(join16(groupDir, e.name));
11546
+ if (e.isDirectory() && !e.isSymbolicLink()) dirs.push(join17(groupDir, e.name));
11415
11547
  }
11416
11548
  } catch {
11417
11549
  }
@@ -11419,18 +11551,18 @@ function workspaceMemberDirs(cwd) {
11419
11551
  return dirs;
11420
11552
  }
11421
11553
  function tokensFromRequirementsTxt(cwd) {
11422
- const content = readFileSafe(join16(cwd, "requirements.txt"));
11554
+ const content = readFileSafe(join17(cwd, "requirements.txt"));
11423
11555
  if (!content) return [];
11424
11556
  return content.split("\n").map((l) => l.trim().split(/[>=<!\[;]/)[0].trim().toLowerCase()).filter(Boolean);
11425
11557
  }
11426
11558
  function tokensFromGoMod(cwd) {
11427
- const content = readFileSafe(join16(cwd, "go.mod"));
11559
+ const content = readFileSafe(join17(cwd, "go.mod"));
11428
11560
  if (!content) return [];
11429
11561
  const requires = Array.from(content.matchAll(/^\s+([^\s]+)\s+v/gm)).map((m) => m[1].split("/").pop() ?? "").filter(Boolean);
11430
11562
  return ["go", ...requires];
11431
11563
  }
11432
11564
  function tokensFromCargoToml(cwd) {
11433
- const content = readFileSafe(join16(cwd, "Cargo.toml"));
11565
+ const content = readFileSafe(join17(cwd, "Cargo.toml"));
11434
11566
  if (!content) return [];
11435
11567
  const deps = [];
11436
11568
  let inDeps = false;
@@ -11451,7 +11583,7 @@ function tokensFromFileExtensions(cwd) {
11451
11583
  const tokens = [];
11452
11584
  const scanDirs = [cwd];
11453
11585
  try {
11454
- const srcDir = join16(cwd, "src");
11586
+ const srcDir = join17(cwd, "src");
11455
11587
  readdirSync2(srcDir);
11456
11588
  scanDirs.push(srcDir);
11457
11589
  } catch {
@@ -11612,9 +11744,9 @@ var init_signal = __esm({
11612
11744
  // bin/jpi-learn.js
11613
11745
  var jpi_learn_exports = {};
11614
11746
  __export(jpi_learn_exports, {
11615
- run: () => run12
11747
+ run: () => run13
11616
11748
  });
11617
- async function run12() {
11749
+ async function run13() {
11618
11750
  try {
11619
11751
  const args5 = process.argv.slice(2);
11620
11752
  const cwdIdx = args5.indexOf("--cwd");
@@ -11641,7 +11773,7 @@ var init_jpi_learn = __esm({
11641
11773
  "use strict";
11642
11774
  isMain = process.argv[1]?.endsWith("jpi-learn.js") || process.argv[1]?.endsWith("jpi-learn");
11643
11775
  if (isMain) {
11644
- run12();
11776
+ run13();
11645
11777
  }
11646
11778
  }
11647
11779
  });
@@ -11649,17 +11781,17 @@ var init_jpi_learn = __esm({
11649
11781
  // bin/jpi-config.js
11650
11782
  var jpi_config_exports = {};
11651
11783
  __export(jpi_config_exports, {
11652
- run: () => run13
11784
+ run: () => run14
11653
11785
  });
11654
- import { join as join17 } from "path";
11655
- import { homedir as homedir15 } from "os";
11786
+ import { join as join18 } from "path";
11787
+ import { homedir as homedir16 } from "os";
11656
11788
  function parseNudgeMode(raw) {
11657
11789
  if (raw === "session" || raw === "always") return raw;
11658
11790
  const m = /^every:(\d+)$/.exec(raw);
11659
11791
  if (m && parseInt(m[1], 10) >= 1) return raw;
11660
11792
  return null;
11661
11793
  }
11662
- async function run13() {
11794
+ async function run14() {
11663
11795
  const args5 = process.argv.slice(2);
11664
11796
  const filtered = args5[0] === "config" ? args5.slice(1) : args5;
11665
11797
  if (filtered.includes("--show") || filtered.length === 0) {
@@ -11725,8 +11857,8 @@ var init_jpi_config = __esm({
11725
11857
  "bin/jpi-config.js"() {
11726
11858
  "use strict";
11727
11859
  init_config();
11728
- TERMINALHIRE_DIR13 = join17(homedir15(), ".terminalhire");
11729
- CONFIG_FILE2 = join17(TERMINALHIRE_DIR13, "config.json");
11860
+ TERMINALHIRE_DIR13 = join18(homedir16(), ".terminalhire");
11861
+ CONFIG_FILE2 = join18(TERMINALHIRE_DIR13, "config.json");
11730
11862
  }
11731
11863
  });
11732
11864
 
@@ -11737,6 +11869,7 @@ __export(spinner_exports, {
11737
11869
  applySpinnerTips: () => applySpinnerTips,
11738
11870
  applySpinnerVerbs: () => applySpinnerVerbs,
11739
11871
  buildContextVerbs: () => buildContextVerbs,
11872
+ buildIncomingIntroLine: () => buildIncomingIntroLine,
11740
11873
  buildPeerLine: () => buildPeerLine,
11741
11874
  buildSpinnerPool: () => buildSpinnerPool,
11742
11875
  buildTips: () => buildTips,
@@ -11749,17 +11882,17 @@ __export(spinner_exports, {
11749
11882
  readSpinnerConfig: () => readSpinnerConfig
11750
11883
  });
11751
11884
  import {
11752
- readFileSync as readFileSync17,
11885
+ readFileSync as readFileSync18,
11753
11886
  writeFileSync as writeFileSync14,
11754
- existsSync as existsSync12,
11887
+ existsSync as existsSync13,
11755
11888
  mkdirSync as mkdirSync14,
11756
11889
  renameSync as renameSync2
11757
11890
  } from "fs";
11758
- import { join as join18, dirname } from "path";
11759
- import { homedir as homedir16 } from "os";
11891
+ import { join as join19, dirname } from "path";
11892
+ import { homedir as homedir17 } from "os";
11760
11893
  function readJson(path, fallback) {
11761
11894
  try {
11762
- return existsSync12(path) ? JSON.parse(readFileSync17(path, "utf8")) : fallback;
11895
+ return existsSync13(path) ? JSON.parse(readFileSync18(path, "utf8")) : fallback;
11763
11896
  } catch {
11764
11897
  return fallback;
11765
11898
  }
@@ -11859,16 +11992,25 @@ function buildPeerLine(topPeers) {
11859
11992
  if (n < 1) return null;
11860
11993
  return n === 1 ? `\u25C6 1 builder matches what you're building \xB7 terminalhire devs` : `\u25C6 ${n} builders match what you're building \xB7 terminalhire devs`;
11861
11994
  }
11995
+ function buildIncomingIntroLine(incomingPending) {
11996
+ const n = incomingPending && typeof incomingPending.count === "number" ? incomingPending.count : 0;
11997
+ if (n < 1) return null;
11998
+ return n === 1 ? `\u2198 someone wants to connect \xB7 terminalhire intro --list` : `\u2198 ${n} people want to connect \xB7 terminalhire intro --list`;
11999
+ }
11862
12000
  function buildSpinnerPool(topMatches, max = 6, opts = {}) {
11863
- const { sessionTags, frequency = "always", topPeers } = opts;
12001
+ const { sessionTags, frequency = "always", topPeers, incomingPending } = opts;
12002
+ const introLine = buildIncomingIntroLine(incomingPending);
11864
12003
  const ranked = rankBySessionTags(topMatches, sessionTags);
11865
12004
  if (!Array.isArray(ranked) || ranked.length === 0) {
12005
+ if (introLine) return [introLine];
11866
12006
  const peerLine = buildPeerLine(topPeers);
11867
12007
  return peerLine ? [peerLine] : [];
11868
12008
  }
11869
12009
  const headers = buildContextVerbs(ranked, sessionTags);
11870
12010
  const cap = Math.max(1, verbCountForFrequency(frequency, headers.length));
11871
- return [...headers.slice(0, cap), ctaVerb()];
12011
+ const pool = [...headers.slice(0, cap), ctaVerb()];
12012
+ if (introLine) pool.push(introLine);
12013
+ return pool;
11872
12014
  }
11873
12015
  function readState() {
11874
12016
  return readJson(SPINNER_STATE_FILE, { verbs: [], mode: "replace" });
@@ -12027,10 +12169,10 @@ var TH_DIR, CLAUDE_SETTINGS, CONFIG_FILE3, SPINNER_STATE_FILE, SPINNER_DEFAULTS,
12027
12169
  var init_spinner = __esm({
12028
12170
  "bin/spinner.js"() {
12029
12171
  "use strict";
12030
- TH_DIR = process.env["TERMINALHIRE_DIR"] || join18(homedir16(), ".terminalhire");
12031
- CLAUDE_SETTINGS = process.env["TERMINALHIRE_CLAUDE_SETTINGS"] || join18(homedir16(), ".claude", "settings.json");
12032
- CONFIG_FILE3 = join18(TH_DIR, "config.json");
12033
- SPINNER_STATE_FILE = join18(TH_DIR, "spinner-state.json");
12172
+ TH_DIR = process.env["TERMINALHIRE_DIR"] || join19(homedir17(), ".terminalhire");
12173
+ CLAUDE_SETTINGS = process.env["TERMINALHIRE_CLAUDE_SETTINGS"] || join19(homedir17(), ".claude", "settings.json");
12174
+ CONFIG_FILE3 = join19(TH_DIR, "config.json");
12175
+ SPINNER_STATE_FILE = join19(TH_DIR, "spinner-state.json");
12034
12176
  SPINNER_DEFAULTS = { enabled: false, mode: "append", max: 6, frequency: "sometimes" };
12035
12177
  VERB_INTROS = ["Matched:", "You\u2019d fit:", "Worth a look:", "On your radar:", "Fits your stack:"];
12036
12178
  }
@@ -12039,21 +12181,21 @@ var init_spinner = __esm({
12039
12181
  // bin/jpi-spinner.js
12040
12182
  var jpi_spinner_exports = {};
12041
12183
  __export(jpi_spinner_exports, {
12042
- run: () => run14
12184
+ run: () => run15
12043
12185
  });
12044
12186
  import {
12045
- readFileSync as readFileSync18,
12187
+ readFileSync as readFileSync19,
12046
12188
  writeFileSync as writeFileSync15,
12047
12189
  copyFileSync,
12048
- existsSync as existsSync13,
12190
+ existsSync as existsSync14,
12049
12191
  mkdirSync as mkdirSync15
12050
12192
  } from "fs";
12051
- import { join as join19 } from "path";
12052
- import { homedir as homedir17 } from "os";
12193
+ import { join as join20 } from "path";
12194
+ import { homedir as homedir18 } from "os";
12053
12195
  import { createInterface as createInterface9 } from "readline";
12054
12196
  function readConfig2() {
12055
12197
  try {
12056
- return existsSync13(CONFIG_FILE4) ? JSON.parse(readFileSync18(CONFIG_FILE4, "utf8")) : {};
12198
+ return existsSync14(CONFIG_FILE4) ? JSON.parse(readFileSync19(CONFIG_FILE4, "utf8")) : {};
12057
12199
  } catch {
12058
12200
  return {};
12059
12201
  }
@@ -12064,7 +12206,7 @@ function writeConfig2(patch) {
12064
12206
  writeFileSync15(CONFIG_FILE4, JSON.stringify(merged, null, 2) + "\n", "utf8");
12065
12207
  }
12066
12208
  function backupSettings() {
12067
- if (!existsSync13(SETTINGS_PATH)) return null;
12209
+ if (!existsSync14(SETTINGS_PATH)) return null;
12068
12210
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
12069
12211
  const backupPath = `${SETTINGS_PATH}.terminalhire-backup-${ts}`;
12070
12212
  copyFileSync(SETTINGS_PATH, backupPath);
@@ -12081,13 +12223,13 @@ function ask(question) {
12081
12223
  }
12082
12224
  function readTopMatches() {
12083
12225
  try {
12084
- const c = JSON.parse(readFileSync18(CACHE_FILE, "utf8"));
12226
+ const c = JSON.parse(readFileSync19(CACHE_FILE, "utf8"));
12085
12227
  return Array.isArray(c.topMatches) ? c.topMatches : [];
12086
12228
  } catch {
12087
12229
  return [];
12088
12230
  }
12089
12231
  }
12090
- async function run14() {
12232
+ async function run15() {
12091
12233
  const args5 = process.argv.slice(2).filter((a) => a !== "spinner");
12092
12234
  const has = (f) => args5.includes(f);
12093
12235
  const val = (f) => {
@@ -12224,21 +12366,21 @@ var init_jpi_spinner = __esm({
12224
12366
  "bin/jpi-spinner.js"() {
12225
12367
  "use strict";
12226
12368
  init_spinner();
12227
- TH_DIR2 = process.env["TERMINALHIRE_DIR"] || join19(homedir17(), ".terminalhire");
12228
- CONFIG_FILE4 = join19(TH_DIR2, "config.json");
12229
- SETTINGS_PATH = process.env["TERMINALHIRE_CLAUDE_SETTINGS"] || join19(homedir17(), ".claude", "settings.json");
12230
- CACHE_FILE = join19(TH_DIR2, "index-cache.json");
12369
+ TH_DIR2 = process.env["TERMINALHIRE_DIR"] || join20(homedir18(), ".terminalhire");
12370
+ CONFIG_FILE4 = join20(TH_DIR2, "config.json");
12371
+ SETTINGS_PATH = process.env["TERMINALHIRE_CLAUDE_SETTINGS"] || join20(homedir18(), ".claude", "settings.json");
12372
+ CACHE_FILE = join20(TH_DIR2, "index-cache.json");
12231
12373
  }
12232
12374
  });
12233
12375
 
12234
12376
  // bin/jpi-sync.js
12235
12377
  var jpi_sync_exports = {};
12236
12378
  __export(jpi_sync_exports, {
12237
- run: () => run15
12379
+ run: () => run16
12238
12380
  });
12239
- import { readFileSync as readFileSync19, writeFileSync as writeFileSync16, mkdirSync as mkdirSync16, existsSync as existsSync14, rmSync as rmSync4 } from "fs";
12240
- import { join as join20 } from "path";
12241
- import { homedir as homedir18, hostname as osHostname } from "os";
12381
+ import { readFileSync as readFileSync20, writeFileSync as writeFileSync16, mkdirSync as mkdirSync16, existsSync as existsSync15, rmSync as rmSync4 } from "fs";
12382
+ import { join as join21 } from "path";
12383
+ import { homedir as homedir19, hostname as osHostname } from "os";
12242
12384
  import { createInterface as createInterface10 } from "readline";
12243
12385
  function ask2(question) {
12244
12386
  const rl = createInterface10({ input: process.stdin, output: process.stdout });
@@ -12251,7 +12393,7 @@ function ask2(question) {
12251
12393
  }
12252
12394
  function readMarker() {
12253
12395
  try {
12254
- return existsSync14(TIER1_MARKER) ? JSON.parse(readFileSync19(TIER1_MARKER, "utf8")) : null;
12396
+ return existsSync15(TIER1_MARKER) ? JSON.parse(readFileSync20(TIER1_MARKER, "utf8")) : null;
12255
12397
  } catch {
12256
12398
  return null;
12257
12399
  }
@@ -12546,7 +12688,7 @@ async function runDelete() {
12546
12688
  clearMarker();
12547
12689
  console.log("\n Synced profile deleted and local marker cleared.\n");
12548
12690
  }
12549
- async function run15() {
12691
+ async function run16() {
12550
12692
  const args5 = process.argv.slice(2).filter((a) => a !== "sync");
12551
12693
  const has = (f) => args5.includes(f);
12552
12694
  if (has("--push") || has("--enable")) {
@@ -12577,8 +12719,8 @@ var init_jpi_sync = __esm({
12577
12719
  "bin/jpi-sync.js"() {
12578
12720
  "use strict";
12579
12721
  init_open_url();
12580
- TH_DIR3 = process.env["TERMINALHIRE_DIR"] || join20(homedir18(), ".terminalhire");
12581
- TIER1_MARKER = join20(TH_DIR3, "tier1.json");
12722
+ TH_DIR3 = process.env["TERMINALHIRE_DIR"] || join21(homedir19(), ".terminalhire");
12723
+ TIER1_MARKER = join21(TH_DIR3, "tier1.json");
12582
12724
  API_URL5 = process.env["TERMINALHIRE_API_URL"] || process.env["JPI_API_URL"] || "https://terminalhire.com";
12583
12725
  SYNC_BASE = "https://www.terminalhire.com";
12584
12726
  POLL_INTERVAL_MS = 2e3;
@@ -12590,14 +12732,14 @@ var init_jpi_sync = __esm({
12590
12732
  // bin/jpi-init.js
12591
12733
  var jpi_init_exports = {};
12592
12734
  __export(jpi_init_exports, {
12593
- run: () => run16
12735
+ run: () => run17
12594
12736
  });
12595
- import { existsSync as existsSync15 } from "fs";
12596
- import { join as join21, resolve } from "path";
12597
- import { fileURLToPath as fileURLToPath3 } from "url";
12737
+ import { existsSync as existsSync16 } from "fs";
12738
+ import { join as join22, resolve } from "path";
12739
+ import { fileURLToPath as fileURLToPath4 } from "url";
12598
12740
  import { createInterface as createInterface11 } from "readline";
12599
12741
  import { spawnSync, spawn as spawn2 } from "child_process";
12600
- import { homedir as homedir19 } from "os";
12742
+ import { homedir as homedir20 } from "os";
12601
12743
  function ask3(question) {
12602
12744
  const rl = createInterface11({ input: process.stdin, output: process.stdout });
12603
12745
  return new Promise((resolve2) => {
@@ -12608,18 +12750,18 @@ function ask3(question) {
12608
12750
  });
12609
12751
  }
12610
12752
  function resolveScript(name) {
12611
- const distPath = resolve(join21(__dirname2, "..", "..", "dist", "bin", `${name}.js`));
12612
- const legacyPath = resolve(join21(__dirname2, `${name}.js`));
12613
- return existsSync15(distPath) ? distPath : legacyPath;
12753
+ const distPath = resolve(join22(__dirname3, "..", "..", "dist", "bin", `${name}.js`));
12754
+ const legacyPath = resolve(join22(__dirname3, `${name}.js`));
12755
+ return existsSync16(distPath) ? distPath : legacyPath;
12614
12756
  }
12615
12757
  function resolveInstallJs() {
12616
- const fromDist = resolve(join21(__dirname2, "..", "..", "install.js"));
12617
- const fromBin = resolve(join21(__dirname2, "..", "install.js"));
12618
- if (existsSync15(fromDist)) return fromDist;
12619
- if (existsSync15(fromBin)) return fromBin;
12758
+ const fromDist = resolve(join22(__dirname3, "..", "..", "install.js"));
12759
+ const fromBin = resolve(join22(__dirname3, "..", "install.js"));
12760
+ if (existsSync16(fromDist)) return fromDist;
12761
+ if (existsSync16(fromBin)) return fromBin;
12620
12762
  return fromBin;
12621
12763
  }
12622
- async function run16() {
12764
+ async function run17() {
12623
12765
  console.log("");
12624
12766
  console.log("\u250C\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\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\u2510");
12625
12767
  console.log("\u2502 terminalhire init \u2014 one-command onboarding \u2502");
@@ -12711,24 +12853,24 @@ async function run16() {
12711
12853
  console.log("\u2514\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\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\u2518");
12712
12854
  console.log("");
12713
12855
  }
12714
- var __dirname2;
12856
+ var __dirname3;
12715
12857
  var init_jpi_init = __esm({
12716
12858
  "bin/jpi-init.js"() {
12717
12859
  "use strict";
12718
- __dirname2 = fileURLToPath3(new URL(".", import.meta.url));
12860
+ __dirname3 = fileURLToPath4(new URL(".", import.meta.url));
12719
12861
  }
12720
12862
  });
12721
12863
 
12722
12864
  // bin/jpi-refresh.js
12723
12865
  var jpi_refresh_exports = {};
12724
12866
  __export(jpi_refresh_exports, {
12725
- run: () => run17
12867
+ run: () => run18
12726
12868
  });
12727
- import { readFileSync as readFileSync20, writeFileSync as writeFileSync17, existsSync as existsSync16, mkdirSync as mkdirSync17 } from "fs";
12728
- import { join as join22 } from "path";
12729
- import { homedir as homedir20 } from "os";
12730
- import { fileURLToPath as fileURLToPath4 } from "url";
12731
- async function run17() {
12869
+ import { readFileSync as readFileSync21, writeFileSync as writeFileSync17, existsSync as existsSync17, mkdirSync as mkdirSync17 } from "fs";
12870
+ import { join as join23 } from "path";
12871
+ import { homedir as homedir21 } from "os";
12872
+ import { fileURLToPath as fileURLToPath5 } from "url";
12873
+ async function run18() {
12732
12874
  try {
12733
12875
  let index;
12734
12876
  try {
@@ -12818,15 +12960,32 @@ async function run17() {
12818
12960
  }
12819
12961
  } catch {
12820
12962
  }
12963
+ let incomingPending = { count: 0 };
12964
+ const sessionCookie = readWebSessionFile();
12965
+ if (sessionCookie && !isInboundNudgeMuted()) try {
12966
+ const res = await fetch(`${API_URL6}/api/intro/list`, {
12967
+ method: "GET",
12968
+ headers: { Cookie: `${GH_SESSION_COOKIE6}=${sessionCookie}` },
12969
+ signal: AbortSignal.timeout(1e4)
12970
+ });
12971
+ if (res.ok) {
12972
+ const body = await res.json();
12973
+ const intros = Array.isArray(body?.intros) ? body.intros : [];
12974
+ const incoming = intros.filter((it) => it && it.role === "incoming" && it.status === "pending");
12975
+ incomingPending = { count: incoming.length };
12976
+ }
12977
+ } catch {
12978
+ }
12821
12979
  mkdirSync17(TERMINALHIRE_DIR14, { recursive: true });
12822
12980
  const cacheEntry = {
12823
12981
  ts: Date.now(),
12824
12982
  index,
12825
12983
  matchCount,
12826
12984
  topMatches,
12827
- topPeers
12985
+ topPeers,
12986
+ incomingPending
12828
12987
  };
12829
- writeFileSync17(INDEX_CACHE_FILE4, JSON.stringify(cacheEntry), "utf8");
12988
+ writeFileSync17(INDEX_CACHE_FILE5, JSON.stringify(cacheEntry), "utf8");
12830
12989
  try {
12831
12990
  const {
12832
12991
  readSpinnerConfig: readSpinnerConfig2,
@@ -12850,7 +13009,7 @@ async function run17() {
12850
13009
  } catch {
12851
13010
  }
12852
13011
  const ranked = rankBySessionTags2(topMatches, sessionTags);
12853
- const verbs = buildSpinnerPool2(ranked, sc.max, { sessionTags, frequency: sc.frequency, topPeers });
13012
+ const verbs = buildSpinnerPool2(ranked, sc.max, { sessionTags, frequency: sc.frequency, topPeers, incomingPending });
12854
13013
  if (verbs.length > 0) applySpinnerVerbs2(verbs, sc.mode);
12855
13014
  else clearSpinnerVerbs2();
12856
13015
  const tips = buildTips2(ranked, API_URL6, 8);
@@ -12862,6 +13021,13 @@ async function run17() {
12862
13021
  }
12863
13022
  } catch {
12864
13023
  }
13024
+ try {
13025
+ const { readLocalVersion: readLocalVersion2, buildStaleNudge: buildStaleNudge2 } = await Promise.resolve().then(() => (init_version_nudge(), version_nudge_exports));
13026
+ const nudge = buildStaleNudge2(readLocalVersion2(), index?.cliVersion);
13027
+ if (nudge) process.stderr.write(`${nudge}
13028
+ `);
13029
+ } catch {
13030
+ }
12865
13031
  process.exit(0);
12866
13032
  } catch (err) {
12867
13033
  const msg = err instanceof Error ? err.message : String(err);
@@ -12870,15 +13036,17 @@ async function run17() {
12870
13036
  process.exit(1);
12871
13037
  }
12872
13038
  }
12873
- var __dirname3, TERMINALHIRE_DIR14, INDEX_CACHE_FILE4, API_URL6;
13039
+ var GH_SESSION_COOKIE6, __dirname4, TERMINALHIRE_DIR14, INDEX_CACHE_FILE5, API_URL6;
12874
13040
  var init_jpi_refresh = __esm({
12875
13041
  "bin/jpi-refresh.js"() {
12876
13042
  "use strict";
12877
13043
  init_directory2();
12878
13044
  init_config();
12879
- __dirname3 = fileURLToPath4(new URL(".", import.meta.url));
12880
- TERMINALHIRE_DIR14 = join22(homedir20(), ".terminalhire");
12881
- INDEX_CACHE_FILE4 = join22(TERMINALHIRE_DIR14, "index-cache.json");
13045
+ init_web_session();
13046
+ GH_SESSION_COOKIE6 = "__jpi_gh_session";
13047
+ __dirname4 = fileURLToPath5(new URL(".", import.meta.url));
13048
+ TERMINALHIRE_DIR14 = join23(homedir21(), ".terminalhire");
13049
+ INDEX_CACHE_FILE5 = join23(TERMINALHIRE_DIR14, "index-cache.json");
12882
13050
  API_URL6 = process.env["TERMINALHIRE_API_URL"] ?? process.env["JPI_API_URL"] ?? "https://terminalhire.com";
12883
13051
  }
12884
13052
  });
@@ -12886,16 +13054,16 @@ var init_jpi_refresh = __esm({
12886
13054
  // bin/jpi-save.js
12887
13055
  var jpi_save_exports = {};
12888
13056
  __export(jpi_save_exports, {
12889
- run: () => run18
13057
+ run: () => run19
12890
13058
  });
12891
- import { readFileSync as readFileSync21, existsSync as existsSync17 } from "fs";
12892
- import { join as join23 } from "path";
12893
- import { homedir as homedir21 } from "os";
12894
- import { fileURLToPath as fileURLToPath5 } from "url";
13059
+ import { readFileSync as readFileSync22, existsSync as existsSync18 } from "fs";
13060
+ import { join as join24 } from "path";
13061
+ import { homedir as homedir22 } from "os";
13062
+ import { fileURLToPath as fileURLToPath6 } from "url";
12895
13063
  function findJobInCache(jobId) {
12896
13064
  try {
12897
- if (!existsSync17(INDEX_CACHE_FILE5)) return null;
12898
- const raw = readFileSync21(INDEX_CACHE_FILE5, "utf8");
13065
+ if (!existsSync18(INDEX_CACHE_FILE6)) return null;
13066
+ const raw = readFileSync22(INDEX_CACHE_FILE6, "utf8");
12899
13067
  const entry = JSON.parse(raw);
12900
13068
  const jobs = entry?.index?.jobs ?? [];
12901
13069
  return jobs.find((j) => j.id === jobId) ?? null;
@@ -12964,7 +13132,7 @@ async function cmdUnsave(jobId) {
12964
13132
  process.exit(1);
12965
13133
  }
12966
13134
  }
12967
- async function run18() {
13135
+ async function run19() {
12968
13136
  const verb = process.argv[2];
12969
13137
  const jobId = process.argv[3];
12970
13138
  try {
@@ -12983,31 +13151,31 @@ async function run18() {
12983
13151
  process.exit(1);
12984
13152
  }
12985
13153
  }
12986
- var __dirname4, TERMINALHIRE_DIR15, INDEX_CACHE_FILE5;
13154
+ var __dirname5, TERMINALHIRE_DIR15, INDEX_CACHE_FILE6;
12987
13155
  var init_jpi_save = __esm({
12988
13156
  "bin/jpi-save.js"() {
12989
13157
  "use strict";
12990
- __dirname4 = fileURLToPath5(new URL(".", import.meta.url));
12991
- TERMINALHIRE_DIR15 = join23(homedir21(), ".terminalhire");
12992
- INDEX_CACHE_FILE5 = join23(TERMINALHIRE_DIR15, "index-cache.json");
13158
+ __dirname5 = fileURLToPath6(new URL(".", import.meta.url));
13159
+ TERMINALHIRE_DIR15 = join24(homedir22(), ".terminalhire");
13160
+ INDEX_CACHE_FILE6 = join24(TERMINALHIRE_DIR15, "index-cache.json");
12993
13161
  }
12994
13162
  });
12995
13163
 
12996
13164
  // bin/jpi-dispatch.js
12997
- import { fileURLToPath as fileURLToPath6 } from "url";
12998
- import { join as join24, dirname as dirname2 } from "path";
12999
- import { existsSync as existsSync18, readFileSync as readFileSync22 } from "fs";
13165
+ import { fileURLToPath as fileURLToPath7 } from "url";
13166
+ import { join as join25, dirname as dirname2 } from "path";
13167
+ import { existsSync as existsSync19, readFileSync as readFileSync23 } from "fs";
13000
13168
  import { createRequire } from "module";
13001
- var __dirname5 = fileURLToPath6(new URL(".", import.meta.url));
13169
+ var __dirname6 = fileURLToPath7(new URL(".", import.meta.url));
13002
13170
  function readPackageVersion() {
13003
13171
  try {
13004
13172
  const candidates = [
13005
- join24(__dirname5, "..", "..", "package.json"),
13006
- join24(__dirname5, "..", "package.json")
13173
+ join25(__dirname6, "..", "..", "package.json"),
13174
+ join25(__dirname6, "..", "package.json")
13007
13175
  ];
13008
13176
  for (const p of candidates) {
13009
- if (existsSync18(p)) {
13010
- const pkg = JSON.parse(readFileSync22(p, "utf8"));
13177
+ if (existsSync19(p)) {
13178
+ const pkg = JSON.parse(readFileSync23(p, "utf8"));
13011
13179
  if (pkg.version) return pkg.version;
13012
13180
  }
13013
13181
  }
@@ -13018,7 +13186,7 @@ function readPackageVersion() {
13018
13186
  var firstArg = process.argv[2];
13019
13187
  if (!firstArg && !process.stdin.isTTY) {
13020
13188
  const { default: childProcess } = await import("child_process");
13021
- const nudgeScript = join24(__dirname5, "jpi.js");
13189
+ const nudgeScript = join25(__dirname6, "jpi.js");
13022
13190
  const child = childProcess.spawnSync(process.execPath, [nudgeScript], {
13023
13191
  stdio: ["inherit", "inherit", "inherit"]
13024
13192
  });
@@ -13054,6 +13222,9 @@ if (!firstArg || firstArg === "help" || firstArg === "--help" || firstArg === "-
13054
13222
  console.log(" terminalhire chat <github-login> --read Read a thread inline (last 8; -n N / --all for depth)");
13055
13223
  console.log(' terminalhire chat <github-login> --send "\u2026" Send one line to a connection (E2E encrypted)');
13056
13224
  console.log(" terminalhire chat <github-login> Open the live E2E chat pane with an accepted connection");
13225
+ console.log(" terminalhire connect Connect family overview + inbound-nudge state");
13226
+ console.log(" terminalhire connect --mute Stop surfacing incoming connection requests in the spinner");
13227
+ console.log(" terminalhire connect --unmute Resume surfacing incoming connection requests");
13057
13228
  console.log(" terminalhire link Connect this terminal to your terminalhire account");
13058
13229
  console.log(" terminalhire link --logout Revoke + remove this terminal's linked session");
13059
13230
  console.log(" terminalhire profile --show Display your encrypted local profile");
@@ -13089,7 +13260,15 @@ if (!firstArg || firstArg === "help" || firstArg === "--help" || firstArg === "-
13089
13260
  process.exit(0);
13090
13261
  }
13091
13262
  if (firstArg === "--version" || firstArg === "-v") {
13092
- console.log(`terminalhire v${readPackageVersion()}`);
13263
+ const local = readPackageVersion();
13264
+ console.log(`terminalhire v${local}`);
13265
+ try {
13266
+ const { cachedStaleNudge: cachedStaleNudge2 } = await Promise.resolve().then(() => (init_version_nudge(), version_nudge_exports));
13267
+ const nudge = cachedStaleNudge2(local);
13268
+ if (nudge) process.stderr.write(`${nudge}
13269
+ `);
13270
+ } catch {
13271
+ }
13093
13272
  process.exit(0);
13094
13273
  }
13095
13274
  if (firstArg === "login" || firstArg === "logout") {
@@ -13145,6 +13324,11 @@ if (firstArg === "chat") {
13145
13324
  await mod2.run();
13146
13325
  process.exit(0);
13147
13326
  }
13327
+ if (firstArg === "connect") {
13328
+ const mod2 = await Promise.resolve().then(() => (init_jpi_connect(), jpi_connect_exports));
13329
+ await mod2.run();
13330
+ process.exit(0);
13331
+ }
13148
13332
  if (firstArg === "link") {
13149
13333
  process.argv.splice(2, 1);
13150
13334
  const mod2 = await Promise.resolve().then(() => (init_jpi_link(), jpi_link_exports));