@sideboard-ai/core 0.1.158 → 0.1.160
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/{abletime-GWZE6OYM.js → abletime-AGF54TNA.js} +2 -2
- package/dist/{abletime-BEIDJG7W.js → abletime-BMS53RIZ.js} +2 -2
- package/dist/{agents-RGE7PSUY.js → agents-6UOXBORU.js} +4 -4
- package/dist/{agents-E3ZPWZRS.js → agents-RD774UEV.js} +4 -4
- package/dist/{app-settings-VRQI3FIE.js → app-settings-BRLZQA7N.js} +13 -17
- package/dist/{app-settings-H3INSR67.js → app-settings-JJQDWPEK.js} +13 -17
- package/dist/{chunk-EPQIOAAY.js → chunk-7OWRFJK6.js} +7 -5
- package/dist/{chunk-KYKNFJK5.js → chunk-BQ4PEXIF.js} +39 -20
- package/dist/{chunk-3BWVOUQG.js → chunk-CVZ4KXVP.js} +127 -113
- package/dist/{chunk-3SUZMPGL.js → chunk-HZROPNYL.js} +12 -13
- package/dist/{chunk-DVJBO3M7.js → chunk-ILXKJ4ZD.js} +1 -1
- package/dist/{chunk-A7QXLYBN.js → chunk-IO5WGZRR.js} +3 -13
- package/dist/{chunk-N27GVFZY.js → chunk-KJFFQAMA.js} +1 -1
- package/dist/{chunk-D7EGJ3EI.js → chunk-LTXS26K5.js} +127 -113
- package/dist/{chunk-CVR4RJ6G.js → chunk-QANLIT2E.js} +12 -13
- package/dist/{chunk-FBFIOVDH.js → chunk-QYKADGPP.js} +38 -19
- package/dist/{chunk-BLRHO7XQ.js → chunk-RQNXLIXO.js} +10 -6
- package/dist/{chunk-LQMFCS54.js → chunk-UUEBNUEG.js} +3 -13
- package/dist/{coordinator-prompt-26IJU2AV.js → coordinator-prompt-X3LXE5Q4.js} +3 -3
- package/dist/{coordinator-prompt-GZESIQNH.js → coordinator-prompt-YYUXC7EM.js} +3 -3
- package/dist/{global-workspace-REI55FZ6.js → global-workspace-E6S5XQZF.js} +3 -3
- package/dist/{global-workspace-5BIW26YR.js → global-workspace-SPR2Y4YQ.js} +3 -3
- package/dist/index.cjs +599 -475
- package/dist/index.d.cts +62 -57
- package/dist/index.d.ts +62 -57
- package/dist/index.js +325 -230
- package/dist/mcp/run-stdio.cjs +528 -402
- package/dist/mcp/run-stdio.js +265 -163
- package/dist/{orchestrator-ZVW2AC6B.js → orchestrator-BSVSOTBK.js} +5 -5
- package/dist/{orchestrator-J727WIJY.js → orchestrator-KVKPIUVU.js} +5 -5
- package/dist/{workspaces-NYSM2EPX.js → workspaces-MZELP4BI.js} +3 -3
- package/dist/{workspaces-OCEPZXFD.js → workspaces-OK2SNNQF.js} +3 -3
- package/dist/{worktree-VTMBZKY6.js → worktree-BX53JZHO.js} +2 -2
- package/dist/{worktree-T4SWFSTT.js → worktree-IY7YCEGH.js} +2 -2
- package/package.json +1 -1
|
@@ -19,42 +19,34 @@ import { homedir, hostname } from "os";
|
|
|
19
19
|
import { join as join2 } from "path";
|
|
20
20
|
|
|
21
21
|
// src/store/account-profile.ts
|
|
22
|
-
var ACCOUNT_ROLE_PRESETS = ["engineering", "design", "product"];
|
|
23
|
-
var ACCOUNT_ROLES = ACCOUNT_ROLE_PRESETS;
|
|
24
|
-
var ACCOUNT_ROLE_MAX = 16;
|
|
25
22
|
var PROFILE_NOTES_MAX = 2e3;
|
|
26
|
-
var
|
|
23
|
+
var LEGACY_ROLE_LABELS = {
|
|
27
24
|
engineering: "Engineering",
|
|
28
25
|
design: "Design",
|
|
29
26
|
product: "Product",
|
|
30
27
|
qa: "QA"
|
|
31
28
|
};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
design: ["design-team", "design"],
|
|
35
|
-
product: ["product-team", "product"]
|
|
36
|
-
};
|
|
37
|
-
function accountRoleLabel(role) {
|
|
38
|
-
const known = ACCOUNT_ROLE_LABELS[role];
|
|
29
|
+
function legacyRoleLabel(role) {
|
|
30
|
+
const known = LEGACY_ROLE_LABELS[role];
|
|
39
31
|
if (known) return known;
|
|
40
32
|
return role.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
41
33
|
}
|
|
42
|
-
function
|
|
34
|
+
function normalizeLegacyRole(value) {
|
|
43
35
|
const key = (value ?? "").trim().toLowerCase().replace(/[_\s]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
44
36
|
if (key === "both") return void 0;
|
|
45
37
|
if (key.length < 2 || key.length > 40) return void 0;
|
|
46
38
|
if (!/^[a-z][a-z0-9-]*$/.test(key)) return void 0;
|
|
47
39
|
return key;
|
|
48
40
|
}
|
|
49
|
-
function
|
|
41
|
+
function legacyRoleLabels(roles, legacyRole) {
|
|
50
42
|
const raw = Array.isArray(roles) ? roles : [];
|
|
51
43
|
const out = [];
|
|
52
44
|
const seen = /* @__PURE__ */ new Set();
|
|
53
45
|
const push = (value) => {
|
|
54
|
-
const role =
|
|
55
|
-
if (!role || seen.has(role) || out.length >=
|
|
46
|
+
const role = normalizeLegacyRole(typeof value === "string" ? value : "");
|
|
47
|
+
if (!role || seen.has(role) || out.length >= 16) return;
|
|
56
48
|
seen.add(role);
|
|
57
|
-
out.push(role);
|
|
49
|
+
out.push(legacyRoleLabel(role));
|
|
58
50
|
};
|
|
59
51
|
for (const item of raw) push(item);
|
|
60
52
|
if (out.length === 0) push(legacyRole);
|
|
@@ -64,97 +56,71 @@ function normalizeProfileNotes(value) {
|
|
|
64
56
|
if (typeof value !== "string") return "";
|
|
65
57
|
return value.replace(/\r\n/g, "\n").trim().slice(0, PROFILE_NOTES_MAX);
|
|
66
58
|
}
|
|
67
|
-
function
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (seen.has(key)) continue;
|
|
75
|
-
seen.add(key);
|
|
76
|
-
out.push(hint);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return out;
|
|
80
|
-
}
|
|
81
|
-
function teamHintsForCustomRole(role) {
|
|
82
|
-
const slug = role.trim().toLowerCase();
|
|
83
|
-
if (!slug) return [];
|
|
84
|
-
if (slug.endsWith("-team") || slug.endsWith("-squad")) return [slug];
|
|
85
|
-
return [`${slug}-team`, slug];
|
|
59
|
+
function foldLegacyRolesIntoNotes(roles, legacyRole, notes) {
|
|
60
|
+
const text = normalizeProfileNotes(notes);
|
|
61
|
+
const labels = legacyRoleLabels(roles, legacyRole);
|
|
62
|
+
if (!labels.length) return text;
|
|
63
|
+
if (text.toLowerCase().startsWith("roles:")) return text;
|
|
64
|
+
const prefix = `Roles: ${labels.join(", ")}`;
|
|
65
|
+
return text ? `${prefix}. ${text}` : prefix;
|
|
86
66
|
}
|
|
87
67
|
function resolveAccountProfile(input) {
|
|
88
68
|
return resolveViewerProfile(input);
|
|
89
69
|
}
|
|
90
70
|
function resolveViewerProfile(account, project) {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const projectNotes =
|
|
97
|
-
|
|
71
|
+
const accountNotes = foldLegacyRolesIntoNotes(
|
|
72
|
+
account?.roles,
|
|
73
|
+
account?.role,
|
|
74
|
+
account?.notes
|
|
75
|
+
);
|
|
76
|
+
const projectNotes = foldLegacyRolesIntoNotes(
|
|
77
|
+
project?.roles,
|
|
78
|
+
project?.role,
|
|
79
|
+
project?.notes
|
|
80
|
+
);
|
|
98
81
|
return {
|
|
99
|
-
|
|
100
|
-
roleLabels: roles.map((role) => accountRoleLabel(role)),
|
|
101
|
-
reviewTeamHints: reviewTeamHintsForRoles(roles),
|
|
102
|
-
notes,
|
|
82
|
+
notes: [accountNotes, projectNotes].filter(Boolean).join("\n"),
|
|
103
83
|
accountNotes,
|
|
104
|
-
projectNotes
|
|
105
|
-
rolesFromProject
|
|
84
|
+
projectNotes
|
|
106
85
|
};
|
|
107
86
|
}
|
|
108
|
-
function preferTeamsForRole(viewerTeams, hints) {
|
|
109
|
-
const teams = viewerTeams.map((t) => t.trim()).filter(Boolean);
|
|
110
|
-
const want = hints.map((t) => t.trim()).filter(Boolean);
|
|
111
|
-
if (!want.length) return teams;
|
|
112
|
-
if (!teams.length) return want;
|
|
113
|
-
const hintKeys = want.map((t) => t.toLowerCase());
|
|
114
|
-
const matched = teams.filter((team) => {
|
|
115
|
-
const key = team.toLowerCase();
|
|
116
|
-
return hintKeys.some((hint) => key === hint || key.includes(hint) || hint.includes(key));
|
|
117
|
-
});
|
|
118
|
-
return matched.length ? matched : teams;
|
|
119
|
-
}
|
|
120
87
|
function compactNotes(notes, max = 160) {
|
|
121
88
|
const oneLine = notes.replace(/\s+/g, " ").trim();
|
|
122
89
|
if (oneLine.length <= max) return oneLine;
|
|
123
90
|
return `${oneLine.slice(0, max - 1).trimEnd()}\u2026`;
|
|
124
91
|
}
|
|
125
92
|
function formatAccountProfilePlaybookLine(profile) {
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
if (profile.roleLabels.length) {
|
|
129
|
-
parts.push(
|
|
130
|
-
`roles ${profile.roleLabels.join(", ")} (tickets to work on / PRs to review; prefer those teams)`
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
if (profile.notes) {
|
|
134
|
-
parts.push(`notes: ${compactNotes(profile.notes)}`);
|
|
135
|
-
}
|
|
136
|
-
return `- Viewer profile: ${parts.join(". ")}. A team review request is not a claim \u2014 only an individual reviewer is. When they ask to find work, use these notes with list_issues (tickets) and list_prs(queue=review) (reviews) and show the options. Only create_thread and start when they also asked to start.`;
|
|
93
|
+
if (!profile.accountNotes) return "";
|
|
94
|
+
return `- Account context (Settings \u2192 Agents): ${compactNotes(profile.accountNotes)}. When they ask to find work, use this with list_issues (tickets) and list_prs(queue=review) (reviews) and show the options. Only create_thread and start when they also asked to start. To change it, show the proposed text, ask_user (Save this context / Do not save), wait, then update_viewer_context(scope=account, confirmed=true).`;
|
|
137
95
|
}
|
|
138
96
|
function formatProjectProfilePlaybookLines(projects) {
|
|
139
97
|
const lines = projects.map((project) => {
|
|
140
|
-
const roles = (project.roleLabels ?? []).filter(Boolean);
|
|
141
98
|
const notes = normalizeProfileNotes(project.notes);
|
|
142
|
-
if (!
|
|
143
|
-
|
|
144
|
-
if (roles.length) bits.push(`roles ${roles.join(", ")}`);
|
|
145
|
-
if (notes) bits.push(compactNotes(notes, 120));
|
|
146
|
-
return `- ${project.name}: ${bits.join(". ")}`;
|
|
99
|
+
if (!notes) return "";
|
|
100
|
+
return `- ${project.name}: ${compactNotes(notes, 120)}`;
|
|
147
101
|
}).filter(Boolean);
|
|
148
102
|
if (!lines.length) return "";
|
|
149
|
-
return [
|
|
150
|
-
"\
|
|
151
|
-
|
|
103
|
+
return [
|
|
104
|
+
"Project context (Settings \u2192 Projects; adds to account context):",
|
|
105
|
+
...lines
|
|
106
|
+
].join("\n");
|
|
152
107
|
}
|
|
153
108
|
function formatWorkspaceProfileSuffix(profile) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
109
|
+
if (!profile.projectNotes) return "";
|
|
110
|
+
return ` context:${compactNotes(profile.projectNotes, 80)}`;
|
|
111
|
+
}
|
|
112
|
+
function formatViewerContextDirective(profile) {
|
|
113
|
+
const account = profile.accountNotes || "(empty)";
|
|
114
|
+
const project = profile.projectNotes || "(empty \u2014 account context applies)";
|
|
115
|
+
return [
|
|
116
|
+
"Viewer context (Settings \u2192 Agents / Projects) \u2014 how to find this user\u2019s tickets and review PRs:",
|
|
117
|
+
`- Account: ${compactNotes(profile.accountNotes || account, 240)}`,
|
|
118
|
+
`- This project: ${compactNotes(profile.projectNotes || project, 240)}`,
|
|
119
|
+
"To change these, show the proposed text in chat, call ask_user (Save this context / Do not save), wait for the answer, then update_viewer_context with confirmed=true. Never write context without confirmation. get_viewer_context reads the current values."
|
|
120
|
+
].join("\n");
|
|
121
|
+
}
|
|
122
|
+
function formatViewerContextReminder() {
|
|
123
|
+
return "Viewer context: get_viewer_context to read; update_viewer_context only after ask_user confirms (Save this context / Do not save).";
|
|
158
124
|
}
|
|
159
125
|
|
|
160
126
|
// src/store/secret-vault.ts
|
|
@@ -654,9 +620,7 @@ function normalizeDefaults(raw) {
|
|
|
654
620
|
if (typeof source.fast === "boolean") {
|
|
655
621
|
out.fast = source.fast;
|
|
656
622
|
}
|
|
657
|
-
const
|
|
658
|
-
if (roles.length) out.roles = roles;
|
|
659
|
-
const notes = normalizeProfileNotes(source.notes);
|
|
623
|
+
const notes = foldLegacyRolesIntoNotes(source.roles, source.role, source.notes);
|
|
660
624
|
if (notes) out.notes = notes;
|
|
661
625
|
return out;
|
|
662
626
|
}
|
|
@@ -667,11 +631,9 @@ function normalizeProjectProfile(raw) {
|
|
|
667
631
|
if (!raw || typeof raw !== "object") return void 0;
|
|
668
632
|
const source = raw;
|
|
669
633
|
const out = {};
|
|
670
|
-
const
|
|
671
|
-
if (roles.length) out.roles = roles;
|
|
672
|
-
const notes = normalizeProfileNotes(source.notes);
|
|
634
|
+
const notes = foldLegacyRolesIntoNotes(source.roles, source.role, source.notes);
|
|
673
635
|
if (notes) out.notes = notes;
|
|
674
|
-
return out.
|
|
636
|
+
return out.notes ? out : void 0;
|
|
675
637
|
}
|
|
676
638
|
function normalizeProjects(raw) {
|
|
677
639
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
|
|
@@ -688,14 +650,19 @@ function findProjectProfileKey(projects, repoPath) {
|
|
|
688
650
|
const want = profileRepoKey(repoPath ?? "");
|
|
689
651
|
if (!want) return void 0;
|
|
690
652
|
if (projects[want]) return want;
|
|
653
|
+
let best;
|
|
654
|
+
let bestLen = 0;
|
|
691
655
|
for (const key of Object.keys(projects)) {
|
|
692
656
|
const stored = profileRepoKey(key);
|
|
693
657
|
if (!stored) continue;
|
|
694
|
-
if (stored === want || want.startsWith(`${stored}/`)
|
|
695
|
-
|
|
658
|
+
if (stored === want || want.startsWith(`${stored}/`)) {
|
|
659
|
+
if (stored.length > bestLen) {
|
|
660
|
+
best = key;
|
|
661
|
+
bestLen = stored.length;
|
|
662
|
+
}
|
|
696
663
|
}
|
|
697
664
|
}
|
|
698
|
-
return
|
|
665
|
+
return best;
|
|
699
666
|
}
|
|
700
667
|
function normalizeAdvanced(raw) {
|
|
701
668
|
if (!raw || typeof raw !== "object") return {};
|
|
@@ -1241,17 +1208,13 @@ function updateDefaultsSettings(patch) {
|
|
|
1241
1208
|
defaults.fast = Boolean(patch.fast);
|
|
1242
1209
|
}
|
|
1243
1210
|
}
|
|
1244
|
-
if ("roles" in patch) {
|
|
1245
|
-
const roles = normalizeAccountRoles(patch.roles);
|
|
1246
|
-
if (!roles.length) delete defaults.roles;
|
|
1247
|
-
else defaults.roles = roles;
|
|
1248
|
-
delete defaults.role;
|
|
1249
|
-
}
|
|
1250
1211
|
if ("notes" in patch) {
|
|
1251
1212
|
const notes = normalizeProfileNotes(patch.notes);
|
|
1252
1213
|
if (!notes) delete defaults.notes;
|
|
1253
1214
|
else defaults.notes = notes;
|
|
1254
1215
|
}
|
|
1216
|
+
delete defaults.roles;
|
|
1217
|
+
delete defaults.role;
|
|
1255
1218
|
return saveAppSettings({ ...current, defaults });
|
|
1256
1219
|
}
|
|
1257
1220
|
function updateProjectProfileSettings(repoPath, patch) {
|
|
@@ -1261,17 +1224,14 @@ function updateProjectProfileSettings(repoPath, patch) {
|
|
|
1261
1224
|
const projects = { ...current.projects };
|
|
1262
1225
|
const existing = projects[key] ?? {};
|
|
1263
1226
|
const next = { ...existing };
|
|
1264
|
-
if ("roles" in patch) {
|
|
1265
|
-
const roles = normalizeAccountRoles(patch.roles);
|
|
1266
|
-
if (!roles.length) delete next.roles;
|
|
1267
|
-
else next.roles = roles;
|
|
1268
|
-
}
|
|
1269
1227
|
if ("notes" in patch) {
|
|
1270
1228
|
const notes = normalizeProfileNotes(patch.notes);
|
|
1271
1229
|
if (!notes) delete next.notes;
|
|
1272
1230
|
else next.notes = notes;
|
|
1273
1231
|
}
|
|
1274
|
-
|
|
1232
|
+
delete next.roles;
|
|
1233
|
+
delete next.role;
|
|
1234
|
+
if (!next.notes) delete projects[key];
|
|
1275
1235
|
else projects[key] = next;
|
|
1276
1236
|
return saveAppSettings({ ...current, projects });
|
|
1277
1237
|
}
|
|
@@ -1296,6 +1256,62 @@ function resolveViewerProfileForRepo(repoPath, settings = loadAppSettings()) {
|
|
|
1296
1256
|
const project = key ? settings.projects[key] : void 0;
|
|
1297
1257
|
return resolveViewerProfile(settings.defaults, project);
|
|
1298
1258
|
}
|
|
1259
|
+
function readViewerContext(repoPath) {
|
|
1260
|
+
const settings = loadAppSettings();
|
|
1261
|
+
const key = findProjectProfileKey(settings.projects, repoPath);
|
|
1262
|
+
const resolved = resolveViewerProfile(
|
|
1263
|
+
settings.defaults,
|
|
1264
|
+
key ? settings.projects[key] : void 0
|
|
1265
|
+
);
|
|
1266
|
+
return {
|
|
1267
|
+
account: resolved.accountNotes,
|
|
1268
|
+
project: resolved.projectNotes,
|
|
1269
|
+
combined: resolved.notes,
|
|
1270
|
+
repoPath: key ?? (repoPath?.trim() ? profileRepoKey(repoPath) : null)
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
var CONFIRM_ACCOUNT = "Show the user the proposed account context, call ask_user (Save this context / Do not save), wait for their answer, then retry with confirmed=true.";
|
|
1274
|
+
var CONFIRM_PROJECT = "Show the user the proposed project context, call ask_user (Save this context / Do not save), wait for their answer, then retry with confirmed=true.";
|
|
1275
|
+
function writeViewerContext(input) {
|
|
1276
|
+
const proposed = normalizeProfileNotes(input.context);
|
|
1277
|
+
if (input.scope === "project") {
|
|
1278
|
+
const settings = loadAppSettings();
|
|
1279
|
+
const key = findProjectProfileKey(settings.projects, input.repoPath) ?? profileRepoKey(input.repoPath ?? "");
|
|
1280
|
+
if (!key) {
|
|
1281
|
+
return {
|
|
1282
|
+
ok: false,
|
|
1283
|
+
error: "repoPath required",
|
|
1284
|
+
message: "Pass repoPath (or run from a registered worktree) when updating project context.",
|
|
1285
|
+
current: "",
|
|
1286
|
+
proposed
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
const current2 = resolveViewerProfileForRepo(key, settings).projectNotes;
|
|
1290
|
+
if (!input.confirmed) {
|
|
1291
|
+
return {
|
|
1292
|
+
ok: false,
|
|
1293
|
+
error: "Confirmation required",
|
|
1294
|
+
message: CONFIRM_PROJECT,
|
|
1295
|
+
current: current2,
|
|
1296
|
+
proposed
|
|
1297
|
+
};
|
|
1298
|
+
}
|
|
1299
|
+
updateProjectProfileSettings(key, { notes: proposed || null });
|
|
1300
|
+
return { ok: true, scope: "project", context: proposed, repoPath: key };
|
|
1301
|
+
}
|
|
1302
|
+
const current = resolveAccountProfileFromSettings().accountNotes;
|
|
1303
|
+
if (!input.confirmed) {
|
|
1304
|
+
return {
|
|
1305
|
+
ok: false,
|
|
1306
|
+
error: "Confirmation required",
|
|
1307
|
+
message: CONFIRM_ACCOUNT,
|
|
1308
|
+
current,
|
|
1309
|
+
proposed
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
updateDefaultsSettings({ notes: proposed || null });
|
|
1313
|
+
return { ok: true, scope: "account", context: proposed };
|
|
1314
|
+
}
|
|
1299
1315
|
function resolveThreadDefaults(settings = loadAppSettings()) {
|
|
1300
1316
|
return {
|
|
1301
1317
|
agent: getDefaultAgent(settings),
|
|
@@ -1650,19 +1666,15 @@ function harnessEnvKey(harness) {
|
|
|
1650
1666
|
}
|
|
1651
1667
|
|
|
1652
1668
|
export {
|
|
1653
|
-
ACCOUNT_ROLE_PRESETS,
|
|
1654
|
-
ACCOUNT_ROLES,
|
|
1655
|
-
ACCOUNT_ROLE_MAX,
|
|
1656
1669
|
PROFILE_NOTES_MAX,
|
|
1657
|
-
|
|
1658
|
-
accountRoleLabel,
|
|
1659
|
-
reviewTeamHintsForRoles,
|
|
1670
|
+
foldLegacyRolesIntoNotes,
|
|
1660
1671
|
resolveAccountProfile,
|
|
1661
1672
|
resolveViewerProfile,
|
|
1662
|
-
preferTeamsForRole,
|
|
1663
1673
|
formatAccountProfilePlaybookLine,
|
|
1664
1674
|
formatProjectProfilePlaybookLines,
|
|
1665
1675
|
formatWorkspaceProfileSuffix,
|
|
1676
|
+
formatViewerContextDirective,
|
|
1677
|
+
formatViewerContextReminder,
|
|
1666
1678
|
setVaultMasterKey,
|
|
1667
1679
|
persistVaultKeyInKeychain,
|
|
1668
1680
|
readKeychainVaultKey,
|
|
@@ -1697,6 +1709,8 @@ export {
|
|
|
1697
1709
|
getDefaultFast,
|
|
1698
1710
|
resolveAccountProfileFromSettings,
|
|
1699
1711
|
resolveViewerProfileForRepo,
|
|
1712
|
+
readViewerContext,
|
|
1713
|
+
writeViewerContext,
|
|
1700
1714
|
resolveThreadDefaults,
|
|
1701
1715
|
resolveNewThreadOptions,
|
|
1702
1716
|
isLinearConnected,
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
resolveRepoRoot,
|
|
8
8
|
takenSlugsFromThread,
|
|
9
9
|
teamSlugFromName
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-IO5WGZRR.js";
|
|
11
11
|
import {
|
|
12
12
|
ATTACHMENTS_DIR,
|
|
13
13
|
attachmentsGitignoreBody
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
resolveThreadDefaults,
|
|
29
29
|
resolveViewerProfile,
|
|
30
30
|
resolveViewerProfileForRepo
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-LTXS26K5.js";
|
|
32
32
|
import {
|
|
33
33
|
appDataDir,
|
|
34
34
|
globalAgentCwd,
|
|
@@ -347,10 +347,10 @@ var COORDINATOR_TOOL_PLAYBOOK = [
|
|
|
347
347
|
"Role: you oversee Sideboard worktree agents across registered repos. You do not live inside one of those worktrees.",
|
|
348
348
|
"Sideboard MCP (fleet control \u2014 prefer these for status and orchestration):",
|
|
349
349
|
"Discover:",
|
|
350
|
-
"- list_workspaces \u2014 registered repos (path + github slug +
|
|
350
|
+
"- list_workspaces \u2014 registered repos (path + github slug + project context when set). Use account / project context to pick the right repo for tickets or reviews.",
|
|
351
351
|
"- list_board \u2014 Home Kanban of worktrees (New / Draft / Review / Merged; one card per checkout). Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 merged. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind, column, limit.",
|
|
352
|
-
'- list_branches / list_prs / list_issues \u2014 pass repoPath from list_workspaces. Review is PRs (the surface for assigned ticket work), not the tickets: "Get me N tickets to review" \u2192 list_prs(queue=review, limit=N) then create_thread sourceType=pr. That is open non-draft PRs labeled eng-review with no individual user reviewer yet. A team request (engineering-team) is not a claim \u2014 the viewer is on that team and can pick it up; claimed means an individual account is the reviewer. Bots ignored.
|
|
353
|
-
"- Find work: when they ask to find work, pick up tickets, or list reviews, use Settings \u2192 Agents (account) plus Settings \u2192 Projects (per-repo)
|
|
352
|
+
'- list_branches / list_prs / list_issues \u2014 pass repoPath from list_workspaces. Review is PRs (the surface for assigned ticket work), not the tickets: "Get me N tickets to review" \u2192 list_prs(queue=review, limit=N) then create_thread sourceType=pr. That is open non-draft PRs labeled eng-review with no individual user reviewer yet. A team request (engineering-team) is not a claim \u2014 the viewer is on that team and can pick it up; claimed means an individual account is the reviewer. Bots ignored. Use Settings \u2192 Agents / Projects context (roles, teams, labels \u2014 freeform text; project adds to account) to prefer the right queues. queue=mine is review-requested:@me; queue=approved|changes uses those labels. Also: state, label, reviewer=me|unassigned|login, query, limit default 40 max 250; raise limit or tighten when truncated. list_issues (query, assignee=me|unassigned|all|user, limit default 40 max 250) lists Linear, AbleTime, or GitHub tickets \u2014 do not use it for that review-inbox ask. When they ask for tickets to work on, follow their context (assignee=me or unassigned as it says).',
|
|
353
|
+
"- Find work: when they ask to find work, pick up tickets, or list reviews, use Settings \u2192 Agents (account) plus Settings \u2192 Projects (per-repo) context. Tickets \u2192 Sideboard list_issues / linear_* (Account Linear). Reviews \u2192 list_prs(queue=review). Do not call Claude Linear MCP or any other vendor Linear MCP \u2014 those HTTP connectors hang or flap on the first turn. If a vendor MCP is down or reconnecting, ignore it and keep going with Sideboard tools. Show the options \u2014 do not create_thread or start unless they also asked to start (e.g. \u201Cfind me work and start it\u201D). Do not start this unprompted. To change that context, show the proposed text, ask_user (Save this context / Do not save), wait, then update_viewer_context with confirmed=true. Never write it without confirmation.",
|
|
354
354
|
"- linear_* (when Linear is connected) \u2014 list_teams for team key/states; get/create/update/comment with ENG-123. Worktree agents have the same Account tools \u2014 prefer they comment, update status, and create spin-offs (parent=) on their own ticket. Scope errors: reconnect Linear in Account settings. Prefer these over any Linear MCP the CLI may still list.",
|
|
355
355
|
"- github_* \u2014 get/comment/update/create GitHub issues via Account `gh` (#123). Worktree agents have these too. Prefer over any vendor GitHub MCP. Pass parent= for spin-offs.",
|
|
356
356
|
"- abletime_* (when AbleTime is connected) \u2014 orientation first; get/comment/update/create (parent= for spin-offs); ensure_task when work has no ticket (or create_thread from the default branch auto-creates one). Worktree agents have the same Account tools.",
|
|
@@ -360,7 +360,8 @@ var COORDINATOR_TOOL_PLAYBOOK = [
|
|
|
360
360
|
"- get_pr_stack / open_pr_stack_layers / add_stack_layer / create_pr_stack \u2014 GitHub stacked PRs (`gh stack`); one worktree per layer",
|
|
361
361
|
"- list_models \u2014 only when you need a specific model (rare); otherwise omit model so Account defaults apply",
|
|
362
362
|
"- list_threads / get_thread \u2014 live thread list (parent id + last message preview). get_thread on this orchestration chat lists child worktree agents (status + lastText). Also includes usage / lastTurnUsage.",
|
|
363
|
-
"- ask_user \u2014 composer multiple-choice only when blocked on a concrete choice (approach fork, which API). Never for hellos, check-ins, or invented \u201Cwhat should we do?\u201D menus \u2014 reply in chat. Explain options first, description on every option, then wait.",
|
|
363
|
+
"- ask_user \u2014 composer multiple-choice only when blocked on a concrete choice (approach fork, which API, Save this context / Do not save). Never for hellos, check-ins, or invented \u201Cwhat should we do?\u201D menus \u2014 reply in chat. Explain options first, description on every option, then wait.",
|
|
364
|
+
"- get_viewer_context / update_viewer_context \u2014 read or replace Settings \u2192 Agents (account) and Settings \u2192 Projects (per-repo) context. You can update a project: pass scope=project and repoPath from list_workspaces (this cwd is not a project). update_viewer_context requires confirmed=true after ask_user. Never write context unprompted.",
|
|
364
365
|
"- set_caffeinate \u2014 keep this Mac awake across turns (macOS caffeinate). Turn on for Slack / away-from-keyboard work, overnight schedules, or when the user will be away. Turn OFF when they say they are done, wrapping up, going to sleep, or no longer need the machine awake. Closing this chat also releases it.",
|
|
365
366
|
"- list_schedules / create_schedule / update_schedule / delete_schedule / run_schedule \u2014 local jobs that send a prompt to an orchestration chat (threadId or self) or start a new Global chat (omit threadId). One-shot `at`, interval `every` (15m/1h/6h/1d), or 5-field `cron`. Recurring jobs without threadId open a new chat each run. Jobs fire only while Sideboard.app is running; sleep skips until wake. Overnight/unattended runs: ask the user to enable Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled, or call set_caffeinate.",
|
|
366
367
|
"Workspaces:",
|
|
@@ -419,16 +420,15 @@ function accountRolePlaybookLine() {
|
|
|
419
420
|
return "";
|
|
420
421
|
}
|
|
421
422
|
}
|
|
422
|
-
function
|
|
423
|
+
function accountContextReminderLine() {
|
|
423
424
|
let profile;
|
|
424
425
|
try {
|
|
425
426
|
profile = resolveAccountProfileFromSettings();
|
|
426
427
|
} catch {
|
|
427
428
|
return "";
|
|
428
429
|
}
|
|
429
|
-
if (
|
|
430
|
-
|
|
431
|
-
return `- Viewer profile: ${roles}. Use Settings notes with list_issues / list_prs when they ask to find work.`;
|
|
430
|
+
if (!profile.accountNotes) return "";
|
|
431
|
+
return "- Account context is set. Use get_viewer_context / Settings notes with list_issues / list_prs when they ask to find work. Update only after ask_user confirms.";
|
|
432
432
|
}
|
|
433
433
|
function projectProfilePlaybookBlock() {
|
|
434
434
|
let settings;
|
|
@@ -445,7 +445,6 @@ function projectProfilePlaybookBlock() {
|
|
|
445
445
|
const key = path.replace(/\/+$/, "");
|
|
446
446
|
return {
|
|
447
447
|
name: names.get(key) ?? key.split("/").filter(Boolean).pop() ?? path,
|
|
448
|
-
roleLabels: resolved.rolesFromProject ? resolved.roleLabels : [],
|
|
449
448
|
notes: resolved.projectNotes
|
|
450
449
|
};
|
|
451
450
|
});
|
|
@@ -459,7 +458,7 @@ function coordinatorTurnReminder(opts) {
|
|
|
459
458
|
`- YOUR orchestration thread id is ${opts.parentId} \u2014 pass parentThreadId="${opts.parentId}" on create_thread, or omit it.`,
|
|
460
459
|
goal ? `- Goal / title: ${goal}` : null,
|
|
461
460
|
accountDefaultsPlaybookLine(),
|
|
462
|
-
|
|
461
|
+
accountContextReminderLine() || null,
|
|
463
462
|
"- Status: list_board (worktree Kanban: New \u2192 Draft \u2192 Review \u2192 Merged) or list_threads. Link chats as `[Title](sideboard://thread/<id>)`. Merge only if the user asked. If a child is stopped/error/broken, it did not finish \u2014 resume or tell the user."
|
|
464
463
|
].filter(Boolean).join("\n");
|
|
465
464
|
}
|
|
@@ -513,7 +512,7 @@ function ensureGlobalCoordinatorCwd(opts) {
|
|
|
513
512
|
"Never pass `agent=codex` when you yourself are Codex \u2014 nested Codex deadlocks on shared ~/.codex locks. Omit agent (Account default) or use cursor/claude.",
|
|
514
513
|
"Typical flow (Home board): list_board \u2192 create_thread (sourceType=ticket|pr|branch) \u2192 send_to_thread \u2192 wait_for_turn (loop while stillRunning) \u2192 ask_git create-draft \u2192 wait_for_turn. Merge only if the user explicitly asked (`ask_git` merge).",
|
|
515
514
|
"Typical flow (branch / explicit source): list_workspaces \u2192 list_branches|list_prs|list_issues \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn (loop while stillRunning) \u2192 ask_git create-draft.",
|
|
516
|
-
"Typical flow (find work): list_workspaces \u2192 pick repo(s) matching
|
|
515
|
+
"Typical flow (find work): list_workspaces \u2192 pick repo(s) matching account / project context \u2192 Sideboard list_issues / linear_* (tickets) or list_prs(queue=review) (reviews) \u2192 show the options. Never wait on Claude Linear MCP. Only create_thread + send_to_thread when they asked to start (e.g. \u201Cfind me work and start it\u201D) \u2014 then wait_for_turn (loop while stillRunning) \u2192 ask_git create-draft.",
|
|
517
516
|
'Typical flow (review inbox): list_workspaces \u2192 list_prs(queue=review, limit=N) \u2192 show those PRs (ticket ids in the title when present). create_thread sourceType=pr only when they asked to start / do the work. Do not list_issues for "tickets to review".',
|
|
518
517
|
"Typical flow (new app): Bash create/clone under repos dir \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread (implement) \u2192 wait_for_turn (loop while stillRunning) \u2192 ask_git create-draft.",
|
|
519
518
|
"Always ask worktree agents to commit, push, and open draft PRs (`ask_git` / `send_to_thread`). If the user gave a goal (Greptile 5/5, CI green), pass that goal through so they watch-fix-push until it lands \u2014 do not start that loop on a plain push, and do not tell the human to poll. Tell them to merge only when the user explicitly asked. The worktree agent runs git/gh; never merge from this orchestration cwd."
|
|
@@ -21,11 +21,8 @@ import {
|
|
|
21
21
|
} from "./chunk-FNQNJOHY.js";
|
|
22
22
|
import {
|
|
23
23
|
getGithubGitAuthMode,
|
|
24
|
-
getGithubPat
|
|
25
|
-
|
|
26
|
-
resolveViewerProfile,
|
|
27
|
-
resolveViewerProfileForRepo
|
|
28
|
-
} from "./chunk-D7EGJ3EI.js";
|
|
24
|
+
getGithubPat
|
|
25
|
+
} from "./chunk-LTXS26K5.js";
|
|
29
26
|
import {
|
|
30
27
|
worktreesRoot
|
|
31
28
|
} from "./chunk-JTJHOYCU.js";
|
|
@@ -2023,14 +2020,7 @@ async function listPrs(repoPath, opts) {
|
|
|
2023
2020
|
const limit = resolved.limit ?? 200;
|
|
2024
2021
|
const reviewerKey = (resolved.reviewer ?? "").trim().toLowerCase();
|
|
2025
2022
|
const needsUnclaimed = reviewerKey === "unassigned" || reviewerKey === "none" || reviewerKey === "null";
|
|
2026
|
-
|
|
2027
|
-
try {
|
|
2028
|
-
profile = resolveViewerProfileForRepo(repoPath);
|
|
2029
|
-
} catch {
|
|
2030
|
-
profile = resolveViewerProfile();
|
|
2031
|
-
}
|
|
2032
|
-
const fetchedTeams = needsUnclaimed ? await listGithubViewerTeamSlugs(repoPath).catch(() => []) : [];
|
|
2033
|
-
const viewerTeams = resolved.viewerTeams ?? (needsUnclaimed ? preferTeamsForRole(fetchedTeams, profile.reviewTeamHints) : void 0);
|
|
2023
|
+
const viewerTeams = resolved.viewerTeams ?? (needsUnclaimed ? await listGithubViewerTeamSlugs(repoPath).catch(() => []) : void 0);
|
|
2034
2024
|
const fetchLimit = prListFetchLimit(limit, resolved.reviewer);
|
|
2035
2025
|
const args = buildGhPrListArgs({
|
|
2036
2026
|
slug,
|