@tablation/crew 0.22.0 → 0.23.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/cli.js +55 -8
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3273,6 +3273,26 @@ function isAutomatic(t, wm) {
|
|
|
3273
3273
|
if (!wm || wm.modes.size === 0) return true;
|
|
3274
3274
|
return modeOf(t, wm) === "automatic";
|
|
3275
3275
|
}
|
|
3276
|
+
function skippedByMode(tickets, wm) {
|
|
3277
|
+
const out = { manual: 0, hybrid: 0 };
|
|
3278
|
+
if (!wm || wm.modes.size === 0) return out;
|
|
3279
|
+
const kept = new Set(withinWorkMode(tickets, wm));
|
|
3280
|
+
for (const t of tickets) {
|
|
3281
|
+
if (kept.has(t)) continue;
|
|
3282
|
+
out[modeOf(t, wm) === "manual" ? "manual" : "hybrid"]++;
|
|
3283
|
+
}
|
|
3284
|
+
return out;
|
|
3285
|
+
}
|
|
3286
|
+
function withoutManualVerified(tickets, verifiedStatus, wm) {
|
|
3287
|
+
if (!wm || wm.modes.size === 0) return tickets;
|
|
3288
|
+
return tickets.filter((t) => t.status !== verifiedStatus || modeOf(t, wm) !== "manual");
|
|
3289
|
+
}
|
|
3290
|
+
function projectModeLabels(rows) {
|
|
3291
|
+
return rows.map((r) => {
|
|
3292
|
+
const name = r.name ?? r.id;
|
|
3293
|
+
return { id: r.id, name, mode: r.mode, label: `${name} (${r.mode})` };
|
|
3294
|
+
});
|
|
3295
|
+
}
|
|
3276
3296
|
|
|
3277
3297
|
// src/board-lock.ts
|
|
3278
3298
|
import { StaleWriteError } from "@tablation/client";
|
|
@@ -3517,6 +3537,12 @@ var Tracker = class {
|
|
|
3517
3537
|
if (!model) return [];
|
|
3518
3538
|
return this.client.records.list(model, { limit: 200 });
|
|
3519
3539
|
}
|
|
3540
|
+
/** Every Projects row with its raw `work_mode`, for `crew status`. */
|
|
3541
|
+
async projectRowsWithModes() {
|
|
3542
|
+
const rows = await this.projectRows();
|
|
3543
|
+
const modes = await this.projectWorkModes();
|
|
3544
|
+
return rows.map((r) => ({ ...r, mode: modes.get(r.id) ?? "automatic" }));
|
|
3545
|
+
}
|
|
3520
3546
|
/** Create a `Repos` row. Throws when the workspace has no Repos table. */
|
|
3521
3547
|
async createRepoRow(fields) {
|
|
3522
3548
|
const model = this.route.resolved?.reposModelId;
|
|
@@ -3929,9 +3955,9 @@ function sweepDiagnostics(tickets, info) {
|
|
|
3929
3955
|
return { dangling, selfBlocked: [...new Set(selfBlocked)] };
|
|
3930
3956
|
}
|
|
3931
3957
|
var describeBlockers = (t, info) => (t.blocked_by ?? []).map((id) => `${info[id]?.issue_id ?? "?"} (${info[id]?.status ?? "unknown"})`).join(", ");
|
|
3932
|
-
function planSweep(tickets, info, blocked) {
|
|
3958
|
+
function planSweep(tickets, info, blocked, wm) {
|
|
3933
3959
|
const steps = [];
|
|
3934
|
-
for (const t of tickets) {
|
|
3960
|
+
for (const t of withinWorkMode(tickets, wm)) {
|
|
3935
3961
|
if (t.status === "accepted" && blocked.has(t.id)) {
|
|
3936
3962
|
steps.push({ action: "park", ticket: t, blockers: describeBlockers(t, info), to: "blocked" });
|
|
3937
3963
|
} else if (t.status === "blocked" && !blocked.has(t.id)) {
|
|
@@ -4652,7 +4678,7 @@ var ms = (iso) => {
|
|
|
4652
4678
|
const n = iso ? Date.parse(iso) : NaN;
|
|
4653
4679
|
return Number.isFinite(n) ? n : 0;
|
|
4654
4680
|
};
|
|
4655
|
-
function planStalled(tickets, comments, ships, contract, holds2 = /* @__PURE__ */ new Set(), now = Date.now()) {
|
|
4681
|
+
function planStalled(tickets, comments, ships, contract, holds2 = /* @__PURE__ */ new Set(), now = Date.now(), wm) {
|
|
4656
4682
|
const watched = /* @__PURE__ */ new Set([contract.statuses.building, contract.statuses.handoff, contract.statuses.verifying]);
|
|
4657
4683
|
const byTicket = /* @__PURE__ */ new Map();
|
|
4658
4684
|
for (const c of comments) {
|
|
@@ -4661,7 +4687,7 @@ function planStalled(tickets, comments, ships, contract, holds2 = /* @__PURE__ *
|
|
|
4661
4687
|
else byTicket.set(c.ticket_id, [c]);
|
|
4662
4688
|
}
|
|
4663
4689
|
const out = [];
|
|
4664
|
-
for (const t of tickets) {
|
|
4690
|
+
for (const t of withinWorkMode(tickets, wm)) {
|
|
4665
4691
|
if (!watched.has(t.status)) continue;
|
|
4666
4692
|
if (t.assignee_id && holds2.has(t.assignee_id)) continue;
|
|
4667
4693
|
const mine = byTicket.get(t.id) ?? [];
|
|
@@ -4785,9 +4811,15 @@ async function decideCycle(o) {
|
|
|
4785
4811
|
if (diag.selfBlocked.length) {
|
|
4786
4812
|
emit.warn(`ticket(s) blocking themselves, parked permanently: ${diag.selfBlocked.join(", ")}`);
|
|
4787
4813
|
}
|
|
4788
|
-
const sweep = planSweep(tickets, info, blocked);
|
|
4814
|
+
const sweep = planSweep(tickets, info, blocked, workModes);
|
|
4789
4815
|
const holdReleases = planHoldReleases(tickets, tracker.contract);
|
|
4790
|
-
const stalled = planStalled(tickets, comments, ships, tracker.contract, holds2);
|
|
4816
|
+
const stalled = planStalled(tickets, comments, ships, tracker.contract, holds2, Date.now(), workModes);
|
|
4817
|
+
const skipped = skippedByMode(tickets, workModes);
|
|
4818
|
+
if (skipped.manual || skipped.hybrid) {
|
|
4819
|
+
emit.emit(`sweeps skipped ${skipped.manual} manual/project-less and ${skipped.hybrid} unassigned hybrid ticket(s)`, {
|
|
4820
|
+
data: { sweepSkipped: skipped }
|
|
4821
|
+
});
|
|
4822
|
+
}
|
|
4791
4823
|
const blockedIssues = tickets.filter((t) => blocked.has(t.id)).map((t) => t.issue_id);
|
|
4792
4824
|
emit.emit(`${blockedIssues.length} ticket(s) parked as blocked`, { data: { blocked: blockedIssues } });
|
|
4793
4825
|
const qaHeld = tickets.filter((t) => t.status === tracker.contract.statuses.verifying).map((t) => t.issue_id);
|
|
@@ -11034,7 +11066,12 @@ ${changed2.map((c) => ` - ${c}
|
|
|
11034
11066
|
let seen = precheckTickets.get(c.route);
|
|
11035
11067
|
if (!seen) precheckTickets.set(c.route, seen = tracker.openTickets());
|
|
11036
11068
|
const { byRepo: preByRepo, unplaceable: preUnplaceable } = ticketsByRepo(c, await seen);
|
|
11037
|
-
const
|
|
11069
|
+
const preModes = { modes: await tracker.projectWorkModes(), agentAssignees: /* @__PURE__ */ new Set() };
|
|
11070
|
+
const pre = withoutManualVerified(
|
|
11071
|
+
preByRepo.get(target.name) ?? [],
|
|
11072
|
+
tracker.contract.statuses.verified,
|
|
11073
|
+
preModes
|
|
11074
|
+
);
|
|
11038
11075
|
reportUnplaceable(c.route, preUnplaceable, tracker.contract.statuses.verified, remit);
|
|
11039
11076
|
const d = decideRelease(target.dir, pre, tracker.contract, {
|
|
11040
11077
|
tagPattern: repo.release.tagPattern ?? "v*",
|
|
@@ -11082,7 +11119,12 @@ ${changed2.map((c) => ` - ${c}
|
|
|
11082
11119
|
}
|
|
11083
11120
|
const all = await tracker.openTickets();
|
|
11084
11121
|
const { byRepo, unplaceable } = ticketsByRepo(c, all);
|
|
11085
|
-
const
|
|
11122
|
+
const releaseModes = { modes: await tracker.projectWorkModes(), agentAssignees: /* @__PURE__ */ new Set() };
|
|
11123
|
+
const tickets = withoutManualVerified(
|
|
11124
|
+
byRepo.get(target.name) ?? [],
|
|
11125
|
+
tracker.contract.statuses.verified,
|
|
11126
|
+
releaseModes
|
|
11127
|
+
);
|
|
11086
11128
|
reportUnplaceable(c.route, unplaceable, tracker.contract.statuses.verified, remit);
|
|
11087
11129
|
const outcome = await runRelease({
|
|
11088
11130
|
cwd: target.dir,
|
|
@@ -12967,6 +13009,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
12967
13009
|
const engagedTickets = engagedTicketIds.length ? await statusTracker.ticketsByIds(engagedTicketIds) : [];
|
|
12968
13010
|
const issueLabelById = new Map(engagedTickets.map((t) => [t.id, t.issue_id]));
|
|
12969
13011
|
const todo = await statusTracker.openTickets().then((open) => operatorTodo(open, statusTracker.contract, route.resolved?.operator ?? "")).catch(() => []);
|
|
13012
|
+
const projects = projectModeLabels(await statusTracker.projectRowsWithModes().catch(() => []));
|
|
12970
13013
|
if (flag("json")) {
|
|
12971
13014
|
const repos = await resolvedRepos(route);
|
|
12972
13015
|
const seatIds = route.resolved?.seats ?? {};
|
|
@@ -13016,6 +13059,8 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
13016
13059
|
watermark: state.watermark(),
|
|
13017
13060
|
route: {
|
|
13018
13061
|
name: route.route,
|
|
13062
|
+
// Additive (CREW-1496): each project and its work mode. `version` stays 1.
|
|
13063
|
+
projects: projects.map((p) => ({ id: p.id, name: p.name, workMode: p.mode })),
|
|
13019
13064
|
dir: route.dir,
|
|
13020
13065
|
enabled: route.enabled,
|
|
13021
13066
|
area: route.area ?? null,
|
|
@@ -13073,6 +13118,8 @@ watermark: ${state.watermark()}
|
|
|
13073
13118
|
if (state.isRolePaused(r)) process.stdout.write(`role ${r}: paused
|
|
13074
13119
|
`);
|
|
13075
13120
|
}
|
|
13121
|
+
if (projects.length) process.stdout.write(`projects: ${projects.map((p) => p.label).join(", ")}
|
|
13122
|
+
`);
|
|
13076
13123
|
for (const i of openShipAttention(state)) {
|
|
13077
13124
|
process.stdout.write(`attention: ${i.kind} \u2014 ${i.message}
|
|
13078
13125
|
`);
|
package/package.json
CHANGED