@tablation/crew 0.28.0 → 0.29.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 +118 -30
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { resolve as resolve4, dirname as dirname11, join as join20, basename as
|
|
4
|
+
import { resolve as resolve4, dirname as dirname11, join as join20, basename as basename9 } from "path";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
6
|
import { hostname as hostname2, homedir as homedir5 } from "os";
|
|
7
7
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
8
8
|
|
|
9
9
|
// src/config.ts
|
|
10
10
|
import { readFileSync as readFileSync4, existsSync as existsSync3 } from "fs";
|
|
11
|
-
import { resolve, dirname as dirname2, isAbsolute, join as join4, basename as
|
|
11
|
+
import { resolve, dirname as dirname2, isAbsolute, join as join4, basename as basename3 } from "path";
|
|
12
12
|
import { homedir as homedir2, tmpdir } from "os";
|
|
13
13
|
import { parse as parse2 } from "yaml";
|
|
14
14
|
|
|
@@ -157,6 +157,9 @@ var Emitter = class _Emitter {
|
|
|
157
157
|
};
|
|
158
158
|
var eventFileFor = (stateDir) => join(stateDir, "events.jsonl");
|
|
159
159
|
|
|
160
|
+
// src/toolchain.ts
|
|
161
|
+
import { basename } from "path";
|
|
162
|
+
|
|
160
163
|
// src/stream.ts
|
|
161
164
|
function toolTarget(name, input) {
|
|
162
165
|
if (!input) return void 0;
|
|
@@ -312,12 +315,22 @@ function allowedTools(role) {
|
|
|
312
315
|
var claudeAdapter = {
|
|
313
316
|
harness: "claude",
|
|
314
317
|
displayName: "Claude Code",
|
|
318
|
+
vendor: "anthropic",
|
|
319
|
+
ownsModel: (model) => model.startsWith("claude-"),
|
|
320
|
+
// The tier's reasoning is the extended-thinking budget; it supersedes the
|
|
321
|
+
// toolchain's `maxThinkingTokens` (0 disables). A non-numeric value is ignored.
|
|
322
|
+
applyReasoning(spec, reasoning) {
|
|
323
|
+
const n = Number(reasoning);
|
|
324
|
+
return Number.isInteger(n) && n >= 0 ? { ...spec, maxThinkingTokens: n } : spec;
|
|
325
|
+
},
|
|
315
326
|
defaultBin: "claude",
|
|
316
327
|
defaultModel: "claude-sonnet-5",
|
|
317
328
|
defaultTiers: {
|
|
318
|
-
|
|
329
|
+
// `standard` carries no reasoning: it defers to the toolchain's own
|
|
330
|
+
// `maxThinkingTokens`, which is what a persona with no tier gets today.
|
|
331
|
+
light: { model: "claude-haiku-4-5-20251001", reasoning: "2048" },
|
|
319
332
|
standard: { model: "claude-sonnet-5-5" },
|
|
320
|
-
deep: { model: "claude-opus-5-5" }
|
|
333
|
+
deep: { model: "claude-opus-5-5", reasoning: "16384" }
|
|
321
334
|
},
|
|
322
335
|
limitations: [
|
|
323
336
|
"Bash is not sandboxed: the deny list refuses known destructive patterns, but a one-liner reaching the same end another way is not caught",
|
|
@@ -411,6 +424,52 @@ function resolveToolchain(ship, route) {
|
|
|
411
424
|
if (!found) throw new Error(`toolchain "${name}" is not defined (have: ${shipToolchains(ship).map((t) => t.name).join(", ")})`);
|
|
412
425
|
return found;
|
|
413
426
|
}
|
|
427
|
+
function toolchainVendor(spec) {
|
|
428
|
+
if (spec.launcher && basename(spec.launcher) === "ori") return "openrouter";
|
|
429
|
+
return adapterFor(spec.harness).vendor;
|
|
430
|
+
}
|
|
431
|
+
function resolveSeat(ship, route, role, persona = {}) {
|
|
432
|
+
const warnings = [];
|
|
433
|
+
const base = resolveToolchain(ship, route);
|
|
434
|
+
const baseVendor = toolchainVendor(base);
|
|
435
|
+
const baseWhy = route?.toolchain ? `route toolchain ${base.name}` : ship.toolchain ? `ship default ${base.name}` : `default ${base.name}`;
|
|
436
|
+
let chosen = base;
|
|
437
|
+
let toolchainWhy = baseWhy;
|
|
438
|
+
if (persona.vendor && persona.vendor !== baseVendor) {
|
|
439
|
+
const match = shipToolchains(ship).find((t) => toolchainVendor(t) === persona.vendor);
|
|
440
|
+
if (match) {
|
|
441
|
+
chosen = match;
|
|
442
|
+
toolchainWhy = `persona vendor ${persona.vendor} \u2192 ${match.name}`;
|
|
443
|
+
} else {
|
|
444
|
+
warnings.push(`seat ${role}: persona vendor "${persona.vendor}" has no toolchain on this ship; running ${base.name} (${baseVendor}) instead`);
|
|
445
|
+
toolchainWhy = `${baseWhy} (persona vendor ${persona.vendor} has no toolchain)`;
|
|
446
|
+
}
|
|
447
|
+
} else if (persona.vendor) {
|
|
448
|
+
toolchainWhy = `${baseWhy} (serves persona vendor ${persona.vendor})`;
|
|
449
|
+
}
|
|
450
|
+
const vendor = toolchainVendor(chosen);
|
|
451
|
+
const adapter = adapterFor(chosen.harness);
|
|
452
|
+
let model = chosen.model;
|
|
453
|
+
let modelWhy = `toolchain ${chosen.name} model`;
|
|
454
|
+
let reasoning = chosen.tiers.standard?.reasoning;
|
|
455
|
+
const explicit = persona.model;
|
|
456
|
+
const explicitOk = explicit !== void 0 && (vendor === "openrouter" || adapter.ownsModel(explicit));
|
|
457
|
+
if (explicit !== void 0 && !explicitOk) {
|
|
458
|
+
warnings.push(`seat ${role}: persona model "${explicit}" does not belong to vendor "${vendor}" (toolchain ${chosen.name}); ignored`);
|
|
459
|
+
}
|
|
460
|
+
if (explicit !== void 0 && explicitOk) {
|
|
461
|
+
model = explicit;
|
|
462
|
+
modelWhy = "persona model";
|
|
463
|
+
reasoning = void 0;
|
|
464
|
+
} else if (persona.tier && persona.tier !== "standard" && chosen.tiers[persona.tier]) {
|
|
465
|
+
const t = chosen.tiers[persona.tier];
|
|
466
|
+
model = t.model;
|
|
467
|
+
reasoning = t.reasoning;
|
|
468
|
+
modelWhy = `persona tier ${persona.tier} via ${chosen.name}`;
|
|
469
|
+
}
|
|
470
|
+
const toolchain = reasoning !== void 0 && adapter.applyReasoning ? adapter.applyReasoning(chosen, reasoning) : chosen;
|
|
471
|
+
return { toolchain, model, toolchainWhy, modelWhy, warnings };
|
|
472
|
+
}
|
|
414
473
|
|
|
415
474
|
// src/config.ts
|
|
416
475
|
import { getSessionStore, hostFromUrl } from "@tablation/client";
|
|
@@ -453,7 +512,7 @@ function explain(ship, required) {
|
|
|
453
512
|
|
|
454
513
|
// src/repo-config.ts
|
|
455
514
|
import { readFileSync as readFileSync3, existsSync as existsSync2 } from "fs";
|
|
456
|
-
import { join as join3, basename } from "path";
|
|
515
|
+
import { join as join3, basename as basename2 } from "path";
|
|
457
516
|
import { parse } from "yaml";
|
|
458
517
|
var SPEC_VERSION = 1;
|
|
459
518
|
var REPO_CONFIG_FILENAMES = [".crew.yaml", ".crew.yml", ".crew.json"];
|
|
@@ -792,7 +851,7 @@ function renderWorktreeName(template, ctx) {
|
|
|
792
851
|
return renderBranchName(template.replace(/\{dir\}/g, ctx.dir).replace(/\{issue\}/g, issue), ctx);
|
|
793
852
|
}
|
|
794
853
|
function defaultWorktreePrefix(dir) {
|
|
795
|
-
const name =
|
|
854
|
+
const name = basename2(dir.replace(/[/\\]+$/, ""));
|
|
796
855
|
return name ? `${name}-issue-` : "issue-";
|
|
797
856
|
}
|
|
798
857
|
var DEFAULTS = {
|
|
@@ -966,7 +1025,7 @@ function effectiveBranchTemplate(cfg, prefix) {
|
|
|
966
1025
|
function effectiveWorktreeDirName(cfg, dir, branchName, number, renderName) {
|
|
967
1026
|
if (cfg.worktrees.name && renderName) return renderName(cfg.worktrees.name);
|
|
968
1027
|
if (cfg.provenance["worktrees.prefix"] !== "default") return `${cfg.worktrees.prefix}${number}`;
|
|
969
|
-
return `${
|
|
1028
|
+
return `${basename2(dir.replace(/[/\\]+$/, ""))}-${branchName}`;
|
|
970
1029
|
}
|
|
971
1030
|
function validateEffective(cfg) {
|
|
972
1031
|
const problems = [];
|
|
@@ -1571,7 +1630,7 @@ function dirForRepo(r, repoId) {
|
|
|
1571
1630
|
}
|
|
1572
1631
|
function repoTargetFor(r, repoId) {
|
|
1573
1632
|
const names = r.resolved?.repoNames;
|
|
1574
|
-
if (!repoId || !names) return Object.keys(r.repos).length ? null : { name:
|
|
1633
|
+
if (!repoId || !names) return Object.keys(r.repos).length ? null : { name: basename3(r.dir), dir: r.dir };
|
|
1575
1634
|
const name = names[repoId];
|
|
1576
1635
|
if (!name) return null;
|
|
1577
1636
|
return reposOf(r).find((t) => t.name === name) ?? null;
|
|
@@ -1654,7 +1713,7 @@ function reposOf(r) {
|
|
|
1654
1713
|
const named = Object.entries(r.repos);
|
|
1655
1714
|
if (named.length > 0) return named.map(([name, dir]) => ({ name, dir }));
|
|
1656
1715
|
const trackerNames = r.resolved?.repoNames ? Object.values(r.resolved.repoNames) : [];
|
|
1657
|
-
if (trackerNames.length <= 1) return [{ name:
|
|
1716
|
+
if (trackerNames.length <= 1) return [{ name: basename3(r.dir), dir: r.dir }];
|
|
1658
1717
|
const remotes = r.resolved?.repoRemotes ?? {};
|
|
1659
1718
|
const remoteByName = {};
|
|
1660
1719
|
for (const [id, name] of Object.entries(r.resolved.repoNames)) {
|
|
@@ -3040,7 +3099,7 @@ import {
|
|
|
3040
3099
|
statSync as statSync4,
|
|
3041
3100
|
unlinkSync as unlinkSync2
|
|
3042
3101
|
} from "fs";
|
|
3043
|
-
import { dirname as dirname4, basename as
|
|
3102
|
+
import { dirname as dirname4, basename as basename4 } from "path";
|
|
3044
3103
|
var GitError = class extends Error {
|
|
3045
3104
|
};
|
|
3046
3105
|
function gitRaw(cwd, args) {
|
|
@@ -3136,7 +3195,7 @@ function deleteRemoteBranch(cwd, remote, branch) {
|
|
|
3136
3195
|
}
|
|
3137
3196
|
function worktreeForNumber(cwd, num) {
|
|
3138
3197
|
const re = new RegExp(`(?:^|-)${num}$`);
|
|
3139
|
-
return worktrees(cwd).find((w) => re.test(
|
|
3198
|
+
return worktrees(cwd).find((w) => re.test(basename4(w.path))) ?? null;
|
|
3140
3199
|
}
|
|
3141
3200
|
var commitBodies = (cwd, range) => gitOk(cwd, ["log", range, "--format=%B"]) ?? "";
|
|
3142
3201
|
var resolve2 = (cwd, ref) => gitOk(cwd, ["rev-parse", "-q", "--verify", `${ref}^{commit}`]);
|
|
@@ -5090,7 +5149,7 @@ ${waiting.map((t) => `| ${displayKey2(t)} | ${t.status} | ${blockers(t, i)} |`).
|
|
|
5090
5149
|
}
|
|
5091
5150
|
|
|
5092
5151
|
// src/ticket-branch.ts
|
|
5093
|
-
import { basename as
|
|
5152
|
+
import { basename as basename5 } from "path";
|
|
5094
5153
|
function branchRenderer(t, role) {
|
|
5095
5154
|
const { tag, prefix } = ticketBranchContext(t);
|
|
5096
5155
|
return (template) => renderBranchName(template, { key: t.issue_id, title: t.title ?? void 0, role, tag, prefix });
|
|
@@ -5101,7 +5160,7 @@ function worktreeForTicket(dir, cfg, t, role) {
|
|
|
5101
5160
|
const number = t.issue_id.replace(/^\D+/, "");
|
|
5102
5161
|
const { tag } = ticketBranchContext(t);
|
|
5103
5162
|
return effectiveWorktreeDirName(cfg, dir, branchName, number, (template) => renderWorktreeName(template, {
|
|
5104
|
-
dir:
|
|
5163
|
+
dir: basename5(dir.replace(/[/\\]+$/, "")),
|
|
5105
5164
|
key: t.issue_id,
|
|
5106
5165
|
title: t.title ?? void 0,
|
|
5107
5166
|
role,
|
|
@@ -5790,7 +5849,7 @@ import { join as join8, dirname as dirname5 } from "path";
|
|
|
5790
5849
|
import { createHash as createHash2 } from "crypto";
|
|
5791
5850
|
|
|
5792
5851
|
// src/environment.ts
|
|
5793
|
-
import { basename as
|
|
5852
|
+
import { basename as basename6 } from "path";
|
|
5794
5853
|
var API_KEY_VAR = "CREW_API_KEY";
|
|
5795
5854
|
function authorshipBullets(a, contract) {
|
|
5796
5855
|
if (!a) return [];
|
|
@@ -5841,7 +5900,7 @@ function repoSection(r, key, heading) {
|
|
|
5841
5900
|
const repo = r.config;
|
|
5842
5901
|
const branchTemplate = effectiveBranchTemplate(repo, repoPrefix(repo));
|
|
5843
5902
|
const branchName = branchExample(repo, key);
|
|
5844
|
-
const checkoutName =
|
|
5903
|
+
const checkoutName = basename6(r.dir.replace(/[/\\]+$/, ""));
|
|
5845
5904
|
const worktreeName = effectiveWorktreeDirName(
|
|
5846
5905
|
repo,
|
|
5847
5906
|
r.dir,
|
|
@@ -6198,9 +6257,9 @@ function planAgentRun(o) {
|
|
|
6198
6257
|
const promptSha = createHash2("sha256").update(`${effectiveCommon}${effectiveBrief}`).digest("hex");
|
|
6199
6258
|
const streamsDir = join8(o.stateDir, "streams");
|
|
6200
6259
|
const base = `${routeLabel}-${o.role}-${o.cycle}`;
|
|
6201
|
-
const
|
|
6260
|
+
const seat = resolveSeat(o.ship, o.route, o.role, o.agentPersona ?? (o.agentModel ? { model: o.agentModel } : {}));
|
|
6261
|
+
const { toolchain, model } = seat;
|
|
6202
6262
|
const adapter = adapterFor(toolchain.harness);
|
|
6203
|
-
const model = o.agentModel ?? toolchain.model;
|
|
6204
6263
|
const invocation = adapter.invocation(toolchain, {
|
|
6205
6264
|
role: o.role,
|
|
6206
6265
|
model,
|
|
@@ -6229,6 +6288,8 @@ function planAgentRun(o) {
|
|
|
6229
6288
|
args: invocation.args,
|
|
6230
6289
|
toolchain: toolchain.name,
|
|
6231
6290
|
harness: toolchain.harness,
|
|
6291
|
+
resolution: { toolchain: seat.toolchainWhy, model: seat.modelWhy },
|
|
6292
|
+
warnings: seat.warnings,
|
|
6232
6293
|
prompt,
|
|
6233
6294
|
promptBytes: Buffer.byteLength(prompt, "utf8"),
|
|
6234
6295
|
unsetEnv: adapter.unsetEnv,
|
|
@@ -6259,6 +6320,8 @@ function planAgentRun(o) {
|
|
|
6259
6320
|
function describePlan(p) {
|
|
6260
6321
|
return [
|
|
6261
6322
|
`role: ${p.role}`,
|
|
6323
|
+
...p.resolution ? [`resolved: toolchain ${p.toolchain} \u2190 ${p.resolution.toolchain}; model ${p.model} \u2190 ${p.resolution.model}`] : [],
|
|
6324
|
+
...(p.warnings ?? []).map((w) => `warning: ${w}`),
|
|
6262
6325
|
// Only for a non-default toolchain, so a plain Claude run's output is unchanged.
|
|
6263
6326
|
...p.toolchain && p.toolchain !== "claude" ? [`toolchain: ${p.toolchain} (${p.harness ?? "claude"})`] : [],
|
|
6264
6327
|
`cwd: ${p.cwd}`,
|
|
@@ -6289,6 +6352,7 @@ function spawnAgent(plan, emit) {
|
|
|
6289
6352
|
const adapter = adapterFor(plan.harness ?? LEGACY_TOOLCHAIN_HARNESS);
|
|
6290
6353
|
const started = Date.now();
|
|
6291
6354
|
const startedAt = new Date(started).toISOString();
|
|
6355
|
+
for (const w of plan.warnings ?? []) emit.warn(w, { step: "agent", role: plan.role });
|
|
6292
6356
|
const rawSink = openSink(plan.streamPath);
|
|
6293
6357
|
const eventsSink = openSink(plan.eventsPath);
|
|
6294
6358
|
return new Promise((resolve5, reject) => {
|
|
@@ -8577,7 +8641,11 @@ async function fetchLastSyncedPrompt(route, agentsModelId, recordId, historyId,
|
|
|
8577
8641
|
return void 0;
|
|
8578
8642
|
}
|
|
8579
8643
|
}
|
|
8580
|
-
|
|
8644
|
+
function choiceValue(v) {
|
|
8645
|
+
const raw = typeof v === "string" ? v : v && typeof v === "object" ? v.value ?? v.key : void 0;
|
|
8646
|
+
return typeof raw === "string" && raw.trim() ? raw.trim().toLowerCase() : void 0;
|
|
8647
|
+
}
|
|
8648
|
+
async function fetchSeatPersona(route, role, opts) {
|
|
8581
8649
|
const agentId = await resolveAgentId(route, role, opts);
|
|
8582
8650
|
if (!agentId || !route.resolved) return void 0;
|
|
8583
8651
|
const client2 = new TablationClient2({
|
|
@@ -8588,7 +8656,14 @@ async function fetchSeatAgentModel(route, role, opts) {
|
|
|
8588
8656
|
try {
|
|
8589
8657
|
const agentsModel = await client2.dataModels.get("agents", route.resolved.workspaceId);
|
|
8590
8658
|
const row = await client2.records.get(agentsModel.id, agentId);
|
|
8591
|
-
|
|
8659
|
+
const tier = choiceValue(row.tier);
|
|
8660
|
+
const vendor = choiceValue(row.vendor);
|
|
8661
|
+
const persona = {
|
|
8662
|
+
...row.model ? { model: row.model } : {},
|
|
8663
|
+
...tier && TIERS.includes(tier) ? { tier } : {},
|
|
8664
|
+
...vendor ? { vendor } : {}
|
|
8665
|
+
};
|
|
8666
|
+
return Object.keys(persona).length > 0 ? persona : void 0;
|
|
8592
8667
|
} catch {
|
|
8593
8668
|
return void 0;
|
|
8594
8669
|
}
|
|
@@ -9392,7 +9467,7 @@ async function showLogEntry(route, entryId, opts = {}) {
|
|
|
9392
9467
|
|
|
9393
9468
|
// src/repos-cmd.ts
|
|
9394
9469
|
import { homedir as homedir4 } from "os";
|
|
9395
|
-
import { basename as
|
|
9470
|
+
import { basename as basename7, resolve as resolve3 } from "path";
|
|
9396
9471
|
import { isMap, isSeq, parseDocument, stringify as stringify2 } from "yaml";
|
|
9397
9472
|
var ReposError = class extends Error {
|
|
9398
9473
|
};
|
|
@@ -9464,7 +9539,7 @@ function configuredRepos(text, route, base) {
|
|
|
9464
9539
|
});
|
|
9465
9540
|
}
|
|
9466
9541
|
const dir = m.get("dir");
|
|
9467
|
-
return dir ? [{ name:
|
|
9542
|
+
return dir ? [{ name: basename7(String(dir)), dir: expandDir(String(dir), base) }] : [];
|
|
9468
9543
|
}
|
|
9469
9544
|
function planRepoAdd(i) {
|
|
9470
9545
|
const doc = parseDocument(i.text);
|
|
@@ -9476,7 +9551,7 @@ function planRepoAdd(i) {
|
|
|
9476
9551
|
throw new ReposError(`${target} is already configured on route ${i.route}`);
|
|
9477
9552
|
}
|
|
9478
9553
|
const matched = matchRepoName(i.origin, i.resolved);
|
|
9479
|
-
const name = matched ?? i.name ?? normalizeRemote(i.origin)?.split("/").pop() ??
|
|
9554
|
+
const name = matched ?? i.name ?? normalizeRemote(i.origin)?.split("/").pop() ?? basename7(target);
|
|
9480
9555
|
let warning;
|
|
9481
9556
|
if (!matched) {
|
|
9482
9557
|
const known = Object.values(i.resolved?.repoNames ?? {});
|
|
@@ -9574,14 +9649,14 @@ function schedulerInstalled(plan, exists, crontab) {
|
|
|
9574
9649
|
|
|
9575
9650
|
// src/worktree-sweep.ts
|
|
9576
9651
|
import { existsSync as existsSync15 } from "fs";
|
|
9577
|
-
import { basename as
|
|
9652
|
+
import { basename as basename8 } from "path";
|
|
9578
9653
|
function planWorktreeSweep(target, tickets, contract) {
|
|
9579
9654
|
const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
|
|
9580
9655
|
const verified = contract.statuses.verified;
|
|
9581
9656
|
const byNumber = new Map(tickets.map((t) => [t.issue_id.replace(/^\D+/, ""), t]));
|
|
9582
9657
|
const actions = [];
|
|
9583
9658
|
for (const w of worktrees(target.dir)) {
|
|
9584
|
-
const m = /(?:^|-)(\d+)$/.exec(
|
|
9659
|
+
const m = /(?:^|-)(\d+)$/.exec(basename8(w.path));
|
|
9585
9660
|
if (!m) continue;
|
|
9586
9661
|
const ticket = byNumber.get(m[1]);
|
|
9587
9662
|
if (!ticket) continue;
|
|
@@ -10746,7 +10821,7 @@ async function runRoleAgent(role, decision, ctx) {
|
|
|
10746
10821
|
cycle: emit.cycle,
|
|
10747
10822
|
ticket: ticketHint,
|
|
10748
10823
|
divergedPrompt: await fetchDivergedPrompt(route, role, { userAgent: ship.userAgent }),
|
|
10749
|
-
|
|
10824
|
+
agentPersona: await fetchSeatPersona(route, role, { userAgent: ship.userAgent }),
|
|
10750
10825
|
resolvedAgentId: await resolveAgentId(route, role, { userAgent: ship.userAgent })
|
|
10751
10826
|
});
|
|
10752
10827
|
emit.enter("agent", role);
|
|
@@ -11832,7 +11907,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
11832
11907
|
cycle: wemit.cycle,
|
|
11833
11908
|
ticket: w.decision.actionable.top?.issue_id,
|
|
11834
11909
|
divergedPrompt: await fetchDivergedPrompt(w.route, w.role, { userAgent: cfg.ship.userAgent }),
|
|
11835
|
-
|
|
11910
|
+
agentPersona: await fetchSeatPersona(w.route, w.role, { userAgent: cfg.ship.userAgent }),
|
|
11836
11911
|
resolvedAgentId: await resolveAgentId(w.route, w.role, { userAgent: cfg.ship.userAgent })
|
|
11837
11912
|
});
|
|
11838
11913
|
process.stdout.write(`${describePlan(fleetPlan2)}
|
|
@@ -11925,7 +12000,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
11925
12000
|
cycle: wemit.cycle,
|
|
11926
12001
|
ticket: fleetTicketHint,
|
|
11927
12002
|
divergedPrompt: await fetchDivergedPrompt(w.route, w.role, { userAgent: cfg.ship.userAgent }),
|
|
11928
|
-
|
|
12003
|
+
agentPersona: await fetchSeatPersona(w.route, w.role, { userAgent: cfg.ship.userAgent }),
|
|
11929
12004
|
resolvedAgentId: await resolveAgentId(w.route, w.role, { userAgent: cfg.ship.userAgent })
|
|
11930
12005
|
});
|
|
11931
12006
|
wemit.enter("agent", w.role);
|
|
@@ -12130,7 +12205,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
12130
12205
|
cycle: emit.cycle,
|
|
12131
12206
|
ticket: ticketHint,
|
|
12132
12207
|
divergedPrompt: await fetchDivergedPrompt(route, current, { userAgent: cfg.ship.userAgent }),
|
|
12133
|
-
|
|
12208
|
+
agentPersona: await fetchSeatPersona(route, current, { userAgent: cfg.ship.userAgent }),
|
|
12134
12209
|
resolvedAgentId: await resolveAgentId(route, current, { userAgent: cfg.ship.userAgent })
|
|
12135
12210
|
});
|
|
12136
12211
|
if (dryRun) {
|
|
@@ -13534,7 +13609,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
13534
13609
|
// `synthesis-tabl-123`. `worktreePrefix` above is kept for
|
|
13535
13610
|
// config-provenance display; this is the field that actually
|
|
13536
13611
|
// describes where a worktree lands by default.
|
|
13537
|
-
worktreeDirPattern: r.config.worktrees.name ?? (r.config.provenance["worktrees.prefix"] !== "default" ? `${r.config.worktrees.prefix}<number>` : `${
|
|
13612
|
+
worktreeDirPattern: r.config.worktrees.name ?? (r.config.provenance["worktrees.prefix"] !== "default" ? `${r.config.worktrees.prefix}<number>` : `${basename9(r.dir.replace(/[/\\]+$/, ""))}-<branch>`),
|
|
13538
13613
|
worktreeParent: resolvePath(r.dir, "..")
|
|
13539
13614
|
})),
|
|
13540
13615
|
waiting: waiting ? { ticket: waiting.ticket, since: waiting.since, streak: fairness.streak() } : null
|
|
@@ -13642,7 +13717,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
|
|
|
13642
13717
|
);
|
|
13643
13718
|
for (const r of await resolvedRepos(route)) {
|
|
13644
13719
|
const { config } = r;
|
|
13645
|
-
const worktreePattern = config.worktrees.name ?? (config.provenance["worktrees.prefix"] !== "default" ? `${config.worktrees.prefix}<number>` : `${
|
|
13720
|
+
const worktreePattern = config.worktrees.name ?? (config.provenance["worktrees.prefix"] !== "default" ? `${config.worktrees.prefix}<number>` : `${basename9(r.dir.replace(/[/\\]+$/, ""))}-${effectiveBranchTemplate(config, void 0)}`);
|
|
13646
13721
|
process.stdout.write(
|
|
13647
13722
|
`${`repo ${r.name}:`.padEnd(19)}${r.dir}
|
|
13648
13723
|
worktrees at ../${worktreePattern} (${config.provenance["worktrees.name"] ?? config.provenance["worktrees.prefix"] ?? "default"}), branch ${config.branch.name}
|
|
@@ -13786,6 +13861,19 @@ crew manifest: ${seats2.length ? seats2.map((c) => c.name).join(", ") : "non
|
|
|
13786
13861
|
);
|
|
13787
13862
|
}
|
|
13788
13863
|
}
|
|
13864
|
+
const shipVendors = new Set(shipToolchains(cfg.ship).map(toolchainVendor));
|
|
13865
|
+
if (route.apiKey && route.resolved) {
|
|
13866
|
+
let flagged = 0;
|
|
13867
|
+
for (const role of ROLE_NAMES) {
|
|
13868
|
+
const persona = await fetchSeatPersona(route, role, { userAgent: cfg.ship.userAgent });
|
|
13869
|
+
if (persona?.vendor && !shipVendors.has(persona.vendor)) {
|
|
13870
|
+
flagged++;
|
|
13871
|
+
process.stdout.write(`seat vendor: ${role} persona wants "${persona.vendor}" \u2014 no toolchain on this ship serves it (have: ${[...shipVendors].join(", ")}); it will run ${resolveSeat(cfg.ship, route, role).toolchain.name} with a warning
|
|
13872
|
+
`);
|
|
13873
|
+
}
|
|
13874
|
+
}
|
|
13875
|
+
if (flagged === 0) process.stdout.write("seat vendor: OK (every persona vendor has a toolchain on this ship)\n");
|
|
13876
|
+
}
|
|
13789
13877
|
for (const c of cfg.routes.filter((r) => r.enabled)) {
|
|
13790
13878
|
let repos;
|
|
13791
13879
|
try {
|
package/package.json
CHANGED