terminalhire 0.42.16 → 0.42.18

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.
Files changed (44) hide show
  1. package/dist/bin/approved-claims-sync.js +2 -0
  2. package/dist/bin/claim-push-bg.js +60 -13
  3. package/dist/bin/directory.js +7 -5
  4. package/dist/bin/founder-note-sync.js +2 -0
  5. package/dist/bin/founder-verdict-sync.js +2 -0
  6. package/dist/bin/jpi-bounties.js +81 -41
  7. package/dist/bin/jpi-chat-read.js +25 -15
  8. package/dist/bin/jpi-chat.js +66 -34
  9. package/dist/bin/jpi-claim.js +413 -145
  10. package/dist/bin/jpi-contribute.js +78 -38
  11. package/dist/bin/jpi-devs.js +25 -23
  12. package/dist/bin/jpi-dispatch-dev.js +53 -0
  13. package/dist/bin/jpi-dispatch.js +671 -352
  14. package/dist/bin/jpi-hub.js +89 -42
  15. package/dist/bin/jpi-inbox.js +65 -33
  16. package/dist/bin/jpi-init.js +24 -22
  17. package/dist/bin/jpi-intro.js +17 -15
  18. package/dist/bin/jpi-jobs.js +73 -49
  19. package/dist/bin/jpi-link.js +7 -5
  20. package/dist/bin/jpi-login.js +30 -28
  21. package/dist/bin/jpi-mcp-chat.js +27 -17
  22. package/dist/bin/jpi-mcp.js +434 -166
  23. package/dist/bin/jpi-post.js +8 -6
  24. package/dist/bin/jpi-project.js +14 -12
  25. package/dist/bin/jpi-refresh.js +174 -92
  26. package/dist/bin/jpi-repo.js +40 -2
  27. package/dist/bin/jpi-run.js +68 -66
  28. package/dist/bin/jpi-spinner.js +22 -7
  29. package/dist/bin/jpi-statusline.js +24 -12
  30. package/dist/bin/jpi-sync.js +29 -20
  31. package/dist/bin/jpi-trajectory.js +26 -24
  32. package/dist/bin/jpi.js +42 -4
  33. package/dist/bin/peer-connect-prompt.js +2 -0
  34. package/dist/bin/pulse-prompt.js +4 -2
  35. package/dist/bin/recall-check.js +7 -5
  36. package/dist/bin/spinner-render.js +23 -7
  37. package/dist/bin/spinner.js +23 -7
  38. package/dist/src/api-base.js +16 -0
  39. package/dist/src/chat-client.js +6 -4
  40. package/dist/src/claims.js +39 -1
  41. package/dist/src/intro.js +17 -15
  42. package/dist/src/link.js +2 -0
  43. package/dist/src/trajectory.js +26 -24
  44. package/package.json +4 -2
@@ -1,4 +1,6 @@
1
1
  // src/api-base.ts
2
+ import { homedir } from "os";
3
+ import { join } from "path";
2
4
  var PROD_API_BASE = "https://terminalhire.com";
3
5
  var DEV_API_BASE = "https://dev.terminalhire.com";
4
6
  var ApiBaseError = class extends Error {
@@ -91,10 +91,14 @@ var init_state_dir = __esm({
91
91
  var claims_exports = {};
92
92
  __export(claims_exports, {
93
93
  CLAIM_STATES: () => CLAIM_STATES,
94
+ CLAIM_TURN_LABEL_WIDTH: () => CLAIM_TURN_LABEL_WIDTH,
94
95
  PUSHED_CLAIM_FIELDS: () => PUSHED_CLAIM_FIELDS,
95
96
  acceptedPRRate: () => acceptedPRRate,
97
+ claimTurn: () => claimTurn,
98
+ claimTurnLabel: () => claimTurnLabel,
96
99
  countAwaitingFounderApproval: () => countAwaitingFounderApproval,
97
100
  findClaim: () => findClaim,
101
+ formatAcceptedPRRate: () => formatAcceptedPRRate,
98
102
  listClaims: () => listClaims,
99
103
  nextPolledState: () => nextPolledState,
100
104
  readClaims: () => readClaims,
@@ -115,8 +119,8 @@ import {
115
119
  statSync
116
120
  } from "fs";
117
121
  import { randomBytes as randomBytes3 } from "crypto";
118
- import { join as join4 } from "path";
119
- import { homedir as homedir3 } from "os";
122
+ import { join as join5 } from "path";
123
+ import { homedir as homedir4 } from "os";
120
124
  function sleepSync(ms) {
121
125
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
122
126
  }
@@ -149,6 +153,12 @@ function withClaimsLock(fn) {
149
153
  rmSync2(LOCK_DIR, { recursive: true, force: true });
150
154
  }
151
155
  }
156
+ function claimTurn(state) {
157
+ return CLAIM_TURN_BY_STATE[state] ?? "you";
158
+ }
159
+ function claimTurnLabel(state) {
160
+ return CLAIM_TURN_LABEL[claimTurn(state)];
161
+ }
152
162
  function toPushedClaim(claim) {
153
163
  return {
154
164
  kind: claim.kind,
@@ -303,15 +313,24 @@ function countAwaitingFounderApproval(claims = readClaims()) {
303
313
  function acceptedPRRate(claims = readClaims()) {
304
314
  const total = claims.length;
305
315
  const merged = claims.filter((c) => c.state === "merged").length;
306
- return { merged, total, rate: total === 0 ? 0 : merged / total };
316
+ const decided = claims.filter((c) => DECIDED_STATES.has(c.state)).length;
317
+ return { merged, decided, inFlight: total - decided, total, rate: decided === 0 ? 0 : merged / decided };
318
+ }
319
+ function formatAcceptedPRRate(rate) {
320
+ const inFlight = rate.inFlight > 0 ? ` \xB7 ${rate.inFlight} in flight` : "";
321
+ if (rate.decided === 0) {
322
+ return `Accepted-PR rate: no claims decided yet${inFlight}`;
323
+ }
324
+ const pct = Math.round(rate.rate * 100);
325
+ return `Accepted-PR rate: ${rate.merged}/${rate.decided} decided claims merged (${pct}%)${inFlight}`;
307
326
  }
308
- var TERMINALHIRE_DIR3, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, CLAIM_STATES, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS, WHITESPACE_CONTROLS, CONTROL_CHARS;
327
+ var TERMINALHIRE_DIR3, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, CLAIM_STATES, CLAIM_TURN_BY_STATE, CLAIM_TURN_LABEL, CLAIM_TURN_LABEL_WIDTH, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS, WHITESPACE_CONTROLS, CONTROL_CHARS, DECIDED_STATES;
309
328
  var init_claims = __esm({
310
329
  "src/claims.ts"() {
311
330
  "use strict";
312
331
  init_state_dir();
313
- TERMINALHIRE_DIR3 = process.env.TERMINALHIRE_DIR || join4(homedir3(), ".terminalhire");
314
- CLAIMS_FILE = join4(TERMINALHIRE_DIR3, "claims.json");
332
+ TERMINALHIRE_DIR3 = process.env.TERMINALHIRE_DIR || join5(homedir4(), ".terminalhire");
333
+ CLAIMS_FILE = join5(TERMINALHIRE_DIR3, "claims.json");
315
334
  LOCK_DIR = `${CLAIMS_FILE}.lock`;
316
335
  LOCK_STALE_MS = Number(process.env.TERMINALHIRE_LOCK_STALE_MS) || 1e4;
317
336
  LOCK_RETRY_MS = Number(process.env.TERMINALHIRE_LOCK_RETRY_MS) || 25;
@@ -332,6 +351,24 @@ var init_claims = __esm({
332
351
  "abandoned"
333
352
  // released, or PR closed unmerged
334
353
  ]);
354
+ CLAIM_TURN_BY_STATE = Object.freeze({
355
+ claimed: "you",
356
+ working: "you",
357
+ "in-review": "you",
358
+ ready: "you",
359
+ submitted: "poster",
360
+ merged: "merged",
361
+ abandoned: "closed"
362
+ });
363
+ CLAIM_TURN_LABEL = Object.freeze({
364
+ you: "waiting on you",
365
+ poster: "waiting on poster",
366
+ merged: "merged",
367
+ closed: "closed"
368
+ });
369
+ CLAIM_TURN_LABEL_WIDTH = Math.max(
370
+ ...Object.values(CLAIM_TURN_LABEL).map((label) => label.length)
371
+ );
335
372
  PUSHED_CLAIM_FIELDS = [
336
373
  "kind",
337
374
  "repoFullName",
@@ -363,14 +400,15 @@ var init_claims = __esm({
363
400
  };
364
401
  WHITESPACE_CONTROLS = /[\t\n\v\f\r]+/g;
365
402
  CONTROL_CHARS = /[\x00-\x1f\x7f-\x9f]/g;
403
+ DECIDED_STATES = /* @__PURE__ */ new Set(["merged", "abandoned"]);
366
404
  }
367
405
  });
368
406
 
369
407
  // bin/claim-push-bg.js
370
408
  import { createHash } from "crypto";
371
409
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as existsSync5, rmSync as rmSync3 } from "fs";
372
- import { join as join5 } from "path";
373
- import { homedir as homedir4 } from "os";
410
+ import { join as join6 } from "path";
411
+ import { homedir as homedir5 } from "os";
374
412
 
375
413
  // src/github-auth.ts
376
414
  init_state_dir();
@@ -511,8 +549,11 @@ function decrypt(blob, key) {
511
549
  init_state_dir();
512
550
 
513
551
  // src/api-base.ts
552
+ import { homedir as homedir3 } from "os";
553
+ import { join as join4 } from "path";
514
554
  var PROD_API_BASE = "https://terminalhire.com";
515
555
  var DEV_API_BASE = "https://dev.terminalhire.com";
556
+ var DEV_STATE_DIR_NAME = ".terminalhire-dev";
516
557
  var ApiBaseError = class extends Error {
517
558
  constructor(message) {
518
559
  super(message);
@@ -613,6 +654,7 @@ function isNonProdApiBase(base = resolveApiBaseOrProd()) {
613
654
  }
614
655
  function warnSharedCredentialsIfNonProd(base, stream = process.stderr) {
615
656
  if (!isNonProdApiBase(base)) return;
657
+ if (usingSeparateStateDir()) return;
616
658
  try {
617
659
  stream.write(
618
660
  "terminalhire: non-prod API base \u2014 using the same local session/push credentials as prod; do not mix environments casually.\n"
@@ -620,12 +662,17 @@ function warnSharedCredentialsIfNonProd(base, stream = process.stderr) {
620
662
  } catch {
621
663
  }
622
664
  }
665
+ function usingSeparateStateDir(env = process.env) {
666
+ const dir = env["TERMINALHIRE_DIR"];
667
+ if (dir === void 0 || dir === "") return false;
668
+ return dir.endsWith(DEV_STATE_DIR_NAME);
669
+ }
623
670
 
624
671
  // bin/claim-push-bg.js
625
- var TERMINALHIRE_DIR4 = process.env.TERMINALHIRE_DIR || join5(homedir4(), ".terminalhire");
626
- var CLAIM_PUSH_AUTO_MARKER = join5(TERMINALHIRE_DIR4, "claim-push-auto.json");
627
- var CLAIM_PUSH_TOKEN_FILE = join5(TERMINALHIRE_DIR4, "claim-push-token.enc");
628
- var CLAIM_PUSH_MANUAL_MARKER = join5(TERMINALHIRE_DIR4, "claim-push.json");
672
+ var TERMINALHIRE_DIR4 = process.env.TERMINALHIRE_DIR || join6(homedir5(), ".terminalhire");
673
+ var CLAIM_PUSH_AUTO_MARKER = join6(TERMINALHIRE_DIR4, "claim-push-auto.json");
674
+ var CLAIM_PUSH_TOKEN_FILE = join6(TERMINALHIRE_DIR4, "claim-push-token.enc");
675
+ var CLAIM_PUSH_MANUAL_MARKER = join6(TERMINALHIRE_DIR4, "claim-push.json");
629
676
  var CLAIM_SYNC_BASE = resolveApiBase();
630
677
  warnSharedCredentialsIfNonProd(CLAIM_SYNC_BASE);
631
678
  var AUTO_CONSENT_VERSION = 3;
@@ -731,7 +778,7 @@ async function shouldNudgeUnpushed() {
731
778
  return false;
732
779
  }
733
780
  }
734
- var CLAIM_HEARTBEAT_FILE = join5(TERMINALHIRE_DIR4, "claim-heartbeat.json");
781
+ var CLAIM_HEARTBEAT_FILE = join6(TERMINALHIRE_DIR4, "claim-heartbeat.json");
735
782
  var HEARTBEAT_MIN_INTERVAL_MS = 3e4;
736
783
  var HEARTBEAT_MIN_CONSENT_VERSION = 3;
737
784
  function readHeartbeatState() {
@@ -2,8 +2,8 @@
2
2
 
3
3
  // bin/directory.js
4
4
  import { readFileSync, writeFileSync, renameSync } from "fs";
5
- import { join } from "path";
6
- import { homedir } from "os";
5
+ import { join as join2 } from "path";
6
+ import { homedir as homedir2 } from "os";
7
7
 
8
8
  // src/state-dir.ts
9
9
  import { closeSync, constants, fchmodSync, fstatSync, mkdirSync, openSync } from "fs";
@@ -54,6 +54,8 @@ function ensureStateDir(dir) {
54
54
  }
55
55
 
56
56
  // src/api-base.ts
57
+ import { homedir } from "os";
58
+ import { join } from "path";
57
59
  var PROD_API_BASE = "https://terminalhire.com";
58
60
  var DEV_API_BASE = "https://dev.terminalhire.com";
59
61
  var ApiBaseError = class extends Error {
@@ -146,9 +148,9 @@ function normalizeOverride(raw) {
146
148
  }
147
149
 
148
150
  // bin/directory.js
149
- var TERMINALHIRE_DIR = process.env.TERMINALHIRE_DIR || join(homedir(), ".terminalhire");
150
- var DIRECTORY_CACHE_FILE = join(TERMINALHIRE_DIR, "directory-cache.json");
151
- var PROJECT_FILE = join(TERMINALHIRE_DIR, "project.json");
151
+ var TERMINALHIRE_DIR = process.env.TERMINALHIRE_DIR || join2(homedir2(), ".terminalhire");
152
+ var DIRECTORY_CACHE_FILE = join2(TERMINALHIRE_DIR, "directory-cache.json");
153
+ var PROJECT_FILE = join2(TERMINALHIRE_DIR, "project.json");
152
154
  var INDEX_TTL_MS = 15 * 60 * 1e3;
153
155
  var API_URL = resolveApiBase();
154
156
  function readDirectoryCache() {
@@ -1,4 +1,6 @@
1
1
  // src/api-base.ts
2
+ import { homedir } from "os";
3
+ import { join } from "path";
2
4
  var PROD_API_BASE = "https://terminalhire.com";
3
5
  var DEV_API_BASE = "https://dev.terminalhire.com";
4
6
  var ApiBaseError = class extends Error {
@@ -1,4 +1,6 @@
1
1
  // src/api-base.ts
2
+ import { homedir } from "os";
3
+ import { join } from "path";
2
4
  var PROD_API_BASE = "https://terminalhire.com";
3
5
  var DEV_API_BASE = "https://dev.terminalhire.com";
4
6
  var ApiBaseError = class extends Error {
@@ -10562,17 +10562,17 @@ var init_founder_pin = __esm({
10562
10562
 
10563
10563
  // src/test-race-barrier.ts
10564
10564
  import { closeSync as closeSync2, constants as constants2, existsSync, lstatSync, openSync as openSync2 } from "fs";
10565
- import { join as join3 } from "path";
10565
+ import { join as join4 } from "path";
10566
10566
  function syncSleepMs(ms) {
10567
10567
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
10568
10568
  }
10569
10569
  function waitForTestRaceBarrier(phase) {
10570
10570
  const root = process.env[ENV_VAR];
10571
10571
  if (!root) return;
10572
- const phaseDir = join3(root, phase);
10572
+ const phaseDir = join4(root, phase);
10573
10573
  if (!existsSync(phaseDir)) return;
10574
- const readyFile = join3(phaseDir, `ready-${process.pid}`);
10575
- const goFile = join3(phaseDir, "go");
10574
+ const readyFile = join4(phaseDir, `ready-${process.pid}`);
10575
+ const goFile = join4(phaseDir, "go");
10576
10576
  const noFollow = constants2.O_NOFOLLOW ?? 0;
10577
10577
  if (lstatSync(readyFile, { throwIfNoEntry: false })) {
10578
10578
  throw new Error(
@@ -10612,8 +10612,8 @@ var init_test_race_barrier = __esm({
10612
10612
  // src/shared-key.ts
10613
10613
  import { randomBytes as randomBytes3 } from "crypto";
10614
10614
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync2, linkSync, unlinkSync } from "fs";
10615
- import { join as join4 } from "path";
10616
- import { homedir as homedir2 } from "os";
10615
+ import { join as join5 } from "path";
10616
+ import { homedir as homedir3 } from "os";
10617
10617
  function isValidKeyHex(value) {
10618
10618
  return KEY_HEX_RE.test(value);
10619
10619
  }
@@ -10667,8 +10667,8 @@ var init_shared_key = __esm({
10667
10667
  "use strict";
10668
10668
  init_state_dir();
10669
10669
  init_test_race_barrier();
10670
- TERMINALHIRE_DIR2 = process.env.TERMINALHIRE_DIR || join4(homedir2(), ".terminalhire");
10671
- KEY_FILE = join4(TERMINALHIRE_DIR2, "key");
10670
+ TERMINALHIRE_DIR2 = process.env.TERMINALHIRE_DIR || join5(homedir3(), ".terminalhire");
10671
+ KEY_FILE = join5(TERMINALHIRE_DIR2, "key");
10672
10672
  KEY_BYTES = 32;
10673
10673
  KEY_HEX_RE = new RegExp(`^[0-9a-f]{${KEY_BYTES * 2}}$`);
10674
10674
  }
@@ -10677,7 +10677,7 @@ var init_shared_key = __esm({
10677
10677
  // src/crypto-store.ts
10678
10678
  import { createCipheriv, createDecipheriv, randomBytes as randomBytes4 } from "crypto";
10679
10679
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync3, renameSync as renameSync2, rmSync, readdirSync } from "fs";
10680
- import { join as join5, dirname, basename } from "path";
10680
+ import { join as join6, dirname, basename } from "path";
10681
10681
  import { createRequire } from "module";
10682
10682
  function encrypt(plaintext, key) {
10683
10683
  const iv = randomBytes4(IV_BYTES);
@@ -10732,7 +10732,7 @@ function makeWarnOnce() {
10732
10732
  function atomicWriteFileSync(filePath, content) {
10733
10733
  const dir = dirname(filePath);
10734
10734
  ensureStateDirForSecret(dir);
10735
- const tmp = join5(
10735
+ const tmp = join6(
10736
10736
  dir,
10737
10737
  `.${basename(filePath)}.tmp-${process.pid}-${randomBytes4(6).toString("hex")}`
10738
10738
  );
@@ -10750,7 +10750,7 @@ async function deleteKey() {
10750
10750
  }
10751
10751
  for (const name of encFiles) {
10752
10752
  try {
10753
- rmSync(join5(stateDir, name));
10753
+ rmSync(join6(stateDir, name));
10754
10754
  } catch (e) {
10755
10755
  const code = e.code;
10756
10756
  if (code !== "ENOENT") {
@@ -10841,8 +10841,8 @@ __export(profile_exports, {
10841
10841
  removeSavedJob: () => removeSavedJob,
10842
10842
  writeProfile: () => writeProfile
10843
10843
  });
10844
- import { join as join6 } from "path";
10845
- import { homedir as homedir3 } from "os";
10844
+ import { join as join7 } from "path";
10845
+ import { homedir as homedir4 } from "os";
10846
10846
  function blankProfile() {
10847
10847
  return {
10848
10848
  version: 3,
@@ -10975,8 +10975,8 @@ var init_profile = __esm({
10975
10975
  "use strict";
10976
10976
  init_src();
10977
10977
  init_crypto_store();
10978
- TERMINALHIRE_DIR3 = process.env.TERMINALHIRE_DIR || join6(homedir3(), ".terminalhire");
10979
- PROFILE_FILE = join6(TERMINALHIRE_DIR3, "profile.enc");
10978
+ TERMINALHIRE_DIR3 = process.env.TERMINALHIRE_DIR || join7(homedir4(), ".terminalhire");
10979
+ PROFILE_FILE = join7(TERMINALHIRE_DIR3, "profile.enc");
10980
10980
  profileStore = createEncryptedStore(PROFILE_FILE, {
10981
10981
  blank: blankProfile,
10982
10982
  keyPolicy: "keytar-first-file-fallback"
@@ -11028,8 +11028,8 @@ __export(repo_experience_exports, {
11028
11028
  recordPolicySnapshot: () => recordPolicySnapshot,
11029
11029
  writeTombstone: () => writeTombstone
11030
11030
  });
11031
- import { join as join7 } from "path";
11032
- import { homedir as homedir4 } from "os";
11031
+ import { join as join8 } from "path";
11032
+ import { homedir as homedir5 } from "os";
11033
11033
  function blankFile() {
11034
11034
  return { version: 1, repos: {} };
11035
11035
  }
@@ -11236,8 +11236,8 @@ var init_repo_experience = __esm({
11236
11236
  "use strict";
11237
11237
  init_crypto_store();
11238
11238
  init_profile();
11239
- TERMINALHIRE_DIR4 = process.env.TERMINALHIRE_DIR || join7(homedir4(), ".terminalhire");
11240
- REPO_EXPERIENCE_FILE = join7(TERMINALHIRE_DIR4, "repo-experience.enc");
11239
+ TERMINALHIRE_DIR4 = process.env.TERMINALHIRE_DIR || join8(homedir5(), ".terminalhire");
11240
+ REPO_EXPERIENCE_FILE = join8(TERMINALHIRE_DIR4, "repo-experience.enc");
11241
11241
  MAX_REPOS = 100;
11242
11242
  MAX_CULTURE_SAMPLES = 12;
11243
11243
  MAX_NOTES = 10;
@@ -11254,10 +11254,14 @@ var init_repo_experience = __esm({
11254
11254
  var claims_exports = {};
11255
11255
  __export(claims_exports, {
11256
11256
  CLAIM_STATES: () => CLAIM_STATES,
11257
+ CLAIM_TURN_LABEL_WIDTH: () => CLAIM_TURN_LABEL_WIDTH,
11257
11258
  PUSHED_CLAIM_FIELDS: () => PUSHED_CLAIM_FIELDS,
11258
11259
  acceptedPRRate: () => acceptedPRRate,
11260
+ claimTurn: () => claimTurn,
11261
+ claimTurnLabel: () => claimTurnLabel,
11259
11262
  countAwaitingFounderApproval: () => countAwaitingFounderApproval,
11260
11263
  findClaim: () => findClaim,
11264
+ formatAcceptedPRRate: () => formatAcceptedPRRate,
11261
11265
  listClaims: () => listClaims,
11262
11266
  nextPolledState: () => nextPolledState,
11263
11267
  readClaims: () => readClaims,
@@ -11278,8 +11282,8 @@ import {
11278
11282
  statSync
11279
11283
  } from "fs";
11280
11284
  import { randomBytes as randomBytes5 } from "crypto";
11281
- import { join as join8 } from "path";
11282
- import { homedir as homedir5 } from "os";
11285
+ import { join as join9 } from "path";
11286
+ import { homedir as homedir6 } from "os";
11283
11287
  function sleepSync(ms) {
11284
11288
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
11285
11289
  }
@@ -11312,6 +11316,12 @@ function withClaimsLock(fn) {
11312
11316
  rmSync2(LOCK_DIR, { recursive: true, force: true });
11313
11317
  }
11314
11318
  }
11319
+ function claimTurn(state) {
11320
+ return CLAIM_TURN_BY_STATE[state] ?? "you";
11321
+ }
11322
+ function claimTurnLabel(state) {
11323
+ return CLAIM_TURN_LABEL[claimTurn(state)];
11324
+ }
11315
11325
  function toPushedClaim(claim) {
11316
11326
  return {
11317
11327
  kind: claim.kind,
@@ -11466,15 +11476,24 @@ function countAwaitingFounderApproval(claims = readClaims()) {
11466
11476
  function acceptedPRRate(claims = readClaims()) {
11467
11477
  const total = claims.length;
11468
11478
  const merged = claims.filter((c) => c.state === "merged").length;
11469
- return { merged, total, rate: total === 0 ? 0 : merged / total };
11479
+ const decided = claims.filter((c) => DECIDED_STATES.has(c.state)).length;
11480
+ return { merged, decided, inFlight: total - decided, total, rate: decided === 0 ? 0 : merged / decided };
11470
11481
  }
11471
- var TERMINALHIRE_DIR5, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, CLAIM_STATES, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS, WHITESPACE_CONTROLS2, CONTROL_CHARS2;
11482
+ function formatAcceptedPRRate(rate) {
11483
+ const inFlight = rate.inFlight > 0 ? ` \xB7 ${rate.inFlight} in flight` : "";
11484
+ if (rate.decided === 0) {
11485
+ return `Accepted-PR rate: no claims decided yet${inFlight}`;
11486
+ }
11487
+ const pct = Math.round(rate.rate * 100);
11488
+ return `Accepted-PR rate: ${rate.merged}/${rate.decided} decided claims merged (${pct}%)${inFlight}`;
11489
+ }
11490
+ var TERMINALHIRE_DIR5, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, CLAIM_STATES, CLAIM_TURN_BY_STATE, CLAIM_TURN_LABEL, CLAIM_TURN_LABEL_WIDTH, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS, WHITESPACE_CONTROLS2, CONTROL_CHARS2, DECIDED_STATES;
11472
11491
  var init_claims = __esm({
11473
11492
  "src/claims.ts"() {
11474
11493
  "use strict";
11475
11494
  init_state_dir();
11476
- TERMINALHIRE_DIR5 = process.env.TERMINALHIRE_DIR || join8(homedir5(), ".terminalhire");
11477
- CLAIMS_FILE = join8(TERMINALHIRE_DIR5, "claims.json");
11495
+ TERMINALHIRE_DIR5 = process.env.TERMINALHIRE_DIR || join9(homedir6(), ".terminalhire");
11496
+ CLAIMS_FILE = join9(TERMINALHIRE_DIR5, "claims.json");
11478
11497
  LOCK_DIR = `${CLAIMS_FILE}.lock`;
11479
11498
  LOCK_STALE_MS = Number(process.env.TERMINALHIRE_LOCK_STALE_MS) || 1e4;
11480
11499
  LOCK_RETRY_MS = Number(process.env.TERMINALHIRE_LOCK_RETRY_MS) || 25;
@@ -11495,6 +11514,24 @@ var init_claims = __esm({
11495
11514
  "abandoned"
11496
11515
  // released, or PR closed unmerged
11497
11516
  ]);
11517
+ CLAIM_TURN_BY_STATE = Object.freeze({
11518
+ claimed: "you",
11519
+ working: "you",
11520
+ "in-review": "you",
11521
+ ready: "you",
11522
+ submitted: "poster",
11523
+ merged: "merged",
11524
+ abandoned: "closed"
11525
+ });
11526
+ CLAIM_TURN_LABEL = Object.freeze({
11527
+ you: "waiting on you",
11528
+ poster: "waiting on poster",
11529
+ merged: "merged",
11530
+ closed: "closed"
11531
+ });
11532
+ CLAIM_TURN_LABEL_WIDTH = Math.max(
11533
+ ...Object.values(CLAIM_TURN_LABEL).map((label) => label.length)
11534
+ );
11498
11535
  PUSHED_CLAIM_FIELDS = [
11499
11536
  "kind",
11500
11537
  "repoFullName",
@@ -11526,6 +11563,7 @@ var init_claims = __esm({
11526
11563
  };
11527
11564
  WHITESPACE_CONTROLS2 = /[\t\n\v\f\r]+/g;
11528
11565
  CONTROL_CHARS2 = /[\x00-\x1f\x7f-\x9f]/g;
11566
+ DECIDED_STATES = /* @__PURE__ */ new Set(["merged", "abandoned"]);
11529
11567
  }
11530
11568
  });
11531
11569
 
@@ -11547,8 +11585,8 @@ __export(config_exports, {
11547
11585
  writeConfig: () => writeConfig
11548
11586
  });
11549
11587
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync5 } from "fs";
11550
- import { join as join9 } from "path";
11551
- import { homedir as homedir6 } from "os";
11588
+ import { join as join10 } from "path";
11589
+ import { homedir as homedir7 } from "os";
11552
11590
  function readConfig() {
11553
11591
  try {
11554
11592
  if (!existsSync5(CONFIG_FILE)) return { ...DEFAULT_CONFIG };
@@ -11630,8 +11668,8 @@ var init_config = __esm({
11630
11668
  "src/config.ts"() {
11631
11669
  "use strict";
11632
11670
  init_state_dir();
11633
- TERMINALHIRE_DIR6 = process.env.TERMINALHIRE_DIR || join9(homedir6(), ".terminalhire");
11634
- CONFIG_FILE = join9(TERMINALHIRE_DIR6, "config.json");
11671
+ TERMINALHIRE_DIR6 = process.env.TERMINALHIRE_DIR || join10(homedir7(), ".terminalhire");
11672
+ CONFIG_FILE = join10(TERMINALHIRE_DIR6, "config.json");
11635
11673
  DEFAULT_CONFIG = {
11636
11674
  nudge: "session",
11637
11675
  peerConnect: false,
@@ -11756,13 +11794,13 @@ __export(web_session_exports, {
11756
11794
  writeWebSessionFile: () => writeWebSessionFile
11757
11795
  });
11758
11796
  import { chmodSync, existsSync as existsSync6, readFileSync as readFileSync7, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "fs";
11759
- import { homedir as homedir7 } from "os";
11760
- import { join as join10 } from "path";
11797
+ import { homedir as homedir8 } from "os";
11798
+ import { join as join11 } from "path";
11761
11799
  function terminalhireDir() {
11762
- return process.env.TERMINALHIRE_DIR || join10(homedir7(), ".terminalhire");
11800
+ return process.env.TERMINALHIRE_DIR || join11(homedir8(), ".terminalhire");
11763
11801
  }
11764
11802
  function webSessionFilePath() {
11765
- return join10(terminalhireDir(), "web-session");
11803
+ return join11(terminalhireDir(), "web-session");
11766
11804
  }
11767
11805
  function parseWebSessionFile(raw) {
11768
11806
  const trimmed = raw.trim();
@@ -11850,13 +11888,13 @@ __export(jpi_decline_exports, {
11850
11888
  });
11851
11889
  import { createInterface } from "readline";
11852
11890
  import { existsSync as existsSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync7, rmSync as rmSync4 } from "fs";
11853
- import { homedir as homedir8 } from "os";
11854
- import { join as join11 } from "path";
11891
+ import { homedir as homedir9 } from "os";
11892
+ import { join as join12 } from "path";
11855
11893
  function terminalhireDir2() {
11856
- return process.env.TERMINALHIRE_DIR || join11(homedir8(), ".terminalhire");
11894
+ return process.env.TERMINALHIRE_DIR || join12(homedir9(), ".terminalhire");
11857
11895
  }
11858
11896
  function consentFilePath() {
11859
- return join11(terminalhireDir2(), "decline-consent");
11897
+ return join12(terminalhireDir2(), "decline-consent");
11860
11898
  }
11861
11899
  function postingIdFromJobId(jobId) {
11862
11900
  const id = String(jobId ?? "");
@@ -11994,8 +12032,8 @@ var init_jpi_decline = __esm({
11994
12032
  // bin/jpi-bounties.js
11995
12033
  init_src();
11996
12034
  import { readFileSync as readFileSync9 } from "fs";
11997
- import { join as join12 } from "path";
11998
- import { homedir as homedir9 } from "os";
12035
+ import { join as join13 } from "path";
12036
+ import { homedir as homedir10 } from "os";
11999
12037
  import { createInterface as createInterface2 } from "readline";
12000
12038
 
12001
12039
  // ../../packages/core/src/payout-split.ts
@@ -12212,6 +12250,8 @@ function linkTitle(title, url) {
12212
12250
  }
12213
12251
 
12214
12252
  // src/api-base.ts
12253
+ import { homedir as homedir2 } from "os";
12254
+ import { join as join3 } from "path";
12215
12255
  var PROD_API_BASE = "https://terminalhire.com";
12216
12256
  var DEV_API_BASE = "https://dev.terminalhire.com";
12217
12257
  var ApiBaseError = class extends Error {
@@ -12305,8 +12345,8 @@ function normalizeOverride(raw) {
12305
12345
 
12306
12346
  // bin/jpi-bounties.js
12307
12347
  init_founder_pin();
12308
- var TERMINALHIRE_DIR7 = process.env.TERMINALHIRE_DIR || join12(homedir9(), ".terminalhire");
12309
- var INDEX_CACHE_FILE2 = join12(TERMINALHIRE_DIR7, "index-cache.json");
12348
+ var TERMINALHIRE_DIR7 = process.env.TERMINALHIRE_DIR || join13(homedir10(), ".terminalhire");
12349
+ var INDEX_CACHE_FILE2 = join13(TERMINALHIRE_DIR7, "index-cache.json");
12310
12350
  var INDEX_TTL_MS = 15 * 60 * 1e3;
12311
12351
  var API_URL2 = resolveApiBase();
12312
12352
  var RANK_MODE = process.env["TERMINALHIRE_BOUNTY_RANK"] ?? "winnability";
@@ -4631,6 +4631,8 @@ var init_web_session = __esm({
4631
4631
  });
4632
4632
 
4633
4633
  // src/api-base.ts
4634
+ import { homedir as homedir5 } from "os";
4635
+ import { join as join7 } from "path";
4634
4636
  function sanitizeOverrideForError(raw) {
4635
4637
  try {
4636
4638
  const url = new URL(raw);
@@ -4730,8 +4732,8 @@ var init_api_base = __esm({
4730
4732
 
4731
4733
  // src/chat-client.ts
4732
4734
  import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
4733
- import { homedir as homedir5 } from "os";
4734
- import { join as join7 } from "path";
4735
+ import { homedir as homedir6 } from "os";
4736
+ import { join as join8 } from "path";
4735
4737
  function defaultReadPeerPins() {
4736
4738
  try {
4737
4739
  if (!existsSync6(PEERS_FILE)) return {};
@@ -4936,8 +4938,8 @@ var init_chat_client = __esm({
4936
4938
  init_api_base();
4937
4939
  CHAT_BASE = resolveApiBase();
4938
4940
  GH_SESSION_COOKIE = "__jpi_gh_session";
4939
- TERMINALHIRE_DIR4 = process.env.TERMINALHIRE_DIR || join7(homedir5(), ".terminalhire");
4940
- PEERS_FILE = join7(TERMINALHIRE_DIR4, "chat-peers.json");
4941
+ TERMINALHIRE_DIR4 = process.env.TERMINALHIRE_DIR || join8(homedir6(), ".terminalhire");
4942
+ PEERS_FILE = join8(TERMINALHIRE_DIR4, "chat-peers.json");
4941
4943
  REQUEST_TIMEOUT_MS = 1e4;
4942
4944
  ChatNotLinkedError = class extends Error {
4943
4945
  constructor() {
@@ -4982,8 +4984,8 @@ var init_chat_client = __esm({
4982
4984
 
4983
4985
  // src/config.ts
4984
4986
  import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync7 } from "fs";
4985
- import { join as join8 } from "path";
4986
- import { homedir as homedir6 } from "os";
4987
+ import { join as join9 } from "path";
4988
+ import { homedir as homedir7 } from "os";
4987
4989
  function readConfig() {
4988
4990
  try {
4989
4991
  if (!existsSync7(CONFIG_FILE)) return { ...DEFAULT_CONFIG };
@@ -5011,8 +5013,8 @@ var init_config = __esm({
5011
5013
  "src/config.ts"() {
5012
5014
  "use strict";
5013
5015
  init_state_dir();
5014
- TERMINALHIRE_DIR5 = process.env.TERMINALHIRE_DIR || join8(homedir6(), ".terminalhire");
5015
- CONFIG_FILE = join8(TERMINALHIRE_DIR5, "config.json");
5016
+ TERMINALHIRE_DIR5 = process.env.TERMINALHIRE_DIR || join9(homedir7(), ".terminalhire");
5017
+ CONFIG_FILE = join9(TERMINALHIRE_DIR5, "config.json");
5016
5018
  DEFAULT_CONFIG = {
5017
5019
  nudge: "session",
5018
5020
  peerConnect: false,
@@ -5084,11 +5086,18 @@ var init_tui_core = __esm({
5084
5086
  }
5085
5087
  });
5086
5088
 
5089
+ // bin/session-case.js
5090
+ var init_session_case = __esm({
5091
+ "bin/session-case.js"() {
5092
+ "use strict";
5093
+ }
5094
+ });
5095
+
5087
5096
  // bin/jpi-chat.js
5088
5097
  import { createInterface } from "readline";
5089
5098
  import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
5090
- import { homedir as homedir7 } from "os";
5091
- import { join as join9 } from "path";
5099
+ import { homedir as homedir8 } from "os";
5100
+ import { join as join10 } from "path";
5092
5101
  function defaultPromptAck({ input = process.stdin, output = process.stdout } = {}) {
5093
5102
  if (!input || input.isTTY !== true) return Promise.resolve(false);
5094
5103
  const rl = createInterface({ input, output });
@@ -5216,6 +5225,7 @@ var init_jpi_chat = __esm({
5216
5225
  init_web_session();
5217
5226
  init_api_base();
5218
5227
  init_tui_core();
5228
+ init_session_case();
5219
5229
  CHAT_BASE2 = resolveApiBase();
5220
5230
  GH_SESSION_COOKIE2 = "__jpi_gh_session";
5221
5231
  CHAT_DISCLOSURE = "Messages are end-to-end encrypted using keys stored only on your device. Our server cannot read message content. Since we distribute your contact's public key, verify your connection by comparing Safety Numbers to rule out a server-side substitution. We store metadata: who messaged whom, when, and message count. Content is purged after 90 days.";
@@ -5228,8 +5238,8 @@ var init_jpi_chat = __esm({
5228
5238
 
5229
5239
  // bin/jpi-chat-read.js
5230
5240
  import { existsSync as existsSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
5231
- import { homedir as homedir8 } from "os";
5232
- import { join as join10 } from "path";
5241
+ import { homedir as homedir9 } from "os";
5242
+ import { join as join11 } from "path";
5233
5243
  async function syncUnreadBadge(deps = {}) {
5234
5244
  const readCookie = deps.readCookie ?? readWebSessionCookie;
5235
5245
  const fetchImpl = deps.fetchImpl ?? globalThis.fetch;
@@ -5635,9 +5645,9 @@ var init_jpi_chat_read = __esm({
5635
5645
  init_api_base();
5636
5646
  CHAT_BASE3 = resolveApiBase();
5637
5647
  GH_SESSION_COOKIE3 = "__jpi_gh_session";
5638
- TERMINALHIRE_DIR6 = process.env.TERMINALHIRE_DIR || join10(homedir8(), ".terminalhire");
5639
- READS_FILE = join10(TERMINALHIRE_DIR6, "chat-reads.json");
5640
- INDEX_CACHE_FILE = join10(TERMINALHIRE_DIR6, "index-cache.json");
5648
+ TERMINALHIRE_DIR6 = process.env.TERMINALHIRE_DIR || join11(homedir9(), ".terminalhire");
5649
+ READS_FILE = join11(TERMINALHIRE_DIR6, "chat-reads.json");
5650
+ INDEX_CACHE_FILE = join11(TERMINALHIRE_DIR6, "index-cache.json");
5641
5651
  REACHABLE_DISPLAY = { shareActivity: false, optin: false, lastSeen: null };
5642
5652
  }
5643
5653
  });