terminalhire 0.40.3 → 0.40.5
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/claim-push-bg.js +18 -1
- package/dist/bin/jpi-bounties.js +32 -6
- package/dist/bin/jpi-chat.js +14 -5
- package/dist/bin/jpi-claim.js +70 -8
- package/dist/bin/jpi-contribute.js +32 -6
- package/dist/bin/jpi-devs.js +14 -5
- package/dist/bin/jpi-dispatch.js +89 -24
- package/dist/bin/jpi-hub.js +32 -6
- package/dist/bin/jpi-inbox.js +14 -5
- package/dist/bin/jpi-init.js +18 -15
- package/dist/bin/jpi-intro.js +14 -5
- package/dist/bin/jpi-jobs.js +14 -5
- package/dist/bin/jpi-learn.js +14 -5
- package/dist/bin/jpi-login.js +14 -5
- package/dist/bin/jpi-mcp.js +71 -9
- package/dist/bin/jpi-profile.js +14 -5
- package/dist/bin/jpi-refresh.js +32 -6
- package/dist/bin/jpi-repo.js +32 -6
- package/dist/bin/jpi-save.js +14 -5
- package/dist/bin/jpi-statusline.js +35 -1
- package/dist/bin/jpi-sync.js +14 -5
- package/dist/bin/jpi-trajectory.js +14 -5
- package/dist/src/claims.js +17 -0
- package/dist/src/crypto-store.js +14 -5
- package/dist/src/intro.js +14 -5
- package/dist/src/profile.js +14 -5
- package/dist/src/repo-experience.js +14 -5
- package/dist/src/trajectory.js +14 -5
- package/package.json +1 -1
- package/statusline-install.js +35 -11
|
@@ -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",
|
package/dist/bin/jpi-bounties.js
CHANGED
|
@@ -10618,6 +10618,14 @@ function warnStderr(message) {
|
|
|
10618
10618
|
process.stderr.write(`${message}
|
|
10619
10619
|
`);
|
|
10620
10620
|
}
|
|
10621
|
+
function makeWarnOnce() {
|
|
10622
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10623
|
+
return (message) => {
|
|
10624
|
+
if (seen.has(message)) return;
|
|
10625
|
+
seen.add(message);
|
|
10626
|
+
warnStderr(message);
|
|
10627
|
+
};
|
|
10628
|
+
}
|
|
10621
10629
|
function atomicWriteFileSync(filePath, content) {
|
|
10622
10630
|
const dir = dirname(filePath);
|
|
10623
10631
|
ensureStateDirForSecret(dir);
|
|
@@ -10663,11 +10671,11 @@ async function deleteKey() {
|
|
|
10663
10671
|
if (e.code !== "ENOENT") throw e;
|
|
10664
10672
|
}
|
|
10665
10673
|
}
|
|
10666
|
-
async function resolveKey(filePath, opts) {
|
|
10674
|
+
async function resolveKey(filePath, opts, warnOnce) {
|
|
10667
10675
|
if (opts.keyPolicy === "keychain-required") {
|
|
10668
10676
|
const key = await tryLoadFromKeytar();
|
|
10669
10677
|
if (!key) {
|
|
10670
|
-
|
|
10678
|
+
warnOnce(
|
|
10671
10679
|
`crypto-store: OS keychain unavailable \u2014 store at ${filePath} is disabled (no plaintext key file will be written)`
|
|
10672
10680
|
);
|
|
10673
10681
|
return null;
|
|
@@ -10677,8 +10685,9 @@ async function resolveKey(filePath, opts) {
|
|
|
10677
10685
|
return loadOrCreateSharedKey();
|
|
10678
10686
|
}
|
|
10679
10687
|
function createEncryptedStore(filePath, opts) {
|
|
10688
|
+
const warnOnce = makeWarnOnce();
|
|
10680
10689
|
async function read() {
|
|
10681
|
-
const key = await resolveKey(filePath, opts);
|
|
10690
|
+
const key = await resolveKey(filePath, opts, warnOnce);
|
|
10682
10691
|
if (!key) return opts.blank();
|
|
10683
10692
|
if (!existsSync3(filePath)) return opts.blank();
|
|
10684
10693
|
try {
|
|
@@ -10687,12 +10696,12 @@ function createEncryptedStore(filePath, opts) {
|
|
|
10687
10696
|
const plaintext = decrypt(blob, key);
|
|
10688
10697
|
return JSON.parse(plaintext);
|
|
10689
10698
|
} catch {
|
|
10690
|
-
|
|
10699
|
+
warnOnce(`crypto-store: failed to decrypt ${filePath} \u2014 returning blank`);
|
|
10691
10700
|
return opts.blank();
|
|
10692
10701
|
}
|
|
10693
10702
|
}
|
|
10694
10703
|
async function write(value) {
|
|
10695
|
-
const key = await resolveKey(filePath, opts);
|
|
10704
|
+
const key = await resolveKey(filePath, opts, warnOnce);
|
|
10696
10705
|
if (!key) return;
|
|
10697
10706
|
const blob = encrypt(JSON.stringify(value), key);
|
|
10698
10707
|
atomicWriteFileSync(filePath, JSON.stringify(blob, null, 2));
|
|
@@ -11122,6 +11131,7 @@ var init_repo_experience = __esm({
|
|
|
11122
11131
|
// src/claims.ts
|
|
11123
11132
|
var claims_exports = {};
|
|
11124
11133
|
__export(claims_exports, {
|
|
11134
|
+
CLAIM_STATES: () => CLAIM_STATES,
|
|
11125
11135
|
PUSHED_CLAIM_FIELDS: () => PUSHED_CLAIM_FIELDS,
|
|
11126
11136
|
acceptedPRRate: () => acceptedPRRate,
|
|
11127
11137
|
countAwaitingFounderApproval: () => countAwaitingFounderApproval,
|
|
@@ -11320,7 +11330,7 @@ function acceptedPRRate(claims = readClaims()) {
|
|
|
11320
11330
|
const merged = claims.filter((c) => c.state === "merged").length;
|
|
11321
11331
|
return { merged, total, rate: total === 0 ? 0 : merged / total };
|
|
11322
11332
|
}
|
|
11323
|
-
var TERMINALHIRE_DIR5, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS;
|
|
11333
|
+
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
11334
|
var init_claims = __esm({
|
|
11325
11335
|
"src/claims.ts"() {
|
|
11326
11336
|
"use strict";
|
|
@@ -11331,6 +11341,22 @@ var init_claims = __esm({
|
|
|
11331
11341
|
LOCK_STALE_MS = Number(process.env.TERMINALHIRE_LOCK_STALE_MS) || 1e4;
|
|
11332
11342
|
LOCK_RETRY_MS = Number(process.env.TERMINALHIRE_LOCK_RETRY_MS) || 25;
|
|
11333
11343
|
LOCK_TIMEOUT_MS = Number(process.env.TERMINALHIRE_LOCK_TIMEOUT_MS) || 5e3;
|
|
11344
|
+
CLAIM_STATES = Object.freeze([
|
|
11345
|
+
"claimed",
|
|
11346
|
+
// recorded, not started
|
|
11347
|
+
"working",
|
|
11348
|
+
// background executor running / work in progress
|
|
11349
|
+
"in-review",
|
|
11350
|
+
// gate ran / dev reviewing the diff
|
|
11351
|
+
"ready",
|
|
11352
|
+
// passed review, cleared to submit
|
|
11353
|
+
"submitted",
|
|
11354
|
+
// PR opened on the source platform (awaiting maintainer)
|
|
11355
|
+
"merged",
|
|
11356
|
+
// PR merged — accepted; counts toward the metric
|
|
11357
|
+
"abandoned"
|
|
11358
|
+
// released, or PR closed unmerged
|
|
11359
|
+
]);
|
|
11334
11360
|
PUSHED_CLAIM_FIELDS = [
|
|
11335
11361
|
"kind",
|
|
11336
11362
|
"repoFullName",
|
package/dist/bin/jpi-chat.js
CHANGED
|
@@ -5478,6 +5478,14 @@ function warnStderr(message) {
|
|
|
5478
5478
|
process.stderr.write(`${message}
|
|
5479
5479
|
`);
|
|
5480
5480
|
}
|
|
5481
|
+
function makeWarnOnce() {
|
|
5482
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5483
|
+
return (message) => {
|
|
5484
|
+
if (seen.has(message)) return;
|
|
5485
|
+
seen.add(message);
|
|
5486
|
+
warnStderr(message);
|
|
5487
|
+
};
|
|
5488
|
+
}
|
|
5481
5489
|
function atomicWriteFileSync(filePath, content) {
|
|
5482
5490
|
const dir = dirname(filePath);
|
|
5483
5491
|
ensureStateDirForSecret(dir);
|
|
@@ -5523,11 +5531,11 @@ async function deleteKey() {
|
|
|
5523
5531
|
if (e.code !== "ENOENT") throw e;
|
|
5524
5532
|
}
|
|
5525
5533
|
}
|
|
5526
|
-
async function resolveKey(filePath, opts) {
|
|
5534
|
+
async function resolveKey(filePath, opts, warnOnce) {
|
|
5527
5535
|
if (opts.keyPolicy === "keychain-required") {
|
|
5528
5536
|
const key = await tryLoadFromKeytar();
|
|
5529
5537
|
if (!key) {
|
|
5530
|
-
|
|
5538
|
+
warnOnce(
|
|
5531
5539
|
`crypto-store: OS keychain unavailable \u2014 store at ${filePath} is disabled (no plaintext key file will be written)`
|
|
5532
5540
|
);
|
|
5533
5541
|
return null;
|
|
@@ -5537,8 +5545,9 @@ async function resolveKey(filePath, opts) {
|
|
|
5537
5545
|
return loadOrCreateSharedKey();
|
|
5538
5546
|
}
|
|
5539
5547
|
function createEncryptedStore(filePath, opts) {
|
|
5548
|
+
const warnOnce = makeWarnOnce();
|
|
5540
5549
|
async function read() {
|
|
5541
|
-
const key = await resolveKey(filePath, opts);
|
|
5550
|
+
const key = await resolveKey(filePath, opts, warnOnce);
|
|
5542
5551
|
if (!key) return opts.blank();
|
|
5543
5552
|
if (!existsSync9(filePath)) return opts.blank();
|
|
5544
5553
|
try {
|
|
@@ -5547,12 +5556,12 @@ function createEncryptedStore(filePath, opts) {
|
|
|
5547
5556
|
const plaintext = decrypt2(blob, key);
|
|
5548
5557
|
return JSON.parse(plaintext);
|
|
5549
5558
|
} catch {
|
|
5550
|
-
|
|
5559
|
+
warnOnce(`crypto-store: failed to decrypt ${filePath} \u2014 returning blank`);
|
|
5551
5560
|
return opts.blank();
|
|
5552
5561
|
}
|
|
5553
5562
|
}
|
|
5554
5563
|
async function write(value) {
|
|
5555
|
-
const key = await resolveKey(filePath, opts);
|
|
5564
|
+
const key = await resolveKey(filePath, opts, warnOnce);
|
|
5556
5565
|
if (!key) return;
|
|
5557
5566
|
const blob = encrypt2(JSON.stringify(value), key);
|
|
5558
5567
|
atomicWriteFileSync(filePath, JSON.stringify(blob, null, 2));
|
package/dist/bin/jpi-claim.js
CHANGED
|
@@ -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",
|
|
@@ -24814,6 +24831,14 @@ function warnStderr(message) {
|
|
|
24814
24831
|
process.stderr.write(`${message}
|
|
24815
24832
|
`);
|
|
24816
24833
|
}
|
|
24834
|
+
function makeWarnOnce() {
|
|
24835
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24836
|
+
return (message) => {
|
|
24837
|
+
if (seen.has(message)) return;
|
|
24838
|
+
seen.add(message);
|
|
24839
|
+
warnStderr(message);
|
|
24840
|
+
};
|
|
24841
|
+
}
|
|
24817
24842
|
function atomicWriteFileSync(filePath, content) {
|
|
24818
24843
|
const dir = dirname4(filePath);
|
|
24819
24844
|
ensureStateDirForSecret(dir);
|
|
@@ -24859,11 +24884,11 @@ async function deleteKey() {
|
|
|
24859
24884
|
if (e.code !== "ENOENT") throw e;
|
|
24860
24885
|
}
|
|
24861
24886
|
}
|
|
24862
|
-
async function resolveKey(filePath, opts) {
|
|
24887
|
+
async function resolveKey(filePath, opts, warnOnce) {
|
|
24863
24888
|
if (opts.keyPolicy === "keychain-required") {
|
|
24864
24889
|
const key = await tryLoadFromKeytar();
|
|
24865
24890
|
if (!key) {
|
|
24866
|
-
|
|
24891
|
+
warnOnce(
|
|
24867
24892
|
`crypto-store: OS keychain unavailable \u2014 store at ${filePath} is disabled (no plaintext key file will be written)`
|
|
24868
24893
|
);
|
|
24869
24894
|
return null;
|
|
@@ -24873,8 +24898,9 @@ async function resolveKey(filePath, opts) {
|
|
|
24873
24898
|
return loadOrCreateSharedKey();
|
|
24874
24899
|
}
|
|
24875
24900
|
function createEncryptedStore(filePath, opts) {
|
|
24901
|
+
const warnOnce = makeWarnOnce();
|
|
24876
24902
|
async function read() {
|
|
24877
|
-
const key = await resolveKey(filePath, opts);
|
|
24903
|
+
const key = await resolveKey(filePath, opts, warnOnce);
|
|
24878
24904
|
if (!key) return opts.blank();
|
|
24879
24905
|
if (!existsSync6(filePath)) return opts.blank();
|
|
24880
24906
|
try {
|
|
@@ -24883,12 +24909,12 @@ function createEncryptedStore(filePath, opts) {
|
|
|
24883
24909
|
const plaintext = decrypt2(blob, key);
|
|
24884
24910
|
return JSON.parse(plaintext);
|
|
24885
24911
|
} catch {
|
|
24886
|
-
|
|
24912
|
+
warnOnce(`crypto-store: failed to decrypt ${filePath} \u2014 returning blank`);
|
|
24887
24913
|
return opts.blank();
|
|
24888
24914
|
}
|
|
24889
24915
|
}
|
|
24890
24916
|
async function write(value) {
|
|
24891
|
-
const key = await resolveKey(filePath, opts);
|
|
24917
|
+
const key = await resolveKey(filePath, opts, warnOnce);
|
|
24892
24918
|
if (!key) return;
|
|
24893
24919
|
const blob = encrypt2(JSON.stringify(value), key);
|
|
24894
24920
|
atomicWriteFileSync(filePath, JSON.stringify(blob, null, 2));
|
|
@@ -26705,6 +26731,38 @@ function printMetric(rate) {
|
|
|
26705
26731
|
console.log(`
|
|
26706
26732
|
\u{1F4CA} Accepted-PR rate: ${rate.merged}/${rate.total} claims merged (${pct}%)`);
|
|
26707
26733
|
}
|
|
26734
|
+
var SUBMIT_ACCEPTS = Object.freeze(["working", "ready"]);
|
|
26735
|
+
function nextStepFor(c) {
|
|
26736
|
+
const founder = Boolean(c.approval);
|
|
26737
|
+
switch (c.state) {
|
|
26738
|
+
case "claimed":
|
|
26739
|
+
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" };
|
|
26740
|
+
// NOT grouped with the two below. `cmdSubmit` accepts 'working' and 'ready'
|
|
26741
|
+
// and nothing else, so pointing an 'in-review' claim at submit would hand the
|
|
26742
|
+
// developer a command that exits 1 — and submit's own refusal then advises
|
|
26743
|
+
// `claim start`, which a founder claim can never use either. Two dead ends in
|
|
26744
|
+
// a row is worse than the silence this block replaced.
|
|
26745
|
+
case "in-review":
|
|
26746
|
+
return {
|
|
26747
|
+
cmd: `terminalhire claim update ${c.id} ready`,
|
|
26748
|
+
why: "clear the review gate \u2014 submit only accepts working/ready"
|
|
26749
|
+
};
|
|
26750
|
+
case "working":
|
|
26751
|
+
case "ready":
|
|
26752
|
+
return { cmd: `terminalhire claim submit ${c.id}`, why: "push the branch + open the PR" };
|
|
26753
|
+
case "submitted":
|
|
26754
|
+
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" };
|
|
26755
|
+
default:
|
|
26756
|
+
return null;
|
|
26757
|
+
}
|
|
26758
|
+
}
|
|
26759
|
+
function printNextSteps(list) {
|
|
26760
|
+
const steps = list.map(nextStepFor).filter(Boolean);
|
|
26761
|
+
if (steps.length === 0) return;
|
|
26762
|
+
const width = Math.max(...steps.map((s) => s.cmd.length));
|
|
26763
|
+
console.log("\n Next:");
|
|
26764
|
+
for (const s of steps) console.log(` ${s.cmd.padEnd(width)} \u2014 ${s.why}`);
|
|
26765
|
+
}
|
|
26708
26766
|
async function resolveBounty(arg) {
|
|
26709
26767
|
let bountyId, title, repoFullName, issueUrl, amountUSD, source, openPRsAtDiscovery, indexNativeId;
|
|
26710
26768
|
let founderPosting = false;
|
|
@@ -27276,6 +27334,7 @@ ${list.length} ${active ? "active " : ""}claim${list.length === 1 ? "" : "s"}:
|
|
|
27276
27334
|
console.log(` id: ${c.id}${pr}`);
|
|
27277
27335
|
}
|
|
27278
27336
|
printMetric(claims.acceptedPRRate());
|
|
27337
|
+
printNextSteps(list);
|
|
27279
27338
|
try {
|
|
27280
27339
|
if (await shouldNudgeUnpushed()) {
|
|
27281
27340
|
console.log(
|
|
@@ -27424,7 +27483,7 @@ async function cmdStatus(id) {
|
|
|
27424
27483
|
}
|
|
27425
27484
|
async function cmdUpdate(id, state, prUrl) {
|
|
27426
27485
|
const claims = await Promise.resolve().then(() => (init_claims(), claims_exports));
|
|
27427
|
-
const VALID =
|
|
27486
|
+
const VALID = claims.CLAIM_STATES;
|
|
27428
27487
|
if (!id || !state || !VALID.includes(state)) {
|
|
27429
27488
|
console.error("Usage: terminalhire claim update <id> <state> [prUrl]");
|
|
27430
27489
|
console.error(" state: " + VALID.join(" | "));
|
|
@@ -28284,7 +28343,7 @@ async function cmdSubmit(id, flags = {}) {
|
|
|
28284
28343
|
console.error(`terminalhire claim: no claim with id '${id}'.`);
|
|
28285
28344
|
process.exit(1);
|
|
28286
28345
|
}
|
|
28287
|
-
if (
|
|
28346
|
+
if (!SUBMIT_ACCEPTS.includes(claim.state)) {
|
|
28288
28347
|
console.error(
|
|
28289
28348
|
`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
28349
|
terminalhire claim start ${id}`
|
|
@@ -29236,6 +29295,7 @@ async function run() {
|
|
|
29236
29295
|
export {
|
|
29237
29296
|
AI_DISCLOSURE_NOTE,
|
|
29238
29297
|
CLAIM_CONSENT_VERSION,
|
|
29298
|
+
SUBMIT_ACCEPTS,
|
|
29239
29299
|
backgroundEnableFailed,
|
|
29240
29300
|
buildAssignmentComment,
|
|
29241
29301
|
buildPatchSubmission,
|
|
@@ -29261,9 +29321,11 @@ export {
|
|
|
29261
29321
|
listMergedPRsReferencingIssue,
|
|
29262
29322
|
listOpenPRsReferencingIssue,
|
|
29263
29323
|
matchReferencingPrs,
|
|
29324
|
+
nextStepFor,
|
|
29264
29325
|
normalizeIntent,
|
|
29265
29326
|
pickBodySource,
|
|
29266
29327
|
pickExistingPr,
|
|
29328
|
+
printNextSteps,
|
|
29267
29329
|
renderRunView,
|
|
29268
29330
|
renderServerRefusal,
|
|
29269
29331
|
resolveBounty,
|
|
@@ -2742,6 +2742,14 @@ function warnStderr(message) {
|
|
|
2742
2742
|
process.stderr.write(`${message}
|
|
2743
2743
|
`);
|
|
2744
2744
|
}
|
|
2745
|
+
function makeWarnOnce() {
|
|
2746
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2747
|
+
return (message) => {
|
|
2748
|
+
if (seen.has(message)) return;
|
|
2749
|
+
seen.add(message);
|
|
2750
|
+
warnStderr(message);
|
|
2751
|
+
};
|
|
2752
|
+
}
|
|
2745
2753
|
function atomicWriteFileSync(filePath, content) {
|
|
2746
2754
|
const dir = dirname(filePath);
|
|
2747
2755
|
ensureStateDirForSecret(dir);
|
|
@@ -2787,11 +2795,11 @@ async function deleteKey() {
|
|
|
2787
2795
|
if (e.code !== "ENOENT") throw e;
|
|
2788
2796
|
}
|
|
2789
2797
|
}
|
|
2790
|
-
async function resolveKey(filePath, opts) {
|
|
2798
|
+
async function resolveKey(filePath, opts, warnOnce) {
|
|
2791
2799
|
if (opts.keyPolicy === "keychain-required") {
|
|
2792
2800
|
const key = await tryLoadFromKeytar();
|
|
2793
2801
|
if (!key) {
|
|
2794
|
-
|
|
2802
|
+
warnOnce(
|
|
2795
2803
|
`crypto-store: OS keychain unavailable \u2014 store at ${filePath} is disabled (no plaintext key file will be written)`
|
|
2796
2804
|
);
|
|
2797
2805
|
return null;
|
|
@@ -2801,8 +2809,9 @@ async function resolveKey(filePath, opts) {
|
|
|
2801
2809
|
return loadOrCreateSharedKey();
|
|
2802
2810
|
}
|
|
2803
2811
|
function createEncryptedStore(filePath, opts) {
|
|
2812
|
+
const warnOnce = makeWarnOnce();
|
|
2804
2813
|
async function read() {
|
|
2805
|
-
const key = await resolveKey(filePath, opts);
|
|
2814
|
+
const key = await resolveKey(filePath, opts, warnOnce);
|
|
2806
2815
|
if (!key) return opts.blank();
|
|
2807
2816
|
if (!existsSync4(filePath)) return opts.blank();
|
|
2808
2817
|
try {
|
|
@@ -2811,12 +2820,12 @@ function createEncryptedStore(filePath, opts) {
|
|
|
2811
2820
|
const plaintext = decrypt(blob, key);
|
|
2812
2821
|
return JSON.parse(plaintext);
|
|
2813
2822
|
} catch {
|
|
2814
|
-
|
|
2823
|
+
warnOnce(`crypto-store: failed to decrypt ${filePath} \u2014 returning blank`);
|
|
2815
2824
|
return opts.blank();
|
|
2816
2825
|
}
|
|
2817
2826
|
}
|
|
2818
2827
|
async function write(value) {
|
|
2819
|
-
const key = await resolveKey(filePath, opts);
|
|
2828
|
+
const key = await resolveKey(filePath, opts, warnOnce);
|
|
2820
2829
|
if (!key) return;
|
|
2821
2830
|
const blob = encrypt(JSON.stringify(value), key);
|
|
2822
2831
|
atomicWriteFileSync(filePath, JSON.stringify(blob, null, 2));
|
|
@@ -3515,6 +3524,7 @@ var init_repo_experience = __esm({
|
|
|
3515
3524
|
// src/claims.ts
|
|
3516
3525
|
var claims_exports = {};
|
|
3517
3526
|
__export(claims_exports, {
|
|
3527
|
+
CLAIM_STATES: () => CLAIM_STATES,
|
|
3518
3528
|
PUSHED_CLAIM_FIELDS: () => PUSHED_CLAIM_FIELDS,
|
|
3519
3529
|
acceptedPRRate: () => acceptedPRRate,
|
|
3520
3530
|
countAwaitingFounderApproval: () => countAwaitingFounderApproval,
|
|
@@ -3713,7 +3723,7 @@ function acceptedPRRate(claims = readClaims()) {
|
|
|
3713
3723
|
const merged = claims.filter((c) => c.state === "merged").length;
|
|
3714
3724
|
return { merged, total, rate: total === 0 ? 0 : merged / total };
|
|
3715
3725
|
}
|
|
3716
|
-
var TERMINALHIRE_DIR7, CLAIMS_FILE, LOCK_DIR, LOCK_STALE_MS, LOCK_RETRY_MS, LOCK_TIMEOUT_MS, PUSHED_CLAIM_FIELDS, TERMINAL_STATES, POLL_TRANSITIONS;
|
|
3726
|
+
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
3727
|
var init_claims = __esm({
|
|
3718
3728
|
"src/claims.ts"() {
|
|
3719
3729
|
"use strict";
|
|
@@ -3724,6 +3734,22 @@ var init_claims = __esm({
|
|
|
3724
3734
|
LOCK_STALE_MS = Number(process.env.TERMINALHIRE_LOCK_STALE_MS) || 1e4;
|
|
3725
3735
|
LOCK_RETRY_MS = Number(process.env.TERMINALHIRE_LOCK_RETRY_MS) || 25;
|
|
3726
3736
|
LOCK_TIMEOUT_MS = Number(process.env.TERMINALHIRE_LOCK_TIMEOUT_MS) || 5e3;
|
|
3737
|
+
CLAIM_STATES = Object.freeze([
|
|
3738
|
+
"claimed",
|
|
3739
|
+
// recorded, not started
|
|
3740
|
+
"working",
|
|
3741
|
+
// background executor running / work in progress
|
|
3742
|
+
"in-review",
|
|
3743
|
+
// gate ran / dev reviewing the diff
|
|
3744
|
+
"ready",
|
|
3745
|
+
// passed review, cleared to submit
|
|
3746
|
+
"submitted",
|
|
3747
|
+
// PR opened on the source platform (awaiting maintainer)
|
|
3748
|
+
"merged",
|
|
3749
|
+
// PR merged — accepted; counts toward the metric
|
|
3750
|
+
"abandoned"
|
|
3751
|
+
// released, or PR closed unmerged
|
|
3752
|
+
]);
|
|
3727
3753
|
PUSHED_CLAIM_FIELDS = [
|
|
3728
3754
|
"kind",
|
|
3729
3755
|
"repoFullName",
|
package/dist/bin/jpi-devs.js
CHANGED
|
@@ -10566,6 +10566,14 @@ function warnStderr(message) {
|
|
|
10566
10566
|
process.stderr.write(`${message}
|
|
10567
10567
|
`);
|
|
10568
10568
|
}
|
|
10569
|
+
function makeWarnOnce() {
|
|
10570
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10571
|
+
return (message) => {
|
|
10572
|
+
if (seen.has(message)) return;
|
|
10573
|
+
seen.add(message);
|
|
10574
|
+
warnStderr(message);
|
|
10575
|
+
};
|
|
10576
|
+
}
|
|
10569
10577
|
function atomicWriteFileSync(filePath, content) {
|
|
10570
10578
|
const dir = dirname(filePath);
|
|
10571
10579
|
ensureStateDirForSecret(dir);
|
|
@@ -10611,11 +10619,11 @@ async function deleteKey() {
|
|
|
10611
10619
|
if (e.code !== "ENOENT") throw e;
|
|
10612
10620
|
}
|
|
10613
10621
|
}
|
|
10614
|
-
async function resolveKey(filePath, opts) {
|
|
10622
|
+
async function resolveKey(filePath, opts, warnOnce) {
|
|
10615
10623
|
if (opts.keyPolicy === "keychain-required") {
|
|
10616
10624
|
const key = await tryLoadFromKeytar();
|
|
10617
10625
|
if (!key) {
|
|
10618
|
-
|
|
10626
|
+
warnOnce(
|
|
10619
10627
|
`crypto-store: OS keychain unavailable \u2014 store at ${filePath} is disabled (no plaintext key file will be written)`
|
|
10620
10628
|
);
|
|
10621
10629
|
return null;
|
|
@@ -10625,8 +10633,9 @@ async function resolveKey(filePath, opts) {
|
|
|
10625
10633
|
return loadOrCreateSharedKey();
|
|
10626
10634
|
}
|
|
10627
10635
|
function createEncryptedStore(filePath, opts) {
|
|
10636
|
+
const warnOnce = makeWarnOnce();
|
|
10628
10637
|
async function read() {
|
|
10629
|
-
const key = await resolveKey(filePath, opts);
|
|
10638
|
+
const key = await resolveKey(filePath, opts, warnOnce);
|
|
10630
10639
|
if (!key) return opts.blank();
|
|
10631
10640
|
if (!existsSync3(filePath)) return opts.blank();
|
|
10632
10641
|
try {
|
|
@@ -10635,12 +10644,12 @@ function createEncryptedStore(filePath, opts) {
|
|
|
10635
10644
|
const plaintext = decrypt(blob, key);
|
|
10636
10645
|
return JSON.parse(plaintext);
|
|
10637
10646
|
} catch {
|
|
10638
|
-
|
|
10647
|
+
warnOnce(`crypto-store: failed to decrypt ${filePath} \u2014 returning blank`);
|
|
10639
10648
|
return opts.blank();
|
|
10640
10649
|
}
|
|
10641
10650
|
}
|
|
10642
10651
|
async function write(value) {
|
|
10643
|
-
const key = await resolveKey(filePath, opts);
|
|
10652
|
+
const key = await resolveKey(filePath, opts, warnOnce);
|
|
10644
10653
|
if (!key) return;
|
|
10645
10654
|
const blob = encrypt(JSON.stringify(value), key);
|
|
10646
10655
|
atomicWriteFileSync(filePath, JSON.stringify(blob, null, 2));
|