squadrant 0.22.0 → 0.24.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/index.js +427 -152
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +267 -34
- package/dist/squadrantd.js.map +1 -1
- package/package.json +2 -1
- package/plugin/skills/gate/SKILL.md +8 -0
- package/plugin/skills/set-gate/SKILL.md +66 -0
package/dist/index.js
CHANGED
|
@@ -98,6 +98,9 @@ function isGateMode(v2) {
|
|
|
98
98
|
function isGatePolicy(v2) {
|
|
99
99
|
return GATE_POLICIES.includes(v2);
|
|
100
100
|
}
|
|
101
|
+
function isGateEngine(v2) {
|
|
102
|
+
return GATE_ENGINES.includes(v2);
|
|
103
|
+
}
|
|
101
104
|
function isThinkingLevel(v2) {
|
|
102
105
|
return THINKING_LEVELS.includes(v2);
|
|
103
106
|
}
|
|
@@ -198,13 +201,14 @@ function saveConfig(config, configPath = DEFAULT_CONFIG_PATH) {
|
|
|
198
201
|
function resolveHome(p) {
|
|
199
202
|
return p.startsWith("~") ? p.replace("~", os.homedir()) : p;
|
|
200
203
|
}
|
|
201
|
-
var ROUTER_KINDS, GATE_MODES, GATE_POLICIES, THINKING_LEVELS, CONTROL_CHANNEL_MODES, DEFAULT_CONFIG_PATH, CONFIG_DIR, DAEMON_SOCK_PATH, DEFAULT_CREW_ROUTING_RULES;
|
|
204
|
+
var ROUTER_KINDS, GATE_MODES, GATE_POLICIES, GATE_ENGINES, THINKING_LEVELS, CONTROL_CHANNEL_MODES, DEFAULT_CONFIG_PATH, CONFIG_DIR, DAEMON_SOCK_PATH, DEFAULT_CREW_ROUTING_RULES;
|
|
202
205
|
var init_config = __esm({
|
|
203
206
|
"packages/shared/dist/config.js"() {
|
|
204
207
|
init_config_io();
|
|
205
208
|
ROUTER_KINDS = ["opencode-go", "openrouter", "ccr", "litellm", "custom"];
|
|
206
209
|
GATE_MODES = ["on", "off", "auto"];
|
|
207
210
|
GATE_POLICIES = ["deny-dangerous", "ask-on-doubt"];
|
|
211
|
+
GATE_ENGINES = ["router", "auto-gate"];
|
|
208
212
|
THINKING_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
209
213
|
CONTROL_CHANNEL_MODES = /* @__PURE__ */ new Set(["off", "shadow", "on"]);
|
|
210
214
|
DEFAULT_CONFIG_PATH = process.env.SQUADRANT_CONFIG || path2.join(os.homedir(), ".config", "squadrant", "config.json");
|
|
@@ -221,7 +225,12 @@ var init_config = __esm({
|
|
|
221
225
|
|
|
222
226
|
// packages/shared/dist/provider-preset.js
|
|
223
227
|
function isProviderPresetId(v2) {
|
|
224
|
-
return v2 === "a" || v2 === "b" || v2 === "c"
|
|
228
|
+
return v2 === "a" || v2 === "b" || v2 === "c";
|
|
229
|
+
}
|
|
230
|
+
function normalizePresetId(v2) {
|
|
231
|
+
if (v2 === "d")
|
|
232
|
+
return "c";
|
|
233
|
+
return isProviderPresetId(v2) ? v2 : void 0;
|
|
225
234
|
}
|
|
226
235
|
function presetCrewRouting(id) {
|
|
227
236
|
const base = (tier) => {
|
|
@@ -244,17 +253,10 @@ function presetCrewRouting(id) {
|
|
|
244
253
|
{ ...base("daily"), agent: "opencode", model: OPENCODE_MODEL }
|
|
245
254
|
];
|
|
246
255
|
case "c":
|
|
247
|
-
return [
|
|
248
|
-
{ ...base("extreme"), agent: "claude", backend: "proxy", model: ROUTER_PRESET_MODEL },
|
|
249
|
-
{ ...base("hard"), agent: "claude", backend: "proxy", model: ROUTER_PRESET_MODEL },
|
|
250
|
-
codex("mobile"),
|
|
251
|
-
{ ...base("daily"), agent: "opencode", model: OPENCODE_MODEL }
|
|
252
|
-
];
|
|
253
|
-
case "d":
|
|
254
256
|
return ["extreme", "hard", "mobile", "daily"].map(codex);
|
|
255
257
|
}
|
|
256
258
|
}
|
|
257
|
-
function providerPresetDefaults(id
|
|
259
|
+
function providerPresetDefaults(id) {
|
|
258
260
|
switch (id) {
|
|
259
261
|
case "a":
|
|
260
262
|
return {
|
|
@@ -283,28 +285,6 @@ function providerPresetDefaults(id, router) {
|
|
|
283
285
|
};
|
|
284
286
|
}
|
|
285
287
|
case "c": {
|
|
286
|
-
if (!router) {
|
|
287
|
-
throw new Error("preset C (claude harness + router backend) requires a router config \u2014 set defaults.router first");
|
|
288
|
-
}
|
|
289
|
-
const routed = { agent: "claude", backend: "proxy", model: ROUTER_PRESET_MODEL };
|
|
290
|
-
return {
|
|
291
|
-
roles: {
|
|
292
|
-
command: { ...routed },
|
|
293
|
-
captain: { ...routed },
|
|
294
|
-
crew: { ...routed },
|
|
295
|
-
exploration: { ...routed },
|
|
296
|
-
side: { ...routed }
|
|
297
|
-
},
|
|
298
|
-
// Auto mode's classifier is hardcoded to Sonnet 5 and fails closed on a
|
|
299
|
-
// router upstream — every claude session must use a manual mode so the
|
|
300
|
-
// U7 gate owns the prompt.
|
|
301
|
-
permissions: { command: "default", captain: "default", crew: "default" },
|
|
302
|
-
crewRouting: { rules: presetCrewRouting("c") },
|
|
303
|
-
router,
|
|
304
|
-
gate: { mode: "on" }
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
case "d": {
|
|
308
288
|
const codex = { agent: "codex" };
|
|
309
289
|
return {
|
|
310
290
|
roles: {
|
|
@@ -315,7 +295,7 @@ function providerPresetDefaults(id, router) {
|
|
|
315
295
|
side: { ...codex }
|
|
316
296
|
},
|
|
317
297
|
permissions: {},
|
|
318
|
-
crewRouting: { rules: presetCrewRouting("
|
|
298
|
+
crewRouting: { rules: presetCrewRouting("c") }
|
|
319
299
|
};
|
|
320
300
|
}
|
|
321
301
|
}
|
|
@@ -325,13 +305,11 @@ function detectProviderPresetId(config) {
|
|
|
325
305
|
if (crew?.agent === "opencode")
|
|
326
306
|
return "b";
|
|
327
307
|
if (crew?.agent === "codex")
|
|
328
|
-
return "d";
|
|
329
|
-
if (crew?.backend && crew.backend !== "native")
|
|
330
308
|
return "c";
|
|
331
309
|
return "a";
|
|
332
310
|
}
|
|
333
311
|
function applyProviderPreset(config, id, opts) {
|
|
334
|
-
const preset = providerPresetDefaults(id
|
|
312
|
+
const preset = providerPresetDefaults(id);
|
|
335
313
|
const next = structuredClone(config);
|
|
336
314
|
const changes = [];
|
|
337
315
|
if (opts.overwrite) {
|
|
@@ -357,21 +335,9 @@ function applyProviderPreset(config, id, opts) {
|
|
|
357
335
|
next.defaults.permissions = { ...preset.permissions };
|
|
358
336
|
changes.push("defaults.permissions");
|
|
359
337
|
}
|
|
360
|
-
if (preset.router) {
|
|
361
|
-
if (opts.overwrite || next.defaults.router === void 0) {
|
|
362
|
-
next.defaults.router = structuredClone(preset.router);
|
|
363
|
-
changes.push("defaults.router");
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
if (preset.gate) {
|
|
367
|
-
if (opts.overwrite || next.defaults.gate === void 0) {
|
|
368
|
-
next.defaults.gate = { ...preset.gate };
|
|
369
|
-
changes.push("defaults.gate");
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
338
|
return { config: next, changes };
|
|
373
339
|
}
|
|
374
|
-
var PROVIDER_PRESETS,
|
|
340
|
+
var PROVIDER_PRESETS, OPENCODE_MODEL;
|
|
375
341
|
var init_provider_preset = __esm({
|
|
376
342
|
"packages/shared/dist/provider-preset.js"() {
|
|
377
343
|
init_config();
|
|
@@ -390,20 +356,11 @@ var init_provider_preset = __esm({
|
|
|
390
356
|
},
|
|
391
357
|
{
|
|
392
358
|
id: "c",
|
|
393
|
-
label: "Claude harness + router backend (advanced)",
|
|
394
|
-
summary: "Claude harness against a router upstream. The U7 gate replaces auto mode.",
|
|
395
|
-
requiresAnthropic: false
|
|
396
|
-
},
|
|
397
|
-
{
|
|
398
|
-
id: "d",
|
|
399
359
|
label: "Codex (ChatGPT Pro)",
|
|
400
360
|
summary: "All roles run on the codex harness.",
|
|
401
361
|
requiresAnthropic: false
|
|
402
362
|
}
|
|
403
363
|
];
|
|
404
|
-
ROUTER_PRESET_MODEL = "deepseek-v4.1-flash";
|
|
405
|
-
DEFAULT_ROUTER_KIND = "opencode-go";
|
|
406
|
-
DEFAULT_ROUTER_BASE_URL = "https://opencode.ai/zen/go";
|
|
407
364
|
OPENCODE_MODEL = "opencode-go/deepseek-v4.1-flash";
|
|
408
365
|
}
|
|
409
366
|
});
|
|
@@ -1657,14 +1614,12 @@ __export(dist_exports, {
|
|
|
1657
1614
|
DEFAULT_CONFIG_PATH: () => DEFAULT_CONFIG_PATH,
|
|
1658
1615
|
DEFAULT_CREW_ROUTING_RULES: () => DEFAULT_CREW_ROUTING_RULES,
|
|
1659
1616
|
DEFAULT_NOTIFY: () => DEFAULT_NOTIFY,
|
|
1660
|
-
|
|
1661
|
-
DEFAULT_ROUTER_KIND: () => DEFAULT_ROUTER_KIND,
|
|
1617
|
+
GATE_ENGINES: () => GATE_ENGINES,
|
|
1662
1618
|
GATE_MODES: () => GATE_MODES,
|
|
1663
1619
|
GATE_POLICIES: () => GATE_POLICIES,
|
|
1664
1620
|
MANAGED_TARGETS: () => MANAGED_TARGETS,
|
|
1665
1621
|
PROVIDER_PRESETS: () => PROVIDER_PRESETS,
|
|
1666
1622
|
ROUTER_KINDS: () => ROUTER_KINDS,
|
|
1667
|
-
ROUTER_PRESET_MODEL: () => ROUTER_PRESET_MODEL,
|
|
1668
1623
|
SAFE_KINDS: () => SAFE_KINDS,
|
|
1669
1624
|
SOCKET_CONTROL_MODE_PATH: () => SOCKET_CONTROL_MODE_PATH,
|
|
1670
1625
|
SPOKE_SUBDIRS: () => SPOKE_SUBDIRS,
|
|
@@ -1705,6 +1660,7 @@ __export(dist_exports, {
|
|
|
1705
1660
|
isBackendMode: () => isBackendMode,
|
|
1706
1661
|
isCacheStale: () => isCacheStale,
|
|
1707
1662
|
isDaemonCachedKey: () => isDaemonCachedKey,
|
|
1663
|
+
isGateEngine: () => isGateEngine,
|
|
1708
1664
|
isGateMode: () => isGateMode,
|
|
1709
1665
|
isGatePolicy: () => isGatePolicy,
|
|
1710
1666
|
isNewerVersion: () => isNewerVersion,
|
|
@@ -1720,6 +1676,7 @@ __export(dist_exports, {
|
|
|
1720
1676
|
mirrorDir: () => mirrorDir,
|
|
1721
1677
|
mirrorFlat: () => mirrorFlat,
|
|
1722
1678
|
needsCheck: () => needsCheck,
|
|
1679
|
+
normalizePresetId: () => normalizePresetId,
|
|
1723
1680
|
notifyIfUpdateAvailable: () => notifyIfUpdateAvailable,
|
|
1724
1681
|
parseSection: () => parseSection,
|
|
1725
1682
|
parseThinkingLevel: () => parseThinkingLevel,
|
|
@@ -2027,10 +1984,11 @@ function formatDelegationReport(rec, originProject, targetProject) {
|
|
|
2027
1984
|
return null;
|
|
2028
1985
|
}
|
|
2029
1986
|
}
|
|
2030
|
-
function firePush(deps, project, prev, next, event, lastCaptainTurnAt) {
|
|
1987
|
+
function firePush(deps, project, prev, next, event, lastCaptainTurnAt, prevRec) {
|
|
2031
1988
|
if (!deps.notify)
|
|
2032
1989
|
return;
|
|
2033
|
-
|
|
1990
|
+
const questionChanged = prev === "blocked" && next.state === "blocked" && prevRec != null && (prevRec.question ?? "").trim() !== (next.question ?? "").trim();
|
|
1991
|
+
if (prev === next.state && !questionChanged)
|
|
2034
1992
|
return;
|
|
2035
1993
|
if (!ATTENTION_STATES.has(next.state))
|
|
2036
1994
|
return;
|
|
@@ -2068,6 +2026,7 @@ function createDaemon(deps) {
|
|
|
2068
2026
|
const { store, now } = deps;
|
|
2069
2027
|
const lastCaptainTurnAt = /* @__PURE__ */ new Map();
|
|
2070
2028
|
const quietNotifiedAt = /* @__PURE__ */ new Map();
|
|
2029
|
+
const blockedRenudgedFor = /* @__PURE__ */ new Map();
|
|
2071
2030
|
const resendAttemptedAt = /* @__PURE__ */ new Map();
|
|
2072
2031
|
const firstTurnUndeliveredBudgetMs = deps.firstTurnUndeliveredBudgetMs ?? DEFAULT_FIRST_TURN_UNDELIVERED_BUDGET_MS;
|
|
2073
2032
|
const firstTurnResendCooldownMs = deps.firstTurnResendCooldownMs ?? DEFAULT_FIRST_TURN_RESEND_COOLDOWN_MS;
|
|
@@ -2088,7 +2047,7 @@ function createDaemon(deps) {
|
|
|
2088
2047
|
const next = reduce(cur, event, now());
|
|
2089
2048
|
if (next !== cur) {
|
|
2090
2049
|
store.put(next);
|
|
2091
|
-
firePush(deps, project, cur.state, next, event, lastCaptainTurnAt.get(next.id));
|
|
2050
|
+
firePush(deps, project, cur.state, next, event, lastCaptainTurnAt.get(next.id), cur);
|
|
2092
2051
|
}
|
|
2093
2052
|
return next;
|
|
2094
2053
|
}
|
|
@@ -2240,6 +2199,8 @@ function createDaemon(deps) {
|
|
|
2240
2199
|
}
|
|
2241
2200
|
const recoveryPromises = [];
|
|
2242
2201
|
for (const r of store.listAll()) {
|
|
2202
|
+
if (r.state !== "blocked")
|
|
2203
|
+
blockedRenudgedFor.delete(r.id);
|
|
2243
2204
|
if (TERMINAL_STATES.has(r.state) && t - r.lastHeartbeat > TERMINAL_RECORD_TTL_MS) {
|
|
2244
2205
|
store.delete(r.project, r.id);
|
|
2245
2206
|
continue;
|
|
@@ -2302,6 +2263,27 @@ function createDaemon(deps) {
|
|
|
2302
2263
|
continue;
|
|
2303
2264
|
}
|
|
2304
2265
|
}
|
|
2266
|
+
if (r.state === "blocked" && !r.operatorHold) {
|
|
2267
|
+
const threshold = deps.blockedRenudgeMs ?? DEFAULT_BLOCKED_RENUDGE_MS;
|
|
2268
|
+
const blockedFor = t - r.lastHeartbeat;
|
|
2269
|
+
const episodeKey = r.question ?? "";
|
|
2270
|
+
if (blockedFor > threshold && blockedRenudgedFor.get(r.id) !== episodeKey) {
|
|
2271
|
+
blockedRenudgedFor.set(r.id, episodeKey);
|
|
2272
|
+
if (deps.notify) {
|
|
2273
|
+
const tag = crewTag(r);
|
|
2274
|
+
const mins = Math.max(1, Math.round(blockedFor / 6e4));
|
|
2275
|
+
const message = `CREW BLOCKED REMINDER ${tag}: still blocked ~${mins}min (reminder \u2014 same question, not a new one): ${(r.question ?? "(no question)").trim()}`;
|
|
2276
|
+
const synthEvent = { type: "task.blocked", id: r.id, reason: "renudge", question: r.question ?? "" };
|
|
2277
|
+
try {
|
|
2278
|
+
const p = deps.notify({ project: r.project, message, record: r, event: synthEvent });
|
|
2279
|
+
if (p && typeof p.catch === "function")
|
|
2280
|
+
p.catch(() => {
|
|
2281
|
+
});
|
|
2282
|
+
} catch {
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2305
2287
|
if (r.mode === "interactive" && r.state === "submitted" && !r.firstTurnConfirmedAt) {
|
|
2306
2288
|
const undeliveredMs = t - r.createdAt;
|
|
2307
2289
|
if (undeliveredMs > firstTurnUndeliveredBudgetMs) {
|
|
@@ -2391,7 +2373,7 @@ function createDaemon(deps) {
|
|
|
2391
2373
|
}
|
|
2392
2374
|
};
|
|
2393
2375
|
}
|
|
2394
|
-
var DEFAULT_FIRST_TURN_UNDELIVERED_BUDGET_MS, DEFAULT_FIRST_TURN_RESEND_COOLDOWN_MS, DEFAULT_TASK_TIMEOUT_MS, TERMINAL_RECORD_TTL_MS, TERMINAL_RECORD_KEEP_PER_PROJECT, ATTENTION_STATES, REAPABLE_SURFACE_STATES, IDLE_DEBOUNCE_MS, KNOWN_EVENT_TYPES;
|
|
2376
|
+
var DEFAULT_FIRST_TURN_UNDELIVERED_BUDGET_MS, DEFAULT_FIRST_TURN_RESEND_COOLDOWN_MS, DEFAULT_BLOCKED_RENUDGE_MS, DEFAULT_TASK_TIMEOUT_MS, TERMINAL_RECORD_TTL_MS, TERMINAL_RECORD_KEEP_PER_PROJECT, ATTENTION_STATES, REAPABLE_SURFACE_STATES, IDLE_DEBOUNCE_MS, KNOWN_EVENT_TYPES;
|
|
2395
2377
|
var init_reduce = __esm({
|
|
2396
2378
|
"packages/core/dist/daemon/reduce.js"() {
|
|
2397
2379
|
init_dist();
|
|
@@ -2399,6 +2381,7 @@ var init_reduce = __esm({
|
|
|
2399
2381
|
init_watchdog();
|
|
2400
2382
|
DEFAULT_FIRST_TURN_UNDELIVERED_BUDGET_MS = 12e4;
|
|
2401
2383
|
DEFAULT_FIRST_TURN_RESEND_COOLDOWN_MS = 6e4;
|
|
2384
|
+
DEFAULT_BLOCKED_RENUDGE_MS = 30 * 60 * 1e3;
|
|
2402
2385
|
DEFAULT_TASK_TIMEOUT_MS = 8 * 60 * 60 * 1e3;
|
|
2403
2386
|
TERMINAL_RECORD_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2404
2387
|
TERMINAL_RECORD_KEEP_PER_PROJECT = 20;
|
|
@@ -4529,8 +4512,11 @@ function buildRouterEnv(creds, model) {
|
|
|
4529
4512
|
if (headers)
|
|
4530
4513
|
env.ANTHROPIC_CUSTOM_HEADERS = headers;
|
|
4531
4514
|
}
|
|
4532
|
-
if (model)
|
|
4515
|
+
if (model) {
|
|
4533
4516
|
env.ANTHROPIC_MODEL = model;
|
|
4517
|
+
env.CLAUDE_CODE_SUBAGENT_MODEL = model;
|
|
4518
|
+
env.ANTHROPIC_SMALL_FAST_MODEL = model;
|
|
4519
|
+
}
|
|
4534
4520
|
return env;
|
|
4535
4521
|
}
|
|
4536
4522
|
function mergeClaudeEnvRouterSettings(routerEnv, claudeEnv) {
|
|
@@ -5413,8 +5399,10 @@ async function resolveAndPersistOpencodeCaptain(opts) {
|
|
|
5413
5399
|
sleep: opts.sleep,
|
|
5414
5400
|
fetchImpl: opts.fetchImpl
|
|
5415
5401
|
});
|
|
5416
|
-
if (!sessionId)
|
|
5402
|
+
if (!sessionId) {
|
|
5403
|
+
opts.log?.(`opencode captain session resolution timed out for ${opts.project} (${opts.directory}) \u2014 captain.json has no session id yet; the daemon's own re-resolve fills it in once a session appears`);
|
|
5417
5404
|
return null;
|
|
5405
|
+
}
|
|
5418
5406
|
writeCaptainAddress(opts.stateRoot, opts.project, {
|
|
5419
5407
|
agent: "opencode",
|
|
5420
5408
|
port: opts.port,
|
|
@@ -5434,6 +5422,33 @@ var init_opencode_session = __esm({
|
|
|
5434
5422
|
function discoverCaptainSurface(surfaces, captainTitle) {
|
|
5435
5423
|
return surfaces.find((s) => s.title === captainTitle) ?? null;
|
|
5436
5424
|
}
|
|
5425
|
+
async function reresolveOpencodeAddress(rec) {
|
|
5426
|
+
const live = discoverLiveOpencodeServer({ directory: rec.directory, sessionId: rec.sessionId });
|
|
5427
|
+
const port = live?.port ?? rec.port;
|
|
5428
|
+
if (port === void 0)
|
|
5429
|
+
return null;
|
|
5430
|
+
const newest = newestSessionInDirectory(await listSessions(port), rec.directory, Date.parse(rec.launchedAt));
|
|
5431
|
+
const sessionId = newest ?? live?.sessionId ?? rec.sessionId;
|
|
5432
|
+
if (!sessionId || port === rec.port && sessionId === rec.sessionId)
|
|
5433
|
+
return null;
|
|
5434
|
+
return { port, sessionId };
|
|
5435
|
+
}
|
|
5436
|
+
async function healStaleOpencodeAddress(stateRoot, project, log, logPrefix) {
|
|
5437
|
+
const rec = readCaptainAddress(stateRoot, project);
|
|
5438
|
+
if (!rec)
|
|
5439
|
+
return false;
|
|
5440
|
+
const resolved = await reresolveOpencodeAddress(rec);
|
|
5441
|
+
if (!resolved)
|
|
5442
|
+
return false;
|
|
5443
|
+
const current = readCaptainAddress(stateRoot, project);
|
|
5444
|
+
if (!current || current.launchedAt !== rec.launchedAt) {
|
|
5445
|
+
log(`captain-channel ${project}: skipped stale-address heal \u2014 a newer launch record won the race`);
|
|
5446
|
+
return false;
|
|
5447
|
+
}
|
|
5448
|
+
writeCaptainAddress(stateRoot, project, { ...current, port: resolved.port, sessionId: resolved.sessionId });
|
|
5449
|
+
log(`captain-channel ${project}: ${logPrefix} \u2192 port ${resolved.port}`);
|
|
5450
|
+
return true;
|
|
5451
|
+
}
|
|
5437
5452
|
async function reapOrphanedCrews(store, project, isSurfaceAlive) {
|
|
5438
5453
|
let reaped = 0;
|
|
5439
5454
|
for (const r of store.list(project)) {
|
|
@@ -5658,27 +5673,7 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
|
|
|
5658
5673
|
log(`captain-channel ${project}: threw, falling back to pane \u2014 ${e.message}`);
|
|
5659
5674
|
}
|
|
5660
5675
|
if (agent === "opencode" && mode !== "off") {
|
|
5661
|
-
const
|
|
5662
|
-
let healed = false;
|
|
5663
|
-
if (rec?.port) {
|
|
5664
|
-
const fresh = newestSessionInDirectory(await listSessions(rec.port), rec.directory, Date.parse(rec.launchedAt));
|
|
5665
|
-
if (fresh && fresh !== rec.sessionId) {
|
|
5666
|
-
writeCaptainAddress(stateRoot, project, { ...rec, sessionId: fresh });
|
|
5667
|
-
healed = true;
|
|
5668
|
-
}
|
|
5669
|
-
}
|
|
5670
|
-
if (!healed && rec) {
|
|
5671
|
-
const live = discoverLiveOpencodeServer({ directory: rec.directory, sessionId: rec.sessionId });
|
|
5672
|
-
if (live && (live.port !== rec.port || live.sessionId && live.sessionId !== rec.sessionId)) {
|
|
5673
|
-
writeCaptainAddress(stateRoot, project, {
|
|
5674
|
-
...rec,
|
|
5675
|
-
port: live.port,
|
|
5676
|
-
sessionId: live.sessionId ?? rec.sessionId
|
|
5677
|
-
});
|
|
5678
|
-
log(`captain-channel ${project}: re-resolved opencode address \u2192 port ${live.port}`);
|
|
5679
|
-
healed = true;
|
|
5680
|
-
}
|
|
5681
|
-
}
|
|
5676
|
+
const healed = await healStaleOpencodeAddress(stateRoot, project, log, "re-resolved opencode address");
|
|
5682
5677
|
if (healed) {
|
|
5683
5678
|
try {
|
|
5684
5679
|
const retry = await deliverToCaptain(project, text, { channel: picked, mode, log });
|
|
@@ -6536,11 +6531,15 @@ function parseCommand(text) {
|
|
|
6536
6531
|
}
|
|
6537
6532
|
return entry.build(args);
|
|
6538
6533
|
}
|
|
6539
|
-
var WRITABLE_CONFIG_KEYS, EFFORT_MODES, REGISTRY;
|
|
6534
|
+
var WRITABLE_CONFIG_KEYS, EFFORT_MODES, GATE_MODES2, CONFIG_VALUE_VALIDATORS, REGISTRY;
|
|
6540
6535
|
var init_commands = __esm({
|
|
6541
6536
|
"packages/core/dist/telegram/commands.js"() {
|
|
6542
|
-
WRITABLE_CONFIG_KEYS = ["defaults.effort"];
|
|
6537
|
+
WRITABLE_CONFIG_KEYS = ["defaults.effort", "defaults.gate.mode"];
|
|
6543
6538
|
EFFORT_MODES = /* @__PURE__ */ new Set(["max", "balance", "low"]);
|
|
6539
|
+
GATE_MODES2 = /* @__PURE__ */ new Set(["on", "off", "auto"]);
|
|
6540
|
+
CONFIG_VALUE_VALIDATORS = {
|
|
6541
|
+
"defaults.gate.mode": { valid: GATE_MODES2, usage: "usage: /config set defaults.gate.mode <on|off|auto>" }
|
|
6542
|
+
};
|
|
6544
6543
|
REGISTRY = {
|
|
6545
6544
|
status: { usage: "/status", build: () => ok("status", ["status"]) },
|
|
6546
6545
|
projects: { usage: "/projects", build: () => ok("projects", ["projects", "list"]) },
|
|
@@ -6587,6 +6586,10 @@ var init_commands = __esm({
|
|
|
6587
6586
|
message: `\u26D4 '${key}' is not writable over Telegram. Allowed: ${WRITABLE_CONFIG_KEYS.join(", ")}`
|
|
6588
6587
|
};
|
|
6589
6588
|
}
|
|
6589
|
+
const validator = CONFIG_VALUE_VALIDATORS[key];
|
|
6590
|
+
if (validator && !validator.valid.has(value)) {
|
|
6591
|
+
return usage("config", validator.usage);
|
|
6592
|
+
}
|
|
6590
6593
|
return ok("config", ["config", "set", key, value]);
|
|
6591
6594
|
}
|
|
6592
6595
|
return usage("config", "usage: /config get <key> | /config set <key> <value>");
|
|
@@ -8693,6 +8696,12 @@ function resolveGateMode(env, gate) {
|
|
|
8693
8696
|
return fromEnv;
|
|
8694
8697
|
return gate?.mode && isGateMode(gate.mode) ? gate.mode : "auto";
|
|
8695
8698
|
}
|
|
8699
|
+
function resolveGateEngine(env, gate) {
|
|
8700
|
+
const fromEnv = env.SQUADRANT_GATE_ENGINE;
|
|
8701
|
+
if (fromEnv !== void 0 && isGateEngine(fromEnv))
|
|
8702
|
+
return fromEnv;
|
|
8703
|
+
return gate?.engine && isGateEngine(gate.engine) ? gate.engine : "router";
|
|
8704
|
+
}
|
|
8696
8705
|
function resolveGatePolicy(env, gate) {
|
|
8697
8706
|
const fromEnv = env.SQUADRANT_GATE_POLICY;
|
|
8698
8707
|
if (fromEnv !== void 0 && isGatePolicy(fromEnv))
|
|
@@ -9077,6 +9086,62 @@ var init_permission_gate = __esm({
|
|
|
9077
9086
|
}
|
|
9078
9087
|
});
|
|
9079
9088
|
|
|
9089
|
+
// packages/core/dist/auto-gate.js
|
|
9090
|
+
import { createAutoGate, projectGateConfig, resolveApiKey } from "@squadrant-ai/auto-gate";
|
|
9091
|
+
function gateForProjection(config) {
|
|
9092
|
+
const gate = config.defaults.gate;
|
|
9093
|
+
if (!gate)
|
|
9094
|
+
return {};
|
|
9095
|
+
const out = {};
|
|
9096
|
+
if (gate.mode)
|
|
9097
|
+
out.mode = gate.mode;
|
|
9098
|
+
if (gate.tools)
|
|
9099
|
+
out.tools = gate.tools;
|
|
9100
|
+
if (gate.deny)
|
|
9101
|
+
out.deny = gate.deny;
|
|
9102
|
+
return out;
|
|
9103
|
+
}
|
|
9104
|
+
function hasAutoGateCredential(env = process.env, home) {
|
|
9105
|
+
return Boolean(resolveApiKey({ env, ...home !== void 0 ? { home } : {} }));
|
|
9106
|
+
}
|
|
9107
|
+
function createSquadrantAutoGate(deps) {
|
|
9108
|
+
const env = deps.env ?? process.env;
|
|
9109
|
+
const taskId = env.SQUADRANT_CREW_TASK_ID;
|
|
9110
|
+
const project = deps.project ?? env.SQUADRANT_CREW_PROJECT;
|
|
9111
|
+
const mode = resolveGateMode(env, deps.config.defaults.gate);
|
|
9112
|
+
const projected = projectGateConfig({ gate: { ...gateForProjection(deps.config), mode } });
|
|
9113
|
+
const enabled = mode === "on";
|
|
9114
|
+
const blockedSignal = async (ctx) => {
|
|
9115
|
+
if (!taskId || !project || !deps.sendBlocked)
|
|
9116
|
+
return;
|
|
9117
|
+
const event = {
|
|
9118
|
+
type: "task.blocked",
|
|
9119
|
+
id: taskId,
|
|
9120
|
+
reason: ctx.reason,
|
|
9121
|
+
question: `crew needs permission to run ${ctx.tool}`
|
|
9122
|
+
};
|
|
9123
|
+
await deps.sendBlocked(project, event);
|
|
9124
|
+
};
|
|
9125
|
+
const host = createAutoGate({
|
|
9126
|
+
config: projected,
|
|
9127
|
+
readIntent: (p) => readUserIntent(p) ?? void 0,
|
|
9128
|
+
blockedSignal,
|
|
9129
|
+
...deps.decide ? { decide: deps.decide } : {},
|
|
9130
|
+
...project ? { project } : {},
|
|
9131
|
+
env,
|
|
9132
|
+
log: deps.log
|
|
9133
|
+
});
|
|
9134
|
+
if (!enabled) {
|
|
9135
|
+
return { config: host.config, decideClaudeHookPayload: async () => void 0 };
|
|
9136
|
+
}
|
|
9137
|
+
return host;
|
|
9138
|
+
}
|
|
9139
|
+
var init_auto_gate = __esm({
|
|
9140
|
+
"packages/core/dist/auto-gate.js"() {
|
|
9141
|
+
init_permission_gate();
|
|
9142
|
+
}
|
|
9143
|
+
});
|
|
9144
|
+
|
|
9080
9145
|
// packages/core/dist/group-dispatch.js
|
|
9081
9146
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
9082
9147
|
import { createConnection as createConnection2 } from "net";
|
|
@@ -9483,10 +9548,11 @@ async function runCrewAnswer(project, name, option, runtime, workspaceId, deps,
|
|
|
9483
9548
|
if (!crew) {
|
|
9484
9549
|
throw new Error(`Crew '${name}' not found for ${project}. Run 'squadrant crew list ${project}'.`);
|
|
9485
9550
|
}
|
|
9486
|
-
const
|
|
9487
|
-
if (!options) {
|
|
9551
|
+
const modal = await deps.readModalOptions(crew);
|
|
9552
|
+
if (!modal || modal.options.length === 0) {
|
|
9488
9553
|
throw new Error(`Crew '${name}' has no interactive option prompt visible right now \u2014 nothing to answer. Read its screen with 'squadrant crew read ${project} ${name}' to check its state.`);
|
|
9489
9554
|
}
|
|
9555
|
+
const options = modal.options;
|
|
9490
9556
|
const target = resolveOption(options, option);
|
|
9491
9557
|
if (opts?.expect && !target.label.toLowerCase().includes(opts.expect.toLowerCase())) {
|
|
9492
9558
|
throw new Error(`Refusing: option ${target.index} is "${target.label}", which does not contain expected text "${opts.expect}". Option order is model-generated and can shift between renders \u2014 re-check with 'squadrant crew read ${project} ${name}'.
|
|
@@ -9498,7 +9564,9 @@ ${describeOptions(options)}`);
|
|
|
9498
9564
|
log(`\u2192 selecting ${target.index}. "${target.label}"`);
|
|
9499
9565
|
const current = options.find((o) => o.highlighted) ?? options[0];
|
|
9500
9566
|
const steps = target.index - current.index;
|
|
9501
|
-
const
|
|
9567
|
+
const forwardKey = modal.axis === "horizontal" ? "Right" : "Down";
|
|
9568
|
+
const backwardKey = modal.axis === "horizontal" ? "Left" : "Up";
|
|
9569
|
+
const key = steps >= 0 ? forwardKey : backwardKey;
|
|
9502
9570
|
for (let i = 0; i < Math.abs(steps); i++) {
|
|
9503
9571
|
await runtime.sendKeyToPane(crew, key);
|
|
9504
9572
|
}
|
|
@@ -9870,6 +9938,7 @@ __export(dist_exports2, {
|
|
|
9870
9938
|
CREW_GONE_MS: () => CREW_GONE_MS,
|
|
9871
9939
|
CREW_STALE_MS: () => CREW_STALE_MS,
|
|
9872
9940
|
CaptainDelivery: () => CaptainDelivery,
|
|
9941
|
+
DEFAULT_BLOCKED_RENUDGE_MS: () => DEFAULT_BLOCKED_RENUDGE_MS,
|
|
9873
9942
|
DEFAULT_FIRST_TURN_RESEND_COOLDOWN_MS: () => DEFAULT_FIRST_TURN_RESEND_COOLDOWN_MS,
|
|
9874
9943
|
DEFAULT_FIRST_TURN_UNDELIVERED_BUDGET_MS: () => DEFAULT_FIRST_TURN_UNDELIVERED_BUDGET_MS,
|
|
9875
9944
|
DEFAULT_GATE_DENY_RULES: () => DEFAULT_GATE_DENY_RULES,
|
|
@@ -9952,6 +10021,7 @@ __export(dist_exports2, {
|
|
|
9952
10021
|
createRouterService: () => createRouterService,
|
|
9953
10022
|
createRouterShim: () => createRouterShim,
|
|
9954
10023
|
createRunCommand: () => createRunCommand,
|
|
10024
|
+
createSquadrantAutoGate: () => createSquadrantAutoGate,
|
|
9955
10025
|
createStore: () => createStore,
|
|
9956
10026
|
createSurfaceLivenessProbe: () => createSurfaceLivenessProbe,
|
|
9957
10027
|
createTelegramBridge: () => createTelegramBridge,
|
|
@@ -10008,6 +10078,7 @@ __export(dist_exports2, {
|
|
|
10008
10078
|
freshTrace: () => freshTrace,
|
|
10009
10079
|
gateCacheKey: () => gateCacheKey,
|
|
10010
10080
|
getDaemonPid: () => getDaemonPid,
|
|
10081
|
+
hasAutoGateCredential: () => hasAutoGateCredential,
|
|
10011
10082
|
hasModalOptionList: () => hasModalOptionList,
|
|
10012
10083
|
healCmdFor: () => healCmdFor,
|
|
10013
10084
|
inboundBody: () => inboundBody,
|
|
@@ -10093,6 +10164,7 @@ __export(dist_exports2, {
|
|
|
10093
10164
|
resolveCurrentProject: () => resolveCurrentProject,
|
|
10094
10165
|
resolveGate: () => resolveGate,
|
|
10095
10166
|
resolveGateDenyRules: () => resolveGateDenyRules,
|
|
10167
|
+
resolveGateEngine: () => resolveGateEngine,
|
|
10096
10168
|
resolveGateMode: () => resolveGateMode,
|
|
10097
10169
|
resolveGatePolicy: () => resolveGatePolicy,
|
|
10098
10170
|
resolveGateTools: () => resolveGateTools,
|
|
@@ -10198,6 +10270,7 @@ var init_dist2 = __esm({
|
|
|
10198
10270
|
init_router_resolution();
|
|
10199
10271
|
init_captain_router();
|
|
10200
10272
|
init_permission_gate();
|
|
10273
|
+
init_auto_gate();
|
|
10201
10274
|
init_restart_daemon();
|
|
10202
10275
|
init_group_dispatch();
|
|
10203
10276
|
init_launch_workspace();
|
|
@@ -13683,6 +13756,37 @@ function parseModalOptions(screen) {
|
|
|
13683
13756
|
}
|
|
13684
13757
|
return options.length > 0 ? options : null;
|
|
13685
13758
|
}
|
|
13759
|
+
function parseOpencodePermissionOptions(screen) {
|
|
13760
|
+
if (!screen)
|
|
13761
|
+
return null;
|
|
13762
|
+
if (!/△\s*Permission required/.test(screen))
|
|
13763
|
+
return null;
|
|
13764
|
+
const lines = screen.split(/\r?\n/);
|
|
13765
|
+
const bar = lines.find((l) => /⇆\s*select/.test(l));
|
|
13766
|
+
if (!bar)
|
|
13767
|
+
return null;
|
|
13768
|
+
let region = bar.split("\u21C6")[0];
|
|
13769
|
+
const fullscreenIdx = region.toLowerCase().lastIndexOf("fullscreen");
|
|
13770
|
+
if (fullscreenIdx !== -1) {
|
|
13771
|
+
region = region.slice(0, fullscreenIdx).replace(/\s+\S+\s*$/, "");
|
|
13772
|
+
}
|
|
13773
|
+
region = region.replace(/^[\s┃│╭╮╰╯|]+/, "").trim();
|
|
13774
|
+
if (!region)
|
|
13775
|
+
return null;
|
|
13776
|
+
const labels = region.split(/\s{2,}/).map((s) => s.trim()).filter(Boolean);
|
|
13777
|
+
if (labels.length === 0)
|
|
13778
|
+
return null;
|
|
13779
|
+
return labels.map((label, i) => ({ index: i + 1, label, highlighted: i === 0 }));
|
|
13780
|
+
}
|
|
13781
|
+
function parseModal(screen) {
|
|
13782
|
+
const claude = parseModalOptions(screen);
|
|
13783
|
+
if (claude)
|
|
13784
|
+
return { options: claude, axis: "vertical" };
|
|
13785
|
+
const opencode = parseOpencodePermissionOptions(screen);
|
|
13786
|
+
if (opencode)
|
|
13787
|
+
return { options: opencode, axis: "horizontal" };
|
|
13788
|
+
return null;
|
|
13789
|
+
}
|
|
13686
13790
|
function readInputBoxRaw(screen, opts) {
|
|
13687
13791
|
if (!screen)
|
|
13688
13792
|
return null;
|
|
@@ -14892,6 +14996,47 @@ function removeCommandHandlers(entries, command) {
|
|
|
14892
14996
|
}
|
|
14893
14997
|
return removed;
|
|
14894
14998
|
}
|
|
14999
|
+
function isForeignAutoGateCommand(command) {
|
|
15000
|
+
if (typeof command !== "string")
|
|
15001
|
+
return false;
|
|
15002
|
+
if (command.includes(FOREIGN_AUTO_GATE_MARKER))
|
|
15003
|
+
return true;
|
|
15004
|
+
const first = command.trim().split(/\s+/)[0] ?? "";
|
|
15005
|
+
return /(^|[/\\])auto-gate$/.test(first);
|
|
15006
|
+
}
|
|
15007
|
+
function findForeignGateHandlers(entries) {
|
|
15008
|
+
const found = [];
|
|
15009
|
+
for (const entry of entries) {
|
|
15010
|
+
const hooks = entry?.hooks;
|
|
15011
|
+
if (!Array.isArray(hooks))
|
|
15012
|
+
continue;
|
|
15013
|
+
for (const h of hooks) {
|
|
15014
|
+
const cmd = h?.command;
|
|
15015
|
+
if (isForeignAutoGateCommand(cmd))
|
|
15016
|
+
found.push(cmd);
|
|
15017
|
+
}
|
|
15018
|
+
}
|
|
15019
|
+
return found;
|
|
15020
|
+
}
|
|
15021
|
+
function removeForeignGateHandlers(entries) {
|
|
15022
|
+
const removed = [];
|
|
15023
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
15024
|
+
const entry = entries[i];
|
|
15025
|
+
if (!Array.isArray(entry?.hooks))
|
|
15026
|
+
continue;
|
|
15027
|
+
entry.hooks = entry.hooks.filter((h) => {
|
|
15028
|
+
const cmd = h?.command;
|
|
15029
|
+
if (isForeignAutoGateCommand(cmd)) {
|
|
15030
|
+
removed.push(cmd);
|
|
15031
|
+
return false;
|
|
15032
|
+
}
|
|
15033
|
+
return true;
|
|
15034
|
+
});
|
|
15035
|
+
if (entry.hooks.length === 0)
|
|
15036
|
+
entries.splice(i, 1);
|
|
15037
|
+
}
|
|
15038
|
+
return removed;
|
|
15039
|
+
}
|
|
14895
15040
|
function installClaudeHooks(opts = {}) {
|
|
14896
15041
|
const settingsPath = opts.settingsPath ?? join25(homedir13(), ".claude", "settings.json");
|
|
14897
15042
|
const hookCmd = opts.hookCmd ?? DEFAULT_HOOK_CMD;
|
|
@@ -14929,6 +15074,23 @@ function installClaudeHooks(opts = {}) {
|
|
|
14929
15074
|
changed = true;
|
|
14930
15075
|
migrated.push(`${eventName}/${sub}`);
|
|
14931
15076
|
}
|
|
15077
|
+
const foreign = findForeignGateHandlers(entries);
|
|
15078
|
+
if (foreign.length > 0) {
|
|
15079
|
+
if (opts.gateMode === "on") {
|
|
15080
|
+
const removed = removeForeignGateHandlers(entries);
|
|
15081
|
+
if (removed.length > 0)
|
|
15082
|
+
changed = true;
|
|
15083
|
+
for (const command2 of removed) {
|
|
15084
|
+
log(`native-hook: removed foreign auto-gate PermissionRequest handler in ${settingsPath} [#P6 \xA715#5]: ${command2}`);
|
|
15085
|
+
opts.recordGateAction?.({ action: "removed-foreign-gate", command: command2 });
|
|
15086
|
+
}
|
|
15087
|
+
} else {
|
|
15088
|
+
for (const command2 of foreign) {
|
|
15089
|
+
log(`native-hook: foreign auto-gate PermissionRequest handler present in ${settingsPath} but squadrant's gate is '${opts.gateMode ?? "auto"}' (not 'on') \u2014 foreign entry left untouched [#P6 \xA715#5]: ${command2}`);
|
|
15090
|
+
opts.recordGateAction?.({ action: "left-foreign-gate", command: command2 });
|
|
15091
|
+
}
|
|
15092
|
+
}
|
|
15093
|
+
}
|
|
14932
15094
|
}
|
|
14933
15095
|
const alreadyPresent = entries.some((m) => Array.isArray(m.hooks) && m.hooks.some((h) => typeof h.command === "string" && h.command === command));
|
|
14934
15096
|
if (!alreadyPresent) {
|
|
@@ -14959,11 +15121,55 @@ function installClaudeHooks(opts = {}) {
|
|
|
14959
15121
|
changed = true;
|
|
14960
15122
|
}
|
|
14961
15123
|
}
|
|
15124
|
+
if (typeof settings.env === "object" && settings.env !== null && !Array.isArray(settings.env)) {
|
|
15125
|
+
if (reconcileSubagentModels(settings.env, log))
|
|
15126
|
+
changed = true;
|
|
15127
|
+
}
|
|
14962
15128
|
if (changed) {
|
|
14963
15129
|
writeFile6(settingsPath, JSON.stringify(settings, null, 2));
|
|
14964
15130
|
}
|
|
14965
15131
|
return settingsPath;
|
|
14966
15132
|
}
|
|
15133
|
+
function isCustomUpstream(baseUrl) {
|
|
15134
|
+
if (typeof baseUrl !== "string" || baseUrl.length === 0)
|
|
15135
|
+
return false;
|
|
15136
|
+
try {
|
|
15137
|
+
return new URL(baseUrl).hostname.toLowerCase() !== "api.anthropic.com";
|
|
15138
|
+
} catch {
|
|
15139
|
+
return false;
|
|
15140
|
+
}
|
|
15141
|
+
}
|
|
15142
|
+
function isAnthropicModelId(value) {
|
|
15143
|
+
if (typeof value !== "string")
|
|
15144
|
+
return false;
|
|
15145
|
+
const v2 = value.trim().toLowerCase();
|
|
15146
|
+
return v2 === "sonnet" || v2 === "opus" || v2 === "haiku" || v2.startsWith("claude-");
|
|
15147
|
+
}
|
|
15148
|
+
function reconcileSubagentModels(env, log) {
|
|
15149
|
+
if (!isCustomUpstream(env.ANTHROPIC_BASE_URL))
|
|
15150
|
+
return false;
|
|
15151
|
+
const model = env.ANTHROPIC_MODEL;
|
|
15152
|
+
if (typeof model !== "string" || model.length === 0)
|
|
15153
|
+
return false;
|
|
15154
|
+
let changed = false;
|
|
15155
|
+
for (const key of [SUBAGENT_MODEL_KEY, SMALL_FAST_MODEL_KEY]) {
|
|
15156
|
+
const current = env[key];
|
|
15157
|
+
if (current === void 0) {
|
|
15158
|
+
env[key] = model;
|
|
15159
|
+
changed = true;
|
|
15160
|
+
log(`native-hook: set ${key}='${model}' to match ANTHROPIC_MODEL on a custom upstream`);
|
|
15161
|
+
} else if (isAnthropicModelId(current)) {
|
|
15162
|
+
if (current !== model) {
|
|
15163
|
+
env[key] = model;
|
|
15164
|
+
changed = true;
|
|
15165
|
+
log(`native-hook: aligned ${key} '${String(current)}' \u2192 '${model}' for a custom upstream`);
|
|
15166
|
+
}
|
|
15167
|
+
} else {
|
|
15168
|
+
log(`native-hook: ${key} '${String(current)}' is a custom model id \u2014 left untouched`);
|
|
15169
|
+
}
|
|
15170
|
+
}
|
|
15171
|
+
return changed;
|
|
15172
|
+
}
|
|
14967
15173
|
function mapSubToLifecycle(sub) {
|
|
14968
15174
|
switch (sub) {
|
|
14969
15175
|
case "session-start":
|
|
@@ -15013,7 +15219,7 @@ function defaultWriteFile(path39, content) {
|
|
|
15013
15219
|
mkdirSync8(path39.replace(/\/[^/]+$/, ""), { recursive: true });
|
|
15014
15220
|
writeFileSync11(path39, content, "utf-8");
|
|
15015
15221
|
}
|
|
15016
|
-
var CLAUDE_HOOK_EVENTS, DEFAULT_HOOK_CMD, GATE_HOOK_CMD, NativeHookSource;
|
|
15222
|
+
var CLAUDE_HOOK_EVENTS, DEFAULT_HOOK_CMD, GATE_HOOK_CMD, FOREIGN_AUTO_GATE_MARKER, SUBAGENT_MODEL_KEY, SMALL_FAST_MODEL_KEY, NativeHookSource;
|
|
15017
15223
|
var init_native_hook_source = __esm({
|
|
15018
15224
|
"packages/workspaces/dist/native-hooks/native-hook-source.js"() {
|
|
15019
15225
|
CLAUDE_HOOK_EVENTS = [
|
|
@@ -15033,6 +15239,9 @@ var init_native_hook_source = __esm({
|
|
|
15033
15239
|
];
|
|
15034
15240
|
DEFAULT_HOOK_CMD = "squadrant hooks";
|
|
15035
15241
|
GATE_HOOK_CMD = "squadrant gate";
|
|
15242
|
+
FOREIGN_AUTO_GATE_MARKER = "auto-gate-managed";
|
|
15243
|
+
SUBAGENT_MODEL_KEY = "CLAUDE_CODE_SUBAGENT_MODEL";
|
|
15244
|
+
SMALL_FAST_MODEL_KEY = "ANTHROPIC_SMALL_FAST_MODEL";
|
|
15036
15245
|
NativeHookSource = class {
|
|
15037
15246
|
name = "native-hook";
|
|
15038
15247
|
hookInstall;
|
|
@@ -15166,7 +15375,7 @@ async function paneHasOpenModal(runtime, pane) {
|
|
|
15166
15375
|
}
|
|
15167
15376
|
async function readModalOptions(runtime, pane) {
|
|
15168
15377
|
const screen = await runtime.readPaneScreen(pane) ?? "";
|
|
15169
|
-
return
|
|
15378
|
+
return parseModal(screen);
|
|
15170
15379
|
}
|
|
15171
15380
|
async function confirmedSendToPane(runtime, pane, message) {
|
|
15172
15381
|
const preSendScreen = await runtime.readPaneScreen(pane) ?? "";
|
|
@@ -16217,27 +16426,6 @@ function promptLine(question) {
|
|
|
16217
16426
|
});
|
|
16218
16427
|
});
|
|
16219
16428
|
}
|
|
16220
|
-
async function resolveRouter(config, opts, isTTY) {
|
|
16221
|
-
if (config.defaults?.router) return config.defaults.router;
|
|
16222
|
-
let kind = opts.routerKind;
|
|
16223
|
-
let baseUrl = opts.routerBaseUrl;
|
|
16224
|
-
let apiKeyEnv = opts.routerApiKeyEnv;
|
|
16225
|
-
if (isTTY && (!kind || !baseUrl)) {
|
|
16226
|
-
const k = await promptLine(chalk4.cyan(` Router kind [${DEFAULT_ROUTER_KIND}]: `));
|
|
16227
|
-
kind = kind ?? (k || DEFAULT_ROUTER_KIND);
|
|
16228
|
-
const b = await promptLine(chalk4.cyan(` Router base URL [${DEFAULT_ROUTER_BASE_URL}]: `));
|
|
16229
|
-
baseUrl = baseUrl ?? (b || DEFAULT_ROUTER_BASE_URL);
|
|
16230
|
-
const e = await promptLine(chalk4.cyan(" Env var holding the API key (optional): "));
|
|
16231
|
-
apiKeyEnv = apiKeyEnv ?? (e || void 0);
|
|
16232
|
-
}
|
|
16233
|
-
const resolvedKind = kind && isRouterKind(kind) ? kind : DEFAULT_ROUTER_KIND;
|
|
16234
|
-
const resolvedBase = baseUrl || DEFAULT_ROUTER_BASE_URL;
|
|
16235
|
-
if (!isTTY && !opts.routerBaseUrl) {
|
|
16236
|
-
console.log(chalk4.dim(` - no router flags given; defaulting to ${resolvedKind} (${resolvedBase})`));
|
|
16237
|
-
console.log(chalk4.dim(" set a credential: squadrant config set defaults.router.apiKeyEnv <VAR>"));
|
|
16238
|
-
}
|
|
16239
|
-
return { kind: resolvedKind, baseUrl: resolvedBase, ...apiKeyEnv ? { apiKeyEnv } : {} };
|
|
16240
|
-
}
|
|
16241
16429
|
async function askProvider(current, auth) {
|
|
16242
16430
|
console.log(chalk4.bold("\n Choose your provider:"));
|
|
16243
16431
|
for (const p of PROVIDER_PRESETS) {
|
|
@@ -16246,10 +16434,10 @@ async function askProvider(current, auth) {
|
|
|
16246
16434
|
console.log(chalk4.dim(` ${p.summary}`));
|
|
16247
16435
|
}
|
|
16248
16436
|
if (!auth.authenticated) {
|
|
16249
|
-
console.log(chalk4.yellow("\n \u26A0 No Anthropic credential detected \u2014 preset a will not work; consider b
|
|
16437
|
+
console.log(chalk4.yellow("\n \u26A0 No Anthropic credential detected \u2014 preset a will not work; consider b."));
|
|
16250
16438
|
}
|
|
16251
16439
|
const answer = (await promptLine(chalk4.cyan(`
|
|
16252
|
-
Provider [a/b/c
|
|
16440
|
+
Provider [a/b/c] (default ${current}): `))).toLowerCase();
|
|
16253
16441
|
if (!answer) return current;
|
|
16254
16442
|
if (isProviderPresetId(answer)) return answer;
|
|
16255
16443
|
console.log(chalk4.yellow(` \u26A0 Unknown choice '${answer}' \u2014 keeping '${current}'.`));
|
|
@@ -16258,9 +16446,9 @@ async function askProvider(current, auth) {
|
|
|
16258
16446
|
function warnIfNoAnthropic(preset, auth) {
|
|
16259
16447
|
if (preset !== "a" || auth.authenticated) return;
|
|
16260
16448
|
console.log(chalk4.yellow(" \u26A0 No Anthropic credential detected (`claude auth status`)."));
|
|
16261
|
-
console.log(chalk4.dim(" Preset A (Claude Pro/Max or an API key) may not work \u2014 consider preset b
|
|
16449
|
+
console.log(chalk4.dim(" Preset A (Claude Pro/Max or an API key) may not work \u2014 consider preset b."));
|
|
16262
16450
|
}
|
|
16263
|
-
var initCommand = new Command2("init").description("Guided first-time setup: provider, hub vault, agents, plugins, projects (re-run-safe)").option("--hub <path>", "Hub vault path", "~/squadrant-hub").option("--preset <id>", "provider preset: a|b|c
|
|
16451
|
+
var initCommand = new Command2("init").description("Guided first-time setup: provider, hub vault, agents, plugins, projects (re-run-safe)").option("--hub <path>", "Hub vault path", "~/squadrant-hub").option("--preset <id>", "provider preset: a|b|c (non-interactive; d accepted as a deprecated alias for c)").action(async (opts) => {
|
|
16264
16452
|
const hubPath = resolveHome(opts.hub);
|
|
16265
16453
|
const pkgRoot = findPackageRoot();
|
|
16266
16454
|
const configDir = path23.join(os13.homedir(), ".config", "squadrant");
|
|
@@ -16268,9 +16456,9 @@ var initCommand = new Command2("init").description("Guided first-time setup: pro
|
|
|
16268
16456
|
console.log(chalk4.bold("\nSquadrant Init\n"));
|
|
16269
16457
|
let explicitPreset;
|
|
16270
16458
|
if (opts.preset !== void 0) {
|
|
16271
|
-
const candidate = opts.preset.toLowerCase();
|
|
16272
|
-
if (!
|
|
16273
|
-
console.error(chalk4.red(` \u2718 Unknown --preset '${opts.preset}'. Valid values: a, b, c
|
|
16459
|
+
const candidate = normalizePresetId(opts.preset.toLowerCase());
|
|
16460
|
+
if (!candidate) {
|
|
16461
|
+
console.error(chalk4.red(` \u2718 Unknown --preset '${opts.preset}'. Valid values: a, b, c`));
|
|
16274
16462
|
process.exitCode = 1;
|
|
16275
16463
|
return;
|
|
16276
16464
|
}
|
|
@@ -16280,7 +16468,7 @@ var initCommand = new Command2("init").description("Guided first-time setup: pro
|
|
|
16280
16468
|
console.log(" Run these steps to get started:\n");
|
|
16281
16469
|
console.log(chalk4.bold(" 1/5 Hub vault + provider"));
|
|
16282
16470
|
console.log(chalk4.cyan(` squadrant init --hub ${opts.hub}`));
|
|
16283
|
-
console.log(chalk4.dim(" Choose a provider: squadrant init --preset a|b|c
|
|
16471
|
+
console.log(chalk4.dim(" Choose a provider: squadrant init --preset a|b|c"));
|
|
16284
16472
|
console.log(chalk4.bold("\n 2/5 Agent + projection setup"));
|
|
16285
16473
|
console.log(" (handled automatically by: " + chalk4.cyan("squadrant init") + ")");
|
|
16286
16474
|
console.log(chalk4.bold("\n 3/5 Plugins \u2014 open Claude Code and run:"));
|
|
@@ -16326,9 +16514,8 @@ var initCommand = new Command2("init").description("Guided first-time setup: pro
|
|
|
16326
16514
|
let appliedChanges = [];
|
|
16327
16515
|
if (chosenPreset) {
|
|
16328
16516
|
const label = PROVIDER_PRESETS.find((p) => p.id === chosenPreset)?.label ?? chosenPreset;
|
|
16329
|
-
const router = chosenPreset === "c" ? await resolveRouter(config, opts, isTTY) : void 0;
|
|
16330
16517
|
const overwrite = !configExists || explicitPreset !== void 0;
|
|
16331
|
-
const result = applyProviderPreset(config, chosenPreset, {
|
|
16518
|
+
const result = applyProviderPreset(config, chosenPreset, { overwrite });
|
|
16332
16519
|
appliedChanges = result.changes;
|
|
16333
16520
|
config = result.config;
|
|
16334
16521
|
console.log(chalk4.bold(`
|
|
@@ -19938,7 +20125,7 @@ var launchCommand = new Command14("launch").description(
|
|
|
19938
20125
|
sessionId: priorRecord?.sessionId
|
|
19939
20126
|
});
|
|
19940
20127
|
if (!live) return;
|
|
19941
|
-
const sessionId = live.sessionId ?? priorRecord?.sessionId;
|
|
20128
|
+
const sessionId = live.sessionId ?? (live.port === priorRecord?.port ? priorRecord?.sessionId : void 0);
|
|
19942
20129
|
if (priorRecord && priorRecord.port === live.port && priorRecord.sessionId === sessionId) return;
|
|
19943
20130
|
writeCaptainAddress(stateRoot, projectName, {
|
|
19944
20131
|
agent: "opencode",
|
|
@@ -19952,13 +20139,20 @@ var launchCommand = new Command14("launch").description(
|
|
|
19952
20139
|
console.log(chalk15.green(` \u2714 Workspace '${name}' created`));
|
|
19953
20140
|
if (!projectName) return;
|
|
19954
20141
|
if (isOpencodeCaptain && captainPort) {
|
|
20142
|
+
writeCaptainAddress(stateRoot, projectName, {
|
|
20143
|
+
agent: "opencode",
|
|
20144
|
+
port: captainPort,
|
|
20145
|
+
directory: realpathOrSelf(cwd),
|
|
20146
|
+
launchedAt
|
|
20147
|
+
});
|
|
19955
20148
|
void resolveAndPersistOpencodeCaptain({
|
|
19956
20149
|
stateRoot,
|
|
19957
20150
|
project: projectName,
|
|
19958
20151
|
port: captainPort,
|
|
19959
20152
|
directory: realpathOrSelf(cwd),
|
|
19960
20153
|
launchedAt,
|
|
19961
|
-
sessionId: opencodeResumeSessionId
|
|
20154
|
+
sessionId: opencodeResumeSessionId,
|
|
20155
|
+
log: (m) => console.error(chalk15.yellow(` \u26A0 ${m}`))
|
|
19962
20156
|
}).catch(() => {
|
|
19963
20157
|
});
|
|
19964
20158
|
} else if (role === "captain") {
|
|
@@ -22348,7 +22542,32 @@ init_dist2();
|
|
|
22348
22542
|
init_dist();
|
|
22349
22543
|
init_dist3();
|
|
22350
22544
|
import { Command as Command32 } from "commander";
|
|
22545
|
+
import chalk33 from "chalk";
|
|
22351
22546
|
var SOCK4 = DAEMON_SOCK_PATH;
|
|
22547
|
+
async function runAutoGateClaudeHook(o) {
|
|
22548
|
+
const make = o.deps.createAutoGate ?? createSquadrantAutoGate;
|
|
22549
|
+
const autoGate = make({
|
|
22550
|
+
config: o.config,
|
|
22551
|
+
env: o.env,
|
|
22552
|
+
...o.project ? { project: o.project } : {},
|
|
22553
|
+
...o.deps.sendEvent ? { sendBlocked: o.deps.sendEvent } : {},
|
|
22554
|
+
log: o.log
|
|
22555
|
+
});
|
|
22556
|
+
const out = await autoGate.decideClaudeHookPayload(o.deps.rawPayload ?? "");
|
|
22557
|
+
if (out === void 0) {
|
|
22558
|
+
o.log("gate auto-gate: ask/yield \u2014 normal dialog");
|
|
22559
|
+
return { decision: "ask", tier: 2, reason: "auto-gate: ask/yield" };
|
|
22560
|
+
}
|
|
22561
|
+
o.stdout(out);
|
|
22562
|
+
let decision = "allow";
|
|
22563
|
+
try {
|
|
22564
|
+
const parsed = JSON.parse(out);
|
|
22565
|
+
if (parsed?.hookSpecificOutput?.decision?.behavior === "deny") decision = "deny";
|
|
22566
|
+
} catch {
|
|
22567
|
+
}
|
|
22568
|
+
o.log(`gate auto-gate ${decision}`);
|
|
22569
|
+
return { decision, tier: 2, reason: "auto-gate" };
|
|
22570
|
+
}
|
|
22352
22571
|
async function runGatePermissionRequest(deps) {
|
|
22353
22572
|
const env = deps.env ?? process.env;
|
|
22354
22573
|
const payload = deps.payload ?? {};
|
|
@@ -22363,6 +22582,10 @@ async function runGatePermissionRequest(deps) {
|
|
|
22363
22582
|
const log = deps.log ?? ((m) => process.stderr.write(`[squadrant] ${m}
|
|
22364
22583
|
`));
|
|
22365
22584
|
const config = deps.config ?? (deps.loadConfigFn ?? loadConfig)();
|
|
22585
|
+
const gate = config.defaults.gate;
|
|
22586
|
+
if (resolveGateMode(env, gate) === "on" && resolveGateEngine(env, gate) === "auto-gate") {
|
|
22587
|
+
return runAutoGateClaudeHook({ deps, env, config, project, stdout, log });
|
|
22588
|
+
}
|
|
22366
22589
|
const cwd = deps.cwd ?? (typeof p.cwd === "string" && p.cwd ? p.cwd : env.SQUADRANT_CREW_CWD ?? process.cwd());
|
|
22367
22590
|
const transcriptPath = typeof p.transcript_path === "string" && p.transcript_path ? p.transcript_path : deriveTranscriptPath(
|
|
22368
22591
|
typeof p.session_id === "string" ? p.session_id : "",
|
|
@@ -22404,10 +22627,60 @@ async function runGatePermissionRequest(deps) {
|
|
|
22404
22627
|
return evaluation;
|
|
22405
22628
|
}
|
|
22406
22629
|
var DEFAULT_EVENT = "permission-request";
|
|
22630
|
+
function runGateModeGet(configPath = DEFAULT_CONFIG_PATH, env = process.env) {
|
|
22631
|
+
const config = loadConfig(configPath);
|
|
22632
|
+
const mode = resolveGateMode(env, config.defaults.gate);
|
|
22633
|
+
const source = env.SQUADRANT_GATE !== void 0 && isGateMode(env.SQUADRANT_GATE) ? "env" : config.defaults.gate?.mode && isGateMode(config.defaults.gate.mode) ? "config" : "default";
|
|
22634
|
+
return { mode, source };
|
|
22635
|
+
}
|
|
22636
|
+
function runGateModeSet(value, configPath = DEFAULT_CONFIG_PATH) {
|
|
22637
|
+
if (!isGateMode(value)) {
|
|
22638
|
+
throw new Error(`Invalid gate mode '${value}'. Valid values: ${GATE_MODES.join(" | ")}`);
|
|
22639
|
+
}
|
|
22640
|
+
const config = loadConfig(configPath);
|
|
22641
|
+
const old = config.defaults.gate?.mode && isGateMode(config.defaults.gate.mode) ? config.defaults.gate.mode : "auto";
|
|
22642
|
+
config.defaults.gate = { ...config.defaults.gate, mode: value };
|
|
22643
|
+
saveConfig(config, configPath);
|
|
22644
|
+
return { old, next: value };
|
|
22645
|
+
}
|
|
22646
|
+
function runGateStatus(configPath = DEFAULT_CONFIG_PATH, env = process.env) {
|
|
22647
|
+
const config = loadConfig(configPath);
|
|
22648
|
+
const { mode, source } = runGateModeGet(configPath, env);
|
|
22649
|
+
const engine = resolveGateEngine(env, config.defaults.gate);
|
|
22650
|
+
const model = resolveClassifierModel(env, config);
|
|
22651
|
+
const credentialPresent = engine === "auto-gate" ? hasAutoGateCredential(env) : Boolean(config.defaults.router?.apiKey ?? (config.defaults.router?.apiKeyEnv ? env[config.defaults.router.apiKeyEnv] : void 0));
|
|
22652
|
+
return { mode, source, engine, model, credentialPresent };
|
|
22653
|
+
}
|
|
22407
22654
|
function gateCommand() {
|
|
22408
22655
|
const gate = new Command32("gate").description(
|
|
22409
|
-
"(
|
|
22656
|
+
"Permission gate (#782/#854): get/set defaults.gate.mode; 'claude <event>' is an internal hook handler"
|
|
22410
22657
|
);
|
|
22658
|
+
gate.command("mode [value]").description("Get or set the permission gate mode (on | off | auto)").action((value) => {
|
|
22659
|
+
if (value === void 0) {
|
|
22660
|
+
const result2 = runGateModeGet();
|
|
22661
|
+
console.log(chalk33.bold("Gate mode:"), chalk33.cyan(result2.mode), chalk33.dim(`(${result2.source})`));
|
|
22662
|
+
return;
|
|
22663
|
+
}
|
|
22664
|
+
let result;
|
|
22665
|
+
try {
|
|
22666
|
+
result = runGateModeSet(value);
|
|
22667
|
+
} catch (e) {
|
|
22668
|
+
console.error(chalk33.red(e.message));
|
|
22669
|
+
process.exit(1);
|
|
22670
|
+
}
|
|
22671
|
+
console.log(chalk33.green(`\u2714 gate mode: ${result.old} \u2192 ${result.next}`));
|
|
22672
|
+
console.log(chalk33.dim("No daemon bounce needed \u2014 defaults.gate.mode is read live."));
|
|
22673
|
+
if (isGateMode(process.env.SQUADRANT_GATE ?? "") && process.env.SQUADRANT_GATE !== result.next) {
|
|
22674
|
+
console.log(chalk33.yellow(`Note: SQUADRANT_GATE=${process.env.SQUADRANT_GATE} in this session still overrides the config value.`));
|
|
22675
|
+
}
|
|
22676
|
+
});
|
|
22677
|
+
gate.command("status").description("Show the effective gate mode, engine, classifier model, and credential presence").action(() => {
|
|
22678
|
+
const result = runGateStatus();
|
|
22679
|
+
console.log(chalk33.bold("Gate mode:"), chalk33.cyan(result.mode), chalk33.dim(`(${result.source})`));
|
|
22680
|
+
console.log(chalk33.bold("Engine:"), result.engine);
|
|
22681
|
+
console.log(chalk33.bold("Classifier model:"), result.model ?? chalk33.dim("(unset)"));
|
|
22682
|
+
console.log(chalk33.bold("Credential:"), result.credentialPresent ? chalk33.green("present") : chalk33.yellow("absent"));
|
|
22683
|
+
});
|
|
22411
22684
|
gate.command("claude <event>", { hidden: true }).description("internal: classify a claude PermissionRequest with the configured router model").action(async (event) => {
|
|
22412
22685
|
let stdin = "";
|
|
22413
22686
|
try {
|
|
@@ -22426,6 +22699,7 @@ function gateCommand() {
|
|
|
22426
22699
|
}
|
|
22427
22700
|
await runGatePermissionRequest({
|
|
22428
22701
|
payload,
|
|
22702
|
+
rawPayload: stdin,
|
|
22429
22703
|
env: process.env,
|
|
22430
22704
|
stdout: (s) => process.stdout.write(s),
|
|
22431
22705
|
sendEvent: async (project, ev) => {
|
|
@@ -22508,6 +22782,7 @@ function hooksCommand() {
|
|
|
22508
22782
|
if (sub === "permission-request") {
|
|
22509
22783
|
await runGatePermissionRequest({
|
|
22510
22784
|
payload,
|
|
22785
|
+
rawPayload: stdin,
|
|
22511
22786
|
env: process.env,
|
|
22512
22787
|
stdout: (s) => process.stdout.write(s),
|
|
22513
22788
|
sendEvent: async (project2, ev2) => {
|
|
@@ -22559,7 +22834,7 @@ init_dist();
|
|
|
22559
22834
|
init_dist2();
|
|
22560
22835
|
import path34 from "path";
|
|
22561
22836
|
import { Command as Command34 } from "commander";
|
|
22562
|
-
import
|
|
22837
|
+
import chalk34 from "chalk";
|
|
22563
22838
|
function detectCurrentProject(config, cwd = process.cwd()) {
|
|
22564
22839
|
for (const [name, proj] of Object.entries(config.projects)) {
|
|
22565
22840
|
const projPath = resolveHome(proj.path);
|
|
@@ -22599,21 +22874,21 @@ function visibleItems(items, includeDone) {
|
|
|
22599
22874
|
function stateColor(state) {
|
|
22600
22875
|
switch (state) {
|
|
22601
22876
|
case "done":
|
|
22602
|
-
return
|
|
22877
|
+
return chalk34.green;
|
|
22603
22878
|
case "cancelled":
|
|
22604
|
-
return
|
|
22879
|
+
return chalk34.dim;
|
|
22605
22880
|
case "blocked":
|
|
22606
|
-
return
|
|
22881
|
+
return chalk34.red;
|
|
22607
22882
|
case "paused":
|
|
22608
|
-
return
|
|
22883
|
+
return chalk34.yellow;
|
|
22609
22884
|
default:
|
|
22610
|
-
return
|
|
22885
|
+
return chalk34.cyan;
|
|
22611
22886
|
}
|
|
22612
22887
|
}
|
|
22613
22888
|
function printItem(item, indent) {
|
|
22614
22889
|
const color = stateColor(item.state);
|
|
22615
|
-
const line = " ".repeat(indent) + `${
|
|
22616
|
-
console.log(TERMINAL_WORK_STATES.has(item.state) ?
|
|
22890
|
+
const line = " ".repeat(indent) + `${chalk34.dim(item.id)} ${item.title} ${color(`[${item.state}]`)}` + (indent === 0 ? chalk34.dim(` (${item.project})`) : "");
|
|
22891
|
+
console.log(TERMINAL_WORK_STATES.has(item.state) ? chalk34.dim(line) : line);
|
|
22617
22892
|
}
|
|
22618
22893
|
function printTree(items) {
|
|
22619
22894
|
const { roots, childrenOf } = groupByParent(items);
|
|
@@ -22632,15 +22907,15 @@ var startCmd = new Command34("start").description("Start a new work item").argum
|
|
|
22632
22907
|
purgeExpiredWorkItems(store);
|
|
22633
22908
|
const project = opts.project ?? detectCurrentProject(config);
|
|
22634
22909
|
if (!project) {
|
|
22635
|
-
console.error(
|
|
22910
|
+
console.error(chalk34.red("No --project given and cwd is not inside a registered project."));
|
|
22636
22911
|
process.exit(1);
|
|
22637
22912
|
}
|
|
22638
22913
|
if (opts.parent && !findWorkItemById(store, opts.parent)) {
|
|
22639
|
-
console.error(
|
|
22914
|
+
console.error(chalk34.red(`Parent work item '${opts.parent}' not found.`));
|
|
22640
22915
|
process.exit(1);
|
|
22641
22916
|
}
|
|
22642
22917
|
const item = createWorkItem(store, { project, title, parent: opts.parent ?? null, tags: opts.tag });
|
|
22643
|
-
console.log(
|
|
22918
|
+
console.log(chalk34.green(`\u2713 ${item.id}`) + ` ${item.title}` + chalk34.dim(` (${item.project})`));
|
|
22644
22919
|
});
|
|
22645
22920
|
var listCmd2 = new Command34("list").description("List work items").option("--project <name>", "scope to one project").option("--all", "list across every project").option("--tree", "render parent/child nesting").option("--include-done", "include done/cancelled items").action((opts) => {
|
|
22646
22921
|
const config = loadConfig();
|
|
@@ -22654,14 +22929,14 @@ var listCmd2 = new Command34("list").description("List work items").option("--pr
|
|
|
22654
22929
|
} else {
|
|
22655
22930
|
const project = detectCurrentProject(config);
|
|
22656
22931
|
if (!project) {
|
|
22657
|
-
console.error(
|
|
22932
|
+
console.error(chalk34.red("cwd is not inside a registered project \u2014 pass --project or --all."));
|
|
22658
22933
|
process.exit(1);
|
|
22659
22934
|
}
|
|
22660
22935
|
items = store.list(project);
|
|
22661
22936
|
}
|
|
22662
22937
|
items = visibleItems(items, opts.includeDone ?? false);
|
|
22663
22938
|
if (items.length === 0) {
|
|
22664
|
-
console.log(
|
|
22939
|
+
console.log(chalk34.dim("\nNo work items.\n"));
|
|
22665
22940
|
return;
|
|
22666
22941
|
}
|
|
22667
22942
|
console.log();
|
|
@@ -22675,15 +22950,15 @@ function closeCommand(name, state, flag, key, desc) {
|
|
|
22675
22950
|
purgeExpiredWorkItems(store);
|
|
22676
22951
|
const item = closeWorkItem(store, id, state, { note: opts[key] });
|
|
22677
22952
|
if (!item) {
|
|
22678
|
-
console.error(
|
|
22953
|
+
console.error(chalk34.red(`Work item '${id}' not found.`));
|
|
22679
22954
|
process.exit(1);
|
|
22680
22955
|
}
|
|
22681
|
-
console.log(
|
|
22956
|
+
console.log(chalk34.green(`\u2713 ${item.id}`) + ` ${item.title} ${chalk34.dim(`[${item.state}]`)}`);
|
|
22682
22957
|
if (state === "done") {
|
|
22683
22958
|
const openChildren = findOpenChildren(store, item.id);
|
|
22684
22959
|
if (openChildren.length > 0) {
|
|
22685
22960
|
const names = openChildren.map((c) => `${c.id} [${c.state}]`).join(", ");
|
|
22686
|
-
console.log(
|
|
22961
|
+
console.log(chalk34.yellow(`\u26A0 still has ${openChildren.length} unfinished child item(s): ${names}`));
|
|
22687
22962
|
}
|
|
22688
22963
|
}
|
|
22689
22964
|
});
|
|
@@ -23166,7 +23441,7 @@ handoffCommand.command("facts <project>").description(
|
|
|
23166
23441
|
init_dist();
|
|
23167
23442
|
init_dist3();
|
|
23168
23443
|
import { Command as Command36 } from "commander";
|
|
23169
|
-
import
|
|
23444
|
+
import chalk35 from "chalk";
|
|
23170
23445
|
var KNOWN_AGENTS = ["claude", "opencode", "codex", "gemini"];
|
|
23171
23446
|
function normalized(p) {
|
|
23172
23447
|
return p.replace(/\/+$/, "");
|
|
@@ -23219,7 +23494,7 @@ async function collectSessions(drivers) {
|
|
|
23219
23494
|
}
|
|
23220
23495
|
var sessionsCommand = new Command36("sessions").description("List live agent sessions squadrant can introspect (read-only; claude + opencode)").option("--json", "emit machine-readable JSON").option("--agent <name>", `filter to one agent (${KNOWN_AGENTS.join("|")})`).option("--project <name>", "filter to one registered project").option("--live-only", "drop sessions whose process is dead (status: stale)").action(async (opts) => {
|
|
23221
23496
|
if (opts.agent && !KNOWN_AGENTS.includes(opts.agent)) {
|
|
23222
|
-
console.error(
|
|
23497
|
+
console.error(chalk35.red(`
|
|
23223
23498
|
\u2718 Unknown agent '${opts.agent}'. Known agents: ${KNOWN_AGENTS.join(", ")}
|
|
23224
23499
|
`));
|
|
23225
23500
|
process.exit(1);
|
|
@@ -23227,7 +23502,7 @@ var sessionsCommand = new Command36("sessions").description("List live agent ses
|
|
|
23227
23502
|
const config = loadConfig();
|
|
23228
23503
|
if (opts.project && !(opts.project in config.projects)) {
|
|
23229
23504
|
const known = Object.keys(config.projects).sort().join(", ") || "(none registered)";
|
|
23230
|
-
console.error(
|
|
23505
|
+
console.error(chalk35.red(`
|
|
23231
23506
|
\u2718 Unknown project '${opts.project}'. Known projects: ${known}
|
|
23232
23507
|
`));
|
|
23233
23508
|
process.exit(1);
|
|
@@ -23243,7 +23518,7 @@ var sessionsCommand = new Command36("sessions").description("List live agent ses
|
|
|
23243
23518
|
};
|
|
23244
23519
|
const { rows, unsupported } = await collectSessions(drivers);
|
|
23245
23520
|
if (opts.agent && unsupported.includes(opts.agent)) {
|
|
23246
|
-
console.error(
|
|
23521
|
+
console.error(chalk35.red(`
|
|
23247
23522
|
\u2718 Agent '${opts.agent}' does not support session introspection.
|
|
23248
23523
|
`));
|
|
23249
23524
|
process.exit(1);
|
|
@@ -23259,7 +23534,7 @@ var sessionsCommand = new Command36("sessions").description("List live agent ses
|
|
|
23259
23534
|
} else {
|
|
23260
23535
|
console.log(formatSessionTable(filtered));
|
|
23261
23536
|
if (!opts.agent && unsupported.length > 0) {
|
|
23262
|
-
console.error(
|
|
23537
|
+
console.error(chalk35.dim(`(unsupported for session introspection: ${unsupported.join(", ")})`));
|
|
23263
23538
|
}
|
|
23264
23539
|
}
|
|
23265
23540
|
});
|
|
@@ -23271,7 +23546,7 @@ init_dist3();
|
|
|
23271
23546
|
import os20 from "os";
|
|
23272
23547
|
import path38 from "path";
|
|
23273
23548
|
import { Command as Command37 } from "commander";
|
|
23274
|
-
import
|
|
23549
|
+
import chalk36 from "chalk";
|
|
23275
23550
|
|
|
23276
23551
|
// packages/cli/src/lib/whoami.ts
|
|
23277
23552
|
function projectForCwd(cwd, projects) {
|
|
@@ -23430,7 +23705,7 @@ var whoamiCommand = new Command37("whoami").description("Show the agent session
|
|
|
23430
23705
|
} else {
|
|
23431
23706
|
console.log(formatWhoami(result));
|
|
23432
23707
|
if (!result.ok) {
|
|
23433
|
-
console.error(
|
|
23708
|
+
console.error(chalk36.yellow("\n Could not identify a squadrant agent session for this invocation."));
|
|
23434
23709
|
}
|
|
23435
23710
|
}
|
|
23436
23711
|
if (!result.ok) process.exitCode = 1;
|