terminalhire 0.42.14 → 0.42.15

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.
@@ -28054,6 +28054,9 @@ function labelSelector(labels) {
28054
28054
  throw new EnvRunError("labels object is empty; pass at least one label or omit it");
28055
28055
  return `${first[0]}=${first[1]}`;
28056
28056
  }
28057
+ function withUserScriptPath(command) {
28058
+ return `PATH="$PATH:$HOME/.local/bin"; export PATH; ${command}`;
28059
+ }
28057
28060
  async function runStep(containment, r) {
28058
28061
  const spec = {
28059
28062
  profile: r.profile,
@@ -28068,7 +28071,7 @@ async function runStep(containment, r) {
28068
28071
  pathDomain: r.pathDomain,
28069
28072
  guestUser: r.guestUser,
28070
28073
  program: "/bin/sh",
28071
- args: ["-c", r.command]
28074
+ args: ["-c", withUserScriptPath(r.command)]
28072
28075
  };
28073
28076
  const startedAt = Date.now();
28074
28077
  const res = await containment.run(spec, r.env, {
@@ -35295,6 +35298,42 @@ function printNextSteps(list) {
35295
35298
  console.log(` ${s.cmd.padEnd(width)} \u2014 ${s.why}`);
35296
35299
  }
35297
35300
  }
35301
+ async function explainUnresolvable(arg) {
35302
+ const generic = [
35303
+ `terminalhire claim: '${arg}' is not in the index cache and is not a GitHub issue URL.`,
35304
+ " Run `terminalhire bounties` to populate the cache, or pass a full issue URL."
35305
+ ];
35306
+ if (!looksLikeShortRef(arg)) return generic;
35307
+ try {
35308
+ const { readClaims: readClaims2 } = await Promise.resolve().then(() => (init_claims(), claims_exports));
35309
+ const mine = readClaims2().find((c) => c?.id && opportunityShortToken(c.id) === arg);
35310
+ if (mine) {
35311
+ return [
35312
+ `terminalhire claim: you have already claimed '${arg}'.`,
35313
+ ` ${mine.title ?? mine.id}`,
35314
+ " A claimed posting leaves the public index, so it cannot be claimed again.",
35315
+ "",
35316
+ ` Get your workspace: terminalhire claim start ${mine.id}`,
35317
+ ` See where it stands: terminalhire claim status ${mine.id}`
35318
+ ];
35319
+ }
35320
+ } catch {
35321
+ }
35322
+ return [
35323
+ `terminalhire claim: '${arg}' did not resolve to anything you can claim.`,
35324
+ " It has the shape of a claim token, so one of two things is true, and this",
35325
+ " command cannot tell them apart:",
35326
+ "",
35327
+ " \u2022 the posting was claimed or withdrawn. A claimed posting leaves the",
35328
+ " public index, and `terminalhire bounties` will never bring it back.",
35329
+ " \u2022 the token is unknown here \u2014 mistyped, or minted against a different",
35330
+ " environment than the one this command is pointed at.",
35331
+ "",
35332
+ ` Open it: ${API_URL}/c/${arg}`,
35333
+ " Already yours? terminalhire claim list",
35334
+ " Refresh the index: terminalhire bounties"
35335
+ ];
35336
+ }
35298
35337
  async function resolveBounty(arg) {
35299
35338
  let bountyId, title, repoFullName, issueUrl, amountUSD, source, openPRsAtDiscovery, indexNativeId;
35300
35339
  let founderPosting = false;
@@ -35774,10 +35813,7 @@ async function cmdRecord(arg, flags = {}) {
35774
35813
  }
35775
35814
  const b = await resolveBounty(arg);
35776
35815
  if (!b) {
35777
- console.error(
35778
- `terminalhire claim: '${arg}' is not in the index cache and is not a GitHub issue URL.`
35779
- );
35780
- console.error(" Run `terminalhire bounties` to populate the cache, or pass a full issue URL.");
35816
+ for (const line of await explainUnresolvable(arg)) console.error(line);
35781
35817
  process.exit(1);
35782
35818
  }
35783
35819
  if (b.issueState === "closed") {
@@ -36024,7 +36060,7 @@ terminalhire claim: refusing to record \u2014 read ${b.repoFullName}'s contribut
36024
36060
  );
36025
36061
  console.log(" \u2022 no access to ~/.terminalhire (the executor never needs your profile)");
36026
36062
  console.log(
36027
- "\n Next \u2014 start work (forks + clones into an isolated worktree; your terminal stays put):"
36063
+ "\n Next \u2014 start work (forks + clones into an isolated worktree, then drops you in it):"
36028
36064
  );
36029
36065
  console.log(" terminalhire claim start " + claim.id);
36030
36066
  console.log(" Then publish when it is done (the only step that pushes + opens the PR):");
@@ -36047,10 +36083,7 @@ async function cmdPreview(arg, { json } = {}) {
36047
36083
  }
36048
36084
  const b = await resolveBounty(arg);
36049
36085
  if (!b) {
36050
- console.error(
36051
- `terminalhire claim: '${arg}' is not in the index cache and is not a GitHub issue URL.`
36052
- );
36053
- console.error(" Run `terminalhire bounties` to populate the cache, or pass a full issue URL.");
36086
+ for (const line of await explainUnresolvable(arg)) console.error(line);
36054
36087
  process.exit(1);
36055
36088
  }
36056
36089
  let policy = null;
@@ -36688,6 +36721,13 @@ async function cmdStart(id, flags = {}) {
36688
36721
  ({ id } = picked);
36689
36722
  }
36690
36723
  let claim = claims.findClaim(id);
36724
+ if (!claim) {
36725
+ const held = heldClaimByShortRef(claims.readClaims(), id);
36726
+ if (held) {
36727
+ claim = held;
36728
+ id = held.id;
36729
+ }
36730
+ }
36691
36731
  let chainedFromRecord = false;
36692
36732
  if (!claim) {
36693
36733
  const b = await resolveBounty(id);
@@ -36727,12 +36767,13 @@ async function cmdStart(id, flags = {}) {
36727
36767
  stillThere = false;
36728
36768
  }
36729
36769
  if (stillThere) {
36730
- console.log("Already started \u2014 your worktree is ready (your terminal stays put):");
36731
- console.log(` cd ${claim.worktreePath}`);
36770
+ console.log("Already started \u2014 your worktree is ready.");
36732
36771
  if (claim.branch) console.log(` branch: ${claim.branch}`);
36733
36772
  console.log(`
36734
- When it's done: terminalhire claim submit ${id}`);
36773
+ Commit as you go \u2014 your commits ARE the patch. Hand it back with:`);
36774
+ console.log(` terminalhire claim submit ${id}`);
36735
36775
  await beatFounderPresence(claim);
36776
+ landDeveloperIn(claim.worktreePath, flags);
36736
36777
  return;
36737
36778
  }
36738
36779
  }
@@ -36753,8 +36794,7 @@ ${sanitizeText(claim.title)}`);
36753
36794
  }
36754
36795
  }
36755
36796
  if (outcome.outcome === "delivered") {
36756
- const workspace = outcome.claim?.worktreePath;
36757
- if (flags.open && workspace) launchAgentIn(workspace, flags.open);
36797
+ landDeveloperIn(outcome.claim?.worktreePath, flags);
36758
36798
  return;
36759
36799
  }
36760
36800
  if (outcome.outcome === "pending") {
@@ -36874,10 +36914,9 @@ terminalhire claim: not started \u2014 starting forks ${claim.repoFullName} to y
36874
36914
  \u2713 Started: ${claim.title}`);
36875
36915
  console.log(` fork: ${forkFullName}`);
36876
36916
  console.log(` branch: ${branch}`);
36877
- console.log("\n Your isolated worktree is ready \u2014 your current terminal is untouched:");
36878
- console.log(` cd ${toplevel}`);
36879
- console.log("\n When the work is done (the only step that pushes + opens the PR):");
36917
+ console.log("\n Commit as you go. When it is done (the only step that pushes + opens the PR):");
36880
36918
  console.log(` terminalhire claim submit ${id}`);
36919
+ landDeveloperIn(toplevel, flags);
36881
36920
  }
36882
36921
  async function cmdStartHere(claims, claim, flags = {}) {
36883
36922
  let toplevel;
@@ -36998,6 +37037,45 @@ function launchAgentIn(dest, agentName, { spawnFn, log = console.log } = {}) {
36998
37037
  }
36999
37038
  return { launched: true, reason: null };
37000
37039
  }
37040
+ function heldClaimByShortRef(allClaims, arg) {
37041
+ if (!looksLikeShortRef(arg)) return null;
37042
+ if (!Array.isArray(allClaims)) return null;
37043
+ return allClaims.find((c) => c?.id && opportunityShortToken(c.id) === arg) ?? null;
37044
+ }
37045
+ function landDeveloperIn(dest, flags = {}, deps = {}) {
37046
+ const { spawnFn, log = console.log, isTTY, shellPath } = deps;
37047
+ if (!dest) return { landed: false, reason: "no-workspace" };
37048
+ const cdLine = () => log(`
37049
+ cd ${dest}`);
37050
+ if (flags.open) {
37051
+ const result2 = launchAgentIn(dest, flags.open, { spawnFn, log });
37052
+ return { landed: result2.launched, reason: result2.reason };
37053
+ }
37054
+ if (flags.stay) {
37055
+ cdLine();
37056
+ return { landed: false, reason: "declined" };
37057
+ }
37058
+ const tty = isTTY ?? Boolean(process.stdout?.isTTY);
37059
+ if (!tty) {
37060
+ cdLine();
37061
+ return { landed: false, reason: "not-a-tty" };
37062
+ }
37063
+ const shell = Object.hasOwn(deps, "shellPath") ? shellPath : process.env.SHELL;
37064
+ if (typeof shell !== "string" || !shell.startsWith("/")) {
37065
+ cdLine();
37066
+ return { landed: false, reason: "no-shell" };
37067
+ }
37068
+ log(`
37069
+ You're in the workspace now \u2014 \`exit\` brings you back here.`);
37070
+ const spawn6 = spawnFn ?? spawnSync8;
37071
+ const result = spawn6(shell, [], { cwd: dest, stdio: "inherit", shell: false });
37072
+ if (result?.error) {
37073
+ log(` ${shell} would not start \u2014 the workspace is ready anyway.`);
37074
+ cdLine();
37075
+ return { landed: false, reason: "shell-failed" };
37076
+ }
37077
+ return { landed: true, reason: null };
37078
+ }
37001
37079
  function renderServerRefusal(status, body) {
37002
37080
  const code = body && typeof body.error === "string" ? body.error : null;
37003
37081
  const prose = body && typeof body.message === "string" ? body.message : null;
@@ -37545,11 +37623,9 @@ async function cmdSliceFullTier(claims, id, local, fullTierBody, flags, cloneRep
37545
37623
  printWorkspacePack(pack);
37546
37624
  console.log(
37547
37625
  `
37548
- Author your change there (commit as you go), then: terminalhire claim submit ${claim.id}`
37626
+ Commit as you go \u2014 your commits ARE the patch. Hand it back with:
37627
+ terminalhire claim submit ${claim.id}`
37549
37628
  );
37550
- if (pack.agents.written === true) {
37551
- console.log(` Working with an agent? Point it at ${AGENTS_REL_PATH} first.`);
37552
- }
37553
37629
  await beatFounderPresence(working ?? claim);
37554
37630
  }
37555
37631
  async function cmdSlice(id, flags = {}) {
@@ -37559,8 +37635,7 @@ async function cmdSlice(id, flags = {}) {
37559
37635
  }
37560
37636
  const outcome = await attemptSliceDelivery(id, flags);
37561
37637
  exitOnSliceOutcome(outcome);
37562
- const workspace = outcome.claim?.worktreePath;
37563
- if (flags.open && workspace) launchAgentIn(workspace, flags.open);
37638
+ landDeveloperIn(outcome.claim?.worktreePath, flags);
37564
37639
  }
37565
37640
  function exitOnSliceOutcome(outcome) {
37566
37641
  if (outcome.outcome === "delivered") return;
@@ -37754,11 +37829,9 @@ async function attemptSliceDelivery(id, flags = {}) {
37754
37829
  printWorkspacePack(pack);
37755
37830
  console.log(
37756
37831
  `
37757
- Author your change there (commit as you go), then: terminalhire claim submit ${claim.id}`
37832
+ Commit as you go \u2014 your commits ARE the patch. Hand it back with:
37833
+ terminalhire claim submit ${claim.id}`
37758
37834
  );
37759
- if (pack.agents.written === true) {
37760
- console.log(` Working with an agent? Point it at ${AGENTS_REL_PATH} first.`);
37761
- }
37762
37835
  await beatFounderPresence(working ?? claim);
37763
37836
  return { outcome: "delivered", claim: working ?? claim };
37764
37837
  }
@@ -39094,6 +39167,7 @@ export {
39094
39167
  cmdSubmit,
39095
39168
  countOpenPRsReferencingIssue,
39096
39169
  diffContention,
39170
+ explainUnresolvable,
39097
39171
  explicitForkConsent,
39098
39172
  fetchFounderApprovals,
39099
39173
  findClaimableByShortRef,
@@ -39102,11 +39176,13 @@ export {
39102
39176
  fmtContestedWarning,
39103
39177
  founderClaimStanding,
39104
39178
  founderPostingIdOf,
39179
+ heldClaimByShortRef,
39105
39180
  inferSubmitClaim,
39106
39181
  isContested,
39107
39182
  isStrayArgShortRefClaim,
39108
39183
  isTerminalRunStatus,
39109
39184
  isVerblessShortRefClaim,
39185
+ landDeveloperIn,
39110
39186
  launchAgentIn,
39111
39187
  listMergedPRsReferencingIssue,
39112
39188
  listOpenPRsReferencingIssue,
@@ -33631,6 +33631,9 @@ function labelSelector(labels) {
33631
33631
  throw new EnvRunError("labels object is empty; pass at least one label or omit it");
33632
33632
  return `${first[0]}=${first[1]}`;
33633
33633
  }
33634
+ function withUserScriptPath(command) {
33635
+ return `PATH="$PATH:$HOME/.local/bin"; export PATH; ${command}`;
33636
+ }
33634
33637
  async function runStep(containment, r) {
33635
33638
  const spec = {
33636
33639
  profile: r.profile,
@@ -33645,7 +33648,7 @@ async function runStep(containment, r) {
33645
33648
  pathDomain: r.pathDomain,
33646
33649
  guestUser: r.guestUser,
33647
33650
  program: "/bin/sh",
33648
- args: ["-c", r.command]
33651
+ args: ["-c", withUserScriptPath(r.command)]
33649
33652
  };
33650
33653
  const startedAt = Date.now();
33651
33654
  const res = await containment.run(spec, r.env, {
@@ -39678,6 +39681,7 @@ __export(jpi_claim_exports, {
39678
39681
  cmdSubmit: () => cmdSubmit,
39679
39682
  countOpenPRsReferencingIssue: () => countOpenPRsReferencingIssue,
39680
39683
  diffContention: () => diffContention,
39684
+ explainUnresolvable: () => explainUnresolvable,
39681
39685
  explicitForkConsent: () => explicitForkConsent,
39682
39686
  fetchFounderApprovals: () => fetchFounderApprovals,
39683
39687
  findClaimableByShortRef: () => findClaimableByShortRef,
@@ -39686,11 +39690,13 @@ __export(jpi_claim_exports, {
39686
39690
  fmtContestedWarning: () => fmtContestedWarning,
39687
39691
  founderClaimStanding: () => founderClaimStanding,
39688
39692
  founderPostingIdOf: () => founderPostingIdOf,
39693
+ heldClaimByShortRef: () => heldClaimByShortRef,
39689
39694
  inferSubmitClaim: () => inferSubmitClaim,
39690
39695
  isContested: () => isContested,
39691
39696
  isStrayArgShortRefClaim: () => isStrayArgShortRefClaim,
39692
39697
  isTerminalRunStatus: () => isTerminalRunStatus,
39693
39698
  isVerblessShortRefClaim: () => isVerblessShortRefClaim,
39699
+ landDeveloperIn: () => landDeveloperIn,
39694
39700
  launchAgentIn: () => launchAgentIn,
39695
39701
  listMergedPRsReferencingIssue: () => listMergedPRsReferencingIssue,
39696
39702
  listOpenPRsReferencingIssue: () => listOpenPRsReferencingIssue,
@@ -40502,6 +40508,42 @@ function printNextSteps(list) {
40502
40508
  console.log(` ${s.cmd.padEnd(width)} \u2014 ${s.why}`);
40503
40509
  }
40504
40510
  }
40511
+ async function explainUnresolvable(arg) {
40512
+ const generic = [
40513
+ `terminalhire claim: '${arg}' is not in the index cache and is not a GitHub issue URL.`,
40514
+ " Run `terminalhire bounties` to populate the cache, or pass a full issue URL."
40515
+ ];
40516
+ if (!looksLikeShortRef(arg)) return generic;
40517
+ try {
40518
+ const { readClaims: readClaims2 } = await Promise.resolve().then(() => (init_claims(), claims_exports));
40519
+ const mine = readClaims2().find((c) => c?.id && opportunityShortToken(c.id) === arg);
40520
+ if (mine) {
40521
+ return [
40522
+ `terminalhire claim: you have already claimed '${arg}'.`,
40523
+ ` ${mine.title ?? mine.id}`,
40524
+ " A claimed posting leaves the public index, so it cannot be claimed again.",
40525
+ "",
40526
+ ` Get your workspace: terminalhire claim start ${mine.id}`,
40527
+ ` See where it stands: terminalhire claim status ${mine.id}`
40528
+ ];
40529
+ }
40530
+ } catch {
40531
+ }
40532
+ return [
40533
+ `terminalhire claim: '${arg}' did not resolve to anything you can claim.`,
40534
+ " It has the shape of a claim token, so one of two things is true, and this",
40535
+ " command cannot tell them apart:",
40536
+ "",
40537
+ " \u2022 the posting was claimed or withdrawn. A claimed posting leaves the",
40538
+ " public index, and `terminalhire bounties` will never bring it back.",
40539
+ " \u2022 the token is unknown here \u2014 mistyped, or minted against a different",
40540
+ " environment than the one this command is pointed at.",
40541
+ "",
40542
+ ` Open it: ${API_URL7}/c/${arg}`,
40543
+ " Already yours? terminalhire claim list",
40544
+ " Refresh the index: terminalhire bounties"
40545
+ ];
40546
+ }
40505
40547
  async function resolveBounty(arg) {
40506
40548
  let bountyId, title, repoFullName, issueUrl, amountUSD, source, openPRsAtDiscovery, indexNativeId;
40507
40549
  let founderPosting = false;
@@ -40956,10 +40998,7 @@ async function cmdRecord(arg, flags = {}) {
40956
40998
  }
40957
40999
  const b = await resolveBounty(arg);
40958
41000
  if (!b) {
40959
- console.error(
40960
- `terminalhire claim: '${arg}' is not in the index cache and is not a GitHub issue URL.`
40961
- );
40962
- console.error(" Run `terminalhire bounties` to populate the cache, or pass a full issue URL.");
41001
+ for (const line of await explainUnresolvable(arg)) console.error(line);
40963
41002
  process.exit(1);
40964
41003
  }
40965
41004
  if (b.issueState === "closed") {
@@ -41206,7 +41245,7 @@ terminalhire claim: refusing to record \u2014 read ${b.repoFullName}'s contribut
41206
41245
  );
41207
41246
  console.log(" \u2022 no access to ~/.terminalhire (the executor never needs your profile)");
41208
41247
  console.log(
41209
- "\n Next \u2014 start work (forks + clones into an isolated worktree; your terminal stays put):"
41248
+ "\n Next \u2014 start work (forks + clones into an isolated worktree, then drops you in it):"
41210
41249
  );
41211
41250
  console.log(" terminalhire claim start " + claim.id);
41212
41251
  console.log(" Then publish when it is done (the only step that pushes + opens the PR):");
@@ -41229,10 +41268,7 @@ async function cmdPreview(arg, { json } = {}) {
41229
41268
  }
41230
41269
  const b = await resolveBounty(arg);
41231
41270
  if (!b) {
41232
- console.error(
41233
- `terminalhire claim: '${arg}' is not in the index cache and is not a GitHub issue URL.`
41234
- );
41235
- console.error(" Run `terminalhire bounties` to populate the cache, or pass a full issue URL.");
41271
+ for (const line of await explainUnresolvable(arg)) console.error(line);
41236
41272
  process.exit(1);
41237
41273
  }
41238
41274
  let policy = null;
@@ -41869,6 +41905,13 @@ async function cmdStart(id, flags = {}) {
41869
41905
  ({ id } = picked);
41870
41906
  }
41871
41907
  let claim = claims.findClaim(id);
41908
+ if (!claim) {
41909
+ const held = heldClaimByShortRef(claims.readClaims(), id);
41910
+ if (held) {
41911
+ claim = held;
41912
+ id = held.id;
41913
+ }
41914
+ }
41872
41915
  let chainedFromRecord = false;
41873
41916
  if (!claim) {
41874
41917
  const b = await resolveBounty(id);
@@ -41908,12 +41951,13 @@ async function cmdStart(id, flags = {}) {
41908
41951
  stillThere = false;
41909
41952
  }
41910
41953
  if (stillThere) {
41911
- console.log("Already started \u2014 your worktree is ready (your terminal stays put):");
41912
- console.log(` cd ${claim.worktreePath}`);
41954
+ console.log("Already started \u2014 your worktree is ready.");
41913
41955
  if (claim.branch) console.log(` branch: ${claim.branch}`);
41914
41956
  console.log(`
41915
- When it's done: terminalhire claim submit ${id}`);
41957
+ Commit as you go \u2014 your commits ARE the patch. Hand it back with:`);
41958
+ console.log(` terminalhire claim submit ${id}`);
41916
41959
  await beatFounderPresence(claim);
41960
+ landDeveloperIn(claim.worktreePath, flags);
41917
41961
  return;
41918
41962
  }
41919
41963
  }
@@ -41934,8 +41978,7 @@ ${sanitizeText(claim.title)}`);
41934
41978
  }
41935
41979
  }
41936
41980
  if (outcome.outcome === "delivered") {
41937
- const workspace = outcome.claim?.worktreePath;
41938
- if (flags.open && workspace) launchAgentIn(workspace, flags.open);
41981
+ landDeveloperIn(outcome.claim?.worktreePath, flags);
41939
41982
  return;
41940
41983
  }
41941
41984
  if (outcome.outcome === "pending") {
@@ -42055,10 +42098,9 @@ terminalhire claim: not started \u2014 starting forks ${claim.repoFullName} to y
42055
42098
  \u2713 Started: ${claim.title}`);
42056
42099
  console.log(` fork: ${forkFullName}`);
42057
42100
  console.log(` branch: ${branch}`);
42058
- console.log("\n Your isolated worktree is ready \u2014 your current terminal is untouched:");
42059
- console.log(` cd ${toplevel}`);
42060
- console.log("\n When the work is done (the only step that pushes + opens the PR):");
42101
+ console.log("\n Commit as you go. When it is done (the only step that pushes + opens the PR):");
42061
42102
  console.log(` terminalhire claim submit ${id}`);
42103
+ landDeveloperIn(toplevel, flags);
42062
42104
  }
42063
42105
  async function cmdStartHere(claims, claim, flags = {}) {
42064
42106
  let toplevel;
@@ -42169,6 +42211,45 @@ function launchAgentIn(dest, agentName, { spawnFn, log = console.log } = {}) {
42169
42211
  }
42170
42212
  return { launched: true, reason: null };
42171
42213
  }
42214
+ function heldClaimByShortRef(allClaims, arg) {
42215
+ if (!looksLikeShortRef(arg)) return null;
42216
+ if (!Array.isArray(allClaims)) return null;
42217
+ return allClaims.find((c) => c?.id && opportunityShortToken(c.id) === arg) ?? null;
42218
+ }
42219
+ function landDeveloperIn(dest, flags = {}, deps = {}) {
42220
+ const { spawnFn, log = console.log, isTTY, shellPath } = deps;
42221
+ if (!dest) return { landed: false, reason: "no-workspace" };
42222
+ const cdLine = () => log(`
42223
+ cd ${dest}`);
42224
+ if (flags.open) {
42225
+ const result2 = launchAgentIn(dest, flags.open, { spawnFn, log });
42226
+ return { landed: result2.launched, reason: result2.reason };
42227
+ }
42228
+ if (flags.stay) {
42229
+ cdLine();
42230
+ return { landed: false, reason: "declined" };
42231
+ }
42232
+ const tty = isTTY ?? Boolean(process.stdout?.isTTY);
42233
+ if (!tty) {
42234
+ cdLine();
42235
+ return { landed: false, reason: "not-a-tty" };
42236
+ }
42237
+ const shell = Object.hasOwn(deps, "shellPath") ? shellPath : process.env.SHELL;
42238
+ if (typeof shell !== "string" || !shell.startsWith("/")) {
42239
+ cdLine();
42240
+ return { landed: false, reason: "no-shell" };
42241
+ }
42242
+ log(`
42243
+ You're in the workspace now \u2014 \`exit\` brings you back here.`);
42244
+ const spawn8 = spawnFn ?? spawnSync9;
42245
+ const result = spawn8(shell, [], { cwd: dest, stdio: "inherit", shell: false });
42246
+ if (result?.error) {
42247
+ log(` ${shell} would not start \u2014 the workspace is ready anyway.`);
42248
+ cdLine();
42249
+ return { landed: false, reason: "shell-failed" };
42250
+ }
42251
+ return { landed: true, reason: null };
42252
+ }
42172
42253
  function renderServerRefusal(status, body) {
42173
42254
  const code = body && typeof body.error === "string" ? body.error : null;
42174
42255
  const prose = body && typeof body.message === "string" ? body.message : null;
@@ -42697,11 +42778,9 @@ async function cmdSliceFullTier(claims, id, local, fullTierBody, flags, cloneRep
42697
42778
  printWorkspacePack(pack);
42698
42779
  console.log(
42699
42780
  `
42700
- Author your change there (commit as you go), then: terminalhire claim submit ${claim.id}`
42781
+ Commit as you go \u2014 your commits ARE the patch. Hand it back with:
42782
+ terminalhire claim submit ${claim.id}`
42701
42783
  );
42702
- if (pack.agents.written === true) {
42703
- console.log(` Working with an agent? Point it at ${AGENTS_REL_PATH} first.`);
42704
- }
42705
42784
  await beatFounderPresence(working ?? claim);
42706
42785
  }
42707
42786
  async function cmdSlice(id, flags = {}) {
@@ -42711,8 +42790,7 @@ async function cmdSlice(id, flags = {}) {
42711
42790
  }
42712
42791
  const outcome = await attemptSliceDelivery(id, flags);
42713
42792
  exitOnSliceOutcome(outcome);
42714
- const workspace = outcome.claim?.worktreePath;
42715
- if (flags.open && workspace) launchAgentIn(workspace, flags.open);
42793
+ landDeveloperIn(outcome.claim?.worktreePath, flags);
42716
42794
  }
42717
42795
  function exitOnSliceOutcome(outcome) {
42718
42796
  if (outcome.outcome === "delivered") return;
@@ -42906,11 +42984,9 @@ async function attemptSliceDelivery(id, flags = {}) {
42906
42984
  printWorkspacePack(pack);
42907
42985
  console.log(
42908
42986
  `
42909
- Author your change there (commit as you go), then: terminalhire claim submit ${claim.id}`
42987
+ Commit as you go \u2014 your commits ARE the patch. Hand it back with:
42988
+ terminalhire claim submit ${claim.id}`
42910
42989
  );
42911
- if (pack.agents.written === true) {
42912
- console.log(` Working with an agent? Point it at ${AGENTS_REL_PATH} first.`);
42913
- }
42914
42990
  await beatFounderPresence(working ?? claim);
42915
42991
  return { outcome: "delivered", claim: working ?? claim };
42916
42992
  }
@@ -28508,6 +28508,9 @@ function labelSelector(labels) {
28508
28508
  throw new EnvRunError("labels object is empty; pass at least one label or omit it");
28509
28509
  return `${first[0]}=${first[1]}`;
28510
28510
  }
28511
+ function withUserScriptPath(command) {
28512
+ return `PATH="$PATH:$HOME/.local/bin"; export PATH; ${command}`;
28513
+ }
28511
28514
  async function runStep(containment, r) {
28512
28515
  const spec = {
28513
28516
  profile: r.profile,
@@ -28522,7 +28525,7 @@ async function runStep(containment, r) {
28522
28525
  pathDomain: r.pathDomain,
28523
28526
  guestUser: r.guestUser,
28524
28527
  program: "/bin/sh",
28525
- args: ["-c", r.command]
28528
+ args: ["-c", withUserScriptPath(r.command)]
28526
28529
  };
28527
28530
  const startedAt = Date.now();
28528
28531
  const res = await containment.run(spec, r.env, {
@@ -34555,6 +34558,7 @@ __export(jpi_claim_exports, {
34555
34558
  cmdSubmit: () => cmdSubmit,
34556
34559
  countOpenPRsReferencingIssue: () => countOpenPRsReferencingIssue,
34557
34560
  diffContention: () => diffContention,
34561
+ explainUnresolvable: () => explainUnresolvable,
34558
34562
  explicitForkConsent: () => explicitForkConsent,
34559
34563
  fetchFounderApprovals: () => fetchFounderApprovals,
34560
34564
  findClaimableByShortRef: () => findClaimableByShortRef,
@@ -34563,11 +34567,13 @@ __export(jpi_claim_exports, {
34563
34567
  fmtContestedWarning: () => fmtContestedWarning,
34564
34568
  founderClaimStanding: () => founderClaimStanding,
34565
34569
  founderPostingIdOf: () => founderPostingIdOf,
34570
+ heldClaimByShortRef: () => heldClaimByShortRef,
34566
34571
  inferSubmitClaim: () => inferSubmitClaim,
34567
34572
  isContested: () => isContested,
34568
34573
  isStrayArgShortRefClaim: () => isStrayArgShortRefClaim,
34569
34574
  isTerminalRunStatus: () => isTerminalRunStatus,
34570
34575
  isVerblessShortRefClaim: () => isVerblessShortRefClaim,
34576
+ landDeveloperIn: () => landDeveloperIn,
34571
34577
  launchAgentIn: () => launchAgentIn,
34572
34578
  listMergedPRsReferencingIssue: () => listMergedPRsReferencingIssue,
34573
34579
  listOpenPRsReferencingIssue: () => listOpenPRsReferencingIssue,
@@ -35379,6 +35385,42 @@ function printNextSteps(list) {
35379
35385
  console.log(` ${s.cmd.padEnd(width)} \u2014 ${s.why}`);
35380
35386
  }
35381
35387
  }
35388
+ async function explainUnresolvable(arg) {
35389
+ const generic = [
35390
+ `terminalhire claim: '${arg}' is not in the index cache and is not a GitHub issue URL.`,
35391
+ " Run `terminalhire bounties` to populate the cache, or pass a full issue URL."
35392
+ ];
35393
+ if (!looksLikeShortRef(arg)) return generic;
35394
+ try {
35395
+ const { readClaims: readClaims2 } = await Promise.resolve().then(() => (init_claims(), claims_exports));
35396
+ const mine = readClaims2().find((c) => c?.id && opportunityShortToken(c.id) === arg);
35397
+ if (mine) {
35398
+ return [
35399
+ `terminalhire claim: you have already claimed '${arg}'.`,
35400
+ ` ${mine.title ?? mine.id}`,
35401
+ " A claimed posting leaves the public index, so it cannot be claimed again.",
35402
+ "",
35403
+ ` Get your workspace: terminalhire claim start ${mine.id}`,
35404
+ ` See where it stands: terminalhire claim status ${mine.id}`
35405
+ ];
35406
+ }
35407
+ } catch {
35408
+ }
35409
+ return [
35410
+ `terminalhire claim: '${arg}' did not resolve to anything you can claim.`,
35411
+ " It has the shape of a claim token, so one of two things is true, and this",
35412
+ " command cannot tell them apart:",
35413
+ "",
35414
+ " \u2022 the posting was claimed or withdrawn. A claimed posting leaves the",
35415
+ " public index, and `terminalhire bounties` will never bring it back.",
35416
+ " \u2022 the token is unknown here \u2014 mistyped, or minted against a different",
35417
+ " environment than the one this command is pointed at.",
35418
+ "",
35419
+ ` Open it: ${API_URL}/c/${arg}`,
35420
+ " Already yours? terminalhire claim list",
35421
+ " Refresh the index: terminalhire bounties"
35422
+ ];
35423
+ }
35382
35424
  async function resolveBounty(arg) {
35383
35425
  let bountyId, title, repoFullName, issueUrl, amountUSD, source, openPRsAtDiscovery, indexNativeId;
35384
35426
  let founderPosting = false;
@@ -35833,10 +35875,7 @@ async function cmdRecord(arg, flags = {}) {
35833
35875
  }
35834
35876
  const b = await resolveBounty(arg);
35835
35877
  if (!b) {
35836
- console.error(
35837
- `terminalhire claim: '${arg}' is not in the index cache and is not a GitHub issue URL.`
35838
- );
35839
- console.error(" Run `terminalhire bounties` to populate the cache, or pass a full issue URL.");
35878
+ for (const line of await explainUnresolvable(arg)) console.error(line);
35840
35879
  process.exit(1);
35841
35880
  }
35842
35881
  if (b.issueState === "closed") {
@@ -36083,7 +36122,7 @@ terminalhire claim: refusing to record \u2014 read ${b.repoFullName}'s contribut
36083
36122
  );
36084
36123
  console.log(" \u2022 no access to ~/.terminalhire (the executor never needs your profile)");
36085
36124
  console.log(
36086
- "\n Next \u2014 start work (forks + clones into an isolated worktree; your terminal stays put):"
36125
+ "\n Next \u2014 start work (forks + clones into an isolated worktree, then drops you in it):"
36087
36126
  );
36088
36127
  console.log(" terminalhire claim start " + claim.id);
36089
36128
  console.log(" Then publish when it is done (the only step that pushes + opens the PR):");
@@ -36106,10 +36145,7 @@ async function cmdPreview(arg, { json } = {}) {
36106
36145
  }
36107
36146
  const b = await resolveBounty(arg);
36108
36147
  if (!b) {
36109
- console.error(
36110
- `terminalhire claim: '${arg}' is not in the index cache and is not a GitHub issue URL.`
36111
- );
36112
- console.error(" Run `terminalhire bounties` to populate the cache, or pass a full issue URL.");
36148
+ for (const line of await explainUnresolvable(arg)) console.error(line);
36113
36149
  process.exit(1);
36114
36150
  }
36115
36151
  let policy = null;
@@ -36746,6 +36782,13 @@ async function cmdStart(id, flags = {}) {
36746
36782
  ({ id } = picked);
36747
36783
  }
36748
36784
  let claim = claims.findClaim(id);
36785
+ if (!claim) {
36786
+ const held = heldClaimByShortRef(claims.readClaims(), id);
36787
+ if (held) {
36788
+ claim = held;
36789
+ id = held.id;
36790
+ }
36791
+ }
36749
36792
  let chainedFromRecord = false;
36750
36793
  if (!claim) {
36751
36794
  const b = await resolveBounty(id);
@@ -36785,12 +36828,13 @@ async function cmdStart(id, flags = {}) {
36785
36828
  stillThere = false;
36786
36829
  }
36787
36830
  if (stillThere) {
36788
- console.log("Already started \u2014 your worktree is ready (your terminal stays put):");
36789
- console.log(` cd ${claim.worktreePath}`);
36831
+ console.log("Already started \u2014 your worktree is ready.");
36790
36832
  if (claim.branch) console.log(` branch: ${claim.branch}`);
36791
36833
  console.log(`
36792
- When it's done: terminalhire claim submit ${id}`);
36834
+ Commit as you go \u2014 your commits ARE the patch. Hand it back with:`);
36835
+ console.log(` terminalhire claim submit ${id}`);
36793
36836
  await beatFounderPresence(claim);
36837
+ landDeveloperIn(claim.worktreePath, flags);
36794
36838
  return;
36795
36839
  }
36796
36840
  }
@@ -36811,8 +36855,7 @@ ${sanitizeText(claim.title)}`);
36811
36855
  }
36812
36856
  }
36813
36857
  if (outcome.outcome === "delivered") {
36814
- const workspace = outcome.claim?.worktreePath;
36815
- if (flags.open && workspace) launchAgentIn(workspace, flags.open);
36858
+ landDeveloperIn(outcome.claim?.worktreePath, flags);
36816
36859
  return;
36817
36860
  }
36818
36861
  if (outcome.outcome === "pending") {
@@ -36932,10 +36975,9 @@ terminalhire claim: not started \u2014 starting forks ${claim.repoFullName} to y
36932
36975
  \u2713 Started: ${claim.title}`);
36933
36976
  console.log(` fork: ${forkFullName}`);
36934
36977
  console.log(` branch: ${branch}`);
36935
- console.log("\n Your isolated worktree is ready \u2014 your current terminal is untouched:");
36936
- console.log(` cd ${toplevel}`);
36937
- console.log("\n When the work is done (the only step that pushes + opens the PR):");
36978
+ console.log("\n Commit as you go. When it is done (the only step that pushes + opens the PR):");
36938
36979
  console.log(` terminalhire claim submit ${id}`);
36980
+ landDeveloperIn(toplevel, flags);
36939
36981
  }
36940
36982
  async function cmdStartHere(claims, claim, flags = {}) {
36941
36983
  let toplevel;
@@ -37046,6 +37088,45 @@ function launchAgentIn(dest, agentName, { spawnFn, log = console.log } = {}) {
37046
37088
  }
37047
37089
  return { launched: true, reason: null };
37048
37090
  }
37091
+ function heldClaimByShortRef(allClaims, arg) {
37092
+ if (!looksLikeShortRef(arg)) return null;
37093
+ if (!Array.isArray(allClaims)) return null;
37094
+ return allClaims.find((c) => c?.id && opportunityShortToken(c.id) === arg) ?? null;
37095
+ }
37096
+ function landDeveloperIn(dest, flags = {}, deps = {}) {
37097
+ const { spawnFn, log = console.log, isTTY, shellPath } = deps;
37098
+ if (!dest) return { landed: false, reason: "no-workspace" };
37099
+ const cdLine = () => log(`
37100
+ cd ${dest}`);
37101
+ if (flags.open) {
37102
+ const result2 = launchAgentIn(dest, flags.open, { spawnFn, log });
37103
+ return { landed: result2.launched, reason: result2.reason };
37104
+ }
37105
+ if (flags.stay) {
37106
+ cdLine();
37107
+ return { landed: false, reason: "declined" };
37108
+ }
37109
+ const tty = isTTY ?? Boolean(process.stdout?.isTTY);
37110
+ if (!tty) {
37111
+ cdLine();
37112
+ return { landed: false, reason: "not-a-tty" };
37113
+ }
37114
+ const shell = Object.hasOwn(deps, "shellPath") ? shellPath : process.env.SHELL;
37115
+ if (typeof shell !== "string" || !shell.startsWith("/")) {
37116
+ cdLine();
37117
+ return { landed: false, reason: "no-shell" };
37118
+ }
37119
+ log(`
37120
+ You're in the workspace now \u2014 \`exit\` brings you back here.`);
37121
+ const spawn6 = spawnFn ?? spawnSync8;
37122
+ const result = spawn6(shell, [], { cwd: dest, stdio: "inherit", shell: false });
37123
+ if (result?.error) {
37124
+ log(` ${shell} would not start \u2014 the workspace is ready anyway.`);
37125
+ cdLine();
37126
+ return { landed: false, reason: "shell-failed" };
37127
+ }
37128
+ return { landed: true, reason: null };
37129
+ }
37049
37130
  function renderServerRefusal(status, body) {
37050
37131
  const code = body && typeof body.error === "string" ? body.error : null;
37051
37132
  const prose = body && typeof body.message === "string" ? body.message : null;
@@ -37574,11 +37655,9 @@ async function cmdSliceFullTier(claims, id, local, fullTierBody, flags, cloneRep
37574
37655
  printWorkspacePack(pack);
37575
37656
  console.log(
37576
37657
  `
37577
- Author your change there (commit as you go), then: terminalhire claim submit ${claim.id}`
37658
+ Commit as you go \u2014 your commits ARE the patch. Hand it back with:
37659
+ terminalhire claim submit ${claim.id}`
37578
37660
  );
37579
- if (pack.agents.written === true) {
37580
- console.log(` Working with an agent? Point it at ${AGENTS_REL_PATH} first.`);
37581
- }
37582
37661
  await beatFounderPresence(working ?? claim);
37583
37662
  }
37584
37663
  async function cmdSlice(id, flags = {}) {
@@ -37588,8 +37667,7 @@ async function cmdSlice(id, flags = {}) {
37588
37667
  }
37589
37668
  const outcome = await attemptSliceDelivery(id, flags);
37590
37669
  exitOnSliceOutcome(outcome);
37591
- const workspace = outcome.claim?.worktreePath;
37592
- if (flags.open && workspace) launchAgentIn(workspace, flags.open);
37670
+ landDeveloperIn(outcome.claim?.worktreePath, flags);
37593
37671
  }
37594
37672
  function exitOnSliceOutcome(outcome) {
37595
37673
  if (outcome.outcome === "delivered") return;
@@ -37783,11 +37861,9 @@ async function attemptSliceDelivery(id, flags = {}) {
37783
37861
  printWorkspacePack(pack);
37784
37862
  console.log(
37785
37863
  `
37786
- Author your change there (commit as you go), then: terminalhire claim submit ${claim.id}`
37864
+ Commit as you go \u2014 your commits ARE the patch. Hand it back with:
37865
+ terminalhire claim submit ${claim.id}`
37787
37866
  );
37788
- if (pack.agents.written === true) {
37789
- console.log(` Working with an agent? Point it at ${AGENTS_REL_PATH} first.`);
37790
- }
37791
37867
  await beatFounderPresence(working ?? claim);
37792
37868
  return { outcome: "delivered", claim: working ?? claim };
37793
37869
  }
@@ -2101,6 +2101,9 @@ function labelSelector(labels) {
2101
2101
  throw new EnvRunError("labels object is empty; pass at least one label or omit it");
2102
2102
  return `${first[0]}=${first[1]}`;
2103
2103
  }
2104
+ function withUserScriptPath(command) {
2105
+ return `PATH="$PATH:$HOME/.local/bin"; export PATH; ${command}`;
2106
+ }
2104
2107
  async function runStep(containment, r) {
2105
2108
  const spec = {
2106
2109
  profile: r.profile,
@@ -2115,7 +2118,7 @@ async function runStep(containment, r) {
2115
2118
  pathDomain: r.pathDomain,
2116
2119
  guestUser: r.guestUser,
2117
2120
  program: "/bin/sh",
2118
- args: ["-c", r.command]
2121
+ args: ["-c", withUserScriptPath(r.command)]
2119
2122
  };
2120
2123
  const startedAt = Date.now();
2121
2124
  const res = await containment.run(spec, r.env, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminalhire",
3
- "version": "0.42.14",
3
+ "version": "0.42.15",
4
4
  "description": "Developer jobs and paid bounties, matched in the terminal — ambient matches in the Claude Code spinner. Claim a scoped bounty and get paid when the poster accepts it.",
5
5
  "repository": {
6
6
  "type": "git",