terminalhire 0.10.0 → 0.10.1

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.
@@ -9732,11 +9732,21 @@ async function runIntroRequest(args5, overrides) {
9732
9732
  deps.log(" shared only if they do.\n");
9733
9733
  }
9734
9734
  }
9735
+ async function fetchIntros(deps, cookie) {
9736
+ const res = await deps.fetchImpl(`${LINK_BASE2}/api/intro/list`, {
9737
+ method: "GET",
9738
+ headers: { Cookie: `${GH_SESSION_COOKIE2}=${cookie}` },
9739
+ signal: AbortSignal.timeout(1e4)
9740
+ });
9741
+ if (!res.ok) throw new Error(`/api/intro/list returned ${res.status}`);
9742
+ const data = await res.json().catch(() => ({}));
9743
+ return data.intros ?? [];
9744
+ }
9735
9745
  async function runIntroDecision(args5, overrides) {
9736
9746
  const deps = { ...defaultIntroDeps(), ...overrides };
9737
- const id = args5.id?.trim();
9747
+ let id = args5.id?.trim() ?? "";
9738
9748
  if (!id) {
9739
- deps.errorLog("\n Usage: terminalhire intro --accept <id> | --decline <id>\n");
9749
+ deps.errorLog("\n Usage: terminalhire intro --accept <@handle|id> | --decline <@handle|id>\n");
9740
9750
  deps.exit(1);
9741
9751
  return;
9742
9752
  }
@@ -9747,19 +9757,51 @@ async function runIntroDecision(args5, overrides) {
9747
9757
  deps.exit(0);
9748
9758
  return;
9749
9759
  }
9760
+ if (!UUID_RE.test(id)) {
9761
+ const handle = id.replace(/^@/, "").toLowerCase();
9762
+ let intros;
9763
+ try {
9764
+ intros = await fetchIntros(deps, cookie);
9765
+ } catch (err) {
9766
+ deps.errorLog(`
9767
+ Could not look up the request: ${err instanceof Error ? err.message : String(err)}
9768
+ `);
9769
+ deps.exit(1);
9770
+ return;
9771
+ }
9772
+ const matches = intros.filter(
9773
+ (it) => it.role === "incoming" && it.status === "pending" && it.counterpartyLogin.toLowerCase() === handle
9774
+ );
9775
+ if (matches.length === 0) {
9776
+ deps.errorLog(`
9777
+ No pending connection request from @${handle}.`);
9778
+ deps.errorLog(" See your requests with `terminalhire intro --list`.\n");
9779
+ deps.exit(1);
9780
+ return;
9781
+ }
9782
+ if (matches.length > 1) {
9783
+ deps.errorLog(`
9784
+ ${matches.length} pending requests from @${handle} \u2014 ${args5.action === "accept" ? "accepting" : "declining"} one (the rest are redundant duplicates).`);
9785
+ }
9786
+ id = matches[0].id;
9787
+ }
9750
9788
  let contact = "";
9789
+ let shareHandle = false;
9751
9790
  if (args5.action === "accept") {
9752
9791
  const profile = await deps.readProfileContact();
9753
9792
  contact = (args5.contact ?? profile.contactEmail ?? "").trim();
9754
- if (!contact) {
9755
- deps.errorLog("\n No contact on file to share back. Set one with `terminalhire profile --edit`,");
9756
- deps.errorLog(" or pass `--contact <email-or-handle>`. Nothing was sent.\n");
9757
- deps.exit(1);
9758
- return;
9793
+ shareHandle = contact.length === 0;
9794
+ let shareLabel = contact;
9795
+ if (shareHandle) {
9796
+ const login = await deps.readGithubLogin().catch(() => null);
9797
+ shareLabel = login ? `your GitHub handle (@${login})` : "your GitHub handle";
9759
9798
  }
9760
9799
  deps.log("");
9761
- deps.log(" Accepting shares your contact with the requester so they can reach you:");
9762
- deps.log(` Your contact : ${contact}`);
9800
+ deps.log(" Accepting shares a contact with the requester so they can reach you:");
9801
+ deps.log(` Your contact : ${shareLabel}`);
9802
+ if (shareHandle) {
9803
+ deps.log(" (share an email/handle instead with `--contact <email-or-handle>`)");
9804
+ }
9763
9805
  deps.log(" Nothing else is shared. Declining shares nothing.");
9764
9806
  deps.log("");
9765
9807
  const answer = await deps.prompt(' Type "yes" to accept and share your contact (anything else cancels): ');
@@ -9769,7 +9811,7 @@ async function runIntroDecision(args5, overrides) {
9769
9811
  return;
9770
9812
  }
9771
9813
  }
9772
- const body = args5.action === "accept" ? { introId: id, action: "accept", targetContact: contact } : { introId: id, action: "decline" };
9814
+ const body = args5.action === "accept" ? shareHandle ? { introId: id, action: "accept" } : { introId: id, action: "accept", targetContact: contact } : { introId: id, action: "decline" };
9773
9815
  let res;
9774
9816
  try {
9775
9817
  res = await deps.fetchImpl(`${LINK_BASE2}/api/intro/accept`, {
@@ -9874,12 +9916,12 @@ async function runIntroList(overrides) {
9874
9916
  if (it.note) deps.log(` note: ${it.note}`);
9875
9917
  if (it.contact) deps.log(` contact: ${it.contact}`);
9876
9918
  else if (it.role === "incoming" && it.status === "pending") {
9877
- deps.log(` \u2192 accept: terminalhire intro --accept ${it.id}`);
9919
+ deps.log(` \u2192 accept: terminalhire intro --accept @${it.counterpartyLogin}`);
9878
9920
  }
9879
9921
  }
9880
9922
  deps.log("");
9881
9923
  }
9882
- var LINK_BASE2, GH_SESSION_COOKIE2;
9924
+ var LINK_BASE2, GH_SESSION_COOKIE2, UUID_RE;
9883
9925
  var init_intro2 = __esm({
9884
9926
  "src/intro.ts"() {
9885
9927
  "use strict";
@@ -9887,6 +9929,7 @@ var init_intro2 = __esm({
9887
9929
  init_web_session();
9888
9930
  LINK_BASE2 = process.env["TERMINALHIRE_API_URL"] || "https://www.terminalhire.com";
9889
9931
  GH_SESSION_COOKIE2 = "__jpi_gh_session";
9932
+ UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
9890
9933
  }
9891
9934
  });
9892
9935
 
@@ -13218,6 +13261,8 @@ if (!firstArg || firstArg === "help" || firstArg === "--help" || firstArg === "-
13218
13261
  console.log(" terminalhire trajectory --push Opt-in: link your derived trajectory to your dashboard (typed-yes)");
13219
13262
  console.log(" terminalhire trajectory --push --delete Unlink (revoke) your trajectory from the dashboard");
13220
13263
  console.log(" terminalhire intro <github-login> Request a consented intro to another developer (typed-yes)");
13264
+ console.log(" terminalhire intro --list See your sent + received intros");
13265
+ console.log(" terminalhire intro --accept @<login> Accept a pending request by handle (or --decline)");
13221
13266
  console.log(" terminalhire chat Inbox: one line per connection (presence \xB7 unread \xB7 last)");
13222
13267
  console.log(" terminalhire chat <github-login> --read Read a thread inline (last 8; -n N / --all for depth)");
13223
13268
  console.log(' terminalhire chat <github-login> --send "\u2026" Send one line to a connection (E2E encrypted)');
@@ -1258,11 +1258,21 @@ async function runIntroRequest(args, overrides) {
1258
1258
  deps.log(" shared only if they do.\n");
1259
1259
  }
1260
1260
  }
1261
+ async function fetchIntros(deps, cookie) {
1262
+ const res = await deps.fetchImpl(`${LINK_BASE}/api/intro/list`, {
1263
+ method: "GET",
1264
+ headers: { Cookie: `${GH_SESSION_COOKIE}=${cookie}` },
1265
+ signal: AbortSignal.timeout(1e4)
1266
+ });
1267
+ if (!res.ok) throw new Error(`/api/intro/list returned ${res.status}`);
1268
+ const data = await res.json().catch(() => ({}));
1269
+ return data.intros ?? [];
1270
+ }
1261
1271
  async function runIntroDecision(args, overrides) {
1262
1272
  const deps = { ...defaultIntroDeps(), ...overrides };
1263
- const id = args.id?.trim();
1273
+ let id = args.id?.trim() ?? "";
1264
1274
  if (!id) {
1265
- deps.errorLog("\n Usage: terminalhire intro --accept <id> | --decline <id>\n");
1275
+ deps.errorLog("\n Usage: terminalhire intro --accept <@handle|id> | --decline <@handle|id>\n");
1266
1276
  deps.exit(1);
1267
1277
  return;
1268
1278
  }
@@ -1273,19 +1283,51 @@ async function runIntroDecision(args, overrides) {
1273
1283
  deps.exit(0);
1274
1284
  return;
1275
1285
  }
1286
+ if (!UUID_RE.test(id)) {
1287
+ const handle = id.replace(/^@/, "").toLowerCase();
1288
+ let intros;
1289
+ try {
1290
+ intros = await fetchIntros(deps, cookie);
1291
+ } catch (err) {
1292
+ deps.errorLog(`
1293
+ Could not look up the request: ${err instanceof Error ? err.message : String(err)}
1294
+ `);
1295
+ deps.exit(1);
1296
+ return;
1297
+ }
1298
+ const matches = intros.filter(
1299
+ (it) => it.role === "incoming" && it.status === "pending" && it.counterpartyLogin.toLowerCase() === handle
1300
+ );
1301
+ if (matches.length === 0) {
1302
+ deps.errorLog(`
1303
+ No pending connection request from @${handle}.`);
1304
+ deps.errorLog(" See your requests with `terminalhire intro --list`.\n");
1305
+ deps.exit(1);
1306
+ return;
1307
+ }
1308
+ if (matches.length > 1) {
1309
+ deps.errorLog(`
1310
+ ${matches.length} pending requests from @${handle} \u2014 ${args.action === "accept" ? "accepting" : "declining"} one (the rest are redundant duplicates).`);
1311
+ }
1312
+ id = matches[0].id;
1313
+ }
1276
1314
  let contact = "";
1315
+ let shareHandle = false;
1277
1316
  if (args.action === "accept") {
1278
1317
  const profile = await deps.readProfileContact();
1279
1318
  contact = (args.contact ?? profile.contactEmail ?? "").trim();
1280
- if (!contact) {
1281
- deps.errorLog("\n No contact on file to share back. Set one with `terminalhire profile --edit`,");
1282
- deps.errorLog(" or pass `--contact <email-or-handle>`. Nothing was sent.\n");
1283
- deps.exit(1);
1284
- return;
1319
+ shareHandle = contact.length === 0;
1320
+ let shareLabel = contact;
1321
+ if (shareHandle) {
1322
+ const login = await deps.readGithubLogin().catch(() => null);
1323
+ shareLabel = login ? `your GitHub handle (@${login})` : "your GitHub handle";
1285
1324
  }
1286
1325
  deps.log("");
1287
- deps.log(" Accepting shares your contact with the requester so they can reach you:");
1288
- deps.log(` Your contact : ${contact}`);
1326
+ deps.log(" Accepting shares a contact with the requester so they can reach you:");
1327
+ deps.log(` Your contact : ${shareLabel}`);
1328
+ if (shareHandle) {
1329
+ deps.log(" (share an email/handle instead with `--contact <email-or-handle>`)");
1330
+ }
1289
1331
  deps.log(" Nothing else is shared. Declining shares nothing.");
1290
1332
  deps.log("");
1291
1333
  const answer = await deps.prompt(' Type "yes" to accept and share your contact (anything else cancels): ');
@@ -1295,7 +1337,7 @@ async function runIntroDecision(args, overrides) {
1295
1337
  return;
1296
1338
  }
1297
1339
  }
1298
- const body = args.action === "accept" ? { introId: id, action: "accept", targetContact: contact } : { introId: id, action: "decline" };
1340
+ const body = args.action === "accept" ? shareHandle ? { introId: id, action: "accept" } : { introId: id, action: "accept", targetContact: contact } : { introId: id, action: "decline" };
1299
1341
  let res;
1300
1342
  try {
1301
1343
  res = await deps.fetchImpl(`${LINK_BASE}/api/intro/accept`, {
@@ -1400,12 +1442,12 @@ async function runIntroList(overrides) {
1400
1442
  if (it.note) deps.log(` note: ${it.note}`);
1401
1443
  if (it.contact) deps.log(` contact: ${it.contact}`);
1402
1444
  else if (it.role === "incoming" && it.status === "pending") {
1403
- deps.log(` \u2192 accept: terminalhire intro --accept ${it.id}`);
1445
+ deps.log(` \u2192 accept: terminalhire intro --accept @${it.counterpartyLogin}`);
1404
1446
  }
1405
1447
  }
1406
1448
  deps.log("");
1407
1449
  }
1408
- var LINK_BASE, GH_SESSION_COOKIE;
1450
+ var LINK_BASE, GH_SESSION_COOKIE, UUID_RE;
1409
1451
  var init_intro2 = __esm({
1410
1452
  "src/intro.ts"() {
1411
1453
  "use strict";
@@ -1413,6 +1455,7 @@ var init_intro2 = __esm({
1413
1455
  init_web_session();
1414
1456
  LINK_BASE = process.env["TERMINALHIRE_API_URL"] || "https://www.terminalhire.com";
1415
1457
  GH_SESSION_COOKIE = "__jpi_gh_session";
1458
+ UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1416
1459
  }
1417
1460
  });
1418
1461
 
package/dist/src/intro.js CHANGED
@@ -1251,11 +1251,22 @@ async function runIntroRequest(args, overrides) {
1251
1251
  deps.log(" shared only if they do.\n");
1252
1252
  }
1253
1253
  }
1254
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1255
+ async function fetchIntros(deps, cookie) {
1256
+ const res = await deps.fetchImpl(`${LINK_BASE}/api/intro/list`, {
1257
+ method: "GET",
1258
+ headers: { Cookie: `${GH_SESSION_COOKIE}=${cookie}` },
1259
+ signal: AbortSignal.timeout(1e4)
1260
+ });
1261
+ if (!res.ok) throw new Error(`/api/intro/list returned ${res.status}`);
1262
+ const data = await res.json().catch(() => ({}));
1263
+ return data.intros ?? [];
1264
+ }
1254
1265
  async function runIntroDecision(args, overrides) {
1255
1266
  const deps = { ...defaultIntroDeps(), ...overrides };
1256
- const id = args.id?.trim();
1267
+ let id = args.id?.trim() ?? "";
1257
1268
  if (!id) {
1258
- deps.errorLog("\n Usage: terminalhire intro --accept <id> | --decline <id>\n");
1269
+ deps.errorLog("\n Usage: terminalhire intro --accept <@handle|id> | --decline <@handle|id>\n");
1259
1270
  deps.exit(1);
1260
1271
  return;
1261
1272
  }
@@ -1266,19 +1277,51 @@ async function runIntroDecision(args, overrides) {
1266
1277
  deps.exit(0);
1267
1278
  return;
1268
1279
  }
1280
+ if (!UUID_RE.test(id)) {
1281
+ const handle = id.replace(/^@/, "").toLowerCase();
1282
+ let intros;
1283
+ try {
1284
+ intros = await fetchIntros(deps, cookie);
1285
+ } catch (err) {
1286
+ deps.errorLog(`
1287
+ Could not look up the request: ${err instanceof Error ? err.message : String(err)}
1288
+ `);
1289
+ deps.exit(1);
1290
+ return;
1291
+ }
1292
+ const matches = intros.filter(
1293
+ (it) => it.role === "incoming" && it.status === "pending" && it.counterpartyLogin.toLowerCase() === handle
1294
+ );
1295
+ if (matches.length === 0) {
1296
+ deps.errorLog(`
1297
+ No pending connection request from @${handle}.`);
1298
+ deps.errorLog(" See your requests with `terminalhire intro --list`.\n");
1299
+ deps.exit(1);
1300
+ return;
1301
+ }
1302
+ if (matches.length > 1) {
1303
+ deps.errorLog(`
1304
+ ${matches.length} pending requests from @${handle} \u2014 ${args.action === "accept" ? "accepting" : "declining"} one (the rest are redundant duplicates).`);
1305
+ }
1306
+ id = matches[0].id;
1307
+ }
1269
1308
  let contact = "";
1309
+ let shareHandle = false;
1270
1310
  if (args.action === "accept") {
1271
1311
  const profile = await deps.readProfileContact();
1272
1312
  contact = (args.contact ?? profile.contactEmail ?? "").trim();
1273
- if (!contact) {
1274
- deps.errorLog("\n No contact on file to share back. Set one with `terminalhire profile --edit`,");
1275
- deps.errorLog(" or pass `--contact <email-or-handle>`. Nothing was sent.\n");
1276
- deps.exit(1);
1277
- return;
1313
+ shareHandle = contact.length === 0;
1314
+ let shareLabel = contact;
1315
+ if (shareHandle) {
1316
+ const login = await deps.readGithubLogin().catch(() => null);
1317
+ shareLabel = login ? `your GitHub handle (@${login})` : "your GitHub handle";
1278
1318
  }
1279
1319
  deps.log("");
1280
- deps.log(" Accepting shares your contact with the requester so they can reach you:");
1281
- deps.log(` Your contact : ${contact}`);
1320
+ deps.log(" Accepting shares a contact with the requester so they can reach you:");
1321
+ deps.log(` Your contact : ${shareLabel}`);
1322
+ if (shareHandle) {
1323
+ deps.log(" (share an email/handle instead with `--contact <email-or-handle>`)");
1324
+ }
1282
1325
  deps.log(" Nothing else is shared. Declining shares nothing.");
1283
1326
  deps.log("");
1284
1327
  const answer = await deps.prompt(' Type "yes" to accept and share your contact (anything else cancels): ');
@@ -1288,7 +1331,7 @@ async function runIntroDecision(args, overrides) {
1288
1331
  return;
1289
1332
  }
1290
1333
  }
1291
- const body = args.action === "accept" ? { introId: id, action: "accept", targetContact: contact } : { introId: id, action: "decline" };
1334
+ const body = args.action === "accept" ? shareHandle ? { introId: id, action: "accept" } : { introId: id, action: "accept", targetContact: contact } : { introId: id, action: "decline" };
1292
1335
  let res;
1293
1336
  try {
1294
1337
  res = await deps.fetchImpl(`${LINK_BASE}/api/intro/accept`, {
@@ -1393,7 +1436,7 @@ async function runIntroList(overrides) {
1393
1436
  if (it.note) deps.log(` note: ${it.note}`);
1394
1437
  if (it.contact) deps.log(` contact: ${it.contact}`);
1395
1438
  else if (it.role === "incoming" && it.status === "pending") {
1396
- deps.log(` \u2192 accept: terminalhire intro --accept ${it.id}`);
1439
+ deps.log(` \u2192 accept: terminalhire intro --accept @${it.counterpartyLogin}`);
1397
1440
  }
1398
1441
  }
1399
1442
  deps.log("");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminalhire",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "description": "Local-first job matching for developers — ambient job matches in the Claude Code spinner. Matching runs on your machine; your profile never leaves it.",
5
5
  "repository": {
6
6
  "type": "git",