terminalhire 0.40.3 → 0.40.4

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.
@@ -90,6 +90,7 @@ var init_state_dir = __esm({
90
90
  // src/claims.ts
91
91
  var claims_exports = {};
92
92
  __export(claims_exports, {
93
+ CLAIM_STATES: () => CLAIM_STATES,
93
94
  PUSHED_CLAIM_FIELDS: () => PUSHED_CLAIM_FIELDS,
94
95
  acceptedPRRate: () => acceptedPRRate,
95
96
  countAwaitingFounderApproval: () => countAwaitingFounderApproval,
@@ -288,7 +289,7 @@ function acceptedPRRate(claims = readClaims()) {
288
289
  const merged = claims.filter((c) => c.state === "merged").length;
289
290
  return { merged, total, rate: total === 0 ? 0 : merged / total };
290
291
  }
291
- var TERMINALHIRE_DIR3, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS;
292
+ 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;
292
293
  var init_claims = __esm({
293
294
  "src/claims.ts"() {
294
295
  "use strict";
@@ -299,6 +300,22 @@ var init_claims = __esm({
299
300
  LOCK_STALE_MS = Number(process.env.TERMINALHIRE_LOCK_STALE_MS) || 1e4;
300
301
  LOCK_RETRY_MS = Number(process.env.TERMINALHIRE_LOCK_RETRY_MS) || 25;
301
302
  LOCK_TIMEOUT_MS = Number(process.env.TERMINALHIRE_LOCK_TIMEOUT_MS) || 5e3;
303
+ CLAIM_STATES = Object.freeze([
304
+ "claimed",
305
+ // recorded, not started
306
+ "working",
307
+ // background executor running / work in progress
308
+ "in-review",
309
+ // gate ran / dev reviewing the diff
310
+ "ready",
311
+ // passed review, cleared to submit
312
+ "submitted",
313
+ // PR opened on the source platform (awaiting maintainer)
314
+ "merged",
315
+ // PR merged — accepted; counts toward the metric
316
+ "abandoned"
317
+ // released, or PR closed unmerged
318
+ ]);
302
319
  PUSHED_CLAIM_FIELDS = [
303
320
  "kind",
304
321
  "repoFullName",
@@ -11122,6 +11122,7 @@ var init_repo_experience = __esm({
11122
11122
  // src/claims.ts
11123
11123
  var claims_exports = {};
11124
11124
  __export(claims_exports, {
11125
+ CLAIM_STATES: () => CLAIM_STATES,
11125
11126
  PUSHED_CLAIM_FIELDS: () => PUSHED_CLAIM_FIELDS,
11126
11127
  acceptedPRRate: () => acceptedPRRate,
11127
11128
  countAwaitingFounderApproval: () => countAwaitingFounderApproval,
@@ -11320,7 +11321,7 @@ function acceptedPRRate(claims = readClaims()) {
11320
11321
  const merged = claims.filter((c) => c.state === "merged").length;
11321
11322
  return { merged, total, rate: total === 0 ? 0 : merged / total };
11322
11323
  }
11323
- var TERMINALHIRE_DIR5, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS;
11324
+ 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;
11324
11325
  var init_claims = __esm({
11325
11326
  "src/claims.ts"() {
11326
11327
  "use strict";
@@ -11331,6 +11332,22 @@ var init_claims = __esm({
11331
11332
  LOCK_STALE_MS = Number(process.env.TERMINALHIRE_LOCK_STALE_MS) || 1e4;
11332
11333
  LOCK_RETRY_MS = Number(process.env.TERMINALHIRE_LOCK_RETRY_MS) || 25;
11333
11334
  LOCK_TIMEOUT_MS = Number(process.env.TERMINALHIRE_LOCK_TIMEOUT_MS) || 5e3;
11335
+ CLAIM_STATES = Object.freeze([
11336
+ "claimed",
11337
+ // recorded, not started
11338
+ "working",
11339
+ // background executor running / work in progress
11340
+ "in-review",
11341
+ // gate ran / dev reviewing the diff
11342
+ "ready",
11343
+ // passed review, cleared to submit
11344
+ "submitted",
11345
+ // PR opened on the source platform (awaiting maintainer)
11346
+ "merged",
11347
+ // PR merged — accepted; counts toward the metric
11348
+ "abandoned"
11349
+ // released, or PR closed unmerged
11350
+ ]);
11334
11351
  PUSHED_CLAIM_FIELDS = [
11335
11352
  "kind",
11336
11353
  "repoFullName",
@@ -10431,6 +10431,7 @@ var init_state_dir = __esm({
10431
10431
  // src/claims.ts
10432
10432
  var claims_exports = {};
10433
10433
  __export(claims_exports, {
10434
+ CLAIM_STATES: () => CLAIM_STATES,
10434
10435
  PUSHED_CLAIM_FIELDS: () => PUSHED_CLAIM_FIELDS,
10435
10436
  acceptedPRRate: () => acceptedPRRate,
10436
10437
  countAwaitingFounderApproval: () => countAwaitingFounderApproval,
@@ -10629,7 +10630,7 @@ function acceptedPRRate(claims = readClaims()) {
10629
10630
  const merged = claims.filter((c) => c.state === "merged").length;
10630
10631
  return { merged, total, rate: total === 0 ? 0 : merged / total };
10631
10632
  }
10632
- var TERMINALHIRE_DIR2, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS;
10633
+ var TERMINALHIRE_DIR2, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, CLAIM_STATES, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS;
10633
10634
  var init_claims = __esm({
10634
10635
  "src/claims.ts"() {
10635
10636
  "use strict";
@@ -10640,6 +10641,22 @@ var init_claims = __esm({
10640
10641
  LOCK_STALE_MS = Number(process.env.TERMINALHIRE_LOCK_STALE_MS) || 1e4;
10641
10642
  LOCK_RETRY_MS = Number(process.env.TERMINALHIRE_LOCK_RETRY_MS) || 25;
10642
10643
  LOCK_TIMEOUT_MS = Number(process.env.TERMINALHIRE_LOCK_TIMEOUT_MS) || 5e3;
10644
+ CLAIM_STATES = Object.freeze([
10645
+ "claimed",
10646
+ // recorded, not started
10647
+ "working",
10648
+ // background executor running / work in progress
10649
+ "in-review",
10650
+ // gate ran / dev reviewing the diff
10651
+ "ready",
10652
+ // passed review, cleared to submit
10653
+ "submitted",
10654
+ // PR opened on the source platform (awaiting maintainer)
10655
+ "merged",
10656
+ // PR merged — accepted; counts toward the metric
10657
+ "abandoned"
10658
+ // released, or PR closed unmerged
10659
+ ]);
10643
10660
  PUSHED_CLAIM_FIELDS = [
10644
10661
  "kind",
10645
10662
  "repoFullName",
@@ -26705,6 +26722,38 @@ function printMetric(rate) {
26705
26722
  console.log(`
26706
26723
  \u{1F4CA} Accepted-PR rate: ${rate.merged}/${rate.total} claims merged (${pct}%)`);
26707
26724
  }
26725
+ var SUBMIT_ACCEPTS = Object.freeze(["working", "ready"]);
26726
+ function nextStepFor(c) {
26727
+ const founder = Boolean(c.approval);
26728
+ switch (c.state) {
26729
+ case "claimed":
26730
+ return founder ? { cmd: `terminalhire claim slice ${c.id}`, why: "collect your granted work slice" } : { cmd: `terminalhire claim start ${c.id}`, why: "fork + clone into a worktree" };
26731
+ // NOT grouped with the two below. `cmdSubmit` accepts 'working' and 'ready'
26732
+ // and nothing else, so pointing an 'in-review' claim at submit would hand the
26733
+ // developer a command that exits 1 — and submit's own refusal then advises
26734
+ // `claim start`, which a founder claim can never use either. Two dead ends in
26735
+ // a row is worse than the silence this block replaced.
26736
+ case "in-review":
26737
+ return {
26738
+ cmd: `terminalhire claim update ${c.id} ready`,
26739
+ why: "clear the review gate \u2014 submit only accepts working/ready"
26740
+ };
26741
+ case "working":
26742
+ case "ready":
26743
+ return { cmd: `terminalhire claim submit ${c.id}`, why: "push the branch + open the PR" };
26744
+ case "submitted":
26745
+ return founder ? { cmd: `terminalhire claim runs ${c.id} --watch`, why: "read the CI result" } : { cmd: `terminalhire claim status ${c.id}`, why: "poll the PR merge state" };
26746
+ default:
26747
+ return null;
26748
+ }
26749
+ }
26750
+ function printNextSteps(list) {
26751
+ const steps = list.map(nextStepFor).filter(Boolean);
26752
+ if (steps.length === 0) return;
26753
+ const width = Math.max(...steps.map((s) => s.cmd.length));
26754
+ console.log("\n Next:");
26755
+ for (const s of steps) console.log(` ${s.cmd.padEnd(width)} \u2014 ${s.why}`);
26756
+ }
26708
26757
  async function resolveBounty(arg) {
26709
26758
  let bountyId, title, repoFullName, issueUrl, amountUSD, source, openPRsAtDiscovery, indexNativeId;
26710
26759
  let founderPosting = false;
@@ -27276,6 +27325,7 @@ ${list.length} ${active ? "active " : ""}claim${list.length === 1 ? "" : "s"}:
27276
27325
  console.log(` id: ${c.id}${pr}`);
27277
27326
  }
27278
27327
  printMetric(claims.acceptedPRRate());
27328
+ printNextSteps(list);
27279
27329
  try {
27280
27330
  if (await shouldNudgeUnpushed()) {
27281
27331
  console.log(
@@ -27424,7 +27474,7 @@ async function cmdStatus(id) {
27424
27474
  }
27425
27475
  async function cmdUpdate(id, state, prUrl) {
27426
27476
  const claims = await Promise.resolve().then(() => (init_claims(), claims_exports));
27427
- const VALID = ["claimed", "working", "in-review", "ready", "submitted", "merged", "abandoned"];
27477
+ const VALID = claims.CLAIM_STATES;
27428
27478
  if (!id || !state || !VALID.includes(state)) {
27429
27479
  console.error("Usage: terminalhire claim update <id> <state> [prUrl]");
27430
27480
  console.error(" state: " + VALID.join(" | "));
@@ -28284,7 +28334,7 @@ async function cmdSubmit(id, flags = {}) {
28284
28334
  console.error(`terminalhire claim: no claim with id '${id}'.`);
28285
28335
  process.exit(1);
28286
28336
  }
28287
- if (claim.state !== "working" && claim.state !== "ready") {
28337
+ if (!SUBMIT_ACCEPTS.includes(claim.state)) {
28288
28338
  console.error(
28289
28339
  `terminalhire claim: ${id} is '${claim.state}'. Submit runs once work has started ('working', via 'claim start') or the review gate cleared it ('ready'). Start it first:
28290
28340
  terminalhire claim start ${id}`
@@ -29236,6 +29286,7 @@ async function run() {
29236
29286
  export {
29237
29287
  AI_DISCLOSURE_NOTE,
29238
29288
  CLAIM_CONSENT_VERSION,
29289
+ SUBMIT_ACCEPTS,
29239
29290
  backgroundEnableFailed,
29240
29291
  buildAssignmentComment,
29241
29292
  buildPatchSubmission,
@@ -29261,9 +29312,11 @@ export {
29261
29312
  listMergedPRsReferencingIssue,
29262
29313
  listOpenPRsReferencingIssue,
29263
29314
  matchReferencingPrs,
29315
+ nextStepFor,
29264
29316
  normalizeIntent,
29265
29317
  pickBodySource,
29266
29318
  pickExistingPr,
29319
+ printNextSteps,
29267
29320
  renderRunView,
29268
29321
  renderServerRefusal,
29269
29322
  resolveBounty,
@@ -3515,6 +3515,7 @@ var init_repo_experience = __esm({
3515
3515
  // src/claims.ts
3516
3516
  var claims_exports = {};
3517
3517
  __export(claims_exports, {
3518
+ CLAIM_STATES: () => CLAIM_STATES,
3518
3519
  PUSHED_CLAIM_FIELDS: () => PUSHED_CLAIM_FIELDS,
3519
3520
  acceptedPRRate: () => acceptedPRRate,
3520
3521
  countAwaitingFounderApproval: () => countAwaitingFounderApproval,
@@ -3713,7 +3714,7 @@ function acceptedPRRate(claims = readClaims()) {
3713
3714
  const merged = claims.filter((c) => c.state === "merged").length;
3714
3715
  return { merged, total, rate: total === 0 ? 0 : merged / total };
3715
3716
  }
3716
- var TERMINALHIRE_DIR7, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS;
3717
+ var TERMINALHIRE_DIR7, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, CLAIM_STATES, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS;
3717
3718
  var init_claims = __esm({
3718
3719
  "src/claims.ts"() {
3719
3720
  "use strict";
@@ -3724,6 +3725,22 @@ var init_claims = __esm({
3724
3725
  LOCK_STALE_MS = Number(process.env.TERMINALHIRE_LOCK_STALE_MS) || 1e4;
3725
3726
  LOCK_RETRY_MS = Number(process.env.TERMINALHIRE_LOCK_RETRY_MS) || 25;
3726
3727
  LOCK_TIMEOUT_MS = Number(process.env.TERMINALHIRE_LOCK_TIMEOUT_MS) || 5e3;
3728
+ CLAIM_STATES = Object.freeze([
3729
+ "claimed",
3730
+ // recorded, not started
3731
+ "working",
3732
+ // background executor running / work in progress
3733
+ "in-review",
3734
+ // gate ran / dev reviewing the diff
3735
+ "ready",
3736
+ // passed review, cleared to submit
3737
+ "submitted",
3738
+ // PR opened on the source platform (awaiting maintainer)
3739
+ "merged",
3740
+ // PR merged — accepted; counts toward the metric
3741
+ "abandoned"
3742
+ // released, or PR closed unmerged
3743
+ ]);
3727
3744
  PUSHED_CLAIM_FIELDS = [
3728
3745
  "kind",
3729
3746
  "repoFullName",
@@ -14859,6 +14859,7 @@ var init_repo_experience = __esm({
14859
14859
  // src/claims.ts
14860
14860
  var claims_exports = {};
14861
14861
  __export(claims_exports, {
14862
+ CLAIM_STATES: () => CLAIM_STATES,
14862
14863
  PUSHED_CLAIM_FIELDS: () => PUSHED_CLAIM_FIELDS,
14863
14864
  acceptedPRRate: () => acceptedPRRate,
14864
14865
  countAwaitingFounderApproval: () => countAwaitingFounderApproval,
@@ -15057,7 +15058,7 @@ function acceptedPRRate(claims = readClaims()) {
15057
15058
  const merged = claims.filter((c) => c.state === "merged").length;
15058
15059
  return { merged, total, rate: total === 0 ? 0 : merged / total };
15059
15060
  }
15060
- var TERMINALHIRE_DIR11, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS2, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS;
15061
+ var TERMINALHIRE_DIR11, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS2, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, CLAIM_STATES, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS;
15061
15062
  var init_claims = __esm({
15062
15063
  "src/claims.ts"() {
15063
15064
  "use strict";
@@ -15068,6 +15069,22 @@ var init_claims = __esm({
15068
15069
  LOCK_STALE_MS2 = Number(process.env.TERMINALHIRE_LOCK_STALE_MS) || 1e4;
15069
15070
  LOCK_RETRY_MS = Number(process.env.TERMINALHIRE_LOCK_RETRY_MS) || 25;
15070
15071
  LOCK_TIMEOUT_MS = Number(process.env.TERMINALHIRE_LOCK_TIMEOUT_MS) || 5e3;
15072
+ CLAIM_STATES = Object.freeze([
15073
+ "claimed",
15074
+ // recorded, not started
15075
+ "working",
15076
+ // background executor running / work in progress
15077
+ "in-review",
15078
+ // gate ran / dev reviewing the diff
15079
+ "ready",
15080
+ // passed review, cleared to submit
15081
+ "submitted",
15082
+ // PR opened on the source platform (awaiting maintainer)
15083
+ "merged",
15084
+ // PR merged — accepted; counts toward the metric
15085
+ "abandoned"
15086
+ // released, or PR closed unmerged
15087
+ ]);
15071
15088
  PUSHED_CLAIM_FIELDS = [
15072
15089
  "kind",
15073
15090
  "repoFullName",
@@ -30462,6 +30479,7 @@ var jpi_claim_exports = {};
30462
30479
  __export(jpi_claim_exports, {
30463
30480
  AI_DISCLOSURE_NOTE: () => AI_DISCLOSURE_NOTE,
30464
30481
  CLAIM_CONSENT_VERSION: () => CLAIM_CONSENT_VERSION,
30482
+ SUBMIT_ACCEPTS: () => SUBMIT_ACCEPTS,
30465
30483
  backgroundEnableFailed: () => backgroundEnableFailed,
30466
30484
  buildAssignmentComment: () => buildAssignmentComment,
30467
30485
  buildPatchSubmission: () => buildPatchSubmission,
@@ -30487,9 +30505,11 @@ __export(jpi_claim_exports, {
30487
30505
  listMergedPRsReferencingIssue: () => listMergedPRsReferencingIssue,
30488
30506
  listOpenPRsReferencingIssue: () => listOpenPRsReferencingIssue,
30489
30507
  matchReferencingPrs: () => matchReferencingPrs,
30508
+ nextStepFor: () => nextStepFor,
30490
30509
  normalizeIntent: () => normalizeIntent,
30491
30510
  pickBodySource: () => pickBodySource,
30492
30511
  pickExistingPr: () => pickExistingPr,
30512
+ printNextSteps: () => printNextSteps,
30493
30513
  renderRunView: () => renderRunView,
30494
30514
  renderServerRefusal: () => renderServerRefusal,
30495
30515
  resolveBounty: () => resolveBounty,
@@ -31166,6 +31186,37 @@ function printMetric(rate) {
31166
31186
  console.log(`
31167
31187
  \u{1F4CA} Accepted-PR rate: ${rate.merged}/${rate.total} claims merged (${pct2}%)`);
31168
31188
  }
31189
+ function nextStepFor(c) {
31190
+ const founder = Boolean(c.approval);
31191
+ switch (c.state) {
31192
+ case "claimed":
31193
+ return founder ? { cmd: `terminalhire claim slice ${c.id}`, why: "collect your granted work slice" } : { cmd: `terminalhire claim start ${c.id}`, why: "fork + clone into a worktree" };
31194
+ // NOT grouped with the two below. `cmdSubmit` accepts 'working' and 'ready'
31195
+ // and nothing else, so pointing an 'in-review' claim at submit would hand the
31196
+ // developer a command that exits 1 — and submit's own refusal then advises
31197
+ // `claim start`, which a founder claim can never use either. Two dead ends in
31198
+ // a row is worse than the silence this block replaced.
31199
+ case "in-review":
31200
+ return {
31201
+ cmd: `terminalhire claim update ${c.id} ready`,
31202
+ why: "clear the review gate \u2014 submit only accepts working/ready"
31203
+ };
31204
+ case "working":
31205
+ case "ready":
31206
+ return { cmd: `terminalhire claim submit ${c.id}`, why: "push the branch + open the PR" };
31207
+ case "submitted":
31208
+ return founder ? { cmd: `terminalhire claim runs ${c.id} --watch`, why: "read the CI result" } : { cmd: `terminalhire claim status ${c.id}`, why: "poll the PR merge state" };
31209
+ default:
31210
+ return null;
31211
+ }
31212
+ }
31213
+ function printNextSteps(list) {
31214
+ const steps = list.map(nextStepFor).filter(Boolean);
31215
+ if (steps.length === 0) return;
31216
+ const width = Math.max(...steps.map((s) => s.cmd.length));
31217
+ console.log("\n Next:");
31218
+ for (const s of steps) console.log(` ${s.cmd.padEnd(width)} \u2014 ${s.why}`);
31219
+ }
31169
31220
  async function resolveBounty(arg) {
31170
31221
  let bountyId, title, repoFullName, issueUrl, amountUSD, source, openPRsAtDiscovery, indexNativeId;
31171
31222
  let founderPosting = false;
@@ -31737,6 +31788,7 @@ ${list.length} ${active ? "active " : ""}claim${list.length === 1 ? "" : "s"}:
31737
31788
  console.log(` id: ${c.id}${pr}`);
31738
31789
  }
31739
31790
  printMetric(claims.acceptedPRRate());
31791
+ printNextSteps(list);
31740
31792
  try {
31741
31793
  if (await shouldNudgeUnpushed()) {
31742
31794
  console.log(
@@ -31884,7 +31936,7 @@ async function cmdStatus(id) {
31884
31936
  }
31885
31937
  async function cmdUpdate(id, state, prUrl) {
31886
31938
  const claims = await Promise.resolve().then(() => (init_claims(), claims_exports));
31887
- const VALID = ["claimed", "working", "in-review", "ready", "submitted", "merged", "abandoned"];
31939
+ const VALID = claims.CLAIM_STATES;
31888
31940
  if (!id || !state || !VALID.includes(state)) {
31889
31941
  console.error("Usage: terminalhire claim update <id> <state> [prUrl]");
31890
31942
  console.error(" state: " + VALID.join(" | "));
@@ -32742,7 +32794,7 @@ async function cmdSubmit(id, flags = {}) {
32742
32794
  console.error(`terminalhire claim: no claim with id '${id}'.`);
32743
32795
  process.exit(1);
32744
32796
  }
32745
- if (claim.state !== "working" && claim.state !== "ready") {
32797
+ if (!SUBMIT_ACCEPTS.includes(claim.state)) {
32746
32798
  console.error(
32747
32799
  `terminalhire claim: ${id} is '${claim.state}'. Submit runs once work has started ('working', via 'claim start') or the review gate cleared it ('ready'). Start it first:
32748
32800
  terminalhire claim start ${id}`
@@ -33691,7 +33743,7 @@ async function run7() {
33691
33743
  process.exit(1);
33692
33744
  }
33693
33745
  }
33694
- var TERMINALHIRE_DIR17, INDEX_CACHE_FILE5, CLAIM_PUSH_MARKER, REPO_CONTINUITY_NUDGE_MARKER, API_URL6, CLAIM_SYNC_BASE2, CLAIM_CONSENT_VERSION, CLAIM_POLL_INTERVAL_MS, CLAIM_POLL_TIMEOUT_MS, GH_API3, GH_HEADERS2, CONTENTION_HINT, AI_DISCLOSURE_NOTE, pExecFile, VALUE_FLAGS, ASSIGNMENT_MARKER, STAKE_MARKER, STANDDOWN_MARKER, OUR_MARKERS, STAKE_POST_TIMEOUT_MS, STAKE_POSTING_GRACE_MS, TAKE_BOT_REPOS, ISSUE_OUTCOME_TERMINAL, RUNS_POLL_INTERVAL_MS, RUNS_POLL_ATTEMPTS;
33746
+ var TERMINALHIRE_DIR17, INDEX_CACHE_FILE5, CLAIM_PUSH_MARKER, REPO_CONTINUITY_NUDGE_MARKER, API_URL6, CLAIM_SYNC_BASE2, CLAIM_CONSENT_VERSION, CLAIM_POLL_INTERVAL_MS, CLAIM_POLL_TIMEOUT_MS, GH_API3, GH_HEADERS2, CONTENTION_HINT, AI_DISCLOSURE_NOTE, pExecFile, VALUE_FLAGS, ASSIGNMENT_MARKER, STAKE_MARKER, STANDDOWN_MARKER, OUR_MARKERS, STAKE_POST_TIMEOUT_MS, STAKE_POSTING_GRACE_MS, TAKE_BOT_REPOS, SUBMIT_ACCEPTS, ISSUE_OUTCOME_TERMINAL, RUNS_POLL_INTERVAL_MS, RUNS_POLL_ATTEMPTS;
33695
33747
  var init_jpi_claim = __esm({
33696
33748
  "bin/jpi-claim.js"() {
33697
33749
  "use strict";
@@ -33723,6 +33775,7 @@ var init_jpi_claim = __esm({
33723
33775
  STAKE_POST_TIMEOUT_MS = 3e4;
33724
33776
  STAKE_POSTING_GRACE_MS = STAKE_POST_TIMEOUT_MS + 15e3;
33725
33777
  TAKE_BOT_REPOS = /* @__PURE__ */ new Set(["paradedb/paradedb"]);
33778
+ SUBMIT_ACCEPTS = Object.freeze(["working", "ready"]);
33726
33779
  ISSUE_OUTCOME_TERMINAL = /* @__PURE__ */ new Set(["own-merge", "other-merge", "closed-unmerged"]);
33727
33780
  RUNS_POLL_INTERVAL_MS = 15e3;
33728
33781
  RUNS_POLL_ATTEMPTS = 20;
@@ -462,6 +462,7 @@ var init_state_dir = __esm({
462
462
  // src/claims.ts
463
463
  var claims_exports = {};
464
464
  __export(claims_exports, {
465
+ CLAIM_STATES: () => CLAIM_STATES,
465
466
  PUSHED_CLAIM_FIELDS: () => PUSHED_CLAIM_FIELDS,
466
467
  acceptedPRRate: () => acceptedPRRate,
467
468
  countAwaitingFounderApproval: () => countAwaitingFounderApproval,
@@ -660,7 +661,7 @@ function acceptedPRRate(claims = readClaims()) {
660
661
  const merged = claims.filter((c) => c.state === "merged").length;
661
662
  return { merged, total, rate: total === 0 ? 0 : merged / total };
662
663
  }
663
- var TERMINALHIRE_DIR, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS;
664
+ var TERMINALHIRE_DIR, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, CLAIM_STATES, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS;
664
665
  var init_claims = __esm({
665
666
  "src/claims.ts"() {
666
667
  "use strict";
@@ -671,6 +672,22 @@ var init_claims = __esm({
671
672
  LOCK_STALE_MS = Number(process.env.TERMINALHIRE_LOCK_STALE_MS) || 1e4;
672
673
  LOCK_RETRY_MS = Number(process.env.TERMINALHIRE_LOCK_RETRY_MS) || 25;
673
674
  LOCK_TIMEOUT_MS = Number(process.env.TERMINALHIRE_LOCK_TIMEOUT_MS) || 5e3;
675
+ CLAIM_STATES = Object.freeze([
676
+ "claimed",
677
+ // recorded, not started
678
+ "working",
679
+ // background executor running / work in progress
680
+ "in-review",
681
+ // gate ran / dev reviewing the diff
682
+ "ready",
683
+ // passed review, cleared to submit
684
+ "submitted",
685
+ // PR opened on the source platform (awaiting maintainer)
686
+ "merged",
687
+ // PR merged — accepted; counts toward the metric
688
+ "abandoned"
689
+ // released, or PR closed unmerged
690
+ ]);
674
691
  PUSHED_CLAIM_FIELDS = [
675
692
  "kind",
676
693
  "repoFullName",
@@ -10903,6 +10903,7 @@ var init_policy_acks = __esm({
10903
10903
  // src/claims.ts
10904
10904
  var claims_exports = {};
10905
10905
  __export(claims_exports, {
10906
+ CLAIM_STATES: () => CLAIM_STATES,
10906
10907
  PUSHED_CLAIM_FIELDS: () => PUSHED_CLAIM_FIELDS,
10907
10908
  acceptedPRRate: () => acceptedPRRate,
10908
10909
  countAwaitingFounderApproval: () => countAwaitingFounderApproval,
@@ -11101,7 +11102,7 @@ function acceptedPRRate(claims = readClaims()) {
11101
11102
  const merged = claims.filter((c) => c.state === "merged").length;
11102
11103
  return { merged, total, rate: total === 0 ? 0 : merged / total };
11103
11104
  }
11104
- var TERMINALHIRE_DIR5, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS;
11105
+ 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;
11105
11106
  var init_claims = __esm({
11106
11107
  "src/claims.ts"() {
11107
11108
  "use strict";
@@ -11112,6 +11113,22 @@ var init_claims = __esm({
11112
11113
  LOCK_STALE_MS = Number(process.env.TERMINALHIRE_LOCK_STALE_MS) || 1e4;
11113
11114
  LOCK_RETRY_MS = Number(process.env.TERMINALHIRE_LOCK_RETRY_MS) || 25;
11114
11115
  LOCK_TIMEOUT_MS = Number(process.env.TERMINALHIRE_LOCK_TIMEOUT_MS) || 5e3;
11116
+ CLAIM_STATES = Object.freeze([
11117
+ "claimed",
11118
+ // recorded, not started
11119
+ "working",
11120
+ // background executor running / work in progress
11121
+ "in-review",
11122
+ // gate ran / dev reviewing the diff
11123
+ "ready",
11124
+ // passed review, cleared to submit
11125
+ "submitted",
11126
+ // PR opened on the source platform (awaiting maintainer)
11127
+ "merged",
11128
+ // PR merged — accepted; counts toward the metric
11129
+ "abandoned"
11130
+ // released, or PR closed unmerged
11131
+ ]);
11115
11132
  PUSHED_CLAIM_FIELDS = [
11116
11133
  "kind",
11117
11134
  "repoFullName",
@@ -26046,6 +26063,7 @@ var jpi_claim_exports = {};
26046
26063
  __export(jpi_claim_exports, {
26047
26064
  AI_DISCLOSURE_NOTE: () => AI_DISCLOSURE_NOTE,
26048
26065
  CLAIM_CONSENT_VERSION: () => CLAIM_CONSENT_VERSION,
26066
+ SUBMIT_ACCEPTS: () => SUBMIT_ACCEPTS,
26049
26067
  backgroundEnableFailed: () => backgroundEnableFailed,
26050
26068
  buildAssignmentComment: () => buildAssignmentComment,
26051
26069
  buildPatchSubmission: () => buildPatchSubmission,
@@ -26071,9 +26089,11 @@ __export(jpi_claim_exports, {
26071
26089
  listMergedPRsReferencingIssue: () => listMergedPRsReferencingIssue,
26072
26090
  listOpenPRsReferencingIssue: () => listOpenPRsReferencingIssue,
26073
26091
  matchReferencingPrs: () => matchReferencingPrs,
26092
+ nextStepFor: () => nextStepFor,
26074
26093
  normalizeIntent: () => normalizeIntent,
26075
26094
  pickBodySource: () => pickBodySource,
26076
26095
  pickExistingPr: () => pickExistingPr,
26096
+ printNextSteps: () => printNextSteps,
26077
26097
  renderRunView: () => renderRunView,
26078
26098
  renderServerRefusal: () => renderServerRefusal,
26079
26099
  resolveBounty: () => resolveBounty,
@@ -26750,6 +26770,37 @@ function printMetric(rate) {
26750
26770
  console.log(`
26751
26771
  \u{1F4CA} Accepted-PR rate: ${rate.merged}/${rate.total} claims merged (${pct}%)`);
26752
26772
  }
26773
+ function nextStepFor(c) {
26774
+ const founder = Boolean(c.approval);
26775
+ switch (c.state) {
26776
+ case "claimed":
26777
+ return founder ? { cmd: `terminalhire claim slice ${c.id}`, why: "collect your granted work slice" } : { cmd: `terminalhire claim start ${c.id}`, why: "fork + clone into a worktree" };
26778
+ // NOT grouped with the two below. `cmdSubmit` accepts 'working' and 'ready'
26779
+ // and nothing else, so pointing an 'in-review' claim at submit would hand the
26780
+ // developer a command that exits 1 — and submit's own refusal then advises
26781
+ // `claim start`, which a founder claim can never use either. Two dead ends in
26782
+ // a row is worse than the silence this block replaced.
26783
+ case "in-review":
26784
+ return {
26785
+ cmd: `terminalhire claim update ${c.id} ready`,
26786
+ why: "clear the review gate \u2014 submit only accepts working/ready"
26787
+ };
26788
+ case "working":
26789
+ case "ready":
26790
+ return { cmd: `terminalhire claim submit ${c.id}`, why: "push the branch + open the PR" };
26791
+ case "submitted":
26792
+ return founder ? { cmd: `terminalhire claim runs ${c.id} --watch`, why: "read the CI result" } : { cmd: `terminalhire claim status ${c.id}`, why: "poll the PR merge state" };
26793
+ default:
26794
+ return null;
26795
+ }
26796
+ }
26797
+ function printNextSteps(list) {
26798
+ const steps = list.map(nextStepFor).filter(Boolean);
26799
+ if (steps.length === 0) return;
26800
+ const width = Math.max(...steps.map((s) => s.cmd.length));
26801
+ console.log("\n Next:");
26802
+ for (const s of steps) console.log(` ${s.cmd.padEnd(width)} \u2014 ${s.why}`);
26803
+ }
26753
26804
  async function resolveBounty(arg) {
26754
26805
  let bountyId, title, repoFullName, issueUrl, amountUSD, source, openPRsAtDiscovery, indexNativeId;
26755
26806
  let founderPosting = false;
@@ -27321,6 +27372,7 @@ ${list.length} ${active ? "active " : ""}claim${list.length === 1 ? "" : "s"}:
27321
27372
  console.log(` id: ${c.id}${pr}`);
27322
27373
  }
27323
27374
  printMetric(claims.acceptedPRRate());
27375
+ printNextSteps(list);
27324
27376
  try {
27325
27377
  if (await shouldNudgeUnpushed()) {
27326
27378
  console.log(
@@ -27468,7 +27520,7 @@ async function cmdStatus(id) {
27468
27520
  }
27469
27521
  async function cmdUpdate(id, state, prUrl) {
27470
27522
  const claims = await Promise.resolve().then(() => (init_claims(), claims_exports));
27471
- const VALID = ["claimed", "working", "in-review", "ready", "submitted", "merged", "abandoned"];
27523
+ const VALID = claims.CLAIM_STATES;
27472
27524
  if (!id || !state || !VALID.includes(state)) {
27473
27525
  console.error("Usage: terminalhire claim update <id> <state> [prUrl]");
27474
27526
  console.error(" state: " + VALID.join(" | "));
@@ -28326,7 +28378,7 @@ async function cmdSubmit(id, flags = {}) {
28326
28378
  console.error(`terminalhire claim: no claim with id '${id}'.`);
28327
28379
  process.exit(1);
28328
28380
  }
28329
- if (claim.state !== "working" && claim.state !== "ready") {
28381
+ if (!SUBMIT_ACCEPTS.includes(claim.state)) {
28330
28382
  console.error(
28331
28383
  `terminalhire claim: ${id} is '${claim.state}'. Submit runs once work has started ('working', via 'claim start') or the review gate cleared it ('ready'). Start it first:
28332
28384
  terminalhire claim start ${id}`
@@ -29275,7 +29327,7 @@ async function run() {
29275
29327
  process.exit(1);
29276
29328
  }
29277
29329
  }
29278
- var TERMINALHIRE_DIR11, INDEX_CACHE_FILE2, CLAIM_PUSH_MARKER, REPO_CONTINUITY_NUDGE_MARKER, API_URL, CLAIM_SYNC_BASE, CLAIM_CONSENT_VERSION, CLAIM_POLL_INTERVAL_MS, CLAIM_POLL_TIMEOUT_MS, GH_API2, GH_HEADERS2, CONTENTION_HINT, AI_DISCLOSURE_NOTE, pExecFile, VALUE_FLAGS, ASSIGNMENT_MARKER, STAKE_MARKER, STANDDOWN_MARKER, OUR_MARKERS, STAKE_POST_TIMEOUT_MS, STAKE_POSTING_GRACE_MS, TAKE_BOT_REPOS, ISSUE_OUTCOME_TERMINAL, RUNS_POLL_INTERVAL_MS, RUNS_POLL_ATTEMPTS;
29330
+ var TERMINALHIRE_DIR11, INDEX_CACHE_FILE2, CLAIM_PUSH_MARKER, REPO_CONTINUITY_NUDGE_MARKER, API_URL, CLAIM_SYNC_BASE, CLAIM_CONSENT_VERSION, CLAIM_POLL_INTERVAL_MS, CLAIM_POLL_TIMEOUT_MS, GH_API2, GH_HEADERS2, CONTENTION_HINT, AI_DISCLOSURE_NOTE, pExecFile, VALUE_FLAGS, ASSIGNMENT_MARKER, STAKE_MARKER, STANDDOWN_MARKER, OUR_MARKERS, STAKE_POST_TIMEOUT_MS, STAKE_POSTING_GRACE_MS, TAKE_BOT_REPOS, SUBMIT_ACCEPTS, ISSUE_OUTCOME_TERMINAL, RUNS_POLL_INTERVAL_MS, RUNS_POLL_ATTEMPTS;
29279
29331
  var init_jpi_claim = __esm({
29280
29332
  "bin/jpi-claim.js"() {
29281
29333
  "use strict";
@@ -29307,6 +29359,7 @@ var init_jpi_claim = __esm({
29307
29359
  STAKE_POST_TIMEOUT_MS = 3e4;
29308
29360
  STAKE_POSTING_GRACE_MS = STAKE_POST_TIMEOUT_MS + 15e3;
29309
29361
  TAKE_BOT_REPOS = /* @__PURE__ */ new Set(["paradedb/paradedb"]);
29362
+ SUBMIT_ACCEPTS = Object.freeze(["working", "ready"]);
29310
29363
  ISSUE_OUTCOME_TERMINAL = /* @__PURE__ */ new Set(["own-merge", "other-merge", "closed-unmerged"]);
29311
29364
  RUNS_POLL_INTERVAL_MS = 15e3;
29312
29365
  RUNS_POLL_ATTEMPTS = 20;
@@ -12283,6 +12283,7 @@ var init_github_auth = __esm({
12283
12283
  // src/claims.ts
12284
12284
  var claims_exports = {};
12285
12285
  __export(claims_exports, {
12286
+ CLAIM_STATES: () => CLAIM_STATES,
12286
12287
  PUSHED_CLAIM_FIELDS: () => PUSHED_CLAIM_FIELDS,
12287
12288
  acceptedPRRate: () => acceptedPRRate,
12288
12289
  countAwaitingFounderApproval: () => countAwaitingFounderApproval,
@@ -12481,7 +12482,7 @@ function acceptedPRRate(claims = readClaims()) {
12481
12482
  const merged = claims.filter((c) => c.state === "merged").length;
12482
12483
  return { merged, total, rate: total === 0 ? 0 : merged / total };
12483
12484
  }
12484
- var TERMINALHIRE_DIR8, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS2, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS;
12485
+ var TERMINALHIRE_DIR8, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS2, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, CLAIM_STATES, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS;
12485
12486
  var init_claims = __esm({
12486
12487
  "src/claims.ts"() {
12487
12488
  "use strict";
@@ -12492,6 +12493,22 @@ var init_claims = __esm({
12492
12493
  LOCK_STALE_MS2 = Number(process.env.TERMINALHIRE_LOCK_STALE_MS) || 1e4;
12493
12494
  LOCK_RETRY_MS = Number(process.env.TERMINALHIRE_LOCK_RETRY_MS) || 25;
12494
12495
  LOCK_TIMEOUT_MS = Number(process.env.TERMINALHIRE_LOCK_TIMEOUT_MS) || 5e3;
12496
+ CLAIM_STATES = Object.freeze([
12497
+ "claimed",
12498
+ // recorded, not started
12499
+ "working",
12500
+ // background executor running / work in progress
12501
+ "in-review",
12502
+ // gate ran / dev reviewing the diff
12503
+ "ready",
12504
+ // passed review, cleared to submit
12505
+ "submitted",
12506
+ // PR opened on the source platform (awaiting maintainer)
12507
+ "merged",
12508
+ // PR merged — accepted; counts toward the metric
12509
+ "abandoned"
12510
+ // released, or PR closed unmerged
12511
+ ]);
12495
12512
  PUSHED_CLAIM_FIELDS = [
12496
12513
  "kind",
12497
12514
  "repoFullName",
@@ -1824,6 +1824,7 @@ var init_repo_experience = __esm({
1824
1824
  // src/claims.ts
1825
1825
  var claims_exports = {};
1826
1826
  __export(claims_exports, {
1827
+ CLAIM_STATES: () => CLAIM_STATES,
1827
1828
  PUSHED_CLAIM_FIELDS: () => PUSHED_CLAIM_FIELDS,
1828
1829
  acceptedPRRate: () => acceptedPRRate,
1829
1830
  countAwaitingFounderApproval: () => countAwaitingFounderApproval,
@@ -2022,7 +2023,7 @@ function acceptedPRRate(claims = readClaims()) {
2022
2023
  const merged = claims.filter((c) => c.state === "merged").length;
2023
2024
  return { merged, total, rate: total === 0 ? 0 : merged / total };
2024
2025
  }
2025
- var TERMINALHIRE_DIR4, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS;
2026
+ var TERMINALHIRE_DIR4, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, CLAIM_STATES, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS;
2026
2027
  var init_claims = __esm({
2027
2028
  "src/claims.ts"() {
2028
2029
  "use strict";
@@ -2033,6 +2034,22 @@ var init_claims = __esm({
2033
2034
  LOCK_STALE_MS = Number(process.env.TERMINALHIRE_LOCK_STALE_MS) || 1e4;
2034
2035
  LOCK_RETRY_MS = Number(process.env.TERMINALHIRE_LOCK_RETRY_MS) || 25;
2035
2036
  LOCK_TIMEOUT_MS = Number(process.env.TERMINALHIRE_LOCK_TIMEOUT_MS) || 5e3;
2037
+ CLAIM_STATES = Object.freeze([
2038
+ "claimed",
2039
+ // recorded, not started
2040
+ "working",
2041
+ // background executor running / work in progress
2042
+ "in-review",
2043
+ // gate ran / dev reviewing the diff
2044
+ "ready",
2045
+ // passed review, cleared to submit
2046
+ "submitted",
2047
+ // PR opened on the source platform (awaiting maintainer)
2048
+ "merged",
2049
+ // PR merged — accepted; counts toward the metric
2050
+ "abandoned"
2051
+ // released, or PR closed unmerged
2052
+ ]);
2036
2053
  PUSHED_CLAIM_FIELDS = [
2037
2054
  "kind",
2038
2055
  "repoFullName",
@@ -99,6 +99,22 @@ function withClaimsLock(fn) {
99
99
  rmSync(LOCK_DIR, { recursive: true, force: true });
100
100
  }
101
101
  }
102
+ var CLAIM_STATES = Object.freeze([
103
+ "claimed",
104
+ // recorded, not started
105
+ "working",
106
+ // background executor running / work in progress
107
+ "in-review",
108
+ // gate ran / dev reviewing the diff
109
+ "ready",
110
+ // passed review, cleared to submit
111
+ "submitted",
112
+ // PR opened on the source platform (awaiting maintainer)
113
+ "merged",
114
+ // PR merged — accepted; counts toward the metric
115
+ "abandoned"
116
+ // released, or PR closed unmerged
117
+ ]);
102
118
  var PUSHED_CLAIM_FIELDS = [
103
119
  "kind",
104
120
  "repoFullName",
@@ -269,6 +285,7 @@ function acceptedPRRate(claims = readClaims()) {
269
285
  return { merged, total, rate: total === 0 ? 0 : merged / total };
270
286
  }
271
287
  export {
288
+ CLAIM_STATES,
272
289
  PUSHED_CLAIM_FIELDS,
273
290
  acceptedPRRate,
274
291
  countAwaitingFounderApproval,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminalhire",
3
- "version": "0.40.3",
3
+ "version": "0.40.4",
4
4
  "description": "Local-first job matching for developers — ambient job matches in the Claude Code spinner. Your profile never leaves your machine.",
5
5
  "repository": {
6
6
  "type": "git",