snow-ai 0.8.9 → 0.8.11

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
  }
@@ -502936,6 +502939,7 @@ Output: ${combinedOutput}`
502936
502939
  var hookResultInterpreter_exports = {};
502937
502940
  __export(hookResultInterpreter_exports, {
502938
502941
  buildErrorDetails: () => buildErrorDetails,
502942
+ extractHookProvidedConfirmation: () => extractHookProvidedConfirmation,
502939
502943
  findFirstFailedCommand: () => findFirstFailedCommand,
502940
502944
  interpretHookResult: () => interpretHookResult
502941
502945
  });
@@ -502962,6 +502966,56 @@ function interpretHookResult(hookType, hookResult, originalContent) {
502962
502966
  const strategy = hookStrategies[hookType];
502963
502967
  return strategy.interpret(hookResult, originalContent);
502964
502968
  }
502969
+ function extractHookProvidedConfirmation(hookResult) {
502970
+ for (const result2 of hookResult.results) {
502971
+ if (result2.type !== "command" || !result2.success || !result2.output) {
502972
+ continue;
502973
+ }
502974
+ const rawOutput = result2.output.trim();
502975
+ if (!rawOutput) {
502976
+ continue;
502977
+ }
502978
+ try {
502979
+ const parsed = JSON.parse(rawOutput);
502980
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.result === "string") {
502981
+ const hookResultValue = parsed.result.toLowerCase();
502982
+ if (hookResultValue === "approve") {
502983
+ return "approve";
502984
+ }
502985
+ if (hookResultValue === "approve_always") {
502986
+ return "approve_always";
502987
+ }
502988
+ if (hookResultValue === "reject") {
502989
+ return "reject";
502990
+ }
502991
+ if (hookResultValue === "reject_with_reply") {
502992
+ return {
502993
+ type: "reject_with_reply",
502994
+ reason: typeof parsed.reason === "string" ? parsed.reason : "Rejected by toolConfirmation hook"
502995
+ };
502996
+ }
502997
+ }
502998
+ } catch {
502999
+ const lowerOutput = rawOutput.toLowerCase().trim();
503000
+ if (lowerOutput === "approve" || lowerOutput === "approve_once") {
503001
+ return "approve";
503002
+ }
503003
+ if (lowerOutput === "approve_always") {
503004
+ return "approve_always";
503005
+ }
503006
+ if (lowerOutput === "reject") {
503007
+ return "reject";
503008
+ }
503009
+ if (lowerOutput === "reject_with_reply") {
503010
+ return {
503011
+ type: "reject_with_reply",
503012
+ reason: "Rejected by toolConfirmation hook"
503013
+ };
503014
+ }
503015
+ }
503016
+ }
503017
+ return null;
503018
+ }
502965
503019
  var init_hookResultInterpreter = __esm({
502966
503020
  "dist/utils/execution/hookResultInterpreter.js"() {
502967
503021
  "use strict";
@@ -535166,6 +535220,9 @@ async function listJsonCommandsRecursively(dir, prefixPath) {
535166
535220
  for (const entry of entries) {
535167
535221
  const entryPath = join32(dir, entry.name);
535168
535222
  if (entry.isDirectory()) {
535223
+ if (entry.name.startsWith(".")) {
535224
+ continue;
535225
+ }
535169
535226
  const childPrefix = prefixPath ? `${prefixPath}/${entry.name}` : entry.name;
535170
535227
  results.push(...await listJsonCommandsRecursively(entryPath, childPrefix));
535171
535228
  continue;
@@ -535189,8 +535246,15 @@ async function loadCustomCommandFromFile(entry, defaultLocation) {
535189
535246
  try {
535190
535247
  const content = await readFile2(entry.filePath, "utf-8");
535191
535248
  const cmd = JSON.parse(content);
535249
+ if (typeof cmd.command !== "string" || cmd.command.length === 0) {
535250
+ return null;
535251
+ }
535252
+ if (cmd.type !== "execute" && cmd.type !== "prompt" && cmd.type !== "panel") {
535253
+ return null;
535254
+ }
535192
535255
  cmd.name = entry.inferredCommandName;
535193
- cmd.description = cmd.description || cmd.command;
535256
+ const rawDesc = cmd.description ?? cmd.command;
535257
+ cmd.description = typeof rawDesc === "string" ? rawDesc : "";
535194
535258
  if (!cmd.location) {
535195
535259
  cmd.location = defaultLocation;
535196
535260
  }
@@ -535267,7 +535331,7 @@ function checkCommandExists(name, location, projectRoot) {
535267
535331
  return false;
535268
535332
  }
535269
535333
  }
535270
- async function saveCustomCommand(name, command, type, description, location = "global", projectRoot) {
535334
+ async function saveCustomCommand(name, command, type, description, location = "global", projectRoot, argsHint, argsOptions) {
535271
535335
  if (isCommandNameConflict(name)) {
535272
535336
  throw new Error(`Command name "${name}" conflicts with an existing built-in or custom command`);
535273
535337
  }
@@ -535275,7 +535339,15 @@ async function saveCustomCommand(name, command, type, description, location = "g
535275
535339
  const dir = getCustomCommandsDir(location, projectRoot);
535276
535340
  const filePath = getCommandJsonFilePath(dir, name);
535277
535341
  await mkdir3(dirname12(filePath), { recursive: true });
535278
- const data = { name, command, type, description, location };
535342
+ const data = {
535343
+ name,
535344
+ command,
535345
+ type,
535346
+ description,
535347
+ location,
535348
+ ...argsHint !== void 0 ? { argsHint } : {},
535349
+ ...argsOptions !== void 0 ? { argsOptions } : {}
535350
+ };
535279
535351
  await writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
535280
535352
  }
535281
535353
  function getCustomCommands() {
@@ -535290,7 +535362,7 @@ function normalizeImportedCustomCommand(value, location) {
535290
535362
  if (!isRecord3(value)) {
535291
535363
  return null;
535292
535364
  }
535293
- const { name, command, type, description } = value;
535365
+ const { name, command, type, description, argsHint, argsOptions } = value;
535294
535366
  if (typeof name !== "string" || typeof command !== "string" || type !== "execute" && type !== "prompt" && type !== "panel") {
535295
535367
  return null;
535296
535368
  }
@@ -535303,6 +535375,10 @@ function normalizeImportedCustomCommand(value, location) {
535303
535375
  command,
535304
535376
  type,
535305
535377
  ...typeof description === "string" ? { description } : {},
535378
+ ...typeof argsHint === "string" ? { argsHint } : {},
535379
+ ...Array.isArray(argsOptions) ? {
535380
+ argsOptions: argsOptions.filter((v) => typeof v === "string")
535381
+ } : {},
535306
535382
  location
535307
535383
  };
535308
535384
  }
@@ -535332,6 +535408,16 @@ async function deleteCustomCommand(name, location = "global", projectRoot) {
535332
535408
  unregisterCommand(name);
535333
535409
  customCommandsCache = customCommandsCache.filter((cmd) => cmd.name !== name);
535334
535410
  }
535411
+ function applyArgsToCommand(command, args2) {
535412
+ const trimmedArgs = args2 == null ? void 0 : args2.trim();
535413
+ if (command.includes("$ARGUMENTS")) {
535414
+ return command.split("$ARGUMENTS").join(trimmedArgs ?? "");
535415
+ }
535416
+ if (!trimmedArgs) {
535417
+ return command;
535418
+ }
535419
+ return `${command} ${trimmedArgs}`;
535420
+ }
535335
535421
  async function registerCustomCommands(projectRoot) {
535336
535422
  const customCommands = await loadCustomCommands(projectRoot);
535337
535423
  customCommandsCache = customCommands;
@@ -535348,7 +535434,7 @@ async function registerCustomCommands(projectRoot) {
535348
535434
  };
535349
535435
  }
535350
535436
  if (cmd.type === "execute") {
535351
- const finalCommand = args2 ? `${cmd.command} ${args2}` : cmd.command;
535437
+ const finalCommand = applyArgsToCommand(cmd.command, args2);
535352
535438
  return {
535353
535439
  success: true,
535354
535440
  message: `Executing: ${finalCommand}`,
@@ -535364,7 +535450,7 @@ async function registerCustomCommands(projectRoot) {
535364
535450
  prompt: cmd.command
535365
535451
  };
535366
535452
  }
535367
- const finalPrompt = args2 ? `${cmd.command} ${args2}` : cmd.command;
535453
+ const finalPrompt = applyArgsToCommand(cmd.command, args2);
535368
535454
  return {
535369
535455
  success: true,
535370
535456
  message: `Sending to AI: ${finalPrompt}`,
@@ -537068,7 +537154,7 @@ var init_encoderManager = __esm({
537068
537154
  });
537069
537155
 
537070
537156
  // dist/utils/core/todoPreprocessor.js
537071
- function formatTodoContext(todos) {
537157
+ function formatTodoContext(todos, options3) {
537072
537158
  if (todos.length === 0) {
537073
537159
  return "";
537074
537160
  }
@@ -537085,6 +537171,11 @@ function formatTodoContext(todos) {
537085
537171
  '**Important**: Update TODO status immediately after completing each task using todo-manage with action "update".',
537086
537172
  ""
537087
537173
  ];
537174
+ const hasIncomplete = todos.some((t) => t.status !== "completed");
537175
+ if ((options3 == null ? void 0 : options3.isFollowUp) && hasIncomplete) {
537176
+ const toolName = options3.ultraMode ? "todo-ultra" : "todo-manage";
537177
+ lines.push(`**Action Required**: This is a follow-up message with an active TODO list. You MUST call \`${toolName}({action: "get"})\` first (paired with an action tool) to restore your TODO session context before doing any other work. This ensures the TODO list stays in sync and you continue using the TODO workflow for this task.`, "");
537178
+ }
537088
537179
  return lines.join("\n");
537089
537180
  }
537090
537181
  var init_todoPreprocessor = __esm({
@@ -537130,14 +537221,18 @@ async function initializeConversationSession(planMode, vulnerabilityHuntingMode,
537130
537221
  ${companionPromptAddon}` : baseSystemPrompt
537131
537222
  }
537132
537223
  ];
537224
+ const session = sessionManager.getCurrentSession();
537133
537225
  if (existingTodoList && existingTodoList.todos.length > 0) {
537134
- const todoContext = formatTodoContext(existingTodoList.todos);
537226
+ const isFollowUp = !!session && session.messages.length > 0;
537227
+ const todoContext = formatTodoContext(existingTodoList.todos, {
537228
+ isFollowUp,
537229
+ ultraMode: existingTodoList.ultraMode
537230
+ });
537135
537231
  conversationMessages.push({
537136
537232
  role: "user",
537137
537233
  content: todoContext
537138
537234
  });
537139
537235
  }
537140
- const session = sessionManager.getCurrentSession();
537141
537236
  if (session && session.messages.length > 0) {
537142
537237
  const filteredMessages = session.messages.filter((msg) => !msg.subAgentInternal);
537143
537238
  conversationMessages.push(...filteredMessages);
@@ -538060,7 +538155,8 @@ var init_tpsTracker = __esm({
538060
538155
  writable: true,
538061
538156
  value: {
538062
538157
  tps: 0,
538063
- peakTps: 0
538158
+ peakTps: 0,
538159
+ ttftMs: null
538064
538160
  }
538065
538161
  });
538066
538162
  Object.defineProperty(this, "tokenBuckets", {
@@ -538081,6 +538177,24 @@ var init_tpsTracker = __esm({
538081
538177
  writable: true,
538082
538178
  value: 0
538083
538179
  });
538180
+ Object.defineProperty(this, "sessionStartTime", {
538181
+ enumerable: true,
538182
+ configurable: true,
538183
+ writable: true,
538184
+ value: 0
538185
+ });
538186
+ Object.defineProperty(this, "ttftMs", {
538187
+ enumerable: true,
538188
+ configurable: true,
538189
+ writable: true,
538190
+ value: null
538191
+ });
538192
+ Object.defineProperty(this, "hasFirstToken", {
538193
+ enumerable: true,
538194
+ configurable: true,
538195
+ writable: true,
538196
+ value: false
538197
+ });
538084
538198
  Object.defineProperty(this, "snapshotRef", {
538085
538199
  enumerable: true,
538086
538200
  configurable: true,
@@ -538147,6 +538261,10 @@ var init_tpsTracker = __esm({
538147
538261
  return;
538148
538262
  }
538149
538263
  const now = Date.now();
538264
+ if (!this.hasFirstToken) {
538265
+ this.hasFirstToken = true;
538266
+ this.ttftMs = now - this.sessionStartTime;
538267
+ }
538150
538268
  const second = Math.floor(now / 1e3);
538151
538269
  if (this.currentSecond !== 0 && this.currentSecond !== second) {
538152
538270
  if (this.currentSecondTokens > 0) {
@@ -538177,9 +538295,13 @@ var init_tpsTracker = __esm({
538177
538295
  this.tokenBuckets.clear();
538178
538296
  this.currentSecond = 0;
538179
538297
  this.currentSecondTokens = 0;
538298
+ this.sessionStartTime = Date.now();
538299
+ this.ttftMs = null;
538300
+ this.hasFirstToken = false;
538180
538301
  this.snapshot = {
538181
538302
  tps: 0,
538182
- peakTps: 0
538303
+ peakTps: 0,
538304
+ ttftMs: null
538183
538305
  };
538184
538306
  this.snapshotRef = this.snapshot;
538185
538307
  }
@@ -538219,7 +538341,8 @@ var init_tpsTracker = __esm({
538219
538341
  }
538220
538342
  this.snapshot = {
538221
538343
  tps,
538222
- peakTps
538344
+ peakTps,
538345
+ ttftMs: this.ttftMs
538223
538346
  };
538224
538347
  this.snapshotRef = this.snapshot;
538225
538348
  this.emitChange();
@@ -538540,6 +538663,7 @@ async function processStreamRound(ctx) {
538540
538663
  signal: controller.signal,
538541
538664
  onRetry
538542
538665
  });
538666
+ tpsTracker.resetSession();
538543
538667
  for await (const chunk2 of streamGenerator) {
538544
538668
  if (controller.signal.aborted) {
538545
538669
  break;
@@ -544024,6 +544148,39 @@ function extractMultimodalContent(result2) {
544024
544148
  textContent: JSON.stringify(result2)
544025
544149
  };
544026
544150
  }
544151
+ function formatAskUserAnswer(selected, customInput) {
544152
+ const selectedText = Array.isArray(selected) ? selected.join(", ") : selected;
544153
+ return customInput ? `${selectedText}: ${customInput}` : selectedText;
544154
+ }
544155
+ function extractHookProvidedAnswer(hookResult) {
544156
+ for (const result2 of hookResult.results) {
544157
+ if (result2.type !== "command" || !result2.success || !result2.output) {
544158
+ continue;
544159
+ }
544160
+ const rawOutput = result2.output.trim();
544161
+ if (!rawOutput) {
544162
+ continue;
544163
+ }
544164
+ try {
544165
+ const parsed = JSON.parse(rawOutput);
544166
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && parsed.selected !== void 0) {
544167
+ const selected = parsed.selected;
544168
+ if (typeof selected === "string" || Array.isArray(selected) && selected.every((s) => typeof s === "string")) {
544169
+ return {
544170
+ selected,
544171
+ customInput: typeof parsed.customInput === "string" ? parsed.customInput : void 0
544172
+ };
544173
+ }
544174
+ }
544175
+ if (Array.isArray(parsed) && parsed.every((s) => typeof s === "string")) {
544176
+ return { selected: parsed };
544177
+ }
544178
+ } catch {
544179
+ return { selected: rawOutput };
544180
+ }
544181
+ }
544182
+ return null;
544183
+ }
544027
544184
  async function executeToolCall(toolCall, abortSignal, onTokenUpdate, onSubAgentMessage, requestToolConfirmation, isToolAutoApproved, yoloMode, addToAlwaysApproved, onUserInteractionNeeded, sessionId, workingDirectory) {
544028
544185
  let result2;
544029
544186
  let executionError = null;
@@ -544054,11 +544211,12 @@ async function executeToolCall(toolCall, abortSignal, onTokenUpdate, onSubAgentM
544054
544211
  try {
544055
544212
  const args2 = safeParseToolArguments(toolCall.function.arguments);
544056
544213
  recordToolContent(telemetry.span, "tool.input", args2, telemetry.metricAttributes);
544214
+ let beforeHookResult = null;
544057
544215
  try {
544058
544216
  const { unifiedHooksExecutor: unifiedHooksExecutor2 } = await Promise.resolve().then(() => (init_unifiedHooksExecutor(), unifiedHooksExecutor_exports));
544059
544217
  const { interpretHookResult: interpretHookResult2 } = await Promise.resolve().then(() => (init_hookResultInterpreter(), hookResultInterpreter_exports));
544060
- const hookResult = await unifiedHooksExecutor2.executeHooks("beforeToolCall", { toolName: toolCall.function.name, args: args2 });
544061
- const interpreted = interpretHookResult2("beforeToolCall", hookResult);
544218
+ beforeHookResult = await unifiedHooksExecutor2.executeHooks("beforeToolCall", { toolName: toolCall.function.name, args: args2 });
544219
+ const interpreted = interpretHookResult2("beforeToolCall", beforeHookResult);
544062
544220
  if (interpreted.action === "block") {
544063
544221
  result2 = {
544064
544222
  tool_call_id: toolCall.id,
@@ -544072,6 +544230,22 @@ async function executeToolCall(toolCall, abortSignal, onTokenUpdate, onSubAgentM
544072
544230
  } catch (error42) {
544073
544231
  console.warn("Failed to execute beforeToolCall hook:", error42);
544074
544232
  }
544233
+ if (toolCall.function.name === "askuser-ask_question" && beforeHookResult) {
544234
+ const hookAnswer = extractHookProvidedAnswer(beforeHookResult);
544235
+ if (hookAnswer) {
544236
+ const answerText = formatAskUserAnswer(hookAnswer.selected, hookAnswer.customInput);
544237
+ result2 = {
544238
+ tool_call_id: toolCall.id,
544239
+ role: "tool",
544240
+ content: JSON.stringify({
544241
+ answer: answerText,
544242
+ selected: hookAnswer.selected,
544243
+ customInput: hookAnswer.customInput
544244
+ })
544245
+ };
544246
+ return result2;
544247
+ }
544248
+ }
544075
544249
  if (toolCall.function.name.startsWith("team-")) {
544076
544250
  const teamToolName = toolCall.function.name.substring("team-".length);
544077
544251
  const teamArgs = args2;
@@ -544254,7 +544428,7 @@ ${injectedSummary}`
544254
544428
  };
544255
544429
  return result2;
544256
544430
  }
544257
- const answerText = response.customInput ? `${Array.isArray(response.selected) ? response.selected.join(", ") : response.selected}: ${response.customInput}` : Array.isArray(response.selected) ? response.selected.join(", ") : response.selected;
544431
+ const answerText = formatAskUserAnswer(response.selected, response.customInput);
544258
544432
  result2 = {
544259
544433
  tool_call_id: toolCall.id,
544260
544434
  role: "tool",
@@ -558428,13 +558602,16 @@ You can also update manually:
558428
558602
  function getNpmRegistry() {
558429
558603
  const result2 = spawnSync8("npm", ["config", "get", "registry"], {
558430
558604
  encoding: "utf8",
558431
- stdio: ["ignore", "pipe", "pipe"]
558605
+ stdio: ["ignore", "pipe", "pipe"],
558606
+ // On Windows, `npm` is actually `npm.cmd`; without a shell spawnSync
558607
+ // cannot resolve it (ENOENT) and result.stdout/stderr are null.
558608
+ shell: process.platform === "win32"
558432
558609
  });
558433
558610
  if (result2.error || typeof result2.status === "number" && result2.status !== 0) {
558434
558611
  console.warn("Unable to check npm registry before updating. Continuing anyway.");
558435
558612
  return null;
558436
558613
  }
558437
- return result2.stdout.trim();
558614
+ return (result2.stdout ?? "").trim();
558438
558615
  }
558439
558616
  function warnIfUsingMirrorRegistry() {
558440
558617
  const registry2 = getNpmRegistry();
@@ -558450,7 +558627,11 @@ function runNpmStep(step) {
558450
558627
  console.log(`
558451
558628
  ${step.label}...`);
558452
558629
  const result2 = spawnSync8("npm", step.args, {
558453
- stdio: "inherit"
558630
+ stdio: "inherit",
558631
+ // On Windows, `npm` is actually `npm.cmd`; without a shell spawnSync
558632
+ // cannot resolve it (ENOENT), aborting the update with
558633
+ // "spawnSync npm ENOENT".
558634
+ shell: process.platform === "win32"
558454
558635
  });
558455
558636
  if (result2.error) {
558456
558637
  console.error(`
@@ -642327,8 +642508,8 @@ var init_HookErrorDisplay = __esm({
642327
642508
  });
642328
642509
 
642329
642510
  // dist/utils/ui/skillMask.js
642330
- function isSkillHeaderLine(line) {
642331
- return line.startsWith("# Skill:");
642511
+ function findSkillHeaderIndex(line) {
642512
+ return line.indexOf("# Skill:");
642332
642513
  }
642333
642514
  function splitSkillEndRemainder(line) {
642334
642515
  const trimmed = line.trimStart();
@@ -642340,7 +642521,9 @@ function isSkillEndLine(line) {
642340
642521
  return line.trim() === "# Skill End";
642341
642522
  }
642342
642523
  function parseSkillIdFromHeader(line) {
642343
- return line.replace(/^# Skill:\s*/i, "").trim() || "unknown";
642524
+ const headerIndex = findSkillHeaderIndex(line);
642525
+ const headerPart = headerIndex >= 0 ? line.slice(headerIndex) : line;
642526
+ return headerPart.replace(/^# Skill:\s*/i, "").trim() || "unknown";
642344
642527
  }
642345
642528
  function maskSkillInjectedText(text2) {
642346
642529
  if (!text2)
@@ -642351,18 +642534,25 @@ function maskSkillInjectedText(text2) {
642351
642534
  let i = 0;
642352
642535
  while (i < lines.length) {
642353
642536
  const line = lines[i] ?? "";
642354
- if (!isSkillHeaderLine(line)) {
642537
+ const headerIndex = findSkillHeaderIndex(line);
642538
+ if (headerIndex < 0) {
642355
642539
  out.push(line);
642356
642540
  i++;
642357
642541
  continue;
642358
642542
  }
642543
+ if (headerIndex > 0) {
642544
+ const prefix = line.slice(0, headerIndex).trimEnd();
642545
+ if (prefix.length > 0) {
642546
+ out.push(prefix);
642547
+ }
642548
+ }
642359
642549
  const skillId = parseSkillIdFromHeader(line);
642360
642550
  skillIds.push(skillId);
642361
642551
  out.push(`[Skill:${skillId}]`);
642362
642552
  i++;
642363
642553
  while (i < lines.length) {
642364
642554
  const next = lines[i] ?? "";
642365
- if (isSkillHeaderLine(next))
642555
+ if (findSkillHeaderIndex(next) >= 0)
642366
642556
  break;
642367
642557
  const remainder = splitSkillEndRemainder(next);
642368
642558
  if (remainder !== null) {
@@ -642388,8 +642578,8 @@ var init_skillMask = __esm({
642388
642578
  });
642389
642579
 
642390
642580
  // dist/utils/ui/gitLineMask.js
642391
- function isGitLineHeaderLine(line) {
642392
- return line.startsWith("# GitLine:");
642581
+ function findGitLineHeaderIndex(line) {
642582
+ return line.indexOf("# GitLine:");
642393
642583
  }
642394
642584
  function isGitLineEndLine(line) {
642395
642585
  return line.trim() === "# GitLine End";
@@ -642401,7 +642591,9 @@ function splitGitLineEndRemainder(line) {
642401
642591
  return trimmed.slice("# GitLine End".length);
642402
642592
  }
642403
642593
  function parseGitLineIdFromHeader(line) {
642404
- const id = line.replace(/^# GitLine:\s*/i, "").trim() || "unknown";
642594
+ const headerIndex = findGitLineHeaderIndex(line);
642595
+ const headerPart = headerIndex >= 0 ? line.slice(headerIndex) : line;
642596
+ const id = headerPart.replace(/^# GitLine:\s*/i, "").trim() || "unknown";
642405
642597
  return id.slice(0, 8);
642406
642598
  }
642407
642599
  function maskGitLineText(text2) {
@@ -642413,18 +642605,25 @@ function maskGitLineText(text2) {
642413
642605
  let i = 0;
642414
642606
  while (i < lines.length) {
642415
642607
  const line = lines[i] ?? "";
642416
- if (!isGitLineHeaderLine(line)) {
642608
+ const headerIndex = findGitLineHeaderIndex(line);
642609
+ if (headerIndex < 0) {
642417
642610
  out.push(line);
642418
642611
  i++;
642419
642612
  continue;
642420
642613
  }
642614
+ if (headerIndex > 0) {
642615
+ const prefix = line.slice(0, headerIndex).trimEnd();
642616
+ if (prefix.length > 0) {
642617
+ out.push(prefix);
642618
+ }
642619
+ }
642421
642620
  const gitLineId = parseGitLineIdFromHeader(line);
642422
642621
  gitLineIds.push(gitLineId);
642423
642622
  out.push(`[GitLine:${gitLineId}]`);
642424
642623
  i++;
642425
642624
  while (i < lines.length) {
642426
642625
  const next = lines[i] ?? "";
642427
- if (isGitLineHeaderLine(next))
642626
+ if (findGitLineHeaderIndex(next) >= 0)
642428
642627
  break;
642429
642628
  const remainder = splitGitLineEndRemainder(next);
642430
642629
  if (remainder !== null) {
@@ -645988,6 +646187,10 @@ var init_textBuffer = __esm({
645988
646187
  while ((m = bracketPattern.exec(text2)) !== null) {
645989
646188
  pushRange(m.index, m.index + m[0].length, "bracket");
645990
646189
  }
646190
+ const wsTagPattern = /@\[[^\]]+\]/g;
646191
+ while ((m = wsTagPattern.exec(text2)) !== null) {
646192
+ pushRange(m.index, m.index + m[0].length, "wsTag");
646193
+ }
645991
646194
  const agentPattern = /(^|\s)(#[A-Za-z][\w-]*)/g;
645992
646195
  while ((m = agentPattern.exec(text2)) !== null) {
645993
646196
  const leading = m[1] ?? "";
@@ -646562,7 +646765,7 @@ var init_textBuffer = __esm({
646562
646765
  const hasTrailingSpace = codePoints[closePos + 1] === " ";
646563
646766
  const placeholderText = hasTrailingSpace ? `${baseText} ` : baseText;
646564
646767
  const end = hasTrailingSpace ? closePos + 2 : closePos + 1;
646565
- if (placeholderText.match(/^\[Paste \d+ lines #\d+\] ?$/) || placeholderText.match(/^\[image #\d+\] ?$/) || placeholderText === "[Pasting...]" || placeholderText === "[Pasting...] " || placeholderText.match(/^\[Skill:[^\]]+\] ?$/)) {
646768
+ if (placeholderText.match(/^\[Paste \d+ lines #\d+\] ?$/) || placeholderText.match(/^\[image #\d+\] ?$/) || placeholderText === "[Pasting...]" || placeholderText === "[Pasting...] " || placeholderText.match(/^\[Skill:[^\]]+\] ?$/) || placeholderText.match(/^\[GitLine:[^\]]+\] ?$/)) {
646566
646769
  return { start: openPos, end };
646567
646770
  }
646568
646771
  }
@@ -647035,7 +647238,23 @@ function getCommandArgsOptions(commandName, inputText) {
647035
647238
  if (commandName === "buddy" && /^\/buddy\s+profile\s*$/.test(inputText ?? "")) {
647036
647239
  return getBuddyProfileArgOptions();
647037
647240
  }
647038
- return COMMAND_ARGS_OPTIONS[commandName] ?? [];
647241
+ const staticOptions = COMMAND_ARGS_OPTIONS[commandName];
647242
+ if (staticOptions) {
647243
+ return staticOptions;
647244
+ }
647245
+ const customCmd = getCustomCommands().find((cmd) => cmd.name === commandName);
647246
+ if ((customCmd == null ? void 0 : customCmd.argsOptions) && customCmd.argsOptions.length > 0) {
647247
+ return customCmd.argsOptions;
647248
+ }
647249
+ return [];
647250
+ }
647251
+ function getCommandArgsHint(commandName) {
647252
+ const staticHint = COMMAND_ARGS_HINTS[commandName];
647253
+ if (staticHint) {
647254
+ return staticHint;
647255
+ }
647256
+ const customCmd = getCustomCommands().find((cmd) => cmd.name === commandName);
647257
+ return (customCmd == null ? void 0 : customCmd.argsHint) ?? "";
647039
647258
  }
647040
647259
  function useCommandPanel(buffer, isProcessing = false) {
647041
647260
  const { t } = useI18n();
@@ -647445,7 +647664,8 @@ function filePickerReducer(state, action) {
647445
647664
  fileSelectedIndex: 0,
647446
647665
  fileQuery: action.query,
647447
647666
  atSymbolPosition: action.position,
647448
- searchMode: action.searchMode
647667
+ searchMode: action.searchMode,
647668
+ workspaceFilter: action.workspaceFilter !== void 0 ? action.workspaceFilter : state.workspaceFilter
647449
647669
  };
647450
647670
  case "HIDE":
647451
647671
  return {
@@ -647453,7 +647673,8 @@ function filePickerReducer(state, action) {
647453
647673
  showFilePicker: false,
647454
647674
  fileSelectedIndex: 0,
647455
647675
  fileQuery: "",
647456
- atSymbolPosition: -1
647676
+ atSymbolPosition: -1,
647677
+ workspaceFilter: null
647457
647678
  };
647458
647679
  case "SELECT_FILE":
647459
647680
  return {
@@ -647461,7 +647682,8 @@ function filePickerReducer(state, action) {
647461
647682
  showFilePicker: false,
647462
647683
  fileSelectedIndex: 0,
647463
647684
  fileQuery: "",
647464
- atSymbolPosition: -1
647685
+ atSymbolPosition: -1,
647686
+ workspaceFilter: null
647465
647687
  };
647466
647688
  case "SET_SELECTED_INDEX":
647467
647689
  return {
@@ -647484,7 +647706,8 @@ function useFilePicker(buffer, triggerUpdate) {
647484
647706
  fileQuery: "",
647485
647707
  atSymbolPosition: -1,
647486
647708
  filteredFileCount: 0,
647487
- searchMode: "file"
647709
+ searchMode: "file",
647710
+ workspaceFilter: null
647488
647711
  });
647489
647712
  const fileListRef = (0, import_react98.useRef)(null);
647490
647713
  const updateFilePickerState = (0, import_react98.useCallback)((_text, cursorPos) => {
@@ -647554,24 +647777,38 @@ function useFilePicker(buffer, triggerUpdate) {
647554
647777
  state.atSymbolPosition,
647555
647778
  state.searchMode
647556
647779
  ]);
647557
- const handleFileSelect = (0, import_react98.useCallback)(async (filePath) => {
647780
+ const handleFileSelect = (0, import_react98.useCallback)(async (filePath, displayName) => {
647558
647781
  if (state.atSymbolPosition !== -1) {
647559
647782
  const displayText = buffer.text;
647560
647783
  const cursorPos = buffer.getCursorPosition();
647784
+ const isWorkspaceSelection = state.fileQuery.startsWith(":");
647561
647785
  const beforeAt = displayText.slice(0, state.atSymbolPosition);
647562
647786
  const afterCursor = displayText.slice(cursorPos);
647563
647787
  const prefix = state.searchMode === "content" ? "@@" : "@";
647788
+ if (isWorkspaceSelection) {
647789
+ const tag2 = displayName || filePath.replace(/\/$/, "").split("/").pop() || filePath;
647790
+ const tagText = `${prefix}[${tag2}]`;
647791
+ const newText2 = beforeAt + tagText + afterCursor;
647792
+ buffer.setText(newText2);
647793
+ const targetPos2 = state.atSymbolPosition + tagText.length;
647794
+ buffer.setCursorPosition(targetPos2);
647795
+ dispatch({
647796
+ type: "SHOW",
647797
+ query: "",
647798
+ position: state.atSymbolPosition,
647799
+ searchMode: state.searchMode,
647800
+ workspaceFilter: filePath
647801
+ });
647802
+ triggerUpdate();
647803
+ return;
647804
+ }
647564
647805
  const isDirectoryContinuation = state.searchMode === "file" && filePath.endsWith("/");
647565
647806
  const suffix = isDirectoryContinuation ? "" : " ";
647566
647807
  const newText = beforeAt + prefix + filePath + suffix + afterCursor;
647567
647808
  buffer.setText(newText);
647568
647809
  const insertedLength = prefix.length + filePath.length + suffix.length;
647569
647810
  const targetPos = state.atSymbolPosition + insertedLength;
647570
- for (let i = 0; i < targetPos; i++) {
647571
- if (i < buffer.text.length) {
647572
- buffer.moveRight();
647573
- }
647574
- }
647811
+ buffer.setCursorPosition(targetPos);
647575
647812
  if (isDirectoryContinuation) {
647576
647813
  dispatch({
647577
647814
  type: "SHOW",
@@ -647584,7 +647821,13 @@ function useFilePicker(buffer, triggerUpdate) {
647584
647821
  }
647585
647822
  triggerUpdate();
647586
647823
  }
647587
- }, [state.atSymbolPosition, state.searchMode, buffer, triggerUpdate]);
647824
+ }, [
647825
+ state.atSymbolPosition,
647826
+ state.searchMode,
647827
+ state.fileQuery,
647828
+ buffer,
647829
+ triggerUpdate
647830
+ ]);
647588
647831
  const handleMultipleFileSelect = (0, import_react98.useCallback)(async (filePaths) => {
647589
647832
  if (filePaths.length === 0) {
647590
647833
  return;
@@ -647601,11 +647844,7 @@ function useFilePicker(buffer, triggerUpdate) {
647601
647844
  const newText = beforeAt + insertedSegment + afterCursor;
647602
647845
  buffer.setText(newText);
647603
647846
  const targetPos = state.atSymbolPosition + insertedSegment.length;
647604
- for (let i = 0; i < targetPos; i++) {
647605
- if (i < buffer.text.length) {
647606
- buffer.moveRight();
647607
- }
647608
- }
647847
+ buffer.setCursorPosition(targetPos);
647609
647848
  dispatch({ type: "SELECT_FILE" });
647610
647849
  triggerUpdate();
647611
647850
  }, [state.atSymbolPosition, state.searchMode, buffer, triggerUpdate]);
@@ -647647,6 +647886,7 @@ function useFilePicker(buffer, triggerUpdate) {
647647
647886
  },
647648
647887
  filteredFileCount: state.filteredFileCount,
647649
647888
  searchMode: state.searchMode,
647889
+ workspaceFilter: state.workspaceFilter,
647650
647890
  updateFilePickerState,
647651
647891
  handleFileSelect,
647652
647892
  handleMultipleFileSelect,
@@ -648553,7 +648793,7 @@ function argsPickerHandler(ctx) {
648553
648793
  if (selected) {
648554
648794
  const value = getCommandArgOptionValue(selected);
648555
648795
  const text2 = buffer.text;
648556
- const hasTrailingSpace = /^\/[a-zA-Z0-9_-]+(?:\s+\S+)*\s+$/.test(text2);
648796
+ const hasTrailingSpace = /^\/[a-zA-Z0-9_:-]+(?:\s+\S+)*\s+$/.test(text2);
648557
648797
  const suffix = hasTrailingSpace ? value : " " + value;
648558
648798
  buffer.insert(suffix);
648559
648799
  buffer.setCursorPosition(buffer.text.length);
@@ -649127,7 +649367,7 @@ var init_deleteAndBackspace = __esm({
649127
649367
 
649128
649368
  // dist/hooks/input/keyboard/handlers/pickers/filePicker.js
649129
649369
  function filePickerHandler(ctx) {
649130
- var _a20, _b14, _c6, _d4, _e2, _f, _g, _h, _i;
649370
+ var _a20, _b14, _c6, _d4, _e2, _f, _g, _h, _i, _j, _k;
649131
649371
  const { input: input2, key, options: options3 } = ctx;
649132
649372
  const { showFilePicker, filteredFileCount, fileSelectedIndex, setFileSelectedIndex, fileListRef, handleFileSelect, handleMultipleFileSelect } = options3;
649133
649373
  if (!showFilePicker)
@@ -649168,7 +649408,8 @@ function filePickerHandler(ctx) {
649168
649408
  }
649169
649409
  const selectedFile = (_i = fileListRef.current) == null ? void 0 : _i.getSelectedFile();
649170
649410
  if (selectedFile) {
649171
- handleFileSelect(selectedFile);
649411
+ const displayName = (_k = (_j = fileListRef.current) == null ? void 0 : _j.getSelectedDisplayName) == null ? void 0 : _k.call(_j);
649412
+ handleFileSelect(selectedFile, displayName ?? void 0);
649172
649413
  }
649173
649414
  }
649174
649415
  return true;
@@ -649344,7 +649585,7 @@ function tabArgsPickerHandler(ctx) {
649344
649585
  const { showCommands, showFilePicker, showArgsPicker, setShowArgsPicker, setArgsSelectedIndex } = options3;
649345
649586
  if (key.tab && !showCommands && !showFilePicker && !showArgsPicker) {
649346
649587
  const text2 = buffer.text;
649347
- const rootMatch = text2.match(/^\/([a-zA-Z0-9_-]+)\s*$/);
649588
+ const rootMatch = text2.match(/^\/([a-zA-Z0-9_:-]+)\s*$/);
649348
649589
  const buddyProfileMatch = text2.match(/^\/(buddy)\s+profile\s*$/);
649349
649590
  const inlineTrigger = findInlineCommandTrigger(text2, buffer.getCursorPosition());
649350
649591
  const cmdName = (rootMatch == null ? void 0 : rootMatch[1]) ?? (buddyProfileMatch == null ? void 0 : buddyProfileMatch[1]) ?? (inlineTrigger == null ? void 0 : inlineTrigger.query) ?? "";
@@ -650395,21 +650636,46 @@ function createStagedEntry(fileCount) {
650395
650636
  fileCount
650396
650637
  };
650397
650638
  }
650639
+ function createUnstagedEntry(fileCount) {
650640
+ return {
650641
+ sha: UNSTAGED_ENTRY_SHA,
650642
+ subject: "Unstaged changes",
650643
+ authorName: "",
650644
+ dateIso: "",
650645
+ kind: "unstaged",
650646
+ fileCount
650647
+ };
650648
+ }
650398
650649
  function buildInjectedGitLineText(commit, gitRoot) {
650399
- const patch = commit.kind === "staged" ? reviewAgent.getStagedDiff(gitRoot).trim() : reviewAgent.getCommitPatch(gitRoot, commit.sha).trim();
650400
650650
  if (commit.kind === "staged") {
650651
+ const patch2 = reviewAgent.getStagedDiff(gitRoot).trim();
650401
650652
  return [
650402
650653
  "# GitLine: staged",
650403
650654
  "Type: staged",
650404
650655
  commit.fileCount !== void 0 ? `Files: ${commit.fileCount}` : void 0,
650405
650656
  "",
650406
650657
  "```git",
650407
- patch,
650658
+ patch2,
650659
+ "```",
650660
+ "# GitLine End",
650661
+ ""
650662
+ ].filter((line) => line !== void 0).join("\n");
650663
+ }
650664
+ if (commit.kind === "unstaged") {
650665
+ const patch2 = reviewAgent.getUnstagedDiff(gitRoot).trim();
650666
+ return [
650667
+ "# GitLine: unstaged",
650668
+ "Type: unstaged",
650669
+ commit.fileCount !== void 0 ? `Files: ${commit.fileCount}` : void 0,
650670
+ "",
650671
+ "```git",
650672
+ patch2,
650408
650673
  "```",
650409
650674
  "# GitLine End",
650410
650675
  ""
650411
650676
  ].filter((line) => line !== void 0).join("\n");
650412
650677
  }
650678
+ const patch = reviewAgent.getCommitPatch(gitRoot, commit.sha).trim();
650413
650679
  return [
650414
650680
  `# GitLine: ${commit.sha}`,
650415
650681
  `Commit: ${commit.sha}`,
@@ -650429,6 +650695,7 @@ function useGitLinePicker(buffer, triggerUpdate) {
650429
650695
  const [gitLineSelectedIndex, setGitLineSelectedIndex] = (0, import_react106.useState)(0);
650430
650696
  const [commits, setCommits] = (0, import_react106.useState)([]);
650431
650697
  const [stagedEntry, setStagedEntry] = (0, import_react106.useState)(null);
650698
+ const [unstagedEntry, setUnstagedEntry] = (0, import_react106.useState)(null);
650432
650699
  const [selectedCommits, setSelectedCommits] = (0, import_react106.useState)(/* @__PURE__ */ new Set());
650433
650700
  const [isLoading, setIsLoading] = (0, import_react106.useState)(false);
650434
650701
  const [isLoadingMore, setIsLoadingMore] = (0, import_react106.useState)(false);
@@ -650439,8 +650706,15 @@ function useGitLinePicker(buffer, triggerUpdate) {
650439
650706
  const [gitRoot, setGitRoot] = (0, import_react106.useState)(null);
650440
650707
  const [insertionRange, setInsertionRange] = (0, import_react106.useState)(null);
650441
650708
  const allCommits = (0, import_react106.useMemo)(() => {
650442
- return stagedEntry ? [stagedEntry, ...commits] : commits;
650443
- }, [commits, stagedEntry]);
650709
+ const entries = [];
650710
+ if (stagedEntry) {
650711
+ entries.push(stagedEntry);
650712
+ }
650713
+ if (unstagedEntry) {
650714
+ entries.push(unstagedEntry);
650715
+ }
650716
+ return [...entries, ...commits];
650717
+ }, [commits, stagedEntry, unstagedEntry]);
650444
650718
  const filteredCommits = (0, import_react106.useMemo)(() => {
650445
650719
  const query = searchQuery.trim().toLowerCase();
650446
650720
  if (!query) {
@@ -650456,6 +650730,9 @@ function useGitLinePicker(buffer, triggerUpdate) {
650456
650730
  if (commit.kind === "staged") {
650457
650731
  searchableFields.push("staged", "staged changes");
650458
650732
  }
650733
+ if (commit.kind === "unstaged") {
650734
+ searchableFields.push("unstaged", "unstaged changes");
650735
+ }
650459
650736
  return searchableFields.some((field) => field.toLowerCase().includes(query));
650460
650737
  });
650461
650738
  }, [allCommits, searchQuery]);
@@ -650469,6 +650746,7 @@ function useGitLinePicker(buffer, triggerUpdate) {
650469
650746
  setGitRoot(null);
650470
650747
  setCommits([]);
650471
650748
  setStagedEntry(null);
650749
+ setUnstagedEntry(null);
650472
650750
  setHasMore(false);
650473
650751
  setSkip(0);
650474
650752
  setError(gitCheck.error || "Not a git repository");
@@ -650478,6 +650756,7 @@ function useGitLinePicker(buffer, triggerUpdate) {
650478
650756
  const result2 = reviewAgent.listCommitsPaginated(gitCheck.gitRoot, 0, PAGE_SIZE);
650479
650757
  setGitRoot(gitCheck.gitRoot);
650480
650758
  setStagedEntry(status.hasStaged ? createStagedEntry(status.stagedFileCount) : null);
650759
+ setUnstagedEntry(status.hasUnstaged ? createUnstagedEntry(status.unstagedFileCount) : null);
650481
650760
  setCommits(result2.commits.map((commit) => ({
650482
650761
  ...commit,
650483
650762
  kind: "commit"
@@ -650489,6 +650768,7 @@ function useGitLinePicker(buffer, triggerUpdate) {
650489
650768
  setGitRoot(null);
650490
650769
  setCommits([]);
650491
650770
  setStagedEntry(null);
650771
+ setUnstagedEntry(null);
650492
650772
  setHasMore(false);
650493
650773
  setSkip(0);
650494
650774
  setError(loadError instanceof Error ? loadError.message : "Failed to load git commits");
@@ -650563,6 +650843,7 @@ function useGitLinePicker(buffer, triggerUpdate) {
650563
650843
  setSkip(0);
650564
650844
  setIsLoadingMore(false);
650565
650845
  setStagedEntry(null);
650846
+ setUnstagedEntry(null);
650566
650847
  setInsertionRange(null);
650567
650848
  triggerUpdate();
650568
650849
  }, [triggerUpdate]);
@@ -650616,6 +650897,7 @@ function useGitLinePicker(buffer, triggerUpdate) {
650616
650897
  setSkip(0);
650617
650898
  setIsLoadingMore(false);
650618
650899
  setStagedEntry(null);
650900
+ setUnstagedEntry(null);
650619
650901
  setInsertionRange(null);
650620
650902
  triggerUpdate();
650621
650903
  }, [
@@ -650649,7 +650931,7 @@ function useGitLinePicker(buffer, triggerUpdate) {
650649
650931
  loadMoreGitLineCommits
650650
650932
  };
650651
650933
  }
650652
- var import_react106, PAGE_SIZE, STAGED_ENTRY_SHA;
650934
+ var import_react106, PAGE_SIZE, STAGED_ENTRY_SHA, UNSTAGED_ENTRY_SHA;
650653
650935
  var init_useGitLinePicker = __esm({
650654
650936
  "dist/hooks/picker/useGitLinePicker.js"() {
650655
650937
  "use strict";
@@ -650657,6 +650939,7 @@ var init_useGitLinePicker = __esm({
650657
650939
  init_reviewAgent();
650658
650940
  PAGE_SIZE = 30;
650659
650941
  STAGED_ENTRY_SHA = "staged";
650942
+ UNSTAGED_ENTRY_SHA = "unstaged";
650660
650943
  }
650661
650944
  });
650662
650945
 
@@ -652046,7 +652329,7 @@ __export(FileList_exports, {
652046
652329
  });
652047
652330
  import fs55 from "fs";
652048
652331
  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;
652332
+ 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
652333
  var init_FileList = __esm({
652051
652334
  async "dist/ui/components/tools/FileList.js"() {
652052
652335
  "use strict";
@@ -652077,6 +652360,16 @@ var init_FileList = __esm({
652077
652360
  }
652078
652361
  return relativePath.split("/").filter(Boolean).length;
652079
652362
  };
652363
+ getSourceDirName = (sourceDir) => {
652364
+ if (sourceDir.startsWith("ssh://")) {
652365
+ const sshInfo = parseSSHUrl(sourceDir);
652366
+ if (sshInfo) {
652367
+ return sshInfo.path.split("/").pop() || sshInfo.host;
652368
+ }
652369
+ return sourceDir;
652370
+ }
652371
+ return path64.basename(sourceDir) || sourceDir;
652372
+ };
652080
652373
  compareTreeItems = (a, b) => {
652081
652374
  const sourceCompare = (a.sourceDir || "").localeCompare(b.sourceDir || "");
652082
652375
  if (sourceCompare !== 0) {
@@ -652202,7 +652495,7 @@ var init_FileList = __esm({
652202
652495
  buildSafeAgentPreviewTitle = (title, terminalWidth) => {
652203
652496
  return sliceByVisualWidth(title, Math.max(1, terminalWidth - 1)) || " ";
652204
652497
  };
652205
- FileList = (0, import_react111.memo)((0, import_react111.forwardRef)(({ query, selectedIndex, visible, maxItems = 10, rootPath = process.cwd(), onFilteredCountChange, searchMode = "file" }, ref) => {
652498
+ FileList = (0, import_react111.memo)((0, import_react111.forwardRef)(({ query, selectedIndex, visible, maxItems = 10, rootPath = process.cwd(), onFilteredCountChange, searchMode = "file", workspaceFilter = null }, ref) => {
652206
652499
  const { t } = useI18n();
652207
652500
  const { theme: theme14 } = useTheme();
652208
652501
  const [files, setFiles] = (0, import_react111.useState)([]);
@@ -652221,7 +652514,9 @@ var init_FileList = __esm({
652221
652514
  const [agentError, setAgentError] = (0, import_react111.useState)(null);
652222
652515
  const agentAbortRef = (0, import_react111.useRef)(null);
652223
652516
  const wasAgentModeRef = (0, import_react111.useRef)(false);
652224
- const isAgentMode = query.startsWith("??");
652517
+ const isWorkspaceSelectionMode = query.startsWith(":");
652518
+ const normalizedQuery = isWorkspaceSelectionMode ? "" : workspaceFilter && query.startsWith("[") ? query.replace(/^\[[^\]]*\]/, "") : query;
652519
+ const isAgentMode = normalizedQuery.startsWith("??");
652225
652520
  const { columns: terminalWidth } = useTerminalSize();
652226
652521
  const agentPreviewLabels = (0, import_react111.useMemo)(() => ({
652227
652522
  assistantPrefix: t.fileList.agentPreviewAssistantPrefix,
@@ -652249,7 +652544,8 @@ var init_FileList = __esm({
652249
652544
  return maxItems ? Math.min(maxItems, MAX_DISPLAY_ITEMS2) : MAX_DISPLAY_ITEMS2;
652250
652545
  }, [maxItems]);
652251
652546
  const loadFiles = (0, import_react111.useCallback)(async () => {
652252
- const workingDirs = await getWorkingDirectories();
652547
+ const allDirs = await getWorkingDirectories();
652548
+ const workingDirs = workspaceFilter ? allDirs.filter((d) => d.path === workspaceFilter || d.path === workspaceFilter.replace(/\/$/, "")) : allDirs;
652253
652549
  const collected = [];
652254
652550
  let depthLimitHit = false;
652255
652551
  const FLUSH_INTERVAL_MS = 80;
@@ -652401,7 +652697,7 @@ var init_FileList = __esm({
652401
652697
  flush(true);
652402
652698
  setHasMoreDepth(depthLimitHit);
652403
652699
  setIsLoading(false);
652404
- }, [searchDepth]);
652700
+ }, [searchDepth, workspaceFilter]);
652405
652701
  const searchFileContent = (0, import_react111.useCallback)(async (query2) => {
652406
652702
  if (!query2.trim()) {
652407
652703
  return [];
@@ -652469,6 +652765,39 @@ var init_FileList = __esm({
652469
652765
  loadFiles();
652470
652766
  }, [visible, rootPath, loadFiles]);
652471
652767
  const [allFilteredFiles, setAllFilteredFiles] = (0, import_react111.useState)([]);
652768
+ const [workspaceDirs, setWorkspaceDirs] = (0, import_react111.useState)([]);
652769
+ (0, import_react111.useEffect)(() => {
652770
+ if (!visible || !isWorkspaceSelectionMode) {
652771
+ return;
652772
+ }
652773
+ let cancelled = false;
652774
+ (async () => {
652775
+ const dirs = await getWorkingDirectories();
652776
+ if (cancelled)
652777
+ return;
652778
+ const items = dirs.map((dir) => {
652779
+ const dirName = dir.displayName || getSourceDirName(dir.path);
652780
+ return {
652781
+ name: dirName,
652782
+ path: dir.path.endsWith("/") ? dir.path : `${dir.path}/`,
652783
+ isDirectory: true,
652784
+ sourceDir: dir.path
652785
+ };
652786
+ });
652787
+ setWorkspaceDirs(items);
652788
+ })();
652789
+ return () => {
652790
+ cancelled = true;
652791
+ };
652792
+ }, [visible, isWorkspaceSelectionMode]);
652793
+ const filteredWorkspaceDirs = (0, import_react111.useMemo)(() => {
652794
+ if (!isWorkspaceSelectionMode)
652795
+ return [];
652796
+ const filterText = query.slice(1).toLowerCase();
652797
+ if (!filterText)
652798
+ return workspaceDirs;
652799
+ return workspaceDirs.filter((dir) => dir.name.toLowerCase().includes(filterText) || (dir.sourceDir || "").toLowerCase().includes(filterText));
652800
+ }, [isWorkspaceSelectionMode, query, workspaceDirs]);
652472
652801
  (0, import_react111.useEffect)(() => {
652473
652802
  if (visible) {
652474
652803
  return;
@@ -652488,20 +652817,20 @@ var init_FileList = __esm({
652488
652817
  return;
652489
652818
  }
652490
652819
  const performSearch = async () => {
652491
- if (!query.trim()) {
652820
+ if (!normalizedQuery.trim()) {
652492
652821
  setAllFilteredFiles(files);
652493
652822
  return;
652494
652823
  }
652495
652824
  if (searchMode === "content") {
652496
- const results = await searchFileContent(query);
652825
+ const results = await searchFileContent(normalizedQuery);
652497
652826
  setAllFilteredFiles(results);
652498
- if (!isLoading && results.length === 0 && query.trim().length > 0 && hasMoreDepth) {
652827
+ if (!isLoading && results.length === 0 && normalizedQuery.trim().length > 0 && hasMoreDepth) {
652499
652828
  setSearchDepth((d) => d + 3);
652500
652829
  setIsIncreasingDepth(true);
652501
652830
  setTimeout(() => setIsIncreasingDepth(false), 400);
652502
652831
  }
652503
652832
  } else {
652504
- const queryLower = query.toLowerCase().replace(/\\/g, "/");
652833
+ const queryLower = normalizedQuery.toLowerCase().replace(/\\/g, "/");
652505
652834
  const filtered = files.filter((file2) => {
652506
652835
  const fileName = file2.name.toLowerCase();
652507
652836
  const filePath = file2.path.toLowerCase().replace(/\\/g, "/");
@@ -652532,7 +652861,7 @@ var init_FileList = __esm({
652532
652861
  return a.name.localeCompare(b.name);
652533
652862
  });
652534
652863
  setAllFilteredFiles(filtered);
652535
- if (!isLoading && filtered.length === 0 && query.trim().length > 0 && hasMoreDepth) {
652864
+ if (!isLoading && filtered.length === 0 && normalizedQuery.trim().length > 0 && hasMoreDepth) {
652536
652865
  setSearchDepth((d) => d + 3);
652537
652866
  setIsIncreasingDepth(true);
652538
652867
  setTimeout(() => setIsIncreasingDepth(false), 400);
@@ -652546,7 +652875,7 @@ var init_FileList = __esm({
652546
652875
  return () => clearTimeout(timer2);
652547
652876
  }, [
652548
652877
  files,
652549
- query,
652878
+ normalizedQuery,
652550
652879
  searchMode,
652551
652880
  searchFileContent,
652552
652881
  isLoading,
@@ -652555,7 +652884,7 @@ var init_FileList = __esm({
652555
652884
  agentResults
652556
652885
  ]);
652557
652886
  (0, import_react111.useEffect)(() => {
652558
- const agentQuery = isAgentMode ? query.slice(2).trim() : "";
652887
+ const agentQuery = isAgentMode ? normalizedQuery.slice(2).trim() : "";
652559
652888
  if (!visible || !isAgentMode || !agentQuery) {
652560
652889
  if (wasAgentModeRef.current) {
652561
652890
  wasAgentModeRef.current = false;
@@ -652589,7 +652918,8 @@ var init_FileList = __esm({
652589
652918
  setAgentRound(0);
652590
652919
  setAgentPreviewContent("");
652591
652920
  try {
652592
- const workingDirs = await getWorkingDirectories();
652921
+ const allDirs = await getWorkingDirectories();
652922
+ const workingDirs = workspaceFilter ? allDirs.filter((d) => d.path === workspaceFilter || d.path === workspaceFilter.replace(/\/$/, "")) : allDirs;
652593
652923
  const generator = fileSearchAgent.search(agentQuery, searchMode, workingDirs, controller.signal, agentPreviewLabels);
652594
652924
  for await (const event of generator) {
652595
652925
  if (controller.signal.aborted) {
@@ -652621,7 +652951,14 @@ var init_FileList = __esm({
652621
652951
  clearTimeout(timer2);
652622
652952
  controller.abort();
652623
652953
  };
652624
- }, [query, searchMode, visible, isAgentMode, agentPreviewLabels]);
652954
+ }, [
652955
+ normalizedQuery,
652956
+ searchMode,
652957
+ visible,
652958
+ isAgentMode,
652959
+ agentPreviewLabels,
652960
+ workspaceFilter
652961
+ ]);
652625
652962
  const displayItems = (0, import_react111.useMemo)(() => {
652626
652963
  if (isAgentMode || searchMode === "content") {
652627
652964
  return allFilteredFiles.map((file2) => ({
@@ -652632,7 +652969,7 @@ var init_FileList = __esm({
652632
652969
  }));
652633
652970
  }
652634
652971
  if (displayMode === "tree") {
652635
- return buildTreeDisplayItems(allFilteredFiles, files, query);
652972
+ return buildTreeDisplayItems(allFilteredFiles, files, normalizedQuery);
652636
652973
  }
652637
652974
  return allFilteredFiles.map((file2) => ({
652638
652975
  file: file2,
@@ -652645,7 +652982,7 @@ var init_FileList = __esm({
652645
652982
  files,
652646
652983
  displayMode,
652647
652984
  searchMode,
652648
- query,
652985
+ normalizedQuery,
652649
652986
  isAgentMode
652650
652987
  ]);
652651
652988
  const normalizedSelectedIndex = (0, import_react111.useMemo)(() => {
@@ -652679,9 +653016,15 @@ var init_FileList = __esm({
652679
653016
  const hiddenBelowCount = Math.max(0, displayItems.length - fileWindow.endIndex);
652680
653017
  (0, import_react111.useEffect)(() => {
652681
653018
  if (onFilteredCountChange) {
652682
- onFilteredCountChange(displayItems.length);
653019
+ const count = isWorkspaceSelectionMode ? filteredWorkspaceDirs.length : displayItems.length;
653020
+ onFilteredCountChange(count);
652683
653021
  }
652684
- }, [displayItems.length, onFilteredCountChange]);
653022
+ }, [
653023
+ displayItems.length,
653024
+ onFilteredCountChange,
653025
+ isWorkspaceSelectionMode,
653026
+ filteredWorkspaceDirs.length
653027
+ ]);
652685
653028
  const resolveInsertionPath = (0, import_react111.useCallback)((entry) => {
652686
653029
  if (!entry) {
652687
653030
  return null;
@@ -652698,12 +653041,25 @@ var init_FileList = __esm({
652698
653041
  }, [rootPath, searchMode]);
652699
653042
  (0, import_react111.useImperativeHandle)(ref, () => ({
652700
653043
  getSelectedFile: () => {
653044
+ if (isWorkspaceSelectionMode) {
653045
+ const wsIndex = Math.min(selectedIndex, Math.max(0, filteredWorkspaceDirs.length - 1));
653046
+ const wsItem = filteredWorkspaceDirs[wsIndex];
653047
+ return wsItem ? wsItem.path : null;
653048
+ }
652701
653049
  const selectedEntry = displayItems[normalizedSelectedIndex];
652702
653050
  if (!selectedEntry) {
652703
653051
  return null;
652704
653052
  }
652705
653053
  return resolveInsertionPath(selectedEntry);
652706
653054
  },
653055
+ getSelectedDisplayName: () => {
653056
+ if (isWorkspaceSelectionMode) {
653057
+ const wsIndex = Math.min(selectedIndex, Math.max(0, filteredWorkspaceDirs.length - 1));
653058
+ const wsItem = filteredWorkspaceDirs[wsIndex];
653059
+ return wsItem ? wsItem.name : null;
653060
+ }
653061
+ return null;
653062
+ },
652707
653063
  toggleDisplayMode: () => {
652708
653064
  if (searchMode !== "file") {
652709
653065
  return false;
@@ -652776,7 +653132,10 @@ var init_FileList = __esm({
652776
653132
  isIncreasingDepth,
652777
653133
  displayMode,
652778
653134
  selectedKeys,
652779
- resolveInsertionPath
653135
+ resolveInsertionPath,
653136
+ isWorkspaceSelectionMode,
653137
+ filteredWorkspaceDirs,
653138
+ selectedIndex
652780
653139
  ]);
652781
653140
  const displaySelectedIndex = filteredFiles.length === 0 ? -1 : normalizedSelectedIndex - fileWindow.startIndex;
652782
653141
  const selectedFileFullPath = (0, import_react111.useMemo)(() => {
@@ -652789,6 +653148,47 @@ var init_FileList = __esm({
652789
653148
  if (!visible) {
652790
653149
  return null;
652791
653150
  }
653151
+ if (isWorkspaceSelectionMode) {
653152
+ const wsItems = filteredWorkspaceDirs;
653153
+ const wsSelectedIndex = Math.min(selectedIndex, Math.max(0, wsItems.length - 1));
653154
+ const wsWindow = wsItems.slice(0, effectiveMaxItems);
653155
+ if (wsItems.length === 0) {
653156
+ return import_react111.default.createElement(
653157
+ Box_default,
653158
+ { paddingX: 1, marginTop: 1 },
653159
+ import_react111.default.createElement(Text, { color: theme14.colors.menuSecondary, dimColor: true }, t.fileList.noFilesFound)
653160
+ );
653161
+ }
653162
+ return import_react111.default.createElement(
653163
+ Box_default,
653164
+ { paddingX: 1, marginTop: 1, flexDirection: "column" },
653165
+ import_react111.default.createElement(
653166
+ Box_default,
653167
+ { marginBottom: 1 },
653168
+ import_react111.default.createElement(Text, { color: theme14.colors.menuInfo, bold: true }, t.fileList.workspaceFilterHint.replace("{filter}", query.slice(1) || "*"))
653169
+ ),
653170
+ wsWindow.map((item, index) => {
653171
+ const isSelected = index === wsSelectedIndex;
653172
+ return import_react111.default.createElement(
653173
+ Text,
653174
+ { key: `${item.sourceDir}::${item.path}`, backgroundColor: isSelected ? theme14.colors.menuSelected : void 0, color: isSelected ? theme14.colors.menuNormal : theme14.colors.warning, wrap: "truncate-end" },
653175
+ isSelected ? "\u276F " : " ",
653176
+ "\u25C7 ",
653177
+ item.name
653178
+ );
653179
+ }),
653180
+ wsItems.length > effectiveMaxItems && import_react111.default.createElement(
653181
+ Box_default,
653182
+ { marginTop: 1 },
653183
+ import_react111.default.createElement(
653184
+ Text,
653185
+ { color: theme14.colors.menuSecondary, dimColor: true },
653186
+ t.commandPanel.scrollHint,
653187
+ t.commandPanel.moreBelow.replace("{count}", (wsItems.length - effectiveMaxItems).toString())
653188
+ )
653189
+ )
653190
+ );
653191
+ }
652792
653192
  if (isAgentMode) {
652793
653193
  if (isAgentSearching && displayItems.length === 0) {
652794
653194
  const agentSearchStatus = t.fileList.agentSearching.replace("{round}", String(agentRound || 1));
@@ -652824,12 +653224,12 @@ var init_FileList = __esm({
652824
653224
  );
652825
653225
  }
652826
653226
  }
652827
- const stillSearching = isLoading || isIncreasingDepth || query.trim().length > 0 && hasMoreDepth;
653227
+ const stillSearching = isLoading || isIncreasingDepth || normalizedQuery.trim().length > 0 && hasMoreDepth;
652828
653228
  if (!isAgentMode && stillSearching && displayItems.length === 0) {
652829
653229
  return import_react111.default.createElement(
652830
653230
  Box_default,
652831
653231
  { 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)
653232
+ 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
653233
  );
652834
653234
  }
652835
653235
  if (displayItems.length === 0) {
@@ -652853,6 +653253,11 @@ var init_FileList = __esm({
652853
653253
  displayItems.length > effectiveMaxItems && `(${normalizedSelectedIndex + 1}/${displayItems.length})`
652854
653254
  )
652855
653255
  ),
653256
+ workspaceFilter && !isWorkspaceSelectionMode && import_react111.default.createElement(
653257
+ Box_default,
653258
+ null,
653259
+ import_react111.default.createElement(Text, { color: theme14.colors.menuInfo, bold: true }, t.fileList.workspaceFilterHint.replace("{filter}", getSourceDirName(workspaceFilter)))
653260
+ ),
652856
653261
  filteredFiles.map((item, index) => {
652857
653262
  const file2 = item.file;
652858
653263
  const isSelected = index === displaySelectedIndex;
@@ -653440,8 +653845,7 @@ var init_GitLinePickerPanel = __esm({
653440
653845
  Text,
653441
653846
  { color: theme14.colors.menuInfo },
653442
653847
  t.gitLinePickerPanel.selectedLabel,
653443
- ":",
653444
- " ",
653848
+ ": ",
653445
653849
  selectedCommits.size
653446
653850
  )
653447
653851
  ) : void 0, scrollHintFormat: (above, below) => import_react115.default.createElement(
@@ -653470,8 +653874,9 @@ var init_GitLinePickerPanel = __esm({
653470
653874
  )
653471
653875
  ), renderItem: (commit, isSelected) => {
653472
653876
  const isChecked = selectedCommits.has(commit.sha);
653473
- const title = commit.kind === "staged" ? `${t.reviewCommitPanel.stagedLabel} (${commit.fileCount ?? 0} ${t.reviewCommitPanel.filesLabel})` : `${formatShortSha(commit.sha)} ${truncateText2(commit.subject, 72)}`;
653474
- const subtitle = commit.kind === "staged" ? "" : `${commit.authorName} \xB7 ${formatDate(commit.dateIso)}`;
653877
+ const isWorkingTreeEntry = commit.kind === "staged" || commit.kind === "unstaged";
653878
+ const title = isWorkingTreeEntry ? `${commit.kind === "staged" ? t.reviewCommitPanel.stagedLabel : t.reviewCommitPanel.unstagedLabel} (${commit.fileCount ?? 0} ${t.reviewCommitPanel.filesLabel})` : `${formatShortSha(commit.sha)} ${truncateText2(commit.subject, 72)}`;
653879
+ const subtitle = isWorkingTreeEntry ? "" : `${commit.authorName} \xB7 ${formatDate(commit.dateIso)}`;
653475
653880
  return import_react115.default.createElement(
653476
653881
  import_react115.default.Fragment,
653477
653882
  null,
@@ -654003,13 +654408,13 @@ function ChatInput({ onSubmit, onCommand, placeholder = "Type your message...",
654003
654408
  const [argsSelectedIndex, setArgsSelectedIndex] = import_react120.default.useState(0);
654004
654409
  const argsPickerContext = (0, import_react120.useMemo)(() => {
654005
654410
  const text2 = buffer.text;
654006
- const rootMatch = text2.match(/^\/([a-zA-Z0-9_-]+)(?:\s+\S+)*\s*$/);
654411
+ const rootMatch = text2.match(/^\/([a-zA-Z0-9_:-]+)(?:\s+\S+)*\s*$/);
654007
654412
  const inlineTrigger = findInlineCommandTrigger(text2, buffer.getCursorPosition());
654008
654413
  const cmd = (rootMatch == null ? void 0 : rootMatch[1]) ?? (inlineTrigger == null ? void 0 : inlineTrigger.query) ?? "";
654009
654414
  const options3 = getCommandArgsOptions(cmd, text2);
654010
654415
  return { commandName: cmd, options: options3 };
654011
654416
  }, [buffer, buffer.text]);
654012
- const { showFilePicker, setShowFilePicker, fileSelectedIndex, setFileSelectedIndex, fileQuery, setFileQuery, atSymbolPosition, setAtSymbolPosition, filteredFileCount, searchMode, updateFilePickerState, handleFileSelect, handleMultipleFileSelect, handleFilteredCountChange, fileListRef } = useFilePicker(buffer, triggerUpdate);
654417
+ const { showFilePicker, setShowFilePicker, fileSelectedIndex, setFileSelectedIndex, fileQuery, setFileQuery, atSymbolPosition, setAtSymbolPosition, filteredFileCount, searchMode, workspaceFilter, updateFilePickerState, handleFileSelect, handleMultipleFileSelect, handleFilteredCountChange, fileListRef } = useFilePicker(buffer, triggerUpdate);
654013
654418
  const { showHistoryMenu, setShowHistoryMenu, historySelectedIndex, setHistorySelectedIndex, escapeKeyCount, setEscapeKeyCount, escapeKeyTimer, getUserMessages, handleHistorySelect, currentHistoryIndex, navigateHistoryUp, navigateHistoryDown, resetHistoryNavigation, saveToHistory } = useHistoryNavigation(buffer, triggerUpdate, chatHistory, onHistorySelect);
654014
654419
  const { showAgentPicker, setShowAgentPicker, agentSelectedIndex, setAgentSelectedIndex, updateAgentPickerState, getFilteredAgents, handleAgentSelect } = useAgentPicker(buffer, triggerUpdate);
654015
654420
  const { showTodoPicker, setShowTodoPicker, todoSelectedIndex, setTodoSelectedIndex, todos, selectedTodos, toggleTodoSelection, confirmTodoSelection, isLoading: todoIsLoading, searchQuery: todoSearchQuery, setSearchQuery: setTodoSearchQuery, totalTodoCount } = useTodoPicker(buffer, triggerUpdate, process.cwd());
@@ -654319,11 +654724,11 @@ function ChatInput({ onSubmit, onCommand, placeholder = "Type your message...",
654319
654724
  const text2 = buffer.text;
654320
654725
  if (!text2.startsWith("/"))
654321
654726
  return "";
654322
- const match = text2.match(/^\/([a-zA-Z0-9_-]+)(\s*)$/);
654727
+ const match = text2.match(/^\/([a-zA-Z0-9_:-]+)(\s*)$/);
654323
654728
  if (!match)
654324
654729
  return "";
654325
654730
  const cmd = match[1] ?? "";
654326
- const hint = COMMAND_ARGS_HINTS[cmd];
654731
+ const hint = getCommandArgsHint(cmd);
654327
654732
  if (!hint)
654328
654733
  return "";
654329
654734
  return match[2] && match[2].length > 0 ? hint : ` ${hint}`;
@@ -654382,7 +654787,7 @@ function ChatInput({ onSubmit, onCommand, placeholder = "Type your message...",
654382
654787
  plainBuf = "";
654383
654788
  }
654384
654789
  };
654385
- const isPlaceholderTag = (tag2) => /^\[Paste \d+ lines #\d+\]$/.test(tag2) || /^\[image #\d+\]$/.test(tag2) || /^\[Skill:[^\]]+\]$/.test(tag2) || /^\[GitLine:[^\]]+\]$/.test(tag2) || /^\[»[^\]]*\]$/.test(tag2);
654790
+ const isPlaceholderTag = (tag2) => /^\[Paste \d+ lines #\d+\]$/.test(tag2) || /^\[image #\d+\]$/.test(tag2) || /^\[Skill:[^\]]+\]$/.test(tag2) || /^\[GitLine:[^\]]+\]$/.test(tag2) || /^\[»[^\]]*\]$/.test(tag2) || /^\[[^\]]+\]$/.test(tag2);
654386
654791
  let i = 0;
654387
654792
  while (i < line.length) {
654388
654793
  const ch = line[i];
@@ -654408,8 +654813,14 @@ function ChatInput({ onSubmit, onCommand, placeholder = "Type your message...",
654408
654813
  if (closeIdx !== -1) {
654409
654814
  const tagText = line.slice(i, closeIdx + 1);
654410
654815
  if (isPlaceholderTag(tagText)) {
654816
+ const atPrefix = i > 0 && line[i - 1] === "@" ? "@" : "";
654817
+ if (atPrefix) {
654818
+ if (plainBuf.endsWith("@")) {
654819
+ plainBuf = plainBuf.slice(0, -1);
654820
+ }
654821
+ }
654411
654822
  flushPlain();
654412
- tokens.push({ text: tagText, highlight: true });
654823
+ tokens.push({ text: atPrefix + tagText, highlight: true });
654413
654824
  i = closeIdx + 1;
654414
654825
  continue;
654415
654826
  }
@@ -654528,7 +654939,7 @@ function ChatInput({ onSubmit, onCommand, placeholder = "Type your message...",
654528
654939
  import_react120.default.createElement(
654529
654940
  import_react120.Suspense,
654530
654941
  { fallback: null },
654531
- import_react120.default.createElement(FileList2, { ref: fileListRef, query: fileQuery, selectedIndex: fileSelectedIndex, visible: showFilePicker, maxItems: 10, rootPath: process.cwd(), onFilteredCountChange: handleFilteredCountChange, searchMode })
654942
+ import_react120.default.createElement(FileList2, { ref: fileListRef, query: fileQuery, selectedIndex: fileSelectedIndex, visible: showFilePicker, maxItems: 10, rootPath: process.cwd(), onFilteredCountChange: handleFilteredCountChange, searchMode, workspaceFilter })
654532
654943
  ),
654533
654944
  import_react120.default.createElement(
654534
654945
  import_react120.Suspense,
@@ -655250,7 +655661,8 @@ function StatusLine({ yoloMode = false, planMode = false, vulnerabilityHuntingMo
655250
655661
  speedometer: {
655251
655662
  enabled: tpsTracker.isActive(),
655252
655663
  tps: tpsSnapshot.tps,
655253
- peakTps: tpsSnapshot.peakTps
655664
+ peakTps: tpsSnapshot.peakTps,
655665
+ ttftMs: tpsSnapshot.ttftMs
655254
655666
  }
655255
655667
  }
655256
655668
  };
@@ -655474,8 +655886,13 @@ function StatusLine({ yoloMode = false, planMode = false, vulnerabilityHuntingMo
655474
655886
  });
655475
655887
  }
655476
655888
  if (tpsTracker.isActive() && !isBuiltinOverridden(BUILTIN_STATUSLINE_IDS.speedometer)) {
655889
+ let speedometerText = `\u23F1 ${tpsSnapshot.tps} tok/s`;
655890
+ if (tpsSnapshot.ttftMs !== null) {
655891
+ const ttftSec = (tpsSnapshot.ttftMs / 1e3).toFixed(1);
655892
+ speedometerText += ` \xB7 ttft ${ttftSec}s`;
655893
+ }
655477
655894
  statusItems.push({
655478
- text: `\u23F1 ${tpsSnapshot.tps} tok/s`,
655895
+ text: speedometerText,
655479
655896
  color: tpsSnapshot.tps > 0 ? theme14.colors.cyan : theme14.colors.menuSecondary
655480
655897
  });
655481
655898
  }
@@ -655539,7 +655956,18 @@ function StatusLine({ yoloMode = false, planMode = false, vulnerabilityHuntingMo
655539
655956
  import_react123.default.createElement(Text, { color: tpsSnapshot.tps > 0 ? theme14.colors.cyan : theme14.colors.menuSecondary, bold: true }, tpsSnapshot.tps),
655540
655957
  " tok/s",
655541
655958
  " \xB7 peak ",
655542
- import_react123.default.createElement(Text, { color: theme14.colors.warning }, tpsSnapshot.peakTps)
655959
+ import_react123.default.createElement(Text, { color: theme14.colors.warning }, tpsSnapshot.peakTps),
655960
+ tpsSnapshot.ttftMs !== null && import_react123.default.createElement(
655961
+ import_react123.default.Fragment,
655962
+ null,
655963
+ " \xB7 ttft ",
655964
+ import_react123.default.createElement(
655965
+ Text,
655966
+ { color: theme14.colors.menuInfo },
655967
+ (tpsSnapshot.ttftMs / 1e3).toFixed(1),
655968
+ "s"
655969
+ )
655970
+ )
655543
655971
  )
655544
655972
  ),
655545
655973
  yoloMode && !isBuiltinOverridden(BUILTIN_STATUSLINE_IDS.modeYolo) && import_react123.default.createElement(
@@ -655839,8 +656267,11 @@ function LoadingIndicator({ isStreaming, isStopping, isSaving, isCompressing, is
655839
656267
  ].join("|");
655840
656268
  const previousStreamActivityMarkerRef = (0, import_react126.useRef)(null);
655841
656269
  const lastStreamActivityElapsedSecondsRef = (0, import_react126.useRef)(elapsedSeconds);
656270
+ const wasStreamingRef = (0, import_react126.useRef)(false);
656271
+ const isStreamingStarted = isStreaming && !wasStreamingRef.current;
656272
+ wasStreamingRef.current = isStreaming;
655842
656273
  const shouldIgnoreStreamDelay = isCompressing || isAutoCompressing;
655843
- if (!isStreaming || shouldIgnoreStreamDelay || previousStreamActivityMarkerRef.current !== streamActivityMarker) {
656274
+ if (!isStreaming || shouldIgnoreStreamDelay || isStreamingStarted || previousStreamActivityMarkerRef.current !== streamActivityMarker) {
655844
656275
  previousStreamActivityMarkerRef.current = streamActivityMarker;
655845
656276
  lastStreamActivityElapsedSecondsRef.current = elapsedSeconds;
655846
656277
  }
@@ -663030,16 +663461,25 @@ function ToolConfirmation({ toolName, toolArguments, allTools, onConfirm, onHook
663030
663461
  };
663031
663462
  }, [terminalCommand, commandPageOffset, terminalColumns]);
663032
663463
  (0, import_react149.useEffect)(() => {
663464
+ var _a21, _b14;
663033
663465
  const context3 = {
663034
663466
  toolName,
663035
663467
  args: toolArguments,
663036
663468
  isSensitive: sensitiveCommandCheck.isSensitive,
663469
+ matchedPattern: (_a21 = sensitiveCommandCheck.matchedCommand) == null ? void 0 : _a21.pattern,
663470
+ matchedReason: (_b14 = sensitiveCommandCheck.matchedCommand) == null ? void 0 : _b14.description,
663037
663471
  allTools: allTools == null ? void 0 : allTools.map((t2) => ({
663038
663472
  name: t2.function.name,
663039
663473
  arguments: t2.function.arguments
663040
663474
  }))
663041
663475
  };
663042
663476
  unifiedHooksExecutor.executeHooks("toolConfirmation", context3).then((hookResult) => {
663477
+ const autoResult = extractHookProvidedConfirmation(hookResult);
663478
+ if (autoResult) {
663479
+ setHasSelected(true);
663480
+ onConfirm(autoResult);
663481
+ return;
663482
+ }
663043
663483
  const interpreted = interpretHookResult("toolConfirmation", hookResult);
663044
663484
  if (interpreted.action === "warn" && interpreted.warningMessage) {
663045
663485
  console.warn(interpreted.warningMessage);
@@ -663053,7 +663493,13 @@ function ToolConfirmation({ toolName, toolArguments, allTools, onConfirm, onHook
663053
663493
  }).catch((error42) => {
663054
663494
  console.error("Failed to execute toolConfirmation hook:", error42);
663055
663495
  });
663056
- }, [toolName, toolArguments, sensitiveCommandCheck.isSensitive, allTools]);
663496
+ }, [
663497
+ toolName,
663498
+ toolArguments,
663499
+ sensitiveCommandCheck.isSensitive,
663500
+ sensitiveCommandCheck.matchedCommand,
663501
+ allTools
663502
+ ]);
663057
663503
  (0, import_react149.useEffect)(() => {
663058
663504
  if (!vscodeConnection.isConnected()) {
663059
663505
  return;
@@ -678878,6 +679324,8 @@ var init_sseManager = __esm({
678878
679324
  init_hashBasedSnapshot();
678879
679325
  init_permissionsConfig();
678880
679326
  init_sensitiveCommandManager();
679327
+ init_unifiedHooksExecutor();
679328
+ init_hookResultInterpreter();
678881
679329
  SSEManager = class {
678882
679330
  constructor() {
678883
679331
  Object.defineProperty(this, "server", {
@@ -679267,6 +679715,25 @@ var init_sseManager = __esm({
679267
679715
  });
679268
679716
  }
679269
679717
  availableOptions.push({ value: "reject_with_reply", label: "Reject with reply" }, { value: "reject", label: "Reject and end session" });
679718
+ try {
679719
+ const hookContext = {
679720
+ toolName: toolCall.function.name,
679721
+ args: toolCall.function.arguments,
679722
+ isSensitive,
679723
+ matchedPattern: sensitiveInfo == null ? void 0 : sensitiveInfo.pattern,
679724
+ matchedReason: sensitiveInfo == null ? void 0 : sensitiveInfo.description,
679725
+ allTools: allTools == null ? void 0 : allTools.map((t) => ({
679726
+ name: t.function.name,
679727
+ arguments: t.function.arguments
679728
+ }))
679729
+ };
679730
+ const hookResult = await unifiedHooksExecutor.executeHooks("toolConfirmation", hookContext);
679731
+ const autoResult = extractHookProvidedConfirmation(hookResult);
679732
+ if (autoResult) {
679733
+ return autoResult;
679734
+ }
679735
+ } catch {
679736
+ }
679270
679737
  sendEvent({
679271
679738
  type: "tool_confirmation_request",
679272
679739
  data: {
@@ -692854,7 +693321,7 @@ var require_package4 = __commonJS({
692854
693321
  "package.json"(exports2, module2) {
692855
693322
  module2.exports = {
692856
693323
  name: "snow-ai",
692857
- version: "0.8.9",
693324
+ version: "0.8.11",
692858
693325
  description: "Agentic coding in your terminal",
692859
693326
  license: "MIT",
692860
693327
  bin: {
@@ -693054,6 +693521,8 @@ var init_acpManager = __esm({
693054
693521
  init_sessionManager();
693055
693522
  init_permissionsConfig();
693056
693523
  init_sensitiveCommandManager();
693524
+ init_unifiedHooksExecutor();
693525
+ init_hookResultInterpreter();
693057
693526
  init_chat();
693058
693527
  init_responses();
693059
693528
  init_anthropic();
@@ -693479,6 +693948,26 @@ var init_acpManager = __esm({
693479
693948
  name: "Reject",
693480
693949
  kind: "reject_once"
693481
693950
  });
693951
+ try {
693952
+ const hookContext = {
693953
+ toolName: toolCall.function.name,
693954
+ args: toolCall.function.arguments,
693955
+ isSensitive,
693956
+ allTools: void 0
693957
+ };
693958
+ const hookResult = await unifiedHooksExecutor.executeHooks("toolConfirmation", hookContext);
693959
+ const autoResult = extractHookProvidedConfirmation(hookResult);
693960
+ if (autoResult) {
693961
+ if (autoResult === "approve" || autoResult === "approve_always") {
693962
+ if (autoResult === "approve_always") {
693963
+ addToolToPermissions(workingDirectory, toolCall.function.name);
693964
+ }
693965
+ return true;
693966
+ }
693967
+ return false;
693968
+ }
693969
+ } catch {
693970
+ }
693482
693971
  const toolCallUpdate = {
693483
693972
  toolCallId: toolCall.id,
693484
693973
  title: toolCall.function.name