agenthud 0.13.3 → 0.14.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 +1 -1
- package/dist/{main-GMNV6O4E.js → main-S5INN7N3.js} +536 -136
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -179,7 +179,8 @@ function loadGlobalConfig() {
|
|
|
179
179
|
config.report.detailLimit = r.detailLimit;
|
|
180
180
|
}
|
|
181
181
|
if (typeof r.withGit === "boolean") config.report.withGit = r.withGit;
|
|
182
|
-
if (r.format === "markdown" || r.format === "json")
|
|
182
|
+
if (r.format === "markdown" || r.format === "json")
|
|
183
|
+
config.report.format = r.format;
|
|
183
184
|
}
|
|
184
185
|
if (configRaw.summary && typeof configRaw.summary === "object") {
|
|
185
186
|
const s = configRaw.summary;
|
|
@@ -194,7 +195,8 @@ function loadGlobalConfig() {
|
|
|
194
195
|
config.summary.detailLimit = s.detailLimit;
|
|
195
196
|
}
|
|
196
197
|
if (typeof s.withGit === "boolean") config.summary.withGit = s.withGit;
|
|
197
|
-
if (s.format === "markdown" || s.format === "json")
|
|
198
|
+
if (s.format === "markdown" || s.format === "json")
|
|
199
|
+
config.summary.format = s.format;
|
|
198
200
|
if (typeof s.model === "string" && s.model.trim().length > 0) {
|
|
199
201
|
config.summary.model = s.model.trim();
|
|
200
202
|
}
|
|
@@ -247,7 +249,7 @@ function loadGlobalConfig() {
|
|
|
247
249
|
return config;
|
|
248
250
|
}
|
|
249
251
|
function updateState(updates) {
|
|
250
|
-
|
|
252
|
+
const state = {
|
|
251
253
|
hiddenSessions: [],
|
|
252
254
|
hiddenSubAgents: [],
|
|
253
255
|
hiddenProjects: []
|
|
@@ -296,6 +298,27 @@ function hideProject(name) {
|
|
|
296
298
|
if (config.hiddenProjects.includes(name)) return;
|
|
297
299
|
updateState({ hiddenProjects: [...config.hiddenProjects, name] });
|
|
298
300
|
}
|
|
301
|
+
function unhideSession(id) {
|
|
302
|
+
const config = loadGlobalConfig();
|
|
303
|
+
if (!config.hiddenSessions.includes(id)) return;
|
|
304
|
+
updateState({
|
|
305
|
+
hiddenSessions: config.hiddenSessions.filter((s) => s !== id)
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
function unhideSubAgent(id) {
|
|
309
|
+
const config = loadGlobalConfig();
|
|
310
|
+
if (!config.hiddenSubAgents.includes(id)) return;
|
|
311
|
+
updateState({
|
|
312
|
+
hiddenSubAgents: config.hiddenSubAgents.filter((s) => s !== id)
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
function unhideProject(name) {
|
|
316
|
+
const config = loadGlobalConfig();
|
|
317
|
+
if (!config.hiddenProjects.includes(name)) return;
|
|
318
|
+
updateState({
|
|
319
|
+
hiddenProjects: config.hiddenProjects.filter((p) => p !== name)
|
|
320
|
+
});
|
|
321
|
+
}
|
|
299
322
|
function hasProjectLevelConfig() {
|
|
300
323
|
const candidate = join(process.cwd(), ".agenthud", "config.yaml");
|
|
301
324
|
if (candidate === join(homedir(), ".agenthud", "config.yaml")) return false;
|
|
@@ -978,7 +1001,11 @@ function buildToolDetailBody(name, input, result) {
|
|
|
978
1001
|
const content = result?.file?.content;
|
|
979
1002
|
if (content) {
|
|
980
1003
|
const start = result?.file?.startLine ?? input?.offset ?? 1;
|
|
981
|
-
return {
|
|
1004
|
+
return {
|
|
1005
|
+
text: numberLines(content, start),
|
|
1006
|
+
kind: "code",
|
|
1007
|
+
numbered: true
|
|
1008
|
+
};
|
|
982
1009
|
}
|
|
983
1010
|
}
|
|
984
1011
|
if (name === "Edit" || name === "Write") {
|
|
@@ -1470,6 +1497,15 @@ function readFirstUserPrompt(filePath) {
|
|
|
1470
1497
|
} catch {
|
|
1471
1498
|
return null;
|
|
1472
1499
|
}
|
|
1500
|
+
const MIN_SUBSTANTIAL_LEN = 10;
|
|
1501
|
+
const isSubstantial = (text) => {
|
|
1502
|
+
const trimmed = text.trim();
|
|
1503
|
+
if (trimmed.length < MIN_SUBSTANTIAL_LEN) return false;
|
|
1504
|
+
if (trimmed.startsWith("/")) return false;
|
|
1505
|
+
return true;
|
|
1506
|
+
};
|
|
1507
|
+
let first = null;
|
|
1508
|
+
let latestSubstantial = null;
|
|
1473
1509
|
for (const line of content.split("\n")) {
|
|
1474
1510
|
if (!line.trim()) continue;
|
|
1475
1511
|
let entry;
|
|
@@ -1495,9 +1531,11 @@ function readFirstUserPrompt(filePath) {
|
|
|
1495
1531
|
if (!text || isSystemNoise(text)) continue;
|
|
1496
1532
|
const firstLine = text.split("\n").find((l) => l.trim()) ?? "";
|
|
1497
1533
|
if (!firstLine || isSystemNoise(firstLine)) continue;
|
|
1498
|
-
|
|
1534
|
+
const capped = capWithEllipsis(firstLine);
|
|
1535
|
+
if (first === null) first = capped;
|
|
1536
|
+
if (isSubstantial(firstLine)) latestSubstantial = capped;
|
|
1499
1537
|
}
|
|
1500
|
-
return
|
|
1538
|
+
return latestSubstantial ?? first;
|
|
1501
1539
|
}
|
|
1502
1540
|
function readEntrypoint(filePath) {
|
|
1503
1541
|
if (!existsSync3(filePath)) return null;
|
|
@@ -1552,9 +1590,10 @@ function buildSubAgents(parentId, projectDir, config, projectName) {
|
|
|
1552
1590
|
} catch {
|
|
1553
1591
|
return null;
|
|
1554
1592
|
}
|
|
1555
|
-
}).filter(
|
|
1556
|
-
(
|
|
1557
|
-
|
|
1593
|
+
}).filter((n) => n !== null).map((n) => {
|
|
1594
|
+
if (config.hiddenSubAgents.includes(n.hideKey)) n.hidden = true;
|
|
1595
|
+
return n;
|
|
1596
|
+
}).sort((a, b) => b.lastModifiedMs - a.lastModifiedMs);
|
|
1558
1597
|
}
|
|
1559
1598
|
function findContainingProject(cwd, projectPaths, options2) {
|
|
1560
1599
|
const resolve2 = options2?.realpath ?? ((p) => p);
|
|
@@ -1592,7 +1631,8 @@ function discoverSessions(config, options2) {
|
|
|
1592
1631
|
projects: [],
|
|
1593
1632
|
coldProjects: [],
|
|
1594
1633
|
totalCount: 0,
|
|
1595
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1634
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1635
|
+
hiddenStats: { total: 0, active: 0 }
|
|
1596
1636
|
};
|
|
1597
1637
|
}
|
|
1598
1638
|
let projectDirs;
|
|
@@ -1609,7 +1649,8 @@ function discoverSessions(config, options2) {
|
|
|
1609
1649
|
projects: [],
|
|
1610
1650
|
coldProjects: [],
|
|
1611
1651
|
totalCount: 0,
|
|
1612
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1652
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1653
|
+
hiddenStats: { total: 0, active: 0 }
|
|
1613
1654
|
};
|
|
1614
1655
|
}
|
|
1615
1656
|
const allSessions = [];
|
|
@@ -1659,8 +1700,23 @@ function discoverSessions(config, options2) {
|
|
|
1659
1700
|
}
|
|
1660
1701
|
}
|
|
1661
1702
|
const byProject = /* @__PURE__ */ new Map();
|
|
1703
|
+
let hiddenTotal = 0;
|
|
1704
|
+
let hiddenActive = 0;
|
|
1662
1705
|
for (const s of allSessions) {
|
|
1663
|
-
|
|
1706
|
+
const sessionHidden = config.hiddenSessions.includes(s.hideKey);
|
|
1707
|
+
const projectHidden = config.hiddenProjects.includes(s.projectName);
|
|
1708
|
+
if (sessionHidden || projectHidden) {
|
|
1709
|
+
hiddenTotal++;
|
|
1710
|
+
if (s.status === "hot" || s.status === "warm") hiddenActive++;
|
|
1711
|
+
s.hidden = true;
|
|
1712
|
+
}
|
|
1713
|
+
for (const sub of s.subAgents) {
|
|
1714
|
+
if (sub.hidden || projectHidden) {
|
|
1715
|
+
if (projectHidden) sub.hidden = true;
|
|
1716
|
+
hiddenTotal++;
|
|
1717
|
+
if (sub.status === "hot" || sub.status === "warm") hiddenActive++;
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1664
1720
|
const arr = byProject.get(s.projectPath) ?? [];
|
|
1665
1721
|
arr.push(s);
|
|
1666
1722
|
byProject.set(s.projectPath, arr);
|
|
@@ -1675,7 +1731,7 @@ function discoverSessions(config, options2) {
|
|
|
1675
1731
|
for (const [projectPath, sessions] of byProject) {
|
|
1676
1732
|
if (sessions.length === 0) continue;
|
|
1677
1733
|
const projectName = sessions[0].projectName;
|
|
1678
|
-
|
|
1734
|
+
const projectHidden = config.hiddenProjects.includes(projectName);
|
|
1679
1735
|
sessions.sort((a, b) => {
|
|
1680
1736
|
if (a.nonInteractive !== b.nonInteractive) {
|
|
1681
1737
|
return a.nonInteractive ? 1 : -1;
|
|
@@ -1685,7 +1741,13 @@ function discoverSessions(config, options2) {
|
|
|
1685
1741
|
return b.lastModifiedMs - a.lastModifiedMs;
|
|
1686
1742
|
});
|
|
1687
1743
|
const hotness = sessions[0].status;
|
|
1688
|
-
allProjects.push({
|
|
1744
|
+
allProjects.push({
|
|
1745
|
+
name: projectName,
|
|
1746
|
+
projectPath,
|
|
1747
|
+
sessions,
|
|
1748
|
+
hotness,
|
|
1749
|
+
hidden: projectHidden || void 0
|
|
1750
|
+
});
|
|
1689
1751
|
}
|
|
1690
1752
|
const activeProjects = allProjects.filter((p) => p.hotness !== "cold");
|
|
1691
1753
|
const coldProjects = allProjects.filter((p) => p.hotness === "cold");
|
|
@@ -1703,7 +1765,8 @@ function discoverSessions(config, options2) {
|
|
|
1703
1765
|
projects: activeProjects,
|
|
1704
1766
|
coldProjects,
|
|
1705
1767
|
totalCount,
|
|
1706
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1768
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1769
|
+
hiddenStats: { total: hiddenTotal, active: hiddenActive }
|
|
1707
1770
|
};
|
|
1708
1771
|
}
|
|
1709
1772
|
|
|
@@ -1842,6 +1905,27 @@ async function openInDefaultApp(path) {
|
|
|
1842
1905
|
});
|
|
1843
1906
|
}
|
|
1844
1907
|
|
|
1908
|
+
// src/utils/stderrTicker.ts
|
|
1909
|
+
function formatTickerLine(label, elapsedSeconds, dots) {
|
|
1910
|
+
const tail = ".".repeat(dots % 4).padEnd(3, " ");
|
|
1911
|
+
return `${label}${tail} ${elapsedSeconds}s`;
|
|
1912
|
+
}
|
|
1913
|
+
function startStderrTicker(label, options2 = {}) {
|
|
1914
|
+
const intervalMs = options2.intervalMs ?? 500;
|
|
1915
|
+
const stream = options2.stream ?? process.stderr;
|
|
1916
|
+
const start = Date.now();
|
|
1917
|
+
let ticks = 0;
|
|
1918
|
+
const id = setInterval(() => {
|
|
1919
|
+
ticks++;
|
|
1920
|
+
const elapsed = Math.floor((Date.now() - start) / 1e3);
|
|
1921
|
+
stream.write(`\r${formatTickerLine(label, elapsed, ticks)}`);
|
|
1922
|
+
}, intervalMs);
|
|
1923
|
+
return function stop() {
|
|
1924
|
+
clearInterval(id);
|
|
1925
|
+
if (ticks > 0) stream.write("\r\x1B[K");
|
|
1926
|
+
};
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1845
1929
|
// src/data/summariesIndex.ts
|
|
1846
1930
|
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "fs";
|
|
1847
1931
|
import { join as join5 } from "path";
|
|
@@ -2001,15 +2085,11 @@ function buildHeaderBlock(currentFilename, entries) {
|
|
|
2001
2085
|
const idx = dailies.findIndex((e) => e.filename === currentFilename);
|
|
2002
2086
|
if (idx > 0) {
|
|
2003
2087
|
const prev = dailies[idx - 1];
|
|
2004
|
-
parts.push(
|
|
2005
|
-
`[\u2190 ${formatDateLabel(prev.date)}](./${prev.filename})`
|
|
2006
|
-
);
|
|
2088
|
+
parts.push(`[\u2190 ${formatDateLabel(prev.date)}](./${prev.filename})`);
|
|
2007
2089
|
}
|
|
2008
2090
|
if (idx >= 0 && idx < dailies.length - 1) {
|
|
2009
2091
|
const next = dailies[idx + 1];
|
|
2010
|
-
parts.push(
|
|
2011
|
-
`[${formatDateLabel(next.date)} \u2192](./${next.filename})`
|
|
2012
|
-
);
|
|
2092
|
+
parts.push(`[${formatDateLabel(next.date)} \u2192](./${next.filename})`);
|
|
2013
2093
|
}
|
|
2014
2094
|
}
|
|
2015
2095
|
const backlinkLine = parts.join(" \xB7 ");
|
|
@@ -2066,11 +2146,7 @@ function regenerateIndex(summariesDir2) {
|
|
|
2066
2146
|
}
|
|
2067
2147
|
const indexPath = join5(summariesDir2, "index.md");
|
|
2068
2148
|
try {
|
|
2069
|
-
writeFileSync2(
|
|
2070
|
-
indexPath,
|
|
2071
|
-
buildIndexMarkdown(entries, snippets),
|
|
2072
|
-
"utf-8"
|
|
2073
|
-
);
|
|
2149
|
+
writeFileSync2(indexPath, buildIndexMarkdown(entries, snippets), "utf-8");
|
|
2074
2150
|
} catch (err) {
|
|
2075
2151
|
process.stderr.write(
|
|
2076
2152
|
`warning: cannot write summaries index (${err.message})
|
|
@@ -2079,27 +2155,6 @@ function regenerateIndex(summariesDir2) {
|
|
|
2079
2155
|
}
|
|
2080
2156
|
}
|
|
2081
2157
|
|
|
2082
|
-
// src/utils/stderrTicker.ts
|
|
2083
|
-
function formatTickerLine(label, elapsedSeconds, dots) {
|
|
2084
|
-
const tail = ".".repeat(dots % 4).padEnd(3, " ");
|
|
2085
|
-
return `${label}${tail} ${elapsedSeconds}s`;
|
|
2086
|
-
}
|
|
2087
|
-
function startStderrTicker(label, options2 = {}) {
|
|
2088
|
-
const intervalMs = options2.intervalMs ?? 500;
|
|
2089
|
-
const stream = options2.stream ?? process.stderr;
|
|
2090
|
-
const start = Date.now();
|
|
2091
|
-
let ticks = 0;
|
|
2092
|
-
const id = setInterval(() => {
|
|
2093
|
-
ticks++;
|
|
2094
|
-
const elapsed = Math.floor((Date.now() - start) / 1e3);
|
|
2095
|
-
stream.write(`\r${formatTickerLine(label, elapsed, ticks)}`);
|
|
2096
|
-
}, intervalMs);
|
|
2097
|
-
return function stop() {
|
|
2098
|
-
clearInterval(id);
|
|
2099
|
-
if (ticks > 0) stream.write("\r\x1B[K");
|
|
2100
|
-
};
|
|
2101
|
-
}
|
|
2102
|
-
|
|
2103
2158
|
// src/data/summaryRunner.ts
|
|
2104
2159
|
function agenthudHomeDir() {
|
|
2105
2160
|
const dir = join6(homedir3(), ".agenthud");
|
|
@@ -2227,14 +2282,10 @@ function spawnClaude(opts) {
|
|
|
2227
2282
|
];
|
|
2228
2283
|
if (opts.model) args.push("--model", opts.model);
|
|
2229
2284
|
args.push(opts.prompt);
|
|
2230
|
-
const proc = spawn2(
|
|
2231
|
-
"
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
2235
|
-
cwd: agenthudHomeDir()
|
|
2236
|
-
}
|
|
2237
|
-
);
|
|
2285
|
+
const proc = spawn2("claude", args, {
|
|
2286
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
2287
|
+
cwd: agenthudHomeDir()
|
|
2288
|
+
});
|
|
2238
2289
|
let cacheStream = null;
|
|
2239
2290
|
if (opts.cachePath) {
|
|
2240
2291
|
cacheStream = createWriteStream(opts.cachePath, { encoding: "utf-8" });
|
|
@@ -2534,10 +2585,8 @@ async function runRangeSummary(options2) {
|
|
|
2534
2585
|
)) {
|
|
2535
2586
|
try {
|
|
2536
2587
|
const content = readFileSync7(rangeCache, "utf-8");
|
|
2537
|
-
process.stderr.write(
|
|
2538
|
-
|
|
2539
|
-
`
|
|
2540
|
-
);
|
|
2588
|
+
process.stderr.write(`cached range summary from ${rangeCache}
|
|
2589
|
+
`);
|
|
2541
2590
|
if (!options2.open) {
|
|
2542
2591
|
process.stdout.write(content);
|
|
2543
2592
|
if (!content.endsWith("\n")) process.stdout.write("\n");
|
|
@@ -2565,10 +2614,8 @@ async function runRangeSummary(options2) {
|
|
|
2565
2614
|
`range ${fromLabel} \u2192 ${toLabel} (${dates.length} days)
|
|
2566
2615
|
`
|
|
2567
2616
|
);
|
|
2568
|
-
process.stderr.write(
|
|
2569
|
-
|
|
2570
|
-
`
|
|
2571
|
-
);
|
|
2617
|
+
process.stderr.write(`${cachedCount} cached, ${missingCount} to generate
|
|
2618
|
+
`);
|
|
2572
2619
|
const dailyMarkdowns = [];
|
|
2573
2620
|
let skippedCount = 0;
|
|
2574
2621
|
for (const d of dates) {
|
|
@@ -3106,12 +3153,19 @@ var SECTIONS = [
|
|
|
3106
3153
|
title: "Project tree",
|
|
3107
3154
|
rows: [
|
|
3108
3155
|
["\u2191 \u2193 / k j", "Move selection"],
|
|
3109
|
-
["\u2190", "Jump to parent (sub-agent \u2192 session, session \u2192 project)"],
|
|
3156
|
+
["\u2190 / h", "Jump to parent (sub-agent \u2192 session, session \u2192 project)"],
|
|
3110
3157
|
["PgUp / Ctrl+B", "Page up"],
|
|
3111
3158
|
["PgDn / Ctrl+F", "Page down"],
|
|
3112
3159
|
["\u21B5", "Expand/collapse project, session, or summary"],
|
|
3113
|
-
[
|
|
3114
|
-
|
|
3160
|
+
[
|
|
3161
|
+
"H",
|
|
3162
|
+
"Toggle hide on selected \u2014 hides if visible, unhides if hidden (Shift+H)"
|
|
3163
|
+
],
|
|
3164
|
+
["a", "Toggle show hidden items in the tree (dim \u2298 marker)"],
|
|
3165
|
+
[
|
|
3166
|
+
"t",
|
|
3167
|
+
"Track: auto-follow the newest live sub-agent (any nav key turns it off)"
|
|
3168
|
+
],
|
|
3115
3169
|
["Tab", "Switch focus to activity viewer"],
|
|
3116
3170
|
["r", "Refresh now"]
|
|
3117
3171
|
]
|
|
@@ -3257,6 +3311,7 @@ function useHotkeys({
|
|
|
3257
3311
|
onHelpScrollToTop,
|
|
3258
3312
|
onToggleTracking,
|
|
3259
3313
|
onJumpToParent,
|
|
3314
|
+
onToggleShowHidden,
|
|
3260
3315
|
filterLabel,
|
|
3261
3316
|
trackingOn = false
|
|
3262
3317
|
}) {
|
|
@@ -3341,7 +3396,7 @@ function useHotkeys({
|
|
|
3341
3396
|
onFilter();
|
|
3342
3397
|
return;
|
|
3343
3398
|
}
|
|
3344
|
-
if (input === "t" && !key.ctrl && onToggleTracking) {
|
|
3399
|
+
if (input === "t" && !key.ctrl && focus === "tree" && onToggleTracking) {
|
|
3345
3400
|
onToggleTracking();
|
|
3346
3401
|
return;
|
|
3347
3402
|
}
|
|
@@ -3372,11 +3427,15 @@ function useHotkeys({
|
|
|
3372
3427
|
}
|
|
3373
3428
|
}
|
|
3374
3429
|
if (focus === "tree") {
|
|
3375
|
-
if (input === "
|
|
3430
|
+
if (input === "H") {
|
|
3376
3431
|
onHide();
|
|
3377
3432
|
return;
|
|
3378
3433
|
}
|
|
3379
|
-
if (
|
|
3434
|
+
if (input === "a" && onToggleShowHidden) {
|
|
3435
|
+
onToggleShowHidden();
|
|
3436
|
+
return;
|
|
3437
|
+
}
|
|
3438
|
+
if ((input === "h" || key.leftArrow) && onJumpToParent) {
|
|
3380
3439
|
onJumpToParent();
|
|
3381
3440
|
return;
|
|
3382
3441
|
}
|
|
@@ -3408,16 +3467,16 @@ function useHotkeys({
|
|
|
3408
3467
|
}
|
|
3409
3468
|
}
|
|
3410
3469
|
};
|
|
3411
|
-
const trackingItems = trackingOn ? ["TRK \u25CF"] : [
|
|
3470
|
+
const trackingItems = trackingOn ? ["TRK \u25CF"] : [];
|
|
3412
3471
|
const statusBarItems = helpMode ? ["\u2191\u2193/jk: scroll", "PgDn/Space: page", "\u21B5/Esc/q/?: close"] : detailMode ? ["\u2191\u2193/jk: scroll", "C-u/d: \xBDpage", "\u21B5/Esc: close", "?: help"] : focus === "tree" ? [
|
|
3413
3472
|
...trackingItems,
|
|
3414
3473
|
"Tab: viewer",
|
|
3415
3474
|
"\u2191\u2193/jk: select",
|
|
3416
|
-
"
|
|
3475
|
+
"h/\u2190: parent",
|
|
3417
3476
|
"PgUp/Dn: page",
|
|
3418
3477
|
"\u21B5: expand",
|
|
3419
|
-
"
|
|
3420
|
-
"
|
|
3478
|
+
"H: hide",
|
|
3479
|
+
"a: show hidden",
|
|
3421
3480
|
"?: help",
|
|
3422
3481
|
"q: quit"
|
|
3423
3482
|
] : [
|
|
@@ -3525,7 +3584,8 @@ function SessionRow({
|
|
|
3525
3584
|
const rightParts = [elapsed];
|
|
3526
3585
|
if (model) rightParts.push(model);
|
|
3527
3586
|
const rightSide = rightParts.join(" ");
|
|
3528
|
-
const
|
|
3587
|
+
const hiddenMarker = session.hidden ? "\u2298 " : "";
|
|
3588
|
+
const leftCoreBase = `${prefix}${rawName}${shortIdDisplay} ${hiddenMarker}${badge}`;
|
|
3529
3589
|
const leftCoreWidth = getDisplayWidth(leftCoreBase);
|
|
3530
3590
|
const rightWidth = getDisplayWidth(rightSide);
|
|
3531
3591
|
const RIGHT_GAP = 3;
|
|
@@ -3550,7 +3610,7 @@ function SessionRow({
|
|
|
3550
3610
|
const focused = isSelected && hasFocus;
|
|
3551
3611
|
const muted = isSelected && !hasFocus;
|
|
3552
3612
|
const showBg = focused || muted;
|
|
3553
|
-
const shouldDim = isNonInteractive || muted;
|
|
3613
|
+
const shouldDim = isNonInteractive || muted || !!session.hidden || session.status === "cold";
|
|
3554
3614
|
return /* @__PURE__ */ jsxs4(Text4, { children: [
|
|
3555
3615
|
BOX.v,
|
|
3556
3616
|
" ",
|
|
@@ -3565,6 +3625,7 @@ function SessionRow({
|
|
|
3565
3625
|
/* @__PURE__ */ jsx4(Text4, { bold: !shouldDim, children: rawName }),
|
|
3566
3626
|
shortIdDisplay ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: shortIdDisplay }) : null,
|
|
3567
3627
|
/* @__PURE__ */ jsx4(Text4, { children: " " }),
|
|
3628
|
+
session.hidden ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "\u2298 " }) : null,
|
|
3568
3629
|
/* @__PURE__ */ jsx4(Text4, { color: badgeColor, children: badge }),
|
|
3569
3630
|
middleText ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: middleSection }) : null,
|
|
3570
3631
|
/* @__PURE__ */ jsx4(Text4, { children: gap }),
|
|
@@ -3626,10 +3687,28 @@ function flattenSessions(projects, coldProjects, expandedIds) {
|
|
|
3626
3687
|
result.push({ kind: "project", project, sentinelId });
|
|
3627
3688
|
const collapsed = expandedIds.has(`__collapsed-${sentinelId}`);
|
|
3628
3689
|
if (!collapsed) {
|
|
3629
|
-
|
|
3690
|
+
const activeSessions = project.sessions.filter(
|
|
3691
|
+
(s) => s.status !== "cold"
|
|
3692
|
+
);
|
|
3693
|
+
const coldSessions = project.sessions.filter((s) => s.status === "cold");
|
|
3694
|
+
for (const session of activeSessions) {
|
|
3630
3695
|
result.push({ kind: "session", session, prefix: " " });
|
|
3631
3696
|
appendSessionRows(result, session, expandedIds);
|
|
3632
3697
|
}
|
|
3698
|
+
if (coldSessions.length > 0) {
|
|
3699
|
+
const coldKey = `__cold-sessions-${project.name}__`;
|
|
3700
|
+
result.push({
|
|
3701
|
+
kind: "cold-sessions-summary",
|
|
3702
|
+
projectName: project.name,
|
|
3703
|
+
count: coldSessions.length
|
|
3704
|
+
});
|
|
3705
|
+
if (expandedIds.has(coldKey)) {
|
|
3706
|
+
for (const session of coldSessions) {
|
|
3707
|
+
result.push({ kind: "session", session, prefix: " " });
|
|
3708
|
+
appendSessionRows(result, session, expandedIds);
|
|
3709
|
+
}
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3633
3712
|
}
|
|
3634
3713
|
}
|
|
3635
3714
|
if (coldProjects.length > 0) {
|
|
@@ -3656,28 +3735,61 @@ function ProjectRow({
|
|
|
3656
3735
|
hasFocus,
|
|
3657
3736
|
contentWidth
|
|
3658
3737
|
}) {
|
|
3659
|
-
const nameText = `> ${project.name}`;
|
|
3738
|
+
const nameText = `> ${project.hidden ? "\u2298 " : ""}${project.name}`;
|
|
3660
3739
|
const pathText = project.projectPath ? formatProjectPath(project.projectPath) : "";
|
|
3661
3740
|
const latestMtime = project.sessions.reduce(
|
|
3662
3741
|
(acc, s) => Math.max(acc, s.lastModifiedMs),
|
|
3663
3742
|
0
|
|
3664
3743
|
);
|
|
3665
3744
|
const elapsed = latestMtime > 0 ? formatElapsed(latestMtime) : "";
|
|
3745
|
+
const sessionCount = project.sessions.length;
|
|
3746
|
+
const subAgentCount = project.sessions.reduce(
|
|
3747
|
+
(n, s) => n + s.subAgents.length,
|
|
3748
|
+
0
|
|
3749
|
+
);
|
|
3750
|
+
const isActive = (status) => status === "hot" || status === "warm";
|
|
3751
|
+
const activeSessionCount = project.sessions.filter(
|
|
3752
|
+
(s) => isActive(s.status)
|
|
3753
|
+
).length;
|
|
3754
|
+
const activeSubAgentCount = project.sessions.reduce(
|
|
3755
|
+
(n, s) => n + s.subAgents.filter((sa) => isActive(sa.status)).length,
|
|
3756
|
+
0
|
|
3757
|
+
);
|
|
3758
|
+
const buildCountsSegments = (short) => [
|
|
3759
|
+
{
|
|
3760
|
+
text: short ? `${sessionCount}s` : `${sessionCount} sessions`,
|
|
3761
|
+
dim: true
|
|
3762
|
+
},
|
|
3763
|
+
...activeParen(activeSessionCount, "green", { bold: false }),
|
|
3764
|
+
{
|
|
3765
|
+
text: short ? ` \xB7 ${subAgentCount}a` : ` \xB7 ${subAgentCount} sub-agents`,
|
|
3766
|
+
dim: true
|
|
3767
|
+
},
|
|
3768
|
+
...activeParen(activeSubAgentCount, "green", { bold: false })
|
|
3769
|
+
];
|
|
3770
|
+
const longSegs = buildCountsSegments(false);
|
|
3771
|
+
const shortSegs = buildCountsSegments(true);
|
|
3772
|
+
const segWidth = (segs) => segs.reduce((n, s) => n + getDisplayWidth(s.text), 0);
|
|
3666
3773
|
const nameWidth = getDisplayWidth(nameText);
|
|
3667
3774
|
const pathWidth = pathText ? getDisplayWidth(pathText) : 0;
|
|
3668
3775
|
const elapsedWidth = elapsed ? getDisplayWidth(elapsed) : 0;
|
|
3669
3776
|
const middleGap = pathText ? 2 : 0;
|
|
3670
3777
|
const leftWidth = nameWidth + middleGap + pathWidth;
|
|
3671
3778
|
const PROJECT_RIGHT_GAP = 3;
|
|
3779
|
+
const COUNTS_GAP = 2;
|
|
3780
|
+
const fitsCounts = (segs) => leftWidth + COUNTS_GAP + segWidth(segs) + PROJECT_RIGHT_GAP + elapsedWidth <= contentWidth;
|
|
3781
|
+
const countsSegs = fitsCounts(longSegs) ? longSegs : fitsCounts(shortSegs) ? shortSegs : null;
|
|
3782
|
+
const countsWidth = countsSegs ? COUNTS_GAP + segWidth(countsSegs) : 0;
|
|
3672
3783
|
const rightGap = Math.max(
|
|
3673
3784
|
PROJECT_RIGHT_GAP,
|
|
3674
|
-
contentWidth - leftWidth - elapsedWidth
|
|
3785
|
+
contentWidth - leftWidth - countsWidth - elapsedWidth
|
|
3675
3786
|
);
|
|
3676
|
-
const totalWidth = leftWidth + rightGap + elapsedWidth;
|
|
3787
|
+
const totalWidth = leftWidth + countsWidth + rightGap + elapsedWidth;
|
|
3677
3788
|
const padding = Math.max(0, contentWidth - totalWidth);
|
|
3678
3789
|
const focused = isSelected && hasFocus;
|
|
3679
3790
|
const muted = isSelected && !hasFocus;
|
|
3680
3791
|
const showBg = focused || muted;
|
|
3792
|
+
const dim = muted || !!project.hidden;
|
|
3681
3793
|
return /* @__PURE__ */ jsxs4(Text4, { children: [
|
|
3682
3794
|
BOX.v,
|
|
3683
3795
|
" ",
|
|
@@ -3685,14 +3797,27 @@ function ProjectRow({
|
|
|
3685
3797
|
Text4,
|
|
3686
3798
|
{
|
|
3687
3799
|
backgroundColor: showBg ? "blue" : void 0,
|
|
3688
|
-
bold: !showBg,
|
|
3689
|
-
dimColor:
|
|
3800
|
+
bold: !showBg && !project.hidden,
|
|
3801
|
+
dimColor: dim,
|
|
3690
3802
|
children: [
|
|
3691
3803
|
nameText,
|
|
3692
3804
|
pathText ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
3693
3805
|
" ",
|
|
3694
3806
|
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: pathText })
|
|
3695
3807
|
] }) : null,
|
|
3808
|
+
countsSegs ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
3809
|
+
" ".repeat(COUNTS_GAP),
|
|
3810
|
+
countsSegs.map((seg, i) => /* @__PURE__ */ jsx4(
|
|
3811
|
+
Text4,
|
|
3812
|
+
{
|
|
3813
|
+
color: seg.color,
|
|
3814
|
+
dimColor: seg.dim,
|
|
3815
|
+
bold: seg.bold,
|
|
3816
|
+
children: seg.text
|
|
3817
|
+
},
|
|
3818
|
+
`pcnt-${i}`
|
|
3819
|
+
))
|
|
3820
|
+
] }) : null,
|
|
3696
3821
|
" ".repeat(rightGap),
|
|
3697
3822
|
elapsed ? /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: elapsed }) : null,
|
|
3698
3823
|
" ".repeat(padding)
|
|
@@ -3712,21 +3837,19 @@ function SubagentSummaryRow({
|
|
|
3712
3837
|
const parts = [];
|
|
3713
3838
|
if (coolCount > 0) parts.push(`${coolCount} cool`);
|
|
3714
3839
|
if (coldCount > 0) parts.push(`${coldCount} cold`);
|
|
3840
|
+
const baseText = ` \u2514\u2500 ... ${parts.join(" ")}`;
|
|
3715
3841
|
const hint = " +";
|
|
3716
|
-
const text = ` \u2514\u2500 ... ${parts.join(" ")}`;
|
|
3717
|
-
const padding = Math.max(
|
|
3718
|
-
0,
|
|
3719
|
-
contentWidth - getDisplayWidth(text) - getDisplayWidth(hint)
|
|
3720
|
-
);
|
|
3721
3842
|
const focused = isSelected && hasFocus;
|
|
3722
3843
|
const muted = isSelected && !hasFocus;
|
|
3844
|
+
const showHint = focused && getDisplayWidth(baseText) + getDisplayWidth(hint) <= contentWidth;
|
|
3845
|
+
const text = showHint ? `${baseText}${hint}` : baseText;
|
|
3846
|
+
const padding = Math.max(0, contentWidth - getDisplayWidth(text));
|
|
3723
3847
|
return /* @__PURE__ */ jsxs4(Text4, { children: [
|
|
3724
3848
|
BOX.v,
|
|
3725
3849
|
" ",
|
|
3726
3850
|
/* @__PURE__ */ jsxs4(Text4, { dimColor: !focused, inverse: focused || muted, children: [
|
|
3727
3851
|
text,
|
|
3728
|
-
" ".repeat(padding)
|
|
3729
|
-
hint
|
|
3852
|
+
" ".repeat(padding)
|
|
3730
3853
|
] }),
|
|
3731
3854
|
BOX.v
|
|
3732
3855
|
] });
|
|
@@ -3757,6 +3880,121 @@ function ColdProjectsSummaryRow({
|
|
|
3757
3880
|
}
|
|
3758
3881
|
);
|
|
3759
3882
|
}
|
|
3883
|
+
function ColdSessionsSummaryRow({
|
|
3884
|
+
count,
|
|
3885
|
+
isSelected,
|
|
3886
|
+
hasFocus,
|
|
3887
|
+
contentWidth
|
|
3888
|
+
}) {
|
|
3889
|
+
const prefix = " ";
|
|
3890
|
+
const baseLabel = `... ${count} cold`;
|
|
3891
|
+
const hint = " +";
|
|
3892
|
+
const baseText = `${prefix}${baseLabel}`;
|
|
3893
|
+
const showHint = isSelected && hasFocus && getDisplayWidth(baseText) + getDisplayWidth(hint) <= contentWidth;
|
|
3894
|
+
const text = showHint ? `${baseText}${hint}` : baseText;
|
|
3895
|
+
const padding = Math.max(0, contentWidth - getDisplayWidth(text));
|
|
3896
|
+
const focused = isSelected && hasFocus;
|
|
3897
|
+
const muted = isSelected && !hasFocus;
|
|
3898
|
+
return /* @__PURE__ */ jsxs4(Text4, { children: [
|
|
3899
|
+
BOX.v,
|
|
3900
|
+
" ",
|
|
3901
|
+
/* @__PURE__ */ jsx4(
|
|
3902
|
+
Text4,
|
|
3903
|
+
{
|
|
3904
|
+
backgroundColor: focused || muted ? "blue" : void 0,
|
|
3905
|
+
bold: focused,
|
|
3906
|
+
dimColor: !focused,
|
|
3907
|
+
children: text
|
|
3908
|
+
}
|
|
3909
|
+
),
|
|
3910
|
+
" ".repeat(padding),
|
|
3911
|
+
BOX.v
|
|
3912
|
+
] });
|
|
3913
|
+
}
|
|
3914
|
+
function activeParen(count, color, opts = {}) {
|
|
3915
|
+
if (count === 0) return [];
|
|
3916
|
+
const { bold = true, dim = false } = opts;
|
|
3917
|
+
return [
|
|
3918
|
+
{ text: " (", dim: true },
|
|
3919
|
+
{ text: `${count}`, color, bold, dim },
|
|
3920
|
+
{ text: ")", dim: true }
|
|
3921
|
+
];
|
|
3922
|
+
}
|
|
3923
|
+
function hiddenSeg(hidden, short) {
|
|
3924
|
+
if (hidden.total === 0) return [];
|
|
3925
|
+
const segs = [{ text: ` \xB7 \u2298 ${hidden.total}`, dim: true }];
|
|
3926
|
+
if (!short) segs.push({ text: " hidden", dim: true });
|
|
3927
|
+
if (hidden.active > 0) {
|
|
3928
|
+
segs.push(...activeParen(hidden.active, "yellow"));
|
|
3929
|
+
}
|
|
3930
|
+
return segs;
|
|
3931
|
+
}
|
|
3932
|
+
function buildTitleSegments(label, census, availableWidth) {
|
|
3933
|
+
const labelSeg = { text: label, dim: true };
|
|
3934
|
+
if (!census) return [labelSeg];
|
|
3935
|
+
const widthOf = (segs) => segs.reduce((w, s) => w + getDisplayWidth(s.text), 0);
|
|
3936
|
+
const c = census;
|
|
3937
|
+
const LEVELS = [
|
|
3938
|
+
// L0: full long form
|
|
3939
|
+
{
|
|
3940
|
+
projectsSuffix: " projects",
|
|
3941
|
+
sessionsSuffix: " sessions",
|
|
3942
|
+
subAgentsSuffix: " sub-agents"
|
|
3943
|
+
},
|
|
3944
|
+
// L1: short form (single-letter abbreviations)
|
|
3945
|
+
{
|
|
3946
|
+
projectsSuffix: "p",
|
|
3947
|
+
sessionsSuffix: "s",
|
|
3948
|
+
subAgentsSuffix: "a",
|
|
3949
|
+
hiddenShort: true
|
|
3950
|
+
},
|
|
3951
|
+
// L2: drop sub-agents
|
|
3952
|
+
{ projectsSuffix: "p", sessionsSuffix: "s", hiddenShort: true },
|
|
3953
|
+
// L3: drop sessions
|
|
3954
|
+
{ projectsSuffix: "p", hiddenShort: true },
|
|
3955
|
+
// L4: drop project active counter
|
|
3956
|
+
{ projectsSuffix: "p", showProjectActive: false, hiddenShort: true },
|
|
3957
|
+
// L5: just hidden alert
|
|
3958
|
+
{ hiddenShort: true },
|
|
3959
|
+
// L6: just the label
|
|
3960
|
+
{ hiddenShort: true, omitHidden: true }
|
|
3961
|
+
];
|
|
3962
|
+
const buildLevel = (level) => {
|
|
3963
|
+
const segs = [labelSeg];
|
|
3964
|
+
if (level.projectsSuffix !== void 0) {
|
|
3965
|
+
segs.push({
|
|
3966
|
+
text: ` ${c.projects.total}${level.projectsSuffix}`,
|
|
3967
|
+
dim: true
|
|
3968
|
+
});
|
|
3969
|
+
if (level.showProjectActive !== false) {
|
|
3970
|
+
segs.push(...activeParen(c.projects.active, "green"));
|
|
3971
|
+
}
|
|
3972
|
+
}
|
|
3973
|
+
if (level.sessionsSuffix !== void 0) {
|
|
3974
|
+
segs.push({
|
|
3975
|
+
text: ` \xB7 ${c.sessions.total}${level.sessionsSuffix}`,
|
|
3976
|
+
dim: true
|
|
3977
|
+
});
|
|
3978
|
+
segs.push(...activeParen(c.sessions.active, "green"));
|
|
3979
|
+
}
|
|
3980
|
+
if (level.subAgentsSuffix !== void 0) {
|
|
3981
|
+
segs.push({
|
|
3982
|
+
text: ` \xB7 ${c.subAgents.total}${level.subAgentsSuffix}`,
|
|
3983
|
+
dim: true
|
|
3984
|
+
});
|
|
3985
|
+
segs.push(...activeParen(c.subAgents.active, "green"));
|
|
3986
|
+
}
|
|
3987
|
+
if (!level.omitHidden) {
|
|
3988
|
+
segs.push(...hiddenSeg(c.hidden, level.hiddenShort ?? false));
|
|
3989
|
+
}
|
|
3990
|
+
return segs;
|
|
3991
|
+
};
|
|
3992
|
+
const candidates = LEVELS.map(buildLevel);
|
|
3993
|
+
for (const cand of candidates) {
|
|
3994
|
+
if (widthOf(cand) <= availableWidth) return cand;
|
|
3995
|
+
}
|
|
3996
|
+
return candidates[candidates.length - 1];
|
|
3997
|
+
}
|
|
3760
3998
|
function SessionTreePanel({
|
|
3761
3999
|
projects,
|
|
3762
4000
|
coldProjects,
|
|
@@ -3767,7 +4005,8 @@ function SessionTreePanel({
|
|
|
3767
4005
|
expandedIds = /* @__PURE__ */ new Set(),
|
|
3768
4006
|
trackingOn = false,
|
|
3769
4007
|
spinner = "",
|
|
3770
|
-
scopeLabel
|
|
4008
|
+
scopeLabel,
|
|
4009
|
+
census
|
|
3771
4010
|
}) {
|
|
3772
4011
|
const innerWidth = getInnerWidth(width);
|
|
3773
4012
|
const contentWidth = innerWidth - 1;
|
|
@@ -3775,12 +4014,38 @@ function SessionTreePanel({
|
|
|
3775
4014
|
const titleText = scopeLabel ? `Projects [${scopeLabel}]` : "Projects";
|
|
3776
4015
|
const titleLine = createTitleLine(titleText, titleSuffix, width);
|
|
3777
4016
|
const bottomLine = createBottomLine(width);
|
|
4017
|
+
const censusSegments = census ? (() => {
|
|
4018
|
+
const segs = buildTitleSegments("", census, contentWidth);
|
|
4019
|
+
const withoutLabel = segs[0]?.text === "" ? segs.slice(1) : segs;
|
|
4020
|
+
return withoutLabel.map(
|
|
4021
|
+
(seg, i) => i === 0 ? { ...seg, text: seg.text.replace(/^ /, "") } : seg
|
|
4022
|
+
);
|
|
4023
|
+
})() : null;
|
|
4024
|
+
const censusWidth = censusSegments ? censusSegments.reduce((w, s) => w + getDisplayWidth(s.text), 0) : 0;
|
|
4025
|
+
const censusPadding = censusSegments ? Math.max(0, contentWidth - censusWidth) : 0;
|
|
4026
|
+
const renderCensusRow = () => censusSegments ? /* @__PURE__ */ jsxs4(Text4, { children: [
|
|
4027
|
+
BOX.v,
|
|
4028
|
+
" ",
|
|
4029
|
+
censusSegments.map((seg, i) => /* @__PURE__ */ jsx4(
|
|
4030
|
+
Text4,
|
|
4031
|
+
{
|
|
4032
|
+
color: seg.color,
|
|
4033
|
+
dimColor: seg.dim,
|
|
4034
|
+
bold: seg.bold,
|
|
4035
|
+
children: seg.text
|
|
4036
|
+
},
|
|
4037
|
+
`census-${i}`
|
|
4038
|
+
)),
|
|
4039
|
+
" ".repeat(censusPadding),
|
|
4040
|
+
BOX.v
|
|
4041
|
+
] }) : null;
|
|
3778
4042
|
const totalProjectCount = projects.length + coldProjects.length;
|
|
3779
4043
|
if (totalProjectCount === 0) {
|
|
3780
4044
|
const emptyText = "No Claude sessions";
|
|
3781
4045
|
const emptyPadding = Math.max(0, contentWidth - emptyText.length);
|
|
3782
4046
|
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", width, children: [
|
|
3783
4047
|
/* @__PURE__ */ jsx4(Text4, { children: titleLine }),
|
|
4048
|
+
renderCensusRow(),
|
|
3784
4049
|
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
3785
4050
|
BOX.v,
|
|
3786
4051
|
" ",
|
|
@@ -3799,10 +4064,14 @@ function SessionTreePanel({
|
|
|
3799
4064
|
if (row.kind === "subagent-summary")
|
|
3800
4065
|
return selectedId === `__sub-${row.parentId}__`;
|
|
3801
4066
|
if (row.kind === "cold-projects-summary") return selectedId === "__cold__";
|
|
4067
|
+
if (row.kind === "cold-sessions-summary")
|
|
4068
|
+
return selectedId === `__cold-sessions-${row.projectName}__`;
|
|
3802
4069
|
return false;
|
|
3803
4070
|
});
|
|
3804
|
-
const
|
|
3805
|
-
const
|
|
4071
|
+
const censusRowCost = censusSegments ? 1 : 0;
|
|
4072
|
+
const effectiveMaxRows = maxRows !== void 0 ? Math.max(1, maxRows - censusRowCost) : void 0;
|
|
4073
|
+
const needsOverflow = effectiveMaxRows !== void 0 && totalRows > effectiveMaxRows;
|
|
4074
|
+
const visibleCount = needsOverflow ? effectiveMaxRows - 1 : totalRows;
|
|
3806
4075
|
let scrollTop = 0;
|
|
3807
4076
|
if (needsOverflow && selectedFlatIndex >= 0) {
|
|
3808
4077
|
scrollTop = Math.max(0, selectedFlatIndex - visibleCount + 1);
|
|
@@ -3812,6 +4081,7 @@ function SessionTreePanel({
|
|
|
3812
4081
|
const hiddenBelow = totalRows - (scrollTop + displayRows.length);
|
|
3813
4082
|
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", width, children: [
|
|
3814
4083
|
/* @__PURE__ */ jsx4(Text4, { children: titleLine }),
|
|
4084
|
+
renderCensusRow(),
|
|
3815
4085
|
displayRows.map(
|
|
3816
4086
|
(row, idx) => row.kind === "project" ? /* @__PURE__ */ jsx4(
|
|
3817
4087
|
ProjectRow,
|
|
@@ -3842,7 +4112,7 @@ function SessionTreePanel({
|
|
|
3842
4112
|
hasFocus
|
|
3843
4113
|
},
|
|
3844
4114
|
`subagent-summary-${idx}`
|
|
3845
|
-
) : /* @__PURE__ */ jsx4(
|
|
4115
|
+
) : row.kind === "cold-projects-summary" ? /* @__PURE__ */ jsx4(
|
|
3846
4116
|
ColdProjectsSummaryRow,
|
|
3847
4117
|
{
|
|
3848
4118
|
count: row.count,
|
|
@@ -3851,6 +4121,16 @@ function SessionTreePanel({
|
|
|
3851
4121
|
width
|
|
3852
4122
|
},
|
|
3853
4123
|
"cold-summary"
|
|
4124
|
+
) : /* @__PURE__ */ jsx4(
|
|
4125
|
+
ColdSessionsSummaryRow,
|
|
4126
|
+
{
|
|
4127
|
+
count: row.count,
|
|
4128
|
+
projectName: row.projectName,
|
|
4129
|
+
isSelected: selectedId === `__cold-sessions-${row.projectName}__`,
|
|
4130
|
+
hasFocus,
|
|
4131
|
+
contentWidth
|
|
4132
|
+
},
|
|
4133
|
+
`cold-sessions-${row.projectName}`
|
|
3854
4134
|
)
|
|
3855
4135
|
),
|
|
3856
4136
|
hiddenBelow > 0 && /* @__PURE__ */ jsxs4(Text4, { children: [
|
|
@@ -3904,9 +4184,9 @@ function adjustViewerCursorOnNewActivities(args) {
|
|
|
3904
4184
|
// src/ui/App.tsx
|
|
3905
4185
|
import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
3906
4186
|
var VIEWER_HEIGHT_FRACTION = 0.55;
|
|
3907
|
-
function
|
|
4187
|
+
function makeSentinel(id) {
|
|
3908
4188
|
return {
|
|
3909
|
-
id
|
|
4189
|
+
id,
|
|
3910
4190
|
hideKey: "",
|
|
3911
4191
|
filePath: "",
|
|
3912
4192
|
projectPath: "",
|
|
@@ -3920,6 +4200,8 @@ function subSummarySentinel(parentId) {
|
|
|
3920
4200
|
liveState: null
|
|
3921
4201
|
};
|
|
3922
4202
|
}
|
|
4203
|
+
var subSummarySentinel = (parentId) => makeSentinel(`__sub-${parentId}__`);
|
|
4204
|
+
var coldSessionsSummarySentinel = (projectName) => makeSentinel(`__cold-sessions-${projectName}__`);
|
|
3923
4205
|
function appendSubAgentRows(result, session, expandedIds) {
|
|
3924
4206
|
const isCold = session.status === "cold";
|
|
3925
4207
|
const sessionCollapsedKey = `__collapsed-session-${session.id}`;
|
|
@@ -3942,6 +4224,66 @@ function appendSubAgentRows(result, session, expandedIds) {
|
|
|
3942
4224
|
}
|
|
3943
4225
|
}
|
|
3944
4226
|
}
|
|
4227
|
+
function filterTreeByHidden(tree) {
|
|
4228
|
+
const visibleSession = (s) => ({
|
|
4229
|
+
...s,
|
|
4230
|
+
subAgents: s.subAgents.filter((sa) => !sa.hidden)
|
|
4231
|
+
});
|
|
4232
|
+
const visibleProject = (p) => ({
|
|
4233
|
+
...p,
|
|
4234
|
+
sessions: p.sessions.filter((s) => !s.hidden).map(visibleSession)
|
|
4235
|
+
});
|
|
4236
|
+
return {
|
|
4237
|
+
...tree,
|
|
4238
|
+
projects: tree.projects.filter((p) => !p.hidden).map(visibleProject),
|
|
4239
|
+
coldProjects: tree.coldProjects.filter((p) => !p.hidden).map(visibleProject)
|
|
4240
|
+
};
|
|
4241
|
+
}
|
|
4242
|
+
function computeCensus(tree) {
|
|
4243
|
+
let projectsTotal = 0;
|
|
4244
|
+
let projectsActive = 0;
|
|
4245
|
+
let sessionsTotal = 0;
|
|
4246
|
+
let sessionsActive = 0;
|
|
4247
|
+
let subAgentsTotal = 0;
|
|
4248
|
+
let subAgentsActive = 0;
|
|
4249
|
+
let hiddenTotal = 0;
|
|
4250
|
+
let hiddenActive = 0;
|
|
4251
|
+
const isActive = (n) => n.status === "hot" || n.status === "warm";
|
|
4252
|
+
for (const p of [...tree.projects, ...tree.coldProjects]) {
|
|
4253
|
+
projectsTotal++;
|
|
4254
|
+
let projectHasVisibleActive = false;
|
|
4255
|
+
for (const s of p.sessions) {
|
|
4256
|
+
sessionsTotal++;
|
|
4257
|
+
const sessionVisible = !s.hidden && !p.hidden;
|
|
4258
|
+
if (sessionVisible) {
|
|
4259
|
+
if (isActive(s)) {
|
|
4260
|
+
sessionsActive++;
|
|
4261
|
+
projectHasVisibleActive = true;
|
|
4262
|
+
}
|
|
4263
|
+
} else {
|
|
4264
|
+
hiddenTotal++;
|
|
4265
|
+
if (isActive(s)) hiddenActive++;
|
|
4266
|
+
}
|
|
4267
|
+
for (const sa of s.subAgents) {
|
|
4268
|
+
subAgentsTotal++;
|
|
4269
|
+
const saVisible = !sa.hidden && !s.hidden && !p.hidden;
|
|
4270
|
+
if (saVisible) {
|
|
4271
|
+
if (isActive(sa)) subAgentsActive++;
|
|
4272
|
+
} else {
|
|
4273
|
+
hiddenTotal++;
|
|
4274
|
+
if (isActive(sa)) hiddenActive++;
|
|
4275
|
+
}
|
|
4276
|
+
}
|
|
4277
|
+
}
|
|
4278
|
+
if (projectHasVisibleActive) projectsActive++;
|
|
4279
|
+
}
|
|
4280
|
+
return {
|
|
4281
|
+
projects: { total: projectsTotal, active: projectsActive },
|
|
4282
|
+
sessions: { total: sessionsTotal, active: sessionsActive },
|
|
4283
|
+
subAgents: { total: subAgentsTotal, active: subAgentsActive },
|
|
4284
|
+
hidden: { total: hiddenTotal, active: hiddenActive }
|
|
4285
|
+
};
|
|
4286
|
+
}
|
|
3945
4287
|
function flattenSessions2(tree, expandedIds) {
|
|
3946
4288
|
const result = [];
|
|
3947
4289
|
const projectToFlat = (project, isCold) => {
|
|
@@ -3962,9 +4304,32 @@ function flattenSessions2(tree, expandedIds) {
|
|
|
3962
4304
|
});
|
|
3963
4305
|
const shouldShowSessions = isCold ? expandedIds.has(`__expanded-${sentinelId}`) : !expandedIds.has(`__collapsed-${sentinelId}`);
|
|
3964
4306
|
if (shouldShowSessions) {
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
4307
|
+
if (isCold) {
|
|
4308
|
+
for (const session of project.sessions) {
|
|
4309
|
+
result.push(session);
|
|
4310
|
+
appendSubAgentRows(result, session, expandedIds);
|
|
4311
|
+
}
|
|
4312
|
+
} else {
|
|
4313
|
+
const activeSessions = project.sessions.filter(
|
|
4314
|
+
(s) => s.status !== "cold"
|
|
4315
|
+
);
|
|
4316
|
+
const coldSessions = project.sessions.filter(
|
|
4317
|
+
(s) => s.status === "cold"
|
|
4318
|
+
);
|
|
4319
|
+
for (const session of activeSessions) {
|
|
4320
|
+
result.push(session);
|
|
4321
|
+
appendSubAgentRows(result, session, expandedIds);
|
|
4322
|
+
}
|
|
4323
|
+
if (coldSessions.length > 0) {
|
|
4324
|
+
const coldKey = `__cold-sessions-${project.name}__`;
|
|
4325
|
+
result.push(coldSessionsSummarySentinel(project.name));
|
|
4326
|
+
if (expandedIds.has(coldKey)) {
|
|
4327
|
+
for (const session of coldSessions) {
|
|
4328
|
+
result.push(session);
|
|
4329
|
+
appendSubAgentRows(result, session, expandedIds);
|
|
4330
|
+
}
|
|
4331
|
+
}
|
|
4332
|
+
}
|
|
3968
4333
|
}
|
|
3969
4334
|
}
|
|
3970
4335
|
};
|
|
@@ -4127,9 +4492,15 @@ function App({
|
|
|
4127
4492
|
const helpTotalLinesRef = useRef(0);
|
|
4128
4493
|
const [tracking, setTracking] = useState3(false);
|
|
4129
4494
|
const seenIdsRef = useRef(/* @__PURE__ */ new Set());
|
|
4495
|
+
const [showHidden, setShowHidden] = useState3(false);
|
|
4496
|
+
const displayTree = useMemo(
|
|
4497
|
+
() => showHidden ? sessionTree : filterTreeByHidden(sessionTree),
|
|
4498
|
+
[sessionTree, showHidden]
|
|
4499
|
+
);
|
|
4500
|
+
const census = useMemo(() => computeCensus(sessionTree), [sessionTree]);
|
|
4130
4501
|
const allFlat = useMemo(
|
|
4131
|
-
() => flattenSessions2(
|
|
4132
|
-
[
|
|
4502
|
+
() => flattenSessions2(displayTree, expandedIds),
|
|
4503
|
+
[displayTree, expandedIds]
|
|
4133
4504
|
);
|
|
4134
4505
|
const allFlatRef = useRef(allFlat);
|
|
4135
4506
|
useEffect3(() => {
|
|
@@ -4210,11 +4581,12 @@ function App({
|
|
|
4210
4581
|
const refresh = useCallback(() => {
|
|
4211
4582
|
const freshConfig = loadGlobalConfig();
|
|
4212
4583
|
const tree = discoverSessions(freshConfig, discoverOptions);
|
|
4213
|
-
const
|
|
4584
|
+
const trackingTree = showHidden ? tree : filterTreeByHidden(tree);
|
|
4585
|
+
const updatedFlat = flattenSessions2(trackingTree, expandedIds);
|
|
4214
4586
|
let nextSelected = selectedId;
|
|
4215
4587
|
if (tracking) {
|
|
4216
4588
|
const { target, ids } = pickTrackingTarget(
|
|
4217
|
-
|
|
4589
|
+
trackingTree,
|
|
4218
4590
|
selectedId,
|
|
4219
4591
|
seenIdsRef.current
|
|
4220
4592
|
);
|
|
@@ -4236,7 +4608,7 @@ function App({
|
|
|
4236
4608
|
if (!node || !node.filePath) return;
|
|
4237
4609
|
const newActivities = parseSessionHistory(node.filePath);
|
|
4238
4610
|
setActivities(newActivities);
|
|
4239
|
-
}, [selectedId, expandedIds, tracking, discoverOptions]);
|
|
4611
|
+
}, [selectedId, expandedIds, tracking, discoverOptions, showHidden]);
|
|
4240
4612
|
const refreshRef = useRef(refresh);
|
|
4241
4613
|
useEffect3(() => {
|
|
4242
4614
|
refreshRef.current = refresh;
|
|
@@ -4354,9 +4726,9 @@ function App({
|
|
|
4354
4726
|
setTracking((on) => {
|
|
4355
4727
|
const next = !on;
|
|
4356
4728
|
if (next) {
|
|
4357
|
-
seenIdsRef.current = collectAllIds(
|
|
4729
|
+
seenIdsRef.current = collectAllIds(displayTree);
|
|
4358
4730
|
const { target } = pickTrackingTarget(
|
|
4359
|
-
|
|
4731
|
+
displayTree,
|
|
4360
4732
|
selectedId,
|
|
4361
4733
|
seenIdsRef.current
|
|
4362
4734
|
);
|
|
@@ -4370,7 +4742,7 @@ function App({
|
|
|
4370
4742
|
if (focus !== "tree") return;
|
|
4371
4743
|
stopTracking();
|
|
4372
4744
|
if (!selectedId) return;
|
|
4373
|
-
const target = findParentTarget(selectedId,
|
|
4745
|
+
const target = findParentTarget(selectedId, displayTree, allFlat);
|
|
4374
4746
|
if (target && target !== selectedId) setSelectedId(target);
|
|
4375
4747
|
},
|
|
4376
4748
|
onSwitchFocus: () => setFocus((f) => f === "tree" ? "viewer" : "tree"),
|
|
@@ -4389,10 +4761,7 @@ function App({
|
|
|
4389
4761
|
} else {
|
|
4390
4762
|
setIsLive(false);
|
|
4391
4763
|
setScrollOffset(
|
|
4392
|
-
(o) => Math.min(
|
|
4393
|
-
o + 1,
|
|
4394
|
-
Math.max(0, mergedActivities.length - viewerRows)
|
|
4395
|
-
)
|
|
4764
|
+
(o) => Math.min(o + 1, Math.max(0, mergedActivities.length - viewerRows))
|
|
4396
4765
|
);
|
|
4397
4766
|
}
|
|
4398
4767
|
}
|
|
@@ -4544,7 +4913,7 @@ function App({
|
|
|
4544
4913
|
stopTracking();
|
|
4545
4914
|
if (selectedId.startsWith("__proj-") && selectedId.endsWith("__")) {
|
|
4546
4915
|
const projectName = selectedId.slice(7, -2);
|
|
4547
|
-
const isCold =
|
|
4916
|
+
const isCold = displayTree.coldProjects.some(
|
|
4548
4917
|
(p) => p.name === projectName
|
|
4549
4918
|
);
|
|
4550
4919
|
const toggleKey = isCold ? `__expanded-${selectedId}` : `__collapsed-${selectedId}`;
|
|
@@ -4571,6 +4940,15 @@ function App({
|
|
|
4571
4940
|
});
|
|
4572
4941
|
return;
|
|
4573
4942
|
}
|
|
4943
|
+
if (selectedId.startsWith("__cold-sessions-") && selectedId.endsWith("__")) {
|
|
4944
|
+
setExpandedIds((prev) => {
|
|
4945
|
+
const next = new Set(prev);
|
|
4946
|
+
if (next.has(selectedId)) next.delete(selectedId);
|
|
4947
|
+
else next.add(selectedId);
|
|
4948
|
+
return next;
|
|
4949
|
+
});
|
|
4950
|
+
return;
|
|
4951
|
+
}
|
|
4574
4952
|
if (selectedId.startsWith("__sub-") && selectedId.endsWith("__")) {
|
|
4575
4953
|
const parentId = selectedId.slice(6, -2);
|
|
4576
4954
|
setExpandedIds((prev) => {
|
|
@@ -4580,7 +4958,10 @@ function App({
|
|
|
4580
4958
|
setSelectedId(parentId);
|
|
4581
4959
|
} else {
|
|
4582
4960
|
next.add(parentId);
|
|
4583
|
-
const allSessions2 =
|
|
4961
|
+
const allSessions2 = [
|
|
4962
|
+
...displayTree.projects.flatMap((p) => p.sessions),
|
|
4963
|
+
...displayTree.coldProjects.flatMap((p) => p.sessions)
|
|
4964
|
+
];
|
|
4584
4965
|
const parent = allSessions2.find((s) => s.id === parentId);
|
|
4585
4966
|
const firstNew = parent?.subAgents.find(
|
|
4586
4967
|
(sa) => sa.status === "cool" || sa.status === "cold"
|
|
@@ -4592,8 +4973,8 @@ function App({
|
|
|
4592
4973
|
return;
|
|
4593
4974
|
}
|
|
4594
4975
|
const allSessions3 = [
|
|
4595
|
-
...
|
|
4596
|
-
...
|
|
4976
|
+
...displayTree.projects.flatMap((p) => p.sessions),
|
|
4977
|
+
...displayTree.coldProjects.flatMap((p) => p.sessions)
|
|
4597
4978
|
];
|
|
4598
4979
|
const selectedSessionObj = allSessions3.find((s) => s.id === selectedId);
|
|
4599
4980
|
if (selectedSessionObj && selectedSessionObj.subAgents.length > 0) {
|
|
@@ -4627,42 +5008,60 @@ function App({
|
|
|
4627
5008
|
onHide: () => {
|
|
4628
5009
|
if (focus !== "tree" || !selectedId) return;
|
|
4629
5010
|
stopTracking();
|
|
5011
|
+
const nextAfterHide = () => allFlat[selectedIndex + 1]?.id ?? allFlat[selectedIndex - 1]?.id ?? null;
|
|
5012
|
+
const applyHideToggle = (params) => {
|
|
5013
|
+
if (params.isHidden) {
|
|
5014
|
+
params.unhide();
|
|
5015
|
+
refresh();
|
|
5016
|
+
return;
|
|
5017
|
+
}
|
|
5018
|
+
params.hide();
|
|
5019
|
+
refresh();
|
|
5020
|
+
if (!showHidden) setSelectedId(nextAfterHide());
|
|
5021
|
+
};
|
|
4630
5022
|
if (selectedId.startsWith("__proj-") && selectedId.endsWith("__")) {
|
|
4631
5023
|
const projectName = selectedId.slice(7, -2);
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
5024
|
+
const proj = sessionTree.projects.find((p) => p.name === projectName) ?? sessionTree.coldProjects.find((p) => p.name === projectName);
|
|
5025
|
+
applyHideToggle({
|
|
5026
|
+
isHidden: !!proj?.hidden,
|
|
5027
|
+
hide: () => hideProject(projectName),
|
|
5028
|
+
unhide: () => unhideProject(projectName)
|
|
5029
|
+
});
|
|
4636
5030
|
return;
|
|
4637
5031
|
}
|
|
4638
5032
|
if (selectedId === "__cold__") {
|
|
4639
5033
|
const coldSessions = sessionTree.coldProjects?.flatMap((p) => p.sessions) ?? [];
|
|
4640
5034
|
for (const s of coldSessions) hideSession(s.hideKey);
|
|
4641
|
-
const nextId = allFlat[selectedIndex - 1]?.id ?? null;
|
|
4642
5035
|
refresh();
|
|
4643
|
-
setSelectedId(
|
|
5036
|
+
setSelectedId(nextAfterHide());
|
|
4644
5037
|
return;
|
|
4645
5038
|
}
|
|
4646
|
-
const
|
|
4647
|
-
|
|
5039
|
+
const allSessionsFull = [
|
|
5040
|
+
...sessionTree.projects.flatMap((p) => p.sessions),
|
|
5041
|
+
...sessionTree.coldProjects.flatMap((p) => p.sessions)
|
|
5042
|
+
];
|
|
5043
|
+
const selectedSession2 = allSessionsFull.find((s) => s.id === selectedId);
|
|
4648
5044
|
if (selectedSession2) {
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
5045
|
+
applyHideToggle({
|
|
5046
|
+
isHidden: !!selectedSession2.hidden,
|
|
5047
|
+
hide: () => hideSession(selectedSession2.hideKey),
|
|
5048
|
+
unhide: () => unhideSession(selectedSession2.hideKey)
|
|
5049
|
+
});
|
|
4653
5050
|
return;
|
|
4654
5051
|
}
|
|
4655
|
-
for (const s of
|
|
4656
|
-
const
|
|
4657
|
-
if (
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
5052
|
+
for (const s of allSessionsFull) {
|
|
5053
|
+
const sa = s.subAgents.find((x) => x.id === selectedId);
|
|
5054
|
+
if (sa) {
|
|
5055
|
+
applyHideToggle({
|
|
5056
|
+
isHidden: !!sa.hidden,
|
|
5057
|
+
hide: () => hideSubAgent(sa.hideKey),
|
|
5058
|
+
unhide: () => unhideSubAgent(sa.hideKey)
|
|
5059
|
+
});
|
|
4662
5060
|
return;
|
|
4663
5061
|
}
|
|
4664
5062
|
}
|
|
4665
5063
|
},
|
|
5064
|
+
onToggleShowHidden: () => setShowHidden((on) => !on),
|
|
4666
5065
|
onRefresh: refresh,
|
|
4667
5066
|
onQuit: exit,
|
|
4668
5067
|
onFilter: () => setFilterIndex((i) => (i + 1) % filterPresets.length),
|
|
@@ -4674,7 +5073,7 @@ function App({
|
|
|
4674
5073
|
let selectedSession = rawSelected;
|
|
4675
5074
|
if (isProjectSentinel && selectedId) {
|
|
4676
5075
|
const projectName = selectedId.slice(7, -2);
|
|
4677
|
-
const project =
|
|
5076
|
+
const project = displayTree.projects.find((p) => p.name === projectName) ?? displayTree.coldProjects.find((p) => p.name === projectName);
|
|
4678
5077
|
if (project && project.sessions.length > 0) {
|
|
4679
5078
|
selectedSession = project.sessions[0];
|
|
4680
5079
|
}
|
|
@@ -4732,8 +5131,8 @@ function App({
|
|
|
4732
5131
|
/* @__PURE__ */ jsx5(
|
|
4733
5132
|
SessionTreePanel,
|
|
4734
5133
|
{
|
|
4735
|
-
projects:
|
|
4736
|
-
coldProjects:
|
|
5134
|
+
projects: displayTree.projects ?? [],
|
|
5135
|
+
coldProjects: displayTree.coldProjects ?? [],
|
|
4737
5136
|
selectedId,
|
|
4738
5137
|
hasFocus: focus === "tree",
|
|
4739
5138
|
width,
|
|
@@ -4741,7 +5140,8 @@ function App({
|
|
|
4741
5140
|
expandedIds,
|
|
4742
5141
|
trackingOn: tracking,
|
|
4743
5142
|
spinner,
|
|
4744
|
-
scopeLabel: scopeToProject2 ? basename3(scopeToProject2) : void 0
|
|
5143
|
+
scopeLabel: scopeToProject2 ? basename3(scopeToProject2) : void 0,
|
|
5144
|
+
census: isWatchMode ? census : void 0
|
|
4745
5145
|
}
|
|
4746
5146
|
),
|
|
4747
5147
|
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: detailMode && detailActivity ? /* @__PURE__ */ jsx5(
|
package/package.json
CHANGED