@atlasharness/atlascode 0.1.37 → 0.1.39

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.
Files changed (2) hide show
  1. package/dist/cli.js +470 -102
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -135054,10 +135054,13 @@ function applyColor(text, color) {
135054
135054
  }
135055
135055
  return colorize(text, color, "foreground");
135056
135056
  }
135057
- var CHALK_BOOSTED_FOR_XTERMJS, CHALK_CLAMPED_FOR_TMUX, RGB_REGEX, ANSI_REGEX, colorize = (str, color, type) => {
135057
+ var CHALK_BOOSTED_FOR_XTERMJS, CHALK_CLAMPED_FOR_TMUX, RGB_REGEX, ANSI_REGEX, BARE_ANSI_COLOR_NAMES, colorize = (str, color, type) => {
135058
135058
  if (!color) {
135059
135059
  return str;
135060
135060
  }
135061
+ if (BARE_ANSI_COLOR_NAMES.has(color)) {
135062
+ return colorize(str, `ansi:${color}`, type);
135063
+ }
135061
135064
  if (color.startsWith("ansi:")) {
135062
135065
  const value = color.substring("ansi:".length);
135063
135066
  switch (value) {
@@ -135124,6 +135127,24 @@ var init_colorize = __esm(() => {
135124
135127
  CHALK_CLAMPED_FOR_TMUX = clampChalkLevelForTmux();
135125
135128
  RGB_REGEX = /^rgb\(\s?(\d+),\s?(\d+),\s?(\d+)\s?\)$/;
135126
135129
  ANSI_REGEX = /^ansi256\(\s?(\d+)\s?\)$/;
135130
+ BARE_ANSI_COLOR_NAMES = new Set([
135131
+ "black",
135132
+ "red",
135133
+ "green",
135134
+ "yellow",
135135
+ "blue",
135136
+ "magenta",
135137
+ "cyan",
135138
+ "white",
135139
+ "blackBright",
135140
+ "redBright",
135141
+ "greenBright",
135142
+ "yellowBright",
135143
+ "blueBright",
135144
+ "magentaBright",
135145
+ "cyanBright",
135146
+ "whiteBright"
135147
+ ]);
135127
135148
  });
135128
135149
 
135129
135150
  // src/tui/utils/earlyInput.ts
@@ -159664,7 +159685,14 @@ function resolveColor2(color, theme) {
159664
159685
  if (color.startsWith("rgb(") || color.startsWith("#") || color.startsWith("ansi256(") || color.startsWith("ansi:")) {
159665
159686
  return color;
159666
159687
  }
159667
- return theme[color];
159688
+ const themed = theme[color];
159689
+ if (themed !== undefined) {
159690
+ return themed;
159691
+ }
159692
+ if (BARE_ANSI_COLOR_NAMES.has(color)) {
159693
+ return color;
159694
+ }
159695
+ return;
159668
159696
  }
159669
159697
  function ThemedText(t0) {
159670
159698
  const $ = import_compiler_runtime7.c(10);
@@ -159723,6 +159751,7 @@ function ThemedText(t0) {
159723
159751
  var import_compiler_runtime7, import_react12, jsx_dev_runtime10, TextHoverColorContext;
159724
159752
  var init_ThemedText = __esm(() => {
159725
159753
  init_Text();
159754
+ init_colorize();
159726
159755
  init_theme();
159727
159756
  init_ThemeProvider();
159728
159757
  import_compiler_runtime7 = __toESM(require_compiler_runtime(), 1);
@@ -419856,6 +419885,7 @@ __export(exports_sessionStorage, {
419856
419885
  clearAgentTranscriptSubdir: () => clearAgentTranscriptSubdir2,
419857
419886
  clearSessionMessagesCache: () => clearSessionMessagesCache2,
419858
419887
  clearSessionMetadata: () => clearSessionMetadata2,
419888
+ countVisibleMessages: () => countVisibleMessages2,
419859
419889
  enrichLogs: () => enrichLogs,
419860
419890
  extractTeammateTranscriptsFromTasks: () => extractTeammateTranscriptsFromTasks,
419861
419891
  getAgentTranscript: () => getAgentTranscript,
@@ -419883,6 +419913,7 @@ __export(exports_sessionStorage, {
419883
419913
  loadAllSubagentTranscriptsFromDisk: () => loadAllSubagentTranscriptsFromDisk,
419884
419914
  loadFullLog: () => loadFullLog,
419885
419915
  loadMessageLogs: () => loadMessageLogs,
419916
+ loadSameRepoAllMessageLogs: () => loadSameRepoAllMessageLogs,
419886
419917
  loadSameRepoMessageLogs: () => loadSameRepoMessageLogs,
419887
419918
  loadSameRepoMessageLogsProgressive: () => loadSameRepoMessageLogsProgressive,
419888
419919
  loadTranscriptFile: () => loadTranscriptFile2,
@@ -421829,6 +421860,21 @@ async function loadSameRepoMessageLogsProgressive(worktreePaths, limit, initialE
421829
421860
  });
421830
421861
  return { logs, allStatLogs, nextIndex };
421831
421862
  }
421863
+ async function loadSameRepoAllMessageLogs(worktreePaths) {
421864
+ const r = await loadSameRepoMessageLogsProgressive(worktreePaths);
421865
+ let logs = r.logs;
421866
+ let nextIndex = r.nextIndex;
421867
+ while (nextIndex < r.allStatLogs.length) {
421868
+ const more = await enrichLogs(r.allStatLogs, nextIndex, INITIAL_ENRICH_COUNT);
421869
+ logs = logs.concat(more.logs);
421870
+ nextIndex = more.nextIndex;
421871
+ }
421872
+ logs = sortLogs2(logs);
421873
+ logs.forEach((l, i) => {
421874
+ l.value = i;
421875
+ });
421876
+ return logs;
421877
+ }
421832
421878
  async function getStatOnlyLogsForWorktrees(worktreePaths, limit) {
421833
421879
  const projectsDir = getProjectsDir3();
421834
421880
  if (worktreePaths.length <= 1) {
@@ -422027,7 +422073,7 @@ async function getSessionFilesWithMtime(projectDir) {
422027
422073
  sessionFilesMap.set(sessionId, {
422028
422074
  path: filePath,
422029
422075
  mtime: st.mtime.getTime(),
422030
- ctime: st.birthtime.getTime(),
422076
+ birthtime: st.birthtime.getTime(),
422031
422077
  size: st.size
422032
422078
  });
422033
422079
  } catch {
@@ -422138,10 +422184,18 @@ async function readLiteMetadata(filePath, fileSize, buf) {
422138
422184
  const { head, tail } = await readHeadAndTail(filePath, fileSize, buf);
422139
422185
  if (!head)
422140
422186
  return { firstPrompt: "", isSidechain: false };
422141
- const isSidechain = head.includes('"isSidechain":true') || head.includes('"isSidechain": true');
422187
+ const firstLine = head.split(`
422188
+ `, 1)[0] ?? "";
422189
+ const isSidechain = firstLine.includes('"isSidechain":true') || firstLine.includes('"isSidechain": true');
422142
422190
  const projectPath = extractJsonStringField(head, "cwd");
422143
422191
  const teamName = extractJsonStringField(head, "teamName");
422144
422192
  const agentSetting = extractJsonStringField(head, "agentSetting");
422193
+ const lastTimestamp = extractLastJsonStringField2(tail, "timestamp");
422194
+ const firstTimestamp = extractJsonStringField(head, "timestamp");
422195
+ const modeRaw = extractLastJsonStringField2(tail, "mode");
422196
+ const mode = modeRaw === "coordinator" || modeRaw === "normal" ? modeRaw : undefined;
422197
+ const agentName = extractLastJsonStringField2(tail, "agentName");
422198
+ const agentColor = extractLastJsonStringField2(tail, "agentColor");
422145
422199
  const firstPrompt = extractLastJsonStringField2(tail, "lastPrompt") || extractFirstPromptFromChunk(head) || extractJsonStringFieldPrefix(head, "content", 200) || extractJsonStringFieldPrefix(head, "text", 200) || "";
422146
422200
  const customTitle = extractLastJsonStringField2(tail, "customTitle") ?? extractLastJsonStringField2(head, "customTitle") ?? extractLastJsonStringField2(tail, "aiTitle") ?? extractLastJsonStringField2(head, "aiTitle");
422147
422201
  const summary = extractLastJsonStringField2(tail, "summary");
@@ -422177,7 +422231,12 @@ async function readLiteMetadata(filePath, fileSize, buf) {
422177
422231
  worktreeName,
422178
422232
  prNumber,
422179
422233
  prUrl,
422180
- prRepository
422234
+ prRepository,
422235
+ firstTimestamp,
422236
+ lastTimestamp,
422237
+ mode,
422238
+ agentName,
422239
+ agentColor
422181
422240
  };
422182
422241
  }
422183
422242
  function extractFirstPromptFromChunk(chunk) {
@@ -422300,7 +422359,7 @@ async function getSessionFilesLite(projectDir, limit, projectPath) {
422300
422359
  isLite: true,
422301
422360
  fullPath: fileInfo.path,
422302
422361
  value: 0,
422303
- created: new Date(fileInfo.ctime),
422362
+ created: new Date(fileInfo.birthtime),
422304
422363
  modified: new Date(fileInfo.mtime),
422305
422364
  firstPrompt: "",
422306
422365
  messageCount: 0,
@@ -422316,6 +422375,12 @@ async function getSessionFilesLite(projectDir, limit, projectPath) {
422316
422375
  });
422317
422376
  return sorted;
422318
422377
  }
422378
+ function parseLiteTimestamp(iso) {
422379
+ if (!iso)
422380
+ return;
422381
+ const d = new Date(iso);
422382
+ return Number.isNaN(d.getTime()) ? undefined : d;
422383
+ }
422319
422384
  async function enrichLog(log, readBuf) {
422320
422385
  if (!log.isLite || !log.fullPath)
422321
422386
  return log;
@@ -422331,10 +422396,15 @@ async function enrichLog(log, readBuf) {
422331
422396
  summary: meta.summary,
422332
422397
  tag: meta.tag,
422333
422398
  agentSetting: meta.agentSetting,
422399
+ mode: meta.mode,
422400
+ agentName: meta.agentName,
422401
+ agentColor: meta.agentColor,
422334
422402
  prNumber: meta.prNumber,
422335
422403
  prUrl: meta.prUrl,
422336
422404
  prRepository: meta.prRepository,
422337
422405
  projectPath: meta.projectPath ?? log.projectPath,
422406
+ created: parseLiteTimestamp(meta.firstTimestamp) ?? log.created,
422407
+ modified: parseLiteTimestamp(meta.lastTimestamp) ?? log.modified,
422338
422408
  worktreeSession: meta.worktreeName ? {
422339
422409
  worktreeName: meta.worktreeName,
422340
422410
  originalCwd: "",
@@ -473564,14 +473634,207 @@ function formatShortActive(date) {
473564
473634
  const min = String(date.getMinutes()).padStart(2, "0");
473565
473635
  return `${m}/${d} ${h}:${min}`;
473566
473636
  }
473567
- function SessionTreeScreen({ onBack, onResume }) {
473637
+ function formatActive(date, now) {
473638
+ const ageMs = now - date.getTime();
473639
+ const m = Math.floor(ageMs / 60000);
473640
+ if (m < 1)
473641
+ return "now";
473642
+ if (m < 60)
473643
+ return `${m}m`;
473644
+ const h = Math.floor(m / 60);
473645
+ if (h < 24)
473646
+ return `${h}h`;
473647
+ const d = Math.floor(h / 24);
473648
+ if (d < 7)
473649
+ return `${d}d`;
473650
+ return formatShortActive(date);
473651
+ }
473652
+ function formatCreated(date) {
473653
+ const m = date.getMonth() + 1;
473654
+ const d = date.getDate();
473655
+ return `${m}/${d}/${date.getFullYear()}`;
473656
+ }
473657
+ function computeColumnLayout(termCols) {
473658
+ const NAME_MIN = 16;
473659
+ const ACTIVE_SLOT = 12;
473660
+ const CREATED_SLOT = 12;
473661
+ const BRANCH_SLOT = 13;
473662
+ const MSG_SLOT = 6;
473663
+ const LEFT = 4;
473664
+ const PAD = 4;
473665
+ const budget = termCols - PAD - LEFT - ACTIVE_SLOT;
473666
+ let showCreated = true;
473667
+ let showBranch = true;
473668
+ let showMsg = true;
473669
+ if (budget < NAME_MIN + CREATED_SLOT + BRANCH_SLOT + MSG_SLOT) {
473670
+ showMsg = false;
473671
+ if (budget < NAME_MIN + CREATED_SLOT + BRANCH_SLOT) {
473672
+ showBranch = false;
473673
+ if (budget < NAME_MIN + CREATED_SLOT) {
473674
+ showCreated = false;
473675
+ }
473676
+ }
473677
+ }
473678
+ let optional = 0;
473679
+ if (showCreated)
473680
+ optional += CREATED_SLOT;
473681
+ if (showBranch)
473682
+ optional += BRANCH_SLOT;
473683
+ if (showMsg)
473684
+ optional += MSG_SLOT;
473685
+ const flagsSlot = Math.round(budget * 0.3);
473686
+ const nameW = Math.max(NAME_MIN, budget - optional - flagsSlot);
473687
+ return { nameW, showCreated, showBranch, showMsg };
473688
+ }
473689
+ function agentColorToThemeColor(agentColor, theme) {
473690
+ const field = agentColor ? AGENT_COLOR_THEME_FIELD[agentColor] : undefined;
473691
+ return field ? theme[field] : undefined;
473692
+ }
473693
+ function agentRowColors(log, isSelected, isFocused, isCurrent, theme) {
473694
+ const identity = agentColorToThemeColor(log.agentColor, theme);
473695
+ const rowColor = isSelected ? "magentaBright" : isFocused ? "cyan" : identity;
473696
+ const dotGlyph = identity || isSelected || isCurrent ? "● " : "○ ";
473697
+ const dotColor = isSelected ? "magentaBright" : identity !== undefined ? identity : isCurrent ? "cyan" : undefined;
473698
+ return { rowColor, dotColor, dotGlyph };
473699
+ }
473700
+ function buildActionRow(state, title) {
473701
+ switch (state.kind) {
473702
+ case "confirm":
473703
+ return { text: `✋ ${title} · [再按 f 确认 fork · 3s 自动取消]`, color: "yellow" };
473704
+ case "forking":
473705
+ return { text: `→ forking ${title}…`, color: "yellow" };
473706
+ case "done":
473707
+ return { text: `✓ 已创建分支 ${title}`, color: "green" };
473708
+ case "error":
473709
+ return { text: `✗ fork 失败 · ${state.message}`, color: "red" };
473710
+ case "idle":
473711
+ default:
473712
+ return { text: SESSION_ROW_HINT };
473713
+ }
473714
+ }
473715
+ function rowSlotCost(idx, expandedIdx) {
473716
+ return idx === expandedIdx ? 2 : 1;
473717
+ }
473718
+ function slotPrefix(idx, expandedIdx, count) {
473719
+ const n = Math.min(Math.max(idx, 0), count);
473720
+ let slots = n;
473721
+ if (expandedIdx !== null && expandedIdx < n)
473722
+ slots += 1;
473723
+ return slots;
473724
+ }
473725
+ function computeRenderWindow(count, offset, expandedIdx, visibleRows) {
473726
+ if (count <= 0)
473727
+ return { start: 0, end: 0 };
473728
+ const start = Math.max(0, Math.min(offset, count - 1));
473729
+ let end = start;
473730
+ let slots = 0;
473731
+ while (end < count) {
473732
+ const cost = rowSlotCost(end, expandedIdx);
473733
+ if (slots + cost > visibleRows)
473734
+ break;
473735
+ slots += cost;
473736
+ end += 1;
473737
+ }
473738
+ return { start, end };
473739
+ }
473740
+ function clampWindowToCursor(focusedIdx, offset, expandedIdx, visibleRows, count) {
473741
+ if (count <= 0 || visibleRows <= 0)
473742
+ return 0;
473743
+ const cursor = Math.max(0, Math.min(focusedIdx, count - 1));
473744
+ let off = Math.max(0, Math.min(offset, count - 1));
473745
+ for (let guard = 0;guard <= count + 1; guard++) {
473746
+ const cStart = slotPrefix(cursor, expandedIdx, count);
473747
+ if (cStart < slotPrefix(off, expandedIdx, count))
473748
+ return cursor;
473749
+ const win = computeRenderWindow(count, off, expandedIdx, visibleRows);
473750
+ const cEnd = cStart + rowSlotCost(cursor, expandedIdx);
473751
+ if (cEnd <= slotPrefix(win.end, expandedIdx, count))
473752
+ return off;
473753
+ off += 1;
473754
+ }
473755
+ return Math.max(0, count - 1);
473756
+ }
473757
+ function getSummaryLine(log) {
473758
+ const s = (log.summary ?? "").trim();
473759
+ if (s)
473760
+ return s;
473761
+ const fp = log.firstPrompt ? stripDisplayTagsAllowEmpty(log.firstPrompt) : "";
473762
+ if (fp)
473763
+ return fp;
473764
+ return "(无摘要)";
473765
+ }
473766
+ function nextSortKey(current) {
473767
+ const i = SESSION_SORT_KEYS.indexOf(current);
473768
+ return SESSION_SORT_KEYS[(i + 1) % SESSION_SORT_KEYS.length];
473769
+ }
473770
+ function sortLogsBy(logs, key) {
473771
+ const out = [...logs];
473772
+ switch (key) {
473773
+ case "modified":
473774
+ out.sort((a, b) => b.modified.getTime() - a.modified.getTime());
473775
+ break;
473776
+ case "created":
473777
+ out.sort((a, b) => b.created.getTime() - a.created.getTime());
473778
+ break;
473779
+ case "messages":
473780
+ out.sort((a, b) => (b.messageCount ?? 0) - (a.messageCount ?? 0));
473781
+ break;
473782
+ case "title":
473783
+ out.sort((a, b) => getLogDisplayTitle(a).localeCompare(getLogDisplayTitle(b)));
473784
+ break;
473785
+ }
473786
+ return out;
473787
+ }
473788
+ function filterLogs(logs, query) {
473789
+ const q = query.trim().toLowerCase();
473790
+ if (!q)
473791
+ return logs;
473792
+ return logs.filter((l) => [
473793
+ getLogDisplayTitle(l),
473794
+ l.summary ?? "",
473795
+ l.firstPrompt ?? "",
473796
+ l.gitBranch ?? "",
473797
+ l.tag ?? "",
473798
+ l.agentSetting ?? "",
473799
+ l.sessionId ?? ""
473800
+ ].join(" ").toLowerCase().includes(q));
473801
+ }
473802
+ function makeLru(max) {
473803
+ const m = new Map;
473804
+ return {
473805
+ get: (k) => m.get(k),
473806
+ set: (k, v) => {
473807
+ if (m.has(k))
473808
+ m.delete(k);
473809
+ m.set(k, v);
473810
+ if (m.size > max) {
473811
+ const oldest = m.keys().next().value;
473812
+ if (oldest !== undefined)
473813
+ m.delete(oldest);
473814
+ }
473815
+ },
473816
+ has: (k) => m.has(k)
473817
+ };
473818
+ }
473819
+ function SessionTreeScreen({
473820
+ onBack,
473821
+ onResume,
473822
+ onAgenticSearch
473823
+ }) {
473568
473824
  const [logs, setLogs] = React134.useState([]);
473569
473825
  const [loading, setLoading] = React134.useState(true);
473570
473826
  const [focusedIdx, setFocusedIdx] = React134.useState(0);
473571
473827
  const [selectedIdx, setSelectedIdx] = React134.useState(null);
473572
473828
  const [rowStates, setRowStates] = React134.useState(new Map);
473573
- const [bottomMessage, setBottomMessage] = React134.useState(null);
473574
473829
  const [scrollOffset, setScrollOffset] = React134.useState(0);
473830
+ const [expanded, setExpanded] = React134.useState(false);
473831
+ const expandedIdx = expanded ? focusedIdx : null;
473832
+ const [searchMode, setSearchMode] = React134.useState(false);
473833
+ const [query, setQuery] = React134.useState("");
473834
+ const [sortKey, setSortKey] = React134.useState("modified");
473835
+ const [agenticResults, setAgenticResults] = React134.useState(null);
473836
+ const exactCountCacheRef = React134.useRef(makeLru(64));
473837
+ const [, bumpExactCounts] = React134.useReducer((x) => x + 1, 0);
473575
473838
  const confirmTimerRef = React134.useRef(null);
473576
473839
  const forkInFlightRef = React134.useRef(false);
473577
473840
  const fileMetaCacheRef = React134.useRef(new Map);
@@ -473585,16 +473848,7 @@ function SessionTreeScreen({ onBack, onResume }) {
473585
473848
  const loadLogs = React134.useCallback(async () => {
473586
473849
  const wt = getCurrentWorktreeSession2();
473587
473850
  const paths = wt ? [wt.worktreePath] : [getOriginalCwd()];
473588
- let l = await loadSameRepoMessageLogs(paths);
473589
- for (const log of l) {
473590
- if (log.customTitle || !log.sessionId)
473591
- continue;
473592
- const auto = deriveAutoTitle(log);
473593
- if (auto && log.fullPath) {
473594
- log.customTitle = auto;
473595
- saveCustomTitle(log.sessionId, auto, log.fullPath, "auto");
473596
- }
473597
- }
473851
+ const l = await loadSameRepoAllMessageLogs(paths);
473598
473852
  for (const log of l) {
473599
473853
  if (log.messageCount === 0 && log.fullPath) {
473600
473854
  const meta = getSessionFileMeta(log);
@@ -473610,15 +473864,6 @@ function SessionTreeScreen({ onBack, onResume }) {
473610
473864
  setLogs(l);
473611
473865
  setLoading(false);
473612
473866
  }, []);
473613
- function deriveAutoTitle(log) {
473614
- const raw = log.firstPrompt ?? log.summary ?? "";
473615
- if (!raw.trim())
473616
- return null;
473617
- const cleaned = raw.replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim();
473618
- if (!cleaned)
473619
- return null;
473620
- return truncateToVisibleWidth(cleaned, 24);
473621
- }
473622
473867
  React134.useEffect(() => {
473623
473868
  loadLogs();
473624
473869
  return () => {
@@ -473650,7 +473895,6 @@ function SessionTreeScreen({ onBack, onResume }) {
473650
473895
  } catch (err) {
473651
473896
  const message = err instanceof Error ? err.message : String(err);
473652
473897
  setRowState(target.sessionId, { kind: "error", message });
473653
- setBottomMessage(message);
473654
473898
  setTimeout(() => setRowState(target.sessionId, { kind: "idle" }), 2000);
473655
473899
  } finally {
473656
473900
  forkInFlightRef.current = false;
@@ -473660,41 +473904,90 @@ function SessionTreeScreen({ onBack, onResume }) {
473660
473904
  setFocusedIdx(idx);
473661
473905
  setSelectedIdx(null);
473662
473906
  }
473663
- function clampScrollToCursor(idx, offset, visible) {
473664
- if (visible <= 0)
473665
- return offset;
473666
- if (idx < offset)
473667
- return idx;
473668
- if (idx >= offset + visible)
473669
- return idx - visible + 1;
473670
- return offset;
473671
- }
473907
+ const computeViewLogs = React134.useCallback((base, q, sk, agentic) => {
473908
+ const local = q ? filterLogs(base, q) : base;
473909
+ const chosen = agentic && local.length === 0 ? agentic : local;
473910
+ return sortLogsBy(chosen, sk);
473911
+ }, []);
473912
+ const ensureExactCount = React134.useCallback(async (log) => {
473913
+ const sid = log.sessionId ?? "";
473914
+ if (!sid)
473915
+ return;
473916
+ if (exactCountCacheRef.current.has(sid))
473917
+ return;
473918
+ try {
473919
+ const full = isLiteLog(log) ? await loadFullLog(log) : log;
473920
+ exactCountCacheRef.current.set(sid, countVisibleMessages2(full.messages));
473921
+ bumpExactCounts();
473922
+ } catch {}
473923
+ }, [bumpExactCounts]);
473672
473924
  use_input_default((input, key, event) => {
473673
473925
  if (loading)
473674
473926
  return;
473675
- const current = logs[focusedIdx];
473927
+ const view = computeViewLogs(logs, query, sortKey, agenticResults);
473928
+ if (searchMode) {
473929
+ if (key.backspace) {
473930
+ setQuery((q) => q.slice(0, -1));
473931
+ setAgenticResults(null);
473932
+ setFocusedIdx(0);
473933
+ } else if (key.escape || input === "/") {
473934
+ setSearchMode(false);
473935
+ setQuery("");
473936
+ setAgenticResults(null);
473937
+ setFocusedIdx(0);
473938
+ } else if (key.return) {
473939
+ if (onAgenticSearch && query.trim()) {
473940
+ const localCount = filterLogs(logs, query).length;
473941
+ if (localCount === 0) {
473942
+ onAgenticSearch(query, logs).then((r) => setAgenticResults(r)).catch(() => {});
473943
+ }
473944
+ }
473945
+ } else if (input.length === 1 && !key.ctrl && !key.meta && !key.backspace && !key.return && !key.escape && input >= " ") {
473946
+ setQuery((q) => q + input);
473947
+ setAgenticResults(null);
473948
+ setFocusedIdx(0);
473949
+ }
473950
+ event.stopImmediatePropagation();
473951
+ return;
473952
+ }
473953
+ const current = view[focusedIdx];
473676
473954
  if (!current)
473677
473955
  return;
473678
473956
  const state = rowStates.get(current.sessionId ?? "")?.kind ?? "idle";
473679
473957
  if (state === "forking")
473680
473958
  return;
473959
+ if (input === "/") {
473960
+ setSearchMode(true);
473961
+ setQuery("");
473962
+ setAgenticResults(null);
473963
+ setFocusedIdx(0);
473964
+ event.stopImmediatePropagation();
473965
+ return;
473966
+ } else if (input === "s") {
473967
+ setSortKey((sk) => nextSortKey(sk));
473968
+ setFocusedIdx(0);
473969
+ setScrollOffset(0);
473970
+ event.stopImmediatePropagation();
473971
+ return;
473972
+ }
473681
473973
  const termRows = process.stdout.rows ?? 24;
473682
- const HEADER_ROWS = 3;
473683
- const FOOTER_ROWS = 2;
473684
473974
  const visibleRows = Math.max(1, termRows - HEADER_ROWS - FOOTER_ROWS);
473975
+ const clamp = (next, off) => clampWindowToCursor(next, off, expandedIdx, visibleRows, view.length);
473685
473976
  if (key.upArrow || key.wheelUp) {
473686
473977
  setSelectedIdx(null);
473687
473978
  setFocusedIdx((i) => {
473688
473979
  const next = Math.max(0, i - 1);
473689
- setScrollOffset((off) => clampScrollToCursor(next, off, visibleRows));
473980
+ setScrollOffset((off) => clamp(next, off));
473981
+ ensureExactCount(view[next]);
473690
473982
  return next;
473691
473983
  });
473692
473984
  event.stopImmediatePropagation();
473693
473985
  } else if (key.downArrow || key.wheelDown) {
473694
473986
  setSelectedIdx(null);
473695
473987
  setFocusedIdx((i) => {
473696
- const next = Math.min(logs.length - 1, i + 1);
473697
- setScrollOffset((off) => clampScrollToCursor(next, off, visibleRows));
473988
+ const next = Math.min(view.length - 1, i + 1);
473989
+ setScrollOffset((off) => clamp(next, off));
473990
+ ensureExactCount(view[next]);
473698
473991
  return next;
473699
473992
  });
473700
473993
  event.stopImmediatePropagation();
@@ -473702,18 +473995,23 @@ function SessionTreeScreen({ onBack, onResume }) {
473702
473995
  setSelectedIdx(null);
473703
473996
  setFocusedIdx((i) => {
473704
473997
  const next = Math.max(0, i - visibleRows);
473705
- setScrollOffset((off) => clampScrollToCursor(next, off, visibleRows));
473998
+ setScrollOffset((off) => clamp(next, off));
473999
+ ensureExactCount(view[next]);
473706
474000
  return next;
473707
474001
  });
473708
474002
  event.stopImmediatePropagation();
473709
474003
  } else if (key.pageDown) {
473710
474004
  setSelectedIdx(null);
473711
474005
  setFocusedIdx((i) => {
473712
- const next = Math.min(logs.length - 1, i + visibleRows);
473713
- setScrollOffset((off) => clampScrollToCursor(next, off, visibleRows));
474006
+ const next = Math.min(view.length - 1, i + visibleRows);
474007
+ setScrollOffset((off) => clamp(next, off));
474008
+ ensureExactCount(view[next]);
473714
474009
  return next;
473715
474010
  });
473716
474011
  event.stopImmediatePropagation();
474012
+ } else if (input === "x") {
474013
+ setExpanded((e) => e ? false : visibleRows >= 2);
474014
+ event.stopImmediatePropagation();
473717
474015
  } else if (key.escape || input === "q") {
473718
474016
  onBack();
473719
474017
  event.stopImmediatePropagation();
@@ -473763,26 +474061,32 @@ function SessionTreeScreen({ onBack, onResume }) {
473763
474061
  }, undefined, true, undefined, this);
473764
474062
  }
473765
474063
  const currentSessionId = getSessionId();
474064
+ const viewLogs = computeViewLogs(logs, query, sortKey, agenticResults);
473766
474065
  const termCols = process.stdout.columns ?? 100;
473767
474066
  const termRows = process.stdout.rows ?? 24;
473768
- const NARROW = termCols < 80;
474067
+ const layout = computeColumnLayout(termCols);
473769
474068
  const CURSOR_W = 2;
473770
474069
  const ICON_W = 2;
473771
- const NAME_W = NARROW ? 20 : 28;
474070
+ const NAME_W = layout.nameW;
474071
+ const CREATED_W = 11;
473772
474072
  const ACTIVE_W = 11;
473773
474073
  const BRANCH_W = 12;
473774
474074
  const MSG_W = 5;
473775
474075
  const GAP = 1;
473776
- const showBranch = !NARROW;
473777
- const showMsg = !NARROW;
473778
- const HEADER_ROWS = NARROW ? 4 : 4;
473779
- const FOOTER_ROWS = 2;
474076
+ const showCreated = layout.showCreated;
474077
+ const showBranch = layout.showBranch;
474078
+ const showMsg = layout.showMsg;
473780
474079
  const visibleRows = Math.max(1, termRows - HEADER_ROWS - FOOTER_ROWS);
473781
- const effectiveOffset = Math.min(scrollOffset, Math.max(0, logs.length - visibleRows));
473782
- const startIdx = effectiveOffset;
473783
- const endIdx = Math.min(logs.length, startIdx + visibleRows);
474080
+ const effectiveOffset = Math.min(scrollOffset, Math.max(0, viewLogs.length - visibleRows));
474081
+ const win = computeRenderWindow(viewLogs.length, effectiveOffset, expandedIdx, visibleRows);
474082
+ const startIdx = win.start;
474083
+ const endIdx = win.end;
473784
474084
  let fixedW = CURSOR_W + ICON_W + NAME_W + ACTIVE_W;
473785
474085
  let gapCount = 3;
474086
+ if (showCreated) {
474087
+ fixedW += CREATED_W;
474088
+ gapCount++;
474089
+ }
473786
474090
  if (showBranch) {
473787
474091
  fixedW += BRANCH_W;
473788
474092
  gapCount++;
@@ -473792,7 +474096,31 @@ function SessionTreeScreen({ onBack, onResume }) {
473792
474096
  gapCount++;
473793
474097
  }
473794
474098
  fixedW += gapCount * GAP;
473795
- const headerLine = " " + " " + padToVisibleWidth("名称", NAME_W) + " " + padToVisibleWidth("最近活跃", ACTIVE_W) + (showBranch ? " " + padToVisibleWidth("分支", BRANCH_W) : "") + (showMsg ? " " + padLeftToVisibleWidth("消息", MSG_W) : "");
474099
+ const sortIndicator = ` · 排序:${SESSION_SORT_LABELS[sortKey]}`;
474100
+ const filterIndicator = query ? ` · 过滤:"${query}"` : "";
474101
+ const headerLine = truncateToVisibleWidth(" " + " " + padToVisibleWidth("名称", NAME_W) + " " + (showCreated ? padToVisibleWidth("创建", CREATED_W) + " " : "") + padToVisibleWidth("最近活跃", ACTIVE_W) + (showBranch ? " " + padToVisibleWidth("分支", BRANCH_W) : "") + (showMsg ? " " + padLeftToVisibleWidth("消息", MSG_W) : "") + sortIndicator + filterIndicator, Math.max(10, termCols - 4));
474102
+ const nowMs = Date.now();
474103
+ const theme = getTheme(resolveThemeSetting(getGlobalConfig().theme));
474104
+ const STATE_PRIORITY = {
474105
+ forking: 4,
474106
+ confirm: 3,
474107
+ done: 2,
474108
+ error: 1,
474109
+ idle: 0
474110
+ };
474111
+ let activeState = { kind: "idle" };
474112
+ let activeSid = "";
474113
+ for (const [sid, s] of rowStates) {
474114
+ if (STATE_PRIORITY[s.kind] > STATE_PRIORITY[activeState.kind]) {
474115
+ activeState = s;
474116
+ activeSid = sid;
474117
+ }
474118
+ }
474119
+ const actionLog = activeSid ? logs.find((l) => l.sessionId === activeSid) : undefined;
474120
+ const actionRow = buildActionRow(activeState, actionLog ? getLogDisplayTitle(actionLog) || "untitled" : "untitled");
474121
+ const searchActionText = searchMode ? `搜索:"${query}"${onAgenticSearch ? " · enter 触发语义搜索" : ""} · esc 退出` : undefined;
474122
+ const actionText = truncateToVisibleWidth(searchActionText ?? actionRow.text, Math.max(10, termCols - 4));
474123
+ const actionColor = searchMode ? "cyan" : actionRow.color;
473796
474124
  return /* @__PURE__ */ jsx_dev_runtime424.jsxDEV(ThemedBox_default, {
473797
474125
  flexDirection: "column",
473798
474126
  paddingX: 2,
@@ -473819,15 +474147,15 @@ function SessionTreeScreen({ onBack, onResume }) {
473819
474147
  const isCurrent = sid === currentSessionId;
473820
474148
  const cached = fileMetaCacheRef.current.get(sid);
473821
474149
  const isBranch = cached?.isBranch ?? false;
473822
- const rowState = rowStates.get(sid)?.kind ?? "idle";
473823
- const rowStateObj = rowStates.get(sid);
473824
474150
  const cursor = isSelected ? "» " : isFocused ? "> " : " ";
473825
- const statusIcon = isSelected || isCurrent ? "● " : "○ ";
474151
+ const { rowColor, dotColor, dotGlyph } = agentRowColors(log, isSelected, isFocused, isCurrent, theme);
473826
474152
  const title = truncateToVisibleWidth(getLogDisplayTitle(log) || "untitled", NAME_W);
473827
- const active = formatShortActive(log.modified);
474153
+ const created = formatCreated(log.created);
474154
+ const active = formatActive(log.modified, nowMs);
473828
474155
  const branch = log.gitBranch ? truncateToVisibleWidth(log.gitBranch, BRANCH_W) : "—";
473829
- const msgCount = String(cached?.messageCount ?? log.messageCount ?? 0);
473830
- let line = cursor + statusIcon + padToVisibleWidth(title, NAME_W) + " " + padToVisibleWidth(active, ACTIVE_W);
474156
+ const exactCount = exactCountCacheRef.current.get(sid);
474157
+ const msgCount = String(exactCount ?? cached?.messageCount ?? log.messageCount ?? 0);
474158
+ let line = cursor + dotGlyph + padToVisibleWidth(title, NAME_W) + " " + (showCreated ? padToVisibleWidth(created, CREATED_W) + " " : "") + padToVisibleWidth(active, ACTIVE_W);
473831
474159
  if (showBranch)
473832
474160
  line += " " + padToVisibleWidth(branch, BRANCH_W);
473833
474161
  if (showMsg)
@@ -473841,75 +474169,115 @@ function SessionTreeScreen({ onBack, onResume }) {
473841
474169
  flags += ` #${log.tag}`;
473842
474170
  if (log.agentSetting)
473843
474171
  flags += ` @${log.agentSetting}`;
473844
- let statusColor;
473845
- if (rowState === "confirm") {
473846
- flags += " [f 确认 fork]";
473847
- statusColor = "yellow";
473848
- } else if (rowState === "forking") {
473849
- flags += " → forking…";
473850
- statusColor = "yellow";
473851
- } else if (rowState === "done") {
473852
- flags += " ✓ 已创建分支";
473853
- statusColor = "green";
473854
- } else if (rowState === "error") {
473855
- flags += " ✗ fork 失败";
473856
- statusColor = "red";
474172
+ if (log.mode === "coordinator")
474173
+ flags += " [C]";
474174
+ if (log.prNumber)
474175
+ flags += ` [PR #${log.prNumber}]`;
474176
+ if (log.fileSize)
474177
+ flags += ` (${formatFileSize3(log.fileSize)})`;
474178
+ if (log.worktreeSession?.worktreeName) {
474179
+ flags += ` [wt:${log.worktreeSession.worktreeName}]`;
473857
474180
  }
473858
474181
  const maxFlags = termCols - 2 - fixedW;
473859
474182
  if (visibleWidth(flags) > maxFlags) {
473860
474183
  flags = truncateToVisibleWidth(flags, maxFlags);
473861
474184
  }
473862
474185
  line += " " + flags;
473863
- const color = rowStateObj && rowStateObj.kind !== "idle" ? statusColor : isSelected ? "magentaBright" : isFocused ? "cyan" : undefined;
473864
- return /* @__PURE__ */ jsx_dev_runtime424.jsxDEV(ThemedBox_default, {
473865
- noSelect: true,
473866
- onClick: () => handleRowClick(i),
473867
- children: /* @__PURE__ */ jsx_dev_runtime424.jsxDEV(ThemedText, {
473868
- color,
473869
- bold: isFocused || isSelected,
473870
- wrap: "truncate",
473871
- children: line
473872
- }, undefined, false, undefined, this)
473873
- }, sid || i, false, undefined, this);
474186
+ const summaryLine = " └ " + truncateToVisibleWidth(getSummaryLine(log), termCols - 4 - 6);
474187
+ return /* @__PURE__ */ jsx_dev_runtime424.jsxDEV(React134.Fragment, {
474188
+ children: [
474189
+ /* @__PURE__ */ jsx_dev_runtime424.jsxDEV(ThemedBox_default, {
474190
+ noSelect: true,
474191
+ onClick: () => handleRowClick(i),
474192
+ children: /* @__PURE__ */ jsx_dev_runtime424.jsxDEV(ThemedBox_default, {
474193
+ flexDirection: "row",
474194
+ noSelect: true,
474195
+ children: [
474196
+ /* @__PURE__ */ jsx_dev_runtime424.jsxDEV(ThemedText, {
474197
+ color: rowColor,
474198
+ bold: isFocused || isSelected,
474199
+ children: line.slice(0, 2)
474200
+ }, undefined, false, undefined, this),
474201
+ /* @__PURE__ */ jsx_dev_runtime424.jsxDEV(ThemedText, {
474202
+ color: dotColor,
474203
+ bold: isFocused || isSelected,
474204
+ children: line.slice(2, 4)
474205
+ }, undefined, false, undefined, this),
474206
+ /* @__PURE__ */ jsx_dev_runtime424.jsxDEV(ThemedText, {
474207
+ color: rowColor,
474208
+ bold: isFocused || isSelected,
474209
+ wrap: "truncate",
474210
+ children: line.slice(4)
474211
+ }, undefined, false, undefined, this)
474212
+ ]
474213
+ }, undefined, true, undefined, this)
474214
+ }, undefined, false, undefined, this),
474215
+ i === expandedIdx && /* @__PURE__ */ jsx_dev_runtime424.jsxDEV(ThemedBox_default, {
474216
+ noSelect: true,
474217
+ children: /* @__PURE__ */ jsx_dev_runtime424.jsxDEV(ThemedText, {
474218
+ dimColor: true,
474219
+ wrap: "truncate",
474220
+ children: summaryLine
474221
+ }, undefined, false, undefined, this)
474222
+ }, undefined, false, undefined, this)
474223
+ ]
474224
+ }, sid || i, true, undefined, this);
473874
474225
  }),
473875
- logs.length === 0 && /* @__PURE__ */ jsx_dev_runtime424.jsxDEV(ThemedText, {
474226
+ viewLogs.length === 0 && /* @__PURE__ */ jsx_dev_runtime424.jsxDEV(ThemedText, {
473876
474227
  dimColor: true,
473877
- children: "No sessions found"
474228
+ children: query ? `No sessions match "${query}"` : "No sessions found"
473878
474229
  }, undefined, false, undefined, this)
473879
474230
  ]
473880
474231
  }, undefined, true, undefined, this),
473881
474232
  /* @__PURE__ */ jsx_dev_runtime424.jsxDEV(ThemedBox_default, {
473882
474233
  marginTop: 1,
473883
474234
  children: /* @__PURE__ */ jsx_dev_runtime424.jsxDEV(ThemedText, {
473884
- dimColor: true,
473885
- children: "\uD83D\uDDB1 点击移动光标 · 滚轮/↑↓ 滚动 · enter 选中/进入 · f fork · q 返回"
473886
- }, undefined, false, undefined, this)
473887
- }, undefined, false, undefined, this),
473888
- bottomMessage && /* @__PURE__ */ jsx_dev_runtime424.jsxDEV(ThemedBox_default, {
473889
- marginTop: 1,
473890
- children: /* @__PURE__ */ jsx_dev_runtime424.jsxDEV(ThemedText, {
473891
- color: "red",
474235
+ color: actionColor,
474236
+ dimColor: !actionColor,
473892
474237
  wrap: "truncate",
473893
- children: [
473894
- "✗ ",
473895
- bottomMessage
473896
- ]
473897
- }, undefined, true, undefined, this)
474238
+ children: actionText
474239
+ }, undefined, false, undefined, this)
473898
474240
  }, undefined, false, undefined, this)
473899
474241
  ]
473900
474242
  }, undefined, true, undefined, this);
473901
474243
  }
473902
- var React134, jsx_dev_runtime424;
474244
+ var React134, jsx_dev_runtime424, AGENT_COLOR_THEME_FIELD, SESSION_ROW_HINT = "\uD83D\uDDB1 点击移动光标 · 滚轮/↑↓ 滚动 · enter 选中/进入 · f fork · x 摘要 · q 返回", SESSION_SORT_KEYS, SESSION_SORT_LABELS, HEADER_ROWS = 4, FOOTER_ROWS = 2;
473903
474245
  var init_SessionTreeScreen = __esm(() => {
473904
474246
  init_ink2();
474247
+ init_config6();
474248
+ init_format2();
474249
+ init_theme();
473905
474250
  init_sessionStorage();
473906
474251
  init_worktree4();
473907
474252
  init_bootstrap();
473908
474253
  init_log5();
474254
+ init_displayTags();
473909
474255
  init_sessionStorage();
473910
474256
  init_branch();
473911
474257
  React134 = __toESM(require_react(), 1);
473912
474258
  jsx_dev_runtime424 = __toESM(require_jsx_dev_runtime(), 1);
474259
+ AGENT_COLOR_THEME_FIELD = {
474260
+ red: "red_FOR_SUBAGENTS_ONLY",
474261
+ blue: "blue_FOR_SUBAGENTS_ONLY",
474262
+ green: "green_FOR_SUBAGENTS_ONLY",
474263
+ yellow: "yellow_FOR_SUBAGENTS_ONLY",
474264
+ purple: "purple_FOR_SUBAGENTS_ONLY",
474265
+ orange: "orange_FOR_SUBAGENTS_ONLY",
474266
+ pink: "pink_FOR_SUBAGENTS_ONLY",
474267
+ cyan: "cyan_FOR_SUBAGENTS_ONLY"
474268
+ };
474269
+ SESSION_SORT_KEYS = [
474270
+ "modified",
474271
+ "created",
474272
+ "messages",
474273
+ "title"
474274
+ ];
474275
+ SESSION_SORT_LABELS = {
474276
+ modified: "最近活跃",
474277
+ created: "创建",
474278
+ messages: "消息数",
474279
+ title: "名称"
474280
+ };
473913
474281
  });
473914
474282
 
473915
474283
  // src/tui/components/DebugPanel/debugEventBuffer.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlasharness/atlascode",
3
- "version": "0.1.37",
3
+ "version": "0.1.39",
4
4
  "description": "AtlasCode — Coding Agent for the domestic computing ecosystem (Ascend NPU + domestic LLM backends)",
5
5
  "bin": {
6
6
  "atlas": "dist/cli.js",