terminalhire 0.26.2 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/jpi-claim.js +197 -9
- package/dist/bin/jpi-dispatch.js +198 -9
- package/package.json +1 -1
package/dist/bin/jpi-claim.js
CHANGED
|
@@ -9162,10 +9162,19 @@ terminalhire claim: refusing to record \u2014 read ${b.repoFullName}'s contribut
|
|
|
9162
9162
|
console.log(" \u2022 MUST NOT `git push` or `gh pr` \u2014 pushing happens only via `terminalhire submit`");
|
|
9163
9163
|
console.log(" \u2022 clone + static analysis + patch only; NO test/build execution without explicit approval");
|
|
9164
9164
|
console.log(" \u2022 no access to ~/.terminalhire (the executor never needs your profile)");
|
|
9165
|
-
console.log("\n Next:");
|
|
9166
|
-
console.log("
|
|
9167
|
-
console.log("
|
|
9168
|
-
console.log("
|
|
9165
|
+
console.log("\n Next \u2014 start work (forks + clones into an isolated worktree; your terminal stays put):");
|
|
9166
|
+
console.log(" terminalhire claim start " + claim.id);
|
|
9167
|
+
console.log(" Then publish when it is done (the only step that pushes + opens the PR):");
|
|
9168
|
+
console.log(" terminalhire claim submit " + claim.id);
|
|
9169
|
+
if (flags.start) {
|
|
9170
|
+
await cmdStart(claim.id, { start: true });
|
|
9171
|
+
} else if (process.stdin.isTTY && !flags["no-start"]) {
|
|
9172
|
+
const go = await confirm(`
|
|
9173
|
+
Fork ${claim.repoFullName} and start now? (y/N) `);
|
|
9174
|
+
if (go) await cmdStart(claim.id, { start: true });
|
|
9175
|
+
else console.log(`
|
|
9176
|
+
Saved. Start anytime: terminalhire claim start ${claim.id}`);
|
|
9177
|
+
}
|
|
9169
9178
|
}
|
|
9170
9179
|
async function cmdPreview(arg, { json } = {}) {
|
|
9171
9180
|
if (!arg) {
|
|
@@ -9362,6 +9371,179 @@ async function cmdAttach(id, worktree, branch) {
|
|
|
9362
9371
|
claims.updateClaim(id, { worktreePath: toplevel, branch });
|
|
9363
9372
|
console.log(`Attached ${id}: worktree=${toplevel} branch=${branch}`);
|
|
9364
9373
|
}
|
|
9374
|
+
function workDirFor(repoFullName, issueNumber) {
|
|
9375
|
+
const [owner, repo] = String(repoFullName).split("/");
|
|
9376
|
+
const suffix = issueNumber ? `-${issueNumber}` : "";
|
|
9377
|
+
return join7(homedir6(), "terminalhire", "work", `${owner}-${repo}${suffix}`);
|
|
9378
|
+
}
|
|
9379
|
+
function startBranchFor(repoFullName, issueNumber) {
|
|
9380
|
+
const repo = String(repoFullName).split("/")[1] || "claim";
|
|
9381
|
+
return `th/${repo}-${issueNumber || "work"}`;
|
|
9382
|
+
}
|
|
9383
|
+
function explicitForkConsent(flags = {}) {
|
|
9384
|
+
return Boolean(flags.start) || Boolean(flags.fork);
|
|
9385
|
+
}
|
|
9386
|
+
async function ensureForkExists(repoFullName, ghUser) {
|
|
9387
|
+
const repoShort = repoFullName.split("/")[1];
|
|
9388
|
+
const forkFullName = `${ghUser}/${repoShort}`;
|
|
9389
|
+
await sh("gh", ["repo", "fork", repoFullName, "--clone=false"]);
|
|
9390
|
+
let isFork = false;
|
|
9391
|
+
try {
|
|
9392
|
+
isFork = await sh("gh", ["api", `repos/${forkFullName}`, "--jq", ".fork"]) === "true";
|
|
9393
|
+
} catch {
|
|
9394
|
+
isFork = false;
|
|
9395
|
+
}
|
|
9396
|
+
if (!isFork) throw new Error(`fork ${forkFullName} created but could not be verified as a fork`);
|
|
9397
|
+
return forkFullName;
|
|
9398
|
+
}
|
|
9399
|
+
async function cmdStart(id, flags = {}) {
|
|
9400
|
+
const claims = await Promise.resolve().then(() => (init_claims(), claims_exports));
|
|
9401
|
+
if (!id) {
|
|
9402
|
+
const startable = claims.listClaims({ active: true }).filter((c) => c.state === "claimed");
|
|
9403
|
+
if (startable.length === 0) {
|
|
9404
|
+
console.log("No claims ready to start. Claim one first: terminalhire claim <ref>");
|
|
9405
|
+
return;
|
|
9406
|
+
}
|
|
9407
|
+
console.log(`
|
|
9408
|
+
${startable.length} claim${startable.length === 1 ? "" : "s"} ready to start:
|
|
9409
|
+
`);
|
|
9410
|
+
for (const c of startable) {
|
|
9411
|
+
console.log(` ${c.title}`);
|
|
9412
|
+
console.log(` terminalhire claim start ${c.id}`);
|
|
9413
|
+
}
|
|
9414
|
+
console.log("\nRun the command under the one you want to start.");
|
|
9415
|
+
return;
|
|
9416
|
+
}
|
|
9417
|
+
const claim = claims.findClaim(id);
|
|
9418
|
+
if (!claim) {
|
|
9419
|
+
console.error(`terminalhire claim: no claim with id '${id}'.`);
|
|
9420
|
+
process.exit(1);
|
|
9421
|
+
}
|
|
9422
|
+
if (claim.worktreePath) {
|
|
9423
|
+
let stillThere = false;
|
|
9424
|
+
try {
|
|
9425
|
+
stillThere = await sh("git", ["-C", claim.worktreePath, "rev-parse", "--show-toplevel"]) === claim.worktreePath;
|
|
9426
|
+
} catch {
|
|
9427
|
+
stillThere = false;
|
|
9428
|
+
}
|
|
9429
|
+
if (stillThere) {
|
|
9430
|
+
console.log("Already started \u2014 your worktree is ready (your terminal stays put):");
|
|
9431
|
+
console.log(` cd ${claim.worktreePath}`);
|
|
9432
|
+
if (claim.branch) console.log(` branch: ${claim.branch}`);
|
|
9433
|
+
console.log(`
|
|
9434
|
+
When it's done: terminalhire claim submit ${id}`);
|
|
9435
|
+
return;
|
|
9436
|
+
}
|
|
9437
|
+
}
|
|
9438
|
+
if (flags.here) {
|
|
9439
|
+
await cmdStartHere(claims, claim);
|
|
9440
|
+
return;
|
|
9441
|
+
}
|
|
9442
|
+
let ghUser;
|
|
9443
|
+
try {
|
|
9444
|
+
ghUser = await sh("gh", ["api", "user", "-q", ".login"]);
|
|
9445
|
+
} catch {
|
|
9446
|
+
console.error("terminalhire claim: 'gh' CLI not available or not authenticated. Run 'gh auth login'.");
|
|
9447
|
+
process.exit(1);
|
|
9448
|
+
}
|
|
9449
|
+
let consented = explicitForkConsent(flags);
|
|
9450
|
+
if (!consented && process.stdin.isTTY) {
|
|
9451
|
+
consented = await confirm(`
|
|
9452
|
+
Fork ${claim.repoFullName} to @${ghUser} and clone it to start? (y/N) `);
|
|
9453
|
+
}
|
|
9454
|
+
if (!consented) {
|
|
9455
|
+
console.error(
|
|
9456
|
+
`
|
|
9457
|
+
terminalhire claim: not started \u2014 starting forks ${claim.repoFullName} to your GitHub account (a network write).
|
|
9458
|
+
Re-run in a terminal to confirm interactively, or pass --fork to consent non-interactively.`
|
|
9459
|
+
);
|
|
9460
|
+
process.exit(1);
|
|
9461
|
+
}
|
|
9462
|
+
const issueNumber = (parseGitHubUrl(claim.issueUrl) || {}).number;
|
|
9463
|
+
const destDir = workDirFor(claim.repoFullName, issueNumber);
|
|
9464
|
+
if (existsSync6(destDir)) {
|
|
9465
|
+
console.error(
|
|
9466
|
+
`terminalhire claim: ${destDir} already exists \u2014 refusing to clobber it.
|
|
9467
|
+
Remove it and retry, or attach it: terminalhire claim attach ${id} --worktree ${destDir} --branch <branch>`
|
|
9468
|
+
);
|
|
9469
|
+
process.exit(1);
|
|
9470
|
+
}
|
|
9471
|
+
mkdirSync6(join7(homedir6(), "terminalhire", "work"), { recursive: true });
|
|
9472
|
+
let forkFullName;
|
|
9473
|
+
try {
|
|
9474
|
+
forkFullName = await ensureForkExists(claim.repoFullName, ghUser);
|
|
9475
|
+
} catch (err) {
|
|
9476
|
+
console.error(`terminalhire claim: could not create your fork of ${claim.repoFullName}. ${err.message ?? err}`);
|
|
9477
|
+
process.exit(1);
|
|
9478
|
+
}
|
|
9479
|
+
try {
|
|
9480
|
+
await sh("gh", ["repo", "clone", forkFullName, destDir]);
|
|
9481
|
+
} catch (err) {
|
|
9482
|
+
try {
|
|
9483
|
+
rmSync3(destDir, { recursive: true, force: true });
|
|
9484
|
+
} catch {
|
|
9485
|
+
}
|
|
9486
|
+
console.error(`terminalhire claim: clone of ${forkFullName} failed. ${err.stderr || err.message || err}`);
|
|
9487
|
+
process.exit(1);
|
|
9488
|
+
}
|
|
9489
|
+
try {
|
|
9490
|
+
await sh("git", ["-C", destDir, "remote", "get-url", "upstream"]);
|
|
9491
|
+
} catch {
|
|
9492
|
+
try {
|
|
9493
|
+
await sh("git", ["-C", destDir, "remote", "add", "upstream", `https://github.com/${claim.repoFullName}.git`]);
|
|
9494
|
+
} catch {
|
|
9495
|
+
}
|
|
9496
|
+
}
|
|
9497
|
+
const branch = startBranchFor(claim.repoFullName, issueNumber);
|
|
9498
|
+
await sh("git", ["-C", destDir, "checkout", "-b", branch]);
|
|
9499
|
+
const toplevel = await sh("git", ["-C", destDir, "rev-parse", "--show-toplevel"]);
|
|
9500
|
+
claims.updateClaim(id, { worktreePath: toplevel, branch, state: "working" });
|
|
9501
|
+
console.log(`
|
|
9502
|
+
\u2713 Started: ${claim.title}`);
|
|
9503
|
+
console.log(` fork: ${forkFullName}`);
|
|
9504
|
+
console.log(` branch: ${branch}`);
|
|
9505
|
+
console.log("\n Your isolated worktree is ready \u2014 your current terminal is untouched:");
|
|
9506
|
+
console.log(` cd ${toplevel}`);
|
|
9507
|
+
console.log("\n When the work is done (the only step that pushes + opens the PR):");
|
|
9508
|
+
console.log(` terminalhire claim submit ${id}`);
|
|
9509
|
+
}
|
|
9510
|
+
async function cmdStartHere(claims, claim) {
|
|
9511
|
+
let toplevel;
|
|
9512
|
+
try {
|
|
9513
|
+
toplevel = await sh("git", ["-C", process.cwd(), "rev-parse", "--show-toplevel"]);
|
|
9514
|
+
} catch {
|
|
9515
|
+
console.error("terminalhire claim: --here must be run inside a git repository.");
|
|
9516
|
+
process.exit(1);
|
|
9517
|
+
}
|
|
9518
|
+
const remotesOut = await sh("git", ["-C", toplevel, "remote", "-v"]).catch(() => "");
|
|
9519
|
+
const repos = new Set(
|
|
9520
|
+
remotesOut.split("\n").map((l) => parseRepoFromRemote(l.split(/\s+/)[1])).filter(Boolean).map((r) => r.toLowerCase())
|
|
9521
|
+
);
|
|
9522
|
+
if (!repos.has(claim.repoFullName.toLowerCase())) {
|
|
9523
|
+
console.error(
|
|
9524
|
+
`terminalhire claim: --here expects a clone of ${claim.repoFullName}, but no remote here points there.
|
|
9525
|
+
Use \`terminalhire claim start ${claim.id}\` (no --here) to fork + clone it into a fresh worktree.`
|
|
9526
|
+
);
|
|
9527
|
+
process.exit(1);
|
|
9528
|
+
}
|
|
9529
|
+
const dirty = await sh("git", ["-C", toplevel, "status", "--porcelain", "--untracked-files=no"]).catch(() => "");
|
|
9530
|
+
if (dirty) {
|
|
9531
|
+
console.error(
|
|
9532
|
+
"terminalhire claim: --here needs a clean working tree (claim work must not mix with your current changes).\n Commit or stash first, or use `terminalhire claim start` for an isolated worktree."
|
|
9533
|
+
);
|
|
9534
|
+
process.exit(1);
|
|
9535
|
+
}
|
|
9536
|
+
const issueNumber = (parseGitHubUrl(claim.issueUrl) || {}).number;
|
|
9537
|
+
const branch = startBranchFor(claim.repoFullName, issueNumber);
|
|
9538
|
+
await sh("git", ["-C", toplevel, "checkout", "-b", branch]);
|
|
9539
|
+
claims.updateClaim(claim.id, { worktreePath: toplevel, branch, state: "working" });
|
|
9540
|
+
console.log(`
|
|
9541
|
+
\u2713 Started here: ${claim.title}`);
|
|
9542
|
+
console.log(` worktree: ${toplevel}`);
|
|
9543
|
+
console.log(` branch: ${branch}`);
|
|
9544
|
+
console.log(`
|
|
9545
|
+
When the work is done: terminalhire claim submit ${claim.id}`);
|
|
9546
|
+
}
|
|
9365
9547
|
async function cmdSubmit(id, flags = {}) {
|
|
9366
9548
|
const worktreeOverride = flags.worktree;
|
|
9367
9549
|
const claims = await Promise.resolve().then(() => (init_claims(), claims_exports));
|
|
@@ -9376,10 +9558,10 @@ async function cmdSubmit(id, flags = {}) {
|
|
|
9376
9558
|
console.error(`terminalhire claim: no claim with id '${id}'.`);
|
|
9377
9559
|
process.exit(1);
|
|
9378
9560
|
}
|
|
9379
|
-
if (claim.state !== "ready") {
|
|
9561
|
+
if (claim.state !== "working" && claim.state !== "ready") {
|
|
9380
9562
|
console.error(
|
|
9381
|
-
`terminalhire claim: ${id} is '${claim.state}'
|
|
9382
|
-
terminalhire claim
|
|
9563
|
+
`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:
|
|
9564
|
+
terminalhire claim start ${id}`
|
|
9383
9565
|
);
|
|
9384
9566
|
process.exit(1);
|
|
9385
9567
|
}
|
|
@@ -9971,6 +10153,9 @@ async function run() {
|
|
|
9971
10153
|
case "record":
|
|
9972
10154
|
await cmdRecord(positional[0], flags);
|
|
9973
10155
|
break;
|
|
10156
|
+
case "start":
|
|
10157
|
+
await cmdStart(positional[0], flags);
|
|
10158
|
+
break;
|
|
9974
10159
|
case "list":
|
|
9975
10160
|
await cmdList(active);
|
|
9976
10161
|
break;
|
|
@@ -9990,7 +10175,7 @@ async function run() {
|
|
|
9990
10175
|
await cmdRelease(positional[0]);
|
|
9991
10176
|
break;
|
|
9992
10177
|
default:
|
|
9993
|
-
console.error(`terminalhire claim: unknown verb '${verb ?? ""}'. Expected: preview | record | attach | list | status | update | submit | release`);
|
|
10178
|
+
console.error(`terminalhire claim: unknown verb '${verb ?? ""}'. Expected: preview | record | start | attach | list | status | update | submit | release`);
|
|
9994
10179
|
process.exit(1);
|
|
9995
10180
|
}
|
|
9996
10181
|
} catch (err) {
|
|
@@ -10006,6 +10191,7 @@ export {
|
|
|
10006
10191
|
cmdRecord,
|
|
10007
10192
|
countOpenPRsReferencingIssue,
|
|
10008
10193
|
diffContention,
|
|
10194
|
+
explicitForkConsent,
|
|
10009
10195
|
findClaimableByShortRef,
|
|
10010
10196
|
findClaimableInCache,
|
|
10011
10197
|
fmtAge,
|
|
@@ -10021,7 +10207,9 @@ export {
|
|
|
10021
10207
|
resolveSubmitWorktree,
|
|
10022
10208
|
revokeFailureAction,
|
|
10023
10209
|
run,
|
|
10024
|
-
selectPushRemote
|
|
10210
|
+
selectPushRemote,
|
|
10211
|
+
startBranchFor,
|
|
10212
|
+
workDirFor
|
|
10025
10213
|
};
|
|
10026
10214
|
/*! Bundled license information:
|
|
10027
10215
|
|
package/dist/bin/jpi-dispatch.js
CHANGED
|
@@ -11039,6 +11039,7 @@ __export(jpi_claim_exports, {
|
|
|
11039
11039
|
cmdRecord: () => cmdRecord,
|
|
11040
11040
|
countOpenPRsReferencingIssue: () => countOpenPRsReferencingIssue,
|
|
11041
11041
|
diffContention: () => diffContention,
|
|
11042
|
+
explicitForkConsent: () => explicitForkConsent,
|
|
11042
11043
|
findClaimableByShortRef: () => findClaimableByShortRef,
|
|
11043
11044
|
findClaimableInCache: () => findClaimableInCache,
|
|
11044
11045
|
fmtAge: () => fmtAge,
|
|
@@ -11054,7 +11055,9 @@ __export(jpi_claim_exports, {
|
|
|
11054
11055
|
resolveSubmitWorktree: () => resolveSubmitWorktree,
|
|
11055
11056
|
revokeFailureAction: () => revokeFailureAction,
|
|
11056
11057
|
run: () => run7,
|
|
11057
|
-
selectPushRemote: () => selectPushRemote
|
|
11058
|
+
selectPushRemote: () => selectPushRemote,
|
|
11059
|
+
startBranchFor: () => startBranchFor,
|
|
11060
|
+
workDirFor: () => workDirFor
|
|
11058
11061
|
});
|
|
11059
11062
|
import { readFileSync as readFileSync17, writeFileSync as writeFileSync11, mkdirSync as mkdirSync11, existsSync as existsSync10, rmSync as rmSync4 } from "fs";
|
|
11060
11063
|
import { join as join17 } from "path";
|
|
@@ -11565,10 +11568,19 @@ terminalhire claim: refusing to record \u2014 read ${b.repoFullName}'s contribut
|
|
|
11565
11568
|
console.log(" \u2022 MUST NOT `git push` or `gh pr` \u2014 pushing happens only via `terminalhire submit`");
|
|
11566
11569
|
console.log(" \u2022 clone + static analysis + patch only; NO test/build execution without explicit approval");
|
|
11567
11570
|
console.log(" \u2022 no access to ~/.terminalhire (the executor never needs your profile)");
|
|
11568
|
-
console.log("\n Next:");
|
|
11569
|
-
console.log("
|
|
11570
|
-
console.log("
|
|
11571
|
-
console.log("
|
|
11571
|
+
console.log("\n Next \u2014 start work (forks + clones into an isolated worktree; your terminal stays put):");
|
|
11572
|
+
console.log(" terminalhire claim start " + claim.id);
|
|
11573
|
+
console.log(" Then publish when it is done (the only step that pushes + opens the PR):");
|
|
11574
|
+
console.log(" terminalhire claim submit " + claim.id);
|
|
11575
|
+
if (flags.start) {
|
|
11576
|
+
await cmdStart(claim.id, { start: true });
|
|
11577
|
+
} else if (process.stdin.isTTY && !flags["no-start"]) {
|
|
11578
|
+
const go = await confirm(`
|
|
11579
|
+
Fork ${claim.repoFullName} and start now? (y/N) `);
|
|
11580
|
+
if (go) await cmdStart(claim.id, { start: true });
|
|
11581
|
+
else console.log(`
|
|
11582
|
+
Saved. Start anytime: terminalhire claim start ${claim.id}`);
|
|
11583
|
+
}
|
|
11572
11584
|
}
|
|
11573
11585
|
async function cmdPreview(arg, { json } = {}) {
|
|
11574
11586
|
if (!arg) {
|
|
@@ -11765,6 +11777,179 @@ async function cmdAttach(id, worktree, branch) {
|
|
|
11765
11777
|
claims.updateClaim(id, { worktreePath: toplevel, branch });
|
|
11766
11778
|
console.log(`Attached ${id}: worktree=${toplevel} branch=${branch}`);
|
|
11767
11779
|
}
|
|
11780
|
+
function workDirFor(repoFullName, issueNumber) {
|
|
11781
|
+
const [owner, repo] = String(repoFullName).split("/");
|
|
11782
|
+
const suffix = issueNumber ? `-${issueNumber}` : "";
|
|
11783
|
+
return join17(homedir15(), "terminalhire", "work", `${owner}-${repo}${suffix}`);
|
|
11784
|
+
}
|
|
11785
|
+
function startBranchFor(repoFullName, issueNumber) {
|
|
11786
|
+
const repo = String(repoFullName).split("/")[1] || "claim";
|
|
11787
|
+
return `th/${repo}-${issueNumber || "work"}`;
|
|
11788
|
+
}
|
|
11789
|
+
function explicitForkConsent(flags = {}) {
|
|
11790
|
+
return Boolean(flags.start) || Boolean(flags.fork);
|
|
11791
|
+
}
|
|
11792
|
+
async function ensureForkExists(repoFullName, ghUser) {
|
|
11793
|
+
const repoShort = repoFullName.split("/")[1];
|
|
11794
|
+
const forkFullName = `${ghUser}/${repoShort}`;
|
|
11795
|
+
await sh("gh", ["repo", "fork", repoFullName, "--clone=false"]);
|
|
11796
|
+
let isFork = false;
|
|
11797
|
+
try {
|
|
11798
|
+
isFork = await sh("gh", ["api", `repos/${forkFullName}`, "--jq", ".fork"]) === "true";
|
|
11799
|
+
} catch {
|
|
11800
|
+
isFork = false;
|
|
11801
|
+
}
|
|
11802
|
+
if (!isFork) throw new Error(`fork ${forkFullName} created but could not be verified as a fork`);
|
|
11803
|
+
return forkFullName;
|
|
11804
|
+
}
|
|
11805
|
+
async function cmdStart(id, flags = {}) {
|
|
11806
|
+
const claims = await Promise.resolve().then(() => (init_claims(), claims_exports));
|
|
11807
|
+
if (!id) {
|
|
11808
|
+
const startable = claims.listClaims({ active: true }).filter((c) => c.state === "claimed");
|
|
11809
|
+
if (startable.length === 0) {
|
|
11810
|
+
console.log("No claims ready to start. Claim one first: terminalhire claim <ref>");
|
|
11811
|
+
return;
|
|
11812
|
+
}
|
|
11813
|
+
console.log(`
|
|
11814
|
+
${startable.length} claim${startable.length === 1 ? "" : "s"} ready to start:
|
|
11815
|
+
`);
|
|
11816
|
+
for (const c of startable) {
|
|
11817
|
+
console.log(` ${c.title}`);
|
|
11818
|
+
console.log(` terminalhire claim start ${c.id}`);
|
|
11819
|
+
}
|
|
11820
|
+
console.log("\nRun the command under the one you want to start.");
|
|
11821
|
+
return;
|
|
11822
|
+
}
|
|
11823
|
+
const claim = claims.findClaim(id);
|
|
11824
|
+
if (!claim) {
|
|
11825
|
+
console.error(`terminalhire claim: no claim with id '${id}'.`);
|
|
11826
|
+
process.exit(1);
|
|
11827
|
+
}
|
|
11828
|
+
if (claim.worktreePath) {
|
|
11829
|
+
let stillThere = false;
|
|
11830
|
+
try {
|
|
11831
|
+
stillThere = await sh("git", ["-C", claim.worktreePath, "rev-parse", "--show-toplevel"]) === claim.worktreePath;
|
|
11832
|
+
} catch {
|
|
11833
|
+
stillThere = false;
|
|
11834
|
+
}
|
|
11835
|
+
if (stillThere) {
|
|
11836
|
+
console.log("Already started \u2014 your worktree is ready (your terminal stays put):");
|
|
11837
|
+
console.log(` cd ${claim.worktreePath}`);
|
|
11838
|
+
if (claim.branch) console.log(` branch: ${claim.branch}`);
|
|
11839
|
+
console.log(`
|
|
11840
|
+
When it's done: terminalhire claim submit ${id}`);
|
|
11841
|
+
return;
|
|
11842
|
+
}
|
|
11843
|
+
}
|
|
11844
|
+
if (flags.here) {
|
|
11845
|
+
await cmdStartHere(claims, claim);
|
|
11846
|
+
return;
|
|
11847
|
+
}
|
|
11848
|
+
let ghUser;
|
|
11849
|
+
try {
|
|
11850
|
+
ghUser = await sh("gh", ["api", "user", "-q", ".login"]);
|
|
11851
|
+
} catch {
|
|
11852
|
+
console.error("terminalhire claim: 'gh' CLI not available or not authenticated. Run 'gh auth login'.");
|
|
11853
|
+
process.exit(1);
|
|
11854
|
+
}
|
|
11855
|
+
let consented = explicitForkConsent(flags);
|
|
11856
|
+
if (!consented && process.stdin.isTTY) {
|
|
11857
|
+
consented = await confirm(`
|
|
11858
|
+
Fork ${claim.repoFullName} to @${ghUser} and clone it to start? (y/N) `);
|
|
11859
|
+
}
|
|
11860
|
+
if (!consented) {
|
|
11861
|
+
console.error(
|
|
11862
|
+
`
|
|
11863
|
+
terminalhire claim: not started \u2014 starting forks ${claim.repoFullName} to your GitHub account (a network write).
|
|
11864
|
+
Re-run in a terminal to confirm interactively, or pass --fork to consent non-interactively.`
|
|
11865
|
+
);
|
|
11866
|
+
process.exit(1);
|
|
11867
|
+
}
|
|
11868
|
+
const issueNumber = (parseGitHubUrl(claim.issueUrl) || {}).number;
|
|
11869
|
+
const destDir = workDirFor(claim.repoFullName, issueNumber);
|
|
11870
|
+
if (existsSync10(destDir)) {
|
|
11871
|
+
console.error(
|
|
11872
|
+
`terminalhire claim: ${destDir} already exists \u2014 refusing to clobber it.
|
|
11873
|
+
Remove it and retry, or attach it: terminalhire claim attach ${id} --worktree ${destDir} --branch <branch>`
|
|
11874
|
+
);
|
|
11875
|
+
process.exit(1);
|
|
11876
|
+
}
|
|
11877
|
+
mkdirSync11(join17(homedir15(), "terminalhire", "work"), { recursive: true });
|
|
11878
|
+
let forkFullName;
|
|
11879
|
+
try {
|
|
11880
|
+
forkFullName = await ensureForkExists(claim.repoFullName, ghUser);
|
|
11881
|
+
} catch (err) {
|
|
11882
|
+
console.error(`terminalhire claim: could not create your fork of ${claim.repoFullName}. ${err.message ?? err}`);
|
|
11883
|
+
process.exit(1);
|
|
11884
|
+
}
|
|
11885
|
+
try {
|
|
11886
|
+
await sh("gh", ["repo", "clone", forkFullName, destDir]);
|
|
11887
|
+
} catch (err) {
|
|
11888
|
+
try {
|
|
11889
|
+
rmSync4(destDir, { recursive: true, force: true });
|
|
11890
|
+
} catch {
|
|
11891
|
+
}
|
|
11892
|
+
console.error(`terminalhire claim: clone of ${forkFullName} failed. ${err.stderr || err.message || err}`);
|
|
11893
|
+
process.exit(1);
|
|
11894
|
+
}
|
|
11895
|
+
try {
|
|
11896
|
+
await sh("git", ["-C", destDir, "remote", "get-url", "upstream"]);
|
|
11897
|
+
} catch {
|
|
11898
|
+
try {
|
|
11899
|
+
await sh("git", ["-C", destDir, "remote", "add", "upstream", `https://github.com/${claim.repoFullName}.git`]);
|
|
11900
|
+
} catch {
|
|
11901
|
+
}
|
|
11902
|
+
}
|
|
11903
|
+
const branch = startBranchFor(claim.repoFullName, issueNumber);
|
|
11904
|
+
await sh("git", ["-C", destDir, "checkout", "-b", branch]);
|
|
11905
|
+
const toplevel = await sh("git", ["-C", destDir, "rev-parse", "--show-toplevel"]);
|
|
11906
|
+
claims.updateClaim(id, { worktreePath: toplevel, branch, state: "working" });
|
|
11907
|
+
console.log(`
|
|
11908
|
+
\u2713 Started: ${claim.title}`);
|
|
11909
|
+
console.log(` fork: ${forkFullName}`);
|
|
11910
|
+
console.log(` branch: ${branch}`);
|
|
11911
|
+
console.log("\n Your isolated worktree is ready \u2014 your current terminal is untouched:");
|
|
11912
|
+
console.log(` cd ${toplevel}`);
|
|
11913
|
+
console.log("\n When the work is done (the only step that pushes + opens the PR):");
|
|
11914
|
+
console.log(` terminalhire claim submit ${id}`);
|
|
11915
|
+
}
|
|
11916
|
+
async function cmdStartHere(claims, claim) {
|
|
11917
|
+
let toplevel;
|
|
11918
|
+
try {
|
|
11919
|
+
toplevel = await sh("git", ["-C", process.cwd(), "rev-parse", "--show-toplevel"]);
|
|
11920
|
+
} catch {
|
|
11921
|
+
console.error("terminalhire claim: --here must be run inside a git repository.");
|
|
11922
|
+
process.exit(1);
|
|
11923
|
+
}
|
|
11924
|
+
const remotesOut = await sh("git", ["-C", toplevel, "remote", "-v"]).catch(() => "");
|
|
11925
|
+
const repos = new Set(
|
|
11926
|
+
remotesOut.split("\n").map((l) => parseRepoFromRemote(l.split(/\s+/)[1])).filter(Boolean).map((r) => r.toLowerCase())
|
|
11927
|
+
);
|
|
11928
|
+
if (!repos.has(claim.repoFullName.toLowerCase())) {
|
|
11929
|
+
console.error(
|
|
11930
|
+
`terminalhire claim: --here expects a clone of ${claim.repoFullName}, but no remote here points there.
|
|
11931
|
+
Use \`terminalhire claim start ${claim.id}\` (no --here) to fork + clone it into a fresh worktree.`
|
|
11932
|
+
);
|
|
11933
|
+
process.exit(1);
|
|
11934
|
+
}
|
|
11935
|
+
const dirty = await sh("git", ["-C", toplevel, "status", "--porcelain", "--untracked-files=no"]).catch(() => "");
|
|
11936
|
+
if (dirty) {
|
|
11937
|
+
console.error(
|
|
11938
|
+
"terminalhire claim: --here needs a clean working tree (claim work must not mix with your current changes).\n Commit or stash first, or use `terminalhire claim start` for an isolated worktree."
|
|
11939
|
+
);
|
|
11940
|
+
process.exit(1);
|
|
11941
|
+
}
|
|
11942
|
+
const issueNumber = (parseGitHubUrl(claim.issueUrl) || {}).number;
|
|
11943
|
+
const branch = startBranchFor(claim.repoFullName, issueNumber);
|
|
11944
|
+
await sh("git", ["-C", toplevel, "checkout", "-b", branch]);
|
|
11945
|
+
claims.updateClaim(claim.id, { worktreePath: toplevel, branch, state: "working" });
|
|
11946
|
+
console.log(`
|
|
11947
|
+
\u2713 Started here: ${claim.title}`);
|
|
11948
|
+
console.log(` worktree: ${toplevel}`);
|
|
11949
|
+
console.log(` branch: ${branch}`);
|
|
11950
|
+
console.log(`
|
|
11951
|
+
When the work is done: terminalhire claim submit ${claim.id}`);
|
|
11952
|
+
}
|
|
11768
11953
|
async function cmdSubmit(id, flags = {}) {
|
|
11769
11954
|
const worktreeOverride = flags.worktree;
|
|
11770
11955
|
const claims = await Promise.resolve().then(() => (init_claims(), claims_exports));
|
|
@@ -11779,10 +11964,10 @@ async function cmdSubmit(id, flags = {}) {
|
|
|
11779
11964
|
console.error(`terminalhire claim: no claim with id '${id}'.`);
|
|
11780
11965
|
process.exit(1);
|
|
11781
11966
|
}
|
|
11782
|
-
if (claim.state !== "ready") {
|
|
11967
|
+
if (claim.state !== "working" && claim.state !== "ready") {
|
|
11783
11968
|
console.error(
|
|
11784
|
-
`terminalhire claim: ${id} is '${claim.state}'
|
|
11785
|
-
terminalhire claim
|
|
11969
|
+
`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:
|
|
11970
|
+
terminalhire claim start ${id}`
|
|
11786
11971
|
);
|
|
11787
11972
|
process.exit(1);
|
|
11788
11973
|
}
|
|
@@ -12374,6 +12559,9 @@ async function run7() {
|
|
|
12374
12559
|
case "record":
|
|
12375
12560
|
await cmdRecord(positional[0], flags);
|
|
12376
12561
|
break;
|
|
12562
|
+
case "start":
|
|
12563
|
+
await cmdStart(positional[0], flags);
|
|
12564
|
+
break;
|
|
12377
12565
|
case "list":
|
|
12378
12566
|
await cmdList(active);
|
|
12379
12567
|
break;
|
|
@@ -12393,7 +12581,7 @@ async function run7() {
|
|
|
12393
12581
|
await cmdRelease(positional[0]);
|
|
12394
12582
|
break;
|
|
12395
12583
|
default:
|
|
12396
|
-
console.error(`terminalhire claim: unknown verb '${verb ?? ""}'. Expected: preview | record | attach | list | status | update | submit | release`);
|
|
12584
|
+
console.error(`terminalhire claim: unknown verb '${verb ?? ""}'. Expected: preview | record | start | attach | list | status | update | submit | release`);
|
|
12397
12585
|
process.exit(1);
|
|
12398
12586
|
}
|
|
12399
12587
|
} catch (err) {
|
|
@@ -42869,6 +43057,7 @@ if (!firstArg || firstArg === "help" || firstArg === "--help" || firstArg === "-
|
|
|
42869
43057
|
console.log(" terminalhire bounties --priced Only bounties with a known $ amount");
|
|
42870
43058
|
console.log(" terminalhire contribute Open issues where a merged PR counts toward your r\xE9sum\xE9");
|
|
42871
43059
|
console.log(" terminalhire claim record <id|issueUrl> Claim a bounty locally + print the executor brief");
|
|
43060
|
+
console.log(" terminalhire claim start [<id>] Fork + clone + branch into an isolated worktree (never cd's you)");
|
|
42872
43061
|
console.log(" terminalhire claim list [--active] List your claims + accepted-PR rate");
|
|
42873
43062
|
console.log(" terminalhire claim status [<id>] Poll source PR merge state (updates the metric)");
|
|
42874
43063
|
console.log(" terminalhire claim --push Opt-in: show your claims on your dashboard (typed-yes + browser confirm)");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "terminalhire",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "Local-first job matching for developers — ambient job matches in the Claude Code spinner. Matching runs on your machine; your profile never leaves it.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|