@tablation/crew 0.37.0 → 0.39.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/README.md +38 -10
- package/dist/cli.js +274 -175
- package/package.json +1 -1
- package/prompts/default/personas/lane-design.md +63 -96
- package/prompts/default/personas/lane-dev.md +15 -15
- package/prompts/default/personas/lane-pair.md +64 -84
- package/prompts/default/personas/lane-qa.md +127 -158
- package/prompts/default/personas/lane-triage.md +79 -64
- package/prompts/default/skills/crew-lane-agent.md +479 -0
- package/prompts/default/skills/design-pass.md +53 -0
- package/prompts/default/skills/filing-a-ticket.md +56 -0
- package/prompts/default/skills/question-and-investigation.md +59 -0
- package/prompts/default/skills/running-the-suite.md +56 -0
- package/prompts/default/skills/ticket-attachments.md +34 -0
- package/prompts/default/skills/workspace-change-authorization.md +35 -0
- package/prompts/default/skills/worktree-setup.md +101 -0
- package/prompts/dev-qa/personas/lane-design.md +2 -0
- package/prompts/dev-qa/personas/lane-dev.md +15 -14
- package/prompts/dev-qa/personas/lane-pair.md +64 -84
- package/prompts/dev-qa/personas/lane-qa.md +126 -144
- package/prompts/dev-qa/personas/lane-triage.md +69 -56
- package/prompts/dev-qa/skills/crew-lane-agent.md +473 -0
- package/prompts/dev-qa/skills/filing-a-ticket.md +56 -0
- package/prompts/dev-qa/skills/question-and-investigation.md +59 -0
- package/prompts/dev-qa/skills/running-the-suite.md +56 -0
- package/prompts/dev-qa/skills/ticket-attachments.md +34 -0
- package/prompts/dev-qa/skills/workspace-change-authorization.md +35 -0
- package/prompts/dev-qa/skills/worktree-setup.md +101 -0
- package/prompts/default/personas/common.md +0 -903
- package/prompts/dev-qa/personas/common.md +0 -874
package/dist/cli.js
CHANGED
|
@@ -1806,6 +1806,7 @@ var DEFAULT_BASE_URL = "https://app.tablation.com";
|
|
|
1806
1806
|
var DEFAULT_MAX_THINKING_TOKENS = 4096;
|
|
1807
1807
|
var DEFAULT_REPOS_BASE_PATH = "~/Crew";
|
|
1808
1808
|
var ROLE_NAMES = ["dev", "design", "qa", "triage", "pair"];
|
|
1809
|
+
var SELF_CONTAINED_ROLES = /* @__PURE__ */ new Set(["pair", "triage"]);
|
|
1809
1810
|
var ROLE_LABEL = {
|
|
1810
1811
|
dev: "Dev",
|
|
1811
1812
|
design: "Design",
|
|
@@ -6606,9 +6607,9 @@ var alreadyReported = (ticket, g, comments) => comments.some((c) => c.ticket_id
|
|
|
6606
6607
|
|
|
6607
6608
|
// src/agent.ts
|
|
6608
6609
|
import { spawn as spawn2 } from "child_process";
|
|
6609
|
-
import { readFileSync as
|
|
6610
|
+
import { readFileSync as readFileSync9, existsSync as existsSync10, statSync as statSync5, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7, createWriteStream } from "fs";
|
|
6610
6611
|
import { finished } from "stream/promises";
|
|
6611
|
-
import { join as
|
|
6612
|
+
import { join as join11, dirname as dirname6 } from "path";
|
|
6612
6613
|
import { createHash as createHash2 } from "crypto";
|
|
6613
6614
|
|
|
6614
6615
|
// src/environment.ts
|
|
@@ -6952,6 +6953,199 @@ async function reportAgentRun(target, run2) {
|
|
|
6952
6953
|
return { agentLogId: entry.id };
|
|
6953
6954
|
}
|
|
6954
6955
|
|
|
6956
|
+
// src/skill-include.ts
|
|
6957
|
+
var SkillIncludeError = class extends Error {
|
|
6958
|
+
};
|
|
6959
|
+
var MAX_INCLUDE_DEPTH = 3;
|
|
6960
|
+
var MAX_INCLUDED_BYTES = 2e5;
|
|
6961
|
+
var MARKER = /\{\{[ \t]*skill:[ \t]*([^{}\r\n]*?)[ \t]*\}\}/g;
|
|
6962
|
+
function skillMarkerNames(text) {
|
|
6963
|
+
const names = [];
|
|
6964
|
+
for (const m of text.matchAll(MARKER)) {
|
|
6965
|
+
const name = m[1].trim();
|
|
6966
|
+
if (name && !names.includes(name)) names.push(name);
|
|
6967
|
+
}
|
|
6968
|
+
return names;
|
|
6969
|
+
}
|
|
6970
|
+
function expandSkillMarkers(text, lookup, opts = {}) {
|
|
6971
|
+
const maxDepth = opts.maxDepth ?? MAX_INCLUDE_DEPTH;
|
|
6972
|
+
const maxBytes = opts.maxBytes ?? MAX_INCLUDED_BYTES;
|
|
6973
|
+
const warnings = [];
|
|
6974
|
+
let included = 0;
|
|
6975
|
+
const expand2 = (source, chain) => source.replace(MARKER, (literal, rawName) => {
|
|
6976
|
+
const name = rawName.trim();
|
|
6977
|
+
if (!name) return literal;
|
|
6978
|
+
if (chain.includes(name)) {
|
|
6979
|
+
warnings.push(`skill include "${name}" left literal: it includes itself (${[...chain, name].join(" \u2192 ")})`);
|
|
6980
|
+
return literal;
|
|
6981
|
+
}
|
|
6982
|
+
if (chain.length >= maxDepth) {
|
|
6983
|
+
warnings.push(`skill include "${name}" left literal: nested deeper than ${maxDepth} levels`);
|
|
6984
|
+
return literal;
|
|
6985
|
+
}
|
|
6986
|
+
const body = lookup(name);
|
|
6987
|
+
if (body === void 0) throw new SkillIncludeError(`no skill named "${name}" for the include ${literal}`);
|
|
6988
|
+
const trimmed = body.replace(/^\n+/, "").replace(/\n+$/, "");
|
|
6989
|
+
const bytes = Buffer.byteLength(trimmed, "utf8");
|
|
6990
|
+
if (included + bytes > maxBytes) {
|
|
6991
|
+
warnings.push(`skill include "${name}" left literal: it would exceed the ${maxBytes}-byte include budget`);
|
|
6992
|
+
return literal;
|
|
6993
|
+
}
|
|
6994
|
+
included += bytes;
|
|
6995
|
+
return expand2(trimmed, [...chain, name]);
|
|
6996
|
+
});
|
|
6997
|
+
return { text: expand2(text, []), warnings };
|
|
6998
|
+
}
|
|
6999
|
+
|
|
7000
|
+
// src/skills.ts
|
|
7001
|
+
import { readFileSync as readFileSync8, readdirSync as readdirSync2, existsSync as existsSync9 } from "fs";
|
|
7002
|
+
import { join as join10 } from "path";
|
|
7003
|
+
import { parse as parseYaml } from "yaml";
|
|
7004
|
+
import { TablationClient as TablationClient2, StaleWriteError as StaleWriteError6, TablationApiError } from "@tablation/client";
|
|
7005
|
+
var SkillSyncError = class extends Error {
|
|
7006
|
+
};
|
|
7007
|
+
function parseSkillFile(path) {
|
|
7008
|
+
const text = readFileSync8(path, "utf8");
|
|
7009
|
+
const match = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(text);
|
|
7010
|
+
if (!match) throw new SkillSyncError(`${path}: missing YAML frontmatter (expected a leading --- block)`);
|
|
7011
|
+
const frontmatter = match[1];
|
|
7012
|
+
const body = match[2];
|
|
7013
|
+
const meta = parseYaml(frontmatter);
|
|
7014
|
+
if (typeof meta.name !== "string" || !meta.name) throw new SkillSyncError(`${path}: frontmatter is missing "name"`);
|
|
7015
|
+
if (typeof meta.description !== "string" || !meta.description) {
|
|
7016
|
+
throw new SkillSyncError(`${path}: frontmatter is missing "description"`);
|
|
7017
|
+
}
|
|
7018
|
+
return { name: meta.name, description: meta.description, prompt: body, file: path };
|
|
7019
|
+
}
|
|
7020
|
+
function listLocalSkills(promptsDir) {
|
|
7021
|
+
const dir = join10(promptsDir, "skills");
|
|
7022
|
+
if (!existsSync9(dir)) return [];
|
|
7023
|
+
return readdirSync2(dir).filter((f) => f.endsWith(".md")).map((f) => parseSkillFile(join10(dir, f)));
|
|
7024
|
+
}
|
|
7025
|
+
async function syncSkills(route, opts) {
|
|
7026
|
+
if (!route.resolved) {
|
|
7027
|
+
throw new SkillSyncError(`route "${route.route}" has no resolved ids \u2014 run \`crew connect\` first`);
|
|
7028
|
+
}
|
|
7029
|
+
const workspaceId = route.resolved.workspaceId;
|
|
7030
|
+
const client2 = new TablationClient2({
|
|
7031
|
+
baseUrl: `${route.baseUrl}/api`,
|
|
7032
|
+
apiKey: resolveApiKey(route),
|
|
7033
|
+
headers: { "User-Agent": opts.userAgent ?? "Mozilla/5.0 TablationCrewAgent/1.0" }
|
|
7034
|
+
});
|
|
7035
|
+
let skillsModel;
|
|
7036
|
+
try {
|
|
7037
|
+
skillsModel = await client2.dataModels.get("agent_skills", workspaceId);
|
|
7038
|
+
} catch (e) {
|
|
7039
|
+
if (e instanceof TablationApiError && e.status === 404) {
|
|
7040
|
+
throw new SkillSyncError(
|
|
7041
|
+
`workspace ${workspaceId} has no Agent Skills table yet \u2014 every workspace auto-provisions one; an older workspace may still need its one-time backfill`
|
|
7042
|
+
);
|
|
7043
|
+
}
|
|
7044
|
+
throw e;
|
|
7045
|
+
}
|
|
7046
|
+
const local = listLocalSkills(route.promptsDir);
|
|
7047
|
+
const rows = await client2.records.list(skillsModel.id, { limit: 200 });
|
|
7048
|
+
const cache = route.resolved.agentSkills ?? {};
|
|
7049
|
+
const outcomes = [];
|
|
7050
|
+
const agentSkills = { ...cache };
|
|
7051
|
+
for (const skill of local) {
|
|
7052
|
+
const row = rows.find((r) => r.name === skill.name);
|
|
7053
|
+
if (!row) {
|
|
7054
|
+
if (opts.dryRun) {
|
|
7055
|
+
outcomes.push({ name: skill.name, action: "created", skillId: "(dry run)" });
|
|
7056
|
+
continue;
|
|
7057
|
+
}
|
|
7058
|
+
const created = await client2.records.create(skillsModel.id, {
|
|
7059
|
+
name: skill.name,
|
|
7060
|
+
description: skill.description,
|
|
7061
|
+
prompt: skill.prompt
|
|
7062
|
+
});
|
|
7063
|
+
agentSkills[skill.name] = { skillId: created.id, lastSyncedUpdatedAt: created.updated_at };
|
|
7064
|
+
outcomes.push({ name: skill.name, action: "created", skillId: created.id });
|
|
7065
|
+
continue;
|
|
7066
|
+
}
|
|
7067
|
+
if (row.description === skill.description && row.prompt === skill.prompt) {
|
|
7068
|
+
agentSkills[skill.name] = { skillId: row.id, lastSyncedUpdatedAt: row.updated_at };
|
|
7069
|
+
outcomes.push({ name: skill.name, action: "unchanged", skillId: row.id });
|
|
7070
|
+
continue;
|
|
7071
|
+
}
|
|
7072
|
+
const knownBaseline = cache[skill.name]?.skillId === row.id ? cache[skill.name]?.lastSyncedUpdatedAt : void 0;
|
|
7073
|
+
if (!knownBaseline) {
|
|
7074
|
+
outcomes.push({ name: skill.name, action: "diverged", skillId: row.id });
|
|
7075
|
+
continue;
|
|
7076
|
+
}
|
|
7077
|
+
if (opts.dryRun) {
|
|
7078
|
+
outcomes.push({ name: skill.name, action: "updated", skillId: row.id });
|
|
7079
|
+
continue;
|
|
7080
|
+
}
|
|
7081
|
+
try {
|
|
7082
|
+
const updated = await client2.records.update(
|
|
7083
|
+
skillsModel.id,
|
|
7084
|
+
row.id,
|
|
7085
|
+
{ description: skill.description, prompt: skill.prompt },
|
|
7086
|
+
knownBaseline
|
|
7087
|
+
);
|
|
7088
|
+
agentSkills[skill.name] = { skillId: row.id, lastSyncedUpdatedAt: updated.updated_at };
|
|
7089
|
+
outcomes.push({ name: skill.name, action: "updated", skillId: row.id });
|
|
7090
|
+
} catch (e) {
|
|
7091
|
+
if (e instanceof StaleWriteError6) {
|
|
7092
|
+
outcomes.push({ name: skill.name, action: "diverged", skillId: row.id });
|
|
7093
|
+
continue;
|
|
7094
|
+
}
|
|
7095
|
+
throw e;
|
|
7096
|
+
}
|
|
7097
|
+
}
|
|
7098
|
+
return { outcomes, agentSkills };
|
|
7099
|
+
}
|
|
7100
|
+
function describeSkillSyncOutcome(o) {
|
|
7101
|
+
switch (o.action) {
|
|
7102
|
+
case "created":
|
|
7103
|
+
return `${o.name}: created (${o.skillId})`;
|
|
7104
|
+
case "updated":
|
|
7105
|
+
return `${o.name}: prompt updated from crew's local file`;
|
|
7106
|
+
case "unchanged":
|
|
7107
|
+
return `${o.name}: already in sync`;
|
|
7108
|
+
case "diverged":
|
|
7109
|
+
return `${o.name}: HAS LOCAL EDITS \u2014 crew's default is newer but was NOT applied; review ${o.skillId} in the app before syncing again`;
|
|
7110
|
+
}
|
|
7111
|
+
}
|
|
7112
|
+
async function fetchSkillOverrides(route, opts) {
|
|
7113
|
+
if (!route.resolved) return {};
|
|
7114
|
+
const client2 = new TablationClient2({
|
|
7115
|
+
baseUrl: `${route.baseUrl}/api`,
|
|
7116
|
+
apiKey: resolveApiKey(route),
|
|
7117
|
+
headers: { "User-Agent": opts.userAgent ?? "Mozilla/5.0 TablationCrewAgent/1.0" }
|
|
7118
|
+
});
|
|
7119
|
+
let rows;
|
|
7120
|
+
try {
|
|
7121
|
+
const model = await client2.dataModels.get("agent_skills", route.resolved.workspaceId);
|
|
7122
|
+
rows = await client2.records.list(model.id, { limit: 200 });
|
|
7123
|
+
} catch {
|
|
7124
|
+
return {};
|
|
7125
|
+
}
|
|
7126
|
+
const local = new Map(listLocalSkills(route.promptsDir).map((s) => [s.name, s]));
|
|
7127
|
+
const cache = route.resolved.agentSkills ?? {};
|
|
7128
|
+
const out = {};
|
|
7129
|
+
for (const row of rows) {
|
|
7130
|
+
const name = row.name;
|
|
7131
|
+
if (row.prompt === local.get(name)?.prompt) continue;
|
|
7132
|
+
const cached = cache[name];
|
|
7133
|
+
if (cached?.skillId === row.id && cached.lastSyncedUpdatedAt === row.updated_at) continue;
|
|
7134
|
+
out[name] = row.prompt;
|
|
7135
|
+
}
|
|
7136
|
+
return out;
|
|
7137
|
+
}
|
|
7138
|
+
function skillMarkerWarnings(promptsDir, personaPrompts) {
|
|
7139
|
+
const shipped = new Set(listLocalSkills(promptsDir).map((s) => s.name));
|
|
7140
|
+
const out = [];
|
|
7141
|
+
for (const [role, text] of Object.entries(personaPrompts)) {
|
|
7142
|
+
for (const name of skillMarkerNames(text ?? "")) {
|
|
7143
|
+
if (!shipped.has(name)) out.push(`${role}: brief includes {{skill: ${name}}} but this prompt set ships no skill with that name`);
|
|
7144
|
+
}
|
|
7145
|
+
}
|
|
7146
|
+
return out;
|
|
7147
|
+
}
|
|
7148
|
+
|
|
6955
7149
|
// src/ticket-attribution.ts
|
|
6956
7150
|
var TICKET_KEY_RE = /\b[A-Z][A-Z0-9]{1,9}-\d+\b/g;
|
|
6957
7151
|
function ticketMentions(text) {
|
|
@@ -7006,34 +7200,45 @@ var AgentError = class extends Error {
|
|
|
7006
7200
|
var AgentSpawnError = class extends AgentError {
|
|
7007
7201
|
};
|
|
7008
7202
|
function planAgentRun(o) {
|
|
7009
|
-
const briefPath =
|
|
7010
|
-
if (!
|
|
7203
|
+
const briefPath = join11(o.route.promptsDir, "personas", `lane-${o.role}.md`);
|
|
7204
|
+
if (!existsSync10(briefPath)) {
|
|
7011
7205
|
throw new AgentError(`no brief at ${briefPath} \u2014 refusing to run an unscoped session`);
|
|
7012
7206
|
}
|
|
7013
|
-
const commonPath =
|
|
7014
|
-
|
|
7207
|
+
const commonPath = join11(o.route.promptsDir, "personas", "common.md");
|
|
7208
|
+
const usesCommon = !SELF_CONTAINED_ROLES.has(o.role) && existsSync10(commonPath);
|
|
7015
7209
|
const now = o.now ?? (() => Date.now());
|
|
7016
7210
|
const routeLabel = routeSlug(o.route.route);
|
|
7017
|
-
const digestPath2 =
|
|
7211
|
+
const digestPath2 = join11(o.stateDir, `digest-${routeLabel}-${o.role}.md`);
|
|
7018
7212
|
let digest;
|
|
7019
7213
|
let ageSeconds;
|
|
7020
|
-
if (
|
|
7214
|
+
if (existsSync10(digestPath2)) {
|
|
7021
7215
|
ageSeconds = Math.floor((now() - statSync5(digestPath2).mtimeMs) / 1e3);
|
|
7022
|
-
if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest =
|
|
7216
|
+
if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync9(digestPath2, "utf8");
|
|
7023
7217
|
}
|
|
7024
|
-
const common =
|
|
7025
|
-
const brief =
|
|
7218
|
+
const common = usesCommon ? readFileSync9(commonPath, "utf8") : "";
|
|
7219
|
+
const brief = readFileSync9(briefPath, "utf8");
|
|
7026
7220
|
const effectiveCommon = o.divergedPrompt ? "" : common;
|
|
7027
7221
|
const effectiveBrief = o.divergedPrompt ?? brief;
|
|
7222
|
+
const localSkills = new Map(listLocalSkills(o.route.promptsDir).map((s) => [s.name, s.prompt]));
|
|
7223
|
+
let expanded;
|
|
7224
|
+
try {
|
|
7225
|
+
expanded = expandSkillMarkers(
|
|
7226
|
+
`${effectiveCommon}${effectiveBrief}`,
|
|
7227
|
+
(name) => o.skillOverrides?.[name] ?? localSkills.get(name)
|
|
7228
|
+
);
|
|
7229
|
+
} catch (e) {
|
|
7230
|
+
if (e instanceof SkillIncludeError) throw new AgentError(`${e.message} \u2014 refusing to run an unscoped session`);
|
|
7231
|
+
throw e;
|
|
7232
|
+
}
|
|
7028
7233
|
const prompt = assemblePrompt({
|
|
7029
7234
|
roster: o.roster,
|
|
7030
7235
|
environment: o.environment,
|
|
7031
|
-
common:
|
|
7032
|
-
brief:
|
|
7236
|
+
common: "",
|
|
7237
|
+
brief: expanded.text,
|
|
7033
7238
|
digest
|
|
7034
7239
|
});
|
|
7035
|
-
const promptSha = createHash2("sha256").update(
|
|
7036
|
-
const streamsDir =
|
|
7240
|
+
const promptSha = createHash2("sha256").update(expanded.text).digest("hex");
|
|
7241
|
+
const streamsDir = join11(o.stateDir, "streams");
|
|
7037
7242
|
const base = `${routeLabel}-${o.role}-${o.cycle}`;
|
|
7038
7243
|
const seat = resolveSeat(o.ship, o.route, o.role, o.agentPersona ?? (o.agentModel ? { model: o.agentModel } : {}), o.repo);
|
|
7039
7244
|
const { toolchain, model } = seat;
|
|
@@ -7069,7 +7274,7 @@ function planAgentRun(o) {
|
|
|
7069
7274
|
harness: toolchain.harness,
|
|
7070
7275
|
provider: toolchainProvider(toolchain),
|
|
7071
7276
|
resolution: { toolchain: seat.toolchainWhy, model: seat.modelWhy },
|
|
7072
|
-
warnings: seat.warnings,
|
|
7277
|
+
warnings: [...seat.warnings ?? [], ...expanded.warnings],
|
|
7073
7278
|
prompt,
|
|
7074
7279
|
promptBytes: Buffer.byteLength(prompt, "utf8"),
|
|
7075
7280
|
unsetEnv: adapter.unsetEnv,
|
|
@@ -7089,8 +7294,8 @@ function planAgentRun(o) {
|
|
|
7089
7294
|
digestAttached: digest !== void 0,
|
|
7090
7295
|
digestAgeSeconds: ageSeconds,
|
|
7091
7296
|
model,
|
|
7092
|
-
streamPath:
|
|
7093
|
-
eventsPath:
|
|
7297
|
+
streamPath: join11(streamsDir, `${base}.jsonl`),
|
|
7298
|
+
eventsPath: join11(streamsDir, `${base}.events.jsonl`),
|
|
7094
7299
|
ticket: o.ticket,
|
|
7095
7300
|
agentLog,
|
|
7096
7301
|
promptVersion: o.route.resolved?.agentPersonas?.[o.role]?.historyId,
|
|
@@ -7296,13 +7501,13 @@ function spawnAgent(plan, emit) {
|
|
|
7296
7501
|
}
|
|
7297
7502
|
|
|
7298
7503
|
// src/run-worktree.ts
|
|
7299
|
-
import { existsSync as
|
|
7300
|
-
import { dirname as dirname7, join as
|
|
7504
|
+
import { existsSync as existsSync12 } from "fs";
|
|
7505
|
+
import { dirname as dirname7, join as join13 } from "path";
|
|
7301
7506
|
|
|
7302
7507
|
// src/ports.ts
|
|
7303
7508
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
7304
|
-
import { existsSync as
|
|
7305
|
-
import { join as
|
|
7509
|
+
import { existsSync as existsSync11 } from "fs";
|
|
7510
|
+
import { join as join12 } from "path";
|
|
7306
7511
|
var run = (cmd, args) => {
|
|
7307
7512
|
try {
|
|
7308
7513
|
return execFileSync2(cmd, args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
@@ -7347,7 +7552,7 @@ function ticketForPort(port, s = DEFAULT_PORTS) {
|
|
|
7347
7552
|
}
|
|
7348
7553
|
function worktreeExists(parent, prefix, n) {
|
|
7349
7554
|
for (const name of [`${prefix}${n}`, `${prefix}${String(n).padStart(3, "0")}`]) {
|
|
7350
|
-
if (
|
|
7555
|
+
if (existsSync11(join12(parent, name, ".git"))) return true;
|
|
7351
7556
|
}
|
|
7352
7557
|
return false;
|
|
7353
7558
|
}
|
|
@@ -7421,7 +7626,7 @@ function planRunWorktree(dir, cfg, t, role) {
|
|
|
7421
7626
|
const existing = worktreeForNumber(dir, num);
|
|
7422
7627
|
const located = locateBranchForTicket(dir, cfg, t, role);
|
|
7423
7628
|
const baseRef = hasRemote ? `${remote}/${base}` : base;
|
|
7424
|
-
if (existing && !existing.prunable &&
|
|
7629
|
+
if (existing && !existing.prunable && existsSync12(existing.path)) {
|
|
7425
7630
|
const branch = existing.branch;
|
|
7426
7631
|
if (status(existing.path).length > 0) return { action: "leave-dirty", path: existing.path, branch };
|
|
7427
7632
|
if (!branch) return { action: "leave-unpushed", path: existing.path, branch: "(detached HEAD)" };
|
|
@@ -7435,7 +7640,7 @@ function planRunWorktree(dir, cfg, t, role) {
|
|
|
7435
7640
|
if (countCommits(existing.path, `${baseRef}..HEAD`) > 0) return { action: "leave-unpushed", path: existing.path, branch };
|
|
7436
7641
|
return { action: "recut", path: existing.path, branch, base: baseRef };
|
|
7437
7642
|
}
|
|
7438
|
-
const path =
|
|
7643
|
+
const path = join13(dirname7(dir), worktreeForTicket(dir, cfg, t, role));
|
|
7439
7644
|
if (located?.where === "remote" || located && hasRemote && remoteBranchExists(dir, remote, located.branch)) {
|
|
7440
7645
|
return { action: "cut-remote", path, branch: located.branch };
|
|
7441
7646
|
}
|
|
@@ -7482,7 +7687,7 @@ function worktreeHoldsUnpushed(path, remote) {
|
|
|
7482
7687
|
}
|
|
7483
7688
|
async function finishRunWorktree(dir, cfg, branch) {
|
|
7484
7689
|
const wt = worktrees(dir).find((w) => w.branch === branch && !w.prunable && w.path !== dir);
|
|
7485
|
-
if (!wt || !
|
|
7690
|
+
if (!wt || !existsSync12(wt.path)) return { kind: "none" };
|
|
7486
7691
|
if (cfg.hooks.handoff) return { kind: "kept-handoff", path: wt.path };
|
|
7487
7692
|
if (status(wt.path).length > 0) return { kind: "kept-dirty", path: wt.path };
|
|
7488
7693
|
if (remoteConfigured(dir, cfg.branch.remote) && worktreeHoldsUnpushed(wt.path, cfg.branch.remote)) {
|
|
@@ -7494,7 +7699,7 @@ async function finishRunWorktree(dir, cfg, branch) {
|
|
|
7494
7699
|
if (pids.length) await killGently(pids);
|
|
7495
7700
|
}
|
|
7496
7701
|
gitOk(dir, ["worktree", "remove", wt.path]);
|
|
7497
|
-
return
|
|
7702
|
+
return existsSync12(wt.path) ? { kind: "kept-dirty", path: wt.path } : { kind: "removed", path: wt.path };
|
|
7498
7703
|
}
|
|
7499
7704
|
|
|
7500
7705
|
// src/handoff-guard.ts
|
|
@@ -7643,13 +7848,13 @@ async function retireRunWorktree(tracker, dir, cfg, t, role, seatId, shipName2,
|
|
|
7643
7848
|
}
|
|
7644
7849
|
|
|
7645
7850
|
// src/connect-wizard.ts
|
|
7646
|
-
import { delimiter, dirname as dirname8, join as
|
|
7647
|
-
import { accessSync as accessSync2, constants as constants2, existsSync as
|
|
7851
|
+
import { delimiter, dirname as dirname8, join as join14 } from "path";
|
|
7852
|
+
import { accessSync as accessSync2, constants as constants2, existsSync as existsSync13, mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
7648
7853
|
import { stringify } from "yaml";
|
|
7649
7854
|
var HOST_PASSENGERS_DOCS_URL = "https://crew.tablation.dev/host-passengers";
|
|
7650
7855
|
function shouldRunWizard(opts) {
|
|
7651
7856
|
if (!opts.isTTY) return false;
|
|
7652
|
-
const exists = opts.configExists ??
|
|
7857
|
+
const exists = opts.configExists ?? existsSync13;
|
|
7653
7858
|
return !configSearchPath(opts.crewHome).some((p) => exists(p));
|
|
7654
7859
|
}
|
|
7655
7860
|
function firstRunConfigPath() {
|
|
@@ -7666,7 +7871,7 @@ function defaultIsExecutable(p) {
|
|
|
7666
7871
|
function findOnPath2(name, pathEnv, isExecutable = defaultIsExecutable) {
|
|
7667
7872
|
for (const dir of pathEnv.split(delimiter)) {
|
|
7668
7873
|
if (!dir) continue;
|
|
7669
|
-
const candidate =
|
|
7874
|
+
const candidate = join14(dir, name);
|
|
7670
7875
|
if (isExecutable(candidate)) return candidate;
|
|
7671
7876
|
}
|
|
7672
7877
|
return void 0;
|
|
@@ -7761,8 +7966,8 @@ Wrote ${o.configPath}.
|
|
|
7761
7966
|
}
|
|
7762
7967
|
|
|
7763
7968
|
// src/release-run.ts
|
|
7764
|
-
import { readFileSync as
|
|
7765
|
-
import { dirname as dirname9, join as
|
|
7969
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, existsSync as existsSync14, copyFileSync, mkdirSync as mkdirSync8, mkdtempSync, rmSync as rmSync6 } from "fs";
|
|
7970
|
+
import { dirname as dirname9, join as join15 } from "path";
|
|
7766
7971
|
import { tmpdir as tmpdir2 } from "os";
|
|
7767
7972
|
|
|
7768
7973
|
// src/release.ts
|
|
@@ -8420,13 +8625,13 @@ async function bump(o, size) {
|
|
|
8420
8625
|
}
|
|
8421
8626
|
return printed;
|
|
8422
8627
|
}
|
|
8423
|
-
const files = o.repo.release.versionFiles.map((f) =>
|
|
8628
|
+
const files = o.repo.release.versionFiles.map((f) => join15(o.cwd, f));
|
|
8424
8629
|
const first = files[0];
|
|
8425
|
-
if (!
|
|
8630
|
+
if (!existsSync14(first)) {
|
|
8426
8631
|
o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
|
|
8427
8632
|
return null;
|
|
8428
8633
|
}
|
|
8429
|
-
const current = JSON.parse(
|
|
8634
|
+
const current = JSON.parse(readFileSync10(first, "utf8")).version;
|
|
8430
8635
|
if (!current) {
|
|
8431
8636
|
o.emit.error(`no version field in ${o.repo.release.versionFiles[0]}`);
|
|
8432
8637
|
return null;
|
|
@@ -8439,7 +8644,7 @@ async function bump(o, size) {
|
|
|
8439
8644
|
return version;
|
|
8440
8645
|
}
|
|
8441
8646
|
for (const f of files) {
|
|
8442
|
-
const src =
|
|
8647
|
+
const src = readFileSync10(f, "utf8");
|
|
8443
8648
|
const out = src.replace(/("version"\s*:\s*")[^"]*(")/, `$1${version}$2`);
|
|
8444
8649
|
if (out === src) {
|
|
8445
8650
|
o.emit.error(`no version field in ${f}`);
|
|
@@ -8452,13 +8657,13 @@ async function bump(o, size) {
|
|
|
8452
8657
|
function writeChangelog(o, version, entries) {
|
|
8453
8658
|
const file = o.repo.release.changelog;
|
|
8454
8659
|
if (!file || entries.length === 0) return;
|
|
8455
|
-
const path =
|
|
8660
|
+
const path = join15(o.cwd, file);
|
|
8456
8661
|
const section = renderChangelogSection(version, (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), entries);
|
|
8457
8662
|
if (o.dryRun) {
|
|
8458
8663
|
o.emit.emit(`would prepend ${entries.length} entr(ies) to ${file}`);
|
|
8459
8664
|
return;
|
|
8460
8665
|
}
|
|
8461
|
-
const existing =
|
|
8666
|
+
const existing = existsSync14(path) ? readFileSync10(path, "utf8") : "# Changelog\n";
|
|
8462
8667
|
writeFileSync9(path, insertChangelogSection(existing, section));
|
|
8463
8668
|
}
|
|
8464
8669
|
async function confirm(o, expected) {
|
|
@@ -8540,8 +8745,8 @@ async function confirmLastRelease(o, decision) {
|
|
|
8540
8745
|
function openReleaseCheckout(o) {
|
|
8541
8746
|
const { remote, base } = o.repo.branch;
|
|
8542
8747
|
const branch = `crew/release-${Date.now()}`;
|
|
8543
|
-
const root = mkdtempSync(
|
|
8544
|
-
const dir =
|
|
8748
|
+
const root = mkdtempSync(join15(tmpdir2(), "crew-release-"));
|
|
8749
|
+
const dir = join15(root, "checkout");
|
|
8545
8750
|
const close = () => {
|
|
8546
8751
|
gitOk(o.cwd, ["worktree", "remove", "--force", dir]);
|
|
8547
8752
|
rmSync6(root, { recursive: true, force: true });
|
|
@@ -8555,10 +8760,10 @@ function openReleaseCheckout(o) {
|
|
|
8555
8760
|
throw e;
|
|
8556
8761
|
}
|
|
8557
8762
|
for (const f of o.repo.worktrees.copy) {
|
|
8558
|
-
const src =
|
|
8559
|
-
if (!
|
|
8560
|
-
mkdirSync8(dirname9(
|
|
8561
|
-
copyFileSync(src,
|
|
8763
|
+
const src = join15(o.cwd, f);
|
|
8764
|
+
if (!existsSync14(src)) continue;
|
|
8765
|
+
mkdirSync8(dirname9(join15(dir, f)), { recursive: true });
|
|
8766
|
+
copyFileSync(src, join15(dir, f));
|
|
8562
8767
|
}
|
|
8563
8768
|
return { dir, branch, close };
|
|
8564
8769
|
}
|
|
@@ -9027,7 +9232,7 @@ function missingReleaserWarning(repo, release, ships, now = Date.now()) {
|
|
|
9027
9232
|
}
|
|
9028
9233
|
|
|
9029
9234
|
// src/watch.ts
|
|
9030
|
-
import { watch as watchFile, existsSync as
|
|
9235
|
+
import { watch as watchFile, existsSync as existsSync15, statSync as statSync6, openSync as openSync3, readSync, closeSync as closeSync3 } from "fs";
|
|
9031
9236
|
var E = "\x1B[";
|
|
9032
9237
|
var dim = (s) => `${E}2m${s}${E}0m`;
|
|
9033
9238
|
var bold = (s) => `${E}1m${s}${E}0m`;
|
|
@@ -9128,7 +9333,7 @@ function renderHeaders(states, now = Date.now()) {
|
|
|
9128
9333
|
return states.map((s) => renderHeader(s, now)).join("");
|
|
9129
9334
|
}
|
|
9130
9335
|
function readFrom(file, from) {
|
|
9131
|
-
if (!
|
|
9336
|
+
if (!existsSync15(file)) return { events: [], offset: from };
|
|
9132
9337
|
const size = statSync6(file).size;
|
|
9133
9338
|
if (size < from) return readFrom(file, 0);
|
|
9134
9339
|
if (size === from) return { events: [], offset: from };
|
|
@@ -9368,9 +9573,9 @@ function nextRoles(decision, ran) {
|
|
|
9368
9573
|
}
|
|
9369
9574
|
|
|
9370
9575
|
// src/agents.ts
|
|
9371
|
-
import { readFileSync as
|
|
9372
|
-
import { join as
|
|
9373
|
-
import { TablationClient as
|
|
9576
|
+
import { readFileSync as readFileSync11, existsSync as existsSync16 } from "fs";
|
|
9577
|
+
import { join as join16 } from "path";
|
|
9578
|
+
import { TablationClient as TablationClient3, StaleWriteError as StaleWriteError7, TablationApiError as TablationApiError2 } from "@tablation/client";
|
|
9374
9579
|
var PERSONA_NAME = {
|
|
9375
9580
|
dev: "Developer",
|
|
9376
9581
|
design: "Design",
|
|
@@ -9383,19 +9588,19 @@ var CREW_AGENT_COLUMN = "agent_id";
|
|
|
9383
9588
|
var AgentsSyncError = class extends Error {
|
|
9384
9589
|
};
|
|
9385
9590
|
function personaDefaultPrompt(promptsDir, role) {
|
|
9386
|
-
const briefPath =
|
|
9387
|
-
if (!
|
|
9388
|
-
if (role
|
|
9389
|
-
const commonPath =
|
|
9390
|
-
if (!
|
|
9391
|
-
return `${
|
|
9591
|
+
const briefPath = join16(promptsDir, "personas", `lane-${role}.md`);
|
|
9592
|
+
if (!existsSync16(briefPath)) throw new AgentsSyncError(`no brief at ${briefPath}`);
|
|
9593
|
+
if (SELF_CONTAINED_ROLES.has(role)) return readFileSync11(briefPath, "utf8");
|
|
9594
|
+
const commonPath = join16(promptsDir, "personas", "common.md");
|
|
9595
|
+
if (!existsSync16(commonPath)) return readFileSync11(briefPath, "utf8");
|
|
9596
|
+
return `${readFileSync11(commonPath, "utf8")}${readFileSync11(briefPath, "utf8")}`;
|
|
9392
9597
|
}
|
|
9393
9598
|
async function resolveAgentId(route, role, opts) {
|
|
9394
9599
|
const cached = route.resolved?.agentPersonas?.[role]?.agentId;
|
|
9395
9600
|
if (cached) return cached;
|
|
9396
9601
|
const seatId = route.resolved?.seats?.[role];
|
|
9397
9602
|
if (!seatId || !route.resolved) return void 0;
|
|
9398
|
-
const client2 = new
|
|
9603
|
+
const client2 = new TablationClient3({
|
|
9399
9604
|
baseUrl: `${route.baseUrl}/api`,
|
|
9400
9605
|
apiKey: resolveApiKey(route),
|
|
9401
9606
|
headers: { "User-Agent": opts.userAgent ?? "Mozilla/5.0 TablationCrewAgent/1.0" }
|
|
@@ -9410,7 +9615,7 @@ async function resolveAgentId(route, role, opts) {
|
|
|
9410
9615
|
async function fetchDivergedPrompt(route, role, opts) {
|
|
9411
9616
|
const agentId = await resolveAgentId(route, role, opts);
|
|
9412
9617
|
if (!agentId || !route.resolved) return void 0;
|
|
9413
|
-
const client2 = new
|
|
9618
|
+
const client2 = new TablationClient3({
|
|
9414
9619
|
baseUrl: `${route.baseUrl}/api`,
|
|
9415
9620
|
apiKey: resolveApiKey(route),
|
|
9416
9621
|
headers: { "User-Agent": opts.userAgent ?? "Mozilla/5.0 TablationCrewAgent/1.0" }
|
|
@@ -9448,7 +9653,7 @@ function choiceValue(v) {
|
|
|
9448
9653
|
async function fetchSeatPersona(route, role, opts) {
|
|
9449
9654
|
const agentId = await resolveAgentId(route, role, opts);
|
|
9450
9655
|
if (!agentId || !route.resolved) return void 0;
|
|
9451
|
-
const client2 = new
|
|
9656
|
+
const client2 = new TablationClient3({
|
|
9452
9657
|
baseUrl: `${route.baseUrl}/api`,
|
|
9453
9658
|
apiKey: resolveApiKey(route),
|
|
9454
9659
|
headers: { "User-Agent": opts.userAgent ?? "Mozilla/5.0 TablationCrewAgent/1.0" }
|
|
@@ -9496,7 +9701,7 @@ async function syncPersonas(route, opts) {
|
|
|
9496
9701
|
throw new AgentsSyncError(`route "${route.route}" has no resolved ids \u2014 run \`crew connect\` first`);
|
|
9497
9702
|
}
|
|
9498
9703
|
const workspaceId = route.resolved.workspaceId;
|
|
9499
|
-
const client2 = new
|
|
9704
|
+
const client2 = new TablationClient3({
|
|
9500
9705
|
baseUrl: `${route.baseUrl}/api`,
|
|
9501
9706
|
apiKey: resolveApiKey(route),
|
|
9502
9707
|
headers: { "User-Agent": opts.userAgent ?? "Mozilla/5.0 TablationCrewAgent/1.0" }
|
|
@@ -9505,7 +9710,7 @@ async function syncPersonas(route, opts) {
|
|
|
9505
9710
|
try {
|
|
9506
9711
|
agentsModel = await client2.dataModels.get("agents", workspaceId);
|
|
9507
9712
|
} catch (e) {
|
|
9508
|
-
if (e instanceof
|
|
9713
|
+
if (e instanceof TablationApiError2 && e.status === 404) {
|
|
9509
9714
|
throw new AgentsSyncError(
|
|
9510
9715
|
`workspace ${workspaceId} has no Agents table yet \u2014 every workspace auto-provisions one (ISSUE-465); an older workspace may still need its one-time backfill`
|
|
9511
9716
|
);
|
|
@@ -9579,7 +9784,7 @@ async function syncPersonas(route, opts) {
|
|
|
9579
9784
|
agentPersonas[role] = { agentId: row.id, lastSyncedUpdatedAt: updated.updated_at, ...historyId ? { historyId } : {} };
|
|
9580
9785
|
outcomes.push({ role, action: "updated", agentId: row.id });
|
|
9581
9786
|
} catch (e) {
|
|
9582
|
-
if (e instanceof
|
|
9787
|
+
if (e instanceof StaleWriteError7) {
|
|
9583
9788
|
outcomes.push({ role, action: "diverged", agentId: row.id });
|
|
9584
9789
|
continue;
|
|
9585
9790
|
}
|
|
@@ -10064,119 +10269,6 @@ import {
|
|
|
10064
10269
|
DeviceLoginError
|
|
10065
10270
|
} from "@tablation/client";
|
|
10066
10271
|
|
|
10067
|
-
// src/skills.ts
|
|
10068
|
-
import { readFileSync as readFileSync11, readdirSync as readdirSync2, existsSync as existsSync16 } from "fs";
|
|
10069
|
-
import { join as join16 } from "path";
|
|
10070
|
-
import { parse as parseYaml } from "yaml";
|
|
10071
|
-
import { TablationClient as TablationClient3, StaleWriteError as StaleWriteError7, TablationApiError as TablationApiError2 } from "@tablation/client";
|
|
10072
|
-
var SkillSyncError = class extends Error {
|
|
10073
|
-
};
|
|
10074
|
-
function parseSkillFile(path) {
|
|
10075
|
-
const text = readFileSync11(path, "utf8");
|
|
10076
|
-
const match = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(text);
|
|
10077
|
-
if (!match) throw new SkillSyncError(`${path}: missing YAML frontmatter (expected a leading --- block)`);
|
|
10078
|
-
const frontmatter = match[1];
|
|
10079
|
-
const body = match[2];
|
|
10080
|
-
const meta = parseYaml(frontmatter);
|
|
10081
|
-
if (typeof meta.name !== "string" || !meta.name) throw new SkillSyncError(`${path}: frontmatter is missing "name"`);
|
|
10082
|
-
if (typeof meta.description !== "string" || !meta.description) {
|
|
10083
|
-
throw new SkillSyncError(`${path}: frontmatter is missing "description"`);
|
|
10084
|
-
}
|
|
10085
|
-
return { name: meta.name, description: meta.description, prompt: body, file: path };
|
|
10086
|
-
}
|
|
10087
|
-
function listLocalSkills(promptsDir) {
|
|
10088
|
-
const dir = join16(promptsDir, "skills");
|
|
10089
|
-
if (!existsSync16(dir)) return [];
|
|
10090
|
-
return readdirSync2(dir).filter((f) => f.endsWith(".md")).map((f) => parseSkillFile(join16(dir, f)));
|
|
10091
|
-
}
|
|
10092
|
-
async function syncSkills(route, opts) {
|
|
10093
|
-
if (!route.resolved) {
|
|
10094
|
-
throw new SkillSyncError(`route "${route.route}" has no resolved ids \u2014 run \`crew connect\` first`);
|
|
10095
|
-
}
|
|
10096
|
-
const workspaceId = route.resolved.workspaceId;
|
|
10097
|
-
const client2 = new TablationClient3({
|
|
10098
|
-
baseUrl: `${route.baseUrl}/api`,
|
|
10099
|
-
apiKey: resolveApiKey(route),
|
|
10100
|
-
headers: { "User-Agent": opts.userAgent ?? "Mozilla/5.0 TablationCrewAgent/1.0" }
|
|
10101
|
-
});
|
|
10102
|
-
let skillsModel;
|
|
10103
|
-
try {
|
|
10104
|
-
skillsModel = await client2.dataModels.get("agent_skills", workspaceId);
|
|
10105
|
-
} catch (e) {
|
|
10106
|
-
if (e instanceof TablationApiError2 && e.status === 404) {
|
|
10107
|
-
throw new SkillSyncError(
|
|
10108
|
-
`workspace ${workspaceId} has no Agent Skills table yet \u2014 every workspace auto-provisions one; an older workspace may still need its one-time backfill`
|
|
10109
|
-
);
|
|
10110
|
-
}
|
|
10111
|
-
throw e;
|
|
10112
|
-
}
|
|
10113
|
-
const local = listLocalSkills(route.promptsDir);
|
|
10114
|
-
const rows = await client2.records.list(skillsModel.id, { limit: 200 });
|
|
10115
|
-
const cache = route.resolved.agentSkills ?? {};
|
|
10116
|
-
const outcomes = [];
|
|
10117
|
-
const agentSkills = { ...cache };
|
|
10118
|
-
for (const skill of local) {
|
|
10119
|
-
const row = rows.find((r) => r.name === skill.name);
|
|
10120
|
-
if (!row) {
|
|
10121
|
-
if (opts.dryRun) {
|
|
10122
|
-
outcomes.push({ name: skill.name, action: "created", skillId: "(dry run)" });
|
|
10123
|
-
continue;
|
|
10124
|
-
}
|
|
10125
|
-
const created = await client2.records.create(skillsModel.id, {
|
|
10126
|
-
name: skill.name,
|
|
10127
|
-
description: skill.description,
|
|
10128
|
-
prompt: skill.prompt
|
|
10129
|
-
});
|
|
10130
|
-
agentSkills[skill.name] = { skillId: created.id, lastSyncedUpdatedAt: created.updated_at };
|
|
10131
|
-
outcomes.push({ name: skill.name, action: "created", skillId: created.id });
|
|
10132
|
-
continue;
|
|
10133
|
-
}
|
|
10134
|
-
if (row.description === skill.description && row.prompt === skill.prompt) {
|
|
10135
|
-
agentSkills[skill.name] = { skillId: row.id, lastSyncedUpdatedAt: row.updated_at };
|
|
10136
|
-
outcomes.push({ name: skill.name, action: "unchanged", skillId: row.id });
|
|
10137
|
-
continue;
|
|
10138
|
-
}
|
|
10139
|
-
const knownBaseline = cache[skill.name]?.skillId === row.id ? cache[skill.name]?.lastSyncedUpdatedAt : void 0;
|
|
10140
|
-
if (!knownBaseline) {
|
|
10141
|
-
outcomes.push({ name: skill.name, action: "diverged", skillId: row.id });
|
|
10142
|
-
continue;
|
|
10143
|
-
}
|
|
10144
|
-
if (opts.dryRun) {
|
|
10145
|
-
outcomes.push({ name: skill.name, action: "updated", skillId: row.id });
|
|
10146
|
-
continue;
|
|
10147
|
-
}
|
|
10148
|
-
try {
|
|
10149
|
-
const updated = await client2.records.update(
|
|
10150
|
-
skillsModel.id,
|
|
10151
|
-
row.id,
|
|
10152
|
-
{ description: skill.description, prompt: skill.prompt },
|
|
10153
|
-
knownBaseline
|
|
10154
|
-
);
|
|
10155
|
-
agentSkills[skill.name] = { skillId: row.id, lastSyncedUpdatedAt: updated.updated_at };
|
|
10156
|
-
outcomes.push({ name: skill.name, action: "updated", skillId: row.id });
|
|
10157
|
-
} catch (e) {
|
|
10158
|
-
if (e instanceof StaleWriteError7) {
|
|
10159
|
-
outcomes.push({ name: skill.name, action: "diverged", skillId: row.id });
|
|
10160
|
-
continue;
|
|
10161
|
-
}
|
|
10162
|
-
throw e;
|
|
10163
|
-
}
|
|
10164
|
-
}
|
|
10165
|
-
return { outcomes, agentSkills };
|
|
10166
|
-
}
|
|
10167
|
-
function describeSkillSyncOutcome(o) {
|
|
10168
|
-
switch (o.action) {
|
|
10169
|
-
case "created":
|
|
10170
|
-
return `${o.name}: created (${o.skillId})`;
|
|
10171
|
-
case "updated":
|
|
10172
|
-
return `${o.name}: prompt updated from crew's local file`;
|
|
10173
|
-
case "unchanged":
|
|
10174
|
-
return `${o.name}: already in sync`;
|
|
10175
|
-
case "diverged":
|
|
10176
|
-
return `${o.name}: HAS LOCAL EDITS \u2014 crew's default is newer but was NOT applied; review ${o.skillId} in the app before syncing again`;
|
|
10177
|
-
}
|
|
10178
|
-
}
|
|
10179
|
-
|
|
10180
10272
|
// src/logbook.ts
|
|
10181
10273
|
import { createHash as createHash3 } from "crypto";
|
|
10182
10274
|
import { TablationClient as TablationClient4 } from "@tablation/client";
|
|
@@ -11653,6 +11745,7 @@ async function runRoleAgent(role, decision, ctx) {
|
|
|
11653
11745
|
ticket: ticketHint,
|
|
11654
11746
|
repo: ticketRepo,
|
|
11655
11747
|
divergedPrompt: await fetchDivergedPrompt(route, role, { userAgent: ship.userAgent }),
|
|
11748
|
+
skillOverrides: await fetchSkillOverrides(route, { userAgent: ship.userAgent }),
|
|
11656
11749
|
agentPersona: await fetchSeatPersona(route, role, { userAgent: ship.userAgent }),
|
|
11657
11750
|
resolvedAgentId: await resolveAgentId(route, role, { userAgent: ship.userAgent })
|
|
11658
11751
|
});
|
|
@@ -12747,6 +12840,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
12747
12840
|
ticket: w.decision.actionable.top?.issue_id,
|
|
12748
12841
|
repo: repoTargetFor(w.route, w.decision.actionable.top?.repo_id)?.name,
|
|
12749
12842
|
divergedPrompt: await fetchDivergedPrompt(w.route, w.role, { userAgent: cfg.ship.userAgent }),
|
|
12843
|
+
skillOverrides: await fetchSkillOverrides(w.route, { userAgent: cfg.ship.userAgent }),
|
|
12750
12844
|
agentPersona: await fetchSeatPersona(w.route, w.role, { userAgent: cfg.ship.userAgent }),
|
|
12751
12845
|
resolvedAgentId: await resolveAgentId(w.route, w.role, { userAgent: cfg.ship.userAgent })
|
|
12752
12846
|
});
|
|
@@ -12843,6 +12937,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
12843
12937
|
ticket: fleetTicketHint,
|
|
12844
12938
|
repo: fleetTicketRepo,
|
|
12845
12939
|
divergedPrompt: await fetchDivergedPrompt(w.route, w.role, { userAgent: cfg.ship.userAgent }),
|
|
12940
|
+
skillOverrides: await fetchSkillOverrides(w.route, { userAgent: cfg.ship.userAgent }),
|
|
12846
12941
|
agentPersona: await fetchSeatPersona(w.route, w.role, { userAgent: cfg.ship.userAgent }),
|
|
12847
12942
|
resolvedAgentId: await resolveAgentId(w.route, w.role, { userAgent: cfg.ship.userAgent })
|
|
12848
12943
|
});
|
|
@@ -13051,6 +13146,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
13051
13146
|
ticket: ticketHint,
|
|
13052
13147
|
repo: ticketRepo,
|
|
13053
13148
|
divergedPrompt: await fetchDivergedPrompt(route, current, { userAgent: cfg.ship.userAgent }),
|
|
13149
|
+
skillOverrides: await fetchSkillOverrides(route, { userAgent: cfg.ship.userAgent }),
|
|
13054
13150
|
agentPersona: await fetchSeatPersona(route, current, { userAgent: cfg.ship.userAgent }),
|
|
13055
13151
|
resolvedAgentId: await resolveAgentId(route, current, { userAgent: cfg.ship.userAgent })
|
|
13056
13152
|
});
|
|
@@ -13313,6 +13409,9 @@ ${changed2.map((c) => ` - ${c}
|
|
|
13313
13409
|
for (const o of personaResult.outcomes) process.stdout.write(`${describeSyncOutcome(o)}
|
|
13314
13410
|
`);
|
|
13315
13411
|
for (const o of personaResult.crewLinks) process.stdout.write(`${describeCrewLink(o)}
|
|
13412
|
+
`);
|
|
13413
|
+
const briefs = Object.fromEntries(ROLE_NAMES.map((r) => [r, personaDefaultPrompt(target.promptsDir, r)]));
|
|
13414
|
+
for (const w of skillMarkerWarnings(target.promptsDir, briefs)) process.stderr.write(`crew agents sync: warning: ${w}
|
|
13316
13415
|
`);
|
|
13317
13416
|
let skillResult;
|
|
13318
13417
|
try {
|