@tablation/crew 0.9.0 → 0.10.1
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/cli.js
CHANGED
|
@@ -3955,6 +3955,13 @@ function fastForward(cwd, branch) {
|
|
|
3955
3955
|
if (!s.canFastForward) return false;
|
|
3956
3956
|
return gitOk(cwd, ["merge", "--ff-only", s.upstream]) !== null;
|
|
3957
3957
|
}
|
|
3958
|
+
function baseBranchUnsafe(r) {
|
|
3959
|
+
return r.action === "diverged" || r.action === "ahead" || r.action === "ff-failed";
|
|
3960
|
+
}
|
|
3961
|
+
function describeUnsafeBase(r, remote, base) {
|
|
3962
|
+
const counts = r.action === "diverged" ? `${r.ahead} ahead, ${r.behind} behind ${remote}/${base}` : r.action === "ahead" ? `${r.ahead} ahead of ${remote}/${base}` : `${r.behind} behind ${remote}/${base}, cannot fast-forward`;
|
|
3963
|
+
return `${r.detail} [${counts}] \u2014 reconcile it (\`crew sync\` afterwards), or \`git reset --hard ${remote}/${base}\` if the local commits duplicate a release`;
|
|
3964
|
+
}
|
|
3958
3965
|
function refreshBaseBranch(cwd, remote, base, dryRun = false) {
|
|
3959
3966
|
if (currentBranch(cwd) !== base || status(cwd).length > 0) {
|
|
3960
3967
|
return { action: "not-applicable", detail: `${cwd} is not on a clean ${base}` };
|
|
@@ -3981,6 +3988,9 @@ function refreshBaseBranch(cwd, remote, base, dryRun = false) {
|
|
|
3981
3988
|
detail: `${base} has diverged from ${upstream} (${ahead} ahead, ${behind} behind)`
|
|
3982
3989
|
};
|
|
3983
3990
|
}
|
|
3991
|
+
if (behind === 0 && ahead > 0) {
|
|
3992
|
+
return { action: "ahead", ahead, detail: `${base} has ${ahead} commit(s) ${upstream} does not` };
|
|
3993
|
+
}
|
|
3984
3994
|
if (behind === 0) return { action: "level", detail: `${base} is level with ${upstream}` };
|
|
3985
3995
|
if (dryRun) {
|
|
3986
3996
|
return { action: "would-fast-forward", behind, detail: `would fast-forward ${base}: ${behind} behind ${upstream}` };
|
|
@@ -5979,9 +5989,13 @@ function refreshBase(o) {
|
|
|
5979
5989
|
// base. Fast-forwarding is impossible and merging would be this crew
|
|
5980
5990
|
// inventing a resolution nobody asked for, on the branch everything
|
|
5981
5991
|
// ships from.
|
|
5992
|
+
// AHEAD is a stop too (CREW-1379): local commits nobody asked for on the
|
|
5993
|
+
// base are how a second ship's release came to diverge from origin's.
|
|
5994
|
+
// Releasing on top of a hand commit is the failure; reconcile it first.
|
|
5995
|
+
case "ahead":
|
|
5982
5996
|
case "diverged":
|
|
5983
5997
|
case "ff-failed":
|
|
5984
|
-
return { ok: false, why: r.
|
|
5998
|
+
return { ok: false, why: describeUnsafeBase(r, o.repo.branch.remote, o.repo.branch.base) };
|
|
5985
5999
|
}
|
|
5986
6000
|
}
|
|
5987
6001
|
async function reconfirmLastRelease(o, decision) {
|
|
@@ -6825,14 +6839,30 @@ async function fetchDivergedPrompt(route, role, opts) {
|
|
|
6825
6839
|
headers: { "User-Agent": opts.userAgent ?? "Mozilla/5.0 TablationCrewAgent/1.0" }
|
|
6826
6840
|
});
|
|
6827
6841
|
let row;
|
|
6842
|
+
let agentsModelId;
|
|
6828
6843
|
try {
|
|
6829
6844
|
const agentsModel = await client2.dataModels.get("agents", route.resolved.workspaceId);
|
|
6830
|
-
|
|
6845
|
+
agentsModelId = agentsModel.id;
|
|
6846
|
+
row = await client2.records.get(agentsModelId, agentId);
|
|
6831
6847
|
} catch {
|
|
6832
6848
|
return void 0;
|
|
6833
6849
|
}
|
|
6834
6850
|
const defaultPrompt = personaDefaultPrompt(route.promptsDir, role);
|
|
6835
|
-
|
|
6851
|
+
if (row.prompt === defaultPrompt) return void 0;
|
|
6852
|
+
const cached = route.resolved.agentPersonas?.[role];
|
|
6853
|
+
if (cached?.agentId === agentId && cached.historyId) {
|
|
6854
|
+
const lastSynced = await fetchLastSyncedPrompt(route, agentsModelId, agentId, cached.historyId, opts.userAgent);
|
|
6855
|
+
if (lastSynced !== void 0 && lastSynced === row.prompt) return void 0;
|
|
6856
|
+
}
|
|
6857
|
+
return row.prompt;
|
|
6858
|
+
}
|
|
6859
|
+
async function fetchLastSyncedPrompt(route, agentsModelId, recordId, historyId, userAgent) {
|
|
6860
|
+
try {
|
|
6861
|
+
const entries = await fetchHistoryEntries(route, agentsModelId, recordId, userAgent);
|
|
6862
|
+
return entries.find((e) => e.history_id === historyId)?.prompt;
|
|
6863
|
+
} catch {
|
|
6864
|
+
return void 0;
|
|
6865
|
+
}
|
|
6836
6866
|
}
|
|
6837
6867
|
async function fetchSeatAgentModel(route, role, opts) {
|
|
6838
6868
|
const agentId = await resolveAgentId(route, role, opts);
|
|
@@ -9270,6 +9300,11 @@ if (command === "passenger-sync-daemon") {
|
|
|
9270
9300
|
if (!why) return false;
|
|
9271
9301
|
emit.emit(`${why} \u2014 release skipped`, { step: "release" });
|
|
9272
9302
|
return true;
|
|
9303
|
+
}, baseHealth = function(r) {
|
|
9304
|
+
const { remote, base } = r.config.branch;
|
|
9305
|
+
const out = refreshBaseBranch(r.dir, remote, base, true);
|
|
9306
|
+
const unsafe = baseBranchUnsafe(out);
|
|
9307
|
+
return { action: out.action, unsafe, detail: unsafe ? describeUnsafeBase(out, remote, base) : out.detail };
|
|
9273
9308
|
}, reportUnplaceable = function(c, unplaceable, verified, emit2) {
|
|
9274
9309
|
if (unplaceableReported.has(c.route)) return;
|
|
9275
9310
|
unplaceableReported.add(c.route);
|
|
@@ -9278,7 +9313,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
9278
9313
|
else emit2.emit(note.message, { step: "release" });
|
|
9279
9314
|
}
|
|
9280
9315
|
};
|
|
9281
|
-
requireArmed2 = requireArmed, parseTicketNumber2 = parseTicketNumber, passengerApiKeys2 = passengerApiKeys, endpointApiFor2 = endpointApiFor, endpointReconcileDeps2 = endpointReconcileDeps, releaseRefused2 = releaseRefused, reportUnplaceable2 = reportUnplaceable;
|
|
9316
|
+
requireArmed2 = requireArmed, parseTicketNumber2 = parseTicketNumber, passengerApiKeys2 = passengerApiKeys, endpointApiFor2 = endpointApiFor, endpointReconcileDeps2 = endpointReconcileDeps, releaseRefused2 = releaseRefused, baseHealth2 = baseHealth, reportUnplaceable2 = reportUnplaceable;
|
|
9282
9317
|
let cfg;
|
|
9283
9318
|
let firstRun;
|
|
9284
9319
|
try {
|
|
@@ -11318,14 +11353,16 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
11318
11353
|
let acted = 0;
|
|
11319
11354
|
let baseChecked = 0;
|
|
11320
11355
|
let baseActed = 0;
|
|
11356
|
+
let baseUnsafe = 0;
|
|
11321
11357
|
for (const r of await resolvedRepos(route)) {
|
|
11322
11358
|
const base = refreshBaseBranch(r.dir, r.config.branch.remote, r.config.branch.base, dryRun);
|
|
11323
11359
|
if (base.action !== "not-applicable") {
|
|
11324
11360
|
baseChecked++;
|
|
11325
11361
|
if (base.action === "fetch-failed") emit.warn(base.detail);
|
|
11326
|
-
else if (base
|
|
11362
|
+
else if (baseBranchUnsafe(base)) {
|
|
11327
11363
|
baseActed++;
|
|
11328
|
-
|
|
11364
|
+
baseUnsafe++;
|
|
11365
|
+
emit.error(`${r.name}: DO NOT CUT A WORKTREE FROM THIS BASE \u2014 ${describeUnsafeBase(base, r.config.branch.remote, r.config.branch.base)}`);
|
|
11329
11366
|
} else if (base.action === "would-fast-forward" || base.action === "fast-forwarded") {
|
|
11330
11367
|
baseActed++;
|
|
11331
11368
|
emit.emit(base.detail);
|
|
@@ -11357,6 +11394,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
11357
11394
|
}
|
|
11358
11395
|
}
|
|
11359
11396
|
}
|
|
11397
|
+
if (baseUnsafe > 0) process.exitCode = 1;
|
|
11360
11398
|
if (baseChecked > 0 && baseActed === 0) emit.emit(`${baseChecked} base branch(es) checked, all level with the remote`);
|
|
11361
11399
|
if (tracked === 0) emit.emit("no worktree tracks a remote branch \u2014 nothing to sync");
|
|
11362
11400
|
else if (acted === 0) emit.emit(`${tracked} worktree(s) tracking a remote, all level with it`);
|
|
@@ -11487,6 +11525,9 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
11487
11525
|
repos: repos.map((r) => ({
|
|
11488
11526
|
name: r.name,
|
|
11489
11527
|
dir: r.dir,
|
|
11528
|
+
// Additive (CREW-1379): `unsafe` means a ship must not cut a
|
|
11529
|
+
// worktree or release from this base. `version` stays 1.
|
|
11530
|
+
baseBranch: baseHealth(r),
|
|
11490
11531
|
worktreePrefix: r.config.worktrees.prefix,
|
|
11491
11532
|
// A repo with no explicit `worktrees.prefix` no longer names its
|
|
11492
11533
|
// worktrees `<prefix><number>` (ISSUE-969) — the checkout's own
|
|
@@ -11527,6 +11568,11 @@ watermark: ${state.watermark()}
|
|
|
11527
11568
|
}
|
|
11528
11569
|
for (const i of openShipAttention(state)) {
|
|
11529
11570
|
process.stdout.write(`attention: ${i.kind} \u2014 ${i.message}
|
|
11571
|
+
`);
|
|
11572
|
+
}
|
|
11573
|
+
for (const r of await resolvedRepos(route)) {
|
|
11574
|
+
const bh = baseHealth(r);
|
|
11575
|
+
if (bh.unsafe) process.stdout.write(`base: ${r.name} \u2014 UNSAFE, no worktree cuts or releases: ${bh.detail}
|
|
11530
11576
|
`);
|
|
11531
11577
|
}
|
|
11532
11578
|
if (waiting) {
|
|
@@ -11581,6 +11627,12 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
|
|
|
11581
11627
|
process.stdout.write(` - ${p2}
|
|
11582
11628
|
`);
|
|
11583
11629
|
}
|
|
11630
|
+
const bh = baseHealth(r);
|
|
11631
|
+
if (bh.action !== "not-applicable") {
|
|
11632
|
+
process.stdout.write(` base ${config.branch.base}: ${bh.unsafe ? "ERROR \u2014 " : ""}${bh.detail}
|
|
11633
|
+
`);
|
|
11634
|
+
if (bh.unsafe) process.exitCode = 1;
|
|
11635
|
+
}
|
|
11584
11636
|
}
|
|
11585
11637
|
const missingAuthorship = await tracker.missingAuthorshipColumns();
|
|
11586
11638
|
process.stdout.write(
|
|
@@ -11761,4 +11813,5 @@ var passengerApiKeys2;
|
|
|
11761
11813
|
var endpointApiFor2;
|
|
11762
11814
|
var endpointReconcileDeps2;
|
|
11763
11815
|
var releaseRefused2;
|
|
11816
|
+
var baseHealth2;
|
|
11764
11817
|
var reportUnplaceable2;
|
package/package.json
CHANGED
|
@@ -346,10 +346,13 @@ if `report_type` is either of those.
|
|
|
346
346
|
missing commits another ship or a reviewer pushed since the last sync. It
|
|
347
347
|
is quiet, not an alert, about a primary checkout that isn't cleanly on the
|
|
348
348
|
base branch (something else may be mid-edit there) — that's expected, not
|
|
349
|
-
a fault, and just means this sync is a no-op. A
|
|
350
|
-
branch
|
|
351
|
-
|
|
352
|
-
|
|
349
|
+
a fault, and just means this sync is a no-op. A DIVERGED base
|
|
350
|
+
branch (local commits the remote lacks, or one that cannot fast-forward)
|
|
351
|
+
makes `crew sync` print `DO NOT CUT A WORKTREE FROM THIS BASE` and exit
|
|
352
|
+
non-zero. That is a hard stop: do not run `git worktree add`, do not try to
|
|
353
|
+
reconcile it yourself. Post a comment on the ticket naming the repo and the
|
|
354
|
+
counts `crew sync` printed, leave the ticket as you found it, and end the
|
|
355
|
+
run — the operator reconciles the checkout. Otherwise, from
|
|
353
356
|
the primary checkout, `git worktree add` a sibling worktree for this
|
|
354
357
|
ticket, on a new branch cut from the base branch's current HEAD. The
|
|
355
358
|
digest's `worktree` and `branch` columns give this ticket's own directory
|
|
@@ -327,10 +327,13 @@ if `report_type` is either of those.
|
|
|
327
327
|
missing commits another ship or a reviewer pushed since the last sync. It
|
|
328
328
|
is quiet, not an alert, about a primary checkout that isn't cleanly on the
|
|
329
329
|
base branch (something else may be mid-edit there) — that's expected, not
|
|
330
|
-
a fault, and just means this sync is a no-op. A
|
|
331
|
-
branch
|
|
332
|
-
|
|
333
|
-
|
|
330
|
+
a fault, and just means this sync is a no-op. A DIVERGED base
|
|
331
|
+
branch (local commits the remote lacks, or one that cannot fast-forward)
|
|
332
|
+
makes `crew sync` print `DO NOT CUT A WORKTREE FROM THIS BASE` and exit
|
|
333
|
+
non-zero. That is a hard stop: do not run `git worktree add`, do not try to
|
|
334
|
+
reconcile it yourself. Post a comment on the ticket naming the repo and the
|
|
335
|
+
counts `crew sync` printed, leave the ticket as you found it, and end the
|
|
336
|
+
run — the operator reconciles the checkout. Otherwise, from
|
|
334
337
|
the primary checkout, `git worktree add` a sibling worktree for this
|
|
335
338
|
ticket, on a new branch cut from the base branch's current HEAD. The
|
|
336
339
|
digest's `worktree` and `branch` columns give this ticket's own directory
|