@tablation/crew 0.115.0 → 0.116.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 +73 -6
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2382,6 +2382,7 @@ var HOOK_NAMES = [
|
|
|
2382
2382
|
"isolate",
|
|
2383
2383
|
"handoff",
|
|
2384
2384
|
"preview",
|
|
2385
|
+
"map",
|
|
2385
2386
|
"ci_status",
|
|
2386
2387
|
"version",
|
|
2387
2388
|
"bump",
|
|
@@ -3014,6 +3015,7 @@ var ConfigError = class extends Error {
|
|
|
3014
3015
|
};
|
|
3015
3016
|
var DEFAULT_MAX_PERISCOPES = 3;
|
|
3016
3017
|
var DEFAULT_PERISCOPE_IDLE_MINUTES = 120;
|
|
3018
|
+
var DEFAULT_MAP_TOKEN_BUDGET = 1e3;
|
|
3017
3019
|
function parseCloneScheme(v) {
|
|
3018
3020
|
if (v === void 0 || v === null || v === "") return void 0;
|
|
3019
3021
|
if (v === "ssh" || v === "https") return v;
|
|
@@ -3106,6 +3108,7 @@ var SHIP_KEYS = /* @__PURE__ */ new Set([
|
|
|
3106
3108
|
"previewHost",
|
|
3107
3109
|
"maxPeriscopes",
|
|
3108
3110
|
"periscopeIdleMinutes",
|
|
3111
|
+
"mapTokenBudget",
|
|
3109
3112
|
"secretStore",
|
|
3110
3113
|
"env",
|
|
3111
3114
|
"runner",
|
|
@@ -3454,6 +3457,10 @@ Create the first of those:
|
|
|
3454
3457
|
if (!Number.isInteger(periscopeIdleMinutes) || periscopeIdleMinutes < 1) {
|
|
3455
3458
|
missing.add(`ship.periscopeIdleMinutes must be a positive integer (got "${shipRaw.periscopeIdleMinutes}")`);
|
|
3456
3459
|
}
|
|
3460
|
+
const mapTokenBudget = shipRaw.mapTokenBudget === void 0 ? DEFAULT_MAP_TOKEN_BUDGET : Number(shipRaw.mapTokenBudget);
|
|
3461
|
+
if (!Number.isInteger(mapTokenBudget) || mapTokenBudget < 1) {
|
|
3462
|
+
missing.add(`ship.mapTokenBudget must be a positive integer (got "${shipRaw.mapTokenBudget}")`);
|
|
3463
|
+
}
|
|
3457
3464
|
const secretStoreRaw = shipRaw.secretStore === void 0 ? "auto" : shipRaw.secretStore;
|
|
3458
3465
|
if (!isSecretStoreMode(secretStoreRaw)) {
|
|
3459
3466
|
missing.add(`ship.secretStore must be one of ${SECRET_STORE_MODES.join(" | ")} (got "${String(shipRaw.secretStore)}")`);
|
|
@@ -3537,6 +3544,7 @@ Create the first of those:
|
|
|
3537
3544
|
previewHost: shipRaw.previewHost ? String(shipRaw.previewHost).trim() || void 0 : void 0,
|
|
3538
3545
|
maxPeriscopes,
|
|
3539
3546
|
periscopeIdleMinutes,
|
|
3547
|
+
mapTokenBudget,
|
|
3540
3548
|
secretStore,
|
|
3541
3549
|
env: parseShipEnv(shipRaw.env)
|
|
3542
3550
|
},
|
|
@@ -4039,6 +4047,7 @@ function runScript(script, opts) {
|
|
|
4039
4047
|
stdio: ["ignore", "pipe", "pipe"]
|
|
4040
4048
|
});
|
|
4041
4049
|
let output = "";
|
|
4050
|
+
let stdout = "";
|
|
4042
4051
|
let pending = "";
|
|
4043
4052
|
const absorb = (buf) => {
|
|
4044
4053
|
const text2 = buf.toString();
|
|
@@ -4049,18 +4058,21 @@ function runScript(script, opts) {
|
|
|
4049
4058
|
pending = lines2.pop() ?? "";
|
|
4050
4059
|
for (const l of lines2) opts.onLine(l);
|
|
4051
4060
|
};
|
|
4052
|
-
child.stdout.on("data",
|
|
4061
|
+
child.stdout.on("data", (buf) => {
|
|
4062
|
+
stdout += buf.toString();
|
|
4063
|
+
absorb(buf);
|
|
4064
|
+
});
|
|
4053
4065
|
child.stderr.on("data", absorb);
|
|
4054
4066
|
const timer = opts.timeoutMs ? setTimeout(() => child.kill("SIGKILL"), opts.timeoutMs) : void 0;
|
|
4055
4067
|
child.on("error", (err3) => {
|
|
4056
4068
|
if (timer) clearTimeout(timer);
|
|
4057
4069
|
resolvePromise({ code: 127, output: `${output}
|
|
4058
|
-
crew: cannot run ${shell.bin}: ${err3.message}
|
|
4070
|
+
crew: cannot run ${shell.bin}: ${err3.message}`, stdout });
|
|
4059
4071
|
});
|
|
4060
4072
|
child.on("close", (code) => {
|
|
4061
4073
|
if (timer) clearTimeout(timer);
|
|
4062
4074
|
if (pending && opts.onLine) opts.onLine(pending);
|
|
4063
|
-
resolvePromise({ code: code ?? 1, output });
|
|
4075
|
+
resolvePromise({ code: code ?? 1, output, stdout });
|
|
4064
4076
|
});
|
|
4065
4077
|
});
|
|
4066
4078
|
}
|
|
@@ -6280,6 +6292,7 @@ var HOOK_PURPOSE = [
|
|
|
6280
6292
|
["ports", "this worktree's port assignments; prints KEY=value overrides to export"],
|
|
6281
6293
|
["handoff", "leave the operator able to open what you built; put its output in your progress comment"],
|
|
6282
6294
|
["preview", "start this worktree's dev server for a viewer, bound to CREW_PREVIEW_HOST, detached; its last stdout line is the URL"],
|
|
6295
|
+
["map", 'prints an outline of the repository; the crew places it under "Repository map" below'],
|
|
6283
6296
|
["test", "run the suite"],
|
|
6284
6297
|
["build", "build it"],
|
|
6285
6298
|
["ci_status", "after you push and before you hand off: exit 0 means CI is green for your branch; on failure fix and push again, and say in your progress comment if it is still pending"]
|
|
@@ -6376,6 +6389,13 @@ function repoSection(r, key, heading) {
|
|
|
6376
6389
|
}
|
|
6377
6390
|
return lines2;
|
|
6378
6391
|
}
|
|
6392
|
+
function mapSection(r, heading) {
|
|
6393
|
+
if (r.map) return ["", heading, "", r.map, ""];
|
|
6394
|
+
if (r.mapSkipped) {
|
|
6395
|
+
return ["", `Note: ${r.mapSkipped}. Say so in one line of your progress comment; it does not block the work.`, ""];
|
|
6396
|
+
}
|
|
6397
|
+
return [];
|
|
6398
|
+
}
|
|
6379
6399
|
function renderEnvironment(i) {
|
|
6380
6400
|
const { route: route2, contract } = i;
|
|
6381
6401
|
const repos = i.repos;
|
|
@@ -6567,12 +6587,16 @@ function renderEnvironment(i) {
|
|
|
6567
6587
|
for (const r of repos) {
|
|
6568
6588
|
lines2.push(...i.sandbox ? ["", `### ${r.name}`] : repoSection(r, key, `### ${r.name} \u2014 \`${r.dir}\``));
|
|
6569
6589
|
lines2.push("", `Hooks for ${r.name} \u2014 the only commands you should run here:`, "", hookTable(r.config));
|
|
6590
|
+
lines2.push(...mapSection(r, `#### Repository map \u2014 ${r.name}`));
|
|
6570
6591
|
}
|
|
6571
6592
|
}
|
|
6572
6593
|
if (i.repoDir) {
|
|
6573
6594
|
lines2.push("", `This ticket's work happens in \`${i.repoDir}\`.`);
|
|
6574
6595
|
}
|
|
6575
|
-
if (only)
|
|
6596
|
+
if (only) {
|
|
6597
|
+
lines2.push("", "## Hooks \u2014 the only commands you should run", "", hookTable(only.config));
|
|
6598
|
+
lines2.push(...mapSection(only, "## Repository map"));
|
|
6599
|
+
}
|
|
6576
6600
|
return lines2.join("\n");
|
|
6577
6601
|
}
|
|
6578
6602
|
|
|
@@ -17764,6 +17788,49 @@ async function mergeAndRelease(o, decision, fresh, releaseBranch, isolated = fal
|
|
|
17764
17788
|
return { merged, conflicts, unbuildable, version, tag, deployed, integrated, confirmed, decision };
|
|
17765
17789
|
}
|
|
17766
17790
|
|
|
17791
|
+
// src/repo-map.ts
|
|
17792
|
+
var CHARS_PER_TOKEN = 4;
|
|
17793
|
+
var MAP_TIMEOUT_MS = 6e4;
|
|
17794
|
+
function budgetMap(text2, tokenBudget) {
|
|
17795
|
+
const body = text2.replace(/\s+$/, "");
|
|
17796
|
+
const limit = tokenBudget * CHARS_PER_TOKEN;
|
|
17797
|
+
if (body.length <= limit) return body;
|
|
17798
|
+
const lines2 = body.split("\n");
|
|
17799
|
+
const noteFor = (kept2) => `\u2026 ${lines2.length - kept2} of ${lines2.length} lines cut to fit ship.mapTokenBudget (${tokenBudget} tokens)`;
|
|
17800
|
+
let kept = lines2.length;
|
|
17801
|
+
let size = body.length;
|
|
17802
|
+
while (kept > 0 && size + 1 + noteFor(kept).length > limit) {
|
|
17803
|
+
kept--;
|
|
17804
|
+
size -= lines2[kept].length + 1;
|
|
17805
|
+
}
|
|
17806
|
+
return [...lines2.slice(0, kept), noteFor(kept)].join("\n");
|
|
17807
|
+
}
|
|
17808
|
+
function loadRepoMap(repo, tokenBudget, shellFallback) {
|
|
17809
|
+
const script = repo.config.hooks.map;
|
|
17810
|
+
if (!script) return Promise.resolve({ skipped: "" });
|
|
17811
|
+
return runScript(script, {
|
|
17812
|
+
cwd: repo.dir,
|
|
17813
|
+
shell: resolveShell(repo.config.shell ?? shellFallback),
|
|
17814
|
+
env: { CREW_REPO: repo.name },
|
|
17815
|
+
timeoutMs: MAP_TIMEOUT_MS
|
|
17816
|
+
}).then(
|
|
17817
|
+
(r) => {
|
|
17818
|
+
if (r.code !== 0) return { skipped: `the \`map\` hook for ${repo.name} exited ${r.code}, so the brief has no repository map` };
|
|
17819
|
+
if (!r.stdout.trim()) return { skipped: `the \`map\` hook for ${repo.name} printed nothing, so the brief has no repository map` };
|
|
17820
|
+
return { text: budgetMap(r.stdout, tokenBudget) };
|
|
17821
|
+
},
|
|
17822
|
+
(e) => ({ skipped: `the \`map\` hook for ${repo.name} could not run (${e instanceof Error ? e.message : String(e)}), so the brief has no repository map` })
|
|
17823
|
+
);
|
|
17824
|
+
}
|
|
17825
|
+
async function withRepoMaps(repos, tokenBudget, shellFallback) {
|
|
17826
|
+
return Promise.all(repos.map(async (r) => {
|
|
17827
|
+
if (!r.config.hooks.map) return r;
|
|
17828
|
+
const out = await loadRepoMap(r, tokenBudget, shellFallback);
|
|
17829
|
+
if ("skipped" in out) return out.skipped ? { ...r, mapSkipped: out.skipped } : r;
|
|
17830
|
+
return { ...r, map: out.text };
|
|
17831
|
+
}));
|
|
17832
|
+
}
|
|
17833
|
+
|
|
17767
17834
|
// src/failure-alert.ts
|
|
17768
17835
|
var FAILURE_ALERT_MARKER = calloutHeader("CAUTION", "Failure Alert");
|
|
17769
17836
|
function findOpenFailureAlert(tickets, headline) {
|
|
@@ -21971,7 +22038,7 @@ async function buildEnvironment(route2, ship, ticket) {
|
|
|
21971
22038
|
});
|
|
21972
22039
|
const authorship = await tracker.authorshipProbe().catch(() => null);
|
|
21973
22040
|
const attachments = await tracker.attachmentsProbe().catch(() => null);
|
|
21974
|
-
return renderEnvironment({ route: route2, userAgent: ship.userAgent, repos, contract, sourceTicket: ticket, authorship, attachments, shipName: ship.name, ...sandboxEnv(ship) });
|
|
22041
|
+
return renderEnvironment({ route: route2, userAgent: ship.userAgent, repos: await withRepoMaps(repos, ship.mapTokenBudget, ship.shell), contract, sourceTicket: ticket, authorship, attachments, shipName: ship.name, ...sandboxEnv(ship) });
|
|
21975
22042
|
}
|
|
21976
22043
|
|
|
21977
22044
|
// src/control-api.ts
|
|
@@ -24137,7 +24204,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
24137
24204
|
return renderEnvironment({
|
|
24138
24205
|
route: c,
|
|
24139
24206
|
userAgent: cfg.ship.userAgent,
|
|
24140
|
-
repos: await resolvedRepos(c),
|
|
24207
|
+
repos: await withRepoMaps(await resolvedRepos(c), cfg.ship.mapTokenBudget, cfg.ship.shell),
|
|
24141
24208
|
contract,
|
|
24142
24209
|
sourceTicket: ticket,
|
|
24143
24210
|
authorship: await tracker.authorshipProbe().catch(() => null),
|
package/package.json
CHANGED