snow-ai 0.8.9 → 0.8.10

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/bundle/cli.mjs CHANGED
@@ -46749,6 +46749,7 @@ var init_en = __esm({
46749
46749
  deeperSearchHint: "More directories not scanned \xB7 press \u2193 on the last item to search deeper",
46750
46750
  multiSelectHint: "Space toggle \xB7 Enter insert \xB7 select multiple files at once",
46751
46751
  multiSelectActiveHint: "{count} selected \xB7 Space toggle \xB7 Enter insert all",
46752
+ workspaceFilterHint: "Workspace: {filter} \xB7 searching in selected dir only",
46752
46753
  contentSearchHeader: "\u2261 Content Search",
46753
46754
  filesHeader: "\u2261 Files [{mode} \u2022 Ctrl+T]",
46754
46755
  treeMode: "Tree",
@@ -48976,6 +48977,7 @@ var init_zh = __esm({
48976
48977
  deeperSearchHint: "\u5C1A\u6709\u66F4\u6DF1\u76EE\u5F55\u672A\u626B\u63CF \xB7 \u5728\u672B\u9879\u6309 \u2193 \u7EE7\u7EED\u6DF1\u5165\u641C\u7D22",
48977
48978
  multiSelectHint: "\u7A7A\u683C \u52FE\u9009 \xB7 Enter \u4E00\u6B21\u63D2\u5165 \xB7 \u652F\u6301\u591A\u9009",
48978
48979
  multiSelectActiveHint: "\u5DF2\u52FE\u9009 {count} \u9879 \xB7 \u7A7A\u683C \u5207\u6362 \xB7 Enter \u4E00\u6B21\u63D2\u5165",
48980
+ workspaceFilterHint: "\u5DE5\u4F5C\u533A: {filter} \xB7 \u4EC5\u641C\u7D22\u6240\u9009\u76EE\u5F55",
48979
48981
  contentSearchHeader: "\u2261 \u5185\u5BB9\u641C\u7D22",
48980
48982
  filesHeader: "\u2261 \u6587\u4EF6 [{mode} \u2022 Ctrl+T]",
48981
48983
  treeMode: "\u6811\u5F62",
@@ -51202,6 +51204,7 @@ var init_zh_TW = __esm({
51202
51204
  deeperSearchHint: "\u5C1A\u6709\u66F4\u6DF1\u76EE\u9304\u672A\u6383\u63CF \xB7 \u5728\u672B\u9805\u6309 \u2193 \u7E7C\u7E8C\u6DF1\u5165\u641C\u5C0B",
51203
51205
  multiSelectHint: "\u7A7A\u767D\u9375 \u52FE\u9078 \xB7 Enter \u4E00\u6B21\u63D2\u5165 \xB7 \u652F\u63F4\u591A\u9078",
51204
51206
  multiSelectActiveHint: "\u5DF2\u52FE\u9078 {count} \u9805 \xB7 \u7A7A\u767D\u9375 \u5207\u63DB \xB7 Enter \u4E00\u6B21\u63D2\u5165",
51207
+ workspaceFilterHint: "\u5DE5\u4F5C\u5340: {filter} \xB7 \u50C5\u641C\u5C0B\u6240\u9078\u76EE\u9304",
51205
51208
  contentSearchHeader: "\u2261 \u5167\u5BB9\u641C\u5C0B",
51206
51209
  filesHeader: "\u2261 \u6A94\u6848 [{mode} \u2022 Ctrl+T]",
51207
51210
  treeMode: "\u6A39\u72C0",
@@ -157661,8 +157664,8 @@ function toGeminiImagePart(image2) {
157661
157664
  return {
157662
157665
  inlineData: {
157663
157666
  mimeType: dataUrlMatch[1] || image2.mimeType || "image/png",
157664
- data: image2.data
157665
- // 保留完整的 data URL
157667
+ data: dataUrlMatch[2]
157668
+ // 只保留纯 base64 数据
157666
157669
  }
157667
157670
  };
157668
157671
  }
@@ -157670,8 +157673,8 @@ function toGeminiImagePart(image2) {
157670
157673
  return {
157671
157674
  inlineData: {
157672
157675
  mimeType,
157673
- data: `data:${mimeType};base64,${data}`
157674
- // 补齐 data:
157676
+ data
157677
+ // base64 数据
157675
157678
  }
157676
157679
  };
157677
157680
  }
@@ -538060,7 +538063,8 @@ var init_tpsTracker = __esm({
538060
538063
  writable: true,
538061
538064
  value: {
538062
538065
  tps: 0,
538063
- peakTps: 0
538066
+ peakTps: 0,
538067
+ ttftMs: null
538064
538068
  }
538065
538069
  });
538066
538070
  Object.defineProperty(this, "tokenBuckets", {
@@ -538081,6 +538085,24 @@ var init_tpsTracker = __esm({
538081
538085
  writable: true,
538082
538086
  value: 0
538083
538087
  });
538088
+ Object.defineProperty(this, "sessionStartTime", {
538089
+ enumerable: true,
538090
+ configurable: true,
538091
+ writable: true,
538092
+ value: 0
538093
+ });
538094
+ Object.defineProperty(this, "ttftMs", {
538095
+ enumerable: true,
538096
+ configurable: true,
538097
+ writable: true,
538098
+ value: null
538099
+ });
538100
+ Object.defineProperty(this, "hasFirstToken", {
538101
+ enumerable: true,
538102
+ configurable: true,
538103
+ writable: true,
538104
+ value: false
538105
+ });
538084
538106
  Object.defineProperty(this, "snapshotRef", {
538085
538107
  enumerable: true,
538086
538108
  configurable: true,
@@ -538147,6 +538169,10 @@ var init_tpsTracker = __esm({
538147
538169
  return;
538148
538170
  }
538149
538171
  const now = Date.now();
538172
+ if (!this.hasFirstToken) {
538173
+ this.hasFirstToken = true;
538174
+ this.ttftMs = now - this.sessionStartTime;
538175
+ }
538150
538176
  const second = Math.floor(now / 1e3);
538151
538177
  if (this.currentSecond !== 0 && this.currentSecond !== second) {
538152
538178
  if (this.currentSecondTokens > 0) {
@@ -538177,9 +538203,13 @@ var init_tpsTracker = __esm({
538177
538203
  this.tokenBuckets.clear();
538178
538204
  this.currentSecond = 0;
538179
538205
  this.currentSecondTokens = 0;
538206
+ this.sessionStartTime = Date.now();
538207
+ this.ttftMs = null;
538208
+ this.hasFirstToken = false;
538180
538209
  this.snapshot = {
538181
538210
  tps: 0,
538182
- peakTps: 0
538211
+ peakTps: 0,
538212
+ ttftMs: null
538183
538213
  };
538184
538214
  this.snapshotRef = this.snapshot;
538185
538215
  }
@@ -538219,7 +538249,8 @@ var init_tpsTracker = __esm({
538219
538249
  }
538220
538250
  this.snapshot = {
538221
538251
  tps,
538222
- peakTps
538252
+ peakTps,
538253
+ ttftMs: this.ttftMs
538223
538254
  };
538224
538255
  this.snapshotRef = this.snapshot;
538225
538256
  this.emitChange();
@@ -538540,6 +538571,7 @@ async function processStreamRound(ctx) {
538540
538571
  signal: controller.signal,
538541
538572
  onRetry
538542
538573
  });
538574
+ tpsTracker.resetSession();
538543
538575
  for await (const chunk2 of streamGenerator) {
538544
538576
  if (controller.signal.aborted) {
538545
538577
  break;
@@ -558428,13 +558460,16 @@ You can also update manually:
558428
558460
  function getNpmRegistry() {
558429
558461
  const result2 = spawnSync8("npm", ["config", "get", "registry"], {
558430
558462
  encoding: "utf8",
558431
- stdio: ["ignore", "pipe", "pipe"]
558463
+ stdio: ["ignore", "pipe", "pipe"],
558464
+ // On Windows, `npm` is actually `npm.cmd`; without a shell spawnSync
558465
+ // cannot resolve it (ENOENT) and result.stdout/stderr are null.
558466
+ shell: process.platform === "win32"
558432
558467
  });
558433
558468
  if (result2.error || typeof result2.status === "number" && result2.status !== 0) {
558434
558469
  console.warn("Unable to check npm registry before updating. Continuing anyway.");
558435
558470
  return null;
558436
558471
  }
558437
- return result2.stdout.trim();
558472
+ return (result2.stdout ?? "").trim();
558438
558473
  }
558439
558474
  function warnIfUsingMirrorRegistry() {
558440
558475
  const registry2 = getNpmRegistry();
@@ -558450,7 +558485,11 @@ function runNpmStep(step) {
558450
558485
  console.log(`
558451
558486
  ${step.label}...`);
558452
558487
  const result2 = spawnSync8("npm", step.args, {
558453
- stdio: "inherit"
558488
+ stdio: "inherit",
558489
+ // On Windows, `npm` is actually `npm.cmd`; without a shell spawnSync
558490
+ // cannot resolve it (ENOENT), aborting the update with
558491
+ // "spawnSync npm ENOENT".
558492
+ shell: process.platform === "win32"
558454
558493
  });
558455
558494
  if (result2.error) {
558456
558495
  console.error(`
@@ -647445,7 +647484,8 @@ function filePickerReducer(state, action) {
647445
647484
  fileSelectedIndex: 0,
647446
647485
  fileQuery: action.query,
647447
647486
  atSymbolPosition: action.position,
647448
- searchMode: action.searchMode
647487
+ searchMode: action.searchMode,
647488
+ workspaceFilter: action.workspaceFilter !== void 0 ? action.workspaceFilter : state.workspaceFilter
647449
647489
  };
647450
647490
  case "HIDE":
647451
647491
  return {
@@ -647453,7 +647493,8 @@ function filePickerReducer(state, action) {
647453
647493
  showFilePicker: false,
647454
647494
  fileSelectedIndex: 0,
647455
647495
  fileQuery: "",
647456
- atSymbolPosition: -1
647496
+ atSymbolPosition: -1,
647497
+ workspaceFilter: null
647457
647498
  };
647458
647499
  case "SELECT_FILE":
647459
647500
  return {
@@ -647461,7 +647502,8 @@ function filePickerReducer(state, action) {
647461
647502
  showFilePicker: false,
647462
647503
  fileSelectedIndex: 0,
647463
647504
  fileQuery: "",
647464
- atSymbolPosition: -1
647505
+ atSymbolPosition: -1,
647506
+ workspaceFilter: null
647465
647507
  };
647466
647508
  case "SET_SELECTED_INDEX":
647467
647509
  return {
@@ -647484,7 +647526,8 @@ function useFilePicker(buffer, triggerUpdate) {
647484
647526
  fileQuery: "",
647485
647527
  atSymbolPosition: -1,
647486
647528
  filteredFileCount: 0,
647487
- searchMode: "file"
647529
+ searchMode: "file",
647530
+ workspaceFilter: null
647488
647531
  });
647489
647532
  const fileListRef = (0, import_react98.useRef)(null);
647490
647533
  const updateFilePickerState = (0, import_react98.useCallback)((_text, cursorPos) => {
@@ -647554,13 +647597,35 @@ function useFilePicker(buffer, triggerUpdate) {
647554
647597
  state.atSymbolPosition,
647555
647598
  state.searchMode
647556
647599
  ]);
647557
- const handleFileSelect = (0, import_react98.useCallback)(async (filePath) => {
647600
+ const handleFileSelect = (0, import_react98.useCallback)(async (filePath, displayName) => {
647558
647601
  if (state.atSymbolPosition !== -1) {
647559
647602
  const displayText = buffer.text;
647560
647603
  const cursorPos = buffer.getCursorPosition();
647604
+ const isWorkspaceSelection = state.fileQuery.startsWith(":");
647561
647605
  const beforeAt = displayText.slice(0, state.atSymbolPosition);
647562
647606
  const afterCursor = displayText.slice(cursorPos);
647563
647607
  const prefix = state.searchMode === "content" ? "@@" : "@";
647608
+ if (isWorkspaceSelection) {
647609
+ const tag2 = displayName || filePath.replace(/\/$/, "").split("/").pop() || filePath;
647610
+ const tagText = `${prefix}[${tag2}]`;
647611
+ const newText2 = beforeAt + tagText + afterCursor;
647612
+ buffer.setText(newText2);
647613
+ const targetPos2 = state.atSymbolPosition + tagText.length;
647614
+ for (let i = 0; i < targetPos2; i++) {
647615
+ if (i < buffer.text.length) {
647616
+ buffer.moveRight();
647617
+ }
647618
+ }
647619
+ dispatch({
647620
+ type: "SHOW",
647621
+ query: "",
647622
+ position: state.atSymbolPosition,
647623
+ searchMode: state.searchMode,
647624
+ workspaceFilter: filePath
647625
+ });
647626
+ triggerUpdate();
647627
+ return;
647628
+ }
647564
647629
  const isDirectoryContinuation = state.searchMode === "file" && filePath.endsWith("/");
647565
647630
  const suffix = isDirectoryContinuation ? "" : " ";
647566
647631
  const newText = beforeAt + prefix + filePath + suffix + afterCursor;
@@ -647584,7 +647649,13 @@ function useFilePicker(buffer, triggerUpdate) {
647584
647649
  }
647585
647650
  triggerUpdate();
647586
647651
  }
647587
- }, [state.atSymbolPosition, state.searchMode, buffer, triggerUpdate]);
647652
+ }, [
647653
+ state.atSymbolPosition,
647654
+ state.searchMode,
647655
+ state.fileQuery,
647656
+ buffer,
647657
+ triggerUpdate
647658
+ ]);
647588
647659
  const handleMultipleFileSelect = (0, import_react98.useCallback)(async (filePaths) => {
647589
647660
  if (filePaths.length === 0) {
647590
647661
  return;
@@ -647647,6 +647718,7 @@ function useFilePicker(buffer, triggerUpdate) {
647647
647718
  },
647648
647719
  filteredFileCount: state.filteredFileCount,
647649
647720
  searchMode: state.searchMode,
647721
+ workspaceFilter: state.workspaceFilter,
647650
647722
  updateFilePickerState,
647651
647723
  handleFileSelect,
647652
647724
  handleMultipleFileSelect,
@@ -649127,7 +649199,7 @@ var init_deleteAndBackspace = __esm({
649127
649199
 
649128
649200
  // dist/hooks/input/keyboard/handlers/pickers/filePicker.js
649129
649201
  function filePickerHandler(ctx) {
649130
- var _a20, _b14, _c6, _d4, _e2, _f, _g, _h, _i;
649202
+ var _a20, _b14, _c6, _d4, _e2, _f, _g, _h, _i, _j, _k;
649131
649203
  const { input: input2, key, options: options3 } = ctx;
649132
649204
  const { showFilePicker, filteredFileCount, fileSelectedIndex, setFileSelectedIndex, fileListRef, handleFileSelect, handleMultipleFileSelect } = options3;
649133
649205
  if (!showFilePicker)
@@ -649168,7 +649240,8 @@ function filePickerHandler(ctx) {
649168
649240
  }
649169
649241
  const selectedFile = (_i = fileListRef.current) == null ? void 0 : _i.getSelectedFile();
649170
649242
  if (selectedFile) {
649171
- handleFileSelect(selectedFile);
649243
+ const displayName = (_k = (_j = fileListRef.current) == null ? void 0 : _j.getSelectedDisplayName) == null ? void 0 : _k.call(_j);
649244
+ handleFileSelect(selectedFile, displayName ?? void 0);
649172
649245
  }
649173
649246
  }
649174
649247
  return true;
@@ -652046,7 +652119,7 @@ __export(FileList_exports, {
652046
652119
  });
652047
652120
  import fs55 from "fs";
652048
652121
  import path64 from "path";
652049
- var import_react111, SEARCH_RESULT_TTL_MS, getDisplayItemKey, getNormalizedItemPath, getLookupKey, getRelativeTreePath, getTreeDepth, compareTreeItems, buildTreeDisplayItems, getFullFilePath, AGENT_PREVIEW_VIEWPORT_HEIGHT, AGENT_PREVIEW_INDENT_WIDTH, AGENT_PREVIEW_RESERVED_COLUMNS, AGENT_PREVIEW_PANEL_PADDING, normalizeAgentPreviewContent, sliceByVisualWidth, getSafeAgentPreviewLineWidth, buildAgentPreviewLines, buildSafeAgentPreviewTitle, FileList, FileList_default;
652122
+ var import_react111, SEARCH_RESULT_TTL_MS, getDisplayItemKey, getNormalizedItemPath, getLookupKey, getRelativeTreePath, getTreeDepth, getSourceDirName, compareTreeItems, buildTreeDisplayItems, getFullFilePath, AGENT_PREVIEW_VIEWPORT_HEIGHT, AGENT_PREVIEW_INDENT_WIDTH, AGENT_PREVIEW_RESERVED_COLUMNS, AGENT_PREVIEW_PANEL_PADDING, normalizeAgentPreviewContent, sliceByVisualWidth, getSafeAgentPreviewLineWidth, buildAgentPreviewLines, buildSafeAgentPreviewTitle, FileList, FileList_default;
652050
652123
  var init_FileList = __esm({
652051
652124
  async "dist/ui/components/tools/FileList.js"() {
652052
652125
  "use strict";
@@ -652077,6 +652150,16 @@ var init_FileList = __esm({
652077
652150
  }
652078
652151
  return relativePath.split("/").filter(Boolean).length;
652079
652152
  };
652153
+ getSourceDirName = (sourceDir) => {
652154
+ if (sourceDir.startsWith("ssh://")) {
652155
+ const sshInfo = parseSSHUrl(sourceDir);
652156
+ if (sshInfo) {
652157
+ return sshInfo.path.split("/").pop() || sshInfo.host;
652158
+ }
652159
+ return sourceDir;
652160
+ }
652161
+ return path64.basename(sourceDir) || sourceDir;
652162
+ };
652080
652163
  compareTreeItems = (a, b) => {
652081
652164
  const sourceCompare = (a.sourceDir || "").localeCompare(b.sourceDir || "");
652082
652165
  if (sourceCompare !== 0) {
@@ -652202,7 +652285,7 @@ var init_FileList = __esm({
652202
652285
  buildSafeAgentPreviewTitle = (title, terminalWidth) => {
652203
652286
  return sliceByVisualWidth(title, Math.max(1, terminalWidth - 1)) || " ";
652204
652287
  };
652205
- FileList = (0, import_react111.memo)((0, import_react111.forwardRef)(({ query, selectedIndex, visible, maxItems = 10, rootPath = process.cwd(), onFilteredCountChange, searchMode = "file" }, ref) => {
652288
+ FileList = (0, import_react111.memo)((0, import_react111.forwardRef)(({ query, selectedIndex, visible, maxItems = 10, rootPath = process.cwd(), onFilteredCountChange, searchMode = "file", workspaceFilter = null }, ref) => {
652206
652289
  const { t } = useI18n();
652207
652290
  const { theme: theme14 } = useTheme();
652208
652291
  const [files, setFiles] = (0, import_react111.useState)([]);
@@ -652221,7 +652304,9 @@ var init_FileList = __esm({
652221
652304
  const [agentError, setAgentError] = (0, import_react111.useState)(null);
652222
652305
  const agentAbortRef = (0, import_react111.useRef)(null);
652223
652306
  const wasAgentModeRef = (0, import_react111.useRef)(false);
652224
- const isAgentMode = query.startsWith("??");
652307
+ const isWorkspaceSelectionMode = query.startsWith(":");
652308
+ const normalizedQuery = isWorkspaceSelectionMode ? "" : workspaceFilter && query.startsWith("[") ? query.replace(/^\[[^\]]*\]/, "") : query;
652309
+ const isAgentMode = normalizedQuery.startsWith("??");
652225
652310
  const { columns: terminalWidth } = useTerminalSize();
652226
652311
  const agentPreviewLabels = (0, import_react111.useMemo)(() => ({
652227
652312
  assistantPrefix: t.fileList.agentPreviewAssistantPrefix,
@@ -652249,7 +652334,8 @@ var init_FileList = __esm({
652249
652334
  return maxItems ? Math.min(maxItems, MAX_DISPLAY_ITEMS2) : MAX_DISPLAY_ITEMS2;
652250
652335
  }, [maxItems]);
652251
652336
  const loadFiles = (0, import_react111.useCallback)(async () => {
652252
- const workingDirs = await getWorkingDirectories();
652337
+ const allDirs = await getWorkingDirectories();
652338
+ const workingDirs = workspaceFilter ? allDirs.filter((d) => d.path === workspaceFilter || d.path === workspaceFilter.replace(/\/$/, "")) : allDirs;
652253
652339
  const collected = [];
652254
652340
  let depthLimitHit = false;
652255
652341
  const FLUSH_INTERVAL_MS = 80;
@@ -652401,7 +652487,7 @@ var init_FileList = __esm({
652401
652487
  flush(true);
652402
652488
  setHasMoreDepth(depthLimitHit);
652403
652489
  setIsLoading(false);
652404
- }, [searchDepth]);
652490
+ }, [searchDepth, workspaceFilter]);
652405
652491
  const searchFileContent = (0, import_react111.useCallback)(async (query2) => {
652406
652492
  if (!query2.trim()) {
652407
652493
  return [];
@@ -652469,6 +652555,39 @@ var init_FileList = __esm({
652469
652555
  loadFiles();
652470
652556
  }, [visible, rootPath, loadFiles]);
652471
652557
  const [allFilteredFiles, setAllFilteredFiles] = (0, import_react111.useState)([]);
652558
+ const [workspaceDirs, setWorkspaceDirs] = (0, import_react111.useState)([]);
652559
+ (0, import_react111.useEffect)(() => {
652560
+ if (!visible || !isWorkspaceSelectionMode) {
652561
+ return;
652562
+ }
652563
+ let cancelled = false;
652564
+ (async () => {
652565
+ const dirs = await getWorkingDirectories();
652566
+ if (cancelled)
652567
+ return;
652568
+ const items = dirs.map((dir) => {
652569
+ const dirName = dir.displayName || getSourceDirName(dir.path);
652570
+ return {
652571
+ name: dirName,
652572
+ path: dir.path.endsWith("/") ? dir.path : `${dir.path}/`,
652573
+ isDirectory: true,
652574
+ sourceDir: dir.path
652575
+ };
652576
+ });
652577
+ setWorkspaceDirs(items);
652578
+ })();
652579
+ return () => {
652580
+ cancelled = true;
652581
+ };
652582
+ }, [visible, isWorkspaceSelectionMode]);
652583
+ const filteredWorkspaceDirs = (0, import_react111.useMemo)(() => {
652584
+ if (!isWorkspaceSelectionMode)
652585
+ return [];
652586
+ const filterText = query.slice(1).toLowerCase();
652587
+ if (!filterText)
652588
+ return workspaceDirs;
652589
+ return workspaceDirs.filter((dir) => dir.name.toLowerCase().includes(filterText) || (dir.sourceDir || "").toLowerCase().includes(filterText));
652590
+ }, [isWorkspaceSelectionMode, query, workspaceDirs]);
652472
652591
  (0, import_react111.useEffect)(() => {
652473
652592
  if (visible) {
652474
652593
  return;
@@ -652488,20 +652607,20 @@ var init_FileList = __esm({
652488
652607
  return;
652489
652608
  }
652490
652609
  const performSearch = async () => {
652491
- if (!query.trim()) {
652610
+ if (!normalizedQuery.trim()) {
652492
652611
  setAllFilteredFiles(files);
652493
652612
  return;
652494
652613
  }
652495
652614
  if (searchMode === "content") {
652496
- const results = await searchFileContent(query);
652615
+ const results = await searchFileContent(normalizedQuery);
652497
652616
  setAllFilteredFiles(results);
652498
- if (!isLoading && results.length === 0 && query.trim().length > 0 && hasMoreDepth) {
652617
+ if (!isLoading && results.length === 0 && normalizedQuery.trim().length > 0 && hasMoreDepth) {
652499
652618
  setSearchDepth((d) => d + 3);
652500
652619
  setIsIncreasingDepth(true);
652501
652620
  setTimeout(() => setIsIncreasingDepth(false), 400);
652502
652621
  }
652503
652622
  } else {
652504
- const queryLower = query.toLowerCase().replace(/\\/g, "/");
652623
+ const queryLower = normalizedQuery.toLowerCase().replace(/\\/g, "/");
652505
652624
  const filtered = files.filter((file2) => {
652506
652625
  const fileName = file2.name.toLowerCase();
652507
652626
  const filePath = file2.path.toLowerCase().replace(/\\/g, "/");
@@ -652532,7 +652651,7 @@ var init_FileList = __esm({
652532
652651
  return a.name.localeCompare(b.name);
652533
652652
  });
652534
652653
  setAllFilteredFiles(filtered);
652535
- if (!isLoading && filtered.length === 0 && query.trim().length > 0 && hasMoreDepth) {
652654
+ if (!isLoading && filtered.length === 0 && normalizedQuery.trim().length > 0 && hasMoreDepth) {
652536
652655
  setSearchDepth((d) => d + 3);
652537
652656
  setIsIncreasingDepth(true);
652538
652657
  setTimeout(() => setIsIncreasingDepth(false), 400);
@@ -652546,7 +652665,7 @@ var init_FileList = __esm({
652546
652665
  return () => clearTimeout(timer2);
652547
652666
  }, [
652548
652667
  files,
652549
- query,
652668
+ normalizedQuery,
652550
652669
  searchMode,
652551
652670
  searchFileContent,
652552
652671
  isLoading,
@@ -652555,7 +652674,7 @@ var init_FileList = __esm({
652555
652674
  agentResults
652556
652675
  ]);
652557
652676
  (0, import_react111.useEffect)(() => {
652558
- const agentQuery = isAgentMode ? query.slice(2).trim() : "";
652677
+ const agentQuery = isAgentMode ? normalizedQuery.slice(2).trim() : "";
652559
652678
  if (!visible || !isAgentMode || !agentQuery) {
652560
652679
  if (wasAgentModeRef.current) {
652561
652680
  wasAgentModeRef.current = false;
@@ -652589,7 +652708,8 @@ var init_FileList = __esm({
652589
652708
  setAgentRound(0);
652590
652709
  setAgentPreviewContent("");
652591
652710
  try {
652592
- const workingDirs = await getWorkingDirectories();
652711
+ const allDirs = await getWorkingDirectories();
652712
+ const workingDirs = workspaceFilter ? allDirs.filter((d) => d.path === workspaceFilter || d.path === workspaceFilter.replace(/\/$/, "")) : allDirs;
652593
652713
  const generator = fileSearchAgent.search(agentQuery, searchMode, workingDirs, controller.signal, agentPreviewLabels);
652594
652714
  for await (const event of generator) {
652595
652715
  if (controller.signal.aborted) {
@@ -652621,7 +652741,14 @@ var init_FileList = __esm({
652621
652741
  clearTimeout(timer2);
652622
652742
  controller.abort();
652623
652743
  };
652624
- }, [query, searchMode, visible, isAgentMode, agentPreviewLabels]);
652744
+ }, [
652745
+ normalizedQuery,
652746
+ searchMode,
652747
+ visible,
652748
+ isAgentMode,
652749
+ agentPreviewLabels,
652750
+ workspaceFilter
652751
+ ]);
652625
652752
  const displayItems = (0, import_react111.useMemo)(() => {
652626
652753
  if (isAgentMode || searchMode === "content") {
652627
652754
  return allFilteredFiles.map((file2) => ({
@@ -652632,7 +652759,7 @@ var init_FileList = __esm({
652632
652759
  }));
652633
652760
  }
652634
652761
  if (displayMode === "tree") {
652635
- return buildTreeDisplayItems(allFilteredFiles, files, query);
652762
+ return buildTreeDisplayItems(allFilteredFiles, files, normalizedQuery);
652636
652763
  }
652637
652764
  return allFilteredFiles.map((file2) => ({
652638
652765
  file: file2,
@@ -652645,7 +652772,7 @@ var init_FileList = __esm({
652645
652772
  files,
652646
652773
  displayMode,
652647
652774
  searchMode,
652648
- query,
652775
+ normalizedQuery,
652649
652776
  isAgentMode
652650
652777
  ]);
652651
652778
  const normalizedSelectedIndex = (0, import_react111.useMemo)(() => {
@@ -652679,9 +652806,15 @@ var init_FileList = __esm({
652679
652806
  const hiddenBelowCount = Math.max(0, displayItems.length - fileWindow.endIndex);
652680
652807
  (0, import_react111.useEffect)(() => {
652681
652808
  if (onFilteredCountChange) {
652682
- onFilteredCountChange(displayItems.length);
652809
+ const count = isWorkspaceSelectionMode ? filteredWorkspaceDirs.length : displayItems.length;
652810
+ onFilteredCountChange(count);
652683
652811
  }
652684
- }, [displayItems.length, onFilteredCountChange]);
652812
+ }, [
652813
+ displayItems.length,
652814
+ onFilteredCountChange,
652815
+ isWorkspaceSelectionMode,
652816
+ filteredWorkspaceDirs.length
652817
+ ]);
652685
652818
  const resolveInsertionPath = (0, import_react111.useCallback)((entry) => {
652686
652819
  if (!entry) {
652687
652820
  return null;
@@ -652698,12 +652831,25 @@ var init_FileList = __esm({
652698
652831
  }, [rootPath, searchMode]);
652699
652832
  (0, import_react111.useImperativeHandle)(ref, () => ({
652700
652833
  getSelectedFile: () => {
652834
+ if (isWorkspaceSelectionMode) {
652835
+ const wsIndex = Math.min(selectedIndex, Math.max(0, filteredWorkspaceDirs.length - 1));
652836
+ const wsItem = filteredWorkspaceDirs[wsIndex];
652837
+ return wsItem ? wsItem.path : null;
652838
+ }
652701
652839
  const selectedEntry = displayItems[normalizedSelectedIndex];
652702
652840
  if (!selectedEntry) {
652703
652841
  return null;
652704
652842
  }
652705
652843
  return resolveInsertionPath(selectedEntry);
652706
652844
  },
652845
+ getSelectedDisplayName: () => {
652846
+ if (isWorkspaceSelectionMode) {
652847
+ const wsIndex = Math.min(selectedIndex, Math.max(0, filteredWorkspaceDirs.length - 1));
652848
+ const wsItem = filteredWorkspaceDirs[wsIndex];
652849
+ return wsItem ? wsItem.name : null;
652850
+ }
652851
+ return null;
652852
+ },
652707
652853
  toggleDisplayMode: () => {
652708
652854
  if (searchMode !== "file") {
652709
652855
  return false;
@@ -652776,7 +652922,10 @@ var init_FileList = __esm({
652776
652922
  isIncreasingDepth,
652777
652923
  displayMode,
652778
652924
  selectedKeys,
652779
- resolveInsertionPath
652925
+ resolveInsertionPath,
652926
+ isWorkspaceSelectionMode,
652927
+ filteredWorkspaceDirs,
652928
+ selectedIndex
652780
652929
  ]);
652781
652930
  const displaySelectedIndex = filteredFiles.length === 0 ? -1 : normalizedSelectedIndex - fileWindow.startIndex;
652782
652931
  const selectedFileFullPath = (0, import_react111.useMemo)(() => {
@@ -652789,6 +652938,47 @@ var init_FileList = __esm({
652789
652938
  if (!visible) {
652790
652939
  return null;
652791
652940
  }
652941
+ if (isWorkspaceSelectionMode) {
652942
+ const wsItems = filteredWorkspaceDirs;
652943
+ const wsSelectedIndex = Math.min(selectedIndex, Math.max(0, wsItems.length - 1));
652944
+ const wsWindow = wsItems.slice(0, effectiveMaxItems);
652945
+ if (wsItems.length === 0) {
652946
+ return import_react111.default.createElement(
652947
+ Box_default,
652948
+ { paddingX: 1, marginTop: 1 },
652949
+ import_react111.default.createElement(Text, { color: theme14.colors.menuSecondary, dimColor: true }, t.fileList.noFilesFound)
652950
+ );
652951
+ }
652952
+ return import_react111.default.createElement(
652953
+ Box_default,
652954
+ { paddingX: 1, marginTop: 1, flexDirection: "column" },
652955
+ import_react111.default.createElement(
652956
+ Box_default,
652957
+ { marginBottom: 1 },
652958
+ import_react111.default.createElement(Text, { color: theme14.colors.menuInfo, bold: true }, t.fileList.workspaceFilterHint.replace("{filter}", query.slice(1) || "*"))
652959
+ ),
652960
+ wsWindow.map((item, index) => {
652961
+ const isSelected = index === wsSelectedIndex;
652962
+ return import_react111.default.createElement(
652963
+ Text,
652964
+ { key: `${item.sourceDir}::${item.path}`, backgroundColor: isSelected ? theme14.colors.menuSelected : void 0, color: isSelected ? theme14.colors.menuNormal : theme14.colors.warning, wrap: "truncate-end" },
652965
+ isSelected ? "\u276F " : " ",
652966
+ "\u25C7 ",
652967
+ item.name
652968
+ );
652969
+ }),
652970
+ wsItems.length > effectiveMaxItems && import_react111.default.createElement(
652971
+ Box_default,
652972
+ { marginTop: 1 },
652973
+ import_react111.default.createElement(
652974
+ Text,
652975
+ { color: theme14.colors.menuSecondary, dimColor: true },
652976
+ t.commandPanel.scrollHint,
652977
+ t.commandPanel.moreBelow.replace("{count}", (wsItems.length - effectiveMaxItems).toString())
652978
+ )
652979
+ )
652980
+ );
652981
+ }
652792
652982
  if (isAgentMode) {
652793
652983
  if (isAgentSearching && displayItems.length === 0) {
652794
652984
  const agentSearchStatus = t.fileList.agentSearching.replace("{round}", String(agentRound || 1));
@@ -652824,12 +653014,12 @@ var init_FileList = __esm({
652824
653014
  );
652825
653015
  }
652826
653016
  }
652827
- const stillSearching = isLoading || isIncreasingDepth || query.trim().length > 0 && hasMoreDepth;
653017
+ const stillSearching = isLoading || isIncreasingDepth || normalizedQuery.trim().length > 0 && hasMoreDepth;
652828
653018
  if (!isAgentMode && stillSearching && displayItems.length === 0) {
652829
653019
  return import_react111.default.createElement(
652830
653020
  Box_default,
652831
653021
  { paddingX: 1, marginTop: 1 },
652832
- import_react111.default.createElement(Text, { color: "blue", dimColor: true }, isIncreasingDepth || query.trim().length > 0 && hasMoreDepth ? t.fileList.searchingDeeper.replace("{depth}", searchDepth.toString()) : t.fileList.loadingFiles)
653022
+ import_react111.default.createElement(Text, { color: "blue", dimColor: true }, isIncreasingDepth || normalizedQuery.trim().length > 0 && hasMoreDepth ? t.fileList.searchingDeeper.replace("{depth}", searchDepth.toString()) : t.fileList.loadingFiles)
652833
653023
  );
652834
653024
  }
652835
653025
  if (displayItems.length === 0) {
@@ -652853,6 +653043,11 @@ var init_FileList = __esm({
652853
653043
  displayItems.length > effectiveMaxItems && `(${normalizedSelectedIndex + 1}/${displayItems.length})`
652854
653044
  )
652855
653045
  ),
653046
+ workspaceFilter && !isWorkspaceSelectionMode && import_react111.default.createElement(
653047
+ Box_default,
653048
+ null,
653049
+ import_react111.default.createElement(Text, { color: theme14.colors.menuInfo, bold: true }, t.fileList.workspaceFilterHint.replace("{filter}", getSourceDirName(workspaceFilter)))
653050
+ ),
652856
653051
  filteredFiles.map((item, index) => {
652857
653052
  const file2 = item.file;
652858
653053
  const isSelected = index === displaySelectedIndex;
@@ -654009,7 +654204,7 @@ function ChatInput({ onSubmit, onCommand, placeholder = "Type your message...",
654009
654204
  const options3 = getCommandArgsOptions(cmd, text2);
654010
654205
  return { commandName: cmd, options: options3 };
654011
654206
  }, [buffer, buffer.text]);
654012
- const { showFilePicker, setShowFilePicker, fileSelectedIndex, setFileSelectedIndex, fileQuery, setFileQuery, atSymbolPosition, setAtSymbolPosition, filteredFileCount, searchMode, updateFilePickerState, handleFileSelect, handleMultipleFileSelect, handleFilteredCountChange, fileListRef } = useFilePicker(buffer, triggerUpdate);
654207
+ const { showFilePicker, setShowFilePicker, fileSelectedIndex, setFileSelectedIndex, fileQuery, setFileQuery, atSymbolPosition, setAtSymbolPosition, filteredFileCount, searchMode, workspaceFilter, updateFilePickerState, handleFileSelect, handleMultipleFileSelect, handleFilteredCountChange, fileListRef } = useFilePicker(buffer, triggerUpdate);
654013
654208
  const { showHistoryMenu, setShowHistoryMenu, historySelectedIndex, setHistorySelectedIndex, escapeKeyCount, setEscapeKeyCount, escapeKeyTimer, getUserMessages, handleHistorySelect, currentHistoryIndex, navigateHistoryUp, navigateHistoryDown, resetHistoryNavigation, saveToHistory } = useHistoryNavigation(buffer, triggerUpdate, chatHistory, onHistorySelect);
654014
654209
  const { showAgentPicker, setShowAgentPicker, agentSelectedIndex, setAgentSelectedIndex, updateAgentPickerState, getFilteredAgents, handleAgentSelect } = useAgentPicker(buffer, triggerUpdate);
654015
654210
  const { showTodoPicker, setShowTodoPicker, todoSelectedIndex, setTodoSelectedIndex, todos, selectedTodos, toggleTodoSelection, confirmTodoSelection, isLoading: todoIsLoading, searchQuery: todoSearchQuery, setSearchQuery: setTodoSearchQuery, totalTodoCount } = useTodoPicker(buffer, triggerUpdate, process.cwd());
@@ -654528,7 +654723,7 @@ function ChatInput({ onSubmit, onCommand, placeholder = "Type your message...",
654528
654723
  import_react120.default.createElement(
654529
654724
  import_react120.Suspense,
654530
654725
  { fallback: null },
654531
- import_react120.default.createElement(FileList2, { ref: fileListRef, query: fileQuery, selectedIndex: fileSelectedIndex, visible: showFilePicker, maxItems: 10, rootPath: process.cwd(), onFilteredCountChange: handleFilteredCountChange, searchMode })
654726
+ import_react120.default.createElement(FileList2, { ref: fileListRef, query: fileQuery, selectedIndex: fileSelectedIndex, visible: showFilePicker, maxItems: 10, rootPath: process.cwd(), onFilteredCountChange: handleFilteredCountChange, searchMode, workspaceFilter })
654532
654727
  ),
654533
654728
  import_react120.default.createElement(
654534
654729
  import_react120.Suspense,
@@ -655250,7 +655445,8 @@ function StatusLine({ yoloMode = false, planMode = false, vulnerabilityHuntingMo
655250
655445
  speedometer: {
655251
655446
  enabled: tpsTracker.isActive(),
655252
655447
  tps: tpsSnapshot.tps,
655253
- peakTps: tpsSnapshot.peakTps
655448
+ peakTps: tpsSnapshot.peakTps,
655449
+ ttftMs: tpsSnapshot.ttftMs
655254
655450
  }
655255
655451
  }
655256
655452
  };
@@ -655474,8 +655670,13 @@ function StatusLine({ yoloMode = false, planMode = false, vulnerabilityHuntingMo
655474
655670
  });
655475
655671
  }
655476
655672
  if (tpsTracker.isActive() && !isBuiltinOverridden(BUILTIN_STATUSLINE_IDS.speedometer)) {
655673
+ let speedometerText = `\u23F1 ${tpsSnapshot.tps} tok/s`;
655674
+ if (tpsSnapshot.ttftMs !== null) {
655675
+ const ttftSec = (tpsSnapshot.ttftMs / 1e3).toFixed(1);
655676
+ speedometerText += ` \xB7 ttft ${ttftSec}s`;
655677
+ }
655477
655678
  statusItems.push({
655478
- text: `\u23F1 ${tpsSnapshot.tps} tok/s`,
655679
+ text: speedometerText,
655479
655680
  color: tpsSnapshot.tps > 0 ? theme14.colors.cyan : theme14.colors.menuSecondary
655480
655681
  });
655481
655682
  }
@@ -655539,7 +655740,18 @@ function StatusLine({ yoloMode = false, planMode = false, vulnerabilityHuntingMo
655539
655740
  import_react123.default.createElement(Text, { color: tpsSnapshot.tps > 0 ? theme14.colors.cyan : theme14.colors.menuSecondary, bold: true }, tpsSnapshot.tps),
655540
655741
  " tok/s",
655541
655742
  " \xB7 peak ",
655542
- import_react123.default.createElement(Text, { color: theme14.colors.warning }, tpsSnapshot.peakTps)
655743
+ import_react123.default.createElement(Text, { color: theme14.colors.warning }, tpsSnapshot.peakTps),
655744
+ tpsSnapshot.ttftMs !== null && import_react123.default.createElement(
655745
+ import_react123.default.Fragment,
655746
+ null,
655747
+ " \xB7 ttft ",
655748
+ import_react123.default.createElement(
655749
+ Text,
655750
+ { color: theme14.colors.menuInfo },
655751
+ (tpsSnapshot.ttftMs / 1e3).toFixed(1),
655752
+ "s"
655753
+ )
655754
+ )
655543
655755
  )
655544
655756
  ),
655545
655757
  yoloMode && !isBuiltinOverridden(BUILTIN_STATUSLINE_IDS.modeYolo) && import_react123.default.createElement(
@@ -655839,8 +656051,11 @@ function LoadingIndicator({ isStreaming, isStopping, isSaving, isCompressing, is
655839
656051
  ].join("|");
655840
656052
  const previousStreamActivityMarkerRef = (0, import_react126.useRef)(null);
655841
656053
  const lastStreamActivityElapsedSecondsRef = (0, import_react126.useRef)(elapsedSeconds);
656054
+ const wasStreamingRef = (0, import_react126.useRef)(false);
656055
+ const isStreamingStarted = isStreaming && !wasStreamingRef.current;
656056
+ wasStreamingRef.current = isStreaming;
655842
656057
  const shouldIgnoreStreamDelay = isCompressing || isAutoCompressing;
655843
- if (!isStreaming || shouldIgnoreStreamDelay || previousStreamActivityMarkerRef.current !== streamActivityMarker) {
656058
+ if (!isStreaming || shouldIgnoreStreamDelay || isStreamingStarted || previousStreamActivityMarkerRef.current !== streamActivityMarker) {
655844
656059
  previousStreamActivityMarkerRef.current = streamActivityMarker;
655845
656060
  lastStreamActivityElapsedSecondsRef.current = elapsedSeconds;
655846
656061
  }
@@ -692854,7 +693069,7 @@ var require_package4 = __commonJS({
692854
693069
  "package.json"(exports2, module2) {
692855
693070
  module2.exports = {
692856
693071
  name: "snow-ai",
692857
- version: "0.8.9",
693072
+ version: "0.8.10",
692858
693073
  description: "Agentic coding in your terminal",
692859
693074
  license: "MIT",
692860
693075
  bin: {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-ai",
3
- "version": "0.8.9",
3
+ "version": "0.8.10",
4
4
  "description": "Agentic coding in your terminal",
5
5
  "license": "MIT",
6
6
  "bin": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-ai",
3
- "version": "0.8.9",
3
+ "version": "0.8.10",
4
4
  "description": "Agentic coding in your terminal",
5
5
  "license": "MIT",
6
6
  "bin": {