@martintrojer/mu 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +286 -47
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +72 -48
- package/dist/index.js +58 -14
- package/dist/index.js.map +1 -1
- package/docs/USAGE_GUIDE.md +38 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -645,7 +645,12 @@ var init_logs = __esm({
|
|
|
645
645
|
// src/exporting.ts — archive export emits the bucket-render summary
|
|
646
646
|
// as a machine-wide event (workstream=null; the export spans every
|
|
647
647
|
// source-ws in the archive).
|
|
648
|
-
"archive export"
|
|
648
|
+
"archive export",
|
|
649
|
+
// src/db-sync.ts — emitted per-workstream after a successful
|
|
650
|
+
// `mu db export`. Used as the marker for src/parked.ts (the
|
|
651
|
+
// "presumed parked on another machine" heuristic for `mu workstream
|
|
652
|
+
// list` / TUI tab strip).
|
|
653
|
+
"db export"
|
|
649
654
|
];
|
|
650
655
|
}
|
|
651
656
|
});
|
|
@@ -1078,17 +1083,19 @@ async function reconcile(db, opts) {
|
|
|
1078
1083
|
let statusChanges = 0;
|
|
1079
1084
|
const orphans = [];
|
|
1080
1085
|
const survivors = [];
|
|
1086
|
+
const pendingSurvivors = [];
|
|
1081
1087
|
for (const agent of dbAgents) {
|
|
1082
|
-
if (
|
|
1088
|
+
if (isPendingPaneId(agent.paneId)) {
|
|
1089
|
+
pendingSurvivors.push(agent);
|
|
1090
|
+
} else if (tmuxByPaneId.has(agent.paneId)) {
|
|
1083
1091
|
survivors.push(agent);
|
|
1084
1092
|
} else {
|
|
1085
1093
|
if (mode === "full") deleteAgent(db, agent.name, agent.workstreamName);
|
|
1086
1094
|
prunedGhosts++;
|
|
1087
1095
|
}
|
|
1088
1096
|
}
|
|
1089
|
-
if (mode
|
|
1097
|
+
if (mode === "full") {
|
|
1090
1098
|
for (const agent of survivors) {
|
|
1091
|
-
if (isPendingPaneId(agent.paneId)) continue;
|
|
1092
1099
|
const scrollback = await capturePane(agent.paneId, { lines: 100 });
|
|
1093
1100
|
const detected = detectPiStatus(scrollback);
|
|
1094
1101
|
if (shouldOverwriteAgentStatus(agent.status, detected) && detected !== agent.status) {
|
|
@@ -5633,6 +5640,37 @@ var init_exporting = __esm({
|
|
|
5633
5640
|
}
|
|
5634
5641
|
});
|
|
5635
5642
|
|
|
5643
|
+
// src/parked.ts
|
|
5644
|
+
function parkedStatus(db, workstream, opts = {}) {
|
|
5645
|
+
const wsRow = db.prepare("SELECT id FROM workstreams WHERE name = ?").get(workstream);
|
|
5646
|
+
if (wsRow === void 0) return { parked: false };
|
|
5647
|
+
const latest = db.prepare(
|
|
5648
|
+
"SELECT kind, payload, created_at FROM agent_logs WHERE workstream_id = ? ORDER BY seq DESC LIMIT 1"
|
|
5649
|
+
).get(wsRow.id);
|
|
5650
|
+
if (latest === void 0) return { parked: false };
|
|
5651
|
+
if (latest.kind !== "event") return { parked: false };
|
|
5652
|
+
if (!latest.payload.startsWith("db export ")) return { parked: false };
|
|
5653
|
+
const aliveAgent = db.prepare("SELECT 1 AS x FROM agents WHERE workstream_id = ? AND status != 'closed' LIMIT 1").get(wsRow.id);
|
|
5654
|
+
if (aliveAgent !== void 0) return { parked: false };
|
|
5655
|
+
const inProgress = db.prepare("SELECT 1 AS x FROM tasks WHERE workstream_id = ? AND status = 'IN_PROGRESS' LIMIT 1").get(wsRow.id);
|
|
5656
|
+
if (inProgress !== void 0) return { parked: false };
|
|
5657
|
+
const threshold = Math.max(0, opts.thresholdDays ?? WORKSTREAM_PARKED_THRESHOLD_DAYS);
|
|
5658
|
+
const exportedAt = Date.parse(latest.created_at);
|
|
5659
|
+
if (Number.isNaN(exportedAt)) return { parked: false };
|
|
5660
|
+
const now = (opts.now ?? /* @__PURE__ */ new Date()).getTime();
|
|
5661
|
+
const deltaMs = now - exportedAt;
|
|
5662
|
+
const deltaDays = Math.floor(deltaMs / (24 * 60 * 60 * 1e3));
|
|
5663
|
+
if (deltaDays < threshold) return { parked: false };
|
|
5664
|
+
return { parked: true, sinceDays: deltaDays };
|
|
5665
|
+
}
|
|
5666
|
+
var WORKSTREAM_PARKED_THRESHOLD_DAYS;
|
|
5667
|
+
var init_parked = __esm({
|
|
5668
|
+
"src/parked.ts"() {
|
|
5669
|
+
"use strict";
|
|
5670
|
+
WORKSTREAM_PARKED_THRESHOLD_DAYS = 1;
|
|
5671
|
+
}
|
|
5672
|
+
});
|
|
5673
|
+
|
|
5636
5674
|
// src/workstream.ts
|
|
5637
5675
|
import { existsSync as existsSync11, readdirSync as readdirSync2, rmdirSync } from "fs";
|
|
5638
5676
|
import { join as join7, resolve as resolve4 } from "path";
|
|
@@ -5715,6 +5753,7 @@ async function listEmptyWorkstreams(db) {
|
|
|
5715
5753
|
}
|
|
5716
5754
|
async function summarizeWorkstream(db, opts) {
|
|
5717
5755
|
const tmuxSession = opts.tmuxSession ?? `mu-${opts.workstream}`;
|
|
5756
|
+
const parked = parkedStatus(db, opts.workstream);
|
|
5718
5757
|
return {
|
|
5719
5758
|
name: opts.workstream,
|
|
5720
5759
|
tmuxSession,
|
|
@@ -5724,7 +5763,8 @@ async function summarizeWorkstream(db, opts) {
|
|
|
5724
5763
|
noteCount: countNotes(db, opts.workstream),
|
|
5725
5764
|
edgeCount: countEdges(db, opts.workstream),
|
|
5726
5765
|
workspaceCount: listWorkspaces(db, opts.workstream).length,
|
|
5727
|
-
registered: isRegistered(db, opts.workstream)
|
|
5766
|
+
registered: isRegistered(db, opts.workstream),
|
|
5767
|
+
...parked.parked ? { parked: { sinceDays: parked.sinceDays ?? 0 } } : {}
|
|
5728
5768
|
};
|
|
5729
5769
|
}
|
|
5730
5770
|
function isRegistered(db, workstream) {
|
|
@@ -5868,6 +5908,7 @@ var init_workstream = __esm({
|
|
|
5868
5908
|
init_db();
|
|
5869
5909
|
init_exporting();
|
|
5870
5910
|
init_logs();
|
|
5911
|
+
init_parked();
|
|
5871
5912
|
init_snapshots();
|
|
5872
5913
|
init_tmux();
|
|
5873
5914
|
init_vcs2();
|
|
@@ -6854,6 +6895,9 @@ function shouldOverwriteAgentStatus(current, detected) {
|
|
|
6854
6895
|
}
|
|
6855
6896
|
return true;
|
|
6856
6897
|
}
|
|
6898
|
+
function agentStatusGlyph(status) {
|
|
6899
|
+
return STATUS_EMOJI[status] ?? "?";
|
|
6900
|
+
}
|
|
6857
6901
|
function pendingPaneIdFor(agentName) {
|
|
6858
6902
|
return `${PENDING_PANE_PREFIX}${agentName}`;
|
|
6859
6903
|
}
|
|
@@ -6865,7 +6909,7 @@ function composeAgentTitle(db, agent) {
|
|
|
6865
6909
|
const tasks = listTasksByOwner(db, agent.workstreamName, agent.name);
|
|
6866
6910
|
let title = agent.name;
|
|
6867
6911
|
if (showStatus) {
|
|
6868
|
-
title += ` \xB7 ${
|
|
6912
|
+
title += ` \xB7 ${agentStatusGlyph(agent.status)}`;
|
|
6869
6913
|
}
|
|
6870
6914
|
if (tasks.length === 1) {
|
|
6871
6915
|
title += ` \xB7 ${tasks[0]?.name}`;
|
|
@@ -7271,7 +7315,7 @@ function loadDoctorSummary(db, snapshot) {
|
|
|
7271
7315
|
checks.push({
|
|
7272
7316
|
name: "agents",
|
|
7273
7317
|
status: "warn",
|
|
7274
|
-
detail: `${ghosts} ghost pane${ghosts === 1 ? "" : "s"}; run \`mu agent list
|
|
7318
|
+
detail: `${ghosts} ghost pane${ghosts === 1 ? "" : "s"}; run \`mu state\` or \`mu agent list\` to reap`
|
|
7275
7319
|
});
|
|
7276
7320
|
}
|
|
7277
7321
|
const orphanPanes = snapshot.view.orphans.length;
|
|
@@ -7308,7 +7352,7 @@ function loadDoctorChecks(db, snapshot) {
|
|
|
7308
7352
|
function yankCommandForCheck(check) {
|
|
7309
7353
|
switch (check.name) {
|
|
7310
7354
|
case "agents":
|
|
7311
|
-
return "mu
|
|
7355
|
+
return "mu state";
|
|
7312
7356
|
case "panes":
|
|
7313
7357
|
return "mu agent adopt";
|
|
7314
7358
|
case "workspaces":
|
|
@@ -7326,10 +7370,10 @@ function remediationParagraph(check) {
|
|
|
7326
7370
|
switch (check.name) {
|
|
7327
7371
|
case "agents":
|
|
7328
7372
|
return [
|
|
7329
|
-
"A 'ghost pane' is a
|
|
7330
|
-
"
|
|
7331
|
-
"
|
|
7332
|
-
"
|
|
7373
|
+
"A 'ghost pane' is a registered agent whose tmux pane is gone.",
|
|
7374
|
+
"Doctor only reports the count. Run `mu state` or `mu agent list`",
|
|
7375
|
+
"to reap ghost agents and return their IN_PROGRESS tasks to OPEN.",
|
|
7376
|
+
"The TUI is read-only, but its slow tick uses the same state reap."
|
|
7333
7377
|
];
|
|
7334
7378
|
case "panes":
|
|
7335
7379
|
return [
|
|
@@ -7513,7 +7557,7 @@ async function loadWorkstreamSnapshotFast(db, workstream, opts = {}) {
|
|
|
7513
7557
|
};
|
|
7514
7558
|
}
|
|
7515
7559
|
async function loadWorkstreamSnapshotSlow(db, workstream, opts = {}, baseSnapshot) {
|
|
7516
|
-
const view = await listLiveAgents(db, { workstream
|
|
7560
|
+
const view = await listLiveAgents(db, { workstream });
|
|
7517
7561
|
let workspaces = listWorkspaces(db, workstream);
|
|
7518
7562
|
if (opts.withDirty === true) workspaces = await decorateWithDirty(workspaces);
|
|
7519
7563
|
const commits = await loadRecentCommits(opts.withRecentCommits);
|
|
@@ -7556,7 +7600,7 @@ function emptyLiveAgentsView() {
|
|
|
7556
7600
|
return {
|
|
7557
7601
|
agents: [],
|
|
7558
7602
|
orphans: [],
|
|
7559
|
-
report: { prunedGhosts: 0, statusChanges: 0, orphans: [], mode: "
|
|
7603
|
+
report: { prunedGhosts: 0, statusChanges: 0, orphans: [], mode: "full" }
|
|
7560
7604
|
};
|
|
7561
7605
|
}
|
|
7562
7606
|
function minimalSnapshot(workstream) {
|
|
@@ -7640,9 +7684,30 @@ var init_state = __esm({
|
|
|
7640
7684
|
}
|
|
7641
7685
|
});
|
|
7642
7686
|
|
|
7687
|
+
// src/cli/tui/agent-display.ts
|
|
7688
|
+
function agentByName(snapshot) {
|
|
7689
|
+
const agents = snapshot?.view?.agents ?? [];
|
|
7690
|
+
return new Map(agents.map((a) => [a.name, a]));
|
|
7691
|
+
}
|
|
7692
|
+
function formatKnownAgentDisplayName(agent) {
|
|
7693
|
+
return `${agentStatusGlyph(agent.status)} ${agent.name}`;
|
|
7694
|
+
}
|
|
7695
|
+
function formatAgentRefDisplayName(agentName, agents) {
|
|
7696
|
+
if (agentName === null) return "\u2014";
|
|
7697
|
+
const agent = agents.get(agentName);
|
|
7698
|
+
if (agent === void 0) return agentName;
|
|
7699
|
+
return formatKnownAgentDisplayName(agent);
|
|
7700
|
+
}
|
|
7701
|
+
var init_agent_display = __esm({
|
|
7702
|
+
"src/cli/tui/agent-display.ts"() {
|
|
7703
|
+
"use strict";
|
|
7704
|
+
init_agents();
|
|
7705
|
+
}
|
|
7706
|
+
});
|
|
7707
|
+
|
|
7643
7708
|
// src/cli/format.ts
|
|
7644
7709
|
function statusIcon(status) {
|
|
7645
|
-
return STATUS_COLORS[status](
|
|
7710
|
+
return STATUS_COLORS[status](agentStatusGlyph(status));
|
|
7646
7711
|
}
|
|
7647
7712
|
function colorStatus(status) {
|
|
7648
7713
|
switch (status) {
|
|
@@ -7823,17 +7888,19 @@ function printLogRow(row2) {
|
|
|
7823
7888
|
}
|
|
7824
7889
|
function formatWorkstreamsTable(rows) {
|
|
7825
7890
|
const table = muTable({
|
|
7826
|
-
head: ["name", "tmux", "agents", "tasks", "edges", "notes"].map((h) => pc.bold(h)),
|
|
7827
|
-
colWidths: [40, null, null, null, null, null]
|
|
7891
|
+
head: ["name", "tmux", "agents", "tasks", "edges", "notes", "parked"].map((h) => pc.bold(h)),
|
|
7892
|
+
colWidths: [40, null, null, null, null, null, null]
|
|
7828
7893
|
});
|
|
7829
7894
|
for (const r of rows) {
|
|
7895
|
+
const parkedCell = r.parked ? pc.yellow(`${r.parked.sinceDays}d`) : pc.dim("\u2014");
|
|
7830
7896
|
table.push([
|
|
7831
7897
|
r.name,
|
|
7832
7898
|
r.tmuxAlive ? pc.green("alive") : pc.dim("\u2014"),
|
|
7833
7899
|
String(r.agentCount),
|
|
7834
7900
|
String(r.taskCount),
|
|
7835
7901
|
String(r.edgeCount),
|
|
7836
|
-
String(r.noteCount)
|
|
7902
|
+
String(r.noteCount),
|
|
7903
|
+
parkedCell
|
|
7837
7904
|
]);
|
|
7838
7905
|
}
|
|
7839
7906
|
return table.toString();
|
|
@@ -8125,6 +8192,58 @@ function cullCardsForRows(visibleCardIds2, availableRows) {
|
|
|
8125
8192
|
}
|
|
8126
8193
|
return { cards: cards.filter((id) => remaining.has(id)), hidden };
|
|
8127
8194
|
}
|
|
8195
|
+
function balanceColumns(assignments, dataCountFn) {
|
|
8196
|
+
if (assignments.length < 2) return assignments.map((a) => ({ cards: [...a.cards] }));
|
|
8197
|
+
const totalCards = assignments.reduce((sum, a) => sum + a.cards.length, 0);
|
|
8198
|
+
if (totalCards >= TOTAL_CARD_COUNT) return assignments.map((a) => ({ cards: [...a.cards] }));
|
|
8199
|
+
const cols = assignments.map((a) => [...a.cards]);
|
|
8200
|
+
const heightOf = (id) => {
|
|
8201
|
+
const config = CARD_CONFIGS[id];
|
|
8202
|
+
const data = Math.max(0, Math.floor(dataCountFn(id)));
|
|
8203
|
+
return config.chrome + clamp(data, config.minRows, config.maxRows);
|
|
8204
|
+
};
|
|
8205
|
+
const isAnchored = (id) => id === 0 || id === 3;
|
|
8206
|
+
const heightsOf = (lanes) => lanes.map((lane) => lane.reduce((sum, id) => sum + heightOf(id), 0));
|
|
8207
|
+
const safetyMax = cols.length * TOTAL_CARD_COUNT;
|
|
8208
|
+
for (let iter = 0; iter < safetyMax; iter++) {
|
|
8209
|
+
const heights = heightsOf(cols);
|
|
8210
|
+
const startSpread = Math.max(...heights) - Math.min(...heights);
|
|
8211
|
+
if (startSpread <= 0) break;
|
|
8212
|
+
let best = null;
|
|
8213
|
+
for (let donor = 0; donor < cols.length; donor++) {
|
|
8214
|
+
const donorCards2 = cols[donor];
|
|
8215
|
+
const donorH = heights[donor];
|
|
8216
|
+
if (donorCards2 === void 0 || donorH === void 0) continue;
|
|
8217
|
+
if (donorCards2.length <= 1) continue;
|
|
8218
|
+
for (let cardIndex = 0; cardIndex < donorCards2.length; cardIndex++) {
|
|
8219
|
+
const card = donorCards2[cardIndex];
|
|
8220
|
+
if (card === void 0 || isAnchored(card)) continue;
|
|
8221
|
+
const cardH = heightOf(card);
|
|
8222
|
+
for (let receiver = 0; receiver < cols.length; receiver++) {
|
|
8223
|
+
if (receiver === donor) continue;
|
|
8224
|
+
const receiverH = heights[receiver];
|
|
8225
|
+
if (receiverH === void 0) continue;
|
|
8226
|
+
const newHeights = heights.slice();
|
|
8227
|
+
newHeights[donor] = donorH - cardH;
|
|
8228
|
+
newHeights[receiver] = receiverH + cardH;
|
|
8229
|
+
const newSpread = Math.max(...newHeights) - Math.min(...newHeights);
|
|
8230
|
+
if (newSpread < startSpread && (best === null || newSpread < best.spread)) {
|
|
8231
|
+
best = { donor, receiver, cardIndex, spread: newSpread };
|
|
8232
|
+
}
|
|
8233
|
+
}
|
|
8234
|
+
}
|
|
8235
|
+
}
|
|
8236
|
+
if (best === null) break;
|
|
8237
|
+
const donorCards = cols[best.donor];
|
|
8238
|
+
const receiverCards = cols[best.receiver];
|
|
8239
|
+
if (donorCards === void 0 || receiverCards === void 0) break;
|
|
8240
|
+
const moved = donorCards.splice(best.cardIndex, 1)[0];
|
|
8241
|
+
if (moved === void 0) break;
|
|
8242
|
+
receiverCards.push(moved);
|
|
8243
|
+
receiverCards.sort(compareSlot);
|
|
8244
|
+
}
|
|
8245
|
+
return cols.map((cards) => ({ cards }));
|
|
8246
|
+
}
|
|
8128
8247
|
function allocateRowBudgets(availableRows, cards) {
|
|
8129
8248
|
const entries = cards.map((card) => {
|
|
8130
8249
|
const config = card.config ?? CARD_CONFIGS[card.id];
|
|
@@ -8238,7 +8357,7 @@ function tallestMinStackRows(ids) {
|
|
|
8238
8357
|
function clamp(value, min, max) {
|
|
8239
8358
|
return Math.max(min, Math.min(max, value));
|
|
8240
8359
|
}
|
|
8241
|
-
var CARD_CHROME_ROWS, CARD_CONFIGS, CARD_CULL_PRIORITY, CARD_CULL_LAYOUT_COLS;
|
|
8360
|
+
var CARD_CHROME_ROWS, CARD_CONFIGS, CARD_CULL_PRIORITY, CARD_CULL_LAYOUT_COLS, TOTAL_CARD_COUNT;
|
|
8242
8361
|
var init_layout = __esm({
|
|
8243
8362
|
"src/cli/tui/layout.ts"() {
|
|
8244
8363
|
"use strict";
|
|
@@ -8327,6 +8446,7 @@ var init_layout = __esm({
|
|
|
8327
8446
|
};
|
|
8328
8447
|
CARD_CULL_PRIORITY = [9, 8, 5, 2, 7, 6, 4, 0, 1, 3];
|
|
8329
8448
|
CARD_CULL_LAYOUT_COLS = 140;
|
|
8449
|
+
TOTAL_CARD_COUNT = 10;
|
|
8330
8450
|
}
|
|
8331
8451
|
});
|
|
8332
8452
|
|
|
@@ -8588,7 +8708,7 @@ function AgentsCard({ snapshot, rowBudget, cols }) {
|
|
|
8588
8708
|
const owned = snapshot.inProgress.filter((t) => t.ownerName === a.name);
|
|
8589
8709
|
const taskBit = summarizeOwnedTasks(owned).bit;
|
|
8590
8710
|
const idle = a.idle ? "\u26A0 idle" : "";
|
|
8591
|
-
return [
|
|
8711
|
+
return [agentStatusGlyph(a.status), a.name, taskBit, idle];
|
|
8592
8712
|
});
|
|
8593
8713
|
const widths = layoutColumns(rows, COLUMN_SPECS, contentWidth);
|
|
8594
8714
|
return /* @__PURE__ */ jsx6(
|
|
@@ -8630,8 +8750,8 @@ var cardConfig, COLUMN_SPECS;
|
|
|
8630
8750
|
var init_agents2 = __esm({
|
|
8631
8751
|
"src/cli/tui/cards/agents.tsx"() {
|
|
8632
8752
|
"use strict";
|
|
8633
|
-
init_agents();
|
|
8634
8753
|
init_state();
|
|
8754
|
+
init_agent_display();
|
|
8635
8755
|
init_columns();
|
|
8636
8756
|
init_layout();
|
|
8637
8757
|
init_list_row();
|
|
@@ -9077,11 +9197,12 @@ function InProgressCard({ snapshot, rowBudget, cols }) {
|
|
|
9077
9197
|
const shown = inProgress.slice(0, rowBudget ?? cardConfig5.maxRows);
|
|
9078
9198
|
const more = inProgress.length - shown.length;
|
|
9079
9199
|
const bottomLabel = more > 0 ? `+${more} more \xB7 Shift+6` : void 0;
|
|
9200
|
+
const agentLookup = agentByName(snapshot);
|
|
9080
9201
|
const rows = shown.map((t, i) => [
|
|
9081
9202
|
GLYPH2,
|
|
9082
9203
|
t.name,
|
|
9083
9204
|
t.status,
|
|
9084
|
-
t.ownerName
|
|
9205
|
+
formatAgentRefDisplayName(t.ownerName, agentLookup),
|
|
9085
9206
|
formatSinceClaim(ages[i] ?? null),
|
|
9086
9207
|
t.title
|
|
9087
9208
|
]);
|
|
@@ -9132,8 +9253,8 @@ var cardConfig5, GLYPH2, STALE_CLAIM_THRESHOLD_MS, COLUMN_SPECS5, glyphFor3;
|
|
|
9132
9253
|
var init_inprogress = __esm({
|
|
9133
9254
|
"src/cli/tui/cards/inprogress.tsx"() {
|
|
9134
9255
|
"use strict";
|
|
9135
|
-
init_agents();
|
|
9136
9256
|
init_format();
|
|
9257
|
+
init_agent_display();
|
|
9137
9258
|
init_columns();
|
|
9138
9259
|
init_format_helpers();
|
|
9139
9260
|
init_layout();
|
|
@@ -9141,7 +9262,7 @@ var init_inprogress = __esm({
|
|
|
9141
9262
|
init_titled_box();
|
|
9142
9263
|
init_placeholder();
|
|
9143
9264
|
cardConfig5 = CARD_CONFIGS[6];
|
|
9144
|
-
GLYPH2 =
|
|
9265
|
+
GLYPH2 = agentStatusGlyph("busy");
|
|
9145
9266
|
STALE_CLAIM_THRESHOLD_MS = 3e5;
|
|
9146
9267
|
COLUMN_SPECS5 = [
|
|
9147
9268
|
{ kind: "protect" },
|
|
@@ -9282,12 +9403,13 @@ function ReadyCard({ snapshot, rowBudget, cols }) {
|
|
|
9282
9403
|
const roiText = formatRoi(t.impact, t.effortDays);
|
|
9283
9404
|
return { bucket, roiText };
|
|
9284
9405
|
});
|
|
9406
|
+
const agentLookup = agentByName(snapshot);
|
|
9285
9407
|
const rows = shown.map((t, i) => [
|
|
9286
9408
|
t.name,
|
|
9287
9409
|
t.status,
|
|
9288
9410
|
`ROI ${meta[i]?.roiText ?? ""}`,
|
|
9289
9411
|
t.title,
|
|
9290
|
-
t.ownerName
|
|
9412
|
+
formatAgentRefDisplayName(t.ownerName, agentLookup)
|
|
9291
9413
|
]);
|
|
9292
9414
|
const widths = layoutColumns(rows, COLUMN_SPECS7, contentWidth);
|
|
9293
9415
|
return /* @__PURE__ */ jsx12(
|
|
@@ -9327,6 +9449,7 @@ var init_ready = __esm({
|
|
|
9327
9449
|
"use strict";
|
|
9328
9450
|
init_state();
|
|
9329
9451
|
init_format();
|
|
9452
|
+
init_agent_display();
|
|
9330
9453
|
init_columns();
|
|
9331
9454
|
init_format_helpers();
|
|
9332
9455
|
init_layout();
|
|
@@ -9577,9 +9700,10 @@ function WorkspacesCard({ snapshot, rowBudget, cols }) {
|
|
|
9577
9700
|
const shown = workspaces.slice(0, rowBudget ?? cardConfig10.maxRows);
|
|
9578
9701
|
const more = workspaces.length - shown.length;
|
|
9579
9702
|
const bottomLabel = more > 0 ? `+${more} more \xB7 Shift+5` : void 0;
|
|
9703
|
+
const agentLookup = agentByName(snapshot);
|
|
9580
9704
|
const rows = shown.map((w) => [
|
|
9581
9705
|
glyphFor5(w),
|
|
9582
|
-
w.agentName,
|
|
9706
|
+
formatAgentRefDisplayName(w.agentName, agentLookup),
|
|
9583
9707
|
w.backend,
|
|
9584
9708
|
formatBehind2(w.commitsBehindMain),
|
|
9585
9709
|
w.parentRef ? w.parentRef.slice(0, 12) : "\u2014"
|
|
@@ -9649,6 +9773,7 @@ var init_workspaces = __esm({
|
|
|
9649
9773
|
"src/cli/tui/cards/workspaces.tsx"() {
|
|
9650
9774
|
"use strict";
|
|
9651
9775
|
init_workspace();
|
|
9776
|
+
init_agent_display();
|
|
9652
9777
|
init_columns();
|
|
9653
9778
|
init_layout();
|
|
9654
9779
|
init_list_row();
|
|
@@ -9790,6 +9915,9 @@ var init_keymap_spec = __esm({
|
|
|
9790
9915
|
row("/", "filter/search rows", ["/"]),
|
|
9791
9916
|
row("Enter", "drill into focused row", ["Enter"]),
|
|
9792
9917
|
row("y", "yank action for focused row", ["y"]),
|
|
9918
|
+
row("a", "attach to focused agent's tmux pane (Agents popup only; user-driven TUI escape)", [
|
|
9919
|
+
"a"
|
|
9920
|
+
]),
|
|
9793
9921
|
row("l", "launch lazygit in the project root (Commits popup only; user-driven TUI escape)", [
|
|
9794
9922
|
"l"
|
|
9795
9923
|
]),
|
|
@@ -10287,6 +10415,65 @@ var init_popup_shell = __esm({
|
|
|
10287
10415
|
}
|
|
10288
10416
|
});
|
|
10289
10417
|
|
|
10418
|
+
// src/cli/tui/tmux-attach.ts
|
|
10419
|
+
import { spawnSync } from "child_process";
|
|
10420
|
+
function runTmuxAttachInteractive(opts, deps = {}) {
|
|
10421
|
+
const run3 = deps.spawn ?? spawnSync;
|
|
10422
|
+
const write = deps.write ?? ((text2) => process.stdout.write(text2));
|
|
10423
|
+
const env = deps.env ?? process.env;
|
|
10424
|
+
const target = `${opts.session}:${opts.window}`;
|
|
10425
|
+
const insideTmux = typeof env.TMUX === "string" && env.TMUX.length > 0;
|
|
10426
|
+
let result = { ok: true };
|
|
10427
|
+
try {
|
|
10428
|
+
write(ALT_SCREEN_EXIT);
|
|
10429
|
+
if (insideTmux) {
|
|
10430
|
+
const r = run3("tmux", ["switch-client", "-t", target], {
|
|
10431
|
+
stdio: "inherit",
|
|
10432
|
+
env
|
|
10433
|
+
});
|
|
10434
|
+
if (r.error !== void 0) {
|
|
10435
|
+
result = { ok: false, error: tmuxAttachErrorMessage(r.error) };
|
|
10436
|
+
} else if (typeof r.status === "number" && r.status !== 0) {
|
|
10437
|
+
result = { ok: false, error: `tmux switch-client exited ${r.status}` };
|
|
10438
|
+
}
|
|
10439
|
+
} else {
|
|
10440
|
+
const attach = run3("tmux", ["attach-session", "-t", opts.session], {
|
|
10441
|
+
stdio: "inherit",
|
|
10442
|
+
env
|
|
10443
|
+
});
|
|
10444
|
+
if (attach.error !== void 0) {
|
|
10445
|
+
result = { ok: false, error: tmuxAttachErrorMessage(attach.error) };
|
|
10446
|
+
} else if (typeof attach.status === "number" && attach.status !== 0) {
|
|
10447
|
+
result = { ok: false, error: `tmux attach-session exited ${attach.status}` };
|
|
10448
|
+
} else {
|
|
10449
|
+
run3("tmux", ["select-window", "-t", target], { stdio: "inherit", env });
|
|
10450
|
+
}
|
|
10451
|
+
}
|
|
10452
|
+
} catch (err) {
|
|
10453
|
+
result = { ok: false, error: tmuxAttachErrorMessage(err) };
|
|
10454
|
+
} finally {
|
|
10455
|
+
try {
|
|
10456
|
+
write(ALT_SCREEN_ENTER);
|
|
10457
|
+
} catch {
|
|
10458
|
+
}
|
|
10459
|
+
}
|
|
10460
|
+
return result;
|
|
10461
|
+
}
|
|
10462
|
+
function tmuxAttachErrorMessage(err) {
|
|
10463
|
+
if (err instanceof Error) {
|
|
10464
|
+
const code = err.code;
|
|
10465
|
+
if (code === "ENOENT") return "tmux not found \xB7 install tmux";
|
|
10466
|
+
return err.message.length > 0 ? err.message : String(err);
|
|
10467
|
+
}
|
|
10468
|
+
return String(err);
|
|
10469
|
+
}
|
|
10470
|
+
var init_tmux_attach = __esm({
|
|
10471
|
+
"src/cli/tui/tmux-attach.ts"() {
|
|
10472
|
+
"use strict";
|
|
10473
|
+
init_escapes();
|
|
10474
|
+
}
|
|
10475
|
+
});
|
|
10476
|
+
|
|
10290
10477
|
// src/cli/tui/use-popup-action-queue.ts
|
|
10291
10478
|
import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
10292
10479
|
function usePopupActionQueue(actions, dispatch) {
|
|
@@ -10653,6 +10840,7 @@ function AgentsPopup({
|
|
|
10653
10840
|
onModeChange,
|
|
10654
10841
|
onFilterEditingChange,
|
|
10655
10842
|
popupActions,
|
|
10843
|
+
onFooter,
|
|
10656
10844
|
db,
|
|
10657
10845
|
workstream
|
|
10658
10846
|
}) {
|
|
@@ -10771,6 +10959,14 @@ function AgentsPopup({
|
|
|
10771
10959
|
void yank2(`mu agent close ${a.name} -w ${ws}`);
|
|
10772
10960
|
return;
|
|
10773
10961
|
}
|
|
10962
|
+
if (action.key === "a") {
|
|
10963
|
+
const session = `mu-${ws}`;
|
|
10964
|
+
const window = a.tab ?? a.name;
|
|
10965
|
+
const r = runTmuxAttachInteractive({ session, window });
|
|
10966
|
+
if (!r.ok) onFooter?.(r.error ?? "tmux attach failed", false, "error");
|
|
10967
|
+
else onFooter?.(`tmux switch-client \u2192 ${session}:${window}`, true, "info");
|
|
10968
|
+
return;
|
|
10969
|
+
}
|
|
10774
10970
|
return;
|
|
10775
10971
|
}
|
|
10776
10972
|
}
|
|
@@ -10811,13 +11007,13 @@ function AgentsPopup({
|
|
|
10811
11007
|
) }) });
|
|
10812
11008
|
}
|
|
10813
11009
|
const { start, visible } = centredVisibleSlice(agents, safeCursor, viewport);
|
|
10814
|
-
const rows = visible.map((a) => [
|
|
11010
|
+
const rows = visible.map((a) => [agentStatusGlyph(a.status), a.name, a.status, a.role]);
|
|
10815
11011
|
const widths = layoutColumns(rows, COLUMN_SPECS11, contentWidth);
|
|
10816
11012
|
return /* @__PURE__ */ jsxs10(
|
|
10817
11013
|
PopupShell,
|
|
10818
11014
|
{
|
|
10819
11015
|
title: `Agents \xB7 popup (${safeCursor + 1}/${agents.length})`,
|
|
10820
|
-
hint: "f free \xB7 x close \xB7 y yanks `mu agent send`",
|
|
11016
|
+
hint: "a attach \xB7 f free \xB7 x close \xB7 y yanks `mu agent send`",
|
|
10821
11017
|
children: [
|
|
10822
11018
|
/* @__PURE__ */ jsx20(Box7, { flexDirection: "column", flexGrow: 1, children: visible.map((a, i) => {
|
|
10823
11019
|
const sel = start + i === safeCursor;
|
|
@@ -10845,10 +11041,12 @@ var init_agents3 = __esm({
|
|
|
10845
11041
|
"src/cli/tui/popups/agents.tsx"() {
|
|
10846
11042
|
"use strict";
|
|
10847
11043
|
init_agents();
|
|
11044
|
+
init_agent_display();
|
|
10848
11045
|
init_columns();
|
|
10849
11046
|
init_keys();
|
|
10850
11047
|
init_list_row();
|
|
10851
11048
|
init_popup_shell();
|
|
11049
|
+
init_tmux_attach();
|
|
10852
11050
|
init_use_popup_action_queue();
|
|
10853
11051
|
init_use_popup_filter();
|
|
10854
11052
|
init_drill();
|
|
@@ -11201,10 +11399,11 @@ function AllTasksPopup({
|
|
|
11201
11399
|
) }) });
|
|
11202
11400
|
}
|
|
11203
11401
|
const { start, visible: windowed } = centredVisibleSlice(visibleTasks, safeCursor, viewport);
|
|
11402
|
+
const agentLookup = agentByName(snapshot);
|
|
11204
11403
|
const rows = windowed.map((t) => [
|
|
11205
11404
|
t.name,
|
|
11206
11405
|
t.status,
|
|
11207
|
-
t.ownerName
|
|
11406
|
+
formatAgentRefDisplayName(t.ownerName, agentLookup),
|
|
11208
11407
|
formatRoi(t.impact, t.effortDays),
|
|
11209
11408
|
t.title
|
|
11210
11409
|
]);
|
|
@@ -11297,6 +11496,7 @@ var init_all_tasks = __esm({
|
|
|
11297
11496
|
init_tasks();
|
|
11298
11497
|
init_sort();
|
|
11299
11498
|
init_format();
|
|
11499
|
+
init_agent_display();
|
|
11300
11500
|
init_columns();
|
|
11301
11501
|
init_format_helpers();
|
|
11302
11502
|
init_keys();
|
|
@@ -11534,9 +11734,9 @@ var init_blocked2 = __esm({
|
|
|
11534
11734
|
});
|
|
11535
11735
|
|
|
11536
11736
|
// src/cli/tui/lazygit.ts
|
|
11537
|
-
import { spawnSync } from "child_process";
|
|
11737
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
11538
11738
|
function runLazygitInteractive(opts, deps = {}) {
|
|
11539
|
-
const run3 = deps.spawn ??
|
|
11739
|
+
const run3 = deps.spawn ?? spawnSync2;
|
|
11540
11740
|
const write = deps.write ?? ((text2) => process.stdout.write(text2));
|
|
11541
11741
|
const env = deps.env ?? process.env;
|
|
11542
11742
|
let result = { ok: true };
|
|
@@ -11580,9 +11780,9 @@ var init_lazygit = __esm({
|
|
|
11580
11780
|
});
|
|
11581
11781
|
|
|
11582
11782
|
// src/cli/tui/tuicr.ts
|
|
11583
|
-
import { spawnSync as
|
|
11783
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
11584
11784
|
function runTuicrInteractive(opts, deps = {}) {
|
|
11585
|
-
const run3 = deps.spawn ??
|
|
11785
|
+
const run3 = deps.spawn ?? spawnSync3;
|
|
11586
11786
|
const write = deps.write ?? ((text2) => process.stdout.write(text2));
|
|
11587
11787
|
const env = deps.env ?? process.env;
|
|
11588
11788
|
let result = { ok: true };
|
|
@@ -12306,13 +12506,14 @@ function InProgressPopup({
|
|
|
12306
12506
|
const now = Date.now();
|
|
12307
12507
|
const ages = tasks.map((t) => ageMs(t, now));
|
|
12308
12508
|
const { start, visible } = centredVisibleSlice(tasks, safeCursor, viewport);
|
|
12509
|
+
const agentLookup = agentByName(snapshot);
|
|
12309
12510
|
const rows = visible.map((t, i) => {
|
|
12310
12511
|
const absoluteIndex = start + i;
|
|
12311
12512
|
return [
|
|
12312
12513
|
glyphFor3(),
|
|
12313
12514
|
t.name,
|
|
12314
12515
|
t.status,
|
|
12315
|
-
t.ownerName
|
|
12516
|
+
formatAgentRefDisplayName(t.ownerName, agentLookup),
|
|
12316
12517
|
formatSinceClaim(ages[absoluteIndex] ?? null),
|
|
12317
12518
|
formatRoi(t.impact, t.effortDays),
|
|
12318
12519
|
t.title
|
|
@@ -12372,6 +12573,7 @@ var init_inprogress2 = __esm({
|
|
|
12372
12573
|
"src/cli/tui/popups/inprogress.tsx"() {
|
|
12373
12574
|
"use strict";
|
|
12374
12575
|
init_format();
|
|
12576
|
+
init_agent_display();
|
|
12375
12577
|
init_inprogress();
|
|
12376
12578
|
init_columns();
|
|
12377
12579
|
init_format_helpers();
|
|
@@ -12717,7 +12919,13 @@ function ReadyPopup({
|
|
|
12717
12919
|
) }) });
|
|
12718
12920
|
}
|
|
12719
12921
|
const { start, visible } = centredVisibleSlice(tasks, safeCursor, viewport);
|
|
12720
|
-
const
|
|
12922
|
+
const agentLookup = agentByName(snapshot);
|
|
12923
|
+
const rows = visible.map((t) => [
|
|
12924
|
+
t.name,
|
|
12925
|
+
t.status,
|
|
12926
|
+
formatAgentRefDisplayName(t.ownerName, agentLookup),
|
|
12927
|
+
t.title
|
|
12928
|
+
]);
|
|
12721
12929
|
const widths = layoutColumns(rows, COLUMN_SPECS18, contentWidth);
|
|
12722
12930
|
return /* @__PURE__ */ jsxs19(
|
|
12723
12931
|
PopupShell,
|
|
@@ -12775,6 +12983,7 @@ var init_ready2 = __esm({
|
|
|
12775
12983
|
"src/cli/tui/popups/ready.tsx"() {
|
|
12776
12984
|
"use strict";
|
|
12777
12985
|
init_format();
|
|
12986
|
+
init_agent_display();
|
|
12778
12987
|
init_columns();
|
|
12779
12988
|
init_keys();
|
|
12780
12989
|
init_list_row();
|
|
@@ -13587,9 +13796,10 @@ function WorkspacesPopup({
|
|
|
13587
13796
|
] });
|
|
13588
13797
|
}
|
|
13589
13798
|
const { start, visible } = centredVisibleSlice(workspaces, safeCursor, viewport);
|
|
13799
|
+
const agentLookup = agentByName(snapshot);
|
|
13590
13800
|
const rows = visible.map((w) => [
|
|
13591
13801
|
glyphFor5(w),
|
|
13592
|
-
w.agentName,
|
|
13802
|
+
formatAgentRefDisplayName(w.agentName, agentLookup),
|
|
13593
13803
|
w.backend,
|
|
13594
13804
|
formatBehind2(w.commitsBehindMain),
|
|
13595
13805
|
formatDirty(w.dirty),
|
|
@@ -13730,6 +13940,7 @@ var init_workspaces2 = __esm({
|
|
|
13730
13940
|
"use strict";
|
|
13731
13941
|
init_vcs2();
|
|
13732
13942
|
init_workspace();
|
|
13943
|
+
init_agent_display();
|
|
13733
13944
|
init_workspaces();
|
|
13734
13945
|
init_columns();
|
|
13735
13946
|
init_keys();
|
|
@@ -14189,22 +14400,25 @@ import { jsx as jsx35, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
|
14189
14400
|
function TabStrip({
|
|
14190
14401
|
workstreams,
|
|
14191
14402
|
active,
|
|
14192
|
-
terminalColumns
|
|
14403
|
+
terminalColumns,
|
|
14404
|
+
parked
|
|
14193
14405
|
}) {
|
|
14194
14406
|
if (workstreams.length <= 1) return null;
|
|
14195
14407
|
const layout = layoutTabStrip(workstreams, active, terminalColumns);
|
|
14196
14408
|
if (layout === null) return null;
|
|
14409
|
+
const isParked = (name) => parked?.has(name) ?? false;
|
|
14410
|
+
const decorate = (name) => isParked(name) ? `~${name}` : name;
|
|
14197
14411
|
const tabs = [];
|
|
14198
14412
|
for (let i = 0; i < layout.visible.length; i++) {
|
|
14199
14413
|
const tab = layout.visible[i];
|
|
14200
14414
|
if (tab === void 0) continue;
|
|
14201
14415
|
if (tab.isActive) {
|
|
14202
14416
|
tabs.push(
|
|
14203
|
-
/* @__PURE__ */ jsx35(Text28, { bold: true, color: "cyan", children: `\u25B8 ${tab.name}` }, `t-${i}`)
|
|
14417
|
+
/* @__PURE__ */ jsx35(Text28, { bold: true, color: "cyan", children: `\u25B8 ${decorate(tab.name)}` }, `t-${i}`)
|
|
14204
14418
|
);
|
|
14205
14419
|
} else {
|
|
14206
14420
|
tabs.push(
|
|
14207
|
-
/* @__PURE__ */ jsx35(Text28, { dimColor: true, children: tab.name }, `t-${i}`)
|
|
14421
|
+
/* @__PURE__ */ jsx35(Text28, { dimColor: true, children: decorate(tab.name) }, `t-${i}`)
|
|
14208
14422
|
);
|
|
14209
14423
|
}
|
|
14210
14424
|
if (i < layout.visible.length - 1) {
|
|
@@ -14320,7 +14534,7 @@ var init_yank = __esm({
|
|
|
14320
14534
|
|
|
14321
14535
|
// src/cli/tui/app.tsx
|
|
14322
14536
|
import { Box as Box22, Text as Text29, useApp, useInput as useInput14, useStdin, useStdout as useStdout16 } from "ink";
|
|
14323
|
-
import { useCallback as useCallback7, useEffect as useEffect10, useRef as useRef8, useState as useState18 } from "react";
|
|
14537
|
+
import { useCallback as useCallback7, useEffect as useEffect10, useMemo as useMemo9, useRef as useRef8, useState as useState18 } from "react";
|
|
14324
14538
|
import { jsx as jsx36, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
14325
14539
|
function dashboardAvailableRows(rows, opts) {
|
|
14326
14540
|
const tabRows = opts.hasTabStrip ? 1 : 0;
|
|
@@ -14379,6 +14593,14 @@ function App({ db, workstreams, initialActive = 0 }) {
|
|
|
14379
14593
|
const safeActive = Math.max(0, Math.min(activeWs, workstreams.length - 1));
|
|
14380
14594
|
const workstream = workstreams[safeActive] ?? "";
|
|
14381
14595
|
const snap = useDashboardSnapshot(db, workstream, tickMs, true, refreshNonce);
|
|
14596
|
+
const parkedSet = useMemo9(() => {
|
|
14597
|
+
void snap.slowTickNonce;
|
|
14598
|
+
const set = /* @__PURE__ */ new Set();
|
|
14599
|
+
for (const ws of workstreams) {
|
|
14600
|
+
if (parkedStatus(db, ws).parked) set.add(ws);
|
|
14601
|
+
}
|
|
14602
|
+
return set;
|
|
14603
|
+
}, [db, workstreams, snap.slowTickNonce]);
|
|
14382
14604
|
const { stdout } = useStdout16();
|
|
14383
14605
|
const cols = stdout.columns ?? 80;
|
|
14384
14606
|
const rows = stdout.rows ?? 24;
|
|
@@ -14558,7 +14780,15 @@ function App({ db, workstreams, initialActive = 0 }) {
|
|
|
14558
14780
|
] });
|
|
14559
14781
|
}
|
|
14560
14782
|
return /* @__PURE__ */ jsxs25(Box22, { flexDirection: "column", height: rows, overflow: "hidden", children: [
|
|
14561
|
-
/* @__PURE__ */ jsx36(
|
|
14783
|
+
/* @__PURE__ */ jsx36(
|
|
14784
|
+
TabStrip,
|
|
14785
|
+
{
|
|
14786
|
+
workstreams,
|
|
14787
|
+
active: safeActive,
|
|
14788
|
+
terminalColumns: cols,
|
|
14789
|
+
parked: parkedSet
|
|
14790
|
+
}
|
|
14791
|
+
),
|
|
14562
14792
|
hasSnapshotError && /* @__PURE__ */ jsx36(Box22, { borderStyle: "round", borderColor: "red", paddingX: 1, children: /* @__PURE__ */ jsxs25(Text29, { color: "red", children: [
|
|
14563
14793
|
"snapshot error: ",
|
|
14564
14794
|
snap.error
|
|
@@ -14615,7 +14845,8 @@ function buildDashboardLayoutModel(cols, rows, visibility, snapshot) {
|
|
|
14615
14845
|
const cullBudget = firstCull.hidden.length > 0 ? Math.max(1, rows - 1) : rows;
|
|
14616
14846
|
const culled = cullBudget === rows ? firstCull : cullCardsForRows(visible, cullBudget);
|
|
14617
14847
|
const cardsRows = culled.hidden.length > 0 ? Math.max(1, rows - 1) : rows;
|
|
14618
|
-
const
|
|
14848
|
+
const packed = layoutColumns2(cols, culled.cards);
|
|
14849
|
+
const assignments = balanceColumns(packed, (id) => dataCountForCard(id, snapshot));
|
|
14619
14850
|
const widths = columnWidths(cols, assignments.length);
|
|
14620
14851
|
const budgetsByColumn = assignments.map(
|
|
14621
14852
|
(assignment) => capRowBudgetsForColumn(
|
|
@@ -14693,6 +14924,7 @@ var CARD_REGISTRY, POPUP_REGISTRY, DASHBOARD_MIN_ROWS, POPUP_CHROME_TOP;
|
|
|
14693
14924
|
var init_app = __esm({
|
|
14694
14925
|
"src/cli/tui/app.tsx"() {
|
|
14695
14926
|
"use strict";
|
|
14927
|
+
init_parked();
|
|
14696
14928
|
init_agents2();
|
|
14697
14929
|
init_blocked();
|
|
14698
14930
|
init_commits();
|
|
@@ -16394,10 +16626,10 @@ async function cmdAttach(db, rawName, opts) {
|
|
|
16394
16626
|
const { name } = await resolveEntityRef(db, rawName, opts, "agent");
|
|
16395
16627
|
const workstream = await resolveWorkstream(opts.workstream);
|
|
16396
16628
|
const sessionName = `mu-${workstream}`;
|
|
16629
|
+
const view = await listLiveAgents(db, { workstream });
|
|
16397
16630
|
if (!await sessionExists(sessionName)) {
|
|
16398
16631
|
throw new UsageError(`workstream "${workstream}" has no tmux session yet`);
|
|
16399
16632
|
}
|
|
16400
|
-
const view = await listLiveAgents(db, { workstream, mode: "status-only" });
|
|
16401
16633
|
const agent = view.agents.find((a) => a.name === name);
|
|
16402
16634
|
if (!agent) {
|
|
16403
16635
|
throw new AgentNotFoundError(name);
|
|
@@ -17330,6 +17562,10 @@ function exportDb(db, file, opts = {}) {
|
|
|
17330
17562
|
const manifestPath = `${target}.manifest.json`;
|
|
17331
17563
|
const targetExists = existsSync12(target);
|
|
17332
17564
|
if (targetExists && opts.force !== true) throw new DbExportTargetExistsError(target);
|
|
17565
|
+
const preEventManifest = buildExportManifest(db);
|
|
17566
|
+
for (const ws of preEventManifest.workstreams) {
|
|
17567
|
+
emitEvent(db, ws.name, `db export ${ws.name} seq=${ws.latestSeq}`);
|
|
17568
|
+
}
|
|
17333
17569
|
const manifest = buildExportManifest(db);
|
|
17334
17570
|
mkdirSync4(dirname5(target), { recursive: true });
|
|
17335
17571
|
try {
|
|
@@ -18096,7 +18332,10 @@ state (workstream=${ws})`));
|
|
|
18096
18332
|
console.log(` agent_logs rows : ${counts.logs}`);
|
|
18097
18333
|
try {
|
|
18098
18334
|
const view = await listLiveAgents(db, { workstream: ws, mode: "report-only" });
|
|
18099
|
-
const
|
|
18335
|
+
const ghosts = view.report.prunedGhosts;
|
|
18336
|
+
const ghostNote = ghosts > 0 ? pc.yellow(
|
|
18337
|
+
`${ghosts} ghost pane${ghosts === 1 ? "" : "s"} would be reaped by \`mu state\` or \`mu agent list\``
|
|
18338
|
+
) : pc.green("none");
|
|
18100
18339
|
console.log(` ghosts : ${ghostNote}`);
|
|
18101
18340
|
const orphanColor = view.orphans.length > 0 ? pc.yellow : pc.green;
|
|
18102
18341
|
console.log(
|
|
@@ -18702,9 +18941,9 @@ async function cmdUndo(db, opts = {}) {
|
|
|
18702
18941
|
wouldBePrunedGhosts: totalGhostsWouldBePruned,
|
|
18703
18942
|
orphansSurfaced: totalOrphans,
|
|
18704
18943
|
// Reconcile mode: "report-only" preserves the snapshot's
|
|
18705
|
-
// restored rows verbatim. (Was `dryRun: true` before
|
|
18706
|
-
//
|
|
18707
|
-
//
|
|
18944
|
+
// restored rows verbatim. (Was `dryRun: true` before named
|
|
18945
|
+
// modes — BREAKING for SDK consumers reading this field; see
|
|
18946
|
+
// CHANGELOG.)
|
|
18708
18947
|
mode: "report-only",
|
|
18709
18948
|
perWorkstream: reconcilePerWorkstream
|
|
18710
18949
|
},
|